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