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