sh.lex.c 37.3 KB
Newer Older
1
/* $Header: /p/tcsh/cvsroot/tcsh/sh.lex.c,v 3.91 2016/08/01 16:21:09 christos Exp $ */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*
 * sh.lex.c: Lexical analysis into tokens
 */
/*-
 * Copyright (c) 1980, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "sh.h"

35
RCSID("$tcsh: sh.lex.c,v 3.91 2016/08/01 16:21:09 christos Exp $")
36 37 38 39 40 41 42 43 44 45 46

#include "ed.h"

#include <assert.h>
/* #define DEBUG_INP */
/* #define DEBUG_SEEK */

/*
 * C shell
 */

47 48
#define FLAG_G	1
#define FLAG_A	2
49 50 51 52 53
/*
 * These lexical routines read input and form lists of words.
 * There is some involved processing here, because of the complications
 * of input buffering, and especially because of history substitution.
 */
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
static	Char		*word		(int);
static	eChar	 	 getC1		(int);
static	void	 	 getdol		(void);
static	void	 	 getexcl	(Char);
static	struct Hist 	*findev		(Char *, int);
static	void	 	 setexclp	(Char *);
static	eChar	 	 bgetc		(void);
static	void		 balloc		(int);
static	void	 	 bfree		(void);
static	struct wordent	*gethent	(Char);
static	int	 	 matchs		(const Char *, const Char *);
static	int	 	 getsel		(int *, int *, int);
static	struct wordent	*getsub		(struct wordent *);
static	Char 		*subword	(Char *, Char, int *, size_t *);
static	struct wordent	*dosub		(Char, struct wordent *, int);
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

/*
 * Peekc is a peek character for getC, peekread for readc.
 * There is a subtlety here in many places... history routines
 * will read ahead and then insert stuff into the input stream.
 * If they push back a character then they must push it behind
 * the text substituted by the history substitution.  On the other
 * hand in several places we need 2 peek characters.  To make this
 * all work, the history routines read with getC, and make use both
 * of ungetC and unreadc.  The key observation is that the state
 * of getC at the call of a history reference is such that calls
 * to getC from the history routines will always yield calls of
 * readc, unless this peeking is involved.  That is to say that during
 * getexcl the variables lap, exclp, and exclnxt are all zero.
 *
 * Getdol invokes history substitution, hence the extra peek, peekd,
 * which it can ungetD to be before history substitutions.
 */
static Char peekc = 0, peekd = 0;
static Char peekread = 0;

/* (Tail of) current word from ! subst */
static Char *exclp = NULL;

/* The rest of the ! subst words */
static struct wordent *exclnxt = NULL;

/* Count of remaining words in ! subst */
static int exclc = 0;

/* "Globp" for alias resubstitution */
int aret = TCSH_F_SEEK;

/*
 * Labuf implements a general buffer for lookahead during lexical operations.
 * Text which is to be placed in the input stream can be stuck here.
 * We stick parsed ahead $ constructs during initial input,
 * process id's from `$$', and modified variable values (from qualifiers
 * during expansion in sh.dol.c) here.
 */
109
struct Strbuf labuf; /* = Strbuf_INIT; */
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

/*
 * Lex returns to its caller not only a wordlist (as a "var" parameter)
 * but also whether a history substitution occurred.  This is used in
 * the main (process) routine to determine whether to echo, and also
 * when called by the alias routine to determine whether to keep the
 * argument list.
 */
static int hadhist = 0;

/*
 * Avoid alias expansion recursion via \!#
 */
int     hleft;

125
struct Strbuf histline; /* = Strbuf_INIT; last line input */
126 127 128 129 130 131 132 133 134 135 136 137 138

int    histvalid = 0;		/* is histline valid */

static Char getCtmp;

#define getC(f)		(((getCtmp = peekc) != '\0') ? (peekc = 0, (eChar)getCtmp) : getC1(f))
#define	ungetC(c)	peekc = (Char) c
#define	ungetD(c)	peekd = (Char) c

/* Use Htime to store timestamps picked up from history file for enthist()
 * if reading saved history (sg)
 */
time_t Htime = (time_t)0;
139
static time_t a2time_t (Char *);
140 141 142 143 144 145 146

/*
 * special parsing rules apply for source -h
 */
extern int enterhist;

int
147
lex(struct wordent *hp)
148 149 150 151 152 153
{
    struct wordent *wdp;
    eChar    c;
    int     parsehtime = enterhist;

    histvalid = 0;
154
    histline.len = 0;
155 156 157 158 159 160 161 162 163 164 165 166 167

    btell(&lineloc);
    hp->next = hp->prev = hp;
    hp->word = STRNULL;
    hadhist = 0;
    do
	c = readc(0);
    while (c == ' ' || c == '\t');
    if (c == (eChar)HISTSUB && intty)
	/* ^lef^rit	from tty is short !:s^lef^rit */
	getexcl(c);
    else
	unreadc(c);
168
    cleanup_push(hp, lex_cleanup);
169 170 171 172 173 174 175 176
    wdp = hp;
    /*
     * The following loop is written so that the links needed by freelex will
     * be ready and rarin to go even if it is interrupted.
     */
    do {
	struct wordent *new;

177 178
	new = xmalloc(sizeof(*new));
	new->word = NULL;
179 180 181 182 183 184 185 186
	new->prev = wdp;
	new->next = hp;
	wdp->next = new;
	hp->prev = new;
	wdp = new;
	wdp->word = word(parsehtime);
	parsehtime = 0;
    } while (wdp->word[0] != '\n');
187 188 189 190 191 192
    cleanup_ignore(hp);
    cleanup_until(hp);
    Strbuf_terminate(&histline);
    if (histline.len != 0 && histline.s[histline.len - 1] == '\n')
	histline.s[histline.len - 1] = '\0';
    histvalid = 1;
193 194 195 196 197

    return (hadhist);
}

static time_t
198
a2time_t(Char *wordx)
199 200 201 202 203 204 205 206 207 208 209
{
    /* Attempt to distinguish timestamps from other possible entries.
     * Format: "+NNNNNNNNNN" (10 digits, left padded with ascii '0') */

    time_t ret;
    Char *s;
    int ct;

    if (!wordx || *(s = wordx) != '+')
	return (time_t)0;

210
    for (++s, ret = 0, ct = 0; *s; ++s, ++ct) {
211 212 213 214 215 216 217 218 219 220 221 222
	if (!isdigit((unsigned char)*s))
	    return (time_t)0;
	ret = ret * 10 + (time_t)((unsigned char)*s - '0');
    }

    if (ct != 10)
	return (time_t)0;

    return ret;
}

