loksh-noxz

[fork] a Linux port of OpenBSD's ksh
git clone https://noxz.tech/git/loksh-noxz.git
Log | Files | README

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