jdict

command line tool for looking up terms in yomidict dictionaries
git clone anongit@rnpnr.xyz:jdict.git
Log | Files | Refs | Feed | README | LICENSE

Commit: b9d57c873356ce080dd0ac539c663b082f9689cd
Parent: 53bb73ed4482305371e875ac0bfc6067b40c55ca
Author: Randy Palamar
Date:   Tue, 15 Oct 2024 08:06:59 -0600

remove *printf calls

This drops the binary size by >10K on musl and likely more on glibc

Diffstat:
Mconfig.def.h | 6+++---
Mjdict.c | 216++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 163 insertions(+), 59 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -1,14 +1,14 @@ /* See LICENSE for license details. */ /* dir where unzipped yomidicts are stored */ -static char *prefix = "/usr/share/yomidicts"; +static s8 prefix = s8("/usr/share/yomidicts"); /* field separator for output printing */ static s8 fsep = s8("\t"); /* repl prompt and quit strings */ -static char *repl_prompt = "\033[32;1m入力:\033[0m "; -static char *repl_quit = "\n\033[36m(=^ᆺ^)ノ バイバイ~\033[0m"; +static s8 repl_prompt = s8("\033[32;1m入力:\033[0m "); +static s8 repl_quit = s8("\n\033[36m(=^ᆺ^)ノ バイバイ~\033[0m"); /* default yomidicts to search */ Dict default_dict_map[] = { diff --git a/jdict.c b/jdict.c @@ -1,8 +1,6 @@ /* See LICENSE for license details. */ #include <dirent.h> #include <fcntl.h> -#include <limits.h> -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> @@ -37,6 +35,14 @@ typedef struct { #define s8(cstr) (s8){.len = ARRAY_COUNT(cstr) - 1, .s = (u8 *)cstr} typedef struct { + u8 *data; + u32 cap; + u32 widx; + i32 fd; + b32 errors; +} Stream; + +typedef struct { u8 *beg, *end; #ifdef _DEBUG_ARENA size min_capacity_remaining; @@ -76,6 +82,66 @@ typedef struct { #include "config.h" +#define os_path_sep s8("/") +static b32 os_write(i32, s8); + +static Stream error_stream; + +static void +stream_append_byte(Stream *s, u8 b) +{ + s->errors |= s->widx + 1 > s->cap; + if (!s->errors) + s->data[s->widx++] = b; +} + +static void +stream_append_s8(Stream *s, s8 str) +{ + s->errors |= (s->cap - s->widx) < str.len; + if (!s->errors) { + for (size i = 0; i < str.len; i++) + s->data[s->widx++] = str.s[i]; + } +} + +static void +stream_append_u64(Stream *s, u64 n) +{ + u8 tmp[64]; + u8 *end = tmp + sizeof(tmp); + u8 *beg = end; + do { *--beg = '0' + (n % 10); } while (n /= 10); + stream_append_s8(s, (s8){.len = end - beg, .s = beg}); +} + +static void +stream_flush(Stream *s) +{ + if (s->widx) { + os_write(s->fd, (s8){.len = s->widx, .s = s->data}); + s->widx = 0; + s->errors = 0; + } +} + +static s8 +cstr_to_s8(char *cstr) +{ + s8 result = {.s = (u8 *)cstr}; + if (cstr) while (*cstr) { result.len++; cstr++; } + return result; +} + +static void __attribute__((noreturn)) +die(Stream *s) +{ + if (s->data[s->widx - 1] != '\n') + stream_append_byte(s, '\n'); + stream_flush(s); + exit(1); +} + static Arena os_new_arena(size cap) { @@ -100,8 +166,9 @@ os_file_size(char *file) { struct stat st; if (stat(file, &st) < 0) { - fprintf(stderr, "failed to stat: %s\n", file); - exit(1); + stream_append_s8(&error_stream, s8("failed to stat: ")); + stream_append_s8(&error_stream, cstr_to_s8(file)); + die(&error_stream); } return st.st_size; } @@ -111,8 +178,9 @@ os_read_file(char *file, u8 *buf, size file_size) { i32 fd = open(file, O_RDONLY); if (fd < 0) { - fprintf(stderr, "failed to open: %s\n", file); - exit(1); + stream_append_s8(&error_stream, s8("failed to open: ")); + stream_append_s8(&error_stream, cstr_to_s8(file)); + die(&error_stream); } s8 result = {.len = file_size, .s = buf}; @@ -120,13 +188,21 @@ os_read_file(char *file, u8 *buf, size file_size) close(fd); if (rlen != result.len) { - fprintf(stderr, "failed to read whole file: %s\n", file); - exit(1); + stream_append_s8(&error_stream, s8("failed to read whole file: ")); + stream_append_s8(&error_stream, cstr_to_s8(file)); + die(&error_stream); } return result; } +static b32 +os_write(i32 file, s8 raw) +{ + b32 result = write(file, raw.s, raw.len) == raw.len; + return result; +} + static void * mem_clear(void *p_, u8 c, size len) { @@ -178,22 +254,13 @@ alloc_(Arena *a, size len, size align, size count, u32 flags) else return mem_clear(result, 0, count * len); } -static void __attribute__((noreturn)) -die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - exit(1); -} - static void -usage(char *argv0) +usage(s8 argv0) { - die("usage: %s [-d path] [-F FS] [-i] term ...\n", argv0); + stream_append_s8(&error_stream, s8("usage: ")); + stream_append_s8(&error_stream, argv0); + stream_append_s8(&error_stream, s8(" [-d path] [-F FS] [-i] term ...\n")); + die(&error_stream); } static s8 @@ -212,15 +279,6 @@ s8_dup(Arena *a, s8 old) return result; } -static s8 -cstr_to_s8(char *cstr) -{ - s8 result = {.s = (u8 *)cstr}; - if (cstr) while (*cstr) { result.len++; cstr++; } - return result; -} - - static i32 s8_cmp(s8 a, s8 b) { @@ -311,14 +369,17 @@ intern(struct ht *t, s8 key) } static size_t -count_term_banks(const char *path) +count_term_banks(char *path) { DIR *dir; struct dirent *dent; size_t nbanks = 0; - if (!(dir = opendir(path))) - die("opendir(): failed to open: %s\n", path); + if (!(dir = opendir(path))) { + stream_append_s8(&error_stream, s8("opendir: failed to open: ")); + stream_append_s8(&error_stream, cstr_to_s8(path)); + die(&error_stream); + } /* count term banks in path */ while ((dent = readdir(dir)) != NULL) @@ -353,9 +414,11 @@ parse_term_bank(Arena *a, struct ht *ht, char *tbank) goto cleanup; case YOMI_ERROR_INVAL: case YOMI_ERROR_MALFO: - fprintf(stderr, "yomi_parse: %s\n", - r == YOMI_ERROR_INVAL? "YOMI_ERROR_INVAL" - : "YOMI_ERROR_MALFO"); + stream_append_s8(&error_stream, s8("yomi_parse: ")); + if (r == YOMI_ERROR_INVAL) + stream_append_s8(&error_stream, s8("YOMI_ERROR_INVAL\n")); + else + stream_append_s8(&error_stream, s8("YOMI_ERROR_MALFO\n")); goto cleanup; } } @@ -382,8 +445,12 @@ parse_term_bank(Arena *a, struct ht *ht, char *tbank) /* check if entry was valid */ if (tdefs == NULL || tstr == NULL) { - fprintf(stderr, "parse_term_bank: %s == NULL\n", - tdefs == NULL? "tdefs" : "tstr"); + stream_append_s8(&error_stream, s8("parse_term_bank: ")); + if (!tdefs) + stream_append_s8(&error_stream, s8("tdefs")); + else + stream_append_s8(&error_stream, s8("tstr")); + stream_append_s8(&error_stream, s8(" == NULL\n")); break; } @@ -413,6 +480,7 @@ parse_term_bank(Arena *a, struct ht *ht, char *tbank) } cleanup: + stream_flush(&error_stream); /* NOTE: clear temporary allocations */ a->end = start.end; } @@ -420,31 +488,54 @@ cleanup: static int make_dict(Arena *a, Dict *d) { - char path[PATH_MAX - 20], tbank[PATH_MAX]; - size_t nbanks; + Arena start = *a; + Stream path = {.cap = 1 * MEGABYTE}; + path.data = alloc(a, u8, path.cap, ARENA_ALLOC_END|ARENA_NO_CLEAR); + + stream_append_s8(&path, prefix); + stream_append_s8(&path, os_path_sep); + stream_append_s8(&path, d->rom); + stream_append_byte(&path, 0); d->ht.ents = alloc(a, DictEnt *, 1 << HT_EXP, 0); - snprintf(path, ARRAY_COUNT(path), "%s/%s", prefix, d->rom.s); - if ((nbanks = count_term_banks(path)) == 0) { - fprintf(stderr, "no term banks found: %s\n", path); + size_t nbanks; + if ((nbanks = count_term_banks((char *)path.data)) == 0) { + stream_append_s8(&error_stream, s8("no term banks found: ")); + stream_append_s8(&error_stream, (s8){.len = path.widx - 1, .s = path.data}); + stream_append_byte(&error_stream, '\n'); + stream_flush(&error_stream); return 0; } + path.widx -= 1; + stream_append_s8(&path, os_path_sep); + stream_append_s8(&path, s8("term_bank_")); + i32 start_widx = path.widx; + for (size_t i = 1; i <= nbanks; i++) { - snprintf(tbank, ARRAY_COUNT(tbank), "%s/term_bank_%zu.json", path, i); - parse_term_bank(a, &d->ht, tbank); + path.widx = start_widx; + stream_append_u64(&path, i); + stream_append_s8(&path, s8(".json\0")); + parse_term_bank(a, &d->ht, (char *)path.data); } + /* NOTE: clear temporary allocations */ + a->end = start.end; + return 1; } static void make_dicts(Arena *a, Dict *dicts, size_t ndicts) { - for (size_t i = 0; i < ndicts; i++) - if (!make_dict(a, &dicts[i])) - die("make_dict(%s): returned NULL\n", dicts[i].rom); + for (size_t i = 0; i < ndicts; i++) { + if (!make_dict(a, &dicts[i])) { + stream_append_s8(&error_stream, s8("make_dict failed for: ")); + stream_append_s8(&error_stream, dicts[i].rom); + die(&error_stream); + } + } } static DictEnt * @@ -496,7 +587,7 @@ repl(Arena *a, Dict *dicts, size_t ndicts) fsep = s8("\n"); for (;;) { - fputs(repl_prompt, stdout); + fwrite(repl_prompt.s, 1, repl_prompt.len, stdout); fflush(stdout); if (fgets((char *)buf.s, ARRAY_COUNT(t), stdin) == NULL) break; @@ -504,7 +595,9 @@ repl(Arena *a, Dict *dicts, size_t ndicts) for (size_t i = 0; i < ndicts; i++) find_and_print(s8trim(buf), &dicts[i]); } - puts(repl_quit); + fwrite(repl_quit.s, 1, repl_quit.len, stdout); + if (repl_quit.s[repl_quit.len - 1] != '\n') + fputc('\n', stdout); } int @@ -515,7 +608,11 @@ main(int argc, char *argv[]) size_t ndicts = 0, nterms = 0; int iflag = 0; - char *argv0 = argv[0]; + error_stream.fd = STDERR_FILENO; + error_stream.cap = 4096; + error_stream.data = alloc(&memory, u8, error_stream.cap, 0); + + s8 argv0 = cstr_to_s8(argv[0]); for (argv++, argc--; argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { /* NOTE: '--' to end parameters */ if (argv[0][1] == '-' && argv[0][2] == 0) { @@ -541,8 +638,11 @@ main(int argc, char *argv[]) break; } } - if (dicts == NULL) - die("invalid dictionary name: %s\n", argv[1]); + if (dicts == NULL) { + stream_append_s8(&error_stream, s8("invalid dictionary name: ")); + stream_append_s8(&error_stream, dname); + die(&error_stream); + } argv++; } break; case 'i': iflag = 1; break; @@ -571,8 +671,12 @@ main(int argc, char *argv[]) repl(&memory, dicts, ndicts); #ifdef _DEBUG_ARENA - printf("min remaining arena capacity: %zd\n", memory.min_capacity_remaining); - printf("remaining arena capacity: %zd\n", memory.end - memory.beg); + stream_append_s8(&error_stream, s8("min remaining arena capacity: ")); + stream_append_u64(&error_stream, memory.min_capacity_remaining); + stream_append_s8(&error_stream, s8("\nremaining arena capacity: ")); + stream_append_u64(&error_stream, memory.end - memory.beg); + stream_append_byte(&error_stream, '\n'); + stream_flush(&error_stream); #endif return 0;