void
223
prlex(struct wordent *sp0)
224 225 226 227 228 229 230 231 232 233 234 235 236 237
{
    struct wordent *sp = sp0->next;

    for (;;) {
	xprintf("%S", sp->word);
	sp = sp->next;
	if (sp == sp0)
	    break;
	if (sp->word[0] != '\n')
	    xputchar(' ');
    }
}

void
238
copylex(struct wordent *hp, struct wordent *fp)
239 240 241 242 243 244 245
{
    struct wordent *wdp;

    wdp = hp;
    fp = fp->next;
    do {
	struct wordent *new;
246 247 248

	new = xmalloc(sizeof(*new));
	new->word = NULL;
249 250 251 252 253 254 255 256 257 258
	new->prev = wdp;
	new->next = hp;
	wdp->next = new;
	hp->prev = new;
	wdp = new;
	wdp->word = Strsave(fp->word);
	fp = fp->next;
    } while (wdp->word[0] != '\n');
}

259 260 261 262 263 264 265 266
void
initlex(struct wordent *vp)
{
	vp->word = STRNULL;
	vp->prev = vp;
	vp->next = vp;
}

267
void
268
freelex(struct wordent *vp)
269 270 271 272 273 274
{
    struct wordent *fp;

    while (vp->next != vp) {
	fp = vp->next;
	vp->next = fp->next;
275 276
	xfree(fp->word);
	xfree(fp);
277 278 279 280
    }
    vp->prev = vp;
}

281 282 283 284 285 286 287 288 289
void
lex_cleanup(void *xvp)
{
    struct wordent *vp;

    vp = xvp;
    freelex(vp);
}

290
static Char *
291
word(int parsehtime)
292 293
{
    eChar c, c1;
294
    struct Strbuf wbuf = Strbuf_INIT;
295 296 297 298
    Char    hbuf[12];
    int	    h;
    int dolflg;

299
    cleanup_push(&wbuf, Strbuf_cleanup);
300 301 302 303 304 305 306 307 308
loop:
    while ((c = getC(DOALL)) == ' ' || c == '\t')
	continue;
    if (cmap(c, _META | _ESC))
	switch (c) {
	case '&':
	case '|':
	case '<':
	case '>':
309
	    Strbuf_append1(&wbuf, c);
310 311
	    c1 = getC(DOALL);
	    if (c1 == c)
312
		Strbuf_append1(&wbuf, c1);
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	    else
		ungetC(c1);
	    goto ret;

	case '#':
	    if (intty || (enterhist && !parsehtime))
		break;
	    c = 0;
	    h = 0;
	    do {
		c1 = c;
		c = getC(0);
		if (h < 11 && parsehtime)
		    hbuf[h++] = c;
	    } while (c != '\n');
	    if (parsehtime) {
		hbuf[11] = '\0';
		Htime = a2time_t(hbuf); 
	    }
	    if (c1 == '\\')
		goto loop;
	    /*FALLTHROUGH*/

	case ';':
	case '(':
	case ')':
	case '\n':
340
	    Strbuf_append1(&wbuf, c);
341 342 343 344 345 346 347 348 349 350
	    goto ret;

	case '\\':
	    c = getC(0);
	    if (c == '\n') {
		if (onelflg == 1)
		    onelflg = 2;
		goto loop;
	    }
	    if (c != (eChar)HIST)
351
		Strbuf_append1(&wbuf, '\\');
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
	    c |= QUOTE;
	default:
	    break;
	}
    c1 = 0;
    dolflg = DOALL;
    for (;;) {
	if (c1) {
	    if (c == c1) {
		c1 = 0;
		dolflg = DOALL;
	    }
	    else if (c == '\\') {
		c = getC(0);
/*
 * PWP: this is dumb, but how all of the other shells work.  If \ quotes
 * a character OUTSIDE of a set of ''s, why shouldn't it quote EVERY
 * following character INSIDE a set of ''s.
 *
 * Actually, all I really want to be able to say is 'foo\'bar' --> foo'bar
 */
		if (c == (eChar)HIST)
		    c |= QUOTE;
		else {
		    if (bslash_quote &&
			((c == '\'') || (c == '"') ||
378
			 (c == '\\') || (c == '$'))) {
379 380 381 382 383 384 385 386 387
			c |= QUOTE;
		    }
		    else {
			if (c == '\n')
			    /*
			     * if (c1 == '`') c = ' '; else
			     */
			    c |= QUOTE;
			ungetC(c);
388
			c = '\\' | QUOTE;
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
		    }
		}
	    }
	    else if (c == '\n') {
		seterror(ERR_UNMATCHED, c1);
		ungetC(c);
		break;
	    }
	}
	else if (cmap(c, _META | _QF | _QB | _ESC)) {
	    if (c == '\\') {
		c = getC(0);
		if (c == '\n') {
		    if (onelflg == 1)
			onelflg = 2;
		    break;
		}
		if (c != (eChar)HIST)
407
		    Strbuf_append1(&wbuf, '\\');
408 409 410 411 412 413 414 415 416 417 418
		c |= QUOTE;
	    }
	    else if (cmap(c, _QF | _QB)) {	/* '"` */
		c1 = c;
		dolflg = c == '"' ? DOALL : DOEXCL;
	    }
	    else if (c != '#' || (!intty && !enterhist)) {
		ungetC(c);
		break;
	    }
	}
419 420
	Strbuf_append1(&wbuf, c);
	c = getC(dolflg);
421 422
    }
ret:
423 424 425
    cleanup_ignore(&wbuf);
    cleanup_until(&wbuf);
    return Strbuf_finish(&wbuf);
426 427 428
}

static eChar
429
getC1(int flag)
430 431 432 433 434 435 436 437
{
    eChar c;

    for (;;) {
	if ((c = peekc) != 0) {
	    peekc = 0;
	    return (c);
	}
438 439 440 441 442
	if (lap < labuf.len) {
	    c = labuf.s[lap++];
	    if (cmap(c, _META | _QF | _QB))
		c |= QUOTE;
	    return (c);
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
	}
	if ((c = peekd) != 0) {
	    peekd = 0;
	    return (c);
	}
	if (exclp) {
	    if ((c = *exclp++) != 0)
		return (c);
	    if (exclnxt && --exclc >= 0) {
		exclnxt = exclnxt->next;
		setexclp(exclnxt->word);
		return (' ');
	    }
	    exclp = 0;
	    exclnxt = 0;
	    /* this will throw away the dummy history entries */
	    savehist(NULL, 0);

	}
	if (exclnxt) {
	    exclnxt = exclnxt->next;
	    if (--exclc < 0)
		exclnxt = 0;
	    else
		setexclp(exclnxt->word);
	    continue;
	}
470 471 472 473 474 475 476
	c = readc(1);

	/* Catch EOF in the middle of a line.  (An EOF at the beginning of
	 * a line would have been processed by the readc(0) in lex().) */
	if (c == CHAR_ERR)
	    c = '\n';

477 478 479 480 481 482 483 484 485 486 487 488 489 490
	if (c == '$' && (flag & DODOL)) {
	    getdol();
	    continue;
	}
	if (c == (eChar)HIST && (flag & DOEXCL)) {
	    getexcl(0);
	    continue;
	}
	break;
    }
    return (c);
}

