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