Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / apps / plugins / shortcuts / shortcuts_common.c
blobda212a55a3c173f37a1cf4a889758c04c571d4a1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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) */
37 void *memory_buf;
38 size_t memory_bufsize; /* Size of memory_buf in bytes */
41 /* The file we're processing */
42 sc_file_t sc_file;
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 %ld bytes of memory\n", *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);
66 file->entry_cnt = 0;
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)
74 int fd = -1;
75 bool ret_val = false; /* Assume bad case */
76 int amountread = 0;
77 char sc_content[2*MAX_PATH];
78 sc_entry_t entry;
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);
84 if (fd < 0) {
85 /* The file didn't exist on disk */
86 if (!must_exist) {
87 DEBUGF("Trying to create link file '%s'...\n", filename);
88 fd = rb->creat(filename, 0666);
89 if (fd < 0){
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",
93 filename);
94 goto end_of_proc;
96 /* File created, but there's nothing in it, so just exit */
97 ret_val = true;
98 goto end_of_proc;
99 } else {
100 rb->splashf(HZ, "Couldn't open %s", filename);
101 goto end_of_proc;
105 while ((amountread=rb->read_line(fd,sc_content, sizeof(sc_content)))) {
106 if (is_control(sc_content, file)) {
107 continue;
109 if (file->entry_cnt >= file->max_entries) {
110 rb->splashf(HZ*2, "Too many entries in the file, max allowed: %d",
111 file->max_entries);
112 goto end_of_proc;
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);
117 continue;
119 DEBUGF("Parsed entry: path=%s, disp=%s\n", entry.path, entry.disp);
120 append_entry(file, &entry);
123 #ifdef SC_DEBUG
124 print_file(file);
125 #endif
127 ret_val = true; /* Everything went ok */
129 end_of_proc:
130 if (fd >= 0) {
131 rb->close(fd);
132 fd = -1;
134 return ret_val;
137 #ifdef SC_DEBUG
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);
142 int i;
143 sc_entry_t *entry;
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);
147 } else {
148 DEBUGF("%2d. '%s' (%s)\n", i+1, entry->path, entry->disp);
152 #endif
155 bool append_entry(sc_file_t *file, sc_entry_t *entry)
157 if (file->entry_cnt >= file->max_entries) {
158 return false;
160 rb->memcpy(file->entries+file->entry_cnt, entry, sizeof(*entry));
161 file->entry_cnt++;
162 return true;
166 bool remove_entry(sc_file_t *file, int entry_idx)
168 if ((entry_idx<0) || (entry_idx>=file->entry_cnt)) {
169 return false;
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));
174 file->entry_cnt--;
175 return true;
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)
187 char *sep;
188 char *path, *disp;
189 unsigned int path_len, disp_len;
190 bool expl;
192 sep = rb->strcasestr(line, PATH_DISP_SEPARATOR);
193 expl = (sep != NULL);
194 if (expl) {
195 /* Explicit name for the entry is specified -> use it */
196 path = line;
197 path_len = sep - line;
198 disp = sep + PATH_DISP_SEPARATOR_LEN;
199 disp_len = rb->strlen(disp);
200 } else {
201 /* No special name to display */
202 path = line;
203 path_len = rb->strlen(line);
204 if (last_segm <= 0) {
205 disp = path;
206 } else {
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);
214 return false;
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;
219 return true;
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;
227 int seg_cnt = 0;
228 if(p <= path)
229 return path;
230 while ((p > path) && (seg_cnt < nsegm)) {
231 p--;
232 if (!starts_with(p, PATH_SEPARATOR)) {
233 continue;
235 seg_cnt++;
236 if (seg_cnt == nsegm && p > path) {
237 p++; /* Eat the '/' to show that something has been truncated */
240 return p;
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)) {
248 return false;
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);
255 return true;
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);
262 } else {
263 /* Unknown instruction -> ignore */
264 DEBUGF("Unknown processing instruction: '%s'\n", name);
267 return true;
271 bool starts_with(char *string, char *prefix)
273 unsigned int pfx_len = rb->strlen(prefix);
274 if (rb->strlen(string) < pfx_len)
275 return false;
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)
283 char *sep;
284 int name_len, val_len;
285 name[0] = value[0] = '\0';
287 sep = rb->strcasestr(line, NAME_VALUE_SEPARATOR);
288 if (sep == NULL) {
289 /* No separator char -> weird instruction */
290 return false;
292 name_len = sep - line;
293 if (name_len >= namesize) {
294 /* Too long name */
295 return false;
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) {
301 /* Too long value */
302 return false;
304 rb->strlcpy(value, sep+NAME_VALUE_SEPARATOR_LEN, val_len+1);
305 return true;
309 bool dump_sc_file(sc_file_t *file, char *filename)
311 DEBUGF("Dumping shortcuts to the file '%s'\n", filename);
312 int fd;
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
317 * thing. */
318 fd = rb->open(filename, O_WRONLY|O_TRUNC);
319 if (fd < 0) {
320 rb->splashf(HZ*2, "Could not open shortcuts file %s for writing",
321 filename);
322 return false;
326 * Write instructions
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);
334 int i;
335 sc_entry_t *entry;
336 for (i=0, entry=file->entries; i<file->entry_cnt; i++,entry++) {
337 write_entry_to_file(fd, entry);
340 rb->close(fd);
341 DEBUGF("Dumped %d entries to the file '%s'\n", file->entry_cnt, filename);
343 return true;
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");