2 * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "telescope.h"
41 static void gm_parse_selector(char *, struct gm_selector *);
43 static int gm_parse_line(struct buffer *, const char *, size_t);
44 static int gm_serialize(struct buffer *, FILE *);
46 const struct parser gophermap_parser = {
48 .parseline = &gm_parse_line,
49 .serialize = &gm_serialize,
53 gm_parse_selector(char *line, struct gm_selector *s)
61 if ((line = strchr(line, '\t')) == NULL)
66 if ((line = strchr(line, '\t')) == NULL)
71 if ((line = strchr(line, '\t')) == NULL)
78 selector2uri(struct gm_selector *s, char *buf, size_t len)
82 r = snprintf(buf, len, "gopher://%s:%s/%c%s",
83 s->addr, s->port, s->type, *s->selector != '/' ? "/" : "");
84 if (r < 0 || (size_t)r >= len)
89 return (iri_urlescape(s->selector, buf, len));
93 emit_line(struct buffer *b, enum line_type type, struct gm_selector *s)
98 l = xcalloc(1, sizeof(*l));
100 l->line = xstrdup(s->ds);
102 switch (l->type = type) {
104 if (s->type == 'h' && !strncmp(s->selector, "URL:", 4)) {
105 strlcpy(buf, s->selector+4, sizeof(buf));
106 } else if (selector2uri(s, buf, sizeof(buf)) == -1)
109 l->alt = xstrdup(buf);
116 TAILQ_INSERT_TAIL(&b->head, l, lines);
130 gm_parse_line(struct buffer *b, const char *line, size_t linelen)
132 char buf[LINE_MAX] = {0};
133 struct gm_selector s = {0};
135 memcpy(buf, line, MIN(sizeof(buf)-1, linelen));
136 gm_parse_selector(buf, &s);
139 case '0': /* text file */
140 case '1': /* gopher submenu */
141 case '2': /* CCSO nameserver */
142 case '4': /* binhex-encoded file */
143 case '5': /* DOS file */
144 case '6': /* uuencoded file */
145 case '7': /* full-text search */
146 case '8': /* telnet */
147 case '9': /* binary file */
148 case '+': /* mirror or alternate server */
150 case 'I': /* image */
151 case 'T': /* telnet 3270 */
152 case ':': /* gopher+: bitmap image */
153 case ';': /* gopher+: movie file */
154 case 'd': /* non-canonical: doc */
155 case 'h': /* non-canonical: html file */
156 case 's': /* non-canonical: sound file */
157 if (!emit_line(b, LINE_LINK, &s))
161 case 'i': /* non-canonical: message */
162 if (!emit_line(b, LINE_TEXT, &s))
166 case '3': /* error code */
167 if (!emit_line(b, LINE_QUOTE, &s))
175 static inline const char *
176 gopher_skip_selector(const char *path, int *ret_type)
180 if (!strcmp(path, "/") || *path == '\0') {
189 switch (*ret_type = *path) {
205 serialize_link(struct line *line, const char *text, FILE *fp)
209 const char *uri, *endhost, *port, *path, *colon;
211 if ((uri = line->alt) == NULL)
214 if (strncmp(uri, "gopher://", 9) != 0)
215 return fprintf(fp, "h%s\tURL:%s\terror.host\t1\n",
218 uri += 9; /* skip gopher:// */
220 path = strchr(uri, '/');
221 colon = strchr(uri, ':');
223 if (path != NULL && colon > path)
226 if ((endhost = colon) == NULL &&
227 (endhost = path) == NULL)
228 endhost = strchr(uri, '\0');
231 for (port = colon+1; *port && *port != '/'; ++port)
243 path = gopher_skip_selector(path, &type);
245 return fprintf(fp, "%c%s\t%s\t%.*s\t%.*s\n", type, text,
246 path, (int)(endhost - uri), uri, (int)portlen, port);
250 gm_serialize(struct buffer *b, FILE *fp)
256 TAILQ_FOREACH(line, &b->head, lines) {
257 if ((text = line->line) == NULL)
260 switch (line->type) {
262 r = serialize_link(line, text, fp);
266 r = fprintf(fp, "i%s\t\terror.host\t1\n", text);
270 r = fprintf(fp, "3%s\t\terror.host\t1\n", text);