/*
 * Copyright (C) 1990 Commodore-Amiga, Inc.
 * All rights reserved
 */

/*
 * $Id: fn_handler.c,v 1.11 90/06/23 13:29:46 ewout Exp Locker: ewout $
 * 
 */

/*
 * Example handler for AppShell.
 * 
 * Handler sets up a process and lets the application/user have a file polled
 * with a specified interval. Applictation is expected to supply the address
 * of a function, which will be called in case of a change in the file
 * status.
 * 
 * Note that this is just an example handler with a lot of overhead for
 * file notification.
 * 
 */

#include <libraries/appshell.h>
#include <dos/dostags.h>
#include <dos/dosextens.h>
#include <devices/timer.h>
#include <string.h>
#include "fn_handler.h"
#include "fn_error.h"

#include <clib/appshell_protos.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>



/* I N T E R N A L  F U N C T I O N  P R O T O T Y P E S */

void __saveds   FileNotifier(void);
struct TargetNode *FindTarget(struct MinList * list, UBYTE * name);
struct TargetNode *AllocNodeMem(UBYTE * filename, struct PoolHeader * ph);
void            FreeNodeMem(struct TargetNode * node, struct PoolHeader * ph);
struct Process *__stdargs CreateAProcess(ULONG firsttag,...);

struct MsgHandler *setup_fnA(struct AppInfo *, struct TagItem *);
BOOL            open_fn(struct AppInfo *, struct MsgHandler *, struct TagItem *);
BOOL            handle_fn(struct AppInfo *, struct MsgHandler *, struct TagItem *);
BOOL            close_fn(struct AppInfo *, struct MsgHandler *, struct TagItem *);
BOOL            shutdown_fn(struct AppInfo *, struct MsgHandler *, struct TagItem *);
void            FileNotifyA(struct AppInfo *, STRPTR, struct TagItem *);

/* The message structure passed to the process to exchange
 * port addresses.
 */
struct StartUpMessage {
    struct Message  su_Msg;
    struct MsgPort *su_Port;
    LONG            su_Error;
};

/* node for the hitlist maintained by the process */
struct TargetNode {
    struct MinNode  tnm_Node;
    struct timerequest *fntr;
    struct SIPCMessage *ntfymsg;
    struct FNData  *fndata;
    LONG            FileSize;
    struct DateStamp datestamp;
};

#define FNotifyID       APSH_FN_ID+1
#define FN_NOTIFICATION APSH_FN_ID+2

#define FN_SHUTDOWN     5

/* Function address for NOTIFICATION will be supplied in setup_fnA */
struct Funcs    fn_funcs[] =
{
    {"NOTIFY", FileNotifyA, FNotifyID,},
    {"NOTIFICATION", 0L, FN_NOTIFICATION, NULL, NULL, APSH_FF_PRIVATE,},
    {NULL, NO_FUNCTION,}
};


