diff --git a/www-client/netsurf/Makefile b/www-client/netsurf/Makefile index 1951dfe..79e39f3 100644 --- a/www-client/netsurf/Makefile +++ b/www-client/netsurf/Makefile @@ -86,6 +86,12 @@ NETSURF_ARGS := \ # it references. COMPAT_LIB := $(CURDIR)/compat/libnscompat.a +# NetSurf formats floats (page load time in the status bar, plot coordinates). +# The Montauk libc keeps %e/%f/%g in a separate archive member reached by a +# WEAK reference, and a weak undefined ref does not extract a member -- without +# this the status bar reads "Done (%fs)". See programs/lib/libc/printf_float.c. +NS_LDFLAGS := $(COMPAT_LIB) -Wl,-u,_pf_putfloat + $(COMPAT_LIB): compat/iconv.c compat/iconv.h $(CC) $(NETSURF_CFLAGS) -std=gnu99 -c compat/iconv.c -o compat/iconv.o $(AR) rcs $@ compat/iconv.o @@ -96,7 +102,7 @@ all: netsurf-build netsurf-build: $(COMPAT_LIB) @$(call ns_patch,$(UPSTREAM)) - CFLAGS="$(NETSURF_CFLAGS)" LDFLAGS="$(COMPAT_LIB)" $(NS_ENV) \ + CFLAGS="$(NETSURF_CFLAGS)" LDFLAGS="$(NS_LDFLAGS)" $(NS_ENV) \ $(NS_MAKE) -C $(UPSTREAM) $(NETSURF_ARGS) @test -f $(NS_BUILD_OUT) || { echo "netsurf: expected $(NS_BUILD_OUT)" >&2; exit 1; } @echo "Built: $(NS_BUILD_OUT) ($$(stat -c%s $(NS_BUILD_OUT)) bytes)" @@ -108,7 +114,12 @@ netsurf-build: $(COMPAT_LIB) install: netsurf-build mkdir -p $(NS_INSTALL)/netsurf cp $(NS_BUILD_OUT) $(NS_INSTALL)/nsmonkey.elf - cp -r $(UPSTREAM)/frontends/monkey/res/. $(NS_INSTALL)/netsurf/ + # -L, not -r alone: nearly everything in a NetSurf frontend res/ dir is a + # symlink into ../../../resources/. Copied as links they dangle outside + # the installed tree, and the ramdisk turns a symlink into a ZERO-LENGTH + # FILE (Fs/Ramdisk.cpp only handles typeflag '5'), so default.css and + # welcome.html arrive empty and every page lays out to nothing. + cp -rL $(UPSTREAM)/frontends/monkey/res/. $(NS_INSTALL)/netsurf/ @echo "installed: 0:/os/nsmonkey.elf + 0:/os/netsurf/ resources" uninstall: diff --git a/www-client/netsurf/compat/sys/select.h b/www-client/netsurf/compat/sys/select.h index fc10683..eb03088 100644 --- a/www-client/netsurf/compat/sys/select.h +++ b/www-client/netsurf/compat/sys/select.h @@ -11,19 +11,20 @@ * NETSURF_USE_CURL=NO no fetcher registers a descriptor, so the only fd that * ever appears is the monkey frontend's stdin (fd 0). * - * That makes a useful shim possible: + * That makes a useful shim possible: fd 0 is answered from the libc's + * montauk_stdin_ready(), which reports whether a COMPLETE line is buffered. + * That is the readiness question monkey is really asking -- it goes on to call + * fgets(), so reporting "readable" on a half-typed line would park the whole + * browser inside stdio until Enter. With no line pending we sleep out the + * caller's timeout and return 0, which lets the core's scheduled callbacks run + * to completion: a fetch takes many main-loop iterations, and under the old + * always-ready shim it got exactly one per line typed. * - * - if any read descriptor is requested, report them ready immediately and - * let the caller block in read(). monkey is a line-driven REPL, so - * blocking on stdin is what it wants anyway. - * - otherwise sleep for the timeout so an idle loop does not spin. - * - * LIMITATION: because a requested read fd is always reported ready, scheduled - * callbacks do not fire while the process sits idle waiting for a command. - * That is fine for driving monkey by hand or by script, and it is wrong for - * anything with real network activity. When fetch_montauk lands it will - * register descriptors here and this needs replacing with a genuine - * waitset-backed implementation. + * LIMITATION: only fd 0 has a readiness source. Any other read descriptor is + * still reported ready unconditionally, and write/except sets are always + * empty. With NETSURF_USE_CURL=NO no fetcher registers a descriptor, so fd 0 + * is the only one that ever appears. When fetch_montauk lands it will register + * descriptors here and this needs a genuine waitset-backed implementation. */ #ifndef _MONTAUK_COMPAT_SYS_SELECT_H_ #define _MONTAUK_COMPAT_SYS_SELECT_H_ @@ -31,6 +32,7 @@ #include #include #include +#include #define FD_SETSIZE 64 @@ -80,6 +82,7 @@ static inline int montauk_fdset_count(const fd_set *s, int nfds) static inline int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { + int stdin_wanted; int ready; if (writefds != NULL) @@ -87,21 +90,57 @@ static inline int select(int nfds, fd_set *readfds, fd_set *writefds, if (exceptfds != NULL) FD_ZERO(exceptfds); - if (montauk_fdset_empty(readfds) == 0) { - /* leave readfds as-is: every requested fd is "ready" */ - return montauk_fdset_count(readfds, nfds); + stdin_wanted = (readfds != NULL) && (nfds > 0) && FD_ISSET(0, readfds); + if (stdin_wanted) { + FD_CLR(0, readfds); + if (montauk_stdin_ready()) { + FD_SET(0, readfds); + } } - ready = 0; + /* + * Descriptors other than stdin have no readiness source here, so they + * keep the old unconditional-ready answer. + */ + ready = montauk_fdset_count(readfds, nfds); + if (ready > 0) + return ready; - if (timeout != NULL) { + if (timeout == NULL) { + /* + * The core has nothing scheduled ("POLL BLOCKING"), so blocking + * for a command is right -- and returning 0 here instead would + * spin the CPU at 100%. + */ + if (stdin_wanted && montauk_stdin_wait()) { + FD_SET(0, readfds); + return 1; + } + return 0; + } + + { unsigned long usec = (unsigned long) timeout->tv_sec * 1000000UL + (unsigned long) timeout->tv_usec; - if (usec != 0) - usleep(usec); + + /* + * Sleep in slices rather than one long usleep: a scheduled + * timeout can be seconds long, and typing must not wait it out. + */ + while (usec > 0) { + unsigned long slice = (usec > 5000UL) ? 5000UL : usec; + + usleep(slice); + usec -= slice; + + if (stdin_wanted && montauk_stdin_ready()) { + FD_SET(0, readfds); + return 1; + } + } } - return ready; + return 0; } #endif diff --git a/www-client/netsurf/patches/0001-config-detect-montaukos.patch b/www-client/netsurf/patches/0001-config-detect-montaukos.patch index c8b7008..b0632c6 100644 --- a/www-client/netsurf/patches/0001-config-detect-montaukos.patch +++ b/www-client/netsurf/patches/0001-config-detect-montaukos.patch @@ -1,5 +1,5 @@ diff --git a/utils/config.h b/utils/config.h -index 3914771fd..09f2659d6 100644 +index 3914771fd..578280321 100644 --- a/utils/config.h +++ b/utils/config.h @@ -119,7 +119,7 @@ char *strchrnul(const char *s, int c); @@ -11,7 +11,22 @@ index 3914771fd..09f2659d6 100644 #undef HAVE_UTSNAME #endif -@@ -158,14 +158,14 @@ char *realpath(const char *path, char *resolved_path); +@@ -144,8 +144,13 @@ char *realpath(const char *path, char *resolved_path); + #undef HAVE_STDOUT + #endif + ++/* MontaukOS: the libc mmap() is anonymous-only (it is a thin wrapper over ++ * SYS_ALLOC and rejects any fd != -1), so the file fetcher's ++ * mmap(..., MAP_SHARED, fd, 0) fails and every file: URL dies with "Unable to ++ * map memory for file data buffer". The fread() fallback in the same function ++ * is what we want. */ + #define HAVE_MMAP +-#if (defined(_WIN32) || defined(__riscos__) || defined(__HAIKU__) || defined(__BEOS__) || defined(__amigaos4__) || defined(__AMIGA__) || defined(__MINT__)) ++#if (defined(_WIN32) || defined(__riscos__) || defined(__HAIKU__) || defined(__BEOS__) || defined(__amigaos4__) || defined(__AMIGA__) || defined(__MINT__) || defined(__montauk__)) + #undef HAVE_MMAP + #endif + +@@ -158,14 +163,14 @@ char *realpath(const char *path, char *resolved_path); #define HAVE_DIRFD #define HAVE_UNLINKAT #define HAVE_FSTATAT diff --git a/www-client/netsurf/patches/0003-file-url-drive-prefix.patch b/www-client/netsurf/patches/0003-file-url-drive-prefix.patch new file mode 100644 index 0000000..6bde4b7 --- /dev/null +++ b/www-client/netsurf/patches/0003-file-url-drive-prefix.patch @@ -0,0 +1,35 @@ +diff --git a/utils/file.c b/utils/file.c +index 75a8a1c03..a8ab70f67 100644 +--- a/utils/file.c ++++ b/utils/file.c +@@ -152,6 +152,30 @@ static nserror posix_nsurl_to_path(struct nsurl *url, char **path_out) + return res; + } + ++#if defined(__montauk__) ++ /* ++ * MontaukOS absolute paths are drive-qualified ("0:/dir/file"). The ++ * file: URL syntax requires a leading slash before the path, so the ++ * round trip through path_to_nsurl() yields "/0%3A/dir/file", which ++ * unescapes to "/0:/dir/file". Resolving that treats the leading slash ++ * as "root of the current drive" and leaves the drive prefix as a ++ * directory name, producing "0:/0:/dir/file". ++ * ++ * Drop the slash when what follows is a drive prefix, restoring the ++ * original path. ++ */ ++ if (path[0] == '/') { ++ const char *p = path + 1; ++ ++ while (*p >= '0' && *p <= '9') { ++ p++; ++ } ++ if ((p > path + 1) && (*p == ':')) { ++ memmove(path, path + 1, strlen(path)); ++ } ++ } ++#endif ++ + *path_out = path; + + return NSERROR_OK; diff --git a/www-client/netsurf/patches/0004-monkey-window-dump-diagnostic.patch b/www-client/netsurf/patches/0004-monkey-window-dump-diagnostic.patch new file mode 100644 index 0000000..5d88f9b --- /dev/null +++ b/www-client/netsurf/patches/0004-monkey-window-dump-diagnostic.patch @@ -0,0 +1,97 @@ +diff --git a/frontends/monkey/browser.c b/frontends/monkey/browser.c +index 958375486..6b406f552 100644 +--- a/frontends/monkey/browser.c ++++ b/frontends/monkey/browser.c +@@ -27,10 +27,13 @@ + #include "utils/log.h" + #include "utils/messages.h" + #include "utils/nsurl.h" ++#include "utils/nsoption.h" + #include "netsurf/mouse.h" + #include "netsurf/window.h" + #include "netsurf/browser_window.h" ++#include "netsurf/content_type.h" + #include "netsurf/plotters.h" ++#include "css/utils.h" + + #include "monkey/output.h" + #include "monkey/browser.h" +@@ -599,6 +602,69 @@ monkey_window_handle_redraw(int argc, char **argv) + moutf(MOUT_WINDOW, "REDRAW WIN %d STOP", atoi(argv[2])); + } + ++/** ++ * WINDOW DUMP [path] ++ * ++ * Port-local diagnostic. The layout dump goes to a file because it is ++ * hundreds of lines, then the head of it is echoed so the box geometry is ++ * visible without any way to get a file off the ramdisk. The LENGTHS line ++ * carries the inputs every CSS length conversion depends on -- if the DPI or ++ * font sizes come out wrong, every box is wrong and nothing plots. ++ */ ++static void ++monkey_window_handle_dump(int argc, char **argv) ++{ ++ const char *path = (argc > 3) ? argv[3] : "0:/users/admin/nsbox.txt"; ++ struct gui_window *gw; ++ char line[512]; ++ int lineno; ++ FILE *f; ++ ++ if (argc != 3 && argc != 4) { ++ moutf(MOUT_ERROR, "WINDOW DUMP ARGS BAD"); ++ return; ++ } ++ ++ gw = monkey_find_window_by_num(atoi(argv[2])); ++ if (gw == NULL) { ++ moutf(MOUT_ERROR, "WINDOW NUM BAD"); ++ return; ++ } ++ ++ /* Raw css_fixed (22.10), not FIXTOINT: a value that is merely small ++ * rather than zero has to stay visible. */ ++ moutf(MOUT_WINDOW, ++ "DUMP WIN %s LENGTHS DPI %d FONT_SIZE %d FONT_MIN %d SCALE %d", ++ argv[2], (int) nscss_screen_dpi, nsoption_int(font_size), ++ nsoption_int(font_min_size), nsoption_int(scale)); ++ ++ f = fopen(path, "w"); ++ if (f == NULL) { ++ moutf(MOUT_ERROR, "WINDOW DUMP OPEN FAILED %s", path); ++ return; ++ } ++ ++ browser_window_debug_dump(gw->bw, f, CONTENT_DEBUG_RENDER); ++ fclose(f); ++ ++ f = fopen(path, "r"); ++ if (f == NULL) { ++ moutf(MOUT_ERROR, "WINDOW DUMP REOPEN FAILED %s", path); ++ return; ++ } ++ ++ for (lineno = 0; lineno < 40; lineno++) { ++ if (fgets(line, sizeof(line), f) == NULL) { ++ break; ++ } ++ line[strcspn(line, "\n")] = '\0'; ++ moutf(MOUT_WINDOW, "DUMP WIN %s BOX %s", argv[2], line); ++ } ++ fclose(f); ++ ++ moutf(MOUT_WINDOW, "DUMP WIN %s END LINES %d", argv[2], lineno); ++} ++ + static void + monkey_window_handle_reload(int argc, char **argv) + { +@@ -721,6 +787,8 @@ monkey_window_handle_command(int argc, char **argv) + monkey_window_handle_stop(argc, argv); + } else if (strcmp(argv[1], "REDRAW") == 0) { + monkey_window_handle_redraw(argc, argv); ++ } else if (strcmp(argv[1], "DUMP") == 0) { ++ monkey_window_handle_dump(argc, argv); + } else if (strcmp(argv[1], "RELOAD") == 0) { + monkey_window_handle_reload(argc, argv); + } else if (strcmp(argv[1], "EXEC") == 0) { diff --git a/www-client/netsurf/patches/0005-fetcherror-recursion-guard.patch b/www-client/netsurf/patches/0005-fetcherror-recursion-guard.patch new file mode 100644 index 0000000..48a1949 --- /dev/null +++ b/www-client/netsurf/patches/0005-fetcherror-recursion-guard.patch @@ -0,0 +1,29 @@ +diff --git a/desktop/browser_window.c b/desktop/browser_window.c +index 226741be5..57da7d56e 100644 +--- a/desktop/browser_window.c ++++ b/desktop/browser_window.c +@@ -1338,6 +1338,24 @@ browser_window__handle_fetcherror(struct browser_window *bw, + + memset(¶ms, 0, sizeof(params)); + ++ /* If it is the error page itself that failed, navigating to the error ++ * page again lands straight back here. Nothing bounds that: the pair ++ * recurses, allocating a content per turn, until the heap is gone and ++ * the real failure is buried under hundreds of retries. Report the ++ * reason and stop instead. */ ++ if (nsurl_compare(url, corestring_nsurl_about_query_fetcherror, ++ NSURL_COMPLETE)) { ++ NSLOG(netsurf, WARNING, ++ "error page itself failed: %s -- not retrying", reason); ++ fprintf(stderr, ++ "netsurf: error page failed (%s); giving up\n", reason); ++ browser_window_set_status(bw, reason); ++ return browser_window_stop_throbber(bw); ++ } ++ ++ fprintf(stderr, "netsurf: fetch of '%s' failed: %s\n", ++ nsurl_access(url), reason); ++ + params.url = nsurl_ref(corestring_nsurl_about_query_fetcherror); + params.referrer = nsurl_ref(url); + params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;