GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/cmd2.c Lines: 18 160 11.3 %
Date: 2017-11-07 Branches: 6 98 6.1 %

Line Branch Exec Source
1
/*	$OpenBSD: cmd2.c,v 1.22 2015/10/16 17:56:07 mmcc Exp $	*/
2
/*	$NetBSD: cmd2.c,v 1.7 1997/05/17 19:55:10 pk Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include "rcv.h"
34
#include <sys/wait.h>
35
#include "extern.h"
36
37
/*
38
 * Mail -- a mail program
39
 *
40
 * More user commands.
41
 */
42
static int igcomp(const void *, const void *);
43
44
/*
45
 * If any arguments were given, go to the next applicable argument
46
 * following dot, otherwise, go to the next applicable message.
47
 * If given as first command with no arguments, print first message.
48
 */
49
int
50
next(void *v)
51
{
52
	struct message *mp;
53
	int *msgvec = v;
54
	int *ip, *ip2, list[2], mdot;
55
56
	if (*msgvec != 0) {
57
		/*
58
		 * If some messages were supplied, find the
59
		 * first applicable one following dot using
60
		 * wrap around.
61
		 */
62
		mdot = dot - &message[0] + 1;
63
64
		/*
65
		 * Find the first message in the supplied
66
		 * message list which follows dot.
67
		 */
68
		for (ip = msgvec; *ip != 0; ip++)
69
			if (*ip > mdot)
70
				break;
71
		if (*ip == 0)
72
			ip = msgvec;
73
		ip2 = ip;
74
		do {
75
			mp = &message[*ip2 - 1];
76
			if ((mp->m_flag & MDELETED) == 0) {
77
				dot = mp;
78
				goto hitit;
79
			}
80
			if (*ip2 != 0)
81
				ip2++;
82
			if (*ip2 == 0)
83
				ip2 = msgvec;
84
		} while (ip2 != ip);
85
		puts("No messages applicable");
86
		return(1);
87
	}
88
89
	/*
90
	 * If this is the first command, select message 1.
91
	 * Note that this must exist for us to get here at all.
92
	 */
93
	if (!sawcom)
94
		goto hitit;
95
96
	/*
97
	 * Just find the next good message after dot, no
98
	 * wraparound.
99
	 */
100
	for (mp = dot+1; mp < &message[msgCount]; mp++)
101
		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
102
			break;
103
	if (mp >= &message[msgCount]) {
104
		puts("At EOF");
105
		return(0);
106
	}
107
	dot = mp;
108
hitit:
109
	/*
110
	 * Print dot.
111
	 */
112
	list[0] = dot - &message[0] + 1;
113
	list[1] = 0;
114
	return(type(list));
115
}
116
117
/*
118
 * Save a message in a file.  Mark the message as saved
119
 * so we can discard when the user quits.
120
 */
121
int
122
save(void *v)
123
{
124
	char *str = v;
125
126
	return(save1(str, 1, "save", saveignore));
127
}
128
129
/*
130
 * Copy a message to a file without affected its saved-ness
131
 */
132
int
133
copycmd(void *v)
134
{
135
	char *str = v;
136
137
	return(save1(str, 0, "copy", saveignore));
138
}
139
140
/*
141
 * Save/copy the indicated messages at the end of the passed file name.
142
 * If mark is true, mark the message "saved."
143
 */
144
int
145
save1(char *str, int mark, char *cmd, struct ignoretab *ignore)
146
{
147
	struct message *mp;
148
	char *file, *disp;
149
	int f, *msgvec, *ip;
150
	FILE *obuf;
151
152
	msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
153
	if ((file = snarf(str, &f)) == NULL)
154
		return(1);
155
	if (!f) {
156
		*msgvec = first(0, MMNORM);
157
		if (*msgvec == 0) {
158
			printf("No messages to %s.\n", cmd);
159
			return(1);
160
		}
161
		msgvec[1] = 0;
162
	}
163
	if (f && getmsglist(str, msgvec, 0) < 0)
164
		return(1);
165
	if ((file = expand(file)) == NULL)
166
		return(1);
167
	printf("\"%s\" ", file);
168
	fflush(stdout);
169
	if (access(file, F_OK) >= 0)
170
		disp = "[Appended]";
171
	else
172
		disp = "[New file]";
173
	if ((obuf = Fopen(file, "a")) == NULL) {
174
		warn(NULL);
175
		return(1);
176
	}
177
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
178
		mp = &message[*ip - 1];
179
		touch(mp);
180
		if (sendmessage(mp, obuf, ignore, NULL) < 0) {
181
			warn("%s", file);
182
			(void)Fclose(obuf);
183
			return(1);
184
		}
185
		if (mark)
186
			mp->m_flag |= MSAVED;
187
	}
188
	fflush(obuf);
189
	if (ferror(obuf))
190
		warn("%s", file);
191
	(void)Fclose(obuf);
192
	printf("%s\n", disp);
193
	return(0);
194
}
195
196
/*
197
 * Write the indicated messages at the end of the passed
198
 * file name, minus header and trailing blank line.
199
 */
200
int
201
swrite(void *v)
202
{
203
	char *str = v;
204
205
	return(save1(str, 1, "write", ignoreall));
206
}
207
208
/*
209
 * Snarf the file from the end of the command line and
210
 * return a pointer to it.  If there is no file attached,
211
 * just return NULL.  Put a null in front of the file
212
 * name so that the message list processing won't see it,
213
 * unless the file name is the only thing on the line, in
214
 * which case, return 0 in the reference flag variable.
215
 */
