interpret/kjv/src/main.c

89 lines
2.7 KiB
C

/** @license 20xx Neil Edelman, distributed under the terms of the
[GNU General Public License 3](https://opensource.org/licenses/GPL-3.0).
@license 20xx Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
This is a standard C file.
@std C89 */
#include "kjv.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <dirent.h> /* opendir readdir closedir */
#include <unistd.h> /* chdir (POSIX) (because I'm lazy) */
#define ARRAY_NAME char
#define ARRAY_TYPE char
#include "array.h"
/** Append a text file, `fn`, to `c`, and add a '\0'.
@return Success. A partial read is failure. @throws[fopen, fread, malloc]
@throws[EISEQ] The text file has embedded nulls.
@throws[ERANGE] If the standard library does not follow POSIX. */
static int append_file(struct char_array *c, const char *const fn) {
FILE *fp = 0;
const size_t granularity = 1024;
size_t nread;
char *cursor;
int success = 0;
assert(c && fn);
if(!(fp = fopen(fn, "r"))) goto catch;
/* Read entire file in chunks. */
do if(!(cursor = char_array_buffer(c, granularity))
|| (nread = fread(cursor, 1, granularity, fp), ferror(fp))
|| !char_array_append(c, nread)) goto catch;
while(nread == granularity);
/* File to `C` string. */
if(!(cursor = char_array_new(c))) goto catch;
*cursor = '\0';
/* Binary files with embedded '\0' are not allowed. */
if(strchr(c->data, '\0') != cursor) { errno = EILSEQ; goto catch; }
{ success = 1; goto finally; }
catch:
if(!errno) errno = EILSEQ; /* Will never be true on POSIX. */
finally:
if(fp) fclose(fp);
return success;
}
int main(void) {
const char *const dir_name = "KJV";
int success = EXIT_SUCCESS;
DIR *dir = 0;
struct dirent *de = 0;
struct char_array book[KJV_BOOK_SIZE] = { 0 };
unsigned i;
errno = 0;
/* Read all files in `dir_name`. */
if(chdir(dir_name) == -1 || !(dir = opendir("."))) goto catch;
while((de = readdir(dir))) {
unsigned ordinal;
enum kjv_book b;
if(!kjv_filename(de->d_name, &ordinal))
{ fprintf(stderr, "Ignored <%s>.\n", de->d_name); continue; }
printf("<%s> ordinal: %u\n", de->d_name, ordinal);
if(ordinal < 1 || ordinal > KJV_BOOK_SIZE)
{ errno = ERANGE; goto catch; } /* Not in range. */
if(book[b = ordinal - 1].data)
{ errno = EDOM; goto catch; } /* Duplicate. */
if(!append_file(book + b, de->d_name)) goto catch;
}
closedir(dir), de = 0, dir = 0;
for(i = 0; i < KJV_BOOK_SIZE; i++)
if(!book[i].data) { errno = EDOM; goto catch; } /* Not there. */
/**/
goto finally;
catch:
success = EXIT_FAILURE;
perror(de ? de->d_name : dir_name);
if(dir && closedir(dir)) perror(dir_name);
finally:
for(i = 0; i < KJV_BOOK_SIZE; i++) char_array_(&book[i]);
return success;
}