summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/bltins
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/bltins
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/bltins')
-rw-r--r--src/cmd/ksh93/bltins/alarm.c276
-rw-r--r--src/cmd/ksh93/bltins/cd_pwd.c250
-rw-r--r--src/cmd/ksh93/bltins/cflow.c117
-rw-r--r--src/cmd/ksh93/bltins/enum.c284
-rw-r--r--src/cmd/ksh93/bltins/getopts.c199
-rw-r--r--src/cmd/ksh93/bltins/hist.c312
-rw-r--r--src/cmd/ksh93/bltins/misc.c590
-rw-r--r--src/cmd/ksh93/bltins/mkservice.c494
-rw-r--r--src/cmd/ksh93/bltins/print.c1058
-rw-r--r--src/cmd/ksh93/bltins/read.c802
-rw-r--r--src/cmd/ksh93/bltins/regress.c343
-rw-r--r--src/cmd/ksh93/bltins/sleep.c235
-rw-r--r--src/cmd/ksh93/bltins/test.c667
-rw-r--r--src/cmd/ksh93/bltins/trap.c420
-rw-r--r--src/cmd/ksh93/bltins/typeset.c1471
-rw-r--r--src/cmd/ksh93/bltins/ulimit.c216
-rw-r--r--src/cmd/ksh93/bltins/umask.c98
-rw-r--r--src/cmd/ksh93/bltins/whence.c294
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("&lt;");
+ else if(c=='>')
+ stakputs("&gt;");
+ else if(c=='&')
+ stakputs("&amp;");
+ else if(c=='"')
+ stakputs("&quot;");
+ else if(c=='\'')
+ stakputs("&apos;");
+ else if(c==' ')
+ stakputs("&nbsp;");
+ 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, &notused))
+ {
+ 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);
+}
+