]> git.ipfire.org Git - thirdparty/bash.git/blob - mailcheck.c
Imported from ../bash-2.05.tar.gz.
[thirdparty/bash.git] / mailcheck.c
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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include "bashtypes.h"
25 #include "posixstat.h"
26 #ifndef _MINIX
27 # include <sys/param.h>
28 #endif
29 #if defined (HAVE_UNISTD_H)
30 # include <unistd.h>
31 #endif
32 #include "bashansi.h"
33
34 #include "shell.h"
35 #include "maxpath.h"
36 #include "execute_cmd.h"
37 #include "mailcheck.h"
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;
46 char *msg;
47 time_t access_time;
48 time_t mod_time;
49 off_t file_size;
50 } FILEINFO;
51
52 /* The list of remembered mail files. */
53 static FILEINFO **mailfiles = (FILEINFO **)NULL;
54
55 /* Number of mail files that we have. */
56 static int mailfiles_count;
57
58 /* The last known time that mail was checked. */
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;
63
64 /* Returns non-zero if it is time to check mail. */
65 int
66 time_to_check_mail ()
67 {
68 char *temp;
69 time_t now;
70 long seconds;
71
72 temp = get_string_value ("MAILCHECK");
73 seconds = -1L;
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
89 now = NOW;
90 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
91 time has passed since the last check. */
92 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
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++)
112 if (STREQ (mailfiles[i]->name, file))
113 return i;
114
115 return -1;
116 }
117
118 #define RESET_MAIL_FILE(i) \
119 do \
120 { \
121 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
122 mailfiles[i]->file_size = 0; \
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
144 /* Add this file to the list of remembered files and return its index
145 in the list of mail files. */
146 static int
147 add_mail_file (file, msg)
148 char *file, *msg;
149 {
150 struct stat finfo;
151 char *filename;
152 int i;
153
154 filename = full_pathname (file);
155 i = find_mail_file (filename);
156 if (i >= 0)
157 {
158 if (stat (filename, &finfo) == 0)
159 {
160 mailfiles[i]->mod_time = finfo.st_mtime;
161 mailfiles[i]->access_time = finfo.st_atime;
162 mailfiles[i]->file_size = finfo.st_size;
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;
174 mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
175 update_mail_file (i);
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 {
187 RESET_MAIL_FILE (i);
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);
200 FREE (mailfiles[i]->msg);
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
214 file_mod_date_changed (i)
215 int i;
216 {
217 time_t mtime;
218 struct stat finfo;
219 char *file;
220
221 file = mailfiles[i]->name;
222 mtime = mailfiles[i]->mod_time;
223
224 if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
225 return (mtime != finfo.st_mtime);
226
227 return (0);
228 }
229
230 /* Return non-zero if FILE's access date has changed. */
231 static int
232 file_access_date_changed (i)
233 int i;
234 {
235 time_t atime;
236 struct stat finfo;
237 char *file;
238
239 file = mailfiles[i]->name;
240 atime = mailfiles[i]->access_time;
241
242 if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
243 return (atime != finfo.st_atime);
244
245 return (0);
246 }
247
248 /* Return non-zero if FILE's size has increased. */
249 static int
250 file_has_grown (i)
251 int i;
252 {
253 off_t size;
254 struct stat finfo;
255 char *file;
256
257 file = mailfiles[i]->name;
258 size = mailfiles[i]->file_size;
259
260 return ((stat (file, &finfo) == 0) && (finfo.st_size > size));
261 }
262
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 == '%')
286 return s;
287 }
288 return ((char *)NULL);
289 }
290
291 char *
292 make_default_mailpath ()
293 {
294 char *mp;
295
296 get_current_user_info ();
297 mp = xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
298 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
299 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
300 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
301 return (mp);
302 }
303
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
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
331 while (mailfile = extract_colon_unit (mailpaths, &i))
332 {
333 mp = parse_mailpath_spec (mailfile);
334 if (mp && *mp)
335 *mp++ = '\0';
336 add_mail_file (mailfile, mp);
337 free (mailfile);
338 }
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.
350 Special hack: If the variable MAIL_WARNING is non-zero and the
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 {
356 char *current_mail_file, *message;
357 int i, use_user_notification;
358 char *dollar_underscore, *temp;
359 WORD_LIST *tlist;
360
361 dollar_underscore = get_string_value ("_");
362 if (dollar_underscore)
363 dollar_underscore = savestring (dollar_underscore);
364
365 for (i = 0; i < mailfiles_count; i++)
366 {
367 current_mail_file = mailfiles[i]->name;
368
369 if (*current_mail_file == '\0')
370 continue;
371
372 if (file_mod_date_changed (i))
373 {
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 $_";
378
379 bind_variable ("_", current_mail_file);
380
381 #define atime mailfiles[i]->access_time
382 #define mtime mailfiles[i]->mod_time
383
384 /* Have to compute this before the call to update_mail_file, which
385 resets all the information. */
386 file_is_bigger = file_has_grown (i);
387
388 update_mail_file (i);
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)
397 continue;
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. */
401 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
402 message = "You have new mail in $_";
403 #undef atime
404 #undef mtime
405
406 if ((tlist = expand_string (message, Q_DOUBLE_QUOTES)))
407 {
408 temp = string_list (tlist);
409 puts (temp);
410 free (temp);
411 dispose_words (tlist);
412 }
413 else
414 putchar ('\n');
415 }
416
417 if (mail_warning && file_access_date_changed (i))
418 {
419 update_mail_file (i);
420 printf ("The mail in %s has been read\n", current_mail_file);
421 }
422 }
423
424 if (dollar_underscore)
425 {
426 bind_variable ("_", dollar_underscore);
427 free (dollar_underscore);
428 }
429 else
430 unbind_variable ("_");
431 }