]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - contrib/bind9/lib/isc/lex.c
MFC: r253983-253984
[FreeBSD/stable/8.git] / contrib / bind9 / lib / isc / lex.c
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2013, 2014  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         memmove(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         memmove(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         memmove(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                                      (unsigned int)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 = *((unsigned 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                                                         (unsigned int)
619                                                         (lex->max_token -
620                                                          remaining);
621                                         } else
622                                                 goto done;
623                                         done = ISC_TRUE;
624                                         continue;
625                                 } else if (!(options & ISC_LEXOPT_CNUMBER) ||
626                                            ((c != 'x' && c != 'X') ||
627                                            (curr != &lex->data[1]) ||
628                                            (lex->data[0] != '0'))) {
629                                         /* Above test supports hex numbers */
630                                         state = lexstate_string;
631                                 }
632                         } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
633                                    (c == '8' || c == '9')) {
634                                 state = lexstate_string;
635                         }
636                         if (remaining == 0U) {
637                                 result = grow_data(lex, &remaining,
638                                                    &curr, &prev);
639                                 if (result != ISC_R_SUCCESS)
640                                         goto done;
641                         }
642                         INSIST(remaining > 0U);
643                         *curr++ = c;
644                         *curr = '\0';
645                         remaining--;
646                         break;
647                 case lexstate_string:
648                         /*
649                          * EOF needs to be checked before lex->specials[c]
650                          * as lex->specials[EOF] is not a good idea.
651                          */
652                         if (c == '\r' || c == '\n' || c == EOF ||
653                             (!escaped &&
654                              (c == ' ' || c == '\t' || lex->specials[c]))) {
655                                 pushback(source, c);
656                                 if (source->result != ISC_R_SUCCESS) {
657                                         result = source->result;
658                                         goto done;
659                                 }
660                                 tokenp->type = isc_tokentype_string;
661                                 tokenp->value.as_textregion.base = lex->data;
662                                 tokenp->value.as_textregion.length =
663                                         (unsigned int)
664                                         (lex->max_token - remaining);
665                                 done = ISC_TRUE;
666                                 continue;
667                         }
668                         if ((options & ISC_LEXOPT_ESCAPE) != 0)
669                                 escaped = (!escaped && c == '\\') ?
670                                                 ISC_TRUE : ISC_FALSE;
671                         if (remaining == 0U) {
672                                 result = grow_data(lex, &remaining,
673                                                    &curr, &prev);
674                                 if (result != ISC_R_SUCCESS)
675                                         goto done;
676                         }
677                         INSIST(remaining > 0U);
678                         *curr++ = c;
679                         *curr = '\0';
680                         remaining--;
681                         break;
682                 case lexstate_maybecomment:
683                         if (c == '*' &&
684                             (lex->comments & ISC_LEXCOMMENT_C) != 0) {
685                                 state = lexstate_ccomment;
686                                 continue;
687                         } else if (c == '/' &&
688                             (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) {
689                                 state = lexstate_eatline;
690                                 continue;
691                         }
692                         pushback(source, c);
693                         c = '/';
694                         no_comments = ISC_FALSE;
695                         state = saved_state;
696                         goto no_read;
697                 case lexstate_ccomment:
698                         if (c == EOF) {
699                                 result = ISC_R_UNEXPECTEDEND;
700                                 goto done;
701                         }
702                         if (c == '*')
703                                 state = lexstate_ccommentend;
704                         break;
705                 case lexstate_ccommentend:
706                         if (c == EOF) {
707                                 result = ISC_R_UNEXPECTEDEND;
708                                 goto done;
709                         }
710                         if (c == '/') {
711                                 /*
712                                  * C-style comments become a single space.
713                                  * We do this to ensure that a comment will
714                                  * act as a delimiter for strings and
715                                  * numbers.
716                                  */
717                                 c = ' ';
718                                 no_comments = ISC_FALSE;
719                                 state = saved_state;
720                                 goto no_read;
721                         } else if (c != '*')
722                                 state = lexstate_ccomment;
723                         break;
724                 case lexstate_eatline:
725                         if ((c == '\n') || (c == EOF)) {
726                                 no_comments = ISC_FALSE;
727                                 state = saved_state;
728                                 goto no_read;
729                         }
730                         break;
731                 case lexstate_qstring:
732                         if (c == EOF) {
733                                 result = ISC_R_UNEXPECTEDEND;
734                                 goto done;
735                         }
736                         if (c == '"') {
737                                 if (escaped) {
738                                         escaped = ISC_FALSE;
739                                         /*
740                                          * Overwrite the preceding backslash.
741                                          */
742                                         INSIST(prev != NULL);
743                                         *prev = '"';
744                                 } else {
745                                         tokenp->type = isc_tokentype_qstring;
746                                         tokenp->value.as_textregion.base =
747                                                 lex->data;
748                                         tokenp->value.as_textregion.length =
749                                                 (unsigned int)
750                                                 (lex->max_token - remaining);
751                                         no_comments = ISC_FALSE;
752                                         done = ISC_TRUE;
753                                 }
754                         } else {
755                                 if (c == '\n' && !escaped &&
756                             (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) {
757                                         pushback(source, c);
758                                         result = ISC_R_UNBALANCEDQUOTES;
759                                         goto done;
760                                 }
761                                 if (c == '\\' && !escaped)
762                                         escaped = ISC_TRUE;
763                                 else
764                                         escaped = ISC_FALSE;
765                                 if (remaining == 0U) {
766                                         result = grow_data(lex, &remaining,
767                                                            &curr, &prev);
768                                         if (result != ISC_R_SUCCESS)
769                                                 goto done;
770                                 }
771                                 INSIST(remaining > 0U);
772                                 prev = curr;
773                                 *curr++ = c;
774                                 *curr = '\0';
775                                 remaining--;
776                         }
777                         break;
778                 default:
779                         FATAL_ERROR(__FILE__, __LINE__,
780                                     isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX,
781                                                    ISC_MSG_UNEXPECTEDSTATE,
782                                                    "Unexpected state %d"),
783                                     state);
784                         /* Does not return. */
785                 }
786
787         } while (!done);
788
789         result = ISC_R_SUCCESS;
790  done:
791 #ifdef HAVE_FLOCKFILE
792         if (source->is_file)
793                 funlockfile(source->input);
794 #endif
795         return (result);
796 }
797
798 isc_result_t
799 isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
800                        isc_tokentype_t expect, isc_boolean_t eol)
801 {
802         unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
803                                ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
804         isc_result_t result;
805
806         if (expect == isc_tokentype_qstring)
807                 options |= ISC_LEXOPT_QSTRING;
808         else if (expect == isc_tokentype_number)
809                 options |= ISC_LEXOPT_NUMBER;
810         result = isc_lex_gettoken(lex, options, token);
811         if (result == ISC_R_RANGE)
812                 isc_lex_ungettoken(lex, token);
813         if (result != ISC_R_SUCCESS)
814                 return (result);
815
816         if (eol && ((token->type == isc_tokentype_eol) ||
817                     (token->type == isc_tokentype_eof)))
818                 return (ISC_R_SUCCESS);
819         if (token->type == isc_tokentype_string &&
820             expect == isc_tokentype_qstring)
821                 return (ISC_R_SUCCESS);
822         if (token->type != expect) {
823                 isc_lex_ungettoken(lex, token);
824                 if (token->type == isc_tokentype_eol ||
825                     token->type == isc_tokentype_eof)
826                         return (ISC_R_UNEXPECTEDEND);
827                 if (expect == isc_tokentype_number)
828                         return (ISC_R_BADNUMBER);
829                 return (ISC_R_UNEXPECTEDTOKEN);
830         }
831         return (ISC_R_SUCCESS);
832 }
833
834 isc_result_t
835 isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, isc_boolean_t eol)
836 {
837         unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
838                                ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE|
839                                ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
840         isc_result_t result;
841
842         result = isc_lex_gettoken(lex, options, token);
843         if (result == ISC_R_RANGE)
844                 isc_lex_ungettoken(lex, token);
845         if (result != ISC_R_SUCCESS)
846                 return (result);
847
848         if (eol && ((token->type == isc_tokentype_eol) ||
849                     (token->type == isc_tokentype_eof)))
850                 return (ISC_R_SUCCESS);
851         if (token->type != isc_tokentype_number) {
852                 isc_lex_ungettoken(lex, token);
853                 if (token->type == isc_tokentype_eol ||
854                     token->type == isc_tokentype_eof)
855                         return (ISC_R_UNEXPECTEDEND);
856                 return (ISC_R_BADNUMBER);
857         }
858         return (ISC_R_SUCCESS);
859 }
860
861 void
862 isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
863         inputsource *source;
864         /*
865          * Unget the current token.
866          */
867
868         REQUIRE(VALID_LEX(lex));
869         source = HEAD(lex->sources);
870         REQUIRE(source != NULL);
871         REQUIRE(tokenp != NULL);
872         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
873                 tokenp->type == isc_tokentype_eof);
874
875         UNUSED(tokenp);
876
877         isc_buffer_first(source->pushback);
878         lex->paren_count = lex->saved_paren_count;
879         source->line = source->saved_line;
880         source->at_eof = ISC_FALSE;
881 }
882
883 void
884 isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r)
885 {
886         inputsource *source;
887
888         REQUIRE(VALID_LEX(lex));
889         source = HEAD(lex->sources);
890         REQUIRE(source != NULL);
891         REQUIRE(tokenp != NULL);
892         REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
893                 tokenp->type == isc_tokentype_eof);
894
895         UNUSED(tokenp);
896
897         INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
898         r->base = (unsigned char *)isc_buffer_base(source->pushback) +
899                   source->ignored;
900         r->length = isc_buffer_consumedlength(source->pushback) -
901                     source->ignored;
902 }
903
904
905 char *
906 isc_lex_getsourcename(isc_lex_t *lex) {
907         inputsource *source;
908
909         REQUIRE(VALID_LEX(lex));
910         source = HEAD(lex->sources);
911
912         if (source == NULL)
913                 return (NULL);
914
915         return (source->name);
916 }
917
918 unsigned long
919 isc_lex_getsourceline(isc_lex_t *lex) {
920         inputsource *source;
921
922         REQUIRE(VALID_LEX(lex));
923         source = HEAD(lex->sources);
924
925         if (source == NULL)
926                 return (0);
927
928         return (source->line);
929 }
930
931
932 isc_result_t
933 isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
934         inputsource *source;
935         char *newname;
936
937         REQUIRE(VALID_LEX(lex));
938         source = HEAD(lex->sources);
939
940         if (source == NULL)
941                 return(ISC_R_NOTFOUND);
942         newname = isc_mem_strdup(lex->mctx, name);
943         if (newname == NULL)
944                 return (ISC_R_NOMEMORY);
945         isc_mem_free(lex->mctx, source->name);
946         source->name = newname;
947         return (ISC_R_SUCCESS);
948 }
949
950 isc_boolean_t
951 isc_lex_isfile(isc_lex_t *lex) {
952         inputsource *source;
953
954         REQUIRE(VALID_LEX(lex));
955
956         source = HEAD(lex->sources);
957
958         if (source == NULL)
959                 return (ISC_FALSE);
960
961         return (source->is_file);
962 }