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