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