/**
***  IPDial - IPDial.c
***
***  This is the main part of the program.
***
***
***  This program is free software; you can redistribute it and/or modify
***  it under the terms of the GNU General Public License as published by
***  the Free Software Foundation; either version 2 of the License, or
***  (at your option) any later version.
***
***  This program is distributed in the hope that it will be useful,
***  but WITHOUT ANY WARRANTY; without even the implied warranty of
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
***  GNU General Public License for more details.
***
***  You should have received a copy of the GNU General Public License
***  along with this program; if not, write to the Free Software
***  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***
***  Authors: Jochen Wiedmann <wiedmann@neckar-alb.de>
***           Stefan Gybas <cab@studbox.uni-stuttgart.de>
**/



#define VERS "IPDial 2.0"
#define DATE "06.03.96"
#define VSTRING VERS " (" DATE ")"
#define VERSTAG "\\0$VER: " VERS " (" DATE ")"
const static char VersTag[] = VERSTAG;


/**
***  Include files
**/
#ifndef IPDIAL_H
#include "IPDial.h"
#endif

#include <ctype.h>
#include <clib/alib_protos.h>





/**
***  This structure describes one command. All commands are stored
***  in a table at the end of the file.
**/
struct ScriptLine;
typedef VOID (*CommandFunc) (struct ScriptLine *);
struct Command
{ CommandFunc Func;
  STRPTR Name;
};





/**
***  Each line of the script file is stored in a structure like below.
**/
struct ScriptLine
{ struct MinNode mn;
  ULONG Num;
  CommandFunc CommFunc;
  STRPTR Label;
  STRPTR Args;
};





/**
***  Global variables
**/
LONG StatusVar;

struct MinList ScriptLineList;

struct ScriptLine *CurrentScriptLine;

ULONG EchoMode = FALSE;
ULONG VerboseMode = FALSE;

STRPTR SerialDeviceName = NULL;

struct RDArgs *MainRDArgs = NULL;

UBYTE ScratchBuffer[ScBufSize];        /*  temp buffer - use with caution!  */



/**
***  This function is used to skip blanks.
**/
STRPTR SkipBlanks(const UBYTE *ptr)

{ while(*ptr == ' '  ||  *ptr == '\t')
  { ++ptr;
  }
  return((STRPTR) ptr);
}





/**
***  This function is used to parse a string for characters like '\r' and
***  '\n'. $VAR or ${VAR} may be used to insert the value of local or
***  environment variable VAR, $$ may be used to insert the '$' character
***  itself, likewise \\.
***
***  Returns a pointer to a string allocated with malloc().
***  It is the task of the caller, to free() this string.
**/
STRPTR ParseString(const UBYTE *ptr)

