#include #include #include /* open_node() This opens the nodelist files, and leaves the global handles set. If any one of the files is missing, all handles are closed: NODELIST.BBS system name, city, phone # NODELIST.NMP (see below)node data; route-to, etc NODELIST.IDX index NODELIST.NDX net index/index NODELIST.ZDX zone index/index NODELIST.NTC Nodes To Call NTC is an index used at FidoNet time, and is a sequential list of nodes & record numbers of nodes that have mail packets; a faster way to locate them. open_nmap(event) should be used to open a specific nodemap. close_node() Closes the files above, and mark them closed. (handle == -1) open_nmap(event) This opens the correct nodemap file; if FBIT_KEEPNMP is false, then it simply uses NODELIST.NMP. Otherwise, if an event is running (event != -1) it looks for NODEMAP.tag. If it doesnt exist, it is created by copying NODELIST.NMP. (When an event starts, the router will make the current file, whatever it is, the proper nodemap for this event (via route files, etc. This is preserved in NODELIST.NMP, or NODEMAP.tag, if KEEPNMP is on.) makenname(dest,src) Create a full pathname using the node path. index_node(&node) Return the record number of the specified node, or -1 if not found. This does a logical compare, ie. negative net/node numbers are normalized to 0 for comparison. find_node(&node) Locate a node in the nodemap, return its record number or -1 if not found. This does a logical compare, as above. find_ndat(&node) Locate a node in the nodelist, return its record number or -1 if not found. This does a logical compare, as above. The node structure in memory is filled in. get_ndat(recd) Read the specified ndat record. Returns its index, or -1 if error. This fills in the ndat structure from the ASCII .BBS file that contains the system information, and the node number from the .IDX index file. ntc_file() Creates a new, empty NODELIST.NTC file. get_ntc(recd) put_ntc(recd) Read or write the specified NTC record. Returns its index, or -1 if error. find_ntc(node) Search for the specified NTC record, return its record number and the record loaded. This does an exact zone:net/node.point match. get_node(recd) put_node(recd) Read or write the specified node record. Returns its index, or -1 if error. NODES AND POINTS: The functions below copy, compare, and otherwise process _node structures. Comparisons uses only zone, net and number; point is generally not used, since it's handling is limited to replying, entering and packeting for points to pickup; there is no routing of points. fix_node(&node) Fixes negative net and node numbers to 0. is_zonehost(&node) is_reghost(&node) is_nethost(&node) is_host(&node) Returns true if the node is a zone, a region or net host, or either. is_point(&node) Returns true if this node is a point. is_us(&node) Returns true if this node is us. (ID or ALTID) cpy_node(&dest,&src) Copies one node into another, including point. char *str_node(&node) Generates a static string expression of the node, and returns a pointer to it. Through a kludge it can be called up to 4 times without overwriting a previous invokation. The point field will be expressed if it is non-zero. same_zone(&n1,&n2) Both are same zone same_net(&n1,&n2) Both are same zone and net/region same_node(&n1,&n2) Both are same zone, net/region and number same_point(&n1,&n2) Both are same zone, net/region, number and point set_nn(string,&node) Parse the string into various zone:net/node numbers. This accepts "zone:net/node" format. If zone or net isnt specified, it is left untouched. This will parse points. */ static unsigned nn,traversal; /* node indexing */ static unsigned nodetotal; /* total packets */ static int node_found; /* node record number, */ /* Open all the NODELIST files; The indices are not buffered. */ open_node() { char ln[SS]; struct _rptrcd *r; /* the index_node() routine does buffering on these */ makenname(ln,"nodelist.idx"); idxfile= open(ln,0 + 0x100); /* we read this only */ makenname(ln,"nodelist.ndx"); ndxfile= open(ln,0 + 0x100); /* we read this only */ makenname(ln,"nodelist.zdx"); zdxfile= open(ln,0 + 0x100); /* we read this only */ /* These we let the system buffer */ makenname(ln,"nodelist.nmp"); /* open basic NMP file */ nodefile= open(ln,2); makenname(ln,"nodelist.bbs"); ndatfile= open(ln,0); /* we read this only */ makenname(ln,"nodelist.ntc"); ntcfile= open(ln,2); /* we read/write this */ if (node_files()) return; /* missing files */ /* Check the repeat-record for the correct version number. Note that we read only part of the record. */ lseek(nodefile,0L,0); read(nodefile,ln,sizeof(ln)); r= (struct _rptrcd *) ln; /* cast it */ if (r-> revision != NODEVERS) { clprintf(SM+199); /* wrong revision */ close_node(); } } /* Close the node file */ close_node() { if (nodefile != -1) close(nodefile); if (idxfile != -1) close(idxfile); if (ndxfile != -1) close(ndxfile); if (zdxfile != -1) close(zdxfile); if (ndatfile != -1) close(ndatfile); if (ntcfile != -1) close(ntcfile); nodefile= -1; ndatfile= -1; idxfile= -1; ndxfile= -1; zdxfile= -1; ntcfile= -1; } /* Return true if any one of the nodelist files is not open. */ node_files() { return((nodefile == -1) || (idxfile == -1) || (ndxfile == -1) || (zdxfile == -1) || (ndatfile == -1) || (ntcfile == -1)); } /* Open the nodemap file. If KEEP_NMP is not enabled, or no event is running, it simply returns, as NODELIST.NMP was opened by open_node(). Otherwise, it tries to open NODEMAP.tag; if that fails, it creates one by copying NODELIST.NMP. */ open_nmap(e) int e; { char *name,buff[500]; int f; unsigned n; if ((e == -1) || !fbit(FBIT_KEEPNMP)) return; name= &buff[100]; /* cheap & easy 2nd buffer */ sprintf(buff,"NODEMAP.%c",fido.sched[e].tag); makenname(name,buff); /* build the right filename */ f= open(name,2); /* open the nodemap file, */ if (f == -1) { /* if it doesnt exist, */ cprintf(" * Creating new nodemap %s\r\n",buff); f= creat(name,2); /* make a new one, */ if (f == -1) return; /* oops! */ lseek(nodefile,0L,0); /* the entire file! */ while (n= read(nodefile,buff,sizeof(buff))) { if (write(f,buff,n) != n) { clprintf("!DISK FULL!\r\n"); /* disk full! */ close(f); /* abandon this */ return; } } } close(nodefile); /* close .NMP */ nodefile= f; /* replace with NODEMAP.tag */ } /* Load the specified nodemap entry. This is built from the .IDX file (node address portion) and the nodemap data from the .NMP file. */ get_node(recd) int recd; { long o; o= 0L + recd; o *= sizeof(struct _idx); lseek(idxfile,o,0); /* node address */ if (read(idxfile,&nmap.node,sizeof(struct _node)) != sizeof(struct _node)) return(-1); /* from .IDX */ nmap.node.point= 0; /* (clean up mess) */ o= 0L + recd; o *= sizeof(struct _nmp); o += sizeof(struct _rptrcd); /* skip the repeat recd */ lseek(nodefile,o,0); /* get nmap recd */ if (read(nodefile,&nmap.route_recd,sizeof(struct _nmp)) != sizeof(struct _nmp)) return(-1); if (nmap.attr & NMAP_REGION) nmap.node.number= -1; /* flag REGION hosts */ return(recd); } /* Find the next record with the POLL bit set, starting at the specified record numbers. Returns the record number found, or -1 if not found. */ find_poll(recd) unsigned recd; { struct _nmp *np,buff[100]; /* for file buffering */ unsigned ii,ic; long o; o= 0L + recd; o *= sizeof(struct _nmp); /* starting position */ o += sizeof(struct _rptrcd); /* skip the repeat recd */ lseek(nodefile,o,0); /* seek there */ ii= ic= 0; /* buffer empty, etc */ while (1) { if (ii >= ic) { /* load buffer */ ii= 0; ic= read(nodefile,buff,sizeof(buff)) / sizeof(struct _nmp); if (! ic) return(-1); /* not found -- no such node */ np= (struct _nmp *) buff; } if (np-> bits & NMAP_POLL) { /* if a POLL recd */ get_node(recd); /* load the record */ return(recd); /* found one */ } ++recd; ++np; ++ii; } } /* Write out a node record; return -1 if error. Of course only the nodemap data is written out. */ put_node(recd) int recd; { long o; o= 0L + recd; o *= sizeof(struct _nmp); o += sizeof(struct _rptrcd); /* skip the repeat recd */ lseek(nodefile,o,0); write(nodefile,&nmap.route_recd,sizeof(struct _nmp)); return(recd); } /* Create a new NODELIST.NTC file; close the existing one, and create a new one. Returns 0 if something goes wrong. */ ntc_file() { char buff[SS]; close(ntcfile); /* close current, */ makenname(buff,"NODELIST.NTC"); ntcfile= creat(buff,2); /* create a new one */ return(!node_files()); /* 0 == error */ } /* Load a node index record into memory; return the record number. */ get_ntc(recd) int recd; { long o; o= 0L + recd; o *= sizeof(struct _ntc); lseek(ntcfile,o,0); if (read(ntcfile,&ntc,sizeof(struct _ntc)) != sizeof(struct _ntc)) recd= -1; return(recd); } /* Write out a ntc record; return -1 if error. */ put_ntc(recd) int recd; { long o; o= 0L + recd; o *= sizeof(struct _ntc); lseek(ntcfile,o,0); write(ntcfile,&ntc,sizeof(struct _ntc)); return(recd); } /* Search for an NTC record by node; this is just a brute force linear search; not much else we can do, besides there are almost always very few of them. */ find_ntc(n) struct _node *n; { int r; r= 0; while (get_ntc(r) != -1) { /* hard search */ if (same_point(n,&ntc.node)) return(r); ++r; } return(-1); /* not found */ } /* Return the record number of this node, or -1 if not found. This works by searching through the three indices: the ZONE INDEX to locate the first node in that zone; the net/region index to find the first record in that net; then a sequential search to locate the specific node. This works with the nodelist in any order. Note that there is not a _node structure in the index file, but we treat it as one; the elements (zone, net, number) are in the correct order, and point is not used by the comparison functions; cpy_node will copy two bytes of what follows the number, but we just fill that part in later. We use the same code for all three searches because the files are so similar. The NDX and ZDX files contain record numbers in the position field; the IDX file does not, and so we have to set the starting recd number from the NDX file and increment as we search IDX. Also there are some speedups: in the NDX file, we check for the correct node itself; default host routing should increase the probability of hits. (The ZDX record number is relative to the NDX file itself; the NDX record number is relative to the IDX file and the rest of the database.) Also in the IDX search, since the list is heirarchical by definition, if we encounter another NET during the search, we can assume that the target node does not exist. */ index_node(n) struct _node *n; { struct _node target; /* logical node to search for */ int recd; /* record number, from ZDX and NDX file */ struct _idx *ip,buff[100]; /* for file buffering */ unsigned ii,ic; int f; /* file we are currently searching */ int level; /* file rotator */ cpy_node(&target,n); /* make a copy, */ fix_node(&target); /* normalize for compare */ f= zdxfile; /* start with ZONE index */ lseek(zdxfile,0L,0); /* seek to BOF */ level= ii= ic= 0; /* buffer empty, etc */ while (1) { if (ii >= ic) { /* load buffer */ ii= 0; ic= (read(f,buff,sizeof(buff)) / sizeof(struct _idx)); if (! ic) return(-1); /* not found -- no such node */ ip= (struct _idx *) buff; } switch (level) { case 0: /* ZONE index */ if (! same_zone(ip,&target)) break; recd= ip-> pos; /* (cuz NODE search is linear) */ f= ndxfile; /* found ZONE host */ goto start; /* continue from next file */ case 1: /* NET index */ if (! same_net(ip,&target)) break; recd= ip-> pos; /* (cuz NODE search is linear) */ if (same_node(ip,&target)) return(recd); f= idxfile; /* found NET host */ start: ++level; /* next search ... */ ip-> pos *= sizeof(struct _idx); lseek(f,ip-> pos,0); /* (long) compiler bug */ ii= ic= 0; /* buffer empty, etc */ break; case 2: /* NODE index */ if (same_node(ip,&target)) return(recd); if (! same_net(ip,&target)) return(-1); ++recd; /* generate recd number */ break; } ++ip; ++ii; /* next struct, next record */ } } /* Find a particular node in the nodemap; return its index or -1 if not found. */ find_node(n) struct _node *n; { int recd; recd= index_node(n); /* look for it, */ if (recd == -1) return(-1); /* error! */ recd= get_node(recd); /* get it if found, */ return(recd); } /* Parse a string into net and node number. zone and net are untouched if not found in the input string. This parses as follows: (xx means unchanged) string output 99:88/77 99:88/77.0 88/77 xx:88/88.0 77 xx:xx/77.0 99:88 99:88/00.0 88/ xx:88/00.0 99:88/77.3 99:88/77.3 */ set_nn(cp,n) char *cp; struct _node *n; { int x; if (isdigit(*cp)) { n-> number= atoi(cp); /* assume its node only */ while (isdigit(*cp)) ++cp; /* skip the number, */ } if (*cp == ':') { /* if it was a zone, */ n-> zone= n-> number; /* that was a zone number, */ n-> net= atoi(++cp); /* net must follow */ while (isdigit(*cp)) ++cp; /* skip the net number */ if (*cp == '/') { /* if that was a net number */ n-> number= atoi(++cp); /* set node number (or zero) */ while (isdigit(*cp)) ++cp; /* skip node number */ } } else if (*cp == '/') { /* if that was a net number, */ n-> net= n-> number; /* copy it up, */ n-> number= atoi(++cp); /* now set node number */ while (isdigit(*cp)) ++cp; /* skip node number */ } if (*cp == '.') { /* if a dot */ n-> point= atoi(++cp); /* parse point */ } } /* Pull the descriptive info on a particular node from NODELIST.BBS. Return -1 if not found. */ find_ndat(n) struct _node *n; { int recd; recd= index_node(n); /* look for it, */ if (recd == -1) return(-1); /* error! */ recd= get_ndat(recd); /* get it if found, */ return(recd); } /* Load a ndat record into memory; return the record number. This involves loading the .IDX index record to get the node address and .BBS file offset to find the actual data. Note that this treats the address within the index file as a _node struct; since the elements are in the right order this works (except that it copies 2 bytes of garbage instead of the 'point' field). */ get_ndat(recd) int recd; { long o; struct _idx index; char *cp,buff[256]; o= 0L + recd; o *= sizeof(struct _idx); /* compiler (long) bug */ lseek(idxfile,o,0); /* load the .IDX */ read(idxfile,&index,sizeof(struct _idx)); /* record, */ lseek(ndatfile,index.pos,0); /* seek in .BBS file */ rline(ndatfile,buff,sizeof(buff)); /* load raw data */ cpy_node(&ndat.node,&index); /* set node address, */ ndat.node.point= 0; /* clear copied garbage */ cp= buff; ndat.cost= atoi(cp); cp= next_arg(cp); /* set the cost, */ ndat.rate= atoi(cp); cp= next_arg(cp); /* baud rate, */ cpynli(ndat.name,cp,24); cp= next_arg(cp); /* copy/process name, */ cpynli(ndat.phone,cp,40); cp= next_arg(cp); /* phone, */ cpynli(ndat.city,cp,40); cp= next_arg(cp); /* city */ cpynli(ndat.pwd,cp,8); /* password */ return(recd); } /* Local routine to copy a nodelist.bbs field item, expanding _'s to spaces and stopping at the first delimiter. */ static cpynli(d,s,n) char *d,*s; { while (*s && !delim(*s) && --n) { if (*s == '_') *d++= ' '; else *d++= *s; ++s; } *d= NUL; } /* Set negative node numbers to zero, their logical equivelant, for routing, etc. */ fix_node(n) struct _node *n; { if (n-> net < 0) n-> net= 0; if (n-> number < 0) n-> number= 0; } /* Return true if this node is a net host. */ is_nethost(n) struct _node *n; { return(! n-> number); } /* Return true if this node is a non-host net. */ is_reghost(n) struct _node *n; { return(n-> number < 0); } /* Return true if this node is a net or region host. */ is_host(n) struct _node *n; { return(n-> number <= 0); } /* Return true if this node is a zone host. */ is_zonehost(n) struct _node *n; { return((n-> number <= 0) && (n-> net <= 0)); } /* Return true if this is a point. */ is_point(n) struct _node *n; { return(n-> point); } /* Return true if the two nodes are members of the same net. */ same_zone(n1,n2) struct _node *n1,*n2; { return(n1-> zone == n2-> zone); } /* Return true if the two nodes are members of the same net. */ same_net(n1,n2) struct _node *n1,*n2; { return( (n1-> net == n2-> net) && (n1-> zone == n2-> zone) ); } /* Return true if the two nodes are the same. */ same_node(n1,n2) struct _node *n1,*n2; { return( (n1-> number == n2-> number) && (n1-> net == n2-> net) && (n1-> zone == n2-> zone) ); } /* Return true if the two nodes are the point. */ same_point(n1,n2) struct _node *n1,*n2; { return( (n1-> point == n2-> point) && (n1-> number == n2-> number) && (n1-> net == n2-> net) && (n1-> zone == n2-> zone) ); } /* Copy one node structure into another. IMPORTANT NOTE: Notice that they structures are treated as char * pointers. */ cpy_node(d,s) char *d,*s; /* our code */ /* struct _node *d,*s; REALITY */ { int i; for (i= sizeof(struct _node); i--;) *d++= *s++; } /* Return a pointer to a string of the zone:net/node. */ char *str_node(n) struct _node *n; { #define KLUDGE 4 /* strings we can make at once */ static char *cp,work[KLUDGE][40]; /* where we keep them */ static int k; /* which one as are on */ k = ++k % KLUDGE; /* select next ... */ cp= work[k]; /* easier faster shorter */ *cp= NUL; /* empty it */ sprintf(cp,"%d:",n-> zone); sprintf(cp,(n-> point ? "%d:%d/%d.%d" : "%d:%d/%d"), n-> zone,n-> net,n-> number,n-> point); return(cp); } /* Return true if this net/node combination is us. */ is_us(n) struct _node *n; { return(same_node(n,&id) || same_node(n,&altid)); } /* Make a full nodelist pathname. */ makenname(pathname,filename) char *pathname,*filename; { strcpy(pathname,fido.nodepath); strcat(pathname,filename); }