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