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