/*
**  :ts=4
**
**  Amiga support module for HYDRA protocol sample implementation.
**
**  Written by  Olaf Barthel
**      Brabeckstrasse 35
**      D-30559 Hannover
**
**      eMail: olsen@sourcery.han.de
**
**  Freely distributable.
*/

  /* System includes. */

#include <intuition/intuitionbase.h>

#include <libraries/gadtools.h>
#include <libraries/locale.h>
#include <libraries/asl.h>

#include <graphics/gfxbase.h>

#include <utility/date.h>

#include <devices/conunit.h>
#include <devices/serial.h>
#include <devices/timer.h>

#include <hardware/cia.h>

#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dostags.h>
#include <dos/dosasl.h>

#include <exec/memory.h>

#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/locale_protos.h>
#include <clib/timer_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/asl_protos.h>
#include <clib/macros.h>

#include <pragmas/intuition_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/locale_pragmas.h>
#include <pragmas/timer_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/asl_pragmas.h>

#include <errno.h>

//#define DB(x) x
#define DB(x) ;

void __stdargs kprintf(STRPTR,...);

#include "hydracom.h"

#include <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/Access.h>
#include <HBBS/types.h>
#include <HBBS/structures.h>

#include <HBBS/Hbbscommon_protos.h>
#ifdef __SASC
#include <HBBS/hbbscommon_pragmas_sas.h>
#else
#include <HBBS/hbbscommon_pragmas_stc.h>
#endif

#include <HBBS/Hbbsnode_protos.h>
#ifdef __SASC
#include <HBBS/Hbbsnode_pragmas_sas.h>
#else
#include <HBBS/Hbbsnode_pragmas_stc.h>
#endif

#include <HBBS/release.h>

extern int N_NodeNum;
extern struct BBSGlobalData *BBSGlobal;
extern struct NodeData *N_ND;

extern long AlreadyClosed;
  /* Difference between UTC and Amiga time. */

#define UTC_OFFSET      252482400

  /* Minimum of lines to reserve for local input. */

#define MIN_LINES     3

  /* Serial buffer size. */

#define BUFFER_SIZE     8192

  /* Maximum length of a file name for now. */

#define MAX_FILENAME_LENGTH 256

  /* A handy macro. */

#define ClrSignal(s)  SetSignal(0,s)

  /* this macro lets us long-align structures on the stack */

