source: vendor/bash/3.1/mailcheck.c@ 3494

Last change on this file since 3494 was 3228, checked in by bird, 18 years ago

bash 3.1

File size: 10.5 KB
Line 
1/* mailcheck.c -- The check is in the mail... */
2
3/* Copyright (C) 1987-2004 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
9Software Foundation; either version 2, or (at your option) any later
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
19Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21#include "config.h"
22
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"
35
36#include "shell.h"
37#include "execute_cmd.h"
38#include "mailcheck.h"
39#include <tilde/tilde.h>
40
41extern int mailstat __P((const char *, struct stat *));
42
43typedef struct {
44 char *name;
45 char *msg;
46 time_t access_time;
47 time_t mod_time;
48 off_t file_size;
49} FILEINFO;
50
51/* The list of remembered mail files. */
52static FILEINFO **mailfiles = (FILEINFO **)NULL;
53
54/* Number of mail files that we have. */
55static int mailfiles_count;
56
57/* The last known time that mail was checked. */
58static time_t last_time_mail_checked;
59
60/* Non-zero means warn if a mail file has been read since last checked. */
61int mail_warning;
62
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
73/* Returns non-zero if it is time to check mail. */
74int
75time_to_check_mail ()
76{
77 char *temp;
78 time_t now;
79 intmax_t seconds;
80
81 temp = get_string_value ("MAILCHECK");
82
83 /* Negative number, or non-numbers (such as empty string) cause no
84 checking to take place. */
85 if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
86 return (0);
87
88 now = NOW;
89 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
90 time has passed since the last check. */
91 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
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++)
111 if (STREQ (mailfiles[i]->name, file))
112 return i;
113
114 return -1;
115}
116
117#define RESET_MAIL_FILE(i) \
118 do \
119 { \
120 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
121 mailfiles[i]->file_size = 0; \
122 } \
123 while (0)
124
125#define UPDATE_MAIL_FILE(i, finfo) \
126 do \
127 { \
128 mailfiles[i]->access_time = finfo.st_atime; \
129 mailfiles[i]->mod_time = finfo.st_mtime; \
130 mailfiles[i]->file_size = finfo.st_size; \
131 } \
132 while (0)
133
134static void
135update_mail_file (i)
136 int i;
137{
138 char *file;
139 struct stat finfo;
140
141 file = mailfiles[i]->name;
142 if (mailstat (file, &finfo) == 0)
143 UPDATE_MAIL_FILE (i, finfo);
144 else
145 RESET_MAIL_FILE (i);
146}
147
148/* Add this file to the list of remembered files and return its index
149 in the list of mail files. */
150static int
151add_mail_file (file, msg)
152 char *file, *msg;
153{
154 struct stat finfo;
155 char *filename;
156 int i;
157
158 filename = full_pathname (file);
159 i = find_mail_file (filename);
160 if (i >= 0)
161 {
162 if (mailstat (filename, &finfo) == 0)
163 UPDATE_MAIL_FILE (i, finfo);
164
165 free (filename);
166 return i;
167 }
168
169 i = mailfiles_count++;
170 mailfiles = (FILEINFO **)xrealloc
171 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
172
173 mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
174 mailfiles[i]->name = filename;
175 mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
176 update_mail_file (i);
177 return i;
178}
179
180/* Reset the existing mail files access and modification times to zero. */
181void
182reset_mail_files ()
183{
184 register int i;
185
186 for (i = 0; i < mailfiles_count; i++)
187 RESET_MAIL_FILE (i);
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);
199 FREE (mailfiles[i]->msg);
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. If the size has dropped to zero, reset
212 the cached mail file info. */
213static int
214file_mod_date_changed (i)
215 int i;
216{
217 time_t mtime;
218 struct stat finfo;
219 char *file;
220
221 file = mailfiles[i]->name;
222 mtime = mailfiles[i]->mod_time;
223
224 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
225 return (mtime != finfo.st_mtime);
226
227 if (finfo.st_size == 0 && mailfiles[i]->file_size > 0)
228 UPDATE_MAIL_FILE (i, finfo);
229
230 return (0);
231}
232
233/* Return non-zero if FILE's access date has changed. */
234static int
235file_access_date_changed (i)
236 int i;
237{
238 time_t atime;
239 struct stat finfo;
240 char *file;
241
242 file = mailfiles[i]->name;
243 atime = mailfiles[i]->access_time;
244
245 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
246 return (atime != finfo.st_atime);
247
248 return (0);
249}
250
251/* Return non-zero if FILE's size has increased. */
252static int
253file_has_grown (i)
254 int i;
255{
256 off_t size;
257 struct stat finfo;
258 char *file;
259
260 file = mailfiles[i]->name;
261 size = mailfiles[i]->file_size;
262
263 return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
264}
265
266/* Take an element from $MAILPATH and return the portion from
267 the first unquoted `?' or `%' to the end of the string. This is the
268 message to be printed when the file contents change. */
269static char *
270parse_mailpath_spec (str)
271 char *str;
272{
273 char *s;
274 int pass_next;
275
276 for (s = str, pass_next = 0; s && *s; s++)
277 {
278 if (pass_next)
279 {
280 pass_next = 0;
281 continue;
282 }
283 if (*s == '\\')
284 {
285 pass_next++;
286 continue;
287 }
288 if (*s == '?' || *s == '%')
289 return s;
290 }
291 return ((char *)NULL);
292}
293
294char *
295make_default_mailpath ()
296{
297#if defined (DEFAULT_MAIL_DIRECTORY)
298 char *mp;
299
300 get_current_user_info ();
301 mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
302 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
303 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
304 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
305 return (mp);
306#else
307 return ((char *)NULL);
308#endif
309}
310
311/* Remember the dates of the files specified by MAILPATH, or if there is
312 no MAILPATH, by the file specified in MAIL. If neither exists, use a
313 default value, which we randomly concoct from using Unix. */
314void
315remember_mail_dates ()
316{
317 char *mailpaths;
318 char *mailfile, *mp;
319 int i = 0;
320
321 mailpaths = get_string_value ("MAILPATH");
322
323 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
324 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
325 {
326 add_mail_file (mailpaths, (char *)NULL);
327 return;
328 }
329
330 if (mailpaths == 0)
331 {
332 mailpaths = make_default_mailpath ();
333 if (mailpaths)
334 {
335 add_mail_file (mailpaths, (char *)NULL);
336 free (mailpaths);
337 }
338 return;
339 }
340
341 while (mailfile = extract_colon_unit (mailpaths, &i))
342 {
343 mp = parse_mailpath_spec (mailfile);
344 if (mp && *mp)
345 *mp++ = '\0';
346 add_mail_file (mailfile, mp);
347 free (mailfile);
348 }
349}
350
351/* check_mail () is useful for more than just checking mail. Since it has
352 the paranoids dream ability of telling you when someone has read your
353 mail, it can just as easily be used to tell you when someones .profile
354 file has been read, thus letting one know when someone else has logged
355 in. Pretty good, huh? */
356
357/* Check for mail in some files. If the modification date of any
358 of the files in MAILPATH has changed since we last did a
359 remember_mail_dates () then mention that the user has mail.
360 Special hack: If the variable MAIL_WARNING is non-zero and the
361 mail file has been accessed since the last time we remembered, then
362 the message "The mail in <mailfile> has been read" is printed. */
363void
364check_mail ()
365{
366 char *current_mail_file, *message;
367 int i, use_user_notification;
368 char *dollar_underscore, *temp;
369
370 dollar_underscore = get_string_value ("_");
371 if (dollar_underscore)
372 dollar_underscore = savestring (dollar_underscore);
373
374 for (i = 0; i < mailfiles_count; i++)
375 {
376 current_mail_file = mailfiles[i]->name;
377
378 if (*current_mail_file == '\0')
379 continue;
380
381 if (file_mod_date_changed (i))
382 {
383 int file_is_bigger;
384
385 use_user_notification = mailfiles[i]->msg != (char *)NULL;
386 message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
387
388 bind_variable ("_", current_mail_file, 0);
389
390#define atime mailfiles[i]->access_time
391#define mtime mailfiles[i]->mod_time
392
393 /* Have to compute this before the call to update_mail_file, which
394 resets all the information. */
395 file_is_bigger = file_has_grown (i);
396
397 update_mail_file (i);
398
399 /* If the user has just run a program which manipulates the
400 mail file, then don't bother explaining that the mail
401 file has been manipulated. Since some systems don't change
402 the access time to be equal to the modification time when
403 the mail in the file is manipulated, check the size also. If
404 the file has not grown, continue. */
405 if ((atime >= mtime) && !file_is_bigger)
406 continue;
407
408 /* If the mod time is later than the access time and the file
409 has grown, note the fact that this is *new* mail. */
410 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
411 message = _("You have new mail in $_");
412#undef atime
413#undef mtime
414
415 if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
416 {
417 puts (temp);
418 free (temp);
419 }
420 else
421 putchar ('\n');
422 }
423
424 if (mail_warning && file_access_date_changed (i))
425 {
426 update_mail_file (i);
427 printf (_("The mail in %s has been read\n"), current_mail_file);
428 }
429 }
430
431 if (dollar_underscore)
432 {
433 bind_variable ("_", dollar_underscore, 0);
434 free (dollar_underscore);
435 }
436 else
437 unbind_variable ("_");
438}
Note: See TracBrowser for help on using the repository browser.