/* C K O S L P -- Kermit interface to the IBM SLIP driver */

/*
  Authors: Jeffrey Altman (jaltman@secure-endpoints.com),
             Secure Endpoints Inc., New York City.
           David Bolen (db3l@ans.net)

  Copyright (C) 1985, 2004, Trustees of Columbia University in the City of New
  York.
*/

#include "ckcdeb.h"
#ifndef NT
#define INCL_ERRORS
#define INCL_DOSMISC
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define  INCL_DOSNMPIPES
#include <os2.h>
#undef COMMENT

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#include "ckoslp.h"

/*--------------------------------------------------------------------------*/
/* Definitions used for interface information                               */
/*--------------------------------------------------------------------------*/

/* VJ Compression Options */
#define slc_NOCOMPRESSION       0       /* No compression at all            */
#define slc_COMPRESSION         1       /* Always send compression          */
#define slc_AUTOCOMPRESSION     2       /* Enable compression when received */


/*--------------------------------------------------------------------------*/
/* Interface structure (used to return interface configuration information) */
/*--------------------------------------------------------------------------*/
typedef struct slcS_INTERFACE_ {
                                        /* -- INET Attachment Information -- */

   char           if_name[4];           /* sl?   (?=0-9)                     */
   unsigned short if_mtu;               /* MTU for interface                 */

   unsigned short if_rtt;               /* Estimated rtt, rttvar and rttmin  */
   unsigned short if_rttvar;            /*   for routes making use of this   */
   unsigned short if_rttmin;            /*   interface (in ms)               */

   unsigned short if_sendpipe;          /* Maximum send/recv pipes (socket   */
   unsigned short if_recvpipe;          /*   buffers and TCP windows)        */
   unsigned short if_ssthresh;          /* Slow-start threshold (segments)   */

   unsigned short if_maxqueue;          /* Maximum interface queue size      */
   unsigned short if_maxfastq;          /* Maximum fast queue size           */

                                        /* -- Other Interface Settings  --   */

   int allowfastq,                      /* Should fast queueing be used      */
       compression;                     /* VJ compression options            */
   char *device;                        /* OS/2 device for interface         */

                                        /* -- Interface Command Scripts --   */
   char *attachcmd,
        *attachparms;                   /* and their parameters              */


   struct slcS_INTERFACE_ *next;        /* Pointer to next parsed interface  */

} slcS_INTERFACE, *slcPS_INTERFACE;


/*--------------------------------------------------------------------------*/
/* Function prototypes.                                                     */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------*/
/*                Parsing Functions                 */
/*--------------------------------------------------*/

int slcParseConfiguration (char *Filename, slcPS_INTERFACE *Interfaces);
int slcFreeConfiguration  (slcPS_INTERFACE *Interfaces);

/*
 *  Variables
 */

/* Semaphores and Pipe for communicating with SLIP.EXE */
HEV hevSlipMonitor = 0,
    hevSlipPause = 0,
    hevSlipPaused = 0,
    hevSlipContinue = 0;
HPIPE hpipeSlip = 0 ;

PSZ
SlipCfgFile( void )
{
    static PSZ slipcfgfile = 0 ;       /* Config file name */
    char * etc ;

    if ( slipcfgfile )
        return slipcfgfile ;

    if ( ( etc = getenv( "ETC" ) ) == NULL ) {
        slipcfgfile = "" ;
        return slipcfgfile ;
        }

    slipcfgfile = malloc( strlen( etc ) + 10 ) ;
    strcpy( slipcfgfile, etc ) ;
    strcat( slipcfgfile, "\\slip.cfg" ) ;
    return slipcfgfile ;
}

PSZ
PPPCfgFile( void )
{
    static PSZ pppcfgfile = 0 ;       /* Config file name */
    char * etc ;

    if ( pppcfgfile )
        return pppcfgfile ;

    if ( ( etc = getenv( "ETC" ) ) == NULL ) {
        pppcfgfile = "" ;
        return pppcfgfile ;
        }

    pppcfgfile = malloc( strlen( etc ) + 10 ) ;
    strcpy( pppcfgfile, etc ) ;
    strcat( pppcfgfile, "\\ppp.cfg" ) ;
    return pppcfgfile ;
}

