util.c revision 88150
136285Sbrian/*
236285Sbrian * Copyright (c) 1980, 1993
336285Sbrian *	The Regents of the University of California.  All rights reserved.
436285Sbrian *
536285Sbrian * Redistribution and use in source and binary forms, with or without
636285Sbrian * modification, are permitted provided that the following conditions
736285Sbrian * are met:
836285Sbrian * 1. Redistributions of source code must retain the above copyright
936285Sbrian *    notice, this list of conditions and the following disclaimer.
1036285Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1136285Sbrian *    notice, this list of conditions and the following disclaimer in the
1236285Sbrian *    documentation and/or other materials provided with the distribution.
1336285Sbrian * 3. All advertising materials mentioning features or use of this software
1436285Sbrian *    must display the following acknowledgement:
1536285Sbrian *	This product includes software developed by the University of
1636285Sbrian *	California, Berkeley and its contributors.
1736285Sbrian * 4. Neither the name of the University nor the names of its contributors
1836285Sbrian *    may be used to endorse or promote products derived from this software
1936285Sbrian *    without specific prior written permission.
2036285Sbrian *
2136285Sbrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2236285Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2336285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2436285Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2536285Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2650479Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2736285Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2836285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2936452Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3036285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3136285Sbrian * SUCH DAMAGE.
3236285Sbrian */
3356413Sbrian
3436285Sbrian#ifndef lint
3536285Sbrian#if 0
3636285Sbrianstatic char sccsid[] = "@(#)aux.c	8.1 (Berkeley) 6/6/93";
3736285Sbrian#endif
3836285Sbrianstatic const char rcsid[] =
3936285Sbrian  "$FreeBSD: head/usr.bin/mail/aux.c 88150 2001-12-18 20:52:09Z mikeh $";
4036285Sbrian#endif /* not lint */
4153298Sbrian
4253298Sbrian#include "rcv.h"
4353298Sbrian#include "extern.h"
4453298Sbrian
4553298Sbrian/*
4636285Sbrian * Mail -- a mail program
47102500Sbrian *
4836285Sbrian * Auxiliary functions.
4936285Sbrian */
5036285Sbrian
5136285Sbrianstatic char *save2str __P((char *, char *));
5236345Sbrian
5336285Sbrian/*
5436285Sbrian * Return a pointer to a dynamic copy of the argument.
5536285Sbrian */
5646686Sbrianchar *
5737009Sbriansavestr(str)
5836285Sbrian	char *str;
5936285Sbrian{
6036285Sbrian	char *new;
6136285Sbrian	int size = strlen(str) + 1;
6236285Sbrian
6336285Sbrian	if ((new = salloc(size)) != NULL)
6436285Sbrian		bcopy(str, new, size);
6536285Sbrian	return (new);
6636285Sbrian}
6736285Sbrian
6836285Sbrian/*
6981634Sbrian * Make a copy of new argument incorporating old one.
7081634Sbrian */
7136285Sbrianchar *
7236285Sbriansave2str(str, old)
7336285Sbrian	char *str, *old;
7436285Sbrian{
7536285Sbrian	char *new;
7636285Sbrian	int newsize = strlen(str) + 1;
7736285Sbrian	int oldsize = old ? strlen(old) + 1 : 0;
7836285Sbrian
7943313Sbrian	if ((new = salloc(newsize + oldsize)) != NULL) {
8043313Sbrian		if (oldsize) {
8143313Sbrian			bcopy(old, new, oldsize);
8281634Sbrian			new[oldsize - 1] = ' ';
8381634Sbrian		}
8436285Sbrian		bcopy(str, new + oldsize, newsize);
8536285Sbrian	}
8636285Sbrian	return (new);
8736285Sbrian}
8846686Sbrian
8936285Sbrian/*
9036285Sbrian * Touch the named message by setting its MTOUCH flag.
9136285Sbrian * Touched messages have the effect of not being sent
9236285Sbrian * back to the system mailbox on exit.
9338174Sbrian */
9436285Sbrianvoid
9540561Sbriantouch(mp)
9671657Sbrian	struct message *mp;
9781697Sbrian{
9893418Sbrian
9971971Sbrian	mp->m_flag |= MTOUCH;
10075212Sbrian	if ((mp->m_flag & MREAD) == 0)
10136285Sbrian		mp->m_flag |= MREAD|MSTATUS;
10264670Sbrian}
10364670Sbrian
10436285Sbrian/*
10553684Sbrian * Test to see if the passed file name is a directory.
10653684Sbrian * Return true if it is.
10752942Sbrian */
10836285Sbrianint
10936285Sbrianisdir(name)
11055146Sbrian	char name[];
11136285Sbrian{
11236285Sbrian	struct stat sbuf;
11336285Sbrian
11436285Sbrian	if (stat(name, &sbuf) < 0)
11536285Sbrian		return (0);
11636285Sbrian	return (S_ISDIR(sbuf.st_mode));
11736285Sbrian}
11836285Sbrian
11936285Sbrian/*
12036285Sbrian * Count the number of arguments in the given string raw list.
12136285Sbrian */
12236285Sbrianint
12336285Sbrianargcount(argv)
12436285Sbrian	char **argv;
12536285Sbrian{
12636285Sbrian	char **ap;
12736285Sbrian
12836285Sbrian	for (ap = argv; *ap++ != NULL;)
12936285Sbrian		;
13036285Sbrian	return (ap - argv - 1);
13136285Sbrian}
13271971Sbrian
13393418Sbrian/*
13471971Sbrian * Return the desired header line from the passed message
13571974Sbrian * pointer (or NULL if the desired header field is not available).
13636314Sbrian */
13736285Sbrianchar *
13836285Sbrianhfield(field, mp)
13936285Sbrian	const char *field;
14036285Sbrian	struct message *mp;
14136285Sbrian{
14236285Sbrian	FILE *ibuf;
14336285Sbrian	char linebuf[LINESIZE];
14436285Sbrian	int lc;
14536314Sbrian	char *hfield;
14636285Sbrian	char *colon, *oldhfield = NULL;
14736285Sbrian
14836285Sbrian	ibuf = setinput(mp);
14981634Sbrian	if ((lc = mp->m_lines - 1) < 0)
15081634Sbrian		return (NULL);
15181634Sbrian	if (readline(ibuf, linebuf, LINESIZE) < 0)
15281634Sbrian		return (NULL);
15381634Sbrian	while (lc > 0) {
15481634Sbrian		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
15581634Sbrian			return (oldhfield);
15636285Sbrian		if ((hfield = ishfield(linebuf, colon, field)) != NULL)
15736285Sbrian			oldhfield = save2str(hfield, oldhfield);
15836285Sbrian	}
15936285Sbrian	return (oldhfield);
16036285Sbrian}
16136314Sbrian
16236285Sbrian/*
16336285Sbrian * Return the next header field found in the given message.
16436285Sbrian * Return >= 0 if something found, < 0 elsewise.
16536285Sbrian * "colon" is set to point to the colon in the header.
16636285Sbrian * Must deal with \ continuations & other such fraud.
167134789Sbrian */
16836285Sbrianint
16936285Sbriangethfield(f, linebuf, rem, colon)
17036285Sbrian	FILE *f;
17136285Sbrian	char linebuf[];
17236285Sbrian	int rem;
17359084Sbrian	char **colon;
17436285Sbrian{
17536285Sbrian	char line2[LINESIZE];
17636285Sbrian	char *cp, *cp2;
17759084Sbrian	int c;
17859084Sbrian
17959084Sbrian	for (;;) {
18059084Sbrian		if (--rem < 0)
18159084Sbrian			return (-1);
18259084Sbrian		if ((c = readline(f, linebuf, LINESIZE)) <= 0)
18359084Sbrian			return (-1);
18459084Sbrian		for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
18559084Sbrian		    cp++)
18659084Sbrian			;
18759084Sbrian		if (*cp != ':' || cp == linebuf)
18859084Sbrian			continue;
18959084Sbrian		/*
19036285Sbrian		 * I guess we got a headline.
19159084Sbrian		 * Handle wraparounding
19236285Sbrian		 */
19336285Sbrian		*colon = cp;
19436285Sbrian		cp = linebuf + c;
19598243Sbrian		for (;;) {
19638544Sbrian			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
19738544Sbrian				;
19838544Sbrian			cp++;
19938544Sbrian			if (rem <= 0)
20038544Sbrian				break;
20138544Sbrian			ungetc(c = getc(f), f);
20238544Sbrian			if (c != ' ' && c != '\t')
20338544Sbrian				break;
20438544Sbrian			if ((c = readline(f, line2, LINESIZE)) < 0)
20538544Sbrian				break;
20638544Sbrian			rem--;
20738544Sbrian			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
20838544Sbrian				;
20938544Sbrian			c -= cp2 - line2;
21038544Sbrian			if (cp + c >= linebuf + LINESIZE - 2)
21138544Sbrian				break;
21238544Sbrian			*cp++ = ' ';
21338544Sbrian			bcopy(cp2, cp, c);
21438544Sbrian			cp += c;
21538544Sbrian		}
21638544Sbrian		*cp = 0;
21738544Sbrian		return (rem);
21838544Sbrian	}
21981634Sbrian	/* NOTREACHED */
22038544Sbrian}
22138544Sbrian
22238544Sbrian/*
22338544Sbrian * Check whether the passed line is a header line of
22436285Sbrian * the desired breed.  Return the field body, or 0.
22536928Sbrian */
22636928Sbrian
22736928Sbrianchar*
22836928Sbrianishfield(linebuf, colon, field)
22936928Sbrian	char linebuf[];
23036285Sbrian	char *colon;
23196153Sbrian	const char *field;
23236928Sbrian{
23396153Sbrian	char *cp = colon;
23496153Sbrian
23596153Sbrian	*cp = 0;
23696153Sbrian	if (strcasecmp(linebuf, field) != 0) {
23796153Sbrian		*cp = ':';
23896153Sbrian		return (0);
23936928Sbrian	}
24036928Sbrian	*cp = ':';
24162977Sbrian	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
24236928Sbrian		;
24336928Sbrian	return (cp);
24438174Sbrian}
24536928Sbrian
24636928Sbrian/*
24736928Sbrian * Copy a string and lowercase the result.
24836928Sbrian * dsize: space left in buffer (including space for NULL)
24936928Sbrian */
25036928Sbrianvoid
25136928Sbrianistrncpy(dest, src, dsize)
25236928Sbrian	char *dest;
25349434Sbrian	const char *src;
25449434Sbrian	size_t dsize;
25549434Sbrian{
25636928Sbrian
25796153Sbrian	strlcpy(dest, src, dsize);
25896153Sbrian	while (*dest)
25996153Sbrian		*dest++ = tolower(*dest);
26096153Sbrian}
26196153Sbrian
26236928Sbrian/*
26396153Sbrian * The following code deals with input stacking to do source
26436928Sbrian * commands.  All but the current file pointer are saved on
26536928Sbrian * the stack.
26636928Sbrian */
26736285Sbrian
26836285Sbrianstatic	int	ssp;			/* Top of file stack */
26936285Sbrianstruct sstack {
27036285Sbrian	FILE	*s_file;		/* File we were in. */
27149434Sbrian	int	s_cond;			/* Saved state of conditionals */
27249434Sbrian	int	s_loading;		/* Loading .mailrc, etc. */
27349434Sbrian};
27449978Sbrian#define	SSTACK_SIZE	64		/* XXX was NOFILE. */
27549434Sbrianstatic struct sstack sstack[SSTACK_SIZE];
27636285Sbrian
27749434Sbrian/*
27836285Sbrian * Pushdown current input file and switch to a new one.
27936285Sbrian * Set the global flag "sourcing" so that others will realize
28036285Sbrian * that they are no longer reading from a tty (in all probability).
28136285Sbrian */
28236928Sbrianint
28336928Sbriansource(arglist)
28436928Sbrian	char **arglist;
28549434Sbrian{
28681634Sbrian	FILE *fi;
28781634Sbrian	char *cp;
28881634Sbrian
28981634Sbrian	if ((cp = expand(*arglist)) == NULL)
29096153Sbrian		return (1);
29196153Sbrian	if ((fi = Fopen(cp, "r")) == NULL) {
29296153Sbrian		warn("%s", cp);
29398243Sbrian		return (1);
29481634Sbrian	}
29581634Sbrian	if (ssp >= SSTACK_SIZE - 1) {
29681634Sbrian		printf("Too much \"sourcing\" going on.\n");
29736285Sbrian		(void)Fclose(fi);
29879165Sbrian		return (1);
29979165Sbrian	}
30036285Sbrian	sstack[ssp].s_file = input;
30136285Sbrian	sstack[ssp].s_cond = cond;
30236285Sbrian	sstack[ssp].s_loading = loading;
30336285Sbrian	ssp++;
30436285Sbrian	loading = 0;
30536285Sbrian	cond = CANY;
30636285Sbrian	input = fi;
30736285Sbrian	sourcing++;
30849978Sbrian	return (0);
30949434Sbrian}
31036928Sbrian
31136312Sbrian/*
31259070Sbrian * Pop the current input back to the previous level.
31336312Sbrian * Update the "sourcing" flag as appropriate.
31436285Sbrian */
31536285Sbrianint
31636285Sbrianunstack()
31736285Sbrian{
31881634Sbrian	if (ssp <= 0) {
31981634Sbrian		printf("\"Source\" stack over-pop.\n");
32096153Sbrian		sourcing = 0;
32196153Sbrian		return (1);
32296153Sbrian	}
32396153Sbrian	(void)Fclose(input);
32481634Sbrian	if (cond != CANY)
32581634Sbrian		printf("Unmatched \"if\"\n");
32681634Sbrian	ssp--;
32781634Sbrian	cond = sstack[ssp].s_cond;
32849434Sbrian	loading = sstack[ssp].s_loading;
32959070Sbrian	input = sstack[ssp].s_file;
33059070Sbrian	if (ssp == 0)
33159070Sbrian		sourcing = loading;
33259070Sbrian	return (0);
33336928Sbrian}
33436285Sbrian
33559070Sbrian/*
33659070Sbrian * Touch the indicated file.
33759070Sbrian * This is nifty for the shell.
33859070Sbrian */
33959070Sbrianvoid
34059070Sbrianalter(name)
34159070Sbrian	char *name;
34259070Sbrian{
34336312Sbrian	struct stat sb;
34459070Sbrian	struct timeval tv[2];
34549434Sbrian
34636312Sbrian	if (stat(name, &sb))
34736928Sbrian		return;
34836928Sbrian	(void)gettimeofday(&tv[0], (struct timezone *)NULL);
34936928Sbrian	tv[0].tv_sec++;
35037019Sbrian	TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
35136928Sbrian	(void)utimes(name, tv);
35236928Sbrian}
35359070Sbrian
35493422Sbrian/*
35559070Sbrian * Get sender's name from this message.  If the message has
35681634Sbrian * a bunch of arpanet stuff in it, we may have to skin the name
35793422Sbrian * before returning it.
35893422Sbrian */
35936285Sbrianchar *
36036285Sbriannameof(mp, reptype)
36136285Sbrian	struct message *mp;
36236285Sbrian	int reptype;
36336285Sbrian{
36436285Sbrian	char *cp, *cp2;
36536285Sbrian
36636285Sbrian	cp = skin(name1(mp, reptype));
36736285Sbrian	if (reptype != 0 || charcount(cp, '!') < 2)
36893422Sbrian		return (cp);
36936285Sbrian	cp2 = strrchr(cp, '!');
37036285Sbrian	cp2--;
37136285Sbrian	while (cp2 > cp && *cp2 != '!')
37236285Sbrian		cp2--;
37336285Sbrian	if (*cp2 == '!')
37481634Sbrian		return (cp2 + 1);
37536285Sbrian	return (cp);
37636285Sbrian}
37736285Sbrian
37859070Sbrian/*
37959070Sbrian * Start of a "comment".
38037060Sbrian * Ignore it.
38193422Sbrian */
38236285Sbrianchar *
38336285Sbrianskip_comment(cp)
38436285Sbrian	char *cp;
38536285Sbrian{
38637007Sbrian	int nesting = 1;
38736285Sbrian
38836285Sbrian	for (; nesting > 0 && *cp; cp++) {
38936285Sbrian		switch (*cp) {
39036285Sbrian		case '\\':
39136285Sbrian			if (cp[1])
39236285Sbrian				cp++;
39336285Sbrian			break;
39436285Sbrian		case '(':
39536285Sbrian			nesting++;
39636285Sbrian			break;
39736285Sbrian		case ')':
39836285Sbrian			nesting--;
39936285Sbrian			break;
40036285Sbrian		}
40136285Sbrian	}
40236285Sbrian	return (cp);
40336285Sbrian}
40436285Sbrian
40537007Sbrian/*
40637007Sbrian * Skin an arpa net address according to the RFC 822 interpretation
40737007Sbrian * of "host-phrase."
40871970Sbrian */
40937007Sbrianchar *
41037007Sbrianskin(name)
41137007Sbrian	char *name;
41237007Sbrian{
41336285Sbrian	char *nbuf, *bufend, *cp, *cp2;
41436285Sbrian	int c, gotlt, lastsp;
41536285Sbrian
41636285Sbrian	if (name == NULL)
41736285Sbrian		return (NULL);
41836285Sbrian	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
41936285Sbrian	    && strchr(name, ' ') == NULL)
42036285Sbrian		return (name);
42136285Sbrian
42236285Sbrian	/* We assume that length(input) <= length(output) */
42396153Sbrian	if ((nbuf = malloc(strlen(name) + 1)) == NULL)
42496153Sbrian		err(1, "Out of memory");
42596153Sbrian	gotlt = 0;
42696153Sbrian	lastsp = 0;
42736285Sbrian	bufend = nbuf;
42881634Sbrian	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
42981634Sbrian		switch (c) {
43036285Sbrian		case '(':
43181634Sbrian			cp = skip_comment(cp);
43293422Sbrian			lastsp = 0;
43336285Sbrian			break;
43437007Sbrian
43536285Sbrian		case '"':
43636285Sbrian			/*
43736285Sbrian			 * Start of a "quoted-string".
43837007Sbrian			 * Copy it in its entirety.
43936285Sbrian			 */
44036285Sbrian			while ((c = *cp) != '\0') {
44136285Sbrian				cp++;
44237018Sbrian				if (c == '"')
44336285Sbrian					break;
44436285Sbrian				if (c != '\\')
44536285Sbrian					*cp2++ = c;
44636285Sbrian				else if ((c = *cp) != '\0') {
44737018Sbrian					*cp2++ = c;
44836285Sbrian					cp++;
44936285Sbrian				}
45036285Sbrian			}
45158028Sbrian			lastsp = 0;
45236285Sbrian			break;
45336285Sbrian
45436285Sbrian		case ' ':
45554912Sbrian			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
45661534Sbrian				cp += 3, *cp2++ = '@';
45754912Sbrian			else
45836285Sbrian			if (cp[0] == '@' && cp[1] == ' ')
45936285Sbrian				cp += 2, *cp2++ = '@';
46036285Sbrian			else
46136285Sbrian				lastsp = 1;
46236285Sbrian			break;
46336285Sbrian
46436285Sbrian		case '<':
46536285Sbrian			cp2 = bufend;
46681634Sbrian			gotlt++;
46781634Sbrian			lastsp = 0;
46836285Sbrian			break;
46936928Sbrian
47036928Sbrian		case '>':
47136285Sbrian			if (gotlt) {
47261534Sbrian				gotlt = 0;
47361534Sbrian				while ((c = *cp) != '\0' && c != ',') {
47436285Sbrian					cp++;
47538544Sbrian					if (c == '(')
47638544Sbrian						cp = skip_comment(cp);
47736285Sbrian					else if (c == '"')
47836285Sbrian						while ((c = *cp) != '\0') {
47936285Sbrian							cp++;
48036452Sbrian							if (c == '"')
48136285Sbrian								break;
48238544Sbrian							if (c == '\\' && *cp != '\0')
48338544Sbrian								cp++;
48438544Sbrian						}
48538544Sbrian				}
48638544Sbrian				lastsp = 0;
48738544Sbrian				break;
48836285Sbrian			}
48936285Sbrian			/* Fall into . . . */
49036285Sbrian
49136285Sbrian		default:
49243693Sbrian			if (lastsp) {
49343693Sbrian				lastsp = 0;
49443693Sbrian				*cp2++ = ' ';
49543693Sbrian			}
49636714Sbrian			*cp2++ = c;
49736714Sbrian			if (c == ',' && *cp == ' ' && !gotlt) {
49836714Sbrian				*cp2++ = ' ';
49936714Sbrian				while (*++cp == ' ')
50036285Sbrian					;
50136285Sbrian				lastsp = 0;
50236285Sbrian				bufend = cp2;
50358038Sbrian			}
50436285Sbrian		}
50536314Sbrian	}
50636285Sbrian	*cp2 = '\0';
50736285Sbrian
50836285Sbrian	if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
50936285Sbrian		nbuf = cp;
51036285Sbrian	return (nbuf);
51158028Sbrian}
51236285Sbrian
51336285Sbrian/*
51436285Sbrian * Fetch the sender's name from the passed message.
51536285Sbrian * Reptype can be
51636285Sbrian *	0 -- get sender's name for display purposes
51736285Sbrian *	1 -- get sender's name for reply
51836285Sbrian *	2 -- get sender's name for Reply
51936285Sbrian */
52043693Sbrianchar *
52143693Sbrianname1(mp, reptype)
52243693Sbrian	struct message *mp;
52343693Sbrian	int reptype;
52443693Sbrian{
52536285Sbrian	char namebuf[LINESIZE];
52636285Sbrian	char linebuf[LINESIZE];
52736285Sbrian	char *cp, *cp2;
52836285Sbrian	FILE *ibuf;
52936285Sbrian	int first = 1;
53036285Sbrian
53136285Sbrian	if ((cp = hfield("from", mp)) != NULL)
532134789Sbrian		return (cp);
53336285Sbrian	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
53436285Sbrian		return (cp);
53536285Sbrian	ibuf = setinput(mp);
53662977Sbrian	namebuf[0] = '\0';
53781634Sbrian	if (readline(ibuf, linebuf, LINESIZE) < 0)
53836285Sbrian		return (savestr(namebuf));
53936285Sbriannewname:
54036285Sbrian	for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++)
54136285Sbrian		;
54236285Sbrian	for (; *cp == ' ' || *cp == '\t'; cp++)
54336285Sbrian		;
54436285Sbrian	for (cp2 = &namebuf[strlen(namebuf)];
54536285Sbrian	    *cp != '\0' && *cp != ' ' && *cp != '\t' &&
54643693Sbrian	    cp2 < namebuf + LINESIZE - 1;)
54743693Sbrian		*cp2++ = *cp++;
54843693Sbrian	*cp2 = '\0';
54943693Sbrian	if (readline(ibuf, linebuf, LINESIZE) < 0)
55043693Sbrian		return (savestr(namebuf));
55136285Sbrian	if ((cp = strchr(linebuf, 'F')) == NULL)
55236285Sbrian		return (savestr(namebuf));
55336285Sbrian	if (strncmp(cp, "From", 4) != 0)
55496043Sbrian		return (savestr(namebuf));
55556413Sbrian	while ((cp = strchr(cp, 'r')) != NULL) {
55636285Sbrian		if (strncmp(cp, "remote", 6) == 0) {
55756413Sbrian			if ((cp = strchr(cp, 'f')) == NULL)
55896043Sbrian				break;
55956413Sbrian			if (strncmp(cp, "from", 4) != 0)
56056413Sbrian				break;
56156413Sbrian			if ((cp = strchr(cp, ' ')) == NULL)
56256413Sbrian				break;
56356413Sbrian			cp++;
56456413Sbrian			if (first) {
56536285Sbrian				cp2 = namebuf;
56656413Sbrian				first = 0;
56756413Sbrian			} else
56836285Sbrian				cp2 = strrchr(namebuf, '!') + 1;
56956413Sbrian			strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
57036285Sbrian			strcat(namebuf, "!");
57136285Sbrian			goto newname;
57256413Sbrian		}
57356413Sbrian		cp++;
57456413Sbrian	}
57556413Sbrian	return (savestr(namebuf));
57656413Sbrian}
57756413Sbrian
57856413Sbrian/*
57956413Sbrian * Count the occurances of c in str
58081634Sbrian */
58181634Sbrianint
58281897Sbriancharcount(str, c)
58381634Sbrian	char *str;
58481634Sbrian	int c;
58581634Sbrian{
58656413Sbrian	char *cp;
58756413Sbrian	int i;
58881634Sbrian
58981634Sbrian	for (i = 0, cp = str; *cp != '\0'; cp++)
59036285Sbrian		if (*cp == c)
59181634Sbrian			i++;
59236285Sbrian	return (i);
59336285Sbrian}
59436285Sbrian
59581634Sbrian/*
59681634Sbrian * See if the given header field is supposed to be ignored.
59736285Sbrian */
59856413Sbrianint
59956413Sbrianisign(field, ignore)
60036285Sbrian	const char *field;
60136285Sbrian	struct ignoretab ignore[2];
60236285Sbrian{
60336285Sbrian	char realfld[LINESIZE];
60436285Sbrian
60536285Sbrian	if (ignore == ignoreall)
60636285Sbrian		return (1);
60736285Sbrian	/*
60881634Sbrian	 * Lower-case the string, so that "Status" and "status"
60981634Sbrian	 * will hash to the same place.
61036285Sbrian	 */
61136285Sbrian	istrncpy(realfld, field, sizeof(realfld));
61236285Sbrian	if (ignore[1].i_count > 0)
61336285Sbrian		return (!member(realfld, ignore + 1));
61436285Sbrian	else
61536285Sbrian		return (member(realfld, ignore));
61636285Sbrian}
61781634Sbrian
61881634Sbrianint
61962938Sbrianmember(realfield, table)
62037955Sbrian	char *realfield;
62136285Sbrian	struct ignoretab *table;
62236285Sbrian{
62336285Sbrian	struct ignore *igp;
62436285Sbrian
62598243Sbrian	for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link)
62636285Sbrian		if (*igp->i_field == *realfield &&
62736285Sbrian		    equal(igp->i_field, realfield))
62836285Sbrian			return (1);
62936285Sbrian	return (0);
63036285Sbrian}
63136285Sbrian