Revert "Add limits.h to fix Win32 and MacOS X compilation (sorry for the breakage)."
[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         if( i_len == PATH_MAX )
97             i_len--; /* truncate to prevent buffer overflow */
98 #if defined(WIN32) || defined(UNDER_CE)
99         if( (i_len > 0) && path[i_len] != '\\' )
100             path[i_len] = '\\';
101 #else
102         if( (i_len > 0) && path[i_len] != '/' )
103             path[i_len] = '/';
104 #endif
105         path[i_len+1] = '\0';
106         if( result == 0 || result == EOF )
107             goto error;
108         msg_Dbg( p_this, "osdmenu dir %s", &path[0] );
109
110         if( i_len == 0 )
111             p_menu = osd_MenuNew( p_menu, NULL, 0, 0 );
112         else
113             p_menu = osd_MenuNew( p_menu, &path[0], 0, 0 );
114
115         /* Peek for 'style' argument */
116         pos = ftell( fd );
117         if( pos < 0 )
118                 goto error;
119
120         result = fscanf(fd, "%24s %24s", &cmd[0], &action[0] );
121         if( result == 0 || result == EOF )
122             goto error;
123
124         msg_Dbg( p_this, "osdmenu %s %s", &cmd[0], &action[0] );
125         if( strncmp( &cmd[0], "style", 5 ) == 0 )
126         {
127             if( strncmp( &action[0], "default", 7) == 0 )
128             {
129                 p_menu->i_style = OSD_MENU_STYLE_SIMPLE;
130             }
131             else if( strncmp( &action[0], "concat", 6) == 0 )
132             {
133                 p_menu->i_style = OSD_MENU_STYLE_CONCAT;
134             }
135         }
136         else
137         {
138             result = fseek( fd, pos, SEEK_SET );
139             if( result < 0 )
140                 goto error;
141         }
142     }
143
144     if( !p_menu )
145         goto error;
146
147     /* read successive lines */
148     while( !feof( fd ) )
149     {
150         osd_state_t   *p_state_current = NULL; /* button state currently processed */
151         osd_state_t   *p_state_prev = NULL;    /* previous state processed button */
152
153         char cmd[25] = "";
154         char action[25] = "";
155         char state[25]  = "";
156         char file[256]  = "";
157         char path[PATH_MAX]  = "";
158         int  i_x = 0;
159         int  i_y = 0;
160
161         result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
162         if( result == 0 )
163             goto error;
164         if( strncmp( &cmd[0], "action", 6 ) != 0 )
165             break;
166         msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
167
168         p_prev = p_current;
169         p_current = osd_ButtonNew( &action[0], i_x, i_y );
170         if( !p_current )
171             goto error;
172
173         if( p_prev )
174             p_prev->p_next = p_current;
175         else
176             p_menu->p_button = p_current;
177         p_current->p_prev = p_prev;
178
179         /* parse all states */
180         while( !feof( fd ) )
181         {
182             char type[25] = "";
183
184             result = fscanf( fd, "\t%24s", &state[0] );
185             if( result == 0 )
186                 goto error;
187
188             /* FIXME: We only parse one level deep now */
189             if( strncmp( &state[0], "action", 6 ) == 0 )
190             {
191                 osd_button_t   *p_up = NULL;
192
193                 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
194                 if( result == 0 )
195                     goto error;
196                 /* create new button */
197                 p_up = osd_ButtonNew( &action[0], i_x, i_y );
198                 if( !p_up )
199                     goto error;
200                 /* Link to list */
201                 p_up->p_down = p_current;
202                 p_current->p_up = p_up;
203                 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
204                 /* Parse type state */
205                 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
206                 if( result == 0 )
207                     goto error;
208                 if( strncmp( &cmd[0], "type", 4 ) == 0 )
209                 {
210                     if( strncmp( &type[0], "volume", 6 ) == 0 )
211                     {
212                         p_menu->p_state->p_volume = p_up;
213                         msg_Dbg( p_this, " + type=%s", &type[0] );
214                     }
215                 }
216                 /* Parse range state */
217                 result = fscanf( fd, "\t%24s", &state[0] );
218                 if( result == 0 )
219                     goto error;
220                 /* Parse the range state */
221                 if( strncmp( &state[0], "range", 5 ) == 0 )
222                 {
223                     osd_state_t   *p_range_current = NULL; /* range state currently processed */
224                     osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
225                     int i_index = 0;
226
227                     p_up->b_range = true;
228
229                     result = fscanf( fd, "\t%24s", &action[0] );
230                     if( result == 0 )
231                         goto error;
232
233                     result = fscanf( fd, "\t%d", &i_index );
234                     if( result == 0 )
235                         goto error;
236
237                     msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s",
238                              &action[0], p_menu->psz_path, &file[0] );
239
240                     free( p_up->psz_action_down );
241                     p_up->psz_action_down = strdup( &action[0] );
242
243                     /* Parse range contstruction :
244                      * range <hotkey>
245                      *      <state1> <file1>
246                      *
247                      *      <stateN> <fileN>
248                      * end
249                      */
250                     while( !feof( fd ) )
251                     {
252                         result = fscanf( fd, "\t%255s", &file[0] );
253                         if( result == 0 )
254                             goto error;
255                         if( strncmp( &file[0], "end", 3 ) == 0 )
256                             break;
257
258                         p_range_prev = p_range_current;
259
260                         if( p_menu->psz_path )
261                         {
262                             size_t i_path_size = strlen( p_menu->psz_path );
263                             size_t i_file_size = strlen( &file[0] );
264
265                             if( (i_path_size + i_file_size >= PATH_MAX) ||
266                                 (i_path_size >= PATH_MAX) )
267                                 goto error;
268
269                             strncpy( &path[0], p_menu->psz_path, i_path_size );
270                             strncpy( &path[i_path_size], &file[0],
271                                      PATH_MAX - (i_path_size + i_file_size) );
272                             path[ i_path_size + i_file_size ] = '\0';
273
274                             p_range_current = osd_StateNew( p_menu, &path[0], "pressed" );
275                         }
276                         else /* absolute paths are used. */
277                             p_range_current = osd_StateNew( p_menu, &file[0], "pressed" );
278
279                         if( !p_range_current )
280                             goto error;
281
282                         if( !p_range_current->p_pic )
283                         {
284                             osd_StatesFree( p_menu, p_range_current );
285                             goto error;
286                         }
287
288                         p_range_current->i_x = i_x;
289                         p_range_current->i_y = i_y;
290
291                         /* increment the number of ranges for this button */
292                         p_up->i_ranges++;
293
294                         if( p_range_prev )
295                             p_range_prev->p_next = p_range_current;
296                         else
297                             p_up->p_states = p_range_current;
298                         p_range_current->p_prev = p_range_prev;
299
300                         msg_Dbg( p_this, "  |- range=%d, file=%s%s",
301                                  p_up->i_ranges,
302                                  p_menu->psz_path, &file[0] );
303                     }
304                     if( i_index > 0 )
305                     {
306                         osd_state_t *p_range = NULL;
307
308                         /* Find the default index for state range */
309                         p_range = p_up->p_states;
310                         while( (--i_index > 0) && p_range->p_next )
311                         {
312                             osd_state_t *p_temp = NULL;
313                             p_temp = p_range->p_next;
314                             p_range = p_temp;
315                         }
316                         p_up->p_current_state = p_range;
317                     }
318                     else p_up->p_current_state = p_up->p_states;
319
320                 }
321                 result = fscanf( fd, "\t%24s", &state[0] );
322                 if( result == 0 )
323                     goto error;
324                 if( strncmp( &state[0], "end", 3 ) != 0 )
325                     goto error;
326
327                 /* Continue at the beginning of the while() */
328                 continue;
329             }
330
331             /* Parse the range state */
332             if( strncmp( &state[0], "range", 5 ) == 0 )
333             {
334                 osd_state_t   *p_range_current = NULL; /* range state currently processed */
335                 osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
336                 int i_index = 0;
337
338                 p_current->b_range = true;
339
340                 result = fscanf( fd, "\t%24s", &action[0] );
341                 if( result == 0 )
342                     goto error;
343
344                 result = fscanf( fd, "\t%d", &i_index );
345                 if( result == 0 )
346                     goto error;
347
348                 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", 
349                          &action[0], p_menu->psz_path, &file[0] );
350                 free( p_current->psz_action_down );
351                 p_current->psz_action_down = strdup( &action[0] );
352
353                 /* Parse range contstruction :
354                  * range <hotkey>
355                  *      <state1> <file1>
356                  *
357                  *      <stateN> <fileN>
358                  * end
359                  */
360                 while( !feof( fd ) )
361                 {
362                     result = fscanf( fd, "\t%255s", &file[0] );
363                     if( result == 0 )
364                         goto error;
365                     if( strncmp( &file[0], "end", 3 ) == 0 )
366                         break;
367
368                     p_range_prev = p_range_current;
369
370                     if( p_menu->psz_path )
371                     {
372                         size_t i_path_size = strlen( p_menu->psz_path );
373                         size_t i_file_size = strlen( &file[0] );
374
375                         if( (i_path_size + i_file_size >= PATH_MAX) ||
376                             (i_path_size >= PATH_MAX) )
377                             goto error;
378
379                         strncpy( &path[0], p_menu->psz_path, i_path_size );
380                         strncpy( &path[i_path_size], &file[0],
381                                  PATH_MAX - (i_path_size + i_file_size) );
382                         path[ i_path_size + i_file_size ] = '\0';
383
384                         p_range_current = osd_StateNew( p_menu, &path[0], "pressed" );
385                     }
386                     else /* absolute paths are used. */
387                         p_range_current = osd_StateNew( p_menu, &file[0], "pressed" );
388
389                     if( !p_range_current )
390                         goto error;
391
392                     if( !p_range_current->p_pic )
393                     {
394                         osd_StatesFree( p_menu, p_range_current );
395                         goto error;
396                     }
397
398                     p_range_current->i_x = i_x;
399                     p_range_current->i_y = i_y;
400
401                     /* increment the number of ranges for this button */
402                     p_current->i_ranges++;
403
404                     if( p_range_prev )
405                         p_range_prev->p_next = p_range_current;
406                     else
407                         p_current->p_states = p_range_current;
408                     p_range_current->p_prev = p_range_prev;
409
410                     msg_Dbg( p_this, "  |- range=%d, file=%s%s",
411                              p_current->i_ranges,
412                              p_menu->psz_path, &file[0] );
413                 }
414                 if( i_index > 0 )
415                 {
416                     osd_state_t *p_range = NULL;
417
418                     /* Find the default index for state range */
419                     p_range = p_current->p_states;
420                     while( (--i_index > 0) && p_range->p_next )
421                     {
422                         osd_state_t *p_temp = NULL;
423                         p_temp = p_range->p_next;
424                         p_range = p_temp;
425                     }
426                     p_current->p_current_state = p_range;
427                 }
428                 else p_current->p_current_state = p_current->p_states;
429                 /* Continue at the beginning of the while() */
430                 continue;
431             }
432             if( strncmp( &state[0], "end", 3 ) == 0 )
433                 break;
434
435             result = fscanf( fd, "\t%255s", &file[0] );
436             if( result == 0 )
437                 goto error;
438
439             p_state_prev = p_state_current;
440
441             if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
442                 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
443                 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
444             {
445                 msg_Err( p_this, "invalid button state %s for button %s "
446                          "expected %u: unselect, select or pressed)",
447                          &state[0], &action[0], (unsigned)strlen(&state[0]));
448                 goto error;
449             }
450
451             if( p_menu->psz_path )
452             {
453                 size_t i_path_size = strlen( p_menu->psz_path );
454                 size_t i_file_size = strlen( &file[0] );
455
456                 if( (i_path_size + i_file_size >= PATH_MAX) ||
457                     (i_path_size >= PATH_MAX) )
458                     goto error;
459
460                 strncpy( &path[0], p_menu->psz_path, i_path_size );
461                 strncpy( &path[i_path_size], &file[0],
462                          PATH_MAX - (i_path_size + i_file_size) );
463                 path[ i_path_size + i_file_size ] = '\0';
464
465                 p_state_current = osd_StateNew( p_menu, &path[0], &state[0] );
466             }
467             else /* absolute paths are used. */
468                 p_state_current = osd_StateNew( p_menu, &file[0], &state[0] );
469
470             if( !p_state_current )
471                 goto error;
472
473             if( !p_state_current->p_pic )
474             {
475                 osd_StatesFree( p_menu, p_state_current );
476                 goto error;
477             }
478
479             p_state_current->i_x = i_x;
480             p_state_current->i_y = i_y;
481
482             if( p_state_prev )
483                 p_state_prev->p_next = p_state_current;
484             else
485                 p_current->p_states = p_state_current;
486             p_state_current->p_prev = p_state_prev;
487
488             msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0],
489                      p_menu->psz_path, &file[0] );
490         }
491         p_current->p_current_state = p_current->p_states;
492     }
493
494     /* Find the last button and store its pointer.
495      * The OSD menu behaves like a roundrobin list.
496      */
497     p_current = p_menu->p_button;
498     while( p_current && p_current->p_next )
499     {
500         osd_button_t *p_temp = NULL;
501         p_temp = p_current->p_next;
502         p_current = p_temp;
503     }
504     p_menu->p_last_button = p_current;
505     fclose( fd );
506     return VLC_SUCCESS;
507
508 error:
509     msg_Err( p_menu, "parsing file failed (returned %d)", result );
510     osd_MenuFree( p_menu );
511     fclose( fd );
512     return VLC_EGENERIC;
513 }