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