Merge from vendor branch HEIMDAL:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / lex.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1998-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: lex.c,v 1.66.2.8 2004/03/09 06:11:47 marka Exp $ */
19
20 #include <config.h>
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <stdlib.h>
25
26 #include <isc/buffer.h>
27 #include <isc/file.h>
28 #include <isc/lex.h>
29 #include <isc/mem.h>
30 #include <isc/msgs.h>
31 #include <isc/stdio.h>
32 #include <isc/string.h>
33 #include <isc/util.h>
34
35 typedef struct inputsource {
36         isc_result_t                    result;
37         isc_boolean_t                   is_file;
38         isc_boolean_t                   need_close;
39         isc_boolean_t                   at_eof;
40         isc_buffer_t *                  pushback;
41         unsigned int                    ignored;
42         void *                          input;
43         char *                          name;
44         unsigned long                   line;
45         unsigned long                   saved_line;
46         ISC_LINK(struct inputsource)    link;
47 } inputsource;
48
49 #define LEX_MAGIC                       ISC_MAGIC('L', 'e', 'x', '!')
50 #define VALID_LEX(l)                    ISC_MAGIC_VALID(l, LEX_MAGIC)
51
52 struct isc_lex {
53         /* Unlocked. */
54         unsigned int                    magic;
55         isc_mem_t *                     mctx;
56         size_t                          max_token;
57         char *                          data;
58         unsigned int                    options;
59         unsigned int                    comments;
60         isc_boolean_t                   comment_ok;
61         isc_boolean_t                   last_was_eol;
62         unsigned int                    paren_count;
63         unsigned int                    saved_paren_count;
64         isc_lexspecials_t               specials;
65         LIST(struct inputsource)        sources;
66 };
67
68 static inline isc_result_t
69 grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
70         char *new;
71
72         new = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
73         if (new == NULL)
74                 return (ISC_R_NOMEMORY);
75         memcpy(new, lex->data, lex->max_token + 1);
76         *currp = new + (*currp - lex->data);
77         if (*prevp != NULL)
78                 *prevp = new + (*prevp - lex->data);
79         isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
80         lex->data = new;
81         *remainingp += lex->max_token;
82         lex->max_token *= 2;
83         return (ISC_R_SUCCESS);
84 }
85
86 isc_result_t
87 isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
88         isc_lex_t *lex;
89
90         /*
91          * Create a lexer.
92          */
93
94         REQUIRE(lexp != NULL && *lexp == NULL);
95         REQUIRE(max_token > 0U);
96
97         lex = isc_mem_get(mctx, sizeof *lex);
98         if (lex == NULL)
99                 return (ISC_R_NOMEMORY);
100         lex->data = isc_mem_get(mctx, max_token + 1);
101         if (lex->data == NULL) {
102                 isc_mem_put(mctx, lex, sizeof *lex);
103                 return (ISC_R_NOMEMORY);
104         }
105         lex->mctx = mctx;
106         lex->max_token = max_token;
107         lex->comments = 0;
108         lex->comment_ok = ISC_TRUE;
109         lex->last_was_eol = ISC_TRUE;
110         lex->paren_count = 0;
111         lex->saved_paren_count = 0;
112         memset(lex->specials, 0, 256);
113         INIT_LIST(lex->sources);
114         lex->magic = LEX_MAGIC;
115
116         *lexp = lex;
117
118         return (ISC_R_SUCCESS);
119 }
120
121 void
122 isc_lex_destroy(isc_lex_t **lexp) {
123         isc_lex_t *lex;
124
125         /*
126          * Destroy the lexer.
127          */
128
129         REQUIRE(lexp != NULL);
130         lex = *lexp;
131         REQUIRE(VALID_LEX(lex));
132
133         while (!EMPTY(lex->sources))
134                 isc_lex_close(lex);
135         if (lex->data != NULL)
136                 isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
137         lex->magic = 0;
138         isc_mem_put(lex->mctx, lex, sizeof *lex);
139
140         *lexp = NULL;
141 }
142
143 unsigned int
144 isc_lex_getcomments(isc_lex_t *lex) {
145         /*
146          * Return the current lexer commenting styles.
147          */
148
149         REQUIRE(VALID_LEX(lex));
150
151         return (lex->comments);
152 }
153
154 void
155 isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
156         /*
157          * Set allowed lexer commenting styles.
158          */
159
160         REQUIRE(VALID_LEX(lex));
161
162         lex->comments = comments;
163 }
164
165 void
166 isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
167         /*
168          * Put the current list of specials into 'specials'.
169          */
170
171         REQUIRE(VALID_LEX(lex));
172
173         memcpy(specials, lex->specials, 256);
174 }
175
176 void
177 isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
178         /*
179          * The characters in 'specials' are returned as tokens.  Along with
180          * whitespace, they delimit strings and numbers.
181          */
182
183         REQUIRE(VALID_LEX(lex));
184
185         memcpy(lex->specials, specials, 256);
186 }
187
188 static inline isc_result_t
189 new_source(isc_lex_t *lex, isc_boolean_t is_file, isc_boolean_t need_close,
190            void *input, const char *name)
191 {
192         inputsource *source;
193         isc_result_t result;
194
195         source = isc_mem_get(lex->mctx, sizeof *source);
196         if (source == NULL)
197                 return (ISC_R_NOMEMORY);
198         source->result = ISC_R_SUCCESS;
199         source->is_file = is_file;
200         source->need_close = need_close;
201         source->at_eof = ISC_FALSE;
202         source->input = input;
203         source->name = isc_mem_strdup(lex->mctx, name);
204         if (source->name == NULL) {
205                 isc_mem_put(lex->mctx, source, sizeof *source);
206                 return (ISC_R_NOMEMORY);
207         }
208         source->pushback = NULL;
209         result = isc_buffer_allocate(lex->mctx, &source->pushback,
210                                      lex->max_token);
211         if (result != ISC_R_SUCCESS) {
212                 isc_mem_free(lex->mctx, source->name);
213                 isc_mem_put(lex->mctx, source, sizeof *source);
214                 return (result);
215         }
216         source->ignored = 0;
217         source->line = 1;
218         ISC_LIST_INITANDPREPEND(lex->sources, source, link);
219
220         return (ISC_R_SUCCESS);
221 }
222
223 isc_result_t
224 isc_lex_openfile(isc_lex_t *lex, const char *filename) {
225         isc_result_t result;
226         FILE *stream = NULL;
227
228         /*
229          * Open 'filename' and make it the current input source for 'lex'.
230          */
231
232         REQUIRE(VALID_LEX(lex));
233
234         result = isc_stdio_open(filename, "r", &stream);
235         if (result != ISC_R_SUCCESS)
236                 return (result);
237
238         result = new_source(lex, ISC_TRUE, ISC_TRUE, stream, filename);
239         if (result != ISC_R_SUCCESS)
240                 fclose(stream);
241         return (result);
242 }
243
244 isc_result_t
245 isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
246         char name[128];
247
248         /*
249          * Make 'stream' the current input source for 'lex'.
250          */
251
252         REQUIRE(VALID_LEX(lex));
253
254         /* This is safe. */
255         sprintf(name, "stream-%p", stream);
256
257         return (new_source(lex, ISC_TRUE, ISC_FALSE, stream, name));
258 }
259
260 isc_result_t
261 isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
262         char name[128];
263
264         /*
265          * Make 'buffer' the current input source for 'lex'.
266          */
267
268         REQUIRE(VALID_LEX(lex));
269
270         /* This is safe. */
271         sprintf(name, "buffer-%p", buffer);
272
273         return (new_source(lex, ISC_FALSE, ISC_FALSE, buffer, name));
274 }
275
276 isc_result_t
277 isc_lex_close(isc_lex_t *lex) {
278         inputsource *source;
279
280         /*
281          * Close the most recently opened object (i.e. file or buffer).
282          */
283
284         REQUIRE(VALID_LEX(lex));
285
286         source = HEAD(lex->sources);
287         if (source == NULL)
288                 return (ISC_R_NOMORE);
289
290         ISC_LIST_UNLINK(lex->sources, source, link);
291         if (source->is_file) {
292                 if (source->need_close)
293                         fclose((FILE *)(source->input));
294         }
295         isc_mem_free(lex->mctx, source->name);
296         isc_buffer_free(&source->pushback);
297         isc_mem_put(lex->mctx, source, sizeof *source);
298
299         return (ISC_R_SUCCESS);
300 }
301
302 typedef enum {
303         lexstate_start,
304         lexstate_crlf,
305         lexstate_string,
306         lexstate_number,
307         lexstate_maybecomment,
308         lexstate_ccomment,
309         lexstate_ccommentend,
310         lexstate_eatline,
311         lexstate_qstring
312 } lexstate;
313
314 #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
315
316 static void
317 pushback(inputsource *source, int c) {
318         REQUIRE(source->pushback->current > 0);
319         if (c == EOF) {
320                 source->at_eof = ISC_FALSE;
321                 return;
322         }
323         source->pushback->current--;
324         if (c == '\n')
325                 source->line--;
326 }
327
328 static isc_result_t
329 pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
330         if (isc_buffer_availablelength(source->pushback) == 0) {
331                 isc_buffer_t *tbuf = NULL;
332                 unsigned int oldlen;
333                 isc_region_t used;
334                 isc_result_t result;
335
336                 oldlen = isc_buffer_length(source->pushback);
337                 result = isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
338                 if (result != ISC_R_SUCCESS)
339                         return (result);
340                 isc_buffer_usedregion(source->pushback, &used);
341                 result = isc_buffer_copyregion(tbuf, &used);
342                 INSIST(result == ISC_R_SUCCESS);
343                 tbuf->current = source->pushback->current;
344                 isc_buffer_free(&source->pushback);
345                 source->pushback = tbuf;
346         }
347         isc_buffer_putuint8(source->pushback, (isc_uint8_t)c);
348         return (ISC_R_SUCCESS);
349 }
350
351 isc_result_t
352 isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
353         inputsource *source;
354         int c;
355         isc_boolean_t done = ISC_FALSE;
356         isc_boolean_t no_comments = ISC_FALSE;
357         isc_boolean_t escaped = ISC_FALSE;
358         lexstate state = lexstate_start;
359         lexstate saved_state = lexstate_start;
360         isc_buffer_t *buffer;
361         FILE *stream;
362         char *curr, *prev;
363         size_t remaining;
364         unsigned long as_ulong;
365         unsigned int saved_options;
366         char *e;
367         isc_result_t result;
368
369         /*
370          * Get the next token.
371          */
372
373         REQUIRE(VALID_LEX(lex));
374         source = HEAD(lex->sources);
375         REQUIRE(tokenp != NULL);
376
377         lex->saved_paren_count = lex->paren_count;
378         source->saved_line = source->line;
379
380         if (source == NULL) {
381                 if ((options & ISC_LEXOPT_NOMORE) != 0) {
382                         tokenp->type = isc_tokentype_nomore;
383                         return (ISC_R_SUCCESS);
384                 }
385                 return (ISC_R_NOMORE);
386         }
387
388         if (source->result != ISC_R_SUCCESS)
389                 return (source->result);
390
391         if (isc_buffer_remaininglength(source->pushback) == 0 &&
392             source->at_eof)
393         {
394                 if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
395                     lex->paren_count != 0) {
396                         lex->paren_count = 0;
397                         return (ISC_R_UNBALANCED);
398                 }
399                 if ((options & ISC_LEXOPT_EOF) != 0) {
400                         tokenp->type = isc_tokentype_eof;
401                         return (ISC_R_SUCCESS);
402                 }
403                 return (ISC_R_EOF);
404         }
405
406         isc_buffer_compact(source->pushback);
407
408         saved_options = options;
409         if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0)
410                 options &= ~IWSEOL;
411
412         curr = lex->data;
413         *curr = '\0';
414
415         prev = NULL;
416         remaining = lex->max_token;
417
418 #ifdef HAVE_FLOCKFILE
419         if (source->is_file)
420                 flockfile(source->input);
421 #endif
422
423         do {
424                 if (isc_buffer_remaininglength(source->pushback) == 0) {
425                         if (source->is_file) {
426                                 stream = source->input;
427
428 #if defined(HAVE_FLOCKFILE) && defined(HAVE_GETCUNLOCKED)
429                                 c = getc_unlocked(stream);
430 #else
431                                 c = getc(stream);
432 #endif
433                                 if (c == EOF) {
434                                         if (ferror(stream)) {
435                                                 source->result = ISC_R_IOERROR;
436                                                 result = source->result;
437                                                 goto done;
438                                         }
439                                         source->at_eof = ISC_TRUE;
440                                 }
441                         } else {
442                                 buffer = source->input;
443
444                                 if (buffer->current == buffer->used) {
445                                         c = EOF;
446                                         source->at_eof = ISC_TRUE;
447                                 } else {
448                                         c = *((char *)buffer->base +
449                                               buffer->current);
450                                         buffer->current++;
451                                 }
452                         }
453                         if (c != EOF) {
454                                 source->result = pushandgrow(lex, source, c);
455                                 if (source->result != ISC_R_SUCCESS) {
456                                         result = source->result;
457                                         goto done;
458                                 }
459                         }
460                 }
461
462                 if (!source->at_eof) {
463                         if (state == lexstate_start)
464                                 /* Token has not started yet. */
465                                 source->ignored =
466                                    isc_buffer_consumedlength(source->pushback);
467                         c = isc_buffer_getuint8(source->pushback);
468                 } else {
469                         c = EOF;
470                 }
471
472                 if (c == '\n')
473                         source->line++;
474
475                 if (lex->comment_ok && !no_comments) {
476                         if (!escaped && c == ';' &&
477                             ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE)
478                              != 0)) {
479                                 saved_state = state;
480                                 state = lexstate_eatline;
481                                 no_comments = ISC_TRUE;
482                                 continue;
483                         } else if (c == '/' &&
484                                    (lex->comments &
485                                     (ISC_LEXCOMMENT_C|
486                                      ISC_LEXCOMMENT_CPLUSPLUS)) != 0) {
487                                 saved_state = state;
488                                 state = lexstate_maybecomment;
489                                 no_comments = ISC_TRUE;
490                                 continue;
491                         } else if (c == '#' &&
492                                    ((lex->comments & ISC_LEXCOMMENT_SHELL)
493                                     != 0)) {
494                                 saved_state = state;
495                                 state = lexstate_eatline;
496                                 no_comments = ISC_TRUE;
497                                 continue;
498                         }
499                 }
500
501         no_read:
502                 /* INSIST(c == EOF || (c >= 0 && c <= 255)); */
503                 switch (state) {
504                 case lexstate_start:
505                         if (c == EOF) {
506                                 lex->last_was_eol = ISC_FALSE;
507                                 if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
508                                     lex->paren_count != 0) {
509                                         lex->paren_count = 0;
510                                         result = ISC_R_UNBALANCED;
511                                         goto done;
512                                 }
513                                 if ((options & ISC_LEXOPT_EOF) == 0) {
514                                         result = ISC_R_EOF;
515                                         goto done;
516                                 }
517                                 tokenp->type = isc_tokentype_eof;
518                                 done = ISC_TRUE;
519                         } else if (c == ' ' || c == '\t') {
520                                 if (lex->last_was_eol &&
521                                     (options & ISC_LEXOPT_INITIALWS)
522                                     != 0) {
523                                         lex->last_was_eol = ISC_FALSE;
524                                         tokenp->type = isc_tokentype_initialws;
525                                         tokenp->value.as_char = c;
526                                         done = ISC_TRUE;
527                                 }
528                         } else if (c == '\n') {
529                                 if ((options & ISC_LEXOPT_EOL) != 0) {
530                                         tokenp->type = isc_tokentype_eol;
531                                         done = ISC_TRUE;
532                                 }
533                                 lex->last_was_eol = ISC_TRUE;
534                         } else if (c == '\r') {
535                                 if ((options & ISC_LEXOPT_EOL) != 0)
536                                         state = lexstate_crlf;
537                         } else if (c == '"' &&
538                                    (options & ISC_LEXOPT_QSTRING) != 0) {
539                                 lex->last_was_eol = ISC_FALSE;
540                                 no_comments = ISC_TRUE;
541                                 state = lexstate_qstring;
542                         } else if (lex->specials[c]) {
543                                 lex->last_was_eol = ISC_FALSE;
544                                 if ((c == '(' || c == ')') &&
545                                     (options & ISC_LEXOPT_DNSMULTILINE) != 0) {
546                                         if (c == '(') {
547                                                 if (lex->paren_count == 0)
548                                                         options &= ~IWSEOL;
549                                                 lex->paren_count++;
550                                         } else {
551                                                 if (lex->paren_count == 0) {
552                                                     result = ISC_R_UNBALANCED;
553                                                     goto done;
554                                                 }
555                                                 lex->paren_count--;
556                                                 if (lex->paren_count == 0)
557                                                         options =
558                                                                 saved_options;
559                                         }
560                                         continue;
561                                 }
562                                 tokenp->type = isc_tokentype_special;
563                                 tokenp->value.as_char = c;
564                                 done = ISC_TRUE;
565                         } else if (isdigit((unsigned char)c) &&
566                                    (options & ISC_LEXOPT_NUMBER) != 0) {
567                                 lex->last_was_eol = ISC_FALSE;
568                                 state = lexstate_number;
569                                 goto no_read;
570                         } else {
571                                 lex->last_was_eol = ISC_FALSE;
572                                 state = lexstate_string;
573                                 goto no_read;
574                         }
575                         break;
576                 case lexstate_crlf:
577                         if (c != '\n')
578                                 pushback(source, c);
579                         tokenp->type = isc_tokentype_eol;
580                         done = ISC_TRUE;
581                         lex->last_was_eol = ISC_TRUE;
582                         break;
583                 case lexstate_number:
584                         if (c == EOF || !isdigit((unsigned char)c)) {
585                                 if (c == ' ' || c == '\t' || c == '\r' ||
586                                     c == '\n' || c == EOF ||
587                                     lex->specials[c]) {
588                                         int base;
589                                         if ((options & ISC_LEXOPT_CNUMBER) != 0)
590                                                 base = 0;
591                                         else
592                                                 base = 10;
593                                         pushback(source, c);
594                                         as_ulong = strtoul(lex->data, &e, base);
595                                         if (as_ulong == ULONG_MAX &&
596                                             errno == ERANGE) {
597                                                 result = ISC_R_RANGE;
598                                                 goto done;
599                                         } else if (*e == 0) {
600                                                 tokenp->type =
601                                                         isc_tokentype_number;
602                                                 tokenp->value.as_ulong =
603                                                         as_ulong;
604                                         } else {
605                                                 isc_tokenvalue_t *v;
606
607                                                 tokenp->type =
608                                                         isc_tokentype_string;
609                                                 v = &(tokenp->value);
610                                                 v->as_textregion.base =
611                                                         lex->data;
612                                                 v->as_textregion.length =
613                                                         lex->max_token -
614                                                         remaining;
615                                         }
616                                         done = ISC_TRUE;
617                                         continue;
618                                 } else if (!(options & ISC_LEXOPT_CNUMBER) ||
619                                            ((c != 'x' && c != 'X') ||
620                                            (curr != &lex->data[1]) ||
621                                            (lex->data[0] != '0'))) {
622                                         /* Above test supports hex numbers */
623                                         state = lexstate_string;
624                                 }
625                         }
626                         if (remaining == 0U) {
627                                 result = grow_data(lex, &remaining,
628                                                    &curr, &prev);
629                                 if (result != ISC_R_SUCCESS)
630                                         goto done;
631                         }
632                         INSIST(remaining > 0U);
633                         *curr++ = c;
634                         *curr = '\0';
635                         remaining--;
636                         break;
637                 case lexstate_string:
638                         if ((!escaped &&
639                              (c == ' ' || c == '\t' || lex->specials[c])) ||
640                             c == '\r' || c == '\n' || c == EOF) {
641                                 pushback(source, c);
642                                 if (source->result != ISC_R_SUCCESS) {
643                                         result = source->result;
644                                         goto done;
645                                 }
646                                 tokenp->type = isc_tokentype_string;
647                                 tokenp->value.as_textregion.base = lex->data;
648                                 tokenp->value.as_textregion.length =
649                                         lex->max_token - remaining;
650                                 done = ISC_TRUE;
651                                 continue;
652                         }
653                         if ((options & ISC_LEXOPT_ESCAPE) != 0)
654                                 escaped = (!escaped && c == '\\') ?
655                                                 ISC_TRUE : ISC_FALSE;
656                         if (remaining == 0U) {
657                                 result = grow_data(lex, &remaining,
658                                                    &curr, &prev);
659                                 if (result != ISC_R_SUCCESS)
660                                         goto done;
661                         }
662                         INSIST(remaining > 0U);
663                         *curr++ = c;
664                         *curr = '\0';
665                         remaining--;
666                         break;
667                 case lexstate_maybecomment:
668                         if (c == '*' &&
669                             (lex->comments & ISC_LEXCOMMENT_C) != 0) {
670                                 state = lexstate_ccomment;
671                                 continue;
672                         } else if (c == '/' &&
673                             (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) {
674                                 state = lexstate_eatline;
675                                 continue;
676                         }
677                         pushback(source, c);
678                         c = '/';
679                         no_comments = ISC_FALSE;
680                         state = saved_state;
681                         goto no_read;
682                 case lexstate_ccomment:
683                         if (c == EOF) {
684                                 result = ISC_R_UNEXPECTEDEND;
685                                 goto done;
686                         }
687                         if (c == '*')
688                                 state = lexstate_ccommentend;
689                         break;
690                 case lexstate_ccommentend:
691                         if (c == EOF) {
692                                 result = ISC_R_UNEXPECTEDEND;
693                                 goto done;
694                         }
695                         if (c == '/') {
696                                 /*
697                                  * C-style comments become a single space.
698                                  * We do this to ensure that a comment will
699                                  * act as a delimiter for strings and
700                                  * numbers.
701                                  */
702                                 c = ' ';
703                                 no_comments = ISC_FALSE;
704                                 state = saved_state;
705                                 goto no_read;
706                         } else if (c != '*')
707                                 state = lexstate_ccomment;
708                         break;
709                 case lexstate_eatline:
710                         if (c == EOF) {
711                                 result = ISC_R_UNEXPECTEDEND;
712                                 goto done;
713                         }
714                         if (c == '\n') {
715                                 no_comments = ISC_FALSE;
716                                 state = saved_state;
717                                 goto no_read;
718                         }
719                         break;
720                 case lexstate_qstring:
721                         if (c == EOF) {
722                                 result = ISC_R_UNEXPECTEDEND;
723                                 goto done;
724                         }
725                         if (c == '"') {
726                                 if (escaped) {
727                                         escaped = ISC_FALSE;
728                                         /*
729                                          * Overwrite the preceding backslash.
730                                          */
731                                         INSIST(prev != NULL);
732                                         *prev = '"';
733                                 } else {
734                                         tokenp->type = isc_tokentype_qstring;
735                                         tokenp->value.as_textregion.base =
736                                                 lex->data;
737                                         tokenp->value.as_textregion.length =
738                                                 lex->max_token - remaining;
739                                         no_comments = ISC_FALSE;
740                                         done = ISC_TRUE;
741                                 }
742                         } else {
743                                 if (c == '\n' && !escaped &&
744                             (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) {
745                                         pushback(source, c);
746                                         result = ISC_R_UNBALANCEDQUOTES;
747                                         goto done;
748                                 }
749                                 if (c == '\\' && !escaped)
750                                         escaped = ISC_TRUE;
751                                 else
752                                         escaped = ISC_FALSE;
753                                 if (remaining == 0U) {
754                                         result = grow_data(lex, &remaining,
755                                                            &curr, &prev);
756                                         if (result != ISC_R_SUCCESS)
757                                                 goto done;
758                                 }
759                                 INSIST(remaining > 0U);
760                                 prev = curr;
761                                 *curr++ = c;
762                                 *curr = '\0';
763                                 remaining--;
764                         }
765                         break;
766                 default:
767                         FATAL_ERROR(__FILE__, __LINE__,
768                                     isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX,
769                                                    ISC_MSG_UNEXPECTEDSTATE,
770                                                    "Unexpected state %d"),
771                                     state);
772                         /* Does not return. */
773                 }
774
775         } while (!done);
776
777         result = ISC_R_SUCCESS;
778  done:
779 #ifdef HAVE_FLOCKFILE
780         if (source->is_file)
781                 funlockfile(source->input);
782 #endif
783         return (result);
784 }
785
786 isc_result_t
787 isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
788                        isc_tokentype_t expect, isc_boolean_t eol)
789 {
790         unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
791                                ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
792         isc_result_t result;
793
794         if (expect == isc_tokentype_qstring)
795                 options |= ISC_LEXOPT_QSTRING;
796         else if (expect == isc_tokentype_number)
797                 options |= ISC_LEXOPT_NUMBER;
798         result = isc_lex_gettoken(lex, options, token);
799         if (result == ISC_R_RANGE)
800                 isc_lex_ungettoken(lex, token);
801         if (result != ISC_R_SUCCESS)
802                 return (result);
803
804         if (eol && ((token->type == isc_tokentype_eol) ||
805                     (token->type == isc_tokentype_eof)))
806                 return (ISC_R_SUCCESS);
807         if (token->type == isc_tokentype_string &&
808             expect == isc_tokentype_qstring)
809                 return (ISC_R_SUCCESS);
810         if (token->type != expect) {
811                 isc_lex_ungettoken(lex, token);
812                 if (token->type == isc_tokentype_eol ||
813                     token->type == isc_tokentype_eof)
814                         return (ISC_R_UNEXPECTEDEND);
815                 if (expect == isc_tokentype_number)
816                         return (ISC_R_BADNUMBER);
817                 return (ISC_R_UNEXPECTEDTOKEN);
818         }
819         return (ISC_R_SUCCESS);
820 }
821
822 void
823 isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
824         inputsource *source;
825         /*
826          * Unget the current token.
827          */
828
829         REQUIRE(VALID_LEX(lex));
830         source = HEAD(lex->sources);
831         REQUIRE(source != NULL);
832         REQUIRE(tokenp != NULL);
833         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
834                 tokenp->type == isc_tokentype_eof);
835
836         UNUSED(tokenp);
837
838         isc_buffer_first(source->pushback);
839         lex->paren_count = lex->saved_paren_count;
840         source->line = source->saved_line;
841         source->at_eof = ISC_FALSE;
842 }
843
844 void
845 isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r)
846 {
847         inputsource *source;
848
849         REQUIRE(VALID_LEX(lex));
850         source = HEAD(lex->sources);
851         REQUIRE(source != NULL);
852         REQUIRE(tokenp != NULL);
853         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
854                 tokenp->type == isc_tokentype_eof);
855
856         UNUSED(tokenp);
857
858         INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
859         r->base = (unsigned char *)isc_buffer_base(source->pushback) +
860                   source->ignored;
861         r->length = isc_buffer_consumedlength(source->pushback) -
862                     source->ignored;
863 }
864
865
866 char *
867 isc_lex_getsourcename(isc_lex_t *lex) {
868         inputsource *source;
869
870         REQUIRE(VALID_LEX(lex));
871         source = HEAD(lex->sources);
872
873         if (source == NULL)
874                 return(NULL);
875
876         return (source->name);
877 }
878
879 unsigned long
880 isc_lex_getsourceline(isc_lex_t *lex) {
881         inputsource *source;
882
883         REQUIRE(VALID_LEX(lex));
884         source = HEAD(lex->sources);
885
886         if (source == NULL)
887                 return(0);
888
889         return (source->line);
890 }
891
892 isc_boolean_t
893 isc_lex_isfile(isc_lex_t *lex) {
894         inputsource *source;
895
896         REQUIRE(VALID_LEX(lex));
897
898         source = HEAD(lex->sources);
899
900         if (source == NULL)
901                 return (ISC_FALSE);
902
903         return (source->is_file);
904 }