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