commit 81839fee39ad184c28b20b13297027be9d10f200 from: Omar Polo date: Sun Jul 25 17:24:51 2021 UTC initial gophermaps support commit - 7527561b1106e58c4f926b259d87fd4e55d6be70 commit + 81839fee39ad184c28b20b13297027be9d10f200 blob - 60d416a534b2e9e358fe933856e74ea20325bcc0 blob + eadce1c536e0178454f2f2be0e5487d97b32b0de --- Makefile.am +++ Makefile.am @@ -27,6 +27,7 @@ telescope_SOURCES = cmd.c \ parser.c \ parser.h \ parser_gemtext.c \ + parser_gophermap.c \ parser_textplain.c \ sandbox.c \ telescope.c \ blob - 43423133b915661820713d65a7a6f48c4766025b blob + b5e42ce1ddf4465a8052e9cc624ea371d0b092ff --- parser.h +++ parser.h @@ -27,6 +27,9 @@ int parser_foreach_line(struct parser*, const char*, /* parser_gemtext.c */ void gemtext_initparser(struct parser*); +/* parser_gophermap.c */ +void gophermap_initparser(struct parser *); + /* parser_textplain.c */ void textplain_initparser(struct parser*); blob - /dev/null blob + 9b05562e49d6b62f6466864e7b7cbf7057f0bc40 (mode 644) --- /dev/null +++ parser_gophermap.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "compat.h" + +#include +#include +#include + +#include "parser.h" + +struct gm_selector { + char type; + const char *ds; + const char *selector; + const char *addr; + const char *port; +}; + +static void gm_parse_selector(char *, struct gm_selector *); + +static int gm_parse(struct parser *, const char *, size_t); +static int gm_foreach_line(struct parser *, const char *, size_t); +static int gm_free(struct parser *); + +void +gophermap_initparser(struct parser *p) +{ + memset(p, 0, sizeof(*p)); + + p->name = "gophermap"; + p->parse = &gm_parse; + p->free = &gm_free; +} + +static void +gm_parse_selector(char *line, struct gm_selector *s) +{ + s->type = *line++; + s->ds = line; + + if ((line = strchr(line, '\t')) == NULL) + return; + *line++ = '\0'; + s->selector = line; + + if ((line = strchr(line, '\t')) == NULL) + return; + *line++ = '\0'; + s->addr = line; + + if ((line = strchr(line, '\t')) == NULL) + return; + *line++ = '\0'; + s->port = line; +} + +static int +gm_parse(struct parser *p, const char *buf, size_t size) +{ + return parser_foreach_line(p, buf, size, gm_foreach_line); +} + +static inline int +emit_line(struct parser *p, enum line_type type, struct gm_selector *s) +{ + struct line *l; + char buf[LINE_MAX], b[2] = {0}; + + if ((l = calloc(1, sizeof(*l))) == NULL) + goto err; + + if ((l->line = strdup(s->ds)) == NULL) + goto err; + + switch (l->type = type) { + case LINE_LINK: + if (s->type == 'h' && has_prefix(s->selector, "URL:")) { + strlcpy(buf, s->selector+4, sizeof(buf)); + } else { + strlcpy(buf, "gopher://", sizeof(buf)); + strlcat(buf, s->addr, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, s->port, sizeof(buf)); + strlcat(buf, "/", sizeof(buf)); + b[0] = s->type; + strlcat(buf, b, sizeof(buf)); + if (*s->selector != '/') + strlcat(buf, "/", sizeof(buf)); + strlcat(buf, s->selector, sizeof(buf)); + } + + if ((l->alt = strdup(buf)) == NULL) + goto err; + break; + + default: + break; + } + + if (TAILQ_EMPTY(&p->head)) + TAILQ_INSERT_HEAD(&p->head, l, lines); + else + TAILQ_INSERT_TAIL(&p->head, l, lines); + + return 1; + +err: + if (l != NULL) { + free(l->line); + free(l->alt); + free(l); + } + return 0; +} + +static int +gm_foreach_line(struct parser *p, const char *line, size_t linelen) +{ + char buf[LINE_MAX] = {0}; + struct gm_selector s = {0}; + + memcpy(buf, line, MIN(sizeof(buf)-1, linelen)); + gm_parse_selector(buf, &s); + + switch (s.type) { + case '0': /* text file */ + case '1': /* gopher submenu */ + case '2': /* CCSO nameserver */ + case '4': /* binhex-encoded file */ + case '5': /* DOS file */ + case '6': /* uuencoded file */ + case '7': /* full-text search */ + case '8': /* telnet */ + case '9': /* binary file */ + case '+': /* mirror or alternate server */ + case 'g': /* gif */ + case 'I': /* image */ + case 'T': /* telnet 3270 */ + case ':': /* gopher+: bitmap image */ + case ';': /* gopher+: movie file */ + case 'd': /* non-canonical: doc */ + case 'h': /* non-canonical: html file */ + case 's': /* non-canonical: sound file */ + if (!emit_line(p, LINE_LINK, &s)) + return 0; + break; + + break; + + case 'i': /* non-canonical: message */ + if (!emit_line(p, LINE_TEXT, &s)) + return 0; + break; + + case '3': /* error code */ + if (!emit_line(p, LINE_QUOTE, &s)) + return 0; + break; + } + + return 1; +} + +static int +gm_free(struct parser *p) +{ + /* flush the buffer */ + if (p->len != 0) + gm_foreach_line(p, p->buf, p->len); + + free(p->buf); + + return 1; +} blob - 7c61398338fc18e8e7c29d0793db9d97a11daa74 blob + bc3bfcc362be9577969382cb8717cbec7636da58 --- telescope.c +++ telescope.c @@ -636,15 +636,29 @@ load_gemini_url(struct tab *tab, const char *url) static void load_gopher_url(struct tab *tab, const char *url) { - struct get_req req; + struct get_req req; + const char *path; memset(&req, 0, sizeof(req)); strlcpy(req.host, tab->uri.host, sizeof(req.host)); strlcpy(req.port, tab->uri.port, sizeof(req.host)); - /* cheat a bit by considering gophermaps text */ - textplain_initparser(&tab->buffer.page); - make_request(tab, &req, PROTO_GOPHER, tab->uri.path); + path = tab->uri.path; + if (!strcmp(path, "/") || *path == '\0') { + /* expect the top directory to be a gophermap */ + gophermap_initparser(&tab->buffer.page); + } else if (has_prefix(path, "/1/")) { + /* gophermap menu/submenu */ + gophermap_initparser(&tab->buffer.page); + path += 3; + } else if (has_prefix(path, "/0/")) { + textplain_initparser(&tab->buffer.page); + path += 3; + } else { + return; + } + + make_request(tab, &req, PROTO_GOPHER, path); } static void