/**
***  IPDial     Script program for initializing a SLIP connection
***  Copyright  (C)   1994    Jochen Wiedmann
***
***  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.
***
***
***  This is the main part of the program.
***
***
***  Computer: Amiga 1200                       Compiler: Dice 3.01
***
***  Author:    Jochen Wiedmann
***             Am Eisteich 9
***             72555 Metzingen
***             Germany
***
***             Phone: (+0049) 7123 / 14881
***             Internet: wiedmann@neckar-alb.de
***
***
***  History:   V 1.1   23.11.94        Initial version
***
***             V 1.2   27.02.95        Added terminal mode
***                                     Now using ReadArgs() for
***                                     command line parsing.
***
***             V 1.3   09.03.95        Added environment variable
***                                     aupport to "send" command.
***                                     Added unit support.
***
***             V 1.4   21.04.95        Added "system" command.
***                                     Added environment variable
***                                     support to "echo" command.
***                                     Terminal mode now converts
***                                     LF to CR/LF, so that modem
***                                     recognizes commands.
***                                     (Let's hope, that will still
***                                     work for entering passwords. :-(
***
***             V 1.5   30.04.95        Added "scan" command.
***
***             V 1.6   26.06.95        "delay" command supporting
***                                     ticks; ParseString()
***                                     supporting octal characters;
***                                     "wait" command using
***                                     ParseString()
***                                     (All Suggested by Will Bow.)
***
***                                     Fixed bug in SerialSend():
***                                     *.io_Device = *.io_Unit
***
***             V 1.7   22.07.95        Added ECHO, RAW and EOF options
***                                     to "terminal" command.
***                                     (Suggested by Klaus Heinz.)
***
***                                     Added BAUD option to command
***                                     line.
***
***                                     Added "setvar" command.
***
***             v 1.8   24.11.95        Put a 10 tick delay after the
***                                     DoIO() call in DeviceIODo()
**/
#define VERSION     1
#define REVISION    8
#define VSTRING     "IPDial 1.8 (24.11.95)"
#define VERSTAG     "\0$VER: IPDial 1.8 (24.11.95)"
/**
***  Include files
**/
#include "IPDial.h"
#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;

const UBYTE VersTag [] = VERSTAG;
const UBYTE VString [] = VSTRING;

ULONG EchoMode = FALSE;
ULONG VerboseMode = FALSE;

STRPTR SerialDeviceName = NULL;

