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