/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1995-2002

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

# include "picoblob.h"
static char rcsid[] = "$Id: picoblob.c,v 1.1 2002/05/06 13:10:59 picoSoft Exp $";
static char rcsidh2[] = picoblob_h;
# include <fcntl.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <signal.h>
# include <sys/stat.h>
# include <time.h>
# include <ctype.h>
# if defined(MSDOS) || defined(WIN32)
# include <io.h>
# include <sys/locking.h>
# define getuid() 0
# if !defined(WIN32)
# include <windows.h>
# define open(a,b,c) _lopen(a, READ_WRITE|OF_SHARE_COMPAT)
# define lseek       _llseek
# define read        _lread
# define write       _lwrite
# define close       _lclose
# endif
# else
# include <unistd.h>
# endif
# include <errno.h>

# define BLOB_EXT  ".blb"
# define BLOB_ERR    (-1)
# define BLOB_SUCCESS 0

# define TYPE_START_BLK 0xFF
# define TYPE_CONT_BLK  0xFE
# define TYPE_FREE_BLK  0xFD

/*
 * limits
 */

# ifdef _NFILE
# define MAXOPENFILE (_NFILE)
# else
# define MAXOPENFILE 255
# endif

/*
 * Header
 */
# define SIGN "\xB1\xC0\x42lob"
# define SIGN_LEN (sizeof(SIGN) - 1)
# define RELEASE (UINT2)0x0100

# define MIN_BLK_SIZE 24
# define DEF_BLK_SIZE 1024
# define MIN_HEADER_SIZE 24

typedef UINT4 ADDR;
typedef int ISBOOL;
# define ISTRUE (1==1)
# define ISFALSE (1==0)

typedef struct tFileDesc {
   CHAR * name;
   int fd;
   UINT4 firstFreeBlock;
   UINT4 afterEndBlock;
   UINT4 blockSize;
   int openMode;
   UINT4 headerSize;
   UINT2 releaseLevel;
   enum _pI__lckType {
       ISNOLCK,
       ISRDLCK,
       ISWRLCK
   } headerLock;
   int errorCode;
} FileDesc;

/*
 * Global variables
 */

static FileDesc *Files[MAXOPENFILE];
static void (*origSigInt)(int) = SIG_DFL;
static void (*origSigQuit)(int) = SIG_DFL;
static void (*origSigTerm)(int) = SIG_DFL;

/*
 * Macro
 */

# define checkMode(m) switch (m) {\
                      case ISINPUT:\
                      case ISOUTPUT:\
                      case ISINOUT:\
                         break;\
                      default:\
                         errno = EBADARG;\
                         return BLOB_ERR;\
                      }

# define checkIsOpen(f) if (f < 0 || f >= MAXOPENFILE || Files[f] == 0) {\
                            errno = ENOTOPEN;\
                            return Return;\
                        }


# define putLongInBuffer(cp,l)  htonlp(cp, l),cp+=sizeof(UINT4)
# define putShortInBuffer(cp,s) htonsp(cp, s),cp+=sizeof(UINT2)
# define getLongFromBuffer(l,cp) l=pntohl(cp),cp+=sizeof(UINT4)
# define getShortFromBuffer(s,cp) s=pntohs(cp),cp+=sizeof(UINT2)

/* Endian functions */

typedef enum _pI__tag_endianType {
   _pI__UNDEF_ENDIAN,
   _pI__BIG_ENDIAN,
   _pI__LITTLE_ENDIAN
} _pI__endianType;

static _pI__endianType _pI__Endian = _pI__UNDEF_ENDIAN;

static void
_pI__checkEndian (void)
{
   INT4  num = 0x31323334L;

   if ( ((((char *)&num)[0]) == 0x31) && ((((char *)&num)[1]) == 0x32) &&
    ((((char *)&num)[2]) == 0x33) && ((((char *)&num)[3]) == 0x34) )
      _pI__Endian = _pI__BIG_ENDIAN;
   else if ( ((((char *)&num)[3]) == 0x31) && ((((char *)&num)[2]) == 0x32) &&
         ((((char *)&num)[1]) == 0x33) && ((((char *)&num)[0]) == 0x34) )
      _pI__Endian = _pI__LITTLE_ENDIAN; 
}

