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