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