]> git.sesse.net Git - vlc/blob - src/interface/interface.c
A few new things:
[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 ,  27, 'Q');
350      intf_AssignKey( p_intf ,   3, 'Q');
351      intf_AssignKey( p_intf , '0', '0');
352      intf_AssignKey( p_intf , '1', '1');
353      intf_AssignKey( p_intf , '2', '2');
354      intf_AssignKey( p_intf , '3', '3');
355      intf_AssignKey( p_intf , '4', '4');
356      intf_AssignKey( p_intf , '5', '5');
357      intf_AssignKey( p_intf , '6', '6');
358      intf_AssignKey( p_intf , '7', '7');
359      intf_AssignKey( p_intf , '8', '8');
360      intf_AssignKey( p_intf , '9', '9');
361      intf_AssignKey( p_intf , '0', '0');
362      intf_AssignKey( p_intf , '+', '+');
363      intf_AssignKey( p_intf , '-', '-');
364      intf_AssignKey( p_intf , 'm', 'M');
365      intf_AssignKey( p_intf , 'M', 'M');
366      intf_AssignKey( p_intf , 'g', 'g');
367      intf_AssignKey( p_intf , 'G', 'G');
368      intf_AssignKey( p_intf , 'c', 'c');
369      intf_AssignKey( p_intf , ' ', ' ');
370      intf_AssignKey( p_intf , 'i', 'i');
371      intf_AssignKey( p_intf , 's', 's');
372 }   
373
374 /*****************************************************************************
375  * intf_ProcessKey: process standard keys
376  *****************************************************************************
377  * This function will process standard keys and return non 0 if the key was
378  * unknown.
379  *****************************************************************************/
380 int intf_ProcessKey( intf_thread_t *p_intf, int g_key )
381 {
382     static int i_volbackup;
383     int i_key;
384     
385     i_key = intf_getKey( p_intf, g_key); 
386     switch( i_key )
387     {
388     case 'Q':                                                  /* quit order */
389         p_intf->b_die = 1;
390         break;
391     case '0':                                               /* source change */
392     case '1':
393     case '2':
394     case '3':
395     case '4':
396     case '5':
397     case '6':
398     case '7':
399     case '8':
400     case '9':
401         /* Change channel - return code is ignored since SelectChannel displays
402          * its own error messages */
403         intf_SelectChannel( p_intf, i_key - '0' );
404         break;
405     case '+':                                                    /* volume + */
406         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol < VOLMAX) )
407             p_main->p_aout->vol += VOLSTEP;
408         break;
409     case '-':                                                    /* volume - */
410         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol > VOLSTEP) )
411             p_main->p_aout->vol -= VOLSTEP;
412         break;
413     case 'M':                                                 /* toggle mute */
414         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol))
415         {
416             i_volbackup = p_main->p_aout->vol;
417             p_main->p_aout->vol = 0;
418         }
419         else if( (p_main->p_aout != NULL) && (!p_main->p_aout->vol))
420             p_main->p_aout->vol = i_volbackup;
421         break;
422     case 'g':                                                     /* gamma - */
423         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma > -INTF_GAMMA_LIMIT) )
424         {
425             vlc_mutex_lock( &p_intf->p_vout->change_lock );
426             p_intf->p_vout->f_gamma   -= INTF_GAMMA_STEP;
427             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
428             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
429         }
430         break;
431     case 'G':                                                     /* gamma + */
432         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma < INTF_GAMMA_LIMIT) )
433         {
434             vlc_mutex_lock( &p_intf->p_vout->change_lock );
435             p_intf->p_vout->f_gamma   += INTF_GAMMA_STEP;
436             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
437             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
438         }
439         break;
440     case 'c':                                            /* toggle grayscale */
441         if( p_intf->p_vout != NULL )
442         {
443             vlc_mutex_lock( &p_intf->p_vout->change_lock );
444             p_intf->p_vout->b_grayscale = !p_intf->p_vout->b_grayscale;
445             p_intf->p_vout->i_changes  |= VOUT_GRAYSCALE_CHANGE;
446             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
447         }
448         break;
449     case ' ':                                            /* toggle interface */
450         if( p_intf->p_vout != NULL )
451         {
452             vlc_mutex_lock( &p_intf->p_vout->change_lock );
453             p_intf->p_vout->b_interface     = !p_intf->p_vout->b_interface;
454             p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
455             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
456         }
457         break;
458     case 'i':                                                 /* toggle info */
459         if( p_intf->p_vout != NULL )
460         {
461             vlc_mutex_lock( &p_intf->p_vout->change_lock );
462             p_intf->p_vout->b_info     = !p_intf->p_vout->b_info;
463             p_intf->p_vout->i_changes |= VOUT_INFO_CHANGE;
464             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
465         }
466         break;
467     case 's':                                              /* toggle scaling */
468         if( p_intf->p_vout != NULL )
469         {
470             vlc_mutex_lock( &p_intf->p_vout->change_lock );
471             p_intf->p_vout->b_scale    = !p_intf->p_vout->b_scale;
472             p_intf->p_vout->i_changes |= VOUT_SCALE_CHANGE;
473             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
474         }
475         break;
476    default:                                                   /* unknown key */
477         return( 1 );
478     }
479
480     return( 0 );
481 }
482
483 /* following functions are local */
484
485 /*****************************************************************************
486  * LoadChannels: load channels description from a file
487  *****************************************************************************
488  * This structe describes all interface-specific data of the main (interface)
489  * thread.
490  * Each line of the file is a semicolon separated list of the following
491  * fields :
492  *      integer         channel number
493  *      string          channel description
494  *      integer         input method (see input.h)
495  *      string          input source
496  *      integer         input port
497  *      integer         input vlan
498  * The last field must end with a semicolon.
499  * Comments and empty lines are not explicitely allowed, but lines with parsing
500  * errors are ignored without warning.
501  *****************************************************************************/
502 static int LoadChannels( intf_thread_t *p_intf, char *psz_filename )
503 {
504     FILE *              p_file;                                      /* file */
505     intf_channel_t *    p_channel;                        /* current channel */
506     char                psz_line[INTF_MAX_CMD_SIZE];          /* line buffer */
507     int                 i_index;                   /* channel or field index */
508
509     /* Set default value */
510     p_intf->p_channel = NULL;
511
512     /* Open file */
513     p_file = fopen( psz_filename, "r" );
514     if( p_file == NULL )
515     {
516         intf_ErrMsg("error: can't open %s (%s)\n", psz_filename, strerror(errno));
517         return( 1 );
518     }
519
520     /* First pass: count number of lines */
521     for( i_index = 0; fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL; i_index++ )
522     {
523         ;
524     }
525
526     if( i_index != 0 )
527     {
528         /* Allocate array and rewind - some of the lines may be invalid, and the
529          * array will probably be larger than the actual number of channels, but
530          * it has no consequence. */
531         p_intf->p_channel = malloc( sizeof( intf_channel_t ) * i_index );
532         if( p_intf->p_channel == NULL )
533         {
534             intf_ErrMsg("error: %s\n", strerror(ENOMEM));
535             fclose( p_file );
536             return( 1 );
537         }
538         p_channel = p_intf->p_channel;
539         rewind( p_file );
540
541         /* Second pass: read channels descriptions */
542         while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
543         {
544             if( !ParseChannel( p_channel, psz_line ) )
545             {
546                 intf_DbgMsg( "channel [%d] %s : method %d (%s:%d vlan %d)\n",
547                          p_channel->i_channel, p_channel->psz_description,
548                          p_channel->i_input_method,
549                          p_channel->psz_input_source,
550                          p_channel->i_input_port, p_channel->i_input_vlan );
551                 p_channel++;
552             }
553         }
554
555         /* Add marker at the end of the array */
556         p_channel->i_channel = -1;
557     }
558
559     /* Close file */
560     fclose( p_file );
561     return( 0 );
562 }
563
564 /*****************************************************************************
565  * UnloadChannels: unload channels description
566  *****************************************************************************
567  * This function free all resources allocated by LoadChannels, if any.
568  *****************************************************************************/
569 static void UnloadChannels( intf_thread_t *p_intf )
570 {
571     int i_channel;                                          /* channel index */
572
573     if( p_intf->p_channel != NULL )
574     {
575         /* Free allocated strings */
576         for( i_channel = 0;
577              p_intf->p_channel[ i_channel ].i_channel != -1;
578              i_channel++ )
579         {
580             if( p_intf->p_channel[ i_channel ].psz_description != NULL )
581             {
582                 free( p_intf->p_channel[ i_channel ].psz_description );
583             }
584             if( p_intf->p_channel[ i_channel ].psz_input_source != NULL )
585             {
586                 free( p_intf->p_channel[ i_channel ].psz_input_source );
587             }
588         }
589
590         /* Free array */
591         free( p_intf->p_channel );
592         p_intf->p_channel = NULL;
593     }
594 }
595
596
597 /*****************************************************************************
598  * ParseChannel: parse a channel description line
599  *****************************************************************************
600  * See LoadChannels. This function return non 0 on parsing error.
601  *****************************************************************************/
602 static int ParseChannel( intf_channel_t *p_channel, char *psz_str )
603 {
604     char *      psz_index;                              /* current character */
605     char *      psz_end;                           /* end pointer for strtol */
606     int         i_field;                        /* field number, -1 on error */
607     int         i_field_length;             /* field length, for text fields */
608
609     /* Set some default fields */
610     p_channel->i_channel =              0;
611     p_channel->psz_description =        NULL;
612     p_channel->i_input_method =         0;
613     p_channel->psz_input_source =       NULL;
614     p_channel->i_input_port =           0;
615     p_channel->i_input_vlan =           0;
616
617     /* Parse string */
618     i_field = 0;
619     for( psz_index = psz_str; (i_field != -1) && (*psz_index != '\0'); psz_index++ )
620     {
621         if( *psz_index == ';' )
622         {
623             /* Mark end of field */
624             *psz_index = '\0';
625
626             /* Parse field */
627             switch( i_field++ )
628             {
629             case 0:                                        /* channel number */
630                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
631                 if( (*psz_str == '\0') || (*psz_end != '\0') )
632                 {
633                     i_field = -1;
634                 }
635                 break;
636             case 1:                                   /* channel description */
637                 i_field_length = strlen( psz_str );
638                 if( i_field_length != 0 )
639                 {
640                     p_channel->psz_description = malloc( i_field_length + 1 );
641                     if( p_channel->psz_description == NULL )
642                     {
643                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));
644                         i_field = -1;
645                     }
646                     else
647                     {
648                         strcpy( p_channel->psz_description, psz_str );
649                     }
650                 }
651                 break;
652             case 2:                                          /* input method */
653                 p_channel->i_input_method = strtol( psz_str, &psz_end, 0);
654                 if( (*psz_str == '\0') || (*psz_end != '\0') )
655                 {
656                     i_field = -1;
657                 }
658                 break;
659             case 3:                                          /* input source */
660                 i_field_length = strlen( psz_str );
661                 if( i_field_length != 0 )
662                 {
663                     p_channel->psz_input_source = malloc( i_field_length + 1 );
664                     if( p_channel->psz_input_source == NULL )
665                     {
666                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));
667                         i_field = -1;
668                     }
669                     else
670                     {
671                         strcpy( p_channel->psz_input_source, psz_str );
672                     }
673                 }
674                 break;
675             case 4:                                            /* input port */
676                 p_channel->i_input_port = strtol( psz_str, &psz_end, 0);
677                 if( (*psz_str == '\0') || (*psz_end != '\0') )
678                 {
679                     i_field = -1;
680                 }
681                 break;
682             case 5:                                            /* input vlan */
683                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
684                 if( (*psz_str == '\0') || (*psz_end != '\0') )
685                 {
686                     i_field = -1;
687                 }
688                 break;
689                 /* ... following fields are ignored */
690             }
691
692             /* Set new beginning of field */
693             psz_str = psz_index + 1;
694         }
695     }
696
697     /* At least the first three fields must be parsed sucessfully for function
698      * success. Other parsing errors are returned using i_field = -1. */
699     if( i_field < 3 )
700     {
701         /* Function fails. Free allocated strings */
702         if( p_channel->psz_description != NULL )
703         {
704             free( p_channel->psz_description );
705         }
706         if( p_channel->psz_input_source != NULL )
707         {
708             free( p_channel->psz_input_source );
709         }
710         return( 1 );
711     }
712
713     /* Return success */
714     return( 0 );
715 }