util.c revision 205793
11539Srgrimes/*
21539Srgrimes * Copyright (c) 1980, 1993
31539Srgrimes *	The Regents of the University of California.  All rights reserved.
41539Srgrimes *
51539Srgrimes * Redistribution and use in source and binary forms, with or without
61539Srgrimes * modification, are permitted provided that the following conditions
71539Srgrimes * are met:
81539Srgrimes * 1. Redistributions of source code must retain the above copyright
91539Srgrimes *    notice, this list of conditions and the following disclaimer.
101539Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111539Srgrimes *    notice, this list of conditions and the following disclaimer in the
121539Srgrimes *    documentation and/or other materials provided with the distribution.
131539Srgrimes * 3. All advertising materials mentioning features or use of this software
141539Srgrimes *    must display the following acknowledgement:
151539Srgrimes *	This product includes software developed by the University of
161539Srgrimes *	California, Berkeley and its contributors.
171539Srgrimes * 4. Neither the name of the University nor the names of its contributors
181539Srgrimes *    may be used to endorse or promote products derived from this software
191539Srgrimes *    without specific prior written permission.
201539Srgrimes *
211539Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221539Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231539Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241539Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251539Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261539Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271539Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281539Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291539Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301539Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311539Srgrimes * SUCH DAMAGE.
321539Srgrimes */
331539Srgrimes
341539Srgrimes#ifndef lint
351539Srgrimes#if 0
3623655Speterstatic char sccsid[] = "@(#)aux.c	8.1 (Berkeley) 6/6/93";
3750473Speter#endif
381539Srgrimes#endif /* not lint */
391539Srgrimes#include <sys/cdefs.h>
401539Srgrimes__FBSDID("$FreeBSD: head/usr.bin/mail/util.c 205793 2010-03-28 13:16:08Z ed $");
411539Srgrimes
421539Srgrimes#include <sys/time.h>
431539Srgrimes
44123257Smarcel#include "rcv.h"
45102227Smike#include "extern.h"
461539Srgrimes
47104585Smike/*
48104585Smike * Mail -- a mail program
49102227Smike *
50102227Smike * Auxiliary functions.
51102227Smike */
521539Srgrimes
531539Srgrimesstatic char *save2str(char *, char *);
54104585Smike
55104585Smike/*
56104585Smike * Return a pointer to a dynamic copy of the argument.
57104585Smike */
58104585Smikechar *
59104585Smikesavestr(str)
60104585Smike	char *str;
611539Srgrimes{
621539Srgrimes	char *new;
631539Srgrimes	int size = strlen(str) + 1;
641539Srgrimes
651539Srgrimes	if ((new = salloc(size)) != NULL)
661539Srgrimes		bcopy(str, new, size);
671539Srgrimes	return (new);
681539Srgrimes}
691539Srgrimes
701539Srgrimes/*
711539Srgrimes * Make a copy of new argument incorporating old one.
721539Srgrimes */
731539Srgrimesstatic char *
741539Srgrimessave2str(str, old)
7572529Simp	char *str, *old;
7673254Sdeischen{
7772529Simp	char *new;
781539Srgrimes	int newsize = strlen(str) + 1;
791539Srgrimes	int oldsize = old ? strlen(old) + 1 : 0;
801539Srgrimes
811539Srgrimes	if ((new = salloc(newsize + oldsize)) != NULL) {
821539Srgrimes		if (oldsize) {
831539Srgrimes			bcopy(old, new, oldsize);
841539Srgrimes			new[oldsize - 1] = ' ';
851539Srgrimes		}
861539Srgrimes		bcopy(str, new + oldsize, newsize);
871539Srgrimes	}
881539Srgrimes	return (new);
891539Srgrimes}
901539Srgrimes
911539Srgrimes/*
921539Srgrimes * Touch the named message by setting its MTOUCH flag.
931539Srgrimes * Touched messages have the effect of not being sent
941539Srgrimes * back to the system mailbox on exit.
951539Srgrimes */
961539Srgrimesvoid
971539Srgrimestouch(mp)
981539Srgrimes	struct message *mp;
991539Srgrimes{
1001539Srgrimes
1011539Srgrimes	mp->m_flag |= MTOUCH;
1021539Srgrimes	if ((mp->m_flag & MREAD) == 0)
1031539Srgrimes		mp->m_flag |= MREAD|MSTATUS;
1041539Srgrimes}
1051539Srgrimes
1061539Srgrimes/*
1071539Srgrimes * Test to see if the passed file name is a directory.
1081539Srgrimes * Return true if it is.
1091539Srgrimes */
1101539Srgrimesint
1111539Srgrimesisdir(name)
1121539Srgrimes	char name[];
1131539Srgrimes{
1141539Srgrimes	struct stat sbuf;
11593032Simp
11693032Simp	if (stat(name, &sbuf) < 0)
11793032Simp		return (0);
11893032Simp	return (S_ISDIR(sbuf.st_mode));
1191539Srgrimes}
1201539Srgrimes
1211539Srgrimes/*
12272529Simp * Count the number of arguments in the given string raw list.
1231539Srgrimes */
1241539Srgrimesint
1251539Srgrimesargcount(argv)
1261539Srgrimes	char **argv;
1271539Srgrimes{
1281539Srgrimes	char **ap;
1291539Srgrimes
1301539Srgrimes	for (ap = argv; *ap++ != NULL;)
1311539Srgrimes		;
1321539Srgrimes	return (ap - argv - 1);
1331539Srgrimes}
1341539Srgrimes
1351539Srgrimes/*
1361539Srgrimes * Return the desired header line from the passed message
137129774Stjr * pointer (or NULL if the desired header field is not available).
1381539Srgrimes */
13981600Speterchar *
14081600Speterhfield(field, mp)
14181600Speter	const char *field;
1421539Srgrimes	struct message *mp;
143129774Stjr{
144129774Stjr	FILE *ibuf;
1451539Srgrimes	char linebuf[LINESIZE];
1461539Srgrimes	int lc;
1471539Srgrimes	char *hfield;
1481539Srgrimes	char *colon, *oldhfield = NULL;
1491539Srgrimes
1501539Srgrimes	ibuf = setinput(mp);
1511539Srgrimes	if ((lc = mp->m_lines - 1) < 0)
1521539Srgrimes		return (NULL);
1531539Srgrimes	if (readline(ibuf, linebuf, LINESIZE) < 0)
1541539Srgrimes		return (NULL);
1551539Srgrimes	while (lc > 0) {
1561539Srgrimes		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
15713771Smpp			return (oldhfield);
15813771Smpp		if ((hfield = ishfield(linebuf, colon, field)) != NULL)
1591539Srgrimes			oldhfield = save2str(hfield, oldhfield);
1601539Srgrimes	}
16137489Speter	return (oldhfield);
16272372Sdeischen}
1631539Srgrimes
1641539Srgrimes/*
1651539Srgrimes * Return the next header field found in the given message.
1661539Srgrimes * Return >= 0 if something found, < 0 elsewise.
1671539Srgrimes * "colon" is set to point to the colon in the header.
1681539Srgrimes * Must deal with \ continuations & other such fraud.
1691539Srgrimes */
1701539Srgrimesint
1711539Srgrimesgethfield(f, linebuf, rem, colon)
1721539Srgrimes	FILE *f;
1731539Srgrimes	char linebuf[];
1741539Srgrimes	int rem;
1751539Srgrimes	char **colon;
1761539Srgrimes{
1771539Srgrimes	char line2[LINESIZE];
1781539Srgrimes	char *cp, *cp2;
1791539Srgrimes	int c;
1801539Srgrimes
1811539Srgrimes	for (;;) {
1821539Srgrimes		if (--rem < 0)
1831539Srgrimes			return (-1);
1841539Srgrimes		if ((c = readline(f, linebuf, LINESIZE)) <= 0)
1851539Srgrimes			return (-1);
1861539Srgrimes		for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
1871539Srgrimes		    cp++)
1881539Srgrimes			;
1891539Srgrimes		if (*cp != ':' || cp == linebuf)
190100133Swollman			continue;
1911539Srgrimes		/*
1921539Srgrimes		 * I guess we got a headline.
1931539Srgrimes		 * Handle wraparounding
1941539Srgrimes		 */
1951539Srgrimes		*colon = cp;
1961539Srgrimes		cp = linebuf + c;
1971539Srgrimes		for (;;) {
1981539Srgrimes			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
1991539Srgrimes				;
2001539Srgrimes			cp++;
2011539Srgrimes			if (rem <= 0)
2021539Srgrimes				break;
2031539Srgrimes			ungetc(c = getc(f), f);
2041539Srgrimes			if (c != ' ' && c != '\t')
2051539Srgrimes				break;
20681600Speter			if ((c = readline(f, line2, LINESIZE)) < 0)
20781600Speter				break;
20881600Speter			rem--;
2091539Srgrimes			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
210100133Swollman				;
2111539Srgrimes			c -= cp2 - line2;
2121539Srgrimes			if (cp + c >= linebuf + LINESIZE - 2)
2131539Srgrimes				break;
21493032Simp			*cp++ = ' ';
21593032Simp			bcopy(cp2, cp, c);
21693032Simp			cp += c;
21793032Simp		}
21893032Simp		*cp = 0;
21993032Simp		return (rem);
220104989Smike	}
221104989Smike	/* NOTREACHED */
222104989Smike}
223103012Stjr
22493032Simp/*
225104989Smike * Check whether the passed line is a header line of
226104989Smike * the desired breed.  Return the field body, or 0.
227104989Smike */
228104989Smike
22993032Simpchar*
23093032Simpishfield(linebuf, colon, field)
23193032Simp	char linebuf[];
232104989Smike	char *colon;
23393032Simp	const char *field;
23493032Simp{
23593032Simp	char *cp = colon;
23693032Simp
237103012Stjr	*cp = 0;
23893032Simp	if (strcasecmp(linebuf, field) != 0) {
23993032Simp		*cp = ':';
24093032Simp		return (0);
24193032Simp	}
24293032Simp	*cp = ':';
24393032Simp	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
244104989Smike		;
245103012Stjr	return (cp);
246103012Stjr}
247103012Stjr
248104989Smike/*
24993032Simp * Copy a string and lowercase the result.
25093032Simp * dsize: space left in buffer (including space for NULL)
25193032Simp */
252103012Stjrvoid
253102227Smikeistrncpy(dest, src, dsize)
254103012Stjr	char *dest;
255103012Stjr	const char *src;
256102227Smike	size_t dsize;
2571539Srgrimes{
258100133Swollman
259103012Stjr	strlcpy(dest, src, dsize);
260101914Srobert	while (*dest)
261105098Stjr		*dest++ = tolower((unsigned char)*dest);
262105098Stjr}
263104989Smike
264103012Stjr/*
265102227Smike * The following code deals with input stacking to do source
266104989Smike * commands.  All but the current file pointer are saved on
267104585Smike * the stack.
268100133Swollman */
269100133Swollman
2701539Srgrimesstatic	int	ssp;			/* Top of file stack */
271100133Swollmanstruct sstack {
2721539Srgrimes	FILE	*s_file;		/* File we were in. */
273104585Smike	int	s_cond;			/* Saved state of conditionals */
27419211Swosch	int	s_loading;		/* Loading .mailrc, etc. */
275104585Smike};
276104585Smike#define	SSTACK_SIZE	64		/* XXX was NOFILE. */
2771539Srgrimesstatic struct sstack sstack[SSTACK_SIZE];
278104585Smike
27919211Swosch/*
28019211Swosch * Pushdown current input file and switch to a new one.
28193032Simp * Set the global flag "sourcing" so that others will realize
28293032Simp * that they are no longer reading from a tty (in all probability).
28393032Simp */
284100133Swollmanint
285100133Swollmansource(arglist)
286100133Swollman	char **arglist;
287100133Swollman{
288100133Swollman	FILE *fi;
289100133Swollman	char *cp;
290100133Swollman
291100133Swollman	if ((cp = expand(*arglist)) == NULL)
29293032Simp		return (1);
29393032Simp	if ((fi = Fopen(cp, "r")) == NULL) {
29493032Simp		warn("%s", cp);
2951539Srgrimes		return (1);
2961539Srgrimes	}
297100133Swollman	if (ssp >= SSTACK_SIZE - 1) {
298100133Swollman		printf("Too much \"sourcing\" going on.\n");
29924897Sbde		(void)Fclose(fi);
300100133Swollman		return (1);
301100133Swollman	}
302100133Swollman	sstack[ssp].s_file = input;
303100133Swollman	sstack[ssp].s_cond = cond;
30424897Sbde	sstack[ssp].s_loading = loading;
305109168Stjr	ssp++;
306109168Stjr	loading = 0;
307109168Stjr	cond = CANY;
308109168Stjr	input = fi;
309109168Stjr	sourcing++;
310109168Stjr	return (0);
311100133Swollman}
312100133Swollman
313102227Smike/*
314102227Smike * Pop the current input back to the previous level.
31524897Sbde * Update the "sourcing" flag as appropriate.
316100133Swollman */
317100133Swollmanint
318100133Swollmanunstack()
319100133Swollman{
320100133Swollman	if (ssp <= 0) {
321100133Swollman		printf("\"Source\" stack over-pop.\n");
322100133Swollman		sourcing = 0;
323100133Swollman		return (1);
32424897Sbde	}
32524897Sbde	(void)Fclose(input);
32624897Sbde	if (cond != CANY)
3271539Srgrimes		printf("Unmatched \"if\"\n");
3281539Srgrimes	ssp--;
329100133Swollman	cond = sstack[ssp].s_cond;
33093032Simp	loading = sstack[ssp].s_loading;
33193032Simp	input = sstack[ssp].s_file;
33293032Simp	if (ssp == 0)
33387369Sobrien		sourcing = loading;
33487369Sobrien	return (0);
33587369Sobrien}
33687369Sobrien
33787369Sobrien/*
33893032Simp * Touch the indicated file.
33993032Simp * This is nifty for the shell.
34093032Simp */
34193032Simpvoid
342102227Smikealter(name)
34337614Sbde	char *name;
3441539Srgrimes{
3451539Srgrimes	struct stat sb;
346100133Swollman	struct timeval tv[2];
347100133Swollman
348100133Swollman	if (stat(name, &sb))
349100133Swollman		return;
350100133Swollman	(void)gettimeofday(&tv[0], (struct timezone *)NULL);
351100133Swollman	tv[0].tv_sec++;
352100133Swollman	TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtim);
353100133Swollman	(void)utimes(name, tv);
3541539Srgrimes}
3551539Srgrimes
35693032Simp/*
35775818Sobrien * Get sender's name from this message.  If the message has
35875818Sobrien * a bunch of arpanet stuff in it, we may have to skin the name
35975818Sobrien * before returning it.
36093032Simp */
3611539Srgrimeschar *
3621539Srgrimesnameof(mp, reptype)
3631539Srgrimes	struct message *mp;
3641539Srgrimes	int reptype;
365100133Swollman{
366100133Swollman	char *cp, *cp2;
367100133Swollman
368100133Swollman	cp = skin(name1(mp, reptype));
369102227Smike	if (reptype != 0 || charcount(cp, '!') < 2)
370100133Swollman		return (cp);
371100133Swollman	cp2 = strrchr(cp, '!');
372100133Swollman	cp2--;
373102227Smike	while (cp2 > cp && *cp2 != '!')
374100133Swollman		cp2--;
375100133Swollman	if (*cp2 == '!')
376100133Swollman		return (cp2 + 1);
377102227Smike	return (cp);
378100133Swollman}
379100133Swollman
380100133Swollman/*
381102227Smike * Start of a "comment".
382100133Swollman * Ignore it.
383100133Swollman */
384100133Swollmanchar *
385100133Swollmanskip_comment(cp)
3861539Srgrimes	char *cp;
3871539Srgrimes{
38893032Simp	int nesting = 1;
38993032Simp
3901539Srgrimes	for (; nesting > 0 && *cp; cp++) {
3911539Srgrimes		switch (*cp) {
3928858Srgrimes		case '\\':
3931539Srgrimes			if (cp[1])
3941539Srgrimes				cp++;
3951539Srgrimes			break;
3961539Srgrimes		case '(':
3971539Srgrimes			nesting++;
3981539Srgrimes			break;
3991539Srgrimes		case ')':
4001539Srgrimes			nesting--;
4011539Srgrimes			break;
4021539Srgrimes		}
4031539Srgrimes	}
4041539Srgrimes	return (cp);
4051539Srgrimes}
4061539Srgrimes
4071539Srgrimes/*
4081539Srgrimes * Skin an arpa net address according to the RFC 822 interpretation
4091539Srgrimes * of "host-phrase."
4101539Srgrimes */
4111539Srgrimeschar *
4121539Srgrimesskin(name)
4131539Srgrimes	char *name;
4141539Srgrimes{
4151539Srgrimes	char *nbuf, *bufend, *cp, *cp2;
4161539Srgrimes	int c, gotlt, lastsp;
4171539Srgrimes
4181539Srgrimes	if (name == NULL)
4191539Srgrimes		return (NULL);
4201539Srgrimes	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
4211539Srgrimes	    && strchr(name, ' ') == NULL)
422127100Stjr		return (name);
423127100Stjr
424127230Stjr	/* We assume that length(input) <= length(output) */
425127230Stjr	if ((nbuf = malloc(strlen(name) + 1)) == NULL)
426127230Stjr		err(1, "Out of memory");
427127100Stjr	gotlt = 0;
428127100Stjr	lastsp = 0;
429127230Stjr	bufend = nbuf;
430127100Stjr	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
431127100Stjr		switch (c) {
432127230Stjr		case '(':
433127230Stjr			cp = skip_comment(cp);
434127100Stjr			lastsp = 0;
435127100Stjr			break;
436127100Stjr
437127100Stjr		case '"':
438104585Smike			/*
43935127Sjb			 * Start of a "quoted-string".
44035127Sjb			 * Copy it in its entirety.
44135127Sjb			 */
44235127Sjb			while ((c = *cp) != '\0') {
44335127Sjb				cp++;
44435127Sjb				if (c == '"')
44535127Sjb					break;
44635127Sjb				if (c != '\\')
447104585Smike					*cp2++ = c;
448104585Smike				else if ((c = *cp) != '\0') {
44935127Sjb					*cp2++ = c;
45071580Sdeischen					cp++;
4511539Srgrimes				}
45235127Sjb			}
45335127Sjb			lastsp = 0;
454104585Smike			break;
45524897Sbde
456100133Swollman		case ' ':
45724897Sbde			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
458				cp += 3, *cp2++ = '@';
459			else
460			if (cp[0] == '@' && cp[1] == ' ')
461				cp += 2, *cp2++ = '@';
462			else
463				lastsp = 1;
464			break;
465
466		case '<':
467			cp2 = bufend;
468			gotlt++;
469			lastsp = 0;
470			break;
471
472		case '>':
473			if (gotlt) {
474				gotlt = 0;
475				while ((c = *cp) != '\0' && c != ',') {
476					cp++;
477					if (c == '(')
478						cp = skip_comment(cp);
479					else if (c == '"')
480						while ((c = *cp) != '\0') {
481							cp++;
482							if (c == '"')
483								break;
484							if (c == '\\' && *cp != '\0')
485								cp++;
486						}
487				}
488				lastsp = 0;
489				break;
490			}
491			/* FALLTHROUGH */
492
493		default:
494			if (lastsp) {
495				lastsp = 0;
496				*cp2++ = ' ';
497			}
498			*cp2++ = c;
499			if (c == ',' && *cp == ' ' && !gotlt) {
500				*cp2++ = ' ';
501				while (*++cp == ' ')
502					;
503				lastsp = 0;
504				bufend = cp2;
505			}
506		}
507	}
508	*cp2 = '\0';
509
510	if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
511		nbuf = cp;
512	return (nbuf);
513}
514
515/*
516 * Fetch the sender's name from the passed message.
517 * Reptype can be
518 *	0 -- get sender's name for display purposes
519 *	1 -- get sender's name for reply
520 *	2 -- get sender's name for Reply
521 */
522char *
523name1(mp, reptype)
524	struct message *mp;
525	int reptype;
526{
527	char namebuf[LINESIZE];
528	char linebuf[LINESIZE];
529	char *cp, *cp2;
530	FILE *ibuf;
531	int first = 1;
532
533	if ((cp = hfield("from", mp)) != NULL)
534		return (cp);
535	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
536		return (cp);
537	ibuf = setinput(mp);
538	namebuf[0] = '\0';
539	if (readline(ibuf, linebuf, LINESIZE) < 0)
540		return (savestr(namebuf));
541newname:
542	for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++)
543		;
544	for (; *cp == ' ' || *cp == '\t'; cp++)
545		;
546	for (cp2 = &namebuf[strlen(namebuf)];
547	    *cp != '\0' && *cp != ' ' && *cp != '\t' &&
548	    cp2 < namebuf + LINESIZE - 1;)
549		*cp2++ = *cp++;
550	*cp2 = '\0';
551	if (readline(ibuf, linebuf, LINESIZE) < 0)
552		return (savestr(namebuf));
553	if ((cp = strchr(linebuf, 'F')) == NULL)
554		return (savestr(namebuf));
555	if (strncmp(cp, "From", 4) != 0)
556		return (savestr(namebuf));
557	while ((cp = strchr(cp, 'r')) != NULL) {
558		if (strncmp(cp, "remote", 6) == 0) {
559			if ((cp = strchr(cp, 'f')) == NULL)
560				break;
561			if (strncmp(cp, "from", 4) != 0)
562				break;
563			if ((cp = strchr(cp, ' ')) == NULL)
564				break;
565			cp++;
566			if (first) {
567				cp2 = namebuf;
568				first = 0;
569			} else
570				cp2 = strrchr(namebuf, '!') + 1;
571			strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
572			strcat(namebuf, "!");
573			goto newname;
574		}
575		cp++;
576	}
577	return (savestr(namebuf));
578}
579
580/*
581 * Count the occurances of c in str
582 */
583int
584charcount(str, c)
585	char *str;
586	int c;
587{
588	char *cp;
589	int i;
590
591	for (i = 0, cp = str; *cp != '\0'; cp++)
592		if (*cp == c)
593			i++;
594	return (i);
595}
596
597/*
598 * See if the given header field is supposed to be ignored.
599 */
600int
601isign(field, ignore)
602	const char *field;
603	struct ignoretab ignore[2];
604{
605	char realfld[LINESIZE];
606
607	if (ignore == ignoreall)
608		return (1);
609	/*
610	 * Lower-case the string, so that "Status" and "status"
611	 * will hash to the same place.
612	 */
613	istrncpy(realfld, field, sizeof(realfld));
614	if (ignore[1].i_count > 0)
615		return (!member(realfld, ignore + 1));
616	else
617		return (member(realfld, ignore));
618}
619
620int
621member(realfield, table)
622	char *realfield;
623	struct ignoretab *table;
624{
625	struct ignore *igp;
626
627	for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link)
628		if (*igp->i_field == *realfield &&
629		    equal(igp->i_field, realfield))
630			return (1);
631	return (0);
632}
633