jcs
/detritus
/amendments
/29
*: Various tweaks all over the place
jcs made amendment 29 about 1 year ago
--- browser.c Wed Nov 6 22:33:34 2024
+++ browser.c Sat Nov 9 11:34:53 2024
@@ -35,11 +35,13 @@ static Pattern fill_pattern;
extern struct uri_handler finger_handler;
extern struct uri_handler gemini_handler;
extern struct uri_handler gopher_handler;
+extern struct uri_handler http_handler;
struct uri_handler * uri_handlers[] = {
&finger_handler,
&gemini_handler,
&gopher_handler,
+ &http_handler,
};
bool browser_close(struct focusable *focusable);
@@ -82,7 +84,7 @@ browser_idle(struct focusable *focusable, EventRecord
if (page->fetch_cookie != NULL || page->download_frefnum) {
ret = handler->fetch(page);
- /* handle may have grown during fetch */
+ /* handle may have grown/moved during fetch */
HLock(browser->loading_page);
page = *(browser->loading_page);
handler = page->handler;
@@ -254,6 +256,7 @@ browser_close(struct focusable *focusable)
HLock(fpage);
tpage = (*fpage)->fwd_page;
(*fpage)->handler->cleanup(*fpage);
+ /* re-lock */
HLock(fpage);
xfree(&(*fpage)->uri);
DisposeHandle(fpage);
@@ -265,6 +268,7 @@ browser_close(struct focusable *focusable)
HLock(bpage);
tpage = (*bpage)->back_page;
(*bpage)->handler->cleanup(*bpage);
+ /* re-lock */
HLock(bpage);
xfree(&(*bpage)->uri);
DisposeHandle(bpage);
@@ -273,6 +277,7 @@ browser_close(struct focusable *focusable)
HLock(pageh);
(*pageh)->handler->cleanup(*pageh);
+ /* re-lock */
HLock(pageh);
xfree(&(*pageh)->uri);
DisposeHandle(pageh);
@@ -296,7 +301,7 @@ browser_finished_loading(struct browser *browser)
{
struct URI *redir;
size_t size;
- bool download = false;
+ bool download = false, committed;
if ((*(browser->loading_page))->redir_to) {
browser_follow_redir(browser);
@@ -305,17 +310,22 @@ browser_finished_loading(struct browser *browser)
if ((*(browser->loading_page))->download_frefnum) {
FSClose((*(browser->loading_page))->download_frefnum);
- browser_statusf(browser, "Finished downloading %ld bytes",
+ browser_statusf(browser, "Downloaded (%ld bytes)",
(*(browser->loading_page))->download_len);
download = true;
}
+ committed = (browser->loading_page == browser->current_page);
browser_stop_loading_page(browser);
browser->redirs = 0;
if (download)
return;
+ if (browser->current_page == NULL)
+ /* browser_commit_to_loading_page never called */
+ Debugger();
+
/* release some memory if we can */
if ((*(browser->current_page))->content_size >
(*(browser->current_page))->content_len) {
@@ -323,9 +333,12 @@ browser_finished_loading(struct browser *browser)
(*(browser->current_page))->content_size = size;
HUnlock(browser->current_page);
SetHandleSize(browser->current_page, sizeof(struct page) + size);
+ HLock(browser->current_page);
}
- browser_statusf(browser, "Finished loading %ld bytes",
- (*(browser->current_page))->content_len);
+
+ if (committed)
+ browser_statusf(browser, "Done (%ld bytes)",
+ (*(browser->current_page))->content_len);
}
void
@@ -337,16 +350,16 @@ browser_stop_loading_page(struct browser *browser)
if (!browser->loading_page)
return;
- HLock(browser->loading_page);
page = *(browser->loading_page);
page->handler->cleanup(page);
- HUnlock(browser->loading_page);
if ((*(browser->loading_page))->download_frefnum)
FSClose((*(browser->loading_page))->download_frefnum);
-
+
/* only dispose if uncommitted */
- if (browser->current_page != browser->loading_page) {
+ if (browser->current_page == browser->loading_page)
+ HUnlock(browser->loading_page);
+ else {
xfree(&(*(browser->loading_page))->uri);
DisposeHandle(browser->loading_page);
}
@@ -365,9 +378,7 @@ browser_follow_redir(struct browser *browser)
if (!browser->loading_page)
return;
- HLock(browser->loading_page);
redir = (*(browser->loading_page))->redir_to;
- HUnlock(browser->loading_page);
browser_stop_loading_page(browser);
@@ -667,7 +678,11 @@ browser_go(struct browser *browser, short dir)
warn("Out of memory");
return;
}
+ HLock(browser->uri_te);
+ HLock((*(browser->uri_te))->hText);
memcpy(uristr, *((*(browser->uri_te))->hText), len);
+ HUnlock((*(browser->uri_te))->hText);
+ HUnlock(browser->uri_te);
uristr[len] = '\0';
browser_create_page(browser, uristr);
@@ -692,7 +707,13 @@ browser_go(struct browser *browser, short dir)
HUnlock(opage);
browser->loading_page = NULL;
browser_update_buttons(browser);
+ return;
}
+
+ (*opage)->content_pos = 0;
+ browser_commit_to_loading_page(browser);
+
+ HUnlock(opage);
}
}
@@ -781,11 +802,12 @@ browser_draw_status(struct browser *browser)
}
size_t
-browser_print(struct browser *browser, const char *str, size_t len)
+browser_print(struct browser *browser, const char *str, size_t len,
+ bool newline)
{
struct TVStyle style;
struct browser_link *link;
- size_t n;
+ size_t n, nlines;
style.font = BROWSER_FONT;
style.size = BROWSER_FONT_SIZE;
@@ -811,23 +833,28 @@ browser_print(struct browser *browser, const char *str
else if (browser->style & STYLE_H3)
style.size += 2;
- if (str[len - 1] == '\r' &&
- (browser->style & (STYLE_H1 | STYLE_H2 | STYLE_H3))) {
- /* print newlines in a small size */
- TVAppend(browser->output_tv, &style, (char *)str, len - 1);
- style.font = BROWSER_FONT;
- style.size = BROWSER_FONT_SIZE;
- style.style = 0;
- TVAppend(browser->output_tv, &style, "\r", 1);
- } else
- TVAppend(browser->output_tv, &style, (char *)str, len);
+ nlines = (*(browser->output_tv))->nlines;
+
+ (newline ? TVAppendLine : TVAppend)(browser->output_tv, &style,
+ (char *)str, len);
+ if ((*(browser->output_tv))->nlines != nlines)
+ TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
+
return len;
}
+void
+browser_recalc_scrollbar(struct browser *browser)
+{
+ TVCalcLines(browser->output_tv);
+ TVUpdate(browser->output_tv);
+ TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
+}
+
size_t
browser_print_link(struct browser *browser, const char *uri, size_t uri_len,
- const char *title, size_t title_len)
+ const char *title, size_t title_len, bool newline)
{
struct TVStyle style;
struct browser_link *link;
@@ -863,7 +890,7 @@ browser_print_link(struct browser *browser, const char
browser->last_link = link;
browser->style |= STYLE_LINK;
- browser_print(browser, link->title, link->len);
+ browser_print(browser, link->title, link->len, newline);
browser->style &= ~(STYLE_LINK);
return link->len;
@@ -883,13 +910,14 @@ browser_commit_to_loading_page(struct browser *browser
TESetText((*(browser->loading_page))->uri->str,
strlen((*(browser->loading_page))->uri->str), browser->uri_te);
TESetSelect(SHRT_MAX, SHRT_MAX, browser->uri_te);
-
+
r = (*(browser->uri_te))->viewRect;
TEUpdate(&r, browser->uri_te);
- TVAutoCalc(browser->output_tv, true);
TVClear(browser->output_tv);
TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
+ TVAutoCalc(browser->output_tv, true);
+ browser->style = STYLE_NONE;
if (browser->current_page) {
fpage = (*(browser->current_page))->fwd_page;
@@ -934,9 +962,11 @@ browser_start_download(struct browser *browser, char *
CtoPstr(buf);
SFPutFile(centered_sfput_dialog(), buf, pfilename, NULL, &reply);
- if (!reply.good)
+ if (!reply.good) {
+ browser_statusf(browser, "Download canceled");
return false;
-
+ }
+
error = Create(reply.fName, reply.vRefNum, '????', '????');
if (error && error != dupFNErr) {
warn("Failed to create file %s: %d", PtoCstr(reply.fName), error);
@@ -971,11 +1001,9 @@ browser_grow_page_content(struct page *page, size_t le
return NULL;
}
- HLock(pageh);
size = (*pageh)->content_size + MAX(len, PAGE_CONTENT_CHUNK_SIZE);
- HUnlock(pageh);
-
gsize = sizeof(struct page) + size;
+ HUnlock(pageh);
SetHandleSize(pageh, gsize);
if (MemError() != 0) {
warn("Out of memory growing page to %ld bytes", gsize);
--- browser.h Wed Nov 6 22:31:48 2024
+++ browser.h Thu Nov 7 16:55:32 2024
@@ -58,9 +58,10 @@ struct page {
size_t content_pos;
struct uri_handler *handler;
void *fetch_cookie;
- char type[32];
- short state;
short parse_state;
+ size_t server_content_len;
+ short server_status;
+ char content_type[32];
struct URI *redir_to;
short download_frefnum;
size_t download_len;
@@ -110,8 +111,10 @@ struct uri_handler {
struct browser * browser_init(void);
size_t browser_statusf(struct browser *browser, const char *format, ...);
size_t browser_print_link(struct browser *browser, const char *url,
- size_t url_len, const char *title, size_t title_len);
-size_t browser_print(struct browser *browser, const char *str, size_t len);
+ size_t url_len, const char *title, size_t title_len, bool newline);
+size_t browser_print(struct browser *browser, const char *str, size_t len,
+ bool newline);
+void browser_recalc_scrollbar(struct browser *browser);
struct page * browser_grow_page_content(struct page *page, size_t len);
void browser_go_uri(struct browser *browser, char *uristr);
void browser_commit_to_loading_page(struct browser *browser);
--- finger.c Wed Nov 6 22:07:25 2024
+++ finger.c Sat Nov 9 11:38:24 2024
@@ -54,12 +54,9 @@ finger_init_request(struct page *page)
{
struct finger_request *finger = NULL;
- if (page->content_len) {
- /* already fetched, nothing to do here but reset */
- page->content_pos = 0;
- browser_commit_to_loading_page(page->browser);
+ if (page->content_len)
+ /* already fetched */
return true;
- }
finger = xmalloczero(sizeof(struct finger_request));
if (finger == NULL) {
@@ -142,7 +139,7 @@ finger_fetch(struct page *page)
return false;
page->content_len += len;
- browser_statusf(page->browser, "Read %ld bytes", page->content_len);
+ browser_statusf(page->browser, "%ld bytes", page->content_len);
return true;
}
@@ -150,13 +147,12 @@ finger_fetch(struct page *page)
bool
finger_process(struct page *page)
{
- long n, plines = 0;
+ long n;
bool ret = true;
if (page->content_pos == page->content_len)
return (page->fetch_cookie != NULL);
- TVAutoCalc(page->browser->output_tv, false);
page->browser->style = STYLE_PRE;
/* text file, convert newlines and display */
@@ -164,46 +160,30 @@ finger_process(struct page *page)
if (page->content[n] == '\r') {
browser_print(page->browser,
page->content + page->content_pos,
- n - page->content_pos + 1);
+ n - page->content_pos + 1, false);
page->content_pos = n + 1;
if (page->content[n + 1] == '\n') {
page->content_pos++;
n++;
}
- plines++;
} else if (page->content[n] == '\n') {
/* lone \n */
if (n > page->content_pos)
browser_print(page->browser,
page->content + page->content_pos,
- n - page->content_pos);
- browser_print(page->browser, "\r", 1);
+ n - page->content_pos, true);
page->content_pos = n + 1;
- plines++;
}
-
- if (plines == 25) {
- TVCalcLines(page->browser->output_tv);
- TVUpdate(page->browser->output_tv);
- TVUpdateScrollbar(page->browser->output_tv,
- page->browser->output_tv_scroller);
- plines = 0;
- }
}
if (page->fetch_cookie == NULL &&
page->content_pos < page->content_len) {
/* no request to fetch more content, finish */
browser_print(page->browser, page->content + page->content_pos,
- page->content_len - page->content_pos);
+ page->content_len - page->content_pos, false);
page->content_pos = page->content_len;
ret = false;
}
-
- TVCalcLines(page->browser->output_tv);
- TVUpdate(page->browser->output_tv);
- TVUpdateScrollbar(page->browser->output_tv,
- page->browser->output_tv_scroller);
return ret;
}
--- gemini.c Wed Nov 6 22:07:32 2024
+++ gemini.c Fri Nov 8 08:56:25 2024
@@ -24,7 +24,7 @@
enum {
PARSE_STATE_HEADER,
- PARSE_STATE_GEMTEXT,
+ PARSE_STATE_BODY,
PARSE_STATE_DOWNLOAD
};
@@ -33,7 +33,6 @@ struct gemini_request {
char message[URI_MAX_STR_LEN + 1];
size_t message_len;
- short state;
};
struct URI * gemini_parse_uri(char *uristr);
@@ -64,9 +63,8 @@ gemini_init_request(struct page *page)
struct gemini_request *gemini = NULL;
if (page->content_len) {
- /* already fetched, nothing to do here but reset */
- page->content_pos = 0;
- page->parse_state = PARSE_STATE_HEADER;
+ /* already fetched */
+ page->parse_state = PARSE_STATE_BODY;
return true;
}
@@ -113,22 +111,20 @@ gemini_fetch_tls_write_callback(struct tls_request *re
size_t *len)
{
struct gemini_request *gemini;
- page_handle pageh;
struct page *page;
+ page_handle pageh;
size_t tlen;
- if (cookie == NULL)
- return false;
-
pageh = (page_handle)cookie;
- HLock(pageh);
+ if (pageh == NULL)
+ return false;
page = *pageh;
- gemini = (struct gemini_request *)page->fetch_cookie;
+ if (page == NULL)
+ return false;
- if (page == NULL) {
- HUnlock(pageh);
+ gemini = (struct gemini_request *)page->fetch_cookie;
+ if (gemini == NULL)
return false;
- }
if (wrote) {
if (*len == gemini->message_len) {
@@ -145,8 +141,6 @@ gemini_fetch_tls_write_callback(struct tls_request *re
*len = tlen;
}
- HUnlock(pageh);
-
return true;
}
@@ -157,29 +151,36 @@ gemini_fetch_tls_read_callback(struct tls_request *req
page_handle pageh;
struct page *page;
size_t tlen;
+ short err;
- if (cookie == NULL)
- return false;
-
pageh = (page_handle)cookie;
- HLock(pageh);
+ if (pageh == NULL)
+ return false;
page = *pageh;
-
- if (page == NULL) {
- HUnlock(pageh);
+ if (page == NULL)
return false;
- }
- if (page->content_len + len >= page->content_size) {
- page = browser_grow_page_content(page, len);
- if (page == NULL)
+ if (page->parse_state == PARSE_STATE_DOWNLOAD) {
+ tlen = len;
+ err = FSWrite(page->download_frefnum, &tlen, buf);
+ if (err || tlen != len) {
+ warn("Failed to write %ld bytes: %d", len, err);
return false;
+ }
+
+ page->download_len += tlen;
+ browser_statusf(page->browser, "%ld bytes", page->download_len);
+ } else {
+ if (page->content_len + len >= page->content_size) {
+ page = browser_grow_page_content(page, len);
+ if (page == NULL)
+ return false;
+ }
+
+ memcpy(page->content + page->content_len, buf, len);
+ page->content_len += len;
+ browser_statusf(page->browser, "%ld bytes", page->content_len);
}
-
- memcpy(page->content + page->content_len, buf, len);
- page->content_len += len;
- browser_statusf(browser, "Read %ld bytes", page->content_len);
- HUnlock(pageh);
return true;
}
@@ -202,8 +203,6 @@ gemini_fetch(struct page *page)
gemini_fetch_tls_read_callback, pageh,
gemini_fetch_tls_write_callback, pageh);
- HUnlock(pageh);
-
return ret;
}
@@ -212,21 +211,19 @@ gemini_process(struct page *page)
{
page_handle pageh;
size_t n, trail, skip, len;
- char c;
- bool newline, ret;
+ ssize_t tlen, link_len, title_len, j;
+ char *link, *title, *tcontent;
+ struct URI *newuri;
+ short err;
+ char c, *filename;
+ bool newline, gemtext;
pageh = (page_handle)RecoverHandle(page);
if (page->content_pos == page->content_len)
return (page->fetch_cookie != NULL);
-handle_state:
- ret = true;
-
- switch (page->parse_state) {
- case PARSE_STATE_HEADER: {
- short status;
-
+ if (page->parse_state == PARSE_STATE_HEADER) {
for (n = page->content_pos; n < page->content_len; n++) {
c = page->content[n];
@@ -239,243 +236,262 @@ handle_state:
"Header parsing failed, disconnecting"));
return false;
}
-
- if (strncmp(page->type, "text/gemini", 10) == 0) {
- page->parse_state = PARSE_STATE_GEMTEXT;
+
+ /* ignore any "; charset" after type */
+ if (strncasecmp(page->content_type, "text/gemini", 11) == 0 ||
+ strncasecmp(page->content_type, "text/plain", 10) == 0) {
+ page->parse_state = PARSE_STATE_BODY;
browser_commit_to_loading_page(page->browser);
- HLock(pageh);
- page = *pageh;
- } else
+ } else {
page->parse_state = PARSE_STATE_DOWNLOAD;
+
+ filename = strrchr(page->uri->path, '/');
+ if (filename && filename[0] == '/')
+ filename++;
+ if (!browser_start_download(page->browser, filename))
+ return false;
+ }
+
page->content_pos = n + 1;
- /* avoid a round trip through idle handler */
- goto handle_state;
+ /* gemini only has one header */
+ break;
}
- if (page->fetch_cookie == NULL)
- /* no more data will be coming */
- return false;
+ if (page->parse_state == PARSE_STATE_BODY) {
+ /* shift out the header */
+ if (page->content_pos == page->content_len)
+ page->content_pos = page->content_len = 0;
+ else {
+ memmove(page->content, page->content + page->content_pos,
+ page->content_len - page->content_pos);
+ page->content_len -= page->content_pos;
+ page->content_pos = 0;
+ }
+ } else if (page->parse_state == PARSE_STATE_DOWNLOAD) {
+ /* dump remaining data after header into our new file */
+ tlen = len = page->content_len - page->content_pos;
+ if (tlen) {
+ err = FSWrite(page->download_frefnum, &tlen,
+ page->content + page->content_pos);
+ if (err || tlen != len) {
+ warn("Failed to write %ld bytes: %d", len, err);
+ return false;
+ }
+ page->download_len = tlen;
+ }
- break;
+ /* reset for future writing */
+ page->content_pos = page->content_len = 0;
+ return true;
+ }
}
- case PARSE_STATE_DOWNLOAD:
- /* TODO */
- break;
- case PARSE_STATE_GEMTEXT:
- TVAutoCalc(page->browser->output_tv, false);
+
+ if (page->parse_state != PARSE_STATE_BODY)
+ return true;
+
+ gemtext = (strncasecmp(page->content_type, "text/gemini", 11) == 0);
+ if (!gemtext)
+ page->browser->style = STYLE_PRE;
+
+ for (n = page->content_pos; n < page->content_len; n++) {
+ if (page->content[n] != '\n' &&
+ !(n == page->content_len - 1 && page->fetch_cookie == NULL))
+ continue;
- for (n = page->content_pos; n < page->content_len; n++) {
- if (page->content[n] != '\n' &&
- !(n == page->content_len - 1 && page->fetch_cookie == NULL))
- continue;
+ len = n - page->content_pos + 1;
+ trail = 0;
+ skip = 0;
+ newline = false;
+
+ if (page->content[n] == '\n') {
+ len--;
+ trail = 1;
+ newline = true;
- len = n - page->content_pos + 1;
- trail = 0;
- skip = 0;
- newline = false;
-
- if (page->content[n] == '\n') {
+ if (n > 0 && page->content[n - 1] == '\r') {
len--;
- trail = 1;
- newline = true;
-
- if (n > 0 && page->content[n - 1] == '\r') {
- len--;
- trail++;
- }
- } else if (page->fetch_cookie != NULL) {
- /*
- * No newline found at the end of the buffer, but the
- * buffer isn't full so wait for more data.
- */
- break;
+ trail++;
}
+ } else if (page->fetch_cookie != NULL)
+ /* no newline at the end and fetching, so wait for more data */
+ return true;
- /* check for pre first because the other styles can be in it */
- if (page->content[page->content_pos] == '`' &&
- page->content[page->content_pos + 1] == '`' &&
- page->content[page->content_pos + 2] == '`') {
- /* ``` toggle */
- if (page->browser->style & STYLE_PRE)
- page->browser->style = STYLE_NONE;
- else
- page->browser->style = STYLE_PRE;
-
- /* the rest of the line can be a description, ignore */
- trail += len;
- len = 0;
- newline = false;
- goto print_line;
- } else if (page->browser->style & STYLE_PRE)
- goto print_line;
+ if (!gemtext)
+ goto print_line;
- if (page->content[page->content_pos] == '=' &&
- page->content[page->content_pos + 1] == '>') {
- /* link */
- ssize_t link_len = 0;
- ssize_t title_len = 0;
- ssize_t j, tlen;
- char *link = NULL;
- char *title = NULL;
- char *tcontent;
- struct URI *newuri;
-
- skip = 2;
- len -= 2;
- tlen = len;
-
- tcontent = page->content + page->content_pos + skip;
- link = tcontent;
-
- /* skip leading whitespace */
- while (len && (c = *tcontent) && (c == ' ' || c == '\t')) {
- tcontent++;
- link++;
- len--;
- }
-
- /* consume link */
- while (len && (c = *tcontent) && c != ' ' && c != '\t') {
- tcontent++;
- len--;
- link_len++;
- }
-
- /* consume whitespace after link */
- while (len && (c = *tcontent) && (c == ' ' || c == '\t')) {
- tcontent++;
- len--;
- }
-
- if (len) {
- title = tcontent;
- title_len = len;
-
- /* trim trailing whitespace from title */
- while (title_len && (c = title[title_len - 1]) &&
- (c == ' ' || c == '\t')) {
- title_len--;
- }
-
- if (title_len == 0)
- title = NULL;
- }
-
- newuri = build_relative_uri(page->uri, link, link_len);
- if (newuri) {
- browser_print_link(page->browser,
- newuri->str, strlen(newuri->str), title, title_len);
- xfree(&newuri);
- } else
- browser_print_link(page->browser,
- link, link_len, title, title_len);
+ /* check for pre first because the other styles can be in it */
+ if (page->content[page->content_pos] == '`' &&
+ page->content[page->content_pos + 1] == '`' &&
+ page->content[page->content_pos + 2] == '`') {
+ /* ``` toggle */
+ if (page->browser->style & STYLE_PRE)
page->browser->style = STYLE_NONE;
- browser_print(page->browser, "\r", 1);
- page->content_pos += skip + tlen + trail;
- continue;
- }
+ else
+ page->browser->style = STYLE_PRE;
- if (page->content[page->content_pos] == '#' &&
- page->content[page->content_pos + 1] == '#' &&
- page->content[page->content_pos + 2] == '#') {
- /* ### h3 */
- page->browser->style = STYLE_H3;
- skip = 3;
- len -= 3;
- while ((c = page->content[page->content_pos + skip]) &&
- (c == ' ' || c == '\t')) {
- skip++;
- len--;
- }
- goto print_line;
- }
+ /* the rest of the line can be a description, ignore */
+ trail += len;
+ len = 0;
+ newline = false;
+ goto print_line;
+ } else if (page->browser->style & STYLE_PRE)
+ goto print_line;
+
+ if (page->content[page->content_pos] == '=' &&
+ page->content[page->content_pos + 1] == '>') {
+ /* link */
+ link_len = 0;
+ title_len = 0;
+ link = NULL;
+ title = NULL;
- if (page->content[page->content_pos] == '#' &&
- page->content[page->content_pos + 1] == '#') {
- /* ## h2 */
- page->browser->style = STYLE_H2;
- skip = 2;
- len -= 2;
- while ((c = page->content[page->content_pos + skip]) &&
- (c == ' ' || c == '\t')) {
- skip++;
- len--;
- }
- goto print_line;
- }
+ skip = 2;
+ len -= 2;
+ tlen = len;
- if (page->content[page->content_pos] == '#') {
- /* # h1 */
- page->browser->style = STYLE_H1;
- skip = 1;
+ tcontent = page->content + page->content_pos + skip;
+ link = tcontent;
+
+ /* skip leading whitespace */
+ while (len && (c = *tcontent) && (c == ' ' || c == '\t')) {
+ tcontent++;
+ link++;
len--;
- while ((c = page->content[page->content_pos + skip]) &&
- (c == ' ' || c == '\t')) {
- skip++;
- len--;
- }
- goto print_line;
}
- if (page->content[page->content_pos] == '*') {
- /* * list item */
- page->browser->style = STYLE_LIST;
- skip = 1;
+ /* consume link */
+ while (len && (c = *tcontent) && c != ' ' && c != '\t') {
+ tcontent++;
len--;
- while ((c = page->content[page->content_pos + skip]) &&
- (c == ' ' || c == '\t')) {
- skip++;
- len--;
- }
- browser_print(page->browser, " • ", 3);
- goto print_line;
+ link_len++;
}
- if (page->content[page->content_pos] == '>') {
- /* > quote text */
- page->browser->style = STYLE_QUOTE;
- skip = 1;
+ /* consume whitespace after link */
+ while (len && (c = *tcontent) && (c == ' ' || c == '\t')) {
+ tcontent++;
len--;
- while ((c = page->content[page->content_pos + skip]) &&
+ }
+
+ if (len) {
+ title = tcontent;
+ title_len = len;
+
+ /* trim trailing whitespace from title */
+ while (title_len && (c = title[title_len - 1]) &&
(c == ' ' || c == '\t')) {
- skip++;
- len--;
+ title_len--;
}
- goto print_line;
+
+ if (title_len == 0)
+ title = NULL;
}
-
-print_line:
- if (len)
- browser_print(page->browser,
- page->content + page->content_pos + skip, len);
- page->content_pos += skip + len + trail;
-
- if (!(page->browser->style & STYLE_PRE))
- page->browser->style = STYLE_NONE;
- if (newline)
- browser_print(page->browser, "\r", 1);
+ newuri = build_relative_uri(page->uri, link, link_len);
+ if (newuri) {
+ browser_print_link(page->browser, newuri->str,
+ strlen(newuri->str), title, title_len, true);
+ xfree(&newuri);
+ } else
+ browser_print_link(page->browser, link, link_len, title,
+ title_len, true);
+ page->browser->style = STYLE_NONE;
+ page->content_pos += skip + tlen + trail;
+ continue;
}
- if (page->fetch_cookie == NULL &&
- page->content_pos < page->content_len) {
- browser_print(page->browser, page->content + page->content_pos,
- page->content_len - page->content_pos);
- page->content_pos = page->content_len;
- ret = false;
+ if (page->content[page->content_pos] == '#' &&
+ page->content[page->content_pos + 1] == '#' &&
+ page->content[page->content_pos + 2] == '#') {
+ /* ### h3 */
+ page->browser->style = STYLE_H3;
+ skip = 3;
+ len -= 3;
+ while ((c = page->content[page->content_pos + skip]) &&
+ (c == ' ' || c == '\t')) {
+ skip++;
+ len--;
+ }
+ goto print_line;
}
- TVAutoCalc(page->browser->output_tv, true);
- TVCalcLines(page->browser->output_tv);
- TVUpdate(page->browser->output_tv);
- TVUpdateScrollbar(page->browser->output_tv,
- page->browser->output_tv_scroller);
- break;
+ if (page->content[page->content_pos] == '#' &&
+ page->content[page->content_pos + 1] == '#') {
+ /* ## h2 */
+ page->browser->style = STYLE_H2;
+ skip = 2;
+ len -= 2;
+ while ((c = page->content[page->content_pos + skip]) &&
+ (c == ' ' || c == '\t')) {
+ skip++;
+ len--;
+ }
+ goto print_line;
+ }
+
+ if (page->content[page->content_pos] == '#') {
+ /* # h1 */
+ page->browser->style = STYLE_H1;
+ skip = 1;
+ len--;
+ while ((c = page->content[page->content_pos + skip]) &&
+ (c == ' ' || c == '\t')) {
+ skip++;
+ len--;
+ }
+ goto print_line;
+ }
+
+ if (page->content[page->content_pos] == '*') {
+ /* * list item */
+ page->browser->style = STYLE_LIST;
+ skip = 1;
+ len--;
+ while ((c = page->content[page->content_pos + skip]) &&
+ (c == ' ' || c == '\t')) {
+ skip++;
+ len--;
+ }
+ browser_print(page->browser, " • ", 3, false);
+ goto print_line;
+ }
+
+ if (page->content[page->content_pos] == '>') {
+ /* > quote text */
+ page->browser->style = STYLE_QUOTE;
+ skip = 1;
+ len--;
+ while ((c = page->content[page->content_pos + skip]) &&
+ (c == ' ' || c == '\t')) {
+ skip++;
+ len--;
+ }
+ goto print_line;
+ }
+
+print_line:
+ if (len || newline)
+ browser_print(page->browser,
+ page->content + page->content_pos + skip, len, newline);
+
+ page->content_pos += skip + len + trail;
+
+ if (!(page->browser->style & STYLE_PRE))
+ page->browser->style = STYLE_NONE;
}
- return ret;
+ if (page->fetch_cookie == NULL &&
+ page->content_pos < page->content_len) {
+ browser_print(page->browser, page->content + page->content_pos,
+ page->content_len - page->content_pos, false);
+ page->content_pos = page->content_len;
+ }
+
+ if (page->content_pos == page->content_len)
+ return (page->fetch_cookie != NULL);
+
+ return true;
}
static bool
@@ -491,44 +507,44 @@ parse_header(struct page *page, char *str, size_t len)
return false;
}
- status = ((str[0] - '0') * 10) + (str[1] - '0');
+ page->server_status = ((str[0] - '0') * 10) + (str[1] - '0');
memcpy(fail, str, MIN(len, sizeof(fail)));
fail[MIN(len, sizeof(fail) - 1)] = '\0';
- if (status >= 10 && status <= 19) {
+ if (page->server_status >= 10 && page->server_status <= 19) {
/* input, not supported */
browser_statusf(page->browser, "Error: Input not supported (%d)",
- status);
+ page->server_status);
return false;
}
- if (status >= 20 && status <= 29) {
+ if (page->server_status >= 20 && page->server_status <= 29) {
/* success */
- if (len + 3 + 1 > sizeof(page->type))
- len = sizeof(page->type) - 1;
- memcpy(page->type, str + 3, len - 2);
- page->type[len - 2] = '\0';
+ if (len + 3 + 1 > sizeof(page->content_type))
+ len = sizeof(page->content_type) - 1;
+ memcpy(page->content_type, str + 3, len - 2);
+ page->content_type[len - 2] = '\0';
return true;
}
- if (status >= 30 && status <= 39) {
+ if (page->server_status >= 30 && page->server_status <= 39) {
/* redirect */
page->redir_to = build_relative_uri(page->uri, str + 3, len - 2);
/* TODO: infinite loop detection */
return false;
}
- if (status >= 40 && status <= 49) {
+ if (page->server_status >= 40 && page->server_status <= 49) {
/* temp fail */
browser_statusf(page->browser, "Error: Server reported temporary "
"failure: %s", fail);
return false;
}
- if (status >= 50 && status <= 59) {
+ if (page->server_status >= 50 && page->server_status <= 59) {
/* perm fail */
browser_statusf(page->browser, "Error: Server reported "
"permanent failure: %s", fail);
return false;
}
- if (status >= 60 && status <= 69) {
+ if (page->server_status >= 60 && page->server_status <= 69) {
/* auth, not supported */
browser_statusf(page->browser, "Error: Auth not supported (%d)",
status);
--- gopher.c Wed Nov 6 22:07:39 2024
+++ gopher.c Sat Nov 9 11:54:06 2024
@@ -22,6 +22,9 @@
#define GOPHER_PORT 70
+/* printing line-by-line is slow, so only recalc every n lines */
+#define RECALC_EVERY_N_LINES 25
+
static const char showable_types[] = "013i";
struct gopher_request {
@@ -60,9 +63,8 @@ gopher_init_request(struct page *page)
char *filename;
if (page->content_len) {
- /* already fetched, nothing to do here but reset */
- page->content_pos = 0;
- browser_commit_to_loading_page(page->browser);
+ /* already fetched */
+ page->parse_state = 0;
return true;
}
@@ -87,17 +89,17 @@ gopher_init_request(struct page *page)
*/
if (page->uri->path[0] == '\0' ||
(page->uri->path[0] == '/' && page->uri->path[1] == '\0')) {
- page->type[0] = '1';
+ page->content_type[0] = '1';
gopher->selector_len = snprintf(gopher->selector,
sizeof(gopher->selector), "\r\n");
} else {
/* /0/blah -> blah */
- page->type[0] = page->uri->path[1];
+ page->content_type[0] = page->uri->path[1];
gopher->selector_len = snprintf(gopher->selector,
sizeof(gopher->selector), "%s\r\n", page->uri->path + 2);
}
- browser_statusf(page->browser, "Connected to %s, sending request...",
+ browser_statusf(page->browser, "Connected to %s, sending selector...",
page->uri->hostname);
if (!request_tcp_send(&gopher->tcp, page->browser, gopher->selector,
@@ -109,7 +111,7 @@ gopher_init_request(struct page *page)
page->fetch_cookie = gopher;
- if (strchr(showable_types, page->type[0]) == NULL) {
+ if (strchr(showable_types, page->content_type[0]) == NULL) {
if (gopher->selector_len == 2)
filename = NULL;
else {
@@ -157,21 +159,15 @@ gopher_fetch(struct page *page)
if (len == 0)
return true;
- if (page->download_frefnum) {
- if (len > page->content_size)
- len = page->content_size;
- buf = page->content;
- } else {
- if (page->content_len + len >= page->content_size) {
- page = browser_grow_page_content(page, len);
- if (page == NULL)
- return false;
- gopher = (struct gopher_request *)(page->fetch_cookie);
- }
- buf = page->content + page->content_len;
+ if (page->content_len + len >= page->content_size) {
+ page = browser_grow_page_content(page, len);
+ if (page == NULL)
+ return false;
+ gopher = (struct gopher_request *)(page->fetch_cookie);
}
- len = request_tcp_read(&gopher->tcp, page->browser, buf, len);
+ len = request_tcp_read(&gopher->tcp, page->browser,
+ page->content + page->content_len, len);
if (len < 0)
return false;
@@ -182,15 +178,13 @@ gopher_fetch(struct page *page)
warn("Failed to write %ld bytes: %d", len, err);
return false;
}
-
page->download_len += wlen;
- browser_statusf(page->browser, "Wrote %ld bytes",
- page->download_len);
+ browser_statusf(page->browser, "%ld bytes", page->download_len);
} else {
page->content_len += len;
- browser_statusf(page->browser, "Read %ld bytes", page->content_len);
+ browser_statusf(page->browser, "%ld bytes", page->content_len);
- if (strchr(showable_types, page->type[0]) &&
+ if (strchr(showable_types, page->content_type[0]) &&
page->content_len >= 3 &&
(page->content_len == 3 ||
page->content[page->content_len - 4] == '\n') &&
@@ -217,7 +211,7 @@ gopher_process(struct page *page)
TVAutoCalc(page->browser->output_tv, false);
- if (page->type[0] == '1') {
+ if (page->content_type[0] == '1') {
/* gopher menu */
for (n = page->content_pos; n < page->content_len; n++) {
if (n > 0 && page->content[n - 1] == '\r' &&
@@ -226,15 +220,6 @@ gopher_process(struct page *page)
gopher_print_menu(page, page->content +
page->content_pos, n - page->content_pos);
page->content_pos = n + 1;
- plines++;
-
- if (plines == 25) {
- TVCalcLines(page->browser->output_tv);
- TVUpdate(page->browser->output_tv);
- TVUpdateScrollbar(page->browser->output_tv,
- page->browser->output_tv_scroller);
- plines = 0;
- }
}
}
@@ -245,58 +230,44 @@ gopher_process(struct page *page)
page->content_pos = page->content_len;
ret = false;
}
- } else if (page->type[0] == '0') {
+ } else if (page->content_type[0] == '0') {
/* text file, convert newlines and display */
for (n = page->content_pos; n < page->content_len; n++) {
if (page->content[n] == '\r') {
browser_print(page->browser,
page->content + page->content_pos,
- n - page->content_pos + 1);
+ n - page->content_pos + 1, false);
page->content_pos = n + 1;
if (page->content[n + 1] == '\n') {
page->content_pos++;
n++;
}
- plines++;
} else if (page->content[n] == '\n') {
/* lone \n */
if (n > page->content_pos)
browser_print(page->browser,
page->content + page->content_pos,
- n - page->content_pos);
- browser_print(page->browser, "\r", 1);
+ n - page->content_pos, true);
page->content_pos = n + 1;
- plines++;
}
-
- if (plines == 25) {
- TVCalcLines(page->browser->output_tv);
- TVUpdate(page->browser->output_tv);
- TVUpdateScrollbar(page->browser->output_tv,
- page->browser->output_tv_scroller);
- plines = 0;
- }
}
if (page->fetch_cookie == NULL &&
page->content_pos < page->content_len) {
browser_print(page->browser, page->content + page->content_pos,
- page->content_len - page->content_pos);
+ page->content_len - page->content_pos, false);
page->content_pos = page->content_len;
ret = false;
}
} else {
- /* anything else, just download it */
+ /* anything else, just show it */
browser_print(page->browser, page->content + page->content_pos,
- page->content_len - page->content_pos);
+ page->content_len - page->content_pos, false);
page->content_pos = page->content_len;
ret = false;
}
- TVCalcLines(page->browser->output_tv);
- TVUpdate(page->browser->output_tv);
- TVUpdateScrollbar(page->browser->output_tv,
- page->browser->output_tv_scroller);
+ browser_recalc_scrollbar(page->browser);
return ret;
}
@@ -333,12 +304,11 @@ gopher_print_menu(struct page *page, char *line, size_
switch (type) {
case 'i':
/* informational */
- browser_print(page->browser, label, strlen(label));
- browser_print(page->browser, "\r", 1);
+ browser_print(page->browser, label, strlen(label), true);
break;
default:
snprintf(prefix, sizeof(prefix), "[%c] ", type);
- browser_print(page->browser, prefix, 4);
+ browser_print(page->browser, prefix, 4, false);
if (type == 'h' && strncmp(selector, "URL:", 4) == 0)
tlen = strlcpy(uri, selector + 4, sizeof(uri));
@@ -347,9 +317,9 @@ gopher_print_menu(struct page *page, char *line, size_
type, selector);
page->browser->style |= STYLE_BOLD;
- browser_print_link(page->browser, uri, tlen, label, strlen(label));
+ browser_print_link(page->browser, uri, tlen, label, strlen(label),
+ true);
page->browser->style &= ~(STYLE_BOLD);
- browser_print(page->browser, "\r", 1);
break;
}
@@ -357,4 +327,8 @@ gopher_print_menu(struct page *page, char *line, size_
if (line[n] == '\0')
line[n] = '\t';
}
+
+ /* stuff number of lines printed into parse state */
+ if (++(page->parse_state) % RECALC_EVERY_N_LINES == 0)
+ browser_recalc_scrollbar(page->browser);
}
\ No newline at end of file
--- main.c Wed Nov 6 22:06:06 2024
+++ main.c Thu Nov 7 21:42:43 2024
@@ -62,7 +62,7 @@ main(void)
menu_defaults();
AppendMenu(bookmarks_menu, "\p.");
-SetItem(bookmarks_menu, 1, "\pgemini://geminiprotocol.net/");
+SetItem(bookmarks_menu, 1, "\pgemini://geminiprotocol.net/history/");
AppendMenu(bookmarks_menu, "\p.");
SetItem(bookmarks_menu, 2, "\pgopher://gopher.floodgap.com/");