/****************************************************************************
**  File:       ID-handler.c
**  Program:    ID-handler - an AmigaDOS handler for generating unique names
**  Version:    1.0
**  Author:     Ed Puckett      qix@mit-oz
**
**  Copyright 1987 EpAc Software.  All Rights Reserved.
**
**  History:    02-Feb-87       Original Version
*/

#include   <libraries/dos.h>
#include   <libraries/dosextens.h>
#include   <libraries/filehandler.h>
#include   <exec/exec.h>
#include   <ctype.h>



/*---------------------------------------------------------------------------
** References to system
*/

extern struct Library     *OpenLibrary ();
extern void               CloseLibrary ();
extern struct Task        *FindTask ();
extern ULONG              Wait ();
extern struct Message     *GetMsg ();
extern void               PutMsg ();
extern BYTE               *AllocMem ();
extern void               FreeMem ();

extern struct Library     *AbsExecBase;



/*---------------------------------------------------------------------------
** These are new to the 1.2 release
*/

#ifndef MODE_READWRITE
# define   MODE_READWRITE   1004
#endif  MODE_READWRITE

#ifndef MODE_READONLY
# define   MODE_READONLY    MODE_OLDFILE
#endif  MODE_READONLY

#ifndef ACTION_END
# define   ACTION_END       1007     /* not really new, just missing */
#endif  ACTION_END



/*---------------------------------------------------------------------------
*/

#define   ALLOCMEM_FLAGS    MEMF_PUBLIC


#define   ID_DIGITS   16

typedef struct opendata
  { char   id[ID_DIGITS];
    UBYTE  pos;
  }
OPENDATA;


struct Library  *SysBase  =  NULL;
struct Library  *DOSBase  =  NULL;

static char  ID[ID_DIGITS];



/*---------------------------------------------------------------------------
*/

#define   BPTRtoCptr(Bp)      ((char *) ((ULONG) (Bp) << 2))
#define   CptrtoBPTR(Cp)      ((BPTR)   ((ULONG) (Cp) >> 2))

#define   ReplyPkt(pkt)       PutMsg ((pkt)->dp_Port, (pkt)->dp_Link)



/*---------------------------------------------------------------------------
**      handler() performs initialization, replies to startup packet, and
** dispatches incoming request packets to the apropriate functions.
**      Our DeviceNode Task field is patched with our process ID so that this
** process is used for subsequent handler requests.  The function exits only
** if there is some initialization error.
*/

void  handler (StartPkt)

struct DosPacket  *StartPkt;

{ struct Task        *Task;
  struct MsgPort     *IDPort;
  ULONG              WakeupMask, SigMask;
  struct DeviceNode  *DevNode;
  struct DosPacket   *pkt, *GetPkt();
  unsigned           i;
  void               OpenID(), CloseID(), ReadID();


  SysBase= AbsExecBase;
  if ((DOSBase= OpenLibrary (DOSNAME, 0)) == NULL)
    goto QUIT;

  Task= FindTask (0);
  IDPort= (struct MsgPort *) ((ULONG) Task + sizeof (struct Task));
  ((struct Process *) Task)->pr_CurrentDir= 0;     /* initial file system root */

  WakeupMask= (1L << IDPort->mp_SigBit);

  DevNode= (struct DeviceNode *) BPTRtoCptr (StartPkt->dp_Arg3);
  DevNode->dn_Task= IDPort;

  ReplyPkt (StartPkt);


  for (i= 0; i < ID_DIGITS; ++i)
    ID[i]= '0';


LOOP:
  SigMask= Wait (WakeupMask);

