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