Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / lib / isc / lex.c
1 /*
2  * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1998-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or 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.86 2007/09/17 09:56:29 shane Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdlib.h>
27
28 #include <isc/buffer.h>
29 #include <isc/file.h>
30 #include <isc/lex.h>
31 #include <isc/mem.h>
32 #include <isc/msgs.h>
33 #include <isc/parseint.h>
34 #include <isc/print.h>
35 #include <isc/stdio.h>
36 #include <isc/string.h>
37 #include <isc/util.h>
38
39 typedef struct inputsource {
40         isc_result_t                    result;
41         isc_boolean_t                   is_file;
42         isc_boolean_t                   need_close;
43         isc_boolean_t                   at_eof;
44         isc_buffer_t *                  pushback;
45         unsigned int                    ignored;
46         void *                          input;
47         char *                          name;
48         unsigned long                   line;
49         unsigned long                   saved_line;
50         ISC_LINK(struct inputsource)    link;
51 } inputsource;
52
53 #define LEX_MAGIC                       ISC_MAGIC('L', 'e', 'x', '!')
54 #define VALID_LEX(l)                    ISC_MAGIC_VALID(l, LEX_MAGIC)
55
56 struct isc_lex {
57         /* Unlocked. */
58         unsigned int                    magic;
59         isc_mem_t *                     mctx;
60         size_t                          max_token;
61         char *                          data;
62         unsigned int                    comments;
63         isc_boolean_t                   comment_ok;
64         isc_boolean_t                   last_was_eol;
65         unsigned int                    paren_count;
66         unsigned int                    saved_paren_count;
67         isc_lexspecials_t               specials;
68         LIST(struct inputsource)        sources;
69 };
70
71 static inline isc_result_t
72 grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
73         char *new;
74
75         new = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
76         if (new == NULL)
77                 return (ISC_R_NOMEMORY);
78         memcpy(new, lex->data, lex->max_token + 1);
79         *currp = new + (*currp - lex->data);
80         if (*prevp != NULL)
81                 *prevp = new + (*prevp - lex->data);
82         isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
83         lex->data = new;
84         *remainingp += lex->max_token;
85         lex->max_token *= 2;
86         return (ISC_R_SUCCESS);
87 }
88
89 isc_result_t
90 isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
91         isc_lex_t *lex;
92
93         /*
94          * Create a lexer.
95          */
96
97         REQUIRE(lexp != NULL && *lexp == NULL);
98         REQUIRE(max_token > 0U);
99
100         lex = isc_mem_get(mctx, sizeof(*lex));
101         if (lex == NULL)
102                 return (ISC_R_NOMEMORY);
103         lex->data = isc_mem_get(mctx, max_token + 1);
104         if (lex->data == NULL) {
105                 isc_mem_put(mctx, lex, sizeof(*lex));
106                 return (ISC_R_NOMEMORY);
107         }
108         lex->mctx = mctx;
109         lex->max_token = max_token;
110         lex->comments = 0;
111         lex->comment_ok = ISC_TRUE;
112         lex->last_was_eol = ISC_TRUE;
113         lex->paren_count = 0;
114         lex->saved_paren_count = 0;
115         memset(lex->specials, 0, 256);
116         INIT_LIST(lex->sources);
117         lex->magic = LEX_MAGIC;
118
119         *lexp = lex;
120
121         return (ISC_R_SUCCESS);
122 }
123
124 void
125 isc_lex_destroy(isc_lex_t **lexp) {
126         isc_lex_t *lex;
127
128         /*
129          * Destroy the lexer.
130          */
131
132         REQUIRE(lexp != NULL);
133         lex = *lexp;
134         REQUIRE(VALID_LEX(lex));
135
136         while (!EMPTY(lex->sources))
137                 RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
138         if (lex->data != NULL)
139                 isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
140         lex->magic = 0;
141         isc_mem_put(lex->mctx, lex, sizeof(*lex));
142
143         *lexp = NULL;
144 }
145
146 unsigned int
147 isc_lex_getcomments(isc_lex_t *lex) {
148         /*
149          * Return the current lexer commenting styles.
150          */
151
152         REQUIRE(VALID_LEX(lex));
153
154         return (lex->comments);
155 }
156
157 void
158 isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
159         /*
160          * Set allowed lexer commenting styles.
161          */
162
163         REQUIRE(VALID_LEX(lex));
164
165         lex->comments = comments;
166 }
167
168 void
169 isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
170         /*
171          * Put the current list of specials into 'specials'.
172          */
173
174         REQUIRE(VALID_LEX(lex));
175
176         memcpy(specials, lex->specials, 256);
177 }
178
179 void
180 isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
181         /*
182          * The characters in 'specials' are returned as tokens.  Along with
183          * whitespace, they delimit strings and numbers.
184          */
185
186         REQUIRE(VALID_LEX(lex));
187
188         memcpy(lex->specials, specials, 256);
189 }
190
191 static inline isc_result_t
192 new_source(isc_lex_t *lex, isc_boolean_t is_file, isc_boolean_t need_close,
193            void *input, const char *name)
194 {
195         inputsource *source;
196         isc_result_t result;
197
198         source = isc_mem_get(lex->mctx, sizeof(*source));
199         if (source == NULL)
200                 return (ISC_R_NOMEMORY);
201         source->result = ISC_R_SUCCESS;
202         source->is_file = is_file;
203         source->need_close = need_close;
204         source->at_eof = ISC_FALSE;
205         source->input = input;
206         source->name = isc_mem_strdup(lex->mctx, name);
207         if (source->name == NULL) {
208                 isc_mem_put(lex->mctx, source, sizeof(*source));
209                 return (ISC_R_NOMEMORY);
210         }
211         source->pushback = NULL;
212         result = isc_buffer_allocate(lex->mctx, &source->pushback,
213                                      lex->max_token);
214         if (result != ISC_R_SUCCESS) {
215                 isc_mem_free(lex->mctx, source->name);
216                 isc_mem_put(lex->mctx, source, sizeof(*source));
217                 return (result);
218         }
219         source->ignored = 0;
220         source->line = 1;
221         ISC_LIST_INITANDPREPEND(lex->sources, source, link);
222
223         return (ISC_R_SUCCESS);
224 }
225
226 isc_result_t
227 isc_lex_openfile(isc_lex_t *lex, const char *filename) {
228         isc_result_t result;
229         FILE *stream = NULL;
230
231         /*
232          * Open 'filename' and make it the current input source for 'lex'.
233          */
234
235         REQUIRE(VALID_LEX(lex));
236
237         result = isc_stdio_open(filename, "r", &stream);
238         if (result != ISC_R_SUCCESS)
239                 return (result);
240
241         result = new_source(lex, ISC_TRUE, ISC_TRUE, stream, filename);
242         if (result != ISC_R_SUCCESS)
243                 (void)fclose(stream);
244         return (result);
245 }
246
247 isc_result_t
248 isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
249         char name[128];
250
251         /*
252          * Make 'stream' the current input source for 'lex'.
253          */
254
255         REQUIRE(VALID_LEX(lex));
256
257         snprintf(name, sizeof(name), "stream-%p", stream);
258
259         return (new_source(lex, ISC_TRUE, ISC_FALSE, stream, name));
260 }
261
262 isc_result_t
263 isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
264         char name[128];
265
266         /*
267          * Make 'buffer' the current input source for 'lex'.
268          */
269
270         REQUIRE(VALID_LEX(lex));
271
272         snprintf(name, sizeof(name), "buffer-%p", buffer);
273
274         return (new_source(lex, ISC_FALSE, ISC_FALSE, buffer, name));
275 }
276
277 isc_result_t
278 isc_lex_close(isc_lex_t *lex) {
279         inputsource *source;
280
281         /*
282          * Close the most recently opened object (i.e. file or buffer).
283          */
284
285         REQUIRE(VALID_LEX(lex));
286
287         source = HEAD(lex->sources);
288         if (source == NULL)
289                 return (ISC_R_NOMORE);
290
291         ISC_LIST_UNLINK(lex->sources, source, link);
292         if (source->is_file) {
293                 if (source->need_close)
294                         (void)fclose((FILE *)(source->input));
295         }
296         isc_mem_free(lex->mctx, source->name);
297         isc_buffer_free(&source->pushback);
298         isc_mem_put(lex->mctx, source, sizeof(*source));
299
300         return (ISC_R_SUCCESS);
301 }
302
303 typedef enum {
304         lexstate_start,
305         lexstate_crlf,
306         lexstate_string,
307         lexstate_number,
308         lexstate_maybecomment,
309         lexstate_ccomment,
310         lexstate_ccommentend,
311         lexstate_eatline,
312         lexstate_qstring
313 } lexstate;
314
315 #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
316
317 static void
318 pushback(inputsource *source, int c) {
319         REQUIRE(source->pushback->current > 0);
320         if (c == EOF) {
321                 source->at_eof = ISC_FALSE;
322                 return;
323         }
324         source->pushback->current--;
325         if (c == '\n')
326                 source->line--;
327 }
328
329 static isc_result_t
330 pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
331         if (isc_buffer_availablelength(source->pushback) == 0) {
332                 isc_buffer_t *tbuf = NULL;
333                 unsigned int oldlen;
334                 isc_region_t used;
335                 isc_result_t result;
336
337                 oldlen = isc_buffer_length(source->pushback);
338                 result = isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
339                 if (result != ISC_R_SUCCESS)
340                         return (result);
341                 isc_buffer_usedregion(source->pushback, &used);
342                 result = isc_buffer_copyregion(tbuf, &used);
343                 INSIST(result == ISC_R_SUCCESS);
344                 tbuf->current = source->pushback->current;
345                 isc_buffer_free(&source->pushback);
346                 source->pushback = tbuf;
347         }
348         isc_buffer_putuint8(source->pushback, (isc_uint8_t)c);
349         return (ISC_R_SUCCESS);
350 }
351
352 isc_result_t
353 isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
354         inputsource *source;
355         int c;
356         isc_boolean_t done = ISC_FALSE;
357         isc_boolean_t no_comments = ISC_FALSE;
358         isc_boolean_t escaped = ISC_FALSE;
359         lexstate state = lexstate_start;
360         lexstate saved_state = lexstate_start;
361         isc_buffer_t *buffer;
362         FILE *stream;
363         char *curr, *prev;
364         size_t remaining;
365         isc_uint32_t as_ulong;
366         unsigned int saved_options;
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         if (source == NULL) {
378                 if ((options & ISC_LEXOPT_NOMORE) != 0) {
379                         tokenp->type = isc_tokentype_nomore;
380                         return (ISC_R_SUCCESS);
381                 }
382                 return (ISC_R_NOMORE);
383         }
384
385         if (source->result != ISC_R_SUCCESS)
386                 return (source->result);
387
388         lex->saved_paren_count = lex->paren_count;
389         source->saved_line = source->line;
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                                 if ((options & ISC_LEXOPT_OCTAL) != 0 &&
569                                     (c == '8' || c == '9'))
570                                         state = lexstate_string;
571                                 else
572                                         state = lexstate_number;
573                                 goto no_read;
574                         } else {
575                                 lex->last_was_eol = ISC_FALSE;
576                                 state = lexstate_string;
577                                 goto no_read;
578                         }
579                         break;
580                 case lexstate_crlf:
581                         if (c != '\n')
582                                 pushback(source, c);
583                         tokenp->type = isc_tokentype_eol;
584                         done = ISC_TRUE;
585                         lex->last_was_eol = ISC_TRUE;
586                         break;
587                 case lexstate_number:
588                         if (c == EOF || !isdigit((unsigned char)c)) {
589                                 if (c == ' ' || c == '\t' || c == '\r' ||
590                                     c == '\n' || c == EOF ||
591                                     lex->specials[c]) {
592                                         int base;
593                                         if ((options & ISC_LEXOPT_OCTAL) != 0)
594                                                 base = 8;
595                                         else if ((options & ISC_LEXOPT_CNUMBER) != 0)
596                                                 base = 0;
597                                         else
598                                                 base = 10;
599                                         pushback(source, c);
600
601                                         result = isc_parse_uint32(&as_ulong,
602                                                                   lex->data,
603                                                                   base);
604                                         if (result == ISC_R_SUCCESS) {
605                                                 tokenp->type =
606                                                         isc_tokentype_number;
607                                                 tokenp->value.as_ulong =
608                                                         as_ulong;
609                                         } else if (result == ISC_R_BADNUMBER) {
610                                                 isc_tokenvalue_t *v;
611
612                                                 tokenp->type =
613                                                         isc_tokentype_string;
614                                                 v = &(tokenp->value);
615                                                 v->as_textregion.base =
616                                                         lex->data;
617                                                 v->as_textregion.length =
618                                                         lex->max_token -
619                                                         remaining;
620                                         } else
621                                                 goto done;
622                                         done = ISC_TRUE;
623                                         continue;
624                                 } else if (!(options & ISC_LEXOPT_CNUMBER) ||
625                                            ((c != 'x' && c != 'X') ||
626                                            (curr != &lex->data[1]) ||
627                                            (lex->data[0] != '0'))) {
628                                         /* Above test supports hex numbers */
629                                         state = lexstate_string;
630                                 }
631                         } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
632                                    (c == '8' || c == '9')) {
633                                 state = lexstate_string;
634                         }
635                         if (remaining == 0U) {
636                                 result = grow_data(lex, &remaining,
637                                                    &curr, &prev);
638                                 if (result != ISC_R_SUCCESS)
639                                         goto done;
640                         }
641                         INSIST(remaining > 0U);
642                         *curr++ = c;
643                         *curr = '\0';
644                         remaining--;
645                         break;
646                 case lexstate_string:
647                         /*
648                          * EOF needs to be checked before lex->specials[c]
649                          * as lex->specials[EOF] is not a good idea.
650                          */
651                         if (c == '\r' || c == '\n' || c == EOF ||
652                             (!escaped &&
653                              (c == ' ' || c == '\t' || lex->specials[c]))) {
654                                 pushback(source, c);
655                                 if (source->result != ISC_R_SUCCESS) {
656                                         result = source->result;
657                                         goto done;
658                                 }
659                                 tokenp->type = isc_tokentype_string;
660                                 tokenp->value.as_textregion.base = lex->data;
661                                 tokenp->value.as_textregion.length =
662                                         lex->max_token - remaining;
663                                 done = ISC_TRUE;
664                                 continue;
665                         }
666                         if ((options & ISC_LEXOPT_ESCAPE) != 0)
667                                 escaped = (!escaped && c == '\\') ?
668                                                 ISC_TRUE : ISC_FALSE;
669                         if (remaining == 0U) {
670                                 result = grow_data(lex, &remaining,
671                                                    &curr, &prev);
672                                 if (result != ISC_R_SUCCESS)
673                                         goto done;
674                         }
675                         INSIST(remaining > 0U);
676                         *curr++ = c;
677                         *curr = '\0';
678                         remaining--;
679                         break;
680                 case lexstate_maybecomment:
681                         if (c == '*' &&
682                             (lex->comments & ISC_LEXCOMMENT_C) != 0) {
683                                 state = lexstate_ccomment;
684                                 continue;
685                         } else if (c == '/' &&
686                             (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) {
687                                 state = lexstate_eatline;
688                                 continue;
689                         }
690                         pushback(source, c);
691                         c = '/';
692                         no_comments = ISC_FALSE;
693                         state = saved_state;
694                         goto no_read;
695                 case lexstate_ccomment:
696                         if (c == EOF) {
697                                 result = ISC_R_UNEXPECTEDEND;
698                                 goto done;
699                         }
700                         if (c == '*')
701                                 state = lexstate_ccommentend;
702                         break;
703                 case lexstate_ccommentend:
704                         if (c == EOF) {
705                                 result = ISC_R_UNEXPECTEDEND;
706                                 goto done;
707                         }
708                         if (c == '/') {
709                                 /*
710                                  * C-style comments become a single space.
711                                  * We do this to ensure that a comment will
712                                  * act as a delimiter for strings and
713                                  * numbers.
714                                  */
715                                 c = ' ';
716                                 no_comments = ISC_FALSE;
717                                 state = saved_state;
718                                 goto no_read;
719                         } else if (c != '*')
720                                 state = lexstate_ccomment;
721                         break;
722                 case lexstate_eatline:
723                         if ((c == '\n') || (c == EOF)) {
724                                 no_comments = ISC_FALSE;
725                                 state = saved_state;
726                                 goto no_read;
727                         }
728                         break;
729                 case lexstate_qstring:
730                         if (c == EOF) {
731                                 result = ISC_R_UNEXPECTEDEND;
732                                 goto done;
733                         }
734                         if (c == '"') {
735                                 if (escaped) {
736                                         escaped = ISC_FALSE;
737                                         /*
738                                          * Overwrite the preceding backslash.
739                                          */
740                                         INSIST(prev != NULL);
741                                         *prev = '"';
742                                 } else {
743                                         tokenp->type = isc_tokentype_qstring;
744                                         tokenp->value.as_textregion.base =
745                                                 lex->data;
746                                         tokenp->value.as_textregion.length =
747                                                 lex->max_token - remaining;
748                                         no_comments = ISC_FALSE;
749                                         done = ISC_TRUE;
750                                 }
751                         } else {
752                                 if (c == '\n' && !escaped &&
753                             (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) {
754                                         pushback(source, c);
755                                         result = ISC_R_UNBALANCEDQUOTES;
756                                         goto done;
757                                 }
758                                 if (c == '\\' && !escaped)
759                                         escaped = ISC_TRUE;
760                                 else
761                                         escaped = ISC_FALSE;
762                                 if (remaining == 0U) {
763                                         result = grow_data(lex, &remaining,
764                                                            &curr, &prev);
765                                         if (result != ISC_R_SUCCESS)
766                                                 goto done;
767                                 }
768                                 INSIST(remaining > 0U);
769                                 prev = curr;
770                                 *curr++ = c;
771                                 *curr = '\0';
772                                 remaining--;
773                         }
774                         break;
775                 default:
776                         FATAL_ERROR(__FILE__, __LINE__,
777                                     isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX,
778                                                    ISC_MSG_UNEXPECTEDSTATE,
779                                                    "Unexpected state %d"),
780                                     state);
781                         /* Does not return. */
782                 }
783
784         } while (!done);
785
786         result = ISC_R_SUCCESS;
787  done:
788 #ifdef HAVE_FLOCKFILE
789         if (source->is_file)
790                 funlockfile(source->input);
791 #endif
792         return (result);
793 }
794
795 isc_result_t
796 isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
797                        isc_tokentype_t expect, isc_boolean_t eol)
798 {
799         unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
800                                ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
801         isc_result_t result;
802
803         if (expect == isc_tokentype_qstring)
804                 options |= ISC_LEXOPT_QSTRING;
805         else if (expect == isc_tokentype_number)
806                 options |= ISC_LEXOPT_NUMBER;
807         result = isc_lex_gettoken(lex, options, token);
808         if (result == ISC_R_RANGE)
809                 isc_lex_ungettoken(lex, token);
810         if (result != ISC_R_SUCCESS)
811                 return (result);
812
813         if (eol && ((token->type == isc_tokentype_eol) ||
814                     (token->type == isc_tokentype_eof)))
815                 return (ISC_R_SUCCESS);
816         if (token->type == isc_tokentype_string &&
817             expect == isc_tokentype_qstring)
818                 return (ISC_R_SUCCESS);
819         if (token->type != expect) {
820                 isc_lex_ungettoken(lex, token);
821                 if (token->type == isc_tokentype_eol ||
822                     token->type == isc_tokentype_eof)
823                         return (ISC_R_UNEXPECTEDEND);
824                 if (expect == isc_tokentype_number)
825                         return (ISC_R_BADNUMBER);
826                 return (ISC_R_UNEXPECTEDTOKEN);
827         }
828         return (ISC_R_SUCCESS);
829 }
830
831 isc_result_t
832 isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, isc_boolean_t eol)
833 {
834         unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
835                                ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE|
836                                ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
837         isc_result_t result;
838
839         result = isc_lex_gettoken(lex, options, token);
840         if (result == ISC_R_RANGE)
841                 isc_lex_ungettoken(lex, token);
842         if (result != ISC_R_SUCCESS)
843                 return (result);
844
845         if (eol && ((token->type == isc_tokentype_eol) ||
846                     (token->type == isc_tokentype_eof)))
847                 return (ISC_R_SUCCESS);
848         if (token->type != isc_tokentype_number) {
849                 isc_lex_ungettoken(lex, token);
850                 if (token->type == isc_tokentype_eol ||
851                     token->type == isc_tokentype_eof)
852                         return (ISC_R_UNEXPECTEDEND);
853                 return (ISC_R_BADNUMBER);
854         }
855         return (ISC_R_SUCCESS);
856 }
857
858 void
859 isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
860         inputsource *source;
861         /*
862          * Unget the current token.
863          */
864
865         REQUIRE(VALID_LEX(lex));
866         source = HEAD(lex->sources);
867         REQUIRE(source != NULL);
868         REQUIRE(tokenp != NULL);
869         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
870                 tokenp->type == isc_tokentype_eof);
871
872         UNUSED(tokenp);
873
874         isc_buffer_first(source->pushback);
875         lex->paren_count = lex->saved_paren_count;
876         source->line = source->saved_line;
877         source->at_eof = ISC_FALSE;
878 }
879
880 void
881 isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r)
882 {
883         inputsource *source;
884
885         REQUIRE(VALID_LEX(lex));
886         source = HEAD(lex->sources);
887         REQUIRE(source != NULL);
888         REQUIRE(tokenp != NULL);
889         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
890                 tokenp->type == isc_tokentype_eof);
891
892         UNUSED(tokenp);
893
894         INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
895         r->base = (unsigned char *)isc_buffer_base(source->pushback) +
896                   source->ignored;
897         r->length = isc_buffer_consumedlength(source->pushback) -
898                     source->ignored;
899 }
900
901
902 char *
903 isc_lex_getsourcename(isc_lex_t *lex) {
904         inputsource *source;
905
906         REQUIRE(VALID_LEX(lex));
907         source = HEAD(lex->sources);
908
909         if (source == NULL)
910                 return (NULL);
911
912         return (source->name);
913 }
914
915 unsigned long
916 isc_lex_getsourceline(isc_lex_t *lex) {
917         inputsource *source;
918
919         REQUIRE(VALID_LEX(lex));
920         source = HEAD(lex->sources);
921
922         if (source == NULL)
923                 return (0);
924
925         return (source->line);
926 }
927
928
929 isc_result_t
930 isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
931         inputsource *source;
932         char *newname;
933
934         REQUIRE(VALID_LEX(lex));
935         source = HEAD(lex->sources);
936
937         if (source == NULL)
938                 return(ISC_R_NOTFOUND);
939         newname = isc_mem_strdup(lex->mctx, name);
940         if (newname == NULL)
941                 return (ISC_R_NOMEMORY);
942         isc_mem_free(lex->mctx, source->name);
943         source->name = newname;
944         return (ISC_R_SUCCESS);
945 }
946
947 isc_boolean_t
948 isc_lex_isfile(isc_lex_t *lex) {
949         inputsource *source;
950
951         REQUIRE(VALID_LEX(lex));
952
953         source = HEAD(lex->sources);
954
955         if (source == NULL)
956                 return (ISC_FALSE);
957
958         return (source->is_file);
959 }