1 /* 2 * PROJECT: .inf file parser 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PROGRAMMER: Royce Mitchell III 5 * Eric Kohl 6 * Ge van Geldorp <gvg@reactos.org> 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include "inflib.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 #define CONTROL_Z '\x1a' 17 #define MAX_SECTION_NAME_LEN 255 18 #define MAX_FIELD_LEN 511 /* larger fields get silently truncated */ 19 /* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */ 20 #define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1) 21 22 23 /* parser definitions */ 24 25 enum parser_state 26 { 27 LINE_START, /* at beginning of a line */ 28 SECTION_NAME, /* parsing a section name */ 29 KEY_NAME, /* parsing a key name */ 30 VALUE_NAME, /* parsing a value name */ 31 EOL_BACKSLASH, /* backslash at end of line */ 32 QUOTES, /* inside quotes */ 33 LEADING_SPACES, /* leading spaces */ 34 TRAILING_SPACES, /* trailing spaces */ 35 COMMENT, /* inside a comment */ 36 NB_PARSER_STATES 37 }; 38 39 struct parser 40 { 41 const WCHAR *start; /* start position of item being parsed */ 42 const WCHAR *end; /* end of buffer */ 43 PINFCACHE file; /* file being built */ 44 enum parser_state state; /* current parser state */ 45 enum parser_state stack[4]; /* state stack */ 46 int stack_pos; /* current pos in stack */ 47 48 PINFCACHESECTION cur_section; /* pointer to the section being parsed*/ 49 PINFCACHELINE line; /* current line */ 50 unsigned int line_pos; /* current line position in file */ 51 INFSTATUS error; /* error code */ 52 unsigned int token_len; /* current token len */ 53 WCHAR token[MAX_FIELD_LEN+1]; /* current token */ 54 }; 55 56 typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos ); 57 58 /* parser state machine functions */ 59 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ); 60 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ); 61 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ); 62 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ); 63 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ); 64 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ); 65 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ); 66 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ); 67 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ); 68 69 static const parser_state_func parser_funcs[NB_PARSER_STATES] = 70 { 71 line_start_state, /* LINE_START */ 72 section_name_state, /* SECTION_NAME */ 73 key_name_state, /* KEY_NAME */ 74 value_name_state, /* VALUE_NAME */ 75 eol_backslash_state, /* EOL_BACKSLASH */ 76 quotes_state, /* QUOTES */ 77 leading_spaces_state, /* LEADING_SPACES */ 78 trailing_spaces_state, /* TRAILING_SPACES */ 79 comment_state /* COMMENT */ 80 }; 81 82 83 /* PRIVATE FUNCTIONS ********************************************************/ 84 85 static PINFCACHELINE 86 InfpFreeLine (PINFCACHELINE Line) 87 { 88 PINFCACHELINE Next; 89 PINFCACHEFIELD Field; 90 91 if (Line == NULL) 92 { 93 return NULL; 94 } 95 96 Next = Line->Next; 97 if (Line->Key != NULL) 98 { 99 FREE (Line->Key); 100 Line->Key = NULL; 101 } 102 103 /* Remove data fields */ 104 while (Line->FirstField != NULL) 105 { 106 Field = Line->FirstField->Next; 107 FREE (Line->FirstField); 108 Line->FirstField = Field; 109 } 110 Line->LastField = NULL; 111 112 FREE (Line); 113 114 return Next; 115 } 116 117 118 PINFCACHESECTION 119 InfpFreeSection (PINFCACHESECTION Section) 120 { 121 PINFCACHESECTION Next; 122 123 if (Section == NULL) 124 { 125 return NULL; 126 } 127 128 /* Release all keys */ 129 Next = Section->Next; 130 while (Section->FirstLine != NULL) 131 { 132 Section->FirstLine = InfpFreeLine (Section->FirstLine); 133 } 134 Section->LastLine = NULL; 135 136 FREE (Section); 137 138 return Next; 139 } 140 141 142 PINFCACHESECTION 143 InfpFindSection(PINFCACHE Cache, 144 PCWSTR Name) 145 { 146 PINFCACHESECTION Section = NULL; 147 148 if (Cache == NULL || Name == NULL) 149 { 150 return NULL; 151 } 152 153 /* iterate through list of sections */ 154 Section = Cache->FirstSection; 155 while (Section != NULL) 156 { 157 if (strcmpiW(Section->Name, Name) == 0) 158 { 159 return Section; 160 } 161 162 /* get the next section*/ 163 Section = Section->Next; 164 } 165 166 return NULL; 167 } 168 169 170 PINFCACHESECTION 171 InfpAddSection(PINFCACHE Cache, 172 PCWSTR Name) 173 { 174 PINFCACHESECTION Section = NULL; 175 ULONG Size; 176 177 if (Cache == NULL || Name == NULL) 178 { 179 DPRINT("Invalid parameter\n"); 180 return NULL; 181 } 182 183 /* Allocate and initialize the new section */ 184 Size = (ULONG)FIELD_OFFSET(INFCACHESECTION, 185 Name[strlenW(Name) + 1]); 186 Section = (PINFCACHESECTION)MALLOC(Size); 187 if (Section == NULL) 188 { 189 DPRINT("MALLOC() failed\n"); 190 return NULL; 191 } 192 ZEROMEMORY (Section, 193 Size); 194 Section->Id = ++Cache->NextSectionId; 195 196 /* Copy section name */ 197 strcpyW(Section->Name, Name); 198 199 /* Append section */ 200 if (Cache->FirstSection == NULL) 201 { 202 Cache->FirstSection = Section; 203 Cache->LastSection = Section; 204 } 205 else 206 { 207 Cache->LastSection->Next = Section; 208 Section->Prev = Cache->LastSection; 209 Cache->LastSection = Section; 210 } 211 212 return Section; 213 } 214 215 216 PINFCACHELINE 217 InfpAddLine(PINFCACHESECTION Section) 218 { 219 PINFCACHELINE Line; 220 221 if (Section == NULL) 222 { 223 DPRINT("Invalid parameter\n"); 224 return NULL; 225 } 226 227 Line = (PINFCACHELINE)MALLOC(sizeof(INFCACHELINE)); 228 if (Line == NULL) 229 { 230 DPRINT("MALLOC() failed\n"); 231 return NULL; 232 } 233 ZEROMEMORY(Line, 234 sizeof(INFCACHELINE)); 235 Line->Id = ++Section->NextLineId; 236 237 /* Append line */ 238 if (Section->FirstLine == NULL) 239 { 240 Section->FirstLine = Line; 241 Section->LastLine = Line; 242 } 243 else 244 { 245 Section->LastLine->Next = Line; 246 Line->Prev = Section->LastLine; 247 Section->LastLine = Line; 248 } 249 Section->LineCount++; 250 251 return Line; 252 } 253 254 PINFCACHESECTION 255 InfpFindSectionById(PINFCACHE Cache, UINT Id) 256 { 257 PINFCACHESECTION Section; 258 259 for (Section = Cache->FirstSection; 260 Section != NULL; 261 Section = Section->Next) 262 { 263 if (Section->Id == Id) 264 { 265 return Section; 266 } 267 } 268 269 return NULL; 270 } 271 272 PINFCACHESECTION 273 InfpGetSectionForContext(PINFCONTEXT Context) 274 { 275 PINFCACHE Cache; 276 277 if (Context == NULL) 278 { 279 return NULL; 280 } 281 282 Cache = (PINFCACHE)Context->Inf; 283 if (Cache == NULL) 284 { 285 return NULL; 286 } 287 288 return InfpFindSectionById(Cache, Context->Section); 289 } 290 291 PINFCACHELINE 292 InfpFindLineById(PINFCACHESECTION Section, UINT Id) 293 { 294 PINFCACHELINE Line; 295 296 for (Line = Section->FirstLine; 297 Line != NULL; 298 Line = Line->Next) 299 { 300 if (Line->Id == Id) 301 { 302 return Line; 303 } 304 } 305 306 return NULL; 307 } 308 309 PINFCACHELINE 310 InfpGetLineForContext(PINFCONTEXT Context) 311 { 312 PINFCACHESECTION Section; 313 314 Section = InfpGetSectionForContext(Context); 315 if (Section == NULL) 316 { 317 return NULL; 318 } 319 320 return InfpFindLineById(Section, Context->Line); 321 } 322 323 PVOID 324 InfpAddKeyToLine(PINFCACHELINE Line, 325 PCWSTR Key) 326 { 327 if (Line == NULL) 328 { 329 DPRINT1("Invalid Line\n"); 330 return NULL; 331 } 332 333 if (Line->Key != NULL) 334 { 335 DPRINT1("Line already has a key\n"); 336 return NULL; 337 } 338 339 Line->Key = (PWCHAR)MALLOC((strlenW(Key) + 1) * sizeof(WCHAR)); 340 if (Line->Key == NULL) 341 { 342 DPRINT1("MALLOC() failed\n"); 343 return NULL; 344 } 345 346 strcpyW(Line->Key, Key); 347 348 return (PVOID)Line->Key; 349 } 350 351 352 PVOID 353 InfpAddFieldToLine(PINFCACHELINE Line, 354 PCWSTR Data) 355 { 356 PINFCACHEFIELD Field; 357 ULONG Size; 358 359 Size = (ULONG)FIELD_OFFSET(INFCACHEFIELD, 360 Data[strlenW(Data) + 1]); 361 Field = (PINFCACHEFIELD)MALLOC(Size); 362 if (Field == NULL) 363 { 364 DPRINT1("MALLOC() failed\n"); 365 return NULL; 366 } 367 ZEROMEMORY (Field, 368 Size); 369 strcpyW(Field->Data, Data); 370 371 /* Append key */ 372 if (Line->FirstField == NULL) 373 { 374 Line->FirstField = Field; 375 Line->LastField = Field; 376 } 377 else 378 { 379 Line->LastField->Next = Field; 380 Field->Prev = Line->LastField; 381 Line->LastField = Field; 382 } 383 Line->FieldCount++; 384 385 return (PVOID)Field; 386 } 387 388 389 PINFCACHELINE 390 InfpFindKeyLine(PINFCACHESECTION Section, 391 PCWSTR Key) 392 { 393 PINFCACHELINE Line; 394 395 Line = Section->FirstLine; 396 while (Line != NULL) 397 { 398 if (Line->Key != NULL && strcmpiW(Line->Key, Key) == 0) 399 { 400 return Line; 401 } 402 403 Line = Line->Next; 404 } 405 406 return NULL; 407 } 408 409 410 /* push the current state on the parser stack */ 411 __inline static void push_state( struct parser *parser, enum parser_state state ) 412 { 413 // assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) ); 414 parser->stack[parser->stack_pos++] = state; 415 } 416 417 418 /* pop the current state */ 419 __inline static void pop_state( struct parser *parser ) 420 { 421 // assert( parser->stack_pos ); 422 parser->state = parser->stack[--parser->stack_pos]; 423 } 424 425 426 /* set the parser state and return the previous one */ 427 __inline static enum parser_state set_state( struct parser *parser, enum parser_state state ) 428 { 429 enum parser_state ret = parser->state; 430 parser->state = state; 431 return ret; 432 } 433 434 435 /* check if the pointer points to an end of file */ 436 __inline static int is_eof( struct parser *parser, const WCHAR *ptr ) 437 { 438 return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == 0); 439 } 440 441 442 /* check if the pointer points to an end of line */ 443 __inline static int is_eol( struct parser *parser, const WCHAR *ptr ) 444 { 445 return (ptr >= parser->end || 446 *ptr == CONTROL_Z || 447 *ptr == '\n' || 448 (*ptr == '\r' && *(ptr + 1) == '\n') || 449 *ptr == 0); 450 } 451 452 453 /* push data from current token start up to pos into the current token */ 454 static int push_token( struct parser *parser, const WCHAR *pos ) 455 { 456 UINT len = (UINT)(pos - parser->start); 457 const WCHAR *src = parser->start; 458 WCHAR *dst = parser->token + parser->token_len; 459 460 if (len > MAX_FIELD_LEN - parser->token_len) 461 len = MAX_FIELD_LEN - parser->token_len; 462 463 parser->token_len += len; 464 for ( ; len > 0; len--, dst++, src++) 465 { 466 if (*src) 467 { 468 *dst = *src; 469 } 470 else 471 { 472 *dst = ' '; 473 } 474 } 475 476 *dst = 0; 477 parser->start = pos; 478 479 return 0; 480 } 481 482 483 484 /* add a section with the current token as name */ 485 static PVOID add_section_from_token( struct parser *parser ) 486 { 487 PINFCACHESECTION Section; 488 489 if (parser->token_len > MAX_SECTION_NAME_LEN) 490 { 491 parser->error = INF_STATUS_SECTION_NAME_TOO_LONG; 492 return NULL; 493 } 494 495 Section = InfpFindSection(parser->file, 496 parser->token); 497 if (Section == NULL) 498 { 499 /* need to create a new one */ 500 Section= InfpAddSection(parser->file, 501 parser->token); 502 if (Section == NULL) 503 { 504 parser->error = INF_STATUS_NOT_ENOUGH_MEMORY; 505 return NULL; 506 } 507 } 508 509 parser->token_len = 0; 510 parser->cur_section = Section; 511 512 return (PVOID)Section; 513 } 514 515 516 /* add a field containing the current token to the current line */ 517 static struct field *add_field_from_token( struct parser *parser, int is_key ) 518 { 519 PVOID field; 520 521 if (!parser->line) /* need to start a new line */ 522 { 523 if (parser->cur_section == NULL) /* got a line before the first section */ 524 { 525 parser->error = INF_STATUS_WRONG_INF_STYLE; 526 return NULL; 527 } 528 529 parser->line = InfpAddLine(parser->cur_section); 530 if (parser->line == NULL) 531 goto error; 532 } 533 else 534 { 535 // assert(!is_key); 536 } 537 538 if (is_key) 539 { 540 field = InfpAddKeyToLine(parser->line, parser->token); 541 } 542 else 543 { 544 field = InfpAddFieldToLine(parser->line, parser->token); 545 } 546 547 if (field != NULL) 548 { 549 parser->token_len = 0; 550 return field; 551 } 552 553 error: 554 parser->error = INF_STATUS_NOT_ENOUGH_MEMORY; 555 return NULL; 556 } 557 558 559 /* close the current line and prepare for parsing a new one */ 560 static void close_current_line( struct parser *parser ) 561 { 562 parser->line = NULL; 563 } 564 565 566 567 /* handler for parser LINE_START state */ 568 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ) 569 { 570 const WCHAR *p; 571 572 for (p = pos; !is_eof( parser, p ); p++) 573 { 574 switch(*p) 575 { 576 case '\r': 577 continue; 578 579 case '\n': 580 parser->line_pos++; 581 close_current_line( parser ); 582 break; 583 584 case ';': 585 push_state( parser, LINE_START ); 586 set_state( parser, COMMENT ); 587 return p + 1; 588 589 case '[': 590 parser->start = p + 1; 591 set_state( parser, SECTION_NAME ); 592 return p + 1; 593 594 default: 595 if (!isspaceW(*p)) 596 { 597 parser->start = p; 598 set_state( parser, KEY_NAME ); 599 return p; 600 } 601 break; 602 } 603 } 604 close_current_line( parser ); 605 return NULL; 606 } 607 608 609 /* handler for parser SECTION_NAME state */ 610 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ) 611 { 612 const WCHAR *p; 613 614 for (p = pos; !is_eol( parser, p ); p++) 615 { 616 if (*p == ']') 617 { 618 push_token( parser, p ); 619 if (add_section_from_token( parser ) == NULL) 620 return NULL; 621 push_state( parser, LINE_START ); 622 set_state( parser, COMMENT ); /* ignore everything else on the line */ 623 return p + 1; 624 } 625 } 626 parser->error = INF_STATUS_BAD_SECTION_NAME_LINE; /* unfinished section name */ 627 return NULL; 628 } 629 630 631 /* handler for parser KEY_NAME state */ 632 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ) 633 { 634 const WCHAR *p, *token_end = parser->start; 635 636 for (p = pos; !is_eol( parser, p ); p++) 637 { 638 if (*p == ',') break; 639 switch(*p) 640 { 641 642 case '=': 643 push_token( parser, token_end ); 644 if (!add_field_from_token( parser, 1 )) return NULL; 645 parser->start = p + 1; 646 push_state( parser, VALUE_NAME ); 647 set_state( parser, LEADING_SPACES ); 648 return p + 1; 649 case ';': 650 push_token( parser, token_end ); 651 if (!add_field_from_token( parser, 0 )) return NULL; 652 push_state( parser, LINE_START ); 653 set_state( parser, COMMENT ); 654 return p + 1; 655 case '"': 656 push_token( parser, token_end ); 657 parser->start = p + 1; 658 push_state( parser, KEY_NAME ); 659 set_state( parser, QUOTES ); 660 return p + 1; 661 case '\\': 662 push_token( parser, token_end ); 663 parser->start = p; 664 push_state( parser, KEY_NAME ); 665 set_state( parser, EOL_BACKSLASH ); 666 return p; 667 default: 668 if (!isspaceW(*p)) token_end = p + 1; 669 else 670 { 671 push_token( parser, p ); 672 push_state( parser, KEY_NAME ); 673 set_state( parser, TRAILING_SPACES ); 674 return p; 675 } 676 break; 677 } 678 } 679 push_token( parser, token_end ); 680 set_state( parser, VALUE_NAME ); 681 return p; 682 } 683 684 685 /* handler for parser VALUE_NAME state */ 686 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ) 687 { 688 const WCHAR *p, *token_end = parser->start; 689 690 for (p = pos; !is_eol( parser, p ); p++) 691 { 692 switch(*p) 693 { 694 case ';': 695 push_token( parser, token_end ); 696 if (!add_field_from_token( parser, 0 )) return NULL; 697 push_state( parser, LINE_START ); 698 set_state( parser, COMMENT ); 699 return p + 1; 700 case ',': 701 push_token( parser, token_end ); 702 if (!add_field_from_token( parser, 0 )) return NULL; 703 parser->start = p + 1; 704 push_state( parser, VALUE_NAME ); 705 set_state( parser, LEADING_SPACES ); 706 return p + 1; 707 case '"': 708 push_token( parser, token_end ); 709 parser->start = p + 1; 710 push_state( parser, VALUE_NAME ); 711 set_state( parser, QUOTES ); 712 return p + 1; 713 case '\\': 714 push_token( parser, token_end ); 715 parser->start = p; 716 push_state( parser, VALUE_NAME ); 717 set_state( parser, EOL_BACKSLASH ); 718 return p; 719 default: 720 if (!isspaceW(*p)) token_end = p + 1; 721 else 722 { 723 push_token( parser, p ); 724 push_state( parser, VALUE_NAME ); 725 set_state( parser, TRAILING_SPACES ); 726 return p; 727 } 728 break; 729 } 730 } 731 push_token( parser, token_end ); 732 if (!add_field_from_token( parser, 0 )) return NULL; 733 set_state( parser, LINE_START ); 734 return p; 735 } 736 737 738 /* handler for parser EOL_BACKSLASH state */ 739 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ) 740 { 741 const WCHAR *p; 742 743 for (p = pos; !is_eof( parser, p ); p++) 744 { 745 switch(*p) 746 { 747 case '\r': 748 continue; 749 750 case '\n': 751 parser->line_pos++; 752 parser->start = p + 1; 753 set_state( parser, LEADING_SPACES ); 754 return p + 1; 755 756 case '\\': 757 continue; 758 759 case ';': 760 push_state( parser, EOL_BACKSLASH ); 761 set_state( parser, COMMENT ); 762 return p + 1; 763 764 default: 765 if (isspaceW(*p)) 766 continue; 767 push_token( parser, p ); 768 pop_state( parser ); 769 return p; 770 } 771 } 772 parser->start = p; 773 pop_state( parser ); 774 775 return p; 776 } 777 778 779 /* handler for parser QUOTES state */ 780 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ) 781 { 782 const WCHAR *p, *token_end = parser->start; 783 784 for (p = pos; !is_eol( parser, p ); p++) 785 { 786 if (*p == '"') 787 { 788 if (p+1 < parser->end && p[1] == '"') /* double quotes */ 789 { 790 push_token( parser, p + 1 ); 791 parser->start = token_end = p + 2; 792 p++; 793 } 794 else /* end of quotes */ 795 { 796 push_token( parser, p ); 797 parser->start = p + 1; 798 pop_state( parser ); 799 return p + 1; 800 } 801 } 802 } 803 push_token( parser, p ); 804 pop_state( parser ); 805 return p; 806 } 807 808 809 /* handler for parser LEADING_SPACES state */ 810 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ) 811 { 812 const WCHAR *p; 813 814 for (p = pos; !is_eol( parser, p ); p++) 815 { 816 if (*p == '\\') 817 { 818 parser->start = p; 819 set_state( parser, EOL_BACKSLASH ); 820 return p; 821 } 822 if (!isspaceW(*p)) 823 break; 824 } 825 parser->start = p; 826 pop_state( parser ); 827 return p; 828 } 829 830 831 /* handler for parser TRAILING_SPACES state */ 832 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ) 833 { 834 const WCHAR *p; 835 836 for (p = pos; !is_eol( parser, p ); p++) 837 { 838 if (*p == '\\') 839 { 840 set_state( parser, EOL_BACKSLASH ); 841 return p; 842 } 843 if (!isspaceW(*p)) 844 break; 845 } 846 pop_state( parser ); 847 return p; 848 } 849 850 851 /* handler for parser COMMENT state */ 852 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ) 853 { 854 const WCHAR *p = pos; 855 856 while (!is_eol( parser, p )) 857 p++; 858 pop_state( parser ); 859 return p; 860 } 861 862 863 /* parse a complete buffer */ 864 INFSTATUS 865 InfpParseBuffer (PINFCACHE file, 866 const WCHAR *buffer, 867 const WCHAR *end, 868 PULONG error_line) 869 { 870 struct parser parser; 871 const WCHAR *pos = buffer; 872 873 parser.start = buffer; 874 parser.end = end; 875 parser.file = file; 876 parser.line = NULL; 877 parser.state = LINE_START; 878 parser.stack_pos = 0; 879 parser.cur_section = NULL; 880 parser.line_pos = 1; 881 parser.error = 0; 882 parser.token_len = 0; 883 884 /* parser main loop */ 885 while (pos) 886 pos = (parser_funcs[parser.state])(&parser, pos); 887 888 if (parser.error) 889 { 890 if (error_line) 891 *error_line = parser.line_pos; 892 return parser.error; 893 } 894 895 /* find the [strings] section */ 896 file->StringsSection = InfpFindSection(file, 897 L"Strings"); 898 899 return INF_STATUS_SUCCESS; 900 } 901 902 /* EOF */ 903