xref: /netbsd-src/bin/csh/set.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
1 /* $NetBSD: set.c,v 1.31 2013/01/23 16:39:03 christos Exp $ */
2 
3 /*-
4  * Copyright (c) 1980, 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)set.c	8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: set.c,v 1.31 2013/01/23 16:39:03 christos Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/types.h>
42 
43 #include <stdarg.h>
44 #include <stdlib.h>
45 
46 #ifndef SHORT_STRINGS
47 #include <string.h>
48 #endif /* SHORT_STRINGS */
49 
50 #include "csh.h"
51 #include "extern.h"
52 
53 static Char *getinx(Char *, int *);
54 static void asx(Char *, int, Char *);
55 static struct varent *getvx(Char *, int);
56 static Char *xset(Char *, Char ***);
57 static Char *operate(int, Char *, Char *);
58 static void putn1(int);
59 static struct varent *madrof(Char *, struct varent *);
60 static void unsetv1(struct varent *);
61 static void exportpath(Char **);
62 static void balance(struct varent *, int, int);
63 
64 /*
65  * C Shell
66  */
67 
68 void
69 /*ARGSUSED*/
70 doset(Char **v, struct command *t)
71 {
72     Char op, *p, **vecp, *vp;
73     int subscr = 0;	/* XXX: GCC */
74     int hadsub;
75 
76     v++;
77     p = *v++;
78     if (p == 0) {
79 	prvars();
80 	return;
81     }
82     do {
83 	hadsub = 0;
84 	vp = p;
85 	if (letter(*p))
86 	    for (; alnum(*p); p++)
87 		continue;
88 	if (vp == p || !letter(*vp))
89 	    stderror(ERR_NAME | ERR_VARBEGIN);
90 	if ((p - vp) > MAXVARLEN)
91 	    stderror(ERR_NAME | ERR_VARTOOLONG);
92 	if (*p == '[') {
93 	    hadsub++;
94 	    p = getinx(p, &subscr);
95 	}
96 	if ((op = *p) != '\0') {
97 	    *p++ = 0;
98 	    if (*p == 0 && *v && **v == '(')
99 		p = *v++;
100 	}
101 	else if (*v && eq(*v, STRequal)) {
102 	    op = '=', v++;
103 	    if (*v)
104 		p = *v++;
105 	}
106 	if (op && op != '=')
107 	    stderror(ERR_NAME | ERR_SYNTAX);
108 	if (eq(p, STRLparen)) {
109 	    Char **e = v;
110 
111 	    if (hadsub)
112 		stderror(ERR_NAME | ERR_SYNTAX);
113 	    for (;;) {
114 		if (!*e)
115 		    stderror(ERR_NAME | ERR_MISSING, ')');
116 		if (**e == ')')
117 		    break;
118 		e++;
119 	    }
120 	    p = *e;
121 	    *e = 0;
122 	    vecp = saveblk(v);
123 	    set1(vp, vecp, &shvhed);
124 	    *e = p;
125 	    v = e + 1;
126 	}
127 	else if (hadsub)
128 	    asx(vp, subscr, Strsave(p));
129 	else
130 	    set(vp, Strsave(p));
131 	if (eq(vp, STRpath)) {
132 	    struct varent *pt = adrof(STRpath);
133 	    if (pt == NULL)
134 		stderror(ERR_NAME | ERR_UNDVAR);
135 	    else {
136 		exportpath(pt->vec);
137 		dohash(NULL, NULL);
138 	    }
139 	}
140 	else if (eq(vp, STRhistchars)) {
141 	    Char *pn = value(STRhistchars);
142 
143 	    HIST = *pn++;
144 	    HISTSUB = *pn;
145 	}
146 	else if (eq(vp, STRuser)) {
147 	    Setenv(STRUSER, value(vp));
148 	    Setenv(STRLOGNAME, value(vp));
149 	}
150 	else if (eq(vp, STRwordchars)) {
151 	    word_chars = value(vp);
152 	}
153 	else if (eq(vp, STRterm))
154 	    Setenv(STRTERM, value(vp));
155 	else if (eq(vp, STRhome)) {
156 	    Char *cp;
157 
158 	    cp = Strsave(value(vp));	/* get the old value back */
159 
160 	    /*
161 	     * convert to canonical pathname (possibly resolving symlinks)
162 	     */
163 	    cp = dcanon(cp, cp);
164 
165 	    set(vp, Strsave(cp));	/* have to save the new val */
166 
167 	    /* and now mirror home with HOME */
168 	    Setenv(STRHOME, cp);
169 	    /* fix directory stack for new tilde home */
170 	    dtilde();
171 	    xfree((ptr_t)cp);
172 	}
173 #ifdef FILEC
174 	else if (eq(vp, STRfilec))
175 	    filec = 1;
176 #endif
177 #ifdef EDIT
178 	else if (eq(vp, STRedit)) {
179 	    HistEvent ev;
180 	    editing = 1;
181 	    el = el_init_fd(getprogname(), cshin, cshout, csherr,
182 		SHIN, SHOUT, SHERR);
183 	    el_set(el, EL_EDITOR, "emacs");
184 	    el_set(el, EL_PROMPT, printpromptstr);
185 	    hi = history_init();
186 	    history(hi, &ev, H_SETSIZE, getn(value(STRhistory)));
187 	    loadhist(Histlist.Hnext);
188 	    el_set(el, EL_HIST, history, hi);
189 	}
190 #endif
191     } while ((p = *v++) != NULL);
192 }
193 
194 static Char *
195 getinx(Char *cp, int *ip)
196 {
197     *ip = 0;
198     *cp++ = 0;
199     while (*cp && Isdigit(*cp))
200 	*ip = *ip * 10 + *cp++ - '0';
201     if (*cp++ != ']')
202 	stderror(ERR_NAME | ERR_SUBSCRIPT);
203     return (cp);
204 }
205 
206 static void
207 asx(Char *vp, int subscr, Char *p)
208 {
209     struct varent *v;
210 
211     v = getvx(vp, subscr);
212     xfree((ptr_t) v->vec[subscr - 1]);
213     v->vec[subscr - 1] = globone(p, G_APPEND);
214 }
215 
216 static struct varent *
217 getvx(Char *vp, int subscr)
218 {
219     struct varent *v;
220 
221     v = adrof(vp);
222     if (v == 0)
223 	udvar(vp);
224     if (subscr < 1 || subscr > blklen(v->vec))
225 	stderror(ERR_NAME | ERR_RANGE);
226     return (v);
227 }
228 
229 void
230 /*ARGSUSED*/
231 dolet(Char **v, struct command *t)
232 {
233     Char c, op, *p, *vp;
234     int subscr = 0;	/* XXX: GCC */
235     int hadsub;
236 
237     v++;
238     p = *v++;
239     if (p == 0) {
240 	prvars();
241 	return;
242     }
243     do {
244 	hadsub = 0;
245 	vp = p;
246 	if (letter(*p))
247 	    for (; alnum(*p); p++)
248 		continue;
249 	if (vp == p || !letter(*vp))
250 	    stderror(ERR_NAME | ERR_VARBEGIN);
251 	if ((p - vp) > MAXVARLEN)
252 	    stderror(ERR_NAME | ERR_VARTOOLONG);
253 	if (*p == '[') {
254 	    hadsub++;
255 	    p = getinx(p, &subscr);
256 	}
257 	if (*p == 0 && *v)
258 	    p = *v++;
259 	if ((op = *p) != '\0')
260 	    *p++ = 0;
261 	else
262 	    stderror(ERR_NAME | ERR_ASSIGN);
263 
264 	if (*p == '\0' && *v == NULL)
265 	    stderror(ERR_NAME | ERR_ASSIGN);
266 
267 	vp = Strsave(vp);
268 	if (op == '=') {
269 	    c = '=';
270 	    p = xset(p, &v);
271 	}
272 	else {
273 	    c = *p++;
274 	    if (any("+-", c)) {
275 		if (c != op || *p)
276 		    stderror(ERR_NAME | ERR_UNKNOWNOP);
277 		p = Strsave(STR1);
278 	    }
279 	    else {
280 		if (any("<>", op)) {
281 		    if (c != op)
282 			stderror(ERR_NAME | ERR_UNKNOWNOP);
283 		    c = *p++;
284 		    stderror(ERR_NAME | ERR_SYNTAX);
285 		}
286 		if (c != '=')
287 		    stderror(ERR_NAME | ERR_UNKNOWNOP);
288 		p = xset(p, &v);
289 	    }
290 	}
291 	if (op == '=') {
292 	    if (hadsub)
293 		asx(vp, subscr, p);
294 	    else
295 		set(vp, p);
296 	} else if (hadsub) {
297 	    struct varent *gv = getvx(vp, subscr);
298 
299 	    asx(vp, subscr, operate(op, gv->vec[subscr - 1], p));
300 	}
301 	else
302 	    set(vp, operate(op, value(vp), p));
303 	if (eq(vp, STRpath)) {
304 	    struct varent *pt = adrof(STRpath);
305 	    if (pt == NULL)
306 		stderror(ERR_NAME | ERR_UNDVAR);
307 	    else {
308 		exportpath(pt->vec);
309 		dohash(NULL, NULL);
310 	    }
311 	}
312 	xfree((ptr_t) vp);
313 	if (c != '=')
314 	    xfree((ptr_t) p);
315     } while ((p = *v++) != NULL);
316 }
317 
318 static Char *
319 xset(Char *cp, Char ***vp)
320 {
321     Char *dp;
322 
323     if (*cp) {
324 	dp = Strsave(cp);
325 	--(*vp);
326 	xfree((ptr_t) ** vp);
327 	**vp = dp;
328     }
329     return (putn(expr(vp)));
330 }
331 
332 static Char *
333 operate(int op, Char *vp, Char *p)
334 {
335     Char opr[2], **v, *vec[5], **vecp;
336     int i;
337 
338     v = vec;
339     vecp = v;
340     if (op != '=') {
341 	if (*vp)
342 	    *v++ = vp;
343 	opr[0] = op;
344 	opr[1] = 0;
345 	*v++ = opr;
346 	if (op == '<' || op == '>')
347 	    *v++ = opr;
348     }
349     *v++ = p;
350     *v++ = 0;
351     i = expr(&vecp);
352     if (*vecp)
353 	stderror(ERR_NAME | ERR_EXPRESSION);
354     return (putn(i));
355 }
356 
357 static Char *putp;
358 
359 Char *
360 putn(int n)
361 {
362     static Char numbers[15];
363 
364     putp = numbers;
365     if (n < 0) {
366 	n = -n;
367 	*putp++ = '-';
368     }
369     if ((unsigned int)n == 0x80000000U) {
370 	*putp++ = '2';
371 	n = 147483648;
372     }
373     putn1(n);
374     *putp = 0;
375     return (Strsave(numbers));
376 }
377 
378 static void
379 putn1(int n)
380 {
381     if (n > 9)
382 	putn1(n / 10);
383     *putp++ = n % 10 + '0';
384 }
385 
386 int
387 getn(Char *cp)
388 {
389     int n, sign;
390 
391     sign = 0;
392     if (cp[0] == '+' && cp[1])
393 	cp++;
394     if (*cp == '-') {
395 	sign++;
396 	cp++;
397 	if (!Isdigit(*cp))
398 	    stderror(ERR_NAME | ERR_BADNUM);
399     }
400     n = 0;
401     while (Isdigit(*cp))
402 	n = n * 10 + *cp++ - '0';
403     if (*cp)
404 	stderror(ERR_NAME | ERR_BADNUM);
405     return (sign ? -n : n);
406 }
407 
408 Char *
409 value1(Char *var, struct varent *head)
410 {
411     struct varent *vp;
412 
413     vp = adrof1(var, head);
414     return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]);
415 }
416 
417 static struct varent *
418 madrof(Char *pat, struct varent *vp)
419 {
420     struct varent *vp1;
421 
422     for (; vp; vp = vp->v_right) {
423 	if (vp->v_left && (vp1 = madrof(pat, vp->v_left)))
424 	    return vp1;
425 	if (Gmatch(vp->v_name, pat))
426 	    return vp;
427     }
428     return vp;
429 }
430 
431 struct varent *
432 adrof1(Char *name, struct varent *v)
433 {
434     int cmp;
435 
436     v = v->v_left;
437     while (v && ((cmp = *name - *v->v_name) ||
438 		 (cmp = Strcmp(name, v->v_name))))
439 	if (cmp < 0)
440 	    v = v->v_left;
441 	else
442 	    v = v->v_right;
443     return v;
444 }
445 
446 /*
447  * The caller is responsible for putting value in a safe place
448  */
449 void
450 set(Char *var, Char *val)
451 {
452     Char **vec;
453 
454     vec = (Char **)xmalloc((size_t)(2 * sizeof(Char **)));
455     vec[0] = val;
456     vec[1] = 0;
457     set1(var, vec, &shvhed);
458 }
459 
460 void
461 set1(Char *var, Char **vec, struct varent *head)
462 {
463     Char **oldv;
464 
465     oldv = vec;
466     gflag = 0;
467     tglob(oldv);
468     if (gflag) {
469 	vec = globall(oldv);
470 	if (vec == 0) {
471 	    blkfree(oldv);
472 	    stderror(ERR_NAME | ERR_NOMATCH);
473 	}
474 	blkfree(oldv);
475 	gargv = 0;
476     }
477     setq(var, vec, head);
478 }
479 
480 void
481 setq(Char *name, Char **vec, struct varent *p)
482 {
483     struct varent *c;
484     int f;
485 
486     f = 0;			/* tree hangs off the header's left link */
487     while ((c = p->v_link[f]) != NULL) {
488 	if ((f = *name - *c->v_name) == 0 &&
489 	    (f = Strcmp(name, c->v_name)) == 0) {
490 	    blkfree(c->vec);
491 	    goto found;
492 	}
493 	p = c;
494 	f = f > 0;
495     }
496     p->v_link[f] = c = (struct varent *)xmalloc((size_t)sizeof(struct varent));
497     c->v_name = Strsave(name);
498     c->v_bal = 0;
499     c->v_left = c->v_right = 0;
500     c->v_parent = p;
501     balance(p, f, 0);
502 found:
503     trim(c->vec = vec);
504 }
505 
506 void
507 /*ARGSUSED*/
508 unset(Char **v, struct command *t)
509 {
510     unset1(v, &shvhed);
511     if (adrof(STRhistchars) == 0) {
512 	HIST = '!';
513 	HISTSUB = '^';
514     }
515     else if (adrof(STRwordchars) == 0)
516 	word_chars = STR_WORD_CHARS;
517 #ifdef FILEC
518     else if (adrof(STRfilec) == 0)
519 	filec = 0;
520 #endif
521 #ifdef EDIT
522     else if (adrof(STRedit) == 0) {
523 	el_end(el);
524 	history_end(hi);
525 	el = NULL;
526 	hi = NULL;
527 	editing = 0;
528     }
529 #endif
530 }
531 
532 void
533 unset1(Char *v[], struct varent *head)
534 {
535     struct varent *vp;
536     int cnt;
537 
538     while (*++v) {
539 	cnt = 0;
540 	while ((vp = madrof(*v, head->v_left)) != NULL)
541 	    unsetv1(vp), cnt++;
542 	if (cnt == 0)
543 	    setname(vis_str(*v));
544     }
545 }
546 
547 void
548 unsetv(Char *var)
549 {
550     struct varent *vp;
551 
552     if ((vp = adrof1(var, &shvhed)) == 0)
553 	udvar(var);
554     unsetv1(vp);
555 }
556 
557 static void
558 unsetv1(struct varent *p)
559 {
560     struct varent *c, *pp;
561     int f;
562 
563     /*
564      * Free associated memory first to avoid complications.
565      */
566     blkfree(p->vec);
567     xfree((ptr_t) p->v_name);
568     /*
569      * If p is missing one child, then we can move the other into where p is.
570      * Otherwise, we find the predecessor of p, which is guaranteed to have no
571      * right child, copy it into p, and move its 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 	    continue;
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) != NULL)
591 	c->v_parent = pp;
592     /*
593      * Free the deleted node, and rebalance.
594      */
595     xfree((ptr_t) p);
596     balance(pp, f, 1);
597 }
598 
599 void
600 setNS(Char *cp)
601 {
602     set(cp, Strsave(STRNULL));
603 }
604 
605 void
606 /*ARGSUSED*/
607 shift(Char **v, struct command *t)
608 {
609     struct varent *argv;
610     Char *name;
611 
612     v++;
613     name = *v;
614     if (name == 0)
615 	name = STRargv;
616     else
617 	(void) strip(name);
618     argv = adrof(name);
619     if (argv == 0)
620 	udvar(name);
621     if (argv->vec[0] == 0)
622 	stderror(ERR_NAME | ERR_NOMORE);
623     lshift(argv->vec, 1);
624 }
625 
626 static void
627 exportpath(Char **val)
628 {
629     Char exppath[BUFSIZE];
630 
631     exppath[0] = 0;
632     if (val)
633 	while (*val) {
634 	    if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZE) {
635 		(void)fprintf(csherr,
636 			       "Warning: ridiculously long PATH truncated\n");
637 		break;
638 	    }
639 	    (void)Strcat(exppath, *val++);
640 	    if (*val == 0 || eq(*val, STRRparen))
641 		break;
642 	    (void)Strcat(exppath, STRcolon);
643 	}
644     Setenv(STRPATH, exppath);
645 }
646 
647 #ifndef lint
648  /*
649   * Lint thinks these have null effect
650   */
651  /* macros to do single rotations on node p */
652 #define rright(p) (\
653 	t = (p)->v_left,\
654 	(t)->v_parent = (p)->v_parent,\
655 	((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\
656 	(t->v_right = (p))->v_parent = t,\
657 	(p) = t)
658 #define rleft(p) (\
659 	t = (p)->v_right,\
660 	(t)->v_parent = (p)->v_parent,\
661 	((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\
662 	(t->v_left = (p))->v_parent = t,\
663 	(p) = t)
664 #else
665 struct varent *
666 rleft(struct varent *p)
667 {
668     return (p);
669 }
670 struct varent *
671 rright(struct varent *p)
672 {
673     return (p);
674 }
675 #endif				/* ! lint */
676 
677 
678 /*
679  * Rebalance a tree, starting at p and up.
680  * F == 0 means we've come from p's left child.
681  * D == 1 means we've just done a delete, otherwise an insert.
682  */
683 static void
684 balance(struct varent *p, int f, int d)
685 {
686     struct varent *pp;
687 
688 #ifndef lint
689     struct varent *t;	/* used by the rotate macros */
690 
691 #endif
692     int ff;
693 
694     /*
695      * Ok, from here on, p is the node we're operating on; pp is its parent; f
696      * is the branch of p from which we have come; ff is the branch of pp which
697      * is p.
698      */
699     for (; (pp = p->v_parent) != NULL; p = pp, f = ff) {
700 	ff = pp->v_right == p;
701 	if (f ^ d) {		/* right heavy */
702 	    switch (p->v_bal) {
703 	    case -1:		/* was left heavy */
704 		p->v_bal = 0;
705 		break;
706 	    case 0:		/* was balanced */
707 		p->v_bal = 1;
708 		break;
709 	    case 1:		/* was already right heavy */
710 		switch (p->v_right->v_bal) {
711 		case 1:	/* single rotate */
712 		    pp->v_link[ff] = rleft(p);
713 		    p->v_left->v_bal = 0;
714 		    p->v_bal = 0;
715 		    break;
716 		case 0:	/* single rotate */
717 		    pp->v_link[ff] = rleft(p);
718 		    p->v_left->v_bal = 1;
719 		    p->v_bal = -1;
720 		    break;
721 		case -1:	/* double rotate */
722 		    (void) rright(p->v_right);
723 		    pp->v_link[ff] = rleft(p);
724 		    p->v_left->v_bal =
725 			p->v_bal < 1 ? 0 : -1;
726 		    p->v_right->v_bal =
727 			p->v_bal > -1 ? 0 : 1;
728 		    p->v_bal = 0;
729 		    break;
730 		}
731 		break;
732 	    }
733 	}
734 	else {			/* left heavy */
735 	    switch (p->v_bal) {
736 	    case 1:		/* was right heavy */
737 		p->v_bal = 0;
738 		break;
739 	    case 0:		/* was balanced */
740 		p->v_bal = -1;
741 		break;
742 	    case -1:		/* was already left heavy */
743 		switch (p->v_left->v_bal) {
744 		case -1:	/* single rotate */
745 		    pp->v_link[ff] = rright(p);
746 		    p->v_right->v_bal = 0;
747 		    p->v_bal = 0;
748 		    break;
749 		case 0:	/* single rotate */
750 		    pp->v_link[ff] = rright(p);
751 		    p->v_right->v_bal = -1;
752 		    p->v_bal = 1;
753 		    break;
754 		case 1:	/* double rotate */
755 		    (void) rleft(p->v_left);
756 		    pp->v_link[ff] = rright(p);
757 		    p->v_left->v_bal =
758 			p->v_bal < 1 ? 0 : -1;
759 		    p->v_right->v_bal =
760 			p->v_bal > -1 ? 0 : 1;
761 		    p->v_bal = 0;
762 		    break;
763 		}
764 		break;
765 	    }
766 	}
767 	/*
768 	 * If from insert, then we terminate when p is balanced. If from
769 	 * delete, then we terminate when p is unbalanced.
770 	 */
771 	if ((p->v_bal == 0) ^ d)
772 	    break;
773     }
774 }
775 
776 void
777 plist(struct varent *p)
778 {
779     struct varent *c;
780     sigset_t nsigset;
781     int len;
782 
783     if (setintr) {
784 	sigemptyset(&nsigset);
785 	(void)sigaddset(&nsigset, SIGINT);
786 	(void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
787     }
788 
789     for (;;) {
790 	while (p->v_left)
791 	    p = p->v_left;
792 x:
793 	if (p->v_parent == 0)	/* is it the header? */
794 	    return;
795 	len = blklen(p->vec);
796 	(void)fprintf(cshout, "%s\t", short2str(p->v_name));
797 	if (len != 1)
798 	    (void)fputc('(', cshout);
799 	blkpr(cshout, p->vec);
800 	if (len != 1)
801 	    (void)fputc(')', cshout);
802 	(void)fputc('\n', cshout);
803 	if (p->v_right) {
804 	    p = p->v_right;
805 	    continue;
806 	}
807 	do {
808 	    c = p;
809 	    p = p->v_parent;
810 	} while (p->v_right == c);
811 	goto x;
812     }
813 }
814