/*static char *SCCSID = "@(#)fs_notfy.c 6.2 92/01/31";*/
#define _BTXT _BTXT
/*
 ***    fs_notfy.c - FS_FINDNOTIFY routines
 *
 *      Copyright Microsoft, 1988.
 */

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include "heap.h"
#include "api.h"
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "find.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pbmacros.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "notify.h"
#include "pr_fsd.h"
#include "pr_HEAP.h"
#include "pr_misc.h"
#include "pr_WRSUB.h"

#include "pr_DEBUG.h"

/*
 *      HISTORY
 *          26-May-1989 by JR (John Rogers, JohnRo@Microsoft)
 *              - DCR 259.2: changed names of macros in "frame.h".
 */

/* put stand-alone fs_xxx functions in separate code segment */

/*
 **     Private variables used to track DosFindNotify handles.
 *
 *      Since the OS/2 kernel only gives us two bytes to keep track of these
 *      things with, we allocate a single segment for NOTIFY structures and
 *      use the offset into that segment as the handle that kernel keeps for
 *      us.
 */

STATIC PNOTIFY pnotFree;        /* free list of NOTIFY structures */
STATIC USHORT selNotify;        /* segment selector for NOTIFY structures */
STATIC ULONG cbNotify;          /* current size of NOTIFY handle segment */
STATIC ULONG semNotify =  0;    /* semaphore to protect segment allocation */
#ifndef DEBUG
extern PSZ pszSemClrErr;
#endif


/***    FS_FINDNOTIFYCLOSE - close a notify handle
 *
 *      This routine is used to close a handle returned from FS_FINDNOTIFYFIRST.
 *      We convert the handle into a pointer to a NOTIFY struct.  NotifyClose
 *      is called to allow the filesystem to decommission the NOTIFY struct.
 *      It is then put onto the free list to be used by the next
 *      FS_FINDNOTIFYFIRST request.
 *
 *      Returns:        NO_ERROR (to indicate success)
 *                      pnotFree updated
 */

int far pascal
FS_FINDNOTIFYCLOSE(USHORT handle)
{
    PNOTIFY pnot;
    FRAME   frm;

    frm.usHardErr = NO_ERROR;
    frm.fRecheckAddr = TRUE;
    frm.fFlag = 0;               // Clear Frame flag  @132506

    pnot = MAKEP(selNotify, handle);
#ifdef FIND_NOTIFY
    NotifyClose(pnot,&frm);
#endif
    pnot->dcpNext = pnotFree;
    pnotFree = pnot;

    return NO_ERROR;
}

/***    FS_FINDNOTIFYFIRST - initiate directory change monitoring
 *
 *      This routine is used to open a handle to monitor directory changes
 *      and to wait for some number of changes to occur.  We try to allocate
 *      a NOTIFY structure from the free list.  If the list is empty, we
 *      either allocate a segment for the NOTIFY structs if we haven't
 *      already done so or we try to grow the existing segment.  If the
 *      allocations all fail, an error is returned.  A path buffer is
 *      allocated and the pathname is copied into it and converted into
 *      internal form. NotifyOpen is called to initalize the NOTIFY struct
 *      and link it into the directory's notify list.  NotifyWait is called
 *      to wait for the requested number of changes to happen.  If NotifyWait
 *      returns an error, NotifyClose is called to undo the NotifyOpen and
 *      the error is returned to the caller.
 *
 *      Returns:        0 if all ok, error code otherwise.
 *                      NOTIFY struct initialized
 *                      *pHandle = offset of NOTIFY struct
 *                      *pcMatch = # of changes
 *                      pnotFree modified
 */


