1 /* mailcheck.c -- The check is in the mail... */
3 /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
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
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
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. */
24 #include "bashtypes.h"
25 #include "posixstat.h"
26 #include <sys/param.h>
27 #if defined (HAVE_UNISTD_H)
34 #include "execute_cmd.h"
35 #include "mailcheck.h"
36 #include <tilde/tilde.h>
39 #define NOW ((time_t)time ((time_t *)0))
50 /* The list of remembered mail files. */
51 static FILEINFO **mailfiles = (FILEINFO **)NULL;
53 /* Number of mail files that we have. */
54 static int mailfiles_count;
56 /* The last known time that mail was checked. */
57 static int last_time_mail_checked;
59 /* Non-zero means warn if a mail file has been read since last checked. */
62 /* Returns non-zero if it is time to check mail. */
70 temp = get_string_value ("MAILCHECK");
73 /* Skip leading whitespace in MAILCHECK. */
76 while (whitespace (*temp))
79 seconds = atoi (temp);
82 /* Negative number, or non-numbers (such as empty string) cause no
83 checking to take place. */
88 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
89 time has passed since the last check. */
90 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
93 /* Okay, we have checked the mail. Perhaps I should make this function
98 last_time_mail_checked = NOW;
101 /* Locate a file in the list. Return index of
102 entry, or -1 if not found. */
104 find_mail_file (file)
109 for (i = 0; i < mailfiles_count; i++)
110 if (STREQ (mailfiles[i]->name, file))
116 #define RESET_MAIL_FILE(i) \
119 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
120 mailfiles[i]->file_size = 0L; \
131 file = mailfiles[i]->name;
132 if (stat (file, &finfo) == 0)
134 mailfiles[i]->access_time = finfo.st_atime;
135 mailfiles[i]->mod_time = finfo.st_mtime;
136 mailfiles[i]->file_size = finfo.st_size;
142 /* Add this file to the list of remembered files and return its index
143 in the list of mail files. */
145 add_mail_file (file, msg)
152 filename = full_pathname (file);
153 i = find_mail_file (filename);
156 if (stat (filename, &finfo) == 0)
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;
166 i = mailfiles_count++;
167 mailfiles = (FILEINFO **)xrealloc
168 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
170 mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
171 mailfiles[i]->name = filename;
172 mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
173 update_mail_file (i);
177 /* Reset the existing mail files access and modification times to zero. */
183 for (i = 0; i < mailfiles_count; i++)
189 /* Free the information that we have about the remembered mail files. */
195 for (i = 0; i < mailfiles_count; i++)
197 free (mailfiles[i]->name);
198 FREE (mailfiles[i]->msg);
206 mailfiles = (FILEINFO **)NULL;
209 /* Return non-zero if FILE's mod date has changed and it has not been
210 accessed since modified. */
212 file_mod_date_changed (i)
219 file = mailfiles[i]->name;
220 mtime = mailfiles[i]->mod_time;
222 if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
223 return (mtime != finfo.st_mtime);
228 /* Return non-zero if FILE's access date has changed. */
230 file_access_date_changed (i)
237 file = mailfiles[i]->name;
238 atime = mailfiles[i]->access_time;
240 if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
241 return (atime != finfo.st_atime);
246 /* Return non-zero if FILE's size has increased. */
255 file = mailfiles[i]->name;
256 size = mailfiles[i]->file_size;
258 return ((stat (file, &finfo) == 0) && (finfo.st_size > size));
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. */
265 parse_mailpath_spec (str)
271 for (s = str, pass_next = 0; s && *s; s++)
283 if (*s == '?' || *s == '%')
286 return ((char *)NULL);
290 make_default_mailpath ()
294 mp = xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
295 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
296 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
297 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
301 /* Remember the dates of the files specified by MAILPATH, or if there is
302 no MAILPATH, by the file specified in MAIL. If neither exists, use a
303 default value, which we randomly concoct from using Unix. */
305 remember_mail_dates ()
311 mailpaths = get_string_value ("MAILPATH");
313 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
314 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
316 add_mail_file (mailpaths, (char *)NULL);
322 mailpaths = make_default_mailpath ();
323 add_mail_file (mailpaths, (char *)NULL);
328 while (mailfile = extract_colon_unit (mailpaths, &i))
330 mp = parse_mailpath_spec (mailfile);
333 add_mail_file (mailfile, mp);
338 /* check_mail () is useful for more than just checking mail. Since it has
339 the paranoids dream ability of telling you when someone has read your
340 mail, it can just as easily be used to tell you when someones .profile
341 file has been read, thus letting one know when someone else has logged
342 in. Pretty good, huh? */
344 /* Check for mail in some files. If the modification date of any
345 of the files in MAILPATH has changed since we last did a
346 remember_mail_dates () then mention that the user has mail.
347 Special hack: If the variable MAIL_WARNING is non-zero and the
348 mail file has been accessed since the last time we remembered, then
349 the message "The mail in <mailfile> has been read" is printed. */
353 char *current_mail_file, *message;
354 int i, use_user_notification;
355 char *dollar_underscore, *temp;
358 dollar_underscore = get_string_value ("_");
359 if (dollar_underscore)
360 dollar_underscore = savestring (dollar_underscore);
362 for (i = 0; i < mailfiles_count; i++)
364 current_mail_file = mailfiles[i]->name;
366 if (*current_mail_file == '\0')
369 if (file_mod_date_changed (i))
373 use_user_notification = mailfiles[i]->msg != (char *)NULL;
374 message = mailfiles[i]->msg ? mailfiles[i]->msg : "You have mail in $_";
376 bind_variable ("_", current_mail_file);
378 #define atime mailfiles[i]->access_time
379 #define mtime mailfiles[i]->mod_time
381 /* Have to compute this before the call to update_mail_file, which
382 resets all the information. */
383 file_is_bigger = file_has_grown (i);
385 update_mail_file (i);
387 /* If the user has just run a program which manipulates the
388 mail file, then don't bother explaining that the mail
389 file has been manipulated. Since some systems don't change
390 the access time to be equal to the modification time when
391 the mail in the file is manipulated, check the size also. If
392 the file has not grown, continue. */
393 if ((atime >= mtime) && !file_is_bigger)
396 /* If the mod time is later than the access time and the file
397 has grown, note the fact that this is *new* mail. */
398 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
399 message = "You have new mail in $_";
403 if ((tlist = expand_string (message, Q_DOUBLE_QUOTES)))
405 temp = string_list (tlist);
408 dispose_words (tlist);
414 if (mail_warning && file_access_date_changed (i))
416 update_mail_file (i);
417 printf ("The mail in %s has been read\n", current_mail_file);
421 if (dollar_underscore)
423 bind_variable ("_", dollar_underscore);
424 free (dollar_underscore);
427 unbind_variable ("_");