diff --git a/config b/config index 17427e6..f535588 100644 --- a/config +++ b/config @@ -32,3 +32,8 @@ source "scripts/paths" # Project-specific variables below. +[ -z "${LIBISO8601_CFLAGS}" ] && LIBISO8601_CFLAGS="$(libiso8601-config --cflags)" +[ -z "${LIBISO8601_LIBS}" ] && LIBISO8601_LIBS="$(libiso8601-config --libs)" + +[ -z "${CC}" ] && CC="gcc" +[ -z "${CFLAGS}" ] && CFLAGS="-g -O2 -W -Wall" diff --git a/src/leaptable-compiler/.params b/src/leaptable-compiler/.params new file mode 100644 index 0000000..695dce4 --- /dev/null +++ b/src/leaptable-compiler/.params @@ -0,0 +1 @@ +app c leaptable-compiler bin diff --git a/src/leaptable-compiler/000_TopSource.c b/src/leaptable-compiler/000_TopSource.c new file mode 100644 index 0000000..57f77d8 --- /dev/null +++ b/src/leaptable-compiler/000_TopSource.c @@ -0,0 +1,349 @@ +/* leaptable-compiler/src/leaptable-compiler/000_TopSource.c + * + * (c)2009, Laurence Withers, . + * Released under the GNU GPLv3. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + +/* Below are all the includes used throughout the application. */ +#include +#include +#include +#include +#include +#include + +#include "iso8601.h" + + + +const char* signature_string = "/O9PdPZI"; +struct iso8601_details print_details = { + .date_prec = iso8601_prec_day, + .time_prec = iso8601_prec_none, + .extended = 1, +}; + +const char* text_fname = 0, * table_fname = 0; + +FILE* do_open(const char* fname, const char* mode) +{ + FILE* fp; + + if(!fname) { + if(mode[0] == 'r') return stdin; + fflush(stdout); + return stdout; + } + + fp = fopen(fname, mode); + if(!fp) { + fprintf(stderr, "Unable to open \"%s\": %m.\n", fname); + exit(1); + } + return fp; +} + + + +struct day_list { + int* days; + int num, size; +}; + +struct day_list* day_list_new(void) +{ + struct day_list* l; + + l = malloc(sizeof(struct day_list)); + l->days = malloc(sizeof(int) * 8); + l->num = 0; + l->size = 8; + + return l; +} + +void day_list_add(struct day_list* l, int day) +{ + if(l->num == l->size) { + l->size <<= 1; + l->days = realloc(l->days, l->size * sizeof(int)); + } + + l->days[l->num++] = day; +} + +int _day_list_sort_cmp(const void* v1, const void* v2) +{ + const int* i1, * i2; + i1 = v1; + i2 = v2; + if(*i1 < *i2) return -1; + if(*i1 > *i2) return 1; + return 0; +} + +void day_list_sort(struct day_list* dl) +{ + qsort(dl->days, dl->num, sizeof(int), _day_list_sort_cmp); +} + + + +void write_i32(char* buf, uint32_t val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; +} + +uint32_t read_i32(const char* buf) +{ + const uint8_t* b; + b = (const uint8_t*)buf; + + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; +} + + + +int do_compile(void) +{ + FILE* fin, * fout; + char* line = 0; + size_t line_sz = 0; + int lineno = 0, year, month, day; + struct iso8601_date d; + struct day_list* dl; + char buf[4]; + + fin = do_open(text_fname, "r"); + dl = day_list_new(); + + while(getline(&line, &line_sz, fin) != -1) { + ++lineno; + if(iso8601_parse(line, &d, 0, 0)) { + fprintf(stderr, "%s:%d: unable to parse ISO8601 date.\n", + text_fname ?: "stdin", lineno); + return 1; + } + + day_list_add(dl, d.day); + iso8601_to_cal(&year, &month, &day, &d); + if(!(month == 6 && day == 30) && !(month == 12 && day == 31)) { + fprintf(stderr, "%s:%d: warning: date (%04d-%02d-%02d) does not " + "correspond to a usual leap second day.\n", + text_fname ?: "stdin", lineno, year, month, day); + } + } + + day_list_sort(dl); + + fout = do_open(table_fname, "w"); + fwrite(signature_string, 1, 8, fout); + write_i32(buf, dl->num); + fwrite(buf, 1, 4, fout); + for(lineno = 0; lineno < dl->num; ++lineno) { + write_i32(buf, dl->days[lineno]); + fwrite(buf, 1, 4, fout); + } + + return 0; +} + + + +int do_dump(void) +{ + FILE* fin, * fout; + char buf[32]; + int num, i; + struct iso8601_date d = { + .sec = 0, + .nsec = 0, + }; + + fin = do_open(table_fname, "r"); + + if(fread(buf, 1, 8, fin) != 8) goto read_err; + if(memcmp(buf, signature_string, 8)) { + fprintf(stderr, "%s: invalid signature.\n", + table_fname ?: "stdin"); + return 1; + } + + if(fread(buf, 1, 4, fin) != 4) goto read_err; + num = read_i32(buf); + + fout = do_open(text_fname, "w"); + for(i = 0; i < num; ++i) { + if(fread(buf, 1, 4, fin) != 4) goto read_err; + d.day = read_i32(buf); + fputs(iso8601_print(buf, sizeof(buf), &d, &print_details), fout); + putc('\n', fout); + } + + return 0; + + read_err: + fprintf(stderr, "%s: error reading from file (truncated or %m).\n", + table_fname ?: "stdin"); + return 1; +} + + + +int do_verify(void) +{ + FILE* fin; + char buf[8]; + int i, num, day; + struct day_list* dl; + + char s1[32], s2[32]; + struct iso8601_date d1, d2; + + if(!table_fname) { + fputs("Verify mode cannot work with stdin.\n", stderr); + return 1; + } + + if(iso8601_leap_table_load(table_fname)) { + fprintf(stderr, "%s: unable to load table (%m).\n", table_fname); + return 1; + } + + fputs("Table loaded by library.\n", stdout); + fin = do_open(table_fname, "r"); + dl = day_list_new(); + + if(fread(buf, 1, 8, fin) != 8) goto read_err; + if(memcmp(buf, signature_string, 8)) { + fprintf(stderr, "%s: invalid signature.\n", table_fname); + return 1; + } + + if(fread(buf, 1, 4, fin) != 4) goto read_err; + num = read_i32(buf); + + for(i = 0; i < num; ++i) { + if(fread(buf, 1, 4, fin) != 4) goto read_err; + day = read_i32(buf); + day_list_add(dl, day); + } + printf("Loaded %d entries from disk.\n", num); + + d1.sec = 0; + d1.nsec = 0; + d1.day = dl->days[0] - 1; + iso8601_print(s1, sizeof(s1), &d1, &print_details); + d2.sec = 0; + d2.nsec = 0; + d2.day = dl->days[dl->num - 1] + 1; + iso8601_print(s2, sizeof(s2), &d2, &print_details); + + num = iso8601_leap_elapsed(&d1, &d2); + + printf("Number of leap seconds elapsed between %s and %s: %d (%sexpected).\n", + s1, s2, num, (num == dl->num) ? "" : "un"); + if(num != dl->num) return 1; + + fputs("Scanning days.\n", stdout); + for(num = 0; d1.day < d2.day; ++d1.day) { + if(iso8601_seconds_leap(&d1) == 86400) continue; + iso8601_print(s1, sizeof(s1), &d1, &print_details); + fputs("Found leap second at ", stdout); + fputs(s1, stdout); + + if(num == dl->num) { + fputs("(unexpected -- past end of table)\n", stdout); + return 1; + } + + if(dl->days[num] != d1.day) { + fputs("(unexpected -- table's next leap at ", stdout); + d1.day = dl->days[num]; + iso8601_print(s1, sizeof(s1), &d1, &print_details); + fputs(s1, stdout); + fputs(")\n", stdout); + return 1; + } + + fputs("(expected).\n", stdout); + ++num; + } + + return 0; + + read_err: + fprintf(stderr, "%s: error reading from file (truncated or %m).\n", + table_fname ?: "stdin"); + return 1; +} + + + +void usage(void) +{ + fputs("Usage:\n\n" + " leaptable-compiler MODE [options]\n\n" + "Modes:\n" + " compile Compile text file to table file.\n" + " dump Dump table file as text.\n" + " verify Verify correct operation of table file.\n" + "\n" + "Options:\n" + " -h, --help Display this screen.\n" + " -V, --version Display version number.\n" + " -x, --text Name of text file.\n" + " -t, --table Name of table file.\n" + "", stdout); +} + + + +struct option options[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { "text", required_argument, 0, 'x' }, + { "table", required_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + +const char* optstr = "ht:x:V"; + + + +int main(int argc, char* argv[]) +{ + while(1) { + switch(getopt_long(argc, argv, optstr, options, 0)) { + case -1: goto opts_done; + case '?': return 1; + case 'h': usage(); return 0; + case 'V': fputs("leaptable-compiler " VERSION "\n", stdout); return 0; + case 'x': text_fname = optarg; break; + case 't': table_fname = optarg; break; + } + } + opts_done: + if(optind != (argc - 1)) { + usage(); + return 1; + } + + if(!strcmp(argv[optind], "compile")) return do_compile(); + if(!strcmp(argv[optind], "dump")) return do_dump(); + if(!strcmp(argv[optind], "verify")) return do_verify(); + + fputs("Unknown mode.\n", stderr); + return 1; +} + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/leaptable-compiler/build.app b/src/leaptable-compiler/build.app new file mode 100644 index 0000000..bccc580 --- /dev/null +++ b/src/leaptable-compiler/build.app @@ -0,0 +1,41 @@ +# These are external variables, and shouldn't clash with anything else +# leaptable_compiler +# leaptable_compiler_BUILT +# + +if [ -z ${leaptable_compiler_BUILT} ] +then + leaptable_compiler="obj/leaptable-compiler" + EXTRAS="-std=gnu99 -D_GNU_SOURCE ${LIBISO8601_CFLAGS} ${LIBISO8601_LIBS}" + + echo "Building application ${leaptable_compiler}..." + + do_cmd source src/leaptable-compiler/build.monolithic || return 1 + + MODIFIED=0 + for test in ${MONOLITHIC_TESTS} ${SRC} + do + if [ ${test} -nt ${leaptable_compiler} ] + then + MODIFIED=1 + break + fi + done + + if [ ${MODIFIED} -ne 0 ] + then + echo " Compiling..." + + do_cmd ${CC} ${CFLAGS} -I obj -o "${leaptable_compiler}" ${SRC} ${EXTRAS} || return 1 + + print_success "Application built" + else + print_success "Application up to date" + fi + + leaptable_compiler_BUILT=1 + +fi + +# kate: replace-trailing-space-save true; space-indent true; tab-width 4; +# vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/src/leaptable-compiler/build.default b/src/leaptable-compiler/build.default new file mode 100644 index 0000000..bc020d0 --- /dev/null +++ b/src/leaptable-compiler/build.default @@ -0,0 +1 @@ +source src/leaptable-compiler/build.app diff --git a/src/leaptable-compiler/build.install b/src/leaptable-compiler/build.install new file mode 100644 index 0000000..f35777f --- /dev/null +++ b/src/leaptable-compiler/build.install @@ -0,0 +1 @@ +source src/leaptable-compiler/build.install-app diff --git a/src/leaptable-compiler/build.install-app b/src/leaptable-compiler/build.install-app new file mode 100644 index 0000000..2c5485a --- /dev/null +++ b/src/leaptable-compiler/build.install-app @@ -0,0 +1,12 @@ +build_target leaptable-compiler + +# make paths (this is for Gentoo in particular) +build_dir_tree "${BINDIR}" || return 1 + +# install binary +echo "Installing binaries into '${BINDIR}'" +install_file "${leaptable_compiler}" "${BINDIR}" 0755 || return 1 +print_success "Done" + +# kate: replace-trailing-space-save true; space-indent true; tab-width 4; +# vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/src/leaptable-compiler/build.monolithic b/src/leaptable-compiler/build.monolithic new file mode 100644 index 0000000..cba2726 --- /dev/null +++ b/src/leaptable-compiler/build.monolithic @@ -0,0 +1,18 @@ +# These are external variables, and shouldn't clash with anything else +# leaptable_compiler_MONOLITHIC +# + +SRC="obj/leaptable-compiler.c" +MONOLITHIC_TESTS="src/leaptable-compiler/build.app src/leaptable-compiler/build.monolithic" + +if [ -z "${leaptable_compiler_MONOLITHIC}" ] +then + MONOLITHIC_SOURCE="$(find src/leaptable-compiler/ -name '*.c' | sort)" + make_monolithic ${SRC} C || return 1 + + leaptable_compiler_MONOLITHIC=1 + MONOLITHIC_DOC="${MONOLITHIC_DOC} ${SRC}" +fi + +# kate: replace-trailing-space-save true; space-indent true; tab-width 4; +# vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/version b/version index 53d81de..b641fa6 100644 --- a/version +++ b/version @@ -10,8 +10,8 @@ # VERSION contains the full version number of the library, which is # expected to be in 'major.minor.micro' format. VERMAJOR=0 -VERMINOR=0 -VERMICRO=0 +VERMINOR=3 +VERMICRO=4 # kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: expandtab:ts=4:sw=4:syntax=sh