1 | /* Input routines. |
2 | Copyright (C) 1989-1998, 2002-2004, 2011 Free Software Foundation, Inc. |
3 | Written by Douglas C. Schmidt <schmidt@ics.uci.edu> |
4 | and Bruno Haible <bruno@clisp.org>. |
5 | |
6 | This file is part of GNU GPERF. |
7 | |
8 | This program is free software: you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 3 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
20 | |
21 | /* Specification. */ |
22 | #include "input.h" |
23 | |
24 | #include <stdio.h> |
25 | #include <stdlib.h> /* declares exit() */ |
26 | #include <string.h> /* declares strncpy(), strchr() */ |
27 | #include <limits.h> /* defines UCHAR_MAX etc. */ |
28 | #include "options.h" |
29 | #include "getline.h" |
30 | |
31 | Input::Input (FILE *stream, Keyword_Factory *keyword_factory) |
32 | : _stream (stream), _factory (keyword_factory) |
33 | { |
34 | } |
35 | |
36 | /* Returns a pretty representation of the input file name, for error and |
37 | warning messages. */ |
38 | static const char * |
39 | pretty_input_file_name () |
40 | { |
41 | if (option.get_input_file_name ()) |
42 | return option.get_input_file_name (); |
43 | else |
44 | return "(standard input)" ; |
45 | } |
46 | |
47 | /* Returns true if the given line contains a "%DECL" declaration. */ |
48 | static bool |
49 | is_declaration (const char *line, const char *line_end, unsigned int lineno, |
50 | const char *decl) |
51 | { |
52 | /* Skip '%'. */ |
53 | line++; |
54 | |
55 | /* Skip DECL. */ |
56 | for (const char *d = decl; *d; d++) |
57 | { |
58 | if (!(line < line_end)) |
59 | return false; |
60 | if (!(*line == *d || (*d == '-' && *line == '_'))) |
61 | return false; |
62 | line++; |
63 | } |
64 | if (line < line_end |
65 | && ((*line >= 'A' && *line <= 'Z') |
66 | || (*line >= 'a' && *line <= 'z') |
67 | || *line == '-' || *line == '_')) |
68 | return false; |
69 | |
70 | /* OK, found DECL. */ |
71 | |
72 | /* Skip whitespace. */ |
73 | while (line < line_end && (*line == ' ' || *line == '\t')) |
74 | line++; |
75 | |
76 | /* Expect end of line. */ |
77 | if (line < line_end && *line != '\n') |
78 | { |
79 | fprintf (stderr, "%s:%u: junk after declaration\n" , |
80 | pretty_input_file_name (), lineno); |
81 | exit (1); |
82 | } |
83 | |
84 | return true; |
85 | } |
86 | |
87 | /* Tests if the given line contains a "%DECL=ARG" declaration. |
88 | If yes, it sets *ARGP to the argument, and returns true. |
89 | Otherwise, it returns false. */ |
90 | static bool |
91 | is_declaration_with_arg (const char *line, const char *line_end, |
92 | unsigned int lineno, |
93 | const char *decl, char **argp) |
94 | { |
95 | /* Skip '%'. */ |
96 | line++; |
97 | |
98 | /* Skip DECL. */ |
99 | for (const char *d = decl; *d; d++) |
100 | { |
101 | if (!(line < line_end)) |
102 | return false; |
103 | if (!(*line == *d || (*d == '-' && *line == '_'))) |
104 | return false; |
105 | line++; |
106 | } |
107 | if (line < line_end |
108 | && ((*line >= 'A' && *line <= 'Z') |
109 | || (*line >= 'a' && *line <= 'z') |
110 | || *line == '-' || *line == '_')) |
111 | return false; |
112 | |
113 | /* OK, found DECL. */ |
114 | |
115 | /* Skip '='. */ |
116 | if (!(line < line_end && *line == '=')) |
117 | { |
118 | fprintf (stderr, "%s:%u: missing argument in %%%s=ARG declaration.\n" , |
119 | pretty_input_file_name (), lineno, decl); |
120 | exit (1); |
121 | } |
122 | line++; |
123 | |
124 | /* The next word is the argument. */ |
125 | char *arg = new char[line_end - line + 1]; |
126 | char *p = arg; |
127 | while (line < line_end && !(*line == ' ' || *line == '\t' || *line == '\n')) |
128 | *p++ = *line++; |
129 | *p = '\0'; |
130 | |
131 | /* Skip whitespace. */ |
132 | while (line < line_end && (*line == ' ' || *line == '\t')) |
133 | line++; |
134 | |
135 | /* Expect end of line. */ |
136 | if (line < line_end && *line != '\n') |
137 | { |
138 | fprintf (stderr, "%s:%u: junk after declaration\n" , |
139 | pretty_input_file_name (), lineno); |
140 | exit (1); |
141 | } |
142 | |
143 | *argp = arg; |
144 | return true; |
145 | } |
146 | |
147 | /* Tests if the given line contains a "%define DECL ARG" declaration. |
148 | If yes, it sets *ARGP to the argument, and returns true. |
149 | Otherwise, it returns false. */ |
150 | static bool |
151 | is_define_declaration (const char *line, const char *line_end, |
152 | unsigned int lineno, |
153 | const char *decl, char **argp) |
154 | { |
155 | /* Skip '%'. */ |
156 | line++; |
157 | |
158 | /* Skip "define". */ |
159 | { |
160 | for (const char *d = "define" ; *d; d++) |
161 | { |
162 | if (!(line < line_end)) |
163 | return false; |
164 | if (!(*line == *d)) |
165 | return false; |
166 | line++; |
167 | } |
168 | if (!(line < line_end && (*line == ' ' || *line == '\t'))) |
169 | return false; |
170 | } |
171 | |
172 | /* Skip whitespace. */ |
173 | while (line < line_end && (*line == ' ' || *line == '\t')) |
174 | line++; |
175 | |
176 | /* Skip DECL. */ |
177 | for (const char *d = decl; *d; d++) |
178 | { |
179 | if (!(line < line_end)) |
180 | return false; |
181 | if (!(*line == *d || (*d == '-' && *line == '_'))) |
182 | return false; |
183 | line++; |
184 | } |
185 | if (line < line_end |
186 | && ((*line >= 'A' && *line <= 'Z') |
187 | || (*line >= 'a' && *line <= 'z') |
188 | || *line == '-' || *line == '_')) |
189 | return false; |
190 | |
191 | /* OK, found DECL. */ |
192 | |
193 | /* Skip whitespace. */ |
194 | if (!(line < line_end && (*line == ' ' || *line == '\t'))) |
195 | { |
196 | fprintf (stderr, "%s:%u:" |
197 | " missing argument in %%define %s ARG declaration.\n" , |
198 | pretty_input_file_name (), lineno, decl); |
199 | exit (1); |
200 | } |
201 | do |
202 | line++; |
203 | while (line < line_end && (*line == ' ' || *line == '\t')); |
204 | |
205 | /* The next word is the argument. */ |
206 | char *arg = new char[line_end - line + 1]; |
207 | char *p = arg; |
208 | while (line < line_end && !(*line == ' ' || *line == '\t' || *line == '\n')) |
209 | *p++ = *line++; |
210 | *p = '\0'; |
211 | |
212 | /* Skip whitespace. */ |
213 | while (line < line_end && (*line == ' ' || *line == '\t')) |
214 | line++; |
215 | |
216 | /* Expect end of line. */ |
217 | if (line < line_end && *line != '\n') |
218 | { |
219 | fprintf (stderr, "%s:%u: junk after declaration\n" , |
220 | pretty_input_file_name (), lineno); |
221 | exit (1); |
222 | } |
223 | |
224 | *argp = arg; |
225 | return true; |
226 | } |
227 | |
228 | /* Reads the entire input file. */ |
229 | void |
230 | Input::read_input () |
231 | { |
232 | /* The input file has the following structure: |
233 | DECLARATIONS |
234 | %% |
235 | KEYWORDS |
236 | %% |
237 | ADDITIONAL_CODE |
238 | Since the DECLARATIONS and the ADDITIONAL_CODE sections are optional, |
239 | we have to read the entire file in the case there is only one %% |
240 | separator line, in order to determine whether the structure is |
241 | DECLARATIONS |
242 | %% |
243 | KEYWORDS |
244 | or |
245 | KEYWORDS |
246 | %% |
247 | ADDITIONAL_CODE |
248 | When the option -t is given or when the first section contains |
249 | declaration lines starting with %, we go for the first interpretation, |
250 | otherwise for the second interpretation. */ |
251 | |
252 | char *input = NULL; |
253 | size_t input_size = 0; |
254 | int input_length = get_delim (&input, &input_size, EOF, _stream); |
255 | if (input_length < 0) |
256 | { |
257 | if (ferror (_stream)) |
258 | fprintf (stderr, "%s: error while reading input file\n" , |
259 | pretty_input_file_name ()); |
260 | else |
261 | fprintf (stderr, "%s: The input file is empty!\n" , |
262 | pretty_input_file_name ()); |
263 | exit (1); |
264 | } |
265 | |
266 | /* We use input_end as a limit, in order to cope with NUL bytes in the |
267 | input. But note that one trailing NUL byte has been added after |
268 | input_end, for convenience. */ |
269 | char *input_end = input + input_length; |
270 | |
271 | const char *declarations; |
272 | const char *declarations_end; |
273 | const char *keywords; |
274 | const char *keywords_end; |
275 | unsigned int keywords_lineno; |
276 | |
277 | /* Break up the input into the three sections. */ |
278 | { |
279 | const char *separator[2] = { NULL, NULL }; |
280 | unsigned int separator_lineno[2] = { 0, 0 }; |
281 | int separators = 0; |
282 | { |
283 | unsigned int lineno = 1; |
284 | for (const char *p = input; p < input_end; ) |
285 | { |
286 | if (p[0] == '%' && p[1] == '%') |
287 | { |
288 | separator[separators] = p; |
289 | separator_lineno[separators] = lineno; |
290 | if (++separators == 2) |
291 | break; |
292 | } |
293 | lineno++; |
294 | p = (const char *) memchr (p, '\n', input_end - p); |
295 | if (p != NULL) |
296 | p++; |
297 | else |
298 | p = input_end; |
299 | } |
300 | } |
301 | |
302 | bool has_declarations; |
303 | if (separators == 1) |
304 | { |
305 | if (option[TYPE]) |
306 | has_declarations = true; |
307 | else |
308 | { |
309 | has_declarations = false; |
310 | for (const char *p = input; p < separator[0]; ) |
311 | { |
312 | if (p[0] == '%') |
313 | { |
314 | has_declarations = true; |
315 | break; |
316 | } |
317 | p = (const char *) memchr (p, '\n', separator[0] - p); |
318 | if (p != NULL) |
319 | p++; |
320 | else |
321 | p = separator[0]; |
322 | } |
323 | } |
324 | } |
325 | else |
326 | has_declarations = (separators > 0); |
327 | |
328 | if (has_declarations) |
329 | { |
330 | declarations = input; |
331 | declarations_end = separator[0]; |
332 | /* Give a warning if the separator line is nonempty. */ |
333 | bool nonempty_line = false; |
334 | const char *p; |
335 | for (p = declarations_end + 2; p < input_end; ) |
336 | { |
337 | if (*p == '\n') |
338 | { |
339 | p++; |
340 | break; |
341 | } |
342 | if (!(*p == ' ' || *p == '\t')) |
343 | nonempty_line = true; |
344 | p++; |
345 | } |
346 | if (nonempty_line) |
347 | fprintf (stderr, "%s:%u: warning: junk after %%%% is ignored\n" , |
348 | pretty_input_file_name (), separator_lineno[0]); |
349 | keywords = p; |
350 | keywords_lineno = separator_lineno[0] + 1; |
351 | } |
352 | else |
353 | { |
354 | declarations = NULL; |
355 | declarations_end = NULL; |
356 | keywords = input; |
357 | keywords_lineno = 1; |
358 | } |
359 | |
360 | if (separators > (has_declarations ? 1 : 0)) |
361 | { |
362 | keywords_end = separator[separators-1]; |
363 | _verbatim_code = separator[separators-1] + 2; |
364 | _verbatim_code_end = input_end; |
365 | _verbatim_code_lineno = separator_lineno[separators-1]; |
366 | } |
367 | else |
368 | { |
369 | keywords_end = input_end; |
370 | _verbatim_code = NULL; |
371 | _verbatim_code_end = NULL; |
372 | _verbatim_code_lineno = 0; |
373 | } |
374 | } |
375 | |
376 | /* Parse the declarations section. */ |
377 | |
378 | _verbatim_declarations = NULL; |
379 | _verbatim_declarations_end = NULL; |
380 | _verbatim_declarations_lineno = 0; |
381 | _struct_decl = NULL; |
382 | _struct_decl_lineno = 0; |
383 | _return_type = NULL; |
384 | _struct_tag = NULL; |
385 | { |
386 | unsigned int lineno = 1; |
387 | char *struct_decl = NULL; |
388 | unsigned int *struct_decl_linenos = NULL; |
389 | unsigned int struct_decl_linecount = 0; |
390 | for (const char *line = declarations; line < declarations_end; ) |
391 | { |
392 | const char *line_end; |
393 | line_end = (const char *) memchr (line, '\n', declarations_end - line); |
394 | if (line_end != NULL) |
395 | line_end++; |
396 | else |
397 | line_end = declarations_end; |
398 | |
399 | if (*line == '%') |
400 | { |
401 | if (line[1] == '{') |
402 | { |
403 | /* Handle %{. */ |
404 | if (_verbatim_declarations != NULL) |
405 | { |
406 | fprintf (stderr, "%s:%u:\n%s:%u:" |
407 | " only one %%{...%%} section is allowed\n" , |
408 | pretty_input_file_name (), |
409 | _verbatim_declarations_lineno, |
410 | pretty_input_file_name (), lineno); |
411 | exit (1); |
412 | } |
413 | _verbatim_declarations = line + 2; |
414 | _verbatim_declarations_lineno = lineno; |
415 | } |
416 | else if (line[1] == '}') |
417 | { |
418 | /* Handle %}. */ |
419 | if (_verbatim_declarations == NULL) |
420 | { |
421 | fprintf (stderr, "%s:%u:" |
422 | " %%} outside of %%{...%%} section\n" , |
423 | pretty_input_file_name (), lineno); |
424 | exit (1); |
425 | } |
426 | if (_verbatim_declarations_end != NULL) |
427 | { |
428 | fprintf (stderr, "%s:%u:" |
429 | " %%{...%%} section already closed\n" , |
430 | pretty_input_file_name (), lineno); |
431 | exit (1); |
432 | } |
433 | _verbatim_declarations_end = line; |
434 | /* Give a warning if the rest of the line is nonempty. */ |
435 | bool nonempty_line = false; |
436 | const char *q; |
437 | for (q = line + 2; q < line_end; q++) |
438 | { |
439 | if (*q == '\n') |
440 | { |
441 | q++; |
442 | break; |
443 | } |
444 | if (!(*q == ' ' || *q == '\t')) |
445 | nonempty_line = true; |
446 | } |
447 | if (nonempty_line) |
448 | fprintf (stderr, "%s:%u:" |
449 | " warning: junk after %%} is ignored\n" , |
450 | pretty_input_file_name (), lineno); |
451 | } |
452 | else if (_verbatim_declarations != NULL |
453 | && _verbatim_declarations_end == NULL) |
454 | { |
455 | fprintf (stderr, "%s:%u:" |
456 | " warning: %% directives are ignored" |
457 | " inside the %%{...%%} section\n" , |
458 | pretty_input_file_name (), lineno); |
459 | } |
460 | else |
461 | { |
462 | char *arg; |
463 | |
464 | if (is_declaration_with_arg (line, line_end, lineno, |
465 | "delimiters" , &arg)) |
466 | option.set_delimiters (arg); |
467 | else |
468 | |
469 | if (is_declaration (line, line_end, lineno, "struct-type" )) |
470 | option.set (TYPE); |
471 | else |
472 | |
473 | if (is_declaration (line, line_end, lineno, "ignore-case" )) |
474 | option.set (UPPERLOWER); |
475 | else |
476 | |
477 | if (is_declaration_with_arg (line, line_end, lineno, |
478 | "language" , &arg)) |
479 | option.set_language (arg); |
480 | else |
481 | |
482 | if (is_define_declaration (line, line_end, lineno, |
483 | "slot-name" , &arg)) |
484 | option.set_slot_name (arg); |
485 | else |
486 | |
487 | if (is_define_declaration (line, line_end, lineno, |
488 | "initializer-suffix" , &arg)) |
489 | option.set_initializer_suffix (arg); |
490 | else |
491 | |
492 | if (is_define_declaration (line, line_end, lineno, |
493 | "hash-function-name" , &arg)) |
494 | option.set_hash_name (arg); |
495 | else |
496 | |
497 | if (is_define_declaration (line, line_end, lineno, |
498 | "lookup-function-name" , &arg)) |
499 | option.set_function_name (arg); |
500 | else |
501 | |
502 | if (is_define_declaration (line, line_end, lineno, |
503 | "class-name" , &arg)) |
504 | option.set_class_name (arg); |
505 | else |
506 | |
507 | if (is_declaration (line, line_end, lineno, "7bit" )) |
508 | option.set (SEVENBIT); |
509 | else |
510 | |
511 | if (is_declaration (line, line_end, lineno, "compare-lengths" )) |
512 | option.set (LENTABLE); |
513 | else |
514 | |
515 | if (is_declaration (line, line_end, lineno, "compare-strncmp" )) |
516 | option.set (COMP); |
517 | else |
518 | |
519 | if (is_declaration (line, line_end, lineno, "readonly-tables" )) |
520 | option.set (CONST); |
521 | else |
522 | |
523 | if (is_declaration (line, line_end, lineno, "enum" )) |
524 | option.set (ENUM); |
525 | else |
526 | |
527 | if (is_declaration (line, line_end, lineno, "includes" )) |
528 | option.set (INCLUDE); |
529 | else |
530 | |
531 | if (is_declaration (line, line_end, lineno, "global-table" )) |
532 | option.set (GLOBAL); |
533 | else |
534 | |
535 | if (is_declaration (line, line_end, lineno, "pic" )) |
536 | option.set (SHAREDLIB); |
537 | else |
538 | |
539 | if (is_define_declaration (line, line_end, lineno, |
540 | "string-pool-name" , &arg)) |
541 | option.set_stringpool_name (arg); |
542 | else |
543 | |
544 | if (is_declaration (line, line_end, lineno, "null-strings" )) |
545 | option.set (NULLSTRINGS); |
546 | else |
547 | |
548 | if (is_define_declaration (line, line_end, lineno, |
549 | "constants-prefix" , &arg)) |
550 | option.set_constants_prefix (arg); |
551 | else |
552 | |
553 | if (is_define_declaration (line, line_end, lineno, |
554 | "word-array-name" , &arg)) |
555 | option.set_wordlist_name (arg); |
556 | else |
557 | |
558 | if (is_define_declaration (line, line_end, lineno, |
559 | "length-table-name" , &arg)) |
560 | option.set_lengthtable_name (arg); |
561 | else |
562 | |
563 | if (is_declaration_with_arg (line, line_end, lineno, |
564 | "switch" , &arg)) |
565 | { |
566 | option.set_total_switches (atoi (arg)); |
567 | if (option.get_total_switches () <= 0) |
568 | { |
569 | fprintf (stderr, "%s:%u: number of switches %s" |
570 | " must be a positive number\n" , |
571 | pretty_input_file_name (), lineno, arg); |
572 | exit (1); |
573 | } |
574 | } |
575 | else |
576 | |
577 | if (is_declaration (line, line_end, lineno, "omit-struct-type" )) |
578 | option.set (NOTYPE); |
579 | else |
580 | |
581 | { |
582 | fprintf (stderr, "%s:%u: unrecognized %% directive\n" , |
583 | pretty_input_file_name (), lineno); |
584 | exit (1); |
585 | } |
586 | } |
587 | } |
588 | else if (!(_verbatim_declarations != NULL |
589 | && _verbatim_declarations_end == NULL)) |
590 | { |
591 | /* Append the line to struct_decl. */ |
592 | size_t old_len = (struct_decl ? strlen (struct_decl) : 0); |
593 | size_t line_len = line_end - line; |
594 | size_t new_len = old_len + line_len + 1; |
595 | char *new_struct_decl = new char[new_len]; |
596 | if (old_len > 0) |
597 | memcpy (new_struct_decl, struct_decl, old_len); |
598 | memcpy (new_struct_decl + old_len, line, line_len); |
599 | new_struct_decl[old_len + line_len] = '\0'; |
600 | if (struct_decl) |
601 | delete[] struct_decl; |
602 | struct_decl = new_struct_decl; |
603 | /* Append the lineno to struct_decl_linenos. */ |
604 | unsigned int *new_struct_decl_linenos = |
605 | new unsigned int[struct_decl_linecount + 1]; |
606 | if (struct_decl_linecount > 0) |
607 | memcpy (new_struct_decl_linenos, struct_decl_linenos, |
608 | struct_decl_linecount * sizeof (unsigned int)); |
609 | new_struct_decl_linenos[struct_decl_linecount] = lineno; |
610 | if (struct_decl_linenos) |
611 | delete[] struct_decl_linenos; |
612 | struct_decl_linenos = new_struct_decl_linenos; |
613 | /* Increment struct_decl_linecount. */ |
614 | struct_decl_linecount++; |
615 | } |
616 | lineno++; |
617 | line = line_end; |
618 | } |
619 | if (_verbatim_declarations != NULL && _verbatim_declarations_end == NULL) |
620 | { |
621 | fprintf (stderr, "%s:%u: unterminated %%{ section\n" , |
622 | pretty_input_file_name (), _verbatim_declarations_lineno); |
623 | exit (1); |
624 | } |
625 | |
626 | /* Determine _struct_decl, _return_type, _struct_tag. */ |
627 | if (option[TYPE]) |
628 | { |
629 | if (struct_decl) |
630 | { |
631 | /* Drop leading whitespace and comments. */ |
632 | { |
633 | char *p = struct_decl; |
634 | unsigned int *l = struct_decl_linenos; |
635 | for (;;) |
636 | { |
637 | if (p[0] == ' ' || p[0] == '\t') |
638 | { |
639 | p++; |
640 | continue; |
641 | } |
642 | if (p[0] == '\n') |
643 | { |
644 | l++; |
645 | p++; |
646 | continue; |
647 | } |
648 | if (p[0] == '/') |
649 | { |
650 | if (p[1] == '*') |
651 | { |
652 | /* Skip over ANSI C style comment. */ |
653 | p += 2; |
654 | while (p[0] != '\0') |
655 | { |
656 | if (p[0] == '*' && p[1] == '/') |
657 | { |
658 | p += 2; |
659 | break; |
660 | } |
661 | if (p[0] == '\n') |
662 | l++; |
663 | p++; |
664 | } |
665 | continue; |
666 | } |
667 | if (p[1] == '/') |
668 | { |
669 | /* Skip over ISO C99 or C++ style comment. */ |
670 | p += 2; |
671 | while (p[0] != '\0' && p[0] != '\n') |
672 | p++; |
673 | if (p[0] == '\n') |
674 | { |
675 | l++; |
676 | p++; |
677 | } |
678 | continue; |
679 | } |
680 | } |
681 | break; |
682 | } |
683 | if (p != struct_decl) |
684 | { |
685 | size_t len = strlen (p); |
686 | char *new_struct_decl = new char[len + 1]; |
687 | memcpy (new_struct_decl, p, len + 1); |
688 | delete[] struct_decl; |
689 | struct_decl = new_struct_decl; |
690 | } |
691 | _struct_decl_lineno = *l; |
692 | } |
693 | /* Drop trailing whitespace. */ |
694 | for (char *p = struct_decl + strlen (struct_decl); p > struct_decl;) |
695 | if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') |
696 | *--p = '\0'; |
697 | else |
698 | break; |
699 | } |
700 | if (struct_decl == NULL || struct_decl[0] == '\0') |
701 | { |
702 | fprintf (stderr, "%s: missing struct declaration" |
703 | " for option --struct-type\n" , |
704 | pretty_input_file_name ()); |
705 | exit (1); |
706 | } |
707 | { |
708 | /* Ensure trailing semicolon. */ |
709 | size_t old_len = strlen (struct_decl); |
710 | if (struct_decl[old_len - 1] != ';') |
711 | { |
712 | char *new_struct_decl = new char[old_len + 2]; |
713 | memcpy (new_struct_decl, struct_decl, old_len); |
714 | new_struct_decl[old_len] = ';'; |
715 | new_struct_decl[old_len + 1] = '\0'; |
716 | delete[] struct_decl; |
717 | struct_decl = new_struct_decl; |
718 | } |
719 | } |
720 | /* Set _struct_decl to the entire declaration. */ |
721 | _struct_decl = struct_decl; |
722 | /* Set _struct_tag to the naked "struct something". */ |
723 | const char *p; |
724 | for (p = struct_decl; *p && *p != '{' && *p != ';' && *p != '\n'; p++) |
725 | ; |
726 | for (; p > struct_decl;) |
727 | if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') |
728 | --p; |
729 | else |
730 | break; |
731 | size_t struct_tag_length = p - struct_decl; |
732 | char *struct_tag = new char[struct_tag_length + 1]; |
733 | memcpy (struct_tag, struct_decl, struct_tag_length); |
734 | struct_tag[struct_tag_length] = '\0'; |
735 | _struct_tag = struct_tag; |
736 | /* The return type of the lookup function is "struct something *". |
737 | No "const" here, because if !option[CONST], some user code might |
738 | want to modify the structure. */ |
739 | char *return_type = new char[struct_tag_length + 3]; |
740 | memcpy (return_type, struct_decl, struct_tag_length); |
741 | return_type[struct_tag_length] = ' '; |
742 | return_type[struct_tag_length + 1] = '*'; |
743 | return_type[struct_tag_length + 2] = '\0'; |
744 | _return_type = return_type; |
745 | } |
746 | |
747 | if (struct_decl_linenos) |
748 | delete[] struct_decl_linenos; |
749 | } |
750 | |
751 | /* Parse the keywords section. */ |
752 | { |
753 | Keyword_List **list_tail = &_head; |
754 | const char *delimiters = option.get_delimiters (); |
755 | unsigned int lineno = keywords_lineno; |
756 | bool charset_dependent = false; |
757 | for (const char *line = keywords; line < keywords_end; ) |
758 | { |
759 | const char *line_end; |
760 | line_end = (const char *) memchr (line, '\n', keywords_end - line); |
761 | if (line_end != NULL) |
762 | line_end++; |
763 | else |
764 | line_end = keywords_end; |
765 | |
766 | if (line[0] == '#') |
767 | ; /* Comment line. */ |
768 | else if (line[0] == '%') |
769 | { |
770 | fprintf (stderr, "%s:%u:" |
771 | " declarations are not allowed in the keywords section.\n" |
772 | "To declare a keyword starting with %%, enclose it in" |
773 | " double-quotes.\n" , |
774 | pretty_input_file_name (), lineno); |
775 | exit (1); |
776 | } |
777 | else |
778 | { |
779 | /* An input line carrying a keyword. */ |
780 | const char *keyword; |
781 | size_t keyword_length; |
782 | const char *rest; |
783 | |
784 | if (line[0] == '"') |
785 | { |
786 | /* Parse a string in ANSI C syntax. */ |
787 | char *kp = new char[line_end-line]; |
788 | keyword = kp; |
789 | const char *lp = line + 1; |
790 | |
791 | for (;;) |
792 | { |
793 | if (lp == line_end) |
794 | { |
795 | fprintf (stderr, "%s:%u: unterminated string\n" , |
796 | pretty_input_file_name (), lineno); |
797 | exit (1); |
798 | } |
799 | |
800 | char c = *lp; |
801 | if (c == '\\') |
802 | { |
803 | c = *++lp; |
804 | switch (c) |
805 | { |
806 | case '0': case '1': case '2': case '3': |
807 | case '4': case '5': case '6': case '7': |
808 | { |
809 | int code = 0; |
810 | int count = 0; |
811 | while (count < 3 && *lp >= '0' && *lp <= '7') |
812 | { |
813 | code = (code << 3) + (*lp - '0'); |
814 | lp++; |
815 | count++; |
816 | } |
817 | if (code > UCHAR_MAX) |
818 | fprintf (stderr, |
819 | "%s:%u: octal escape out of range\n" , |
820 | pretty_input_file_name (), lineno); |
821 | *kp = static_cast<char>(code); |
822 | break; |
823 | } |
824 | case 'x': |
825 | { |
826 | int code = 0; |
827 | int count = 0; |
828 | lp++; |
829 | while ((*lp >= '0' && *lp <= '9') |
830 | || (*lp >= 'A' && *lp <= 'F') |
831 | || (*lp >= 'a' && *lp <= 'f')) |
832 | { |
833 | code = (code << 4) |
834 | + (*lp >= 'A' && *lp <= 'F' |
835 | ? *lp - 'A' + 10 : |
836 | *lp >= 'a' && *lp <= 'f' |
837 | ? *lp - 'a' + 10 : |
838 | *lp - '0'); |
839 | lp++; |
840 | count++; |
841 | } |
842 | if (count == 0) |
843 | fprintf (stderr, "%s:%u: hexadecimal escape" |
844 | " without any hex digits\n" , |
845 | pretty_input_file_name (), lineno); |
846 | if (code > UCHAR_MAX) |
847 | fprintf (stderr, "%s:%u: hexadecimal escape" |
848 | " out of range\n" , |
849 | pretty_input_file_name (), lineno); |
850 | *kp = static_cast<char>(code); |
851 | break; |
852 | } |
853 | case '\\': case '\'': case '"': |
854 | *kp = c; |
855 | lp++; |
856 | charset_dependent = true; |
857 | break; |
858 | case 'n': |
859 | *kp = '\n'; |
860 | lp++; |
861 | charset_dependent = true; |
862 | break; |
863 | case 't': |
864 | *kp = '\t'; |
865 | lp++; |
866 | charset_dependent = true; |
867 | break; |
868 | case 'r': |
869 | *kp = '\r'; |
870 | lp++; |
871 | charset_dependent = true; |
872 | break; |
873 | case 'f': |
874 | *kp = '\f'; |
875 | lp++; |
876 | charset_dependent = true; |
877 | break; |
878 | case 'b': |
879 | *kp = '\b'; |
880 | lp++; |
881 | charset_dependent = true; |
882 | break; |
883 | case 'a': |
884 | *kp = '\a'; |
885 | lp++; |
886 | charset_dependent = true; |
887 | break; |
888 | case 'v': |
889 | *kp = '\v'; |
890 | lp++; |
891 | charset_dependent = true; |
892 | break; |
893 | default: |
894 | fprintf (stderr, "%s:%u: invalid escape sequence" |
895 | " in string\n" , |
896 | pretty_input_file_name (), lineno); |
897 | exit (1); |
898 | } |
899 | } |
900 | else if (c == '"') |
901 | break; |
902 | else |
903 | { |
904 | *kp = c; |
905 | lp++; |
906 | charset_dependent = true; |
907 | } |
908 | kp++; |
909 | } |
910 | lp++; |
911 | if (lp < line_end && *lp != '\n') |
912 | { |
913 | if (strchr (delimiters, *lp) == NULL) |
914 | { |
915 | fprintf (stderr, "%s:%u: string not followed" |
916 | " by delimiter\n" , |
917 | pretty_input_file_name (), lineno); |
918 | exit (1); |
919 | } |
920 | lp++; |
921 | } |
922 | keyword_length = kp - keyword; |
923 | if (option[TYPE]) |
924 | { |
925 | char *line_rest = new char[line_end - lp + 1]; |
926 | memcpy (line_rest, lp, line_end - lp); |
927 | line_rest[line_end - lp - |
928 | (line_end > lp && line_end[-1] == '\n' ? 1 : 0)] |
929 | = '\0'; |
930 | rest = line_rest; |
931 | } |
932 | else |
933 | rest = empty_string; |
934 | } |
935 | else |
936 | { |
937 | /* Not a string. Look for the delimiter. */ |
938 | const char *lp = line; |
939 | for (;;) |
940 | { |
941 | if (!(lp < line_end && *lp != '\n')) |
942 | { |
943 | keyword = line; |
944 | keyword_length = lp - line; |
945 | rest = empty_string; |
946 | break; |
947 | } |
948 | if (strchr (delimiters, *lp) != NULL) |
949 | { |
950 | keyword = line; |
951 | keyword_length = lp - line; |
952 | lp++; |
953 | if (option[TYPE]) |
954 | { |
955 | char *line_rest = new char[line_end - lp + 1]; |
956 | memcpy (line_rest, lp, line_end - lp); |
957 | line_rest[line_end - lp - |
958 | (line_end > lp && line_end[-1] == '\n' |
959 | ? 1 : 0)] |
960 | = '\0'; |
961 | rest = line_rest; |
962 | } |
963 | else |
964 | rest = empty_string; |
965 | break; |
966 | } |
967 | lp++; |
968 | } |
969 | if (keyword_length > 0) |
970 | charset_dependent = true; |
971 | } |
972 | |
973 | /* Allocate Keyword and add it to the list. */ |
974 | Keyword *new_kw = _factory->create_keyword (keyword, keyword_length, |
975 | rest); |
976 | new_kw->_lineno = lineno; |
977 | *list_tail = new Keyword_List (new_kw); |
978 | list_tail = &(*list_tail)->rest(); |
979 | } |
980 | |
981 | lineno++; |
982 | line = line_end; |
983 | } |
984 | *list_tail = NULL; |
985 | |
986 | if (_head == NULL) |
987 | { |
988 | fprintf (stderr, "%s: No keywords in input file!\n" , |
989 | pretty_input_file_name ()); |
990 | exit (1); |
991 | } |
992 | |
993 | _charset_dependent = charset_dependent; |
994 | } |
995 | |
996 | /* To be freed in the destructor. */ |
997 | _input = input; |
998 | _input_end = input_end; |
999 | } |
1000 | |
1001 | Input::~Input () |
1002 | { |
1003 | /* Free allocated memory. */ |
1004 | delete[] const_cast<char*>(_return_type); |
1005 | delete[] const_cast<char*>(_struct_tag); |
1006 | delete[] const_cast<char*>(_struct_decl); |
1007 | delete[] _input; |
1008 | } |
1009 | |