/** @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 #include #include #include #include /* opendir readdir closedir */ #include /* 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; }