static union _pI__tagSwap {
   UINT2 num2;
   UINT4 num4;
   UCHAR chr[4];
} _pI__Swap;

static UINT2
pntohs (UCHAR * x)
{
start:
   switch (_pI__Endian) {
   case _pI__BIG_ENDIAN:
      _pI__Swap.chr[0] = x[0];
      _pI__Swap.chr[1] = x[1];
      return _pI__Swap.num2;
   case _pI__LITTLE_ENDIAN:
      _pI__Swap.chr[0] = x[1];
      _pI__Swap.chr[1] = x[0];
      return _pI__Swap.num2;
   default:
      _pI__checkEndian();
      goto start;
   }
}

static void
htonsp (UCHAR *d, UINT2 x)
{
start:
   switch (_pI__Endian) {
   case _pI__BIG_ENDIAN:
      _pI__Swap.num2 = x;
      d[0] = _pI__Swap.chr[0];
      d[1] = _pI__Swap.chr[1];
      break;
   case _pI__LITTLE_ENDIAN:
      _pI__Swap.num2 = x;
      d[0] = _pI__Swap.chr[1];
      d[1] = _pI__Swap.chr[0];
      break;
   default:
      _pI__checkEndian();
      goto start;
   }
} 

static UINT4
pntohl (UCHAR * x)
{
start:
   switch (_pI__Endian) {
   case _pI__BIG_ENDIAN:
      _pI__Swap.chr[0] = x[0];
      _pI__Swap.chr[1] = x[1];
      _pI__Swap.chr[2] = x[2];
      _pI__Swap.chr[3] = x[3];
      return _pI__Swap.num4;
   case _pI__LITTLE_ENDIAN:
      _pI__Swap.chr[0] = x[3];
      _pI__Swap.chr[1] = x[2];
      _pI__Swap.chr[2] = x[1];
      _pI__Swap.chr[3] = x[0];
      return _pI__Swap.num4;
   default:
      _pI__checkEndian();
      goto start;
   }
}

static void
htonlp (UCHAR *d, UINT4 x)
{
start:
   switch (_pI__Endian) {
   case _pI__BIG_ENDIAN:
      _pI__Swap.num4 = x;
      d[0] = _pI__Swap.chr[0];
      d[1] = _pI__Swap.chr[1];
      d[2] = _pI__Swap.chr[2];
      d[3] = _pI__Swap.chr[3];
      break;
   case _pI__LITTLE_ENDIAN:
      _pI__Swap.num4 = x;
      d[0] = _pI__Swap.chr[3];
      d[1] = _pI__Swap.chr[2];
      d[2] = _pI__Swap.chr[1];
      d[3] = _pI__Swap.chr[0];
      break;
   default:
      _pI__checkEndian();
      goto start;
   }
}

/* */

static ISBOOL
_pI__checkFileName (char * name)
{
   register int i;

   for (i = strlen(name); i >= 0; i--)
      if (name[i] == '/')
         break;
   if (strlen (&name[i + 1]) == 0)
      return ISFALSE;
   else
      return ISTRUE;
}

static int
_pI__mapOpenMode(int m)
{
   int Return;

   switch (m) {
   case ISINPUT:
      Return = O_RDONLY;
      break;
   case ISOUTPUT:
      Return = O_RDWR;
      break;
   default:
      Return = O_RDWR;
      break;
   }
# if defined(MSDOS) || defined(WIN32)
   Return |= O_BINARY;
# endif
   return Return;
}

static FileDesc *
FileDesc_new (char *name)
{
   FileDesc *Return = malloc (sizeof (FileDesc));
   int nameLen = strlen (name);

   Return->name = malloc (nameLen + sizeof(BLOB_EXT));
   sprintf (Return->name,"%s%s", name, BLOB_EXT);
   Return->fd = BLOB_ERR;
   Return->firstFreeBlock = 0;
   Return->afterEndBlock = 0;
   Return->blockSize = 0;
   Return->openMode = 0;
   Return->headerSize = 0;
   Return->releaseLevel = RELEASE;
   Return->headerLock = ISNOLCK;
   Return->errorCode = 0;
   return Return;
}

static void
FileDesc_delete (FileDesc *fd)
{
   if (fd->name != 0)
      free (fd->name);
   free (fd);
}

