/*******************************************************************
*                                                                  *
*             This software is part of the ast package             *
*                Copyright (c) 1982-2003 AT&T Corp.                *
*        and it may only be used by you under license from         *
*                       AT&T Corp. ("AT&T")                        *
*         A copy of the Source Code Agreement is available         *
*                at the AT&T Internet web site URL                 *
*                                                                  *
*       http://www.research.att.com/sw/license/ast-open.html       *
*                                                                  *
*    If you have copied or used this software without agreeing     *
*        to the terms of the license you are infringing on         *
*           the license and copyright and are violating            *
*               AT&T's intellectual property rights.               *
*                                                                  *
*            Information and Software Systems Research             *
*                        AT&T Labs 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	<ctype.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(const char*);
static void	sig_list(Shell_t*,int);

int	b_trap(int argc,char *argv[],void *extra)
{
	register char *arg = argv[1];
	register int sig, pflag = 0;
	register Shell_t *shp = (Shell_t*)extra;
	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)
	{
		register int	clear;
		char *action = arg;
		if(!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++;
			}
			while(!argv[0])
				errormsg(SH_DICT,ERROR_exit(1),e_condition);
		}
		while(arg = *argv++)
		{
			sig = sig_number(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->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);
			else
			{
				if(sig >= shp->st.trapmax)
					shp->st.trapmax = sig+1;
				if(arg=shp->st.trapcom[sig])
					free(arg);
				shp->st.trapcom[sig] = strdup(action);
				sh_sigtrap(sig);
			}
		}
	}
	else /* print out current traps */
		sig_list(shp,-1);
	return(0);
}

int	b_kill(int argc,char *argv[],void *extra)
{
	register char *signame;
	register int sig=SIGTERM, flag=0, n;
	register Shell_t *shp = (Shell_t*)extra;
	NOT_USED(argc);
	while((n = optget(argv,sh_optkill))) switch(n)
	{
		case ':':
			if((signame=argv[opt_info.index++]) && (sig=sig_number(signame+1))>=0)
			{
				if(argv[opt_info.index] && strcmp(argv[opt_info.index],"--")==0)
					opt_info.index++;
				goto endopts;
			}
			opt_info.index--;
			errormsg(SH_DICT,2, "%s", opt_info.arg);
			break;
		case 'n':
			sig = (int)opt_info.num;
			break;
		case 's':
			flag |= S_FLAG;
			signame = opt_info.arg;
			break;
		case 'l':
			flag |= L_FLAG;
			break;
		case '?':
			errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
			break;
	}
endopts:
	argv += opt_info.index;
	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(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(signame)) < 0 || sig > shp->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(const char *string)
{
	register int n;
	char *last;
	if(isdigit(*string))
	{
		n = strtol(string,&last,10);
		if(*last)
			n = -1;
	}
	else
	{
		register int c;
		n = staktell();
		do
		{
			c = *string++;
			if(islower(c))
				c = toupper(c);
			stakputc(c);
		}
		while(c);
		stakseek(n);
		if(memcmp(stakptr(n),"SIG",3)==0)
			n += 3;
		n = sh_lookup(stakptr(n),shtab_signals);
		n &= (1<<SH_SIGBITS)-1;
		if(n < SH_TRAP)
			n--;
	}
	return(n);
}

/*
 * 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 = shp->sigmax+1;
	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 */
		while(--sig >= 0)
			names[sig] = 0;
		for(sig=SH_DEBUGTRAP; sig>=0; sig--)
			traps[sig] = 0;
	}
	while(*tp->sh_name)
	{
		sig = tp->sh_number;
		sig &= ((1<<SH_SIGBITS)-1);
		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 < sizeof(names)/sizeof(char*))
			names[sig] = (char*)tp->sh_name;
		tp++;
	}
	if(flag > 0)
		sfprintf(sfstdout,"%d\n",flag-1);
	else if(flag<0)
	{
		/* print the traps */
		register char *trap,*sname,**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(!(sname=(char*)names[sig+1]))
			{
				sname[0] = 'S';
				sname[1] = 'I';
				sname[2] = 'G';
				sname[3] = (sig/10)+'0';
				sname[4] = (sig%10)+'0';
			}
			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=2; sig <= shp->sigmax; sig++)
		{
			if(names[sig])
				sfputr(sfstdout,names[sig],'\n');
			else
				sfprintf(sfstdout,"SIG%d\n",sig-1);
		}
	}
}

