/* net/rom user command processing
 * Copyright 1989 by Daniel M. Frank, W9NK.  Permission granted for
 * non-commercial distribution only.
 */
/* Mods by G1EMM, PA0GRI and WG7J */
  
#include <ctype.h>
#include <time.h>
#ifdef MSDOS
#include <dos.h>
#endif
#include "global.h"
#ifdef NETROM
#include "mbuf.h"
#include "proc.h"
#include "ax25.h"
#include "mailbox.h"
#include "netrom.h"
#include "nr4.h"
#include "timer.h"
#include "iface.h"
#include "pktdrvr.h"
#include "lapb.h"
#include "cmdparse.h"
#include "session.h"
#include "socket.h"
#include "commands.h"
#include "files.h"
  
int G8bpq;
int Nr_hidden = 1;
unsigned Nr_sorttype = 1;
char Nr4user[AXALEN];
  
char *Nr4states[] = {
    "Disconnected",
    "Conn Pending",
    "Connected",
    "Disc Pending",
    "Listening"
} ;
  
char *Nr4reasons[] = {
    "Normal",
    "By Peer",
    "Timeout",
    "Reset",
    "Refused"
} ;
static int dobcnodes __ARGS((int argc,char *argv[],void *p));
static int dobcpoll __ARGS((int argc,char *argv[],void *p));
static int dointerface __ARGS((int argc,char *argv[],void *p));
static int donfadd __ARGS((int argc,char *argv[],void *p));
static int donfdrop __ARGS((int argc,char *argv[],void *p));
static int donfdump __ARGS((void));
static int donfmode __ARGS((int argc,char *argv[],void *p));
static int donodefilter __ARGS((int argc,char *argv[],void *p));
static int donodetimer __ARGS((int argc,char *argv[],void *p));
extern int donralias __ARGS((int argc,char *argv[],void *p));
static int donracktime __ARGS((int argc,char *argv[],void *p));
static int donrmycall __ARGS((int argc,char *argv[],void *p));
static int donrchoketime __ARGS((int argc,char *argv[],void *p));
static int donrconnect __ARGS((int argc,char *argv[],void *p));
static int donrirtt __ARGS((int argc,char *argv[],void *p));
static int donrkick __ARGS((int argc,char *argv[],void *p));
static int dorouteadd __ARGS((int argc,char *argv[],void *p));
static int doroutedrop __ARGS((int argc,char *argv[],void *p));
static int donrqlimit __ARGS((int argc,char *argv[],void *p));
static int donrreset __ARGS((int argc,char *argv[],void *p));
static int donrretries __ARGS((int argc,char *argv[],void *p));
static int donrroute __ARGS((int argc,char *argv[],void *p));
int donrstatus __ARGS((int argc,char *argv[],void *p));
static int donrsave __ARGS((int argc,char *argv[],void *p));
static int donrload __ARGS((int argc,char *argv[],void *p));
static int donrttl __ARGS((int argc,char *argv[],void *p));
static int donruser __ARGS((int argc,char *argv[],void *p));
static int donrwindow __ARGS((int argc,char *argv[],void *p));
void doobsotick __ARGS((void));
static int doobsotimer __ARGS((int argc,char *argv[],void *p));
static int dominquality __ARGS((int argc,char *argv[],void *p));
static int donrtype __ARGS((int argc,char *argv[],void *p));
static int donrpromisc __ARGS((int argc,char *argv[],void *p));
static int donrderate __ARGS((int argc,char *argv[],void *p));
static int doroutesort __ARGS((int argc,char *argv[],void *p));
static int donrhidden __ARGS((int argc,char *argv[],void *p));
static int donrg8bpq __ARGS((int argc,char *argv[],void *p));
static void doallinfo __ARGS((void));
static struct iface *FindNrIface __ARGS((char *name));
  
int donrneighbour __ARGS((int argc,char *argv[],void *p));
  
extern int donr4tdisc __ARGS((int argc,char *argv[],void *p));
extern struct nr_bind *find_best __ARGS((struct nr_bind *list,unsigned obso));
extern struct nr_bind *find_bind __ARGS((struct nr_bind *list,struct nrnbr_tab *np));
extern void nrresetlinks(struct nrroute_tab *rp);
  
static struct cmds DFAR Nrcmds[] = {
    "acktime",      donracktime,    0, 0,   NULLCHAR,
    "alias",    donralias,  0, 0,   NULLCHAR,
    "bcnodes",  dobcnodes,  0, 2,   "netrom bcnodes <iface>",
    "bcpoll",   dobcpoll,   0, 2,   "netrom bcpoll <iface>",
#ifdef NETROMSESSION
    "connect",  donrconnect, 1024, 2,   "netrom connect <node>",
#endif
    "call",     donrmycall,   0, 0,   NULLCHAR,
    "choketime",    donrchoketime,  0, 0,   NULLCHAR,
    "derate",       donrderate,     0, 0,   NULLCHAR,
#ifdef G8BPQ
    "g8bpq",        donrg8bpq,      0, 0,   NULLCHAR,
#endif
    "hidden",   donrhidden, 0, 0,   NULLCHAR,
    "interface",    dointerface,    0, 0, NULLCHAR,
    "irtt",         donrirtt,       0, 0,   NULLCHAR,
    "kick",         donrkick,       0, 2,   "netrom kick <&nrcb>",
    "load",         donrload,       0, 0,   NULLCHAR,
    "minquality",   dominquality,   0, 0,   NULLCHAR,
    "neighbour",    donrneighbour,  0, 0,   NULLCHAR,
    "nodefilter",   donodefilter,   0, 0,   NULLCHAR,
    "nodetimer",    donodetimer,    0, 0,   NULLCHAR,
    "obsotimer",    doobsotimer,    0, 0,   NULLCHAR,
    "promiscuous",  donrpromisc,    0, 0,   NULLCHAR,
    "qlimit",       donrqlimit,     0, 0,   NULLCHAR,
    "route",    donrroute,  0, 0,   NULLCHAR,
    "reset",        donrreset,      0, 2,   "netrom reset <&nrcb>",
    "retries",      donrretries,    0, 0,   NULLCHAR,
    "status",       donrstatus,     0, 0,   NULLCHAR,
    "save",         donrsave,       0, 0,   NULLCHAR,
#if defined(NETROMSESSION) && defined(SPLITSCREEN)
    "split",    donrconnect, 1024, 2,   "netrom split <node>",
#endif
    "timertype",    donrtype,   0, 0,   NULLCHAR,
    "ttl",          donrttl,        0, 0,   NULLCHAR,
#ifdef NR4TDISC
    "tdisc",    donr4tdisc, 0, 0,   NULLCHAR,
#endif
    "user",     donruser,   0, 0,   NULLCHAR,
    "window",       donrwindow,     0, 0,   NULLCHAR,
    NULLCHAR,
} ;
  
  
struct timer Nodetimer ; /* timer for nodes broadcasts */
struct timer Obsotimer ; /* timer for aging routes */
  
