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