/** Dynamic contiguous string that is used to load files. */

#define ARRAY_NAME char
#define ARRAY_TYPE char
#include "../src/array.h"
#define HAVE_CHAR_ARRAY
#include "text.h"
#include <stdio.h>

/** Append a text file, `fn`, to `c`, and add a '\0'.
 @return The start of the appended file or null on error. A partial read is a
 failure. @throws[fopen, fread, malloc]
 @throws[EISEQ] The text file has embedded nulls.
 @throws[ERANGE] If the standard library does not follow POSIX. */
static char *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. */
	do if(!(cursor = char_array_buffer(text, granularity))
		|| (nread = fread(cursor, 1, granularity, fp), ferror(fp))
		|| !char_array_append(text, nread)) 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;
}

struct text text(void) { struct text text; text.a = char_array(); return text; }
void text_(struct text *const text) { char_array_(&text->a); }
char *text_append_file(struct text *text, const char *const fn)
	{ return append_file(&text->a, fn); }