#define D_S(type,name) char a_##name[sizeof(type)+3]; \
                       type *name = (type *)((LONG)(a_##name+3) & ~3);

  /* Signal masks. */

#define SIG_SERREAD   (1UL << ReadPort->mp_SigBit)
#define SIG_SERWRITE  (1UL << WritePort->mp_SigBit)
#define SIG_CONREAD   (1UL << ConsoleReadPort->mp_SigBit)
#define SIG_TIMER   (1UL << TimePort->mp_SigBit)
#define SIG_WINDOW    (1UL << WindowPort->mp_SigBit)
#define SIG_HANDSHAKE SIGF_SINGLE
#define SIG_KILL    SIGBREAKF_CTRL_C

  /* A serial buffer structure. */

struct SerialBuffer
{
  struct IOExtSer *SerialRequest;
  UBYTE     *SerialBuffer,
          *SerialIndex,
          *SerialTop;
  LONG       SerialSize,
           SerialFilled;
  BOOL       IsClone,
           IsBusy;
       struct MsgPort *OldPort;
};

  /* Library bases. */

extern struct ExecBase    *SysBase;
extern struct DosLibrary  *DOSBase;

extern struct IntuitionBase   *IntuitionBase;
extern struct GfxBase       *GfxBase;
extern struct LocaleBase      *LocaleBase;
extern struct Library       *GadToolsBase;

struct Library        *UtilityBase,
              *TimerBase,
              *AslBase;

  /* Timer data. */

struct MsgPort    *TimePort;
struct timerequest  *TimeRequest;

  /* Serial data. */

struct MsgPort    *ReadPort,
          *WritePort;

struct SerialBuffer *ThisBuffer,
          *NextBuffer,
          *ReadBuffer;

  /* Console data. */

struct MsgPort    *WindowPort;

struct Window
          *RemoteWindow,
          *LocalWindow;

struct MsgPort    *ConsoleWritePort,
          *ConsoleReadPort;
struct IOStdReq   *ConsoleReadRequest;
UBYTE        ConsoleChar;
BOOL         ConsoleReady = FALSE,
           WindowReady = FALSE;

struct IOStdReq
          *RemoteRequest,
          *LocalRequest;

  /* DOS Data. */

struct Process    *ThisProcess;
APTR         OldPtr;
LONG         OldPri;

struct AnchorPath *Anchor;
BOOL         AnchorUsed = FALSE;

  /* Screen data. */

struct Screen     *Screen=NULL;

  /* Menu data. */

APTR         VisualInfo;
struct Menu     *Menu;

struct NewMenu MenuTemplate[] =
{
  { NM_TITLE, "Project",         0 ,  0,  0,  (APTR)0},
  {  NM_ITEM, "Toggle chat",      "C",  0,  0,  (APTR)Alt_C},
  {  NM_ITEM, NM_BARLABEL,       0 ,  0,  0,  (APTR)0},
  {  NM_ITEM, "Hang up",        "H",  0,  0,  (APTR)Alt_H},
  {  NM_ITEM, "Toggle duplex",    "E",  0,  0,  (APTR)Alt_E},
  {  NM_ITEM, "Toggle 7 bits/8 bits", "B",  0,  0,  (APTR)Alt_B},
  {  NM_ITEM, NM_BARLABEL,       0 ,  0,  0,  (APTR)0},
  {  NM_ITEM, "Start upload",     "U",  0,  0,  (APTR)PgUp},
  {  NM_ITEM, "Start download",   "D",  0,  0,  (APTR)PgDn},
  {  NM_ITEM, NM_BARLABEL,       0 ,  0,  0,  (APTR)0},
  {  NM_ITEM, "Abort Hydra session",  ".",  0,  0,  (APTR)Esc},
  {  NM_ITEM, NM_BARLABEL,       0 ,  0,  0,  (APTR)0},
  {  NM_ITEM, "Exit HydraCom",    "Q",  0,  0,  (APTR)Alt_X},
  { NM_END }
};

  /* Time data. */

LONG       GMT_Offset = UTC_OFFSET;

  /* Standard file requester. */

struct FileRequester *FileRequester;

  /* Version ID. */

STRPTR VersionTag = "$VER: hydracom 1.0r7 (15.12.95)\r\n";

  /* CloseWindowSafely(struct Window *Window):
   *
   *  Close a window sharing its UserPort with other
   *  windows.
   */

STATIC VOID
CloseWindowSafely(struct Window *Window)
{
  Forbid();

  if(Window->UserPort)
  {
    struct Node *Node,*Next;

    for(Node = Window->UserPort->mp_MsgList.lh_Head ; Next = Node->ln_Succ ; Node = Next)
    {
      if(((struct IntuiMessage *)Node)->IDCMPWindow == Window)
      {
        Remove(Node);
        ReplyMsg((struct Message *)Node);
      }
    }

    Window->UserPort = NULL;
  }

  ModifyIDCMP(Window,NULL);

  Permit();

  CloseWindow(Window);
}

  /* UpdateTime(struct timeval *Now):
   *
   *  Get the current time and/or update the current
   *  time offset data.
   */

STATIC VOID
UpdateTime(struct timeval *Now)
{
  if(Now)
    Now->tv_secs = Now->tv_micro = 0;

  if(TimePort = CreateMsgPort())
  {
    if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
    {
      if(!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimeRequest,NULL))
      {
        TimerBase = (struct Library *)TimeRequest->tr_node.io_Device;

        if(LocaleBase)
        // = (struct LocaleBase *)OpenLibrary("locale.library",38))
        {
          struct Locale *Locale = OpenLocale(NULL);

          GMT_Offset = 60 * Locale->loc_GMTOffset + UTC_OFFSET;
          CloseLocale(Locale);

          //CloseLibrary(LocaleBase);
          //LocaleBase = NULL;
        }

        if(Now)
          GetSysTime(Now);

        TimerBase = NULL;
        CloseDevice((struct IORequest *)TimeRequest);
      }

      DeleteIORequest(TimeRequest);
      TimeRequest = NULL;
    }

    DeleteMsgPort(TimePort);
    TimePort = NULL;
  }
}

STATIC VOID
AddString(STRPTR Buffer,LONG MaxLen,STRPTR NewString)
{
  LONG i,Len,BufferLen;
  BOOL HasSpace;

  Len     = strlen(NewString);
  BufferLen = strlen(Buffer);
  HasSpace  = FALSE;

  for(i = 0 ; i < Len ; i++)
  {
    if(NewString[i] == ' ')
    {
      HasSpace = TRUE;
      Len += 2;
      break;
    }
  }

  Len++;

  if(BufferLen + Len < MaxLen)
  {
    if(HasSpace)
      sprintf(&Buffer[BufferLen],"\"%s\" ",NewString);
    else
      sprintf(&Buffer[BufferLen],"%s ",NewString);
  }
}

  /* GetFiles(char *Buffer,int MaxLen):
   *
   *  Get a list of file names.
   */

char *
GetFiles(char *Buffer,int MaxLen)
{
  if(AslRequestTags(FileRequester,
    ASLFR_Window,   LocalWindow,
    ASLFR_SleepWindow,  TRUE,
  TAG_DONE))
  {
    if(FileRequester->fr_NumArgs > 0)
    {
      UBYTE LocalBuffer[MAX_FILENAME_LENGTH];
      LONG  Len,Count,i;

      for(i = Count = 0 ; i < FileRequester->fr_NumArgs ; i++)
      {
        if(FileRequester->fr_ArgList[i].wa_Lock)
        {
          if(!NameFromLock(FileRequester->fr_ArgList[i].wa_Lock,LocalBuffer,sizeof(LocalBuffer)))
          {
            Count = -1;
            break;
          }
        }
        else
        {
          Len = strlen(FileRequester->fr_Drawer);

          if(Len >= sizeof(LocalBuffer))
          {
            Count = -1;
            break;
          }
          else
            strcpy(LocalBuffer,FileRequester->fr_Drawer);
        }

        if(AddPart(LocalBuffer,FileRequester->fr_ArgList[i].wa_Name,sizeof(LocalBuffer)))
        {
          LONG j;

          Len = strlen(LocalBuffer) + 1;

          for(j = 0 ; j < Len ; j++)
          {
            if(j == ' ')
            {
              Len += 2;
              break;
            }
          }

          if(Len < sizeof(LocalBuffer))
          {
            if(Count + Len < MaxLen)
              Count += Len;
            else
            {
              Count = -1;
              break;
            }
          }
        }
      }

      if(Count > 0)
      {
        Buffer[0] = 0;

        for(i = 0 ; i < FileRequester->fr_NumArgs ; i++)
        {
          if(FileRequester->fr_ArgList[i].wa_Lock)
          {
            if(!NameFromLock(FileRequester->fr_ArgList[i].wa_Lock,LocalBuffer,sizeof(LocalBuffer)))
              break;
          }
          else
          {
            Len = strlen(FileRequester->fr_Drawer);

            if(Len >= sizeof(LocalBuffer))
              break;
            else
              strcpy(LocalBuffer,FileRequester->fr_Drawer);
          }

          if(AddPart(LocalBuffer,FileRequester->fr_ArgList[i].wa_Name,sizeof(LocalBuffer)))
            AddString(Buffer,MaxLen,LocalBuffer);
        }

        return(Buffer);
      }
    }
    else
    {
      if(FileRequester->fr_File[0])
      {
        LONG Len;

        Len = strlen(FileRequester->fr_Drawer) + strlen(FileRequester->fr_File) + 2 + 2;

        if(Len <= MaxLen)
        {
          strcpy(Buffer,FileRequester->fr_Drawer);
          AddPart(Buffer,FileRequester->fr_File,MaxLen);

          return(Buffer);
        }
      }
    }
  }

  return(NULL);
}

  /* OpenConsole():
   *
   *  Open a console window.
   */

STATIC BOOL
OpenConsole(struct Screen *Screen,LONG Top,LONG Height,STRPTR Title,BOOL Resize,struct Window **WindowPtr,struct IOStdReq **ConsolePtr)
{
  LONG LeftEdge=Screen->Width/2 - 320,Width=640;

  struct Window *Window;


  if (LeftEdge<=0) LeftEdge=0;

  if (N_ND->ConOK)
  {
    LeftEdge=N_ND->ConWin->LeftEdge;
    Width=N_ND->ConWin->Width;
  }

  if(Window = OpenWindowTags(NULL,
    WA_Left,      LeftEdge,
    WA_Top,       Top,
    WA_Width,     Width,
    WA_Height,      Height,
    WA_Title,     Title,
    WA_SimpleRefresh, TRUE,
    WA_DepthGadget,   TRUE,
    WA_DragBar,     TRUE,
    WA_SizeGadget,    Resize,
    WA_SizeBRight,    TRUE,
    WA_CustomScreen,  Screen,
    WA_NewLookMenus,  TRUE,
  TAG_DONE))
  {
    Window->UserPort = WindowPort;

    SetMenuStrip(Window,Menu);

    if(ModifyIDCMP(Window,IDCMP_MENUPICK))
    {
      struct IOStdReq *ConsoleRequest;

      if(Window->RPort->Font->tf_Flags & FPF_PROPORTIONAL)
        SetFont(Window->RPort,GfxBase->DefaultFont);

      if(ConsoleRequest = (struct IOStdReq *)CreateIORequest(ConsoleWritePort,sizeof(struct IOStdReq)))
      {
        ConsoleRequest->io_Data = Window;

        if(!OpenDevice("console.device",CONU_CHARMAP,(struct IORequest *)ConsoleRequest,CONFLAG_DEFAULT))
        {
          WindowLimits(Window,Window->BorderLeft + 10 * Window->RPort->Font->tf_XSize * 10 + Window->BorderRight,Window->BorderTop + 2 * Window->RPort->Font->tf_YSize + Window->BorderBottom,Screen->Width,Screen->Height);

            /* Turn off the cursor. */

          ConPrintf(ConsoleRequest,ANSI_RESET); // reset the console and set the color to white

          *WindowPtr  = Window;
          *ConsolePtr = ConsoleRequest;

          return(TRUE);
        }

        DeleteIORequest(ConsoleRequest);
      }
    }

    ClearMenuStrip(Window);

    CloseWindowSafely(Window);
  }

  return(FALSE);
}

  /* CloseConsole():
   *
   *  Close a console window.
   */

STATIC VOID
CloseConsole(struct Window **WindowPtr,struct IOStdReq **ConsolePtr)
{
  if(*ConsolePtr)
  {
    CloseDevice((struct IORequest *)*ConsolePtr);

    DeleteIORequest(*ConsolePtr);

    *ConsolePtr = NULL;
  }

  if(*WindowPtr)
  {
    ClearMenuStrip(*WindowPtr);

    CloseWindowSafely(*WindowPtr);

    *WindowPtr = NULL;
  }
}

STATIC struct SerialBuffer * __regargs
CloneSerialBuffer(struct SerialBuffer *Source,struct MsgPort *MsgPort)
{
  struct SerialBuffer *Buffer;

  if(Buffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + Source -> SerialSize,MEMF_ANY | MEMF_PUBLIC))
  {
    Buffer -> SerialBuffer  = Buffer -> SerialIndex = (UBYTE *)(Buffer + 1);
    Buffer -> SerialFilled  = 0;
    Buffer -> SerialTop = Buffer -> SerialBuffer + Source -> SerialSize;
    Buffer -> SerialSize  = Source -> SerialSize;
    Buffer -> IsClone = TRUE;
    Buffer -> IsBusy  = FALSE;

    if(Buffer -> SerialRequest = (struct IOExtSer *)AllocVec(sizeof(struct IOExtSer),MEMF_ANY | MEMF_PUBLIC))
    {
      CopyMem(Source -> SerialRequest,Buffer -> SerialRequest,sizeof(struct IOExtSer));

      Buffer -> OldPort = Buffer -> SerialRequest -> IOSer . io_Message . mn_ReplyPort;
      Buffer -> SerialRequest -> IOSer . io_Message . mn_ReplyPort = MsgPort;

      return(Buffer);
    }
    else
      cprint("Could not create serial request\n");

    FreeVec(Buffer);
  }
  else
    cprint("Could not create serial buffer\n");

  return(NULL);
}

  /* DeleteSerialBuffer():
   *
   *  Delete a SerialBuffer structure.
   */

STATIC VOID __regargs
DeleteSerialBuffer(struct SerialBuffer *Buffer)
{
  if(Buffer)
  {
    if(Buffer -> IsBusy)
    {
      if(!CheckIO((struct IORequest *)Buffer -> SerialRequest))
        AbortIO((struct IORequest *)Buffer -> SerialRequest);

      WaitIO((struct IORequest *)Buffer -> SerialRequest);
    }

    if(Buffer -> IsClone)
      FreeVec(Buffer -> SerialRequest);
/*
    else
    {
      CloseDevice(Buffer -> SerialRequest);

      DeleteIORequest((struct IORequest *)Buffer -> SerialRequest);
    }
*/

    Buffer->SerialRequest->IOSer.io_Message.mn_ReplyPort=Buffer->OldPort;

    FreeVec(Buffer);
  }
}

  /* CreateSerialBuffer():
   *
   *  Create a serial buffer structure.
   */

STATIC struct SerialBuffer * __regargs
CreateSerialBuffer(STRPTR Device,LONG Unit,LONG Size,struct MsgPort *MsgPort)
{
  struct SerialBuffer *Buffer;

  if(Buffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + Size,MEMF_ANY | MEMF_PUBLIC))
  {
    Buffer -> SerialBuffer  = Buffer -> SerialIndex = (UBYTE *)(Buffer + 1);
    Buffer -> SerialFilled  = 0;
    Buffer -> SerialTop = Buffer -> SerialBuffer + Size;
    Buffer -> SerialSize  = Size;
    Buffer -> IsClone = FALSE;
    Buffer -> IsBusy  = FALSE;

/*
    if(Buffer -> SerialRequest = (struct IOExtSer *)CreateIORequest(MsgPort,sizeof(struct IOExtSer)))
    {
      if(!OpenDevice(Device,Unit,Buffer -> SerialRequest,NULL))
        return(Buffer);
      else
      {
        cprint("Could not open \"%s\", unit %d\n",Device,Unit);

        DeleteIORequest(Buffer -> SerialRequest);
      }
    }
    else
      cprint("Could not create serial request\n");
    FreeVec(Buffer);

*/
    Buffer->SerialRequest = N_ND->SerWrite;
    Buffer -> OldPort = Buffer -> SerialRequest -> IOSer . io_Message . mn_ReplyPort;
    Buffer->SerialRequest->IOSer.io_Message.mn_ReplyPort=MsgPort;
    return(Buffer);

  }
  else
    cprint("Could not create serial buffer\n");

  return(NULL);
}

/*

STATIC struct SerialBuffer *
CloneSerialBuffer(struct SerialBuffer *Source,struct MsgPort *MsgPort)
{
  struct SerialBuffer *Buffer;

  if(Buffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + Source->SerialSize,MEMF_ANY | MEMF_PUBLIC))
  {
    Buffer->SerialBuffer  = Buffer->SerialIndex = (UBYTE *)(Buffer + 1);
    Buffer->SerialFilled  = 0;
    Buffer->SerialTop   = Buffer->SerialBuffer + Source->SerialSize;
    Buffer->SerialSize    = Source->SerialSize;
    Buffer->IsClone     = TRUE;
    Buffer->IsBusy      = FALSE;

    if(Buffer->SerialRequest = (struct IOExtSer *)AllocVec(sizeof(struct IOExtSer),MEMF_ANY | MEMF_PUBLIC))
    {
      CopyMem(Source->SerialRequest,Buffer->SerialRequest,sizeof(struct IOExtSer));

      Buffer->SerialRequest->IOSer.io_Message.mn_ReplyPort = MsgPort;

      return(Buffer);
    }
    else
      cprint("Could not create serial request\n");

    FreeVec(Buffer);
  }
  else
    cprint("Could not create serial buffer\n");

  return(NULL);
}

STATIC VOID
DeleteSerialBuffer(struct SerialBuffer *Buffer)
{
  if(Buffer)
  {
    if(Buffer->IsBusy)
    {
      if(!CheckIO((struct IORequest *)Buffer->SerialRequest))
        AbortIO((struct IORequest *)Buffer->SerialRequest);

      WaitIO((struct IORequest *)Buffer->SerialRequest);
    }

    if(Buffer->IsClone)
      FreeVec(Buffer->SerialRequest);
    else
    {
      CloseDevice((struct IORequest *)Buffer->SerialRequest);

      DeleteIORequest(Buffer->SerialRequest);
    }

    FreeVec(Buffer);
  }
}


STATIC struct SerialBuffer *
CreateSerialBuffer(STRPTR Device,LONG Unit,LONG Size,struct MsgPort *MsgPort)
{
  struct SerialBuffer *Buffer;

  if(Buffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + Size,MEMF_ANY | MEMF_PUBLIC))
  {
    Buffer->SerialBuffer  = Buffer->SerialIndex = (UBYTE *)(Buffer + 1);
    Buffer->SerialFilled  = 0;
    Buffer->SerialTop   = Buffer->SerialBuffer + Size;
    Buffer->SerialSize    = Size;
    Buffer->IsClone     = FALSE;
    Buffer->IsBusy      = FALSE;

    if(Buffer->SerialRequest = (struct IOExtSer *)CreateIORequest(MsgPort,sizeof(struct IOExtSer)))
    {
      Buffer->SerialRequest->io_SerFlags = SERF_SHARED|SERF_7WIRE;

      if(!OpenDevice(Device,Unit,(struct IORequest *)Buffer->SerialRequest,NULL))
        return(Buffer);
      else
      {
        cprint("Could not open \"%s\", unit %d\n",Device,Unit);

        DeleteIORequest(Buffer->SerialRequest);
      }
    }
    else
      cprint("Could not create serial request\n");

    FreeVec(Buffer);
  }
  else
    cprint("Could not create serial buffer\n");

  return(NULL);
}

*/
  /* OpenAll():
   *
   *  Allocate all the resources required.
   */

STATIC BOOL
OpenAll(STRPTR Device,LONG Unit)
{
  LONG Top,Lines,BorderSize,FontSize,ExtraLines,RemainingLines,TotalHeight;

  LONG Height;

  pri=1000;

  ThisProcess = (struct Process *)FindTask(NULL);

  if(pri != 1000)
  {
    if(pri < -128)
      pri = 128;
    else
    {
      if(pri > 127)
        pri = 127;
    }

    OldPri = SetTaskPri((struct Task *)ThisProcess,pri);
  }

/*
  if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)))
  {
    cprint("Could not open intuition.library v37\n");

    return(FALSE);
  }

  if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37)))
  {
    cprint("Could not open graphics.library v37\n");

    return(FALSE);
  }

  if(!(GadToolsBase = OpenLibrary("gadtools.library",37)))
  {
    cprint("Could not open gadtools.library v37\n");

    return(FALSE);
  }
*/

  if(!(UtilityBase = OpenLibrary("utility.library",37)))
  {
    cprint("Could not open utility.library v37\n");

    return(FALSE);
  }

  if(!(AslBase = OpenLibrary("asl.library",37)))
  {
    cprint("Could not open asl.library v37\n");

    return(FALSE);
  }

  if(!(FileRequester = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
    ASLFR_TitleText,    "Select file(s) to upload",
    ASLFR_InitialPattern, "#?",
    ASLFR_Flags1,     FRF_PRIVATEIDCMP | FRF_DOMULTISELECT | FRF_DOPATTERNS,
  TAG_DONE)))
  {
    cprint("Could not allocate file requester.\n");

    return(FALSE);
  }

  if(LocaleBase)// = (struct LocaleBase *)OpenLibrary("locale.library",38))
  {
    struct Locale *Locale;

    if(Locale = OpenLocale(NULL))
    {
      GMT_Offset = 60 * Locale->loc_GMTOffset + UTC_OFFSET;

      CloseLocale(Locale);
    }

//    CloseLibrary(LocaleBase);
  }

  if(!(TimePort = CreateMsgPort()))
  {
    cprint("Could not create timer port\n");

    return(FALSE);
  }

  if(!(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest))))
  {
    cprint("Could not create timer request\n");

    return(FALSE);
  }

  if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimeRequest,NULL))
  {
    cprint("Could not open timer\n");

    return(FALSE);
  }

  TimerBase = (struct Library *)TimeRequest->tr_node.io_Device;

  if(!(Anchor = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath) + 512,MEMF_ANY | MEMF_CLEAR)))
  {
    cprint("Could not allocate pattern matching buffe\n");

    return(FALSE);
  }

  Anchor->ap_Strlen = 512;

  if(!(ReadPort = CreateMsgPort()))
  {
    cprint("Could not create serial read port\n");

    return(FALSE);
  }

  if(!(WritePort = CreateMsgPort()))
  {
    cprint("Could not create serial write port\n");

    return(FALSE);
  }

  if(!(ConsoleReadPort = CreateMsgPort()))
  {
    cprint("Could not create console read port\n");

    return(FALSE);
  }

  if(!(ConsoleWritePort = CreateMsgPort()))
  {
    cprint("Could not create console write port\n");

    return(FALSE);
  }

  if(!(ConsoleReadRequest = (struct IOStdReq *)AllocVec(sizeof(struct IOStdReq),MEMF_ANY | MEMF_PUBLIC | MEMF_CLEAR)))
  {
    cprint("Could not create console read request\n");

    return(FALSE);
  }


  if(!(ReadBuffer = CreateSerialBuffer(Device,Unit,BUFFER_SIZE,ReadPort)))
    return(FALSE);

  if(!(ThisBuffer = CloneSerialBuffer(ReadBuffer,WritePort)))
    return(FALSE);

  if(!(NextBuffer = CloneSerialBuffer(ReadBuffer,WritePort)))
    return(FALSE);

  // use the watch window if it's open, otherwise use the same screen that control is on...

  if (N_ND->ConOK && N_ND->NodeSettings.UseOwnScreen)
  {
    Screen=LockPubScreen(N_ND->ConScreenName);
  }
  else
  {
    Screen=LockPubScreen(BBSGlobal->ScreenInfo->PubScreenName);
  }

  if(!Screen)
  {
    cprint("Could not lock public screen\n");

    return(FALSE);
  }

  if(!(VisualInfo = GetVisualInfo(Screen,TAG_DONE)))
  {
    cprint("Could not obtain screen visual info\n");

    return(FALSE);
  }

  if(!(Menu = CreateMenus(MenuTemplate,TAG_DONE)))
  {
    cprint("Could not create menus\n");

    return(FALSE);
  }

  if(!LayoutMenus(Menu,VisualInfo,
    GTMN_TextAttr,    Screen->Font,
    GTMN_NewLookMenus,  TRUE,
  TAG_DONE))
  {
    cprint("Could not layout menus\n");

    return(FALSE);
  }

  Top       = Screen->BarHeight + 1;
  BorderSize    = Screen->WBorTop + Screen->Font->ta_YSize + 1 + Screen->WBorBottom;
  FontSize    = GfxBase->DefaultFont->tf_YSize;
  TotalHeight   = Screen->Height - Top;
  Lines     = (TotalHeight - 3 - 4 * BorderSize) / FontSize;
  ExtraLines    = Lines > MIN_LINES ? (Lines - MIN_LINES) / 3 : 0;
  RemainingLines  = Lines > MIN_LINES + ExtraLines * 3 ? Lines - (MIN_LINES + ExtraLines * 3) : 0;

  if(Lines < MIN_LINES)
  {
    cprint("Screen size too small (need at least %d text lines, can get only %d)\n",MIN_LINES,Lines);

    return(FALSE);
  }

  if(!(WindowPort = CreateMsgPort()))
  {
    cprint("Could not create window port\n");

    return(FALSE);
  }

  Height=BorderSize + 10 * FontSize;

  if (!N_ND->NodeSettings.UseOwnScreen)
  {
    if (N_ND->ConOK)
    {
      Top=N_ND->ConWin->TopEdge;

      if (N_ND->ConWin->Height / 3 > Height) Height=N_ND->ConWin->Height / 3;
    }
  }


  if(!OpenConsole(Screen,Top,Height,"Remote",TRUE,&RemoteWindow,&RemoteRequest))
  {
    cprint("Could not open console window #3\n");

    return(FALSE);
  }

  Top     += RemoteWindow->Height + 1;

  if(!OpenConsole(Screen,Top,Height,"Local (Press [Amiga+C] to start/end chat mode, [Esc] to abort Hydra session)",TRUE,&LocalWindow,&LocalRequest))
  {
    cprint("Could not open console window #4\n");

    return(FALSE);
  }

  CopyMem(LocalRequest,ConsoleReadRequest,sizeof(struct IOStdReq));

  ConsoleReadRequest->io_Message.mn_ReplyPort = ConsoleReadPort;

    /* Turn the cursors back on. */

  ConPrintf(LocalRequest,"\33[ p");
  ConPrintf(RemoteRequest,"\33[ p");

  ConsoleReadRequest->io_Command  = CMD_READ;
  ConsoleReadRequest->io_Data   = &ConsoleChar;
  ConsoleReadRequest->io_Length = 1;

  ClrSignal(SIG_CONREAD);
  SendIO((struct IORequest *)ConsoleReadRequest);

  OldPtr = ThisProcess->pr_WindowPtr;

  ThisProcess->pr_WindowPtr = (APTR)LocalWindow;

  if(!quiet)
  {
    ScreenToFront(Screen);

    ActivateWindow(LocalWindow);
  }

  com_flow(flowflags);

  return(TRUE);
}

  /* CloseAll():
   *
   *  Close all the resources.
   */

STATIC VOID
CloseAll(VOID)
{
  if (AlreadyClosed) return;

  if(FileRequester)
    FreeAslRequest(FileRequester);

  if(LocalWindow)
    ClearMenuStrip(LocalWindow);

  if(Menu)
    FreeMenus(Menu);

  if(VisualInfo)
    FreeVisualInfo(VisualInfo);

  if(AnchorUsed)
    MatchEnd(Anchor);

  if(Anchor)
    FreeVec(Anchor);

  if(ThisProcess)
    ThisProcess->pr_WindowPtr = OldPtr;

  if(ConsoleReadRequest)
  {
    if(ConsoleReadRequest->io_Device)
    {
      if(!CheckIO((struct IORequest *)ConsoleReadRequest))
        AbortIO((struct IORequest *)ConsoleReadRequest);

      WaitIO((struct IORequest *)ConsoleReadRequest);
    }

    FreeVec(ConsoleReadRequest);
  }

  CloseConsole(&LocalWindow,&LocalRequest);
  CloseConsole(&RemoteWindow,&RemoteRequest);

  if(WindowPort)
    DeleteMsgPort(WindowPort);

//  if(Screen)
//    CloseScreen(Screen);

  if(Screen)
    UnlockPubScreen(NULL,Screen);

  DeleteSerialBuffer(NextBuffer);
  DeleteSerialBuffer(ThisBuffer);

  DeleteSerialBuffer(ReadBuffer);

  if(ConsoleWritePort)
    DeleteMsgPort(ConsoleWritePort);

  if(ConsoleReadPort)
    DeleteMsgPort(ConsoleReadPort);

  if(WritePort)
    DeleteMsgPort(WritePort);

  if(ReadPort)
    DeleteMsgPort(ReadPort);

  if(TimeRequest)
  {
    if(TimeRequest->tr_node.io_Device)
      CloseDevice((struct IORequest *)TimeRequest);

    DeleteIORequest(TimeRequest);
  }

  if(TimePort)
    DeleteMsgPort(TimePort);

  if(UtilityBase)
    CloseLibrary(UtilityBase);

  if(AslBase)
    CloseLibrary(AslBase);

  if(GadToolsBase)
    CloseLibrary(GadToolsBase);

  if(GfxBase)
    CloseLibrary((struct Library *)GfxBase);

  if(IntuitionBase)
    CloseLibrary((struct Library *)IntuitionBase);

  if(pri != 1000)
    OldPri = SetTaskPri((struct Task*)ThisProcess,OldPri);

  AlreadyClosed=TRUE;
}

  /* ConPutc():
   *
   *  Output a single character.
   */

VOID __stdargs
ConPutc(struct IOStdReq *Request,UBYTE Char)
{
  Request->io_Command = CMD_WRITE;
  Request->io_Data  = &Char;
  Request->io_Length  = 1;

  DoIO((struct IORequest *)Request);
}

  /* ConPuts():
   *
   *  Output a string.
   */

VOID
ConPuts(struct IOStdReq *Request,STRPTR String)
{
  Request->io_Command = CMD_WRITE;
  Request->io_Data  = String;
  Request->io_Length  = strlen(String);

  DoIO((struct IORequest *)Request);
}

  /* ConPrintf():
   *
   *  Formatted console output.
   */

VOID __stdargs
ConPrintf(struct IOStdReq *Request,STRPTR Format,...)
{
  STATIC UBYTE Buffer[512];

  va_list VarArgs;
  LONG  Len;

  va_start(VarArgs,Format);
  vsprintf(Buffer,Format,VarArgs);
  va_end(VarArgs);

  Len = strlen(Buffer);

  Request->io_Command = CMD_WRITE;
  Request->io_Data  = Buffer;
  Request->io_Length  = Len;

  DoIO((struct IORequest *)Request);
}

  /* ConMove():
   *
   *  Move the cursor to a new position.
   */

VOID
ConMove(struct IOStdReq *Request,LONG x,LONG y)
{
  ConPrintf(Request,"\33[%ld;%ldH",y,x);
}

  /* ConClear():
   *
   *  Clear the console window.
   */

VOID
ConClear(struct IOStdReq *Request)
{
  struct ConUnit *ConUnit = (struct ConUnit *)Request->io_Device;
  LONG x,y;

  x = ConUnit->cu_XCCP;
  y = ConUnit->cu_YCCP;

  ConPrintf(Request,"\033[2J\33[%ld;%ldH",y,x);
}

  /* ConGetKey():
   *
   *  Read a character from a console window.
   */

int
ConGetKey()
{
  if(ConsoleReady)
  {
    int Result = ConsoleChar;

    ConsoleReady = FALSE;

    ConsoleReadRequest->io_Command  = CMD_READ;
    ConsoleReadRequest->io_Data   = &ConsoleChar;
    ConsoleReadRequest->io_Length = 1;

    ClrSignal(SIG_CONREAD);
    SendIO((struct IORequest *)ConsoleReadRequest);

    return(Result);
  }
  else
  {
    int Result = 0;

    if(WindowReady)
    {
      struct IntuiMessage *IntuiMessage;
      ULONG MsgClass;
      UWORD MsgCode;

      while(IntuiMessage = (struct IntuiMessage *)GetMsg(LocalWindow->UserPort))
      {
        MsgClass  = IntuiMessage->Class;
        MsgCode   = IntuiMessage->Code;

        ReplyMsg((struct Message *)IntuiMessage);

        if(MsgClass == IDCMP_MENUPICK)
        {
          struct MenuItem *Item;

          while(MsgCode != MENUNULL)
          {
            if(Item = ItemAddress(Menu,MsgCode))
            {
              if(MENU_USERDATA(Item))
              {
                if(!Result)
                  Result = (int)MENU_USERDATA(Item);
              }

              MsgCode = Item->NextSelect;
            }
            else
              break;
          }
        }
      }

      WindowReady = FALSE;
    }

    return(Result);
  }
}

  /* ConScanKey():
   *
   *  Check for a keyboard event.
   */

int
ConScanKey()
{
  if(ConsoleReady || WindowReady)
    return(1);
  else
  {
    int Result = 0;

    if(CheckSignal(SIG_WINDOW))
    {
      WindowReady = TRUE;

      Result = 1;
    }

    if(CheckIO((struct IORequest *)ConsoleReadRequest))
    {
      if(!WaitIO((struct IORequest *)ConsoleReadRequest))
      {
        ConsoleReady = TRUE;

        return(1);
      }
      else
      {
        ConsoleReadRequest->io_Command  = CMD_READ;
        ConsoleReadRequest->io_Data   = &ConsoleChar;
        ConsoleReadRequest->io_Length = 1;

        ClrSignal(SIG_CONREAD);
        SendIO((struct IORequest *)ConsoleReadRequest);
      }
    }

    return(Result);
  }

  return(0);
}

  /* dtr_out(byte flag):
   *
   *  If flag == 0->drop DTR signal, else set it.
   */

VOID
dtr_out(byte flag)
{
  if(!flag)
  {
    if(ThisBuffer->IsBusy)
    {
      WaitIO((struct IORequest *)ThisBuffer->SerialRequest);

      ThisBuffer->IsBusy    = FALSE;
      ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;
    }

    if(ReadBuffer->IsBusy)
    {
      if(!CheckIO((struct IORequest *)ReadBuffer->SerialRequest))
        AbortIO((struct IORequest *)ReadBuffer->SerialRequest);

      WaitIO((struct IORequest *)ReadBuffer->SerialRequest);

      ReadBuffer->IsBusy      = FALSE;
      ReadBuffer->SerialFilled  = ReadBuffer->SerialRequest->IOSer.io_Actual;
      ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;

      ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

      DoIO((struct IORequest *)ReadBuffer->SerialRequest);

      if(ReadBuffer->SerialRequest->IOSer.io_Actual)
      {
        LONG Size = ReadBuffer->SerialSize - ReadBuffer->SerialFilled;

        if(Size > 0)
        {
          if(Size > ReadBuffer->SerialRequest->IOSer.io_Actual)
            Size = ReadBuffer->SerialRequest->IOSer.io_Actual;

          ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
          ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer + ReadBuffer->SerialFilled;
          ReadBuffer->SerialRequest->IOSer.io_Length  = Size;

          DoIO((struct IORequest *)ReadBuffer->SerialRequest);

          ReadBuffer->SerialFilled += ReadBuffer->SerialRequest->IOSer.io_Actual;
        }
      }
    }

    CloseDevice((struct IORequest *)ReadBuffer->SerialRequest);

    TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
    TimeRequest->tr_time.tv_secs  = 1;
    TimeRequest->tr_time.tv_micro = 0;

    DoIO((struct IORequest *)TimeRequest);

    if(OpenDevice(device,port,(struct IORequest *)ReadBuffer->SerialRequest,NULL))
    {
      endprog(10);
    }
    else
    {
      ReadBuffer->SerialRequest->io_Baud    = ThisBuffer->SerialRequest->io_Baud;
      ReadBuffer->SerialRequest->io_ReadLen = ThisBuffer->SerialRequest->io_ReadLen;
      ReadBuffer->SerialRequest->io_WriteLen  = ThisBuffer->SerialRequest->io_WriteLen;
      ReadBuffer->SerialRequest->io_SerFlags  = ThisBuffer->SerialRequest->io_SerFlags;

      ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_SETPARAMS;

      DoIO((struct IORequest *)ReadBuffer->SerialRequest);

      CopyMem(ReadBuffer->SerialRequest,ThisBuffer->SerialRequest,sizeof(struct IOExtSer));

      ThisBuffer->SerialRequest->IOSer.io_Message.mn_ReplyPort = WritePort;

      CopyMem(ReadBuffer->SerialRequest,NextBuffer->SerialRequest,sizeof(struct IOExtSer));

      NextBuffer->SerialRequest->IOSer.io_Message.mn_ReplyPort = WritePort;
    }
  }
}

  /* com_flow(byte flags):
   *
   *  The bit mask `flags' determines the style(s) of
   *  handshaking:
   *
   *  if (flags & 9)->enable xON/xOFF software handshaking,
   *  if (flags & 2)->enable RTS/CTS hardware handshaking
   */

VOID
com_flow(byte flags)
{

  if(ThisBuffer->IsBusy)
  {
    WaitIO((struct IORequest *)ThisBuffer->SerialRequest);

    ThisBuffer->IsBusy    = FALSE;
    ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;
  }

  if(ReadBuffer->IsBusy)
  {
    if(!CheckIO((struct IORequest *)ReadBuffer->SerialRequest))
      AbortIO((struct IORequest *)ReadBuffer->SerialRequest);

    WaitIO((struct IORequest *)ReadBuffer->SerialRequest);

    ReadBuffer->IsBusy      = FALSE;
    ReadBuffer->SerialFilled  = ReadBuffer->SerialRequest->IOSer.io_Actual;
    ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;

    ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

    DoIO((struct IORequest *)ReadBuffer->SerialRequest);

    if(ReadBuffer->SerialRequest->IOSer.io_Actual)
    {
      LONG Size = ReadBuffer->SerialSize - ReadBuffer->SerialFilled;

      if(Size > 0)
      {
        if(Size > ReadBuffer->SerialRequest->IOSer.io_Actual)
          Size = ReadBuffer->SerialRequest->IOSer.io_Actual;

        ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
        ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer + ReadBuffer->SerialFilled;
        ReadBuffer->SerialRequest->IOSer.io_Length  = Size;

        DoIO((struct IORequest *)ReadBuffer->SerialRequest);

        ReadBuffer->SerialFilled += ReadBuffer->SerialRequest->IOSer.io_Actual;
      }
    }
  }

  if(flags & 2)
    ReadBuffer->SerialRequest->io_SerFlags |= SERF_7WIRE;
  else
    ReadBuffer->SerialRequest->io_SerFlags &= ~SERF_7WIRE;

  if(flags & 9)
    ReadBuffer->SerialRequest->io_SerFlags &= ~SERF_XDISABLED;
  else
    ReadBuffer->SerialRequest->io_SerFlags |= SERF_XDISABLED;

  ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_SETPARAMS;

  DoIO((struct IORequest *)ReadBuffer->SerialRequest);

  ThisBuffer->SerialRequest->io_SerFlags = ReadBuffer->SerialRequest->io_SerFlags;
  NextBuffer->SerialRequest->io_SerFlags = ReadBuffer->SerialRequest->io_SerFlags;

}

  /* com_setspeed(word speed):
   *
   *  Set the transfer speed (in bits/second).
   */

VOID
com_setspeed(word speed)
{
/*
  if(ThisBuffer->IsBusy)
  {
    WaitIO((struct IORequest *)ThisBuffer->SerialRequest);

    ThisBuffer->IsBusy    = FALSE;
    ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;
  }

  if(ReadBuffer->IsBusy)
  {
    if(!CheckIO(ReadBuffer->SerialRequest))
      AbortIO((struct IORequest *)ReadBuffer->SerialRequest);

    WaitIO((struct IORequest *)ReadBuffer->SerialRequest);

    ReadBuffer->IsBusy      = FALSE;
    ReadBuffer->SerialFilled  = ReadBuffer->SerialRequest->IOSer.io_Actual;
    ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;

    ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

    DoIO((struct IORequest *)ReadBuffer->SerialRequest);

    if(ReadBuffer->SerialRequest->IOSer.io_Actual)
    {
      LONG Size = ReadBuffer->SerialSize - ReadBuffer->SerialFilled;

      if(Size > 0)
      {
        if(Size > ReadBuffer->SerialRequest->IOSer.io_Actual)
          Size = ReadBuffer->SerialRequest->IOSer.io_Actual;

        ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
        ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer + ReadBuffer->SerialFilled;
        ReadBuffer->SerialRequest->IOSer.io_Length  = Size;

        DoIO((struct IORequest *)ReadBuffer->SerialRequest);

        ReadBuffer->SerialFilled += ReadBuffer->SerialRequest->IOSer.io_Actual;
      }
    }
  }

  ReadBuffer->SerialRequest->io_Baud = speed;

  if(parity)
  {
    ReadBuffer->SerialRequest->io_ReadLen = ReadBuffer->SerialRequest->io_WriteLen = 7;
    ReadBuffer->SerialRequest->io_SerFlags |= SERF_PARTY_ON;
  }
  else
  {
    ReadBuffer->SerialRequest->io_ReadLen = ReadBuffer->SerialRequest->io_WriteLen = 8;
    ReadBuffer->SerialRequest->io_SerFlags &= ~SERF_PARTY_ON;
  }

  ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_SETPARAMS;

  DoIO((struct IORequest *)ReadBuffer->SerialRequest);

  ThisBuffer->SerialRequest->io_Baud    = ReadBuffer->SerialRequest->io_Baud;
  ThisBuffer->SerialRequest->io_ReadLen = ReadBuffer->SerialRequest->io_ReadLen;
  ThisBuffer->SerialRequest->io_WriteLen  = ReadBuffer->SerialRequest->io_WriteLen;
  ThisBuffer->SerialRequest->io_SerFlags  = ReadBuffer->SerialRequest->io_SerFlags;

  NextBuffer->SerialRequest->io_Baud    = ReadBuffer->SerialRequest->io_Baud;
  NextBuffer->SerialRequest->io_ReadLen = ReadBuffer->SerialRequest->io_ReadLen;
  NextBuffer->SerialRequest->io_WriteLen  = ReadBuffer->SerialRequest->io_WriteLen;
  NextBuffer->SerialRequest->io_SerFlags  = ReadBuffer->SerialRequest->io_SerFlags;
*/
}

  /* com_putblock(byte *s,word len):
   *
   *  Send a data block asynchronously.
   */

VOID
com_putblock(byte *s,word len)
{
  struct SerialBuffer *Swap = ThisBuffer;

  if(ThisBuffer->IsBusy)
    WaitIO((struct IORequest *)ThisBuffer->SerialRequest);
  else
  {
    if(ThisBuffer->SerialIndex > ThisBuffer->SerialBuffer)
    {
      ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
      ThisBuffer->SerialRequest->IOSer.io_Data  = ThisBuffer->SerialBuffer;
      ThisBuffer->SerialRequest->IOSer.io_Length  = ThisBuffer->SerialIndex - ThisBuffer->SerialBuffer;

      DoIO((struct IORequest *)ThisBuffer->SerialRequest);
    }
  }

  ThisBuffer->IsBusy    = FALSE;
  ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;

  ThisBuffer = NextBuffer;
  NextBuffer = Swap;

  if(ThisBuffer->SerialIndex > ThisBuffer->SerialBuffer)
  {
    ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
    ThisBuffer->SerialRequest->IOSer.io_Data  = ThisBuffer->SerialBuffer;
    ThisBuffer->SerialRequest->IOSer.io_Length  = ThisBuffer->SerialIndex - ThisBuffer->SerialBuffer;

    DoIO((struct IORequest *)ThisBuffer->SerialRequest);
  }

  ThisBuffer->IsBusy    = FALSE;
  ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;

  while(len > 2 * ThisBuffer->SerialSize)
  {
    ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
    ThisBuffer->SerialRequest->IOSer.io_Data  = s;
    ThisBuffer->SerialRequest->IOSer.io_Length  = ThisBuffer->SerialSize;

    s += ThisBuffer->SerialSize;
    len -= ThisBuffer->SerialSize;

    DoIO((struct IORequest *)ThisBuffer->SerialRequest);
  }

  CopyMem(s,ThisBuffer->SerialBuffer,MIN(len,ThisBuffer->SerialSize));

  ThisBuffer->IsBusy              = TRUE;
  ThisBuffer->SerialIndex           = ThisBuffer->SerialBuffer + MIN(len,ThisBuffer->SerialSize);
  ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
  ThisBuffer->SerialRequest->IOSer.io_Data  = ThisBuffer->SerialBuffer;
  ThisBuffer->SerialRequest->IOSer.io_Length  = MIN(len,ThisBuffer->SerialSize);

  len -= ThisBuffer->SerialRequest->IOSer.io_Length;
  s += ThisBuffer->SerialRequest->IOSer.io_Length;

  ClrSignal(SIG_SERWRITE);
  SendIO((struct IORequest *)ThisBuffer->SerialRequest);

  if(len > 0)
  {
    CopyMem(s,NextBuffer->SerialBuffer,len);

    NextBuffer->SerialIndex = NextBuffer->SerialBuffer + len;
  }
}

  /* breakfunc():
   *
   *  Cleanup routine for SAS/C.
   */

static int
breakfunc(void)
{
  CloseAll();

  return(1);
}

  /* sys_init(VOID):
   *
   *  Initialize this driver implementation.
   */

VOID
sys_init(VOID)
{
  if(!OpenAll(device,port))
  {
    CloseAll();

    endprog(2);
  }

  onbreak(breakfunc);
}

  /* sys_reset(VOID):
   *
   *  Perform cleanup for this driver implementation.
   */

VOID
sys_reset(VOID)
{
  CloseAll();
}

  /* sys_idle(VOID):
   *
   *  This routine gets called when the system is idle.
   *  That's a nice one. We are supposed to call the
   *  system scheduler, etc.
   */

VOID
sys_idle(VOID)
{
  ULONG Signals;

  if(ReadBuffer->SerialFilled <= 0 && !ReadBuffer->IsBusy)
  {
    ReadBuffer->IsBusy              = TRUE;
    ReadBuffer->SerialIndex           = ReadBuffer->SerialBuffer;
    ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
    ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer;
    ReadBuffer->SerialRequest->IOSer.io_Length  = 1;

    ClrSignal(SIG_SERREAD);
    SendIO((struct IORequest *)ReadBuffer->SerialRequest);
  }

  TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
  TimeRequest->tr_time.tv_secs  = 1;
  TimeRequest->tr_time.tv_micro = 0;

  ClrSignal(SIG_TIMER);
  SendIO((struct IORequest *)TimeRequest);

  Signals = Wait(SIG_SERREAD | SIG_SERWRITE | SIG_CONREAD | SIG_WINDOW | SIG_TIMER);

  if(!(Signals & SIG_TIMER))
  {
    if(!CheckIO((struct IORequest *)TimeRequest))
      AbortIO((struct IORequest *)TimeRequest);
  }

  WaitIO((struct IORequest *)TimeRequest);

  if(Signals & SIG_SERREAD || CheckIO((struct IORequest *)ReadBuffer->SerialRequest))
  {
    WaitIO((struct IORequest *)ReadBuffer->SerialRequest);

    ReadBuffer->IsBusy      = FALSE;
    ReadBuffer->SerialFilled  = ReadBuffer->SerialRequest->IOSer.io_Actual;
    ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;

    ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

    DoIO((struct IORequest *)ReadBuffer->SerialRequest);

    if(ReadBuffer->SerialRequest->IOSer.io_Actual)
    {
      LONG Size = ReadBuffer->SerialSize - ReadBuffer->SerialFilled;

      if(Size > 0)
      {
        if(Size > ReadBuffer->SerialRequest->IOSer.io_Actual)
          Size = ReadBuffer->SerialRequest->IOSer.io_Actual;

        ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
        ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer + ReadBuffer->SerialFilled;
        ReadBuffer->SerialRequest->IOSer.io_Length  = Size;

        DoIO((struct IORequest *)ReadBuffer->SerialRequest);

        ReadBuffer->SerialFilled += ReadBuffer->SerialRequest->IOSer.io_Actual;
      }
    }
  }

  if(Signals & SIG_SERWRITE)
  {
    struct SerialBuffer *Swap = ThisBuffer;

    WaitIO((struct IORequest *)ThisBuffer->SerialRequest);

    ThisBuffer->IsBusy    = FALSE;
    ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;

    ThisBuffer = NextBuffer;
    NextBuffer = Swap;
  }

  if(Signals & SIG_CONREAD)
  {
    WaitIO((struct IORequest *)ConsoleReadRequest);

    ConsoleReady = TRUE;
  }

  if(Signals & SIG_WINDOW)
    WindowReady = TRUE;
}

  /* com_outfull(VOID):
   *
   *  Return number of bytes still to be transferred.
   */

int
com_outfull(VOID)
{
  return(ThisBuffer->SerialIndex - ThisBuffer->SerialBuffer + NextBuffer->SerialIndex - NextBuffer->SerialBuffer);
}

  /* carrier(VOID):
   *
   *  Return current carrier status.
   */

