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