1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 
38 struct	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 
50 static const char ALARM[] = "alarm";
51 
52 static void	trap_timeout(void*);
53 
54 /*
55  * insert timeout item on current given list in sorted order
56  */
time_add(struct tevent * item,void * list)57 static 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  */
time_delete(register struct tevent * item,void * list)80 static 	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 
print_alarms(void * list)97 static 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 
trap_timeout(void * handle)117 static 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) && !tp->sh->bltinfun)
126 		sh_timetraps(tp->sh);
127 }
128 
sh_timetraps(Shell_t * shp)129 void	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  */
setdisc(Namval_t * np,const char * event,Namval_t * action,Namfun_t * fp)162 static 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  */
putval(Namval_t * np,const char * val,int flag,Namfun_t * fp)183 static 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 
224 static const Namdisc_t alarmdisc =
225 {
226 	sizeof(struct tevent),
227 	putval,
228 	0,
229 	0,
230 	setdisc,
231 };
232 
b_alarm(int argc,char * argv[],Shbltin_t * context)233 int	b_alarm(int argc,char *argv[],Shbltin_t *context)
234 {
235 	register int n,rflag=0;
236 	register Namval_t *np;
237 	register struct tevent *tp;
238 	register Shell_t *shp = context->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