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  * Deal with the appointments alarm file.
21  */
22 
23 
24 #include "xalarm.h"
25 #include "dates.h"
26 
27 #include <X11/Xaw/Dialog.h>
28 
29 /*
30  * We use fcntl() to lock files, or we can use flock() or lockf():
31  */
32 
33 #if defined (USEFLOCK)
34 #include <sys/file.h>
35 #elif defined (USELOCKF)
36 #include <unistd.h>
37 #elif not defined (SEEK_SET)
38 #define SEEK_SET 0
39 #endif
40 
41 
42 #define		ALARMFILEFORMAT		"\n%s %s %2d - %s\t%s\n"
43 #define		DIALOGVALUE(str, widget) (str = \
44 					  XawDialogGetValueString (widget), \
45 					  str = XtNewString (str))
46 #define		UPCASE(ch)		(((ch) >= 'a') and ((ch) <= 'z') ? \
47 					 ((ch) - 'a' + 'A') : (ch))
48 
49 
50 void		SaveAlarmSettings(), AlarmDying(), RestartDiedAlarms();
51 int		ParseAlarmFile();
52 static Boolean	SaveAlarm();
53 static void	RenameFile();
54 extern Boolean	AppointmentWithin();
55 extern long	TimeToMilliSeconds();
56 extern unsigned long DateToMilliSeconds();
57 extern String	ReplaceNewlines(), HomeDirectory(), DisplayName(), getenv();
58 extern void	DoAlarm(), EditedText(), MakeConfirmMessage(), EnsureNotGrownOffScreen();
59 extern FORKRET	fork();
60 extern time_t	time();
61 extern struct tm *localtime();
62 
63 
64 
65 extern AlarmData	xalarm;
66 
67 
68 
69 /*
70  * Parse each line of the alarm file, if it's around.  If the
71  * appointment is within that given, fork and do the alarm.  Returns
72  * number forked.
73  */
74 
ParseAlarmFile(days,argv,argc)75 int ParseAlarmFile (days, argv, argc)
76   String  *argv;
77   int 	   days, argc;
78 {
79     FILE     *file;
80     String    timestr, datestr, alarmfilepath = getenv ("XALARMFILEPATH"),
81 	      home = HomeDirectory ();
82     Boolean   failed = True;
83     char      alarmpath[TEXT], filename[TEXT], line[TEXT];
84     int       newargc, endofdate, start, finish = 0, kids = 0;
85 
86     (void) sprintf (alarmpath, "%s/.xalarms", HomeDirectory ());
87     if (NONNIL (String, alarmfilepath))
88 	(void) sprintf (ENDOF (alarmpath), ":%s", alarmfilepath);
89 
90     do {
91 	/*
92 	 * Try each file in the path list.
93 	 */
94 	start = finish;
95 	while ((alarmpath[finish] != '\0') and (alarmpath[finish] != ':'))
96 	    finish++;
97 
98 	if (alarmpath[start] == '/') {
99 	    (void) strncpy (filename, alarmpath+start, finish-start);
100 	    filename[finish-start] = '\0';
101 	} else {
102 	    (void) sprintf (filename, "%s/", home);
103 	    (void) strncpy (ENDOF (filename), alarmpath+start, finish-start);
104 	    filename[finish-start + strlen (home)+1] = '\0';
105 	}
106 
107 	if (NONNIL (FILE *, (file = fopen (filename, "r")))) {
108 	    /*
109 	     * Deal with this file.
110 	     */
111 	    failed = False;
112 
113 	    while (NONNIL (String, fgets (line, TEXT, file))) {
114 		endofdate = 0;
115 		if (AppointmentWithin (days, line, &timestr, &datestr, &endofdate)) {
116 		    while (isspace (line[endofdate]))
117 			endofdate++;
118 
119 		    newargc = argc;
120 		    argv[newargc++] = XtNewString ("-time");
121 		    argv[newargc++] = timestr;
122 		    argv[newargc++] = XtNewString ("-date");
123 		    argv[newargc++] = datestr;
124 
125 		    if (line[endofdate] != '\0') {
126 			line[strlen (line) - 1] = '\0';
127 			argv[newargc++] = line+endofdate;
128 		    }
129 
130 		    switch ((int) fork ()) {
131 		     case -1:
132 			perror ("xalarm");
133 			exit (-1);
134 		     case 0:
135 			DoAlarm (argv, newargc);
136 		     default:
137 			kids++;
138 		    }
139 		}
140 	    }
141 	}
142 
143 	while (alarmpath[finish] == ':')
144 	    finish++;
145     } while (alarmpath[finish] != '\0');
146 
147     if (failed)
148 	fprintf (stderr, "Failed to open any of these alarm files:\n%s\n", alarmpath);
149 
150     return (kids);
151 }
152 
153 
154 
155 /*
156  * Save the alarm settings in the alarm file.
157  */
158 
SaveAlarmSettings(widget,clientdata,calldata)159 void SaveAlarmSettings (widget, clientdata, calldata)
160   Widget      widget;
161   XtPointer   clientdata, calldata;
162 {
163     String   message;
164     char     buf[TEXT], filename[TEXT];
165 
166     DIALOGVALUE (message, xalarm.confirmwidget);
167 
168     if (*message != '\0')
169 	XtVaSetValues (xalarm.messagewidget, XtNlabel,
170 		       (XtArgVal) (xalarm.messagestr = message), NULL);
171 
172     MakeConfirmMessage (buf);
173     (void) sprintf (filename, "%s/%s", HomeDirectory (), XALARMFILE);
174     if (not SaveAlarm (filename))
175 	(void) sprintf (ENDOF (buf), "\nCouldn't open %s", filename);
176     else {
177 	(void) sprintf (ENDOF (buf), "\nSaved in %s", filename);
178 	EditedText ((Widget) NULL, (XtPointer) NULL, (XtPointer) False);
179     }
180 
181     XtVaSetValues (xalarm.confirmwidget, XtNlabel, (XtArgVal) buf, NULL);
182     EnsureNotGrownOffScreen (XtParent (XtParent (xalarm.confirmwidget)));
183 }
184 
185 
186 
187 /*
188  * Save the alarm in the alarm-died file.  Can be resurrected later.
189  */
190 
AlarmDying(display)191 void AlarmDying (display)
192   Display  *display;
193 {
194     char   filename[TEXT];
195 
196     if (xalarm.saveonshutdown) {
197 	(void) sprintf (filename, "%s/%s.died", HomeDirectory (), XALARMFILE);
198 	if (not SaveAlarm (filename))
199 	    (void) fprintf (stderr, "xalarm: Couldn't open %s\n", filename);
200     }
201 
202     exit (0);
203 }
204 
205 
206 
207 /*
208  * Save the current alarm.
209  */
210 
SaveAlarm(filename)211 static Boolean SaveAlarm (filename)
212   String   filename;
213 {
214     FILE 	  *file;
215     static char    days[][4] = {WEEKDAYS}, months[][4] = {MONTHS};
216 #if not (defined (USEFLOCK) or defined (USELOCKF))
217     struct flock   lock;
218 #endif
219     time_t 	   now;
220     struct tm 	  *alarmtime;
221 
222     (void) umask (63);
223     if (NONNIL (FILE *, (file = fopen (filename, "a")))) {
224 #if defined (USEFLOCK)
225 	flock (fileno (file), LOCK_EX);
226 #elif defined (USELOCKF)
227 	lockf (fileno (file), F_LOCK, (long) 0);
228 #else
229 	lock.l_type = F_WRLCK;
230 	lock.l_whence = SEEK_SET;
231 	lock.l_start = lock.l_len = (long) 0;
232 	fcntl (fileno (file), F_SETFL, O_FSYNC);
233 	fcntl (fileno (file), F_SETLKW, &lock);
234 #endif
235 
236 	(void) time (&now);
237 	now += ((TimeToMilliSeconds (xalarm.timestr) +
238 		 DateToMilliSeconds (xalarm.datestr)) / MSECSIN1SEC);
239 	alarmtime = localtime (&now);
240 
241 	days[alarmtime->tm_wday][0] = UPCASE (days[alarmtime->tm_wday][0]);
242 	months[alarmtime->tm_mon][0] = UPCASE (months[alarmtime->tm_mon][0]);
243 
244 #if defined (USELSEEK)
245 	lseek (fileno (file), (off_t) 0, L_XTND);
246 #else
247 	fseek (file, (long) 0, 2);
248 #endif
249 
250 	(void) fprintf (file, ALARMFILEFORMAT, days[alarmtime->tm_wday],
251 			months[alarmtime->tm_mon], alarmtime->tm_mday,
252 			xalarm.timestr, ReplaceNewlines (xalarm.messagestr));
253 
254 	fflush (file);
255 #if defined (USEFLOCK)
256 	flock (fileno (file), LOCK_UN);
257 #elif defined (USELOCKF)
258 	lockf (fileno (file), F_ULOCK, (long) 0);
259 #else
260 	lock.l_type = F_UNLCK;
261 	fcntl (fileno (file), F_SETLK, &lock);
262 #endif
263 	fclose (file);
264     }
265 
266     return (NONNIL (FILE *, file));
267 }
268 
269 
270 
271 /*
272  * Check to see if there are any old alarms, then restart them.
273  */
274 
RestartDiedAlarms(argv,argc)275 void RestartDiedAlarms (argv, argc)
276   String  *argv;
277   int 	   argc;
278 {
279     Display  *display;
280     char      diedfilename[TEXT], livefilename[TEXT], tmp[TEXT];
281     int       kids;
282 
283     (void) sprintf (diedfilename, "%s/%s.died", HomeDirectory (), XALARMFILE);
284     if (access (diedfilename, R_OK) == 0)
285 	if (NIL (Display *, (display = XOpenDisplay (DisplayName (argv, argc)))))
286 	    (void) fprintf (stderr, "xalarm: Can't open display to restart alarms.\n");
287 	else {
288 	    /* Save the current alarm file. */
289 	    (void) sprintf (livefilename, "%s/%s", HomeDirectory (), XALARMFILE);
290 	    (void) sprintf (tmp, "%s/%s.%05d", HomeDirectory (), XALARMFILE, getpid ());
291 	    if (access (livefilename, R_OK) == 0)
292 		RenameFile (livefilename, tmp);
293 	    RenameFile (diedfilename, livefilename);
294 
295 	    /* Parse this file as if it were the alarm file. */
296 	    xalarm.saveonshutdown = True;
297 	    kids = ParseAlarmFile (MAXDAYS, argv, argc);
298 	    while (kids-- > 0)
299 		(void) wait ((int *) NULL);
300 	    (void) unlink (livefilename);
301 
302 	    /* Restore the alarm file. */
303 	    if (access (tmp, R_OK) == 0)
304 		RenameFile (tmp, livefilename);
305 	    XCloseDisplay (display);
306 	}
307 }
308 
309 
310 
311 /*
312  * Rename the file.
313  */
314 
RenameFile(from,to)315 static void RenameFile (from, to)
316   String   from, to;
317 {
318 #if defined (USERENAME)
319     (void) rename (from, to);
320 #else
321     (void) unlink (to);
322     (void) link (from, to);
323     (void) unlink (from);
324 #endif
325 }
326