/*
* texfile.c - a text file reader
* Copyright (C) 2010 Martin Broadhurst
* www.martinbroadhurst.com
*/
#include <stdlib.h>
#include <string.h>
#include "textfile.h"
#define BUFSIZE 128
MBtextfile * MBtextfile_create(FILE *fptr)
{
MBtextfile * file = malloc(sizeof(MBtextfile));
if (file) {
file->fptr = fptr;
file->inbuf = malloc(BUFSIZE);
file->inbufsize = BUFSIZE;
file->outbuf = NULL;
file->outbufsize = 0;
file->count = 0;
file->eof = 0;
}
return file;
}
MBtextfile * MBtextfile_open(const char *filename)
{
MBtextfile *file = NULL;
FILE *fptr;
if ((fptr = fopen(filename, "rt")) != NULL) {
file = MBtextfile_create(fptr);
}
return file;
}
void MBtextfile_delete(MBtextfile *file)
{
if (file) {
free(file->inbuf);
free(file->outbuf);
free(file);
}
}
void MBtextfile_close(MBtextfile *file)
{
fclose(file->fptr);
MBtextfile_delete(file);
}
static void *reallocate(void **buffer, size_t size)
{
void *temp = realloc(*buffer, size);
if (temp) {
*buffer = temp;
}
else {
free(*buffer);
*buffer = NULL;
}
return *buffer;
}
const char *MBtextfile_read(MBtextfile *file)
{
unsigned int eof = 0; /* At end of file */
ssize_t cr = -1; /* Position of first cr/lf character */
size_t lf = 0; /* Position of the matching cr/lf character */
if (file->eof) {
return NULL;
}
while (cr == -1) {
unsigned long i;
/* Find the first cr/lf character */
for (i = 0; i < file->count && cr == -1; i++) {
if (file->inbuf[i] == 0x0A) {
cr = i;
lf = cr;
/* And its partner, if any */
if (i < file->count && file->inbuf[i + 1] == 0x0D) {
lf++;
}
}
}
if (cr == -1 && !eof) {
/* No cr/lf yet, and more bytes to read in the file */
size_t bytes;
size_t len;
if (file->count == file->inbufsize) {
/* Expand the in-buffer */
file->inbufsize *= 2;
if (!reallocate((void*)&file->inbuf, file->inbufsize)) {
return NULL;
}
}
len = file->inbufsize - file->count;
/* Read into the in-buffer at the end of the data already there */
bytes = fread(file->inbuf + file->count, 1, len, file->fptr);
file->count += bytes;
eof = bytes < len;
}
if (eof && cr == -1) {
/* End of file, and no cr/lf */
file->eof = 1;
cr = (ssize_t)file->count; /* To set the out-buffer size */
}
}
if ((size_t)(cr + 1) > file->outbufsize) {
/* Expand the out-buffer */
file->outbufsize = cr + 1;
if (!reallocate((void*)&file->outbuf, file->outbufsize)) {
return NULL;
}
}
/* Copy the line to the out-buffer and nul-terminate it */
memcpy(file->outbuf, file->inbuf, cr);
file->outbuf[cr] = '\0';
if (!file->eof) {
/* Move the remainder to the front of the in-buffer */
size_t len = file->count - (lf + 1);
memmove(file->inbuf, file->inbuf + lf + 1, len);
file->count = len;
}
return file->outbuf;
}