]> git.sesse.net Git - vlc/blob - src/input/vlm.c
Force VLM stream to not use sout-keep.
[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_CreateThread2( vlm, &media->item, psz_header
1144                                               ) ) )
1145             {
1146                 while( !p_input->b_eof && !p_input->b_error )
1147                     msleep( 100000 );
1148
1149                 input_StopThread( p_input );
1150                 input_DestroyThread( p_input );
1151             }
1152             free( psz_output );
1153             free( psz_header );
1154
1155             if( media->psz_mux )
1156             {
1157                 input_item_t item;
1158                 es_format_t es, *p_es = &es;
1159                 char fourcc[5];
1160
1161                 sprintf( fourcc, "%4.4s", media->psz_mux );
1162                 fourcc[0] = tolower(fourcc[0]); fourcc[1] = tolower(fourcc[1]);
1163                 fourcc[2] = tolower(fourcc[2]); fourcc[3] = tolower(fourcc[3]);
1164
1165                 item = media->item;
1166                 item.i_es = 1;
1167                 item.es = &p_es;
1168                 es_format_Init( &es, VIDEO_ES, *((int *)fourcc) );
1169
1170                 media->vod_media =
1171                   vlm->vod->pf_media_new( vlm->vod, media->psz_name, &item );
1172                 return VLC_SUCCESS;
1173             }
1174
1175             media->vod_media =
1176                 vlm->vod->pf_media_new( vlm->vod, media->psz_name,
1177                                         &media->item );
1178         }
1179     }
1180
1181     return VLC_SUCCESS;
1182 }
1183
1184 int vlm_MediaControl( vlm_t *vlm, vlm_media_t *media, const char *psz_id,
1185                       const char *psz_command, const char *psz_args )
1186 {
1187     vlm_media_instance_t *p_instance;
1188     int i;
1189     char *psz_header;
1190
1191     p_instance = vlm_MediaInstanceSearch( vlm, media, psz_id );
1192
1193     if( !strcmp( psz_command, "play" ) )
1194     {
1195         if( !media->b_enabled || media->i_input == 0 ) return 0;
1196
1197         if( !p_instance )
1198         {
1199             char *psz_dup;
1200
1201             p_instance = malloc( sizeof(vlm_media_instance_t) );
1202             if( !p_instance )
1203                 return VLC_ENOMEM;
1204
1205             memset( p_instance, 0, sizeof(vlm_media_instance_t) );
1206
1207             p_instance->psz_name = psz_id ? strdup( psz_id ) : NULL;
1208             input_ItemInit( VLC_OBJECT(vlm), &p_instance->item );
1209             p_instance->p_input = NULL;
1210
1211             TAB_INIT( p_instance->item.i_options, p_instance->item.ppsz_options );
1212
1213             if( media->psz_output != NULL || media->psz_vod_output != NULL )
1214             {
1215                 asprintf( &psz_dup, "sout=%s%s%s",
1216                           media->psz_output ? media->psz_output : "",
1217                           (media->psz_output && media->psz_vod_output) ? ":" : media->psz_vod_output ? "#" : "",
1218                           media->psz_vod_output ? media->psz_vod_output : "" );
1219                 TAB_APPEND( p_instance->item.i_options, p_instance->item.ppsz_options, psz_dup );
1220             }
1221
1222             for( i = 0; i < media->i_option; i++ )
1223             {
1224                 psz_dup = strdup( media->option[i] );
1225                 TAB_APPEND( p_instance->item.i_options, p_instance->item.ppsz_options, psz_dup );
1226             }
1227             psz_dup = strdup( "no-sout-keep" );
1228             TAB_APPEND( p_instance->item.i_options, p_instance->item.ppsz_options, psz_dup );
1229
1230             TAB_APPEND( media->i_instance, media->instance, p_instance );
1231         }
1232
1233         if( ( psz_args && sscanf(psz_args, "%d", &i) == 1 ) && i > 0 && i-1 < media->i_input )
1234         {
1235             p_instance->i_index = i-1;
1236         }
1237
1238         if( p_instance->item.psz_uri ) free( p_instance->item.psz_uri );
1239         p_instance->item.psz_uri =
1240             strdup( media->input[p_instance->i_index] );
1241
1242         if( p_instance->p_input )
1243         {
1244             input_StopThread( p_instance->p_input );
1245             input_DestroyThread( p_instance->p_input );
1246         }
1247
1248         asprintf( &psz_header, _("Media: %s"), media->psz_name );
1249         p_instance->p_input = input_CreateThread2( vlm, &p_instance->item,
1250                                                    psz_header );
1251         if( !p_instance->p_input )
1252         {
1253             TAB_REMOVE( media->i_instance, media->instance, p_instance );
1254             input_ItemClean( &p_instance->item );
1255             if( p_instance->psz_name ) free( p_instance->psz_name );
1256         }
1257         free( psz_header );
1258
1259         return VLC_SUCCESS;
1260     }
1261
1262     if( !p_instance ) return VLC_EGENERIC;
1263
1264     if( !strcmp( psz_command, "seek" ) )
1265     {
1266         if( psz_args )
1267         {
1268             vlc_bool_t i_rel;
1269             float f_value = i18n_atof( psz_args );
1270             if( psz_args[0] == '+' || psz_args[0] == '-' )
1271                 i_rel = VLC_TRUE;
1272             else
1273                 i_rel = VLC_FALSE;
1274             if( strstr( psz_args, "ms" ) )
1275             {
1276                 /* milliseconds */
1277                 int64_t i_msec =  1000 * (int64_t)atoi( psz_args );
1278                 if( i_rel )
1279                 {
1280                     var_SetTime( p_instance->p_input, "time-offset",
1281                                  i_msec );
1282                 }
1283                 else if( i_msec >= 0
1284                       && i_msec < var_GetTime( p_instance->p_input, "length" ) )
1285                 {
1286                     var_SetTime( p_instance->p_input, "time",
1287                                  i_msec );
1288                 }
1289             }
1290             else if( strchr( psz_args, 's' ) )
1291             {
1292                 /* seconds */
1293                 int64_t i_sec = 1000000 * (int64_t)atoi( psz_args );
1294                 if( i_rel )
1295                 {
1296                     var_SetTime( p_instance->p_input, "time-offset",
1297                                  i_sec );
1298                 }
1299                 else if( i_sec >= 0
1300                       && i_sec < var_GetTime( p_instance->p_input, "length" ) )
1301                 {
1302                     var_SetTime( p_instance->p_input, "time",
1303                                  i_sec );
1304                 }
1305             }
1306             else
1307             {
1308                 /* percentage */
1309                 f_value /= 100.;
1310                 if( i_rel )
1311                 {
1312                     float f_orig = var_GetFloat( p_instance->p_input,
1313                                                  "position" );
1314                     f_value += f_orig;
1315                 }
1316                 if( f_value >= 0.0 && f_value <= 1.0 )
1317                 {
1318                     var_SetFloat( p_instance->p_input, "position",
1319                                   f_value );
1320                     return VLC_SUCCESS;
1321                 }
1322             }
1323         }
1324     }
1325     else if( !strcmp( psz_command, "rewind" ) )
1326     {
1327         float f_pos;
1328         float f_scale;
1329
1330         if( psz_args )
1331         {
1332             f_scale = i18n_atof( psz_args );
1333             f_pos = var_GetFloat( p_instance->p_input, "position" );
1334             f_pos -= (f_scale / 1000.0);
1335             if( f_pos < 0. )
1336                 f_pos = 0.;
1337             var_SetFloat( p_instance->p_input, "position", f_pos );
1338             return VLC_SUCCESS;
1339         }
1340     }
1341     else if( !strcmp( psz_command, "forward" ) )
1342     {
1343         float f_pos;
1344         float f_scale;
1345
1346         if( psz_args )
1347         {
1348             f_scale = i18n_atof( psz_args );
1349             f_pos = var_GetFloat( p_instance->p_input, "position" );
1350             f_pos += (f_scale / 1000.0);
1351             if( f_pos > 1.0 )
1352                 f_pos = 1.0;
1353             var_SetFloat( p_instance->p_input, "position", f_pos );
1354             return VLC_SUCCESS;
1355         }
1356     }
1357     else if( !strcmp( psz_command, "stop" ) )
1358     {
1359         TAB_REMOVE( media->i_instance, media->instance, p_instance );
1360
1361         if( p_instance->p_input )
1362         {
1363             input_StopThread( p_instance->p_input );
1364             input_DestroyThread( p_instance->p_input );
1365         }
1366
1367         input_ItemClean( &p_instance->item );
1368         if( p_instance->psz_name ) free( p_instance->psz_name );
1369         free( p_instance );
1370
1371         return VLC_SUCCESS;
1372     }
1373     else if( !strcmp( psz_command, "pause" ) )
1374     {
1375         int i_state;
1376
1377         if( !p_instance->p_input ) return VLC_SUCCESS;
1378
1379         i_state = var_GetInteger( p_instance->p_input, "state" );
1380
1381         if( i_state == PAUSE_S ) i_state = PLAYING_S;
1382         else i_state = PAUSE_S;
1383         var_SetInteger( p_instance->p_input, "state", i_state );
1384
1385         return VLC_SUCCESS;
1386     }
1387
1388     return VLC_EGENERIC;
1389 }
1390
1391 /*****************************************************************************
1392  * Schedule handling
1393  *****************************************************************************/
1394 static int64_t vlm_Date(void)
1395 {
1396 #ifdef WIN32
1397     struct timeb tm;
1398     ftime( &tm );
1399     return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000;
1400 #else
1401     return mdate();
1402 #endif
1403 }
1404
1405 vlm_schedule_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name )
1406 {
1407     vlm_schedule_t *p_sched = malloc( sizeof( vlm_schedule_t ) );
1408
1409     if( !p_sched )
1410     {
1411         return NULL;
1412     }
1413
1414     if( !psz_name )
1415     {
1416         return NULL;
1417     }
1418
1419     p_sched->psz_name = strdup( psz_name );
1420     p_sched->b_enabled = VLC_FALSE;
1421     p_sched->i_command = 0;
1422     p_sched->command = NULL;
1423     p_sched->i_date = 0;
1424     p_sched->i_period = 0;
1425     p_sched->i_repeat = -1;
1426
1427     TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched );
1428
1429     return p_sched;
1430 }
1431
1432 /* for now, simple delete. After, del with options (last arg) */
1433 void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_t *sched,
1434                          const char *psz_name )
1435 {
1436     if( sched == NULL ) return;
1437
1438     TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched );
1439
1440     if( vlm->i_schedule == 0 && vlm->schedule ) free( vlm->schedule );
1441     free( sched->psz_name );
1442     while( sched->i_command )
1443     {
1444         char *psz_cmd = sched->command[0];
1445         TAB_REMOVE( sched->i_command, sched->command, psz_cmd );
1446         free( psz_cmd );
1447     }
1448     free( sched );
1449 }
1450
1451 static vlm_schedule_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name )
1452 {
1453     int i;
1454
1455     for( i = 0; i < vlm->i_schedule; i++ )
1456     {
1457         if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 )
1458         {
1459             return vlm->schedule[i];
1460         }
1461     }
1462
1463     return NULL;
1464 }
1465
1466 /* Ok, setup schedule command will be able to support only one (argument value) at a time  */
1467 int vlm_ScheduleSetup( vlm_schedule_t *schedule, const char *psz_cmd,
1468                        const char *psz_value )
1469 {
1470     if( !strcmp( psz_cmd, "enabled" ) )
1471     {
1472         schedule->b_enabled = VLC_TRUE;
1473     }
1474     else if( !strcmp( psz_cmd, "disabled" ) )
1475     {
1476         schedule->b_enabled = VLC_FALSE;
1477     }
1478 #if !defined( UNDER_CE )
1479     else if( !strcmp( psz_cmd, "date" ) )
1480     {
1481         struct tm time;
1482         const char *p;
1483         time_t date;
1484
1485         time.tm_sec = 0;         /* seconds */
1486         time.tm_min = 0;         /* minutes */
1487         time.tm_hour = 0;        /* hours */
1488         time.tm_mday = 0;        /* day of the month */
1489         time.tm_mon = 0;         /* month */
1490         time.tm_year = 0;        /* year */
1491         time.tm_wday = 0;        /* day of the week */
1492         time.tm_yday = 0;        /* day in the year */
1493         time.tm_isdst = -1;       /* daylight saving time */
1494
1495         /* date should be year/month/day-hour:minutes:seconds */
1496         p = strchr( psz_value, '-' );
1497
1498         if( !strcmp( psz_value, "now" ) )
1499         {
1500             schedule->i_date = 0;
1501         }
1502         else if( (p == NULL) && sscanf( psz_value, "%d:%d:%d", &time.tm_hour,
1503                                         &time.tm_min, &time.tm_sec ) != 3 )
1504                                         /* it must be a hour:minutes:seconds */
1505         {
1506             return 1;
1507         }
1508         else
1509         {
1510             unsigned i,j,k;
1511
1512             switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) )
1513             {
1514                 case 1:
1515                     time.tm_sec = i;
1516                     break;
1517                 case 2:
1518                     time.tm_min = i;
1519                     time.tm_sec = j;
1520                     break;
1521                 case 3:
1522                     time.tm_hour = i;
1523                     time.tm_min = j;
1524                     time.tm_sec = k;
1525                     break;
1526                 default:
1527                     return 1;
1528             }
1529
1530             switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) )
1531             {
1532                 case 1:
1533                     time.tm_mday = i;
1534                     break;
1535                 case 2:
1536                     time.tm_mon = i - 1;
1537                     time.tm_mday = j;
1538                     break;
1539                 case 3:
1540                     time.tm_year = i - 1900;
1541                     time.tm_mon = j - 1;
1542                     time.tm_mday = k;
1543                     break;
1544                 default:
1545                     return 1;
1546             }
1547
1548             date = mktime( &time );
1549             schedule->i_date = ((mtime_t) date) * 1000000;
1550         }
1551     }
1552     else if( !strcmp( psz_cmd, "period" ) )
1553     {
1554         struct tm time;
1555         const char *p;
1556         const char *psz_time = NULL, *psz_date = NULL;
1557         time_t date;
1558         unsigned i,j,k;
1559
1560         /* First, if date or period are modified, repeat should be equal to -1 */
1561         schedule->i_repeat = -1;
1562
1563         time.tm_sec = 0;         /* seconds */
1564         time.tm_min = 0;         /* minutes */
1565         time.tm_hour = 0;        /* hours */
1566         time.tm_mday = 0;        /* day of the month */
1567         time.tm_mon = 0;         /* month */
1568         time.tm_year = 0;        /* year */
1569         time.tm_wday = 0;        /* day of the week */
1570         time.tm_yday = 0;        /* day in the year */
1571         time.tm_isdst = -1;       /* daylight saving time */
1572
1573         /* date should be year/month/day-hour:minutes:seconds */
1574         p = strchr( psz_value, '-' );
1575         if( p )
1576         {
1577             psz_date = psz_value;
1578             psz_time = p + 1;
1579         }
1580         else
1581         {
1582             psz_time = psz_value;
1583         }
1584
1585         switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) )
1586         {
1587             case 1:
1588                 time.tm_sec = i;
1589                 break;
1590             case 2:
1591                 time.tm_min = i;
1592                 time.tm_sec = j;
1593                 break;
1594             case 3:
1595                 time.tm_hour = i;
1596                 time.tm_min = j;
1597                 time.tm_sec = k;
1598                 break;
1599             default:
1600                 return 1;
1601         }
1602         if( psz_date )
1603         {
1604             switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) )
1605             {
1606                 case 1:
1607                     time.tm_mday = i;
1608                     break;
1609                 case 2:
1610                     time.tm_mon = i;
1611                     time.tm_mday = j;
1612                     break;
1613                 case 3:
1614                     time.tm_year = i;
1615                     time.tm_mon = j;
1616                     time.tm_mday = k;
1617                     break;
1618                 default:
1619                     return 1;
1620             }
1621         }
1622
1623         /* ok, that's stupid... who is going to schedule streams every 42 years ? */
1624         date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ;
1625         schedule->i_period = ((mtime_t) date) * 1000000;
1626     }
1627 #endif /* UNDER_CE */
1628     else if( !strcmp( psz_cmd, "repeat" ) )
1629     {
1630         int i;
1631
1632         if( sscanf( psz_value, "%d", &i ) == 1 )
1633         {
1634             schedule->i_repeat = i;
1635         }
1636         else
1637         {
1638             return 1;
1639         }
1640     }
1641     else if( !strcmp( psz_cmd, "append" ) )
1642     {
1643         char *command = strdup( psz_value );
1644
1645         TAB_APPEND( schedule->i_command, schedule->command, command );
1646     }
1647     else
1648     {
1649         return 1;
1650     }
1651     return 0;
1652 }
1653
1654 /*****************************************************************************
1655  * Message handling functions
1656  *****************************************************************************/
1657 vlm_message_t *vlm_MessageNew( const char *psz_name,
1658                                const char *psz_format, ... )
1659 {
1660     vlm_message_t *p_message;
1661     va_list args;
1662
1663     if( !psz_name ) return NULL;
1664
1665     p_message = malloc( sizeof(vlm_message_t) );
1666     if( !p_message)
1667     {
1668         return NULL;
1669     }
1670
1671     p_message->psz_value = 0;
1672
1673     if( psz_format )
1674     {
1675         va_start( args, psz_format );
1676         if( vasprintf( &p_message->psz_value, psz_format, args ) == -1 )
1677         {
1678             va_end( args );
1679             free( p_message );
1680             return NULL;
1681         }
1682         va_end( args );
1683     }
1684
1685     p_message->psz_name = strdup( psz_name );
1686     p_message->i_child = 0;
1687     p_message->child = NULL;
1688
1689     return p_message;
1690 }
1691
1692 void vlm_MessageDelete( vlm_message_t *p_message )
1693 {
1694     if( p_message->psz_name ) free( p_message->psz_name );
1695     if( p_message->psz_value ) free( p_message->psz_value );
1696     while( p_message->i_child-- )
1697         vlm_MessageDelete( p_message->child[p_message->i_child] );
1698     if( p_message->child ) free( p_message->child );
1699     free( p_message );
1700 }
1701
1702 /* Add a child */
1703 vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message,
1704                                vlm_message_t *p_child )
1705 {
1706     if( p_message == NULL ) return NULL;
1707
1708     if( p_child )
1709     {
1710         TAB_APPEND( p_message->i_child, p_message->child, p_child );
1711     }
1712
1713     return p_child;
1714 }
1715
1716 /*****************************************************************************
1717  * Misc utility functions
1718  *****************************************************************************/
1719 static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_t *media,
1720                                 vlm_schedule_t *schedule,
1721                                 const char *psz_filter )
1722 {
1723     if( media != NULL )
1724     {
1725         int i;
1726         vlm_message_t *msg;
1727         vlm_message_t *msg_media;
1728         vlm_message_t *msg_child;
1729
1730         msg = vlm_MessageNew( "show", NULL );
1731         msg_media = vlm_MessageAdd( msg, vlm_MessageNew( media->psz_name, 0 ));
1732
1733         vlm_MessageAdd( msg_media,
1734                         vlm_MessageNew( "type", media->i_type == VOD_TYPE ?
1735                                         "vod" : "broadcast" ) );
1736         vlm_MessageAdd( msg_media,
1737                         vlm_MessageNew( "enabled", media->b_enabled ?
1738                                         "yes" : "no" ) );
1739
1740         vlm_MessageAdd( msg_media,
1741                         vlm_MessageNew( "loop", media->b_loop ?
1742                                         "yes" : "no" ) );
1743
1744         if( media->i_type == VOD_TYPE && media->psz_mux )
1745             vlm_MessageAdd( msg_media,
1746                             vlm_MessageNew( "mux", media->psz_mux ) );
1747
1748         msg_child = vlm_MessageAdd( msg_media,
1749                                     vlm_MessageNew( "inputs", NULL ) );
1750
1751         for( i = 0; i < media->i_input; i++ )
1752         {
1753             vlm_MessageAdd( msg_child,
1754                             vlm_MessageNew( media->input[i], NULL ) );
1755         }
1756
1757         vlm_MessageAdd( msg_media,
1758                         vlm_MessageNew( "output", media->psz_output ?
1759                                         media->psz_output : "" ) );
1760
1761         msg_child = vlm_MessageAdd( msg_media, vlm_MessageNew( "options", 0 ));
1762
1763         for( i = 0; i < media->i_option; i++ )
1764         {
1765             vlm_MessageAdd( msg_child, vlm_MessageNew( media->option[i], 0 ) );
1766         }
1767
1768         msg_child = vlm_MessageAdd( msg_media,
1769                                     vlm_MessageNew( "instances", NULL ) );
1770
1771         for( i = 0; i < media->i_instance; i++ )
1772         {
1773             vlm_media_instance_t *p_instance = media->instance[i];
1774             vlc_value_t val;
1775             vlm_message_t *msg_instance;
1776             char *psz_tmp;
1777
1778             if( !p_instance->p_input ) val.i_int = END_S;
1779             else var_Get( p_instance->p_input, "state", &val );
1780
1781             msg_instance = vlm_MessageNew( "instance" , NULL );
1782             vlm_MessageAdd( msg_instance, vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) );
1783             vlm_MessageAdd( msg_instance, vlm_MessageNew( "state",
1784                                 val.i_int == PLAYING_S ? "playing" :
1785                                 val.i_int == PAUSE_S ? "paused" :
1786                                 "stopped" ) );
1787 #define APPEND_INPUT_INFO( a, format, type ) \
1788             asprintf( &psz_tmp, format, \
1789                       var_Get ## type( p_instance->p_input, a ) ); \
1790             vlm_MessageAdd( msg_instance, vlm_MessageNew( a, psz_tmp ) ); \
1791             free( psz_tmp );
1792             APPEND_INPUT_INFO( "position", "%f", Float );
1793             APPEND_INPUT_INFO( "time", I64Fi, Time );
1794             APPEND_INPUT_INFO( "length", I64Fi, Time );
1795             APPEND_INPUT_INFO( "rate", "%d", Integer );
1796             APPEND_INPUT_INFO( "title", "%d", Integer );
1797             APPEND_INPUT_INFO( "chapter", "%d", Integer );
1798             APPEND_INPUT_INFO( "seekable", "%d", Bool );
1799 #undef APPEND_INPUT_INFO
1800             asprintf( &psz_tmp, "%d", p_instance->i_index + 1 );
1801             vlm_MessageAdd( msg_instance, vlm_MessageNew( "playlistindex", psz_tmp ) );
1802             free( psz_tmp );
1803             vlm_MessageAdd( msg_child, msg_instance );
1804         }
1805
1806         return msg;
1807
1808     }
1809
1810     else if( schedule != NULL )
1811     {
1812         int i;
1813         vlm_message_t *msg;
1814         vlm_message_t *msg_schedule;
1815         vlm_message_t *msg_child;
1816         char buffer[100];
1817
1818         msg = vlm_MessageNew( "show", NULL );
1819         msg_schedule =
1820             vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, 0 ) );
1821
1822         vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") );
1823
1824         vlm_MessageAdd( msg_schedule,
1825                         vlm_MessageNew( "enabled", schedule->b_enabled ?
1826                                         "yes" : "no" ) );
1827
1828 #if !defined( UNDER_CE )
1829         if( schedule->i_date != 0 )
1830         {
1831             struct tm date;
1832             time_t i_time = (time_t)( schedule->i_date / 1000000 );
1833             char *psz_date;
1834
1835 #ifdef HAVE_LOCALTIME_R
1836             localtime_r( &i_time, &date);
1837 #else
1838             struct tm *p_date = localtime( &i_time );
1839             date = *p_date;
1840 #endif
1841
1842             asprintf( &psz_date, "%d/%d/%d-%d:%d:%d",
1843                       date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1844                       date.tm_hour, date.tm_min, date.tm_sec );
1845
1846             vlm_MessageAdd( msg_schedule,
1847                             vlm_MessageNew( "date", psz_date ) );
1848             free( psz_date );
1849         }
1850         else
1851         {
1852             vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") );
1853         }
1854
1855         if( schedule->i_period != 0 )
1856         {
1857             time_t i_time = (time_t) ( schedule->i_period / 1000000 );
1858             struct tm date;
1859
1860             date.tm_sec = (int)( i_time % 60 );
1861             i_time = i_time / 60;
1862             date.tm_min = (int)( i_time % 60 );
1863             i_time = i_time / 60;
1864             date.tm_hour = (int)( i_time % 24 );
1865             i_time = i_time / 24;
1866             date.tm_mday = (int)( i_time % 30 );
1867             i_time = i_time / 30;
1868             /* okay, okay, months are not always 30 days long */
1869             date.tm_mon = (int)( i_time % 12 );
1870             i_time = i_time / 12;
1871             date.tm_year = (int)i_time;
1872
1873             sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon,
1874                      date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
1875
1876             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) );
1877         }
1878         else
1879         {
1880             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") );
1881         }
1882 #endif /* UNDER_CE */
1883
1884         sprintf( buffer, "%d", schedule->i_repeat );
1885         vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) );
1886
1887         msg_child =
1888             vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", 0) );
1889
1890         for( i = 0; i < schedule->i_command; i++ )
1891         {
1892            vlm_MessageAdd( msg_child,
1893                            vlm_MessageNew( schedule->command[i], NULL ) );
1894         }
1895
1896         return msg;
1897
1898     }
1899
1900     else if( psz_filter && !strcmp( psz_filter, "media" ) )
1901     {
1902         int i, j;
1903         vlm_message_t *msg;
1904         vlm_message_t *msg_child;
1905         int i_vod = 0, i_broadcast = 0;
1906         char *psz_count;
1907
1908         for( i = 0; i < vlm->i_media; i++ )
1909         {
1910             if( vlm->media[i]->i_type == VOD_TYPE )
1911                 i_vod ++;
1912             else
1913                 i_broadcast ++;
1914         }
1915
1916         asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast, i_vod);
1917
1918         msg = vlm_MessageNew( "show", NULL );
1919         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "media", psz_count ) );
1920         free( psz_count );
1921
1922         for( i = 0; i < vlm->i_media; i++ )
1923         {
1924             vlm_media_t *m = vlm->media[i];
1925             vlm_message_t *msg_media, *msg_instance;
1926
1927             msg_media = vlm_MessageAdd( msg_child,
1928                                         vlm_MessageNew( m->psz_name, 0 ) );
1929
1930             vlm_MessageAdd( msg_media,
1931                             vlm_MessageNew( "type", m->i_type == VOD_TYPE ?
1932                                             "vod" : "broadcast" ) );
1933
1934             vlm_MessageAdd( msg_media,
1935                             vlm_MessageNew( "enabled", m->b_enabled ?
1936                                             "yes" : "no" ) );
1937
1938             if( m->i_type == VOD_TYPE && m->psz_mux )
1939                 vlm_MessageAdd( msg_media,
1940                                 vlm_MessageNew( "mux", m->psz_mux ) );
1941
1942             msg_instance = vlm_MessageAdd( msg_media,
1943                                            vlm_MessageNew( "instances", 0 ) );
1944
1945             for( j = 0; j < m->i_instance; j++ )
1946             {
1947                 vlm_media_instance_t *p_instance = m->instance[j];
1948                 vlc_value_t val;
1949
1950                 if( !p_instance->p_input ) val.i_int = END_S;
1951                 else var_Get( p_instance->p_input, "state", &val );
1952
1953                 vlm_MessageAdd( msg_instance,
1954                     vlm_MessageNew( p_instance->psz_name ?
1955                                     p_instance->psz_name : "default",
1956                                     val.i_int == PLAYING_S ? "playing" :
1957                                     val.i_int == PAUSE_S ? "paused" :
1958                                     "stopped" ) );
1959             }
1960         }
1961
1962         return msg;
1963     }
1964
1965     else if( psz_filter && !strcmp( psz_filter, "schedule" ) )
1966     {
1967         int i;
1968         vlm_message_t *msg;
1969         vlm_message_t *msg_child;
1970
1971         msg = vlm_MessageNew( "show", NULL );
1972         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", NULL ) );
1973
1974         for( i = 0; i < vlm->i_schedule; i++ )
1975         {
1976             vlm_schedule_t *s = vlm->schedule[i];
1977             vlm_message_t *msg_schedule;
1978             mtime_t i_time, i_next_date;
1979
1980             msg_schedule = vlm_MessageAdd( msg_child,
1981                                            vlm_MessageNew( s->psz_name, 0 ) );
1982             vlm_MessageAdd( msg_schedule,
1983                             vlm_MessageNew( "enabled", s->b_enabled ?
1984                                             "yes" : "no" ) );
1985
1986             /* calculate next date */
1987             i_time = vlm_Date();
1988             i_next_date = s->i_date;
1989
1990             if( s->i_period != 0 )
1991             {
1992                 int j = 0;
1993                 while( s->i_date + j * s->i_period <= i_time &&
1994                        s->i_repeat > j )
1995                 {
1996                     j++;
1997                 }
1998
1999                 i_next_date = s->i_date + j * s->i_period;
2000             }
2001
2002             if( i_next_date > i_time )
2003             {
2004                 time_t i_date = (time_t) (i_next_date / 1000000) ;
2005
2006 #if !defined( UNDER_CE )
2007 #ifdef HAVE_CTIME_R
2008                 char psz_date[500];
2009                 ctime_r( &i_date, psz_date );
2010 #else
2011                 char *psz_date = ctime( &i_date );
2012 #endif
2013
2014                 vlm_MessageAdd( msg_schedule,
2015                                 vlm_MessageNew( "next launch", psz_date ) );
2016 #endif
2017             }
2018         }
2019
2020         return msg;
2021     }
2022
2023     else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) )
2024     {
2025         vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" );
2026         vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" );
2027
2028         vlm_MessageAdd( show1, show2->child[0] );
2029
2030         /* We must destroy the parent node "show" of show2
2031          * and not the children */
2032         free( show2->psz_name );
2033         free( show2 );
2034
2035         return show1;
2036     }
2037
2038     else
2039     {
2040         return vlm_MessageNew( "show", NULL );
2041     }
2042 }
2043
2044 static vlm_message_t *vlm_Help( vlm_t *vlm, char *psz_filter )
2045 {
2046     vlm_message_t *message, *message_child;
2047
2048 #define MessageAdd( a ) \
2049         vlm_MessageAdd( message, vlm_MessageNew( a, NULL ) );
2050 #define MessageAddChild( a ) \
2051         vlm_MessageAdd( message_child, vlm_MessageNew( a, NULL ) );
2052
2053     if( psz_filter == NULL )
2054     {
2055         message = vlm_MessageNew( "help", NULL );
2056
2057         message_child = MessageAdd( "Commands Syntax:" );
2058         MessageAddChild( "new (name) vod|broadcast|schedule [properties]" );
2059         MessageAddChild( "setup (name) (properties)" );
2060         MessageAddChild( "show [(name)|media|schedule]" );
2061         MessageAddChild( "del (name)|all|media|schedule" );
2062         MessageAddChild( "control (name) [instance_name] (command)" );
2063         MessageAddChild( "save (config_file)" );
2064         MessageAddChild( "export" );
2065         MessageAddChild( "load (config_file)" );
2066
2067         message_child = MessageAdd( "Media Proprieties Syntax:" );
2068         MessageAddChild( "input (input_name)" );
2069         MessageAddChild( "inputdel (input_name)|all" );
2070         MessageAddChild( "inputdeln input_number" );
2071         MessageAddChild( "output (output_name)" );
2072         MessageAddChild( "option (option_name)[=value]" );
2073         MessageAddChild( "enabled|disabled" );
2074         MessageAddChild( "loop|unloop (broadcast only)" );
2075         MessageAddChild( "mux (mux_name)" );
2076
2077         message_child = MessageAdd( "Schedule Proprieties Syntax:" );
2078         MessageAddChild( "enabled|disabled" );
2079         MessageAddChild( "append (command_until_rest_of_the_line)" );
2080         MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):"
2081                          "(seconds)|now" );
2082         MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/"
2083                          "(days)-(hours):(minutes):(seconds)" );
2084         MessageAddChild( "repeat (number_of_repetitions)" );
2085
2086         message_child = MessageAdd( "Control Commands Syntax:" );
2087         MessageAddChild( "play [input_number]" );
2088         MessageAddChild( "pause" );
2089         MessageAddChild( "stop" );
2090         MessageAddChild( "seek [+-](percentage) | [+-](seconds)s | [+-](miliseconds)ms" );
2091
2092         return message;
2093     }
2094
2095     return vlm_MessageNew( "help", NULL );
2096 }
2097
2098 /*****************************************************************************
2099  * Config handling functions
2100  *****************************************************************************/
2101 static int Load( vlm_t *vlm, char *file )
2102 {
2103     char *pf = file;
2104     int  i_line = 1;
2105
2106     while( *pf != '\0' )
2107     {
2108         vlm_message_t *message = NULL;
2109         int i_end = 0;
2110
2111         while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' )
2112         {
2113             i_end++;
2114         }
2115
2116         if( pf[i_end] == '\r' || pf[i_end] == '\n' )
2117         {
2118             pf[i_end] = '\0';
2119             i_end++;
2120             if( pf[i_end] == '\n' ) i_end++;
2121         }
2122
2123         if( *pf && ExecuteCommand( vlm, pf, &message ) )
2124         {
2125             if( message )
2126             {
2127                 if( message->psz_value )
2128                     msg_Err( vlm, "Load error on line %d: %s: %s",
2129                              i_line, message->psz_name, message->psz_value );
2130                 vlm_MessageDelete( message );
2131             }
2132             return 1;
2133         }
2134         if( message ) vlm_MessageDelete( message );
2135
2136         pf += i_end;
2137         i_line++;
2138     }
2139
2140     return 0;
2141 }
2142
2143 static char *Save( vlm_t *vlm )
2144 {
2145     char *save = NULL;
2146     char psz_header[] = "\n"
2147                         "# VLC media player VLM command batch\n"
2148                         "# http://www.videolan.org/vlc/\n\n" ;
2149     char *p;
2150     int i,j;
2151     int i_length = strlen( psz_header );
2152
2153     for( i = 0; i < vlm->i_media; i++ )
2154     {
2155         vlm_media_t *media = vlm->media[i];
2156
2157         if( media->i_type == VOD_TYPE )
2158         {
2159             i_length += strlen( "new  vod " ) + strlen(media->psz_name);
2160         }
2161         else
2162         {
2163             i_length += strlen( "new  broadcast " ) + strlen(media->psz_name);
2164         }
2165
2166         if( media->b_enabled == VLC_TRUE )
2167         {
2168             i_length += strlen( "enabled" );
2169         }
2170         else
2171         {
2172             i_length += strlen( "disabled" );
2173         }
2174
2175         if( media->b_loop == VLC_TRUE )
2176         {
2177             i_length += strlen( " loop\n" );
2178         }
2179         else
2180         {
2181             i_length += strlen( "\n" );
2182         }
2183
2184         for( j = 0; j < media->i_input; j++ )
2185         {
2186             i_length += strlen( "setup  input \"\"\n" ) +
2187                 strlen( media->psz_name ) + strlen( media->input[j] );
2188         }
2189
2190         if( media->psz_output != NULL )
2191         {
2192             i_length += strlen(media->psz_name) + strlen(media->psz_output) +
2193                 strlen( "setup  output \n" );
2194         }
2195
2196         for( j=0 ; j < media->i_option ; j++ )
2197         {
2198             i_length += strlen(media->psz_name) + strlen(media->option[j]) +
2199                 strlen("setup  option \n");
2200         }
2201     }
2202
2203     for( i = 0; i < vlm->i_schedule; i++ )
2204     {
2205         vlm_schedule_t *schedule = vlm->schedule[i];
2206
2207         i_length += strlen( "new  schedule " ) + strlen( schedule->psz_name );
2208
2209         if( schedule->b_enabled == VLC_TRUE )
2210         {
2211             i_length += strlen( "date //-:: enabled\n" ) + 14;
2212         }
2213         else
2214         {
2215             i_length += strlen( "date //-:: disabled\n" ) + 14;
2216         }
2217
2218
2219         if( schedule->i_period != 0 )
2220         {
2221             i_length += strlen( "setup  " ) + strlen( schedule->psz_name ) +
2222                 strlen( "period //-::\n" ) + 14;
2223         }
2224
2225         if( schedule->i_repeat >= 0 )
2226         {
2227             char buffer[12];
2228
2229             sprintf( buffer, "%d", schedule->i_repeat );
2230             i_length += strlen( "setup  repeat \n" ) +
2231                 strlen( schedule->psz_name ) + strlen( buffer );
2232         }
2233         else
2234         {
2235             i_length++;
2236         }
2237
2238         for( j = 0; j < schedule->i_command; j++ )
2239         {
2240             i_length += strlen( "setup  append \n" ) +
2241                 strlen( schedule->psz_name ) + strlen( schedule->command[j] );
2242         }
2243
2244     }
2245
2246     /* Don't forget the '\0' */
2247     i_length++;
2248     /* now we have the length of save */
2249
2250     p = save = malloc( i_length );
2251     *save = '\0';
2252
2253     p += sprintf( p, "%s", psz_header );
2254
2255     /* finally we can write in it */
2256     for( i = 0; i < vlm->i_media; i++ )
2257     {
2258         vlm_media_t *media = vlm->media[i];
2259
2260         if( media->i_type == VOD_TYPE )
2261         {
2262             p += sprintf( p, "new %s vod ", media->psz_name);
2263         }
2264         else
2265         {
2266             p += sprintf( p, "new %s broadcast ", media->psz_name);
2267         }
2268
2269         if( media->b_enabled == VLC_TRUE )
2270         {
2271             p += sprintf( p, "enabled" );
2272         }
2273         else
2274         {
2275             p += sprintf( p, "disabled" );
2276         }
2277
2278         if( media->b_loop == VLC_TRUE )
2279         {
2280             p += sprintf( p, " loop\n" );
2281         }
2282         else
2283         {
2284             p += sprintf( p, "\n" );
2285         }
2286
2287         for( j = 0; j < media->i_input; j++ )
2288         {
2289             p += sprintf( p, "setup %s input \"%s\"\n", media->psz_name,
2290                           media->input[j] );
2291         }
2292
2293         if( media->psz_output != NULL )
2294         {
2295             p += sprintf( p, "setup %s output %s\n", media->psz_name,
2296                           media->psz_output );
2297         }
2298
2299         for( j = 0; j < media->i_option; j++ )
2300         {
2301             p += sprintf( p, "setup %s option %s\n", media->psz_name,
2302                           media->option[j] );
2303         }
2304     }
2305
2306     /* and now, the schedule scripts */
2307 #if !defined( UNDER_CE )
2308     for( i = 0; i < vlm->i_schedule; i++ )
2309     {
2310         vlm_schedule_t *schedule = vlm->schedule[i];
2311         struct tm date;
2312         time_t i_time = (time_t) ( schedule->i_date / 1000000 );
2313
2314 #ifdef HAVE_LOCALTIME_R
2315         localtime_r( &i_time, &date);
2316 #else
2317         struct tm *p_date = localtime( &i_time );
2318         date = *p_date;
2319 #endif
2320
2321         p += sprintf( p, "new %s schedule ", schedule->psz_name);
2322
2323         if( schedule->b_enabled == VLC_TRUE )
2324         {
2325             p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n",
2326                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
2327                           date.tm_hour, date.tm_min, date.tm_sec );
2328         }
2329         else
2330         {
2331             p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n",
2332                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
2333                           date.tm_hour, date.tm_min, date.tm_sec);
2334         }
2335
2336         if( schedule->i_period != 0 )
2337         {
2338             p += sprintf( p, "setup %s ", schedule->psz_name );
2339
2340             i_time = (time_t) ( schedule->i_period / 1000000 );
2341
2342             date.tm_sec = (int)( i_time % 60 );
2343             i_time = i_time / 60;
2344             date.tm_min = (int)( i_time % 60 );
2345             i_time = i_time / 60;
2346             date.tm_hour = (int)( i_time % 24 );
2347             i_time = i_time / 24;
2348             date.tm_mday = (int)( i_time % 30 );
2349             i_time = i_time / 30;
2350             /* okay, okay, months are not always 30 days long */
2351             date.tm_mon = (int)( i_time % 12 );
2352             i_time = i_time / 12;
2353             date.tm_year = (int)i_time;
2354
2355             p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n",
2356                           date.tm_year, date.tm_mon, date.tm_mday,
2357                           date.tm_hour, date.tm_min, date.tm_sec);
2358         }
2359
2360         if( schedule->i_repeat >= 0 )
2361         {
2362             p += sprintf( p, "setup %s repeat %d\n",
2363                           schedule->psz_name, schedule->i_repeat );
2364         }
2365         else
2366         {
2367             p += sprintf( p, "\n" );
2368         }
2369
2370         for( j = 0; j < schedule->i_command; j++ )
2371         {
2372             p += sprintf( p, "setup %s append %s\n",
2373                           schedule->psz_name, schedule->command[j] );
2374         }
2375
2376     }
2377 #endif /* UNDER_CE */
2378
2379     return save;
2380 }
2381
2382 /*****************************************************************************
2383  * Manage:
2384  *****************************************************************************/
2385 int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
2386                          const char *psz_id, int i_query, va_list args )
2387 {
2388     vlm_t *vlm = (vlm_t *)p_private;
2389     int i, i_ret = VLC_EGENERIC;
2390
2391     if( !p_private || !p_vod_media ) return VLC_EGENERIC;
2392
2393     vlc_mutex_lock( &vlm->lock );
2394
2395     /* Find media */
2396     for( i = 0; i < vlm->i_media; i++ )
2397     {
2398         if( p_vod_media == vlm->media[i]->vod_media ) break;
2399     }
2400
2401     if( i == vlm->i_media )
2402     {
2403         vlc_mutex_unlock( &vlm->lock );
2404         return VLC_EGENERIC;
2405     }
2406
2407     switch( i_query )
2408     {
2409     case VOD_MEDIA_PLAY:
2410         vlm->media[i]->psz_vod_output = (char *)va_arg( args, char * );
2411         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "play", 0 );
2412         vlm->media[i]->psz_vod_output = 0;
2413         break;
2414
2415     case VOD_MEDIA_PAUSE:
2416         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "pause", 0 );
2417         break;
2418
2419     case VOD_MEDIA_STOP:
2420         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "stop", 0 );
2421         break;
2422
2423     case VOD_MEDIA_SEEK:
2424     {
2425         double f_pos = (double)va_arg( args, double );
2426         char psz_pos[50];
2427         lldiv_t div = lldiv( f_pos * 10000000, 10000000 );
2428         sprintf( psz_pos, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2429         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "seek", psz_pos);
2430         break;
2431     }
2432
2433     case VOD_MEDIA_REWIND:
2434     {
2435         double f_scale = (double)va_arg( args, double );
2436         char psz_scale[50];
2437         lldiv_t div = lldiv( f_scale * 10000000, 10000000 );
2438         sprintf( psz_scale, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2439         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "rewind", psz_scale );
2440         break;
2441     }
2442
2443     case VOD_MEDIA_FORWARD:
2444     {
2445         double f_scale = (double)va_arg( args, double );
2446         char psz_scale[50];
2447         lldiv_t div = lldiv( f_scale * 10000000, 10000000 );
2448         sprintf( psz_scale, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2449         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "forward", psz_scale );
2450         break;
2451     }
2452
2453     default:
2454         break;
2455     }
2456
2457     vlc_mutex_unlock( &vlm->lock );
2458
2459     return i_ret;
2460 }
2461
2462
2463 /*****************************************************************************
2464  * Manage:
2465  *****************************************************************************/
2466 static int Manage( vlc_object_t* p_object )
2467 {
2468     vlm_t *vlm = (vlm_t*)p_object;
2469     int i, j;
2470     mtime_t i_lastcheck;
2471     mtime_t i_time;
2472
2473     i_lastcheck = vlm_Date();
2474
2475     msleep( 100000 );
2476
2477     while( !vlm->b_die )
2478     {
2479         char **ppsz_scheduled_commands = NULL;
2480         int    i_scheduled_commands = 0;
2481         vlc_mutex_lock( &vlm->lock );
2482
2483         /* destroy the inputs that wants to die, and launch the next input */
2484         for( i = 0; i < vlm->i_media; i++ )
2485         {
2486             vlm_media_t *p_media = vlm->media[i];
2487
2488             for( j = 0; j < p_media->i_instance; j++ )
2489             {
2490                 vlm_media_instance_t *p_instance = p_media->instance[j];
2491
2492                 if( !p_instance->p_input ||
2493                     ( !p_instance->p_input->b_eof && !p_instance->p_input->b_error ) )
2494                     continue;
2495
2496                 p_instance->i_index++;
2497                 if( p_instance->i_index >= p_media->i_input && p_media->b_loop )
2498                     p_instance->i_index = 0;
2499
2500                 if( p_instance->i_index < p_media->i_input )
2501                 {
2502                     char buffer[12];
2503
2504                     input_StopThread( p_instance->p_input );
2505                     input_DestroyThread( p_instance->p_input );
2506                     p_instance->p_input = NULL;
2507
2508                     /* FIXME, find a way to select the right instance */
2509                     sprintf( buffer, "%d", p_instance->i_index+1 );
2510
2511                     vlm_MediaControl( vlm, p_media, p_instance->psz_name,
2512                                       "play", buffer );
2513                 }
2514                 else
2515                 {
2516                     if( vlm_MediaControl( vlm, p_media, p_instance->psz_name,
2517                                           "stop", 0 ) == VLC_SUCCESS )
2518                     {
2519                          j--; /* the aray is one element smaller now. */
2520                     }
2521                 }
2522             }
2523         }
2524
2525         /* scheduling */
2526         i_time = vlm_Date();
2527
2528         for( i = 0; i < vlm->i_schedule; i++ )
2529         {
2530             mtime_t i_real_date = vlm->schedule[i]->i_date;
2531
2532             if( vlm->schedule[i]->b_enabled == VLC_TRUE )
2533             {
2534                 if( vlm->schedule[i]->i_date == 0 ) // now !
2535                 {
2536                     vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ;
2537                     i_real_date = i_time;
2538                 }
2539                 else if( vlm->schedule[i]->i_period != 0 )
2540                 {
2541                     int j = 0;
2542                     while( vlm->schedule[i]->i_date + j *
2543                            vlm->schedule[i]->i_period <= i_lastcheck &&
2544                            ( vlm->schedule[i]->i_repeat > j ||
2545                              vlm->schedule[i]->i_repeat == -1 ) )
2546                     {
2547                         j++;
2548                     }
2549
2550                     i_real_date = vlm->schedule[i]->i_date + j *
2551                         vlm->schedule[i]->i_period;
2552                 }
2553
2554                 if( i_real_date <= i_time && i_real_date > i_lastcheck )
2555                 {
2556                     for( j = 0; j < vlm->schedule[i]->i_command; j++ )
2557                     {
2558                         TAB_APPEND( i_scheduled_commands,
2559                                     ppsz_scheduled_commands,
2560                                     strdup(vlm->schedule[i]->command[j] ) );
2561                     }
2562                 }
2563             }
2564         }
2565         while( i_scheduled_commands )
2566         {
2567             vlm_message_t *message = NULL;
2568             char *psz_command = ppsz_scheduled_commands[0];
2569             ExecuteCommand( vlm, psz_command,&message );
2570
2571             /* for now, drop the message */
2572             vlm_MessageDelete( message );
2573             TAB_REMOVE( i_scheduled_commands,
2574                         ppsz_scheduled_commands,
2575                         psz_command );
2576             free( psz_command );
2577         }
2578
2579         i_lastcheck = i_time;
2580
2581         vlc_mutex_unlock( &vlm->lock );
2582
2583         msleep( 100000 );
2584     }
2585
2586     return VLC_SUCCESS;
2587 }
2588
2589 #else /* ENABLE_VLM */
2590
2591 /* We just define an empty wrapper */
2592 vlm_t *__vlm_New( vlc_object_t *a )
2593 {
2594     msg_Err( a, "VideoLAN manager support is disabled" );
2595     return 0;
2596 }
2597 void vlm_Delete( vlm_t *a ){}
2598 int vlm_ExecuteCommand( vlm_t *a, char *b, vlm_message_t **c ){ return -1; }
2599 void vlm_MessageDelete( vlm_message_t *a ){}
2600 vlm_media_t *vlm_MediaNew( vlm_t *a, char *b, int c ){ return NULL; }
2601 vlm_media_t *vlm_MediaSearch (vlm_t *a, const char *b ) { return NULL; }
2602 void vlm_MediaDelete( vlm_t *a, vlm_media_t *b, char *c ){}
2603 int vlm_MediaSetup( vlm_t *a, vlm_media_t *b, char *c, char *d ){ return -1; }
2604 int vlm_MediaControl( vlm_t *a, vlm_media_t *b, char *c, char *d, char *e )
2605     { return -1; }
2606 vlm_schedule_t * vlm_ScheduleNew( vlm_t *a, char *b ){ return NULL; }
2607 void  vlm_ScheduleDelete( vlm_t *a, vlm_schedule_t *b, char *c ){}
2608 int vlm_ScheduleSetup( vlm_schedule_t *a, char *b, char *c ){ return -1; }
2609 int vlm_MediaVodControl( void *a, vod_media_t *b, char *c, int d, va_list e )
2610     { return -1; }
2611 int vlm_Save( vlm_t *a, char *b ){ return -1; }
2612 int vlm_Load( vlm_t *a, char *b ){ return -1; }
2613
2614 #endif /* ENABLE_VLM */