From 1e2814af87aa29351809872adb6b0b233c41552d Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 23 May 2026 19:03:41 +0900 Subject: [PATCH] more convenient internet accessing using net.mjs --- .../hopper/bin/{hop.per => hopper.hop.per} | 2 +- assets/disk0/nettest.js | 8 +- assets/disk0/tvdos/include/net.mjs | 123 ++++++++++++++++++ 3 files changed, 129 insertions(+), 4 deletions(-) rename assets/disk0/hopper/bin/{hop.per => hopper.hop.per} (76%) create mode 100644 assets/disk0/tvdos/include/net.mjs diff --git a/assets/disk0/hopper/bin/hop.per b/assets/disk0/hopper/bin/hopper.hop.per similarity index 76% rename from assets/disk0/hopper/bin/hop.per rename to assets/disk0/hopper/bin/hopper.hop.per index dc06dee..d46e664 100644 --- a/assets/disk0/hopper/bin/hop.per +++ b/assets/disk0/hopper/bin/hopper.hop.per @@ -9,4 +9,4 @@ ProperAuthor:CuriousTorvald ProperDescription:Package manager for TVDOS Licence:MIT SupportMe:https://github.com/sponsors/curioustorvald/ -PackageFileList:https://raw.githubusercontent.com/curioustorvald/hopper/refs/heads/master/hopper.js; +PackageFileList:https://raw.githubusercontent.com/curioustorvald/hopper/refs/heads/master/hopper.js;https://raw.githubusercontent.com/curioustorvald/hopper/refs/heads/master/hop.alias diff --git a/assets/disk0/nettest.js b/assets/disk0/nettest.js index 8f8ea83..7a9bef0 100644 --- a/assets/disk0/nettest.js +++ b/assets/disk0/nettest.js @@ -1,11 +1,13 @@ -let url="http:localhost/testnet/test.txt" +/*let url="https:raw.githubusercontent.com/curioustorvald/hopper-mirror/refs/heads/master/aa.hop.per" let file = files.open("B:\\"+url) if (!file.exists) { printerrln("No such URL: "+url) return 1 -} +}*/ -let text = file.sread() +let net = require("A:/tvdos/include/net.mjs") +let text = net.fetchText("https://raw.githubusercontent.com/curioustorvald/hopper-mirror/refs/heads/master/aa.hop.per") +if (text === null) { printerrln("No such URL"); return 1 } println(text) diff --git a/assets/disk0/tvdos/include/net.mjs b/assets/disk0/tvdos/include/net.mjs new file mode 100644 index 0000000..1b39ca5 --- /dev/null +++ b/assets/disk0/tvdos/include/net.mjs @@ -0,0 +1,123 @@ +/* + * net.mjs — Internet text-fetch helper for TVDOS + * + * Wraps the HttpModem peripheral (driven by `_TVDOS.DRV.FS.NET`, see + * TVDOS.SYS:1001-1034) behind a small, regular-URL-friendly API. The + * helper looks up whichever drive letter the boot probe assigned to the + * HTTP modem and translates ordinary URLs (`https://host/path`) into the + * scheme-without-double-slash form (`https:host/path`) that the modem + * expects on the wire. + * + * Usage + * ----- + * let net = require("A:/tvdos/include/net.mjs") + * + * if (!net.isAvailable()) + * printerrln("No HTTP modem attached") + * + * let body = net.fetchText("https://example.com/index.html") + * if (body === null) printerrln("Fetch failed") + * else println(body) + */ + + +let _cachedDrive = null + +/** Scan TVDOS drive table for an HTTP-typed device. Returns the drive + * letter (e.g. "B") or null. */ +function _findHttpDrive() { + if (typeof _TVDOS === 'undefined' || !_TVDOS.DRIVEINFO) return null + if (_cachedDrive !== null && _TVDOS.DRIVEINFO[_cachedDrive] && + _TVDOS.DRIVEINFO[_cachedDrive].type === 'HTTP') + return _cachedDrive + + for (let letter in _TVDOS.DRIVEINFO) { + let info = _TVDOS.DRIVEINFO[letter] + if (info && info.type === 'HTTP') { + _cachedDrive = letter + return letter + } + } + return null +} + +/** Convert a regular URL into the form the HTTP modem accepts: + * - strip the `//` between scheme and authority + * - drop any URL fragment + * - assume `https` when no scheme is provided + */ +function _normaliseUrl(url) { + if (typeof url !== 'string') + throw new TypeError("url must be a string") + let s = url.trim() + if (s.length === 0) throw new Error("url is empty") + + // Drop fragment — the modem speaks to the server, # is client-side. + let hash = s.indexOf('#') + if (hash >= 0) s = s.substring(0, hash) + + // scheme://host/path → scheme:host/path + let m = s.match(/^([a-zA-Z][a-zA-Z0-9+.\-]*):\/\/(.*)$/) + if (m) return m[1].toLowerCase() + ':' + m[2] + + // Already in scheme:host/path form (the modem's native shape) + if (/^[a-zA-Z][a-zA-Z0-9+.\-]*:[^/]/.test(s)) return s + + // No scheme — default to https + if (!/^[a-zA-Z][a-zA-Z0-9+.\-]*:/.test(s)) + return 'https:' + s.replace(/^\/\//, '') + + return s +} + + +let net = {} + +/** Returns the drive letter currently bound to the HTTP modem, or null + * when no such device is attached. */ +net.getHttpDrive = function () { + return _findHttpDrive() +} + +/** True iff an HTTP modem is reachable through TVDOS. */ +net.isAvailable = function () { + return _findHttpDrive() !== null +} + +/** Translate a URL into the `:\` form that + * `files.open()` would route through `_TVDOS.DRV.FS.NET`. Useful when + * another component wants the descriptor directly. Throws if no HTTP + * modem is attached. */ +net.toModemPath = function (url) { + let drive = _findHttpDrive() + if (drive === null) throw new Error("No HTTP modem device is attached") + return drive + ':\\' + _normaliseUrl(url) +} + +/** Open a TVDOS file descriptor backed by the HTTP modem for the given + * URL. The descriptor's sread()/bread() trigger the actual fetch. + * Throws if no HTTP modem is attached. */ +net.open = function (url) { + return files.open(net.toModemPath(url)) +} + +/** Fetch the body of `url` as a string. Returns the response text on + * success, or null when the modem reports a non-zero status (bad URL, + * I/O error, etc.). Throws if no HTTP modem is attached. */ +net.fetchText = function (url) { + let fd = net.open(url) + let text = fd.sread() + try { fd.close() } catch (_) {} + return (text === undefined) ? null : text +} + +/** Like fetchText, but throws an Error instead of returning null on + * fetch failure. */ +net.fetchTextOrThrow = function (url) { + let body = net.fetchText(url) + if (body === null) throw new Error("Failed to fetch URL: " + url) + return body +} + + +exports = net