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