struct MsgHandler *
setup_fnA(struct AppInfo * ai, struct TagItem * tl)
{
    struct MsgHandler *mh;
    struct MHObject *mho;
    struct FNInfo  *fni;
    struct Process *mp;
    struct MsgPort *port;
    struct StartUpMessage *sumsg;
    ULONG           hstatus;
    BOOL            FAILED = TRUE;

    ai->ai_TextRtn = NULL;

    /* Allocate all we need to get going */

    /* Get some memory */
    if (mh = (struct MsgHandler *)
	AllocVec(sizeof(struct MsgHandler) +
		 sizeof(struct FNInfo) +
		 sizeof(struct SIPCMessage) +
		 sizeof(struct FNData) +
		 sizeof(ULONG) * 4, MEMF_CLEAR | MEMF_PUBLIC)) {
	mho = &(mh->mh_Header);
        /* It's a handler so the Type is handler type and the
         * priority handler priority.
         */
	mho->mho_Node.ln_Type = MH_HANDLER_T;
	mho->mho_Node.ln_Pri = MH_HANDLER_P;
        /* Give the handler a name */
	mho->mho_Node.ln_Name = "FN";
	NewList(&(mho->mho_ObjList));
        /* Give the handler an ID */
	mho->mho_ID = APSH_FN_ID;
        /* Inititally the handler is closed and disabled */
	mho->mho_Status = (MHS_ENABLED | MHS_CLOSE);
	mho->mho_SysData = fni = MEMORY_FOLLOWING(mh);
	fni->fni_sipcmsg = MEMORY_FOLLOWING(fni);
	fni->fni_fndata = MEMORY_FOLLOWING(fni->fni_sipcmsg);
	mh->mh_NumFuncs = 4;
	mh->mh_Func = MEMORY_FOLLOWING(fni->fni_fndata);
	if (fni->fni_clientport = CreatePort(0, 0)) {
	    if (fni->fni_replyport = CreatePort(0, 0)) {
		/* Fire up process */
		if (mp = (struct Process *)
		    CreateAProcess(NP_Entry, FileNotifier,
				   NP_Name, "File Notify",
				   NP_StackSize, 8000,
				   NP_Priority, 5,
				   TAG_END)) {
		    sumsg = AllocVec(sizeof(struct StartUpMessage), MEMF_CLEAR);
		    sumsg->su_Msg.mn_Node.ln_Type = NT_MESSAGE;
		    sumsg->su_Msg.mn_Length = sizeof(struct StartUpMessage);
		    sumsg->su_Msg.mn_ReplyPort = fni->fni_replyport;
		    sumsg->su_Port = fni->fni_clientport;
		    port = &(mp->pr_MsgPort);
		    PutMsg((struct MsgPort *) port, (struct Message *) sumsg);
		    WaitPort(fni->fni_replyport);
		    while (GetMsg(fni->fni_replyport));
		    if (sumsg->su_Error == RETURN_OK) {
			FAILED = FALSE;
			fni->fni_fnport = sumsg->su_Port;
		    }
		    FreeVec(sumsg);
		}
	    }
	}
    }
    /* Hmmm. Oh well. */
    if (FAILED) {
	if (mh) {
	    if (fni->fni_clientport) {
		if (fni->fni_replyport)
		    DeletePort(fni->fni_replyport);
		DeletePort(fni->fni_clientport);
	    }
	    FreeVec(mh);
	    mh = NULL;
	}
	ai->ai_Pri_Ret = RETURN_FAIL;
	ai->ai_Sec_Ret = APSH_CLDNT_INIT_MSGH;
	ai->ai_TextRtn = PrepText(ai, APSH_MAIN_ID, ai->ai_Sec_Ret, "File Notification");
    } else {
	hstatus = GetTagData(APSH_Status, NULL, tl);
        /* Declare the handler active if so defined in initialisation */
	if (hstatus & P_ACTIVE)
	    open_fn(ai, mh, tl);

        /* Store the address of the basic handlers functions in the
         * message handler structure.
         */
	mh->mh_Func[MH_OPEN] = open_fn;
	mh->mh_Func[MH_HANDLE] = handle_fn;
	mh->mh_Func[MH_CLOSE] = close_fn;
	mh->mh_Func[MH_SHUTDOWN] = shutdown_fn;
	mh->mh_SigBits = (1L << fni->fni_clientport->mp_SigBit);
	mh->mh_DefText = FN_ErrorMsgs;
	/*
	 * Get address of appliction function to call, use donothing
	 * (StubFunc()) if not supplied
	 */
	fn_funcs[1].fe_Func = (VOID *) GetTagData(APSH_CmdData, (ULONG) StubFunc, tl);
	AddFuncEntries(ai, fn_funcs);
    }
    return (mh);
}


