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