Imported from ../bash-1.14.7.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 <stdio.h>
22 #include "bashtypes.h"
23 #include "posixstat.h"
24 #include <sys/param.h>
25 #include "bashansi.h"
26 #include "shell.h"
27 #include "maxpath.h"
28 #include "execute_cmd.h"
29 #include <tilde/tilde.h>
30
31 #ifndef NOW
32 #define NOW ((time_t)time ((time_t *)0))
33 #endif
34
35 typedef struct {
36   char *name;
37   time_t access_time;
38   time_t mod_time;
39   long file_size;
40 } FILEINFO;
41
42 /* The list of remembered mail files. */
43 FILEINFO **mailfiles = (FILEINFO **)NULL;
44
45 /* Number of mail files that we have. */
46 int mailfiles_count = 0;
47
48 /* The last known time that mail was checked. */
49 int last_time_mail_checked = 0;
50
51 /* Returns non-zero if it is time to check mail. */
52 int
53 time_to_check_mail ()
54 {
55   char *temp = get_string_value ("MAILCHECK");
56   time_t now = NOW;
57   long seconds = -1L;
58
59   /* Skip leading whitespace in MAILCHECK. */
60   if (temp)
61     {
62       while (whitespace (*temp))
63         temp++;
64
65       seconds = atoi (temp);
66     }
67
68   /* Negative number, or non-numbers (such as empty string) cause no
69      checking to take place. */
70   if (seconds < 0)
71     return (0);
72
73   /* Time to check if MAILCHECK is explicitly set to zero, or if enough
74      time has passed since the last check. */
75   return (!seconds || ((now - last_time_mail_checked) >= seconds));
76 }
77
78 /* Okay, we have checked the mail.  Perhaps I should make this function
79    go away. */
80 void
81 reset_mail_timer ()
82 {
83   last_time_mail_checked = NOW;
84 }
85
86 /* Locate a file in the list.  Return index of
87    entry, or -1 if not found. */
88 static int
89 find_mail_file (file)
90      char *file;
91 {
92   register int i;
93
94   for (i = 0; i < mailfiles_count; i++)
95     if (STREQ ((mailfiles[i])->name, file))
96       return i;
97
98   return -1;
99 }
100
101 /* Add this file to the list of remembered files and return its index
102    in the list of mail files. */
103 static int
104 add_mail_file (file)
105      char *file;
106 {
107   struct stat finfo;
108   char *filename;
109   int i;
110
111   filename = full_pathname (file);
112   i = find_mail_file (file);
113   if (i > -1)
114     {
115       if (stat (filename, &finfo) == 0)
116         {
117           mailfiles[i]->mod_time = finfo.st_mtime;
118           mailfiles[i]->access_time = finfo.st_atime;
119           mailfiles[i]->file_size = (long)finfo.st_size;
120         }
121       free (filename);
122       return i;
123     }
124
125   i = mailfiles_count++;
126   mailfiles = (FILEINFO **)xrealloc
127                 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
128
129   mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
130   mailfiles[i]->name = filename;
131   if (stat (filename, &finfo) == 0)
132     {
133       mailfiles[i]->access_time = finfo.st_atime;
134       mailfiles[i]->mod_time = finfo.st_mtime;
135       mailfiles[i]->file_size = finfo.st_size;
136     }
137   else
138     {
139       mailfiles[i]->access_time = mailfiles[i]->mod_time = (time_t)-1;
140       mailfiles[i]->file_size = -1L;
141     }
142   return i;
143 }
144
145 /* Reset the existing mail files access and modification times to zero. */
146 void
147 reset_mail_files ()
148 {
149   register int i;
150
151   for (i = 0; i < mailfiles_count; i++)
152     {
153       mailfiles[i]->access_time = mailfiles[i]->mod_time = 0;
154       mailfiles[i]->file_size = 0L;
155     }
156 }
157
158 /* Free the information that we have about the remembered mail files. */
159 void
160 free_mail_files ()
161 {
162   register int i;
163
164   for (i = 0; i < mailfiles_count; i++)
165     {
166       free (mailfiles[i]->name);
167       free (mailfiles[i]);
168     }
169
170   if (mailfiles)
171     free (mailfiles);
172
173   mailfiles_count = 0;
174   mailfiles = (FILEINFO **)NULL;
175 }
176
177 /* Return non-zero if FILE's mod date has changed and it has not been
178    accessed since modified. */
179 static int
180 file_mod_date_changed (file)
181      char *file;
182 {
183   time_t time = (time_t)0;
184   struct stat finfo;
185   int i;
186
187   i = find_mail_file (file);
188   if (i != -1)
189     time = mailfiles[i]->mod_time;
190
191   if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
192     return (time != finfo.st_mtime);
193
194   return (0);
195 }
196
197 /* Return non-zero if FILE's access date has changed. */
198 static int
199 file_access_date_changed (file)
200      char *file;
201 {
202   time_t time = (time_t)0;
203   struct stat finfo;
204   int i;
205
206   i = find_mail_file (file);
207   if (i != -1)
208     time = mailfiles[i]->access_time;
209
210   if ((stat (file, &finfo) == 0) && (finfo.st_size > 0))
211     return (time != finfo.st_atime);
212
213   return (0);
214 }
215
216 /* Return non-zero if FILE's size has increased. */
217 static int
218 file_has_grown (file)
219      char *file;
220 {
221   long size = 0L;
222   struct stat finfo;
223   int i;
224
225   i = find_mail_file (file);
226   if (i != -1)
227     size = mailfiles[i]->file_size;
228
229   return ((stat (file, &finfo) == 0) && (finfo.st_size > size));
230 }
231
232 char *
233 make_default_mailpath ()
234 {
235   char *mp;
236
237   mp = xmalloc (1 + sizeof (DEFAULT_MAIL_PATH) + strlen (current_user.user_name));
238   strcpy (mp, DEFAULT_MAIL_PATH);
239   strcpy (mp + sizeof (DEFAULT_MAIL_PATH) - 1, current_user.user_name);
240   return (mp);
241 }
242
243 /* Return the colon separated list of pathnames to check for mail. */
244 static char *
245 get_mailpaths ()
246 {
247   char *mailpaths;
248
249   mailpaths = get_string_value ("MAILPATH");
250
251   if (!mailpaths)
252     mailpaths = get_string_value ("MAIL");
253
254   if (mailpaths)
255     return (savestring (mailpaths));
256     
257   return (make_default_mailpath ());
258 }
259
260 /* Take an element from $MAILPATH and return the portion from
261    the first unquoted `?' or `%' to the end of the string.  This is the
262    message to be printed when the file contents change. */
263 static char *
264 parse_mailpath_spec (str)
265      char *str;
266 {
267   char *s;
268   int pass_next;
269
270   for (s = str, pass_next = 0; s && *s; s++)
271     {
272       if (pass_next)
273         {
274           pass_next = 0;
275           continue;
276         }
277       if (*s == '\\')
278         {
279           pass_next++;
280           continue;
281         }
282       if (*s == '?' || *s == '%')
283         return s;
284     }
285   return ((char *)NULL);
286 }
287       
288 /* Remember the dates of the files specified by MAILPATH, or if there is
289    no MAILPATH, by the file specified in MAIL.  If neither exists, use a
290    default value, which we randomly concoct from using Unix. */
291 void
292 remember_mail_dates ()
293 {
294   char *mailpaths;
295   char *mailfile, *mp;
296   int i = 0;
297
298   mailpaths = get_mailpaths ();  
299   while (mailfile = extract_colon_unit (mailpaths, &i))
300     {
301       mp = parse_mailpath_spec (mailfile);
302       if (mp && *mp)
303         *mp = '\0';
304       add_mail_file (mailfile);
305       free (mailfile);
306     }
307   free (mailpaths);
308 }
309
310 /* check_mail () is useful for more than just checking mail.  Since it has
311    the paranoids dream ability of telling you when someone has read your
312    mail, it can just as easily be used to tell you when someones .profile
313    file has been read, thus letting one know when someone else has logged
314    in.  Pretty good, huh? */
315
316 /* Check for mail in some files.  If the modification date of any
317    of the files in MAILPATH has changed since we last did a
318    remember_mail_dates () then mention that the user has mail.
319    Special hack:  If the shell variable MAIL_WARNING is on and the
320    mail file has been accessed since the last time we remembered, then
321    the message "The mail in <mailfile> has been read" is printed. */
322 void
323 check_mail ()
324 {
325   char *current_mail_file, *you_have_mail_message;
326   char *mailpaths, *mp;
327   int file_index = 0;
328   char *dollar_underscore;
329
330   dollar_underscore = get_string_value ("_");
331
332   if (dollar_underscore)
333     dollar_underscore = savestring (dollar_underscore);
334
335   mailpaths = get_mailpaths ();
336   while ((current_mail_file = extract_colon_unit (mailpaths, &file_index)))
337     {
338       char *t;
339       int use_user_notification;
340
341       if (!*current_mail_file)
342         {
343           free (current_mail_file);
344           continue;
345         }
346
347       t = full_pathname (current_mail_file);
348       free (current_mail_file);
349       current_mail_file = t;
350
351       use_user_notification = 0;
352       you_have_mail_message = "You have mail in $_";
353
354       mp = parse_mailpath_spec (current_mail_file);
355       if (mp && *mp)
356         {
357           *mp = '\0';
358           you_have_mail_message = ++mp;
359           use_user_notification++;
360         }
361
362       if (file_mod_date_changed (current_mail_file))
363         {
364           WORD_LIST *tlist;
365           int i, file_is_bigger;
366           bind_variable ("_", current_mail_file);
367 #define atime mailfiles[i]->access_time
368 #define mtime mailfiles[i]->mod_time
369
370           /* Have to compute this before the call to add_mail_file, which
371              resets all the information. */
372           file_is_bigger = file_has_grown (current_mail_file);
373
374           i = add_mail_file (current_mail_file);
375
376           if (i == -1)
377             continue;           /* if this returns -1 , it is a bug */
378
379           /* If the user has just run a program which manipulates the
380              mail file, then don't bother explaining that the mail
381              file has been manipulated.  Since some systems don't change
382              the access time to be equal to the modification time when
383              the mail in the file is manipulated, check the size also.  If
384              the file has not grown, continue. */
385           if ((atime >= mtime) && !file_is_bigger)
386             {
387               free (current_mail_file);
388               continue;
389             }
390
391           /* If the mod time is later than the access time and the file
392              has grown, note the fact that this is *new* mail. */
393           if (!use_user_notification && (atime < mtime) && file_is_bigger)
394             you_have_mail_message = "You have new mail in $_";
395 #undef atime
396 #undef mtime
397
398           if ((tlist = expand_string (you_have_mail_message, 1)))
399             {
400               char *tem = string_list (tlist);
401               printf ("%s\n", tem);
402               free (tem);
403               dispose_words (tlist);
404             }
405           else
406             printf ("\n");
407         }
408
409       if (find_variable ("MAIL_WARNING") &&
410             file_access_date_changed (current_mail_file))
411         {
412           add_mail_file (current_mail_file);
413           printf ("The mail in %s has been read!\n", current_mail_file);
414         }
415
416       free (current_mail_file);
417     }
418   free (mailpaths);
419
420   if (dollar_underscore)
421     {
422       bind_variable ("_", dollar_underscore);
423       free (dollar_underscore);
424     }
425   else
426     unbind_variable ("_");
427 }