mcm (03/01/83)
/* * Mkpath creates UUCP routing information from the Usenet directory * Mark and Karen Horton send down the Usenet. The map input should * look something like : * * Name: name-of-site * (extra fields) * Usenet: connected sites * (any extra connected sites) * UUCP: connected sites * (any extra connected sites) * Name: * * The Usenet: and UUCP: fields must not have any other fields between * them. Blank lines can be used to separate fields. The connected sites * can be separated by any number of blanks, tabs, commas, or new-lines. * * Usage: mkpath [-lsite][-2][-dsite][-dsite1!site2][-asite1!site2] map-file(s) * * -lsite creates paths starting at "site". If not supplied, * the system name from the <whoami.h> file is used. * * -2 forces all routes to be two-way. * * -dsite removes "site" from the list; -dsite1!site2 removes the connection * from "site1" to "site2". * * -asite1!site2 adds the connection from "site1" to "site2". If the -2 * option is used, it adds the connection from "site2" to "site1" as well. * * The output goes to stdout. * * Version 3.0 2/12/83 * Version 3.1 2/21/83 added -a option * Version 3.2 2/23/83 made storage for -a,-d options dynamic * * Written by Mike Mitchell decvax!duke!mcnc!ikonas!mcm * */ #include <stdio.h> #include <ctype.h> #include <whoami.h> FILE *map; /* map input file */ struct site { /* structure for all needed info on a site */ char *name; /* name of site */ struct site *route; /* route to take to get there */ struct path *conn; /* who the site is connected to */ struct site *nxtsite; /* pointer to next site in list */ }; struct path { /* structure for connection & routing info */ struct site *siteptr; /* pointer to site data */ struct path *nxtpath; /* pointer to next hop in route */ }; struct site *Begsite; /* pointer to the start of site data */ struct site *Strsite; /* pointer to starting point's site data */ char *SYSNAME = sysname; /* system name from whoami.h */ main(argc, argv) int argc; char **argv; { int i, dno, ano, twoflg, upcase(), getcon(); register struct site *curptr; /* current site */ register struct path *conptr; /* pointer to connections */ struct site *search(), *getsite(); struct site *sptr; struct path *twoptr, *lnkptr; struct path *ptalloc(); char buf[BUFSIZ], tmp[BUFSIZ], *fgets(); char **dsites, **asites, *stnam, *cp, *index(); char **calloc(); FILE *fopen(); if (argc < 2) { fprintf(stderr, "Usage: %s [-lsite][-dsite][-dsite!site][-asite!site] mapfile...\n", argv[0]); exit(1); } Begsite = NULL; i = 0; argc--; dno = 0; ano = 0; twoflg = 0; stnam = SYSNAME; /* * scan for options we can take care of now. Allocate space for -a and -d * options. */ while(i++ < argc) { if (argv[i][0] == '-') { switch(argv[i][1]) { case 'l': stnam = &argv[i][2]; break; case '2': twoflg = 1; break; case 'd': dno++; break; case 'a': ano++; break; default: fprintf(stderr, "Bad option %s\n", argv[i]); exit(1); } } } if (dno != 0) { if ((dsites = calloc(dno, sizeof(char *))) == NULL) { fprintf(stderr, "Can't get mem for -d options"); exit(4); } } if (ano != 0) { if ((asites = calloc(ano, sizeof(char *))) == NULL) { fprintf(stderr, "Can't get mem for -a options"); exit(4); } } ano = 0; dno = 0; i = 0; /* * Loop through the map data files, building the site & connection data */ while(i++ < argc) { if (argv[i][0] == '-') { switch(argv[i][1]) { case 'd': dsites[dno++] = &argv[i][2]; break; case 'a': asites[ano++] = &argv[i][2]; break; } continue; } map = fopen(argv[i], "r"); if (map == NULL) { fprintf(stderr, "Cannot open %s for map input\n", argv[i]); exit(2); } while(fgets(buf, BUFSIZ, map) != NULL) { /* * look for a "Name:" field */ if ((upcase(buf))&&(strncmp(buf,"NAME:",5)==0)) { /* * find out if the current site is in our list. If not, add it to the * list. */ if (sscanf(buf, "NAME: %s", tmp)!=1) continue; curptr = getsite(tmp); buf[0] = '\0'; /* * now grab the first connected site name, if any. */ if (getcon(buf, tmp, 1)) { /* * allocate space for the path structure, then check to see if the * connection is a "known" site. If it isn't, add it to our list * of "known" sites. Make the recently allocated path structure * point to the site, and link it in with the rest of the connection * info. */ curptr->conn = ptalloc(); conptr = curptr->conn; conptr->siteptr=getsite(tmp); /* * now get the rest of the connected site names */ while(getcon(buf, tmp, 0)) { conptr->nxtpath = ptalloc(); conptr = conptr->nxtpath; conptr->siteptr = getsite(tmp); } } } } fclose(map); } /* * add any connections specified on the command line */ while(ano--) { if ((cp=index(asites[ano],'!')) == NULL) { fprintf(stderr, "Warning: -a%s is an invalid -a command.\n", asites[ano]); continue; } *cp++ = '\0'; curptr = getsite(asites[ano]); sptr = getsite(cp); /* * go through connection list for site1, and make sure site2 is not already * connected. */ conptr = curptr->conn; while(conptr != NULL) { if (conptr->siteptr->name == sptr->name) break; conptr = conptr->nxtpath; } if (conptr == NULL) { /* * add the site into the connection list. */ conptr = curptr->conn; curptr->conn = ptalloc(); curptr->conn->nxtpath = conptr; curptr->conn->siteptr = sptr; } } /* * go through the list and make sure the connections are two way. */ if (twoflg) { curptr = Begsite; while(curptr != NULL) { twoptr = curptr->conn; /* * loop through each site's connection list */ while(twoptr != NULL) { conptr = twoptr->siteptr->conn; /* * loop through the connection's connections, looking for the current * site. Add the site to the connection's connections if it is not * found. */ while((conptr != NULL) && (conptr->siteptr != curptr)) conptr = conptr->nxtpath; if (conptr == NULL) { lnkptr = ptalloc(); lnkptr->siteptr = curptr; lnkptr->nxtpath = twoptr->siteptr->conn; twoptr->siteptr->conn = lnkptr; } twoptr = twoptr->nxtpath; } curptr = curptr->nxtsite; } } /* * now handle the -d options. */ while( dno-- ) { /* * if a '!' is not in the argument, remove the site name. */ if ((cp=index(dsites[dno], '!')) == NULL) { curptr = search(dsites[dno]); if (curptr == NULL) { fprintf(stderr, "Warning: site %s already removed\n", dsites[dno]); continue; } remsite(curptr); /* remove the site */ } /* * otherwise, remove the connection from the first site to the second site */ else { *cp++ = '\0'; curptr = search(dsites[dno]); sptr = search(cp); if (curptr == NULL || sptr == NULL ) { fprintf(stderr, "Warning: connection %s!%s non-existent\n", dsites[dno], cp); continue; } rempath(curptr, sptr); /* remove the connection */ } } /* * look for the starting point */ Strsite = search(stnam); if (Strsite == NULL) { fprintf(stderr, "cannot find site %s\n", stnam); exit(3); } /* * The hard part -- create the UUCP route for every site. Paths() is * a recursive routine that wanders through the connection info * looking for the best way to get somewhere. */ paths(Strsite); /* * now that paths has finished, output the routing information. * All we have to do is traverse the "route" pointers! */ curptr = Begsite; while(curptr != NULL) { if (curptr->route != NULL) { printf("%s\t", curptr->name); proute(curptr); printf("%%s\n"); } curptr = curptr->nxtsite; } } /* * search() searches the "known" sites, looking for the site name * passed to it. If it finds it, it returns the site pointer to * it. Otherwise, it returns NULL. */ struct site *search(nam) register char *nam; { register struct site *sp; sp = Begsite; while(sp != NULL) { if (strcmp(sp->name, nam) == 0) return(sp); sp = sp->nxtsite; } return(NULL); } /* * getsite() searchs the site list for a given site. If the site is not * found, it adds the site name passed to it to the list of "known" * sites. It allocates the site structure & space for the name, * copies the name, and links everything in. It returns a pointer to * the site structure. */ struct site *getsite(nam) register char *nam; { char *malloc(), *calloc(); register char *cp; register struct site *sp; struct site *search(); cp = nam; /* remove any junk from the name */ while (*cp) { if (isspace(*cp) || *cp == ',') { *cp = '\0'; break; } cp++; } if ((sp = search(nam)) != NULL) return(sp); sp = (struct site *)calloc(1, sizeof(struct site)); if (sp == NULL) { fprintf(stderr, "Cannot get mem for new site!\n"); exit(4); } cp = malloc(strlen(nam) + 1); if (cp == NULL) { fprintf(stderr, "Cannot get mem for name!\n"); exit(4); } strcpy(cp, nam); sp->name = cp; sp->nxtsite = Begsite; Begsite = sp; sp->route = NULL; sp->conn = NULL; return(sp); } /* * getcon() grabs a site name from the "Usenet:" field or the "UUCP:" * field. If the buffer passed is empty, it reads from the map file. * if it reads a "Name:" field, it returns false. If it reads a line * with a colon in it and the passed flag is false, it returns false. * It reads until there is something on the line that is not a blank, * tab, comma, or a newline. Once there is something useful in the * buffer, it removes the first "word" terminated by a blank, tab, comma, * or newline and returns true. */ getcon(buf, name, flag) char *buf; register char *name; int flag; { long ftell(), pos; register char *cp0, *cp1; char *fgets(), *wrdpos(); cp0 = buf; cp1 = wrdpos(cp0); pos = ftell(map); /* * read until there is some useful data in buf */ while((cp1==NULL)&&((cp0=fgets(buf,BUFSIZ,map))!=NULL)) { /* * if there is a colon on this line, see if its a "Name:" field, "Usenet:" * field, or a "UUCP:" field. */ if (upcase(cp0)) { if (strncmp(cp0, "NAME:", 5) == 0) { fseek(map, pos, 0); return(0); } if (strncmp(cp0, "USENET:", 7) == 0) cp0 += 7; else if (strncmp(cp0, "UUCP:", 5) == 0) cp0 += 5; else if (!flag) { fseek(map, pos, 0); return(0); } else *cp0 = '\0'; } else if (flag) *cp0 = '\0'; pos = ftell(map); cp1 = wrdpos(cp0); } if (cp0 == NULL) { fseek(map, pos, 0); return(0); } /* * now that there is some data, we copy the first "word" into name, and * remove the "word" from buf, returning true. */ while((*cp1) && (!isspace(*cp1)) && (*cp1 != ',')) *name++ = *cp1++; *name = '\0'; cp1 = wrdpos(cp1); if (cp1 == NULL) *buf = '\0'; else strcpy(buf, cp1); return(1); } /* * ptalloc() allocates a path structure. */ struct path *ptalloc() { char *calloc(); register struct path *ptr; ptr = (struct path *)calloc(1, sizeof(struct path)); if (ptr == NULL) { fprintf(stderr, "Cannot allocate memory for path\n"); exit(4); } ptr->siteptr = NULL; ptr->nxtpath = NULL; return(ptr); } /* * paths() does all the dirty work. For each site in the connection list, * it checks the hop count of the stored route and the current route. * if the current route is shorter or there is no stored route, it * and assigns the current route as the stored route. * it then calls itself with the current site's connection list. */ paths(sptr) struct site *sptr; { register struct path *pptr; register struct site *conptr; int scnt, hopcnt(); register int cnt; /* * loop through all the connections */ #ifdef DEBUG /*DBG*/ printf("\npaths: at site %s\n", sptr->name); #endif scnt = hopcnt(sptr); pptr = sptr->conn; while(pptr != NULL) { conptr = pptr->siteptr; if (conptr != Strsite) { cnt = hopcnt(conptr->route); /* * if there is no stored route or if the stored route is longer than * the current route, replace it. */ if ((cnt == 0) || (cnt >= scnt)) { #ifdef DEBUG /*DBG*/ printf("changing path for %s.\nOld: ",conptr->name); /*DBG*/ proute(conptr->route); /*DBG*/ printf("\nNew: "); /*DBG*/ proute(sptr); /*DBG*/ printf("\n"); #endif conptr->route = sptr; /* * final step, call paths() with this site's list of connections */ paths(conptr); #ifdef DEBUG /*DBG*/ printf("back from paths, now in %s\n", sptr->name); #endif } } pptr = pptr->nxtpath; } } /* * upcase() first scans to see if there is a colon in the buffer passed * to it. If not, it returns false. Otherwise, it converts everything * in the buffer up to the colon to uppercase and returns true. */ upcase(buf) register char *buf; { register char *cp; char *index(); cp = index(buf, ':'); if (cp == NULL) return(0); while(buf != cp) { if (islower(*buf)) *buf = toupper(*buf); buf++; } return(1); } /* * wrdpos() returns a pointer to the first character that is not a * blank, tab, comma, or newline. If it can't find one, it returns * NULL. */ char *wrdpos(buf) register char *buf; { while((*buf) && (isspace(*buf)||(*buf==','))) buf++; if (*buf) return(buf); return(NULL); } /* * hopcnt() returns the number of site structures in the routing list its * argument points to. */ hopcnt(sptr) register struct site *sptr; { register int cnt; cnt = 0; while(sptr != NULL) { cnt++; sptr = sptr->route; } return(cnt); } /* * proute() prints out the way to get to the passed site. It calls itself * with a pointer to the next site in the route, then prints the current * site name. */ proute(sptr) register struct site *sptr; { if (sptr == NULL || sptr->route == NULL) return; proute(sptr->route); printf("%s!", sptr->name); } /* * rempath removes the connection from its first argument to its * second argument. */ rempath(sptr, cptr) register struct site *sptr, *cptr; { register struct path *pptr; struct path *pptr0; pptr = sptr->conn; /* * loop through connections */ while( pptr != NULL) { if (pptr->siteptr == cptr) { /* * found the connection, now must remove it. */ if (pptr == sptr->conn) { sptr->conn = pptr->nxtpath; free(pptr); pptr = sptr->conn; } else { pptr0->nxtpath = pptr->nxtpath; free(pptr); pptr = pptr0->nxtpath; } } else { pptr0 = pptr; pptr = pptr->nxtpath; } } } /* * remsite removes a site from the data base. */ remsite(sptr) register struct site *sptr; { register struct site *sitep, *sitep0; struct path *pptr, *pptr0; sitep = Begsite; /* * search the site list for the one we want. */ while( sitep != NULL) { if (sitep == sptr) { /* * we found it. Now free up the name and the connection list. */ free(sptr->name); pptr = sptr->conn; while(pptr != NULL) { pptr0 = pptr->nxtpath; free(pptr); pptr = pptr0; } /* * now link the data around the site. */ if (sitep == Begsite) { Begsite = sitep->nxtsite; free(sitep); sitep = Begsite; } else { sitep0->nxtsite = sitep->nxtsite; free(sitep); sitep = sitep0->nxtsite; } } else { /* * remove any connections between the current site & the one we are removing */ rempath(sitep, sptr); sitep0 = sitep; sitep = sitep->nxtsite; } } }