]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* mailcheck.c -- The check is in the mail... */ |
2 | ||
3 | /* Copyright (C) 1987,1989 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
7 | Bash is free software; you can redistribute it and/or modify it under | |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 1, or (at your option) any later | |
10 | version. | |
11 | ||
12 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License along | |
18 | with Bash; see the file COPYING. If not, write to the Free Software | |
19 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
ccc6cda3 JA |
21 | #include "config.h" |
22 | ||
726f6388 JA |
23 | #include <stdio.h> |
24 | #include "bashtypes.h" | |
25 | #include "posixstat.h" | |
26 | #include <sys/param.h> | |
ccc6cda3 JA |
27 | #if defined (HAVE_UNISTD_H) |
28 | # include <unistd.h> | |
29 | #endif | |
726f6388 | 30 | #include "bashansi.h" |
ccc6cda3 | 31 | |
726f6388 JA |
32 | #include "shell.h" |
33 | #include "maxpath.h" | |
34 | #include "execute_cmd.h" | |
ccc6cda3 | 35 | #include "mailcheck.h" |
726f6388 JA |
36 | #include <tilde/tilde.h> |
37 | ||
38 | #ifndef NOW | |
39 | #define NOW ((time_t)time ((time_t *)0)) | |
40 | #endif | |
41 | ||
42 | typedef struct { | |
43 | char *name; | |
ccc6cda3 | 44 | char *msg; |
726f6388 JA |
45 | time_t access_time; |
46 | time_t mod_time; | |
47 | long file_size; | |
48 | } FILEINFO; | |
49 | ||
50 | /* The list of remembered mail files. */ | |
ccc6cda3 | 51 | static FILEINFO **mailfiles = (FILEINFO **)NULL; |
726f6388 JA |
52 | |
53 | /* Number of mail files that we have. */ | |
ccc6cda3 | 54 | static int mailfiles_count; |
726f6388 JA |
55 | |
56 | /* The last known time that mail was checked. */ | |
ccc6cda3 JA |
57 | static int last_time_mail_checked; |
58 | ||
59 | /* Non-zero means warn if a mail file has been read since last checked. */ | |
60 | int mail_warning; | |
726f6388 JA |
61 | |
62 | /* Returns non-zero if it is time to check mail. */ | |
63 | int | |
64 | time_to_check_mail () | |
65 | { | |
ccc6cda3 JA |
66 | char *temp; |
67 | time_t now; | |
68 | long seconds; | |
69 | ||
70 | temp = get_string_value ("MAILCHECK"); | |
71 | seconds = -1L; | |
726f6388 JA |
72 | |
73 | /* Skip leading whitespace in MAILCHECK. */ | |
74 | if (temp) | |
75 | { | |
76 | while (whitespace (*temp)) | |
77 | temp++; | |
78 | ||
79 | seconds = atoi (temp); | |
80 | } | |
81 | ||
82 | /* Negative number, or non-numbers (such as empty string) cause no | |
83 | checking to take place. */ | |
84 | if (seconds < 0) | |
85 | return (0); | |
86 | ||
ccc6cda3 | 87 | now = NOW; |
726f6388 JA |
88 | /* Time to check if MAILCHECK is explicitly set to zero, or if enough |
89 | time has passed since the last check. */ | |
ccc6cda3 | 90 | return (seconds == 0 || ((now - last_time_mail_checked) >= seconds)); |
726f6388 JA |
91 | } |
92 | ||
93 | /* Okay, we have checked the mail. Perhaps I should make this function | |
94 | go away. */ | |
95 | void | |
96 | reset_mail_timer () | |
97 | { | |
98 | last_time_mail_checked = NOW; | |
99 | } | |
100 | ||
101 | /* Locate a file in the list. Return index of | |
102 | entry, or -1 if not found. */ | |
103 | static int | |
104 | find_mail_file (file) | |
105 | char *file; | |
106 | { | |
107 | register int i; | |
108 | ||
109 | for (i = 0; i < mailfiles_count; i++) | |
ccc6cda3 | 110 | if (STREQ (mailfiles[i]->name, file)) |
726f6388 JA |
111 | return i; |
112 | ||
113 | return -1; | |
114 | } | |
115 | ||
ccc6cda3 JA |
116 | #define RESET_MAIL_FILE(i) \ |
117 | do \ | |
118 | { \ | |
119 | mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \ | |
120 | mailfiles[i]->file_size = 0L; \ | |
121 | } \ | |
122 | while (0) | |
123 | ||
124 | static void | |
125 | update_mail_file (i) | |
126 | int i; | |
127 | { | |
128 | char *file; | |
129 | struct stat finfo; | |
130 | ||
131 | file = mailfiles[i]->name; | |
132 | if (stat (file, &finfo) == 0) | |
133 | { | |
134 | mailfiles[i]->access_time = finfo.st_atime; | |
135 | mailfiles[i]->mod_time = finfo.st_mtime; | |
136 | mailfiles[i]->file_size = finfo.st_size; | |
137 | } | |
138 | else | |
139 | RESET_MAIL_FILE (i); | |
140 | } | |
141 | ||
726f6388 JA |
142 | /* Add this file to the list of remembered files and return its index |
143 | in the list of mail files. */ | |
144 | static int | |
ccc6cda3 JA |
145 | add_mail_file (file, msg) |
146 | char *file, *msg; | |
726f6388 JA |
147 | { |
148 | struct stat finfo; | |
149 | char *filename; | |
150 | int i; | |
151 | ||
152 | filename = full_pathname (file); | |
153 | i = find_mail_file (file); | |
ccc6cda3 | 154 | if (i >= 0) |
726f6388 JA |
155 | { |
156 | if (stat (filename, &finfo) == 0) | |
157 | { | |
158 | mailfiles[i]->mod_time = finfo.st_mtime; | |
159 | mailfiles[i]->access_time = finfo.st_atime; | |
160 | mailfiles[i]->file_size = (long)finfo.st_size; | |
161 | } | |
162 | free (filename); | |
163 | return i; | |
164 | } | |
165 | ||
166 | i = mailfiles_count++; | |
167 | mailfiles = (FILEINFO **)xrealloc | |
168 | (mailfiles, mailfiles_count * sizeof (FILEINFO *)); | |
169 | ||
170 | mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO)); | |
171 | mailfiles[i]->name = filename; | |
ccc6cda3 JA |
172 | mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL; |
173 | update_mail_file (i); | |
726f6388 JA |
174 | return i; |
175 | } | |
176 | ||
177 | /* Reset the existing mail files access and modification times to zero. */ | |
178 | void | |
179 | reset_mail_files () | |
180 | { | |
181 | register int i; | |
182 | ||
183 | for (i = 0; i < mailfiles_count; i++) | |
184 | { | |
ccc6cda3 | 185 | RESET_MAIL_FILE (i); |
726f6388 JA |
186 | } |
187 | } | |
188 | ||
189 | /* Free the information that we have about the remembered mail files. */ | |
190 | void | |
191 | free_mail_files () | |
192 | { | |
193 | register int i; | |
194 | ||
195 | for (i = 0; i < mailfiles_count; i++) | |
196 | { | |
197 | free (mailfiles[i]->name); | |
ccc6cda3 | 198 | FREE (mailfiles[i]->msg); |
726f6388 JA |
199 | free (mailfiles[i]); |
200 | } | |
201 | ||
202 | if (mailfiles) | |
203 | free (mailfiles); | |
204 | ||
205 | mailfiles_count = 0; | |
206 | mailfiles = (FILEINFO **)NULL; | |
207 | } | |
208 | ||
209 | /* Return non-zero if FILE's mod date has changed and it has not been | |
210 | accessed since modified. */ | |
211 | static int | |
ccc6cda3 JA |
212 | file_mod_date_changed (i) |
213 | int i; | |
726f6388 | 214 | { |
ccc6cda3 | 215 | time_t mtime; |
726f6388 | 216 | struct stat finfo; |
ccc6cda3 | 217 | char *file; |
726f6388 | 218 | |
ccc6cda3 JA |
219 | file = mailfiles[i]->name; |
220 | mtime = mailfiles[i]->mod_time; | |
726f6388 JA |
221 | |
222 | if ((stat (file, &finfo) == 0) && (finfo.st_size > 0)) | |
ccc6cda3 | 223 | return (mtime != finfo.st_mtime); |
726f6388 JA |
224 | |
225 | return (0); | |
226 | } | |
227 | ||
228 | /* Return non-zero if FILE's access date has changed. */ | |
229 | static int | |
ccc6cda3 JA |
230 | file_access_date_changed (i) |
231 | int i; | |
726f6388 | 232 | { |
ccc6cda3 | 233 | time_t atime; |
726f6388 | 234 | struct stat finfo; |
ccc6cda3 | 235 | char *file; |
726f6388 | 236 | |
ccc6cda3 JA |
237 | file = mailfiles[i]->name; |
238 | atime = mailfiles[i]->access_time; | |
726f6388 JA |
239 | |
240 | if ((stat (file, &finfo) == 0) && (finfo.st_size > 0)) | |
ccc6cda3 | 241 | return (atime != finfo.st_atime); |
726f6388 JA |
242 | |
243 | return (0); | |
244 | } | |
245 | ||
246 | /* Return non-zero if FILE's size has increased. */ | |
247 | static int | |
ccc6cda3 JA |
248 | file_has_grown (i) |
249 | int i; | |
726f6388 | 250 | { |
ccc6cda3 | 251 | long size; |
726f6388 | 252 | struct stat finfo; |
ccc6cda3 | 253 | char *file; |
726f6388 | 254 | |
ccc6cda3 JA |
255 | file = mailfiles[i]->name; |
256 | size = mailfiles[i]->file_size; | |
726f6388 JA |
257 | |
258 | return ((stat (file, &finfo) == 0) && (finfo.st_size > size)); | |
259 | } | |
260 | ||
726f6388 JA |
261 | /* Take an element from $MAILPATH and return the portion from |
262 | the first unquoted `?' or `%' to the end of the string. This is the | |
263 | message to be printed when the file contents change. */ | |
264 | static char * | |
265 | parse_mailpath_spec (str) | |
266 | char *str; | |
267 | { | |
268 | char *s; | |
269 | int pass_next; | |
270 | ||
271 | for (s = str, pass_next = 0; s && *s; s++) | |
272 | { | |
273 | if (pass_next) | |
274 | { | |
275 | pass_next = 0; | |
276 | continue; | |
277 | } | |
278 | if (*s == '\\') | |
279 | { | |
280 | pass_next++; | |
281 | continue; | |
282 | } | |
283 | if (*s == '?' || *s == '%') | |
284 | return s; | |
285 | } | |
286 | return ((char *)NULL); | |
287 | } | |
ccc6cda3 JA |
288 | |
289 | char * | |
290 | make_default_mailpath () | |
291 | { | |
292 | char *mp; | |
293 | ||
294 | mp = xmalloc (1 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name)); | |
295 | strcpy (mp, DEFAULT_MAIL_DIRECTORY); | |
296 | strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY) - 1, current_user.user_name); | |
297 | return (mp); | |
298 | } | |
299 | ||
726f6388 JA |
300 | /* Remember the dates of the files specified by MAILPATH, or if there is |
301 | no MAILPATH, by the file specified in MAIL. If neither exists, use a | |
302 | default value, which we randomly concoct from using Unix. */ | |
303 | void | |
304 | remember_mail_dates () | |
305 | { | |
306 | char *mailpaths; | |
307 | char *mailfile, *mp; | |
308 | int i = 0; | |
309 | ||
ccc6cda3 JA |
310 | mailpaths = get_string_value ("MAILPATH"); |
311 | ||
312 | /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */ | |
313 | if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL"))) | |
314 | { | |
315 | add_mail_file (mailpaths, (char *)NULL); | |
316 | return; | |
317 | } | |
318 | ||
319 | if (mailpaths == 0) | |
320 | { | |
321 | mailpaths = make_default_mailpath (); | |
322 | add_mail_file (mailpaths, (char *)NULL); | |
323 | free (mailpaths); | |
324 | return; | |
325 | } | |
326 | ||
726f6388 JA |
327 | while (mailfile = extract_colon_unit (mailpaths, &i)) |
328 | { | |
329 | mp = parse_mailpath_spec (mailfile); | |
330 | if (mp && *mp) | |
ccc6cda3 JA |
331 | *mp++ = '\0'; |
332 | add_mail_file (mailfile, mp); | |
726f6388 JA |
333 | free (mailfile); |
334 | } | |
726f6388 JA |
335 | } |
336 | ||
337 | /* check_mail () is useful for more than just checking mail. Since it has | |
338 | the paranoids dream ability of telling you when someone has read your | |
339 | mail, it can just as easily be used to tell you when someones .profile | |
340 | file has been read, thus letting one know when someone else has logged | |
341 | in. Pretty good, huh? */ | |
342 | ||
343 | /* Check for mail in some files. If the modification date of any | |
344 | of the files in MAILPATH has changed since we last did a | |
345 | remember_mail_dates () then mention that the user has mail. | |
ccc6cda3 | 346 | Special hack: If the variable MAIL_WARNING is non-zero and the |
726f6388 JA |
347 | mail file has been accessed since the last time we remembered, then |
348 | the message "The mail in <mailfile> has been read" is printed. */ | |
349 | void | |
350 | check_mail () | |
351 | { | |
ccc6cda3 JA |
352 | char *current_mail_file, *message; |
353 | int i, use_user_notification; | |
354 | char *dollar_underscore, *temp; | |
355 | WORD_LIST *tlist; | |
726f6388 JA |
356 | |
357 | dollar_underscore = get_string_value ("_"); | |
726f6388 JA |
358 | if (dollar_underscore) |
359 | dollar_underscore = savestring (dollar_underscore); | |
360 | ||
ccc6cda3 | 361 | for (i = 0; i < mailfiles_count; i++) |
726f6388 | 362 | { |
ccc6cda3 | 363 | current_mail_file = mailfiles[i]->name; |
726f6388 | 364 | |
ccc6cda3 JA |
365 | if (*current_mail_file == '\0') |
366 | continue; | |
726f6388 | 367 | |
ccc6cda3 | 368 | if (file_mod_date_changed (i)) |
726f6388 | 369 | { |
ccc6cda3 JA |
370 | int file_is_bigger; |
371 | ||
372 | use_user_notification = mailfiles[i]->msg != (char *)NULL; | |
373 | message = mailfiles[i]->msg ? mailfiles[i]->msg : "You have mail in $_"; | |
726f6388 | 374 | |
726f6388 | 375 | bind_variable ("_", current_mail_file); |
ccc6cda3 | 376 | |
726f6388 JA |
377 | #define atime mailfiles[i]->access_time |
378 | #define mtime mailfiles[i]->mod_time | |
379 | ||
ccc6cda3 | 380 | /* Have to compute this before the call to update_mail_file, which |
726f6388 | 381 | resets all the information. */ |
ccc6cda3 | 382 | file_is_bigger = file_has_grown (i); |
726f6388 | 383 | |
ccc6cda3 | 384 | update_mail_file (i); |
726f6388 JA |
385 | |
386 | /* If the user has just run a program which manipulates the | |
387 | mail file, then don't bother explaining that the mail | |
388 | file has been manipulated. Since some systems don't change | |
389 | the access time to be equal to the modification time when | |
390 | the mail in the file is manipulated, check the size also. If | |
391 | the file has not grown, continue. */ | |
392 | if ((atime >= mtime) && !file_is_bigger) | |
ccc6cda3 | 393 | continue; |
726f6388 JA |
394 | |
395 | /* If the mod time is later than the access time and the file | |
396 | has grown, note the fact that this is *new* mail. */ | |
ccc6cda3 JA |
397 | if (use_user_notification == 0 && (atime < mtime) && file_is_bigger) |
398 | message = "You have new mail in $_"; | |
726f6388 JA |
399 | #undef atime |
400 | #undef mtime | |
401 | ||
ccc6cda3 | 402 | if ((tlist = expand_string (message, Q_DOUBLE_QUOTES))) |
726f6388 | 403 | { |
ccc6cda3 JA |
404 | temp = string_list (tlist); |
405 | puts (temp); | |
406 | free (temp); | |
726f6388 JA |
407 | dispose_words (tlist); |
408 | } | |
409 | else | |
ccc6cda3 | 410 | putchar ('\n'); |
726f6388 JA |
411 | } |
412 | ||
ccc6cda3 | 413 | if (mail_warning && file_access_date_changed (i)) |
726f6388 | 414 | { |
ccc6cda3 JA |
415 | update_mail_file (i); |
416 | printf ("The mail in %s has been read\n", current_mail_file); | |
726f6388 | 417 | } |
726f6388 | 418 | } |
726f6388 JA |
419 | |
420 | if (dollar_underscore) | |
421 | { | |
422 | bind_variable ("_", dollar_underscore); | |
423 | free (dollar_underscore); | |
424 | } | |
425 | else | |
426 | unbind_variable ("_"); | |
427 | } |