xref: /openbsd-src/usr.bin/mail/util.c (revision d9a51c353c88dac7b4a389c112b4cfe97b8e3a46)
1*d9a51c35Sjmc /*	$OpenBSD: util.c,v 1.2 2022/12/26 19:16:01 jmc Exp $	*/
20f2746abSdaniel /*	$NetBSD: aux.c,v 1.5 1997/05/13 06:15:52 mikel Exp $	*/
30f2746abSdaniel 
40f2746abSdaniel /*
50f2746abSdaniel  * Copyright (c) 1980, 1993
60f2746abSdaniel  *	The Regents of the University of California.  All rights reserved.
70f2746abSdaniel  *
80f2746abSdaniel  * Redistribution and use in source and binary forms, with or without
90f2746abSdaniel  * modification, are permitted provided that the following conditions
100f2746abSdaniel  * are met:
110f2746abSdaniel  * 1. Redistributions of source code must retain the above copyright
120f2746abSdaniel  *    notice, this list of conditions and the following disclaimer.
130f2746abSdaniel  * 2. Redistributions in binary form must reproduce the above copyright
140f2746abSdaniel  *    notice, this list of conditions and the following disclaimer in the
150f2746abSdaniel  *    documentation and/or other materials provided with the distribution.
160f2746abSdaniel  * 3. Neither the name of the University nor the names of its contributors
170f2746abSdaniel  *    may be used to endorse or promote products derived from this software
180f2746abSdaniel  *    without specific prior written permission.
190f2746abSdaniel  *
200f2746abSdaniel  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
210f2746abSdaniel  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
220f2746abSdaniel  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
230f2746abSdaniel  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
240f2746abSdaniel  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
250f2746abSdaniel  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
260f2746abSdaniel  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
270f2746abSdaniel  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
280f2746abSdaniel  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
290f2746abSdaniel  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
300f2746abSdaniel  * SUCH DAMAGE.
310f2746abSdaniel  */
320f2746abSdaniel 
330f2746abSdaniel #include "rcv.h"
340f2746abSdaniel #include <fcntl.h>
350f2746abSdaniel #include "extern.h"
360f2746abSdaniel 
370f2746abSdaniel /*
380f2746abSdaniel  * Mail -- a mail program
390f2746abSdaniel  *
400f2746abSdaniel  * Auxiliary functions.
410f2746abSdaniel  */
420f2746abSdaniel static char *save2str(char *, char *);
430f2746abSdaniel 
440f2746abSdaniel /*
450f2746abSdaniel  * Return a pointer to a dynamic copy of the argument.
460f2746abSdaniel  */
470f2746abSdaniel char *
savestr(const char * str)480f2746abSdaniel savestr(const char *str)
490f2746abSdaniel {
500f2746abSdaniel 	char *new;
510f2746abSdaniel 	int size = strlen(str) + 1;
520f2746abSdaniel 
530f2746abSdaniel 	if ((new = salloc(size)) != NULL)
540f2746abSdaniel 		(void)memcpy(new, str, size);
550f2746abSdaniel 	return(new);
560f2746abSdaniel }
570f2746abSdaniel 
580f2746abSdaniel /*
590f2746abSdaniel  * Make a copy of new argument incorporating old one.
600f2746abSdaniel  */
610f2746abSdaniel static char *
save2str(char * str,char * old)620f2746abSdaniel save2str(char *str, char *old)
630f2746abSdaniel {
640f2746abSdaniel 	char *new;
650f2746abSdaniel 	int newsize = strlen(str) + 1;
660f2746abSdaniel 	int oldsize = old ? strlen(old) + 1 : 0;
670f2746abSdaniel 
680f2746abSdaniel 	if ((new = salloc(newsize + oldsize)) != NULL) {
690f2746abSdaniel 		if (oldsize) {
700f2746abSdaniel 			(void)memcpy(new, old, oldsize);
710f2746abSdaniel 			new[oldsize - 1] = ' ';
720f2746abSdaniel 		}
730f2746abSdaniel 		(void)memcpy(new + oldsize, str, newsize);
740f2746abSdaniel 	}
750f2746abSdaniel 	return(new);
760f2746abSdaniel }
770f2746abSdaniel 
780f2746abSdaniel /*
790f2746abSdaniel  * Touch the named message by setting its MTOUCH flag.
800f2746abSdaniel  * Touched messages have the effect of not being sent
810f2746abSdaniel  * back to the system mailbox on exit.
820f2746abSdaniel  */
830f2746abSdaniel void
touch(struct message * mp)840f2746abSdaniel touch(struct message *mp)
850f2746abSdaniel {
860f2746abSdaniel 
870f2746abSdaniel 	mp->m_flag |= MTOUCH;
880f2746abSdaniel 	if ((mp->m_flag & MREAD) == 0)
890f2746abSdaniel 		mp->m_flag |= MREAD|MSTATUS;
900f2746abSdaniel }
910f2746abSdaniel 
920f2746abSdaniel /*
930f2746abSdaniel  * Test to see if the passed file name is a directory.
940f2746abSdaniel  * Return true if it is.
950f2746abSdaniel  */
960f2746abSdaniel int
isdir(char * name)970f2746abSdaniel isdir(char *name)
980f2746abSdaniel {
990f2746abSdaniel 	struct stat sbuf;
1000f2746abSdaniel 
1010f2746abSdaniel 	if (stat(name, &sbuf) == -1)
1020f2746abSdaniel 		return(0);
1030f2746abSdaniel 	return(S_ISDIR(sbuf.st_mode));
1040f2746abSdaniel }
1050f2746abSdaniel 
1060f2746abSdaniel /*
1070f2746abSdaniel  * Count the number of arguments in the given string raw list.
1080f2746abSdaniel  */
1090f2746abSdaniel int
argcount(char ** argv)1100f2746abSdaniel argcount(char **argv)
1110f2746abSdaniel {
1120f2746abSdaniel 	char **ap;
1130f2746abSdaniel 
1140f2746abSdaniel 	for (ap = argv; *ap++ != NULL;)
1150f2746abSdaniel 		;
1160f2746abSdaniel 	return(ap - argv - 1);
1170f2746abSdaniel }
1180f2746abSdaniel 
1190f2746abSdaniel /*
1200f2746abSdaniel  * Return the desired header line from the passed message
1210f2746abSdaniel  * pointer (or NULL if the desired header field is not available).
1220f2746abSdaniel  */
1230f2746abSdaniel char *
hfield(char * field,struct message * mp)1240f2746abSdaniel hfield(char *field, struct message *mp)
1250f2746abSdaniel {
1260f2746abSdaniel 	FILE *ibuf;
1270f2746abSdaniel 	char linebuf[LINESIZE];
1280f2746abSdaniel 	int lc;
1290f2746abSdaniel 	char *hfield;
1300f2746abSdaniel 	char *colon, *oldhfield = NULL;
1310f2746abSdaniel 
1320f2746abSdaniel 	ibuf = setinput(mp);
1330f2746abSdaniel 	if ((lc = mp->m_lines - 1) < 0)
1340f2746abSdaniel 		return(NULL);
1350f2746abSdaniel 	if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
1360f2746abSdaniel 		return(NULL);
1370f2746abSdaniel 	while (lc > 0) {
1380f2746abSdaniel 		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
1390f2746abSdaniel 			return(oldhfield);
1400f2746abSdaniel 		if ((hfield = ishfield(linebuf, colon, field)) != NULL)
1410f2746abSdaniel 			oldhfield = save2str(hfield, oldhfield);
1420f2746abSdaniel 	}
1430f2746abSdaniel 	return(oldhfield);
1440f2746abSdaniel }
1450f2746abSdaniel 
1460f2746abSdaniel /*
1470f2746abSdaniel  * Return the next header field found in the given message.
1480f2746abSdaniel  * Return >= 0 if something found, < 0 elsewise.
1490f2746abSdaniel  * "colon" is set to point to the colon in the header.
1500f2746abSdaniel  * Must deal with \ continuations & other such fraud.
1510f2746abSdaniel  */
1520f2746abSdaniel int
gethfield(FILE * f,char * linebuf,int rem,char ** colon)1530f2746abSdaniel gethfield(FILE *f, char *linebuf, int rem, char **colon)
1540f2746abSdaniel {
1550f2746abSdaniel 	char line2[LINESIZE];
1560f2746abSdaniel 	char *cp, *cp2;
1570f2746abSdaniel 	int c;
1580f2746abSdaniel 
1590f2746abSdaniel 	for (;;) {
1600f2746abSdaniel 		if (--rem < 0)
1610f2746abSdaniel 			return(-1);
1620f2746abSdaniel 		if ((c = readline(f, linebuf, LINESIZE, NULL)) <= 0)
1630f2746abSdaniel 			return(-1);
1640f2746abSdaniel 		for (cp = linebuf;
1650f2746abSdaniel 		    isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
1660f2746abSdaniel 		    cp++)
1670f2746abSdaniel 			;
1680f2746abSdaniel 		if (*cp != ':' || cp == linebuf)
1690f2746abSdaniel 			continue;
1700f2746abSdaniel 		/*
1710f2746abSdaniel 		 * I guess we got a headline.
1720f2746abSdaniel 		 * Handle wraparounding
1730f2746abSdaniel 		 */
1740f2746abSdaniel 		*colon = cp;
1750f2746abSdaniel 		cp = linebuf + c;
1760f2746abSdaniel 		for (;;) {
1770f2746abSdaniel 			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
1780f2746abSdaniel 				;
1790f2746abSdaniel 			cp++;
1800f2746abSdaniel 			if (rem <= 0)
1810f2746abSdaniel 				break;
1820f2746abSdaniel 			ungetc(c = getc(f), f);
1830f2746abSdaniel 			if (c != ' ' && c != '\t')
1840f2746abSdaniel 				break;
1850f2746abSdaniel 			if ((c = readline(f, line2, LINESIZE, NULL)) < 0)
1860f2746abSdaniel 				break;
1870f2746abSdaniel 			rem--;
1880f2746abSdaniel 			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
1890f2746abSdaniel 				;
1900f2746abSdaniel 			c -= cp2 - line2;
1910f2746abSdaniel 			if (cp + c >= linebuf + LINESIZE - 2)
1920f2746abSdaniel 				break;
1930f2746abSdaniel 			*cp++ = ' ';
1940f2746abSdaniel 			(void)memcpy(cp, cp2, c);
1950f2746abSdaniel 			cp += c;
1960f2746abSdaniel 		}
1970f2746abSdaniel 		*cp = 0;
1980f2746abSdaniel 		return(rem);
1990f2746abSdaniel 	}
2000f2746abSdaniel 	/* NOTREACHED */
2010f2746abSdaniel }
2020f2746abSdaniel 
2030f2746abSdaniel /*
2040f2746abSdaniel  * Check whether the passed line is a header line of
2050f2746abSdaniel  * the desired breed.  Return the field body, or 0.
2060f2746abSdaniel  */
2070f2746abSdaniel 
2080f2746abSdaniel char*
ishfield(char * linebuf,char * colon,char * field)2090f2746abSdaniel ishfield(char *linebuf, char *colon, char *field)
2100f2746abSdaniel {
2110f2746abSdaniel 	char *cp = colon;
2120f2746abSdaniel 
2130f2746abSdaniel 	*cp = 0;
2140f2746abSdaniel 	if (strcasecmp(linebuf, field) != 0) {
2150f2746abSdaniel 		*cp = ':';
2160f2746abSdaniel 		return(0);
2170f2746abSdaniel 	}
2180f2746abSdaniel 	*cp = ':';
2190f2746abSdaniel 	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
2200f2746abSdaniel 		;
2210f2746abSdaniel 	return(cp);
2220f2746abSdaniel }
2230f2746abSdaniel 
2240f2746abSdaniel /*
2250f2746abSdaniel  * Copy a string, lowercasing it as we go.  ``dsize'' should be
2260f2746abSdaniel  * the real size (not len) of the dest string (guarantee NUL term).
2270f2746abSdaniel  */
2280f2746abSdaniel size_t
istrlcpy(char * dst,const char * src,size_t dsize)2290f2746abSdaniel istrlcpy(char *dst, const char *src, size_t dsize)
2300f2746abSdaniel {
2310f2746abSdaniel 	char *d = dst;
2320f2746abSdaniel 	const char *s = src;
2330f2746abSdaniel 	size_t n = dsize;
2340f2746abSdaniel 
2350f2746abSdaniel 	/* Copy as many bytes as will fit */
2360f2746abSdaniel 	if (n != 0 && --n != 0) {
2370f2746abSdaniel 		do {
2380f2746abSdaniel 			if ((*d++ = tolower((unsigned char)*s++)) == 0)
2390f2746abSdaniel 				break;
2400f2746abSdaniel 		} while (--n != 0);
2410f2746abSdaniel 	}
2420f2746abSdaniel 
2430f2746abSdaniel 	/* Not enough room in dst, add NUL and traverse rest of src */
2440f2746abSdaniel 	if (n == 0) {
2450f2746abSdaniel 		if (dsize != 0)
2460f2746abSdaniel 			*d = '\0';		/* NUL-terminate dst */
2470f2746abSdaniel 		while (*s++)
2480f2746abSdaniel 			;
2490f2746abSdaniel 	}
2500f2746abSdaniel 
2510f2746abSdaniel 	return(s - src - 1);	/* count does not include NUL */
2520f2746abSdaniel }
2530f2746abSdaniel 
2540f2746abSdaniel /*
2550f2746abSdaniel  * The following code deals with input stacking to do source
2560f2746abSdaniel  * commands.  All but the current file pointer are saved on
2570f2746abSdaniel  * the stack.
2580f2746abSdaniel  */
2590f2746abSdaniel static	int	ssp;			/* Top of file stack */
2600f2746abSdaniel struct sstack {
2610f2746abSdaniel 	FILE	*s_file;		/* File we were in. */
2620f2746abSdaniel 	int	s_cond;			/* Saved state of conditionals */
2630f2746abSdaniel 	int	s_loading;		/* Loading .mailrc, etc. */
2640f2746abSdaniel } sstack[OPEN_MAX];
2650f2746abSdaniel 
2660f2746abSdaniel /*
2670f2746abSdaniel  * Pushdown current input file and switch to a new one.
2680f2746abSdaniel  * Set the global flag "sourcing" so that others will realize
2690f2746abSdaniel  * that they are no longer reading from a tty (in all probability).
2700f2746abSdaniel  */
2710f2746abSdaniel int
source(void * v)2720f2746abSdaniel source(void *v)
2730f2746abSdaniel {
2740f2746abSdaniel 	char **arglist = v;
2750f2746abSdaniel 	FILE *fi;
2760f2746abSdaniel 	char *cp;
2770f2746abSdaniel 
2780f2746abSdaniel 	if ((cp = expand(*arglist)) == NULL)
2790f2746abSdaniel 		return(1);
2800f2746abSdaniel 	if ((fi = Fopen(cp, "r")) == NULL) {
2810f2746abSdaniel 		warn("%s", cp);
2820f2746abSdaniel 		return(1);
2830f2746abSdaniel 	}
2840f2746abSdaniel 	if (ssp >= OPEN_MAX - 1) {
2850f2746abSdaniel 		puts("Too much \"sourcing\" going on.");
2860f2746abSdaniel 		(void)Fclose(fi);
2870f2746abSdaniel 		return(1);
2880f2746abSdaniel 	}
2890f2746abSdaniel 	sstack[ssp].s_file = input;
2900f2746abSdaniel 	sstack[ssp].s_cond = cond;
2910f2746abSdaniel 	sstack[ssp].s_loading = loading;
2920f2746abSdaniel 	ssp++;
2930f2746abSdaniel 	loading = 0;
2940f2746abSdaniel 	cond = CANY;
2950f2746abSdaniel 	input = fi;
2960f2746abSdaniel 	sourcing++;
2970f2746abSdaniel 	return(0);
2980f2746abSdaniel }
2990f2746abSdaniel 
3000f2746abSdaniel /*
3010f2746abSdaniel  * Pop the current input back to the previous level.
3020f2746abSdaniel  * Update the "sourcing" flag as appropriate.
3030f2746abSdaniel  */
3040f2746abSdaniel int
unstack(void)3050f2746abSdaniel unstack(void)
3060f2746abSdaniel {
3070f2746abSdaniel 
3080f2746abSdaniel 	if (ssp <= 0) {
3090f2746abSdaniel 		puts("\"Source\" stack over-pop.");
3100f2746abSdaniel 		sourcing = 0;
3110f2746abSdaniel 		return(1);
3120f2746abSdaniel 	}
3130f2746abSdaniel 	(void)Fclose(input);
3140f2746abSdaniel 	if (cond != CANY)
3150f2746abSdaniel 		puts("Unmatched \"if\"");
3160f2746abSdaniel 	ssp--;
3170f2746abSdaniel 	cond = sstack[ssp].s_cond;
3180f2746abSdaniel 	loading = sstack[ssp].s_loading;
3190f2746abSdaniel 	input = sstack[ssp].s_file;
3200f2746abSdaniel 	if (ssp == 0)
3210f2746abSdaniel 		sourcing = loading;
3220f2746abSdaniel 	return(0);
3230f2746abSdaniel }
3240f2746abSdaniel 
3250f2746abSdaniel /*
3260f2746abSdaniel  * Touch the indicated file.
3270f2746abSdaniel  * This is nifty for the shell.
3280f2746abSdaniel  */
3290f2746abSdaniel void
alter(char * name)3300f2746abSdaniel alter(char *name)
3310f2746abSdaniel {
3320f2746abSdaniel 	struct timespec ts[2];
3330f2746abSdaniel 
3340f2746abSdaniel 	clock_gettime(CLOCK_REALTIME, &ts[0]);
3350f2746abSdaniel 	ts[0].tv_sec++;
3360f2746abSdaniel 	ts[1].tv_nsec = UTIME_OMIT;
3370f2746abSdaniel 	(void)utimensat(AT_FDCWD, name, ts, 0);
3380f2746abSdaniel }
3390f2746abSdaniel 
3400f2746abSdaniel /*
3410f2746abSdaniel  * Examine the passed line buffer and
3420f2746abSdaniel  * return true if it is all blanks and tabs.
3430f2746abSdaniel  */
3440f2746abSdaniel int
blankline(char * linebuf)3450f2746abSdaniel blankline(char *linebuf)
3460f2746abSdaniel {
3470f2746abSdaniel 	char *cp;
3480f2746abSdaniel 
3490f2746abSdaniel 	for (cp = linebuf; *cp; cp++)
3500f2746abSdaniel 		if (*cp != ' ' && *cp != '\t')
3510f2746abSdaniel 			return(0);
3520f2746abSdaniel 	return(1);
3530f2746abSdaniel }
3540f2746abSdaniel 
3550f2746abSdaniel /*
3560f2746abSdaniel  * Get sender's name from this message.  If the message has
3570f2746abSdaniel  * a bunch of arpanet stuff in it, we may have to skin the name
3580f2746abSdaniel  * before returning it.
3590f2746abSdaniel  */
3600f2746abSdaniel char *
nameof(struct message * mp,int reptype)3610f2746abSdaniel nameof(struct message *mp, int reptype)
3620f2746abSdaniel {
3630f2746abSdaniel 	char *cp, *cp2;
3640f2746abSdaniel 
3650f2746abSdaniel 	cp = skin(name1(mp, reptype));
3660f2746abSdaniel 	if (reptype != 0 || charcount(cp, '!') < 2)
3670f2746abSdaniel 		return(cp);
3680f2746abSdaniel 	cp2 = strrchr(cp, '!');
3690f2746abSdaniel 	cp2--;
3700f2746abSdaniel 	while (cp2 > cp && *cp2 != '!')
3710f2746abSdaniel 		cp2--;
3720f2746abSdaniel 	if (*cp2 == '!')
3730f2746abSdaniel 		return(cp2 + 1);
3740f2746abSdaniel 	return(cp);
3750f2746abSdaniel }
3760f2746abSdaniel 
3770f2746abSdaniel /*
3780f2746abSdaniel  * Start of a "comment".
3790f2746abSdaniel  * Ignore it.
3800f2746abSdaniel  */
3810f2746abSdaniel char *
skip_comment(char * cp)3820f2746abSdaniel skip_comment(char *cp)
3830f2746abSdaniel {
3840f2746abSdaniel 	int nesting = 1;
3850f2746abSdaniel 
3860f2746abSdaniel 	for (; nesting > 0 && *cp; cp++) {
3870f2746abSdaniel 		switch (*cp) {
3880f2746abSdaniel 		case '\\':
3890f2746abSdaniel 			if (cp[1])
3900f2746abSdaniel 				cp++;
3910f2746abSdaniel 			break;
3920f2746abSdaniel 		case '(':
3930f2746abSdaniel 			nesting++;
3940f2746abSdaniel 			break;
3950f2746abSdaniel 		case ')':
3960f2746abSdaniel 			nesting--;
3970f2746abSdaniel 			break;
3980f2746abSdaniel 		}
3990f2746abSdaniel 	}
4000f2746abSdaniel 	return(cp);
4010f2746abSdaniel }
4020f2746abSdaniel 
4030f2746abSdaniel /*
4040f2746abSdaniel  * Skin an arpa net address according to the RFC 822 interpretation
4050f2746abSdaniel  * of "host-phrase."
4060f2746abSdaniel  */
4070f2746abSdaniel char *
skin(char * name)4080f2746abSdaniel skin(char *name)
4090f2746abSdaniel {
4100f2746abSdaniel 	char *nbuf, *bufend, *cp, *cp2;
4110f2746abSdaniel 	int c, gotlt, lastsp;
4120f2746abSdaniel 
4130f2746abSdaniel 	if (name == NULL)
4140f2746abSdaniel 		return(NULL);
4150f2746abSdaniel 	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
4160f2746abSdaniel 	    && strchr(name, ' ') == NULL)
4170f2746abSdaniel 		return(name);
4180f2746abSdaniel 
4190f2746abSdaniel 	/* We assume that length(input) <= length(output) */
4200f2746abSdaniel 	if ((nbuf = malloc(strlen(name) + 1)) == NULL)
4210f2746abSdaniel 		err(1, "malloc");
4220f2746abSdaniel 	gotlt = 0;
4230f2746abSdaniel 	lastsp = 0;
4240f2746abSdaniel 	bufend = nbuf;
4250f2746abSdaniel 	for (cp = name, cp2 = bufend; (c = (unsigned char)*cp++) != '\0'; ) {
4260f2746abSdaniel 		switch (c) {
4270f2746abSdaniel 		case '(':
4280f2746abSdaniel 			cp = skip_comment(cp);
4290f2746abSdaniel 			lastsp = 0;
4300f2746abSdaniel 			break;
4310f2746abSdaniel 
4320f2746abSdaniel 		case '"':
4330f2746abSdaniel 			/*
4340f2746abSdaniel 			 * Start of a "quoted-string".
4350f2746abSdaniel 			 * Copy it in its entirety.
4360f2746abSdaniel 			 */
4370f2746abSdaniel 			while ((c = (unsigned char)*cp) != '\0') {
4380f2746abSdaniel 				cp++;
4390f2746abSdaniel 				if (c == '"')
4400f2746abSdaniel 					break;
4410f2746abSdaniel 				if (c != '\\')
4420f2746abSdaniel 					*cp2++ = c;
4430f2746abSdaniel 				else if ((c = (unsigned char)*cp) != '\0') {
4440f2746abSdaniel 					*cp2++ = c;
4450f2746abSdaniel 					cp++;
4460f2746abSdaniel 				}
4470f2746abSdaniel 			}
4480f2746abSdaniel 			lastsp = 0;
4490f2746abSdaniel 			break;
4500f2746abSdaniel 
4510f2746abSdaniel 		case ' ':
4520f2746abSdaniel 			if (strncmp(cp, "at ", 3) == 0)
4530f2746abSdaniel 				cp += 3, *cp2++ = '@';
4540f2746abSdaniel 			else
4550f2746abSdaniel 			if (strncmp(cp, "@ ", 2) == 0)
4560f2746abSdaniel 				cp += 2, *cp2++ = '@';
4570f2746abSdaniel 			else
4580f2746abSdaniel 				lastsp = 1;
4590f2746abSdaniel 			break;
4600f2746abSdaniel 
4610f2746abSdaniel 		case '<':
4620f2746abSdaniel 			cp2 = bufend;
4630f2746abSdaniel 			gotlt++;
4640f2746abSdaniel 			lastsp = 0;
4650f2746abSdaniel 			break;
4660f2746abSdaniel 
4670f2746abSdaniel 		case '>':
4680f2746abSdaniel 			if (gotlt) {
4690f2746abSdaniel 				gotlt = 0;
4700f2746abSdaniel 				while ((c = (unsigned char)*cp) && c != ',') {
4710f2746abSdaniel 					cp++;
4720f2746abSdaniel 					if (c == '(')
4730f2746abSdaniel 						cp = skip_comment(cp);
4740f2746abSdaniel 					else if (c == '"')
4750f2746abSdaniel 						while ((c = (unsigned char)*cp) != '\0') {
4760f2746abSdaniel 							cp++;
4770f2746abSdaniel 							if (c == '"')
4780f2746abSdaniel 								break;
4790f2746abSdaniel 							if (c == '\\' && *cp)
4800f2746abSdaniel 								cp++;
4810f2746abSdaniel 						}
4820f2746abSdaniel 				}
4830f2746abSdaniel 				lastsp = 0;
4840f2746abSdaniel 				break;
4850f2746abSdaniel 			}
4860f2746abSdaniel 			/* Fall into . . . */
4870f2746abSdaniel 
4880f2746abSdaniel 		default:
4890f2746abSdaniel 			if (lastsp) {
4900f2746abSdaniel 				lastsp = 0;
4910f2746abSdaniel 				*cp2++ = ' ';
4920f2746abSdaniel 			}
4930f2746abSdaniel 			*cp2++ = c;
4940f2746abSdaniel 			if (c == ',' && *cp == ' ' && !gotlt) {
4950f2746abSdaniel 				*cp2++ = ' ';
4960f2746abSdaniel 				while (*++cp == ' ')
4970f2746abSdaniel 					;
4980f2746abSdaniel 				lastsp = 0;
4990f2746abSdaniel 				bufend = cp2;
5000f2746abSdaniel 			}
5010f2746abSdaniel 		}
5020f2746abSdaniel 	}
5030f2746abSdaniel 	*cp2 = 0;
5040f2746abSdaniel 
5050f2746abSdaniel 	if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
5060f2746abSdaniel 		nbuf = cp;
5070f2746abSdaniel 	return(nbuf);
5080f2746abSdaniel }
5090f2746abSdaniel 
5100f2746abSdaniel /*
5110f2746abSdaniel  * Fetch the sender's name from the passed message.
5120f2746abSdaniel  * Reptype can be
5130f2746abSdaniel  *	0 -- get sender's name for display purposes
5140f2746abSdaniel  *	1 -- get sender's name for reply
5150f2746abSdaniel  *	2 -- get sender's name for Reply
5160f2746abSdaniel  */
5170f2746abSdaniel char *
name1(struct message * mp,int reptype)5180f2746abSdaniel name1(struct message *mp, int reptype)
5190f2746abSdaniel {
5200f2746abSdaniel 	char namebuf[LINESIZE];
5210f2746abSdaniel 	char linebuf[LINESIZE];
5220f2746abSdaniel 	char *cp, *cp2;
5230f2746abSdaniel 	FILE *ibuf;
5240f2746abSdaniel 	int first = 1;
5250f2746abSdaniel 
5260f2746abSdaniel 	if ((cp = hfield("from", mp)) != NULL)
5270f2746abSdaniel 		return(cp);
5280f2746abSdaniel 	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
5290f2746abSdaniel 		return(cp);
5300f2746abSdaniel 	ibuf = setinput(mp);
5310f2746abSdaniel 	namebuf[0] = '\0';
5320f2746abSdaniel 	if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
5330f2746abSdaniel 		return(savestr(namebuf));
5340f2746abSdaniel newname:
5350f2746abSdaniel 	for (cp = linebuf; *cp && *cp != ' '; cp++)
5360f2746abSdaniel 		;
5370f2746abSdaniel 	for (; *cp == ' ' || *cp == '\t'; cp++)
5380f2746abSdaniel 		;
5390f2746abSdaniel 	for (cp2 = &namebuf[strlen(namebuf)];
5400f2746abSdaniel 	     *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
5410f2746abSdaniel 		*cp2++ = *cp++;
5420f2746abSdaniel 	*cp2 = '\0';
5430f2746abSdaniel 	if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
5440f2746abSdaniel 		return(savestr(namebuf));
5450f2746abSdaniel 	if ((cp = strchr(linebuf, 'F')) == NULL)
5460f2746abSdaniel 		return(savestr(namebuf));
5470f2746abSdaniel 	if (strncmp(cp, "From", 4) != 0)
5480f2746abSdaniel 		return(savestr(namebuf));
5490f2746abSdaniel 	while ((cp = strchr(cp, 'r')) != NULL) {
5500f2746abSdaniel 		if (strncmp(cp, "remote", 6) == 0) {
5510f2746abSdaniel 			if ((cp = strchr(cp, 'f')) == NULL)
5520f2746abSdaniel 				break;
5530f2746abSdaniel 			if (strncmp(cp, "from", 4) != 0)
5540f2746abSdaniel 				break;
5550f2746abSdaniel 			if ((cp = strchr(cp, ' ')) == NULL)
5560f2746abSdaniel 				break;
5570f2746abSdaniel 			cp++;
5580f2746abSdaniel 			if (first) {
5590f2746abSdaniel 				cp2 = namebuf;
5600f2746abSdaniel 				first = 0;
5610f2746abSdaniel 			} else
5620f2746abSdaniel 				cp2 = strrchr(namebuf, '!') + 1;
5630f2746abSdaniel 			strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
5640f2746abSdaniel 			strlcat(namebuf, "!", sizeof(namebuf));
5650f2746abSdaniel 			goto newname;
5660f2746abSdaniel 		}
5670f2746abSdaniel 		cp++;
5680f2746abSdaniel 	}
5690f2746abSdaniel 	return(savestr(namebuf));
5700f2746abSdaniel }
5710f2746abSdaniel 
5720f2746abSdaniel /*
573*d9a51c35Sjmc  * Count the occurrences of c in str
5740f2746abSdaniel  */
5750f2746abSdaniel int
charcount(char * str,int c)5760f2746abSdaniel charcount(char *str, int c)
5770f2746abSdaniel {
5780f2746abSdaniel 	char *cp;
5790f2746abSdaniel 	int i;
5800f2746abSdaniel 
5810f2746abSdaniel 	for (i = 0, cp = str; *cp; cp++)
5820f2746abSdaniel 		if (*cp == c)
5830f2746abSdaniel 			i++;
5840f2746abSdaniel 	return(i);
5850f2746abSdaniel }
5860f2746abSdaniel 
5870f2746abSdaniel /*
5880f2746abSdaniel  * Copy s1 to s2, return pointer to null in s2.
5890f2746abSdaniel  */
5900f2746abSdaniel char *
copy(char * s1,char * s2)5910f2746abSdaniel copy(char *s1, char *s2)
5920f2746abSdaniel {
5930f2746abSdaniel 
5940f2746abSdaniel 	while ((*s2++ = *s1++) != '\0')
5950f2746abSdaniel 		;
5960f2746abSdaniel 	return(s2 - 1);
5970f2746abSdaniel }
5980f2746abSdaniel 
5990f2746abSdaniel /*
6000f2746abSdaniel  * See if the given header field is supposed to be ignored.
6010f2746abSdaniel  */
6020f2746abSdaniel int
isign(char * field,struct ignoretab ignore[2])6030f2746abSdaniel isign(char *field, struct ignoretab ignore[2])
6040f2746abSdaniel {
6050f2746abSdaniel 	char realfld[LINESIZE];
6060f2746abSdaniel 
6070f2746abSdaniel 	if (ignore == ignoreall)
6080f2746abSdaniel 		return(1);
6090f2746abSdaniel 	/*
6100f2746abSdaniel 	 * Lower-case the string, so that "Status" and "status"
6110f2746abSdaniel 	 * will hash to the same place.
6120f2746abSdaniel 	 */
6130f2746abSdaniel 	istrlcpy(realfld, field, sizeof(realfld));
6140f2746abSdaniel 	if (ignore[1].i_count > 0)
6150f2746abSdaniel 		return(!member(realfld, ignore + 1));
6160f2746abSdaniel 	else
6170f2746abSdaniel 		return(member(realfld, ignore));
6180f2746abSdaniel }
6190f2746abSdaniel 
6200f2746abSdaniel int
member(char * realfield,struct ignoretab * table)6210f2746abSdaniel member(char *realfield, struct ignoretab *table)
6220f2746abSdaniel {
6230f2746abSdaniel 	struct ignore *igp;
6240f2746abSdaniel 
6250f2746abSdaniel 	for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
6260f2746abSdaniel 		if (*igp->i_field == *realfield &&
6270f2746abSdaniel 		    equal(igp->i_field, realfield))
6280f2746abSdaniel 			return(1);
6290f2746abSdaniel 	return(0);
6300f2746abSdaniel }
6310f2746abSdaniel 
6320f2746abSdaniel void
clearnew(void)6330f2746abSdaniel clearnew(void)
6340f2746abSdaniel {
6350f2746abSdaniel 	struct message *mp;
6360f2746abSdaniel 
6370f2746abSdaniel 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
6380f2746abSdaniel 		if (mp->m_flag & MNEW) {
6390f2746abSdaniel 			mp->m_flag &= ~MNEW;
6400f2746abSdaniel 			mp->m_flag |= MSTATUS;
6410f2746abSdaniel 		}
6420f2746abSdaniel 	}
6430f2746abSdaniel }
644