Added gitignore entries needed to ignore derived objects generated from full build...
[bash.git] / mailcheck.c
blobbd95f0d6dca7c15597f0ece62d9c2a410e781c26
1 /* mailcheck.c -- The check is in the mail... */
3 /* Copyright (C) 1987-2009 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
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
21 #include "config.h"
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 "posixtime.h"
33 #include "bashansi.h"
34 #include "bashintl.h"
36 #include "shell.h"
37 #include "execute_cmd.h"
38 #include "mailcheck.h"
39 #include <tilde/tilde.h>
41 /* Values for flags word in struct _fileinfo */
42 #define MBOX_INITIALIZED 0x01
44 extern time_t shell_start_time;
46 extern int mailstat __P((const char *, struct stat *));
48 typedef struct _fileinfo {
49 char *name;
50 char *msg;
51 time_t access_time;
52 time_t mod_time;
53 off_t file_size;
54 int flags;
55 } FILEINFO;
57 /* The list of remembered mail files. */
58 static FILEINFO **mailfiles = (FILEINFO **)NULL;
60 /* Number of mail files that we have. */
61 static int mailfiles_count;
63 /* The last known time that mail was checked. */
64 static time_t last_time_mail_checked = 0;
66 /* Non-zero means warn if a mail file has been read since last checked. */
67 int mail_warning;
69 static int find_mail_file __P((char *));
70 static void init_mail_file __P((int));
71 static void update_mail_file __P((int));
72 static int add_mail_file __P((char *, char *));
74 static FILEINFO *alloc_mail_file __P((char *, char *));
75 static void dispose_mail_file __P((FILEINFO *));
77 static int file_mod_date_changed __P((int));
78 static int file_access_date_changed __P((int));
79 static int file_has_grown __P((int));
81 static char *parse_mailpath_spec __P((char *));
83 /* Returns non-zero if it is time to check mail. */
84 int
85 time_to_check_mail ()
87 char *temp;
88 time_t now;
89 intmax_t seconds;
91 temp = get_string_value ("MAILCHECK");
93 /* Negative number, or non-numbers (such as empty string) cause no
94 checking to take place. */
95 if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
96 return (0);
98 now = NOW;
99 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
100 time has passed since the last check. */
101 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
104 /* Okay, we have checked the mail. Perhaps I should make this function
105 go away. */
106 void
107 reset_mail_timer ()
109 last_time_mail_checked = NOW;
112 /* Locate a file in the list. Return index of
113 entry, or -1 if not found. */
114 static int
115 find_mail_file (file)
116 char *file;
118 register int i;
120 for (i = 0; i < mailfiles_count; i++)
121 if (STREQ (mailfiles[i]->name, file))
122 return i;
124 return -1;
127 #define RESET_MAIL_FILE(i) \
128 do \
130 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
131 mailfiles[i]->file_size = 0; \
132 mailfiles[i]->flags = 0; \
134 while (0)
136 #define UPDATE_MAIL_FILE(i, finfo) \
137 do \
139 mailfiles[i]->access_time = finfo.st_atime; \
140 mailfiles[i]->mod_time = finfo.st_mtime; \
141 mailfiles[i]->file_size = finfo.st_size; \
142 mailfiles[i]->flags |= MBOX_INITIALIZED; \
144 while (0)
146 static void
147 init_mail_file (i)
148 int i;
150 mailfiles[i]->access_time = mailfiles[i]->mod_time = last_time_mail_checked ? last_time_mail_checked : shell_start_time;
151 mailfiles[i]->file_size = 0;
152 mailfiles[i]->flags = 0;
155 static void
156 update_mail_file (i)
157 int i;
159 char *file;
160 struct stat finfo;
162 file = mailfiles[i]->name;
163 if (mailstat (file, &finfo) == 0)
164 UPDATE_MAIL_FILE (i, finfo);
165 else
166 RESET_MAIL_FILE (i);
169 /* Add this file to the list of remembered files and return its index
170 in the list of mail files. */
171 static int
172 add_mail_file (file, msg)
173 char *file, *msg;
175 struct stat finfo;
176 char *filename;
177 int i;
179 filename = full_pathname (file);
180 i = find_mail_file (filename);
181 if (i >= 0)
183 if (mailstat (filename, &finfo) == 0)
184 UPDATE_MAIL_FILE (i, finfo);
186 free (filename);
187 return i;
190 i = mailfiles_count++;
191 mailfiles = (FILEINFO **)xrealloc
192 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
194 mailfiles[i] = alloc_mail_file (filename, msg);
195 init_mail_file (i);
197 return i;
200 /* Reset the existing mail files access and modification times to zero. */
201 void
202 reset_mail_files ()
204 register int i;
206 for (i = 0; i < mailfiles_count; i++)
207 RESET_MAIL_FILE (i);
210 static FILEINFO *
211 alloc_mail_file (filename, msg)
212 char *filename, *msg;
214 FILEINFO *mf;
216 mf = (FILEINFO *)xmalloc (sizeof (FILEINFO));
217 mf->name = filename;
218 mf->msg = msg ? savestring (msg) : (char *)NULL;
219 mf->flags = 0;
221 return mf;
224 static void
225 dispose_mail_file (mf)
226 FILEINFO *mf;
228 free (mf->name);
229 FREE (mf->msg);
230 free (mf);
233 /* Free the information that we have about the remembered mail files. */
234 void
235 free_mail_files ()
237 register int i;
239 for (i = 0; i < mailfiles_count; i++)
240 dispose_mail_file (mailfiles[i]);
242 if (mailfiles)
243 free (mailfiles);
245 mailfiles_count = 0;
246 mailfiles = (FILEINFO **)NULL;
249 void
250 init_mail_dates ()
252 if (mailfiles == 0)
253 remember_mail_dates ();
256 /* Return non-zero if FILE's mod date has changed and it has not been
257 accessed since modified. If the size has dropped to zero, reset
258 the cached mail file info. */
259 static int
260 file_mod_date_changed (i)
261 int i;
263 time_t mtime;
264 struct stat finfo;
265 char *file;
267 file = mailfiles[i]->name;
268 mtime = mailfiles[i]->mod_time;
270 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
271 return (mtime < finfo.st_mtime);
273 if (finfo.st_size == 0 && mailfiles[i]->file_size > 0)
274 UPDATE_MAIL_FILE (i, finfo);
276 return (0);
279 /* Return non-zero if FILE's access date has changed. */
280 static int
281 file_access_date_changed (i)
282 int i;
284 time_t atime;
285 struct stat finfo;
286 char *file;
288 file = mailfiles[i]->name;
289 atime = mailfiles[i]->access_time;
291 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
292 return (atime < finfo.st_atime);
294 return (0);
297 /* Return non-zero if FILE's size has increased. */
298 static int
299 file_has_grown (i)
300 int i;
302 off_t size;
303 struct stat finfo;
304 char *file;
306 file = mailfiles[i]->name;
307 size = mailfiles[i]->file_size;
309 return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
312 /* Take an element from $MAILPATH and return the portion from
313 the first unquoted `?' or `%' to the end of the string. This is the
314 message to be printed when the file contents change. */
315 static char *
316 parse_mailpath_spec (str)
317 char *str;
319 char *s;
320 int pass_next;
322 for (s = str, pass_next = 0; s && *s; s++)
324 if (pass_next)
326 pass_next = 0;
327 continue;
329 if (*s == '\\')
331 pass_next++;
332 continue;
334 if (*s == '?' || *s == '%')
335 return s;
337 return ((char *)NULL);
340 char *
341 make_default_mailpath ()
343 #if defined (DEFAULT_MAIL_DIRECTORY)
344 char *mp;
346 get_current_user_info ();
347 mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
348 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
349 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
350 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
351 return (mp);
352 #else
353 return ((char *)NULL);
354 #endif
357 /* Remember the dates of the files specified by MAILPATH, or if there is
358 no MAILPATH, by the file specified in MAIL. If neither exists, use a
359 default value, which we randomly concoct from using Unix. */
361 void
362 remember_mail_dates ()
364 char *mailpaths;
365 char *mailfile, *mp;
366 int i = 0;
368 mailpaths = get_string_value ("MAILPATH");
370 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
371 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
373 add_mail_file (mailpaths, (char *)NULL);
374 return;
377 if (mailpaths == 0)
379 mailpaths = make_default_mailpath ();
380 if (mailpaths)
382 add_mail_file (mailpaths, (char *)NULL);
383 free (mailpaths);
385 return;
388 while (mailfile = extract_colon_unit (mailpaths, &i))
390 mp = parse_mailpath_spec (mailfile);
391 if (mp && *mp)
392 *mp++ = '\0';
393 add_mail_file (mailfile, mp);
394 free (mailfile);
398 /* check_mail () is useful for more than just checking mail. Since it has
399 the paranoids dream ability of telling you when someone has read your
400 mail, it can just as easily be used to tell you when someones .profile
401 file has been read, thus letting one know when someone else has logged
402 in. Pretty good, huh? */
404 /* Check for mail in some files. If the modification date of any
405 of the files in MAILPATH has changed since we last did a
406 remember_mail_dates () then mention that the user has mail.
407 Special hack: If the variable MAIL_WARNING is non-zero and the
408 mail file has been accessed since the last time we remembered, then
409 the message "The mail in <mailfile> has been read" is printed. */
410 void
411 check_mail ()
413 char *current_mail_file, *message;
414 int i, use_user_notification;
415 char *dollar_underscore, *temp;
417 dollar_underscore = get_string_value ("_");
418 if (dollar_underscore)
419 dollar_underscore = savestring (dollar_underscore);
421 for (i = 0; i < mailfiles_count; i++)
423 current_mail_file = mailfiles[i]->name;
425 if (*current_mail_file == '\0')
426 continue;
428 if (file_mod_date_changed (i))
430 int file_is_bigger;
432 use_user_notification = mailfiles[i]->msg != (char *)NULL;
433 message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
435 bind_variable ("_", current_mail_file, 0);
437 #define atime mailfiles[i]->access_time
438 #define mtime mailfiles[i]->mod_time
440 /* Have to compute this before the call to update_mail_file, which
441 resets all the information. */
442 file_is_bigger = file_has_grown (i);
444 update_mail_file (i);
446 /* If the user has just run a program which manipulates the
447 mail file, then don't bother explaining that the mail
448 file has been manipulated. Since some systems don't change
449 the access time to be equal to the modification time when
450 the mail in the file is manipulated, check the size also. If
451 the file has not grown, continue. */
452 if ((atime >= mtime) && !file_is_bigger)
453 continue;
455 /* If the mod time is later than the access time and the file
456 has grown, note the fact that this is *new* mail. */
457 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
458 message = _("You have new mail in $_");
459 #undef atime
460 #undef mtime
462 if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
464 puts (temp);
465 free (temp);
467 else
468 putchar ('\n');
471 if (mail_warning && file_access_date_changed (i))
473 update_mail_file (i);
474 printf (_("The mail in %s has been read\n"), current_mail_file);
478 if (dollar_underscore)
480 bind_variable ("_", dollar_underscore, 0);
481 free (dollar_underscore);
483 else
484 unbind_variable ("_");