/* Enable handler */
BOOL
open_fn(struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
{
    struct MHObject *mho = &(mh->mh_Header);

    mho->mho_Status &= ~MHS_CLOSE;
    mho->mho_Status |= MHS_OPEN;

    return (TRUE);
}

/* Disable handler */
BOOL
close_fn(struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
{
    struct MHObject *mho = &(mh->mh_Header);

    mho->mho_Status &= ~MHS_OPEN;
    mho->mho_Status |= MHS_CLOSE;

    return (TRUE);
}

/* Handle incoming messages */
BOOL
handle_fn(struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
{
    struct MHObject *mho = &(mh->mh_Header);
    struct FNInfo  *fni = (struct FNInfo *) mho->mho_SysData;
    struct MsgPort *ClientPort = fni->fni_clientport;
    struct SIPCMessage *sipcmsg;
    struct FNData  *fwd_fndata;
    struct TagItem  tg[3];
    ULONG           FuncID;

    while (sipcmsg = (struct SIPCMessage *) GetMsg(ClientPort)) {
	FuncID = sipcmsg->sipc_Type;

	if ((FuncID != NO_FUNCTION) && (mho->mho_Status & MHS_OPEN) && (mho->mho_Status & MHS_ENABLED)) {
	    fwd_fndata = fni->fni_fndata;
	    CopyMem(sipcmsg->sipc_Data, fwd_fndata, sizeof(struct FNData));

	    /* Make fn taglist, pass tl for consistency */
	    tg[0].ti_Tag = APSH_CmdData;
	    tg[0].ti_Data = (ULONG) fwd_fndata;
	    tg[1].ti_Tag = APSH_CmdDataLength;
	    tg[1].ti_Data = sizeof(struct FNData);
	    tg[2].ti_Tag = TAG_MORE;
	    tg[2].ti_Data = (ULONG) tl;

            /* Call the function */
	    PerfFunc(ai, FuncID, NULL, tg);
	}
	ReplyMsg((struct Message *) sipcmsg);
    }
    return (TRUE);
}

/* Shutdown process, free all allocations */
BOOL
shutdown_fn(struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
{
    struct MHObject *mho;
    struct FNInfo  *fni;
    struct SIPCMessage *sipcmsg;

    if (mh) {
	mho = &(mh->mh_Header);
	fni = mho->mho_SysData;

	/* Inform the process it's time to leave. */
	sipcmsg = fni->fni_sipcmsg;
	sipcmsg->sipc_Msg.mn_Node.ln_Type = NT_MESSAGE;
	sipcmsg->sipc_Msg.mn_Length = sizeof(struct SIPCMessage);
	sipcmsg->sipc_Msg.mn_ReplyPort = fni->fni_replyport;
	sipcmsg->sipc_Type = FN_SHUTDOWN;
	PutMsg((struct MsgPort *) fni->fni_fnport, (struct Message *) sipcmsg);

	WaitPort((struct MsgPort *) (fni->fni_replyport));
	while (GetMsg(fni->fni_replyport));

	DeletePort(fni->fni_replyport);
	DeletePort(fni->fni_clientport);
	Remove((struct Node *) mh);
	FreeVec(mh);
    }
    return (TRUE);
}


/****** fn_handler/NOTIFY *********************************************
*
*   NAME
*	NOTIFY - Send a command to the fn_handler
*
*   SYNOPSIS
*	FNotifyID   <Command> <FILE=filename> [INT=interval]
*                    [RET=retries] [FLAGS=flags]
*
*   FUNCTION
*	Allows command line commands to be directly send to the handler.
*	
*	Accepted commands: ADD, DEL, SYNC.
*	See fn_handler.h for valid flags.
*	If optional arguments are ommited, interval defaults to 60,
*	retries to -1, flags to FN_NEWER.
*
*   EXAMPLE
*
*	NOTIFY ADD FILE=topcat:mbox INT=20 RET=-1 FLAGS=FN_NEWER|FN_MUSTEXIST
*
*
*   SEE ALSO
*	FileNotify() / fn_handler.h
*
***********************************************************************
*/

VOID
FileNotifyA(struct AppInfo * ai, STRPTR str, struct TagItem * tl)
{
    struct MsgHandler *mh;
    struct MHObject *mho;
    struct FNInfo  *fni;
    struct TagItem *fti;
    struct SIPCMessage *sipcmsg;
    struct FNData  *fndata;
    struct FNData  *userbuffer = NULL;
    STRPTR          argv[MAXARG];
    ULONG           argc;
    STRPTR          parsedline;
    STRPTR          tmpptr;
    BOOL           *usererror = NULL;

    if (mh = HandlerData(ai, APSH_Handler, "FN", TAG_DONE)) {
	mho = &(mh->mh_Header);
	fni = mho->mho_SysData;
	sipcmsg = fni->fni_sipcmsg;
	fndata = fni->fni_fndata;
	fndata->fnd_Flags = 0;

	parsedline = BuildParseLine(str, &argc, argv);

	/* Always gets at least the function nameid string passed */
	if (argc >= 2) {
	    if (QStrCmpI(argv[1], "ADD"))
		sipcmsg->sipc_Type = FN_ADDTARGET;
	    else if (QStrCmpI(argv[1], "DEL"))
		sipcmsg->sipc_Type = FN_DELTARGET;
	    else if (QStrCmpI(argv[1], "SYNC"))
		sipcmsg->sipc_Type = FN_SYNC;
	    else
		sipcmsg->sipc_Type = 0;

	    if (sipcmsg->sipc_Type != 0) {
		tmpptr = FindType(argv, "INT", "60");
		stcd_l(tmpptr, &(fndata->fnd_Interval));
		tmpptr = FindType(argv, "RET", "-1");
		stcd_l(tmpptr, &(fndata->fnd_Retries));
		fndata->fnd_Filename = FindType(argv, "FILE", NULL);
		tmpptr = FindType(argv, "FLAGS", "FN_NEWER");
		if (MatchValue(tmpptr, "FN_MUSTEXIST"))
		    fndata->fnd_Flags |= FN_MUSTEXIST;
		if (MatchValue(tmpptr, "FN_CREATED"))
		    fndata->fnd_Flags |= FN_CREATED;
		if (MatchValue(tmpptr, "FN_SMALLER"))
		    fndata->fnd_Flags |= FN_SMALLER;
		if (MatchValue(tmpptr, "FN_BIGGER"))
		    fndata->fnd_Flags |= FN_BIGGER;
		if (MatchValue(tmpptr, "FN_NEWER"))
		    fndata->fnd_Flags |= FN_NEWER;
	    }
	} else {
	    /* No commandline or arexx arguments, check for tags. */
	    userbuffer = (struct FNData *) GetTagData(FN_Buffer, NULL, tl);
	    fndata->fnd_Retries = (LONG) GetTagData(FN_Retries, -1, tl);
	    fndata->fnd_Interval = (LONG) GetTagData(FN_Interval, 60, tl);
	    fndata->fnd_Flags = (ULONG) GetTagData(FN_Flags, FN_NEWER, tl);
	    fndata->fnd_Filename = (UBYTE *) GetTagData(FN_Filename, NULL, tl);
	    sipcmsg->sipc_Type = (LONG) GetTagData(FN_Command, 0L, tl);
	    usererror = (BOOL *) GetTagData(FN_Error, NULL, tl);

	    /*
	     * Removes all tags. Individual flags can be set and override
	     * FN_Flags tag.
	     */

	    do {
		fti = NextTagItem(&tl);
		if (fti) {
		    switch (fti->ti_Tag) {
		    case FN_MustExist:
			if ((BOOL) fti->ti_Data)
			    fndata->fnd_Flags |= FN_MUSTEXIST;
			else
			    fndata->fnd_Flags ^= FN_MUSTEXIST;
			break;
		    case FN_Created:
			if ((BOOL) fti->ti_Data)
			    fndata->fnd_Flags |= FN_CREATED;
			else
			    fndata->fnd_Flags ^= FN_CREATED;
			break;
		    case FN_Deleted:
			if ((BOOL) fti->ti_Data)
			    fndata->fnd_Flags |= FN_DELETED;
			else
			    fndata->fnd_Flags ^= FN_DELETED;
			break;
		    case FN_Smaller:
			if ((BOOL) fti->ti_Data)
			    fndata->fnd_Flags |= FN_SMALLER;
			else
			    fndata->fnd_Flags ^= FN_SMALLER;
			break;
		    case FN_Bigger:
			if ((BOOL) fti->ti_Data)
			    fndata->fnd_Flags |= FN_BIGGER;
			else
			    fndata->fnd_Flags ^= FN_BIGGER;
			break;
		    case FN_Newer:
			if ((BOOL) fti->ti_Data)
			    fndata->fnd_Flags |= FN_NEWER;
			else
			    fndata->fnd_Flags ^= FN_NEWER;
			break;
		    }
		}
	    } while (fti != TAG_DONE);
	}

	ai->ai_Pri_Ret = RETURN_WARN;
	ai->ai_Sec_Ret = RETURN_OK;

        /* If arguments were passed, either string or tags, we have
         * them now. Check if they are valid.
         */
	if (sipcmsg->sipc_Type > 0 && sipcmsg->sipc_Type < 5) {
	    if (strlen(fndata->fnd_Filename) != 0) {
		if (!(sipcmsg->sipc_Type == FN_ADDTARGET && (fndata->fnd_Flags & FN_ALLFLAGS) == 0)) {
		    /* Seems we got a valid set of arguments.
                     * Make a message and send to the notify process.
                     */
		    sipcmsg->sipc_Msg.mn_Node.ln_Type = NT_MESSAGE;
		    sipcmsg->sipc_Msg.mn_Length = sizeof(struct SIPCMessage);
		    sipcmsg->sipc_Msg.mn_ReplyPort = fni->fni_replyport;
                    /* Pass it a pointer to the relevant data */
		    sipcmsg->sipc_Data = (APTR) fndata;
		    PutMsg((struct MsgPort *) fni->fni_fnport, (struct Message *) sipcmsg);
		    WaitPort((struct MsgPort *) fni->fni_replyport);
		    while (GetMsg(fni->fni_replyport));

                    /* Copy the return values from the notify process,
                     * the dispatcher will deal with them.
                     */
		    ai->ai_Pri_Ret = sipcmsg->sipc_Pri_Ret;
		    ai->ai_Sec_Ret = sipcmsg->sipc_Sec_Ret;
		    ai->ai_TextRtn = FN_ErrorMsgs[sipcmsg->sipc_Sec_Ret];

		    if (ai->ai_Pri_Ret == RETURN_OK) {
			/*
			 * If desired, return arguments in application
			 * supplied buffer
			 */
			if (userbuffer) {
			    CopyMem(sipcmsg->sipc_Data, userbuffer, sizeof(struct FNData));
			    /* Give back own ptr to targetname, not ours */
			    userbuffer->fnd_Filename = fndata->fnd_Filename;
			}
		    }
		} else { /* No flags were set */
		    ai->ai_Sec_Ret = FN_FLAGS_ERR;
		    ai->ai_TextRtn = PrepText(ai, APSH_FN_ID, ai->ai_Sec_Ret, NULL);
		}
	    } else { /* No targetname was given */
		ai->ai_Sec_Ret = FN_FILENAME_ERR;
		ai->ai_TextRtn = PrepText(ai, APSH_FN_ID, ai->ai_Sec_Ret, NULL);
	    }
	} else { /* No handler command found */
	    ai->ai_Sec_Ret = FN_COMMAND_ERR;
	    ai->ai_TextRtn = ai->ai_TextRtn = PrepText(ai, APSH_FN_ID, ai->ai_Sec_Ret, NULL);
	}
	if (str)
	    FreeParseLine(parsedline);
    } else { /* The handler data could not be found */
	ai->ai_Pri_Ret = RETURN_FAIL;
	ai->ai_Sec_Ret = FN_HANDLER_ERR;
	ai->ai_TextRtn = PrepText(ai, APSH_FN_ID, ai->ai_Sec_Ret, NULL);
    }
    if (usererror) /* Return the error to the appliction, if desired */
	*usererror = ai->ai_Pri_Ret;
}

/* The notify process */
void            __saveds
FileNotifier(void)
{
    struct PoolHeader *ph;

    struct MsgPort *fnport;
    struct MsgPort *clientport;
    struct MsgPort *timerport;
    struct MsgPort *dosport;

    struct timerequest *tr;
    struct SIPCMessage *msg;
    struct SIPCMessage *finalmsg;
    struct StartUpMessage *startupmsg;
    struct Message *timermsg;

    struct FNData  *fndata;

    struct MinList *hitlist;
    struct TargetNode *tnode, *wnode, *nnode;
    struct FileInfoBlock *fib;
    LONG            lock;
    BOOL            ABORT = FALSE;
    long            pri_error, sec_error, result;
    BOOL            FAILED = TRUE;
    ULONG           client_sig, timer_sig, signal;

    /* Chi sono? */
    dosport = &(((struct Process *) FindTask(NULL))->pr_MsgPort);
    WaitPort(dosport);
    startupmsg = (struct StartUpMessage *) GetMsg(dosport);

    if (fnport = (struct MsgPort *) CreatePort(0, 0)) {
	if (timerport = (struct MsgPort *) CreatePort(0, 0)) {
	    if (ph = (struct PoolHeader *) CreatePrivatePool(MEMF_CLEAR | MEMF_PUBLIC, 4096, 0)) {
		fib = (struct FileInfoBlock *) AllocPooled(sizeof(struct FileInfoBlock), ph);
		tr = (struct timerequest *) AllocPooled(sizeof(struct timerequest), ph);
		hitlist = (struct MinList *) AllocPooled(sizeof(struct MinList), ph);
		NewList((struct List *) hitlist);
		if (!(OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *) tr, 0))) {

		    /* Get client port address & return my port */
		    clientport = (struct MsgPort *) startupmsg->su_Port;
		    startupmsg->su_Error = 0L;
		    startupmsg->su_Port = fnport;
		    ReplyMsg((struct Message *) startupmsg);
		    FAILED = FALSE;
		    client_sig = 1L << fnport->mp_SigBit;
		    timer_sig = 1L << timerport->mp_SigBit;

		    for (;;) {
			signal = Wait(client_sig | timer_sig);

			/* C L I E N T  S I G N A L */

			if (signal & client_sig) {
			    while (msg = (struct SIPCMessage *) GetMsg(fnport)) {
				fndata = (struct FNData *) msg->sipc_Data;
                                /* Start out being OK */
				msg->sipc_Pri_Ret = RETURN_OK;
				msg->sipc_Sec_Ret = RETURN_OK;

				switch (msg->sipc_Type) {

				case FN_ADDTARGET:

				    /*
				     * Don't allow duplicate targetfiles no
				     * more
				     */
				    if (tnode = FindTarget(hitlist, fndata->fnd_Filename)) {
					msg->sipc_Pri_Ret = RETURN_WARN;
					msg->sipc_Sec_Ret = FN_HASNOTIFICATION_ERR;
				    } else {
					lock = Lock(fndata->fnd_Filename, ACCESS_READ);
					if (lock == NULL) { /* File doesn't exist */
					    if (fndata->fnd_Flags & FN_MUSTEXIST) {
						msg->sipc_Pri_Ret = RETURN_WARN;
						msg->sipc_Sec_Ret = FN_DOESNOTEXIST_ERR;
					    } else
						fib->fib_Size = 0;
					} else {
					    /* this is more serious */
					    if (!(Examine(lock, fib))) {
						msg->sipc_Pri_Ret = RETURN_ERROR;
						msg->sipc_Sec_Ret = FN_READ_ERR;
					    } else {
						if (fib->fib_DirEntryType > 0) {
						    msg->sipc_Pri_Ret = RETURN_WARN;
						    msg->sipc_Sec_Ret = FN_NOTFILE_ERR;
						}
						UnLock(lock);
					    }
					}
				    }
				    if (msg->sipc_Pri_Ret == RETURN_OK) {
					if (tnode = AllocNodeMem(fndata->fnd_Filename, ph)) {
					    *(tnode->fntr) = *tr;
					    tnode->fntr->tr_node.io_Command = TR_ADDREQUEST;
					    tnode->fntr->tr_node.io_Message.mn_ReplyPort = timerport;
					    tnode->fntr->tr_node.io_Message.mn_Node.ln_Pri = 0;
					    tnode->fntr->tr_node.io_Message.mn_Node.ln_Name = NULL;
					    tnode->ntfymsg = NULL;
					    strcpy(tnode->fndata->fnd_Filename, fndata->fnd_Filename);
					    tnode->fndata->fnd_Interval = fndata->fnd_Interval;
					    tnode->fndata->fnd_Retries = fndata->fnd_Retries;
					    tnode->fndata->fnd_Flags = fndata->fnd_Flags;
					    tnode->FileSize = fib->fib_Size;
					    if (tnode->FileSize != 0)
						CopyMem(&(fib->fib_Date), &(tnode->datestamp), sizeof(struct DateStamp));
					    AddTail((struct List *) hitlist, (struct Node *) tnode);
					    tnode->fntr->tr_time.tv_secs = tnode->fndata->fnd_Interval;
					    tnode->fntr->tr_time.tv_micro = 0;
					    SendIO((struct IORequest *) tnode->fntr);
					} else {
					    msg->sipc_Pri_Ret = RETURN_ERROR;
					    msg->sipc_Sec_Ret = FN_NOMEM_ERR;
					}
				    }
				    break;

				case FN_DELTARGET:
				    if (tnode = (struct TargetNode *) FindTarget(hitlist, fndata->fnd_Filename)) {
					if (!(CheckIO((struct IORequest *) tnode->fntr))) {
					    AbortIO((struct IORequest *) tnode->fntr);
					    WaitIO((struct IORequest *) tnode->fntr);
					}
					FreeNodeMem(tnode, ph);
					msg->sipc_Pri_Ret = 0;
				    } else {
					msg->sipc_Pri_Ret = RETURN_WARN;
					msg->sipc_Sec_Ret = FN_UNKNOWN_ERR;
				    }
				    break;

				case FN_INQUIRY:
				    if (tnode = (struct TargetNode *) FindTarget(hitlist, fndata->fnd_Filename)) {
					msg->sipc_Data = tnode->fndata;
				    } else {
					msg->sipc_Pri_Ret = RETURN_WARN;
					msg->sipc_Sec_Ret = FN_UNKNOWN_ERR;
				    }
				    break;

				case FN_SHUTDOWN:
				    wnode = (struct TargetNode *) hitlist->mlh_Head;
				    while (nnode = (struct TargetNode *) (wnode->tnm_Node.mln_Succ)) {
					if (!(CheckIO((struct IORequest *) wnode->fntr))) {
					    AbortIO((struct IORequest *) wnode->fntr);
					    WaitIO((struct IORequest *) wnode->fntr);
					}
					FreeNodeMem(wnode, ph);
					wnode = nnode;
				    }
				    ABORT = TRUE;
				    finalmsg = msg;
				    break;

				case FN_SYNC:
				    if (tnode = (struct TargetNode *) FindTarget(hitlist, fndata->fnd_Filename)) {
					if (!(CheckIO((struct IORequest *) tnode->fntr))) {
					    AbortIO((struct IORequest *) tnode->fntr);
					    WaitIO((struct IORequest *) tnode->fntr);
					}
					tnode->fntr->tr_time.tv_secs = 0;
					tnode->fntr->tr_time.tv_micro = 0;
					SendIO((struct IORequest *) tnode->fntr);
				    } else {
					msg->sipc_Pri_Ret = RETURN_WARN;
					msg->sipc_Sec_Ret = FN_UNKNOWN_ERR;
				    }
				    break;

				case FN_NOTIFICATION:
				    /* Meglio tardi che mai! */
				    if (tnode = (struct TargetNode *) FindTarget(hitlist, fndata->fnd_Filename)) {
					if (fndata->fnd_Status & FN_DONE) {
					    FreeNodeMem(tnode, ph);
					} else {
					    if (lock = Lock(tnode->fndata->fnd_Filename, ACCESS_READ)) {
						if (Examine(lock, fib)) {
						    tnode->FileSize = fib->fib_Size;
						}
						UnLock(lock);
					    }
					    tnode->fntr->tr_time.tv_secs = tnode->fndata->fnd_Interval;
					    tnode->fntr->tr_time.tv_micro = 0;
					    SendIO((struct IORequest *) tnode->fntr);
					}
				    }
				    break;
				}
				if (msg->sipc_Msg.mn_Node.ln_Type != NT_REPLYMSG && ABORT == FALSE)
				    ReplyMsg((struct Message *) msg);
			    }
			}
			/* T I M E R  S I G N A L */

			if (signal & timer_sig) {
			    while (timermsg = (struct Message *) GetMsg(timerport)) {
				for (tnode = (struct TargetNode *) hitlist->mlh_Head; tnode->tnm_Node.mln_Succ; tnode = (struct TargetNode *) tnode->tnm_Node.mln_Succ) {
				    if (tnode->fntr == (struct timerequest *) timermsg) {
					break;
				    }
				}
				if (tnode->fntr == (struct timerequest *) timermsg) {
				    pri_error = sec_error = RETURN_OK;
				    result = fib->fib_Size = 0;
				    if (tnode->fndata->fnd_Retries > 0)
					tnode->fndata->fnd_Retries--;
				    if (tnode->fndata->fnd_Retries == 0)
					result |= FN_DONE;
				    if (lock = Lock(tnode->fndata->fnd_Filename, ACCESS_READ)) {
					if (!(Examine(lock, fib))) {
					    pri_error = RETURN_ERROR;
					    sec_error = FN_READ_ERR;
					}
					UnLock(lock);
				    }
				    if (pri_error == RETURN_OK) {
					if (!(lock)) {
					    if (tnode->fndata->fnd_Flags & FN_DELETED && tnode->FileSize > 0)
						result |= FN_DELETED;
					    if (tnode->fndata->fnd_Flags & FN_SMALLER && tnode->FileSize > 0)
						result |= FN_SMALLER;
					} else {
					    if (tnode->fndata->fnd_Flags & FN_CREATED && tnode->FileSize == 0)
						result |= FN_CREATED;
					    if (tnode->fndata->fnd_Flags & FN_NEWER && tnode->FileSize == 0)
						result |= FN_NEWER;
					    if (tnode->fndata->fnd_Flags & FN_SMALLER && fib->fib_Size < tnode->FileSize)
						result |= FN_SMALLER;
					    if (tnode->fndata->fnd_Flags & FN_BIGGER && fib->fib_Size > tnode->FileSize)
						result |= FN_BIGGER;
					    if (tnode->fndata->fnd_Flags & FN_NEWER && tnode->FileSize != 0) {
						if (CompareDates(&(tnode->datestamp), &(fib->fib_Date)))
						    result |= FN_NEWER;
					    }
					}

					tnode->FileSize = fib->fib_Size;

					if (result != 0 || pri_error != RETURN_OK) {
					    /* OK, it's a go. */
					    tnode->fndata->fnd_Status = result;
					    tnode->ntfymsg = (struct SIPCMessage *) AllocPooled(sizeof(struct SIPCMessage), ph);
					    if (tnode->FileSize != 0)
						CopyMem(&(fib->fib_Date), &(tnode->datestamp), sizeof(struct DateStamp));
					    tnode->ntfymsg->sipc_Msg.mn_Node.ln_Type = NT_MESSAGE;
					    tnode->ntfymsg->sipc_Msg.mn_Length = sizeof(struct SIPCMessage);
					    tnode->ntfymsg->sipc_Msg.mn_ReplyPort = fnport;
					    tnode->ntfymsg->sipc_Type = FN_NOTIFICATION;
					    tnode->ntfymsg->sipc_Data = tnode->fndata;
					    tnode->ntfymsg->sipc_DSize = sizeof(struct FNData) + strlen(tnode->fndata->fnd_Filename) + 1;
					    tnode->ntfymsg->sipc_Pri_Ret = pri_error;
					    tnode->ntfymsg->sipc_Sec_Ret = sec_error;
					    PutMsg((struct MsgPort *) clientport, (struct Message *) tnode->ntfymsg);
					} else {
					    tnode->fntr->tr_time.tv_secs = tnode->fndata->fnd_Interval;
					    tnode->fntr->tr_time.tv_micro = 0;
					    SendIO((struct IORequest *) tnode->fntr);
					}
				    }
				}
			    }
			}
			if (ABORT)
			    break;
		    }
		    CloseDevice((struct IORequest *) tr);
		}
		FreePooled(hitlist, ph);
		FreePooled(tr, ph);
		FreePooled(fib, ph);

		DeletePrivatePool(ph);
	    }
	    DeletePort(timerport);
	}
	DeletePort(fnport);
    }
    Forbid();

    if (FAILED) {
	startupmsg->su_Error = RETURN_FAIL;
	startupmsg->su_Port = NULL;
	ReplyMsg((struct Message *) startupmsg);
    } else {
	ReplyMsg((struct Message *) finalmsg);
    }
}


struct TargetNode *
FindTarget(struct MinList * list, UBYTE * name)
{
    struct TargetNode *node;

    for (node = (struct TargetNode *) list->mlh_Head; node->tnm_Node.mln_Succ; node = (struct TargetNode *) node->tnm_Node.mln_Succ) {
	if (strcmp(name, node->fndata->fnd_Filename) == 0)
	    return (node);
    }
    return (NULL);
}


struct TargetNode *
AllocNodeMem(UBYTE * filename, struct PoolHeader * ph)
{
    struct TargetNode *node;

    if (node = (struct TargetNode *) AllocPooled(sizeof(struct TargetNode), ph)) {
	if (node->fntr = (struct timerequest *) AllocPooled(sizeof(struct timerequest), ph)) {
	    if (node->fndata = (struct FNData *) AllocPooled(sizeof(struct FNData), ph)) {
		if (!(node->fndata->fnd_Filename = (UBYTE *) AllocPooled(strlen(filename) + 1, ph))) {
		    FreePooled(node->fndata, ph);
		    FreePooled(node->fntr, ph);
		    FreePooled(node, ph);
		    node = NULL;
		}
	    } else {
		FreePooled(node->fntr, ph);
		FreePooled(node, ph);
		node = NULL;
	    }
	} else {
	    FreePooled(node, ph);
	    node = NULL;
	}
    }
    return (node);
}


void
FreeNodeMem(struct TargetNode * node, struct PoolHeader * ph)
{
    FreePooled(node->fndata->fnd_Filename, ph);
    FreePooled(node->fndata, ph);
    FreePooled(node->fntr, ph);
    if (node->ntfymsg) {
	FreePooled(node->ntfymsg, ph);
    }
    Remove((struct Node *) node);
    FreePooled(node, ph);
}



/* stack to tag */

struct MsgHandler *
setup_fn(struct AppInfo * ai, ULONG tags,...)
{
    return (setup_fnA(ai, (struct TagItem *) & tags));
}

/****** fn_handler/FileNotify *****************************************
*
*   NAME
*	FileNotify - Send command to fn_handler
*
*   SYNOPSIS
*	FileNotify(struct AppInfo * ai, STRPTR str, ULONG tags,...)
*
*	struct AppInfo *ai;
*	STRPTR str;
*	ULONG tags,...
*
*   FUNCTION
*	This function lets the application send commands to the fn_handler.
*
*   EXAMPLE
*	FileNotify(ai, NULL,
*		FN_Command, FN_INQUIRY,
*		FN_Filename, "dh0:testfile"
*		FN_Error, &error,
*		FN_Buffer, fndata,
*		TAG_DONE);
*
*   INPUTS
*	ai	- pointer to the AppInfo structure for this application.
*	str	- pointer to a command line like string or NULL
*	tags	- stack based TagItems.
*
*
*   RESULT
*	Error values are returned in ai->Pri_Err and ai->Sec_Err.
*	If requested, the primary error value is returned in an 
*	application supplied BOOL variable.
*	If requested, the data as used by the fn_handler is returned
*	in an application supplied FNData structure.
*
*   SEE ALSO
*	NOTIFY / fn_handler.h
*
***********************************************************************
*/

VOID
FileNotify(struct AppInfo * ai, STRPTR str, ULONG tags,...)
{
    PerfFunc(ai, FNotifyID, str, (struct TagItem *) & tags);
}

struct Process *__stdargs
CreateAProcess(ULONG firsttag,...)
{
    return (CreateNewProc((struct TagItem *) & firsttag));
}