{ STRPTR result;
  STATIC APTR parseBuffer = NULL;

  /**
  ***  Be sure, that buffer is valid.
  **/
  if (!parseBuffer  &&  !(parseBuffer = BufferCreate()))
  { perror("malloc");
    exit(10);
  }

  /**
  ***  Clear the buffer.
  **/
  BufferClear(parseBuffer);

  while(*ptr)
  { if (*ptr == '\\')
    { ++ptr;
      switch(*ptr)
      { case 'r':
          BufferExtend(parseBuffer, (STRPTR) "\r", 1);
          break;
        case 'n':
          BufferExtend(parseBuffer, (STRPTR) "\n", 1);
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          { char c;

            c = *ptr - '0';
            if (ptr[1] >= '0'  &&  ptr[1] <= '7')
            { c = (c << 3) + *(++ptr) - '0';
              if (ptr[1] >= '0'  &&  ptr[2] <= '7')
              { c = (c << 3) + *(++ptr) - '0';
              }
            }
            BufferExtend(parseBuffer, (STRPTR) &c, 1);
          }
          break;
        default:
          BufferExtend(parseBuffer, ptr, 1);
          break;
      }
      ++ptr;
    }
    else if (*ptr == '$')              /*  Found "$"  */
    { ++ptr;
      if (*ptr == '$')                 /*  Replace "$$" with "$"  */
      { BufferExtend(parseBuffer, (STRPTR) "$", 1);
        ++ptr;
      }
      else
      { const char* varPtr;
        size_t varLen = 0;

        if (*ptr == '{')               /*  Replace "${Var}" with value of  */
        { ++ptr;                       /*  of environment variable Var.    */
          varPtr = (const char*) ptr;
          while (*ptr && *ptr != '}')
          { ++ptr;
            ++varLen;
          }
         if (*ptr == '}')              /*  Skip '}'  */
          { ++ptr;
          }
        }
        else                           /*  Replace "$Var" with value of    */
        { varPtr = (const char*) ptr;  /*  environment variable Var.       */
          while (*ptr  &&  isalnum(*ptr))
          { ++ptr;
            ++varLen;
          }
        }

        /* Copy variable name to ScratchBuffer */

        strncpy(ScratchBuffer, varPtr, varLen);
        ScratchBuffer[varLen] = '\0';

        /* Is this legal: var name and result buffer are equal? */

        if (GetVar(ScratchBuffer, ScratchBuffer, ScBufSize, 0L))
        { BufferExtend(parseBuffer, ScratchBuffer, strlen(ScratchBuffer));
        }
      }
    }
    else if (*ptr == '"')              /*  filter extra quotes  */
    { ++ptr;
    }
    else
    { BufferExtend(parseBuffer, ptr, 1);
      ++ptr;
    }
  }

  BufferExtend(parseBuffer, (STRPTR) "", 1);

  if (!(result = (STRPTR) strdup((char*) BufferBuffer(parseBuffer))))
  { perror("malloc");
    exit(10);
  }

  return(result);
}





/**
***  This is an empty function. Just to allow lines with labels only.
**/
VOID NoneFunc(struct ScriptLine *line)

{
}





/**
***  This is the echo function.
**/
VOID EchoFunc(struct ScriptLine *line)

{ STRPTR args;

  if (!StrReadArgs(line->Args, (LONG *) &args, (STRPTR) "ARGS/F"))
  { fprintf(stderr, "Line %ld: Argument or memory error\n", line->Num);
    exit(10);
  }

  printf("%s", (char*) ParseString(args));
  fflush(stdout);
}





/**
***  This is the device function.
**/
VOID DeviceFunc(struct ScriptLine *line)

{ struct
  { STRPTR Device;
    STRPTR Protocol;
    LONG *Unit;
  } args;

  if (SerialDeviceName)
  { fprintf(stderr, "Line %ld: Device already open, ignoring.\n", line->Num);
  }

  args.Device = NULL;
  args.Protocol = NULL;
  args.Unit = NULL;

  if (!StrReadArgs(line->Args, (LONG *) &args,
                   (STRPTR) "DEVICE/A,PROTOCOL,UNIT/K/N"))
  { fprintf(stderr, "Line %ld: Argument or memory error\n", line->Num);
    exit(10);
  }

  if (!(SerialDeviceName = (STRPTR) strdup((char *) args.Device)))
  { perror("malloc");
    exit(10);
  }

  if (!SerialOpen(args.Device, args.Protocol, args.Unit ? *args.Unit : 0))
  { fprintf(stderr, "Line %ld: Unknown protocol.\n", line->Num);
    exit(10);
  }
}





/**
***  This function is used to set the serial.device parameters.
**/
VOID SetFunc(struct ScriptLine *line)

