* timer.c: Fix mispellings of get_date function's name.
[bpt/emacs.git] / lib-src / timer.c
1 /* timer.c --- daemon to provide a tagged interval timer service
2
3 This little daemon runs forever waiting for signals. SIGIO (or
4 SIGUSR1) causes it to read an event spec from stdin; that is, a
5 date followed by colon followed by an event label. SIGALRM causes
6 it to check its queue for events attached to the current second; if
7 one is found, its label is written to stdout. SIGTERM causes it to
8 terminate, printing a list of pending events.
9
10 This program is intended to be used with the lisp package called
11 timer.el. It was written anonymously in 1990. This version was
12 documented and rewritten for portability by esr@snark.thyrsus.com,
13 Aug 7 1992. */
14
15 #include <stdio.h>
16 #include <signal.h>
17 #include <fcntl.h> /* FASYNC */
18 #include <sys/types.h> /* time_t */
19
20 #include "../src/config.h"
21 #ifdef USG
22 #undef SIGIO
23 #define SIGIO SIGUSR1
24 #endif
25
26 extern int errno;
27 extern char *sys_errlist[], *malloc ();
28 extern time_t time ();
29
30 /*
31 * The field separator for input. This character shouldn't be legal in a date,
32 * and should be printable so event strings are readable by people. Was
33 * originally ';', then got changed to bogus `\001'.
34 */
35 #define FS '@'
36
37 struct event
38 {
39 char *token;
40 time_t reply_at;
41 };
42 int events_size; /* How many slots have we allocated? */
43 int num_events; /* How many are actually scheduled? */
44 struct event *events; /* events[0 .. num_events-1] are the
45 valid events. */
46
47 char *pname; /* programme name for error messages */
48
49 /* Accepts a string of two fields seperated by FS.
50 First field is string for get_date, saying when to wake-up.
51 Second field is a token to identify the request. */
52 void
53 schedule (str)
54 char *str;
55 {
56 extern time_t get_date ();
57 extern char *strcpy ();
58 time_t now;
59 register char *p;
60 static struct event *ep;
61
62 /* check entry format */
63 for (p = str; *p && *p != FS; p++)
64 continue;
65 if (!*p)
66 {
67 fprintf (stderr, "%s: bad input format: %s\n", pname, str);
68 return;
69 }
70 *p++ = 0;
71
72 /* allocate an event slot */
73 ep = events + num_events;
74
75 /* If the event array is full, stretch it. After stretching, we know
76 that ep will be pointing to an available event spot. */
77 if (ep == events + events_size)
78 {
79 int old_size = events_size;
80
81 events_size *= 2;
82 events = ((struct event *)
83 realloc (events, events_size * sizeof (struct event)));
84 if (! events)
85 {
86 fprintf (stderr, "%s: virtual memory exhausted.\n", pname);
87
88 /* Should timer exit now? Well, we've still got other
89 events in the queue, and more memory might become
90 available in the future, so we'll just toss this event.
91 This will screw up whoever scheduled the event, but
92 maybe someone else will survive. */
93 return;
94 }
95
96 while (old_size < events_size)
97 events[old_size++].token = NULL;
98 }
99
100 /* Don't allow users to schedule events in past time. */
101 ep->reply_at = get_date (str, NULL);
102 if (ep->reply_at - time (&now) < 0)
103 {
104 fprintf (stderr, "%s: bad time spec: %s%c%s\n", pname, str, FS, p);
105 return;
106 }
107
108 /* save the event description */
109 ep->token = (char *) malloc ((unsigned) strlen (p) + 1);
110 if (! ep->token)
111 {
112 fprintf (stderr, "%s: malloc %s: %s%c%s\n",
113 pname, sys_errlist[errno], str, FS, p);
114 return;
115 }
116
117 strcpy (ep->token, p);
118 num_events++;
119 }
120
121 void
122 notify ()
123 {
124 time_t now, tdiff, waitfor = -1;
125 register struct event *ep;
126
127 /* If an alarm timer runs out while this function is executing,
128 it could get called recursively. This would be bad, because
129 it's not re-entrant. So we must try to suspend the signal. */
130 #ifdef sigmask
131 sighold(SIGIO);
132 #endif
133
134 now = time ((time_t *) NULL);
135
136 for (ep = events; ep < events + num_events; ep++)
137 /* Are any events ready to fire? */
138 if (ep->reply_at <= now)
139 {
140 fputs (ep->token, stdout);
141 putc ('\n', stdout);
142 fflush (stdout);
143 free (ep->token);
144
145 /* We now have a hole in the event array; fill it with the last
146 event. */
147 ep->token = events[num_events - 1].token;
148 ep->reply_at = events[num_events - 1].reply_at;
149 num_events--;
150
151 /* We ought to scan this event again. */
152 ep--;
153 }
154 else
155 {
156 /* next timeout should be the soonest of any remaining */
157 if ((tdiff = ep->reply_at - now) < waitfor || waitfor < 0)
158 waitfor = (long)tdiff;
159 }
160
161 /* If there are no more events, we needn't bother setting an alarm. */
162 if (num_events > 0)
163 alarm (waitfor);
164
165 #ifdef sigmask
166 sigrelse(SIGIO);
167 #endif
168 }
169
170 void
171 getevent ()
172 {
173 int i;
174 char *buf;
175 int buf_size;
176
177 /* In principle the itimer should be disabled on entry to this
178 function, but it really doesn't make any important difference
179 if it isn't. */
180
181 buf_size = 80;
182 buf = (char *) malloc (buf_size);
183
184 /* Read a line from standard input, expanding buf if it is too short
185 to hold the line. */
186 for (i = 0; ; i++)
187 {
188 int c;
189
190 if (i >= buf_size)
191 {
192 buf_size *= 2;
193 buf = (char *) realloc (buf, buf_size);
194
195 /* If we're out of memory, toss this event. */
196 do
197 {
198 c = getchar ();
199 }
200 while (c != '\n' && c != EOF);
201
202 return;
203 }
204
205 c = getchar ();
206
207 if (c == EOF)
208 exit (0);
209
210 if (c == '\n')
211 {
212 buf[i] = '\0';
213 break;
214 }
215
216 buf[i] = c;
217 }
218
219 /* Register the event. */
220 schedule (buf);
221 free (buf);
222
223 /* Who knows what this interrupted, or if it said "now"? */
224 notify ();
225 }
226
227 SIGTYPE
228 sigcatch (sig)
229 int sig;
230 /* dispatch on incoming signal, then restore it */
231 {
232 struct event *ep;
233
234 switch (sig)
235 {
236 case SIGALRM:
237 notify ();
238 break;
239 case SIGIO:
240 getevent ();
241 break;
242 case SIGTERM:
243 fprintf (stderr, "Events still queued:\n");
244 for (ep = events; ep < events + num_events; ep++)
245 fprintf (stderr, "%d = %ld @ %s\n",
246 ep - events, ep->reply_at, ep->token);
247 exit (0);
248 break;
249 }
250
251 /* required on older UNIXes; harmless on newer ones */
252 signal (sig, sigcatch);
253 }
254
255 /*ARGSUSED*/
256 int
257 main (argc, argv)
258 int argc;
259 char **argv;
260 {
261 for (pname = argv[0] + strlen (argv[0]);
262 *pname != '/' && pname != argv[0];
263 pname--);
264 if (*pname == '/')
265 pname++;
266
267 events_size = 16;
268 events = ((struct event *) malloc (events_size * sizeof (*events)));
269 num_events = 0;
270
271 signal (SIGIO, sigcatch);
272 signal (SIGALRM, sigcatch);
273 signal (SIGTERM, sigcatch);
274
275 #ifndef USG
276 if (fcntl (0, F_SETOWN, getpid ()) == -1)
277 {
278 fprintf (stderr, "%s: can't set ownership of stdin\n", pname);
279 fprintf (stderr, "%s\n", sys_errlist[errno]);
280 exit (1);
281 }
282 if (fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | FASYNC) == -1)
283 {
284 fprintf (stderr, "%s: can't request asynchronous I/O on stdin\n", pname);
285 fprintf (stderr, "%s\n", sys_errlist[errno]);
286 exit (1);
287 }
288 #endif /* USG */
289
290 for (;;)
291 pause ();
292 }
293
294 /* timer.c ends here */