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