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