- Create another branch for networking fixes
[reactos.git] / lib / inflib / infcore.c
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 CHAR *start; /* start position of item being parsed */
42 const CHAR *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 TCHAR token[MAX_FIELD_LEN+1]; /* current token */
54 };
55
56 typedef const CHAR * (*parser_state_func)( struct parser *parser, const CHAR *pos );
57
58 /* parser state machine functions */
59 static const CHAR *line_start_state( struct parser *parser, const CHAR *pos );
60 static const CHAR *section_name_state( struct parser *parser, const CHAR *pos );
61 static const CHAR *key_name_state( struct parser *parser, const CHAR *pos );
62 static const CHAR *value_name_state( struct parser *parser, const CHAR *pos );
63 static const CHAR *eol_backslash_state( struct parser *parser, const CHAR *pos );
64 static const CHAR *quotes_state( struct parser *parser, const CHAR *pos );
65 static const CHAR *leading_spaces_state( struct parser *parser, const CHAR *pos );
66 static const CHAR *trailing_spaces_state( struct parser *parser, const CHAR *pos );
67 static const CHAR *comment_state( struct parser *parser, const CHAR *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 PCTSTR 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 (_tcsicmp (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 PCTSTR 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[_tcslen (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
195 /* Copy section name */
196 _tcscpy (Section->Name, Name);
197
198 /* Append section */
199 if (Cache->FirstSection == NULL)
200 {
201 Cache->FirstSection = Section;
202 Cache->LastSection = Section;
203 }
204 else
205 {
206 Cache->LastSection->Next = Section;
207 Section->Prev = Cache->LastSection;
208 Cache->LastSection = Section;
209 }
210
211 return Section;
212 }
213
214
215 PINFCACHELINE
216 InfpAddLine(PINFCACHESECTION Section)
217 {
218 PINFCACHELINE Line;
219
220 if (Section == NULL)
221 {
222 DPRINT("Invalid parameter\n");
223 return NULL;
224 }
225
226 Line = (PINFCACHELINE)MALLOC (sizeof(INFCACHELINE));
227 if (Line == NULL)
228 {
229 DPRINT("MALLOC() failed\n");
230 return NULL;
231 }
232 ZEROMEMORY(Line,
233 sizeof(INFCACHELINE));
234
235 /* Append line */
236 if (Section->FirstLine == NULL)
237 {
238 Section->FirstLine = Line;
239 Section->LastLine = Line;
240 }
241 else
242 {
243 Section->LastLine->Next = Line;
244 Line->Prev = Section->LastLine;
245 Section->LastLine = Line;
246 }
247 Section->LineCount++;
248
249 return Line;
250 }
251
252
253 PVOID
254 InfpAddKeyToLine(PINFCACHELINE Line,
255 PCTSTR Key)
256 {
257 if (Line == NULL)
258 {
259 DPRINT1("Invalid Line\n");
260 return NULL;
261 }
262
263 if (Line->Key != NULL)
264 {
265 DPRINT1("Line already has a key\n");
266 return NULL;
267 }
268
269 Line->Key = (PTCHAR)MALLOC((_tcslen(Key) + 1) * sizeof(TCHAR));
270 if (Line->Key == NULL)
271 {
272 DPRINT1("MALLOC() failed\n");
273 return NULL;
274 }
275
276 _tcscpy(Line->Key, Key);
277
278 return (PVOID)Line->Key;
279 }
280
281
282 PVOID
283 InfpAddFieldToLine(PINFCACHELINE Line,
284 PCTSTR Data)
285 {
286 PINFCACHEFIELD Field;
287 ULONG Size;
288
289 Size = (ULONG)FIELD_OFFSET(INFCACHEFIELD,
290 Data[_tcslen(Data) + 1]);
291 Field = (PINFCACHEFIELD)MALLOC(Size);
292 if (Field == NULL)
293 {
294 DPRINT1("MALLOC() failed\n");
295 return NULL;
296 }
297 ZEROMEMORY (Field,
298 Size);
299 _tcscpy (Field->Data, Data);
300
301 /* Append key */
302 if (Line->FirstField == NULL)
303 {
304 Line->FirstField = Field;
305 Line->LastField = Field;
306 }
307 else
308 {
309 Line->LastField->Next = Field;
310 Field->Prev = Line->LastField;
311 Line->LastField = Field;
312 }
313 Line->FieldCount++;
314
315 return (PVOID)Field;
316 }
317
318
319 PINFCACHELINE
320 InfpFindKeyLine(PINFCACHESECTION Section,
321 PCTSTR Key)
322 {
323 PINFCACHELINE Line;
324
325 Line = Section->FirstLine;
326 while (Line != NULL)
327 {
328 if (Line->Key != NULL && _tcsicmp (Line->Key, Key) == 0)
329 {
330 return Line;
331 }
332
333 Line = Line->Next;
334 }
335
336 return NULL;
337 }
338
339
340 /* push the current state on the parser stack */
341 __inline static void push_state( struct parser *parser, enum parser_state state )
342 {
343 // assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
344 parser->stack[parser->stack_pos++] = state;
345 }
346
347
348 /* pop the current state */
349 __inline static void pop_state( struct parser *parser )
350 {
351 // assert( parser->stack_pos );
352 parser->state = parser->stack[--parser->stack_pos];
353 }
354
355
356 /* set the parser state and return the previous one */
357 __inline static enum parser_state set_state( struct parser *parser, enum parser_state state )
358 {
359 enum parser_state ret = parser->state;
360 parser->state = state;
361 return ret;
362 }
363
364
365 /* check if the pointer points to an end of file */
366 __inline static int is_eof( struct parser *parser, const CHAR *ptr )
367 {
368 return (ptr >= parser->end || *ptr == CONTROL_Z);
369 }
370
371
372 /* check if the pointer points to an end of line */
373 __inline static int is_eol( struct parser *parser, const CHAR *ptr )
374 {
375 return (ptr >= parser->end ||
376 *ptr == CONTROL_Z ||
377 *ptr == '\n' ||
378 (*ptr == '\r' && *(ptr + 1) == '\n'));
379 }
380
381
382 /* push data from current token start up to pos into the current token */
383 static int push_token( struct parser *parser, const CHAR *pos )
384 {
385 UINT len = (UINT)(pos - parser->start);
386 const CHAR *src = parser->start;
387 TCHAR *dst = parser->token + parser->token_len;
388
389 if (len > MAX_FIELD_LEN - parser->token_len)
390 len = MAX_FIELD_LEN - parser->token_len;
391
392 parser->token_len += len;
393 for ( ; len > 0; len--, dst++, src++)
394 {
395 if (*src)
396 {
397 *dst = *src;
398 }
399 else
400 {
401 *dst = _T(' ');
402 }
403 }
404
405 *dst = 0;
406 parser->start = pos;
407
408 return 0;
409 }
410
411
412
413 /* add a section with the current token as name */
414 static PVOID add_section_from_token( struct parser *parser )
415 {
416 PINFCACHESECTION Section;
417
418 if (parser->token_len > MAX_SECTION_NAME_LEN)
419 {
420 parser->error = INF_STATUS_SECTION_NAME_TOO_LONG;
421 return NULL;
422 }
423
424 Section = InfpFindSection(parser->file,
425 parser->token);
426 if (Section == NULL)
427 {
428 /* need to create a new one */
429 Section= InfpAddSection(parser->file,
430 parser->token);
431 if (Section == NULL)
432 {
433 parser->error = INF_STATUS_NOT_ENOUGH_MEMORY;
434 return NULL;
435 }
436 }
437
438 parser->token_len = 0;
439 parser->cur_section = Section;
440
441 return (PVOID)Section;
442 }
443
444
445 /* add a field containing the current token to the current line */
446 static struct field *add_field_from_token( struct parser *parser, int is_key )
447 {
448 PVOID field;
449
450 if (!parser->line) /* need to start a new line */
451 {
452 if (parser->cur_section == NULL) /* got a line before the first section */
453 {
454 parser->error = INF_STATUS_WRONG_INF_STYLE;
455 return NULL;
456 }
457
458 parser->line = InfpAddLine(parser->cur_section);
459 if (parser->line == NULL)
460 goto error;
461 }
462 else
463 {
464 // assert(!is_key);
465 }
466
467 if (is_key)
468 {
469 field = InfpAddKeyToLine(parser->line, parser->token);
470 }
471 else
472 {
473 field = InfpAddFieldToLine(parser->line, parser->token);
474 }
475
476 if (field != NULL)
477 {
478 parser->token_len = 0;
479 return field;
480 }
481
482 error:
483 parser->error = INF_STATUS_NOT_ENOUGH_MEMORY;
484 return NULL;
485 }
486
487
488 /* close the current line and prepare for parsing a new one */
489 static void close_current_line( struct parser *parser )
490 {
491 parser->line = NULL;
492 }
493
494
495
496 /* handler for parser LINE_START state */
497 static const CHAR *line_start_state( struct parser *parser, const CHAR *pos )
498 {
499 const CHAR *p;
500
501 for (p = pos; !is_eof( parser, p ); p++)
502 {
503 switch(*p)
504 {
505 case '\r':
506 continue;
507
508 case '\n':
509 parser->line_pos++;
510 close_current_line( parser );
511 break;
512
513 case ';':
514 push_state( parser, LINE_START );
515 set_state( parser, COMMENT );
516 return p + 1;
517
518 case '[':
519 parser->start = p + 1;
520 set_state( parser, SECTION_NAME );
521 return p + 1;
522
523 default:
524 if (!isspace(*p))
525 {
526 parser->start = p;
527 set_state( parser, KEY_NAME );
528 return p;
529 }
530 break;
531 }
532 }
533 close_current_line( parser );
534 return NULL;
535 }
536
537
538 /* handler for parser SECTION_NAME state */
539 static const CHAR *section_name_state( struct parser *parser, const CHAR *pos )
540 {
541 const CHAR *p;
542
543 for (p = pos; !is_eol( parser, p ); p++)
544 {
545 if (*p == ']')
546 {
547 push_token( parser, p );
548 if (add_section_from_token( parser ) == NULL)
549 return NULL;
550 push_state( parser, LINE_START );
551 set_state( parser, COMMENT ); /* ignore everything else on the line */
552 return p + 1;
553 }
554 }
555 parser->error = INF_STATUS_BAD_SECTION_NAME_LINE; /* unfinished section name */
556 return NULL;
557 }
558
559
560 /* handler for parser KEY_NAME state */
561 static const CHAR *key_name_state( struct parser *parser, const CHAR *pos )
562 {
563 const CHAR *p, *token_end = parser->start;
564
565 for (p = pos; !is_eol( parser, p ); p++)
566 {
567 if (*p == ',') break;
568 switch(*p)
569 {
570
571 case '=':
572 push_token( parser, token_end );
573 if (!add_field_from_token( parser, 1 )) return NULL;
574 parser->start = p + 1;
575 push_state( parser, VALUE_NAME );
576 set_state( parser, LEADING_SPACES );
577 return p + 1;
578 case ';':
579 push_token( parser, token_end );
580 if (!add_field_from_token( parser, 0 )) return NULL;
581 push_state( parser, LINE_START );
582 set_state( parser, COMMENT );
583 return p + 1;
584 case '"':
585 push_token( parser, token_end );
586 parser->start = p + 1;
587 push_state( parser, KEY_NAME );
588 set_state( parser, QUOTES );
589 return p + 1;
590 case '\\':
591 push_token( parser, token_end );
592 parser->start = p;
593 push_state( parser, KEY_NAME );
594 set_state( parser, EOL_BACKSLASH );
595 return p;
596 default:
597 if (!isspace(*p)) token_end = p + 1;
598 else
599 {
600 push_token( parser, p );
601 push_state( parser, KEY_NAME );
602 set_state( parser, TRAILING_SPACES );
603 return p;
604 }
605 break;
606 }
607 }
608 push_token( parser, token_end );
609 set_state( parser, VALUE_NAME );
610 return p;
611 }
612
613
614 /* handler for parser VALUE_NAME state */
615 static const CHAR *value_name_state( struct parser *parser, const CHAR *pos )
616 {
617 const CHAR *p, *token_end = parser->start;
618
619 for (p = pos; !is_eol( parser, p ); p++)
620 {
621 switch(*p)
622 {
623 case ';':
624 push_token( parser, token_end );
625 if (!add_field_from_token( parser, 0 )) return NULL;
626 push_state( parser, LINE_START );
627 set_state( parser, COMMENT );
628 return p + 1;
629 case ',':
630 push_token( parser, token_end );
631 if (!add_field_from_token( parser, 0 )) return NULL;
632 parser->start = p + 1;
633 push_state( parser, VALUE_NAME );
634 set_state( parser, LEADING_SPACES );
635 return p + 1;
636 case '"':
637 push_token( parser, token_end );
638 parser->start = p + 1;
639 push_state( parser, VALUE_NAME );
640 set_state( parser, QUOTES );
641 return p + 1;
642 case '\\':
643 push_token( parser, token_end );
644 parser->start = p;
645 push_state( parser, VALUE_NAME );
646 set_state( parser, EOL_BACKSLASH );
647 return p;
648 default:
649 if (!isspace(*p)) token_end = p + 1;
650 else
651 {
652 push_token( parser, p );
653 push_state( parser, VALUE_NAME );
654 set_state( parser, TRAILING_SPACES );
655 return p;
656 }
657 break;
658 }
659 }
660 push_token( parser, token_end );
661 if (!add_field_from_token( parser, 0 )) return NULL;
662 set_state( parser, LINE_START );
663 return p;
664 }
665
666
667 /* handler for parser EOL_BACKSLASH state */
668 static const CHAR *eol_backslash_state( struct parser *parser, const CHAR *pos )
669 {
670 const CHAR *p;
671
672 for (p = pos; !is_eof( parser, p ); p++)
673 {
674 switch(*p)
675 {
676 case '\r':
677 continue;
678
679 case '\n':
680 parser->line_pos++;
681 parser->start = p + 1;
682 set_state( parser, LEADING_SPACES );
683 return p + 1;
684
685 case '\\':
686 continue;
687
688 case ';':
689 push_state( parser, EOL_BACKSLASH );
690 set_state( parser, COMMENT );
691 return p + 1;
692
693 default:
694 if (isspace(*p))
695 continue;
696 push_token( parser, p );
697 pop_state( parser );
698 return p;
699 }
700 }
701 parser->start = p;
702 pop_state( parser );
703
704 return p;
705 }
706
707
708 /* handler for parser QUOTES state */
709 static const CHAR *quotes_state( struct parser *parser, const CHAR *pos )
710 {
711 const CHAR *p, *token_end = parser->start;
712
713 for (p = pos; !is_eol( parser, p ); p++)
714 {
715 if (*p == '"')
716 {
717 if (p+1 < parser->end && p[1] == '"') /* double quotes */
718 {
719 push_token( parser, p + 1 );
720 parser->start = token_end = p + 2;
721 p++;
722 }
723 else /* end of quotes */
724 {
725 push_token( parser, p );
726 parser->start = p + 1;
727 pop_state( parser );
728 return p + 1;
729 }
730 }
731 }
732 push_token( parser, p );
733 pop_state( parser );
734 return p;
735 }
736
737
738 /* handler for parser LEADING_SPACES state */
739 static const CHAR *leading_spaces_state( struct parser *parser, const CHAR *pos )
740 {
741 const CHAR *p;
742
743 for (p = pos; !is_eol( parser, p ); p++)
744 {
745 if (*p == '\\')
746 {
747 parser->start = p;
748 set_state( parser, EOL_BACKSLASH );
749 return p;
750 }
751 if (!isspace(*p))
752 break;
753 }
754 parser->start = p;
755 pop_state( parser );
756 return p;
757 }
758
759
760 /* handler for parser TRAILING_SPACES state */
761 static const CHAR *trailing_spaces_state( struct parser *parser, const CHAR *pos )
762 {
763 const CHAR *p;
764
765 for (p = pos; !is_eol( parser, p ); p++)
766 {
767 if (*p == '\\')
768 {
769 set_state( parser, EOL_BACKSLASH );
770 return p;
771 }
772 if (!isspace(*p))
773 break;
774 }
775 pop_state( parser );
776 return p;
777 }
778
779
780 /* handler for parser COMMENT state */
781 static const CHAR *comment_state( struct parser *parser, const CHAR *pos )
782 {
783 const CHAR *p = pos;
784
785 while (!is_eol( parser, p ))
786 p++;
787 pop_state( parser );
788 return p;
789 }
790
791
792 /* parse a complete buffer */
793 INFSTATUS
794 InfpParseBuffer (PINFCACHE file,
795 const CHAR *buffer,
796 const CHAR *end,
797 PULONG error_line)
798 {
799 struct parser parser;
800 const CHAR *pos = buffer;
801
802 parser.start = buffer;
803 parser.end = end;
804 parser.file = file;
805 parser.line = NULL;
806 parser.state = LINE_START;
807 parser.stack_pos = 0;
808 parser.cur_section = NULL;
809 parser.line_pos = 1;
810 parser.error = 0;
811 parser.token_len = 0;
812
813 /* parser main loop */
814 while (pos)
815 pos = (parser_funcs[parser.state])(&parser, pos);
816
817 if (parser.error)
818 {
819 if (error_line)
820 *error_line = parser.line_pos;
821 return parser.error;
822 }
823
824 /* find the [strings] section */
825 file->StringsSection = InfpFindSection(file,
826 _T("Strings"));
827
828 return INF_STATUS_SUCCESS;
829 }
830
831 /* EOF */