/* Command multiplexer */
int
donetrom(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return subcmd(Nrcmds,argc,argv,p) ;
}
  
static struct cmds Routecmds[] = {
    "add",  dorouteadd,     0, 6,
    "netrom route add <alias> <destination> <interface> <quality> <neighbor>",
    "drop", doroutedrop, 0, 4,
    "netrom route drop <destination> <neighbor> <interface>",
    "info", dorouteinfo, 0, 0,
    "",
    "sort", doroutesort, 0, 1,
    "",
    NULLCHAR,
} ;
  
/* Route command multiplexer */
static int
donrroute(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    if(argc < 2) {
        doroutedump() ;
        return 0 ;
    }
    return subcmd(Routecmds,argc,argv,p) ;
}
  
/* Code to sort Netrom node listing
 * D. Crompton  2/92
 * Dump a list of known netrom routes in
 * sorted order determined by sort
 * flag - default = sort by alias
 */
  
int
doroutedump()
{
    extern unsigned Nr_sorttype;
    register struct nrroute_tab *rp ;
    register int i,j,k, column ;
    char buf[17] ;
    char *cp,*temp ;
  
    column = 1 ;
  
    for(i = 0,j=0 ; i < NRNUMCHAINS ; i++)
        for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ;j++,rp = rp->next);
  
#define RTSIZE 17
  
    if (j) {
        /* Allocate maximum size */
        if ((temp = malloc ((unsigned)j*RTSIZE)) == NULLCHAR) {
            tputs(Nospace);
            return -1;
        }
  
        for(i = 0,j=0,k=0 ; i < NRNUMCHAINS ; i++) {
            for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rp->next) {
                if(!Nr_hidden && *rp->alias == '#')
                    continue;
                if (Nr_sorttype) {
                    strcpy(buf,rp->alias) ;
                    /* remove trailing spaces */
                    if((cp = strchr(buf,' ')) == NULLCHAR)
                        cp = &buf[strlen(buf)] ;
                    if(cp != buf)   /* don't include colon for null alias */
                        *cp++ = ':' ;
                    pax25(cp,rp->call) ;
                } else {
                    pax25(buf,rp->call);
                    cp=&buf[strlen(buf)];
                    *cp++=':';
                    strcpy(cp,rp->alias);
                }
                sprintf(&temp[k],"%-16.16s",buf);
                k+=RTSIZE;
                j++;    /* number actually shown */
            }
        }
  
#ifdef UNIX
        qsort(temp,(size_t)j,RTSIZE,(int (*)__FARGS((const void*,const void*))) strcmp);
#else
        qsort(temp,(size_t)j,RTSIZE,(int (*) ()) strcmp);
#endif
  
        for (i=0,k=0;i<j;i++,k+=RTSIZE) {
            tprintf("%-16s  ",&temp[k]) ;
            if(column++ == 4) {
                if(tputc('\n') == EOF) {
                    free(temp);
                    return 0;
                }
                column = 1 ;
            }
        }
  
        if(column != 1)
            tputc('\n') ;
        free(temp);
    }
    return 0 ;
}
  
/* netrom Route Dump Sort - ALIAS or CALL first */
static int
doroutesort(argc,argv,p)
int argc ;
char *argv[] ;
void *p ;
{
    extern unsigned Nr_sorttype;
  
    if(argc < 2) {
        tprintf("Netrom Sort by %s\n", Nr_sorttype ? "Alias" : "Call" ) ;
        return 0 ;
    }
  
    switch(argv[1][0]) {
        case 'A':
        case 'a':
            Nr_sorttype = 1 ;
            break ;
        case 'C':
        case 'c':
            Nr_sorttype = 0 ;
            break ;
        default:
            tputs("usage: netrom sort [alias|call]\n") ;
            return -1 ;
    }
  
    return 0 ;
}
  
/* Print detailed information on  ALL routes  (sorted) */
/*  D. Crompton */
static void
doallinfo()
{
    extern unsigned Nr_sorttype;
    register struct nrroute_tab *rp ;
    register struct nr_bind *bp ;
    register struct nrnbr_tab *np ;
    char neighbor[AXBUF] ;
    char buf[17];
    char *cp,*temp;
    int i,j,k, flow_tmp;
  
    flow_tmp=Current->flowmode;
    Current->flowmode=1;
  
    for (i=0,j=0;i<NRNUMCHAINS;i++)
        for (rp=Nrroute_tab[i];rp!=NULLNRRTAB;rp= rp->next)
            for(bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next,j++);
  
#define STRSIZE 72
  
    if(j) {
        if ((temp = malloc((unsigned)j*STRSIZE)) == NULLCHAR) {
            tputs(Nospace);
            return;
        }
  
        for (i=0,k=0;i<NRNUMCHAINS;i++)
            for (rp=Nrroute_tab[i];rp!=NULLNRRTAB;rp= rp->next)
                for(bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next,k+=STRSIZE) {
                    np = bp->via ;
                    if (Nr_sorttype) {
                        strcpy(buf,rp->alias) ;
                        if((cp = strchr(buf,' ')) == NULLCHAR)
                            cp = &buf[strlen(buf)] ;
                        if(cp != buf)
                            *cp++ = ':' ;
                        pax25(cp,rp->call) ;
                    } else {
                        pax25(buf,rp->call);
                        cp=&buf[strlen(buf)];
                        *cp++=':';
                        strcpy(cp,rp->alias);
                    }
                    /* NOTE:
                     * IF you change the size of this sprintf buffer,
                     * ALSO change the size of the STRSIZE define above !!!
                     */
                    sprintf(&temp[k],"%-16s %s %3d %3d %-8s %-9s %c %-5u",buf,
                    rp->flags & G8BPQ_NODEMASK ? "(BPQ)" : "     ",
                    bp->quality,bp->obsocnt,
                    np->iface->name,
                    pax25(neighbor,np->call),
                    (bp->flags & NRB_PERMANENT ? 'P' :
                    bp->flags & NRB_RECORDED ? 'R' : 'B'),bp->usage);
                    if(rp->flags & G8BPQ_NODETTL) {
                        sprintf(buf,"    %u\n",rp->hops);
                        strcat(&temp[k],buf);
                    } else
                        strcat(&temp[k],"\n");
                }
#ifdef UNIX
        qsort(temp,(size_t)j,STRSIZE,(int (*)__FARGS((const void*,const void*))) strcmp);
#else
        qsort(temp,(size_t)j,STRSIZE,(int (*) ()) strcmp);
#endif
        tprintf("%-16s      Qual Obs Port    Neighbor Type Usage Hops\n",
            (Nr_sorttype?"Alias:Node":"Node:Alias"));
        for(i=0,k=0;i<j;i++,k+=STRSIZE)
            if (tputs(&temp[k])==EOF)
                break;
        free(temp);
        Current->flowmode=flow_tmp;
    }
}
  
