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