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 (§ion->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 = §ion->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 = §ion->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