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