xref: /original-bsd/usr.bin/mail/cmd2.c (revision 53787e02)
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.2 (Berkeley) 06/21/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 - 2;
234 		readline(mesf, linebuf);
235 		while (t-- > 0) {
236 			fgets(linebuf, BUFSIZ, mesf);
237 			fputs(linebuf, obuf);
238 			cc += strlen(linebuf);
239 		}
240 		lc += mp->m_lines - 2;
241 		mp->m_flag |= MSAVED;
242 	}
243 	fflush(obuf);
244 	if (ferror(obuf))
245 		perror(file);
246 	fclose(obuf);
247 	printf("%s %d/%d\n", disp, lc, cc);
248 	return(0);
249 }
250 
251 /*
252  * Snarf the file from the end of the command line and
253  * return a pointer to it.  If there is no file attached,
254  * just return NOSTR.  Put a null in front of the file
255  * name so that the message list processing won't see it,
256  * unless the file name is the only thing on the line, in
257  * which case, return 0 in the reference flag variable.
258  */
259 
260 char *
261 snarf(linebuf, flag)
262 	char linebuf[];
263 	int *flag;
264 {
265 	register char *cp;
266 
267 	*flag = 1;
268 	cp = strlen(linebuf) + linebuf - 1;
269 
270 	/*
271 	 * Strip away trailing blanks.
272 	 */
273 
274 	while (*cp == ' ' && cp > linebuf)
275 		cp--;
276 	*++cp = 0;
277 
278 	/*
279 	 * Now search for the beginning of the file name.
280 	 */
281 
282 	while (cp > linebuf && !any(*cp, "\t "))
283 		cp--;
284 	if (*cp == '\0') {
285 		printf("No file specified.\n");
286 		return(NOSTR);
287 	}
288 	if (any(*cp, " \t"))
289 		*cp++ = 0;
290 	else
291 		*flag = 0;
292 	return(cp);
293 }
294 
295 /*
296  * Delete messages.
297  */
298 
299 delete(msgvec)
300 	int msgvec[];
301 {
302 	return(delm(msgvec));
303 }
304 
305 /*
306  * Delete messages, then type the new dot.
307  */
308 
309 deltype(msgvec)
310 	int msgvec[];
311 {
312 	int list[2];
313 	int lastdot;
314 
315 	lastdot = dot - &message[0] + 1;
316 	if (delm(msgvec) >= 0) {
317 		list[0] = dot - &message[0];
318 		list[0]++;
319 		if (list[0] > lastdot) {
320 			touch(list[0]);
321 			list[1] = NULL;
322 			return(type(list));
323 		}
324 		printf("At EOF\n");
325 		return(0);
326 	}
327 	else {
328 		printf("No more messages\n");
329 		return(0);
330 	}
331 }
332 
333 /*
334  * Delete the indicated messages.
335  * Set dot to some nice place afterwards.
336  * Internal interface.
337  */
338 
339 delm(msgvec)
340 	int *msgvec;
341 {
342 	register struct message *mp;
343 	register *ip, mesg;
344 	int last;
345 
346 	last = NULL;
347 	for (ip = msgvec; *ip != NULL; ip++) {
348 		mesg = *ip;
349 		touch(mesg);
350 		mp = &message[mesg-1];
351 		mp->m_flag |= MDELETED|MTOUCH;
352 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
353 		last = mesg;
354 	}
355 	if (last != NULL) {
356 		dot = &message[last-1];
357 		last = first(0, MDELETED);
358 		if (last != NULL) {
359 			dot = &message[last-1];
360 			return(0);
361 		}
362 		else {
363 			dot = &message[0];
364 			return(-1);
365 		}
366 	}
367 
368 	/*
369 	 * Following can't happen -- it keeps lint happy
370 	 */
371 
372 	return(-1);
373 }
374 
375 /*
376  * Undelete the indicated messages.
377  */
378 
379 undelete(msgvec)
380 	int *msgvec;
381 {
382 	register struct message *mp;
383 	register *ip, mesg;
384 
385 	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
386 		mesg = *ip;
387 		if (mesg == 0)
388 			return;
389 		touch(mesg);
390 		mp = &message[mesg-1];
391 		dot = mp;
392 		mp->m_flag &= ~MDELETED;
393 	}
394 }
395 
396 /*
397  * Interactively dump core on "core"
398  */
399 
400 core()
401 {
402 	register int pid;
403 	int status;
404 
405 	if ((pid = vfork()) == -1) {
406 		perror("fork");
407 		return(1);
408 	}
409 	if (pid == 0) {
410 		sigchild();
411 		abort();
412 		_exit(1);
413 	}
414 	printf("Okie dokie");
415 	fflush(stdout);
416 	while (wait(&status) != pid)
417 		;
418 	if (status & 0200)
419 		printf(" -- Core dumped\n");
420 	else
421 		printf("\n");
422 }
423 
424 /*
425  * Clobber as many bytes of stack as the user requests.
426  */
427 clobber(argv)
428 	char **argv;
429 {
430 	register int times;
431 
432 	if (argv[0] == 0)
433 		times = 1;
434 	else
435 		times = (atoi(argv[0]) + 511) / 512;
436 	clob1(times);
437 }
438 
439 /*
440  * Clobber the stack.
441  */
442 clob1(n)
443 {
444 	char buf[512];
445 	register char *cp;
446 
447 	if (n <= 0)
448 		return;
449 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
450 		;
451 	clob1(n - 1);
452 }
453 
454 /*
455  * Add the given header fields to the retained list.
456  * If no arguments, print the current list of retained fields.
457  */
458 retfield(list)
459 	char *list[];
460 {
461 	char field[BUFSIZ];
462 	register int h;
463 	register struct ignore *igp;
464 	char **ap;
465 
466 	if (argcount(list) == 0)
467 		return(retshow());
468 	for (ap = list; *ap != 0; ap++) {
469 		istrcpy(field, *ap);
470 
471 		if (member(field, retain))
472 			continue;
473 
474 		h = hash(field);
475 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
476 		igp->i_field = calloc(strlen(field) + 1, sizeof (char));
477 		strcpy(igp->i_field, field);
478 		igp->i_link = retain[h];
479 		retain[h] = igp;
480 		nretained++;
481 	}
482 	return(0);
483 }
484 
485 /*
486  * Print out all currently retained fields.
487  */
488 retshow()
489 {
490 	register int h, count;
491 	struct ignore *igp;
492 	char **ap, **ring;
493 	int igcomp();
494 
495 	count = 0;
496 	for (h = 0; h < HSHSIZE; h++)
497 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
498 			count++;
499 	if (count == 0) {
500 		printf("No fields currently being retained.\n");
501 		return(0);
502 	}
503 	ring = (char **) salloc((count + 1) * sizeof (char *));
504 	ap = ring;
505 	for (h = 0; h < HSHSIZE; h++)
506 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
507 			*ap++ = igp->i_field;
508 	*ap = 0;
509 	qsort(ring, count, sizeof (char *), igcomp);
510 	for (ap = ring; *ap != 0; ap++)
511 		printf("%s\n", *ap);
512 	return(0);
513 }
514 
515 /*
516  * Add the given header fields to the ignored list.
517  * If no arguments, print the current list of ignored fields.
518  */
519 igfield(list)
520 	char *list[];
521 {
522 	char field[BUFSIZ];
523 	register int h;
524 	register struct ignore *igp;
525 	char **ap;
526 
527 	if (argcount(list) == 0)
528 		return(igshow());
529 	for (ap = list; *ap != 0; ap++) {
530 		if (isign(*ap))
531 			continue;
532 		istrcpy(field, *ap);
533 		h = hash(field);
534 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
535 		igp->i_field = calloc(strlen(field) + 1, sizeof (char));
536 		strcpy(igp->i_field, field);
537 		igp->i_link = ignore[h];
538 		ignore[h] = igp;
539 	}
540 	return(0);
541 }
542 
543 /*
544  * Print out all currently ignored fields.
545  */
546 igshow()
547 {
548 	register int h, count;
549 	struct ignore *igp;
550 	char **ap, **ring;
551 	int igcomp();
552 
553 	count = 0;
554 	for (h = 0; h < HSHSIZE; h++)
555 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
556 			count++;
557 	if (count == 0) {
558 		printf("No fields currently being ignored.\n");
559 		return(0);
560 	}
561 	ring = (char **) salloc((count + 1) * sizeof (char *));
562 	ap = ring;
563 	for (h = 0; h < HSHSIZE; h++)
564 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
565 			*ap++ = igp->i_field;
566 	*ap = 0;
567 	qsort(ring, count, sizeof (char *), igcomp);
568 	for (ap = ring; *ap != 0; ap++)
569 		printf("%s\n", *ap);
570 	return(0);
571 }
572 
573 /*
574  * Compare two names for sorting ignored field list.
575  */
576 igcomp(l, r)
577 	char **l, **r;
578 {
579 
580 	return(strcmp(*l, *r));
581 }
582