/* wblock.c -- Copyright 1991, 1993, 1994, 1996 Liam R. E. Quin.
 * All Rights Reserved.
 * This code is NOT in the public domain.
 * See the file COPYRIGHT for full details.
 */

/* The low-level physical Word Database for lq-text.
 *
 * $Id: wblock.c,v 1.9 1996/05/14 16:36:03 lee Exp $
 */

#include "error.h"
#include "globals.h" /* defines and declarations for database filenames */

#include <stdio.h> /* stderr, also for fileinfo.h */

#include <sys/types.h>
#ifdef HAVE_FCNTL_H
# ifdef HAVE_SYSV_FCNTL_H
#  include <sys/stat.h>
# endif
# include <fcntl.h>
#endif

#include "fileinfo.h" /* for wordinfo.h */
#include "wordinfo.h"
#include "pblock.h"
#include "numbers.h"
#include "wordrules.h"
#include "putbyte.h"
#include "blkheader.h"
#include "liblqtext.h"

/** C library functions that need to be declared: **/
/** lqtext library functions that need to be declared: **/
/** Functions within this file that need to be declared: **/
/** **/


/* <Function>
 *   <Class>Database/Physical
 *   <Name>LQT_FlushBlock
 *   <Purpose>
 *      Writes out the given block to the cache.  This is really the
 *	same as LQT_WriteBlock, except that it is used for the last
 *	block in each chain of matches.
 * </Function>
 */
API void
LQT_FlushBlock(db, Block, ByteCount, NextOffset, LastStart, WID)
    t_LQTEXT_Database *db;
    unsigned char *Block;
    int ByteCount;
    unsigned long *NextOffset, *LastStart;
    t_WID WID;
{
    if (*LastStart && Block) {
	/*NOSTRICT*/
	t_BlockHeader *BH = (t_BlockHeader *) Block;
	register unsigned char *p;
	int BlocksToWrite = (ByteCount + BLOCKSIZE - 1) / BLOCKSIZE;
							/* round up! */

	BH->NextOffset = 0L;
#ifdef WIDINBLOCK
	BH->WID = WID;
#endif
	/* pad with -1 for future compatibility */
	for (
	    p = &Block[BLOCKSIZE*BlocksToWrite - 1];
	    p >= &Block[ByteCount];
	    p--
	) {
	     *p = (unsigned char) 0xFF;
	}

	LQT_SetLastBlockInChain(db, WID, LastStart, &Block[ByteCount], Block);
	LQT_WriteBlock(db, *LastStart, Block, BlocksToWrite, WID);
    }

    if (*NextOffset) {
	/* We allocated it in case we needed it, and we didn't need it */
	LQT_SetBlockStatus(db, *NextOffset, SET_BLOCK_AS_FREE);
    }

    *LastStart = *NextOffset = 0L;
}

/* This is simply to help keep the source lines getting too long! */
typedef unsigned char *UCP;

LIBRARY int
LQTp__PutByte(
    db,
    Byte,
    WID,
    sp,
    Blockp,
    BlockLength,
    LastStart,
    NextBlock,
    NextLength
)
    t_LQTEXT_Database *db;
    unsigned int Byte;
    t_WID WID;
    unsigned char **sp;
    unsigned char **Blockp;
    unsigned int *BlockLength;
    unsigned long *NextBlock;
    unsigned long *NextLength;
    unsigned long *LastStart; /* for writing the linked list */
{
    t_BlockHeader *BH;

    if (*sp - (*Blockp) >= (*BlockLength)) {
	int NumberOfBlocks;

	if (!*NextBlock && !*LastStart) {
	    return -1; /* only do the 1st block */
	}

	NumberOfBlocks = (*BlockLength + BLOCKSIZE - 1) / BLOCKSIZE;

	if (*NextBlock == (unsigned long) 0) {
	    *NextBlock = LQT_FindFreeBlock(db, WID, BlockLength, 0);
	} else {
	    if (NextLength && *NextLength) {
		*BlockLength = (*NextLength);
		*NextLength = 0;
	    } else {
		Error(E_BUG,
		    "LQTp__PutByte: NextLength is %s",
		    NextLength ? "zero" : "a null pointer"
		);
	    }
	}

	/* Complete the information in the previous block, if required */
	if (*LastStart) {
	    BH = (t_BlockHeader *) (*Blockp);
#ifdef WIDINBLOCK
	    BH->WID = WID;
#endif
	    BH->NextOffset = (*NextBlock);
	    /* Write the old block */
	    LQT_WriteBlock(db, *LastStart, *Blockp, NumberOfBlocks, WID);
	    *LastStart = 0L;
	}
	*LastStart = (*NextBlock);
	(*NextBlock) = 0L;
	*Blockp = LQT_ReadBlock(db, *LastStart, (t_WID) 0);
	/*NOSTRICT*/
	BH = (t_BlockHeader *) (*Blockp);
	BH->NumberOfBlocks = (*BlockLength/BLOCKSIZE); /*exact*/
	(*sp) = (UCP) BH->Data;
    }
    **sp = Byte;
    (*sp)++;
    return 0;
}

