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