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