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