# if defined(WIN32) || defined(MSDOS)

static void
_pI__headerLock (int isfd, ISBOOL wlck)
{
   if (Files[isfd]->headerLock == ISNOLCK || 
         (Files[isfd]->headerLock == ISRDLCK && wlck)) {
      lseek (Files[isfd]->fd, 0L, 0);
      errno = 0;
      while (_locking(Files[isfd]->fd,_LK_LOCK ,1L) && errno == EDEADLOCK)
         errno = 0;
      Files[isfd]->errorCode = errno;
      if (Files[isfd]->errorCode == 0) {
         Files[isfd]->headerLock = ISWRLCK;
         origSigInt = signal (SIGINT, SIG_IGN);
         origSigTerm = signal (SIGTERM, SIG_IGN);
      }
   }
}

static void
_pI__headerUnlock (int isfd)
{
   if (Files[isfd]->headerLock != ISNOLCK) {
      if (Files[isfd]->headerLock == ISWRLCK) {
         signal (SIGINT, origSigInt);
         signal (SIGTERM, origSigTerm);
      }
      lseek (Files[isfd]->fd, 0L, 0);
      if (_locking(Files[isfd]->fd, _LK_UNLCK, 1L))
         Files[isfd]->errorCode = errno;
      Files[isfd]->headerLock = ISNOLCK;
   }
}

# else

static void
_pI__headerLock (int isfd, ISBOOL wlck)
{
   if (Files[isfd]->headerLock == ISNOLCK || 
         (Files[isfd]->headerLock == ISRDLCK && wlck)) {
      struct flock lck;
      lck.l_whence = 0;
      lck.l_start = 0;
      lck.l_len = 1;
      lck.l_type = wlck ? F_WRLCK : F_RDLCK;
      if (fcntl(Files[isfd]->fd, F_SETLKW, &lck) == -1) {
         Files[isfd]->errorCode = errno;
      } else if (wlck) {
         origSigInt = signal (SIGINT, SIG_IGN);
         origSigQuit = signal (SIGQUIT, SIG_IGN);
         origSigTerm = signal (SIGTERM, SIG_IGN);
         Files[isfd]->headerLock =  ISWRLCK;
      } else {
         Files[isfd]->headerLock =  ISRDLCK;
      }
   }
}

static void
_pI__headerUnlock (int isfd)
{
   if (Files[isfd]->headerLock != ISNOLCK) {
      struct flock lck;
      lck.l_whence = 0;
      lck.l_start = 0;
      lck.l_len = 1;
      lck.l_type = F_UNLCK;
      if (Files[isfd]->headerLock == ISWRLCK) {
         signal (SIGINT, origSigInt);
         signal (SIGQUIT, origSigQuit);
         signal (SIGTERM, origSigTerm);
      }
      if (fcntl(Files[isfd]->fd, F_SETLK, &lck) == -1) {
         Files[isfd]->errorCode = errno;
      }
      Files[isfd]->headerLock = ISNOLCK;
   }
}

# endif

static int
_pI__open (char *fileName, int openMode)
{
   register int i;
   int Return = -1;
   errno = ETOOMANY;
 
   for (i = 0; i < MAXOPENFILE; i++)
      if (Files[i] == 0) {
         Files[i] = FileDesc_new (fileName);
# if defined(MSDOS) && !defined(WIN32)
         if (openMode & O_CREAT) {
            if (access (Files[i]->name, 0) != 0) {
               if ((Files[i]->fd = _lcreat(Files[i]->name, 0)) < 0) {
                  FileDesc_delete (Files[i]);
                  Files[i] = 0;
               } else {
                  Return = i;
                  errno = 0;
               }    
            } else { 
               _pI__freeFile (i);
               errno = EEXIST;
            }
         } else
#endif
         if ((Files[i]->fd=open(Files[i]->name, openMode, 0666)) < 0) {
            FileDesc_delete (Files[i]);
            Files[i] = 0;
            if (errno == 0)
               errno = EMISSINGFILE;
         } else {
            Return = i;
            errno = 0;
         }
         break;
      }
   return Return;
}

