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