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