static ISBOOL
_pI__writeHeader (FileDesc *fd)
{
   ISBOOL Return;
   unsigned char * headerArea = malloc (MIN_HEADER_SIZE);
   unsigned char * pnt = headerArea;

   memset(headerArea, 0, MIN_HEADER_SIZE);

/* 0-5 Signature */
   memcpy (pnt, SIGN, SIGN_LEN);
   pnt += SIGN_LEN;
/* 6-7 Release */
   putShortInBuffer(pnt,fd->releaseLevel);

/* 8-11 first free block */
   putLongInBuffer(pnt, fd->firstFreeBlock);

/* 12-15 after end block */
   putLongInBuffer(pnt, fd->afterEndBlock);

/* 16-19 block size */
   putLongInBuffer(pnt, fd->blockSize);

/* 20-23 headerSize */
   putLongInBuffer(pnt, fd->headerSize);

   if (lseek (fd->fd, 0L, 0L) < 0 ||
       write (fd->fd,headerArea,MIN_HEADER_SIZE) != MIN_HEADER_SIZE) {
      fd->errorCode = errno;
      Return = ISFALSE;
   } else
      Return = ISTRUE;
   free (headerArea);
   return Return;
}

static ISBOOL
_pI__readHeader (int isfd, ISBOOL wlck)
{
   ISBOOL Return;
   unsigned char * headerArea = malloc (MIN_HEADER_SIZE);
   unsigned char *pnt = headerArea;

   _pI__headerLock (isfd, wlck);
   if (lseek (Files[isfd]->fd, 0L, 0L) < 0 ||
       read (Files[isfd]->fd, headerArea, MIN_HEADER_SIZE) != MIN_HEADER_SIZE) {
      if (errno == 0)
         Files[isfd]->errorCode = ENOREC;
      else
         Files[isfd]->errorCode = errno;
      Return = ISFALSE;
   } else if (memcmp(pnt, SIGN, SIGN_LEN) != 0) { /* 0-5 Signature */ 
      Files[isfd]->errorCode = EBADFILE;
      Return = ISFALSE;
   } else {
      pnt += SIGN_LEN;
/* 6-7 Release */
      getShortFromBuffer(Files[isfd]->releaseLevel, pnt);

/* 8-11 first free block */
      getLongFromBuffer(Files[isfd]->firstFreeBlock, pnt);

/* 12-15 file after end block */
      getLongFromBuffer(Files[isfd]->afterEndBlock, pnt);

/* 16-19 block size */
      getLongFromBuffer(Files[isfd]->blockSize, pnt);

/* 20-23 header size */
      getLongFromBuffer(Files[isfd]->headerSize, pnt);
      if (Files[isfd]->releaseLevel != 0x0100) {
         errno = EBADFILE;
         Return = ISFALSE;
      } else {
         Return = ISTRUE;
      }
   }
   free (headerArea);
   return Return;
}

static ADDR
_pI__calcAddr (FileDesc *fd, UINT4 blockNum)
{
   return (blockNum - 1) * fd->blockSize + fd->headerSize;
}

static int
_pI__deleteBlocks (FileDesc *fd, UINT4 blkNum, UINT4 blobLen, UCHAR *block)
{
   int Return = BLOB_SUCCESS;
   unsigned char *pntBlk;
   UINT4 used;
   UINT4 nextBlkNum = blkNum;
   ADDR addr;
   do {
      errno = 0;
      if (lseek (fd->fd, (addr = _pI__calcAddr(fd, nextBlkNum)), 0) < 0 ||
          read (fd->fd, block, fd->blockSize) != fd->blockSize) {
         if (errno == 0)
            fd->errorCode = ENOREC;
         else
            fd->errorCode = errno;
         nextBlkNum = 0;
         Return = BLOB_ERR;
      } else {
         pntBlk = block;
         switch (*pntBlk) {
         case TYPE_START_BLK:
            if (blobLen == 0) {
               pntBlk++;
               getLongFromBuffer (blobLen,pntBlk);
               getLongFromBuffer (nextBlkNum,pntBlk);
               getLongFromBuffer (used,pntBlk);
            } else {
               fd->errorCode = EBADFILE;
               nextBlkNum = 0;
               Return = BLOB_ERR;
            }
            break;
         case TYPE_CONT_BLK:
            if (blobLen > 0) {
               pntBlk++;
               getLongFromBuffer (nextBlkNum,pntBlk);
               getLongFromBuffer (used,pntBlk);
            } else {
               fd->errorCode = ENOREC;
               nextBlkNum = 0;
               Return = BLOB_ERR;
            }
            break;
         case TYPE_FREE_BLK:
         default:
            fd->errorCode = EBADFILE;
            Return = BLOB_ERR;
            break;
         }
         if (Return == BLOB_SUCCESS) {
            pntBlk = block;
            memset (pntBlk, '\0', fd->blockSize);
            *pntBlk++ = TYPE_FREE_BLK;
            putLongInBuffer (pntBlk, fd->firstFreeBlock);
            fd->firstFreeBlock = blkNum;
            if (!_pI__writeHeader (fd) ||
                lseek(fd->fd, addr, 0) < 0 ||
                write (fd->fd, block, fd->blockSize) != fd->blockSize) {
               fd->errorCode = errno;
               Return = BLOB_ERR;
               nextBlkNum = 0;
            } else {
               blkNum = nextBlkNum;
            }
         }
      }
   } while (nextBlkNum > 0);
   return Return;
}

