a4d36701bdbcd73dc57d4f0725d9daaeee272f98
[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 2, 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, 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 "bashansi.h"
33
34 #include "shell.h"
35 #include "maxpath.h"
36 #include "execute_cmd.h"
37 #include "mailcheck.h"
38 #include <tilde/tilde.h>
39
40 #ifndef NOW
41 #define NOW ((time_t)time ((time_t *)0))
42 #endif
43
44 typedef struct {
45   char *name;
46   char *msg;
47   time_t access_time;
48   time_t mod_time;
49   off_t file_size;
50 } FILEINFO;
51
52 /* The list of remembered mail files. */
53 static FILEINFO **mailfiles = (FILEINFO **)NULL;
54
55 /* Number of mail files that we have. */
56 static int mailfiles_count;
57
58 /* The last known time that mail was checked. */
59 static int last_time_mail_checked;
60
61 /* Non-zero means warn if a mail file has been read since last checked. */
62 int mail_warning;
63
64 /* Returns non-zero if it is time to check mail. */
65 int
66 time_to_check_mail ()
67 {
68   char *temp;
69   time_t now;
70   long seconds;
71
72   temp = get_string_value ("MAILCHECK");
73   seconds = -1L;
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
89   now = NOW;
90   /* Time to check if MAILCHECK is explicitly set to zero, or if enough
91      time has passed since the last check. */
92   return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
93 }
94
95 /* Okay, we have checked the mail.  Perhaps I should make this function
96    go away. */
97 void
98 reset_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. */
105 static int
106 find_mail_file (file)
107      char *file;
108 {
109   register int i;
110
111   for (i = 0; i < mailfiles_count; i++)
112     if (STREQ (mailfiles[i]->name, file))
113       return i;
114
115   return -1;
116 }
117
118 #define RESET_MAIL_FILE(i) \
119   do \
120     { \
121       mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
122       mailfiles[i]->file_size = 0; \
123     } \
124   while (0)
125
126 static void
127 update_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
144 /* Add this file to the list of remembered files and return its index
145    in the list of mail files. */
146 static int
147 add_mail_file (file, msg)
148      char *file, *msg;
149 {
150   struct stat finfo;
151   char *filename;
152   int i;
153
154   filename = full_pathname (file);
155   i = find_mail_file (filename);
156   if (i >= 0)
157     {
158       if (stat (filename, &finfo) == 0)
159         {
160           mailfiles[i]->mod_time = finfo.st_mtime;
161           mailfiles[i]->access_time = finfo.st_atime;
162           mailfiles[i]->file_size = finfo.st_size;
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;
174   mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
175   update_mail_file (i);
176   return i;
177 }
178
179 /* Reset the existing mail files access and modification times to zero. */
180 void
181 reset_mail_files ()
182 {
183   register int i;
184
185   for (i = 0; i < mailfiles_count; i++)
186     {
187       RESET_MAIL_FILE (i);
188     }
189 }
190
191 /* Free the information that we have about the remembered mail files. */
192 void
193 free_mail_files ()
194 {
195   register int i;
196
197   for (i = 0; i < mailfiles_count; i++)
198     {
199       free (mailfiles[i]->name);
200       FREE (mailfiles[i]->msg);
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. */
213 static int
214 file_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 ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
225     return (mtime != finfo.st_mtime);
226
227   return (0);
228 }
229
230 /* Return non-zero if FILE's access date has changed. */
231 static int
232 file_access_date_changed (i)
233      int i;
234 {
235   time_t atime;
236   struct stat finfo;
237   char *file;
238
239   file = mailfiles[i]->name;
240   atime = mailfiles[i]->access_time;
241
242   if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
243     return (atime != finfo.st_atime);
244
245   return (0);
246 }
247
248 /* Return non-zero if FILE's size has increased. */
249 static int
250 file_has_grown (i)
251      int i;
252 {
253   off_t size;
254   struct stat finfo;
255   char *file;
256
257   file = mailfiles[i]->name;
258   size = mailfiles[i]->file_size;
259
260   return ((stat (file, &finfo) == 0) && (finfo.st_size > size));
261 }
262
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. */
266 static char *
267 parse_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 == '%')
286         return s;
287     }
288   return ((char *)NULL);
289 }
290
291 char *
292 make_default_mailpath ()
293 {
294   char *mp;
295
296   get_current_user_info ();
297   mp = xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
298   strcpy (mp, DEFAULT_MAIL_DIRECTORY);
299   mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
300   strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
301   return (mp);
302 }
303
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. */
307 void
308 remember_mail_dates ()
309 {
310   char *mailpaths;
311   char *mailfile, *mp;
312   int i = 0;
313
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
331   while (mailfile = extract_colon_unit (mailpaths, &i))
332     {
333       mp = parse_mailpath_spec (mailfile);
334       if (mp && *mp)
335         *mp++ = '\0';
336       add_mail_file (mailfile, mp);
337       free (mailfile);
338     }
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.
350    Special hack:  If the variable MAIL_WARNING is non-zero and the
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. */
353 void
354 check_mail ()
355 {
356   char *current_mail_file, *message;
357   int i, use_user_notification;
358   char *dollar_underscore, *temp;
359   WORD_LIST *tlist;
360
361   dollar_underscore = get_string_value ("_");
362   if (dollar_underscore)
363     dollar_underscore = savestring (dollar_underscore);
364
365   for (i = 0; i < mailfiles_count; i++)
366     {
367       current_mail_file = mailfiles[i]->name;
368
369       if (*current_mail_file == '\0')
370         continue;
371
372       if (file_mod_date_changed (i))
373         {
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 $_";
378
379           bind_variable ("_", current_mail_file);
380
381 #define atime mailfiles[i]->access_time
382 #define mtime mailfiles[i]->mod_time
383
384           /* Have to compute this before the call to update_mail_file, which
385              resets all the information. */
386           file_is_bigger = file_has_grown (i);
387
388           update_mail_file (i);
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)
397             continue;
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. */
401           if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
402             message = "You have new mail in $_";
403 #undef atime
404 #undef mtime
405
406           if ((tlist = expand_string (message, Q_DOUBLE_QUOTES)))
407             {
408               temp = string_list (tlist);
409               puts (temp);
410               free (temp);
411               dispose_words (tlist);
412             }
413           else
414             putchar ('\n');
415         }
416
417       if (mail_warning && file_access_date_changed (i))
418         {
419           update_mail_file (i);
420           printf ("The mail in %s has been read\n", current_mail_file);
421         }
422     }
423
424   if (dollar_underscore)
425     {
426       bind_variable ("_", dollar_underscore);
427       free (dollar_underscore);
428     }
429   else
430     unbind_variable ("_");
431 }