entered into RCS
[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 getdate, 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 getdate ();
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", 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", 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",
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;
125 register struct event *ep;
126
127 now = time ((time_t *) NULL);
128
129 for (ep = events; ep < events + num_events; ep++)
130 /* Are any events ready to fire? */
131 if (ep->reply_at <= now)
132 {
133 fputs (ep->token, stdout);
134 free (ep->token);
135
136 /* We now have a hole in the event array; fill it with the last
137 event. */
138 ep->token = events[num_events].token;
139 ep->reply_at = events[num_events].reply_at;
140 num_events--;
141
142 /* We ought to scan this event again. */
143 ep--;
144 }
145 else
146 {
147 /* next timeout should be the soonest of any remaining */
148 if ((tdiff = ep->reply_at - now) < waitfor || waitfor < 0)
149 waitfor = (long)tdiff;
150 }
151
152 /* If there are no more events, we needn't bother setting an alarm. */
153 if (num_events > 0)
154 alarm (waitfor);
155 }
156
157 void
158 getevent ()
159 {
160 int i;
161 char *buf;
162 int buf_size;
163
164 /* In principle the itimer should be disabled on entry to this
165 function, but it really doesn't make any important difference
166 if it isn't. */
167
168 buf_size = 80;
169 buf = (char *) malloc (buf_size);
170
171 /* Read a line from standard input, expanding buf if it is too short
172 to hold the line. */
173 for (i = 0; ; i++)
174 {
175 int c;
176
177 if (i >= buf_size)
178 {
179 buf_size *= 2;
180 buf = (char *) realloc (buf, buf_size);
181
182 /* If we're out of memory, toss this event. */
183 do
184 {
185 c = getchar ();
186 }
187 while (c != '\n' && c != EOF);
188
189 return;
190 }
191
192 c = getchar ();
193
194 if (c == EOF)
195 exit (0);
196
197 if (c == '\n')
198 {
199 buf[i] = '\0';
200 break;
201 }
202
203 buf[i] = c;
204 }
205
206 /* Register the event. */
207 schedule (buf);
208 free (buf);
209
210 /* Who knows what this interrupted, or if it said "now"? */
211 notify ();
212 }
213
214 void
215 sigcatch (sig)
216 int sig;
217 /* dispatch on incoming signal, then restore it */
218 {
219 struct event *ep;
220
221 switch (sig)
222 {
223 case SIGALRM:
224 notify ();
225 break;
226 case SIGIO:
227 getevent ();
228 break;
229 case SIGTERM:
230 fprintf (stderr, "Events still queued:\n");
231 for (ep = events; ep < events + num_events; ep++)
232 fprintf (stderr, "%d = %ld @ %s",
233 ep - events, ep->reply_at, ep->token);
234 exit (0);
235 break;
236 }
237
238 /* required on older UNIXes; harmless on newer ones */
239 signal (sig, sigcatch);
240 }
241
242 /*ARGSUSED*/
243 int
244 main (argc, argv)
245 int argc;
246 char **argv;
247 {
248 for (pname = argv[0] + strlen (argv[0]);
249 *pname != '/' && pname != argv[0];
250 pname--);
251 if (*pname == '/')
252 pname++;
253
254 events_size = 16;
255 events = ((struct event *) malloc (events_size * sizeof (*events)));
256 num_events = 0;
257
258 signal (SIGIO, sigcatch);
259 signal (SIGALRM, sigcatch);
260 signal (SIGTERM, sigcatch);
261
262 #ifndef USG
263 fcntl (0, F_SETFL, FASYNC);
264 #endif /* USG */
265
266 while (1) pause ();
267 }
268
269 /* timer.c ends here */