APIRET
SlipOpen( char * device )
{
    APIRET rc ;
    ULONG action, actual ;
    char  interface[4] ;
    slcPS_INTERFACE pInterfaces = 0,
        pInterface              = 0 ; /* Ptr to S_INTERFACE structure */

    if ( rc = slcParseConfiguration ( SlipCfgFile(), &pInterfaces ) ) {
        printf("Unable to parse %s\n",SlipCfgFile() );
        return rc ;
        }

    /* Find the proper interface to use */
    interface[0] = '\0' ;
    pInterface = pInterfaces ;
    do
        {
        if ( !strcmp( strlwr(pInterface->device), device ) ) {
            strcpy( interface, pInterface->if_name ) ;
            break;
            }
        pInterface = pInterface->next ;
        }
    while ( pInterface != NULL );

    if ( interface[0] == '\0' ) {
        printf("%s is not listed in %s\n",device,SlipCfgFile());
        slcFreeConfiguration( &pInterfaces ) ;
        return 1 ;
        }

    if (!(rc = DosOpenEventSem( "\\sem32\\slip\\monitor",
        &hevSlipMonitor )))
        if(!(rc = DosOpenEventSem( "\\sem32\\slip\\com\\pause",
            &hevSlipPause )))
            if(!(rc = DosOpenEventSem( "\\sem32\\slip\\com\\paused",
                &hevSlipPaused)))
                if (!(rc = DosOpenEventSem( "\\sem32\\slip\\com\\continue",
                    &hevSlipContinue)))
                    rc = DosOpen( "\\pipe\\slip", &hpipeSlip, &action, 0,
                        FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
                        OPEN_SHARE_DENYNONE, NULL ) ;

    if ( !rc ) {
        int PauseCount = 0 ;
        DosWrite( hpipeSlip, interface, 4, &actual ) ;
        do {
            if (PauseCount)
                msleep(500);
            PauseCount++ ;
            rc = DosPostEventSem( hevSlipPause ) ;
        } while ( PauseCount <= 10 && rc == ERROR_ALREADY_POSTED ) ;

        if (!rc)
            rc =DosWaitEventSem( hevSlipPaused, SEM_INDEFINITE_WAIT ) ;
        }

    slcFreeConfiguration( &pInterfaces ) ;
    return rc ;
}

APIRET
PPPOpen( char * device )
{
    extern int ttppp;
    APIRET rc ;
    ULONG action, actual ;
    UCHAR MonitorSem[32] ;
    UCHAR PauseSem[32];
    UCHAR PausedSem[32];
    UCHAR ContinueSem[32];
    char  interface[8] ;
    slcPS_INTERFACE pInterfaces = 0,
        pInterface              = 0 ; /* Ptr to S_INTERFACE structure */

    sprintf(interface, "ppp%d", ttppp-1 );

    /* Create the Sem strings to use */
    sprintf(MonitorSem, "\\sem32\\ppp%d\\monitor", ttppp-1);
    sprintf(PauseSem, "\\sem32\\ppp%d\\pause", ttppp-1);
    sprintf(PausedSem, "\\sem32\\ppp%d\\paused", ttppp-1);
    sprintf(ContinueSem, "\\sem32\\ppp%d\\continue", ttppp-1);

    /* Find the proper interface to use */
    if (!(rc = DosOpenEventSem( MonitorSem, &hevSlipMonitor )))
        if(!(rc = DosOpenEventSem( PauseSem, &hevSlipPause )))
            if(!(rc = DosOpenEventSem( PausedSem, &hevSlipPaused)))
                if (!(rc = DosOpenEventSem( ContinueSem, &hevSlipContinue)))
                    rc = DosOpen( "\\pipe\\ppp", &hpipeSlip, &action, 0,
                        FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
                        OPEN_SHARE_DENYNONE, NULL ) ;

    if ( !rc ) {
        int PauseCount = 0 ;
        DosWrite( hpipeSlip, interface, 4, &actual ) ;
        do {
            if (PauseCount)
                msleep(500);
            PauseCount++ ;
            rc = DosPostEventSem( hevSlipPause ) ;
        } while ( PauseCount <= 10 && rc == ERROR_ALREADY_POSTED ) ;

        if (!rc)
            rc =DosWaitEventSem( hevSlipPaused, SEM_INDEFINITE_WAIT ) ;
        }
    return rc ;
}

void
PPPSlipClose( void )
{
   if ( hevSlipMonitor )
      {
        DosPostEventSem( hevSlipContinue ) ;   /* give the port back */
                                               /* to the slip driver */
        DosCloseEventSem( hevSlipContinue ) ;
        DosCloseEventSem( hevSlipPaused ) ;
        DosCloseEventSem( hevSlipPause ) ;
        DosCloseEventSem( hevSlipMonitor ) ;
        hevSlipMonitor = 0 ;
        hevSlipPaused = 0 ;
        hevSlipPause = 0 ;
        hevSlipContinue = 0 ;
      }

    if ( hpipeSlip ) {
        DosClose( hpipeSlip ) ;
        hpipeSlip = 0 ;
        }

}


/*==========================================================================*/
/*                                                                          */
/*            --------------------------------------------------            */
/*                                                                          */
/*                          PRIVATE DATA/FUNCTIONS                          */
/*                                                                          */
/*            --------------------------------------------------            */
/*                                                                          */
/*==========================================================================*/

/*--------------------------------------------------------------------------*/
/* Macro/Constant definitions                                               */
/*--------------------------------------------------------------------------*/
#define _slcC_BUFSIZE     512   /* Maximum "chunks" used for config file    */
#define _slcC_ERRSIZE     256   /* Maximum size for a single error message  */
#define _slcC_TOKSIZE      64   /* Maximum size for "tokens" in config file */

#define _slcC_TOKEN_NONE    0   /* Token types */
#define _slcC_TOKEN_TOOLONG 1
#define _slcC_TOKEN_ERROR   1   /* < this indicates an error token */

#define _slcC_TOKEN_EOF     10  /* Success tokens */
#define _slcC_TOKEN_TEXT    11
#define _slcC_TOKEN_EQUAL   12
#define _slcC_TOKEN_LBRACE  13
#define _slcC_TOKEN_RBRACE  14

#define _slcC_TOKENTBL_TERM 1000  /* Must be > max entries in any table */