static void
491
getdol(void)
492
{
493
    struct Strbuf name = Strbuf_INIT;
494 495
    eChar c;
    eChar   sc;
496
    int    special = 0;
497 498 499 500 501 502 503

    c = sc = getC(DOEXCL);
    if (any("\t \n", c)) {
	ungetD(c);
	ungetC('$' | QUOTE);
	return;
    }
504 505
    cleanup_push(&name, Strbuf_cleanup);
    Strbuf_append1(&name, '$');
506
    if (c == '{')
507
	Strbuf_append1(&name, c), c = getC(DOEXCL);
508
    if (c == '#' || c == '?' || c == '%')
509 510
	special++, Strbuf_append1(&name, c), c = getC(DOEXCL);
    Strbuf_append1(&name, c);
511 512 513 514 515 516 517
    switch (c) {

    case '<':
    case '$':
    case '!':
	if (special)
	    seterror(ERR_SPDOLLT);
518
	goto end;
519 520 521

    case '\n':
	ungetD(c);
522
	name.len--;
523 524
	if (!special)
	    seterror(ERR_NEWLINE);
525
	goto end;
526 527 528 529

    case '*':
	if (special)
	    seterror(ERR_SPSTAR);
530
	goto end;
531 532 533 534 535 536 537

    default:
	if (Isdigit(c)) {
#ifdef notdef
	    /* let $?0 pass for now */
	    if (special) {
		seterror(ERR_DIGIT);
538
		goto end;
539 540 541 542 543
	    }
#endif
	    while ((c = getC(DOEXCL)) != 0) {
		if (!Isdigit(c))
		    break;
544
		Strbuf_append1(&name, c);
545 546 547 548 549 550 551
	    }
	}
	else if (letter(c)) {
	    while ((c = getC(DOEXCL)) != 0) {
		/* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
		if (!letter(c) && !Isdigit(c))
		    break;
552
		Strbuf_append1(&name, c);
553 554 555 556 557 558 559
	    }
	}
	else {
	    if (!special)
		seterror(ERR_VARILL);
	    else {
		ungetD(c);
560
		name.len--;
561
	    }
562
	    goto end;
563 564 565 566
	}
	break;
    }
    if (c == '[') {
567
	Strbuf_append1(&name, c);
568 569 570 571 572 573 574 575
	do {
	    /*
	     * Michael Greim: Allow $ expansion to take place in selector
	     * expressions. (limits the number of characters returned)
	     */
	    c = getC(DOEXCL | DODOL);
	    if (c == '\n') {
		ungetD(c);
576
		name.len--;
577
		seterror(ERR_NLINDEX);
578
		goto end;
579
	    }
580
	    Strbuf_append1(&name, c);
581 582 583 584 585 586 587 588 589 590 591 592
	} while (c != ']');
	c = getC(DOEXCL);
    }
    if (c == ':') {
	/*
	 * if the :g modifier is followed by a newline, then error right away!
	 * -strike
	 */

	int     gmodflag = 0, amodflag = 0;

	do {
593
	    Strbuf_append1(&name, c), c = getC(DOEXCL);
594 595 596 597 598
	    if (c == 'g' || c == 'a') {
		if (c == 'g')
		    gmodflag++;
		else
		    amodflag++;
599
		Strbuf_append1(&name, c); c = getC(DOEXCL);
600 601 602 603 604 605
	    }
	    if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
		if (c == 'g')
		    gmodflag++;
		else
		    amodflag++;
606
		Strbuf_append1(&name, c); c = getC(DOEXCL);
607
	    }
608
	    Strbuf_append1(&name, c);
609 610 611 612
	    /* scan s// [eichin:19910926.0512EST] */
	    if (c == 's') {
		int delimcnt = 2;
		eChar delim = getC(0);
613 614

		Strbuf_append1(&name, delim);
615 616 617 618
		if (!delim || letter(delim)
		    || Isdigit(delim) || any(" \t\n", delim)) {
		    seterror(ERR_BADSUBST);
		    break;
619
		}
620
		while ((c = getC(0)) != CHAR_ERR) {
621
		    Strbuf_append1(&name, c);
622 623 624 625 626 627 628 629 630 631 632 633 634
		    if(c == delim) delimcnt--;
		    if(!delimcnt) break;
		}
		if(delimcnt) {
		    seterror(ERR_BADSUBST);
		    break;
		}
		c = 's';
	    }
	    if (!any("htrqxesul", c)) {
		if ((amodflag || gmodflag) && c == '\n')
		    stderror(ERR_VARSYN);	/* strike */
		seterror(ERR_BADMOD, c);
635
		goto end;
636 637 638 639 640 641 642 643 644 645 646 647
	    }
	}
	while ((c = getC(DOEXCL)) == ':');
	ungetD(c);
    }
    else
	ungetD(c);
    if (sc == '{') {
	c = getC(DOEXCL);
	if (c != '}') {
	    ungetD(c);
	    seterror(ERR_MISSING, '}');
648
	    goto end;
649
	}
650
	Strbuf_append1(&name, c);
651
    }
652 653 654 655
 end:
    cleanup_ignore(&name);
    cleanup_until(&name);
    addla(Strbuf_finish(&name));
656 657
}

658
/* xfree()'s its argument */
659
void
660
addla(Char *cp)
661
{
662 663 664 665 666 667 668 669 670 671
    static struct Strbuf buf; /* = Strbuf_INIT; */

    buf.len = 0;
    Strbuf_appendn(&buf, labuf.s + lap, labuf.len - lap);
    labuf.len = 0;
    Strbuf_append(&labuf, cp);
    Strbuf_terminate(&labuf);
    Strbuf_appendn(&labuf, buf.s, buf.len);
    xfree(cp);
    lap = 0;
672 673
}

674 675 676 677
/* left-hand side of last :s or search string of last ?event? */
static struct Strbuf lhsb; /* = Strbuf_INIT; */
static struct Strbuf slhs; /* = Strbuf_INIT; left-hand side of last :s */
static struct Strbuf rhsb; /* = Strbuf_INIT; right-hand side of last :s */
678 679 680
static int quesarg;