  if (SigMask & WakeupMask)
    while ((pkt= GetPkt (IDPort)) != NULL)
      switch (pkt->dp_Type)
        { case MODE_READWRITE:
            OpenID (pkt);
            break;

          case MODE_NEWFILE:     /* syn: ACTION_FINDOUTPUT */
            pkt->dp_Res1= 0;
            pkt->dp_Res2= ERROR_WRITE_PROTECTED;
            ReplyPkt (pkt);
            break;

          case MODE_READONLY:     /* syn: MODE_OLDFILE, ACTION_FINDINPUT */
            OpenID (pkt);
            break;

          case ACTION_END:
            CloseID (pkt);
            break;

          case ACTION_READ:
            ReadID (pkt);
            break;

          case ACTION_WRITE:
            pkt->dp_Res1= -1;
            pkt->dp_Res2= ERROR_WRITE_PROTECTED;
            ReplyPkt (pkt);
            break;

          default:
            pkt->dp_Res1= 0;
            pkt->dp_Res2= ERROR_ACTION_NOT_KNOWN;
            ReplyPkt (pkt);
        }

  goto LOOP;


QUIT:
  DevNode->dn_Task= NULL;     /* bad if someone in process of accessing us . . . */

  if (DOSBase != NULL)
    CloseLibrary (DOSBase);
}



/*---------------------------------------------------------------------------
**      GetPkt() returns the DosPacket associated with the next message on
** "port", or NULL if the port is empty.  The message is removed from the
** port.  A related macro, ReplyPkt(), is provided above.
*/

static struct DosPacket  *GetPkt (port)

register struct MsgPort  *port;

{ register struct Message  *msg;

  return  ((msg= GetMsg (port)) == NULL)
            ? NULL
            : (struct DosPacket *) msg->mn_Node.ln_Name;
}



/*---------------------------------------------------------------------------
*/

static void  OpenID (pkt)

struct DosPacket  *pkt;

{ struct FileHandle  *handle;
  OPENDATA           *OpenData  =  NULL;
  unsigned           i;
  void               NextID();


  if ((OpenData= (OPENDATA *) AllocMem (sizeof (OPENDATA), ALLOCMEM_FLAGS)) == NULL)
    { pkt->dp_Res1= 0;
      pkt->dp_Res2= ERROR_NO_FREE_STORE;
      ReplyPkt (pkt);
    }

  for (i= 0; i < ID_DIGITS; ++i)
    OpenData->id[i]= ID[i];

  OpenData->pos= 0;

  NextID ();

  handle= (struct FileHandle *) BPTRtoCptr (pkt->dp_Arg1);
  handle->fh_Arg1= (LONG) OpenData;     /* for identification on Read, Close */

  pkt->dp_Res1= 1;
  pkt->dp_Res2= 0;     /* for successful open */
  ReplyPkt (pkt);
}



/*---------------------------------------------------------------------------
*/

static void  CloseID (pkt)

struct DosPacket  *pkt;

{ OPENDATA  *OpenData;


  OpenData= (OPENDATA *) pkt->dp_Arg1;

  FreeMem (OpenData, sizeof (OPENDATA));

  pkt->dp_Res1= 1;
  pkt->dp_Res2= 0;
  ReplyPkt (pkt);
}



/*---------------------------------------------------------------------------
*/

static void  ReadID (pkt)

struct DosPacket  *pkt;

{ OPENDATA  *OpenData;
  unsigned  n;


  OpenData= (OPENDATA *) pkt->dp_Arg1;

  pkt->dp_Res1= 0;

  if (OpenData->pos < ID_DIGITS)
    { if ((n= pkt->dp_Arg3) > (ID_DIGITS - OpenData->pos))
        n= (ID_DIGITS - OpenData->pos);

      for ( ; pkt->dp_Res1 < n; ++(pkt->dp_Res1), ++(OpenData->pos))
        ((char *) pkt->dp_Arg2)[pkt->dp_Res1]= OpenData->id[OpenData->pos];
    }

  pkt->dp_Res2= 0;
  ReplyPkt (pkt);
}



/*---------------------------------------------------------------------------
*/

static void  NextID ()

{ int  i;

  for (i= ID_DIGITS - 1; (i >= 0) && (++(ID[i]) > '9'); --i)
    ID[i]= '0';
}
