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