1 /*
2 		 Copyright (c) 1991, 1992, 1995 Simon Marshall
3 
4 		   If you still end up late, don't blame me!
5 
6   Permission to use, copy, modify, distribute, and sell this software and its
7        documentation for any purpose and without fee is hereby granted,
8     provided that the above copyright notice appear in all copies and that
9 	both that copyright notice and this permission notice appear in
10 			   supporting documentation.
11 
12   This software is provided AS IS with no warranties of any kind.  The author
13     shall have no liability with respect to the infringement of copyrights,
14      trade secrets or any patents by this file or any part thereof.  In no
15       event will the author be liable for any lost revenue or profits or
16 	      other special, indirect and consequential damages.
17 */
18 
19 /*
20  * Just a few bits and bobs.
21  */
22 
23 
24 #include "xalarm.h"
25 #include "fns.h"
26 
27 #if defined (USEGETPWUID)
28 #include <pwd.h>
29 #else
30 extern String	getenv();
31 #endif
32 
33 #if defined (USEUNAME)
34 #include <sys/utsname.h>
35 #endif
36 
37 
38 #define		FILEOPTION1	"-file"
39 #define		FILEOPTION2	"-f"
40 #define		DAEMONOPTION1	"-daemon"
41 #define		DAEMONOPTION2	"-demon"
42 #define		RESTARTONLY1	"-restartonly"
43 #define		RESTARTONLY2	"-restart"
44 
45 #define		COMMANDLIST	"echo xalarms: `%s`"
46 #define		COMMANDRESET	"%s -USR1 `%s`"
47 #define		COMMANDKILL	"%s -USR2 `%s`"
48 #define		DOWNCASE(ch)	(((ch) >= 'A') and ((ch) <= 'Z') ? \
49 				 ((ch) - 'A' + 'a') : (ch))
50 
51 
52 
53 String		Concat(), ReplaceNewlines(), NextWord(), DisplayName(),
54 		UserName(), HomeDirectory(), MachineName();
55 Boolean		IsInteger();
56 Instance	PreParseArgList();
57 XtIntervalId	ResetTracker();
58 SIGRET		KillAlarm();
59 void		Initialise(), AddTimeOuts(), SetWMName(), StartDaemon(),
60 		SetGeometry(), EnsureNotGrownOffScreen(), Audio(), Quit();
61 static void	CentreWidgetUnderPointer(), ParseGeometry();
62 static SIGRET	KillDaemon();
63 extern void	DoAlarm(), Warning(), WakeUp(), PopupAndAsk();
64 extern FORKRET	fork();
65 extern int	ParseAlarmFile(), DaysTo();
66 extern long	TimeToMilliSeconds();
67 extern unsigned long DateToMilliSeconds();
68 extern time_t	time();
69 extern struct tm *localtime();
70 
71 
72 
73 extern AlarmData   xalarm;
74 
75 
76 /*
77  * Just give a bit of help etc.
78  * Fork off a child otherwise.
79  */
80 
Initialise(proggie,data)81 void Initialise (proggie, data)
82   String     proggie;
83   ApplData  *data;
84 {
85     char   getpids[TEXT], command[TEXT];
86 
87     if (NIL (String, data->proggie))
88 	data->proggie = proggie;
89 
90     if (data->version)
91 	(void) printf ("%s: using xalarm version %s\n", data->proggie, XALARMVERSION);
92 
93     if (data->help) {
94 	(void) printf ("Usage: %s [options] [text]\n\n", data->proggie);
95 	(void) printf ("Default values for these resources/options are used by %s,\n\t",
96 		       data->proggie);
97 	(void) printf ("but may be over-ruled.\n");
98 	(void) printf ("Usual X options, plus:\n\t");
99 	(void) printf ("-file +days|date\tSet alarms up to date from alarm file(s).\n\t");
100 	(void) printf ("-daemon +days|date\tStart a daemon to look at alarm file(s).\n\t");
101 	(void) printf ("-date +days|date\tDate at which to trigger %s.\n\t",
102 		       data->proggie+1);
103 	(void) printf ("-time +time|time\tTime at which to trigger %s.\n\t",
104 		       data->proggie+1);
105 	(void) printf ("-[no]confirm\t\t[Don't] ask for confirmation.\n\t");
106 	(void) printf ("-warn time[,time...]\tTimes before %s for warnings.\n\t",
107 		       data->proggie+1);
108 	(void) printf ("-warnwords number\tWords from %s message in warning.\n\t",
109 		       data->proggie+1);
110 	(void) printf ("-list\t\t\tList xalarm process numbers (pids).\n\t");
111 	(void) printf ("-reset pid|all\t\tReset xalarm process number pid/all xalarms.\n\t");
112 	(void) printf ("-kill pid|all\t\tKill xalarm process number pid/all xalarms.\n\t");
113 	(void) printf ("-restart\t\tOnly try to restart previous alarms.\n\t");
114 	(void) printf ("-alarmaudio method\tWhat noise to make on the alarm.\n\t");
115 	(void) printf ("-warningaudio method\tWhat noise to make on warnings.\n\t");
116 	(void) printf ("-volume percentage\tPercentage volume for the terminal bell.\n\t");
117 	(void) printf ("-quiet\t\t\tDon't make a sound.\n\t");
118 	(void) printf ("-pester time\t\tTime after which %s re-triggers.\n\t",
119 		       data->proggie+1);
120 	(void) printf ("-snooze time\t\tSet initial alarm snooze time value.\n\t");
121 	(void) printf ("-nowarn\t\t\tNo warnings.\n\t");
122 	(void) printf ("-nowarnwords\t\tNo words from %s message in warning.\n\t",
123 		       data->proggie+1);
124 	(void) printf ("-nopester\t\tDon't persecute after %s triggers.\n",
125 		       data->proggie+1);
126     }
127 
128     if (data->list) {
129 	(void) sprintf (getpids, GETXALARMPIDS, getpid ());
130 	(void) sprintf (command, COMMANDLIST, getpids);
131 	if (system (command) != 0)
132 	    perror (data->proggie);
133     }
134 
135     if (NONNIL (String, data->reset))
136 	if (not STREQUAL (data->reset, ALL)) {
137 	    if (kill (atoi (data->reset), SIGUSR1) == -1)
138 		perror (data->proggie);
139 	} else {
140 	    (void) sprintf (getpids, GETXALARMPIDS, getpid ());
141 	    (void) sprintf (command, COMMANDRESET, KILLPATH, getpids);
142 	    if (system (command) != 0)
143 		perror (data->proggie);
144 	}
145 
146     if (NONNIL (String, data->kill))
147 	if (not STREQUAL (data->kill, ALL)) {
148 	    if (kill (atoi (data->kill), SIGUSR2) == -1)
149 		perror (data->proggie);
150 	} else {
151 	    (void) sprintf (getpids, GETXALARMPIDS, getpid ());
152 	    (void) sprintf (command, COMMANDKILL, KILLPATH, getpids);
153 	    if (system (command) != 0)
154 		perror (data->proggie);
155 	}
156 
157     if ((data->list) or (data->version) or (data->help) or
158 	(NONNIL (String, data->kill)) or (NONNIL (String, data->reset)))
159 	exit (0);
160 
161     /*
162      * Fork & exit the parent.  If the fork fails, carry on anyway.
163      * Is this OK?  Should we sleep a bit first?
164      */
165 
166     switch ((int) fork ()) {
167      case -1:
168 	perror (data->proggie);
169      case 0:
170 	break;
171      default:
172 	exit (0);
173     }
174 }
175 
176 
177 
178 /*
179  * Check to see if we're to do the appointments thing, and copy out
180  * the arg list, then clear the original to prevent others snooping.
181  */
182 
PreParseArgList(argv,argc,days,args)183 Instance PreParseArgList (argv, argc, days, args)
184   String  *argv, **args;
185   int 	  *argc, *days;
186 {
187     String     ch;
188     Instance   instance = Alarm;
189     int        last = *argc, i;
190 
191     *args = (String *) XtCalloc (*argc+4, sizeof (String *));
192     (*args)[0] = XtNewString (argv[0]);
193     *argc = 1;
194 
195     for (i=1; i<last; i++)
196 	if ((STREQUAL (argv[i], RESTARTONLY1)) or (STREQUAL (argv[i], RESTARTONLY2)))
197 	    instance = RestartOnly;
198 	else if ((STREQUAL (argv[i], FILEOPTION1)) or
199 		 (STREQUAL (argv[i], FILEOPTION2))) {
200 	    if (++i == last) {
201 		(void) fprintf (stderr, "No file date to parse alarm file with.\n");
202 		exit (-1);
203 	    } else if (ISINVALID (*days = DaysTo (argv[i], instance = File))) {
204 		(void) fprintf (stderr, "Can't use file date: %s", xalarm.errormessage);
205 		exit (-1);
206 	    }
207 	} else if ((STREQUAL (argv[i], DAEMONOPTION1)) or
208 		 (STREQUAL (argv[i], DAEMONOPTION2))) {
209 	    if (++i == last) {
210 		(void) fprintf (stderr, "No daemon date to parse alarm file with.\n");
211 		exit (-1);
212 	    } else if (ISINVALID (*days = DaysTo (argv[i], instance = Daemon))) {
213 		(void) fprintf (stderr, "Can't use daemon date: %s", xalarm.errormessage);
214 		exit (-1);
215 	    } else if (*days == 0) {
216 		(void) fprintf (stderr, "Zero days means daemon would not sleep!\n");
217 		exit (-1);
218 	    }
219 	} else
220 	    /* Just copy the option.
221 	     */
222 	    (*args)[(*argc)++] = XtNewString (argv[i]);
223 
224     for (i=1; i<last; i++) {
225 	ch = argv[i];
226 	while (*ch != '\0')
227 	    *(ch++) = ' ';
228     }
229 
230     return (instance);
231 }
232 
233 
234 
235 /*
236  * Fork off an xalarm process which will repeatedly parse the
237  * alarm file.
238  */
239 
StartDaemon(days,argv,argc)240 void StartDaemon (days, argv, argc)
241   String  *argv;
242   int 	   days, argc;
243 {
244     Display    *display;
245     String 	displayname = DisplayName (argv, argc);
246     int 	pid, kids;
247     time_t 	abstime;
248     struct tm  *now;
249 
250     /*
251      * Fork off the daemon.
252      */
253 
254     switch (pid = (int) fork ()) {
255      case -1:
256 	perror ("xalarm");
257 	exit (-1);
258      case 0:
259 	(void) signal (SIGUSR1, SIG_IGN);
260 	(void) signal (SIGUSR2, (SIGRET (*)()) KillDaemon);
261 	break;
262      default:
263 	(void) printf ("xalarm: Daemon started (%d).\n", pid);
264 	exit (0);
265     }
266 
267     /*
268      * Open the display for the daemon and parse & sleep until we
269      * loose the connection.
270      */
271 
272     if (NONNIL (Display *, (display = XOpenDisplay (displayname)))) {
273 	(void) time (&abstime);
274 	now = localtime (&abstime);
275 
276 	while (True) {
277 	    /*
278 	     * Start any alarms in the alarm file, then wait for them
279 	     * to finish.
280 	     */
281 	    if (ISWEEKLYD (days))
282 		kids = ParseAlarmFile (7 - now->tm_wday, argv, argc);
283 	    else
284 		kids = ParseAlarmFile (days - 1, argv, argc);
285 
286 	    while (kids-- > 0)
287 		(void) wait ((int *) NULL);
288 
289 	    /*
290 	     * Sleep until the next time, then test the connection.
291 	     */
292 	    (void) time (&abstime);
293 	    now = localtime (&abstime);
294 	    (void) sleep ((unsigned)
295 		(30 + ((ISWEEKLYD (days) ?
296 			(days = 7) - now->tm_wday + 1 : days) * SECSIN1DAY) -
297 		 (now->tm_hour * SECSIN1HR) - (now->tm_min * SECSIN1MIN) - now->tm_sec));
298 
299 	    XSync (display, False);
300 	}
301     }
302 }
303 
304 
305 
306 /*
307  * Signal handler for a daemon - it just kills the process.
308  */
309 
KillDaemon(sig,code,scp,addr)310 static SIGRET KillDaemon (sig, code, scp, addr)
311   int 		      sig, code;
312   struct sigcontext  *scp;
313   char 		     *addr;
314 {
315     (void) fprintf (stderr, "xalarm: Daemon killed (%d).\n", getpid ());
316     exit (0);
317 }
318 
319 
320 
321 /*
322  * Signal handler for a normal alarm - it just kills the process.
323  */
324 
KillAlarm(sig,code,scp,addr)325 SIGRET KillAlarm (sig, code, scp, addr)
326   int 		      sig, code;
327   struct sigcontext  *scp;
328   char 		     *addr;
329 {
330     Quit ((Widget) NULL, (XtPointer) NULL, (XtPointer) NULL);
331 }
332 
333 
334 
335 /*
336  * We add the time outs for the alarm & warnings.
337  * The warnings are added only if there is time enuf to go.
338  * ie. if "-time +10 -warn 15" you will get nowt.
339  * "-time +10 -warn 9" will, however, give you a warning in 1 min.
340  */
341 
AddTimeOuts()342 void AddTimeOuts ()
343 {
344     unsigned long   timeout = SUMTIMEOUTS (xalarm.dateout, xalarm.timeout);
345     int 	    i;
346 
347     xalarm.settime = (unsigned long) time ((time_t *) NULL);
348     xalarm.offtime = xalarm.settime + (timeout / MSECSIN1SEC);
349 
350     for (i=0; i<xalarm.numwarnings; i++)
351 	if (timeout > xalarm.warnings[i])
352 	    xalarm.timeoutid[i] =
353 		XtAppAddTimeOut (xalarm.appcon,
354 				 TIMEOUT (timeout - xalarm.warnings[i]),
355 				 (XtTimerCallbackProc) Warning,
356 				 (XtPointer) (xalarm.warnings[i] / MSECSIN1MIN));
357     xalarm.timeoutid[xalarm.numwarnings] =
358 	XtAppAddTimeOut (xalarm.appcon, TIMEOUT (timeout),
359 			 (XtTimerCallbackProc) WakeUp, (XtPointer) NULL);
360 }
361 
362 
363 
364 /*
365  * Set the window manager name of the given widget.
366  */
367 
SetWMName(widget,name)368 void SetWMName (widget, name)
369   Widget   widget;
370   String   name;
371 {
372     Display 	   *display = XtDisplay (xalarm.toplevel);
373     String 	    wmname[1];
374     XTextProperty   property;
375 
376     if (not XtIsRealized (widget))
377 	XtRealizeWidget (widget);
378 
379     wmname[0] = name;
380     if (XStringListToTextProperty (wmname, 1, &property)) {
381 	XSetWMName (display, XtWindow (widget), &property);
382 	XSetWMIconName (display, XtWindow (widget), &property);
383 	XtFree (property.value);
384     }
385 }
386 
387 
388 
389 /*
390  * Reset the tracker so it calls itself.
391  */
392 
ResetTracker(tracker,clientdata,triggered)393 XtIntervalId ResetTracker (tracker, clientdata, triggered)
394   XtTimerCallbackProc 	tracker;
395   XtPointer 		clientdata;
396   int 			triggered;
397 {
398     time_t 	now;
399     struct tm  *clock;
400 
401     (void) time (&now);
402     clock = localtime (&now);
403     /*
404      * 60 seconds from when it triggered.
405      */
406     return (XtAppAddTimeOut (xalarm.appcon,
407 			     TIMEOUT (MSECSIN1SEC * (60 - clock->tm_sec + triggered)),
408 			     tracker, clientdata));
409 }
410 
411 
412 
413 /*
414  * Set the geometry of the given widget.
415  */
416 
SetGeometry(widget)417 void SetGeometry (widget)
418   Widget   widget;
419 {
420     Dimension 	width, height, borderwidth;
421 
422     if (not XtIsRealized (widget))
423 	XtRealizeWidget (widget);
424 
425     XtVaGetValues (widget, XtNwidth, &width, XtNheight, &height,
426 		   XtNborderWidth, &borderwidth, NULL);
427 
428     if (STREQUAL (xalarm.geometry, NOGEOMETRY))
429 	CentreWidgetUnderPointer (widget, width, height, borderwidth);
430     else
431 	ParseGeometry (widget, width, height, borderwidth, xalarm.geometry);
432 }
433 
434 
435 
436 /*
437  * Move the given widget so that it is directly underneath the pointer.
438  */
439 
CentreWidgetUnderPointer(widget,width,height,borderwidth)440 static void CentreWidgetUnderPointer (widget, width, height, borderwidth)
441   Widget      widget;
442   Dimension   width, height, borderwidth;
443 {
444     Window 	   root, child;
445     int 	   x, y, dummy;
446     unsigned int   mask;
447 
448     width += (borderwidth * 2);
449     height += (borderwidth * 2);
450 
451     if (XQueryPointer (XtDisplay (widget), XtWindow (widget),
452 		       &root, &child, &x, &y, &dummy, &dummy, &mask)) {
453 	x = MAX (0, MIN (x - ((int) width / 2),
454 			 XWidthOfScreen (XtScreen (widget)) - (int) width));
455 	y = MAX (0, MIN (y - ((int) height / 2),
456 			 XHeightOfScreen (XtScreen (widget)) - (int) height));
457 	XtVaSetValues (widget, XtNx, (XtArgVal) x, XtNy, (XtArgVal) y, NULL);
458     }
459 }
460 
461 
462 
463 /*
464  * Move +/ resize the given widget so that it has the given geometry.
465  */
466 
ParseGeometry(widget,currentwidth,currentheight,borderwidth,geometry)467 static void ParseGeometry (widget, currentwidth, currentheight, borderwidth, geometry)
468   Widget      widget;
469   Dimension   currentwidth, currentheight, borderwidth;
470   String      geometry;
471 {
472     unsigned int   width = (unsigned int) currentwidth,
473 		   height = (unsigned int) currentheight;
474     int 	   x = 0, y = 0, mask;
475 
476     mask = XParseGeometry (geometry, &x, &y, &width, &height);
477 
478     if (mask & WidthValue)
479 	XtVaSetValues (widget, XtNwidth, (XtArgVal) width, NULL);
480     if (mask & HeightValue)
481 	XtVaSetValues (widget, XtNheight, (XtArgVal) height, NULL);
482     if (mask & (WidthValue | HeightValue))
483 	XtVaSetValues (widget, XtNallowShellResize, (XtArgVal) False, NULL);
484 
485     if (not (mask & (XValue | YValue)))
486 	CentreWidgetUnderPointer (widget, width, height, borderwidth);
487     else {
488 	if (mask & XNegative)
489 	    x = x + XWidthOfScreen (XtScreen (widget)) - (int) width;
490 	if (mask & YNegative)
491 	    y = y + XHeightOfScreen (XtScreen (widget)) - (int) height;
492 	XtVaSetValues (widget, XtNx, (XtArgVal) x, XtNy, (XtArgVal) y, NULL);
493     }
494 }
495 
496 
497 
498 /*
499  * Make sure that the widget's right side is visible.
500  * For resizing popups.
501  */
502 
EnsureNotGrownOffScreen(widget)503 void EnsureNotGrownOffScreen (widget)
504   Widget   widget;
505 {
506     Position 	x;
507     Dimension 	width, borderwidth, screenwidth = XWidthOfScreen (XtScreen (widget));
508 
509     XtVaGetValues (widget, XtNx, &x, XtNwidth, &width, XtNborderWidth, &borderwidth,
510 		   NULL);
511     width += (borderwidth * 2);
512     if (screenwidth > width)
513 	XtVaSetValues (widget, XtNx, (XtArgVal) MIN (x, screenwidth - width), NULL);
514 }
515 
516 
517 
518 /*
519  * Pull out any display arg.
520  */
521 
DisplayName(argv,argc)522 String DisplayName (argv, argc)
523   String  *argv;
524   int 	   argc;
525 {
526     String   displayname = (String) NULL;
527     int      i;
528 
529     for (i=1; i<argc; i++)
530 	if ((strcmp (argv[i], "-display") == 0) or (strcmp (argv[i], "-d") == 0))
531 	    if (i < argc-1)
532 		displayname = argv[i+1];
533 	    else {
534 		(void) printf ("xalarm: No display to open.\n");
535 		exit (-1);
536 	    }
537 
538     return (displayname);
539 }
540 
541 
542 
543 /*
544  * Make a sound, if that's what's wanted.
545  */
Audio(sound)546 void Audio (sound)
547   String   sound;
548 {
549     if ((STREQUAL (sound, BELL)) or (STREQUAL (sound, BEEP)))
550 	XBell (XtDisplay (xalarm.toplevel), xalarm.volume);
551     else if (not STREQUAL (sound, QUIET))
552 	if (system (sound) != 0)
553 	    perror (sound);
554 }
555 
556 
557 
558 /*
559  * Returns a string concated from the given array of strings.
560  * Separates the strings with a newline.
561  *
562  * Hacked from various books on X & Xt.
563  */
564 
Concat(strings,n)565 String Concat (strings, n)
566   String  *strings;
567   int 	   n;
568 {
569     String 	   buffer;
570     unsigned int   i, len = 0;
571 
572     if (n <= 1)
573 	return ((String) NULL);
574 
575     for (i=1; i<n; i++)
576 	len += strlen (strings[i]);
577     len += (n-1);
578 
579     buffer = XtMalloc (len+1);
580     buffer[0] = '\0';
581     for (i=1; i<n; i++) {
582 	if (i > 1)
583 	    (void) strcat (buffer, "\n");
584 	(void) strcat (buffer, strings[i]);
585     }
586 
587     return (buffer);
588 }
589 
590 
591 
592 /*
593  * Replace each newline by a space.  Returns the new string.
594  */
595 
ReplaceNewlines(str)596 String ReplaceNewlines (str)
597   String   str;
598 {
599     String   s, newstr = XtNewString (str);
600 
601     if (NONNIL (String, s = newstr))
602 	do
603 	    if (*s == '\n')
604 		*s = ' ';
605 	while (*(s++) != '\0');
606 
607     return (newstr);
608 }
609 
610 
611 
612 /*
613  * Return the next word in str, starting at position chpos in the
614  * string.  The returned word is in lower case.
615  */
616 
NextWord(str,chpos)617 String NextWord (str, chpos)
618   String   str;
619   int 	  *chpos;
620 {
621     String   word;
622     int      start, i;
623 
624     while (isspace (str[*chpos]))
625 	(*chpos)++;
626 
627     start = *chpos;
628     while ((not isspace (str[*chpos])) and (str[*chpos] != '\0'))
629 	(*chpos)++;
630 
631     if ((str[*chpos] == '\0') and (start == *chpos))
632 	return ("");
633     else {
634 	word = XtMalloc (*chpos - start + 1);
635 	for (i=0; i<(*chpos-start); i++)
636 	    word[i] = DOWNCASE (str[start+i]);
637 	word[*chpos-start] = '\0';
638 
639 	return (word);
640     }
641 }
642 
643 
644 
645 /*
646  * Returns true iff the string contains a valid integer.
647  */
648 
IsInteger(str)649 Boolean IsInteger (str)
650   String   str;
651 {
652     if (*str == '+')
653 	str++;
654     while (*str != '\0')
655 	if (not isdigit (*str++))
656 	    return (False);
657     return (True);
658 }
659 
660 
661 
662 /*
663  * Return the user, users' home directory & host names.
664  */
665 
UserName()666 String UserName ()
667 {
668     static String   name = (String) NULL;
669 
670     if (NIL (String, name))
671 #if defined (USEGETLOGIN)
672 	name = (String) getlogin ();
673 #elif defined (USECUSERID)
674 	(void) cuserid (name = XtMalloc ((Cardinal) L_cuserid + 1));
675 #elif defined (USEGETPWUID)
676 	name = getpwuid (getuid ())->pw_name;
677 	name = XtNewString (name);
678 #else
679 	name = getenv ("USER");
680 #endif
681 
682     return ((NIL (String, name)) ? NOTKNOWN : name);
683 }
684 
685 
686 
HomeDirectory()687 String HomeDirectory ()
688 {
689     static String   name = (String) NULL;
690 
691     if (NIL (String, name))
692 #if defined (USEGETPWUID)
693 	name = getpwuid (getuid ())->pw_dir;
694 	name = XtNewString (name);
695 #else
696 	name = getenv ("HOME");
697 #endif
698 
699     return ((NIL (String, name)) ? NOTKNOWN : name);
700 }
701 
702 
703 
MachineName()704 String MachineName ()
705 {
706 #if defined (USEGETHOSTNAME)
707     static char   name[TEXT];
708 
709     return ((gethostname (name, TEXT) < 0) ? NOTKNOWN : name);
710 #elif defined (USEUNAME)
711     static struct utsname   name;
712 
713     return ((uname (&name) < 0) ? NOTKNOWN : name.nodename);
714 #else
715     return (NOTKNOWN);
716 #endif
717 }
718 
719 
720 
721 /*
722  * This function generates a random number and stuffs it down the pipe.
723  */
724 
Quit(widget,clientdata,calldata)725 void Quit (widget, clientdata, calldata)
726   Widget      widget;
727   XtPointer   clientdata, calldata;
728 {
729     XtDestroyApplicationContext (xalarm.appcon);
730     exit (0);
731 }
732