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