xref: /illumos-gate/usr/src/cmd/mailx/optim.c (revision 8cd45542f2a452ca0dab13d8b2d5cfa876ccbebc)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1998 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  * mailx -- a modified version of a University of California at Berkeley
35  *	mail program
36  *
37  * Network name modification routines.
38  */
39 
40 #include "rcv.h"
41 #include "configdefs.h"
42 #include <locale.h>
43 
44 static char		*arpafix(char name[], char from[]);
45 static char		*lasthost(char *addr);
46 static char		*makeremote(char name[], char from[]);
47 static int		mstash(char name[], int attnet);
48 static int		mtype(int mid);
49 static int		netlook(char machine[], int attnet);
50 static int		nettype(int mid);
51 static int		ntype(register int nc);
52 static void		stradd(register char *str, int n, register int c);
53 static char		*tackon(char *sys, char *rest);
54 static struct xtrahash	*xlocate(char name[]);
55 #ifdef OPTIM
56 static char		best(int src, int dest);
57 static char		*mlook(int mid);
58 static int		netkind(register int nt);
59 static void		optiboth(char net[]);
60 static void		optim(char net[], char name[]);
61 static void		optim1(char netstr[], char name[]);
62 static int		optimex(char net[], char name[]);
63 static int		optimimp(char net[], char name[]);
64 static void		prefer(char name[]);
65 static char		*rpair(char str[], int mach);
66 #endif
67 
68 /*
69  * Map a name into the correct network "view" of the
70  * name.  This is done by prepending the name with the
71  * network address of the sender, then optimizing away
72  * nonsense.
73  */
74 
75 char *
76 netmap(char name[], char from[])
77 {
78 	char nbuf[BUFSIZ], ret[BUFSIZ];
79 	register char *cp, *oname;
80 
81 	if (debug) fprintf(stderr, "netmap(name '%s', from '%s')\n", name, from);
82 	if (strlen(from) == 0)
83 		return(name);	/* "from" is empty - can't do anything */
84 
85 	if (strcmp(from, name) == 0)
86 		return(name);	/* "from" and "name" are the same, do nothing */
87 
88 	/*
89 	 * If the name contains an "@" or a "%", remove it and the host
90 	 * following it if that host is "known".
91 	 */
92 	if (any('@', name) || any('%', name))
93 		return(arpafix(name, from));
94 
95 	/*
96 	 * If the sender contains a "@" or a "%", make "name" into an
97 	 * address on that host, on the presumption that it should
98 	 * really have read "name@from" when we received the message
99 	 * rather than just "name".
100 	 */
101 	if (any('@', from) || any('%', from))
102 		return(unuucp(makeremote(name, from)));
103 	if (value("onehop") && (cp = strchr(name, '!')) && cp > name) {
104 		/*
105 		 * "onehop" is set, meaning all machines are one UUCP
106 		 * hop away (fat chance, in this day and age), and "name"
107 		 * is a UUCP path rather than just a name.  Leave it alone.
108 		 */
109 		nstrcpy(nbuf, sizeof (nbuf), name);
110 	} else {
111 		from = tackon(host, from);
112 		*strrchr(from, '!') = 0;
113 		name = tackon(lasthost(from), name);
114 		while (((cp = lasthost(from)) != 0) && ishost(cp, name)) {
115 			oname = name;
116 			name = strchr(name, '!') + 1;
117 			if (cp == from) {
118 				from[strlen(from)] = '!';
119 				if (value("mustbang") && !strchr(name, '!'))
120 					name = oname;
121 				return(unuucp(name));
122 			}
123 			*--cp = 0;
124 		}
125 		from[strlen(from)] = '!';
126 		from = strchr(from, '!') + 1;
127 		snprintf(nbuf, sizeof (nbuf), "%s!%s", from, name);
128 	}
129 	if (debug) fprintf(stderr, "before optim, nbuf '%s'\n", name);
130 #ifdef	OPTIM
131 	if ((cp = value("conv"))==NOSTR || strcmp(cp, "optimize") != 0)
132 		nstrcpy(ret, sizeof (ret), nbuf);
133 	else
134 		optim(nbuf, ret);
135 #else
136 	nstrcpy(ret, sizeof (ret), nbuf);
137 #endif	/* OPTIM */
138 	if (debug) fprintf(stderr, "after  optim, nbuf '%s', ret '%s'\n", nbuf, ret);
139 	cp = ret;
140 	if (debug) fprintf(stderr, "wind up with '%s'\n", name);
141 	if (!icequal(name, cp))
142 		return(unuucp((char *) savestr(cp)));
143 	return(unuucp(name));
144 }
145 
146 /*
147  * Stick a host on the beginning of a uucp
148  * address if it isn't there already.
149  */
150 static char *
151 tackon(char *sys, char *rest)
152 {
153 	while (*rest == '!')
154 		rest++;
155 	if (!ishost(sys, rest)) {
156 		char *r = (char *)salloc(strlen(sys) + strlen(rest) + 2);
157 		sprintf(r, "%s!%s", sys, rest);
158 		rest = r;
159 	}
160 	return rest;
161 }
162 
163 /*
164  * Check equality of the first host in a uucp address.
165  */
166 int
167 ishost(char *sys, char *rest)
168 {
169 	while (*sys && *sys == *rest)
170 		sys++, rest++;
171 	return(*sys == 0 && *rest == '!');
172 }
173 
174 /*
175  * Return last host in a uucp address.
176  */
177 static char *
178 lasthost(char *addr)
179 {
180 	char *r = strrchr(addr, '!');
181 	return r ? ++r : addr;
182 }
183 
184 /*
185  * Optionally translate an old format uucp name into a new one, e.g.
186  * "mach1!mach2!user" becomes "user@mach2.UUCP".  This optional because
187  * some information is necessarily lost (e.g. the route it got here
188  * via) and if we don't have the host in our routing tables, we lose.
189  * XXX THIS IS NO LONGER VALID WITH THE NEW UUCP PROJECT PLANS TO
190  * REGISTER UUCP HOSTS IN THE STANDARD INTERNET NAMESPACE, E.G.
191  * ihnp4 BECOMES "ihnp4.att.com".
192  */
193 char *
194 unuucp(char *name)
195 {
196 	register char *np, *hp, *cp;
197 	char result[100];
198 	char tname[300];
199 
200 	if (UnUUCP==0 &&
201 	    ((cp = value("conv"))==NOSTR || strcmp(cp, "internet")))
202 		return name;
203 	if (debug) fprintf(stderr, "unuucp(%s)\n", name);
204 	nstrcpy(tname, sizeof (tname), name);
205 	np = strrchr(tname, '!');
206 	if (np == NOSTR)
207 		return name;
208 	*np++ = 0;
209 	hp = strrchr(tname, '!');
210 	if (hp == NOSTR)
211 		hp = tname;
212 	else
213 		*hp++ = 0;
214 	cp = strchr(np, '@');
215 	if (cp == NOSTR)
216 		cp = strchr(np, '%');
217 	if (cp)
218 		*cp = 0;
219 	if (debug) fprintf(stderr, "host %s, name %s\n", hp, np);
220 	snprintf(result, sizeof (result), "%s@%s.UUCP", np, hp);
221 	if (debug) fprintf(stderr, "unuucp returns %s\n", result);
222 	return savestr(result);
223 }
224 
225 /*
226  * Turn a network machine name into a unique character
227  */
228 static int
229 netlook(char machine[], int attnet)
230 {
231 	register struct netmach *np;
232 	register char *cp, *cp2;
233 	char nbuf[BUFSIZ];
234 
235 	/*
236 	 * Make into lower case.
237 	 */
238 	for (cp = machine, cp2 = nbuf;
239 	     *cp && cp2 < &nbuf[BUFSIZ-1];
240 	     *cp2++ = tolower(*cp++))
241 		/*nothing*/;
242 	*cp2 = 0;
243 
244 	/*
245 	 * If a single letter machine, look through those first.
246 	 */
247 
248 	if (strlen(nbuf) == 1)
249 		for (np = netmach; np->nt_mid != 0; np++)
250 			if (np->nt_mid == nbuf[0])
251 				return(nbuf[0]);
252 
253 	/*
254 	 * Look for usual name
255 	 */
256 
257 	for (np = netmach; np->nt_mid != 0; np++)
258 		if (strcmp(np->nt_machine, nbuf) == 0)
259 			return(np->nt_mid);
260 
261 	/*
262 	 * Look in side hash table.
263 	 */
264 
265 	return(mstash(nbuf, attnet));
266 }
267 
268 #ifdef OPTIM
269 /*
270  * Turn a network unique character identifier into a network name.
271  */
272 
273 static char *
274 netname(int mid)
275 {
276 	register struct netmach *np;
277 
278 	if (mid & 0200)
279 		return(mlook(mid));
280 	for (np = netmach; np->nt_mid != 0; np++)
281 		if (np->nt_mid == mid)
282 			return(np->nt_machine);
283 	return(NOSTR);
284 }
285 #endif
286 
287 /*
288  * Deal with arpa net addresses.  The way this is done is strange.
289  * name contains an "@" or "%".  Look up the machine after it in
290  * the hash table.  If it isn't found, return name unmolested.
291  * If ???, return name unmolested.
292  * Otherwise, delete the "@" or "%" and the machine after it from
293  * name, and return the new string.
294  */
295 static char *
296 arpafix(char name[], char from[])
297 {
298 	register char *cp;
299 	register int arpamach;
300 	char newname[BUFSIZ];
301 
302 	if (debug) {
303 		fprintf(stderr, "arpafix(%s, %s)\n", name, from);
304 	}
305 	cp = strrchr(name, '@');
306 	if (cp == NOSTR)
307 		cp = strrchr(name, '%');
308 	if (cp == NOSTR) {
309 		fprintf(stderr,
310 		    gettext("Something's amiss -- no @ or %% in arpafix\n"));
311 		return(name);
312 	}
313 	cp++;
314 	arpamach = netlook(cp, '@');
315 	if (debug)
316 		fprintf(stderr,
317 		    "cp '%s', arpamach %o, nettypes arpamach %o LOCAL %o\n",
318 		    cp, arpamach, nettype(arpamach), nettype(LOCAL));
319 	if (arpamach == 0) {
320 		if (debug)
321 			fprintf(stderr, "machine %s unknown, uses: %s\n",
322 			    cp, name);
323 		return(name);
324 	}
325 	if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) {
326 		if (debug)
327 			fprintf(stderr, "machine %s known but remote, uses: %s\n",
328 			    cp, name);
329 		return(name);
330 	}
331 	nstrcpy(newname, sizeof (newname), name);
332 	cp = strrchr(newname, '@');
333 	if (cp == NOSTR)
334 		cp = strrchr(newname, '%');
335 	*cp = 0;
336 	if (debug) fprintf(stderr, "local address, return '%s'\n", newname);
337 	return(savestr(newname));
338 }
339 
340 /*
341  * We have name with no @'s in it, and from with @'s.
342  * Assume that name is meaningful only on the site in from,
343  * and return "name@site_in_from".
344  */
345 static char *
346 makeremote(char name[], char from[])
347 {
348 	register char *cp;
349 	char rbuf[BUFSIZ];
350 
351 	if (!value("makeremote"))
352 		return(name);
353 	if (debug) fprintf(stderr, "makeremote(%s, %s) returns ", name, from);
354 	cp = strrchr(from, '@');
355 	if (cp == NOSTR)
356 		cp = strrchr(from, '%');
357 	snprintf(rbuf, sizeof (rbuf), "%s%s", name, cp);
358 	if (debug) fprintf(stderr, "%s\n", rbuf);
359 	return(savestr(rbuf));
360 }
361 
362 /*
363  * Take a network machine descriptor and find the types of connected
364  * nets and return it.
365  */
366 static int
367 nettype(int mid)
368 {
369 	register struct netmach *np;
370 
371 	if (mid & 0200)
372 		return(mtype(mid));
373 	for (np = netmach; np->nt_mid != 0; np++)
374 		if (np->nt_mid == mid)
375 			return(np->nt_type);
376 	return(0);
377 }
378 
379 /*
380  * Hashing routines to salt away machines seen scanning
381  * networks paths that we don't know about.
382  */
383 
384 #define	XHSIZE		97		/* Size of extra hash table */
385 #define	NXMID		(XHSIZE*3/4)	/* Max extra machines */
386 
387 struct xtrahash {
388 	char	*xh_name;		/* Name of machine */
389 	short	xh_mid;			/* Machine ID */
390 	short	xh_attnet;		/* Attached networks */
391 } xtrahash[XHSIZE];
392 
393 static struct xtrahash	*xtab[XHSIZE];		/* F: mid-->machine name */
394 
395 static short	midfree;			/* Next free machine id */
396 
397 /*
398  * Initialize the extra host hash table.
399  * Called by sreset.
400  */
401 void
402 minit(void)
403 {
404 	register struct xtrahash *xp, **tp;
405 
406 	midfree = 0;
407 	tp = &xtab[0];
408 	for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) {
409 		xp->xh_name = NOSTR;
410 		xp->xh_mid = 0;
411 		xp->xh_attnet = 0;
412 		*tp++ = (struct xtrahash *) 0;
413 	}
414 }
415 
416 /*
417  * Stash a net name in the extra host hash table.
418  * If a new entry is put in the hash table, deduce what
419  * net the machine is attached to from the net character.
420  *
421  * If the machine is already known, add the given attached
422  * net to those already known.
423  */
424 static int
425 mstash(char name[], int attnet)
426 {
427 	register struct xtrahash *xp;
428 	int x;
429 
430 	xp = xlocate(name);
431 	if (xp == (struct xtrahash *) 0) {
432 		printf(gettext("Ran out of machine id spots\n"));
433 		return(0);
434 	}
435 	if (xp->xh_name == NOSTR) {
436 		if (midfree >= XHSIZE) {
437 			printf(gettext("Out of machine ids\n"));
438 			return(0);
439 		}
440 		xtab[midfree] = xp;
441 		xp->xh_name = savestr(name);
442 		xp->xh_mid = 0200 + midfree++;
443 	}
444 	x = ntype(attnet);
445 	if (x == 0)
446 		xp->xh_attnet |= AN;
447 	else
448 		xp->xh_attnet |= x;
449 	return(xp->xh_mid);
450 }
451 
452 /*
453  * Search for the given name in the hash table
454  * and return the pointer to it if found, or to the first
455  * empty slot if not found.
456  *
457  * If no free slots can be found, return 0.
458  */
459 
460 static struct xtrahash *
461 xlocate(char name[])
462 {
463 	register int h, q, i;
464 	register char *cp;
465 	register struct xtrahash *xp;
466 
467 	for (h = 0, cp = name; *cp; h = (h << 2) + *cp++)
468 		;
469 	if (h < 0 && (h = -h) < 0)
470 		h = 0;
471 	h = h % XHSIZE;
472 	cp = name;
473 	for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) {
474 		xp = &xtrahash[(h + q) % XHSIZE];
475 		if (xp->xh_name == NOSTR)
476 			return(xp);
477 		if (strcmp(cp, xp->xh_name) == 0)
478 			return(xp);
479 		if (h - q < 0)
480 			h += XHSIZE;
481 		xp = &xtrahash[(h - q) % XHSIZE];
482 		if (xp->xh_name == NOSTR)
483 			return(xp);
484 		if (strcmp(cp, xp->xh_name) == 0)
485 			return(xp);
486 	}
487 	return((struct xtrahash *) 0);
488 }
489 
490 #ifdef OPTIM
491 /*
492  * Return the name from the extra host hash table corresponding
493  * to the passed machine id.
494  */
495 
496 static char *
497 mlook(int mid)
498 {
499 	register int m;
500 
501 	if ((mid & 0200) == 0)
502 		return(NOSTR);
503 	m = mid & 0177;
504 	if (m >= midfree) {
505 		printf(gettext("Use made of undefined machine id\n"));
506 		return(NOSTR);
507 	}
508 	return(xtab[m]->xh_name);
509 }
510 #endif
511 
512 /*
513  * Return the bit mask of net's that the given extra host machine
514  * id has so far.
515  */
516 static int
517 mtype(int mid)
518 {
519 	register int m;
520 
521 	if ((mid & 0200) == 0)
522 		return(0);
523 	m = mid & 0177;
524 	if (m >= midfree) {
525 		printf(gettext("Use made of undefined machine id\n"));
526 		return(0);
527 	}
528 	return(xtab[m]->xh_attnet);
529 }
530 
531 #ifdef	OPTIM
532 /*
533  * Take a network name and optimize it.  This gloriously messy
534  * operation takes place as follows:  the name with machine names
535  * in it is tokenized by mapping each machine name into a single
536  * character machine id (netlook).  The separator characters (network
537  * metacharacters) are left intact.  The last component of the network
538  * name is stripped off and assumed to be the destination user name --
539  * it does not participate in the optimization.  As an example, the
540  * name "res!vax!res!uvax!bill" becomes, tokenized,
541  * "r!x!r!v!" and "bill"  A low level routine, optim1, fixes up the
542  * network part (eg, "r!x!r!v!"), then we convert back to network
543  * machine names and tack the user name on the end.
544  *
545  * The result of this is copied into the parameter "name"
546  */
547 
548 static void
549 optim(char net[], char name[])
550 {
551 	char netcomp[BUFSIZ], netstr[STSIZ], xfstr[STSIZ];
552 	register char *cp, *cp2;
553 	register int c;
554 
555 	if (debug) fprintf(stderr, "optim(%s, %s) called\n", net, name);
556 	*netstr = '\0';
557 	cp = net;
558 	for (;;) {
559 		/*
560 		 * Rip off next path component into netcomp
561 		 */
562 		cp2 = netcomp;
563 		while (*cp && !any(*cp, metanet))
564 			*cp2++ = *cp++;
565 		*cp2 = 0;
566 		/*
567 		 * If we hit null byte, then we just scanned
568 		 * the destination user name.  Go off and optimize
569 		 * if its so.
570 		 */
571 		if (*cp == 0)
572 			break;
573 		if ((c = netlook(netcomp, *cp)) == 0) {
574 			printf(gettext("No host named \"%s\"\n"), netcomp);
575 err:
576 			nstrcpy(name, BUFSIZ, net);
577 			return;
578 		}
579 		stradd(name, BUFSIZ, c);
580 		stradd(name, BUFSIZ, *cp++);
581 		/*
582 		 * If multiple network separators given,
583 		 * throw away the extras.
584 		 */
585 		while (any(*cp, metanet))
586 			cp++;
587 	}
588 	if (strlen(netcomp) == 0) {
589 		printf(gettext("net name syntax\n"));
590 		goto err;
591 	}
592 	if (debug) fprintf(stderr, "optim1(%s,%s) called\n", netstr, xfstr);
593 	optim1(netstr, xfstr);
594 	if (debug) fprintf(stderr, "optim1(%s,%s) returns\n", netstr, xfstr);
595 
596 	/*
597 	 * Convert back to machine names.
598 	 */
599 
600 	cp = xfstr;
601 	*name = '\0';
602 	while (*cp) {
603 		if ((cp2 = netname(*cp++)) == NOSTR) {
604 			printf(gettext("Made up bad net name\n"));
605 			printf(gettext("Machine code %c (0%o)\n"), cp[-1],
606 cp[-1]);
607 			printf(gettext("Sorry.\n"));
608 			goto err;
609 		}
610 		nstrcat(name, BUFSIZ, cp2);
611 		stradd(name, BUFSIZ, *cp++);
612 	}
613 	nstrcat(name, BUFSIZ, netcomp);
614 	if (debug) fprintf(stderr, "optim returns %s in name\n", name);
615 }
616 
617 /*
618  * Take a string of network machine id's and separators and
619  * optimize them.  We process these by pulling off maximal
620  * leading strings of the same type, passing these to the appropriate
621  * optimizer and concatenating the results.
622  */
623 
624 static void
625 optim1(char netstr[], char name[])
626 {
627 	char path[STSIZ], rpath[STSIZ];
628 	register char *cp, *cp2;
629 	register int tp, nc;
630 
631 	cp = netstr;
632 	prefer(cp);
633 	*name  = '\0';
634 	/*
635 	 * If the address ultimately points back to us,
636 	 * just return a null network path.
637 	 */
638 	if ((int)strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL)
639 		return;
640 	while (*cp != 0) {
641 		*path = '\0';
642 
643 		tp = ntype(cp[1]);
644 		nc = cp[1];
645 		while (*cp && tp == ntype(cp[1])) {
646 			stradd(path, sizeof (path), *cp++);
647 			cp++;
648 		}
649 		switch (netkind(tp)) {
650 		default:
651 			nstrcpy(rpath, sizeof (rpath), path);
652 			break;
653 
654 		case IMPLICIT:
655 			optimimp(path, rpath);
656 			break;
657 
658 		case EXPLICIT:
659 			optimex(path, rpath);
660 			break;
661 		}
662 		for (cp2 = rpath; *cp2 != 0; cp2++) {
663 			stradd(name, BUFSIZ, *cp2);
664 			stradd(name, BUFSIZ, nc);
665 		}
666 	}
667 	optiboth(name);
668 	prefer(name);
669 }
670 #endif	/* OPTIM */
671 
672 /*
673  * Return the network of the separator --
674  *	AN for arpa net
675  *	BN for Bell labs net	(e.g. UUCP, NOT Berknet)
676  *	SN for Schmidt net	(Berknet)
677  *	0 if we don't know.
678  */
679 static int
680 ntype(register int nc)
681 {
682 	register struct ntypetab *np;
683 
684 	for (np = ntypetab; np->nt_char != 0; np++)
685 		if (np->nt_char == nc)
686 			return(np->nt_bcode);
687 	return(0);
688 }
689 
690 #ifdef	OPTIM
691 /*
692  * Return the kind of routing used for the particular net
693  * EXPLICIT means explicitly routed
694  * IMPLICIT means implicitly routed
695  * 0 means don't know
696  */
697 
698 static int
699 netkind(register int nt)
700 {
701 	register struct nkindtab *np;
702 
703 	for (np = nkindtab; np->nk_type != 0; np++)
704 		if (np->nk_type == nt)
705 			return(np->nk_kind);
706 	return(0);
707 }
708 
709 /*
710  * Do name optimization for an explicitly routed network (eg uucp).
711  */
712 
713 static int
714 optimex(char net[], char name[])
715 {
716 	register char *cp, *rp;
717 	register int m;
718 
719 	nstrcpy(name, STSIZ, net);
720 	cp = name;
721 	if (strlen(cp) == 0)
722 		return(-1);
723 	if (cp[strlen(cp)-1] == LOCAL) {
724 		name[0] = 0;
725 		return(0);
726 	}
727 	for (cp = name; *cp; cp++) {
728 		m = *cp;
729 		rp = strrchr(cp+1, m);
730 		if (rp != NOSTR)
731 			strcpy(cp, rp);
732 	}
733 	return(0);
734 }
735 
736 /*
737  * Do name optimization for implicitly routed network (eg, arpanet).
738  */
739 
740 static int
741 optimimp(char net[], char name[])
742 {
743 	register char *cp;
744 	register char m;
745 
746 	cp = net;
747 	if (strlen(cp) == 0)
748 		return(-1);
749 	m = cp[strlen(cp) - 1];
750 	if (m == LOCAL) {
751 		*name = '\0';
752 		return(0);
753 	}
754 	name[0] = m;
755 	name[1] = 0;
756 	return(0);
757 }
758 
759 /*
760  * Perform global optimization on the given network path.
761  * The trick here is to look ahead to see if there are any loops
762  * in the path and remove them.  The interpretation of loops is
763  * more strict here than in optimex since both the machine and net
764  * type must match.
765  */
766 
767 static void
768 optiboth(char net[])
769 {
770 	register char *cp, *cp2;
771 
772 	cp = net;
773 	if (strlen(cp) == 0)
774 		return;
775 	if (((int)strlen(cp) % 2) != 0) {
776 		printf(gettext("Strange arg to optiboth\n"));
777 		return;
778 	}
779 	while (*cp) {
780 		cp2 = rpair(cp+2, *cp);
781 		if (cp2 != NOSTR)
782 			strcpy(cp, cp2);
783 		cp += 2;
784 	}
785 }
786 
787 /*
788  * Find the rightmost instance of the given (machine, type) pair.
789  */
790 
791 static char *
792 rpair(char str[], int mach)
793 {
794 	register char *cp, *last;
795 
796 	cp = str;
797 	last = NOSTR;
798 	while (*cp) {
799 		if (*cp == mach)
800 			last = cp;
801 		cp += 2;
802 	}
803 	return(last);
804 }
805 
806 /*
807  * Change the network separators in the given network path
808  * to the preferred network transmission means.
809  */
810 
811 static void
812 prefer(char name[])
813 {
814 	register char *cp, n;
815 	register int state;
816 
817 	state = LOCAL;
818 	for (cp = name; *cp; cp += 2) {
819 		n = best(state, *cp);
820 		if (n)
821 			cp[1] = n;
822 		state = *cp;
823 	}
824 }
825 
826 /*
827  * Return the best network separator for the given machine pair.
828  */
829 
830 static char
831 best(int src, int dest)
832 {
833 	register int dtype, stype;
834 	register struct netorder *np;
835 
836 	stype = nettype(src);
837 	dtype = nettype(dest);
838 	fflush(stdout);
839 	if (stype == 0 || dtype == 0) {
840 		printf(gettext("ERROR:  unknown internal machine id\n"));
841 		return(0);
842 	}
843 	if ((stype & dtype) == 0)
844 		return(0);
845 	np = &netorder[0];
846 	while ((np->no_stat & stype & dtype) == 0)
847 		np++;
848 	return(np->no_char);
849 }
850 #endif	/* OPTIM */
851 
852 #ifdef notdef
853 /*
854  * Code to twist around arpa net names.
855  */
856 
857 #define WORD 257			/* Token for a string */
858 
859 static	char netbuf[256];
860 static	char *yylval;
861 
862 /*
863  * Reverse all of the arpa net addresses in the given name to
864  * be of the form "host @ user" instead of "user @ host"
865  * This function is its own inverse.
866  */
867 
868 char *
869 revarpa(char str[])
870 {
871 
872 	if (yyinit(str) < 0)
873 		return(NOSTR);
874 	if (name())
875 		return(NOSTR);
876 	if (strcmp(str, netbuf) == 0)
877 		return(str);
878 	return(savestr(netbuf));
879 }
880 
881 /*
882  * Parse (by recursive descent) network names, using the following grammar:
883  *	name:
884  *		term {':' term}
885  *		term {'^' term}
886  *		term {'!' term}
887  *		term '@' name
888  *		term '%' name
889  *
890  *	term:
891  *		string of characters.
892  */
893 
894 static int
895 name(void)
896 {
897 	register int t;
898 	register char *cp;
899 
900 	for (;;) {
901 		t = yylex();
902 		if (t != WORD)
903 			return(-1);
904 		cp = yylval;
905 		t = yylex();
906 		switch (t) {
907 		case 0:
908 			nstrcat(netbuf, sizeof (netbuf), cp);
909 			return(0);
910 
911 		case '@':
912 		case '%':
913 			if (name())
914 				return(-1);
915 			stradd(netbuf, sizeof (netbuf), '@');
916 			nstrcat(netbuf, sizeof (netbuf), cp);
917 			return(0);
918 		case WORD:
919 			return(-1);
920 
921 		default:
922 			nstrcat(netbuf, sizeof (netbuf), cp);
923 			stradd(netbuf, sizeof (netbuf), t);
924 		}
925 	}
926 }
927 
928 /*
929  * Scanner for network names.
930  */
931 
932 static	char *charp;			/* Current input pointer */
933 static	int nexttok;			/* Salted away next token */
934 
935 /*
936  * Initialize the network name scanner.
937  */
938 
939 int
940 yyinit(char str[])
941 {
942 	static char lexbuf[BUFSIZ];
943 
944 	netbuf[0] = 0;
945 	if (strlen(str) >= sizeof lexbuf - 1)
946 		return(-1);
947 	nexttok = 0;
948 	nstrcpy(lexbuf, sizeof (lexbuf), str);
949 	charp = lexbuf;
950 	return(0);
951 }
952 
953 /*
954  * Scan and return a single token.
955  * yylval is set to point to a scanned string.
956  */
957 
958 int
959 yylex(void)
960 {
961 	register char *cp, *dotp;
962 	register int s;
963 
964 	if (nexttok) {
965 		s = nexttok;
966 		nexttok = 0;
967 		return(s);
968 	}
969 	cp = charp;
970 	while (*cp && isspace(*cp))
971 		cp++;
972 	if (*cp == 0)
973 		return(0);
974 	if (any(*cp, metanet)) {
975 		charp = cp+1;
976 		return(*cp);
977 	}
978 	dotp = cp;
979 	while (*cp && !any(*cp, metanet) && !any(*cp, " \t"))
980 		cp++;
981 	if (any(*cp, metanet))
982 		nexttok = *cp;
983 	if (*cp == 0)
984 		charp = cp;
985 	else
986 		charp = cp+1;
987 	*cp = 0;
988 	yylval = dotp;
989 	return(WORD);
990 }
991 #endif
992 
993 /*
994  * Add a single character onto a string. Here dstsize is the size of the
995  * destnation buffer.
996  */
997 
998 static void
999 stradd(register char *dst, int dstsize, register int c)
1000 {
1001 	while (*dst != '\0') {
1002 		dst++;
1003 		dstsize--;
1004 	}
1005 	if (--dstsize > 0)
1006 		*dst++ = (char)c;
1007 	*dst = '\0';
1008 }
1009