Home | History | Annotate | Download | only in bus
      1 /* -*- mode: C; c-file-style: "gnu" -*- */
      2 /* desktop-file.c  .desktop file parser
      3  *
      4  * Copyright (C) 2003  CodeFactory AB
      5  * Copyright (C) 2003  Red Hat Inc.
      6  *
      7  * Licensed under the Academic Free License version 2.1
      8  *
      9  * This program is free software; you can redistribute it and/or modify
     10  * it under the terms of the GNU General Public License as published by
     11  * the Free Software Foundation; either version 2 of the License, or
     12  * (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     22  *
     23  */
     24 #include <dbus/dbus-sysdeps.h>
     25 #include <dbus/dbus-internals.h>
     26 #include "desktop-file.h"
     27 #include "utils.h"
     28 
     29 typedef struct
     30 {
     31   char *key;
     32   char *value;
     33 } BusDesktopFileLine;
     34 
     35 typedef struct
     36 {
     37   char *section_name;
     38 
     39   int n_lines;
     40   BusDesktopFileLine *lines;
     41   int n_allocated_lines;
     42 } BusDesktopFileSection;
     43 
     44 struct BusDesktopFile
     45 {
     46   int n_sections;
     47   BusDesktopFileSection *sections;
     48   int n_allocated_sections;
     49 };
     50 
     51 /**
     52  * Parser for service files.
     53  */
     54 typedef struct
     55 {
     56   DBusString data; /**< The data from the file */
     57 
     58   BusDesktopFile *desktop_file; /**< The resulting object */
     59   int current_section;    /**< The current section being parsed */
     60 
     61   int pos;          /**< Current position */
     62   int len;          /**< Length */
     63   int line_num;     /**< Current line number */
     64 
     65 } BusDesktopFileParser;
     66 
     67 #define VALID_KEY_CHAR 1
     68 #define VALID_LOCALE_CHAR 2
     69 unsigned char valid[256] = {
     70    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     71    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     72    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 ,
     73    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     74    0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
     75    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
     76    0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
     77    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     78    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     79    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     80    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     81    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     82    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     83    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     84    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     85    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
     86 };
     87 
     88 static void report_error (BusDesktopFileParser *parser,
     89 			  char                 *message,
     90 			  const char           *error_name,
     91 			  DBusError            *error);
     92 
     93 static void
     94 parser_free (BusDesktopFileParser *parser)
     95 {
     96   bus_desktop_file_free (parser->desktop_file);
     97 
     98   _dbus_string_free (&parser->data);
     99 }
    100 
    101 static void
    102 bus_desktop_file_line_free (BusDesktopFileLine *line)
    103 {
    104   dbus_free (line->key);
    105   dbus_free (line->value);
    106 }
    107 
    108 static void
    109 bus_desktop_file_section_free (BusDesktopFileSection *section)
    110 {
    111   int i;
    112 
    113   for (i = 0; i < section->n_lines; i++)
    114     bus_desktop_file_line_free (&section->lines[i]);
    115 
    116   dbus_free (section->lines);
    117   dbus_free (section->section_name);
    118 }
    119 
    120 void
    121 bus_desktop_file_free (BusDesktopFile *desktop_file)
    122 {
    123   int i;
    124 
    125   for (i = 0; i < desktop_file->n_sections; i++)
    126     bus_desktop_file_section_free (&desktop_file->sections[i]);
    127   dbus_free (desktop_file->sections);
    128 
    129   dbus_free (desktop_file);
    130 }
    131 
    132 static dbus_bool_t
    133 grow_lines_in_section (BusDesktopFileSection *section)
    134 {
    135   BusDesktopFileLine *lines;
    136 
    137   int new_n_lines;
    138 
    139   if (section->n_allocated_lines == 0)
    140     new_n_lines = 1;
    141   else
    142     new_n_lines = section->n_allocated_lines*2;
    143 
    144   lines = dbus_realloc (section->lines,
    145                         sizeof (BusDesktopFileLine) * new_n_lines);
    146 
    147   if (lines == NULL)
    148     return FALSE;
    149 
    150   section->lines = lines;
    151   section->n_allocated_lines = new_n_lines;
    152 
    153   return TRUE;
    154 }
    155 
    156 static dbus_bool_t
    157 grow_sections (BusDesktopFile *desktop_file)
    158 {
    159   int new_n_sections;
    160   BusDesktopFileSection *sections;
    161 
    162   if (desktop_file->n_allocated_sections == 0)
    163     new_n_sections = 1;
    164   else
    165     new_n_sections = desktop_file->n_allocated_sections*2;
    166 
    167   sections = dbus_realloc (desktop_file->sections,
    168                            sizeof (BusDesktopFileSection) * new_n_sections);
    169   if (sections == NULL)
    170     return FALSE;
    171 
    172   desktop_file->sections = sections;
    173 
    174   desktop_file->n_allocated_sections = new_n_sections;
    175 
    176   return TRUE;
    177 }
    178 
    179 static char *
    180 unescape_string (BusDesktopFileParser *parser,
    181                  const DBusString     *str,
    182                  int                   pos,
    183                  int                   end_pos,
    184                  DBusError            *error)
    185 {
    186   char *retval, *q;
    187 
    188   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    189 
    190   /* len + 1 is enough, because unescaping never makes the
    191    * string longer
    192    */
    193   retval = dbus_malloc (end_pos - pos + 1);
    194   if (retval == NULL)
    195     {
    196       BUS_SET_OOM (error);
    197       return NULL;
    198     }
    199 
    200   q = retval;
    201 
    202   while (pos < end_pos)
    203     {
    204       if (_dbus_string_get_byte (str, pos) == 0)
    205 	{
    206 	  /* Found an embedded null */
    207 	  dbus_free (retval);
    208           report_error (parser, "Text to be unescaped contains embedded nul",
    209                         BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
    210 	  return NULL;
    211 	}
    212 
    213       if (_dbus_string_get_byte (str, pos) == '\\')
    214 	{
    215 	  pos ++;
    216 
    217 	  if (pos >= end_pos)
    218 	    {
    219 	      /* Escape at end of string */
    220 	      dbus_free (retval);
    221               report_error (parser, "Text to be unescaped ended in \\",
    222                             BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
    223 	      return NULL;
    224 	    }
    225 
    226 	  switch (_dbus_string_get_byte (str, pos))
    227 	    {
    228 	    case 's':
    229               *q++ = ' ';
    230               break;
    231            case 't':
    232               *q++ = '\t';
    233               break;
    234            case 'n':
    235               *q++ = '\n';
    236               break;
    237            case 'r':
    238               *q++ = '\r';
    239               break;
    240            case '\\':
    241               *q++ = '\\';
    242               break;
    243            default:
    244 	     /* Invalid escape code */
    245 	     dbus_free (retval);
    246              report_error (parser, "Text to be unescaped had invalid escape sequence",
    247                            BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
    248              return NULL;
    249 	    }
    250 	  pos++;
    251 	}
    252       else
    253 	{
    254 	  *q++ =_dbus_string_get_byte (str, pos);
    255 
    256 	  pos++;
    257 	}
    258     }
    259 
    260   *q = 0;
    261 
    262   return retval;
    263 }
    264 
    265 static BusDesktopFileSection*
    266 new_section (BusDesktopFile *desktop_file,
    267              const char     *name)
    268 {
    269   int n;
    270   char *name_copy;
    271 
    272   if (desktop_file->n_allocated_sections == desktop_file->n_sections)
    273     {
    274       if (!grow_sections (desktop_file))
    275         return NULL;
    276     }
    277 
    278   name_copy = _dbus_strdup (name);
    279   if (name_copy == NULL)
    280     return NULL;
    281 
    282   n = desktop_file->n_sections;
    283   desktop_file->sections[n].section_name = name_copy;
    284 
    285   desktop_file->sections[n].n_lines = 0;
    286   desktop_file->sections[n].lines = NULL;
    287   desktop_file->sections[n].n_allocated_lines = 0;
    288 
    289   if (!grow_lines_in_section (&desktop_file->sections[n]))
    290     {
    291       dbus_free (desktop_file->sections[n].section_name);
    292       desktop_file->sections[n].section_name = NULL;
    293       return NULL;
    294     }
    295 
    296   desktop_file->n_sections += 1;
    297 
    298   return &desktop_file->sections[n];
    299 }
    300 
    301 static BusDesktopFileSection*
    302 open_section (BusDesktopFileParser *parser,
    303               char                 *name)
    304 {
    305   BusDesktopFileSection *section;
    306 
    307   section = new_section (parser->desktop_file, name);
    308   if (section == NULL)
    309     return NULL;
    310 
    311   parser->current_section = parser->desktop_file->n_sections - 1;
    312   _dbus_assert (&parser->desktop_file->sections[parser->current_section] == section);
    313 
    314   return section;
    315 }
    316 
    317 static BusDesktopFileLine *
    318 new_line (BusDesktopFileParser *parser)
    319 {
    320   BusDesktopFileSection *section;
    321   BusDesktopFileLine *line;
    322 
    323   section = &parser->desktop_file->sections[parser->current_section];
    324 
    325   if (section->n_allocated_lines == section->n_lines)
    326     {
    327       if (!grow_lines_in_section (section))
    328         return NULL;
    329     }
    330 
    331   line = &section->lines[section->n_lines++];
    332 
    333   memset (line, 0, sizeof (BusDesktopFileLine));
    334 
    335   return line;
    336 }
    337 
    338 static dbus_bool_t
    339 is_blank_line (BusDesktopFileParser *parser)
    340 {
    341   int p;
    342   char c;
    343 
    344   p = parser->pos;
    345 
    346   c = _dbus_string_get_byte (&parser->data, p);
    347 
    348   while (c && c != '\n')
    349     {
    350       if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'))
    351 	return FALSE;
    352 
    353       p++;
    354       c = _dbus_string_get_byte (&parser->data, p);
    355     }
    356 
    357   return TRUE;
    358 }
    359 
    360 static void
    361 parse_comment_or_blank (BusDesktopFileParser *parser)
    362 {
    363   int line_end;
    364 
    365   if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end))
    366     line_end = parser->len;
    367 
    368   if (line_end == parser->len)
    369     parser->pos = parser->len;
    370   else
    371     parser->pos = line_end + 1;
    372 
    373   parser->line_num += 1;
    374 }
    375 
    376 static dbus_bool_t
    377 is_valid_section_name (const char *name)
    378 {
    379   /* 5. Group names may contain all ASCII characters except for control characters and '[' and ']'. */
    380 
    381   while (*name)
    382     {
    383       if (!((*name >= 'A' && *name <= 'Z') || (*name >= 'a' || *name <= 'z') ||
    384 	    *name == '\n' || *name == '\t'))
    385 	return FALSE;
    386 
    387       name++;
    388     }
    389 
    390   return TRUE;
    391 }
    392 
    393 static dbus_bool_t
    394 parse_section_start (BusDesktopFileParser *parser, DBusError *error)
    395 {
    396   int line_end;
    397   char *section_name;
    398 
    399   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    400 
    401   if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end))
    402     line_end = parser->len;
    403 
    404   if (line_end - parser->pos <= 2 ||
    405       _dbus_string_get_byte (&parser->data, line_end - 1) != ']')
    406     {
    407       report_error (parser, "Invalid syntax for section header", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
    408       parser_free (parser);
    409       return FALSE;
    410     }
    411 
    412   section_name = unescape_string (parser,
    413                                   &parser->data, parser->pos + 1, line_end - 1,
    414                                   error);
    415 
    416   if (section_name == NULL)
    417     {
    418       parser_free (parser);
    419       return FALSE;
    420     }
    421 
    422   if (!is_valid_section_name (section_name))
    423     {
    424       report_error (parser, "Invalid characters in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
    425       parser_free (parser);
    426       dbus_free (section_name);
    427       return FALSE;
    428     }
    429 
    430   if (open_section (parser, section_name) == NULL)
    431     {
    432       dbus_free (section_name);
    433       parser_free (parser);
    434       BUS_SET_OOM (error);
    435       return FALSE;
    436     }
    437 
    438   if (line_end == parser->len)
    439     parser->pos = parser->len;
    440   else
    441     parser->pos = line_end + 1;
    442 
    443   parser->line_num += 1;
    444 
    445   dbus_free (section_name);
    446 
    447   return TRUE;
    448 }
    449 
    450 static dbus_bool_t
    451 parse_key_value (BusDesktopFileParser *parser, DBusError *error)
    452 {
    453   int line_end;
    454   int key_start, key_end;
    455   int value_start;
    456   int p;
    457   char *value, *tmp;
    458   DBusString key;
    459   BusDesktopFileLine *line;
    460 
    461   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    462 
    463   if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end))
    464     line_end = parser->len;
    465 
    466   p = parser->pos;
    467   key_start = p;
    468   while (p < line_end &&
    469 	 (valid[_dbus_string_get_byte (&parser->data, p)] & VALID_KEY_CHAR))
    470     p++;
    471   key_end = p;
    472 
    473   if (key_start == key_end)
    474     {
    475       report_error (parser, "Empty key name", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
    476       parser_free (parser);
    477       return FALSE;
    478     }
    479 
    480   /* We ignore locales for now */
    481   if (p < line_end && _dbus_string_get_byte (&parser->data, p) == '[')
    482     {
    483       if (line_end == parser->len)
    484 	parser->pos = parser->len;
    485       else
    486 	parser->pos = line_end + 1;
    487 
    488       parser->line_num += 1;
    489 
    490       return TRUE;
    491     }
    492 
    493   /* Skip space before '=' */
    494   while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
    495     p++;
    496 
    497   if (p < line_end && _dbus_string_get_byte (&parser->data, p) != '=')
    498     {
    499       report_error (parser, "Invalid characters in key name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
    500       parser_free (parser);
    501       return FALSE;
    502     }
    503 
    504   if (p == line_end)
    505     {
    506       report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
    507       parser_free (parser);
    508       return FALSE;
    509     }
    510 
    511   /* Skip the '=' */
    512   p++;
    513 
    514   /* Skip space after '=' */
    515   while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
    516     p++;
    517 
    518   value_start = p;
    519 
    520   value = unescape_string (parser, &parser->data, value_start, line_end, error);
    521   if (value == NULL)
    522     {
    523       parser_free (parser);
    524       return FALSE;
    525     }
    526 
    527   line = new_line (parser);
    528   if (line == NULL)
    529     {
    530       dbus_free (value);
    531       parser_free (parser);
    532       BUS_SET_OOM (error);
    533       return FALSE;
    534     }
    535 
    536   if (!_dbus_string_init (&key))
    537     {
    538       dbus_free (value);
    539       parser_free (parser);
    540       BUS_SET_OOM (error);
    541       return FALSE;
    542     }
    543 
    544   if (!_dbus_string_copy_len (&parser->data, key_start, key_end - key_start,
    545                               &key, 0))
    546     {
    547       _dbus_string_free (&key);
    548       dbus_free (value);
    549       parser_free (parser);
    550       BUS_SET_OOM (error);
    551       return FALSE;
    552     }
    553 
    554   if (!_dbus_string_steal_data (&key, &tmp))
    555     {
    556       _dbus_string_free (&key);
    557       dbus_free (value);
    558       parser_free (parser);
    559       BUS_SET_OOM (error);
    560       return FALSE;
    561     }
    562 
    563   _dbus_string_free (&key);
    564 
    565   line->key = tmp;
    566   line->value = value;
    567 
    568   if (line_end == parser->len)
    569     parser->pos = parser->len;
    570   else
    571     parser->pos = line_end + 1;
    572 
    573   parser->line_num += 1;
    574 
    575   return TRUE;
    576 }
    577 
    578 static void
    579 report_error (BusDesktopFileParser *parser,
    580 	      char                 *message,
    581 	      const char           *error_name,
    582 	      DBusError            *error)
    583 {
    584   const char *section_name = NULL;
    585 
    586   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    587 
    588   if (parser->current_section != -1)
    589     section_name = parser->desktop_file->sections[parser->current_section].section_name;
    590 
    591   if (section_name)
    592     dbus_set_error (error, error_name,
    593                     "Error in section %s at line %d: %s\n", section_name, parser->line_num, message);
    594   else
    595     dbus_set_error (error, error_name,
    596                     "Error at line %d: %s\n", parser->line_num, message);
    597 }
    598 
    599 #if 0
    600 static void
    601 dump_desktop_file (BusDesktopFile *file)
    602 {
    603   int i;
    604 
    605   for (i = 0; i < file->n_sections; i++)
    606     {
    607       int j;
    608 
    609       printf ("[%s]\n", file->sections[i].section_name);
    610 
    611       for (j = 0; j < file->sections[i].n_lines; j++)
    612 	{
    613 	  printf ("%s=%s\n", file->sections[i].lines[j].key,
    614 		  file->sections[i].lines[j].value);
    615 	}
    616     }
    617 }
    618 #endif
    619 
    620 BusDesktopFile*
    621 bus_desktop_file_load (DBusString *filename,
    622 		       DBusError  *error)
    623 {
    624   DBusString str;
    625   BusDesktopFileParser parser;
    626   DBusStat sb;
    627 
    628   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    629 
    630   /* Clearly there's a race here, but it's just to make it unlikely
    631    * that we do something silly, we still handle doing it below.
    632    */
    633   if (!_dbus_stat (filename, &sb, error))
    634     return NULL;
    635 
    636   if (sb.size > _DBUS_ONE_KILOBYTE * 128)
    637     {
    638       dbus_set_error (error, DBUS_ERROR_FAILED,
    639                       "Desktop file size (%ld bytes) is too large", (long) sb.size);
    640       return NULL;
    641     }
    642 
    643   if (!_dbus_string_init (&str))
    644     {
    645       BUS_SET_OOM (error);
    646       return NULL;
    647     }
    648 
    649   if (!_dbus_file_get_contents (&str, filename, error))
    650     {
    651       _dbus_string_free (&str);
    652       return NULL;
    653     }
    654 
    655   if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str)))
    656     {
    657       _dbus_string_free (&str);
    658       dbus_set_error (error, DBUS_ERROR_FAILED,
    659                       "invalid UTF-8");
    660       return NULL;
    661     }
    662 
    663   parser.desktop_file = dbus_new0 (BusDesktopFile, 1);
    664   if (parser.desktop_file == NULL)
    665     {
    666       _dbus_string_free (&str);
    667       BUS_SET_OOM (error);
    668       return NULL;
    669     }
    670 
    671   parser.data = str;
    672   parser.line_num = 1;
    673   parser.pos = 0;
    674   parser.len = _dbus_string_get_length (&parser.data);
    675   parser.current_section = -1;
    676 
    677   while (parser.pos < parser.len)
    678     {
    679       if (_dbus_string_get_byte (&parser.data, parser.pos) == '[')
    680 	{
    681 	  if (!parse_section_start (&parser, error))
    682             {
    683               return NULL;
    684             }
    685 	}
    686       else if (is_blank_line (&parser) ||
    687 	       _dbus_string_get_byte (&parser.data, parser.pos) == '#')
    688 	parse_comment_or_blank (&parser);
    689       else
    690 	{
    691 	  if (!parse_key_value (&parser, error))
    692             {
    693               return NULL;
    694             }
    695 	}
    696     }
    697 
    698   _dbus_string_free (&parser.data);
    699 
    700   return parser.desktop_file;
    701 }
    702 
    703 static BusDesktopFileSection *
    704 lookup_section (BusDesktopFile *desktop_file,
    705 		const char     *section_name)
    706 {
    707   BusDesktopFileSection *section;
    708   int i;
    709 
    710   if (section_name == NULL)
    711     return NULL;
    712 
    713   for (i = 0; i < desktop_file->n_sections; i ++)
    714     {
    715       section = &desktop_file->sections[i];
    716 
    717       if (strcmp (section->section_name, section_name) == 0)
    718 	return section;
    719     }
    720 
    721   return NULL;
    722 }
    723 
    724 static BusDesktopFileLine *
    725 lookup_line (BusDesktopFile        *desktop_file,
    726 	     BusDesktopFileSection *section,
    727 	     const char            *keyname)
    728 {
    729   BusDesktopFileLine *line;
    730   int i;
    731 
    732   for (i = 0; i < section->n_lines; i++)
    733     {
    734       line = &section->lines[i];
    735 
    736       if (strcmp (line->key, keyname) == 0)
    737 	return line;
    738     }
    739 
    740   return NULL;
    741 }
    742 
    743 dbus_bool_t
    744 bus_desktop_file_get_raw (BusDesktopFile  *desktop_file,
    745 			  const char      *section_name,
    746 			  const char      *keyname,
    747 			  const char     **val)
    748 {
    749   BusDesktopFileSection *section;
    750   BusDesktopFileLine *line;
    751 
    752   *val = NULL;
    753 
    754   section = lookup_section (desktop_file, section_name);
    755 
    756   if (!section)
    757     return FALSE;
    758 
    759   line = lookup_line (desktop_file,
    760 		      section,
    761 		      keyname);
    762 
    763   if (!line)
    764     return FALSE;
    765 
    766   *val = line->value;
    767 
    768   return TRUE;
    769 }
    770 
    771 dbus_bool_t
    772 bus_desktop_file_get_string (BusDesktopFile  *desktop_file,
    773 			     const char      *section,
    774 			     const char      *keyname,
    775 			     char           **val,
    776 			     DBusError       *error)
    777 {
    778   const char *raw;
    779 
    780   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    781 
    782   *val = NULL;
    783 
    784   if (!bus_desktop_file_get_raw (desktop_file, section, keyname, &raw))
    785     {
    786       dbus_set_error (error, DBUS_ERROR_FAILED,
    787                       "No \"%s\" key in .service file\n", keyname);
    788       return FALSE;
    789     }
    790 
    791   *val = _dbus_strdup (raw);
    792 
    793   if (*val == NULL)
    794     {
    795       BUS_SET_OOM (error);
    796       return FALSE;
    797     }
    798 
    799   return TRUE;
    800 }
    801