| /* |
| * 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; |
| } |