]> git.ipfire.org Git - thirdparty/bash.git/blame - mailcheck.c
Imported from ../bash-3.0.tar.gz.
[thirdparty/bash.git] / mailcheck.c
CommitLineData
726f6388
JA
1/* mailcheck.c -- The check is in the mail... */
2
7117c2d2 3/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
726f6388
JA
4
5This file is part of GNU Bash, the Bourne Again SHell.
6
7Bash is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
bb70624e 9Software Foundation; either version 2, or (at your option) any later
726f6388
JA
10version.
11
12Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with Bash; see the file COPYING. If not, write to the Free Software
bb70624e 19Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
726f6388 20
ccc6cda3
JA
21#include "config.h"
22
726f6388
JA
23#include <stdio.h>
24#include "bashtypes.h"
25#include "posixstat.h"
cce855bc
JA
26#ifndef _MINIX
27# include <sys/param.h>
28#endif
ccc6cda3
JA
29#if defined (HAVE_UNISTD_H)
30# include <unistd.h>
31#endif
f73dda09 32#include "posixtime.h"
726f6388 33#include "bashansi.h"
b80f6443 34#include "bashintl.h"
ccc6cda3 35
726f6388 36#include "shell.h"
726f6388 37#include "execute_cmd.h"
ccc6cda3 38#include "mailcheck.h"
726f6388
JA
39#include <tilde/tilde.h>
40
f73dda09 41extern int mailstat __P((const char *, struct stat *));
726f6388
JA
42
43typedef struct {
44 char *name;
ccc6cda3 45 char *msg;
726f6388
JA
46 time_t access_time;
47 time_t mod_time;
cce855bc 48 off_t file_size;
726f6388
JA
49} FILEINFO;
50
51/* The list of remembered mail files. */
ccc6cda3 52static FILEINFO **mailfiles = (FILEINFO **)NULL;
726f6388
JA
53
54/* Number of mail files that we have. */
ccc6cda3 55static int mailfiles_count;
726f6388
JA
56
57/* The last known time that mail was checked. */
f73dda09 58static time_t last_time_mail_checked;
ccc6cda3
JA
59
60/* Non-zero means warn if a mail file has been read since last checked. */
61int mail_warning;
726f6388 62
f73dda09
JA
63static int find_mail_file __P((char *));
64static void update_mail_file __P((int));
65static int add_mail_file __P((char *, char *));
66
67static int file_mod_date_changed __P((int));
68static int file_access_date_changed __P((int));
69static int file_has_grown __P((int));
70
71static char *parse_mailpath_spec __P((char *));
72
726f6388
JA
73/* Returns non-zero if it is time to check mail. */
74int
75time_to_check_mail ()
76{
ccc6cda3
JA
77 char *temp;
78 time_t now;
7117c2d2 79 intmax_t seconds;
ccc6cda3
JA
80
81 temp = get_string_value ("MAILCHECK");
726f6388
JA
82
83 /* Negative number, or non-numbers (such as empty string) cause no
84 checking to take place. */
f73dda09 85 if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
726f6388
JA
86 return (0);
87
ccc6cda3 88 now = NOW;
726f6388
JA
89 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
90 time has passed since the last check. */
ccc6cda3 91 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
726f6388
JA
92}
93
94/* Okay, we have checked the mail. Perhaps I should make this function
95 go away. */
96void
97reset_mail_timer ()
98{
99 last_time_mail_checked = NOW;
100}
101
102/* Locate a file in the list. Return index of
103 entry, or -1 if not found. */
104static int
105find_mail_file (file)
106 char *file;
107{
108 register int i;
109
110 for (i = 0; i < mailfiles_count; i++)
ccc6cda3 111 if (STREQ (mailfiles[i]->name, file))
726f6388
JA
112 return i;
113
114 return -1;
115}
116
ccc6cda3
JA
117#define RESET_MAIL_FILE(i) \
118 do \
119 { \
120 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
cce855bc 121 mailfiles[i]->file_size = 0; \
ccc6cda3
JA
122 } \
123 while (0)
124
125static void
126update_mail_file (i)
127 int i;
128{
129 char *file;
130 struct stat finfo;
131
132 file = mailfiles[i]->name;
f73dda09 133 if (mailstat (file, &finfo) == 0)
ccc6cda3
JA
134 {
135 mailfiles[i]->access_time = finfo.st_atime;
136 mailfiles[i]->mod_time = finfo.st_mtime;
137 mailfiles[i]->file_size = finfo.st_size;
138 }
139 else
140 RESET_MAIL_FILE (i);
141}
142
726f6388
JA
143/* Add this file to the list of remembered files and return its index
144 in the list of mail files. */
145static int
ccc6cda3
JA
146add_mail_file (file, msg)
147 char *file, *msg;
726f6388
JA
148{
149 struct stat finfo;
150 char *filename;
151 int i;
152
153 filename = full_pathname (file);
e8ce775d 154 i = find_mail_file (filename);
ccc6cda3 155 if (i >= 0)
726f6388 156 {
f73dda09 157 if (mailstat (filename, &finfo) == 0)
726f6388
JA
158 {
159 mailfiles[i]->mod_time = finfo.st_mtime;
160 mailfiles[i]->access_time = finfo.st_atime;
cce855bc 161 mailfiles[i]->file_size = finfo.st_size;
726f6388
JA
162 }
163 free (filename);
164 return i;
165 }
166
167 i = mailfiles_count++;
168 mailfiles = (FILEINFO **)xrealloc
169 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
170
171 mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
172 mailfiles[i]->name = filename;
ccc6cda3
JA
173 mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
174 update_mail_file (i);
726f6388
JA
175 return i;
176}
177
178/* Reset the existing mail files access and modification times to zero. */
179void
180reset_mail_files ()
181{
182 register int i;
183
184 for (i = 0; i < mailfiles_count; i++)
185 {
ccc6cda3 186 RESET_MAIL_FILE (i);
726f6388
JA
187 }
188}
189
190/* Free the information that we have about the remembered mail files. */
191void
192free_mail_files ()
193{
194 register int i;
195
196 for (i = 0; i < mailfiles_count; i++)
197 {
198 free (mailfiles[i]->name);
ccc6cda3 199 FREE (mailfiles[i]->msg);
726f6388
JA
200 free (mailfiles[i]);
201 }
202
203 if (mailfiles)
204 free (mailfiles);
205
206 mailfiles_count = 0;
207 mailfiles = (FILEINFO **)NULL;
208}
209
210/* Return non-zero if FILE's mod date has changed and it has not been
211 accessed since modified. */
212static int
ccc6cda3
JA
213file_mod_date_changed (i)
214 int i;
726f6388 215{
ccc6cda3 216 time_t mtime;
726f6388 217 struct stat finfo;
ccc6cda3 218 char *file;
726f6388 219
ccc6cda3
JA
220 file = mailfiles[i]->name;
221 mtime = mailfiles[i]->mod_time;
726f6388 222
f73dda09 223 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
ccc6cda3 224 return (mtime != finfo.st_mtime);
726f6388
JA
225
226 return (0);
227}
228
229/* Return non-zero if FILE's access date has changed. */
230static int
ccc6cda3
JA
231file_access_date_changed (i)
232 int i;
726f6388 233{
ccc6cda3 234 time_t atime;
726f6388 235 struct stat finfo;
ccc6cda3 236 char *file;
726f6388 237
ccc6cda3
JA
238 file = mailfiles[i]->name;
239 atime = mailfiles[i]->access_time;
726f6388 240
f73dda09 241 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
ccc6cda3 242 return (atime != finfo.st_atime);
726f6388
JA
243
244 return (0);
245}
246
247/* Return non-zero if FILE's size has increased. */
248static int
ccc6cda3
JA
249file_has_grown (i)
250 int i;
726f6388 251{
cce855bc 252 off_t size;
726f6388 253 struct stat finfo;
ccc6cda3 254 char *file;
726f6388 255
ccc6cda3
JA
256 file = mailfiles[i]->name;
257 size = mailfiles[i]->file_size;
726f6388 258
f73dda09 259 return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
726f6388
JA
260}
261
726f6388
JA
262/* Take an element from $MAILPATH and return the portion from
263 the first unquoted `?' or `%' to the end of the string. This is the
264 message to be printed when the file contents change. */
265static char *
266parse_mailpath_spec (str)
267 char *str;
268{
269 char *s;
270 int pass_next;
271
272 for (s = str, pass_next = 0; s && *s; s++)
273 {
274 if (pass_next)
275 {
276 pass_next = 0;
277 continue;
278 }
279 if (*s == '\\')
280 {
281 pass_next++;
282 continue;
283 }
284 if (*s == '?' || *s == '%')
28ef6c31 285 return s;
726f6388
JA
286 }
287 return ((char *)NULL);
288}
ccc6cda3
JA
289
290char *
291make_default_mailpath ()
292{
b80f6443 293#if defined (DEFAULT_MAIL_DIRECTORY)
ccc6cda3
JA
294 char *mp;
295
cce855bc 296 get_current_user_info ();
f73dda09 297 mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
ccc6cda3 298 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
d166f048
JA
299 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
300 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
ccc6cda3 301 return (mp);
b80f6443
JA
302#else
303 return ((char *)NULL);
304#endif
ccc6cda3
JA
305}
306
726f6388
JA
307/* Remember the dates of the files specified by MAILPATH, or if there is
308 no MAILPATH, by the file specified in MAIL. If neither exists, use a
309 default value, which we randomly concoct from using Unix. */
310void
311remember_mail_dates ()
312{
313 char *mailpaths;
314 char *mailfile, *mp;
315 int i = 0;
316
ccc6cda3
JA
317 mailpaths = get_string_value ("MAILPATH");
318
319 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
320 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
321 {
322 add_mail_file (mailpaths, (char *)NULL);
323 return;
324 }
325
326 if (mailpaths == 0)
327 {
328 mailpaths = make_default_mailpath ();
b80f6443
JA
329 if (mailpaths)
330 {
331 add_mail_file (mailpaths, (char *)NULL);
332 free (mailpaths);
333 }
ccc6cda3
JA
334 return;
335 }
336
726f6388
JA
337 while (mailfile = extract_colon_unit (mailpaths, &i))
338 {
339 mp = parse_mailpath_spec (mailfile);
340 if (mp && *mp)
ccc6cda3
JA
341 *mp++ = '\0';
342 add_mail_file (mailfile, mp);
726f6388
JA
343 free (mailfile);
344 }
726f6388
JA
345}
346
347/* check_mail () is useful for more than just checking mail. Since it has
348 the paranoids dream ability of telling you when someone has read your
349 mail, it can just as easily be used to tell you when someones .profile
350 file has been read, thus letting one know when someone else has logged
351 in. Pretty good, huh? */
352
353/* Check for mail in some files. If the modification date of any
354 of the files in MAILPATH has changed since we last did a
355 remember_mail_dates () then mention that the user has mail.
ccc6cda3 356 Special hack: If the variable MAIL_WARNING is non-zero and the
726f6388
JA
357 mail file has been accessed since the last time we remembered, then
358 the message "The mail in <mailfile> has been read" is printed. */
359void
360check_mail ()
361{
ccc6cda3
JA
362 char *current_mail_file, *message;
363 int i, use_user_notification;
364 char *dollar_underscore, *temp;
726f6388
JA
365
366 dollar_underscore = get_string_value ("_");
726f6388
JA
367 if (dollar_underscore)
368 dollar_underscore = savestring (dollar_underscore);
369
ccc6cda3 370 for (i = 0; i < mailfiles_count; i++)
726f6388 371 {
ccc6cda3 372 current_mail_file = mailfiles[i]->name;
726f6388 373
ccc6cda3
JA
374 if (*current_mail_file == '\0')
375 continue;
726f6388 376
ccc6cda3 377 if (file_mod_date_changed (i))
726f6388 378 {
ccc6cda3
JA
379 int file_is_bigger;
380
381 use_user_notification = mailfiles[i]->msg != (char *)NULL;
b80f6443 382 message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
726f6388 383
726f6388 384 bind_variable ("_", current_mail_file);
ccc6cda3 385
726f6388
JA
386#define atime mailfiles[i]->access_time
387#define mtime mailfiles[i]->mod_time
388
ccc6cda3 389 /* Have to compute this before the call to update_mail_file, which
726f6388 390 resets all the information. */
ccc6cda3 391 file_is_bigger = file_has_grown (i);
726f6388 392
ccc6cda3 393 update_mail_file (i);
726f6388
JA
394
395 /* If the user has just run a program which manipulates the
396 mail file, then don't bother explaining that the mail
397 file has been manipulated. Since some systems don't change
398 the access time to be equal to the modification time when
399 the mail in the file is manipulated, check the size also. If
400 the file has not grown, continue. */
b80f6443 401 if ((atime >= mtime) && !file_is_bigger)
ccc6cda3 402 continue;
726f6388
JA
403
404 /* If the mod time is later than the access time and the file
405 has grown, note the fact that this is *new* mail. */
ccc6cda3 406 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
b80f6443 407 message = _("You have new mail in $_");
726f6388
JA
408#undef atime
409#undef mtime
410
f73dda09 411 if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
726f6388 412 {
ccc6cda3
JA
413 puts (temp);
414 free (temp);
726f6388
JA
415 }
416 else
ccc6cda3 417 putchar ('\n');
726f6388
JA
418 }
419
ccc6cda3 420 if (mail_warning && file_access_date_changed (i))
726f6388 421 {
ccc6cda3 422 update_mail_file (i);
b80f6443 423 printf (_("The mail in %s has been read\n"), current_mail_file);
726f6388 424 }
726f6388 425 }
726f6388
JA
426
427 if (dollar_underscore)
428 {
429 bind_variable ("_", dollar_underscore);
430 free (dollar_underscore);
431 }
432 else
433 unbind_variable ("_");
434}