xref: /freebsd/usr.bin/mail/cmd2.c (revision 0640d357f29fb1c0daaaffadd0416c5981413afd)
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 
34 #ifndef lint
35 static char sccsid[] = "@(#)cmd2.c	8.1 (Berkeley) 6/6/93";
36 #endif /* not lint */
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 /*
49  * If any arguments were given, go to the next applicable argument
50  * following dot, otherwise, go to the next applicable message.
51  * If given as first command with no arguments, print first message.
52  */
53 int
54 next(msgvec)
55 	int *msgvec;
56 {
57 	register struct message *mp;
58 	register int *ip, *ip2;
59 	int 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(str)
134 	char str[];
135 {
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(str)
145 	char str[];
146 {
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(str, mark, cmd, ignore)
157 	char str[];
158 	int mark;
159 	char *cmd;
160 	struct ignoretab *ignore;
161 {
162 	register int *ip;
163 	register struct message *mp;
164 	char *file, *disp;
165 	int f, *msgvec;
166 	FILE *obuf;
167 
168 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
169 	if ((file = snarf(str, &f)) == NOSTR)
170 		return(1);
171 	if (!f) {
172 		*msgvec = first(0, MMNORM);
173 		if (*msgvec == 0) {
174 			printf("No messages to %s.\n", cmd);
175 			return(1);
176 		}
177 		msgvec[1] = 0;
178 	}
179 	if (f && getmsglist(str, msgvec, 0) < 0)
180 		return(1);
181 	if ((file = expand(file)) == NOSTR)
182 		return(1);
183 	printf("\"%s\" ", file);
184 	fflush(stdout);
185 	if (access(file, 0) >= 0)
186 		disp = "[Appended]";
187 	else
188 		disp = "[New file]";
189 	if ((obuf = Fopen(file, "a")) == NULL) {
190 		perror(NOSTR);
191 		return(1);
192 	}
193 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
194 		mp = &message[*ip - 1];
195 		touch(mp);
196 		if (send(mp, obuf, ignore, NOSTR) < 0) {
197 			perror(file);
198 			Fclose(obuf);
199 			return(1);
200 		}
201 		if (mark)
202 			mp->m_flag |= MSAVED;
203 	}
204 	fflush(obuf);
205 	if (ferror(obuf))
206 		perror(file);
207 	Fclose(obuf);
208 	printf("%s\n", disp);
209 	return(0);
210 }
211 
212 /*
213  * Write the indicated messages at the end of the passed
214  * file name, minus header and trailing blank line.
215  */
216 int
217 swrite(str)
218 	char str[];
219 {
220 
221 	return save1(str, 1, "write", ignoreall);
222 }
223 
224 /*
225  * Snarf the file from the end of the command line and
226  * return a pointer to it.  If there is no file attached,
227  * just return NOSTR.  Put a null in front of the file
228  * name so that the message list processing won't see it,
229  * unless the file name is the only thing on the line, in
230  * which case, return 0 in the reference flag variable.
231  */
232 
233 char *
234 snarf(linebuf, flag)
235 	char linebuf[];
236 	int *flag;
237 {
238 	register char *cp;
239 
240 	*flag = 1;
241 	cp = strlen(linebuf) + linebuf - 1;
242 
243 	/*
244 	 * Strip away trailing blanks.
245 	 */
246 
247 	while (cp > linebuf && isspace(*cp))
248 		cp--;
249 	*++cp = 0;
250 
251 	/*
252 	 * Now search for the beginning of the file name.
253 	 */
254 
255 	while (cp > linebuf && !isspace(*cp))
256 		cp--;
257 	if (*cp == '\0') {
258 		printf("No file specified.\n");
259 		return(NOSTR);
260 	}
261 	if (isspace(*cp))
262 		*cp++ = 0;
263 	else
264 		*flag = 0;
265 	return(cp);
266 }
267 
268 /*
269  * Delete messages.
270  */
271 int
272 delete(msgvec)
273 	int msgvec[];
274 {
275 	delm(msgvec);
276 	return 0;
277 }
278 
279 /*
280  * Delete messages, then type the new dot.
281  */
282 int
283 deltype(msgvec)
284 	int msgvec[];
285 {
286 	int list[2];
287 	int lastdot;
288 
289 	lastdot = dot - &message[0] + 1;
290 	if (delm(msgvec) >= 0) {
291 		list[0] = dot - &message[0] + 1;
292 		if (list[0] > lastdot) {
293 			touch(dot);
294 			list[1] = 0;
295 			return(type(list));
296 		}
297 		printf("At EOF\n");
298 	} else
299 		printf("No more messages\n");
300 	return(0);
301 }
302 
303 /*
304  * Delete the indicated messages.
305  * Set dot to some nice place afterwards.
306  * Internal interface.
307  */
308 int
309 delm(msgvec)
310 	int *msgvec;
311 {
312 	register struct message *mp;
313 	register *ip;
314 	int last;
315 
316 	last = 0;
317 	for (ip = msgvec; *ip != 0; ip++) {
318 		mp = &message[*ip - 1];
319 		touch(mp);
320 		mp->m_flag |= MDELETED|MTOUCH;
321 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
322 		last = *ip;
323 	}
324 	if (last != 0) {
325 		dot = &message[last-1];
326 		last = first(0, MDELETED);
327 		if (last != 0) {
328 			dot = &message[last-1];
329 			return(0);
330 		}
331 		else {
332 			dot = &message[0];
333 			return(-1);
334 		}
335 	}
336 
337 	/*
338 	 * Following can't happen -- it keeps lint happy
339 	 */
340 
341 	return(-1);
342 }
343 
344 /*
345  * Undelete the indicated messages.
346  */
347 int
348 undelete_messages(msgvec)
349 	int *msgvec;
350 {
351 	register struct message *mp;
352 	register *ip;
353 
354 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
355 		mp = &message[*ip - 1];
356 		touch(mp);
357 		dot = mp;
358 		mp->m_flag &= ~MDELETED;
359 	}
360 	return 0;
361 }
362 
363 /*
364  * Interactively dump core on "core"
365  */
366 int
367 core()
368 {
369 	int pid;
370 	extern union wait wait_status;
371 
372 	switch (pid = fork()) {
373 	case -1:
374 		perror("fork");
375 		return(1);
376 	case 0:
377 		abort();
378 		_exit(1);
379 	}
380 	printf("Okie dokie");
381 	fflush(stdout);
382 	wait_child(pid);
383 	if (wait_status.w_coredump)
384 		printf(" -- Core dumped.\n");
385 	else
386 		printf(" -- Can't dump core.\n");
387 	return 0;
388 }
389 
390 /*
391  * Clobber as many bytes of stack as the user requests.
392  */
393 int
394 clobber(argv)
395 	char **argv;
396 {
397 	register int times;
398 
399 	if (argv[0] == 0)
400 		times = 1;
401 	else
402 		times = (atoi(argv[0]) + 511) / 512;
403 	clob1(times);
404 	return 0;
405 }
406 
407 /*
408  * Clobber the stack.
409  */
410 void
411 clob1(n)
412 	int n;
413 {
414 	char buf[512];
415 	register char *cp;
416 
417 	if (n <= 0)
418 		return;
419 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
420 		;
421 	clob1(n - 1);
422 }
423 
424 /*
425  * Add the given header fields to the retained list.
426  * If no arguments, print the current list of retained fields.
427  */
428 int
429 retfield(list)
430 	char *list[];
431 {
432 
433 	return ignore1(list, ignore + 1, "retained");
434 }
435 
436 /*
437  * Add the given header fields to the ignored list.
438  * If no arguments, print the current list of ignored fields.
439  */
440 int
441 igfield(list)
442 	char *list[];
443 {
444 
445 	return ignore1(list, ignore, "ignored");
446 }
447 
448 int
449 saveretfield(list)
450 	char *list[];
451 {
452 
453 	return ignore1(list, saveignore + 1, "retained");
454 }
455 
456 int
457 saveigfield(list)
458 	char *list[];
459 {
460 
461 	return ignore1(list, saveignore, "ignored");
462 }
463 
464 int
465 ignore1(list, tab, which)
466 	char *list[];
467 	struct ignoretab *tab;
468 	char *which;
469 {
470 	char field[BUFSIZ];
471 	register int h;
472 	register struct ignore *igp;
473 	char **ap;
474 
475 	if (*list == NOSTR)
476 		return igshow(tab, which);
477 	for (ap = list; *ap != 0; ap++) {
478 		istrcpy(field, *ap);
479 		if (member(field, tab))
480 			continue;
481 		h = hash(field);
482 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
483 		igp->i_field = calloc((unsigned) strlen(field) + 1,
484 			sizeof (char));
485 		strcpy(igp->i_field, field);
486 		igp->i_link = tab->i_head[h];
487 		tab->i_head[h] = igp;
488 		tab->i_count++;
489 	}
490 	return 0;
491 }
492 
493 /*
494  * Print out all currently retained fields.
495  */
496 int
497 igshow(tab, which)
498 	struct ignoretab *tab;
499 	char *which;
500 {
501 	register int h;
502 	struct ignore *igp;
503 	char **ap, **ring;
504 	int igcomp();
505 
506 	if (tab->i_count == 0) {
507 		printf("No fields currently being %s.\n", which);
508 		return 0;
509 	}
510 	ring = (char **) salloc((tab->i_count + 1) * sizeof (char *));
511 	ap = ring;
512 	for (h = 0; h < HSHSIZE; h++)
513 		for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
514 			*ap++ = igp->i_field;
515 	*ap = 0;
516 	qsort(ring, tab->i_count, sizeof (char *), igcomp);
517 	for (ap = ring; *ap != 0; ap++)
518 		printf("%s\n", *ap);
519 	return 0;
520 }
521 
522 /*
523  * Compare two names for sorting ignored field list.
524  */
525 int
526 igcomp(l, r)
527 	const void *l, *r;
528 {
529 	return (strcmp(*(char **)l, *(char **)r));
530 }
531