1 /* 2 * sh.lex.c: Lexical analysis into tokens 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include "ed.h" 34 35 #include <assert.h> 36 /* #define DEBUG_INP */ 37 /* #define DEBUG_SEEK */ 38 39 /* 40 * C shell 41 */ 42 43 #define FLAG_G 1 44 #define FLAG_A 2 45 /* 46 * These lexical routines read input and form lists of words. 47 * There is some involved processing here, because of the complications 48 * of input buffering, and especially because of history substitution. 49 */ 50 static Char *word (int); 51 static eChar getC1 (int); 52 static void getdol (void); 53 static void getexcl (Char); 54 static struct Hist *findev (Char *, int); 55 static void setexclp (Char *); 56 static eChar bgetc (void); 57 static void balloc (int); 58 static void bfree (void); 59 static struct wordent *gethent (Char); 60 static int matchs (const Char *, const Char *); 61 static int getsel (int *, int *, int); 62 static struct wordent *getsub (struct wordent *); 63 static Char *subword (Char *, Char, int *, size_t *); 64 static struct wordent *dosub (Char, struct wordent *, int); 65 66 /* 67 * Peekc is a peek character for getC, peekread for readc. 68 * There is a subtlety here in many places... history routines 69 * will read ahead and then insert stuff into the input stream. 70 * If they push back a character then they must push it behind 71 * the text substituted by the history substitution. On the other 72 * hand in several places we need 2 peek characters. To make this 73 * all work, the history routines read with getC, and make use both 74 * of ungetC and unreadc. The key observation is that the state 75 * of getC at the call of a history reference is such that calls 76 * to getC from the history routines will always yield calls of 77 * readc, unless this peeking is involved. That is to say that during 78 * getexcl the variables lap, exclp, and exclnxt are all zero. 79 * 80 * Getdol invokes history substitution, hence the extra peek, peekd, 81 * which it can ungetD to be before history substitutions. 82 */ 83 static Char peekc = 0, peekd = 0; 84 static Char peekread = 0; 85 86 /* (Tail of) current word from ! subst */ 87 static Char *exclp = NULL; 88 89 /* The rest of the ! subst words */ 90 static struct wordent *exclnxt = NULL; 91 92 /* Count of remaining words in ! subst */ 93 static int exclc = 0; 94 95 /* "Globp" for alias resubstitution */ 96 int aret = TCSH_F_SEEK; 97 98 /* 99 * Labuf implements a general buffer for lookahead during lexical operations. 100 * Text which is to be placed in the input stream can be stuck here. 101 * We stick parsed ahead $ constructs during initial input, 102 * process id's from `$$', and modified variable values (from qualifiers 103 * during expansion in sh.dol.c) here. 104 */ 105 struct Strbuf labuf; /* = Strbuf_INIT; */ 106 107 /* 108 * Lex returns to its caller not only a wordlist (as a "var" parameter) 109 * but also whether a history substitution occurred. This is used in 110 * the main (process) routine to determine whether to echo, and also 111 * when called by the alias routine to determine whether to keep the 112 * argument list. 113 */ 114 static int hadhist = 0; 115 116 /* 117 * Avoid alias expansion recursion via \!# 118 */ 119 int hleft; 120 121 struct Strbuf histline; /* = Strbuf_INIT; last line input */ 122 123 int histvalid = 0; /* is histline valid */ 124 125 static Char getCtmp; 126 127 #define getC(f) (((getCtmp = peekc) != '\0') ? (peekc = 0, (eChar)getCtmp) : getC1(f)) 128 #define ungetC(c) peekc = (Char) c 129 #define ungetD(c) peekd = (Char) c 130 131 /* Use Htime to store timestamps picked up from history file for enthist() 132 * if reading saved history (sg) 133 */ 134 time_t Htime = (time_t)0; 135 static time_t a2time_t (Char *); 136 137 /* 138 * special parsing rules apply for source -h 139 */ 140 extern int enterhist; 141 extern int postcmd_active; 142 143 int 144 lex(struct wordent *hp) 145 { 146 struct wordent *wdp; 147 eChar c; 148 int parsehtime = enterhist; 149 int toolong = 0; 150 151 histvalid = 0; 152 histline.len = 0; 153 154 if (!postcmd_active) 155 btell(&lineloc); 156 hp->next = hp->prev = hp; 157 hp->word = STRNULL; 158 hadhist = 0; 159 do 160 c = readc(0); 161 while (c == ' ' || c == '\t'); 162 if (c == (eChar)HISTSUB && intty) 163 /* ^lef^rit from tty is short !:s^lef^rit */ 164 getexcl(c); 165 else 166 unreadc(c); 167 cleanup_push(hp, lex_cleanup); 168 wdp = hp; 169 /* 170 * The following loop is written so that the links needed by freelex will 171 * be ready and rarin to go even if it is interrupted. 172 */ 173 do { 174 struct wordent *new; 175 176 new = xmalloc(sizeof(*new)); 177 new->word = NULL; 178 new->prev = wdp; 179 new->next = hp; 180 wdp->next = new; 181 hp->prev = new; 182 wdp = new; 183 wdp->word = word(parsehtime); 184 parsehtime = 0; 185 if (enterhist && toolong++ > 10 * 1024) { 186 stderror(ERR_LTOOLONG); 187 } 188 } while (wdp->word[0] != '\n'); 189 cleanup_ignore(hp); 190 cleanup_until(hp); 191 Strbuf_terminate(&histline); 192 if (histline.len != 0 && histline.s[histline.len - 1] == '\n') 193 histline.s[histline.len - 1] = '\0'; 194 histvalid = 1; 195 196 return (hadhist); 197 } 198 199 static time_t 200 a2time_t(Char *wordx) 201 { 202 /* Attempt to distinguish timestamps from other possible entries. 203 * Format: "+NNNNNNNNNN" (10 digits, left padded with ascii '0') */ 204 205 time_t ret; 206 Char *s; 207 int ct; 208 209 if (!wordx || *(s = wordx) != '+') 210 return (time_t)0; 211 212 for (++s, ret = 0, ct = 0; *s; ++s, ++ct) { 213 if (!isdigit((unsigned char)*s)) 214 return (time_t)0; 215 ret = ret * 10 + (time_t)((unsigned char)*s - '0'); 216 } 217 218 if (ct != 10) 219 return (time_t)0; 220 221 return ret; 222 } 223 224 void 225 prlex(struct wordent *sp0) 226 { 227 struct wordent *sp = sp0->next; 228 229 for (;;) { 230 xprintf("%S", sp->word); 231 sp = sp->next; 232 if (sp == sp0) 233 break; 234 if (sp->word[0] != '\n') 235 xputchar(' '); 236 } 237 } 238 239 void 240 copylex(struct wordent *hp, struct wordent *fp) 241 { 242 struct wordent *wdp; 243 244 wdp = hp; 245 fp = fp->next; 246 do { 247 struct wordent *new; 248 249 new = xmalloc(sizeof(*new)); 250 new->word = NULL; 251 new->prev = wdp; 252 new->next = hp; 253 wdp->next = new; 254 hp->prev = new; 255 wdp = new; 256 wdp->word = Strsave(fp->word); 257 fp = fp->next; 258 } while (wdp->word[0] != '\n'); 259 } 260 261 void 262 initlex(struct wordent *vp) 263 { 264 vp->word = STRNULL; 265 vp->prev = vp; 266 vp->next = vp; 267 } 268 269 void 270 freelex(struct wordent *vp) 271 { 272 struct wordent *fp; 273 274 while (vp->next != vp) { 275 fp = vp->next; 276 vp->next = fp->next; 277 xfree(fp->word); 278 xfree(fp); 279 } 280 vp->prev = vp; 281 } 282 283 void 284 lex_cleanup(void *xvp) 285 { 286 struct wordent *vp; 287 288 vp = xvp; 289 freelex(vp); 290 } 291 292 static Char * 293 word(int parsehtime) 294 { 295 eChar c, c1; 296 struct Strbuf wbuf = Strbuf_INIT; 297 Char hbuf[12]; 298 int h; 299 int dolflg; 300 int toolong = 0; 301 302 cleanup_push(&wbuf, Strbuf_cleanup); 303 loop: 304 if (enterhist && toolong++ > 256 * 1024) { 305 stderror(ERR_WTOOLONG); 306 } 307 while ((c = getC(DOALL)) == ' ' || c == '\t') 308 continue; 309 if (cmap(c, _META | _ESC)) 310 switch (c) { 311 case '&': 312 case '|': 313 case '<': 314 case '>': 315 Strbuf_append1(&wbuf, c); 316 c1 = getC(DOALL); 317 if (c1 == c) 318 Strbuf_append1(&wbuf, c1); 319 else 320 ungetC(c1); 321 goto ret; 322 323 case '#': 324 if (intty || (enterhist && !parsehtime)) 325 break; 326 c = 0; 327 h = 0; 328 do { 329 c1 = c; 330 c = getC(0); 331 if (h < 11 && parsehtime) 332 hbuf[h++] = c; 333 } while (c != '\n'); 334 if (parsehtime) { 335 hbuf[11] = '\0'; 336 Htime = a2time_t(hbuf); 337 } 338 if (c1 == '\\') 339 goto loop; 340 /*FALLTHROUGH*/ 341 342 case ';': 343 case '(': 344 case ')': 345 case '\n': 346 Strbuf_append1(&wbuf, c); 347 goto ret; 348 349 case '\\': 350 c = getC(0); 351 if (c == '\n') { 352 if (onelflg == 1) 353 onelflg = 2; 354 goto loop; 355 } 356 if (c != (eChar)HIST) 357 Strbuf_append1(&wbuf, '\\'); 358 c |= QUOTE; 359 default: 360 break; 361 } 362 c1 = 0; 363 dolflg = DOALL; 364 for (;;) { 365 if (enterhist && toolong++ > 256 * 1024) { 366 stderror(ERR_WTOOLONG); 367 } 368 if (c1) { 369 if (c == c1) { 370 c1 = 0; 371 dolflg = DOALL; 372 } 373 else if (c == '\\') { 374 c = getC(0); 375 /* 376 * PWP: this is dumb, but how all of the other shells work. If \ quotes 377 * a character OUTSIDE of a set of ''s, why shouldn't it quote EVERY 378 * following character INSIDE a set of ''s. 379 * 380 * Actually, all I really want to be able to say is 'foo\'bar' --> foo'bar 381 */ 382 if (c == (eChar)HIST) 383 c |= QUOTE; 384 else { 385 if (bslash_quote && 386 ((c == '\'') || (c == '"') || 387 (c == '\\') || (c == '$'))) { 388 c |= QUOTE; 389 } 390 else { 391 if (c == '\n') 392 /* 393 * if (c1 == '`') c = ' '; else 394 */ 395 c |= QUOTE; 396 ungetC(c); 397 c = '\\' | QUOTE; 398 } 399 } 400 } 401 else if (c == '\n') { 402 seterror(ERR_UNMATCHED, c1); 403 ungetC(c); 404 break; 405 } 406 } 407 else if (cmap(c, _META | _QF | _QB | _ESC)) { 408 if (c == '\\') { 409 c = getC(0); 410 if (c == '\n') { 411 if (onelflg == 1) 412 onelflg = 2; 413 break; 414 } 415 if (c != (eChar)HIST) 416 Strbuf_append1(&wbuf, '\\'); 417 c |= QUOTE; 418 } 419 else if (cmap(c, _QF | _QB)) { /* '"` */ 420 c1 = c; 421 dolflg = c == '"' ? DOALL : DOEXCL; 422 } 423 else if (c != '#' || (!intty && !enterhist)) { 424 ungetC(c); 425 break; 426 } 427 } 428 Strbuf_append1(&wbuf, c); 429 c = getC(dolflg); 430 } 431 ret: 432 cleanup_ignore(&wbuf); 433 cleanup_until(&wbuf); 434 return Strbuf_finish(&wbuf); 435 } 436 437 static eChar 438 getC1(int flag) 439 { 440 eChar c; 441 442 for (;;) { 443 if ((c = peekc) != 0) { 444 peekc = 0; 445 return (c); 446 } 447 if (lap < labuf.len) { 448 c = labuf.s[lap++]; 449 if (cmap(c, _META | _QF | _QB)) 450 c |= QUOTE; 451 return (c); 452 } 453 if ((c = peekd) != 0) { 454 peekd = 0; 455 return (c); 456 } 457 if (exclp) { 458 if ((c = *exclp++) != 0) 459 return (c); 460 if (exclnxt && --exclc >= 0) { 461 exclnxt = exclnxt->next; 462 setexclp(exclnxt->word); 463 return (' '); 464 } 465 exclp = 0; 466 exclnxt = 0; 467 /* this will throw away the dummy history entries */ 468 savehist(NULL, 0); 469 470 } 471 if (exclnxt) { 472 exclnxt = exclnxt->next; 473 if (--exclc < 0) 474 exclnxt = 0; 475 else 476 setexclp(exclnxt->word); 477 continue; 478 } 479 c = readc(1); 480 481 /* Catch EOF in the middle of a line. (An EOF at the beginning of 482 * a line would have been processed by the readc(0) in lex().) */ 483 if (c == CHAR_ERR) 484 c = '\n'; 485 486 if (c == '$' && (flag & DODOL)) { 487 getdol(); 488 continue; 489 } 490 if (c == (eChar)HIST && (flag & DOEXCL)) { 491 getexcl(0); 492 continue; 493 } 494 break; 495 } 496 return (c); 497 } 498 499 static void 500 getdol(void) 501 { 502 struct Strbuf name = Strbuf_INIT; 503 eChar c; 504 eChar sc; 505 int special = 0; 506 507 c = sc = getC(DOEXCL); 508 if (any("\t \n", c)) { 509 ungetD(c); 510 ungetC('$' | QUOTE); 511 return; 512 } 513 cleanup_push(&name, Strbuf_cleanup); 514 Strbuf_append1(&name, '$'); 515 if (c == '{') 516 Strbuf_append1(&name, c), c = getC(DOEXCL); 517 if (c == '#' || c == '?' || c == '%') 518 special++, Strbuf_append1(&name, c), c = getC(DOEXCL); 519 Strbuf_append1(&name, c); 520 switch (c) { 521 522 case '<': 523 case '$': 524 case '!': 525 if (special) 526 seterror(ERR_SPDOLLT); 527 goto end; 528 529 case '\n': 530 ungetD(c); 531 name.len--; 532 if (!special) 533 seterror(ERR_NEWLINE); 534 goto end; 535 536 case '*': 537 if (special) 538 seterror(ERR_SPSTAR); 539 goto end; 540 541 default: 542 if (Isdigit(c)) { 543 #ifdef notdef 544 /* let $?0 pass for now */ 545 if (special) { 546 seterror(ERR_DIGIT); 547 goto end; 548 } 549 #endif 550 while ((c = getC(DOEXCL)) != 0) { 551 if (!Isdigit(c)) 552 break; 553 Strbuf_append1(&name, c); 554 } 555 } 556 else if (letter(c)) { 557 while ((c = getC(DOEXCL)) != 0) { 558 /* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */ 559 if (!letter(c) && !Isdigit(c)) 560 break; 561 Strbuf_append1(&name, c); 562 } 563 } 564 else { 565 if (!special) 566 seterror(ERR_VARILL); 567 else { 568 ungetD(c); 569 name.len--; 570 } 571 goto end; 572 } 573 break; 574 } 575 if (c == '[') { 576 Strbuf_append1(&name, c); 577 do { 578 /* 579 * Michael Greim: Allow $ expansion to take place in selector 580 * expressions. (limits the number of characters returned) 581 */ 582 c = getC(DOEXCL | DODOL); 583 if (c == '\n') { 584 ungetD(c); 585 name.len--; 586 seterror(ERR_NLINDEX); 587 goto end; 588 } 589 Strbuf_append1(&name, c); 590 } while (c != ']'); 591 c = getC(DOEXCL); 592 } 593 if (c == ':') { 594 /* 595 * if the :g modifier is followed by a newline, then error right away! 596 * -strike 597 */ 598 599 int gmodflag = 0, amodflag = 0; 600 601 do { 602 Strbuf_append1(&name, c), c = getC(DOEXCL), gmodflag = 0, amodflag = 0; 603 if (c == 'g' || c == 'a') { 604 if (c == 'g') 605 gmodflag++; 606 else 607 amodflag++; 608 Strbuf_append1(&name, c); c = getC(DOEXCL); 609 } 610 if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) { 611 if (c == 'g') 612 gmodflag++; 613 else 614 amodflag++; 615 Strbuf_append1(&name, c); c = getC(DOEXCL); 616 } 617 Strbuf_append1(&name, c); 618 /* scan s// [eichin:19910926.0512EST] */ 619 if (c == 's') { 620 int delimcnt = 2; 621 eChar delim = getC(0); 622 623 Strbuf_append1(&name, delim); 624 if (!delim || letter(delim) 625 || Isdigit(delim) || any(" \t\n", delim)) { 626 seterror(ERR_BADSUBST); 627 break; 628 } 629 while ((c = getC(0)) != CHAR_ERR) { 630 Strbuf_append1(&name, c); 631 if (c == delim) delimcnt--; 632 if (!delimcnt) break; 633 } 634 if (delimcnt) { 635 seterror(ERR_BADSUBST); 636 break; 637 } 638 c = 's'; 639 } 640 if (!any(TCSH_MODIFIERS, c)) { 641 if ((amodflag || gmodflag) && c == '\n') 642 stderror(ERR_VARSYN); /* strike */ 643 seterror(ERR_BADMOD, c); 644 goto end; 645 } 646 } 647 while ((c = getC(DOEXCL)) == ':'); 648 ungetD(c); 649 } 650 else 651 ungetD(c); 652 if (sc == '{') { 653 c = getC(DOEXCL); 654 if (c != '}') { 655 ungetD(c); 656 seterror(ERR_MISSING, '}'); 657 goto end; 658 } 659 Strbuf_append1(&name, c); 660 } 661 end: 662 cleanup_ignore(&name); 663 cleanup_until(&name); 664 addla(Strbuf_finish(&name)); 665 } 666 667 /* xfree()'s its argument */ 668 void 669 addla(Char *cp) 670 { 671 static struct Strbuf buf; /* = Strbuf_INIT; */ 672 673 buf.len = 0; 674 Strbuf_appendn(&buf, labuf.s + lap, labuf.len - lap); 675 labuf.len = 0; 676 Strbuf_append(&labuf, cp); 677 Strbuf_terminate(&labuf); 678 Strbuf_appendn(&labuf, buf.s, buf.len); 679 xfree(cp); 680 lap = 0; 681 } 682 683 /* left-hand side of last :s or search string of last ?event? */ 684 static struct Strbuf lhsb; /* = Strbuf_INIT; */ 685 static struct Strbuf slhs; /* = Strbuf_INIT; left-hand side of last :s */ 686 static struct Strbuf rhsb; /* = Strbuf_INIT; right-hand side of last :s */ 687 static int quesarg; 688 689 static void 690 getexcl(Char sc) 691 { 692 struct wordent *hp, *ip; 693 int left, right, dol; 694 eChar c; 695 696 if (sc == 0) { 697 c = getC(0); 698 if (c == '{') 699 sc = (Char) c; 700 else 701 ungetC(c); 702 } 703 quesarg = -1; 704 705 lastev = eventno; 706 hp = gethent(sc); 707 if (hp == 0) 708 return; 709 hadhist = 1; 710 dol = 0; 711 if (hp == alhistp) 712 for (ip = hp->next->next; ip != alhistt; ip = ip->next) 713 dol++; 714 else 715 for (ip = hp->next->next; ip != hp->prev; ip = ip->next) 716 dol++; 717 left = 0, right = dol; 718 if (sc == HISTSUB && HISTSUB != '\0') { 719 ungetC('s'), unreadc(HISTSUB), c = ':'; 720 goto subst; 721 } 722 c = getC(0); 723 if (!any(":^$*-%", c)) 724 goto subst; 725 left = right = -1; 726 if (c == ':') { 727 c = getC(0); 728 unreadc(c); 729 if (letter(c) || c == '&') { 730 c = ':'; 731 left = 0, right = dol; 732 goto subst; 733 } 734 } 735 else 736 ungetC(c); 737 if (!getsel(&left, &right, dol)) 738 return; 739 c = getC(0); 740 if (c == '*') 741 ungetC(c), c = '-'; 742 if (c == '-') { 743 if (!getsel(&left, &right, dol)) 744 return; 745 c = getC(0); 746 } 747 subst: 748 exclc = right - left + 1; 749 while (--left >= 0) 750 hp = hp->next; 751 if ((sc == HISTSUB && HISTSUB != '\0') || c == ':') { 752 do { 753 hp = getsub(hp); 754 c = getC(0); 755 } while (c == ':'); 756 } 757 unreadc(c); 758 if (sc == '{') { 759 c = getC(0); 760 if (c != '}') 761 seterror(ERR_BADBANG); 762 } 763 exclnxt = hp; 764 } 765 766 static struct wordent * 767 getsub(struct wordent *en) 768 { 769 eChar delim; 770 eChar c; 771 eChar sc; 772 int global; 773 774 do { 775 exclnxt = 0; 776 global = 0; 777 sc = c = getC(0); 778 while (c == 'g' || c == 'a') { 779 global |= (c == 'g') ? FLAG_G : FLAG_A; 780 sc = c = getC(0); 781 } 782 783 switch (c) { 784 case 'p': 785 justpr++; 786 return (en); 787 788 case 'x': 789 case 'q': 790 global |= FLAG_G; 791 /*FALLTHROUGH*/ 792 793 case 'h': 794 case 'r': 795 case 't': 796 case 'e': 797 case 'u': 798 case 'l': 799 break; 800 801 case '&': 802 if (slhs.len == 0) { 803 seterror(ERR_NOSUBST); 804 return (en); 805 } 806 lhsb.len = 0; 807 Strbuf_append(&lhsb, slhs.s); 808 Strbuf_terminate(&lhsb); 809 break; 810 811 #ifdef notdef 812 case '~': 813 if (lhsb.len == 0) 814 goto badlhs; 815 break; 816 #endif 817 818 case 's': 819 delim = getC(0); 820 if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) { 821 unreadc(delim); 822 lhsb.len = 0; 823 seterror(ERR_BADSUBST); 824 return (en); 825 } 826 Strbuf_terminate(&lhsb); 827 lhsb.len = 0; 828 for (;;) { 829 c = getC(0); 830 if (c == '\n') { 831 unreadc(c); 832 break; 833 } 834 if (c == delim) 835 break; 836 if (c == '\\') { 837 c = getC(0); 838 if (c != delim && c != '\\') 839 Strbuf_append1(&lhsb, '\\'); 840 } 841 Strbuf_append1(&lhsb, c); 842 } 843 if (lhsb.len != 0) 844 Strbuf_terminate(&lhsb); 845 else if (lhsb.s[0] == 0) { 846 seterror(ERR_LHS); 847 return (en); 848 } else 849 lhsb.len = Strlen(lhsb.s); /* lhsb.s wasn't changed */ 850 rhsb.len = 0; 851 for (;;) { 852 c = getC(0); 853 if (c == '\n') { 854 unreadc(c); 855 break; 856 } 857 if (c == delim) 858 break; 859 if (c == '\\') { 860 c = getC(0); 861 if (c != delim /* && c != '~' */ ) 862 Strbuf_append1(&rhsb, '\\'); 863 } 864 Strbuf_append1(&rhsb, c); 865 } 866 Strbuf_terminate(&rhsb); 867 break; 868 869 default: 870 if (c == '\n') 871 unreadc(c); 872 seterror(ERR_BADBANGMOD, (int)c); 873 return (en); 874 } 875 slhs.len = 0; 876 if (lhsb.s != NULL && lhsb.len != 0) 877 Strbuf_append(&slhs, lhsb.s); 878 Strbuf_terminate(&slhs); 879 if (exclc) 880 en = dosub(sc, en, global); 881 } 882 while ((c = getC(0)) == ':'); 883 unreadc(c); 884 return (en); 885 } 886 887 /* 888 * 889 * From Beto Appleton (beto@aixwiz.austin.ibm.com) 890 * 891 * when using history substitution, and the variable 892 * 'history' is set to a value higher than 1000, 893 * the shell might either freeze (hang) or core-dump. 894 * We raise the limit to 50000000 895 */ 896 897 #define HIST_PURGE -50000000 898 static struct wordent * 899 dosub(Char sc, struct wordent *en, int global) 900 { 901 struct wordent lexi; 902 int didsub = 0, didone = 0; 903 struct wordent *hp = &lexi; 904 struct wordent *wdp; 905 int i = exclc; 906 struct Hist *hst; 907 908 wdp = hp; 909 while (--i >= 0) { 910 struct wordent *new = xcalloc(1, sizeof *wdp); 911 912 new->word = 0; 913 new->prev = wdp; 914 new->next = hp; 915 wdp->next = new; 916 wdp = new; 917 en = en->next; 918 if (en->word) { 919 Char *tword, *otword; 920 921 if ((global & FLAG_G) || didsub == 0) { 922 size_t pos; 923 924 pos = 0; 925 tword = subword(en->word, sc, &didone, &pos); 926 if (didone) 927 didsub = 1; 928 if (global & FLAG_A) { 929 while (didone && tword != STRNULL) { 930 otword = tword; 931 tword = subword(otword, sc, &didone, &pos); 932 if (Strcmp(tword, otword) == 0) { 933 xfree(otword); 934 break; 935 } 936 else 937 xfree(otword); 938 } 939 } 940 } 941 else 942 tword = Strsave(en->word); 943 wdp->word = tword; 944 } 945 } 946 if (didsub == 0) 947 seterror(ERR_MODFAIL); 948 hp->prev = wdp; 949 /* 950 * ANSI mode HP/UX compiler chokes on 951 * return &enthist(HIST_PURGE, &lexi, 0)->Hlex; 952 */ 953 hst = enthist(HIST_PURGE, &lexi, 0, 0, -1); 954 return &(hst->Hlex); 955 } 956 957 /* Return a newly allocated result of one modification of CP using the 958 operation TYPE. Set ADID to 1 if a modification was performed. 959 If TYPE == 's', perform substitutions only from *START_POS on and set 960 *START_POS to the position of next substitution attempt. */ 961 static Char * 962 subword(Char *cp, Char type, int *adid, size_t *start_pos) 963 { 964 Char *wp; 965 const Char *mp, *np; 966 967 switch (type) { 968 969 case 'r': 970 case 'e': 971 case 'h': 972 case 't': 973 case 'q': 974 case 'x': 975 case 'u': 976 case 'l': 977 wp = domod(cp, type); 978 if (wp == 0) { 979 *adid = 0; 980 return (Strsave(cp)); 981 } 982 *adid = 1; 983 return (wp); 984 985 default: 986 for (mp = cp + *start_pos; *mp; mp++) { 987 if (matchs(mp, lhsb.s)) { 988 struct Strbuf wbuf = Strbuf_INIT; 989 990 Strbuf_appendn(&wbuf, cp, mp - cp); 991 for (np = rhsb.s; *np; np++) 992 switch (*np) { 993 994 case '\\': 995 if (np[1] == '&') 996 np++; 997 /* fall into ... */ 998 999 default: 1000 Strbuf_append1(&wbuf, *np); 1001 continue; 1002 1003 case '&': 1004 Strbuf_append(&wbuf, lhsb.s); 1005 continue; 1006 } 1007 *start_pos = wbuf.len; 1008 Strbuf_append(&wbuf, mp + lhsb.len); 1009 *adid = 1; 1010 return Strbuf_finish(&wbuf); 1011 } 1012 } 1013 *adid = 0; 1014 return (Strsave(cp)); 1015 } 1016 } 1017 1018 Char * 1019 domod(Char *cp, Char type) 1020 { 1021 Char *wp, *xp; 1022 int c; 1023 1024 switch (type) { 1025 case 'Q': 1026 if (*cp == '\0') 1027 return Strsave(STRQNULL); 1028 /*FALLTHROUGH*/ 1029 case 'q': 1030 case 'x': 1031 wp = Strsave(cp); 1032 for (xp = wp; (c = *xp) != 0; xp++) 1033 if ((c != ' ' && c != '\t') || type == 'q' || type == 'Q') 1034 *xp |= QUOTE; 1035 return (wp); 1036 1037 case 'l': 1038 wp = NLSChangeCase(cp, 1); 1039 return wp ? wp : Strsave(cp); 1040 1041 case 'u': 1042 wp = NLSChangeCase(cp, 0); 1043 return wp ? wp : Strsave(cp); 1044 1045 case 'h': 1046 case 't': 1047 wp = Strrchr(cp, '/'); 1048 if (wp == NULL) 1049 return NULL; 1050 if (type == 't') 1051 xp = Strsave(wp + 1); 1052 else 1053 xp = Strnsave(cp, wp - cp); 1054 return (xp); 1055 1056 case 'e': 1057 case 'r': 1058 wp = Strend(cp); 1059 for (wp--; wp >= cp && *wp != '/'; wp--) 1060 if (*wp == '.') { 1061 if (type == 'e') 1062 xp = Strsave(wp + 1); 1063 else 1064 xp = Strnsave(cp, wp - cp); 1065 return (xp); 1066 } 1067 return (Strsave(type == 'e' ? STRNULL : cp)); 1068 1069 default: 1070 break; 1071 } 1072 return (0); 1073 } 1074 1075 static int 1076 matchs(const Char *str, const Char *pat) 1077 { 1078 while (*str && *pat && *str == *pat) 1079 str++, pat++; 1080 return (*pat == 0); 1081 } 1082 1083 static int 1084 getsel(int *al, int *ar, int dol) 1085 { 1086 eChar c = getC(0); 1087 int i; 1088 int first = *al < 0; 1089 1090 switch (c) { 1091 1092 case '%': 1093 if (quesarg == -1) { 1094 seterror(ERR_BADBANGARG); 1095 return (0); 1096 } 1097 if (*al < 0) 1098 *al = quesarg; 1099 *ar = quesarg; 1100 break; 1101 1102 case '-': 1103 if (*al < 0) { 1104 *al = 0; 1105 *ar = dol - 1; 1106 unreadc(c); 1107 } 1108 return (1); 1109 1110 case '^': 1111 if (*al < 0) 1112 *al = 1; 1113 *ar = 1; 1114 break; 1115 1116 case '$': 1117 if (*al < 0) 1118 *al = dol; 1119 *ar = dol; 1120 break; 1121 1122 case '*': 1123 if (*al < 0) 1124 *al = 1; 1125 *ar = dol; 1126 if (*ar < *al) { 1127 *ar = 0; 1128 *al = 1; 1129 return (1); 1130 } 1131 break; 1132 1133 default: 1134 if (Isdigit(c)) { 1135 i = 0; 1136 while (Isdigit(c)) { 1137 i = i * 10 + c - '0'; 1138 c = getC(0); 1139 } 1140 if (i < 0) 1141 i = dol + 1; 1142 if (*al < 0) 1143 *al = i; 1144 *ar = i; 1145 } 1146 else if (*al < 0) 1147 *al = 0, *ar = dol; 1148 else 1149 *ar = dol - 1; 1150 unreadc(c); 1151 break; 1152 } 1153 if (first) { 1154 c = getC(0); 1155 unreadc(c); 1156 if (any("-$*", c)) 1157 return (1); 1158 } 1159 if (*al > *ar || *ar > dol) { 1160 seterror(ERR_BADBANGARG); 1161 return (0); 1162 } 1163 return (1); 1164 1165 } 1166 1167 static struct wordent * 1168 gethent(Char sc) 1169 { 1170 struct Hist *hp; 1171 Char *np; 1172 eChar c; 1173 int event; 1174 int back = 0; 1175 1176 c = (sc == HISTSUB && HISTSUB != '\0') ? (eChar)HIST : getC(0); 1177 if (c == (eChar)HIST) { 1178 if (alhistp) 1179 return (alhistp); 1180 event = eventno; 1181 } 1182 else 1183 switch (c) { 1184 1185 case ':': 1186 case '^': 1187 case '$': 1188 case '*': 1189 case '%': 1190 ungetC(c); 1191 if (lastev == eventno && alhistp) 1192 return (alhistp); 1193 event = lastev; 1194 break; 1195 1196 case '#': /* !# is command being typed in (mrh) */ 1197 if (--hleft == 0) { 1198 seterror(ERR_HISTLOOP); 1199 return (0); 1200 } 1201 else 1202 return (¶ml); 1203 /* NOTREACHED */ 1204 1205 case '-': 1206 back = 1; 1207 c = getC(0); 1208 /* FALLSTHROUGH */ 1209 1210 default: 1211 if (any("(=~", c)) { 1212 unreadc(c); 1213 ungetC(HIST); 1214 return (0); 1215 } 1216 Strbuf_terminate(&lhsb); 1217 lhsb.len = 0; 1218 event = 0; 1219 while (!cmap(c, _ESC | _META | _QF | _QB) && !any("^*-%${}:#", c)) { 1220 if (event != -1 && Isdigit(c)) 1221 event = event * 10 + c - '0'; 1222 else 1223 event = -1; 1224 Strbuf_append1(&lhsb, c); 1225 c = getC(0); 1226 } 1227 unreadc(c); 1228 if (lhsb.len == 0) { 1229 lhsb.len = Strlen(lhsb.s); /* lhsb.s wasn't changed */ 1230 ungetC(HIST); 1231 return (0); 1232 } 1233 Strbuf_terminate(&lhsb); 1234 if (event != -1) { 1235 /* 1236 * History had only digits 1237 */ 1238 if (back) 1239 event = eventno + (alhistp == 0) - event; 1240 break; 1241 } 1242 if (back) { 1243 Strbuf_append1(&lhsb, '\0'); /* Allocate space */ 1244 Strbuf_terminate(&lhsb); 1245 memmove(lhsb.s + 1, lhsb.s, (lhsb.len - 1) * sizeof (*lhsb.s)); 1246 lhsb.s[0] = '-'; 1247 } 1248 hp = findev(lhsb.s, 0); 1249 if (hp) 1250 lastev = hp->Hnum; 1251 return (&hp->Hlex); 1252 1253 case '?': 1254 Strbuf_terminate(&lhsb); 1255 lhsb.len = 0; 1256 for (;;) { 1257 c = getC(0); 1258 if (c == '\n') { 1259 unreadc(c); 1260 break; 1261 } 1262 if (c == '?') 1263 break; 1264 Strbuf_append1(&lhsb, c); 1265 } 1266 if (lhsb.len == 0) { 1267 lhsb.len = Strlen(lhsb.s); /* lhsb.s wasn't changed */ 1268 if (lhsb.len == 0) { 1269 seterror(ERR_NOSEARCH); 1270 return (0); 1271 } 1272 } 1273 else 1274 Strbuf_terminate(&lhsb); 1275 hp = findev(lhsb.s, 1); 1276 if (hp) 1277 lastev = hp->Hnum; 1278 return (&hp->Hlex); 1279 } 1280 1281 for (hp = Histlist.Hnext; hp; hp = hp->Hnext) 1282 if (hp->Hnum == event) { 1283 hp->Href = eventno; 1284 lastev = hp->Hnum; 1285 return (&hp->Hlex); 1286 } 1287 np = putn((tcsh_number_t)event); 1288 seterror(ERR_NOEVENT, short2str(np)); 1289 xfree(np); 1290 return (0); 1291 } 1292 1293 static struct Hist * 1294 findev(Char *cp, int anyarg) 1295 { 1296 struct Hist *hp; 1297 1298 for (hp = Histlist.Hnext; hp; hp = hp->Hnext) { 1299 Char *dp; 1300 Char *p, *q; 1301 struct wordent *lp = hp->Hlex.next; 1302 int argno = 0; 1303 1304 /* 1305 * The entries added by alias substitution don't have a newline but do 1306 * have a negative event number. Savehist() trims off these entries, 1307 * but it happens before alias expansion, too early to delete those 1308 * from the previous command. 1309 */ 1310 if (hp->Hnum < 0) 1311 continue; 1312 if (lp->word[0] == '\n') 1313 continue; 1314 if (!anyarg) { 1315 p = cp; 1316 q = lp->word; 1317 do 1318 if (!*p) 1319 return (hp); 1320 while (*p++ == *q++); 1321 continue; 1322 } 1323 do { 1324 for (dp = lp->word; *dp; dp++) { 1325 p = cp; 1326 q = dp; 1327 do 1328 if (!*p) { 1329 quesarg = argno; 1330 return (hp); 1331 } 1332 while (*p++ == *q++); 1333 } 1334 lp = lp->next; 1335 argno++; 1336 } while (lp->word[0] != '\n'); 1337 } 1338 seterror(ERR_NOEVENT, short2str(cp)); 1339 return (0); 1340 } 1341 1342 1343 static void 1344 setexclp(Char *cp) 1345 { 1346 if (cp && cp[0] == '\n') 1347 return; 1348 exclp = cp; 1349 } 1350 1351 void 1352 unreadc(Char c) 1353 { 1354 peekread = (Char) c; 1355 } 1356 1357 eChar 1358 readc(int wanteof) 1359 { 1360 eChar c; 1361 static int sincereal; /* Number of real EOFs we've seen */ 1362 1363 #ifdef DEBUG_INP 1364 xprintf("readc\n"); 1365 #endif 1366 if ((c = peekread) != 0) { 1367 peekread = 0; 1368 return (c); 1369 } 1370 1371 top: 1372 aret = TCSH_F_SEEK; 1373 if (alvecp) { 1374 arun = 1; 1375 #ifdef DEBUG_INP 1376 xprintf("alvecp %c\n", *alvecp & 0xff); 1377 #endif 1378 aret = TCSH_A_SEEK; 1379 if ((c = *alvecp++) != 0) 1380 return (c); 1381 if (alvec && *alvec) { 1382 alvecp = *alvec++; 1383 return (' '); 1384 } 1385 else { 1386 alvecp = NULL; 1387 aret = TCSH_F_SEEK; 1388 return('\n'); 1389 } 1390 } 1391 if (alvec) { 1392 arun = 1; 1393 if ((alvecp = *alvec) != 0) { 1394 alvec++; 1395 goto top; 1396 } 1397 /* Infinite source! */ 1398 return ('\n'); 1399 } 1400 arun = 0; 1401 if (evalp) { 1402 aret = TCSH_E_SEEK; 1403 if ((c = *evalp++) != 0) 1404 return (c); 1405 if (evalvec && *evalvec) { 1406 evalp = *evalvec++; 1407 return (' '); 1408 } 1409 aret = TCSH_F_SEEK; 1410 evalp = 0; 1411 } 1412 if (evalvec) { 1413 if (evalvec == INVPPTR) { 1414 doneinp = 1; 1415 reset(); 1416 } 1417 if ((evalp = *evalvec) != 0) { 1418 evalvec++; 1419 goto top; 1420 } 1421 evalvec = INVPPTR; 1422 return ('\n'); 1423 } 1424 do { 1425 if (arginp == INVPTR || onelflg == 1) { 1426 if (wanteof) 1427 return CHAR_ERR; 1428 exitstat(); 1429 } 1430 if (arginp) { 1431 if ((c = *arginp++) == 0) { 1432 arginp = INVPTR; 1433 return ('\n'); 1434 } 1435 return (c); 1436 } 1437 #ifdef BSDJOBS 1438 reread: 1439 #endif /* BSDJOBS */ 1440 c = bgetc(); 1441 if (c == CHAR_ERR) { 1442 #ifndef WINNT_NATIVE 1443 # ifndef POSIX 1444 # ifdef TERMIO 1445 struct termio tty; 1446 # else /* SGTTYB */ 1447 struct sgttyb tty; 1448 # endif /* TERMIO */ 1449 # else /* POSIX */ 1450 struct termios tty; 1451 # endif /* POSIX */ 1452 #endif /* !WINNT_NATIVE */ 1453 if (wanteof) 1454 return CHAR_ERR; 1455 /* was isatty but raw with ignoreeof yields problems */ 1456 #ifndef WINNT_NATIVE 1457 # ifndef POSIX 1458 # ifdef TERMIO 1459 if (ioctl(SHIN, TCGETA, (ioctl_t) & tty) == 0 && 1460 (tty.c_lflag & ICANON)) 1461 # else /* GSTTYB */ 1462 if (ioctl(SHIN, TIOCGETP, (ioctl_t) & tty) == 0 && 1463 (tty.sg_flags & RAW) == 0) 1464 # endif /* TERMIO */ 1465 # else /* POSIX */ 1466 if (tcgetattr(SHIN, &tty) == 0 && 1467 (tty.c_lflag & ICANON)) 1468 # endif /* POSIX */ 1469 #else /* WINNT_NATIVE */ 1470 if (isatty(SHIN)) 1471 #endif /* !WINNT_NATIVE */ 1472 { 1473 #ifdef BSDJOBS 1474 pid_t ctpgrp; 1475 #endif /* BSDJOBS */ 1476 1477 if (numeof != 0 && ++sincereal >= numeof) /* Too many EOFs? Bye! */ 1478 goto oops; 1479 #ifdef BSDJOBS 1480 if (tpgrp != -1 && 1481 (ctpgrp = tcgetpgrp(FSHTTY)) != -1 && 1482 tpgrp != ctpgrp) { 1483 (void) tcsetpgrp(FSHTTY, tpgrp); 1484 # ifdef _SEQUENT_ 1485 if (ctpgrp) 1486 # endif /* _SEQUENT */ 1487 (void) killpg(ctpgrp, SIGHUP); 1488 # ifdef notdef 1489 /* 1490 * With the walking process group fix, this message 1491 * is now obsolete. As the foreground process group 1492 * changes, the shell needs to adjust. Well too bad. 1493 */ 1494 xprintf(CGETS(16, 1, "Reset tty pgrp from %d to %d\n"), 1495 (int)ctpgrp, (int)tpgrp); 1496 # endif /* notdef */ 1497 goto reread; 1498 } 1499 #endif /* BSDJOBS */ 1500 /* What follows is complicated EOF handling -- sterling@netcom.com */ 1501 /* First, we check to see if we have ignoreeof set */ 1502 if (adrof(STRignoreeof)) { 1503 /* If so, we check for any stopped jobs only on the first EOF */ 1504 if ((sincereal == 1) && (chkstop == 0)) { 1505 panystop(1); 1506 } 1507 } else { 1508 /* If we don't have ignoreeof set, always check for stopped jobs */ 1509 if (chkstop == 0) { 1510 panystop(1); 1511 } 1512 } 1513 /* At this point, if there were stopped jobs, we would have already 1514 * called reset(). If we got this far, assume we can print an 1515 * exit/logout message if we ignoreeof, or just exit. 1516 */ 1517 if (adrof(STRignoreeof)) { 1518 /* If so, tell the user to use exit or logout */ 1519 if (loginsh) { 1520 xprintf("%s", CGETS(16, 2, 1521 "\nUse \"logout\" to logout.\n")); 1522 } else { 1523 xprintf(CGETS(16, 3, 1524 "\nUse \"exit\" to leave %s.\n"), 1525 progname); 1526 } 1527 reset(); 1528 } else { 1529 /* If we don't have ignoreeof set, just fall through */ 1530 ; /* EMPTY */ 1531 } 1532 } 1533 oops: 1534 doneinp = 1; 1535 reset(); 1536 } 1537 sincereal = 0; 1538 if (c == '\n' && onelflg) 1539 onelflg--; 1540 } while (c == 0); 1541 Strbuf_append1(&histline, c); 1542 return (c); 1543 } 1544 1545 static void 1546 balloc(int buf) 1547 { 1548 Char **nfbuf; 1549 1550 while (buf >= fblocks) { 1551 nfbuf = xcalloc(fblocks + 2, sizeof(Char **)); 1552 if (fbuf) { 1553 (void) blkcpy(nfbuf, fbuf); 1554 xfree(fbuf); 1555 } 1556 fbuf = nfbuf; 1557 fbuf[fblocks] = xcalloc(BUFSIZE, sizeof(Char)); 1558 fblocks++; 1559 } 1560 } 1561 1562 ssize_t 1563 wide_read(int fildes, Char *buf, size_t nchars, int use_fclens) 1564 { 1565 char cbuf[BUFSIZE + 1]; 1566 ssize_t res, r = 0; 1567 size_t partial; 1568 int err; 1569 1570 if (nchars == 0) 1571 return 0; 1572 assert (nchars <= sizeof(cbuf) / sizeof(*cbuf)); 1573 USE(use_fclens); 1574 res = 0; 1575 partial = 0; 1576 do { 1577 size_t i; 1578 size_t len = nchars > partial ? nchars - partial : 1; 1579 1580 if (partial + len >= sizeof(cbuf) / sizeof(*cbuf)) 1581 break; 1582 1583 r = xread(fildes, cbuf + partial, len); 1584 1585 if (partial == 0 && r <= 0) 1586 break; 1587 partial += r; 1588 i = 0; 1589 while (i < partial && nchars != 0) { 1590 int tlen; 1591 1592 tlen = normal_mbtowc(buf + res, cbuf + i, partial - i); 1593 if (tlen == -1) { 1594 reset_mbtowc(); 1595 if ((partial - i) < MB_LEN_MAX && r > 0) 1596 /* Maybe a partial character and there is still a chance 1597 to read more */ 1598 break; 1599 buf[res] = (unsigned char)cbuf[i] | INVALID_BYTE; 1600 } 1601 if (tlen <= 0) 1602 tlen = 1; 1603 #ifdef WIDE_STRINGS 1604 if (use_fclens) 1605 fclens[res] = tlen; 1606 #endif 1607 i += tlen; 1608 res++; 1609 nchars--; 1610 } 1611 if (i != partial) 1612 memmove(cbuf, cbuf + i, partial - i); 1613 partial -= i; 1614 } while (partial != 0 && nchars > 0); 1615 /* Throwing away possible partial multibyte characters on error if the 1616 stream is not seekable */ 1617 err = errno; 1618 lseek(fildes, -(off_t)partial, L_INCR); 1619 errno = err; 1620 return res != 0 ? res : r; 1621 } 1622 1623 static eChar 1624 bgetc(void) 1625 { 1626 Char ch; 1627 int c, off, buf; 1628 int numleft = 0, roomleft; 1629 1630 if (cantell) { 1631 if (fseekp < fbobp || fseekp > feobp) { 1632 fbobp = feobp = fseekp; 1633 (void) lseek(SHIN, fseekp, L_SET); 1634 } 1635 if (fseekp == feobp) { 1636 #ifdef WIDE_STRINGS 1637 off_t bytes; 1638 size_t i; 1639 1640 bytes = fbobp; 1641 for (i = 0; i < (size_t)(feobp - fbobp); i++) 1642 bytes += fclens[i]; 1643 fseekp = feobp = bytes; 1644 #endif 1645 fbobp = feobp; 1646 c = wide_read(SHIN, fbuf[0], BUFSIZE, 1); 1647 #ifdef convex 1648 if (c < 0) 1649 stderror(ERR_SYSTEM, progname, strerror(errno)); 1650 #endif /* convex */ 1651 if (c <= 0) 1652 return CHAR_ERR; 1653 feobp += c; 1654 } 1655 #if !defined(WINNT_NATIVE) && !defined(__CYGWIN__) 1656 ch = fbuf[0][fseekp - fbobp]; 1657 fseekp++; 1658 #else 1659 do { 1660 ch = fbuf[0][fseekp - fbobp]; 1661 fseekp++; 1662 } while (ch == '\r'); 1663 #endif /* !WINNT_NATIVE && !__CYGWIN__ */ 1664 return (ch); 1665 } 1666 1667 while (fseekp >= feobp) { 1668 if ((editing 1669 #if defined(FILEC) && defined(TIOCSTI) 1670 || filec 1671 #endif /* FILEC && TIOCSTI */ 1672 ) && intty) { /* then use twenex routine */ 1673 fseekp = feobp; /* where else? */ 1674 #if defined(FILEC) && defined(TIOCSTI) 1675 if (!editing) 1676 c = numleft = tenex(InputBuf, BUFSIZE); 1677 else 1678 #endif /* FILEC && TIOCSTI */ 1679 c = numleft = Inputl(); /* PWP: get a line */ 1680 while (numleft > 0) { 1681 off = (int) feobp % BUFSIZE; 1682 buf = (int) feobp / BUFSIZE; 1683 balloc(buf); 1684 roomleft = BUFSIZE - off; 1685 if (roomleft > numleft) 1686 roomleft = numleft; 1687 (void) memcpy(fbuf[buf] + off, InputBuf + c - numleft, 1688 roomleft * sizeof(Char)); 1689 numleft -= roomleft; 1690 feobp += roomleft; 1691 } 1692 } else { 1693 off = (int) feobp % BUFSIZE; 1694 buf = (int) feobp / BUFSIZE; 1695 balloc(buf); 1696 roomleft = BUFSIZE - off; 1697 c = wide_read(SHIN, fbuf[buf] + off, roomleft, 0); 1698 if (c > 0) 1699 feobp += c; 1700 } 1701 if (c == 0 || (c < 0 && fixio(SHIN, errno) == -1)) 1702 return CHAR_ERR; 1703 } 1704 #ifdef SIG_WINDOW 1705 if (windowchg) 1706 (void) check_window_size(0); /* for window systems */ 1707 #endif /* SIG_WINDOW */ 1708 #if !defined(WINNT_NATIVE) && !defined(__CYGWIN__) 1709 ch = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE]; 1710 fseekp++; 1711 #else 1712 do { 1713 ch = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE]; 1714 fseekp++; 1715 } while (ch == '\r'); 1716 #endif /* !WINNT_NATIVE && !__CYGWIN__ */ 1717 return (ch); 1718 } 1719 1720 static void 1721 bfree(void) 1722 { 1723 int sb, i; 1724 1725 if (cantell) 1726 return; 1727 if (whyles) 1728 return; 1729 sb = (int) (fseekp - 1) / BUFSIZE; 1730 if (sb > 0) { 1731 for (i = 0; i < sb; i++) 1732 xfree(fbuf[i]); 1733 (void) blkcpy(fbuf, &fbuf[sb]); 1734 fseekp -= BUFSIZE * sb; 1735 feobp -= BUFSIZE * sb; 1736 fblocks -= sb; 1737 } 1738 } 1739 1740 void 1741 bseek(struct Ain *l) 1742 { 1743 switch (aret = l->type) { 1744 case TCSH_E_SEEK: 1745 evalvec = l->a_seek; 1746 evalp = l->c_seek; 1747 #ifdef DEBUG_SEEK 1748 xprintf(CGETS(16, 4, "seek to eval %x %x\n"), evalvec, evalp); 1749 #endif 1750 return; 1751 case TCSH_A_SEEK: 1752 alvec = l->a_seek; 1753 alvecp = l->c_seek; 1754 #ifdef DEBUG_SEEK 1755 xprintf(CGETS(16, 5, "seek to alias %x %x\n"), alvec, alvecp); 1756 #endif 1757 return; 1758 case TCSH_F_SEEK: 1759 #ifdef DEBUG_SEEK 1760 xprintf(CGETS(16, 6, "seek to file %x\n"), fseekp); 1761 #endif 1762 fseekp = l->f_seek; 1763 #ifdef WIDE_STRINGS 1764 if (cantell) { 1765 if (fseekp >= fbobp && feobp >= fbobp) { 1766 size_t i; 1767 off_t o; 1768 1769 o = fbobp; 1770 for (i = 0; i < (size_t)(feobp - fbobp); i++) { 1771 if (fseekp == o) { 1772 fseekp = fbobp + i; 1773 return; 1774 } 1775 o += fclens[i]; 1776 } 1777 if (fseekp == o) { 1778 fseekp = feobp; 1779 return; 1780 } 1781 } 1782 fbobp = feobp = fseekp + 1; /* To force lseek() */ 1783 } 1784 #endif 1785 return; 1786 default: 1787 xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret); 1788 abort(); 1789 } 1790 } 1791 1792 /* any similarity to bell telephone is purely accidental */ 1793 void 1794 btell(struct Ain *l) 1795 { 1796 switch (l->type = aret) { 1797 case TCSH_E_SEEK: 1798 l->a_seek = evalvec; 1799 l->c_seek = evalp; 1800 #ifdef DEBUG_SEEK 1801 xprintf(CGETS(16, 8, "tell eval %x %x\n"), evalvec, evalp); 1802 #endif 1803 return; 1804 case TCSH_A_SEEK: 1805 l->a_seek = alvec; 1806 l->c_seek = alvecp; 1807 #ifdef DEBUG_SEEK 1808 xprintf(CGETS(16, 9, "tell alias %x %x\n"), alvec, alvecp); 1809 #endif 1810 return; 1811 case TCSH_F_SEEK: 1812 #ifdef WIDE_STRINGS 1813 if (cantell && fseekp >= fbobp && fseekp <= feobp) { 1814 size_t i; 1815 1816 l->f_seek = fbobp; 1817 for (i = 0; i < (size_t)(fseekp - fbobp); i++) 1818 l->f_seek += fclens[i]; 1819 } else 1820 #endif 1821 /*SUPPRESS 112*/ 1822 l->f_seek = fseekp; 1823 l->a_seek = NULL; 1824 #ifdef DEBUG_SEEK 1825 xprintf(CGETS(16, 10, "tell file %x\n"), fseekp); 1826 #endif 1827 return; 1828 default: 1829 xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret); 1830 abort(); 1831 } 1832 } 1833 1834 void 1835 btoeof(void) 1836 { 1837 (void) lseek(SHIN, (off_t) 0, L_XTND); 1838 aret = TCSH_F_SEEK; 1839 fseekp = feobp; 1840 alvec = NULL; 1841 alvecp = NULL; 1842 evalvec = NULL; 1843 evalp = NULL; 1844 wfree(); 1845 bfree(); 1846 } 1847 1848 void 1849 settell(void) 1850 { 1851 off_t x; 1852 cantell = 0; 1853 if (arginp || onelflg || intty) 1854 return; 1855 if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1) 1856 return; 1857 fbuf = xcalloc(2, sizeof(Char **)); 1858 fblocks = 1; 1859 fbuf[0] = xcalloc(BUFSIZE, sizeof(Char)); 1860 fseekp = fbobp = feobp = x; 1861 cantell = 1; 1862 } 1863