diff options
-rw-r--r-- | design.txt | 4 | ||||
-rw-r--r-- | doc.c | 143 | ||||
-rw-r--r-- | doc.h | 24 | ||||
-rw-r--r-- | err.c | 12 | ||||
-rw-r--r-- | err.h | 7 | ||||
-rw-r--r-- | main.c | 272 | ||||
-rw-r--r-- | net.c | 34 | ||||
-rw-r--r-- | net.h | 26 | ||||
-rw-r--r-- | parse.c | 17 | ||||
-rw-r--r-- | parse.h | 9 |
10 files changed, 343 insertions, 205 deletions
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 <string.h> +#include <stdlib.h> +#include <stdio.h> + +#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 <stdlib.h> +#include <stdio.h> +#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 <string.h> #include <stdlib.h> -/* 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 <string.h> +#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 <stddef.h> +#include "doc.h" + +int parse_doc(enum doc_type doct, int (*line_get)(char **, size_t *)); + +#endif |