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