]> git.sesse.net Git - vlc/blob - src/input/vlmshell.c
vlm: don't remove quotes around name in
[vlc] / src / input / vlmshell.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 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34
35 #include <stdio.h>
36 #include <ctype.h>                                              /* tolower() */
37 #include <assert.h>
38
39 #include <vlc_vlm.h>
40
41 #ifdef ENABLE_VLM
42
43 #ifndef WIN32
44 #   include <sys/time.h>                                   /* gettimeofday() */
45 #endif
46
47 #ifdef HAVE_TIME_H
48 #   include <time.h>                                              /* ctime() */
49 #   include <sys/timeb.h>                                         /* ftime() */
50 #endif
51
52 #include <vlc_input.h>
53 #include "input_internal.h"
54 #include <vlc_stream.h>
55 #include "vlm_internal.h"
56 #include <vlc_vod.h>
57 #include <vlc_charset.h>
58 #include <vlc_sout.h>
59 #include "../stream_output/stream_output.h"
60 #include "../libvlc.h"
61
62 /*****************************************************************************
63  * Local prototypes.
64  *****************************************************************************/
65
66 /* ugly kludge to avoid "null format string" warnings,
67  * even if we handle NULL format string in vlm_MessageNew() */
68 static const char *vlm_NULL = NULL;
69
70 /* */
71 static vlm_message_t *vlm_Show( vlm_t *, vlm_media_sys_t *, vlm_schedule_sys_t *, const char * );
72
73 static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *, const char * );
74
75 static char *Save( vlm_t * );
76 static int Load( vlm_t *, char * );
77
78 static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name );
79 static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd,
80                               const char *psz_value );
81
82 /* */
83 static vlm_media_sys_t *vlm_MediaSearch( vlm_t *, const char *);
84
85 static const char quotes[] = "\"'";
86 /**
87  * FindCommandEnd: look for the end of a possibly quoted string
88  * @return NULL on mal-formatted string,
89  * pointer past the last character otherwise.
90  */
91 static const char *FindCommandEnd( const char *psz_sent )
92 {
93     char c, quote = 0;
94
95     while( (c = *psz_sent) != '\0' )
96     {
97         if( !quote )
98         {
99             if( strchr(quotes,c) )   // opening quote
100                 quote = c;
101             else if( isspace(c) )         // non-escaped space
102                 return psz_sent;
103             else if( c == '\\' )
104             {
105                 psz_sent++;         // skip escaped character
106                 if( *psz_sent == '\0' )
107                     return psz_sent;
108             }
109         }
110         else
111         {
112             if( c == quote )         // non-escaped matching quote
113                 quote = 0;
114             else if( (quote == '"') && (c == '\\') )
115             {
116                 psz_sent++;         // skip escaped character
117                 if (*psz_sent == '\0')
118                     return NULL;    // error, closing quote missing
119             }
120         }
121         psz_sent++;
122     }
123
124     // error (NULL) if we could not find a matching quote
125     return quote ? NULL : psz_sent;
126 }
127
128
129 /**
130  * Unescape a nul-terminated string.
131  * Note that in and out can be identical.
132  *
133  * @param out output buffer (at least <strlen (in) + 1> characters long)
134  * @param in nul-terminated string to be unescaped
135  *
136  * @return 0 on success, -1 on error.
137  */
138 static int Unescape( char *out, const char *in )
139 {
140     char c, quote = 0;
141     bool param = false;
142
143     while( (c = *in++) != '\0' )
144     {
145         // Don't escape the end of the string if we find a '#'
146         // that's the begining of a vlc command
147         // TODO: find a better solution
148         if( c == '#' || param )
149         {
150             param = true;
151             *out++ = c;
152             continue;
153         }
154
155         if( !quote )
156         {
157             if (strchr(quotes,c))   // opening quote
158             {
159                 quote = c;
160                 continue;
161             }
162             else if( c == '\\' )
163             {
164                 switch (c = *in++)
165                 {
166                     case '"':
167                     case '\'':
168                     case '\\':
169                         *out++ = c;
170                         continue;
171
172                     case '\0':
173                         *out = '\0';
174                         return 0;
175                 }
176                 if( isspace(c) )
177                 {
178                     *out++ = c;
179                     continue;
180                 }
181                 /* None of the special cases - copy the backslash */
182                 *out++ = '\\';
183             }
184         }
185         else
186         {
187             if( c == quote )         // non-escaped matching quote
188             {
189                 quote = 0;
190                 continue;
191             }
192             if( (quote == '"') && (c == '\\') )
193             {
194                 switch( c = *in++ )
195                 {
196                     case '"':
197                     case '\\':
198                         *out++ = c;
199                         continue;
200
201                     case '\0':   // should never happen
202                         *out = '\0';
203                         return -1;
204                 }
205                 /* None of the special cases - copy the backslash */
206                 *out++ = '\\';
207             }
208         }
209         *out++ = c;
210     }
211
212     *out = '\0';
213     return 0;
214 }
215
216
217 /*****************************************************************************
218  * ExecuteCommand: The main state machine
219  *****************************************************************************
220  * Execute a command which ends with '\0' (string)
221  *****************************************************************************/
222 static int ExecuteSyntaxError( const char *psz_cmd, vlm_message_t **pp_status )
223 {
224     *pp_status = vlm_MessageNew( psz_cmd, "Wrong command syntax" );
225     return VLC_EGENERIC;
226 }
227
228 static bool ExecuteIsMedia( vlm_t *p_vlm, const char *psz_name )
229 {
230     int64_t id;
231
232     if( !psz_name || vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) )
233         return false;
234     return true;
235 }
236 static bool ExecuteIsSchedule( vlm_t *p_vlm, const char *psz_name )
237 {
238     if( !psz_name || !vlm_ScheduleSearch( p_vlm, psz_name ) )
239         return false;
240     return true;
241 }
242
243 static int ExecuteDel( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status )
244 {
245     vlm_media_sys_t *p_media;
246     vlm_schedule_sys_t *p_schedule;
247
248     p_media = vlm_MediaSearch( p_vlm, psz_name );
249     p_schedule = vlm_ScheduleSearch( p_vlm, psz_name );
250
251     if( p_schedule != NULL )
252     {
253         vlm_ScheduleDelete( p_vlm, p_schedule );
254     }
255     else if( p_media != NULL )
256     {
257         vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_media->cfg.id );
258     }
259     else if( !strcmp(psz_name, "media") )
260     {
261         vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
262     }
263     else if( !strcmp(psz_name, "schedule") )
264     {
265         vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
266     }
267     else if( !strcmp(psz_name, "all") )
268     {
269         vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
270         vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
271     }
272     else
273     {
274         *pp_status = vlm_MessageNew( "del", "%s: media unknown", psz_name );
275         return VLC_EGENERIC;
276     }
277
278     *pp_status = vlm_MessageNew( "del", vlm_NULL );
279     return VLC_SUCCESS;
280 }
281
282 static int ExecuteShow( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status )
283 {
284     vlm_media_sys_t *p_media;
285     vlm_schedule_sys_t *p_schedule;
286
287     if( !psz_name )
288     {
289         *pp_status = vlm_Show( p_vlm, NULL, NULL, NULL );
290         return VLC_SUCCESS;
291     }
292
293     p_media = vlm_MediaSearch( p_vlm, psz_name );
294     p_schedule = vlm_ScheduleSearch( p_vlm, psz_name );
295
296     if( p_schedule != NULL )
297         *pp_status = vlm_Show( p_vlm, NULL, p_schedule, NULL );
298     else if( p_media != NULL )
299         *pp_status = vlm_Show( p_vlm, p_media, NULL, NULL );
300     else
301         *pp_status = vlm_Show( p_vlm, NULL, NULL, psz_name );
302
303     return VLC_SUCCESS;
304 }
305
306 static int ExecuteHelp( vlm_message_t **pp_status )
307 {
308     vlm_message_t *message_child;
309
310 #define MessageAdd( a ) \
311         vlm_MessageAdd( *pp_status, vlm_MessageNew( a, vlm_NULL ) );
312 #define MessageAddChild( a ) \
313         vlm_MessageAdd( message_child, vlm_MessageNew( a, vlm_NULL ) );
314
315     *pp_status = vlm_MessageNew( "help", vlm_NULL );
316
317     message_child = MessageAdd( "Commands Syntax:" );
318     MessageAddChild( "new (name) vod|broadcast|schedule [properties]" );
319     MessageAddChild( "setup (name) (properties)" );
320     MessageAddChild( "show [(name)|media|schedule]" );
321     MessageAddChild( "del (name)|all|media|schedule" );
322     MessageAddChild( "control (name) [instance_name] (command)" );
323     MessageAddChild( "save (config_file)" );
324     MessageAddChild( "export" );
325     MessageAddChild( "load (config_file)" );
326
327     message_child = MessageAdd( "Media Proprieties Syntax:" );
328     MessageAddChild( "input (input_name)" );
329     MessageAddChild( "inputdel (input_name)|all" );
330     MessageAddChild( "inputdeln input_number" );
331     MessageAddChild( "output (output_name)" );
332     MessageAddChild( "option (option_name)[=value]" );
333     MessageAddChild( "enabled|disabled" );
334     MessageAddChild( "loop|unloop (broadcast only)" );
335     MessageAddChild( "mux (mux_name)" );
336
337     message_child = MessageAdd( "Schedule Proprieties Syntax:" );
338     MessageAddChild( "enabled|disabled" );
339     MessageAddChild( "append (command_until_rest_of_the_line)" );
340     MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):"
341                      "(seconds)|now" );
342     MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/"
343                      "(days)-(hours):(minutes):(seconds)" );
344     MessageAddChild( "repeat (number_of_repetitions)" );
345
346     message_child = MessageAdd( "Control Commands Syntax:" );
347     MessageAddChild( "play [input_number]" );
348     MessageAddChild( "pause" );
349     MessageAddChild( "stop" );
350     MessageAddChild( "seek [+-](percentage) | [+-](seconds)s | [+-](milliseconds)ms" );
351
352     return VLC_SUCCESS;
353 }
354
355 static int ExecuteControl( vlm_t *p_vlm, const char *psz_name, const int i_arg, char ** ppsz_arg, vlm_message_t **pp_status )
356 {
357     vlm_media_sys_t *p_media;
358     const char *psz_control = NULL;
359     const char *psz_instance = NULL;
360     const char *psz_argument = NULL;
361     int i_index;
362     int i_result;
363
364     if( !ExecuteIsMedia( p_vlm, psz_name ) )
365     {
366         *pp_status = vlm_MessageNew( "control", "%s: media unknown", psz_name );
367         return VLC_EGENERIC;
368     }
369
370     assert( i_arg > 0 );
371
372 #define IS(txt) ( !strcmp( ppsz_arg[i_index], (txt) ) )
373     i_index = 0;
374     if( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") )
375     {
376         i_index = 1;
377         psz_instance = ppsz_arg[0];
378
379         if( i_index >= i_arg || ( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") ) )
380             return ExecuteSyntaxError( "control", pp_status );
381     }
382 #undef IS
383     psz_control = ppsz_arg[i_index];
384
385     if( i_index+1 < i_arg )
386         psz_argument = ppsz_arg[i_index+1];
387
388     p_media = vlm_MediaSearch( p_vlm, psz_name );
389     assert( p_media );
390
391     if( !strcmp( psz_control, "play" ) )
392     {
393         int i_input_index = 0;
394         int i;
395
396         if( ( psz_argument && sscanf(psz_argument, "%d", &i) == 1 ) && i > 0 && i-1 < p_media->cfg.i_input )
397         {
398             i_input_index = i-1;
399         }
400         else if( psz_argument )
401         {
402             int j;
403             vlm_media_t *p_cfg = &p_media->cfg;
404             for ( j=0; j < p_cfg->i_input; j++)
405             {
406                 if( !strcmp( p_cfg->ppsz_input[j], psz_argument ) )
407                 {
408                     i_input_index = j;
409                     break;
410                 }
411             }
412         }
413
414         if( p_media->cfg.b_vod )
415             i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_VOD_INSTANCE, p_media->cfg.id, psz_instance, i_input_index, NULL );    // we should get here now
416         else
417             i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, psz_instance, i_input_index );
418     }
419     else if( !strcmp( psz_control, "seek" ) )
420     {
421         if( psz_argument )
422         {
423             bool b_relative;
424             if( psz_argument[0] == '+' || psz_argument[0] == '-' )
425                 b_relative = true;
426             else
427                 b_relative = false;
428
429             if( strstr( psz_argument, "ms" ) || strstr( psz_argument, "s" ) )
430             {
431                 /* Time (ms or s) */
432                 int64_t i_new_time;
433
434                 if( strstr( psz_argument, "ms" ) )
435                     i_new_time =  1000 * (int64_t)atoi( psz_argument );
436                 else
437                     i_new_time = 1000000 * (int64_t)atoi( psz_argument );
438
439                 if( b_relative )
440                 {
441                     int64_t i_time = 0;
442                     vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, &i_time );
443                     i_new_time += i_time;
444                 }
445                 if( i_new_time < 0 )
446                     i_new_time = 0;
447                 i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, i_new_time );
448             }
449             else
450             {
451                 /* Percent */
452                 double d_new_position = us_atof( psz_argument ) / 100.0;
453
454                 if( b_relative )
455                 {
456                     double d_position = 0.0;
457
458                     vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position );
459                     d_new_position += d_position;
460                 }
461                 if( d_new_position < 0.0 )
462                     d_new_position = 0.0;
463                 else if( d_new_position > 1.0 )
464                     d_new_position = 1.0;
465                 i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_new_position );
466             }
467         }
468         else
469         {
470             i_result = VLC_EGENERIC;
471         }
472     }
473     else if( !strcmp( psz_control, "rewind" ) )
474     {
475         if( psz_argument )
476         {
477             const double d_scale = us_atof( psz_argument );
478             double d_position;
479
480             vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position );
481             d_position -= (d_scale / 1000.0);
482             if( d_position < 0.0 )
483                 d_position = 0.0;
484             i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position );
485         }
486         else
487         {
488             i_result = VLC_EGENERIC;
489         }
490     }
491     else if( !strcmp( psz_control, "forward" ) )
492     {
493         if( psz_argument )
494         {
495             const double d_scale = us_atof( psz_argument );
496             double d_position;
497
498             vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position );
499             d_position += (d_scale / 1000.0);
500             if( d_position > 1.0 )
501                 d_position = 1.0;
502             i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position );
503
504         }
505         else
506         {
507             i_result = VLC_EGENERIC;
508         }
509     }
510     else if( !strcmp( psz_control, "stop" ) )
511     {
512         i_result = vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, psz_instance );
513     }
514     else if( !strcmp( psz_control, "pause" ) )
515     {
516         i_result = vlm_ControlInternal( p_vlm, VLM_PAUSE_MEDIA_INSTANCE, p_media->cfg.id, psz_instance );
517     }
518     else
519     {
520         i_result = VLC_EGENERIC;
521     }
522
523     if( i_result )
524     {
525         *pp_status = vlm_MessageNew( "control", "unknown error" );
526         return VLC_SUCCESS;
527     }
528     *pp_status = vlm_MessageNew( "control", vlm_NULL );
529     return VLC_SUCCESS;
530 }
531
532 static int ExecuteExport( vlm_t *p_vlm, vlm_message_t **pp_status )
533 {
534     char *psz_export = Save( p_vlm );
535
536     *pp_status = vlm_MessageNew( "export", psz_export );
537     free( psz_export );
538     return VLC_SUCCESS;
539 }
540
541 static int ExecuteSave( vlm_t *p_vlm, const char *psz_file, vlm_message_t **pp_status )
542 {
543     FILE *f = utf8_fopen( psz_file, "wt" );
544     char *psz_save = NULL;
545
546     if( !f )
547         goto error;
548
549     psz_save = Save( p_vlm );
550     if( psz_save == NULL )
551         goto error;
552     if( fputs( psz_save, f ) == EOF )
553         goto error;;
554     if( fclose( f ) )
555     {
556         f = NULL;
557         goto error;
558     }
559
560     free( psz_save );
561
562     *pp_status = vlm_MessageNew( "save", vlm_NULL );
563     return VLC_SUCCESS;
564
565 error:
566     free( psz_save );
567     if( f )
568          fclose( f );
569     *pp_status = vlm_MessageNew( "save", "Unable to save to file");
570     return VLC_EGENERIC;
571 }
572
573 static int ExecuteLoad( vlm_t *p_vlm, const char *psz_url, vlm_message_t **pp_status )
574 {
575     stream_t *p_stream = stream_UrlNew( p_vlm, psz_url );
576     int64_t i_size;
577     char *psz_buffer;
578
579     if( !p_stream )
580     {
581         *pp_status = vlm_MessageNew( "load", "Unable to load from file" );
582         return VLC_EGENERIC;
583     }
584
585     /* FIXME needed ? */
586     if( stream_Seek( p_stream, 0 ) != 0 )
587     {
588         stream_Delete( p_stream );
589
590         *pp_status = vlm_MessageNew( "load", "Read file error" );
591         return VLC_EGENERIC;
592     }
593
594     i_size = stream_Size( p_stream );
595
596     psz_buffer = malloc( i_size + 1 );
597     if( !psz_buffer )
598     {
599         stream_Delete( p_stream );
600
601         *pp_status = vlm_MessageNew( "load", "Read file error" );
602         return VLC_EGENERIC;
603     }
604
605     stream_Read( p_stream, psz_buffer, i_size );
606     psz_buffer[i_size] = '\0';
607
608     stream_Delete( p_stream );
609
610     if( Load( p_vlm, psz_buffer ) )
611     {
612         free( psz_buffer );
613
614         *pp_status = vlm_MessageNew( "load", "Error while loading file" );
615         return VLC_EGENERIC;
616     }
617
618     free( psz_buffer );
619
620     *pp_status = vlm_MessageNew( "load", vlm_NULL );
621     return VLC_SUCCESS;
622 }
623
624 static int ExecuteScheduleProperty( vlm_t *p_vlm, vlm_schedule_sys_t *p_schedule, bool b_new,
625                                     const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
626 {
627     const char *psz_cmd = b_new ? "new" : "setup";
628     int i;
629
630     for( i = 0; i < i_property; i++ )
631     {
632         if( !strcmp( ppsz_property[i], "enabled" ) ||
633             !strcmp( ppsz_property[i], "disabled" ) )
634         {
635             if ( vlm_ScheduleSetup( p_schedule, ppsz_property[i], NULL ) )
636                 goto error;
637         }
638         else if( !strcmp( ppsz_property[i], "append" ) )
639         {
640             char *psz_line;
641             int j;
642             /* Beware: everything behind append is considered as
643              * command line */
644
645             if( ++i >= i_property )
646                 break;
647
648             psz_line = strdup( ppsz_property[i] );
649             for( j = i+1; j < i_property; j++ )
650             {
651                 psz_line = realloc( psz_line, strlen(psz_line) + strlen(ppsz_property[j]) + 1 + 1 );
652                 strcat( psz_line, " " );
653                 strcat( psz_line, ppsz_property[j] );
654             }
655
656             if( vlm_ScheduleSetup( p_schedule, "append", psz_line ) )
657                 goto error;
658             break;
659         }
660         else
661         {
662             if( i + 1 >= i_property )
663             {
664                 if( b_new )
665                     vlm_ScheduleDelete( p_vlm, p_schedule );
666                 return ExecuteSyntaxError( psz_cmd, pp_status );
667             }
668
669             if( vlm_ScheduleSetup( p_schedule, ppsz_property[i], ppsz_property[i+1] ) )
670                 goto error;
671             i++;
672         }
673     }
674     *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL );
675     return VLC_SUCCESS;
676
677 error:
678     *pp_status = vlm_MessageNew( psz_cmd, "Error while setting the property '%s' to the schedule",
679                                  ppsz_property[i] );
680     return VLC_EGENERIC;
681 }
682
683 static int ExecuteMediaProperty( vlm_t *p_vlm, int64_t id, bool b_new,
684                                  const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
685 {
686     const char *psz_cmd = b_new ? "new" : "setup";
687     vlm_media_t *p_cfg = NULL;
688     int i_result;
689     int i;
690
691 #undef ERROR
692 #undef MISSING
693 #define ERROR( txt ) do { *pp_status = vlm_MessageNew( psz_cmd, txt); goto error; } while(0)
694     if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA, id, &p_cfg ) )
695         ERROR( "unknown media" );
696
697 #define MISSING(cmd) do { if( !psz_value ) ERROR( "missing argument for " cmd ); } while(0)
698     for( i = 0; i < i_property; i++ )
699     {
700         const char *psz_option = ppsz_property[i];
701         const char *psz_value = i+1 < i_property ? ppsz_property[i+1] :  NULL;
702
703         if( !strcmp( psz_option, "enabled" ) )
704         {
705             p_cfg->b_enabled = true;
706         }
707         else if( !strcmp( psz_option, "disabled" ) )
708         {
709             p_cfg->b_enabled = false;
710         }
711         else if( !strcmp( psz_option, "input" ) )
712         {
713             MISSING( "input" );
714             TAB_APPEND( p_cfg->i_input, p_cfg->ppsz_input, strdup(psz_value) );
715             i++;
716         }
717         else if( !strcmp( psz_option, "inputdel" ) && psz_value && !strcmp( psz_value, "all" ) )
718         {
719             while( p_cfg->i_input > 0 )
720                 TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[0] );
721             i++;
722         }
723         else if( !strcmp( psz_option, "inputdel" ) )
724         {
725             int j;
726
727             MISSING( "inputdel" );
728
729             for( j = 0; j < p_cfg->i_input; j++ )
730             {
731                 if( !strcmp( p_cfg->ppsz_input[j], psz_value ) )
732                 {
733                     TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[j] );
734                     break;
735                 }
736             }
737             i++;
738         }
739         else if( !strcmp( psz_option, "inputdeln" ) )
740         {
741             int i_index;
742
743             MISSING( "inputdeln" );
744  
745             i_index = atoi( psz_value );
746             if( i_index > 0 && i_index <= p_cfg->i_input )
747                 TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[i_index-1] );
748             i++;
749         }
750         else if( !strcmp( psz_option, "output" ) )
751         {
752             MISSING( "output" );
753
754             free( p_cfg->psz_output );
755             p_cfg->psz_output = *psz_value ? strdup( psz_value ) : NULL;
756             i++;
757         }
758         else if( !strcmp( psz_option, "option" ) )
759         {
760             MISSING( "option" );
761
762             TAB_APPEND( p_cfg->i_option, p_cfg->ppsz_option, strdup( psz_value ) );
763             i++;
764         }
765         else if( !strcmp( psz_option, "loop" ) )
766         {
767             if( p_cfg->b_vod )
768                 ERROR( "invalid loop option for vod" );
769             p_cfg->broadcast.b_loop = true;
770         }
771         else if( !strcmp( psz_option, "unloop" ) )
772         {
773             if( p_cfg->b_vod )
774                 ERROR( "invalid unloop option for vod" );
775             p_cfg->broadcast.b_loop = false;
776         }
777         else if( !strcmp( psz_option, "mux" ) )
778         {
779             MISSING( "mux" );
780             if( !p_cfg->b_vod )
781                 ERROR( "invalid mux option for broadcast" );
782
783             free( p_cfg->vod.psz_mux );
784             p_cfg->vod.psz_mux = *psz_value ? strdup( psz_value ) : NULL;
785             i++;
786         }
787         else
788         {
789             fprintf( stderr, "PROP: name=%s unknown\n", psz_option );
790             ERROR( "Wrong command syntax" );
791         }
792     }
793 #undef MISSING
794 #undef ERROR
795
796     /* */
797     i_result = vlm_ControlInternal( p_vlm, VLM_CHANGE_MEDIA, p_cfg );
798     vlm_media_Delete( p_cfg );
799
800     *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL );
801     return i_result;
802
803 error:
804     if( p_cfg )
805     {
806         if( b_new )
807             vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_cfg->id );
808         vlm_media_Delete( p_cfg );
809     }
810     return VLC_EGENERIC;
811 }
812
813 static int ExecuteNew( vlm_t *p_vlm, const char *psz_name, const char *psz_type, const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
814 {
815     /* Check name */
816     if( !strcmp( psz_name, "all" ) || !strcmp( psz_name, "media" ) || !strcmp( psz_name, "schedule" ) )
817     {
818         *pp_status = vlm_MessageNew( "new", "\"all\", \"media\" and \"schedule\" are reserved names" );
819         return VLC_EGENERIC;
820     }
821     if( ExecuteIsMedia( p_vlm, psz_name ) || ExecuteIsSchedule( p_vlm, psz_name ) )
822     {
823         *pp_status = vlm_MessageNew( "new", "%s: Name already in use", psz_name );
824         return VLC_EGENERIC;
825     }
826     /* */
827     if( !strcmp( psz_type, "schedule" ) )
828     {
829         vlm_schedule_sys_t *p_schedule = vlm_ScheduleNew( p_vlm, psz_name );
830         if( !p_schedule )
831         {
832             *pp_status = vlm_MessageNew( "new", "could not create schedule" );
833             return VLC_EGENERIC;
834         }
835         return ExecuteScheduleProperty( p_vlm, p_schedule, true, i_property, ppsz_property, pp_status );
836     }
837     else if( !strcmp( psz_type, "vod" ) || !strcmp( psz_type, "broadcast" ) )
838     {
839         vlm_media_t cfg;
840         int64_t id;
841
842         vlm_media_Init( &cfg );
843         cfg.psz_name = strdup( psz_name );
844         cfg.b_vod = !strcmp( psz_type, "vod" );
845
846         if( vlm_ControlInternal( p_vlm, VLM_ADD_MEDIA, &cfg, &id ) )
847         {
848             vlm_media_Clean( &cfg );
849             *pp_status = vlm_MessageNew( "new", "could not create media" );
850             return VLC_EGENERIC;
851         }
852         vlm_media_Clean( &cfg );
853         return ExecuteMediaProperty( p_vlm, id, true, i_property, ppsz_property, pp_status );
854     }
855     else
856     {
857         *pp_status = vlm_MessageNew( "new", "%s: Choose between vod, broadcast or schedule", psz_type );
858         return VLC_EGENERIC;
859     }
860 }
861
862 static int ExecuteSetup( vlm_t *p_vlm, const char *psz_name, const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
863 {
864     if( ExecuteIsSchedule( p_vlm, psz_name ) )
865     {
866         vlm_schedule_sys_t *p_schedule = vlm_ScheduleSearch( p_vlm, psz_name );
867         return ExecuteScheduleProperty( p_vlm, p_schedule, false, i_property, ppsz_property, pp_status );
868     }
869     else if( ExecuteIsMedia( p_vlm, psz_name ) )
870     {
871         int64_t id;
872         if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) )
873             goto error;
874         return ExecuteMediaProperty( p_vlm, id, false, i_property, ppsz_property, pp_status );
875     }
876
877 error:
878     *pp_status = vlm_MessageNew( "setup", "%s unknown", psz_name );
879     return VLC_EGENERIC;
880 }
881
882 int ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
883                            vlm_message_t **pp_message )
884 {
885     size_t i_command = 0;
886     char buf[strlen (psz_command) + 1], *psz_buf = buf;
887     char *ppsz_command[3+sizeof (buf) / 2];
888     vlm_message_t *p_message = NULL;
889
890     /* First, parse the line and cut it */
891     while( *psz_command != '\0' )
892     {
893         const char *psz_temp;
894
895         if(isspace (*psz_command))
896         {
897             psz_command++;
898             continue;
899         }
900
901         /* support for comments */
902         if( i_command == 0 && *psz_command == '#')
903         {
904             p_message = vlm_MessageNew( "", vlm_NULL );
905             goto success;
906         }
907
908         psz_temp = FindCommandEnd( psz_command );
909
910         if( psz_temp == NULL )
911         {
912             p_message = vlm_MessageNew( "Incomplete command", psz_command );
913             goto error;
914         }
915
916         assert (i_command < (sizeof (ppsz_command) / sizeof (ppsz_command[0])));
917
918         ppsz_command[i_command] = psz_buf;
919         memcpy (psz_buf, psz_command, psz_temp - psz_command);
920         psz_buf[psz_temp - psz_command] = '\0';
921
922         Unescape (psz_buf, psz_buf);
923
924         i_command++;
925         psz_buf += psz_temp - psz_command + 1;
926         psz_command = psz_temp;
927
928         assert (buf + sizeof (buf) >= psz_buf);
929     }
930
931     /*
932      * And then Interpret it
933      */
934
935 #define IF_EXECUTE( name, check, cmd ) if( !strcmp(ppsz_command[0], name ) ) { if( (check) ) goto syntax_error;  if( (cmd) ) goto error; goto success; }
936     if( i_command == 0 )
937     {
938         p_message = vlm_MessageNew( "", vlm_NULL );
939         goto success;
940     }
941     else IF_EXECUTE( "del",     (i_command != 2),   ExecuteDel(p_vlm, ppsz_command[1], &p_message) )
942     else IF_EXECUTE( "show",    (i_command > 2),    ExecuteShow(p_vlm, i_command > 1 ? ppsz_command[1] : NULL, &p_message) )
943     else IF_EXECUTE( "help",    (i_command != 1),   ExecuteHelp( &p_message ) )
944     else IF_EXECUTE( "control", (i_command < 3),    ExecuteControl(p_vlm, ppsz_command[1], i_command - 2, &ppsz_command[2], &p_message) )
945     else IF_EXECUTE( "save",    (i_command != 2),   ExecuteSave(p_vlm, ppsz_command[1], &p_message) )
946     else IF_EXECUTE( "export",  (i_command != 1),   ExecuteExport(p_vlm, &p_message) )
947     else IF_EXECUTE( "load",    (i_command != 2),   ExecuteLoad(p_vlm, ppsz_command[1], &p_message) )
948     else IF_EXECUTE( "new",     (i_command < 3),    ExecuteNew(p_vlm, ppsz_command[1], ppsz_command[2], i_command-3, &ppsz_command[3], &p_message) )
949     else IF_EXECUTE( "setup",   (i_command < 2),    ExecuteSetup(p_vlm, ppsz_command[1], i_command-2, &ppsz_command[2], &p_message) )
950     else
951     {
952         p_message = vlm_MessageNew( ppsz_command[0], "Unknown command" );
953         goto error;
954     }
955 #undef IF_EXECUTE
956
957 success:
958     *pp_message = p_message;
959     return VLC_SUCCESS;
960
961 syntax_error:
962     return ExecuteSyntaxError( ppsz_command[0], pp_message );
963
964 error:
965     *pp_message = p_message;
966     return VLC_EGENERIC;
967 }
968
969 /*****************************************************************************
970  * Media handling
971  *****************************************************************************/
972 vlm_media_sys_t *vlm_MediaSearch( vlm_t *vlm, const char *psz_name )
973 {
974     int i;
975
976     for( i = 0; i < vlm->i_media; i++ )
977     {
978         if( strcmp( psz_name, vlm->media[i]->cfg.psz_name ) == 0 )
979             return vlm->media[i];
980     }
981
982     return NULL;
983 }
984
985 /*****************************************************************************
986  * Schedule handling
987  *****************************************************************************/
988 static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name )
989 {
990     if( !psz_name )
991         return NULL;
992
993     vlm_schedule_sys_t *p_sched = malloc( sizeof( vlm_schedule_sys_t ) );
994     if( !p_sched )
995         return NULL;
996
997     p_sched->psz_name = strdup( psz_name );
998     p_sched->b_enabled = false;
999     p_sched->i_command = 0;
1000     p_sched->command = NULL;
1001     p_sched->i_date = 0;
1002     p_sched->i_period = 0;
1003     p_sched->i_repeat = -1;
1004
1005     TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched );
1006
1007     return p_sched;
1008 }
1009
1010 /* for now, simple delete. After, del with options (last arg) */
1011 void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_sys_t *sched )
1012 {
1013     if( sched == NULL ) return;
1014
1015     TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched );
1016
1017     if( vlm->i_schedule == 0 ) free( vlm->schedule );
1018     free( sched->psz_name );
1019     while( sched->i_command )
1020     {
1021         char *psz_cmd = sched->command[0];
1022         TAB_REMOVE( sched->i_command, sched->command, psz_cmd );
1023         free( psz_cmd );
1024     }
1025     free( sched );
1026 }
1027
1028 static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name )
1029 {
1030     int i;
1031
1032     for( i = 0; i < vlm->i_schedule; i++ )
1033     {
1034         if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 )
1035         {
1036             return vlm->schedule[i];
1037         }
1038     }
1039
1040     return NULL;
1041 }
1042
1043 /* Ok, setup schedule command will be able to support only one (argument value) at a time  */
1044 static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd,
1045                        const char *psz_value )
1046 {
1047     if( !strcmp( psz_cmd, "enabled" ) )
1048     {
1049         schedule->b_enabled = true;
1050     }
1051     else if( !strcmp( psz_cmd, "disabled" ) )
1052     {
1053         schedule->b_enabled = false;
1054     }
1055     else if( !strcmp( psz_cmd, "date" ) )
1056     {
1057         struct tm time;
1058         const char *p;
1059         time_t date;
1060
1061         time.tm_sec = 0;         /* seconds */
1062         time.tm_min = 0;         /* minutes */
1063         time.tm_hour = 0;        /* hours */
1064         time.tm_mday = 0;        /* day of the month */
1065         time.tm_mon = 0;         /* month */
1066         time.tm_year = 0;        /* year */
1067         time.tm_wday = 0;        /* day of the week */
1068         time.tm_yday = 0;        /* day in the year */
1069         time.tm_isdst = -1;       /* daylight saving time */
1070
1071         /* date should be year/month/day-hour:minutes:seconds */
1072         p = strchr( psz_value, '-' );
1073
1074         if( !strcmp( psz_value, "now" ) )
1075         {
1076             schedule->i_date = 0;
1077         }
1078         else if(p == NULL)
1079         {
1080             return 1;
1081         }
1082         else
1083         {
1084             unsigned i,j,k;
1085
1086             switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) )
1087             {
1088                 case 1:
1089                     time.tm_sec = i;
1090                     break;
1091                 case 2:
1092                     time.tm_min = i;
1093                     time.tm_sec = j;
1094                     break;
1095                 case 3:
1096                     time.tm_hour = i;
1097                     time.tm_min = j;
1098                     time.tm_sec = k;
1099                     break;
1100                 default:
1101                     return 1;
1102             }
1103
1104             switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) )
1105             {
1106                 case 1:
1107                     time.tm_mday = i;
1108                     break;
1109                 case 2:
1110                     time.tm_mon = i - 1;
1111                     time.tm_mday = j;
1112                     break;
1113                 case 3:
1114                     time.tm_year = i - 1900;
1115                     time.tm_mon = j - 1;
1116                     time.tm_mday = k;
1117                     break;
1118                 default:
1119                     return 1;
1120             }
1121
1122             date = mktime( &time );
1123             schedule->i_date = ((mtime_t) date) * 1000000;
1124         }
1125     }
1126     else if( !strcmp( psz_cmd, "period" ) )
1127     {
1128         struct tm time;
1129         const char *p;
1130         const char *psz_time = NULL, *psz_date = NULL;
1131         time_t date;
1132         unsigned i,j,k;
1133
1134         /* First, if date or period are modified, repeat should be equal to -1 */
1135         schedule->i_repeat = -1;
1136
1137         time.tm_sec = 0;         /* seconds */
1138         time.tm_min = 0;         /* minutes */
1139         time.tm_hour = 0;        /* hours */
1140         time.tm_mday = 0;        /* day of the month */
1141         time.tm_mon = 0;         /* month */
1142         time.tm_year = 0;        /* year */
1143         time.tm_wday = 0;        /* day of the week */
1144         time.tm_yday = 0;        /* day in the year */
1145         time.tm_isdst = -1;       /* daylight saving time */
1146
1147         /* date should be year/month/day-hour:minutes:seconds */
1148         p = strchr( psz_value, '-' );
1149         if( p )
1150         {
1151             psz_date = psz_value;
1152             psz_time = p + 1;
1153         }
1154         else
1155         {
1156             psz_time = psz_value;
1157         }
1158
1159         switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) )
1160         {
1161             case 1:
1162                 time.tm_sec = i;
1163                 break;
1164             case 2:
1165                 time.tm_min = i;
1166                 time.tm_sec = j;
1167                 break;
1168             case 3:
1169                 time.tm_hour = i;
1170                 time.tm_min = j;
1171                 time.tm_sec = k;
1172                 break;
1173             default:
1174                 return 1;
1175         }
1176         if( psz_date )
1177         {
1178             switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) )
1179             {
1180                 case 1:
1181                     time.tm_mday = i;
1182                     break;
1183                 case 2:
1184                     time.tm_mon = i;
1185                     time.tm_mday = j;
1186                     break;
1187                 case 3:
1188                     time.tm_year = i;
1189                     time.tm_mon = j;
1190                     time.tm_mday = k;
1191                     break;
1192                 default:
1193                     return 1;
1194             }
1195         }
1196
1197         /* ok, that's stupid... who is going to schedule streams every 42 years ? */
1198         date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ;
1199         schedule->i_period = ((mtime_t) date) * 1000000;
1200     }
1201     else if( !strcmp( psz_cmd, "repeat" ) )
1202     {
1203         int i;
1204
1205         if( sscanf( psz_value, "%d", &i ) == 1 )
1206         {
1207             schedule->i_repeat = i;
1208         }
1209         else
1210         {
1211             return 1;
1212         }
1213     }
1214     else if( !strcmp( psz_cmd, "append" ) )
1215     {
1216         char *command = strdup( psz_value );
1217
1218         TAB_APPEND( schedule->i_command, schedule->command, command );
1219     }
1220     else
1221     {
1222         return 1;
1223     }
1224     return 0;
1225 }
1226
1227 /*****************************************************************************
1228  * Message handling functions
1229  *****************************************************************************/
1230 vlm_message_t *vlm_MessageNew( const char *psz_name,
1231                                const char *psz_format, ... )
1232 {
1233     vlm_message_t *p_message;
1234     va_list args;
1235
1236     if( !psz_name ) return NULL;
1237
1238     p_message = malloc( sizeof(vlm_message_t) );
1239     if( !p_message)
1240     {
1241         return NULL;
1242     }
1243
1244     p_message->psz_value = 0;
1245
1246     if( psz_format )
1247     {
1248         va_start( args, psz_format );
1249         if( vasprintf( &p_message->psz_value, psz_format, args ) == -1 )
1250         {
1251             va_end( args );
1252             free( p_message );
1253             return NULL;
1254         }
1255         va_end( args );
1256     }
1257
1258     p_message->psz_name = strdup( psz_name );
1259     p_message->i_child = 0;
1260     p_message->child = NULL;
1261
1262     return p_message;
1263 }
1264
1265 void vlm_MessageDelete( vlm_message_t *p_message )
1266 {
1267     free( p_message->psz_name );
1268     free( p_message->psz_value );
1269     while( p_message->i_child-- )
1270         vlm_MessageDelete( p_message->child[p_message->i_child] );
1271     free( p_message->child );
1272     free( p_message );
1273 }
1274
1275 /* Add a child */
1276 vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message,
1277                                vlm_message_t *p_child )
1278 {
1279     if( p_message == NULL ) return NULL;
1280
1281     if( p_child )
1282     {
1283         TAB_APPEND( p_message->i_child, p_message->child, p_child );
1284     }
1285
1286     return p_child;
1287 }
1288
1289 /*****************************************************************************
1290  * Misc utility functions
1291  *****************************************************************************/
1292 static vlm_message_t *vlm_ShowMedia( vlm_media_sys_t *p_media )
1293 {
1294     vlm_media_t *p_cfg = &p_media->cfg;
1295     vlm_message_t *p_msg;
1296     vlm_message_t *p_msg_sub;
1297     int i;
1298
1299     p_msg = vlm_MessageNew( p_cfg->psz_name, vlm_NULL );
1300     vlm_MessageAdd( p_msg,
1301                     vlm_MessageNew( "type", p_cfg->b_vod ? "vod" : "broadcast" ) );
1302     vlm_MessageAdd( p_msg,
1303                     vlm_MessageNew( "enabled", p_cfg->b_enabled ? "yes" : "no" ) );
1304
1305     if( p_cfg->b_vod )
1306         vlm_MessageAdd( p_msg,
1307                         vlm_MessageNew( "mux", p_cfg->vod.psz_mux ) );
1308     else
1309         vlm_MessageAdd( p_msg,
1310                         vlm_MessageNew( "loop", p_cfg->broadcast.b_loop ? "yes" : "no" ) );
1311
1312     p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "inputs", vlm_NULL ) );
1313     for( i = 0; i < p_cfg->i_input; i++ )
1314     {
1315         char *psz_tmp;
1316         if( asprintf( &psz_tmp, "%d", i+1 ) != -1 )
1317         {
1318             vlm_MessageAdd( p_msg_sub,
1319                             vlm_MessageNew( psz_tmp, p_cfg->ppsz_input[i] ) );
1320             free( psz_tmp );
1321         }
1322     }
1323
1324     vlm_MessageAdd( p_msg,
1325                     vlm_MessageNew( "output", p_cfg->psz_output ? p_cfg->psz_output : "" ) );
1326
1327     p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "options", vlm_NULL ) );
1328     for( i = 0; i < p_cfg->i_option; i++ )
1329         vlm_MessageAdd( p_msg_sub, vlm_MessageNew( p_cfg->ppsz_option[i], vlm_NULL ) );
1330
1331     p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "instances", vlm_NULL ) );
1332     for( i = 0; i < p_media->i_instance; i++ )
1333     {
1334         vlm_media_instance_sys_t *p_instance = p_media->instance[i];
1335         vlc_value_t val;
1336         vlm_message_t *p_msg_instance;
1337         char *psz_tmp;
1338
1339         val.i_int = END_S;
1340         if( p_instance->p_input )
1341             var_Get( p_instance->p_input, "state", &val );
1342
1343         p_msg_instance = vlm_MessageAdd( p_msg_sub, vlm_MessageNew( "instance" , vlm_NULL ) );
1344
1345         vlm_MessageAdd( p_msg_instance,
1346                         vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) );
1347         vlm_MessageAdd( p_msg_instance,
1348                         vlm_MessageNew( "state",
1349                             val.i_int == PLAYING_S ? "playing" :
1350                             val.i_int == PAUSE_S ? "paused" :
1351                             "stopped" ) );
1352
1353         /* FIXME should not do that this way */
1354         if( p_instance->p_input )
1355         {
1356 #define APPEND_INPUT_INFO( a, format, type ) \
1357             if( asprintf( &psz_tmp, format, \
1358                       var_Get ## type( p_instance->p_input, a ) ) != -1 ) \
1359             { \
1360                 vlm_MessageAdd( p_msg_instance, vlm_MessageNew( a, \
1361                                 psz_tmp ) ); \
1362                 free( psz_tmp ); \
1363             }
1364             APPEND_INPUT_INFO( "position", "%f", Float );
1365             APPEND_INPUT_INFO( "time", "%"PRIi64, Time );
1366             APPEND_INPUT_INFO( "length", "%"PRIi64, Time );
1367             APPEND_INPUT_INFO( "rate", "%d", Integer );
1368             APPEND_INPUT_INFO( "title", "%d", Integer );
1369             APPEND_INPUT_INFO( "chapter", "%d", Integer );
1370             APPEND_INPUT_INFO( "can-seek", "%d", Bool );
1371         }
1372 #undef APPEND_INPUT_INFO
1373         if( asprintf( &psz_tmp, "%d", p_instance->i_index + 1 ) != -1 )
1374         {
1375             vlm_MessageAdd( p_msg_instance, vlm_MessageNew( "playlistindex",
1376                             psz_tmp ) );
1377             free( psz_tmp );
1378         }
1379     }
1380     return p_msg;
1381 }
1382
1383 static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_sys_t *media,
1384                                 vlm_schedule_sys_t *schedule,
1385                                 const char *psz_filter )
1386 {
1387     if( media != NULL )
1388     {
1389         vlm_message_t *p_msg = vlm_MessageNew( "show", vlm_NULL );
1390         if( p_msg )
1391             vlm_MessageAdd( p_msg, vlm_ShowMedia( media ) );
1392         return p_msg;
1393     }
1394
1395     else if( schedule != NULL )
1396     {
1397         int i;
1398         vlm_message_t *msg;
1399         vlm_message_t *msg_schedule;
1400         vlm_message_t *msg_child;
1401         char buffer[100];
1402
1403         msg = vlm_MessageNew( "show", vlm_NULL );
1404         msg_schedule =
1405             vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, vlm_NULL ) );
1406
1407         vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") );
1408
1409         vlm_MessageAdd( msg_schedule,
1410                         vlm_MessageNew( "enabled", schedule->b_enabled ?
1411                                         "yes" : "no" ) );
1412
1413         if( schedule->i_date != 0 )
1414         {
1415             struct tm date;
1416             time_t i_time = (time_t)( schedule->i_date / 1000000 );
1417             char *psz_date;
1418
1419             localtime_r( &i_time, &date);
1420             if( asprintf( &psz_date, "%d/%d/%d-%d:%d:%d",
1421                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1422                           date.tm_hour, date.tm_min, date.tm_sec ) != -1 )
1423             {
1424                  vlm_MessageAdd( msg_schedule,
1425                                  vlm_MessageNew( "date", psz_date ) );
1426                  free( psz_date );
1427             }
1428         }
1429         else
1430             vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") );
1431
1432         if( schedule->i_period != 0 )
1433         {
1434             time_t i_time = (time_t) ( schedule->i_period / 1000000 );
1435             struct tm date;
1436
1437             date.tm_sec = (int)( i_time % 60 );
1438             i_time = i_time / 60;
1439             date.tm_min = (int)( i_time % 60 );
1440             i_time = i_time / 60;
1441             date.tm_hour = (int)( i_time % 24 );
1442             i_time = i_time / 24;
1443             date.tm_mday = (int)( i_time % 30 );
1444             i_time = i_time / 30;
1445             /* okay, okay, months are not always 30 days long */
1446             date.tm_mon = (int)( i_time % 12 );
1447             i_time = i_time / 12;
1448             date.tm_year = (int)i_time;
1449
1450             sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon,
1451                      date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
1452
1453             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) );
1454         }
1455         else
1456             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") );
1457
1458         sprintf( buffer, "%d", schedule->i_repeat );
1459         vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) );
1460
1461         msg_child =
1462             vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", vlm_NULL ) );
1463
1464         for( i = 0; i < schedule->i_command; i++ )
1465         {
1466            vlm_MessageAdd( msg_child,
1467                            vlm_MessageNew( schedule->command[i], vlm_NULL ) );
1468         }
1469
1470         return msg;
1471
1472     }
1473
1474     else if( psz_filter && !strcmp( psz_filter, "media" ) )
1475     {
1476         vlm_message_t *p_msg;
1477         vlm_message_t *p_msg_child;
1478         int i_vod = 0, i_broadcast = 0;
1479         int i;
1480         char *psz_count;
1481
1482         for( i = 0; i < vlm->i_media; i++ )
1483         {
1484             if( vlm->media[i]->cfg.b_vod )
1485                 i_vod++;
1486             else
1487                 i_broadcast++;
1488         }
1489
1490         if( asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast,
1491                       i_vod) == -1 )
1492             return NULL;
1493         p_msg = vlm_MessageNew( "show", vlm_NULL );
1494         p_msg_child = vlm_MessageAdd( p_msg, vlm_MessageNew( "media", psz_count ) );
1495         free( psz_count );
1496
1497         for( i = 0; i < vlm->i_media; i++ )
1498             vlm_MessageAdd( p_msg_child, vlm_ShowMedia( vlm->media[i] ) );
1499
1500         return p_msg;
1501     }
1502
1503     else if( psz_filter && !strcmp( psz_filter, "schedule" ) )
1504     {
1505         int i;
1506         vlm_message_t *msg;
1507         vlm_message_t *msg_child;
1508
1509         msg = vlm_MessageNew( "show", vlm_NULL );
1510         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", vlm_NULL ) );
1511
1512         for( i = 0; i < vlm->i_schedule; i++ )
1513         {
1514             vlm_schedule_sys_t *s = vlm->schedule[i];
1515             vlm_message_t *msg_schedule;
1516             mtime_t i_time, i_next_date;
1517
1518             msg_schedule = vlm_MessageAdd( msg_child,
1519                                            vlm_MessageNew( s->psz_name, vlm_NULL ) );
1520             vlm_MessageAdd( msg_schedule,
1521                             vlm_MessageNew( "enabled", s->b_enabled ?
1522                                             "yes" : "no" ) );
1523
1524             /* calculate next date */
1525             i_time = vlm_Date();
1526             i_next_date = s->i_date;
1527
1528             if( s->i_period != 0 )
1529             {
1530                 int j = 0;
1531                 while( s->i_date + j * s->i_period <= i_time &&
1532                        s->i_repeat > j )
1533                 {
1534                     j++;
1535                 }
1536
1537                 i_next_date = s->i_date + j * s->i_period;
1538             }
1539
1540             if( i_next_date > i_time )
1541             {
1542                 time_t i_date = (time_t) (i_next_date / 1000000) ;
1543
1544 #if !defined( UNDER_CE )
1545 #ifdef HAVE_CTIME_R
1546                 char psz_date[500];
1547                 ctime_r( &i_date, psz_date );
1548 #else
1549                 char *psz_date = ctime( &i_date );
1550 #endif
1551
1552                 vlm_MessageAdd( msg_schedule,
1553                                 vlm_MessageNew( "next launch", psz_date ) );
1554 #endif
1555             }
1556         }
1557
1558         return msg;
1559     }
1560
1561     else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) )
1562     {
1563         vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" );
1564         vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" );
1565
1566         vlm_MessageAdd( show1, show2->child[0] );
1567
1568         /* We must destroy the parent node "show" of show2
1569          * and not the children */
1570         free( show2->psz_name );
1571         free( show2 );
1572
1573         return show1;
1574     }
1575
1576     else
1577     {
1578         return vlm_MessageNew( "show", vlm_NULL );
1579     }
1580 }
1581
1582 /*****************************************************************************
1583  * Config handling functions
1584  *****************************************************************************/
1585 static int Load( vlm_t *vlm, char *file )
1586 {
1587     char *pf = file;
1588     int  i_line = 1;
1589
1590     while( *pf != '\0' )
1591     {
1592         vlm_message_t *message = NULL;
1593         int i_end = 0;
1594
1595         while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' )
1596         {
1597             i_end++;
1598         }
1599
1600         if( pf[i_end] == '\r' || pf[i_end] == '\n' )
1601         {
1602             pf[i_end] = '\0';
1603             i_end++;
1604             if( pf[i_end] == '\n' ) i_end++;
1605         }
1606
1607         if( *pf && ExecuteCommand( vlm, pf, &message ) )
1608         {
1609             if( message )
1610             {
1611                 if( message->psz_value )
1612                     msg_Err( vlm, "Load error on line %d: %s: %s",
1613                              i_line, message->psz_name, message->psz_value );
1614                 vlm_MessageDelete( message );
1615             }
1616             return 1;
1617         }
1618         if( message ) vlm_MessageDelete( message );
1619
1620         pf += i_end;
1621         i_line++;
1622     }
1623
1624     return 0;
1625 }
1626
1627 static char *Save( vlm_t *vlm )
1628 {
1629     char *save = NULL;
1630     char psz_header[] = "\n"
1631                         "# VLC media player VLM command batch\n"
1632                         "# http://www.videolan.org/vlc/\n\n" ;
1633     char *p;
1634     int i,j;
1635     int i_length = strlen( psz_header );
1636
1637     for( i = 0; i < vlm->i_media; i++ )
1638     {
1639         vlm_media_sys_t *media = vlm->media[i];
1640         vlm_media_t *p_cfg = &media->cfg;
1641
1642         if( p_cfg->b_vod )
1643             i_length += strlen( "new * vod " ) + strlen(p_cfg->psz_name);
1644         else
1645             i_length += strlen( "new * broadcast " ) + strlen(p_cfg->psz_name);
1646
1647         if( p_cfg->b_enabled == true )
1648             i_length += strlen( "enabled" );
1649         else
1650             i_length += strlen( "disabled" );
1651
1652         if( !p_cfg->b_vod && p_cfg->broadcast.b_loop == true )
1653             i_length += strlen( " loop\n" );
1654         else
1655             i_length += strlen( "\n" );
1656
1657         for( j = 0; j < p_cfg->i_input; j++ )
1658             i_length += strlen( "setup * input \"\"\n" ) + strlen( p_cfg->psz_name ) + strlen( p_cfg->ppsz_input[j] );
1659
1660         if( p_cfg->psz_output != NULL )
1661             i_length += strlen( "setup * output \n" ) + strlen(p_cfg->psz_name) + strlen(p_cfg->psz_output);
1662
1663         for( j = 0; j < p_cfg->i_option; j++ )
1664             i_length += strlen("setup * option \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->ppsz_option[j]);
1665
1666         if( p_cfg->b_vod && p_cfg->vod.psz_mux )
1667             i_length += strlen("setup * mux \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->vod.psz_mux);
1668     }
1669
1670     for( i = 0; i < vlm->i_schedule; i++ )
1671     {
1672         vlm_schedule_sys_t *schedule = vlm->schedule[i];
1673
1674         i_length += strlen( "new  schedule " ) + strlen( schedule->psz_name );
1675
1676         if( schedule->b_enabled == true )
1677         {
1678             i_length += strlen( "date //-:: enabled\n" ) + 14;
1679         }
1680         else
1681         {
1682             i_length += strlen( "date //-:: disabled\n" ) + 14;
1683         }
1684
1685
1686         if( schedule->i_period != 0 )
1687         {
1688             i_length += strlen( "setup  " ) + strlen( schedule->psz_name ) +
1689                 strlen( "period //-::\n" ) + 14;
1690         }
1691
1692         if( schedule->i_repeat >= 0 )
1693         {
1694             char buffer[12];
1695
1696             sprintf( buffer, "%d", schedule->i_repeat );
1697             i_length += strlen( "setup  repeat \n" ) +
1698                 strlen( schedule->psz_name ) + strlen( buffer );
1699         }
1700         else
1701         {
1702             i_length++;
1703         }
1704
1705         for( j = 0; j < schedule->i_command; j++ )
1706         {
1707             i_length += strlen( "setup  append \n" ) +
1708                 strlen( schedule->psz_name ) + strlen( schedule->command[j] );
1709         }
1710
1711     }
1712
1713     /* Don't forget the '\0' */
1714     i_length++;
1715     /* now we have the length of save */
1716
1717     p = save = malloc( i_length );
1718     if( !save ) return NULL;
1719     *save = '\0';
1720
1721     p += sprintf( p, "%s", psz_header );
1722
1723     /* finally we can write in it */
1724     for( i = 0; i < vlm->i_media; i++ )
1725     {
1726         vlm_media_sys_t *media = vlm->media[i];
1727         vlm_media_t *p_cfg = &media->cfg;
1728
1729         if( p_cfg->b_vod )
1730             p += sprintf( p, "new %s vod ", p_cfg->psz_name );
1731         else
1732             p += sprintf( p, "new %s broadcast ", p_cfg->psz_name );
1733
1734         if( p_cfg->b_enabled )
1735             p += sprintf( p, "enabled" );
1736         else
1737             p += sprintf( p, "disabled" );
1738
1739         if( !p_cfg->b_vod && p_cfg->broadcast.b_loop )
1740             p += sprintf( p, " loop\n" );
1741         else
1742             p += sprintf( p, "\n" );
1743
1744         for( j = 0; j < p_cfg->i_input; j++ )
1745             p += sprintf( p, "setup %s input \"%s\"\n", p_cfg->psz_name, p_cfg->ppsz_input[j] );
1746
1747         if( p_cfg->psz_output )
1748             p += sprintf( p, "setup %s output %s\n", p_cfg->psz_name, p_cfg->psz_output );
1749
1750         for( j = 0; j < p_cfg->i_option; j++ )
1751             p += sprintf( p, "setup %s option %s\n", p_cfg->psz_name, p_cfg->ppsz_option[j] );
1752
1753         if( p_cfg->b_vod && p_cfg->vod.psz_mux )
1754             p += sprintf( p, "setup %s mux %s\n", p_cfg->psz_name, p_cfg->vod.psz_mux );
1755     }
1756
1757     /* and now, the schedule scripts */
1758     for( i = 0; i < vlm->i_schedule; i++ )
1759     {
1760         vlm_schedule_sys_t *schedule = vlm->schedule[i];
1761         struct tm date;
1762         time_t i_time = (time_t) ( schedule->i_date / 1000000 );
1763
1764         localtime_r( &i_time, &date);
1765         p += sprintf( p, "new %s schedule ", schedule->psz_name);
1766
1767         if( schedule->b_enabled == true )
1768         {
1769             p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n",
1770                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1771                           date.tm_hour, date.tm_min, date.tm_sec );
1772         }
1773         else
1774         {
1775             p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n",
1776                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1777                           date.tm_hour, date.tm_min, date.tm_sec);
1778         }
1779
1780         if( schedule->i_period != 0 )
1781         {
1782             p += sprintf( p, "setup %s ", schedule->psz_name );
1783
1784             i_time = (time_t) ( schedule->i_period / 1000000 );
1785
1786             date.tm_sec = (int)( i_time % 60 );
1787             i_time = i_time / 60;
1788             date.tm_min = (int)( i_time % 60 );
1789             i_time = i_time / 60;
1790             date.tm_hour = (int)( i_time % 24 );
1791             i_time = i_time / 24;
1792             date.tm_mday = (int)( i_time % 30 );
1793             i_time = i_time / 30;
1794             /* okay, okay, months are not always 30 days long */
1795             date.tm_mon = (int)( i_time % 12 );
1796             i_time = i_time / 12;
1797             date.tm_year = (int)i_time;
1798
1799             p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n",
1800                           date.tm_year, date.tm_mon, date.tm_mday,
1801                           date.tm_hour, date.tm_min, date.tm_sec);
1802         }
1803
1804         if( schedule->i_repeat >= 0 )
1805         {
1806             p += sprintf( p, "setup %s repeat %d\n",
1807                           schedule->psz_name, schedule->i_repeat );
1808         }
1809         else
1810         {
1811             p += sprintf( p, "\n" );
1812         }
1813
1814         for( j = 0; j < schedule->i_command; j++ )
1815         {
1816             p += sprintf( p, "setup %s append %s\n",
1817                           schedule->psz_name, schedule->command[j] );
1818         }
1819
1820     }
1821
1822     return save;
1823 }
1824
1825 #endif /* ENABLE_VLM */