struct tnode *addtree(struct tnode *p, char *word) { int cmp; struct tnode *newnode; if (p == NULL) { p = talloc(); p->word = strdupx(word); p->count = 1; p->left = NULL; p->right = NULL; } else { if ((cmp = strcmp(p->word, word)) == 0) { p->count++; } else if (cmp > 0) { p->left = addtree(p->left, word); } else if (cmp < 0) { p->right = addtree(p->right, word); } else { printf("error"); exit(-1); } } return p; }
void setextlibpath(const char *name, const char *val) { int flag; char *p, *cp; if (!strcmp(name, "BEGINLIBPATH")) flag = BEGIN_LIBPATH; else if (!strcmp(name, "ENDLIBPATH")) flag = END_LIBPATH; else if (!strcmp(name, "LIBPATHSTRICT")) flag = LIBPATHSTRICT; else return; /* convert slashes to backslashes */ strdupx(cp, val, ATEMP); for (p = cp; *p; p++) { if (*p == '/') *p = '\\'; } DosSetExtLIBPATH(cp, flag); afree(cp, ATEMP); }
/* * Process here documents by providing the content, either as * result (globally allocated) string or in a temp file; if * unquoted, the string is expanded first. */ static int hereinval(const char *content, int sub, char **resbuf, struct shf *shf) { const char * volatile ccp = content; struct source *s, *osource; osource = source; newenv(E_ERRH); if (kshsetjmp(e->jbuf)) { source = osource; quitenv(shf); /* special to iosetup(): don't print error */ return (-2); } if (sub) { /* do substitutions on the content of heredoc */ s = pushs(SSTRING, ATEMP); s->start = s->str = ccp; source = s; if (yylex(sub) != LWORD) internal_errorf("%s: %s", "herein", "yylex"); source = osource; ccp = evalstr(yylval.cp, 0); } if (resbuf == NULL) shf_puts(ccp, shf); else strdupx(*resbuf, ccp, APERM); quitenv(NULL); return (0); }
void set_prompt(int to, Source *s) { cur_prompt = (uint8_t)to; switch (to) { /* command */ case PS1: /* * Substitute ! and !! here, before substitutions are done * so ! in expanded variables are not expanded. * NOTE: this is not what AT&T ksh does (it does it after * substitutions, POSIX doesn't say which is to be done. */ { struct shf *shf; char * volatile ps1; Area *saved_atemp; int saved_lineno; ps1 = str_val(global("PS1")); shf = shf_sopen(NULL, strlen(ps1) * 2, SHF_WR | SHF_DYNAMIC, NULL); while (*ps1) if (*ps1 != '!' || *++ps1 == '!') shf_putchar(*ps1++, shf); else shf_fprintf(shf, "%lu", s ? (unsigned long)s->line + 1 : 0UL); ps1 = shf_sclose(shf); saved_lineno = current_lineno; if (s) current_lineno = s->line + 1; saved_atemp = ATEMP; newenv(E_ERRH); if (kshsetjmp(e->jbuf)) { prompt = safe_prompt; /* * Don't print an error - assume it has already * been printed. Reason is we may have forked * to run a command and the child may be * unwinding its stack through this code as it * exits. */ } else { char *cp = substitute(ps1, 0); strdupx(prompt, cp, saved_atemp); } current_lineno = saved_lineno; quitenv(NULL); } break; /* command continuation */ case PS2: prompt = str_val(global("PS2")); break; } }
struct nlist *install(char *name, char *defn) { struct nlist *np; unsigned hashval; if ((np = lookup(name)) == NULL) { np = (struct nlist *) malloc(sizeof(*np)); if (np == NULL || (np->name = strdupx(name)) == NULL) { return NULL; } hashval = hash(name); np->next = hashtab[hashval]; hashtab[hashval] = np; } else { free((void *) np->defn); } if ((np->defn = strdupx(defn)) == NULL) { return NULL; } return np; }
static void unsetspec(struct tbl *vp) { /* * AT&T ksh man page says OPTIND, OPTARG and _ lose special * meaning, but OPTARG does not (still set by getopts) and _ is * also still set in various places. Don't know what AT&T does * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR */ switch (special(vp->name)) { #ifdef __OS2__ case V_BEGINLIBPATH: case V_ENDLIBPATH: case V_LIBPATHSTRICT: setextlibpath(vp->name, ""); return; #endif #if HAVE_PERSISTENT_HISTORY case V_HISTFILE: sethistfile(NULL); return; #endif case V_IFS: set_ifs(TC_IFSWS); break; case V_PATH: afree(path, APERM); strdupx(path, def_path, APERM); /* clear tracked aliases */ flushcom(true); break; #ifndef MKSH_NO_CMDLINE_EDITING case V_TERM: x_initterm(null); return; #endif case V_TMPDIR: /* should not become unspecial */ if (tmpdir) { afree(tmpdir, APERM); tmpdir = NULL; } break; case V_LINENO: case V_RANDOM: case V_SECONDS: case V_TMOUT: /* AT&T ksh leaves previous value in place */ unspecial(vp->name); break; } }
const char * real_exec_name(const char *name) { char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1]; const char *real_name = name; if (access_stat_ex(test_exec_exist, real_name, x_name) != -1) /*XXX memory leak */ strdupx(real_name, x_name, ATEMP); return (real_name); }
static institute_t *instituteNewEntry(cdiResH resH, int center, int subcenter, const char *name, const char *longname) { institute_t *instituteptr = (institute_t*) xmalloc(sizeof(institute_t)); instituteDefaultValue(instituteptr); if (resH == CDI_UNDEFID) instituteptr->self = reshPut(instituteptr, &instituteOps); else { instituteptr->self = resH; reshReplace(resH, instituteptr, &instituteOps); } instituteptr->used = 1; instituteptr->center = center; instituteptr->subcenter = subcenter; if ( name && *name ) instituteptr->name = strdupx(name); if (longname && *longname) instituteptr->longname = strdupx(longname); return instituteptr; }
void set_current_wd(const char *nwd) { char *allocd = NULL; if (nwd == NULL) { allocd = ksh_get_wd(); nwd = allocd ? allocd : null; } afree(current_wd, APERM); strdupx(current_wd, nwd, APERM); afree(allocd, ATEMP); }
/* set variable to string value */ int setstr(struct tbl *vq, const char *s, int error_ok) { char *salloc = NULL; bool no_ro_check = tobool(error_ok & 0x4); error_ok &= ~0x4; if ((vq->flag & RDONLY) && !no_ro_check) { warningf(true, "read-only: %s", vq->name); if (!error_ok) errorfxz(2); return (0); } if (!(vq->flag&INTEGER)) { /* string dest */ if ((vq->flag&ALLOC)) { #ifndef MKSH_SMALL /* debugging */ if (s >= vq->val.s && s <= vq->val.s + strlen(vq->val.s)) { internal_errorf( "setstr: %s=%s: assigning to self", vq->name, s); } #endif afree(vq->val.s, vq->areap); } vq->flag &= ~(ISSET|ALLOC); vq->type = 0; if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST))) s = salloc = formatstr(vq, s); if ((vq->flag&EXPORT)) exportprep(vq, s); else { strdupx(vq->val.s, s, vq->areap); vq->flag |= ALLOC; } } else { /* integer dest */ if (!v_evaluate(vq, s, error_ok, true)) return (0); } vq->flag |= ISSET; if ((vq->flag&SPECIAL)) setspec(vq); afree(salloc, ATEMP); return (1); }
static void unsetspec(struct tbl *vp) { /* * AT&T ksh man page says OPTIND, OPTARG and _ lose special * meaning, but OPTARG does not (still set by getopts) and _ is * also still set in various places. Don't know what AT&T does * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR */ switch (special(vp->name)) { #if HAVE_PERSISTENT_HISTORY case V_HISTFILE: sethistfile(NULL); return; #endif case V_IFS: setctypes(TC_IFSWS, C_IFS); ifs0 = ' '; break; case V_PATH: if (path) afree(path, APERM); strdupx(path, def_path, APERM); /* clear tracked aliases */ flushcom(true); break; case V_TMPDIR: /* should not become unspecial */ if (tmpdir) { afree(tmpdir, APERM); tmpdir = NULL; } break; case V_LINENO: case V_RANDOM: case V_SECONDS: case V_TMOUT: /* AT&T ksh leaves previous value in place */ unspecial(vp->name); break; } }
/* make a response file to pass a very long command line */ static char * make_response_file(char * const *argv) { char rsp_name_arg[] = "@mksh-rsp-XXXXXX"; char *rsp_name = &rsp_name_arg[1]; int arg_len = 0; int i; for (i = 0; argv[i]; i++) arg_len += strlen(argv[i]) + 1; /* * If a length of command line is longer than MAX_CMD_LINE_LEN, then * use a response file. OS/2 cannot process a command line longer * than 32K. Of course, a response file cannot be recognised by a * normal OS/2 program, that is, neither non-EMX or non-kLIBC. But * it cannot accept a command line longer than 32K in itself. So * using a response file in this case, is an acceptable solution. */ if (arg_len > MAX_CMD_LINE_LEN) { int fd; char *result; if ((fd = mkstemp(rsp_name)) == -1) return (NULL); /* write all the arguments except a 0th program name */ for (i = 1; argv[i]; i++) { write(fd, argv[i], strlen(argv[i])); write(fd, "\n", 1); } close(fd); add_temp(rsp_name); strdupx(result, rsp_name_arg, ATEMP); return (result); } return (NULL); }
void thrcode_printf_va( THRCODE *tc, cexception_t *ex, const char *format, va_list ap ) { cexception_t inner; va_list ap2; if( tc->flags & THRF_IMMEDIATE_PRINTOUT ) { vprintf( format, ap ); } else { cexception_t innermost; char * volatile str = NULL; char buff[80]; ssize_t len = sizeof( buff ); ssize_t chars; va_copy( ap2, ap ); chars = vsnprintf( buff, len, format, ap ); cexception_guard( inner ) { if( chars > 0 ) { if( chars < len ) { str = strdupx( buff, &inner ); } else { str = mallocx( chars + 1, &inner ); vsnprintf( str, chars + 1, format, ap2 ); } cexception_guard( innermost ) { thrcode_insert_string( tc, &str, &inner ); } cexception_catch { freex( str ); cexception_reraise( innermost, &inner ); } } } cexception_catch { va_end( ap2 ); cexception_reraise( inner, ex ); } va_end( ap2 ); } }
int InsertWord(const char *Word, struct WordList *WL) { char *WrdCpy; unsigned long Len; if ((WrdCpy = strdupx(Word, WALLBYTES))) { if (StkPush(WrdCpy, &WL->Stack)) { Len = strlen(Word); if (WL->MaxLen < Len) WL->MaxLen = Len; InsertHash(WrdCpy, &WL->Hash); return (TRUE); } free(WrdCpy); } return (FALSE); }
static uint8_t isuc(const char *cx) { char *cp, *x; uint8_t rv = 0; if (!cx || !*cx) return (0); /* uppercase a string duplicate */ strdupx(x, cx, ATEMP); cp = x; while ((*cp = ksh_toupper(*cp))) ++cp; /* check for UTF-8 */ if (strstr(x, "UTF-8") || strstr(x, "UTF8")) rv = 1; /* free copy and out */ afree(x, ATEMP); return (rv); }
/* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */ char * ksh_get_wd(void) { #ifdef MKSH__NO_PATH_MAX char *rv, *cp; if ((cp = get_current_dir_name())) { strdupx(rv, cp, ATEMP); free_gnu_gcdn(cp); } else rv = NULL; #else char *rv; if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) { afree(rv, ATEMP); rv = NULL; } #endif return (rv); }
BOOL InsertWord(const STRPTR Word, struct WordList *WL) { STRPTR WrdCpy; ULONG Len; if((WrdCpy = strdupx(Word, WALLBYTES))) { if(StkPush(WrdCpy, &WL->Stack)) { Len = strlen(Word); if(WL->MaxLen < Len) WL->MaxLen = Len; InsertHash(WrdCpy, &WL->Hash); return(TRUE); } free(WrdCpy); } return(FALSE); }
/* * find command * either function, hashed command, or built-in (in that order) */ struct tbl * findcom(const char *name, int flags) { static struct tbl temp; uint32_t h = hash(name); struct tbl *tp = NULL, *tbi; /* insert if not found */ unsigned char insert = Flag(FTRACKALL); /* for function autoloading */ char *fpath; union mksh_cchack npath; if (vstrchr(name, '/')) { insert = 0; /* prevent FPATH search below */ flags &= ~FC_FUNC; goto Search; } tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL; /* * POSIX says special builtins first, then functions, then * POSIX regular builtins, then search path... */ if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI)) tp = tbi; if (!tp && (flags & FC_FUNC)) { tp = findfunc(name, h, false); if (tp && !(tp->flag & ISSET)) { if ((fpath = str_val(global("FPATH"))) == null) { tp->u.fpath = NULL; tp->u2.errnov = ENOENT; } else tp->u.fpath = search_path(name, fpath, R_OK, &tp->u2.errnov); } } if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) tp = tbi; if (!tp && (flags & FC_UNREGBI) && tbi) tp = tbi; if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { tp = ktsearch(&taliases, name, h); if (tp && (tp->flag & ISSET) && ksh_access(tp->val.s, X_OK) != 0) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; afree(tp->val.s, APERM); } tp->flag &= ~ISSET; } } Search: if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) && (flags & FC_PATH)) { if (!tp) { if (insert && !(flags & FC_DEFPATH)) { tp = ktenter(&taliases, name, h); tp->type = CTALIAS; } else { tp = &temp; tp->type = CEXEC; } /* make ~ISSET */ tp->flag = DEFINED; } npath.ro = search_path(name, (flags & FC_DEFPATH) ? def_path : path, X_OK, &tp->u2.errnov); if (npath.ro) { strdupx(tp->val.s, npath.ro, APERM); if (npath.ro != name) afree(npath.rw, ATEMP); tp->flag |= ISSET|ALLOC; } else if ((flags & FC_FUNC) && (fpath = str_val(global("FPATH"))) != null && (npath.ro = search_path(name, fpath, R_OK, &tp->u2.errnov)) != NULL) { /* * An undocumented feature of AT&T ksh is that * it searches FPATH if a command is not found, * even if the command hasn't been set up as an * autoloaded function (ie, no typeset -uf). */ tp = &temp; tp->type = CFUNC; /* make ~ISSET */ tp->flag = DEFINED; tp->u.fpath = npath.ro; } } return (tp); }
int include(const char *name, int argc, const char **argv, bool intr_ok) { Source *volatile s = NULL; struct shf *shf; const char **volatile old_argv; volatile int old_argc; int i; shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC); if (shf == NULL) return (-1); if (argv) { old_argv = e->loc->argv; old_argc = e->loc->argc; } else { old_argv = NULL; old_argc = 0; } newenv(E_INCL); if ((i = kshsetjmp(e->jbuf))) { quitenv(s ? s->u.shf : NULL); if (old_argv) { e->loc->argv = old_argv; e->loc->argc = old_argc; } switch (i) { case LRETURN: case LERROR: /* see below */ return (exstat & 0xFF); case LINTR: /* * intr_ok is set if we are including .profile or $ENV. * If user ^Cs out, we don't want to kill the shell... */ if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM) return (1); /* FALLTHROUGH */ case LEXIT: case LLEAVE: case LSHELL: unwind(i); /* NOTREACHED */ default: internal_errorf(Tunexpected_type, Tunwind, Tsource, i); /* NOTREACHED */ } } if (argv) { e->loc->argv = argv; e->loc->argc = argc; } s = pushs(SFILE, ATEMP); s->u.shf = shf; strdupx(s->file, name, ATEMP); i = shell(s, 1); quitenv(s->u.shf); if (old_argv) { e->loc->argv = old_argv; e->loc->argc = old_argc; } /* & 0xff to ensure value not -1 */ return (i & 0xFF); }
int c_cd(const char **wp) { int optc, rv, phys_path; bool physical = tobool(Flag(FPHYSICAL)); /* was a node from cdpath added in? */ int cdnode; /* show where we went?, error for $PWD */ bool printpath = false, eflag = false; struct tbl *pwd_s, *oldpwd_s; XString xs; char *dir, *allocd = NULL, *tryp, *pwd, *cdpath; while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1) switch (optc) { case 'e': eflag = true; break; case 'L': physical = false; break; case 'P': physical = true; break; case '?': return (2); } wp += builtin_opt.optind; if (Flag(FRESTRICTED)) { bi_errorf("restricted shell - can't cd"); return (2); } pwd_s = global("PWD"); oldpwd_s = global("OLDPWD"); if (!wp[0]) { /* No arguments - go home */ if ((dir = str_val(global("HOME"))) == null) { bi_errorf("no home directory (HOME not set)"); return (2); } } else if (!wp[1]) { /* One argument: - or dir */ strdupx(allocd, wp[0], ATEMP); if (ksh_isdash((dir = allocd))) { afree(allocd, ATEMP); allocd = NULL; dir = str_val(oldpwd_s); if (dir == null) { bi_errorf("no OLDPWD"); return (2); } printpath = true; } } else if (!wp[2]) { /* Two arguments - substitute arg1 in PWD for arg2 */ size_t ilen, olen, nlen, elen; char *cp; if (!current_wd[0]) { bi_errorf("can't determine current directory"); return (2); } /* * substitute arg1 for arg2 in current path. * if the first substitution fails because the cd fails * we could try to find another substitution. For now * we don't */ if ((cp = strstr(current_wd, wp[0])) == NULL) { bi_errorf("bad substitution"); return (2); } /*- * ilen = part of current_wd before wp[0] * elen = part of current_wd after wp[0] * because current_wd and wp[1] need to be in memory at the * same time beforehand the addition can stay unchecked */ ilen = cp - current_wd; olen = strlen(wp[0]); nlen = strlen(wp[1]); elen = strlen(current_wd + ilen + olen) + 1; dir = allocd = alloc(ilen + nlen + elen, ATEMP); memcpy(dir, current_wd, ilen); memcpy(dir + ilen, wp[1], nlen); memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen); printpath = true; } else { bi_errorf("too many arguments"); return (2); } #ifdef MKSH__NO_PATH_MAX /* only a first guess; make_path will enlarge xs if necessary */ XinitN(xs, 1024, ATEMP); #else XinitN(xs, PATH_MAX, ATEMP); #endif cdpath = str_val(global("CDPATH")); do { cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path); if (physical) rv = chdir(tryp = Xstring(xs, xp) + phys_path); else { simplify_path(Xstring(xs, xp)); rv = chdir(tryp = Xstring(xs, xp)); } } while (rv < 0 && cdpath != NULL); if (rv < 0) { if (cdnode) bi_errorf("%s: %s", dir, "bad directory"); else bi_errorf("%s: %s", tryp, cstrerror(errno)); afree(allocd, ATEMP); Xfree(xs, xp); return (2); } rv = 0; /* allocd (above) => dir, which is no longer used */ afree(allocd, ATEMP); allocd = NULL; /* Clear out tracked aliases with relative paths */ flushcom(false); /* * Set OLDPWD (note: unsetting OLDPWD does not disable this * setting in AT&T ksh) */ if (current_wd[0]) /* Ignore failure (happens if readonly or integer) */ setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR); if (!mksh_abspath(Xstring(xs, xp))) { pwd = NULL; } else if (!physical) { goto norealpath_PWD; } else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) { if (eflag) rv = 1; norealpath_PWD: pwd = Xstring(xs, xp); } /* Set PWD */ if (pwd) { char *ptmp = pwd; set_current_wd(ptmp); /* Ignore failure (happens if readonly or integer) */ setstr(pwd_s, ptmp, KSH_RETURN_ERROR); } else { set_current_wd(null); pwd = Xstring(xs, xp); /* XXX unset $PWD? */ if (eflag) rv = 1; } if (printpath || cdnode) shprintf("%s\n", pwd); afree(allocd, ATEMP); Xfree(xs, xp); return (rv); }
char * do_realpath(const char *upath) { char *xp, *ip, *tp, *ipath, *ldest = NULL; XString xs; size_t pos, len; int llen; struct stat sb; #ifdef MKSH__NO_PATH_MAX size_t ldestlen = 0; #define pathlen sb.st_size #define pathcnd (ldestlen < (pathlen + 1)) #else #define pathlen PATH_MAX #define pathcnd (!ldest) #endif /* max. recursion depth */ int symlinks = 32; if (mksh_abspath(upath)) { /* upath is an absolute pathname */ strdupx(ipath, upath, ATEMP); } else { /* upath is a relative pathname, prepend cwd */ if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp)) return (NULL); ipath = shf_smprintf("%s%s%s", tp, "/", upath); afree(tp, ATEMP); } /* ipath and upath are in memory at the same time -> unchecked */ Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP); /* now jump into the deep of the loop */ goto beginning_of_a_pathname; while (*ip) { /* skip slashes in input */ while (*ip == '/') ++ip; if (!*ip) break; /* get next pathname component from input */ tp = ip; while (*ip && *ip != '/') ++ip; len = ip - tp; /* check input for "." and ".." */ if (tp[0] == '.') { if (len == 1) /* just continue with the next one */ continue; else if (len == 2 && tp[1] == '.') { /* strip off last pathname component */ while (xp > Xstring(xs, xp)) if (*--xp == '/') break; /* then continue with the next one */ continue; } } /* store output position away, then append slash to output */ pos = Xsavepos(xs, xp); /* 1 for the '/' and len + 1 for tp and the NUL from below */ XcheckN(xs, xp, 1 + len + 1); Xput(xs, xp, '/'); /* append next pathname component to output */ memcpy(xp, tp, len); xp += len; *xp = '\0'; /* lstat the current output, see if it's a symlink */ if (mksh_lstat(Xstring(xs, xp), &sb)) { /* lstat failed */ if (errno == ENOENT) { /* because the pathname does not exist */ while (*ip == '/') /* skip any trailing slashes */ ++ip; /* no more components left? */ if (!*ip) /* we can still return successfully */ break; /* more components left? fall through */ } /* not ENOENT or not at the end of ipath */ goto notfound; } /* check if we encountered a symlink? */ if (S_ISLNK(sb.st_mode)) { #ifndef MKSH__NO_SYMLINK /* reached maximum recursion depth? */ if (!symlinks--) { /* yep, prevent infinite loops */ errno = ELOOP; goto notfound; } /* get symlink(7) target */ if (pathcnd) { #ifdef MKSH__NO_PATH_MAX if (notoktoadd(pathlen, 1)) { errno = ENAMETOOLONG; goto notfound; } #endif ldest = aresize(ldest, pathlen + 1, ATEMP); } llen = readlink(Xstring(xs, xp), ldest, pathlen); if (llen < 0) /* oops... */ goto notfound; ldest[llen] = '\0'; /* * restart if symlink target is an absolute path, * otherwise continue with currently resolved prefix */ /* append rest of current input path to link target */ tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip); afree(ipath, ATEMP); ip = ipath = tp; if (!mksh_abspath(ldest)) { /* symlink target is a relative path */ xp = Xrestpos(xs, xp, pos); } else #endif { /* symlink target is an absolute path */ xp = Xstring(xs, xp); beginning_of_a_pathname: /* assert: (ip == ipath)[0] == '/' */ /* assert: xp == xs.beg => start of path */ /* exactly two leading slashes? (SUSv4 3.266) */ if (ip[1] == '/' && ip[2] != '/') { /* keep them, e.g. for UNC pathnames */ Xput(xs, xp, '/'); } } } /* otherwise (no symlink) merely go on */ } /* * either found the target and successfully resolved it, * or found its parent directory and may create it */ if (Xlength(xs, xp) == 0) /* * if the resolved pathname is "", make it "/", * otherwise do not add a trailing slash */ Xput(xs, xp, '/'); Xput(xs, xp, '\0'); /* * if source path had a trailing slash, check if target path * is not a non-directory existing file */ if (ip > ipath && ip[-1] == '/') { if (stat(Xstring(xs, xp), &sb)) { if (errno != ENOENT) goto notfound; } else if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; goto notfound; } /* target now either does not exist or is a directory */ } /* return target path */ if (ldest != NULL) afree(ldest, ATEMP); afree(ipath, ATEMP); return (Xclose(xs, xp)); notfound: /* save; freeing memory might trash it */ llen = errno; if (ldest != NULL) afree(ldest, ATEMP); afree(ipath, ATEMP); Xfree(xs, xp); errno = llen; return (NULL); #undef pathlen #undef pathcnd }
/* * lookup variable (according to (set&LOCAL)), set its attributes * (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, LCASEV, * UCASEV_AL), and optionally set its value if an assignment. */ struct tbl * typeset(const char *var, uint32_t set, uint32_t clr, int field, int base) { struct tbl *vp; struct tbl *vpbase, *t; char *tvar; const char *val; size_t len; bool vappend = false; enum namerefflag new_refflag = SRF_NOP; if ((set & (ARRAY | ASSOC)) == ASSOC) { new_refflag = SRF_ENABLE; set &= ~(ARRAY | ASSOC); } if ((clr & (ARRAY | ASSOC)) == ASSOC) { new_refflag = SRF_DISABLE; clr &= ~(ARRAY | ASSOC); } /* check for valid variable name, search for value */ val = skip_varname(var, false); if (val == var) { /* no variable name given */ return (NULL); } if (*val == '[') { if (new_refflag != SRF_NOP) errorf("%s: %s", var, "reference variable can't be an array"); len = array_ref_len(val); if (len == 0) return (NULL); /* * IMPORT is only used when the shell starts up and is * setting up its environment. Allow only simple array * references at this time since parameter/command * substitution is performed on the [expression] which * would be a major security hole. */ if (set & IMPORT) { size_t i; for (i = 1; i < len - 1; i++) if (!ksh_isdigit(val[i])) return (NULL); } val += len; } if (val[0] == '=') { strndupx(tvar, var, val - var, ATEMP); ++val; } else if (set & IMPORT) { /* environment invalid variable name or no assignment */ return (NULL); } else if (val[0] == '+' && val[1] == '=') { strndupx(tvar, var, val - var, ATEMP); val += 2; vappend = true; } else if (val[0] != '\0') { /* other invalid variable names (not from environment) */ return (NULL); } else { /* just varname with no value part nor equals sign */ strdupx(tvar, var, ATEMP); val = NULL; /* handle foo[*] => foo (whole array) mapping for R39b */ len = strlen(tvar); if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' && tvar[len - 1] == ']') tvar[len - 3] = '\0'; } if (new_refflag == SRF_ENABLE) { const char *qval, *ccp; /* bail out on 'nameref foo+=bar' */ if (vappend) errorf("appending not allowed for nameref"); /* find value if variable already exists */ if ((qval = val) == NULL) { varsearch(e->loc, &vp, tvar, hash(tvar)); if (vp == NULL) goto nameref_empty; qval = str_val(vp); } /* check target value for being a valid variable name */ ccp = skip_varname(qval, false); if (ccp == qval) { if (ksh_isdigit(qval[0])) { int c; if (getn(qval, &c)) goto nameref_rhs_checked; } else if (qval[1] == '\0') switch (qval[0]) { case '$': case '!': case '?': case '#': case '-': goto nameref_rhs_checked; } nameref_empty: errorf("%s: %s", var, "empty nameref target"); } len = (*ccp == '[') ? array_ref_len(ccp) : 0; if (ccp[len]) { /* * works for cases "no array", "valid array with * junk after it" and "invalid array"; in the * latter case, len is also 0 and points to '[' */ errorf("%s: %s", qval, "nameref target not a valid parameter name"); } nameref_rhs_checked: /* prevent nameref loops */ while (qval) { if (!strcmp(qval, tvar)) errorf("%s: %s", qval, "expression recurses on parameter"); varsearch(e->loc, &vp, qval, hash(qval)); qval = NULL; if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC)) qval = str_val(vp); } } /* prevent typeset from creating a local PATH/ENV/SHELL */ if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 || strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0)) errorf("%s: %s", tvar, "restricted"); innermost_refflag = new_refflag; vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) : global(tvar); if (new_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC) vp->flag &= ~ASSOC; else if (new_refflag == SRF_ENABLE) { if (vp->flag & ARRAY) { struct tbl *a, *tmp; /* free up entire array */ for (a = vp->u.array; a; ) { tmp = a; a = a->u.array; if (tmp->flag & ALLOC) afree(tmp->val.s, tmp->areap); afree(tmp, tmp->areap); } vp->u.array = NULL; vp->flag &= ~ARRAY; } vp->flag |= ASSOC; } set &= ~(LOCAL|LOCAL_COPY); vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp; /* * only allow export flag to be set; AT&T ksh allows any * attribute to be changed which means it can be truncated or * modified (-L/-R/-Z/-i) */ if ((vpbase->flag & RDONLY) && (val || clr || (set & ~EXPORT))) /* XXX check calls - is error here ok by POSIX? */ errorfx(2, "read-only: %s", tvar); afree(tvar, ATEMP); /* most calls are with set/clr == 0 */ if (set | clr) { bool ok = true; /* * XXX if x[0] isn't set, there will be problems: need * to have one copy of attributes for arrays... */ for (t = vpbase; t; t = t->u.array) { bool fake_assign; char *s = NULL; char *free_me = NULL; fake_assign = (t->flag & ISSET) && (!val || t != vp) && ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) || ((t->flag & INTEGER) && (clr & INTEGER)) || (!(t->flag & INTEGER) && (set & INTEGER))); if (fake_assign) { if (t->flag & INTEGER) { s = str_val(t); free_me = NULL; } else { s = t->val.s + t->type; free_me = (t->flag & ALLOC) ? t->val.s : NULL; } t->flag &= ~ALLOC; } if (!(t->flag & INTEGER) && (set & INTEGER)) { t->type = 0; t->flag &= ~ALLOC; } t->flag = (t->flag | set) & ~clr; /* * Don't change base if assignment is to be * done, in case assignment fails. */ if ((set & INTEGER) && base > 0 && (!val || t != vp)) t->type = base; if (set & (LJUST|RJUST|ZEROFIL)) t->u2.field = field; if (fake_assign) { if (!setstr(t, s, KSH_RETURN_ERROR)) { /* * Somewhat arbitrary action * here: zap contents of * variable, but keep the flag * settings. */ ok = false; if (t->flag & INTEGER) t->flag &= ~ISSET; else { if (t->flag & ALLOC) afree(t->val.s, t->areap); t->flag &= ~(ISSET|ALLOC); t->type = 0; } } if (free_me) afree(free_me, t->areap); } } if (!ok) errorfz(); } if (val != NULL) { char *tval; if (vappend) { tval = shf_smprintf("%s%s", str_val(vp), val); val = tval; } else tval = NULL; if (vp->flag&INTEGER) { /* do not zero base before assignment */ setstr(vp, val, KSH_UNWIND_ERROR | 0x4); /* done after assignment to override default */ if (base > 0) vp->type = base; } else /* setstr can't fail (readonly check already done) */ setstr(vp, val, KSH_RETURN_ERROR | 0x4); if (tval != NULL) afree(tval, ATEMP); } /* only x[0] is ever exported, so use vpbase */ if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) && vpbase->type == 0) exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null); return (vp); }
/* get variable string value */ char * str_val(struct tbl *vp) { char *s; if ((vp->flag&SPECIAL)) getspec(vp); if (!(vp->flag&ISSET)) /* special to dollar() */ s = null; else if (!(vp->flag&INTEGER)) /* string source */ s = vp->val.s + vp->type; else { /* integer source */ mksh_uari_t n; unsigned int base; /** * worst case number length is when base == 2: * 1 (minus) + 2 (base, up to 36) + 1 ('#') + * number of bits in the mksh_uari_t + 1 (NUL) */ char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1]; const char *digits = (vp->flag & UCASEV_AL) ? digits_uc : digits_lc; s = strbuf + sizeof(strbuf); if (vp->flag & INT_U) n = vp->val.u; else n = (vp->val.i < 0) ? -vp->val.u : vp->val.u; base = (vp->type == 0) ? 10U : (unsigned int)vp->type; if (base == 1 && n == 0) base = 2; if (base == 1) { size_t sz = 1; *(s = strbuf) = '1'; s[1] = '#'; if (!UTFMODE || ((n & 0xFF80) == 0xEF80)) /* OPTU-16 -> raw octet */ s[2] = n & 0xFF; else sz = utf_wctomb(s + 2, n); s[2 + sz] = '\0'; } else { *--s = '\0'; do { *--s = digits[n % base]; n /= base; } while (n != 0); if (base != 10) { *--s = '#'; *--s = digits[base % 10]; if (base >= 10) *--s = digits[base / 10]; } if (!(vp->flag & INT_U) && vp->val.i < 0) *--s = '-'; } if (vp->flag & (RJUST|LJUST)) /* case already dealt with */ s = formatstr(vp, s); else strdupx(s, s, ATEMP); } return (s); }
static void setspec(struct tbl *vp) { mksh_ari_u num; char *s; int st; switch ((st = special(vp->name))) { #if HAVE_PERSISTENT_HISTORY case V_HISTFILE: sethistfile(str_val(vp)); return; #endif case V_IFS: setctypes(s = str_val(vp), C_IFS); ifs0 = *s; return; case V_PATH: if (path) afree(path, APERM); s = str_val(vp); strdupx(path, s, APERM); /* clear tracked aliases */ flushcom(true); return; case V_TMPDIR: if (tmpdir) { afree(tmpdir, APERM); tmpdir = NULL; } /* * Use tmpdir iff it is an absolute path, is writable * and searchable and is a directory... */ { struct stat statb; s = str_val(vp); /* LINTED use of access */ if (mksh_abspath(s) && access(s, W_OK|X_OK) == 0 && stat(s, &statb) == 0 && S_ISDIR(statb.st_mode)) strdupx(tmpdir, s, APERM); } return; /* common sub-cases */ case V_COLUMNS: case V_LINES: if (vp->flag & IMPORT) { /* do not touch */ unspecial(vp->name); vp->flag &= ~SPECIAL; return; } /* FALLTHROUGH */ case V_HISTSIZE: case V_LINENO: case V_OPTIND: case V_RANDOM: case V_SECONDS: case V_TMOUT: vp->flag &= ~SPECIAL; if (getint(vp, &num, false) == -1) { s = str_val(vp); if (st != V_RANDOM) errorf("%s: %s: %s", vp->name, "bad number", s); num.u = hash(s); } vp->flag |= SPECIAL; break; default: /* do nothing, do not touch vp at all */ return; } /* process the singular parts of the common cases */ switch (st) { case V_COLUMNS: if (num.i >= MIN_COLS) x_cols = num.i; break; case V_HISTSIZE: sethistsize(num.i); break; case V_LINENO: /* The -1 is because line numbering starts at 1. */ user_lineno = num.u - (mksh_uari_t)current_lineno - 1; break; case V_LINES: if (num.i >= MIN_LINS) x_lins = num.i; break; case V_OPTIND: getopts_reset((int)num.i); break; case V_RANDOM: /* * mksh R39d+ no longer has the traditional repeatability * of $RANDOM sequences, but always retains state */ rndset((unsigned long)num.u); break; case V_SECONDS: { struct timeval tv; mksh_TIME(tv); seconds = tv.tv_sec - num.i; } break; case V_TMOUT: ksh_tmout = num.i >= 0 ? num.i : 0; break; } }