11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3174769Smikeh#if 0
321590Srgrimesstatic char sccsid[] = "@(#)aux.c	8.1 (Berkeley) 6/6/93";
3374769Smikeh#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD$");
371590Srgrimes
3891227Sbde#include <sys/time.h>
3991227Sbde
401590Srgrimes#include "rcv.h"
411590Srgrimes#include "extern.h"
421590Srgrimes
431590Srgrimes/*
441590Srgrimes * Mail -- a mail program
451590Srgrimes *
461590Srgrimes * Auxiliary functions.
471590Srgrimes */
481590Srgrimes
4992921Simpstatic char *save2str(char *, char *);
5077274Smikeh
511590Srgrimes/*
521590Srgrimes * Return a pointer to a dynamic copy of the argument.
531590Srgrimes */
541590Srgrimeschar *
55216564Scharniersavestr(char *str)
561590Srgrimes{
571590Srgrimes	char *new;
581590Srgrimes	int size = strlen(str) + 1;
591590Srgrimes
6077274Smikeh	if ((new = salloc(size)) != NULL)
611590Srgrimes		bcopy(str, new, size);
6277274Smikeh	return (new);
631590Srgrimes}
641590Srgrimes
651590Srgrimes/*
661590Srgrimes * Make a copy of new argument incorporating old one.
671590Srgrimes */
68173438Sddsstatic char *
69216564Scharniersave2str(char *str, char *old)
701590Srgrimes{
711590Srgrimes	char *new;
721590Srgrimes	int newsize = strlen(str) + 1;
731590Srgrimes	int oldsize = old ? strlen(old) + 1 : 0;
741590Srgrimes
7577274Smikeh	if ((new = salloc(newsize + oldsize)) != NULL) {
761590Srgrimes		if (oldsize) {
771590Srgrimes			bcopy(old, new, oldsize);
781590Srgrimes			new[oldsize - 1] = ' ';
791590Srgrimes		}
801590Srgrimes		bcopy(str, new + oldsize, newsize);
811590Srgrimes	}
8277274Smikeh	return (new);
831590Srgrimes}
841590Srgrimes
851590Srgrimes/*
861590Srgrimes * Touch the named message by setting its MTOUCH flag.
871590Srgrimes * Touched messages have the effect of not being sent
881590Srgrimes * back to the system mailbox on exit.
891590Srgrimes */
901590Srgrimesvoid
91216564Scharniertouch(struct message *mp)
921590Srgrimes{
931590Srgrimes
941590Srgrimes	mp->m_flag |= MTOUCH;
951590Srgrimes	if ((mp->m_flag & MREAD) == 0)
961590Srgrimes		mp->m_flag |= MREAD|MSTATUS;
971590Srgrimes}
981590Srgrimes
991590Srgrimes/*
1001590Srgrimes * Test to see if the passed file name is a directory.
1011590Srgrimes * Return true if it is.
1021590Srgrimes */
1031590Srgrimesint
104216564Scharnierisdir(char name[])
1051590Srgrimes{
1061590Srgrimes	struct stat sbuf;
1071590Srgrimes
1081590Srgrimes	if (stat(name, &sbuf) < 0)
10977274Smikeh		return (0);
11077274Smikeh	return (S_ISDIR(sbuf.st_mode));
1111590Srgrimes}
1121590Srgrimes
1131590Srgrimes/*
1141590Srgrimes * Count the number of arguments in the given string raw list.
1151590Srgrimes */
1161590Srgrimesint
117216564Scharnierargcount(char **argv)
1181590Srgrimes{
11977274Smikeh	char **ap;
1201590Srgrimes
12177274Smikeh	for (ap = argv; *ap++ != NULL;)
1228874Srgrimes		;
12377274Smikeh	return (ap - argv - 1);
1241590Srgrimes}
1251590Srgrimes
1261590Srgrimes/*
1271590Srgrimes * Return the desired header line from the passed message
12877274Smikeh * pointer (or NULL if the desired header field is not available).
1291590Srgrimes */
1301590Srgrimeschar *
131216564Scharnierhfield(const char *field, struct message *mp)
1321590Srgrimes{
13377274Smikeh	FILE *ibuf;
1341590Srgrimes	char linebuf[LINESIZE];
13577274Smikeh	int lc;
13677274Smikeh	char *hfield;
13777274Smikeh	char *colon, *oldhfield = NULL;
1381590Srgrimes
1391590Srgrimes	ibuf = setinput(mp);
1401590Srgrimes	if ((lc = mp->m_lines - 1) < 0)
14177274Smikeh		return (NULL);
1421590Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
14377274Smikeh		return (NULL);
1441590Srgrimes	while (lc > 0) {
1451590Srgrimes		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
14677274Smikeh			return (oldhfield);
14727643Scharnier		if ((hfield = ishfield(linebuf, colon, field)) != NULL)
1481590Srgrimes			oldhfield = save2str(hfield, oldhfield);
1491590Srgrimes	}
15077274Smikeh	return (oldhfield);
1511590Srgrimes}
1521590Srgrimes
1531590Srgrimes/*
1541590Srgrimes * Return the next header field found in the given message.
1551590Srgrimes * Return >= 0 if something found, < 0 elsewise.
1561590Srgrimes * "colon" is set to point to the colon in the header.
1571590Srgrimes * Must deal with \ continuations & other such fraud.
1581590Srgrimes */
1591590Srgrimesint
160216564Scharniergethfield(FILE *f, char linebuf[], int rem, char **colon)
1611590Srgrimes{
1621590Srgrimes	char line2[LINESIZE];
16377274Smikeh	char *cp, *cp2;
16477274Smikeh	int c;
1651590Srgrimes
1661590Srgrimes	for (;;) {
1671590Srgrimes		if (--rem < 0)
16877274Smikeh			return (-1);
1691590Srgrimes		if ((c = readline(f, linebuf, LINESIZE)) <= 0)
17077274Smikeh			return (-1);
17188227Sache		for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
17277274Smikeh		    cp++)
1731590Srgrimes			;
1741590Srgrimes		if (*cp != ':' || cp == linebuf)
1751590Srgrimes			continue;
1761590Srgrimes		/*
1771590Srgrimes		 * I guess we got a headline.
1781590Srgrimes		 * Handle wraparounding
1791590Srgrimes		 */
1801590Srgrimes		*colon = cp;
1811590Srgrimes		cp = linebuf + c;
1821590Srgrimes		for (;;) {
1831590Srgrimes			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
1841590Srgrimes				;
1851590Srgrimes			cp++;
1861590Srgrimes			if (rem <= 0)
1871590Srgrimes				break;
1881590Srgrimes			ungetc(c = getc(f), f);
1891590Srgrimes			if (c != ' ' && c != '\t')
1901590Srgrimes				break;
1911590Srgrimes			if ((c = readline(f, line2, LINESIZE)) < 0)
1921590Srgrimes				break;
1931590Srgrimes			rem--;
1941590Srgrimes			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
1951590Srgrimes				;
1961590Srgrimes			c -= cp2 - line2;
1971590Srgrimes			if (cp + c >= linebuf + LINESIZE - 2)
1981590Srgrimes				break;
1991590Srgrimes			*cp++ = ' ';
2001590Srgrimes			bcopy(cp2, cp, c);
2011590Srgrimes			cp += c;
2021590Srgrimes		}
2031590Srgrimes		*cp = 0;
20477274Smikeh		return (rem);
2051590Srgrimes	}
2061590Srgrimes	/* NOTREACHED */
2071590Srgrimes}
2081590Srgrimes
2091590Srgrimes/*
2101590Srgrimes * Check whether the passed line is a header line of
2111590Srgrimes * the desired breed.  Return the field body, or 0.
2121590Srgrimes */
2131590Srgrimes
2141590Srgrimeschar*
215216564Scharnierishfield(char linebuf[], char *colon, const char *field)
2161590Srgrimes{
21777274Smikeh	char *cp = colon;
2181590Srgrimes
2191590Srgrimes	*cp = 0;
2201590Srgrimes	if (strcasecmp(linebuf, field) != 0) {
2211590Srgrimes		*cp = ':';
22277274Smikeh		return (0);
2231590Srgrimes	}
2241590Srgrimes	*cp = ':';
2251590Srgrimes	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
2261590Srgrimes		;
22777274Smikeh	return (cp);
2281590Srgrimes}
2291590Srgrimes
2301590Srgrimes/*
23174769Smikeh * Copy a string and lowercase the result.
23274769Smikeh * dsize: space left in buffer (including space for NULL)
2331590Srgrimes */
2341590Srgrimesvoid
235216564Scharnieristrncpy(char *dest, const char *src, size_t dsize)
2361590Srgrimes{
2371590Srgrimes
23874769Smikeh	strlcpy(dest, src, dsize);
239246860Sdim	for (; *dest; dest++)
240246860Sdim		*dest = tolower((unsigned char)*dest);
2411590Srgrimes}
2421590Srgrimes
2431590Srgrimes/*
2441590Srgrimes * The following code deals with input stacking to do source
2451590Srgrimes * commands.  All but the current file pointer are saved on
2461590Srgrimes * the stack.
2471590Srgrimes */
2481590Srgrimes
2491590Srgrimesstatic	int	ssp;			/* Top of file stack */
2501590Srgrimesstruct sstack {
2511590Srgrimes	FILE	*s_file;		/* File we were in. */
2521590Srgrimes	int	s_cond;			/* Saved state of conditionals */
2531590Srgrimes	int	s_loading;		/* Loading .mailrc, etc. */
25418532Sbde};
25518532Sbde#define	SSTACK_SIZE	64		/* XXX was NOFILE. */
25618532Sbdestatic struct sstack sstack[SSTACK_SIZE];
2571590Srgrimes
2581590Srgrimes/*
2591590Srgrimes * Pushdown current input file and switch to a new one.
2601590Srgrimes * Set the global flag "sourcing" so that others will realize
2611590Srgrimes * that they are no longer reading from a tty (in all probability).
2621590Srgrimes */
2631590Srgrimesint
264216564Scharniersource(char **arglist)
2651590Srgrimes{
2661590Srgrimes	FILE *fi;
2671590Srgrimes	char *cp;
2681590Srgrimes
26977274Smikeh	if ((cp = expand(*arglist)) == NULL)
27077274Smikeh		return (1);
2711590Srgrimes	if ((fi = Fopen(cp, "r")) == NULL) {
27274769Smikeh		warn("%s", cp);
27377274Smikeh		return (1);
2741590Srgrimes	}
27518532Sbde	if (ssp >= SSTACK_SIZE - 1) {
2761590Srgrimes		printf("Too much \"sourcing\" going on.\n");
27777274Smikeh		(void)Fclose(fi);
27877274Smikeh		return (1);
2791590Srgrimes	}
2801590Srgrimes	sstack[ssp].s_file = input;
2811590Srgrimes	sstack[ssp].s_cond = cond;
2821590Srgrimes	sstack[ssp].s_loading = loading;
2831590Srgrimes	ssp++;
2841590Srgrimes	loading = 0;
2851590Srgrimes	cond = CANY;
2861590Srgrimes	input = fi;
2871590Srgrimes	sourcing++;
28877274Smikeh	return (0);
2891590Srgrimes}
2901590Srgrimes
2911590Srgrimes/*
2921590Srgrimes * Pop the current input back to the previous level.
2931590Srgrimes * Update the "sourcing" flag as appropriate.
2941590Srgrimes */
2951590Srgrimesint
296216564Scharnierunstack(void)
2971590Srgrimes{
2981590Srgrimes	if (ssp <= 0) {
2991590Srgrimes		printf("\"Source\" stack over-pop.\n");
3001590Srgrimes		sourcing = 0;
30177274Smikeh		return (1);
3021590Srgrimes	}
30377274Smikeh	(void)Fclose(input);
3041590Srgrimes	if (cond != CANY)
3051590Srgrimes		printf("Unmatched \"if\"\n");
3061590Srgrimes	ssp--;
3071590Srgrimes	cond = sstack[ssp].s_cond;
3081590Srgrimes	loading = sstack[ssp].s_loading;
3091590Srgrimes	input = sstack[ssp].s_file;
3101590Srgrimes	if (ssp == 0)
3111590Srgrimes		sourcing = loading;
31277274Smikeh	return (0);
3131590Srgrimes}
3141590Srgrimes
3151590Srgrimes/*
3161590Srgrimes * Touch the indicated file.
3171590Srgrimes * This is nifty for the shell.
3181590Srgrimes */
3191590Srgrimesvoid
320216564Scharnieralter(char *name)
3211590Srgrimes{
3221590Srgrimes	struct stat sb;
3231590Srgrimes	struct timeval tv[2];
3241590Srgrimes
3251590Srgrimes	if (stat(name, &sb))
3261590Srgrimes		return;
327239991Sed	(void)gettimeofday(&tv[0], NULL);
32888150Smikeh	tv[0].tv_sec++;
329205793Sed	TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtim);
3301590Srgrimes	(void)utimes(name, tv);
3311590Srgrimes}
3321590Srgrimes
3331590Srgrimes/*
3341590Srgrimes * Get sender's name from this message.  If the message has
3351590Srgrimes * a bunch of arpanet stuff in it, we may have to skin the name
3361590Srgrimes * before returning it.
3371590Srgrimes */
3381590Srgrimeschar *
339216564Scharniernameof(struct message *mp, int reptype)
3401590Srgrimes{
34177274Smikeh	char *cp, *cp2;
3421590Srgrimes
3431590Srgrimes	cp = skin(name1(mp, reptype));
3441590Srgrimes	if (reptype != 0 || charcount(cp, '!') < 2)
34577274Smikeh		return (cp);
34674769Smikeh	cp2 = strrchr(cp, '!');
3471590Srgrimes	cp2--;
3481590Srgrimes	while (cp2 > cp && *cp2 != '!')
3491590Srgrimes		cp2--;
3501590Srgrimes	if (*cp2 == '!')
35177274Smikeh		return (cp2 + 1);
35277274Smikeh	return (cp);
3531590Srgrimes}
3541590Srgrimes
3551590Srgrimes/*
3561590Srgrimes * Start of a "comment".
3571590Srgrimes * Ignore it.
3581590Srgrimes */
3591590Srgrimeschar *
360216564Scharnierskip_comment(char *cp)
3611590Srgrimes{
36277274Smikeh	int nesting = 1;
3631590Srgrimes
3641590Srgrimes	for (; nesting > 0 && *cp; cp++) {
3651590Srgrimes		switch (*cp) {
3661590Srgrimes		case '\\':
3671590Srgrimes			if (cp[1])
3681590Srgrimes				cp++;
3691590Srgrimes			break;
3701590Srgrimes		case '(':
3711590Srgrimes			nesting++;
3721590Srgrimes			break;
3731590Srgrimes		case ')':
3741590Srgrimes			nesting--;
3751590Srgrimes			break;
3761590Srgrimes		}
3771590Srgrimes	}
37877274Smikeh	return (cp);
3791590Srgrimes}
3801590Srgrimes
3811590Srgrimes/*
3821590Srgrimes * Skin an arpa net address according to the RFC 822 interpretation
3831590Srgrimes * of "host-phrase."
3841590Srgrimes */
3851590Srgrimeschar *
386216564Scharnierskin(char *name)
3871590Srgrimes{
38877274Smikeh	char *nbuf, *bufend, *cp, *cp2;
38977274Smikeh	int c, gotlt, lastsp;
3901590Srgrimes
39177274Smikeh	if (name == NULL)
39277274Smikeh		return (NULL);
39377274Smikeh	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
39477274Smikeh	    && strchr(name, ' ') == NULL)
39577274Smikeh		return (name);
39674769Smikeh
39774769Smikeh	/* We assume that length(input) <= length(output) */
39877274Smikeh	if ((nbuf = malloc(strlen(name) + 1)) == NULL)
39974769Smikeh		err(1, "Out of memory");
4001590Srgrimes	gotlt = 0;
4011590Srgrimes	lastsp = 0;
4021590Srgrimes	bufend = nbuf;
40377274Smikeh	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
4041590Srgrimes		switch (c) {
4051590Srgrimes		case '(':
4061590Srgrimes			cp = skip_comment(cp);
4071590Srgrimes			lastsp = 0;
4081590Srgrimes			break;
4091590Srgrimes
4101590Srgrimes		case '"':
4111590Srgrimes			/*
4121590Srgrimes			 * Start of a "quoted-string".
4131590Srgrimes			 * Copy it in its entirety.
4141590Srgrimes			 */
41577274Smikeh			while ((c = *cp) != '\0') {
4161590Srgrimes				cp++;
4171590Srgrimes				if (c == '"')
4181590Srgrimes					break;
4191590Srgrimes				if (c != '\\')
4201590Srgrimes					*cp2++ = c;
42177274Smikeh				else if ((c = *cp) != '\0') {
4221590Srgrimes					*cp2++ = c;
4231590Srgrimes					cp++;
4241590Srgrimes				}
4251590Srgrimes			}
4261590Srgrimes			lastsp = 0;
4271590Srgrimes			break;
4281590Srgrimes
4291590Srgrimes		case ' ':
4301590Srgrimes			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
4311590Srgrimes				cp += 3, *cp2++ = '@';
4321590Srgrimes			else
4331590Srgrimes			if (cp[0] == '@' && cp[1] == ' ')
4341590Srgrimes				cp += 2, *cp2++ = '@';
4351590Srgrimes			else
4361590Srgrimes				lastsp = 1;
4371590Srgrimes			break;
4381590Srgrimes
4391590Srgrimes		case '<':
4401590Srgrimes			cp2 = bufend;
4411590Srgrimes			gotlt++;
4421590Srgrimes			lastsp = 0;
4431590Srgrimes			break;
4441590Srgrimes
4451590Srgrimes		case '>':
4461590Srgrimes			if (gotlt) {
4471590Srgrimes				gotlt = 0;
44877274Smikeh				while ((c = *cp) != '\0' && c != ',') {
4491590Srgrimes					cp++;
4501590Srgrimes					if (c == '(')
4511590Srgrimes						cp = skip_comment(cp);
4521590Srgrimes					else if (c == '"')
45377274Smikeh						while ((c = *cp) != '\0') {
4541590Srgrimes							cp++;
4551590Srgrimes							if (c == '"')
4561590Srgrimes								break;
45777274Smikeh							if (c == '\\' && *cp != '\0')
4581590Srgrimes								cp++;
4591590Srgrimes						}
4601590Srgrimes				}
4611590Srgrimes				lastsp = 0;
4621590Srgrimes				break;
4631590Srgrimes			}
464102412Scharnier			/* FALLTHROUGH */
4651590Srgrimes
4661590Srgrimes		default:
4671590Srgrimes			if (lastsp) {
4681590Srgrimes				lastsp = 0;
4691590Srgrimes				*cp2++ = ' ';
4701590Srgrimes			}
4711590Srgrimes			*cp2++ = c;
472208592Suqs			if (c == ',' && !gotlt &&
473208592Suqs			    (*cp == ' ' || *cp == '"' || *cp == '<')) {
4741590Srgrimes				*cp2++ = ' ';
475208592Suqs				while (*cp == ' ')
476208592Suqs					cp++;
4771590Srgrimes				lastsp = 0;
4781590Srgrimes				bufend = cp2;
4791590Srgrimes			}
4801590Srgrimes		}
4811590Srgrimes	}
48277274Smikeh	*cp2 = '\0';
4831590Srgrimes
48483848Smikeh	if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
48583848Smikeh		nbuf = cp;
48677274Smikeh	return (nbuf);
4871590Srgrimes}
4881590Srgrimes
4891590Srgrimes/*
4901590Srgrimes * Fetch the sender's name from the passed message.
4911590Srgrimes * Reptype can be
4921590Srgrimes *	0 -- get sender's name for display purposes
4931590Srgrimes *	1 -- get sender's name for reply
4941590Srgrimes *	2 -- get sender's name for Reply
4951590Srgrimes */
4961590Srgrimeschar *
497216564Scharniername1(struct message *mp, int reptype)
4981590Srgrimes{
4991590Srgrimes	char namebuf[LINESIZE];
5001590Srgrimes	char linebuf[LINESIZE];
50177274Smikeh	char *cp, *cp2;
50277274Smikeh	FILE *ibuf;
5031590Srgrimes	int first = 1;
5041590Srgrimes
50577274Smikeh	if ((cp = hfield("from", mp)) != NULL)
50677274Smikeh		return (cp);
50777274Smikeh	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
50877274Smikeh		return (cp);
5091590Srgrimes	ibuf = setinput(mp);
51027643Scharnier	namebuf[0] = '\0';
5111590Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
51277274Smikeh		return (savestr(namebuf));
5131590Srgrimesnewname:
51477274Smikeh	for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++)
5151590Srgrimes		;
5161590Srgrimes	for (; *cp == ' ' || *cp == '\t'; cp++)
5171590Srgrimes		;
5181590Srgrimes	for (cp2 = &namebuf[strlen(namebuf)];
51977274Smikeh	    *cp != '\0' && *cp != ' ' && *cp != '\t' &&
52077274Smikeh	    cp2 < namebuf + LINESIZE - 1;)
5211590Srgrimes		*cp2++ = *cp++;
5221590Srgrimes	*cp2 = '\0';
5231590Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
52477274Smikeh		return (savestr(namebuf));
52574769Smikeh	if ((cp = strchr(linebuf, 'F')) == NULL)
52677274Smikeh		return (savestr(namebuf));
5271590Srgrimes	if (strncmp(cp, "From", 4) != 0)
52877274Smikeh		return (savestr(namebuf));
52974769Smikeh	while ((cp = strchr(cp, 'r')) != NULL) {
5301590Srgrimes		if (strncmp(cp, "remote", 6) == 0) {
53174769Smikeh			if ((cp = strchr(cp, 'f')) == NULL)
5321590Srgrimes				break;
5331590Srgrimes			if (strncmp(cp, "from", 4) != 0)
5341590Srgrimes				break;
53574769Smikeh			if ((cp = strchr(cp, ' ')) == NULL)
5361590Srgrimes				break;
5371590Srgrimes			cp++;
5381590Srgrimes			if (first) {
53974769Smikeh				cp2 = namebuf;
5401590Srgrimes				first = 0;
5411590Srgrimes			} else
54274769Smikeh				cp2 = strrchr(namebuf, '!') + 1;
54374769Smikeh			strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
5441590Srgrimes			strcat(namebuf, "!");
5451590Srgrimes			goto newname;
5461590Srgrimes		}
5471590Srgrimes		cp++;
5481590Srgrimes	}
54977274Smikeh	return (savestr(namebuf));
5501590Srgrimes}
5511590Srgrimes
5521590Srgrimes/*
553229655Suqs * Count the occurrences of c in str
5541590Srgrimes */
5551590Srgrimesint
556216564Scharniercharcount(char *str, int c)
5571590Srgrimes{
55877274Smikeh	char *cp;
55977274Smikeh	int i;
5601590Srgrimes
56177274Smikeh	for (i = 0, cp = str; *cp != '\0'; cp++)
5621590Srgrimes		if (*cp == c)
5631590Srgrimes			i++;
56477274Smikeh	return (i);
5651590Srgrimes}
5661590Srgrimes
5671590Srgrimes/*
5681590Srgrimes * See if the given header field is supposed to be ignored.
5691590Srgrimes */
5701590Srgrimesint
571216564Scharnierisign(const char *field, struct ignoretab ignore[2])
5721590Srgrimes{
57374769Smikeh	char realfld[LINESIZE];
5741590Srgrimes
5751590Srgrimes	if (ignore == ignoreall)
57677274Smikeh		return (1);
5771590Srgrimes	/*
5781590Srgrimes	 * Lower-case the string, so that "Status" and "status"
5791590Srgrimes	 * will hash to the same place.
5801590Srgrimes	 */
58174769Smikeh	istrncpy(realfld, field, sizeof(realfld));
5821590Srgrimes	if (ignore[1].i_count > 0)
5831590Srgrimes		return (!member(realfld, ignore + 1));
5841590Srgrimes	else
5851590Srgrimes		return (member(realfld, ignore));
5861590Srgrimes}
5871590Srgrimes
5881590Srgrimesint
589216564Scharniermember(char *realfield, struct ignoretab *table)
5901590Srgrimes{
59177274Smikeh	struct ignore *igp;
5921590Srgrimes
59377274Smikeh	for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link)
5941590Srgrimes		if (*igp->i_field == *realfield &&
5951590Srgrimes		    equal(igp->i_field, realfield))
5961590Srgrimes			return (1);
5971590Srgrimes	return (0);
5981590Srgrimes}
599