539 lines
9.4 KiB
C
539 lines
9.4 KiB
C
/****************************************************************************
|
|
* apps/system/hexed/src/bfile.c
|
|
* Buffered file control
|
|
*
|
|
* Copyright (c) 2011, B.ZaaR, All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* The names of contributors may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "bfile.h"
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/* Allocate file buffer */
|
|
|
|
static FAR void *bfallocbuf(FAR struct bfile_s *bf, long sz)
|
|
{
|
|
FAR char *buf;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate buffer */
|
|
|
|
sz = BFILE_BUF_ALIGN(sz);
|
|
if ((buf = realloc(bf->buf, sz)) == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bf->buf = buf;
|
|
|
|
/* Clear new memory */
|
|
|
|
if (sz > bf->bufsz)
|
|
{
|
|
memset(bf->buf + bf->bufsz, 0, sz - bf->bufsz);
|
|
}
|
|
|
|
bf->bufsz = sz;
|
|
return bf->buf;
|
|
}
|
|
|
|
/* Free a buffered file */
|
|
|
|
static int bffree(FAR struct bfile_s *bf)
|
|
{
|
|
if (bf == NULL)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
/* Free buffer */
|
|
|
|
if (bf->buf != NULL)
|
|
{
|
|
free(bf->buf);
|
|
}
|
|
|
|
/* Free file name */
|
|
|
|
if (bf->name != NULL)
|
|
{
|
|
free(bf->name);
|
|
}
|
|
|
|
free(bf);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/* Get file size */
|
|
|
|
long fsize(FILE * fp)
|
|
{
|
|
long off, sz;
|
|
|
|
if (fp == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
off = ftell(fp);
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
sz = ftell(fp);
|
|
fseek(fp, off, SEEK_SET);
|
|
|
|
return sz;
|
|
}
|
|
|
|
long bftruncate(FAR struct bfile_s *bf, long sz)
|
|
{
|
|
if (bf == NULL)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
/* Reopen file with truncate */
|
|
|
|
bf->fp = freopen(bf->name, "w+", bf->fp);
|
|
return bfflush(bf);
|
|
}
|
|
|
|
/* Flush buffer data to the file */
|
|
|
|
int bfflush(FAR struct bfile_s *bf)
|
|
{
|
|
ssize_t nread;
|
|
int ret = OK;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
/* Check for file changes */
|
|
|
|
if (!(bf->flags & BFILE_FL_DIRTY))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Write file */
|
|
|
|
fseek(bf->fp, 0, SEEK_SET);
|
|
nread = fwrite(bf->buf, 1, bf->size, bf->fp);
|
|
if (nread < 0)
|
|
{
|
|
int errcode = errno;
|
|
fprintf(stderr, "ERROR: Write to file failed: %d\n", errcode);
|
|
ret = -errcode;
|
|
}
|
|
else if (fwrite(bf->buf, 1, bf->size, bf->fp) == bf->size)
|
|
{
|
|
fprintf(stderr, "ERROR: Bad write size\n");
|
|
ret = -EIO;
|
|
}
|
|
else
|
|
{
|
|
bf->flags &= ~BFILE_FL_DIRTY;
|
|
ret = OK;
|
|
}
|
|
|
|
fflush(bf->fp);
|
|
return ret;
|
|
}
|
|
|
|
/* Opens a Buffered File */
|
|
|
|
FAR struct bfile_s *bfopen(char *name, char *mode)
|
|
{
|
|
FAR struct bfile_s *bf;
|
|
|
|
/* NULL file name */
|
|
|
|
if (name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate a buffered file structure */
|
|
|
|
if ((bf = malloc(sizeof(struct bfile_s))) == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
memset(bf, 0, sizeof(struct bfile_s));
|
|
|
|
/* Set file name */
|
|
|
|
if ((bf->name = malloc(strlen(name) + 1)) == NULL)
|
|
{
|
|
bffree(bf);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(bf->name, name);
|
|
|
|
/* Open file */
|
|
|
|
if ((bf->fp = fopen(bf->name, mode)) == NULL)
|
|
{
|
|
bffree(bf);
|
|
return NULL;
|
|
}
|
|
|
|
/* Set file buffer */
|
|
|
|
bf->buf = NULL;
|
|
bf->size = fsize(bf->fp);
|
|
if (bfallocbuf(bf, bf->size) == NULL)
|
|
{
|
|
bfclose(bf);
|
|
return NULL;
|
|
}
|
|
|
|
return bf;
|
|
}
|
|
|
|
/* Closes a Buffered File */
|
|
|
|
int bfclose(FAR struct bfile_s *bf)
|
|
{
|
|
int r;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
bfflush(bf);
|
|
|
|
/* Close file */
|
|
|
|
r = fclose(bf->fp);
|
|
bffree(bf);
|
|
return r;
|
|
}
|
|
|
|
/* Remove bytes from the Buffered File
|
|
*
|
|
* Moves the data from the end of the buffer, then shrinks the file
|
|
* size.
|
|
*/
|
|
|
|
long bfclip(FAR struct bfile_s *bf, long off, long sz)
|
|
{
|
|
long cnt;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Negative error */
|
|
|
|
if (off < 0 || sz <= 0)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Offset past EOF */
|
|
|
|
if (off > bf->size)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Size past EOF */
|
|
|
|
if ((off + sz) > bf->size)
|
|
{
|
|
sz = bf->size - off;
|
|
}
|
|
|
|
/* Remove from file */
|
|
|
|
cnt = bf->size - (off + sz);
|
|
memmove(bf->buf + off, bf->buf + off + sz, cnt);
|
|
|
|
bf->size -= sz;
|
|
bf->flags |= BFILE_FL_DIRTY;
|
|
|
|
memset(bf->buf + bf->size, 0, sz);
|
|
return cnt;
|
|
}
|
|
|
|
/* Insert bytes into the Buffered File
|
|
*
|
|
* Increases the file size, moves the current data and inserts the
|
|
* new data.
|
|
*/
|
|
|
|
long bfinsert(FAR struct bfile_s *bf, long off, void *mem, long sz)
|
|
{
|
|
long cnt;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Negative error */
|
|
|
|
if (off < 0 || sz <= 0)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Increase buffer size */
|
|
|
|
if (bf->bufsz < (bf->size + off + sz))
|
|
{
|
|
if (bfallocbuf(bf, bf->size + off + sz) == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
/* Move data */
|
|
|
|
if (off < bf->size)
|
|
{
|
|
cnt = bf->size - off;
|
|
memmove(bf->buf + off + sz, bf->buf + off, cnt);
|
|
}
|
|
|
|
memmove(bf->buf + off, mem, sz);
|
|
|
|
/* Increase file size */
|
|
|
|
if (off > bf->size)
|
|
{
|
|
bf->size += off - bf->size;
|
|
}
|
|
|
|
bf->size += sz;
|
|
bf->flags |= BFILE_FL_DIRTY;
|
|
return sz;
|
|
}
|
|
|
|
/* Copies bytes from src to off */
|
|
|
|
long bfcopy(FAR struct bfile_s *bf, long off, long src, long sz)
|
|
{
|
|
if (bf == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Error: Source past EOF */
|
|
|
|
if (src > bf->size)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Adjust sz to EOF */
|
|
|
|
if ((src > off) && (src + sz > bf->size))
|
|
{
|
|
sz = bf->size - src;
|
|
}
|
|
|
|
/* Adjust source/length for insert */
|
|
|
|
if (src >= off)
|
|
{
|
|
if (src + sz > bf->size)
|
|
{
|
|
sz = bf->size - src;
|
|
}
|
|
|
|
src += sz;
|
|
}
|
|
|
|
return bfinsert(bf, off, bf->buf + src, sz);
|
|
}
|
|
|
|
/* Moves bytes from src to off
|
|
*
|
|
* Moves the data from src to off then removes the data from the original src
|
|
* Moves data in chunks to save memory allocation
|
|
*/
|
|
|
|
long bfmove(FAR struct bfile_s *bf, long off, long src, long sz)
|
|
{
|
|
long adj;
|
|
long cnt;
|
|
long len;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Error: source past EOF */
|
|
|
|
if (src > bf->size)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Edjust sz to EOF */
|
|
|
|
if ((src > off) && (src + sz > bf->size))
|
|
{
|
|
sz = bf->size - src;
|
|
}
|
|
|
|
/* Adjust source/offset */
|
|
|
|
len = BFILE_BUF_MIN > sz ? sz : BFILE_BUF_MIN;
|
|
if (src > off)
|
|
{
|
|
src += len;
|
|
adj = len;
|
|
}
|
|
else
|
|
{
|
|
off += sz;
|
|
adj = 0;
|
|
}
|
|
|
|
/* Move data in chunks */
|
|
|
|
for (cnt = sz; cnt; cnt -= len)
|
|
{
|
|
/* End of count? */
|
|
|
|
if (cnt < len)
|
|
{
|
|
len = cnt;
|
|
}
|
|
|
|
/* Move data */
|
|
|
|
bfinsert(bf, off, bf->buf + src, len);
|
|
bfclip(bf, src, len);
|
|
off += adj;
|
|
}
|
|
|
|
return sz;
|
|
}
|
|
|
|
/* Read Buffered File
|
|
*
|
|
* Reads an entire file to the buffer.
|
|
*/
|
|
|
|
long bfread(FAR struct bfile_s *bf)
|
|
{
|
|
long r;
|
|
|
|
if (bf == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Check buffer size */
|
|
|
|
bf->size = fsize(bf->fp);
|
|
if (bf->size > bf->bufsz)
|
|
{
|
|
if (bfallocbuf(bf, bf->size) == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
/* Read whole file into the buffer */
|
|
|
|
if ((r = fread(bf->buf, 1, bf->size, bf->fp)) == bf->size)
|
|
{
|
|
bf->flags &= ~BFILE_FL_DIRTY;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Write Buffered File
|
|
*
|
|
* Writes data to the buffer, the buffer still needs to be flushed
|
|
* to the file before closing or reading.
|
|
*/
|
|
|
|
long bfwrite(FAR struct bfile_s *bf, long off, void *mem, long sz)
|
|
{
|
|
if (bf == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
/* Increase buffer size */
|
|
|
|
if (bf->bufsz < off + sz)
|
|
{
|
|
if (bfallocbuf(bf, off + sz) == NULL)
|
|
{
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
memmove(bf->buf + off, mem, sz);
|
|
|
|
/* Increase file size */
|
|
|
|
if (bf->size < off + sz)
|
|
{
|
|
bf->size = off + sz;
|
|
}
|
|
|
|
bf->flags |= BFILE_FL_DIRTY;
|
|
return sz;
|
|
}
|