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