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