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