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