/*--------------------------------------------------------------------------*/
/* Structure declarations                                                   */
/*--------------------------------------------------------------------------*/
typedef struct _slcS_TOKEN_ {
   int type, length;              /* Type of token and text length         */
   char text[_slcC_TOKSIZE+1];    /* Remember room for terminating NULL    */
} _slcS_TOKEN, *_slcPS_TOKEN;

typedef struct _slcS_TOKENTBL_ {  /* Map of text tokens to cmd/parm values */
   int  command;                  /* Really an _slcE* type                 */
   char *name;                    /* Actual text of the cmd/element/value  */
   int  valuetype,                /* For parms, >0 means need value.  1 is */
        minimum, maximum;         /*   string, 2=number what range is ok   */
} _slcS_TOKENTBL, *_slcPS_TOKENTBL;


/*--------------------------------------------------------------------------*/
/* Declarations for commands/elements and values allowed within the         */
/* configuration file.                                                      */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------*/
/*          Configuration file commands             */
/*--------------------------------------------------*/
#define _slcA_CMD_INTERFACE     "interface"

typedef enum {cmd_interface} _slcE_CMD;

/*--------------------------------------------------*/
/*          "Interface" command parameters          */
/*--------------------------------------------------*/
#define _slcA_INTF_DEVICE         "device"
#define _slcA_INTF_MTU            "mtu"
#define _slcA_INTF_RTT            "rtt"
#define _slcA_INTF_RTTVAR         "rttvar"
#define _slcA_INTF_RTTMIN         "rttmin"
#define _slcA_INTF_SENDPIPE       "sendpipe"
#define _slcA_INTF_RECVPIPE       "recvpipe"
#define _slcA_INTF_SSTHRESH       "ssthresh"
#define _slcA_INTF_QUEUESIZE      "queuesize"
#define _slcA_INTF_FASTQUEUESIZE  "fastqueuesize"
#define _slcA_INTF_FASTQUEUE      "fastqueue"
#define _slcA_INTF_NOFASTQUEUE    "nofastqueue"
#define _slcA_INTF_COMPRESSION    "compression"
#define _slcA_INTF_ATTACHCMD      "attachcmd"
#define _slcA_INTF_ATTACHPARMS    "attachparms"

typedef enum {intf_device, intf_mtu, intf_rtt, intf_rttvar, intf_rttmin,
              intf_sendpipe, intf_recvpipe, intf_ssthresh,
              intf_queuesize, intf_fastqueuesize, intf_fastqueue,
              intf_nofastqueue, intf_compression, intf_attachcmd,
              intf_attachparms} _slcE_INTF;


/*--------------------------------------------------------------------------*/
/* Module-global data                                                       */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------*/
/*                Token error table                 */
/*--------------------------------------------------*/
char *_slc_TokenErrorTable[] = {   /* WARNING: Keep in same order as codes */
   "No token", "Token too long" };


/*--------------------------------------------------*/
/*      Token tables for commands/parameters        */
/*--------------------------------------------------*/
_slcS_TOKENTBL _slc_CmdTable[] = {
   {cmd_interface,       _slcA_CMD_INTERFACE,0},
   {_slcC_TOKENTBL_TERM, "",0} };

_slcS_TOKENTBL _slc_IntfTable[] = {
   {intf_device,        _slcA_INTF_DEVICE,1},
   {intf_mtu,           _slcA_INTF_MTU,2,64,2048},
   {intf_rtt,           _slcA_INTF_RTT,2,0,65536},
   {intf_rttvar,        _slcA_INTF_RTTVAR,2,0,65536},
   {intf_rttmin,        _slcA_INTF_RTTMIN,3,0,65536},
   {intf_sendpipe,      _slcA_INTF_SENDPIPE,2,0,65536},
   {intf_recvpipe,      _slcA_INTF_RECVPIPE,2,0,65536},
   {intf_ssthresh,      _slcA_INTF_SSTHRESH,2,0,65536},
   {intf_queuesize,     _slcA_INTF_QUEUESIZE,2,4,52},
   {intf_fastqueuesize, _slcA_INTF_FASTQUEUESIZE,2,4,52},
   {intf_fastqueue,     _slcA_INTF_FASTQUEUE,0},
   {intf_nofastqueue,   _slcA_INTF_NOFASTQUEUE,0},
   {intf_compression,   _slcA_INTF_COMPRESSION,1},
   {intf_attachcmd,     _slcA_INTF_ATTACHCMD,1},
   {intf_attachparms,   _slcA_INTF_ATTACHPARMS,1},
   {_slcC_TOKENTBL_TERM,""} };


