xref: /csrg-svn/usr.bin/mail/optim.c (revision 1242)
1*1242Skas #
2*1242Skas 
3*1242Skas /*
4*1242Skas  * Mail -- a program for sending and receiving mail.
5*1242Skas  *
6*1242Skas  * Network name modification routines.
7*1242Skas  */
8*1242Skas 
9*1242Skas #include "rcv.h"
10*1242Skas #include <ctype.h>
11*1242Skas 
12*1242Skas static char *SccsId = "@(#)optim.c	1.1 10/08/80";
13*1242Skas 
14*1242Skas /*
15*1242Skas  * Map a name into the correct network "view" of the
16*1242Skas  * name.  This is done by prepending the name with the
17*1242Skas  * network address of the sender, then optimizing away
18*1242Skas  * nonsense.
19*1242Skas  */
20*1242Skas 
21*1242Skas char	*metanet = "!^:%@.";
22*1242Skas 
23*1242Skas char *
24*1242Skas netmap(name, from)
25*1242Skas 	char name[], from[];
26*1242Skas {
27*1242Skas 	char nbuf[BUFSIZ], ret[BUFSIZ];
28*1242Skas 	register char *cp;
29*1242Skas 
30*1242Skas 	if (strlen(from) == 0)
31*1242Skas 		return(name);
32*1242Skas 	if (any('@', name) || any('%', name))
33*1242Skas 		return(arpafix(name, from));
34*1242Skas 	cp = revarpa(from);
35*1242Skas 	if (cp == NOSTR)
36*1242Skas 		return(name);
37*1242Skas 	strcpy(nbuf, cp);
38*1242Skas 	cp = &nbuf[strlen(nbuf) - 1];
39*1242Skas 	while (!any(*cp, metanet) && cp > nbuf)
40*1242Skas 		cp--;
41*1242Skas 	if (cp == nbuf)
42*1242Skas 		return(name);
43*1242Skas 	*++cp = 0;
44*1242Skas 	strcat(nbuf, revarpa(name));
45*1242Skas 	optim(nbuf, ret);
46*1242Skas 	cp = revarpa(ret);
47*1242Skas 	if (!icequal(name, cp))
48*1242Skas 		return((char *) savestr(cp));
49*1242Skas 	return(name);
50*1242Skas }
51*1242Skas 
52*1242Skas /*
53*1242Skas  * Rename the given network path to use
54*1242Skas  * the kinds of names that we would right here.
55*1242Skas  */
56*1242Skas 
57*1242Skas char *
58*1242Skas rename(str)
59*1242Skas 	char str[];
60*1242Skas {
61*1242Skas 	register char *cp, *cp2;
62*1242Skas 	char buf[BUFSIZ], path[BUFSIZ];
63*1242Skas 	register int c, host;
64*1242Skas 
65*1242Skas 	strcpy(path, "");
66*1242Skas 	for (;;) {
67*1242Skas 		if ((c = *cp++) == 0)
68*1242Skas 			break;
69*1242Skas 		cp2 = buf;
70*1242Skas 		while (!any(c, metanet) && c != 0) {
71*1242Skas 			*cp2++ = c;
72*1242Skas 			c = *cp++;
73*1242Skas 		}
74*1242Skas 		*cp2 = 0;
75*1242Skas 		if (c == 0) {
76*1242Skas 			strcat(path, buf);
77*1242Skas 			break;
78*1242Skas 		}
79*1242Skas 		host = netlook(buf, ntype(c));
80*1242Skas 		strcat(path, netname(host));
81*1242Skas 		stradd(path, c);
82*1242Skas 	}
83*1242Skas 	if (strcmp(str, path) != 0)
84*1242Skas 		return(savestr(path));
85*1242Skas 	return(str);
86*1242Skas }
87*1242Skas /*
88*1242Skas  * Turn a network machine name into a unique character
89*1242Skas  * + give connection-to status.  BN -- connected to Bell Net.
90*1242Skas  * AN -- connected to ARPA net, SN -- connected to Schmidt net.
91*1242Skas  * CN -- connected to COCANET.
92*1242Skas  */
93*1242Skas 
94*1242Skas #define	AN	1			/* Connected to ARPA net */
95*1242Skas #define	BN	2			/* Connected to BTL net */
96*1242Skas #define	CN	4			/* Connected to COCANET */
97*1242Skas #define	SN	8			/* Connected to Schmidt net */
98*1242Skas 
99*1242Skas struct netmach {
100*1242Skas 	char	*nt_machine;
101*1242Skas 	char	nt_mid;
102*1242Skas 	short	nt_type;
103*1242Skas } netmach[] = {
104*1242Skas 	"a",		'a',		SN,
105*1242Skas 	"b",		'b',		SN,
106*1242Skas 	"c",		'c',		SN,
107*1242Skas 	"d",		'd',		SN,
108*1242Skas 	"e",		'e',		SN,
109*1242Skas 	"f",		'f',		SN,
110*1242Skas 	"g",		'g',		SN,
111*1242Skas 	"ingres",	'i',		AN|SN,
112*1242Skas 	"ing70",	'i',		AN|SN,
113*1242Skas 	"berkeley",	'i',		AN|SN,
114*1242Skas 	"ingvax",	'j',		SN,
115*1242Skas 	"virus",	'k',		SN,
116*1242Skas 	"vlsi",		'l',		SN,
117*1242Skas 	"image",	'm',		SN,
118*1242Skas 	"esvax",	'o',		SN,
119*1242Skas 	"sesm",		'o',		SN,
120*1242Skas 	"q",		'q',		SN,
121*1242Skas 	"research",	'R',		BN,
122*1242Skas 	"arpavax",	'r',		SN,
123*1242Skas 	"src",		's',		SN,
124*1242Skas 	"mathstat",	't',		SN,
125*1242Skas 	"csvax",	'v',		BN|SN,
126*1242Skas 	"vax",		'v',		BN|SN,
127*1242Skas 	"ucb",		'v',		BN|SN,
128*1242Skas 	"ucbvax",	'v',		BN|SN,
129*1242Skas 	"vax135",	'x',		BN,
130*1242Skas 	"cory",		'y',		SN,
131*1242Skas 	"eecs40",	'z',		SN,
132*1242Skas 	0,		0,		0
133*1242Skas };
134*1242Skas 
135*1242Skas netlook(machine, attnet)
136*1242Skas 	char machine[];
137*1242Skas {
138*1242Skas 	register struct netmach *np;
139*1242Skas 	register char *cp, *cp2;
140*1242Skas 	char nbuf[20];
141*1242Skas 
142*1242Skas 	/*
143*1242Skas 	 * Make into lower case.
144*1242Skas 	 */
145*1242Skas 
146*1242Skas 	for (cp = machine, cp2 = nbuf; *cp; *cp2++ = little(*cp++))
147*1242Skas 		;
148*1242Skas 	*cp2 = 0;
149*1242Skas 
150*1242Skas 	/*
151*1242Skas 	 * If a single letter machine, look through those first.
152*1242Skas 	 */
153*1242Skas 
154*1242Skas 	if (strlen(nbuf) == 1)
155*1242Skas 		for (np = netmach; np->nt_mid != 0; np++)
156*1242Skas 			if (np->nt_mid == nbuf[0])
157*1242Skas 				return(nbuf[0]);
158*1242Skas 
159*1242Skas 	/*
160*1242Skas 	 * Look for usual name
161*1242Skas 	 */
162*1242Skas 
163*1242Skas 	for (np = netmach; np->nt_mid != 0; np++)
164*1242Skas 		if (strcmp(np->nt_machine, nbuf) == 0)
165*1242Skas 			return(np->nt_mid);
166*1242Skas 
167*1242Skas 	/*
168*1242Skas 	 * Look in side hash table.
169*1242Skas 	 */
170*1242Skas 
171*1242Skas 	return(mstash(nbuf, attnet));
172*1242Skas }
173*1242Skas 
174*1242Skas /*
175*1242Skas  * Make a little character.
176*1242Skas  */
177*1242Skas 
178*1242Skas little(c)
179*1242Skas 	register int c;
180*1242Skas {
181*1242Skas 
182*1242Skas 	if (c >= 'A' && c <= 'Z')
183*1242Skas 		c += 'a' - 'A';
184*1242Skas 	return(c);
185*1242Skas }
186*1242Skas 
187*1242Skas /*
188*1242Skas  * Turn a network unique character identifier into a network name.
189*1242Skas  */
190*1242Skas 
191*1242Skas char *
192*1242Skas netname(mid)
193*1242Skas {
194*1242Skas 	register struct netmach *np;
195*1242Skas 	char *mlook();
196*1242Skas 
197*1242Skas 	if (mid & 0200)
198*1242Skas 		return(mlook(mid));
199*1242Skas 	for (np = netmach; np->nt_mid != 0; np++)
200*1242Skas 		if (np->nt_mid == mid)
201*1242Skas 			return(np->nt_machine);
202*1242Skas 	return(NOSTR);
203*1242Skas }
204*1242Skas 
205*1242Skas /*
206*1242Skas  * Deal with arpa net addresses.  The way this is done is strange.
207*1242Skas  * In particular, if the destination arpa net host is not Berkeley,
208*1242Skas  * then the address is correct as stands.  Otherwise, we strip off
209*1242Skas  * the trailing @Berkeley, then cook up a phony person for it to
210*1242Skas  * be from and optimize the result.
211*1242Skas  */
212*1242Skas char *
213*1242Skas arpafix(name, from)
214*1242Skas 	char name[];
215*1242Skas 	char from[];
216*1242Skas {
217*1242Skas 	register char *cp;
218*1242Skas 	register int arpamach;
219*1242Skas 	char newname[BUFSIZ];
220*1242Skas 	char fake[5];
221*1242Skas 	char fakepath[20];
222*1242Skas 
223*1242Skas 	if (debug) {
224*1242Skas 		fprintf(stderr, "arpafix(%s, %s)\n", name, from);
225*1242Skas 	}
226*1242Skas 	cp = rindex(name, '@');
227*1242Skas 	if (cp == NOSTR)
228*1242Skas 		cp = rindex(name, '%');
229*1242Skas 	if (cp == NOSTR) {
230*1242Skas 		fprintf(stderr, "Somethings amiss -- no @ or % in arpafix\n");
231*1242Skas 		return(name);
232*1242Skas 	}
233*1242Skas 	cp++;
234*1242Skas 	arpamach = netlook(cp, '@');
235*1242Skas 	if (arpamach == 0) {
236*1242Skas 		if (debug)
237*1242Skas 			fprintf(stderr, "machine %s unknown, uses: %s\n", cp, name);
238*1242Skas 		return(name);
239*1242Skas 	}
240*1242Skas 	if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) {
241*1242Skas 		if (debug)
242*1242Skas 			fprintf(stderr, "machine %s known but remote, uses: %s\n",
243*1242Skas 			    cp, name);
244*1242Skas 		return(name);
245*1242Skas 	}
246*1242Skas 	strcpy(newname, name);
247*1242Skas 	cp = rindex(newname, '@');
248*1242Skas 	if (cp == NOSTR)
249*1242Skas 		cp = rindex(newname, '%');
250*1242Skas 	*cp = 0;
251*1242Skas 	fake[0] = arpamach;
252*1242Skas 	fake[1] = ':';
253*1242Skas 	fake[2] = LOCAL;
254*1242Skas 	fake[3] = ':';
255*1242Skas 	fake[4] = 0;
256*1242Skas 	prefer(fake);
257*1242Skas 	strcpy(fakepath, netname(fake[0]));
258*1242Skas 	stradd(fakepath, fake[1]);
259*1242Skas 	strcat(fakepath, "daemon");
260*1242Skas 	if (debug)
261*1242Skas 		fprintf(stderr, "machine local, call netmap(%s, %s)\n",
262*1242Skas 		    newname, fakepath);
263*1242Skas 	return(netmap(newname, fakepath));
264*1242Skas }
265*1242Skas 
266*1242Skas /*
267*1242Skas  * Take a network machine descriptor and find the types of connected
268*1242Skas  * nets and return it.
269*1242Skas  */
270*1242Skas 
271*1242Skas nettype(mid)
272*1242Skas {
273*1242Skas 	register struct netmach *np;
274*1242Skas 
275*1242Skas 	if (mid & 0200)
276*1242Skas 		return(mtype(mid));
277*1242Skas 	for (np = netmach; np->nt_mid != 0; np++)
278*1242Skas 		if (np->nt_mid == mid)
279*1242Skas 			return(np->nt_type);
280*1242Skas 	return(0);
281*1242Skas }
282*1242Skas 
283*1242Skas /*
284*1242Skas  * Hashing routines to salt away machines seen scanning
285*1242Skas  * networks paths that we don't know about.
286*1242Skas  */
287*1242Skas 
288*1242Skas #define	XHSIZE		19		/* Size of extra hash table */
289*1242Skas #define	NXMID		(XHSIZE*3/4)	/* Max extra machines */
290*1242Skas 
291*1242Skas struct xtrahash {
292*1242Skas 	char	*xh_name;		/* Name of machine */
293*1242Skas 	short	xh_mid;			/* Machine ID */
294*1242Skas 	short	xh_attnet;		/* Attached networks */
295*1242Skas } xtrahash[XHSIZE];
296*1242Skas 
297*1242Skas struct xtrahash	*xtab[XHSIZE];		/* F: mid-->machine name */
298*1242Skas 
299*1242Skas short	midfree;			/* Next free machine id */
300*1242Skas 
301*1242Skas /*
302*1242Skas  * Initialize the extra host hash table.
303*1242Skas  * Called by sreset.
304*1242Skas  */
305*1242Skas 
306*1242Skas minit()
307*1242Skas {
308*1242Skas 	register struct xtrahash *xp, **tp;
309*1242Skas 	register int i;
310*1242Skas 
311*1242Skas 	midfree = 0;
312*1242Skas 	tp = &xtab[0];
313*1242Skas 	for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) {
314*1242Skas 		xp->xh_name = NOSTR;
315*1242Skas 		xp->xh_mid = 0;
316*1242Skas 		xp->xh_attnet = 0;
317*1242Skas 		*tp++ = (struct xtrahash *) 0;
318*1242Skas 	}
319*1242Skas }
320*1242Skas 
321*1242Skas /*
322*1242Skas  * Stash a net name in the extra host hash table.
323*1242Skas  * If a new entry is put in the hash table, deduce what
324*1242Skas  * net the machine is attached to from the net character.
325*1242Skas  *
326*1242Skas  * If the machine is already known, add the given attached
327*1242Skas  * net to those already known.
328*1242Skas  */
329*1242Skas 
330*1242Skas mstash(name, attnet)
331*1242Skas 	char name[];
332*1242Skas {
333*1242Skas 	register struct xtrahash *xp;
334*1242Skas 	struct xtrahash *xlocate();
335*1242Skas 
336*1242Skas 	xp = xlocate(name);
337*1242Skas 	if (xp == (struct xtrahash *) 0) {
338*1242Skas 		printf("Ran out of machine id spots\n");
339*1242Skas 		return(0);
340*1242Skas 	}
341*1242Skas 	if (xp->xh_name == NOSTR) {
342*1242Skas 		if (midfree >= XHSIZE) {
343*1242Skas 			printf("Out of machine ids\n");
344*1242Skas 			return(0);
345*1242Skas 		}
346*1242Skas 		xtab[midfree] = xp;
347*1242Skas 		xp->xh_name = savestr(name);
348*1242Skas 		xp->xh_mid = 0200 + midfree++;
349*1242Skas 	}
350*1242Skas 	switch (attnet) {
351*1242Skas 	case '!':
352*1242Skas 	case '^':
353*1242Skas 		xp->xh_attnet |= BN;
354*1242Skas 		break;
355*1242Skas 
356*1242Skas 	default:
357*1242Skas 	case ':':
358*1242Skas 		xp->xh_attnet |= SN;
359*1242Skas 		break;
360*1242Skas 
361*1242Skas 	case '@':
362*1242Skas 	case '%':
363*1242Skas 		xp->xh_attnet |= AN;
364*1242Skas 		break;
365*1242Skas 	}
366*1242Skas 	return(xp->xh_mid);
367*1242Skas }
368*1242Skas 
369*1242Skas /*
370*1242Skas  * Search for the given name in the hash table
371*1242Skas  * and return the pointer to it if found, or to the first
372*1242Skas  * empty slot if not found.
373*1242Skas  *
374*1242Skas  * If no free slots can be found, return 0.
375*1242Skas  */
376*1242Skas 
377*1242Skas struct xtrahash *
378*1242Skas xlocate(name)
379*1242Skas 	char name[];
380*1242Skas {
381*1242Skas 	register int h, q, i;
382*1242Skas 	register char *cp;
383*1242Skas 	register struct xtrahash *xp;
384*1242Skas 
385*1242Skas 	for (h = 0, cp = name; *cp; h = (h << 2) + *cp++)
386*1242Skas 		;
387*1242Skas 	if (h < 0 && (h = -h) < 0)
388*1242Skas 		h = 0;
389*1242Skas 	h = h % XHSIZE;
390*1242Skas 	cp = name;
391*1242Skas 	for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) {
392*1242Skas 		xp = &xtrahash[(h + q) % XHSIZE];
393*1242Skas 		if (xp->xh_name == NOSTR)
394*1242Skas 			return(xp);
395*1242Skas 		if (strcmp(cp, xp->xh_name) == 0)
396*1242Skas 			return(xp);
397*1242Skas 		if (h - q < 0)
398*1242Skas 			q += XHSIZE;
399*1242Skas 		xp = &xtrahash[(h - q) % XHSIZE];
400*1242Skas 		if (xp->xh_name == NOSTR)
401*1242Skas 			return(xp);
402*1242Skas 		if (strcmp(cp, xp->xh_name) == 0)
403*1242Skas 			return(xp);
404*1242Skas 	}
405*1242Skas 	return((struct xtrahash *) 0);
406*1242Skas }
407*1242Skas 
408*1242Skas /*
409*1242Skas  * Return the name from the extra host hash table corresponding
410*1242Skas  * to the passed machine id.
411*1242Skas  */
412*1242Skas 
413*1242Skas char *
414*1242Skas mlook(mid)
415*1242Skas {
416*1242Skas 	register int m;
417*1242Skas 
418*1242Skas 	if ((mid & 0200) == 0)
419*1242Skas 		return(NOSTR);
420*1242Skas 	m = mid & 0177;
421*1242Skas 	if (m >= midfree) {
422*1242Skas 		printf("Use made of undefined machine id\n");
423*1242Skas 		return(NOSTR);
424*1242Skas 	}
425*1242Skas 	return(xtab[m]->xh_name);
426*1242Skas }
427*1242Skas 
428*1242Skas /*
429*1242Skas  * Return the bit mask of net's that the given extra host machine
430*1242Skas  * id has so far.
431*1242Skas  */
432*1242Skas 
433*1242Skas mtype(mid)
434*1242Skas {
435*1242Skas 	register int m;
436*1242Skas 
437*1242Skas 	if ((mid & 0200) == 0)
438*1242Skas 		return(0);
439*1242Skas 	m = mid & 0177;
440*1242Skas 	if (m >= midfree) {
441*1242Skas 		printf("Use made of undefined machine id\n");
442*1242Skas 		return(0);
443*1242Skas 	}
444*1242Skas 	return(xtab[m]->xh_attnet);
445*1242Skas }
446*1242Skas 
447*1242Skas /*
448*1242Skas  * Take a network name and optimize it.  This gloriously messy
449*1242Skas  * opertions takes place as follows:  the name with machine names
450*1242Skas  * in it is tokenized by mapping each machine name into a single
451*1242Skas  * character machine id (netlook).  The separator characters (network
452*1242Skas  * metacharacters) are left intact.  The last component of the network
453*1242Skas  * name is stripped off and assumed to be the destination user name --
454*1242Skas  * it does not participate in the optimization.  As an example, the
455*1242Skas  * name "research!vax135!research!ucbvax!bill" becomes, tokenized,
456*1242Skas  * "r!x!r!v!" and "bill"  A low level routine, optim1, fixes up the
457*1242Skas  * network part (eg, "r!x!r!v!"), then we convert back to network
458*1242Skas  * machine names and tack the user name on the end.
459*1242Skas  *
460*1242Skas  * The result of this is copied into the parameter "name"
461*1242Skas  */
462*1242Skas 
463*1242Skas optim(net, name)
464*1242Skas 	char net[], name[];
465*1242Skas {
466*1242Skas 	char netcomp[BUFSIZ], netstr[40], xfstr[40];
467*1242Skas 	register char *cp, *cp2;
468*1242Skas 	register int c;
469*1242Skas 
470*1242Skas 	strcpy(netstr, "");
471*1242Skas 	cp = net;
472*1242Skas 	for (;;) {
473*1242Skas 		/*
474*1242Skas 		 * Rip off next path component into netcomp
475*1242Skas 		 */
476*1242Skas 		cp2 = netcomp;
477*1242Skas 		while (*cp && !any(*cp, metanet))
478*1242Skas 			*cp2++ = *cp++;
479*1242Skas 		*cp2 = 0;
480*1242Skas 		/*
481*1242Skas 		 * If we hit null byte, then we just scanned
482*1242Skas 		 * the destination user name.  Go off and optimize
483*1242Skas 		 * if its so.
484*1242Skas 		 */
485*1242Skas 		if (*cp == 0)
486*1242Skas 			break;
487*1242Skas 		if ((c = netlook(netcomp, *cp)) == 0) {
488*1242Skas 			printf("No host named \"%s\"\n", netcomp);
489*1242Skas err:
490*1242Skas 			strcpy(name, net);
491*1242Skas 			return;
492*1242Skas 		}
493*1242Skas 		stradd(netstr, c);
494*1242Skas 		stradd(netstr, *cp++);
495*1242Skas 		/*
496*1242Skas 		 * If multiple network separators given,
497*1242Skas 		 * throw away the extras.
498*1242Skas 		 */
499*1242Skas 		while (any(*cp, metanet))
500*1242Skas 			cp++;
501*1242Skas 	}
502*1242Skas 	if (strlen(netcomp) == 0) {
503*1242Skas 		printf("net name syntax\n");
504*1242Skas 		goto err;
505*1242Skas 	}
506*1242Skas 	optim1(netstr, xfstr);
507*1242Skas 
508*1242Skas 	/*
509*1242Skas 	 * Convert back to machine names.
510*1242Skas 	 */
511*1242Skas 
512*1242Skas 	cp = xfstr;
513*1242Skas 	strcpy(name, "");
514*1242Skas 	while (*cp) {
515*1242Skas 		if ((cp2 = netname(*cp++)) == NOSTR) {
516*1242Skas 			printf("Made up bad net name\n");
517*1242Skas 			goto err;
518*1242Skas 		}
519*1242Skas 		strcat(name, cp2);
520*1242Skas 		stradd(name, *cp++);
521*1242Skas 	}
522*1242Skas 	strcat(name, netcomp);
523*1242Skas }
524*1242Skas 
525*1242Skas /*
526*1242Skas  * Take a string of network machine id's and separators and
527*1242Skas  * optimize them.  We process these by pulling off maximal
528*1242Skas  * leading strings of the same type, passing these to the appropriate
529*1242Skas  * optimizer and concatenating the results.
530*1242Skas  */
531*1242Skas 
532*1242Skas #define	IMPLICIT	1
533*1242Skas #define	EXPLICIT	2
534*1242Skas 
535*1242Skas optim1(netstr, name)
536*1242Skas 	char netstr[], name[];
537*1242Skas {
538*1242Skas 	char path[40], rpath[40];
539*1242Skas 	register char *cp, *cp2;
540*1242Skas 	register int tp, nc;
541*1242Skas 
542*1242Skas 	cp = netstr;
543*1242Skas 	prefer(cp);
544*1242Skas 	strcpy(name, "");
545*1242Skas 	while (*cp != 0) {
546*1242Skas 		strcpy(path, "");
547*1242Skas 		tp = ntype(cp[1]);
548*1242Skas 		nc = cp[1];
549*1242Skas 		while (*cp && tp == ntype(cp[1])) {
550*1242Skas 			stradd(path, *cp++);
551*1242Skas 			cp++;
552*1242Skas 		}
553*1242Skas 		switch (netkind(tp)) {
554*1242Skas 		default:
555*1242Skas 			strcpy(rpath, path);
556*1242Skas 			break;
557*1242Skas 
558*1242Skas 		case IMPLICIT:
559*1242Skas 			optimimp(path, rpath);
560*1242Skas 			break;
561*1242Skas 
562*1242Skas 		case EXPLICIT:
563*1242Skas 			optimex(path, rpath);
564*1242Skas 			break;
565*1242Skas 		}
566*1242Skas 		for (cp2 = rpath; *cp2 != 0; cp2++) {
567*1242Skas 			stradd(name, *cp2);
568*1242Skas 			stradd(name, nc);
569*1242Skas 		}
570*1242Skas 	}
571*1242Skas 	optiboth(name);
572*1242Skas 	prefer(name);
573*1242Skas }
574*1242Skas 
575*1242Skas /*
576*1242Skas  * Return the network of the separator --
577*1242Skas  *	AN for arpa net
578*1242Skas  *	BN for Bell labs net
579*1242Skas  *	SN for Schmidt (berkeley net)
580*1242Skas  *	0 if we don't know.
581*1242Skas  */
582*1242Skas 
583*1242Skas ntype(nc)
584*1242Skas 	register int nc;
585*1242Skas {
586*1242Skas 
587*1242Skas 	switch (nc) {
588*1242Skas 	case '^':
589*1242Skas 	case '!':
590*1242Skas 		return(BN);
591*1242Skas 
592*1242Skas 	case ':':
593*1242Skas 	case '.':
594*1242Skas 		return(SN);
595*1242Skas 
596*1242Skas 	case '@':
597*1242Skas 	case '%':
598*1242Skas 		return(AN);
599*1242Skas 
600*1242Skas 	default:
601*1242Skas 		return(0);
602*1242Skas 	}
603*1242Skas 	/* NOTREACHED */
604*1242Skas }
605*1242Skas 
606*1242Skas /*
607*1242Skas  * Return the kind of routing used for the particular net
608*1242Skas  * EXPLICIT means explicitly routed
609*1242Skas  * IMPLICIT means implicitly routed
610*1242Skas  * 0 means don't know
611*1242Skas  */
612*1242Skas 
613*1242Skas netkind(nt)
614*1242Skas 	register int nt;
615*1242Skas {
616*1242Skas 
617*1242Skas 	switch (nt) {
618*1242Skas 	case BN:
619*1242Skas 		return(EXPLICIT);
620*1242Skas 
621*1242Skas 	case AN:
622*1242Skas 	case SN:
623*1242Skas 		return(IMPLICIT);
624*1242Skas 
625*1242Skas 	default:
626*1242Skas 		return(0);
627*1242Skas 	}
628*1242Skas 	/* NOTREACHED */
629*1242Skas }
630*1242Skas 
631*1242Skas /*
632*1242Skas  * Do name optimization for an explicitly routed network (eg BTL network).
633*1242Skas  */
634*1242Skas 
635*1242Skas optimex(net, name)
636*1242Skas 	char net[], name[];
637*1242Skas {
638*1242Skas 	register char *cp, *rp;
639*1242Skas 	register int m;
640*1242Skas 	char *rindex();
641*1242Skas 
642*1242Skas 	strcpy(name, net);
643*1242Skas 	cp = name;
644*1242Skas 	if (strlen(cp) == 0)
645*1242Skas 		return(-1);
646*1242Skas 	if (cp[strlen(cp)-1] == LOCAL) {
647*1242Skas 		name[0] = 0;
648*1242Skas 		return(0);
649*1242Skas 	}
650*1242Skas 	for (cp = name; *cp; cp++) {
651*1242Skas 		m = *cp;
652*1242Skas 		rp = rindex(cp+1, m);
653*1242Skas 		if (rp != NOSTR)
654*1242Skas 			strcpy(cp, rp);
655*1242Skas 	}
656*1242Skas 	return(0);
657*1242Skas }
658*1242Skas 
659*1242Skas /*
660*1242Skas  * Do name optimization for implicitly routed network (eg, arpanet,
661*1242Skas  * Berkeley network)
662*1242Skas  */
663*1242Skas 
664*1242Skas optimimp(net, name)
665*1242Skas 	char net[], name[];
666*1242Skas {
667*1242Skas 	register char *cp;
668*1242Skas 	register int m;
669*1242Skas 
670*1242Skas 	cp = net;
671*1242Skas 	if (strlen(cp) == 0)
672*1242Skas 		return(-1);
673*1242Skas 	m = cp[strlen(cp) - 1];
674*1242Skas 	if (m == LOCAL) {
675*1242Skas 		strcpy(name, "");
676*1242Skas 		return(0);
677*1242Skas 	}
678*1242Skas 	name[0] = m;
679*1242Skas 	name[1] = 0;
680*1242Skas 	return(0);
681*1242Skas }
682*1242Skas 
683*1242Skas /*
684*1242Skas 
685*1242Skas  * Perform global optimization on the given network path.
686*1242Skas  * The trick here is to look ahead to see if there are any loops
687*1242Skas  * in the path and remove them.  The interpretation of loops is
688*1242Skas  * more strict here than in optimex since both the machine and net
689*1242Skas  * type must match.
690*1242Skas  */
691*1242Skas 
692*1242Skas optiboth(net)
693*1242Skas 	char net[];
694*1242Skas {
695*1242Skas 	register char *cp, *cp2;
696*1242Skas 	char *rpair();
697*1242Skas 
698*1242Skas 	cp = net;
699*1242Skas 	if (strlen(cp) == 0)
700*1242Skas 		return;
701*1242Skas 	if ((strlen(cp) % 2) != 0) {
702*1242Skas 		printf("Strange arg to optiboth\n");
703*1242Skas 		return;
704*1242Skas 	}
705*1242Skas 	while (*cp) {
706*1242Skas 		cp2 = rpair(cp+2, *cp);
707*1242Skas 		if (cp2 != NOSTR)
708*1242Skas 			strcpy(cp, cp2);
709*1242Skas 		cp += 2;
710*1242Skas 	}
711*1242Skas }
712*1242Skas 
713*1242Skas /*
714*1242Skas  * Find the rightmost instance of the given (machine, type) pair.
715*1242Skas  */
716*1242Skas 
717*1242Skas char *
718*1242Skas rpair(str, mach)
719*1242Skas 	char str[];
720*1242Skas {
721*1242Skas 	register char *cp, *last;
722*1242Skas 
723*1242Skas 	last = NOSTR;
724*1242Skas 	while (*cp) {
725*1242Skas 		if (*cp == mach)
726*1242Skas 			last = cp;
727*1242Skas 		cp += 2;
728*1242Skas 	}
729*1242Skas 	return(last);
730*1242Skas }
731*1242Skas 
732*1242Skas /*
733*1242Skas  * Change the network separators in the given network path
734*1242Skas  * to the preferred network transmission means.
735*1242Skas  */
736*1242Skas 
737*1242Skas prefer(name)
738*1242Skas 	char name[];
739*1242Skas {
740*1242Skas 	register char *cp;
741*1242Skas 	register int state, n;
742*1242Skas 
743*1242Skas 	state = LOCAL;
744*1242Skas 	for (cp = name; *cp; cp += 2) {
745*1242Skas 		n = best(state, *cp);
746*1242Skas 		if (n)
747*1242Skas 			cp[1] = n;
748*1242Skas 		state = *cp;
749*1242Skas 	}
750*1242Skas }
751*1242Skas 
752*1242Skas /*
753*1242Skas  * Return the best network separator for the given machine pair.
754*1242Skas  */
755*1242Skas 
756*1242Skas struct netorder {
757*1242Skas 	short	no_stat;
758*1242Skas 	char	no_char;
759*1242Skas } netorder[] = {
760*1242Skas 	CN,	':',
761*1242Skas 	AN,	'@',
762*1242Skas 	AN,	'%',
763*1242Skas 	SN,	':',
764*1242Skas 	BN,	'!',
765*1242Skas 	-1,	0
766*1242Skas };
767*1242Skas 
768*1242Skas best(src, dest)
769*1242Skas {
770*1242Skas 	register int dtype, stype;
771*1242Skas 	register struct netorder *np;
772*1242Skas 
773*1242Skas 	stype = nettype(src);
774*1242Skas 	dtype = nettype(dest);
775*1242Skas 	if (stype == 0 || dtype == 0) {
776*1242Skas 		printf("ERROR:  unknown internal machine id\n");
777*1242Skas 		return(0);
778*1242Skas 	}
779*1242Skas 	if ((stype & dtype) == 0) {
780*1242Skas #ifdef DELIVERMAIL
781*1242Skas 		if (src != LOCAL)
782*1242Skas #endif
783*1242Skas 			printf("No way to get from \"%s\" to \"%s\"\n",
784*1242Skas 			    netname(src), netname(dest));
785*1242Skas 		return(0);
786*1242Skas 	}
787*1242Skas 	np = &netorder[0];
788*1242Skas 	while ((np->no_stat & stype & dtype) == 0)
789*1242Skas 		np++;
790*1242Skas 	return(np->no_char);
791*1242Skas }
792*1242Skas 
793*1242Skas /*
794*1242Skas  * Code to twist around arpa net names.
795*1242Skas  */
796*1242Skas 
797*1242Skas #define WORD 257			/* Token for a string */
798*1242Skas 
799*1242Skas static	char netbuf[256];
800*1242Skas static	char *yylval;
801*1242Skas 
802*1242Skas /*
803*1242Skas  * Reverse all of the arpa net addresses in the given name to
804*1242Skas  * be of the form "host @ user" instead of "user @ host"
805*1242Skas  * This function is its own inverse.
806*1242Skas  */
807*1242Skas 
808*1242Skas char *
809*1242Skas revarpa(str)
810*1242Skas 	char str[];
811*1242Skas {
812*1242Skas 
813*1242Skas 	if (yyinit(str) < 0)
814*1242Skas 		return(NOSTR);
815*1242Skas 	if (name())
816*1242Skas 		return(NOSTR);
817*1242Skas 	if (strcmp(str, netbuf) == 0)
818*1242Skas 		return(str);
819*1242Skas 	return(savestr(netbuf));
820*1242Skas }
821*1242Skas 
822*1242Skas /*
823*1242Skas  * Parse (by recursive descent) network names, using the following grammar:
824*1242Skas  *	name:
825*1242Skas  *		term {':' term}
826*1242Skas  *		term {'^' term}
827*1242Skas  *		term {'!' term}
828*1242Skas  *		term '@' name
829*1242Skas  *		term '%' name
830*1242Skas  *
831*1242Skas  *	term:
832*1242Skas  *		string of characters.
833*1242Skas  */
834*1242Skas 
835*1242Skas name()
836*1242Skas {
837*1242Skas 	register int t;
838*1242Skas 	register char *cp;
839*1242Skas 
840*1242Skas 	for (;;) {
841*1242Skas 		t = yylex();
842*1242Skas 		if (t != WORD)
843*1242Skas 			return(-1);
844*1242Skas 		cp = yylval;
845*1242Skas 		t = yylex();
846*1242Skas 		switch (t) {
847*1242Skas 		case 0:
848*1242Skas 			strcat(netbuf, cp);
849*1242Skas 			return(0);
850*1242Skas 
851*1242Skas 		case '@':
852*1242Skas 		case '%':
853*1242Skas 			if (name())
854*1242Skas 				return(-1);
855*1242Skas 			stradd(netbuf, '@');
856*1242Skas 			strcat(netbuf, cp);
857*1242Skas 			return(0);
858*1242Skas 
859*1242Skas 		case WORD:
860*1242Skas 			return(-1);
861*1242Skas 
862*1242Skas 		default:
863*1242Skas 			strcat(netbuf, cp);
864*1242Skas 			stradd(netbuf, t);
865*1242Skas 		}
866*1242Skas 	}
867*1242Skas }
868*1242Skas 
869*1242Skas /*
870*1242Skas  * Scanner for network names.
871*1242Skas  */
872*1242Skas 
873*1242Skas static	char *charp;			/* Current input pointer */
874*1242Skas static	int nexttok;			/* Salted away next token */
875*1242Skas 
876*1242Skas /*
877*1242Skas  * Initialize the network name scanner.
878*1242Skas  */
879*1242Skas 
880*1242Skas yyinit(str)
881*1242Skas 	char str[];
882*1242Skas {
883*1242Skas 	static char lexbuf[BUFSIZ];
884*1242Skas 
885*1242Skas 	netbuf[0] = 0;
886*1242Skas 	if (strlen(str) >= sizeof lexbuf - 1)
887*1242Skas 		return(-1);
888*1242Skas 	nexttok = 0;
889*1242Skas 	strcpy(lexbuf, str);
890*1242Skas 	charp = lexbuf;
891*1242Skas 	return(0);
892*1242Skas }
893*1242Skas 
894*1242Skas /*
895*1242Skas  * Scan and return a single token.
896*1242Skas  * yylval is set to point to a scanned string.
897*1242Skas  */
898*1242Skas 
899*1242Skas yylex()
900*1242Skas {
901*1242Skas 	register char *cp, *dot;
902*1242Skas 	register int s;
903*1242Skas 
904*1242Skas 	if (nexttok) {
905*1242Skas 		s = nexttok;
906*1242Skas 		nexttok = 0;
907*1242Skas 		return(s);
908*1242Skas 	}
909*1242Skas 	cp = charp;
910*1242Skas 	while (*cp && isspace(*cp))
911*1242Skas 		cp++;
912*1242Skas 	if (*cp == 0)
913*1242Skas 		return(0);
914*1242Skas 	if (any(*cp, "!^@:%")) {
915*1242Skas 		charp = cp+1;
916*1242Skas 		return(*cp);
917*1242Skas 	}
918*1242Skas 	dot = cp;
919*1242Skas 	while (*cp && !any(*cp, " \t!^@:%"))
920*1242Skas 		cp++;
921*1242Skas 	if (any(*cp, "!^@:%"))
922*1242Skas 		nexttok = *cp;
923*1242Skas 	if (*cp == 0)
924*1242Skas 		charp = cp;
925*1242Skas 	else
926*1242Skas 		charp = cp+1;
927*1242Skas 	*cp = 0;
928*1242Skas 	yylval = dot;
929*1242Skas 	return(WORD);
930*1242Skas }
931*1242Skas 
932*1242Skas /*
933*1242Skas  * Add a single character onto a string.
934*1242Skas  */
935*1242Skas 
936*1242Skas stradd(str, c)
937*1242Skas 	register char *str;
938*1242Skas 	register int c;
939*1242Skas {
940*1242Skas 
941*1242Skas 	str += strlen(str);
942*1242Skas 	*str++ = c;
943*1242Skas 	*str = 0;
944*1242Skas }
945