{ struct
  { ULONG *Baud;
    ULONG *DataBits;
    ULONG *StopBits;
    ULONG *BufSize;
    STRPTR Parity;
    STRPTR Protocol;
  } args;

  if (!SerialDeviceName)
  { fprintf(stderr, "Line %ld: Must open serial.device first.\n", line->Num);
    exit(10);
  }

  args.Baud = NULL;
  args.DataBits = NULL;
  args.StopBits = NULL;
  args.BufSize = NULL;
  args.Parity = NULL;
  args.Protocol = NULL;

  if (!(StrReadArgs(line->Args, (LONG *) &args,
                    (STRPTR) "BAUD/K/N,DATABITS/K/N,STOPBITS/K/N,BUFSIZE/K/N,"
                             "PARITY/K,PROTOCOL/K")))
  { fprintf(stderr, "Line %ld: Argument or memory error.\n", line->Num);
    exit(10);
  }

  if (args.Baud)
  { SerialSetBaud(*args.Baud);
  }
  if (args.DataBits)
  { SerialSetDataBits(*args.DataBits);
  }
  if (args.StopBits)
  { SerialSetStopBits(*args.StopBits);
  }
  if (args.BufSize)
  { SerialSetBufSize(*args.BufSize);
  }
  if (args.Parity)
  { if (!SerialSetParity(args.Parity))
    { fprintf(stderr, "Line %ld: Unknown parity.\n", line->Num);
      exit(10);
    }
  }
  if (args.Protocol)
  { if (!SerialSetProtocol(args.Protocol))
    { fprintf(stderr, "Line %ld: Unknown protocol.\n", line->Num);
      exit(10);
    }
  }
}





/**
***  This function shows the serial.device parameters.
**/
VOID ShowParmsFunc(struct ScriptLine *line)

{ if (!SerialDeviceName)
  { fprintf(stderr, "Line %ld: Must open serial.device first.\n", line->Num);
    exit(10);
  }

  printf("%s Parameters:\n\n", SerialDeviceName);
  SerialShowParms();
}





/**
***  This function sends a string to the serial.device.
**/
VOID SendFunc(struct ScriptLine *line)

{ STRPTR args; /* , send; */

  if (!SerialDeviceName)
  { fprintf(stderr, "Line %ld: Must open serial.device first.\n", line->Num);
    exit(10);
  }

  if (!StrReadArgs(line->Args, (LONG *) &args, (STRPTR) "ARGS/F"))
  { fprintf(stderr, "Line %ld: Argument or memory error\n", line->Num);
    exit(10);
  }

  SerialSend(ParseString(args));
}





/**
***  This is similar to "if".
**/
VOID DoGoto(STRPTR Label, int Num)

{ struct ScriptLine *sl;

  for(sl = (struct ScriptLine *) ScriptLineList.mlh_Head;
            sl->mn.mln_Succ;
            sl = (struct ScriptLine *) sl->mn.mln_Succ)
  { if (sl->Label  &&  strcmp((char *) sl->Label, (char *) Label) == 0)
    { CurrentScriptLine = (struct ScriptLine *) sl->mn.mln_Pred;
      return;
    }
  }

  fprintf(stderr, "Line %d: Unknown label.\n", Num);
  exit(10);
}
VOID OnFunc(struct ScriptLine *line)

{ STRPTR ptr = line->Args;

  if (strnicmp((char *) ptr, "status", 6) == 0)
  { ptr = SkipBlanks(ptr+6);
    if (strnicmp((char *) ptr, "goto", 4) == 0)
    { STRPTR *labels;
      int i;

      if (!StrReadArgs(ptr+4, (LONG *) &labels, (STRPTR) "LABELS/M"))
      { fprintf(stderr, "Line %ld: Memory error.\n", line->Num);
        exit(10);
      }
      for (i = -1;  *labels;  ++i, ++labels)
      { if (i == StatusVar)
        { DoGoto(*labels, line->Num);
          return;
        }
      }
      return;
    }
  }

  fprintf(stderr, "Line %ld: Condition syntax.", line->Num);
  exit(10);
}





/**
***  This function waits for certain strings.
**/
VOID WaitFunc(struct ScriptLine *line)