static void
681
getexcl(Char sc)
682 683 684 685 686 687
{
    struct wordent *hp, *ip;
    int     left, right, dol;
    eChar c;

    if (sc == 0) {
688 689 690 691 692
	c = getC(0);
	if (c == '{')
	    sc = (Char) c;
	else
	    ungetC(c);
693 694 695
    }
    quesarg = -1;

696
    lastev = eventno;
697 698 699 700 701 702 703 704 705 706 707 708
    hp = gethent(sc);
    if (hp == 0)
	return;
    hadhist = 1;
    dol = 0;
    if (hp == alhistp)
	for (ip = hp->next->next; ip != alhistt; ip = ip->next)
	    dol++;
    else
	for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
	    dol++;
    left = 0, right = dol;
709
    if (sc == HISTSUB && HISTSUB != '\0') {
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
	ungetC('s'), unreadc(HISTSUB), c = ':';
	goto subst;
    }
    c = getC(0);
    if (!any(":^$*-%", c))
	goto subst;
    left = right = -1;
    if (c == ':') {
	c = getC(0);
	unreadc(c);
	if (letter(c) || c == '&') {
	    c = ':';
	    left = 0, right = dol;
	    goto subst;
	}
    }
    else
	ungetC(c);
    if (!getsel(&left, &right, dol))
	return;
    c = getC(0);
    if (c == '*')
	ungetC(c), c = '-';
    if (c == '-') {
	if (!getsel(&left, &right, dol))
	    return;
	c = getC(0);
    }
subst:
    exclc = right - left + 1;
    while (--left >= 0)
	hp = hp->next;
742
    if ((sc == HISTSUB && HISTSUB != '\0') || c == ':') {
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
	do {
	    hp = getsub(hp);
	    c = getC(0);
	} while (c == ':');
    }
    unreadc(c);
    if (sc == '{') {
	c = getC(0);
	if (c != '}')
	    seterror(ERR_BADBANG);
    }
    exclnxt = hp;
}

static struct wordent *
758
getsub(struct wordent *en)
759 760 761 762 763 764 765 766 767 768
{
    eChar   delim;
    eChar   c;
    eChar   sc;
    int global;

    do {
	exclnxt = 0;
	global = 0;
	sc = c = getC(0);
769 770
	while (c == 'g' || c == 'a') {
	    global |= (c == 'g') ? FLAG_G : FLAG_A;
771 772 773 774 775 776 777 778 779 780
	    sc = c = getC(0);
	}

	switch (c) {
	case 'p':
	    justpr++;
	    return (en);

	case 'x':
	case 'q':
781
	    global |= FLAG_G;
782 783 784 785 786 787 788 789 790 791 792
	    /*FALLTHROUGH*/

	case 'h':
	case 'r':
	case 't':
	case 'e':
	case 'u':
	case 'l':
	    break;

	case '&':
793
	    if (slhs.len == 0) {
794 795 796
		seterror(ERR_NOSUBST);
		return (en);
	    }
797 798 799
	    lhsb.len = 0;
	    Strbuf_append(&lhsb, slhs.s);
	    Strbuf_terminate(&lhsb);
800 801 802 803
	    break;

#ifdef notdef
	case '~':
804
	    if (lhsb.len == 0)
805 806 807 808 809 810 811 812
		goto badlhs;
	    break;
#endif

	case 's':
	    delim = getC(0);
	    if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
		unreadc(delim);
813
		lhsb.len = 0;
814 815 816
		seterror(ERR_BADSUBST);
		return (en);
	    }
817 818
	    Strbuf_terminate(&lhsb);
	    lhsb.len = 0;
819 820 821 822 823 824 825 826 827 828 829
	    for (;;) {
		c = getC(0);
		if (c == '\n') {
		    unreadc(c);
		    break;
		}
		if (c == delim)
		    break;
		if (c == '\\') {
		    c = getC(0);
		    if (c != delim && c != '\\')
830
			Strbuf_append1(&lhsb, '\\');
831
		}
832
		Strbuf_append1(&lhsb, c);
833
	    }
834 835 836
	    if (lhsb.len != 0)
		Strbuf_terminate(&lhsb);
	    else if (lhsb.s[0] == 0) {
837 838
		seterror(ERR_LHS);
		return (en);
839 840 841
	    } else
		lhsb.len = Strlen(lhsb.s); /* lhsb.s wasn't changed */
	    rhsb.len = 0;
842 843 844 845 846 847 848 849 850 851 852
	    for (;;) {
		c = getC(0);
		if (c == '\n') {
		    unreadc(c);
		    break;
		}
		if (c == delim)
		    break;
		if (c == '\\') {
		    c = getC(0);
		    if (c != delim /* && c != '~' */ )
853
			Strbuf_append1(&rhsb,  '\\');
854
		}
855
		Strbuf_append1(&rhsb, c);
856
	    }
857
	    Strbuf_terminate(&rhsb);
858 859 860 861 862 863 864 865
	    break;

	default:
	    if (c == '\n')
		unreadc(c);
	    seterror(ERR_BADBANGMOD, (int)c);
	    return (en);
	}
866
	slhs.len = 0;
867 868
	if (lhsb.s != NULL && lhsb.len != 0)
	    Strbuf_append(&slhs, lhsb.s);
869
	Strbuf_terminate(&slhs);
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
	if (exclc)
	    en = dosub(sc, en, global);
    }
    while ((c = getC(0)) == ':');
    unreadc(c);
    return (en);
}

/*
 * 
 * From Beto Appleton (beto@aixwiz.austin.ibm.com)
 *
 * when using history substitution, and the variable
 * 'history' is set to a value higher than 1000,
 * the shell might either freeze (hang) or core-dump.
 * We raise the limit to 50000000
 */

#define HIST_PURGE -50000000
static struct wordent *
890
dosub(Char sc, struct wordent *en, int global)
891 892 893 894 895 896 897 898 899 900
{
    struct wordent lexi;
    int    didsub = 0, didone = 0;
    struct wordent *hp = &lexi;
    struct wordent *wdp;
    int i = exclc;
    struct Hist *hst;

    wdp = hp;
    while (--i >= 0) {
901
	struct wordent *new = xcalloc(1, sizeof *wdp);
902 903 904 905 906 907 908 909 910 911

	new->word = 0;
	new->prev = wdp;
	new->next = hp;
	wdp->next = new;
	wdp = new;
	en = en->next;
	if (en->word) {
	    Char *tword, *otword;

912 913 914 915 916
	    if ((global & FLAG_G) || didsub == 0) {
		size_t pos;

		pos = 0;
		tword = subword(en->word, sc, &didone, &pos);
917 918
		if (didone)
		    didsub = 1;
919
		if (global & FLAG_A) {
920 921
		    while (didone && tword != STRNULL) {
			otword = tword;
922
			tword = subword(otword, sc, &didone, &pos);
923
			if (Strcmp(tword, otword) == 0) {
924
			    xfree(otword);
925 926 927
			    break;
			}
			else
928
			    xfree(otword);
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
		    }
		}
	    }
	    else
		tword = Strsave(en->word);
	    wdp->word = tword;
	}
    }
    if (didsub == 0)
	seterror(ERR_MODFAIL);
    hp->prev = wdp;
    /* 
     * ANSI mode HP/UX compiler chokes on
     * return &enthist(HIST_PURGE, &lexi, 0)->Hlex;
     */
944
    hst = enthist(HIST_PURGE, &lexi, 0, 0, -1);
945 946 947
    return &(hst->Hlex);
}

