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