{ struct
  { STRPTR *WaitArgs;
    ULONG *TimeOut;
  } args;
  STRPTR* parsedArgs;
  LONG TimeOut;
  int i = 0;

  if (!SerialDeviceName)
  { fprintf(stderr, "Line %ld: Must open serial.device first.\n", line->Num);
    exit(10);
  }

  args.WaitArgs = NULL;
  args.TimeOut = NULL;

  if (!(StrReadArgs(line->Args, (LONG *) &args, (STRPTR) "ARGS/M,TIMEOUT/N/K")))
  { fprintf(stderr, "Line %ld: Argument or memory error.\n", line->Num);
    exit(10);
  }

  if (!args.WaitArgs)
  { fprintf(stderr, "Line %ld: Missing argument.\n", line->Num);
    exit(10);
  }
  if (!args.TimeOut)
  { fprintf(stderr, "Line %ld: Missing timeout argument.\n", line->Num);
    exit(10);
  }
  TimeOut = *args.TimeOut;


  while (args.WaitArgs[i])
  { ++i;
  }

  if (!(parsedArgs = malloc(sizeof(STRPTR) * (i+1))))
  { perror("malloc");
    exit(10);
  }

  for (i = 0;  args.WaitArgs[i];  i++)
  { parsedArgs[i] = ParseString(args.WaitArgs[i]);
  }
  parsedArgs[i] = NULL;

  StatusVar = SerialWait(parsedArgs, TimeOut);
}





/**
***  The delay function waits for a certain amount of time.
**/
VOID DelayFunc(struct ScriptLine *line)

{ struct
  { LONG *secs;
    LONG *millisecs;
  } args;

  args.secs = NULL;
  args.millisecs = NULL;

  if (!StrReadArgs(line->Args, (LONG *) &args,
                   (STRPTR) "SECS/A/N,MILLISECS/N"))
  { fprintf(stderr, "Line %ld: Argument or memory error\n", line->Num);
    exit(10);
  }

  if (*args.secs < 0)
  { fprintf(stderr, "Line %ld: Invalid time.\n", line->Num);
    exit(10);
  }

  if (args.millisecs == NULL)
  { TimerWait(*args.secs, 0L);
  }
  else
  { if (*args.millisecs < 0)
    { fprintf(stderr, "Line %ld: Invalid time.\n", line->Num);
      exit(10);
    }
    TimerWait(*args.secs, *args.millisecs);
  }
}





/**
***  And this is the Exit function.
**/
VOID ExitFunc(struct ScriptLine *line)

{ exit(atol((char *) line->Args));
}





/**
***  The Goto command
**/
VOID GotoFunc(struct ScriptLine *line)

{ STRPTR Label;

  if (!(StrReadArgs(line->Args, (LONG *) &Label, (STRPTR) "LABEL/A")))
  { fprintf(stderr, "Line %ld: Missing argument or memory error.\n",
            line->Num);
    exit(10);
  }
  DoGoto(Label, line->Num);
}





/**
***  This function enters terminal mode.
**/
VOID TermFunc(struct ScriptLine *line)

{ struct
  { STRPTR eof;
    ULONG noecho;
    ULONG raw;
  } args;

  args.eof = NULL;
  args.noecho = FALSE;
  args.raw = FALSE;

  if (!StrReadArgs(line->Args, (LONG *) &args, (STRPTR) "EOF,NOECHO/S,RAW/S"))
  { fprintf(stderr, "Line %ld: Argument or memory error\n", line->Num);
    exit(10);
  }

  if (args.eof)
  { args.eof = ParseString(args.eof);
  }
  SerialTerminal(args.eof, !args.noecho, !args.raw);
}





/**
***  Execute external commands.
**/
VOID SystemFunc(struct ScriptLine *line)

