xref: /csrg-svn/usr.bin/mail/cmd2.c (revision 34905)
122445Sdist /*
222445Sdist  * Copyright (c) 1980 Regents of the University of California.
333499Sbostic  * All rights reserved.
433499Sbostic  *
533499Sbostic  * Redistribution and use in source and binary forms are permitted
6*34905Sbostic  * provided that the above copyright notice and this paragraph are
7*34905Sbostic  * duplicated in all such forms and that any documentation,
8*34905Sbostic  * advertising materials, and other materials related to such
9*34905Sbostic  * distribution and use acknowledge that the software was developed
10*34905Sbostic  * by the University of California, Berkeley.  The name of the
11*34905Sbostic  * University may not be used to endorse or promote products derived
12*34905Sbostic  * from this software without specific prior written permission.
13*34905Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34905Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34905Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1622445Sdist  */
1722445Sdist 
18*34905Sbostic #ifndef lint
19*34905Sbostic static char sccsid[] = "@(#)cmd2.c	5.7 (Berkeley) 06/29/88";
20*34905Sbostic #endif /* not lint */
211224Skas 
221224Skas #include "rcv.h"
231224Skas #include <sys/stat.h>
2431142Sedward #include <sys/wait.h>
251224Skas 
261224Skas /*
271224Skas  * Mail -- a mail program
281224Skas  *
291224Skas  * More user commands.
301224Skas  */
311224Skas 
321224Skas /*
331224Skas  * If any arguments were given, go to the next applicable argument
341224Skas  * following dot, otherwise, go to the next applicable message.
351224Skas  * If given as first command with no arguments, print first message.
361224Skas  */
371224Skas 
381224Skas next(msgvec)
391224Skas 	int *msgvec;
401224Skas {
411224Skas 	register struct message *mp;
421224Skas 	register int *ip, *ip2;
431224Skas 	int list[2], mdot;
441224Skas 
451224Skas 	if (*msgvec != NULL) {
461224Skas 
471224Skas 		/*
481224Skas 		 * If some messages were supplied, find the
491224Skas 		 * first applicable one following dot using
501224Skas 		 * wrap around.
511224Skas 		 */
521224Skas 
531224Skas 		mdot = dot - &message[0] + 1;
541470Skas 
551470Skas 		/*
561470Skas 		 * Find the first message in the supplied
571470Skas 		 * message list which follows dot.
581470Skas 		 */
591470Skas 
601224Skas 		for (ip = msgvec; *ip != NULL; ip++)
611224Skas 			if (*ip > mdot)
621224Skas 				break;
631224Skas 		if (*ip == NULL)
641224Skas 			ip = msgvec;
651224Skas 		ip2 = ip;
661224Skas 		do {
671224Skas 			mp = &message[*ip2 - 1];
681224Skas 			if ((mp->m_flag & MDELETED) == 0) {
691224Skas 				dot = mp;
701224Skas 				goto hitit;
711224Skas 			}
721470Skas 			if (*ip2 != NULL)
731470Skas 				ip2++;
741470Skas 			if (*ip2 == NULL)
751470Skas 				ip2 = msgvec;
761224Skas 		} while (ip2 != ip);
771224Skas 		printf("No messages applicable\n");
781224Skas 		return(1);
791224Skas 	}
801224Skas 
811224Skas 	/*
821224Skas 	 * If this is the first command, select message 1.
831224Skas 	 * Note that this must exist for us to get here at all.
841224Skas 	 */
851224Skas 
861481Skas 	if (!sawcom)
871224Skas 		goto hitit;
881224Skas 
891224Skas 	/*
901224Skas 	 * Just find the next good message after dot, no
911224Skas 	 * wraparound.
921224Skas 	 */
931224Skas 
941224Skas 	for (mp = dot+1; mp < &message[msgCount]; mp++)
951224Skas 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
961224Skas 			break;
971224Skas 	if (mp >= &message[msgCount]) {
981224Skas 		printf("At EOF\n");
991224Skas 		return(0);
1001224Skas 	}
1011224Skas 	dot = mp;
1021224Skas hitit:
1031224Skas 	/*
1041224Skas 	 * Print dot.
1051224Skas 	 */
1061224Skas 
1071224Skas 	list[0] = dot - &message[0] + 1;
1081224Skas 	list[1] = NULL;
1091224Skas 	return(type(list));
1101224Skas }
1111224Skas 
1121224Skas /*
1135776Skurt  * Save a message in a file.  Mark the message as saved
1145776Skurt  * so we can discard when the user quits.
1151224Skas  */
1161224Skas save(str)
1171224Skas 	char str[];
1181224Skas {
1195776Skurt 
1205776Skurt 	return(save1(str, 1));
1215776Skurt }
1225776Skurt 
1235776Skurt /*
1245776Skurt  * Copy a message to a file without affected its saved-ness
1255776Skurt  */
1265776Skurt copycmd(str)
1275776Skurt 	char str[];
1285776Skurt {
1295776Skurt 
1305776Skurt 	return(save1(str, 0));
1315776Skurt }
1325776Skurt 
1335776Skurt /*
1345776Skurt  * Save/copy the indicated messages at the end of the passed file name.
1355776Skurt  * If mark is true, mark the message "saved."
1365776Skurt  */
1375776Skurt save1(str, mark)
1385776Skurt 	char str[];
1395776Skurt {
1401224Skas 	register int *ip, mesg;
1411224Skas 	register struct message *mp;
1425776Skurt 	char *file, *disp, *cmd;
1438747Scarl 	int f, *msgvec, lc, t;
1448747Scarl 	long cc;
1451224Skas 	FILE *obuf;
1461224Skas 	struct stat statb;
1471224Skas 
1485776Skurt 	cmd = mark ? "save" : "copy";
1491224Skas 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
1501224Skas 	if ((file = snarf(str, &f)) == NOSTR)
1511224Skas 		return(1);
1521224Skas 	if (!f) {
1531224Skas 		*msgvec = first(0, MMNORM);
1541224Skas 		if (*msgvec == NULL) {
1555776Skurt 			printf("No messages to %s.\n", cmd);
1561224Skas 			return(1);
1571224Skas 		}
1581224Skas 		msgvec[1] = NULL;
1591224Skas 	}
1601224Skas 	if (f && getmsglist(str, msgvec, 0) < 0)
1611224Skas 		return(1);
1621224Skas 	if ((file = expand(file)) == NOSTR)
1631224Skas 		return(1);
1641224Skas 	printf("\"%s\" ", file);
16516859Sralph 	fflush(stdout);
1661224Skas 	if (stat(file, &statb) >= 0)
1671224Skas 		disp = "[Appended]";
1681224Skas 	else
1691224Skas 		disp = "[New file]";
1701224Skas 	if ((obuf = fopen(file, "a")) == NULL) {
1711224Skas 		perror(NOSTR);
1721224Skas 		return(1);
1731224Skas 	}
1748747Scarl 	cc = 0L;
1758747Scarl 	lc = 0;
1761224Skas 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1771224Skas 		mesg = *ip;
1781224Skas 		touch(mesg);
1791224Skas 		mp = &message[mesg-1];
18034692Sedward 		if ((t = send(mp, obuf, saveignore)) < 0) {
1811224Skas 			perror(file);
1821224Skas 			fclose(obuf);
1831224Skas 			return(1);
1841224Skas 		}
1851224Skas 		lc += t;
1868747Scarl 		cc += mp->m_size;
1875776Skurt 		if (mark)
1885776Skurt 			mp->m_flag |= MSAVED;
1891224Skas 	}
1901224Skas 	fflush(obuf);
1911224Skas 	if (ferror(obuf))
1921224Skas 		perror(file);
1931224Skas 	fclose(obuf);
1948747Scarl 	printf("%s %d/%ld\n", disp, lc, cc);
1951224Skas 	return(0);
1961224Skas }
1971224Skas 
1981224Skas /*
1991224Skas  * Write the indicated messages at the end of the passed
2001224Skas  * file name, minus header and trailing blank line.
2011224Skas  */
2021224Skas 
2031224Skas swrite(str)
2041224Skas 	char str[];
2051224Skas {
2061224Skas 	register int *ip, mesg;
2071224Skas 	register struct message *mp;
2081224Skas 	register char *file, *disp;
2091224Skas 	char linebuf[BUFSIZ];
2101224Skas 	int f, *msgvec, lc, cc, t;
2111224Skas 	FILE *obuf, *mesf;
2121224Skas 	struct stat statb;
2131224Skas 
2141224Skas 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
2151224Skas 	if ((file = snarf(str, &f)) == NOSTR)
2161224Skas 		return(1);
2171224Skas 	if ((file = expand(file)) == NOSTR)
2181224Skas 		return(1);
2191224Skas 	if (!f) {
2201224Skas 		*msgvec = first(0, MMNORM);
2211224Skas 		if (*msgvec == NULL) {
2221224Skas 			printf("No messages to write.\n");
2231224Skas 			return(1);
2241224Skas 		}
2251224Skas 		msgvec[1] = NULL;
2261224Skas 	}
2271224Skas 	if (f && getmsglist(str, msgvec, 0) < 0)
2281224Skas 		return(1);
2291224Skas 	printf("\"%s\" ", file);
23016859Sralph 	fflush(stdout);
2311224Skas 	if (stat(file, &statb) >= 0)
2321224Skas 		disp = "[Appended]";
2331224Skas 	else
2341224Skas 		disp = "[New file]";
2351224Skas 	if ((obuf = fopen(file, "a")) == NULL) {
2361224Skas 		perror(NOSTR);
2371224Skas 		return(1);
2381224Skas 	}
2391224Skas 	cc = lc = 0;
2401224Skas 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
2411224Skas 		mesg = *ip;
2421224Skas 		touch(mesg);
2431224Skas 		mp = &message[mesg-1];
2441224Skas 		mesf = setinput(mp);
24524681Sserge 		t = mp->m_lines - 1;
2461224Skas 		while (t-- > 0) {
24724681Sserge 			readline(mesf, linebuf);
24824681Sserge 			if (blankline(linebuf))
24924681Sserge 				break;
25024681Sserge 		}
25124681Sserge 		while (t-- > 0) {
2521224Skas 			fgets(linebuf, BUFSIZ, mesf);
2531224Skas 			fputs(linebuf, obuf);
2541224Skas 			cc += strlen(linebuf);
2551224Skas 		}
2561224Skas 		lc += mp->m_lines - 2;
2571224Skas 		mp->m_flag |= MSAVED;
2581224Skas 	}
2591224Skas 	fflush(obuf);
2601224Skas 	if (ferror(obuf))
2611224Skas 		perror(file);
2621224Skas 	fclose(obuf);
2631224Skas 	printf("%s %d/%d\n", disp, lc, cc);
2641224Skas 	return(0);
2651224Skas }
2661224Skas 
2671224Skas /*
2681224Skas  * Snarf the file from the end of the command line and
2691224Skas  * return a pointer to it.  If there is no file attached,
2701224Skas  * just return NOSTR.  Put a null in front of the file
2711224Skas  * name so that the message list processing won't see it,
2721224Skas  * unless the file name is the only thing on the line, in
2731224Skas  * which case, return 0 in the reference flag variable.
2741224Skas  */
2751224Skas 
2761224Skas char *
2771224Skas snarf(linebuf, flag)
2781224Skas 	char linebuf[];
2791224Skas 	int *flag;
2801224Skas {
2811224Skas 	register char *cp;
2821224Skas 
2831224Skas 	*flag = 1;
2841224Skas 	cp = strlen(linebuf) + linebuf - 1;
2851224Skas 
2861224Skas 	/*
2871224Skas 	 * Strip away trailing blanks.
2881224Skas 	 */
2891224Skas 
29031142Sedward 	while (cp > linebuf && isspace(*cp))
2911224Skas 		cp--;
2921224Skas 	*++cp = 0;
2931224Skas 
2941224Skas 	/*
2951224Skas 	 * Now search for the beginning of the file name.
2961224Skas 	 */
2971224Skas 
29831142Sedward 	while (cp > linebuf && !isspace(*cp))
2991224Skas 		cp--;
3001224Skas 	if (*cp == '\0') {
3011224Skas 		printf("No file specified.\n");
3021224Skas 		return(NOSTR);
3031224Skas 	}
30431142Sedward 	if (isspace(*cp))
3051224Skas 		*cp++ = 0;
3061224Skas 	else
3071224Skas 		*flag = 0;
3081224Skas 	return(cp);
3091224Skas }
3101224Skas 
3111224Skas /*
3121224Skas  * Delete messages.
3131224Skas  */
3141224Skas 
3151224Skas delete(msgvec)
3161224Skas 	int msgvec[];
3171224Skas {
3181224Skas 	return(delm(msgvec));
3191224Skas }
3201224Skas 
3211224Skas /*
3221224Skas  * Delete messages, then type the new dot.
3231224Skas  */
3241224Skas 
3251224Skas deltype(msgvec)
3261224Skas 	int msgvec[];
3271224Skas {
3281224Skas 	int list[2];
3294493Skurt 	int lastdot;
3301224Skas 
3314493Skurt 	lastdot = dot - &message[0] + 1;
3321224Skas 	if (delm(msgvec) >= 0) {
3331224Skas 		list[0] = dot - &message[0];
3341224Skas 		list[0]++;
3354493Skurt 		if (list[0] > lastdot) {
3364493Skurt 			touch(list[0]);
3374493Skurt 			list[1] = NULL;
3384493Skurt 			return(type(list));
3394493Skurt 		}
3404493Skurt 		printf("At EOF\n");
3414493Skurt 		return(0);
3421224Skas 	}
3431224Skas 	else {
3441224Skas 		printf("No more messages\n");
3451224Skas 		return(0);
3461224Skas 	}
3471224Skas }
3481224Skas 
3491224Skas /*
3501224Skas  * Delete the indicated messages.
3511224Skas  * Set dot to some nice place afterwards.
3521224Skas  * Internal interface.
3531224Skas  */
3541224Skas 
3551224Skas delm(msgvec)
3561224Skas 	int *msgvec;
3571224Skas {
3581224Skas 	register struct message *mp;
3591224Skas 	register *ip, mesg;
3601224Skas 	int last;
3611224Skas 
3621224Skas 	last = NULL;
3631224Skas 	for (ip = msgvec; *ip != NULL; ip++) {
3641224Skas 		mesg = *ip;
3651224Skas 		touch(mesg);
3661224Skas 		mp = &message[mesg-1];
3673319Skas 		mp->m_flag |= MDELETED|MTOUCH;
3683319Skas 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
3691224Skas 		last = mesg;
3701224Skas 	}
3711224Skas 	if (last != NULL) {
3721224Skas 		dot = &message[last-1];
3731224Skas 		last = first(0, MDELETED);
3741224Skas 		if (last != NULL) {
3751224Skas 			dot = &message[last-1];
3761224Skas 			return(0);
3771224Skas 		}
3781224Skas 		else {
3791224Skas 			dot = &message[0];
3801224Skas 			return(-1);
3811224Skas 		}
3821224Skas 	}
3831224Skas 
3841224Skas 	/*
3851224Skas 	 * Following can't happen -- it keeps lint happy
3861224Skas 	 */
3871224Skas 
3881224Skas 	return(-1);
3891224Skas }
3901224Skas 
3911224Skas /*
3921224Skas  * Undelete the indicated messages.
3931224Skas  */
3941224Skas 
3951224Skas undelete(msgvec)
3961224Skas 	int *msgvec;
3971224Skas {
3981224Skas 	register struct message *mp;
3991224Skas 	register *ip, mesg;
4001224Skas 
4011224Skas 	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
4021224Skas 		mesg = *ip;
4031224Skas 		if (mesg == 0)
4041224Skas 			return;
4051224Skas 		touch(mesg);
4061224Skas 		mp = &message[mesg-1];
4071224Skas 		dot = mp;
4081224Skas 		mp->m_flag &= ~MDELETED;
4091224Skas 	}
4101224Skas }
4111224Skas 
4121224Skas /*
4131224Skas  * Interactively dump core on "core"
4141224Skas  */
4151224Skas 
4161224Skas core()
4171224Skas {
4181224Skas 	register int pid;
41931142Sedward 	union wait status;
4201224Skas 
4211224Skas 	if ((pid = vfork()) == -1) {
4221224Skas 		perror("fork");
4231224Skas 		return(1);
4241224Skas 	}
4251224Skas 	if (pid == 0) {
4261224Skas 		abort();
4271224Skas 		_exit(1);
4281224Skas 	}
4291224Skas 	printf("Okie dokie");
4301224Skas 	fflush(stdout);
4311224Skas 	while (wait(&status) != pid)
4321224Skas 		;
43331142Sedward 	if (status.w_coredump)
4341224Skas 		printf(" -- Core dumped\n");
4351224Skas 	else
4361224Skas 		printf("\n");
43731142Sedward 	return 0;
4381224Skas }
4394340Skurt 
4404340Skurt /*
4414340Skurt  * Clobber as many bytes of stack as the user requests.
4424340Skurt  */
4434340Skurt clobber(argv)
4444340Skurt 	char **argv;
4454340Skurt {
4464340Skurt 	register int times;
4474340Skurt 
4484340Skurt 	if (argv[0] == 0)
4494340Skurt 		times = 1;
4504340Skurt 	else
4514340Skurt 		times = (atoi(argv[0]) + 511) / 512;
4527561Skurt 	clob1(times);
4534340Skurt }
4544340Skurt 
4554340Skurt /*
4564340Skurt  * Clobber the stack.
4574340Skurt  */
4587561Skurt clob1(n)
4594340Skurt {
4604340Skurt 	char buf[512];
4614340Skurt 	register char *cp;
4624340Skurt 
4634340Skurt 	if (n <= 0)
4644340Skurt 		return;
4654340Skurt 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
4664340Skurt 		;
4677561Skurt 	clob1(n - 1);
4684340Skurt }
4697573Skurt 
4707573Skurt /*
47118662Sserge  * Add the given header fields to the retained list.
47218662Sserge  * If no arguments, print the current list of retained fields.
47318662Sserge  */
47418662Sserge retfield(list)
47518662Sserge 	char *list[];
47618662Sserge {
47718662Sserge 
47834692Sedward 	return ignore1(list, ignore + 1, "retained");
47918662Sserge }
48018662Sserge 
48118662Sserge /*
48234692Sedward  * Add the given header fields to the ignored list.
48334692Sedward  * If no arguments, print the current list of ignored fields.
48418662Sserge  */
48534692Sedward igfield(list)
48634692Sedward 	char *list[];
48718662Sserge {
48818662Sserge 
48934692Sedward 	return ignore1(list, ignore, "ignored");
49018662Sserge }
49118662Sserge 
49234692Sedward saveretfield(list)
4937573Skurt 	char *list[];
4947573Skurt {
49534692Sedward 
49634692Sedward 	return ignore1(list, saveignore + 1, "retained");
49734692Sedward }
49834692Sedward 
49934692Sedward saveigfield(list)
50034692Sedward 	char *list[];
50134692Sedward {
50234692Sedward 
50334692Sedward 	return ignore1(list, saveignore, "ignored");
50434692Sedward }
50534692Sedward 
50634692Sedward ignore1(list, tab, which)
50734692Sedward 	char *list[];
50834692Sedward 	struct ignoretab *tab;
50934692Sedward 	char *which;
51034692Sedward {
5117573Skurt 	char field[BUFSIZ];
5127573Skurt 	register int h;
5137573Skurt 	register struct ignore *igp;
5147573Skurt 	char **ap;
5157573Skurt 
5167573Skurt 	if (argcount(list) == 0)
51734692Sedward 		return igshow(tab, which);
5187573Skurt 	for (ap = list; *ap != 0; ap++) {
51934692Sedward 		istrcpy(field, *ap);
52034692Sedward 		if (member(field, tab))
5217582Skurt 			continue;
5227573Skurt 		h = hash(field);
5237573Skurt 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
52431142Sedward 		igp->i_field = calloc((unsigned) strlen(field) + 1,
52531142Sedward 			sizeof (char));
5267573Skurt 		strcpy(igp->i_field, field);
52734692Sedward 		igp->i_link = tab->i_head[h];
52834692Sedward 		tab->i_head[h] = igp;
52934692Sedward 		tab->i_count++;
5307573Skurt 	}
53134692Sedward 	return 0;
5327573Skurt }
5337573Skurt 
5347573Skurt /*
53534692Sedward  * Print out all currently retained fields.
5367573Skurt  */
53734692Sedward igshow(tab, which)
53834692Sedward 	struct ignoretab *tab;
53934692Sedward 	char *which;
5407573Skurt {
54134692Sedward 	register int h;
5427573Skurt 	struct ignore *igp;
5437582Skurt 	char **ap, **ring;
5447582Skurt 	int igcomp();
5457573Skurt 
54634692Sedward 	if (tab->i_count == 0) {
54734692Sedward 		printf("No fields currently being %s.\n", which);
54834692Sedward 		return 0;
5497582Skurt 	}
55034692Sedward 	ring = (char **) salloc((tab->i_count + 1) * sizeof (char *));
5517582Skurt 	ap = ring;
5527582Skurt 	for (h = 0; h < HSHSIZE; h++)
55334692Sedward 		for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
5547582Skurt 			*ap++ = igp->i_field;
5557582Skurt 	*ap = 0;
55634692Sedward 	qsort((char *) ring, tab->i_count, sizeof (char *), igcomp);
5577582Skurt 	for (ap = ring; *ap != 0; ap++)
5587582Skurt 		printf("%s\n", *ap);
55934692Sedward 	return 0;
5607573Skurt }
5617582Skurt 
5627582Skurt /*
5637582Skurt  * Compare two names for sorting ignored field list.
5647582Skurt  */
5657582Skurt igcomp(l, r)
5667582Skurt 	char **l, **r;
5677582Skurt {
5687582Skurt 
56934692Sedward 	return strcmp(*l, *r);
5707582Skurt }
571