static UINT4
_pI__writeBlocks (FileDesc *fd, UCHAR *blob, UINT4 blkNum,
                  UINT4 size, UCHAR *block, ISBOOL isWrite)
{
   UINT4 toWrite = size;
   UCHAR *pntBlob = blob;
   UCHAR *pntBlk = block;
   UINT4 thisBlock;
   UINT4 blockOcc;
   UINT4 oldBlobSize = 0;
   UINT4 usableBlkSize = fd->blockSize - 1 - sizeof(UINT4) -
                                             sizeof(ADDR) - sizeof(UINT4);
   UINT4 Return = 0;

   thisBlock = blkNum;
   do {
      if (thisBlock == fd->afterEndBlock) {
         blkNum = fd->firstFreeBlock = ++fd->afterEndBlock;
      } else if (lseek (fd->fd, _pI__calcAddr(fd, thisBlock), 0) < 0 ||
         read (fd->fd, block, fd->blockSize) != fd->blockSize) {
         if (errno == 0)
            fd->errorCode = ENOREC;
         else
            fd->errorCode = errno;
         toWrite = 0;
         Return = 0;
      } else {
         pntBlk = block;
         switch (*pntBlk) {
         case TYPE_START_BLK:
            if (Return == 0) {
               pntBlk++;
               getLongFromBuffer (oldBlobSize,pntBlk);
               getLongFromBuffer (blkNum,pntBlk);
            } else {
               fd->errorCode = EBADFILE;
               toWrite = 0;
               Return = 0;
            }
            break;
         case TYPE_CONT_BLK:
            if (Return > 0) {
               pntBlk++;
               getLongFromBuffer (blkNum,pntBlk);
            } else {
               fd->errorCode = ENOREC;
               toWrite = 0;
               Return = 0;
            }
            break;
         case TYPE_FREE_BLK:
            if (Return > 0 || isWrite) {
               pntBlk++;
               getLongFromBuffer (blkNum,pntBlk);
               fd->firstFreeBlock = blkNum;
            } else {
               fd->errorCode = ENOREC;
               toWrite = 0;
               Return = 0;
            }
            break;
         default:
            fd->errorCode = EBADFILE;
            Return = 0;
            toWrite = 0;
            break;
         }
      }
      if (toWrite > 0) {
         pntBlk = block;
         memset (block, 0, fd->blockSize);
         if (Return == 0) {
            Return = thisBlock;
            *pntBlk++ = TYPE_START_BLK;
            putLongInBuffer(pntBlk, size);
         } else {
            *pntBlk++ = TYPE_CONT_BLK;
         }
         if (blkNum == 0)
            blkNum = fd->firstFreeBlock;
         if (toWrite <= usableBlkSize)
            putLongInBuffer(pntBlk, 0);
         else
            putLongInBuffer(pntBlk, blkNum);
         blockOcc = toWrite < usableBlkSize ? toWrite : usableBlkSize;
         putLongInBuffer(pntBlk, blockOcc);
         memcpy (pntBlk, pntBlob, blockOcc);
         if (lseek(fd->fd, _pI__calcAddr(fd, thisBlock), 0) < 0 ||
             write (fd->fd, block, fd->blockSize) != fd->blockSize) {
            fd->errorCode = errno;
            toWrite = 0;
            Return = 0;
         } else {
            toWrite -= blockOcc;
            pntBlob += blockOcc;
            thisBlock = blkNum;
         }
         usableBlkSize = fd->blockSize - 1 - sizeof(UINT4) - sizeof(ADDR);
      }
   } while (toWrite > 0);
   if (Return > 0) {
      if (thisBlock != fd->firstFreeBlock) {
         if (_pI__deleteBlocks (fd,thisBlock,oldBlobSize,block)==BLOB_ERR){
            Return = 0;
         }
      } else if ( !_pI__writeHeader (fd)) {
         fd->errorCode = errno;
         Return = 0;
      }
   }

   return Return;
}