{ STRPTR args;
  int result;

  if (!StrReadArgs(line->Args, (LONG *) &args, (STRPTR) "ARGS/F"))
  { fprintf(stderr, "Line %ld: Argument or memory error\n", line->Num);
    exit(10);
  }

  if (VerboseMode)
  { printf(">>> Executing command: \"%s\"\n", args);
  }

  result = system((char *) args);

  if (result)
  { if (result > 1  &&  result < 10)  /* Warning */
    { if (VerboseMode)
      { printf(">>> Returned %d.\n", result);
      }
    }
    else
    { printf("Error: Command \"%s\" returned %d.\n", args, result);
      exit(result);
    }
  }
}





/**
***  Scan the buffer received by the last "wait" command.
**/
VOID ScanFunc(struct ScriptLine *line)

{ struct
  { STRPTR format;
    ULONG global;
    ULONG save;
  } args;
  ULONG flags;

  args.format = NULL;
  args.global = FALSE;
  args.save = FALSE;

  if (!(StrReadArgs(line->Args, (LONG *) &args,
                    (STRPTR) "FORMAT/A,GLOBAL/S,SAVE/S")))
  { fprintf(stderr, "Line %ld: Argument or memory error.\n", line->Num);
    exit(10);
  }

  flags = 0;
  if (args.save)
  { args.global = TRUE;
    flags |= GVF_SAVE_VAR;
  }
  if (args.global)
  { flags |= GVF_GLOBAL_ONLY;
  }
  else
  { flags |= GVF_LOCAL_ONLY;
  }

  StatusVar = Vsscanf(SerialWaitBuffer(), args.format, flags);
}




/**
***  The SetVar command
**/
void SetVarFunc(struct ScriptLine *line)

{ struct {
    STRPTR name;
    STRPTR value;
    ULONG global;
    ULONG save;
  } args;
  int flags;

  args.name = NULL;
  args.value = NULL;
  args.global = FALSE;
  args.save = FALSE;

  if (!(StrReadArgs(line->Args, (LONG *) &args,
                    (STRPTR) "NAME/A,VALUE/A,GLOBAL=GLOBALONLY/S,SAVE/S")))
  { fprintf(stderr, "Line %ld: Missing argument or memory error.\n",
            line->Num);
    exit(10);
  }

  flags = 0;
  if (args.save) {
      flags |= GVF_SAVE_VAR;
      args.global = TRUE;
  }
  if (args.global) {
      flags |= GVF_GLOBAL_ONLY;
  } else {
      flags |= GVF_LOCAL_ONLY;
  }

  SetVar(args.name, args.value, -1, LV_VAR | flags);
}




/**
***  This is the command table.
**/
struct Command CommandTab[] =
{ { EchoFunc,       (STRPTR) "echo"         },
  { DeviceFunc,     (STRPTR) "device"       },
  { SetFunc,        (STRPTR) "set"          },
  { ShowParmsFunc,  (STRPTR) "showparms"    },
  { SendFunc,       (STRPTR) "send"         },
  { OnFunc,         (STRPTR) "on"           },
  { WaitFunc,       (STRPTR) "wait"         },
  { DelayFunc,      (STRPTR) "delay"        },
  { ExitFunc,       (STRPTR) "exit"         },
  { GotoFunc,       (STRPTR) "goto"         },
  { TermFunc,       (STRPTR) "terminal"     },
  { SystemFunc,     (STRPTR) "system"       },
  { ScanFunc,       (STRPTR) "scan"         },
  { SetVarFunc,     (STRPTR) "setvar"       },
  { NULL,           NULL                    }
};





/**
***  This function is used to process the file. Rather easy,
***  isn't it? :-)
**/
VOID ProcessFile(VOID)

{ for(CurrentScriptLine = (struct ScriptLine *) ScriptLineList.mlh_Head;
      CurrentScriptLine->mn.mln_Succ;
      CurrentScriptLine = (struct ScriptLine *) CurrentScriptLine->mn.mln_Succ)
  { (*CurrentScriptLine->CommFunc)(CurrentScriptLine);
  }
}





