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
44static char		*arpafix(char name[], char from[]);
45static char		*lasthost(char *addr);
46static char		*makeremote(char name[], char from[]);
47static int		mstash(char name[], int attnet);
48static int		mtype(int mid);
49static int		netlook(char machine[], int attnet);
50static int		nettype(int mid);
51static int		ntype(register int nc);
52static void		stradd(register char *str, int n, register int c);
53static char		*tackon(char *sys, char *rest);
54static struct xtrahash	*xlocate(char name[]);
55#ifdef OPTIM
56static char		best(int src, int dest);
57static char		*mlook(int mid);
58static int		netkind(register int nt);
59static void		optiboth(char net[]);
60static void		optim(char net[], char name[]);
61static void		optim1(char netstr[], char name[]);
62static int		optimex(char net[], char name[]);
63static int		optimimp(char net[], char name[]);
64static void		prefer(char name[]);
65static 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
75char *
76netmap(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 */
150static char *
151tackon(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 */
166int
167ishost(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 */
177static char *
178lasthost(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 */
193char *
194unuucp(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 */
228static int
229netlook(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
273static char *
274netname(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 */
295static char *
296arpafix(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 */
345static char *
346makeremote(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 */
366static int
367nettype(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
387struct xtrahash {
388	char	*xh_name;		/* Name of machine */
389	short	xh_mid;			/* Machine ID */
390	short	xh_attnet;		/* Attached networks */
391} xtrahash[XHSIZE];
392
393static struct xtrahash	*xtab[XHSIZE];		/* F: mid-->machine name */
394
395static short	midfree;			/* Next free machine id */
396
397/*
398 * Initialize the extra host hash table.
399 * Called by sreset.
400 */
401void
402minit(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 */
424static int
425mstash(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
460static struct xtrahash *
461xlocate(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
496static char *
497mlook(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 */
516static int
517mtype(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
548static void
549optim(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);
575err:
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],
606cp[-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
624static void
625optim1(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 */
679static int
680ntype(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
698static int
699netkind(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
713static int
714optimex(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
740static int
741optimimp(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
767static void
768optiboth(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
791static char *
792rpair(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
811static void
812prefer(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
830static char
831best(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
859static	char netbuf[256];
860static	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
868char *
869revarpa(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
894static int
895name(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
932static	char *charp;			/* Current input pointer */
933static	int nexttok;			/* Salted away next token */
934
935/*
936 * Initialize the network name scanner.
937 */
938
939int
940yyinit(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
958int
959yylex(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
998static void
999stradd(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