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