/**
***  This function reads and parses the file.
**/
VOID ParseFile(STRPTR file)

{ FILE *fp;
  ULONG linenum = 0;
  ULONG success = TRUE;

  if (!(fp = fopen((char *) file, "r")))
  { fprintf(stderr, "Could not open %s for reading.\n", file);
    exit(10);
  }

  NewList((struct List *) &ScriptLineList);

  while(fgets((char *) ScratchBuffer, ScBufSize, fp))
  { ULONG len = strlen((char *) ScratchBuffer);
    STRPTR line;
    STRPTR Label = NULL;
    CommandFunc CommFunc;

    ++linenum;

    if (ScratchBuffer[len-1] != '\n')
    { fprintf(stderr, "Line %ld too long (must be less than %ld).\n",
                      linenum, ScBufSize);
      exit(10);
    }

    if (!(line = malloc(len+1)))
    { perror("malloc");
      exit(10);
    }
    strcpy((char*) line, (char*) ScratchBuffer);
    line = SkipBlanks(line);

    /**
    ***  Check for a label
    **/
    { STRPTR ptr = line;

      if (isalpha(*ptr))
      { do
        { ++ptr;
        }
        while(isalnum(*ptr));

        if (*ptr == ':')
        { *ptr = '\0';
          Label = line;
          line = SkipBlanks(ptr+1);
        }
      }
    }

    /**
    ***  Check for empty line or comment line
    **/
    if (*line == ';'  ||  *line == '\r'  ||  *line == '\n')
    { CommFunc = NoneFunc;
      if (!Label)
      { continue;
      }
    }

    /**
    ***  If no empty line: Check for command
    **/
    else
    { struct Command *comm;

      for (comm = &CommandTab[0];  comm->Func;  ++comm)
      { ULONG len = strlen((char *) comm->Name);

        if (strnicmp((char *) comm->Name, (char *) line, len) == 0  &&
            (line[len] == ' '  ||  line[len] == '\t'  ||
             line[len] == '\r' ||  line[len] == '\n'))
        { line = SkipBlanks(line+len);
          CommFunc = comm->Func;
          break;
        }
      }

      if (!comm->Func)
      { fprintf(stderr, "Line %ld: Unknown command - ignored\n", linenum);
        success = FALSE;
      }
    }

    /**
    ***  Allocate a new scriptline structure.
    **/
    { struct ScriptLine *sl;

      if (!(sl = malloc(sizeof(*sl))))
      { perror("malloc");
        exit(10);
      }

      AddTail((struct List *) &ScriptLineList, (struct Node *) sl);
      sl->Num = linenum;
      sl->CommFunc = CommFunc;
      sl->Args = line;
      sl->Label = Label;
    }
  }

  if (ferror(fp))
  { perror("fgets");
    exit(10);
  }

  if (!success)
  { exit(10);
  }

  fclose(fp);
}





/**
***  This is the Cleanup() function, called, when the program terminates.
**/
VOID Cleanup(VOID)

{ SerialCleanup();
  StrReadArgsFree();
  if (MainRDArgs)
  { FreeArgs(MainRDArgs);
  }
}





/**
***  This function hints about the GPL.
**/
VOID ShowGPL(FILE *fp)

{ fprintf(fp, "This program is governed by the terms and conditions of the\n");
  fprintf(fp, "GNU General Public License. A copy should have come with\n");
  fprintf(fp, "this distribution. (See the file COPYING.) In that license\n");
  fprintf(fp, "it is made clear that you are welcome to redistribute either\n");
  fprintf(fp, "verbatim or modified copies of the program and the documentation\n");
  fprintf(fp, "under certain conditions. Further you are told that this program\n");
  fprintf(fp, "comes with ABSOLUTELY NO WARRANTY!\n\n");
}





/**
***  This function displays the Usage() message.
**/
VOID Usage(VOID)

