/*static char *SCCSID = "@(#)mkroom.c   6.1 90/11/17";*/
/*static char *SCCSID = "@(#)mkroom.c   13.3 90/01/25";*/
// @75229 move to _BTXT
#define _BTXT _BTXT
#include <os2.h>
#include "api.h"
#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "fnode.h"
#include "tables.h"
#include "pb_ifs.h"
#include "superb.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "pbmacros.h"

#include "pr_misc.h"
#include "pr_debug.h"
#include "pr_DIR.h"


/*
 ***    MkRoom - Make Room in the Directory
 *
 *      MkRoom makes enough room for a new entry in a directory by
 *      sliding open a hole.  The directories are kept fully packed
 *      and we move open a hole when necessary and move it closed
 *      when the file is removed.
 *
 *      This is not the time waste that it seems because the 80386
 *      moves bytes very rapidly, and the directory entires are always
 *      quad aligned.  Given that an average directory will be
 *      3/4 full, and that an average move is for half of that,
 *      our timings are:
 *
 *      average move:   96 dwords       28 us (16 mhz)
 *      worst   move:   250 dwords      73 us (16 mhz)
 *
 *      Of course, we have to remember that we'll pay this price
 *      again when we delete the file, wheras if we left the holes
 *      we wouldn't.  Likewise, when we
 *              delete foo.bak
 *              rename foo.asm -> foo.bak
 *              rename temp    -> foo.asm
 *      we can't reuse the holes for foo.bak and foo.asm because we
 *      packed them.
 *
 *      Never the less, I think that the above times are reasonable,
 *      given the overhead of a scattered hole scheme that doesn't
 *      add bulk to the directory entries.  This later is a key concern,
 *      since directories are read a lot more than written, and maximizing
 *      the entries in one block is important.
 *
 *      ENTRY   cusHowMuch = size needed
 *              pdeInsertPt = address of insert point (is some DIRENT)
 *              pdbToAdd = address of start of dir block
 *      EXIT    Returns address of new directory entry if room made.
 *                      The DIR_ELEN field is already setup and the
 *                      flags are cleared.  The caller must setup the rest
 *              Return NULL pointer if no space available
 */

PDIRENT far
MkRoom (cusHowMuch, pdeInsertPt, pdbToAdd)
USHORT  cusHowMuch;     /* size needed */
PDIRENT pdeInsertPt;    /* address of insert point */
PDIRBLK pdbToAdd;       /* address of directory block to add */
{
        USHORT  offInsertEnd, offTargetEnd, cusBytes;

        /*
         * offInsertEnd is the end of the block of memory to be moved up
         * offTargetEnd is the end of the range to which it will be moved
         * Both are offsets into the DIRBLK, not memory addresses
         */
        offInsertEnd = (USHORT)pdbToAdd->offulFirstFree - 1;
        offTargetEnd = offInsertEnd + cusHowMuch;

        /*
         * If there is not room in this DIRBLK for the entry, return NULL
         */
        if (offTargetEnd > DIRBLKS)
                return NULL;
        /*
         * Update the free pointer in the DIRBLK to reflect this insertion
         */
        pdbToAdd->offulFirstFree = offTargetEnd + 1;

        /*
         * Mark the contents of the DIRBLK as changed
         */
        pdbToAdd->culChange += 2;

        /*
         * We will move bytes from the insert point to the end of the used
         * space in the DIRBLK (offInsertEnd)
         */
        cusBytes = offInsertEnd + 1 - ((PBYTE)pdeInsertPt - (PBYTE)pdbToAdd);

        BYTE_COPY_DN ((PBYTE) pdbToAdd + offInsertEnd,  /* src ptr */
                      (PBYTE) pdbToAdd + offTargetEnd,  /* dest ptr */
                      cusBytes);

        /*
         * Store length field in the new DIRENT
         */
        pdeInsertPt->cchThisEntry = cusHowMuch;

        /* ??? BUGBUG - What about the various flag fields in the DIRENT? */

        pdeInsertPt->fFlags = 0;
        pdeInsertPt->fAttr = 0;

        return pdeInsertPt;
}
