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