1 /* mailcheck.c -- The check is in the mail... */
3 /* Copyright (C) 1987-2002 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
24 #include "bashtypes.h"
25 #include "posixstat.h"
27 # include <sys/param.h>
29 #if defined (HAVE_UNISTD_H)
32 #include "posixtime.h"
37 #include "execute_cmd.h"
38 #include "mailcheck.h"
39 #include <tilde/tilde.h>
41 extern int mailstat __P((const char *, struct stat *));
51 /* The list of remembered mail files. */
52 static FILEINFO **mailfiles = (FILEINFO **)NULL;
54 /* Number of mail files that we have. */
55 static int mailfiles_count;
57 /* The last known time that mail was checked. */
58 static time_t last_time_mail_checked;
60 /* Non-zero means warn if a mail file has been read since last checked. */
63 static int find_mail_file __P((char *));
64 static void update_mail_file __P((int));
65 static int add_mail_file __P((char *, char *));
67 static int file_mod_date_changed __P((int));
68 static int file_access_date_changed __P((int));
69 static int file_has_grown __P((int));
71 static char *parse_mailpath_spec __P((char *));
73 /* Returns non-zero if it is time to check mail. */
81 temp = get_string_value ("MAILCHECK");
83 /* Negative number, or non-numbers (such as empty string) cause no
84 checking to take place. */
85 if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
89 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
90 time has passed since the last check. */
91 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
94 /* Okay, we have checked the mail. Perhaps I should make this function
99 last_time_mail_checked = NOW;
102 /* Locate a file in the list. Return index of
103 entry, or -1 if not found. */
105 find_mail_file (file)
110 for (i = 0; i < mailfiles_count; i++)
111 if (STREQ (mailfiles[i]->name, file))
117 #define RESET_MAIL_FILE(i) \
120 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
121 mailfiles[i]->file_size = 0; \
125 #define UPDATE_MAIL_FILE(i, finfo) \
128 mailfiles[i]->access_time = finfo.st_atime; \
129 mailfiles[i]->mod_time = finfo.st_mtime; \
130 mailfiles[i]->file_size = finfo.st_size; \
141 file = mailfiles[i]->name;
142 if (mailstat (file, &finfo) == 0)
143 UPDATE_MAIL_FILE (i, finfo);
148 /* Add this file to the list of remembered files and return its index
149 in the list of mail files. */
151 add_mail_file (file, msg)
158 filename = full_pathname (file);
159 i = find_mail_file (filename);
162 if (mailstat (filename, &finfo) == 0)
163 UPDATE_MAIL_FILE (i, finfo);
169 i = mailfiles_count++;
170 mailfiles = (FILEINFO **)xrealloc
171 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
173 mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
174 mailfiles[i]->name = filename;
175 mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
176 update_mail_file (i);
180 /* Reset the existing mail files access and modification times to zero. */
186 for (i = 0; i < mailfiles_count; i++)
190 /* Free the information that we have about the remembered mail files. */
196 for (i = 0; i < mailfiles_count; i++)
198 free (mailfiles[i]->name);
199 FREE (mailfiles[i]->msg);
207 mailfiles = (FILEINFO **)NULL;
210 /* Return non-zero if FILE's mod date has changed and it has not been
211 accessed since modified. If the size has dropped to zero, reset
212 the cached mail file info. */
214 file_mod_date_changed (i)
221 file = mailfiles[i]->name;
222 mtime = mailfiles[i]->mod_time;
224 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
225 return (mtime != finfo.st_mtime);
227 if (finfo.st_size == 0 && mailfiles[i]->file_size > 0)
228 UPDATE_MAIL_FILE (i, finfo);
233 /* Return non-zero if FILE's access date has changed. */
235 file_access_date_changed (i)
242 file = mailfiles[i]->name;
243 atime = mailfiles[i]->access_time;
245 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
246 return (atime != finfo.st_atime);
251 /* Return non-zero if FILE's size has increased. */
260 file = mailfiles[i]->name;
261 size = mailfiles[i]->file_size;
263 return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
266 /* Take an element from $MAILPATH and return the portion from
267 the first unquoted `?' or `%' to the end of the string. This is the
268 message to be printed when the file contents change. */
270 parse_mailpath_spec (str)
276 for (s = str, pass_next = 0; s && *s; s++)
288 if (*s == '?' || *s == '%')
291 return ((char *)NULL);
295 make_default_mailpath ()
297 #if defined (DEFAULT_MAIL_DIRECTORY)
300 get_current_user_info ();
301 mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
302 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
303 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
304 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
307 return ((char *)NULL);
311 /* Remember the dates of the files specified by MAILPATH, or if there is
312 no MAILPATH, by the file specified in MAIL. If neither exists, use a
313 default value, which we randomly concoct from using Unix. */
315 remember_mail_dates ()
321 mailpaths = get_string_value ("MAILPATH");
323 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
324 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
326 add_mail_file (mailpaths, (char *)NULL);
332 mailpaths = make_default_mailpath ();
335 add_mail_file (mailpaths, (char *)NULL);
341 while (mailfile = extract_colon_unit (mailpaths, &i))
343 mp = parse_mailpath_spec (mailfile);
346 add_mail_file (mailfile, mp);
351 /* check_mail () is useful for more than just checking mail. Since it has
352 the paranoids dream ability of telling you when someone has read your
353 mail, it can just as easily be used to tell you when someones .profile
354 file has been read, thus letting one know when someone else has logged
355 in. Pretty good, huh? */
357 /* Check for mail in some files. If the modification date of any
358 of the files in MAILPATH has changed since we last did a
359 remember_mail_dates () then mention that the user has mail.
360 Special hack: If the variable MAIL_WARNING is non-zero and the
361 mail file has been accessed since the last time we remembered, then
362 the message "The mail in <mailfile> has been read" is printed. */
366 char *current_mail_file, *message;
367 int i, use_user_notification;
368 char *dollar_underscore, *temp;
370 dollar_underscore = get_string_value ("_");
371 if (dollar_underscore)
372 dollar_underscore = savestring (dollar_underscore);
374 for (i = 0; i < mailfiles_count; i++)
376 current_mail_file = mailfiles[i]->name;
378 if (*current_mail_file == '\0')
381 if (file_mod_date_changed (i))
385 use_user_notification = mailfiles[i]->msg != (char *)NULL;
386 message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
388 bind_variable ("_", current_mail_file);
390 #define atime mailfiles[i]->access_time
391 #define mtime mailfiles[i]->mod_time
393 /* Have to compute this before the call to update_mail_file, which
394 resets all the information. */
395 file_is_bigger = file_has_grown (i);
397 update_mail_file (i);
399 /* If the user has just run a program which manipulates the
400 mail file, then don't bother explaining that the mail
401 file has been manipulated. Since some systems don't change
402 the access time to be equal to the modification time when
403 the mail in the file is manipulated, check the size also. If
404 the file has not grown, continue. */
405 if ((atime >= mtime) && !file_is_bigger)
408 /* If the mod time is later than the access time and the file
409 has grown, note the fact that this is *new* mail. */
410 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
411 message = _("You have new mail in $_");
415 if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
424 if (mail_warning && file_access_date_changed (i))
426 update_mail_file (i);
427 printf (_("The mail in %s has been read\n"), current_mail_file);
431 if (dollar_underscore)
433 bind_variable ("_", dollar_underscore);
434 free (dollar_underscore);
437 unbind_variable ("_");