home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / MSDOS / KA9Q / MSWIN / DSP2D_UP.ZIP / NRCMD.C < prev   
Encoding:
C/C++ Source or Header  |  1992-02-21  |  32.7 KB  |  1,426 lines

  1. /* net/rom user command processing
  2.  * Copyright 1989 by Daniel M. Frank, W9NK.  Permission granted for
  3.  * non-commercial distribution only.
  4.  *
  5.  * Mods by G1EMM
  6.  * Mods by PA0GRI
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <ctype.h>
  11. #include <time.h>
  12. #include "global.h"
  13. #include "config.h"
  14. #include "mbuf.h"
  15. #include "ax25.h"
  16. #include "mailbox.h"
  17. #include "netrom.h"
  18. #include "nr4.h"
  19. #include "timer.h"
  20. #include "iface.h"
  21. #include "lapb.h"
  22. #include "cmdparse.h"
  23. #include "session.h"
  24. #include "socket.h"
  25. #include "commands.h"
  26. #include "files.h"
  27. #include "pktdrvr.h"
  28.  
  29. extern char EightString[];
  30. char Nr4user[AXALEN];
  31.  
  32. /* Define the initial sort for netrom list dumps */
  33.  
  34. unsigned Nr_sorttype=1;
  35.  
  36. char *Nr4states[] = {
  37.     "Disconnected",
  38.     "Conn Pending",
  39.     "Connected",
  40.     "Disc Pending",
  41.     "Listening"
  42. } ;
  43.  
  44. char *Nr4reasons[] = {
  45.     "Normal",
  46.     "By Peer",
  47.     "Timeout",
  48.     "Reset",
  49.     "Refused"
  50. } ;
  51. static int dobcnodes __ARGS((int argc,char *argv[],void *p));
  52. static int dointerface __ARGS((int argc,char *argv[],void *p));
  53. static int donfadd __ARGS((int argc,char *argv[],void *p));
  54. static int donfdrop __ARGS((int argc,char *argv[],void *p));
  55. static int donfdump __ARGS((void));
  56. static int donfmode __ARGS((int argc,char *argv[],void *p));
  57. static int donodefilter __ARGS((int argc,char *argv[],void *p));
  58. static int donodetimer __ARGS((int argc,char *argv[],void *p));
  59. static int donralias __ARGS((int argc,char *argv[],void *p));
  60. static int donracktime __ARGS((int argc,char *argv[],void *p));
  61. static int donrcall __ARGS((int argc,char *argv[],void *p));
  62. static int donrchoketime __ARGS((int argc,char *argv[],void *p));
  63. static int donrconnect __ARGS((int argc,char *argv[],void *p));
  64. static int donrirtt __ARGS((int argc,char *argv[],void *p));
  65. static int donrkick __ARGS((int argc,char *argv[],void *p));
  66. static int dorouteadd __ARGS((int argc,char *argv[],void *p));
  67. static int doroutedrop __ARGS((int argc,char *argv[],void *p));
  68. static int donrqlimit __ARGS((int argc,char *argv[],void *p));
  69. static int donrreset __ARGS((int argc,char *argv[],void *p));
  70. static int donrretries __ARGS((int argc,char *argv[],void *p));
  71. static int donrroute __ARGS((int argc,char *argv[],void *p));
  72. static int donrstatus __ARGS((int argc,char *argv[],void *p));
  73. static int donrsave __ARGS((int argc,char *argv[],void *p));
  74. static int donrload __ARGS((int argc,char *argv[],void *p));
  75. static int donrttl __ARGS((int argc,char *argv[],void *p));
  76. static int donruser __ARGS((int argc,char *argv[],void *p));
  77. static int donrverbose __ARGS((int argc,char *argv[],void *p));
  78. static int donrwindow __ARGS((int argc,char *argv[],void *p));
  79. void doobsotick __ARGS((void));
  80. static int doobsotimer __ARGS((int argc,char *argv[],void *p));
  81. static int dominquality __ARGS((int argc,char *argv[],void *p));
  82. static int donrtype __ARGS((int argc,char *argv[],void *p));
  83. static int donrpromisc __ARGS((int argc,char *argv[],void *p));
  84. static int donrderate __ARGS((int argc,char *argv[],void *p));
  85. static int doroutesort __ARGS((int argc,char *argv[],void *p));
  86.  
  87. static struct cmds Nrcmds[] = {
  88.     "acktime",      donracktime,    0, 0,   NULLCHAR,
  89.     "alias",        donralias,  0, 0,   NULLCHAR,
  90.     "bcnodes",      dobcnodes,  0, 2,   "netrom bcnodes <interface>",
  91.     "connect",      donrconnect, 1024, 2,   "netrom connect <node>",
  92.     "call",         donrcall,   0, 0,   NULLCHAR,
  93.     "choketime",    donrchoketime,  0, 0,   NULLCHAR,
  94.     "derate",       donrderate,     0, 0,   NULLCHAR,
  95.     "interface",    dointerface,    0, 3,
  96.     "netrom interface <interface> <quality>",
  97.     "irtt",         donrirtt,       0, 0,   NULLCHAR,
  98.     "kick",         donrkick,       0, 2,   "netrom kick <&nrcb>",
  99. #ifdef notdef
  100.     "load",         donrload,       0, 0,   NULLCHAR,
  101. #endif
  102.     "minquality",   dominquality,   0, 0,   NULLCHAR,
  103.     "nodefilter",   donodefilter,   0, 0,   NULLCHAR,
  104.     "nodetimer",    donodetimer,    0, 0,   NULLCHAR,
  105.     "obsotimer",    doobsotimer,    0, 0,   NULLCHAR,
  106.     "promiscuous",  donrpromisc,    0, 0,   NULLCHAR,
  107.     "qlimit",       donrqlimit,     0, 0,   NULLCHAR,
  108.     "route",        donrroute,      0, 0,   NULLCHAR,
  109.     "reset",        donrreset,      0, 2,   "netrom reset <&nrcb>",
  110.     "retries",      donrretries,    0, 0,   NULLCHAR,
  111. #ifdef notdef
  112.     "save",         donrsave,       0, 0,   NULLCHAR,
  113. #endif
  114.     "status",       donrstatus,     0, 0,   NULLCHAR,
  115.     "timertype",    donrtype,       0, 0,   NULLCHAR,
  116.     "ttl",          donrttl,        0, 0,   NULLCHAR,
  117.     "user",         donruser,       0, 0,   NULLCHAR,
  118.     "verbose",      donrverbose,    0, 0,   NULLCHAR,
  119.     "window",       donrwindow,     0, 0,   NULLCHAR,
  120.     NULLCHAR,
  121. } ;
  122.  
  123. char Myalias[AXALEN] = "";      /* the NETROM alias in 'call' form */
  124. char Nralias[ALEN+1] = "";      /* the NETROM alias in 'alias' form */
  125.  
  126. struct timer Nodetimer ;        /* timer for nodes broadcasts */
  127. struct timer Obsotimer ;        /* timer for aging routes */
  128.  
  129. /* Command multiplexer */
  130. int
  131. donetrom(argc,argv,p)
  132. int argc ;
  133. char *argv[] ;
  134. void *p;
  135. {
  136.     return subcmd(Nrcmds,argc,argv,p) ;
  137. }
  138.  
  139. static struct cmds Routecmds[] = {
  140.     "add",  dorouteadd,     0, 6,
  141.         "netrom route add <alias> <destination> <interface> <quality> <neighbor>",
  142.     "drop", doroutedrop, 0, 4,
  143.         "netrom route drop <destination> <neighbor> <interface>",
  144.     "info", dorouteinfo, 0, 1,
  145.         "",
  146.     "sort", doroutesort, 0, 1,
  147.         "",
  148.     NULLCHAR,
  149. } ;
  150.  
  151. /* Route command multiplexer */
  152. static int
  153. donrroute(argc, argv,p)
  154. int argc ;
  155. char *argv[] ;
  156. void *p;
  157. {
  158.     if(argc < 2) {
  159.         doroutedump() ;
  160.         return 0 ;
  161.     }
  162.     return subcmd(Routecmds,argc,argv,p) ;
  163. }
  164.  
  165. /* Dump a list of known routes */
  166. int
  167. doroutedump()
  168. {
  169.     extern unsigned Nr_sorttype;
  170.     register struct nrroute_tab *rp ;
  171.     register int i,j,k, column, flow_tmp;
  172.     char buf[17] ;
  173.     char *cp,*temp ;
  174.     
  175.     column = 1 ;
  176.     
  177.     for(i = 0,j=0 ; i < NRNUMCHAINS ; i++)
  178.         for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ;j++,rp = rp->next);
  179.     if (j) {
  180.  
  181.     flow_tmp=Current->flowmode;
  182.     Current->flowmode=1;
  183.     
  184.     temp = mallocw (j*17);
  185.     
  186.      for(i = 0,k=0 ; i < NRNUMCHAINS ; i++)
  187.         for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rp->next,k+=17) {
  188.              if (Nr_sorttype)   
  189.             {  
  190.              strcpy(buf,rp->alias) ;
  191.              /* remove trailing spaces */
  192.              if((cp = strchr(buf,' ')) == NULLCHAR)
  193.                 cp = &buf[strlen(buf)] ;
  194.              if(cp != buf)           /* don't include colon for null alias */
  195.                 *cp++ = ':' ;
  196.              pax25(cp,rp->call) ;
  197.                
  198.             } 
  199.               else 
  200.              {
  201.               pax25(buf,rp->call);
  202.               cp=&buf[strlen(buf)];
  203.               *cp++=':';
  204.               strcpy(cp,rp->alias);
  205.              }
  206.             sprintf(&temp[k],"%-16s",buf);
  207.             }
  208.  
  209.          qsort(temp,(size_t)j,17,strcmp);
  210.         
  211.          for (i=0,k=0;i<j;i++,k+=17) {       
  212.             tprintf("%-16s  ",&temp[k]) ;
  213.             if(column++ == 4) {
  214.                 if(tprintf("\n") == EOF)  break;
  215.                 column = 1 ;
  216.             }
  217.         }
  218.  
  219.      if(column != 1)
  220.         tprintf("\n") ;
  221.      free(temp);        
  222.      Current->flowmode=flow_tmp;
  223.     } 
  224.     return 0 ;
  225. }
  226.  
  227. /* netrom Route Dump Sort - ALIAS or CALL first */
  228. static
  229. doroutesort(argc,argv,p)
  230. int argc ;
  231. char *argv[] ;
  232. void *p ;
  233. {
  234.     extern unsigned Nr_sorttype;
  235.  
  236.     if (argc >= 2) {
  237.     
  238.     switch(argv[1][0]) {
  239.         case 'A':
  240.         case 'a':
  241.             Nr_sorttype = 1 ;
  242.             break ;
  243.         case 'C':
  244.         case 'c':
  245.             Nr_sorttype = 0 ;
  246.             break ;
  247.         default:
  248.             tprintf("usage: netrom sort [alias|call]\n") ;
  249.             return -1 ;
  250.      }
  251.     }
  252.     tprintf("Netrom lists Sorted by %s\n", Nr_sorttype ? "Alias" : "Call" ) ;
  253.     return 0 ;
  254. }
  255.  
  256. /* print detailed information on an individual or ALL routes  (sorted) */
  257. int
  258. dorouteinfo(argc,argv,p)
  259. int argc ;
  260. char *argv[] ;
  261. void *p;
  262. {
  263.       extern unsigned Nr_sorttype;
  264.       register struct nrroute_tab *rp ;
  265.       register struct nr_bind *bp ;
  266.       register struct nrnbr_tab *np ;
  267.       char dest[AXALEN] ;
  268.       char neighbor[AXBUF] ;
  269.       char buf[17];
  270.       char *cp,*temp;
  271.       int i,j,k, flow_tmp;
  272.  
  273.       if (argc>1){
  274.  
  275.       if(setcall(dest,argv[1]) == -1) {
  276.         tprintf("bad destination name\n") ;
  277.         return -1 ;
  278.       }
  279.         
  280.       if((rp = find_nrroute(dest)) == NULLNRRTAB) {
  281.         tprintf("no such route\n") ;
  282.         return -1 ;
  283.       }
  284.       
  285.       for(bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next) {
  286.          np = bp->via ;
  287.          if(tprintf("%1s %3d  %3d  %-8s  %s\n",
  288.              (bp->flags & NRB_PERMANENT ? "P" :
  289.              bp->flags & NRB_RECORDED ? "R" : " "),
  290.              bp->quality,bp->obsocnt,
  291.              Nrifaces[np->iface].iface->name,
  292.              pax25(neighbor,np->call)) == EOF)
  293.                break;
  294.        }
  295.       
  296.       } else {
  297.        
  298.       flow_tmp=Current->flowmode;
  299.       Current->flowmode=1;
  300.         
  301.       for (i=0,j=0;i<NRNUMCHAINS;i++)
  302.         for (rp=Nrroute_tab[i];rp!=NULLNRRTAB;rp= rp->next)
  303.            for(bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next,j++); 
  304.            
  305.       if (j) {
  306.  
  307.        temp=mallocw (j*50);
  308.  
  309.        for (i=0,k=0;i<NRNUMCHAINS;i++)
  310.         for (rp=Nrroute_tab[i];rp!=NULLNRRTAB;rp= rp->next)
  311.            for(bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next,k+=50) {
  312.          np = bp->via ;
  313.          if (Nr_sorttype) {
  314.            
  315.            strcpy(buf,rp->alias) ;
  316.            if((cp = strchr(buf,' ')) == NULLCHAR)
  317.                cp = &buf[strlen(buf)] ;
  318.            if(cp != buf) 
  319.                *cp++ = ':' ;
  320.            pax25(cp,rp->call) ;
  321.          
  322.          } else {
  323.            
  324.            pax25(buf,rp->call);
  325.            cp=&buf[strlen(buf)];
  326.            *cp++=':';
  327.            strcpy(cp,rp->alias);
  328.          
  329.          }
  330.  
  331.          sprintf(&temp[k],"%-16s %3d  %3d  %-8s  %s  %1s\n",buf,
  332.              bp->quality,bp->obsocnt,
  333.              Nrifaces[np->iface].iface->name,
  334.              pax25(neighbor,np->call),
  335.              (bp->flags & NRB_PERMANENT ? "P" :
  336.              bp->flags & NRB_RECORDED ? "R" : " "));
  337.             
  338.         }
  339.          qsort(temp,(size_t)j,50,strcmp);
  340.  
  341.          for (i=0,k=0;i<j;i++,k+=50)
  342.         if (tprintf("%s",&temp[k])==EOF)
  343.                break;
  344.          free (temp);
  345.          Current->flowmode=flow_tmp;
  346.        }
  347.       }
  348.     return 0 ;
  349. }
  350.         
  351.  
  352. extern char Mbnrid[];
  353.  
  354. /*set the mailbox netrom id*/
  355. void
  356. setmbnrid() {
  357.     char tmp[AXBUF];
  358.     char tmp2[AXBUF];
  359.  
  360.     if(Nr_iface != NULLIF){
  361.         if(*Myalias != '\0')
  362.             sprintf(Mbnrid,"%s:%s} ",pax25(tmp,Myalias),
  363.                 pax25(tmp2,Nr_iface->hwaddr));
  364.         else
  365.             sprintf(Mbnrid,"%s} ",
  366.                 pax25(tmp2,Nr_iface->hwaddr));
  367.         return;
  368.     }
  369.     if(*Myalias != '\0')
  370.         sprintf(Mbnrid,"%s:%s} ",pax25(tmp,Myalias),
  371.             pax25(tmp2,Nr_iface->hwaddr));
  372.     else
  373.         sprintf(Mbnrid,"%s} ",pax25(tmp,Myalias),
  374.             pax25(tmp2,Mycall));
  375. }
  376.  
  377. /* define the netrom alias*/
  378. static int
  379. donralias(argc,argv,p)
  380. int argc ;
  381. char *argv[] ;
  382. void *p;
  383. {
  384.     int len;
  385.     char tmp[AXBUF];
  386.  
  387.     if(argc < 2) {
  388.         tprintf("%s\n",pax25(tmp,Myalias));
  389.         return 0;
  390.     }
  391.     if((setcall(Myalias,argv[1]) == -1) ||
  392.            (putalias(Nralias,argv[1],1) == -1)) {
  393.             tputs("can't set alias\n");
  394.             return 0;
  395.     }
  396.     setmbnrid();
  397.     return 0;
  398. }
  399.  
  400. /* define the netrom call,
  401.  * this simply changes the interface linkaddress!
  402.  * but is a little easier to use...
  403.  */
  404. static int
  405. donrcall(argc,argv,p)
  406. int argc ;
  407. char *argv[] ;
  408. void *p;
  409. {
  410.     int len;
  411.     char tmp[AXBUF];
  412.  
  413.     if(Nr_iface == NULLIF) {
  414.         tputs("Attach netrom interface first\n") ;
  415.         return 1 ;
  416.     }
  417.  
  418.     if(argc < 2) {
  419.         if (Nr_iface->hwaddr == NULLCHAR)
  420.             tputs("not set\n");
  421.         else
  422.             tprintf("%s\n",pax25(tmp,Nr_iface->hwaddr));
  423.     } else {
  424.         if( (len=strlen(argv[1])) > (AXBUF - 1)) {
  425.             tputs("too long\n");
  426.             return 1;
  427.         }
  428.         if(Nr_iface->hwaddr != NULLCHAR)
  429.             free(Nr_iface->hwaddr);
  430.         Nr_iface->hwaddr = mallocw(Nr_iface->iftype->hwalen);
  431.         (*Nr_iface->iftype->scan)(Nr_iface->hwaddr,argv[1]);
  432.         setmbnrid();
  433.     }
  434.     return 0;
  435. }
  436.  
  437. /* make an interface available to net/rom */
  438. static int
  439. dointerface(argc,argv,p)
  440. int argc ;
  441. char *argv[] ;
  442. void *p;
  443. {
  444.     int i;
  445.     register struct iface *ifp;
  446.     char *tmpbuf;
  447.     char tmp[AXBUF];
  448.  
  449.     if(Nr_iface == NULLIF) {
  450.         tputs("Attach netrom interface first\n") ;
  451.         return 1 ;
  452.     }
  453.  
  454.     if(Nr_numiface >= NRNUMIFACE) {
  455.         tprintf("Only %d net/rom interfaces available\n",NRNUMIFACE) ;
  456.         return 1 ;
  457.     }
  458.     
  459.     if((ifp = if_lookup(argv[1])) == NULLIF){
  460.         tprintf("Interface \"%s\" unknown\n",argv[1]);
  461.         return 1;
  462.     }
  463.  
  464.     if( ifp->type != CL_AX25){
  465.         tprintf("Interface %s is not NETROM compatible\n",argv[1]);
  466.         return 1;
  467.     }
  468.  
  469.     for(i = 0 ; i < Nr_numiface ; i++)
  470.         if(Nrifaces[i].iface == ifp) {
  471.             tprintf("Interface \"%s\" is already registered\n",argv[1]) ;
  472.             return 1 ;
  473.         }
  474.         
  475.     Nrifaces[Nr_numiface].iface = ifp ;
  476.  
  477.     if((Nrifaces[Nr_numiface].quality = atoi(argv[2])) > 255) {
  478.         tputs("Quality cannot be greater than 255\n") ;
  479.         return 1 ;
  480.     }
  481.  
  482.     if(Nr_numiface == 0) /*first one we attach*/
  483.         setmbnrid();
  484.  
  485.     Nr_numiface++ ;                 /* accept this interface */
  486.     return 0 ;
  487. }
  488.  
  489.  
  490. /* convert a null-terminated alias name to a blank-filled, upcased */
  491. /* version.  Return -1 on failure. */
  492. int
  493. putalias(to,from,complain)
  494. register char *to, *from ;
  495. int complain ;    
  496. {
  497.     int len, i ;
  498.     
  499.     if((len = strlen(from)) > ALEN) {
  500.         if(complain)
  501.             tprintf("Too long - 6 max\n") ;
  502.         return -1 ;
  503.     }
  504.     
  505.     for(i = 0 ; i < ALEN ; i++) {
  506.         if(i < len) {
  507.             if(islower(*from))
  508.                 *to++ = toupper(*from++) ;
  509.             else
  510.                 *to++ = *from++ ;
  511.         }
  512.         else
  513.             *to++ = ' ' ;
  514.     }
  515.             
  516.     *to = '\0' ;
  517.     return 0 ;
  518. }
  519.  
  520. /* Add a route */
  521. static int
  522. dorouteadd(argc, argv,p)
  523. int argc ;
  524. char *argv[] ;
  525. void *p;
  526. {
  527.     char alias[AXALEN] ;
  528.     char dest[AXALEN] ;
  529.     unsigned quality ;
  530.     char neighbor[AXALEN] ;
  531.     register int i ;
  532.     int naddr ;
  533.  
  534.     /* format alias (putalias prints error message if necessary) */
  535.     if(putalias(alias,argv[1],1) == -1)
  536.         return -1 ;
  537.  
  538.     /* format destination callsign */
  539.     if(setcall(dest,argv[2]) == -1) {
  540.         tprintf("bad destination callsign\n") ;
  541.         return -1 ;
  542.     }
  543.  
  544.     /* find interface */
  545.     for(i = 0 ; i < Nr_numiface ; i++)
  546.         if(!strcmp(Nrifaces[i].iface->name,argv[3]))
  547.             break ;
  548.     if(i == Nr_numiface) {
  549.         tprintf("Interface \"%s\" not found\n",argv[3]) ;
  550.         return -1 ;
  551.     }
  552.     
  553.     /* get and check quality value */
  554.     if((quality = atoi(argv[4])) > 255) {
  555.         tprintf("maximum quality is 255\n") ;
  556.         return -1 ;
  557.     }
  558.  
  559.     /* Change from 871225 -- no digis in net/rom table */
  560.     naddr = argc - 5 ;
  561.     if(naddr > 1) {
  562.         tprintf("Use 'ax25 route' to specify digipeaters\n") ;
  563.         return -1 ;
  564.     }
  565.     
  566.     /* format neighbor address string */
  567.     setcall(neighbor,argv[5]) ;
  568.  
  569.     return nr_routeadd(alias,dest,i,quality,neighbor,1,0) ;
  570. }
  571.  
  572.  
  573. /* drop a route */
  574. static int
  575. doroutedrop(argc,argv,p)
  576. int argc ;
  577. char *argv[] ;
  578. void *p;
  579. {
  580.     char dest[AXALEN], neighbor[AXALEN] ;
  581.     register int i ;
  582.  
  583.     /* format destination and neighbor callsigns */
  584.     if(setcall(dest,argv[1]) == -1) {
  585.         tprintf("bad destination callsign\n") ;
  586.         return -1 ;
  587.     }
  588.     if(setcall(neighbor,argv[2]) == -1) {
  589.         tprintf("bad neighbor callsign\n") ;
  590.         return -1 ;
  591.     }
  592.  
  593.     /* find interface */
  594.     for(i = 0 ; i < Nr_numiface ; i++)
  595.         if(!strcmp(Nrifaces[i].iface->name,argv[3]))
  596.             break ;
  597.     if(i == Nr_numiface) {
  598.         tprintf("Interface \"%s\" not found\n",argv[3]) ;
  599.         return -1 ;
  600.     }
  601.  
  602.     return nr_routedrop(dest,neighbor,i) ;
  603. }
  604. /* Broadcast nodes list on named interface. */
  605. static int
  606. dobcnodes(argc,argv,p)
  607. int argc ;
  608. char *argv[] ;
  609. void *p;
  610. {
  611.     register int i ;
  612.  
  613.     for(i = 0 ; i < Nr_numiface ; i++)
  614.         if(!strcmp(Nrifaces[i].iface->name,argv[1]))
  615.             break ;
  616.     if(i == Nr_numiface) {
  617.         tprintf("Interface \"%s\" not found\n",argv[1]) ;
  618.         return 1 ;
  619.     }
  620.         
  621.     nr_bcnodes(i) ;
  622.     return 0;
  623. }
  624.  
  625. /* Set outbound node broadcast interval */
  626. static int
  627. donodetimer(argc,argv,p)
  628. int argc;
  629. char *argv[];
  630. void *p;
  631. {
  632.     if(argc < 2){
  633.         tprintf("Nodetimer %lu/%lu seconds\n",
  634.             read_timer(&Nodetimer)/1000L,
  635.             dur_timer(&Nodetimer)/1000L);
  636.         return 0;
  637.     }
  638.     stop_timer(&Nodetimer) ;        /* in case it's already running */
  639.     Nodetimer.func = (void (*)())donodetick;/* what to call on timeout */
  640.     Nodetimer.arg = NULLCHAR;               /* dummy value */
  641.     set_timer(&Nodetimer,atoi(argv[1])*1000L);      /* set timer duration */
  642.     start_timer(&Nodetimer);                /* and fire it up */
  643.     return 0;
  644. }
  645.  
  646. void
  647. donodetick()
  648. {
  649.     register int i ;
  650.  
  651.     for(i = 0 ; i < Nr_numiface ; i++)
  652.         nr_bcnodes(i) ;
  653.  
  654.     /* Restart timer */
  655.     start_timer(&Nodetimer) ;
  656. }
  657.  
  658. /* Set timer for aging routes */
  659. static int
  660. doobsotimer(argc,argv,p)
  661. int argc;
  662. char *argv[];
  663. void *p;
  664. {
  665.     if(argc < 2){
  666.         tprintf("Obsotimer %lu/%lu seconds\n",
  667.             read_timer(&Obsotimer)/1000L,
  668.             dur_timer(&Obsotimer)/1000L);
  669.         return 0;
  670.     }
  671.     stop_timer(&Obsotimer) ;        /* just in case it's already running */
  672.     Obsotimer.func = (void (*)())doobsotick;/* what to call on timeout */
  673.     Obsotimer.arg = NULLCHAR;               /* dummy value */
  674.     set_timer(&Obsotimer,atoi(argv[1])*1000L);      /* set timer duration */
  675.     start_timer(&Obsotimer);                /* and fire it up */
  676.     return 0;
  677. }
  678.  
  679.  
  680. /* Go through the routing table, reducing the obsolescence count of
  681.  * non-permanent routes, and purging them if the count reaches 0
  682.  */
  683. void
  684. doobsotick()
  685. {
  686.     register struct nrnbr_tab *np ;
  687.     register struct nrroute_tab *rp, *rpnext ;
  688.     register struct nr_bind *bp, *bpnext ;
  689.     int i ;
  690.  
  691.     for(i = 0 ; i < NRNUMCHAINS ; i++) {
  692.         for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rpnext) {
  693.             rpnext = rp->next ;     /* save in case we free this route */
  694.             for(bp = rp->routes ; bp != NULLNRBIND ; bp = bpnext) {
  695.                 bpnext = bp->next ;     /* in case we free this binding */
  696.                 if(bp->flags & NRB_PERMANENT)   /* don't age these */
  697.                     continue ;
  698.                 if(--bp->obsocnt == 0) {                /* time's up! */
  699.                     if(bp->next != NULLNRBIND)
  700.                         bp->next->prev = bp->prev ;
  701.                     if(bp->prev != NULLNRBIND)
  702.                         bp->prev->next = bp->next ;
  703.                     else
  704.                         rp->routes = bp->next ;
  705.                     rp->num_routes-- ;                      /* one less binding */
  706.                     np = bp->via ;                          /* find the neighbor */
  707.                     free((char *)bp) ;                              /* now we can free the bind */
  708.                     /* Check to see if we can free the neighbor */
  709.                     if(--np->refcnt == 0) {
  710.                         if(np->next != NULLNTAB)
  711.                             np->next->prev = np->prev ;
  712.                         if(np->prev != NULLNTAB)
  713.                             np->prev->next = np->next ;
  714.                         else {
  715.                             Nrnbr_tab[nrhash(np->call)] = np->next ;
  716.                         }
  717.                         free((char *)np) ;      /* free the storage */
  718.                     }
  719.                 }
  720.             }
  721.             if(rp->num_routes == 0) {               /* did we free them all? */
  722.                 if(rp->next != NULLNRRTAB)
  723.                     rp->next->prev = rp->prev ;
  724.                 if(rp->prev != NULLNRRTAB)
  725.                     rp->prev->next = rp->next ;
  726.                 else
  727.                     Nrroute_tab[i] = rp->next ;
  728.  
  729.                 free((char *)rp) ;
  730.             }
  731.         }
  732.     }
  733.  
  734.     start_timer(&Obsotimer) ;
  735. }
  736.  
  737.  
  738. static struct cmds Nfcmds[] = {
  739.     "add",  donfadd,        0, 3,
  740.         "netrom nodefilter add <neighbor> <interface> [quality]",
  741.     "drop", donfdrop,       0, 3,
  742.         "netrom nodefilter drop <neighbor> <interface>",
  743.     "mode", donfmode,       0, 0,   NULLCHAR,
  744.     NULLCHAR,       NULLFP, 0, 0,
  745.         "nodefilter subcommands: add drop mode",
  746. } ;
  747.  
  748. /* nodefilter command multiplexer */
  749. static int
  750. donodefilter(argc,argv,p)
  751. int argc ;
  752. char *argv[] ;
  753. void *p;
  754. {
  755.     if(argc < 2) {
  756.         donfdump() ;
  757.         return 0 ;
  758.     }
  759.     return subcmd(Nfcmds,argc,argv,p) ;
  760. }
  761.  
  762. /* display a list of <callsign,interface> pairs from the filter
  763.  * list.
  764.  */
  765. static int
  766. donfdump()
  767. {
  768.     int i, column = 1 ;
  769.     struct nrnf_tab *fp ;
  770.     char buf[AXBUF] ;
  771.  
  772.     for(i = 0 ; i < NRNUMCHAINS ; i++)
  773.         for(fp = Nrnf_tab[i] ; fp != NULLNRNFTAB ; fp = fp->next) {
  774.             pax25(buf,fp->neighbor) ;
  775.             tprintf("%-7s  %-8s  %-3d   ",
  776.              buf,Nrifaces[fp->iface].iface->name, fp->quality) ;
  777.             if(column++ == 3) {
  778.                 if(tprintf("\n") == EOF)
  779.                     return 0;
  780.                 column = 1 ;
  781.             }
  782.         }
  783.  
  784.     if(column != 1)
  785.         tprintf("\n") ;
  786.  
  787.     return 0 ;
  788. }
  789.  
  790. /* add an entry to the filter table */
  791. static int
  792. donfadd(argc,argv,p)
  793. int argc ;
  794. char *argv[] ;
  795. void *p;
  796. {
  797.     unsigned qual;
  798.     char neighbor[AXALEN] ;
  799.     register int i ;
  800.  
  801.     /* format callsign */
  802.     if(setcall(neighbor,argv[1]) == -1) {
  803.         tprintf("bad neighbor callsign\n") ;
  804.         return -1 ;
  805.     }
  806.  
  807.     /* find interface */
  808.     for(i = 0 ; i < Nr_numiface ; i++)
  809.         if(!strcmp(Nrifaces[i].iface->name,argv[2]))
  810.             break ;
  811.     if(i == Nr_numiface) {
  812.         tprintf("Interface \"%s\" not found\n",argv[2]) ;
  813.         return -1 ;
  814.     }
  815.  
  816.     qual = Nrifaces[i].quality;     /* set default quality */
  817.  
  818.     if(argc >= 4)
  819.         qual = atoi(argv[3]);
  820.  
  821.     return nr_nfadd(neighbor,i,qual) ;
  822. }
  823.  
  824. /* drop an entry from the filter table */
  825. static int
  826. donfdrop(argc,argv,p)
  827. int argc ;
  828. char *argv[] ;
  829. void *p;
  830. {
  831.     char neighbor[AXALEN] ;
  832.     register int i ;
  833.  
  834.     /* format neighbor callsign */
  835.     if(setcall(neighbor,argv[1]) == -1) {
  836.         tprintf("bad neighbor callsign\n") ;
  837.         return -1 ;
  838.     }
  839.  
  840.     /* find interface */
  841.     for(i = 0 ; i < Nr_numiface ; i++)
  842.         if(!strcmp(Nrifaces[i].iface->name,argv[2]))
  843.             break ;
  844.     if(i == Nr_numiface) {
  845.         tprintf("Interface \"%s\" not found\n",argv[2]) ;
  846.         return -1 ;
  847.     }
  848.  
  849.     return nr_nfdrop(neighbor,i) ;
  850. }
  851.  
  852. /* nodefilter mode subcommand */
  853. static int
  854. donfmode(argc,argv,p)
  855. int argc ;
  856. char *argv[] ;
  857. void *p;
  858. {
  859.     if(argc < 2) {
  860.         tprintf("filter mode is ") ;
  861.         switch(Nr_nfmode) {
  862.             case NRNF_NOFILTER:
  863.                 tprintf("none\n") ;
  864.                 break ;
  865.             case NRNF_ACCEPT:
  866.                 tprintf("accept\n") ;
  867.                 break ;
  868.             case NRNF_REJECT:
  869.                 tprintf("reject\n") ;
  870.                 break ;
  871.             default:
  872.                 tprintf("some strange, unknown value\n") ;
  873.         }
  874.         return 0 ;
  875.     }
  876.     
  877.     switch(argv[1][0]) {
  878.         case 'n':
  879.         case 'N':
  880.             Nr_nfmode = NRNF_NOFILTER ;
  881.             break ;
  882.         case 'a':
  883.         case 'A':
  884.             Nr_nfmode = NRNF_ACCEPT ;
  885.             break ;
  886.         case 'r':
  887.         case 'R':
  888.             Nr_nfmode = NRNF_REJECT ;
  889.             break ;
  890.         default:
  891.             tprintf("modes are: none accept reject\n") ;
  892.             return -1 ;
  893.     }
  894.  
  895.     return 0 ;
  896. }
  897.  
  898. /* netrom network packet time-to-live initializer */
  899. static int
  900. donrttl(argc, argv,p)
  901. int argc ;
  902. char *argv[] ;
  903. void *p;
  904. {
  905.     return setshort(&Nr_ttl,"Time to live",argc,argv);
  906. }
  907.  
  908. /* verbose route broadcast */
  909. static int
  910. donrverbose(argc,argv,p)
  911. int argc ;
  912. char *argv[] ;
  913. void *p;
  914. {
  915.     return setbool(&Nr_verbose,"Verbose flag",argc,argv);
  916. }
  917. /* allow automatic derating of netrom routes on link failure */
  918. static int
  919. donrderate(argc,argv,p)
  920. int argc ;
  921. char *argv[] ;
  922. void *p;
  923. {
  924.     extern int Nr_derate;
  925.  
  926.     return setbool(&Nr_derate,"Derate flag",argc,argv);
  927. }
  928.  
  929. /* promiscuous acceptance of broadcasts */
  930. static int
  931. donrpromisc(argc,argv,p)
  932. int argc ;
  933. char *argv[] ;
  934. void *p;
  935. {
  936.     extern int Nr_promisc;
  937.  
  938.     return setbool(&Nr_promisc,"Promiscuous flag",argc,argv);
  939. }
  940.  
  941. /* Initiate a NET/ROM transport connection */
  942. static int
  943. donrconnect(argc,argv,p)
  944. int argc ;
  945. char *argv[] ;
  946. void *p;
  947. {
  948.     char *np ;
  949.     struct sockaddr_nr lsocket, fsocket;
  950.     char alias[AXBUF];
  951.     struct session *sp;
  952.  
  953.     /* Get a session descriptor */
  954.     if((sp = newsession(argv[1],NRSESSION,0)) == NULLSESSION) {
  955.         tputs(TooManySessions) ;
  956.         return 1 ;
  957.     }
  958.  
  959.     if((sp->s = socket(AF_NETROM,SOCK_SEQPACKET,0)) == -1){
  960.         tprintf("Can't create socket\n");
  961.         keywait(NULLCHAR,1);
  962.         freesession(sp);
  963.         return 1;
  964.     }
  965.     lsocket.nr_family = AF_NETROM;
  966.     /* Set up our local username, bind would use Mycall instead */
  967.     memcpy(lsocket.nr_addr.user,Nr4user,AXALEN);
  968.     /* Putting anything else than Mycall here will not work */
  969.     memcpy(lsocket.nr_addr.node,Nr_iface->hwaddr,AXALEN);
  970.     bind(sp->s,(char *)&lsocket,sizeof(struct sockaddr_nr));
  971.  
  972.     /* See if the requested destination could be an alias, and */
  973.     /* find and use it if it is.  Otherwise assume it is an ax.25 */
  974.     /* address. */
  975.     
  976.     if(putalias(alias,argv[1],0) != -1 &&
  977.         (np = find_nralias(alias)) != NULLCHAR) {
  978.         memcpy(fsocket.nr_addr.user,np,AXALEN) ;
  979.         memcpy(fsocket.nr_addr.node,np,AXALEN) ;
  980.     } else {        /* parse ax25 callsign */
  981.         /* Only the user callsign of the remote station is never used by */
  982.         /* NET/ROM, but it is needed for the psocket() call. */
  983.         setcall(fsocket.nr_addr.user,argv[1]);
  984.         setcall(fsocket.nr_addr.node,argv[1]);
  985.     }
  986.     fsocket.nr_family = AF_NETROM;
  987.     pax25(alias,fsocket.nr_addr.node);
  988.     return tel_connect(sp, (char *)&fsocket, sizeof(struct sockaddr_nr));
  989. }
  990.  
  991. /* Reset a net/rom connection abruptly */
  992. static int
  993. donrreset(argc,argv,p)
  994. int argc;
  995. char *argv[];
  996. void *p;
  997. {
  998.     struct nr4cb *cb ;
  999.  
  1000.     cb = (struct nr4cb *)htol(argv[1]);
  1001.     if(!nr4valcb(cb)){
  1002.         tprintf(Notval);
  1003.         return 1;
  1004.     }
  1005.     reset_nr4(cb);
  1006.     return 0;
  1007. }
  1008.  
  1009. /* Force retransmission on a net/rom connection */
  1010.  
  1011. static int
  1012. donrkick(argc,argv,p)
  1013. int argc;
  1014. char *argv[];
  1015. void *p;
  1016. {
  1017.     char nr4_str[9];
  1018.     struct nr4cb *cb ;
  1019.  
  1020.     strcpy(nr4_str,argv[1]);
  1021.     if(strlen(nr4_str) == 4)
  1022.         strcat(nr4_str,EightString);
  1023.     cb = (struct nr4cb *)htol(nr4_str);
  1024.  
  1025.     if(kick_nr4(cb) == -1) {
  1026.         tprintf(Notval);
  1027.         return 1;
  1028.     } else
  1029.         return 0;
  1030. }
  1031.  
  1032. /* netrom transport ACK delay timer */
  1033. static int
  1034. donracktime(argc, argv,p)
  1035. int argc ;
  1036. char *argv[] ;
  1037. void *p;
  1038. {
  1039.     return setlong(&Nr4acktime,"Ack delay time (ms)",argc,argv);
  1040. }
  1041.  
  1042. /* netrom transport choke timeout */
  1043. static int
  1044. donrchoketime(argc, argv,p)
  1045. int argc ;
  1046. char *argv[] ;
  1047. void *p;
  1048. {
  1049.     return setlong(&Nr4choketime,"Choke timeout (ms)",argc,argv);
  1050. }
  1051.  
  1052. /* netrom transport initial round trip time */
  1053.  
  1054. static int
  1055. donrirtt(argc, argv,p)
  1056. int argc ;
  1057. char *argv[] ;
  1058. void *p;
  1059. {
  1060.     return setlong(&Nr4irtt,"Initial RTT (ms)",argc,argv);
  1061. }
  1062.  
  1063. /* netrom transport receive queue length limit.  This is the */
  1064. /* threshhold at which we will CHOKE the sender. */
  1065.  
  1066. static int
  1067. donrqlimit(argc, argv,p)
  1068. int argc ;
  1069. char *argv[] ;
  1070. void *p;
  1071. {
  1072.     return setshort(&Nr4qlimit,"Queue limit (bytes)",argc,argv);
  1073. }
  1074.  
  1075. /* Display or change our NET/ROM username */
  1076. static int
  1077. donruser(argc,argv,p)
  1078. int argc;
  1079. char *argv[];
  1080. void *p;
  1081. {
  1082.     char buf[AXBUF];
  1083.  
  1084.     if(argc < 2){
  1085.         pax25(buf,Nr4user);
  1086.         tprintf("%s\n",buf);
  1087.         return 0;
  1088.     }
  1089.     if(setcall(Nr4user,argv[1]) == -1)
  1090.         return -1;
  1091.     Nr4user[ALEN] |= E;
  1092.     return 0;
  1093. }
  1094.  
  1095. /* netrom transport maximum window.  This is the largest send and */
  1096. /* receive window we may negotiate */
  1097.  
  1098. static int
  1099. donrwindow(argc, argv,p)
  1100. int argc ;
  1101. char *argv[] ;
  1102. void *p;
  1103. {
  1104.     return setshort(&Nr4window,"Window (frames)",argc,argv);
  1105. }
  1106.  
  1107. /* netrom transport maximum retries.  This is used in connect and */
  1108. /* disconnect attempts; I haven't decided what to do about actual */
  1109. /* data retries yet. */
  1110.  
  1111. static int
  1112. donrretries(argc, argv,p)
  1113. int argc ;
  1114. char *argv[] ;
  1115. void *p;
  1116. {
  1117.     return setshort(&Nr4retries,"Retry limit",argc,argv);
  1118. }
  1119.  
  1120. /* Display the status of NET/ROM connections */
  1121.  
  1122. static int
  1123. donrstatus(argc, argv,p)
  1124. int argc ;
  1125. char *argv[] ;
  1126. void *p;
  1127. {
  1128.     int i ;
  1129.     char nr4_str[9];
  1130.     struct nr4cb *cb ;
  1131.     char luser[AXBUF], ruser[AXBUF], node[AXBUF] ;
  1132.     
  1133.     if(argc < 2) {
  1134.         tprintf("     &CB Snd-W Snd-Q Rcv-Q     LUser      RUser @Node     State\n");
  1135.         for(i = 0 ; i < NR4MAXCIRC ; i++) {
  1136.             if((cb = Nr4circuits[i].ccb) == NULLNR4CB)
  1137.                 continue ;
  1138.             pax25(luser,cb->local.user) ;
  1139.             pax25(ruser,cb->remote.user) ;
  1140.             pax25(node,cb->remote.node) ;
  1141.             if(tprintf("%8lx   %3d %5d %5d %9s  %9s %-9s %s\n",
  1142.              ptol(cb), cb->nbuffered, len_q(cb->txq),
  1143.              len_p(cb->rxq), luser, ruser, node,
  1144.              Nr4states[cb->state]) == EOF)
  1145.                 break;
  1146.         }
  1147.         return 0 ;
  1148.     }
  1149.  
  1150.     strcpy(nr4_str,argv[1]);
  1151.     if(strlen(nr4_str) == 4)
  1152.         strcat(nr4_str,EightString);
  1153.     cb = (struct nr4cb *)htol(nr4_str) ;
  1154.     if(!nr4valcb(cb)) {
  1155.         tprintf(Notval) ;
  1156.         return 1 ;
  1157.     }
  1158.  
  1159.     donrdump(cb) ;
  1160.     return 0 ;
  1161. }
  1162.  
  1163. /* Dump one control block */
  1164.  
  1165. void
  1166. donrdump(cb)
  1167. struct nr4cb *cb ;
  1168. {
  1169.     char luser[AXBUF], ruser[AXBUF], node[AXBUF] ;
  1170.     unsigned seq ;
  1171.     struct nr4txbuf *b ;
  1172.     struct timer *t ;
  1173.  
  1174.     pax25(luser,cb->local.user) ;
  1175.     pax25(ruser,cb->remote.user) ;
  1176.     pax25(node,cb->remote.node) ;
  1177.  
  1178.     tprintf("Local: %s %d/%d Remote: %s @ %s %d/%d State: %s\n",
  1179.            luser, cb->mynum, cb->myid, ruser, node,
  1180.            cb->yournum, cb->yourid, Nr4states[cb->state]) ;
  1181.  
  1182.     tprintf("Window: %-5u Rxpect: %-5u RxNext: %-5u RxQ: %-5d %s\n",
  1183.            cb->window, uchar(cb->rxpected), uchar(cb->rxpastwin),
  1184.            len_p(cb->rxq), cb->qfull ? "RxCHOKED" : "") ;
  1185.  
  1186.     tprintf(" Unack: %-5u Txpect: %-5u TxNext: %-5u TxQ: %-5d %s\n",
  1187.            cb->nbuffered, uchar(cb->ackxpected), uchar(cb->nextosend),
  1188.            len_q(cb->txq), cb->choked ? "TxCHOKED" : "") ;
  1189.  
  1190.     tprintf("TACK: ") ;
  1191.     if(run_timer(&cb->tack))
  1192.         tprintf("%lu", read_timer(&cb->tack)) ;
  1193.     else
  1194.         tprintf("stop") ;
  1195.     tprintf("/%lu ms; ", dur_timer(&cb->tack)) ;
  1196.  
  1197.     tprintf("TChoke: ") ;
  1198.     if(run_timer(&cb->tchoke))
  1199.         tprintf("%lu", read_timer(&cb->tchoke)) ;
  1200.     else
  1201.         tprintf("stop") ;
  1202.     tprintf("/%lu ms; ", dur_timer(&cb->tchoke)) ;
  1203.  
  1204.     tprintf("TCD: ") ;
  1205.     if(run_timer(&cb->tcd))
  1206.         tprintf("%lu", read_timer(&cb->tcd)) ;
  1207.     else
  1208.         tprintf("stop") ;
  1209.     tprintf("/%lu ms", dur_timer(&cb->tcd)) ;
  1210.  
  1211.     if(run_timer(&cb->tcd))
  1212.         tprintf("; Tries: %u\n", cb->cdtries) ;
  1213.     else
  1214.         tprintf("\n") ;
  1215.  
  1216.     tprintf("Backoff Level %u SRTT %ld ms Mean dev %ld ms\n",
  1217.            cb->blevel, cb->srtt, cb->mdev) ;
  1218.  
  1219.     /* If we are connected and the send window is open, display */
  1220.     /* the status of all the buffers and their timers */
  1221.     
  1222.     if(cb->state == NR4STCON && cb->nextosend != cb->ackxpected) {
  1223.  
  1224.         tprintf("TxBuffers:  Seq  Size  Tries  Timer\n") ;
  1225.  
  1226.         for(seq = cb->ackxpected ;
  1227.              nr4between(cb->ackxpected, seq, cb->nextosend) ;
  1228.              seq = (seq + 1) & NR4SEQMASK) {
  1229.  
  1230.             b = &cb->txbufs[seq % cb->window] ;
  1231.             t = &b->tretry ;
  1232.  
  1233.             if(tprintf("            %3u   %3d  %5d  %lu/%lu\n",
  1234.              seq, len_p(b->data), b->retries + 1,
  1235.              read_timer(t), dur_timer(t))
  1236.              == EOF)
  1237.                 break;
  1238.         }
  1239.  
  1240.     }
  1241.  
  1242. }
  1243.  
  1244. /* netrom timers type - linear v exponential */
  1245. static
  1246. donrtype(argc,argv,p)
  1247. int argc ;
  1248. char *argv[] ;
  1249. void *p ;
  1250. {
  1251.     extern unsigned Nr_timertype;
  1252.  
  1253.     if(argc < 2) {
  1254.         tprintf("Netrom timer type is %s\n", Nr_timertype ? "linear" : "exponential" ) ;
  1255.         return 0 ;
  1256.     }
  1257.     
  1258.     switch(argv[1][0]) {
  1259.         case 'l':
  1260.         case 'L':
  1261.             Nr_timertype = 1 ;
  1262.             break ;
  1263.         case 'e':
  1264.         case 'E':
  1265.             Nr_timertype = 0 ;
  1266.             break ;
  1267.         default:
  1268.             tprintf("use: netrom timertype [linear|exponential]\n") ;
  1269.             return -1 ;
  1270.     }
  1271.  
  1272.     return 0 ;
  1273. }
  1274.  
  1275. static
  1276. dominquality(argc,argv,p)
  1277. int argc ;
  1278. char *argv[] ;
  1279. void *p ;
  1280. {
  1281.     unsigned val ;
  1282.     extern unsigned Nr_autofloor;
  1283.  
  1284.     if(argc < 2) {
  1285.         tprintf("%u\n", Nr_autofloor) ;
  1286.         return 0 ;
  1287.     }
  1288.  
  1289.     val = atoi(argv[1]) ;
  1290.  
  1291.     if(val == 0 || val > 255 ) {
  1292.         tprintf("The minimum acceptable quality must be 1 to 255\n") ;
  1293.         return 1 ;
  1294.     }
  1295.     
  1296.     Nr_autofloor = val ;
  1297.  
  1298.     return 0 ;
  1299. }
  1300. #ifdef notdef
  1301. int
  1302. donrload(argc,argv,p)
  1303. int argc ;
  1304. char *argv[] ;
  1305. void *p;
  1306. {
  1307.     char buff[255];
  1308.     FILE *fn;
  1309.     time_t now, prev;
  1310.     long t1,t2;
  1311.     int quality,obso,i,j;
  1312.     char alias[12],dest[12],iface[12],neighbor[12],type[3],*ptr;
  1313.     
  1314.     time(&now);
  1315.     if((fn = fopen(Netromfile,READ_TEXT)) == NULLFILE){
  1316.         tprintf("Can't open netrom save file!\n");
  1317.         return 1;
  1318.     }
  1319.  
  1320.     fgets(buff,255,fn);     /* read the timestamp */
  1321.     if(feof(fn)){
  1322.         fclose(fn);
  1323.         return 1;
  1324.     }
  1325.     if((strncmp(buff,"time = ",7))!= 0){
  1326.         tprintf("Wrong node file content\n");
  1327.         fclose(fn);
  1328.         return 1;
  1329.     }
  1330.     sscanf(buff,"time =%ld",&prev);
  1331.     tprintf("now = %ld , prev = %ld\n",now,prev);
  1332.     if(prev >= now){
  1333.         tprintf("You traveled back in time!!\n");
  1334.         fclose(fn);
  1335.         return 1;
  1336.     }
  1337.     t1 = now - prev;
  1338.     t2 = dur_timer(&Obsotimer)/1000L;
  1339.     j = t1 / t2;            /* recalculate obsolete count */
  1340.     tprintf("%ld seconds are past ( %d obsolete scans)\n",t1,j);
  1341.  
  1342.     fgets(buff,255,fn);
  1343.     while(!feof(fn)){
  1344.         if((ptr = strchr(buff,':')) == 0){
  1345.             sscanf(buff,"%s%s%i%i%s%s"
  1346.                 ,dest,type,&quality,&obso,iface,neighbor);
  1347.             alias[0] = '\0';
  1348.         } else {        
  1349.             *ptr = ' ';
  1350.             sscanf(buff,"%s%s%s%i%i%s%s"
  1351.                 ,alias,dest,type,&quality,&obso,iface,neighbor);
  1352.         }
  1353.         /* find interface */
  1354.         for(i = 0 ; i < Nr_numiface ; i++)
  1355.             if(!strcmp(Nrifaces[i].iface->name,iface))
  1356.                 break ;
  1357.         if(i == Nr_numiface) {
  1358.             tprintf("Interface \"%s\" not found\n",iface) ;
  1359.         }
  1360.     
  1361.         /* get and check quality value */
  1362.         if(quality  > 255) {
  1363.             tprintf("maximum route quality is 255\n") ;
  1364.         }
  1365.  
  1366. /*              nr_routeadd(alias,dest,i,quality,neighbor,1,0) ; */
  1367.         fgets(buff,255,fn);
  1368.     }
  1369.     fclose(fn);
  1370.     return 0;
  1371. }
  1372.  
  1373. int
  1374. donrsave(argc,argv,p)
  1375. int argc ;
  1376. char *argv[] ;
  1377. void *p;
  1378. {
  1379.     register struct nrroute_tab *rp ;
  1380.     register struct nr_bind *bp ;
  1381.     register struct nrnbr_tab *np ;
  1382.     char dest[AXALEN] ;
  1383.     char neighbor[AXBUF] ;
  1384.     register int i;
  1385.     char buf[16] ;
  1386.     char *cp ;
  1387.     FILE *fn;
  1388.     time_t now;
  1389.     
  1390. #ifdef __TURBOC__
  1391.     if((fn = fopen(Netromfile,"wt+")) == NULLFILE){
  1392. #else
  1393.     if((fn = fopen(Netromfile,"w+")) == NULLFILE){
  1394. #endif
  1395.         tprintf("Can't write netrom save file!\n");
  1396.         return 1;
  1397.     }
  1398.     time(&now);
  1399.     fprintf(fn,"time = %ld\n",now);
  1400.     for(i = 0 ; i < NRNUMCHAINS ; i++){
  1401.         for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rp->next){
  1402.             strcpy(buf,rp->alias) ;
  1403.             /* remove trailing spaces */
  1404.             if((cp = strchr(buf,' ')) == NULLCHAR)
  1405.                 cp = &buf[strlen(buf)] ;
  1406.             if(cp != buf)           /* don't include colon for null alias */
  1407.                 *cp++ = ':' ;
  1408.             for(bp = rp->routes; bp != NULLNRBIND; bp = bp->next) {
  1409.                 pax25(cp,rp->call) ;
  1410.                 fprintf(fn,"%-16s  ",buf) ;
  1411.                 np = bp->via ;
  1412.                 if(fprintf(fn,"%1s %3d  %3d  %-8s  %s\n",
  1413.                       (bp->flags & NRB_PERMANENT ? "P" :
  1414.                       bp->flags & NRB_RECORDED ? "R" : "X"),
  1415.                       bp->quality,bp->obsocnt,
  1416.                       Nrifaces[np->iface].iface->name,
  1417.                       pax25(neighbor,np->call)) == EOF)
  1418.                    break;
  1419.             }
  1420.         }
  1421.     }
  1422.     fclose(fn);
  1423.     return 0;
  1424. }
  1425. #endif
  1426.