diff --git a/src/gmnlm.c b/src/gmnlm.c

index 5d6ce3cc5ff97884bb10c2e9e7f6e870b5d2f54a..e6910607646fdd4f4d5a04a052715eda35147c30 100644

--- a/src/gmnlm.c

+++ b/src/gmnlm.c

@@ -146,6 +146,45 @@ for (; *in && isspace(*in); ++in);

return in;

}

+static int

+wrap(FILE *f, char *s, struct winsize *ws, int *row, int *col)

+{

+ if (!s[0]) {

+ fprintf(f, "\n");

+ return 0;

+ }

+ for (int i = 0; s[i]; ++i) {

+ // TODO: Other control sequences, and eat ANSI escapes before

+ // they become a problem

+ switch (s[i]) {

+ case '\n':

+ assert(0); // Not supposed to happen

+ case '\t':

+ *col = *col + (8 - *col % 8);

+ break;

+ default:

+ *col += 1;

+ break;

+ }

+

+ if (*col >= ws->ws_col) {

+ int j = i--;

+ while (&s[i] != s && !isspace(s[i])) --i;

+ if (&s[i] == s) {

+ i = j;

+ }

+ char c = s[i];

+ s[i] = 0;

+ int n = fprintf(f, "%s\n", s);

+ s[i] = c;

+ *row += 1;

+ *col = 0;

+ return n;

+ }

+ }

+ return fprintf(f, "%s\n", s) - 1;

+}

+

static bool

display_gemini(struct browser *browser, struct gemini_response *resp)

{

@@ -157,15 +196,29 @@

struct winsize ws;

ioctl(fileno(browser->tty), TIOCGWINSZ, &ws);

+ char *text = NULL;

int row = 0, col = 0;

struct gemini_token tok;

struct link **next = &browser->links;

- while (gemini_parser_next(&p, &tok) == 0) {

+ while (text != NULL || gemini_parser_next(&p, &tok) == 0) {

switch (tok.token) {

case GEMINI_TEXT:

- // TODO: word wrap

- col += fprintf(browser->tty, " %s\n",

- trim_ws(tok.text));

+ if (text == NULL) {

+ text = tok.text;

+ }

+

+ do {

+ col += fprintf(browser->tty, " ");

+ int w = wrap(browser->tty, text, &ws, &row, &col);

+ text += w;

+ if (row >= ws.ws_row - 4) {

+ break;

+ }

+ } while (text[0]);

+

+ if (!text[0]) {

+ text = NULL;

+ }

break;

case GEMINI_LINK:

col += fprintf(browser->tty, "%d) %s\n", nlinks++,