/*

** New handler that makes it possible to use the output of one program
** as file available for another program.
**
** Example:
**
** sort "BLA:list ram: all lformat *"%N*"" to ram:ee
**
** Bases on:
**
** Echo Handler V1.2
** ©1991 Henrik Nordström
** Ängsvägen 1
** 756 45 Uppsala
** Sweden
**
** Fully public domain, and truly a real hack...
** Use it in any way you like, but don't blame me if it doesn't work.
** This little code needs AmigaDOS 2.0-> to work without modification
**
** compiled with gcc 2.5.8
**
** copy BLAHandler L:
**
** Mountlist entry to use BLA:
** BLA:
**     Handler = l:BlaHandler
**     Stacksize = 4096
**     Priority = 5
**     GlobVec = -1
** #
**
*/

#include <string.h>

#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <inline/stubs.h>
#ifdef __OPTIMIZE__
#include <inline/exec.h>
#include <inline/dos.h>
#else
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#endif


/* We use this to skip definitions of constant strings */

void _pre_main(void);

int start(void)
{
	_pre_main();
	return 0;
}


struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

struct filedata
{
  BPTR filefh;
  char *filename;
};

static const char *version="\x00$VER: BLAHandler 1.0 (18.4.94)";

void _pre_main(void)
{
  struct DosPacket *pkt;
  struct DeviceNode *node;
  struct Process *myproc;
  struct Process *theirproc;
  struct MsgPort *myport;
  struct FileHandle *fh;
  struct filedata *fd;
  int die = FALSE;
  LONG type;
  LONG arg1;
  LONG arg2;
  LONG arg3;
  int opencount = 0;
  char *name;
  BPTR filefh;
  BPTR filefh2;
  char *data;
  int len;
  BPTR oldcurrentdir;

  LONG openint=0;		/* numer of file to open */
  char *mem;            /* our temporary mem for filename and prg name */
  LONG err_code;        /* return code from SystemTags */

  SysBase = *((struct ExecBase **) 4L);
  myproc = (struct Process *) FindTask (NULL);
  myport = &myproc->pr_MsgPort;

  DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", 36);
  if (DOSBase == NULL)
    return;

  pkt = WaitPkt ();
  node = BADDR (pkt->dp_Arg3);

  /*
   * Install process if everything OK and stay resident forever! * (or
   * until killed whith ACTION_DIE) )
   */
  node->dn_Task = myport;
  ReplyPkt (pkt, DOSTRUE, pkt->dp_Arg2);

  do
    {
      pkt = WaitPkt ();
      type = pkt->dp_Type;
      arg1 = pkt->dp_Arg1;
      arg2 = pkt->dp_Arg2;
      arg3 = pkt->dp_Arg3;
      fd = (struct filedata *) arg1;

    switch (type)
	{
		/* we don't matter what type of FIND we get, we are always doing the same :-) */

	case ACTION_FINDINPUT:

	case ACTION_FINDUPDATE:

	  fh = (struct FileHandle *) BADDR (arg1);
	  name = (char *) BADDR (arg3) + 1;
	  /* Strip ECHO: from filename */
	  while (*name && *name != ':')
	    name++;
	  if (*name == ':')
	    name++;

	  theirproc = (struct Process *) pkt->dp_Port->mp_SigTask;

	/* build temporary name */

		if(mem=AllocVec(100+strlen(name)+5,MEMF_ANY))
		{
			/* everything ok */

			/* strip '"' from filename */
			char *s=&mem[100];
			if(*name=='\"') name++;

			/* copy name */

			while(*s++=*name++);

			if(s[-1]=='\"') s[-1]=0;
		}
		else
		{
	      ReplyPkt (pkt, DOSFALSE, ERROR_NO_FREE_STORE);
	      break;
		}

		sprintf(mem,"PIPE:xx_temp_%ld", openint++);

	  filefh2 = Open (mem, MODE_NEWFILE);

	  if (!filefh2)
	    {
	      FreeVec(mem);
	      ReplyPkt (pkt, DOSFALSE, IoErr ());
	      break;
	    }

	  fd = (struct filedata *) AllocMem (sizeof (*fd), 0);
	  if (fd == NULL)
	    {
	      Close (filefh2);
   	      FreeVec(mem);
	      ReplyPkt (pkt, DOSFALSE, ERROR_NO_FREE_STORE);
	      break;
	    }

	  oldcurrentdir = CurrentDir (theirproc->pr_CurrentDir);

	  err_code=SystemTags(&mem[100], SYS_Asynch, (LONG) TRUE, NP_Cli, (LONG) TRUE, SYS_Output, (LONG)filefh2, NP_CloseInput, (LONG) FALSE, NP_CloseOutput, (LONG)TRUE);

	  CurrentDir (oldcurrentdir);

	  if(err_code==-1)
 	  {
		/* error */
   	      FreeVec(mem);
		  Close(filefh2);
	      ReplyPkt (pkt, DOSFALSE, IoErr ());
	      break;
	  }


      filefh = Open (mem, MODE_OLDFILE);

      /* we are ready to get read commands now */

	  /* Everything OK! fill in datastructure and reply. */
      fd->filename=mem;
	  fd->filefh = filefh;

	  /* real strange names in 2.02... probably better names in include 2.04 */

	  fh->fh_Port = (struct MsgPort *)(-1L); /* Interactive */
	  fh->fh_Type = myport;                  /* Message port */
	  fh->fh_Arg1 = (LONG) fd;               /* File data, passed as ARG1 to all functions */

	  ReplyPkt (pkt, DOSTRUE, 0);
	  opencount++;
	  break;


	case ACTION_READ:
	  data = (char *) arg2;
	  len = arg3;
	  if (fd->filefh)
	    len=Read (fd->filefh, data, len);
	  if(len<0)
	    ReplyPkt(pkt, len, IoErr());

	  ReplyPkt (pkt, len, 0);
	  break;


	case ACTION_END:

	  if (fd->filefh)
      {
		int Buffer[512];

		while(Read(fd->filefh,Buffer,512)==512);

	    Close (fd->filefh);
	  }

	  FreeMem ((APTR) fd, sizeof (*fd));
	  ReplyPkt (pkt, DOSTRUE, 0);
	  opencount--;
	  break;

	case ACTION_DIE:

	  /* Action_KillHandler */
	  die = TRUE;
	  node->dn_Task = NULL;
	  ReplyPkt (pkt, DOSTRUE, 0);
	  break;

	default:
	  /* Action_CantHandleThis B-( */
	  ReplyPkt (pkt, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
	  break;
	}
  } while (!die || opencount > 0);

  CloseLibrary ((struct Library *) DOSBase);
}
