1*ccb9728fSkre /* $NetBSD: var.c,v 1.88 2024/12/26 03:23:28 kre Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 437ed7877Sjtc * Copyright (c) 1991, 1993 537ed7877Sjtc * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * This code is derived from software contributed to Berkeley by 861f28255Scgd * Kenneth Almquist. 961f28255Scgd * 1061f28255Scgd * Redistribution and use in source and binary forms, with or without 1161f28255Scgd * modification, are permitted provided that the following conditions 1261f28255Scgd * are met: 1361f28255Scgd * 1. Redistributions of source code must retain the above copyright 1461f28255Scgd * notice, this list of conditions and the following disclaimer. 1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer in the 1761f28255Scgd * documentation and/or other materials provided with the distribution. 18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors 1961f28255Scgd * may be used to endorse or promote products derived from this software 2061f28255Scgd * without specific prior written permission. 2161f28255Scgd * 2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261f28255Scgd * SUCH DAMAGE. 3361f28255Scgd */ 3461f28255Scgd 35cd799663Schristos #include <sys/cdefs.h> 3661f28255Scgd #ifndef lint 3749f0ad86Scgd #if 0 3807bae7edSchristos static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; 3949f0ad86Scgd #else 40*ccb9728fSkre __RCSID("$NetBSD: var.c,v 1.88 2024/12/26 03:23:28 kre Exp $"); 4149f0ad86Scgd #endif 4261f28255Scgd #endif /* not lint */ 4361f28255Scgd 44ce6f66ceSkre #include <stdio.h> 4507bae7edSchristos #include <unistd.h> 4607bae7edSchristos #include <stdlib.h> 476814c65dSchristos #include <string.h> 48667e4d4cSfair #include <paths.h> 49a080d612Schristos #include <limits.h> 50dc833851Skre #include <time.h> 51dc833851Skre #include <pwd.h> 52dc833851Skre #include <fcntl.h> 53c6c29888Skre #include <inttypes.h> 5407bae7edSchristos 5561f28255Scgd /* 5661f28255Scgd * Shell variables. 5761f28255Scgd */ 5861f28255Scgd 5961f28255Scgd #include "shell.h" 6061f28255Scgd #include "output.h" 6161f28255Scgd #include "expand.h" 6261f28255Scgd #include "nodes.h" /* for other headers */ 6361f28255Scgd #include "eval.h" /* defines cmdenviron */ 6461f28255Scgd #include "exec.h" 6561f28255Scgd #include "syntax.h" 6661f28255Scgd #include "options.h" 674fc4fe2eSchristos #include "builtins.h" 6861f28255Scgd #include "mail.h" 6961f28255Scgd #include "var.h" 7061f28255Scgd #include "memalloc.h" 7161f28255Scgd #include "error.h" 7261f28255Scgd #include "mystring.h" 731a523dd5Schristos #include "parser.h" 74e314f958Sdsl #include "show.h" 75dc833851Skre #include "machdep.h" 767accaec4Schristos #ifndef SMALL 7707bae7edSchristos #include "myhistedit.h" 7807bae7edSchristos #endif 7961f28255Scgd 805f9b9101Sdsl #ifdef SMALL 8161f28255Scgd #define VTABSIZE 39 825f9b9101Sdsl #else 835f9b9101Sdsl #define VTABSIZE 517 845f9b9101Sdsl #endif 8561f28255Scgd 8661f28255Scgd 8761f28255Scgd struct varinit { 8861f28255Scgd struct var *var; 8961f28255Scgd int flags; 903d424690Schristos const char *text; 91727a69dcSkre union var_func_union v_u; 9261f28255Scgd }; 93727a69dcSkre #define func v_u.set_func 94727a69dcSkre #define rfunc v_u.ref_func 95727a69dcSkre 96727a69dcSkre char *get_lineno(struct var *); 9761f28255Scgd 98dc833851Skre #ifndef SMALL 99dc833851Skre char *get_tod(struct var *); 100dc833851Skre char *get_hostname(struct var *); 101dc833851Skre char *get_seconds(struct var *); 102dc833851Skre char *get_euser(struct var *); 103dc833851Skre char *get_random(struct var *); 104dc833851Skre #endif 105dc833851Skre 1060faa1734Sdholland struct localvar *localvars; 10761f28255Scgd 1087accaec4Schristos #ifndef SMALL 10937ed7877Sjtc struct var vhistsize; 110c591669fSkre struct var vhistfile; 111c591669fSkre struct var vhistappend; 1127efa5addSchristos struct var vterm; 1133c2922e7Skre struct var editrc; 1144754b1e8Skre struct var ps_lit; 115e3c63ad9Scgd #endif 11661f28255Scgd struct var vifs; 11761f28255Scgd struct var vmail; 11861f28255Scgd struct var vmpath; 11961f28255Scgd struct var vpath; 12061f28255Scgd struct var vps1; 12161f28255Scgd struct var vps2; 122e314f958Sdsl struct var vps4; 1239302f8efSchristos struct var vvers; 124beb57fb3Schristos struct var voptind; 125fd38bbe2Skre struct var line_num; 126dc833851Skre #ifndef SMALL 127dc833851Skre struct var tod; 128dc833851Skre struct var host_name; 129dc833851Skre struct var seconds; 130dc833851Skre struct var euname; 131dc833851Skre struct var random_num; 132dc833851Skre 133dc833851Skre intmax_t sh_start_time; 134dc833851Skre #endif 13561f28255Scgd 136727a69dcSkre struct var line_num; 137727a69dcSkre int line_number; 138727a69dcSkre int funclinebase = 0; 139727a69dcSkre int funclineabs = 0; 140727a69dcSkre 141ca12a0b8Schristos char ifs_default[] = " \t\n"; 142ca12a0b8Schristos 14361f28255Scgd const struct varinit varinit[] = { 1447accaec4Schristos #ifndef SMALL 145beb57fb3Schristos { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", 146727a69dcSkre { .set_func= sethistsize } }, 147c591669fSkre { &vhistfile, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE=", 148c591669fSkre { .set_func= sethistfile } }, 149c591669fSkre { &vhistappend, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTAPPEND=", 150c591669fSkre { .set_func= sethistappend } }, 151e3c63ad9Scgd #endif 152beb57fb3Schristos { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", 153727a69dcSkre { NULL } }, 154beb57fb3Schristos { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", 155727a69dcSkre { NULL } }, 156beb57fb3Schristos { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", 157727a69dcSkre { NULL } }, 1589302f8efSchristos { &vvers, VSTRFIXED|VTEXTFIXED|VNOEXPORT, "NETBSD_SHELL=", 159727a69dcSkre { NULL } }, 16084d782a5Scgd { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, 161727a69dcSkre { .set_func= changepath } }, 16261f28255Scgd /* 16361f28255Scgd * vps1 depends on uid 16461f28255Scgd */ 165beb57fb3Schristos { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", 166727a69dcSkre { NULL } }, 167e314f958Sdsl { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 168727a69dcSkre { NULL } }, 1697efa5addSchristos #ifndef SMALL 170beb57fb3Schristos { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", 171727a69dcSkre { .set_func= setterm } }, 1723c2922e7Skre { &editrc, VSTRFIXED|VTEXTFIXED|VUNSET, "EDITRC=", 1733c2922e7Skre { .set_func= set_editrc } }, 1744754b1e8Skre { &ps_lit, VSTRFIXED|VTEXTFIXED|VUNSET, "PSlit=", 1754754b1e8Skre { .set_func= set_prompt_lit } }, 17661f28255Scgd #endif 177e314f958Sdsl { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1", 178727a69dcSkre { .set_func= getoptsreset } }, 179370f00caSkre { &line_num, VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL, "LINENO=1", 180727a69dcSkre { .ref_func= get_lineno } }, 181dc833851Skre #ifndef SMALL 182dc833851Skre { &tod, VSTRFIXED|VTEXTFIXED|VFUNCREF, "ToD=", 183dc833851Skre { .ref_func= get_tod } }, 184dc833851Skre { &host_name, VSTRFIXED|VTEXTFIXED|VFUNCREF, "HOSTNAME=", 185dc833851Skre { .ref_func= get_hostname } }, 186dc833851Skre { &seconds, VSTRFIXED|VTEXTFIXED|VFUNCREF, "SECONDS=", 187dc833851Skre { .ref_func= get_seconds } }, 188dc833851Skre { &euname, VSTRFIXED|VTEXTFIXED|VFUNCREF, "EUSER=", 189dc833851Skre { .ref_func= get_euser } }, 190370f00caSkre { &random_num, VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL, "RANDOM=", 191dc833851Skre { .ref_func= get_random } }, 192dc833851Skre #endif 193beb57fb3Schristos { NULL, 0, NULL, 194727a69dcSkre { NULL } } 19561f28255Scgd }; 19661f28255Scgd 19761f28255Scgd struct var *vartab[VTABSIZE]; 19861f28255Scgd 1995f9b9101Sdsl STATIC int strequal(const char *, const char *); 2005f9b9101Sdsl STATIC struct var *find_var(const char *, struct var ***, int *); 201150d8297Skre STATIC void showvar(struct var *, const char *, const char *, int); 202150d8297Skre static void export_usage(const char *) __dead; 203727a664bSkre STATIC int makespecial(const char *); 20461f28255Scgd 20561f28255Scgd /* 206f1229b53Sandvar * Initialize the variable symbol tables and import the environment 20761f28255Scgd */ 20861f28255Scgd 20961f28255Scgd #ifdef mkinit 2101dd2c2fdSchristos INCLUDE <stdio.h> 2111dd2c2fdSchristos INCLUDE <unistd.h> 212dc833851Skre INCLUDE <time.h> 21361f28255Scgd INCLUDE "var.h" 2149302f8efSchristos INCLUDE "version.h" 21533809804Schristos MKINIT char **environ; 216d73cf48cSkre MKINIT void setvareqsafe(char *, int); 21761f28255Scgd INIT { 21861f28255Scgd char **envp; 2191dd2c2fdSchristos char buf[64]; 22061f28255Scgd 221dc833851Skre #ifndef SMALL 222dc833851Skre sh_start_time = (intmax_t)time((time_t *)0); 223dc833851Skre #endif 224dc833851Skre /* 225dc833851Skre * Set up our default variables and their values. 226dc833851Skre */ 22761f28255Scgd initvar(); 228dc833851Skre 229dc833851Skre /* 230dc833851Skre * Import variables from the environment, which will 231d73cf48cSkre * if permitted, override anything initialised just previously. 232dc833851Skre */ 23361f28255Scgd for (envp = environ ; *envp ; envp++) { 23461f28255Scgd if (strchr(*envp, '=')) { 235d73cf48cSkre setvareqsafe(*envp, VEXPORT|VTEXTFIXED|VUNSAFE); 23661f28255Scgd } 23761f28255Scgd } 2381dd2c2fdSchristos 2391dd2c2fdSchristos /* 240f52337daSkre * Set variables which override anything read from environment. 241f52337daSkre * 2421dd2c2fdSchristos * PPID is readonly 243f52337daSkre * Always default IFS 244c6c29888Skre * POSIX: "Whenever the shell is invoked, OPTIND shall 245c6c29888Skre * be initialized to 1." 246dc833851Skre * PSc indicates the root/non-root status of this shell. 247dc833851Skre * START_TIME belongs only to this shell. 248c6c29888Skre * NETBSD_SHELL is a constant (readonly), and is never exported 249727a69dcSkre * LINENO is simply magic... 2501dd2c2fdSchristos */ 2511dd2c2fdSchristos snprintf(buf, sizeof(buf), "%d", (int)getppid()); 2521dd2c2fdSchristos setvar("PPID", buf, VREADONLY); 253ca12a0b8Schristos setvar("IFS", ifs_default, VTEXTFIXED); 254c6c29888Skre setvar("OPTIND", "1", VTEXTFIXED); 255dc833851Skre setvar("PSc", (geteuid() == 0 ? "#" : "$"), VTEXTFIXED); 256dc833851Skre 257dc833851Skre #ifndef SMALL 258dc833851Skre snprintf(buf, sizeof(buf), "%jd", sh_start_time); 259dc833851Skre setvar("START_TIME", buf, VTEXTFIXED); 260dc833851Skre #endif 261f52337daSkre 262f52337daSkre setvar("NETBSD_SHELL", NETBSD_SHELL 263f52337daSkre #ifdef BUILD_DATE 264f52337daSkre " BUILD:" BUILD_DATE 265f52337daSkre #endif 266f52337daSkre #ifdef DEBUG 267f52337daSkre " DEBUG" 268f52337daSkre #endif 269f52337daSkre #if !defined(JOBS) || JOBS == 0 270f52337daSkre " -JOBS" 271f52337daSkre #endif 272f52337daSkre #ifndef DO_SHAREDVFORK 273f52337daSkre " -VFORK" 274f52337daSkre #endif 275f52337daSkre #ifdef SMALL 276f52337daSkre " SMALL" 277f52337daSkre #endif 278f52337daSkre #ifdef TINY 279f52337daSkre " TINY" 280f52337daSkre #endif 281f52337daSkre #ifdef OLD_TTY_DRIVER 282f52337daSkre " OLD_TTY" 283f52337daSkre #endif 284f52337daSkre #ifdef SYSV 285f52337daSkre " SYSV" 286f52337daSkre #endif 287f52337daSkre #ifndef BSD 288f52337daSkre " -BSD" 289f52337daSkre #endif 2902d8874d9Skre #ifdef BOGUS_NOT_COMMAND 2912d8874d9Skre " BOGUS_NOT" 2922d8874d9Skre #endif 293472691bdSkre #ifdef REJECT_NULS 294472691bdSkre " REJECT_NULS" 295472691bdSkre #endif 296472691bdSkre #ifdef RESCUEDIR 297472691bdSkre " RESCUE" 298472691bdSkre #endif 299f52337daSkre , VTEXTFIXED|VREADONLY|VNOEXPORT); 300fd38bbe2Skre 301fd38bbe2Skre setvar("LINENO", "1", VTEXTFIXED); 30261f28255Scgd } 30361f28255Scgd #endif 30461f28255Scgd 30561f28255Scgd 30661f28255Scgd /* 30761f28255Scgd * This routine initializes the builtin variables. It is called when the 30861f28255Scgd * shell is initialized and again when a shell procedure is spawned. 30961f28255Scgd */ 31061f28255Scgd 31161f28255Scgd void 312c02b3bbdSchristos initvar(void) 313c02b3bbdSchristos { 31461f28255Scgd const struct varinit *ip; 31561f28255Scgd struct var *vp; 31661f28255Scgd struct var **vpp; 31761f28255Scgd 31861f28255Scgd for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { 3195f9b9101Sdsl if (find_var(ip->text, &vpp, &vp->name_len) != NULL) 3205f9b9101Sdsl continue; 32161f28255Scgd vp->next = *vpp; 32261f28255Scgd *vpp = vp; 3233d424690Schristos vp->text = strdup(ip->text); 324dc833851Skre vp->flags = (ip->flags & ~VTEXTFIXED) | VSTRFIXED; 325727a69dcSkre vp->v_u = ip->v_u; 32661f28255Scgd } 32761f28255Scgd /* 32861f28255Scgd * PS1 depends on uid 32961f28255Scgd */ 3305f9b9101Sdsl if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { 33161f28255Scgd vps1.next = *vpp; 33261f28255Scgd *vpp = &vps1; 333dc833851Skre vps1.flags = VSTRFIXED; 334ac33f4eaSchristos vps1.text = NULL; 335ac33f4eaSchristos choose_ps1(); 33661f28255Scgd } 33761f28255Scgd } 33861f28255Scgd 339ac33f4eaSchristos void 340ac33f4eaSchristos choose_ps1(void) 341ac33f4eaSchristos { 342522d1631Skre uid_t u = geteuid(); 343522d1631Skre 344dc833851Skre if ((vps1.flags & (VTEXTFIXED|VSTACK)) == 0) 345ac33f4eaSchristos free(vps1.text); 346522d1631Skre vps1.text = strdup(u != 0 ? "PS1=$ " : "PS1=# "); 347dc833851Skre vps1.flags &= ~(VTEXTFIXED|VSTACK); 348dc833851Skre 349dc833851Skre /* 350dc833851Skre * Update PSc whenever we feel the need to update PS1 351dc833851Skre */ 352522d1631Skre setvarsafe("PSc", (u == 0 ? "#" : "$"), 0); 353ac33f4eaSchristos } 354ac33f4eaSchristos 35561f28255Scgd /* 3564512d568Skre * Validate a string as a valid variable name 3574512d568Skre * nb: not parameter - special params and such are "invalid" here. 3584512d568Skre * Name terminated by either \0 or the term param (usually '=' or '\0'). 3594512d568Skre * 3604512d568Skre * If not NULL, the length of the (intended) name is returned via len 3614512d568Skre */ 3624512d568Skre 3634512d568Skre int 3644512d568Skre validname(const char *name, int term, int *len) 3654512d568Skre { 3664512d568Skre const char *p = name; 3674512d568Skre int ok = 1; 3684512d568Skre 3694512d568Skre if (p == NULL || *p == '\0' || *p == term) { 3704512d568Skre if (len != NULL) 3714512d568Skre *len = 0; 3724512d568Skre return 0; 3734512d568Skre } 3744512d568Skre 3754512d568Skre if (!is_name(*p)) 3764512d568Skre ok = 0; 3774512d568Skre p++; 3784512d568Skre for (;;) { 3794512d568Skre if (*p == '\0' || *p == term) 3804512d568Skre break; 3814512d568Skre if (!is_in_name(*p)) 3824512d568Skre ok = 0; 3834512d568Skre p++; 3844512d568Skre } 3854512d568Skre if (len != NULL) 3864512d568Skre *len = p - name; 3874512d568Skre 3884512d568Skre return ok; 3894512d568Skre } 3904512d568Skre 3914512d568Skre /* 392beb57fb3Schristos * Safe version of setvar, returns 1 on success 0 on failure. 393beb57fb3Schristos */ 394beb57fb3Schristos 395beb57fb3Schristos int 396c02b3bbdSchristos setvarsafe(const char *name, const char *val, int flags) 397beb57fb3Schristos { 398beb57fb3Schristos struct jmploc jmploc; 399354d46cbSkre struct jmploc * const savehandler = handler; 400a54fe38aSchristos int volatile err = 0; 401beb57fb3Schristos 402beb57fb3Schristos if (setjmp(jmploc.loc)) 403beb57fb3Schristos err = 1; 404beb57fb3Schristos else { 405beb57fb3Schristos handler = &jmploc; 406beb57fb3Schristos setvar(name, val, flags); 407beb57fb3Schristos } 408beb57fb3Schristos handler = savehandler; 409beb57fb3Schristos return err; 410beb57fb3Schristos } 411beb57fb3Schristos 412d73cf48cSkre void 413d73cf48cSkre setvareqsafe(char *s, int flags) 414d73cf48cSkre { 415d73cf48cSkre struct jmploc jmploc; 416d73cf48cSkre struct jmploc * const savehandler = handler; 417d73cf48cSkre volatile int e_s = errors_suppressed; 418d73cf48cSkre 419d73cf48cSkre if (!setjmp(jmploc.loc)) { 420d73cf48cSkre handler = &jmploc; 421d73cf48cSkre errors_suppressed = 1; 422d73cf48cSkre setvareq(s, flags); 423d73cf48cSkre } 424d73cf48cSkre handler = savehandler; 425d73cf48cSkre errors_suppressed = e_s; 426d73cf48cSkre } 427d73cf48cSkre 428beb57fb3Schristos /* 42961f28255Scgd * Set the value of a variable. The flags argument is ored with the 43061f28255Scgd * flags of the variable. If val is NULL, the variable is unset. 431dc833851Skre * 432dc833851Skre * This always copies name and val when setting a variable, so 433dc833851Skre * the source strings can be from anywhere, and are no longer needed 434dc833851Skre * after this function returns. The VTEXTFIXED and VSTACK flags should 435dc833851Skre * not be used (but just in case they were, clear them.) 43661f28255Scgd */ 43761f28255Scgd 43861f28255Scgd void 439c02b3bbdSchristos setvar(const char *name, const char *val, int flags) 44061f28255Scgd { 4413d424690Schristos const char *p; 4423d424690Schristos const char *q; 4433d424690Schristos char *d; 44461f28255Scgd int len; 44561f28255Scgd int namelen; 44661f28255Scgd char *nameeq; 44761f28255Scgd 44861f28255Scgd p = name; 4494512d568Skre 4504512d568Skre if (!validname(p, '=', &namelen)) 45137ed7877Sjtc error("%.*s: bad variable name", namelen, name); 45261f28255Scgd len = namelen + 2; /* 2 is space for '=' and '\0' */ 45361f28255Scgd if (val == NULL) { 45461f28255Scgd flags |= VUNSET; 45561f28255Scgd } else { 45661f28255Scgd len += strlen(val); 45761f28255Scgd } 4583d424690Schristos d = nameeq = ckmalloc(len); 45961f28255Scgd q = name; 46061f28255Scgd while (--namelen >= 0) 4613d424690Schristos *d++ = *q++; 4623d424690Schristos *d++ = '='; 4633d424690Schristos *d = '\0'; 46461f28255Scgd if (val) 4653d424690Schristos scopy(val, d); 466dc833851Skre setvareq(nameeq, flags & ~(VTEXTFIXED | VSTACK)); 46761f28255Scgd } 46861f28255Scgd 46961f28255Scgd 47061f28255Scgd 47161f28255Scgd /* 47261f28255Scgd * Same as setvar except that the variable and value are passed in 47361f28255Scgd * the first argument as name=value. Since the first argument will 47461f28255Scgd * be actually stored in the table, it should not be a string that 475dc833851Skre * will go away. The flags (VTEXTFIXED or VSTACK) can be used to 476dc833851Skre * indicate the source of the string (if neither is set, the string will 477dc833851Skre * eventually be free()d when a replacement value is assigned.) 47861f28255Scgd */ 47961f28255Scgd 48061f28255Scgd void 481c02b3bbdSchristos setvareq(char *s, int flags) 48261f28255Scgd { 48361f28255Scgd struct var *vp, **vpp; 4845f9b9101Sdsl int nlen; 48561f28255Scgd 48659695ae2Skre VTRACE(DBG_VARS, ("setvareq([%s],%#x) aflag=%d ", s, flags, aflag)); 4879302f8efSchristos if (aflag && !(flags & VNOEXPORT)) 4886f786375Sbjh21 flags |= VEXPORT; 4895f9b9101Sdsl vp = find_var(s, &vpp, &nlen); 4905f9b9101Sdsl if (vp != NULL) { 49159695ae2Skre VTRACE(DBG_VARS, ("was [%s] fl:%#x\n", vp->text, 49259695ae2Skre vp->flags)); 493ad0b72d9Skre if (vp->flags & VREADONLY) { 494ad0b72d9Skre if ((flags & (VTEXTFIXED|VSTACK)) == 0) 495ad0b72d9Skre ckfree(s); 496ad0b72d9Skre if (flags & VNOERROR) 497c02b3bbdSchristos return; 498cdfea658Skre error("%.*s: is read only", vp->name_len, vp->text); 499ad0b72d9Skre } 500ad0b72d9Skre if (flags & VNOSET) { 501ad0b72d9Skre if ((flags & (VTEXTFIXED|VSTACK)) == 0) 502ad0b72d9Skre ckfree(s); 503ad0b72d9Skre return; 504ad0b72d9Skre } 505727a69dcSkre 50661f28255Scgd INTOFF; 507beb57fb3Schristos 508727a69dcSkre if (vp->func && !(vp->flags & VFUNCREF) && !(flags & VNOFUNC)) 509d73cf48cSkre (*vp->func)(s + vp->name_len + 1, flags); 510beb57fb3Schristos 51161f28255Scgd if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) 51261f28255Scgd ckfree(vp->text); 513beb57fb3Schristos 514370f00caSkre /* 515370f00caSkre * if we set a magic var, the magic dissipates, 516370f00caSkre * unless it is very special indeed. 517370f00caSkre */ 518370f00caSkre if (vp->rfunc && (vp->flags & (VFUNCREF|VSPECIAL)) == VFUNCREF) 519370f00caSkre vp->rfunc = NULL; 520370f00caSkre 521d73cf48cSkre vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET|VUNSAFE); 5229302f8efSchristos if (flags & VNOEXPORT) 5239302f8efSchristos vp->flags &= ~VEXPORT; 524522d1631Skre if (flags & VDOEXPORT) 525522d1631Skre vp->flags &= ~VNOEXPORT; 5268df083c1Skre if (vp->flags & VNOEXPORT) 5278df083c1Skre flags &= ~VEXPORT; 528522d1631Skre vp->flags |= flags & ~(VNOFUNC | VDOEXPORT); 52961f28255Scgd vp->text = s; 530beb57fb3Schristos 531beb57fb3Schristos /* 532beb57fb3Schristos * We could roll this to a function, to handle it as 533beb57fb3Schristos * a regular variable function callback, but why bother? 534beb57fb3Schristos */ 53561f28255Scgd if (vp == &vmpath || (vp == &vmail && ! mpathset())) 53661f28255Scgd chkmail(1); 537727a69dcSkre 53861f28255Scgd INTON; 53961f28255Scgd return; 54061f28255Scgd } 54161f28255Scgd /* not found */ 542ad0b72d9Skre if (flags & VNOSET) { 543522d1631Skre VTRACE(DBG_VARS, ("new noset\n")); 544ad0b72d9Skre if ((flags & (VTEXTFIXED|VSTACK)) == 0) 545ad0b72d9Skre ckfree(s); 546c02b3bbdSchristos return; 547ad0b72d9Skre } 54861f28255Scgd vp = ckmalloc(sizeof (*vp)); 549522d1631Skre vp->flags = flags & ~(VNOFUNC|VFUNCREF|VDOEXPORT); 55061f28255Scgd vp->text = s; 5515f9b9101Sdsl vp->name_len = nlen; 552beb57fb3Schristos vp->func = NULL; 553522d1631Skre vp->next = *vpp; 55461f28255Scgd *vpp = vp; 555522d1631Skre 556522d1631Skre VTRACE(DBG_VARS, ("new [%s] (%d) %#x\n", s, nlen, vp->flags)); 55761f28255Scgd } 55861f28255Scgd 559e7b0505eSkre void 560e7b0505eSkre setvar_invocation(int argc, char **argv) 561e7b0505eSkre { 562e7b0505eSkre char value[32]; /* if we ever get 30, HELP */ 563e7b0505eSkre char *v; 56461f28255Scgd 565e7b0505eSkre /* 566e7b0505eSkre * Keep the following in ascii lexical order ( ie: Z before a ) 567e7b0505eSkre */ 568e7b0505eSkre 569e7b0505eSkre v = value; 5709eccf618Skre *v++ = '!'; /* never empty, and the '-' is not first */ 571e7b0505eSkre 572e7b0505eSkre if (argc > 0 && argv[0] != NULL && argv[0][0] == '-') 573e7b0505eSkre *v++ = '-'; 574e7b0505eSkre if (shellparam.nparam == 0) 575e7b0505eSkre *v++ = '0'; 576e7b0505eSkre if (minusc) 577e7b0505eSkre *v++ = 'c'; 578e7b0505eSkre if (commandname) 579e7b0505eSkre *v++ = 'f'; 580e7b0505eSkre if (iflag) 581e7b0505eSkre *v++ = 'i'; 582e7b0505eSkre if (loginsh) 583e7b0505eSkre *v++ = 'l'; 584e7b0505eSkre if (privileged) 585e7b0505eSkre *v++ = 'p'; 586e7b0505eSkre if (sflag) 587e7b0505eSkre *v++ = 's'; 588e7b0505eSkre 589e7b0505eSkre *v++ = '\0'; 590e7b0505eSkre 591e7b0505eSkre /* 592e7b0505eSkre * this cannot fail, the var name is OK, 593e7b0505eSkre * there cannot be any (non special) read only 594e7b0505eSkre * variables at this point, ... 595e7b0505eSkre */ 596e7b0505eSkre setvar("NBSH_INVOCATION", value, VNOEXPORT); 597e7b0505eSkre } 59861f28255Scgd 59961f28255Scgd /* 60061f28255Scgd * Process a linked list of variable assignments. 60161f28255Scgd */ 60261f28255Scgd 60361f28255Scgd void 604c02b3bbdSchristos listsetvar(struct strlist *list, int flags) 60561f28255Scgd { 60661f28255Scgd struct strlist *lp; 60761f28255Scgd 60861f28255Scgd INTOFF; 60961f28255Scgd for (lp = list ; lp ; lp = lp->next) { 610c02b3bbdSchristos setvareq(savestr(lp->text), flags); 61161f28255Scgd } 61261f28255Scgd INTON; 61361f28255Scgd } 61461f28255Scgd 615e314f958Sdsl void 616e314f958Sdsl listmklocal(struct strlist *list, int flags) 617e314f958Sdsl { 618e314f958Sdsl struct strlist *lp; 619e314f958Sdsl 620e314f958Sdsl for (lp = list ; lp ; lp = lp->next) 621e314f958Sdsl mklocal(lp->text, flags); 622e314f958Sdsl } 62361f28255Scgd 62461f28255Scgd 62561f28255Scgd /* 62661f28255Scgd * Find the value of a variable. Returns NULL if not set. 62761f28255Scgd */ 62861f28255Scgd 62961f28255Scgd char * 630c02b3bbdSchristos lookupvar(const char *name) 63161f28255Scgd { 63261f28255Scgd struct var *v; 63383735e24Skre char *p; 63461f28255Scgd 6355f9b9101Sdsl v = find_var(name, NULL, NULL); 6365f9b9101Sdsl if (v == NULL || v->flags & VUNSET) 63761f28255Scgd return NULL; 63883735e24Skre if (v->rfunc && (v->flags & VFUNCREF) != 0) { 63983735e24Skre p = (*v->rfunc)(v); 64083735e24Skre if (p == NULL) 64183735e24Skre return NULL; 64283735e24Skre } else 64383735e24Skre p = v->text; 64483735e24Skre 64583735e24Skre return p + v->name_len + 1; 64661f28255Scgd } 64761f28255Scgd 64861f28255Scgd 64961f28255Scgd 65061f28255Scgd /* 65161f28255Scgd * Search the environment of a builtin command. If the second argument 65261f28255Scgd * is nonzero, return the value of a variable even if it hasn't been 65361f28255Scgd * exported. 65461f28255Scgd */ 65561f28255Scgd 65661f28255Scgd char * 657c02b3bbdSchristos bltinlookup(const char *name, int doall) 65861f28255Scgd { 65961f28255Scgd struct strlist *sp; 66061f28255Scgd struct var *v; 66183735e24Skre char *p; 66261f28255Scgd 66361f28255Scgd for (sp = cmdenviron ; sp ; sp = sp->next) { 6645f9b9101Sdsl if (strequal(sp->text, name)) 66561f28255Scgd return strchr(sp->text, '=') + 1; 66661f28255Scgd } 6675f9b9101Sdsl 6685f9b9101Sdsl v = find_var(name, NULL, NULL); 6695f9b9101Sdsl 6705f9b9101Sdsl if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT))) 67161f28255Scgd return NULL; 67283735e24Skre 67383735e24Skre if (v->rfunc && (v->flags & VFUNCREF) != 0) { 67483735e24Skre p = (*v->rfunc)(v); 67583735e24Skre if (p == NULL) 67683735e24Skre return NULL; 67783735e24Skre } else 67883735e24Skre p = v->text; 67983735e24Skre 68083735e24Skre return p + v->name_len + 1; 68161f28255Scgd } 68261f28255Scgd 68361f28255Scgd 68461f28255Scgd 68561f28255Scgd /* 68661f28255Scgd * Generate a list of exported variables. This routine is used to construct 68761f28255Scgd * the third argument to execve when executing a program. 68861f28255Scgd */ 68961f28255Scgd 69061f28255Scgd char ** 691c02b3bbdSchristos environment(void) 692c02b3bbdSchristos { 69361f28255Scgd int nenv; 69461f28255Scgd struct var **vpp; 69561f28255Scgd struct var *vp; 6963d424690Schristos char **env; 6973d424690Schristos char **ep; 69861f28255Scgd 69961f28255Scgd nenv = 0; 70061f28255Scgd for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 70161f28255Scgd for (vp = *vpp ; vp ; vp = vp->next) 7023276f23bSkre if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) 70361f28255Scgd nenv++; 70461f28255Scgd } 705ce6f66ceSkre CTRACE(DBG_VARS, ("environment: %d vars to export\n", nenv)); 70661f28255Scgd ep = env = stalloc((nenv + 1) * sizeof *env); 70761f28255Scgd for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 70861f28255Scgd for (vp = *vpp ; vp ; vp = vp->next) 709727a69dcSkre if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) { 71083735e24Skre if (vp->rfunc && (vp->flags & VFUNCREF)) { 71183735e24Skre *ep = (*vp->rfunc)(vp); 71283735e24Skre if (*ep != NULL) 71383735e24Skre ep++; 71483735e24Skre } else 71561f28255Scgd *ep++ = vp->text; 716ce6f66ceSkre VTRACE(DBG_VARS, ("environment: %s\n", ep[-1])); 71761f28255Scgd } 718727a69dcSkre } 71961f28255Scgd *ep = NULL; 72061f28255Scgd return env; 72161f28255Scgd } 72261f28255Scgd 72361f28255Scgd 72461f28255Scgd /* 72561f28255Scgd * Called when a shell procedure is invoked to clear out nonexported 72661f28255Scgd * variables. It is also necessary to reallocate variables of with 72761f28255Scgd * VSTACK set since these are currently allocated on the stack. 72861f28255Scgd */ 72961f28255Scgd 73061f28255Scgd #ifdef mkinit 731c02b3bbdSchristos void shprocvar(void); 73261f28255Scgd 73361f28255Scgd SHELLPROC { 73461f28255Scgd shprocvar(); 73561f28255Scgd } 73661f28255Scgd #endif 73761f28255Scgd 73861f28255Scgd void 739c02b3bbdSchristos shprocvar(void) 740c02b3bbdSchristos { 74161f28255Scgd struct var **vpp; 74261f28255Scgd struct var *vp, **prev; 74361f28255Scgd 74461f28255Scgd for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 74561f28255Scgd for (prev = vpp ; (vp = *prev) != NULL ; ) { 74661f28255Scgd if ((vp->flags & VEXPORT) == 0) { 74761f28255Scgd *prev = vp->next; 74861f28255Scgd if ((vp->flags & VTEXTFIXED) == 0) 74961f28255Scgd ckfree(vp->text); 75061f28255Scgd if ((vp->flags & VSTRFIXED) == 0) 75161f28255Scgd ckfree(vp); 75261f28255Scgd } else { 75361f28255Scgd if (vp->flags & VSTACK) { 75461f28255Scgd vp->text = savestr(vp->text); 75561f28255Scgd vp->flags &=~ VSTACK; 75661f28255Scgd } 75761f28255Scgd prev = &vp->next; 75861f28255Scgd } 75961f28255Scgd } 76061f28255Scgd } 76161f28255Scgd initvar(); 76261f28255Scgd } 76361f28255Scgd 76461f28255Scgd 76561f28255Scgd 76661f28255Scgd /* 76761f28255Scgd * Command to list all variables which are set. Currently this command 76861f28255Scgd * is invoked from the set command when the set command is called without 76961f28255Scgd * any variables. 77061f28255Scgd */ 77161f28255Scgd 772c02b3bbdSchristos void 773c02b3bbdSchristos print_quoted(const char *p) 774c02b3bbdSchristos { 775c02b3bbdSchristos const char *q; 776c02b3bbdSchristos 7776f0d4805Skre if (p[0] == '\0') { 7786f0d4805Skre out1fmt("''"); 7796f0d4805Skre return; 7806f0d4805Skre } 781c02b3bbdSchristos if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) { 782c02b3bbdSchristos out1fmt("%s", p); 783c02b3bbdSchristos return; 784c02b3bbdSchristos } 785c02b3bbdSchristos while (*p) { 786c02b3bbdSchristos if (*p == '\'') { 787c02b3bbdSchristos out1fmt("\\'"); 788c02b3bbdSchristos p++; 789c02b3bbdSchristos continue; 790c02b3bbdSchristos } 7916814c65dSchristos q = strchr(p, '\''); 792c02b3bbdSchristos if (!q) { 793c02b3bbdSchristos out1fmt("'%s'", p ); 794c02b3bbdSchristos return; 795c02b3bbdSchristos } 796ea1d6159Sagc out1fmt("'%.*s'", (int)(q - p), p ); 797c02b3bbdSchristos p = q; 798c02b3bbdSchristos } 799c02b3bbdSchristos } 800c02b3bbdSchristos 801c02b3bbdSchristos static int 802c02b3bbdSchristos sort_var(const void *v_v1, const void *v_v2) 803c02b3bbdSchristos { 804c02b3bbdSchristos const struct var * const *v1 = v_v1; 805c02b3bbdSchristos const struct var * const *v2 = v_v2; 806be0a98abSkre char *t1 = (*v1)->text, *t2 = (*v2)->text; 807c02b3bbdSchristos 808be0a98abSkre if (*t1 == *t2) { 809be0a98abSkre char *p, *s; 810be0a98abSkre 811be0a98abSkre STARTSTACKSTR(p); 812be0a98abSkre 813be0a98abSkre /* 814be0a98abSkre * note: if lengths are equal, strings must be different 815be0a98abSkre * so we don't care which string we pick for the \0 in 816be0a98abSkre * that case. 817be0a98abSkre */ 818be0a98abSkre if ((strchr(t1, '=') - t1) <= (strchr(t2, '=') - t2)) { 819be0a98abSkre s = t1; 820be0a98abSkre t1 = p; 821be0a98abSkre } else { 822be0a98abSkre s = t2; 823be0a98abSkre t2 = p; 824be0a98abSkre } 825be0a98abSkre 826be0a98abSkre while (*s && *s != '=') { 827be0a98abSkre STPUTC(*s, p); 828be0a98abSkre s++; 829be0a98abSkre } 830be0a98abSkre STPUTC('\0', p); 831be0a98abSkre } 832be0a98abSkre 833be0a98abSkre return strcoll(t1, t2); 834c02b3bbdSchristos } 835c02b3bbdSchristos 836c02b3bbdSchristos /* 837c02b3bbdSchristos * POSIX requires that 'set' (but not export or readonly) output the 838c02b3bbdSchristos * variables in lexicographic order - by the locale's collating order (sigh). 839c02b3bbdSchristos * Maybe we could keep them in an ordered balanced binary tree 840c02b3bbdSchristos * instead of hashed lists. 841c02b3bbdSchristos * For now just roll 'em through qsort for printing... 842c02b3bbdSchristos */ 843c02b3bbdSchristos 844150d8297Skre STATIC void 845150d8297Skre showvar(struct var *vp, const char *cmd, const char *xtra, int show_value) 846150d8297Skre { 847150d8297Skre const char *p; 848150d8297Skre 84983735e24Skre p = vp->text; 85083735e24Skre if (vp->rfunc && (vp->flags & VFUNCREF) != 0) { 85183735e24Skre p = (*vp->rfunc)(vp); 85283735e24Skre if (p == NULL) { 85383735e24Skre if (!(show_value & 2)) 85483735e24Skre return; 85583735e24Skre p = vp->text; 85683735e24Skre show_value = 0; 85783735e24Skre } 85883735e24Skre } 859150d8297Skre if (cmd) 860150d8297Skre out1fmt("%s ", cmd); 861150d8297Skre if (xtra) 862150d8297Skre out1fmt("%s ", xtra); 863150d8297Skre for ( ; *p != '=' ; p++) 864150d8297Skre out1c(*p); 865150d8297Skre if (!(vp->flags & VUNSET) && show_value) { 866150d8297Skre out1fmt("="); 867150d8297Skre print_quoted(++p); 868150d8297Skre } 869150d8297Skre out1c('\n'); 870150d8297Skre } 871150d8297Skre 87261f28255Scgd int 873150d8297Skre showvars(const char *cmd, int flag, int show_value, const char *xtra) 8744ce0d34aScgd { 87561f28255Scgd struct var **vpp; 87661f28255Scgd struct var *vp; 877c02b3bbdSchristos 878c02b3bbdSchristos static struct var **list; /* static in case we are interrupted */ 879c02b3bbdSchristos static int list_len; 880c02b3bbdSchristos int count = 0; 881c02b3bbdSchristos 882c02b3bbdSchristos if (!list) { 883c02b3bbdSchristos list_len = 32; 884c02b3bbdSchristos list = ckmalloc(list_len * sizeof *list); 885c02b3bbdSchristos } 88661f28255Scgd 88761f28255Scgd for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 88861f28255Scgd for (vp = *vpp ; vp ; vp = vp->next) { 889c02b3bbdSchristos if (flag && !(vp->flags & flag)) 890c02b3bbdSchristos continue; 891c02b3bbdSchristos if (vp->flags & VUNSET && !(show_value & 2)) 892c02b3bbdSchristos continue; 893c02b3bbdSchristos if (count >= list_len) { 894c02b3bbdSchristos list = ckrealloc(list, 895c02b3bbdSchristos (list_len << 1) * sizeof *list); 896c02b3bbdSchristos list_len <<= 1; 89761f28255Scgd } 898c02b3bbdSchristos list[count++] = vp; 899c02b3bbdSchristos } 900c02b3bbdSchristos } 901c02b3bbdSchristos 902c02b3bbdSchristos qsort(list, count, sizeof *list, sort_var); 903c02b3bbdSchristos 904150d8297Skre for (vpp = list; count--; vpp++) 905150d8297Skre showvar(*vpp, cmd, xtra, show_value); 906150d8297Skre 907150d8297Skre /* no free(list), will be used again next time ... */ 908150d8297Skre 90961f28255Scgd return 0; 91061f28255Scgd } 91161f28255Scgd 91261f28255Scgd 91361f28255Scgd 91461f28255Scgd /* 91561f28255Scgd * The export and readonly commands. 91661f28255Scgd */ 91761f28255Scgd 918150d8297Skre static void __dead 919150d8297Skre export_usage(const char *cmd) 920150d8297Skre { 921150d8297Skre #ifdef SMALL 922150d8297Skre if (*cmd == 'r') 923150d8297Skre error("Usage: %s [ -p | var[=val]... ]", cmd); 924150d8297Skre else 925150d8297Skre error("Usage: %s [ -p | [-n] var[=val]... ]", cmd); 926150d8297Skre #else 927150d8297Skre if (*cmd == 'r') 928150d8297Skre error("Usage: %s [-p [var...] | -q var... | var[=val]... ]", cmd); 929150d8297Skre else 930150d8297Skre error( 931150d8297Skre "Usage: %s [ -px [var...] | -q[x] var... | [-n|x] var[=val]... ]", 932150d8297Skre cmd); 933150d8297Skre #endif 934150d8297Skre } 935150d8297Skre 93661f28255Scgd int 937c02b3bbdSchristos exportcmd(int argc, char **argv) 9384ce0d34aScgd { 93961f28255Scgd struct var *vp; 94061f28255Scgd char *name; 941150d8297Skre const char *p = argv[0]; 942150d8297Skre int flag = p[0] == 'r'? VREADONLY : VEXPORT; 9439302f8efSchristos int pflg = 0; 9449302f8efSchristos int nflg = 0; 945150d8297Skre #ifndef SMALL 9469302f8efSchristos int xflg = 0; 947150d8297Skre int qflg = 0; 948150d8297Skre #endif 9499302f8efSchristos int res; 9509302f8efSchristos int c; 9513276f23bSkre int f; 9529302f8efSchristos 953150d8297Skre #ifdef SMALL 954150d8297Skre #define EXPORT_OPTS "np" 955150d8297Skre #else 956150d8297Skre #define EXPORT_OPTS "npqx" 957150d8297Skre #endif 958150d8297Skre 959150d8297Skre while ((c = nextopt(EXPORT_OPTS)) != '\0') { 960150d8297Skre 961150d8297Skre #undef EXPORT_OPTS 962150d8297Skre 9639302f8efSchristos switch (c) { 964150d8297Skre case 'n': 965150d8297Skre if (pflg || flag == VREADONLY 966150d8297Skre #ifndef SMALL 967150d8297Skre || qflg || xflg 968150d8297Skre #endif 969150d8297Skre ) 970150d8297Skre export_usage(p); 971150d8297Skre nflg = 1; 972150d8297Skre break; 9739302f8efSchristos case 'p': 974150d8297Skre if (nflg 975150d8297Skre #ifndef SMALL 976150d8297Skre || qflg 977150d8297Skre #endif 978150d8297Skre ) 979150d8297Skre export_usage(p); 9809302f8efSchristos pflg = 3; 9819302f8efSchristos break; 982150d8297Skre #ifndef SMALL 983150d8297Skre case 'q': 984150d8297Skre if (nflg || pflg) 985150d8297Skre export_usage(p); 986150d8297Skre qflg = 1; 9879302f8efSchristos break; 9889302f8efSchristos case 'x': 9899302f8efSchristos if (nflg || flag == VREADONLY) 990150d8297Skre export_usage(p); 9919302f8efSchristos flag = VNOEXPORT; 9929302f8efSchristos xflg = 1; 9939302f8efSchristos break; 994150d8297Skre #endif 9959302f8efSchristos } 9969302f8efSchristos } 9979302f8efSchristos 998150d8297Skre if ((nflg 999150d8297Skre #ifndef SMALL 1000150d8297Skre || qflg 1001150d8297Skre #endif 1002150d8297Skre ) && *argptr == NULL) 1003150d8297Skre export_usage(p); 10049302f8efSchristos 1005150d8297Skre #ifndef SMALL 1006150d8297Skre if (pflg && *argptr != NULL) { 1007150d8297Skre while ((name = *argptr++) != NULL) { 1008150d8297Skre int len; 1009150d8297Skre 1010150d8297Skre vp = find_var(name, NULL, &len); 1011150d8297Skre if (name[len] == '=') 1012150d8297Skre export_usage(p); 1013150d8297Skre if (!goodname(name)) 1014150d8297Skre error("%s: bad variable name", name); 1015150d8297Skre 1016150d8297Skre if (vp && vp->flags & flag) 1017150d8297Skre showvar(vp, p, xflg ? "-x" : NULL, 1); 1018150d8297Skre } 1019c02b3bbdSchristos return 0; 1020c02b3bbdSchristos } 1021150d8297Skre #endif 1022150d8297Skre 1023150d8297Skre if (pflg || *argptr == NULL) 1024150d8297Skre return showvars( pflg ? p : 0, flag, pflg, 1025150d8297Skre #ifndef SMALL 1026150d8297Skre pflg && xflg ? "-x" : 1027150d8297Skre #endif 1028150d8297Skre NULL ); 1029c02b3bbdSchristos 10309302f8efSchristos res = 0; 1031150d8297Skre #ifndef SMALL 1032150d8297Skre if (qflg) { 1033150d8297Skre while ((name = *argptr++) != NULL) { 1034150d8297Skre int len; 1035150d8297Skre 1036150d8297Skre vp = find_var(name, NULL, &len); 1037150d8297Skre if (name[len] == '=') 1038150d8297Skre export_usage(p); 1039150d8297Skre if (!goodname(name)) 1040150d8297Skre error("%s: bad variable name", name); 1041150d8297Skre 1042150d8297Skre if (vp == NULL || !(vp->flags & flag)) 1043150d8297Skre res = 1; 1044150d8297Skre } 1045150d8297Skre return res; 1046150d8297Skre } 1047150d8297Skre #endif 1048150d8297Skre 104961f28255Scgd while ((name = *argptr++) != NULL) { 1050522d1631Skre int len; 1051522d1631Skre 10523276f23bSkre f = flag; 1053522d1631Skre 1054522d1631Skre vp = find_var(name, NULL, &len); 1055522d1631Skre p = name + len; 1056522d1631Skre if (*p++ != '=') 1057522d1631Skre p = NULL; 1058522d1631Skre 10595f9b9101Sdsl if (vp != NULL) { 10609302f8efSchristos if (nflg) 10619302f8efSchristos vp->flags &= ~flag; 1062522d1631Skre else if (flag&VEXPORT && vp->flags&VNOEXPORT) { 106364c9a713Skre /* note we go ahead and do any assignment */ 1064522d1631Skre sh_warnx("%.*s: not available for export", 1065522d1631Skre len, name); 10669302f8efSchristos res = 1; 1067522d1631Skre } else { 10689302f8efSchristos if (flag == VNOEXPORT) 10699302f8efSchristos vp->flags &= ~VEXPORT; 107064c9a713Skre 107164c9a713Skre /* if not NULL will be done in setvar below */ 107264c9a713Skre if (p == NULL) 107364c9a713Skre vp->flags |= flag; 10749302f8efSchristos } 1075522d1631Skre if (p == NULL) 107687aaf3d7Senami continue; 1077150d8297Skre } else if (nflg && p == NULL && !goodname(name)) 1078150d8297Skre error("%s: bad variable name", name); 1079522d1631Skre 1080522d1631Skre if (!nflg || p != NULL) 10813276f23bSkre setvar(name, p, f); 108261f28255Scgd } 10839302f8efSchristos return res; 108461f28255Scgd } 108561f28255Scgd 108661f28255Scgd 108761f28255Scgd /* 108861f28255Scgd * The "local" command. 108961f28255Scgd */ 109061f28255Scgd 10914ce0d34aScgd int 1092c02b3bbdSchristos localcmd(int argc, char **argv) 10934ce0d34aScgd { 109461f28255Scgd char *name; 10958df083c1Skre int c; 10968df083c1Skre int flags = 0; /*XXX perhaps VUNSET from a -o option value */ 109761f28255Scgd 109861f28255Scgd if (! in_function()) 109961f28255Scgd error("Not in a function"); 11008df083c1Skre 11018df083c1Skre /* upper case options, as bash stole all the good ones ... */ 11028df083c1Skre while ((c = nextopt("INx")) != '\0') 11038df083c1Skre switch (c) { 11048df083c1Skre case 'I': flags &= ~VUNSET; break; 11058df083c1Skre case 'N': flags |= VUNSET; break; 11068df083c1Skre case 'x': flags |= VEXPORT; break; 11078df083c1Skre } 11088df083c1Skre 110961f28255Scgd while ((name = *argptr++) != NULL) { 11108df083c1Skre mklocal(name, flags); 111161f28255Scgd } 111261f28255Scgd return 0; 111361f28255Scgd } 111461f28255Scgd 111561f28255Scgd 111661f28255Scgd /* 1117a640fe8cSsnj * Make a variable a local variable. When a variable is made local, its 111861f28255Scgd * value and flags are saved in a localvar structure. The saved values 111961f28255Scgd * will be restored when the shell function returns. We handle the name 112061f28255Scgd * "-" as a special case. 112161f28255Scgd */ 112261f28255Scgd 112361f28255Scgd void 1124e314f958Sdsl mklocal(const char *name, int flags) 112561f28255Scgd { 112661f28255Scgd struct localvar *lvp; 112761f28255Scgd struct var **vpp; 112861f28255Scgd struct var *vp; 112961f28255Scgd 113061f28255Scgd INTOFF; 113161f28255Scgd lvp = ckmalloc(sizeof (struct localvar)); 113261f28255Scgd if (name[0] == '-' && name[1] == '\0') { 11333d424690Schristos char *p; 1134c02b3bbdSchristos p = ckmalloc(sizeof_optlist); 1135c02b3bbdSchristos lvp->text = memcpy(p, optlist, sizeof_optlist); 1136370f00caSkre lvp->rfunc = NULL; 113761f28255Scgd vp = NULL; 1138ffc64c63Skre xtrace_clone(0); 113961f28255Scgd } else { 11405f9b9101Sdsl vp = find_var(name, &vpp, NULL); 114161f28255Scgd if (vp == NULL) { 11428df083c1Skre flags &= ~VNOEXPORT; 114361f28255Scgd if (strchr(name, '=')) 11448df083c1Skre setvareq(savestr(name), 11458df083c1Skre VSTRFIXED | (flags & ~VUNSET)); 114661f28255Scgd else 1147edcb4544Schristos setvar(name, NULL, VSTRFIXED|flags); 114861f28255Scgd vp = *vpp; /* the new variable */ 114961f28255Scgd lvp->text = NULL; 115061f28255Scgd lvp->flags = VUNSET; 1151370f00caSkre lvp->rfunc = NULL; 115261f28255Scgd } else { 115361f28255Scgd lvp->text = vp->text; 115461f28255Scgd lvp->flags = vp->flags; 1155370f00caSkre lvp->v_u = vp->v_u; 115661f28255Scgd vp->flags |= VSTRFIXED|VTEXTFIXED; 1157522d1631Skre if (flags & (VDOEXPORT | VUNSET)) 1158522d1631Skre vp->flags &= ~VNOEXPORT; 1159522d1631Skre if (vp->flags & VNOEXPORT && 1160522d1631Skre (flags & (VEXPORT|VDOEXPORT|VUNSET)) == VEXPORT) 11618df083c1Skre flags &= ~VEXPORT; 11628df083c1Skre if (flags & (VNOEXPORT | VUNSET)) 11638df083c1Skre vp->flags &= ~VEXPORT; 11648df083c1Skre flags &= ~VNOEXPORT; 11655f9b9101Sdsl if (name[vp->name_len] == '=') 11668df083c1Skre setvareq(savestr(name), flags & ~VUNSET); 11678df083c1Skre else if (flags & VUNSET) 11688df083c1Skre unsetvar(name, 0); 11698df083c1Skre else 11708df083c1Skre vp->flags |= flags & (VUNSET|VEXPORT); 1171727a69dcSkre 1172727a69dcSkre if (vp == &line_num) { 1173727a69dcSkre if (name[vp->name_len] == '=') 1174727a69dcSkre funclinebase = funclineabs -1; 1175727a69dcSkre else 1176727a69dcSkre funclinebase = 0; 1177727a69dcSkre } 117861f28255Scgd } 117961f28255Scgd } 118061f28255Scgd lvp->vp = vp; 118161f28255Scgd lvp->next = localvars; 118261f28255Scgd localvars = lvp; 118361f28255Scgd INTON; 118461f28255Scgd } 118561f28255Scgd 118661f28255Scgd 118761f28255Scgd /* 118861f28255Scgd * Called after a function returns. 118961f28255Scgd */ 119061f28255Scgd 119161f28255Scgd void 1192c02b3bbdSchristos poplocalvars(void) 1193c02b3bbdSchristos { 119461f28255Scgd struct localvar *lvp; 119561f28255Scgd struct var *vp; 119661f28255Scgd 119761f28255Scgd while ((lvp = localvars) != NULL) { 119861f28255Scgd localvars = lvp->next; 119961f28255Scgd vp = lvp->vp; 1200522d1631Skre VTRACE(DBG_VARS, ("poplocalvar %s\n", vp ? vp->text : "-")); 120161f28255Scgd if (vp == NULL) { /* $- saved */ 1202c02b3bbdSchristos memcpy(optlist, lvp->text, sizeof_optlist); 120361f28255Scgd ckfree(lvp->text); 1204ffc64c63Skre xtrace_pop(); 1205727a69dcSkre optschanged(); 120661f28255Scgd } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { 1207c02b3bbdSchristos (void)unsetvar(vp->text, 0); 120861f28255Scgd } else { 1209370f00caSkre if (lvp->func && (lvp->flags & (VNOFUNC|VFUNCREF)) == 0) 1210d73cf48cSkre (*lvp->func)(lvp->text + vp->name_len + 1, 1211d73cf48cSkre lvp->flags); 121261f28255Scgd if ((vp->flags & VTEXTFIXED) == 0) 121361f28255Scgd ckfree(vp->text); 121461f28255Scgd vp->flags = lvp->flags; 121561f28255Scgd vp->text = lvp->text; 1216370f00caSkre vp->v_u = lvp->v_u; 121761f28255Scgd } 121861f28255Scgd ckfree(lvp); 121961f28255Scgd } 122061f28255Scgd } 122161f28255Scgd 122261f28255Scgd 12234ce0d34aScgd int 1224c02b3bbdSchristos setvarcmd(int argc, char **argv) 12254ce0d34aScgd { 122661f28255Scgd if (argc <= 2) 122761f28255Scgd return unsetcmd(argc, argv); 122861f28255Scgd else if (argc == 3) 122961f28255Scgd setvar(argv[1], argv[2], 0); 123061f28255Scgd else 123161f28255Scgd error("List assignment not implemented"); 123261f28255Scgd return 0; 123361f28255Scgd } 123461f28255Scgd 123561f28255Scgd 123661f28255Scgd /* 123761f28255Scgd * The unset builtin command. We unset the function before we unset the 123861f28255Scgd * variable to allow a function to be unset when there is a readonly variable 123961f28255Scgd * with the same name. 124061f28255Scgd */ 124161f28255Scgd 12424ce0d34aScgd int 1243c02b3bbdSchristos unsetcmd(int argc, char **argv) 12444ce0d34aScgd { 124561f28255Scgd char **ap; 124637ed7877Sjtc int i; 124737ed7877Sjtc int flg_func = 0; 124837ed7877Sjtc int flg_var = 0; 12498df083c1Skre int flg_x = 0; 125037ed7877Sjtc int ret = 0; 125161f28255Scgd 12528df083c1Skre while ((i = nextopt("efvx")) != '\0') { 12538df083c1Skre switch (i) { 12548df083c1Skre case 'f': 125537ed7877Sjtc flg_func = 1; 12568df083c1Skre break; 12578df083c1Skre case 'e': 12588df083c1Skre case 'x': 12598df083c1Skre flg_x = (2 >> (i == 'e')); 12608df083c1Skre /* FALLTHROUGH */ 12618df083c1Skre case 'v': 12628df083c1Skre flg_var = 1; 12638df083c1Skre break; 126461f28255Scgd } 12658df083c1Skre } 12668df083c1Skre 126737ed7877Sjtc if (flg_func == 0 && flg_var == 0) 126837ed7877Sjtc flg_var = 1; 126937ed7877Sjtc 127037ed7877Sjtc for (ap = argptr; *ap ; ap++) { 127137ed7877Sjtc if (flg_func) 127237ed7877Sjtc ret |= unsetfunc(*ap); 127337ed7877Sjtc if (flg_var) 12748df083c1Skre ret |= unsetvar(*ap, flg_x); 127537ed7877Sjtc } 127637ed7877Sjtc return ret; 127761f28255Scgd } 127861f28255Scgd 127961f28255Scgd 128061f28255Scgd /* 128161f28255Scgd * Unset the specified variable. 128261f28255Scgd */ 128361f28255Scgd 1284beb57fb3Schristos int 1285c02b3bbdSchristos unsetvar(const char *s, int unexport) 128661f28255Scgd { 128761f28255Scgd struct var **vpp; 128861f28255Scgd struct var *vp; 128961f28255Scgd 12905f9b9101Sdsl vp = find_var(s, &vpp, NULL); 12915f9b9101Sdsl if (vp == NULL) 12925c83aa64Schristos return 0; 12935f9b9101Sdsl 12948df083c1Skre if (vp->flags & VREADONLY && !(unexport & 1)) 12955c83aa64Schristos return 1; 12965f9b9101Sdsl 129761f28255Scgd INTOFF; 12988df083c1Skre if (unexport & 1) { 1299c02b3bbdSchristos vp->flags &= ~VEXPORT; 1300c02b3bbdSchristos } else { 1301d73cf48cSkre if (vp->text[vp->name_len + 1] != '\0' || !(vp->flags & VUNSET)) 1302d73cf48cSkre setvar(s, nullstr, VUNSET); 13038df083c1Skre if (!(unexport & 2)) 130461f28255Scgd vp->flags &= ~VEXPORT; 130561f28255Scgd vp->flags |= VUNSET; 13068df083c1Skre if ((vp->flags&(VEXPORT|VSTRFIXED|VREADONLY|VNOEXPORT)) == 0) { 130761f28255Scgd if ((vp->flags & VTEXTFIXED) == 0) 130861f28255Scgd ckfree(vp->text); 130961f28255Scgd *vpp = vp->next; 131061f28255Scgd ckfree(vp); 131161f28255Scgd } 1312c02b3bbdSchristos } 131361f28255Scgd INTON; 13145f9b9101Sdsl return 0; 131561f28255Scgd } 131661f28255Scgd 131761f28255Scgd 131861f28255Scgd /* 13196a1a2ce2Skre * Returns true if the two strings specify the same variable. The first 132061f28255Scgd * variable name is terminated by '='; the second may be terminated by 132161f28255Scgd * either '=' or '\0'. 132261f28255Scgd */ 132361f28255Scgd 132461f28255Scgd STATIC int 13255f9b9101Sdsl strequal(const char *p, const char *q) 132661f28255Scgd { 132761f28255Scgd while (*p == *q++) { 132861f28255Scgd if (*p++ == '=') 132961f28255Scgd return 1; 133061f28255Scgd } 133161f28255Scgd if (*p == '=' && *(q - 1) == '\0') 133261f28255Scgd return 1; 133361f28255Scgd return 0; 133461f28255Scgd } 13355f9b9101Sdsl 13365f9b9101Sdsl /* 13375f9b9101Sdsl * Search for a variable. 13385f9b9101Sdsl * 'name' may be terminated by '=' or a NUL. 13395f9b9101Sdsl * vppp is set to the pointer to vp, or the list head if vp isn't found 1340522d1631Skre * lenp is set to the number of characters in 'name' 13415f9b9101Sdsl */ 13425f9b9101Sdsl 13435f9b9101Sdsl STATIC struct var * 13445f9b9101Sdsl find_var(const char *name, struct var ***vppp, int *lenp) 13455f9b9101Sdsl { 13465f9b9101Sdsl unsigned int hashval; 13475f9b9101Sdsl int len; 13485f9b9101Sdsl struct var *vp, **vpp; 13495f9b9101Sdsl const char *p = name; 13505f9b9101Sdsl 13515f9b9101Sdsl hashval = 0; 13525f9b9101Sdsl while (*p && *p != '=') 13535f9b9101Sdsl hashval = 2 * hashval + (unsigned char)*p++; 13545f9b9101Sdsl 1355522d1631Skre len = p - name; 13565f9b9101Sdsl if (lenp) 13575f9b9101Sdsl *lenp = len; 1358522d1631Skre 1359d9eb0136Skre if (len == 0) 1360d9eb0136Skre return NULL; 1361d9eb0136Skre 13625f9b9101Sdsl vpp = &vartab[hashval % VTABSIZE]; 13635f9b9101Sdsl if (vppp) 13645f9b9101Sdsl *vppp = vpp; 13655f9b9101Sdsl 13665f9b9101Sdsl for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { 13675f9b9101Sdsl if (vp->name_len != len) 13685f9b9101Sdsl continue; 13695f9b9101Sdsl if (memcmp(vp->text, name, len) != 0) 13705f9b9101Sdsl continue; 13715f9b9101Sdsl if (vppp) 13725f9b9101Sdsl *vppp = vpp; 13735f9b9101Sdsl return vp; 13745f9b9101Sdsl } 13755f9b9101Sdsl return NULL; 13765f9b9101Sdsl } 1377727a69dcSkre 1378dc833851Skre /* 1379dc833851Skre * The following are the functions that create the values for 1380dc833851Skre * shell variables that are dynamically produced when needed. 1381dc833851Skre * 1382dc833851Skre * The output strings cannot be malloc'd as there is nothing to 1383dc833851Skre * free them - callers assume these are ordinary variables where 1384dc833851Skre * the value returned is vp->text 1385dc833851Skre * 1386dc833851Skre * Each function needs its own storage space, as the results are 1387dc833851Skre * used to create processes' environment, and (if exported) all 1388dc833851Skre * the values will (might) be needed simultaneously. 1389dc833851Skre * 1390dc833851Skre * It is not a problem if a var is updated while nominally in use 1391dc833851Skre * somewhere, all these are intended to be dynamic, the value they 1392dc833851Skre * return is not guaranteed, an updated vaue is just as good. 1393dc833851Skre * 1394dc833851Skre * So, malloc a single buffer for the result of each function, 1395dc833851Skre * grow, and even shrink, it as needed, but once we have one that 1396dc833851Skre * is a suitable size for the actual usage, simply hold it forever. 1397dc833851Skre * 1398dc833851Skre * For a SMALL shell we implement only LINENO, none of the others, 1399dc833851Skre * and give it just a fixed length static buffer for its result. 1400dc833851Skre */ 1401dc833851Skre 1402dc833851Skre #ifndef SMALL 1403dc833851Skre 1404dc833851Skre struct space_reserved { /* record of space allocated for results */ 1405dc833851Skre char *b; 1406dc833851Skre int len; 1407dc833851Skre }; 1408dc833851Skre 1409dc833851Skre /* rough (over-)estimate of the number of bytes needed to hold a number */ 1410dc833851Skre static int 1411dc833851Skre digits_in(intmax_t number) 1412dc833851Skre { 1413dc833851Skre int res = 0; 1414dc833851Skre 1415dc833851Skre if (number & ~((1LL << 62) - 1)) 1416dc833851Skre res = 64; /* enough for 2^200 and a bit more */ 1417dc833851Skre else if (number & ~((1LL << 32) - 1)) 1418dc833851Skre res = 20; /* enough for 2^64 */ 1419dc833851Skre else if (number & ~((1 << 23) - 1)) 1420dc833851Skre res = 10; /* enough for 2^32 */ 1421dc833851Skre else 1422dc833851Skre res = 8; /* enough for 2^23 or smaller */ 1423dc833851Skre 1424dc833851Skre return res; 1425dc833851Skre } 1426dc833851Skre 1427dc833851Skre static int 1428dc833851Skre make_space(struct space_reserved *m, int bytes) 1429dc833851Skre { 1430dc833851Skre void *p; 1431dc833851Skre 1432dc833851Skre if (m->len >= bytes && m->len <= (bytes<<2)) 1433dc833851Skre return 1; 1434dc833851Skre 1435dc833851Skre bytes = SHELL_ALIGN(bytes); 1436e8999de4Skre INTOFF; 1437dc833851Skre /* not ckrealloc() - we want failure, not error() here */ 1438dc833851Skre p = realloc(m->b, bytes); 1439e8999de4Skre if (p != NULL) { 1440dc833851Skre m->b = p; 1441dc833851Skre m->len = bytes; 1442dc833851Skre m->b[bytes - 1] = '\0'; 1443e8999de4Skre } 1444e8999de4Skre INTON; 1445dc833851Skre 1446e8999de4Skre return p != NULL; 1447dc833851Skre } 1448dc833851Skre #endif 1449dc833851Skre 1450727a69dcSkre char * 1451727a69dcSkre get_lineno(struct var *vp) 1452727a69dcSkre { 1453dc833851Skre #ifdef SMALL 1454dc833851Skre #define length (8 + 10) /* 10 digits is enough for a 32 bit line num */ 1455dc833851Skre static char result[length]; 1456dc833851Skre #else 1457dc833851Skre static struct space_reserved buf; 1458dc833851Skre #define result buf.b 1459dc833851Skre #define length buf.len 1460dc833851Skre #endif 1461727a69dcSkre int ln = line_number; 1462727a69dcSkre 1463727a69dcSkre if (vp->flags & VUNSET) 1464727a69dcSkre return NULL; 1465727a69dcSkre 1466727a69dcSkre ln -= funclinebase; 1467dc833851Skre 1468dc833851Skre #ifndef SMALL 1469dc833851Skre if (!make_space(&buf, vp->name_len + 2 + digits_in(ln))) 1470dc833851Skre return vp->text; 1471dc833851Skre #endif 1472dc833851Skre 1473dd761e12Skre snprintf(result, length, "%.*s=%d", vp->name_len, vp->text, ln); 1474dc833851Skre return result; 1475727a69dcSkre } 1476dc833851Skre #undef result 1477dc833851Skre #undef length 1478dc833851Skre 1479dc833851Skre #ifndef SMALL 1480dc833851Skre 1481dc833851Skre char * 1482dc833851Skre get_hostname(struct var *vp) 1483dc833851Skre { 1484dc833851Skre static struct space_reserved buf; 1485dc833851Skre 1486dc833851Skre if (vp->flags & VUNSET) 1487dc833851Skre return NULL; 1488dc833851Skre 1489dc833851Skre if (!make_space(&buf, vp->name_len + 2 + 256)) 1490dc833851Skre return vp->text; 1491dc833851Skre 1492dc833851Skre memcpy(buf.b, vp->text, vp->name_len + 1); /* include '=' */ 1493dc833851Skre (void)gethostname(buf.b + vp->name_len + 1, 1494dc833851Skre buf.len - vp->name_len - 3); 1495dc833851Skre return buf.b; 1496dc833851Skre } 1497dc833851Skre 1498dc833851Skre char * 1499dc833851Skre get_tod(struct var *vp) 1500dc833851Skre { 1501dc833851Skre static struct space_reserved buf; /* space for answers */ 1502dc833851Skre static struct space_reserved tzs; /* remember TZ last used */ 1503dc833851Skre static timezone_t last_zone; /* timezone data for tzs zone */ 1504dc833851Skre const char *fmt; 1505dc833851Skre char *tz; 1506dc833851Skre time_t now; 1507dc833851Skre struct tm tm_now, *tmp; 1508dc833851Skre timezone_t zone = NULL; 1509dc833851Skre static char t_err[] = "time error"; 1510dc833851Skre int len; 1511dc833851Skre 1512dc833851Skre if (vp->flags & VUNSET) 1513dc833851Skre return NULL; 1514dc833851Skre 1515dc833851Skre fmt = lookupvar("ToD_FORMAT"); 1516dc833851Skre if (fmt == NULL) 1517dc833851Skre fmt="%T"; 1518dc833851Skre tz = lookupvar("TZ"); 1519dc833851Skre (void)time(&now); 1520dc833851Skre 1521dc833851Skre if (tz != NULL) { 1522dc833851Skre if (tzs.b == NULL || strcmp(tzs.b, tz) != 0) { 1523dc833851Skre INTOFF; 1524e8999de4Skre if (make_space(&tzs, strlen(tz) + 1)) { 1525dc833851Skre strcpy(tzs.b, tz); 1526dc833851Skre if (last_zone) 1527dc833851Skre tzfree(last_zone); 1528dc833851Skre last_zone = zone = tzalloc(tz); 1529dc833851Skre INTON; 1530dc833851Skre } else 1531dc833851Skre zone = tzalloc(tz); 1532dc833851Skre } else 1533dc833851Skre zone = last_zone; 1534dc833851Skre 1535dc833851Skre tmp = localtime_rz(zone, &now, &tm_now); 1536dc833851Skre } else 1537dc833851Skre tmp = localtime_r(&now, &tm_now); 1538dc833851Skre 1539dc833851Skre len = (strlen(fmt) * 4) + vp->name_len + 2; 1540dc833851Skre while (make_space(&buf, len)) { 1541dc833851Skre memcpy(buf.b, vp->text, vp->name_len+1); 1542dc833851Skre if (tmp == NULL) { 1543dc833851Skre if (buf.len >= vp->name_len+2+(int)(sizeof t_err - 1)) { 1544dc833851Skre strcpy(buf.b + vp->name_len + 1, t_err); 1545e8999de4Skre if (zone && zone != last_zone) { 1546dc833851Skre tzfree(zone); 1547e8999de4Skre INTON; 1548e8999de4Skre } 1549dc833851Skre return buf.b; 1550dc833851Skre } 1551dc833851Skre len = vp->name_len + 4 + sizeof t_err - 1; 1552dc833851Skre continue; 1553dc833851Skre } 1554*ccb9728fSkre if (zone != NULL) { 1555dc833851Skre if (strftime_z(zone, buf.b + vp->name_len + 1, 1556dc833851Skre buf.len - vp->name_len - 2, fmt, tmp)) { 1557*ccb9728fSkre if (zone != last_zone) { 1558dc833851Skre tzfree(zone); 1559e8999de4Skre INTON; 1560e8999de4Skre } 1561dc833851Skre return buf.b; 1562dc833851Skre } 1563*ccb9728fSkre } else if (strftime(buf.b + vp->name_len + 1, 1564*ccb9728fSkre buf.len - vp->name_len - 2, fmt, tmp)) 1565*ccb9728fSkre return buf.b; 1566*ccb9728fSkre 1567dc833851Skre if (len >= 4096) /* Let's be reasonable */ 1568dc833851Skre break; 1569dc833851Skre len <<= 1; 1570dc833851Skre } 1571e8999de4Skre if (zone && zone != last_zone) { 1572dc833851Skre tzfree(zone); 1573e8999de4Skre INTON; 1574e8999de4Skre } 1575dc833851Skre return vp->text; 1576dc833851Skre } 1577dc833851Skre 1578dc833851Skre char * 1579dc833851Skre get_seconds(struct var *vp) 1580dc833851Skre { 1581dc833851Skre static struct space_reserved buf; 1582dc833851Skre intmax_t secs; 1583dc833851Skre 1584dc833851Skre if (vp->flags & VUNSET) 1585dc833851Skre return NULL; 1586dc833851Skre 1587dc833851Skre secs = (intmax_t)time((time_t *)0) - sh_start_time; 1588dc833851Skre if (!make_space(&buf, vp->name_len + 2 + digits_in(secs))) 1589dc833851Skre return vp->text; 1590dc833851Skre 1591dd761e12Skre snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text, secs); 1592dc833851Skre return buf.b; 1593dc833851Skre } 1594dc833851Skre 1595dc833851Skre char * 1596dc833851Skre get_euser(struct var *vp) 1597dc833851Skre { 1598dc833851Skre static struct space_reserved buf; 1599dc833851Skre static uid_t lastuid = 0; 1600dc833851Skre uid_t euid; 1601dc833851Skre struct passwd *pw; 1602dc833851Skre 1603dc833851Skre if (vp->flags & VUNSET) 1604dc833851Skre return NULL; 1605dc833851Skre 1606dc833851Skre euid = geteuid(); 1607dc833851Skre if (buf.b != NULL && lastuid == euid) 1608dc833851Skre return buf.b; 1609dc833851Skre 1610dc833851Skre pw = getpwuid(euid); 1611dc833851Skre if (pw == NULL) 1612dc833851Skre return vp->text; 1613dc833851Skre 1614dc833851Skre if (make_space(&buf, vp->name_len + 2 + strlen(pw->pw_name))) { 1615e8999de4Skre INTOFF; 1616dc833851Skre lastuid = euid; 1617dc833851Skre snprintf(buf.b, buf.len, "%.*s=%s", vp->name_len, vp->text, 1618dc833851Skre pw->pw_name); 1619e8999de4Skre INTON; 1620dc833851Skre return buf.b; 1621dc833851Skre } 1622dc833851Skre 1623dc833851Skre return vp->text; 1624dc833851Skre } 1625dc833851Skre 1626dc833851Skre char * 1627dc833851Skre get_random(struct var *vp) 1628dc833851Skre { 1629dc833851Skre static struct space_reserved buf; 1630dc833851Skre static intmax_t random_val = 0; 1631dc833851Skre 1632dc833851Skre #ifdef USE_LRAND48 1633dc833851Skre #define random lrand48 1634dc833851Skre #define srandom srand48 1635dc833851Skre #endif 1636dc833851Skre 1637dc833851Skre if (vp->flags & VUNSET) 1638dc833851Skre return NULL; 1639dc833851Skre 1640dc833851Skre if (vp->text != buf.b) { 1641dc833851Skre /* 1642dc833851Skre * Either initialisation, or a new seed has been set 1643dc833851Skre */ 1644dc833851Skre if (vp->text[vp->name_len + 1] == '\0') { 1645dc833851Skre int fd; 1646dc833851Skre 1647dc833851Skre /* 1648dc833851Skre * initialisation (without pre-seeding), 1649f1229b53Sandvar * or explicitly requesting a truly random seed. 1650dc833851Skre */ 1651e8999de4Skre INTOFF; 1652dc833851Skre fd = open("/dev/urandom", 0); 1653dc833851Skre if (fd == -1) { 1654dc833851Skre out2str("RANDOM initialisation failed\n"); 1655dc833851Skre random_val = (getpid()<<3) ^ time((time_t *)0); 1656dc833851Skre } else { 1657dc833851Skre int n; 1658dc833851Skre 1659dc833851Skre do { 1660dc833851Skre n = read(fd,&random_val,sizeof random_val); 1661dc833851Skre } while (n != sizeof random_val); 1662dc833851Skre close(fd); 1663dc833851Skre } 1664e8999de4Skre INTON; 1665dc833851Skre } else 1666dc833851Skre /* good enough for today */ 1667c6c29888Skre random_val = strtoimax(vp->text+vp->name_len+1,NULL,0); 1668dc833851Skre 1669dc833851Skre srandom((long)random_val); 1670dc833851Skre } 1671dc833851Skre 1672dc833851Skre #if 0 1673dc833851Skre random_val = (random_val + 1) & 0x7FFF; /* 15 bit "random" numbers */ 1674dc833851Skre #else 1675dc833851Skre random_val = (random() >> 5) & 0x7FFF; 1676dc833851Skre #endif 1677dc833851Skre 1678dc833851Skre if (!make_space(&buf, vp->name_len + 2 + digits_in(random_val))) 1679dc833851Skre return vp->text; 1680dc833851Skre 1681dd761e12Skre snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text, 1682dc833851Skre random_val); 1683dc833851Skre 1684e8999de4Skre INTOFF; 1685dc833851Skre if (buf.b != vp->text && (vp->flags & (VTEXTFIXED|VSTACK)) == 0) 1686dc833851Skre free(vp->text); 1687dc833851Skre vp->flags |= VTEXTFIXED; 1688dc833851Skre vp->text = buf.b; 1689e8999de4Skre INTON; 1690dc833851Skre 1691dc833851Skre return vp->text; 1692dc833851Skre #undef random 1693dc833851Skre #undef srandom 1694dc833851Skre } 1695dc833851Skre 1696727a664bSkre STATIC int 1697727a664bSkre makespecial(const char *name) 1698727a664bSkre { 1699727a664bSkre const struct varinit *ip; 1700727a664bSkre struct var *vp; 1701727a664bSkre 1702727a664bSkre CTRACE(DBG_VARS, ("makespecial('%s') -> ", name)); 1703727a664bSkre for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { 1704727a664bSkre if (strequal(ip->text, name)) { 1705727a664bSkre if (!(ip->flags & VFUNCREF)) { 1706727a664bSkre CTRACE(DBG_VARS, ("+1\n")); 1707727a664bSkre return 1; 1708727a664bSkre } 1709727a664bSkre INTOFF; 1710727a664bSkre vp->flags &= ~VUNSET; 1711727a664bSkre vp->v_u = ip->v_u; 1712727a664bSkre INTON; 1713727a664bSkre CTRACE(DBG_VARS, ("0\n")); 1714727a664bSkre return 0; 1715727a664bSkre } 1716727a664bSkre } 1717727a664bSkre CTRACE(DBG_VARS, ("1\n")); 1718727a664bSkre return 1; 1719727a664bSkre } 1720727a664bSkre 1721727a664bSkre int 1722727a664bSkre specialvarcmd(int argc, char **argv) 1723727a664bSkre { 1724727a664bSkre int res = 0; 1725727a664bSkre char **ap; 1726727a664bSkre 1727727a664bSkre (void) nextopt(""); 1728727a664bSkre 1729727a664bSkre if (!*argptr) 1730727a664bSkre error("Usage: specialvar var..."); 1731727a664bSkre 1732727a664bSkre for (ap = argptr; *ap ; ap++) 1733727a664bSkre res |= makespecial(*ap); 1734727a664bSkre 1735727a664bSkre return res; 1736727a664bSkre } 1737727a664bSkre 1738dc833851Skre #endif /* SMALL */ 1739