blob: e2226e229b1a635c8f0fa1ddf9772889dbdc22c9 [file] [log] [blame]
/*
* Copyright (c) 2013-2017, Intel Corporation
*
* 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.
* * Neither the name of Intel Corporation nor the names of its contributors
* may 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.
*/
#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(const char *s)
{
size_t n, i;
char **line;
struct text *t;
t = calloc(1, sizeof(struct text));
if (!t)
return NULL;
/* If s is NULL or empty, there is nothing to do. */
if (!s || *s == '\0')
return t;
/* beginning of s is the first line. */
t->n = 1;
t->line = calloc(1, sizeof(*t->line));
if (!t->line)
goto error;
t->line[0] = duplicate_str(s);
if (!t->line[0])
goto error;
/* iterate through all chars and make \r?\n to \0. */
n = strlen(t->line[0]);
for (i = 0; i < n; i++) {
if (t->line[0][i] == '\r') {
if (i+1 >= n) {
/* the file ends with \r. */
t->line[0][i] = '\0';
break;
}
/* terminate the line string if it's a line end. */
if (t->line[0][i+1] == '\n')
t->line[0][i] = '\0';
} else if (t->line[0][i] == '\n') {
/* set newline character always to \0. */
t->line[0][i] = '\0';
if (i+1 >= n) {
/* the file ends with \n. */
break;
}
/* increase line pointer buffer. */
line = realloc(t->line, (t->n+1) * sizeof(*t->line));
if (!line)
goto error;
t->line = line;
/* point to the next character after the
* newline and increment the number of lines.
*/
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);
/* Make sure the string is terminated. */
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);
}
/* Appends the @filename to @fl and stores a pointer to the internal
* text structure in @t.
*
* Returns 0 on success; a negative enum errcode otherwise.
* Returns -err_internal if @fl or @t is the NULL pointer.
* Returns -err_file_stat if @filename could not be found.
* Returns -err_file_open if @filename could not be opened.
* Returns -err_file_read if the content of @filename could not be fully
* read.
*/
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;
}
fl->next->filename = duplicate_str(filename);
if (!fl->next->filename) {
errcode = -err_no_mem;
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); /* size + 1: space for last null byte. */
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);
errcode = -err_file_read;
goto error;
}
*t = text_alloc(s);
if (!*t) {
errcode = -err_no_mem;
goto error;
}
free(s);
fl->next->text = *t;
return 0;
error_file:
fclose(f);
error:
/* filename is closed after reading before handling error. */
fl_free(fl->next);
fl->next = NULL;
free(s);
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;
}