| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | |
| /* desktop-file.c .desktop file parser | |
| * | |
| * Copyright (C) 2003 CodeFactory AB | |
| * Copyright (C) 2003 Red Hat Inc. | |
| * | |
| * Licensed under the Academic Free License version 2.1 | |
| * | |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| * | |
| */ | |
| #include <config.h> | |
| #include <dbus/dbus-sysdeps.h> | |
| #include <dbus/dbus-internals.h> | |
| #include "desktop-file.h" | |
| #include "utils.h" | |
| 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; | |
| }; | |
| /** | |
| * Parser for service files. | |
| */ | |
| typedef struct | |
| { | |
| DBusString data; /**< The data from the file */ | |
| BusDesktopFile *desktop_file; /**< The resulting object */ | |
| int current_section; /**< The current section being parsed */ | |
| int pos; /**< Current position */ | |
| int len; /**< Length */ | |
| int line_num; /**< Current line number */ | |
| } BusDesktopFileParser; | |
| #define VALID_KEY_CHAR 1 | |
| #define VALID_LOCALE_CHAR 2 | |
| static 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, | |
| const char *error_name, | |
| DBusError *error); | |
| 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 dbus_bool_t | |
| 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; | |
| lines = dbus_realloc (section->lines, | |
| sizeof (BusDesktopFileLine) * new_n_lines); | |
| if (lines == NULL) | |
| return FALSE; | |
| section->lines = lines; | |
| section->n_allocated_lines = new_n_lines; | |
| return TRUE; | |
| } | |
| static dbus_bool_t | |
| 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; | |
| sections = dbus_realloc (desktop_file->sections, | |
| sizeof (BusDesktopFileSection) * new_n_sections); | |
| if (sections == NULL) | |
| return FALSE; | |
| desktop_file->sections = sections; | |
| desktop_file->n_allocated_sections = new_n_sections; | |
| return TRUE; | |
| } | |
| static char * | |
| unescape_string (BusDesktopFileParser *parser, | |
| const DBusString *str, | |
| int pos, | |
| int end_pos, | |
| DBusError *error) | |
| { | |
| char *retval, *q; | |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); | |
| /* len + 1 is enough, because unescaping never makes the | |
| * string longer | |
| */ | |
| retval = dbus_malloc (end_pos - pos + 1); | |
| if (retval == NULL) | |
| { | |
| BUS_SET_OOM (error); | |
| return NULL; | |
| } | |
| q = retval; | |
| while (pos < end_pos) | |
| { | |
| if (_dbus_string_get_byte (str, pos) == 0) | |
| { | |
| /* Found an embedded null */ | |
| dbus_free (retval); | |
| report_error (parser, "Text to be unescaped contains embedded nul", | |
| BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error); | |
| return NULL; | |
| } | |
| if (_dbus_string_get_byte (str, pos) == '\\') | |
| { | |
| pos ++; | |
| if (pos >= end_pos) | |
| { | |
| /* Escape at end of string */ | |
| dbus_free (retval); | |
| report_error (parser, "Text to be unescaped ended in \\", | |
| BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error); | |
| 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); | |
| report_error (parser, "Text to be unescaped had invalid escape sequence", | |
| BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error); | |
| 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; | |
| char *name_copy; | |
| if (desktop_file->n_allocated_sections == desktop_file->n_sections) | |
| { | |
| if (!grow_sections (desktop_file)) | |
| return NULL; | |
| } | |
| name_copy = _dbus_strdup (name); | |
| if (name_copy == NULL) | |
| return NULL; | |
| n = desktop_file->n_sections; | |
| desktop_file->sections[n].section_name = name_copy; | |
| desktop_file->sections[n].n_lines = 0; | |
| desktop_file->sections[n].lines = NULL; | |
| desktop_file->sections[n].n_allocated_lines = 0; | |
| if (!grow_lines_in_section (&desktop_file->sections[n])) | |
| { | |
| dbus_free (desktop_file->sections[n].section_name); | |
| desktop_file->sections[n].section_name = NULL; | |
| return NULL; | |
| } | |
| desktop_file->n_sections += 1; | |
| 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) | |
| { | |
| if (!grow_lines_in_section (section)) | |
| return NULL; | |
| } | |
| line = §ion->lines[section->n_lines++]; | |
| _DBUS_ZERO(*line); | |
| 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, eol_len; | |
| if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len)) | |
| line_end = parser->len; | |
| if (line_end == parser->len) | |
| parser->pos = parser->len; | |
| else | |
| parser->pos = line_end + eol_len; | |
| 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, DBusError *error) | |
| { | |
| int line_end, eol_len; | |
| char *section_name; | |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); | |
| if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len)) | |
| 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, error); | |
| parser_free (parser); | |
| return FALSE; | |
| } | |
| section_name = unescape_string (parser, | |
| &parser->data, parser->pos + 1, line_end - 1, | |
| error); | |
| if (section_name == NULL) | |
| { | |
| 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, error); | |
| parser_free (parser); | |
| dbus_free (section_name); | |
| return FALSE; | |
| } | |
| if (open_section (parser, section_name) == NULL) | |
| { | |
| dbus_free (section_name); | |
| parser_free (parser); | |
| BUS_SET_OOM (error); | |
| return FALSE; | |
| } | |
| if (line_end == parser->len) | |
| parser->pos = parser->len; | |
| else | |
| parser->pos = line_end + eol_len; | |
| parser->line_num += 1; | |
| dbus_free (section_name); | |
| return TRUE; | |
| } | |
| static dbus_bool_t | |
| parse_key_value (BusDesktopFileParser *parser, DBusError *error) | |
| { | |
| int line_end, eol_len; | |
| int key_start, key_end; | |
| int value_start; | |
| int p; | |
| char *value, *tmp; | |
| DBusString key; | |
| BusDesktopFileLine *line; | |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); | |
| if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len)) | |
| 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, error); | |
| parser_free (parser); | |
| return FALSE; | |
| } | |
| /* We ignore locales for now */ | |
| if (p < line_end && _dbus_string_get_byte (&parser->data, p) == '[') | |
| { | |
| if (line_end == parser->len) | |
| parser->pos = parser->len; | |
| else | |
| parser->pos = line_end + eol_len; | |
| parser->line_num += 1; | |
| return TRUE; | |
| } | |
| /* 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, error); | |
| parser_free (parser); | |
| return FALSE; | |
| } | |
| if (p == line_end) | |
| { | |
| report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error); | |
| 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, &parser->data, value_start, line_end, error); | |
| if (value == NULL) | |
| { | |
| parser_free (parser); | |
| return FALSE; | |
| } | |
| line = new_line (parser); | |
| if (line == NULL) | |
| { | |
| dbus_free (value); | |
| parser_free (parser); | |
| BUS_SET_OOM (error); | |
| return FALSE; | |
| } | |
| if (!_dbus_string_init (&key)) | |
| { | |
| dbus_free (value); | |
| parser_free (parser); | |
| BUS_SET_OOM (error); | |
| return FALSE; | |
| } | |
| if (!_dbus_string_copy_len (&parser->data, key_start, key_end - key_start, | |
| &key, 0)) | |
| { | |
| _dbus_string_free (&key); | |
| dbus_free (value); | |
| parser_free (parser); | |
| BUS_SET_OOM (error); | |
| return FALSE; | |
| } | |
| if (!_dbus_string_steal_data (&key, &tmp)) | |
| { | |
| _dbus_string_free (&key); | |
| dbus_free (value); | |
| parser_free (parser); | |
| BUS_SET_OOM (error); | |
| return FALSE; | |
| } | |
| _dbus_string_free (&key); | |
| line->key = tmp; | |
| line->value = value; | |
| if (line_end == parser->len) | |
| parser->pos = parser->len; | |
| else | |
| parser->pos = line_end + eol_len; | |
| parser->line_num += 1; | |
| return TRUE; | |
| } | |
| static void | |
| report_error (BusDesktopFileParser *parser, | |
| char *message, | |
| const char *error_name, | |
| DBusError *error) | |
| { | |
| const char *section_name = NULL; | |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); | |
| if (parser->current_section != -1) | |
| section_name = parser->desktop_file->sections[parser->current_section].section_name; | |
| if (section_name) | |
| dbus_set_error (error, error_name, | |
| "Error in section %s at line %d: %s\n", section_name, parser->line_num, message); | |
| else | |
| dbus_set_error (error, error_name, | |
| "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 (DBusString *filename, | |
| DBusError *error) | |
| { | |
| DBusString str; | |
| BusDesktopFileParser parser; | |
| DBusStat sb; | |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); | |
| /* Clearly there's a race here, but it's just to make it unlikely | |
| * that we do something silly, we still handle doing it below. | |
| */ | |
| if (!_dbus_stat (filename, &sb, error)) | |
| return NULL; | |
| if (sb.size > _DBUS_ONE_KILOBYTE * 128) | |
| { | |
| dbus_set_error (error, DBUS_ERROR_FAILED, | |
| "Desktop file size (%ld bytes) is too large", (long) sb.size); | |
| return NULL; | |
| } | |
| if (!_dbus_string_init (&str)) | |
| { | |
| BUS_SET_OOM (error); | |
| return NULL; | |
| } | |
| if (!_dbus_file_get_contents (&str, filename, error)) | |
| { | |
| _dbus_string_free (&str); | |
| return NULL; | |
| } | |
| if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str))) | |
| { | |
| _dbus_string_free (&str); | |
| dbus_set_error (error, DBUS_ERROR_FAILED, | |
| "invalid UTF-8"); | |
| return NULL; | |
| } | |
| parser.desktop_file = dbus_new0 (BusDesktopFile, 1); | |
| if (parser.desktop_file == NULL) | |
| { | |
| _dbus_string_free (&str); | |
| BUS_SET_OOM (error); | |
| return NULL; | |
| } | |
| parser.data = str; | |
| parser.line_num = 1; | |
| parser.pos = 0; | |
| parser.len = _dbus_string_get_length (&parser.data); | |
| parser.current_section = -1; | |
| while (parser.pos < parser.len) | |
| { | |
| if (_dbus_string_get_byte (&parser.data, parser.pos) == '[') | |
| { | |
| if (!parse_section_start (&parser, error)) | |
| { | |
| 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, error)) | |
| { | |
| return NULL; | |
| } | |
| } | |
| } | |
| _dbus_string_free (&parser.data); | |
| return parser.desktop_file; | |
| } | |
| static BusDesktopFileSection * | |
| lookup_section (BusDesktopFile *desktop_file, | |
| const char *section_name) | |
| { | |
| BusDesktopFileSection *section; | |
| int i; | |
| if (section_name == NULL) | |
| return NULL; | |
| for (i = 0; i < desktop_file->n_sections; i ++) | |
| { | |
| section = &desktop_file->sections[i]; | |
| if (strcmp (section->section_name, section_name) == 0) | |
| return section; | |
| } | |
| return NULL; | |
| } | |
| static BusDesktopFileLine * | |
| lookup_line (BusDesktopFile *desktop_file, | |
| BusDesktopFileSection *section, | |
| const char *keyname) | |
| { | |
| BusDesktopFileLine *line; | |
| int i; | |
| for (i = 0; i < section->n_lines; i++) | |
| { | |
| line = §ion->lines[i]; | |
| if (strcmp (line->key, keyname) == 0) | |
| return line; | |
| } | |
| return NULL; | |
| } | |
| dbus_bool_t | |
| bus_desktop_file_get_raw (BusDesktopFile *desktop_file, | |
| const char *section_name, | |
| const char *keyname, | |
| const char **val) | |
| { | |
| BusDesktopFileSection *section; | |
| BusDesktopFileLine *line; | |
| *val = NULL; | |
| section = lookup_section (desktop_file, section_name); | |
| if (!section) | |
| return FALSE; | |
| line = lookup_line (desktop_file, | |
| section, | |
| keyname); | |
| if (!line) | |
| return FALSE; | |
| *val = line->value; | |
| return TRUE; | |
| } | |
| dbus_bool_t | |
| bus_desktop_file_get_string (BusDesktopFile *desktop_file, | |
| const char *section, | |
| const char *keyname, | |
| char **val, | |
| DBusError *error) | |
| { | |
| const char *raw; | |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); | |
| *val = NULL; | |
| if (!bus_desktop_file_get_raw (desktop_file, section, keyname, &raw)) | |
| { | |
| dbus_set_error (error, DBUS_ERROR_FAILED, | |
| "No \"%s\" key in .service file\n", keyname); | |
| return FALSE; | |
| } | |
| *val = _dbus_strdup (raw); | |
| if (*val == NULL) | |
| { | |
| BUS_SET_OOM (error); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } |