
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <exec/memory.h>
#include <proto/dos.h>
#include <proto/exec.h>

#include "action_abort.h"

UBYTE *JTName = "AbortPktPatch-JT";

BPTR
LockAndOpenFile (char *file, char *device)
{
  BPTR lock, dlock, fh = NULL;
  int ret;

  if (!(lock = Lock (file, ACCESS_READ)))
  {
    printf ("No such file.\n");
    return NULL;
  }
  if (!(dlock = Lock (device, ACCESS_READ)))
  {
    printf ("Can't lock device \"%s\".\n", device);
    return NULL;
  }
  ret = SameLock (lock, dlock); // Can't use dp -> dvp_Lock here!?
  UnLock (dlock);

  if (ret == LOCK_SAME || ret == LOCK_SAME_VOLUME)
    fh = OpenFromLock (lock);
  else
  {
    if (ret == LOCK_DIFFERENT)
      printf ("\n\"%s\" is not on \"%s\".\n\n", file, device);
    else
      printf ("Lock() returned unknown lock status.\n");
    UnLock (lock);
    return NULL;
  }
  return fh;
}

void
SendAndAbort (struct DevProc *dp, int use_patch, char *device, char *file)
{
  struct StandardPacket *spr = NULL;
  struct StandardPacket *spa = NULL;
  struct MsgPort *port = dp -> dvp_Port;
  struct MsgPort *mpr = NULL;
  struct MsgPort *mpa = NULL;
  void *buffer;
  ULONG mask;
  int out = 0;
  BPTR fh;

  if (file)
  {
    if (!(fh = LockAndOpenFile (file, device)))
      return;
  }
  if (!(mpr = CreatePort (NULL, 0)))
    goto cleanup;

  if (!(mpa = CreatePort (NULL, 0)))
    goto cleanup;

  if (!(spr = AllocMem (sizeof (struct StandardPacket), MEMF_CLEAR | MEMF_PUBLIC)))
    goto cleanup;

  if (!(spa = AllocMem (sizeof (struct StandardPacket), MEMF_CLEAR | MEMF_PUBLIC)))
    goto cleanup;

  if (!(buffer = AllocMem (16, MEMF_PUBLIC)))
    goto cleanup;

  spr -> sp_Msg.mn_Length = sizeof (struct StandardPacket);
  spr -> sp_Msg.mn_Node.ln_Name = (char *) &spr -> sp_Pkt;
  spr -> sp_Pkt.dp_Link = &spr -> sp_Msg;
  spr -> sp_Pkt.dp_Port = mpr;
  spr -> sp_Pkt.dp_Type = ACTION_READ;
  spr -> sp_Pkt.dp_Arg1 = ((struct FileHandle *) BADDR (fh)) -> fh_Arg1;
  spr -> sp_Pkt.dp_Arg2 = buffer;
  spr -> sp_Pkt.dp_Arg3 = 16;
  SendPkt (&spr -> sp_Pkt, port, mpr);
  out ++;
  
  if (use_patch)
  {
    printf ("Calling AbortPkt () ...\n");
    AbortPkt (port, &spr -> sp_Pkt);
    printf ("AbortPkt () ... done.\n");
  }
  else
  { 
    spa -> sp_Msg.mn_Length = sizeof (struct StandardPacket);
    spa -> sp_Msg.mn_Node.ln_Name = (char *) &spa -> sp_Pkt;
    spa -> sp_Pkt.dp_Link = &spa -> sp_Msg;
    spa -> sp_Pkt.dp_Port = mpa;
    spa -> sp_Pkt.dp_Type = ACTION_ABORT;
    spa -> sp_Pkt.dp_Arg1 = &spr -> sp_Pkt;
    SendPkt (&spa -> sp_Pkt, port, mpa);
    out ++;
  }
  
  mask = 1 << mpr -> mp_SigBit | 1 << mpa -> mp_SigBit;

  while (out)
  {
    Wait (mask);
    if (GetMsg (mpr))
    {
      printf ("ACTION_READ returned.\n");
      out --;
    }
    if (GetMsg (mpa))
    {
      if (spa -> sp_Pkt.dp_Res1 == DOSTRUE)
        printf ("\nACTION_ABORT is supported and was successful.\n");
      else if (spa -> sp_Pkt.dp_Res2 == ERROR_OBJECT_NOT_FOUND)
        printf ("\nACTION_ABORT is supported but failed.\n");
      else if (spa -> sp_Pkt.dp_Res2 == ERROR_ACTION_NOT_KNOWN)
        printf ("\nACTION_ABORT is not implemented by this device.\n");
      else
        printf ("\nACTION_ABORT: Unexpected error %d.\n", spa -> sp_Pkt.dp_Res2);
      out --;
    }
  }

  cleanup:

  if (fh) Close (fh);
  if (buffer) FreeMem (buffer, 16);
  if (mpr) DeletePort (mpr);
  if (mpa) DeletePort (mpa);
  if (spr) FreeMem (spr, sizeof (struct StandardPacket));
  if (spa) FreeMem (spa, sizeof (struct StandardPacket));
}

int
main (int argc, char *argv[])
{
  struct DevProc *dp;
  struct RDArgs *rda;
  LONG ar[3] = { 0,0,0 };
  char *template = "P=USEPATCH/S,H=HANDLER/K/A,F=FILE/K";
  char *device, *file;
  int use_patch;

  if (!(rda = ReadArgs (template, ar, NULL)))
  {
    printf ("%s: required argument missing.\n  Template: %s\n", argv[0], template);
    exit (0);
  }
  use_patch = ar[0];
  device = (char *) ar[1];
  file = (char *) ar[2];

  if (use_patch)
  {
    if (!FindSemaphore (JTName))
    {
      printf ("AbortPktPatch is not installed.\n");
      exit (0);
    }
  }
  if (device[strlen(device)-1] == ':' && (dp = GetDeviceProc (device, NULL)))
  {
    SendAndAbort (dp, use_patch, device, file);
    FreeDeviceProc (dp);
  }
  else
    printf ("No such device.\n");

  FreeArgs (rda);
}
