Home | History | Annotate | Download | only in csh
      1 /*
      2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1980 Regents of the University of California.
     11  * All rights reserved. The Berkeley Software License Agreement
     12  * specifies the terms and conditions for redistribution.
     13  */
     14 
     15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16 
     17 #include "sh.h"
     18 #include "sh.tconst.h"
     19 extern int	didchdir;
     20 
     21 /*
     22  * C Shell
     23  */
     24 
     25 void	asx(tchar *, int, tchar *);
     26 void	putn1(int);
     27 void	set(tchar *, tchar *);
     28 void	set1(tchar *, tchar **, struct varent *);
     29 void	setq(tchar *, tchar **, struct varent *);
     30 void	unset1(tchar *[], struct varent *);
     31 void	unsetv1(struct varent *);
     32 void	exportpath(tchar **);
     33 void	balance(struct varent *, int, int);
     34 tchar	*operate(tchar, tchar *, tchar *);
     35 tchar	*getinx(tchar *, int *);
     36 tchar	*xset(tchar *, tchar ***);
     37 struct varent	*getvx(tchar *, int);
     38 
     39 void
     40 doset(tchar **v)
     41 {
     42 	tchar *p;
     43 	tchar *vp, op;
     44 	tchar **vecp;
     45 	bool hadsub;
     46 	int subscr;
     47 	tchar *retp;
     48 
     49 #ifdef TRACE
     50 	tprintf("TRACE- doset()\n");
     51 #endif
     52 	v++;
     53 	p = *v++;
     54 	if (p == 0) {
     55 		prvars();
     56 		return;
     57 	}
     58 	do {
     59 		hadsub = 0;
     60 		/*
     61 		 * check for proper variable syntax
     62 		 * must be alphanumeric, start with a letter and
     63 		 * be at most 20 characters
     64 		 */
     65 		for (vp = p; alnum(*p); p++)
     66 			continue;
     67 		if (vp == p || !letter(*vp))
     68 			goto setsyn;
     69 		if ((p - vp) > MAX_VAR_LEN)
     70 			bferr("Variable name too long");
     71 		if (*p == '[') {
     72 			hadsub++;
     73 			p = getinx(p, &subscr);
     74 		}
     75 		if (op = *p) {
     76 			*p++ = 0;
     77 			if (*p == 0 && *v && **v == '(')
     78 				p = *v++;
     79 		} else if (*v && eq(*v, S_EQ /* "=" */)) {
     80 			op = '=', v++;
     81 			if (*v)
     82 				p = *v++;
     83 		}
     84 		if (op && op != '=')
     85 setsyn:
     86 			bferr("Syntax error");
     87 		if (eq(p, S_LPAR /* "(" */)) {
     88 			tchar **e = v;
     89 
     90 			if (hadsub)
     91 				goto setsyn;
     92 			for (;;) {
     93 				if (!*e)
     94 					bferr("Missing )");
     95 				if (**e == ')')
     96 					break;
     97 				e++;
     98 			}
     99 			p = *e;
    100 			*e = 0;
    101 			vecp = saveblk(v);
    102 			set1(vp, vecp, &shvhed);
    103 			*e = p;
    104 			v = e + 1;
    105 		} else if (hadsub) {
    106 			retp = savestr(p);
    107 			asx(vp, subscr, retp);
    108 			xfree(retp);
    109 			retp = 0;
    110 		} else
    111 			set(vp, savestr(p));
    112 		if (eq(vp, S_path /* "path" */)) {
    113 			exportpath(adrof(S_path /* "path" */)->vec);
    114 			dohash(xhash);
    115 		} else if (eq(vp, S_histchars /* "histchars" */)) {
    116 			tchar *p = value(S_histchars /* "histchars" */);
    117 			HIST = *p++;
    118 			HISTSUB = *p;
    119 		} else if (eq(vp, S_user /* "user" */))
    120 			local_setenv(S_USER /* "USER" */, value(vp));
    121 		else if (eq(vp, S_term /* "term" */))
    122 			local_setenv(S_TERM /* "TERM" */, value(vp));
    123 		else if (eq(vp, S_home /* "home" */))
    124 			local_setenv(S_HOME /* "HOME" */, value(vp));
    125 #ifdef FILEC
    126 		else if (eq(vp, S_filec /* "filec" */))
    127 			filec = 1;
    128 		else if (eq(vp, S_cdpath /* "cdpath" */))
    129 			dohash(xhash2);
    130 #endif
    131 	} while (p = *v++);
    132 }
    133 
    134 tchar *
    135 getinx(tchar *cp, int *ip)
    136 {
    137 
    138 #ifdef TRACE
    139 	tprintf("TRACE- getinx()\n");
    140 #endif
    141 	*ip = 0;
    142 	*cp++ = 0;
    143 	while (*cp && digit(*cp))
    144 		*ip = *ip * 10 + *cp++ - '0';
    145 	if (*cp++ != ']')
    146 		bferr("Subscript error");
    147 	return (cp);
    148 }
    149 
    150 void
    151 asx(tchar *vp, int subscr, tchar *p)
    152 {
    153 	struct varent *v = getvx(vp, subscr);
    154 
    155 #ifdef TRACE
    156 	tprintf("TRACE- asx()\n");
    157 #endif
    158 	xfree(v->vec[subscr - 1]);
    159 	v->vec[subscr - 1] = globone(p);
    160 }
    161 
    162 struct varent *
    163 getvx(tchar *vp, int subscr)
    164 {
    165 	struct varent *v = adrof(vp);
    166 
    167 #ifdef TRACE
    168 	tprintf("TRACE- getvx()\n");
    169 #endif
    170 	if (v == 0)
    171 		udvar(vp);
    172 	if (subscr < 1 || subscr > blklen(v->vec))
    173 		bferr("Subscript out of range");
    174 	return (v);
    175 }
    176 
    177 tchar plusplus[2] = { '1', 0 };
    178 
    179 void
    180 dolet(tchar **v)
    181 {
    182 	tchar *p;
    183 	tchar *vp, c, op;
    184 	bool hadsub;
    185 	int subscr;
    186 
    187 	v++;
    188 	p = *v++;
    189 	if (p == 0) {
    190 		prvars();
    191 		return;
    192 	}
    193 	do {
    194 		hadsub = 0;
    195 		for (vp = p; alnum(*p); p++)
    196 			continue;
    197 		if (vp == p || !letter(*vp))
    198 			goto letsyn;
    199 		if (*p == '[') {
    200 			hadsub++;
    201 			p = getinx(p, &subscr);
    202 		}
    203 		if (*p == 0 && *v)
    204 			p = *v++;
    205 		if (op = *p)
    206 			*p++ = 0;
    207 		else
    208 			goto letsyn;
    209 		vp = savestr(vp);
    210 		if (op == '=') {
    211 			c = '=';
    212 			p = xset(p, &v);
    213 		} else {
    214 			c = *p++;
    215 			/* if (any(c, "+-")) { */
    216 			if (c == '+' || c == '-') {
    217 				if (c != op || *p)
    218 					goto letsyn;
    219 				p = plusplus;
    220 			} else {
    221 				/* if (any(op, "<>")) { */
    222 				if (op == '<' || op == '>') {
    223 					if (c != op)
    224 						goto letsyn;
    225 					c = *p++;
    226 letsyn:
    227 					bferr("Syntax error");
    228 				}
    229 				if (c != '=')
    230 					goto letsyn;
    231 				p = xset(p, &v);
    232 			}
    233 		}
    234 		if (op == '=')
    235 			if (hadsub)
    236 				asx(vp, subscr, p);
    237 			else
    238 				set(vp, p);
    239 		else
    240 			if (hadsub)
    241 #ifndef V6
    242 				/* avoid bug in vax CC */
    243 				{
    244 					struct varent *gv = getvx(vp, subscr);
    245 
    246 					asx(vp, subscr, operate(op, gv->vec[subscr - 1], p));
    247 				}
    248 #else
    249 				asx(vp, subscr, operate(op, getvx(vp, subscr)->vec[subscr - 1], p));
    250 #endif
    251 			else
    252 				set(vp, operate(op, value(vp), p));
    253 		if (eq(vp, S_path /* "path" */)) {
    254 			exportpath(adrof(S_path /* "path" */)->vec);
    255 			dohash(xhash);
    256 		}
    257 
    258 		if (eq(vp, S_cdpath /* "cdpath" */))
    259 			dohash(xhash2);
    260 
    261 		xfree(vp);
    262 		if (c != '=')
    263 			xfree(p);
    264 	} while (p = *v++);
    265 }
    266 
    267 tchar *
    268 xset(tchar *cp, tchar ***vp)
    269 {
    270 	tchar *dp;
    271 
    272 #ifdef TRACE
    273 	tprintf("TRACE- xset()\n");
    274 #endif
    275 	if (*cp) {
    276 		dp = savestr(cp);
    277 		--(*vp);
    278 		xfree(**vp);
    279 		**vp = dp;
    280 	}
    281 	return (putn(exp(vp)));
    282 }
    283 
    284 tchar *
    285 operate(tchar op, tchar *vp, tchar *p)
    286 {
    287 	tchar opr[2];
    288 	tchar *vec[5];
    289 	tchar **v = vec;
    290 	tchar **vecp = v;
    291 	int i;
    292 
    293 	if (op != '=') {
    294 		if (*vp)
    295 			*v++ = vp;
    296 		opr[0] = op;
    297 		opr[1] = 0;
    298 		*v++ = opr;
    299 		if (op == '<' || op == '>')
    300 			*v++ = opr;
    301 	}
    302 	*v++ = p;
    303 	*v++ = 0;
    304 	i = exp(&vecp);
    305 	if (*vecp)
    306 		bferr("Expression syntax");
    307 	return (putn(i));
    308 }
    309 
    310 static tchar *putp;
    311 
    312 tchar *
    313 putn(int n)
    314 {
    315 	static tchar number[15];
    316 
    317 #ifdef TRACE
    318 	tprintf("TRACE- putn()\n");
    319 #endif
    320 	putp = number;
    321 	if (n < 0) {
    322 		n = -n;
    323 		*putp++ = '-';
    324 	}
    325 	if (sizeof (int) == 2 && n == -32768) {
    326 		*putp++ = '3';
    327 		n = 2768;
    328 #ifdef pdp11
    329 	}
    330 #else
    331 	} else if (sizeof (int) == 4 && n == 0x80000000) {
    332 		*putp++ = '2';
    333 		n = 147483648;
    334 	}
    335 #endif
    336 	putn1(n);
    337 	*putp = 0;
    338 	return (savestr(number));
    339 }
    340 
    341 void
    342 putn1(int n)
    343 {
    344 #ifdef TRACE
    345 	tprintf("TRACE- putn1()\n");
    346 #endif
    347 	if (n > 9)
    348 		putn1(n / 10);
    349 	*putp++ = n % 10 + '0';
    350 }
    351 
    352 int
    353 getn(tchar *cp)
    354 {
    355 	int n;
    356 	int sign;
    357 
    358 #ifdef TRACE
    359 	tprintf("TRACE- getn()\n");
    360 #endif
    361 	sign = 0;
    362 	if (cp[0] == '+' && cp[1])
    363 		cp++;
    364 	if (*cp == '-') {
    365 		sign++;
    366 		cp++;
    367 		if (!digit(*cp))
    368 			goto badnum;
    369 	}
    370 	n = 0;
    371 	while (digit(*cp))
    372 		n = n * 10 + *cp++ - '0';
    373 	if (*cp)
    374 		goto badnum;
    375 	return (sign ? -n : n);
    376 badnum:
    377 	bferr("Badly formed number");
    378 	return (0);
    379 }
    380 
    381 tchar *
    382 value1(tchar *var, struct varent *head)
    383 {
    384 	struct varent *vp;
    385 
    386 #ifdef TRACE
    387 	tprintf("TRACE- value1()\n");
    388 #endif
    389 	vp = adrof1(var, head);
    390 	return (vp == 0 || vp->vec[0] == 0 ? S_ /* "" */ : vp->vec[0]);
    391 }
    392 
    393 struct varent *
    394 madrof(tchar *pat, struct varent *vp)
    395 {
    396 	struct varent *vp1;
    397 
    398 #ifdef TRACE
    399 	tprintf("TRACE- madrof()\n");
    400 #endif
    401 	for (; vp; vp = vp->v_right) {
    402 		if (vp->v_left && (vp1 = madrof(pat, vp->v_left)))
    403 			return vp1;
    404 		if (Gmatch(vp->v_name, pat))
    405 			return vp;
    406 	}
    407 	return vp;
    408 }
    409 
    410 struct varent *
    411 adrof1(tchar *name, struct varent *v)
    412 {
    413 	int cmp;
    414 
    415 #ifdef TRACE
    416 	tprintf("TRACE- adrof1()\n");
    417 #endif
    418 	v = v->v_left;
    419 	while (v && ((cmp = *name - *v->v_name) ||
    420 	    (cmp = strcmp_(name, v->v_name))))
    421 		if (cmp < 0)
    422 			v = v->v_left;
    423 		else
    424 			v = v->v_right;
    425 	return v;
    426 }
    427 
    428 /*
    429  * The caller is responsible for putting value in a safe place
    430  */
    431 void
    432 set(tchar *var, tchar *val)
    433 {
    434 	tchar **vec =  (tchar **)xalloc(2 * sizeof (tchar **));
    435 
    436 #ifdef TRACE
    437 	tprintf("TRACE- set()\n");
    438 #endif
    439 	vec[0] = onlyread(val) ? savestr(val) : val;
    440 	vec[1] = 0;
    441 	set1(var, vec, &shvhed);
    442 }
    443 
    444 void
    445 set1(tchar *var, tchar **vec, struct varent *head)
    446 {
    447 	tchar **oldv = vec;
    448 
    449 #ifdef TRACE
    450 	tprintf("TRACE- set1()\n");
    451 #endif
    452 	gflag = 0;
    453 	/*
    454 	 * If setting cwd variable via "set cwd=/tmp/something"
    455 	 * then do globbing.  But if we are setting the cwd
    456 	 * becuz of a cd, chdir, pushd, popd, do not do globbing.
    457 	 */
    458 	if ((!(eq(var, S_cwd))) || (eq(var, S_cwd) && (didchdir == 0)))
    459 		{
    460 		tglob(oldv);
    461 		}
    462 	if (gflag) {
    463 		vec = glob(oldv);
    464 		if (vec == 0) {
    465 			bferr("No match");
    466 			blkfree(oldv);
    467 			return;
    468 		}
    469 		blkfree(oldv);
    470 		gargv = 0;
    471 	}
    472 	setq(var, vec, head);
    473 }
    474 
    475 void
    476 setq(tchar *name, tchar **vec, struct varent *p)
    477 {
    478 	struct varent *c;
    479 	int f;
    480 
    481 #ifdef TRACE
    482 	tprintf("TRACE- setq()\n");
    483 #endif
    484 	f = 0;			/* tree hangs off the header's left link */
    485 	while (c = p->v_link[f]) {
    486 		if ((f = *name - *c->v_name) == 0 &&
    487 		    (f = strcmp_(name, c->v_name)) == 0) {
    488 			blkfree(c->vec);
    489 			goto found;
    490 		}
    491 		p = c;
    492 		f = f > 0;
    493 	}
    494 	p->v_link[f] = c = (struct varent *)xalloc(sizeof (struct varent));
    495 	c->v_name = savestr(name);
    496 	c->v_bal = 0;
    497 	c->v_left = c->v_right = 0;
    498 	c->v_parent = p;
    499 	balance(p, f, 0);
    500 found:
    501 	trim(c->vec = vec);
    502 }
    503 
    504 void
    505 unset(tchar *v[])
    506 {
    507 
    508 #ifdef TRACE
    509 	tprintf("TRACE- unset()\n");
    510 #endif
    511 	unset1(v, &shvhed);
    512 	if (adrof(S_histchars /* "histchars" */) == 0) {
    513 		HIST = '!';
    514 		HISTSUB = '^';
    515 	}
    516 #ifdef FILEC
    517 	if (adrof(S_filec /* "filec" */) == 0)
    518 		filec = 0;
    519 #endif
    520 }
    521 
    522 void
    523 unset1(tchar *v[], struct varent *head)
    524 {
    525 	struct varent *vp;
    526 	int cnt;
    527 
    528 #ifdef TRACE
    529 	tprintf("TRACE- unset1()\n");
    530 #endif
    531 	while (*++v) {
    532 		cnt = 0;
    533 		while (vp = madrof(*v, head->v_left))
    534 			unsetv1(vp), cnt++;
    535 		if (cnt == 0)
    536 			setname(*v);
    537 	}
    538 }
    539 
    540 void
    541 unsetv(tchar *var)
    542 {
    543 	struct varent *vp;
    544 
    545 #ifdef TRACE
    546 	tprintf("TRACE- unsetv()\n");
    547 #endif
    548 	if ((vp = adrof1(var, &shvhed)) == 0)
    549 		udvar(var);
    550 	unsetv1(vp);
    551 }
    552 
    553 void
    554 unsetv1(struct varent *p)
    555 {
    556 	struct varent *c, *pp;
    557 	int f;
    558 
    559 #ifdef TRACE
    560 	tprintf("TRACE- unsetv1()\n");
    561 #endif
    562 	/*
    563 	 * Free associated memory first to avoid complications.
    564 	 */
    565 	blkfree(p->vec);
    566 	xfree(p->v_name);
    567 	/*
    568 	 * If p is missing one child, then we can move the other
    569 	 * into where p is.  Otherwise, we find the predecessor
    570 	 * of p, which is guaranteed to have no right child, copy
    571 	 * it into p, and move it's left child into it.
    572 	 */
    573 	if (p->v_right == 0)
    574 		c = p->v_left;
    575 	else if (p->v_left == 0)
    576 		c = p->v_right;
    577 	else {
    578 		for (c = p->v_left; c->v_right; c = c->v_right)
    579 			;
    580 		p->v_name = c->v_name;
    581 		p->vec = c->vec;
    582 		p = c;
    583 		c = p->v_left;
    584 	}
    585 	/*
    586 	 * Move c into where p is.
    587 	 */
    588 	pp = p->v_parent;
    589 	f = pp->v_right == p;
    590 	if (pp->v_link[f] = c)
    591 		c->v_parent = pp;
    592 	/*
    593 	 * Free the deleted node, and rebalance.
    594 	 */
    595 	xfree(p);
    596 	balance(pp, f, 1);
    597 }
    598 
    599 void
    600 setNS(tchar *cp)
    601 {
    602 #ifdef TRACE
    603 	tprintf("TRACE- setNS()\n");
    604 #endif
    605 
    606 	set(cp, S_ /* "" */);
    607 }
    608 
    609 void
    610 shift(tchar **v)
    611 {
    612 	struct varent *argv;
    613 	tchar *name;
    614 
    615 #ifdef TRACE
    616 	tprintf("TRACE- shift()\n");
    617 #endif
    618 	v++;
    619 	name = *v;
    620 	if (name == 0)
    621 		name = S_argv /* "argv" */;
    622 	else
    623 		(void) strip(name);
    624 	argv = adrof(name);
    625 	if (argv == 0)
    626 		udvar(name);
    627 	if (argv->vec[0] == 0)
    628 		bferr("No more words");
    629 	lshift(argv->vec, 1);
    630 }
    631 
    632 void
    633 exportpath(tchar **val)
    634 {
    635 	tchar exppath[PATHSIZ];
    636 
    637 #ifdef TRACE
    638 	tprintf("TRACE- exportpath()\n");
    639 #endif
    640 	exppath[0] = 0;
    641 	if (val)
    642 		while (*val) {
    643 			if (strlen_(*val) + strlen_(exppath) + 2 > PATHSIZ) {
    644 				printf("Warning: ridiculously long PATH truncated\n");
    645 				break;
    646 			}
    647 			(void) strcat_(exppath, *val++);
    648 			if (*val == 0 || eq(*val, S_RPAR /* ")" */))
    649 				break;
    650 			(void) strcat_(exppath, S_COLON /* ":" */);
    651 		}
    652 	local_setenv(S_PATH /* "PATH" */, exppath);
    653 }
    654 
    655 	/* macros to do single rotations on node p */
    656 #define	rright(p) (\
    657 	t = (p)->v_left,\
    658 	(t)->v_parent = (p)->v_parent,\
    659 	((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\
    660 	(t->v_right = (p))->v_parent = t,\
    661 	(p) = t)
    662 #define	rleft(p) (\
    663 	t = (p)->v_right,\
    664 	(t)->v_parent = (p)->v_parent,\
    665 	((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\
    666 	(t->v_left = (p))->v_parent = t,\
    667 	(p) = t)
    668 
    669 /*
    670  * Rebalance a tree, starting at p and up.
    671  * F == 0 means we've come from p's left child.
    672  * D == 1 means we've just done a delete, otherwise an insert.
    673  */
    674 void
    675 balance(struct varent *p, int f, int d)
    676 {
    677 	struct varent *pp;
    678 	struct varent *t;		/* used by the rotate macros */
    679 	int ff;
    680 
    681 #ifdef TRACE
    682 	tprintf("TRACE- balance()\n");
    683 #endif
    684 	/*
    685 	 * Ok, from here on, p is the node we're operating on;
    686 	 * pp is it's parent; f is the branch of p from which we have come;
    687 	 * ff is the branch of pp which is p.
    688 	 */
    689 	for (; pp = p->v_parent; p = pp, f = ff) {
    690 		ff = pp->v_right == p;
    691 		if (f ^ d) {		/* right heavy */
    692 			switch (p->v_bal) {
    693 			case -1:		/* was left heavy */
    694 				p->v_bal = 0;
    695 				break;
    696 			case 0:			/* was balanced */
    697 				p->v_bal = 1;
    698 				break;
    699 			case 1:			/* was already right heavy */
    700 				switch (p->v_right->v_bal) {
    701 				case 1:			/* sigle rotate */
    702 					pp->v_link[ff] = rleft(p);
    703 					p->v_left->v_bal = 0;
    704 					p->v_bal = 0;
    705 					break;
    706 				case 0:			/* single rotate */
    707 					pp->v_link[ff] = rleft(p);
    708 					p->v_left->v_bal = 1;
    709 					p->v_bal = -1;
    710 					break;
    711 				case -1:		/* double rotate */
    712 					rright(p->v_right);
    713 					pp->v_link[ff] = rleft(p);
    714 					p->v_left->v_bal =
    715 						p->v_bal < 1 ? 0 : -1;
    716 					p->v_right->v_bal =
    717 						p->v_bal > -1 ? 0 : 1;
    718 					p->v_bal = 0;
    719 					break;
    720 				}
    721 				break;
    722 			}
    723 		} else {		/* left heavy */
    724 			switch (p->v_bal) {
    725 			case 1:			/* was right heavy */
    726 				p->v_bal = 0;
    727 				break;
    728 			case 0:			/* was balanced */
    729 				p->v_bal = -1;
    730 				break;
    731 			case -1:		/* was already left heavy */
    732 				switch (p->v_left->v_bal) {
    733 				case -1:		/* single rotate */
    734 					pp->v_link[ff] = rright(p);
    735 					p->v_right->v_bal = 0;
    736 					p->v_bal = 0;
    737 					break;
    738 				case 0:			/* signle rotate */
    739 					pp->v_link[ff] = rright(p);
    740 					p->v_right->v_bal = -1;
    741 					p->v_bal = 1;
    742 					break;
    743 				case 1:			/* double rotate */
    744 					rleft(p->v_left);
    745 					pp->v_link[ff] = rright(p);
    746 					p->v_left->v_bal =
    747 						p->v_bal < 1 ? 0 : -1;
    748 					p->v_right->v_bal =
    749 						p->v_bal > -1 ? 0 : 1;
    750 					p->v_bal = 0;
    751 					break;
    752 				}
    753 				break;
    754 			}
    755 		}
    756 		/*
    757 		 * If from insert, then we terminate when p is balanced.
    758 		 * If from delete, then we terminate when p is unbalanced.
    759 		 */
    760 		if ((p->v_bal == 0) ^ d)
    761 			break;
    762 	}
    763 }
    764 
    765 void
    766 plist(struct varent *p)
    767 {
    768 	struct varent *c;
    769 	int len;
    770 
    771 #ifdef TRACE
    772 	tprintf("TRACE- plist()\n");
    773 #endif
    774 	if (setintr)
    775 		(void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT));
    776 	for (;;) {
    777 		while (p->v_left)
    778 			p = p->v_left;
    779 	x:
    780 		if (p->v_parent == 0)		/* is it the header? */
    781 			return;
    782 		len = blklen(p->vec);
    783 		printf("%t", p->v_name);
    784 		Putchar('\t');
    785 		if (len != 1)
    786 			Putchar('(');
    787 		blkpr(p->vec);
    788 		if (len != 1)
    789 			Putchar(')');
    790 		Putchar('\n');
    791 		if (p->v_right) {
    792 			p = p->v_right;
    793 			continue;
    794 		}
    795 		do {
    796 			c = p;
    797 			p = p->v_parent;
    798 		} while (p->v_right == c);
    799 		goto x;
    800 	}
    801 }
    802