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