summaryrefslogtreecommitdiff
path: root/obt/ddfile.c
diff options
context:
space:
mode:
authorDana Jansens <danakj@orodu.net>2010-03-25 21:10:45 -0400
committerDana Jansens <danakj@orodu.net>2010-03-25 21:11:15 -0400
commit0d90bd57abe304ffca4bf5cd1a647d30dea882b7 (patch)
tree098011cf34842ab8f99e73d5dc5a40116ed844ec /obt/ddfile.c
parentfbc0597d3fc6c25cec703306fdd10572631030ed (diff)
Add a new ObtLink type, which is going to be a generalization of a .desktop entry.
Move the .desktop file parsing details into obt/ddparse.c, which generates some hash tables holding the data of the file. Add a new obt/link.c which will build an ObtLink from a parsed .desktop file, and may support other ways to create these links in the unforseeable future
Diffstat (limited to 'obt/ddfile.c')
-rw-r--r--obt/ddfile.c571
1 files changed, 0 insertions, 571 deletions
diff --git a/obt/ddfile.c b/obt/ddfile.c
deleted file mode 100644
index 4ef8114d..00000000
--- a/obt/ddfile.c
+++ /dev/null
@@ -1,571 +0,0 @@
-/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
-
- obt/ddfile.c for the Openbox window manager
- Copyright (c) 2009 Dana Jansens
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- See the COPYING file for a copy of the GNU General Public License.
-*/
-
-#include "obt/ddfile.h"
-#include <glib.h>
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-#ifdef HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-typedef struct _ObtDDParse ObtDDParse;
-typedef struct _ObtDDParseGroup ObtDDParseGroup;
-
-typedef void (*ObtDDParseGroupFunc)(gchar *key, const gchar *val,
- ObtDDParse *parse, gboolean *error);
-
-struct _ObtDDParseGroup {
- gchar *name;
- gboolean seen;
- ObtDDParseGroupFunc key_func;
- /* the key is a string (a key inside the group in the .desktop).
- the value is an ObtDDParseValue */
- GHashTable *key_hash;
-};
-
-struct _ObtDDParse {
- gchar *filename;
- gulong lineno;
- ObtDDParseGroup *group;
- /* the key is a group name, the value is a ObtDDParseGroup */
- GHashTable *group_hash;
-};
-
-typedef enum {
- DATA_STRING,
- DATA_LOCALESTRING,
- DATA_STRINGS,
- DATA_LOCALESTRINGS,
- DATA_BOOLEAN,
- DATA_NUMERIC,
- NUM_DATA_TYPES
-} ObtDDDataType;
-
-typedef struct _ObtDDParseValue {
- ObtDDDataType type;
- union _ObtDDParseValueValue {
- gchar *string;
- struct _ObtDDParseValueStrings {
- gchar *s;
- gulong n;
- } strings;
- gboolean boolean;
- gfloat numeric;
- } value;
-} ObtDDParseValue;
-
-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) */
- gchar *comment; /*!< Comment/description to display for the object */
- gchar *icon; /*!< Name/path for an icon for the object */
-
- union _ObtDDFileData {
- struct _ObtDDFileApp {
- gchar *exec; /*!< Executable to run for the app */
- gchar *wdir; /*!< Working dir to run the app in */
- gboolean term; /*!< Run the app in a terminal or not */
- ObtDDFileAppOpen open;
-
- /* XXX gchar**? or something better, a mime struct.. maybe
- glib has something i can use. */
- gchar **mime; /*!< Mime types the app can open */
-
- ObtDDFileAppStartup startup;
- gchar *startup_wmclass;
- } app;
- struct _ObtDDFileLink {
- gchar *url;
- } link;
- struct _ObtDDFileDir {
- } dir;
- } d;
-};
-
-static void value_free(ObtDDParseValue *v)
-{
- switch (v->type) {
- case DATA_STRING:
- case DATA_LOCALESTRING:
- g_free(v->value.string); break;
- case DATA_STRINGS:
- case DATA_LOCALESTRINGS:
- g_free(v->value.strings.s);
- v->value.strings.n = 0;
- break;
- case DATA_BOOLEAN:
- break;
- case DATA_NUMERIC:
- break;
- default:
- g_assert_not_reached();
- }
- g_slice_free(ObtDDParseValue, v);
-}
-
-static ObtDDParseGroup* group_new(gchar *name, ObtDDParseGroupFunc f)
-{
- ObtDDParseGroup *g = g_slice_new(ObtDDParseGroup);
- g->name = name;
- g->key_func = f;
- g->seen = FALSE;
- g->key_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)value_free);
- return g;
-}
-
-static void group_free(ObtDDParseGroup *g)
-{
- g_free(g->name);
- g_hash_table_destroy(g->key_hash);
- 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 %lu of input", m, parse->lineno);
- else
- g_warning("%s at line %lu of file %s",
- m, parse->lineno, parse->filename);
- if (error) *error = TRUE;
-}
-
-/* reads an input string, strips out invalid stuff, and parses
- backslash-stuff
- if @nstrings is not NULL, then it splits the output string at ';'
- characters. they are all returned in the same string with null zeros
- between them, @nstrings is set to the number of such strings.
- */
-static gchar* parse_string(const gchar *in,
- gboolean locale,
- gulong *nstrings,
- const ObtDDParse *const parse,
- gboolean *error)
-{
- const gint bytes = strlen(in);
- gboolean backslash;
- gchar *out, *o;
- const gchar *end, *i;
-
- g_return_val_if_fail(in != NULL, NULL);
-
- if (!locale) {
- end = in + bytes;
- for (i = in; i < end; ++i) {
- if ((guchar)*i > 126 || (guchar)*i < 32) {
- /* non-control character ascii */
- end = i;
- parse_error("Invalid bytes in string", parse, error);
- break;
- }
- }
- }
- else if (!g_utf8_validate(in, bytes, &end))
- parse_error("Invalid bytes in localestring", parse, error);
-
- if (nstrings) *nstrings = 1;
-
- out = g_new(char, bytes + 1);
- i = in; o = out;
- backslash = FALSE;
- while (i < end) {
- const gchar *next = locale ? g_utf8_find_next_char(i, end) : i+1;
- if (backslash) {
- switch(*i) {
- case 's': *o++ = ' '; break;
- case 'n': *o++ = '\n'; break;
- case 't': *o++ = '\t'; break;
- case 'r': *o++ = '\r'; break;
- case ';': *o++ = ';'; break;
- case '\\': *o++ = '\\'; break;
- default:
- parse_error((locale ?
- "Invalid escape sequence in localestring" :
- "Invalid escape sequence in string"),
- parse, error);
- }
- backslash = FALSE;
- }
- else if (*i == '\\')
- backslash = TRUE;
- else if (*i == ';' && nstrings) {
- ++nstrings;
- *o = '\0';
- }
- 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;
- }
- i = next;
- }
- *o = '\0';
- return o;
-}
-
-static gboolean parse_bool(const gchar *in, const ObtDDParse *const parse,
- gboolean *error)
-{
- if (strcmp(in, "true") == 0)
- return TRUE;
- else if (strcmp(in, "false") != 0)
- parse_error("Invalid boolean value", parse, error);
- return FALSE;
-}
-
-static gfloat parse_numeric(const gchar *in, const ObtDDParse *const parse,
- gboolean *error)
-{
- gfloat out = 0;
- if (sscanf(in, "%f", &out) == 0)
- parse_error("Invalid numeric value", parse, error);
- return out;
-}
-
-static void parse_group_desktop_entry(gchar *key, const gchar *val,
- ObtDDParse *parse, gboolean *error)
-{
- ObtDDParseValue v, *pv;
-
- /* figure out value type */
- v.type = NUM_DATA_TYPES;
-
- /* parse the value */
-
- switch (v.type) {
- case DATA_STRING:
- v.value.string = parse_string(val, FALSE, NULL, parse, error);
- g_assert(v.value.string);
- break;
- case DATA_LOCALESTRING:
- v.value.string = parse_string(val, TRUE, NULL, parse, error);
- g_assert(v.value.string);
- break;
- case DATA_STRINGS:
- v.value.strings.s = parse_string(val, FALSE, &v.value.strings.n,
- parse, error);
- g_assert(v.value.strings.s);
- g_assert(v.value.strings.n);
- break;
- case DATA_LOCALESTRINGS:
- v.value.strings.s = parse_string(val, TRUE, &v.value.strings.n,
- parse, error);
- g_assert(v.value.strings.s);
- g_assert(v.value.strings.n);
- break;
- case DATA_BOOLEAN:
- v.value.boolean = parse_bool(val, parse, error);
- break;
- case DATA_NUMERIC:
- v.value.numeric = parse_numeric(val, parse, error);
- break;
- default:
- g_assert_not_reached();
- }
-
- pv = g_slice_new(ObtDDParseValue);
- *pv = v;
- g_hash_table_insert(parse->group->key_hash, key, pv);
-}
-
-static 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 = group_new(group, NULL);
- g_hash_table_insert(parse->group_hash, g->name, g);
- }
- else
- g_free(group);
-
- g->seen = TRUE;
- parse->group = g;
- g_print("Found group %s\n", g->name);
- }
-}
-
-static void parse_key_value(const gchar *buf, gulong len,
- ObtDDParse *parse, gboolean *error)
-{
- gulong i, keyend, valstart, eq;
- char *key;
-
- /* find the end of the key */
- for (i = 0; i < len; ++i)
- if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') ||
- ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z') ||
- ((guchar)buf[i] >= '0' && (guchar)buf[i] <= '9') ||
- ((guchar)buf[i] == '-'))) {
- /* not part of the key */
- keyend = i;
- break;
- }
- if (keyend < 1) {
- parse_error("Empty key", parse, error);
- return;
- }
- /* find the = character */
- for (i = keyend; i < len; ++i) {
- if (buf[i] == '=') {
- eq = i;
- break;
- }
- else if (buf[i] != ' ') {
- parse_error("Invalid character in key name", parse, error);
- return ;
- }
- }
- if (i == len) {
- parse_error("Key without value found", parse, error);
- return;
- }
- /* find the start of the value */
- for (i = eq+1; i < len; ++i)
- if (buf[i] != ' ') {
- valstart = i;
- break;
- }
- if (i == len) {
- parse_error("Empty value found", parse, error);
- return;
- }
-
- key = g_strndup(buf, keyend);
- if (g_hash_table_lookup(parse->group->key_hash, key)) {
- parse_error("Duplicate key found", parse, error);
- g_free(key);
- return;
- }
- g_print("Found key/value %s=%s.\n", key, buf+valstart);
- if (parse->group->key_func)
- parse->group->key_func(key, buf+valstart, parse, error);
-}
-
-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);
- else if (!parse->group)
- /* just ignore keys outside of groups */
- parse_error("Key found before group", parse, NULL);
- else
- /* ignore errors in key-value pairs and continue */
- parse_key_value(buf, len, parse, NULL);
- ++parse->lineno;
- }
-
- if (buf) g_free(buf);
- return !error;
-}
-
-ObtDDFile* obt_ddfile_new_from_file(const gchar *name, GSList *paths)
-{
- ObtDDFile *dd;
- ObtDDParse parse;
- ObtDDParseGroup *desktop_entry;
- GSList *it;
- FILE *f;
- gboolean success;
-
- dd = g_slice_new(ObtDDFile);
- dd->ref = 1;
-
- parse.filename = NULL;
- parse.lineno = 0;
- parse.group = NULL;
- parse.group_hash = g_hash_table_new_full(g_str_hash,
- g_str_equal,
- NULL,
- (GDestroyNotify)group_free);
-
- /* set up the groups (there's only one right now) */
- desktop_entry = group_new(g_strdup("Desktop Entry"),
- parse_group_desktop_entry);
- g_hash_table_insert(parse.group_hash, desktop_entry->name, desktop_entry);
-
- success = FALSE;
- for (it = paths; it && !success; 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;
- success = parse_file(dd, f, &parse);
- fclose(f);
- }
- g_free(path);
- }
- if (!success) {
- 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);
- }
-}