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, ×tr, &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