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