int
pBerrno (int blfd)
{
   int Return = BLOB_ERR;

   checkIsOpen (blfd);
   Return = Files[blfd]->errorCode;
   return Return;
}

int
pBclose (int isfd)
{
   int Return = BLOB_ERR;
   checkIsOpen(isfd);
   close (Files[isfd]->fd);
   FileDesc_delete (Files[isfd]);
   Files[isfd] = 0;
   Return = BLOB_SUCCESS;
   return Return;
}

int
pBbuild (char *fileName, unsigned long blockSize, unsigned long headerSize)
{
   int Return = BLOB_ERR;

   if (!_pI__checkFileName (fileName))
      return Return;

   if ((Return =_pI__open(fileName,_pI__mapOpenMode(ISINOUT)|O_CREAT|O_EXCL))<0)
      return Return;

   if (blockSize < MIN_BLK_SIZE)
      blockSize = DEF_BLK_SIZE;

   if (headerSize < MIN_BLK_SIZE)
      headerSize = blockSize;
   
   Files[Return]->firstFreeBlock = 1;
   Files[Return]->afterEndBlock = 1;
   Files[Return]->blockSize = blockSize;
   Files[Return]->headerSize = headerSize;
   Files[Return]->openMode = ISINOUT;
   Files[Return]->releaseLevel = RELEASE;
   Files[Return]->errorCode = 0;

   if (_pI__writeHeader (Files[Return]) == ISFALSE) {
      int myErrno = errno;
      pBclose (Return);
      errno = myErrno;
      Return = BLOB_ERR;
   }
   return Return;
}

int
pBopen (char *fileName, int mode)
{
   int Return = BLOB_ERR;

   if (!_pI__checkFileName (fileName))
      return Return;

   checkMode (mode);
   if ((Return =_pI__open(fileName,_pI__mapOpenMode(mode))) < 0)
      return Return;
   Files[Return]->openMode = mode;

   if (_pI__readHeader (Return, ISFALSE) == ISFALSE) {
      int myErrno = errno;
      pBclose (Return);
      errno = myErrno;
      Return = BLOB_ERR;
   }
   _pI__headerUnlock (Return);
   return Return;
}

