GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/mail.c Lines: 28 67 41.8 %
Date: 2017-11-13 Branches: 18 52 34.6 %

Line Branch Exec Source
1
/*	$OpenBSD: mail.c,v 1.22 2015/10/19 14:42:16 mmcc 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
10
#include <string.h>
11
#include <time.h>
12
13
#include "config.h"
14
#include "sh.h"
15
16
#define MBMESSAGE	"you have mail in $_"
17
18
typedef struct mbox {
19
	struct mbox    *mb_next;	/* next mbox in list */
20
	char	       *mb_path;	/* path to mail file */
21
	char	       *mb_msg;		/* to announce arrival of new mail */
22
	time_t		mb_mtime;	/* mtime of mail file */
23
} mbox_t;
24
25
/*
26
 * $MAILPATH is a linked list of mboxes.  $MAIL is a treated as a
27
 * special case of $MAILPATH, where the list has only one node.  The
28
 * same list is used for both since they are exclusive.
29
 */
30
31
static mbox_t	*mplist;
32
static mbox_t	mbox;
33
static time_t	mlastchkd;	/* when mail was last checked */
34
static time_t	mailcheck_interval;
35
36
static void	munset(mbox_t *); /* free mlist and mval */
37
static mbox_t * mballoc(char *, char *); /* allocate a new mbox */
38
static void	mprintit(mbox_t *);
39
40
void
41
mcheck(void)
42
{
43
	mbox_t		*mbp;
44
	time_t		 now;
45
	struct tbl	*vp;
46
3328
	struct stat	 stbuf;
47
48
1664
	now = time(NULL);
49
1664
	if (mlastchkd == 0)
50
880
		mlastchkd = now;
51
1664
	if (now - mlastchkd >= mailcheck_interval) {
52
1
		mlastchkd = now;
53
54
1
		if (mplist)
55
			mbp = mplist;
56

2
		else if ((vp = global("MAIL")) && (vp->flag & ISSET))
57
1
			mbp = &mbox;
58
		else
59
			mbp = NULL;
60
61
4
		while (mbp) {
62

3
			if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 &&
63
1
			    S_ISREG(stbuf.st_mode)) {
64

1
				if (stbuf.st_size &&
65
1
				    mbp->mb_mtime != stbuf.st_mtime &&
66
				    stbuf.st_atime <= stbuf.st_mtime)
67
					mprintit(mbp);
68
1
				mbp->mb_mtime = stbuf.st_mtime;
69
1
			} else {
70
				/*
71
				 * Some mail readers remove the mail
72
				 * file if all mail is read.  If file
73
				 * does not exist, assume this is the
74
				 * case and set mtime to zero.
75
				 */
76
				mbp->mb_mtime = 0;
77
			}
78
1
			mbp = mbp->mb_next;
79
		}
80
	}
81
1664
}
82
83
void
84
mcset(long int interval)
85
{
86
419052
	mailcheck_interval = interval;
87
209526
}
88
89
void
90
mbset(char *p)
91
{
92
208164
	struct stat	stbuf;
93
94
104082
	afree(mbox.mb_msg, APERM);
95
104082
	afree(mbox.mb_path, APERM);
96
	/* Save a copy to protect from export (which munges the string) */
97
104082
	mbox.mb_path = str_save(p, APERM);
98
104082
	mbox.mb_msg = NULL;
99

310272
	if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
100
102108
		mbox.mb_mtime = stbuf.st_mtime;
101
	else
102
		mbox.mb_mtime = 0;
103
104082
}
104
105
void
106
mpset(char *mptoparse)
107
{
108
	mbox_t	*mbp;
109
	char	*mpath, *mmsg, *mval;
110
	char *p;
111
112
	munset( mplist );
113
	mplist = NULL;
114
	mval = str_save(mptoparse, APERM);
115
	while (mval) {
116
		mpath = mval;
117
		if ((mval = strchr(mval, ':')) != NULL) {
118
			*mval = '\0';
119
			mval++;
120
		}
121
		/* POSIX/bourne-shell say file%message */
122
		for (p = mpath; (mmsg = strchr(p, '%')); ) {
123
			/* a literal percent? (POSIXism) */
124
			if (mmsg[-1] == '\\') {
125
				/* use memmove() to avoid overlap problems */
126
				memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
127
				p = mmsg + 1;
128
				continue;
129
			}
130
			break;
131
		}
132
		/* at&t ksh says file?message */
133
		if (!mmsg && !Flag(FPOSIX))
134
			mmsg = strchr(mpath, '?');
135
		if (mmsg) {
136
			*mmsg = '\0';
137
			mmsg++;
138
		}
139
		mbp = mballoc(mpath, mmsg);
140
		mbp->mb_next = mplist;
141
		mplist = mbp;
142
	}
143
}
144
145
static void
146
munset(mbox_t *mlist)
147
{
148
	mbox_t	*mbp;
149
150
	while (mlist != NULL) {
151
		mbp = mlist;
152
		mlist = mbp->mb_next;
153
		if (!mlist)
154
			afree(mbp->mb_path, APERM);
155
		afree(mbp, APERM);
156
	}
157
}
158
159
static mbox_t *
160
mballoc(char *p, char *m)
161
{
162
	struct stat	stbuf;
163
	mbox_t	*mbp;
164
165
	mbp = alloc(sizeof(mbox_t), APERM);
166
	mbp->mb_next = NULL;
167
	mbp->mb_path = p;
168
	mbp->mb_msg = m;
169
	if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
170
		mbp->mb_mtime = stbuf.st_mtime;
171
	else
172
		mbp->mb_mtime = 0;
173
	return(mbp);
174
}
175
176
static void
177
mprintit(mbox_t *mbp)
178
{
179
	struct tbl	*vp;
180
181
#if 0
182
	/*
183
	 * I doubt this $_ overloading is bad in /bin/sh mode.  Anyhow, we
184
	 * crash as the code looks now if we do not set vp.  Now, this is
185
	 * easy to fix too, but I'd like to see what POSIX says before doing
186
	 * a change like that.
187
	 */
188
	if (!Flag(FSH))
189
#endif
190
		/* Ignore setstr errors here (arbitrary) */
191
		setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR);
192
193
	shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
194
195
	unset(vp, 0);
196
}