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"
25 #define SHORTCUTS_FILENAME "/shortcuts.link"
27 #define PATH_DISP_SEPARATOR "\t"
28 #define PATH_DISP_SEPARATOR_LEN 1 /* strlen(PATH_DISP_SEPARATOR) */
29 #define CONTROL_PREFIX "#"
30 #define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */
31 #define NAME_VALUE_SEPARATOR "="
32 #define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */
34 #define INSTR_DISPLAY_LAST_SEGMENTS "Display last path segments"
36 /* Memory (will be used for entries) */
38 size_t memory_bufsize
; /* Size of memory_buf in bytes */
41 /* The file we're processing */
44 bool parse_entry_content(char *line
, sc_entry_t
*entry
, int last_segm
);
45 char *last_segments(char *path
, int nsegm
);
46 bool is_control(char *line
, sc_file_t
*file
);
47 bool starts_with(char *string
, char *prefix
);
48 bool parse_name_value(char *line
, char *name
, int namesize
,
49 char *value
, int valuesize
);
50 void write_entry_to_file(int fd
, sc_entry_t
*entry
);
51 void write_int_instruction_to_file(int fd
, char *instr
, int value
);
54 void allocate_memory(void **buf
, size_t *bufsize
)
56 *buf
= rb
->plugin_get_buffer(bufsize
);
57 DEBUGF("Got %lu bytes of memory\n", (unsigned long)*bufsize
);
61 void init_sc_file(sc_file_t
*file
, void *buf
, size_t bufsize
)
63 file
->entries
= (sc_entry_t
*)buf
;
64 file
->max_entries
= bufsize
/ sizeof(sc_entry_t
);
65 DEBUGF("Buffer capacity: %d entries\n", file
->max_entries
);
67 file
->show_last_segments
= -1;
71 bool load_sc_file(sc_file_t
*file
, char *filename
, bool must_exist
,
72 void *entry_buf
, size_t entry_bufsize
)
75 bool ret_val
= false; /* Assume bad case */
77 char sc_content
[2*MAX_PATH
];
80 /* We start to load a new file -> prepare it */
81 init_sc_file(&sc_file
, entry_buf
, entry_bufsize
);
83 fd
= rb
->open(filename
, O_RDONLY
);
85 /* The file didn't exist on disk */
87 DEBUGF("Trying to create link file '%s'...\n", filename
);
88 fd
= rb
->creat(filename
, 0666);
90 /* For some reason we couldn't create the file,
91 * so return an error message and exit */
92 rb
->splashf(HZ
*2, "Couldn't create the shortcuts file %s",
96 /* File created, but there's nothing in it, so just exit */
100 rb
->splashf(HZ
, "Couldn't open %s", filename
);
105 while ((amountread
=rb
->read_line(fd
,sc_content
, sizeof(sc_content
)))) {
106 if (is_control(sc_content
, file
)) {
109 if (file
->entry_cnt
>= file
->max_entries
) {
110 rb
->splashf(HZ
*2, "Too many entries in the file, max allowed: %d",
114 if (!parse_entry_content(sc_content
, &entry
,file
->show_last_segments
)) {
115 /* Could not parse the entry (too long path?) -> ignore */
116 DEBUGF("Could not parse '%s' -> ignored\n", sc_content
);
119 DEBUGF("Parsed entry: path=%s, disp=%s\n", entry
.path
, entry
.disp
);
120 append_entry(file
, &entry
);
127 ret_val
= true; /* Everything went ok */
138 void print_file(sc_file_t
*file
)
140 DEBUGF("Number of entries : %d\n", file
->entry_cnt
);
141 DEBUGF("Show Last Segments: %d\n", file
->show_last_segments
);
144 for (i
=0, entry
=file
->entries
; i
<file
->entry_cnt
; i
++,entry
++) {
145 if (entry
->explicit_disp
) {
146 DEBUGF("%2d. '%s', show as '%s'\n", i
+1, entry
->path
, entry
->disp
);
148 DEBUGF("%2d. '%s' (%s)\n", i
+1, entry
->path
, entry
->disp
);
155 bool append_entry(sc_file_t
*file
, sc_entry_t
*entry
)
157 if (file
->entry_cnt
>= file
->max_entries
) {
160 rb
->memcpy(file
->entries
+file
->entry_cnt
, entry
, sizeof(*entry
));
166 bool remove_entry(sc_file_t
*file
, int entry_idx
)
168 if ((entry_idx
<0) || (entry_idx
>=file
->entry_cnt
)) {
171 sc_entry_t
*start
= file
->entries
+ entry_idx
;
172 rb
->memmove(start
, start
+ 1,
173 (file
->entry_cnt
-entry_idx
-1) * sizeof(sc_entry_t
));
179 bool is_valid_index(sc_file_t
*file
, int entry_idx
)
181 return (entry_idx
>= 0) && (entry_idx
< file
->entry_cnt
);
185 bool parse_entry_content(char *line
, sc_entry_t
*entry
, int last_segm
)
189 unsigned int path_len
, disp_len
;
192 sep
= rb
->strcasestr(line
, PATH_DISP_SEPARATOR
);
193 expl
= (sep
!= NULL
);
195 /* Explicit name for the entry is specified -> use it */
197 path_len
= sep
- line
;
198 disp
= sep
+ PATH_DISP_SEPARATOR_LEN
;
199 disp_len
= rb
->strlen(disp
);
201 /* No special name to display */
203 path_len
= rb
->strlen(line
);
204 if (last_segm
<= 0) {
207 disp
= last_segments(line
, last_segm
);
209 disp_len
= rb
->strlen(disp
);
212 if (path_len
>= sizeof(entry
->path
) || disp_len
>= sizeof(entry
->disp
)) {
213 DEBUGF("Bad entry: pathlen=%d, displen=%d\n", path_len
, disp_len
);
216 rb
->strlcpy(entry
->path
, path
, path_len
+ 1);
217 rb
->strcpy(entry
->disp
, disp
); /* Safe since we've checked the length */
218 entry
->explicit_disp
= expl
;
223 char *last_segments(char *path
, int nsegm
)
225 /* don't count one trailing separator */
226 char *p
= path
+rb
->strlen(path
)-PATH_SEPARATOR_LEN
;
230 while ((p
> path
) && (seg_cnt
< nsegm
)) {
232 if (!starts_with(p
, PATH_SEPARATOR
)) {
236 if (seg_cnt
== nsegm
&& p
> path
) {
237 p
++; /* Eat the '/' to show that something has been truncated */
244 bool is_control(char *line
, sc_file_t
*file
)
246 char name
[MAX_PATH
], value
[MAX_PATH
];
247 if (!starts_with(line
, CONTROL_PREFIX
)) {
250 line
+= CONTROL_PREFIX_LEN
;
252 if (!parse_name_value(line
, name
, sizeof(name
),
253 value
, sizeof(value
))) {
254 DEBUGF("Bad processing instruction: '%s'\n", line
);
258 /* Process control instruction */
259 if (rb
->strcasestr(name
, INSTR_DISPLAY_LAST_SEGMENTS
)) {
260 file
->show_last_segments
= rb
->atoi(value
);
261 DEBUGF("Set show last segms to %d\n", file
->show_last_segments
);
263 /* Unknown instruction -> ignore */
264 DEBUGF("Unknown processing instruction: '%s'\n", name
);
271 bool starts_with(char *string
, char *prefix
)
273 unsigned int pfx_len
= rb
->strlen(prefix
);
274 if (rb
->strlen(string
) < pfx_len
)
276 return (rb
->strncmp(string
, prefix
, pfx_len
) == 0);
280 bool parse_name_value(char *line
, char *name
, int namesize
,
281 char *value
, int valuesize
)
284 int name_len
, val_len
;
285 name
[0] = value
[0] = '\0';
287 sep
= rb
->strcasestr(line
, NAME_VALUE_SEPARATOR
);
289 /* No separator char -> weird instruction */
292 name_len
= sep
- line
;
293 if (name_len
>= namesize
) {
297 rb
->strlcpy(name
, line
, name_len
+ 1);
299 val_len
= rb
->strlen(line
) - name_len
- NAME_VALUE_SEPARATOR_LEN
;
300 if (val_len
>= valuesize
) {
304 rb
->strlcpy(value
, sep
+NAME_VALUE_SEPARATOR_LEN
, val_len
+1);
309 bool dump_sc_file(sc_file_t
*file
, char *filename
)
311 DEBUGF("Dumping shortcuts to the file '%s'\n", filename
);
314 /* ideally, we should just write a new
315 * entry to the file, but I'm going to
316 * be lazy, and just re-write the whole
318 fd
= rb
->open(filename
, O_WRONLY
|O_TRUNC
);
320 rb
->splashf(HZ
*2, "Could not open shortcuts file %s for writing",
328 /* Always dump the 'display last segms' settings (even it it's
329 * not active) so that it can be easily changed without having
330 * to remember the setting name */
331 write_int_instruction_to_file(fd
,
332 INSTR_DISPLAY_LAST_SEGMENTS
, file
->show_last_segments
);
336 for (i
=0, entry
=file
->entries
; i
<file
->entry_cnt
; i
++,entry
++) {
337 write_entry_to_file(fd
, entry
);
341 DEBUGF("Dumped %d entries to the file '%s'\n", file
->entry_cnt
, filename
);
347 void write_int_instruction_to_file(int fd
, char *instr
, int value
)
349 rb
->fdprintf(fd
, "%s%s%s%d\n", CONTROL_PREFIX
, instr
,
350 NAME_VALUE_SEPARATOR
, value
);
354 void write_entry_to_file(int fd
, sc_entry_t
*entry
)
356 rb
->fdprintf(fd
, "%s", entry
->path
);
357 if (entry
->explicit_disp
) {
358 rb
->fdprintf(fd
, "%s%s", PATH_DISP_SEPARATOR
, entry
->disp
);
360 rb
->fdprintf(fd
, "\n");