From 63e779e41ca09007af789fff90011860dc69f937 Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Wed, 12 Feb 2003 00:48:29 +0000 Subject: 2003-02-12 Anders Carlsson * bus/Makefile.am: * bus/desktop-file.c: * bus/desktop-file.h: Add a desktop file parser. --- bus/Makefile.am | 2 + bus/desktop-file.c | 588 +++++++++++++++++++++++++++++++++++++++++++++++++++++ bus/desktop-file.h | 35 ++++ 3 files changed, 625 insertions(+) create mode 100644 bus/desktop-file.c create mode 100644 bus/desktop-file.h (limited to 'bus') diff --git a/bus/Makefile.am b/bus/Makefile.am index 0f1e699b..a9dbfa89 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -11,6 +11,8 @@ noinst_LTLIBRARIES=libdbus-daemon.la libdbus_daemon_la_SOURCES= \ connection.c \ connection.h \ + desktop-file.c \ + desktop-file.h \ dispatch.c \ dispatch.h \ driver.c \ diff --git a/bus/desktop-file.c b/bus/desktop-file.c new file mode 100644 index 00000000..d456d7c8 --- /dev/null +++ b/bus/desktop-file.c @@ -0,0 +1,588 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* desktop-file.c .desktop file parser + * + * Copyright (C) 2003 CodeFactory AB + * Copyright (C) 2003 Red Hat Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include "desktop-file.h" + +typedef enum +{ + BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, + BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, + BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS +} BusDesktopParseError; + +typedef struct +{ + char *key; + char *value; +} BusDesktopFileLine; + +typedef struct +{ + char *section_name; + + int n_lines; + BusDesktopFileLine *lines; + int n_allocated_lines; +} BusDesktopFileSection; + +struct BusDesktopFile +{ + int n_sections; + BusDesktopFileSection *sections; + int n_allocated_sections; +}; + +typedef struct +{ + DBusString data; + + BusDesktopFile *desktop_file; + int current_section; + + int pos, len; + int line_num; + +} BusDesktopFileParser; + +#define VALID_KEY_CHAR 1 +#define VALID_LOCALE_CHAR 2 +unsigned char valid[256] = { + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 , + 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , + 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 , + 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , + 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , + 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , +}; + +static void report_error (BusDesktopFileParser *parser, + char *message, + BusDesktopParseError error_code); + +static void +parser_free (BusDesktopFileParser *parser) +{ + bus_desktop_file_free (parser->desktop_file); + + _dbus_string_free (&parser->data); +} + +static void +bus_desktop_file_line_free (BusDesktopFileLine *line) +{ + dbus_free (line->key); + dbus_free (line->value); +} + +static void +bus_desktop_file_section_free (BusDesktopFileSection *section) +{ + int i; + + for (i = 0; i < section->n_lines; i++) + bus_desktop_file_line_free (§ion->lines[i]); + + dbus_free (section->lines); + dbus_free (section->section_name); +} + +void +bus_desktop_file_free (BusDesktopFile *desktop_file) +{ + int i; + + for (i = 0; i < desktop_file->n_sections; i++) + bus_desktop_file_section_free (&desktop_file->sections[i]); + dbus_free (desktop_file->sections); + + dbus_free (desktop_file); +} + +static void +grow_lines_in_section (BusDesktopFileSection *section) +{ + BusDesktopFileLine *lines; + + int new_n_lines; + + if (section->n_allocated_lines == 0) + new_n_lines = 1; + else + new_n_lines = section->n_allocated_lines*2; + + _DBUS_HANDLE_OOM (lines = dbus_realloc (section->lines, + sizeof (BusDesktopFileLine) * new_n_lines)); + section->lines = lines; + + section->n_allocated_lines = new_n_lines; +} + +static void +grow_sections (BusDesktopFile *desktop_file) +{ + int new_n_sections; + BusDesktopFileSection *sections; + + if (desktop_file->n_allocated_sections == 0) + new_n_sections = 1; + else + new_n_sections = desktop_file->n_allocated_sections*2; + + _DBUS_HANDLE_OOM (sections = dbus_realloc (desktop_file->sections, + sizeof (BusDesktopFileSection) * new_n_sections)); + desktop_file->sections = sections; + + desktop_file->n_allocated_sections = new_n_sections; +} + +static char * +unescape_string (const DBusString *str, int pos, int end_pos) +{ + char *retval, *q; + + /* len + 1 is enough, because unescaping never makes the + * string longer */ + _DBUS_HANDLE_OOM (retval = dbus_malloc (end_pos - pos + 1)); + + q = retval; + + while (pos < end_pos) + { + if (_dbus_string_get_byte (str, pos) == 0) + { + /* Found an embedded null */ + dbus_free (retval); + return NULL; + } + + if (_dbus_string_get_byte (str, pos) == '\\') + { + pos ++; + + if (pos >= end_pos) + { + /* Escape at end of string */ + dbus_free (retval); + return NULL; + } + + switch (_dbus_string_get_byte (str, pos)) + { + case 's': + *q++ = ' '; + break; + case 't': + *q++ = '\t'; + break; + case 'n': + *q++ = '\n'; + break; + case 'r': + *q++ = '\r'; + break; + case '\\': + *q++ = '\\'; + break; + default: + /* Invalid escape code */ + dbus_free (retval); + return NULL; + } + pos++; + } + else + { + *q++ =_dbus_string_get_byte (str, pos); + + pos++; + } + } + + *q = 0; + + return retval; +} + +static BusDesktopFileSection* +new_section (BusDesktopFile *desktop_file, + const char *name) +{ + int n; + + if (desktop_file->n_allocated_sections == desktop_file->n_sections) + grow_sections (desktop_file); + + n = desktop_file->n_sections++; + + _DBUS_HANDLE_OOM (desktop_file->sections[n].section_name = _dbus_strdup (name)); + + desktop_file->sections[n].n_lines = 0; + desktop_file->sections[n].lines = NULL; + desktop_file->sections[n].n_allocated_lines = 0; + + grow_lines_in_section (&desktop_file->sections[n]); + + return &desktop_file->sections[n]; +} + +static BusDesktopFileSection* +open_section (BusDesktopFileParser *parser, + char *name) +{ + BusDesktopFileSection *section; + + section = new_section (parser->desktop_file, name); + if (section == NULL) + return NULL; + + parser->current_section = parser->desktop_file->n_sections - 1; + _dbus_assert (&parser->desktop_file->sections[parser->current_section] == section); + + return section; +} + +static BusDesktopFileLine * +new_line (BusDesktopFileParser *parser) +{ + BusDesktopFileSection *section; + BusDesktopFileLine *line; + + section = &parser->desktop_file->sections[parser->current_section]; + + if (section->n_allocated_lines == section->n_lines) + grow_lines_in_section (section); + + line = §ion->lines[section->n_lines++]; + + memset (line, 0, sizeof (BusDesktopFileLine)); + + return line; +} + +static dbus_bool_t +is_blank_line (BusDesktopFileParser *parser) +{ + int p; + char c; + + p = parser->pos; + + c = _dbus_string_get_byte (&parser->data, p); + + while (c && c != '\n') + { + if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')) + return FALSE; + + p++; + c = _dbus_string_get_byte (&parser->data, p); + } + + return TRUE; +} + +static void +parse_comment_or_blank (BusDesktopFileParser *parser) +{ + int line_end; + + if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end)) + line_end = parser->len; + + if (line_end == parser->len) + parser->pos = parser->len; + else + parser->pos = line_end + 1; + + parser->line_num += 1; +} + +static dbus_bool_t +is_valid_section_name (const char *name) +{ + /* 5. Group names may contain all ASCII characters except for control characters and '[' and ']'. */ + + while (*name) + { + if (!((*name >= 'A' && *name <= 'Z') || (*name >= 'a' || *name <= 'z') || + *name == '\n' || *name == '\t')) + return FALSE; + + name++; + } + + return TRUE; +} + +static dbus_bool_t +parse_section_start (BusDesktopFileParser *parser, DBusResultCode *result) +{ + int line_end; + char *section_name; + + if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end)) + line_end = parser->len; + + if (line_end - parser->pos <= 2 || + _dbus_string_get_byte (&parser->data, line_end - 1) != ']') + { + report_error (parser, "Invalid syntax for section header", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX); + parser_free (parser); + return FALSE; + } + + section_name = unescape_string (&parser->data, parser->pos + 1, line_end - 1); + + if (section_name == NULL) + { + report_error (parser, "Invalid escaping in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES); + parser_free (parser); + return FALSE; + } + + if (!is_valid_section_name (section_name)) + { + report_error (parser, "Invalid characters in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS); + parser_free (parser); + dbus_free (section_name); + return FALSE; + } + + if (open_section (parser, section_name) == NULL) + { + dbus_free (section_name); + return FALSE; + } + + if (line_end == parser->len) + parser->pos = parser->len; + else + parser->pos = line_end + 1; + + parser->line_num += 1; + + dbus_free (section_name); + + return TRUE; +} + +static dbus_bool_t +parse_key_value (BusDesktopFileParser *parser, DBusResultCode *result) +{ + int line_end; + int key_start, key_end; + int value_start; + int p; + char *value, *tmp; + DBusString key; + BusDesktopFileLine *line; + + if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end)) + line_end = parser->len; + + p = parser->pos; + key_start = p; + while (p < line_end && + (valid[_dbus_string_get_byte (&parser->data, p)] & VALID_KEY_CHAR)) + p++; + key_end = p; + + if (key_start == key_end) + { + report_error (parser, "Empty key name", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX); + parser_free (parser); + return FALSE; + } + + /* We ignore locales for now */ + + /* Skip space before '=' */ + while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ') + p++; + + if (p < line_end && _dbus_string_get_byte (&parser->data, p) != '=') + { + report_error (parser, "Invalid characters in key name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS); + parser_free (parser); + return FALSE; + } + + if (p == line_end) + { + report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX); + parser_free (parser); + return FALSE; + } + + /* Skip the '=' */ + p++; + + /* Skip space after '=' */ + while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ') + p++; + + value_start = p; + + value = unescape_string (&parser->data, value_start, line_end); + if (value == NULL) + { + report_error (parser, "Invalid escaping in value", BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES); + parser_free (parser); + return FALSE; + } + + line = new_line (parser); + + _DBUS_HANDLE_OOM (_dbus_string_init (&key, key_end - key_start)); + _DBUS_HANDLE_OOM (_dbus_string_copy_len (&parser->data, key_start, key_end - key_start, + &key, 0)); + _DBUS_HANDLE_OOM (_dbus_string_steal_data (&key, &tmp)); + _dbus_string_free (&key); + + line->key = tmp; + line->value = value; + + if (line_end == parser->len) + parser->pos = parser->len; + else + parser->pos = line_end + 1; + + parser->line_num += 1; + + return TRUE; +} + +static void +report_error (BusDesktopFileParser *parser, + char *message, + BusDesktopParseError error_code) +{ + char *section_name = NULL; + + /* FIXME: */ + section_name = NULL; + + if (section_name) + _dbus_verbose ("Error in section %s at line %d: %s\n", section_name, parser->line_num, message); + else + _dbus_verbose ("Error at line %d: %s\n", parser->line_num, message); +} + +#if 0 +static void +dump_desktop_file (BusDesktopFile *file) +{ + int i; + + for (i = 0; i < file->n_sections; i++) + { + int j; + + printf ("[%s]\n", file->sections[i].section_name); + + for (j = 0; j < file->sections[i].n_lines; j++) + { + printf ("%s=%s\n", file->sections[i].lines[j].key, + file->sections[i].lines[j].value); + } + } +} +#endif + +BusDesktopFile * +bus_desktop_file_load (const char *filename, + DBusResultCode *result) +{ + DBusString str, filename_str; + DBusResultCode result_code; + BusDesktopFileParser parser; + + /* FIXME: Check file size so we don't try to load a ridicously large file. */ + + _dbus_string_init_const (&filename_str, filename); + + _DBUS_HANDLE_OOM (_dbus_string_init (&str, _DBUS_INT_MAX)); + + _DBUS_HANDLE_OOM ((result_code = _dbus_file_get_contents (&str, &filename_str)) != + DBUS_RESULT_NO_MEMORY); + + if (result_code != DBUS_RESULT_SUCCESS) + { + _dbus_string_free (&str); + dbus_set_result (result, result_code); + return NULL; + } + + if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str))) + { + _dbus_string_free (&str); + /* FIXME: Use a better error code */ + dbus_set_result (result, result_code); + return NULL; + } + + _DBUS_HANDLE_OOM (parser.desktop_file = dbus_malloc (sizeof (BusDesktopFile))); + + memset (parser.desktop_file, 0, sizeof (BusDesktopFile)); + + parser.data = str; + parser.line_num = 1; + parser.pos = 0; + parser.len = _dbus_string_get_length (&parser.data); + + while (parser.pos < parser.len) + { + if (_dbus_string_get_byte (&parser.data, parser.pos) == '[') + { + if (!parse_section_start (&parser, result)) + return NULL; + } + else if (is_blank_line (&parser) || + _dbus_string_get_byte (&parser.data, parser.pos) == '#') + parse_comment_or_blank (&parser); + else + { + if (!parse_key_value (&parser, result)) + return NULL; + } + } + + _dbus_string_free (&parser.data); + + dbus_set_result (result, DBUS_RESULT_SUCCESS); + + return parser.desktop_file; +} + diff --git a/bus/desktop-file.h b/bus/desktop-file.h new file mode 100644 index 00000000..7ab626ff --- /dev/null +++ b/bus/desktop-file.h @@ -0,0 +1,35 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* desktop-file.h .desktop file parser + * + * Copyright (C) 2003 CodeFactory AB + * + * Licensed under the Academic Free License version 1.2 + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef BUS_DESKTOP_FILE_H +#define BUS_DESKTOP_FILE_H + +#include + +typedef struct BusDesktopFile BusDesktopFile; + +BusDesktopFile *bus_desktop_file_load (const char *filename, + DBusResultCode *result); +void bus_desktop_file_free (BusDesktopFile *file); + + +#endif /* BUS_DESKTOP_FILE_H */ -- cgit