/*--------------------------------------------------------------------------*/
/*     int _slcGetCh (FILE *RuleFile; int SkipWS, MergeWS, *Line, *Pos)     */
/*..........................................................................*/
/*                                                                          */
/* Retrieves the next "character" from the rule file, fetching new lines    */
/* from the file as necessary.  If MergeWS is 1, then any whitespace (tabs, */
/* spaces or newlines) is always returned as a space.  If SkipWS is 1, then */
/* any grouping of whitespace is returned as a single space character (or   */
/* whatever WS character is first in the group if MergeWS is 0).            */
/*                                                                          */
/* Comments are set via the # character and can appear anywhere within a    */
/* line.  Any text following the # will be ignored.                         */
/*                                                                          */
/* Parsing state information is returned in one or both of the Line and Pos */
/* parameters (if they are non-NULL).  Line represents the line number in   */
/* the file and Pos the position within the line (both origin 1) of the     */
/* character being returned by this function.                               */
/*                                                                          */
/* The function returns EOF when end of file is reached.                    */
/*                                                                          */
/* To avoid having to export parsing state information from this function,  */
/* a special calling convention of RuleFile==NULL is used in order to       */
/* initializing the internal buffer and counters for a new file.            */
/*                                                                          */
/*--------------------------------------------------------------------------*/
int _slcGetCh (FILE *RuleFile, int SkipWS, int MergeWS,
               int *Line, int *Pos)
{
   static char buffer[_slcC_BUFSIZE], *curch;
   static int full_line,        /* Does buffer represent a full line    */
              in_comment,       /* Current buffer holds partial comment */
              curline,          /* Current line/position information    */
              curpos;

   int done, result;
   char *ch;

   /* Special argument of RuleFile==NULL means initialize */
   if (RuleFile == NULL) {
      curline   = curpos = in_comment = 0;
      buffer[0] = '\0';
      curch     = buffer;
      full_line = 0;
      return(0);
   }

   /* Run until we have a suitable character to return */
   done   = 0;
   result = EOF;
   while (!done) {
      /*--------------------------------------------------*/
      /* 1. If we need a new line from the file go get it */
      /*--------------------------------------------------*/
      if (*curch == '\0') {
         buffer[_slcC_BUFSIZE-2] = '\0';        /* For quick full_line check */
         /* For now a read error and EOF are both returned as EOF, */
         /* so I just check for NULL and don't bother with feof()  */
         if (fgets(buffer,_slcC_BUFSIZE,RuleFile) == NULL) {
            result = EOF;
            break;      /* EOF - get out of here */
         } else {
            /* Reset current character pointer */
            curch = buffer;
            /* If last line was full, bump linecount, and init pos count */
            if (full_line) {
               curline++;
               curpos = 0;
            }
            /* Then decide if we got a whole line this time.  By checking */
            /* the next to last character (initialized above), we avoid   */
            /* the need to call strlen for each line read.                */
            full_line = (buffer[_slcC_BUFSIZE-2] == '\0' ||
                         buffer[_slcC_BUFSIZE-2] == '\n');
            /* Early-out.  If already in comment (true if a line with # is */
            /* >_slcC_BUFSIZE characters), just ignore this line entirely, */
            /* resetting in_comment only if we got all of rest of line.    */
            if (in_comment) {
               if (full_line) in_comment = 0;
               /* Nullify line - don't worry about WS - the leading # on */
               /* this comment would already have been treated as a ' '. */
               buffer[0] = '\0';
            } else {
               /* Otherwise, check for \n or #.  If found, convert to space */
               /* and terminate the string at that point.  We need a space  */
               /* to insure parsing "aa\n\nbb" separates "aa" and "bb".     */
               ch = buffer;
               while (*ch) {
                  if (*ch == '#' || *ch == '\n') {
                     /* Might need to set flag for long comments */
                     if (*ch == '#' && !full_line) in_comment = 1;
                     *ch++ = ' ';
                     *ch = '\0';
                     break;
                  } else {
                     ch++;
                  }
               }
            } /* \n and # scan */
         } /* if got good line */

         continue;      /* back and keep trying */
      }

      /*--------------------------------------------------*/
      /* 2. Now we have a null terminated buffer.  If on  */
      /*    WS find first non-WS, otherwise bump curch    */
      /*    by 1.  Then if SkipWS always return non-WS,   */
      /*    otherwise return space if we started on a WS. */
      /*    Note that we only set result if it isn't set  */
      /*    already since this may run several times in   */
      /*    order to skip over lines of whitespace.       */
      /*--------------------------------------------------*/
      if (result == EOF) {
         result = *curch;
         if (MergeWS && isspace(result)) result = ' ';
         /* If caller wants, store line/position information for this char */
         /* (remembering that we are origin 0, but returning origin 1)     */
         if (Line != NULL) *Line = curline+1;
         if (Pos  != NULL) *Pos  = curpos+1;
      }
      if (isspace(result) && SkipWS) {
         /* Find first non-WS */
         while (*curch && isspace(*curch)) {
            /* Account for tabs here - assume standard expansion of 8 */
            if (*curch == '\t') {
               curpos = ((curpos / 8)+1) * 8;
            } else {
               curpos++;
            }
            curch++;
         }
         done = (*curch);       /* We're only done if we didn't run out */
      } else {
         curch++;
         curpos++;
         done = 1;
      }
   } /* while !done */

   return(result);
}