int far pascal
FS_FINDNOTIFYFIRST(struct cdfsi far *pcdfsi,
                   struct cdfsd far *pcdfsd,
                   PCHAR  pName,
                   USHORT iCurDirEnd,
                   USHORT attr,
                   USHORT far *pHandle,
                   PCHAR  pData,
                   USHORT cbData,
                   USHORT far *pcMatch,
                   USHORT level,
                   ULONG timeout)
{
    PCRACKEDPATH pcp;       /* ptr to cracked path buffer for NotifyOpen */
    register int iRet;
    PNOTIFY pnot;
    FRAME   frm;

    frm.usHardErr = NO_ERROR;
    frm.fRecheckAddr = TRUE;
    frm.fFlag = 0;               // Clear Frame flag  @132506

    if ((pnot = pnotFree) == NULL) {

        /* about to enter a critical section */
        SemRequestFar (&semNotify,&frm);

        if (selNotify == 0) {
            /*
             * We need to allocatate the initial notify handle segment
             * (non-swappable).
             */
            FS_MAYBLOCK (iRet = FSH_SEGALLOC(SA_FRING0, (ULONG)sizeof(NOTIFY),
                      &selNotify));
            if (iRet)
                goto fnfExit;

            cbNotify = sizeof(NOTIFY);
            pnot = MAKEP(selNotify, 0);

        } else {
            /*
             * We need to grow the Notify handle segment
             */
            FS_MAYBLOCK (iRet = FSH_SEGREALLOC(selNotify,
                                            cbNotify + sizeof(NOTIFY)));
            if (iRet)
                goto fnfExit;

            pnot = MAKEP(selNotify, (USHORT)cbNotify);
            cbNotify += sizeof(NOTIFY);
        }

        /* end of critical section */

        FS_MAYBLOCK (iRet = FSH_SEMCLEAR(&semNotify));

        if (iRet != NO_ERROR) {
            INTERR (pszSemClrErr);
        }

    } else
        pnotFree = pnot->dcpNext;

    *pHandle = OFFSETOF(pnot);

    iRet = CrackPath (pcdfsi->cdi_hVPB, pName, &pcp, &frm);
    if (iRet == NO_ERROR) {

#ifdef FIND_NOTIFY
        if ((iRet = NotifyOpen(pcp, attr, pnot, level,&frm)) == 0) {
            if ((iRet = NotifyWait(pnot, pcMatch, timeout,&frm)) != 0)
                NotifyClose(pnot,&frm); /* error from NotifyWait,
                                         * so we must close the handle. */
        }
#else
        iRet = ERROR_NOT_SUPPORTED;
#endif
    }
    if (pcp != NULL)
        RetHeap (pcp);

    if (iRet != NO_ERROR) {
        pnot->dcpNext = pnotFree;
        pnotFree = pnot;
    }


fnfExit:
    return iRet;

    /* to shutup -W3 warnings */
    pcdfsd; iCurDirEnd; pData; cbData;
#ifndef FIND_NOTIFY
    attr; pcMatch; level; timeout;
#endif
}


/***    FS_FINDNOTIFYNEXT - wait for directory changes
 *
 *      This routine is called with a handle returned from a previous call to
 *      FS_FINDNOTIFYFIRST to wait for a specifed number of changes to occur.
 *      The handle is converted into a pointer to a NOTIFY struct and
 *      NotifyWait is called to wait for the changes to happen.
 *
 *      Returns:        NO_ERROR if all ok, error code otherwise.
 *                      NOTIFY struct initialized
 *                      *pHandle = offset of NOTIFY struct
 *                      *pcMatch = # of changes
 *                      pnotFree modified
 */

int far pascal
FS_FINDNOTIFYNEXT(USHORT handle,
                  PCHAR  pData,
                  USHORT cbData,
                  USHORT far *pcMatch,
                  USHORT level,
                  ULONG  timeout)
{
    INT iRet;

#ifdef FIND_NOTIFY
    FRAME   frm;

    frm.usHardErr = NO_ERROR;
    frm.fRecheckAddr = TRUE;
    frm.fFlag = 0;               // Clear Frame flag  @132506

    iRet = NotifyWait(MAKEP(selNotify, handle), pcMatch, timeout,&frm);

#endif
    return iRet;

    /* to shutup -W3 warnings */
    pData; cbData; level;
#ifndef FIND_NOTIFY
    handle; pcMatch; timeout;
#endif
}
