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