363 lines
10 KiB
C
363 lines
10 KiB
C
|
/***************************************************************************
|
||
|
* __________ __ ___.
|
||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||
|
* \/ \/ \/ \/ \/
|
||
|
* $Id$
|
||
|
*
|
||
|
* Copyright (C) 2007 Bryan Childs
|
||
|
* Copyright (c) 2007 Alexander Levin
|
||
|
*
|
||
|
* All files in this archive are subject to the GNU General Public License.
|
||
|
* See the file COPYING in the source tree root for full license agreement.
|
||
|
*
|
||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||
|
* KIND, either express or implied.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "shortcuts.h"
|
||
|
MEM_FUNCTION_WRAPPERS(rb);
|
||
|
|
||
|
#define SHORTCUTS_FILENAME "/shortcuts.link"
|
||
|
|
||
|
#define PATH_DISP_SEPARATOR "\t"
|
||
|
#define PATH_DISP_SEPARATOR_LEN 1 /* strlen(PATH_DISP_SEPARATOR) */
|
||
|
#define CONTROL_PREFIX "#"
|
||
|
#define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */
|
||
|
#define NAME_VALUE_SEPARATOR "="
|
||
|
#define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */
|
||
|
|
||
|
#define INSTR_DISPLAY_LAST_SEGMENTS "Display last path segments"
|
||
|
|
||
|
/* Memory (will be used for entries) */
|
||
|
void *memory_buf;
|
||
|
long memory_bufsize; /* Size of memory_buf in bytes */
|
||
|
|
||
|
|
||
|
/* The file we're processing */
|
||
|
sc_file_t sc_file;
|
||
|
|
||
|
bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm);
|
||
|
char *last_segments(char *path, int nsegm);
|
||
|
bool is_control(char *line, sc_file_t *file);
|
||
|
bool starts_with(char *string, char *prefix);
|
||
|
bool parse_name_value(char *line, char *name, int namesize,
|
||
|
char *value, int valuesize);
|
||
|
void write_entry_to_file(int fd, sc_entry_t *entry);
|
||
|
void write_int_instruction_to_file(int fd, char *instr, int value);
|
||
|
|
||
|
|
||
|
void allocate_memory(void **buf, size_t *bufsize)
|
||
|
{
|
||
|
*buf = rb->plugin_get_buffer(bufsize);
|
||
|
DEBUGF("Got %ld bytes of memory\n", *bufsize);
|
||
|
}
|
||
|
|
||
|
|
||
|
void init_sc_file(sc_file_t *file, void *buf, size_t bufsize)
|
||
|
{
|
||
|
file->entries = (sc_entry_t*)buf;
|
||
|
file->max_entries = bufsize / sizeof(sc_entry_t);
|
||
|
DEBUGF("Buffer capacity: %d entries\n", file->max_entries);
|
||
|
file->entry_cnt = 0;
|
||
|
file->show_last_segments = -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool load_sc_file(sc_file_t *file, char *filename, bool must_exist,
|
||
|
void *entry_buf, size_t entry_bufsize)
|
||
|
{
|
||
|
int fd = -1;
|
||
|
bool ret_val = false; /* Assume bad case */
|
||
|
int amountread = 0;
|
||
|
char sc_content[2*MAX_PATH];
|
||
|
sc_entry_t entry;
|
||
|
|
||
|
/* We start to load a new file -> prepare it */
|
||
|
init_sc_file(&sc_file, entry_buf, entry_bufsize);
|
||
|
|
||
|
fd = rb->open(filename, O_RDONLY);
|
||
|
if (fd < 0) {
|
||
|
/* The file didn't exist on disk */
|
||
|
if (!must_exist) {
|
||
|
DEBUGF("Trying to create link file '%s'...\n", filename);
|
||
|
fd = rb->creat(filename);
|
||
|
if (fd < 0){
|
||
|
/* For some reason we couldn't create the file,
|
||
|
* so return an error message and exit */
|
||
|
rb->splash(HZ*2, "Couldn't create the shortcuts file %s",
|
||
|
filename);
|
||
|
goto end_of_proc;
|
||
|
}
|
||
|
/* File created, but there's nothing in it, so just exit */
|
||
|
ret_val = true;
|
||
|
goto end_of_proc;
|
||
|
} else {
|
||
|
rb->splash(HZ, "Couldn't open %s", filename);
|
||
|
goto end_of_proc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while ((amountread=rb->read_line(fd,sc_content, sizeof(sc_content)))) {
|
||
|
if (is_control(sc_content, file)) {
|
||
|
continue;
|
||
|
}
|
||
|
if (file->entry_cnt >= file->max_entries) {
|
||
|
rb->splash(HZ*2, "Too many entries in the file, max allowed: %d",
|
||
|
file->max_entries);
|
||
|
goto end_of_proc;
|
||
|
}
|
||
|
if (!parse_entry_content(sc_content, &entry,file->show_last_segments)) {
|
||
|
/* Could not parse the entry (too long path?) -> ignore */
|
||
|
DEBUGF("Could not parse '%s' -> ignored\n", sc_content);
|
||
|
continue;
|
||
|
}
|
||
|
DEBUGF("Parsed entry: path=%s, disp=%s\n", entry.path, entry.disp);
|
||
|
append_entry(file, &entry);
|
||
|
}
|
||
|
|
||
|
#ifdef SC_DEBUG
|
||
|
print_file(file);
|
||
|
#endif
|
||
|
|
||
|
ret_val = true; /* Everything went ok */
|
||
|
|
||
|
end_of_proc:
|
||
|
if (fd >= 0) {
|
||
|
rb->close(fd);
|
||
|
fd = -1;
|
||
|
}
|
||
|
return ret_val;
|
||
|
}
|
||
|
|
||
|
#ifdef SC_DEBUG
|
||
|
void print_file(sc_file_t *file)
|
||
|
{
|
||
|
DEBUGF("Number of entries : %d\n", file->entry_cnt);
|
||
|
DEBUGF("Show Last Segments: %d\n", file->show_last_segments);
|
||
|
int i;
|
||
|
sc_entry_t *entry;
|
||
|
for (i=0, entry=file->entries; i<file->entry_cnt; i++,entry++) {
|
||
|
if (entry->explicit_disp) {
|
||
|
DEBUGF("%2d. '%s', show as '%s'\n", i+1, entry->path, entry->disp);
|
||
|
} else {
|
||
|
DEBUGF("%2d. '%s' (%s)\n", i+1, entry->path, entry->disp);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
bool append_entry(sc_file_t *file, sc_entry_t *entry)
|
||
|
{
|
||
|
if (file->entry_cnt >= file->max_entries) {
|
||
|
return false;
|
||
|
}
|
||
|
rb->memcpy(file->entries+file->entry_cnt, entry, sizeof(*entry));
|
||
|
file->entry_cnt++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool remove_entry(sc_file_t *file, int entry_idx)
|
||
|
{
|
||
|
if ((entry_idx<0) || (entry_idx>=file->entry_cnt)) {
|
||
|
return false;
|
||
|
}
|
||
|
sc_entry_t *start = file->entries + entry_idx;
|
||
|
rb->memmove(start, start + 1,
|
||
|
(file->entry_cnt-entry_idx-1) * sizeof(sc_entry_t));
|
||
|
file->entry_cnt--;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool is_valid_index(sc_file_t *file, int entry_idx)
|
||
|
{
|
||
|
return (entry_idx >= 0) && (entry_idx < file->entry_cnt);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm)
|
||
|
{
|
||
|
char *sep;
|
||
|
char *path, *disp;
|
||
|
unsigned int path_len, disp_len;
|
||
|
bool expl;
|
||
|
|
||
|
sep = rb->strcasestr(line, PATH_DISP_SEPARATOR);
|
||
|
expl = (sep != NULL);
|
||
|
if (expl) {
|
||
|
/* Explicit name for the entry is specified -> use it */
|
||
|
path = line;
|
||
|
path_len = sep - line;
|
||
|
disp = sep + PATH_DISP_SEPARATOR_LEN;
|
||
|
disp_len = rb->strlen(disp);
|
||
|
} else {
|
||
|
/* No special name to display */
|
||
|
path = line;
|
||
|
path_len = rb->strlen(line);
|
||
|
if (last_segm <= 0) {
|
||
|
disp = path;
|
||
|
} else {
|
||
|
disp = last_segments(line, last_segm);
|
||
|
}
|
||
|
disp_len = rb->strlen(disp);
|
||
|
}
|
||
|
|
||
|
if (path_len >= sizeof(entry->path) || disp_len >= sizeof(entry->disp)) {
|
||
|
DEBUGF("Bad entry: pathlen=%d, displen=%d\n", path_len, disp_len);
|
||
|
return false;
|
||
|
}
|
||
|
rb->strncpy(entry->path, path, path_len);
|
||
|
entry->path[path_len] = '\0';
|
||
|
rb->strcpy(entry->disp, disp); /* Safe since we've checked the length */
|
||
|
entry->explicit_disp = expl;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
char *last_segments(char *path, int nsegm)
|
||
|
{
|
||
|
char *p = rb->strrchr(path, PATH_SEPARATOR[0]); /* Hack */
|
||
|
int seg_cnt;
|
||
|
if (p == NULL)
|
||
|
return path; /* No separator??? */
|
||
|
seg_cnt = 0;
|
||
|
while ((p > path) && (seg_cnt < nsegm)) {
|
||
|
p--;
|
||
|
if (!starts_with(p, PATH_SEPARATOR)) {
|
||
|
continue;
|
||
|
}
|
||
|
seg_cnt++;
|
||
|
if (seg_cnt == nsegm && p > path) {
|
||
|
p++; /* Eat the '/' to show that something has been truncated */
|
||
|
}
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool is_control(char *line, sc_file_t *file)
|
||
|
{
|
||
|
char name[MAX_PATH], value[MAX_PATH];
|
||
|
if (!starts_with(line, CONTROL_PREFIX)) {
|
||
|
return false;
|
||
|
}
|
||
|
line += CONTROL_PREFIX_LEN;
|
||
|
|
||
|
if (!parse_name_value(line, name, sizeof(name),
|
||
|
value, sizeof(value))) {
|
||
|
DEBUGF("Bad processing instruction: '%s'\n", line);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/* Process control instruction */
|
||
|
if (rb->strcasestr(name, INSTR_DISPLAY_LAST_SEGMENTS)) {
|
||
|
file->show_last_segments = rb->atoi(value);
|
||
|
DEBUGF("Set show last segms to %d\n", file->show_last_segments);
|
||
|
} else {
|
||
|
/* Unknown instruction -> ignore */
|
||
|
DEBUGF("Unknown processing instruction: '%s'\n", name);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool starts_with(char *string, char *prefix)
|
||
|
{
|
||
|
unsigned int pfx_len = rb->strlen(prefix);
|
||
|
if (rb->strlen(string) < pfx_len)
|
||
|
return false;
|
||
|
return (rb->strncmp(string, prefix, pfx_len) == 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool parse_name_value(char *line, char *name, int namesize,
|
||
|
char *value, int valuesize)
|
||
|
{
|
||
|
char *sep;
|
||
|
int name_len, val_len;
|
||
|
name[0] = value[0] = '\0';
|
||
|
|
||
|
sep = rb->strcasestr(line, NAME_VALUE_SEPARATOR);
|
||
|
if (sep == NULL) {
|
||
|
/* No separator char -> weird instruction */
|
||
|
return false;
|
||
|
}
|
||
|
name_len = sep - line;
|
||
|
if (name_len >= namesize) {
|
||
|
/* Too long name */
|
||
|
return false;
|
||
|
}
|
||
|
rb->strncpy(name, line, name_len);
|
||
|
name[name_len] = '\0';
|
||
|
|
||
|
val_len = rb->strlen(line) - name_len - NAME_VALUE_SEPARATOR_LEN;
|
||
|
if (val_len >= valuesize) {
|
||
|
/* Too long value */
|
||
|
return false;
|
||
|
}
|
||
|
rb->strncpy(value, sep+NAME_VALUE_SEPARATOR_LEN, val_len+1);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool dump_sc_file(sc_file_t *file, char *filename)
|
||
|
{
|
||
|
DEBUGF("Dumping shortcuts to the file '%s'\n", filename);
|
||
|
int fd;
|
||
|
|
||
|
/* ideally, we should just write a new
|
||
|
* entry to the file, but I'm going to
|
||
|
* be lazy, and just re-write the whole
|
||
|
* thing. */
|
||
|
fd = rb->open(filename, O_WRONLY|O_TRUNC);
|
||
|
if (fd < 0) {
|
||
|
rb->splash(HZ*2, "Could not open shortcuts file %s for writing",
|
||
|
filename);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Write instructions
|
||
|
*/
|
||
|
/* Always dump the 'display last segms' settings (even it it's
|
||
|
* not active) so that it can be easily changed without having
|
||
|
* to remember the setting name */
|
||
|
write_int_instruction_to_file(fd,
|
||
|
INSTR_DISPLAY_LAST_SEGMENTS, file->show_last_segments);
|
||
|
|
||
|
int i;
|
||
|
sc_entry_t *entry;
|
||
|
for (i=0, entry=file->entries; i<file->entry_cnt; i++,entry++) {
|
||
|
write_entry_to_file(fd, entry);
|
||
|
}
|
||
|
|
||
|
rb->close(fd);
|
||
|
DEBUGF("Dumped %d entries to the file '%s'\n", file->entry_cnt, filename);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void write_int_instruction_to_file(int fd, char *instr, int value)
|
||
|
{
|
||
|
rb->fdprintf(fd, "%s%s%s%d\n", CONTROL_PREFIX, instr,
|
||
|
NAME_VALUE_SEPARATOR, value);
|
||
|
}
|
||
|
|
||
|
|
||
|
void write_entry_to_file(int fd, sc_entry_t *entry)
|
||
|
{
|
||
|
rb->fdprintf(fd, "%s", entry->path);
|
||
|
if (entry->explicit_disp) {
|
||
|
rb->fdprintf(fd, "%s%s", PATH_DISP_SEPARATOR, entry->disp);
|
||
|
}
|
||
|
rb->fdprintf(fd, "\n");
|
||
|
}
|