xref: /original-bsd/usr.bin/mail/cmd2.c (revision 9c06e5c6)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)cmd2.c	5.3 (Berkeley) 09/10/85";
9 #endif not lint
10 
11 #include "rcv.h"
12 #include <sys/stat.h>
13 
14 /*
15  * Mail -- a mail program
16  *
17  * More user commands.
18  */
19 
20 /*
21  * If any arguments were given, go to the next applicable argument
22  * following dot, otherwise, go to the next applicable message.
23  * If given as first command with no arguments, print first message.
24  */
25 
26 next(msgvec)
27 	int *msgvec;
28 {
29 	register struct message *mp;
30 	register int *ip, *ip2;
31 	int list[2], mdot;
32 
33 	if (*msgvec != NULL) {
34 
35 		/*
36 		 * If some messages were supplied, find the
37 		 * first applicable one following dot using
38 		 * wrap around.
39 		 */
40 
41 		mdot = dot - &message[0] + 1;
42 
43 		/*
44 		 * Find the first message in the supplied
45 		 * message list which follows dot.
46 		 */
47 
48 		for (ip = msgvec; *ip != NULL; ip++)
49 			if (*ip > mdot)
50 				break;
51 		if (*ip == NULL)
52 			ip = msgvec;
53 		ip2 = ip;
54 		do {
55 			mp = &message[*ip2 - 1];
56 			if ((mp->m_flag & MDELETED) == 0) {
57 				dot = mp;
58 				goto hitit;
59 			}
60 			if (*ip2 != NULL)
61 				ip2++;
62 			if (*ip2 == NULL)
63 				ip2 = msgvec;
64 		} while (ip2 != ip);
65 		printf("No messages applicable\n");
66 		return(1);
67 	}
68 
69 	/*
70 	 * If this is the first command, select message 1.
71 	 * Note that this must exist for us to get here at all.
72 	 */
73 
74 	if (!sawcom)
75 		goto hitit;
76 
77 	/*
78 	 * Just find the next good message after dot, no
79 	 * wraparound.
80 	 */
81 
82 	for (mp = dot+1; mp < &message[msgCount]; mp++)
83 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
84 			break;
85 	if (mp >= &message[msgCount]) {
86 		printf("At EOF\n");
87 		return(0);
88 	}
89 	dot = mp;
90 hitit:
91 	/*
92 	 * Print dot.
93 	 */
94 
95 	list[0] = dot - &message[0] + 1;
96 	list[1] = NULL;
97 	return(type(list));
98 }
99 
100 /*
101  * Save a message in a file.  Mark the message as saved
102  * so we can discard when the user quits.
103  */
104 save(str)
105 	char str[];
106 {
107 
108 	return(save1(str, 1));
109 }
110 
111 /*
112  * Copy a message to a file without affected its saved-ness
113  */
114 copycmd(str)
115 	char str[];
116 {
117 
118 	return(save1(str, 0));
119 }
120 
121 /*
122  * Save/copy the indicated messages at the end of the passed file name.
123  * If mark is true, mark the message "saved."
124  */
125 save1(str, mark)
126 	char str[];
127 {
128 	register int *ip, mesg;
129 	register struct message *mp;
130 	char *file, *disp, *cmd;
131 	int f, *msgvec, lc, t;
132 	long cc;
133 	FILE *obuf;
134 	struct stat statb;
135 
136 	cmd = mark ? "save" : "copy";
137 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
138 	if ((file = snarf(str, &f)) == NOSTR)
139 		return(1);
140 	if (!f) {
141 		*msgvec = first(0, MMNORM);
142 		if (*msgvec == NULL) {
143 			printf("No messages to %s.\n", cmd);
144 			return(1);
145 		}
146 		msgvec[1] = NULL;
147 	}
148 	if (f && getmsglist(str, msgvec, 0) < 0)
149 		return(1);
150 	if ((file = expand(file)) == NOSTR)
151 		return(1);
152 	printf("\"%s\" ", file);
153 	fflush(stdout);
154 	if (stat(file, &statb) >= 0)
155 		disp = "[Appended]";
156 	else
157 		disp = "[New file]";
158 	if ((obuf = fopen(file, "a")) == NULL) {
159 		perror(NOSTR);
160 		return(1);
161 	}
162 	cc = 0L;
163 	lc = 0;
164 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
165 		mesg = *ip;
166 		touch(mesg);
167 		mp = &message[mesg-1];
168 		if ((t = send(mp, obuf, 0)) < 0) {
169 			perror(file);
170 			fclose(obuf);
171 			return(1);
172 		}
173 		lc += t;
174 		cc += mp->m_size;
175 		if (mark)
176 			mp->m_flag |= MSAVED;
177 	}
178 	fflush(obuf);
179 	if (ferror(obuf))
180 		perror(file);
181 	fclose(obuf);
182 	printf("%s %d/%ld\n", disp, lc, cc);
183 	return(0);
184 }
185 
186 /*
187  * Write the indicated messages at the end of the passed
188  * file name, minus header and trailing blank line.
189  */
190 
191 swrite(str)
192 	char str[];
193 {
194 	register int *ip, mesg;
195 	register struct message *mp;
196 	register char *file, *disp;
197 	char linebuf[BUFSIZ];
198 	int f, *msgvec, lc, cc, t;
199 	FILE *obuf, *mesf;
200 	struct stat statb;
201 
202 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
203 	if ((file = snarf(str, &f)) == NOSTR)
204 		return(1);
205 	if ((file = expand(file)) == NOSTR)
206 		return(1);
207 	if (!f) {
208 		*msgvec = first(0, MMNORM);
209 		if (*msgvec == NULL) {
210 			printf("No messages to write.\n");
211 			return(1);
212 		}
213 		msgvec[1] = NULL;
214 	}
215 	if (f && getmsglist(str, msgvec, 0) < 0)
216 		return(1);
217 	printf("\"%s\" ", file);
218 	fflush(stdout);
219 	if (stat(file, &statb) >= 0)
220 		disp = "[Appended]";
221 	else
222 		disp = "[New file]";
223 	if ((obuf = fopen(file, "a")) == NULL) {
224 		perror(NOSTR);
225 		return(1);
226 	}
227 	cc = lc = 0;
228 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
229 		mesg = *ip;
230 		touch(mesg);
231 		mp = &message[mesg-1];
232 		mesf = setinput(mp);
233 		t = mp->m_lines - 1;
234 		while (t-- > 0) {
235 			readline(mesf, linebuf);
236 			if (blankline(linebuf))
237 				break;
238 		}
239 		while (t-- > 0) {
240 			fgets(linebuf, BUFSIZ, mesf);
241 			fputs(linebuf, obuf);
242 			cc += strlen(linebuf);
243 		}
244 		lc += mp->m_lines - 2;
245 		mp->m_flag |= MSAVED;
246 	}
247 	fflush(obuf);
248 	if (ferror(obuf))
249 		perror(file);
250 	fclose(obuf);
251 	printf("%s %d/%d\n", disp, lc, cc);
252 	return(0);
253 }
254 
255 /*
256  * Snarf the file from the end of the command line and
257  * return a pointer to it.  If there is no file attached,
258  * just return NOSTR.  Put a null in front of the file
259  * name so that the message list processing won't see it,
260  * unless the file name is the only thing on the line, in
261  * which case, return 0 in the reference flag variable.
262  */
263 
264 char *
265 snarf(linebuf, flag)
266 	char linebuf[];
267 	int *flag;
268 {
269 	register char *cp;
270 
271 	*flag = 1;
272 	cp = strlen(linebuf) + linebuf - 1;
273 
274 	/*
275 	 * Strip away trailing blanks.
276 	 */
277 
278 	while (*cp == ' ' && cp > linebuf)
279 		cp--;
280 	*++cp = 0;
281 
282 	/*
283 	 * Now search for the beginning of the file name.
284 	 */
285 
286 	while (cp > linebuf && !any(*cp, "\t "))
287 		cp--;
288 	if (*cp == '\0') {
289 		printf("No file specified.\n");
290 		return(NOSTR);
291 	}
292 	if (any(*cp, " \t"))
293 		*cp++ = 0;
294 	else
295 		*flag = 0;
296 	return(cp);
297 }
298 
299 /*
300  * Delete messages.
301  */
302 
303 delete(msgvec)
304 	int msgvec[];
305 {
306 	return(delm(msgvec));
307 }
308 
309 /*
310  * Delete messages, then type the new dot.
311  */
312 
313 deltype(msgvec)
314 	int msgvec[];
315 {
316 	int list[2];
317 	int lastdot;
318 
319 	lastdot = dot - &message[0] + 1;
320 	if (delm(msgvec) >= 0) {
321 		list[0] = dot - &message[0];
322 		list[0]++;
323 		if (list[0] > lastdot) {
324 			touch(list[0]);
325 			list[1] = NULL;
326 			return(type(list));
327 		}
328 		printf("At EOF\n");
329 		return(0);
330 	}
331 	else {
332 		printf("No more messages\n");
333 		return(0);
334 	}
335 }
336 
337 /*
338  * Delete the indicated messages.
339  * Set dot to some nice place afterwards.
340  * Internal interface.
341  */
342 
343 delm(msgvec)
344 	int *msgvec;
345 {
346 	register struct message *mp;
347 	register *ip, mesg;
348 	int last;
349 
350 	last = NULL;
351 	for (ip = msgvec; *ip != NULL; ip++) {
352 		mesg = *ip;
353 		touch(mesg);
354 		mp = &message[mesg-1];
355 		mp->m_flag |= MDELETED|MTOUCH;
356 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
357 		last = mesg;
358 	}
359 	if (last != NULL) {
360 		dot = &message[last-1];
361 		last = first(0, MDELETED);
362 		if (last != NULL) {
363 			dot = &message[last-1];
364 			return(0);
365 		}
366 		else {
367 			dot = &message[0];
368 			return(-1);
369 		}
370 	}
371 
372 	/*
373 	 * Following can't happen -- it keeps lint happy
374 	 */
375 
376 	return(-1);
377 }
378 
379 /*
380  * Undelete the indicated messages.
381  */
382 
383 undelete(msgvec)
384 	int *msgvec;
385 {
386 	register struct message *mp;
387 	register *ip, mesg;
388 
389 	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
390 		mesg = *ip;
391 		if (mesg == 0)
392 			return;
393 		touch(mesg);
394 		mp = &message[mesg-1];
395 		dot = mp;
396 		mp->m_flag &= ~MDELETED;
397 	}
398 }
399 
400 /*
401  * Interactively dump core on "core"
402  */
403 
404 core()
405 {
406 	register int pid;
407 	int status;
408 
409 	if ((pid = vfork()) == -1) {
410 		perror("fork");
411 		return(1);
412 	}
413 	if (pid == 0) {
414 		sigchild();
415 		abort();
416 		_exit(1);
417 	}
418 	printf("Okie dokie");
419 	fflush(stdout);
420 	while (wait(&status) != pid)
421 		;
422 	if (status & 0200)
423 		printf(" -- Core dumped\n");
424 	else
425 		printf("\n");
426 }
427 
428 /*
429  * Clobber as many bytes of stack as the user requests.
430  */
431 clobber(argv)
432 	char **argv;
433 {
434 	register int times;
435 
436 	if (argv[0] == 0)
437 		times = 1;
438 	else
439 		times = (atoi(argv[0]) + 511) / 512;
440 	clob1(times);
441 }
442 
443 /*
444  * Clobber the stack.
445  */
446 clob1(n)
447 {
448 	char buf[512];
449 	register char *cp;
450 
451 	if (n <= 0)
452 		return;
453 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
454 		;
455 	clob1(n - 1);
456 }
457 
458 /*
459  * Add the given header fields to the retained list.
460  * If no arguments, print the current list of retained fields.
461  */
462 retfield(list)
463 	char *list[];
464 {
465 	char field[BUFSIZ];
466 	register int h;
467 	register struct ignore *igp;
468 	char **ap;
469 
470 	if (argcount(list) == 0)
471 		return(retshow());
472 	for (ap = list; *ap != 0; ap++) {
473 		istrcpy(field, *ap);
474 
475 		if (member(field, retain))
476 			continue;
477 
478 		h = hash(field);
479 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
480 		igp->i_field = calloc(strlen(field) + 1, sizeof (char));
481 		strcpy(igp->i_field, field);
482 		igp->i_link = retain[h];
483 		retain[h] = igp;
484 		nretained++;
485 	}
486 	return(0);
487 }
488 
489 /*
490  * Print out all currently retained fields.
491  */
492 retshow()
493 {
494 	register int h, count;
495 	struct ignore *igp;
496 	char **ap, **ring;
497 	int igcomp();
498 
499 	count = 0;
500 	for (h = 0; h < HSHSIZE; h++)
501 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
502 			count++;
503 	if (count == 0) {
504 		printf("No fields currently being retained.\n");
505 		return(0);
506 	}
507 	ring = (char **) salloc((count + 1) * sizeof (char *));
508 	ap = ring;
509 	for (h = 0; h < HSHSIZE; h++)
510 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
511 			*ap++ = igp->i_field;
512 	*ap = 0;
513 	qsort(ring, count, sizeof (char *), igcomp);
514 	for (ap = ring; *ap != 0; ap++)
515 		printf("%s\n", *ap);
516 	return(0);
517 }
518 
519 /*
520  * Add the given header fields to the ignored list.
521  * If no arguments, print the current list of ignored fields.
522  */
523 igfield(list)
524 	char *list[];
525 {
526 	char field[BUFSIZ];
527 	register int h;
528 	register struct ignore *igp;
529 	char **ap;
530 
531 	if (argcount(list) == 0)
532 		return(igshow());
533 	for (ap = list; *ap != 0; ap++) {
534 		if (isign(*ap))
535 			continue;
536 		istrcpy(field, *ap);
537 		h = hash(field);
538 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
539 		igp->i_field = calloc(strlen(field) + 1, sizeof (char));
540 		strcpy(igp->i_field, field);
541 		igp->i_link = ignore[h];
542 		ignore[h] = igp;
543 	}
544 	return(0);
545 }
546 
547 /*
548  * Print out all currently ignored fields.
549  */
550 igshow()
551 {
552 	register int h, count;
553 	struct ignore *igp;
554 	char **ap, **ring;
555 	int igcomp();
556 
557 	count = 0;
558 	for (h = 0; h < HSHSIZE; h++)
559 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
560 			count++;
561 	if (count == 0) {
562 		printf("No fields currently being ignored.\n");
563 		return(0);
564 	}
565 	ring = (char **) salloc((count + 1) * sizeof (char *));
566 	ap = ring;
567 	for (h = 0; h < HSHSIZE; h++)
568 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
569 			*ap++ = igp->i_field;
570 	*ap = 0;
571 	qsort(ring, count, sizeof (char *), igcomp);
572 	for (ap = ring; *ap != 0; ap++)
573 		printf("%s\n", *ap);
574 	return(0);
575 }
576 
577 /*
578  * Compare two names for sorting ignored field list.
579  */
580 igcomp(l, r)
581 	char **l, **r;
582 {
583 
584 	return(strcmp(*l, *r));
585 }
586