1 /* $OpenBSD: set.c,v 1.20 2018/09/17 16:00:19 martijn Exp $ */ 2 /* $NetBSD: set.c,v 1.8 1995/03/21 18:35:52 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <stdlib.h> 35 #include <stdarg.h> 36 #include <string.h> 37 38 #include "csh.h" 39 #include "extern.h" 40 41 static Char *getinx(Char *, int *); 42 static void asx(Char *, int, Char *); 43 static struct varent 44 *getvx(Char *, int); 45 static Char *xset(Char *, Char ***); 46 static Char *operate(int, Char *, Char *); 47 static struct varent 48 *madrof(Char *, struct varent *); 49 static void unsetv1(struct varent *); 50 static void exportpath(Char **); 51 static void balance(struct varent *, int, int); 52 53 54 /* 55 * C Shell 56 */ 57 58 void 59 /*ARGSUSED*/ 60 doset(Char **v, struct command *t) 61 { 62 Char *p; 63 Char *vp, op; 64 Char **vecp; 65 bool hadsub; 66 int subscr; 67 68 v++; 69 p = *v++; 70 if (p == 0) { 71 prvars(); 72 return; 73 } 74 do { 75 hadsub = 0; 76 vp = p; 77 if (letter(*p)) 78 for (; alnum(*p); p++) 79 continue; 80 if (vp == p || !letter(*vp)) 81 stderror(ERR_NAME | ERR_VARBEGIN); 82 if ((p - vp) > MAXVARLEN) { 83 stderror(ERR_NAME | ERR_VARTOOLONG); 84 return; 85 } 86 if (*p == '[') { 87 hadsub++; 88 p = getinx(p, &subscr); 89 } 90 if ((op = *p) != '\0') { 91 *p++ = 0; 92 if (*p == 0 && *v && **v == '(') 93 p = *v++; 94 } 95 else if (*v && eq(*v, STRequal)) { 96 op = '=', v++; 97 if (*v) 98 p = *v++; 99 } 100 if (op && op != '=') 101 stderror(ERR_NAME | ERR_SYNTAX); 102 if (eq(p, STRLparen)) { 103 Char **e = v; 104 105 if (hadsub) 106 stderror(ERR_NAME | ERR_SYNTAX); 107 for (;;) { 108 if (!*e) 109 stderror(ERR_NAME | ERR_MISSING, ')'); 110 if (**e == ')') 111 break; 112 e++; 113 } 114 p = *e; 115 *e = 0; 116 vecp = saveblk(v); 117 set1(vp, vecp, &shvhed); 118 *e = p; 119 v = e + 1; 120 } 121 else if (hadsub) 122 asx(vp, subscr, Strsave(p)); 123 else 124 set(vp, Strsave(p)); 125 if (eq(vp, STRpath)) { 126 exportpath(adrof(STRpath)->vec); 127 dohash(NULL, NULL); 128 } 129 else if (eq(vp, STRhistchars)) { 130 Char *pn = value(STRhistchars); 131 132 HIST = *pn++; 133 HISTSUB = *pn; 134 } 135 else if (eq(vp, STRuser)) { 136 Setenv(STRUSER, value(vp)); 137 Setenv(STRLOGNAME, value(vp)); 138 } 139 else if (eq(vp, STRwordchars)) { 140 word_chars = value(vp); 141 } 142 else if (eq(vp, STRterm)) 143 Setenv(STRTERM, value(vp)); 144 else if (eq(vp, STRhome)) { 145 Char *cp; 146 147 cp = Strsave(value(vp)); /* get the old value back */ 148 149 /* 150 * convert to canonical pathname (possibly resolving symlinks) 151 */ 152 cp = dcanon(cp, cp); 153 154 set(vp, Strsave(cp)); /* have to save the new val */ 155 156 /* and now mirror home with HOME */ 157 Setenv(STRHOME, cp); 158 /* fix directory stack for new tilde home */ 159 dtilde(); 160 free(cp); 161 } 162 else if (eq(vp, STRfilec)) 163 filec = 1; 164 } while ((p = *v++) != NULL); 165 } 166 167 static Char * 168 getinx(Char *cp, int *ip) 169 { 170 171 *ip = 0; 172 *cp++ = 0; 173 while (*cp && Isdigit(*cp)) 174 *ip = *ip * 10 + *cp++ - '0'; 175 if (*cp++ != ']') 176 stderror(ERR_NAME | ERR_SUBSCRIPT); 177 return (cp); 178 } 179 180 static void 181 asx(Char *vp, int subscr, Char *p) 182 { 183 struct varent *v = getvx(vp, subscr); 184 185 free(v->vec[subscr - 1]); 186 v->vec[subscr - 1] = globone(p, G_APPEND); 187 } 188 189 static struct varent * 190 getvx(Char *vp, int subscr) 191 { 192 struct varent *v = adrof(vp); 193 194 if (v == 0) 195 udvar(vp); 196 if (subscr < 1 || subscr > blklen(v->vec)) 197 stderror(ERR_NAME | ERR_RANGE); 198 return (v); 199 } 200 201 void 202 /*ARGSUSED*/ 203 dolet(Char **v, struct command *t) 204 { 205 Char *p; 206 Char *vp, c, op; 207 bool hadsub; 208 int subscr; 209 210 v++; 211 p = *v++; 212 if (p == 0) { 213 prvars(); 214 return; 215 } 216 do { 217 hadsub = 0; 218 vp = p; 219 if (letter(*p)) 220 for (; alnum(*p); p++) 221 continue; 222 if (vp == p || !letter(*vp)) 223 stderror(ERR_NAME | ERR_VARBEGIN); 224 if ((p - vp) > MAXVARLEN) 225 stderror(ERR_NAME | ERR_VARTOOLONG); 226 if (*p == '[') { 227 hadsub++; 228 p = getinx(p, &subscr); 229 } 230 if (*p == 0 && *v) 231 p = *v++; 232 if ((op = *p) != '\0') 233 *p++ = 0; 234 else 235 stderror(ERR_NAME | ERR_ASSIGN); 236 237 if (*p == '\0' && *v == NULL) 238 stderror(ERR_NAME | ERR_ASSIGN); 239 240 vp = Strsave(vp); 241 if (op == '=') { 242 c = '='; 243 p = xset(p, &v); 244 } 245 else { 246 c = *p++; 247 if (strchr("+-", c)) { 248 if (c != op || *p) 249 stderror(ERR_NAME | ERR_UNKNOWNOP); 250 p = Strsave(STR1); 251 } 252 else { 253 if (strchr("<>", op)) { 254 if (c != op) 255 stderror(ERR_NAME | ERR_UNKNOWNOP); 256 c = *p++; 257 stderror(ERR_NAME | ERR_SYNTAX); 258 } 259 if (c != '=') 260 stderror(ERR_NAME | ERR_UNKNOWNOP); 261 p = xset(p, &v); 262 } 263 } 264 if (op == '=') 265 if (hadsub) 266 asx(vp, subscr, p); 267 else 268 set(vp, p); 269 else if (hadsub) { 270 struct varent *gv = getvx(vp, subscr); 271 272 asx(vp, subscr, operate(op, gv->vec[subscr - 1], p)); 273 } 274 else 275 set(vp, operate(op, value(vp), p)); 276 if (eq(vp, STRpath)) { 277 exportpath(adrof(STRpath)->vec); 278 dohash(NULL, NULL); 279 } 280 free(vp); 281 if (c != '=') 282 free(p); 283 } while ((p = *v++) != NULL); 284 } 285 286 static Char * 287 xset(Char *cp, Char ***vp) 288 { 289 Char *dp; 290 291 if (*cp) { 292 dp = Strsave(cp); 293 --(*vp); 294 free(** vp); 295 **vp = dp; 296 } 297 return (putn(expr(vp))); 298 } 299 300 static Char * 301 operate(int op, Char *vp, Char *p) 302 { 303 Char opr[2]; 304 Char *vec[5]; 305 Char **v = vec; 306 Char **vecp = v; 307 int i; 308 309 if (op != '=') { 310 if (*vp) 311 *v++ = vp; 312 opr[0] = op; 313 opr[1] = 0; 314 *v++ = opr; 315 if (op == '<' || op == '>') 316 *v++ = opr; 317 } 318 *v++ = p; 319 *v++ = 0; 320 i = expr(&vecp); 321 if (*vecp) 322 stderror(ERR_NAME | ERR_EXPRESSION); 323 return (putn(i)); 324 } 325 326 Char * 327 putn(int n) 328 { 329 char number[15]; 330 int i; 331 332 i = snprintf(number, sizeof(number), "%d", n); 333 if (i == -1 || i >= sizeof(number)) 334 return (STRNULL); 335 return (SAVE(number)); 336 } 337 338 int 339 getn(Char *cp) 340 { 341 int n; 342 int sign; 343 344 sign = 0; 345 if (cp[0] == '+' && cp[1]) 346 cp++; 347 if (*cp == '-') { 348 sign++; 349 cp++; 350 if (!Isdigit(*cp)) 351 stderror(ERR_NAME | ERR_BADNUM); 352 } 353 n = 0; 354 while (Isdigit(*cp)) 355 n = n * 10 + *cp++ - '0'; 356 if (*cp) 357 stderror(ERR_NAME | ERR_BADNUM); 358 return (sign ? -n : n); 359 } 360 361 Char * 362 value1(Char *var, struct varent *head) 363 { 364 struct varent *vp; 365 366 vp = adrof1(var, head); 367 return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]); 368 } 369 370 static struct varent * 371 madrof(Char *pat, struct varent *vp) 372 { 373 struct varent *vp1; 374 375 for (; vp; vp = vp->v_right) { 376 if (vp->v_left && (vp1 = madrof(pat, vp->v_left))) 377 return vp1; 378 if (Gmatch(vp->v_name, pat)) 379 return vp; 380 } 381 return vp; 382 } 383 384 struct varent * 385 adrof1(Char *name, struct varent *v) 386 { 387 int cmp; 388 389 v = v->v_left; 390 while (v && ((cmp = *name - *v->v_name) || 391 (cmp = Strcmp(name, v->v_name)))) 392 if (cmp < 0) 393 v = v->v_left; 394 else 395 v = v->v_right; 396 return v; 397 } 398 399 /* 400 * The caller is responsible for putting value in a safe place 401 */ 402 void 403 set(Char *var, Char *val) 404 { 405 Char **vec = xreallocarray(NULL, 2, sizeof(Char **)); 406 407 vec[0] = val; 408 vec[1] = 0; 409 set1(var, vec, &shvhed); 410 } 411 412 void 413 set1(Char *var, Char **vec, struct varent *head) 414 { 415 Char **oldv = vec; 416 417 gflag = 0; 418 tglob(oldv); 419 if (gflag) { 420 vec = globall(oldv); 421 if (vec == 0) { 422 blkfree(oldv); 423 stderror(ERR_NAME | ERR_NOMATCH); 424 return; 425 } 426 blkfree(oldv); 427 gargv = 0; 428 } 429 setq(var, vec, head); 430 } 431 432 433 void 434 setq(Char *name, Char **vec, struct varent *p) 435 { 436 struct varent *c; 437 int f; 438 439 f = 0; /* tree hangs off the header's left link */ 440 while ((c = p->v_link[f]) != NULL) { 441 if ((f = *name - *c->v_name) == 0 && 442 (f = Strcmp(name, c->v_name)) == 0) { 443 blkfree(c->vec); 444 goto found; 445 } 446 p = c; 447 f = f > 0; 448 } 449 p->v_link[f] = c = (struct varent *) xmalloc((size_t) sizeof(struct varent)); 450 c->v_name = Strsave(name); 451 c->v_bal = 0; 452 c->v_left = c->v_right = 0; 453 c->v_parent = p; 454 balance(p, f, 0); 455 found: 456 trim(c->vec = vec); 457 } 458 459 void 460 /*ARGSUSED*/ 461 unset(Char **v, struct command *t) 462 { 463 unset1(v, &shvhed); 464 if (adrof(STRfilec) == 0) 465 filec = 0; 466 if (adrof(STRhistchars) == 0) { 467 HIST = '!'; 468 HISTSUB = '^'; 469 } 470 if (adrof(STRwordchars) == 0) 471 word_chars = STR_WORD_CHARS; 472 } 473 474 void 475 unset1(Char *v[], struct varent *head) 476 { 477 struct varent *vp; 478 int cnt; 479 480 while (*++v) { 481 cnt = 0; 482 while ((vp = madrof(*v, head->v_left)) != NULL) 483 unsetv1(vp), cnt++; 484 if (cnt == 0) 485 setname(vis_str(*v)); 486 } 487 } 488 489 void 490 unsetv(Char *var) 491 { 492 struct varent *vp; 493 494 if ((vp = adrof1(var, &shvhed)) == 0) 495 udvar(var); 496 unsetv1(vp); 497 } 498 499 static void 500 unsetv1(struct varent *p) 501 { 502 struct varent *c, *pp; 503 int f; 504 505 /* 506 * Free associated memory first to avoid complications. 507 */ 508 blkfree(p->vec); 509 free(p->v_name); 510 /* 511 * If p is missing one child, then we can move the other into where p is. 512 * Otherwise, we find the predecessor of p, which is guaranteed to have no 513 * right child, copy it into p, and move it's left child into it. 514 */ 515 if (p->v_right == 0) 516 c = p->v_left; 517 else if (p->v_left == 0) 518 c = p->v_right; 519 else { 520 for (c = p->v_left; c->v_right; c = c->v_right) 521 continue; 522 p->v_name = c->v_name; 523 p->vec = c->vec; 524 p = c; 525 c = p->v_left; 526 } 527 /* 528 * Move c into where p is. 529 */ 530 pp = p->v_parent; 531 f = pp->v_right == p; 532 if ((pp->v_link[f] = c) != NULL) 533 c->v_parent = pp; 534 /* 535 * Free the deleted node, and rebalance. 536 */ 537 free(p); 538 balance(pp, f, 1); 539 } 540 541 void 542 setNS(Char *cp) 543 { 544 set(cp, Strsave(STRNULL)); 545 } 546 547 void 548 /*ARGSUSED*/ 549 shift(Char **v, struct command *t) 550 { 551 struct varent *argv; 552 Char *name; 553 554 v++; 555 name = *v; 556 if (name == 0) 557 name = STRargv; 558 else 559 (void) strip(name); 560 argv = adrof(name); 561 if (argv == 0) 562 udvar(name); 563 if (argv->vec[0] == 0) 564 stderror(ERR_NAME | ERR_NOMORE); 565 lshift(argv->vec, 1); 566 } 567 568 static void 569 exportpath(Char **val) 570 { 571 Char exppath[BUFSIZ]; 572 573 exppath[0] = 0; 574 if (val) 575 while (*val) { 576 if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZ) { 577 (void) fprintf(csherr, 578 "Warning: ridiculously long PATH truncated\n"); 579 break; 580 } 581 (void) Strlcat(exppath, *val++, sizeof exppath/sizeof(Char)); 582 if (*val == 0 || eq(*val, STRRparen)) 583 break; 584 (void) Strlcat(exppath, STRcolon, sizeof exppath/sizeof(Char)); 585 } 586 Setenv(STRPATH, exppath); 587 } 588 589 /* macros to do single rotations on node p */ 590 #define rright(p) (\ 591 t = (p)->v_left,\ 592 (t)->v_parent = (p)->v_parent,\ 593 ((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\ 594 (t->v_right = (p))->v_parent = t,\ 595 (p) = t) 596 #define rleft(p) (\ 597 t = (p)->v_right,\ 598 (t)->v_parent = (p)->v_parent,\ 599 ((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\ 600 (t->v_left = (p))->v_parent = t,\ 601 (p) = t) 602 603 /* 604 * Rebalance a tree, starting at p and up. 605 * F == 0 means we've come from p's left child. 606 * D == 1 means we've just done a delete, otherwise an insert. 607 */ 608 static void 609 balance(struct varent *p, int f, int d) 610 { 611 struct varent *pp; 612 struct varent *t; /* used by the rotate macros */ 613 int ff; 614 615 /* 616 * Ok, from here on, p is the node we're operating on; pp is it's parent; f 617 * is the branch of p from which we have come; ff is the branch of pp which 618 * is p. 619 */ 620 for (; (pp = p->v_parent) != NULL; p = pp, f = ff) { 621 ff = pp->v_right == p; 622 if (f ^ d) { /* right heavy */ 623 switch (p->v_bal) { 624 case -1: /* was left heavy */ 625 p->v_bal = 0; 626 break; 627 case 0: /* was balanced */ 628 p->v_bal = 1; 629 break; 630 case 1: /* was already right heavy */ 631 switch (p->v_right->v_bal) { 632 case 1: /* single rotate */ 633 pp->v_link[ff] = rleft(p); 634 p->v_left->v_bal = 0; 635 p->v_bal = 0; 636 break; 637 case 0: /* single rotate */ 638 pp->v_link[ff] = rleft(p); 639 p->v_left->v_bal = 1; 640 p->v_bal = -1; 641 break; 642 case -1: /* double rotate */ 643 (void) rright(p->v_right); 644 pp->v_link[ff] = rleft(p); 645 p->v_left->v_bal = 646 p->v_bal < 1 ? 0 : -1; 647 p->v_right->v_bal = 648 p->v_bal > -1 ? 0 : 1; 649 p->v_bal = 0; 650 break; 651 } 652 break; 653 } 654 } 655 else { /* left heavy */ 656 switch (p->v_bal) { 657 case 1: /* was right heavy */ 658 p->v_bal = 0; 659 break; 660 case 0: /* was balanced */ 661 p->v_bal = -1; 662 break; 663 case -1: /* was already left heavy */ 664 switch (p->v_left->v_bal) { 665 case -1: /* single rotate */ 666 pp->v_link[ff] = rright(p); 667 p->v_right->v_bal = 0; 668 p->v_bal = 0; 669 break; 670 case 0: /* single rotate */ 671 pp->v_link[ff] = rright(p); 672 p->v_right->v_bal = -1; 673 p->v_bal = 1; 674 break; 675 case 1: /* double rotate */ 676 (void) rleft(p->v_left); 677 pp->v_link[ff] = rright(p); 678 p->v_left->v_bal = 679 p->v_bal < 1 ? 0 : -1; 680 p->v_right->v_bal = 681 p->v_bal > -1 ? 0 : 1; 682 p->v_bal = 0; 683 break; 684 } 685 break; 686 } 687 } 688 /* 689 * If from insert, then we terminate when p is balanced. If from 690 * delete, then we terminate when p is unbalanced. 691 */ 692 if ((p->v_bal == 0) ^ d) 693 break; 694 } 695 } 696 697 void 698 plist(struct varent *p) 699 { 700 struct varent *c; 701 int len; 702 sigset_t sigset; 703 704 if (setintr) { 705 sigemptyset(&sigset); 706 sigaddset(&sigset, SIGINT); 707 sigprocmask(SIG_UNBLOCK, &sigset, NULL); 708 } 709 710 for (;;) { 711 while (p->v_left) 712 p = p->v_left; 713 x: 714 if (p->v_parent == 0) /* is it the header? */ 715 return; 716 len = blklen(p->vec); 717 (void) fprintf(cshout, "%s\t", short2str(p->v_name)); 718 if (len != 1) 719 (void) fputc('(', cshout); 720 blkpr(cshout, p->vec); 721 if (len != 1) 722 (void) fputc(')', cshout); 723 (void) fputc('\n', cshout); 724 if (p->v_right) { 725 p = p->v_right; 726 continue; 727 } 728 do { 729 c = p; 730 p = p->v_parent; 731 } while (p->v_right == c); 732 goto x; 733 } 734 } 735