948 949 950 951
/* Return a newly allocated result of one modification of CP using the
   operation TYPE.  Set ADID to 1 if a modification was performed.
   If TYPE == 's', perform substitutions only from *START_POS on and set
   *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 956
    Char *wp;
    const Char *mp, *np;
957 958 959 960 961 962 963 964 965 966 967 968

    switch (type) {

    case 'r':
    case 'e':
    case 'h':
    case 't':
    case 'q':
    case 'x':
    case 'u':
    case 'l':
	wp = domod(cp, type);
969 970
	if (wp == 0) {
	    *adid = 0;
971
	    return (Strsave(cp));
972
	}
973 974 975 976
	*adid = 1;
	return (wp);

    default:
977 978 979 980 981 982
	for (mp = cp + *start_pos; *mp; mp++) {
	    if (matchs(mp, lhsb.s)) {
		struct Strbuf wbuf = Strbuf_INIT;

		Strbuf_appendn(&wbuf, cp, mp - cp);
		for (np = rhsb.s; *np; np++)
983 984 985 986 987 988 989 990
		    switch (*np) {

		    case '\\':
			if (np[1] == '&')
			    np++;
			/* fall into ... */

		    default:
991
			Strbuf_append1(&wbuf, *np);
992 993 994
			continue;

		    case '&':
995
			Strbuf_append(&wbuf, lhsb.s);
996 997
			continue;
		    }
998 999
		*start_pos = wbuf.len;
		Strbuf_append(&wbuf, mp + lhsb.len);
1000
		*adid = 1;
1001
		return Strbuf_finish(&wbuf);
1002
	    }
1003 1004
	}
	*adid = 0;
1005 1006 1007 1008 1009
	return (Strsave(cp));
    }
}

Char   *
1010
domod(Char *cp, Char type)
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
{
    Char *wp, *xp;
    int c;

    switch (type) {

    case 'x':
    case 'q':
	wp = Strsave(cp);
	for (xp = wp; (c = *xp) != 0; xp++)
	    if ((c != ' ' && c != '\t') || type == 'q')
		*xp |= QUOTE;
	return (wp);

    case 'l':
	wp = NLSChangeCase(cp, 1);
	return wp ? wp : Strsave(cp);

    case 'u':
	wp = NLSChangeCase(cp, 0);
	return wp ? wp : Strsave(cp);

    case 'h':
    case 't':
	if (!any(short2str(cp), '/'))
	    return (type == 't' ? Strsave(cp) : 0);
1037
	wp = Strrchr(cp, '/');
1038
	if (type == 'h')
1039
	    xp = Strnsave(cp, wp - cp);
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
	else
	    xp = Strsave(wp + 1);
	return (xp);

    case 'e':
    case 'r':
	wp = Strend(cp);
	for (wp--; wp >= cp && *wp != '/'; wp--)
	    if (*wp == '.') {
		if (type == 'e')
		    xp = Strsave(wp + 1);
		else
1052
		    xp = Strnsave(cp, wp - cp);
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
		return (xp);
	    }
	return (Strsave(type == 'e' ? STRNULL : cp));
    default:
	break;
    }
    return (0);
}

static int
1063
matchs(const Char *str, const Char *pat)
1064 1065 1066 1067 1068 1069 1070
{
    while (*str && *pat && *str == *pat)
	str++, pat++;
    return (*pat == 0);
}

static int
1071
getsel(int *al, int *ar, int dol)
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
{
    eChar c = getC(0);
    int i;
    int    first = *al < 0;

    switch (c) {

    case '%':
	if (quesarg == -1) {
	    seterror(ERR_BADBANGARG);
	    return (0);
	}
	if (*al < 0)
	    *al = quesarg;
	*ar = quesarg;
	break;

    case '-':
	if (*al < 0) {
	    *al = 0;
	    *ar = dol - 1;
	    unreadc(c);
	}
	return (1);

    case '^':
	if (*al < 0)
	    *al = 1;
	*ar = 1;
	break;

    case '$':
	if (*al < 0)
	    *al = dol;
	*ar = dol;
	break;

    case '*':
	if (*al < 0)
	    *al = 1;
	*ar = dol;
	if (*ar < *al) {
	    *ar = 0;
	    *al = 1;
	    return (1);
	}
	break;

    default:
	if (Isdigit(c)) {
	    i = 0;
	    while (Isdigit(c)) {
		i = i * 10 + c - '0';
		c = getC(0);
	    }
	    if (i < 0)
		i = dol + 1;
	    if (*al < 0)
		*al = i;
	    *ar = i;
	}
	else if (*al < 0)
	    *al = 0, *ar = dol;
	else
	    *ar = dol - 1;
	unreadc(c);
	break;
    }
    if (first) {
	c = getC(0);
	unreadc(c);
	if (any("-$*", c))
	    return (1);
    }
    if (*al > *ar || *ar > dol) {
	seterror(ERR_BADBANGARG);
	return (0);
    }
    return (1);

}

