xref: /csrg-svn/usr.bin/mail/aux.c (revision 25912)
122444Sdist /*
222444Sdist  * Copyright (c) 1980 Regents of the University of California.
322444Sdist  * All rights reserved.  The Berkeley software License Agreement
422444Sdist  * specifies the terms and conditions for redistribution.
522444Sdist  */
622444Sdist 
714522Ssam #ifndef lint
8*25912Smckusick static char *sccsid = "@(#)aux.c	5.4 (Berkeley) 01/13/86";
922444Sdist #endif not lint
101219Skas 
111219Skas #include "rcv.h"
121219Skas #include <sys/stat.h>
131219Skas #include <ctype.h>
141219Skas 
151219Skas /*
161219Skas  * Mail -- a mail program
171219Skas  *
181219Skas  * Auxiliary functions.
191219Skas  */
201219Skas 
211219Skas /*
221219Skas  * Return a pointer to a dynamic copy of the argument.
231219Skas  */
241219Skas 
251219Skas char *
261219Skas savestr(str)
271219Skas 	char *str;
281219Skas {
291219Skas 	register char *cp, *cp2, *top;
301219Skas 
311219Skas 	for (cp = str; *cp; cp++)
321219Skas 		;
331219Skas 	top = salloc(cp-str + 1);
341219Skas 	if (top == NOSTR)
351219Skas 		return(NOSTR);
361219Skas 	for (cp = str, cp2 = top; *cp; cp++)
371219Skas 		*cp2++ = *cp;
381219Skas 	*cp2 = 0;
391219Skas 	return(top);
401219Skas }
411219Skas 
421219Skas /*
431219Skas  * Copy the name from the passed header line into the passed
441219Skas  * name buffer.  Null pad the name buffer.
451219Skas  */
461219Skas 
471219Skas copyname(linebuf, nbuf)
481219Skas 	char *linebuf, *nbuf;
491219Skas {
501219Skas 	register char *cp, *cp2;
511219Skas 
521219Skas 	for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++)
531219Skas 		*cp2++ = *cp;
541219Skas 	while (cp2-nbuf < 8)
551219Skas 		*cp2++ = 0;
561219Skas }
571219Skas 
581219Skas /*
591219Skas  * Announce a fatal error and die.
601219Skas  */
611219Skas 
621219Skas panic(str)
631219Skas 	char *str;
641219Skas {
651219Skas 	prs("panic: ");
661219Skas 	prs(str);
671219Skas 	prs("\n");
681219Skas 	exit(1);
691219Skas }
701219Skas 
711219Skas /*
721219Skas  * Catch stdio errors and report them more nicely.
731219Skas  */
741219Skas 
751219Skas _error(str)
761219Skas 	char *str;
771219Skas {
781219Skas 	prs("Stdio Error: ");
791219Skas 	prs(str);
801219Skas 	prs("\n");
811219Skas 	abort();
821219Skas }
831219Skas 
841219Skas /*
851219Skas  * Print a string on diagnostic output.
861219Skas  */
871219Skas 
881219Skas prs(str)
891219Skas 	char *str;
901219Skas {
911219Skas 	register char *s;
921219Skas 
931219Skas 	for (s = str; *s; s++)
941219Skas 		;
951219Skas 	write(2, str, s-str);
961219Skas }
971219Skas 
981219Skas /*
991219Skas  * Touch the named message by setting its MTOUCH flag.
1001219Skas  * Touched messages have the effect of not being sent
1011219Skas  * back to the system mailbox on exit.
1021219Skas  */
1031219Skas 
1041219Skas touch(mesg)
1051219Skas {
1061479Skas 	register struct message *mp;
1071479Skas 
1081479Skas 	if (mesg < 1 || mesg > msgCount)
1091479Skas 		return;
1101479Skas 	mp = &message[mesg-1];
1111479Skas 	mp->m_flag |= MTOUCH;
1121479Skas 	if ((mp->m_flag & MREAD) == 0)
1131479Skas 		mp->m_flag |= MREAD|MSTATUS;
1141219Skas }
1151219Skas 
1161219Skas /*
1171219Skas  * Test to see if the passed file name is a directory.
1181219Skas  * Return true if it is.
1191219Skas  */
1201219Skas 
1211219Skas isdir(name)
1221219Skas 	char name[];
1231219Skas {
1241219Skas 	struct stat sbuf;
1251219Skas 
1261219Skas 	if (stat(name, &sbuf) < 0)
1271219Skas 		return(0);
1281219Skas 	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
1291219Skas }
1301219Skas 
1311219Skas /*
1321219Skas  * Count the number of arguments in the given string raw list.
1331219Skas  */
1341219Skas 
1351219Skas argcount(argv)
1361219Skas 	char **argv;
1371219Skas {
1381219Skas 	register char **ap;
1391219Skas 
1401219Skas 	for (ap = argv; *ap != NOSTR; ap++)
1411219Skas 		;
1421219Skas 	return(ap-argv);
1431219Skas }
1441219Skas 
1451219Skas /*
1461219Skas  * Given a file address, determine the
1471219Skas  * block number it represents.
1481219Skas  */
1491219Skas 
1501219Skas blockof(off)
1511219Skas 	off_t off;
1521219Skas {
1531219Skas 	off_t a;
1541219Skas 
1551219Skas 	a = off >> 9;
1561219Skas 	a &= 077777;
1571219Skas 	return((int) a);
1581219Skas }
1591219Skas 
1601219Skas /*
1611219Skas  * Take a file address, and determine
1621219Skas  * its offset in the current block.
1631219Skas  */
1641219Skas 
1651219Skas offsetof(off)
1661219Skas 	off_t off;
1671219Skas {
1681219Skas 	off_t a;
1691219Skas 
1701219Skas 	a = off & 0777;
1711219Skas 	return((int) a);
1721219Skas }
1731219Skas 
1741219Skas /*
1751219Skas  * Return the desired header line from the passed message
1761219Skas  * pointer (or NOSTR if the desired header field is not available).
1771219Skas  */
1781219Skas 
1791219Skas char *
1801219Skas hfield(field, mp)
1811219Skas 	char field[];
1821219Skas 	struct message *mp;
1831219Skas {
1841219Skas 	register FILE *ibuf;
1851219Skas 	char linebuf[LINESIZE];
1861219Skas 	register int lc;
1871219Skas 
1881219Skas 	ibuf = setinput(mp);
1891219Skas 	if ((lc = mp->m_lines) <= 0)
1901219Skas 		return(NOSTR);
1911219Skas 	if (readline(ibuf, linebuf) < 0)
1921219Skas 		return(NOSTR);
1931219Skas 	lc--;
1941219Skas 	do {
1951219Skas 		lc = gethfield(ibuf, linebuf, lc);
1961219Skas 		if (lc == -1)
1971219Skas 			return(NOSTR);
1981219Skas 		if (ishfield(linebuf, field))
1991219Skas 			return(savestr(hcontents(linebuf)));
2001219Skas 	} while (lc > 0);
2011219Skas 	return(NOSTR);
2021219Skas }
2031219Skas 
2041219Skas /*
2051219Skas  * Return the next header field found in the given message.
2061219Skas  * Return > 0 if something found, <= 0 elsewise.
2071219Skas  * Must deal with \ continuations & other such fraud.
2081219Skas  */
2091219Skas 
2101219Skas gethfield(f, linebuf, rem)
2111219Skas 	register FILE *f;
2121219Skas 	char linebuf[];
2131219Skas 	register int rem;
2141219Skas {
2151219Skas 	char line2[LINESIZE];
2161219Skas 	long loc;
2171219Skas 	register char *cp, *cp2;
2181219Skas 	register int c;
2191219Skas 
2201219Skas 
2211219Skas 	for (;;) {
2221219Skas 		if (rem <= 0)
2231219Skas 			return(-1);
2241219Skas 		if (readline(f, linebuf) < 0)
2251219Skas 			return(-1);
2261219Skas 		rem--;
2271219Skas 		if (strlen(linebuf) == 0)
2281219Skas 			return(-1);
2291219Skas 		if (isspace(linebuf[0]))
2301219Skas 			continue;
2311219Skas 		if (linebuf[0] == '>')
2321219Skas 			continue;
2331219Skas 		cp = index(linebuf, ':');
2341219Skas 		if (cp == NOSTR)
2351219Skas 			continue;
2361219Skas 		for (cp2 = linebuf; cp2 < cp; cp2++)
2371219Skas 			if (isdigit(*cp2))
2381219Skas 				continue;
2391219Skas 
2401219Skas 		/*
2411219Skas 		 * I guess we got a headline.
2421219Skas 		 * Handle wraparounding
2431219Skas 		 */
2441219Skas 
2451219Skas 		for (;;) {
2461219Skas 			if (rem <= 0)
2471219Skas 				break;
2481219Skas #ifdef CANTELL
2491219Skas 			loc = ftell(f);
2501219Skas 			if (readline(f, line2) < 0)
2511219Skas 				break;
2521219Skas 			rem--;
2531219Skas 			if (!isspace(line2[0])) {
2541219Skas 				fseek(f, loc, 0);
2551219Skas 				rem++;
2561219Skas 				break;
2571219Skas 			}
2581219Skas #else
2591219Skas 			c = getc(f);
2601219Skas 			ungetc(c, f);
2611219Skas 			if (!isspace(c) || c == '\n')
2621219Skas 				break;
2631219Skas 			if (readline(f, line2) < 0)
2641219Skas 				break;
2651219Skas 			rem--;
2661219Skas #endif
2671219Skas 			cp2 = line2;
2681219Skas 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
2691219Skas 				;
2701219Skas 			if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
2711219Skas 				break;
2721219Skas 			cp = &linebuf[strlen(linebuf)];
2731219Skas 			while (cp > linebuf &&
2741219Skas 			    (isspace(cp[-1]) || cp[-1] == '\\'))
2751219Skas 				cp--;
2761219Skas 			*cp++ = ' ';
2771219Skas 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
2781219Skas 				;
2791219Skas 			strcpy(cp, cp2);
2801219Skas 		}
2811219Skas 		if ((c = strlen(linebuf)) > 0) {
2821219Skas 			cp = &linebuf[c-1];
2831219Skas 			while (cp > linebuf && isspace(*cp))
2841219Skas 				cp--;
2851219Skas 			*++cp = 0;
2861219Skas 		}
2871219Skas 		return(rem);
2881219Skas 	}
2891219Skas 	/* NOTREACHED */
2901219Skas }
2911219Skas 
2921219Skas /*
2931219Skas  * Check whether the passed line is a header line of
2941219Skas  * the desired breed.
2951219Skas  */
2961219Skas 
2971219Skas ishfield(linebuf, field)
2981219Skas 	char linebuf[], field[];
2991219Skas {
3001219Skas 	register char *cp;
3011219Skas 	register int c;
3021219Skas 
3031219Skas 	if ((cp = index(linebuf, ':')) == NOSTR)
3041219Skas 		return(0);
3051219Skas 	if (cp == linebuf)
3061219Skas 		return(0);
3071219Skas 	cp--;
3081219Skas 	while (cp > linebuf && isspace(*cp))
3091219Skas 		cp--;
3101219Skas 	c = *++cp;
3111219Skas 	*cp = 0;
3121219Skas 	if (icequal(linebuf ,field)) {
3131219Skas 		*cp = c;
3141219Skas 		return(1);
3151219Skas 	}
3161219Skas 	*cp = c;
3171219Skas 	return(0);
3181219Skas }
3191219Skas 
3201219Skas /*
3211219Skas  * Extract the non label information from the given header field
3221219Skas  * and return it.
3231219Skas  */
3241219Skas 
3251219Skas char *
3261219Skas hcontents(hfield)
3271219Skas 	char hfield[];
3281219Skas {
3291219Skas 	register char *cp;
3301219Skas 
3311219Skas 	if ((cp = index(hfield, ':')) == NOSTR)
3321219Skas 		return(NOSTR);
3331219Skas 	cp++;
3341219Skas 	while (*cp && isspace(*cp))
3351219Skas 		cp++;
3361219Skas 	return(cp);
3371219Skas }
3381219Skas 
3391219Skas /*
3401219Skas  * Compare two strings, ignoring case.
3411219Skas  */
3421219Skas 
3431219Skas icequal(s1, s2)
3441219Skas 	register char *s1, *s2;
3451219Skas {
3461219Skas 
3471219Skas 	while (raise(*s1++) == raise(*s2))
3481219Skas 		if (*s2++ == 0)
3491219Skas 			return(1);
3501219Skas 	return(0);
3511219Skas }
3521219Skas 
3531219Skas /*
3547571Skurt  * Copy a string, lowercasing it as we go.
3557571Skurt  */
3567571Skurt istrcpy(dest, src)
3577571Skurt 	char *dest, *src;
3587571Skurt {
3597571Skurt 	register char *cp, *cp2;
3607571Skurt 
3617571Skurt 	cp2 = dest;
3627571Skurt 	cp = src;
3637571Skurt 	do {
3647571Skurt 		*cp2++ = little(*cp);
3657571Skurt 	} while (*cp++ != 0);
3667571Skurt }
3677571Skurt 
3687571Skurt /*
3691219Skas  * The following code deals with input stacking to do source
3701219Skas  * commands.  All but the current file pointer are saved on
3711219Skas  * the stack.
3721219Skas  */
3731219Skas 
3741219Skas static	int	ssp = -1;		/* Top of file stack */
3751519Skas struct sstack {
3761519Skas 	FILE	*s_file;		/* File we were in. */
3771519Skas 	int	s_cond;			/* Saved state of conditionals */
3785785Skurt 	int	s_loading;		/* Loading .mailrc, etc. */
37918661Sserge } sstack[NOFILE];
3801219Skas 
3811219Skas /*
3821219Skas  * Pushdown current input file and switch to a new one.
3831219Skas  * Set the global flag "sourcing" so that others will realize
3841219Skas  * that they are no longer reading from a tty (in all probability).
3851219Skas  */
3861219Skas 
3871219Skas source(name)
3881219Skas 	char name[];
3891219Skas {
3901219Skas 	register FILE *fi;
3913914Skurt 	register char *cp;
3921219Skas 
3933914Skurt 	if ((cp = expand(name)) == NOSTR)
3941219Skas 		return(1);
3953914Skurt 	if ((fi = fopen(cp, "r")) == NULL) {
3963914Skurt 		perror(cp);
3973914Skurt 		return(1);
3981219Skas 	}
39918661Sserge 	if (ssp >= NOFILE - 2) {
4001219Skas 		printf("Too much \"sourcing\" going on.\n");
4011219Skas 		fclose(fi);
4021219Skas 		return(1);
4031219Skas 	}
4041519Skas 	sstack[++ssp].s_file = input;
4051519Skas 	sstack[ssp].s_cond = cond;
4065785Skurt 	sstack[ssp].s_loading = loading;
4075785Skurt 	loading = 0;
4081519Skas 	cond = CANY;
4091219Skas 	input = fi;
4101219Skas 	sourcing++;
4111219Skas 	return(0);
4121219Skas }
4131219Skas 
4141219Skas /*
4151219Skas  * Source a file, but do nothing if the file cannot be opened.
4161219Skas  */
4171219Skas 
4181219Skas source1(name)
4191219Skas 	char name[];
4201219Skas {
4211219Skas 	register int f;
4221219Skas 
4231219Skas 	if ((f = open(name, 0)) < 0)
4241219Skas 		return(0);
4251219Skas 	close(f);
4261219Skas 	source(name);
4271219Skas }
4281219Skas 
4291219Skas /*
4301219Skas  * Pop the current input back to the previous level.
4311219Skas  * Update the "sourcing" flag as appropriate.
4321219Skas  */
4331219Skas 
4341219Skas unstack()
4351219Skas {
4361219Skas 	if (ssp < 0) {
4371219Skas 		printf("\"Source\" stack over-pop.\n");
4381219Skas 		sourcing = 0;
4391219Skas 		return(1);
4401219Skas 	}
4411219Skas 	fclose(input);
4421519Skas 	if (cond != CANY)
4431519Skas 		printf("Unmatched \"if\"\n");
4441519Skas 	cond = sstack[ssp].s_cond;
4455785Skurt 	loading = sstack[ssp].s_loading;
4461519Skas 	input = sstack[ssp--].s_file;
4471219Skas 	if (ssp < 0)
4485785Skurt 		sourcing = loading;
4491219Skas 	return(0);
4501219Skas }
4511219Skas 
4521219Skas /*
4531219Skas  * Touch the indicated file.
4541219Skas  * This is nifty for the shell.
4551219Skas  * If we have the utime() system call, this is better served
4561219Skas  * by using that, since it will work for empty files.
4571219Skas  * On non-utime systems, we must sleep a second, then read.
4581219Skas  */
4591219Skas 
4601219Skas alter(name)
4611219Skas 	char name[];
4621219Skas {
4631219Skas #ifdef UTIME
4641219Skas 	struct stat statb;
4651219Skas 	long time();
4661219Skas 	time_t time_p[2];
4671219Skas #else
4681219Skas 	register int pid, f;
4691219Skas 	char w;
4701219Skas #endif UTIME
4711219Skas 
4721219Skas #ifdef UTIME
4731219Skas 	if (stat(name, &statb) < 0)
4741219Skas 		return;
4751219Skas 	time_p[0] = time((long *) 0) + 1;
4761219Skas 	time_p[1] = statb.st_mtime;
4771219Skas 	utime(name, time_p);
4781219Skas #else
4791219Skas 	sleep(1);
4801219Skas 	if ((f = open(name, 0)) < 0)
4814389Skurt 		return;
4821219Skas 	read(f, &w, 1);
4831219Skas 	exit(0);
4841219Skas #endif
4851219Skas }
4861219Skas 
4871219Skas /*
4881219Skas  * Examine the passed line buffer and
4891219Skas  * return true if it is all blanks and tabs.
4901219Skas  */
4911219Skas 
4921219Skas blankline(linebuf)
4931219Skas 	char linebuf[];
4941219Skas {
4951219Skas 	register char *cp;
4961219Skas 
4971219Skas 	for (cp = linebuf; *cp; cp++)
49818661Sserge 		if (*cp != ' ' && *cp != '\t')
4991219Skas 			return(0);
5001219Skas 	return(1);
5011219Skas }
5021219Skas 
5031219Skas /*
5043195Skas  * Get sender's name from this message.  If the message has
5053195Skas  * a bunch of arpanet stuff in it, we may have to skin the name
5063195Skas  * before returning it.
5073195Skas  */
5083195Skas char *
5093195Skas nameof(mp, reptype)
5103195Skas 	register struct message *mp;
5113195Skas {
5125237Skurt 	register char *cp, *cp2;
5133195Skas 
5145237Skurt 	cp = skin(name1(mp, reptype));
5155237Skurt 	if (reptype != 0 || charcount(cp, '!') < 2)
5165237Skurt 		return(cp);
5175237Skurt 	cp2 = rindex(cp, '!');
5185237Skurt 	cp2--;
5195237Skurt 	while (cp2 > cp && *cp2 != '!')
5205237Skurt 		cp2--;
5215237Skurt 	if (*cp2 == '!')
5225237Skurt 		return(cp2 + 1);
5235237Skurt 	return(cp);
5243195Skas }
5253195Skas 
5263195Skas /*
527*25912Smckusick  * Skin an arpa net address according to the RFC 822 interpretation
5283195Skas  * of "host-phrase."
5293195Skas  */
5303195Skas char *
5313195Skas skin(name)
5323195Skas 	char *name;
5333195Skas {
5343195Skas 	register int c;
5353195Skas 	register char *cp, *cp2;
536*25912Smckusick 	char *bufend;
5373195Skas 	int gotlt, lastsp;
5383195Skas 	char nbuf[BUFSIZ];
53918661Sserge 	int nesting;
5403195Skas 
5413195Skas 	if (name == NOSTR)
5423195Skas 		return(NOSTR);
54312819Sleres 	if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
54412819Sleres 	&& index(name, ' ') == NOSTR)
5453195Skas 		return(name);
5463195Skas 	gotlt = 0;
5473195Skas 	lastsp = 0;
548*25912Smckusick 	bufend = nbuf;
549*25912Smckusick 	for (cp = name, cp2 = bufend; c = *cp++; ) {
5503195Skas 		switch (c) {
5513195Skas 		case '(':
552*25912Smckusick 			/*
553*25912Smckusick 			 * Start of a "comment".
554*25912Smckusick 			 * Ignore it.
555*25912Smckusick 			 */
55618661Sserge 			nesting = 1;
557*25912Smckusick 			while ((c = *cp) != 0) {
558*25912Smckusick 				cp++;
559*25912Smckusick 				switch (c) {
560*25912Smckusick 				case '\\':
561*25912Smckusick 					if (*cp == 0)
562*25912Smckusick 						goto outcm;
563*25912Smckusick 					cp++;
564*25912Smckusick 					break;
56518661Sserge 				case '(':
56618661Sserge 					nesting++;
56718661Sserge 					break;
56818661Sserge 
56918661Sserge 				case ')':
57018661Sserge 					--nesting;
57118661Sserge 					break;
57218661Sserge 				}
57318661Sserge 
57418661Sserge 				if (nesting <= 0)
57518661Sserge 					break;
57618661Sserge 			}
577*25912Smckusick 		outcm:
57812819Sleres 			lastsp = 0;
5793195Skas 			break;
5803195Skas 
581*25912Smckusick 		case '"':
582*25912Smckusick 			/*
583*25912Smckusick 			 * Start of a "quoted-string".
584*25912Smckusick 			 * Copy it in its entirety.
585*25912Smckusick 			 */
586*25912Smckusick 			while ((c = *cp) != 0) {
587*25912Smckusick 				cp++;
588*25912Smckusick 				switch (c) {
589*25912Smckusick 				case '\\':
590*25912Smckusick 					if ((c = *cp) == 0)
591*25912Smckusick 						goto outqs;
592*25912Smckusick 					cp++;
593*25912Smckusick 					break;
594*25912Smckusick 				case '"':
595*25912Smckusick 					goto outqs;
596*25912Smckusick 				}
597*25912Smckusick 				*cp2++ = c;
598*25912Smckusick 			}
599*25912Smckusick 		outqs:
600*25912Smckusick 			lastsp = 0;
601*25912Smckusick 			break;
602*25912Smckusick 
6033195Skas 		case ' ':
60412819Sleres 			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
60512819Sleres 				cp += 3, *cp2++ = '@';
60612819Sleres 			else
60712819Sleres 			if (cp[0] == '@' && cp[1] == ' ')
60812819Sleres 				cp += 2, *cp2++ = '@';
60912819Sleres 			else
61012819Sleres 				lastsp = 1;
6113195Skas 			break;
6123195Skas 
6133195Skas 		case '<':
614*25912Smckusick 			cp2 = bufend;
6153195Skas 			gotlt++;
6163195Skas 			lastsp = 0;
6173195Skas 			break;
6183195Skas 
6193195Skas 		case '>':
620*25912Smckusick 			if (gotlt) {
621*25912Smckusick 				gotlt = 0;
622*25912Smckusick 				while (*cp != ',' && *cp != 0)
623*25912Smckusick 					cp++;
624*25912Smckusick 				if (*cp == 0 )
625*25912Smckusick 					goto done;
626*25912Smckusick 				*cp2++ = ',';
627*25912Smckusick 				*cp2++ = ' ';
628*25912Smckusick 				bufend = cp2;
629*25912Smckusick 				break;
630*25912Smckusick 			}
6313195Skas 
6323195Skas 			/* Fall into . . . */
6333195Skas 
6343195Skas 		default:
6353195Skas 			if (lastsp) {
6363195Skas 				lastsp = 0;
6373195Skas 				*cp2++ = ' ';
6383195Skas 			}
6393195Skas 			*cp2++ = c;
6403195Skas 			break;
6413195Skas 		}
6423195Skas 	}
6433195Skas done:
6443195Skas 	*cp2 = 0;
6453195Skas 
6463195Skas 	return(savestr(nbuf));
6473195Skas }
6483195Skas 
6493195Skas /*
6501219Skas  * Fetch the sender's name from the passed message.
6513195Skas  * Reptype can be
6523195Skas  *	0 -- get sender's name for display purposes
6533195Skas  *	1 -- get sender's name for reply
6543195Skas  *	2 -- get sender's name for Reply
6551219Skas  */
6561219Skas 
6571219Skas char *
6583195Skas name1(mp, reptype)
6591219Skas 	register struct message *mp;
6601219Skas {
6611219Skas 	char namebuf[LINESIZE];
6621219Skas 	char linebuf[LINESIZE];
6631219Skas 	register char *cp, *cp2;
6641219Skas 	register FILE *ibuf;
6651219Skas 	int first = 1;
6661219Skas 
6673195Skas 	if ((cp = hfield("from", mp)) != NOSTR)
6683195Skas 		return(cp);
6693195Skas 	if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
6703195Skas 		return(cp);
6711219Skas 	ibuf = setinput(mp);
6721219Skas 	copy("", namebuf);
6731219Skas 	if (readline(ibuf, linebuf) <= 0)
6741219Skas 		return(savestr(namebuf));
6751219Skas newname:
6761219Skas 	for (cp = linebuf; *cp != ' '; cp++)
6771219Skas 		;
6781219Skas 	while (any(*cp, " \t"))
6791219Skas 		cp++;
6801219Skas 	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
6811219Skas 	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
6821219Skas 		;
6831219Skas 	*cp2 = '\0';
6841219Skas 	if (readline(ibuf, linebuf) <= 0)
6851219Skas 		return(savestr(namebuf));
6861219Skas 	if ((cp = index(linebuf, 'F')) == NULL)
6871219Skas 		return(savestr(namebuf));
6881219Skas 	if (strncmp(cp, "From", 4) != 0)
6891219Skas 		return(savestr(namebuf));
6901219Skas 	while ((cp = index(cp, 'r')) != NULL) {
6911219Skas 		if (strncmp(cp, "remote", 6) == 0) {
6921219Skas 			if ((cp = index(cp, 'f')) == NULL)
6931219Skas 				break;
6941219Skas 			if (strncmp(cp, "from", 4) != 0)
6951219Skas 				break;
6961219Skas 			if ((cp = index(cp, ' ')) == NULL)
6971219Skas 				break;
6981219Skas 			cp++;
6991219Skas 			if (first) {
7001219Skas 				copy(cp, namebuf);
7011219Skas 				first = 0;
7021219Skas 			} else
7031219Skas 				strcpy(rindex(namebuf, '!')+1, cp);
7041219Skas 			strcat(namebuf, "!");
7051219Skas 			goto newname;
7061219Skas 		}
7071219Skas 		cp++;
7081219Skas 	}
7091219Skas 	return(savestr(namebuf));
7101219Skas }
7111219Skas 
7121219Skas /*
7135237Skurt  * Count the occurances of c in str
7145237Skurt  */
7155237Skurt charcount(str, c)
7165237Skurt 	char *str;
7175237Skurt {
7185237Skurt 	register char *cp;
7195237Skurt 	register int i;
7205237Skurt 
7215237Skurt 	for (i = 0, cp = str; *cp; cp++)
7225237Skurt 		if (*cp == c)
7235237Skurt 			i++;
7245237Skurt 	return(i);
7255237Skurt }
7265237Skurt 
7275237Skurt /*
7281219Skas  * Find the rightmost pointer to an instance of the
7291219Skas  * character in the string and return it.
7301219Skas  */
7311219Skas char *
7321219Skas rindex(str, c)
7331219Skas 	char str[];
7341219Skas 	register int c;
7351219Skas {
7361219Skas 	register char *cp, *cp2;
7371219Skas 
7381219Skas 	for (cp = str, cp2 = NOSTR; *cp; cp++)
7391219Skas 		if (c == *cp)
7401219Skas 			cp2 = cp;
7411219Skas 	return(cp2);
7421219Skas }
7431219Skas 
7441219Skas /*
7451219Skas  * See if the string is a number.
7461219Skas  */
7471219Skas 
7481219Skas numeric(str)
7491219Skas 	char str[];
7501219Skas {
7511219Skas 	register char *cp = str;
7521219Skas 
7531219Skas 	while (*cp)
7541219Skas 		if (!isdigit(*cp++))
7551219Skas 			return(0);
7561219Skas 	return(1);
7571219Skas }
7581219Skas 
7591219Skas /*
7601219Skas  * Are any of the characters in the two strings the same?
7611219Skas  */
7621219Skas 
7631219Skas anyof(s1, s2)
7641219Skas 	register char *s1, *s2;
7651219Skas {
7661219Skas 	register int c;
7671219Skas 
7681219Skas 	while (c = *s1++)
7691219Skas 		if (any(c, s2))
7701219Skas 			return(1);
7711219Skas 	return(0);
7721219Skas }
7731219Skas 
7741219Skas /*
7751219Skas  * Determine the leftmost index of the character
7761219Skas  * in the string.
7771219Skas  */
7781219Skas 
7791219Skas char *
7801219Skas index(str, ch)
7811219Skas 	char *str;
7821219Skas {
7831219Skas 	register char *cp;
7841219Skas 	register int c;
7851219Skas 
7861219Skas 	for (c = ch, cp = str; *cp; cp++)
7871219Skas 		if (*cp == c)
7881219Skas 			return(cp);
7891219Skas 	return(NOSTR);
7901219Skas }
7911219Skas 
7921219Skas /*
7937571Skurt  * See if the given header field is supposed to be ignored.
7947571Skurt  */
7957571Skurt isign(field)
7967571Skurt 	char *field;
7977571Skurt {
7987571Skurt 	char realfld[BUFSIZ];
7997571Skurt 
80018661Sserge 	/*
80118661Sserge 	 * Lower-case the string, so that "Status" and "status"
80218661Sserge 	 * will hash to the same place.
80318661Sserge 	 */
8047571Skurt 	istrcpy(realfld, field);
80518661Sserge 
80618661Sserge 	if (nretained > 0)
80718661Sserge 		return (!member(realfld, retain));
80818661Sserge 	else
80918661Sserge 		return (member(realfld, ignore));
8107571Skurt }
81118661Sserge 
81218661Sserge member(realfield, table)
81318661Sserge 	register char *realfield;
81418661Sserge 	register struct ignore **table;
81518661Sserge {
81618661Sserge 	register struct ignore *igp;
81718661Sserge 
81818661Sserge 	for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
81918661Sserge 		if (equal(igp->i_field, realfield))
82018661Sserge 			return (1);
82118661Sserge 
82218661Sserge 	return (0);
82318661Sserge }
824