/** Dynamic contiguous string that is used to load files into a `struct char_array`. */ #include "text.h" #include #include #include #define ARRAY_NAME char #define ARRAY_TYPE char #define ARRAY_BODY #include "../src/array.h" /** @return Idle. */ struct char_array text(void) { return char_array(); } /** Destroys `text`. */ void text_(struct char_array *const text) { char_array_(text); } /** Append a text file, `fn`, to `text`, and add a '\0'. @return The start of the appended file or null on error. A partial read is a failure. @throws[fopen, fread, realloc] @throws[EISEQ] The text file has embedded nulls. */ char *text_append_file(struct char_array *text, const char *const fn) { FILE *fp = 0; const size_t granularity = 1024; size_t nread, start; char *cursor; int success = 1; assert(text && fn); start = text->size; if(!(fp = fopen(fn, "r"))) goto catch; /* Read entire file in chunks. We don't allow any text file to go over `INT_MAX` and `UINT32_MAX` because `printf("%.*s", int, char *)` is used and we store it in an `uint32_t`. */ do if(!(cursor = char_array_buffer(text, granularity)) || (nread = fread(cursor, 1, granularity, fp), ferror(fp)) || !char_array_append(text, nread) || text->size - start >= (INT_MAX < UINT32_MAX ? INT_MAX : UINT32_MAX)) goto catch; while(nread == granularity); /* File to `C` string. */ if(!(cursor = char_array_new(text))) goto catch; *cursor = '\0'; /* Binary files with embedded '\0' are not allowed; check just this read. */ if(strchr(text->data + start, '\0') != cursor) { errno = EILSEQ; goto catch; } goto finally; catch: if(!errno) errno = EILSEQ; /* Will never be true on POSIX. */ success = 0; finally: if(fp) fclose(fp); return success ? text->data + start : 0; }