]> git.sesse.net Git - vlc/blob - src/input/vlm.c
Implement shell-style escaping also for double quotes and fix bugs.
[vlc] / src / input / vlm.c
1 /*****************************************************************************
2  * vlm.c: VLM interface plugin
3  *****************************************************************************
4  * Copyright (C) 2000-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Simon Latapie <garf@videolan.org>
8  *          Laurent Aimar <fenrir@videolan.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>                                      /* malloc(), free() */
33 #include <ctype.h>                                              /* tolower() */
34 #include <assert.h>
35
36 #ifdef ENABLE_VLM
37
38
39 #ifdef HAVE_TIME_H
40 #   include <time.h>                                              /* ctime() */
41 #   include <sys/timeb.h>                                         /* ftime() */
42 #endif
43
44 #include <vlc_input.h>
45 #include "input_internal.h"
46 #include <vlc_stream.h>
47 #include <vlc_vlm.h>
48 #include <vlc_vod.h>
49 #include <vlc_charset.h>
50
51 /*****************************************************************************
52  * Local prototypes.
53  *****************************************************************************/
54 static vlm_message_t *vlm_Show( vlm_t *, vlm_media_t *, vlm_schedule_t *, const char * );
55 static vlm_message_t *vlm_Help( vlm_t *, char * );
56
57 static vlm_media_instance_t *vlm_MediaInstanceSearch( vlm_t *, vlm_media_t *, const char * );
58
59 static vlm_schedule_t *vlm_ScheduleSearch( vlm_t *, const char * );
60
61 static char *Save( vlm_t * );
62 static int Load( vlm_t *, char * );
63 static int ExecuteCommand( vlm_t *, const char *, vlm_message_t ** );
64 static int Manage( vlc_object_t * );
65
66 /*****************************************************************************
67  * vlm_New:
68  *****************************************************************************/
69 vlm_t *__vlm_New ( vlc_object_t *p_this )
70 {
71     vlc_value_t lockval;
72     vlm_t *p_vlm = NULL;
73     char *psz_vlmconf;
74
75     /* to be sure to avoid multiple creation */
76     var_Create( p_this->p_libvlc_global, "vlm_mutex", VLC_VAR_MUTEX );
77     var_Get( p_this->p_libvlc_global, "vlm_mutex", &lockval );
78     vlc_mutex_lock( lockval.p_address );
79
80     if( !(p_vlm = vlc_object_find( p_this, VLC_OBJECT_VLM, FIND_ANYWHERE )) )
81     {
82         msg_Info( p_this, "creating VLM" );
83         if( ( p_vlm = vlc_object_create( p_this, VLC_OBJECT_VLM ) ) == NULL )
84         {
85             vlc_mutex_unlock( lockval.p_address );
86             return NULL;
87         }
88
89         vlc_mutex_init( p_this->p_libvlc, &p_vlm->lock );
90         p_vlm->i_media      = 0;
91         p_vlm->media        = NULL;
92         p_vlm->i_vod        = 0;
93         p_vlm->i_schedule   = 0;
94         p_vlm->schedule     = NULL;
95
96         vlc_object_yield( p_vlm );
97         vlc_object_attach( p_vlm, p_this->p_libvlc );
98     }
99     vlc_mutex_unlock( lockval.p_address );
100
101     if( vlc_thread_create( p_vlm, "vlm thread",
102                            Manage, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
103     {
104         vlc_mutex_destroy( &p_vlm->lock );
105         vlc_object_destroy( p_vlm );
106         return NULL;
107     }
108
109     /* Try loading the vlm conf file given by --vlm-conf */
110     psz_vlmconf = config_GetPsz( p_vlm, "vlm-conf" );
111
112     if( psz_vlmconf && *psz_vlmconf )
113     {
114         vlm_message_t *p_message = NULL;
115         char *psz_buffer = NULL;
116
117         msg_Dbg( p_this, "loading VLM configuration" );
118         asprintf(&psz_buffer, "load %s", psz_vlmconf );
119         if( psz_buffer )
120         {
121             msg_Dbg( p_this, psz_buffer);
122             if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) ){
123                 msg_Warn( p_this, "error while loading the configuration file" );
124             }
125             vlm_MessageDelete(p_message);
126             free(psz_buffer);
127         }
128    }
129    free(psz_vlmconf);
130
131    return p_vlm;
132 }
133
134 /*****************************************************************************
135  * vlm_Delete:
136  *****************************************************************************/
137 void vlm_Delete( vlm_t *p_vlm )
138 {
139     vlc_value_t lockval;
140
141     var_Get( p_vlm->p_libvlc_global, "vlm_mutex", &lockval );
142     vlc_mutex_lock( lockval.p_address );
143
144     vlc_object_release( p_vlm );
145
146     if( p_vlm->i_refcount > 0 )
147     {
148         vlc_mutex_unlock( lockval.p_address );
149         return;
150     }
151
152     p_vlm->b_die = VLC_TRUE;
153     vlc_thread_join( p_vlm );
154
155     vlc_mutex_destroy( &p_vlm->lock );
156
157     while( p_vlm->i_media ) vlm_MediaDelete( p_vlm, p_vlm->media[0], NULL );
158     FREENULL( p_vlm->media );
159
160     while( p_vlm->i_schedule ) vlm_ScheduleDelete( p_vlm,
161                                                    p_vlm->schedule[0], NULL );
162     FREENULL( p_vlm->schedule );
163
164     vlc_object_detach( p_vlm );
165     vlc_object_destroy( p_vlm );
166     vlc_mutex_unlock( lockval.p_address );
167 }
168
169 /*****************************************************************************
170  * vlm_ExecuteCommand:
171  *****************************************************************************/
172 int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
173                         vlm_message_t **pp_message)
174 {
175     int i_result;
176
177     vlc_mutex_lock( &p_vlm->lock );
178     i_result = ExecuteCommand( p_vlm, psz_command, pp_message );
179     vlc_mutex_unlock( &p_vlm->lock );
180
181     return i_result;
182 }
183
184 /*****************************************************************************
185  * vlm_Save:
186  *****************************************************************************/
187 int vlm_Save( vlm_t *p_vlm, const char *psz_file )
188 {
189     FILE *file;
190     char *psz_save;
191
192     if( !p_vlm || !psz_file ) return 1;
193
194     file = utf8_fopen( psz_file, "wt" );
195     if( file == NULL ) return 1;
196
197     psz_save = Save( p_vlm );
198     if( psz_save == NULL )
199     {
200         fclose( file );
201         return 1;
202     }
203     fwrite( psz_save, strlen( psz_save ), 1, file );
204     fclose( file );
205     free( psz_save );
206
207     return 0;
208 }
209
210 /*****************************************************************************
211  * vlm_Load:
212  *****************************************************************************/
213 int vlm_Load( vlm_t *p_vlm, const char *psz_file )
214 {
215     stream_t *p_stream;
216     int64_t i_size;
217     char *psz_buffer;
218
219     if( !p_vlm || !psz_file ) return 1;
220
221     p_stream = stream_UrlNew( p_vlm, psz_file );
222     if( p_stream == NULL ) return 1;
223
224     if( stream_Seek( p_stream, 0 ) != 0 )
225     {
226         stream_Delete( p_stream );
227         return 2;
228     }
229
230     i_size = stream_Size( p_stream );
231
232     psz_buffer = malloc( i_size + 1 );
233     if( !psz_buffer )
234     {
235         stream_Delete( p_stream );
236         return 2;
237     }
238
239     stream_Read( p_stream, psz_buffer, i_size );
240     psz_buffer[ i_size ] = '\0';
241
242     stream_Delete( p_stream );
243
244     if( Load( p_vlm, psz_buffer ) )
245     {
246         free( psz_buffer );
247         return 3;
248     }
249
250     free( psz_buffer );
251
252     return 0;
253 }
254
255
256 static const char quotes[] = "\"'";
257 /**
258  * FindCommandEnd: look for the end of a possibly quoted string
259  * @return NULL on mal-formatted string,
260  * pointer past the last character otherwise.
261  */
262 static const char *FindCommandEnd (const char *psz_sent)
263 {
264     const char quote = strchr (quotes, psz_sent[0]) ? psz_sent[0] : 0;
265     char c;
266
267     if (quote)
268         psz_sent++; // skip opening quote
269
270     while ((c = *psz_sent) != '\0')
271     {
272         if ((quote == '"') && (c == '\\'))
273         {
274             psz_sent++; // move past backslash
275             if (*psz_sent == '\0')
276                 return NULL; // cannot escape "nothing"
277         }
278         else
279         if (c == quote) // non-escaped matching quote
280             return psz_sent + 1;
281         else
282         if ((!quote) && isspace(c)) // non-escaped blank
283             return psz_sent;
284
285         psz_sent++;
286     }
287
288     // error (NULL) if we could not find a matching quote
289     return quote ? NULL : psz_sent;
290 }
291
292
293 /**
294  * Unescape a nul-terminated string.
295  * Note that in and out can be identical.
296  *
297  * @param out output buffer (at least <strlen (in) + 1> characters long)
298  * @param in nul-terminated string to be unescaped
299  *
300  * @return 0 on success, -1 on error.
301  */
302 static int Unescape (char *out, const char *in)
303 {
304     const char quote = strchr (quotes, in[0]) ? in[0] : 0;
305
306     if (quote)
307         in++; // skips opening quote
308
309     for (;;)
310     {
311         char c = *in++;
312
313         if ((c == '\0') || (c == quote))
314             break;
315
316         if ((quote == '"') && (c == '\\'))
317         {
318             switch (c = *in++)
319             {
320                 case '"':
321                     *out++ = '"';
322                     continue;
323
324                 case '\\':
325                     *out++ = '\\';
326                     continue;
327
328                 case '\0':   // should never happen
329                     *out = '\0';
330                     return -1;
331             }
332             /* None of the special cases - copy the backslash */
333             *out++ = '\\';
334         }
335         *out++ = c;
336     }
337
338     *out = '\0';
339     return 0;
340 }
341
342
343 /*****************************************************************************
344  * ExecuteCommand: The main state machine
345  *****************************************************************************
346  * Execute a command which ends with '\0' (string)
347  *****************************************************************************/
348 static int ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
349                            vlm_message_t **pp_message )
350 {
351     size_t i_command = 0;
352     char buf[strlen (psz_command) + 1], *psz_buf = buf;
353     char *ppsz_command[sizeof (buf) / 2];
354     vlm_message_t *p_message = NULL;
355
356     /* First, parse the line and cut it */
357     while( *psz_command != '\0' )
358     {
359         const char *psz_temp;
360
361         if(isspace (*psz_command))
362         {
363             psz_command++;
364             continue;
365         }
366
367         /* support for comments */
368         if( i_command == 0 && *psz_command == '#')
369         {
370             p_message = vlm_MessageNew( "", NULL );
371             goto success;
372         }
373
374         psz_temp = FindCommandEnd( psz_command );
375
376         if( psz_temp == NULL )
377         {
378             p_message = vlm_MessageNew( "Incomplete command", psz_command );
379             goto error;
380         }
381
382         assert (i_command < (sizeof (ppsz_command) / sizeof (ppsz_command[0])));
383
384         ppsz_command[i_command] = psz_buf;
385         memcpy (psz_buf, psz_command, psz_temp - psz_command);
386         psz_buf[psz_temp - psz_command] = '\0';
387
388         Unescape (psz_buf, psz_buf);
389
390         i_command++;
391         psz_buf += psz_temp - psz_command + 1;
392         psz_command = psz_temp;
393
394         assert (buf + sizeof (buf) >= psz_buf);
395     }
396
397     /*
398      * And then Interpret it
399      */
400
401     if( i_command == 0 )
402     {
403         p_message = vlm_MessageNew( "", NULL );
404         goto success;
405     }
406
407     if( !strcmp(ppsz_command[0], "new") )
408     {
409         int i_type;
410
411         /* Check the number of arguments */
412         if( i_command < 3 ) goto syntax_error;
413
414         /* Get type */
415         if( !strcmp(ppsz_command[2], "vod") )
416         {
417             i_type = VOD_TYPE;
418         }
419         else if( !strcmp(ppsz_command[2], "broadcast") )
420         {
421             i_type = BROADCAST_TYPE;
422         }
423         else if( !strcmp(ppsz_command[2], "schedule") )
424         {
425             i_type = SCHEDULE_TYPE;
426         }
427         else
428         {
429             p_message =
430                 vlm_MessageNew( "new", "%s: Choose between vod, "
431                                 "broadcast or schedule", ppsz_command[1] );
432             goto error;
433         }
434
435         /* Check for forbidden media names */
436         if( !strcmp(ppsz_command[1], "all") ||
437             !strcmp(ppsz_command[1], "media") ||
438             !strcmp(ppsz_command[1], "schedule") )
439         {
440             p_message = vlm_MessageNew( "new", "\"all\", \"media\" and "
441                                         "\"schedule\" are reserved names" );
442             goto error;
443         }
444
445         /* Check the name is not already in use */
446         if( vlm_ScheduleSearch( p_vlm, ppsz_command[1] ) ||
447             vlm_MediaSearch( p_vlm, ppsz_command[1] ) )
448         {
449             p_message = vlm_MessageNew( "new", "%s: Name already in use",
450                                         ppsz_command[1] );
451             goto error;
452         }
453
454         /* Schedule */
455         if( i_type == SCHEDULE_TYPE )
456         {
457             vlm_schedule_t *p_schedule;
458             p_schedule = vlm_ScheduleNew( p_vlm, ppsz_command[1] );
459             if( !p_schedule )
460             {
461                 p_message = vlm_MessageNew( "new", "could not create schedule" );
462                 goto error;
463             }
464         }
465
466         /* Media */
467         else
468         {
469             vlm_media_t *p_media;
470             p_media = vlm_MediaNew( p_vlm, ppsz_command[1], i_type );
471             if( !p_media )
472             {
473                 p_message = vlm_MessageNew( "new", "could not create media" );
474                 goto error;
475             }
476         }
477
478         if( i_command <= 3 )
479         {
480             p_message = vlm_MessageNew( "new", NULL );
481             goto success;
482         }
483
484         /* Properties will be dealt with later on */
485     }
486
487     else if( !strcmp(ppsz_command[0], "setup") )
488     {
489         if( i_command < 2 ) goto syntax_error;
490
491         /* Properties will be dealt with later on */
492     }
493
494     else if( !strcmp(ppsz_command[0], "del") )
495     {
496         vlm_media_t *p_media;
497         vlm_schedule_t *p_schedule;
498
499         if( i_command < 2 ) goto syntax_error;
500
501         p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] );
502         p_schedule = vlm_ScheduleSearch( p_vlm, ppsz_command[1] );
503
504         if( p_schedule != NULL )
505         {
506             vlm_ScheduleDelete( p_vlm, p_schedule, NULL );
507         }
508         else if( p_media != NULL )
509         {
510             vlm_MediaDelete( p_vlm, p_media, NULL );
511         }
512         else if( !strcmp(ppsz_command[1], "media") )
513         {
514             while( p_vlm->i_media ) vlm_MediaDelete( p_vlm, p_vlm->media[0],
515                                                      NULL );
516         }
517         else if( !strcmp(ppsz_command[1], "schedule") )
518         {
519             while( p_vlm->i_schedule )
520                 vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0], NULL );
521         }
522         else if( !strcmp(ppsz_command[1], "all") )
523         {
524             while( p_vlm->i_media ) vlm_MediaDelete( p_vlm, p_vlm->media[0],
525                                                      NULL );
526
527             while( p_vlm->i_schedule )
528                 vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0], NULL );
529         }
530         else
531         {
532             p_message = vlm_MessageNew( "del", "%s: media unknown",
533                                       ppsz_command[1] );
534             goto error;
535         }
536
537         p_message = vlm_MessageNew( "del", NULL );
538         goto success;
539     }
540
541     else if( !strcmp(ppsz_command[0], "show") )
542     {
543         vlm_media_t *p_media;
544         vlm_schedule_t *p_schedule;
545
546         if( i_command == 1 )
547         {
548             p_message = vlm_Show( p_vlm, NULL, NULL, NULL );
549             goto success;
550         }
551         else if( i_command > 2 ) goto syntax_error;
552
553         p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] );
554         p_schedule = vlm_ScheduleSearch( p_vlm, ppsz_command[1] );
555
556         if( p_schedule != NULL )
557         {
558             p_message = vlm_Show( p_vlm, NULL, p_schedule, NULL );
559         }
560         else if( p_media != NULL )
561         {
562             p_message = vlm_Show( p_vlm, p_media, NULL, NULL );
563         }
564         else
565         {
566             p_message = vlm_Show( p_vlm, NULL, NULL, ppsz_command[1] );
567         }
568
569         goto success;
570     }
571
572     else if( !strcmp(ppsz_command[0], "help") )
573     {
574         if( i_command != 1 ) goto syntax_error;
575
576         p_message = vlm_Help( p_vlm, NULL );
577         goto success;
578     }
579
580     else if( !strcmp(ppsz_command[0], "control") )
581     {
582         vlm_media_t *p_media;
583
584         if( i_command < 3 ) goto syntax_error;
585
586         if( !(p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] ) ) )
587         {
588             p_message = vlm_MessageNew( "control", "%s: media unknown",
589                                       ppsz_command[1] );
590             goto error;
591         }
592         else
593         {
594             char *psz_command, *psz_arg = 0, *psz_instance = 0;
595             size_t i_index = 2;
596
597             if( strcmp( ppsz_command[2], "play" ) &&
598                 strcmp( ppsz_command[2], "stop" ) &&
599                 strcmp( ppsz_command[2], "pause" ) &&
600                 strcmp( ppsz_command[2], "seek" ) )
601             {
602                 i_index++;
603                 psz_instance = ppsz_command[2];
604
605                 if( i_command < 4 ) goto syntax_error;
606
607                 if( strcmp( ppsz_command[3], "play" ) &&
608                     strcmp( ppsz_command[3], "stop" ) &&
609                     strcmp( ppsz_command[3], "pause" ) &&
610                     strcmp( ppsz_command[3], "seek" ) )
611                     goto syntax_error;
612             }
613
614             psz_command = ppsz_command[i_index];
615
616             if( i_command >= i_index + 2 ) psz_arg = ppsz_command[i_index + 1];
617
618             vlm_MediaControl( p_vlm, p_media, psz_instance, psz_command,
619                              psz_arg );
620             p_message = vlm_MessageNew( "control", NULL );
621             goto success;
622         }
623     }
624
625     else if( !strcmp(ppsz_command[0], "save") )
626     {
627         if( i_command != 2 ) goto syntax_error;
628
629         if( vlm_Save( p_vlm, ppsz_command[1] ) )
630         {
631             p_message = vlm_MessageNew( "save", "Unable to save to file" );
632             goto error;
633         }
634         else
635         {
636             p_message = vlm_MessageNew( "save", NULL );
637             goto success;
638         }
639     }
640
641     else if( !strcmp(ppsz_command[0], "export" ) )
642     {
643         char *psz_buf;
644
645         if( i_command != 1 ) goto syntax_error;
646
647         p_message = vlm_MessageNew( "export", psz_buf = Save( p_vlm ) );
648         free( psz_buf );
649
650         goto success;
651     }
652
653     else if( !strcmp(ppsz_command[0], "load") )
654     {
655         if( i_command != 2 ) goto syntax_error;
656
657         switch( vlm_Load( p_vlm, ppsz_command[1] ) )
658         {
659             case 0:
660                 p_message = vlm_MessageNew( "load", NULL );
661                 goto success;
662             case 2:
663                 p_message = vlm_MessageNew( "load", "Read file error" );
664                 goto error;
665             case 3:
666                 p_message =
667                     vlm_MessageNew( "load", "Error while loading file" );
668                 goto error;
669             default:
670                 p_message =
671                     vlm_MessageNew( "load", "Unable to load from file" );
672                 goto error;
673         }
674     }
675
676     else
677     {
678         p_message = vlm_MessageNew( ppsz_command[0], "Unknown command" );
679         goto error;
680     }
681
682     /* Common code between "new" and "setup" */
683     if( !strcmp(ppsz_command[0], "new") ||
684         !strcmp(ppsz_command[0], "setup") )
685     {
686         size_t i_command_start = strcmp(ppsz_command[0], "new") ? 2 : 3;
687         vlm_media_t *p_media;
688         vlm_schedule_t *p_schedule;
689
690         if( i_command < i_command_start ) goto syntax_error;
691
692         p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] );
693         p_schedule = vlm_ScheduleSearch( p_vlm, ppsz_command[1] );
694
695         if( !p_media && !p_schedule )
696         {
697             p_message = vlm_MessageNew( ppsz_command[0], "%s unknown",
698                                         ppsz_command[1] );
699             goto error;
700         }
701
702         if( p_schedule != NULL )
703         {
704             size_t i;
705
706             for( i = i_command_start ; i < i_command ; i++ )
707             {
708                 if( !strcmp( ppsz_command[i], "enabled" ) ||
709                     !strcmp( ppsz_command[i], "disabled" ) )
710                 {
711                     vlm_ScheduleSetup( p_schedule, ppsz_command[i], NULL );
712                 }
713
714                 /* Beware: everything behind append is considered as
715                  * command line */
716                 else if( !strcmp( ppsz_command[i], "append" ) )
717                 {
718                     size_t j;
719
720                     if( ++i >= i_command ) break;
721
722                     for( j = i + 1; j < i_command; j++ )
723                     {
724                         ppsz_command[i] =
725                             realloc( ppsz_command[i], strlen(ppsz_command[i]) +
726                                      strlen(ppsz_command[j]) + 1 + 1 );
727                         strcat( ppsz_command[i], " " );
728                         strcat( ppsz_command[i], ppsz_command[j] );
729                     }
730
731                     vlm_ScheduleSetup( p_schedule, ppsz_command[i - 1],
732                                        ppsz_command[i] );
733                     break;
734                 }
735                 else
736                 {
737                     if( i + 1 >= i_command && !strcmp(ppsz_command[0], "new") )
738                     {
739                         vlm_ScheduleDelete( p_vlm, p_schedule, NULL );
740                         p_message =
741                             vlm_MessageNew( ppsz_command[0],
742                                             "Wrong properties syntax" );
743                         goto error;
744                     }
745                     else if( i + 1 >= i_command )
746                     {
747                         p_message =
748                             vlm_MessageNew( ppsz_command[0],
749                                             "Wrong properties syntax" );
750                         goto error;
751                     }
752
753                     vlm_ScheduleSetup( p_schedule, ppsz_command[i],
754                                        ppsz_command[i+1] );
755                     i++;
756                 }
757             }
758         }
759
760         else if( p_media != NULL )
761         {
762             size_t i;
763
764             for( i = i_command_start ; i < i_command ; i++ )
765             {
766                 if( !strcmp( ppsz_command[i], "enabled" ) ||
767                     !strcmp( ppsz_command[i], "disabled" ) )
768                 {
769                     vlm_MediaSetup( p_vlm, p_media, ppsz_command[i], NULL );
770                 }
771                 else if( i + 1 >= i_command &&
772                          !strcmp( ppsz_command[i], "mux") )
773                 {
774                     if( p_media->i_type != VOD_TYPE )
775                     {
776                         p_message = vlm_MessageNew( ppsz_command[0],
777                                   "mux only available for broadcast" );
778                     }
779                     else
780                     {
781                         vlm_MediaSetup( p_vlm, p_media, ppsz_command[i],
782                                         ppsz_command[i+1] );
783                         i++;
784                     }
785                 }
786                 else if( !strcmp( ppsz_command[i], "loop" ) ||
787                          !strcmp( ppsz_command[i], "unloop" ) )
788                 {
789                     if( p_media->i_type != BROADCAST_TYPE )
790                     {
791                         p_message = vlm_MessageNew( ppsz_command[0],
792                                   "loop only available for broadcast" );
793                     }
794                     else
795                     {
796                         vlm_MediaSetup( p_vlm, p_media, ppsz_command[i], NULL );
797                     }
798                 }
799                 else
800                 {
801                     if( ( (i + 1) >= i_command ) &&
802                         !strcmp(ppsz_command[0], "new") )
803                     {
804                         vlm_MediaDelete( p_vlm, p_media, NULL );
805                         p_message =
806                             vlm_MessageNew( ppsz_command[0],
807                                             "Wrong properties syntax" );
808                         goto error;
809                     }
810                     else if( (i + 1) >= i_command )
811                     {
812                         p_message =
813                             vlm_MessageNew( ppsz_command[0],
814                                             "Wrong properties syntax" );
815                         goto error;
816                     }
817
818                     vlm_MediaSetup( p_vlm, p_media, ppsz_command[i],
819                                     ppsz_command[i+1] );
820                     i++;
821                 }
822             }
823         }
824
825         p_message = vlm_MessageNew( ppsz_command[0], NULL );
826         goto success;
827     }
828
829 success:
830     *pp_message = p_message;
831     return VLC_SUCCESS;
832
833 syntax_error:
834     p_message = vlm_MessageNew( ppsz_command[0], "Wrong command syntax" );
835
836 error:
837     *pp_message = p_message;
838     return VLC_EGENERIC;
839 }
840
841 vlm_media_t *vlm_MediaSearch( vlm_t *vlm, const char *psz_name )
842 {
843     int i;
844
845     for( i = 0; i < vlm->i_media; i++ )
846     {
847         if( strcmp( psz_name, vlm->media[i]->psz_name ) == 0 )
848         {
849             return vlm->media[i];
850         }
851     }
852
853     return NULL;
854 }
855
856 /*****************************************************************************
857  * Media handling
858  *****************************************************************************/
859 static vlm_media_instance_t *
860 vlm_MediaInstanceSearch( vlm_t *vlm, vlm_media_t *media,
861                          const char *psz_name )
862 {
863     int i;
864
865     for( i = 0; i < media->i_instance; i++ )
866     {
867         if( ( !psz_name && !media->instance[i]->psz_name ) ||
868             ( psz_name && media->instance[i]->psz_name &&
869               !strcmp( psz_name, media->instance[i]->psz_name ) ) )
870         {
871             return media->instance[i];
872         }
873     }
874
875     return NULL;
876 }
877
878 vlm_media_t *vlm_MediaNew( vlm_t *vlm, const char *psz_name, int i_type )
879 {
880     vlm_media_t *media = malloc( sizeof( vlm_media_t ) );
881
882     if( !media )
883     {
884         msg_Err( vlm, "out of memory" );
885         return NULL;
886     }
887
888     /* Check if we need to load the VOD server */
889     if( i_type == VOD_TYPE && !vlm->i_vod )
890     {
891         vlm->vod = vlc_object_create( vlm, VLC_OBJECT_VOD );
892         vlc_object_attach( vlm->vod, vlm );
893         vlm->vod->p_module = module_Need( vlm->vod, "vod server", 0, 0 );
894         if( !vlm->vod->p_module )
895         {
896             msg_Err( vlm, "cannot find vod server" );
897             vlc_object_detach( vlm->vod );
898             vlc_object_destroy( vlm->vod );
899             vlm->vod = 0;
900             free( media );
901             return NULL;
902         }
903
904         vlm->vod->p_data = vlm;
905         vlm->vod->pf_media_control = vlm_MediaVodControl;
906     }
907
908     if( i_type == VOD_TYPE ) vlm->i_vod++;
909
910     media->psz_name = strdup( psz_name );
911     media->b_enabled = VLC_FALSE;
912     media->b_loop = VLC_FALSE;
913     media->vod_media = NULL;
914     media->psz_vod_output = NULL;
915     media->psz_mux = NULL;
916     media->i_input = 0;
917     media->input = NULL;
918     media->psz_output = NULL;
919     media->i_option = 0;
920     media->option = NULL;
921     media->i_type = i_type;
922     media->i_instance = 0;
923     media->instance = NULL;
924
925     input_ItemInit( VLC_OBJECT(vlm), &media->item );
926
927     TAB_APPEND( vlm->i_media, vlm->media, media );
928
929     return media;
930 }
931
932 /* for now, simple delete. After, del with options (last arg) */
933 void vlm_MediaDelete( vlm_t *vlm, vlm_media_t *media, const char *psz_name )
934 {
935     if( media == NULL ) return;
936
937     while( media->i_instance )
938     {
939         vlm_media_instance_t *p_instance = media->instance[0];
940         vlm_MediaControl( vlm, media, p_instance->psz_name, "stop", 0 );
941     }
942
943     TAB_REMOVE( vlm->i_media, vlm->media, media );
944
945     if( media->i_type == VOD_TYPE )
946     {
947         vlm_MediaSetup( vlm, media, "disabled", 0 );
948         vlm->i_vod--;
949     }
950
951     /* Check if we need to unload the VOD server */
952     if( media->i_type == VOD_TYPE && !vlm->i_vod )
953     {
954         module_Unneed( vlm->vod, vlm->vod->p_module );
955         vlc_object_detach( vlm->vod );
956         vlc_object_destroy( vlm->vod );
957         vlm->vod = 0;
958     }
959
960     if( vlm->i_media == 0 && vlm->media ) free( vlm->media );
961
962     free( media->psz_name );
963
964     while( media->i_input-- ) free( media->input[media->i_input] );
965     if( media->input ) free( media->input );
966
967     if( media->psz_output ) free( media->psz_output );
968     if( media->psz_mux ) free( media->psz_mux );
969
970     while( media->i_option-- ) free( media->option[media->i_option] );
971     if( media->option ) free( media->option );
972
973     input_ItemClean( &media->item );
974
975     free( media );
976 }
977
978 int vlm_MediaSetup( vlm_t *vlm, vlm_media_t *media, const char *psz_cmd,
979                     const char *psz_value )
980 {
981     if( !psz_cmd) return VLC_EGENERIC;
982
983     if( !strcmp( psz_cmd, "loop" ) )
984     {
985         media->b_loop = VLC_TRUE;
986     }
987     else if( !strcmp( psz_cmd, "unloop" ) )
988     {
989         media->b_loop = VLC_FALSE;
990     }
991     else if( !strcmp( psz_cmd, "enabled" ) )
992     {
993         media->b_enabled = VLC_TRUE;
994     }
995     else if( !strcmp( psz_cmd, "disabled" ) )
996     {
997         media->b_enabled = VLC_FALSE;
998     }
999     else if( !strcmp( psz_cmd, "mux" ) )
1000     {
1001         if( media->psz_mux ) free( media->psz_mux );
1002         media->psz_mux = NULL;
1003         if( psz_value ) media->psz_mux = strdup( psz_value );
1004     }
1005     else if( !strcmp( psz_cmd, "input" ) )
1006     {
1007         char *input;
1008
1009         if( psz_value != NULL && strlen(psz_value) > 1 &&
1010             ( psz_value[0] == '\'' || psz_value[0] == '\"' ) &&
1011             ( psz_value[ strlen(psz_value) - 1 ] == '\'' ||
1012               psz_value[ strlen(psz_value) - 1 ] == '\"' )  )
1013         {
1014             input = malloc( strlen(psz_value) - 1 );
1015
1016             memcpy( input, psz_value + 1, strlen(psz_value) - 2 );
1017             input[ strlen(psz_value) - 2 ] = '\0';
1018         }
1019         else
1020         {
1021             input = strdup( psz_value );
1022         }
1023
1024         TAB_APPEND( media->i_input, media->input, input );
1025     }
1026     else if( !strcmp( psz_cmd, "inputdel" ) && !strcmp( psz_value, "all" ) )
1027     {
1028         while( media->i_input > 0 )
1029         {
1030             TAB_REMOVE( media->i_input, media->input, media->input[0] );
1031         }
1032     }
1033     else if( !strcmp( psz_cmd, "inputdel" ) )
1034     {
1035         char *input;
1036         int i;
1037
1038         if( psz_value != NULL && strlen(psz_value) > 1 &&
1039             ( psz_value[0] == '\'' || psz_value[0] == '\"' ) &&
1040             ( psz_value[ strlen(psz_value) - 1 ] == '\'' ||
1041               psz_value[ strlen(psz_value) - 1 ] == '\"' )  )
1042         {
1043             input = malloc( strlen(psz_value) - 1 );
1044
1045             memcpy( input, psz_value + 1, strlen(psz_value) - 2 );
1046             input[ strlen(psz_value) - 2 ] = '\0';
1047         }
1048         else
1049         {
1050             input = strdup( psz_value );
1051         }
1052
1053         for( i = 0; i < media->i_input; i++ )
1054         {
1055             if( !strcmp( input, media->input[i] ) )
1056             {
1057                 TAB_REMOVE( media->i_input, media->input, media->input[i] );
1058                 break;
1059             }
1060         }
1061     }
1062     else if( !strcmp( psz_cmd, "inputdeln" ) )
1063     {
1064         int index = atoi( psz_value );
1065         if( index > 0 && index <= media->i_input )
1066         {
1067             TAB_REMOVE( media->i_input, media->input, media->input[index-1] );
1068         }
1069     }
1070     else if( !strcmp( psz_cmd, "output" ) )
1071     {
1072         if( media->psz_output != NULL )
1073         {
1074             free( media->psz_output );
1075         }
1076         media->psz_output = strdup( psz_value );
1077     }
1078     else if( !strcmp( psz_cmd, "option" ) )
1079     {
1080         char *psz_option;
1081         psz_option = strdup( psz_value );
1082
1083         TAB_APPEND( media->i_option, media->option, psz_option );
1084     }
1085     else
1086     {
1087         return VLC_EGENERIC;
1088     }
1089
1090     /* Check if we need to create/delete a vod media */
1091     if( media->i_type == VOD_TYPE )
1092     {
1093         if( !media->b_enabled && media->vod_media )
1094         {
1095             vlm->vod->pf_media_del( vlm->vod, media->vod_media );
1096             media->vod_media = 0;
1097         }
1098         else if( media->b_enabled && !media->vod_media && media->i_input )
1099         {
1100             /* Pre-parse the input */
1101             input_thread_t *p_input;
1102             char *psz_output;
1103             char *psz_header;
1104             int i;
1105
1106             input_ItemClean( &media->item );
1107             input_ItemInit( VLC_OBJECT(vlm), &media->item );
1108
1109             if( media->psz_output )
1110                 asprintf( &psz_output, "%s:description", media->psz_output );
1111             else
1112                 asprintf( &psz_output, "#description" );
1113
1114             media->item.psz_uri = strdup( media->input[0] );
1115             media->item.ppsz_options = malloc( sizeof( char* ) );
1116             asprintf( &media->item.ppsz_options[0], "sout=%s", psz_output);
1117             media->item.i_options = 1;
1118             for( i = 0; i < media->i_option; i++ )
1119             {
1120                 media->item.i_options++;
1121                 media->item.ppsz_options =
1122                     realloc( media->item.ppsz_options,
1123                              media->item.i_options * sizeof( char* ) );
1124                 media->item.ppsz_options[ media->item.i_options - 1 ] =
1125                     strdup( media->option[i] );
1126             }
1127
1128             asprintf( &psz_header, _("Media: %s"), media->psz_name );
1129
1130             if( (p_input = input_CreateThread2( vlm, &media->item, psz_header
1131                                               ) ) )
1132             {
1133                 while( !p_input->b_eof && !p_input->b_error ) msleep( 100000 );
1134
1135                 input_StopThread( p_input );
1136                 input_DestroyThread( p_input );
1137                 vlc_object_detach( p_input );
1138                 vlc_object_destroy( p_input );
1139             }
1140             free( psz_output );
1141             free( psz_header );
1142
1143             if( media->psz_mux )
1144             {
1145                 input_item_t item;
1146                 es_format_t es, *p_es = &es;
1147                 char fourcc[5];
1148
1149                 sprintf( fourcc, "%4.4s", media->psz_mux );
1150                 fourcc[0] = tolower(fourcc[0]); fourcc[1] = tolower(fourcc[1]);
1151                 fourcc[2] = tolower(fourcc[2]); fourcc[3] = tolower(fourcc[3]);
1152
1153                 item = media->item;
1154                 item.i_es = 1;
1155                 item.es = &p_es;
1156                 es_format_Init( &es, VIDEO_ES, *((int *)fourcc) );
1157
1158                 media->vod_media =
1159                   vlm->vod->pf_media_new( vlm->vod, media->psz_name, &item );
1160                 return VLC_SUCCESS;
1161             }
1162
1163             media->vod_media =
1164                 vlm->vod->pf_media_new( vlm->vod, media->psz_name,
1165                                         &media->item );
1166         }
1167     }
1168
1169     return VLC_SUCCESS;
1170 }
1171
1172 int vlm_MediaControl( vlm_t *vlm, vlm_media_t *media, const char *psz_id,
1173                       const char *psz_command, const char *psz_args )
1174 {
1175     vlm_media_instance_t *p_instance;
1176     int i;
1177     char *psz_header;
1178
1179     p_instance = vlm_MediaInstanceSearch( vlm, media, psz_id );
1180
1181     if( !strcmp( psz_command, "play" ) )
1182     {
1183         if( !media->b_enabled || media->i_input == 0 ) return 0;
1184
1185         if( !p_instance )
1186         {
1187             p_instance = malloc( sizeof(vlm_media_instance_t) );
1188             if( !p_instance ) return VLC_EGENERIC;
1189             memset( p_instance, 0, sizeof(vlm_media_instance_t) );
1190             input_ItemInit( VLC_OBJECT(vlm), &p_instance->item );
1191             p_instance->p_input = NULL;
1192
1193             if( ( media->psz_output != NULL ) || ( media->psz_vod_output != NULL ) )
1194             {
1195                 p_instance->item.ppsz_options = malloc( sizeof( char* ) );
1196                 asprintf( &p_instance->item.ppsz_options[0], "sout=%s%s%s",
1197                           media->psz_output ? media->psz_output : "",
1198                           (media->psz_output && media->psz_vod_output) ?
1199                           ":" : media->psz_vod_output ? "#" : "",
1200                           media->psz_vod_output ? media->psz_vod_output : "" );
1201                 p_instance->item.i_options = 1;
1202             }
1203
1204             for( i = 0; i < media->i_option; i++ )
1205             {
1206                 p_instance->item.i_options++;
1207                 p_instance->item.ppsz_options =
1208                     realloc( p_instance->item.ppsz_options,
1209                              p_instance->item.i_options * sizeof( char* ) );
1210                 p_instance->item.ppsz_options[p_instance->item.i_options - 1] =
1211                     strdup( media->option[i] );
1212             }
1213
1214             p_instance->psz_name = psz_id ? strdup( psz_id ) : NULL;
1215             TAB_APPEND( media->i_instance, media->instance, p_instance );
1216         }
1217
1218         if( ( psz_args && sscanf(psz_args, "%d", &i) == 1 ) && ( i < media->i_input ) )
1219         {
1220             p_instance->i_index = i;
1221         }
1222
1223         if( p_instance->item.psz_uri ) free( p_instance->item.psz_uri );
1224         p_instance->item.psz_uri =
1225             strdup( media->input[p_instance->i_index] );
1226
1227         if( p_instance->p_input )
1228         {
1229             input_StopThread( p_instance->p_input );
1230             input_DestroyThread( p_instance->p_input );
1231             vlc_object_detach( p_instance->p_input );
1232             vlc_object_destroy( p_instance->p_input );
1233         }
1234
1235         asprintf( &psz_header, _("Media: %s"), media->psz_name );
1236         p_instance->p_input = input_CreateThread2( vlm, &p_instance->item,
1237                                                    psz_header );
1238         if( !p_instance->p_input )
1239         {
1240             TAB_REMOVE( media->i_instance, media->instance, p_instance );
1241             input_ItemClean( &p_instance->item );
1242             if( p_instance->psz_name ) free( p_instance->psz_name );
1243         }
1244         free( psz_header );
1245
1246         return VLC_SUCCESS;
1247     }
1248
1249     if( !p_instance ) return VLC_EGENERIC;
1250
1251     if( !strcmp( psz_command, "seek" ) )
1252     {
1253         if( psz_args )
1254         {
1255             vlc_bool_t i_rel;
1256             float f_value = i18n_atof( psz_args );
1257             if( psz_args[0] == '+' || psz_args[0] == '-' )
1258                 i_rel = VLC_TRUE;
1259             else
1260                 i_rel = VLC_FALSE;
1261             if( strstr( psz_args, "ms" ) )
1262             {
1263                 /* milliseconds */
1264                 int64_t i_msec =  1000 * (int64_t)atoi( psz_args );
1265                 if( i_rel )
1266                 {
1267                     var_SetTime( p_instance->p_input, "time-offset",
1268                                  i_msec );
1269                 }
1270                 else if( i_msec >= 0
1271                       && i_msec < var_GetTime( p_instance->p_input, "length" ) )
1272                 {
1273                     var_SetTime( p_instance->p_input, "time",
1274                                  i_msec );
1275                 }
1276             }
1277             else if( strchr( psz_args, 's' ) )
1278             {
1279                 /* seconds */
1280                 int64_t i_sec = 1000000 * (int64_t)atoi( psz_args );
1281                 if( i_rel )
1282                 {
1283                     var_SetTime( p_instance->p_input, "time-offset",
1284                                  i_sec );
1285                 }
1286                 else if( i_sec >= 0
1287                       && i_sec < var_GetTime( p_instance->p_input, "length" ) )
1288                 {
1289                     var_SetTime( p_instance->p_input, "time",
1290                                  i_sec );
1291                 }
1292             }
1293             else
1294             {
1295                 /* percentage */
1296                 f_value /= 100.;
1297                 if( i_rel )
1298                 {
1299                     float f_orig = var_GetFloat( p_instance->p_input,
1300                                                  "position" );
1301                     f_value += f_orig;
1302                 }
1303                 if( f_value >= 0.0 && f_value <= 1.0 )
1304                 {
1305                     var_SetFloat( p_instance->p_input, "position",
1306                                   f_value );
1307                     return VLC_SUCCESS;
1308                 }
1309             }
1310         }
1311     }
1312     else if( !strcmp( psz_command, "rewind" ) )
1313     {
1314         float f_pos;
1315         float f_scale;
1316
1317         if( psz_args )
1318         {
1319             f_scale = i18n_atof( psz_args );
1320             f_pos = var_GetFloat( p_instance->p_input, "position" );
1321             f_pos -= (f_scale / 1000.0);
1322             if( f_pos < 0. )
1323                 f_pos = 0.;
1324             var_SetFloat( p_instance->p_input, "position", f_pos );
1325             return VLC_SUCCESS;
1326         }
1327     }
1328     else if( !strcmp( psz_command, "forward" ) )
1329     {
1330         float f_pos;
1331         float f_scale;
1332
1333         if( psz_args )
1334         {
1335             f_scale = i18n_atof( psz_args );
1336             f_pos = var_GetFloat( p_instance->p_input, "position" );
1337             f_pos += (f_scale / 1000.0);
1338             if( f_pos > 1.0 )
1339                 f_pos = 1.0;
1340             var_SetFloat( p_instance->p_input, "position", f_pos );
1341             return VLC_SUCCESS;
1342         }
1343     }
1344     else if( !strcmp( psz_command, "stop" ) )
1345     {
1346         TAB_REMOVE( media->i_instance, media->instance, p_instance );
1347
1348         if( p_instance->p_input )
1349         {
1350             input_StopThread( p_instance->p_input );
1351             input_DestroyThread( p_instance->p_input );
1352             vlc_object_detach( p_instance->p_input );
1353             vlc_object_destroy( p_instance->p_input );
1354         }
1355
1356         input_ItemClean( &p_instance->item );
1357         if( p_instance->psz_name ) free( p_instance->psz_name );
1358         free( p_instance );
1359
1360         return VLC_SUCCESS;
1361     }
1362     else if( !strcmp( psz_command, "pause" ) )
1363     {
1364         int i_state;
1365
1366         if( !p_instance->p_input ) return VLC_SUCCESS;
1367
1368         i_state = var_GetInteger( p_instance->p_input, "state" );
1369
1370         if( i_state == PAUSE_S ) i_state = PLAYING_S;
1371         else i_state = PAUSE_S;
1372         var_SetInteger( p_instance->p_input, "state", i_state );
1373
1374         return VLC_SUCCESS;
1375     }
1376
1377     return VLC_EGENERIC;
1378 }
1379
1380 /*****************************************************************************
1381  * Schedule handling
1382  *****************************************************************************/
1383 static int64_t vlm_Date(void)
1384 {
1385 #ifdef WIN32
1386     struct timeb tm;
1387     ftime( &tm );
1388     return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000;
1389 #else
1390     return mdate();
1391 #endif
1392 }
1393
1394 vlm_schedule_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name )
1395 {
1396     vlm_schedule_t *p_sched = malloc( sizeof( vlm_schedule_t ) );
1397
1398     if( !p_sched )
1399     {
1400         return NULL;
1401     }
1402
1403     if( !psz_name )
1404     {
1405         return NULL;
1406     }
1407
1408     p_sched->psz_name = strdup( psz_name );
1409     p_sched->b_enabled = VLC_FALSE;
1410     p_sched->i_command = 0;
1411     p_sched->command = NULL;
1412     p_sched->i_date = 0;
1413     p_sched->i_period = 0;
1414     p_sched->i_repeat = -1;
1415
1416     TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched );
1417
1418     return p_sched;
1419 }
1420
1421 /* for now, simple delete. After, del with options (last arg) */
1422 void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_t *sched,
1423                          const char *psz_name )
1424 {
1425     if( sched == NULL ) return;
1426
1427     TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched );
1428
1429     if( vlm->i_schedule == 0 && vlm->schedule ) free( vlm->schedule );
1430     free( sched->psz_name );
1431     while( sched->i_command )
1432     {
1433         char *psz_cmd = sched->command[0];
1434         TAB_REMOVE( sched->i_command, sched->command, psz_cmd );
1435         free( psz_cmd );
1436     }
1437     free( sched );
1438 }
1439
1440 static vlm_schedule_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name )
1441 {
1442     int i;
1443
1444     for( i = 0; i < vlm->i_schedule; i++ )
1445     {
1446         if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 )
1447         {
1448             return vlm->schedule[i];
1449         }
1450     }
1451
1452     return NULL;
1453 }
1454
1455 /* Ok, setup schedule command will be able to support only one (argument value) at a time  */
1456 int vlm_ScheduleSetup( vlm_schedule_t *schedule, const char *psz_cmd,
1457                        const char *psz_value )
1458 {
1459     if( !strcmp( psz_cmd, "enabled" ) )
1460     {
1461         schedule->b_enabled = VLC_TRUE;
1462     }
1463     else if( !strcmp( psz_cmd, "disabled" ) )
1464     {
1465         schedule->b_enabled = VLC_FALSE;
1466     }
1467 #if !defined( UNDER_CE )
1468     else if( !strcmp( psz_cmd, "date" ) )
1469     {
1470         struct tm time;
1471         const char *p;
1472         time_t date;
1473
1474         time.tm_sec = 0;         /* seconds */
1475         time.tm_min = 0;         /* minutes */
1476         time.tm_hour = 0;        /* hours */
1477         time.tm_mday = 0;        /* day of the month */
1478         time.tm_mon = 0;         /* month */
1479         time.tm_year = 0;        /* year */
1480         time.tm_wday = 0;        /* day of the week */
1481         time.tm_yday = 0;        /* day in the year */
1482         time.tm_isdst = -1;       /* daylight saving time */
1483
1484         /* date should be year/month/day-hour:minutes:seconds */
1485         p = strchr( psz_value, '-' );
1486
1487         if( !strcmp( psz_value, "now" ) )
1488         {
1489             schedule->i_date = 0;
1490         }
1491         else if( (p == NULL) && sscanf( psz_value, "%d:%d:%d", &time.tm_hour,
1492                                         &time.tm_min, &time.tm_sec ) != 3 )
1493                                         /* it must be a hour:minutes:seconds */
1494         {
1495             return 1;
1496         }
1497         else
1498         {
1499             unsigned i,j,k;
1500
1501             switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) )
1502             {
1503                 case 1:
1504                     time.tm_sec = i;
1505                     break;
1506                 case 2:
1507                     time.tm_min = i;
1508                     time.tm_sec = j;
1509                     break;
1510                 case 3:
1511                     time.tm_hour = i;
1512                     time.tm_min = j;
1513                     time.tm_sec = k;
1514                     break;
1515                 default:
1516                     return 1;
1517             }
1518
1519             switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) )
1520             {
1521                 case 1:
1522                     time.tm_mday = i;
1523                     break;
1524                 case 2:
1525                     time.tm_mon = i - 1;
1526                     time.tm_mday = j;
1527                     break;
1528                 case 3:
1529                     time.tm_year = i - 1900;
1530                     time.tm_mon = j - 1;
1531                     time.tm_mday = k;
1532                     break;
1533                 default:
1534                     return 1;
1535             }
1536
1537             date = mktime( &time );
1538             schedule->i_date = ((mtime_t) date) * 1000000;
1539         }
1540     }
1541     else if( !strcmp( psz_cmd, "period" ) )
1542     {
1543         struct tm time;
1544         const char *p;
1545         const char *psz_time = NULL, *psz_date = NULL;
1546         time_t date;
1547         unsigned i,j,k;
1548
1549         /* First, if date or period are modified, repeat should be equal to -1 */
1550         schedule->i_repeat = -1;
1551
1552         time.tm_sec = 0;         /* seconds */
1553         time.tm_min = 0;         /* minutes */
1554         time.tm_hour = 0;        /* hours */
1555         time.tm_mday = 0;        /* day of the month */
1556         time.tm_mon = 0;         /* month */
1557         time.tm_year = 0;        /* year */
1558         time.tm_wday = 0;        /* day of the week */
1559         time.tm_yday = 0;        /* day in the year */
1560         time.tm_isdst = -1;       /* daylight saving time */
1561
1562         /* date should be year/month/day-hour:minutes:seconds */
1563         p = strchr( psz_value, '-' );
1564         if( p )
1565         {
1566             psz_date = psz_value;
1567             psz_time = p + 1;
1568         }
1569         else
1570         {
1571             psz_time = psz_value;
1572         }
1573
1574         switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) )
1575         {
1576             case 1:
1577                 time.tm_sec = i;
1578                 break;
1579             case 2:
1580                 time.tm_min = i;
1581                 time.tm_sec = j;
1582                 break;
1583             case 3:
1584                 time.tm_hour = i;
1585                 time.tm_min = j;
1586                 time.tm_sec = k;
1587                 break;
1588             default:
1589                 return 1;
1590         }
1591         if( psz_date )
1592         {
1593             switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) )
1594             {
1595                 case 1:
1596                     time.tm_mday = i;
1597                     break;
1598                 case 2:
1599                     time.tm_mon = i;
1600                     time.tm_mday = j;
1601                     break;
1602                 case 3:
1603                     time.tm_year = i;
1604                     time.tm_mon = j;
1605                     time.tm_mday = k;
1606                     break;
1607                 default:
1608                     return 1;
1609             }
1610         }
1611
1612         /* ok, that's stupid... who is going to schedule streams every 42 years ? */
1613         date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ;
1614         schedule->i_period = ((mtime_t) date) * 1000000;
1615     }
1616 #endif /* UNDER_CE */
1617     else if( !strcmp( psz_cmd, "repeat" ) )
1618     {
1619         int i;
1620
1621         if( sscanf( psz_value, "%d", &i ) == 1 )
1622         {
1623             schedule->i_repeat = i;
1624         }
1625         else
1626         {
1627             return 1;
1628         }
1629     }
1630     else if( !strcmp( psz_cmd, "append" ) )
1631     {
1632         char *command = strdup( psz_value );
1633
1634         TAB_APPEND( schedule->i_command, schedule->command, command );
1635     }
1636     else
1637     {
1638         return 1;
1639     }
1640     return 0;
1641 }
1642
1643 /*****************************************************************************
1644  * Message handling functions
1645  *****************************************************************************/
1646 vlm_message_t *vlm_MessageNew( const char *psz_name,
1647                                const char *psz_format, ... )
1648 {
1649     vlm_message_t *p_message;
1650     va_list args;
1651
1652     if( !psz_name ) return NULL;
1653
1654     p_message = malloc( sizeof(vlm_message_t) );
1655     if( !p_message)
1656     {
1657         return NULL;
1658     }
1659
1660     p_message->psz_value = 0;
1661
1662     if( psz_format )
1663     {
1664         va_start( args, psz_format );
1665         if( vasprintf( &p_message->psz_value, psz_format, args ) == -1 )
1666         {
1667             va_end( args );
1668             free( p_message );
1669             return NULL;
1670         }
1671         va_end( args );
1672     }
1673
1674     p_message->psz_name = strdup( psz_name );
1675     p_message->i_child = 0;
1676     p_message->child = NULL;
1677
1678     return p_message;
1679 }
1680
1681 void vlm_MessageDelete( vlm_message_t *p_message )
1682 {
1683     if( p_message->psz_name ) free( p_message->psz_name );
1684     if( p_message->psz_value ) free( p_message->psz_value );
1685     while( p_message->i_child-- )
1686         vlm_MessageDelete( p_message->child[p_message->i_child] );
1687     if( p_message->child ) free( p_message->child );
1688     free( p_message );
1689 }
1690
1691 /* Add a child */
1692 vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message,
1693                                vlm_message_t *p_child )
1694 {
1695     if( p_message == NULL ) return NULL;
1696
1697     if( p_child )
1698     {
1699         TAB_APPEND( p_message->i_child, p_message->child, p_child );
1700     }
1701
1702     return p_child;
1703 }
1704
1705 /*****************************************************************************
1706  * Misc utility functions
1707  *****************************************************************************/
1708 static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_t *media,
1709                                 vlm_schedule_t *schedule,
1710                                 const char *psz_filter )
1711 {
1712     if( media != NULL )
1713     {
1714         int i;
1715         vlm_message_t *msg;
1716         vlm_message_t *msg_media;
1717         vlm_message_t *msg_child;
1718
1719         msg = vlm_MessageNew( "show", NULL );
1720         msg_media = vlm_MessageAdd( msg, vlm_MessageNew( media->psz_name, 0 ));
1721
1722         vlm_MessageAdd( msg_media,
1723                         vlm_MessageNew( "type", media->i_type == VOD_TYPE ?
1724                                         "vod" : "broadcast" ) );
1725         vlm_MessageAdd( msg_media,
1726                         vlm_MessageNew( "enabled", media->b_enabled ?
1727                                         "yes" : "no" ) );
1728
1729         vlm_MessageAdd( msg_media,
1730                         vlm_MessageNew( "loop", media->b_loop ?
1731                                         "yes" : "no" ) );
1732
1733         if( media->i_type == VOD_TYPE && media->psz_mux )
1734             vlm_MessageAdd( msg_media,
1735                             vlm_MessageNew( "mux", media->psz_mux ) );
1736
1737         msg_child = vlm_MessageAdd( msg_media,
1738                                     vlm_MessageNew( "inputs", NULL ) );
1739
1740         for( i = 0; i < media->i_input; i++ )
1741         {
1742             vlm_MessageAdd( msg_child,
1743                             vlm_MessageNew( media->input[i], NULL ) );
1744         }
1745
1746         vlm_MessageAdd( msg_media,
1747                         vlm_MessageNew( "output", media->psz_output ?
1748                                         media->psz_output : "" ) );
1749
1750         msg_child = vlm_MessageAdd( msg_media, vlm_MessageNew( "options", 0 ));
1751
1752         for( i = 0; i < media->i_option; i++ )
1753         {
1754             vlm_MessageAdd( msg_child, vlm_MessageNew( media->option[i], 0 ) );
1755         }
1756
1757         msg_child = vlm_MessageAdd( msg_media,
1758                                     vlm_MessageNew( "instances", NULL ) );
1759
1760         for( i = 0; i < media->i_instance; i++ )
1761         {
1762             vlm_media_instance_t *p_instance = media->instance[i];
1763             vlc_value_t val;
1764             vlm_message_t *msg_instance;
1765             char *psz_tmp;
1766
1767             if( !p_instance->p_input ) val.i_int = END_S;
1768             else var_Get( p_instance->p_input, "state", &val );
1769
1770             msg_instance = vlm_MessageNew( "instance" , NULL );
1771             vlm_MessageAdd( msg_instance, vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) );
1772             vlm_MessageAdd( msg_instance, vlm_MessageNew( "state",
1773                                 val.i_int == PLAYING_S ? "playing" :
1774                                 val.i_int == PAUSE_S ? "paused" :
1775                                 "stopped" ) );
1776 #define APPEND_INPUT_INFO( a, format, type ) \
1777             asprintf( &psz_tmp, format, \
1778                       var_Get ## type( p_instance->p_input, a ) ); \
1779             vlm_MessageAdd( msg_instance, vlm_MessageNew( a, psz_tmp ) ); \
1780             free( psz_tmp );
1781             APPEND_INPUT_INFO( "position", "%f", Float );
1782             APPEND_INPUT_INFO( "time", I64Fi, Time );
1783             APPEND_INPUT_INFO( "length", I64Fi, Time );
1784             APPEND_INPUT_INFO( "rate", "%d", Integer );
1785             APPEND_INPUT_INFO( "title", "%d", Integer );
1786             APPEND_INPUT_INFO( "chapter", "%d", Integer );
1787             APPEND_INPUT_INFO( "seekable", "%d", Bool );
1788 #undef APPEND_INPUT_INFO
1789             asprintf( &psz_tmp, "%d", p_instance->i_index + 1 );
1790             vlm_MessageAdd( msg_instance, vlm_MessageNew( "playlistindex", psz_tmp ) );
1791             free( psz_tmp );
1792             vlm_MessageAdd( msg_child, msg_instance );
1793         }
1794
1795         return msg;
1796
1797     }
1798
1799     else if( schedule != NULL )
1800     {
1801         int i;
1802         vlm_message_t *msg;
1803         vlm_message_t *msg_schedule;
1804         vlm_message_t *msg_child;
1805         char buffer[100];
1806
1807         msg = vlm_MessageNew( "show", NULL );
1808         msg_schedule =
1809             vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, 0 ) );
1810
1811         vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") );
1812
1813         vlm_MessageAdd( msg_schedule,
1814                         vlm_MessageNew( "enabled", schedule->b_enabled ?
1815                                         "yes" : "no" ) );
1816
1817 #if !defined( UNDER_CE )
1818         if( schedule->i_date != 0 )
1819         {
1820             struct tm date;
1821             time_t i_time = (time_t)( schedule->i_date / 1000000 );
1822             char *psz_date;
1823
1824 #ifdef HAVE_LOCALTIME_R
1825             localtime_r( &i_time, &date);
1826 #else
1827             struct tm *p_date = localtime( &i_time );
1828             date = *p_date;
1829 #endif
1830
1831             asprintf( &psz_date, "%d/%d/%d-%d:%d:%d",
1832                       date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1833                       date.tm_hour, date.tm_min, date.tm_sec );
1834
1835             vlm_MessageAdd( msg_schedule,
1836                             vlm_MessageNew( "date", psz_date ) );
1837             free( psz_date );
1838         }
1839         else
1840         {
1841             vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") );
1842         }
1843
1844         if( schedule->i_period != 0 )
1845         {
1846             time_t i_time = (time_t) ( schedule->i_period / 1000000 );
1847             struct tm date;
1848
1849             date.tm_sec = (int)( i_time % 60 );
1850             i_time = i_time / 60;
1851             date.tm_min = (int)( i_time % 60 );
1852             i_time = i_time / 60;
1853             date.tm_hour = (int)( i_time % 24 );
1854             i_time = i_time / 24;
1855             date.tm_mday = (int)( i_time % 30 );
1856             i_time = i_time / 30;
1857             /* okay, okay, months are not always 30 days long */
1858             date.tm_mon = (int)( i_time % 12 );
1859             i_time = i_time / 12;
1860             date.tm_year = (int)i_time;
1861
1862             sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon,
1863                      date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
1864
1865             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) );
1866         }
1867         else
1868         {
1869             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") );
1870         }
1871 #endif /* UNDER_CE */
1872
1873         sprintf( buffer, "%d", schedule->i_repeat );
1874         vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) );
1875
1876         msg_child =
1877             vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", 0) );
1878
1879         for( i = 0; i < schedule->i_command; i++ )
1880         {
1881            vlm_MessageAdd( msg_child,
1882                            vlm_MessageNew( schedule->command[i], NULL ) );
1883         }
1884
1885         return msg;
1886
1887     }
1888
1889     else if( psz_filter && !strcmp( psz_filter, "media" ) )
1890     {
1891         int i, j;
1892         vlm_message_t *msg;
1893         vlm_message_t *msg_child;
1894         int i_vod = 0, i_broadcast = 0;
1895         char *psz_count;
1896
1897         for( i = 0; i < vlm->i_media; i++ )
1898         {
1899             if( vlm->media[i]->i_type == VOD_TYPE )
1900                 i_vod ++;
1901             else
1902                 i_broadcast ++;
1903         }
1904
1905         asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast, i_vod);
1906
1907         msg = vlm_MessageNew( "show", NULL );
1908         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "media", psz_count ) );
1909         free( psz_count );
1910
1911         for( i = 0; i < vlm->i_media; i++ )
1912         {
1913             vlm_media_t *m = vlm->media[i];
1914             vlm_message_t *msg_media, *msg_instance;
1915
1916             msg_media = vlm_MessageAdd( msg_child,
1917                                         vlm_MessageNew( m->psz_name, 0 ) );
1918
1919             vlm_MessageAdd( msg_media,
1920                             vlm_MessageNew( "type", m->i_type == VOD_TYPE ?
1921                                             "vod" : "broadcast" ) );
1922
1923             vlm_MessageAdd( msg_media,
1924                             vlm_MessageNew( "enabled", m->b_enabled ?
1925                                             "yes" : "no" ) );
1926
1927             if( m->i_type == VOD_TYPE && m->psz_mux )
1928                 vlm_MessageAdd( msg_media,
1929                                 vlm_MessageNew( "mux", m->psz_mux ) );
1930
1931             msg_instance = vlm_MessageAdd( msg_media,
1932                                            vlm_MessageNew( "instances", 0 ) );
1933
1934             for( j = 0; j < m->i_instance; j++ )
1935             {
1936                 vlm_media_instance_t *p_instance = m->instance[j];
1937                 vlc_value_t val;
1938
1939                 if( !p_instance->p_input ) val.i_int = END_S;
1940                 else var_Get( p_instance->p_input, "state", &val );
1941
1942                 vlm_MessageAdd( msg_instance,
1943                     vlm_MessageNew( p_instance->psz_name ?
1944                                     p_instance->psz_name : "default",
1945                                     val.i_int == PLAYING_S ? "playing" :
1946                                     val.i_int == PAUSE_S ? "paused" :
1947                                     "stopped" ) );
1948             }
1949         }
1950
1951         return msg;
1952     }
1953
1954     else if( psz_filter && !strcmp( psz_filter, "schedule" ) )
1955     {
1956         int i;
1957         vlm_message_t *msg;
1958         vlm_message_t *msg_child;
1959
1960         msg = vlm_MessageNew( "show", NULL );
1961         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", NULL ) );
1962
1963         for( i = 0; i < vlm->i_schedule; i++ )
1964         {
1965             vlm_schedule_t *s = vlm->schedule[i];
1966             vlm_message_t *msg_schedule;
1967             mtime_t i_time, i_next_date;
1968
1969             msg_schedule = vlm_MessageAdd( msg_child,
1970                                            vlm_MessageNew( s->psz_name, 0 ) );
1971             vlm_MessageAdd( msg_schedule,
1972                             vlm_MessageNew( "enabled", s->b_enabled ?
1973                                             "yes" : "no" ) );
1974
1975             /* calculate next date */
1976             i_time = vlm_Date();
1977             i_next_date = s->i_date;
1978
1979             if( s->i_period != 0 )
1980             {
1981                 int j = 0;
1982                 while( s->i_date + j * s->i_period <= i_time &&
1983                        s->i_repeat > j )
1984                 {
1985                     j++;
1986                 }
1987
1988                 i_next_date = s->i_date + j * s->i_period;
1989             }
1990
1991             if( i_next_date > i_time )
1992             {
1993                 time_t i_date = (time_t) (i_next_date / 1000000) ;
1994
1995 #if !defined( UNDER_CE )
1996 #ifdef HAVE_CTIME_R
1997                 char psz_date[500];
1998                 ctime_r( &i_date, psz_date );
1999 #else
2000                 char *psz_date = ctime( &i_date );
2001 #endif
2002
2003                 vlm_MessageAdd( msg_schedule,
2004                                 vlm_MessageNew( "next launch", psz_date ) );
2005 #endif
2006             }
2007         }
2008
2009         return msg;
2010     }
2011
2012     else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) )
2013     {
2014         vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" );
2015         vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" );
2016
2017         vlm_MessageAdd( show1, show2->child[0] );
2018
2019         /* We must destroy the parent node "show" of show2
2020          * and not the children */
2021         free( show2->psz_name );
2022         free( show2 );
2023
2024         return show1;
2025     }
2026
2027     else
2028     {
2029         return vlm_MessageNew( "show", NULL );
2030     }
2031 }
2032
2033 static vlm_message_t *vlm_Help( vlm_t *vlm, char *psz_filter )
2034 {
2035     vlm_message_t *message, *message_child;
2036
2037 #define MessageAdd( a ) \
2038         vlm_MessageAdd( message, vlm_MessageNew( a, NULL ) );
2039 #define MessageAddChild( a ) \
2040         vlm_MessageAdd( message_child, vlm_MessageNew( a, NULL ) );
2041
2042     if( psz_filter == NULL )
2043     {
2044         message = vlm_MessageNew( "help", NULL );
2045
2046         message_child = MessageAdd( "Commands Syntax:" );
2047         MessageAddChild( "new (name) vod|broadcast|schedule [properties]" );
2048         MessageAddChild( "setup (name) (properties)" );
2049         MessageAddChild( "show [(name)|media|schedule]" );
2050         MessageAddChild( "del (name)|all|media|schedule" );
2051         MessageAddChild( "control (name) [instance_name] (command)" );
2052         MessageAddChild( "save (config_file)" );
2053         MessageAddChild( "export" );
2054         MessageAddChild( "load (config_file)" );
2055
2056         message_child = MessageAdd( "Media Proprieties Syntax:" );
2057         MessageAddChild( "input (input_name)" );
2058         MessageAddChild( "inputdel (input_name)|all" );
2059         MessageAddChild( "inputdeln input_number" );
2060         MessageAddChild( "output (output_name)" );
2061         MessageAddChild( "option (option_name)[=value]" );
2062         MessageAddChild( "enabled|disabled" );
2063         MessageAddChild( "loop|unloop (broadcast only)" );
2064         MessageAddChild( "mux (mux_name)" );
2065
2066         message_child = MessageAdd( "Schedule Proprieties Syntax:" );
2067         MessageAddChild( "enabled|disabled" );
2068         MessageAddChild( "append (command_until_rest_of_the_line)" );
2069         MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):"
2070                          "(seconds)|now" );
2071         MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/"
2072                          "(days)-(hours):(minutes):(seconds)" );
2073         MessageAddChild( "repeat (number_of_repetitions)" );
2074
2075         message_child = MessageAdd( "Control Commands Syntax:" );
2076         MessageAddChild( "play" );
2077         MessageAddChild( "pause" );
2078         MessageAddChild( "stop" );
2079         MessageAddChild( "seek [+-](percentage) | [+-](seconds)s | [+-](miliseconds)ms" );
2080
2081         return message;
2082     }
2083
2084     return vlm_MessageNew( "help", NULL );
2085 }
2086
2087 /*****************************************************************************
2088  * Config handling functions
2089  *****************************************************************************/
2090 static int Load( vlm_t *vlm, char *file )
2091 {
2092     char *pf = file;
2093     int  i_line = 1;
2094
2095     while( *pf != '\0' )
2096     {
2097         vlm_message_t *message = NULL;
2098         int i_end = 0;
2099
2100         while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' )
2101         {
2102             i_end++;
2103         }
2104
2105         if( pf[i_end] == '\r' || pf[i_end] == '\n' )
2106         {
2107             pf[i_end] = '\0';
2108             i_end++;
2109             if( pf[i_end] == '\n' ) i_end++;
2110         }
2111
2112         if( *pf && ExecuteCommand( vlm, pf, &message ) )
2113         {
2114             if( message )
2115             {
2116                 if( message->psz_value )
2117                     msg_Err( vlm, "Load error on line %d: %s: %s",
2118                              i_line, message->psz_name, message->psz_value );
2119                 vlm_MessageDelete( message );
2120             }
2121             return 1;
2122         }
2123         if( message ) vlm_MessageDelete( message );
2124
2125         pf += i_end;
2126         i_line++;
2127     }
2128
2129     return 0;
2130 }
2131
2132 static char *Save( vlm_t *vlm )
2133 {
2134     char *save = NULL;
2135     char psz_header[] = "\n"
2136                         "# VLC media player VLM command batch\n"
2137                         "# http://www.videolan.org/vlc/\n\n" ;
2138     char *p;
2139     int i,j;
2140     int i_length = strlen( psz_header );
2141
2142     for( i = 0; i < vlm->i_media; i++ )
2143     {
2144         vlm_media_t *media = vlm->media[i];
2145
2146         if( media->i_type == VOD_TYPE )
2147         {
2148             i_length += strlen( "new  vod " ) + strlen(media->psz_name);
2149         }
2150         else
2151         {
2152             i_length += strlen( "new  broadcast " ) + strlen(media->psz_name);
2153         }
2154
2155         if( media->b_enabled == VLC_TRUE )
2156         {
2157             i_length += strlen( "enabled" );
2158         }
2159         else
2160         {
2161             i_length += strlen( "disabled" );
2162         }
2163
2164         if( media->b_loop == VLC_TRUE )
2165         {
2166             i_length += strlen( " loop\n" );
2167         }
2168         else
2169         {
2170             i_length += strlen( "\n" );
2171         }
2172
2173         for( j = 0; j < media->i_input; j++ )
2174         {
2175             i_length += strlen( "setup  input \"\"\n" ) +
2176                 strlen( media->psz_name ) + strlen( media->input[j] );
2177         }
2178
2179         if( media->psz_output != NULL )
2180         {
2181             i_length += strlen(media->psz_name) + strlen(media->psz_output) +
2182                 strlen( "setup  output \n" );
2183         }
2184
2185         for( j=0 ; j < media->i_option ; j++ )
2186         {
2187             i_length += strlen(media->psz_name) + strlen(media->option[j]) +
2188                 strlen("setup  option \n");
2189         }
2190     }
2191
2192     for( i = 0; i < vlm->i_schedule; i++ )
2193     {
2194         vlm_schedule_t *schedule = vlm->schedule[i];
2195
2196         i_length += strlen( "new  schedule " ) + strlen( schedule->psz_name );
2197
2198         if( schedule->b_enabled == VLC_TRUE )
2199         {
2200             i_length += strlen( "date //-:: enabled\n" ) + 14;
2201         }
2202         else
2203         {
2204             i_length += strlen( "date //-:: disabled\n" ) + 14;
2205         }
2206
2207
2208         if( schedule->i_period != 0 )
2209         {
2210             i_length += strlen( "setup  " ) + strlen( schedule->psz_name ) +
2211                 strlen( "period //-::\n" ) + 14;
2212         }
2213
2214         if( schedule->i_repeat >= 0 )
2215         {
2216             char buffer[12];
2217
2218             sprintf( buffer, "%d", schedule->i_repeat );
2219             i_length += strlen( "setup  repeat \n" ) +
2220                 strlen( schedule->psz_name ) + strlen( buffer );
2221         }
2222         else
2223         {
2224             i_length++;
2225         }
2226
2227         for( j = 0; j < schedule->i_command; j++ )
2228         {
2229             i_length += strlen( "setup  append \n" ) +
2230                 strlen( schedule->psz_name ) + strlen( schedule->command[j] );
2231         }
2232
2233     }
2234
2235     /* Don't forget the '\0' */
2236     i_length++;
2237     /* now we have the length of save */
2238
2239     p = save = malloc( i_length );
2240     *save = '\0';
2241
2242     p += sprintf( p, "%s", psz_header );
2243
2244     /* finally we can write in it */
2245     for( i = 0; i < vlm->i_media; i++ )
2246     {
2247         vlm_media_t *media = vlm->media[i];
2248
2249         if( media->i_type == VOD_TYPE )
2250         {
2251             p += sprintf( p, "new %s vod ", media->psz_name);
2252         }
2253         else
2254         {
2255             p += sprintf( p, "new %s broadcast ", media->psz_name);
2256         }
2257
2258         if( media->b_enabled == VLC_TRUE )
2259         {
2260             p += sprintf( p, "enabled" );
2261         }
2262         else
2263         {
2264             p += sprintf( p, "disabled" );
2265         }
2266
2267         if( media->b_loop == VLC_TRUE )
2268         {
2269             p += sprintf( p, " loop\n" );
2270         }
2271         else
2272         {
2273             p += sprintf( p, "\n" );
2274         }
2275
2276         for( j = 0; j < media->i_input; j++ )
2277         {
2278             p += sprintf( p, "setup %s input \"%s\"\n", media->psz_name,
2279                           media->input[j] );
2280         }
2281
2282         if( media->psz_output != NULL )
2283         {
2284             p += sprintf( p, "setup %s output %s\n", media->psz_name,
2285                           media->psz_output );
2286         }
2287
2288         for( j = 0; j < media->i_option; j++ )
2289         {
2290             p += sprintf( p, "setup %s option %s\n", media->psz_name,
2291                           media->option[j] );
2292         }
2293     }
2294
2295     /* and now, the schedule scripts */
2296 #if !defined( UNDER_CE )
2297     for( i = 0; i < vlm->i_schedule; i++ )
2298     {
2299         vlm_schedule_t *schedule = vlm->schedule[i];
2300         struct tm date;
2301         time_t i_time = (time_t) ( schedule->i_date / 1000000 );
2302
2303 #ifdef HAVE_LOCALTIME_R
2304         localtime_r( &i_time, &date);
2305 #else
2306         struct tm *p_date = localtime( &i_time );
2307         date = *p_date;
2308 #endif
2309
2310         p += sprintf( p, "new %s schedule ", schedule->psz_name);
2311
2312         if( schedule->b_enabled == VLC_TRUE )
2313         {
2314             p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n",
2315                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
2316                           date.tm_hour, date.tm_min, date.tm_sec );
2317         }
2318         else
2319         {
2320             p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n",
2321                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
2322                           date.tm_hour, date.tm_min, date.tm_sec);
2323         }
2324
2325         if( schedule->i_period != 0 )
2326         {
2327             p += sprintf( p, "setup %s ", schedule->psz_name );
2328
2329             i_time = (time_t) ( schedule->i_period / 1000000 );
2330
2331             date.tm_sec = (int)( i_time % 60 );
2332             i_time = i_time / 60;
2333             date.tm_min = (int)( i_time % 60 );
2334             i_time = i_time / 60;
2335             date.tm_hour = (int)( i_time % 24 );
2336             i_time = i_time / 24;
2337             date.tm_mday = (int)( i_time % 30 );
2338             i_time = i_time / 30;
2339             /* okay, okay, months are not always 30 days long */
2340             date.tm_mon = (int)( i_time % 12 );
2341             i_time = i_time / 12;
2342             date.tm_year = (int)i_time;
2343
2344             p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n",
2345                           date.tm_year, date.tm_mon, date.tm_mday,
2346                           date.tm_hour, date.tm_min, date.tm_sec);
2347         }
2348
2349         if( schedule->i_repeat >= 0 )
2350         {
2351             p += sprintf( p, "setup %s repeat %d\n",
2352                           schedule->psz_name, schedule->i_repeat );
2353         }
2354         else
2355         {
2356             p += sprintf( p, "\n" );
2357         }
2358
2359         for( j = 0; j < schedule->i_command; j++ )
2360         {
2361             p += sprintf( p, "setup %s append %s\n",
2362                           schedule->psz_name, schedule->command[j] );
2363         }
2364
2365     }
2366 #endif /* UNDER_CE */
2367
2368     return save;
2369 }
2370
2371 /*****************************************************************************
2372  * Manage:
2373  *****************************************************************************/
2374 int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
2375                          const char *psz_id, int i_query, va_list args )
2376 {
2377     vlm_t *vlm = (vlm_t *)p_private;
2378     int i, i_ret = VLC_EGENERIC;
2379
2380     if( !p_private || !p_vod_media ) return VLC_EGENERIC;
2381
2382     vlc_mutex_lock( &vlm->lock );
2383
2384     /* Find media */
2385     for( i = 0; i < vlm->i_media; i++ )
2386     {
2387         if( p_vod_media == vlm->media[i]->vod_media ) break;
2388     }
2389
2390     if( i == vlm->i_media )
2391     {
2392         vlc_mutex_unlock( &vlm->lock );
2393         return VLC_EGENERIC;
2394     }
2395
2396     switch( i_query )
2397     {
2398     case VOD_MEDIA_PLAY:
2399         vlm->media[i]->psz_vod_output = (char *)va_arg( args, char * );
2400         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "play", 0 );
2401         vlm->media[i]->psz_vod_output = 0;
2402         break;
2403
2404     case VOD_MEDIA_PAUSE:
2405         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "pause", 0 );
2406         break;
2407
2408     case VOD_MEDIA_STOP:
2409         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "stop", 0 );
2410         break;
2411
2412     case VOD_MEDIA_SEEK:
2413     {
2414         double f_pos = (double)va_arg( args, double );
2415         char psz_pos[50];
2416         lldiv_t div = lldiv( f_pos * 10000000, 10000000 );
2417         sprintf( psz_pos, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2418         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "seek", psz_pos);
2419         break;
2420     }
2421
2422     case VOD_MEDIA_REWIND:
2423     {
2424         double f_scale = (double)va_arg( args, double );
2425         char psz_scale[50];
2426         lldiv_t div = lldiv( f_scale * 10000000, 10000000 );
2427         sprintf( psz_scale, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2428         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "rewind", psz_scale );
2429         break;
2430     }
2431
2432     case VOD_MEDIA_FORWARD:
2433     {
2434         double f_scale = (double)va_arg( args, double );
2435         char psz_scale[50];
2436         lldiv_t div = lldiv( f_scale * 10000000, 10000000 );
2437         sprintf( psz_scale, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2438         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "forward", psz_scale );
2439         break;
2440     }
2441
2442     default:
2443         break;
2444     }
2445
2446     vlc_mutex_unlock( &vlm->lock );
2447
2448     return i_ret;
2449 }
2450
2451
2452 /*****************************************************************************
2453  * Manage:
2454  *****************************************************************************/
2455 static int Manage( vlc_object_t* p_object )
2456 {
2457     vlm_t *vlm = (vlm_t*)p_object;
2458     int i, j;
2459     mtime_t i_lastcheck;
2460     mtime_t i_time;
2461
2462     i_lastcheck = vlm_Date();
2463
2464     msleep( 100000 );
2465
2466     while( !vlm->b_die )
2467     {
2468         char **ppsz_scheduled_commands = NULL;
2469         int    i_scheduled_commands = 0;
2470         vlc_mutex_lock( &vlm->lock );
2471
2472         /* destroy the inputs that wants to die, and launch the next input */
2473         for( i = 0; i < vlm->i_media; i++ )
2474         {
2475             vlm_media_t *p_media = vlm->media[i];
2476
2477             for( j = 0; j < p_media->i_instance; j++ )
2478             {
2479                 vlm_media_instance_t *p_instance = p_media->instance[j];
2480
2481                 if( !p_instance->p_input ||
2482                     ( !p_instance->p_input->b_eof &&
2483                       !p_instance->p_input->b_error ) ) continue;
2484
2485                 input_StopThread( p_instance->p_input );
2486                 input_DestroyThread( p_instance->p_input );
2487                 vlc_object_detach( p_instance->p_input );
2488                 vlc_object_destroy( p_instance->p_input );
2489
2490                 p_instance->i_index++;
2491                 if( p_instance->i_index == p_media->i_input &&
2492                     p_media->b_loop ) p_instance->i_index = 0;
2493
2494                 if( p_instance->i_index < p_media->i_input )
2495                 {
2496                     /* FIXME, find a way to select the right instance */
2497                     char buffer[12];
2498                     sprintf( buffer, "%d", p_instance->i_index );
2499                     vlm_MediaControl( vlm, p_media, p_instance->psz_name,
2500                                       "play", buffer );
2501                 }
2502                 else
2503                 {
2504                     if( vlm_MediaControl( vlm, p_media, p_instance->psz_name,
2505                                           "stop", 0 ) == VLC_SUCCESS )
2506                     {
2507                          j--; /* the aray is one element smaller now. */
2508                     }
2509                 }
2510             }
2511         }
2512
2513         /* scheduling */
2514         i_time = vlm_Date();
2515
2516         for( i = 0; i < vlm->i_schedule; i++ )
2517         {
2518             mtime_t i_real_date = vlm->schedule[i]->i_date;
2519
2520             if( vlm->schedule[i]->b_enabled == VLC_TRUE )
2521             {
2522                 if( vlm->schedule[i]->i_date == 0 ) // now !
2523                 {
2524                     vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ;
2525                     i_real_date = i_time;
2526                 }
2527                 else if( vlm->schedule[i]->i_period != 0 )
2528                 {
2529                     int j = 0;
2530                     while( vlm->schedule[i]->i_date + j *
2531                            vlm->schedule[i]->i_period <= i_lastcheck &&
2532                            ( vlm->schedule[i]->i_repeat > j ||
2533                              vlm->schedule[i]->i_repeat == -1 ) )
2534                     {
2535                         j++;
2536                     }
2537
2538                     i_real_date = vlm->schedule[i]->i_date + j *
2539                         vlm->schedule[i]->i_period;
2540                 }
2541
2542                 if( i_real_date <= i_time && i_real_date > i_lastcheck )
2543                 {
2544                     for( j = 0; j < vlm->schedule[i]->i_command; j++ )
2545                     {
2546                         TAB_APPEND( i_scheduled_commands,
2547                                     ppsz_scheduled_commands,
2548                                     strdup(vlm->schedule[i]->command[j] ) );
2549                     }
2550                 }
2551             }
2552         }
2553         while( i_scheduled_commands )
2554         {
2555             vlm_message_t *message = NULL;
2556             char *psz_command = ppsz_scheduled_commands[0];
2557             ExecuteCommand( vlm, psz_command,&message );
2558
2559             /* for now, drop the message */
2560             vlm_MessageDelete( message );
2561             TAB_REMOVE( i_scheduled_commands,
2562                         ppsz_scheduled_commands,
2563                         psz_command );
2564             free( psz_command );
2565         }
2566
2567         i_lastcheck = i_time;
2568
2569         vlc_mutex_unlock( &vlm->lock );
2570
2571         msleep( 100000 );
2572     }
2573
2574     return VLC_SUCCESS;
2575 }
2576
2577 #else /* ENABLE_VLM */
2578
2579 /* We just define an empty wrapper */
2580 vlm_t *__vlm_New( vlc_object_t *a )
2581 {
2582     msg_Err( a, "VideoLAN manager support is disabled" );
2583     return 0;
2584 }
2585 void vlm_Delete( vlm_t *a ){}
2586 int vlm_ExecuteCommand( vlm_t *a, char *b, vlm_message_t **c ){ return -1; }
2587 void vlm_MessageDelete( vlm_message_t *a ){}
2588 vlm_media_t *vlm_MediaNew( vlm_t *a, char *b, int c ){ return NULL; }
2589 vlm_media_t *vlm_MediaSearch (vlm_t *a, const char *b ) { return NULL; }
2590 void vlm_MediaDelete( vlm_t *a, vlm_media_t *b, char *c ){}
2591 int vlm_MediaSetup( vlm_t *a, vlm_media_t *b, char *c, char *d ){ return -1; }
2592 int vlm_MediaControl( vlm_t *a, vlm_media_t *b, char *c, char *d, char *e )
2593     { return -1; }
2594 vlm_schedule_t * vlm_ScheduleNew( vlm_t *a, char *b ){ return NULL; }
2595 void  vlm_ScheduleDelete( vlm_t *a, vlm_schedule_t *b, char *c ){}
2596 int vlm_ScheduleSetup( vlm_schedule_t *a, char *b, char *c ){ return -1; }
2597 int vlm_MediaVodControl( void *a, vod_media_t *b, char *c, int d, va_list e )
2598     { return -1; }
2599 int vlm_Save( vlm_t *a, char *b ){ return -1; }
2600 int vlm_Load( vlm_t *a, char *b ){ return -1; }
2601
2602 #endif /* ENABLE_VLM */