/* Find the interface, and check if it is active for netrom */
static struct iface *FindNrIface(char *name) {
    struct iface *ifp;
  
    if((ifp = if_lookup(name)) == NULLIF) {
        tprintf(Badinterface,name);
        return NULLIF;
    }
    if(!(ifp->flags & IS_NR_IFACE)) {
        tprintf("%s is not active for netrom\n",name);
        return NULLIF;
    }
    return ifp;
}
  
  
/* print detailed information on an individual route
 * Shows alias as well - WG7J
 */
int
dorouteinfo(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    char *cp;
    register struct nrroute_tab *rp ;
    register struct nrroute_tab *npp;
    struct nr_bind *bp ;
    struct nrnbr_tab *np ;
    char destbuf[AXBUF];
    char neighbor[AXBUF] ;
    char alias[AXALEN];
    char buf[AXALEN];
    char nb_alias[AXALEN];
    int print_header=1;
    int16 rhash;
  
    if(argc == 1) {
        doallinfo();
        return 0;
    }
  
    putalias(alias,argv[1],0);
    strupr(argv[1]);    /*make sure it's upper case*/
    if((rp = find_nrboth(alias,argv[1])) == NULLNRRTAB){
        /*no such call or node alias*/
        tputs("no such node\n\n");
        return 0;
    }
    /*copy the real alias*/
    strcpy(buf,rp->alias) ;
    if((cp = strchr(buf,' ')) != NULLCHAR)
        *cp = '\0';
  
    for(bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next) {
        np = bp->via ;
        /* now we have to find the alias of the neighbour used
         * so we can print that as well!
         */
        rhash = nrhash(np->call);
        for(npp=Nrroute_tab[rhash];npp!=NULLNRRTAB;npp=npp->next)
            if(addreq(npp->call,np->call))
                break;
        if (npp==NULLNRRTAB)  /* K5JB */
            strcpy(nb_alias,"##gone");
        else {     /* found; remove trailing spaces */
            strcpy(nb_alias,npp->alias) ;
            if((cp = strchr(nb_alias,' ')) != NULLCHAR)
                *cp = '\0';
        }
        if(print_header) {
            print_header = 0;
            tputs("      Node     "
#ifdef G8BPQ
            "      "
#endif
            "      Neighbour"
#ifdef G8BPQ
            "      "
#endif
            "    Port   Qual Obs Type Usage"
#ifdef G8BPQ
            " Hops Irtt"
#endif
            "\n");
  
            tprintf("%6s:%-9s "
#ifdef G8BPQ
            "%s "
#endif
            ,buf,pax25(destbuf,rp->call)
#ifdef G8BPQ
            ,rp->flags & G8BPQ_NODEMASK ? "(BPQ)" : "     "
#endif
            );
        } else
            tputs("                       ");
        tprintf("%6s:%-9s "
#ifdef G8BPQ
        "%s "
#endif
        "%-6s %3d  %3d  %c   %-5u",
        nb_alias,pax25(neighbor,np->call),
#ifdef G8BPQ
        (npp && npp->flags & G8BPQ_NODEMASK) ? "(BPQ)" : "     ",
#endif
        np->iface->name,
        bp->quality,bp->obsocnt,
        bp->flags & NRB_PERMANENT ? 'P' : \
        (bp->flags & NRB_RECORDED ? 'R' : 'B'),bp->usage );
#ifdef G8BPQ
        if(npp && (npp->hops || npp->irtt)) {
            if(npp->hops)
                tprintf(" %3u",npp->hops);
            else
                tputs(  "    ");
            if(npp->irtt)
                tprintf("  %u",npp->irtt);
        }
#endif
        tputc('\n');
    }
    tputc('\n');
    return 0 ;
}
  
int
donrneighbour(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int i,printheader=1;
    struct nrnbr_tab *np;
    struct nrroute_tab *rp;
    struct nr_bind *bind;
    int16 rhash;
    int justused;
    char tmp[AXBUF];
    char alias[AXALEN];
    char *cp;
    int percentage;
    int quality = 0;
    int obsocnt = 0;
#ifdef G8BPQ
    unsigned int irtt = 0;
#endif
  
    for(i=0;i<NRNUMCHAINS;i++) {    /*loop through all chains of neighbours*/
        for(np=Nrnbr_tab[i];np!=NULLNTAB;np=np->next) {
            /* If the interface is hidden, then do not show the route */
/*
            if(np->iface->flags & HIDE_PORT && Curproc->input != Command->input)
                continue;
 */
            if(printheader) {
                tputs("Routes :\n" \
                "   Neighbour             Port  Qual Obs Dest Tries Retries Perc"
#ifdef G8BPQ
                " Irtt"
#endif
                "\n");
                printheader = 0;
            }
            /* has this one been used recently ? */
            if((secclock() - np->lastsent) < 60)
                justused = 1;
            else
                justused = 0;
  
            /* now we have to find the alias of this neighbour
             * so we can print that as well!
             */
            rhash = nrhash(np->call);
            for(rp=Nrroute_tab[rhash];rp!=NULLNRRTAB;rp=rp->next)
                if(addreq(rp->call,np->call))
                    break;
  
            if(rp != NULLNRRTAB) {
                /* found, now remove trailing spaces */
                strcpy(alias,rp->alias) ;
                if((cp = strchr(alias,' ')) != NULLCHAR)
                    *cp = '\0';
                /*find the quality for this neighbour*/
                bind = find_bind(rp->routes,np);
                if(bind) {
                    quality = bind->quality;
                    obsocnt = bind->obsocnt;
                } else  // should never happen !
                    quality = obsocnt = 0;
#ifdef G8BPQ
                irtt = rp->irtt;
#endif
            } else {
                strcpy(alias,"##TEMP");
            }
            if(np->tries)
                percentage = (int)( (((long)(np->tries)) * 100L) /
                ((long)((long)np->tries + (long)np->retries)) );
            else
                percentage = 0;
            /* print it all out */
            tprintf("%c %6s:%-9s %s %-6s %3d %3d  %3d %-5u %-5u   %2d %%",
            (justused) ? '>' : ' ',
            alias,pax25(tmp,np->call),
            (rp && rp->flags & G8BPQ_NODEMASK) ? "(BPQ)" : "     ",
            np->iface->name,
            quality,obsocnt,np->refcnt,np->tries,np->retries,percentage);
#ifdef G8BPQ
            if(irtt)
                tprintf(" %u\n",irtt);
            else
#endif
                tputc('\n');
        }
    }
    if(!printheader)
        tputc('\n');
    return 0;
}
  
