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