1228060Sbapt/* Input routines.
2228060Sbapt   Copyright (C) 1989-1998, 2002-2004 Free Software Foundation, Inc.
3228060Sbapt   Written by Douglas C. Schmidt <schmidt@ics.uci.edu>
4228060Sbapt   and Bruno Haible <bruno@clisp.org>.
5228060Sbapt
6228060Sbapt   This file is part of GNU GPERF.
7228060Sbapt
8228060Sbapt   GNU GPERF is free software; you can redistribute it and/or modify
9228060Sbapt   it under the terms of the GNU General Public License as published by
10228060Sbapt   the Free Software Foundation; either version 2, or (at your option)
11228060Sbapt   any later version.
12228060Sbapt
13228060Sbapt   GNU GPERF is distributed in the hope that it will be useful,
14228060Sbapt   but WITHOUT ANY WARRANTY; without even the implied warranty of
15228060Sbapt   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16228060Sbapt   GNU General Public License for more details.
17228060Sbapt
18228060Sbapt   You should have received a copy of the GNU General Public License
19228060Sbapt   along with this program; see the file COPYING.
20228060Sbapt   If not, write to the Free Software Foundation, Inc.,
21228060Sbapt   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
22228060Sbapt
23228060Sbapt/* Specification. */
24228060Sbapt#include "input.h"
25228060Sbapt
26228060Sbapt#include <stdio.h>
27228060Sbapt#include <stdlib.h> /* declares exit() */
28228060Sbapt#include <string.h> /* declares strncpy(), strchr() */
29228060Sbapt#include <limits.h> /* defines UCHAR_MAX etc. */
30228060Sbapt#include "options.h"
31228060Sbapt#include "getline.h"
32228060Sbapt
33228060SbaptInput::Input (FILE *stream, Keyword_Factory *keyword_factory)
34228060Sbapt  : _stream (stream), _factory (keyword_factory)
35228060Sbapt{
36228060Sbapt}
37228060Sbapt
38228060Sbapt/* Returns a pretty representation of the input file name, for error and
39228060Sbapt   warning messages.  */
40228060Sbaptstatic const char *
41228060Sbaptpretty_input_file_name ()
42228060Sbapt{
43228060Sbapt  if (option.get_input_file_name ())
44228060Sbapt    return option.get_input_file_name ();
45228060Sbapt  else
46228060Sbapt    return "(standard input)";
47228060Sbapt}
48228060Sbapt
49228060Sbapt/* Returns true if the given line contains a "%DECL" declaration.  */
50228060Sbaptstatic bool
51228060Sbaptis_declaration (const char *line, const char *line_end, unsigned int lineno,
52228060Sbapt                const char *decl)
53228060Sbapt{
54228060Sbapt  /* Skip '%'.  */
55228060Sbapt  line++;
56228060Sbapt
57228060Sbapt  /* Skip DECL.  */
58228060Sbapt  for (const char *d = decl; *d; d++)
59228060Sbapt    {
60228060Sbapt      if (!(line < line_end))
61228060Sbapt        return false;
62228060Sbapt      if (!(*line == *d || (*d == '-' && *line == '_')))
63228060Sbapt        return false;
64228060Sbapt      line++;
65228060Sbapt    }
66228060Sbapt  if (line < line_end
67228060Sbapt      && ((*line >= 'A' && *line <= 'Z')
68228060Sbapt          || (*line >= 'a' && *line <= 'z')
69228060Sbapt          || *line == '-' || *line == '_'))
70228060Sbapt    return false;
71228060Sbapt
72228060Sbapt  /* OK, found DECL.  */
73228060Sbapt
74228060Sbapt  /* Skip whitespace.  */
75228060Sbapt  while (line < line_end && (*line == ' ' || *line == '\t'))
76228060Sbapt    line++;
77228060Sbapt
78228060Sbapt  /* Expect end of line.  */
79228060Sbapt  if (line < line_end && *line != '\n')
80228060Sbapt    {
81228060Sbapt      fprintf (stderr, "%s:%u: junk after declaration\n",
82228060Sbapt               pretty_input_file_name (), lineno);
83228060Sbapt      exit (1);
84228060Sbapt    }
85228060Sbapt
86228060Sbapt  return true;
87228060Sbapt}
88228060Sbapt
89228060Sbapt/* Tests if the given line contains a "%DECL=ARG" declaration.
90228060Sbapt   If yes, it sets *ARGP to the argument, and returns true.
91228060Sbapt   Otherwise, it returns false.  */
92228060Sbaptstatic bool
93228060Sbaptis_declaration_with_arg (const char *line, const char *line_end,
94228060Sbapt                         unsigned int lineno,
95228060Sbapt                         const char *decl, char **argp)
96228060Sbapt{
97228060Sbapt  /* Skip '%'.  */
98228060Sbapt  line++;
99228060Sbapt
100228060Sbapt  /* Skip DECL.  */
101228060Sbapt  for (const char *d = decl; *d; d++)
102228060Sbapt    {
103228060Sbapt      if (!(line < line_end))
104228060Sbapt        return false;
105228060Sbapt      if (!(*line == *d || (*d == '-' && *line == '_')))
106228060Sbapt        return false;
107228060Sbapt      line++;
108228060Sbapt    }
109228060Sbapt  if (line < line_end
110228060Sbapt      && ((*line >= 'A' && *line <= 'Z')
111228060Sbapt          || (*line >= 'a' && *line <= 'z')
112228060Sbapt          || *line == '-' || *line == '_'))
113228060Sbapt    return false;
114228060Sbapt
115228060Sbapt  /* OK, found DECL.  */
116228060Sbapt
117228060Sbapt  /* Skip '='.  */
118228060Sbapt  if (!(line < line_end && *line == '='))
119228060Sbapt    {
120228060Sbapt      fprintf (stderr, "%s:%u: missing argument in %%%s=ARG declaration.\n",
121228060Sbapt               pretty_input_file_name (), lineno, decl);
122228060Sbapt      exit (1);
123228060Sbapt    }
124228060Sbapt  line++;
125228060Sbapt
126228060Sbapt  /* The next word is the argument.  */
127228060Sbapt  char *arg = new char[line_end - line + 1];
128228060Sbapt  char *p = arg;
129228060Sbapt  while (line < line_end && !(*line == ' ' || *line == '\t' || *line == '\n'))
130228060Sbapt    *p++ = *line++;
131228060Sbapt  *p = '\0';
132228060Sbapt
133228060Sbapt  /* Skip whitespace.  */
134228060Sbapt  while (line < line_end && (*line == ' ' || *line == '\t'))
135228060Sbapt    line++;
136228060Sbapt
137228060Sbapt  /* Expect end of line.  */
138228060Sbapt  if (line < line_end && *line != '\n')
139228060Sbapt    {
140228060Sbapt      fprintf (stderr, "%s:%u: junk after declaration\n",
141228060Sbapt               pretty_input_file_name (), lineno);
142228060Sbapt      exit (1);
143228060Sbapt    }
144228060Sbapt
145228060Sbapt  *argp = arg;
146228060Sbapt  return true;
147228060Sbapt}
148228060Sbapt
149228060Sbapt/* Tests if the given line contains a "%define DECL ARG" declaration.
150228060Sbapt   If yes, it sets *ARGP to the argument, and returns true.
151228060Sbapt   Otherwise, it returns false.  */
152228060Sbaptstatic bool
153228060Sbaptis_define_declaration (const char *line, const char *line_end,
154228060Sbapt                       unsigned int lineno,
155228060Sbapt                       const char *decl, char **argp)
156228060Sbapt{
157228060Sbapt  /* Skip '%'.  */
158228060Sbapt  line++;
159228060Sbapt
160228060Sbapt  /* Skip "define".  */
161228060Sbapt  {
162228060Sbapt    for (const char *d = "define"; *d; d++)
163228060Sbapt      {
164228060Sbapt        if (!(line < line_end))
165228060Sbapt          return false;
166228060Sbapt        if (!(*line == *d))
167228060Sbapt          return false;
168228060Sbapt        line++;
169228060Sbapt      }
170228060Sbapt    if (!(line < line_end && (*line == ' ' || *line == '\t')))
171228060Sbapt      return false;
172228060Sbapt  }
173228060Sbapt
174228060Sbapt  /* Skip whitespace.  */
175228060Sbapt  while (line < line_end && (*line == ' ' || *line == '\t'))
176228060Sbapt    line++;
177228060Sbapt
178228060Sbapt  /* Skip DECL.  */
179228060Sbapt  for (const char *d = decl; *d; d++)
180228060Sbapt    {
181228060Sbapt      if (!(line < line_end))
182228060Sbapt        return false;
183228060Sbapt      if (!(*line == *d || (*d == '-' && *line == '_')))
184228060Sbapt        return false;
185228060Sbapt      line++;
186228060Sbapt    }
187228060Sbapt  if (line < line_end
188228060Sbapt      && ((*line >= 'A' && *line <= 'Z')
189228060Sbapt          || (*line >= 'a' && *line <= 'z')
190228060Sbapt          || *line == '-' || *line == '_'))
191228060Sbapt    return false;
192228060Sbapt
193228060Sbapt  /* OK, found DECL.  */
194228060Sbapt
195228060Sbapt  /* Skip whitespace.  */
196228060Sbapt  if (!(line < line_end && (*line == ' ' || *line == '\t')))
197228060Sbapt    {
198228060Sbapt      fprintf (stderr, "%s:%u:"
199228060Sbapt               " missing argument in %%define %s ARG declaration.\n",
200228060Sbapt               pretty_input_file_name (), lineno, decl);
201228060Sbapt      exit (1);
202228060Sbapt    }
203228060Sbapt  do
204228060Sbapt    line++;
205228060Sbapt  while (line < line_end && (*line == ' ' || *line == '\t'));
206228060Sbapt
207228060Sbapt  /* The next word is the argument.  */
208228060Sbapt  char *arg = new char[line_end - line + 1];
209228060Sbapt  char *p = arg;
210228060Sbapt  while (line < line_end && !(*line == ' ' || *line == '\t' || *line == '\n'))
211228060Sbapt    *p++ = *line++;
212228060Sbapt  *p = '\0';
213228060Sbapt
214228060Sbapt  /* Skip whitespace.  */
215228060Sbapt  while (line < line_end && (*line == ' ' || *line == '\t'))
216228060Sbapt    line++;
217228060Sbapt
218228060Sbapt  /* Expect end of line.  */
219228060Sbapt  if (line < line_end && *line != '\n')
220228060Sbapt    {
221228060Sbapt      fprintf (stderr, "%s:%u: junk after declaration\n",
222228060Sbapt               pretty_input_file_name (), lineno);
223228060Sbapt      exit (1);
224228060Sbapt    }
225228060Sbapt
226228060Sbapt  *argp = arg;
227228060Sbapt  return true;
228228060Sbapt}
229228060Sbapt
230228060Sbapt/* Reads the entire input file.  */
231228060Sbaptvoid
232228060SbaptInput::read_input ()
233228060Sbapt{
234228060Sbapt  /* The input file has the following structure:
235228060Sbapt        DECLARATIONS
236228060Sbapt        %%
237228060Sbapt        KEYWORDS
238228060Sbapt        %%
239228060Sbapt        ADDITIONAL_CODE
240228060Sbapt     Since the DECLARATIONS and the ADDITIONAL_CODE sections are optional,
241228060Sbapt     we have to read the entire file in the case there is only one %%
242228060Sbapt     separator line, in order to determine whether the structure is
243228060Sbapt        DECLARATIONS
244228060Sbapt        %%
245228060Sbapt        KEYWORDS
246228060Sbapt     or
247228060Sbapt        KEYWORDS
248228060Sbapt        %%
249228060Sbapt        ADDITIONAL_CODE
250228060Sbapt     When the option -t is given or when the first section contains
251228060Sbapt     declaration lines starting with %, we go for the first interpretation,
252228060Sbapt     otherwise for the second interpretation.  */
253228060Sbapt
254228060Sbapt  char *input = NULL;
255228060Sbapt  size_t input_size = 0;
256228060Sbapt  int input_length = get_delim (&input, &input_size, EOF, _stream);
257228060Sbapt  if (input_length < 0)
258228060Sbapt    {
259228060Sbapt      if (ferror (_stream))
260228060Sbapt        fprintf (stderr, "%s: error while reading input file\n",
261228060Sbapt                 pretty_input_file_name ());
262228060Sbapt      else
263228060Sbapt        fprintf (stderr, "%s: The input file is empty!\n",
264228060Sbapt                 pretty_input_file_name ());
265228060Sbapt      exit (1);
266228060Sbapt    }
267228060Sbapt
268228060Sbapt  /* We use input_end as a limit, in order to cope with NUL bytes in the
269228060Sbapt     input.  But note that one trailing NUL byte has been added after
270228060Sbapt     input_end, for convenience.  */
271228060Sbapt  char *input_end = input + input_length;
272228060Sbapt
273228060Sbapt  const char *declarations;
274228060Sbapt  const char *declarations_end;
275228060Sbapt  const char *keywords;
276228060Sbapt  const char *keywords_end;
277228060Sbapt  unsigned int keywords_lineno;
278228060Sbapt
279228060Sbapt  /* Break up the input into the three sections.  */
280228060Sbapt  {
281228060Sbapt    const char *separator[2] = { NULL, NULL };
282228060Sbapt    unsigned int separator_lineno[2] = { 0, 0 };
283228060Sbapt    int separators = 0;
284228060Sbapt    {
285228060Sbapt      unsigned int lineno = 1;
286228060Sbapt      for (const char *p = input; p < input_end; )
287228060Sbapt        {
288228060Sbapt          if (p[0] == '%' && p[1] == '%')
289228060Sbapt            {
290228060Sbapt              separator[separators] = p;
291228060Sbapt              separator_lineno[separators] = lineno;
292228060Sbapt              if (++separators == 2)
293228060Sbapt                break;
294228060Sbapt            }
295228060Sbapt          lineno++;
296228060Sbapt          p = (const char *) memchr (p, '\n', input_end - p);
297228060Sbapt          if (p != NULL)
298228060Sbapt            p++;
299228060Sbapt          else
300228060Sbapt            p = input_end;
301228060Sbapt        }
302228060Sbapt    }
303228060Sbapt
304228060Sbapt    bool has_declarations;
305228060Sbapt    if (separators == 1)
306228060Sbapt      {
307228060Sbapt        if (option[TYPE])
308228060Sbapt          has_declarations = true;
309228060Sbapt        else
310228060Sbapt          {
311228060Sbapt            has_declarations = false;
312228060Sbapt            for (const char *p = input; p < separator[0]; )
313228060Sbapt              {
314228060Sbapt                if (p[0] == '%')
315228060Sbapt                  {
316228060Sbapt                    has_declarations = true;
317228060Sbapt                    break;
318228060Sbapt                  }
319228060Sbapt                p = (const char *) memchr (p, '\n', separator[0] - p);
320228060Sbapt                if (p != NULL)
321228060Sbapt                  p++;
322228060Sbapt                else
323228060Sbapt                  p = separator[0];
324228060Sbapt              }
325228060Sbapt          }
326228060Sbapt      }
327228060Sbapt    else
328228060Sbapt      has_declarations = (separators > 0);
329228060Sbapt
330228060Sbapt    if (has_declarations)
331228060Sbapt      {
332228060Sbapt        declarations = input;
333228060Sbapt        declarations_end = separator[0];
334228060Sbapt        /* Give a warning if the separator line is nonempty.  */
335228060Sbapt        bool nonempty_line = false;
336228060Sbapt        const char *p;
337228060Sbapt        for (p = declarations_end + 2; p < input_end; )
338228060Sbapt          {
339228060Sbapt            if (*p == '\n')
340228060Sbapt              {
341228060Sbapt                p++;
342228060Sbapt                break;
343228060Sbapt              }
344228060Sbapt            if (!(*p == ' ' || *p == '\t'))
345228060Sbapt              nonempty_line = true;
346228060Sbapt            p++;
347228060Sbapt          }
348228060Sbapt        if (nonempty_line)
349228060Sbapt          fprintf (stderr, "%s:%u: warning: junk after %%%% is ignored\n",
350228060Sbapt                   pretty_input_file_name (), separator_lineno[0]);
351228060Sbapt        keywords = p;
352228060Sbapt        keywords_lineno = separator_lineno[0] + 1;
353228060Sbapt      }
354228060Sbapt    else
355228060Sbapt      {
356228060Sbapt        declarations = NULL;
357228060Sbapt        declarations_end = NULL;
358228060Sbapt        keywords = input;
359228060Sbapt        keywords_lineno = 1;
360228060Sbapt      }
361228060Sbapt
362228060Sbapt    if (separators > (has_declarations ? 1 : 0))
363228060Sbapt      {
364228060Sbapt        keywords_end = separator[separators-1];
365228060Sbapt        _verbatim_code = separator[separators-1] + 2;
366228060Sbapt        _verbatim_code_end = input_end;
367228060Sbapt        _verbatim_code_lineno = separator_lineno[separators-1];
368228060Sbapt      }
369228060Sbapt    else
370228060Sbapt      {
371228060Sbapt        keywords_end = input_end;
372228060Sbapt        _verbatim_code = NULL;
373228060Sbapt        _verbatim_code_end = NULL;
374228060Sbapt        _verbatim_code_lineno = 0;
375228060Sbapt      }
376228060Sbapt  }
377228060Sbapt
378228060Sbapt  /* Parse the declarations section.  */
379228060Sbapt
380228060Sbapt  _verbatim_declarations = NULL;
381228060Sbapt  _verbatim_declarations_end = NULL;
382228060Sbapt  _verbatim_declarations_lineno = 0;
383228060Sbapt  _struct_decl = NULL;
384228060Sbapt  _struct_decl_lineno = 0;
385228060Sbapt  _return_type = NULL;
386228060Sbapt  _struct_tag = NULL;
387228060Sbapt  {
388228060Sbapt    unsigned int lineno = 1;
389228060Sbapt    char *struct_decl = NULL;
390228060Sbapt    unsigned int *struct_decl_linenos = NULL;
391228060Sbapt    unsigned int struct_decl_linecount = 0;
392228060Sbapt    for (const char *line = declarations; line < declarations_end; )
393228060Sbapt      {
394228060Sbapt        const char *line_end;
395228060Sbapt        line_end = (const char *) memchr (line, '\n', declarations_end - line);
396228060Sbapt        if (line_end != NULL)
397228060Sbapt          line_end++;
398228060Sbapt        else
399228060Sbapt          line_end = declarations_end;
400228060Sbapt
401228060Sbapt        if (*line == '%')
402228060Sbapt          {
403228060Sbapt            if (line[1] == '{')
404228060Sbapt              {
405228060Sbapt                /* Handle %{.  */
406228060Sbapt                if (_verbatim_declarations != NULL)
407228060Sbapt                  {
408228060Sbapt                    fprintf (stderr, "%s:%u:\n%s:%u:"
409228060Sbapt                             " only one %%{...%%} section is allowed\n",
410228060Sbapt                             pretty_input_file_name (),
411228060Sbapt                             _verbatim_declarations_lineno,
412228060Sbapt                             pretty_input_file_name (), lineno);
413228060Sbapt                    exit (1);
414228060Sbapt                  }
415228060Sbapt                _verbatim_declarations = line + 2;
416228060Sbapt                _verbatim_declarations_lineno = lineno;
417228060Sbapt              }
418228060Sbapt            else if (line[1] == '}')
419228060Sbapt              {
420228060Sbapt                /* Handle %}.  */
421228060Sbapt                if (_verbatim_declarations == NULL)
422228060Sbapt                  {
423228060Sbapt                    fprintf (stderr, "%s:%u:"
424228060Sbapt                             " %%} outside of %%{...%%} section\n",
425228060Sbapt                             pretty_input_file_name (), lineno);
426228060Sbapt                    exit (1);
427228060Sbapt                  }
428228060Sbapt                if (_verbatim_declarations_end != NULL)
429228060Sbapt                  {
430228060Sbapt                    fprintf (stderr, "%s:%u:"
431228060Sbapt                             " %%{...%%} section already closed\n",
432228060Sbapt                             pretty_input_file_name (), lineno);
433228060Sbapt                    exit (1);
434228060Sbapt                  }
435228060Sbapt                _verbatim_declarations_end = line;
436228060Sbapt                /* Give a warning if the rest of the line is nonempty.  */
437228060Sbapt                bool nonempty_line = false;
438228060Sbapt                const char *q;
439228060Sbapt                for (q = line + 2; q < line_end; q++)
440228060Sbapt                  {
441228060Sbapt                    if (*q == '\n')
442228060Sbapt                      {
443228060Sbapt                        q++;
444228060Sbapt                        break;
445228060Sbapt                      }
446228060Sbapt                    if (!(*q == ' ' || *q == '\t'))
447228060Sbapt                      nonempty_line = true;
448228060Sbapt                  }
449228060Sbapt                if (nonempty_line)
450228060Sbapt                  fprintf (stderr, "%s:%u:"
451228060Sbapt                           " warning: junk after %%} is ignored\n",
452228060Sbapt                           pretty_input_file_name (), lineno);
453228060Sbapt              }
454228060Sbapt            else if (_verbatim_declarations != NULL
455228060Sbapt                     && _verbatim_declarations_end == NULL)
456228060Sbapt              {
457228060Sbapt                fprintf (stderr, "%s:%u:"
458228060Sbapt                         " warning: %% directives are ignored"
459228060Sbapt                         " inside the %%{...%%} section\n",
460228060Sbapt                         pretty_input_file_name (), lineno);
461228060Sbapt              }
462228060Sbapt            else
463228060Sbapt              {
464228060Sbapt                char *arg;
465228060Sbapt
466228060Sbapt                if (is_declaration_with_arg (line, line_end, lineno,
467228060Sbapt                                             "delimiters", &arg))
468228060Sbapt                  option.set_delimiters (arg);
469228060Sbapt                else
470228060Sbapt
471228060Sbapt                if (is_declaration (line, line_end, lineno, "struct-type"))
472228060Sbapt                  option.set (TYPE);
473228060Sbapt                else
474228060Sbapt
475228060Sbapt                if (is_declaration (line, line_end, lineno, "ignore-case"))
476228060Sbapt                  option.set (UPPERLOWER);
477228060Sbapt                else
478228060Sbapt
479228060Sbapt                if (is_declaration_with_arg (line, line_end, lineno,
480228060Sbapt                                             "language", &arg))
481228060Sbapt                  option.set_language (arg);
482228060Sbapt                else
483228060Sbapt
484228060Sbapt                if (is_define_declaration (line, line_end, lineno,
485228060Sbapt                                           "slot-name", &arg))
486228060Sbapt                  option.set_slot_name (arg);
487228060Sbapt                else
488228060Sbapt
489228060Sbapt                if (is_define_declaration (line, line_end, lineno,
490228060Sbapt                                           "initializer-suffix", &arg))
491228060Sbapt                  option.set_initializer_suffix (arg);
492228060Sbapt                else
493228060Sbapt
494228060Sbapt                if (is_define_declaration (line, line_end, lineno,
495228060Sbapt                                           "hash-function-name", &arg))
496228060Sbapt                  option.set_hash_name (arg);
497228060Sbapt                else
498228060Sbapt
499228060Sbapt                if (is_define_declaration (line, line_end, lineno,
500228060Sbapt                                           "lookup-function-name", &arg))
501228060Sbapt                  option.set_function_name (arg);
502228060Sbapt                else
503228060Sbapt
504228060Sbapt                if (is_define_declaration (line, line_end, lineno,
505228060Sbapt                                           "class-name", &arg))
506228060Sbapt                  option.set_class_name (arg);
507228060Sbapt                else
508228060Sbapt
509228060Sbapt                if (is_declaration (line, line_end, lineno, "7bit"))
510228060Sbapt                  option.set (SEVENBIT);
511228060Sbapt                else
512228060Sbapt
513228060Sbapt                if (is_declaration (line, line_end, lineno, "compare-lengths"))
514228060Sbapt                  option.set (LENTABLE);
515228060Sbapt                else
516228060Sbapt
517228060Sbapt                if (is_declaration (line, line_end, lineno, "compare-strncmp"))
518228060Sbapt                  option.set (COMP);
519228060Sbapt                else
520228060Sbapt
521228060Sbapt                if (is_declaration (line, line_end, lineno, "readonly-tables"))
522228060Sbapt                  option.set (CONST);
523228060Sbapt                else
524228060Sbapt
525228060Sbapt                if (is_declaration (line, line_end, lineno, "enum"))
526228060Sbapt                  option.set (ENUM);
527228060Sbapt                else
528228060Sbapt
529228060Sbapt                if (is_declaration (line, line_end, lineno, "includes"))
530228060Sbapt                  option.set (INCLUDE);
531228060Sbapt                else
532228060Sbapt
533228060Sbapt                if (is_declaration (line, line_end, lineno, "global-table"))
534228060Sbapt                  option.set (GLOBAL);
535228060Sbapt                else
536228060Sbapt
537228060Sbapt                if (is_declaration (line, line_end, lineno, "pic"))
538228060Sbapt                  option.set (SHAREDLIB);
539228060Sbapt                else
540228060Sbapt
541228060Sbapt                if (is_define_declaration (line, line_end, lineno,
542228060Sbapt                                           "string-pool-name", &arg))
543228060Sbapt                  option.set_stringpool_name (arg);
544228060Sbapt                else
545228060Sbapt
546228060Sbapt                if (is_declaration (line, line_end, lineno, "null-strings"))
547228060Sbapt                  option.set (NULLSTRINGS);
548228060Sbapt                else
549228060Sbapt
550228060Sbapt                if (is_define_declaration (line, line_end, lineno,
551228060Sbapt                                           "word-array-name", &arg))
552228060Sbapt                  option.set_wordlist_name (arg);
553228060Sbapt                else
554228060Sbapt
555228060Sbapt                if (is_define_declaration (line, line_end, lineno,
556228060Sbapt                                           "length-table-name", &arg))
557228060Sbapt                  option.set_lengthtable_name (arg);
558228060Sbapt                else
559228060Sbapt
560228060Sbapt                if (is_declaration_with_arg (line, line_end, lineno,
561228060Sbapt                                             "switch", &arg))
562228060Sbapt                  {
563228060Sbapt                    option.set_total_switches (atoi (arg));
564228060Sbapt                    if (option.get_total_switches () <= 0)
565228060Sbapt                      {
566228060Sbapt                        fprintf (stderr, "%s:%u: number of switches %s"
567228060Sbapt                                 " must be a positive number\n",
568228060Sbapt                                 pretty_input_file_name (), lineno, arg);
569228060Sbapt                        exit (1);
570228060Sbapt                      }
571228060Sbapt                  }
572228060Sbapt                else
573228060Sbapt
574228060Sbapt                if (is_declaration (line, line_end, lineno, "omit-struct-type"))
575228060Sbapt                  option.set (NOTYPE);
576228060Sbapt                else
577228060Sbapt
578228060Sbapt                  {
579228060Sbapt                    fprintf (stderr, "%s:%u: unrecognized %% directive\n",
580228060Sbapt                             pretty_input_file_name (), lineno);
581228060Sbapt                    exit (1);
582228060Sbapt                  }
583228060Sbapt              }
584228060Sbapt          }
585228060Sbapt        else if (!(_verbatim_declarations != NULL
586228060Sbapt                   && _verbatim_declarations_end == NULL))
587228060Sbapt          {
588228060Sbapt            /* Append the line to struct_decl.  */
589228060Sbapt            size_t old_len = (struct_decl ? strlen (struct_decl) : 0);
590228060Sbapt            size_t line_len = line_end - line;
591228060Sbapt            size_t new_len = old_len + line_len + 1;
592228060Sbapt            char *new_struct_decl = new char[new_len];
593228060Sbapt            if (old_len > 0)
594228060Sbapt              memcpy (new_struct_decl, struct_decl, old_len);
595228060Sbapt            memcpy (new_struct_decl + old_len, line, line_len);
596228060Sbapt            new_struct_decl[old_len + line_len] = '\0';
597228060Sbapt            if (struct_decl)
598228060Sbapt              delete[] struct_decl;
599228060Sbapt            struct_decl = new_struct_decl;
600228060Sbapt            /* Append the lineno to struct_decl_linenos.  */
601228060Sbapt            unsigned int *new_struct_decl_linenos =
602228060Sbapt              new unsigned int[struct_decl_linecount + 1];
603228060Sbapt            if (struct_decl_linecount > 0)
604228060Sbapt              memcpy (new_struct_decl_linenos, struct_decl_linenos,
605228060Sbapt                      struct_decl_linecount * sizeof (unsigned int));
606228060Sbapt            new_struct_decl_linenos[struct_decl_linecount] = lineno;
607228060Sbapt            if (struct_decl_linenos)
608228060Sbapt              delete[] struct_decl_linenos;
609228060Sbapt            struct_decl_linenos = new_struct_decl_linenos;
610228060Sbapt            /* Increment struct_decl_linecount.  */
611228060Sbapt            struct_decl_linecount++;
612228060Sbapt          }
613228060Sbapt        lineno++;
614228060Sbapt        line = line_end;
615228060Sbapt      }
616228060Sbapt    if (_verbatim_declarations != NULL && _verbatim_declarations_end == NULL)
617228060Sbapt      {
618228060Sbapt        fprintf (stderr, "%s:%u: unterminated %%{ section\n",
619228060Sbapt                 pretty_input_file_name (), _verbatim_declarations_lineno);
620228060Sbapt        exit (1);
621228060Sbapt      }
622228060Sbapt
623228060Sbapt    /* Determine _struct_decl, _return_type, _struct_tag.  */
624228060Sbapt    if (option[TYPE])
625228060Sbapt      {
626228060Sbapt        if (struct_decl)
627228060Sbapt          {
628228060Sbapt            /* Drop leading whitespace and comments.  */
629228060Sbapt            {
630228060Sbapt              char *p = struct_decl;
631228060Sbapt              unsigned int *l = struct_decl_linenos;
632228060Sbapt              for (;;)
633228060Sbapt                {
634228060Sbapt                  if (p[0] == ' ' || p[0] == '\t')
635228060Sbapt                    {
636228060Sbapt                      p++;
637228060Sbapt                      continue;
638228060Sbapt                    }
639228060Sbapt                  if (p[0] == '\n')
640228060Sbapt                    {
641228060Sbapt                      l++;
642228060Sbapt                      p++;
643228060Sbapt                      continue;
644228060Sbapt                    }
645228060Sbapt                  if (p[0] == '/')
646228060Sbapt                    {
647228060Sbapt                      if (p[1] == '*')
648228060Sbapt                        {
649228060Sbapt                          /* Skip over ANSI C style comment.  */
650228060Sbapt                          p += 2;
651228060Sbapt                          while (p[0] != '\0')
652228060Sbapt                            {
653228060Sbapt                              if (p[0] == '*' && p[1] == '/')
654228060Sbapt                                {
655228060Sbapt                                  p += 2;
656228060Sbapt                                  break;
657228060Sbapt                                }
658228060Sbapt                              if (p[0] == '\n')
659228060Sbapt                                l++;
660228060Sbapt                              p++;
661228060Sbapt                            }
662228060Sbapt                          continue;
663228060Sbapt                        }
664228060Sbapt                      if (p[1] == '/')
665228060Sbapt                        {
666228060Sbapt                          /* Skip over ISO C99 or C++ style comment.  */
667228060Sbapt                          p += 2;
668228060Sbapt                          while (p[0] != '\0' && p[0] != '\n')
669228060Sbapt                            p++;
670228060Sbapt                          if (p[0] == '\n')
671228060Sbapt                            {
672228060Sbapt                              l++;
673228060Sbapt                              p++;
674228060Sbapt                            }
675228060Sbapt                          continue;
676228060Sbapt                        }
677228060Sbapt                    }
678228060Sbapt                  break;
679228060Sbapt                }
680228060Sbapt              if (p != struct_decl)
681228060Sbapt                {
682228060Sbapt                  size_t len = strlen (p);
683228060Sbapt                  char *new_struct_decl = new char[len + 1];
684228060Sbapt                  memcpy (new_struct_decl, p, len + 1);
685228060Sbapt                  delete[] struct_decl;
686228060Sbapt                  struct_decl = new_struct_decl;
687228060Sbapt                }
688228060Sbapt              _struct_decl_lineno = *l;
689228060Sbapt            }
690228060Sbapt            /* Drop trailing whitespace.  */
691228060Sbapt            for (char *p = struct_decl + strlen (struct_decl); p > struct_decl;)
692228060Sbapt              if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t')
693228060Sbapt                *--p = '\0';
694228060Sbapt              else
695228060Sbapt                break;
696228060Sbapt          }
697228060Sbapt        if (struct_decl == NULL || struct_decl[0] == '\0')
698228060Sbapt          {
699228060Sbapt            fprintf (stderr, "%s: missing struct declaration"
700228060Sbapt                     " for option --struct-type\n",
701228060Sbapt                     pretty_input_file_name ());
702228060Sbapt            exit (1);
703228060Sbapt          }
704228060Sbapt        {
705228060Sbapt          /* Ensure trailing semicolon.  */
706228060Sbapt          size_t old_len = strlen (struct_decl);
707228060Sbapt          if (struct_decl[old_len - 1] != ';')
708228060Sbapt            {
709228060Sbapt              char *new_struct_decl = new char[old_len + 2];
710228060Sbapt              memcpy (new_struct_decl, struct_decl, old_len);
711228060Sbapt              new_struct_decl[old_len] = ';';
712228060Sbapt              new_struct_decl[old_len + 1] = '\0';
713228060Sbapt              delete[] struct_decl;
714228060Sbapt              struct_decl = new_struct_decl;
715228060Sbapt            }
716228060Sbapt        }
717228060Sbapt        /* Set _struct_decl to the entire declaration.  */
718228060Sbapt        _struct_decl = struct_decl;
719228060Sbapt        /* Set _struct_tag to the naked "struct something".  */
720228060Sbapt        const char *p;
721228060Sbapt        for (p = struct_decl; *p && *p != '{' && *p != ';' && *p != '\n'; p++)
722228060Sbapt          ;
723228060Sbapt        for (; p > struct_decl;)
724228060Sbapt          if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t')
725228060Sbapt            --p;
726228060Sbapt          else
727228060Sbapt            break;
728228060Sbapt        size_t struct_tag_length = p - struct_decl;
729228060Sbapt        char *struct_tag = new char[struct_tag_length + 1];
730228060Sbapt        memcpy (struct_tag, struct_decl, struct_tag_length);
731228060Sbapt        struct_tag[struct_tag_length] = '\0';
732228060Sbapt        _struct_tag = struct_tag;
733228060Sbapt        /* The return type of the lookup function is "struct something *".
734228060Sbapt           No "const" here, because if !option[CONST], some user code might
735228060Sbapt           want to modify the structure. */
736228060Sbapt        char *return_type = new char[struct_tag_length + 3];
737228060Sbapt        memcpy (return_type, struct_decl, struct_tag_length);
738228060Sbapt        return_type[struct_tag_length] = ' ';
739228060Sbapt        return_type[struct_tag_length + 1] = '*';
740228060Sbapt        return_type[struct_tag_length + 2] = '\0';
741228060Sbapt        _return_type = return_type;
742228060Sbapt      }
743228060Sbapt
744228060Sbapt    if (struct_decl_linenos)
745228060Sbapt      delete[] struct_decl_linenos;
746228060Sbapt  }
747228060Sbapt
748228060Sbapt  /* Parse the keywords section.  */
749228060Sbapt  {
750228060Sbapt    Keyword_List **list_tail = &_head;
751228060Sbapt    const char *delimiters = option.get_delimiters ();
752228060Sbapt    unsigned int lineno = keywords_lineno;
753228060Sbapt    bool charset_dependent = false;
754228060Sbapt    for (const char *line = keywords; line < keywords_end; )
755228060Sbapt      {
756228060Sbapt        const char *line_end;
757228060Sbapt        line_end = (const char *) memchr (line, '\n', keywords_end - line);
758228060Sbapt        if (line_end != NULL)
759228060Sbapt          line_end++;
760228060Sbapt        else
761228060Sbapt          line_end = keywords_end;
762228060Sbapt
763228060Sbapt        if (line[0] == '#')
764228060Sbapt          ; /* Comment line.  */
765228060Sbapt        else if (line[0] == '%')
766228060Sbapt          {
767228060Sbapt            fprintf (stderr, "%s:%u:"
768228060Sbapt                     " declarations are not allowed in the keywords section.\n"
769228060Sbapt                     "To declare a keyword starting with %%, enclose it in"
770228060Sbapt                     " double-quotes.\n",
771228060Sbapt                     pretty_input_file_name (), lineno);
772228060Sbapt            exit (1);
773228060Sbapt          }
774228060Sbapt        else
775228060Sbapt          {
776228060Sbapt            /* An input line carrying a keyword.  */
777228060Sbapt            const char *keyword;
778228060Sbapt            size_t keyword_length;
779228060Sbapt            const char *rest;
780228060Sbapt
781228060Sbapt            if (line[0] == '"')
782228060Sbapt              {
783228060Sbapt                /* Parse a string in ANSI C syntax.  */
784228060Sbapt                char *kp = new char[line_end-line];
785228060Sbapt                keyword = kp;
786228060Sbapt                const char *lp = line + 1;
787228060Sbapt
788228060Sbapt                for (;;)
789228060Sbapt                  {
790228060Sbapt                    if (lp == line_end)
791228060Sbapt                      {
792228060Sbapt                        fprintf (stderr, "%s:%u: unterminated string\n",
793228060Sbapt                                 pretty_input_file_name (), lineno);
794228060Sbapt                        exit (1);
795228060Sbapt                      }
796228060Sbapt
797228060Sbapt                    char c = *lp;
798228060Sbapt                    if (c == '\\')
799228060Sbapt                      {
800228060Sbapt                        c = *++lp;
801228060Sbapt                        switch (c)
802228060Sbapt                          {
803228060Sbapt                          case '0': case '1': case '2': case '3':
804228060Sbapt                          case '4': case '5': case '6': case '7':
805228060Sbapt                            {
806228060Sbapt                              int code = 0;
807228060Sbapt                              int count = 0;
808228060Sbapt                              while (count < 3 && *lp >= '0' && *lp <= '7')
809228060Sbapt                                {
810228060Sbapt                                  code = (code << 3) + (*lp - '0');
811228060Sbapt                                  lp++;
812228060Sbapt                                  count++;
813228060Sbapt                                }
814228060Sbapt                              if (code > UCHAR_MAX)
815228060Sbapt                                fprintf (stderr,
816228060Sbapt                                         "%s:%u: octal escape out of range\n",
817228060Sbapt                                         pretty_input_file_name (), lineno);
818228060Sbapt                              *kp = static_cast<char>(code);
819228060Sbapt                              break;
820228060Sbapt                            }
821228060Sbapt                          case 'x':
822228060Sbapt                            {
823228060Sbapt                              int code = 0;
824228060Sbapt                              int count = 0;
825228060Sbapt                              lp++;
826228060Sbapt                              while ((*lp >= '0' && *lp <= '9')
827228060Sbapt                                     || (*lp >= 'A' && *lp <= 'F')
828228060Sbapt                                     || (*lp >= 'a' && *lp <= 'f'))
829228060Sbapt                                {
830228060Sbapt                                  code = (code << 4)
831228060Sbapt                                         + (*lp >= 'A' && *lp <= 'F'
832228060Sbapt                                            ? *lp - 'A' + 10 :
833228060Sbapt                                            *lp >= 'a' && *lp <= 'f'
834228060Sbapt                                            ? *lp - 'a' + 10 :
835228060Sbapt                                            *lp - '0');
836228060Sbapt                                  lp++;
837228060Sbapt                                  count++;
838228060Sbapt                                }
839228060Sbapt                              if (count == 0)
840228060Sbapt                                fprintf (stderr, "%s:%u: hexadecimal escape"
841228060Sbapt                                         " without any hex digits\n",
842228060Sbapt                                         pretty_input_file_name (), lineno);
843228060Sbapt                              if (code > UCHAR_MAX)
844228060Sbapt                                fprintf (stderr, "%s:%u: hexadecimal escape"
845228060Sbapt                                         " out of range\n",
846228060Sbapt                                         pretty_input_file_name (), lineno);
847228060Sbapt                              *kp = static_cast<char>(code);
848228060Sbapt                              break;
849228060Sbapt                            }
850228060Sbapt                          case '\\': case '\'': case '"':
851228060Sbapt                            *kp = c;
852228060Sbapt                            lp++;
853228060Sbapt                            charset_dependent = true;
854228060Sbapt                            break;
855228060Sbapt                          case 'n':
856228060Sbapt                            *kp = '\n';
857228060Sbapt                            lp++;
858228060Sbapt                            charset_dependent = true;
859228060Sbapt                            break;
860228060Sbapt                          case 't':
861228060Sbapt                            *kp = '\t';
862228060Sbapt                            lp++;
863228060Sbapt                            charset_dependent = true;
864228060Sbapt                            break;
865228060Sbapt                          case 'r':
866228060Sbapt                            *kp = '\r';
867228060Sbapt                            lp++;
868228060Sbapt                            charset_dependent = true;
869228060Sbapt                            break;
870228060Sbapt                          case 'f':
871228060Sbapt                            *kp = '\f';
872228060Sbapt                            lp++;
873228060Sbapt                            charset_dependent = true;
874228060Sbapt                            break;
875228060Sbapt                          case 'b':
876228060Sbapt                            *kp = '\b';
877228060Sbapt                            lp++;
878228060Sbapt                            charset_dependent = true;
879228060Sbapt                            break;
880228060Sbapt                          case 'a':
881228060Sbapt                            *kp = '\a';
882228060Sbapt                            lp++;
883228060Sbapt                            charset_dependent = true;
884228060Sbapt                            break;
885228060Sbapt                          case 'v':
886228060Sbapt                            *kp = '\v';
887228060Sbapt                            lp++;
888228060Sbapt                            charset_dependent = true;
889228060Sbapt                            break;
890228060Sbapt                          default:
891228060Sbapt                            fprintf (stderr, "%s:%u: invalid escape sequence"
892228060Sbapt                                     " in string\n",
893228060Sbapt                                     pretty_input_file_name (), lineno);
894228060Sbapt                            exit (1);
895228060Sbapt                          }
896228060Sbapt                      }
897228060Sbapt                    else if (c == '"')
898228060Sbapt                      break;
899228060Sbapt                    else
900228060Sbapt                      {
901228060Sbapt                        *kp = c;
902228060Sbapt                        lp++;
903228060Sbapt                        charset_dependent = true;
904228060Sbapt                      }
905228060Sbapt                    kp++;
906228060Sbapt                  }
907228060Sbapt                lp++;
908228060Sbapt                if (lp < line_end && *lp != '\n')
909228060Sbapt                  {
910228060Sbapt                    if (strchr (delimiters, *lp) == NULL)
911228060Sbapt                      {
912228060Sbapt                        fprintf (stderr, "%s:%u: string not followed"
913228060Sbapt                                 " by delimiter\n",
914228060Sbapt                                 pretty_input_file_name (), lineno);
915228060Sbapt                        exit (1);
916228060Sbapt                      }
917228060Sbapt                    lp++;
918228060Sbapt                  }
919228060Sbapt                keyword_length = kp - keyword;
920228060Sbapt                if (option[TYPE])
921228060Sbapt                  {
922228060Sbapt                    char *line_rest = new char[line_end - lp + 1];
923228060Sbapt                    memcpy (line_rest, lp, line_end - lp);
924228060Sbapt                    line_rest[line_end - lp -
925228060Sbapt                              (line_end > lp && line_end[-1] == '\n' ? 1 : 0)]
926228060Sbapt                      = '\0';
927228060Sbapt                    rest = line_rest;
928228060Sbapt                  }
929228060Sbapt                else
930228060Sbapt                  rest = empty_string;
931228060Sbapt              }
932228060Sbapt            else
933228060Sbapt              {
934228060Sbapt                /* Not a string.  Look for the delimiter.  */
935228060Sbapt                const char *lp = line;
936228060Sbapt                for (;;)
937228060Sbapt                  {
938228060Sbapt                    if (!(lp < line_end && *lp != '\n'))
939228060Sbapt                      {
940228060Sbapt                        keyword = line;
941228060Sbapt                        keyword_length = lp - line;
942228060Sbapt                        rest = empty_string;
943228060Sbapt                        break;
944228060Sbapt                      }
945228060Sbapt                    if (strchr (delimiters, *lp) != NULL)
946228060Sbapt                      {
947228060Sbapt                        keyword = line;
948228060Sbapt                        keyword_length = lp - line;
949228060Sbapt                        lp++;
950228060Sbapt                        if (option[TYPE])
951228060Sbapt                          {
952228060Sbapt                            char *line_rest = new char[line_end - lp + 1];
953228060Sbapt                            memcpy (line_rest, lp, line_end - lp);
954228060Sbapt                            line_rest[line_end - lp -
955228060Sbapt                                      (line_end > lp && line_end[-1] == '\n'
956228060Sbapt                                       ? 1 : 0)]
957228060Sbapt                              = '\0';
958228060Sbapt                            rest = line_rest;
959228060Sbapt                          }
960228060Sbapt                        else
961228060Sbapt                          rest = empty_string;
962228060Sbapt                        break;
963228060Sbapt                      }
964228060Sbapt                    lp++;
965228060Sbapt                  }
966228060Sbapt                if (keyword_length > 0)
967228060Sbapt                  charset_dependent = true;
968228060Sbapt              }
969228060Sbapt
970228060Sbapt            /* Allocate Keyword and add it to the list.  */
971228060Sbapt            Keyword *new_kw = _factory->create_keyword (keyword, keyword_length,
972228060Sbapt                                                        rest);
973228060Sbapt            new_kw->_lineno = lineno;
974228060Sbapt            *list_tail = new Keyword_List (new_kw);
975228060Sbapt            list_tail = &(*list_tail)->rest();
976228060Sbapt          }
977228060Sbapt
978228060Sbapt        lineno++;
979228060Sbapt        line = line_end;
980228060Sbapt      }
981228060Sbapt    *list_tail = NULL;
982228060Sbapt
983228060Sbapt    if (_head == NULL)
984228060Sbapt      {
985228060Sbapt        fprintf (stderr, "%s: No keywords in input file!\n",
986228060Sbapt                 pretty_input_file_name ());
987228060Sbapt        exit (1);
988228060Sbapt      }
989228060Sbapt
990228060Sbapt    _charset_dependent = charset_dependent;
991228060Sbapt  }
992228060Sbapt
993228060Sbapt  /* To be freed in the destructor.  */
994228060Sbapt  _input = input;
995228060Sbapt  _input_end = input_end;
996228060Sbapt}
997228060Sbapt
998228060SbaptInput::~Input ()
999228060Sbapt{
1000228060Sbapt  /* Free allocated memory.  */
1001228060Sbapt  delete[] const_cast<char*>(_return_type);
1002228060Sbapt  delete[] const_cast<char*>(_struct_tag);
1003228060Sbapt  delete[] const_cast<char*>(_struct_decl);
1004228060Sbapt  delete[] _input;
1005228060Sbapt}
1006