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