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