1 /* $OpenBSD: lex.c,v 1.44 2008/07/03 17:52:08 otto Exp $ */ 2 3 /* 4 * lexical analysis and source input 5 */ 6 7 #include "sh.h" 8 #include <libgen.h> 9 #include <ctype.h> 10 11 12 /* Structure to keep track of the lexing state and the various pieces of info 13 * needed for each particular state. 14 */ 15 typedef struct lex_state Lex_state; 16 struct lex_state { 17 int ls_state; 18 union { 19 /* $(...) */ 20 struct scsparen_info { 21 int nparen; /* count open parenthesis */ 22 int csstate; /* XXX remove */ 23 #define ls_scsparen ls_info.u_scsparen 24 } u_scsparen; 25 26 /* $((...)) */ 27 struct sasparen_info { 28 int nparen; /* count open parenthesis */ 29 int start; /* marks start of $(( in output str */ 30 #define ls_sasparen ls_info.u_sasparen 31 } u_sasparen; 32 33 /* ((...)) */ 34 struct sletparen_info { 35 int nparen; /* count open parenthesis */ 36 #define ls_sletparen ls_info.u_sletparen 37 } u_sletparen; 38 39 /* `...` */ 40 struct sbquote_info { 41 int indquotes; /* true if in double quotes: "`...`" */ 42 #define ls_sbquote ls_info.u_sbquote 43 } u_sbquote; 44 45 Lex_state *base; /* used to point to next state block */ 46 } ls_info; 47 }; 48 49 typedef struct State_info State_info; 50 struct State_info { 51 Lex_state *base; 52 Lex_state *end; 53 }; 54 55 56 static void readhere(struct ioword *); 57 static int getsc__(void); 58 static void getsc_line(Source *); 59 static int getsc_bn(void); 60 static char *get_brace_var(XString *, char *); 61 static int arraysub(char **); 62 static const char *ungetsc(int); 63 static void gethere(void); 64 static Lex_state *push_state_(State_info *, Lex_state *); 65 static Lex_state *pop_state_(State_info *, Lex_state *); 66 static char *special_prompt_expand(char *); 67 static int dopprompt(const char *, int, const char **, int); 68 69 static int backslash_skip; 70 static int ignore_backslash_newline; 71 72 /* optimized getsc_bn() */ 73 #define getsc() (*source->str != '\0' && *source->str != '\\' \ 74 && !backslash_skip ? *source->str++ : getsc_bn()) 75 /* optimized getsc__() */ 76 #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__()) 77 78 #define STATE_BSIZE 32 79 80 #define PUSH_STATE(s) do { \ 81 if (++statep == state_info.end) \ 82 statep = push_state_(&state_info, statep); \ 83 state = statep->ls_state = (s); \ 84 } while (0) 85 86 #define POP_STATE() do { \ 87 if (--statep == state_info.base) \ 88 statep = pop_state_(&state_info, statep); \ 89 state = statep->ls_state; \ 90 } while (0) 91 92 93 94 /* 95 * Lexical analyzer 96 * 97 * tokens are not regular expressions, they are LL(1). 98 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))". 99 * hence the state stack. 100 */ 101 102 int 103 yylex(int cf) 104 { 105 Lex_state states[STATE_BSIZE], *statep; 106 State_info state_info; 107 int c, state; 108 XString ws; /* expandable output word */ 109 char *wp; /* output word pointer */ 110 char *sp, *dp; 111 int c2; 112 113 114 Again: 115 states[0].ls_state = -1; 116 states[0].ls_info.base = (Lex_state *) 0; 117 statep = &states[1]; 118 state_info.base = states; 119 state_info.end = &states[STATE_BSIZE]; 120 121 Xinit(ws, wp, 64, ATEMP); 122 123 backslash_skip = 0; 124 ignore_backslash_newline = 0; 125 126 if (cf&ONEWORD) 127 state = SWORD; 128 else if (cf&LETEXPR) { 129 *wp++ = OQUOTE; /* enclose arguments in (double) quotes */ 130 state = SLETPAREN; 131 statep->ls_sletparen.nparen = 0; 132 } else { /* normal lexing */ 133 state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; 134 while ((c = getsc()) == ' ' || c == '\t') 135 ; 136 if (c == '#') { 137 ignore_backslash_newline++; 138 while ((c = getsc()) != '\0' && c != '\n') 139 ; 140 ignore_backslash_newline--; 141 } 142 ungetsc(c); 143 } 144 if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ 145 source->flags &= ~SF_ALIAS; 146 /* In POSIX mode, a trailing space only counts if we are 147 * parsing a simple command 148 */ 149 if (!Flag(FPOSIX) || (cf & CMDWORD)) 150 cf |= ALIAS; 151 } 152 153 /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ 154 statep->ls_state = state; 155 156 /* collect non-special or quoted characters to form word */ 157 while (!((c = getsc()) == 0 || 158 ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { 159 Xcheck(ws, wp); 160 switch (state) { 161 case SBASE: 162 if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) && 163 c == '!') { 164 char **replace = NULL; 165 166 c2 = getsc(); 167 if (c2 == '\0' || c2 == ' ' || c2 == '\t') 168 ; 169 else if (c2 == '!') 170 replace = hist_get_newest(0); 171 else if (isdigit(c2) || c2 == '-' || 172 isalpha(c2)) { 173 int get = !isalpha(c2); 174 char match[200], *str = match; 175 176 *str++ = c2; 177 do { 178 if ((c2 = getsc()) == '\0') 179 break; 180 if (c2 == '\t' || c2 == ' ' || 181 c2 == '\n') { 182 ungetsc(c2); 183 break; 184 } 185 *str++ = c2; 186 } while (str < &match[sizeof(match)-1]); 187 *str = '\0'; 188 189 if (get) { 190 int h = findhistrel(match); 191 if (h >= 0) 192 replace = &history[h]; 193 } else { 194 int h = findhist(-1, 0, match, true); 195 if (h >= 0) 196 replace = &history[h]; 197 } 198 } 199 200 /* 201 * XXX ksh history buffer saves un-expanded 202 * commands. Until the history buffer code is 203 * changed to contain expanded commands, we 204 * ignore the bad commands (spinning sucks) 205 */ 206 if (replace && **replace == '!') 207 ungetsc(c2); 208 else if (replace) { 209 Source *s; 210 211 /* do not strdup replacement via alloc */ 212 s = pushs(SREREAD, source->areap); 213 s->start = s->str = *replace; 214 s->next = source; 215 s->u.freeme = NULL; 216 source = s; 217 continue; 218 } else 219 ungetsc(c2); 220 } 221 if (c == '[' && (cf & (VARASN|ARRAYVAR))) { 222 *wp = EOS; /* temporary */ 223 if (is_wdvarname(Xstring(ws, wp), false)) { 224 char *p, *tmp; 225 226 if (arraysub(&tmp)) { 227 *wp++ = CHAR; 228 *wp++ = c; 229 for (p = tmp; *p; ) { 230 Xcheck(ws, wp); 231 *wp++ = CHAR; 232 *wp++ = *p++; 233 } 234 afree(tmp, ATEMP); 235 break; 236 } else { 237 Source *s; 238 239 s = pushs(SREREAD, 240 source->areap); 241 s->start = s->str 242 = s->u.freeme = tmp; 243 s->next = source; 244 source = s; 245 } 246 } 247 *wp++ = CHAR; 248 *wp++ = c; 249 break; 250 } 251 /* FALLTHROUGH */ 252 Sbase1: /* includes *(...|...) pattern (*+?@!) */ 253 if (c == '*' || c == '@' || c == '+' || c == '?' || 254 c == '!') { 255 c2 = getsc(); 256 if (c2 == '(' /*)*/ ) { 257 *wp++ = OPAT; 258 *wp++ = c; 259 PUSH_STATE(SPATTERN); 260 break; 261 } 262 ungetsc(c2); 263 } 264 /* FALLTHROUGH */ 265 Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ 266 switch (c) { 267 case '\\': 268 c = getsc(); 269 if (c) /* trailing \ is lost */ 270 *wp++ = QCHAR, *wp++ = c; 271 break; 272 case '\'': 273 *wp++ = OQUOTE; 274 ignore_backslash_newline++; 275 PUSH_STATE(SSQUOTE); 276 break; 277 case '"': 278 *wp++ = OQUOTE; 279 PUSH_STATE(SDQUOTE); 280 break; 281 default: 282 goto Subst; 283 } 284 break; 285 286 Subst: 287 switch (c) { 288 case '\\': 289 c = getsc(); 290 switch (c) { 291 case '\\': 292 case '$': case '`': 293 *wp++ = QCHAR, *wp++ = c; 294 break; 295 case '"': 296 if ((cf & HEREDOC) == 0) { 297 *wp++ = QCHAR, *wp++ = c; 298 break; 299 } 300 /* FALLTHROUGH */ 301 default: 302 Xcheck(ws, wp); 303 if (c) { /* trailing \ is lost */ 304 *wp++ = CHAR, *wp++ = '\\'; 305 *wp++ = CHAR, *wp++ = c; 306 } 307 break; 308 } 309 break; 310 case '$': 311 c = getsc(); 312 if (c == '(') /*)*/ { 313 c = getsc(); 314 if (c == '(') /*)*/ { 315 PUSH_STATE(SASPAREN); 316 statep->ls_sasparen.nparen = 2; 317 statep->ls_sasparen.start = 318 Xsavepos(ws, wp); 319 *wp++ = EXPRSUB; 320 } else { 321 ungetsc(c); 322 PUSH_STATE(SCSPAREN); 323 statep->ls_scsparen.nparen = 1; 324 statep->ls_scsparen.csstate = 0; 325 *wp++ = COMSUB; 326 } 327 } else if (c == '{') /*}*/ { 328 *wp++ = OSUBST; 329 *wp++ = '{'; /*}*/ 330 wp = get_brace_var(&ws, wp); 331 c = getsc(); 332 /* allow :# and :% (ksh88 compat) */ 333 if (c == ':') { 334 *wp++ = CHAR, *wp++ = c; 335 c = getsc(); 336 } 337 /* If this is a trim operation, 338 * treat (,|,) specially in STBRACE. 339 */ 340 if (c == '#' || c == '%') { 341 ungetsc(c); 342 PUSH_STATE(STBRACE); 343 } else { 344 ungetsc(c); 345 PUSH_STATE(SBRACE); 346 } 347 } else if (ctype(c, C_ALPHA)) { 348 *wp++ = OSUBST; 349 *wp++ = 'X'; 350 do { 351 Xcheck(ws, wp); 352 *wp++ = c; 353 c = getsc(); 354 } while (ctype(c, C_ALPHA|C_DIGIT)); 355 *wp++ = '\0'; 356 *wp++ = CSUBST; 357 *wp++ = 'X'; 358 ungetsc(c); 359 } else if (ctype(c, C_DIGIT|C_VAR1)) { 360 Xcheck(ws, wp); 361 *wp++ = OSUBST; 362 *wp++ = 'X'; 363 *wp++ = c; 364 *wp++ = '\0'; 365 *wp++ = CSUBST; 366 *wp++ = 'X'; 367 } else { 368 *wp++ = CHAR, *wp++ = '$'; 369 ungetsc(c); 370 } 371 break; 372 case '`': 373 PUSH_STATE(SBQUOTE); 374 *wp++ = COMSUB; 375 /* Need to know if we are inside double quotes 376 * since sh/at&t-ksh translate the \" to " in 377 * "`..\"..`". 378 * This is not done in posix mode (section 379 * 3.2.3, Double Quotes: "The backquote shall 380 * retain its special meaning introducing the 381 * other form of command substitution (see 382 * 3.6.3). The portion of the quoted string 383 * from the initial backquote and the 384 * characters up to the next backquote that 385 * is not preceded by a backslash (having 386 * escape characters removed) defines that 387 * command whose output replaces `...` when 388 * the word is expanded." 389 * Section 3.6.3, Command Substitution: 390 * "Within the backquoted style of command 391 * substitution, backslash shall retain its 392 * literal meaning, except when followed by 393 * $ ` \."). 394 */ 395 statep->ls_sbquote.indquotes = 0; 396 if (!Flag(FPOSIX)) { 397 Lex_state *s = statep; 398 Lex_state *base = state_info.base; 399 while (1) { 400 for (; s != base; s--) { 401 if (s->ls_state == SDQUOTE) { 402 statep->ls_sbquote.indquotes = 1; 403 break; 404 } 405 } 406 if (s != base) 407 break; 408 if (!(s = s->ls_info.base)) 409 break; 410 base = s-- - STATE_BSIZE; 411 } 412 } 413 break; 414 default: 415 *wp++ = CHAR, *wp++ = c; 416 } 417 break; 418 419 case SSQUOTE: 420 if (c == '\'') { 421 POP_STATE(); 422 *wp++ = CQUOTE; 423 ignore_backslash_newline--; 424 } else 425 *wp++ = QCHAR, *wp++ = c; 426 break; 427 428 case SDQUOTE: 429 if (c == '"') { 430 POP_STATE(); 431 *wp++ = CQUOTE; 432 } else 433 goto Subst; 434 break; 435 436 case SCSPAREN: /* $( .. ) */ 437 /* todo: deal with $(...) quoting properly 438 * kludge to partly fake quoting inside $(..): doesn't 439 * really work because nested $(..) or ${..} inside 440 * double quotes aren't dealt with. 441 */ 442 switch (statep->ls_scsparen.csstate) { 443 case 0: /* normal */ 444 switch (c) { 445 case '(': 446 statep->ls_scsparen.nparen++; 447 break; 448 case ')': 449 statep->ls_scsparen.nparen--; 450 break; 451 case '\\': 452 statep->ls_scsparen.csstate = 1; 453 break; 454 case '"': 455 statep->ls_scsparen.csstate = 2; 456 break; 457 case '\'': 458 statep->ls_scsparen.csstate = 4; 459 ignore_backslash_newline++; 460 break; 461 } 462 break; 463 464 case 1: /* backslash in normal mode */ 465 case 3: /* backslash in double quotes */ 466 --statep->ls_scsparen.csstate; 467 break; 468 469 case 2: /* double quotes */ 470 if (c == '"') 471 statep->ls_scsparen.csstate = 0; 472 else if (c == '\\') 473 statep->ls_scsparen.csstate = 3; 474 break; 475 476 case 4: /* single quotes */ 477 if (c == '\'') { 478 statep->ls_scsparen.csstate = 0; 479 ignore_backslash_newline--; 480 } 481 break; 482 } 483 if (statep->ls_scsparen.nparen == 0) { 484 POP_STATE(); 485 *wp++ = 0; /* end of COMSUB */ 486 } else 487 *wp++ = c; 488 break; 489 490 case SASPAREN: /* $(( .. )) */ 491 /* todo: deal with $((...); (...)) properly */ 492 /* XXX should nest using existing state machine 493 * (embed "..", $(...), etc.) */ 494 if (c == '(') 495 statep->ls_sasparen.nparen++; 496 else if (c == ')') { 497 statep->ls_sasparen.nparen--; 498 if (statep->ls_sasparen.nparen == 1) { 499 /*(*/ 500 if ((c2 = getsc()) == ')') { 501 POP_STATE(); 502 *wp++ = 0; /* end of EXPRSUB */ 503 break; 504 } else { 505 char *s; 506 507 ungetsc(c2); 508 /* mismatched parenthesis - 509 * assume we were really 510 * parsing a $(..) expression 511 */ 512 s = Xrestpos(ws, wp, 513 statep->ls_sasparen.start); 514 memmove(s + 1, s, wp - s); 515 *s++ = COMSUB; 516 *s = '('; /*)*/ 517 wp++; 518 statep->ls_scsparen.nparen = 1; 519 statep->ls_scsparen.csstate = 0; 520 state = statep->ls_state = 521 SCSPAREN; 522 } 523 } 524 } 525 *wp++ = c; 526 break; 527 528 case SBRACE: 529 /*{*/ 530 if (c == '}') { 531 POP_STATE(); 532 *wp++ = CSUBST; 533 *wp++ = /*{*/ '}'; 534 } else 535 goto Sbase1; 536 break; 537 538 case STBRACE: 539 /* Same as SBRACE, except (,|,) treated specially */ 540 /*{*/ 541 if (c == '}') { 542 POP_STATE(); 543 *wp++ = CSUBST; 544 *wp++ = /*{*/ '}'; 545 } else if (c == '|') { 546 *wp++ = SPAT; 547 } else if (c == '(') { 548 *wp++ = OPAT; 549 *wp++ = ' '; /* simile for @ */ 550 PUSH_STATE(SPATTERN); 551 } else 552 goto Sbase1; 553 break; 554 555 case SBQUOTE: 556 if (c == '`') { 557 *wp++ = 0; 558 POP_STATE(); 559 } else if (c == '\\') { 560 switch (c = getsc()) { 561 case '\\': 562 case '$': case '`': 563 *wp++ = c; 564 break; 565 case '"': 566 if (statep->ls_sbquote.indquotes) { 567 *wp++ = c; 568 break; 569 } 570 /* FALLTHROUGH */ 571 default: 572 if (c) { /* trailing \ is lost */ 573 *wp++ = '\\'; 574 *wp++ = c; 575 } 576 break; 577 } 578 } else 579 *wp++ = c; 580 break; 581 582 case SWORD: /* ONEWORD */ 583 goto Subst; 584 585 case SLETPAREN: /* LETEXPR: (( ... )) */ 586 /*(*/ 587 if (c == ')') { 588 if (statep->ls_sletparen.nparen > 0) 589 --statep->ls_sletparen.nparen; 590 /*(*/ 591 else if ((c2 = getsc()) == ')') { 592 c = 0; 593 *wp++ = CQUOTE; 594 goto Done; 595 } else 596 ungetsc(c2); 597 } else if (c == '(') 598 /* parenthesis inside quotes and backslashes 599 * are lost, but at&t ksh doesn't count them 600 * either 601 */ 602 ++statep->ls_sletparen.nparen; 603 goto Sbase2; 604 605 case SHEREDELIM: /* <<,<<- delimiter */ 606 /* XXX chuck this state (and the next) - use 607 * the existing states ($ and \`..` should be 608 * stripped of their specialness after the 609 * fact). 610 */ 611 /* here delimiters need a special case since 612 * $ and `..` are not to be treated specially 613 */ 614 if (c == '\\') { 615 c = getsc(); 616 if (c) { /* trailing \ is lost */ 617 *wp++ = QCHAR; 618 *wp++ = c; 619 } 620 } else if (c == '\'') { 621 PUSH_STATE(SSQUOTE); 622 *wp++ = OQUOTE; 623 ignore_backslash_newline++; 624 } else if (c == '"') { 625 state = statep->ls_state = SHEREDQUOTE; 626 *wp++ = OQUOTE; 627 } else { 628 *wp++ = CHAR; 629 *wp++ = c; 630 } 631 break; 632 633 case SHEREDQUOTE: /* " in <<,<<- delimiter */ 634 if (c == '"') { 635 *wp++ = CQUOTE; 636 state = statep->ls_state = SHEREDELIM; 637 } else { 638 if (c == '\\') { 639 switch (c = getsc()) { 640 case '\\': case '"': 641 case '$': case '`': 642 break; 643 default: 644 if (c) { /* trailing \ lost */ 645 *wp++ = CHAR; 646 *wp++ = '\\'; 647 } 648 break; 649 } 650 } 651 *wp++ = CHAR; 652 *wp++ = c; 653 } 654 break; 655 656 case SPATTERN: /* in *(...|...) pattern (*+?@!) */ 657 if ( /*(*/ c == ')') { 658 *wp++ = CPAT; 659 POP_STATE(); 660 } else if (c == '|') { 661 *wp++ = SPAT; 662 } else if (c == '(') { 663 *wp++ = OPAT; 664 *wp++ = ' '; /* simile for @ */ 665 PUSH_STATE(SPATTERN); 666 } else 667 goto Sbase1; 668 break; 669 } 670 } 671 Done: 672 Xcheck(ws, wp); 673 if (statep != &states[1]) 674 /* XXX figure out what is missing */ 675 yyerror("no closing quote\n"); 676 677 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ 678 if (state == SHEREDELIM) 679 state = SBASE; 680 681 dp = Xstring(ws, wp); 682 if ((c == '<' || c == '>') && state == SBASE && 683 ((c2 = Xlength(ws, wp)) == 0 || 684 (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { 685 struct ioword *iop = (struct ioword *) alloc(sizeof(*iop), ATEMP); 686 687 if (c2 == 2) 688 iop->unit = dp[1] - '0'; 689 else 690 iop->unit = c == '>'; /* 0 for <, 1 for > */ 691 692 c2 = getsc(); 693 /* <<, >>, <> are ok, >< is not */ 694 if (c == c2 || (c == '<' && c2 == '>')) { 695 iop->flag = c == c2 ? 696 (c == '>' ? IOCAT : IOHERE) : IORDWR; 697 if (iop->flag == IOHERE) { 698 if ((c2 = getsc()) == '-') 699 iop->flag |= IOSKIP; 700 else 701 ungetsc(c2); 702 } 703 } else if (c2 == '&') 704 iop->flag = IODUP | (c == '<' ? IORDUP : 0); 705 else { 706 iop->flag = c == '>' ? IOWRITE : IOREAD; 707 if (c == '>' && c2 == '|') 708 iop->flag |= IOCLOB; 709 else 710 ungetsc(c2); 711 } 712 713 iop->name = (char *) 0; 714 iop->delim = (char *) 0; 715 iop->heredoc = (char *) 0; 716 Xfree(ws, wp); /* free word */ 717 yylval.iop = iop; 718 return REDIR; 719 } 720 721 if (wp == dp && state == SBASE) { 722 Xfree(ws, wp); /* free word */ 723 /* no word, process LEX1 character */ 724 switch (c) { 725 default: 726 return c; 727 728 case '|': 729 case '&': 730 case ';': 731 if ((c2 = getsc()) == c) 732 c = (c == ';') ? BREAK : 733 (c == '|') ? LOGOR : 734 (c == '&') ? LOGAND : 735 YYERRCODE; 736 else if (c == '|' && c2 == '&') 737 c = COPROC; 738 else 739 ungetsc(c2); 740 return c; 741 742 case '\n': 743 gethere(); 744 if (cf & CONTIN) 745 goto Again; 746 return c; 747 748 case '(': /*)*/ 749 if (!Flag(FSH)) { 750 if ((c2 = getsc()) == '(') /*)*/ 751 /* XXX need to handle ((...); (...)) */ 752 c = MDPAREN; 753 else 754 ungetsc(c2); 755 } 756 return c; 757 /*(*/ 758 case ')': 759 return c; 760 } 761 } 762 763 *wp++ = EOS; /* terminate word */ 764 yylval.cp = Xclose(ws, wp); 765 if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ 766 return LWORD; 767 ungetsc(c); /* unget terminator */ 768 769 /* copy word to unprefixed string ident */ 770 for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) 771 *dp++ = *sp++; 772 /* Make sure the ident array stays '\0' padded */ 773 memset(dp, 0, (ident+IDENT) - dp + 1); 774 if (c != EOS) 775 *ident = '\0'; /* word is not unquoted */ 776 777 if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { 778 struct tbl *p; 779 int h = hash(ident); 780 781 /* { */ 782 if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && 783 (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { 784 afree(yylval.cp, ATEMP); 785 return p->val.i; 786 } 787 if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && 788 (p->flag & ISSET)) { 789 Source *s; 790 791 for (s = source; s->type == SALIAS; s = s->next) 792 if (s->u.tblp == p) 793 return LWORD; 794 /* push alias expansion */ 795 s = pushs(SALIAS, source->areap); 796 s->start = s->str = p->val.s; 797 s->u.tblp = p; 798 s->next = source; 799 source = s; 800 afree(yylval.cp, ATEMP); 801 goto Again; 802 } 803 } 804 805 return LWORD; 806 } 807 808 static void 809 gethere(void) 810 { 811 struct ioword **p; 812 813 for (p = heres; p < herep; p++) 814 readhere(*p); 815 herep = heres; 816 } 817 818 /* 819 * read "<<word" text into temp file 820 */ 821 822 static void 823 readhere(struct ioword *iop) 824 { 825 int c; 826 char *volatile eof; 827 char *eofp; 828 int skiptabs; 829 XString xs; 830 char *xp; 831 int xpos; 832 833 eof = evalstr(iop->delim, 0); 834 835 if (!(iop->flag & IOEVAL)) 836 ignore_backslash_newline++; 837 838 Xinit(xs, xp, 256, ATEMP); 839 840 for (;;) { 841 eofp = eof; 842 skiptabs = iop->flag & IOSKIP; 843 xpos = Xsavepos(xs, xp); 844 while ((c = getsc()) != 0) { 845 if (skiptabs) { 846 if (c == '\t') 847 continue; 848 skiptabs = 0; 849 } 850 if (c != *eofp) 851 break; 852 Xcheck(xs, xp); 853 Xput(xs, xp, c); 854 eofp++; 855 } 856 /* Allow EOF here so commands with out trailing newlines 857 * will work (eg, ksh -c '...', $(...), etc). 858 */ 859 if (*eofp == '\0' && (c == 0 || c == '\n')) { 860 xp = Xrestpos(xs, xp, xpos); 861 break; 862 } 863 ungetsc(c); 864 while ((c = getsc()) != '\n') { 865 if (c == 0) 866 yyerror("here document `%s' unclosed\n", eof); 867 Xcheck(xs, xp); 868 Xput(xs, xp, c); 869 } 870 Xcheck(xs, xp); 871 Xput(xs, xp, c); 872 } 873 Xput(xs, xp, '\0'); 874 iop->heredoc = Xclose(xs, xp); 875 876 if (!(iop->flag & IOEVAL)) 877 ignore_backslash_newline--; 878 } 879 880 void 881 yyerror(const char *fmt, ...) 882 { 883 va_list va; 884 885 /* pop aliases and re-reads */ 886 while (source->type == SALIAS || source->type == SREREAD) 887 source = source->next; 888 source->str = null; /* zap pending input */ 889 890 error_prefix(true); 891 va_start(va, fmt); 892 shf_vfprintf(shl_out, fmt, va); 893 va_end(va); 894 errorf(null); 895 } 896 897 /* 898 * input for yylex with alias expansion 899 */ 900 901 Source * 902 pushs(int type, Area *areap) 903 { 904 Source *s; 905 906 s = (Source *) alloc(sizeof(Source), areap); 907 s->type = type; 908 s->str = null; 909 s->start = NULL; 910 s->line = 0; 911 s->cmd_offset = 0; 912 s->errline = 0; 913 s->file = NULL; 914 s->flags = 0; 915 s->next = NULL; 916 s->areap = areap; 917 if (type == SFILE || type == SSTDIN) { 918 char *dummy; 919 Xinit(s->xs, dummy, 256, s->areap); 920 } else 921 memset(&s->xs, 0, sizeof(s->xs)); 922 return s; 923 } 924 925 static int 926 getsc__(void) 927 { 928 Source *s = source; 929 int c; 930 931 while ((c = *s->str++) == 0) { 932 s->str = NULL; /* return 0 for EOF by default */ 933 switch (s->type) { 934 case SEOF: 935 s->str = null; 936 return 0; 937 938 case SSTDIN: 939 case SFILE: 940 getsc_line(s); 941 break; 942 943 case SWSTR: 944 break; 945 946 case SSTRING: 947 break; 948 949 case SWORDS: 950 s->start = s->str = *s->u.strv++; 951 s->type = SWORDSEP; 952 break; 953 954 case SWORDSEP: 955 if (*s->u.strv == NULL) { 956 s->start = s->str = newline; 957 s->type = SEOF; 958 } else { 959 s->start = s->str = space; 960 s->type = SWORDS; 961 } 962 break; 963 964 case SALIAS: 965 if (s->flags & SF_ALIASEND) { 966 /* pass on an unused SF_ALIAS flag */ 967 source = s->next; 968 source->flags |= s->flags & SF_ALIAS; 969 s = source; 970 } else if (*s->u.tblp->val.s && 971 isspace(strchr(s->u.tblp->val.s, 0)[-1])) { 972 source = s = s->next; /* pop source stack */ 973 /* Note that this alias ended with a space, 974 * enabling alias expansion on the following 975 * word. 976 */ 977 s->flags |= SF_ALIAS; 978 } else { 979 /* At this point, we need to keep the current 980 * alias in the source list so recursive 981 * aliases can be detected and we also need 982 * to return the next character. Do this 983 * by temporarily popping the alias to get 984 * the next character and then put it back 985 * in the source list with the SF_ALIASEND 986 * flag set. 987 */ 988 source = s->next; /* pop source stack */ 989 source->flags |= s->flags & SF_ALIAS; 990 c = getsc__(); 991 if (c) { 992 s->flags |= SF_ALIASEND; 993 s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 994 s->start = s->str = s->ugbuf; 995 s->next = source; 996 source = s; 997 } else { 998 s = source; 999 /* avoid reading eof twice */ 1000 s->str = NULL; 1001 break; 1002 } 1003 } 1004 continue; 1005 1006 case SREREAD: 1007 if (s->start != s->ugbuf) /* yuck */ 1008 afree(s->u.freeme, ATEMP); 1009 source = s = s->next; 1010 continue; 1011 } 1012 if (s->str == NULL) { 1013 s->type = SEOF; 1014 s->start = s->str = null; 1015 return '\0'; 1016 } 1017 if (s->flags & SF_ECHO) { 1018 shf_puts(s->str, shl_out); 1019 shf_flush(shl_out); 1020 } 1021 } 1022 return c; 1023 } 1024 1025 static void 1026 getsc_line(Source *s) 1027 { 1028 char *xp = Xstring(s->xs, xp); 1029 int interactive = Flag(FTALKING) && s->type == SSTDIN; 1030 int have_tty = interactive && (s->flags & SF_TTY); 1031 1032 /* Done here to ensure nothing odd happens when a timeout occurs */ 1033 XcheckN(s->xs, xp, LINE); 1034 *xp = '\0'; 1035 s->start = s->str = xp; 1036 1037 if (have_tty && ksh_tmout) { 1038 ksh_tmout_state = TMOUT_READING; 1039 alarm(ksh_tmout); 1040 } 1041 #ifdef EDIT 1042 if (have_tty && (0 1043 # ifdef VI 1044 || Flag(FVI) 1045 # endif /* VI */ 1046 # ifdef EMACS 1047 || Flag(FEMACS) || Flag(FGMACS) 1048 # endif /* EMACS */ 1049 )) { 1050 int nread; 1051 1052 nread = x_read(xp, LINE); 1053 if (nread < 0) /* read error */ 1054 nread = 0; 1055 xp[nread] = '\0'; 1056 xp += nread; 1057 } 1058 else 1059 #endif /* EDIT */ 1060 { 1061 if (interactive) { 1062 pprompt(prompt, 0); 1063 } else 1064 s->line++; 1065 1066 while (1) { 1067 char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); 1068 1069 if (!p && shf_error(s->u.shf) && 1070 shf_errno(s->u.shf) == EINTR) { 1071 shf_clearerr(s->u.shf); 1072 if (trap) 1073 runtraps(0); 1074 continue; 1075 } 1076 if (!p || (xp = p, xp[-1] == '\n')) 1077 break; 1078 /* double buffer size */ 1079 xp++; /* move past null so doubling works... */ 1080 XcheckN(s->xs, xp, Xlength(s->xs, xp)); 1081 xp--; /* ...and move back again */ 1082 } 1083 /* flush any unwanted input so other programs/builtins 1084 * can read it. Not very optimal, but less error prone 1085 * than flushing else where, dealing with redirections, 1086 * etc.. 1087 * todo: reduce size of shf buffer (~128?) if SSTDIN 1088 */ 1089 if (s->type == SSTDIN) 1090 shf_flush(s->u.shf); 1091 } 1092 /* XXX: temporary kludge to restore source after a 1093 * trap may have been executed. 1094 */ 1095 source = s; 1096 if (have_tty && ksh_tmout) { 1097 ksh_tmout_state = TMOUT_EXECUTING; 1098 alarm(0); 1099 } 1100 s->start = s->str = Xstring(s->xs, xp); 1101 strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); 1102 /* Note: if input is all nulls, this is not eof */ 1103 if (Xlength(s->xs, xp) == 0) { /* EOF */ 1104 if (s->type == SFILE) 1105 shf_fdclose(s->u.shf); 1106 s->str = NULL; 1107 } else if (interactive) { 1108 #ifdef HISTORY 1109 char *p = Xstring(s->xs, xp); 1110 if (cur_prompt == PS1) 1111 while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) 1112 p++; 1113 if (*p) { 1114 s->line++; 1115 histsave(s->line, s->str, 1); 1116 } 1117 #endif /* HISTORY */ 1118 } 1119 if (interactive) 1120 set_prompt(PS2, (Source *) 0); 1121 } 1122 1123 static char * 1124 special_prompt_expand(char *str) 1125 { 1126 char *p = str; 1127 1128 while ((p = strstr(p, "\\$")) != NULL) { 1129 *(p+1) = 'p'; 1130 } 1131 return str; 1132 } 1133 1134 void 1135 set_prompt(int to, Source *s) 1136 { 1137 char *ps1; 1138 Area *saved_atemp; 1139 1140 cur_prompt = to; 1141 1142 switch (to) { 1143 case PS1: /* command */ 1144 ps1 = str_save(str_val(global("PS1")), ATEMP); 1145 saved_atemp = ATEMP; /* ps1 is freed by substitute() */ 1146 newenv(E_ERRH); 1147 if (sigsetjmp(e->jbuf, 0)) { 1148 prompt = safe_prompt; 1149 /* Don't print an error - assume it has already 1150 * been printed. Reason is we may have forked 1151 * to run a command and the child may be 1152 * unwinding its stack through this code as it 1153 * exits. 1154 */ 1155 } else { 1156 /* expand \$ before other substitutions are done */ 1157 char *tmp = special_prompt_expand(ps1); 1158 prompt = str_save(substitute(tmp, 0), saved_atemp); 1159 } 1160 quitenv(NULL); 1161 break; 1162 case PS2: /* command continuation */ 1163 prompt = str_val(global("PS2")); 1164 break; 1165 } 1166 } 1167 1168 static int 1169 dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) 1170 { 1171 char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; 1172 int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; 1173 const char *cp = sp; 1174 struct tm *tm; 1175 time_t t; 1176 1177 if (*cp && cp[1] == '\r') { 1178 delimiter = *cp; 1179 cp += 2; 1180 } 1181 1182 while (*cp != 0) { 1183 delimitthis = 0; 1184 if (indelimit && *cp != delimiter) 1185 ; 1186 else if (*cp == '\n' || *cp == '\r') { 1187 totlen = 0; 1188 sp = cp + 1; 1189 } else if (*cp == '\t') { 1190 if (counting) 1191 totlen = (totlen | 7) + 1; 1192 } else if (*cp == delimiter) { 1193 indelimit = !indelimit; 1194 delimitthis = 1; 1195 } 1196 1197 if (*cp == '\\') { 1198 cp++; 1199 if (!*cp) 1200 break; 1201 if (Flag(FSH)) 1202 snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1203 else switch (*cp) { 1204 case 'a': /* '\' 'a' bell */ 1205 strbuf[0] = '\007'; 1206 strbuf[1] = '\0'; 1207 break; 1208 case 'd': /* '\' 'd' Dow Mon DD */ 1209 time(&t); 1210 tm = localtime(&t); 1211 strftime(strbuf, sizeof strbuf, "%a %b %d", tm); 1212 break; 1213 case 'D': /* '\' 'D' '{' strftime format '}' */ 1214 p = strchr(cp + 2, '}'); 1215 if (cp[1] != '{' || p == NULL) { 1216 snprintf(strbuf, sizeof strbuf, 1217 "\\%c", *cp); 1218 break; 1219 } 1220 strlcpy(tmpbuf, cp + 2, sizeof tmpbuf); 1221 p = strchr(tmpbuf, '}'); 1222 if (p) 1223 *p = '\0'; 1224 time(&t); 1225 tm = localtime(&t); 1226 strftime(strbuf, sizeof strbuf, tmpbuf, tm); 1227 cp = strchr(cp + 2, '}'); 1228 break; 1229 case 'e': /* '\' 'e' escape */ 1230 strbuf[0] = '\033'; 1231 strbuf[1] = '\0'; 1232 break; 1233 case 'h': /* '\' 'h' shortened hostname */ 1234 gethostname(strbuf, sizeof strbuf); 1235 p = strchr(strbuf, '.'); 1236 if (p) 1237 *p = '\0'; 1238 break; 1239 case 'H': /* '\' 'H' full hostname */ 1240 gethostname(strbuf, sizeof strbuf); 1241 break; 1242 case 'j': /* '\' 'j' number of jobs */ 1243 snprintf(strbuf, sizeof strbuf, "%d", 1244 j_njobs()); 1245 break; 1246 case 'l': /* '\' 'l' basename of tty */ 1247 p = ttyname(0); 1248 if (p) 1249 p = basename(p); 1250 if (p) 1251 strlcpy(strbuf, p, sizeof strbuf); 1252 break; 1253 case 'n': /* '\' 'n' newline */ 1254 strbuf[0] = '\n'; 1255 strbuf[1] = '\0'; 1256 totlen = 0; /* reset for prompt re-print */ 1257 sp = cp + 1; 1258 break; 1259 case 'p': /* '\' '$' $ or # */ 1260 strbuf[0] = ksheuid ? '$' : '#'; 1261 strbuf[1] = '\0'; 1262 break; 1263 case 'r': /* '\' 'r' return */ 1264 strbuf[0] = '\r'; 1265 strbuf[1] = '\0'; 1266 totlen = 0; /* reset for prompt re-print */ 1267 sp = cp + 1; 1268 break; 1269 case 's': /* '\' 's' basename $0 */ 1270 strlcpy(strbuf, kshname, sizeof strbuf); 1271 break; 1272 case 't': /* '\' 't' 24 hour HH:MM:SS */ 1273 time(&t); 1274 tm = localtime(&t); 1275 strftime(strbuf, sizeof strbuf, "%T", tm); 1276 break; 1277 case 'T': /* '\' 'T' 12 hour HH:MM:SS */ 1278 time(&t); 1279 tm = localtime(&t); 1280 strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm); 1281 break; 1282 case '@': /* '\' '@' 12 hour am/pm format */ 1283 time(&t); 1284 tm = localtime(&t); 1285 strftime(strbuf, sizeof strbuf, "%r", tm); 1286 break; 1287 case 'A': /* '\' 'A' 24 hour HH:MM */ 1288 time(&t); 1289 tm = localtime(&t); 1290 strftime(strbuf, sizeof strbuf, "%R", tm); 1291 break; 1292 case 'u': /* '\' 'u' username */ 1293 strlcpy(strbuf, username, sizeof strbuf); 1294 break; 1295 case 'v': /* '\' 'v' version (short) */ 1296 p = strchr(ksh_version, ' '); 1297 if (p) 1298 p = strchr(p + 1, ' '); 1299 if (p) { 1300 p++; 1301 strlcpy(strbuf, p, sizeof strbuf); 1302 p = strchr(strbuf, ' '); 1303 if (p) 1304 *p = '\0'; 1305 } 1306 break; 1307 case 'V': /* '\' 'V' version (long) */ 1308 strlcpy(strbuf, ksh_version, sizeof strbuf); 1309 break; 1310 case 'w': /* '\' 'w' cwd */ 1311 p = str_val(global("PWD")); 1312 n = strlen(str_val(global("HOME"))); 1313 if (strcmp(p, "/") == 0) { 1314 strlcpy(strbuf, p, sizeof strbuf); 1315 } else if (strcmp(p, str_val(global("HOME"))) == 0) { 1316 strbuf[0] = '~'; 1317 strbuf[1] = '\0'; 1318 } else if (strncmp(p, str_val(global("HOME")), n) 1319 == 0 && p[n] == '/') { 1320 snprintf(strbuf, sizeof strbuf, "~/%s", 1321 str_val(global("PWD")) + n + 1); 1322 } else 1323 strlcpy(strbuf, p, sizeof strbuf); 1324 break; 1325 case 'W': /* '\' 'W' basename(cwd) */ 1326 p = str_val(global("PWD")); 1327 strlcpy(strbuf, basename(p), sizeof strbuf); 1328 break; 1329 case '!': /* '\' '!' history line number */ 1330 snprintf(strbuf, sizeof strbuf, "%d", 1331 source->line + 1); 1332 break; 1333 case '#': /* '\' '#' command line number */ 1334 snprintf(strbuf, sizeof strbuf, "%d", 1335 source->line - source->cmd_offset + 1); 1336 break; 1337 case '0': /* '\' '#' '#' ' #' octal numeric handling */ 1338 case '1': 1339 case '2': 1340 case '3': 1341 case '4': 1342 case '5': 1343 case '6': 1344 case '7': 1345 if ((cp[1] > '7' || cp[1] < '0') || 1346 (cp[2] > '7' || cp[2] < '0')) { 1347 snprintf(strbuf, sizeof strbuf, 1348 "\\%c", *cp); 1349 break; 1350 } 1351 n = cp[0] * 8 * 8 + cp[1] * 8 + cp[2]; 1352 snprintf(strbuf, sizeof strbuf, "%c", n); 1353 cp += 2; 1354 break; 1355 case '\\': /* '\' '\' */ 1356 strbuf[0] = '\\'; 1357 strbuf[1] = '\0'; 1358 break; 1359 case '[': /* '\' '[' .... stop counting */ 1360 strbuf[0] = '\0'; 1361 counting = 0; 1362 break; 1363 case ']': /* '\' ']' restart counting */ 1364 strbuf[0] = '\0'; 1365 counting = 1; 1366 break; 1367 1368 default: 1369 snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1370 break; 1371 } 1372 cp++; 1373 1374 str = strbuf; 1375 len = strlen(str); 1376 if (ntruncate) { 1377 if (ntruncate >= len) { 1378 ntruncate -= len; 1379 continue; 1380 } 1381 str += ntruncate; 1382 len -= ntruncate; 1383 ntruncate = 0; 1384 } 1385 if (doprint) 1386 shf_write(str, len, shl_out); 1387 if (counting && !indelimit && !delimitthis) 1388 totlen += len; 1389 continue; 1390 } else if (*cp != '!') 1391 c = *cp++; 1392 else if (*++cp == '!') 1393 c = *cp++; 1394 else { 1395 char *p; 1396 1397 shf_snprintf(p = nbuf, sizeof(nbuf), "%d", 1398 source->line + 1); 1399 len = strlen(nbuf); 1400 if (ntruncate) { 1401 if (ntruncate >= len) { 1402 ntruncate -= len; 1403 continue; 1404 } 1405 p += ntruncate; 1406 len -= ntruncate; 1407 ntruncate = 0; 1408 } 1409 if (doprint) 1410 shf_write(p, len, shl_out); 1411 if (counting && !indelimit && !delimitthis) 1412 totlen += len; 1413 continue; 1414 } 1415 if (counting && ntruncate) 1416 --ntruncate; 1417 else if (doprint) { 1418 shf_putc(c, shl_out); 1419 } 1420 if (counting && !indelimit && !delimitthis) 1421 totlen++; 1422 } 1423 if (doprint) 1424 shf_flush(shl_out); 1425 if (spp) 1426 *spp = sp; 1427 return (totlen); 1428 } 1429 1430 void 1431 pprompt(const char *cp, int ntruncate) 1432 { 1433 dopprompt(cp, ntruncate, NULL, 1); 1434 } 1435 1436 int 1437 promptlen(const char *cp, const char **spp) 1438 { 1439 return dopprompt(cp, 0, spp, 0); 1440 } 1441 1442 /* Read the variable part of a ${...} expression (ie, up to but not including 1443 * the :[-+?=#%] or close-brace. 1444 */ 1445 static char * 1446 get_brace_var(XString *wsp, char *wp) 1447 { 1448 enum parse_state { 1449 PS_INITIAL, PS_SAW_HASH, PS_IDENT, 1450 PS_NUMBER, PS_VAR1, PS_END 1451 } 1452 state; 1453 char c; 1454 1455 state = PS_INITIAL; 1456 while (1) { 1457 c = getsc(); 1458 /* State machine to figure out where the variable part ends. */ 1459 switch (state) { 1460 case PS_INITIAL: 1461 if (c == '#') { 1462 state = PS_SAW_HASH; 1463 break; 1464 } 1465 /* FALLTHROUGH */ 1466 case PS_SAW_HASH: 1467 if (letter(c)) 1468 state = PS_IDENT; 1469 else if (digit(c)) 1470 state = PS_NUMBER; 1471 else if (ctype(c, C_VAR1)) 1472 state = PS_VAR1; 1473 else 1474 state = PS_END; 1475 break; 1476 case PS_IDENT: 1477 if (!letnum(c)) { 1478 state = PS_END; 1479 if (c == '[') { 1480 char *tmp, *p; 1481 1482 if (!arraysub(&tmp)) 1483 yyerror("missing ]\n"); 1484 *wp++ = c; 1485 for (p = tmp; *p; ) { 1486 Xcheck(*wsp, wp); 1487 *wp++ = *p++; 1488 } 1489 afree(tmp, ATEMP); 1490 c = getsc(); /* the ] */ 1491 } 1492 } 1493 break; 1494 case PS_NUMBER: 1495 if (!digit(c)) 1496 state = PS_END; 1497 break; 1498 case PS_VAR1: 1499 state = PS_END; 1500 break; 1501 case PS_END: /* keep gcc happy */ 1502 break; 1503 } 1504 if (state == PS_END) { 1505 *wp++ = '\0'; /* end of variable part */ 1506 ungetsc(c); 1507 break; 1508 } 1509 Xcheck(*wsp, wp); 1510 *wp++ = c; 1511 } 1512 return wp; 1513 } 1514 1515 /* 1516 * Save an array subscript - returns true if matching bracket found, false 1517 * if eof or newline was found. 1518 * (Returned string double null terminated) 1519 */ 1520 static int 1521 arraysub(char **strp) 1522 { 1523 XString ws; 1524 char *wp; 1525 char c; 1526 int depth = 1; /* we are just past the initial [ */ 1527 1528 Xinit(ws, wp, 32, ATEMP); 1529 1530 do { 1531 c = getsc(); 1532 Xcheck(ws, wp); 1533 *wp++ = c; 1534 if (c == '[') 1535 depth++; 1536 else if (c == ']') 1537 depth--; 1538 } while (depth > 0 && c && c != '\n'); 1539 1540 *wp++ = '\0'; 1541 *strp = Xclose(ws, wp); 1542 1543 return depth == 0 ? 1 : 0; 1544 } 1545 1546 /* Unget a char: handles case when we are already at the start of the buffer */ 1547 static const char * 1548 ungetsc(int c) 1549 { 1550 if (backslash_skip) 1551 backslash_skip--; 1552 /* Don't unget eof... */ 1553 if (source->str == null && c == '\0') 1554 return source->str; 1555 if (source->str > source->start) 1556 source->str--; 1557 else { 1558 Source *s; 1559 1560 s = pushs(SREREAD, source->areap); 1561 s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1562 s->start = s->str = s->ugbuf; 1563 s->next = source; 1564 source = s; 1565 } 1566 return source->str; 1567 } 1568 1569 1570 /* Called to get a char that isn't a \newline sequence. */ 1571 static int 1572 getsc_bn(void) 1573 { 1574 int c, c2; 1575 1576 if (ignore_backslash_newline) 1577 return getsc_(); 1578 1579 if (backslash_skip == 1) { 1580 backslash_skip = 2; 1581 return getsc_(); 1582 } 1583 1584 backslash_skip = 0; 1585 1586 while (1) { 1587 c = getsc_(); 1588 if (c == '\\') { 1589 if ((c2 = getsc_()) == '\n') 1590 /* ignore the \newline; get the next char... */ 1591 continue; 1592 ungetsc(c2); 1593 backslash_skip = 1; 1594 } 1595 return c; 1596 } 1597 } 1598 1599 static Lex_state * 1600 push_state_(State_info *si, Lex_state *old_end) 1601 { 1602 Lex_state *new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP); 1603 1604 new[0].ls_info.base = old_end; 1605 si->base = &new[0]; 1606 si->end = &new[STATE_BSIZE]; 1607 return &new[1]; 1608 } 1609 1610 static Lex_state * 1611 pop_state_(State_info *si, Lex_state *old_end) 1612 { 1613 Lex_state *old_base = si->base; 1614 1615 si->base = old_end->ls_info.base - STATE_BSIZE; 1616 si->end = old_end->ls_info.base; 1617 1618 afree(old_base, ATEMP); 1619 1620 return si->base + STATE_BSIZE - 1; 1621 } 1622