1 /* $OpenBSD: comp_scan.c,v 1.16 2023/10/17 09:52:09 nicm Exp $ */ 2 3 /**************************************************************************** 4 ,* Copyright 2020-2022,2023 Thomas E. Dickey * 5 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 6 * * 7 * Permission is hereby granted, free of charge, to any person obtaining a * 8 * copy of this software and associated documentation files (the * 9 * "Software"), to deal in the Software without restriction, including * 10 * without limitation the rights to use, copy, modify, merge, publish, * 11 * distribute, distribute with modifications, sublicense, and/or sell * 12 * copies of the Software, and to permit persons to whom the Software is * 13 * furnished to do so, subject to the following conditions: * 14 * * 15 * The above copyright notice and this permission notice shall be included * 16 * in all copies or substantial portions of the Software. * 17 * * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 25 * * 26 * Except as contained in this notice, the name(s) of the above copyright * 27 * holders shall not be used in advertising or otherwise to promote the * 28 * sale, use or other dealings in this Software without prior written * 29 * authorization. * 30 ****************************************************************************/ 31 32 /**************************************************************************** 33 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 34 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 35 * and: Thomas E. Dickey 1996 on * 36 ****************************************************************************/ 37 38 /* 39 * comp_scan.c --- Lexical scanner for terminfo compiler. 40 * 41 * _nc_reset_input() 42 * _nc_get_token() 43 * _nc_panic_mode() 44 * int _nc_syntax; 45 * int _nc_curr_line; 46 * long _nc_curr_file_pos; 47 * long _nc_comment_start; 48 * long _nc_comment_end; 49 */ 50 51 #include <curses.priv.h> 52 53 #include <ctype.h> 54 #include <tic.h> 55 56 MODULE_ID("$Id: comp_scan.c,v 1.16 2023/10/17 09:52:09 nicm Exp $") 57 58 /* 59 * Maximum length of string capability we'll accept before raising an error. 60 * Yes, there is a real capability in /etc/termcap this long, an "is". 61 */ 62 #define MAXCAPLEN 600 63 64 #define iswhite(ch) (ch == ' ' || ch == '\t') 65 66 NCURSES_EXPORT_VAR (int) _nc_syntax = 0; /* termcap or terminfo? */ 67 NCURSES_EXPORT_VAR (int) _nc_strict_bsd = 1; /* ncurses extended termcap? */ 68 NCURSES_EXPORT_VAR (long) _nc_curr_file_pos = 0; /* file offset of current line */ 69 NCURSES_EXPORT_VAR (long) _nc_comment_start = 0; /* start of comment range before name */ 70 NCURSES_EXPORT_VAR (long) _nc_comment_end = 0; /* end of comment range before name */ 71 NCURSES_EXPORT_VAR (long) _nc_start_line = 0; /* start line of current entry */ 72 73 NCURSES_EXPORT_VAR (struct token) _nc_curr_token = 74 { 75 0, 0, 0 76 }; 77 78 /***************************************************************************** 79 * 80 * Token-grabbing machinery 81 * 82 *****************************************************************************/ 83 84 static bool first_column; /* See 'next_char()' below */ 85 static bool had_newline; 86 static char separator; /* capability separator */ 87 static int pushtype; /* type of pushback token */ 88 static char *pushname; 89 90 #if NCURSES_EXT_FUNCS 91 NCURSES_EXPORT_VAR (bool) _nc_disable_period = FALSE; /* used by tic -a option */ 92 #endif 93 94 /***************************************************************************** 95 * 96 * Character-stream handling 97 * 98 *****************************************************************************/ 99 100 #define LEXBUFSIZ 1024 101 102 static char *bufptr; /* otherwise, the input buffer pointer */ 103 static char *bufstart; /* start of buffer so we can compute offsets */ 104 static FILE *yyin; /* scanner's input file descriptor */ 105 106 /* 107 * _nc_reset_input() 108 * 109 * Resets the input-reading routines. Used on initialization, 110 * or after a seek has been done. Exactly one argument must be 111 * non-null. 112 */ 113 114 NCURSES_EXPORT(void) 115 _nc_reset_input(FILE *fp, char *buf) 116 { 117 TR(TRACE_DATABASE, 118 (T_CALLED("_nc_reset_input(fp=%p, buf=%p)"), (void *) fp, buf)); 119 120 pushtype = NO_PUSHBACK; 121 if (pushname != 0) 122 pushname[0] = '\0'; 123 yyin = fp; 124 bufstart = bufptr = buf; 125 _nc_curr_file_pos = 0L; 126 if (fp != 0) 127 _nc_curr_line = 0; 128 _nc_curr_col = 0; 129 130 returnVoidDB; 131 } 132 133 /* 134 * int last_char() 135 * 136 * Returns the final nonblank character on the current input buffer 137 */ 138 static int 139 last_char(int from_end) 140 { 141 size_t len = strlen(bufptr); 142 int result = 0; 143 144 while (len--) { 145 if (!isspace(UChar(bufptr[len]))) { 146 if (from_end <= (int) len) 147 result = bufptr[(int) len - from_end]; 148 break; 149 } 150 } 151 return result; 152 } 153 154 /* 155 * Read, like fgets(), but error-out if the input contains nulls. 156 */ 157 static int 158 get_text(char *buffer, int length) 159 { 160 int count = 0; 161 int limit = length - 1; 162 163 while (limit-- > 0) { 164 int ch = fgetc(yyin); 165 166 if (ch == '\0') { 167 _nc_err_abort("This is not a text-file"); 168 } else if (ch == EOF) { 169 break; 170 } 171 ++count; 172 *buffer++ = (char) ch; 173 if (ch == '\n') 174 break; 175 } 176 *buffer = '\0'; 177 return count; 178 } 179 180 /* 181 * int next_char() 182 * 183 * Returns the next character in the input stream. Comments and leading 184 * white space are stripped. 185 * 186 * The global state variable 'firstcolumn' is set TRUE if the character 187 * returned is from the first column of the input line. 188 * 189 * The global variable _nc_curr_line is incremented for each new line. 190 * The global variable _nc_curr_file_pos is set to the file offset of the 191 * beginning of each line. 192 */ 193 194 static int 195 next_char(void) 196 { 197 static char *result; 198 static size_t allocated; 199 int the_char; 200 201 if (!yyin) { 202 if (result != 0) { 203 FreeAndNull(result); 204 FreeAndNull(pushname); 205 bufptr = 0; 206 bufstart = 0; 207 allocated = 0; 208 } 209 /* 210 * An string with an embedded null will truncate the input. This is 211 * intentional (we don't read binary files here). 212 */ 213 if (bufptr == 0 || *bufptr == '\0') 214 return (EOF); 215 if (*bufptr == '\n') { 216 _nc_curr_line++; 217 _nc_curr_col = 0; 218 } else if (*bufptr == '\t') { 219 _nc_curr_col = (_nc_curr_col | 7); 220 } 221 } else if (!bufptr || !*bufptr) { 222 /* 223 * In theory this could be recoded to do its I/O one character at a 224 * time, saving the buffer space. In practice, this turns out to be 225 * quite hard to get completely right. Try it and see. If you 226 * succeed, don't forget to hack push_back() correspondingly. 227 */ 228 size_t len; 229 230 do { 231 size_t used = 0; 232 bufstart = 0; 233 do { 234 if (used + (LEXBUFSIZ / 4) >= allocated) { 235 allocated += (allocated + LEXBUFSIZ); 236 result = typeRealloc(char, allocated, result); 237 if (result == 0) 238 return (EOF); 239 if (bufstart) 240 bufstart = result; 241 } 242 if (used == 0) 243 _nc_curr_file_pos = ftell(yyin); 244 245 if (get_text(result + used, (int) (allocated - used))) { 246 bufstart = result; 247 if (used == 0) { 248 if (_nc_curr_line == 0 249 && IS_TIC_MAGIC(result)) { 250 _nc_err_abort("This is a compiled terminal description, not a source"); 251 } 252 _nc_curr_line++; 253 _nc_curr_col = 0; 254 } 255 } else { 256 if (used != 0) 257 _nc_STRCAT(result, "\n", allocated); 258 } 259 if ((bufptr = bufstart) != 0) { 260 used = strlen(bufptr); 261 if (used == 0) 262 return (EOF); 263 while (iswhite(*bufptr)) { 264 if (*bufptr == '\t') { 265 _nc_curr_col = (_nc_curr_col | 7) + 1; 266 } else { 267 _nc_curr_col++; 268 } 269 bufptr++; 270 } 271 272 /* 273 * Treat a trailing <cr><lf> the same as a <newline> so we 274 * can read files on OS/2, etc. 275 */ 276 if ((len = strlen(bufptr)) > 1) { 277 if (bufptr[len - 1] == '\n' 278 && bufptr[len - 2] == '\r') { 279 len--; 280 bufptr[len - 1] = '\n'; 281 bufptr[len] = '\0'; 282 } 283 } 284 } else { 285 return (EOF); 286 } 287 } while (bufptr[len - 1] != '\n'); /* complete a line */ 288 } while (result[0] == '#'); /* ignore comments */ 289 } else if (*bufptr == '\t') { 290 _nc_curr_col = (_nc_curr_col | 7); 291 } 292 293 first_column = (bufptr == bufstart); 294 if (first_column) 295 had_newline = FALSE; 296 297 _nc_curr_col++; 298 the_char = *bufptr++; 299 return UChar(the_char); 300 } 301 302 static void 303 push_back(int c) 304 /* push a character back onto the input stream */ 305 { 306 if (bufptr == bufstart) 307 _nc_syserr_abort("cannot backspace off beginning of line"); 308 *--bufptr = (char) c; 309 _nc_curr_col--; 310 } 311 312 static long 313 stream_pos(void) 314 /* return our current character position in the input stream */ 315 { 316 return (yyin ? ftell(yyin) : (bufptr ? (long) (bufptr - bufstart) : 0)); 317 } 318 319 static bool 320 end_of_stream(void) 321 /* are we at end of input? */ 322 { 323 return ((yyin 324 ? (feof(yyin) && (bufptr == NULL || *bufptr == '\0')) 325 : (bufptr && *bufptr == '\0')) 326 ? TRUE : FALSE); 327 } 328 329 /* Assume we may be looking at a termcap-style continuation */ 330 static NCURSES_INLINE int 331 eat_escaped_newline(int ch) 332 { 333 if (ch == '\\') { 334 while ((ch = next_char()) == '\n' || iswhite(ch)) { 335 /* EMPTY */ ; 336 } 337 } 338 return ch; 339 } 340 341 #define TOK_BUF_SIZE MAX_ENTRY_SIZE 342 343 #define OkToAdd() \ 344 ((tok_ptr - tok_buf) < (TOK_BUF_SIZE - 2)) 345 346 #define AddCh(ch) \ 347 *tok_ptr++ = (char) ch; \ 348 *tok_ptr = '\0' 349 350 static char *tok_buf; 351 352 /* 353 * int 354 * get_token() 355 * 356 * Scans the input for the next token, storing the specifics in the 357 * global structure 'curr_token' and returning one of the following: 358 * 359 * NAMES A line beginning in column 1. 'name' 360 * will be set to point to everything up to but 361 * not including the first separator on the line. 362 * BOOLEAN An entry consisting of a name followed by 363 * a separator. 'name' will be set to point to 364 * the name of the capability. 365 * NUMBER An entry of the form 366 * name#digits, 367 * 'name' will be set to point to the capability 368 * name and 'valnumber' to the number given. 369 * STRING An entry of the form 370 * name=characters, 371 * 'name' is set to the capability name and 372 * 'valstring' to the string of characters, with 373 * input translations done. 374 * CANCEL An entry of the form 375 * name@, 376 * 'name' is set to the capability name and 377 * 'valnumber' to -1. 378 * EOF The end of the file has been reached. 379 * 380 * A `separator' is either a comma or a semicolon, depending on whether 381 * we are in termcap or terminfo mode. 382 * 383 */ 384 385 NCURSES_EXPORT(int) 386 _nc_get_token(bool silent) 387 { 388 static const char terminfo_punct[] = "@%&*!#"; 389 390 char *after_name; /* after primary name */ 391 char *after_list; /* after primary and alias list */ 392 char *numchk; 393 char *tok_ptr; 394 char *s; 395 char numbuf[80]; 396 int ch, c0, c1; 397 int dot_flag = FALSE; 398 int type; 399 long number; 400 long token_start; 401 unsigned found; 402 #ifdef TRACE 403 int old_line; 404 int old_col; 405 #endif 406 407 DEBUG(3, (T_CALLED("_nc_get_token(silent=%d)"), silent)); 408 409 if (pushtype != NO_PUSHBACK) { 410 int retval = pushtype; 411 412 _nc_set_type(pushname != 0 ? pushname : ""); 413 DEBUG(3, ("pushed-back token: `%s', class %d", 414 _nc_curr_token.tk_name, pushtype)); 415 416 pushtype = NO_PUSHBACK; 417 if (pushname != 0) 418 pushname[0] = '\0'; 419 420 /* currtok wasn't altered by _nc_push_token() */ 421 DEBUG(3, (T_RETURN("%d"), retval)); 422 return (retval); 423 } 424 425 if (end_of_stream()) { 426 yyin = 0; 427 (void) next_char(); /* frees its allocated memory */ 428 if (tok_buf != 0) { 429 if (_nc_curr_token.tk_name == tok_buf) 430 _nc_curr_token.tk_name = 0; 431 } 432 DEBUG(3, (T_RETURN("%d"), EOF)); 433 return (EOF); 434 } 435 436 start_token: 437 token_start = stream_pos(); 438 while ((ch = next_char()) == '\n' || iswhite(ch)) { 439 if (ch == '\n') 440 had_newline = TRUE; 441 } 442 443 ch = eat_escaped_newline(ch); 444 _nc_curr_token.tk_valstring = 0; 445 446 #ifdef TRACE 447 old_line = _nc_curr_line; 448 old_col = _nc_curr_col; 449 #endif 450 if (ch == EOF) 451 type = EOF; 452 else { 453 /* if this is a termcap entry, skip a leading separator */ 454 if (separator == ':' && ch == ':') 455 ch = next_char(); 456 457 if (ch == '.' 458 #if NCURSES_EXT_FUNCS 459 && !_nc_disable_period 460 #endif 461 ) { 462 dot_flag = TRUE; 463 DEBUG(8, ("dot-flag set")); 464 465 while ((ch = next_char()) == '.' || iswhite(ch)) { 466 /* EMPTY */ ; 467 } 468 } 469 470 if (ch == EOF) { 471 type = EOF; 472 goto end_of_token; 473 } 474 475 /* have to make some punctuation chars legal for terminfo */ 476 if (!isalnum(UChar(ch)) 477 #if NCURSES_EXT_FUNCS 478 && !(ch == '.' && _nc_disable_period) 479 #endif 480 && ((strchr) (terminfo_punct, (char) ch) == 0)) { 481 if (!silent) 482 _nc_warning("Illegal character (expected alphanumeric or %s) - '%s'", 483 terminfo_punct, unctrl(UChar(ch))); 484 _nc_panic_mode(separator); 485 goto start_token; 486 } 487 488 if (tok_buf == 0) 489 tok_buf = typeMalloc(char, TOK_BUF_SIZE); 490 491 #ifdef TRACE 492 old_line = _nc_curr_line; 493 old_col = _nc_curr_col; 494 #endif 495 tok_ptr = tok_buf; 496 AddCh(ch); 497 498 if (first_column) { 499 _nc_comment_start = token_start; 500 _nc_comment_end = _nc_curr_file_pos; 501 _nc_start_line = _nc_curr_line; 502 503 _nc_syntax = ERR; 504 after_name = 0; 505 after_list = 0; 506 while ((ch = next_char()) != '\n') { 507 if (ch == EOF) { 508 _nc_err_abort(MSG_NO_INPUTS); 509 } else if (ch == '|') { 510 after_list = tok_ptr; 511 if (after_name == 0) 512 after_name = tok_ptr; 513 } else if (ch == ':' && last_char(0) != ',') { 514 _nc_syntax = SYN_TERMCAP; 515 separator = ':'; 516 break; 517 } else if (ch == ',') { 518 _nc_syntax = SYN_TERMINFO; 519 separator = ','; 520 /* 521 * If we did not see a '|', then we found a name with no 522 * aliases or description. 523 */ 524 if (after_name == 0) 525 break; 526 /* 527 * We saw a comma, but are not entirely sure this is 528 * terminfo format, since we can still be parsing the 529 * description field (for either syntax). 530 * 531 * A properly formatted termcap line ends with either a 532 * colon, or a backslash after a colon. It is possible 533 * to have a backslash in the middle of a capability, but 534 * then there would be no leading whitespace on the next 535 * line - something we want to discourage. 536 */ 537 c0 = last_char(0); 538 c1 = last_char(1); 539 if (c1 != ':' && c0 != '\\' && c0 != ':') { 540 bool capability = FALSE; 541 542 /* 543 * Since it is not termcap, assume the line is terminfo 544 * format. However, the comma can be embedded in a 545 * description field. It also can be a separator 546 * between a description field and a capability. 547 * 548 * Improve the guess by checking if the next word after 549 * the comma does not look like a capability. In that 550 * case, extend the description past the comma. 551 */ 552 for (s = bufptr; isspace(UChar(*s)); ++s) { 553 ; 554 } 555 if (islower(UChar(*s))) { 556 char *name = s; 557 while (isalnum(UChar(*s))) { 558 ++s; 559 } 560 if (*s == '#' || *s == '=' || *s == '@') { 561 /* 562 * Checking solely with syntax allows us to 563 * support extended capabilities with string 564 * values. 565 */ 566 capability = TRUE; 567 } else if (*s == ',') { 568 c0 = *s; 569 *s = '\0'; 570 /* 571 * Otherwise, we can handle predefined boolean 572 * capabilities, still aided by syntax. 573 */ 574 if (_nc_find_entry(name, 575 _nc_get_hash_table(FALSE))) { 576 capability = TRUE; 577 } 578 *s = (char) c0; 579 } 580 } 581 if (capability) { 582 break; 583 } 584 } 585 } else 586 ch = eat_escaped_newline(ch); 587 588 if (OkToAdd()) { 589 AddCh(ch); 590 } else { 591 break; 592 } 593 } 594 *tok_ptr = '\0'; 595 if (_nc_syntax == ERR) { 596 /* 597 * Grrr...what we ought to do here is barf, complaining that 598 * the entry is malformed. But because a couple of name fields 599 * in the 8.2 termcap file end with |\, we just have to assume 600 * it is termcap syntax. 601 */ 602 _nc_syntax = SYN_TERMCAP; 603 separator = ':'; 604 } else if (_nc_syntax == SYN_TERMINFO) { 605 /* throw away trailing /, *$/ */ 606 for (--tok_ptr; 607 iswhite(*tok_ptr) || *tok_ptr == ','; 608 tok_ptr--) { 609 /* EMPTY */ ; 610 } 611 tok_ptr[1] = '\0'; 612 } 613 614 /* 615 * This is the soonest we have the terminal name fetched. Set up 616 * for following warning messages. If there's no '|', then there 617 * is no description. 618 */ 619 if (after_name != 0) { 620 ch = *after_name; 621 *after_name = '\0'; 622 _nc_set_type(tok_buf); 623 *after_name = (char) ch; 624 } 625 626 /* 627 * Compute the boundary between the aliases and the description 628 * field for syntax-checking purposes. 629 */ 630 if (after_list != 0) { 631 if (!silent) { 632 if (*after_list == '\0' || strchr("|", after_list[1]) != NULL) { 633 _nc_warning("empty longname field"); 634 } else if (strchr(after_list, ' ') == 0) { 635 _nc_warning("older tic versions may treat the description field as an alias"); 636 } 637 } 638 } else { 639 after_list = tok_buf + strlen(tok_buf); 640 DEBUG(2, ("missing description")); 641 } 642 643 /* 644 * Whitespace in a name field other than the long name can confuse 645 * rdist and some termcap tools. Slashes are a no-no. Other 646 * special characters can be dangerous due to shell expansion. 647 */ 648 for (s = tok_buf; s < after_list; ++s) { 649 if (isspace(UChar(*s))) { 650 if (!silent) 651 _nc_warning("whitespace in name or alias field"); 652 break; 653 } else if (*s == '/') { 654 if (!silent) 655 _nc_warning("slashes aren't allowed in names or aliases"); 656 break; 657 } else if (strchr("$[]!*?", *s)) { 658 if (!silent) 659 _nc_warning("dubious character `%c' in name or alias field", *s); 660 break; 661 } 662 } 663 664 _nc_curr_token.tk_name = tok_buf; 665 type = NAMES; 666 } else { 667 if (had_newline && _nc_syntax == SYN_TERMCAP) { 668 _nc_warning("Missing backslash before newline"); 669 had_newline = FALSE; 670 } 671 while ((ch = next_char()) != EOF) { 672 if (!isalnum(UChar(ch))) { 673 if (_nc_syntax == SYN_TERMINFO) { 674 if (ch != '_') 675 break; 676 } else { /* allow ';' for "k;" */ 677 if (ch != ';') 678 break; 679 } 680 } 681 if (OkToAdd()) { 682 AddCh(ch); 683 } else { 684 ch = EOF; 685 break; 686 } 687 } 688 689 *tok_ptr++ = '\0'; /* separate name/value in buffer */ 690 switch (ch) { 691 case ',': 692 case ':': 693 if (ch != separator) 694 _nc_err_abort("Separator inconsistent with syntax"); 695 _nc_curr_token.tk_name = tok_buf; 696 type = BOOLEAN; 697 break; 698 case '@': 699 if ((ch = next_char()) != separator && !silent) 700 _nc_warning("Missing separator after `%s', have %s", 701 tok_buf, unctrl(UChar(ch))); 702 _nc_curr_token.tk_name = tok_buf; 703 type = CANCEL; 704 break; 705 706 case '#': 707 found = 0; 708 while (isalnum(ch = next_char())) { 709 numbuf[found++] = (char) ch; 710 if (found >= sizeof(numbuf) - 1) 711 break; 712 } 713 numbuf[found] = '\0'; 714 number = strtol(numbuf, &numchk, 0); 715 if (!silent) { 716 if (numchk == numbuf) 717 _nc_warning("no value given for `%s'", tok_buf); 718 if ((*numchk != '\0') || (ch != separator)) 719 _nc_warning("Missing separator for `%s'", tok_buf); 720 if (number < 0) 721 _nc_warning("value of `%s' cannot be negative", tok_buf); 722 if (number > MAX_OF_TYPE(NCURSES_INT2)) { 723 _nc_warning("limiting value of `%s' from %#lx to %#x", 724 tok_buf, 725 number, MAX_OF_TYPE(NCURSES_INT2)); 726 number = MAX_OF_TYPE(NCURSES_INT2); 727 } 728 } 729 _nc_curr_token.tk_name = tok_buf; 730 _nc_curr_token.tk_valnumber = (int) number; 731 type = NUMBER; 732 break; 733 734 case '=': 735 ch = _nc_trans_string(tok_ptr, tok_buf + TOK_BUF_SIZE); 736 if (!silent && ch != separator) 737 _nc_warning("Missing separator"); 738 _nc_curr_token.tk_name = tok_buf; 739 _nc_curr_token.tk_valstring = tok_ptr; 740 type = STRING; 741 break; 742 743 case EOF: 744 type = EOF; 745 break; 746 default: 747 /* just to get rid of the compiler warning */ 748 type = UNDEF; 749 if (!silent) 750 _nc_warning("Illegal character - '%s'", unctrl(UChar(ch))); 751 } 752 } /* end else (first_column == FALSE) */ 753 } /* end else (ch != EOF) */ 754 755 end_of_token: 756 757 #ifdef TRACE 758 if (dot_flag == TRUE) 759 DEBUG(8, ("Commented out ")); 760 761 if (_nc_tracing >= DEBUG_LEVEL(8)) { 762 _tracef("parsed %d.%d to %d.%d", 763 old_line, old_col, 764 _nc_curr_line, _nc_curr_col); 765 } 766 if (_nc_tracing >= DEBUG_LEVEL(7)) { 767 switch (type) { 768 case BOOLEAN: 769 _tracef("Token: Boolean; name='%s'", 770 _nc_curr_token.tk_name); 771 break; 772 773 case NUMBER: 774 _tracef("Token: Number; name='%s', value=%d", 775 _nc_curr_token.tk_name, 776 _nc_curr_token.tk_valnumber); 777 break; 778 779 case STRING: 780 _tracef("Token: String; name='%s', value=%s", 781 _nc_curr_token.tk_name, 782 _nc_visbuf(_nc_curr_token.tk_valstring)); 783 break; 784 785 case CANCEL: 786 _tracef("Token: Cancel; name='%s'", 787 _nc_curr_token.tk_name); 788 break; 789 790 case NAMES: 791 792 _tracef("Token: Names; value='%s'", 793 _nc_curr_token.tk_name); 794 break; 795 796 case EOF: 797 _tracef("Token: End of file"); 798 break; 799 800 default: 801 _nc_warning("Bad token type"); 802 } 803 } 804 #endif 805 806 if (dot_flag == TRUE) /* if commented out, use the next one */ 807 type = _nc_get_token(silent); 808 809 DEBUG(3, ("token: `%s', class %d", 810 ((_nc_curr_token.tk_name != 0) 811 ? _nc_curr_token.tk_name 812 : "<null>"), 813 type)); 814 815 DEBUG(3, (T_RETURN("%d"), type)); 816 return (type); 817 } 818 819 /* 820 * char 821 * trans_string(ptr) 822 * 823 * Reads characters using next_char() until encountering a separator, nl, 824 * or end-of-file. The returned value is the character which caused 825 * reading to stop. The following translations are done on the input: 826 * 827 * ^X goes to ctrl-X (i.e. X & 037) 828 * {\E,\n,\r,\b,\t,\f} go to 829 * {ESCAPE,newline,carriage-return,backspace,tab,formfeed} 830 * {\^,\\} go to {carat,backslash} 831 * \ddd (for ddd = up to three octal digits) goes to the character ddd 832 * 833 * \e == \E 834 * \0 == \200 835 * 836 */ 837 838 NCURSES_EXPORT(int) 839 _nc_trans_string(char *ptr, const char *const last) 840 { 841 int count = 0; 842 int number = 0; 843 int i, c; 844 int last_ch = '\0'; 845 bool ignored = FALSE; 846 bool long_warning = FALSE; 847 848 while ((c = next_char()) != separator && c != EOF) { 849 if (ptr >= (last - 1)) { 850 if (c != EOF) { 851 while ((c = next_char()) != separator && c != EOF) { 852 ; 853 } 854 } 855 break; 856 } 857 if ((_nc_syntax == SYN_TERMCAP) && c == '\n') 858 break; 859 if (c == '^' && last_ch != '%') { 860 c = next_char(); 861 if (c == EOF) 862 _nc_err_abort(MSG_NO_INPUTS); 863 864 if (!(is7bits(c) && isprint(c))) { 865 _nc_warning("Illegal ^ character - '%s'", unctrl(UChar(c))); 866 } 867 if (c == '?' && (_nc_syntax != SYN_TERMCAP)) { 868 *(ptr++) = '\177'; 869 } else { 870 if ((c &= 037) == 0) 871 c = 128; 872 *(ptr++) = (char) (c); 873 } 874 } else if (c == '\\') { 875 bool strict_bsd = ((_nc_syntax == SYN_TERMCAP) && _nc_strict_bsd); 876 877 c = next_char(); 878 if (c == EOF) 879 _nc_err_abort(MSG_NO_INPUTS); 880 881 if (isoctal(c) || (strict_bsd && isdigit(c))) { 882 number = c - '0'; 883 for (i = 0; i < 2; i++) { 884 c = next_char(); 885 if (c == EOF) 886 _nc_err_abort(MSG_NO_INPUTS); 887 888 if (!isoctal(c)) { 889 if (isdigit(c)) { 890 if (!strict_bsd) { 891 _nc_warning("Non-octal digit `%c' in \\ sequence", c); 892 /* allow the digit; it'll do less harm */ 893 } 894 } else { 895 push_back(c); 896 break; 897 } 898 } 899 900 number = number * 8 + c - '0'; 901 } 902 903 number = UChar(number); 904 if (number == 0 && !strict_bsd) 905 number = 0200; 906 *(ptr++) = (char) number; 907 } else { 908 switch (c) { 909 case 'E': 910 *(ptr++) = '\033'; 911 break; 912 913 case 'n': 914 *(ptr++) = '\n'; 915 break; 916 917 case 'r': 918 *(ptr++) = '\r'; 919 break; 920 921 case 'b': 922 *(ptr++) = '\010'; 923 break; 924 925 case 'f': 926 *(ptr++) = '\014'; 927 break; 928 929 case 't': 930 *(ptr++) = '\t'; 931 break; 932 933 case '\\': 934 *(ptr++) = '\\'; 935 break; 936 937 case '^': 938 *(ptr++) = '^'; 939 break; 940 941 case ',': 942 *(ptr++) = ','; 943 break; 944 945 case '\n': 946 continue; 947 948 default: 949 if ((_nc_syntax == SYN_TERMINFO) || !_nc_strict_bsd) { 950 switch (c) { 951 case 'a': 952 c = '\007'; 953 break; 954 case 'e': 955 c = '\033'; 956 break; 957 case 'l': 958 c = '\n'; 959 break; 960 case 's': 961 c = ' '; 962 break; 963 case ':': 964 c = ':'; 965 break; 966 default: 967 _nc_warning("Illegal character '%s' in \\ sequence", 968 unctrl(UChar(c))); 969 break; 970 } 971 } 972 /* FALLTHRU */ 973 case '|': 974 *(ptr++) = (char) c; 975 } /* endswitch (c) */ 976 } /* endelse (c < '0' || c > '7') */ 977 } 978 /* end else if (c == '\\') */ 979 else if (c == '\n' && (_nc_syntax == SYN_TERMINFO)) { 980 /* 981 * Newlines embedded in a terminfo string are ignored, provided 982 * that the next line begins with whitespace. 983 */ 984 ignored = TRUE; 985 } else { 986 *(ptr++) = (char) c; 987 } 988 989 if (!ignored) { 990 if (_nc_curr_col <= 1) { 991 push_back(c); 992 c = '\n'; 993 break; 994 } 995 last_ch = c; 996 count++; 997 } 998 ignored = FALSE; 999 1000 if (count > MAXCAPLEN && !long_warning) { 1001 _nc_warning("Very long string found. Missing separator?"); 1002 long_warning = TRUE; 1003 } 1004 } /* end while */ 1005 1006 *ptr = '\0'; 1007 1008 return (c); 1009 } 1010 1011 /* 1012 * _nc_push_token() 1013 * 1014 * Push a token of given type so that it will be reread by the next 1015 * get_token() call. 1016 */ 1017 1018 NCURSES_EXPORT(void) 1019 _nc_push_token(int tokclass) 1020 { 1021 /* 1022 * This implementation is kind of bogus, it will fail if we ever do more 1023 * than one pushback at a time between get_token() calls. It relies on the 1024 * fact that _nc_curr_token is static storage that nothing but 1025 * _nc_get_token() touches. 1026 */ 1027 pushtype = tokclass; 1028 if (pushname == 0) 1029 pushname = typeMalloc(char, MAX_NAME_SIZE + 1); 1030 _nc_get_type(pushname); 1031 1032 DEBUG(3, ("pushing token: `%s', class %d", 1033 ((_nc_curr_token.tk_name != 0) 1034 ? _nc_curr_token.tk_name 1035 : "<null>"), 1036 pushtype)); 1037 } 1038 1039 /* 1040 * Panic mode error recovery - skip everything until a "ch" is found. 1041 */ 1042 NCURSES_EXPORT(void) 1043 _nc_panic_mode(char ch) 1044 { 1045 for (;;) { 1046 int c = next_char(); 1047 if (c == ch) 1048 return; 1049 if (c == EOF) 1050 return; 1051 } 1052 } 1053 1054 #if NO_LEAKS 1055 NCURSES_EXPORT(void) 1056 _nc_comp_scan_leaks(void) 1057 { 1058 if (pushname != 0) { 1059 FreeAndNull(pushname); 1060 } 1061 if (tok_buf != 0) { 1062 FreeAndNull(tok_buf); 1063 } 1064 } 1065 #endif 1066