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