util.c revision 77274
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
496196Sdes *
596196Sdes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
796196Sdes * are met:
896196Sdes * 1. Redistributions of source code must retain the above copyright
996196Sdes *    notice, this list of conditions and the following disclaimer.
1096196Sdes * 2. Redistributions in binary form must reproduce the above copyright
1196196Sdes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
351590Srgrimes#if 0
361590Srgrimesstatic char sccsid[] = "@(#)aux.c	8.1 (Berkeley) 6/6/93";
371590Srgrimes#endif
381590Srgrimesstatic const char rcsid[] =
391590Srgrimes  "$FreeBSD: head/usr.bin/mail/aux.c 77274 2001-05-27 20:26:22Z mikeh $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
4227919Scharnier#include "rcv.h"
431590Srgrimes#include "extern.h"
441590Srgrimes
451590Srgrimes/*
461590Srgrimes * Mail -- a mail program
47105235Scharnier *
481590Srgrimes * Auxiliary functions.
4929922Smarkm */
501590Srgrimes
51105235Scharnierstatic char *save2str __P((char *, char *));
521590Srgrimes
53105235Scharnier/*
54105235Scharnier * Return a pointer to a dynamic copy of the argument.
55105235Scharnier */
561590Srgrimeschar *
571590Srgrimessavestr(str)
581590Srgrimes	char *str;
5995621Smarkm{
601590Srgrimes	char *new;
61120547Stjr	int size = strlen(str) + 1;
621590Srgrimes
631590Srgrimes	if ((new = salloc(size)) != NULL)
641590Srgrimes		bcopy(str, new, size);
651590Srgrimes	return (new);
661590Srgrimes}
671590Srgrimes
681590Srgrimes/*
691590Srgrimes * Make a copy of new argument incorporating old one.
708232Sdg */
711590Srgrimeschar *
7227919Scharniersave2str(str, old)
731590Srgrimes	char *str, *old;
741590Srgrimes{
7540103Smarkm	char *new;
761590Srgrimes	int newsize = strlen(str) + 1;
7796197Sdes	int oldsize = old ? strlen(old) + 1 : 0;
781590Srgrimes
791590Srgrimes	if ((new = salloc(newsize + oldsize)) != NULL) {
80120547Stjr		if (oldsize) {
811590Srgrimes			bcopy(old, new, oldsize);
821590Srgrimes			new[oldsize - 1] = ' ';
831590Srgrimes		}
841590Srgrimes		bcopy(str, new + oldsize, newsize);
851590Srgrimes	}
861590Srgrimes	return (new);
871590Srgrimes}
881590Srgrimes
891590Srgrimes/*
901590Srgrimes * Touch the named message by setting its MTOUCH flag.
911590Srgrimes * Touched messages have the effect of not being sent
921590Srgrimes * back to the system mailbox on exit.
931590Srgrimes */
941590Srgrimesvoid
951590Srgrimestouch(mp)
96120547Stjr	struct message *mp;
97120547Stjr{
98120547Stjr
9957232Sshin	mp->m_flag |= MTOUCH;
1001590Srgrimes	if ((mp->m_flag & MREAD) == 0)
1011590Srgrimes		mp->m_flag |= MREAD|MSTATUS;
1021590Srgrimes}
1031590Srgrimes
1041590Srgrimes/*
1051590Srgrimes * Test to see if the passed file name is a directory.
1061590Srgrimes * Return true if it is.
10792921Simp */
10892921Simpint
10992921Simpisdir(name)
11092921Simp	char name[];
11192921Simp{
112105268Smarkm	struct stat sbuf;
11392921Simp
11492921Simp	if (stat(name, &sbuf) < 0)
11595621Smarkm		return (0);
11692921Simp	return (S_ISDIR(sbuf.st_mode));
11792921Simp}
11892921Simp
11992921Simp/*
12092921Simp * Count the number of arguments in the given string raw list.
12192921Simp */
12292921Simpint
12392921Simpargcount(argv)
12492921Simp	char **argv;
1251590Srgrimes{
1261590Srgrimes	char **ap;
12793057Simp
1281590Srgrimes	for (ap = argv; *ap++ != NULL;)
1291590Srgrimes		;
1301590Srgrimes	return (ap - argv - 1);
131120547Stjr}
1321590Srgrimes
133105268Smarkm/*
134105268Smarkm * Return the desired header line from the passed message
13547549Sbde * pointer (or NULL if the desired header field is not available).
136120547Stjr */
13796196Sdeschar *
138148726Sstefanfhfield(field, mp)
139148726Sstefanf	const char *field;
1401590Srgrimes	struct message *mp;
1418232Sdg{
1421590Srgrimes	FILE *ibuf;
14347549Sbde	char linebuf[LINESIZE];
1441590Srgrimes	int lc;
14529922Smarkm	char *hfield;
1461590Srgrimes	char *colon, *oldhfield = NULL;
1471590Srgrimes
1481590Srgrimes	ibuf = setinput(mp);
1491590Srgrimes	if ((lc = mp->m_lines - 1) < 0)
1501590Srgrimes		return (NULL);
1511590Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
1521590Srgrimes		return (NULL);
1531590Srgrimes	while (lc > 0) {
1541590Srgrimes		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
1551590Srgrimes			return (oldhfield);
1561590Srgrimes		if ((hfield = ishfield(linebuf, colon, field)) != NULL)
1571590Srgrimes			oldhfield = save2str(hfield, oldhfield);
1581590Srgrimes	}
159140569Sru	return (oldhfield);
16024360Simp}
1611590Srgrimes
16257232Sshin/*
16357232Sshin * Return the next header field found in the given message.
16457232Sshin * Return >= 0 if something found, < 0 elsewise.
16557232Sshin * "colon" is set to point to the colon in the header.
16657232Sshin * Must deal with \ continuations & other such fraud.
16757232Sshin */
16857232Sshinint
16957232Sshingethfield(f, linebuf, rem, colon)
1701590Srgrimes	FILE *f;
1711590Srgrimes	char linebuf[];
1721590Srgrimes	int rem;
1738232Sdg	char **colon;
1748232Sdg{
1758232Sdg	char line2[LINESIZE];
1761590Srgrimes	char *cp, *cp2;
1771590Srgrimes	int c;
1781590Srgrimes
1791590Srgrimes	for (;;) {
1801590Srgrimes		if (--rem < 0)
1811590Srgrimes			return (-1);
1821590Srgrimes		if ((c = readline(f, linebuf, LINESIZE)) <= 0)
1831590Srgrimes			return (-1);
1841590Srgrimes		for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
1851590Srgrimes		    cp++)
18647488Speter			;
18747549Sbde		if (*cp != ':' || cp == linebuf)
18847549Sbde			continue;
18947488Speter		/*
19047488Speter		 * I guess we got a headline.
1911590Srgrimes		 * Handle wraparounding
1921590Srgrimes		 */
1931590Srgrimes		*colon = cp;
1941590Srgrimes		cp = linebuf + c;
1951590Srgrimes		for (;;) {
1961590Srgrimes			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
1971590Srgrimes				;
1981590Srgrimes			cp++;
1991590Srgrimes			if (rem <= 0)
2001590Srgrimes				break;
20134897Smarkm			ungetc(c = getc(f), f);
2021590Srgrimes			if (c != ' ' && c != '\t')
2031590Srgrimes				break;
20434897Smarkm			if ((c = readline(f, line2, LINESIZE)) < 0)
2051590Srgrimes				break;
2061590Srgrimes			rem--;
20727919Scharnier			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
20827919Scharnier				;
2091590Srgrimes			c -= cp2 - line2;
2101590Srgrimes			if (cp + c >= linebuf + LINESIZE - 2)
21147488Speter				break;
21247488Speter			*cp++ = ' ';
2131590Srgrimes			bcopy(cp2, cp, c);
2141590Srgrimes			cp += c;
215105268Smarkm		}
2161590Srgrimes		*cp = 0;
21727919Scharnier		return (rem);
2181590Srgrimes	}
219120547Stjr	/* NOTREACHED */
220120547Stjr}
221120547Stjr
222120547Stjr/*
223120547Stjr * Check whether the passed line is a header line of
224120547Stjr * the desired breed.  Return the field body, or 0.
225120547Stjr */
226120547Stjr
227120547Stjrchar*
2281590Srgrimesishfield(linebuf, colon, field)
2291590Srgrimes	char linebuf[];
2301590Srgrimes	char *colon;
2311590Srgrimes	const char *field;
2321590Srgrimes{
2331590Srgrimes	char *cp = colon;
2341590Srgrimes
2351590Srgrimes	*cp = 0;
2361590Srgrimes	if (strcasecmp(linebuf, field) != 0) {
2371590Srgrimes		*cp = ':';
2381590Srgrimes		return (0);
2391590Srgrimes	}
2401590Srgrimes	*cp = ':';
2411590Srgrimes	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
2421590Srgrimes		;
2431590Srgrimes	return (cp);
24457232Sshin}
2451590Srgrimes
2461590Srgrimes/*
2471590Srgrimes * Copy a string and lowercase the result.
2481590Srgrimes * dsize: space left in buffer (including space for NULL)
2491590Srgrimes */
2501590Srgrimesvoid
25127919Scharnieristrncpy(dest, src, dsize)
2528232Sdg	char *dest;
2538232Sdg	const char *src;
25427919Scharnier	size_t dsize;
2558232Sdg{
25656590Sshin
2571590Srgrimes	strlcpy(dest, src, dsize);
25856590Sshin	while (*dest)
25956590Sshin		*dest++ = tolower(*dest);
26056590Sshin}
26156590Sshin
26256590Sshin/*
26356590Sshin * The following code deals with input stacking to do source
26456590Sshin * commands.  All but the current file pointer are saved on
26556590Sshin * the stack.
2661590Srgrimes */
2671590Srgrimes
2681590Srgrimesstatic	int	ssp;			/* Top of file stack */
2691590Srgrimesstruct sstack {
2701590Srgrimes	FILE	*s_file;		/* File we were in. */
2711590Srgrimes	int	s_cond;			/* Saved state of conditionals */
272120547Stjr	int	s_loading;		/* Loading .mailrc, etc. */
2731590Srgrimes};
2741590Srgrimes#define	SSTACK_SIZE	64		/* XXX was NOFILE. */
27593057Simpstatic struct sstack sstack[SSTACK_SIZE];
2761590Srgrimes
2771590Srgrimes/*
2781590Srgrimes * Pushdown current input file and switch to a new one.
2791590Srgrimes * Set the global flag "sourcing" so that others will realize
2801590Srgrimes * that they are no longer reading from a tty (in all probability).
281120547Stjr */
2821590Srgrimesint
2831590Srgrimessource(arglist)
28427919Scharnier	char **arglist;
2851590Srgrimes{
2861590Srgrimes	FILE *fi;
2871590Srgrimes	char *cp;
2881590Srgrimes
289105235Scharnier	if ((cp = expand(*arglist)) == NULL)
2901590Srgrimes		return (1);
2911590Srgrimes	if ((fi = Fopen(cp, "r")) == NULL) {
2921590Srgrimes		warn("%s", cp);
293105235Scharnier		return (1);
2941590Srgrimes	}
2951590Srgrimes	if (ssp >= SSTACK_SIZE - 1) {
2961590Srgrimes		printf("Too much \"sourcing\" going on.\n");
2971590Srgrimes		(void)Fclose(fi);
2981590Srgrimes		return (1);
2991590Srgrimes	}
3001590Srgrimes	sstack[ssp].s_file = input;
3011590Srgrimes	sstack[ssp].s_cond = cond;
3021590Srgrimes	sstack[ssp].s_loading = loading;
3031590Srgrimes	ssp++;
3041590Srgrimes	loading = 0;
3051590Srgrimes	cond = CANY;
3061590Srgrimes	input = fi;
307105235Scharnier	sourcing++;
3081590Srgrimes	return (0);
3091590Srgrimes}
3101590Srgrimes
3111590Srgrimes/*
3121590Srgrimes * Pop the current input back to the previous level.
31393057Simp * Update the "sourcing" flag as appropriate.
3141590Srgrimes */
3151590Srgrimesint
3161590Srgrimesunstack()
3171590Srgrimes{
3181590Srgrimes	if (ssp <= 0) {
3191590Srgrimes		printf("\"Source\" stack over-pop.\n");
3201590Srgrimes		sourcing = 0;
3211590Srgrimes		return (1);
32218286Sbde	}
32393057Simp	(void)Fclose(input);
3241590Srgrimes	if (cond != CANY)
3251590Srgrimes		printf("Unmatched \"if\"\n");
3261590Srgrimes	ssp--;
3271590Srgrimes	cond = sstack[ssp].s_cond;
3281590Srgrimes	loading = sstack[ssp].s_loading;
3291590Srgrimes	input = sstack[ssp].s_file;
3301590Srgrimes	if (ssp == 0)
3311590Srgrimes		sourcing = loading;
3321590Srgrimes	return (0);
3331590Srgrimes}
3341590Srgrimes
3351590Srgrimes/*
3361590Srgrimes * Touch the indicated file.
3371590Srgrimes * This is nifty for the shell.
3381590Srgrimes */
3391590Srgrimesvoid
3401590Srgrimesalter(name)
3411590Srgrimes	char *name;
3421590Srgrimes{
343105268Smarkm	struct stat sb;
3441590Srgrimes	struct timeval tv[2];
34595621Smarkm
3461590Srgrimes	if (stat(name, &sb))
3471590Srgrimes		return;
3481590Srgrimes	tv[0].tv_sec = time((time_t *)0) + 1;
3491590Srgrimes	tv[1].tv_sec = sb.st_mtime;
3501590Srgrimes	tv[0].tv_usec = tv[1].tv_usec = 0;
3511590Srgrimes	(void)utimes(name, tv);
3521590Srgrimes}
3531590Srgrimes
354105268Smarkm/*
3551590Srgrimes * Get sender's name from this message.  If the message has
35695621Smarkm * a bunch of arpanet stuff in it, we may have to skin the name
3571590Srgrimes * before returning it.
35897788Smike */
35997788Smikechar *
3601590Srgrimesnameof(mp, reptype)
3611590Srgrimes	struct message *mp;
36297788Smike	int reptype;
3631590Srgrimes{
3641590Srgrimes	char *cp, *cp2;
3651590Srgrimes
3661590Srgrimes	cp = skin(name1(mp, reptype));
36797788Smike	if (reptype != 0 || charcount(cp, '!') < 2)
3681590Srgrimes		return (cp);
3691590Srgrimes	cp2 = strrchr(cp, '!');
3701590Srgrimes	cp2--;
3711590Srgrimes	while (cp2 > cp && *cp2 != '!')
3721590Srgrimes		cp2--;
3731590Srgrimes	if (*cp2 == '!')
3741590Srgrimes		return (cp2 + 1);
3751590Srgrimes	return (cp);
3761590Srgrimes}
3771590Srgrimes
3781590Srgrimes/*
37993057Simp * Start of a "comment".
3801590Srgrimes * Ignore it.
38193057Simp */
3821590Srgrimeschar *
3831590Srgrimesskip_comment(cp)
3841590Srgrimes	char *cp;
3851590Srgrimes{
3861590Srgrimes	int nesting = 1;
3871590Srgrimes
3881590Srgrimes	for (; nesting > 0 && *cp; cp++) {
3891590Srgrimes		switch (*cp) {
3901590Srgrimes		case '\\':
3911590Srgrimes			if (cp[1])
3921590Srgrimes				cp++;
3931590Srgrimes			break;
3941590Srgrimes		case '(':
3951590Srgrimes			nesting++;
3961590Srgrimes			break;
3971590Srgrimes		case ')':
3981590Srgrimes			nesting--;
3991590Srgrimes			break;
4001590Srgrimes		}
4011590Srgrimes	}
4021590Srgrimes	return (cp);
4031590Srgrimes}
4041590Srgrimes
4051590Srgrimes/*
4061590Srgrimes * Skin an arpa net address according to the RFC 822 interpretation
4071590Srgrimes * of "host-phrase."
408120547Stjr */
4091590Srgrimeschar *
4101590Srgrimesskin(name)
4111590Srgrimes	char *name;
412120547Stjr{
413120547Stjr	char *nbuf, *bufend, *cp, *cp2;
4141590Srgrimes	int c, gotlt, lastsp;
4151590Srgrimes
4161590Srgrimes	if (name == NULL)
4171590Srgrimes		return (NULL);
4181590Srgrimes	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
4191590Srgrimes	    && strchr(name, ' ') == NULL)
420105268Smarkm		return (name);
4211590Srgrimes
4221590Srgrimes	/* We assume that length(input) <= length(output) */
423105268Smarkm	if ((nbuf = malloc(strlen(name) + 1)) == NULL)
424105268Smarkm		err(1, "Out of memory");
425105268Smarkm	gotlt = 0;
426105268Smarkm	lastsp = 0;
427120547Stjr	bufend = nbuf;
428120547Stjr	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
429120547Stjr		switch (c) {
430120547Stjr		case '(':
4311590Srgrimes			cp = skip_comment(cp);
4321590Srgrimes			lastsp = 0;
4331590Srgrimes			break;
4341590Srgrimes
4351590Srgrimes		case '"':
43693057Simp			/*
4371590Srgrimes			 * Start of a "quoted-string".
43893057Simp			 * Copy it in its entirety.
4391590Srgrimes			 */
4401590Srgrimes			while ((c = *cp) != '\0') {
4411590Srgrimes				cp++;
4421590Srgrimes				if (c == '"')
4431590Srgrimes					break;
4441590Srgrimes				if (c != '\\')
4451590Srgrimes					*cp2++ = c;
4461590Srgrimes				else if ((c = *cp) != '\0') {
4471590Srgrimes					*cp2++ = c;
4481590Srgrimes					cp++;
4491590Srgrimes				}
4501590Srgrimes			}
4511590Srgrimes			lastsp = 0;
4521590Srgrimes			break;
4531590Srgrimes
4541590Srgrimes		case ' ':
4551590Srgrimes			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
4561590Srgrimes				cp += 3, *cp2++ = '@';
4571590Srgrimes			else
4581590Srgrimes			if (cp[0] == '@' && cp[1] == ' ')
4591590Srgrimes				cp += 2, *cp2++ = '@';
4601590Srgrimes			else
4611590Srgrimes				lastsp = 1;
462120547Stjr			break;
4631590Srgrimes
4641590Srgrimes		case '<':
4651590Srgrimes			cp2 = bufend;
4661590Srgrimes			gotlt++;
4671590Srgrimes			lastsp = 0;
468105268Smarkm			break;
4691590Srgrimes
47095621Smarkm		case '>':
4711590Srgrimes			if (gotlt) {
4721590Srgrimes				gotlt = 0;
4731590Srgrimes				while ((c = *cp) != '\0' && c != ',') {
4741590Srgrimes					cp++;
4751590Srgrimes					if (c == '(')
4761590Srgrimes						cp = skip_comment(cp);
4771590Srgrimes					else if (c == '"')
4781590Srgrimes						while ((c = *cp) != '\0') {
4791590Srgrimes							cp++;
4801590Srgrimes							if (c == '"')
4811590Srgrimes								break;
4821590Srgrimes							if (c == '\\' && *cp != '\0')
4831590Srgrimes								cp++;
4841590Srgrimes						}
48593057Simp				}
4861590Srgrimes				lastsp = 0;
4871590Srgrimes				break;
4881590Srgrimes			}
4891590Srgrimes			/* Fall into . . . */
4901590Srgrimes
4911590Srgrimes		default:
4921590Srgrimes			if (lastsp) {
4931590Srgrimes				lastsp = 0;
4941590Srgrimes				*cp2++ = ' ';
4951590Srgrimes			}
4961590Srgrimes			*cp2++ = c;
4971590Srgrimes			if (c == ',' && !gotlt) {
4981590Srgrimes				*cp2++ = ' ';
4991590Srgrimes				for (; *cp == ' '; cp++)
500105268Smarkm					;
5011590Srgrimes				lastsp = 0;
5021590Srgrimes				bufend = cp2;
5031590Srgrimes			}
5041590Srgrimes		}
5051590Srgrimes	}
5061590Srgrimes	*cp2 = '\0';
5071590Srgrimes
5081590Srgrimes	if ((nbuf = realloc(nbuf, strlen(nbuf) + 1)) == NULL)
5091590Srgrimes		err(1, "Out of memory");
510105268Smarkm	return (nbuf);
511105268Smarkm}
5121590Srgrimes
5131590Srgrimes/*
514105268Smarkm * Fetch the sender's name from the passed message.
5151590Srgrimes * Reptype can be
51695621Smarkm *	0 -- get sender's name for display purposes
5171590Srgrimes *	1 -- get sender's name for reply
518120547Stjr *	2 -- get sender's name for Reply
519120547Stjr */
5201590Srgrimeschar *
5211590Srgrimesname1(mp, reptype)
5221590Srgrimes	struct message *mp;
5231590Srgrimes	int reptype;
5241590Srgrimes{
5251590Srgrimes	char namebuf[LINESIZE];
5261590Srgrimes	char linebuf[LINESIZE];
5271590Srgrimes	char *cp, *cp2;
5281590Srgrimes	FILE *ibuf;
5291590Srgrimes	int first = 1;
5301590Srgrimes
53195621Smarkm	if ((cp = hfield("from", mp)) != NULL)
5321590Srgrimes		return (cp);
5331590Srgrimes	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
5341590Srgrimes		return (cp);
5351590Srgrimes	ibuf = setinput(mp);
5361590Srgrimes	namebuf[0] = '\0';
5371590Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
5381590Srgrimes		return (savestr(namebuf));
5391590Srgrimesnewname:
5401590Srgrimes	for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++)
5411590Srgrimes		;
5421590Srgrimes	for (; *cp == ' ' || *cp == '\t'; cp++)
5431590Srgrimes		;
5441590Srgrimes	for (cp2 = &namebuf[strlen(namebuf)];
5451590Srgrimes	    *cp != '\0' && *cp != ' ' && *cp != '\t' &&
5461590Srgrimes	    cp2 < namebuf + LINESIZE - 1;)
5471590Srgrimes		*cp2++ = *cp++;
5481590Srgrimes	*cp2 = '\0';
5491590Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
5501590Srgrimes		return (savestr(namebuf));
5511590Srgrimes	if ((cp = strchr(linebuf, 'F')) == NULL)
552120547Stjr		return (savestr(namebuf));
553120547Stjr	if (strncmp(cp, "From", 4) != 0)
554120547Stjr		return (savestr(namebuf));
5551590Srgrimes	while ((cp = strchr(cp, 'r')) != NULL) {
5561590Srgrimes		if (strncmp(cp, "remote", 6) == 0) {
557120547Stjr			if ((cp = strchr(cp, 'f')) == NULL)
558120547Stjr				break;
559120547Stjr			if (strncmp(cp, "from", 4) != 0)
5601590Srgrimes				break;
5611590Srgrimes			if ((cp = strchr(cp, ' ')) == NULL)
562120547Stjr				break;
5631590Srgrimes			cp++;
5641590Srgrimes			if (first) {
56527919Scharnier				cp2 = namebuf;
5661590Srgrimes				first = 0;
5671590Srgrimes			} else
5681590Srgrimes				cp2 = strrchr(namebuf, '!') + 1;
5691590Srgrimes			strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
5701590Srgrimes			strcat(namebuf, "!");
5711590Srgrimes			goto newname;
5721590Srgrimes		}
5731590Srgrimes		cp++;
5741590Srgrimes	}
5751590Srgrimes	return (savestr(namebuf));
5761590Srgrimes}
5771590Srgrimes
5781590Srgrimes/*
5791590Srgrimes * Count the occurances of c in str
5801590Srgrimes */
5811590Srgrimesint
5821590Srgrimescharcount(str, c)
5831590Srgrimes	char *str;
5841590Srgrimes	int c;
5851590Srgrimes{
5861590Srgrimes	char *cp;
5871590Srgrimes	int i;
5881590Srgrimes
5891590Srgrimes	for (i = 0, cp = str; *cp != '\0'; cp++)
5901590Srgrimes		if (*cp == c)
5911590Srgrimes			i++;
5921590Srgrimes	return (i);
5931590Srgrimes}
5941590Srgrimes
5951590Srgrimes/*
5961590Srgrimes * See if the given header field is supposed to be ignored.
59793057Simp */
5981590Srgrimesint
599105268Smarkmisign(field, ignore)
6001590Srgrimes	const char *field;
601105268Smarkm	struct ignoretab ignore[2];
6021590Srgrimes{
603105268Smarkm	char realfld[LINESIZE];
6041590Srgrimes
6051590Srgrimes	if (ignore == ignoreall)
60691434Sfenner		return (1);
6071590Srgrimes	/*
6081590Srgrimes	 * Lower-case the string, so that "Status" and "status"
6091590Srgrimes	 * will hash to the same place.
6101590Srgrimes	 */
6111590Srgrimes	istrncpy(realfld, field, sizeof(realfld));
6121590Srgrimes	if (ignore[1].i_count > 0)
6131590Srgrimes		return (!member(realfld, ignore + 1));
6141590Srgrimes	else
6151590Srgrimes		return (member(realfld, ignore));
6161590Srgrimes}
6171590Srgrimes
6181590Srgrimesint
6191590Srgrimesmember(realfield, table)
6201590Srgrimes	char *realfield;
6211590Srgrimes	struct ignoretab *table;
6221590Srgrimes{
6231590Srgrimes	struct ignore *igp;
6241590Srgrimes
6251590Srgrimes	for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link)
6261590Srgrimes		if (*igp->i_field == *realfield &&
627105268Smarkm		    equal(igp->i_field, realfield))
6281590Srgrimes			return (1);
6291590Srgrimes	return (0);
6301590Srgrimes}
6311590Srgrimes