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