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