static struct wordent *
1155
gethent(Char sc)
1156 1157 1158 1159 1160 1161 1162
{
    struct Hist *hp;
    Char *np;
    eChar c;
    int     event;
    int    back = 0;

1163
    c = (sc == HISTSUB && HISTSUB != '\0') ? (eChar)HIST : getC(0);
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
    if (c == (eChar)HIST) {
	if (alhistp)
	    return (alhistp);
	event = eventno;
    }
    else
	switch (c) {

	case ':':
	case '^':
	case '$':
	case '*':
	case '%':
	    ungetC(c);
	    if (lastev == eventno && alhistp)
		return (alhistp);
	    event = lastev;
	    break;

	case '#':		/* !# is command being typed in (mrh) */
	    if (--hleft == 0) {
		seterror(ERR_HISTLOOP);
		return (0);
	    }
	    else
		return (&paraml);
	    /* NOTREACHED */

	case '-':
	    back = 1;
	    c = getC(0);
	    /* FALLSTHROUGH */

	default:
	    if (any("(=~", c)) {
		unreadc(c);
		ungetC(HIST);
		return (0);
	    }
1203 1204
	    Strbuf_terminate(&lhsb);
	    lhsb.len = 0;
1205 1206 1207 1208 1209 1210
	    event = 0;
	    while (!cmap(c, _ESC | _META | _QF | _QB) && !any("^*-%${}:#", c)) {
		if (event != -1 && Isdigit(c))
		    event = event * 10 + c - '0';
		else
		    event = -1;
1211
		Strbuf_append1(&lhsb, c);
1212 1213 1214
		c = getC(0);
	    }
	    unreadc(c);
1215 1216
	    if (lhsb.len == 0) {
		lhsb.len = Strlen(lhsb.s); /* lhsb.s wasn't changed */
1217 1218 1219
		ungetC(HIST);
		return (0);
	    }
1220
	    Strbuf_terminate(&lhsb);
1221 1222 1223 1224 1225
	    if (event != -1) {
		/*
		 * History had only digits
		 */
		if (back)
1226
		    event = eventno + (alhistp == 0) - event;
1227 1228 1229
		break;
	    }
	    if (back) {
1230 1231 1232 1233
		Strbuf_append1(&lhsb, '\0'); /* Allocate space */
		Strbuf_terminate(&lhsb);
		memmove(lhsb.s + 1, lhsb.s, (lhsb.len - 1) * sizeof (*lhsb.s));
		lhsb.s[0] = '-';
1234
	    }
1235
	    hp = findev(lhsb.s, 0);
1236 1237 1238 1239 1240
	    if (hp)
		lastev = hp->Hnum;
	    return (&hp->Hlex);

	case '?':
1241 1242
	    Strbuf_terminate(&lhsb);
	    lhsb.len = 0;
1243 1244 1245 1246 1247 1248 1249 1250
	    for (;;) {
		c = getC(0);
		if (c == '\n') {
		    unreadc(c);
		    break;
		}
		if (c == '?')
		    break;
1251
		Strbuf_append1(&lhsb, c);
1252
	    }
1253 1254 1255
	    if (lhsb.len == 0) {
		lhsb.len = Strlen(lhsb.s); /* lhsb.s wasn't changed */
		if (lhsb.len == 0) {
1256 1257 1258 1259 1260
		    seterror(ERR_NOSEARCH);
		    return (0);
		}
	    }
	    else
1261 1262
		Strbuf_terminate(&lhsb);
	    hp = findev(lhsb.s, 1);
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
	    if (hp)
		lastev = hp->Hnum;
	    return (&hp->Hlex);
	}

    for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
	if (hp->Hnum == event) {
	    hp->Href = eventno;
	    lastev = hp->Hnum;
	    return (&hp->Hlex);
	}
1274
    np = putn((tcsh_number_t)event);
1275
    seterror(ERR_NOEVENT, short2str(np));
1276
    xfree(np);
1277 1278 1279 1280
    return (0);
}

static struct Hist *
1281
findev(Char *cp, int anyarg)
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
{
    struct Hist *hp;

    for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
	Char   *dp;
	Char *p, *q;
	struct wordent *lp = hp->Hlex.next;
	int     argno = 0;

	/*
	 * The entries added by alias substitution don't have a newline but do
	 * have a negative event number. Savehist() trims off these entries,
	 * but it happens before alias expansion, too early to delete those
	 * from the previous command.
	 */
	if (hp->Hnum < 0)
	    continue;
	if (lp->word[0] == '\n')
	    continue;
	if (!anyarg) {
	    p = cp;
	    q = lp->word;
	    do
		if (!*p)
		    return (hp);
	    while (*p++ == *q++);
	    continue;
	}
	do {
	    for (dp = lp->word; *dp; dp++) {
		p = cp;
		q = dp;
		do
		    if (!*p) {
			quesarg = argno;
			return (hp);
		    }
		while (*p++ == *q++);
	    }
	    lp = lp->next;
	    argno++;
	} while (lp->word[0] != '\n');
    }
    seterror(ERR_NOEVENT, short2str(cp));
    return (0);
}


static void
1331
setexclp(Char *cp)
1332 1333 1334 1335 1336 1337 1338
{
    if (cp && cp[0] == '\n')
	return;
    exclp = cp;
}

void
1339
unreadc(Char c)
1340 1341 1342 1343 1344
{
    peekread = (Char) c;
}