/*--------------------------------------------------------------------------*/
/*  int _slcGetToken (FILE *RuleFile; _slcPS_TOKEN Token; int *Line, *Pos)  */
/*..........................................................................*/
/*                                                                          */
/* Grabs the next "token" from the specified file, identifies it, and       */
/* returns it in the supplied token structure.  The maximum number of       */
/* characters in a token cannot exceed _slcC_TOKSIZE.                       */
/*                                                                          */
/* The rules for breaking apart tokens is pretty simple.  The following     */
/* characters are treated specially:                                        */
/*      WS | ,  Separates tokens                                            */
/*      =       Special token - separates a rule element from its value.    */
/*      {       Special token - begins a list of rule elements.             */
/*      }       Special token - terminates a list of rule elements.         */
/* The text that created the special tokens is still stored in Token.text.  */
/*                                                                          */
/* Any of these characters may be quoted with the use of the \ character    */
/* (\\ for \ itself), or may be enclosed within double quotes (") to quote  */
/* an entire string.  Note that the \ character is processed even inside    */
/* quotes, so it must be doubled to yield itself or can also be used to     */
/* quote a quote, so to speak.                                              */
/*                                                                          */
/* If either of the Line or Pos parameters is non-NULL, then the file line  */
/* and character position of the start of the returned token are stored in  */
/* the address(es) they point to.                                           */
/*                                                                          */
/*--------------------------------------------------------------------------*/
#define _slc_ADDCHAR(token,ch)  if (chcount<_slcC_TOKSIZE) { \
                                   token->text[chcount++] = ch; \
                                   line = pos = NULL; \
                                } else { \
                                   token->type = _slcC_TOKEN_TOOLONG; \
                                   break; \
                                }

int _slcGetToken (FILE *RuleFile, _slcPS_TOKEN Token, int *Line, int *Pos)
{
   static int lastch=EOF;
   char *curch;
   int ch, chcount=0, intoken=1, skipws=1, inbslash=0, inquote=0;
   int *line, *pos;

   curch = Token->text;
   line  = Line;
   pos   = Pos;
   while (intoken) {
      if (lastch != EOF) {
         ch = lastch;
         lastch = EOF;
      } else {
         ch = _slcGetCh(RuleFile,skipws,skipws,line,pos);
      }

      /* Handle special quoting stuff first */
      if (inbslash) {           /* \ always quotes next character, even \   */
         /* NEED EOF CHECK */
         _slc_ADDCHAR(Token,ch);
         inbslash = 0;
         skipws = !inquote;
         continue;
      }
      if (ch == '\\') {         /* Prepare to quote next character          */
         inbslash = 1;
         skipws   = 0;
         continue;
      }
      if (inquote) {            /* Unless terminating ", just add next char */
         /* NEED EOF CHECK */
         if (ch == '"') {
            inquote = 0;
            skipws  = 1;
         } else {
            _slc_ADDCHAR(Token,ch);
         }
         continue;
      }

      /* Then normal character processing */
      switch (ch) {
         case EOF:      /* We've hit end of input file */
            if (chcount > 0) {
               Token->type = _slcC_TOKEN_TEXT;
               lastch = ch;     /* this avoids calling GetCh again */
            } else {
               Token->type = _slcC_TOKEN_EOF;
            }
            intoken = 0;
            break;
         case ' ':      /* Whitespace */
         case ',':      /* Or comma   */
            if (chcount > 0) {
               Token->type = _slcC_TOKEN_TEXT;
               intoken = 0;
            }
            break;
         case '"':      /* Start quoting */
            inquote = 1;
            skipws  = 0;
            break;
         case '{':      /* Start of rule elements */
            if (chcount > 0) {
               Token->type = _slcC_TOKEN_TEXT;
               lastch = ch;
            } else {
               Token->type = _slcC_TOKEN_LBRACE;
               Token->text[0] = ch;
               chcount++;
            }
            intoken = 0;
            break;
         case '}':      /* End of rule elements */
            if (chcount > 0) {
               Token->type = _slcC_TOKEN_TEXT;
               lastch = ch;
            } else {
               Token->type = _slcC_TOKEN_RBRACE;
               Token->text[0] = ch;
               chcount++;
            }
            intoken = 0;
            break;
         case '=':      /* Rule element/value separator */
            if (chcount > 0) {
               Token->type = _slcC_TOKEN_TEXT;
               lastch = ch;
            } else {
               Token->type = _slcC_TOKEN_EQUAL;
               Token->text[0] = ch;
               chcount++;
            }
            intoken = 0;
            break;
         default:       /* Normal character for token */
            _slc_ADDCHAR(Token,ch);
            break;
      } /* switch (ch) */
   } /* while in token */

   /* Terminate any stuff put into the text field */
   Token->length        = chcount;
   Token->text[chcount] = '\0';

   return(0);
}


/*--------------------------------------------------------------------------*/
/*   int _slcMatchToken (_slcPS_TOKEN Token; _slcS_TOKENTBL TokenTable[])   */
/*..........................................................................*/
/*                                                                          */
/* Function to attempt to locate the supplied text token in the token table */
/* supplied by the TokenTable parameter.  If a match is found, the index    */
/* of the token table entry is returned (0-n), otherwise -1.                */
/*                                                                          */
/* (Note - this needs to be improved in the future to accept any number of  */
/*  token table parameters - and probably return a pointer to the correct   */
/*  entry rather than an index into the table)                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
int _slcMatchToken (_slcPS_TOKEN Token, _slcS_TOKENTBL TokenTable[])
{
   char tokentext[_slcC_TOKSIZE+1], *src, *dest;
   int index;

   /* Copy over token text, lowercasing it for simpler comparison */
   dest = tokentext;
   src  = Token->text;
   while (*dest++ = tolower(*src++))
      ;

   /* Now run through table for comparision */
   index = 0;
   while (TokenTable[index].command != _slcC_TOKENTBL_TERM) {
      if (strcmp(TokenTable[index].name,tokentext) == 0) break;
      index++;
   }
   if (TokenTable[index].command == _slcC_TOKENTBL_TERM) index = -1;

   return(index);
}


