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
grow_data(isc_lex_t * lex,size_t * remainingp,char ** currp,char ** prevp)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
isc_lex_create(isc_mem_t * mctx,size_t max_token,isc_lex_t ** lexp)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
isc_lex_destroy(isc_lex_t ** lexp)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
isc_lex_getcomments(isc_lex_t * lex)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
isc_lex_setcomments(isc_lex_t * lex,unsigned int comments)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
isc_lex_getspecials(isc_lex_t * lex,isc_lexspecials_t specials)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
isc_lex_setspecials(isc_lex_t * lex,isc_lexspecials_t specials)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
new_source(isc_lex_t * lex,isc_boolean_t is_file,isc_boolean_t need_close,void * input,const char * name)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
isc_lex_openfile(isc_lex_t * lex,const char * filename)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
isc_lex_openstream(isc_lex_t * lex,FILE * stream)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
isc_lex_openbuffer(isc_lex_t * lex,isc_buffer_t * buffer)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
isc_lex_close(isc_lex_t * lex)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
pushback(inputsource * source,int c)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
pushandgrow(isc_lex_t * lex,inputsource * source,int c)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
isc_lex_gettoken(isc_lex_t * lex,unsigned int options,isc_token_t * tokenp)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
isc_lex_getmastertoken(isc_lex_t * lex,isc_token_t * token,isc_tokentype_t expect,isc_boolean_t eol)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
isc_lex_getoctaltoken(isc_lex_t * lex,isc_token_t * token,isc_boolean_t eol)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
isc_lex_ungettoken(isc_lex_t * lex,isc_token_t * tokenp)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
isc_lex_getlasttokentext(isc_lex_t * lex,isc_token_t * tokenp,isc_region_t * r)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 *
isc_lex_getsourcename(isc_lex_t * lex)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
isc_lex_getsourceline(isc_lex_t * lex)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
isc_lex_setsourcename(isc_lex_t * lex,const char * name)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
isc_lex_isfile(isc_lex_t * lex)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 }
960