]> git.sesse.net Git - vlc/blob - src/interface/interface.c
* Added -a, -c and -s options. (-a doesn't work but I will let the people
[vlc] / src / interface / interface.c
1 /*****************************************************************************
2  * interface.c: interface access for other threads
3  * This library provides basic functions for threads to interact with user
4  * interface, such as command line.
5  *****************************************************************************
6  * Copyright (C) 1998, 1999, 2000 VideoLAN
7  *
8  * Authors:
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                      /* free(), strtol() */
32 #include <stdio.h>                                                   /* FILE */
33 #include <string.h>                                            /* strerror() */
34
35 #include "config.h"
36 #include "common.h"
37 #include "threads.h"
38 #include "mtime.h"
39 #include "plugins.h"
40 #include "playlist.h"
41
42 #include "stream_control.h"
43 #include "input_ext-intf.h"
44
45 #include "audio_output.h"
46
47 #include "intf_msg.h"
48 #include "interface.h"
49 #include "intf_cmd.h"
50 #include "intf_console.h"
51 #include "keystrokes.h"
52
53 #include "video.h"
54 #include "video_output.h"
55
56 #include "main.h"
57
58 /*****************************************************************************
59  * intf_channel_t: channel description
60  *****************************************************************************
61  * A 'channel' is a descriptor of an input method. It is used to switch easily
62  * from source to source without having to specify the whole input thread
63  * configuration. The channels array, stored in the interface thread object, is
64  * loaded in intf_Create, and unloaded in intf_Destroy.
65  *****************************************************************************/
66 typedef struct intf_channel_s
67 {
68     /* Channel description */
69     int         i_channel;            /* channel number, -1 for end of array */
70     char *      psz_description;              /* channel description (owned) */
71
72     /* Input configuration */
73     int         i_input_method;                   /* input method descriptor */
74     char *      psz_input_source;                   /* source string (owned) */
75     int         i_input_port;                                        /* port */
76     int         i_input_vlan;                                        /* vlan */
77 } intf_channel_t;
78
79 /*****************************************************************************
80  * Local prototypes
81  *****************************************************************************/
82 static int      LoadChannels    ( intf_thread_t *p_intf, char *psz_filename );
83 static void     UnloadChannels  ( intf_thread_t *p_intf );
84 static int      ParseChannel    ( intf_channel_t *p_channel, char *psz_str );
85
86 /*****************************************************************************
87  * intf_Create: prepare interface before main loop
88  *****************************************************************************
89  * This function opens output devices and creates specific interfaces. It sends
90  * its own error messages.
91  *****************************************************************************/
92 intf_thread_t* intf_Create( void )
93 {
94     intf_thread_t * p_intf;
95     typedef void    ( intf_getplugin_t ) ( intf_thread_t * p_intf );
96     int             i_index;
97     int             i_best_index = 0, i_best_score = 0;
98
99     /* Allocate structure */
100     p_intf = malloc( sizeof( intf_thread_t ) );
101     if( !p_intf )
102     {
103         intf_ErrMsg("error: %s\n", strerror( ENOMEM ) );
104         return( NULL );
105     }
106
107     /* Get a suitable interface plugin */
108     for( i_index = 0 ; i_index < p_main->p_bank->i_plugin_count ; i_index++ )
109     {
110         /* If there's a plugin in p_info ... */
111         if( p_main->p_bank->p_info[ i_index ] != NULL )
112         {
113             /* ... and if this plugin provides the functions we want ... */
114             if( p_main->p_bank->p_info[ i_index ]->intf_GetPlugin != NULL )
115             {
116                 /* ... and if this plugin has a good score ... */
117                 if( p_main->p_bank->p_info[ i_index ]->i_score > i_best_score )
118                 {
119                     /* ... then take it */
120                     i_best_score = p_main->p_bank->p_info[ i_index ]->i_score;
121                     i_best_index = i_index;
122                 }
123             }
124         }
125     }
126
127     if( i_best_score == 0 )
128     {
129         free( p_intf );
130         intf_ErrMsg( "error: no suitable plugin to create interface\n" );
131         return( NULL );
132     }
133
134     /* Get the plugin functions */
135     ( (intf_getplugin_t *)
136       p_main->p_bank->p_info[ i_best_index ]->intf_GetPlugin )( p_intf );
137
138     /* Initialize structure */
139     p_intf->b_die =     0;
140     p_intf->p_vout =    NULL;
141     p_intf->p_input =   NULL;
142     p_intf->p_keys =    NULL;
143
144     /* Warning level initialisation */
145     p_intf->i_warning_level = main_GetIntVariable( INTF_WARNING_VAR, INTF_WARNING_DEFAULT );
146     
147     /* Load channels - the pointer will be set to NULL on failure. The
148      * return value is ignored since the program can work without
149      * channels */
150     LoadChannels( p_intf, main_GetPszVariable( INTF_CHANNELS_VAR, INTF_CHANNELS_DEFAULT ));
151
152     /* Start interfaces */
153     p_intf->p_console = intf_ConsoleCreate();
154     if( p_intf->p_console == NULL )
155     {
156         intf_ErrMsg("error: can't create control console\n");
157         free( p_intf );
158         return( NULL );
159     }
160     if( p_intf->p_sys_create( p_intf ) )
161     {
162         intf_ErrMsg("error: can't create interface\n");
163         intf_ConsoleDestroy( p_intf->p_console );
164         free( p_intf );
165         return( NULL );
166     }
167
168     intf_Msg("Interface initialized\n");
169     return( p_intf );
170 }
171
172 /*****************************************************************************
173  * intf_Run
174  *****************************************************************************
175  * Initialization script and main interface loop.
176  *****************************************************************************/
177 void intf_Run( intf_thread_t *p_intf )
178 {
179     char * psz_server = main_GetPszVariable( INPUT_SERVER_VAR, NULL );
180     input_config_t *    p_input_config;
181
182     /* If a server was specified */
183     if( psz_server )
184     {
185         if( (p_input_config =
186               (input_config_t *)malloc( sizeof(input_config_t) )) == NULL )
187         {
188             intf_ErrMsg("Out of memory");
189         }
190         else
191         {
192             p_input_config->i_method = INPUT_METHOD_UCAST;
193             p_input_config->p_source = psz_server;
194             p_input_config->p_default_aout = p_main->p_aout;
195             p_input_config->p_default_vout = p_intf->p_vout;
196
197             p_intf->p_input = input_CreateThread( p_input_config, NULL );
198         }
199     }
200     /* Or if a file was specified */
201     else if( p_main->p_playlist->p_list != NULL )
202     {
203         if( (p_input_config =
204               (input_config_t *)malloc( sizeof(input_config_t) )) == NULL )
205         {
206             intf_ErrMsg("Out of memory");
207         }
208         else
209         {
210             p_input_config->i_method = INPUT_METHOD_FILE;
211             p_input_config->p_source = p_main->p_playlist->p_list[0]; /* FIXME ??? */
212             p_input_config->p_default_aout = p_main->p_aout;
213             p_input_config->p_default_vout = p_intf->p_vout;
214
215             p_intf->p_input = input_CreateThread( p_input_config, NULL );
216         }
217     }
218     /* Execute the initialization script - if a positive number is returned,
219      * the script could be executed but failed */
220     else if( intf_ExecScript( main_GetPszVariable( INTF_INIT_SCRIPT_VAR, INTF_INIT_SCRIPT_DEFAULT ) ) > 0 )
221     {
222         intf_ErrMsg("warning: error(s) during startup script\n");
223     }
224
225     /* Main loop */
226     while(!p_intf->b_die)
227     {
228         /* Flush waiting messages */
229         intf_FlushMsg();
230
231         /* Manage specific interface */
232         p_intf->p_sys_manage( p_intf );
233
234         /* Check attached threads status */
235         if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_error )
236         {
237             /* FIXME: add aout error detection ?? */
238             p_intf->b_die = 1;
239         }
240         if( (p_intf->p_input != NULL) && p_intf->p_input->b_error )
241         {
242             input_DestroyThread( p_intf->p_input, NULL );
243             p_intf->p_input = NULL;
244             intf_DbgMsg("Input thread destroyed\n");
245         }
246
247         /* Sleep to avoid using all CPU - since some interfaces needs to access
248          * keyboard events, a 100ms delay is a good compromise */
249         msleep( INTF_IDLE_SLEEP );
250     }
251 }
252
253 /*****************************************************************************
254  * intf_Destroy: clean interface after main loop
255  *****************************************************************************
256  * This function destroys specific interfaces and close output devices.
257  *****************************************************************************/
258 void intf_Destroy( intf_thread_t *p_intf )
259 {
260     p_intf_key  p_cur;
261     p_intf_key  p_next;
262     /* Destroy interfaces */
263     p_intf->p_sys_destroy( p_intf );
264     intf_ConsoleDestroy( p_intf->p_console );
265
266     /* Unload channels */
267     UnloadChannels( p_intf );
268
269     /* Destroy keymap */
270     p_cur = p_intf->p_keys;
271     while( p_cur != NULL)
272     {
273         p_next = p_cur->next;
274         free(p_cur);
275         p_cur = p_next;
276     }
277          
278         
279         /* Free structure */
280     free( p_intf );
281 }
282
283 /*****************************************************************************
284  * intf_SelectChannel: change channel
285  *****************************************************************************
286  * Kill existing input, if any, and try to open a new one, using an input
287  * configuration table.
288  *****************************************************************************/
289 int intf_SelectChannel( intf_thread_t * p_intf, int i_channel )
290 {
291     /* FIXME */
292 #if 0
293     intf_channel_t *    p_channel;                                /* channel */
294
295     /* Look for channel in array */
296     if( p_intf->p_channel != NULL )
297     {
298         for( p_channel = p_intf->p_channel; p_channel->i_channel != -1; p_channel++ )
299         {
300             if( p_channel->i_channel == i_channel )
301             {
302             /*
303              * Change channel
304              */
305
306             /* Kill existing input, if any */
307             if( p_intf->p_input != NULL )
308             {
309                 input_DestroyThread( p_intf->p_input, NULL );
310             }
311
312             intf_Msg("Channel %d: %s\n", i_channel, p_channel->psz_description );
313
314             /* Open a new input */
315             p_intf->p_input = input_CreateThread( p_channel->i_input_method, p_channel->psz_input_source,
316                                   p_channel->i_input_port, p_channel->i_input_vlan,
317                                   p_intf->p_vout, p_main->p_aout, NULL );
318             return( p_intf->p_input == NULL );
319             }
320         }
321     }
322
323     /* Channel does not exist */
324     intf_Msg("Channel %d does not exist\n", i_channel );
325 #endif
326     return( 1 );
327 }
328
329 /*****************************************************************************
330  * intf_AssignKey: assign standartkeys                                       *
331  *****************************************************************************
332  * This function fills in the associative array that links the key pressed   *
333  * and the key we use internally. Support one extra parameter.               *
334  ****************************************************************************/
335 void intf_AssignKey( intf_thread_t *p_intf, int r_key, int f_key, int param)
336 {
337     p_intf_key  p_cur =  p_intf->p_keys;
338     if( p_cur == NULL )
339     {
340         p_cur = (p_intf_key )(malloc ( sizeof( intf_key ) ) );
341         p_cur->received_key = r_key;
342         p_cur->forwarded.key = f_key;
343         p_cur->forwarded.param = param; 
344         p_cur->next = NULL;
345         p_intf->p_keys = p_cur;
346     } 
347     else 
348     {
349         while( p_cur->next != NULL && p_cur ->received_key != r_key)
350         {
351             p_cur = p_cur->next;
352         }
353         if( p_cur->next == NULL )
354         {   
355             p_cur->next  = ( p_intf_key )( malloc( sizeof( intf_key ) ) );
356             p_cur = p_cur->next;
357             p_cur->next = NULL;
358             p_cur->forwarded.param = param; 
359             p_cur->received_key = r_key;
360         }
361         p_cur->forwarded.key = f_key;
362     }        
363 }
364
365 /* Basic getKey function... */
366 keyparm intf_GetKey( intf_thread_t *p_intf, int r_key)
367 {   
368     keyparm reply;
369     
370     p_intf_key current = p_intf->p_keys;
371     while(current != NULL && current->received_key != r_key)
372     {    
373         current = current->next;
374     }
375     if(current == NULL)
376     {   /* didn't find any key in the array */ 
377         reply.key = INTF_KEY_UNKNOWN;
378         reply.param = 0;
379     }
380     else
381     {
382         reply.key = current->forwarded.key;
383         reply.param = current->forwarded.param;
384     }
385     return reply;
386 }
387
388 /*****************************************************************************
389 * intf_AssignNormalKeys: used for normal interfaces.
390 *****************************************************************************
391 * This function assign the basic key to the normal keys.
392 *****************************************************************************/
393
394 void intf_AssignNormalKeys( intf_thread_t *p_intf)
395 {
396     p_intf->p_intf_get_key = intf_GetKey;
397
398     intf_AssignKey( p_intf , 'Q', INTF_KEY_QUIT, 0);
399     intf_AssignKey( p_intf , 'q', INTF_KEY_QUIT, 0);
400     intf_AssignKey( p_intf ,  27, INTF_KEY_QUIT, 0);
401     intf_AssignKey( p_intf ,   3, INTF_KEY_QUIT, 0);
402     intf_AssignKey( p_intf , '0', INTF_KEY_SET_CHANNEL, 0);
403     intf_AssignKey( p_intf , '1', INTF_KEY_SET_CHANNEL, 1);
404     intf_AssignKey( p_intf , '2', INTF_KEY_SET_CHANNEL, 2);
405     intf_AssignKey( p_intf , '3', INTF_KEY_SET_CHANNEL, 3);
406     intf_AssignKey( p_intf , '4', INTF_KEY_SET_CHANNEL, 4);
407     intf_AssignKey( p_intf , '5', INTF_KEY_SET_CHANNEL, 5);
408     intf_AssignKey( p_intf , '6', INTF_KEY_SET_CHANNEL, 6);
409     intf_AssignKey( p_intf , '7', INTF_KEY_SET_CHANNEL, 7);
410     intf_AssignKey( p_intf , '8', INTF_KEY_SET_CHANNEL, 8);
411     intf_AssignKey( p_intf , '9', INTF_KEY_SET_CHANNEL, 9);
412     intf_AssignKey( p_intf , '0', INTF_KEY_SET_CHANNEL, 0);
413     intf_AssignKey( p_intf , '+', INTF_KEY_INC_VOLUME, 0);
414     intf_AssignKey( p_intf , '-', INTF_KEY_DEC_VOLUME, 0);
415     intf_AssignKey( p_intf , 'm', INTF_KEY_TOGGLE_VOLUME, 0);
416     intf_AssignKey( p_intf , 'M', INTF_KEY_TOGGLE_VOLUME, 0);
417     intf_AssignKey( p_intf , 'g', INTF_KEY_DEC_GAMMA, 0);
418     intf_AssignKey( p_intf , 'G', INTF_KEY_INC_GAMMA, 0);
419     intf_AssignKey( p_intf , 'c', INTF_KEY_TOGGLE_GRAYSCALE, 0);
420     intf_AssignKey( p_intf , ' ', INTF_KEY_TOGGLE_INTERFACE, 0);
421     intf_AssignKey( p_intf , 'i', INTF_KEY_TOGGLE_INFO, 0);
422     intf_AssignKey( p_intf , 's', INTF_KEY_TOGGLE_SCALING, 0);
423 }   
424
425 /*****************************************************************************
426  * intf_ProcessKey: process standard keys
427  *****************************************************************************
428  * This function will process standard keys and return non 0 if the key was
429  * unknown.
430  *****************************************************************************/
431 int intf_ProcessKey( intf_thread_t *p_intf, int g_key )
432 {
433     static int i_volbackup;
434     keyparm k_reply;
435     
436     k_reply = intf_GetKey( p_intf, g_key); 
437     
438     switch( k_reply.key )
439     {
440     case INTF_KEY_QUIT:                                                  /* quit order */
441         p_intf->b_die = 1;
442         break;
443     case INTF_KEY_SET_CHANNEL:
444         /* Change channel - return code is ignored since SelectChannel displays
445          * its own error messages */
446         intf_SelectChannel( p_intf, k_reply.param );
447         break;
448     case INTF_KEY_INC_VOLUME:                                                    /* volume + */
449         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol < VOLUME_MAX) )
450             p_main->p_aout->vol += VOLUME_STEP;
451         break;
452     case INTF_KEY_DEC_VOLUME:                                                    /* volume - */
453         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol > VOLUME_STEP) )
454             p_main->p_aout->vol -= VOLUME_STEP;
455         break;
456     case INTF_KEY_TOGGLE_VOLUME:                                                 /* toggle mute */
457         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol))
458         {
459             i_volbackup = p_main->p_aout->vol;
460             p_main->p_aout->vol = 0;
461         }
462         else if( (p_main->p_aout != NULL) && (!p_main->p_aout->vol))
463             p_main->p_aout->vol = i_volbackup;
464         break;
465     case INTF_KEY_DEC_GAMMA:                                                     /* gamma - */
466         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma > -INTF_GAMMA_LIMIT) )
467         {
468             vlc_mutex_lock( &p_intf->p_vout->change_lock );
469             p_intf->p_vout->f_gamma   -= INTF_GAMMA_STEP;
470             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
471             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
472         }
473         break;
474     case INTF_KEY_INC_GAMMA:                                                     /* gamma + */
475         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma < INTF_GAMMA_LIMIT) )
476         {
477             vlc_mutex_lock( &p_intf->p_vout->change_lock );
478             p_intf->p_vout->f_gamma   += INTF_GAMMA_STEP;
479             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
480             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
481         }
482         break;
483     case INTF_KEY_TOGGLE_GRAYSCALE:                                            /* toggle grayscale */
484         if( p_intf->p_vout != NULL )
485         {
486             vlc_mutex_lock( &p_intf->p_vout->change_lock );
487             p_intf->p_vout->b_grayscale = !p_intf->p_vout->b_grayscale;
488             p_intf->p_vout->i_changes  |= VOUT_GRAYSCALE_CHANGE;
489             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
490         }
491         break;
492     case INTF_KEY_TOGGLE_INTERFACE:                                            /* toggle interface */
493         if( p_intf->p_vout != NULL )
494         {
495             vlc_mutex_lock( &p_intf->p_vout->change_lock );
496             p_intf->p_vout->b_interface     = !p_intf->p_vout->b_interface;
497             p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
498             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
499         }
500         break;
501     case INTF_KEY_TOGGLE_INFO:                                                 /* toggle info */
502         if( p_intf->p_vout != NULL )
503         {
504             vlc_mutex_lock( &p_intf->p_vout->change_lock );
505             p_intf->p_vout->b_info     = !p_intf->p_vout->b_info;
506             p_intf->p_vout->i_changes |= VOUT_INFO_CHANGE;
507             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
508         }
509         break;
510     case INTF_KEY_TOGGLE_SCALING:                                              /* toggle scaling */
511         if( p_intf->p_vout != NULL )
512         {
513             vlc_mutex_lock( &p_intf->p_vout->change_lock );
514             p_intf->p_vout->b_scale    = !p_intf->p_vout->b_scale;
515             p_intf->p_vout->i_changes |= VOUT_SCALE_CHANGE;
516             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
517         }
518         break;
519    default:                                                   /* unknown key */
520         return( 1 );
521     }
522
523     return( 0 );
524 }
525
526 /* following functions are local */
527
528 /*****************************************************************************
529  * LoadChannels: load channels description from a file
530  *****************************************************************************
531  * This structe describes all interface-specific data of the main (interface)
532  * thread.
533  * Each line of the file is a semicolon separated list of the following
534  * fields :
535  *      integer         channel number
536  *      string          channel description
537  *      integer         input method (see input.h)
538  *      string          input source
539  *      integer         input port
540  *      integer         input vlan
541  * The last field must end with a semicolon.
542  * Comments and empty lines are not explicitely allowed, but lines with parsing
543  * errors are ignored without warning.
544  *****************************************************************************/
545 static int LoadChannels( intf_thread_t *p_intf, char *psz_filename )
546 {
547     FILE *              p_file;                                      /* file */
548     intf_channel_t *    p_channel;                        /* current channel */
549     char                psz_line[INTF_MAX_CMD_SIZE];          /* line buffer */
550     int                 i_index;                   /* channel or field index */
551
552     /* Set default value */
553     p_intf->p_channel = NULL;
554
555     /* FIXME: channels are disabled */
556     //return( 0 );
557
558     /* Open file */
559     p_file = fopen( psz_filename, "r" );
560     if( p_file == NULL )
561     {
562         intf_ErrMsg("error: can't open %s (%s)\n", psz_filename, strerror(errno));
563         return( 1 );
564     }
565
566     /* First pass: count number of lines */
567     for( i_index = 0; fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL; i_index++ )
568     {
569         ;
570     }
571
572     if( i_index != 0 )
573     {
574         /* Allocate array and rewind - some of the lines may be invalid, and the
575          * array will probably be larger than the actual number of channels, but
576          * it has no consequence. */
577         p_intf->p_channel = malloc( sizeof( intf_channel_t ) * i_index );
578         if( p_intf->p_channel == NULL )
579         {
580             intf_ErrMsg("error: %s\n", strerror(ENOMEM));
581             fclose( p_file );
582             return( 1 );
583         }
584         p_channel = p_intf->p_channel;
585         rewind( p_file );
586
587         /* Second pass: read channels descriptions */
588         while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
589         {
590             if( !ParseChannel( p_channel, psz_line ) )
591             {
592                 intf_DbgMsg( "channel [%d] %s : method %d (%s:%d vlan %d)\n",
593                          p_channel->i_channel, p_channel->psz_description,
594                          p_channel->i_input_method,
595                          p_channel->psz_input_source,
596                          p_channel->i_input_port, p_channel->i_input_vlan );
597                 p_channel++;
598             }
599         }
600
601         /* Add marker at the end of the array */
602         p_channel->i_channel = -1;
603     }
604
605     /* Close file */
606     fclose( p_file );
607     return( 0 );
608 }
609
610 /*****************************************************************************
611  * UnloadChannels: unload channels description
612  *****************************************************************************
613  * This function free all resources allocated by LoadChannels, if any.
614  *****************************************************************************/
615 static void UnloadChannels( intf_thread_t *p_intf )
616 {
617     int i_channel;                                          /* channel index */
618
619     if( p_intf->p_channel != NULL )
620     {
621         /* Free allocated strings */
622         for( i_channel = 0;
623              p_intf->p_channel[ i_channel ].i_channel != -1;
624              i_channel++ )
625         {
626             if( p_intf->p_channel[ i_channel ].psz_description != NULL )
627             {
628                 free( p_intf->p_channel[ i_channel ].psz_description );
629             }
630             if( p_intf->p_channel[ i_channel ].psz_input_source != NULL )
631             {
632                 free( p_intf->p_channel[ i_channel ].psz_input_source );
633             }
634         }
635
636         /* Free array */
637         free( p_intf->p_channel );
638         p_intf->p_channel = NULL;
639     }
640 }
641
642
643 /*****************************************************************************
644  * ParseChannel: parse a channel description line
645  *****************************************************************************
646  * See LoadChannels. This function return non 0 on parsing error.
647  *****************************************************************************/
648 static int ParseChannel( intf_channel_t *p_channel, char *psz_str )
649 {
650     char *      psz_index;                              /* current character */
651     char *      psz_end;                           /* end pointer for strtol */
652     int         i_field;                        /* field number, -1 on error */
653     int         i_field_length;             /* field length, for text fields */
654
655     /* Set some default fields */
656     p_channel->i_channel =              0;
657     p_channel->psz_description =        NULL;
658     p_channel->i_input_method =         0;
659     p_channel->psz_input_source =       NULL;
660     p_channel->i_input_port =           0;
661     p_channel->i_input_vlan =           0;
662
663     /* Parse string */
664     i_field = 0;
665     for( psz_index = psz_str; (i_field != -1) && (*psz_index != '\0'); psz_index++ )
666     {
667         if( *psz_index == ';' )
668         {
669             /* Mark end of field */
670             *psz_index = '\0';
671
672             /* Parse field */
673             switch( i_field++ )
674             {
675             case 0:                                        /* channel number */
676                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
677                 if( (*psz_str == '\0') || (*psz_end != '\0') )
678                 {
679                     i_field = -1;
680                 }
681                 break;
682             case 1:                                   /* channel description */
683                 i_field_length = strlen( psz_str );
684                 if( i_field_length != 0 )
685                 {
686                     p_channel->psz_description = malloc( i_field_length + 1 );
687                     if( p_channel->psz_description == NULL )
688                     {
689                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));
690                         i_field = -1;
691                     }
692                     else
693                     {
694                         strcpy( p_channel->psz_description, psz_str );
695                     }
696                 }
697                 break;
698             case 2:                                          /* input method */
699                 p_channel->i_input_method = strtol( psz_str, &psz_end, 0);
700                 if( (*psz_str == '\0') || (*psz_end != '\0') )
701                 {
702                     i_field = -1;
703                 }
704                 break;
705             case 3:                                          /* input source */
706                 i_field_length = strlen( psz_str );
707                 if( i_field_length != 0 )
708                 {
709                     p_channel->psz_input_source = malloc( i_field_length + 1 );
710                     if( p_channel->psz_input_source == NULL )
711                     {
712                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));
713                         i_field = -1;
714                     }
715                     else
716                     {
717                         strcpy( p_channel->psz_input_source, psz_str );
718                     }
719                 }
720                 break;
721             case 4:                                            /* input port */
722                 p_channel->i_input_port = strtol( psz_str, &psz_end, 0);
723                 if( (*psz_str == '\0') || (*psz_end != '\0') )
724                 {
725                     i_field = -1;
726                 }
727                 break;
728             case 5:                                            /* input vlan */
729                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
730                 if( (*psz_str == '\0') || (*psz_end != '\0') )
731                 {
732                     i_field = -1;
733                 }
734                 break;
735                 /* ... following fields are ignored */
736             }
737
738             /* Set new beginning of field */
739             psz_str = psz_index + 1;
740         }
741     }
742
743     /* At least the first three fields must be parsed sucessfully for function
744      * success. Other parsing errors are returned using i_field = -1. */
745     if( i_field < 3 )
746     {
747         /* Function fails. Free allocated strings */
748         if( p_channel->psz_description != NULL )
749         {
750             free( p_channel->psz_description );
751         }
752         if( p_channel->psz_input_source != NULL )
753         {
754             free( p_channel->psz_input_source );
755         }
756         return( 1 );
757     }
758
759     /* Return success */
760     return( 0 );
761 }