/*--------------------------------------------------------------------------*/
/*          int _slcErrorMessage (char *Filename; int Line, Pos;            */
/*                                char *MsgFormat, ...)                     */
/*..........................................................................*/
/*                                                                          */
/* Routine for displaying an error message for the specified Filename,      */
/* using the same semantics as printf.  If the supplied Line/Pos parameters */
/* are >0, they are used automatically as a standard preface to the error   */
/* message.                                                                 */
/*                                                                          */
/* Since this function actually processes the output string, the maximum    */
/* size of the resulting message is _slcC_ERRSIZE characters.               */
/*                                                                          */
/* (Right now this goes to stderr)                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/
int _slcErrorMessage (char *Filename, int Line, int Pos,
                      char *MsgFormat, ...)
{
   char errmsg[_slcC_ERRSIZE], syslogmsg[_slcC_ERRSIZE];
   va_list args;

   sprintf(errmsg,"SL-Config: Error Parsing File \"%s\"",Filename);
   if (Line > 0 || Pos > 0) {
      strcat(errmsg," (");
   }
   if (Line > 0) {
      sprintf(&errmsg[strlen(errmsg)],"Line=%d",Line);
   }
   if (Line > 0 && Pos > 0) {
      strcat(errmsg,", ");
   }
   if (Pos > 0) {
      sprintf(&errmsg[strlen(errmsg)],"Position=%d",Pos);
   }
   if (Line > 0 || Pos > 0) {
      strcat(errmsg,")");
   }
   fprintf(stderr,"%s\n",errmsg);
   va_start(args,MsgFormat);
   vsprintf(errmsg,MsgFormat,args);
   fprintf(stderr,"SL-Config: %s\n",errmsg);
    return(0);
}


/*--------------------------------------------------------------------------*/
/*      int _slcSetIntfParm (slcPS_INTERFACE Interface,                     */
/*                           _slcPS_TOKENTBL Parameter,                     */
/*                           _slcPS_TOKEN Value)                            */
/*..........................................................................*/
/*                                                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/
int _slcSetIntfParm (slcPS_INTERFACE Interface, _slcPS_TOKENTBL Parameter,
                     _slcPS_TOKEN Value)
{
   int result = 0, index, value;

   switch (Parameter->valuetype) {
      case 0:
         /* No value arguments - just set appropriate flags */
         switch (Parameter->command) {
            case intf_nofastqueue:
               Interface->allowfastq = 0;
               break;
            case intf_fastqueue:
               Interface->allowfastq = 1;
               break;
            default:
               /* Should never happen */
               result = 4;
               break;
         }
         break;

      case 1:
         /* String parameter */
         switch (Parameter->command) {
            case intf_device:
               if (Interface->device) {
                  free(Interface->device);
               }
               Interface->device = strdup(Value->text);
               if (Interface->device == NULL) {
                  result = 3;
               }
               break;
            case intf_compression:
               if (!stricmp("on",Value->text)) {
                  Interface->compression = slc_COMPRESSION;
                  if (Interface->if_mtu == 0) {
                     /* Set mtu (since not set already) to smaller chunks */
                     Interface->if_mtu = 296;
                  }
               } else if (!stricmp("off",Value->text)) {
                  Interface->compression = slc_NOCOMPRESSION;
               } else if (!stricmp("auto",Value->text)) {
                  Interface->compression = slc_AUTOCOMPRESSION;
               } else {
                  result = 1;
               }
               break;
            case intf_attachcmd:
               if (Interface->attachcmd) {
                  free(Interface->attachcmd);
               }
               Interface->attachcmd = strdup(Value->text);
               if (Interface->attachcmd == NULL) {
                  result = 3;
               }
               break;
            case intf_attachparms:
               if (Interface->attachparms) {
                  free(Interface->attachparms);
               }
               Interface->attachparms = strdup(Value->text);
               if (Interface->attachparms == NULL) {
                  result = 3;
               }
               break;
         }
         break;

      case 2:
         /* Numeric argument with valid range */
         for (index=0; index < Value->length && !result; index++) {
            if (!isdigit(Value->text[index])) {
               result = 1;
            }
         }
         if (result)
            break;
         value = atoi(Value->text);
         if (value < Parameter->minimum || value > Parameter->maximum) {
            result = 2;
         }
         if (result)
            break;
         /* Store resulting value into interface structure */
         switch (Parameter->command) {
            case intf_mtu:
               Interface->if_mtu = value;
               break;
            case intf_rtt:
               Interface->if_rtt = value;
               break;
            case intf_rttvar:
               Interface->if_rttvar = value;
               break;
            case intf_rttmin:
               Interface->if_rttmin = value;
               break;
            case intf_sendpipe:
               Interface->if_sendpipe = value;
               break;
            case intf_recvpipe:
               Interface->if_recvpipe = value;
               break;
            case intf_ssthresh:
               Interface->if_ssthresh = value;
               break;
            case intf_queuesize:
               Interface->if_maxqueue = value;
               break;
            case intf_fastqueuesize:
               Interface->if_maxfastq = value;
               break;
            default:
               /* Should never happen */
               result = 4;
               break;
         }
   }

   return(result);
}