216
char *
217
snarf(char *linebuf, int *flag)
218
{
219
	char *cp;
220
221
	*flag = 1;
222
	cp = strlen(linebuf) + linebuf - 1;
223
224
	/*
225
	 * Strip away trailing blanks.
226
	 */
227
	while (cp > linebuf && isspace((unsigned char)*cp))
228
		cp--;
229
	*++cp = 0;
230
231
	/*
232
	 * Now search for the beginning of the file name.
233
	 */
234
	while (cp > linebuf && !isspace((unsigned char)*cp))
235
		cp--;
236
	if (*cp == '\0') {
237
		puts("No file specified.");
238
		return(NULL);
239
	}
240
	if (isspace((unsigned char)*cp))
241
		*cp++ = 0;
242
	else
243
		*flag = 0;
244
	return(cp);
245
}
246
247
/*
248
 * Delete messages.
249
 */
250
int
251
deletecmd(void *v)
252
{
253
	int *msgvec = v;
254
255
	delm(msgvec);
256
	return(0);
257
}
258
259
/*
260
 * Delete messages, then type the new dot.
261
 */
262
int
263
deltype(void *v)
264
{
265
	int *msgvec = v;
266
	int list[2];
267
	int lastdot;
268
269
	lastdot = dot - &message[0] + 1;
270
	if (delm(msgvec) >= 0) {
271
		list[0] = dot - &message[0] + 1;
272
		if (list[0] > lastdot) {
273
			touch(dot);
274
			list[1] = 0;
275
			return(type(list));
276
		}
277
		puts("At EOF");
278
	} else
279
		puts("No more messages");
280
	return(0);
281
}
282
283
/*
284
 * Delete the indicated messages.
285
 * Set dot to some nice place afterwards.
286
 * Internal interface.
287
 */
288
int
289
delm(int *msgvec)
290
{
291
	struct message *mp;
292
	int *ip, last;
293
294
	last = 0;
295
	for (ip = msgvec; *ip != 0; ip++) {
296
		mp = &message[*ip - 1];
297
		touch(mp);
298
		mp->m_flag |= MDELETED|MTOUCH;
299
		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
300
		last = *ip;
301
	}
302
	if (last != 0) {
303
		dot = &message[last-1];
304
		last = first(0, MDELETED);
305
		if (last != 0) {
306
			dot = &message[last-1];
307
			return(0);
308
		}
309
		else {
310
			dot = &message[0];
311
			return(-1);
312
		}
313
	}
314
315
	/*
316
	 * Following can't happen
317
	 */
318
	return(-1);
319
}
320
321
/*
322
 * Undelete the indicated messages.
323
 */
324
int
325
undeletecmd(void *v)
326
{
327
	int *msgvec = v;
328
	int *ip;
329
	struct message *mp;
330
331
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
332
		mp = &message[*ip - 1];
333
		touch(mp);
334
		dot = mp;
335
		mp->m_flag &= ~MDELETED;
336
	}
337
	return(0);
338
}
339
340
/*
341
 * Add the given header fields to the retained list.
342
 * If no arguments, print the current list of retained fields.
343
 */
344
int
345
retfield(void *v)
346
{
347
	char **list = v;
348
349
	return(ignore1(list, ignore + 1, "retained"));
350
}
351
352
/*
353
 * Add the given header fields to the ignored list.
354
 * If no arguments, print the current list of ignored fields.
355
 */
356
int
357
igfield(void *v)
358
{
359
4
	char **list = v;
360
361
2
	return(ignore1(list, ignore, "ignored"));
362
}
363
364
int
365
saveretfield(void *v)
366
{
367
	char **list = v;
368
369
	return(ignore1(list, saveignore + 1, "retained"));
370
}
371
372
int
373
saveigfield(void *v)
374
{
375
	char **list = v;
376
377
	return(ignore1(list, saveignore, "ignored"));
378
}
379
380
int
381
ignore1(char **list, struct ignoretab *tab, char *which)
382
{
383
4
	char field[LINESIZE];
384
	char **ap;
385
	struct ignore *igp;
386
	int h;
387
388
2
	if (*list == NULL)
389
		return(igshow(tab, which));
390
32
	for (ap = list; *ap != 0; ap++) {
391
14
		istrlcpy(field, *ap, sizeof(field));
392
14
		if (member(field, tab))
393
			continue;
394
14
		h = hash(field);
395
14
		igp = calloc(1, sizeof(struct ignore));
396
14
		if (igp == NULL)
397
			err(1, "calloc");
398
14
		igp->i_field = strdup(field);
399
14
		if (igp->i_field == NULL)
400
			err(1, "strdup");
401
14
		igp->i_link = tab->i_head[h];
402
14
		tab->i_head[h] = igp;
403
14
		tab->i_count++;
404
14
	}
405
2
	return(0);
406
2
}
407
408
/*
409
 * Print out all currently retained fields.
410
 */
411
int
412
igshow(struct ignoretab *tab, char *which)
413
{
414
	int h;
415
	struct ignore *igp;
416
	char **ap, **ring;
417
418
	if (tab->i_count == 0) {
419
		printf("No fields currently being %s.\n", which);
420
		return(0);
421
	}
422
	ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
423
	ap = ring;
424
	for (h = 0; h < HSHSIZE; h++)
425
		for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
426
			*ap++ = igp->i_field;
427
	*ap = 0;
428
	qsort(ring, tab->i_count, sizeof(char *), igcomp);
429
	for (ap = ring; *ap != 0; ap++)
430
		puts(*ap);
431
	return(0);
432
}
433
434
/*
435
 * Compare two names for sorting ignored field list.
436
 */
437
static int
438
igcomp(const void *l, const void *r)
439
{
440
441
	return(strcmp(*(char **)l, *(char **)r));
442
}