diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/bltins | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/bltins')
-rw-r--r-- | src/cmd/ksh93/bltins/alarm.c | 276 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/cd_pwd.c | 250 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/cflow.c | 117 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/enum.c | 284 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/getopts.c | 199 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/hist.c | 312 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/misc.c | 590 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/mkservice.c | 494 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/print.c | 1058 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/read.c | 802 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/regress.c | 343 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/sleep.c | 235 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/test.c | 667 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/trap.c | 420 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/typeset.c | 1471 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/ulimit.c | 216 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/umask.c | 98 | ||||
-rw-r--r-- | src/cmd/ksh93/bltins/whence.c | 294 |
18 files changed, 8126 insertions, 0 deletions
diff --git a/src/cmd/ksh93/bltins/alarm.c b/src/cmd/ksh93/bltins/alarm.c new file mode 100644 index 0000000..50261c6 --- /dev/null +++ b/src/cmd/ksh93/bltins/alarm.c @@ -0,0 +1,276 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * alarm [-r] [varname [+]when] + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include <error.h> +#include <stak.h> +#include "builtins.h" +#include "FEATURE/time" + +#define R_FLAG 1 +#define L_FLAG 2 + +struct tevent +{ + Namfun_t fun; + Namval_t *node; + Namval_t *action; + struct tevent *next; + long milli; + int flags; + void *timeout; + Shell_t *sh; +}; + +static const char ALARM[] = "alarm"; + +static void trap_timeout(void*); + +/* + * insert timeout item on current given list in sorted order + */ +static void *time_add(struct tevent *item, void *list) +{ + register struct tevent *tp = (struct tevent*)list; + if(!tp || item->milli < tp->milli) + { + item->next = tp; + list = (void*)item; + } + else + { + while(tp->next && item->milli > tp->next->milli) + tp = tp->next; + item->next = tp->next; + tp->next = item; + } + tp = item; + tp->timeout = (void*)sh_timeradd(tp->milli,tp->flags&R_FLAG,trap_timeout,(void*)tp); + return(list); +} + +/* + * delete timeout item from current given list, delete timer + */ +static void *time_delete(register struct tevent *item, void *list) +{ + register struct tevent *tp = (struct tevent*)list; + if(item==tp) + list = (void*)tp->next; + else + { + while(tp && tp->next != item) + tp = tp->next; + if(tp) + tp->next = item->next; + } + if(item->timeout) + timerdel((void*)item->timeout); + return(list); +} + +static void print_alarms(void *list) +{ + register struct tevent *tp = (struct tevent*)list; + while(tp) + { + if(tp->timeout) + { + register char *name = nv_name(tp->node); + if(tp->flags&R_FLAG) + { + double d = tp->milli; + sfprintf(sfstdout,e_alrm1,name,d/1000.); + } + else + sfprintf(sfstdout,e_alrm2,name,nv_getnum(tp->node)); + } + tp = tp->next; + } +} + +static void trap_timeout(void* handle) +{ + register struct tevent *tp = (struct tevent*)handle; + tp->sh->trapnote |= SH_SIGALRM; + if(!(tp->flags&R_FLAG)) + tp->timeout = 0; + tp->flags |= L_FLAG; + tp->sh->sigflag[SIGALRM] |= SH_SIGALRM; + if(sh_isstate(SH_TTYWAIT)) + sh_timetraps(tp->sh); +} + +void sh_timetraps(Shell_t *shp) +{ + register struct tevent *tp, *tpnext; + register struct tevent *tptop; + while(1) + { + shp->sigflag[SIGALRM] &= ~SH_SIGALRM; + tptop= (struct tevent*)shp->st.timetrap; + for(tp=tptop;tp;tp=tpnext) + { + tpnext = tp->next; + if(tp->flags&L_FLAG) + { + tp->flags &= ~L_FLAG; + if(tp->action) + sh_fun(tp->action,tp->node,(char**)0); + tp->flags &= ~L_FLAG; + if(!tp->flags) + { + nv_unset(tp->node); + nv_close(tp->node); + } + } + } + if(!(shp->sigflag[SIGALRM]&SH_SIGALRM)) + break; + } +} + + +/* + * This trap function catches "alarm" actions only + */ +static char *setdisc(Namval_t *np, const char *event, Namval_t* action, Namfun_t + *fp) +{ + register struct tevent *tp = (struct tevent*)fp; + if(!event) + return(action?"":(char*)ALARM); + if(strcmp(event,ALARM)!=0) + { + /* try the next level */ + return(nv_setdisc(np, event, action, fp)); + } + if(action==np) + action = tp->action; + else + tp->action = action; + return(action?(char*)action:""); +} + +/* + * catch assignments and set alarm traps + */ +static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + register struct tevent *tp = (struct tevent*)fp; + register double d; + Shell_t *shp = tp->sh; + if(val) + { + double now; +#ifdef timeofday + struct timeval tmp; + timeofday(&tmp); + now = tmp.tv_sec + 1.e-6*tmp.tv_usec; +#else + now = (double)time(NIL(time_t*)); +#endif /* timeofday */ + nv_putv(np,val,flag,fp); + d = nv_getnum(np); + if(*val=='+') + { + double x = d + now; + nv_putv(np,(char*)&x,NV_INTEGER|NV_DOUBLE,fp); + } + else + d -= now; + tp->milli = 1000*(d+.0005); + if(tp->timeout) + shp->st.timetrap = time_delete(tp,shp->st.timetrap); + if(tp->milli > 0) + shp->st.timetrap = time_add(tp,shp->st.timetrap); + } + else + { + tp = (struct tevent*)nv_stack(np, (Namfun_t*)0); + shp->st.timetrap = time_delete(tp,shp->st.timetrap); + if(tp->action) + nv_close(tp->action); + nv_unset(np); + free((void*)fp); + } +} + +static const Namdisc_t alarmdisc = +{ + sizeof(struct tevent), + putval, + 0, + 0, + setdisc, +}; + +int b_alarm(int argc,char *argv[],Shbltin_t *context) +{ + register int n,rflag=0; + register Namval_t *np; + register struct tevent *tp; + register Shell_t *shp = context->shp; + while (n = optget(argv, sh_optalarm)) switch (n) + { + case 'r': + rflag = R_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + if(argc==0) + { + print_alarms(shp->st.timetrap); + return(0); + } + if(argc!=2) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + np = nv_open(argv[0],shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + nv_setattr(np, NV_DOUBLE); + if(!(tp = newof(NIL(struct tevent*),struct tevent,1,0))) + errormsg(SH_DICT,ERROR_exit(1),e_nospace); + tp->fun.disc = &alarmdisc; + tp->flags = rflag; + tp->node = np; + tp->sh = shp; + nv_stack(np,(Namfun_t*)tp); + nv_putval(np, argv[1], 0); + return(0); +} + diff --git a/src/cmd/ksh93/bltins/cd_pwd.c b/src/cmd/ksh93/bltins/cd_pwd.c new file mode 100644 index 0000000..fb0dcda --- /dev/null +++ b/src/cmd/ksh93/bltins/cd_pwd.c @@ -0,0 +1,250 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * cd [-LP] [dirname] + * cd [-LP] [old] [new] + * pwd [-LP] + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include "defs.h" +#include <stak.h> +#include <error.h> +#include "variables.h" +#include "path.h" +#include "name.h" +#include "builtins.h" +#include <ls.h> + +/* + * Invalidate path name bindings to relative paths + */ +static void rehash(register Namval_t *np,void *data) +{ + Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp; + NOT_USED(data); + if(pp && *pp->name!='/') + _nv_unset(np,0); +} + +int b_cd(int argc, char *argv[],Shbltin_t *context) +{ + register char *dir; + Pathcomp_t *cdpath = 0; + register const char *dp; + register Shell_t *shp = context->shp; + int saverrno=0; + int rval,flag=0; + char *oldpwd; + Namval_t *opwdnod, *pwdnod; + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted+4); + while((rval = optget(argv,sh_optcd))) switch(rval) + { + case 'L': + flag = 0; + break; + case 'P': + flag = 1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + argc -= opt_info.index; + dir = argv[0]; + if(error_info.errors>0 || argc >2) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + oldpwd = (char*)shp->pwd; + opwdnod = (shp->subshell?sh_assignok(OLDPWDNOD,1):OLDPWDNOD); + pwdnod = (shp->subshell?sh_assignok(PWDNOD,1):PWDNOD); + if(argc==2) + dir = sh_substitute(oldpwd,dir,argv[1]); + else if(!dir) + dir = nv_getval(HOME); + else if(*dir == '-' && dir[1]==0) + dir = nv_getval(opwdnod); + if(!dir || *dir==0) + errormsg(SH_DICT,ERROR_exit(1),argc==2?e_subst+4:e_direct); +#if _WINIX + if(*dir != '/' && (dir[1]!=':')) +#else + if(*dir != '/') +#endif /* _WINIX */ + { + if(!(cdpath = (Pathcomp_t*)shp->cdpathlist) && (dp=sh_scoped(shp,CDPNOD)->nvalue.cp)) + { + if(cdpath=path_addpath(shp,(Pathcomp_t*)0,dp,PATH_CDPATH)) + { + shp->cdpathlist = (void*)cdpath; + cdpath->shp = shp; + } + } + if(!oldpwd) + oldpwd = path_pwd(shp,1); + } + if(*dir=='.') + { + /* test for pathname . ./ .. or ../ */ + if(*(dp=dir+1) == '.') + dp++; + if(*dp==0 || *dp=='/') + cdpath = 0; + } + rval = -1; + do + { + dp = cdpath?cdpath->name:""; + cdpath = path_nextcomp(shp,cdpath,dir,0); +#if _WINIX + if(*stakptr(PATH_OFFSET+1)==':' && isalpha(*stakptr(PATH_OFFSET))) + { + *stakptr(PATH_OFFSET+1) = *stakptr(PATH_OFFSET); + *stakptr(PATH_OFFSET)='/'; + } +#endif /* _WINIX */ + if(*stakptr(PATH_OFFSET)!='/') + + { + char *last=(char*)stakfreeze(1); + stakseek(PATH_OFFSET); + stakputs(oldpwd); + /* don't add '/' of oldpwd is / itself */ + if(*oldpwd!='/' || oldpwd[1]) + stakputc('/'); + stakputs(last+PATH_OFFSET); + stakputc(0); + } + if(!flag) + { + register char *cp; + stakseek(PATH_MAX+PATH_OFFSET); +#if SHOPT_FS_3D + if(!(cp = pathcanon(stakptr(PATH_OFFSET),PATH_DOTDOT))) + continue; + /* eliminate trailing '/' */ + while(*--cp == '/' && cp>stakptr(PATH_OFFSET)) + *cp = 0; +#else + if(*(cp=stakptr(PATH_OFFSET))=='/') + if(!pathcanon(cp,PATH_DOTDOT)) + continue; +#endif /* SHOPT_FS_3D */ + } + if((rval=chdir(path_relative(shp,stakptr(PATH_OFFSET)))) >= 0) + goto success; + if(errno!=ENOENT && saverrno==0) + saverrno=errno; + } + while(cdpath); + if(rval<0 && *dir=='/' && *(path_relative(shp,stakptr(PATH_OFFSET)))!='/') + rval = chdir(dir); + /* use absolute chdir() if relative chdir() fails */ + if(rval<0) + { + if(saverrno) + errno = saverrno; + errormsg(SH_DICT,ERROR_system(1),"%s:",dir); + } +success: + if(dir == nv_getval(opwdnod) || argc==2) + dp = dir; /* print out directory for cd - */ + if(flag) + { + dir = stakptr(PATH_OFFSET); + if (!(dir=pathcanon(dir,PATH_PHYSICAL))) + { + dir = stakptr(PATH_OFFSET); + errormsg(SH_DICT,ERROR_system(1),"%s:",dir); + } + stakseek(dir-stakptr(0)); + } + dir = (char*)stakfreeze(1)+PATH_OFFSET; + if(*dp && (*dp!='.'||dp[1]) && strchr(dir,'/')) + sfputr(sfstdout,dir,'\n'); + if(*dir != '/') + return(0); + nv_putval(opwdnod,oldpwd,NV_RDONLY); + if(oldpwd) + free(oldpwd); + flag = strlen(dir); + /* delete trailing '/' */ + while(--flag>0 && dir[flag]=='/') + dir[flag] = 0; + nv_putval(pwdnod,dir,NV_RDONLY); + nv_onattr(pwdnod,NV_NOFREE|NV_EXPORT); + shp->pwd = pwdnod->nvalue.cp; + nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED); + path_newdir(shp,shp->pathlist); + path_newdir(shp,shp->cdpathlist); + return(0); +} + +int b_pwd(int argc, char *argv[],Shbltin_t *context) +{ + register int n, flag = 0; + register char *cp; + register Shell_t *shp = context->shp; + NOT_USED(argc); + while((n = optget(argv,sh_optpwd))) switch(n) + { + case 'L': + flag = 0; + break; + case 'P': + flag = 1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(*(cp = path_pwd(shp,0)) != '/') + errormsg(SH_DICT,ERROR_system(1), e_pwd); + if(flag) + { +#if SHOPT_FS_3D + if(shp->gd->lim.fs3d && (flag = mount(e_dot,NIL(char*),FS3D_GET|FS3D_VIEW,0))>=0) + { + cp = (char*)stakseek(++flag+PATH_MAX); + mount(e_dot,cp,FS3D_GET|FS3D_VIEW|FS3D_SIZE(flag),0); + } + else +#endif /* SHOPT_FS_3D */ + cp = strcpy(stakseek(strlen(cp)+PATH_MAX),cp); + pathcanon(cp,PATH_PHYSICAL); + } + sfputr(sfstdout,cp,'\n'); + return(0); +} + diff --git a/src/cmd/ksh93/bltins/cflow.c b/src/cmd/ksh93/bltins/cflow.c new file mode 100644 index 0000000..c78659f --- /dev/null +++ b/src/cmd/ksh93/bltins/cflow.c @@ -0,0 +1,117 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * break [n] + * continue [n] + * return [n] + * exit [n] + * + * David Korn + * AT&T Labs + * dgk@research.att.com + * + */ + +#include "defs.h" +#include <ast.h> +#include <error.h> +#include "shnodes.h" +#include "builtins.h" + +/* + * return and exit + */ +#if 0 + /* for the dictionary generator */ + int b_exit(int n, register char *argv[],Shbltin_t *context){} +#endif +int b_return(register int n, register char *argv[],Shbltin_t *context) +{ + register char *arg; + register Shell_t *shp = context->shp; + struct checkpt *pp = (struct checkpt*)shp->jmplist; + const char *options = (**argv=='r'?sh_optreturn:sh_optexit); + while((n = optget(argv,options))) switch(n) + { + case ':': + if(!strmatch(argv[opt_info.index],"[+-]+([0-9])")) + errormsg(SH_DICT,2, "%s", opt_info.arg); + goto done; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } +done: + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + pp->mode = (**argv=='e'?SH_JMPEXIT:SH_JMPFUN); + argv += opt_info.index; + n = (((arg= *argv)?(int)strtol(arg, (char**)0, 10)&SH_EXITMASK:shp->oldexit)); + /* return outside of function, dotscript and profile is exit */ + if(shp->fn_depth==0 && shp->dot_depth==0 && !sh_isstate(SH_PROFILE)) + pp->mode = SH_JMPEXIT; + sh_exit(shp->savexit=n); + return(1); +} + + +/* + * break and continue + */ +#if 0 + /* for the dictionary generator */ + int b_continue(int n, register char *argv[],Shbltin_t *context){} +#endif +int b_break(register int n, register char *argv[],Shbltin_t *context) +{ + char *arg; + register int cont= **argv=='c'; + register Shell_t *shp = context->shp; + while((n = optget(argv,cont?sh_optcont:sh_optbreak))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + n=1; + if(arg= *argv) + { + n = strtol(arg,&arg,10); + if(n<=0 || *arg) + errormsg(SH_DICT,ERROR_exit(1),e_nolabels,*argv); + } + if(shp->st.loopcnt) + { + shp->st.execbrk = shp->st.breakcnt = n; + if(shp->st.breakcnt > shp->st.loopcnt) + shp->st.breakcnt = shp->st.loopcnt; + if(cont) + shp->st.breakcnt = -shp->st.breakcnt; + } + return(0); +} + diff --git a/src/cmd/ksh93/bltins/enum.c b/src/cmd/ksh93/bltins/enum.c new file mode 100644 index 0000000..5cc749c --- /dev/null +++ b/src/cmd/ksh93/bltins/enum.c @@ -0,0 +1,284 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +#include <shell.h> + +static const char enum_usage[] = +"[-?@(#)$Id: enum (AT&T Research) 2008-01-08 $\n]" +USAGE_LICENSE +"[+NAME?enum - create an enumeration type]" +"[+DESCRIPTION?\benum\b is a declaration command that creates an enumeration " + "type \atypename\a that can only store any one of the values in the indexed " + "array variable \atypename\a.]" +"[+?If the list of \avalue\as is omitted, then \atypename\a must name an " + "indexed array variable with at least two elements.]" +"[i:ignorecase?The values are case insensitive.]" +"\n" +"\n\atypename\a[\b=(\b \avalue\a ... \b)\b]\n" +"\n" +"[+EXIT STATUS]" + "{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" + "}" +"[+SEE ALSO?\bksh\b(1), \btypeset\b(1).]" +; + +static const char enum_type[] = +"[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-01-08 $\n]" +USAGE_LICENSE +"[+NAME?\f?\f - create an instance of type \b\f?\f\b]" +"[+DESCRIPTION?\b\f?\f\b creates a variable for each \aname\a with " + "enumeration type \b\f?\f\b where \b\f?\f\b is a type that has been " + "created with the \benum\b(1) command.]" +"[+?The variable can have one of the following values\fvalues\f. " + "The the values are \fcase\fcase sensitive.]" +"[+?If \b=\b\avalue\a is omitted, the default is \fdefault\f.]" +"[+?If no \aname\as are specified then the names and values of all " + "variables of this type are written to standard output.]" +"[+?\b\f?\f\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a.]" +"[r?Enables readonly. Once enabled, the value cannot be changed or unset.]" +"[a?index array. Each \aname\a will converted to an index " + "array of type \b\f?\f\b. If a variable already exists, the current " + "value will become index \b0\b.]" +"[A?Associative array. Each \aname\a will converted to an associate " + "array of type \b\f?\f\b. If a variable already exists, the current " + "value will become subscript \b0\b.]" +"[h]:[string?Used within a type definition to provide a help string " + "for variable \aname\a. Otherwise, it is ignored.]" +"[S?Used with a type definition to indicate that the variable is shared by " + "each instance of the type. When used inside a function defined " + "with the \bfunction\b reserved word, the specified variables " + "will have function static scope. Otherwise, the variable is " + "unset prior to processing the assignment list.]" +#if 0 +"[p?Causes the output to be in a form of \b\f?\f\b commands that can be " + "used as input to the shell to recreate the current type of " + "these variables.]" +#endif +"\n" +"\n[name[=value]...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\benum\b(1), \btypeset\b(1)]" +; + +struct Enum +{ + Namfun_t hdr; + short nelem; + short iflag; + const char *values[1]; +}; + +static int enuminfo(Opt_t* op, Sfio_t *out, const char *str, Optdisc_t *fp) +{ + Namval_t *np; + struct Enum *ep; + int n=0; + const char *v; + np = *(Namval_t**)(fp+1); + ep = (struct Enum*)np->nvfun; + if(strcmp(str,"default")==0) +#if 0 + sfprintf(out,"\b%s\b%c",ep->values[0],0); +#else + sfprintf(out,"\b%s\b",ep->values[0]); +#endif + else if(strcmp(str,"case")==0) + { + if(ep->iflag) + sfprintf(out,"not "); + } + else while(v=ep->values[n++]) + { + sfprintf(out,", \b%s\b",v); + } + return(0); +} + +static Namfun_t *clone_enum(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) +{ + struct Enum *ep, *pp=(struct Enum*)fp; + ep = newof(0,struct Enum,1,pp->nelem*sizeof(char*)); + memcpy((void*)ep,(void*)pp,sizeof(struct Enum)+pp->nelem*sizeof(char*)); + return(&ep->hdr); +} + +static void put_enum(Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + struct Enum *ep = (struct Enum*)fp; + register const char *v; + unsigned short i=0, n; + if(!val) + { + nv_putv(np, val, flags,fp); + nv_disc(np,&ep->hdr,NV_POP); + if(!ep->hdr.nofree) + free((void*)ep); + return; + } + if(flags&NV_INTEGER) + { + nv_putv(np,val,flags,fp); + return; + } + while(v=ep->values[i]) + { + if(ep->iflag) + n = strcasecmp(v,val); + else + n = strcmp(v,val); + if(n==0) + { + nv_putv(np, (char*)&i, NV_UINT16, fp); + return; + } + i++; + } + if(nv_isattr(np,NV_NOFREE)) + error(ERROR_exit(1), "%s: invalid value %s",nv_name(np),val); +} + +static char* get_enum(register Namval_t* np, Namfun_t *fp) +{ + static char buff[6]; + struct Enum *ep = (struct Enum*)fp; + long n = nv_getn(np,fp); + if(n < ep->nelem) + return((char*)ep->values[n]); + sfsprintf(buff,sizeof(buff),"%u%c",n,0); + return(buff); +} + +static Sfdouble_t get_nenum(register Namval_t* np, Namfun_t *fp) +{ + return(nv_getn(np,fp)); +} + +const Namdisc_t ENUM_disc = { 0, put_enum, get_enum, get_nenum, 0,0,clone_enum }; + +#ifdef STANDALONE +static int enum_create(int argc, char** argv, Shbltin_t *context) +#else +int b_enum(int argc, char** argv, Shbltin_t *context) +#endif +{ + int sz,i,n,iflag = 0; + Namval_t *np, *tp; + Namarr_t *ap; + char *cp,*sp; + struct Enum *ep; + Shell_t *shp = context->shp; + struct { + Optdisc_t opt; + Namval_t *np; + } optdisc; + + cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); + for (;;) + { + switch (optget(argv, enum_usage)) + { + case 'i': + iflag = 'i'; + continue; + case '?': + error(ERROR_USAGE|4, "%s", opt_info.arg); + break; + case ':': + error(2, "%s", opt_info.arg); + break; + } + break; + } + argv += opt_info.index; + if (error_info.errors || !*argv || *(argv + 1)) + { + error(ERROR_USAGE|2, "%s", optusage(NiL)); + return 1; + } + while(cp = *argv++) + { + if(!(np = nv_open(cp, (void*)0, NV_VARNAME|NV_NOADD)) || !(ap=nv_arrayptr(np)) || ap->fun || (sz=ap->nelem&(((1L<<ARRAY_BITS)-1))) < 2) + error(ERROR_exit(1), "%s must name an array containing at least two elements",cp); + n = staktell(); + sfprintf(stkstd,"%s.%s%c",NV_CLASS,np->nvname,0); + tp = nv_open(stakptr(n), shp->var_tree, NV_VARNAME); + stakseek(n); + n = sz; + i = 0; + nv_onattr(tp, NV_UINT16); + nv_putval(tp, (char*)&i, NV_INTEGER); + nv_putsub(np, (char*)0, ARRAY_SCAN); + do + { + sz += strlen(nv_getval(np)); + } + while(nv_nextsub(np)); + sz += n*sizeof(char*); + if(!(ep = newof(0,struct Enum,1,sz))) + error(ERROR_system(1), "out of space"); + ep->iflag = iflag; + ep->nelem = n; + cp = (char*)&ep->values[n+1]; + nv_putsub(np, (char*)0, ARRAY_SCAN); + ep->values[n] = 0; + i = 0; + do + { + ep->values[i++] = cp; + sp = nv_getval(np); + n = strlen(sp); + memcpy(cp,sp,n+1); + cp += n+1; + } + while(nv_nextsub(np)); + ep->hdr.dsize = sizeof(struct Enum)+sz; + ep->hdr.disc = &ENUM_disc; + ep->hdr.type = tp; + nv_onattr(tp, NV_RDONLY); + nv_disc(tp, &ep->hdr,NV_FIRST); + memset(&optdisc,0,sizeof(optdisc)); + optdisc.opt.infof = enuminfo; + optdisc.np = tp; + nv_addtype(tp, enum_type, &optdisc.opt, sizeof(optdisc)); + } + return error_info.errors != 0; +} + +#ifdef STANDALONE +void lib_init(int flag, void* context) +{ + Shell_t *shp = ((Shbltin_t*)context)->shp; + Namval_t *mp,*bp; + if(flag) + return; + bp = sh_addbuiltin("Enum", enum_create, (void*)0); + mp = nv_search("typeset",shp->bltin_tree,0); + nv_onattr(bp,nv_isattr(mp,NV_PUBLIC)); +} +#endif diff --git a/src/cmd/ksh93/bltins/getopts.c b/src/cmd/ksh93/bltins/getopts.c new file mode 100644 index 0000000..8b92b73 --- /dev/null +++ b/src/cmd/ksh93/bltins/getopts.c @@ -0,0 +1,199 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * getopts optstring name [arg...] + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include "defs.h" +#include "variables.h" +#include <error.h> +#include <nval.h> +#include "builtins.h" + +static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) +{ + Shell_t *shp = *(Shell_t**)(dp+1); + Stk_t *stkp = shp->stk; + if(nv_search(s,shp->fun_tree,0)) + { + int savtop = stktell(stkp); + char *savptr = stkfreeze(stkp,0); + sfputc(stkp,'$'); + sfputc(stkp,'('); + sfputr(stkp,s,')'); + sfputr(sp,sh_mactry(shp,stkfreeze(stkp,1)),-1); + stkset(stkp,savptr,savtop); + } + return(1); +} + +int b_getopts(int argc,char *argv[],Shbltin_t *context) +{ + register char *options=error_info.context->id; + register Namval_t *np; + register int flag, mode; + register Shell_t *shp = context->shp; + char value[2], key[2]; + int jmpval,extended; + volatile int r= -1; + struct checkpt buff, *pp; + struct { + Optdisc_t hdr; + Shell_t *sh; + } disc; + memset(&disc, 0, sizeof(disc)); + disc.hdr.version = OPT_VERSION; + disc.hdr.infof = infof; + disc.sh = shp; + value[1] = 0; + key[1] = 0; + while((flag = optget(argv,sh_optgetopts))) switch(flag) + { + case 'a': + options = opt_info.arg; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + argc -= opt_info.index; + if(error_info.errors || argc<2) + errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); + error_info.context->flags |= ERROR_SILENT; + error_info.id = options; + options = argv[0]; + np = nv_open(argv[1],shp->var_tree,NV_NOASSIGN|NV_VARNAME); + if(argc>2) + { + argv +=1; + argc -=1; + } + else + { + argv = shp->st.dolv; + argc = shp->st.dolc; + } + opt_info.index = shp->st.optindex; + opt_info.offset = shp->st.optchar; + if(mode= (*options==':')) + options++; + extended = *options=='\n' && *(options+1)=='[' || *options=='[' && *(options+1)=='-'; + sh_pushcontext(shp,&buff,1); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval) + { + sh_popcontext(shp,&buff); + shp->st.opterror = 1; + if(r==0) + return(2); + pp = (struct checkpt*)shp->jmplist; + pp->mode = SH_JMPERREXIT; + sh_exit(2); + } + opt_info.disc = &disc.hdr; + switch(opt_info.index>=0 && opt_info.index<=argc?(opt_info.num= LONG_MIN,flag=optget(argv,options)):0) + { + case '?': + if(mode==0) + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + opt_info.option[1] = '?'; + /* FALL THRU */ + case ':': + key[0] = opt_info.option[1]; + if(strmatch(opt_info.arg,"*unknown*")) + flag = '?'; + if(mode) + opt_info.arg = key; + else + { + errormsg(SH_DICT,2, "%s", opt_info.arg); + opt_info.arg = 0; + flag = '?'; + } + *(options = value) = flag; + shp->st.opterror = 1; + if (opt_info.offset != 0 && !argv[opt_info.index][opt_info.offset]) + { + opt_info.offset = 0; + opt_info.index++; + } + break; + case 0: + if(shp->st.opterror) + { + char *com[2]; + com[0] = "-?"; + com[1] = 0; + flag = opt_info.index; + opt_info.index = 0; + optget(com,options); + opt_info.index = flag; + if(!mode && strchr(options,' ')) + errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); + } + opt_info.arg = 0; + options = value; + *options = '?'; + r=1; + opt_info.offset = 0; + break; + default: + options = opt_info.option + (*opt_info.option!='+'); + } + if(r<0) + r = 0; + error_info.context->flags &= ~ERROR_SILENT; + shp->st.optindex = opt_info.index; + shp->st.optchar = opt_info.offset; + nv_putval(np, options, 0); + nv_close(np); + np = nv_open(nv_name(OPTARGNOD),shp->var_tree,NV_NOSCOPE); + if(opt_info.num == LONG_MIN) + nv_putval(np, opt_info.arg, NV_RDONLY); + else if (opt_info.arg && opt_info.num > 0 && isalpha((char)opt_info.num) && !isdigit(opt_info.arg[0]) && opt_info.arg[0] != '-' && opt_info.arg[0] != '+') + { + key[0] = (char)opt_info.num; + key[1] = 0; + nv_putval(np, key, NV_RDONLY); + } + else if(extended) + { + Sfdouble_t d; + d = opt_info.number; + nv_putval(np, (char*)&d, NV_LDOUBLE|NV_RDONLY); + } + else + nv_putval(np, opt_info.arg, NV_RDONLY); + nv_close(np); + sh_popcontext(shp,&buff); + opt_info.disc = 0; + return(r); +} + diff --git a/src/cmd/ksh93/bltins/hist.c b/src/cmd/ksh93/bltins/hist.c new file mode 100644 index 0000000..1c2bc72 --- /dev/null +++ b/src/cmd/ksh93/bltins/hist.c @@ -0,0 +1,312 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +#include "defs.h" +#include <stak.h> +#include <ls.h> +#include <error.h> +#include "variables.h" +#include "io.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#if SHOPT_HISTEXPAND +# include "edit.h" +#endif + +#define HIST_RECURSE 5 + +static void hist_subst(const char*, int fd, char*); + +#if 0 + /* for the benefit of the dictionary generator */ + int b_fc(int argc,char *argv[], Shbltin_t *context){} +#endif +int b_hist(int argc,char *argv[], Shbltin_t *context) +{ + register History_t *hp; + register char *arg; + register int flag,fdo; + register Shell_t *shp = context->shp; + Sfio_t *outfile; + char *fname; + int range[2], incr, index2, indx= -1; + char *edit = 0; /* name of editor */ + char *replace = 0; /* replace old=new */ + int lflag = 0, nflag = 0, rflag = 0; +#if SHOPT_HISTEXPAND + int pflag = 0; +#endif + Histloc_t location; + NOT_USED(argc); + if(!sh_histinit((void*)shp)) + errormsg(SH_DICT,ERROR_system(1),e_histopen); + hp = shp->gd->hist_ptr; + while((flag = optget(argv,sh_opthist))) switch(flag) + { + case 'e': + edit = opt_info.arg; + break; + case 'n': + nflag++; + break; + case 'l': + lflag++; + break; + case 'r': + rflag++; + break; + case 's': + edit = "-"; + break; +#if SHOPT_HISTEXPAND + case 'p': + pflag++; + break; +#endif + case 'N': + if(indx<=0) + { + if((flag = hist_max(hp) - opt_info.num-1) < 0) + flag = 1; + range[++indx] = flag; + break; + } + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += (opt_info.index-1); +#if SHOPT_HISTEXPAND + if(pflag) + { + hist_cancel(hp); + pflag = 0; + while(arg=argv[1]) + { + flag = hist_expand(arg,&replace); + if(!(flag & HIST_ERROR)) + sfputr(sfstdout, replace, '\n'); + else + pflag = 1; + if(replace) + free(replace); + argv++; + } + return pflag; + } +#endif + flag = indx; + while(flag<1 && (arg=argv[1])) + { + /* look for old=new argument */ + if(!replace && strchr(arg+1,'=')) + { + replace = arg; + argv++; + continue; + } + else if(isdigit(*arg) || *arg == '-') + { + /* see if completely numeric */ + do arg++; + while(isdigit(*arg)); + if(*arg==0) + { + arg = argv[1]; + range[++flag] = (int)strtol(arg, (char**)0, 10); + if(*arg == '-') + range[flag] += (hist_max(hp)-1); + argv++; + continue; + } + } + /* search for last line starting with string */ + location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1); + if((range[++flag] = location.hist_command) < 0) + errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]); + argv++; + } + if(flag <0) + { + /* set default starting range */ + if(lflag) + { + flag = hist_max(hp)-16; + if(flag<1) + flag = 1; + } + else + flag = hist_max(hp)-2; + range[0] = flag; + flag = 0; + } + index2 = hist_min(hp); + if(range[0]<index2) + range[0] = index2; + if(flag==0) + /* set default termination range */ + range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]); + if(range[1]>=(flag=(hist_max(hp) - !lflag))) + range[1] = flag; + /* check for valid ranges */ + if(range[1]<index2 || range[0]>=flag) + errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]); + if(edit && *edit=='-' && range[0]!=range[1]) + errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg); + /* now list commands from range[rflag] to range[1-rflag] */ + incr = 1; + flag = rflag>0; + if(range[1-flag] < range[flag]) + incr = -1; + if(lflag) + { + outfile = sfstdout; + arg = "\n\t"; + } + else + { + if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*)))) + errormsg(SH_DICT,ERROR_exit(1),e_create,""); + if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_create,fname); + outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE); + arg = "\n"; + nflag++; + } + while(1) + { + if(nflag==0) + sfprintf(outfile,"%d\t",range[flag]); + else if(lflag) + sfputc(outfile,'\t'); + hist_list(shp->gd->hist_ptr,outfile,hist_tell(shp->gd->hist_ptr,range[flag]),0,arg); + if(lflag) + sh_sigcheck(shp); + if(range[flag] == range[1-flag]) + break; + range[flag] += incr; + } + if(lflag) + return(0); + sfclose(outfile); + hist_eof(hp); + arg = edit; + if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD)))) + { + arg = (char*)e_defedit; + if(*arg!='/') + errormsg(SH_DICT,ERROR_exit(1),"ed not found set FCEDIT"); + } +#ifdef apollo + /* + * Code to support the FC using the pad editor. + * Exampled of how to use: HISTEDIT=pad + */ + if (strcmp (arg, "pad") == 0) + { + extern int pad_create(char*); + sh_close(fdo); + fdo = pad_create(fname); + pad_wait(fdo); + unlink(fname); + strcat(fname, ".bak"); + unlink(fname); + lseek(fdo,(off_t)0,SEEK_SET); + } + else + { +#endif /* apollo */ + if(*arg != '-') + { + char *com[3]; + com[0] = arg; + com[1] = fname; + com[2] = 0; + error_info.errors = sh_eval(sh_sfeval(com),0); + } + fdo = sh_chkopen(fname); + unlink(fname); + free((void*)fname); +#ifdef apollo + } +#endif /* apollo */ + /* don't history fc itself unless forked */ + error_info.flags |= ERROR_SILENT; + if(!sh_isstate(SH_FORKED)) + hist_cancel(hp); + sh_onstate(SH_HISTORY); + sh_onstate(SH_VERBOSE); /* echo lines as read */ + if(replace) + hist_subst(error_info.id,fdo,replace); + else if(error_info.errors == 0) + { + char buff[IOBSIZE+1]; + Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ); + /* read in and run the command */ + if(shp->hist_depth++ > HIST_RECURSE) + errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history"); + sh_eval(iop,1); + shp->hist_depth--; + } + else + { + sh_close(fdo); + if(!sh_isoption(SH_VERBOSE)) + sh_offstate(SH_VERBOSE); + sh_offstate(SH_HISTORY); + } + return(shp->exitval); +} + + +/* + * given a file containing a command and a string of the form old=new, + * execute the command with the string old replaced by new + */ + +static void hist_subst(const char *command,int fd,char *replace) +{ + register char *newp=replace; + register char *sp; + register int c; + off_t size; + char *string; + while(*++newp != '='); /* skip to '=' */ + if((size = lseek(fd,(off_t)0,SEEK_END)) < 0) + return; + lseek(fd,(off_t)0,SEEK_SET); + c = (int)size; + string = stakalloc(c+1); + if(read(fd,string,c)!=c) + return; + string[c] = 0; + *newp++ = 0; + if((sp=sh_substitute(string,replace,newp))==0) + errormsg(SH_DICT,ERROR_exit(1),e_subst,command); + *(newp-1) = '='; + sh_eval(sfopen(NIL(Sfio_t*),sp,"s"),1); +} + diff --git a/src/cmd/ksh93/bltins/misc.c b/src/cmd/ksh93/bltins/misc.c new file mode 100644 index 0000000..d810a05 --- /dev/null +++ b/src/cmd/ksh93/bltins/misc.c @@ -0,0 +1,590 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * exec [arg...] + * eval [arg...] + * jobs [-lnp] [job...] + * login [arg...] + * let expr... + * . file [arg...] + * :, true, false + * vpath [top] [base] + * vmap [top] [base] + * wait [job...] + * shift [n] + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include "variables.h" +#include "shnodes.h" +#include "path.h" +#include "io.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#include "jobs.h" + +#define DOTMAX MAXDEPTH /* maximum level of . nesting */ + +static void noexport(Namval_t*,void*); + +struct login +{ + Shell_t *sh; + int clear; + char *arg0; +}; + +int b_exec(int argc,char *argv[], Shbltin_t *context) +{ + struct login logdata; + register int n; + logdata.clear = 0; + logdata.arg0 = 0; + logdata.sh = context->shp; + logdata.sh->st.ioset = 0; + while (n = optget(argv, sh_optexec)) switch (n) + { + case 'a': + logdata.arg0 = opt_info.arg; + argc = 0; + break; + case 'c': + logdata.clear=1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(*argv) + B_login(0,argv,(Shbltin_t*)&logdata); + return(0); +} + +static void noexport(register Namval_t* np, void *data) +{ + NOT_USED(data); + nv_offattr(np,NV_EXPORT); +} + +int B_login(int argc,char *argv[],Shbltin_t *context) +{ + struct checkpt *pp; + register struct login *logp=0; + register Shell_t *shp; + const char *pname; + if(argc) + shp = context->shp; + else + { + logp = (struct login*)context; + shp = logp->sh; + } + pp = (struct checkpt*)shp->jmplist; + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[0]); + else + { + register struct argnod *arg=shp->envlist; + register Namval_t* np; + register char *cp; + if(shp->subshell && !shp->subshare) + sh_subfork(); + if(logp && logp->clear) + { +#ifdef _ENV_H + env_close(shp->env); + shp->env = env_open((char**)0,3); +#else + nv_scan(shp->var_tree,noexport,0,NV_EXPORT,NV_EXPORT); +#endif + } + while(arg) + { + if((cp=strchr(arg->argval,'=')) && + (*cp=0,np=nv_search(arg->argval,shp->var_tree,0))) + { + nv_onattr(np,NV_EXPORT); + sh_envput(shp->env,np); + } + if(cp) + *cp = '='; + arg=arg->argnxt.ap; + } + pname = argv[0]; + if(logp && logp->arg0) + argv[0] = logp->arg0; +#ifdef JOBS + if(job_close(shp) < 0) + return(1); +#endif /* JOBS */ + /* force bad exec to terminate shell */ + pp->mode = SH_JMPEXIT; + sh_sigreset(2); + sh_freeup(shp); + path_exec(shp,pname,argv,NIL(struct argnod*)); + sh_done(shp,0); + } + return(1); +} + +int b_let(int argc,char *argv[],Shbltin_t *context) +{ + register int r; + register char *arg; + Shell_t *shp = context->shp; + NOT_USED(argc); + while (r = optget(argv,sh_optlet)) switch (r) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || !*argv) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + while(arg= *argv++) + r = !sh_arith(shp,arg); + return(r); +} + +int b_eval(int argc,char *argv[], Shbltin_t *context) +{ + register int r; + register Shell_t *shp = context->shp; + NOT_USED(argc); + while (r = optget(argv,sh_opteval)) switch (r) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + if(*argv && **argv) + { + sh_offstate(SH_MONITOR); + sh_eval(sh_sfeval(argv),0); + } + return(shp->exitval); +} + +int b_dot_cmd(register int n,char *argv[],Shbltin_t *context) +{ + register char *script; + register Namval_t *np; + register int jmpval; + register Shell_t *shp = context->shp; + struct sh_scoped savst, *prevscope = shp->st.self; + char *filename=0; + int fd; + struct dolnod *argsave=0, *saveargfor; + struct checkpt buff; + Sfio_t *iop=0; + short level; + while (n = optget(argv,sh_optdot)) switch (n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg); + return(2); + } + argv += opt_info.index; + script = *argv; + if(error_info.errors || !script) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(shp->dot_depth+1 > DOTMAX) + errormsg(SH_DICT,ERROR_exit(1),e_toodeep,script); + if(!(np=shp->posix_fun)) + { + /* check for KornShell style function first */ + np = nv_search(script,shp->fun_tree,0); + if(np && is_afunction(np) && !nv_isattr(np,NV_FPOSIX)) + { + if(!np->nvalue.ip) + { + path_search(shp,script,NIL(Pathcomp_t**),0); + if(np->nvalue.ip) + { + if(nv_isattr(np,NV_FPOSIX)) + np = 0; + } + else + errormsg(SH_DICT,ERROR_exit(1),e_found,script); + } + } + else + np = 0; + if(!np) + { + if((fd=path_open(shp,script,path_get(shp,script))) < 0) + errormsg(SH_DICT,ERROR_system(1),e_open,script); + filename = path_fullname(shp,stkptr(shp->stk,PATH_OFFSET)); + } + } + *prevscope = shp->st; + shp->st.lineno = np?((struct functnod*)nv_funtree(np))->functline:1; + shp->st.var_local = shp->st.save_tree = shp->var_tree; + if(filename) + { + shp->st.filename = filename; + shp->st.lineno = 1; + } + level = shp->fn_depth+shp->dot_depth+1; + nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16); + shp->st.prevst = prevscope; + shp->st.self = &savst; + shp->topscope = (Shscope_t*)shp->st.self; + prevscope->save_tree = shp->var_tree; + if(np) + shp->st.filename = np->nvalue.rp->fname; + nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE); + shp->posix_fun = 0; + if(np || argv[1]) + argsave = sh_argnew(shp,argv,&saveargfor); + sh_pushcontext(shp,&buff,SH_JMPDOT); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval == 0) + { + shp->dot_depth++; + if(np) + sh_exec((Shnode_t*)(nv_funtree(np)),sh_isstate(SH_ERREXIT)); + else + { + char buff[IOBSIZE+1]; + iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fd,SF_READ); + sh_offstate(SH_NOFORK); + sh_eval(iop,sh_isstate(SH_PROFILE)?SH_FUNEVAL:0); + } + } + sh_popcontext(shp,&buff); + if(!np) + free((void*)shp->st.filename); + shp->dot_depth--; + if((np || argv[1]) && jmpval!=SH_JMPSCRIPT) + sh_argreset(shp,argsave,saveargfor); + else + { + prevscope->dolc = shp->st.dolc; + prevscope->dolv = shp->st.dolv; + } + if (shp->st.self != &savst) + *shp->st.self = shp->st; + /* only restore the top Shscope_t portion for posix functions */ + memcpy((void*)&shp->st, (void*)prevscope, sizeof(Shscope_t)); + shp->topscope = (Shscope_t*)prevscope; + nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE); + if(jmpval && jmpval!=SH_JMPFUN) + siglongjmp(*shp->jmplist,jmpval); + return(shp->exitval); +} + +/* + * null, true command + */ +int b_true(int argc,register char *argv[],Shbltin_t *context) +{ + NOT_USED(argc); + NOT_USED(argv[0]); + NOT_USED(context); + return(0); +} + +/* + * false command + */ +int b_false(int argc,register char *argv[], Shbltin_t *context) +{ + NOT_USED(argc); + NOT_USED(argv[0]); + NOT_USED(context); + return(1); +} + +int b_shift(register int n, register char *argv[], Shbltin_t *context) +{ + register char *arg; + register Shell_t *shp = context->shp; + while((n = optget(argv,sh_optshift))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + n = ((arg= *argv)?(int)sh_arith(shp,arg):1); + if(n<0 || shp->st.dolc<n) + errormsg(SH_DICT,ERROR_exit(1),e_number,arg); + else + { + shp->st.dolv += n; + shp->st.dolc -= n; + } + return(0); +} + +int b_wait(int n,register char *argv[],Shbltin_t *context) +{ + register Shell_t *shp = context->shp; + while((n = optget(argv,sh_optwait))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + job_bwait(argv); + return(shp->exitval); +} + +#ifdef JOBS +# if 0 + /* for the dictionary generator */ + int b_fg(int n,char *argv[],Shbltin_t *context){} + int b_disown(int n,char *argv[],Shbltin_t *context){} +# endif +int b_bg(register int n,register char *argv[],Shbltin_t *context) +{ + register int flag = **argv; + register Shell_t *shp = context->shp; + register const char *optstr = sh_optbg; + if(*argv[0]=='f') + optstr = sh_optfg; + else if(*argv[0]=='d') + optstr = sh_optdisown; + while((n = optget(argv,optstr))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + if(!sh_isoption(SH_MONITOR) || !job.jobcontrol) + { + if(sh_isstate(SH_INTERACTIVE)) + errormsg(SH_DICT,ERROR_exit(1),e_no_jctl); + return(1); + } + if(flag=='d' && *argv==0) + argv = (char**)0; + if(job_walk(sfstdout,job_switch,flag,argv)) + errormsg(SH_DICT,ERROR_exit(1),e_no_job); + return(shp->exitval); +} + +int b_jobs(register int n,char *argv[],Shbltin_t *context) +{ + register int flag = 0; + register Shell_t *shp = context->shp; + while((n = optget(argv,sh_optjobs))) switch(n) + { + case 'l': + flag = JOB_LFLAG; + break; + case 'n': + flag = JOB_NFLAG; + break; + case 'p': + flag = JOB_PFLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(*argv==0) + argv = (char**)0; + if(job_walk(sfstdout,job_list,flag,argv)) + errormsg(SH_DICT,ERROR_exit(1),e_no_job); + job_wait((pid_t)0); + return(shp->exitval); +} +#endif + +#ifdef _cmd_universe +/* + * There are several universe styles that are masked by the getuniv(), + * setuniv() calls. + */ +int b_universe(int argc, char *argv[],Shbltin_t *context) +{ + register char *arg; + register int n; + NOT_USED(context); + while((n = optget(argv,sh_optuniverse))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + argv += opt_info.index; + argc -= opt_info.index; + if(error_info.errors || argc>1) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(arg = argv[0]) + { + if(!astconf("UNIVERSE",0,arg)) + errormsg(SH_DICT,ERROR_exit(1), e_badname,arg); + } + else + { + if(!(arg=astconf("UNIVERSE",0,0))) + errormsg(SH_DICT,ERROR_exit(1),e_nouniverse); + else + sfputr(sfstdout,arg,'\n'); + } + return(0); +} +#endif /* cmd_universe */ + +#if SHOPT_FS_3D +#if _UWIN +#include <sys/mount.h> +#endif +# if 0 + /* for the dictionary generator */ + int b_vmap(int argc,char *argv[], Shbltin_t *context){} +# endif + int b_vpath(register int argc,char *argv[], Shbltin_t *context) + { + register int flag, n; + register const char *optstr; + register char *vend; + register Shell_t *shp = context->shp; + if(argv[0][1]=='p') + { + optstr = sh_optvpath; + flag = FS3D_VIEW; + } + else + { + optstr = sh_optvmap; + flag = FS3D_VERSION; + } + while(n = optget(argv, optstr)) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); +#ifdef MS_3D + flag |= MS_3D; +#else + if(!shp->gd->lim.fs3d) + goto failed; +#endif + argv += opt_info.index; + argc -= opt_info.index; + switch(argc) + { + case 0: + case 1: + flag |= FS3D_GET; + if((n = mount(*argv,(char*)0,flag,0)) >= 0) + { + vend = stkalloc(shp->stk,++n); + n = mount(*argv,vend,flag|FS3D_SIZE(n),0); + } + if(n < 0) + goto failed; + if(argc==1) + { + sfprintf(sfstdout,"%s\n",vend); + break; + } + n = 0; + while(flag = *vend++) + { + if(flag==' ') + { + flag = e_sptbnl[n+1]; + n = !n; + } + sfputc(sfstdout,flag); + } + if(n) + sfputc(sfstdout,'\n'); + break; + default: + if((argc&1)) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + /*FALLTHROUGH*/ + case 2: + if(shp->subshell && !shp->subshare) + sh_subfork(); + for(n=0;n<argc;n+=2) + if(mount(argv[n+1],argv[n],flag,0)<0) + goto failed; + } + return(0); +failed: + errormsg(SH_DICT,ERROR_exit(1),(argc>1)?e_cantset:e_cantget,(flag&FS3D_VIEW)?e_mapping:e_versions); + return(1); + } +#endif /* SHOPT_FS_3D */ + diff --git a/src/cmd/ksh93/bltins/mkservice.c b/src/cmd/ksh93/bltins/mkservice.c new file mode 100644 index 0000000..0cbba25 --- /dev/null +++ b/src/cmd/ksh93/bltins/mkservice.c @@ -0,0 +1,494 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * mkservice varname pathname + * eloop [-t timeout] + * Written by David Korn + * AT&T Labs + */ + +static const char mkservice_usage[] = +"[-?\n@(#)$Id: mkservice (AT&T Research) 2001-06-13 $\n]" +USAGE_LICENSE +"[+NAME? mkservice - create a shell server ]" +"[+DESCRIPTION?\bmkservice\b creates a tcp or udp server that is " + "implemented by shell functions.]" +"[+?The \aservice_path\a must be of the form \b/dev/tcp/localhost/\b\aportno\a " + "or \b/dev/udp/localhost/\b\aportno\a depending on whether the " + "\btcp\b or \budp\b protocol is used. \aportno\a is the port " + "number that the service will use.]" +"[+?The shell variable \avarname\a is associated with the service. This " + "variable can have subvariables that keeps the state of all " + "active connections. The functions \avarname\a\b.accept\b, " + "\avarname\a\b.action\b and \avarname\a\b.close\b implement the " + "service as follows:]{" + "[+accept?This function is invoked when a client tries to connect " + "to the service. It is called with an argument which " + "is the file descriptor number associated with the " + "accepted connection. If the function returns a non-zero " + "value, this connection will be closed.]" + "[+action?This function is invoked when there is data waiting " + "to be read from one of the active connections. It is " + "called with the file descriptor number that has data " + "to be read. If the function returns a non-zero " + "value, this connection will be closed.]" + "[+close?This function is invoked when the connection is closed.]" + "}" +"[+?If \avarname\a is unset, then all active connection, and the service " + "itself will be closed.]" +"" +"\n" +"\nvarname service_path\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\beloop\b(1)]" +; + + +static const char eloop_usage[] = +"[-?\n@(#)$Id: eloop (AT&T Research) 2001-06-13 $\n]" +USAGE_LICENSE +"[+NAME? eloop - process event loop]" +"[+DESCRIPTION?\beloop\b causes the shell to block waiting for events " + "to process. By default, \beloop\b does not return.]" +"[t]#[timeout?\atimeout\a is the number of milliseconds to wait " + "without receiving any events to process.]" +"\n" +"\n\n" +"\n" +"[+EXIT STATUS?If no timeout is specified, \beloop\b will not return " + "unless interrupted. Otherwise]{" + "[+0?The specified timeout interval occurred.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bmkservice\b(1)]" +; + + +#include "defs.h" + +#include <cmd.h> +#include <error.h> +#include <nval.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#define ACCEPT 0 +#define ACTION 1 +#define CLOSE 2 + +#ifndef O_SERVICE +# define O_SERVICE O_NOCTTY +#endif + +static const char* disctab[] = +{ + "accept", + "action", + "close", + 0 +}; + +typedef struct Service_s Service_t; + +struct Service_s +{ + Namfun_t fun; + short fd; + int refcount; + int (*acceptf)(Service_t*,int); + int (*actionf)(Service_t*,int,int); + int (*errorf)(Service_t*,int,const char*, ...); + void *context; + Namval_t* node; + Namval_t* disc[elementsof(disctab)-1]; +}; + +static short *file_list; +static Sfio_t **poll_list; +static Service_t **service_list; +static int npoll; +static int nready; +static int ready; +static int (*covered_fdnotify)(int, int); + +static int fdclose(Service_t *sp, register int fd) +{ + register int i; + service_list[fd] = 0; + if(sp->fd==fd) + sp->fd = -1; + for(i=0; i < npoll; i++) + { + if(file_list[i]==fd) + { + file_list[i] = file_list[npoll--]; + if(sp->actionf) + (*sp->actionf)(sp, fd, 1); + return(1); + } + } + return(0); +} + +static int fdnotify(int fd1, int fd2) +{ + Service_t *sp; + if (covered_fdnotify) + (*covered_fdnotify)(fd1, fd2); + if(fd2!=SH_FDCLOSE) + { + register int i; + service_list[fd2] = service_list[fd1]; + service_list[fd1] = 0; + for(i=0; i < npoll; i++) + { + if(file_list[i]==fd1) + { + file_list[i] = fd2; + return(0); + } + } + } + else if(sp = service_list[fd1]) + { + fdclose(sp,fd1); + if(--sp->refcount==0) + nv_unset(sp->node); + } + return(0); +} + +static void process_stream(Sfio_t* iop) +{ + int r=0, fd = sffileno(iop); + Service_t * sp = service_list[fd]; + if(fd==sp->fd) /* connection socket */ + { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + fd = accept(fd, &addr, &addrlen); + service_list[fd] = sp; + sp->refcount++; + file_list[npoll++] = fd; + if(fd>=0) + { + if(sp->acceptf) + r = (*sp->acceptf)(sp,fd); + } + } + else if(sp->actionf) + { + service_list[fd] = 0; + r = (*sp->actionf)(sp, fd, 0); + service_list[fd] = sp; + if(r<0) + close(fd); + } +} + +static int waitnotify(int fd, long timeout, int rw) +{ + Sfio_t *special=0, **pstream; + register int i; + + if (fd >= 0) + special = sh_fd2sfio(fd); + while(1) + { + pstream = poll_list; + while(ready < nready) + process_stream(pstream[ready++]); + if(special) + *pstream++ = special; + for(i=0; i < npoll; i++) + { + if(service_list[file_list[i]]) + *pstream++ = sh_fd2sfio(file_list[i]); + } +#if 1 + for(i=0; i < pstream-poll_list; i++) + sfset(poll_list[i],SF_WRITE,0); +#endif + nready = ready = 0; + errno = 0; +#ifdef DEBUG + sfprintf(sfstderr,"before poll npoll=%d",pstream-poll_list); + for(i=0; i < pstream-poll_list; i++) + sfprintf(sfstderr," %d",sffileno(poll_list[i])); + sfputc(sfstderr,'\n'); +#endif + nready = sfpoll(poll_list,pstream-poll_list,timeout); +#ifdef DEBUG + sfprintf(sfstderr,"after poll nready=%d",nready); + for(i=0; i < nready; i++) + sfprintf(sfstderr," %d",sffileno(poll_list[i])); + sfputc(sfstderr,'\n'); +#endif +#if 1 + for(i=0; i < pstream-poll_list; i++) + sfset(poll_list[i],SF_WRITE,1); +#endif + if(nready<=0) + return(errno? -1: 0); + if(special && poll_list[0]==special) + { + ready = 1; + return(fd); + } + } +} + +static int service_init(void) +{ + file_list = newof(NULL,short,n,0); + poll_list = newof(NULL,Sfio_t*,n,0); + service_list = newof(NULL,Service_t*,n,0); + covered_fdnotify = sh_fdnotify(fdnotify); + sh_waitnotify(waitnotify); + return(1); +} + +void service_add(Service_t *sp) +{ + static int init; + if (!init) + init = service_init(); + service_list[sp->fd] = sp; + file_list[npoll++] = sp->fd; +} + +static int Accept(register Service_t *sp, int accept_fd) +{ + register Namval_t* nq = sp->disc[ACCEPT]; + int fd; + + fd = fcntl(accept_fd, F_DUPFD, 10); + if (fd >= 0) + { + close(accept_fd); + if (nq) + { + char* av[3]; + char buff[20]; + + av[1] = buff; + av[2] = 0; + sfsprintf(buff, sizeof(buff), "%d", fd); + if (sh_fun(nq, sp->node, av)) + { + close(fd); + return -1; + } + } + } + sfsync(NiL); + return fd; +} + +static int Action(Service_t *sp, int fd, int close) +{ + register Namval_t* nq; + int r=0; + + if(close) + nq = sp->disc[CLOSE]; + else + nq = sp->disc[ACTION]; + if (nq) + { + char* av[3]; + char buff[20]; + + av[1] = buff; + av[2] = 0; + sfsprintf(buff, sizeof(buff), "%d", fd); + r=sh_fun(nq, sp->node, av); + } + sfsync(NiL); + return r > 0 ? -1 : 1; +} + +static int Error(Service_t *sp, int level, const char* arg, ...) +{ + va_list ap; + + va_start(ap, arg); + if(sp->node) + nv_unset(sp->node); + free((void*)sp); + errorv(NiL, ERROR_exit(1), ap); + va_end(ap); + return 0; +} + +static char* setdisc(Namval_t* np, const char* event, Namval_t* action, Namfun_t* fp) +{ + register Service_t* sp = (Service_t*)fp; + register const char* cp; + register int i; + register int n = strlen(event) - 1; + register Namval_t* nq; + + for (i = 0; cp = disctab[i]; i++) + { + if (memcmp(event, cp, n)) + continue; + if (action == np) + action = sp->disc[i]; + else + { + if (nq = sp->disc[i]) + free((void*)nq); + if (action) + sp->disc[i] = action; + else + sp->disc[i] = 0; + } + return action ? (char*)action : ""; + } + /* try the next level */ + return nv_setdisc(np, event, action, fp); +} + +static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + register Service_t* sp = (Service_t*)fp; + if (!val) + fp = nv_stack(np, NiL); + nv_putv(np, val, flag, fp); + if (!val) + { + register int i; + for(i=0; i< sh.lim.open_max; i++) + { + if(service_list[i]==sp) + { + close(i); + if(--sp->refcount<=0) + break; + } + } + free((void*)fp); + return; + } +} + +static const Namdisc_t servdisc = +{ + sizeof(Service_t), + putval, + 0, + 0, + setdisc +}; + +int b_mkservice(int argc, char** argv, Shbltin_t *context) +{ + register char* var; + register char* path; + register Namval_t* np; + register Service_t* sp; + register int fd; + + NOT_USED(argc); + NOT_USED(context); + for (;;) + { + switch (optget(argv, mkservice_usage)) + { + case 0: + break; + case ':': + error(2, opt_info.arg); + continue; + case '?': + error(ERROR_usage(2), opt_info.arg); + continue; + } + break; + } + argv += opt_info.index; + if (error_info.errors || !(var = *argv++) || !(path = *argv++) || *argv) + error(ERROR_usage(2), optusage(NiL)); + if (!(sp = newof(0, Service_t, 1, 0))) + error(ERROR_exit(1), "out of space"); + sp->acceptf = Accept; + sp->actionf = Action; + sp->errorf = Error; + sp->refcount = 1; + sp->context = context; + sp->node = 0; + sp->fun.disc = &servdisc; + if((fd = sh_open(path, O_SERVICE|O_RDWR))<=0) + { + free((void*)sp); + error(ERROR_exit(1), "%s: cannot start service", path); + } + if((sp->fd = fcntl(fd, F_DUPFD, 10))>=10) + close(fd); + else + sp->fd = fd; + np = nv_open(var,sh.var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + sp->node = np; + nv_putval(np, path, 0); + nv_stack(np, (Namfun_t*)sp); + service_add(sp); + return(0); +} + +int b_eloop(int argc, char** argv, Shbltin_t *context) +{ + register long timeout = -1; + NOT_USED(argc); + NOT_USED(context); + for (;;) + { + switch (optget(argv, eloop_usage)) + { + case 0: + break; + case 't': + timeout = opt_info.num; + continue; + case ':': + error(2, opt_info.arg); + continue; + case '?': + error(ERROR_usage(2), opt_info.arg); + continue; + } + break; + } + argv += opt_info.index; + if (error_info.errors || *argv) + error(ERROR_usage(2), optusage(NiL)); + while(1) + { + if(waitnotify(-1, timeout, 0)==0) + break; + sfprintf(sfstderr,"interrupted\n"); + } + return(errno != 0); +} diff --git a/src/cmd/ksh93/bltins/print.c b/src/cmd/ksh93/bltins/print.c new file mode 100644 index 0000000..011bccd --- /dev/null +++ b/src/cmd/ksh93/bltins/print.c @@ -0,0 +1,1058 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * echo [arg...] + * print [-nrps] [-f format] [-u filenum] [arg...] + * printf format [arg...] + * + * David Korn + * AT&T Labs + */ + +#include "defs.h" +#include <error.h> +#include <stak.h> +#include "io.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#include "streval.h" +#include <tmx.h> +#include <ccode.h> + +union types_t +{ + unsigned char c; + short h; + int i; + long l; + Sflong_t ll; + Sfdouble_t ld; + double d; + float f; + char *s; + int *ip; + char **p; +}; + +struct printf +{ + Sffmt_t hdr; + int argsize; + int intvar; + char **nextarg; + char *lastarg; + char cescape; + char err; + Shell_t *sh; +}; + +static int extend(Sfio_t*,void*, Sffmt_t*); +static const char preformat[] = ""; +static char *genformat(char*); +static int fmtvecho(const char*, struct printf*); +static ssize_t fmtbase64(Sfio_t*, char*, int); + +struct print +{ + Shell_t *sh; + const char *options; + char raw; + char echon; +}; + +static char* nullarg[] = { 0, 0 }; + +#if !SHOPT_ECHOPRINT + int B_echo(int argc, char *argv[],Shbltin_t *context) + { + static char bsd_univ; + struct print prdata; + prdata.options = sh_optecho+5; + prdata.raw = prdata.echon = 0; + prdata.sh = context->shp; + NOT_USED(argc); + /* This mess is because /bin/echo on BSD is different */ + if(!prdata.sh->universe) + { + register char *universe; + if(universe=astconf("UNIVERSE",0,0)) + bsd_univ = (strcmp(universe,"ucb")==0); + prdata.sh->universe = 1; + } + if(!bsd_univ) + return(b_print(0,argv,(Shbltin_t*)&prdata)); + prdata.options = sh_optecho; + prdata.raw = 1; + while(argv[1] && *argv[1]=='-') + { + if(strcmp(argv[1],"-n")==0) + prdata.echon = 1; +#if !SHOPT_ECHOE + else if(strcmp(argv[1],"-e")==0) + prdata.raw = 0; + else if(strcmp(argv[1],"-ne")==0 || strcmp(argv[1],"-en")==0) + { + prdata.raw = 0; + prdata.echon = 1; + } +#endif /* SHOPT_ECHOE */ + else + break; + argv++; + } + return(b_print(0,argv,(Shbltin_t*)&prdata)); + } +#endif /* SHOPT_ECHOPRINT */ + +int b_printf(int argc, char *argv[],Shbltin_t *context) +{ + struct print prdata; + NOT_USED(argc); + memset(&prdata,0,sizeof(prdata)); + prdata.sh = context->shp; + prdata.options = sh_optprintf; + return(b_print(-1,argv,(Shbltin_t*)&prdata)); +} + +/* + * argc==0 when called from echo + * argc==-1 when called from printf + */ + +int b_print(int argc, char *argv[], Shbltin_t *context) +{ + register Sfio_t *outfile; + register int exitval=0,n, fd = 1; + register Shell_t *shp = context->shp; + const char *options, *msg = e_file+4; + char *format = 0; + int sflag = 0, nflag=0, rflag=0, vflag=0; + if(argc>0) + { + options = sh_optprint; + nflag = rflag = 0; + format = 0; + } + else + { + struct print *pp = (struct print*)context; + shp = pp->sh; + options = pp->options; + if(argc==0) + { + nflag = pp->echon; + rflag = pp->raw; + argv++; + goto skip; + } + } + while((n = optget(argv,options))) switch(n) + { + case 'n': + nflag++; + break; + case 'p': + fd = shp->coutpipe; + msg = e_query; + break; + case 'f': + format = opt_info.arg; + break; + case 's': + /* print to history file */ + if(!sh_histinit((void*)shp)) + errormsg(SH_DICT,ERROR_system(1),e_history); + fd = sffileno(shp->gd->hist_ptr->histfp); + sh_onstate(SH_HISTORY); + sflag++; + break; + case 'e': + rflag = 0; + break; + case 'r': + rflag = 1; + break; + case 'u': + fd = (int)strtol(opt_info.arg,&opt_info.arg,10); + if(*opt_info.arg) + fd = -1; + else if(!sh_iovalidfd(shp,fd)) + fd = -1; + else if(!(shp->inuse_bits&(1<<fd)) && (sh_inuse(shp,fd) || (shp->gd->hist_ptr && fd==sffileno(shp->gd->hist_ptr->histfp)))) + + fd = -1; + break; + case 'v': + vflag='v'; + break; + case 'C': + vflag='C'; + break; + case ':': + /* The following is for backward compatibility */ +#if OPT_VERSION >= 19990123 + if(strcmp(opt_info.name,"-R")==0) +#else + if(strcmp(opt_info.option,"-R")==0) +#endif + { + rflag = 1; + if(error_info.errors==0) + { + argv += opt_info.index+1; + /* special case test for -Rn */ + if(strchr(argv[-1],'n')) + nflag++; + if(*argv && strcmp(*argv,"-n")==0) + { + + nflag++; + argv++; + } + goto skip2; + } + } + else + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || (argc<0 && !(format = *argv++))) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(vflag && format) + errormsg(SH_DICT,ERROR_usage(2),"-%c and -f are mutually exclusive",vflag); +skip: + if(format) + format = genformat(format); + /* handle special case of '-' operand for print */ + if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--")) + argv++; +skip2: + if(fd < 0) + { + errno = EBADF; + n = 0; + } + else if(!(n=shp->fdstatus[fd])) + n = sh_iocheckfd(shp,fd); + if(!(n&IOWRITE)) + { + /* don't print error message for stdout for compatibility */ + if(fd==1) + return(1); + errormsg(SH_DICT,ERROR_system(1),msg); + } + if(!(outfile=shp->sftable[fd])) + { + sh_onstate(SH_NOTRACK); + n = SF_WRITE|((n&IOREAD)?SF_READ:0); + shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n); + sh_offstate(SH_NOTRACK); + sfpool(outfile,shp->outpool,SF_WRITE); + } + /* turn off share to guarantee atomic writes for printf */ + n = sfset(outfile,SF_SHARE|SF_PUBLIC,0); + if(format) + { + /* printf style print */ + Sfio_t *pool; + struct printf pdata; + memset(&pdata, 0, sizeof(pdata)); + pdata.sh = shp; + pdata.hdr.version = SFIO_VERSION; + pdata.hdr.extf = extend; + pdata.nextarg = argv; + sh_offstate(SH_STOPOK); + pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE); + do + { + if(shp->trapnote&SH_SIGSET) + break; + pdata.hdr.form = format; + sfprintf(outfile,"%!",&pdata); + } while(*pdata.nextarg && pdata.nextarg!=argv); + if(pdata.nextarg == nullarg && pdata.argsize>0) + sfwrite(outfile,stakptr(staktell()),pdata.argsize); + if(sffileno(outfile)!=sffileno(sfstderr)) + sfsync(outfile); + sfpool(sfstderr,pool,SF_WRITE); + exitval = pdata.err; + } + else if(vflag) + { + while(*argv) + { + fmtbase64(outfile,*argv++,vflag=='C'); + if(!nflag) + sfputc(outfile,'\n'); + } + } + else + { + /* echo style print */ + if(nflag && !argv[0]) + sfsync((Sfio_t*)0); + else if(sh_echolist(shp,outfile,rflag,argv) && !nflag) + sfputc(outfile,'\n'); + } + if(sflag) + { + hist_flush(shp->gd->hist_ptr); + sh_offstate(SH_HISTORY); + } + else if(n&SF_SHARE) + { + sfset(outfile,SF_SHARE|SF_PUBLIC,1); + sfsync(outfile); + } + return(exitval); +} + +/* + * echo the argument list onto <outfile> + * if <raw> is non-zero then \ is not a special character. + * returns 0 for \c otherwise 1. + */ + +int sh_echolist(Shell_t *shp,Sfio_t *outfile, int raw, char *argv[]) +{ + register char *cp; + register int n; + struct printf pdata; + pdata.cescape = 0; + pdata.err = 0; + while(!pdata.cescape && (cp= *argv++)) + { + if(!raw && (n=fmtvecho(cp,&pdata))>=0) + { + if(n) + sfwrite(outfile,stakptr(staktell()),n); + } + else + sfputr(outfile,cp,-1); + if(*argv) + sfputc(outfile,' '); + sh_sigcheck(shp); + } + return(!pdata.cescape); +} + +/* + * modified version of stresc for generating formats + */ +static char strformat(char *s) +{ + register char* t; + register int c; + char* b; + char* p; +#if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE) + int w; +#endif + + b = t = s; + for (;;) + { + switch (c = *s++) + { + case '\\': + if(*s==0) + break; +#if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE) + c = chrexp(s - 1, &p, &w, FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_WIDE); +#else + c = chresc(s - 1, &p); +#endif + s = p; +#if SHOPT_MULTIBYTE +#if defined(FMT_EXP_WIDE) + if(w) + { + t += mbwide() ? mbconv(t, c) : wc2utf8(t, c); + continue; + } +#else + if(c>UCHAR_MAX && mbwide()) + { + t += mbconv(t, c); + continue; + } +#endif /* FMT_EXP_WIDE */ +#endif /* SHOPT_MULTIBYTE */ + if(c=='%') + *t++ = '%'; + else if(c==0) + { + *t++ = '%'; + c = 'Z'; + } + break; + case 0: + *t = 0; + return(t - b); + } + *t++ = c; + } +} + + +static char *genformat(char *format) +{ + register char *fp; + stakseek(0); + stakputs(preformat); + stakputs(format); + fp = (char*)stakfreeze(1); + strformat(fp+sizeof(preformat)-1); + return(fp); +} + +static char *fmthtml(const char *string, int flags) +{ + register const char *cp = string; + register int c, offset = staktell(); + if(!(flags&SFFMT_ALTER)) + { + while(c= *(unsigned char*)cp++) + { +#if SHOPT_MULTIBYTE + register int s; + if((s=mbsize(cp-1)) > 1) + { + cp += (s-1); + continue; + } +#endif /* SHOPT_MULTIBYTE */ + if(c=='<') + stakputs("<"); + else if(c=='>') + stakputs(">"); + else if(c=='&') + stakputs("&"); + else if(c=='"') + stakputs("""); + else if(c=='\'') + stakputs("'"); + else if(c==' ') + stakputs(" "); + else if(!isprint(c) && c!='\n' && c!='\r') + sfprintf(stkstd,"&#%X;",CCMAPC(c,CC_NATIVE,CC_ASCII)); + else + stakputc(c); + } + } + else + { + while(c= *(unsigned char*)cp++) + { + if(strchr("!*'();@&+$,#[]<>~.\"{}|\\-`^% ",c) || (!isprint(c) && c!='\n' && c!='\r')) + sfprintf(stkstd,"%%%02X",CCMAPC(c,CC_NATIVE,CC_ASCII)); + else + stakputc(c); + } + } + stakputc(0); + return(stakptr(offset)); +} + +#if 1 +static ssize_t fmtbase64(Sfio_t *iop, char *string, int alt) +#else +static void *fmtbase64(char *string, ssize_t *sz, int alt) +#endif +{ + char *cp; + Sfdouble_t d; + ssize_t size; + Namval_t *np = nv_open(string, NiL, NV_VARNAME|NV_NOASSIGN|NV_NOADD); + Namarr_t *ap; + static union types_t number; + if(!np || nv_isnull(np)) + { + if(sh_isoption(SH_NOUNSET)) + errormsg(SH_DICT,ERROR_exit(1),e_notset,string); + return(0); + } + if(nv_isattr(np,NV_INTEGER)) + { + d = nv_getnum(np); + if(nv_isattr(np,NV_DOUBLE)) + { + if(nv_isattr(np,NV_LONG)) + { + size = sizeof(Sfdouble_t); + number.ld = d; + } + else if(nv_isattr(np,NV_SHORT)) + { + size = sizeof(float); + number.f = (float)d; + } + else + { + size = sizeof(double); + number.d = (double)d; + } + } + else + { + if(nv_isattr(np,NV_LONG)) + { + size = sizeof(Sflong_t); + number.ll = (Sflong_t)d; + } + else if(nv_isattr(np,NV_SHORT)) + { + size = sizeof(short); + number.h = (short)d; + } + else + { + size = sizeof(short); + number.i = (int)d; + } + } +#if 1 + return(sfwrite(iop, (void*)&number, size)); +#else + if(sz) + *sz = size; + return((void*)&number); +#endif + } + if(nv_isattr(np,NV_BINARY)) +#if 1 + { + Namfun_t *fp; + for(fp=np->nvfun; fp;fp=fp->next) + { + if(fp->disc && fp->disc->writef) + break; + } + if(fp) + return (*fp->disc->writef)(np, iop, 0, fp); + else + { + int n = nv_size(np); + if(nv_isarray(np)) + { + nv_onattr(np,NV_RAW); + cp = nv_getval(np); + nv_offattr(np,NV_RAW); + } + else + cp = (char*)np->nvalue.cp; + if((size = n)==0) + size = strlen(cp); + size = sfwrite(iop, cp, size); + return(n?n:size); + } + } + else if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap) && (ap->nelem&(ARRAY_UNDEF|ARRAY_SCAN))) + { + nv_outnode(np,iop,(alt?-1:0),0); + sfputc(iop,')'); + return(sftell(iop)); + } + else + { + if(alt && nv_isvtree(np)) + nv_onattr(np,NV_EXPORT); + else + alt = 0; + cp = nv_getval(np); + if(alt) + nv_offattr(np,NV_EXPORT); + if(!cp) + return(0); + size = strlen(cp); + return(sfwrite(iop,cp,size)); + } +#else + nv_onattr(np,NV_RAW); + cp = nv_getval(np); + if(nv_isattr(np,NV_BINARY)) + nv_offattr(np,NV_RAW); + if((size = nv_size(np))==0) + size = strlen(cp); + if(sz) + *sz = size; + return((void*)cp); +#endif +} + +static int varname(const char *str, int n) +{ + register int c,dot=1,len=1; + if(n < 0) + { + if(*str=='.') + str++; + n = strlen(str); + } + for(;n > 0; n-=len) + { +#ifdef SHOPT_MULTIBYTE + len = mbsize(str); + c = mbchar(str); +#else + c = *(unsigned char*)str++; +#endif + if(dot && !(isalpha(c)||c=='_')) + break; + else if(dot==0 && !(isalnum(c) || c=='_' || c == '.')) + break; + dot = (c=='.'); + } + return(n==0); +} + +static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) +{ + char* lastchar = ""; + register int neg = 0; + Sfdouble_t d; + Sfdouble_t longmin = LDBL_LLONG_MIN; + Sfdouble_t longmax = LDBL_LLONG_MAX; + int format = fe->fmt; + int n; + int fold = fe->base; + union types_t* value = (union types_t*)v; + struct printf* pp = (struct printf*)fe; + Shell_t *shp = pp->sh; + register char* argp = *pp->nextarg; + char *w,*s; + + if(fe->n_str>0 && varname(fe->t_str,fe->n_str) && (!argp || varname(argp,-1))) + { + if(argp) + pp->lastarg = argp; + else + argp = pp->lastarg; + if(argp) + { + sfprintf(pp->sh->strbuf,"%s.%.*s%c",argp,fe->n_str,fe->t_str,0); + argp = sfstruse(pp->sh->strbuf); + } + } + else + pp->lastarg = 0; + fe->flags |= SFFMT_VALUE; + if(!argp || format=='Z') + { + switch(format) + { + case 'c': + value->c = 0; + fe->flags &= ~SFFMT_LONG; + break; + case 'q': + format = 's'; + /* FALL THROUGH */ + case 's': + case 'H': + case 'B': + case 'P': + case 'R': + case 'Z': + case 'b': + fe->fmt = 's'; + fe->size = -1; + fe->base = -1; + value->s = ""; + fe->flags &= ~SFFMT_LONG; + break; + case 'a': + case 'e': + case 'f': + case 'g': + case 'A': + case 'E': + case 'F': + case 'G': + if(SFFMT_LDOUBLE) + value->ld = 0.; + else + value->d = 0.; + break; + case 'n': + value->ip = &pp->intvar; + break; + case 'Q': + value->ll = 0; + break; + case 'T': + fe->fmt = 'd'; + value->ll = tmxgettime(); + break; + default: + if(!strchr("DdXxoUu",format)) + errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); + fe->fmt = 'd'; + value->ll = 0; + break; + } + } + else + { + switch(format) + { + case 'p': + value->p = (char**)strtol(argp,&lastchar,10); + break; + case 'n': + { + Namval_t *np; + np = nv_open(argp,shp->var_tree,NV_VARNAME|NV_NOASSIGN|NV_NOARRAY); + _nv_unset(np,0); + nv_onattr(np,NV_INTEGER); + if (np->nvalue.lp = new_of(int32_t,0)) + *np->nvalue.lp = 0; + nv_setsize(np,10); + if(sizeof(int)==sizeof(int32_t)) + value->ip = (int*)np->nvalue.lp; + else + { + int32_t sl = 1; + value->ip = (int*)(((char*)np->nvalue.lp) + (*((char*)&sl) ? 0 : sizeof(int))); + } + nv_close(np); + break; + } + case 'q': + case 'b': + case 's': + case 'B': + case 'H': + case 'P': + case 'R': + fe->fmt = 's'; + fe->size = -1; + if(format=='s' && fe->base>=0) + { + value->p = pp->nextarg; + pp->nextarg = nullarg; + } + else + { + fe->base = -1; + value->s = argp; + } + fe->flags &= ~SFFMT_LONG; + break; + case 'c': + if(mbwide() && (n = mbsize(argp)) > 1) + { + fe->fmt = 's'; + fe->size = n; + value->s = argp; + } + else if(fe->base >=0) + value->s = argp; + else + value->c = *argp; + fe->flags &= ~SFFMT_LONG; + break; + case 'o': + case 'x': + case 'X': + case 'u': + case 'U': + longmax = LDBL_ULLONG_MAX; + case '.': + if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form)) + { + value->ll = ((unsigned char*)argp)[0]; + break; + } + case 'd': + case 'D': + case 'i': + switch(*argp) + { + case '\'': + case '"': + w = argp + 1; + if(mbwide() && mbsize(w) > 1) + value->ll = mbchar(w); + else + value->ll = *(unsigned char*)w++; + if(w[0] && (w[0] != argp[0] || w[1])) + { + errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); + pp->err = 1; + } + break; + default: + d = sh_strnum(argp,&lastchar,0); + if(d<longmin) + { + errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); + pp->err = 1; + d = longmin; + } + else if(d>longmax) + { + errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); + pp->err = 1; + d = longmax; + } + value->ll = (Sflong_t)d; + if(lastchar == *pp->nextarg) + { + value->ll = *argp; + lastchar = ""; + } + break; + } + if(neg) + value->ll = -value->ll; + fe->size = sizeof(value->ll); + break; + case 'a': + case 'e': + case 'f': + case 'g': + case 'A': + case 'E': + case 'F': + case 'G': + d = sh_strnum(*pp->nextarg,&lastchar,0); + switch(*argp) + { + case '\'': + case '"': + d = ((unsigned char*)argp)[1]; + if(argp[2] && (argp[2] != argp[0] || argp[3])) + { + errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); + pp->err = 1; + } + break; + default: + d = sh_strnum(*pp->nextarg,&lastchar,0); + break; + } + if(SFFMT_LDOUBLE) + { + value->ld = d; + fe->size = sizeof(value->ld); + } + else + { + value->d = d; + fe->size = sizeof(value->d); + } + break; + case 'Q': + value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1); + break; + case 'T': + value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW); + break; + default: + value->ll = 0; + fe->fmt = 'd'; + fe->size = sizeof(value->ll); + errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); + break; + } + if (format == '.') + value->i = value->ll; + if(*lastchar) + { + errormsg(SH_DICT,ERROR_warn(0),e_argtype,format); + pp->err = 1; + } + pp->nextarg++; + } + switch(format) + { + case 'Z': + fe->fmt = 'c'; + fe->base = -1; + value->c = 0; + break; + case 'b': + if((n=fmtvecho(value->s,pp))>=0) + { + if(pp->nextarg == nullarg) + { + pp->argsize = n; + return -1; + } + value->s = stakptr(staktell()); + fe->size = n; + } + break; + case 'B': + if(!shp->strbuf2) + shp->strbuf2 = sfstropen(); + fe->size = fmtbase64(shp->strbuf2,value->s, fe->flags&SFFMT_ALTER); + value->s = sfstruse(shp->strbuf2); + fe->flags |= SFFMT_SHORT; + break; + case 'H': + value->s = fmthtml(value->s, fe->flags); + break; + case 'q': + value->s = sh_fmtqf(value->s, !!(fe->flags & SFFMT_ALTER), fold); + break; + case 'P': + s = fmtmatch(value->s); + if(!s || *s==0) + errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); + value->s = s; + break; + case 'R': + s = fmtre(value->s); + if(!s || *s==0) + errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); + value->s = s; + break; + case 'Q': + if (fe->n_str>0) + { + fe->fmt = 'd'; + fe->size = sizeof(value->ll); + } + else + { + value->s = fmtelapsed(value->ll, 1); + fe->fmt = 's'; + fe->size = -1; + } + break; + case 'T': + if(fe->n_str>0) + { + n = fe->t_str[fe->n_str]; + fe->t_str[fe->n_str] = 0; + value->s = fmttmx(fe->t_str, value->ll); + fe->t_str[fe->n_str] = n; + } + else value->s = fmttmx(NIL(char*), value->ll); + fe->fmt = 's'; + fe->size = -1; + break; + } + return 0; +} + +/* + * construct System V echo string out of <cp> + * If there are not escape sequences, returns -1 + * Otherwise, puts null terminated result on stack, but doesn't freeze it + * returns length of output. + */ + +static int fmtvecho(const char *string, struct printf *pp) +{ + register const char *cp = string, *cpmax; + register int c; + register int offset = staktell(); +#if SHOPT_MULTIBYTE + int chlen; + if(mbwide()) + { + while(1) + { + if ((chlen = mbsize(cp)) > 1) + /* Skip over multibyte characters */ + cp += chlen; + else if((c= *cp++)==0 || c == '\\') + break; + } + } + else +#endif /* SHOPT_MULTIBYTE */ + while((c= *cp++) && (c!='\\')); + if(c==0) + return(-1); + c = --cp - string; + if(c>0) + stakwrite((void*)string,c); + for(; c= *cp; cp++) + { +#if SHOPT_MULTIBYTE + if (mbwide() && ((chlen = mbsize(cp)) > 1)) + { + stakwrite(cp,chlen); + cp += (chlen-1); + continue; + } +#endif /* SHOPT_MULTIBYTE */ + if( c=='\\') switch(*++cp) + { + case 'E': + c = ('a'==97?'\033':39); /* ASCII/EBCDIC */ + break; + case 'a': + c = '\a'; + break; + case 'b': + c = '\b'; + break; + case 'c': + pp->cescape++; + pp->nextarg = nullarg; + goto done; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 'v': + c = '\v'; + break; + case 't': + c = '\t'; + break; + case '\\': + c = '\\'; + break; + case '0': + c = 0; + cpmax = cp + 4; + while(++cp<cpmax && *cp>='0' && *cp<='7') + { + c <<= 3; + c |= (*cp-'0'); + } + default: + cp--; + } + stakputc(c); + } +done: + c = staktell()-offset; + stakputc(0); + stakseek(offset); + return(c); +} diff --git a/src/cmd/ksh93/bltins/read.c b/src/cmd/ksh93/bltins/read.c new file mode 100644 index 0000000..75fc478 --- /dev/null +++ b/src/cmd/ksh93/bltins/read.c @@ -0,0 +1,802 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...] + * + * David Korn + * AT&T Labs + * + */ + +#include <ast.h> +#include <error.h> +#include "defs.h" +#include "variables.h" +#include "lexstates.h" +#include "io.h" +#include "name.h" +#include "builtins.h" +#include "history.h" +#include "terminal.h" +#include "edit.h" + +#define R_FLAG 1 /* raw mode */ +#define S_FLAG 2 /* save in history file */ +#define A_FLAG 4 /* read into array */ +#define N_FLAG 8 /* fixed size read at most */ +#define NN_FLAG 0x10 /* fixed size read exact */ +#define V_FLAG 0x20 /* use default value */ +#define C_FLAG 0x40 /* read into compound variable */ +#define D_FLAG 8 /* must be number of bits for all flags */ +#define SS_FLAG 0x80 /* read .csv format file */ + +struct read_save +{ + char **argv; + char *prompt; + short fd; + short plen; + int flags; + long timeout; +}; + +int b_read(int argc,char *argv[], Shbltin_t *context) +{ + Sfdouble_t sec; + register char *name; + register int r, flags=0, fd=0; + register Shell_t *shp = context->shp; + long timeout = 1000*shp->st.tmout; + int save_prompt, fixargs=context->invariant; + struct read_save *rp; + static char default_prompt[3] = {ESC,ESC}; + rp = (struct read_save*)(context->data); + if(argc==0) + { + if(rp) + free((void*)rp); + return(0); + } + if(rp) + { + flags = rp->flags; + timeout = rp->timeout; + fd = rp->fd; + argv = rp->argv; + name = rp->prompt; + r = rp->plen; + goto bypass; + } + while((r = optget(argv,sh_optread))) switch(r) + { + case 'A': + flags |= A_FLAG; + break; + case 'C': + flags |= C_FLAG; + break; + case 't': + sec = sh_strnum(opt_info.arg, (char**)0,1); + timeout = sec ? 1000*sec : 1; + break; + case 'd': + if(opt_info.arg && *opt_info.arg!='\n') + { + char *cp = opt_info.arg; + flags &= ~((1<<D_FLAG)-1); + flags |= (mbchar(cp)<< D_FLAG); + } + break; + case 'p': + if((fd = shp->cpipe[0])<=0) + errormsg(SH_DICT,ERROR_exit(1),e_query); + break; + case 'n': case 'N': + flags &= ((1<<D_FLAG)-1); + flags |= (r=='n'?N_FLAG:NN_FLAG); + r = (int)opt_info.num; + if((unsigned)r > (1<<((8*sizeof(int))-D_FLAG))-1) + errormsg(SH_DICT,ERROR_exit(1),e_overlimit,opt_info.name); + flags |= (r<< D_FLAG); + break; + case 'r': + flags |= R_FLAG; + break; + case 's': + /* save in history file */ + flags |= S_FLAG; + break; + case 'S': + flags |= SS_FLAG; + break; + case 'u': + fd = (int)opt_info.num; + if(sh_inuse(shp,fd)) + fd = -1; + break; + case 'v': + flags |= V_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); + if(!((r=shp->fdstatus[fd])&IOREAD) || !(r&(IOSEEK|IONOSEEK))) + r = sh_iocheckfd(shp,fd); + if(fd<0 || !(r&IOREAD)) + errormsg(SH_DICT,ERROR_system(1),e_file+4); + /* look for prompt */ + if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY)) + r = strlen(name++); + else + r = 0; + if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0))) + { + context->data = (void*)rp; + rp->fd = fd; + rp->flags = flags; + rp->timeout = timeout; + rp->argv = argv; + rp->prompt = name; + rp->plen = r; + } +bypass: + shp->prompt = default_prompt; + if(r && (shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR))) + { + memcpy(shp->prompt,name,r); + sfwrite(sfstderr,shp->prompt,r-1); + } + shp->timeout = 0; + save_prompt = shp->nextprompt; + shp->nextprompt = 0; + r=sh_readline(shp,argv,fd,flags,timeout); + shp->nextprompt = save_prompt; + if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd])))) + { + if(fd == shp->cpipe[0] && errno!=EINTR) + sh_pclose(shp->cpipe); + } + return(r); +} + +/* + * here for read timeout + */ +static void timedout(void *handle) +{ + sfclrlock((Sfio_t*)handle); + sh_exit(1); +} + +/* + * This is the code to read a line and to split it into tokens + * <names> is an array of variable names + * <fd> is the file descriptor + * <flags> is union of -A, -r, -s, and contains delimiter if not '\n' + * <timeout> is number of milli-seconds until timeout + */ + +int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout) +{ + register ssize_t c; + register unsigned char *cp; + register Namval_t *np; + register char *name, *val; + register Sfio_t *iop; + Namfun_t *nfp; + char *ifs; + unsigned char *cpmax; + unsigned char *del; + char was_escape = 0; + char use_stak = 0; + volatile char was_write = 0; + volatile char was_share = 1; + int rel, wrd, keytrap; + long array_index = 0; + void *timeslot=0; + int delim = '\n'; + int jmpval=0; + ssize_t size = 0; + int binary; + int oflags=NV_ASSIGN|NV_VARNAME; + char inquote = 0; + struct checkpt buff; + Edit_t *ep = (struct edit*)shp->gd->ed_context; + if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd))) + return(1); + sh_stats(STAT_READS); + if(names && (name = *names)) + { + Namval_t *mp; + if(val= strchr(name,'?')) + *val = 0; + if(flags&C_FLAG) + oflags |= NV_ARRAY; + np = nv_open(name,shp->var_tree,oflags); + if(np && nv_isarray(np) && (mp=nv_opensub(np))) + np = mp; + if((flags&V_FLAG) && shp->gd->ed_context) + ((struct edit*)shp->gd->ed_context)->e_default = np; + if(flags&A_FLAG) + { + flags &= ~A_FLAG; + array_index = 1; + nv_unset(np); + nv_putsub(np,NIL(char*),0L); + } + else if(flags&C_FLAG) + { + char *sp = np->nvenv; + delim = -1; + nv_unset(np); + if(!nv_isattr(np,NV_MINIMAL)) + np->nvenv = sp; + nv_setvtree(np); + } + else + name = *++names; + if(val) + *val = '?'; + } + else + { + name = 0; + if(dtvnext(shp->var_tree) || shp->namespace) + np = nv_open(nv_name(REPLYNOD),shp->var_tree,0); + else + np = REPLYNOD; + } + keytrap = ep?ep->e_keytrap:0; + if(flags>>D_FLAG) /* delimiter not new-line or fixed size read */ + { + if(flags&(N_FLAG|NN_FLAG)) + size = ((unsigned)flags)>>D_FLAG; + else + delim = ((unsigned)flags)>>D_FLAG; + if((shp->fdstatus[fd]&IOTTY) && !keytrap) + tty_raw(fd,1); + } + binary = nv_isattr(np,NV_BINARY); + if(!binary && !(flags&(N_FLAG|NN_FLAG))) + { + Namval_t *mp; + /* set up state table based on IFS */ + ifs = nv_getval(mp=sh_scoped(shp,IFSNOD)); + if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC) + shp->ifstable['\\'] = 0; + else if(!(flags&R_FLAG) && shp->ifstable['\\']==0) + shp->ifstable['\\'] = S_ESC; + if(delim>0) + shp->ifstable[delim] = S_NL; + if(delim!='\n') + { + shp->ifstable['\n'] = 0; + nv_putval(mp, ifs, NV_RDONLY); + } + shp->ifstable[0] = S_EOF; + if((flags&SS_FLAG)) + { + shp->ifstable['"'] = S_QUOTE; + shp->ifstable['\r'] = S_ERR; + } + } + sfclrerr(iop); + for(nfp=np->nvfun; nfp; nfp = nfp->next) + { + if(nfp->disc && nfp->disc->readf) + { + Namval_t *mp = nv_open(name,shp->var_tree,oflags|NV_NOREF); + if((c=(*nfp->disc->readf)(mp,iop,delim,nfp))>=0) + return(c); + } + } + if(binary && !(flags&(N_FLAG|NN_FLAG))) + { + flags |= NN_FLAG; + size = nv_size(np); + } + was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0; + if(fd==0) + was_share = (sfset(iop,SF_SHARE,shp->redir0!=2)&SF_SHARE)!=0; + if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) + { + sh_pushcontext(shp,&buff,1); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval) + goto done; + if(timeout) + timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop); + } + if(flags&(N_FLAG|NN_FLAG)) + { + char buf[256],*var=buf,*cur,*end,*up,*v; + /* reserved buffer */ + if((c=size)>=sizeof(buf)) + { + if(!(var = (char*)malloc(c+1))) + sh_exit(1); + end = var + c; + } + else + end = var + sizeof(buf) - 1; + up = cur = var; + if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0) + was_share = 1; + if(size==0) + { + cp = sfreserve(iop,0,0); + c = 0; + } + else + { + ssize_t m; + int f; + for (;;) + { + c = size; + if(keytrap) + { + cp = 0; + f = 0; + m = 0; + while(c-->0 && (buf[m]=ed_getchar(ep,0))) + m++; + if(m>0) + cp = (unsigned char*)buf; + } + else + { + f = 1; + if(cp = sfreserve(iop,c,SF_LOCKR)) + m = sfvalue(iop); + else if(flags&NN_FLAG) + { + c = size; + m = (cp = sfreserve(iop,c,0)) ? sfvalue(iop) : 0; + f = 0; + } + else + { + c = sfvalue(iop); + m = (cp = sfreserve(iop,c,SF_LOCKR)) ? sfvalue(iop) : 0; + } + } + if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m))) + { + *v++ = 0; + m = v-(char*)cp; + } + if((c=m)>size) + c = size; + if(c>0) + { + if(c > (end-cur)) + { + ssize_t cx = cur - var, ux = up - var; + m = (end - var) + (c - (end - cur)); + if (var == buf) + { + v = (char*)malloc(m+1); + var = memcpy(v, var, cur - var); + } + else + var = newof(var, char, m, 1); + end = var + m; + cur = var + cx; + up = var + ux; + } + if(cur!=(char*)cp) + memcpy((void*)cur,cp,c); + if(f) + sfread(iop,cp,c); + cur += c; +#if SHOPT_MULTIBYTE + if(!binary && mbwide()) + { + int x; + int z; + + mbinit(); + *cur = 0; + x = z = 0; + while (up < cur && (z = mbsize(up)) > 0) + { + up += z; + x++; + } + if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c)) + continue; + } +#endif + } +#if SHOPT_MULTIBYTE + if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size)) + cur = var; +#endif + *cur = 0; + if(c>=size || (flags&N_FLAG) || m==0) + { + if(m) + sfclrerr(iop); + break; + } + size -= c; + } + } + if(timeslot) + timerdel(timeslot); + if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size)) + { + if((c==size) && np->nvalue.cp && !nv_isarray(np)) + memcpy((char*)np->nvalue.cp,var,c); + else + { + Namval_t *mp; + if(var==buf) + var = memdup(var,c+1); + nv_putval(np,var,NV_RAW); + nv_setsize(np,c); + if(!nv_isattr(np,NV_IMPORT|NV_EXPORT) && (mp=(Namval_t*)np->nvenv)) + nv_setsize(mp,c); + } + } + else + { + nv_putval(np,var,0); + if(var!=buf) + free((void*)var); + } + goto done; + } + else if(cp = (unsigned char*)sfgetr(iop,delim,0)) + c = sfvalue(iop); + else if(cp = (unsigned char*)sfgetr(iop,delim,-1)) + { + c = sfvalue(iop)+1; + if(!sferror(iop) && sfgetc(iop) >=0) + errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"line length"); + } + if(timeslot) + timerdel(timeslot); + if((flags&S_FLAG) && !shp->gd->hist_ptr) + { + sh_histinit((void*)shp); + if(!shp->gd->hist_ptr) + flags &= ~S_FLAG; + } + if(cp) + { + cpmax = cp + c; +#if SHOPT_CRNL + if(delim=='\n' && c>=2 && cpmax[-2]=='\r') + cpmax--; +#endif /* SHOPT_CRNL */ + if(*(cpmax-1) != delim) + *(cpmax-1) = delim; + if(flags&S_FLAG) + sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c); + c = shp->ifstable[*cp++]; +#if !SHOPT_MULTIBYTE + if(!name && (flags&R_FLAG)) /* special case single argument */ + { + /* skip over leading blanks */ + while(c==S_SPACE) + c = shp->ifstable[*cp++]; + /* strip trailing delimiters */ + if(cpmax[-1] == '\n') + cpmax--; + if(cpmax>cp) + { + while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE); + cpmax[1] = 0; + } + else + *cpmax =0; + if(nv_isattr(np, NV_RDONLY)) + { + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + jmpval = 1; + } + else + nv_putval(np,(char*)cp-1,0); + goto done; + } +#endif /* !SHOPT_MULTIBYTE */ + } + else + c = S_NL; + shp->nextprompt = 2; + rel= staktell(); + /* val==0 at the start of a field */ + val = 0; + del = 0; + while(1) + { + switch(c) + { +#if SHOPT_MULTIBYTE + case S_MBYTE: + if(val==0) + val = (char*)(cp-1); + if(sh_strchr(ifs,(char*)cp-1)>=0) + { + c = mbsize((char*)cp-1); + if(name) + cp[-1] = 0; + if(c>1) + cp += (c-1); + c = S_DELIM; + } + else + c = 0; + continue; +#endif /*SHOPT_MULTIBYTE */ + case S_QUOTE: + c = shp->ifstable[*cp++]; + inquote = !inquote; + goto skip; + case S_ESC: + /* process escape character */ + if((c = shp->ifstable[*cp++]) == S_NL) + was_escape = 1; + else + c = 0; + skip: + if(val) + { + stakputs(val); + use_stak = 1; + *val = 0; + } + continue; + + case S_ERR: + cp++; + case S_EOF: + /* check for end of buffer */ + if(val && *val) + { + stakputs(val); + use_stak = 1; + } + val = 0; + if(cp>=cpmax) + { + c = S_NL; + break; + } + /* eliminate null bytes */ + c = shp->ifstable[*cp++]; + if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE)) + c = 0; + continue; + case S_NL: + if(was_escape) + { + was_escape = 0; + if(cp = (unsigned char*)sfgetr(iop,delim,0)) + c = sfvalue(iop); + else if(cp=(unsigned char*)sfgetr(iop,delim,-1)) + c = sfvalue(iop)+1; + if(cp) + { + if(flags&S_FLAG) + sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c); + cpmax = cp + c; + c = shp->ifstable[*cp++]; + val=0; + if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE)) + c = 0; + continue; + } + } + c = S_NL; + break; + + case S_SPACE: + /* skip over blanks */ + while((c=shp->ifstable[*cp++])==S_SPACE); + if(!val) + continue; +#if SHOPT_MULTIBYTE + if(c==S_MBYTE) + { + if(sh_strchr(ifs,(char*)cp-1)>=0) + { + if((c = mbsize((char*)cp-1))>1) + cp += (c-1); + c = S_DELIM; + } + else + c = 0; + } +#endif /* SHOPT_MULTIBYTE */ + if(c!=S_DELIM) + break; + /* FALL THRU */ + + case S_DELIM: + if(!del) + del = cp - 1; + if(name) + { + /* skip over trailing blanks */ + while((c=shp->ifstable[*cp++])==S_SPACE); + break; + } + /* FALL THRU */ + + case 0: + if(val==0 || was_escape) + { + val = (char*)(cp-1); + was_escape = 0; + } + /* skip over word characters */ + wrd = -1; + while(1) + { + while((c=shp->ifstable[*cp++])==0) + if(!wrd) + wrd = 1; + if(inquote) + { + if(c==S_QUOTE) + { + if(shp->ifstable[*cp]==S_QUOTE) + { + if(val) + { + stakwrite(val,cp-(unsigned char*)val); + use_stak = 1; + } + val = (char*)++cp; + } + else + break; + } + if(c && c!=S_EOF) + { + if(c==S_NL) + { + if(val) + { + stakwrite(val,cp-(unsigned char*)val); + use_stak=1; + } + if(cp = (unsigned char*)sfgetr(iop,delim,0)) + c = sfvalue(iop); + else if(cp = (unsigned char*)sfgetr(iop,delim,-1)) + c = sfvalue(iop)+1; + val = (char*)cp; + } + continue; + } + } + if(!del&&c==S_DELIM) + del = cp - 1; + if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE) + break; + if(wrd<0) + wrd = 0; + } + if(wrd>0) + del = (unsigned char*)""; + if(c!=S_MBYTE) + cp[-1] = 0; + continue; + } + /* assign value and advance to next variable */ + if(!val) + val = ""; + if(use_stak) + { + stakputs(val); + stakputc(0); + val = stakptr(rel); + } + if(!name && *val) + { + /* strip off trailing space delimiters */ + register unsigned char *vp = (unsigned char*)val + strlen(val); + while(shp->ifstable[*--vp]==S_SPACE); + if(vp==del) + { + if(vp==(unsigned char*)val) + vp--; + else + while(shp->ifstable[*--vp]==S_SPACE); + } + vp[1] = 0; + } + if(nv_isattr(np, NV_RDONLY)) + { + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + jmpval = 1; + } + else + nv_putval(np,val,0); + val = 0; + del = 0; + if(use_stak) + { + stakseek(rel); + use_stak = 0; + } + if(array_index) + { + nv_putsub(np, NIL(char*), array_index++); + if(c!=S_NL) + continue; + name = *++names; + } + while(1) + { + if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT)) + { + nv_onattr(np,NV_EXPORT); + sh_envput(shp->env,np); + } + if(name) + { + nv_close(np); + np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME); + name = *++names; + } + else + np = 0; + if(c!=S_NL) + break; + if(!np) + goto done; + if(nv_isattr(np, NV_RDONLY)) + { + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + jmpval = 1; + } + else + nv_putval(np, "", 0); + } + } +done: + if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) + sh_popcontext(shp,&buff); + if(was_write) + sfset(iop,SF_WRITE,1); + if(!was_share) + sfset(iop,SF_SHARE,0); + nv_close(np); + if((shp->fdstatus[fd]&IOTTY) && !keytrap) + tty_cooked(fd); + if(flags&S_FLAG) + hist_flush(shp->gd->hist_ptr); + if(jmpval > 1) + siglongjmp(*shp->jmplist,jmpval); + return(jmpval); +} + diff --git a/src/cmd/ksh93/bltins/regress.c b/src/cmd/ksh93/bltins/regress.c new file mode 100644 index 0000000..25f3c25 --- /dev/null +++ b/src/cmd/ksh93/bltins/regress.c @@ -0,0 +1,343 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * regression test intercept control + * enable with SHOPT_REGRESS==1 in Makefile + * not for production use + * see --man for details + * all string constants inline here instead of in data/... + * + * David Korn + * at&t research + */ + +#include "defs.h" + +#if SHOPT_REGRESS + +#include <error.h> +#include <ls.h> +#include "io.h" +#include "builtins.h" +#include <tmx.h> + +#define REGRESS_HEADER "ksh:REGRESS:" + +#define TRACE(r,i,f) sh_regress(REGRESS_##r, i, sfprints f, __LINE__, __FILE__) + +static const char usage[] = +"[-1p0?\n@(#)$Id: __regress__ (AT&T Research) 2009-03-29 $\n]" +USAGE_LICENSE +"[+NAME?__regress__ - shell regression test intercept control]" +"[+DESCRIPTION?\b__regress__\b controls the regression test intercepts " + "for shells compiled with SHOPT_REGRESS==1. Shells compiled this way are " + "for testing only. In addition to \b__regress__\b and the \b--regress\b " + "command line option, these shells may contain system library function " + "intercepts that behave different from the native counterparts.]" +"[+?Each option controls a different test and possibly a different set " + "of intercepts. The options are interpreted \bdd\b(1) style -- '-' or " + "'--' prefix not required. This simplifies the specification of the " + "command line \b--regress\b=\avalue\a option, where \avalue\a is passed " + "as an option to the \b__regress__\b builtin. Typically regression test " + "intercepts are enabled with one or more command line \b--regress\b " + "options, with optional specific calls to \b__regress__\b in test " + "scripts to enable/disable intercepts as the test progresses.]" +"[+?Each enabled intercept may result in trace lines of the form \b" REGRESS_HEADER + "\aoption\a:\aintercept\a:\ainfo\a on the standard error, where " + "\aoption\a is one of the options below, \aintercept\a is the name of " + "the specific intercept for \aoption\a, and \ainfo\a is \aoption\a " + "specific information. Unless noted otherwise, one regression test trace " + "line is produced each time an enabled intercept is called.]" +"[101:egid?The intercept effective gid is set to \aoriginal-egid\a. The " + "effective gid of the underlying system process is not affected. The " + "trace line info is either \begid==rgid\b or \begid!=rgid\b. The " + "intercepts are:]#?[original-egid:=1]" + "{" + "[+getegid()?The intercept effecive gid is returned. The " + "\bsetgid\b() intercept may change this between the real gid and " + "\aoriginal-egid\a.]" + "[+setgid(gid)?Sets the intercept effective gid to \agid\a. " + "Fails if \agid\a is neither the real gid nor " + "\aoriginal-egid\a.]" + "}" +"[102:euid?The intercept effective uid is set to \aoriginal-euid\a. The " + "effective uid of the underlying system process is not affected. The " + "trace line info is either \beuid==ruid\b or \beuid!=ruid\b. The " + "intercepts are:]#?[original-euid:=1]" + "{" + "[+geteuid()?The intercept effecive uid is returned. The " + "\bsetuid\b() intercept may change this between the real uid and " + "\aoriginal-euid\a.]" + "[+setuid(uid)?Sets the intercept effective uid to \auid\a. " + "Fails if \auid\a is neither the real uid nor " + "\aoriginal-euid\a.]" + "}" +"[103:p_suid?Specifies a value for SHOPT_P_SUID. Effective uids greater " + "than the non-privileged-uid disable the priveleged mode. The intercepts " + "are:]#?[non-privileged-uid:=1]" + "{" + "[+SHOPT_P_SUID?The SHOPT_P_SUID macro value is overridden by " + "\bp_suid\b. A trace line is output for each SHOPT_P_SUID " + "access.]" + "}" +"[104:source?The intercepts are:]" + "{" + "[+sh_source()?The trace line info is the path of the script " + "being sourced. Used to trace shell startup scripts.]" + "}" +"[105:etc?Map file paths matching \b/etc/\b* to \aetc-dir\a/*. The " + "intercepts are:]:[etc-dir:=/etc]" + "{" + "[+sh_open()?Paths matching \b/etc/\b* are changed to " + "\aetc-dir\a/*.]" + "}" +"[+SEE ALSO?\bksh\b(1), \bregress\b(1), \brt\b(1)]" +; + +static const char* regress_options[] = +{ + "ERROR", + "egid", + "euid", + "p_suid", + "source", + "etc", +}; + +void sh_regress_init(Shell_t* shp) +{ + static Regress_t state; + + shp->regress = &state; +} + +/* + * regress info trace output + */ + +void sh_regress(unsigned int index, const char* intercept, const char* info, unsigned int line, const char* file) +{ + char* name; + char buf[16]; + + if (index >= 1 && index <= elementsof(regress_options)) + name = (char*)regress_options[index]; + else + sfsprintf(name = buf, sizeof(buf), "%u", index); + sfprintf(sfstderr, REGRESS_HEADER "%s:%s:%s\n", name, intercept, fmtesc(info)); +} + +/* + * egid intercepts + */ + +static gid_t intercept_sgid = 0; +static gid_t intercept_egid = -1; +static gid_t intercept_rgid = -1; + +gid_t getegid(void) +{ + if (intercept_rgid == -1) + intercept_rgid = getgid(); + if (sh_isregress(REGRESS_egid)) + { + TRACE(egid, "getegid", ("%s", intercept_egid == intercept_rgid ? "egid==rgid" : "egid!=rgid")); + return intercept_egid; + } + return intercept_rgid; +} + +int setgid(gid_t gid) +{ + if (intercept_rgid == -1) + intercept_rgid = getgid(); + if (sh_isregress(REGRESS_egid)) + { + if (gid != intercept_rgid && gid != intercept_sgid) + { + TRACE(egid, "setgid", ("%s", "EPERM")); + errno = EPERM; + return -1; + } + intercept_egid = gid; + TRACE(egid, "setgid", ("%s", intercept_egid == intercept_rgid ? "egid==rgid" : "egid!=rgid")); + } + else if (gid != intercept_rgid) + { + errno = EPERM; + return -1; + } + return 0; +} + +/* + * euid intercepts + */ + +static uid_t intercept_suid = 0; +static uid_t intercept_euid = -1; +static uid_t intercept_ruid = -1; + +uid_t geteuid(void) +{ + if (intercept_ruid == -1) + intercept_ruid = getuid(); + if (sh_isregress(REGRESS_euid)) + { + TRACE(euid, "geteuid", ("%s", intercept_euid == intercept_ruid ? "euid==ruid" : "euid!=ruid")); + return intercept_euid; + } + return intercept_ruid; +} + +int setuid(uid_t uid) +{ + if (intercept_ruid == -1) + intercept_ruid = getuid(); + if (sh_isregress(REGRESS_euid)) + { + if (uid != intercept_ruid && uid != intercept_suid) + { + TRACE(euid, "setuid", ("%s", "EPERM")); + errno = EPERM; + return -1; + } + intercept_euid = uid; + TRACE(euid, "setuid", ("%s", intercept_euid == intercept_ruid ? "euid==ruid" : "euid!=ruid")); + } + else if (uid != intercept_ruid) + { + errno = EPERM; + return -1; + } + return 0; +} + +/* + * p_suid intercept + */ + +static uid_t intercept_p_suid = 0x7fffffff; + +uid_t sh_regress_p_suid(unsigned int line, const char* file) +{ + REGRESS(p_suid, "SHOPT_P_SUID", ("%d", intercept_p_suid)); + return intercept_p_suid; +} + +/* + * p_suid intercept + */ + +static char* intercept_etc = 0; + +char* sh_regress_etc(const char* path, unsigned int line, const char* file) +{ + REGRESS(etc, "sh_open", ("%s => %s%s", path, intercept_etc, path+4)); + return intercept_etc; +} + +/* + * __regress__ builtin + */ + +int b___regress__(int argc, char** argv, Shbltin_t *context) +{ + register Shell_t* shp = context->shp; + int n; + + for (;;) + { + switch (n = optget(argv, usage)) + { + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case 0: + break; + default: + if (n < -100) + { + n = -(n + 100); + if (opt_info.arg || opt_info.number) + sh_onregress(n); + else + sh_offregress(n); + switch (n) + { + case REGRESS_egid: + if (sh_isregress(n)) + { + intercept_egid = intercept_sgid = (gid_t)opt_info.number; + TRACE(egid, argv[0], ("%d", intercept_egid)); + } + else + TRACE(egid, argv[0], ("%s", "off")); + break; + case REGRESS_euid: + if (sh_isregress(n)) + { + intercept_euid = intercept_suid = (uid_t)opt_info.number; + TRACE(euid, argv[0], ("%d", intercept_euid)); + } + else + TRACE(euid, argv[0], ("%s", "off")); + break; + case REGRESS_p_suid: + if (sh_isregress(n)) + { + intercept_p_suid = (uid_t)opt_info.number; + TRACE(p_suid, argv[0], ("%d", intercept_p_suid)); + } + else + TRACE(p_suid, argv[0], ("%s", "off")); + break; + case REGRESS_source: + TRACE(source, argv[0], ("%s", sh_isregress(n) ? "on" : "off")); + break; + case REGRESS_etc: + if (sh_isregress(n)) + { + intercept_etc = opt_info.arg; + TRACE(etc, argv[0], ("%s", intercept_etc)); + } + else + TRACE(etc, argv[0], ("%s", "off")); + break; + } + } + continue; + } + break; + } + if (error_info.errors || *(argv + opt_info.index)) + errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NiL)); + return 0; +} + +#else + +NoN(regress) + +#endif diff --git a/src/cmd/ksh93/bltins/sleep.c b/src/cmd/ksh93/bltins/sleep.c new file mode 100644 index 0000000..ca9d055 --- /dev/null +++ b/src/cmd/ksh93/bltins/sleep.c @@ -0,0 +1,235 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * sleep delay + * + * David Korn + * AT&T Labs + * + */ + +#define sleep ______sleep +#include "defs.h" +#undef sleep +#include <error.h> +#include <errno.h> +#include <tmx.h> +#include "builtins.h" +#include "FEATURE/time" +#include "FEATURE/poll" +#ifdef _NEXT_SOURCE +# define sleep _ast_sleep +#endif /* _NEXT_SOURCE */ +#ifdef _lib_poll_notimer +# undef _lib_poll +#endif /* _lib_poll_notimer */ + +int b_sleep(register int argc,char *argv[],Shbltin_t *context) +{ + register char *cp; + register double d=0; + register Shell_t *shp = context->shp; + int sflag=0; + time_t tloc = 0; + char *last; + if(!(shp->sigflag[SIGALRM]&(SH_SIGFAULT|SH_SIGOFF))) + sh_sigtrap(SIGALRM); + while((argc = optget(argv,sh_optsleep))) switch(argc) + { + case 's': + sflag=1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(cp = *argv) + { + d = strtod(cp, &last); + if(*last) + { + Time_t now,ns; + char* pp; + now = TMX_NOW; + if(*cp == 'P' || *cp == 'p') + ns = tmxdate(cp, &last, now); + else if(*last=='.' && shp->decomma && d==(unsigned long)d) + { + *(pp=last) = ','; + if(!strchr(cp,'.')) + d = strtod(cp,&last); + *pp = '.'; + if(*last==0) + goto skip; + } + else if(*last!='.' && *last!=',') + { + if(pp = sfprints("exact %s", cp)) + ns = tmxdate(pp, &last, now); + if(*last && (pp = sfprints("p%s", cp))) + ns = tmxdate(pp, &last, now); + } + if(*last) + errormsg(SH_DICT,ERROR_exit(1),e_number,*argv); + d = ns - now; + d /= TMX_RESOLUTION; + } +skip: + if(argv[1]) + errormsg(SH_DICT,ERROR_exit(1),e_oneoperand); + } + else if(!sflag) + errormsg(SH_DICT,ERROR_exit(1),e_oneoperand); + if(d > .10) + { + time(&tloc); + tloc += (time_t)(d+.5); + } + if(sflag && d==0) + pause(); + else while(1) + { + time_t now; + errno = 0; + shp->lastsig=0; + sh_delay(d); + if(sflag || tloc==0 || errno!=EINTR || shp->lastsig) + break; + sh_sigcheck(shp); + if(tloc < (now=time(NIL(time_t*)))) + break; + d = (double)(tloc-now); + if(shp->sigflag[SIGALRM]&SH_SIGTRAP) + sh_timetraps(shp); + } + return(0); +} + +static void completed(void * handle) +{ + char *expired = (char*)handle; + *expired = 1; +} + +unsigned int sleep(unsigned int sec) +{ + Shell_t *shp = sh_getinterp(); + pid_t newpid, curpid=getpid(); + void *tp; + char expired = 0; + shp->lastsig = 0; + tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired); + do + { + if(!shp->gd->waitevent || (*shp->gd->waitevent)(-1,-1L,0)==0) + pause(); + if(shp->sigflag[SIGALRM]&SH_SIGTRAP) + sh_timetraps(shp); + if((newpid=getpid()) != curpid) + { + curpid = newpid; + shp->lastsig = 0; + shp->trapnote &= ~SH_SIGSET; + if(expired) + expired = 0; + else + timerdel(tp); + tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired); + } + } + while(!expired && shp->lastsig==0); + if(!expired) + timerdel(tp); + sh_sigcheck(shp); + return(0); +} + +/* + * delay execution for time <t> + */ + +void sh_delay(double t) +{ + register int n = (int)t; + Shell_t *shp = sh_getinterp(); +#ifdef _lib_poll + struct pollfd fd; + if(t<=0) + return; + else if(n > 30) + { + sleep(n); + t -= n; + } + if(n=(int)(1000*t)) + { + if(!shp->gd->waitevent || (*shp->gd->waitevent)(-1,(long)n,0)==0) + poll(&fd,0,n); + } +#else +# if defined(_lib_select) && defined(_mem_tv_usec_timeval) + struct timeval timeloc; + if(t<=0) + return; + if(n=(int)(1000*t) && shp->gd->waitevent && (*shp->gd->waitevent)(-1,(long)n,0)) + return; + n = (int)t; + timeloc.tv_sec = n; + timeloc.tv_usec = 1000000*(t-(double)n); + select(0,(fd_set*)0,(fd_set*)0,(fd_set*)0,&timeloc); +# else +# ifdef _lib_select + /* for 9th edition machines */ + if(t<=0) + return; + if(n > 30) + { + sleep(n); + t -= n; + } + if(n=(int)(1000*t)) + { + if(!shp->gd->waitevent || (*shp->gd->waitevent)(-1,(long)n,0)==0) + select(0,(fd_set*)0,(fd_set*)0,n); + } +# else + struct tms tt; + if(t<=0) + return; + sleep(n); + t -= n; + if(t) + { + clock_t begin = times(&tt); + if(begin==0) + return; + t *= shp->gd->lim.clk_tck; + n += (t+.5); + while((times(&tt)-begin) < n); + } +# endif +# endif +#endif /* _lib_poll */ +} diff --git a/src/cmd/ksh93/bltins/test.c b/src/cmd/ksh93/bltins/test.c new file mode 100644 index 0000000..52cb7cd --- /dev/null +++ b/src/cmd/ksh93/bltins/test.c @@ -0,0 +1,667 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * test expression + * [ expression ] + * + * David Korn + * AT&T Labs + * + */ + + +#include "defs.h" +#include <error.h> +#include <ls.h> +#include "io.h" +#include "terminal.h" +#include "test.h" +#include "builtins.h" +#include "FEATURE/externs" +#include "FEATURE/poll" +#include <tmx.h> + +#if !_lib_setregid +# undef _lib_setreuid +#endif /* _lib_setregid */ + +#ifdef S_ISSOCK +# if _pipe_socketpair +# if _socketpair_shutdown_mode +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino&&((p)->st_mode&(S_IRUSR|S_IWUSR))!=(S_IRUSR|S_IWUSR)) +# else +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino) +# endif +# else +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino) +# endif +# define isasock(f,p) (test_stat(f,p)>=0&&S_ISSOCK((p)->st_mode)) +#else +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)) +# define isasock(f,p) (0) +#endif + +#define permission(a,f) (sh_access(a,f)==0) +static time_t test_time(const char*, const char*); +static int test_stat(const char*, struct stat*); +static int test_mode(const char*); + +/* single char string compare */ +#define c_eq(a,c) (*a==c && *(a+1)==0) +/* two character string compare */ +#define c2_eq(a,c1,c2) (*a==c1 && *(a+1)==c2 && *(a+2)==0) + +struct test +{ + Shell_t *sh; + int ap; + int ac; + char **av; +}; + +static char *nxtarg(struct test*,int); +static int expr(struct test*,int); +static int e3(struct test*); + +static int test_strmatch(Shell_t *shp,const char *str, const char *pat) +{ + int match[2*(MATCH_MAX+1)],n; + register int c, m=0; + register const char *cp=pat; + while(c = *cp++) + { + if(c=='(') + m++; + if(c=='\\' && *cp) + cp++; + } + if(m) + m++; + else + match[0] = 0; + if(m > elementsof(match)/2) + m = elementsof(match)/2; + n = strgrpmatch(str, pat, match, m, STR_GROUP|STR_MAXIMAL|STR_LEFT|STR_RIGHT); + if(m==0 && n==1) + match[1] = strlen(str); + if(n) + sh_setmatch(shp, str, -1, n, match, 0); + return(n); +} + +int b_test(int argc, char *argv[],Shbltin_t *context) +{ + struct test tdata; + register char *cp = argv[0]; + register int not; + tdata.sh = context->shp; + tdata.av = argv; + tdata.ap = 1; + if(c_eq(cp,'[')) + { + cp = argv[--argc]; + if(!c_eq(cp, ']')) + errormsg(SH_DICT,ERROR_exit(2),e_missing,"']'"); + } + if(argc <= 1) + return(1); + cp = argv[1]; + if(c_eq(cp,'(') && argc<=6 && c_eq(argv[argc-1],')')) + { + /* special case ( binop ) to conform with standard */ + if(!(argc==4 && (not=sh_lookup(cp=argv[2],shtab_testops)))) + { + cp = (++argv)[1]; + argc -= 2; + } + } + not = c_eq(cp,'!'); + /* posix portion for test */ + switch(argc) + { + case 5: + if(!not) + break; + argv++; + /* fall through */ + case 4: + { + register int op = sh_lookup(cp=argv[2],shtab_testops); + if(op&TEST_BINOP) + break; + if(!op) + { + if(argc==5) + break; + if(not && cp[0]=='-' && cp[2]==0) + return(test_unop(tdata.sh,cp[1],argv[3])!=0); + else if(argv[1][0]=='-' && argv[1][2]==0) + return(!test_unop(tdata.sh,argv[1][1],cp)); + errormsg(SH_DICT,ERROR_exit(2),e_badop,cp); + } + return(test_binop(tdata.sh,op,argv[1],argv[3])^(argc!=5)); + } + case 3: + if(not) + return(*argv[2]!=0); + if(cp[0] != '-' || cp[2] || cp[1]=='?') + { + if(cp[0]=='-' && (cp[1]=='-' || cp[1]=='?') && + strcmp(argv[2],"--")==0) + { + char *av[3]; + av[0] = argv[0]; + av[1] = argv[1]; + av[2] = 0; + optget(av,sh_opttest); + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + return(2); + } + break; + } + return(!test_unop(tdata.sh,cp[1],argv[2])); + case 2: + return(*cp==0); + } + tdata.ac = argc; + return(!expr(&tdata,0)); +} + +/* + * evaluate a test expression. + * flag is 0 on outer level + * flag is 1 when in parenthesis + * flag is 2 when evaluating -a + */ +static int expr(struct test *tp,register int flag) +{ + register int r; + register char *p; + r = e3(tp); + while(tp->ap < tp->ac) + { + p = nxtarg(tp,0); + /* check for -o and -a */ + if(flag && c_eq(p,')')) + { + tp->ap--; + break; + } + if(*p=='-' && *(p+2)==0) + { + if(*++p == 'o') + { + if(flag==2) + { + tp->ap--; + break; + } + r |= expr(tp,3); + continue; + } + else if(*p == 'a') + { + r &= expr(tp,2); + continue; + } + } + if(flag==0) + break; + errormsg(SH_DICT,ERROR_exit(2),e_badsyntax); + } + return(r); +} + +static char *nxtarg(struct test *tp,int mt) +{ + if(tp->ap >= tp->ac) + { + if(mt) + { + tp->ap++; + return(0); + } + errormsg(SH_DICT,ERROR_exit(2),e_argument); + } + return(tp->av[tp->ap++]); +} + + +static int e3(struct test *tp) +{ + register char *arg, *cp; + register int op; + char *binop; + arg=nxtarg(tp,0); + if(arg && c_eq(arg, '!')) + return(!e3(tp)); + if(c_eq(arg, '(')) + { + op = expr(tp,1); + cp = nxtarg(tp,0); + if(!cp || !c_eq(cp, ')')) + errormsg(SH_DICT,ERROR_exit(2),e_missing,"')'"); + return(op); + } + cp = nxtarg(tp,1); + if(cp!=0 && (c_eq(cp,'=') || c2_eq(cp,'!','='))) + goto skip; + if(c2_eq(arg,'-','t')) + { + if(cp) + { + op = strtol(cp,&binop, 10); + return(*binop?0:tty_check(op)); + } + else + { + /* test -t with no arguments */ + tp->ap--; + return(tty_check(1)); + } + } + if(*arg=='-' && arg[2]==0) + { + op = arg[1]; + if(!cp) + { + /* for backward compatibility with new flags */ + if(op==0 || !strchr(test_opchars+10,op)) + return(1); + errormsg(SH_DICT,ERROR_exit(2),e_argument); + } + if(strchr(test_opchars,op)) + return(test_unop(tp->sh,op,cp)); + } + if(!cp) + { + tp->ap--; + return(*arg!=0); + } +skip: + op = sh_lookup(binop=cp,shtab_testops); + if(!(op&TEST_BINOP)) + cp = nxtarg(tp,0); + if(!op) + errormsg(SH_DICT,ERROR_exit(2),e_badop,binop); + if(op==TEST_AND || op==TEST_OR) + tp->ap--; + return(test_binop(tp->sh,op,arg,cp)); +} + +int test_unop(Shell_t *shp,register int op,register const char *arg) +{ + struct stat statb; + int f; + switch(op) + { + case 'r': + return(permission(arg, R_OK)); + case 'w': + return(permission(arg, W_OK)); + case 'x': + return(permission(arg, X_OK)); + case 'V': +#if SHOPT_FS_3D + { + register int offset = staktell(); + if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode)) + return(0); + /* add trailing / */ + stakputs(arg); + stakputc('/'); + stakputc(0); + arg = (const char*)stakptr(offset); + stakseek(offset); + /* FALL THRU */ + } +#else + return(0); +#endif /* SHOPT_FS_3D */ + case 'd': + return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode)); + case 'c': + return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode)); + case 'b': + return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode)); + case 'f': + return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode)); + case 'u': + return(test_mode(arg)&S_ISUID); + case 'g': + return(test_mode(arg)&S_ISGID); + case 'k': +#ifdef S_ISVTX + return(test_mode(arg)&S_ISVTX); +#else + return(0); +#endif /* S_ISVTX */ +#if SHOPT_TEST_L + case 'l': +#endif + case 'L': + case 'h': /* undocumented, and hopefully will disappear */ + if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0) + return(0); + return(S_ISLNK(statb.st_mode)); + + case 'C': +#ifdef S_ISCTG + return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode)); +#else + return(0); +#endif /* S_ISCTG */ + case 'H': +#ifdef S_ISCDF + { + register int offset = staktell(); + if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)) + return(1); + stakputs(arg); + stakputc('+'); + stakputc(0); + arg = (const char*)stakptr(offset); + stakseek(offset); + return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)); + } +#else + return(0); +#endif /* S_ISCDF */ + + case 'S': + return(isasock(arg,&statb)); + case 'N': + return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb)); + case 'p': + return(isapipe(arg,&statb)); + case 'n': + return(*arg != 0); + case 'z': + return(*arg == 0); + case 's': + sfsync(sfstdout); + case 'O': + case 'G': + if(*arg==0 || test_stat(arg,&statb)<0) + return(0); + if(op=='s') + return(statb.st_size>0); + else if(op=='O') + return(statb.st_uid==shp->gd->userid); + return(statb.st_gid==shp->gd->groupid); + case 'a': + case 'e': + if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK)) + return(1); + return(permission(arg, F_OK)); + case 'o': + f=1; + if(*arg=='?') + return(sh_lookopt(arg+1,&f)>0); + op = sh_lookopt(arg,&f); + return(op && (f==(sh_isoption(op)!=0))); + case 't': + { + char *last; + op = strtol(arg,&last, 10); + return(*last?0:tty_check(op)); + } + case 'v': + case 'R': + { + Namval_t *np; + Namarr_t *ap; + int isref; + if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF))) + return(0); + isref = nv_isref(np); + if(op=='R') + return(isref); + if(isref) + { + if(np->nvalue.cp) + np = nv_refnode(np); + else + return(0); + + } + if(ap = nv_arrayptr(np)) + return(nv_arrayisset(np,ap)); + return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER)); + } + default: + { + static char a[3] = "-?"; + a[1]= op; + errormsg(SH_DICT,ERROR_exit(2),e_badop,a); + /* NOTREACHED */ + return(0); + } + } +} + +int test_binop(Shell_t *shp,register int op,const char *left,const char *right) +{ + register double lnum,rnum; + if(op&TEST_ARITH) + { + while(*left=='0') + left++; + while(*right=='0') + right++; + lnum = sh_arith(shp,left); + rnum = sh_arith(shp,right); + } + switch(op) + { + /* op must be one of the following values */ + case TEST_AND: + case TEST_OR: + return(*left!=0); + case TEST_PEQ: + return(test_strmatch(shp, left, right)); + case TEST_PNE: + return(!test_strmatch(shp, left, right)); + case TEST_SGT: + return(strcoll(left, right)>0); + case TEST_SLT: + return(strcoll(left, right)<0); + case TEST_SEQ: + return(strcmp(left, right)==0); + case TEST_SNE: + return(strcmp(left, right)!=0); + case TEST_EF: + return(test_inode(left,right)); + case TEST_NT: + return(test_time(left,right)>0); + case TEST_OT: + return(test_time(left,right)<0); + case TEST_EQ: + return(lnum==rnum); + case TEST_NE: + return(lnum!=rnum); + case TEST_GT: + return(lnum>rnum); + case TEST_LT: + return(lnum<rnum); + case TEST_GE: + return(lnum>=rnum); + case TEST_LE: + return(lnum<=rnum); + } + /* NOTREACHED */ + return(0); +} + +/* + * returns the modification time of f1 - modification time of f2 + */ + +static time_t test_time(const char *file1,const char *file2) +{ + Time_t t1, t2; + struct stat statb1,statb2; + int r=test_stat(file2,&statb2); + if(test_stat(file1,&statb1)<0) + return(r<0?0:-1); + if(r<0) + return(1); + t1 = tmxgetmtime(&statb1); + t2 = tmxgetmtime(&statb2); + if (t1 > t2) + return(1); + if (t1 < t2) + return(-1); + return(0); +} + +/* + * return true if inode of two files are the same + */ + +int test_inode(const char *file1,const char *file2) +{ + struct stat stat1,stat2; + if(test_stat(file1,&stat1)>=0 && test_stat(file2,&stat2)>=0) + if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino) + return(1); + return(0); +} + + +/* + * This version of access checks against effective uid/gid + * The static buffer statb is shared with test_mode. + */ + +int sh_access(register const char *name, register int mode) +{ + Shell_t *shp = sh_getinterp(); + struct stat statb; + if(*name==0) + return(-1); + if(sh_isdevfd(name)) + return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode)); + /* can't use access function for execute permission with root */ + if(mode==X_OK && shp->gd->euserid==0) + goto skip; + if(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid) + return(access(name,mode)); +#ifdef _lib_setreuid + /* swap the real uid to effective, check access then restore */ + /* first swap real and effective gid, if different */ + if(shp->gd->groupid==shp->gd->euserid || setregid(shp->gd->egroupid,shp->gd->groupid)==0) + { + /* next swap real and effective uid, if needed */ + if(shp->gd->userid==shp->gd->euserid || setreuid(shp->gd->euserid,shp->gd->userid)==0) + { + mode = access(name,mode); + /* restore ids */ + if(shp->gd->userid!=shp->gd->euserid) + setreuid(shp->gd->userid,shp->gd->euserid); + if(shp->gd->groupid!=shp->gd->egroupid) + setregid(shp->gd->groupid,shp->gd->egroupid); + return(mode); + } + else if(shp->gd->groupid!=shp->gd->egroupid) + setregid(shp->gd->groupid,shp->gd->egroupid); + } +#endif /* _lib_setreuid */ +skip: + if(test_stat(name, &statb) == 0) + { + if(mode == F_OK) + return(mode); + else if(shp->gd->euserid == 0) + { + if(!S_ISREG(statb.st_mode) || mode!=X_OK) + return(0); + /* root needs execute permission for someone */ + mode = (S_IXUSR|S_IXGRP|S_IXOTH); + } + else if(shp->gd->euserid == statb.st_uid) + mode <<= 6; + else if(shp->gd->egroupid == statb.st_gid) + mode <<= 3; +#ifdef _lib_getgroups + /* on some systems you can be in several groups */ + else + { + static int maxgroups; + gid_t *groups; + register int n; + if(maxgroups==0) + { + /* first time */ + if((maxgroups=getgroups(0,(gid_t*)0)) <= 0) + { + /* pre-POSIX system */ + maxgroups=NGROUPS_MAX; + } + } + groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t)); + n = getgroups(maxgroups,groups); + while(--n >= 0) + { + if(groups[n] == statb.st_gid) + { + mode <<= 3; + break; + } + } + } +# endif /* _lib_getgroups */ + if(statb.st_mode & mode) + return(0); + } + return(-1); +} + +/* + * Return the mode bits of file <file> + * If <file> is null, then the previous stat buffer is used. + * The mode bits are zero if the file doesn't exist. + */ + +static int test_mode(register const char *file) +{ + struct stat statb; + statb.st_mode = 0; + if(file && (*file==0 || test_stat(file,&statb)<0)) + return(0); + return(statb.st_mode); +} + +/* + * do an fstat() for /dev/fd/n, otherwise stat() + */ +static int test_stat(const char *name,struct stat *buff) +{ + if(*name==0) + { + errno = ENOENT; + return(-1); + } + if(sh_isdevfd(name)) + return(fstat((int)strtol(name+8, (char**)0, 10),buff)); + else + return(stat(name,buff)); +} diff --git a/src/cmd/ksh93/bltins/trap.c b/src/cmd/ksh93/bltins/trap.c new file mode 100644 index 0000000..f5dbedc --- /dev/null +++ b/src/cmd/ksh93/bltins/trap.c @@ -0,0 +1,420 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * trap [-p] action sig... + * kill [-l] [sig...] + * kill [-s sig] pid... + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include "defs.h" +#include "jobs.h" +#include "builtins.h" + +#define L_FLAG 1 +#define S_FLAG 2 + +static const char trapfmt[] = "trap -- %s %s\n"; + +static int sig_number(Shell_t*,const char*); +static void sig_list(Shell_t*,int); + +int b_trap(int argc,char *argv[],Shbltin_t *context) +{ + register char *arg = argv[1]; + register int sig, clear = 0, dflag = 0, pflag = 0; + register Shell_t *shp = context->shp; + NOT_USED(argc); + while (sig = optget(argv, sh_opttrap)) switch (sig) + { + case 'p': + pflag=1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); + if(arg = *argv) + { + char *action = arg; + if(!dflag && !pflag) + { + /* first argument all digits or - means clear */ + while(isdigit(*arg)) + arg++; + clear = (arg!=action && *arg==0); + if(!clear) + { + ++argv; + if(*action=='-' && action[1]==0) + clear++; + /* + * NOTE: 2007-11-26: workaround for tests/signal.sh + * if function semantics can be worked out then it + * may merit a -d,--default option + */ + else if(*action=='+' && action[1]==0 && shp->st.self == &shp->global) + { + clear++; + dflag++; + } + } + if(!argv[0]) + errormsg(SH_DICT,ERROR_exit(1),e_condition); + } + while(arg = *argv++) + { + sig = sig_number(shp,arg); + if(sig<0) + { + errormsg(SH_DICT,2,e_trap,arg); + return(1); + } + /* internal traps */ + if(sig&SH_TRAP) + { + sig &= ~SH_TRAP; + if(sig>SH_DEBUGTRAP) + { + errormsg(SH_DICT,2,e_trap,arg); + return(1); + } + if(pflag) + { + if(arg=shp->st.trap[sig]) + sfputr(sfstdout,sh_fmtq(arg),'\n'); + continue; + } + if(shp->st.trap[sig]) + free(shp->st.trap[sig]); + shp->st.trap[sig] = 0; + if(!clear && *action) + shp->st.trap[sig] = strdup(action); + if(sig == SH_DEBUGTRAP) + { + if(shp->st.trap[sig]) + shp->trapnote |= SH_SIGTRAP; + else + shp->trapnote = 0; + } + continue; + } + if(sig>shp->gd->sigmax) + { + errormsg(SH_DICT,2,e_trap,arg); + return(1); + } + else if(pflag) + { + char **trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom); + if(arg=trapcom[sig]) + sfputr(sfstdout,arg,'\n'); + } + else if(clear) + { + sh_sigclear(sig); + if(dflag) + signal(sig,SIG_DFL); + } + else + { + if(sig >= shp->st.trapmax) + shp->st.trapmax = sig+1; + arg = shp->st.trapcom[sig]; + sh_sigtrap(sig); + shp->st.trapcom[sig] = (shp->sigflag[sig]&SH_SIGOFF) ? Empty : strdup(action); + if(arg && arg != Empty) + free(arg); + } + } + } + else /* print out current traps */ + sig_list(shp,-1); + return(0); +} + +int b_kill(int argc,char *argv[],Shbltin_t *context) +{ + register char *signame; + register int sig=SIGTERM, flag=0, n; + register Shell_t *shp = context->shp; + NOT_USED(argc); + while((n = optget(argv,sh_optkill))) switch(n) + { + case ':': + if((signame=argv[opt_info.index++]) && (sig=sig_number(shp,signame+1))>=0) + goto endopts; + opt_info.index--; + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case 'n': + sig = (int)opt_info.num; + goto endopts; + case 's': + flag |= S_FLAG; + signame = opt_info.arg; + goto endopts; + case 'l': + flag |= L_FLAG; + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } +endopts: + argv += opt_info.index; + if(*argv && strcmp(*argv,"--")==0 && strcmp(*(argv-1),"--")!=0) + argv++; + if(error_info.errors || flag==(L_FLAG|S_FLAG) || (!(*argv) && !(flag&L_FLAG))) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); + /* just in case we send a kill -9 $$ */ + sfsync(sfstderr); + if(flag&L_FLAG) + { + if(!(*argv)) + sig_list(shp,0); + else while(signame = *argv++) + { + if(isdigit(*signame)) + sig_list(shp,((int)strtol(signame, (char**)0, 10)&0177)+1); + else + { + if((sig=sig_number(shp,signame))<0) + { + shp->exitval = 2; + errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame); + } + sfprintf(sfstdout,"%d\n",sig); + } + } + return(shp->exitval); + } + if(flag&S_FLAG) + { + if((sig=sig_number(shp,signame)) < 0 || sig > shp->gd->sigmax) + errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame); + } + if(job_walk(sfstdout,job_kill,sig,argv)) + shp->exitval = 1; + return(shp->exitval); +} + +/* + * Given the name or number of a signal return the signal number + */ + +static int sig_number(Shell_t *shp,const char *string) +{ + const Shtable_t *tp; + register int n,o,sig=0; + char *last, *name; + if(isdigit(*string)) + { + n = strtol(string,&last,10); + if(*last) + n = -1; + } + else + { + register int c; + o = staktell(); + do + { + c = *string++; + if(islower(c)) + c = toupper(c); + stakputc(c); + } + while(c); + stakseek(o); + if(memcmp(stakptr(o),"SIG",3)==0) + { + sig = 1; + o += 3; + if(isdigit(*stakptr(o))) + { + n = strtol(stakptr(o),&last,10); + if(!*last) + return(n); + } + } + tp = sh_locate(stakptr(o),(const Shtable_t*)shtab_signals,sizeof(*shtab_signals)); + n = tp->sh_number; + if(sig==1 && (n>=(SH_TRAP-1) && n < (1<<SH_SIGBITS))) + { + /* sig prefix cannot match internal traps */ + n = 0; + tp = (Shtable_t*)((char*)tp + sizeof(*shtab_signals)); + if(strcmp(stakptr(o),tp->sh_name)==0) + n = tp->sh_number; + } + if((n>>SH_SIGBITS)&SH_SIGRUNTIME) + n = shp->gd->sigruntime[(n&((1<<SH_SIGBITS)-1))-1]; + else + { + n &= (1<<SH_SIGBITS)-1; + if(n < SH_TRAP) + n--; + } + if(n<0 && shp->gd->sigruntime[1] && (name=stakptr(o)) && *name++=='R' && *name++=='T') + { + if(name[0]=='M' && name[1]=='I' && name[2]=='N' && name[3]=='+') + { + if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name) + n = shp->gd->sigruntime[SH_SIGRTMIN] + sig; + } + else if(name[0]=='M' && name[1]=='A' && name[2]=='X' && name[3]=='-') + { + if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name) + n = shp->gd->sigruntime[SH_SIGRTMAX] - sig; + } + else if((sig=(int)strtol(name,&name,10)) > 0 && !*name) + n = shp->gd->sigruntime[SH_SIGRTMIN] + sig - 1; + if(n<shp->gd->sigruntime[SH_SIGRTMIN] || n>shp->gd->sigruntime[SH_SIGRTMAX]) + n = -1; + } + } + return(n); +} + +/* + * synthesize signal name for sig in buf + * pfx!=0 prepends SIG to default signal number + */ +static char* sig_name(Shell_t *shp,int sig, char* buf, int pfx) +{ + register int i; + + i = 0; + if(sig>shp->gd->sigruntime[SH_SIGRTMIN] && sig<shp->gd->sigruntime[SH_SIGRTMAX]) + { + buf[i++] = 'R'; + buf[i++] = 'T'; + buf[i++] = 'M'; + if(sig>shp->gd->sigruntime[SH_SIGRTMIN]+(shp->gd->sigruntime[SH_SIGRTMAX]-shp->gd->sigruntime[SH_SIGRTMIN])/2) + { + buf[i++] = 'A'; + buf[i++] = 'X'; + buf[i++] = '-'; + sig = shp->gd->sigruntime[SH_SIGRTMAX]-sig; + } + else + { + buf[i++] = 'I'; + buf[i++] = 'N'; + buf[i++] = '+'; + sig = sig-shp->gd->sigruntime[SH_SIGRTMIN]; + } + } + else if(pfx) + { + buf[i++] = 'S'; + buf[i++] = 'I'; + buf[i++] = 'G'; + } + i += sfsprintf(buf+i, 8, "%d", sig); + buf[i] = 0; + return buf; +} + +/* + * if <flag> is positive, then print signal name corresponding to <flag> + * if <flag> is zero, then print all signal names + * if <flag> is negative, then print all traps + */ +static void sig_list(register Shell_t *shp,register int flag) +{ + register const struct shtable2 *tp; + register int sig; + register char *sname; + char name[10]; + const char *names[SH_TRAP]; + const char *traps[SH_DEBUGTRAP+1]; + tp=shtab_signals; + if(flag<=0) + { + /* not all signals may be defined, so initialize */ + for(sig=shp->gd->sigmax; sig>=0; sig--) + names[sig] = 0; + for(sig=SH_DEBUGTRAP; sig>=0; sig--) + traps[sig] = 0; + } + for(; *tp->sh_name; tp++) + { + sig = tp->sh_number&((1<<SH_SIGBITS)-1); + if (((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) && (sig = shp->gd->sigruntime[sig-1]+1) == 1) + continue; + if(sig==flag) + { + sfprintf(sfstdout,"%s\n",tp->sh_name); + return; + } + else if(sig&SH_TRAP) + traps[sig&~SH_TRAP] = (char*)tp->sh_name; + else if(sig-- && sig < elementsof(names)) + names[sig] = (char*)tp->sh_name; + } + if(flag > 0) + sfputr(sfstdout, sig_name(shp,flag-1,name,0), '\n'); + else if(flag<0) + { + /* print the traps */ + register char *trap,**trapcom; + sig = shp->st.trapmax; + /* use parent traps if otrapcom is set (for $(trap) */ + trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom); + while(--sig >= 0) + { + if(!(trap=trapcom[sig])) + continue; + if(sig > shp->gd->sigmax || !(sname=(char*)names[sig])) + sname = sig_name(shp,sig,name,1); + sfprintf(sfstdout,trapfmt,sh_fmtq(trap),sname); + } + for(sig=SH_DEBUGTRAP; sig>=0; sig--) + { + if(!(trap=shp->st.trap[sig])) + continue; + sfprintf(sfstdout,trapfmt,sh_fmtq(trap),traps[sig]); + } + } + else + { + /* print all the signal names */ + for(sig=1; sig <= shp->gd->sigmax; sig++) + { + if(!(sname=(char*)names[sig])) + sname = sig_name(shp,sig,name,1); + sfputr(sfstdout,sname,'\n'); + } + } +} diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c new file mode 100644 index 0000000..f5618a1 --- /dev/null +++ b/src/cmd/ksh93/bltins/typeset.c @@ -0,0 +1,1471 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * export [-p] [arg...] + * readonly [-p] [arg...] + * typeset [options] [arg...] + * alias [-ptx] [arg...] + * unalias [arg...] + * builtin [-sd] [-f file] [name...] + * set [options] [name...] + * unset [-fnv] [name...] + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include <error.h> +#include "path.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#include "variables.h" +#include "FEATURE/dynamic" + +struct tdata +{ + Shell_t *sh; + Namval_t *tp; + const char *wctname; + Sfio_t *outfile; + char *prefix; + char *tname; + char *help; + short aflag; + short pflag; + int argnum; + int scanmask; + Dt_t *scanroot; + char **argnam; + int indent; + int noref; +}; + + +static int print_namval(Sfio_t*, Namval_t*, int, struct tdata*); +static void print_attribute(Namval_t*,void*); +static void print_all(Sfio_t*, Dt_t*, struct tdata*); +static void print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*); +static int unall(int, char**, Dt_t*, Shell_t*); +static int setall(char**, int, Dt_t*, struct tdata*); +static void pushname(Namval_t*,void*); +static void(*nullscan)(Namval_t*,void*); + +static Namval_t *load_class(const char *name) +{ + errormsg(SH_DICT,ERROR_exit(1),"%s: type not loadable",name); + return(0); +} + +/* + * Note export and readonly are the same + */ +#if 0 + /* for the dictionary generator */ + int b_export(int argc,char *argv[],Shbltin_t *context){} +#endif +int b_readonly(int argc,char *argv[],Shbltin_t *context) +{ + register int flag; + char *command = argv[0]; + struct tdata tdata; + NOT_USED(argc); + memset((void*)&tdata,0,sizeof(tdata)); + tdata.sh = context->shp; + tdata.aflag = '-'; + while((flag = optget(argv,*command=='e'?sh_optexport:sh_optreadonly))) switch(flag) + { + case 'p': + tdata.prefix = command; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*))); + argv += (opt_info.index-1); + if(*command=='r') + flag = (NV_ASSIGN|NV_RDONLY|NV_VARNAME); +#ifdef _ENV_H + else if(!argv[1]) + { + char *cp,**env=env_get(tdata.sh->env); + while(cp = *env++) + { + if(tdata.prefix) + sfputr(sfstdout,tdata.prefix,' '); + sfprintf(sfstdout,"%s\n",sh_fmtq(cp)); + } + return(0); + } +#endif + else + { + flag = (NV_ASSIGN|NV_EXPORT|NV_IDENT); + if(!tdata.sh->prefix) + tdata.sh->prefix = ""; + } + return(setall(argv,flag,tdata.sh->var_tree, &tdata)); +} + + +int b_alias(int argc,register char *argv[],Shbltin_t *context) +{ + register unsigned flag = NV_NOARRAY|NV_NOSCOPE|NV_ASSIGN; + register Dt_t *troot; + register int n; + struct tdata tdata; + NOT_USED(argc); + memset((void*)&tdata,0,sizeof(tdata)); + tdata.sh = context->shp; + troot = tdata.sh->alias_tree; + if(*argv[0]=='h') + flag = NV_TAGGED; + if(argv[1]) + { + opt_info.offset = 0; + opt_info.index = 1; + *opt_info.option = 0; + tdata.argnum = 0; + tdata.aflag = *argv[1]; + while((n = optget(argv,sh_optalias))) switch(n) + { + case 'p': + tdata.prefix = argv[0]; + break; + case 't': + flag |= NV_TAGGED; + break; + case 'x': + flag |= NV_EXPORT; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); + argv += (opt_info.index-1); + if(flag&NV_TAGGED) + { + /* hacks to handle hash -r | -- */ + if(argv[1] && argv[1][0]=='-') + { + if(argv[1][1]=='r' && argv[1][2]==0) + { + Namval_t *np = nv_search((char*)PATHNOD,tdata.sh->var_tree,HASH_BUCKET); + nv_putval(np,nv_getval(np),NV_RDONLY); + argv++; + if(!argv[1]) + return(0); + } + if(argv[1][0]=='-') + { + if(argv[1][1]=='-' && argv[1][2]==0) + argv++; + else + errormsg(SH_DICT, ERROR_exit(1), e_option, argv[1]); + } + } + troot = tdata.sh->track_tree; + } + } + return(setall(argv,flag,troot,&tdata)); +} + + +#if 0 + /* for the dictionary generator */ + int b_local(int argc,char *argv[],Shbltin_t *context){} +#endif +int b_typeset(int argc,register char *argv[],Shbltin_t *context) +{ + register int n, flag = NV_VARNAME|NV_ASSIGN; + struct tdata tdata; + const char *optstring = sh_opttypeset; + Namdecl_t *ntp = (Namdecl_t*)context->ptr; + Dt_t *troot; + int isfloat=0, shortint=0, sflag=0; + NOT_USED(argc); + memset((void*)&tdata,0,sizeof(tdata)); + tdata.sh = context->shp; + if(ntp) + { + tdata.tp = ntp->tp; + opt_info.disc = (Optdisc_t*)ntp->optinfof; + optstring = ntp->optstring; + } + troot = tdata.sh->var_tree; + while((n = optget(argv,optstring))) + { + if(tdata.aflag==0) + tdata.aflag = *opt_info.option; + switch(n) + { + case 'a': + flag |= NV_IARRAY; + if(opt_info.arg && *opt_info.arg!='[') + { + opt_info.index--; + goto endargs; + } + tdata.tname = opt_info.arg; + break; + case 'A': + flag |= NV_ARRAY; + break; + case 'C': + flag |= NV_COMVAR; + break; + case 'E': + /* The following is for ksh88 compatibility */ + if(opt_info.offset && !strchr(argv[opt_info.index],'E')) + { + tdata.argnum = (int)opt_info.num; + break; + } + case 'F': + case 'X': + if(!opt_info.arg || (tdata.argnum = opt_info.num) <0) + tdata.argnum = (n=='X'?2*sizeof(Sfdouble_t):10); + isfloat = 1; + if(n=='E') + { + flag &= ~NV_HEXFLOAT; + flag |= NV_EXPNOTE; + } + else if(n=='X') + { + flag &= ~NV_EXPNOTE; + flag |= NV_HEXFLOAT; + } + break; + case 'b': + flag |= NV_BINARY; + break; + case 'm': + flag |= NV_MOVE; + break; + case 'n': + flag &= ~NV_VARNAME; + flag |= (NV_REF|NV_IDENT); + break; + case 'H': + flag |= NV_HOST; + break; + case 'T': + flag |= NV_TYPE; + tdata.prefix = opt_info.arg; + break; + case 'L': case 'Z': case 'R': + if(tdata.argnum==0) + tdata.argnum = (int)opt_info.num; + if(tdata.argnum < 0) + errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum); + if(n=='Z') + flag |= NV_ZFILL; + else + { + flag &= ~(NV_LJUST|NV_RJUST); + flag |= (n=='L'?NV_LJUST:NV_RJUST); + } + break; + case 'M': + if((tdata.wctname = opt_info.arg) && !nv_mapchar((Namval_t*)0,tdata.wctname)) + errormsg(SH_DICT, ERROR_exit(1),e_unknownmap, tdata.wctname); + if(tdata.wctname && strcmp(tdata.wctname,e_tolower)==0) + flag |= NV_UTOL; + else + flag |= NV_LTOU; + if(!tdata.wctname) + flag |= NV_UTOL; + break; + case 'f': + flag &= ~(NV_VARNAME|NV_ASSIGN); + troot = tdata.sh->fun_tree; + break; + case 'i': + if(!opt_info.arg || (tdata.argnum = opt_info.num) <0) + tdata.argnum = 10; + flag |= NV_INTEGER; + break; + case 'l': + tdata.wctname = e_tolower; + flag |= NV_UTOL; + break; + case 'p': + tdata.prefix = argv[0]; + tdata.pflag = 1; + flag &= ~NV_ASSIGN; + break; + case 'r': + flag |= NV_RDONLY; + break; +#ifdef SHOPT_TYPEDEF + case 'S': + sflag=1; + break; + case 'h': + tdata.help = opt_info.arg; + break; +#endif /*SHOPT_TYPEDEF*/ + case 's': + shortint=1; + break; + case 't': + flag |= NV_TAGGED; + break; + case 'u': + tdata.wctname = e_toupper; + flag |= NV_LTOU; + break; + case 'x': + flag &= ~NV_VARNAME; + flag |= (NV_EXPORT|NV_IDENT); + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + opt_info.disc = 0; + return(2); + } + } +endargs: + argv += opt_info.index; + opt_info.disc = 0; + /* handle argument of + and - specially */ + if(*argv && argv[0][1]==0 && (*argv[0]=='+' || *argv[0]=='-')) + tdata.aflag = *argv[0]; + else + argv--; + if((flag&NV_ZFILL) && !(flag&NV_LJUST)) + flag |= NV_RJUST; + if((flag&NV_INTEGER) && (flag&(NV_LJUST|NV_RJUST|NV_ZFILL))) + error_info.errors++; + if((flag&NV_BINARY) && (flag&(NV_LJUST|NV_UTOL|NV_LTOU))) + error_info.errors++; + if((flag&NV_MOVE) && (flag&~(NV_MOVE|NV_VARNAME|NV_ASSIGN))) + error_info.errors++; + if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN))) + error_info.errors++; + if((flag&NV_TYPE) && (flag&~(NV_TYPE|NV_VARNAME|NV_ASSIGN))) + error_info.errors++; + if(troot==tdata.sh->fun_tree && ((isfloat || flag&~(NV_FUNCT|NV_TAGGED|NV_EXPORT|NV_LTOU)))) + error_info.errors++; + if(sflag && troot==tdata.sh->fun_tree) + { + /* static function */ + sflag = 0; + flag |= NV_STATICF; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*))); + if(isfloat) + flag |= NV_DOUBLE; + if(shortint) + { + flag &= ~NV_LONG; + flag |= NV_SHORT|NV_INTEGER; + } + if(sflag) + { + if(tdata.sh->mktype) + flag |= NV_REF|NV_TAGGED; + else if(!tdata.sh->typeinit) + flag |= NV_STATIC|NV_IDENT; + } + if(tdata.sh->fn_depth && !tdata.pflag) + flag |= NV_NOSCOPE; + if(tdata.help) + tdata.help = strdup(tdata.help); + if(flag&NV_TYPE) + { + Stk_t *stkp = tdata.sh->stk; + int off=0,offset = stktell(stkp); + if(!tdata.prefix) + return(sh_outtype(tdata.sh,sfstdout)); + sfputr(stkp,NV_CLASS,-1); +#if SHOPT_NAMESPACE + if(tdata.sh->namespace) + { + off = stktell(stkp)+1; + sfputr(stkp,nv_name(tdata.sh->namespace),'.'); + } + else +#endif /* SHOPT_NAMESPACE */ + if(NV_CLASS[sizeof(NV_CLASS)-2]!='.') + sfputc(stkp,'.'); + sfputr(stkp,tdata.prefix,0); + tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN); +#if SHOPT_NAMESPACE + if(!tdata.tp && off) + { + *stkptr(stkp,off)=0; + tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN); + } +#endif /* SHOPT_NAMESPACE */ + stkseek(stkp,offset); + if(!tdata.tp) + errormsg(SH_DICT,ERROR_exit(1),"%s: unknown type",tdata.prefix); + else if(nv_isnull(tdata.tp)) + nv_newtype(tdata.tp); + tdata.tp->nvenv = tdata.help; + flag &= ~NV_TYPE; + } + else if(tdata.aflag==0 && ntp && ntp->tp) + tdata.aflag = '-'; + if(!tdata.sh->mktype) + tdata.help = 0; + if(tdata.aflag=='+' && (flag&(NV_ARRAY|NV_IARRAY|NV_COMVAR))) + errormsg(SH_DICT,ERROR_exit(1),e_nounattr); + return(setall(argv,flag,troot,&tdata)); +} + +static void print_value(Sfio_t *iop, Namval_t *np, struct tdata *tp) +{ + char *name; + int aflag=tp->aflag; + if(nv_isnull(np)) + { + if(!np->nvflag) + return; + aflag = '+'; + } + else if(nv_istable(np)) + { + Dt_t *root = tp->sh->last_root; + Namval_t *nsp = tp->sh->namespace; + char *cp = name = nv_name(np); + if(*name=='.') + name++; + if(tp->indent) + sfnputc(iop,'\t',tp->indent); + sfprintf(iop,"namespace %s\n", name); + if(tp->indent) + sfnputc(iop,'\t',tp->indent); + sfprintf(iop,"{\n", name); + tp->indent++; + /* output types from namespace */ + tp->sh->namespace = 0; + tp->sh->prefix = nv_name(np)+1; + sh_outtype(tp->sh,iop); + tp->sh->prefix = 0; + tp->sh->namespace = np; + tp->sh->last_root = root; + /* output variables from namespace */ + print_scan(iop,NV_NOSCOPE,nv_dict(np),aflag=='+',tp); + tp->wctname = cp; + tp->sh->namespace = 0; + /* output functions from namespace */ + print_scan(iop,NV_FUNCTION|NV_NOSCOPE,tp->sh->fun_tree,aflag=='+',tp); + tp->wctname = 0; + tp->sh->namespace = nsp; + if(--tp->indent) + sfnputc(iop,'\t',tp->indent); + sfwrite(iop,"}\n",2); + return; + } + sfputr(iop,nv_name(np),aflag=='+'?'\n':'='); + if(aflag=='+') + return; + if(nv_isarray(np) && nv_arrayptr(np)) + { + nv_outnode(np,iop,-1,0); + sfwrite(iop,")\n",2); + } + else + { + if(nv_isvtree(np)) + nv_onattr(np,NV_EXPORT); + if(!(name = nv_getval(np))) + name = Empty; + if(!nv_isvtree(np)) + name = sh_fmtq(name); + sfputr(iop,name,'\n'); + } +} + +static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp) +{ + register char *name; + char *last = 0; + int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE)); + int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY); + Shell_t *shp =tp->sh; + if(!shp->prefix) + { + if(!tp->pflag) + nvflags |= NV_NOSCOPE; + } + else if(*shp->prefix==0) + shp->prefix = 0; + if(*argv[0]=='+') + nvflags |= NV_NOADD; + flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT|NV_STATIC|NV_COMVAR|NV_IARRAY); + if(argv[1]) + { + if(flag&NV_REF) + { + flag &= ~NV_REF; + ref=1; + if(tp->aflag!='-') + nvflags |= NV_NOREF; + } + if(tp->pflag) + nvflags |= NV_NOREF; + while(name = *++argv) + { + register unsigned newflag; + register Namval_t *np; + Namarr_t *ap; + Namval_t *mp; + unsigned curflag; + if(troot == shp->fun_tree) + { + /* + *functions can be exported or + * traced but not set + */ + flag &= ~NV_ASSIGN; + if(flag&NV_LTOU) + { + /* Function names cannot be special builtin */ + if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC)) + errormsg(SH_DICT,ERROR_exit(1),e_badfun,name); +#if SHOPT_NAMESPACE + if(shp->namespace) + np = sh_fsearch(shp,name,NV_ADD|HASH_NOSCOPE); + else +#endif /* SHOPT_NAMESPACE */ + np = nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE); + } + else + { + if(shp->prefix) + { + sfprintf(shp->strbuf,"%s.%s%c",shp->prefix,name,0); + name = sfstruse(shp->strbuf); + } +#if SHOPT_NAMESPACE + np = 0; + if(shp->namespace) + np = sh_fsearch(shp,name,HASH_NOSCOPE); + if(!np) +#endif /* SHOPT_NAMESPACE */ + if(np=nv_search(name,troot,0)) + { + if(!is_afunction(np)) + np = 0; + } + else if(memcmp(name,".sh.math.",9)==0 && sh_mathstd(name+9)) + continue; + } + if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU))) + { + if(flag==0 && !tp->help) + { + print_namval(sfstdout,np,tp->aflag=='+',tp); + continue; + } + if(shp->subshell && !shp->subshare) + sh_subfork(); + if(tp->aflag=='-') + nv_onattr(np,flag|NV_FUNCTION); + else if(tp->aflag=='+') + nv_offattr(np,flag); + } + else + r++; + if(tp->help) + { + int offset = stktell(shp->stk); + if(!np) + { + sfputr(shp->stk,shp->prefix,'.'); + sfputr(shp->stk,name,0); + np = nv_search(stkptr(shp->stk,offset),troot,0); + stkseek(shp->stk,offset); + } + if(np && np->nvalue.cp) + np->nvalue.rp->help = tp->help; + } + continue; + } + /* tracked alias */ + if(troot==shp->track_tree && tp->aflag=='-') + { + np = nv_search(name,troot,NV_ADD); + path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*))); + continue; + } + np = nv_open(name,troot,nvflags|((nvflags&NV_ASSIGN)?0:NV_ARRAY)|((iarray|(nvflags&NV_REF))?NV_FARRAY:0)); + if(!np) + continue; + if(nv_isnull(np) && !nv_isarray(np) && nv_isattr(np,NV_NOFREE)) + nv_offattr(np,NV_NOFREE); + else if(tp->tp && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)) && (ap->nelem&ARRAY_TREE)) +#if 0 + errormsg(SH_DICT,ERROR_exit(1),e_typecompat,nv_name(np)); +#else + errormsg(SH_DICT,ERROR_exit(1),"%s: typecompat",nv_name(np)); +#endif + else if((ap=nv_arrayptr(np)) && nv_aindex(np)>0 && ap->nelem==1 && nv_getval(np)==Empty) + { + ap->nelem++; + _nv_unset(np,0); + ap->nelem--; + } + if(tp->pflag) + { + if(!nv_istable(np)) + nv_attribute(np,sfstdout,tp->prefix,1); + print_value(sfstdout,np,tp); + continue; + } + if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'=')) + { + if(troot!=shp->var_tree && (nv_isnull(np) || !print_namval(sfstdout,np,0,tp))) + { + sfprintf(sfstderr,sh_translate(e_noalias),name); + r++; + } + if(!comvar && !iarray) + continue; + } + if(!nv_isarray(np) && !strchr(name,'=') && !(shp->envlist && nv_onlist(shp->envlist,name))) + { + if(comvar || (shp->last_root==shp->var_tree && (tp->tp || (!shp->st.real_fun && (nvflags&NV_STATIC)) || (!(flag&(NV_EXPORT|NV_RDONLY)) && nv_isattr(np,(NV_EXPORT|NV_IMPORT))==(NV_EXPORT|NV_IMPORT))))) +{ + _nv_unset(np,0); +} + } + if(troot==shp->var_tree) + { + if(iarray) + { + if(tp->tname) + nv_atypeindex(np,tp->tname+1); + else if(nv_isnull(np)) + nv_onattr(np,NV_ARRAY|(comvar?NV_NOFREE:0)); + else + { + if(ap && comvar) + ap->nelem |= ARRAY_TREE; + nv_putsub(np, (char*)0, 0); + } + } + else if(nvflags&NV_ARRAY) + { + if(comvar) + { + Namarr_t *ap=nv_arrayptr(np); + if(ap) + ap->nelem |= ARRAY_TREE; + else + { + _nv_unset(np,NV_RDONLY); + nv_onattr(np,NV_NOFREE); + } + } + nv_setarray(np,nv_associative); + } + else if(comvar && !nv_isvtree(np) && !nv_rename(np,flag|NV_COMVAR)) + nv_setvtree(np); + } + if(flag&NV_MOVE) + { + nv_rename(np, flag); + nv_close(np); + continue; + } + if(tp->tp && nv_type(np)!=tp->tp) + { + nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND); + flag = (np->nvflag&NV_NOCHANGE); + } + flag &= ~NV_ASSIGN; + if(last=strchr(name,'=')) + *last = 0; + if (shp->typeinit) + continue; + curflag = np->nvflag; + if(!(flag&NV_INTEGER) && (flag&(NV_LTOU|NV_UTOL))) + { + Namfun_t *fp; + char *cp; + if(!tp->wctname) + errormsg(SH_DICT,ERROR_exit(1),e_mapchararg,nv_name(np)); + cp = (char*)nv_mapchar(np,0); + if(fp=nv_mapchar(np,tp->wctname)) + { + if(tp->aflag=='+') + { + if(cp && strcmp(cp,tp->wctname)==0) + { + nv_disc(np,fp,NV_POP); + if(!(fp->nofree&1)) + free((void*)fp); + nv_offattr(np,flag&(NV_LTOU|NV_UTOL)); + } + } + else if(!cp || strcmp(cp,tp->wctname)) + { + nv_disc(np,fp,NV_LAST); + nv_onattr(np,flag&(NV_LTOU|NV_UTOL)); + } + } + } + if (tp->aflag == '-') + { + if((flag&NV_EXPORT) && (strchr(name,'.') || nv_isvtree(np))) + errormsg(SH_DICT,ERROR_exit(1),e_badexport,name); +#if SHOPT_BSH + if(flag&NV_EXPORT) + nv_offattr(np,NV_IMPORT); +#endif /* SHOPT_BSH */ + newflag = curflag; + if(flag&~NV_NOCHANGE) + newflag &= NV_NOCHANGE; + newflag |= flag; + if (flag & (NV_LJUST|NV_RJUST)) + { + if(!(flag&NV_RJUST)) + newflag &= ~NV_RJUST; + + else if(!(flag&NV_LJUST)) + newflag &= ~NV_LJUST; + } + } + else + { + if((flag&NV_RDONLY) && (curflag&NV_RDONLY)) + errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np)); + newflag = curflag & ~flag; + } + if (tp->aflag && (tp->argnum>0 || (curflag!=newflag))) + { + if(shp->subshell) + sh_assignok(np,1); + if(troot!=shp->var_tree) + nv_setattr(np,newflag&~NV_ASSIGN); + else + { + char *oldname=0; + int len=strlen(name); + if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER)) + tp->argnum = 10; + /* use reference name for export */ + if((newflag^curflag)&NV_EXPORT) + { + oldname = np->nvname; + np->nvname = name; + } + if(np->nvfun && !nv_isarray(np) && name[len-1]=='.') + newflag |= NV_NODISC; + nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum); + if(oldname) + np->nvname = oldname; + } + } + if(tp->help && !nv_isattr(np,NV_MINIMAL|NV_EXPORT)) + { + np->nvenv = tp->help; + nv_onattr(np,NV_EXPORT); + } + if(last) + *last = '='; + /* set or unset references */ + if(ref) + { + if(tp->aflag=='-') + { + Dt_t *hp=0; + if(nv_isattr(np,NV_PARAM) && shp->st.prevst) + { + if(!(hp=(Dt_t*)shp->st.prevst->save_tree)) + hp = dtvnext(shp->var_tree); + } + if(tp->sh->mktype) + nv_onattr(np,NV_REF|NV_FUNCT); + else + nv_setref(np,hp,NV_VARNAME); + } + else + nv_unref(np); + } + nv_close(np); + } + } + else + { + if(shp->prefix) + errormsg(SH_DICT,2, e_subcomvar,shp->prefix); + if(tp->aflag) + { + if(troot==shp->fun_tree) + { + flag |= NV_FUNCTION; + tp->prefix = 0; + } + else if(troot==shp->var_tree) + { + flag |= (nvflags&NV_ARRAY); + if(flag&NV_IARRAY) + flag |= NV_ARRAY; + if(!(flag&~NV_ASSIGN)) + tp->noref = 1; + } + if((flag&(NV_UTOL|NV_LTOU)) ==(NV_UTOL|NV_LTOU)) + { + print_scan(sfstdout,flag&~NV_UTOL,troot,tp->aflag=='+',tp); + flag &= ~NV_LTOU; + } + print_scan(sfstdout,flag,troot,tp->aflag=='+',tp); + if(tp->noref) + { + tp->noref = 0; + print_scan(sfstdout,flag|NV_REF,troot,tp->aflag=='+',tp); + } + } + else if(troot==shp->alias_tree) + print_scan(sfstdout,0,troot,0,tp); + else + print_all(sfstdout,troot,tp); + sfsync(sfstdout); + } + return(r); +} + +typedef void (*Iptr_t)(int,void*); + +#define GROWLIB 4 + +static void **liblist; +static unsigned short *libattr; +static int nlib; +static int maxlib; + +/* + * This allows external routines to load from the same library */ +void **sh_getliblist(void) +{ + return(liblist); +} + +/* + * add library to loaded list + * call (*lib_init)() on first load if defined + * always move to head of search list + * return: 0: already loaded 1: first load + */ +#if SHOPT_DYNAMIC +int sh_addlib(Shell_t *shp,void* library) +{ + register int n; + register int r; + Iptr_t initfn; + Shbltin_t *sp = &shp->bltindata; + + sp->nosfio = 0; + for (n = r = 0; n < nlib; n++) + { + if (r) + { + liblist[n-1] = liblist[n]; + libattr[n-1] = libattr[n]; + } + else if (liblist[n] == library) + r++; + } + if (r) + nlib--; + else if ((initfn = (Iptr_t)dlllook(library, "lib_init"))) + (*initfn)(0,sp); + if (nlib >= maxlib) + { + maxlib += GROWLIB; + if (liblist) + { + liblist = (void**)realloc((void*)liblist, (maxlib+1)*sizeof(void*)); + libattr = (unsigned short*)realloc((void*)libattr, (maxlib+1)*sizeof(unsigned short)); + } + else + { + liblist = (void**)malloc((maxlib+1)*sizeof(void*)); + libattr = (unsigned short*)malloc((maxlib+1)*sizeof(unsigned short)); + } + } + libattr[nlib] = (sp->nosfio?BLT_NOSFIO:0); + liblist[nlib++] = library; + liblist[nlib] = 0; + return !r; +} +#else +int sh_addlib(Shell_t *shp,void* library) +{ + return 0; +} +#endif /* SHOPT_DYNAMIC */ + +/* + * add change or list built-ins + * adding builtins requires dlopen() interface + */ +int b_builtin(int argc,char *argv[],Shbltin_t *context) +{ + register char *arg=0, *name; + register int n, r=0, flag=0; + register Namval_t *np; + long dlete=0; + struct tdata tdata; + Shbltin_f addr; + Stk_t *stkp; + void *library=0; + char *errmsg; +#ifdef SH_PLUGIN_VERSION + unsigned long ver; + int list = 0; + char path[1024]; +#endif + NOT_USED(argc); + memset(&tdata,0,sizeof(tdata)); + tdata.sh = context->shp; + stkp = tdata.sh->stk; + if(!tdata.sh->pathlist) + path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*)); + while (n = optget(argv,sh_optbuiltin)) switch (n) + { + case 's': + flag = BLT_SPC; + break; + case 'd': + dlete=1; + break; + case 'f': +#if SHOPT_DYNAMIC + arg = opt_info.arg; +#else + errormsg(SH_DICT,2, "adding built-ins not supported"); + error_info.errors++; +#endif /* SHOPT_DYNAMIC */ + break; + case 'l': +#ifdef SH_PLUGIN_VERSION + list = 1; +#endif + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*))); + if(arg || *argv) + { + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[-opt_info.index]); + if(sh_isoption(SH_PFSH)) + errormsg(SH_DICT,ERROR_exit(1),e_pfsh,argv[-opt_info.index]); + if(tdata.sh->subshell && !tdata.sh->subshare) + sh_subfork(); + } +#if SHOPT_DYNAMIC + if(arg) + { +#ifdef SH_PLUGIN_VERSION + if(!(library = dllplugin(SH_ID, arg, NiL, SH_PLUGIN_VERSION, &ver, RTLD_LAZY, path, sizeof(path)))) + { + errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dllerror(0)); + return(1); + } + if(list) + sfprintf(sfstdout, "%s %08lu %s\n", arg, ver, path); +#else +#if (_AST_VERSION>=20040404) + if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0))) +#else + if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0))) +#endif + { + errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror()); + return(1); + } +#endif + sh_addlib(tdata.sh,library); + } + else +#endif /* SHOPT_DYNAMIC */ + if(*argv==0 && !dlete) + { + print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata); + return(0); + } + r = 0; + flag = stktell(stkp); + while(arg = *argv) + { + name = path_basename(arg); + sfwrite(stkp,"b_",2); + sfputr(stkp,name,0); + errmsg = 0; + addr = 0; + for(n=(nlib?nlib:dlete); --n>=0;) + { + /* (char*) added for some sgi-mips compilers */ +#if SHOPT_DYNAMIC + if(dlete || (addr = (Shbltin_f)dlllook(liblist[n],stkptr(stkp,flag)))) +#else + if(dlete) +#endif /* SHOPT_DYNAMIC */ + { + if(np = sh_addbuiltin(arg, addr,pointerof(dlete))) + { + if(dlete || nv_isattr(np,BLT_SPC)) + errmsg = "restricted name"; + else + nv_onattr(np,libattr[n]); + } + break; + } + } + if(!dlete && !addr) + { + np = sh_addbuiltin(arg, 0 ,0); + if(np && nv_isattr(np,BLT_SPC)) + errmsg = "restricted name"; + else if(!np) + errmsg = "not found"; + } + if(errmsg) + { + errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg); + r = 1; + } + stkseek(stkp,flag); + argv++; + } + return(r); +} + +int b_set(int argc,register char *argv[],Shbltin_t *context) +{ + struct tdata tdata; + int was_monitor = sh_isoption(SH_MONITOR); + memset(&tdata,0,sizeof(tdata)); + tdata.sh = context->shp; + tdata.prefix=0; + if(argv[1]) + { + if(sh_argopts(argc,argv,tdata.sh) < 0) + return(2); + if(sh_isoption(SH_VERBOSE)) + sh_onstate(SH_VERBOSE); + else + sh_offstate(SH_VERBOSE); + if(sh_isoption(SH_MONITOR) && !was_monitor) + sh_onstate(SH_MONITOR); + else if(!sh_isoption(SH_MONITOR) && was_monitor) + sh_offstate(SH_MONITOR); + } + else + /*scan name chain and print*/ + print_scan(sfstdout,0,tdata.sh->var_tree,0,&tdata); + return(0); +} + +/* + * The removing of Shell variable names, aliases, and functions + * is performed here. + * Unset functions with unset -f + * Non-existent items being deleted give non-zero exit status + */ + +int b_unalias(int argc,register char *argv[],Shbltin_t *context) +{ + Shell_t *shp = context->shp; + return(unall(argc,argv,shp->alias_tree,shp)); +} + +int b_unset(int argc,register char *argv[],Shbltin_t *context) +{ + Shell_t *shp = context->shp; + return(unall(argc,argv,shp->var_tree,shp)); +} + +static int unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) +{ + register Namval_t *np; + register const char *name; + register int r; + Dt_t *dp; + int nflag=0,all=0,isfun,jmpval; + struct checkpt buff; + NOT_USED(argc); + if(troot==shp->alias_tree) + { + name = sh_optunalias; + if(shp->subshell) + troot = sh_subaliastree(0); + } + else + name = sh_optunset; + while(r = optget(argv,name)) switch(r) + { + case 'f': + troot = sh_subfuntree(1); + break; + case 'a': + all=1; + break; + case 'n': + nflag = NV_NOREF; + case 'v': + troot = shp->var_tree; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + argv += opt_info.index; + if(error_info.errors || (*argv==0 &&!all)) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); + if(!troot) + return(1); + r = 0; + if(troot==shp->var_tree) + nflag |= NV_VARNAME; + else + nflag = NV_NOSCOPE; + if(all) + { + dtclear(troot); + return(r); + } + sh_pushcontext(shp,&buff,1); + while(name = *argv++) + { + jmpval = sigsetjmp(buff.buff,0); + np = 0; + if(jmpval==0) + { +#if SHOPT_NAMESPACE + if(shp->namespace && troot!=shp->var_tree) + np = sh_fsearch(shp,name,nflag?HASH_NOSCOPE:0); + if(!np) +#endif /* SHOPT_NAMESPACE */ + np=nv_open(name,troot,NV_NOADD|nflag); + } + else + { + r = 1; + continue; + } + if(np) + { + if(is_abuiltin(np) || nv_isattr(np,NV_RDONLY)) + { + if(nv_isattr(np,NV_RDONLY)) + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + r = 1; + continue; + } + isfun = is_afunction(np); + if(troot==shp->var_tree) + { + Namarr_t *ap; +#if SHOPT_FIXEDARRAY + if((ap=nv_arrayptr(np)) && !ap->fixed && name[strlen(name)-1]==']' && !nv_getsub(np)) +#else + if(nv_isarray(np) && name[strlen(name)-1]==']' && !nv_getsub(np)) +#endif /* SHOPT_FIXEDARRAY */ + { + r=1; + continue; + } + + if(shp->subshell) + np=sh_assignok(np,0); + } + if(!nv_isnull(np)) + _nv_unset(np,0); + if(troot==shp->var_tree && shp->st.real_fun && (dp=shp->var_tree->walk) && dp==shp->st.real_fun->sdict) + nv_delete(np,dp,NV_NOFREE); + else if(isfun) + nv_delete(np,troot,NV_NOFREE); +#if 0 + /* causes unsetting local variable to expose global */ + else if(shp->var_tree==troot && shp->var_tree!=shp->var_base && nv_search((char*)np,shp->var_tree,HASH_BUCKET|HASH_NOSCOPE)) + nv_delete(np,shp->var_tree,0); +#endif + else + nv_close(np); + + } + else if(troot==shp->alias_tree) + r = 1; + } + sh_popcontext(shp,&buff); + return(r); +} + +/* + * print out the name and value of a name-value pair <np> + */ + +static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, struct tdata *tp) +{ + register char *cp; + int indent=tp->indent, outname=0, isfun; + sh_sigcheck(tp->sh); + if(flag) + flag = '\n'; + if(tp->noref && nv_isref(np)) + return(0); + if(nv_istable(np)) + { + print_value(file,np,tp); + return(0); + } + if(nv_isattr(np,NV_NOPRINT|NV_INTEGER)==NV_NOPRINT) + { + if(is_abuiltin(np)) + sfputr(file,nv_name(np),'\n'); + return(0); + } + isfun = is_afunction(np); + if(tp->prefix) + { + outname = (*tp->prefix=='t' && (!nv_isnull(np) || nv_isattr(np,NV_FLOAT|NV_RDONLY|NV_BINARY|NV_RJUST|NV_NOPRINT))); + if(indent && (isfun || outname || *tp->prefix!='t')) + { + sfnputc(file,'\t',indent); + indent = 0; + } + if(!isfun) + { + if(*tp->prefix=='t') + nv_attribute(np,tp->outfile,tp->prefix,tp->aflag); + else + sfputr(file,tp->prefix,' '); + } + } + if(isfun) + { + Sfio_t *iop=0; + char *fname=0; + if(nv_isattr(np,NV_NOFREE)) + return(0); + if(!flag && !np->nvalue.ip) + sfputr(file,"typeset -fu",' '); + else if(!flag && !nv_isattr(np,NV_FPOSIX)) + sfputr(file,"function",' '); + cp = nv_name(np); + if(tp->wctname) + cp += strlen(tp->wctname)+1; + sfputr(file,cp,-1); + if(nv_isattr(np,NV_FPOSIX)) + sfwrite(file,"()",2); + if(np->nvalue.ip && np->nvalue.rp->hoffset>=0) + fname = np->nvalue.rp->fname; + else + flag = '\n'; + if(flag) + { + if(tp->pflag && np->nvalue.ip && np->nvalue.rp->hoffset>=0) + sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):""); + else + sfputc(file, '\n'); + } + else + { + if(nv_isattr(np,NV_FTMP)) + { + fname = 0; + iop = tp->sh->heredocs; + } + else if(fname) + iop = sfopen(iop,fname,"r"); + else if(tp->sh->gd->hist_ptr) + iop = (tp->sh->gd->hist_ptr)->histfp; + if(iop && sfseek(iop,(Sfoff_t)np->nvalue.rp->hoffset,SEEK_SET)>=0) + sfmove(iop,file, nv_size(np), -1); + else + flag = '\n'; + if(fname) + sfclose(iop); + } + return(nv_size(np)+1); + } + if(nv_arrayptr(np)) + { + if(indent) + sfnputc(file,'\t',indent); + print_value(file,np,tp); + return(0); + } + if(nv_isvtree(np)) + nv_onattr(np,NV_EXPORT); + if(cp=nv_getval(np)) + { + if(indent) + sfnputc(file,'\t',indent); + sfputr(file,nv_name(np),-1); + if(!flag) + flag = '='; + sfputc(file,flag); + if(flag != '\n') + { + if(nv_isref(np) && nv_refsub(np)) + { + sfputr(file,sh_fmtq(cp),-1); + sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np))); + } + else +#if SHOPT_TYPEDEF + sfputr(file,nv_isvtree(np)?cp:sh_fmtq(cp),'\n'); +#else + sfputr(file,sh_fmtq(cp),'\n'); +#endif /* SHOPT_TYPEDEF */ + } + return(1); + } + else if(outname || (tp->scanmask && tp->scanroot==tp->sh->var_tree)) + sfputr(file,nv_name(np),'\n'); + return(0); +} + +/* + * print attributes at all nodes + */ +static void print_all(Sfio_t *file,Dt_t *root, struct tdata *tp) +{ + tp->outfile = file; + nv_scan(root, print_attribute, (void*)tp, 0, 0); +} + +/* + * print the attributes of name value pair give by <np> + */ +static void print_attribute(register Namval_t *np,void *data) +{ + register struct tdata *dp = (struct tdata*)data; + nv_attribute(np,dp->outfile,dp->prefix,dp->aflag); +} + +/* + * print the nodes in tree <root> which have attributes <flag> set + * of <option> is non-zero, no subscript or value is printed. + */ + +static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tdata *tp) +{ + register char **argv; + register Namval_t *np; + register int namec; + Namval_t *onp = 0; + char *name=0; + int len; + tp->sh->last_table=0; + flag &= ~NV_ASSIGN; + tp->scanmask = flag&~NV_NOSCOPE; + tp->scanroot = root; + tp->outfile = file; +#if SHOPT_TYPEDEF + if(!tp->prefix && tp->tp) + tp->prefix = nv_name(tp->tp); +#endif /* SHOPT_TYPEDEF */ + if(flag&NV_INTEGER) + tp->scanmask |= (NV_DOUBLE|NV_EXPNOTE); + if(flag==NV_LTOU || flag==NV_UTOL) + tp->scanmask |= NV_UTOL|NV_LTOU; + namec = nv_scan(root,nullscan,(void*)tp,tp->scanmask,flag); + argv = tp->argnam = (char**)stkalloc(tp->sh->stk,(namec+1)*sizeof(char*)); + namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag&~NV_IARRAY); + if(mbcoll()) + strsort(argv,namec,strcoll); + if(namec==0 && tp->sh->namespace && nv_dict(tp->sh->namespace)==root) + { + sfnputc(file,'\t',tp->indent); + sfwrite(file,":\n",2); + } + else while(namec--) + { + if((np=nv_search(*argv++,root,0)) && np!=onp && (!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))) + { + onp = np; + if(name) + { + char *newname = nv_name(np); + if(memcmp(name,newname,len)==0 && newname[len]== '.') + continue; + name = 0; + } + if(flag&NV_ARRAY) + { + if(nv_aindex(np)>=0) + { + if(!(flag&NV_IARRAY)) + continue; + } + else if((flag&NV_IARRAY)) + continue; + + } + tp->scanmask = flag&~NV_NOSCOPE; + tp->scanroot = root; + print_namval(file,np,option,tp); + if(nv_isvtree(np)) + { + name = nv_name(np); + len = strlen(name); + } + } + } +} + +/* + * add the name of the node to the argument list argnam + */ + +static void pushname(Namval_t *np,void *data) +{ + struct tdata *tp = (struct tdata*)data; + *tp->argnam++ = nv_name(np); +} + diff --git a/src/cmd/ksh93/bltins/ulimit.c b/src/cmd/ksh93/bltins/ulimit.c new file mode 100644 index 0000000..c537a81 --- /dev/null +++ b/src/cmd/ksh93/bltins/ulimit.c @@ -0,0 +1,216 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * ulimit [-HSacdfmnstuv] [limit] + * + * David Korn + * AT&T Labs + * + */ + +#include <ast.h> +#include <sfio.h> +#include <error.h> +#include "defs.h" +#include "builtins.h" +#include "name.h" +#include "ulimit.h" +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif + +#ifdef _no_ulimit + int b_ulimit(int argc,char *argv[],Shbltin_t *context) + { + NOT_USED(argc); + NOT_USED(argv); + NOT_USED(context); + errormsg(SH_DICT,ERROR_exit(2),e_nosupport); + return(0); + } +#else + +static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) +{ + register const Limit_t* tp; + + for (tp = shtab_limits; tp->option; tp++) + { + sfprintf(sp, "[%c=%d:%s?The %s", tp->option, tp - shtab_limits + 1, tp->name, tp->description); + if(tp->type != LIM_COUNT) + sfprintf(sp, " in %ss", e_units[tp->type]); + sfprintf(sp, ".]"); + } + return(1); +} + +#define HARD 2 +#define SOFT 4 + +int b_ulimit(int argc,char *argv[],Shbltin_t *context) +{ + register char *limit; + register int mode=0, n; + register unsigned long hit = 0; + Shell_t *shp = context->shp; +#ifdef _lib_getrlimit + struct rlimit rlp; +#endif /* _lib_getrlimit */ + const Limit_t* tp; + char* conf; + int label, unit, nosupport; + rlim_t i; + char tmp[32]; + Optdisc_t disc; + memset(&disc, 0, sizeof(disc)); + disc.version = OPT_VERSION; + disc.infof = infof; + opt_info.disc = &disc; + while((n = optget(argv,sh_optulimit))) switch(n) + { + case 'H': + mode |= HARD; + continue; + case 'S': + mode |= SOFT; + continue; + case 'a': + hit = ~0; + break; + default: + if(n < 0) + hit |= (1L<<(-(n+1))); + else + errormsg(SH_DICT,2, e_notimp, opt_info.name); + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + opt_info.disc = 0; + /* default to -f */ + limit = argv[opt_info.index]; + if(hit==0) + for(n=0; shtab_limits[n].option; n++) + if(shtab_limits[n].index == RLIMIT_FSIZE) + { + hit |= (1L<<n); + break; + } + /* only one option at a time for setting */ + label = (hit&(hit-1)); + if(error_info.errors || (limit && label) || argc>opt_info.index+1) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + if(mode==0) + mode = (HARD|SOFT); + for(tp = shtab_limits; tp->option && hit; tp++,hit>>=1) + { + if(!(hit&1)) + continue; + nosupport = (n = tp->index) == RLIMIT_UNKNOWN; + unit = shtab_units[tp->type]; + if(limit) + { + if(shp->subshell && !shp->subshare) + sh_subfork(); + if(strcmp(limit,e_unlimited)==0) + i = INFINITY; + else + { + char *last; + /* an explicit suffix unit overrides the default */ + if((i=strtol(limit,&last,0))!=INFINITY && !*last) + i *= unit; + else if((i=strton(limit,&last,NiL,0))==INFINITY || *last) + { + if((i=sh_strnum(limit,&last,2))==INFINITY || *last) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); + i *= unit; + } + } + if(nosupport) + errormsg(SH_DICT,ERROR_system(1),e_readonly,tp->name); + else + { +#ifdef _lib_getrlimit + if(getrlimit(n,&rlp) <0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); + if(mode&HARD) + rlp.rlim_max = i; + if(mode&SOFT) + rlp.rlim_cur = i; + if(setrlimit(n,&rlp) <0) + errormsg(SH_DICT,ERROR_system(1),e_overlimit,limit); +#else + if((i=vlimit(n,i)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); +#endif /* _lib_getrlimit */ + } + } + else + { + if(!nosupport) + { +#ifdef _lib_getrlimit + if(getrlimit(n,&rlp) <0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); + if(mode&HARD) + i = rlp.rlim_max; + if(mode&SOFT) + i = rlp.rlim_cur; +#else +# ifdef _lib_ulimit + n--; +# endif /* _lib_ulimit */ + i = -1; + if((i=vlimit(n,i)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); +#endif /* _lib_getrlimit */ + } + if(label) + { + if(tp->type != LIM_COUNT) + sfsprintf(tmp,sizeof(tmp),"%s (%ss)", tp->description, e_units[tp->type]); + else + sfsprintf(tmp,sizeof(tmp),"%s", tp->name); + sfprintf(sfstdout,"%-30s (-%c) ",tmp,tp->option); + } + if(nosupport) + { + if(!tp->conf || !*(conf = astconf(tp->conf, NiL, NiL))) + conf = (char*)e_nosupport; + sfputr(sfstdout,conf,'\n'); + } + else if(i!=INFINITY) + { + i += (unit-1); + sfprintf(sfstdout,"%I*d\n",sizeof(i),i/unit); + } + else + sfputr(sfstdout,e_unlimited,'\n'); + } + } + return(0); +} +#endif /* _no_ulimit */ diff --git a/src/cmd/ksh93/bltins/umask.c b/src/cmd/ksh93/bltins/umask.c new file mode 100644 index 0000000..19978ac --- /dev/null +++ b/src/cmd/ksh93/bltins/umask.c @@ -0,0 +1,98 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * umask [-S] [mask] + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include <ast.h> +#include <sfio.h> +#include <error.h> +#include <ctype.h> +#include <ls.h> +#include <shell.h> +#include "builtins.h" +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif + +int b_umask(int argc,char *argv[],Shbltin_t *context) +{ + register char *mask; + register int flag = 0, sflag = 0; + NOT_USED(context); + while((argc = optget(argv,sh_optumask))) switch(argc) + { + case 'S': + sflag++; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + if(mask = *argv) + { + register int c; + if(isdigit(*mask)) + { + while(c = *mask++) + { + if (c>='0' && c<='7') + flag = (flag<<3) + (c-'0'); + else + errormsg(SH_DICT,ERROR_exit(1),e_number,*argv); + } + } + else + { + char *cp = mask; + flag = umask(0); + c = strperm(cp,&cp,~flag&0777); + if(*cp) + { + umask(flag); + errormsg(SH_DICT,ERROR_exit(1),e_format,mask); + } + flag = (~c&0777); + } + umask(flag); + } + else + { + umask(flag=umask(0)); + if(sflag) + sfprintf(sfstdout,"%s\n",fmtperm(~flag&0777)); + else + sfprintf(sfstdout,"%0#4o\n",flag); + } + return(0); +} + diff --git a/src/cmd/ksh93/bltins/whence.c b/src/cmd/ksh93/bltins/whence.c new file mode 100644 index 0000000..7843ca7 --- /dev/null +++ b/src/cmd/ksh93/bltins/whence.c @@ -0,0 +1,294 @@ +/*********************************************************************** +* * +* 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 <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * command [-pvVx] name [arg...] + * whence [-afvp] name... + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include <error.h> +#include "shtable.h" +#include "name.h" +#include "path.h" +#include "shlex.h" +#include "builtins.h" + +#define P_FLAG 1 +#define V_FLAG 2 +#define A_FLAG 4 +#define F_FLAG 010 +#define X_FLAG 020 +#define Q_FLAG 040 + +static int whence(Shell_t *,char**, int); + +/* + * command is called with argc==0 when checking for -V or -v option + * In this case return 0 when -v or -V or unknown option, otherwise + * the shift count to the command is returned + */ +int b_command(register int argc,char *argv[],Shbltin_t *context) +{ + register int n, flags=0; + register Shell_t *shp = context->shp; + opt_info.index = opt_info.offset = 0; + while((n = optget(argv,sh_optcommand))) switch(n) + { + case 'p': + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,"-p"); + sh_onstate(SH_DEFPATH); + break; + case 'v': + flags |= X_FLAG; + break; + case 'V': + flags |= V_FLAG; + break; + case 'x': + shp->xargexit = 1; + break; + case ':': + if(argc==0) + return(0); + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + if(argc==0) + return(0); + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + if(argc==0) + return(flags?0:opt_info.index); + argv += opt_info.index; + if(error_info.errors || !*argv) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); + return(whence(shp,argv, flags)); +} + +/* + * for the whence command + */ +int b_whence(int argc,char *argv[],Shbltin_t *context) +{ + register int flags=0, n; + register Shell_t *shp = context->shp; + NOT_USED(argc); + if(*argv[0]=='t') + flags = V_FLAG; + while((n = optget(argv,sh_optwhence))) switch(n) + { + case 'a': + flags |= A_FLAG; + /* FALL THRU */ + case 'v': + flags |= V_FLAG; + break; + case 'f': + flags |= F_FLAG; + break; + case 'p': + flags |= P_FLAG; + flags &= ~V_FLAG; + break; + case 'q': + flags |= Q_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || !*argv) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + return(whence(shp, argv, flags)); +} + +static int whence(Shell_t *shp,char **argv, register int flags) +{ + register const char *name; + register Namval_t *np; + register const char *cp; + register int aflag,r=0; + register const char *msg; + int tofree; + Dt_t *root; + Namval_t *nq; + char *notused; + Pathcomp_t *pp=0; + int notrack = 1; + if(flags&Q_FLAG) + flags &= ~A_FLAG; + while(name= *argv++) + { + tofree=0; + aflag = ((flags&A_FLAG)!=0); + cp = 0; + np = 0; + if(flags&P_FLAG) + goto search; + if(flags&Q_FLAG) + goto bltins; + /* reserved words first */ + if(sh_lookup(name,shtab_reserved)) + { + sfprintf(sfstdout,"%s%s\n",name,(flags&V_FLAG)?sh_translate(is_reserved):""); + if(!aflag) + continue; + aflag++; + } + /* non-tracked aliases */ + if((np=nv_search(name,shp->alias_tree,0)) + && !nv_isnull(np) && !(notrack=nv_isattr(np,NV_TAGGED)) + && (cp=nv_getval(np))) + { + if(flags&V_FLAG) + { + if(nv_isattr(np,NV_EXPORT)) + msg = sh_translate(is_xalias); + else + msg = sh_translate(is_alias); + sfprintf(sfstdout,msg,name); + } + sfputr(sfstdout,sh_fmtq(cp),'\n'); + if(!aflag) + continue; + cp = 0; + aflag++; + } + /* built-ins and functions next */ + bltins: + root = (flags&F_FLAG)?shp->bltin_tree:shp->fun_tree; + if(np= nv_bfsearch(name, root, &nq, ¬used)) + { + if(is_abuiltin(np) && nv_isnull(np)) + goto search; + cp = ""; + if(flags&V_FLAG) + { + if(nv_isnull(np)) + cp = sh_translate(is_ufunction); + else if(is_abuiltin(np)) + { + if(nv_isattr(np,BLT_SPC)) + cp = sh_translate(is_spcbuiltin); + else + cp = sh_translate(is_builtin); + } + else + cp = sh_translate(is_function); + } + if(flags&Q_FLAG) + continue; + sfprintf(sfstdout,"%s%s\n",name,cp); + if(!aflag) + continue; + cp = 0; + aflag++; + } + search: + if(sh_isstate(SH_DEFPATH)) + { + cp=0; + notrack=1; + } + do + { + if(path_search(shp,name,&pp,2+(aflag>1))) + { + cp = name; + if((flags&P_FLAG) && *cp!='/') + cp = 0; + } + else + { + cp = stakptr(PATH_OFFSET); + if(*cp==0) + cp = 0; + else if(*cp!='/') + { + cp = path_fullname(shp,cp); + tofree=1; + } + } + if(flags&Q_FLAG) + { + pp = 0; + r |= !cp; + } + else if(cp) + { + if(flags&V_FLAG) + { + if(*cp!= '/') + { + if(!np && (np=nv_search(name,shp->track_tree,0))) + sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),path_pwd(shp,0),cp); + else if(!np || nv_isnull(np)) + sfprintf(sfstdout,"%s%s\n",name,sh_translate(is_ufunction)); + continue; + } + sfputr(sfstdout,sh_fmtq(name),' '); + /* built-in version of program */ + if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0))) + msg = sh_translate(is_builtver); + /* tracked aliases next */ + else if(aflag>1 || !notrack || strchr(name,'/')) + msg = sh_translate("is"); + else + msg = sh_translate(is_talias); + sfputr(sfstdout,msg,' '); + } + sfputr(sfstdout,sh_fmtq(cp),'\n'); + if(aflag) + { + if(aflag<=1) + aflag++; + if (pp) + pp = pp->next; + } + else + pp = 0; + if(tofree) + { + free((char*)cp); + tofree = 0; + } + } + else if(aflag<=1) + { + r |= 1; + if(flags&V_FLAG) + errormsg(SH_DICT,ERROR_exit(0),e_found,sh_fmtq(name)); + } + } while(pp); + } + return(r); +} + |