+
+/*------------------------------------------------------------------.
+| Convert universal character name UCN to a single-byte character, |
+| and return that character. Return -1 if UCN does not correspond |
+| to a single-byte character. |
+`------------------------------------------------------------------*/
+
+static int
+convert_ucn_to_byte (char const *ucn)
+{
+ unsigned long code = strtoul (ucn + 2, 0, 16);
+
+ /* FIXME: Currently we assume Unicode-compatible unibyte characters
+ on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes). On
+ non-ASCII hosts we support only the portable C character set.
+ These limitations should be removed once we add support for
+ multibyte characters. */
+
+ if (UCHAR_MAX < code)
+ return -1;
+
+#if ! ('$' == 0x24 && '@' == 0x40 && '`' == 0x60 && '~' == 0x7e)
+ {
+ /* A non-ASCII host. Use CODE to index into a table of the C
+ basic execution character set, which is guaranteed to exist on
+ all Standard C platforms. This table also includes '$', '@',
+ and '`', which are not in the basic execution character set but
+ which are unibyte characters on all the platforms that we know
+ about. */
+ static signed char const table[] =
+ {
+ '\0', -1, -1, -1, -1, -1, -1, '\a',
+ '\b', '\t', '\n', '\v', '\f', '\r', -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ ' ', '!', '"', '#', '$', '%', '&', '\'',
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', '{', '|', '}', '~'
+ };
+
+ code = code < sizeof table ? table[code] : -1;
+ }
+#endif
+
+ return code;
+}
+
+
+/*----------------------------------------------------------------.
+| Handle `#line INT "FILE"'. ARGS has already skipped `#line '. |
+`----------------------------------------------------------------*/
+
+static void
+handle_syncline (char *args, location_t *location)
+{
+ int lineno = strtol (args, &args, 10);
+ const char *file = NULL;
+ file = strchr (args, '"') + 1;
+ *strchr (file, '"') = 0;
+ /* FIXME: Leaking... Can't free, as some locations are still
+ pointing to the old file name. */
+ infile = xstrdup (file);
+ location->file = infile;
+ location->last_line = lineno;
+}
+
+
+/*-------------------------------------------------------------.
+| Report an unexpected end of file at LOC. An end of file was |
+| encountered and the expected TOKEN_END was missing. After |
+| reporting the problem, pretend that TOKEN_END was found. |
+`-------------------------------------------------------------*/
+
+static void
+unexpected_end_of_file (location_t *loc, char const *token_end)
+{
+ size_t i = strlen (token_end);
+
+ complain_at (*loc, _("missing `%s' at end of file"), token_end);
+
+ /* Adjust location's last column so that any later message does not
+ mention the characters just inserted. */
+ loc->last_column -= i;
+
+ while (i != 0)
+ unput (token_end[--i]);
+}
+
+
+/*-------------------------.
+| Initialize the scanner. |
+`-------------------------*/
+