/*==========================================================================*/
/*                                                                          */
/*            --------------------------------------------------            */
/*                                                                          */
/*                             PUBLIC FUNCTIONS                             */
/*                                                                          */
/*            --------------------------------------------------            */
/*                                                                          */
/*==========================================================================*/



/*--------------------------------------------------------------------------*/
/* int slcParseConfiguration (char *Filename; slcPS_INTERFACE *Interfaces)  */
/*..........................................................................*/
/*                                                                          */
/* Parses a SLIP configuration file, building a linked list of interface    */
/* structures to hold the interface configuration information.              */
/*                                                                          */
/*                                                                          */
/* This function is currently geared to process files of the form:          */
/*                                                                          */
/*      interface sl# | default {                                           */
/*              parameter=value [,] parameter=value [,]  ...                */
/*      }                                                                   */
/*                                                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/
#define _slc_ERRMSG     _slcErrorMessage
#define _slc_ERRHDR     Filename,line,pos
#define _slc_NEXT_TOKEN _slcGetToken(configfile,&token,&line,&pos)

int slcParseConfiguration (char *Filename, slcPS_INTERFACE *Interfaces)
{
   int             result=0;
   _slcS_TOKEN     token;
   slcS_INTERFACE  intf_default;
   slcPS_INTERFACE curinterface, lastinterface=NULL;
   FILE            *configfile;
   int             done=0, allowvalue=0, wantvalue=0, invalue=0,
                   index, line, pos, parameter, rc;
   int             curcommand;


   /* Clear default structure */
   memset(&intf_default,'\0',sizeof(intf_default));
   /* And then insert fundamental defaults */
   intf_default.if_recvpipe = 4096;
   intf_default.if_maxqueue = 12;
   intf_default.if_maxfastq = 24;
   intf_default.allowfastq  = 1;
   intf_default.compression = slc_NOCOMPRESSION;

   if ((configfile = fopen(Filename,"r")) == NULL) {
      result = 1;
   } else {
      /* Make sure any previous parses are flushed */
      _slcGetCh(NULL,0,0,0,0);

      /* Main rule processing loop - handle a command at a time */
      while (!done) {
         /* Fetch next token */
         _slc_NEXT_TOKEN;

         /* If EOF here, then we're done */
         if (token.type == _slcC_TOKEN_EOF) {
            done = 1;
            break;
         }

         /* If not text or text but not in command table, generate error */
         if (token.type != _slcC_TOKEN_TEXT ||
             (index = _slcMatchToken(&token,_slc_CmdTable)) < 0) {
            _slc_ERRMSG(_slc_ERRHDR,"Invalid command encountered - \"%s\"",
                        token.text);
            result = done = 1;
            break;
         }

         /* We have an interface command (eventually, this will have to */
         /* handle more than just an interface command.                 */

         /* Grab next token - should be "sl#" (#=0-9) or "default" */
         _slc_NEXT_TOKEN;
         if (token.type != _slcC_TOKEN_TEXT) {
            _slc_ERRMSG(_slc_ERRHDR,"Missing interface name");
            result = done = 1;
            break;
         } else {
            if (token.length == 3 &&
                tolower(token.text[0]) == 's' &&
                tolower(token.text[1]) == 'l' &&
                token.text[2] == '0') {   /* currently only 'sl0' allowed */
               /* Normal "sl#" interface - lowercase interface name  */
               /* and then make sure that we haven't already used it */
               strlwr(token.text);
               curinterface = *Interfaces;
               while (curinterface) {
                  if (!strcmp(curinterface->if_name,token.text)) {
                     _slc_ERRMSG(_slc_ERRHDR,"Duplicate interface \"%s\"",
                                 token.text);
                     result = done = 1;
                     break;
                  }
                  curinterface = curinterface->next;
               }
               /* Unique - allocate room for new interface */
               if (lastinterface == NULL) {
                  lastinterface = (slcPS_INTERFACE)
                                 calloc(1,sizeof(slcS_INTERFACE));
                  curinterface = *Interfaces = lastinterface;
               } else {
                  lastinterface->next = (slcPS_INTERFACE)
                                        calloc(1,sizeof(slcS_INTERFACE));
                  curinterface = lastinterface = lastinterface->next;
               }
               if (curinterface == NULL) {
                  _slc_ERRMSG(_slc_ERRHDR,
                              "Error allocating space for new interface");
                  result = done = 1;
                  break;
               }

               /* Copy over default block for initial values */
               memcpy(curinterface,&intf_default,sizeof(intf_default));

               /* Update interface name and insert default device name */
               strcpy(curinterface->if_name,token.text);
               curinterface->device = malloc(5);
               strcpy(curinterface->device,"com");
               curinterface->device[3] = token.text[2]+1;
               curinterface->device[4] = '\0';
            } else if (!stricmp(token.text,"default")) {
               /* Default interface */
               curinterface = &intf_default;
            } else {
               _slc_ERRMSG(_slc_ERRHDR,"Invalid interface \"%s\" "
                          "(use sl0 or 'default')", token.text);
               result = done = 1;
               break;
            }
         }

         /* This is a kludge - TBD: work on control flow in here */
         if (done) break;

         /* Process any parameters for this interface - First, make */
         /* sure that the next parseable element is the brace ({).  */
         _slc_NEXT_TOKEN;
         if (token.type != _slcC_TOKEN_LBRACE) {
            _slc_ERRMSG(_slc_ERRHDR,"Expecting {, read \"%s\"",token.text);
            result = 1;
            break;
         }

         /* Ok - we're into the elements - go until right brace (EOF=error) */
         do {
            _slc_NEXT_TOKEN;
            switch (token.type) {
               case _slcC_TOKEN_TEXT:
                  /* This is either element name or value, according to */
                  /* wantvalue variable - verify in either case.        */
                  if (wantvalue) {
                     if (rc = _slcSetIntfParm(curinterface,
                                              &_slc_IntfTable[parameter],
                                              &token)) {
                        switch (rc) {
                           case 1:
                              _slc_ERRMSG(_slc_ERRHDR,
                                          "Invalid parameter value \"%s\"",
                                          token.text);
                              break;
                           case 2:
                              _slc_ERRMSG(_slc_ERRHDR,"Parameter value (%s) "
                                          "out of range (%d-%d)",token.text,
                                          _slc_IntfTable[parameter].minimum,
                                          _slc_IntfTable[parameter].maximum);
                              break;
                           case 3:
                              _slc_ERRMSG(_slc_ERRHDR,"Memory allocation "
                                          "failure while parsing parameter");
                              break;
                           case 4:
                              _slc_ERRMSG(_slc_ERRHDR,"Internal error - "
                                          "unknown parameter");
                              break;
                           default:
                              _slc_ERRMSG(_slc_ERRHDR,"Internal error - "
                                          "unknown result (%d) from "
                                          "_slcSetIntfParm",rc);
                              break;
                        }
                        result = done = 1;
                     }
                     invalue = 1;
                  } else {
                     if ((parameter = _slcMatchToken(&token,
                                                     _slc_IntfTable)) < 0) {
                        _slc_ERRMSG(_slc_ERRHDR,"Invalid parameter \"%s\"",
                                    token.text);
                        result = done = 1;
                     } else {
                        allowvalue = _slc_IntfTable[parameter].valuetype > 0;
                        if (!allowvalue) {
                           _slcSetIntfParm(curinterface,
                                           &_slc_IntfTable[parameter],NULL);
                        }
                     }
                     invalue = 0;
                  }
                  wantvalue = 0;
                  break;
               case _slcC_TOKEN_EQUAL:
                  /* this is only valid if we don't yet want a value */
                  /* and we're allowed a value.                      */
                  if (wantvalue) {
                     _slc_ERRMSG(_slc_ERRHDR,
                                 "Expecting parameter, read \"%s\"",
                                 token.text);
                     result = done = 1;
                  } else if (!allowvalue) {
                     _slc_ERRMSG(_slc_ERRHDR,
                                 "Parameter \"%s\" does not require a value",
                                 _slc_IntfTable[parameter].name);
                     result = done = 1;
                  } else {
                     wantvalue = 1;
                  }
                  break;
               case _slcC_TOKEN_RBRACE:
                  /* Error if we are waiting for a value */
                  if (wantvalue) {
                     _slc_ERRMSG(_slc_ERRHDR,
                                 "Expecting parameter value, read \"%s\"",
                                 token.text);
                     result = done = 1;
                  } else {
                     invalue = 0;
                  }
                  break;
               case _slcC_TOKEN_EOF:
                  _slc_ERRMSG(_slc_ERRHDR,"Expecting }%s, got End-Of-File",
                              wantvalue ? "" : " or =");
                  result = done = 1;
                  break;
               default:
                  if (token.type <= _slcC_TOKEN_ERROR) {
                     _slc_ERRMSG(_slc_ERRHDR,
                                 "Tokenize error %d (%s) while reading file.",
                                 token.type,_slc_TokenErrorTable[token.type]);
                  } else {
                     _slc_ERRMSG(_slc_ERRHDR,"Expecting }%s, read \"%s\"",
                                 wantvalue ? "" : " or =",token.text);
                  }
                  result = done = 1;
                  break;
            } /* switch token.type */
         } while (token.type != _slcC_TOKEN_RBRACE && !done);

         if (curinterface->if_mtu == 0) {
            curinterface->if_mtu = 1006;  /* Default if not set otherwise */
         }
      } /* while !done */
      fclose(configfile);
   } /* if file ok */

   return(result);
}


/*--------------------------------------------------------------------------*/
/*      int slcFreeConfiguration (slcPS_INTERFACE *Interfaces)              */
/*..........................................................................*/
/*                                                                          */
/* Frees up a previously parsed interface configuration.                    */
/*                                                                          */
/*--------------------------------------------------------------------------*/
int slcFreeConfiguration (slcPS_INTERFACE *Interfaces)
{
   slcPS_INTERFACE curptr, delptr;

   curptr = *Interfaces;
   while (curptr) {
      if (curptr->device) {
         free(curptr->device);
      }
      delptr = curptr;
      curptr = curptr->next;
      free(delptr);
   }
   *Interfaces = NULL;
    return(0);
}
#endif /* NT */