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