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