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