/* define the netrom call,
 * this simply changes the interface linkaddress!
 * but is a little easier to use...
 */
static int
donrmycall(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    int len;
    char tmp[AXBUF];
  
    if(Nr_iface == NULLIF) {
        tputs("Attach netrom interface first\n") ;
        return 1 ;
    }
  
    if(argc < 2) {
        if (Nr_iface->hwaddr == NULLCHAR)
            tputs("not set\n");
        else
            tprintf("%s\n",pax25(tmp,Nr_iface->hwaddr));
    } else {
        if( (len=strlen(argv[1])) > (AXBUF - 1)) {
            tputs("too long\n");
            return 1;
        }
        if(Nr_iface->hwaddr != NULLCHAR)
            free(Nr_iface->hwaddr);
        Nr_iface->hwaddr = mallocw(AXALEN);
        setcall(Nr_iface->hwaddr,argv[1]);
#ifdef MAILBOX
        setmbnrid();
#endif
    }
    return 0;
}
  
/* make an interface available to net/rom */
/* arguments are:
 * argv[0] - "interface"
 * argv[1] - "iface" , the interface name
 * argv[2] - "quality", the interface broadcast quality
 * argv[3] - "n" or "v", to override the default (verbose)
 *            n = never broadcast verbose
 *            v = always broadcast verbose
 */
static
int
dointerface(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    struct iface *ifp ;
    int i,mtu ;
  
    if(Nr_iface == NULLIF) {
        tputs("Attach netrom interface first\n") ;
        return 1 ;
    }
  
    if(argc < 3) {
        i = 0;
        for(ifp=Ifaces;ifp;ifp=ifp->next) {
            if(ifp->flags & IS_NR_IFACE){
                if(!i) {
                    i = 1;
                    tputs("Iface  Qual MinBcQual\n");
                }
                tprintf("%-6s %-3d    %d\n",
                ifp->name,ifp->quality,ifp->nr_autofloor);
            }
        }
        return 0;
    }
  
    if((ifp = if_lookup(argv[1])) == NULLIF){
        tprintf(Badinterface,argv[1]);
        return 1;
    }
  
    if(ifp->type != CL_AX25){
        tprintf("Interface %s is not NETROM compatible\n",argv[1]);
        return 1;
    }
  
    /* activate the interface */
    ifp->flags |= IS_NR_IFACE;
  
    /* set quality */
    if((ifp->quality=atoi(argv[2])) > 255) /*Maximum quality possible*/
        ifp->quality = 255;
    /*check to see if quality is not 0 */
    if(ifp->quality <= 0)
        ifp->quality = 1;
  
    /* Set the minimum broadcast quality */
    ifp->nr_autofloor = Nr_autofloor;
    if(argc > 3)
        ifp->nr_autofloor = atoi(argv[3]);
  
    /* Check, and set the NETROM MTU - WG7J */
    if((mtu = ifp->ax25->paclen - 20) < Nr_iface->mtu)
        Nr_iface->mtu = mtu;
  
    /* Poll other nodes on this interface */
    nr_bcpoll(ifp);
  
    return 0 ;
}
  
  
/* convert a null-terminated alias name to a blank-filled, upcased */
/* version.  Return -1 on failure. */
int
putalias(to,from,complain)
register char *to, *from ;
int complain ;
{
    int len, i ;
  
    if((len = strlen(from)) > ALEN) {
        if(complain)
            tputs("alias too long - six characters max\n") ;
        return -1 ;
    }
  
    for(i = 0 ; i < ALEN ; i++) {
        if(i < len) {
            if(islower(*from))
                *to++ = toupper(*from++) ;
            else
                *to++ = *from++ ;
        }
        else
            *to++ = ' ' ;
    }
  
    *to = '\0' ;
    return 0 ;
}
  
/* Add a route */
static int
dorouteadd(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    char alias[AXALEN] ;
    char dest[AXALEN] ;
    unsigned quality ;
    char neighbor[AXALEN] ;
    struct iface *ifp;
    int naddr ;
  
    /* format alias (putalias prints error message if necessary) */
    if(putalias(alias,argv[1],1) == -1)
        return -1 ;
  
    /* format destination callsign */
    if(setcall(dest,argv[2]) == -1) {
        tputs("bad destination callsign\n") ;
        return -1 ;
    }
  
    /* find interface */
    if((ifp = FindNrIface(argv[3])) == NULLIF) {
        return -1;
    }
  
    /* get and check quality value */
    if((quality = atoi(argv[4])) > 255) {
        tputs("maximum route quality is 255\n") ;
        return -1 ;
    }
  
    /* Change from 871225 -- no digis in net/rom table */
    naddr = argc - 5 ;
    if(naddr > 1) {
        tputs("Use the ax25 route command to specify digipeaters\n") ;
        return -1 ;
    }
  
    /* format neighbor address string */
    setcall(neighbor,argv[5]) ;
  
    nr_routeadd(alias,dest,ifp,quality,neighbor,1,0) ;
  
    /* Now see if we have a route to this neighbour,
     * if not add the implied route as a temporary one.
     * 930527 - WG7J
     */
    if(find_nrroute(neighbor) == NULLNRRTAB)
        nr_routeadd("##TEMP",neighbor,ifp,quality,neighbor,0,1);
  
    return 0;
}
  
  
/* drop a route */
static int
doroutedrop(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    char dest[AXALEN], neighbor[AXALEN] ;
    struct iface *ifp;
  
    /* format destination and neighbor callsigns */
    if(setcall(dest,argv[1]) == -1) {
        tputs("bad destination callsign\n") ;
        return -1 ;
    }
    if(setcall(neighbor,argv[2]) == -1) {
        tputs("bad neighbor callsign\n") ;
        return -1 ;
    }
  
    /* find interface */
    if((ifp = FindNrIface(argv[3])) == NULLIF) {
        return -1;
    }
  
    return nr_routedrop(dest,neighbor,ifp) ;
}
  