int
carrier(VOID)
{
  if(nocarrier)
    return(1);
  else
  {
    if(!ThisBuffer->IsBusy)
    {
      ThisBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

      DoIO((struct IORequest *)ThisBuffer->SerialRequest);

      if(ThisBuffer->SerialRequest->io_Status & CIAF_COMCD)
        return(0);
      else
        return(1);
    }
    else
    {
      NextBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

      DoIO((struct IORequest *)NextBuffer->SerialRequest);

      if(NextBuffer->SerialRequest->io_Status & CIAF_COMCD)
        return(0);
      else
        return(1);
    }
  }
}

  /* com_flush(VOID):
   *
   *  Make sure all pending data gets written.
   */

VOID
com_flush(VOID)
{
  if(ThisBuffer->IsBusy)
  {
    WaitIO((struct IORequest *)ThisBuffer->SerialRequest);

    ThisBuffer->IsBusy    = FALSE;
    ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;
  }

  if(NextBuffer->SerialIndex > NextBuffer->SerialBuffer)
  {
    NextBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
    NextBuffer->SerialRequest->IOSer.io_Data  = NextBuffer->SerialBuffer;
    NextBuffer->SerialRequest->IOSer.io_Length  = NextBuffer->SerialIndex - NextBuffer->SerialBuffer;

    DoIO((struct IORequest *)NextBuffer->SerialRequest);

    NextBuffer->SerialIndex = NextBuffer->SerialBuffer;
  }
}

  /* com_putbyte(byte c):
   *
   *  Transmit a single byte, queueing it if necessary.
   */

VOID
com_putbyte(byte c)
{
  if(ThisBuffer->IsBusy)
  {
    if(NextBuffer->SerialIndex + 1 >= NextBuffer->SerialTop)
    {
      struct SerialBuffer *Swap = ThisBuffer;

      WaitIO((struct IORequest *)ThisBuffer->SerialRequest);

      ThisBuffer->IsBusy    = FALSE;
      ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;

      ThisBuffer = NextBuffer;
      NextBuffer = Swap;

      ThisBuffer->IsBusy              = TRUE;
      ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
      ThisBuffer->SerialRequest->IOSer.io_Data  = ThisBuffer->SerialBuffer;
      ThisBuffer->SerialRequest->IOSer.io_Length  = ThisBuffer->SerialIndex - ThisBuffer->SerialBuffer;

      ClrSignal(SIG_SERWRITE);
      SendIO((struct IORequest *)ThisBuffer->SerialRequest);
    }

    *NextBuffer->SerialIndex++ = c;
  }
  else
  {
    if(ThisBuffer->SerialIndex + 1 < ThisBuffer->SerialTop)
    {
      *ThisBuffer->SerialIndex++ = c;

      ThisBuffer->IsBusy              = TRUE;
      ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
      ThisBuffer->SerialRequest->IOSer.io_Data  = ThisBuffer->SerialBuffer;
      ThisBuffer->SerialRequest->IOSer.io_Length  = 1;

      ClrSignal(SIG_SERWRITE);
      SendIO((struct IORequest *)ThisBuffer->SerialRequest);
    }
    else
    {
      ThisBuffer->IsBusy              = TRUE;
      ThisBuffer->SerialRequest->IOSer.io_Command = CMD_WRITE;
      ThisBuffer->SerialRequest->IOSer.io_Data  = ThisBuffer->SerialBuffer;
      ThisBuffer->SerialRequest->IOSer.io_Length  = ThisBuffer->SerialIndex - ThisBuffer->SerialBuffer;

      ClrSignal(SIG_SERWRITE);
      SendIO((struct IORequest *)ThisBuffer->SerialRequest);

      *NextBuffer->SerialIndex++ = c;
    }
  }
}

  /* com_purge(VOID):
   *
   *  Clear the read/write buffers.
   */

VOID
com_purge(VOID)
{
  if(ThisBuffer->IsBusy)
  {
    if(!CheckIO((struct IORequest *)ThisBuffer->SerialRequest))
      AbortIO((struct IORequest *)ThisBuffer->SerialRequest);

    WaitIO((struct IORequest *)ThisBuffer->SerialRequest);
  }

  ThisBuffer->IsBusy    = FALSE;
  ThisBuffer->SerialIndex = ThisBuffer->SerialBuffer;

  NextBuffer->IsBusy    = FALSE;
  NextBuffer->SerialIndex = NextBuffer->SerialBuffer;

  if(ReadBuffer->IsBusy)
  {
    if(!CheckIO((struct IORequest *)ReadBuffer->SerialRequest))
      AbortIO((struct IORequest *)ReadBuffer->SerialRequest);

    WaitIO((struct IORequest *)ReadBuffer->SerialRequest);
  }

  ReadBuffer->IsBusy      = FALSE;
  ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;
  ReadBuffer->SerialFilled  = 0;

  ThisBuffer->SerialRequest->IOSer.io_Command = CMD_CLEAR;
  DoIO((struct IORequest *)ThisBuffer->SerialRequest);
}

  /* com_dump(VOID):
   *
   *  Wait for asynchronous write request to terminate.
   */

VOID
com_dump(VOID)
{
  com_flush();
}

  /* com_getbyte(VOID):
   *
   *  Read a single byte from the serial line. If not available,
   *  return EOF.
   */

int
com_getbyte(VOID)
{
  int Result;

  if(ReadBuffer->SerialFilled <= 0)
  {
    if(ReadBuffer->IsBusy)
    {
      if(!CheckIO((struct IORequest *)ReadBuffer->SerialRequest))
        return(EOF); // returns if no chars waiting
      else
        WaitIO((struct IORequest *)ReadBuffer->SerialRequest);

      ReadBuffer->IsBusy      = FALSE;
      ReadBuffer->SerialFilled  = ReadBuffer->SerialRequest->IOSer.io_Actual;
      ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;

      ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

      DoIO((struct IORequest *)ReadBuffer->SerialRequest);

      if(ReadBuffer->SerialRequest->IOSer.io_Actual)
      {
        LONG Size = ReadBuffer->SerialSize - ReadBuffer->SerialFilled;

        if(Size > 0)
        {
          if(Size > ReadBuffer->SerialRequest->IOSer.io_Actual)
            Size = ReadBuffer->SerialRequest->IOSer.io_Actual;

          ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
          ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer + ReadBuffer->SerialFilled;
          ReadBuffer->SerialRequest->IOSer.io_Length  = Size;

          DoIO((struct IORequest *)ReadBuffer->SerialRequest);

          ReadBuffer->SerialFilled += ReadBuffer->SerialRequest->IOSer.io_Actual;
        }
      }
    }
    else
    {
      ReadBuffer->SerialRequest->IOSer.io_Command = SDCMD_QUERY;

      DoIO((struct IORequest *)ReadBuffer->SerialRequest);

      if(!ReadBuffer->SerialRequest->IOSer.io_Actual)
        return(EOF);
      else
      {
        ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
        ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer;
        ReadBuffer->SerialRequest->IOSer.io_Length  = MIN(ReadBuffer->SerialRequest->IOSer.io_Actual,ReadBuffer->SerialSize);

        DoIO((struct IORequest *)ReadBuffer->SerialRequest);

        if(!ReadBuffer->SerialRequest->IOSer.io_Actual)
          return(EOF);
        else
        {
          ReadBuffer->SerialFilled  = ReadBuffer->SerialRequest->IOSer.io_Actual;
          ReadBuffer->SerialIndex   = ReadBuffer->SerialBuffer;
        }
      }
    }
  }

  Result = *ReadBuffer->SerialIndex++;

  ReadBuffer->SerialFilled--;

  if(ReadBuffer->SerialFilled <= 0)
  {
    ReadBuffer->IsBusy          = TRUE;
    ReadBuffer->SerialIndex       = ReadBuffer->SerialBuffer;

    ReadBuffer->SerialRequest->IOSer.io_Command = CMD_READ;
    ReadBuffer->SerialRequest->IOSer.io_Data  = ReadBuffer->SerialBuffer;
    ReadBuffer->SerialRequest->IOSer.io_Length  = 1;

    ClrSignal(SIG_SERREAD);
    SendIO((struct IORequest *)ReadBuffer->SerialRequest);
  }

  return(Result);
}

  /* setstamp(STRPTR Name,LONG Time):
   *
   *  Set time/date of a file.
   */

VOID
setstamp(char *Name,long Time)
{
  struct DateStamp Date;
  ULONG Seconds;

  DB(kprintf("setstamp |%s| %ld\n",Name,Time));

    /* Translate it into an Amiga date. */

  if(Time > GMT_Offset)
    Seconds = Time - GMT_Offset;
  else
    Seconds = 0;

  Date.ds_Days  = Seconds / (24 * 60 * 60);
  Date.ds_Minute  = (Seconds % (24 * 60 * 60)) / 60;
  Date.ds_Tick  = (Seconds % 60) * TICKS_PER_SECOND;

  SetFileDate(Name,&Date);
}

  /* freespace(STRPTR DrivePath):
   *
   *  Get free disk space for specified drive.
   */

