1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Bryan Childs
11 * Copyright (c) 2007 Alexander Levin
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "shortcuts.h"
22 MEM_FUNCTION_WRAPPERS(rb
);
24 #define SHORTCUTS_FILENAME "/shortcuts.link"
26 #define PATH_DISP_SEPARATOR "\t"
27 #define PATH_DISP_SEPARATOR_LEN 1 /* strlen(PATH_DISP_SEPARATOR) */
28 #define CONTROL_PREFIX "#"
29 #define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */
30 #define NAME_VALUE_SEPARATOR "="
31 #define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */
33 #define INSTR_DISPLAY_LAST_SEGMENTS "Display last path segments"
35 /* Memory (will be used for entries) */
37 long memory_bufsize
; /* Size of memory_buf in bytes */
40 /* The file we're processing */
43 bool parse_entry_content(char *line
, sc_entry_t
*entry
, int last_segm
);
44 char *last_segments(char *path
, int nsegm
);
45 bool is_control(char *line
, sc_file_t
*file
);
46 bool starts_with(char *string
, char *prefix
);
47 bool parse_name_value(char *line
, char *name
, int namesize
,
48 char *value
, int valuesize
);
49 void write_entry_to_file(int fd
, sc_entry_t
*entry
);
50 void write_int_instruction_to_file(int fd
, char *instr
, int value
);
53 void allocate_memory(void **buf
, size_t *bufsize
)
55 *buf
= rb
->plugin_get_buffer(bufsize
);
56 DEBUGF("Got %ld bytes of memory\n", *bufsize
);
60 void init_sc_file(sc_file_t
*file
, void *buf
, size_t bufsize
)
62 file
->entries
= (sc_entry_t
*)buf
;
63 file
->max_entries
= bufsize
/ sizeof(sc_entry_t
);
64 DEBUGF("Buffer capacity: %d entries\n", file
->max_entries
);
66 file
->show_last_segments
= -1;
70 bool load_sc_file(sc_file_t
*file
, char *filename
, bool must_exist
,
71 void *entry_buf
, size_t entry_bufsize
)
74 bool ret_val
= false; /* Assume bad case */
76 char sc_content
[2*MAX_PATH
];
79 /* We start to load a new file -> prepare it */
80 init_sc_file(&sc_file
, entry_buf
, entry_bufsize
);
82 fd
= rb
->open(filename
, O_RDONLY
);
84 /* The file didn't exist on disk */
86 DEBUGF("Trying to create link file '%s'...\n", filename
);
87 fd
= rb
->creat(filename
);
89 /* For some reason we couldn't create the file,
90 * so return an error message and exit */
91 rb
->splash(HZ
*2, "Couldn't create the shortcuts file %s",
95 /* File created, but there's nothing in it, so just exit */
99 rb
->splash(HZ
, "Couldn't open %s", filename
);
104 while ((amountread
=rb
->read_line(fd
,sc_content
, sizeof(sc_content
)))) {
105 if (is_control(sc_content
, file
)) {
108 if (file
->entry_cnt
>= file
->max_entries
) {
109 rb
->splash(HZ
*2, "Too many entries in the file, max allowed: %d",
113 if (!parse_entry_content(sc_content
, &entry
,file
->show_last_segments
)) {
114 /* Could not parse the entry (too long path?) -> ignore */
115 DEBUGF("Could not parse '%s' -> ignored\n", sc_content
);
118 DEBUGF("Parsed entry: path=%s, disp=%s\n", entry
.path
, entry
.disp
);
119 append_entry(file
, &entry
);
126 ret_val
= true; /* Everything went ok */
137 void print_file(sc_file_t
*file
)
139 DEBUGF("Number of entries : %d\n", file
->entry_cnt
);
140 DEBUGF("Show Last Segments: %d\n", file
->show_last_segments
);
143 for (i
=0, entry
=file
->entries
; i
<file
->entry_cnt
; i
++,entry
++) {
144 if (entry
->explicit_disp
) {
145 DEBUGF("%2d. '%s', show as '%s'\n", i
+1, entry
->path
, entry
->disp
);
147 DEBUGF("%2d. '%s' (%s)\n", i
+1, entry
->path
, entry
->disp
);
154 bool append_entry(sc_file_t
*file
, sc_entry_t
*entry
)
156 if (file
->entry_cnt
>= file
->max_entries
) {
159 rb
->memcpy(file
->entries
+file
->entry_cnt
, entry
, sizeof(*entry
));
165 bool remove_entry(sc_file_t
*file
, int entry_idx
)
167 if ((entry_idx
<0) || (entry_idx
>=file
->entry_cnt
)) {
170 sc_entry_t
*start
= file
->entries
+ entry_idx
;
171 rb
->memmove(start
, start
+ 1,
172 (file
->entry_cnt
-entry_idx
-1) * sizeof(sc_entry_t
));
178 bool is_valid_index(sc_file_t
*file
, int entry_idx
)
180 return (entry_idx
>= 0) && (entry_idx
< file
->entry_cnt
);
184 bool parse_entry_content(char *line
, sc_entry_t
*entry
, int last_segm
)
188 unsigned int path_len
, disp_len
;
191 sep
= rb
->strcasestr(line
, PATH_DISP_SEPARATOR
);
192 expl
= (sep
!= NULL
);
194 /* Explicit name for the entry is specified -> use it */
196 path_len
= sep
- line
;
197 disp
= sep
+ PATH_DISP_SEPARATOR_LEN
;
198 disp_len
= rb
->strlen(disp
);
200 /* No special name to display */
202 path_len
= rb
->strlen(line
);
203 if (last_segm
<= 0) {
206 disp
= last_segments(line
, last_segm
);
208 disp_len
= rb
->strlen(disp
);
211 if (path_len
>= sizeof(entry
->path
) || disp_len
>= sizeof(entry
->disp
)) {
212 DEBUGF("Bad entry: pathlen=%d, displen=%d\n", path_len
, disp_len
);
215 rb
->strncpy(entry
->path
, path
, path_len
);
216 entry
->path
[path_len
] = '\0';
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 char *p
= rb
->strrchr(path
, PATH_SEPARATOR
[0]); /* Hack */
228 return path
; /* No separator??? */
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
->strncpy(name
, line
, name_len
);
298 name
[name_len
] = '\0';
300 val_len
= rb
->strlen(line
) - name_len
- NAME_VALUE_SEPARATOR_LEN
;
301 if (val_len
>= valuesize
) {
305 rb
->strncpy(value
, sep
+NAME_VALUE_SEPARATOR_LEN
, val_len
+1);
310 bool dump_sc_file(sc_file_t
*file
, char *filename
)
312 DEBUGF("Dumping shortcuts to the file '%s'\n", filename
);
315 /* ideally, we should just write a new
316 * entry to the file, but I'm going to
317 * be lazy, and just re-write the whole
319 fd
= rb
->open(filename
, O_WRONLY
|O_TRUNC
);
321 rb
->splash(HZ
*2, "Could not open shortcuts file %s for writing",
329 /* Always dump the 'display last segms' settings (even it it's
330 * not active) so that it can be easily changed without having
331 * to remember the setting name */
332 write_int_instruction_to_file(fd
,
333 INSTR_DISPLAY_LAST_SEGMENTS
, file
->show_last_segments
);
337 for (i
=0, entry
=file
->entries
; i
<file
->entry_cnt
; i
++,entry
++) {
338 write_entry_to_file(fd
, entry
);
342 DEBUGF("Dumped %d entries to the file '%s'\n", file
->entry_cnt
, filename
);
348 void write_int_instruction_to_file(int fd
, char *instr
, int value
)
350 rb
->fdprintf(fd
, "%s%s%s%d\n", CONTROL_PREFIX
, instr
,
351 NAME_VALUE_SEPARATOR
, value
);
355 void write_entry_to_file(int fd
, sc_entry_t
*entry
)
357 rb
->fdprintf(fd
, "%s", entry
->path
);
358 if (entry
->explicit_disp
) {
359 rb
->fdprintf(fd
, "%s%s", PATH_DISP_SEPARATOR
, entry
->disp
);
361 rb
->fdprintf(fd
, "\n");