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 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)
36 #include "execute_cmd.h"
37 #include "mailcheck.h"
38 #include <tilde/tilde.h>
41 #define NOW ((time_t)time ((time_t *)0))
52 /* The list of remembered mail files. */
53 static FILEINFO **mailfiles = (FILEINFO **)NULL;
55 /* Number of mail files that we have. */
56 static int mailfiles_count;
58 /* The last known time that mail was checked. */
59 static int last_time_mail_checked;
61 /* Non-zero means warn if a mail file has been read since last checked. */
64 /* Returns non-zero if it is time to check mail. */
72 temp = get_string_value ("MAILCHECK");
75 /* Skip leading whitespace in MAILCHECK. */
78 while (whitespace (*temp))
81 seconds = atoi (temp);
84 /* Negative number, or non-numbers (such as empty string) cause no
85 checking to take place. */
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));
95 /* Okay, we have checked the mail. Perhaps I should make this function
100 last_time_mail_checked = NOW;
103 /* Locate a file in the list. Return index of
104 entry, or -1 if not found. */
106 find_mail_file (file)
111 for (i = 0; i < mailfiles_count; i++)
112 if (STREQ (mailfiles[i]->name, file))
118 #define RESET_MAIL_FILE(i) \
121 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
122 mailfiles[i]->file_size = 0; \
133 file = mailfiles[i]->name;
134 if (stat (file, &finfo) == 0)
136 mailfiles[i]->access_time = finfo.st_atime;
137 mailfiles[i]->mod_time = finfo.st_mtime;
138 mailfiles[i]->file_size = finfo.st_size;
144 /* Add this file to the list of remembered files and return its index
145 in the list of mail files. */
147 add_mail_file (file, msg)
154 filename = full_pathname (file);
155 i = find_mail_file (filename);
158 if (stat (filename, &finfo) == 0)
160 mailfiles[i]->mod_time = finfo.st_mtime;
161 mailfiles[i]->access_time = finfo.st_atime;
162 mailfiles[i]->file_size = finfo.st_size;
168 i = mailfiles_count++;
169 mailfiles = (FILEINFO **)xrealloc
170 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
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);
179 /* Reset the existing mail files access and modification times to zero. */
185 for (i = 0; i < mailfiles_count; i++)
191 /* Free the information that we have about the remembered mail files. */
197 for (i = 0; i < mailfiles_count; i++)
199 free (mailfiles[i]->name);
200 FREE (mailfiles[i]->msg);
208 mailfiles = (FILEINFO **)NULL;
211 /* Return non-zero if FILE's mod date has changed and it has not been
212 accessed since modified. */
214 file_mod_date_changed (i)
221 file = mailfiles[i]->name;
222 mtime = mailfiles[i]->mod_time;
224 if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
225 return (mtime != finfo.st_mtime);
230 /* Return non-zero if FILE's access date has changed. */
232 file_access_date_changed (i)
239 file = mailfiles[i]->name;
240 atime = mailfiles[i]->access_time;
242 if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
243 return (atime != finfo.st_atime);
248 /* Return non-zero if FILE's size has increased. */
257 file = mailfiles[i]->name;
258 size = mailfiles[i]->file_size;
260 return ((stat (file, &finfo) == 0) && (finfo.st_size > size));
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. */
267 parse_mailpath_spec (str)
273 for (s = str, pass_next = 0; s && *s; s++)
285 if (*s == '?' || *s == '%')
288 return ((char *)NULL);
292 make_default_mailpath ()
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);
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. */
308 remember_mail_dates ()
314 mailpaths = get_string_value ("MAILPATH");
316 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
317 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
319 add_mail_file (mailpaths, (char *)NULL);
325 mailpaths = make_default_mailpath ();
326 add_mail_file (mailpaths, (char *)NULL);
331 while (mailfile = extract_colon_unit (mailpaths, &i))
333 mp = parse_mailpath_spec (mailfile);
336 add_mail_file (mailfile, mp);
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? */
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. */
356 char *current_mail_file, *message;
357 int i, use_user_notification;
358 char *dollar_underscore, *temp;
361 dollar_underscore = get_string_value ("_");
362 if (dollar_underscore)
363 dollar_underscore = savestring (dollar_underscore);
365 for (i = 0; i < mailfiles_count; i++)
367 current_mail_file = mailfiles[i]->name;
369 if (*current_mail_file == '\0')
372 if (file_mod_date_changed (i))
376 use_user_notification = mailfiles[i]->msg != (char *)NULL;
377 message = mailfiles[i]->msg ? mailfiles[i]->msg : "You have mail in $_";
379 bind_variable ("_", current_mail_file);
381 #define atime mailfiles[i]->access_time
382 #define mtime mailfiles[i]->mod_time
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);
388 update_mail_file (i);
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)
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 $_";
406 if ((tlist = expand_string (message, Q_DOUBLE_QUOTES)))
408 temp = string_list (tlist);
411 dispose_words (tlist);
417 if (mail_warning && file_access_date_changed (i))
419 update_mail_file (i);
420 printf ("The mail in %s has been read\n", current_mail_file);
424 if (dollar_underscore)
426 bind_variable ("_", dollar_underscore);
427 free (dollar_underscore);
430 unbind_variable ("_");