]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/localedef/scanner.c
bluetooth: Fix a mandoc related issues
[FreeBSD/FreeBSD.git] / usr.bin / localedef / scanner.c
1 /*-
2  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
3  * Copyright 2015 John Marino <draco@marino.st>
4  *
5  * This source code is derived from the illumos localedef command, and
6  * provided under BSD-style license terms by Nexenta Systems, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /*
32  * This file contains the "scanner", which tokenizes the input files
33  * for localedef for processing by the higher level grammar processor.
34  */
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <limits.h>
42 #include <string.h>
43 #include <wchar.h>
44 #include <sys/types.h>
45 #include <assert.h>
46 #include "localedef.h"
47 #include "parser.h"
48
49 int                     com_char = '#';
50 int                     esc_char = '\\';
51 int                     mb_cur_min = 1;
52 int                     mb_cur_max = 1;
53 int                     lineno = 1;
54 int                     warnings = 0;
55 int                     is_stdin = 1;
56 FILE                    *input;
57 static int              nextline;
58 //static FILE           *input = stdin;
59 static const char       *filename = "<stdin>";
60 static int              instring = 0;
61 static int              escaped = 0;
62
63 /*
64  * Token space ... grows on demand.
65  */
66 static char *token = NULL;
67 static int tokidx;
68 static int toksz = 0;
69 static int hadtok = 0;
70
71 /*
72  * Wide string space ... grows on demand.
73  */
74 static wchar_t *widestr = NULL;
75 static int wideidx = 0;
76 static int widesz = 0;
77
78 /*
79  * The last keyword seen.  This is useful to trigger the special lexer rules
80  * for "copy" and also collating symbols and elements.
81  */
82 int     last_kw = 0;
83 static int      category = T_END;
84
85 static struct token {
86         int id;
87         const char *name;
88 } keywords[] = {
89         { T_COM_CHAR,           "comment_char" },
90         { T_ESC_CHAR,           "escape_char" },
91         { T_END,                "END" },
92         { T_COPY,               "copy" },
93         { T_MESSAGES,           "LC_MESSAGES" },
94         { T_YESSTR,             "yesstr" },
95         { T_YESEXPR,            "yesexpr" },
96         { T_NOSTR,              "nostr" },
97         { T_NOEXPR,             "noexpr" },
98         { T_MONETARY,           "LC_MONETARY" },
99         { T_INT_CURR_SYMBOL,    "int_curr_symbol" },
100         { T_CURRENCY_SYMBOL,    "currency_symbol" },
101         { T_MON_DECIMAL_POINT,  "mon_decimal_point" },
102         { T_MON_THOUSANDS_SEP,  "mon_thousands_sep" },
103         { T_POSITIVE_SIGN,      "positive_sign" },
104         { T_NEGATIVE_SIGN,      "negative_sign" },
105         { T_MON_GROUPING,       "mon_grouping" },
106         { T_INT_FRAC_DIGITS,    "int_frac_digits" },
107         { T_FRAC_DIGITS,        "frac_digits" },
108         { T_P_CS_PRECEDES,      "p_cs_precedes" },
109         { T_P_SEP_BY_SPACE,     "p_sep_by_space" },
110         { T_N_CS_PRECEDES,      "n_cs_precedes" },
111         { T_N_SEP_BY_SPACE,     "n_sep_by_space" },
112         { T_P_SIGN_POSN,        "p_sign_posn" },
113         { T_N_SIGN_POSN,        "n_sign_posn" },
114         { T_INT_P_CS_PRECEDES,  "int_p_cs_precedes" },
115         { T_INT_N_CS_PRECEDES,  "int_n_cs_precedes" },
116         { T_INT_P_SEP_BY_SPACE, "int_p_sep_by_space" },
117         { T_INT_N_SEP_BY_SPACE, "int_n_sep_by_space" },
118         { T_INT_P_SIGN_POSN,    "int_p_sign_posn" },
119         { T_INT_N_SIGN_POSN,    "int_n_sign_posn" },
120         { T_COLLATE,            "LC_COLLATE" },
121         { T_COLLATING_SYMBOL,   "collating-symbol" },
122         { T_COLLATING_ELEMENT,  "collating-element" },
123         { T_FROM,               "from" },
124         { T_ORDER_START,        "order_start" },
125         { T_ORDER_END,          "order_end" },
126         { T_FORWARD,            "forward" },
127         { T_BACKWARD,           "backward" },
128         { T_POSITION,           "position" },
129         { T_IGNORE,             "IGNORE" },
130         { T_UNDEFINED,          "UNDEFINED" },
131         { T_NUMERIC,            "LC_NUMERIC" },
132         { T_DECIMAL_POINT,      "decimal_point" },
133         { T_THOUSANDS_SEP,      "thousands_sep" },
134         { T_GROUPING,           "grouping" },
135         { T_TIME,               "LC_TIME" },
136         { T_ABDAY,              "abday" },
137         { T_DAY,                "day" },
138         { T_ABMON,              "abmon" },
139         { T_MON,                "mon" },
140         { T_D_T_FMT,            "d_t_fmt" },
141         { T_D_FMT,              "d_fmt" },
142         { T_T_FMT,              "t_fmt" },
143         { T_AM_PM,              "am_pm" },
144         { T_T_FMT_AMPM,         "t_fmt_ampm" },
145         { T_ERA,                "era" },
146         { T_ERA_D_FMT,          "era_d_fmt" },
147         { T_ERA_T_FMT,          "era_t_fmt" },
148         { T_ERA_D_T_FMT,        "era_d_t_fmt" },
149         { T_ALT_DIGITS,         "alt_digits" },
150         { T_CTYPE,              "LC_CTYPE" },
151         { T_ISUPPER,            "upper" },
152         { T_ISLOWER,            "lower" },
153         { T_ISALPHA,            "alpha" },
154         { T_ISDIGIT,            "digit" },
155         { T_ISPUNCT,            "punct" },
156         { T_ISXDIGIT,           "xdigit" },
157         { T_ISSPACE,            "space" },
158         { T_ISPRINT,            "print" },
159         { T_ISGRAPH,            "graph" },
160         { T_ISBLANK,            "blank" },
161         { T_ISCNTRL,            "cntrl" },
162         /*
163          * These entries are local additions, and not specified by
164          * TOG.  Note that they are not guaranteed to be accurate for
165          * all locales, and so applications should not depend on them.
166          */
167         { T_ISSPECIAL,          "special" },
168         { T_ISENGLISH,          "english" },
169         { T_ISPHONOGRAM,        "phonogram" },
170         { T_ISIDEOGRAM,         "ideogram" },
171         { T_ISNUMBER,           "number" },
172         /*
173          * We have to support this in the grammar, but it would be a
174          * syntax error to define a character as one of these without
175          * also defining it as an alpha or digit.  We ignore it in our
176          * parsing.
177          */
178         { T_ISALNUM,            "alnum" },
179         { T_TOUPPER,            "toupper" },
180         { T_TOLOWER,            "tolower" },
181
182         /*
183          * These are keywords used in the charmap file.  Note that
184          * Solaris originally used angle brackets to wrap some of them,
185          * but we removed that to simplify our parser.  The first of these
186          * items are "global items."
187          */
188         { T_CHARMAP,            "CHARMAP" },
189         { T_WIDTH,              "WIDTH" },
190
191         { -1, NULL },
192 };
193
194 /*
195  * These special words are only used in a charmap file, enclosed in <>.
196  */
197 static struct token symwords[] = {
198         { T_COM_CHAR,           "comment_char" },
199         { T_ESC_CHAR,           "escape_char" },
200         { T_CODE_SET,           "code_set_name" },
201         { T_MB_CUR_MAX,         "mb_cur_max" },
202         { T_MB_CUR_MIN,         "mb_cur_min" },
203         { -1, NULL },
204 };
205
206 static int categories[] = {
207         T_CHARMAP,
208         T_CTYPE,
209         T_COLLATE,
210         T_MESSAGES,
211         T_MONETARY,
212         T_NUMERIC,
213         T_TIME,
214         T_WIDTH,
215         0
216 };
217
218 void
219 reset_scanner(const char *fname)
220 {
221         if (fname == NULL) {
222                 filename = "<stdin>";
223                 is_stdin = 1;
224         } else {
225                 if (!is_stdin)
226                         (void) fclose(input);
227                 if ((input = fopen(fname, "r")) == NULL) {
228                         perror("fopen");
229                         exit(4);
230                 } else {
231                         is_stdin = 0;
232                 }
233                 filename = fname;
234         }
235         com_char = '#';
236         esc_char = '\\';
237         instring = 0;
238         escaped = 0;
239         lineno = 1;
240         nextline = 1;
241         tokidx = 0;
242         wideidx = 0;
243 }
244
245 #define hex(x)  \
246         (isdigit(x) ? (x - '0') : ((islower(x) ? (x - 'a') : (x - 'A')) + 10))
247 #define isodigit(x)     ((x >= '0') && (x <= '7'))
248
249 static int
250 scanc(void)
251 {
252         int     c;
253
254         if (is_stdin)
255                 c = getc(stdin);
256         else
257                 c = getc(input);
258         lineno = nextline;
259         if (c == '\n') {
260                 nextline++;
261         }
262         return (c);
263 }
264
265 static void
266 unscanc(int c)
267 {
268         if (c == '\n') {
269                 nextline--;
270         }
271         if (ungetc(c, is_stdin ? stdin : input) < 0) {
272                 yyerror("ungetc failed");
273         }
274 }
275
276 static int
277 scan_hex_byte(void)
278 {
279         int     c1, c2;
280         int     v;
281
282         c1 = scanc();
283         if (!isxdigit(c1)) {
284                 yyerror("malformed hex digit");
285                 return (0);
286         }
287         c2 = scanc();
288         if (!isxdigit(c2)) {
289                 yyerror("malformed hex digit");
290                 return (0);
291         }
292         v = ((hex(c1) << 4) | hex(c2));
293         return (v);
294 }
295
296 static int
297 scan_dec_byte(void)
298 {
299         int     c1, c2, c3;
300         int     b;
301
302         c1 = scanc();
303         if (!isdigit(c1)) {
304                 yyerror("malformed decimal digit");
305                 return (0);
306         }
307         b = c1 - '0';
308         c2 = scanc();
309         if (!isdigit(c2)) {
310                 yyerror("malformed decimal digit");
311                 return (0);
312         }
313         b *= 10;
314         b += (c2 - '0');
315         c3 = scanc();
316         if (!isdigit(c3)) {
317                 unscanc(c3);
318         } else {
319                 b *= 10;
320                 b += (c3 - '0');
321         }
322         return (b);
323 }
324
325 static int
326 scan_oct_byte(void)
327 {
328         int c1, c2, c3;
329         int     b;
330
331         b = 0;
332
333         c1 = scanc();
334         if (!isodigit(c1)) {
335                 yyerror("malformed octal digit");
336                 return (0);
337         }
338         b = c1 - '0';
339         c2 = scanc();
340         if (!isodigit(c2)) {
341                 yyerror("malformed octal digit");
342                 return (0);
343         }
344         b *= 8;
345         b += (c2 - '0');
346         c3 = scanc();
347         if (!isodigit(c3)) {
348                 unscanc(c3);
349         } else {
350                 b *= 8;
351                 b += (c3 - '0');
352         }
353         return (b);
354 }
355
356 void
357 add_tok(int c)
358 {
359         if ((tokidx + 1) >= toksz) {
360                 toksz += 64;
361                 if ((token = realloc(token, toksz)) == NULL) {
362                         yyerror("out of memory");
363                         tokidx = 0;
364                         toksz = 0;
365                         return;
366                 }
367         }
368
369         token[tokidx++] = (char)c;
370         token[tokidx] = 0;
371 }
372 void
373 add_wcs(wchar_t c)
374 {
375         if ((wideidx + 1) >= widesz) {
376                 widesz += 64;
377                 widestr = realloc(widestr, (widesz * sizeof (wchar_t)));
378                 if (widestr == NULL) {
379                         yyerror("out of memory");
380                         wideidx = 0;
381                         widesz = 0;
382                         return;
383                 }
384         }
385
386         widestr[wideidx++] = c;
387         widestr[wideidx] = 0;
388 }
389
390 wchar_t *
391 get_wcs(void)
392 {
393         wchar_t *ws = widestr;
394         wideidx = 0;
395         widestr = NULL;
396         widesz = 0;
397         if (ws == NULL) {
398                 if ((ws = wcsdup(L"")) == NULL) {
399                         yyerror("out of memory");
400                 }
401         }
402         return (ws);
403 }
404
405 static int
406 get_byte(void)
407 {
408         int     c;
409
410         if ((c = scanc()) != esc_char) {
411                 unscanc(c);
412                 return (EOF);
413         }
414         c = scanc();
415
416         switch (c) {
417         case 'd':
418         case 'D':
419                 return (scan_dec_byte());
420         case 'x':
421         case 'X':
422                 return (scan_hex_byte());
423         case '0':
424         case '1':
425         case '2':
426         case '3':
427         case '4':
428         case '5':
429         case '6':
430         case '7':
431                 /* put the character back so we can get it */
432                 unscanc(c);
433                 return (scan_oct_byte());
434         default:
435                 unscanc(c);
436                 unscanc(esc_char);
437                 return (EOF);
438         }
439 }
440
441 int
442 get_escaped(int c)
443 {
444         switch (c) {
445         case 'n':
446                 return ('\n');
447         case 'r':
448                 return ('\r');
449         case 't':
450                 return ('\t');
451         case 'f':
452                 return ('\f');
453         case 'v':
454                 return ('\v');
455         case 'b':
456                 return ('\b');
457         case 'a':
458                 return ('\a');
459         default:
460                 return (c);
461         }
462 }
463
464 int
465 get_wide(void)
466 {
467         static char mbs[MB_LEN_MAX + 1] = "";
468         static int mbi = 0;
469         int c;
470         wchar_t wc;
471
472         if (mb_cur_max >= (int)sizeof (mbs)) {
473                 yyerror("max multibyte character size too big");
474                 mbi = 0;
475                 return (T_NULL);
476         }
477         for (;;) {
478                 if ((mbi == mb_cur_max) || ((c = get_byte()) == EOF)) {
479                         /*
480                          * end of the byte sequence reached, but no
481                          * valid wide decoding.  fatal error.
482                          */
483                         mbi = 0;
484                         yyerror("not a valid character encoding");
485                         return (T_NULL);
486                 }
487                 mbs[mbi++] = c;
488                 mbs[mbi] = 0;
489
490                 /* does it decode? */
491                 if (to_wide(&wc, mbs) >= 0) {
492                         break;
493                 }
494         }
495
496         mbi = 0;
497         if ((category != T_CHARMAP) && (category != T_WIDTH)) {
498                 if (check_charmap(wc) < 0) {
499                         yyerror("no symbolic name for character");
500                         return (T_NULL);
501                 }
502         }
503
504         yylval.wc = wc;
505         return (T_CHAR);
506 }
507
508 int
509 get_symbol(void)
510 {
511         int     c;
512
513         while ((c = scanc()) != EOF) {
514                 if (escaped) {
515                         escaped = 0;
516                         if (c == '\n')
517                                 continue;
518                         add_tok(get_escaped(c));
519                         continue;
520                 }
521                 if (c == esc_char) {
522                         escaped = 1;
523                         continue;
524                 }
525                 if (c == '\n') {        /* well that's strange! */
526                         yyerror("unterminated symbolic name");
527                         continue;
528                 }
529                 if (c == '>') {         /* end of symbol */
530
531                         /*
532                          * This restarts the token from the beginning
533                          * the next time we scan a character.  (This
534                          * token is complete.)
535                          */
536
537                         if (token == NULL) {
538                                 yyerror("missing symbolic name");
539                                 return (T_NULL);
540                         }
541                         tokidx = 0;
542
543                         /*
544                          * A few symbols are handled as keywords outside
545                          * of the normal categories.
546                          */
547                         if (category == T_END) {
548                                 int i;
549                                 for (i = 0; symwords[i].name != 0; i++) {
550                                         if (strcmp(token, symwords[i].name) ==
551                                             0) {
552                                                 last_kw = symwords[i].id;
553                                                 return (last_kw);
554                                         }
555                                 }
556                         }
557                         /*
558                          * Contextual rule: Only literal characters are
559                          * permitted in CHARMAP.  Anywhere else the symbolic
560                          * forms are fine.
561                          */
562                         if ((category != T_CHARMAP) &&
563                             (lookup_charmap(token, &yylval.wc)) != -1) {
564                                 return (T_CHAR);
565                         }
566                         if ((yylval.collsym = lookup_collsym(token)) != NULL) {
567                                 return (T_COLLSYM);
568                         }
569                         if ((yylval.collelem = lookup_collelem(token)) !=
570                             NULL) {
571                                 return (T_COLLELEM);
572                         }
573                         /* its an undefined symbol */
574                         yylval.token = strdup(token);
575                         token = NULL;
576                         toksz = 0;
577                         tokidx = 0;
578                         return (T_SYMBOL);
579                 }
580                 add_tok(c);
581         }
582
583         yyerror("unterminated symbolic name");
584         return (EOF);
585 }
586
587 int
588 get_category(void)
589 {
590         return (category);
591 }
592
593 static int
594 consume_token(void)
595 {
596         int     len = tokidx;
597         int     i;
598
599         tokidx = 0;
600         if (token == NULL)
601                 return (T_NULL);
602
603         /*
604          * this one is special, because we don't want it to alter the
605          * last_kw field.
606          */
607         if (strcmp(token, "...") == 0) {
608                 return (T_ELLIPSIS);
609         }
610
611         /* search for reserved words first */
612         for (i = 0; keywords[i].name; i++) {
613                 int j;
614                 if (strcmp(keywords[i].name, token) != 0) {
615                         continue;
616                 }
617
618                 last_kw = keywords[i].id;
619
620                 /* clear the top level category if we're done with it */
621                 if (last_kw == T_END) {
622                         category = T_END;
623                 }
624
625                 /* set the top level category if we're changing */
626                 for (j = 0; categories[j]; j++) {
627                         if (categories[j] != last_kw)
628                                 continue;
629                         category = last_kw;
630                 }
631
632                 return (keywords[i].id);
633         }
634
635         /* maybe its a numeric constant? */
636         if (isdigit(*token) || (*token == '-' && isdigit(token[1]))) {
637                 char *eptr;
638                 yylval.num = strtol(token, &eptr, 10);
639                 if (*eptr != 0)
640                         yyerror("malformed number");
641                 return (T_NUMBER);
642         }
643
644         /*
645          * A single lone character is treated as a character literal.
646          * To avoid duplication of effort, we stick in the charmap.
647          */
648         if (len == 1) {
649                 yylval.wc = token[0];
650                 return (T_CHAR);
651         }
652
653         /* anything else is treated as a symbolic name */
654         yylval.token = strdup(token);
655         token = NULL;
656         toksz = 0;
657         tokidx = 0;
658         return (T_NAME);
659 }
660
661 void
662 scan_to_eol(void)
663 {
664         int     c;
665         while ((c = scanc()) != '\n') {
666                 if (c == EOF) {
667                         /* end of file without newline! */
668                         errf("missing newline");
669                         return;
670                 }
671         }
672         assert(c == '\n');
673 }
674
675 int
676 yylex(void)
677 {
678         int             c;
679
680         while ((c = scanc()) != EOF) {
681
682                 /* special handling for quoted string */
683                 if (instring) {
684                         if (escaped) {
685                                 escaped = 0;
686
687                                 /* if newline, just eat and forget it */
688                                 if (c == '\n')
689                                         continue;
690
691                                 if (strchr("xXd01234567", c)) {
692                                         unscanc(c);
693                                         unscanc(esc_char);
694                                         return (get_wide());
695                                 }
696                                 yylval.wc = get_escaped(c);
697                                 return (T_CHAR);
698                         }
699                         if (c == esc_char) {
700                                 escaped = 1;
701                                 continue;
702                         }
703                         switch (c) {
704                         case '<':
705                                 return (get_symbol());
706                         case '>':
707                                 /* oops! should generate syntax error  */
708                                 return (T_GT);
709                         case '"':
710                                 instring = 0;
711                                 return (T_QUOTE);
712                         default:
713                                 yylval.wc = c;
714                                 return (T_CHAR);
715                         }
716                 }
717
718                 /* escaped characters first */
719                 if (escaped) {
720                         escaped = 0;
721                         if (c == '\n') {
722                                 /* eat the newline */
723                                 continue;
724                         }
725                         hadtok = 1;
726                         if (tokidx) {
727                                 /* an escape mid-token is nonsense */
728                                 return (T_NULL);
729                         }
730
731                         /* numeric escapes are treated as wide characters */
732                         if (strchr("xXd01234567", c)) {
733                                 unscanc(c);
734                                 unscanc(esc_char);
735                                 return (get_wide());
736                         }
737
738                         add_tok(get_escaped(c));
739                         continue;
740                 }
741
742                 /* if it is the escape charter itself note it */
743                 if (c == esc_char) {
744                         escaped = 1;
745                         continue;
746                 }
747
748                 /* remove from the comment char to end of line */
749                 if (c == com_char) {
750                         while (c != '\n') {
751                                 if ((c = scanc()) == EOF) {
752                                         /* end of file without newline! */
753                                         return (EOF);
754                                 }
755                         }
756                         assert(c == '\n');
757                         if (!hadtok) {
758                                 /*
759                                  * If there were no tokens on this line,
760                                  * then just pretend it didn't exist at all.
761                                  */
762                                 continue;
763                         }
764                         hadtok = 0;
765                         return (T_NL);
766                 }
767
768                 if (strchr(" \t\n;()<>,\"", c) && (tokidx != 0)) {
769                         /*
770                          * These are all token delimiters.  If there
771                          * is a token already in progress, we need to
772                          * process it.
773                          */
774                         unscanc(c);
775                         return (consume_token());
776                 }
777
778                 switch (c) {
779                 case '\n':
780                         if (!hadtok) {
781                                 /*
782                                  * If the line was completely devoid of tokens,
783                                  * then just ignore it.
784                                  */
785                                 continue;
786                         }
787                         /* we're starting a new line, reset the token state */
788                         hadtok = 0;
789                         return (T_NL);
790                 case ',':
791                         hadtok = 1;
792                         return (T_COMMA);
793                 case ';':
794                         hadtok = 1;
795                         return (T_SEMI);
796                 case '(':
797                         hadtok = 1;
798                         return (T_LPAREN);
799                 case ')':
800                         hadtok = 1;
801                         return (T_RPAREN);
802                 case '>':
803                         hadtok = 1;
804                         return (T_GT);
805                 case '<':
806                         /* symbol start! */
807                         hadtok = 1;
808                         return (get_symbol());
809                 case ' ':
810                 case '\t':
811                         /* whitespace, just ignore it */
812                         continue;
813                 case '"':
814                         hadtok = 1;
815                         instring = 1;
816                         return (T_QUOTE);
817                 default:
818                         hadtok = 1;
819                         add_tok(c);
820                         continue;
821                 }
822         }
823         return (EOF);
824 }
825
826 void
827 yyerror(const char *msg)
828 {
829         (void) fprintf(stderr, "%s: %d: error: %s\n",
830             filename, lineno, msg);
831         exit(4);
832 }
833
834 void
835 errf(const char *fmt, ...)
836 {
837         char    *msg;
838
839         va_list va;
840         va_start(va, fmt);
841         (void) vasprintf(&msg, fmt, va);
842         va_end(va);
843
844         (void) fprintf(stderr, "%s: %d: error: %s\n",
845             filename, lineno, msg);
846         free(msg);
847         exit(4);
848 }
849
850 void
851 warn(const char *fmt, ...)
852 {
853         char    *msg;
854
855         va_list va;
856         va_start(va, fmt);
857         (void) vasprintf(&msg, fmt, va);
858         va_end(va);
859
860         (void) fprintf(stderr, "%s: %d: warning: %s\n",
861             filename, lineno, msg);
862         free(msg);
863         warnings++;
864         if (!warnok)
865                 exit(4);
866 }