xref: /openbsd-src/usr.bin/mail/cmd2.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: cmd2.c,v 1.15 2004/09/15 22:21:40 deraadt 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 #ifndef lint
34 #if 0
35 static const char sccsid[] = "@(#)cmd2.c	8.1 (Berkeley) 6/6/93";
36 #else
37 static const char rcsid[] = "$OpenBSD: cmd2.c,v 1.15 2004/09/15 22:21:40 deraadt Exp $";
38 #endif
39 #endif /* not lint */
40 
41 #include "rcv.h"
42 #include <sys/wait.h>
43 #include "extern.h"
44 
45 /*
46  * Mail -- a mail program
47  *
48  * More user commands.
49  */
50 static int igcomp(const void *, const void *);
51 
52 /*
53  * If any arguments were given, go to the next applicable argument
54  * following dot, otherwise, go to the next applicable message.
55  * If given as first command with no arguments, print first message.
56  */
57 int
58 next(void *v)
59 {
60 	struct message *mp;
61 	int *msgvec = v;
62 	int *ip, *ip2, list[2], mdot;
63 
64 	if (*msgvec != NULL) {
65 		/*
66 		 * If some messages were supplied, find the
67 		 * first applicable one following dot using
68 		 * wrap around.
69 		 */
70 		mdot = dot - &message[0] + 1;
71 
72 		/*
73 		 * Find the first message in the supplied
74 		 * message list which follows dot.
75 		 */
76 		for (ip = msgvec; *ip != NULL; ip++)
77 			if (*ip > mdot)
78 				break;
79 		if (*ip == 0)
80 			ip = msgvec;
81 		ip2 = ip;
82 		do {
83 			mp = &message[*ip2 - 1];
84 			if ((mp->m_flag & MDELETED) == 0) {
85 				dot = mp;
86 				goto hitit;
87 			}
88 			if (*ip2 != NULL)
89 				ip2++;
90 			if (*ip2 == 0)
91 				ip2 = msgvec;
92 		} while (ip2 != ip);
93 		puts("No messages applicable");
94 		return(1);
95 	}
96 
97 	/*
98 	 * If this is the first command, select message 1.
99 	 * Note that this must exist for us to get here at all.
100 	 */
101 	if (!sawcom)
102 		goto hitit;
103 
104 	/*
105 	 * Just find the next good message after dot, no
106 	 * wraparound.
107 	 */
108 	for (mp = dot+1; mp < &message[msgCount]; mp++)
109 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
110 			break;
111 	if (mp >= &message[msgCount]) {
112 		puts("At EOF");
113 		return(0);
114 	}
115 	dot = mp;
116 hitit:
117 	/*
118 	 * Print dot.
119 	 */
120 	list[0] = dot - &message[0] + 1;
121 	list[1] = NULL;
122 	return(type(list));
123 }
124 
125 /*
126  * Save a message in a file.  Mark the message as saved
127  * so we can discard when the user quits.
128  */
129 int
130 save(void *v)
131 {
132 	char *str = v;
133 
134 	return(save1(str, 1, "save", saveignore));
135 }
136 
137 /*
138  * Copy a message to a file without affected its saved-ness
139  */
140 int
141 copycmd(void *v)
142 {
143 	char *str = v;
144 
145 	return(save1(str, 0, "copy", saveignore));
146 }
147 
148 /*
149  * Save/copy the indicated messages at the end of the passed file name.
150  * If mark is true, mark the message "saved."
151  */
152 int
153 save1(char *str, int mark, char *cmd, struct ignoretab *ignore)
154 {
155 	struct message *mp;
156 	char *file, *disp;
157 	int f, *msgvec, *ip;
158 	FILE *obuf;
159 
160 	msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
161 	if ((file = snarf(str, &f)) == NULL)
162 		return(1);
163 	if (!f) {
164 		*msgvec = first(0, MMNORM);
165 		if (*msgvec == 0) {
166 			printf("No messages to %s.\n", cmd);
167 			return(1);
168 		}
169 		msgvec[1] = NULL;
170 	}
171 	if (f && getmsglist(str, msgvec, 0) < 0)
172 		return(1);
173 	if ((file = expand(file)) == NULL)
174 		return(1);
175 	printf("\"%s\" ", file);
176 	fflush(stdout);
177 	if (access(file, 0) >= 0)
178 		disp = "[Appended]";
179 	else
180 		disp = "[New file]";
181 	if ((obuf = Fopen(file, "a")) == NULL) {
182 		warn(NULL);
183 		return(1);
184 	}
185 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
186 		mp = &message[*ip - 1];
187 		touch(mp);
188 		if (sendmessage(mp, obuf, ignore, NULL) < 0) {
189 			warn("%s", file);
190 			(void)Fclose(obuf);
191 			return(1);
192 		}
193 		if (mark)
194 			mp->m_flag |= MSAVED;
195 	}
196 	fflush(obuf);
197 	if (ferror(obuf))
198 		warn("%s", file);
199 	(void)Fclose(obuf);
200 	printf("%s\n", disp);
201 	return(0);
202 }
203 
204 /*
205  * Write the indicated messages at the end of the passed
206  * file name, minus header and trailing blank line.
207  */
208 int
209 swrite(void *v)
210 {
211 	char *str = v;
212 
213 	return(save1(str, 1, "write", ignoreall));
214 }
215 
216 /*
217  * Snarf the file from the end of the command line and
218  * return a pointer to it.  If there is no file attached,
219  * just return NULL.  Put a null in front of the file
220  * name so that the message list processing won't see it,
221  * unless the file name is the only thing on the line, in
222  * which case, return 0 in the reference flag variable.
223  */
224 char *
225 snarf(char *linebuf, int *flag)
226 {
227 	char *cp;
228 
229 	*flag = 1;
230 	cp = strlen(linebuf) + linebuf - 1;
231 
232 	/*
233 	 * Strip away trailing blanks.
234 	 */
235 	while (cp > linebuf && isspace(*cp))
236 		cp--;
237 	*++cp = 0;
238 
239 	/*
240 	 * Now search for the beginning of the file name.
241 	 */
242 	while (cp > linebuf && !isspace(*cp))
243 		cp--;
244 	if (*cp == '\0') {
245 		puts("No file specified.");
246 		return(NULL);
247 	}
248 	if (isspace(*cp))
249 		*cp++ = 0;
250 	else
251 		*flag = 0;
252 	return(cp);
253 }
254 
255 /*
256  * Delete messages.
257  */
258 int
259 deletecmd(void *v)
260 {
261 	int *msgvec = v;
262 
263 	delm(msgvec);
264 	return(0);
265 }
266 
267 /*
268  * Delete messages, then type the new dot.
269  */
270 int
271 deltype(void *v)
272 {
273 	int *msgvec = v;
274 	int list[2];
275 	int lastdot;
276 
277 	lastdot = dot - &message[0] + 1;
278 	if (delm(msgvec) >= 0) {
279 		list[0] = dot - &message[0] + 1;
280 		if (list[0] > lastdot) {
281 			touch(dot);
282 			list[1] = NULL;
283 			return(type(list));
284 		}
285 		puts("At EOF");
286 	} else
287 		puts("No more messages");
288 	return(0);
289 }
290 
291 /*
292  * Delete the indicated messages.
293  * Set dot to some nice place afterwards.
294  * Internal interface.
295  */
296 int
297 delm(int *msgvec)
298 {
299 	struct message *mp;
300 	int *ip, last;
301 
302 	last = NULL;
303 	for (ip = msgvec; *ip != NULL; ip++) {
304 		mp = &message[*ip - 1];
305 		touch(mp);
306 		mp->m_flag |= MDELETED|MTOUCH;
307 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
308 		last = *ip;
309 	}
310 	if (last != NULL) {
311 		dot = &message[last-1];
312 		last = first(0, MDELETED);
313 		if (last != NULL) {
314 			dot = &message[last-1];
315 			return(0);
316 		}
317 		else {
318 			dot = &message[0];
319 			return(-1);
320 		}
321 	}
322 
323 	/*
324 	 * Following can't happen -- it keeps lint happy
325 	 */
326 	return(-1);
327 }
328 
329 /*
330  * Undelete the indicated messages.
331  */
332 int
333 undeletecmd(void *v)
334 {
335 	int *msgvec = v;
336 	int *ip;
337 	struct message *mp;
338 
339 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
340 		mp = &message[*ip - 1];
341 		touch(mp);
342 		dot = mp;
343 		mp->m_flag &= ~MDELETED;
344 	}
345 	return(0);
346 }
347 
348 /*
349  * Interactively dump core on "core"
350  */
351 int
352 core(void *v)
353 {
354 	pid_t pid;
355 	extern int wait_status;
356 
357 	switch (pid = vfork()) {
358 	case -1:
359 		warn("vfork");
360 		return(1);
361 	case 0:
362 		abort();
363 		_exit(1);
364 	}
365 	fputs("Okie dokie", stdout);
366 	fflush(stdout);
367 	wait_child(pid);
368 	if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status))
369 		puts(" -- Core dumped.");
370 	else
371 		puts(" -- Can't dump core.");
372 	return(0);
373 }
374 
375 /*
376  * Add the given header fields to the retained list.
377  * If no arguments, print the current list of retained fields.
378  */
379 int
380 retfield(void *v)
381 {
382 	char **list = v;
383 
384 	return(ignore1(list, ignore + 1, "retained"));
385 }
386 
387 /*
388  * Add the given header fields to the ignored list.
389  * If no arguments, print the current list of ignored fields.
390  */
391 int
392 igfield(void *v)
393 {
394 	char **list = v;
395 
396 	return(ignore1(list, ignore, "ignored"));
397 }
398 
399 int
400 saveretfield(void *v)
401 {
402 	char **list = v;
403 
404 	return(ignore1(list, saveignore + 1, "retained"));
405 }
406 
407 int
408 saveigfield(void *v)
409 {
410 	char **list = v;
411 
412 	return(ignore1(list, saveignore, "ignored"));
413 }
414 
415 int
416 ignore1(char **list, struct ignoretab *tab, char *which)
417 {
418 	char field[LINESIZE];
419 	char **ap;
420 	struct ignore *igp;
421 	int h;
422 
423 	if (*list == NULL)
424 		return(igshow(tab, which));
425 	for (ap = list; *ap != 0; ap++) {
426 		istrlcpy(field, *ap, sizeof(field));
427 		if (member(field, tab))
428 			continue;
429 		h = hash(field);
430 		igp = (struct ignore *)calloc(1, sizeof(struct ignore));
431 		if (igp == NULL)
432 			errx(1, "Out of memory");
433 		igp->i_field = strdup(field);
434 		if (igp->i_field == NULL)
435 			errx(1, "Out of memory");
436 		igp->i_link = tab->i_head[h];
437 		tab->i_head[h] = igp;
438 		tab->i_count++;
439 	}
440 	return(0);
441 }
442 
443 /*
444  * Print out all currently retained fields.
445  */
446 int
447 igshow(struct ignoretab *tab, char *which)
448 {
449 	int h;
450 	struct ignore *igp;
451 	char **ap, **ring;
452 
453 	if (tab->i_count == 0) {
454 		printf("No fields currently being %s.\n", which);
455 		return(0);
456 	}
457 	ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
458 	ap = ring;
459 	for (h = 0; h < HSHSIZE; h++)
460 		for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
461 			*ap++ = igp->i_field;
462 	*ap = 0;
463 	qsort(ring, tab->i_count, sizeof(char *), igcomp);
464 	for (ap = ring; *ap != 0; ap++)
465 		puts(*ap);
466 	return(0);
467 }
468 
469 /*
470  * Compare two names for sorting ignored field list.
471  */
472 static int
473 igcomp(const void *l, const void *r)
474 {
475 
476 	return(strcmp(*(char **)l, *(char **)r));
477 }
478