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