unsigned long
pBwrite (int blfd, char *blob, unsigned long size)
{
   unsigned long Return = 0;
   FileDesc *fd;

   checkIsOpen (blfd);
   fd = Files[blfd];
   fd->errorCode = 0;
   if (size == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (fd->openMode == ISINPUT) {
      fd->errorCode = EBADOPENMODE;
      return Return;
   }
   if (blob == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (_pI__readHeader (blfd, ISTRUE)) {
      UCHAR *block = malloc (fd->blockSize);
      Return=_pI__writeBlocks(fd, blob, fd->firstFreeBlock, size, block,ISTRUE);
      if (Return > 0 && !_pI__writeHeader (fd)) {
         fd->errorCode = errno;
         Return = 0;
      }
      free (block);
      _pI__headerUnlock (blfd);
   }
   return Return;
}

unsigned long
pBread (int blfd, unsigned long blkNum, char **blob)
{
   unsigned long Return = 0;
   FileDesc *fd;

   checkIsOpen (blfd);
   fd = Files[blfd];
   fd->errorCode = 0;
   if (blkNum == 0 || blob == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (_pI__readHeader (blfd, ISFALSE)) {
      char *block = malloc (fd->blockSize);
      char *pntBlob = 0;
      unsigned char *pntBlk;
      UINT4 used = 0;

      *blob = 0;
      do {
         errno = 0;
         if (lseek (fd->fd, _pI__calcAddr(fd, blkNum), 0) < 0 ||
             read (fd->fd, block, fd->blockSize) != fd->blockSize) {
            blkNum = 0;
            if (errno == 0)
               fd->errorCode = ENOREC;
            else
               fd->errorCode = errno;
            Return = 0;
         } else {
            pntBlk = block;
            if (*blob == 0) {
               if (*pntBlk++ != TYPE_START_BLK) {
                  blkNum = 0;
                  if (*(--pntBlk) == TYPE_CONT_BLK)
                     fd->errorCode = ENOREC;
                  else
                     fd->errorCode = EBADFILE;
                  Return = 0;
               } else {
                  getLongFromBuffer (Return,pntBlk);
                  getLongFromBuffer (blkNum,pntBlk);
                  getLongFromBuffer (used,pntBlk);
                  pntBlob = *blob = malloc (Return + 1);
                  pntBlob[Return] = 0;
               }
            } else {
               if (*pntBlk++ != TYPE_CONT_BLK) {
                  blkNum = 0;
                  fd->errorCode = EBADFILE;
                  Return = 0;
               } else {
                  getLongFromBuffer (blkNum,pntBlk);
                  getLongFromBuffer (used,pntBlk);
               }
            }
            if (Return != 0) {
               memcpy (pntBlob, pntBlk, used);
               pntBlob += used;
            }
         }
      } while (blkNum > 0);

      free (block);
      _pI__headerUnlock (blfd);
   }
   return Return;
}

int
pBdelete (int blfd, unsigned long blkNum)
{
   int Return = BLOB_ERR;
   FileDesc *fd;

   checkIsOpen (blfd);
   fd = Files[blfd];
   fd->errorCode = 0;
   if (blkNum == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (_pI__readHeader (blfd, ISTRUE)) {
      char *block = malloc (fd->blockSize);
      Return = _pI__deleteBlocks (fd, blkNum, 0, block);
      free (block);
      _pI__headerUnlock (blfd);
   }
   return Return;
}

unsigned long
pBrewrite (int blfd, unsigned long blkNum, char *blob, unsigned long size)
{
   unsigned long Return = 0;
   FileDesc *fd;

   checkIsOpen (blfd);
   fd = Files[blfd];
   fd->errorCode = 0;
   if (size == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (blkNum == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (blob == 0) {
      fd->errorCode = EBADARG;
      return Return;
   }
   if (fd->openMode == ISINPUT) {
      fd->errorCode = EBADOPENMODE;
      return Return;
   }
   if (_pI__readHeader (blfd, ISTRUE)) {
      UCHAR *block = malloc (fd->blockSize);
      Return = _pI__writeBlocks (fd, blob, blkNum, size, block, ISFALSE);
      free (block);
      _pI__headerUnlock (blfd);
   }
   return Return;
}

int
pBerase(char * fileName)
{
   char * fileNameErase;
   int Return = BLOB_ERR;
 
   if (!_pI__checkFileName (fileName))
      return Return;

   fileNameErase = malloc (strlen (fileName) + sizeof(BLOB_EXT));
   sprintf (fileNameErase, "%s%s", fileName, BLOB_EXT);
   if (unlink (fileNameErase) == 0 )
      Return = BLOB_SUCCESS;
   free (fileNameErase);
   return Return;
}


int
pBrename(char * oldName, char *newName)
{
   char * fullOldName;
   char * fullNewName;
   int Return = BLOB_ERR;
 
   if (!_pI__checkFileName (oldName) || !_pI__checkFileName (newName))
      return Return;

   fullOldName = malloc (strlen (oldName) + sizeof(BLOB_EXT));
   sprintf (fullOldName, "%s%s", oldName, BLOB_EXT);
   fullNewName = malloc (strlen (newName) + sizeof(BLOB_EXT));
   sprintf (fullNewName, "%s%s", newName, BLOB_EXT);
   if (rename (fullOldName, fullNewName) == 0 )
      Return = BLOB_SUCCESS;
   free (fullOldName);
   free (fullNewName);
   return Return;
}
