X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=obt%2Fddfile.c;h=e32dff3ee4623fb7bacfdc715341705cc29eb91f;hb=159a3cb3b1dcf0b3d1cb3a194a181d4e6aea57a4;hp=9c651e6b87b962c727d51a69a513090d30907bcf;hpb=0b9910b44263fc36590f562768e42c6e5683a46c;p=chaz%2Fopenbox diff --git a/obt/ddfile.c b/obt/ddfile.c index 9c651e6b..e32dff3e 100644 --- a/obt/ddfile.c +++ b/obt/ddfile.c @@ -25,9 +25,21 @@ #include #endif +typedef void (*ObtDDParseGroupFunc)(const gchar *group, + const gchar *key, + const gchar *value); + +typedef struct _ObtDDParseGroup { + gchar *name; + gboolean seen; + ObtDDParseGroupFunc func; +} ObtDDParseGroup; + typedef struct _ObtDDParse { gchar *filename; gulong lineno; + ObtDDParseGroup *group; + GHashTable *group_hash; } ObtDDParse; typedef enum { @@ -39,6 +51,8 @@ typedef enum { } ObtDDDataType; struct _ObtDDFile { + guint ref; + ObtDDFileType type; gchar *name; /*!< Specific name for the object (eg Firefox) */ gchar *generic; /*!< Generic name for the object (eg Web Browser) */ @@ -67,13 +81,22 @@ struct _ObtDDFile { } d; }; +static void group_free(ObtDDParseGroup *g) +{ + g_free(g->name); + g_slice_free(ObtDDParseGroup, g); +} + +/* Displays a warning message including the file name and line number, and + sets the boolean @error to true if it points to a non-NULL address. +*/ static void parse_error(const gchar *m, const ObtDDParse *const parse, gboolean *error) { if (!parse->filename) - g_warning("%s at line %lud of input\n", m, parse->lineno); + g_warning("%s at line %lu of input", m, parse->lineno); else - g_warning("%s at line %lud of file %s\n", + g_warning("%s at line %lu of file %s", m, parse->lineno, parse->filename); if (error) *error = TRUE; } @@ -94,7 +117,8 @@ static gchar* parse_string(const gchar *in, gboolean locale, if (!locale) { end = in + bytes; for (i = in; i < end; ++i) { - if (*i > 127) { + if ((guchar)*i > 126 || (guchar)*i < 32) { + /* non-control character ascii */ end = i; parse_error("Invalid bytes in string", parse, error); break; @@ -126,6 +150,11 @@ static gchar* parse_string(const gchar *in, gboolean locale, } else if (*i == '\\') backslash = TRUE; + else if ((guchar)*i >= 127 || (guchar)*i < 32) { + /* avoid ascii control characters */ + parse_error("Found control character in string", parse, error); + break; + } else { memcpy(o, i, next-i); o += next-i; @@ -154,3 +183,207 @@ static float parse_numeric(const gchar *in, const ObtDDParse *const parse, parse_error("Invalid numeric value", parse, error); return out; } + +gboolean parse_file_line(FILE *f, gchar **buf, gulong *size, gulong *read, + ObtDDParse *parse, gboolean *error) +{ + const gulong BUFMUL = 80; + size_t ret; + gulong i, null; + + if (*size == 0) { + g_assert(*read == 0); + *size = BUFMUL; + *buf = g_new(char, *size); + } + + /* remove everything up to a null zero already in the buffer and shift + the rest to the front */ + null = *size; + for (i = 0; i < *read; ++i) { + if (null < *size) + (*buf)[i-null-1] = (*buf)[i]; + else if ((*buf)[i] == '\0') + null = i; + } + if (null < *size) + *read -= null + 1; + + /* is there already a newline in the buffer? */ + for (i = 0; i < *read; ++i) + if ((*buf)[i] == '\n') { + /* turn it into a null zero and done */ + (*buf)[i] = '\0'; + return TRUE; + } + + /* we need to read some more to find a newline */ + while (TRUE) { + gulong eol; + gchar *newread; + + newread = *buf + *read; + ret = fread(newread, sizeof(char), *size-*read, f); + if (ret < *size - *read && !feof(f)) { + parse_error("Error reading", parse, error); + return FALSE; + } + *read += ret; + + /* strip out null zeros in the input and look for an endofline */ + null = 0; + eol = *size; + for (i = newread-*buf; i < *read; ++i) { + if (null > 0) + (*buf)[i] = (*buf)[i+null]; + if ((*buf)[i] == '\0') { + ++null; + --(*read); + --i; /* try again */ + } + else if ((*buf)[i] == '\n' && eol == *size) { + eol = i; + /* turn it into a null zero */ + (*buf)[i] = '\0'; + } + } + + if (eol != *size) + /* found an endofline, done */ + break; + else if (feof(f) && *read < *size) { + /* found the endoffile, done (if there is space) */ + if (*read > 0) { + /* stick a null zero on if there is test on the last line */ + (*buf)[(*read)++] = '\0'; + } + break; + } + else { + /* read more */ + size += BUFMUL; + *buf = g_renew(char, *buf, *size); + } + } + return *read > 0; +} + +static void parse_group(const gchar *buf, gulong len, + ObtDDParse *parse, gboolean *error) +{ + ObtDDParseGroup *g; + gchar *group; + gulong i; + + /* get the group name */ + group = g_strndup(buf+1, len-2); + for (i = 0; i < len-2; ++i) + if ((guchar)group[i] < 32 || (guchar)group[i] >= 127) { + /* valid ASCII only */ + parse_error("Invalid character found", parse, NULL); + group[i] = '\0'; /* stopping before this character */ + break; + } + + /* make sure it's a new group */ + g = g_hash_table_lookup(parse->group_hash, group); + if (g && g->seen) { + parse_error("Duplicate group found", parse, error); + g_free(group); + return; + } + /* if it's the first group, make sure it's named Desktop Entry */ + else if (!parse->group && strcmp(group, "Desktop Entry") != 0) + { + parse_error("Incorrect group found, " + "expected [Desktop Entry]", + parse, error); + g_free(group); + return; + } + else { + if (!g) { + g = g_slice_new(ObtDDParseGroup); + g->name = group; + g->func = NULL; + g_hash_table_insert(parse->group_hash, group, g); + } + else + g_free(group); + + g->seen = TRUE; + parse->group = g; + g_print("Found group %s\n", g->name); + } +} + +static gboolean parse_file(ObtDDFile *dd, FILE *f, ObtDDParse *parse) +{ + gchar *buf = NULL; + gulong bytes = 0, read = 0; + gboolean error = FALSE; + + while (!error && parse_file_line(f, &buf, &bytes, &read, parse, &error)) { + /* XXX use the string in buf */ + gulong len = strlen(buf); + if (buf[0] == '#' || buf[0] == '\0') + ; /* ignore comment lines */ + else if (buf[0] == '[' && buf[len-1] == ']') + parse_group(buf, len, parse, &error); + ++parse->lineno; + } + + if (buf) g_free(buf); + return !error; +} + +ObtDDFile* obt_ddfile_new_from_file(const gchar *name, GSList *paths) +{ + ObtDDFile *dd; + ObtDDParse parse; + GSList *it; + FILE *f; + + dd = g_slice_new(ObtDDFile); + dd->ref = 1; + + parse.filename = NULL; + parse.lineno = 0; + parse.group = NULL; + /* hashtable keys are group names, value is a ObtDDParseGroup */ + parse.group_hash = g_hash_table_new_full(g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify)group_free); + + f = NULL; + for (it = paths; it && !f; it = g_slist_next(it)) { + gchar *path = g_strdup_printf("%s/%s", (char*)it->data, name); + if ((f = fopen(path, "r"))) { + parse.filename = path; + parse.lineno = 1; + if (!parse_file(dd, f, &parse)) f = NULL; + } + g_free(path); + } + if (!f) { + obt_ddfile_unref(dd); + dd = NULL; + } + + g_hash_table_destroy(parse.group_hash); + + return dd; +} + +void obt_ddfile_ref(ObtDDFile *dd) +{ + ++dd->ref; +} + +void obt_ddfile_unref(ObtDDFile *dd) +{ + if (--dd->ref < 1) { + g_slice_free(ObtDDFile, dd); + } +}