#include "lex.h" #include /* chdir (POSIX) */ #include /* mode_t (POSIX) */ #include /* umask (POSIX) */ #include /* opendir readdir closedir */ #include #include #include #if INT_MAX >= 100000000000 #error int_to_string requires truncation on this compiler. #endif static void int_to_string(const int *const n, char (*const a)[12]) { sprintf(*a, "%d", *n); } #define ARRAY_NAME int #define ARRAY_TYPE int #define ARRAY_EXPECT_TRAIT #include "../src/array.h" #define ARRAY_TO_STRING &int_to_string #include "../src/array.h" static int int_cmp(const int *const a, const int *const b) { return (*b < *a) - (*a < *b); } static int void_int_cmp(const void *const a, const void *const b) { return int_cmp(a, b); } #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)) success = 0; return success; } int main(int argc, char **argv) { int success = EXIT_FAILURE; DIR *dir = 0; struct dirent *de; struct int_array years = ARRAY_IDLE, months = ARRAY_IDLE, days = ARRAY_IDLE; struct char_array entry = ARRAY_IDLE; int *y, *y_end; /* Get the years list as directories matching a year in order. */ errno = 0; if(argc != 2) { fprintf(stderr, "Needs journal location.\n" "(should contain //.txt)\n"); goto finally; } if(chdir(argv[1]) == -1 || !(dir = opendir("."))) goto catch; while((de = readdir(dir))) { struct stat st; int year, *p; if(!lex_looks_like_year(de->d_name, &year)) continue; if(stat(de->d_name, &st)) goto catch; if(!S_ISDIR(st.st_mode)) continue; if(!(p = int_array_new(&years))) goto catch; *p = year; } closedir(dir), dir = 0; qsort(years.data, years.size, sizeof *years.data, &void_int_cmp); fprintf(stderr, "%s: %s.\n", argv[1], int_array_to_string(&years)); /* Go though each year. */ for(y = years.data, y_end = y + years.size; y < y_end; y++) { char temp[64]; int *m, *m_end; sprintf(temp, "%d", *y); /* Get the months as directories. */ if(chdir(temp) == -1 || !(dir = opendir("."))) goto catch; while((de = readdir(dir))) { struct stat st; int month, *p; if(!(month = lex_looks_like_month(de->d_name))) continue; if(stat(de->d_name, &st)) goto catch; if(!S_ISDIR(st.st_mode)) continue; if(!(p = int_array_new(&months))) goto catch; *p = month; } closedir(dir), dir = 0; qsort(months.data, months.size, sizeof *months.data, &void_int_cmp); fprintf(stderr, "%s: %s.\n", temp, int_array_to_string(&months)); /* Go though each month. */ for(m = months.data, m_end = m + months.size; m < m_end; m++) { int *d, *d_end; sprintf(temp, "%.2d", *m); /* Get the days as files. */ if(chdir(temp) == -1 || !(dir = opendir("."))) goto catch; while((de = readdir(dir))) { struct stat st; int day, *p; /* fixme: Have yyyy-mm-dd to figure out how many days. */ if(!(day = lex_looks_like_day(de->d_name))) continue; if(stat(de->d_name, &st)) goto catch; if(S_ISDIR(st.st_mode)) continue; if(!(p = int_array_new(&days))) goto catch; *p = day; } closedir(dir), dir = 0; qsort(days.data, days.size, sizeof *days.data, &void_int_cmp); fprintf(stderr, "%s: %s.\n", temp, int_array_to_string(&days)); for(d = days.data, d_end = d + days.size; d < d_end; d++) { struct lex lex; printf("%d-%.2d-%.2d\n", *y, *m, *d); sprintf(temp, "%.2d.txt", *d); if(!append_file(&entry, temp)) goto catch; printf("%s", entry.data); printf("Lexing:\n"); lex_reset(entry.data); while(lex_next(&lex)) { printf("%lu: %s", (unsigned long)lex.line, lex_symbols[lex.symbol]); if(lex.symbol == TEXT || lex.symbol == ARG_KEYWORD || lex.symbol == ARG_DATE || lex.symbol == ARG_FREEFORM) { if(lex.s0 + INT_MAX < lex.s1) { errno = EILSEQ; goto catch; } printf(" <<%.*s>>", (int)(lex.s1 - lex.s0), lex.s0); } printf(".\n"); } printf("Lexing finished: %s on %lu.\n", lex_symbols[lex.symbol], lex.line); char_array_clear(&entry); break; /* fixme */ } int_array_clear(&days); if(chdir("..") == -1) goto catch; break; /* fixme */ } int_array_clear(&months); if(chdir("..") == -1) goto catch; break; /* fixme */ } { success = EXIT_SUCCESS; goto finally; } catch: perror("interpret"); finally: if(dir && closedir(dir)) success = EXIT_FAILURE, perror("dir"); int_array_(&years); int_array_(&months); return EXIT_FAILURE; }