From c1152e40a75ef5e1ef2308fce364def0a9179dd3 Mon Sep 17 00:00:00 2001 From: wrmr Date: Fri, 26 Sep 2025 04:54:34 -0400 Subject: add -w --width option to do basic HTML word wrap --- main.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index f52297d..14794fb 100644 --- a/main.c +++ b/main.c @@ -25,6 +25,7 @@ typedef struct { int hvarc; Str docroot; Str header_file, footer_file; + unsigned wrap_width; } Options; Options opts = { 0 }; @@ -88,6 +89,47 @@ int uri_ch_allowed(char c) { || c == '%'; } +int str_to_uint(Str s, unsigned *out) { + unsigned x = 0; + if (!s.n) return -1; + for (int i = 0; i < s.n; i++) { + if (s.s[i] < '0' || s.s[i] > '9') return -1; + x = x * 10 + (s.s[i] - '0'); + } + *out = x; + return 0; +} + +void str_cat_wrap_lines(Str *out, Str src, int width, int hanging_indent, Arena *a) { + Str line; + while (next_line(&src, &line)) { + unsigned col = 1; + while (line.n > 0) { + if (line.s[0] == ' ') { + line = str_skip(line, 1); +space: + col++; + str_catc(out, ' ', a); + } else { + Cut c = str_cut(line, ' '); + if (col + c.head.n > width) { + str_catc(out, '\n', a); + col = 1; + for (int i = 0; i < hanging_indent; i++) { + str_catc(out, ' ', a); + col++; + } + } + str_cat(out, c.head, a); + col += c.head.n; + line = c.tail; + goto space; + } + } + str_catc(out, '\n', a); + } +} + void str_cat_uri_internal(Str *s, Str uri, Arena *a) { for (isize i = 0; i < uri.n; i++) { char c = uri.s[i]; @@ -497,10 +539,17 @@ int wdoc(FILE *f, Doc **dp, Arena *perm, Arena *scratch) { Doc *d = new(perm, Doc); BlockList blk = blk_gather(buf, scratch); for (size_t i = 0; i < blk.len; i++) { - if (blk.data[i].type == LN_HDR1 && !d->title.s) { - d->title = blk.data[i].lines->txt; + Block *b = &blk.data[i]; + if (b->type == LN_HDR1 && !d->title.s) { + d->title = b->lines->txt; + } + Str blk_txt = { 0 }; + markup_block(&blk_txt, b, scratch, scratch); + if (b->type == LN_CODE || opts.wrap_width == 0) { + str_cat(&d->html, blk_txt, perm); + } else { + str_cat_wrap_lines(&d->html, blk_txt, opts.wrap_width, 0, perm); } - markup_block(&d->html, &blk.data[i], perm, scratch); if (i + 1 < blk.len) str_cat(&d->html, S("\n"), perm); } *dp = d; @@ -560,7 +609,8 @@ void usage(const char *cmd) { " -R --root set a root url prepended to absolute paths\n" " -H --header prepend the given file before the document\n" " -F --footer append the given file after the document\n" - " -L --lang set the document's language\n", + " -L --lang set the document's language\n" + " -w --width wrap lines at the given column width\n", cmd, cmd); } @@ -670,7 +720,7 @@ int main(int argc, const char **argv) { Str param = { 0 }; opts.from_stdin = 1; - while ((r = arg_get(&a, "?sliqc:h:t:L:R:H:F:", ¶m, + while ((r = arg_get(&a, "?sliqc:h:t:L:R:H:F:w:", ¶m, "help", '?', "standalone", 's', "smartq", 'q', @@ -682,7 +732,8 @@ int main(int argc, const char **argv) { "root", 'R', ":hvar", 'h', "header", 'H', - "footer", 'F')) >= ARG_OK) { + "footer", 'F', + ":width", 'w')) >= ARG_OK) { Arena reset = scratch; FILE *f; switch (r) { @@ -726,6 +777,12 @@ int main(int argc, const char **argv) { case 'F': opts.footer_file = param; break; + case 'w': + if (str_to_uint(param, &opts.wrap_width)) { + fprintf(stderr, "invalid wrap width '%*s'\n", (int)param.n, param.s); + return 1; + } + break; default: if (str_eql(param, S("-"))) { if (wdoc(stdin, &doc, &perm, &scratch)) { -- cgit v1.2.3