{ fprintf(stderr, "\n%s - written by Jochen Wiedmann and Stefan Gybas\n\n", VSTRING);
  fprintf(stderr, "Usage:  IPDial SCRIPT,DEVICE/K,PROTOCOL/K,UNIT/K/N,BAUD/K/N,SANADEV/K,\n");
  fprintf(stderr, "               TERMINAL/S,ECHO/S,VERBOSE/S,RAW/S,HELP/S\n\n");
  fprintf(stderr, "\tSCRIPT:    Script file to execute [T}\n");
  fprintf(stderr, "\tDEVICE:    Device to use (default: \"serial.device\") [T]\n");
  fprintf(stderr, "\tPROTOCOL:  Protocol to use: XONXOFF, 7WIRE (default) or NONE [T]\n");
  fprintf(stderr, "\tUNIT:      Unit to use (default 0) [T]\n");
  fprintf(stderr, "\tBAUD:      Baud rate to use (default is taken from serial prefs) [T]\n");
  fprintf(stderr, "\tSANADEV:   SANA config file to read serial config from [T,S]\n");
  fprintf(stderr, "\tTERMINAL:  Run in terminal mode (i.e. don't execute script)\n");
  fprintf(stderr, "\tECHO:      Show modem replies [S]\n");
  fprintf(stderr, "\t           Use local echo mode [T]\n");
  fprintf(stderr, "\tVERBOSE:   Show all operations the program performs [T,S]\n");
  fprintf(stderr, "\tRAW:       Use character mode instead of line mode [T]\n\n");
  fprintf(stderr, "\t[S]: argument is used in script mode\n");
  fprintf(stderr, "\t[T]: argument is used in terminal mode\n\n");
  ShowGPL(stderr);
  exit(5);
}





/**
***  This is main().
**/
int main(int argc, char *argv[])

{ struct
  { STRPTR script;
    STRPTR device;
    STRPTR protocol;
    LONG *unit;
    LONG *baud;
    STRPTR sanadev;
    ULONG terminal;
    ULONG echo;
    ULONG verbose;
    ULONG raw;
  } args;

  args.script = NULL;
  args.device = (STRPTR) "serial.device";
  args.protocol = (STRPTR) "7wire";
  args.unit = NULL;
  args.baud = NULL;
  args.sanadev = NULL;
  args.terminal = FALSE;
  args.echo = FALSE;
  args.verbose = FALSE;
  args.raw = FALSE;

  if (!argc)    /*  No WB handling.     */
  { exit(-1);
  }

  if (atexit(Cleanup))
  { fprintf(stderr, "Memory error.\n");
    exit(20);
  }

  if (!(MainRDArgs = ReadArgs((STRPTR) "SCRIPT,DEVICE/K,PROTOCOL/K,UNIT/K/N,"
                                                   "BAUD/K/N,SANADEV/K,TERMINAL/S,"
                                                    "ECHO/S,VERBOSE/S,RAW/S",
                              (LONG *) &args, NULL)))
  { fprintf(stderr, "Cannot parse command line.\n");
  }

  if (!args.script  &&  !args.terminal)
  { Usage();
  }

  EchoMode = args.echo;
  if (args.verbose)
  { VerboseMode = TRUE;
    printf(">>> %s  - verbose mode\n", VSTRING);
  }

  if(args.sanadev)
  { SerialDeviceName = (STRPTR) strdup(ReadSanaConfig(args.sanadev));
  }

  if (args.terminal)
  { if (!SerialDeviceName)
    { SerialOpen(args.device, args.protocol, args.unit ? *args.unit : 0);
      SerialDeviceName = (STRPTR) strdup(args.device);
      if (args.baud)
      { SerialSetBaud(*args.baud);
      }
    }

    SerialTerminal(NULL, EchoMode, !args.raw);
  }
  else if (args.script)
  { ParseFile(args.script);
    ProcessFile();
  }

  exit(0);
}
