Code

Added _utf8 suffix to all functions that take utf8 arguments.
[ncmpc.git] / src / mpdclient.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <time.h>
25 #include <string.h>
26 #include <glib.h>
28 #include "config.h"
29 #include "ncmpc.h"
30 #include "support.h"
31 #include "mpdclient.h"
32 #include "options.h"
34 #undef  ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
35 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
37 #define ENABLE_SONG_ID
38 #define ENABLE_PLCHANGES 
40 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
43 /* Error callbacks */
44 static gint
45 error_cb(mpdclient_t *c, gint error, gchar *msg)
46 {
47   GList *list = c->error_callbacks;
48   
49   if( list==NULL )
50     fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
52   while(list)
53     {
54       mpdc_error_cb_t cb = list->data;
55       if( cb )
56         cb(c, error, msg);
57       list=list->next;
58     }
59   mpd_clearError(c->connection);
60   return error;
61 }
63 #ifdef DEBUG
64 #include "strfsong.h"
66 static gchar *
67 get_song_name(mpd_Song *song)
68 {
69   static gchar name[256];
71   strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
72   return name;
73 }
75 #endif
77 /****************************************************************************/
78 /*** mpdclient functions ****************************************************/
79 /****************************************************************************/
81 gint
82 mpdclient_finish_command(mpdclient_t *c) 
83 {
84   mpd_finishCommand(c->connection);
86   if( c->connection->error )
87     {
88       gchar *msg = locale_to_utf8(c->connection->errorStr);
89       gint error = c->connection->error;
90       
91       if( error == MPD_ERROR_ACK )
92         error = error | (c->connection->errorCode << 8);
94       error_cb(c, error, msg);
95       g_free(msg);
96       return error;
97     }
99   return 0;
102 mpdclient_t *
103 mpdclient_new(void)
105   mpdclient_t *c;
107   c = g_malloc0(sizeof(mpdclient_t));
109   return c;
112 mpdclient_t *
113 mpdclient_free(mpdclient_t *c)
115   mpdclient_disconnect(c);
116   g_list_free(c->error_callbacks);
117   g_list_free(c->playlist_callbacks);
118   g_list_free(c->browse_callbacks);
119   g_free(c);
121   return NULL;
124 gint
125 mpdclient_disconnect(mpdclient_t *c)
127   if( c->connection )
128     mpd_closeConnection(c->connection);
129   c->connection = NULL;
131   if( c->status )
132     mpd_freeStatus(c->status);
133   c->status = NULL;
135   if( c->playlist.list )
136     mpdclient_playlist_free(&c->playlist);
138   if( c->song )
139     c->song = NULL;
140   
141   return 0;
144 gint
145 mpdclient_connect(mpdclient_t *c, 
146                   gchar *host, 
147                   gint port, 
148                   gfloat timeout,
149                   gchar *password)
151   gint retval = 0;
152   
153   /* close any open connection */
154   if( c->connection )
155     mpdclient_disconnect(c);
157   /* connect to MPD */
158   c->connection = mpd_newConnection(host, port, timeout);
159   if( c->connection->error )
160     return error_cb(c, c->connection->error, c->connection->errorStr);
162   /* send password */
163   if( password )
164     {
165       mpd_sendPasswordCommand(c->connection, password);
166       retval = mpdclient_finish_command(c);
167     }
168   c->need_update = TRUE;
170   return retval;
173 gint
174 mpdclient_update(mpdclient_t *c)
176   gint retval = 0;
178   if( MPD_ERROR(c) )
179     return -1;
181   /* free the old status */
182   if( c->status )
183     mpd_freeStatus(c->status);
184   
185   /* retreive new status */
186   mpd_sendStatusCommand(c->connection);
187   c->status = mpd_getStatus(c->connection);
188   if( (retval=mpdclient_finish_command(c)) )
189     return retval;
190 #ifdef DEBUG
191   if( c->status->error )
192     D("status> %s\n", c->status->error);
193 #endif
195   /* check if the playlist needs an update */
196   if( c->playlist.id != c->status->playlist )
197     {
198       if( c->playlist.list )
199         retval = mpdclient_playlist_update_changes(c);
200       else
201         retval = mpdclient_playlist_update(c);
202     }
204   /* update the current song */
205   if( !c->song || c->status->songid != c->song->id )
206     {
207       c->song = playlist_get_song(c, c->status->song);
208     }
210   c->need_update = FALSE;
212   return retval;
216 /****************************************************************************/
217 /*** MPD Commands  **********************************************************/
218 /****************************************************************************/
220 gint 
221 mpdclient_cmd_play(mpdclient_t *c, gint index)
223 #ifdef ENABLE_SONG_ID
224   mpd_Song *song = playlist_get_song(c, index);
226   D("Play id:%d\n", song ? song->id : -1);
227   if( song )
228     mpd_sendPlayIdCommand(c->connection, song->id);
229   else
230     mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
231 #else
232   mpd_sendPlayCommand(c->connection, index);
233 #endif
234   c->need_update = TRUE;
235   return mpdclient_finish_command(c);
238 gint 
239 mpdclient_cmd_pause(mpdclient_t *c, gint value)
241   mpd_sendPauseCommand(c->connection, value);
242   return mpdclient_finish_command(c);
245 gint 
246 mpdclient_cmd_stop(mpdclient_t *c)
248   mpd_sendStopCommand(c->connection);
249   return mpdclient_finish_command(c);
252 gint 
253 mpdclient_cmd_next(mpdclient_t *c)
255   mpd_sendNextCommand(c->connection);
256   c->need_update = TRUE;
257   return mpdclient_finish_command(c);
260 gint 
261 mpdclient_cmd_prev(mpdclient_t *c)
263   mpd_sendPrevCommand(c->connection);
264   c->need_update = TRUE;
265   return mpdclient_finish_command(c);
268 gint 
269 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
271   D("Seek id:%d\n", id);
272   mpd_sendSeekIdCommand(c->connection, id, pos);
273   return mpdclient_finish_command(c);
276 gint 
277 mpdclient_cmd_shuffle(mpdclient_t *c)
279   mpd_sendShuffleCommand(c->connection);
280   c->need_update = TRUE;
281   return mpdclient_finish_command(c);
284 gint 
285 mpdclient_cmd_clear(mpdclient_t *c)
287   gint retval = 0;
289   mpd_sendClearCommand(c->connection);
290   retval = mpdclient_finish_command(c);
291   /* call playlist updated callback */
292   mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
293   c->need_update = TRUE;
294   return retval;
297 gint 
298 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
300   mpd_sendRepeatCommand(c->connection, value);
301   return mpdclient_finish_command(c);
304 gint 
305 mpdclient_cmd_random(mpdclient_t *c, gint value)
307   mpd_sendRandomCommand(c->connection, value);
308   return mpdclient_finish_command(c);
311 gint 
312 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
314   mpd_sendCrossfadeCommand(c->connection, value);
315   return mpdclient_finish_command(c);
318 gint 
319 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
321   mpd_sendUpdateCommand(c->connection, path ? path : "");
322   return mpdclient_finish_command(c);
325 gint 
326 mpdclient_cmd_volume(mpdclient_t *c, gint value)
328   mpd_sendSetvolCommand(c->connection, value);
329   return mpdclient_finish_command(c);
332 gint 
333 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
335   mpd_sendAddCommand(c->connection, path_utf8);
336   return mpdclient_finish_command(c);
339 gint 
340 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
342   gint retval;
343   gchar *path_utf8 = locale_to_utf8(path);
345   retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
346   g_free(path_utf8);
347   return retval;
350 gint 
351 mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song)
352
353   gint retval = 0;
355   if( !song || !song->file )
356     return -1;
358   /* send the add command to mpd */
359   mpd_sendAddCommand(c->connection, song->file);
360   if( (retval=mpdclient_finish_command(c)) )
361     return retval;
363 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
364   /* add the song to playlist */
365   c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
366   c->playlist.length++;
368   /* increment the playlist id, so we dont retrives a new playlist */
369   c->playlist.id++;
371   /* call playlist updated callback */
372   mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
373 #else
374   c->need_update = TRUE;
375 #endif
377   return 0;
380 gint
381 mpdclient_cmd_delete(mpdclient_t *c, gint index)
383   gint retval = 0;
384   mpd_Song *song = playlist_get_song(c, index);
386   if( !song )
387     return -1;
389   /* send the delete command to mpd */
390 #ifdef ENABLE_SONG_ID
391   D("Delete id:%d\n", song->id);
392   mpd_sendDeleteIdCommand(c->connection, song->id);
393 #else
394   mpd_sendDeleteCommand(c->connection, index);
395 #endif
396   if( (retval=mpdclient_finish_command(c)) )
397     return retval;
399 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
400   /* increment the playlist id, so we dont retrive a new playlist */
401   c->playlist.id++;
403   /* remove the song from the playlist */
404   c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
405   c->playlist.length = g_list_length(c->playlist.list);
407   /* call playlist updated callback */
408   mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
410   /* remove references to the song */
411   if( c->song == song )
412     {
413       c->song = NULL;   
414       c->need_update = TRUE;
415     }
417   /* free song */
418   mpd_freeSong(song);  
420 #else
421   c->need_update = TRUE;
422 #endif
424   return 0;
427 gint
428 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
430   gint n, index1, index2;
431   GList *item1, *item2;
432   gpointer data1, data2;
433   mpd_Song *song1, *song2;
435   if( old_index==new_index || new_index<0 || new_index>=c->playlist.length )
436     return -1;
438   song1 = playlist_get_song(c, old_index);
439   song2 = playlist_get_song(c, new_index);
441   /* send the move command to mpd */  
442 #ifdef ENABLE_SONG_ID
443   D("Swaping id:%d with id:%d\n", song1->id, song2->id);
444   mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
445 #else
446   D("Moving index %d to id:%d\n", old_index, new_index);
447   mpd_sendMoveCommand(c->connection, old_index, new_index);
448 #endif
449   if( (n=mpdclient_finish_command(c)) )
450     return n;
452 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
453   /* update the songs position field */
454   n = song1->pos;
455   song1->pos = song2->pos;
456   song2->pos = n;
457   index1 = MIN(old_index, new_index);
458   index2 = MAX(old_index, new_index);
459   /* retreive the list items */
460   item1 = g_list_nth(c->playlist.list, index1);
461   item2 = g_list_nth(c->playlist.list, index2);
462   /* retrieve the songs */
463   data1 = item1->data;
464   data2 = item2->data;
466   /* move the second item */
467   c->playlist.list = g_list_remove(c->playlist.list, data2);
468   c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
470   /* move the first item */
471   if( index2-index1 >1 )
472     {
473       item2 = g_list_nth(c->playlist.list, index2);
474       c->playlist.list = g_list_remove(c->playlist.list, data1);
475       c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1);
476     }
478   /* increment the playlist id, so we dont retrives a new playlist */
479   c->playlist.id++;
481 #else
482   c->need_update = TRUE;
483 #endif 
485   /* call playlist updated callback */
486   mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
488   return 0;
491 gint 
492 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
494   gint retval = 0;
496   mpd_sendSaveCommand(c->connection, filename_utf8);
497   if( (retval=mpdclient_finish_command(c)) == 0 )
498     mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
499   return retval;
502 gint 
503 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
505   gint retval = 0;
506   gchar *filename_utf8 = locale_to_utf8(filename);
507   
508   retval = mpdclient_cmd_save_playlist_utf8(c, filename);
509   g_free(filename_utf8);
510   return retval;
513 gint 
514 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
516   mpd_sendLoadCommand(c->connection, filename_utf8);
517   c->need_update = TRUE;
518   return mpdclient_finish_command(c);
521 gint 
522 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
524   gint retval = 0;
526   mpd_sendRmCommand(c->connection, filename_utf8);
527   if( (retval=mpdclient_finish_command(c)) == 0 )
528     mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
529   return retval;
532 gint 
533 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
535   gint retval = 0;
536   gchar *filename_utf8 = locale_to_utf8(filename);
538   retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
539   g_free(filename_utf8);
540   return retval;
544 /****************************************************************************/
545 /*** Callback managment functions *******************************************/
546 /****************************************************************************/
547 static void
548 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
550   while(list)
551     {
552       mpdc_list_cb_t fn = list->data;
554       fn(c, event, data);
555       list=list->next;
556     }
559 void
560 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
562   do_list_callbacks(c, c->playlist_callbacks, event, data);
565 void
566 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
568   c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
571 void
572 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
574   c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
577 void
578 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
580   do_list_callbacks(c, c->browse_callbacks, event, data);
584 void
585 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
587   c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
590 void
591 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
593   c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
596 void
597 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
599   c->error_callbacks = g_list_append(c->error_callbacks, cb);
602 void
603 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
605   c->error_callbacks = g_list_remove(c->error_callbacks, cb);
608 /****************************************************************************/
609 /*** Playlist managment functions *******************************************/
610 /****************************************************************************/
612 gint
613 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
615   GList *list = g_list_first(playlist->list);
617   while(list)
618     {
619       mpd_Song *song = (mpd_Song *) list->data;
620       mpd_freeSong(song);
621       list=list->next;
622     }
623   g_list_free(playlist->list);
624   memset(playlist, 0, sizeof(mpdclient_playlist_t));
625   return 0;
628 /* update playlist */
629 gint 
630 mpdclient_playlist_update(mpdclient_t *c)
632   mpd_InfoEntity *entity;
634   D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
636   if( MPD_ERROR(c) )
637     return -1;
639   if( c->playlist.list )
640     mpdclient_playlist_free(&c->playlist);
642   mpd_sendPlaylistInfoCommand(c->connection,-1);
643   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
644     {
645       if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) 
646         {
647           mpd_Song *song = mpd_songDup(entity->info.song);
649           c->playlist.list = g_list_append(c->playlist.list, (gpointer) song);
650           c->playlist.length++;
651         }
652       mpd_freeInfoEntity(entity);
653     }
654   c->playlist.id = c->status->playlist;
655   c->song = NULL;
656   c->playlist.updated = TRUE;
658   /* call playlist updated callbacks */
659   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
661   return mpdclient_finish_command(c);
664 #ifdef ENABLE_PLCHANGES
666 gint 
667 mpdclient_compare_songs(gconstpointer a, gconstpointer b)
669   mpd_Song *song1 = (mpd_Song *) a; 
670   mpd_Song *song2 = (mpd_Song *) b; 
672   return song1->pos - song2->pos;
677 /* update playlist (plchanges) */
679 gint 
680 mpdclient_playlist_update_changes(mpdclient_t *c)
682   mpd_InfoEntity *entity;
684   D("mpdclient_playlist_update_changes() [%lld -> %lld]\n", 
685     c->status->playlist, c->playlist.id);
687   if( MPD_ERROR(c) )
688     return -1;
690   mpd_sendPlChangesCommand(c->connection, c->playlist.id); 
692   while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL   ) 
693     {
695       if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) 
696         {
697           mpd_Song *song = entity->info.song;
698           GList *item;
700           item =  playlist_lookup(c, song->id);
702 #ifdef DEBUG      
703           if( item )
704             D("Changing index:%d, pos:%d, id:%d => %s\n",
705               g_list_position(c->playlist.list, item), 
706               song->pos, song->id, get_song_name(song));
707           else
708             D("Unable to find pos:%d, id:%d => %s\n",
709               song->pos, song->id, get_song_name(song));
710 #endif
712           if( item && item->data)
713             {
714               GList *old;
715               gint index = g_list_position(c->playlist.list, item);
717               /* remove previous song at song->pos */
718               while( song->pos != (index=g_list_position(c->playlist.list, item))
719                      && 
720                      (old=g_list_nth(c->playlist.list, song->pos)) )
721                 {
722                   D("Removing item with index %d id:%d (%d)\n", 
723                     song->pos, old ? ((mpd_Song *) old->data)->id : -1, index);
724                   if( item->data == c->song )
725                     c->song = NULL;
726                   mpd_freeSong((mpd_Song *) old->data);
727                   old->data = NULL;
728                   c->playlist.list = g_list_delete_link(c->playlist.list, old);
729                   c->playlist.length = g_list_length(c->playlist.list);
730                 }
732               /* Update playlist entry */
733               mpd_freeSong((mpd_Song *) item->data);                 
734               item->data = mpd_songDup(song);
735               if( c->song && c->song->id == song->id )
736                 c->song = item->data;
738             }
739           else
740             {
741               /* Add a new  playlist entry */
742               D("Adding pos:%d, id;%d - %s\n",
743                 song->pos, song->id, get_song_name(song));
744               c->playlist.list = g_list_append(c->playlist.list, 
745                                                (gpointer) mpd_songDup(song));
746               c->playlist.length++;
747             }
748         }
749       mpd_freeInfoEntity(entity);      
750     }
751   mpd_finishCommand(c->connection);
752   
753   while( g_list_length(c->playlist.list) > c->status->playlistLength )
754     {
755       GList *item = g_list_last(c->playlist.list);
757       /* Remove the last playlist entry */
758       mpd_freeSong((mpd_Song *) item->data);
759       c->playlist.list = g_list_delete_link(c->playlist.list, item);
760       c->playlist.length--;      
761       D("Removed the last playlist entry\n");
762     }
764   c->playlist.id = c->status->playlist;
765   c->playlist.updated = TRUE;
767   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
769   return 0;
771 #else
772 gint 
773 mpdclient_playlist_update_changes(mpdclient_t *c)
775   return mpdclient_playlist_update(c);
777 #endif
779 mpd_Song *
780 playlist_get_song(mpdclient_t *c, gint index)
782   return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
785 GList *
786 playlist_lookup(mpdclient_t *c, int id)
788   GList *list = g_list_first(c->playlist.list);
790   while( list )
791     {
792       mpd_Song *song = (mpd_Song *) list->data;
793       if( song->id == id )
794         return list;
795       list=list->next;
796     }
797   return NULL;
800 mpd_Song *
801 playlist_lookup_song(mpdclient_t *c, gint id)
803   GList *list = c->playlist.list;
805   while( list )
806     {
807       mpd_Song *song = (mpd_Song *) list->data;
808       if( song->id == id )
809         return song;
810       list=list->next;
811     }
812   return NULL;
815 gint 
816 playlist_get_index(mpdclient_t *c, mpd_Song *song)
818   return g_list_index(c->playlist.list, song);
821 gint 
822 playlist_get_index_from_id(mpdclient_t *c, gint id)
824   return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
827 gint
828 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
830   GList *list = c->playlist.list;
831   gint i=0;
833   while( list )
834     {
835       mpd_Song *song = (mpd_Song *) list->data;
836       if( strcmp(song->file, filename ) == 0 )  
837         return i;
838       list=list->next;
839       i++;
840     }
841   return -1;
845 /****************************************************************************/
846 /*** Filelist functions *****************************************************/
847 /****************************************************************************/
849 mpdclient_filelist_t *
850 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
852   GList *list = g_list_first(filelist->list);
854   D("mpdclient_filelist_free()\n");
855   while( list!=NULL )
856     {
857       filelist_entry_t *entry = list->data;
859       if( entry->entity )
860         mpd_freeInfoEntity(entry->entity);
861       g_free(entry);
862       list=list->next;
863     }
864   g_list_free(filelist->list);
865   g_free(filelist->path);
866   filelist->path = NULL;
867   filelist->list = NULL;
868   filelist->length = 0;
869   g_free(filelist);
871   return NULL;
875 mpdclient_filelist_t *
876 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
878   mpdclient_filelist_t *filelist;
879   mpd_InfoEntity *entity;
880   gchar *path_utf8 = locale_to_utf8(path);
882   D("mpdclient_filelist_get(%s)\n", path);
883   mpd_sendLsInfoCommand(c->connection, path_utf8);
884   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
885   if( path && path[0] && strcmp(path, "/") )
886     {
887       /* add a dummy entry for ./.. */
888       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
889       entry->entity = NULL;
890       filelist->list = g_list_append(filelist->list, (gpointer) entry);
891       filelist->length++;
892     }
894   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
895     {
896       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
897       
898       entry->entity = entity;
899       filelist->list = g_list_append(filelist->list, (gpointer) entry);
900       filelist->length++;
901     }
902   
903   if( mpdclient_finish_command(c) )
904     {
905       g_free(path_utf8);
906       return mpdclient_filelist_free(filelist);
907     }
908   
909   g_free(path_utf8);
910   filelist->path = g_strdup(path);
911   filelist->updated = TRUE;
913   return filelist;
916 mpdclient_filelist_t *
917 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
919   if( filelist != NULL )
920     {    
921       gchar *path = g_strdup(filelist->path);
923       filelist = mpdclient_filelist_free(filelist);
924       filelist = mpdclient_filelist_get(c, path);
925       g_free(path);
926       return filelist;
927     }
928   return NULL;
931 filelist_entry_t *
932 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
934   GList *list = g_list_first(fl->list);
936   while( list && song)
937     {
938       filelist_entry_t *entry = list->data;
939       mpd_InfoEntity *entity  = entry->entity;
941       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
942         {
943           mpd_Song *song2 = entity->info.song;
945           if( strcmp(song->file, song2->file) == 0 )
946             {
947               return entry;
948             }
949         }
950       list = list->next;
951     }
952   return NULL;