/* Broadcast nodes list on named interface. */
static int
dobcnodes(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    struct iface *ifp;
  
    /* find interface */
    if((ifp = FindNrIface(argv[1])) == NULLIF) {
        return -1;
    }
    nr_bcnodes(ifp) ;
    return 0;
}
  
/* Poll nodes for routes on named interface. - WG7J */
static int
dobcpoll(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    struct iface *ifp;
  
    /* find interface */
    if((ifp = FindNrIface(argv[1])) == NULLIF) {
        return -1;
    }
    nr_bcpoll(ifp) ;
    return 0;
}
  
/* Set outbound node broadcast interval */
static int
donodetimer(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if(argc < 2){
        tprintf("Nodetimer %lu/%lu seconds\n",
        read_timer(&Nodetimer)/1000L,
        dur_timer(&Nodetimer)/1000L);
        return 0;
    }
    stop_timer(&Nodetimer) ;        /* in case it's already running */
    Nodetimer.func = (void (*)__ARGS((void*)))donodetick;/* what to call on timeout */
    Nodetimer.arg = NULLCHAR;               /* dummy value */
    set_timer(&Nodetimer,atoi(argv[1])*1000L);      /* set timer duration */
    start_timer(&Nodetimer);                /* and fire it up */
    return 0;
}
  
void
donodetick()
{
    struct iface *ifp;
  
    for(ifp=Ifaces;ifp;ifp=ifp->next)
        if(ifp->flags & IS_NR_IFACE)
            nr_bcnodes(ifp) ;
  
    /* Restart timer */
    start_timer(&Nodetimer) ;
}
  
/* Set timer for aging routes */
static int
doobsotimer(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if(argc < 2){
        tprintf("Obsotimer %lu/%lu seconds\n",
        read_timer(&Obsotimer)/1000L,
        dur_timer(&Obsotimer)/1000L);
        return 0;
    }
    stop_timer(&Obsotimer) ;        /* just in case it's already running */
    Obsotimer.func = (void (*)__ARGS((void*)))doobsotick;/* what to call on timeout */
    Obsotimer.arg = NULLCHAR;               /* dummy value */
    set_timer(&Obsotimer,atoi(argv[1])*1000L);      /* set timer duration */
    start_timer(&Obsotimer);                /* and fire it up */
    return 0;
}
  
  
/* Go through the routing table, reducing the obsolescence count of
 * non-permanent routes, and purging them if the count reaches 0
 */
void
doobsotick()
{
    register struct nrnbr_tab *np ;
    register struct nrroute_tab *rp, *rpnext ;
    register struct nr_bind *bp, *bpnext ;
    int i ;
  
    for(i = 0 ; i < NRNUMCHAINS ; i++) {
        for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rpnext) {
            rpnext = rp->next ;     /* save in case we free this route */
        /* Check all bindings for this route */
            for(bp = rp->routes ; bp != NULLNRBIND ; bp = bpnext) {
                bpnext = bp->next ;     /* in case we free this binding */
                if(bp->flags & NRB_PERMANENT)   /* don't age these */
                    continue ;
                if(--bp->obsocnt == 0) {                /* time's up! */
                    if(bp->next != NULLNRBIND)
                        bp->next->prev = bp->prev ;
                    if(bp->prev != NULLNRBIND)
                        bp->prev->next = bp->next ;
                    else
                        rp->routes = bp->next ;
                    rp->num_routes-- ;                      /* one less binding */
                    np = bp->via ;                          /* find the neighbor */
                    free((char *)bp) ;                              /* now we can free the bind */
                    /* Check to see if we can free the neighbor */
                    if(--np->refcnt == 0) {
                        if(np->next != NULLNTAB)
                            np->next->prev = np->prev ;
                        if(np->prev != NULLNTAB)
                            np->prev->next = np->next ;
                        else {
                            Nrnbr_tab[nrhash(np->call)] = np->next ;
                        }
                        free((char *)np) ;      /* free the storage */
                    }
                }
            }
            if(rp->num_routes == 0) {               /* did we free them all? */
                if(rp->next != NULLNRRTAB)
                    rp->next->prev = rp->prev ;
                if(rp->prev != NULLNRRTAB)
                    rp->prev->next = rp->next ;
                else
                    Nrroute_tab[i] = rp->next ;
        /* No more routes left !
         * We should close/reset any netrom connections
         * still idling for this route ! - WG7J
         */
                nrresetlinks(rp);
                free((char *)rp) ;
            }
        }
    }
  
    start_timer(&Obsotimer) ;
}
  
  
static struct cmds Nfcmds[] = {
    "add",  donfadd,        0, 3,
        "netrom nodefilter add <neighbor> <interface> [quality]",
    "drop", donfdrop,       0, 3,
        "netrom nodefilter drop <neighbor> <interface>",
    "mode", donfmode,       0, 0,   NULLCHAR,
    NULLCHAR, NULLFP((int,char**,void*)), 0, 0,
        "nodefilter subcommands: add drop mode",
} ;
  
/* nodefilter command multiplexer */
static int
donodefilter(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    if(argc < 2) {
        donfdump() ;
        return 0 ;
    }
    return subcmd(Nfcmds,argc,argv,p) ;
}
  
/* display a list of <callsign,interface> pairs from the filter
 * list.
 */
static int
donfdump()
{
    int i, column = 1 ;
    struct nrnf_tab *fp ;
    char buf[AXBUF] ;
  
    for(i = 0 ; i < NRNUMCHAINS ; i++)
        for(fp = Nrnf_tab[i] ; fp != NULLNRNFTAB ; fp = fp->next) {
            pax25(buf,fp->neighbor) ;
            tprintf("%-7s  %-8s  %-3d   ",
            buf,fp->iface->name, fp->quality) ;
            if(column++ == 3) {
                if(tputc('\n') == EOF)
                    return 0;
                column = 1 ;
            }
        }
  
    if(column != 1)
        tputc('\n') ;
  
    return 0 ;
}
  
