From 58214ec5f982c1b97aadce254c958a5f922c9724 Mon Sep 17 00:00:00 2001 From: wrmr Date: Sat, 2 Nov 2024 15:10:41 -0500 Subject: split main.c in several places, began work on design doc --- design.txt | 4 + doc.c | 143 ++++++++++++++++++++++++++++++++ doc.h | 24 ++++++ err.c | 12 +++ err.h | 7 ++ main.c | 272 +++++++++++++++---------------------------------------------- net.c | 34 ++++++++ net.h | 26 ++++++ parse.c | 17 ++++ parse.h | 9 ++ 10 files changed, 343 insertions(+), 205 deletions(-) create mode 100644 design.txt create mode 100644 doc.c create mode 100644 doc.h create mode 100644 err.c create mode 100644 err.h create mode 100644 net.c create mode 100644 net.h create mode 100644 parse.c create mode 100644 parse.h diff --git a/design.txt b/design.txt new file mode 100644 index 0000000..84fa2f0 --- /dev/null +++ b/design.txt @@ -0,0 +1,4 @@ +===================== + WORM'S LINE BROWSER +===================== +Design Considerations diff --git a/doc.c b/doc.c new file mode 100644 index 0000000..3478f8b --- /dev/null +++ b/doc.c @@ -0,0 +1,143 @@ +#include +#include +#include + +#include "doc.h" +#include "err.h" + +/* text */ + +static struct { + char *buf; + size_t len, cap; +} txt; + +void txt_init(void) { + txt.buf = malloc(1); + if (!txt.buf) efatal("txt_init"); + txt.buf[0] = 0; + txt.len = 0; + txt.cap = 1; +} + +void txt_fini(void) { + free(txt.buf); +} + +void txt_grow(size_t n) { + size_t cap = txt.cap; + size_t len = txt.len + n; + if (cap < len) { + while (cap < len) cap <<= 1; + char *s = realloc(txt.buf, cap); + if (!s) efatal("txt_grow"); + txt.cap = cap; + txt.buf = s; + } +} + +/* docs */ + +struct doc { + size_t ofs, len, idx; +}; + +#define DOC_MAX 32 + +static struct doc docv[DOC_MAX] = { + { + + .ofs = 0, + .len = 0, + .idx = 0 + } +}; + +static size_t docn = 0, doci = 0; + +void doc_pop(void) { + if (docn > 0) { + docn--; + if (doci > 0) doci--; + } +} + +void doc_shift(void) { + if (docn > 0) { + docn--; + memmove(txt.buf, txt.buf + docv[0].len, txt.len - docv[0].len); + memmove(&docv[0], &docv[1], docn * sizeof(struct doc)); + docv[0].ofs = 0; + for (int i = 1; i < docn; i++) { + docv[i].idx = docv[i - 1].ofs + (docv[i].idx - docv[i].ofs); + docv[i].ofs = docv[i - 1].ofs + docv[i - 1].len; + } + if (docn > 0 && doci >= docn) doci = docn - 1; + } +} + +void doc_new(void) { + while (docn >= DOC_MAX) { + doc_shift(); + } + if (doci < docn) { + txt.len = docv[doci].ofs; + docn = doci; + } + memset(&docv[docn], 0, sizeof(struct doc)); + docv[docn].ofs = txt.len; + docv[docn].idx = txt.len; + doci = docn++; +} + +void doc_add(const char *buf, size_t n) { + txt_grow(n); + memcpy(&txt.buf[txt.len], buf, n); + txt.len += n; + docv[doci].len += n; +} + +void doc_adds(const char *s) { + doc_add(s, strlen(s)); +} + +void doc_prev(void) { + if (doci > 0) doci--; +} + +void doc_next(void) { + if (doci < docn) doci++; +} + +void doc_back_line(void) { + if (doci >= docn) return; + struct doc *d = &docv[doci]; + size_t i = d->idx; + while (i > 0 && txt.buf[d->idx] != '\n') i--; + if (i > 0) i--; + while (i > 0 && txt.buf[d->idx] != '\n') i--; + if (i > 0) i++; + d->idx = i; +} + +void doc_print_line(void) { + if (doci >= docn) return; + struct doc *d = &docv[doci]; + size_t i = d->idx; + size_t n = d->ofs + d->len; + while (i < n && txt.buf[d->idx] != '\n') { + putchar(txt.buf[i++]); + } + if (i < n) i++; + d->idx = i; +} + +void doc_init(void) { + txt_init(); + doc_new(); + doc_adds("Type ? for command help.\n"); +} + +void doc_fini(void) { + txt_fini(); +} diff --git a/doc.h b/doc.h new file mode 100644 index 0000000..3c5dfcc --- /dev/null +++ b/doc.h @@ -0,0 +1,24 @@ +#ifndef DOC_H +#define DOC_H + +enum doc_type { + TYPE_UNKNOWN, + TYPE_GOPHERDOC, + TYPE_GEMTEXT, + TYPE_PLAIN, +}; + +void doc_new(void); +void doc_prev(void); +void doc_next(void); + +void doc_add(const char *, size_t); +void doc_adds(const char *); + +void doc_back_line(void); +void doc_print_line(void); + +void doc_init(void); +void doc_fini(void); + +#endif diff --git a/err.c b/err.c new file mode 100644 index 0000000..0b98a2b --- /dev/null +++ b/err.c @@ -0,0 +1,12 @@ +#include +#include +#include "err.h" + +void perr(const char *s) { + fprintf(stderr, "%s\n", s); +} + +void efatal(const char *s) { + perror(s); + exit(1); +} diff --git a/err.h b/err.h new file mode 100644 index 0000000..312795d --- /dev/null +++ b/err.h @@ -0,0 +1,7 @@ +#ifndef ERR_H +#define ERR_H + +void perr(const char *s); +void efatal(const char *s); + +#endif diff --git a/main.c b/main.c index c2c9575..adc488a 100644 --- a/main.c +++ b/main.c @@ -4,126 +4,9 @@ #include #include -/* errors */ - -void perr(const char *s) { - fprintf(stderr, "%s\n", s); -} - -/* misc */ - -int running; - -int quit(const char *_) { - puts("goodbye!"); - running = 0; - return 0; -} - -/* documents */ - -#define TXT_BUF_SIZE (1024 * 1024) -struct { - char buf[TXT_BUF_SIZE]; - size_t n; -} text; - -struct doc { - struct { - size_t ofs, len, idx; - } txt; -}; - -#define DOC_MAX 32 -struct doc docv[DOC_MAX] = { - { - .txt = { - .ofs = 0, - .len = 0, - .idx = 0 - } - } -}; -size_t docn = 0, doci = 0; - -void doc_pop(void) { - if (docn > 0) { - docn--; - if (doci > 0) doci--; - } -} - -void doc_shift(void) { - if (docn > 0) { - docn--; - memmove(text.buf, text.buf + docv[0].txt.len, text.n - docv[0].txt.len); - memmove(&docv[0], &docv[1], docn * sizeof(struct doc)); - docv[0].txt.ofs = 0; - for (int i = 1; i < docn; i++) { - docv[i].txt.idx = docv[i - 1].txt.ofs + (docv[i].txt.idx - docv[i].txt.ofs); - docv[i].txt.ofs = docv[i - 1].txt.ofs + docv[i - 1].txt.len; - } - if (docn > 0 && doci >= docn) doci = docn - 1; - } -} - -int doc_new(void) { - if (doci < docn) { - text.n = docv[doci].txt.ofs; - docn = doci; - } - if (docn >= DOC_MAX) return -1; - memset(&docv[docn], 0, sizeof(struct doc)); - docv[docn].txt.ofs = text.n; - docv[docn].txt.idx = text.n; - doci = docn++; - return 0; -} - -int doc_add(const char *buf, size_t n) { - if (text.n + n > TXT_BUF_SIZE) { - return -1; - } - memcpy(&text.buf[text.n], buf, n); - docv[doci].txt.len += n; - text.n += n; - return 0; -} - -int doc_adds(const char *s) { - return doc_add(s, strlen(s)); -} - -void doc_prev(void) { - if (doci > 0) doci--; -} - -void doc_next(void) { - if (doci < docn) doci++; -} - -void doc_back_line(void) { - if (doci >= docn) return; - struct doc *d = &docv[doci]; - size_t i = d->txt.idx; - while (i > 0 && text.buf[d->txt.idx] != '\n') i--; - if (i > 0) i--; - while (i > 0 && text.buf[d->txt.idx] != '\n') i--; - if (i > 0) i++; - d->txt.idx = i; -} - -void doc_print_line(void) { - if (doci >= docn) return; - struct doc *d = &docv[doci]; - size_t i = d->txt.idx; - size_t n = d->txt.ofs + d->txt.len; - while (i < n && text.buf[d->txt.idx] != '\n') { - putchar(text.buf[i++]); - } - if (i < n) i++; - d->txt.idx = i; -} +#include "doc.h" +#include "net.h" +#include "err.h" /* pagination */ @@ -131,68 +14,29 @@ size_t pg_lines() { return 24; } -int pg_down(const char *_) { +void pg_down(void) { size_t lines = pg_lines(); while (lines--) doc_print_line(); - return 0; } -int pg_up(const char *_) { +void pg_up(void) { size_t lines = pg_lines() << 1; while (lines--) doc_back_line(); - pg_down(_); - return 0; + pg_down(); } -int pg_redraw(const char *_) { +void pg_redraw(void) { size_t lines = pg_lines(); while (lines--) doc_back_line(); - pg_down(_); - return 0; + pg_down(); } /* navigation */ -#define HOST_MAX 255 -#define PATH_MAX 255 - -enum doc_type { - TYPE_UNKNOWN, - TYPE_GOPHERDOC, - TYPE_GEMTEXT, - TYPE_PLAIN, -}; - -enum protocol { - PROT_UNKNOWN, - PROT_FILE, - PROT_GOPHER, - PROT_GEMINI, -}; - -struct addr { - char host[HOST_MAX]; - char path[PATH_MAX]; - size_t host_len, path_len; - enum protocol prot; - enum doc_type type; -}; - -int url_to_addr(const char *url, struct addr *adr, enum protocol prot_default) { - adr->prot = prot_default; - adr->type = TYPE_PLAIN; - adr->host_len = 0; - size_t n = strlen(url); - if (n > PATH_MAX) return -1; - adr->path_len = n; - memcpy(adr->path, url, n); - return 0; -} - int nav_to(const char *url) { struct addr a; static enum protocol prot_default = PROT_FILE; /* change to gopher later */ - if (url_to_addr(url, &a, prot_default)) { + if (net_addr(url, &a, prot_default)) { return -1; } prot_default = a.prot; @@ -210,44 +54,52 @@ struct cmd { int (*fn)(const char *); }; -int addtxt(const char *s) { - return doc_add(s, strlen(s)) || doc_add("\n", 1); -} - -struct cmd cmd_tbl[] = { - { 'g', nav_to }, - { '\n', pg_down }, - { 'b', pg_up }, - { 'r', pg_redraw }, - { 'a', addtxt }, - { 'q', quit } -}; - -/* cmd is mutated when trimming strings */ -void cmd_do(char *cmd) { - if (isdigit(*cmd)) { - errno = 0; - unsigned long n = strtoul(cmd, NULL, 10); - if (errno) { - perr("invalid link number"); - } else if (nav_link_nr(n)) { - perr("navigation failure"); - } - } else { - for (size_t i = 0; i < sizeof cmd_tbl / sizeof(struct cmd); i++) { - if (cmd_tbl[i].ch == *cmd) { - while (isspace(*++cmd)); - for (int j = strlen(cmd) - 1; j > 0 && isspace(cmd[j]); j--) { - cmd[j] = 0; - } - if (cmd_tbl[i].fn(cmd)) { - perr("failed"); - } - goto found; +/* cmd is mutated when trimming strings + * returns whether to quit */ +int cmd_do(char *cmd) { + switch (*cmd) { + case 'q': + puts("goodbye!"); + return 1; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + errno = 0; + unsigned long n = strtoul(cmd, NULL, 10); + if (errno) { + perr("invalid link number"); + } else if (nav_link_nr(n)) { + perr("navigation failure"); } } + break; + case 'b': + pg_up(); + break; + case '\0': + pg_down(); + break; + case 'r': + pg_redraw(); + break; + case 'g': + if (nav_to(cmd + 1)) perr("navigation failure"); + break; + default: perr("?"); -found: ; + break; + } + return 0; +} + + +void cmd_trim(char *buf, size_t max) { + size_t n = strlen(buf); + while (n > 0 && isspace(buf[n - 1])) { + buf[--n] = 0; + } + while (n > 1 && isspace(buf[1])) { + memmove(&buf[0], &buf[1], n--); } } @@ -256,13 +108,23 @@ int cmd_get(char *buf, size_t n) { return !!fgets(buf, n, stdin); } +void init(void) { + doc_init(); +} + +void fini(void) { + doc_fini(); +} + int main(void) { char cmd_buf[1024]; - running = 1; - doc_new(); - doc_adds("Press ? for help.\n"); - while (running && cmd_get(cmd_buf, sizeof cmd_buf)) { - cmd_do(cmd_buf); + init(); + atexit(fini); + while (cmd_get(cmd_buf, sizeof cmd_buf)) { + cmd_trim(cmd_buf, sizeof cmd_buf); + if (cmd_do(cmd_buf)) { + break; + } } return 0; } diff --git a/net.c b/net.c new file mode 100644 index 0000000..73f50d4 --- /dev/null +++ b/net.c @@ -0,0 +1,34 @@ +#include +#include "net.h" + +int net_addr(const char *url, struct addr *adr, enum protocol prot_default) { + char *prot_mark; + if ((prot_mark = strstr(url, "://"))) { + static struct { + const char *str; + enum protocol prot; + } prot_str_tbl[] = { + { "gopher", PROT_GOPHER }, + { "gemini", PROT_GEMINI }, + { "file", PROT_FILE }, + }; + size_t n = prot_mark - url; + adr->prot = PROT_UNKNOWN; + for (size_t i = 0; i < sizeof prot_str_tbl / sizeof *prot_str_tbl; i++) { + if (!strncmp(prot_str_tbl[i].str, url, n)) { + adr->prot = prot_str_tbl[i].prot; + break; + } + } + url = prot_mark + 3; + } else { + adr->prot = prot_default; + } + adr->type = TYPE_PLAIN; + adr->host_len = 0; + size_t n = strlen(url); + if (n > PATH_MAX) return -1; + adr->path_len = n; + memcpy(adr->path, url, n); + return 0; +} diff --git a/net.h b/net.h new file mode 100644 index 0000000..0b4422f --- /dev/null +++ b/net.h @@ -0,0 +1,26 @@ +#ifndef NET_H +#define NET_H + +#include "doc.h" + +#define HOST_MAX 255 +#define PATH_MAX 255 + +enum protocol { + PROT_UNKNOWN, + PROT_FILE, + PROT_GOPHER, + PROT_GEMINI, +}; + +struct addr { + char host[HOST_MAX]; + char path[PATH_MAX]; + size_t host_len, path_len; + enum protocol prot; + enum doc_type type; +}; + +int net_addr(const char *url, struct addr *adr, enum protocol prot_default); + +#endif diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..8061257 --- /dev/null +++ b/parse.c @@ -0,0 +1,17 @@ +#include "parse.h" + +int parse_doc(enum doc_type doct, int (*line_get)(char **, size_t *)) { + char *s; + size_t n; + switch (doct) { + case TYPE_PLAIN: + doc_new(); + while (line_get(&s, &n)) { + doc_add(s, n); + doc_add("\n", 1); + } + return 0; + default: + return -1; + } +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..1267b3c --- /dev/null +++ b/parse.h @@ -0,0 +1,9 @@ +#ifndef PARSE_H +#define PARSE_H + +#include +#include "doc.h" + +int parse_doc(enum doc_type doct, int (*line_get)(char **, size_t *)); + +#endif -- cgit 1.4.1-2-gfad0