xref: /freebsd/usr.bin/mail/cmd2.c (revision f4dc9bf43457515e5c88d1400d4f5ff70a82d9c7)
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  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #ifndef lint
31 #if 0
32 static char sccsid[] = "@(#)cmd2.c	8.1 (Berkeley) 6/6/93";
33 #endif
34 #endif /* not lint */
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
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(int *msgvec)
57 {
58 	struct message *mp;
59 	int *ip, *ip2, list[2], mdot;
60 
61 	if (*msgvec != 0) {
62 
63 		/*
64 		 * If some messages were supplied, find the
65 		 * first applicable one following dot using
66 		 * wrap around.
67 		 */
68 
69 		mdot = dot - &message[0] + 1;
70 
71 		/*
72 		 * Find the first message in the supplied
73 		 * message list which follows dot.
74 		 */
75 
76 		for (ip = msgvec; *ip != 0; ip++)
77 			if (*ip > mdot)
78 				break;
79 		if (*ip == 0)
80 			ip = msgvec;
81 		ip2 = ip;
82 		do {
83 			mp = &message[*ip2 - 1];
84 			if ((mp->m_flag & MDELETED) == 0) {
85 				dot = mp;
86 				goto hitit;
87 			}
88 			if (*ip2 != 0)
89 				ip2++;
90 			if (*ip2 == 0)
91 				ip2 = msgvec;
92 		} while (ip2 != ip);
93 		printf("No messages applicable\n");
94 		return (1);
95 	}
96 
97 	/*
98 	 * If this is the first command, select message 1.
99 	 * Note that this must exist for us to get here at all.
100 	 */
101 
102 	if (!sawcom)
103 		goto hitit;
104 
105 	/*
106 	 * Just find the next good message after dot, no
107 	 * wraparound.
108 	 */
109 
110 	for (mp = dot+1; mp < &message[msgCount]; mp++)
111 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
112 			break;
113 	if (mp >= &message[msgCount]) {
114 		printf("At EOF\n");
115 		return (0);
116 	}
117 	dot = mp;
118 hitit:
119 	/*
120 	 * Print dot.
121 	 */
122 
123 	list[0] = dot - &message[0] + 1;
124 	list[1] = 0;
125 	return (type(list));
126 }
127 
128 /*
129  * Save a message in a file.  Mark the message as saved
130  * so we can discard when the user quits.
131  */
132 int
133 save(void *v)
134 {
135 	char *str = v;
136 
137 	return (save1(str, 1, "save", saveignore));
138 }
139 
140 /*
141  * Copy a message to a file without affected its saved-ness
142  */
143 int
144 copycmd(void *v)
145 {
146 	char *str = v;
147 
148 	return (save1(str, 0, "copy", saveignore));
149 }
150 
151 /*
152  * Save/copy the indicated messages at the end of the passed file name.
153  * If mark is true, mark the message "saved."
154  */
155 int
156 save1(char str[], int mark, const char *cmd, struct ignoretab *ignore)
157 {
158 	struct message *mp;
159 	char *file;
160 	const char *disp;
161 	int f, *msgvec, *ip;
162 	FILE *obuf;
163 
164 	msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
165 	if ((file = snarf(str, &f)) == NULL)
166 		return (1);
167 	if (!f) {
168 		*msgvec = first(0, MMNORM);
169 		if (*msgvec == 0) {
170 			printf("No messages to %s.\n", cmd);
171 			return (1);
172 		}
173 		msgvec[1] = 0;
174 	}
175 	if (f && getmsglist(str, msgvec, 0) < 0)
176 		return (1);
177 	if ((file = expand(file)) == NULL)
178 		return (1);
179 	printf("\"%s\" ", file);
180 	(void)fflush(stdout);
181 	if (access(file, 0) >= 0)
182 		disp = "[Appended]";
183 	else
184 		disp = "[New file]";
185 	if ((obuf = Fopen(file, "a")) == NULL) {
186 		warn((char *)NULL);
187 		return (1);
188 	}
189 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
190 		mp = &message[*ip - 1];
191 		touch(mp);
192 		if (sendmessage(mp, obuf, ignore, NULL) < 0) {
193 			warnx("%s", file);
194 			(void)Fclose(obuf);
195 			return (1);
196 		}
197 		if (mark)
198 			mp->m_flag |= MSAVED;
199 	}
200 	(void)fflush(obuf);
201 	if (ferror(obuf))
202 		warn("%s", file);
203 	(void)Fclose(obuf);
204 	printf("%s\n", disp);
205 	return (0);
206 }
207 
208 /*
209  * Write the indicated messages at the end of the passed
210  * file name, minus header and trailing blank line.
211  */
212 int
213 swrite(char str[])
214 {
215 
216 	return (save1(str, 1, "write", ignoreall));
217 }
218 
219 /*
220  * Snarf the file from the end of the command line and
221  * return a pointer to it.  If there is no file attached,
222  * just return NULL.  Put a null in front of the file
223  * name so that the message list processing won't see it,
224  * unless the file name is the only thing on the line, in
225  * which case, return 0 in the reference flag variable.
226  */
227 
228 char *
229 snarf(char linebuf[], int *flag)
230 {
231 	char *cp;
232 
233 	*flag = 1;
234 	cp = strlen(linebuf) + linebuf - 1;
235 
236 	/*
237 	 * Strip away trailing blanks.
238 	 */
239 
240 	while (cp > linebuf && isspace((unsigned char)*cp))
241 		cp--;
242 	*++cp = '\0';
243 
244 	/*
245 	 * Now search for the beginning of the file name.
246 	 */
247 
248 	while (cp > linebuf && !isspace((unsigned char)*cp))
249 		cp--;
250 	if (*cp == '\0') {
251 		printf("No file specified.\n");
252 		return (NULL);
253 	}
254 	if (isspace((unsigned char)*cp))
255 		*cp++ = '\0';
256 	else
257 		*flag = 0;
258 	return (cp);
259 }
260 
261 /*
262  * Delete messages.
263  */
264 int
265 delete(int msgvec[])
266 {
267 
268 	delm(msgvec);
269 	return (0);
270 }
271 
272 /*
273  * Delete messages, then type the new dot.
274  */
275 int
276 deltype(int msgvec[])
277 {
278 	int list[2];
279 	int lastdot;
280 
281 	lastdot = dot - &message[0] + 1;
282 	if (delm(msgvec) >= 0) {
283 		list[0] = dot - &message[0] + 1;
284 		if (list[0] > lastdot) {
285 			touch(dot);
286 			list[1] = 0;
287 			return (type(list));
288 		}
289 		printf("At EOF\n");
290 	} else
291 		printf("No more messages\n");
292 	return (0);
293 }
294 
295 /*
296  * Delete the indicated messages.
297  * Set dot to some nice place afterwards.
298  * Internal interface.
299  */
300 int
301 delm(int *msgvec)
302 {
303 	struct message *mp;
304 	int *ip, last;
305 
306 	last = 0;
307 	for (ip = msgvec; *ip != 0; ip++) {
308 		mp = &message[*ip - 1];
309 		touch(mp);
310 		mp->m_flag |= MDELETED|MTOUCH;
311 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
312 		last = *ip;
313 	}
314 	if (last != 0) {
315 		dot = &message[last-1];
316 		last = first(0, MDELETED);
317 		if (last != 0) {
318 			dot = &message[last-1];
319 			return (0);
320 		}
321 		else {
322 			dot = &message[0];
323 			return (-1);
324 		}
325 	}
326 
327 	/*
328 	 * Following can't happen -- it keeps lint happy
329 	 */
330 
331 	return (-1);
332 }
333 
334 /*
335  * Undelete the indicated messages.
336  */
337 int
338 undelete_messages(int *msgvec)
339 {
340 	struct message *mp;
341 	int *ip;
342 
343 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
344 		mp = &message[*ip - 1];
345 		touch(mp);
346 		dot = mp;
347 		mp->m_flag &= ~MDELETED;
348 	}
349 	return (0);
350 }
351 
352 /*
353  * Interactively dump core on "core"
354  */
355 int
356 core(void)
357 {
358 	int pid;
359 
360 	switch (pid = fork()) {
361 	case -1:
362 		warn("fork");
363 		return (1);
364 	case 0:
365 		abort();
366 		_exit(1);
367 	}
368 	printf("Okie dokie");
369 	(void)fflush(stdout);
370 	wait_child(pid);
371 	if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status))
372 		printf(" -- Core dumped.\n");
373 	else
374 		printf(" -- Can't dump core.\n");
375 	return (0);
376 }
377 
378 /*
379  * Clobber as many bytes of stack as the user requests.
380  */
381 int
382 clobber(char **argv)
383 {
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(char *list[])
416 {
417 
418 	return (ignore1(list, ignore + 1, "retained"));
419 }
420 
421 /*
422  * Add the given header fields to the ignored list.
423  * If no arguments, print the current list of ignored fields.
424  */
425 int
426 igfield(char *list[])
427 {
428 
429 	return (ignore1(list, ignore, "ignored"));
430 }
431 
432 int
433 saveretfield(char *list[])
434 {
435 
436 	return (ignore1(list, saveignore + 1, "retained"));
437 }
438 
439 int
440 saveigfield(char *list[])
441 {
442 
443 	return (ignore1(list, saveignore, "ignored"));
444 }
445 
446 int
447 ignore1(char *list[], struct ignoretab *tab, const char *which)
448 {
449 	char field[LINESIZE];
450 	int h;
451 	struct ignore *igp;
452 	char **ap;
453 
454 	if (*list == NULL)
455 		return (igshow(tab, which));
456 	for (ap = list; *ap != 0; ap++) {
457 		istrncpy(field, *ap, sizeof(field));
458 		if (member(field, tab))
459 			continue;
460 		h = hash(field);
461 		igp = calloc(1, sizeof(struct ignore));
462 		igp->i_field = calloc((unsigned)strlen(field) + 1,
463 		    sizeof(char));
464 		strcpy(igp->i_field, field);
465 		igp->i_link = tab->i_head[h];
466 		tab->i_head[h] = igp;
467 		tab->i_count++;
468 	}
469 	return (0);
470 }
471 
472 /*
473  * Print out all currently retained fields.
474  */
475 int
476 igshow(struct ignoretab *tab, const char *which)
477 {
478 	int h;
479 	struct ignore *igp;
480 	char **ap, **ring;
481 
482 	if (tab->i_count == 0) {
483 		printf("No fields currently being %s.\n", which);
484 		return (0);
485 	}
486 	ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
487 	ap = ring;
488 	for (h = 0; h < HSHSIZE; h++)
489 		for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link)
490 			*ap++ = igp->i_field;
491 	*ap = 0;
492 	qsort(ring, tab->i_count, sizeof(char *), igcomp);
493 	for (ap = ring; *ap != 0; ap++)
494 		printf("%s\n", *ap);
495 	return (0);
496 }
497 
498 /*
499  * Compare two names for sorting ignored field list.
500  */
501 int
502 igcomp(const void *l, const void *r)
503 {
504 
505 	return (strcmp(*(const char **)l, *(const char **)r));
506 }
507