/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2012 AT&T Intellectual Property * * and is licensed under the * * Eclipse Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.eclipse.org/org/documents/epl-v10.html * * (with md5 checksum b35adb5213ca9657e911e9befb180842) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * David Korn * * * ***********************************************************************/ #pragma prototyped /* * * Shell initialization * * David Korn * AT&T Labs * */ #include "defs.h" #include #include #include #include #include #include "variables.h" #include "path.h" #include "fault.h" #include "name.h" #include "edit.h" #include "jobs.h" #include "io.h" #include "shlex.h" #include "builtins.h" #include "FEATURE/time" #include "FEATURE/dynamic" #include "FEATURE/externs" #include "lexstates.h" #include "version.h" #ifdef BUILD_DTKSH #include
#include
#include #include #include #include #include #include #include #include #include #include #include #include #include #include "hash.h" #include "stdio.h" #define NO_AST #include "dtksh.h" #undef NO_AST #include "xmksh.h" #include "dtkcmds.h" #include "xmcvt.h" #include "widget.h" #include "extra.h" #include "xmwidgets.h" #include "msgs.h" #include #endif #if _hdr_wctype #include #include #endif #if !_typ_wctrans_t #undef wctrans_t #define wctrans_t sh_wctrans_t typedef long wctrans_t; #endif #if !_lib_wctrans #undef wctrans #define wctrans sh_wctrans static wctrans_t wctrans(const char *name) { if(strcmp(name,e_tolower)==0) return(1); else if(strcmp(name,e_toupper)==0) return(2); return(0); } #endif #if !_lib_towctrans #undef towctrans #define towctrans sh_towctrans static int towctrans(int c, wctrans_t t) { if(t==1 && isupper(c)) c = tolower(c); else if(t==2 && islower(c)) c = toupper(c); return(c); } #endif char e_version[] = "\n@(#)$Id: Version " #if SHOPT_AUDIT #define ATTRS 1 "A" #endif #if SHOPT_BGX #define ATTRS 1 "J" #endif #if SHOPT_ACCT #define ATTRS 1 "L" #endif #if SHOPT_MULTIBYTE #define ATTRS 1 "M" #endif #if SHOPT_PFSH && _hdr_exec_attr #define ATTRS 1 "P" #endif #if SHOPT_REGRESS #define ATTRS 1 "R" #endif #if ATTRS " " #endif SH_RELEASE " $\0\n"; #define RANDMASK 0x7fff #ifndef ARG_MAX # define ARG_MAX (1*1024*1024) #endif #ifndef CHILD_MAX # define CHILD_MAX (1*1024) #endif #ifndef CLK_TCK # define CLK_TCK 60 #endif /* CLK_TCK */ #ifndef environ extern char **environ; #endif #undef getconf #define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0) struct seconds { Namfun_t hdr; Shell_t *sh; }; struct rand { Namfun_t hdr; Shell_t *sh; int32_t rand_last; }; struct ifs { Namfun_t hdr; Namval_t *ifsnp; }; struct match { Namfun_t hdr; const char *v; char *val; char *rval[2]; regoff_t *match; char node[NV_MINSZ+sizeof(char*)]; regoff_t first; int vsize; int nmatch; int index; int lastsub[2]; }; typedef struct _init_ { Shell_t *sh; struct ifs IFS_init; Namfun_t PATH_init; Namfun_t FPATH_init; Namfun_t CDPATH_init; Namfun_t SHELL_init; Namfun_t ENV_init; Namfun_t VISUAL_init; Namfun_t EDITOR_init; Namfun_t HISTFILE_init; Namfun_t HISTSIZE_init; Namfun_t OPTINDEX_init; struct seconds SECONDS_init; struct rand RAND_init; Namfun_t LINENO_init; Namfun_t L_ARG_init; Namfun_t SH_VERSION_init; struct match SH_MATCH_init; Namfun_t SH_MATH_init; #ifdef _hdr_locale Namfun_t LC_TYPE_init; Namfun_t LC_NUM_init; Namfun_t LC_COLL_init; Namfun_t LC_MSG_init; Namfun_t LC_ALL_init; Namfun_t LANG_init; #endif /* _hdr_locale */ } Init_t; static Init_t *ip; static int lctype; static int nbltins; static void env_init(Shell_t*,int); static Init_t *nv_init(Shell_t*); static Dt_t *inittree(Shell_t*,const struct shtable2*); static int shlvl; #ifdef _WINIX # define EXE "?(.exe)" #else # define EXE #endif static int rand_shift; /* * out of memory routine for stak routines */ static char *nospace(int unused) { NOT_USED(unused); errormsg(SH_DICT,ERROR_exit(3),e_nospace); return(NIL(char*)); } /* Trap for VISUAL and EDITOR variables */ static void put_ed(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { register const char *cp, *name=nv_name(np); register int newopt=0; Shell_t *shp = nv_shell(np); if(*name=='E' && nv_getval(sh_scoped(shp,VISINOD))) goto done; if(!(cp=val) && (*name=='E' || !(cp=nv_getval(sh_scoped(shp,EDITNOD))))) goto done; /* turn on vi or emacs option if editor name is either*/ cp = path_basename(cp); if(strmatch(cp,"*[Vv][Ii]*")) newopt=SH_VI; else if(strmatch(cp,"*gmacs*")) newopt=SH_GMACS; else if(strmatch(cp,"*macs*")) newopt=SH_EMACS; if(newopt) { sh_offoption(SH_VI); sh_offoption(SH_EMACS); sh_offoption(SH_GMACS); sh_onoption(newopt); } done: nv_putv(np, val, flags, fp); } /* Trap for HISTFILE and HISTSIZE variables */ static void put_history(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = nv_shell(np); void *histopen = shp->gd->hist_ptr; char *cp; if(val && histopen) { if(np==HISTFILE && (cp=nv_getval(np)) && strcmp(val,cp)==0) return; if(np==HISTSIZE && sh_arith(shp,val)==nv_getnum(HISTSIZE)) return; hist_close(shp->gd->hist_ptr); } nv_putv(np, val, flags, fp); if(histopen) { if(val) sh_histinit(shp); else hist_close(histopen); } } /* Trap for OPTINDEX */ static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = nv_shell(np); shp->st.opterror = shp->st.optchar = 0; nv_putv(np, val, flags, fp); if(!val) nv_disc(np,fp,NV_POP); } static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp) { return((Sfdouble_t)*np->nvalue.lp); } static Namfun_t *clone_optindex(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) { Namfun_t *dp = (Namfun_t*)malloc(sizeof(Namfun_t)); memcpy((void*)dp,(void*)fp,sizeof(Namfun_t)); mp->nvalue.lp = np->nvalue.lp; dp->nofree = 0; return(dp); } /* Trap for restricted variables FPATH, PATH, SHELL, ENV */ static void put_restricted(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = nv_shell(np); int path_scoped = 0, fpath_scoped=0; Pathcomp_t *pp; char *name = nv_name(np); if(!(flags&NV_RDONLY) && sh_isoption(SH_RESTRICTED)) errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); if(np==PATHNOD || (path_scoped=(strcmp(name,PATHNOD->nvname)==0))) { /* Clear the hash table */ nv_scan(shp->track_tree,nv_rehash,(void*)0,NV_TAGGED,NV_TAGGED); if(path_scoped && !val) val = PATHNOD->nvalue.cp; } if(val && !(flags&NV_RDONLY) && np->nvalue.cp && strcmp(val,np->nvalue.cp)==0) return; if(np==FPATHNOD || (fpath_scoped=(strcmp(name,FPATHNOD->nvname)==0))) shp->pathlist = (void*)path_unsetfpath(shp); nv_putv(np, val, flags, fp); shp->universe = 0; if(shp->pathlist) { val = np->nvalue.cp; if(np==PATHNOD || path_scoped) pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH); else if(val && (np==FPATHNOD || fpath_scoped)) pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_FPATH); else return; if(shp->pathlist = (void*)pp) pp->shp = shp; if(!val && (flags&NV_NOSCOPE)) { Namval_t *mp = dtsearch(shp->var_tree,np); if(mp && (val=nv_getval(mp))) nv_putval(mp,val,NV_RDONLY); } } } static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { Pathcomp_t *pp; Shell_t *shp = nv_shell(np); nv_putv(np, val, flags, fp); if(!shp->cdpathlist) return; val = np->nvalue.cp; pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->cdpathlist,val,PATH_CDPATH); if(shp->cdpathlist = (void*)pp) pp->shp = shp; } #ifdef _hdr_locale #ifdef BUILD_DTKSH /* * This version of putenv uses the hash storage to assign environment values * * The original ksh93 code had this routine called "putenv". This hid * the libc version of putenv, which caused problems for dtksh on systems * with shared libraries, as it caused the existence of two separate and * non-overlapping environments. To the best of my knowledge there are * no calls to this routine. - harry phinney 8/15/1994. */ int ksh_putenv(const char *name) { Namval_t *np; if(name) { np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_ARRAY|NV_ASSIGN); if(!strchr(name,'=')) nv_unset(np); } return(0); } void SyncEnv( char *name) { char *value, *buf; value = getenv(name); if(value != (char *)NULL) { buf = malloc(strlen(name) + strlen(value) + 2); strcpy(buf, name); strcat(buf, "="); strcat(buf, value); ksh_putenv(buf); free(buf); /* I hope it's legal to free this! */ } } /* * The following struct is based on an alphanumerically sorted list of * all ksh-generated messages which might be displayed, and * must be looked up in a message catalog. The point of this is to * translate from the AT&T/UI style of string-based message indexing * to the X/OPEN style of message number lookup. The following struct * allows us to do a binary search on the strings to find the associated * set number and message ID. While we could have used the string index * as the message number, that would cause headaches if/when we * need to add more messages, as all following messages would have to * be renumbered. */ typedef struct { char *string; int setNum; int msgNum; } MsgStr, *MsgPtr; static MsgStr allmsgs[] = { {" Done", 25, 1}, {" Running", 25, 3}, {" [-n] [arg...]", 25, 4}, {" [arg...]", 25, 5}, {" [dir] [list]", 25, 6}, {" [job...]", 25, 7}, {" [n]", 25, 8}, {" [name [pathname] ]", 25, 9}, {" [name]", 25, 10}, {" [top] [base]", 25, 11}, {" expr...", 25, 12}, {" format [arg...]", 25, 13}, {" is a function", 25, 14}, {" is a keyword", 25, 15}, {" is a shell builtin", 25, 16}, {" is an exported function", 25, 17}, {" is an undefined function", 25, 18}, {" name [arg...]", 25, 19}, {":a:[name] optstring name [args...]", 25, 20}, {" seconds", 25, 21}, {"${HOME:-.}/.profile", 25, 23}, {"%c: invalid character in expression - %s", 25, 24}, {"%c: unknown format specifier", 25, 25}, {"%d-%d: invalid range", 25, 26}, {"%d: invalid binary script version", 25, 27}, {"%s is an alias for ", 25, 28}, {"%s is an exported alias for ", 25, 29}, {"%s missing", 25, 30}, {"%s unknown base", 25, 31}, {"%s: ':' expected for '?' operator", 25, 32}, {"%s: Ambiguous", 25, 33}, {"%s: Arguments must be %job or process ids", 25, 34}, {"%s: alias not found\n", 25, 35}, {"%s: arithmetic syntax error", 25, 36}, {"%s: assignment requires lvalue", 25, 37}, {"%s: bad file unit number", 25, 38}, {"%s: bad format", 25, 39}, {"%s: bad number", 25, 40}, {"%s: bad option(s)", 25, 41}, {"%s: bad substitution", 25, 42}, {"%s: bad trap", 25, 43}, {"%s: cannot create", 25, 44}, {"%s: cannot execute", 25, 45}, {"%s: cannot open", 25, 46}, {"%s: divide by zero", 25, 47}, {"%s: domain exception", 25, 48}, {"%s: fails %s", 25, 49}, {"%s: file already exists", 25, 50}, {"%s: illegal function name", 25, 51}, {"%s: invalid alias name", 25, 52}, {"%s: invalid discipline function", 25, 53}, {"%s: invalid export name", 25, 54}, {"%s: invalid function name", 25, 55}, {"%s: invalid name", 25, 56}, {"%s: invalid regular expression", 25, 57}, {"%s: invalid self reference", 25, 58}, {"%s: invalid use of :", 25, 59}, {"%s: invalid variable name", 25, 60}, {"%s: is not an identifier", 25, 61}, {"%s: is read only", 25, 62}, {"%s: label not implemented", 25, 63}, {"%s: limit exceeded", 25, 64}, {"%s: more tokens expected", 25, 65}, {"%s: no parent", 25, 66}, {"%s: no reference name", 25, 67}, {"%s: not found", 25, 68}, {"%s: not implemented", 25, 69}, {"%s: operands have incompatible types", 25, 70}, {"%s: overflow exception", 25, 71}, {"%s: parameter not set", 25, 72}, {"%s: parameter null or not set", 25, 73}, {"%s: recursion too deep", 25, 74}, {"%s: reference variable cannot be an array", 25, 75}, {"%s: requires pathname argument", 25, 76}, {"%s: restricted", 25, 77}, {"%s: singularity exception", 25, 78}, {"%s: subscript out of range", 25, 79}, {"%s: unbalanced parenthesis", 25, 80}, {"%s: unknown function", 25, 81}, {"%s: unknown locale", 25, 82}, {"%s: unknown operator", 25, 83}, {"%s: unknown signal name", 25, 84}, {"%s: would cause loop", 25, 85}, {"(coredump)", 25, 86}, {"-c requires argument", 25, 87}, {"-e - requires single argument", 25, 88}, {"/vpix", 25, 89}, {"", 25, 90}, {"AC:E#?F#?H:[name]L#?R#?Z#?fi#?[base]lnprtux [name=[value]...]", 25, 91}, {"AE#?F#?HL#?R#?Z#?fi#?[base]lnprtux [name=[value]...]", 25, 92}, {"Abort", 25, 93}, {"Ad:[delim]prst#[timeout]u#[filenum] [name...]", 25, 94}, {"Alarm call", 25, 95}, {"Bad root node specification", 25, 96}, {"Bad system call", 25, 97}, {"Broken Pipe", 25, 98}, {"Bus error", 25, 99}, {"Cannot start job control", 25, 100}, {"Current option settings", 25, 101}, {"DIL signal", 25, 102}, {"Death of Child", 25, 103}, {"DircabefhkmnpstuvxCR:[file]o:?[option] [arg...]", 25, 104}, {"DircabefhkmnpstuvxCo:?[option] [arg...]", 25, 105}, {"EMT trap", 25, 106}, {"Exceeded CPU time limit", 25, 107}, {"Exceeded file size limit", 25, 108}, {"Floating exception", 25, 109}, {"HSacdfmnstv [limit]", 25, 110}, {"Hangup", 25, 111}, {"IO signal", 25, 112}, {"Illegal instruction", 25, 113}, {"Interrupt", 25, 114}, {"Killed", 25, 115}, {"LP [dir] [change]", 25, 116}, {"Memory fault", 25, 117}, {"Migrate process", 25, 118}, {"No job control", 25, 119}, {"Phone interrupt", 25, 120}, {"Polling alarm", 25, 121}, {"Power fail", 25, 122}, {"Profiling time alarm", 25, 123}, {"Quit", 25, 124}, {"Resources lost", 25, 125}, {"Reverting to old tty driver...", 25, 126}, {"S [mask]", 25, 127}, {"Security label changed", 25, 129}, {"Socket interrupt", 25, 130}, {"Sound completed", 25, 131}, {"Stopped (signal)", 25, 132}, {"Stopped (tty input)", 25, 133}, {"Stopped process continued", 25, 134}, {"Stopped", 25, 135}, {"Stopped(tty output)", 25, 136}, {"Switching to new tty driver...", 25, 137}, {"System crash soon", 25, 138}, {"Terminated", 25, 139}, {"Trace/BPT trap", 25, 140}, {"Unrecognized version", 25, 141}, {"Use 'exit' to terminate this shell", 25, 142}, {"User signal 1", 25, 143}, {"User signal 2", 25, 144}, {"Version not defined", 25, 145}, {"Virtual time alarm", 25, 146}, {"Window size change", 25, 147}, {"You have running jobs", 25, 148}, {"You have stopped jobs", 25, 149}, {"[_[:alpha:]]*([_[:alnum:]])", 25, 150}, {"\n@(#)Version 12/28/93\0\n", 25, 151}, {"\n@(#)Version M-12/28/93\0\n", 25, 152}, {"\nreal", 25, 153}, {"\r\n\007shell will timeout in 60 seconds due to inactivity", 25, 154}, {"a name...", 25, 155}, {"a:c [command [args...] ]", 25, 156}, {"afpv name...", 25, 157}, {"alarm %s %.3f\n", 25, 158}, {"alarm -r %s +%.3g\n", 25, 159}, {"argument expected", 25, 160}, {"bad directory", 25, 161}, {"bad file unit number", 25, 162}, {"bad substitution", 25, 163}, {"cannot access parent directories", 25, 164}, {"cannot create pipe", 25, 165}, {"cannot create tempory file", 25, 166}, {"cannot fork", 25, 167}, {"cannot get %s", 25, 168}, {"cannot set %s", 25, 169}, {"cannot set alarm", 25, 170}, {"condition(s) required", 25, 171}, {"dsf:[library] [name...]", 25, 172}, {"e:[editor]lnrsN# [first] [last]", 25, 173}, {"end of file", 25, 174}, {"f:[format]enprsu:[filenum] [arg...]", 25, 175}, {"fnv name...", 25, 176}, {"hist -e \"${VISUAL:-${EDITOR:-vi}}\" ", 25, 177}, {"history file cannot open", 25, 178}, {"incorrect syntax", 25, 179}, {"invalid argument of type %c", 25, 180}, {"is a shell builtin version of", 25, 181}, {"is a tracked alias for", 25, 182}, {"kill", 25, 183}, {"line %d: $ not preceded by \\", 25, 184}, {"line %d: %c within ${} should be quoted", 25, 185}, {"line %d: %s unknown label", 25, 186}, {"line %d: %s within [[...]] obsolete, use ((...))", 25, 187}, {"line %d: '=' obsolete, use '=='", 25, 188}, {"line %d: -a obsolete, use -e", 25, 189}, {"line %d: \\ in front of %c reserved for future use", 25, 190}, {"line %d: `...` obsolete, use $(...)", 25, 191}, {"line %d: escape %c to avoid ambiguities", 25, 192}, {"line %d: label %s ignored", 25, 193}, {"line %d: quote %c to avoid ambiguities", 25, 194}, {"line %d: set %s obsolete", 25, 195}, {"line %d: spaces required for nested subshell", 25, 196}, {"line %d: use braces to avoid ambiguities with $id[...]", 25, 197}, {"line %d: use space or tab to separate operators %c and %c", 25, 198}, {"ln#[signum]s:[signame] sig...", 25, 199}, {"login setuid/setgid shells prohibited", 25, 200}, {"mapping", 25, 201}, {"newline", 25, 202}, {"nlp [job...]", 25, 203}, {"no history file", 25, 204}, {"no query process", 25, 205}, {"no such job", 25, 206}, {"no such process", 25, 207}, {"not supported", 25, 208}, {"off", 25, 209}, {"on", 25, 210}, {"open file limit exceeded", 25, 211}, {"out of memory", 25, 212}, {"p [action condition...]", 25, 213}, {"p [name[=value]...]", 25, 214}, {"parameter not set", 25, 215}, {"permission denied", 25, 216}, {"process already exists", 25, 217}, {"ptx [name=[value]...]", 25, 218}, {"pvV name [arg]...", 25, 219}, {"r [varname seconds]", 25, 220}, {"syntax error at line %d: `%s' %s", 25, 221}, {"syntax error at line %d: duplicate label %s", 25, 222}, {"syntax error: `%s' %s", 25, 223}, {"sys", 25, 224}, {"timed out waiting for input", 25, 225}, {"unexpected", 25, 226}, {"universe not accessible", 25, 227}, {"unlimited", 25, 228}, {"unmatched", 25, 229}, {"user", 25, 230}, {"versions", 25, 231}, {"write to %d failed", 25, 232}, {"you have mail in $_", 25, 233}, {"zero byte", 25, 234}, }; #define _CLIENT_CAT_NAME "dtksh" /* * Without this proto, standard C says that _DtGetMessage() returns * an int, even though it really returns a pointer. The compiler is * then free to use the high 32-bits of the return for * something else (like scratch), and that can garble the pointer. */ char *_DtGetMessage(char *filename, int set, int n, char *s); #define GETMESSAGE(set, number, string)\ (_DtGetMessage(_CLIENT_CAT_NAME, set, number, string)) static int localeChanged = 1; void LocaleChanged(Namval_t *np, const char *val, int flags, Namfun_t *fp) { localeChanged = 1; nv_putv(np, val, flags, fp); setlocale(LC_ALL, ""); } static Namdisc_t localeDisc = { 0, LocaleChanged, NULL, NULL, NULL, NULL, NULL, NULL }; static Namfun_t localeFun = {NULL, NULL }; /**************************************************************************** * * The following two functions are ugly, but necessary. Ksh reserves file * descriptors 0 - 9 for use by shell scripts, and has intimate knowledge * of how and when they were opened. Unfortunately, certain dtksh functions * (XtInitialize, catopen, ttdt_open, _DtActionInvoke, others) open file * descriptors which are not known to ksh. We can't let these file * descriptors fall in the 0 - 9 range, because we can't afford to have * the shell script overriding our file descriptors. Therefore, any of * our commands which open files must first lock our file descriptors 0 - 9, * thus forcing the command to get a file descriptor out of the shell's * range. After the command has opened its file descriptor, it then needs * to unlock file descriptors 0 - 9, so that the shell script will have * access to them again. * **************************************************************************/ /* * Return a list of the file descriptors we had to open, to lock out file * descriptors 0 - 9; this list should be freed (and the file descriptors * closed) by calling UnlockkshFileDescriptors(). */ int *LockKshFileDescriptors(void) { int * fdList; int i; int fd, newfd; fdList = (int *)malloc(sizeof(int) * 10); for (i = 0; i < 10; i++) fdList[i] = -1; if ((fd = open("/dev/null", O_RDONLY)) >= 0) { if (fd < 10) { fdList[0] = fd; for (i = 1; i < 10; i++) { if ((newfd = dup(fd)) < 10) fdList[i] = newfd; else { close(newfd); break; } } } else close(fd); } return(fdList); } void UnlockKshFileDescriptors(int *fdList) { int i; for (i = 0; i < 10; i++) { if (fdList[i] != (-1)) close(fdList[i]); } free((char *)fdList); } /* * This function overrides the traditional libDtSvc version of this function. * Since ksh dynamically changes to match the user's locale, we potentially * need to change our message catalog in the middle of running. */ char *savedNlsPath; char *_DtGetMessage(char *filename, int set, int n, char *s) { nl_catd catopen(); char *catgets(); int catclose(); static nl_catd nlmsg_fd = (nl_catd)-1; static char *lang = NULL; char *msg; char * newLang; int * lockedFds; int swappedNlsPath = 0; char *pEqual; static char pathBuf[1024] = {(char)'N', (char)'L', (char)'S', (char)'P', (char)'A', (char)'T', (char)'H', (char)'='}; if ( localeChanged ) { char *oldPath; localeChanged = 0; newLang = (char *) getenv ("LANG"); if (lang) free(lang); if (newLang) lang = strdup(newLang); else lang = NULL; if (nlmsg_fd != (nl_catd)-1) catclose(nlmsg_fd); if(strcmp((oldPath = getenv("NLSPATH")), savedNlsPath) != 0) { swappedNlsPath = 1; pEqual = strchr(pathBuf, '='); strcpy(pEqual + 1, savedNlsPath); /* * Only call putenv if pathBuf isn't already holding NLSPATH * in the environment. */ if(oldPath != (pEqual + 1)) putenv(pathBuf); } lockedFds = LockKshFileDescriptors(); nlmsg_fd = catopen(filename, 0); UnlockKshFileDescriptors(lockedFds); if(swappedNlsPath != 0) { swappedNlsPath = 0; pEqual = strchr(pathBuf, '='); strcpy(pEqual + 1, oldPath); if(oldPath != (pEqual + 1)) putenv(pathBuf); } } msg=catgets(nlmsg_fd,set,n,s); return (msg); } /* * This function needs to be modified to handle international * error message translations */ static char *msg_translate(const char *message,int type) { int startIndex = 0, endIndex = sizeof(allmsgs)/sizeof(MsgStr) - 1, midIndex, res, weFoundIt = 0; if(type != 1) /* if it's not a shell message, don't translate */ return((char*)message); while(startIndex != endIndex) { midIndex = (startIndex + endIndex) / 2; if(midIndex == startIndex) { if((res = strcmp(allmsgs[startIndex].string, message)) == 0) { weFoundIt = 1; midIndex = startIndex; break; } else if(res < 0) { if((res = strcmp(allmsgs[endIndex].string, message)) == 0) { weFoundIt = 1; midIndex = endIndex; break; } } /* we didn't find a match in the table */ weFoundIt = 0; break; } else { if((res = strcmp(allmsgs[midIndex].string, message)) == 0) { weFoundIt = 1; break; /* we found it */ } else if(res < 0) startIndex = midIndex; else endIndex = midIndex; } } if(weFoundIt) return GETMESSAGE(allmsgs[midIndex].setNum, allmsgs[midIndex].msgNum, (char *)message); return((char*)message); } #else static char* msg_translate(const char* catalog, const char* message) { NOT_USED(catalog); return((char*)message); } #endif /* Trap for LC_ALL, LC_CTYPE, LC_MESSAGES, LC_COLLATE and LANG */ static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = nv_shell(np); int type; char *name = nv_name(np); if(name==(LCALLNOD)->nvname) type = LC_ALL; else if(name==(LCTYPENOD)->nvname) type = LC_CTYPE; else if(name==(LCMSGNOD)->nvname) type = LC_MESSAGES; else if(name==(LCCOLLNOD)->nvname) type = LC_COLLATE; else if(name==(LCNUMNOD)->nvname) type = LC_NUMERIC; #ifdef LC_LANG else if(name==(LANGNOD)->nvname) type = LC_LANG; #else #define LC_LANG LC_ALL else if(name==(LANGNOD)->nvname && (!(name=nv_getval(LCALLNOD)) || !*name)) type = LC_LANG; #endif else type= -1; if(!sh_isstate(SH_INIT) && (type>=0 || type==LC_ALL || type==LC_LANG)) { char* r; #ifdef AST_LC_setenv ast.locale.set |= AST_LC_setenv; #endif r = setlocale(type,val?val:""); #ifdef AST_LC_setenv ast.locale.set ^= AST_LC_setenv; #endif if(!r && val) { if(!sh_isstate(SH_INIT) || shp->login_sh==0) errormsg(SH_DICT,0,e_badlocale,val); return; } } nv_putv(np, val, flags, fp); if(CC_NATIVE!=CC_ASCII && (type==LC_ALL || type==LC_LANG || type==LC_CTYPE)) { if(sh_lexstates[ST_BEGIN]!=sh_lexrstates[ST_BEGIN]) free((void*)sh_lexstates[ST_BEGIN]); lctype++; if(ast.locale.set&(1<ifsnp = 0; if(!val) { fp = nv_stack(np, NIL(Namfun_t*)); if(fp && !fp->nofree) { free((void*)fp); fp = 0; } } if(val != np->nvalue.cp) nv_putv(np, val, flags, fp); if(!val) { if(fp) fp->next = np->nvfun; np->nvfun = fp; } } /* * This is the lookup function for IFS * It keeps the sh.ifstable up to date */ static char* get_ifs(register Namval_t* np, Namfun_t *fp) { register struct ifs *ip = (struct ifs*)fp; register char *cp, *value; register int c,n; register Shell_t *shp = nv_shell(np); value = nv_getv(np,fp); if(np!=ip->ifsnp) { ip->ifsnp = np; memset(shp->ifstable,0,(1<1) { cp += (n-1); shp->ifstable[c] = S_MBYTE; continue; } #endif /* SHOPT_MULTIBYTE */ n = S_DELIM; if(c== *cp) cp++; else if(c=='\n') n = S_NL; else if(isspace(c)) n = S_SPACE; shp->ifstable[c] = n; } } else { shp->ifstable[' '] = shp->ifstable['\t'] = S_SPACE; shp->ifstable['\n'] = S_NL; } } return(value); } /* * these functions are used to get and set the SECONDS variable */ #ifdef timeofday # define dtime(tp) ((double)((tp)->tv_sec)+1e-6*((double)((tp)->tv_usec))) # define tms timeval #else # define dtime(tp) (((double)times(tp))/shgd->lim.clk_tck) # define timeofday(a) #endif static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { double d; struct tms tp; if(!val) { nv_putv(np, val, flags, fp); fp = nv_stack(np, NIL(Namfun_t*)); if(fp && !fp->nofree) free((void*)fp); return; } if(!np->nvalue.dp) { nv_setsize(np,3); nv_onattr(np,NV_DOUBLE); np->nvalue.dp = new_of(double,0); } nv_putv(np, val, flags, fp); d = *np->nvalue.dp; timeofday(&tp); *np->nvalue.dp = dtime(&tp)-d; } static char* get_seconds(register Namval_t* np, Namfun_t *fp) { Shell_t *shp = nv_shell(np); register int places = nv_size(np); struct tms tp; double d, offset = (np->nvalue.dp?*np->nvalue.dp:0); NOT_USED(fp); timeofday(&tp); d = dtime(&tp)- offset; sfprintf(shp->strbuf,"%.*f",places,d); return(sfstruse(shp->strbuf)); } static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp) { struct tms tp; double offset = (np->nvalue.dp?*np->nvalue.dp:0); NOT_USED(fp); timeofday(&tp); return(dtime(&tp)- offset); } /* * These three functions are used to get and set the RANDOM variable */ static void put_rand(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { struct rand *rp = (struct rand*)fp; register long n; if(!val) { fp = nv_stack(np, NIL(Namfun_t*)); if(fp && !fp->nofree) free((void*)fp); _nv_unset(np,NV_RDONLY); return; } if(flags&NV_INTEGER) n = *(double*)val; else n = sh_arith(rp->sh,val); srand((int)(n&RANDMASK)); rp->rand_last = -1; if(!np->nvalue.lp) np->nvalue.lp = &rp->rand_last; } /* * get random number in range of 0 - 2**15 * never pick same number twice in a row */ static Sfdouble_t nget_rand(register Namval_t* np, Namfun_t *fp) { register long cur, last= *np->nvalue.lp; NOT_USED(fp); do cur = (rand()>>rand_shift)&RANDMASK; while(cur==last); *np->nvalue.lp = cur; return((Sfdouble_t)cur); } static char* get_rand(register Namval_t* np, Namfun_t *fp) { register long n = nget_rand(np,fp); return(fmtbase(n, 10, 0)); } /* * These three routines are for LINENO */ static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp) { double d=1; if(error_info.line >0) d = error_info.line; else if(error_info.context && error_info.context->line>0) d = error_info.context->line; NOT_USED(np); NOT_USED(fp); return(d); } static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp) { register long n; Shell_t *shp = nv_shell(np); if(!val) { fp = nv_stack(np, NIL(Namfun_t*)); if(fp && !fp->nofree) free((void*)fp); _nv_unset(np,NV_RDONLY); return; } if(flags&NV_INTEGER) n = *(double*)val; else n = sh_arith(shp,val); shp->st.firstline += nget_lineno(np,fp)+1-n; } static char* get_lineno(register Namval_t* np, Namfun_t *fp) { register long n = nget_lineno(np,fp); return(fmtbase(n, 10, 0)); } static char* get_lastarg(Namval_t* np, Namfun_t *fp) { Shell_t *shp = nv_shell(np); char *cp; int pid; if(sh_isstate(SH_INIT) && (cp=shp->lastarg) && *cp=='*' && (pid=strtol(cp+1,&cp,10)) && *cp=='*') nv_putval(np,cp+1,0); return(shp->lastarg); } static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = nv_shell(np); if(flags&NV_INTEGER) { sfprintf(shp->strbuf,"%.*g",12,*((double*)val)); val = sfstruse(shp->strbuf); } if(val) val = strdup(val); if(shp->lastarg && !nv_isattr(np,NV_NOFREE)) free((void*)shp->lastarg); else nv_offattr(np,NV_NOFREE); shp->lastarg = (char*)val; nv_offattr(np,NV_EXPORT); np->nvenv = 0; } static int hasgetdisc(register Namfun_t *fp) { while(fp && !fp->disc->getnum && !fp->disc->getval) fp = fp->next; return(fp!=0); } /* * store the most recent value for use in .sh.match * treat .sh.match as a two dimensional array */ void sh_setmatch(Shell_t *shp,const char *v, int vsize, int nmatch, regoff_t match[],int index) { struct match *mp = &ip->SH_MATCH_init; Namval_t *np = nv_namptr(mp->node,0); register int i,n,x; unsigned int savesub = shp->subshell; Namarr_t *ap = nv_arrayptr(SH_MATCHNOD); shp->subshell = 0; #ifndef SHOPT_2DMATCH index = 0; #else if(index==0) #endif /* SHOPT_2DMATCH */ { if(ap->hdr.next != &mp->hdr) { free((void*)ap); ap = nv_arrayptr(np); SH_MATCHNOD->nvfun = &ap->hdr; } if(ap) { ap->nelem &= ~ARRAY_SCAN; i = array_elem(ap); ap->nelem++; while(--i>= 0) { nv_putsub(SH_MATCHNOD, (char*)0,i); _nv_unset(SH_MATCHNOD,NV_RDONLY); } ap->nelem--; } if(!nv_hasdisc(SH_MATCHNOD,mp->hdr.disc)) nv_disc(SH_MATCHNOD,&mp->hdr,NV_LAST); if(nmatch) nv_putsub(SH_MATCHNOD, NIL(char*), (nmatch-1)|ARRAY_FILL|ARRAY_SETSUB); ap = nv_arrayptr(SH_MATCHNOD); ap->nelem = mp->nmatch = nmatch; mp->v = v; mp->first = match[0]; } #ifdef SHOPT_2DMATCH else { if(index==1) { np->nvalue.cp = Empty; np->nvfun = SH_MATCHNOD->nvfun; nv_onattr(np,NV_NOFREE|NV_ARRAY); SH_MATCHNOD->nvfun = 0; for(i=0; i < mp->nmatch; i++) { nv_putsub(SH_MATCHNOD, (char*)0, i); nv_arraychild(SH_MATCHNOD, np,0); } if(ap = nv_arrayptr(SH_MATCHNOD)) ap->nelem = mp->nmatch; } ap = nv_arrayptr(np); nv_putsub(np, NIL(char*), index|ARRAY_FILL|ARRAY_SETSUB); } #endif /* SHOPT_2DMATCH */ shp->subshell = savesub; index *= 2*mp->nmatch; if(mp->nmatch) { for(n=mp->first+(mp->v-v),vsize=0,i=0; i < 2*nmatch; i++) { if(match[i]>=0 && (match[i] - n) > vsize) vsize = match[i] -n; } i = (index+2*mp->nmatch)*sizeof(match[0]); if((i+vsize) >= mp->vsize) { if(mp->vsize) mp->match = (int*)realloc(mp->match,i+vsize+1); else mp->match = (int*)malloc(i+vsize+1); mp->vsize = i+vsize+1; } mp->val = ((char*)mp->match)+i; memcpy(mp->match+index,match,nmatch*2*sizeof(match[0])); for(x=0,i=0; i < 2*nmatch; i++) { if(match[i]>=0) mp->match[index+i] -= n; else x=1; } ap->nelem -= x; while(i < 2*mp->nmatch) mp->match[index+i++] = -1; memcpy(mp->val,v+n,vsize); mp->val[vsize] = 0; mp->lastsub[0] = mp->lastsub[1] = -1; } } #define array_scan(np) ((nv_arrayptr(np)->nelem&ARRAY_SCAN)) static char* get_match(register Namval_t* np, Namfun_t *fp) { struct match *mp = (struct match*)fp; int sub,sub2=0,n,i =!mp->index; char *val; sub = nv_aindex(SH_MATCHNOD); if(np!=SH_MATCHNOD) sub2 = nv_aindex(np); if(sub>=mp->nmatch) return(0); if(sub2>0) sub += sub2*mp->nmatch; if(sub==mp->lastsub[!i]) return(mp->rval[!i]); else if(sub==mp->lastsub[i]) return(mp->rval[i]); n = mp->match[2*sub+1]-mp->match[2*sub]; if(n<=0) return(mp->match[2*sub]<0?Empty:""); val = mp->val+mp->match[2*sub]; if(mp->val[mp->match[2*sub+1]]==0) return(val); mp->index = i; if(mp->rval[i]) { free((void*)mp->rval[i]); mp->rval[i] = 0; } mp->rval[i] = (char*)malloc(n+1); mp->lastsub[i] = sub; memcpy(mp->rval[i],val,n); mp->rval[i][n] = 0; return(mp->rval[i]); } static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match }; static char* get_version(register Namval_t* np, Namfun_t *fp) { return(nv_getv(np,fp)); } static Sfdouble_t nget_version(register Namval_t* np, Namfun_t *fp) { register const char *cp = e_version + strlen(e_version)-10; register int c; Sflong_t t = 0; NOT_USED(fp); while (c = *cp++) if (c >= '0' && c <= '9') { t *= 10; t += c - '0'; } return((Sfdouble_t)t); } static const Namdisc_t SH_VERSION_disc = { 0, 0, get_version, nget_version }; static const Namdisc_t IFS_disc = { sizeof(struct ifs), put_ifs, get_ifs }; const Namdisc_t RESTRICTED_disc = { sizeof(Namfun_t), put_restricted }; static const Namdisc_t CDPATH_disc = { sizeof(Namfun_t), put_cdpath }; static const Namdisc_t EDITOR_disc = { sizeof(Namfun_t), put_ed }; static const Namdisc_t HISTFILE_disc = { sizeof(Namfun_t), put_history }; static const Namdisc_t OPTINDEX_disc = { sizeof(Namfun_t), put_optindex, 0, nget_optindex, 0, 0, clone_optindex }; static const Namdisc_t SECONDS_disc = { sizeof(struct seconds), put_seconds, get_seconds, nget_seconds }; static const Namdisc_t RAND_disc = { sizeof(struct rand), put_rand, get_rand, nget_rand }; static const Namdisc_t LINENO_disc = { sizeof(Namfun_t), put_lineno, get_lineno, nget_lineno }; static const Namdisc_t L_ARG_disc = { sizeof(Namfun_t), put_lastarg, get_lastarg }; #define MAX_MATH_ARGS 3 static char *name_math(Namval_t *np, Namfun_t *fp) { Shell_t *shp = sh_getinterp(); sfprintf(shp->strbuf,".sh.math.%s",np->nvname); return(sfstruse(shp->strbuf)); } static const Namdisc_t math_child_disc = { 0,0,0,0,0,0,0, name_math }; static Namfun_t math_child_fun = { &math_child_disc, 1, 0, sizeof(Namfun_t) }; static void math_init(Shell_t *shp) { Namval_t *np; char *name; int i; shp->mathnodes = (char*)calloc(1,MAX_MATH_ARGS*(NV_MINSZ+5)); name = shp->mathnodes+MAX_MATH_ARGS*NV_MINSZ; for(i=0; i < MAX_MATH_ARGS; i++) { np = nv_namptr(shp->mathnodes,i); np->nvfun = &math_child_fun; memcpy(name,"arg",3); name[3] = '1'+i; np->nvname = name; name+=5; nv_onattr(np,NV_MINIMAL|NV_NOFREE|NV_LDOUBLE|NV_RDONLY); } } static Namval_t *create_math(Namval_t *np,const char *name,int flag,Namfun_t *fp) { Shell_t *shp = nv_shell(np); if(!name) return(SH_MATHNOD); if(name[0]!='a' || name[1]!='r' || name[2]!='g' || name[4] || !isdigit(name[3]) || (name[3]=='0' || (name[3]-'0')>MAX_MATH_ARGS)) return(0); fp->last = (char*)&name[4]; return(nv_namptr(shp->mathnodes,name[3]-'1')); } static char* get_math(register Namval_t* np, Namfun_t *fp) { Shell_t *shp = nv_shell(np); Namval_t *mp,fake; char *val; int first=0; fake.nvname = ".sh.math."; mp = (Namval_t*)dtprev(shp->fun_tree,&fake); while(mp=(Namval_t*)dtnext(shp->fun_tree,mp)) { if(memcmp(mp->nvname,".sh.math.",9)) break; if(first++) sfputc(shp->strbuf,' '); sfputr(shp->strbuf,mp->nvname+9,-1); } val = sfstruse(shp->strbuf); return(val); } static char *setdisc_any(Namval_t *np, const char *event, Namval_t *action, Namfun_t *fp) { Shell_t *shp=nv_shell(np); Namval_t *mp,fake; char *name; int getname=0, off=staktell(); fake.nvname = nv_name(np); if(!event) { if(!action) { mp = (Namval_t*)dtprev(shp->fun_tree,&fake); return((char*)dtnext(shp->fun_tree,mp)); } getname = 1; } stakputs(fake.nvname); stakputc('.'); stakputs(event); stakputc(0); name = stakptr(off); mp = nv_search(name, shp->fun_tree, action?NV_ADD:0); stakseek(off); if(getname) return(mp?(char*)dtnext(shp->fun_tree,mp):0); if(action==np) action = mp; return(action?(char*)action:""); } static const Namdisc_t SH_MATH_disc = { 0, 0, get_math, 0, setdisc_any, create_math, }; #if SHOPT_NAMESPACE static char* get_nspace(Namval_t* np, Namfun_t *fp) { if(sh.namespace) return(nv_name(sh.namespace)); return((char*)np->nvalue.cp); } static const Namdisc_t NSPACE_disc = { 0, 0, get_nspace }; static Namfun_t NSPACE_init = { &NSPACE_disc, 1}; #endif /* SHOPT_NAMESPACE */ #ifdef _hdr_locale static const Namdisc_t LC_disc = { sizeof(Namfun_t), put_lang }; #endif /* _hdr_locale */ /* * This function will get called whenever a configuration parameter changes */ static int newconf(const char *name, const char *path, const char *value) { Shell_t *shp = sh_getinterp(); register char *arg; if(!name) setenviron(value); else if(strcmp(name,"UNIVERSE")==0 && strcmp(astconf(name,0,0),value)) { shp->universe = 0; /* set directory in new universe */ if(*(arg = path_pwd(shp,0))=='/') chdir(arg); /* clear out old tracked alias */ stakseek(0); stakputs(nv_getval(PATHNOD)); stakputc(0); nv_putval(PATHNOD,stakseek(0),NV_RDONLY); } return(1); } #if (CC_NATIVE != CC_ASCII) static void a2e(char *d, const char *s) { register const unsigned char *t; register int i; t = CCMAP(CC_ASCII, CC_NATIVE); for(i=0; i<(1<current_pid = shgd->pid = getpid(); shgd->ppid = getppid(); shgd->userid=getuid(); shgd->euserid=geteuid(); shgd->groupid=getgid(); shgd->egroupid=getegid(); shgd->lim.clk_tck = getconf("CLK_TCK"); shgd->lim.arg_max = getconf("ARG_MAX"); shgd->lim.child_max = getconf("CHILD_MAX"); shgd->lim.ngroups_max = getconf("NGROUPS_MAX"); shgd->lim.posix_version = getconf("VERSION"); shgd->lim.posix_jobcontrol = getconf("JOB_CONTROL"); if(shgd->lim.arg_max <=0) shgd->lim.arg_max = ARG_MAX; if(shgd->lim.child_max <=0) shgd->lim.child_max = CHILD_MAX; if(shgd->lim.clk_tck <=0) shgd->lim.clk_tck = CLK_TCK; shgd->ed_context = (void*)ed_open(shp); error_info.exit = sh_exit; error_info.id = path_basename(argv[0]); } else shp = newof(0,Shell_t,1,0); umask(shp->mask=umask(0)); shp->gd = shgd; shp->mac_context = sh_macopen(shp); shp->arg_context = sh_argopen(shp); shp->lex_context = (void*)sh_lexopen(0,shp,1); shp->strbuf = sfstropen(); shp->stk = stkstd; sfsetbuf(shp->strbuf,(char*)0,64); sh_onstate(SH_INIT); error_info.catalog = e_dict; #if SHOPT_REGRESS { Opt_t* nopt; Opt_t* oopt; char* a; char** av = argv; char* regress[3]; regress[0] = "__regress__"; regress[2] = 0; /* NOTE: only shp is used by __regress__ at this point */ shp->bltindata.shp = shp; while ((a = *++av) && a[0] == '-' && (a[1] == 'I' || a[1] == '-' && a[2] == 'r')) { if (a[1] == 'I') { if (a[2]) regress[1] = a + 2; else if (!(regress[1] = *++av)) break; } else if (strncmp(a+2, "regress", 7)) break; else if (a[9] == '=') regress[1] = a + 10; else if (!(regress[1] = *++av)) break; nopt = optctx(0, 0); oopt = optctx(nopt, 0); error_info.exit = exit; /* avoid crash on b___regress__ error as shell is not fully initialized */ b___regress__(2, regress, &shp->bltindata); error_info.exit = sh_exit; optctx(oopt, nopt); } } #endif shp->cpipe[0] = -1; shp->coutpipe = -1; for(n=0;n < 10; n++) { /* don't use lower bits when rand() generates large numbers */ if(rand() > RANDMASK) { rand_shift = 3; break; } } sh_ioinit(shp); /* initialize signal handling */ sh_siginit(shp); stakinstall(NIL(Stak_t*),nospace); /* set up memory for name-value pairs */ shp->init_context = nv_init(shp); /* initialize shell type */ if(argc>0) { type = sh_type(*argv); if(type&SH_TYPE_LOGIN) shp->login_sh = 2; if(type&SH_TYPE_POSIX) sh_onoption(SH_POSIX); } /* read the environment; don't import attributes yet */ env_init(shp,0); if(!ENVNOD->nvalue.cp) { sfprintf(shp->strbuf,"%s/.kshrc",nv_getval(HOME)); nv_putval(ENVNOD,sfstruse(shp->strbuf),NV_RDONLY); } *SHLVL->nvalue.ip +=1; nv_offattr(SHLVL,NV_IMPORT); #if SHOPT_SPAWN { /* * try to find the pathname for this interpreter * try using environment variable _ or argv[0] */ char *cp=nv_getval(L_ARGNOD); char buff[PATH_MAX+1]; shp->gd->shpath = 0; if((n = pathprog(NiL, buff, sizeof(buff))) > 0 && n <= sizeof(buff)) shp->gd->shpath = strdup(buff); else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/'))) { if(*cp=='/') shp->gd->shpath = strdup(cp); else if(cp = nv_getval(PWDNOD)) { int offset = staktell(); stakputs(cp); stakputc('/'); stakputs(argv[0]); pathcanon(stakptr(offset),PATH_DOTDOT); shp->gd->shpath = strdup(stakptr(offset)); stakseek(offset); } } } #endif nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY); astconfdisc(newconf); #if SHOPT_TIMEOUT shp->st.tmout = SHOPT_TIMEOUT; #endif /* SHOPT_TIMEOUT */ /* initialize jobs table */ job_clear(); sh_onoption(SH_MULTILINE); if(argc>0) { /* check for restricted shell */ if(type&SH_TYPE_RESTRICTED) sh_onoption(SH_RESTRICTED); #if SHOPT_PFSH /* check for profile shell */ else if(type&SH_TYPE_PROFILE) sh_onoption(SH_PFSH); #endif /* look for options */ /* shp->st.dolc is $# */ if((shp->st.dolc = sh_argopts(-argc,argv,shp)) < 0) { shp->exitval = 2; sh_done(shp,0); } opt_info.disc = 0; shp->st.dolv=argv+(argc-1)-shp->st.dolc; shp->st.dolv[0] = argv[0]; if(shp->st.dolc < 1) sh_onoption(SH_SFLAG); if(!sh_isoption(SH_SFLAG)) { shp->st.dolc--; shp->st.dolv++; #if _WINIX { char* name; name = shp->st.dolv[0]; if(name[1]==':' && (name[2]=='/' || name[2]=='\\')) { #if _lib_pathposix char* p; if((n = pathposix(name, NIL(char*), 0)) > 0 && (p = (char*)malloc(++n))) { pathposix(name, p, n); name = p; } else #endif { name[1] = name[0]; name[0] = name[2] = '/'; } } } #endif /* _WINIX */ } if(beenhere==1) { struct lconv* lc; shp->decomma = (lc=localeconv()) && lc->decimal_point && *lc->decimal_point==','; beenhere = 2; } } /* import variable attributes from environment */ if(!sh_isoption(SH_POSIX)) env_init(shp,1); #if SHOPT_PFSH if (sh_isoption(SH_PFSH)) { struct passwd *pw = getpwuid(shp->gd->userid); if(pw) shp->gd->user = strdup(pw->pw_name); } #endif /* set[ug]id scripts require the -p flag */ if(shp->gd->userid!=shp->gd->euserid || shp->gd->groupid!=shp->gd->egroupid) { #ifdef SHOPT_P_SUID /* require sh -p to run setuid and/or setgid */ if(!sh_isoption(SH_PRIVILEGED) && shp->gd->userid >= SHOPT_P_SUID) { setuid(shp->gd->euserid=shp->gd->userid); setgid(shp->gd->egroupid=shp->gd->groupid); } else #endif /* SHOPT_P_SUID */ sh_onoption(SH_PRIVILEGED); #ifdef SHELLMAGIC /* careful of #! setuid scripts with name beginning with - */ if(shp->login_sh && argv[1] && strcmp(argv[0],argv[1])==0) errormsg(SH_DICT,ERROR_exit(1),e_prohibited); #endif /*SHELLMAGIC*/ } else sh_offoption(SH_PRIVILEGED); /* shname for $0 in profiles and . scripts */ if(sh_isdevfd(argv[1])) shp->shname = strdup(argv[0]); else shp->shname = strdup(shp->st.dolv[0]); /* * return here for shell script execution * but not for parenthesis subshells */ error_info.id = strdup(shp->st.dolv[0]); /* error_info.id is $0 */ shp->jmpbuffer = (void*)&shp->checkbase; sh_pushcontext(shp,&shp->checkbase,SH_JMPSCRIPT); shp->st.self = &shp->global; shp->topscope = (Shscope_t*)shp->st.self; sh_offstate(SH_INIT); login_files[0] = (char*)e_profile; login_files[1] = ".profile"; shp->gd->login_files = login_files; shp->bltindata.version = SH_VERSION; shp->bltindata.shp = shp; shp->bltindata.shrun = sh_run; shp->bltindata.shtrap = sh_trap; shp->bltindata.shexit = sh_exit; shp->bltindata.shbltin = sh_addbuiltin; shp->bltindata.shgetenv = sh_getenv; shp->bltindata.shsetenv = sh_setenviron; astintercept(&shp->bltindata,1); #if 0 #define NV_MKINTTYPE(x,y,z) nv_mkinttype(#x,sizeof(x),(x)-1<0,(y),(Namdisc_t*)z); NV_MKINTTYPE(pid_t,"process id",0); NV_MKINTTYPE(gid_t,"group id",0); NV_MKINTTYPE(uid_t,"user id",0); NV_MKINTTYPE(size_t,(const char*)0,0); NV_MKINTTYPE(ssize_t,(const char*)0,0); NV_MKINTTYPE(off_t,"offset in bytes",0); NV_MKINTTYPE(ino_t,"\ai-\anode number",0); NV_MKINTTYPE(mode_t,(const char*)0,&modedisc); NV_MKINTTYPE(dev_t,"device id",0); NV_MKINTTYPE(nlink_t,"hard link count",0); NV_MKINTTYPE(blkcnt_t,"block count",0); NV_MKINTTYPE(time_t,"seconds since the epoch",0); nv_mkstat(); #endif if(shp->userinit=userinit) (*userinit)(shp, 0); #ifdef BUILD_DTKSH int * lockedFds; lockedFds = LockKshFileDescriptors(); (void) XtSetLanguageProc((XtAppContext)NULL, (XtLanguageProc)NULL, (XtPointer)NULL); setlocale(LC_ALL, ""); DtNlInitialize(); _DtEnvControl(DT_ENV_SET); localeFun.disc = &localeDisc; nv_stack(LANGNOD, &localeFun); UnlockKshFileDescriptors(lockedFds); /* * Save the current setting of NLSPATH. The user/script may want to * set its own NLSPATH to access its message catalog, so we need to * remember where to find our own catalog(s). This saved path is used * in ksh93/src/cmd/ksh93/sh/init.c: _DtGetMessage(). We don't mess * with the user/script's setting of LANG as we want to track changes * in LANG. */ savedNlsPath = strdup(getenv("NLSPATH")); /* * Sync the libc environment (set up by DtEnvControl) with our internal * hash table environment. */ SyncEnv("NLSPATH"); SyncEnv("LANG"); #endif return(shp); } Shell_t *sh_getinterp(void) { return(&sh); } /* * reinitialize before executing a script */ int sh_reinit(char *argv[]) { Shell_t *shp = sh_getinterp(); Shopt_t opt; Namval_t *np,*npnext; Dt_t *dp; struct adata { Shell_t *sh; void *extra[2]; } data; for(np=dtfirst(shp->fun_tree);np;np=npnext) { if((dp=shp->fun_tree)->walk) dp = dp->walk; npnext = (Namval_t*)dtnext(shp->fun_tree,np); if(np>= shgd->bltin_cmds && np < &shgd->bltin_cmds[nbltins]) continue; if(is_abuiltin(np) && nv_isattr(np,NV_EXPORT)) continue; if(*np->nvname=='/') continue; nv_delete(np,dp,NV_NOFREE); } dtclose(shp->alias_tree); shp->alias_tree = dtopen(&_Nvdisc,Dtoset); shp->last_root = shp->var_tree; shp->inuse_bits = 0; if(shp->userinit) (*shp->userinit)(shp, 1); if(shp->heredocs) { sfclose(shp->heredocs); shp->heredocs = 0; } /* remove locals */ sh_onstate(SH_INIT); memset(&data,0,sizeof(data)); data.sh = shp; nv_scan(shp->var_tree,sh_envnolocal,(void*)&data,NV_EXPORT,0); nv_scan(shp->var_tree,sh_envnolocal,(void*)&data,NV_ARRAY,NV_ARRAY); sh_offstate(SH_INIT); memset(shp->st.trapcom,0,(shp->st.trapmax+1)*sizeof(char*)); memset((void*)&opt,0,sizeof(opt)); #if SHOPT_NAMESPACE if(shp->namespace) { dp=nv_dict(shp->namespace); if(dp==shp->var_tree) shp->var_tree = dtview(dp,0); _nv_unset(shp->namespace,NV_RDONLY); shp->namespace = 0; } #endif /* SHOPT_NAMESPACE */ if(sh_isoption(SH_TRACKALL)) on_option(&opt,SH_TRACKALL); if(sh_isoption(SH_EMACS)) on_option(&opt,SH_EMACS); if(sh_isoption(SH_GMACS)) on_option(&opt,SH_GMACS); if(sh_isoption(SH_VI)) on_option(&opt,SH_VI); if(sh_isoption(SH_VIRAW)) on_option(&opt,SH_VIRAW); shp->options = opt; /* set up new args */ if(argv) shp->arglist = sh_argcreate(argv); if(shp->arglist) sh_argreset(shp,shp->arglist,NIL(struct dolnod*)); shp->envlist=0; shp->curenv = 0; shp->shname = error_info.id = strdup(shp->st.dolv[0]); sh_offstate(SH_FORKED); shp->fn_depth = shp->dot_depth = 0; sh_sigreset(0); if(!(SHLVL->nvalue.ip)) { shlvl = 0; SHLVL->nvalue.ip = &shlvl; nv_onattr(SHLVL,NV_INTEGER|NV_EXPORT|NV_NOFREE); } *SHLVL->nvalue.ip +=1; nv_offattr(SHLVL,NV_IMPORT); shp->st.filename = strdup(shp->lastarg); nv_delete((Namval_t*)0, (Dt_t*)0, 0); job.exitval = 0; shp->inpipe = shp->outpipe = 0; job_clear(); job.in_critical = 0; return(1); } /* * set when creating a local variable of this name */ Namfun_t *nv_cover(register Namval_t *np) { if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS || np==ENVNOD || np==LINENO) return(np->nvfun); #ifdef _hdr_locale if(np==LCALLNOD || np==LCTYPENOD || np==LCMSGNOD || np==LCCOLLNOD || np==LCNUMNOD || np==LANGNOD) return(np->nvfun); #endif return(0); } static const char *shdiscnames[] = { "tilde", 0}; #ifdef SHOPT_STATS struct Stats { Namfun_t hdr; Shell_t *sh; char *nodes; int numnodes; int current; }; static Namval_t *next_stat(register Namval_t* np, Dt_t *root,Namfun_t *fp) { struct Stats *sp = (struct Stats*)fp; if(!root) sp->current = 0; else if(++sp->current>=sp->numnodes) return(0); return(nv_namptr(sp->nodes,sp->current)); } static Namval_t *create_stat(Namval_t *np,const char *name,int flag,Namfun_t *fp) { struct Stats *sp = (struct Stats*)fp; register const char *cp=name; register int i=0,n; Namval_t *nq=0; Shell_t *shp = sp->sh; if(!name) return(SH_STATS); while((i=*cp++) && i != '=' && i != '+' && i!='['); n = (cp-1) -name; for(i=0; i < sp->numnodes; i++) { nq = nv_namptr(sp->nodes,i); if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0) goto found; } nq = 0; found: if(nq) { fp->last = (char*)&name[n]; shp->last_table = SH_STATS; } else errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np)); return(nq); } static const Namdisc_t stat_disc = { 0, 0, 0, 0, 0, create_stat, 0, 0, next_stat }; static char *name_stat(Namval_t *np, Namfun_t *fp) { Shell_t *shp = sh_getinterp(); sfprintf(shp->strbuf,".sh.stats.%s",np->nvname); return(sfstruse(shp->strbuf)); } static const Namdisc_t stat_child_disc = { 0,0,0,0,0,0,0, name_stat }; static Namfun_t stat_child_fun = { &stat_child_disc, 1, 0, sizeof(Namfun_t) }; static void stat_init(Shell_t *shp) { int i,nstat = STAT_SUBSHELL+1; struct Stats *sp = newof(0,struct Stats,1,nstat*NV_MINSZ); Namval_t *np; sp->numnodes = nstat; sp->nodes = (char*)(sp+1); shgd->stats = (int*)calloc(sizeof(int),nstat); sp->sh = shp; for(i=0; i < nstat; i++) { np = nv_namptr(sp->nodes,i); np->nvfun = &stat_child_fun; np->nvname = (char*)shtab_stats[i].sh_name; nv_onattr(np,NV_RDONLY|NV_MINIMAL|NV_NOFREE|NV_INTEGER); nv_setsize(np,10); np->nvalue.ip = &shgd->stats[i]; } sp->hdr.dsize = sizeof(struct Stats) + nstat*(sizeof(int)+NV_MINSZ); sp->hdr.disc = &stat_disc; nv_stack(SH_STATS,&sp->hdr); sp->hdr.nofree = 1; nv_setvtree(SH_STATS); } #else # define stat_init(x) #endif /* SHOPT_STATS */ /* * Initialize the shell name and alias table */ static Init_t *nv_init(Shell_t *shp) { double d=0; ip = newof(0,Init_t,1,0); if(!ip) return(0); shp->nvfun.last = (char*)shp; shp->nvfun.nofree = 1; ip->sh = shp; shp->var_base = shp->var_tree = inittree(shp,shtab_variables); SHLVL->nvalue.ip = &shlvl; ip->IFS_init.hdr.disc = &IFS_disc; ip->PATH_init.disc = &RESTRICTED_disc; ip->PATH_init.nofree = 1; ip->FPATH_init.disc = &RESTRICTED_disc; ip->FPATH_init.nofree = 1; ip->CDPATH_init.disc = &CDPATH_disc; ip->CDPATH_init.nofree = 1; ip->SHELL_init.disc = &RESTRICTED_disc; ip->SHELL_init.nofree = 1; ip->ENV_init.disc = &RESTRICTED_disc; ip->ENV_init.nofree = 1; ip->VISUAL_init.disc = &EDITOR_disc; ip->VISUAL_init.nofree = 1; ip->EDITOR_init.disc = &EDITOR_disc; ip->EDITOR_init.nofree = 1; ip->HISTFILE_init.disc = &HISTFILE_disc; ip->HISTFILE_init.nofree = 1; ip->HISTSIZE_init.disc = &HISTFILE_disc; ip->HISTSIZE_init.nofree = 1; ip->OPTINDEX_init.disc = &OPTINDEX_disc; ip->OPTINDEX_init.nofree = 1; ip->SECONDS_init.hdr.disc = &SECONDS_disc; ip->SECONDS_init.hdr.nofree = 1; ip->RAND_init.hdr.disc = &RAND_disc; ip->RAND_init.hdr.nofree = 1; ip->RAND_init.sh = shp; ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc; ip->SH_MATCH_init.hdr.nofree = 1; ip->SH_MATH_init.disc = &SH_MATH_disc; ip->SH_MATH_init.nofree = 1; ip->SH_VERSION_init.disc = &SH_VERSION_disc; ip->SH_VERSION_init.nofree = 1; ip->LINENO_init.disc = &LINENO_disc; ip->LINENO_init.nofree = 1; ip->L_ARG_init.disc = &L_ARG_disc; ip->L_ARG_init.nofree = 1; #ifdef _hdr_locale ip->LC_TYPE_init.disc = &LC_disc; ip->LC_TYPE_init.nofree = 1; ip->LC_NUM_init.disc = &LC_disc; ip->LC_NUM_init.nofree = 1; ip->LC_COLL_init.disc = &LC_disc; ip->LC_COLL_init.nofree = 1; ip->LC_MSG_init.disc = &LC_disc; ip->LC_MSG_init.nofree = 1; ip->LC_ALL_init.disc = &LC_disc; ip->LC_ALL_init.nofree = 1; ip->LANG_init.disc = &LC_disc; ip->LANG_init.nofree = 1; #endif /* _hdr_locale */ nv_stack(IFSNOD, &ip->IFS_init.hdr); ip->IFS_init.hdr.nofree = 1; nv_stack(PATHNOD, &ip->PATH_init); nv_stack(FPATHNOD, &ip->FPATH_init); nv_stack(CDPNOD, &ip->CDPATH_init); nv_stack(SHELLNOD, &ip->SHELL_init); nv_stack(ENVNOD, &ip->ENV_init); nv_stack(VISINOD, &ip->VISUAL_init); nv_stack(EDITNOD, &ip->EDITOR_init); nv_stack(HISTFILE, &ip->HISTFILE_init); nv_stack(HISTSIZE, &ip->HISTSIZE_init); nv_stack(OPTINDNOD, &ip->OPTINDEX_init); nv_stack(SECONDS, &ip->SECONDS_init.hdr); nv_stack(L_ARGNOD, &ip->L_ARG_init); nv_putval(SECONDS, (char*)&d, NV_DOUBLE); nv_stack(RANDNOD, &ip->RAND_init.hdr); d = (shp->gd->pid&RANDMASK); nv_putval(RANDNOD, (char*)&d, NV_DOUBLE); nv_stack(LINENO, &ip->LINENO_init); SH_MATCHNOD->nvfun = &ip->SH_MATCH_init.hdr; nv_putsub(SH_MATCHNOD,(char*)0,10); nv_stack(SH_MATHNOD, &ip->SH_MATH_init); nv_stack(SH_VERSIONNOD, &ip->SH_VERSION_init); #ifdef _hdr_locale nv_stack(LCTYPENOD, &ip->LC_TYPE_init); nv_stack(LCALLNOD, &ip->LC_ALL_init); nv_stack(LCMSGNOD, &ip->LC_MSG_init); nv_stack(LCCOLLNOD, &ip->LC_COLL_init); nv_stack(LCNUMNOD, &ip->LC_NUM_init); nv_stack(LANGNOD, &ip->LANG_init); #endif /* _hdr_locale */ (PPIDNOD)->nvalue.lp = (&shp->gd->ppid); (SH_PIDNOD)->nvalue.lp = (&shp->gd->current_pid); (TMOUTNOD)->nvalue.lp = (&shp->st.tmout); (MCHKNOD)->nvalue.lp = (&sh_mailchk); (OPTINDNOD)->nvalue.lp = (&shp->st.optindex); /* set up the seconds clock */ shp->alias_tree = dtopen(&_Nvdisc,Dtoset); shp->track_tree = dtopen(&_Nvdisc,Dtset); shp->bltin_tree = inittree(shp,(const struct shtable2*)shtab_builtins); shp->fun_tree = dtopen(&_Nvdisc,Dtoset); dtview(shp->fun_tree,shp->bltin_tree); nv_mount(DOTSHNOD, "type", shp->typedict=dtopen(&_Nvdisc,Dtoset)); nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0); DOTSHNOD->nvalue.cp = Empty; nv_onattr(DOTSHNOD,NV_RDONLY); SH_LINENO->nvalue.ip = &shp->st.lineno; VERSIONNOD->nvalue.nrp = newof(0,struct Namref,1,0); VERSIONNOD->nvalue.nrp->np = SH_VERSIONNOD; VERSIONNOD->nvalue.nrp->root = nv_dict(DOTSHNOD); VERSIONNOD->nvalue.nrp->table = DOTSHNOD; nv_onattr(VERSIONNOD,NV_REF); math_init(shp); if(!shgd->stats) stat_init(shp); return(ip); } /* * initialize name-value pairs */ static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals) { register Namval_t *np; register const struct shtable2 *tp; register unsigned n = 0; register Dt_t *treep; Dt_t *base_treep, *dict; for(tp=name_vals;*tp->sh_name;tp++) n++; np = (Namval_t*)calloc(n,sizeof(Namval_t)); if(!shgd->bltin_nodes) { shgd->bltin_nodes = np; shgd->bltin_nnodes = n; } else if(name_vals==(const struct shtable2*)shtab_builtins) { shgd->bltin_cmds = np; nbltins = n; } base_treep = treep = dtopen(&_Nvdisc,Dtoset); treep->user = (void*)shp; for(tp=name_vals;*tp->sh_name;tp++,np++) { if((np->nvname = strrchr(tp->sh_name,'.')) && np->nvname!=((char*)tp->sh_name)) np->nvname++; else { np->nvname = (char*)tp->sh_name; treep = base_treep; } np->nvenv = 0; if(name_vals==(const struct shtable2*)shtab_builtins) np->nvalue.bfp = (Nambfp_f)((struct shtable3*)tp)->sh_value; else { if(name_vals == shtab_variables) np->nvfun = &shp->nvfun; np->nvalue.cp = (char*)tp->sh_value; } nv_setattr(np,tp->sh_number); if(nv_isattr(np,NV_TABLE)) nv_mount(np,(const char*)0,dict=dtopen(&_Nvdisc,Dtoset)); if(nv_isattr(np,NV_INTEGER)) nv_setsize(np,10); else nv_setsize(np,0); dtinsert(treep,np); if(nv_istable(np)) treep = dict; } return(treep); } /* * read in the process environment and set up name-value pairs * skip over items that are not name-value pairs * * Must be called with import_attributes == 0 first, then again with * import_attributes == 1 if variable attributes are to be imported * from the environment. */ static void env_init(Shell_t *shp, int import_attributes) { register char *cp; register Namval_t *np,*mp; register char **ep=environ; char *dp; int nenv=0,k=0,size=0; Namval_t *np0; static char *next=0; /* next variable whose attributes to import */ if(import_attributes) goto import_attributes; if(!ep) goto skip; while(*ep++) nenv++; np = newof(0,Namval_t,nenv,0); for(np0=np,ep=environ;cp= *ep; ep++) { dp = strchr(cp,'='); if(!dp) continue; *dp++ = 0; if(mp = dtmatch(shp->var_base,cp)) { mp->nvenv = (char*)cp; dp[-1] = '='; } else if(strcmp(cp,e_envmarker)==0) { dp[-1] = '='; next = cp + strlen(e_envmarker); continue; } else { k++; mp = np++; mp->nvname = cp; size += strlen(cp); } nv_onattr(mp,NV_IMPORT); if(mp->nvfun || nv_isattr(mp,NV_INTEGER)) nv_putval(mp,dp,0); else { mp->nvalue.cp = dp; nv_onattr(mp,NV_NOFREE); } nv_onattr(mp,NV_EXPORT|NV_IMPORT); } np = (Namval_t*)realloc((void*)np0,k*sizeof(Namval_t)); dp = (char*)malloc(size+k); while(k-->0) { size = strlen(np->nvname); memcpy(dp,np->nvname,size+1); np->nvname[size] = '='; np->nvenv = np->nvname; np->nvname = dp; dp += size+1; dtinsert(shp->var_base,np++); } skip: if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED)) { nv_offattr(PWDNOD,NV_TAGGED); path_pwd(shp,0); } if((cp = nv_getval(SHELLNOD)) && (sh_type(cp)&SH_TYPE_RESTRICTED)) sh_onoption(SH_RESTRICTED); /* restricted shell */ return; /* Import variable attributes from environment (from variable named by e_envmarker) */ import_attributes: while(cp=next) { if(next = strchr(++cp,'=')) *next = 0; np = nv_search(cp+2,shp->var_tree,NV_ADD); if(np!=SHLVL && nv_isattr(np,NV_IMPORT|NV_EXPORT)) { int flag = *(unsigned char*)cp-' '; int size = *(unsigned char*)(cp+1)-' '; if((flag&NV_INTEGER) && size==0) { /* check for floating*/ char *val = nv_getval(np); strtol(val,&dp,10); if(*dp=='.' || *dp=='e' || *dp=='E') { char *lp; flag |= NV_DOUBLE; if(*dp=='.') { strtol(dp+1,&lp,10); if(*lp) dp = lp; } if(*dp && *dp!='.') { flag |= NV_EXPNOTE; size = dp-val; } else size = strlen(dp); size--; } } nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size); if((flag&(NV_INTEGER|NV_UTOL|NV_LTOU))==(NV_UTOL|NV_LTOU)) nv_mapchar(np,(flag&NV_UTOL)?e_tolower:e_toupper); } else cp += 2; } return; } /* * terminate shell and free up the space */ int sh_term(void) { sfdisc(sfstdin,SF_POPDISC); free((char*)sh.outbuff); stakset(NIL(char*),0); return(0); } /* function versions of these */ #define DISABLE /* proto workaround */ unsigned long sh_isoption DISABLE (int opt) { return(sh_isoption(opt)); } unsigned long sh_onoption DISABLE (int opt) { return(sh_onoption(opt)); } unsigned long sh_offoption DISABLE (int opt) { return(sh_offoption(opt)); } void sh_sigcheck DISABLE (Shell_t *shp) { if(!shp) shp = sh_getinterp(); sh_sigcheck(shp); } Dt_t* sh_bltin_tree DISABLE (void) { return(sh.bltin_tree); } /* * This code is for character mapped variables with wctrans() */ struct Mapchar { Namfun_t hdr; const char *name; wctrans_t trans; int lctype; }; static void put_trans(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { struct Mapchar *mp = (struct Mapchar*)fp; int c,offset = staktell(),off=offset; if(val) { if(mp->lctype!=lctype) { mp->lctype = lctype; mp->trans = wctrans(mp->name); } if(!mp->trans || (flags&NV_INTEGER)) goto skip; while(c = mbchar(val)) { c = towctrans(c,mp->trans); stakseek(off+c); stakseek(off); c = mbconv(stakptr(off),c); off += c; stakseek(off); } stakputc(0); val = stakptr(offset); } else { nv_putv(np,val,flags,fp); nv_disc(np,fp,NV_POP); if(!(fp->nofree&1)) free((void*)fp); stakseek(offset); return; } skip: nv_putv(np,val,flags,fp); stakseek(offset); } static const Namdisc_t TRANS_disc = { sizeof(struct Mapchar), put_trans }; Namfun_t *nv_mapchar(Namval_t *np,const char *name) { wctrans_t trans = name?wctrans(name):0; struct Mapchar *mp=0; int n=0,low; if(np) mp = (struct Mapchar*)nv_hasdisc(np,&TRANS_disc); if(!name) return(mp?(Namfun_t*)mp->name:0); if(!trans) return(0); if(!np) return(((Namfun_t*)0)+1); if((low=strcmp(name,e_tolower)) && strcmp(name,e_toupper)) n += strlen(name)+1; if(mp) { if(strcmp(name,mp->name)==0) return(&mp->hdr); nv_disc(np,&mp->hdr,NV_POP); if(!(mp->hdr.nofree&1)) free((void*)mp); } mp = newof(0,struct Mapchar,1,n); mp->trans = trans; mp->lctype = lctype; if(low==0) mp->name = e_tolower; else if(n==0) mp->name = e_toupper; else { mp->name = (char*)(mp+1); strcpy((char*)mp->name,name); } mp->hdr.disc = &TRANS_disc; return(&mp->hdr); }