xref: /netbsd-src/usr.bin/mail/cmd2.c (revision 1ca5c1b28139779176bd5c13ad7c5f25c0bcd5f8)
1 /*	$NetBSD: cmd2.c,v 1.11 2001/02/05 02:07:52 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)cmd2.c	8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: cmd2.c,v 1.11 2001/02/05 02:07:52 christos Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include "rcv.h"
46 #include "extern.h"
47 
48 /*
49  * Mail -- a mail program
50  *
51  * More user commands.
52  */
53 extern int wait_status;
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 	struct message *mp;
67 	int *ip, *ip2;
68 	int list[2], mdot;
69 
70 	if (*msgvec != 0) {
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 != 0; ip++)
86 			if (*ip > mdot)
87 				break;
88 		if (*ip == 0)
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 != 0)
98 				ip2++;
99 			if (*ip2 == 0)
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] = 0;
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 	int *ip;
174 	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 == 0) {
185 			printf("No messages to %s.\n", cmd);
186 			return(1);
187 		}
188 		msgvec[1] = 0;
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 (sendmessage(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 	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((unsigned char)*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((unsigned char)*cp))
268 		cp--;
269 	if (*cp == '\0') {
270 		printf("No file specified.\n");
271 		return(NOSTR);
272 	}
273 	if (isspace((unsigned char)*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] = 0;
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 	struct message *mp;
327 	int *ip;
328 	int last;
329 
330 	last = 0;
331 	for (ip = msgvec; *ip != 0; 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 != 0) {
339 		dot = &message[last-1];
340 		last = first(0, MDELETED);
341 		if (last != 0) {
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 	struct message *mp;
367 	int *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 
387 	switch (pid = vfork()) {
388 	case -1:
389 		perror("fork");
390 		return(1);
391 	case 0:
392 		abort();
393 		_exit(1);
394 	}
395 	printf("Okie dokie");
396 	fflush(stdout);
397 	wait_child(pid);
398 	if (WCOREDUMP(wait_status))
399 		printf(" -- Core dumped.\n");
400 	else
401 		printf(" -- Can't dump core.\n");
402 	return 0;
403 }
404 
405 /*
406  * Clobber as many bytes of stack as the user requests.
407  */
408 int
409 clobber(v)
410 	void *v;
411 {
412 	char **argv = v;
413 	int times;
414 
415 	if (argv[0] == 0)
416 		times = 1;
417 	else
418 		times = (atoi(argv[0]) + 511) / 512;
419 	clob1(times);
420 	return 0;
421 }
422 
423 /*
424  * Clobber the stack.
425  */
426 void
427 clob1(n)
428 	int n;
429 {
430 	char buf[512];
431 	char *cp;
432 
433 	if (n <= 0)
434 		return;
435 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
436 		;
437 	clob1(n - 1);
438 }
439 
440 /*
441  * Add the given header fields to the retained list.
442  * If no arguments, print the current list of retained fields.
443  */
444 int
445 retfield(v)
446 	void *v;
447 {
448 	char **list = v;
449 
450 	return ignore1(list, ignore + 1, "retained");
451 }
452 
453 /*
454  * Add the given header fields to the ignored list.
455  * If no arguments, print the current list of ignored fields.
456  */
457 int
458 igfield(v)
459 	void *v;
460 {
461 	char **list = v;
462 
463 	return ignore1(list, ignore, "ignored");
464 }
465 
466 int
467 saveretfield(v)
468 	void *v;
469 {
470 	char **list = v;
471 
472 	return ignore1(list, saveignore + 1, "retained");
473 }
474 
475 int
476 saveigfield(v)
477 	void *v;
478 {
479 	char **list = v;
480 
481 	return ignore1(list, saveignore, "ignored");
482 }
483 
484 int
485 ignore1(list, tab, which)
486 	char *list[];
487 	struct ignoretab *tab;
488 	char *which;
489 {
490 	char field[LINESIZE];
491 	int h;
492 	struct ignore *igp;
493 	char **ap;
494 
495 	if (*list == NOSTR)
496 		return igshow(tab, which);
497 	for (ap = list; *ap != 0; ap++) {
498 		istrcpy(field, *ap);
499 		if (member(field, tab))
500 			continue;
501 		h = hash(field);
502 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
503 		igp->i_field = calloc((unsigned) strlen(field) + 1,
504 			sizeof (char));
505 		strcpy(igp->i_field, field);
506 		igp->i_link = tab->i_head[h];
507 		tab->i_head[h] = igp;
508 		tab->i_count++;
509 	}
510 	return 0;
511 }
512 
513 /*
514  * Print out all currently retained fields.
515  */
516 int
517 igshow(tab, which)
518 	struct ignoretab *tab;
519 	char *which;
520 {
521 	int h;
522 	struct ignore *igp;
523 	char **ap, **ring;
524 
525 	if (tab->i_count == 0) {
526 		printf("No fields currently being %s.\n", which);
527 		return 0;
528 	}
529 	ring = (char **) salloc((tab->i_count + 1) * sizeof (char *));
530 	ap = ring;
531 	for (h = 0; h < HSHSIZE; h++)
532 		for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
533 			*ap++ = igp->i_field;
534 	*ap = 0;
535 	qsort(ring, tab->i_count, sizeof (char *), igcomp);
536 	for (ap = ring; *ap != 0; ap++)
537 		printf("%s\n", *ap);
538 	return 0;
539 }
540 
541 /*
542  * Compare two names for sorting ignored field list.
543  */
544 static int
545 igcomp(l, r)
546 	const void *l, *r;
547 {
548 	return (strcmp(*(char **)l, *(char **)r));
549 }
550