/* add an entry to the filter table */
static int
donfadd(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    struct iface *ifp;
    unsigned qual;
    char neighbor[AXALEN] ;
  
    /* format callsign */
    if(setcall(neighbor,argv[1]) == -1) {
        tputs("bad neighbor callsign\n") ;
        return -1 ;
    }
  
    /* find interface */
    if((ifp = FindNrIface(argv[2])) == NULLIF) {
        return -1;
    }
  
    qual = ifp->quality; /* set default quality */
  
    if(argc > 3)
        qual = atoi(argv[3]);
  
    return nr_nfadd(neighbor,ifp,qual) ;
}
  
/* drop an entry from the filter table */
static int
donfdrop(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    struct iface *ifp;
    char neighbor[AXALEN] ;
  
    /* format neighbor callsign */
    if(setcall(neighbor,argv[1]) == -1) {
        tputs("bad neighbor callsign\n") ;
        return -1 ;
    }
  
    /* find interface */
    if((ifp = FindNrIface(argv[2])) == NULLIF) {
        return -1;
    }
  
    return nr_nfdrop(neighbor,ifp) ;
}
  
/* nodefilter mode subcommand */
static int
donfmode(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    if(argc < 2) {
        tputs("filter mode is ") ;
        switch(Nr_nfmode) {
            case NRNF_NOFILTER:
                tputs("none\n") ;
                break ;
            case NRNF_ACCEPT:
                tputs("accept\n") ;
                break ;
            case NRNF_REJECT:
                tputs("reject\n") ;
                break ;
            default:
                tputs("some strange, unknown value\n") ;
        }
        return 0 ;
    }
  
    switch(argv[1][0]) {
        case 'n':
        case 'N':
            Nr_nfmode = NRNF_NOFILTER ;
            break ;
        case 'a':
        case 'A':
            Nr_nfmode = NRNF_ACCEPT ;
            break ;
        case 'r':
        case 'R':
            Nr_nfmode = NRNF_REJECT ;
            break ;
        default:
            tputs("modes are: none accept reject\n") ;
            return -1 ;
    }
  
    return 0 ;
}
  
/* netrom network packet time-to-live initializer */
static int
donrttl(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setshort(&Nr_ttl,"Time to live",argc,argv);
}
  
/* show hidden (ie '#...') nodes or not */
static int
donrhidden(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setbool(&Nr_hidden,"Hidden nodes",argc,argv);
}
  
#ifdef G8BPQ
/* Emulate G8BPQ conreq/conack frames - WG7J */
static int
donrg8bpq(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setbool(&G8bpq,"G8BPQ mode",argc,argv);
}
#endif
  
/* allow automatic derating of netrom routes on link failure */
static int
donrderate(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    extern int Nr_derate;
  
    return setbool(&Nr_derate,"Derate flag",argc,argv);
}
  
/* promiscuous acceptance of broadcasts */
static int
donrpromisc(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    extern int Nr_promisc;
  
    return setbool(&Nr_promisc,"Promiscuous flag",argc,argv);
}
  
#ifdef NETROMSESSION
/* Initiate a NET/ROM transport connection */
static int
donrconnect(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    struct nrroute_tab *np;
    struct sockaddr_nr lsocket, fsocket;
    char alias[AXBUF];
    struct session *sp;
    int split = 0;
  
    /*Make sure this comes from console - WG7J*/
    if(Curproc->input != Command->input)
        return 0;
  
#ifdef SPLITSCREEN
    if(argv[0][0] == 's')
        split  = 1;
#endif
  
    /* Get a session descriptor */
    if((sp = newsession(argv[1],NRSESSION,split)) == NULLSESSION) {
        tputs(TooManySessions);
        return 1 ;
    }
  
    if((sp->s = socket(AF_NETROM,SOCK_SEQPACKET,0)) == -1){
        tputs(Nosock);
        keywait(NULLCHAR,1);
        freesession(sp);
        return 1;
    }
  
    /* See if the requested destination is a known alias or call,
     * use it if it is.  Otherwize give an error message. - WG7J
     */
    putalias(alias,argv[1],0);
    strupr(argv[1]);    /*make sure it's upper case*/
    if((np = find_nrboth(alias,argv[1])) == NULLNRRTAB){
    /*no such call or node alias*/
        tputs("no such node\n\n");
        keywait(NULLCHAR,1);
        freesession(sp);
        return 1;
    }
  
    /* Setup the local side of the connection */
    lsocket.nr_family = AF_NETROM;
  
    /* Set up our local username, bind would use Mycall instead */
    memcpy(lsocket.nr_addr.user,Nr4user,AXALEN);
  
    /* Putting anything else than Nr_iface->hwaddr here will not work ! */
    memcpy(lsocket.nr_addr.node,Nr_iface->hwaddr,AXALEN);
  
    /* Now bind the socket to this */
    bind(sp->s,(char *)&lsocket,sizeof(struct sockaddr_nr));
  
  
    /* Set up the remote side of the connection */
    fsocket.nr_family = AF_NETROM;
    memcpy(fsocket.nr_addr.user,np->call,AXALEN);
    memcpy(fsocket.nr_addr.node,np->call,AXALEN);
    fsocket.nr_family = AF_NETROM;
  
    return tel_connect(sp, (char *)&fsocket, sizeof(struct sockaddr_nr));
}
#endif
  
/* Reset a net/rom connection abruptly */
static int
donrreset(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct nr4cb *cb ;
  
    cb = MK_FP(htoi(argv[1]),8);
    if(!nr4valcb(cb)){
        tputs(Notval);
        return 1;
    }
    reset_nr4(cb);
    return 0;
}
  
/* Force retransmission on a net/rom connection */
  
static int
donrkick(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct nr4cb *cb ;
  
    cb = MK_FP(htoi(argv[1]),8);
    if(kick_nr4(cb) == -1) {
        tputs(Notval);
        return 1;
    } else
        return 0;
}
  
/* netrom transport ACK delay timer */
static int
donracktime(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setlong(&Nr4acktime,"Ack delay time (ms)",argc,argv);
}
  