struct RDArgs *MainRDArgs = NULL;
/**
***  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' or '\n'. $VAR or ${VAR} may be used to insert
***  the value of 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 == '$')
    { 
      ++ptr;
      if (*ptr == '$')
      { 
        BufferExtend(parseBuffer, (STRPTR) "$", 1);
	      ++ptr;
      }
      else
      { 
        char *varName;
	      const char* varPtr;
	      size_t varLen = 0;

	      if (*ptr == '{')
	      { 
          ++ptr;
	        varPtr = (const char*) ptr;
	        while (*ptr && *ptr != '}')
	        { 
            ++ptr;
	          ++varLen;
	        }
	        if (*ptr == '}')
	        { 
            ++ptr;
	        }
	      }
	      else
	      { 
          varPtr = (const char*) ptr;
	        while (*ptr  &&  isalnum(*ptr))
	        { 
            ++ptr;
	          ++varLen;
	        }
	      }

	      if (!(varName = malloc(varLen + 1)))
	      { 
          perror("malloc");
	        exit(10);
	      }
	      strncpy(varName, varPtr, varLen);
	      varName[varLen] = '\0';

	      if ((varPtr = getenv(varName)))
	      { 
          BufferExtend(parseBuffer, (STRPTR) varPtr, strlen(varPtr));
	      }
      }
    }
    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;
  int i;

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

  for(i = 0;  *args;  ++i, ++args)
  { 
    char *str;

    if (i)
    { 
      putchar(' ');
    }
    str = (char*) ParseString(*args);
    fputs((char *) str, stdout);
    fflush(stdout);
    free(str);
  }
}
/**
***  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.Unit = NULL;
  args.Device = NULL;
  args.Protocol = NULL;

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

  if (!args.Device)
  { 
    fprintf(stderr, "Line %ld: Missing device name.\n", line->Num);
    exit(10);
  }
  if (!(SerialDeviceName = (STRPTR) strdup((char *) args.Device)))
  { 
    perror("malloc");
    exit(10);
  }

  if (VerboseMode)
  { 
    printf("DeviceFunc: Opening %s.\n", args.Device);
  }

  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);
    }
  }

  if (VerboseMode)
  { 
    printf("SetFunc: %s Parameters modified:\n\n", SerialDeviceName);
    SerialShowParms();
  }
}
/**
***  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;

  if (VerboseMode)
  { 
    fflush(stdout);
    printf("\nSendFunc: Sending: ");
  }


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

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

  for(;  *args;  args++)
  { 
    STRPTR send = ParseString(*args);

    if (VerboseMode)
      printf(" %s \n", send);
      
    SerialSend(send, strlen((char *) send));
    free(send);
  }

  if (VerboseMode)
  { 
    printf("\n");
  }
}
/**
***  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(VerboseMode)
    printf("\nWaitFunc: line->Args %s \n",line->Args);
    
  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)

{ 
  ULONG tics; 
  
  if (VerboseMode)
    printf("DelayFunc: line->Args: %s\n",(char *) line->Args);
    
  tics = (atoi((char *) line->Args) * 50);

  if (tics == 0)
  { 
    fprintf(stderr, "DelayFunc: Line %ld: Missing argument.\n", line->Num);
    exit(10);
  }

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

{ 
  LONG result = atol((char *) line->Args);
  exit(result);
}
/**
***  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 i, result;
  char *cmd;
  STATIC APTR systemBuffer = NULL;

  /**
  ***  Initialize buffer.
  **/
  if (!systemBuffer  &&  !(systemBuffer = BufferCreate()))
  { 
    perror("malloc");
    exit(10);
  }
  BufferClear(systemBuffer);


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

  for(i = 0;  *args;  ++i, ++args)
  { 
    char *str;

    if (i)
    { 
      BufferExtend(systemBuffer, (STRPTR) " ", 1);
    }
    str = (char*) ParseString(*args);
    BufferExtend(systemBuffer, (STRPTR) str, strlen(str));
    free(str);
  }
  cmd = (char*) BufferBuffer(systemBuffer);

  if (VerboseMode)
  { 
    printf("Executing command: \"%s\"\n", cmd);
  }
  result = system(cmd);
  if (result)
  { 
    if (result > 1  &&  result < 10)  /* Warning */
    { 
      if (VerboseMode)
      { 
        printf("Returned %d.\n", result);
      }
    }
    else
    { printf("Error: Command \"%s\" returned %d.\n", cmd, 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,LOCALONLY/S,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, 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;
  STATIC UBYTE buffer[4096];
  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 *) buffer, sizeof(buffer), fp))
  { 
    ULONG len = strlen((char *) buffer);
    STRPTR line;
    STRPTR Label = NULL;
    CommandFunc CommFunc;

    ++linenum;

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

    if (!(line = malloc(len+1)))
    { 
      perror("malloc");
      exit(10);
    }
    strcpy((char*) line, (char*) buffer);
    
    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.\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(VerboseMode)
      {
        printf("\n");
        printf("ParseFile: Num      %d  \n", sl->Num);
        printf("ParseFile: CommFunc 0x%x\n", sl->CommFunc);
        printf("ParseFile: Args     %s  \n", sl->Args);
        printf("ParseFile: Label    %s  \n", sl->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");
}
/**
***  This function displays the Usage() message.
**/
VOID Usage(VOID)
{ 
  fprintf(stderr, "Usage: IPDial SCRIPT,DEVICE/K,PROTOCOL/K,TERMINAL/S,ECHO/S,VERBOSE/S,HELP/S\n\n");
  fprintf(stderr, "\tSCRIPT:    Script file to execute.\n");
  fprintf(stderr, "\tDEVICE:    Device to use (default serial.device)\n");
  fprintf(stderr, "\tPROTOCOL:  Protocol to use; XONXOFF, 7WIRE (default) or NONE)\n");
  fprintf(stderr, "\tUNIT:      Unit to use (default 0)\n");
  fprintf(stderr, "\tECHO:      Show modems replies.\n");
  fprintf(stderr, "\tVERBOSE:   Be verbose.\n\n");
  fprintf(stderr, "\tTERMINAL:  Run in terminal mode.\n");
  fprintf(stderr, "\tHELP:      Print this message.\n");
  fprintf(stderr, "\n\n%s  @ 1994 by Jochen Wiedmann\n\n", VString);
  ShowGPL(stderr);
  exit(5);
}
/**
***  This is main().
**/
int main(int argc, char *argv[])
{ 
  struct
  { 
    STRPTR file;
    STRPTR device;
    STRPTR protocol;
    LONG *unit;
    LONG *baud;
    ULONG terminal;
    ULONG help;
    ULONG echo;
    ULONG verbose;
    ULONG raw;
  } args;

  args.file = NULL;
  args.device = (STRPTR) "serial.device";
  args.protocol = (STRPTR) "7wire";
  args.unit = NULL;
  args.baud = NULL;
  args.terminal = FALSE;
  args.echo = FALSE;
  args.verbose = FALSE;
  args.help = 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,TERMINAL/S,"
			                                 "HELP/S,ECHO/S,VERBOSE/S,RAW/S",
			                                  (LONG *) &args, NULL)))
  { 
    fprintf(stderr, "Cannot parse command line.\n");
  }

  if (args.help  || (!args.file  &&  !args.terminal))
  { 
    Usage();
  }

  EchoMode = args.echo;
  if (args.verbose)
  { 
    VerboseMode = TRUE;
    printf("%s  @ 1994 by Jochen Wiedmann\n\n", VString);
    ShowGPL(stdout);
    printf("\n\n");
  }

  if (args.terminal)
  { 
    if(VerboseMode)
      printf("\nMain: %d\n",__LINE__);
      
    SerialOpen(args.device, args.protocol, args.unit ? *args.unit : 0);
    if (args.baud)
    { 
      if(VerboseMode)
        printf("\nMain: %d\n",__LINE__);
      SerialSetBaud(*args.baud);
    }
    if(VerboseMode)
      printf("\nMain: %d\n",__LINE__);
    SerialTerminal(NULL, TRUE, !args.raw);
  }
  else if (args.file)
  { 
    if(VerboseMode)
      printf("\nMain: %d\n",__LINE__);
    ParseFile(args.file);
    if(VerboseMode)
      printf("\nMain: %d\n",__LINE__);
    ProcessFile();
  }

  exit(0);
}
