#include "errcode.h"
#include "file.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
struct text *text_alloc(char *s, size_t n)
{
size_t i;
char **line;
struct text *t;
t = calloc(1, sizeof(struct text));
if (!t) {
free(s);
return NULL;
}
if (!s || *s == '\0') {
free(s);
return t;
}
t->n = 1;
t->line = calloc(1, sizeof(*t->line));
if (!t->line) {
free(s);
goto error;
}
t->line[0] = s;
for (i = 0; i < n; i++) {
if (t->line[0][i] == '\r') {
if (i+1 >= n) {
t->line[0][i] = '\0';
break;
}
if (t->line[0][i+1] == '\n') {
t->line[0][i] = '\0';
continue;
}
}
if (t->line[0][i] == '\n') {
t->line[0][i] = '\0';
if (i+1 >= n) {
break;
}
}
if (t->line[0][i] == '\0') {
line = realloc(t->line, (t->n+1) * sizeof(*t->line));
if (!line)
goto error;
t->line = line;
t->line[t->n++] = &(t->line[0][i+1]);
}
}
return t;
error:
text_free(t);
return NULL;
}
void text_free(struct text *t)
{
if (!t)
return;
if (t->line)
free(t->line[0]);
free(t->line);
free(t);
}
int text_line(const struct text *t, char *dest, size_t destlen, size_t n)
{
if (bug_on(!t))
return -err_internal;
if (bug_on(!dest && destlen))
return -err_internal;
if (n >= t->n)
return -err_out_of_range;
if (!dest)
return 0;
if (!destlen)
return -err_internal;
strncpy(dest, t->line[n], destlen);
dest[destlen-1] = '\0';
return 0;
}
struct file_list *fl_alloc(void)
{
return calloc(1, sizeof(struct file_list));
}
void fl_free(struct file_list *fl)
{
if (!fl)
return;
fl_free(fl->next);
text_free(fl->text);
free(fl->filename);
free(fl);
}
static int fl_append(struct file_list *fl, struct text **t,
const char *filename)
{
int errcode;
FILE *f;
char *s;
long pos;
size_t fsize;
size_t read;
if (bug_on(!fl))
return -err_internal;
if (bug_on(!t))
return -err_internal;
if (bug_on(!filename))
return -err_internal;
s = NULL;
*t = NULL;
while (fl->next)
fl = fl->next;
fl->next = fl_alloc();
if (!fl->next) {
errcode = -err_no_mem;
goto error;
}
errcode = duplicate_name(&fl->next->filename, filename, FILENAME_MAX);
if (errcode < 0)
goto error;
errno = 0;
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "open %s failed: %s\n",
filename, strerror(errno));
errcode = -err_file_open;
goto error;
}
errcode = fseek(f, 0, SEEK_END);
if (errcode) {
fprintf(stderr, "%s: failed to seek end: %s\n",
filename, strerror(errno));
errcode = -err_file_size;
goto error_file;
}
pos = ftell(f);
if (pos < 0) {
fprintf(stderr, "%s: failed to determine file size: %s\n",
filename, strerror(errno));
errcode = -err_file_size;
goto error_file;
}
fsize = (size_t) pos;
errcode = fseek(f, 0, SEEK_SET);
if (errcode) {
fprintf(stderr, "%s: failed to seek begin: %s\n",
filename, strerror(errno));
errcode = -err_file_size;
goto error_file;
}
s = calloc(fsize+1, 1);
if (!s) {
errcode = -err_no_mem;
goto error_file;
}
read = fread(s, 1, fsize, f);
fclose(f);
if (read != fsize) {
fprintf(stderr, "read %s failed\n", filename);
free(s);
errcode = -err_file_read;
goto error;
}
*t = text_alloc(s, fsize);
if (!*t) {
errcode = -err_no_mem;
goto error;
}
fl->next->text = *t;
return 0;
error_file:
fclose(f);
error:
fl_free(fl->next);
fl->next = NULL;
text_free(*t);
*t = NULL;
return errcode;
}
int fl_getline(struct file_list *fl, char *dest, size_t destlen,
const char *filename, size_t n)
{
int errcode;
const struct text *t;
if (bug_on(!fl))
return -err_internal;
errcode = fl_gettext(fl, &t, filename);
if (errcode < 0)
return errcode;
return text_line(t, dest, destlen, n);
}
int fl_gettext(struct file_list *fl, const struct text **t,
const char *filename)
{
struct text *tmp;
int errcode;
if (bug_on(!fl))
return -err_internal;
if (bug_on(!t))
return -err_internal;
if (bug_on(!filename))
return -err_internal;
while (fl->next) {
fl = fl->next;
if (strcmp(fl->filename, filename) == 0) {
*t = fl->text;
return 0;
}
}
errcode = fl_append(fl, &tmp, filename);
if (errcode < 0)
return errcode;
*t = tmp;
return 0;
}