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 * alarm [-r] [varname [+]when]
23 *
24 *   David Korn
25 *   AT&T Labs
26 *
27 */
28
29#include	"defs.h"
30#include	<error.h>
31#include	<stak.h>
32#include	"builtins.h"
33#include	"FEATURE/time"
34
35#define R_FLAG	1
36#define L_FLAG	2
37
38struct	tevent
39{
40	Namfun_t	fun;
41	Namval_t	*node;
42	Namval_t	*action;
43	struct tevent	*next;
44	long		milli;
45	int		flags;
46	void            *timeout;
47	Shell_t		*sh;
48};
49
50static const char ALARM[] = "alarm";
51
52static void	trap_timeout(void*);
53
54/*
55 * insert timeout item on current given list in sorted order
56 */
57static void *time_add(struct tevent *item, void *list)
58{
59	register struct tevent *tp = (struct tevent*)list;
60	if(!tp || item->milli < tp->milli)
61	{
62		item->next = tp;
63		list = (void*)item;
64	}
65	else
66	{
67		while(tp->next && item->milli > tp->next->milli)
68			tp = tp->next;
69		item->next = tp->next;
70		tp->next = item;
71	}
72	tp = item;
73	tp->timeout = (void*)sh_timeradd(tp->milli,tp->flags&R_FLAG,trap_timeout,(void*)tp);
74	return(list);
75}
76
77/*
78 * delete timeout item from current given list, delete timer
79 */
80static 	void *time_delete(register struct tevent *item, void *list)
81{
82	register struct tevent *tp = (struct tevent*)list;
83	if(item==tp)
84		list = (void*)tp->next;
85	else
86	{
87		while(tp && tp->next != item)
88			tp = tp->next;
89		if(tp)
90			tp->next = item->next;
91	}
92	if(item->timeout)
93		timerdel((void*)item->timeout);
94	return(list);
95}
96
97static void	print_alarms(void *list)
98{
99	register struct tevent *tp = (struct tevent*)list;
100	while(tp)
101	{
102		if(tp->timeout)
103		{
104			register char *name = nv_name(tp->node);
105			if(tp->flags&R_FLAG)
106			{
107				double d = tp->milli;
108				sfprintf(sfstdout,e_alrm1,name,d/1000.);
109			}
110			else
111				sfprintf(sfstdout,e_alrm2,name,nv_getnum(tp->node));
112		}
113		tp = tp->next;
114	}
115}
116
117static void	trap_timeout(void* handle)
118{
119	register struct tevent *tp = (struct tevent*)handle;
120	tp->sh->trapnote |= SH_SIGALRM;
121	if(!(tp->flags&R_FLAG))
122		tp->timeout = 0;
123	tp->flags |= L_FLAG;
124	tp->sh->sigflag[SIGALRM] |= SH_SIGALRM;
125	if(sh_isstate(SH_TTYWAIT))
126		sh_timetraps(tp->sh);
127}
128
129void	sh_timetraps(Shell_t *shp)
130{
131	register struct tevent *tp, *tpnext;
132	register struct tevent *tptop;
133	while(1)
134	{
135		shp->sigflag[SIGALRM] &= ~SH_SIGALRM;
136		tptop= (struct tevent*)shp->st.timetrap;
137		for(tp=tptop;tp;tp=tpnext)
138		{
139			tpnext = tp->next;
140			if(tp->flags&L_FLAG)
141			{
142				tp->flags &= ~L_FLAG;
143				if(tp->action)
144					sh_fun(tp->action,tp->node,(char**)0);
145				tp->flags &= ~L_FLAG;
146				if(!tp->flags)
147				{
148					nv_unset(tp->node);
149					nv_close(tp->node);
150				}
151			}
152		}
153		if(!(shp->sigflag[SIGALRM]&SH_SIGALRM))
154			break;
155	}
156}
157
158
159/*
160 * This trap function catches "alarm" actions only
161 */
162static char *setdisc(Namval_t *np, const char *event, Namval_t* action, Namfun_t
163 *fp)
164{
165        register struct tevent *tp = (struct tevent*)fp;
166	if(!event)
167		return(action?"":(char*)ALARM);
168	if(strcmp(event,ALARM)!=0)
169	{
170		/* try the next level */
171		return(nv_setdisc(np, event, action, fp));
172	}
173	if(action==np)
174		action = tp->action;
175	else
176		tp->action = action;
177	return(action?(char*)action:"");
178}
179
180/*
181 * catch assignments and set alarm traps
182 */
183static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp)
184{
185	register struct tevent	*tp = (struct tevent*)fp;
186	register double d;
187	Shell_t		*shp = tp->sh;
188	if(val)
189	{
190		double now;
191#ifdef timeofday
192		struct timeval tmp;
193		timeofday(&tmp);
194		now = tmp.tv_sec + 1.e-6*tmp.tv_usec;
195#else
196		now = (double)time(NIL(time_t*));
197#endif /* timeofday */
198		nv_putv(np,val,flag,fp);
199		d = nv_getnum(np);
200		if(*val=='+')
201		{
202			double x = d + now;
203			nv_putv(np,(char*)&x,NV_INTEGER|NV_DOUBLE,fp);
204		}
205		else
206			d -= now;
207		tp->milli = 1000*(d+.0005);
208		if(tp->timeout)
209			shp->st.timetrap = time_delete(tp,shp->st.timetrap);
210		if(tp->milli > 0)
211			shp->st.timetrap = time_add(tp,shp->st.timetrap);
212	}
213	else
214	{
215		tp = (struct tevent*)nv_stack(np, (Namfun_t*)0);
216		shp->st.timetrap = time_delete(tp,shp->st.timetrap);
217		if(tp->action)
218			nv_close(tp->action);
219		nv_unset(np);
220		free((void*)fp);
221	}
222}
223
224static const Namdisc_t alarmdisc =
225{
226	sizeof(struct tevent),
227	putval,
228	0,
229	0,
230	setdisc,
231};
232
233int	b_alarm(int argc,char *argv[],void *extra)
234{
235	register int n,rflag=0;
236	register Namval_t *np;
237	register struct tevent *tp;
238	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
239	while (n = optget(argv, sh_optalarm)) switch (n)
240	{
241	    case 'r':
242		rflag = R_FLAG;
243		break;
244	    case ':':
245		errormsg(SH_DICT,2, "%s", opt_info.arg);
246		break;
247	    case '?':
248		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
249		break;
250	}
251	argc -= opt_info.index;
252	argv += opt_info.index;
253	if(error_info.errors)
254		errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0));
255	if(argc==0)
256	{
257		print_alarms(shp->st.timetrap);
258		return(0);
259	}
260	if(argc!=2)
261		errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0));
262	np = nv_open(argv[0],shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOASSIGN);
263	if(!nv_isnull(np))
264		nv_unset(np);
265	nv_setattr(np, NV_DOUBLE);
266	if(!(tp = newof(NIL(struct tevent*),struct tevent,1,0)))
267		errormsg(SH_DICT,ERROR_exit(1),e_nospace);
268	tp->fun.disc = &alarmdisc;
269	tp->flags = rflag;
270	tp->node = np;
271	tp->sh = shp;
272	nv_stack(np,(Namfun_t*)tp);
273	nv_putval(np, argv[1], 0);
274	return(0);
275}
276
277