/* netrom transport choke timeout */
static int
donrchoketime(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setlong(&Nr4choketime,"Choke timeout (ms)",argc,argv);
}
  
/* netrom transport initial round trip time */
  
static int
donrirtt(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setlong(&Nr4irtt,"Initial RTT (ms)",argc,argv);
}
  
/* netrom transport receive queue length limit.  This is the */
/* threshhold at which we will CHOKE the sender. */
  
static int
donrqlimit(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setshort(&Nr4qlimit,"Queue limit (bytes)",argc,argv);
}
  
/* Display or change our NET/ROM username */
static int
donruser(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char buf[AXBUF];
  
    if(argc < 2){
        pax25(buf,Nr4user);
        tprintf("%s\n",buf);
        return 0;
    }
    if(setcall(Nr4user,argv[1]) == -1)
        return -1;
    Nr4user[ALEN] |= E;
    return 0;
}
  
/* netrom transport maximum window.  This is the largest send and */
/* receive window we may negotiate */
  
static int
donrwindow(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setshort(&Nr4window,"Window (frames)",argc,argv);
}
  
/* netrom transport maximum retries.  This is used in connect and */
/* disconnect attempts; I haven't decided what to do about actual */
/* data retries yet. */
  
static int
donrretries(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    return setshort(&Nr4retries,"Retry limit",argc,argv);
}
  
  
/* Display the status of NET/ROM connections */
  
