Check if the file could be modified, error if not.
[Rockbox.git] / apps / filetypes.c
blobb76ea27a8c9d9fd5d1b9b19f5053afc445fe0279
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * $Id$
10 * Copyright (C) 2004 Henrik Backe
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
25 #include "sprintf.h"
26 #include "settings.h"
27 #include "debug.h"
28 #include "lang.h"
29 #include "language.h"
30 #include "kernel.h"
31 #include "plugin.h"
32 #include "filetypes.h"
33 #include "screens.h"
34 #include "icons.h"
35 #include "dir.h"
36 #include "file.h"
37 #include "icons.h"
39 /* max plugin name size without extensions and path */
40 #define MAX_PLUGIN_LENGTH 32
42 /* max filetypes (plugins & icons stored here) */
43 #define MAX_FILETYPES 32
45 /* max exttypes (extensions stored here) */
46 #define MAX_EXTTYPES 32
48 /* string buffer length */
49 #define STRING_BUFFER_SIZE 512
51 /* number of bytes for the binary icon */
52 #define ICON_LENGTH 6
54 /* mask for dynamic filetype info in attribute */
55 #define FILETYPES_MASK 0xFF00
57 /* filenames */
58 #define ROCK_EXTENSION ".rock"
59 #define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config"
60 #define VIEWERS_DIR ROCKBOX_DIR "/viewers"
62 /* global variables */
63 static int cnt_filetypes;
64 static int cnt_exttypes;
65 static struct ext_type exttypes [MAX_EXTTYPES];
66 static struct file_type filetypes[MAX_FILETYPES];
67 static int first_soft_exttype;
68 static int first_soft_filetype;
69 static char* next_free_string;
70 static char plugin_name[sizeof(VIEWERS_DIR) + 7 + MAX_PLUGIN_LENGTH];
71 static char string_buffer[STRING_BUFFER_SIZE];
73 /* prototypes */
74 #ifdef HAVE_LCD_BITMAP
75 static char* string2icon(const char*);
76 static int add_plugin(char*,char*);
77 #else
78 static int add_plugin(char*);
79 #endif
80 static char* get_string(const char*);
81 static int find_attr_index(int);
82 static bool read_config(const char*);
83 static void rm_whitespaces(char*);
84 static void scan_plugins(void);
86 /* initialize dynamic filetypes (called at boot from tree.c) */
87 void filetype_init(void)
89 int cnt,i,ix;
90 const struct filetype* ftypes;
92 memset(exttypes,0,sizeof(exttypes));
93 memset(filetypes,0,sizeof(filetypes));
94 next_free_string=string_buffer;
96 /* The special filetype folder must always be stored at index 0 */
97 #ifdef HAVE_LCD_BITMAP
98 if (!filetypes[0].icon)
99 filetypes[0].icon = bitmap_icons_6x8[Folder];
100 #else
101 if (!filetypes[0].icon)
102 filetypes[0].icon = Folder;
103 for (i=1; i < MAX_FILETYPES; i++)
104 filetypes[i].icon = -1;
105 #endif
107 /* register hardcoded filetypes */
108 tree_get_filetypes(&ftypes, &cnt);
109 cnt_exttypes=0;
110 cnt_filetypes=0;
112 for (i = 0; i < cnt ; i++)
114 ix = ((ftypes[i].tree_attr & FILETYPES_MASK) >> 8);
115 if (ix < MAX_FILETYPES && i < MAX_EXTTYPES)
117 #ifdef HAVE_LCD_BITMAP
118 if (filetypes[ix].icon == NULL)
119 filetypes[ix].icon=bitmap_icons_6x8[ftypes[i].icon];
120 #else
121 if (filetypes[ix].icon == -1)
122 filetypes[ix].icon=ftypes[i].icon;
123 #endif
124 if (ix > cnt_filetypes)
125 cnt_filetypes=ix;
126 exttypes[cnt_exttypes].type=&filetypes[ix];
127 exttypes[cnt_exttypes].extension=ftypes[i].extension;
128 cnt_exttypes++;
131 first_soft_exttype=cnt_exttypes;
132 cnt_filetypes++;
133 first_soft_filetype=cnt_filetypes;
135 /* register dynamic filetypes */
136 read_config(VIEWERS_CONFIG);
137 scan_plugins();
140 /* get icon */
141 #ifdef HAVE_LCD_BITMAP
142 const char* filetype_get_icon(int attr)
143 #else
144 int filetype_get_icon(int attr)
145 #endif
147 int ix;
149 ix = find_attr_index(attr);
151 if (ix < 0)
153 #ifdef HAVE_LCD_BITMAP
154 return NULL;
155 #else
156 return -1;
157 #endif
159 else
161 return filetypes[ix].icon;
165 /* get plugin */
166 char* filetype_get_plugin(const struct entry* file)
168 int ix;
170 ix=find_attr_index(file->attr);
172 if (ix < 0)
174 return NULL;
177 if ((filetypes[ix].plugin == NULL) ||
178 (strlen(filetypes[ix].plugin) > MAX_PLUGIN_LENGTH))
179 return NULL;
181 snprintf(plugin_name, sizeof(plugin_name),
182 VIEWERS_DIR "/%s.rock",filetypes[ix].plugin);
184 return plugin_name;
187 /* check if filetype is supported */
188 bool filetype_supported(int attr)
190 int ix;
192 ix=find_attr_index(attr);
194 /* hard filetypes and soft filetypes with plugins is supported */
195 if (ix > 0)
196 if (filetypes[ix].plugin || ix < first_soft_filetype)
197 return true;
199 return false;
202 /* get the "dynamic" attribute for an extension */
203 int filetype_get_attr(const char* name)
205 int i;
207 for (i=0; i < cnt_exttypes; i++)
209 if (exttypes[i].extension)
211 if (!strcasecmp(&name[strlen(name)-
212 strlen(exttypes[i].extension)],
213 exttypes[i].extension))
215 return ((((unsigned long)exttypes[i].type -
216 (unsigned long)&filetypes[0]) /
217 sizeof(struct file_type)) << 8);
222 return 0;
225 /* fill a menu list with viewers (used in onplay.c) */
226 int filetype_load_menu(struct menu_item* menu,int max_items)
228 int i;
229 int cnt=0;
231 for (i=0; i < cnt_filetypes; i++)
233 if (filetypes[i].plugin)
235 menu[cnt].desc = filetypes[i].plugin;
236 cnt++;
237 if (cnt == max_items)
238 break;
241 return cnt;
244 /* start a plugin with an argument (called from onplay.c) */
245 int filetype_load_plugin(const char* plugin, char* file)
247 snprintf(plugin_name,sizeof(plugin_name),"%s/%s.rock",
248 VIEWERS_DIR,plugin);
249 return plugin_load(plugin_name,file);
252 /* get index to filetypes[] from the file attribute */
253 static int find_attr_index(int attr)
255 int ix;
256 ix = ((attr & FILETYPES_MASK) >> 8);
258 if ((attr & ATTR_DIRECTORY)==ATTR_DIRECTORY)
260 ix=0;
262 else
264 if (ix==0)
265 ix=-1;
266 if (ix > cnt_filetypes)
267 ix=-1;
268 else
269 if ((filetypes[ix].plugin == NULL) &&
270 #ifdef HAVE_LCD_BITMAP
271 (filetypes[ix].icon == NULL)
272 #else
273 (filetypes[ix].icon == -1)
274 #endif
276 ix=-1;
279 return ix;
282 /* scan the plugin directory and register filetypes */
283 static void scan_plugins(void)
285 DIR *dir;
286 struct dirent *entry;
287 char* cp;
288 char* dot;
289 char* dash;
290 int ix;
291 int i;
292 bool found;
294 dir = opendir(VIEWERS_DIR);
295 if(!dir)
296 return;
298 while (true)
300 /* exttypes[] full, bail out */
301 if (cnt_exttypes >= MAX_EXTTYPES)
303 splash(HZ,true,str(LANG_FILETYPES_EXTENSION_FULL));
304 break;
307 /* filetypes[] full, bail out */
308 if (cnt_filetypes >= MAX_FILETYPES)
310 splash(HZ,true,str(LANG_FILETYPES_FULL));
311 break;
314 entry = readdir(dir);
316 if (!entry)
317 break;
319 /* skip directories */
320 if ((entry->attribute & ATTR_DIRECTORY))
321 continue;
323 /* Skip FAT volume ID */
324 if (entry->attribute & ATTR_VOLUME_ID)
325 continue;
327 /* filter out dotfiles and hidden files */
328 if ((entry->d_name[0]=='.') ||
329 (entry->attribute & ATTR_HIDDEN)) {
330 continue;
333 /* filter out non rock files */
334 if (strcasecmp(
335 &entry->d_name[strlen(entry->d_name) - sizeof(ROCK_EXTENSION) + 1],
336 ROCK_EXTENSION)) {
337 continue;
340 /* filter out to long filenames */
341 if (strlen(entry->d_name) > MAX_PLUGIN_LENGTH + 5)
343 splash(HZ,true,str(LANG_FILETYPES_PLUGIN_NAME_LONG));
344 continue;
347 dot=strrchr(entry->d_name,'.');
348 *dot='\0';
349 dash=strchr(entry->d_name,'-');
351 /* add plugin and extension */
352 if (dash)
354 *dash='\0';
355 ix=(filetype_get_attr(entry->d_name) >> 8);
356 if (!ix)
358 cp=get_string(entry->d_name);
359 if (cp)
361 exttypes[cnt_exttypes].extension=cp;
362 exttypes[cnt_exttypes].type=&filetypes[cnt_filetypes];
363 #ifdef HAVE_LCD_BITMAP
364 exttypes[cnt_exttypes].type->icon = bitmap_icons_6x8[Plugin];
365 #else
366 exttypes[cnt_exttypes].type->icon = Plugin;
367 #endif
368 cnt_exttypes++;
370 *dash='-';
371 cp=get_string(entry->d_name);
372 if (cp)
374 filetypes[cnt_filetypes].plugin=cp;
375 cnt_filetypes++;
377 else
378 break;
380 else
381 break;
383 else
385 *dash='-';
386 if (!filetypes[ix].plugin)
388 cp=get_string(entry->d_name);
389 if (cp)
391 filetypes[cnt_filetypes].plugin=cp;
392 cnt_filetypes++;
394 else
395 break;
398 *dash='-';
400 /* add plugin only */
401 else
403 found=false;
404 for (i = first_soft_filetype; i < cnt_filetypes; i++)
406 if (filetypes[i].plugin)
407 if (!strcasecmp(filetypes[i].plugin,entry->d_name))
409 found=true;
410 break;
414 if (!found)
416 cp=get_string(entry->d_name);
417 if (cp)
419 filetypes[cnt_filetypes].plugin=cp;
420 filetypes[cnt_filetypes].no_extension=true;
421 cnt_filetypes++;
423 else
424 break;
427 *dot='.';
429 closedir(dir);
432 #ifdef HAVE_LCD_BITMAP
433 static int add_plugin(char *plugin, char *icon)
434 #else
435 static int add_plugin(char *plugin)
436 #endif
438 char *cp;
439 int i;
441 if (!plugin)
442 return 0;
444 cp=strrchr(plugin, '.');
445 if (cp)
446 *cp='\0';
448 for (i=first_soft_filetype; i < cnt_filetypes; i++)
450 if (filetypes[i].plugin)
452 if (!strcasecmp(plugin, filetypes[i].plugin))
454 #ifdef HAVE_LCD_BITMAP
455 if (filetypes[i].icon == NULL && icon)
457 cp = string2icon(icon);
458 if (cp)
459 filetypes[cnt_filetypes].icon = cp;
460 else
461 return 0;
463 #endif
464 return i;
469 /* new plugin */
470 cp = get_string(plugin);
471 if (cp)
473 filetypes[cnt_filetypes].plugin = cp;
474 #ifdef HAVE_LCD_BITMAP
475 /* add icon */
476 if (icon)
478 cp = string2icon(icon);
479 if (cp)
480 filetypes[cnt_filetypes].icon = cp;
481 else
482 return 0;
484 #endif
486 else
488 return 0;
491 cnt_filetypes++;
492 return cnt_filetypes - 1;
495 /* read config file (or cahe file) */
496 bool read_config(const char* file)
498 enum {extension,
499 plugin,
500 #ifdef HAVE_LCD_BITMAP
501 icon,
502 #endif
503 last};
505 int i,ix;
506 int fd;
507 char* end;
508 char* cp;
509 char* str[last];
510 char buf[80];
512 fd = open(file, O_RDONLY);
513 if (fd < 0)
514 return false;
516 while (read_line(fd, buf, sizeof(buf)))
518 if (cnt_exttypes >= MAX_EXTTYPES)
520 splash(HZ,true,str(LANG_FILETYPES_EXTENSION_FULL));
521 break;
524 if (cnt_filetypes >= MAX_FILETYPES)
526 splash(HZ,true,str(LANG_FILETYPES_FULL));
527 break;
530 /* parse buffer */
531 rm_whitespaces(buf);
533 if (strlen(buf) == 0)
534 continue;
536 if (buf[0] == '#')
537 continue;
539 memset(str,0,sizeof(str));
540 i=0;
541 cp=buf;
542 while (*cp==',') {
543 cp++;
544 i++;
546 str[i] = strtok_r(cp, ",", &end);
547 i++;
549 while (end && i < last)
551 if (end)
553 cp=end;
554 while (*cp==',') {
555 cp++;
556 i++;
559 str[i] = strtok_r(NULL, ",", &end);
560 if (str[i])
561 if (!strlen(str[i]))
562 str[i]=NULL;
563 i++;
566 /* bail out if no icon and no plugin */
567 if (!str[plugin]
568 #ifdef HAVE_LCD_BITMAP
569 && !str[icon]
570 #endif
572 continue;
574 /* bail out if no plugin and icon is incorrect*/
575 if (!str[plugin]
576 #ifdef HAVE_LCD_BITMAP
577 && strlen(str[icon]) != ICON_LENGTH*2
578 #endif
580 continue;
582 /* bail out if no icon and no plugin and no extension*/
583 if (!str[plugin] &&
584 #ifdef HAVE_LCD_BITMAP
585 !str[icon] &&
586 #endif
587 !str[extension])
588 continue;
590 /* bail out if we are not able to start plugin from onplay.c ?*/
591 if (str[plugin])
593 if (strlen(str[plugin]) > MAX_PLUGIN_LENGTH)
595 splash(HZ, true, str(LANG_FILETYPES_PLUGIN_NAME_LONG));
596 str[plugin] = NULL;
597 continue;
601 ix=0;
602 /* if extension already exist don't add a new one */
603 for (i=0; i < cnt_exttypes; i++)
605 if (!strcasecmp(str[extension],exttypes[i].extension))
607 #ifdef HAVE_LCD_BITMAP
608 ix=add_plugin(str[plugin],NULL);
609 if (ix)
611 if (str[icon] && filetypes[ix].icon == NULL)
613 if (exttypes[i].type->icon == NULL)
615 cp = string2icon(str[icon]);
616 if (cp)
617 exttypes[i].type->icon = cp;
621 #else
622 ix=add_plugin(str[plugin]);
623 #endif
624 if (exttypes[i].type == NULL)
626 exttypes[i].type = &filetypes[ix];
628 break;
631 if (ix)
632 continue;
634 /* add extension */
635 if (str[extension])
637 #ifdef HAVE_LCD_BITMAP
638 ix=add_plugin(str[plugin],str[icon]);
639 #else
640 ix=add_plugin(str[plugin]);
641 #endif
642 if (ix)
644 cp=get_string(str[extension]);
645 if (cp)
647 exttypes[cnt_exttypes].extension = cp;
649 exttypes[cnt_exttypes].type = &filetypes[ix];
650 cnt_exttypes++;
651 filetypes[i].no_extension=false;
653 else
655 break;
658 else
660 break;
663 else
665 #ifdef HAVE_LCD_BITMAP
666 ix=add_plugin(str[plugin],str[icon]);
667 #else
668 ix=add_plugin(str[plugin]);
669 #endif
670 filetypes[ix].no_extension=true;
671 if (!i)
672 break;
675 close(fd);
677 return true;
680 #ifdef HAVE_LCD_BITMAP
681 /* convert an ascii hexadecimal icon to a binary icon */
682 static char* string2icon(const char* str)
684 char tmp[ICON_LENGTH*2];
685 char *cp;
686 int i;
688 if (strlen(str)!=ICON_LENGTH*2)
689 return NULL;
691 if ((sizeof(string_buffer) +
692 (unsigned long) string_buffer -
693 (unsigned long) next_free_string) < ICON_LENGTH)
695 splash(HZ,true,str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
696 return NULL;
699 for (i=0; i<12; i++)
701 if (str[i] >= '0' && str[i] <= '9')
703 tmp[i]=str[i]-'0';
704 continue;
707 if (str[i] >= 'a' && str[i] <= 'f')
709 tmp[i]=str[i]-'a'+10;
710 continue;
713 if (str[i] >= 'A' && str[i] <= 'F')
715 tmp[i]=str[i]-'A'+10;
716 continue;
719 return NULL;
722 cp=next_free_string;
723 for (i = 0; i < ICON_LENGTH; i++)
724 cp[i]=((tmp[i*2]<<4) | tmp[i*2+1]);
726 next_free_string=&next_free_string[ICON_LENGTH];
727 return cp;
729 #endif
731 /* get string from buffer */
732 static char* get_string(const char* str)
734 unsigned int l=strlen(str)+1;
735 char* cp;
737 if (!str)
738 return NULL;
740 if (l <= (sizeof(string_buffer) +
741 (unsigned long) string_buffer -
742 (unsigned long) next_free_string))
744 strcpy(next_free_string,str);
745 cp=next_free_string;
746 next_free_string=&next_free_string[l];
747 return cp;
749 else
751 splash(HZ,true,str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
752 return NULL;
756 /* remove all white spaces from string */
757 static void rm_whitespaces(char* str)
759 char *cp, *free;
761 cp=str;
762 free=cp;
764 while (cp < &str[strlen(str)])
766 switch (*cp)
768 case ' ' :
769 case '\t' :
770 case '\r' :
771 break;
773 default:
774 *free=*cp;
775 free++;
776 break;
778 cp++;
781 *free='\0';