Code

Documentation update for 0.11.0
[ncmpc.git] / src / libmpdclient.c
1 /* libmpdclient
2    (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
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.
32 */
34 #include "libmpdclient.h"
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <sys/param.h>
42 #include <string.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
49 #ifndef MPD_NO_IPV6
50 #ifdef AF_INET6
51 #define MPD_HAVE_IPV6
52 #endif
53 #endif
55 #define COMMAND_LIST    1
56 #define COMMAND_LIST_OK 2
58 #ifdef MPD_HAVE_IPV6        
59 int mpd_ipv6Supported() {
60         int s;          
61         s = socket(AF_INET6,SOCK_STREAM,0);
62         if(s == -1) return 0;
63         close(s);       
64         return 1;                       
65 }                       
66 #endif  
69 char * mpd_sanitizeArg(const char * arg) {
70         size_t i;
71         int count=0;
72         char * ret;
74         for(i=0;i<strlen(arg);i++) {
75                 if(arg[i]=='"' || arg[i]=='\\') count++;
76         }
78         ret = malloc(strlen(arg)+count+1);
80         count = 0;
81         for(i=0;i<strlen(arg)+1;i++) {
82                 if(arg[i]=='"' || arg[i]=='\\') {
83                         ret[i+count] = '\\';
84                         count++;
85                 }
86                 ret[i+count] = arg[i];
87         }
89         return ret;
90 }
92 mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
93 {
94         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
96         ret->name = strdup(name);
97         ret->value = strdup(value);
99         return ret;
102 void mpd_freeReturnElement(mpd_ReturnElement * re) {
103         free(re->name);
104         free(re->value);
105         free(re);
108 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
109                 connection->timeout.tv_sec = (int)timeout;
110                 connection->timeout.tv_usec = (int)(timeout*1e6 -
111                                 connection->timeout.tv_sec*1000000+0.5);
114 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
115         int err;
116         struct hostent * he;
117         struct sockaddr * dest;
118 #ifdef HAVE_SOCKLEN_T
119         socklen_t destlen;
120 #else
121         int destlen;
122 #endif
123         struct sockaddr_in sin;
124         char * rt;
125         char * output;
126         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
127         struct timeval tv;
128         fd_set fds;
129 #ifdef MPD_HAVE_IPV6
130         struct sockaddr_in6 sin6;
131 #endif
132         strcpy(connection->buffer,"");
133         connection->buflen = 0;
134         connection->bufstart = 0;
135         strcpy(connection->errorStr,"");
136         connection->error = 0;
137         connection->doneProcessing = 0;
138         connection->commandList = 0;
139         connection->listOks = 0;
140         connection->doneListOk = 0;
141         connection->returnElement = NULL;
143         if(!(he=gethostbyname(host))) {
144                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
145                                 "host \"%s\" not found",host);
146                 connection->error = MPD_ERROR_UNKHOST;
147                 return connection;
148         }
150         memset(&sin,0,sizeof(struct sockaddr_in));
151         /*dest.sin_family = he->h_addrtype;*/
152         sin.sin_family = AF_INET;
153         sin.sin_port = htons(port);
154 #ifdef MPD_HAVE_IPV6
155         memset(&sin6,0,sizeof(struct sockaddr_in6));
156         sin6.sin6_family = AF_INET6;
157         sin6.sin6_port = htons(port);
158 #endif
159         switch(he->h_addrtype) {
160         case AF_INET:
161                 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
162                                 he->h_length);
163                 dest = (struct sockaddr *)&sin;
164                 destlen = sizeof(struct sockaddr_in);
165                 break;
166 #ifdef MPD_HAVE_IPV6
167         case AF_INET6:
168                 if(!mpd_ipv6Supported()) {
169                         strcpy(connection->errorStr,"no IPv6 suuport but a "
170                                         "IPv6 address found\n");
171                         connection->error = MPD_ERROR_SYSTEM;
172                         return connection;
173                 }
174                 memcpy((char *)&sin6.sin6_addr.s6_addr,(char *)he->h_addr,
175                                 he->h_length);
176                 dest = (struct sockaddr *)&sin6;
177                 destlen = sizeof(struct sockaddr_in6);
178                 break;
179 #endif
180         default:
181                 strcpy(connection->errorStr,"address type is not IPv4 or "
182                                 "IPv6\n");
183                 connection->error = MPD_ERROR_SYSTEM;
184                 return connection;
185                 break;
186         }
187         
188         if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
189                 strcpy(connection->errorStr,"problems creating socket");
190                 connection->error = MPD_ERROR_SYSTEM;
191                 return connection;
192         }
194         mpd_setConnectionTimeout(connection,timeout);
196         /* connect stuff */
197         {
198                 int flags = fcntl(connection->sock, F_GETFL, 0);
199                 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
201                 if(connect(connection->sock,dest,destlen)<0 && 
202                                 errno!=EINPROGRESS) 
203                 {
204                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
205                                         "problems connecting to \"%s\" on port"
206                                         " %i",host,port);
207                         connection->error = MPD_ERROR_CONNPORT;
208                         return connection;
209                 }
210         }
212         while(!(rt = strstr(connection->buffer,"\n"))) {
213                 tv.tv_sec = connection->timeout.tv_sec;
214                 tv.tv_usec = connection->timeout.tv_usec;
215                 FD_ZERO(&fds);
216                 FD_SET(connection->sock,&fds);
217                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
218                         int readed;
219                         readed = recv(connection->sock,
220                                 &(connection->buffer[connection->buflen]),
221                                 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
222                         if(readed<=0) {
223                                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
224                                         "problems getting a response from"
225                                         " \"%s\" on port %i",host,
226                                         port);
227                                 connection->error = MPD_ERROR_NORESPONSE;
228                                 return connection;
229                         }
230                         connection->buflen+=readed;
231                         connection->buffer[connection->buflen] = '\0';
232                         tv.tv_sec = connection->timeout.tv_sec;
233                         tv.tv_usec = connection->timeout.tv_usec;
234                 }
235                 else if(err<0) {
236                         switch(errno) {
237                         case EINTR:
238                                 continue;
239                         default:
240                                 snprintf(connection->errorStr,
241                                         MPD_BUFFER_MAX_LENGTH,
242                                         "problems connecting to \"%s\" on port"
243                                         " %i",host,port);
244                                 connection->error = MPD_ERROR_CONNPORT;
245                                 return connection;
246                         }
247                 }
248                 else {
249                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
250                                 "timeout in attempting to get a response from"
251                                  " \"%s\" on port %i",host,port);
252                         connection->error = MPD_ERROR_NORESPONSE;
253                         return connection;
254                 }
255         }
257         *rt = '\0';
258         output = strdup(connection->buffer);
259         strcpy(connection->buffer,rt+1);
260         connection->buflen = strlen(connection->buffer);
262         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
263                 free(output);
264                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
265                                 "mpd not running on port %i on host \"%s\"",
266                                 port,host);
267                 connection->error = MPD_ERROR_NOTMPD;
268                 return connection;
269         }
271         {
272                 char * test;
273                 char * version[3];
274                 char * tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
275                 char * search = ".";
276                 int i;
278                 for(i=0;i<3;i++) {
279                         char * tok;
280                         if(i==3) search = " ";
281                         version[i] = strtok_r(tmp,search,&tok);
282                         if(!version[i]) {
283                                 free(output);
284                                 snprintf(connection->errorStr,
285                                         MPD_BUFFER_MAX_LENGTH,
286                                         "error parsing version number at "
287                                         "\"%s\"",
288                                         &output[strlen(MPD_WELCOME_MESSAGE)]);
289                                 connection->error = MPD_ERROR_NOTMPD;
290                                 return connection;
291                         }
292                         connection->version[i] = strtol(version[i],&test,10);
293                         if(version[i]==test || *test!='\0') {
294                                 free(output);
295                                 snprintf(connection->errorStr,
296                                         MPD_BUFFER_MAX_LENGTH,
297                                         "error parsing version number at "
298                                         "\"%s\"",
299                                         &output[strlen(MPD_WELCOME_MESSAGE)]);
300                                 connection->error = MPD_ERROR_NOTMPD;
301                                 return connection;
302                         }
303                         tmp = NULL;
304                 }
305         }
307         free(output);
309         connection->doneProcessing = 1;
311         return connection;
314 void mpd_clearError(mpd_Connection * connection) {
315         connection->error = 0;
316         connection->errorStr[0] = '\0';
319 void mpd_closeConnection(mpd_Connection * connection) {
320         close(connection->sock);
321         if(connection->returnElement) free(connection->returnElement);
322         free(connection);
325 void mpd_executeCommand(mpd_Connection * connection, char * command) {
326         int ret;
327         struct timeval tv;
328         fd_set fds;
329         char * commandPtr = command;
330         int commandLen = strlen(command);
332         if(!connection->doneProcessing && !connection->commandList) {
333                 strcpy(connection->errorStr,"not done processing current command");
334                 connection->error = 1;
335                 return;
336         }
338         mpd_clearError(connection);
340         FD_ZERO(&fds);
341         FD_SET(connection->sock,&fds);
342         tv.tv_sec = connection->timeout.tv_sec;
343         tv.tv_usec = connection->timeout.tv_usec;
345         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) || 
346                         (ret==-1 && errno==EINTR)) {
347                 ret = send(connection->sock,commandPtr,commandLen,
348                                 MSG_DONTWAIT);
349                 if(ret<=0)
350                 {
351                         if(ret==EAGAIN || ret==EINTR) continue;
352                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
353                                 "problems giving command \"%s\"",command);
354                         connection->error = MPD_ERROR_SENDING;
355                         return;
356                 }
357                 else {
358                         commandPtr+=ret;
359                         commandLen-=ret;
360                 }
362                 if(commandLen<=0) break;
363         }
365         if(commandLen>0) {
366                 perror("");
367                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
368                         "timeout sending command \"%s\"",command);
369                 connection->error = MPD_ERROR_TIMEOUT;
370                 return;
371         }
373         if(!connection->commandList) connection->doneProcessing = 0;
374         else if(connection->commandList == COMMAND_LIST_OK) {
375                 connection->listOks++;
376         }
379 void mpd_getNextReturnElement(mpd_Connection * connection) {
380         char * output = NULL;
381         char * rt = NULL;
382         char * name = NULL;
383         char * value = NULL;
384         fd_set fds;
385         struct timeval tv;
386         char * tok = NULL;
387         int readed;
388         char * bufferCheck = NULL;
389         int err;
391         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
392         connection->returnElement = NULL;
394         if(connection->doneProcessing || (connection->listOks &&
395                         connection->doneListOk)) 
396         {
397                 strcpy(connection->errorStr,"already done processing current command");
398                 connection->error = 1;
399                 return;
400         }
402         bufferCheck = connection->buffer+connection->bufstart;
403         while(connection->bufstart>=connection->buflen || 
404                         !(rt = strstr(bufferCheck,"\n"))) {
405                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
406                         memmove(connection->buffer,
407                                         connection->buffer+
408                                         connection->bufstart,
409                                         connection->buflen-
410                                         connection->bufstart+1);
411                         bufferCheck-=connection->bufstart;
412                         connection->buflen-=connection->bufstart;
413                         connection->bufstart = 0;
414                 }
415                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
416                         strcpy(connection->errorStr,"buffer overrun");
417                         connection->error = MPD_ERROR_BUFFEROVERRUN;
418                         connection->doneProcessing = 1;
419                         connection->doneListOk = 0;
420                         return;
421                 }
422                 bufferCheck+=connection->buflen-connection->bufstart;
423                 tv.tv_sec = connection->timeout.tv_sec;
424                 tv.tv_usec = connection->timeout.tv_usec;
425                 FD_ZERO(&fds);
426                 FD_SET(connection->sock,&fds);
427                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
428                         readed = recv(connection->sock,
429                                 connection->buffer+connection->buflen,
430                                 MPD_BUFFER_MAX_LENGTH-connection->buflen,
431                                 MSG_DONTWAIT);
432                         if(readed<0 && (errno==EAGAIN || errno==EINTR)) {
433                                 continue;
434                         }
435                         if(readed<=0) {
436                                 strcpy(connection->errorStr,"connection"
437                                         " closed");
438                                 connection->error = MPD_ERROR_CONNCLOSED;
439                                 connection->doneProcessing = 1;
440                                 connection->doneListOk = 0;
441                                 return;
442                         }
443                         connection->buflen+=readed;
444                         connection->buffer[connection->buflen] = '\0';
445                 }
446                 else if(err<0 && errno==EINTR) continue;
447                 else {
448                         strcpy(connection->errorStr,"connection timeout");
449                         connection->error = MPD_ERROR_TIMEOUT;
450                         connection->doneProcessing = 1;
451                         connection->doneListOk = 0;
452                         return;
453                 }
454         }
456         *rt = '\0';
457         output = connection->buffer+connection->bufstart;
458         connection->bufstart = rt - connection->buffer + 1;
460         if(strcmp(output,"OK")==0) {
461                 if(connection->listOks > 0) {
462                         strcpy(connection->errorStr, "expected more list_OK's");
463                         connection->error = 1;
464                 }
465                 connection->listOks = 0;
466                 connection->doneProcessing = 1;
467                 connection->doneListOk = 0;
468                 return;
469         }
471         if(strcmp(output, "list_OK") == 0) {
472                 if(!connection->listOks) {
473                         strcpy(connection->errorStr, 
474                                         "got an unexpected list_OK");
475                         connection->error = 1;
476                 }
477                 else {
478                         connection->doneListOk = 1;
479                         connection->listOks--;
480                 }
481                 return;
482         }
484         if(strncmp(output,"ACK",strlen("ACK"))==0) {
485                 char * test;
486                 char * needle;
487                 int val;
488         
489                 strcpy(connection->errorStr, output);
490                 connection->error = MPD_ERROR_ACK;
491                 connection->errorCode = MPD_ACK_ERROR_UNK;
492                 connection->errorAt = MPD_ERROR_AT_UNK;
493                 connection->doneProcessing = 1;
494                 connection->doneListOk = 0;
496                 needle = strchr(output, '[');
497                 if(!needle) return;
498                 val = strtol(needle+1, &test, 10);
499                 if(*test != '@') return;
500                 connection->errorCode = val;
501                 val = strtol(test+1, &test, 10);
502                 if(*test != ']') return;
503                 connection->errorAt = val;
504                 return;
505         }
507         name = strtok_r(output,":",&tok);
508         if(name && (value = strtok_r(NULL,"",&tok)) && value[0]==' ') {
509                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
510         }
511         else {
512                 if(!name || !value) {
513                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
514                                         "error parsing: %s",output);
515                 }
516                 else {
517                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
518                                         "error parsing: %s:%s",name,value);
519                 }
520                 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
521                 connection->error = 1;
522         }
525 void mpd_finishCommand(mpd_Connection * connection) {
526         while(!connection->doneProcessing) {
527                 if(connection->doneListOk) connection->doneListOk = 0;
528                 mpd_getNextReturnElement(connection);
529         }
532 void mpd_finishListOkCommand(mpd_Connection * connection) {
533         while(!connection->doneProcessing && connection->listOks && 
534                         !connection->doneListOk ) 
535         {
536                 mpd_getNextReturnElement(connection);
537         }
540 int mpd_nextListOkCommand(mpd_Connection * connection) {
541         mpd_finishListOkCommand(connection);
542         if(!connection->doneProcessing) connection->doneListOk = 0;
543         if(connection->listOks == 0 || connection->doneProcessing) return -1;
544         return 0;
547 void mpd_sendStatusCommand(mpd_Connection * connection) {
548         mpd_executeCommand(connection,"status\n");
551 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
552         mpd_Status * status;
554         /*mpd_executeCommand(connection,"status\n");
555                 
556         if(connection->error) return NULL;*/
558         if(connection->doneProcessing || (connection->listOks && 
559                         connection->doneListOk))
560         {
561                 return NULL;
562         }
564         if(!connection->returnElement) mpd_getNextReturnElement(connection);
566         status = malloc(sizeof(mpd_Status));
567         status->volume = -1;
568         status->repeat = 0;
569         status->random = 0;
570         status->playlist = -1;
571         status->playlistLength = -1;
572         status->state = -1;
573         status->song = 0;
574         status->elapsedTime = 0;
575         status->totalTime = 0;
576         status->bitRate = 0;
577         status->sampleRate = 0;
578         status->bits = 0;
579         status->channels = 0;
580         status->crossfade = -1;
581         status->error = NULL;
582         status->updatingDb = 0;
584         if(connection->error) {
585                 free(status);
586                 return NULL;
587         }
588         while(connection->returnElement) {
589                 mpd_ReturnElement * re = connection->returnElement;
590                 if(strcmp(re->name,"volume")==0) {
591                         status->volume = atoi(re->value);
592                 }
593                 else if(strcmp(re->name,"repeat")==0) {
594                         status->repeat = atoi(re->value);
595                 }
596                 else if(strcmp(re->name,"random")==0) {
597                         status->random = atoi(re->value);
598                 }
599                 else if(strcmp(re->name,"playlist")==0) {
600                         status->playlist = strtol(re->value,NULL,10);
601                 }
602                 else if(strcmp(re->name,"playlistlength")==0) {
603                         status->playlistLength = atoi(re->value);
604                 }
605                 else if(strcmp(re->name,"bitrate")==0) {
606                         status->bitRate = atoi(re->value);
607                 }
608                 else if(strcmp(re->name,"state")==0) {
609                         if(strcmp(re->value,"play")==0) {
610                                 status->state = MPD_STATUS_STATE_PLAY;
611                         }
612                         else if(strcmp(re->value,"stop")==0) {
613                                 status->state = MPD_STATUS_STATE_STOP;
614                         }
615                         else if(strcmp(re->value,"pause")==0) {
616                                 status->state = MPD_STATUS_STATE_PAUSE;
617                         }
618                         else {
619                                 status->state = MPD_STATUS_STATE_UNKNOWN;
620                         }
621                 }
622                 else if(strcmp(re->name,"song")==0) {
623                         status->song = atoi(re->value);
624                 }
625                 else if(strcmp(re->name,"songid")==0) {
626                         status->songid = atoi(re->value);
627                 }
628                 else if(strcmp(re->name,"time")==0) {
629                         char * tok;
630                         char * copy;
631                         char * temp;
632                         copy = strdup(re->value);
633                         temp = strtok_r(copy,":",&tok);
634                         if(temp) {
635                                 status->elapsedTime = atoi(temp);
636                                 temp = strtok_r(NULL,"",&tok);
637                                 if(temp) status->totalTime = atoi(temp);
638                         }
639                         free(copy);
640                 }
641                 else if(strcmp(re->name,"error")==0) {
642                         status->error = strdup(re->value);
643                 }
644                 else if(strcmp(re->name,"xfade")==0) {
645                         status->crossfade = atoi(re->value);
646                 }
647                 else if(strcmp(re->name,"updating_db")==0) {
648                         status->updatingDb = atoi(re->value);
649                 }
650                 else if(strcmp(re->name,"audio")==0) {
651                         char * tok;
652                         char * copy;
653                         char * temp;
654                         copy = strdup(re->value);
655                         temp = strtok_r(copy,":",&tok);
656                         if(temp) {
657                                 status->sampleRate = atoi(temp);
658                                 temp = strtok_r(NULL,":",&tok);
659                                 if(temp) {
660                                         status->bits = atoi(temp);
661                                         temp = strtok_r(NULL,"",&tok);
662                                         if(temp) status->channels = atoi(temp);
663                                 }
664                         }
665                         free(copy);
666                 }
668                 mpd_getNextReturnElement(connection);
669                 if(connection->error) {
670                         free(status);
671                         return NULL;
672                 }
673         }
675         if(connection->error) {
676                 free(status);
677                 return NULL;
678         }
679         else if(status->state<0) {
680                 strcpy(connection->errorStr,"state not found");
681                 connection->error = 1;
682                 free(status);
683                 return NULL;
684         }
686         return status;
689 void mpd_freeStatus(mpd_Status * status) {
690         if(status->error) free(status->error);
691         free(status);
694 void mpd_sendStatsCommand(mpd_Connection * connection) {
695         mpd_executeCommand(connection,"stats\n");
698 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
699         mpd_Stats * stats;
701         /*mpd_executeCommand(connection,"stats\n");
702                 
703         if(connection->error) return NULL;*/
705         if(connection->doneProcessing || (connection->listOks && 
706                         connection->doneListOk))
707         {
708                 return NULL;
709         }
711         if(!connection->returnElement) mpd_getNextReturnElement(connection);
713         stats = malloc(sizeof(mpd_Stats));
714         stats->numberOfArtists = 0;
715         stats->numberOfAlbums = 0;
716         stats->numberOfSongs = 0;
717         stats->uptime = 0;
718         stats->dbUpdateTime = 0;
719         stats->playTime = 0;
720         stats->dbPlayTime = 0;
722         if(connection->error) {
723                 free(stats);
724                 return NULL;
725         }
726         while(connection->returnElement) {
727                 mpd_ReturnElement * re = connection->returnElement;
728                 if(strcmp(re->name,"artists")==0) {
729                         stats->numberOfArtists = atoi(re->value);
730                 }
731                 else if(strcmp(re->name,"albums")==0) {
732                         stats->numberOfAlbums = atoi(re->value);
733                 }
734                 else if(strcmp(re->name,"songs")==0) {
735                         stats->numberOfSongs = atoi(re->value);
736                 }
737                 else if(strcmp(re->name,"uptime")==0) {
738                         stats->uptime = strtol(re->value,NULL,10);
739                 }
740                 else if(strcmp(re->name,"db_update")==0) {
741                         stats->dbUpdateTime = strtol(re->value,NULL,10);
742                 }
743                 else if(strcmp(re->name,"playtime")==0) {
744                         stats->playTime = strtol(re->value,NULL,10);
745                 }
746                 else if(strcmp(re->name,"db_playtime")==0) {
747                         stats->dbPlayTime = strtol(re->value,NULL,10);
748                 }
750                 mpd_getNextReturnElement(connection);
751                 if(connection->error) {
752                         free(stats);
753                         return NULL;
754                 }
755         }
757         if(connection->error) {
758                 free(stats);
759                 return NULL;
760         }
762         return stats;
765 void mpd_freeStats(mpd_Stats * stats) {
766         free(stats);
769 void mpd_initSong(mpd_Song * song) {
770         song->file = NULL;
771         song->artist = NULL;
772         song->album = NULL;
773         song->track = NULL;
774         song->title = NULL;
775         song->name = NULL;
776         song->time = MPD_SONG_NO_TIME;
777         song->pos = MPD_SONG_NO_NUM;
778         song->id = MPD_SONG_NO_ID;
781 void mpd_finishSong(mpd_Song * song) {
782         if(song->file) free(song->file);
783         if(song->artist) free(song->artist);
784         if(song->album) free(song->album);
785         if(song->title) free(song->title);
786         if(song->track) free(song->track);
787         if(song->name) free(song->name);
790 mpd_Song * mpd_newSong() {
791         mpd_Song * ret = malloc(sizeof(mpd_Song));
793         mpd_initSong(ret);
795         return ret;
798 void mpd_freeSong(mpd_Song * song) {
799         mpd_finishSong(song);
800         free(song);
803 mpd_Song * mpd_songDup(mpd_Song * song) {
804         mpd_Song * ret = mpd_newSong();
806         if(song->file) ret->file = strdup(song->file);
807         if(song->artist) ret->artist = strdup(song->artist);
808         if(song->album) ret->album = strdup(song->album);
809         if(song->title) ret->title = strdup(song->title);
810         if(song->track) ret->track = strdup(song->track);
811         if(song->name) ret->name = strdup(song->name);
812         ret->time = song->time;
813         ret->pos = song->pos;
814         ret->id = song->id;
816         return ret;
819 void mpd_initDirectory(mpd_Directory * directory) {
820         directory->path = NULL;
823 void mpd_finishDirectory(mpd_Directory * directory) {
824         if(directory->path) free(directory->path);
827 mpd_Directory * mpd_newDirectory () {
828         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
830         mpd_initDirectory(directory);
831         
832         return directory;
835 void mpd_freeDirectory(mpd_Directory * directory) {
836         mpd_finishDirectory(directory);
838         free(directory);
841 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
842         mpd_Directory * ret = mpd_newDirectory();
844         if(directory->path) ret->path = strdup(directory->path);
846         return ret;
849 void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
850         playlist->path = NULL;
853 void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
854         if(playlist->path) free(playlist->path);
857 mpd_PlaylistFile * mpd_newPlaylistFile() {
858         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
860         mpd_initPlaylistFile(playlist);
862         return playlist;
865 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
866         mpd_finishPlaylistFile(playlist);
867         free(playlist);
870 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
871         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
873         if(playlist->path) ret->path = strdup(playlist->path);
875         return ret;
878 void mpd_initInfoEntity(mpd_InfoEntity * entity) {
879         entity->info.directory = NULL;
880
882 void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
883         if(entity->info.directory) {
884                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
885                         mpd_freeDirectory(entity->info.directory);
886                 }
887                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
888                         mpd_freeSong(entity->info.song);
889                 }
890                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
891                         mpd_freePlaylistFile(entity->info.playlistFile);
892                 }
893         }
896 mpd_InfoEntity * mpd_newInfoEntity() {
897         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
898         
899         mpd_initInfoEntity(entity);
901         return entity;
904 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
905         mpd_finishInfoEntity(entity);
906         free(entity);
909 void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
910         mpd_executeCommand(connection,command);
913 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
914         mpd_InfoEntity * entity = NULL;
916         if(connection->doneProcessing || (connection->listOks && 
917                         connection->doneListOk))
918         {
919                 return NULL;
920         }
922         if(!connection->returnElement) mpd_getNextReturnElement(connection);
924         if(connection->returnElement) { 
925                 if(strcmp(connection->returnElement->name,"file")==0) {
926                         entity = mpd_newInfoEntity();
927                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
928                         entity->info.song = mpd_newSong();
929                         entity->info.song->file = 
930                                 strdup(connection->returnElement->value);
931                 }
932                 else if(strcmp(connection->returnElement->name,
933                                         "directory")==0) {
934                         entity = mpd_newInfoEntity();
935                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
936                         entity->info.directory = mpd_newDirectory();
937                         entity->info.directory->path = 
938                                 strdup(connection->returnElement->value);
939                 }
940                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
941                         entity = mpd_newInfoEntity();
942                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
943                         entity->info.playlistFile = mpd_newPlaylistFile();
944                         entity->info.playlistFile->path = 
945                                 strdup(connection->returnElement->value);
946                 }
947                 else {
948                         connection->error = 1;
949                         strcpy(connection->errorStr,"problem parsing song info");
950                         return NULL;
951                 }
952         }
953         else return NULL;
955         mpd_getNextReturnElement(connection);
956         while(connection->returnElement) {
957                 mpd_ReturnElement * re = connection->returnElement;
959                 if(strcmp(re->name,"file")==0) return entity;
960                 else if(strcmp(re->name,"directory")==0) return entity;
961                 else if(strcmp(re->name,"playlist")==0) return entity;
963                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG && 
964                                 strlen(re->value)) {
965                         if(!entity->info.song->artist &&
966                                         strcmp(re->name,"Artist")==0) {
967                                 entity->info.song->artist = strdup(re->value);
968                         }
969                         else if(!entity->info.song->album &&
970                                         strcmp(re->name,"Album")==0) {
971                                 entity->info.song->album = strdup(re->value);
972                         }
973                         else if(!entity->info.song->title &&
974                                         strcmp(re->name,"Title")==0) {
975                                 entity->info.song->title = strdup(re->value);
976                         }
977                         else if(!entity->info.song->track &&
978                                         strcmp(re->name,"Track")==0) {
979                                 entity->info.song->track = strdup(re->value);
980                         }
981                         else if(!entity->info.song->name &&
982                                         strcmp(re->name,"Name")==0) {
983                                 entity->info.song->name = strdup(re->value);
984                         }
985                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
986                                         strcmp(re->name,"Time")==0) {
987                                 entity->info.song->time = atoi(re->value);
988                         }
989                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
990                                         strcmp(re->name,"Pos")==0) {
991                                 entity->info.song->pos = atoi(re->value);
992                         }
993                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
994                                         strcmp(re->name,"Id")==0) {
995                                 entity->info.song->id = atoi(re->value);
996                         }
997                 }
998                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
999                 }
1000                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1001                 }
1003                 mpd_getNextReturnElement(connection);
1004         }
1006         return entity;
1009 char * mpd_getNextReturnElementNamed(mpd_Connection * connection, 
1010                 const char * name) 
1012         if(connection->doneProcessing || (connection->listOks && 
1013                         connection->doneListOk)) 
1014         {
1015                 return NULL;
1016         }
1018         mpd_getNextReturnElement(connection);
1019         while(connection->returnElement) {
1020                 mpd_ReturnElement * re = connection->returnElement;
1022                 if(strcmp(re->name,name)==0) return strdup(re->value);
1023                 mpd_getNextReturnElement(connection);
1024         }
1026         return NULL;
1029 char * mpd_getNextArtist(mpd_Connection * connection) {
1030         return mpd_getNextReturnElementNamed(connection,"Artist");
1033 char * mpd_getNextAlbum(mpd_Connection * connection) {
1034         return mpd_getNextReturnElementNamed(connection,"Album");
1037 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1038         char * string = malloc(strlen("playlistinfo")+25);
1039         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1040         mpd_sendInfoCommand(connection,string);
1041         free(string);
1044 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1045         char * string = malloc(strlen("playlistid")+25);
1046         sprintf(string, "playlistid \"%i\"\n", id);
1047         mpd_sendInfoCommand(connection, string);
1048         free(string);
1051 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1052         char * string = malloc(strlen("plchanges")+25);
1053         sprintf(string,"plchanges \"%lld\"\n",playlist);
1054         mpd_sendInfoCommand(connection,string);
1055         free(string);
1058 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1059         char * sDir = mpd_sanitizeArg(dir);
1060         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1061         sprintf(string,"listall \"%s\"\n",sDir);
1062         mpd_sendInfoCommand(connection,string);
1063         free(string);
1064         free(sDir);
1067 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1068         char * sDir = mpd_sanitizeArg(dir);
1069         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1070         sprintf(string,"listallinfo \"%s\"\n",sDir);
1071         mpd_sendInfoCommand(connection,string);
1072         free(string);
1073         free(sDir);
1076 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1077         char * sDir = mpd_sanitizeArg(dir);
1078         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1079         sprintf(string,"lsinfo \"%s\"\n",sDir);
1080         mpd_sendInfoCommand(connection,string);
1081         free(string);
1082         free(sDir);
1085 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1086         mpd_executeCommand(connection,"currentsong\n");
1089 void mpd_sendSearchCommand(mpd_Connection * connection, int table, 
1090                 const char * str) 
1092         char st[10];
1093         char * string;
1094         char * sanitStr = mpd_sanitizeArg(str);
1095         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1096         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1097         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1098         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1099         else {
1100                 connection->error = 1;
1101                 strcpy(connection->errorStr,"unknown table for search");
1102                 return;
1103         }
1104         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1105         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1106         mpd_sendInfoCommand(connection,string);
1107         free(string);
1108         free(sanitStr);
1111 void mpd_sendFindCommand(mpd_Connection * connection, int table, 
1112                 const char * str) 
1114         char st[10];
1115         char * string;
1116         char * sanitStr = mpd_sanitizeArg(str);
1117         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1118         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1119         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1120         else {
1121                 connection->error = 1;
1122                 strcpy(connection->errorStr,"unknown table for find");
1123                 return;
1124         }
1125         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1126         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1127         mpd_sendInfoCommand(connection,string);
1128         free(string);
1129         free(sanitStr);
1132 void mpd_sendListCommand(mpd_Connection * connection, int table, 
1133                 const char * arg1) 
1135         char st[10];
1136         char * string;
1137         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1138         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1139         else {
1140                 connection->error = 1;
1141                 strcpy(connection->errorStr,"unknown table for list");
1142                 return;
1143         }
1144         if(arg1) {
1145                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1146                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1147                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1148                 free(sanitArg1);
1149         }
1150         else {
1151                 string = malloc(strlen("list")+strlen(st)+3);
1152                 sprintf(string,"list %s\n",st);
1153         }
1154         mpd_sendInfoCommand(connection,string);
1155         free(string);
1158 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1159         char * sFile = mpd_sanitizeArg(file);
1160         char * string = malloc(strlen("add")+strlen(sFile)+5);
1161         sprintf(string,"add \"%s\"\n",sFile);
1162         mpd_executeCommand(connection,string);
1163         free(string);
1164         free(sFile);
1167 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1168         char * string = malloc(strlen("delete")+25);
1169         sprintf(string,"delete \"%i\"\n",songPos);
1170         mpd_sendInfoCommand(connection,string);
1171         free(string);
1174 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1175         char * string = malloc(strlen("deleteid")+25);
1176         sprintf(string, "deleteid \"%i\"\n", id);
1177         mpd_sendInfoCommand(connection,string);
1178         free(string);
1181 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1182         char * sName = mpd_sanitizeArg(name);
1183         char * string = malloc(strlen("save")+strlen(sName)+5);
1184         sprintf(string,"save \"%s\"\n",sName);
1185         mpd_executeCommand(connection,string);
1186         free(string);
1187         free(sName);
1190 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1191         char * sName = mpd_sanitizeArg(name);
1192         char * string = malloc(strlen("load")+strlen(sName)+5);
1193         sprintf(string,"load \"%s\"\n",sName);
1194         mpd_executeCommand(connection,string);
1195         free(string);
1196         free(sName);
1199 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1200         char * sName = mpd_sanitizeArg(name);
1201         char * string = malloc(strlen("rm")+strlen(sName)+5);
1202         sprintf(string,"rm \"%s\"\n",sName);
1203         mpd_executeCommand(connection,string);
1204         free(string);
1205         free(sName);
1208 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1209         mpd_executeCommand(connection,"shuffle\n");
1212 void mpd_sendClearCommand(mpd_Connection * connection) {
1213         mpd_executeCommand(connection,"clear\n");
1216 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1217         char * string = malloc(strlen("play")+25);
1218         sprintf(string,"play \"%i\"\n",songPos);
1219         mpd_sendInfoCommand(connection,string);
1220         free(string);
1223 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1224         char * string = malloc(strlen("playid")+25);
1225         sprintf(string,"playid \"%i\"\n",id);
1226         mpd_sendInfoCommand(connection,string);
1227         free(string);
1230 void mpd_sendStopCommand(mpd_Connection * connection) {
1231         mpd_executeCommand(connection,"stop\n");
1234 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1235         char * string = malloc(strlen("pause")+25);
1236         sprintf(string,"pause \"%i\"\n",pauseMode);
1237         mpd_executeCommand(connection,string);
1238         free(string);
1241 void mpd_sendNextCommand(mpd_Connection * connection) {
1242         mpd_executeCommand(connection,"next\n");
1245 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1246         char * string = malloc(strlen("move")+25);
1247         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1248         mpd_sendInfoCommand(connection,string);
1249         free(string);
1252 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1253         char * string = malloc(strlen("moveid")+25);
1254         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1255         mpd_sendInfoCommand(connection,string);
1256         free(string);
1259 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1260         char * string = malloc(strlen("swap")+25);
1261         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1262         mpd_sendInfoCommand(connection,string);
1263         free(string);
1266 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1267         char * string = malloc(strlen("swapid")+25);
1268         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1269         mpd_sendInfoCommand(connection,string);
1270         free(string);
1273 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1274         char * string = malloc(strlen("seek")+25);
1275         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1276         mpd_sendInfoCommand(connection,string);
1277         free(string);
1280 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1281         char * string = malloc(strlen("seekid")+25);
1282         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1283         mpd_sendInfoCommand(connection,string);
1284         free(string);
1287 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1288         char * sPath = mpd_sanitizeArg(path);
1289         char * string = malloc(strlen("update")+strlen(sPath)+5);
1290         sprintf(string,"update \"%s\"\n",sPath);
1291         mpd_sendInfoCommand(connection,string);
1292         free(string);
1293         free(sPath);
1296 int mpd_getUpdateId(mpd_Connection * connection) {
1297         char * jobid;
1298         int ret = 0;
1300         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1301         if(jobid) {
1302                 ret = atoi(jobid);
1303                 free(jobid);
1304         }
1306         return ret;
1309 void mpd_sendPrevCommand(mpd_Connection * connection) {
1310         mpd_executeCommand(connection,"previous\n");
1313 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1314         char * string = malloc(strlen("repeat")+25);
1315         sprintf(string,"repeat \"%i\"\n",repeatMode);
1316         mpd_executeCommand(connection,string);
1317         free(string);
1320 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1321         char * string = malloc(strlen("random")+25);
1322         sprintf(string,"random \"%i\"\n",randomMode);
1323         mpd_executeCommand(connection,string);
1324         free(string);
1327 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1328         char * string = malloc(strlen("setvol")+25);
1329         sprintf(string,"setvol \"%i\"\n",volumeChange);
1330         mpd_executeCommand(connection,string);
1331         free(string);
1334 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1335         char * string = malloc(strlen("volume")+25);
1336         sprintf(string,"volume \"%i\"\n",volumeChange);
1337         mpd_executeCommand(connection,string);
1338         free(string);
1341 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1342         char * string = malloc(strlen("crossfade")+25);
1343         sprintf(string,"crossfade \"%i\"\n",seconds);
1344         mpd_executeCommand(connection,string);
1345         free(string);
1348 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1349         char * sPass = mpd_sanitizeArg(pass);
1350         char * string = malloc(strlen("password")+strlen(sPass)+5);
1351         sprintf(string,"password \"%s\"\n",sPass);
1352         mpd_executeCommand(connection,string);
1353         free(string);
1354         free(sPass);
1357 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1358         if(connection->commandList) {
1359                 strcpy(connection->errorStr,"already in command list mode");
1360                 connection->error = 1;
1361                 return;
1362         }
1363         connection->commandList = COMMAND_LIST;
1364         mpd_executeCommand(connection,"command_list_begin\n");
1367 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1368         if(connection->commandList) {
1369                 strcpy(connection->errorStr,"already in command list mode");
1370                 connection->error = 1;
1371                 return;
1372         }
1373         connection->commandList = COMMAND_LIST_OK;
1374         mpd_executeCommand(connection,"command_list_ok_begin\n");
1375         connection->listOks = 0;
1378 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1379         if(!connection->commandList) {
1380                 strcpy(connection->errorStr,"not in command list mode");
1381                 connection->error = 1;
1382                 return;
1383         }
1384         connection->commandList = 0;
1385         mpd_executeCommand(connection,"command_list_end\n");