Also, write a newline after the token.
[bpt/emacs.git] / lib-src / timer.c
CommitLineData
fbfed6f0
JB
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
5cc564a6 15#include <stdio.h>
16#include <signal.h>
17#include <fcntl.h> /* FASYNC */
5cc564a6 18#include <sys/types.h> /* time_t */
19
9e2b097b
JB
20#include "../src/config.h"
21#ifdef USG
22#undef SIGIO
23#define SIGIO SIGUSR1
24#endif
25
5cc564a6 26extern int errno;
fbfed6f0
JB
27extern char *sys_errlist[], *malloc ();
28extern time_t time ();
5cc564a6 29
9e2b097b
JB
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
37struct event
fbfed6f0 38 {
9e2b097b
JB
39 char *token;
40 time_t reply_at;
fbfed6f0
JB
41 };
42int events_size; /* How many slots have we allocated? */
43int num_events; /* How many are actually scheduled? */
44struct event *events; /* events[0 .. num_events-1] are the
45 valid events. */
5cc564a6 46
5cc564a6 47char *pname; /* programme name for error messages */
48
9e2b097b 49/* Accepts a string of two fields seperated by FS.
fbfed6f0
JB
50 First field is string for getdate, saying when to wake-up.
51 Second field is a token to identify the request. */
52void
53schedule (str)
54 char *str;
5cc564a6 55{
fbfed6f0
JB
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)
9e2b097b 66 {
fbfed6f0
JB
67 fprintf (stderr, "%s: bad input format: %s", pname, str);
68 return;
9e2b097b 69 }
fbfed6f0 70 *p++ = 0;
5cc564a6 71
fbfed6f0
JB
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)
9e2b097b 78 {
fbfed6f0 79 int old_size = events_size;
9e2b097b 80
fbfed6f0
JB
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;
9e2b097b 115 }
fbfed6f0
JB
116
117 strcpy (ep->token, p);
118 num_events++;
5cc564a6 119}
120
121void
fbfed6f0 122notify ()
5cc564a6 123{
fbfed6f0
JB
124 time_t now, tdiff, waitfor;
125 register struct event *ep;
126
127 now = time ((time_t *) NULL);
9e2b097b 128
fbfed6f0
JB
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);
07941899 134 putc ('\n', stdout);
507876f3 135 fflush (stdout);
fbfed6f0 136 free (ep->token);
9e2b097b 137
fbfed6f0
JB
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
159void
160getevent ()
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)
9e2b097b 180 {
fbfed6f0
JB
181 buf_size *= 2;
182 buf = (char *) realloc (buf, buf_size);
183
184 /* If we're out of memory, toss this event. */
185 do
9e2b097b 186 {
fbfed6f0 187 c = getchar ();
9e2b097b 188 }
fbfed6f0
JB
189 while (c != '\n' && c != EOF);
190
191 return;
9e2b097b
JB
192 }
193
fbfed6f0 194 c = getchar ();
5cc564a6 195
fbfed6f0
JB
196 if (c == EOF)
197 exit (0);
9e2b097b 198
fbfed6f0
JB
199 if (c == '\n')
200 {
201 buf[i] = '\0';
202 break;
203 }
9e2b097b 204
fbfed6f0
JB
205 buf[i] = c;
206 }
9e2b097b 207
fbfed6f0
JB
208 /* Register the event. */
209 schedule (buf);
210 free (buf);
5cc564a6 211
fbfed6f0
JB
212 /* Who knows what this interrupted, or if it said "now"? */
213 notify ();
9e2b097b
JB
214}
215
216void
fbfed6f0
JB
217sigcatch (sig)
218 int sig;
9e2b097b 219/* dispatch on incoming signal, then restore it */
5cc564a6 220{
fbfed6f0 221 struct event *ep;
9e2b097b 222
fbfed6f0 223 switch (sig)
9e2b097b
JB
224 {
225 case SIGALRM:
fbfed6f0
JB
226 notify ();
227 break;
9e2b097b 228 case SIGIO:
fbfed6f0
JB
229 getevent ();
230 break;
9e2b097b 231 case SIGTERM:
fbfed6f0
JB
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;
9e2b097b
JB
238 }
239
fbfed6f0
JB
240 /* required on older UNIXes; harmless on newer ones */
241 signal (sig, sigcatch);
5cc564a6 242}
9e2b097b 243
5cc564a6 244/*ARGSUSED*/
245int
fbfed6f0 246main (argc, argv)
5cc564a6 247 int argc;
248 char **argv;
5cc564a6 249{
fbfed6f0
JB
250 for (pname = argv[0] + strlen (argv[0]);
251 *pname != '/' && pname != argv[0];
5cc564a6 252 pname--);
fbfed6f0
JB
253 if (*pname == '/')
254 pname++;
255
256 events_size = 16;
257 events = ((struct event *) malloc (events_size * sizeof (*events)));
258 num_events = 0;
5cc564a6 259
fbfed6f0
JB
260 signal (SIGIO, sigcatch);
261 signal (SIGALRM, sigcatch);
262 signal (SIGTERM, sigcatch);
9e2b097b
JB
263
264#ifndef USG
fbfed6f0 265 fcntl (0, F_SETFL, FASYNC);
9e2b097b 266#endif /* USG */
5cc564a6 267
fbfed6f0 268 while (1) pause ();
5cc564a6 269}
9e2b097b
JB
270
271/* timer.c ends here */