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