/* PutLong -- write a long number in compressed/abbreviated form into a
 * string.  If this moves the string pointer beyond the block, write out
 * the block and start a new one.  In that case, the number written may well
 * span the gap between the blocks.  We use an overflow buffer to copy
 * the bytes (if any) that overflowed into it.
 * Then we write them at the start of the next block.
 *
 * This routine returns -1 and writes a partial number (no allocated block)
 * if *LastBlock and *NextBlock are zero.  This allows PutwOrdPlaces to be
 * called to put the WordPlaces into the WIDFILE block without writing out
 * an entire chain.
 */

LIBRARY int
LQTp__PutLong(
    db,
    Long,
    WID,
    sp,
    Blockp,
    BlockLength,
    LastStart,
    NextBlock,
    NextLength
)
    t_LQTEXT_Database *db;
    unsigned long Long;
    t_WID WID;
    unsigned char **sp;
    unsigned char **Blockp;
    unsigned int *BlockLength;
    unsigned long *NextBlock;
    unsigned long *NextLength;
    unsigned long *LastStart; /* for writing the linked list */
{
    unsigned char *Start = (*sp);
 
    if (LQT_sWriteNumber(
	(unsigned char **) sp,
	Long, 
	*Blockp,
	*BlockLength
    ) < 0) {
	unsigned int BytesWritten;
        int NumberOfBlocks;
	t_BlockHeader *BH;
 
        if (!*NextBlock && !*LastStart) {
	    /* We were being tentative, seeing if the data would fit
	     * or not, and it didn't.
	     */
	    return -1;
	}
 
        NumberOfBlocks = (*BlockLength + BLOCKSIZE - 1) / BLOCKSIZE;
	BytesWritten = (*BlockLength - (Start - (*Blockp)));
 
        if (*NextBlock == (unsigned long) 0) {
            *NextBlock = LQT_FindFreeBlock(db, WID, BlockLength, 0);
        } else {
            if (NextLength && *NextLength) {
                *BlockLength = (*NextLength);
                *NextLength = 0;
            } else {
                Error(E_BUG,
                    "LQTp__PutLong: NextLength is %s",
                    NextLength ? "zero" : "a null pointer"
                );
            }
        }

        /* Complete the information in the previous block, if required */
        if (*LastStart) {
            BH = (t_BlockHeader *) (*Blockp);
            BH->NextOffset = *NextBlock;
#ifdef WIDINBLOCK
            BH->WID = WID;
#endif
            /* Write the old block */
            LQT_WriteBlock(db, *LastStart, *Blockp, NumberOfBlocks, WID);
	}

	/* If both LastStart and NextBlock are set, it is because
	 * we allocated a block before calling LQT_WriteWordPlaces.
	 * In that case, we will use that block next, and then after
	 * that NextBlock will always be zero.
	 */
        *LastStart = (*NextBlock);
        (*NextBlock) = 0L;
	*Blockp = LQT_ReadBlock(db, *LastStart, (t_WID) 0);
        BH = (t_BlockHeader *) (*Blockp);
        (*sp) = (UCP) BH->Data;
        BH->NumberOfBlocks = *BlockLength / BLOCKSIZE; /* always exact */

	/* now write the rest of the number out */
	{
	    unsigned char Buffer[sizeof(unsigned long) * 8 / 7 + 1];
	    unsigned char *Bufp = Buffer;
	    register unsigned char *p;

	    (void) LQT_sWriteNumber(
		&Bufp, Long, Buffer, sizeof Buffer
	    );

            for (p = &Buffer[BytesWritten]; p < Bufp; p++) {
                *((*sp)++) = (*p);
            }
        }
    }
    return 0;
}