eChar
1345
readc(int wanteof)
1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
{
    eChar c;
    static  int sincereal;	/* Number of real EOFs we've seen */

#ifdef DEBUG_INP
    xprintf("readc\n");
#endif
    if ((c = peekread) != 0) {
	peekread = 0;
	return (c);
    }

top:
    aret = TCSH_F_SEEK;
    if (alvecp) {
	arun = 1;
#ifdef DEBUG_INP
	xprintf("alvecp %c\n", *alvecp & 0xff);
#endif
	aret = TCSH_A_SEEK;
	if ((c = *alvecp++) != 0)
	    return (c);
	if (alvec && *alvec) {
		alvecp = *alvec++;
		return (' ');
	}
	else {
	    alvecp = NULL;
	    aret = TCSH_F_SEEK;
	    return('\n');
	}
    }
    if (alvec) {
	arun = 1;
	if ((alvecp = *alvec) != 0) {
	    alvec++;
	    goto top;
	}
	/* Infinite source! */
	return ('\n');
    }
    arun = 0;
    if (evalp) {
	aret = TCSH_E_SEEK;
	if ((c = *evalp++) != 0)
	    return (c);
	if (evalvec && *evalvec) {
	    evalp = *evalvec++;
	    return (' ');
	}
	aret = TCSH_F_SEEK;
	evalp = 0;
    }
    if (evalvec) {
	if (evalvec == INVPPTR) {
	    doneinp = 1;
	    reset();
	}
	if ((evalp = *evalvec) != 0) {
	    evalvec++;
	    goto top;
	}
	evalvec = INVPPTR;
	return ('\n');
    }
    do {
	if (arginp == INVPTR || onelflg == 1) {
	    if (wanteof)
		return CHAR_ERR;
	    exitstat();
	}
	if (arginp) {
	    if ((c = *arginp++) == 0) {
		arginp = INVPTR;
		return ('\n');
	    }
	    return (c);
	}
#ifdef BSDJOBS
reread:
#endif /* BSDJOBS */
	c = bgetc();
	if (c == CHAR_ERR) {
#ifndef WINNT_NATIVE
# ifndef POSIX
#  ifdef TERMIO
	    struct termio tty;
#  else /* SGTTYB */
	    struct sgttyb tty;
#  endif /* TERMIO */
# else /* POSIX */
	    struct termios tty;
# endif /* POSIX */
#endif /* !WINNT_NATIVE */
	    if (wanteof)
		return CHAR_ERR;
	    /* was isatty but raw with ignoreeof yields problems */
#ifndef WINNT_NATIVE
# ifndef POSIX
#  ifdef TERMIO
	    if (ioctl(SHIN, TCGETA, (ioctl_t) & tty) == 0 &&
		(tty.c_lflag & ICANON))
#  else /* GSTTYB */
	    if (ioctl(SHIN, TIOCGETP, (ioctl_t) & tty) == 0 &&
		(tty.sg_flags & RAW) == 0)
#  endif /* TERMIO */
# else /* POSIX */
	    if (tcgetattr(SHIN, &tty) == 0 &&
		(tty.c_lflag & ICANON))
# endif /* POSIX */
#else /* WINNT_NATIVE */
	    if (isatty(SHIN))
#endif /* !WINNT_NATIVE */
	    {
#ifdef BSDJOBS
1461
		pid_t ctpgrp;
1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
#endif /* BSDJOBS */

		if (numeof != 0 && ++sincereal >= numeof)	/* Too many EOFs?  Bye! */
		    goto oops;
#ifdef BSDJOBS
		if (tpgrp != -1 &&
		    (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
		    tpgrp != ctpgrp) {
		    (void) tcsetpgrp(FSHTTY, tpgrp);
# ifdef _SEQUENT_
		    if (ctpgrp)
# endif /* _SEQUENT */
1474
		    (void) killpg(ctpgrp, SIGHUP);
1475 1476 1477 1478 1479 1480 1481
# ifdef notdef
		    /*
		     * With the walking process group fix, this message
		     * is now obsolete. As the foreground process group
		     * changes, the shell needs to adjust. Well too bad.
		     */
		    xprintf(CGETS(16, 1, "Reset tty pgrp from %d to %d\n"),
1482
			    (int)ctpgrp, (int)tpgrp);
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
# endif /* notdef */
		    goto reread;
		}
#endif /* BSDJOBS */
		/* What follows is complicated EOF handling -- sterling@netcom.com */
		/* First, we check to see if we have ignoreeof set */
		if (adrof(STRignoreeof)) {
			/* If so, we check for any stopped jobs only on the first EOF */
			if ((sincereal == 1) && (chkstop == 0)) {
				panystop(1);
			}
		} else {
			/* If we don't have ignoreeof set, always check for stopped jobs */
			if (chkstop == 0) {
				panystop(1);
			}
		}
		/* At this point, if there were stopped jobs, we would have already
		 * called reset().  If we got this far, assume we can print an
		 * exit/logout message if we ignoreeof, or just exit.
		 */
		if (adrof(STRignoreeof)) {
			/* If so, tell the user to use exit or logout */
		    if (loginsh) {
1507
				xprintf("%s", CGETS(16, 2,
1508 1509 1510 1511 1512 1513
					"\nUse \"logout\" to logout.\n"));
		   	} else {
				xprintf(CGETS(16, 3,
					"\nUse \"exit\" to leave %s.\n"),
					progname);
			}
1514
		    reset();
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
		} else {
			/* If we don't have ignoreeof set, just fall through */
			;	/* EMPTY */
		}
	    }
    oops:
	    doneinp = 1;
	    reset();
	}
	sincereal = 0;
	if (c == '\n' && onelflg)
	    onelflg--;
    } while (c == 0);
1528
    Strbuf_append1(&histline, c);
1529 1530 1531 1532
    return (c);
}

static void
1533
balloc(int buf)
1534 1535 1536 1537
{
    Char **nfbuf;

    while (buf >= fblocks) {
1538
	nfbuf = xcalloc(fblocks + 2, sizeof(Char **));
1539 1540
	if (fbuf) {
	    (void) blkcpy(nfbuf, fbuf);
1541
	    xfree(fbuf);
1542 1543
	}
	fbuf = nfbuf;
1544
	fbuf[fblocks] = xcalloc(BUFSIZE, sizeof(Char));
1545 1546 1547 1548
	fblocks++;
    }
}

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 1556 1557 1558 1559
    int err;

    if (nchars == 0)
	return 0;
    assert (nchars <= sizeof(cbuf) / sizeof(*cbuf));
1560 1561 1562 1563 1564
    USE(use_fclens);
    res = 0;
    partial = 0;
    do {
	size_t i;
1565 1566 1567 1568
	size_t len = nchars > partial ? nchars - partial : 1;

	if (partial + len >= sizeof(cbuf) / sizeof(*cbuf))
	    break;
1569
	
1570 1571
	r = xread(fildes, cbuf + partial, len);
		  
1572 1573 1574 1575
	if (partial == 0 && r <= 0)
	    break;
	partial += r;
	i = 0;
1576 1577
	while (i < partial && nchars != 0) {
	    int tlen;
1578

1579 1580
	    tlen = normal_mbtowc(buf + res, cbuf + i, partial - i);
	    if (tlen == -1) {
1581
	        reset_mbtowc();
1582
		if ((partial - i) < MB_LEN_MAX && r > 0)
1583 1584 1585 1586 1587
		    /* Maybe a partial character and there is still a chance
		       to read more */
		    break;
		buf[res] = (unsigned char)cbuf[i] | INVALID_BYTE;
	    }
1588 1589
	    if (tlen <= 0)
		tlen = 1;
1590 1591
#ifdef WIDE_STRINGS
	    if (use_fclens)
1592
		fclens[res] = tlen;
1593
#endif
1594
	    i += tlen;
1595 1596 1597 1598 1599 1600
	    res++;
	    nchars--;
	}
	if (i != partial)
	    memmove(cbuf, cbuf + i, partial - i);
	partial -= i;
1601 1602 1603 1604 1605 1606
    } while (partial != 0 && nchars > 0);
    /* Throwing away possible partial multibyte characters on error if the
       stream is not seekable */
    err = errno;
    lseek(fildes, -(off_t)partial, L_INCR);
    errno = err;
1607 1608 1609 1610
    return res != 0 ? res : r;
}

