xref: /csrg-svn/usr.bin/mail/optim.c (revision 24756)
122462Sdist /*
222462Sdist  * Copyright (c) 1980 Regents of the University of California.
322462Sdist  * All rights reserved.  The Berkeley software License Agreement
422462Sdist  * specifies the terms and conditions for redistribution.
522462Sdist  */
622462Sdist 
714538Ssam #ifndef lint
8*24756Sserge static char *sccsid = "@(#)optim.c	5.4 (Berkeley) 09/15/85";
922462Sdist #endif not lint
101242Skas 
111242Skas /*
121242Skas  * Mail -- a program for sending and receiving mail.
131242Skas  *
141242Skas  * Network name modification routines.
151242Skas  */
161242Skas 
171242Skas #include "rcv.h"
184337Skurt #include "configdefs.h"
191242Skas #include <ctype.h>
201242Skas 
211242Skas /*
221242Skas  * Map a name into the correct network "view" of the
231242Skas  * name.  This is done by prepending the name with the
241242Skas  * network address of the sender, then optimizing away
251242Skas  * nonsense.
261242Skas  */
271242Skas 
281242Skas char *
291242Skas netmap(name, from)
301242Skas 	char name[], from[];
311242Skas {
321242Skas 	char nbuf[BUFSIZ], ret[BUFSIZ];
331242Skas 	register char *cp;
341242Skas 
351242Skas 	if (strlen(from) == 0)
361242Skas 		return(name);
371242Skas 	if (any('@', name) || any('%', name))
3811387Sleres 		return(savestr(arpafix(name, from)));
391242Skas 	cp = revarpa(from);
401242Skas 	if (cp == NOSTR)
411242Skas 		return(name);
421242Skas 	strcpy(nbuf, cp);
431242Skas 	cp = &nbuf[strlen(nbuf) - 1];
441242Skas 	while (!any(*cp, metanet) && cp > nbuf)
451242Skas 		cp--;
461242Skas 	if (cp == nbuf)
471242Skas 		return(name);
481242Skas 	*++cp = 0;
491242Skas 	strcat(nbuf, revarpa(name));
501242Skas 	optim(nbuf, ret);
511242Skas 	cp = revarpa(ret);
521242Skas 	if (!icequal(name, cp))
5311387Sleres 		return(savestr(cp));
541242Skas 	return(name);
551242Skas }
561242Skas 
571242Skas /*
581242Skas  * Rename the given network path to use
591242Skas  * the kinds of names that we would right here.
601242Skas  */
611242Skas 
621242Skas char *
631242Skas rename(str)
641242Skas 	char str[];
651242Skas {
661242Skas 	register char *cp, *cp2;
671242Skas 	char buf[BUFSIZ], path[BUFSIZ];
681242Skas 	register int c, host;
691242Skas 
70*24756Sserge 	cp = str;
711242Skas 	strcpy(path, "");
721242Skas 	for (;;) {
731242Skas 		if ((c = *cp++) == 0)
741242Skas 			break;
751242Skas 		cp2 = buf;
761242Skas 		while (!any(c, metanet) && c != 0) {
771242Skas 			*cp2++ = c;
781242Skas 			c = *cp++;
791242Skas 		}
801242Skas 		*cp2 = 0;
811242Skas 		if (c == 0) {
821242Skas 			strcat(path, buf);
831242Skas 			break;
841242Skas 		}
851242Skas 		host = netlook(buf, ntype(c));
861242Skas 		strcat(path, netname(host));
871242Skas 		stradd(path, c);
881242Skas 	}
891242Skas 	if (strcmp(str, path) != 0)
901242Skas 		return(savestr(path));
911242Skas 	return(str);
921242Skas }
934337Skurt 
941242Skas /*
951242Skas  * Turn a network machine name into a unique character
961242Skas  */
971242Skas netlook(machine, attnet)
981242Skas 	char machine[];
991242Skas {
1001242Skas 	register struct netmach *np;
1011242Skas 	register char *cp, *cp2;
10216768Sralph 	char nbuf[100];
1031242Skas 
1041242Skas 	/*
1051242Skas 	 * Make into lower case.
1061242Skas 	 */
1071242Skas 
1081242Skas 	for (cp = machine, cp2 = nbuf; *cp; *cp2++ = little(*cp++))
10916768Sralph 		if (cp2 >= &nbuf[sizeof(nbuf)-1])
11016768Sralph 			break;
1111242Skas 	*cp2 = 0;
1121242Skas 
1131242Skas 	/*
1141242Skas 	 * If a single letter machine, look through those first.
1151242Skas 	 */
1161242Skas 
1171242Skas 	if (strlen(nbuf) == 1)
1181242Skas 		for (np = netmach; np->nt_mid != 0; np++)
1191242Skas 			if (np->nt_mid == nbuf[0])
1201242Skas 				return(nbuf[0]);
1211242Skas 
1221242Skas 	/*
1231242Skas 	 * Look for usual name
1241242Skas 	 */
1251242Skas 
1261242Skas 	for (np = netmach; np->nt_mid != 0; np++)
1271242Skas 		if (strcmp(np->nt_machine, nbuf) == 0)
1281242Skas 			return(np->nt_mid);
1291242Skas 
1301242Skas 	/*
1311242Skas 	 * Look in side hash table.
1321242Skas 	 */
1331242Skas 
1341242Skas 	return(mstash(nbuf, attnet));
1351242Skas }
1361242Skas 
1371242Skas /*
1381242Skas  * Make a little character.
1391242Skas  */
1401242Skas 
1411242Skas little(c)
1421242Skas 	register int c;
1431242Skas {
1441242Skas 
1451242Skas 	if (c >= 'A' && c <= 'Z')
1461242Skas 		c += 'a' - 'A';
1471242Skas 	return(c);
1481242Skas }
1491242Skas 
1501242Skas /*
1511242Skas  * Turn a network unique character identifier into a network name.
1521242Skas  */
1531242Skas 
1541242Skas char *
1551242Skas netname(mid)
1561242Skas {
1571242Skas 	register struct netmach *np;
1581242Skas 	char *mlook();
1591242Skas 
1601242Skas 	if (mid & 0200)
1611242Skas 		return(mlook(mid));
1621242Skas 	for (np = netmach; np->nt_mid != 0; np++)
1631242Skas 		if (np->nt_mid == mid)
1641242Skas 			return(np->nt_machine);
1651242Skas 	return(NOSTR);
1661242Skas }
1671242Skas 
1681242Skas /*
1691242Skas  * Deal with arpa net addresses.  The way this is done is strange.
1701242Skas  * In particular, if the destination arpa net host is not Berkeley,
1711242Skas  * then the address is correct as stands.  Otherwise, we strip off
1721242Skas  * the trailing @Berkeley, then cook up a phony person for it to
1731242Skas  * be from and optimize the result.
1741242Skas  */
1751242Skas char *
1761242Skas arpafix(name, from)
1771242Skas 	char name[];
1781242Skas 	char from[];
1791242Skas {
1801242Skas 	register char *cp;
1811242Skas 	register int arpamach;
1821242Skas 	char newname[BUFSIZ];
1831242Skas 	char fake[5];
1841242Skas 	char fakepath[20];
1851242Skas 
1861242Skas 	if (debug) {
1871242Skas 		fprintf(stderr, "arpafix(%s, %s)\n", name, from);
1881242Skas 	}
1891242Skas 	cp = rindex(name, '@');
1901242Skas 	if (cp == NOSTR)
1911242Skas 		cp = rindex(name, '%');
1921242Skas 	if (cp == NOSTR) {
1931242Skas 		fprintf(stderr, "Somethings amiss -- no @ or % in arpafix\n");
1941242Skas 		return(name);
1951242Skas 	}
1961242Skas 	cp++;
1971242Skas 	arpamach = netlook(cp, '@');
1981242Skas 	if (arpamach == 0) {
1991242Skas 		if (debug)
2001242Skas 			fprintf(stderr, "machine %s unknown, uses: %s\n", cp, name);
2011242Skas 		return(name);
2021242Skas 	}
2031242Skas 	if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) {
2041242Skas 		if (debug)
2051242Skas 			fprintf(stderr, "machine %s known but remote, uses: %s\n",
2061242Skas 			    cp, name);
2071242Skas 		return(name);
2081242Skas 	}
2091242Skas 	strcpy(newname, name);
2101242Skas 	cp = rindex(newname, '@');
2111242Skas 	if (cp == NOSTR)
2121242Skas 		cp = rindex(newname, '%');
2131242Skas 	*cp = 0;
2141242Skas 	fake[0] = arpamach;
2151242Skas 	fake[1] = ':';
2161242Skas 	fake[2] = LOCAL;
2171242Skas 	fake[3] = ':';
2181242Skas 	fake[4] = 0;
2191242Skas 	prefer(fake);
2201242Skas 	strcpy(fakepath, netname(fake[0]));
2211242Skas 	stradd(fakepath, fake[1]);
2221242Skas 	strcat(fakepath, "daemon");
2231242Skas 	if (debug)
2241242Skas 		fprintf(stderr, "machine local, call netmap(%s, %s)\n",
2251242Skas 		    newname, fakepath);
2261242Skas 	return(netmap(newname, fakepath));
2271242Skas }
2281242Skas 
2291242Skas /*
2301242Skas  * Take a network machine descriptor and find the types of connected
2311242Skas  * nets and return it.
2321242Skas  */
2331242Skas 
2341242Skas nettype(mid)
2351242Skas {
2361242Skas 	register struct netmach *np;
2371242Skas 
2381242Skas 	if (mid & 0200)
2391242Skas 		return(mtype(mid));
2401242Skas 	for (np = netmach; np->nt_mid != 0; np++)
2411242Skas 		if (np->nt_mid == mid)
2421242Skas 			return(np->nt_type);
2431242Skas 	return(0);
2441242Skas }
2451242Skas 
2461242Skas /*
2471242Skas  * Hashing routines to salt away machines seen scanning
2481242Skas  * networks paths that we don't know about.
2491242Skas  */
2501242Skas 
2511242Skas #define	XHSIZE		19		/* Size of extra hash table */
2521242Skas #define	NXMID		(XHSIZE*3/4)	/* Max extra machines */
2531242Skas 
2541242Skas struct xtrahash {
2551242Skas 	char	*xh_name;		/* Name of machine */
2561242Skas 	short	xh_mid;			/* Machine ID */
2571242Skas 	short	xh_attnet;		/* Attached networks */
2581242Skas } xtrahash[XHSIZE];
2591242Skas 
2601242Skas struct xtrahash	*xtab[XHSIZE];		/* F: mid-->machine name */
2611242Skas 
2621242Skas short	midfree;			/* Next free machine id */
2631242Skas 
2641242Skas /*
2651242Skas  * Initialize the extra host hash table.
2661242Skas  * Called by sreset.
2671242Skas  */
2681242Skas 
2691242Skas minit()
2701242Skas {
2711242Skas 	register struct xtrahash *xp, **tp;
2721242Skas 	register int i;
2731242Skas 
2741242Skas 	midfree = 0;
2751242Skas 	tp = &xtab[0];
2761242Skas 	for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) {
2771242Skas 		xp->xh_name = NOSTR;
2781242Skas 		xp->xh_mid = 0;
2791242Skas 		xp->xh_attnet = 0;
2801242Skas 		*tp++ = (struct xtrahash *) 0;
2811242Skas 	}
2821242Skas }
2831242Skas 
2841242Skas /*
2851242Skas  * Stash a net name in the extra host hash table.
2861242Skas  * If a new entry is put in the hash table, deduce what
2871242Skas  * net the machine is attached to from the net character.
2881242Skas  *
2891242Skas  * If the machine is already known, add the given attached
2901242Skas  * net to those already known.
2911242Skas  */
2921242Skas 
2931242Skas mstash(name, attnet)
2941242Skas 	char name[];
2951242Skas {
2961242Skas 	register struct xtrahash *xp;
2971242Skas 	struct xtrahash *xlocate();
2984337Skurt 	int x;
2991242Skas 
3001242Skas 	xp = xlocate(name);
3011242Skas 	if (xp == (struct xtrahash *) 0) {
3021242Skas 		printf("Ran out of machine id spots\n");
3031242Skas 		return(0);
3041242Skas 	}
3051242Skas 	if (xp->xh_name == NOSTR) {
3061242Skas 		if (midfree >= XHSIZE) {
3071242Skas 			printf("Out of machine ids\n");
3081242Skas 			return(0);
3091242Skas 		}
3101242Skas 		xtab[midfree] = xp;
3111242Skas 		xp->xh_name = savestr(name);
3121242Skas 		xp->xh_mid = 0200 + midfree++;
3131242Skas 	}
3144337Skurt 	x = ntype(attnet);
3154337Skurt 	if (x == 0)
3161242Skas 		xp->xh_attnet |= SN;
3174337Skurt 	else
3184337Skurt 		xp->xh_attnet |= x;
3191242Skas 	return(xp->xh_mid);
3201242Skas }
3211242Skas 
3221242Skas /*
3231242Skas  * Search for the given name in the hash table
3241242Skas  * and return the pointer to it if found, or to the first
3251242Skas  * empty slot if not found.
3261242Skas  *
3271242Skas  * If no free slots can be found, return 0.
3281242Skas  */
3291242Skas 
3301242Skas struct xtrahash *
3311242Skas xlocate(name)
3321242Skas 	char name[];
3331242Skas {
3341242Skas 	register int h, q, i;
3351242Skas 	register char *cp;
3361242Skas 	register struct xtrahash *xp;
3371242Skas 
3381242Skas 	for (h = 0, cp = name; *cp; h = (h << 2) + *cp++)
3391242Skas 		;
3401242Skas 	if (h < 0 && (h = -h) < 0)
3411242Skas 		h = 0;
3421242Skas 	h = h % XHSIZE;
3431242Skas 	cp = name;
3441242Skas 	for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) {
3451242Skas 		xp = &xtrahash[(h + q) % XHSIZE];
3461242Skas 		if (xp->xh_name == NOSTR)
3471242Skas 			return(xp);
3481242Skas 		if (strcmp(cp, xp->xh_name) == 0)
3491242Skas 			return(xp);
3501242Skas 		if (h - q < 0)
35123466Slepreau 			h += XHSIZE;
3521242Skas 		xp = &xtrahash[(h - q) % XHSIZE];
3531242Skas 		if (xp->xh_name == NOSTR)
3541242Skas 			return(xp);
3551242Skas 		if (strcmp(cp, xp->xh_name) == 0)
3561242Skas 			return(xp);
3571242Skas 	}
3581242Skas 	return((struct xtrahash *) 0);
3591242Skas }
3601242Skas 
3611242Skas /*
3621242Skas  * Return the name from the extra host hash table corresponding
3631242Skas  * to the passed machine id.
3641242Skas  */
3651242Skas 
3661242Skas char *
3671242Skas mlook(mid)
3681242Skas {
3691242Skas 	register int m;
3701242Skas 
3711242Skas 	if ((mid & 0200) == 0)
3721242Skas 		return(NOSTR);
3731242Skas 	m = mid & 0177;
3741242Skas 	if (m >= midfree) {
3751242Skas 		printf("Use made of undefined machine id\n");
3761242Skas 		return(NOSTR);
3771242Skas 	}
3781242Skas 	return(xtab[m]->xh_name);
3791242Skas }
3801242Skas 
3811242Skas /*
3821242Skas  * Return the bit mask of net's that the given extra host machine
3831242Skas  * id has so far.
3841242Skas  */
3851242Skas 
3861242Skas mtype(mid)
3871242Skas {
3881242Skas 	register int m;
3891242Skas 
3901242Skas 	if ((mid & 0200) == 0)
3911242Skas 		return(0);
3921242Skas 	m = mid & 0177;
3931242Skas 	if (m >= midfree) {
3941242Skas 		printf("Use made of undefined machine id\n");
3951242Skas 		return(0);
3961242Skas 	}
3971242Skas 	return(xtab[m]->xh_attnet);
3981242Skas }
3991242Skas 
4001242Skas /*
4011242Skas  * Take a network name and optimize it.  This gloriously messy
4023915Skurt  * operation takes place as follows:  the name with machine names
4031242Skas  * in it is tokenized by mapping each machine name into a single
4041242Skas  * character machine id (netlook).  The separator characters (network
4051242Skas  * metacharacters) are left intact.  The last component of the network
4061242Skas  * name is stripped off and assumed to be the destination user name --
4071242Skas  * it does not participate in the optimization.  As an example, the
4081242Skas  * name "research!vax135!research!ucbvax!bill" becomes, tokenized,
4091242Skas  * "r!x!r!v!" and "bill"  A low level routine, optim1, fixes up the
4101242Skas  * network part (eg, "r!x!r!v!"), then we convert back to network
4111242Skas  * machine names and tack the user name on the end.
4121242Skas  *
4131242Skas  * The result of this is copied into the parameter "name"
4141242Skas  */
4151242Skas 
4161242Skas optim(net, name)
4171242Skas 	char net[], name[];
4181242Skas {
4191242Skas 	char netcomp[BUFSIZ], netstr[40], xfstr[40];
4201242Skas 	register char *cp, *cp2;
4211242Skas 	register int c;
4221242Skas 
4231242Skas 	strcpy(netstr, "");
4241242Skas 	cp = net;
4251242Skas 	for (;;) {
4261242Skas 		/*
4271242Skas 		 * Rip off next path component into netcomp
4281242Skas 		 */
4291242Skas 		cp2 = netcomp;
4301242Skas 		while (*cp && !any(*cp, metanet))
4311242Skas 			*cp2++ = *cp++;
4321242Skas 		*cp2 = 0;
4331242Skas 		/*
4341242Skas 		 * If we hit null byte, then we just scanned
4351242Skas 		 * the destination user name.  Go off and optimize
4361242Skas 		 * if its so.
4371242Skas 		 */
4381242Skas 		if (*cp == 0)
4391242Skas 			break;
4401242Skas 		if ((c = netlook(netcomp, *cp)) == 0) {
4411242Skas 			printf("No host named \"%s\"\n", netcomp);
4421242Skas err:
4431242Skas 			strcpy(name, net);
4441242Skas 			return;
4451242Skas 		}
4461242Skas 		stradd(netstr, c);
4471242Skas 		stradd(netstr, *cp++);
4481242Skas 		/*
4491242Skas 		 * If multiple network separators given,
4501242Skas 		 * throw away the extras.
4511242Skas 		 */
4521242Skas 		while (any(*cp, metanet))
4531242Skas 			cp++;
4541242Skas 	}
4551242Skas 	if (strlen(netcomp) == 0) {
4561242Skas 		printf("net name syntax\n");
4571242Skas 		goto err;
4581242Skas 	}
4591242Skas 	optim1(netstr, xfstr);
4601242Skas 
4611242Skas 	/*
4621242Skas 	 * Convert back to machine names.
4631242Skas 	 */
4641242Skas 
4651242Skas 	cp = xfstr;
4661242Skas 	strcpy(name, "");
4671242Skas 	while (*cp) {
4681242Skas 		if ((cp2 = netname(*cp++)) == NOSTR) {
4691242Skas 			printf("Made up bad net name\n");
4704333Skurt 			printf("Machine code %c (0%o)\n", cp[-1], cp[-1]);
4714333Skurt 			printf("Sorry -- dumping now.  Alert K. Shoens\n");
4724333Skurt 			core(0);
4731242Skas 			goto err;
4741242Skas 		}
4751242Skas 		strcat(name, cp2);
4761242Skas 		stradd(name, *cp++);
4771242Skas 	}
4781242Skas 	strcat(name, netcomp);
4791242Skas }
4801242Skas 
4811242Skas /*
4821242Skas  * Take a string of network machine id's and separators and
4831242Skas  * optimize them.  We process these by pulling off maximal
4841242Skas  * leading strings of the same type, passing these to the appropriate
4851242Skas  * optimizer and concatenating the results.
4861242Skas  */
4871242Skas 
4881242Skas optim1(netstr, name)
4891242Skas 	char netstr[], name[];
4901242Skas {
4911242Skas 	char path[40], rpath[40];
4921242Skas 	register char *cp, *cp2;
4931242Skas 	register int tp, nc;
4941242Skas 
4951242Skas 	cp = netstr;
4961242Skas 	prefer(cp);
4974342Skurt 	strcpy(name, "");
4983915Skurt 	/*
4993915Skurt 	 * If the address ultimately points back to us,
5003915Skurt 	 * just return a null network path.
5013915Skurt 	 */
5023915Skurt 	if (strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL)
5033915Skurt 		return;
5041242Skas 	while (*cp != 0) {
5051242Skas 		strcpy(path, "");
5061242Skas 		tp = ntype(cp[1]);
5071242Skas 		nc = cp[1];
5081242Skas 		while (*cp && tp == ntype(cp[1])) {
5091242Skas 			stradd(path, *cp++);
5101242Skas 			cp++;
5111242Skas 		}
5121242Skas 		switch (netkind(tp)) {
5131242Skas 		default:
5141242Skas 			strcpy(rpath, path);
5151242Skas 			break;
5161242Skas 
5171242Skas 		case IMPLICIT:
5181242Skas 			optimimp(path, rpath);
5191242Skas 			break;
5201242Skas 
5211242Skas 		case EXPLICIT:
5221242Skas 			optimex(path, rpath);
5231242Skas 			break;
5241242Skas 		}
5251242Skas 		for (cp2 = rpath; *cp2 != 0; cp2++) {
5261242Skas 			stradd(name, *cp2);
5271242Skas 			stradd(name, nc);
5281242Skas 		}
5291242Skas 	}
5301242Skas 	optiboth(name);
5311242Skas 	prefer(name);
5321242Skas }
5331242Skas 
5341242Skas /*
5351242Skas  * Return the network of the separator --
5361242Skas  *	AN for arpa net
5371242Skas  *	BN for Bell labs net
5381242Skas  *	SN for Schmidt (berkeley net)
5391242Skas  *	0 if we don't know.
5401242Skas  */
5411242Skas 
5421242Skas ntype(nc)
5431242Skas 	register int nc;
5441242Skas {
5457566Skurt 	register struct ntypetab *np;
5461242Skas 
5477566Skurt 	for (np = ntypetab; np->nt_char != 0; np++)
5484337Skurt 		if (np->nt_char == nc)
5497566Skurt 			return(np->nt_bcode);
5504337Skurt 	return(0);
5511242Skas }
5521242Skas 
5531242Skas /*
5541242Skas  * Return the kind of routing used for the particular net
5551242Skas  * EXPLICIT means explicitly routed
5561242Skas  * IMPLICIT means implicitly routed
5571242Skas  * 0 means don't know
5581242Skas  */
5591242Skas 
5601242Skas netkind(nt)
5611242Skas 	register int nt;
5621242Skas {
5637566Skurt 	register struct nkindtab *np;
5641242Skas 
5657566Skurt 	for (np = nkindtab; np->nk_type != 0; np++)
5664337Skurt 		if (np->nk_type == nt)
5674337Skurt 			return(np->nk_kind);
5684337Skurt 	return(0);
5691242Skas }
5701242Skas 
5711242Skas /*
5721242Skas  * Do name optimization for an explicitly routed network (eg BTL network).
5731242Skas  */
5741242Skas 
5751242Skas optimex(net, name)
5761242Skas 	char net[], name[];
5771242Skas {
5781242Skas 	register char *cp, *rp;
5791242Skas 	register int m;
5801242Skas 	char *rindex();
5811242Skas 
5821242Skas 	strcpy(name, net);
5831242Skas 	cp = name;
5841242Skas 	if (strlen(cp) == 0)
5851242Skas 		return(-1);
5861242Skas 	if (cp[strlen(cp)-1] == LOCAL) {
5871242Skas 		name[0] = 0;
5881242Skas 		return(0);
5891242Skas 	}
5901242Skas 	for (cp = name; *cp; cp++) {
5911242Skas 		m = *cp;
5921242Skas 		rp = rindex(cp+1, m);
5931242Skas 		if (rp != NOSTR)
5941242Skas 			strcpy(cp, rp);
5951242Skas 	}
5961242Skas 	return(0);
5971242Skas }
5981242Skas 
5991242Skas /*
6001242Skas  * Do name optimization for implicitly routed network (eg, arpanet,
6011242Skas  * Berkeley network)
6021242Skas  */
6031242Skas 
6041242Skas optimimp(net, name)
6051242Skas 	char net[], name[];
6061242Skas {
6071242Skas 	register char *cp;
6081242Skas 	register int m;
6091242Skas 
6101242Skas 	cp = net;
6111242Skas 	if (strlen(cp) == 0)
6121242Skas 		return(-1);
6131242Skas 	m = cp[strlen(cp) - 1];
6141242Skas 	if (m == LOCAL) {
6151242Skas 		strcpy(name, "");
6161242Skas 		return(0);
6171242Skas 	}
6181242Skas 	name[0] = m;
6191242Skas 	name[1] = 0;
6201242Skas 	return(0);
6211242Skas }
6221242Skas 
6231242Skas /*
6241242Skas  * Perform global optimization on the given network path.
6251242Skas  * The trick here is to look ahead to see if there are any loops
6261242Skas  * in the path and remove them.  The interpretation of loops is
6271242Skas  * more strict here than in optimex since both the machine and net
6281242Skas  * type must match.
6291242Skas  */
6301242Skas 
6311242Skas optiboth(net)
6321242Skas 	char net[];
6331242Skas {
6341242Skas 	register char *cp, *cp2;
6351242Skas 	char *rpair();
6361242Skas 
6371242Skas 	cp = net;
6381242Skas 	if (strlen(cp) == 0)
6391242Skas 		return;
6401242Skas 	if ((strlen(cp) % 2) != 0) {
6411242Skas 		printf("Strange arg to optiboth\n");
6421242Skas 		return;
6431242Skas 	}
6441242Skas 	while (*cp) {
6451242Skas 		cp2 = rpair(cp+2, *cp);
6461242Skas 		if (cp2 != NOSTR)
6471242Skas 			strcpy(cp, cp2);
6481242Skas 		cp += 2;
6491242Skas 	}
6501242Skas }
6511242Skas 
6521242Skas /*
6531242Skas  * Find the rightmost instance of the given (machine, type) pair.
6541242Skas  */
6551242Skas 
6561242Skas char *
6571242Skas rpair(str, mach)
6581242Skas 	char str[];
6591242Skas {
6601242Skas 	register char *cp, *last;
6611242Skas 
662*24756Sserge 	cp = str;
6631242Skas 	last = NOSTR;
6641242Skas 	while (*cp) {
6651242Skas 		if (*cp == mach)
6661242Skas 			last = cp;
6671242Skas 		cp += 2;
6681242Skas 	}
6691242Skas 	return(last);
6701242Skas }
6711242Skas 
6721242Skas /*
6731242Skas  * Change the network separators in the given network path
6741242Skas  * to the preferred network transmission means.
6751242Skas  */
6761242Skas 
6771242Skas prefer(name)
6781242Skas 	char name[];
6791242Skas {
6801242Skas 	register char *cp;
6811242Skas 	register int state, n;
6821242Skas 
6831242Skas 	state = LOCAL;
6841242Skas 	for (cp = name; *cp; cp += 2) {
6851242Skas 		n = best(state, *cp);
6861242Skas 		if (n)
6871242Skas 			cp[1] = n;
6881242Skas 		state = *cp;
6891242Skas 	}
6901242Skas }
6911242Skas 
6921242Skas /*
6931242Skas  * Return the best network separator for the given machine pair.
6941242Skas  */
6951242Skas 
6961242Skas best(src, dest)
6971242Skas {
6981242Skas 	register int dtype, stype;
6991242Skas 	register struct netorder *np;
7001242Skas 
7011242Skas 	stype = nettype(src);
7021242Skas 	dtype = nettype(dest);
7033915Skurt 	fflush(stdout);
7041242Skas 	if (stype == 0 || dtype == 0) {
7051242Skas 		printf("ERROR:  unknown internal machine id\n");
7061242Skas 		return(0);
7071242Skas 	}
7083915Skurt 	if ((stype & dtype) == 0)
7091242Skas 		return(0);
7101242Skas 	np = &netorder[0];
7111242Skas 	while ((np->no_stat & stype & dtype) == 0)
7121242Skas 		np++;
7131242Skas 	return(np->no_char);
7141242Skas }
7151242Skas 
7169069Smckusick #ifdef	GETHOST
7171242Skas /*
7188014Smckusick  * Initialize the network name of the current host.
7198014Smckusick  */
7208014Smckusick inithost()
7218014Smckusick {
7228014Smckusick 	register struct netmach *np;
7238014Smckusick 	static char host[64];
7248014Smckusick 
7258014Smckusick 	gethostname(host, sizeof host);
7268014Smckusick 	for (np = netmach; np->nt_machine != 0; np++)
7278014Smckusick 		if (strcmp(np->nt_machine, EMPTY) == 0)
7288014Smckusick 			break;
7298014Smckusick 	if (np->nt_machine == 0) {
7308014Smckusick 		printf("Cannot find empty slot for dynamic host entry\n");
7318014Smckusick 		exit(1);
7328014Smckusick 	}
7338014Smckusick 	np->nt_machine = host;
7348014Smckusick }
7359069Smckusick #endif	GETHOST
7368014Smckusick 
7378014Smckusick /*
7381242Skas  * Code to twist around arpa net names.
7391242Skas  */
7401242Skas 
7411242Skas #define WORD 257			/* Token for a string */
7421242Skas 
7431242Skas static	char netbuf[256];
7441242Skas static	char *yylval;
7451242Skas 
7461242Skas /*
7471242Skas  * Reverse all of the arpa net addresses in the given name to
7481242Skas  * be of the form "host @ user" instead of "user @ host"
7491242Skas  * This function is its own inverse.
7501242Skas  */
7511242Skas 
7521242Skas char *
7531242Skas revarpa(str)
7541242Skas 	char str[];
7551242Skas {
7561242Skas 
7571242Skas 	if (yyinit(str) < 0)
7581242Skas 		return(NOSTR);
7591242Skas 	if (name())
7601242Skas 		return(NOSTR);
7611242Skas 	if (strcmp(str, netbuf) == 0)
7621242Skas 		return(str);
7631242Skas 	return(savestr(netbuf));
7641242Skas }
7651242Skas 
7661242Skas /*
7671242Skas  * Parse (by recursive descent) network names, using the following grammar:
7681242Skas  *	name:
7691242Skas  *		term {':' term}
7701242Skas  *		term {'^' term}
7711242Skas  *		term {'!' term}
7721242Skas  *		term '@' name
7731242Skas  *		term '%' name
7741242Skas  *
7751242Skas  *	term:
7761242Skas  *		string of characters.
7771242Skas  */
7781242Skas 
7791242Skas name()
7801242Skas {
7811242Skas 	register int t;
7821242Skas 	register char *cp;
7831242Skas 
7841242Skas 	for (;;) {
7851242Skas 		t = yylex();
7861242Skas 		if (t != WORD)
7871242Skas 			return(-1);
7881242Skas 		cp = yylval;
7891242Skas 		t = yylex();
7901242Skas 		switch (t) {
7911242Skas 		case 0:
7921242Skas 			strcat(netbuf, cp);
7931242Skas 			return(0);
7941242Skas 
7951242Skas 		case '@':
7961242Skas 		case '%':
7971242Skas 			if (name())
7981242Skas 				return(-1);
7991242Skas 			stradd(netbuf, '@');
8001242Skas 			strcat(netbuf, cp);
8011242Skas 			return(0);
8021242Skas 
8031242Skas 		case WORD:
8041242Skas 			return(-1);
8051242Skas 
8061242Skas 		default:
8071242Skas 			strcat(netbuf, cp);
8081242Skas 			stradd(netbuf, t);
8091242Skas 		}
8101242Skas 	}
8111242Skas }
8121242Skas 
8131242Skas /*
8141242Skas  * Scanner for network names.
8151242Skas  */
8161242Skas 
8171242Skas static	char *charp;			/* Current input pointer */
8181242Skas static	int nexttok;			/* Salted away next token */
8191242Skas 
8201242Skas /*
8211242Skas  * Initialize the network name scanner.
8221242Skas  */
8231242Skas 
8241242Skas yyinit(str)
8251242Skas 	char str[];
8261242Skas {
8271242Skas 	static char lexbuf[BUFSIZ];
8281242Skas 
8291242Skas 	netbuf[0] = 0;
8301242Skas 	if (strlen(str) >= sizeof lexbuf - 1)
8311242Skas 		return(-1);
8321242Skas 	nexttok = 0;
8331242Skas 	strcpy(lexbuf, str);
8341242Skas 	charp = lexbuf;
8351242Skas 	return(0);
8361242Skas }
8371242Skas 
8381242Skas /*
8391242Skas  * Scan and return a single token.
8401242Skas  * yylval is set to point to a scanned string.
8411242Skas  */
8421242Skas 
8431242Skas yylex()
8441242Skas {
8451242Skas 	register char *cp, *dot;
8461242Skas 	register int s;
8471242Skas 
8481242Skas 	if (nexttok) {
8491242Skas 		s = nexttok;
8501242Skas 		nexttok = 0;
8511242Skas 		return(s);
8521242Skas 	}
8531242Skas 	cp = charp;
8541242Skas 	while (*cp && isspace(*cp))
8551242Skas 		cp++;
8561242Skas 	if (*cp == 0)
8571242Skas 		return(0);
8584337Skurt 	if (any(*cp, metanet)) {
8591242Skas 		charp = cp+1;
8601242Skas 		return(*cp);
8611242Skas 	}
8621242Skas 	dot = cp;
8634337Skurt 	while (*cp && !any(*cp, metanet) && !any(*cp, " \t"))
8641242Skas 		cp++;
8654337Skurt 	if (any(*cp, metanet))
8661242Skas 		nexttok = *cp;
8671242Skas 	if (*cp == 0)
8681242Skas 		charp = cp;
8691242Skas 	else
8701242Skas 		charp = cp+1;
8711242Skas 	*cp = 0;
8721242Skas 	yylval = dot;
8731242Skas 	return(WORD);
8741242Skas }
8751242Skas 
8761242Skas /*
8771242Skas  * Add a single character onto a string.
8781242Skas  */
8791242Skas 
8801242Skas stradd(str, c)
8811242Skas 	register char *str;
8821242Skas 	register int c;
8831242Skas {
8841242Skas 
8851242Skas 	str += strlen(str);
8861242Skas 	*str++ = c;
8871242Skas 	*str = 0;
8881242Skas }
889