Imported from ../bash-2.01.1.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 (filename);
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 (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
295   strcpy (mp, DEFAULT_MAIL_DIRECTORY);
296   mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
297   strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
298   return (mp);
299 }
300
301 /* Remember the dates of the files specified by MAILPATH, or if there is
302    no MAILPATH, by the file specified in MAIL.  If neither exists, use a
303    default value, which we randomly concoct from using Unix. */
304 void
305 remember_mail_dates ()
306 {
307   char *mailpaths;
308   char *mailfile, *mp;
309   int i = 0;
310
311   mailpaths = get_string_value ("MAILPATH");
312
313   /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
314   if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
315     {
316       add_mail_file (mailpaths, (char *)NULL);
317       return;
318     }
319
320   if (mailpaths == 0)
321     {
322       mailpaths = make_default_mailpath ();
323       add_mail_file (mailpaths, (char *)NULL);
324       free (mailpaths);
325       return;
326     }
327
328   while (mailfile = extract_colon_unit (mailpaths, &i))
329     {
330       mp = parse_mailpath_spec (mailfile);
331       if (mp && *mp)
332         *mp++ = '\0';
333       add_mail_file (mailfile, mp);
334       free (mailfile);
335     }
336 }
337
338 /* check_mail () is useful for more than just checking mail.  Since it has
339    the paranoids dream ability of telling you when someone has read your
340    mail, it can just as easily be used to tell you when someones .profile
341    file has been read, thus letting one know when someone else has logged
342    in.  Pretty good, huh? */
343
344 /* Check for mail in some files.  If the modification date of any
345    of the files in MAILPATH has changed since we last did a
346    remember_mail_dates () then mention that the user has mail.
347    Special hack:  If the variable MAIL_WARNING is non-zero and the
348    mail file has been accessed since the last time we remembered, then
349    the message "The mail in <mailfile> has been read" is printed. */
350 void
351 check_mail ()
352 {
353   char *current_mail_file, *message;
354   int i, use_user_notification;
355   char *dollar_underscore, *temp;
356   WORD_LIST *tlist;
357
358   dollar_underscore = get_string_value ("_");
359   if (dollar_underscore)
360     dollar_underscore = savestring (dollar_underscore);
361
362   for (i = 0; i < mailfiles_count; i++)
363     {
364       current_mail_file = mailfiles[i]->name;
365
366       if (*current_mail_file == '\0')
367         continue;
368
369       if (file_mod_date_changed (i))
370         {
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 $_";
375
376           bind_variable ("_", current_mail_file);
377
378 #define atime mailfiles[i]->access_time
379 #define mtime mailfiles[i]->mod_time
380
381           /* Have to compute this before the call to update_mail_file, which
382              resets all the information. */
383           file_is_bigger = file_has_grown (i);
384
385           update_mail_file (i);
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. */
393           if ((atime >= mtime) && !file_is_bigger)
394             continue;
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. */
398           if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
399             message = "You have new mail in $_";
400 #undef atime
401 #undef mtime
402
403           if ((tlist = expand_string (message, Q_DOUBLE_QUOTES)))
404             {
405               temp = string_list (tlist);
406               puts (temp);
407               free (temp);
408               dispose_words (tlist);
409             }
410           else
411             putchar ('\n');
412         }
413
414       if (mail_warning && file_access_date_changed (i))
415         {
416           update_mail_file (i);
417           printf ("The mail in %s has been read\n", current_mail_file);
418         }
419     }
420
421   if (dollar_underscore)
422     {
423       bind_variable ("_", dollar_underscore);
424       free (dollar_underscore);
425     }
426   else
427     unbind_variable ("_");
428 }