static eChar
1611
bgetc(void)
1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
{
    Char ch;
    int c, off, buf;
    int numleft = 0, roomleft;

    if (cantell) {
	if (fseekp < fbobp || fseekp > feobp) {
	    fbobp = feobp = fseekp;
	    (void) lseek(SHIN, fseekp, L_SET);
	}
	if (fseekp == feobp) {
1623 1624 1625 1626 1627 1628 1629 1630 1631
#ifdef WIDE_STRINGS
	    off_t bytes;
	    size_t i;

	    bytes = fbobp;
	    for (i = 0; i < (size_t)(feobp - fbobp); i++)
		bytes += fclens[i];
	    fseekp = feobp = bytes;
#endif
1632
	    fbobp = feobp;
1633
	    c = wide_read(SHIN, fbuf[0], BUFSIZE, 1);
1634 1635 1636 1637 1638 1639 1640 1641
#ifdef convex
	    if (c < 0)
		stderror(ERR_SYSTEM, progname, strerror(errno));
#endif /* convex */
	    if (c <= 0)
		return CHAR_ERR;
	    feobp += c;
	}
1642
#if !defined(WINNT_NATIVE) && !defined(__CYGWIN__)
1643 1644 1645 1646 1647 1648 1649
	ch = fbuf[0][fseekp - fbobp];
	fseekp++;
#else
	do {
	    ch = fbuf[0][fseekp - fbobp];
	    fseekp++;
	} while(ch == '\r');
1650
#endif /* !WINNT_NATIVE && !__CYGWIN__ */
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
	return (ch);
    }

    while (fseekp >= feobp) {
	if ((editing
#if defined(FILEC) && defined(TIOCSTI)
	    || filec
#endif /* FILEC && TIOCSTI */
	    ) && intty) {		/* then use twenex routine */
	    fseekp = feobp;		/* where else? */
#if defined(FILEC) && defined(TIOCSTI)
	    if (!editing)
		c = numleft = tenex(InputBuf, BUFSIZE);
	    else
#endif /* FILEC && TIOCSTI */
	    c = numleft = Inputl();	/* PWP: get a line */
	    while (numleft > 0) {
		off = (int) feobp % BUFSIZE;
		buf = (int) feobp / BUFSIZE;
		balloc(buf);
		roomleft = BUFSIZE - off;
		if (roomleft > numleft)
		    roomleft = numleft;
1674 1675
		(void) memcpy(fbuf[buf] + off, InputBuf + c - numleft,
			      roomleft * sizeof(Char));
1676 1677 1678 1679 1680 1681 1682 1683
		numleft -= roomleft;
		feobp += roomleft;
	    }
	} else {
	    off = (int) feobp % BUFSIZE;
	    buf = (int) feobp / BUFSIZE;
	    balloc(buf);
	    roomleft = BUFSIZE - off;
1684
	    c = wide_read(SHIN, fbuf[buf] + off, roomleft, 0);
1685 1686 1687 1688 1689 1690 1691 1692 1693 1694
	    if (c > 0)
		feobp += c;
	}
	if (c == 0 || (c < 0 && fixio(SHIN, errno) == -1))
	    return CHAR_ERR;
    }
#ifdef SIG_WINDOW
    if (windowchg)
	(void) check_window_size(0);	/* for window systems */
#endif /* SIG_WINDOW */
1695
#if !defined(WINNT_NATIVE) && !defined(__CYGWIN__)
1696 1697 1698 1699 1700 1701 1702
    ch = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE];
    fseekp++;
#else
    do {
	ch = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE];
	fseekp++;
    } while(ch == '\r');
1703
#endif /* !WINNT_NATIVE && !__CYGWIN__ */
1704 1705 1706 1707
    return (ch);
}

static void
1708
bfree(void)
1709 1710 1711 1712 1713 1714 1715 1716 1717 1718
{
    int sb, i;

    if (cantell)
	return;
    if (whyles)
	return;
    sb = (int) (fseekp - 1) / BUFSIZE;
    if (sb > 0) {
	for (i = 0; i < sb; i++)
1719
	    xfree(fbuf[i]);
1720 1721 1722 1723 1724 1725 1726 1727
	(void) blkcpy(fbuf, &fbuf[sb]);
	fseekp -= BUFSIZE * sb;
	feobp -= BUFSIZE * sb;
	fblocks -= sb;
    }
}

void
1728
bseek(struct Ain *l)
1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
{
    switch (aret = l->type) {
    case TCSH_E_SEEK:
	evalvec = l->a_seek;
	evalp = l->c_seek;
#ifdef DEBUG_SEEK
	xprintf(CGETS(16, 4, "seek to eval %x %x\n"), evalvec, evalp);
#endif
	return;
    case TCSH_A_SEEK:
	alvec = l->a_seek;
	alvecp = l->c_seek;
#ifdef DEBUG_SEEK
	xprintf(CGETS(16, 5, "seek to alias %x %x\n"), alvec, alvecp);
#endif
	return;
    case TCSH_F_SEEK:	
#ifdef DEBUG_SEEK
	xprintf(CGETS(16, 6, "seek to file %x\n"), fseekp);
#endif
	fseekp = l->f_seek;
#ifdef WIDE_STRINGS
	if (cantell) {
1752
	    if (fseekp >= fbobp && feobp >= fbobp) {
1753 1754 1755 1756
		size_t i;
		off_t o;

		o = fbobp;
1757
		for (i = 0; i < (size_t)(feobp - fbobp); i++) {
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780
		    if (fseekp == o) {
			fseekp = fbobp + i;
			return;
		    }
		    o += fclens[i];
		}
		if (fseekp == o) {
		    fseekp = feobp;
		    return;
		}
	    }
	    fbobp = feobp = fseekp + 1; /* To force lseek() */
	}
#endif
	return;
    default:
	xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret);
	abort();
    }
}

/* any similarity to bell telephone is purely accidental */
void
1781
btell(struct Ain *l)
1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799
{
    switch (l->type = aret) {
    case TCSH_E_SEEK:
	l->a_seek = evalvec;
	l->c_seek = evalp;
#ifdef DEBUG_SEEK
	xprintf(CGETS(16, 8, "tell eval %x %x\n"), evalvec, evalp);
#endif
	return;
    case TCSH_A_SEEK:
	l->a_seek = alvec;
	l->c_seek = alvecp;
#ifdef DEBUG_SEEK
	xprintf(CGETS(16, 9, "tell alias %x %x\n"), alvec, alvecp);
#endif
	return;
    case TCSH_F_SEEK:
#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 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
		l->f_seek += fclens[i];
	} else
#endif
	    /*SUPPRESS 112*/
	    l->f_seek = fseekp;
	l->a_seek = NULL;
#ifdef DEBUG_SEEK
	xprintf(CGETS(16, 10, "tell file %x\n"), fseekp);
#endif
	return;
    default:
	xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret);
	abort();
    }
}

void
1822
btoeof(void)
1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835
{
    (void) lseek(SHIN, (off_t) 0, L_XTND);
    aret = TCSH_F_SEEK;
    fseekp = feobp;
    alvec = NULL;
    alvecp = NULL;
    evalvec = NULL;
    evalp = NULL;
    wfree();
    bfree();
}

void
1836
settell(void)
1837 1838 1839 1840 1841 1842 1843
{
    off_t x;
    cantell = 0;
    if (arginp || onelflg || intty)
	return;
    if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1)
	return;
1844
    fbuf = xcalloc(2, sizeof(Char **));
1845
    fblocks = 1;
1846
    fbuf[0] = xcalloc(BUFSIZE, sizeof(Char));
1847 1848 1849
    fseekp = fbobp = feobp = x;
    cantell = 1;
}