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