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