calls to nonexistant fonts (from gbergman@cartan)
[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
97ea6d23 8static char *sccsid = "@(#)optim.c 5.5 (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
2f25b8e3 70 cp = str;
7537529b
KS
71 strcpy(path, "");
72 for (;;) {
73 if ((c = *cp++) == 0)
74 break;
75 cp2 = buf;
76 while (!any(c, metanet) && c != 0) {
77 *cp2++ = c;
78 c = *cp++;
79 }
80 *cp2 = 0;
81 if (c == 0) {
82 strcat(path, buf);
83 break;
84 }
85 host = netlook(buf, ntype(c));
86 strcat(path, netname(host));
87 stradd(path, c);
88 }
89 if (strcmp(str, path) != 0)
90 return(savestr(path));
91 return(str);
92}
6049dc7a 93
7537529b
KS
94/*
95 * Turn a network machine name into a unique character
7537529b 96 */
7537529b
KS
97netlook(machine, attnet)
98 char machine[];
99{
100 register struct netmach *np;
101 register char *cp, *cp2;
97ea6d23 102 char nbuf[BUFSIZ];
7537529b
KS
103
104 /*
105 * Make into lower case.
106 */
107
108 for (cp = machine, cp2 = nbuf; *cp; *cp2++ = little(*cp++))
f486c0e5
RC
109 if (cp2 >= &nbuf[sizeof(nbuf)-1])
110 break;
7537529b
KS
111 *cp2 = 0;
112
113 /*
114 * If a single letter machine, look through those first.
115 */
116
117 if (strlen(nbuf) == 1)
118 for (np = netmach; np->nt_mid != 0; np++)
119 if (np->nt_mid == nbuf[0])
120 return(nbuf[0]);
121
122 /*
123 * Look for usual name
124 */
125
126 for (np = netmach; np->nt_mid != 0; np++)
127 if (strcmp(np->nt_machine, nbuf) == 0)
128 return(np->nt_mid);
129
130 /*
131 * Look in side hash table.
132 */
133
134 return(mstash(nbuf, attnet));
135}
136
137/*
138 * Make a little character.
139 */
140
141little(c)
142 register int c;
143{
144
145 if (c >= 'A' && c <= 'Z')
146 c += 'a' - 'A';
147 return(c);
148}
149
150/*
151 * Turn a network unique character identifier into a network name.
152 */
153
154char *
155netname(mid)
156{
157 register struct netmach *np;
158 char *mlook();
159
160 if (mid & 0200)
161 return(mlook(mid));
162 for (np = netmach; np->nt_mid != 0; np++)
163 if (np->nt_mid == mid)
164 return(np->nt_machine);
165 return(NOSTR);
166}
167
168/*
169 * Deal with arpa net addresses. The way this is done is strange.
170 * In particular, if the destination arpa net host is not Berkeley,
171 * then the address is correct as stands. Otherwise, we strip off
172 * the trailing @Berkeley, then cook up a phony person for it to
173 * be from and optimize the result.
174 */
175char *
176arpafix(name, from)
177 char name[];
178 char from[];
179{
180 register char *cp;
181 register int arpamach;
182 char newname[BUFSIZ];
183 char fake[5];
184 char fakepath[20];
185
186 if (debug) {
187 fprintf(stderr, "arpafix(%s, %s)\n", name, from);
188 }
189 cp = rindex(name, '@');
190 if (cp == NOSTR)
191 cp = rindex(name, '%');
192 if (cp == NOSTR) {
193 fprintf(stderr, "Somethings amiss -- no @ or % in arpafix\n");
194 return(name);
195 }
196 cp++;
197 arpamach = netlook(cp, '@');
198 if (arpamach == 0) {
199 if (debug)
200 fprintf(stderr, "machine %s unknown, uses: %s\n", cp, name);
201 return(name);
202 }
203 if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) {
204 if (debug)
205 fprintf(stderr, "machine %s known but remote, uses: %s\n",
206 cp, name);
207 return(name);
208 }
209 strcpy(newname, name);
210 cp = rindex(newname, '@');
211 if (cp == NOSTR)
212 cp = rindex(newname, '%');
213 *cp = 0;
214 fake[0] = arpamach;
215 fake[1] = ':';
216 fake[2] = LOCAL;
217 fake[3] = ':';
218 fake[4] = 0;
219 prefer(fake);
220 strcpy(fakepath, netname(fake[0]));
221 stradd(fakepath, fake[1]);
222 strcat(fakepath, "daemon");
223 if (debug)
224 fprintf(stderr, "machine local, call netmap(%s, %s)\n",
225 newname, fakepath);
226 return(netmap(newname, fakepath));
227}
228
229/*
230 * Take a network machine descriptor and find the types of connected
231 * nets and return it.
232 */
233
234nettype(mid)
235{
236 register struct netmach *np;
237
238 if (mid & 0200)
239 return(mtype(mid));
240 for (np = netmach; np->nt_mid != 0; np++)
241 if (np->nt_mid == mid)
242 return(np->nt_type);
243 return(0);
244}
245
246/*
247 * Hashing routines to salt away machines seen scanning
248 * networks paths that we don't know about.
249 */
250
251#define XHSIZE 19 /* Size of extra hash table */
252#define NXMID (XHSIZE*3/4) /* Max extra machines */
253
254struct xtrahash {
255 char *xh_name; /* Name of machine */
256 short xh_mid; /* Machine ID */
257 short xh_attnet; /* Attached networks */
258} xtrahash[XHSIZE];
259
260struct xtrahash *xtab[XHSIZE]; /* F: mid-->machine name */
261
262short midfree; /* Next free machine id */
263
264/*
265 * Initialize the extra host hash table.
266 * Called by sreset.
267 */
268
269minit()
270{
271 register struct xtrahash *xp, **tp;
272 register int i;
273
274 midfree = 0;
275 tp = &xtab[0];
276 for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) {
277 xp->xh_name = NOSTR;
278 xp->xh_mid = 0;
279 xp->xh_attnet = 0;
280 *tp++ = (struct xtrahash *) 0;
281 }
282}
283
284/*
285 * Stash a net name in the extra host hash table.
286 * If a new entry is put in the hash table, deduce what
287 * net the machine is attached to from the net character.
288 *
289 * If the machine is already known, add the given attached
290 * net to those already known.
291 */
292
293mstash(name, attnet)
294 char name[];
295{
296 register struct xtrahash *xp;
297 struct xtrahash *xlocate();
6049dc7a 298 int x;
7537529b
KS
299
300 xp = xlocate(name);
301 if (xp == (struct xtrahash *) 0) {
302 printf("Ran out of machine id spots\n");
303 return(0);
304 }
305 if (xp->xh_name == NOSTR) {
306 if (midfree >= XHSIZE) {
307 printf("Out of machine ids\n");
308 return(0);
309 }
310 xtab[midfree] = xp;
311 xp->xh_name = savestr(name);
312 xp->xh_mid = 0200 + midfree++;
313 }
6049dc7a
KS
314 x = ntype(attnet);
315 if (x == 0)
7537529b 316 xp->xh_attnet |= SN;
6049dc7a
KS
317 else
318 xp->xh_attnet |= x;
7537529b
KS
319 return(xp->xh_mid);
320}
321
322/*
323 * Search for the given name in the hash table
324 * and return the pointer to it if found, or to the first
325 * empty slot if not found.
326 *
327 * If no free slots can be found, return 0.
328 */
329
330struct xtrahash *
331xlocate(name)
332 char name[];
333{
334 register int h, q, i;
335 register char *cp;
336 register struct xtrahash *xp;
337
338 for (h = 0, cp = name; *cp; h = (h << 2) + *cp++)
339 ;
340 if (h < 0 && (h = -h) < 0)
341 h = 0;
342 h = h % XHSIZE;
343 cp = name;
344 for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) {
345 xp = &xtrahash[(h + q) % XHSIZE];
346 if (xp->xh_name == NOSTR)
347 return(xp);
348 if (strcmp(cp, xp->xh_name) == 0)
349 return(xp);
350 if (h - q < 0)
5b429fc0 351 h += XHSIZE;
7537529b
KS
352 xp = &xtrahash[(h - q) % XHSIZE];
353 if (xp->xh_name == NOSTR)
354 return(xp);
355 if (strcmp(cp, xp->xh_name) == 0)
356 return(xp);
357 }
358 return((struct xtrahash *) 0);
359}
360
361/*
362 * Return the name from the extra host hash table corresponding
363 * to the passed machine id.
364 */
365
366char *
367mlook(mid)
368{
369 register int m;
370
371 if ((mid & 0200) == 0)
372 return(NOSTR);
373 m = mid & 0177;
374 if (m >= midfree) {
375 printf("Use made of undefined machine id\n");
376 return(NOSTR);
377 }
378 return(xtab[m]->xh_name);
379}
380
381/*
382 * Return the bit mask of net's that the given extra host machine
383 * id has so far.
384 */
385
386mtype(mid)
387{
388 register int m;
389
390 if ((mid & 0200) == 0)
391 return(0);
392 m = mid & 0177;
393 if (m >= midfree) {
394 printf("Use made of undefined machine id\n");
395 return(0);
396 }
397 return(xtab[m]->xh_attnet);
398}
399
400/*
401 * Take a network name and optimize it. This gloriously messy
bedc7b35 402 * operation takes place as follows: the name with machine names
7537529b
KS
403 * in it is tokenized by mapping each machine name into a single
404 * character machine id (netlook). The separator characters (network
405 * metacharacters) are left intact. The last component of the network
406 * name is stripped off and assumed to be the destination user name --
407 * it does not participate in the optimization. As an example, the
408 * name "research!vax135!research!ucbvax!bill" becomes, tokenized,
409 * "r!x!r!v!" and "bill" A low level routine, optim1, fixes up the
410 * network part (eg, "r!x!r!v!"), then we convert back to network
411 * machine names and tack the user name on the end.
412 *
413 * The result of this is copied into the parameter "name"
414 */
415
416optim(net, name)
417 char net[], name[];
418{
419 char netcomp[BUFSIZ], netstr[40], xfstr[40];
420 register char *cp, *cp2;
421 register int c;
422
423 strcpy(netstr, "");
424 cp = net;
425 for (;;) {
426 /*
427 * Rip off next path component into netcomp
428 */
429 cp2 = netcomp;
430 while (*cp && !any(*cp, metanet))
431 *cp2++ = *cp++;
432 *cp2 = 0;
433 /*
434 * If we hit null byte, then we just scanned
435 * the destination user name. Go off and optimize
436 * if its so.
437 */
438 if (*cp == 0)
439 break;
440 if ((c = netlook(netcomp, *cp)) == 0) {
441 printf("No host named \"%s\"\n", netcomp);
442err:
443 strcpy(name, net);
444 return;
445 }
446 stradd(netstr, c);
447 stradd(netstr, *cp++);
448 /*
449 * If multiple network separators given,
450 * throw away the extras.
451 */
452 while (any(*cp, metanet))
453 cp++;
454 }
455 if (strlen(netcomp) == 0) {
456 printf("net name syntax\n");
457 goto err;
458 }
459 optim1(netstr, xfstr);
460
461 /*
462 * Convert back to machine names.
463 */
464
465 cp = xfstr;
466 strcpy(name, "");
467 while (*cp) {
468 if ((cp2 = netname(*cp++)) == NOSTR) {
469 printf("Made up bad net name\n");
34e33c1a
KS
470 printf("Machine code %c (0%o)\n", cp[-1], cp[-1]);
471 printf("Sorry -- dumping now. Alert K. Shoens\n");
472 core(0);
7537529b
KS
473 goto err;
474 }
475 strcat(name, cp2);
476 stradd(name, *cp++);
477 }
478 strcat(name, netcomp);
479}
480
481/*
482 * Take a string of network machine id's and separators and
483 * optimize them. We process these by pulling off maximal
484 * leading strings of the same type, passing these to the appropriate
485 * optimizer and concatenating the results.
486 */
487
7537529b
KS
488optim1(netstr, name)
489 char netstr[], name[];
490{
491 char path[40], rpath[40];
492 register char *cp, *cp2;
493 register int tp, nc;
494
495 cp = netstr;
496 prefer(cp);
9d24b3cb 497 strcpy(name, "");
bedc7b35
KS
498 /*
499 * If the address ultimately points back to us,
500 * just return a null network path.
501 */
502 if (strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL)
503 return;
7537529b
KS
504 while (*cp != 0) {
505 strcpy(path, "");
506 tp = ntype(cp[1]);
507 nc = cp[1];
508 while (*cp && tp == ntype(cp[1])) {
509 stradd(path, *cp++);
510 cp++;
511 }
512 switch (netkind(tp)) {
513 default:
514 strcpy(rpath, path);
515 break;
516
517 case IMPLICIT:
518 optimimp(path, rpath);
519 break;
520
521 case EXPLICIT:
522 optimex(path, rpath);
523 break;
524 }
525 for (cp2 = rpath; *cp2 != 0; cp2++) {
526 stradd(name, *cp2);
527 stradd(name, nc);
528 }
529 }
530 optiboth(name);
531 prefer(name);
532}
533
534/*
535 * Return the network of the separator --
536 * AN for arpa net
537 * BN for Bell labs net
538 * SN for Schmidt (berkeley net)
539 * 0 if we don't know.
540 */
541
542ntype(nc)
543 register int nc;
544{
7737ae6a 545 register struct ntypetab *np;
7537529b 546
7737ae6a 547 for (np = ntypetab; np->nt_char != 0; np++)
6049dc7a 548 if (np->nt_char == nc)
7737ae6a 549 return(np->nt_bcode);
6049dc7a 550 return(0);
7537529b
KS
551}
552
553/*
554 * Return the kind of routing used for the particular net
555 * EXPLICIT means explicitly routed
556 * IMPLICIT means implicitly routed
557 * 0 means don't know
558 */
559
560netkind(nt)
561 register int nt;
562{
7737ae6a 563 register struct nkindtab *np;
7537529b 564
7737ae6a 565 for (np = nkindtab; np->nk_type != 0; np++)
6049dc7a
KS
566 if (np->nk_type == nt)
567 return(np->nk_kind);
568 return(0);
7537529b
KS
569}
570
571/*
572 * Do name optimization for an explicitly routed network (eg BTL network).
573 */
574
575optimex(net, name)
576 char net[], name[];
577{
578 register char *cp, *rp;
579 register int m;
580 char *rindex();
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}