1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Bryan Childs
11 * Copyright (c) 2007 Alexander Levin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "shortcuts.h"
24 MEM_FUNCTION_WRAPPERS(rb
);
26 #define SHORTCUTS_FILENAME "/shortcuts.link"
28 #define PATH_DISP_SEPARATOR "\t"
29 #define PATH_DISP_SEPARATOR_LEN 1 /* strlen(PATH_DISP_SEPARATOR) */
30 #define CONTROL_PREFIX "#"
31 #define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */
32 #define NAME_VALUE_SEPARATOR "="
33 #define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */
35 #define INSTR_DISPLAY_LAST_SEGMENTS "Display last path segments"
37 /* Memory (will be used for entries) */
39 long memory_bufsize
; /* Size of memory_buf in bytes */
42 /* The file we're processing */
45 bool parse_entry_content(char *line
, sc_entry_t
*entry
, int last_segm
);
46 char *last_segments(char *path
, int nsegm
);
47 bool is_control(char *line
, sc_file_t
*file
);
48 bool starts_with(char *string
, char *prefix
);
49 bool parse_name_value(char *line
, char *name
, int namesize
,
50 char *value
, int valuesize
);
51 void write_entry_to_file(int fd
, sc_entry_t
*entry
);
52 void write_int_instruction_to_file(int fd
, char *instr
, int value
);
55 void allocate_memory(void **buf
, size_t *bufsize
)
57 *buf
= rb
->plugin_get_buffer(bufsize
);
58 DEBUGF("Got %ld bytes of memory\n", *bufsize
);
62 void init_sc_file(sc_file_t
*file
, void *buf
, size_t bufsize
)
64 file
->entries
= (sc_entry_t
*)buf
;
65 file
->max_entries
= bufsize
/ sizeof(sc_entry_t
);
66 DEBUGF("Buffer capacity: %d entries\n", file
->max_entries
);
68 file
->show_last_segments
= -1;
72 bool load_sc_file(sc_file_t
*file
, char *filename
, bool must_exist
,
73 void *entry_buf
, size_t entry_bufsize
)
76 bool ret_val
= false; /* Assume bad case */
78 char sc_content
[2*MAX_PATH
];
81 /* We start to load a new file -> prepare it */
82 init_sc_file(&sc_file
, entry_buf
, entry_bufsize
);
84 fd
= rb
->open(filename
, O_RDONLY
);
86 /* The file didn't exist on disk */
88 DEBUGF("Trying to create link file '%s'...\n", filename
);
89 fd
= rb
->creat(filename
);
91 /* For some reason we couldn't create the file,
92 * so return an error message and exit */
93 rb
->splashf(HZ
*2, "Couldn't create the shortcuts file %s",
97 /* File created, but there's nothing in it, so just exit */
101 rb
->splashf(HZ
, "Couldn't open %s", filename
);
106 while ((amountread
=rb
->read_line(fd
,sc_content
, sizeof(sc_content
)))) {
107 if (is_control(sc_content
, file
)) {
110 if (file
->entry_cnt
>= file
->max_entries
) {
111 rb
->splashf(HZ
*2, "Too many entries in the file, max allowed: %d",
115 if (!parse_entry_content(sc_content
, &entry
,file
->show_last_segments
)) {
116 /* Could not parse the entry (too long path?) -> ignore */
117 DEBUGF("Could not parse '%s' -> ignored\n", sc_content
);
120 DEBUGF("Parsed entry: path=%s, disp=%s\n", entry
.path
, entry
.disp
);
121 append_entry(file
, &entry
);
128 ret_val
= true; /* Everything went ok */
139 void print_file(sc_file_t
*file
)
141 DEBUGF("Number of entries : %d\n", file
->entry_cnt
);
142 DEBUGF("Show Last Segments: %d\n", file
->show_last_segments
);
145 for (i
=0, entry
=file
->entries
; i
<file
->entry_cnt
; i
++,entry
++) {
146 if (entry
->explicit_disp
) {
147 DEBUGF("%2d. '%s', show as '%s'\n", i
+1, entry
->path
, entry
->disp
);
149 DEBUGF("%2d. '%s' (%s)\n", i
+1, entry
->path
, entry
->disp
);
156 bool append_entry(sc_file_t
*file
, sc_entry_t
*entry
)
158 if (file
->entry_cnt
>= file
->max_entries
) {
161 rb
->memcpy(file
->entries
+file
->entry_cnt
, entry
, sizeof(*entry
));
167 bool remove_entry(sc_file_t
*file
, int entry_idx
)
169 if ((entry_idx
<0) || (entry_idx
>=file
->entry_cnt
)) {
172 sc_entry_t
*start
= file
->entries
+ entry_idx
;
173 rb
->memmove(start
, start
+ 1,
174 (file
->entry_cnt
-entry_idx
-1) * sizeof(sc_entry_t
));
180 bool is_valid_index(sc_file_t
*file
, int entry_idx
)
182 return (entry_idx
>= 0) && (entry_idx
< file
->entry_cnt
);
186 bool parse_entry_content(char *line
, sc_entry_t
*entry
, int last_segm
)
190 unsigned int path_len
, disp_len
;
193 sep
= rb
->strcasestr(line
, PATH_DISP_SEPARATOR
);
194 expl
= (sep
!= NULL
);
196 /* Explicit name for the entry is specified -> use it */
198 path_len
= sep
- line
;
199 disp
= sep
+ PATH_DISP_SEPARATOR_LEN
;
200 disp_len
= rb
->strlen(disp
);
202 /* No special name to display */
204 path_len
= rb
->strlen(line
);
205 if (last_segm
<= 0) {
208 disp
= last_segments(line
, last_segm
);
210 disp_len
= rb
->strlen(disp
);
213 if (path_len
>= sizeof(entry
->path
) || disp_len
>= sizeof(entry
->disp
)) {
214 DEBUGF("Bad entry: pathlen=%d, displen=%d\n", path_len
, disp_len
);
217 rb
->strncpy(entry
->path
, path
, path_len
);
218 entry
->path
[path_len
] = '\0';
219 rb
->strcpy(entry
->disp
, disp
); /* Safe since we've checked the length */
220 entry
->explicit_disp
= expl
;
225 char *last_segments(char *path
, int nsegm
)
227 char *p
= rb
->strrchr(path
, PATH_SEPARATOR
[0]); /* Hack */
230 return path
; /* No separator??? */
232 while ((p
> path
) && (seg_cnt
< nsegm
)) {
234 if (!starts_with(p
, PATH_SEPARATOR
)) {
238 if (seg_cnt
== nsegm
&& p
> path
) {
239 p
++; /* Eat the '/' to show that something has been truncated */
246 bool is_control(char *line
, sc_file_t
*file
)
248 char name
[MAX_PATH
], value
[MAX_PATH
];
249 if (!starts_with(line
, CONTROL_PREFIX
)) {
252 line
+= CONTROL_PREFIX_LEN
;
254 if (!parse_name_value(line
, name
, sizeof(name
),
255 value
, sizeof(value
))) {
256 DEBUGF("Bad processing instruction: '%s'\n", line
);
260 /* Process control instruction */
261 if (rb
->strcasestr(name
, INSTR_DISPLAY_LAST_SEGMENTS
)) {
262 file
->show_last_segments
= rb
->atoi(value
);
263 DEBUGF("Set show last segms to %d\n", file
->show_last_segments
);
265 /* Unknown instruction -> ignore */
266 DEBUGF("Unknown processing instruction: '%s'\n", name
);
273 bool starts_with(char *string
, char *prefix
)
275 unsigned int pfx_len
= rb
->strlen(prefix
);
276 if (rb
->strlen(string
) < pfx_len
)
278 return (rb
->strncmp(string
, prefix
, pfx_len
) == 0);
282 bool parse_name_value(char *line
, char *name
, int namesize
,
283 char *value
, int valuesize
)
286 int name_len
, val_len
;
287 name
[0] = value
[0] = '\0';
289 sep
= rb
->strcasestr(line
, NAME_VALUE_SEPARATOR
);
291 /* No separator char -> weird instruction */
294 name_len
= sep
- line
;
295 if (name_len
>= namesize
) {
299 rb
->strncpy(name
, line
, name_len
);
300 name
[name_len
] = '\0';
302 val_len
= rb
->strlen(line
) - name_len
- NAME_VALUE_SEPARATOR_LEN
;
303 if (val_len
>= valuesize
) {
307 rb
->strncpy(value
, sep
+NAME_VALUE_SEPARATOR_LEN
, val_len
+1);
312 bool dump_sc_file(sc_file_t
*file
, char *filename
)
314 DEBUGF("Dumping shortcuts to the file '%s'\n", filename
);
317 /* ideally, we should just write a new
318 * entry to the file, but I'm going to
319 * be lazy, and just re-write the whole
321 fd
= rb
->open(filename
, O_WRONLY
|O_TRUNC
);
323 rb
->splashf(HZ
*2, "Could not open shortcuts file %s for writing",
331 /* Always dump the 'display last segms' settings (even it it's
332 * not active) so that it can be easily changed without having
333 * to remember the setting name */
334 write_int_instruction_to_file(fd
,
335 INSTR_DISPLAY_LAST_SEGMENTS
, file
->show_last_segments
);
339 for (i
=0, entry
=file
->entries
; i
<file
->entry_cnt
; i
++,entry
++) {
340 write_entry_to_file(fd
, entry
);
344 DEBUGF("Dumped %d entries to the file '%s'\n", file
->entry_cnt
, filename
);
350 void write_int_instruction_to_file(int fd
, char *instr
, int value
)
352 rb
->fdprintf(fd
, "%s%s%s%d\n", CONTROL_PREFIX
, instr
,
353 NAME_VALUE_SEPARATOR
, value
);
357 void write_entry_to_file(int fd
, sc_entry_t
*entry
)
359 rb
->fdprintf(fd
, "%s", entry
->path
);
360 if (entry
->explicit_disp
) {
361 rb
->fdprintf(fd
, "%s%s", PATH_DISP_SEPARATOR
, entry
->disp
);
363 rb
->fdprintf(fd
, "\n");