xref: /csrg-svn/usr.bin/mail/cmd2.c (revision 18662)
114524Ssam #ifndef lint
2*18662Sserge static char *sccsid = "@(#)cmd2.c	2.13 (Berkeley) 04/18/85";
314524Ssam #endif
41224Skas 
51224Skas #include "rcv.h"
61224Skas #include <sys/stat.h>
71224Skas 
81224Skas /*
91224Skas  * Mail -- a mail program
101224Skas  *
111224Skas  * More user commands.
121224Skas  */
131224Skas 
141224Skas /*
151224Skas  * If any arguments were given, go to the next applicable argument
161224Skas  * following dot, otherwise, go to the next applicable message.
171224Skas  * If given as first command with no arguments, print first message.
181224Skas  */
191224Skas 
201224Skas next(msgvec)
211224Skas 	int *msgvec;
221224Skas {
231224Skas 	register struct message *mp;
241224Skas 	register int *ip, *ip2;
251224Skas 	int list[2], mdot;
261224Skas 
271224Skas 	if (*msgvec != NULL) {
281224Skas 
291224Skas 		/*
301224Skas 		 * If some messages were supplied, find the
311224Skas 		 * first applicable one following dot using
321224Skas 		 * wrap around.
331224Skas 		 */
341224Skas 
351224Skas 		mdot = dot - &message[0] + 1;
361470Skas 
371470Skas 		/*
381470Skas 		 * Find the first message in the supplied
391470Skas 		 * message list which follows dot.
401470Skas 		 */
411470Skas 
421224Skas 		for (ip = msgvec; *ip != NULL; ip++)
431224Skas 			if (*ip > mdot)
441224Skas 				break;
451224Skas 		if (*ip == NULL)
461224Skas 			ip = msgvec;
471224Skas 		ip2 = ip;
481224Skas 		do {
491224Skas 			mp = &message[*ip2 - 1];
501224Skas 			if ((mp->m_flag & MDELETED) == 0) {
511224Skas 				dot = mp;
521224Skas 				goto hitit;
531224Skas 			}
541470Skas 			if (*ip2 != NULL)
551470Skas 				ip2++;
561470Skas 			if (*ip2 == NULL)
571470Skas 				ip2 = msgvec;
581224Skas 		} while (ip2 != ip);
591224Skas 		printf("No messages applicable\n");
601224Skas 		return(1);
611224Skas 	}
621224Skas 
631224Skas 	/*
641224Skas 	 * If this is the first command, select message 1.
651224Skas 	 * Note that this must exist for us to get here at all.
661224Skas 	 */
671224Skas 
681481Skas 	if (!sawcom)
691224Skas 		goto hitit;
701224Skas 
711224Skas 	/*
721224Skas 	 * Just find the next good message after dot, no
731224Skas 	 * wraparound.
741224Skas 	 */
751224Skas 
761224Skas 	for (mp = dot+1; mp < &message[msgCount]; mp++)
771224Skas 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
781224Skas 			break;
791224Skas 	if (mp >= &message[msgCount]) {
801224Skas 		printf("At EOF\n");
811224Skas 		return(0);
821224Skas 	}
831224Skas 	dot = mp;
841224Skas hitit:
851224Skas 	/*
861224Skas 	 * Print dot.
871224Skas 	 */
881224Skas 
891224Skas 	list[0] = dot - &message[0] + 1;
901224Skas 	list[1] = NULL;
911224Skas 	return(type(list));
921224Skas }
931224Skas 
941224Skas /*
955776Skurt  * Save a message in a file.  Mark the message as saved
965776Skurt  * so we can discard when the user quits.
971224Skas  */
981224Skas save(str)
991224Skas 	char str[];
1001224Skas {
1015776Skurt 
1025776Skurt 	return(save1(str, 1));
1035776Skurt }
1045776Skurt 
1055776Skurt /*
1065776Skurt  * Copy a message to a file without affected its saved-ness
1075776Skurt  */
1085776Skurt copycmd(str)
1095776Skurt 	char str[];
1105776Skurt {
1115776Skurt 
1125776Skurt 	return(save1(str, 0));
1135776Skurt }
1145776Skurt 
1155776Skurt /*
1165776Skurt  * Save/copy the indicated messages at the end of the passed file name.
1175776Skurt  * If mark is true, mark the message "saved."
1185776Skurt  */
1195776Skurt save1(str, mark)
1205776Skurt 	char str[];
1215776Skurt {
1221224Skas 	register int *ip, mesg;
1231224Skas 	register struct message *mp;
1245776Skurt 	char *file, *disp, *cmd;
1258747Scarl 	int f, *msgvec, lc, t;
1268747Scarl 	long cc;
1271224Skas 	FILE *obuf;
1281224Skas 	struct stat statb;
1291224Skas 
1305776Skurt 	cmd = mark ? "save" : "copy";
1311224Skas 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
1321224Skas 	if ((file = snarf(str, &f)) == NOSTR)
1331224Skas 		return(1);
1341224Skas 	if (!f) {
1351224Skas 		*msgvec = first(0, MMNORM);
1361224Skas 		if (*msgvec == NULL) {
1375776Skurt 			printf("No messages to %s.\n", cmd);
1381224Skas 			return(1);
1391224Skas 		}
1401224Skas 		msgvec[1] = NULL;
1411224Skas 	}
1421224Skas 	if (f && getmsglist(str, msgvec, 0) < 0)
1431224Skas 		return(1);
1441224Skas 	if ((file = expand(file)) == NOSTR)
1451224Skas 		return(1);
1461224Skas 	printf("\"%s\" ", file);
14716859Sralph 	fflush(stdout);
1481224Skas 	if (stat(file, &statb) >= 0)
1491224Skas 		disp = "[Appended]";
1501224Skas 	else
1511224Skas 		disp = "[New file]";
1521224Skas 	if ((obuf = fopen(file, "a")) == NULL) {
1531224Skas 		perror(NOSTR);
1541224Skas 		return(1);
1551224Skas 	}
1568747Scarl 	cc = 0L;
1578747Scarl 	lc = 0;
1581224Skas 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1591224Skas 		mesg = *ip;
1601224Skas 		touch(mesg);
1611224Skas 		mp = &message[mesg-1];
1627573Skurt 		if ((t = send(mp, obuf, 0)) < 0) {
1631224Skas 			perror(file);
1641224Skas 			fclose(obuf);
1651224Skas 			return(1);
1661224Skas 		}
1671224Skas 		lc += t;
1688747Scarl 		cc += mp->m_size;
1695776Skurt 		if (mark)
1705776Skurt 			mp->m_flag |= MSAVED;
1711224Skas 	}
1721224Skas 	fflush(obuf);
1731224Skas 	if (ferror(obuf))
1741224Skas 		perror(file);
1751224Skas 	fclose(obuf);
1768747Scarl 	printf("%s %d/%ld\n", disp, lc, cc);
1771224Skas 	return(0);
1781224Skas }
1791224Skas 
1801224Skas /*
1811224Skas  * Write the indicated messages at the end of the passed
1821224Skas  * file name, minus header and trailing blank line.
1831224Skas  */
1841224Skas 
1851224Skas swrite(str)
1861224Skas 	char str[];
1871224Skas {
1881224Skas 	register int *ip, mesg;
1891224Skas 	register struct message *mp;
1901224Skas 	register char *file, *disp;
1911224Skas 	char linebuf[BUFSIZ];
1921224Skas 	int f, *msgvec, lc, cc, t;
1931224Skas 	FILE *obuf, *mesf;
1941224Skas 	struct stat statb;
1951224Skas 
1961224Skas 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
1971224Skas 	if ((file = snarf(str, &f)) == NOSTR)
1981224Skas 		return(1);
1991224Skas 	if ((file = expand(file)) == NOSTR)
2001224Skas 		return(1);
2011224Skas 	if (!f) {
2021224Skas 		*msgvec = first(0, MMNORM);
2031224Skas 		if (*msgvec == NULL) {
2041224Skas 			printf("No messages to write.\n");
2051224Skas 			return(1);
2061224Skas 		}
2071224Skas 		msgvec[1] = NULL;
2081224Skas 	}
2091224Skas 	if (f && getmsglist(str, msgvec, 0) < 0)
2101224Skas 		return(1);
2111224Skas 	printf("\"%s\" ", file);
21216859Sralph 	fflush(stdout);
2131224Skas 	if (stat(file, &statb) >= 0)
2141224Skas 		disp = "[Appended]";
2151224Skas 	else
2161224Skas 		disp = "[New file]";
2171224Skas 	if ((obuf = fopen(file, "a")) == NULL) {
2181224Skas 		perror(NOSTR);
2191224Skas 		return(1);
2201224Skas 	}
2211224Skas 	cc = lc = 0;
2221224Skas 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
2231224Skas 		mesg = *ip;
2241224Skas 		touch(mesg);
2251224Skas 		mp = &message[mesg-1];
2261224Skas 		mesf = setinput(mp);
2271224Skas 		t = mp->m_lines - 2;
2281224Skas 		readline(mesf, linebuf);
2291224Skas 		while (t-- > 0) {
2301224Skas 			fgets(linebuf, BUFSIZ, mesf);
2311224Skas 			fputs(linebuf, obuf);
2321224Skas 			cc += strlen(linebuf);
2331224Skas 		}
2341224Skas 		lc += mp->m_lines - 2;
2351224Skas 		mp->m_flag |= MSAVED;
2361224Skas 	}
2371224Skas 	fflush(obuf);
2381224Skas 	if (ferror(obuf))
2391224Skas 		perror(file);
2401224Skas 	fclose(obuf);
2411224Skas 	printf("%s %d/%d\n", disp, lc, cc);
2421224Skas 	return(0);
2431224Skas }
2441224Skas 
2451224Skas /*
2461224Skas  * Snarf the file from the end of the command line and
2471224Skas  * return a pointer to it.  If there is no file attached,
2481224Skas  * just return NOSTR.  Put a null in front of the file
2491224Skas  * name so that the message list processing won't see it,
2501224Skas  * unless the file name is the only thing on the line, in
2511224Skas  * which case, return 0 in the reference flag variable.
2521224Skas  */
2531224Skas 
2541224Skas char *
2551224Skas snarf(linebuf, flag)
2561224Skas 	char linebuf[];
2571224Skas 	int *flag;
2581224Skas {
2591224Skas 	register char *cp;
2601224Skas 
2611224Skas 	*flag = 1;
2621224Skas 	cp = strlen(linebuf) + linebuf - 1;
2631224Skas 
2641224Skas 	/*
2651224Skas 	 * Strip away trailing blanks.
2661224Skas 	 */
2671224Skas 
2681224Skas 	while (*cp == ' ' && cp > linebuf)
2691224Skas 		cp--;
2701224Skas 	*++cp = 0;
2711224Skas 
2721224Skas 	/*
2731224Skas 	 * Now search for the beginning of the file name.
2741224Skas 	 */
2751224Skas 
2761224Skas 	while (cp > linebuf && !any(*cp, "\t "))
2771224Skas 		cp--;
2781224Skas 	if (*cp == '\0') {
2791224Skas 		printf("No file specified.\n");
2801224Skas 		return(NOSTR);
2811224Skas 	}
2821224Skas 	if (any(*cp, " \t"))
2831224Skas 		*cp++ = 0;
2841224Skas 	else
2851224Skas 		*flag = 0;
2861224Skas 	return(cp);
2871224Skas }
2881224Skas 
2891224Skas /*
2901224Skas  * Delete messages.
2911224Skas  */
2921224Skas 
2931224Skas delete(msgvec)
2941224Skas 	int msgvec[];
2951224Skas {
2961224Skas 	return(delm(msgvec));
2971224Skas }
2981224Skas 
2991224Skas /*
3001224Skas  * Delete messages, then type the new dot.
3011224Skas  */
3021224Skas 
3031224Skas deltype(msgvec)
3041224Skas 	int msgvec[];
3051224Skas {
3061224Skas 	int list[2];
3074493Skurt 	int lastdot;
3081224Skas 
3094493Skurt 	lastdot = dot - &message[0] + 1;
3101224Skas 	if (delm(msgvec) >= 0) {
3111224Skas 		list[0] = dot - &message[0];
3121224Skas 		list[0]++;
3134493Skurt 		if (list[0] > lastdot) {
3144493Skurt 			touch(list[0]);
3154493Skurt 			list[1] = NULL;
3164493Skurt 			return(type(list));
3174493Skurt 		}
3184493Skurt 		printf("At EOF\n");
3194493Skurt 		return(0);
3201224Skas 	}
3211224Skas 	else {
3221224Skas 		printf("No more messages\n");
3231224Skas 		return(0);
3241224Skas 	}
3251224Skas }
3261224Skas 
3271224Skas /*
3281224Skas  * Delete the indicated messages.
3291224Skas  * Set dot to some nice place afterwards.
3301224Skas  * Internal interface.
3311224Skas  */
3321224Skas 
3331224Skas delm(msgvec)
3341224Skas 	int *msgvec;
3351224Skas {
3361224Skas 	register struct message *mp;
3371224Skas 	register *ip, mesg;
3381224Skas 	int last;
3391224Skas 
3401224Skas 	last = NULL;
3411224Skas 	for (ip = msgvec; *ip != NULL; ip++) {
3421224Skas 		mesg = *ip;
3431224Skas 		touch(mesg);
3441224Skas 		mp = &message[mesg-1];
3453319Skas 		mp->m_flag |= MDELETED|MTOUCH;
3463319Skas 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
3471224Skas 		last = mesg;
3481224Skas 	}
3491224Skas 	if (last != NULL) {
3501224Skas 		dot = &message[last-1];
3511224Skas 		last = first(0, MDELETED);
3521224Skas 		if (last != NULL) {
3531224Skas 			dot = &message[last-1];
3541224Skas 			return(0);
3551224Skas 		}
3561224Skas 		else {
3571224Skas 			dot = &message[0];
3581224Skas 			return(-1);
3591224Skas 		}
3601224Skas 	}
3611224Skas 
3621224Skas 	/*
3631224Skas 	 * Following can't happen -- it keeps lint happy
3641224Skas 	 */
3651224Skas 
3661224Skas 	return(-1);
3671224Skas }
3681224Skas 
3691224Skas /*
3701224Skas  * Undelete the indicated messages.
3711224Skas  */
3721224Skas 
3731224Skas undelete(msgvec)
3741224Skas 	int *msgvec;
3751224Skas {
3761224Skas 	register struct message *mp;
3771224Skas 	register *ip, mesg;
3781224Skas 
3791224Skas 	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
3801224Skas 		mesg = *ip;
3811224Skas 		if (mesg == 0)
3821224Skas 			return;
3831224Skas 		touch(mesg);
3841224Skas 		mp = &message[mesg-1];
3851224Skas 		dot = mp;
3861224Skas 		mp->m_flag &= ~MDELETED;
3871224Skas 	}
3881224Skas }
3891224Skas 
3901224Skas /*
3911224Skas  * Interactively dump core on "core"
3921224Skas  */
3931224Skas 
3941224Skas core()
3951224Skas {
3961224Skas 	register int pid;
3971224Skas 	int status;
3981224Skas 
3991224Skas 	if ((pid = vfork()) == -1) {
4001224Skas 		perror("fork");
4011224Skas 		return(1);
4021224Skas 	}
4031224Skas 	if (pid == 0) {
4047540Skurt 		sigchild();
4051224Skas 		abort();
4061224Skas 		_exit(1);
4071224Skas 	}
4081224Skas 	printf("Okie dokie");
4091224Skas 	fflush(stdout);
4101224Skas 	while (wait(&status) != pid)
4111224Skas 		;
4121224Skas 	if (status & 0200)
4131224Skas 		printf(" -- Core dumped\n");
4141224Skas 	else
4151224Skas 		printf("\n");
4161224Skas }
4174340Skurt 
4184340Skurt /*
4194340Skurt  * Clobber as many bytes of stack as the user requests.
4204340Skurt  */
4214340Skurt clobber(argv)
4224340Skurt 	char **argv;
4234340Skurt {
4244340Skurt 	register int times;
4254340Skurt 
4264340Skurt 	if (argv[0] == 0)
4274340Skurt 		times = 1;
4284340Skurt 	else
4294340Skurt 		times = (atoi(argv[0]) + 511) / 512;
4307561Skurt 	clob1(times);
4314340Skurt }
4324340Skurt 
4334340Skurt /*
4344340Skurt  * Clobber the stack.
4354340Skurt  */
4367561Skurt clob1(n)
4374340Skurt {
4384340Skurt 	char buf[512];
4394340Skurt 	register char *cp;
4404340Skurt 
4414340Skurt 	if (n <= 0)
4424340Skurt 		return;
4434340Skurt 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
4444340Skurt 		;
4457561Skurt 	clob1(n - 1);
4464340Skurt }
4477573Skurt 
4487573Skurt /*
449*18662Sserge  * Add the given header fields to the retained list.
450*18662Sserge  * If no arguments, print the current list of retained fields.
451*18662Sserge  */
452*18662Sserge retfield(list)
453*18662Sserge 	char *list[];
454*18662Sserge {
455*18662Sserge 	char field[BUFSIZ];
456*18662Sserge 	register int h;
457*18662Sserge 	register struct ignore *igp;
458*18662Sserge 	char **ap;
459*18662Sserge 
460*18662Sserge 	if (argcount(list) == 0)
461*18662Sserge 		return(retshow());
462*18662Sserge 	for (ap = list; *ap != 0; ap++) {
463*18662Sserge 		istrcpy(field, *ap);
464*18662Sserge 
465*18662Sserge 		if (member(field, retain))
466*18662Sserge 			continue;
467*18662Sserge 
468*18662Sserge 		h = hash(field);
469*18662Sserge 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
470*18662Sserge 		igp->i_field = calloc(strlen(field) + 1, sizeof (char));
471*18662Sserge 		strcpy(igp->i_field, field);
472*18662Sserge 		igp->i_link = retain[h];
473*18662Sserge 		retain[h] = igp;
474*18662Sserge 		nretained++;
475*18662Sserge 	}
476*18662Sserge 	return(0);
477*18662Sserge }
478*18662Sserge 
479*18662Sserge /*
480*18662Sserge  * Print out all currently retained fields.
481*18662Sserge  */
482*18662Sserge retshow()
483*18662Sserge {
484*18662Sserge 	register int h, count;
485*18662Sserge 	struct ignore *igp;
486*18662Sserge 	char **ap, **ring;
487*18662Sserge 	int igcomp();
488*18662Sserge 
489*18662Sserge 	count = 0;
490*18662Sserge 	for (h = 0; h < HSHSIZE; h++)
491*18662Sserge 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
492*18662Sserge 			count++;
493*18662Sserge 	if (count == 0) {
494*18662Sserge 		printf("No fields currently being retained.\n");
495*18662Sserge 		return(0);
496*18662Sserge 	}
497*18662Sserge 	ring = (char **) salloc((count + 1) * sizeof (char *));
498*18662Sserge 	ap = ring;
499*18662Sserge 	for (h = 0; h < HSHSIZE; h++)
500*18662Sserge 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
501*18662Sserge 			*ap++ = igp->i_field;
502*18662Sserge 	*ap = 0;
503*18662Sserge 	qsort(ring, count, sizeof (char *), igcomp);
504*18662Sserge 	for (ap = ring; *ap != 0; ap++)
505*18662Sserge 		printf("%s\n", *ap);
506*18662Sserge 	return(0);
507*18662Sserge }
508*18662Sserge 
509*18662Sserge /*
5107573Skurt  * Add the given header fields to the ignored list.
5117573Skurt  * If no arguments, print the current list of ignored fields.
5127573Skurt  */
5137573Skurt igfield(list)
5147573Skurt 	char *list[];
5157573Skurt {
5167573Skurt 	char field[BUFSIZ];
5177573Skurt 	register int h;
5187573Skurt 	register struct ignore *igp;
5197573Skurt 	char **ap;
5207573Skurt 
5217573Skurt 	if (argcount(list) == 0)
5227573Skurt 		return(igshow());
5237573Skurt 	for (ap = list; *ap != 0; ap++) {
5247582Skurt 		if (isign(*ap))
5257582Skurt 			continue;
5267573Skurt 		istrcpy(field, *ap);
5277573Skurt 		h = hash(field);
5287573Skurt 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
5297573Skurt 		igp->i_field = calloc(strlen(field) + 1, sizeof (char));
5307573Skurt 		strcpy(igp->i_field, field);
5317573Skurt 		igp->i_link = ignore[h];
5327573Skurt 		ignore[h] = igp;
5337573Skurt 	}
5347573Skurt 	return(0);
5357573Skurt }
5367573Skurt 
5377573Skurt /*
5387573Skurt  * Print out all currently ignored fields.
5397573Skurt  */
5407573Skurt igshow()
5417573Skurt {
5427582Skurt 	register int h, count;
5437573Skurt 	struct ignore *igp;
5447582Skurt 	char **ap, **ring;
5457582Skurt 	int igcomp();
5467573Skurt 
5477582Skurt 	count = 0;
5487573Skurt 	for (h = 0; h < HSHSIZE; h++)
5497582Skurt 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
5507582Skurt 			count++;
5517582Skurt 	if (count == 0) {
5527573Skurt 		printf("No fields currently being ignored.\n");
5537582Skurt 		return(0);
5547582Skurt 	}
5557584Skurt 	ring = (char **) salloc((count + 1) * sizeof (char *));
5567582Skurt 	ap = ring;
5577582Skurt 	for (h = 0; h < HSHSIZE; h++)
5587582Skurt 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
5597582Skurt 			*ap++ = igp->i_field;
5607582Skurt 	*ap = 0;
5617582Skurt 	qsort(ring, count, sizeof (char *), igcomp);
5627582Skurt 	for (ap = ring; *ap != 0; ap++)
5637582Skurt 		printf("%s\n", *ap);
5647573Skurt 	return(0);
5657573Skurt }
5667582Skurt 
5677582Skurt /*
5687582Skurt  * Compare two names for sorting ignored field list.
5697582Skurt  */
5707582Skurt igcomp(l, r)
5717582Skurt 	char **l, **r;
5727582Skurt {
5737582Skurt 
5747582Skurt 	return(strcmp(*l, *r));
5757582Skurt }
576