Merge from vendor branch LESS:
[dragonfly.git] / contrib / bind-9.3 / 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.6.2.8 2004/08/28 06:25: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         lex->saved_paren_count = lex->paren_count;
376         source->saved_line = source->line;
377
378         if (source == NULL) {
379                 if ((options & ISC_LEXOPT_NOMORE) != 0) {
380                         tokenp->type = isc_tokentype_nomore;
381                         return (ISC_R_SUCCESS);
382                 }
383                 return (ISC_R_NOMORE);
384         }
385
386         if (source->result != ISC_R_SUCCESS)
387                 return (source->result);
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                         if ((!escaped &&
637                              (c == ' ' || c == '\t' || lex->specials[c])) ||
638                             c == '\r' || c == '\n' || c == EOF) {
639                                 pushback(source, c);
640                                 if (source->result != ISC_R_SUCCESS) {
641                                         result = source->result;
642                                         goto done;
643                                 }
644                                 tokenp->type = isc_tokentype_string;
645                                 tokenp->value.as_textregion.base = lex->data;
646                                 tokenp->value.as_textregion.length =
647                                         lex->max_token - remaining;
648                                 done = ISC_TRUE;
649                                 continue;
650                         }
651                         if ((options & ISC_LEXOPT_ESCAPE) != 0)
652                                 escaped = (!escaped && c == '\\') ?
653                                                 ISC_TRUE : ISC_FALSE;
654                         if (remaining == 0U) {
655                                 result = grow_data(lex, &remaining,
656                                                    &curr, &prev);
657                                 if (result != ISC_R_SUCCESS)
658                                         goto done;
659                         }
660                         INSIST(remaining > 0U);
661                         *curr++ = c;
662                         *curr = '\0';
663                         remaining--;
664                         break;
665                 case lexstate_maybecomment:
666                         if (c == '*' &&
667                             (lex->comments & ISC_LEXCOMMENT_C) != 0) {
668                                 state = lexstate_ccomment;
669                                 continue;
670                         } else if (c == '/' &&
671                             (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) {
672                                 state = lexstate_eatline;
673                                 continue;
674                         }
675                         pushback(source, c);
676                         c = '/';
677                         no_comments = ISC_FALSE;
678                         state = saved_state;
679                         goto no_read;
680                 case lexstate_ccomment:
681                         if (c == EOF) {
682                                 result = ISC_R_UNEXPECTEDEND;
683                                 goto done;
684                         }
685                         if (c == '*')
686                                 state = lexstate_ccommentend;
687                         break;
688                 case lexstate_ccommentend:
689                         if (c == EOF) {
690                                 result = ISC_R_UNEXPECTEDEND;
691                                 goto done;
692                         }
693                         if (c == '/') {
694                                 /*
695                                  * C-style comments become a single space.
696                                  * We do this to ensure that a comment will
697                                  * act as a delimiter for strings and
698                                  * numbers.
699                                  */
700                                 c = ' ';
701                                 no_comments = ISC_FALSE;
702                                 state = saved_state;
703                                 goto no_read;
704                         } else if (c != '*')
705                                 state = lexstate_ccomment;
706                         break;
707                 case lexstate_eatline:
708                         if (c == EOF) {
709                                 result = ISC_R_UNEXPECTEDEND;
710                                 goto done;
711                         }
712                         if (c == '\n') {
713                                 no_comments = ISC_FALSE;
714                                 state = saved_state;
715                                 goto no_read;
716                         }
717                         break;
718                 case lexstate_qstring:
719                         if (c == EOF) {
720                                 result = ISC_R_UNEXPECTEDEND;
721                                 goto done;
722                         }
723                         if (c == '"') {
724                                 if (escaped) {
725                                         escaped = ISC_FALSE;
726                                         /*
727                                          * Overwrite the preceding backslash.
728                                          */
729                                         INSIST(prev != NULL);
730                                         *prev = '"';
731                                 } else {
732                                         tokenp->type = isc_tokentype_qstring;
733                                         tokenp->value.as_textregion.base =
734                                                 lex->data;
735                                         tokenp->value.as_textregion.length =
736                                                 lex->max_token - remaining;
737                                         no_comments = ISC_FALSE;
738                                         done = ISC_TRUE;
739                                 }
740                         } else {
741                                 if (c == '\n' && !escaped &&
742                             (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) {
743                                         pushback(source, c);
744                                         result = ISC_R_UNBALANCEDQUOTES;
745                                         goto done;
746                                 }
747                                 if (c == '\\' && !escaped)
748                                         escaped = ISC_TRUE;
749                                 else
750                                         escaped = ISC_FALSE;
751                                 if (remaining == 0U) {
752                                         result = grow_data(lex, &remaining,
753                                                            &curr, &prev);
754                                         if (result != ISC_R_SUCCESS)
755                                                 goto done;
756                                 }
757                                 INSIST(remaining > 0U);
758                                 prev = curr;
759                                 *curr++ = c;
760                                 *curr = '\0';
761                                 remaining--;
762                         }
763                         break;
764                 default:
765                         FATAL_ERROR(__FILE__, __LINE__,
766                                     isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX,
767                                                    ISC_MSG_UNEXPECTEDSTATE,
768                                                    "Unexpected state %d"),
769                                     state);
770                         /* Does not return. */
771                 }
772
773         } while (!done);
774
775         result = ISC_R_SUCCESS;
776  done:
777 #ifdef HAVE_FLOCKFILE
778         if (source->is_file)
779                 funlockfile(source->input);
780 #endif
781         return (result);
782 }
783
784 isc_result_t
785 isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
786                        isc_tokentype_t expect, isc_boolean_t eol)
787 {
788         unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
789                                ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
790         isc_result_t result;
791
792         if (expect == isc_tokentype_qstring)
793                 options |= ISC_LEXOPT_QSTRING;
794         else if (expect == isc_tokentype_number)
795                 options |= ISC_LEXOPT_NUMBER;
796         result = isc_lex_gettoken(lex, options, token);
797         if (result == ISC_R_RANGE)
798                 isc_lex_ungettoken(lex, token);
799         if (result != ISC_R_SUCCESS)
800                 return (result);
801
802         if (eol && ((token->type == isc_tokentype_eol) ||
803                     (token->type == isc_tokentype_eof)))
804                 return (ISC_R_SUCCESS);
805         if (token->type == isc_tokentype_string &&
806             expect == isc_tokentype_qstring)
807                 return (ISC_R_SUCCESS);
808         if (token->type != expect) {
809                 isc_lex_ungettoken(lex, token);
810                 if (token->type == isc_tokentype_eol ||
811                     token->type == isc_tokentype_eof)
812                         return (ISC_R_UNEXPECTEDEND);
813                 if (expect == isc_tokentype_number)
814                         return (ISC_R_BADNUMBER);
815                 return (ISC_R_UNEXPECTEDTOKEN);
816         }
817         return (ISC_R_SUCCESS);
818 }
819
820 void
821 isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
822         inputsource *source;
823         /*
824          * Unget the current token.
825          */
826
827         REQUIRE(VALID_LEX(lex));
828         source = HEAD(lex->sources);
829         REQUIRE(source != NULL);
830         REQUIRE(tokenp != NULL);
831         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
832                 tokenp->type == isc_tokentype_eof);
833
834         UNUSED(tokenp);
835
836         isc_buffer_first(source->pushback);
837         lex->paren_count = lex->saved_paren_count;
838         source->line = source->saved_line;
839         source->at_eof = ISC_FALSE;
840 }
841
842 void
843 isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r)
844 {
845         inputsource *source;
846
847         REQUIRE(VALID_LEX(lex));
848         source = HEAD(lex->sources);
849         REQUIRE(source != NULL);
850         REQUIRE(tokenp != NULL);
851         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
852                 tokenp->type == isc_tokentype_eof);
853
854         UNUSED(tokenp);
855
856         INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
857         r->base = (unsigned char *)isc_buffer_base(source->pushback) +
858                   source->ignored;
859         r->length = isc_buffer_consumedlength(source->pushback) -
860                     source->ignored;
861 }
862
863
864 char *
865 isc_lex_getsourcename(isc_lex_t *lex) {
866         inputsource *source;
867
868         REQUIRE(VALID_LEX(lex));
869         source = HEAD(lex->sources);
870
871         if (source == NULL)
872                 return (NULL);
873
874         return (source->name);
875 }
876
877 unsigned long
878 isc_lex_getsourceline(isc_lex_t *lex) {
879         inputsource *source;
880
881         REQUIRE(VALID_LEX(lex));
882         source = HEAD(lex->sources);
883
884         if (source == NULL)
885                 return (0);
886
887         return (source->line);
888 }
889
890
891 isc_result_t
892 isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
893         inputsource *source;
894         char *newname;
895
896         REQUIRE(VALID_LEX(lex));
897         source = HEAD(lex->sources);
898
899         if (source == NULL)
900                 return(ISC_R_NOTFOUND);
901         newname = isc_mem_strdup(lex->mctx, name);
902         if (newname == NULL)
903                 return (ISC_R_NOMEMORY);
904         isc_mem_free(lex->mctx, source->name);
905         source->name = newname;
906         return (ISC_R_SUCCESS);
907 }
908
909 isc_boolean_t
910 isc_lex_isfile(isc_lex_t *lex) {
911         inputsource *source;
912
913         REQUIRE(VALID_LEX(lex));
914
915         source = HEAD(lex->sources);
916
917         if (source == NULL)
918                 return (ISC_FALSE);
919
920         return (source->is_file);
921 }