interpret/kjv/src/main.c

109 lines
3.3 KiB
C

/** @license 2022 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT). */
#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;
}
#define ARRAY_NAME verse
#define ARRAY_TYPE struct verse_array
#include "array.h"
struct book { struct char_array backing; struct verse_array chapter; };
int main_new_chapter(struct book *const book) {
assert(book);
return 0;
}
struct verse *main_new_verse(struct verse_array *const chapter) {
assert(chapter);
return verse_array_new(chapter);
}
int main(void) {
const char *const dir_name = "KJV";
struct book kjv[KJV_BOOK_SIZE] = { 0 };
int success = EXIT_SUCCESS;
DIR *dir = 0;
struct dirent *de = 0;
unsigned i;
errno = 0;
/* Read in the kjv from all files.
fixme: this is lazy; all one object would be best. */
if(chdir(dir_name) == -1 || !(dir = opendir("."))) goto catch;
while((de = readdir(dir))) { /* For all files in directory. */
unsigned ordinal;
enum kjv_book b;
if(!kjv_filename(de->d_name, &ordinal)) /* Extract 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(kjv[b = ordinal - 1].backing.data) /* Convert to zero-based. */
{ errno = EDOM; goto catch; } /* Duplicate. */
if(!append_file(&kjv[b].backing, de->d_name)) goto catch;
}
closedir(dir), de = 0, dir = 0;
/* Parse the files into chapters. */
for(i = 0; i < KJV_BOOK_SIZE; i++) {
if(!kjv[i].backing.data) { fprintf(stderr, "Missing book %u.\n", i + 1);
errno = EDOM; goto catch; }
/*for( ; ; ) { switch(kjv_chapter(kjv + i)) {
case KJV_ERROR: goto catch;
case KJV_DONE: goto finally;
case KJV_CHAPTER: break;
}}*/
}
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_(&kjv[i].backing), verse_array_(&kjv[i].chapter);
return success;
}