00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <dbus/dbus-sysdeps.h>
00025 #include <dbus/dbus-internals.h>
00026 #include "desktop-file.h"
00027 #include "utils.h"
00028
00029 typedef struct
00030 {
00031 char *key;
00032 char *value;
00033 } BusDesktopFileLine;
00034
00035 typedef struct
00036 {
00037 char *section_name;
00038
00039 int n_lines;
00040 BusDesktopFileLine *lines;
00041 int n_allocated_lines;
00042 } BusDesktopFileSection;
00043
00044 struct BusDesktopFile
00045 {
00046 int n_sections;
00047 BusDesktopFileSection *sections;
00048 int n_allocated_sections;
00049 };
00050
00054 typedef struct
00055 {
00056 DBusString data;
00058 BusDesktopFile *desktop_file;
00059 int current_section;
00061 int pos;
00062 int len;
00063 int line_num;
00065 } BusDesktopFileParser;
00066
00067 #define VALID_KEY_CHAR 1
00068 #define VALID_LOCALE_CHAR 2
00069 unsigned char valid[256] = {
00070 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00071 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00072 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 ,
00073 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00074 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
00075 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
00076 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
00077 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00078 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00079 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00080 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00081 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00082 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00083 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00084 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00085 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
00086 };
00087
00088 static void report_error (BusDesktopFileParser *parser,
00089 char *message,
00090 const char *error_name,
00091 DBusError *error);
00092
00093 static void
00094 parser_free (BusDesktopFileParser *parser)
00095 {
00096 bus_desktop_file_free (parser->desktop_file);
00097
00098 _dbus_string_free (&parser->data);
00099 }
00100
00101 static void
00102 bus_desktop_file_line_free (BusDesktopFileLine *line)
00103 {
00104 dbus_free (line->key);
00105 dbus_free (line->value);
00106 }
00107
00108 static void
00109 bus_desktop_file_section_free (BusDesktopFileSection *section)
00110 {
00111 int i;
00112
00113 for (i = 0; i < section->n_lines; i++)
00114 bus_desktop_file_line_free (§ion->lines[i]);
00115
00116 dbus_free (section->lines);
00117 dbus_free (section->section_name);
00118 }
00119
00120 void
00121 bus_desktop_file_free (BusDesktopFile *desktop_file)
00122 {
00123 int i;
00124
00125 for (i = 0; i < desktop_file->n_sections; i++)
00126 bus_desktop_file_section_free (&desktop_file->sections[i]);
00127 dbus_free (desktop_file->sections);
00128
00129 dbus_free (desktop_file);
00130 }
00131
00132 static dbus_bool_t
00133 grow_lines_in_section (BusDesktopFileSection *section)
00134 {
00135 BusDesktopFileLine *lines;
00136
00137 int new_n_lines;
00138
00139 if (section->n_allocated_lines == 0)
00140 new_n_lines = 1;
00141 else
00142 new_n_lines = section->n_allocated_lines*2;
00143
00144 lines = dbus_realloc (section->lines,
00145 sizeof (BusDesktopFileLine) * new_n_lines);
00146
00147 if (lines == NULL)
00148 return FALSE;
00149
00150 section->lines = lines;
00151 section->n_allocated_lines = new_n_lines;
00152
00153 return TRUE;
00154 }
00155
00156 static dbus_bool_t
00157 grow_sections (BusDesktopFile *desktop_file)
00158 {
00159 int new_n_sections;
00160 BusDesktopFileSection *sections;
00161
00162 if (desktop_file->n_allocated_sections == 0)
00163 new_n_sections = 1;
00164 else
00165 new_n_sections = desktop_file->n_allocated_sections*2;
00166
00167 sections = dbus_realloc (desktop_file->sections,
00168 sizeof (BusDesktopFileSection) * new_n_sections);
00169 if (sections == NULL)
00170 return FALSE;
00171
00172 desktop_file->sections = sections;
00173
00174 desktop_file->n_allocated_sections = new_n_sections;
00175
00176 return TRUE;
00177 }
00178
00179 static char *
00180 unescape_string (BusDesktopFileParser *parser,
00181 const DBusString *str,
00182 int pos,
00183 int end_pos,
00184 DBusError *error)
00185 {
00186 char *retval, *q;
00187
00188 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00189
00190
00191
00192
00193 retval = dbus_malloc (end_pos - pos + 1);
00194 if (retval == NULL)
00195 {
00196 BUS_SET_OOM (error);
00197 return NULL;
00198 }
00199
00200 q = retval;
00201
00202 while (pos < end_pos)
00203 {
00204 if (_dbus_string_get_byte (str, pos) == 0)
00205 {
00206
00207 dbus_free (retval);
00208 report_error (parser, "Text to be unescaped contains embedded nul",
00209 BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
00210 return NULL;
00211 }
00212
00213 if (_dbus_string_get_byte (str, pos) == '\\')
00214 {
00215 pos ++;
00216
00217 if (pos >= end_pos)
00218 {
00219
00220 dbus_free (retval);
00221 report_error (parser, "Text to be unescaped ended in \\",
00222 BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
00223 return NULL;
00224 }
00225
00226 switch (_dbus_string_get_byte (str, pos))
00227 {
00228 case 's':
00229 *q++ = ' ';
00230 break;
00231 case 't':
00232 *q++ = '\t';
00233 break;
00234 case 'n':
00235 *q++ = '\n';
00236 break;
00237 case 'r':
00238 *q++ = '\r';
00239 break;
00240 case '\\':
00241 *q++ = '\\';
00242 break;
00243 default:
00244
00245 dbus_free (retval);
00246 report_error (parser, "Text to be unescaped had invalid escape sequence",
00247 BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
00248 return NULL;
00249 }
00250 pos++;
00251 }
00252 else
00253 {
00254 *q++ =_dbus_string_get_byte (str, pos);
00255
00256 pos++;
00257 }
00258 }
00259
00260 *q = 0;
00261
00262 return retval;
00263 }
00264
00265 static BusDesktopFileSection*
00266 new_section (BusDesktopFile *desktop_file,
00267 const char *name)
00268 {
00269 int n;
00270 char *name_copy;
00271
00272 if (desktop_file->n_allocated_sections == desktop_file->n_sections)
00273 {
00274 if (!grow_sections (desktop_file))
00275 return NULL;
00276 }
00277
00278 name_copy = _dbus_strdup (name);
00279 if (name_copy == NULL)
00280 return NULL;
00281
00282 n = desktop_file->n_sections;
00283 desktop_file->sections[n].section_name = name_copy;
00284
00285 desktop_file->sections[n].n_lines = 0;
00286 desktop_file->sections[n].lines = NULL;
00287 desktop_file->sections[n].n_allocated_lines = 0;
00288
00289 if (!grow_lines_in_section (&desktop_file->sections[n]))
00290 {
00291 dbus_free (desktop_file->sections[n].section_name);
00292 desktop_file->sections[n].section_name = NULL;
00293 return NULL;
00294 }
00295
00296 desktop_file->n_sections += 1;
00297
00298 return &desktop_file->sections[n];
00299 }
00300
00301 static BusDesktopFileSection*
00302 open_section (BusDesktopFileParser *parser,
00303 char *name)
00304 {
00305 BusDesktopFileSection *section;
00306
00307 section = new_section (parser->desktop_file, name);
00308 if (section == NULL)
00309 return NULL;
00310
00311 parser->current_section = parser->desktop_file->n_sections - 1;
00312 _dbus_assert (&parser->desktop_file->sections[parser->current_section] == section);
00313
00314 return section;
00315 }
00316
00317 static BusDesktopFileLine *
00318 new_line (BusDesktopFileParser *parser)
00319 {
00320 BusDesktopFileSection *section;
00321 BusDesktopFileLine *line;
00322
00323 section = &parser->desktop_file->sections[parser->current_section];
00324
00325 if (section->n_allocated_lines == section->n_lines)
00326 {
00327 if (!grow_lines_in_section (section))
00328 return NULL;
00329 }
00330
00331 line = §ion->lines[section->n_lines++];
00332
00333 memset (line, 0, sizeof (BusDesktopFileLine));
00334
00335 return line;
00336 }
00337
00338 static dbus_bool_t
00339 is_blank_line (BusDesktopFileParser *parser)
00340 {
00341 int p;
00342 char c;
00343
00344 p = parser->pos;
00345
00346 c = _dbus_string_get_byte (&parser->data, p);
00347
00348 while (c && c != '\n')
00349 {
00350 if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'))
00351 return FALSE;
00352
00353 p++;
00354 c = _dbus_string_get_byte (&parser->data, p);
00355 }
00356
00357 return TRUE;
00358 }
00359
00360 static void
00361 parse_comment_or_blank (BusDesktopFileParser *parser)
00362 {
00363 int line_end;
00364
00365 if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end))
00366 line_end = parser->len;
00367
00368 if (line_end == parser->len)
00369 parser->pos = parser->len;
00370 else
00371 parser->pos = line_end + 1;
00372
00373 parser->line_num += 1;
00374 }
00375
00376 static dbus_bool_t
00377 is_valid_section_name (const char *name)
00378 {
00379
00380
00381 while (*name)
00382 {
00383 if (!((*name >= 'A' && *name <= 'Z') || (*name >= 'a' || *name <= 'z') ||
00384 *name == '\n' || *name == '\t'))
00385 return FALSE;
00386
00387 name++;
00388 }
00389
00390 return TRUE;
00391 }
00392
00393 static dbus_bool_t
00394 parse_section_start (BusDesktopFileParser *parser, DBusError *error)
00395 {
00396 int line_end;
00397 char *section_name;
00398
00399 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00400
00401 if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end))
00402 line_end = parser->len;
00403
00404 if (line_end - parser->pos <= 2 ||
00405 _dbus_string_get_byte (&parser->data, line_end - 1) != ']')
00406 {
00407 report_error (parser, "Invalid syntax for section header", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
00408 parser_free (parser);
00409 return FALSE;
00410 }
00411
00412 section_name = unescape_string (parser,
00413 &parser->data, parser->pos + 1, line_end - 1,
00414 error);
00415
00416 if (section_name == NULL)
00417 {
00418 parser_free (parser);
00419 return FALSE;
00420 }
00421
00422 if (!is_valid_section_name (section_name))
00423 {
00424 report_error (parser, "Invalid characters in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
00425 parser_free (parser);
00426 dbus_free (section_name);
00427 return FALSE;
00428 }
00429
00430 if (open_section (parser, section_name) == NULL)
00431 {
00432 dbus_free (section_name);
00433 return FALSE;
00434 }
00435
00436 if (line_end == parser->len)
00437 parser->pos = parser->len;
00438 else
00439 parser->pos = line_end + 1;
00440
00441 parser->line_num += 1;
00442
00443 dbus_free (section_name);
00444
00445 return TRUE;
00446 }
00447
00448 static dbus_bool_t
00449 parse_key_value (BusDesktopFileParser *parser, DBusError *error)
00450 {
00451 int line_end;
00452 int key_start, key_end;
00453 int value_start;
00454 int p;
00455 char *value, *tmp;
00456 DBusString key;
00457 BusDesktopFileLine *line;
00458
00459 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00460
00461 if (!_dbus_string_find (&parser->data, parser->pos, "\n", &line_end))
00462 line_end = parser->len;
00463
00464 p = parser->pos;
00465 key_start = p;
00466 while (p < line_end &&
00467 (valid[_dbus_string_get_byte (&parser->data, p)] & VALID_KEY_CHAR))
00468 p++;
00469 key_end = p;
00470
00471 if (key_start == key_end)
00472 {
00473 report_error (parser, "Empty key name", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
00474 parser_free (parser);
00475 return FALSE;
00476 }
00477
00478
00479
00480
00481 while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
00482 p++;
00483
00484 if (p < line_end && _dbus_string_get_byte (&parser->data, p) != '=')
00485 {
00486 report_error (parser, "Invalid characters in key name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
00487 parser_free (parser);
00488 return FALSE;
00489 }
00490
00491 if (p == line_end)
00492 {
00493 report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
00494 parser_free (parser);
00495 return FALSE;
00496 }
00497
00498
00499 p++;
00500
00501
00502 while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
00503 p++;
00504
00505 value_start = p;
00506
00507 value = unescape_string (parser, &parser->data, value_start, line_end, error);
00508 if (value == NULL)
00509 {
00510 parser_free (parser);
00511 return FALSE;
00512 }
00513
00514 line = new_line (parser);
00515 if (line == NULL)
00516 {
00517 parser_free (parser);
00518 return FALSE;
00519 }
00520
00521 if (!_dbus_string_init (&key))
00522 {
00523 parser_free (parser);
00524 return FALSE;
00525 }
00526
00527 if (!_dbus_string_copy_len (&parser->data, key_start, key_end - key_start,
00528 &key, 0))
00529 {
00530 parser_free (parser);
00531 return FALSE;
00532 }
00533
00534 if (!_dbus_string_steal_data (&key, &tmp))
00535 {
00536 parser_free (parser);
00537 return FALSE;
00538 }
00539
00540 _dbus_string_free (&key);
00541
00542 line->key = tmp;
00543 line->value = value;
00544
00545 if (line_end == parser->len)
00546 parser->pos = parser->len;
00547 else
00548 parser->pos = line_end + 1;
00549
00550 parser->line_num += 1;
00551
00552 return TRUE;
00553 }
00554
00555 static void
00556 report_error (BusDesktopFileParser *parser,
00557 char *message,
00558 const char *error_name,
00559 DBusError *error)
00560 {
00561 const char *section_name = NULL;
00562
00563 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00564
00565 if (parser->current_section != -1)
00566 section_name = parser->desktop_file->sections[parser->current_section].section_name;
00567
00568 if (section_name)
00569 dbus_set_error (error, error_name,
00570 "Error in section %s at line %d: %s\n", section_name, parser->line_num, message);
00571 else
00572 dbus_set_error (error, error_name,
00573 "Error at line %d: %s\n", parser->line_num, message);
00574 }
00575
00576 #if 0
00577 static void
00578 dump_desktop_file (BusDesktopFile *file)
00579 {
00580 int i;
00581
00582 for (i = 0; i < file->n_sections; i++)
00583 {
00584 int j;
00585
00586 printf ("[%s]\n", file->sections[i].section_name);
00587
00588 for (j = 0; j < file->sections[i].n_lines; j++)
00589 {
00590 printf ("%s=%s\n", file->sections[i].lines[j].key,
00591 file->sections[i].lines[j].value);
00592 }
00593 }
00594 }
00595 #endif
00596
00597 BusDesktopFile*
00598 bus_desktop_file_load (DBusString *filename,
00599 DBusError *error)
00600 {
00601 DBusString str;
00602 BusDesktopFileParser parser;
00603 DBusStat sb;
00604
00605 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00606
00607
00608
00609
00610 if (!_dbus_stat (filename, &sb, error))
00611 return NULL;
00612
00613 if (sb.size > _DBUS_ONE_KILOBYTE * 128)
00614 {
00615 dbus_set_error (error, DBUS_ERROR_FAILED,
00616 "Desktop file size (%ld bytes) is too large", (long) sb.size);
00617 return NULL;
00618 }
00619
00620 if (!_dbus_string_init (&str))
00621 return NULL;
00622
00623 if (!_dbus_file_get_contents (&str, filename, error))
00624 {
00625 _dbus_string_free (&str);
00626 return NULL;
00627 }
00628
00629 if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str)))
00630 {
00631 _dbus_string_free (&str);
00632 dbus_set_error (error, DBUS_ERROR_FAILED,
00633 "invalid UTF-8");
00634 return NULL;
00635 }
00636
00637 parser.desktop_file = dbus_new0 (BusDesktopFile, 1);
00638 if (parser.desktop_file == NULL)
00639 {
00640 _dbus_string_free (&str);
00641 BUS_SET_OOM (error);
00642 return NULL;
00643 }
00644
00645 parser.data = str;
00646 parser.line_num = 1;
00647 parser.pos = 0;
00648 parser.len = _dbus_string_get_length (&parser.data);
00649 parser.current_section = -1;
00650
00651 while (parser.pos < parser.len)
00652 {
00653 if (_dbus_string_get_byte (&parser.data, parser.pos) == '[')
00654 {
00655 if (!parse_section_start (&parser, error))
00656 {
00657 _dbus_string_free (&parser.data);
00658 return NULL;
00659 }
00660 }
00661 else if (is_blank_line (&parser) ||
00662 _dbus_string_get_byte (&parser.data, parser.pos) == '#')
00663 parse_comment_or_blank (&parser);
00664 else
00665 {
00666 if (!parse_key_value (&parser, error))
00667 {
00668 _dbus_string_free (&parser.data);
00669 return NULL;
00670 }
00671 }
00672 }
00673
00674 _dbus_string_free (&parser.data);
00675
00676 return parser.desktop_file;
00677 }
00678
00679 static BusDesktopFileSection *
00680 lookup_section (BusDesktopFile *desktop_file,
00681 const char *section_name)
00682 {
00683 BusDesktopFileSection *section;
00684 int i;
00685
00686 if (section_name == NULL)
00687 return NULL;
00688
00689 for (i = 0; i < desktop_file->n_sections; i ++)
00690 {
00691 section = &desktop_file->sections[i];
00692
00693 if (strcmp (section->section_name, section_name) == 0)
00694 return section;
00695 }
00696
00697 return NULL;
00698 }
00699
00700 static BusDesktopFileLine *
00701 lookup_line (BusDesktopFile *desktop_file,
00702 BusDesktopFileSection *section,
00703 const char *keyname)
00704 {
00705 BusDesktopFileLine *line;
00706 int i;
00707
00708 for (i = 0; i < section->n_lines; i++)
00709 {
00710 line = §ion->lines[i];
00711
00712 if (strcmp (line->key, keyname) == 0)
00713 return line;
00714 }
00715
00716 return NULL;
00717 }
00718
00719 dbus_bool_t
00720 bus_desktop_file_get_raw (BusDesktopFile *desktop_file,
00721 const char *section_name,
00722 const char *keyname,
00723 const char **val)
00724 {
00725 BusDesktopFileSection *section;
00726 BusDesktopFileLine *line;
00727
00728 *val = NULL;
00729
00730 section = lookup_section (desktop_file, section_name);
00731
00732 if (!section)
00733 return FALSE;
00734
00735 line = lookup_line (desktop_file,
00736 section,
00737 keyname);
00738
00739 if (!line)
00740 return FALSE;
00741
00742 *val = line->value;
00743
00744 return TRUE;
00745 }
00746
00747 dbus_bool_t
00748 bus_desktop_file_get_string (BusDesktopFile *desktop_file,
00749 const char *section,
00750 const char *keyname,
00751 char **val)
00752 {
00753 const char *raw;
00754
00755 *val = NULL;
00756
00757 if (!bus_desktop_file_get_raw (desktop_file, section, keyname, &raw))
00758 return FALSE;
00759
00760 *val = _dbus_strdup (raw);
00761
00762
00763
00764
00765 if (*val == NULL)
00766 return FALSE;
00767
00768 return TRUE;
00769 }