1/*	$NetBSD: mail.c,v 1.5 2006/01/15 18:16:30 jschauma Exp $	*/
2
3/*
4 * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
5 * John R. MacMillan
6 */
7#include <sys/cdefs.h>
8
9#ifndef lint
10__RCSID("$NetBSD: mail.c,v 1.5 2006/01/15 18:16:30 jschauma Exp $");
11#endif
12
13
14#include "config.h"
15
16#ifdef KSH
17#include "sh.h"
18#include "ksh_stat.h"
19#include "ksh_time.h"
20
21#define MBMESSAGE	"You have mail in $_"
22
23typedef struct mbox {
24	struct mbox    *mb_next;	/* next mbox in list */
25	char	       *mb_path;	/* path to mail file */
26	char	       *mb_msg;		/* to announce arrival of new mail */
27	time_t		mb_mtime;	/* mtime of mail file */
28} mbox_t;
29
30/*
31 * $MAILPATH is a linked list of mboxes.  $MAIL is a treated as a
32 * special case of $MAILPATH, where the list has only one node.  The
33 * same list is used for both since they are exclusive.
34 */
35
36static mbox_t	*mplist;
37static mbox_t	mbox;
38static time_t	mlastchkd;	/* when mail was last checked */
39static time_t	mailcheck_interval;
40
41static void     munset      ARGS((mbox_t *mlist)); /* free mlist and mval */
42static mbox_t * mballoc     ARGS((char *p, char *m)); /* allocate a new mbox */
43static void     mprintit    ARGS((mbox_t *mbp));
44
45void
46mcheck()
47{
48	register mbox_t	*mbp;
49	time_t		 now;
50	struct tbl	*vp;
51	struct stat	 stbuf;
52
53	now = time((time_t *) 0);
54	if (mlastchkd == 0)
55		mlastchkd = now;
56	if (now - mlastchkd >= mailcheck_interval) {
57		mlastchkd = now;
58
59		if (mplist)
60			mbp = mplist;
61		else if ((vp = global("MAIL")) && (vp->flag & ISSET))
62			mbp = &mbox;
63		else
64			mbp = NULL;
65
66		while (mbp) {
67			if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0
68			    && S_ISREG(stbuf.st_mode))
69			{
70				if (stbuf.st_size
71				    && mbp->mb_mtime != stbuf.st_mtime
72				    && stbuf.st_atime <= stbuf.st_mtime)
73					mprintit(mbp);
74				mbp->mb_mtime = stbuf.st_mtime;
75			} else {
76				/*
77				 * Some mail readers remove the mail
78				 * file if all mail is read.  If file
79				 * does not exist, assume this is the
80				 * case and set mtime to zero.
81				 */
82				mbp->mb_mtime = 0;
83			}
84			mbp = mbp->mb_next;
85		}
86	}
87}
88
89void
90mcset(interval)
91	long interval;
92{
93	mailcheck_interval = interval;
94}
95
96void
97mbset(p)
98	register char	*p;
99{
100	struct stat	stbuf;
101
102	if (mbox.mb_msg)
103		afree((void *)mbox.mb_msg, APERM);
104	if (mbox.mb_path)
105		afree((void *)mbox.mb_path, APERM);
106	/* Save a copy to protect from export (which munges the string) */
107	mbox.mb_path = str_save(p, APERM);
108	mbox.mb_msg = NULL;
109	if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
110		mbox.mb_mtime = stbuf.st_mtime;
111	else
112		mbox.mb_mtime = 0;
113}
114
115void
116mpset(mptoparse)
117	register char	*mptoparse;
118{
119	register mbox_t	*mbp;
120	register char	*mpath, *mmsg, *mval;
121	char *p;
122
123	munset( mplist );
124	mplist = NULL;
125	mval = str_save(mptoparse, APERM);
126	while (mval) {
127		mpath = mval;
128		if ((mval = strchr(mval, PATHSEP)) != NULL) {
129			*mval = '\0', mval++;
130		}
131		/* POSIX/bourne-shell say file%message */
132		for (p = mpath; (mmsg = strchr(p, '%')); ) {
133			/* a literal percent? (POSIXism) */
134			if (mmsg[-1] == '\\') {
135				/* use memmove() to avoid overlap problems */
136				memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
137				p = mmsg + 1;
138				continue;
139			}
140			break;
141		}
142		/* at&t ksh says file?message */
143		if (!mmsg && !Flag(FPOSIX))
144			mmsg = strchr(mpath, '?');
145		if (mmsg) {
146			*mmsg = '\0';
147			mmsg++;
148		}
149		mbp = mballoc(mpath, mmsg);
150		mbp->mb_next = mplist;
151		mplist = mbp;
152	}
153}
154
155static void
156munset(mlist)
157register mbox_t	*mlist;
158{
159	register mbox_t	*mbp;
160
161	while (mlist != NULL) {
162		mbp = mlist;
163		mlist = mbp->mb_next;
164		if (!mlist)
165			afree((void *)mbp->mb_path, APERM);
166		afree((void *)mbp, APERM);
167	}
168}
169
170static mbox_t *
171mballoc(p, m)
172	char	*p;
173	char	*m;
174{
175	struct stat	stbuf;
176	register mbox_t	*mbp;
177
178	mbp = (mbox_t *)alloc(sizeof(mbox_t), APERM);
179	mbp->mb_next = NULL;
180	mbp->mb_path = p;
181	mbp->mb_msg = m;
182	if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
183		mbp->mb_mtime = stbuf.st_mtime;
184	else
185		mbp->mb_mtime = 0;
186	return(mbp);
187}
188
189static void
190mprintit( mbp )
191mbox_t	*mbp;
192{
193	struct tbl	*vp;
194
195	/* Ignore setstr errors here (arbitrary) */
196	setstr((vp = local("_", FALSE)), mbp->mb_path, KSH_RETURN_ERROR);
197
198	shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
199
200	unset(vp, 0);
201}
202#endif /* KSH */
203