make diskio accept devices starting with "/dev/"
[monky] / src / libmpdclient.c
1 /* libmpdclient
2  * (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3  * This project's homepage is: http://www.musicpd.org
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * - Neither the name of the Music Player Daemon nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $Id$
33  *
34  * */
35
36 #include "conky.h"
37 #include "libmpdclient.h"
38
39 #include <errno.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <stdio.h>
43 #include <sys/param.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <limits.h>
49
50 #ifdef WIN32
51 #  include <ws2tcpip.h>
52 #  include <winsock.h>
53 #else
54 #  include <netinet/in.h>
55 #  include <arpa/inet.h>
56 #  include <sys/socket.h>
57 #  include <netdb.h>
58 #endif
59
60 /* (bits + 1) / 3 (plus the sign character) */
61 #define INTLEN          ((sizeof(int)           * CHAR_BIT + 1) / 3 + 1)
62 #define LONGLONGLEN     ((sizeof(long long)     * CHAR_BIT + 1) / 3 + 1)
63
64 #define COMMAND_LIST    1
65 #define COMMAND_LIST_OK 2
66
67 #ifndef MPD_NO_GAI
68 #  ifdef AI_ADDRCONFIG
69 #    define MPD_HAVE_GAI
70 #  endif
71 #endif
72
73 #ifndef MSG_DONTWAIT
74 #  define MSG_DONTWAIT 0
75 #endif
76
77 #ifdef WIN32
78 #  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
79 #  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
80 #else
81 #  define SELECT_ERRNO_IGNORE   (errno == EINTR)
82 #  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
83 #  define winsock_dll_error(c)  0
84 #  define closesocket(s)                close(s)
85 #  define WSACleanup()                  do { /* nothing */ } while (0)
86 #endif
87
88 #ifdef WIN32
89 static int winsock_dll_error(mpd_Connection *connection)
90 {
91         WSADATA wsaData;
92
93         if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0
94                         || LOBYTE(wsaData.wVersion) != 2
95                         || HIBYTE(wsaData.wVersion) != 2) {
96                 strcpy(connection->errorStr, "Could not find usable WinSock DLL.");
97                 connection->error = MPD_ERROR_SYSTEM;
98                 return 1;
99         }
100         return 0;
101 }
102
103 static int do_connect_fail(mpd_Connection *connection,
104                 const struct sockaddr *serv_addr, int addrlen)
105 {
106         int iMode = 1;  /* 0 = blocking, else non-blocking */
107
108         ioctlsocket(connection->sock, FIONBIO, (u_long FAR *) &iMode);
109         return (connect(connection->sock, serv_addr, addrlen) == SOCKET_ERROR
110                 && WSAGetLastError() != WSAEWOULDBLOCK);
111 }
112 #else /* !WIN32 (sane operating systems) */
113 static int do_connect_fail(mpd_Connection *connection,
114                 const struct sockaddr *serv_addr, int addrlen)
115 {
116         int flags = fcntl(connection->sock, F_GETFL, 0);
117
118         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
119         return (connect(connection->sock, serv_addr, addrlen) < 0
120                 && errno != EINPROGRESS);
121 }
122 #endif /* !WIN32 */
123
124 #ifdef MPD_HAVE_GAI
125 static int mpd_connect(mpd_Connection *connection, const char *host, int port,
126                 float timeout)
127 {
128         int error;
129         char service[INTLEN + 1];
130         struct addrinfo hints;
131         struct addrinfo *res = NULL;
132         struct addrinfo *addrinfo = NULL;
133
134         /* Setup hints */
135         hints.ai_flags          = AI_ADDRCONFIG;
136         hints.ai_family         = PF_UNSPEC;
137         hints.ai_socktype       = SOCK_STREAM;
138         hints.ai_protocol       = IPPROTO_TCP;
139         hints.ai_addrlen        = 0;
140         hints.ai_addr           = NULL;
141         hints.ai_canonname      = NULL;
142         hints.ai_next           = NULL;
143
144         snprintf(service, sizeof(service), "%i", port);
145
146         error = getaddrinfo(host, service, &hints, &addrinfo);
147
148         if (error) {
149                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
150                         "host \"%s\" not found: %s", host, gai_strerror(error));
151                 connection->error = MPD_ERROR_UNKHOST;
152                 return -1;
153         }
154
155         for (res = addrinfo; res; res = res->ai_next) {
156                 /* create socket */
157                 if (connection->sock > -1) {
158                         closesocket(connection->sock);
159                 }
160                 connection->sock = socket(res->ai_family, SOCK_STREAM,
161                         res->ai_protocol);
162                 if (connection->sock < 0) {
163                         snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
164                                 "problems creating socket: %s", strerror(errno));
165                         connection->error = MPD_ERROR_SYSTEM;
166                         freeaddrinfo(addrinfo);
167                         return -1;
168                 }
169
170                 mpd_setConnectionTimeout(connection, timeout);
171
172                 /* connect stuff */
173                 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
174                         /* try the next address family */
175                         closesocket(connection->sock);
176                         connection->sock = -1;
177                         continue;
178                 }
179         }
180
181         freeaddrinfo(addrinfo);
182
183         if (connection->sock < 0) {
184                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
185                         "problems connecting to \"%s\" on port %i: %s", host, port,
186                         strerror(errno));
187                 connection->error = MPD_ERROR_CONNPORT;
188
189                 return -1;
190         }
191
192         return 0;
193 }
194 #else /* !MPD_HAVE_GAI */
195 static int mpd_connect(mpd_Connection *connection, const char *host, int port,
196                 float timeout)
197 {
198         struct hostent *he;
199         struct sockaddr *dest;
200         int destlen;
201         struct sockaddr_in sin;
202
203         if (!(he = gethostbyname(host))) {
204                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
205                         "host \"%s\" not found", host);
206                 connection->error = MPD_ERROR_UNKHOST;
207                 return -1;
208         }
209
210         memset(&sin, 0, sizeof(struct sockaddr_in));
211         /* dest.sin_family = he->h_addrtype; */
212         sin.sin_family = AF_INET;
213         sin.sin_port = htons(port);
214
215         switch (he->h_addrtype) {
216                 case AF_INET:
217                         memcpy((char *) &sin.sin_addr.s_addr, (char *) he->h_addr,
218                                 he->h_length);
219                         dest = (struct sockaddr *) &sin;
220                         destlen = sizeof(struct sockaddr_in);
221                         break;
222                 default:
223                         strcpy(connection->errorStr, "address type is not IPv4");
224                         connection->error = MPD_ERROR_SYSTEM;
225                         return -1;
226                         break;
227         }
228
229         if (connection->sock > -1) {
230                 closesocket(connection->sock);
231         }
232         if ((connection->sock = socket(dest->sa_family, SOCK_STREAM, 0)) < 0) {
233                 strcpy(connection->errorStr, "problems creating socket");
234                 connection->error = MPD_ERROR_SYSTEM;
235                 return -1;
236         }
237
238         mpd_setConnectionTimeout(connection, timeout);
239
240         /* connect stuff */
241         if (do_connect_fail(connection, dest, destlen)) {
242                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
243                         "problems connecting to \"%s\" on port %i", host, port);
244                 connection->error = MPD_ERROR_CONNPORT;
245                 return -1;
246         }
247
248         return 0;
249 }
250 #endif /* !MPD_HAVE_GAI */
251
252 const char *mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] = {
253         "Artist",
254         "Album",
255         "Title",
256         "Track",
257         "Name",
258         "Genre",
259         "Date",
260         "Composer",
261         "Performer",
262         "Comment",
263         "Disc",
264         "Filename",
265         "Any"
266 };
267
268 static char *mpd_sanitizeArg(const char *arg)
269 {
270         size_t i;
271         char *ret;
272         register const char *c;
273         register char *rc;
274
275         /* instead of counting in that loop above,
276          * just use a bit more memory and halve running time */
277         ret = malloc(strlen(arg) * 2 + 1);
278
279         c = arg;
280         rc = ret;
281         for (i = strlen(arg) + 1; i != 0; --i) {
282                 if (*c == '"' || *c == '\\') {
283                         *rc++ = '\\';
284                 }
285                 *(rc++) = *(c++);
286         }
287
288         return ret;
289 }
290
291 static mpd_ReturnElement *mpd_newReturnElement(const char *name,
292                 const char *value)
293 {
294         mpd_ReturnElement *ret = malloc(sizeof(mpd_ReturnElement));
295
296         ret->name = strdup(name);
297         ret->value = strdup(value);
298
299         return ret;
300 }
301
302 static void mpd_freeReturnElement(mpd_ReturnElement *re)
303 {
304         free(re->name);
305         free(re->value);
306         free(re);
307 }
308
309 void mpd_setConnectionTimeout(mpd_Connection *connection, float timeout)
310 {
311         connection->timeout.tv_sec = (int) timeout;
312         connection->timeout.tv_usec =
313                 (int) ((timeout - connection->timeout.tv_sec) * 1e6 + 0.5);
314 }
315
316 static int mpd_parseWelcome(mpd_Connection *connection, const char *host,
317                 int port, /* char *rt, */ char *output)
318 {
319         char *tmp;
320         char *test;
321         int i;
322
323         if (strncmp(output, MPD_WELCOME_MESSAGE, strlen(MPD_WELCOME_MESSAGE))) {
324                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
325                         "mpd not running on port %i on host \"%s\"", port, host);
326                 connection->error = MPD_ERROR_NOTMPD;
327                 return 1;
328         }
329
330         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
331
332         for (i = 0; i < 3; i++) {
333                 if (tmp) {
334                         connection->version[i] = strtol(tmp, &test, 10);
335                 }
336
337                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
338                         snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
339                                 "error parsing version number at \"%s\"",
340                                 &output[strlen(MPD_WELCOME_MESSAGE)]);
341                         connection->error = MPD_ERROR_NOTMPD;
342                         return 1;
343                 }
344                 tmp = ++test;
345         }
346
347         return 0;
348 }
349
350 mpd_Connection *mpd_newConnection(const char *host, int port, float timeout)
351 {
352         int err;
353         char *rt;
354         char *output = NULL;
355         mpd_Connection *connection = malloc(sizeof(mpd_Connection));
356         struct timeval tv;
357         fd_set fds;
358
359         strcpy(connection->buffer, "");
360         connection->buflen = 0;
361         connection->bufstart = 0;
362         strcpy(connection->errorStr, "");
363         connection->error = 0;
364         connection->doneProcessing = 0;
365         connection->commandList = 0;
366         connection->listOks = 0;
367         connection->doneListOk = 0;
368         connection->sock = -1;
369         connection->returnElement = NULL;
370         connection->request = NULL;
371
372         if (winsock_dll_error(connection)) {
373                 return connection;
374         }
375
376         if (mpd_connect(connection, host, port, timeout) < 0) {
377                 return connection;
378         }
379
380         while (!(rt = strstr(connection->buffer, "\n"))) {
381                 tv.tv_sec = connection->timeout.tv_sec;
382                 tv.tv_usec = connection->timeout.tv_usec;
383                 FD_ZERO(&fds);
384                 FD_SET(connection->sock, &fds);
385                 if ((err = select(connection->sock + 1, &fds, NULL, NULL, &tv)) == 1) {
386                         int readed;
387
388                         readed = recv(connection->sock,
389                                 &(connection->buffer[connection->buflen]),
390                                 MPD_BUFFER_MAX_LENGTH - connection->buflen, 0);
391                         if (readed <= 0) {
392                                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
393                                         "problems getting a response from \"%s\" on port %i : %s",
394                                         host, port, strerror(errno));
395                                 connection->error = MPD_ERROR_NORESPONSE;
396                                 return connection;
397                         }
398                         connection->buflen += readed;
399                         connection->buffer[connection->buflen] = '\0';
400                 } else if (err < 0) {
401                         if (SELECT_ERRNO_IGNORE) {
402                                 continue;
403                         }
404                         snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
405                                 "problems connecting to \"%s\" on port %i", host, port);
406                         connection->error = MPD_ERROR_CONNPORT;
407                         return connection;
408                 } else {
409                         snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
410                                 "timeout in attempting to get a response from \"%s\" on "
411                                 "port %i", host, port);
412                         connection->error = MPD_ERROR_NORESPONSE;
413                         return connection;
414                 }
415         }
416
417         *rt = '\0';
418         output = strdup(connection->buffer);
419         strcpy(connection->buffer, rt + 1);
420         connection->buflen = strlen(connection->buffer);
421
422         if (mpd_parseWelcome(connection, host, port, /* rt, */ output) == 0) {
423                 connection->doneProcessing = 1;
424         }
425
426         free(output);
427
428         return connection;
429 }
430
431 void mpd_clearError(mpd_Connection *connection)
432 {
433         connection->error = 0;
434         connection->errorStr[0] = '\0';
435 }
436
437 void mpd_closeConnection(mpd_Connection *connection)
438 {
439         closesocket(connection->sock);
440         if (connection->returnElement) {
441                 free(connection->returnElement);
442         }
443         if (connection->request) {
444                 free(connection->request);
445         }
446         free(connection);
447         WSACleanup();
448 }
449
450 static void mpd_executeCommand(mpd_Connection *connection, const char *command)
451 {
452         int ret;
453         struct timeval tv;
454         fd_set fds;
455         const char *commandPtr = command;
456         int commandLen = strlen(command);
457
458         if (!connection->doneProcessing && !connection->commandList) {
459                 strcpy(connection->errorStr, "not done processing current command");
460                 connection->error = 1;
461                 return;
462         }
463
464         mpd_clearError(connection);
465
466         FD_ZERO(&fds);
467         FD_SET(connection->sock, &fds);
468         tv.tv_sec = connection->timeout.tv_sec;
469         tv.tv_usec = connection->timeout.tv_usec;
470
471         while ((ret = select(connection->sock + 1, NULL, &fds, NULL, &tv) == 1)
472                         || (ret == -1 && SELECT_ERRNO_IGNORE)) {
473                 ret = send(connection->sock, commandPtr, commandLen, MSG_DONTWAIT);
474                 if (ret <= 0) {
475                         if (SENDRECV_ERRNO_IGNORE) {
476                                 continue;
477                         }
478                         snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
479                                 "problems giving command \"%s\"", command);
480                         connection->error = MPD_ERROR_SENDING;
481                         return;
482                 } else {
483                         commandPtr += ret;
484                         commandLen -= ret;
485                 }
486
487                 if (commandLen <= 0) {
488                         break;
489                 }
490         }
491
492         if (commandLen > 0) {
493                 perror("");
494                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
495                         "timeout sending command \"%s\"", command);
496                 connection->error = MPD_ERROR_TIMEOUT;
497                 return;
498         }
499
500         if (!connection->commandList) {
501                 connection->doneProcessing = 0;
502         } else if (connection->commandList == COMMAND_LIST_OK) {
503                 connection->listOks++;
504         }
505 }
506
507 static void mpd_getNextReturnElement(mpd_Connection *connection)
508 {
509         char *output = NULL;
510         char *rt = NULL;
511         char *name = NULL;
512         char *value = NULL;
513         fd_set fds;
514         struct timeval tv;
515         char *tok = NULL;
516         int readed;
517         char *bufferCheck = NULL;
518         int err;
519         int pos;
520
521         if (connection->returnElement) {
522                 mpd_freeReturnElement(connection->returnElement);
523         }
524         connection->returnElement = NULL;
525
526         if (connection->doneProcessing
527                         || (connection->listOks && connection->doneListOk)) {
528                 strcpy(connection->errorStr,
529                         "already done processing current command");
530                 connection->error = 1;
531                 return;
532         }
533
534         bufferCheck = connection->buffer + connection->bufstart;
535         while (connection->bufstart >= connection->buflen
536                         || !(rt = strchr(bufferCheck, '\n'))) {
537                 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
538                         memmove(connection->buffer,
539                                 connection->buffer + connection->bufstart,
540                                 connection->buflen - connection->bufstart + 1);
541                         connection->buflen -= connection->bufstart;
542                         connection->bufstart = 0;
543                 }
544                 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
545                         strcpy(connection->errorStr, "buffer overrun");
546                         connection->error = MPD_ERROR_BUFFEROVERRUN;
547                         connection->doneProcessing = 1;
548                         connection->doneListOk = 0;
549                         return;
550                 }
551                 bufferCheck = connection->buffer + connection->buflen;
552                 tv.tv_sec = connection->timeout.tv_sec;
553                 tv.tv_usec = connection->timeout.tv_usec;
554                 FD_ZERO(&fds);
555                 FD_SET(connection->sock, &fds);
556                 if ((err = select(connection->sock + 1, &fds, NULL, NULL, &tv) == 1)) {
557                         readed = recv(connection->sock,
558                                 connection->buffer + connection->buflen,
559                                 MPD_BUFFER_MAX_LENGTH - connection->buflen, MSG_DONTWAIT);
560                         if (readed < 0 && SENDRECV_ERRNO_IGNORE) {
561                                 continue;
562                         }
563                         if (readed <= 0) {
564                                 strcpy(connection->errorStr, "connection closed");
565                                 connection->error = MPD_ERROR_CONNCLOSED;
566                                 connection->doneProcessing = 1;
567                                 connection->doneListOk = 0;
568                                 return;
569                         }
570                         connection->buflen += readed;
571                         connection->buffer[connection->buflen] = '\0';
572                 } else if (err < 0 && SELECT_ERRNO_IGNORE) {
573                         continue;
574                 } else {
575                         strcpy(connection->errorStr, "connection timeout");
576                         connection->error = MPD_ERROR_TIMEOUT;
577                         connection->doneProcessing = 1;
578                         connection->doneListOk = 0;
579                         return;
580                 }
581         }
582
583         *rt = '\0';
584         output = connection->buffer + connection->bufstart;
585         connection->bufstart = rt - connection->buffer + 1;
586
587         if (strcmp(output, "OK") == 0) {
588                 if (connection->listOks > 0) {
589                         strcpy(connection->errorStr, "expected more list_OK's");
590                         connection->error = 1;
591                 }
592                 connection->listOks = 0;
593                 connection->doneProcessing = 1;
594                 connection->doneListOk = 0;
595                 return;
596         }
597
598         if (strcmp(output, "list_OK") == 0) {
599                 if (!connection->listOks) {
600                         strcpy(connection->errorStr, "got an unexpected list_OK");
601                         connection->error = 1;
602                 } else {
603                         connection->doneListOk = 1;
604                         connection->listOks--;
605                 }
606                 return;
607         }
608
609         if (strncmp(output, "ACK", strlen("ACK")) == 0) {
610                 char *test;
611                 char *needle;
612                 int val;
613
614                 strcpy(connection->errorStr, output);
615                 connection->error = MPD_ERROR_ACK;
616                 connection->errorCode = MPD_ACK_ERROR_UNK;
617                 connection->errorAt = MPD_ERROR_AT_UNK;
618                 connection->doneProcessing = 1;
619                 connection->doneListOk = 0;
620
621                 needle = strchr(output, '[');
622                 if (!needle) {
623                         return;
624                 }
625                 val = strtol(needle + 1, &test, 10);
626                 if (*test != '@') {
627                         return;
628                 }
629                 connection->errorCode = val;
630                 val = strtol(test + 1, &test, 10);
631                 if (*test != ']') {
632                         return;
633                 }
634                 connection->errorAt = val;
635                 return;
636         }
637
638         tok = strchr(output, ':');
639         if (!tok) {
640                 return;
641         }
642         pos = tok - output;
643         value = ++tok;
644         name = output;
645         name[pos] = '\0';
646
647         if (value[0] == ' ') {
648                 connection->returnElement = mpd_newReturnElement(name, &(value[1]));
649         } else {
650                 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
651                         "error parsing: %s:%s", name, value);
652                 connection->error = 1;
653         }
654 }
655
656 void mpd_finishCommand(mpd_Connection *connection)
657 {
658         while (!connection->doneProcessing) {
659                 if (connection->doneListOk) {
660                         connection->doneListOk = 0;
661                 }
662                 mpd_getNextReturnElement(connection);
663         }
664 }
665
666 static void mpd_finishListOkCommand(mpd_Connection *connection)
667 {
668         while (!connection->doneProcessing && connection->listOks
669                         && !connection->doneListOk) {
670                 mpd_getNextReturnElement(connection);
671         }
672 }
673
674 int mpd_nextListOkCommand(mpd_Connection *connection)
675 {
676         mpd_finishListOkCommand(connection);
677         if (!connection->doneProcessing) {
678                 connection->doneListOk = 0;
679         }
680         if (connection->listOks == 0 || connection->doneProcessing) {
681                 return -1;
682         }
683         return 0;
684 }
685
686 void mpd_sendStatusCommand(mpd_Connection *connection)
687 {
688         mpd_executeCommand(connection, "status\n");
689 }
690
691 mpd_Status *mpd_getStatus(mpd_Connection *connection)
692 {
693         mpd_Status *status;
694
695         /* mpd_executeCommand(connection, "status\n");
696         if (connection->error) {
697                 return NULL;
698         } */
699
700         if (connection->doneProcessing
701                         || (connection->listOks && connection->doneListOk)) {
702                 return NULL;
703         }
704
705         if (!connection->returnElement) {
706                 mpd_getNextReturnElement(connection);
707         }
708
709         status = malloc(sizeof(mpd_Status));
710         status->volume = -1;
711         status->repeat = 0;
712         status->random = 0;
713         status->playlist = -1;
714         status->playlistLength = -1;
715         status->state = -1;
716         status->song = 0;
717         status->songid = 0;
718         status->elapsedTime = 0;
719         status->totalTime = 0;
720         status->bitRate = 0;
721         status->sampleRate = 0;
722         status->bits = 0;
723         status->channels = 0;
724         status->crossfade = -1;
725         status->error = NULL;
726         status->updatingDb = 0;
727
728         if (connection->error) {
729                 free(status);
730                 return NULL;
731         }
732         while (connection->returnElement) {
733                 mpd_ReturnElement *re = connection->returnElement;
734
735                 if (strcmp(re->name, "volume") == 0) {
736                         status->volume = atoi(re->value);
737                 } else if (strcmp(re->name, "repeat") == 0) {
738                         status->repeat = atoi(re->value);
739                 } else if (strcmp(re->name, "random") == 0) {
740                         status->random = atoi(re->value);
741                 } else if (strcmp(re->name, "playlist") == 0) {
742                         status->playlist = strtol(re->value, NULL, 10);
743                 } else if (strcmp(re->name, "playlistlength") == 0) {
744                         status->playlistLength = atoi(re->value);
745                 } else if (strcmp(re->name, "bitrate") == 0) {
746                         status->bitRate = atoi(re->value);
747                 } else if (strcmp(re->name, "state") == 0) {
748                         if (strcmp(re->value, "play") == 0) {
749                                 status->state = MPD_STATUS_STATE_PLAY;
750                         } else if (strcmp(re->value, "stop") == 0) {
751                                 status->state = MPD_STATUS_STATE_STOP;
752                         } else if (strcmp(re->value, "pause") == 0) {
753                                 status->state = MPD_STATUS_STATE_PAUSE;
754                         } else {
755                                 status->state = MPD_STATUS_STATE_UNKNOWN;
756                         }
757                 } else if (strcmp(re->name, "song") == 0) {
758                         status->song = atoi(re->value);
759                 } else if (strcmp(re->name, "songid") == 0) {
760                         status->songid = atoi(re->value);
761                 } else if (strcmp(re->name, "time") == 0) {
762                         char *tok = strchr(re->value, ':');
763
764                         /* the second strchr below is a safety check */
765                         if (tok && (strchr(tok, 0) > (tok + 1))) {
766                                 /* atoi stops at the first non-[0-9] char: */
767                                 status->elapsedTime = atoi(re->value);
768                                 status->totalTime = atoi(tok + 1);
769                         }
770                 } else if (strcmp(re->name, "error") == 0) {
771                         status->error = strdup(re->value);
772                 } else if (strcmp(re->name, "xfade") == 0) {
773                         status->crossfade = atoi(re->value);
774                 } else if (strcmp(re->name, "updating_db") == 0) {
775                         status->updatingDb = atoi(re->value);
776                 } else if (strcmp(re->name, "audio") == 0) {
777                         char *tok = strchr(re->value, ':');
778
779                         if (tok && (strchr(tok, 0) > (tok + 1))) {
780                                 status->sampleRate = atoi(re->value);
781                                 status->bits = atoi(++tok);
782                                 tok = strchr(tok, ':');
783                                 if (tok && (strchr(tok, 0) > (tok + 1))) {
784                                         status->channels = atoi(tok + 1);
785                                 }
786                         }
787                 }
788
789                 mpd_getNextReturnElement(connection);
790                 if (connection->error) {
791                         free(status);
792                         return NULL;
793                 }
794         }
795
796         if (connection->error) {
797                 free(status);
798                 return NULL;
799         } else if (status->state < 0) {
800                 strcpy(connection->errorStr, "state not found");
801                 connection->error = 1;
802                 free(status);
803                 return NULL;
804         }
805
806         return status;
807 }
808
809 void mpd_freeStatus(mpd_Status *status)
810 {
811         if (status->error) {
812                 free(status->error);
813         }
814         free(status);
815 }
816
817 void mpd_sendStatsCommand(mpd_Connection *connection)
818 {
819         mpd_executeCommand(connection, "stats\n");
820 }
821
822 mpd_Stats *mpd_getStats(mpd_Connection *connection)
823 {
824         mpd_Stats *stats;
825
826         /* mpd_executeCommand(connection, "stats\n");
827         if (connection->error) {
828                 return NULL;
829         } */
830
831         if (connection->doneProcessing
832                         || (connection->listOks && connection->doneListOk)) {
833                 return NULL;
834         }
835
836         if (!connection->returnElement) {
837                 mpd_getNextReturnElement(connection);
838         }
839
840         stats = malloc(sizeof(mpd_Stats));
841         stats->numberOfArtists = 0;
842         stats->numberOfAlbums = 0;
843         stats->numberOfSongs = 0;
844         stats->uptime = 0;
845         stats->dbUpdateTime = 0;
846         stats->playTime = 0;
847         stats->dbPlayTime = 0;
848
849         if (connection->error) {
850                 free(stats);
851                 return NULL;
852         }
853         while (connection->returnElement) {
854                 mpd_ReturnElement *re = connection->returnElement;
855
856                 if (strcmp(re->name, "artists") == 0) {
857                         stats->numberOfArtists = atoi(re->value);
858                 } else if (strcmp(re->name, "albums") == 0) {
859                         stats->numberOfAlbums = atoi(re->value);
860                 } else if (strcmp(re->name, "songs") == 0) {
861                         stats->numberOfSongs = atoi(re->value);
862                 } else if (strcmp(re->name, "uptime") == 0) {
863                         stats->uptime = strtol(re->value, NULL, 10);
864                 } else if (strcmp(re->name, "db_update") == 0) {
865                         stats->dbUpdateTime = strtol(re->value, NULL, 10);
866                 } else if (strcmp(re->name, "playtime") == 0) {
867                         stats->playTime = strtol(re->value, NULL, 10);
868                 } else if (strcmp(re->name, "db_playtime") == 0) {
869                         stats->dbPlayTime = strtol(re->value, NULL, 10);
870                 }
871
872                 mpd_getNextReturnElement(connection);
873                 if (connection->error) {
874                         free(stats);
875                         return NULL;
876                 }
877         }
878
879         if (connection->error) {
880                 free(stats);
881                 return NULL;
882         }
883
884         return stats;
885 }
886
887 void mpd_freeStats(mpd_Stats *stats)
888 {
889         free(stats);
890 }
891
892 mpd_SearchStats *mpd_getSearchStats(mpd_Connection *connection)
893 {
894         mpd_SearchStats *stats;
895         mpd_ReturnElement *re;
896
897         if (connection->doneProcessing
898                         || (connection->listOks && connection->doneListOk)) {
899                 return NULL;
900         }
901
902         if (!connection->returnElement) {
903                 mpd_getNextReturnElement(connection);
904         }
905
906         if (connection->error) {
907                 return NULL;
908         }
909
910         stats = malloc(sizeof(mpd_SearchStats));
911         stats->numberOfSongs = 0;
912         stats->playTime = 0;
913
914         while (connection->returnElement) {
915                 re = connection->returnElement;
916
917                 if (strcmp(re->name, "songs") == 0) {
918                         stats->numberOfSongs = atoi(re->value);
919                 } else if (strcmp(re->name, "playtime") == 0) {
920                         stats->playTime = strtol(re->value, NULL, 10);
921                 }
922
923                 mpd_getNextReturnElement(connection);
924                 if (connection->error) {
925                         free(stats);
926                         return NULL;
927                 }
928         }
929
930         if (connection->error) {
931                 free(stats);
932                 return NULL;
933         }
934
935         return stats;
936 }
937
938 void mpd_freeSearchStats(mpd_SearchStats *stats)
939 {
940         free(stats);
941 }
942
943 static void mpd_initSong(mpd_Song *song)
944 {
945         song->file = NULL;
946         song->artist = NULL;
947         song->album = NULL;
948         song->track = NULL;
949         song->title = NULL;
950         song->name = NULL;
951         song->date = NULL;
952         /* added by Qball */
953         song->genre = NULL;
954         song->composer = NULL;
955         song->performer = NULL;
956         song->disc = NULL;
957         song->comment = NULL;
958
959         song->time = MPD_SONG_NO_TIME;
960         song->pos = MPD_SONG_NO_NUM;
961         song->id = MPD_SONG_NO_ID;
962 }
963
964 static void mpd_finishSong(mpd_Song *song)
965 {
966         if (song->file) {
967                 free(song->file);
968         }
969         if (song->artist) {
970                 free(song->artist);
971         }
972         if (song->album) {
973                 free(song->album);
974         }
975         if (song->title) {
976                 free(song->title);
977         }
978         if (song->track) {
979                 free(song->track);
980         }
981         if (song->name) {
982                 free(song->name);
983         }
984         if (song->date) {
985                 free(song->date);
986         }
987         if (song->genre) {
988                 free(song->genre);
989         }
990         if (song->composer) {
991                 free(song->composer);
992         }
993         if (song->disc) {
994                 free(song->disc);
995         }
996         if (song->comment) {
997                 free(song->comment);
998         }
999 }
1000
1001 mpd_Song *mpd_newSong(void)
1002 {
1003         mpd_Song *ret = malloc(sizeof(mpd_Song));
1004
1005         mpd_initSong(ret);
1006
1007         return ret;
1008 }
1009
1010 void mpd_freeSong(mpd_Song *song)
1011 {
1012         mpd_finishSong(song);
1013         free(song);
1014 }
1015
1016 mpd_Song *mpd_songDup(mpd_Song *song)
1017 {
1018         mpd_Song *ret = mpd_newSong();
1019
1020         if (song->file) {
1021                 ret->file = strdup(song->file);
1022         }
1023         if (song->artist) {
1024                 ret->artist = strdup(song->artist);
1025         }
1026         if (song->album) {
1027                 ret->album = strdup(song->album);
1028         }
1029         if (song->title) {
1030                 ret->title = strdup(song->title);
1031         }
1032         if (song->track) {
1033                 ret->track = strdup(song->track);
1034         }
1035         if (song->name) {
1036                 ret->name = strdup(song->name);
1037         }
1038         if (song->date) {
1039                 ret->date = strdup(song->date);
1040         }
1041         if (song->genre) {
1042                 ret->genre = strdup(song->genre);
1043         }
1044         if (song->composer) {
1045                 ret->composer = strdup(song->composer);
1046         }
1047         if (song->disc) {
1048                 ret->disc = strdup(song->disc);
1049         }
1050         if (song->comment) {
1051                 ret->comment = strdup(song->comment);
1052         }
1053         ret->time = song->time;
1054         ret->pos = song->pos;
1055         ret->id = song->id;
1056
1057         return ret;
1058 }
1059
1060 static void mpd_initDirectory(mpd_Directory *directory)
1061 {
1062         directory->path = NULL;
1063 }
1064
1065 static void mpd_finishDirectory(mpd_Directory *directory)
1066 {
1067         if (directory->path) {
1068                 free(directory->path);
1069         }
1070 }
1071
1072 mpd_Directory *mpd_newDirectory(void)
1073 {
1074         mpd_Directory *directory = malloc(sizeof(mpd_Directory));
1075
1076         mpd_initDirectory(directory);
1077
1078         return directory;
1079 }
1080
1081 void mpd_freeDirectory(mpd_Directory *directory)
1082 {
1083         mpd_finishDirectory(directory);
1084
1085         free(directory);
1086 }
1087
1088 mpd_Directory *mpd_directoryDup(mpd_Directory *directory)
1089 {
1090         mpd_Directory *ret = mpd_newDirectory();
1091
1092         if (directory->path) {
1093                 ret->path = strdup(directory->path);
1094         }
1095
1096         return ret;
1097 }
1098
1099 static void mpd_initPlaylistFile(mpd_PlaylistFile *playlist)
1100 {
1101         playlist->path = NULL;
1102 }
1103
1104 static void mpd_finishPlaylistFile(mpd_PlaylistFile *playlist)
1105 {
1106         if (playlist->path) {
1107                 free(playlist->path);
1108         }
1109 }
1110
1111 mpd_PlaylistFile *mpd_newPlaylistFile(void)
1112 {
1113         mpd_PlaylistFile *playlist = malloc(sizeof(mpd_PlaylistFile));
1114
1115         mpd_initPlaylistFile(playlist);
1116
1117         return playlist;
1118 }
1119
1120 void mpd_freePlaylistFile(mpd_PlaylistFile *playlist)
1121 {
1122         mpd_finishPlaylistFile(playlist);
1123         free(playlist);
1124 }
1125
1126 mpd_PlaylistFile *mpd_playlistFileDup(mpd_PlaylistFile *playlist)
1127 {
1128         mpd_PlaylistFile *ret = mpd_newPlaylistFile();
1129
1130         if (playlist->path) {
1131                 ret->path = strdup(playlist->path);
1132         }
1133
1134         return ret;
1135 }
1136
1137 static void mpd_initInfoEntity(mpd_InfoEntity *entity)
1138 {
1139         entity->info.directory = NULL;
1140 }
1141
1142 static void mpd_finishInfoEntity(mpd_InfoEntity *entity)
1143 {
1144         if (entity->info.directory) {
1145                 if (entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1146                         mpd_freeDirectory(entity->info.directory);
1147                 } else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1148                         mpd_freeSong(entity->info.song);
1149                 } else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1150                         mpd_freePlaylistFile(entity->info.playlistFile);
1151                 }
1152         }
1153 }
1154
1155 mpd_InfoEntity *mpd_newInfoEntity(void)
1156 {
1157         mpd_InfoEntity *entity = malloc(sizeof(mpd_InfoEntity));
1158
1159         mpd_initInfoEntity(entity);
1160
1161         return entity;
1162 }
1163
1164 void mpd_freeInfoEntity(mpd_InfoEntity *entity)
1165 {
1166         mpd_finishInfoEntity(entity);
1167         free(entity);
1168 }
1169
1170 static void mpd_sendInfoCommand(mpd_Connection *connection, char *command)
1171 {
1172         mpd_executeCommand(connection, command);
1173 }
1174
1175 mpd_InfoEntity *mpd_getNextInfoEntity(mpd_Connection *connection)
1176 {
1177         mpd_InfoEntity *entity = NULL;
1178
1179         if (connection->doneProcessing
1180                         || (connection->listOks && connection->doneListOk)) {
1181                 return NULL;
1182         }
1183
1184         if (!connection->returnElement) {
1185                 mpd_getNextReturnElement(connection);
1186         }
1187
1188         if (connection->returnElement) {
1189                 if (strcmp(connection->returnElement->name, "file") == 0) {
1190                         entity = mpd_newInfoEntity();
1191                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1192                         entity->info.song = mpd_newSong();
1193                         entity->info.song->file = strdup(connection->returnElement->value);
1194                 } else if (strcmp(connection->returnElement->name, "directory") == 0) {
1195                         entity = mpd_newInfoEntity();
1196                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1197                         entity->info.directory = mpd_newDirectory();
1198                         entity->info.directory->path =
1199                                 strdup(connection->returnElement->value);
1200                 } else if (strcmp(connection->returnElement->name, "playlist") == 0) {
1201                         entity = mpd_newInfoEntity();
1202                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1203                         entity->info.playlistFile = mpd_newPlaylistFile();
1204                         entity->info.playlistFile->path =
1205                                 strdup(connection->returnElement->value);
1206                 } else if (strcmp(connection->returnElement->name, "cpos") == 0) {
1207                         entity = mpd_newInfoEntity();
1208                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1209                         entity->info.song = mpd_newSong();
1210                         entity->info.song->pos = atoi(connection->returnElement->value);
1211                 } else {
1212                         connection->error = 1;
1213                         strcpy(connection->errorStr, "problem parsing song info");
1214                         return NULL;
1215                 }
1216         } else {
1217                 return NULL;
1218         }
1219
1220         mpd_getNextReturnElement(connection);
1221         while (connection->returnElement) {
1222                 mpd_ReturnElement *re = connection->returnElement;
1223
1224                 if (strcmp(re->name, "file") == 0) {
1225                         return entity;
1226                 } else if (strcmp(re->name, "directory") == 0) {
1227                         return entity;
1228                 } else if (strcmp(re->name, "playlist") == 0) {
1229                         return entity;
1230                 } else if (strcmp(re->name, "cpos") == 0) {
1231                         return entity;
1232                 }
1233
1234                 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG && strlen(re->value)) {
1235                         if (!entity->info.song->artist
1236                                         && strcmp(re->name, "Artist") == 0) {
1237                                 entity->info.song->artist = strdup(re->value);
1238                         } else if (!entity->info.song->album
1239                                         && strcmp(re->name, "Album") == 0) {
1240                                 entity->info.song->album = strdup(re->value);
1241                         } else if (!entity->info.song->title
1242                                         && strcmp(re->name, "Title") == 0) {
1243                                 entity->info.song->title = strdup(re->value);
1244                         } else if (!entity->info.song->track
1245                                         && strcmp(re->name, "Track") == 0) {
1246                                 entity->info.song->track = strdup(re->value);
1247                         } else if (!entity->info.song->name
1248                                         && strcmp(re->name, "Name") == 0) {
1249                                 entity->info.song->name = strdup(re->value);
1250                         } else if (entity->info.song->time == MPD_SONG_NO_TIME
1251                                         && strcmp(re->name, "Time") == 0) {
1252                                 entity->info.song->time = atoi(re->value);
1253                         } else if (entity->info.song->pos == MPD_SONG_NO_NUM
1254                                         && strcmp(re->name, "Pos") == 0) {
1255                                 entity->info.song->pos = atoi(re->value);
1256                         } else if (entity->info.song->id == MPD_SONG_NO_ID
1257                                         && strcmp(re->name, "Id") == 0) {
1258                                 entity->info.song->id = atoi(re->value);
1259                         } else if (!entity->info.song->date
1260                                         && strcmp(re->name, "Date") == 0) {
1261                                 entity->info.song->date = strdup(re->value);
1262                         } else if (!entity->info.song->genre
1263                                         && strcmp(re->name, "Genre") == 0) {
1264                                 entity->info.song->genre = strdup(re->value);
1265                         } else if (!entity->info.song->composer
1266                                         && strcmp(re->name, "Composer") == 0) {
1267                                 entity->info.song->composer = strdup(re->value);
1268                         } else if (!entity->info.song->performer
1269                                         && strcmp(re->name, "Performer") == 0) {
1270                                 entity->info.song->performer = strdup(re->value);
1271                         } else if (!entity->info.song->disc
1272                                         && strcmp(re->name, "Disc") == 0) {
1273                                 entity->info.song->disc = strdup(re->value);
1274                         } else if (!entity->info.song->comment
1275                                         && strcmp(re->name, "Comment") == 0) {
1276                                 entity->info.song->comment = strdup(re->value);
1277                         }
1278                 } else if (entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1279                 } else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1280                 }
1281
1282                 mpd_getNextReturnElement(connection);
1283         }
1284
1285         return entity;
1286 }
1287
1288 static char *mpd_getNextReturnElementNamed(mpd_Connection *connection,
1289                 const char *name)
1290 {
1291         if (connection->doneProcessing
1292                         || (connection->listOks && connection->doneListOk)) {
1293                 return NULL;
1294         }
1295
1296         mpd_getNextReturnElement(connection);
1297         while (connection->returnElement) {
1298                 mpd_ReturnElement *re = connection->returnElement;
1299
1300                 if (strcmp(re->name, name) == 0) {
1301                         return strdup(re->value);
1302                 }
1303                 mpd_getNextReturnElement(connection);
1304         }
1305
1306         return NULL;
1307 }
1308
1309 char *mpd_getNextTag(mpd_Connection *connection, int type)
1310 {
1311         if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES
1312                         || type == MPD_TAG_ITEM_ANY) {
1313                 return NULL;
1314         }
1315         if (type == MPD_TAG_ITEM_FILENAME) {
1316                 return mpd_getNextReturnElementNamed(connection, "file");
1317         }
1318         return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1319 }
1320
1321 char *mpd_getNextArtist(mpd_Connection *connection)
1322 {
1323         return mpd_getNextReturnElementNamed(connection, "Artist");
1324 }
1325
1326 char *mpd_getNextAlbum(mpd_Connection *connection)
1327 {
1328         return mpd_getNextReturnElementNamed(connection, "Album");
1329 }
1330
1331 void mpd_sendPlaylistInfoCommand(mpd_Connection *connection, int songPos)
1332 {
1333         int len = strlen("playlistinfo") + 2 + INTLEN + 3;
1334         char *string = malloc(len);
1335
1336         snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1337         mpd_sendInfoCommand(connection, string);
1338         free(string);
1339 }
1340
1341 void mpd_sendPlaylistIdCommand(mpd_Connection *connection, int id)
1342 {
1343         int len = strlen("playlistid") + 2 + INTLEN + 3;
1344         char *string = malloc(len);
1345
1346         snprintf(string, len, "playlistid \"%i\"\n", id);
1347         mpd_sendInfoCommand(connection, string);
1348         free(string);
1349 }
1350
1351 void mpd_sendPlChangesCommand(mpd_Connection *connection, long long playlist)
1352 {
1353         int len = strlen("plchanges") + 2 + LONGLONGLEN + 3;
1354         char *string = malloc(len);
1355
1356         snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1357         mpd_sendInfoCommand(connection, string);
1358         free(string);
1359 }
1360
1361 void mpd_sendPlChangesPosIdCommand(mpd_Connection *connection,
1362                 long long playlist)
1363 {
1364         int len = strlen("plchangesposid") + 2 + LONGLONGLEN + 3;
1365         char *string = malloc(len);
1366
1367         snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1368         mpd_sendInfoCommand(connection, string);
1369         free(string);
1370 }
1371
1372 void mpd_sendListallCommand(mpd_Connection *connection, const char *dir)
1373 {
1374         char *sDir = mpd_sanitizeArg(dir);
1375         int len = strlen("listall") + 2 + strlen(sDir) + 3;
1376         char *string = malloc(len);
1377
1378         snprintf(string, len, "listall \"%s\"\n", sDir);
1379         mpd_sendInfoCommand(connection, string);
1380         free(string);
1381         free(sDir);
1382 }
1383
1384 void mpd_sendListallInfoCommand(mpd_Connection *connection, const char *dir)
1385 {
1386         char *sDir = mpd_sanitizeArg(dir);
1387         int len = strlen("listallinfo") + 2 + strlen(sDir) + 3;
1388         char *string = malloc(len);
1389
1390         snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1391         mpd_sendInfoCommand(connection, string);
1392         free(string);
1393         free(sDir);
1394 }
1395
1396 void mpd_sendLsInfoCommand(mpd_Connection *connection, const char *dir)
1397 {
1398         char *sDir = mpd_sanitizeArg(dir);
1399         int len = strlen("lsinfo") + 2 + strlen(sDir) + 3;
1400         char *string = malloc(len);
1401
1402         snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1403         mpd_sendInfoCommand(connection, string);
1404         free(string);
1405         free(sDir);
1406 }
1407
1408 void mpd_sendCurrentSongCommand(mpd_Connection *connection)
1409 {
1410         mpd_executeCommand(connection, "currentsong\n");
1411 }
1412
1413 void mpd_sendSearchCommand(mpd_Connection *connection, int table,
1414                 const char *str)
1415 {
1416         mpd_startSearch(connection, 0);
1417         mpd_addConstraintSearch(connection, table, str);
1418         mpd_commitSearch(connection);
1419 }
1420
1421 void mpd_sendFindCommand(mpd_Connection *connection, int table,
1422                 const char *str)
1423 {
1424         mpd_startSearch(connection, 1);
1425         mpd_addConstraintSearch(connection, table, str);
1426         mpd_commitSearch(connection);
1427 }
1428
1429 void mpd_sendListCommand(mpd_Connection *connection, int table,
1430                 const char *arg1)
1431 {
1432         char st[10];
1433         int len;
1434         char *string;
1435
1436         if (table == MPD_TABLE_ARTIST) {
1437                 strcpy(st, "artist");
1438         } else if (table == MPD_TABLE_ALBUM) {
1439                 strcpy(st, "album");
1440         } else {
1441                 connection->error = 1;
1442                 strcpy(connection->errorStr, "unknown table for list");
1443                 return;
1444         }
1445         if (arg1) {
1446                 char *sanitArg1 = mpd_sanitizeArg(arg1);
1447
1448                 len = strlen("list") + 1 + strlen(sanitArg1) + 2 + strlen(st) + 3;
1449                 string = malloc(len);
1450                 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1451                 free(sanitArg1);
1452         } else {
1453                 len = strlen("list") + 1 + strlen(st) + 2;
1454                 string = malloc(len);
1455                 snprintf(string, len, "list %s\n", st);
1456         }
1457         mpd_sendInfoCommand(connection, string);
1458         free(string);
1459 }
1460
1461 void mpd_sendAddCommand(mpd_Connection *connection, const char *file)
1462 {
1463         char *sFile = mpd_sanitizeArg(file);
1464         int len = strlen("add") + 2 + strlen(sFile) + 3;
1465         char *string = malloc(len);
1466
1467         snprintf(string, len, "add \"%s\"\n", sFile);
1468         mpd_executeCommand(connection, string);
1469         free(string);
1470         free(sFile);
1471 }
1472
1473 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1474 {
1475         int retval = -1;
1476         char *sFile = mpd_sanitizeArg(file);
1477         int len = strlen("addid") + 2 + strlen(sFile) + 3;
1478         char *string = malloc(len);
1479
1480         snprintf(string, len, "addid \"%s\"\n", sFile);
1481         mpd_sendInfoCommand(connection, string);
1482         free(string);
1483         free(sFile);
1484
1485         string = mpd_getNextReturnElementNamed(connection, "Id");
1486         if (string) {
1487                 retval = atoi(string);
1488                 free(string);
1489         }
1490
1491         return retval;
1492 }
1493
1494 void mpd_sendDeleteCommand(mpd_Connection *connection, int songPos)
1495 {
1496         int len = strlen("delete") + 2 + INTLEN + 3;
1497         char *string = malloc(len);
1498
1499         snprintf(string, len, "delete \"%i\"\n", songPos);
1500         mpd_sendInfoCommand(connection, string);
1501         free(string);
1502 }
1503
1504 void mpd_sendDeleteIdCommand(mpd_Connection *connection, int id)
1505 {
1506         int len = strlen("deleteid") + 2 + INTLEN + 3;
1507         char *string = malloc(len);
1508
1509         snprintf(string, len, "deleteid \"%i\"\n", id);
1510         mpd_sendInfoCommand(connection, string);
1511         free(string);
1512 }
1513
1514 void mpd_sendSaveCommand(mpd_Connection *connection, const char *name)
1515 {
1516         char *sName = mpd_sanitizeArg(name);
1517         int len = strlen("save") + 2 + strlen(sName) + 3;
1518         char *string = malloc(len);
1519
1520         snprintf(string, len, "save \"%s\"\n", sName);
1521         mpd_executeCommand(connection, string);
1522         free(string);
1523         free(sName);
1524 }
1525
1526 void mpd_sendLoadCommand(mpd_Connection *connection, const char *name)
1527 {
1528         char *sName = mpd_sanitizeArg(name);
1529         int len = strlen("load") + 2 + strlen(sName) + 3;
1530         char *string = malloc(len);
1531
1532         snprintf(string, len, "load \"%s\"\n", sName);
1533         mpd_executeCommand(connection, string);
1534         free(string);
1535         free(sName);
1536 }
1537
1538 void mpd_sendRmCommand(mpd_Connection *connection, const char *name)
1539 {
1540         char *sName = mpd_sanitizeArg(name);
1541         int len = strlen("rm") + 2 + strlen(sName) + 3;
1542         char *string = malloc(len);
1543
1544         snprintf(string, len, "rm \"%s\"\n", sName);
1545         mpd_executeCommand(connection, string);
1546         free(string);
1547         free(sName);
1548 }
1549
1550 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1551                 const char *to)
1552 {
1553         char *sFrom = mpd_sanitizeArg(from);
1554         char *sTo = mpd_sanitizeArg(to);
1555         int len = strlen("rename") + 2 + strlen(sFrom) + 3 + strlen(sTo) + 3;
1556         char *string = malloc(len);
1557
1558         snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1559         mpd_executeCommand(connection, string);
1560         free(string);
1561         free(sFrom);
1562         free(sTo);
1563 }
1564
1565 void mpd_sendShuffleCommand(mpd_Connection *connection)
1566 {
1567         mpd_executeCommand(connection, "shuffle\n");
1568 }
1569
1570 void mpd_sendClearCommand(mpd_Connection *connection)
1571 {
1572         mpd_executeCommand(connection, "clear\n");
1573 }
1574
1575 void mpd_sendPlayCommand(mpd_Connection *connection, int songPos)
1576 {
1577         int len = strlen("play") + 2 + INTLEN + 3;
1578         char *string = malloc(len);
1579
1580         snprintf(string, len, "play \"%i\"\n", songPos);
1581         mpd_sendInfoCommand(connection, string);
1582         free(string);
1583 }
1584
1585 void mpd_sendPlayIdCommand(mpd_Connection *connection, int id)
1586 {
1587         int len = strlen("playid") + 2 + INTLEN + 3;
1588         char *string = malloc(len);
1589
1590         snprintf(string, len, "playid \"%i\"\n", id);
1591         mpd_sendInfoCommand(connection, string);
1592         free(string);
1593 }
1594
1595 void mpd_sendStopCommand(mpd_Connection *connection)
1596 {
1597         mpd_executeCommand(connection, "stop\n");
1598 }
1599
1600 void mpd_sendPauseCommand(mpd_Connection *connection, int pauseMode)
1601 {
1602         int len = strlen("pause") + 2 + INTLEN + 3;
1603         char *string = malloc(len);
1604
1605         snprintf(string, len, "pause \"%i\"\n", pauseMode);
1606         mpd_executeCommand(connection, string);
1607         free(string);
1608 }
1609
1610 void mpd_sendNextCommand(mpd_Connection *connection)
1611 {
1612         mpd_executeCommand(connection, "next\n");
1613 }
1614
1615 void mpd_sendMoveCommand(mpd_Connection *connection, int from, int to)
1616 {
1617         int len = strlen("move") + 2 + INTLEN + 3 + INTLEN + 3;
1618         char *string = malloc(len);
1619
1620         snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1621         mpd_sendInfoCommand(connection, string);
1622         free(string);
1623 }
1624
1625 void mpd_sendMoveIdCommand(mpd_Connection *connection, int id, int to)
1626 {
1627         int len = strlen("moveid") + 2 + INTLEN + 3 + INTLEN + 3;
1628         char *string = malloc(len);
1629
1630         snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1631         mpd_sendInfoCommand(connection, string);
1632         free(string);
1633 }
1634
1635 void mpd_sendSwapCommand(mpd_Connection *connection, int song1, int song2)
1636 {
1637         int len = strlen("swap") + 2 + INTLEN + 3 + INTLEN + 3;
1638         char *string = malloc(len);
1639
1640         snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1641         mpd_sendInfoCommand(connection, string);
1642         free(string);
1643 }
1644
1645 void mpd_sendSwapIdCommand(mpd_Connection *connection, int id1, int id2)
1646 {
1647         int len = strlen("swapid") + 2 + INTLEN + 3 + INTLEN + 3;
1648         char *string = malloc(len);
1649
1650         snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1651         mpd_sendInfoCommand(connection, string);
1652         free(string);
1653 }
1654
1655 void mpd_sendSeekCommand(mpd_Connection *connection, int song, int seek_time)
1656 {
1657         int len = strlen("seek") + 2 + INTLEN + 3 + INTLEN + 3;
1658         char *string = malloc(len);
1659
1660         snprintf(string, len, "seek \"%i\" \"%i\"\n", song, seek_time);
1661         mpd_sendInfoCommand(connection, string);
1662         free(string);
1663 }
1664
1665 void mpd_sendSeekIdCommand(mpd_Connection *connection, int id, int seek_time)
1666 {
1667         int len = strlen("seekid") + 2 + INTLEN + 3 + INTLEN + 3;
1668         char *string = malloc(len);
1669
1670         snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, seek_time);
1671         mpd_sendInfoCommand(connection, string);
1672         free(string);
1673 }
1674
1675 void mpd_sendUpdateCommand(mpd_Connection *connection, char *path)
1676 {
1677         char *sPath = mpd_sanitizeArg(path);
1678         int len = strlen("update") + 2 + strlen(sPath) + 3;
1679         char *string = malloc(len);
1680
1681         snprintf(string, len, "update \"%s\"\n", sPath);
1682         mpd_sendInfoCommand(connection, string);
1683         free(string);
1684         free(sPath);
1685 }
1686
1687 int mpd_getUpdateId(mpd_Connection *connection)
1688 {
1689         char *jobid;
1690         int ret = 0;
1691
1692         jobid = mpd_getNextReturnElementNamed(connection, "updating_db");
1693         if (jobid) {
1694                 ret = atoi(jobid);
1695                 free(jobid);
1696         }
1697
1698         return ret;
1699 }
1700
1701 void mpd_sendPrevCommand(mpd_Connection *connection)
1702 {
1703         mpd_executeCommand(connection, "previous\n");
1704 }
1705
1706 void mpd_sendRepeatCommand(mpd_Connection *connection, int repeatMode)
1707 {
1708         int len = strlen("repeat") + 2 + INTLEN + 3;
1709         char *string = malloc(len);
1710
1711         snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1712         mpd_executeCommand(connection, string);
1713         free(string);
1714 }
1715
1716 void mpd_sendRandomCommand(mpd_Connection *connection, int randomMode)
1717 {
1718         int len = strlen("random") + 2 + INTLEN + 3;
1719         char *string = malloc(len);
1720
1721         snprintf(string, len, "random \"%i\"\n", randomMode);
1722         mpd_executeCommand(connection, string);
1723         free(string);
1724 }
1725
1726 void mpd_sendSetvolCommand(mpd_Connection *connection, int volumeChange)
1727 {
1728         int len = strlen("setvol") + 2 + INTLEN + 3;
1729         char *string = malloc(len);
1730
1731         snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1732         mpd_executeCommand(connection, string);
1733         free(string);
1734 }
1735
1736 void mpd_sendVolumeCommand(mpd_Connection *connection, int volumeChange)
1737 {
1738         int len = strlen("volume") + 2 + INTLEN + 3;
1739         char *string = malloc(len);
1740
1741         snprintf(string, len, "volume \"%i\"\n", volumeChange);
1742         mpd_executeCommand(connection, string);
1743         free(string);
1744 }
1745
1746 void mpd_sendCrossfadeCommand(mpd_Connection *connection, int seconds)
1747 {
1748         int len = strlen("crossfade") + 2 + INTLEN + 3;
1749         char *string = malloc(len);
1750
1751         snprintf(string, len, "crossfade \"%i\"\n", seconds);
1752         mpd_executeCommand(connection, string);
1753         free(string);
1754 }
1755
1756 void mpd_sendPasswordCommand(mpd_Connection *connection, const char *pass)
1757 {
1758         char *sPass = mpd_sanitizeArg(pass);
1759         int len = strlen("password") + 2 + strlen(sPass) + 3;
1760         char *string = malloc(len);
1761
1762         snprintf(string, len, "password \"%s\"\n", sPass);
1763         mpd_executeCommand(connection, string);
1764         free(string);
1765         free(sPass);
1766 }
1767
1768 void mpd_sendCommandListBegin(mpd_Connection *connection)
1769 {
1770         if (connection->commandList) {
1771                 strcpy(connection->errorStr, "already in command list mode");
1772                 connection->error = 1;
1773                 return;
1774         }
1775         connection->commandList = COMMAND_LIST;
1776         mpd_executeCommand(connection, "command_list_begin\n");
1777 }
1778
1779 void mpd_sendCommandListOkBegin(mpd_Connection *connection)
1780 {
1781         if (connection->commandList) {
1782                 strcpy(connection->errorStr, "already in command list mode");
1783                 connection->error = 1;
1784                 return;
1785         }
1786         connection->commandList = COMMAND_LIST_OK;
1787         mpd_executeCommand(connection, "command_list_ok_begin\n");
1788         connection->listOks = 0;
1789 }
1790
1791 void mpd_sendCommandListEnd(mpd_Connection *connection)
1792 {
1793         if (!connection->commandList) {
1794                 strcpy(connection->errorStr, "not in command list mode");
1795                 connection->error = 1;
1796                 return;
1797         }
1798         connection->commandList = 0;
1799         mpd_executeCommand(connection, "command_list_end\n");
1800 }
1801
1802 void mpd_sendOutputsCommand(mpd_Connection *connection)
1803 {
1804         mpd_executeCommand(connection, "outputs\n");
1805 }
1806
1807 mpd_OutputEntity *mpd_getNextOutput(mpd_Connection *connection)
1808 {
1809         mpd_OutputEntity *output = NULL;
1810
1811         if (connection->doneProcessing
1812                         || (connection->listOks && connection->doneListOk)) {
1813                 return NULL;
1814         }
1815
1816         if (connection->error) {
1817                 return NULL;
1818         }
1819
1820         output = malloc(sizeof(mpd_OutputEntity));
1821         output->id = -10;
1822         output->name = NULL;
1823         output->enabled = 0;
1824
1825         if (!connection->returnElement) {
1826                 mpd_getNextReturnElement(connection);
1827         }
1828
1829         while (connection->returnElement) {
1830                 mpd_ReturnElement *re = connection->returnElement;
1831
1832                 if (strcmp(re->name, "outputid") == 0) {
1833                         if (output != NULL && output->id >= 0) {
1834                                 return output;
1835                         }
1836                         output->id = atoi(re->value);
1837                 } else if (strcmp(re->name, "outputname") == 0) {
1838                         output->name = strdup(re->value);
1839                 } else if (strcmp(re->name, "outputenabled") == 0) {
1840                         output->enabled = atoi(re->value);
1841                 }
1842
1843                 mpd_getNextReturnElement(connection);
1844                 if (connection->error) {
1845                         free(output);
1846                         return NULL;
1847                 }
1848         }
1849
1850         return output;
1851 }
1852
1853 void mpd_sendEnableOutputCommand(mpd_Connection *connection, int outputId)
1854 {
1855         int len = strlen("enableoutput") + 2 + INTLEN + 3;
1856         char *string = malloc(len);
1857
1858         snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1859         mpd_executeCommand(connection, string);
1860         free(string);
1861 }
1862
1863 void mpd_sendDisableOutputCommand(mpd_Connection *connection, int outputId)
1864 {
1865         int len = strlen("disableoutput") + 2 + INTLEN + 3;
1866         char *string = malloc(len);
1867
1868         snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1869         mpd_executeCommand(connection, string);
1870         free(string);
1871 }
1872
1873 void mpd_freeOutputElement(mpd_OutputEntity *output)
1874 {
1875         free(output->name);
1876         free(output);
1877 }
1878
1879 /** odd naming, but it gets the not allowed commands */
1880 void mpd_sendNotCommandsCommand(mpd_Connection *connection)
1881 {
1882         mpd_executeCommand(connection, "notcommands\n");
1883 }
1884
1885 /** odd naming, but it gets the allowed commands */
1886 void mpd_sendCommandsCommand(mpd_Connection *connection)
1887 {
1888         mpd_executeCommand(connection, "commands\n");
1889 }
1890
1891 /** Get the next returned command */
1892 char *mpd_getNextCommand(mpd_Connection *connection)
1893 {
1894         return mpd_getNextReturnElementNamed(connection, "command");
1895 }
1896
1897 void mpd_sendUrlHandlersCommand(mpd_Connection *connection)
1898 {
1899         mpd_executeCommand(connection, "urlhandlers\n");
1900 }
1901
1902 char *mpd_getNextHandler(mpd_Connection *connection)
1903 {
1904         return mpd_getNextReturnElementNamed(connection, "handler");
1905 }
1906
1907 void mpd_sendTagTypesCommand(mpd_Connection *connection)
1908 {
1909         mpd_executeCommand(connection, "tagtypes\n");
1910 }
1911
1912 char *mpd_getNextTagType(mpd_Connection *connection)
1913 {
1914         return mpd_getNextReturnElementNamed(connection, "tagtype");
1915 }
1916
1917 void mpd_startSearch(mpd_Connection *connection, int exact)
1918 {
1919         if (connection->request) {
1920                 strcpy(connection->errorStr, "search already in progress");
1921                 connection->error = 1;
1922                 return;
1923         }
1924
1925         if (exact) {
1926                 connection->request = strdup("find");
1927         } else {
1928                 connection->request = strdup("search");
1929         }
1930 }
1931
1932 void mpd_startStatsSearch(mpd_Connection *connection)
1933 {
1934         if (connection->request) {
1935                 strcpy(connection->errorStr, "search already in progress");
1936                 connection->error = 1;
1937                 return;
1938         }
1939
1940         connection->request = strdup("count");
1941 }
1942
1943 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1944 {
1945         if (connection->request) {
1946                 strcpy(connection->errorStr, "search already in progress");
1947                 connection->error = 1;
1948                 return;
1949         }
1950
1951         if (exact) {
1952                 connection->request = strdup("playlistfind");
1953         } else {
1954                 connection->request = strdup("playlistsearch");
1955         }
1956 }
1957
1958 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1959 {
1960         const char *strtype;
1961         int len;
1962
1963         if (connection->request) {
1964                 strcpy(connection->errorStr, "search already in progress");
1965                 connection->error = 1;
1966                 return;
1967         }
1968
1969         if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1970                 strcpy(connection->errorStr, "invalid type specified");
1971                 connection->error = 1;
1972                 return;
1973         }
1974
1975         strtype = mpdTagItemKeys[type];
1976
1977         len = 5 + strlen(strtype) + 1;
1978         connection->request = malloc(len);
1979
1980         snprintf(connection->request, len, "list %c%s", tolower(strtype[0]),
1981                 strtype + 1);
1982 }
1983
1984 void mpd_addConstraintSearch(mpd_Connection *connection, int type,
1985                 const char *name)
1986 {
1987         const char *strtype;
1988         char *arg;
1989         int len;
1990         char *string;
1991
1992         if (!connection->request) {
1993                 strcpy(connection->errorStr, "no search in progress");
1994                 connection->error = 1;
1995                 return;
1996         }
1997
1998         if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1999                 strcpy(connection->errorStr, "invalid type specified");
2000                 connection->error = 1;
2001                 return;
2002         }
2003
2004         if (name == NULL) {
2005                 strcpy(connection->errorStr, "no name specified");
2006                 connection->error = 1;
2007                 return;
2008         }
2009
2010         string = strdup(connection->request);
2011         strtype = mpdTagItemKeys[type];
2012         arg = mpd_sanitizeArg(name);
2013
2014         len = strlen(string) + 1 + strlen(strtype) + 2 + strlen(arg) + 2;
2015         connection->request = realloc(connection->request, len);
2016         snprintf(connection->request, len, "%s %c%s \"%s\"", string,
2017                 tolower(strtype[0]), strtype + 1, arg);
2018
2019         free(string);
2020         free(arg);
2021 }
2022
2023 void mpd_commitSearch(mpd_Connection *connection)
2024 {
2025         int len;
2026
2027         if (!connection->request) {
2028                 strcpy(connection->errorStr, "no search in progress");
2029                 connection->error = 1;
2030                 return;
2031         }
2032
2033         len = strlen(connection->request) + 2;
2034         connection->request = realloc(connection->request, len);
2035         connection->request[len - 2] = '\n';
2036         connection->request[len - 1] = '\0';
2037         mpd_sendInfoCommand(connection, connection->request);
2038
2039         free(connection->request);
2040         connection->request = NULL;
2041 }
2042
2043 /**
2044  * @param connection    a MpdConnection
2045  * @param path                  the path to the playlist.
2046  *
2047  * List the content, with full metadata, of a stored playlist. */
2048 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
2049 {
2050         char *arg = mpd_sanitizeArg(path);
2051         int len = strlen("listplaylistinfo") + 2 + strlen(arg) + 3;
2052         char *query = malloc(len);
2053
2054         snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
2055         mpd_sendInfoCommand(connection, query);
2056         free(arg);
2057         free(query);
2058 }
2059
2060 /**
2061  * @param connection    a MpdConnection
2062  * @param path                  the path to the playlist.
2063  *
2064  * List the content of a stored playlist. */
2065 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
2066 {
2067         char *arg = mpd_sanitizeArg(path);
2068         int len = strlen("listplaylist") + 2 + strlen(arg) + 3;
2069         char *query = malloc(len);
2070
2071         snprintf(query, len, "listplaylist \"%s\"\n", arg);
2072         mpd_sendInfoCommand(connection, query);
2073         free(arg);
2074         free(query);
2075 }
2076
2077 void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path)
2078 {
2079         char *sPath = mpd_sanitizeArg(path);
2080         int len = strlen("playlistclear") + 2 + strlen(sPath) + 3;
2081         char *string = malloc(len);
2082
2083         snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2084         mpd_executeCommand(connection, string);
2085         free(sPath);
2086         free(string);
2087 }
2088
2089 void mpd_sendPlaylistAddCommand(mpd_Connection *connection, char *playlist,
2090                 char *path)
2091 {
2092         char *sPlaylist = mpd_sanitizeArg(playlist);
2093         char *sPath = mpd_sanitizeArg(path);
2094         int len = strlen("playlistadd") + 2 + strlen(sPlaylist) + 3 +
2095                 strlen(sPath) + 3;
2096         char *string = malloc(len);
2097
2098         snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2099         mpd_executeCommand(connection, string);
2100         free(sPlaylist);
2101         free(sPath);
2102         free(string);
2103 }
2104
2105 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection, char *playlist,
2106                 int from, int to)
2107 {
2108         char *sPlaylist = mpd_sanitizeArg(playlist);
2109         int len = strlen("playlistmove") + 2 + strlen(sPlaylist) + 3 + INTLEN +
2110                 3 + INTLEN + 3;
2111         char *string = malloc(len);
2112
2113         snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n", sPlaylist,
2114                 from, to);
2115         mpd_executeCommand(connection, string);
2116         free(sPlaylist);
2117         free(string);
2118 }
2119
2120 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection, char *playlist,
2121                 int pos)
2122 {
2123         char *sPlaylist = mpd_sanitizeArg(playlist);
2124         int len = strlen("playlistdelete") + 2 + strlen(sPlaylist) + 3 +
2125                 INTLEN + 3;
2126         char *string = malloc(len);
2127
2128         snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2129         mpd_executeCommand(connection, string);
2130         free(sPlaylist);
2131         free(string);
2132 }