FIx potential segfault (CID 168)
[vlc.git] / modules / misc / osd / simple.c
1 /*****************************************************************************
2  * simple.c - The OSD Menu simple parser code.
3  *****************************************************************************
4  * Copyright (C) 2005-2008 M2X
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_vout.h>
34 #include <vlc_config.h>
35
36 #include <vlc_keys.h>
37 #include <vlc_image.h>
38 #include <vlc_osd.h>
39 #include <vlc_charset.h>
40
41 #include <limits.h>
42
43 #include "osd_menu.h"
44
45 int osd_parser_simpleOpen( vlc_object_t *p_this );
46
47 /*****************************************************************************
48  * Simple parser open function
49  *****************************************************************************/
50 int osd_parser_simpleOpen( vlc_object_t *p_this )
51 {
52     osd_menu_t     *p_menu = (osd_menu_t *) p_this;
53     osd_button_t   *p_current = NULL; /* button currently processed */
54     osd_button_t   *p_prev = NULL;    /* previous processed button */
55
56     FILE       *fd = NULL;
57     int        result = 0;
58
59     if( !p_menu ) return VLC_ENOOBJ;
60
61     msg_Dbg( p_this, "opening osdmenu definition file %s", p_menu->psz_file );
62     fd = utf8_fopen( p_menu->psz_file, "r" );
63     if( !fd )
64     {
65         msg_Err( p_this, "failed to open osdmenu definition file %s",
66                 p_menu->psz_file );
67         return VLC_EGENERIC;
68     }
69
70     /* Read first line */
71     if( !feof( fd ) )
72     {
73         char action[25] = "";
74         char cmd[25] = "";
75         char path[PATH_MAX] = "";
76         char *psz_path = NULL;
77         size_t i_len = 0;
78         long pos = 0;
79
80         result = fscanf(fd, "%24s %255s", &action[0], &path[0] );
81
82         /* override images path ? */
83         psz_path = config_GetPsz( p_this, "osdmenu-file-path" );
84         if( psz_path )
85         {
86             /* psz_path is not null and therefor &path[0] cannot be NULL
87              * it might be null terminated.
88              */
89             strncpy( &path[0], psz_path, PATH_MAX );
90             free( psz_path );
91             psz_path = NULL;
92         }
93         /* NULL terminate before asking the length of path[] */
94         path[PATH_MAX-1] = '\0';
95         i_len = strlen(&path[0]);
96         /* Protect against buffer overflow:
97          * max index is PATH_MAX-1 and we increment by 1 after
98          * so PATH_MAX-2 is the bigest we can have */
99         if( i_len > PATH_MAX - 2 )
100             i_len = PATH_MAX - 2;
101 #if defined(WIN32) || defined(UNDER_CE)
102         if( (i_len > 0) && path[i_len] != '\\' )
103             path[i_len] = '\\';
104 #else
105         if( (i_len > 0) && path[i_len] != '/' )
106             path[i_len] = '/';
107 #endif
108         path[i_len+1] = '\0';
109         if( result == 0 || result == EOF )
110             goto error;
111         msg_Dbg( p_this, "osdmenu dir %s", &path[0] );
112
113         if( i_len == 0 )
114             p_menu = osd_MenuNew( p_menu, NULL, 0, 0 );
115         else
116             p_menu = osd_MenuNew( p_menu, &path[0], 0, 0 );
117
118         /* Peek for 'style' argument */
119         pos = ftell( fd );
120         if( pos < 0 )
121             goto error;
122
123         result = fscanf(fd, "%24s %24s", &cmd[0], &action[0] );
124         if( result == 0 || result == EOF )
125             goto error;
126
127         msg_Dbg( p_this, "osdmenu %s %s", &cmd[0], &action[0] );
128         if( strncmp( &cmd[0], "style", 5 ) == 0 )
129         {
130             if( strncmp( &action[0], "default", 7) == 0 )
131             {
132                 p_menu->i_style = OSD_MENU_STYLE_SIMPLE;
133             }
134             else if( strncmp( &action[0], "concat", 6) == 0 )
135             {
136                 p_menu->i_style = OSD_MENU_STYLE_CONCAT;
137             }
138         }
139         else
140         {
141             result = fseek( fd, pos, SEEK_SET );
142             if( result < 0 )
143                 goto error;
144         }
145     }
146
147     if( !p_menu )
148         goto error;
149
150     /* read successive lines */
151     while( !feof( fd ) )
152     {
153         osd_state_t   *p_state_current = NULL; /* button state currently processed */
154         osd_state_t   *p_state_prev = NULL;    /* previous state processed button */
155
156         char cmd[25] = "";
157         char action[25] = "";
158         char state[25]  = "";
159         char file[256]  = "";
160         char path[PATH_MAX]  = "";
161         int  i_x = 0;
162         int  i_y = 0;
163
164         result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
165         if( result == 0 )
166             goto error;
167         if( strncmp( &cmd[0], "action", 6 ) != 0 )
168             break;
169         msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
170
171         p_prev = p_current;
172         p_current = osd_ButtonNew( &action[0], i_x, i_y );
173         if( !p_current )
174             goto error;
175
176         if( p_prev )
177             p_prev->p_next = p_current;
178         else
179             p_menu->p_button = p_current;
180         p_current->p_prev = p_prev;
181
182         /* parse all states */
183         while( !feof( fd ) )
184         {
185             char type[25] = "";
186
187             result = fscanf( fd, "\t%24s", &state[0] );
188             if( result == 0 )
189                 goto error;
190
191             /* FIXME: We only parse one level deep now */
192             if( strncmp( &state[0], "action", 6 ) == 0 )
193             {
194                 osd_button_t   *p_up = NULL;
195
196                 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
197                 if( result == 0 )
198                     goto error;
199                 /* create new button */
200                 p_up = osd_ButtonNew( &action[0], i_x, i_y );
201                 if( !p_up )
202                     goto error;
203                 /* Link to list */
204                 p_up->p_down = p_current;
205                 p_current->p_up = p_up;
206                 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
207                 /* Parse type state */
208                 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
209                 if( result == 0 )
210                     goto error;
211                 if( strncmp( &cmd[0], "type", 4 ) == 0 )
212                 {
213                     if( strncmp( &type[0], "volume", 6 ) == 0 )
214                     {
215                         p_menu->p_state->p_volume = p_up;
216                         msg_Dbg( p_this, " + type=%s", &type[0] );
217                     }
218                 }
219                 /* Parse range state */
220                 result = fscanf( fd, "\t%24s", &state[0] );
221                 if( result == 0 )
222                     goto error;
223                 /* Parse the range state */
224                 if( strncmp( &state[0], "range", 5 ) == 0 )
225                 {
226                     osd_state_t   *p_range_current = NULL; /* range state currently processed */
227                     osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
228                     int i_index = 0;
229
230                     p_up->b_range = true;
231
232                     result = fscanf( fd, "\t%24s", &action[0] );
233                     if( result == 0 )
234                         goto error;
235
236                     result = fscanf( fd, "\t%d", &i_index );
237                     if( result == 0 )
238                         goto error;
239
240                     msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s",
241                              &action[0], p_menu->psz_path, &file[0] );
242
243                     free( p_up->psz_action_down );
244                     p_up->psz_action_down = strdup( &action[0] );
245
246                     /* Parse range contstruction :
247                      * range <hotkey>
248                      *      <state1> <file1>
249                      *
250                      *      <stateN> <fileN>
251                      * end
252                      */
253                     while( !feof( fd ) )
254                     {
255                         result = fscanf( fd, "\t%255s", &file[0] );
256                         if( result == 0 )
257                             goto error;
258                         if( strncmp( &file[0], "end", 3 ) == 0 )
259                             break;
260
261                         p_range_prev = p_range_current;
262
263                         if( p_menu->psz_path )
264                         {
265                             size_t i_path_size = strlen( p_menu->psz_path );
266                             size_t i_file_size = strlen( &file[0] );
267
268                             if( (i_path_size + i_file_size >= PATH_MAX) ||
269                                 (i_path_size >= PATH_MAX) )
270                                 goto error;
271
272                             strncpy( &path[0], p_menu->psz_path, i_path_size );
273                             strncpy( &path[i_path_size], &file[0],
274                                      PATH_MAX - (i_path_size + i_file_size) );
275                             path[ i_path_size + i_file_size ] = '\0';
276
277                             p_range_current = osd_StateNew( p_menu, &path[0], "pressed" );
278                         }
279                         else /* absolute paths are used. */
280                             p_range_current = osd_StateNew( p_menu, &file[0], "pressed" );
281
282                         if( !p_range_current )
283                             goto error;
284
285                         if( !p_range_current->p_pic )
286                         {
287                             osd_StatesFree( p_menu, p_range_current );
288                             goto error;
289                         }
290
291                         p_range_current->i_x = i_x;
292                         p_range_current->i_y = i_y;
293
294                         /* increment the number of ranges for this button */
295                         p_up->i_ranges++;
296
297                         if( p_range_prev )
298                             p_range_prev->p_next = p_range_current;
299                         else
300                             p_up->p_states = p_range_current;
301                         p_range_current->p_prev = p_range_prev;
302
303                         msg_Dbg( p_this, "  |- range=%d, file=%s%s",
304                                  p_up->i_ranges,
305                                  p_menu->psz_path, &file[0] );
306                     }
307                     if( i_index > 0 )
308                     {
309                         osd_state_t *p_range = NULL;
310
311                         /* Find the default index for state range */
312                         p_range = p_up->p_states;
313                         while( (--i_index > 0) && p_range->p_next )
314                         {
315                             osd_state_t *p_temp = NULL;
316                             p_temp = p_range->p_next;
317                             p_range = p_temp;
318                         }
319                         p_up->p_current_state = p_range;
320                     }
321                     else p_up->p_current_state = p_up->p_states;
322
323                 }
324                 result = fscanf( fd, "\t%24s", &state[0] );
325                 if( result == 0 )
326                     goto error;
327                 if( strncmp( &state[0], "end", 3 ) != 0 )
328                     goto error;
329
330                 /* Continue at the beginning of the while() */
331                 continue;
332             }
333
334             /* Parse the range state */
335             if( strncmp( &state[0], "range", 5 ) == 0 )
336             {
337                 osd_state_t   *p_range_current = NULL; /* range state currently processed */
338                 osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
339                 int i_index = 0;
340
341                 p_current->b_range = true;
342
343                 result = fscanf( fd, "\t%24s", &action[0] );
344                 if( result == 0 )
345                     goto error;
346
347                 result = fscanf( fd, "\t%d", &i_index );
348                 if( result == 0 )
349                     goto error;
350
351                 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", 
352                          &action[0], p_menu->psz_path, &file[0] );
353                 free( p_current->psz_action_down );
354                 p_current->psz_action_down = strdup( &action[0] );
355
356                 /* Parse range contstruction :
357                  * range <hotkey>
358                  *      <state1> <file1>
359                  *
360                  *      <stateN> <fileN>
361                  * end
362                  */
363                 while( !feof( fd ) )
364                 {
365                     result = fscanf( fd, "\t%255s", &file[0] );
366                     if( result == 0 )
367                         goto error;
368                     if( strncmp( &file[0], "end", 3 ) == 0 )
369                         break;
370
371                     p_range_prev = p_range_current;
372
373                     if( p_menu->psz_path )
374                     {
375                         size_t i_path_size = strlen( p_menu->psz_path );
376                         size_t i_file_size = strlen( &file[0] );
377
378                         if( (i_path_size + i_file_size >= PATH_MAX) ||
379                             (i_path_size >= PATH_MAX) )
380                             goto error;
381
382                         strncpy( &path[0], p_menu->psz_path, i_path_size );
383                         strncpy( &path[i_path_size], &file[0],
384                                  PATH_MAX - (i_path_size + i_file_size) );
385                         path[ i_path_size + i_file_size ] = '\0';
386
387                         p_range_current = osd_StateNew( p_menu, &path[0], "pressed" );
388                     }
389                     else /* absolute paths are used. */
390                         p_range_current = osd_StateNew( p_menu, &file[0], "pressed" );
391
392                     if( !p_range_current )
393                         goto error;
394
395                     if( !p_range_current->p_pic )
396                     {
397                         osd_StatesFree( p_menu, p_range_current );
398                         goto error;
399                     }
400
401                     p_range_current->i_x = i_x;
402                     p_range_current->i_y = i_y;
403
404                     /* increment the number of ranges for this button */
405                     p_current->i_ranges++;
406
407                     if( p_range_prev )
408                         p_range_prev->p_next = p_range_current;
409                     else
410                         p_current->p_states = p_range_current;
411                     p_range_current->p_prev = p_range_prev;
412
413                     msg_Dbg( p_this, "  |- range=%d, file=%s%s",
414                              p_current->i_ranges,
415                              p_menu->psz_path, &file[0] );
416                 }
417                 if( i_index > 0 )
418                 {
419                     osd_state_t *p_range = NULL;
420
421                     /* Find the default index for state range */
422                     p_range = p_current->p_states;
423                     while( (--i_index > 0) && p_range->p_next )
424                     {
425                         osd_state_t *p_temp = NULL;
426                         p_temp = p_range->p_next;
427                         p_range = p_temp;
428                     }
429                     p_current->p_current_state = p_range;
430                 }
431                 else p_current->p_current_state = p_current->p_states;
432                 /* Continue at the beginning of the while() */
433                 continue;
434             }
435             if( strncmp( &state[0], "end", 3 ) == 0 )
436                 break;
437
438             result = fscanf( fd, "\t%255s", &file[0] );
439             if( result == 0 )
440                 goto error;
441
442             p_state_prev = p_state_current;
443
444             if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
445                 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
446                 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
447             {
448                 msg_Err( p_this, "invalid button state %s for button %s "
449                          "expected %u: unselect, select or pressed)",
450                          &state[0], &action[0], (unsigned)strlen(&state[0]));
451                 goto error;
452             }
453
454             if( p_menu->psz_path )
455             {
456                 size_t i_path_size = strlen( p_menu->psz_path );
457                 size_t i_file_size = strlen( &file[0] );
458
459                 if( (i_path_size + i_file_size >= PATH_MAX) ||
460                     (i_path_size >= PATH_MAX) )
461                     goto error;
462
463                 strncpy( &path[0], p_menu->psz_path, i_path_size );
464                 strncpy( &path[i_path_size], &file[0],
465                          PATH_MAX - (i_path_size + i_file_size) );
466                 path[ i_path_size + i_file_size ] = '\0';
467
468                 p_state_current = osd_StateNew( p_menu, &path[0], &state[0] );
469             }
470             else /* absolute paths are used. */
471                 p_state_current = osd_StateNew( p_menu, &file[0], &state[0] );
472
473             if( !p_state_current )
474                 goto error;
475
476             if( !p_state_current->p_pic )
477             {
478                 osd_StatesFree( p_menu, p_state_current );
479                 goto error;
480             }
481
482             p_state_current->i_x = i_x;
483             p_state_current->i_y = i_y;
484
485             if( p_state_prev )
486                 p_state_prev->p_next = p_state_current;
487             else
488                 p_current->p_states = p_state_current;
489             p_state_current->p_prev = p_state_prev;
490
491             msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0],
492                      p_menu->psz_path, &file[0] );
493         }
494         p_current->p_current_state = p_current->p_states;
495     }
496
497     /* Find the last button and store its pointer.
498      * The OSD menu behaves like a roundrobin list.
499      */
500     p_current = p_menu->p_button;
501     while( p_current && p_current->p_next )
502     {
503         osd_button_t *p_temp = NULL;
504         p_temp = p_current->p_next;
505         p_current = p_temp;
506     }
507     p_menu->p_last_button = p_current;
508     fclose( fd );
509     return VLC_SUCCESS;
510
511 error:
512     msg_Err( p_menu, "parsing file failed (returned %d)", result );
513     if( p_menu )
514         osd_MenuFree( p_menu );
515     fclose( fd );
516     return VLC_EGENERIC;
517 }