int
donrstatus(argc, argv,p)
int argc ;
char *argv[] ;
void *p;
{
    int i ;
    struct nr4cb *cb ;
    char luser[AXBUF], ruser[AXBUF], node[AXBUF] ;
  
    if(argc < 2) {
#ifdef UNIX
        tputs("&NCB     Snd-W Snd-Q Rcv-Q     LUser      RUser @Node     State\n");
#else
        tputs("&NCB Snd-W Snd-Q Rcv-Q     LUser      RUser @Node     State\n");
#endif
        for(i = 0 ; i < NR4MAXCIRC ; i++) {
            if((cb = Nr4circuits[i].ccb) == NULLNR4CB)
                continue ;
            pax25(luser,cb->local.user) ;
            pax25(ruser,cb->remote.user) ;
            pax25(node,cb->remote.node) ;
#ifdef UNIX
            if(tprintf("%8.8lx   %3d %5d %5d %9s  %9s %-9s %s\n",
#else
                if(tprintf("%4.4x   %3d %5d %5d %9s  %9s %-9s %s\n",
#endif
                    FP_SEG(cb), cb->nbuffered, len_q(cb->txq),
                    len_p(cb->rxq), luser, ruser, node,
                    Nr4states[cb->state]) == EOF)
                    break;
        }
        return 0 ;
    }
    cb = MK_FP(htoi(argv[1]),8);
    if(!nr4valcb(cb)) {
        tputs(Notval) ;
        return 1 ;
    }
    donrdump(cb) ;
    return 0 ;
}
  
/* Dump one control block */
  
void
donrdump(cb)
struct nr4cb *cb ;
{
    char luser[AXBUF], ruser[AXBUF], node[AXBUF] ;
    unsigned seq ;
    struct nr4txbuf *b ;
    struct timer *t ;
  
    pax25(luser,cb->local.user) ;
    pax25(ruser,cb->remote.user) ;
    pax25(node,cb->remote.node) ;
  
    tprintf("Local: %s %d/%d Remote: %s @ %s %d/%d State: %s\n",
    luser, cb->mynum, cb->myid, ruser, node,
    cb->yournum, cb->yourid, Nr4states[cb->state]) ;
  
    tprintf("Window: %-5u Rxpect: %-5u RxNext: %-5u RxQ: %-5d %s\n",
    cb->window, uchar(cb->rxpected), uchar(cb->rxpastwin),
    len_p(cb->rxq), cb->qfull ? "RxCHOKED" : "") ;
  
    tprintf(" Unack: %-5u Txpect: %-5u TxNext: %-5u TxQ: %-5d %s\n",
    cb->nbuffered, uchar(cb->ackxpected), uchar(cb->nextosend),
    len_q(cb->txq), cb->choked ? "TxCHOKED" : "") ;
  
    tputs("TACK: ") ;
    if(run_timer(&cb->tack))
        tprintf("%lu", read_timer(&cb->tack)) ;
    else
        tputs("stop") ;
    tprintf("/%lu ms; ", dur_timer(&cb->tack)) ;
  
    tputs("TChoke: ") ;
    if(run_timer(&cb->tchoke))
        tprintf("%lu", read_timer(&cb->tchoke)) ;
    else
        tputs("stop") ;
    tprintf("/%lu ms; ", dur_timer(&cb->tchoke)) ;
  
    tputs("TCD: ") ;
    if(run_timer(&cb->tcd))
        tprintf("%lu", read_timer(&cb->tcd)) ;
    else
#ifndef NR4TDISC
        tputs("stop") ;
    tprintf("/%lu ms", dur_timer(&cb->tcd)) ;
#else
    tputs("stop") ;
    tprintf("/%lu ms; ", dur_timer(&cb->tcd)) ;
  
    tputs("TDisc: ") ;
    if(run_timer(&cb->tdisc))
        tprintf("%lu", (read_timer(&cb->tdisc)/1000L)) ;
    else
        tputs("stop") ;
    tprintf("/%lu", (dur_timer(&cb->tdisc)/1000L)) ;
#endif
  
    if(run_timer(&cb->tcd))
        tprintf("; Tries: %u\n", cb->cdtries) ;
    else
        tputc('\n') ;
  
    tprintf("Backoff Level %u SRTT %ld ms Mean dev %ld ms\n",
    cb->blevel, cb->srtt, cb->mdev) ;
  
    /* If we are connected and the send window is open, display */
    /* the status of all the buffers and their timers */
  
    if(cb->state == NR4STCON && cb->nextosend != cb->ackxpected) {
  
        tputs("TxBuffers:  Seq  Size  Tries  Timer\n") ;
  
        for(seq = cb->ackxpected ;
            nr4between(cb->ackxpected, seq, cb->nextosend) ;
        seq = (seq + 1) & NR4SEQMASK) {
  
            b = &cb->txbufs[seq % cb->window] ;
            t = &b->tretry ;
  
            if(tprintf("            %3u   %3d  %5d  %lu/%lu\n",
                seq, len_p(b->data), b->retries + 1,
                read_timer(t), dur_timer(t))
                == EOF)
                break;
        }
  
    }
  
}
  
/* netrom timers type - linear v exponential */
static int
donrtype(argc,argv,p)
int argc ;
char *argv[] ;
void *p ;
{
    extern unsigned Nr_timertype;
  
    if(argc < 2) {
        tprintf("Netrom timer type is %s\n", Nr_timertype ? "linear" : "exponential" ) ;
        return 0 ;
    }
  
    switch(argv[1][0]) {
        case 'l':
        case 'L':
            Nr_timertype = 1 ;
            break ;
        case 'e':
        case 'E':
            Nr_timertype = 0 ;
            break ;
        default:
            tputs("use: netrom timertype [linear|exponential]\n") ;
            return -1 ;
    }
  
    return 0 ;
}
  
static int
dominquality(argc,argv,p)
int argc ;
char *argv[] ;
void *p ;
{
    unsigned val ;
    extern unsigned Nr_autofloor;
  
    if(argc < 2) {
        tprintf("%u\n", Nr_autofloor) ;
        return 0 ;
    }
  
    val = atoi(argv[1]) ;
  
    if(val == 0 || val > 255 ) {
        tputs("maximum route quality is 255\n") ;
        return 1 ;
    }
  
    Nr_autofloor = val ;
  
    return 0 ;
}
  
/* Fixed and now functional, 920317 WG7J */
int
donrload(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    char buff[255];
    FILE *fn;
    time_t now, prev;
#ifdef notdef
    long t1,t2;
    int j;
#endif
    int quality,obso;
    int permanent,record;
    struct iface *ifp;
    char alias[12],dest[12],iface[12],neighbor[12],type[3],*ptr;
    char destalias[ALEN+1]; /*alias in 'alias form'*/
    char destcall[AXALEN];  /*in callsign (ie shifted) form */
    char destneighbor[AXALEN];
  
    if(Nr_iface == NULLIF) {
        tputs("Attach netrom interface first\n") ;
        return 1;
    }
  
    if((fn = fopen(Netromfile,READ_TEXT)) == NULLFILE){
/*
        tputs("Can't open netrom save file!\n");
*/
        return 0;
    }
  
    if(fgets(buff,sizeof(buff),fn) == NULLCHAR){ /* read the timestamp */
        fclose(fn);
        return 1;
    }
    if((strncmp(buff,"time = ",7))!= 0){
/*
        tputs("Wrong node file content\n");
*/
        fclose(fn);
        return 1;
    }
    time(&now);
    sscanf(buff,"time =%ld",&prev);
/*
    tprintf("now = %ld , prev = %ld\n",now,prev);
*/
    if(prev >= now){
    /*
        tputs("You traveled back in time!!\n");
    */
        fclose(fn);
        return 1;
    }
#ifdef notdef
    t1 = now - prev;
    t2 = dur_timer(&Obsotimer)/1000L;
    j = t1 / t2;            /* recalculate obsolete count */
    tprintf("%ld seconds are past ( %d obsolete scans)\n",t1,j);
#endif
  
    while(fgets(buff,sizeof(buff),fn) != NULLCHAR){
        if((ptr = strchr(buff,':')) == 0){
            sscanf(buff,"%s%s%i%i%s%s"
            ,dest,type,&quality,&obso,iface,neighbor);
            alias[0] = '\0';
        } else {
            *ptr = ' ';
            sscanf(buff,"%s%s%s%i%i%s%s"
            ,alias,dest,type,&quality,&obso,iface,neighbor);
        }
    /*Set and check calls / alias - WG7J */
        if(setcall(destcall,dest) == -1) {
        /*
        tprintf("Bad call %s\n",dest);
        */
            continue;
        }
        if(setcall(destneighbor,neighbor) == -1) {
        /*
        tprintf("Bad call %s\n",neighbor);
        */
            continue;
        }
        if(putalias(destalias,alias,1) == -1)
            continue;
  
    /* find interface */
        if((ifp = if_lookup(iface)) == NULLIF)
            continue;
  
    /* Is it a netrom interface ? */
        if(!(ifp->flags & IS_NR_IFACE))
            continue;
  
        /* get and check quality value */
        if(quality  > 255 || quality < Nr_autofloor) {
        /*
            tputs("maximum route quality is 255\n") ;
         */
            continue;
        }
    /* Check the type of route - WG7J */
        permanent = record = 0;
        if(strchr(type,'P') != NULLCHAR)
            permanent = 1;
        else {
            if(strchr(type,'R') != NULLCHAR)
                record = 1;
        }
        nr_routeadd(destalias,destcall,ifp,quality,destneighbor, \
        permanent,record) ;
    }
    fclose(fn);
    return 0;
}
  
int
donrsave(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    register struct nrroute_tab *rp ;
    register struct nr_bind *bp ;
    register struct nrnbr_tab *np ;
    char neighbor[AXBUF] ;
    register int i;
    char buf[16] ;
    char *cp ;
    FILE *fn;
    time_t now;
  
#ifdef __TURBOC__
    if((fn = fopen(Netromfile,"wt+")) == NULLFILE){
#else
        if((fn = fopen(Netromfile,"w+")) == NULLFILE){
#endif
            tputs("Can't write netrom save file!\n");
            return 1;
        }
        time(&now);
        fprintf(fn,"time = %ld\n",now);
        for(i = 0 ; i < NRNUMCHAINS ; i++){
            for(rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rp->next){
                strcpy(buf,rp->alias) ;
            /* remove trailing spaces */
                if((cp = strchr(buf,' ')) == NULLCHAR)
                    cp = &buf[strlen(buf)] ;
                if(cp != buf)           /* don't include colon for null alias */
                    *cp++ = ':' ;
                for(bp = rp->routes; bp != NULLNRBIND; bp = bp->next) {
                    pax25(cp,rp->call) ;
                    fprintf(fn,"%-16s  ",buf) ;
                    np = bp->via ;
                    if(fprintf(fn,"%1s %3d  %3d  %-8s  %s\n",
                        (bp->flags & NRB_PERMANENT ? "P" :
                        bp->flags & NRB_RECORDED ? "R" : "X"),
                        bp->quality,bp->obsocnt,
                        np->iface->name,
                        pax25(neighbor,np->call)) == EOF)
                        break;
                }
            }
        }
        fclose(fn);
        return 0;
    }
  
#endif /* NETROM */
  
