1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * exec [arg...]
23 * eval [arg...]
24 * jobs [-lnp] [job...]
25 * login [arg...]
26 * let expr...
27 * . file [arg...]
28 * :, true, false
29 * vpath [top] [base]
30 * vmap [top] [base]
31 * wait [job...]
32 * shift [n]
33 *
34 *   David Korn
35 *   AT&T Labs
36 *
37 */
38
39#include	"defs.h"
40#include	"variables.h"
41#include	"shnodes.h"
42#include	"path.h"
43#include	"io.h"
44#include	"name.h"
45#include	"history.h"
46#include	"builtins.h"
47#include	"jobs.h"
48
49#define DOTMAX	MAXDEPTH	/* maximum level of . nesting */
50
51static void     noexport(Namval_t*,void*);
52
53struct login
54{
55	Shell_t *sh;
56	int     clear;
57	char    *arg0;
58};
59
60int    b_exec(int argc,char *argv[], void *extra)
61{
62	struct login logdata;
63	register int n;
64	logdata.clear = 0;
65	logdata.arg0 = 0;
66	logdata.sh = ((Shbltin_t*)extra)->shp;
67        logdata.sh->st.ioset = 0;
68	while (n = optget(argv, sh_optexec)) switch (n)
69	{
70	    case 'a':
71		logdata.arg0 = opt_info.arg;
72		argc = 0;
73		break;
74	    case 'c':
75		logdata.clear=1;
76		break;
77	    case ':':
78		errormsg(SH_DICT,2, "%s", opt_info.arg);
79		break;
80	    case '?':
81		errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
82		return(2);
83	}
84	argv += opt_info.index;
85	if(error_info.errors)
86		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
87	if(*argv)
88                B_login(0,argv,(void*)&logdata);
89	return(0);
90}
91
92static void     noexport(register Namval_t* np, void *data)
93{
94	NOT_USED(data);
95	nv_offattr(np,NV_EXPORT);
96}
97
98int    B_login(int argc,char *argv[],void *extra)
99{
100	struct checkpt *pp;
101	register struct login *logp=0;
102	register Shell_t *shp;
103	const char *pname;
104	if(argc)
105		shp = ((Shbltin_t*)extra)->shp;
106	else
107	{
108		logp = (struct login*)extra;
109		shp = logp->sh;
110	}
111	pp = (struct checkpt*)shp->jmplist;
112	if(sh_isoption(SH_RESTRICTED))
113		errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[0]);
114	else
115        {
116		register struct argnod *arg=shp->envlist;
117		register Namval_t* np;
118		register char *cp;
119		if(shp->subshell && !shp->subshare)
120			sh_subfork();
121		if(logp && logp->clear)
122		{
123#ifdef _ENV_H
124			env_close(shp->env);
125			shp->env = env_open((char**)0,3);
126#else
127			nv_scan(shp->var_tree,noexport,0,NV_EXPORT,NV_EXPORT);
128#endif
129		}
130		while(arg)
131		{
132			if((cp=strchr(arg->argval,'=')) &&
133				(*cp=0,np=nv_search(arg->argval,shp->var_tree,0)))
134			{
135				nv_onattr(np,NV_EXPORT);
136				sh_envput(shp->env,np);
137			}
138			if(cp)
139				*cp = '=';
140			arg=arg->argnxt.ap;
141		}
142		pname = argv[0];
143		if(logp && logp->arg0)
144			argv[0] = logp->arg0;
145#ifdef JOBS
146		if(job_close(shp) < 0)
147			return(1);
148#endif /* JOBS */
149		/* force bad exec to terminate shell */
150		pp->mode = SH_JMPEXIT;
151		sh_sigreset(2);
152		sh_freeup(shp);
153		path_exec(shp,pname,argv,NIL(struct argnod*));
154		sh_done(shp,0);
155        }
156	return(1);
157}
158
159int    b_let(int argc,char *argv[],void *extra)
160{
161	register int r;
162	register char *arg;
163	Shell_t *shp = ((Shbltin_t*)extra)->shp;
164	NOT_USED(argc);
165	while (r = optget(argv,sh_optlet)) switch (r)
166	{
167	    case ':':
168		errormsg(SH_DICT,2, "%s", opt_info.arg);
169		break;
170	    case '?':
171		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
172		break;
173	}
174	argv += opt_info.index;
175	if(error_info.errors || !*argv)
176		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
177	while(arg= *argv++)
178		r = !sh_arith(shp,arg);
179	return(r);
180}
181
182int    b_eval(int argc,char *argv[], void *extra)
183{
184	register int r;
185	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
186	NOT_USED(argc);
187	while (r = optget(argv,sh_opteval)) switch (r)
188	{
189	    case ':':
190		errormsg(SH_DICT,2, "%s", opt_info.arg);
191		break;
192	    case '?':
193		errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg);
194		return(2);
195	}
196	if(error_info.errors)
197		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
198	argv += opt_info.index;
199	if(*argv && **argv)
200	{
201		sh_offstate(SH_MONITOR);
202		sh_eval(sh_sfeval(argv),0);
203	}
204	return(shp->exitval);
205}
206
207int    b_dot_cmd(register int n,char *argv[],void* extra)
208{
209	register char *script;
210	register Namval_t *np;
211	register int jmpval;
212	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
213	struct sh_scoped savst, *prevscope = shp->st.self;
214	char *filename=0;
215	int	fd;
216	struct dolnod   *argsave=0, *saveargfor;
217	struct checkpt buff;
218	Sfio_t *iop=0;
219	short level;
220	while (n = optget(argv,sh_optdot)) switch (n)
221	{
222	    case ':':
223		errormsg(SH_DICT,2, "%s", opt_info.arg);
224		break;
225	    case '?':
226		errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg);
227		return(2);
228	}
229	argv += opt_info.index;
230	script = *argv;
231	if(error_info.errors || !script)
232		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
233	if(shp->dot_depth+1 > DOTMAX)
234		errormsg(SH_DICT,ERROR_exit(1),e_toodeep,script);
235	if(!(np=shp->posix_fun))
236	{
237		/* check for KornShell style function first */
238		np = nv_search(script,shp->fun_tree,0);
239		if(np && is_afunction(np) && !nv_isattr(np,NV_FPOSIX))
240		{
241			if(!np->nvalue.ip)
242			{
243				path_search(shp,script,NIL(Pathcomp_t**),0);
244				if(np->nvalue.ip)
245				{
246					if(nv_isattr(np,NV_FPOSIX))
247						np = 0;
248				}
249				else
250					errormsg(SH_DICT,ERROR_exit(1),e_found,script);
251			}
252		}
253		else
254			np = 0;
255		if(!np)
256		{
257			if((fd=path_open(shp,script,path_get(shp,script))) < 0)
258				errormsg(SH_DICT,ERROR_system(1),e_open,script);
259			filename = path_fullname(shp,stkptr(shp->stk,PATH_OFFSET));
260		}
261	}
262	*prevscope = shp->st;
263	shp->st.lineno = np?((struct functnod*)nv_funtree(np))->functline:1;
264	shp->st.var_local = shp->st.save_tree = shp->var_tree;
265	if(filename)
266	{
267		shp->st.filename = filename;
268		shp->st.lineno = 1;
269	}
270	level  = shp->fn_depth+shp->dot_depth+1;
271	nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16);
272	shp->st.prevst = prevscope;
273	shp->st.self = &savst;
274	shp->topscope = (Shscope_t*)shp->st.self;
275	prevscope->save_tree = shp->var_tree;
276	shp->st.cmdname = argv[0];
277	if(np)
278		shp->st.filename = np->nvalue.rp->fname;
279	nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE);
280	shp->posix_fun = 0;
281	if(np || argv[1])
282		argsave = sh_argnew(shp,argv,&saveargfor);
283	sh_pushcontext(shp,&buff,SH_JMPDOT);
284	jmpval = sigsetjmp(buff.buff,0);
285	if(jmpval == 0)
286	{
287		shp->dot_depth++;
288		if(np)
289			sh_exec((Shnode_t*)(nv_funtree(np)),sh_isstate(SH_ERREXIT));
290		else
291		{
292			char buff[IOBSIZE+1];
293			iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fd,SF_READ);
294			sh_offstate(SH_NOFORK);
295			sh_eval(iop,0);
296		}
297	}
298	sh_popcontext(shp,&buff);
299	if(!np)
300		free((void*)shp->st.filename);
301	shp->dot_depth--;
302	if((np || argv[1]) && jmpval!=SH_JMPSCRIPT)
303		sh_argreset(shp,argsave,saveargfor);
304	else
305	{
306		prevscope->dolc = shp->st.dolc;
307		prevscope->dolv = shp->st.dolv;
308	}
309	if (shp->st.self != &savst)
310		*shp->st.self = shp->st;
311	/* only restore the top Shscope_t portion for posix functions */
312	memcpy((void*)&shp->st, (void*)prevscope, sizeof(Shscope_t));
313	shp->topscope = (Shscope_t*)prevscope;
314	nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE);
315	if(jmpval && jmpval!=SH_JMPFUN)
316		siglongjmp(*shp->jmplist,jmpval);
317	return(shp->exitval);
318}
319
320/*
321 * null, true  command
322 */
323int    b_true(int argc,register char *argv[],void *extra)
324{
325	NOT_USED(argc);
326	NOT_USED(argv[0]);
327	NOT_USED(extra);
328	return(0);
329}
330
331/*
332 * false  command
333 */
334int    b_false(int argc,register char *argv[], void *extra)
335{
336	NOT_USED(argc);
337	NOT_USED(argv[0]);
338	NOT_USED(extra);
339	return(1);
340}
341
342int    b_shift(register int n, register char *argv[], void *extra)
343{
344	register char *arg;
345	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
346	while((n = optget(argv,sh_optshift))) switch(n)
347	{
348		case ':':
349			errormsg(SH_DICT,2, "%s", opt_info.arg);
350			break;
351		case '?':
352			errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg);
353			return(2);
354	}
355	if(error_info.errors)
356		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
357	argv += opt_info.index;
358	n = ((arg= *argv)?(int)sh_arith(shp,arg):1);
359	if(n<0 || shp->st.dolc<n)
360		errormsg(SH_DICT,ERROR_exit(1),e_number,arg);
361	else
362	{
363		shp->st.dolv += n;
364		shp->st.dolc -= n;
365	}
366	return(0);
367}
368
369int    b_wait(int n,register char *argv[],void *extra)
370{
371	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
372	while((n = optget(argv,sh_optwait))) switch(n)
373	{
374		case ':':
375			errormsg(SH_DICT,2, "%s", opt_info.arg);
376			break;
377		case '?':
378			errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
379			break;
380	}
381	if(error_info.errors)
382		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
383	argv += opt_info.index;
384	job_bwait(argv);
385	return(shp->exitval);
386}
387
388#ifdef JOBS
389#   if 0
390    /* for the dictionary generator */
391	int    b_fg(int n,char *argv[],void *extra){}
392	int    b_disown(int n,char *argv[],void *extra){}
393#   endif
394int    b_bg(register int n,register char *argv[],void *extra)
395{
396	register int flag = **argv;
397	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
398	register const char *optstr = sh_optbg;
399	if(*argv[0]=='f')
400		optstr = sh_optfg;
401	else if(*argv[0]=='d')
402		optstr = sh_optdisown;
403	while((n = optget(argv,optstr))) switch(n)
404	{
405	    case ':':
406		errormsg(SH_DICT,2, "%s", opt_info.arg);
407		break;
408	    case '?':
409		errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
410		break;
411	}
412	if(error_info.errors)
413		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
414	argv += opt_info.index;
415	if(!sh_isoption(SH_MONITOR) || !job.jobcontrol)
416	{
417		if(sh_isstate(SH_INTERACTIVE))
418			errormsg(SH_DICT,ERROR_exit(1),e_no_jctl);
419		return(1);
420	}
421	if(flag=='d' && *argv==0)
422		argv = (char**)0;
423	if(job_walk(sfstdout,job_switch,flag,argv))
424		errormsg(SH_DICT,ERROR_exit(1),e_no_job);
425	return(shp->exitval);
426}
427
428int    b_jobs(register int n,char *argv[],void *extra)
429{
430	register int flag = 0;
431	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
432	while((n = optget(argv,sh_optjobs))) switch(n)
433	{
434	    case 'l':
435		flag = JOB_LFLAG;
436		break;
437	    case 'n':
438		flag = JOB_NFLAG;
439		break;
440	    case 'p':
441		flag = JOB_PFLAG;
442		break;
443	    case ':':
444		errormsg(SH_DICT,2, "%s", opt_info.arg);
445		break;
446	    case '?':
447		errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
448		break;
449	}
450	argv += opt_info.index;
451	if(error_info.errors)
452		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
453	if(*argv==0)
454		argv = (char**)0;
455	if(job_walk(sfstdout,job_list,flag,argv))
456		errormsg(SH_DICT,ERROR_exit(1),e_no_job);
457	job_wait((pid_t)0);
458	return(shp->exitval);
459}
460#endif
461
462#ifdef _cmd_universe
463/*
464 * There are several universe styles that are masked by the getuniv(),
465 * setuniv() calls.
466 */
467int	b_universe(int argc, char *argv[],void *extra)
468{
469	register char *arg;
470	register int n;
471	NOT_USED(extra);
472	while((n = optget(argv,sh_optuniverse))) switch(n)
473	{
474	    case ':':
475		errormsg(SH_DICT,2, "%s", opt_info.arg);
476		break;
477	    case '?':
478		errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
479		break;
480	}
481	argv += opt_info.index;
482	argc -= opt_info.index;
483	if(error_info.errors || argc>1)
484		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
485	if(arg = argv[0])
486	{
487		if(!astconf("UNIVERSE",0,arg))
488			errormsg(SH_DICT,ERROR_exit(1), e_badname,arg);
489	}
490	else
491	{
492		if(!(arg=astconf("UNIVERSE",0,0)))
493			errormsg(SH_DICT,ERROR_exit(1),e_nouniverse);
494		else
495			sfputr(sfstdout,arg,'\n');
496	}
497	return(0);
498}
499#endif /* cmd_universe */
500
501#if SHOPT_FS_3D
502#if _UWIN
503#include <sys/mount.h>
504#endif
505#   if 0
506    /* for the dictionary generator */
507    int	b_vmap(int argc,char *argv[], void *extra){}
508#   endif
509    int	b_vpath(register int argc,char *argv[], void *extra)
510    {
511	register int flag, n;
512	register const char *optstr;
513	register char *vend;
514	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
515	if(argv[0][1]=='p')
516	{
517		optstr = sh_optvpath;
518		flag = FS3D_VIEW;
519	}
520	else
521	{
522		optstr = sh_optvmap;
523		flag = FS3D_VERSION;
524	}
525	while(n = optget(argv, optstr)) switch(n)
526	{
527	    case ':':
528		errormsg(SH_DICT,2, "%s", opt_info.arg);
529		break;
530	    case '?':
531		errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
532		break;
533	}
534	if(error_info.errors)
535		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
536#ifdef MS_3D
537	flag |= MS_3D;
538#else
539	if(!shp->gd->lim.fs3d)
540		goto failed;
541#endif
542	argv += opt_info.index;
543	argc -= opt_info.index;
544	switch(argc)
545	{
546	    case 0:
547	    case 1:
548		flag |= FS3D_GET;
549		if((n = mount(*argv,(char*)0,flag,0)) >= 0)
550		{
551			vend = stkalloc(shp->stk,++n);
552			n = mount(*argv,vend,flag|FS3D_SIZE(n),0);
553		}
554		if(n < 0)
555			goto failed;
556		if(argc==1)
557		{
558			sfprintf(sfstdout,"%s\n",vend);
559			break;
560		}
561		n = 0;
562		while(flag = *vend++)
563		{
564			if(flag==' ')
565			{
566				flag  = e_sptbnl[n+1];
567				n = !n;
568			}
569			sfputc(sfstdout,flag);
570		}
571		if(n)
572			sfputc(sfstdout,'\n');
573		break;
574	     default:
575		if((argc&1))
576			errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
577		/*FALLTHROUGH*/
578	     case 2:
579		if(shp->subshell && !shp->subshare)
580			sh_subfork();
581 		for(n=0;n<argc;n+=2)
582			if(mount(argv[n+1],argv[n],flag,0)<0)
583				goto failed;
584	}
585	return(0);
586failed:
587	errormsg(SH_DICT,ERROR_exit(1),(argc>1)?e_cantset:e_cantget,(flag&FS3D_VIEW)?e_mapping:e_versions);
588	return(1);
589    }
590#endif /* SHOPT_FS_3D */
591
592