long
freespace(char *DrivePath)
{
  struct DevProc  *DevProc;
  struct DosList  *DosList;
  BOOL       GoodDevice;
  LONG       Size;

  if(!DrivePath)
    DrivePath = "";

  DB(kprintf("freespace |%s|\n",DrivePath));

  DevProc = GetDeviceProc(DrivePath,NULL);

  GoodDevice = FALSE;
  Size = (LONG)((ULONG)~0 >> 2);

  if(DosList = LockDosList(LDF_DEVICES | LDF_READ))
  {
    while(DosList = NextDosEntry(DosList,LDF_DEVICES))
    {
      if(DosList->dol_Task == DevProc->dvp_Port)
      {
        struct FileSysStartupMsg *FSSM = (struct FileSysStartupMsg *)BADDR(DosList->dol_misc.dol_handler.dol_Startup);

        if(TypeOfMem(FSSM))
        {
          struct DosEnvec *DosEnvec = (struct DosEnvec *)BADDR(FSSM->fssm_Environ);
          STRPTR Name = (STRPTR)BADDR(FSSM->fssm_Device);

          if(TypeOfMem(DosEnvec) && TypeOfMem(Name))
          {
            if(Name[0] > 0 && !Name[(WORD)Name[0] + 1])
            {
              struct IOStdReq IORequest;

              memset(&IORequest,0,sizeof(IORequest));
              IORequest.io_Message.mn_Length = sizeof(IORequest);

              if(!OpenDevice(Name + 1,FSSM->fssm_Unit,(struct IORequest *)&IORequest,FSSM->fssm_Unit))
              {
                CloseDevice((struct IORequest *)&IORequest);

                if(DosEnvec->de_TableSize > 0 && DosEnvec->de_LowCyl <= DosEnvec->de_HighCyl)
                  GoodDevice = TRUE;
              }
            }
          }
        }
      }
    }

    UnLockDosList(LDF_DEVICES | LDF_READ);
  }

  FreeDeviceProc(DevProc);

  if(GoodDevice)
  {
    struct InfoData *InfoData;

    if(InfoData = (struct InfoData *)AllocVec(sizeof(struct InfoData),MEMF_ANY | MEMF_PUBLIC))
    {
      UBYTE NewName[MAX_FILENAME_LENGTH],*Index;
      BPTR FileLock;

      memcpy(NewName,DrivePath,sizeof(NewName) - 1);

      NewName[sizeof(NewName) - 1] = 0;

      Index = PathPart(NewName);

      *Index = 0;

      FileLock = Lock(NewName,ACCESS_READ);

      if(FileLock)
      {
        if(Info(FileLock,InfoData))
          Size = InfoData->id_BytesPerBlock * (InfoData->id_NumBlocks - InfoData->id_NumBlocksUsed);

        UnLock(FileLock);
      }

      FreeVec(InfoData);
    }
  }

  return(Size);
}


  /* time(time_t *timeptr):
   *
   *  Get the current time.
   */

time_t
time(time_t *timeptr)
{
  struct timeval  Now;
  time_t      CurrentTime;

  DB(kprintf("time 0x%08lx\n",timeptr));

    /* If the timer is already available,
     * just read the time. Otherwise, open what
     * we need to tell the time.
     */

  if(TimerBase)
    GetSysTime(&Now);
  else
    UpdateTime(&Now);

    /* Determine the current time, taking the time
     * zone into account.
     */

  CurrentTime = (time_t)(Now.tv_secs + GMT_Offset);

  if(timeptr)
    *timeptr = CurrentTime;

  return(CurrentTime);
}

  /* localtime(const time_t *t):
   *
   *  Convert UTC into local time.
   */

struct tm *
localtime(const time_t *t)
{
  STATIC struct tm Time;

  ULONG       Seconds,
            Delta;
  struct ClockData  ClockData;
  BOOL        CloseIt = FALSE;

  DB(kprintf("localtime 0x%08lx\n",t));

    /* We need utility.library for the date conversion. */

  if(!UtilityBase)
  {
    if(UtilityBase = OpenLibrary("utility.library",37))
      CloseIt = TRUE;
  }

    /* Any luck? */

  if(!UtilityBase)
  {
    memset(&Time,0,sizeof(struct tm));

    return(&Time);
  }

    /* Update the time data. */

  if(!TimerBase)
    UpdateTime(NULL);

    /* Add the offset. */

  if(*t < GMT_Offset)
    Seconds = 0;
  else
    Seconds = (ULONG)*t - GMT_Offset;

    /* Convert the seconds into time data. */

  Amiga2Date(Seconds,&ClockData);

    /* Convert the time data. */

  Time.tm_sec   = ClockData.sec;
  Time.tm_min   = ClockData.min;
  Time.tm_hour  = ClockData.hour;
  Time.tm_mday  = ClockData.mday;
  Time.tm_mon   = ClockData.month - 1;
  Time.tm_year  = ClockData.year - 1900;
  Time.tm_wday  = ClockData.wday;

    /* No daylight savings time info is provided. */

  Time.tm_isdst = 0;

    /* We will need to fill in the yday entry. */

  ClockData.mday  = 1;
  ClockData.month = 1;

  Delta = Date2Amiga(&ClockData);

  Time.tm_yday = (Seconds - Delta) / (24 * 60 * 60) + 1;

    /* Clean up if necessary. */

  if(CloseIt)
  {
    CloseLibrary(UtilityBase);

    UtilityBase = NULL;
  }

  return(&Time);
}

  /* stat(const char *file,struct stat *st):
   *
   *  Get information on a file.
   */

int
stat(const char *file,struct stat *st)
{
  BPTR FileLock;
  D_S(struct FileInfoBlock,FileInfo);

  DB(kprintf("stat |%s| 0%08lx\n",file,st));

    /* Try to get a lock on the file. */

  if(FileLock = Lock((STRPTR)file,ACCESS_READ))
  {
    STATIC char Volume[MAX_FILENAME_LENGTH],Comment[80];
    D_S(struct InfoData,InfoData);

      /* Get information on file and filing system. */

    if(Examine(FileLock,FileInfo) && Info(FileLock,InfoData))
    {
      unsigned short mode = 0;

        /* Try to get the name of the volume. */

      if(!NameFromLock(FileLock,Volume,sizeof(Volume)))
        Volume[0] = 0;
      else
      {
        WORD i;

          /* Chop off everything after the colon. */

        for(i = 0 ; i < sizeof(Volume) ; i++)
        {
          if(Volume[i] == ':')
          {
            Volume[i + 1] = 0;

            break;
          }
        }
      }

      UnLock(FileLock);

        /* Build the protection bits. */

      if(!(FileInfo->fib_Protection & FIBF_EXECUTE))
        mode |= S_IEXECUTE;

      if(!(FileInfo->fib_Protection & FIBF_DELETE))
        mode |= S_IDELETE;

      if(!(FileInfo->fib_Protection & FIBF_READ))
        mode |= S_IREAD;

      if(!(FileInfo->fib_Protection & FIBF_WRITE))
        mode |= S_IWRITE;

      if(!(FileInfo->fib_Protection & FIBF_ARCHIVE))
        mode |= S_IARCHIVE;

      if(FileInfo->fib_Protection & FIBF_PURE)
        mode |= S_IPURE;

      if(FileInfo->fib_Protection & FIBF_SCRIPT)
        mode |= S_ISCRIPT;

        /* Keep the comment. */

      CopyMem(FileInfo->fib_Comment,Comment,79);
      Comment[79] = 0;

        /* Fix the time if necessary. */

      if(!TimerBase)
        UpdateTime(NULL);

        /* Fill in the data. */

      st->st_mode   = mode;
      st->st_ino    = FileInfo->fib_DiskKey;
      st->st_dev    = InfoData->id_DiskType;
      st->st_rdev   = Volume;
      st->st_nlink  = FileInfo->fib_DirEntryType == ST_SOFTLINK || FileInfo->fib_DirEntryType == ST_LINKDIR || FileInfo->fib_DirEntryType == ST_LINKFILE;
      st->st_uid    = FileInfo->fib_OwnerUID;
      st->st_gid    = FileInfo->fib_OwnerGID;
      st->st_size   = FileInfo->fib_Size;
      st->st_atime  = GMT_Offset + (FileInfo->fib_Date.ds_Days * 24 * 60 + FileInfo->fib_Date.ds_Minute) * 60 + (FileInfo->fib_Date.ds_Tick / TICKS_PER_SECOND);
      st->st_mtime  = st->st_atime;
      st->st_ctime  = st->st_atime;
      st->st_type   = FileInfo->fib_DirEntryType;
      st->st_comment  = Comment;

        /* Success. */

      return(0);
    }

    UnLock(FileLock);
  }

    /* What else should I choose? */

  errno = _OSERR = EIO;

  return(-1);
}
