89 lines
2.7 KiB
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;
|
|
}
|