New utils

This commit is contained in:
Raven Scott
2026-04-03 23:04:42 -04:00
parent eeef3da6e1
commit f42f50eb73
214 changed files with 17562 additions and 615 deletions
+4 -1
View File
@@ -2,6 +2,8 @@
Files in this directory are **read from disk by the seeder** (or copied into `packages/bare-os-seeder/kernel/` for Pear) and written into the **system Hyperdrive** with **no temporary directory** on the host.
**This `README.md` file** only documents the tree layout in the repository; the seeder **does not** install it as **`/README.md`** on the image (so the guest root directory stays free of repo docs).
## Staging map (seeder)
| Source | Drive path |
@@ -12,11 +14,12 @@ Files in this directory are **read from disk by the seeder** (or copied into `pa
| `share/man/...` | `/share/man/...` |
| `lib/bare/...` | `/lib/bare/...` (optional **`ctx.bare`** bundles; see **`bare-os-bare-libs`**) |
| Any other file | `/<relative path>` |
| `README.md` (this file) | *(skipped — not copied to `/README.md`)* |
## Contents
- **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; digit-prefixed names only; skip dotfiles, `*~`, `README*`, `*.md`; optional **`BARE_OS_RC_D_SKIP`** comma list and **`prefix*`** patterns) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** or **`/etc/bare-os/onboot`** → **`readLine` / `execLine`** loop. Boot **`execLine`** errors in trusted snippets are logged; with **`BARE_OS_BOOT_STRICT=1`** or **`true`**, the first throw calls **`requestBooterExit(1)`** and stops later boot phases. Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)).
- **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md). Each file is **`runtime.js`** + optional **`lib/*-engine.js`** (**`sed`**, **`awk`**), **`lib/man-render.js`** (**`man`**), **`lib/edit-*.js`** (**`edit`** / **`nano`** — same **`src/edit.js`**), lscolors chunks (**`ls`**, **`dircolors`**), etc. + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**). **`/bin/nano`** duplicates **`/bin/edit`** for familiarity; the shells default **`nano``edit`** alias uses the **`edit`** command name after expansion.
- **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md) (**~111** commands; list in **`packages/bare-os-coreutils/lib/commands.mjs`**). Each file is **`runtime.js`** + optional preamble (**`lib/md5.js`** for **`md5sum`**, **`lib/*-engine.js`** for **`sed`**/**`awk`**, **`jq-engine.js`**, **`lib/man-render.js`**, **`lib/edit-*.js`** for **`edit`**/**`nano`**, lscolors for **`ls`**/**`dircolors`**, …) + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**). **`/bin/nano`** duplicates **`/bin/edit`** for familiarity; the shells default **`nano``edit`** alias uses the **`edit`** command name after expansion. **`dir`**/**`vdir`** invoke **`ls`** via **`ctx.runBinCommand`**.
- **`lib/bare/`** — Optional IIFE bundles + **`manifest.json`** for **`ctx.bare`** drive merge, built by [bare-os-bare-libs](../packages/bare-os-bare-libs/README.md). Same trust model as **`bin/`** (trusted seeded image).
- **`share/man/man.json`** — Merged manual database for **`/bin/man`** (built by **`bare-os-coreutils`**; see [handbook ch.10](../handbook/10-manpages-and-online-help.md)).
- **`etc/os-release`** — Static OS metadata (`NAME`, `VERSION`, …).
+106
View File
@@ -0,0 +1,106 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log('usage: arch\nPrint machine hardware name (same idea as uname -m).')
return
}
if (argv[i].startsWith('-')) {
ctx.console.error('arch: unknown option ' + argv[i])
ctx.exitCode = 1
return
}
}
const e = ctx.vfs.env || {}
ctx.console.log(
e.PROCESSOR_ARCHITECTURE || e.MACHINE || e.BARE_OS_ARCH || 'unknown'
)
}
+200
View File
@@ -0,0 +1,200 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
const B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
function bareB32EncodeBytes(u8) {
let out = ''
let i = 0
let buf = 0
let bits = 0
for (; i < u8.length; i++) {
buf = (buf << 8) | u8[i]
bits += 8
while (bits >= 5) {
bits -= 5
out += B32[(buf >> bits) & 31]
}
}
if (bits > 0) out += B32[(buf << (5 - bits)) & 31]
while (out.length % 8 !== 0) out += '='
return out
}
function bareB32DecodeToU8(s) {
const t = String(s).replace(/\s+/g, '').replace(/=+$/, '')
let buf = 0
let bits = 0
const bytes = []
for (let i = 0; i < t.length; i++) {
const c = t[i]
const v = B32.indexOf(c)
if (v < 0) throw new Error('invalid base32 character')
buf = (buf << 5) | v
bits += 5
if (bits >= 8) {
bits -= 8
bytes.push((buf >> bits) & 255)
}
}
return new Uint8Array(bytes)
}
async function run(ctx, argv) {
let decode = false
let wrap = 0
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: base32 [-d] [-w COLS] [FILE]\n' +
'RFC 4648 Base32; -d decodes to raw bytes.'
)
ctx.exitCode = 0
return
}
if (a === '-d' || a === '--decode') {
decode = true
continue
}
if ((a === '-w' || a === '--wrap') && argv[i + 1]) {
wrap = Number.parseInt(argv[++i], 10)
if (!Number.isFinite(wrap) || wrap < 0) wrap = 0
continue
}
if (a.startsWith('-')) {
ctx.console.error('base32: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
let buf
if (!paths.length || paths[0] === '-') {
buf = b4.from(bareStdin(ctx))
} else {
const b = await ctx.vfs.readFile(paths[0])
if (!b) {
ctx.console.error('base32: cannot read ' + paths[0])
ctx.exitCode = 1
return
}
buf = b instanceof Uint8Array ? b : new Uint8Array(b)
}
if (decode) {
const text = ctx.b4a.toString(buf)
let raw
try {
raw = bareB32DecodeToU8(text)
} catch (e) {
ctx.console.error('base32: ' + (e.message || e))
ctx.exitCode = 1
return
}
if (!bareOsEmitRaw(ctx, raw)) {
ctx.console.error(
'base32: decode output requires process.stdout.write or ctx.bareOsBinWrite'
)
ctx.exitCode = 1
}
return
}
let enc = bareB32EncodeBytes(buf)
if (wrap > 0) {
const lines = []
for (let i = 0; i < enc.length; i += wrap) {
lines.push(enc.slice(i, i + wrap))
}
ctx.console.log(lines.join('\n'))
} else {
ctx.console.log(enc)
}
}
+210
View File
@@ -0,0 +1,210 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* base64 — encode or decode Base64 (RFC 4648).
* Uses btoa/atob or Buffer when available; otherwise a small inline encoder.
*/
function bareB64EncodeBytes(u8) {
const B = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
let out = ''
let i = 0
for (; i + 2 < u8.length; i += 3) {
const n = (u8[i] << 16) | (u8[i + 1] << 8) | u8[i + 2]
out += B[(n >> 18) & 63] + B[(n >> 12) & 63] + B[(n >> 6) & 63] + B[n & 63]
}
const rest = u8.length - i
if (rest === 1) {
const n = u8[i] << 16
out += B[(n >> 18) & 63] + B[(n >> 12) & 63] + '=='
} else if (rest === 2) {
const n = (u8[i] << 16) | (u8[i + 1] << 8)
out += B[(n >> 18) & 63] + B[(n >> 12) & 63] + B[(n >> 6) & 63] + '='
}
return out
}
function bareB64DecodeToU8(s) {
const t = String(s).replace(/\s+/g, '')
if (typeof globalThis.Buffer !== 'undefined') {
return new Uint8Array(globalThis.Buffer.from(t, 'base64'))
}
if (typeof globalThis.atob === 'function') {
const bin = globalThis.atob(t)
const out = new Uint8Array(bin.length)
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i) & 255
return out
}
throw new Error('base64 decode requires Buffer or atob')
}
async function run(ctx, argv) {
let decode = false
let wrap = 76
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: base64 [-d] [-w COLS] [FILE]\n' +
' base64 --decode [-w COLS] [FILE]\n' +
'Default: encode stdin or FILE; -d/--decode decodes to raw bytes on stdout.'
)
ctx.exitCode = 0
return
}
if (a === '-d' || a === '--decode') {
decode = true
continue
}
if ((a === '-w' || a === '--wrap') && argv[i + 1]) {
wrap = Number.parseInt(argv[++i], 10)
if (!Number.isFinite(wrap) || wrap < 0) wrap = 0
continue
}
if (a.startsWith('-')) {
ctx.console.error('base64: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
let buf
if (!paths.length || paths[0] === '-') {
buf = b4.from(bareStdin(ctx))
} else {
const b = await ctx.vfs.readFile(paths[0])
if (!b) {
ctx.console.error('base64: cannot read ' + paths[0])
ctx.exitCode = 1
return
}
buf = b instanceof Uint8Array ? b : new Uint8Array(b)
}
if (decode) {
const text = new TextDecoder().decode(buf)
let raw
try {
raw = bareB64DecodeToU8(text)
} catch (e) {
ctx.console.error('base64: ' + (e.message || e))
ctx.exitCode = 1
return
}
if (!bareOsEmitRaw(ctx, raw)) {
ctx.console.error(
'base64: decode output requires process.stdout.write or ctx.bareOsBinWrite'
)
ctx.exitCode = 1
}
return
}
let enc
if (typeof globalThis.btoa === 'function') {
let s = ''
const step = 0x8000
for (let i = 0; i < buf.length; i += step) {
const chunk = buf.subarray(i, i + step)
s += String.fromCharCode.apply(null, chunk)
}
enc = globalThis.btoa(s)
} else {
enc = bareB64EncodeBytes(buf)
}
if (wrap > 0) {
const lines = []
for (let i = 0; i < enc.length; i += wrap) {
lines.push(enc.slice(i, i + wrap))
}
ctx.console.log(lines.join('\n'))
} else {
ctx.console.log(enc)
}
}
+175
View File
@@ -0,0 +1,175 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function bareHexEncode(u8) {
let s = ''
for (let i = 0; i < u8.length; i++) {
s += u8[i].toString(16).padStart(2, '0')
}
return s
}
function bareHexDecode(s) {
const t = String(s).replace(/\s+/g, '')
if (t.length % 2 !== 0) throw new Error('odd hex length')
const out = new Uint8Array(t.length / 2)
for (let i = 0; i < out.length; i++) {
out[i] = parseInt(t.slice(i * 2, i * 2 + 2), 16)
if (!Number.isFinite(out[i])) throw new Error('invalid hex')
}
return out
}
async function run(ctx, argv) {
let decode = false
let base16 = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: basenc --base16 [-d] [FILE]\n' +
'Encode or decode hex (Base16). Other bases are not implemented.'
)
ctx.exitCode = 0
return
}
if (a === '-d' || a === '--decode') {
decode = true
continue
}
if (a === '--base16') {
base16 = true
continue
}
if (a.startsWith('-')) {
ctx.console.error('basenc: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
if (!base16) {
ctx.console.error('basenc: requires --base16')
ctx.exitCode = 1
return
}
const b4 = ctx.b4a
let buf
if (!paths.length || paths[0] === '-') {
buf = b4.from(bareStdin(ctx))
} else {
const b = await ctx.vfs.readFile(paths[0])
if (!b) {
ctx.console.error('basenc: cannot read ' + paths[0])
ctx.exitCode = 1
return
}
buf = b instanceof Uint8Array ? b : new Uint8Array(b)
}
if (decode) {
const text = b4.toString(buf)
let raw
try {
raw = bareHexDecode(text)
} catch (e) {
ctx.console.error('basenc: ' + (e.message || e))
ctx.exitCode = 1
return
}
if (!bareOsEmitRaw(ctx, raw)) {
ctx.console.error(
'basenc: decode output requires process.stdout.write or ctx.bareOsBinWrite'
)
ctx.exitCode = 1
}
return
}
ctx.console.log(bareHexEncode(buf))
}
+164
View File
@@ -0,0 +1,164 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function readLines(ctx, path) {
const b4 = ctx.b4a
if (path === '-') return bareStdin(ctx).split('\n')
const b = await ctx.vfs.readFile(path)
if (!b) return null
return b4.toString(b).split('\n')
}
async function run(ctx, argv) {
let c1 = true
let c2 = true
let c3 = true
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: comm [-123] FILE1 FILE2\nCompare sorted files; columns: only FILE1, only FILE2, both.'
)
return
}
if (a === '-1') c1 = false
else if (a === '-2') c2 = false
else if (a === '-3') c3 = false
else if (a.startsWith('-')) {
ctx.console.error('comm: unsupported option ' + a)
ctx.exitCode = 1
return
} else paths.push(a)
}
if (paths.length !== 2) {
ctx.console.error('usage: comm [-123] FILE1 FILE2')
ctx.exitCode = 1
return
}
const A = await readLines(ctx, paths[0])
const B = await readLines(ctx, paths[1])
if (!A || !B) {
ctx.console.error('comm: missing input file')
ctx.exitCode = 1
return
}
const trimNl = (arr) => {
if (arr.length && arr[arr.length - 1] === '') arr.pop()
return arr
}
const a = trimNl(A.slice())
const b = trimNl(B.slice())
let i = 0
let j = 0
while (i < a.length || j < b.length) {
if (i >= a.length) {
if (c2) ctx.console.log('\t' + b[j])
j++
continue
}
if (j >= b.length) {
if (c1) ctx.console.log(a[i])
i++
continue
}
const cmp = a[i].localeCompare(b[j])
if (cmp < 0) {
if (c1) ctx.console.log(a[i])
i++
} else if (cmp > 0) {
if (c2) ctx.console.log('\t' + b[j])
j++
} else {
if (c3) ctx.console.log('\t\t' + a[i])
i++
j++
}
}
}
+61 -7
View File
@@ -87,7 +87,10 @@ function bareOsEmitRaw(ctx, chunk) {
return false
}
async function copyPath(ctx, from, to, recursive, followSymlink) {
async function copyPath(ctx, from, to, recursive, followSymlink, opts) {
const preserve = opts && opts.preserveTime
const update = opts && opts.update
const verbose = opts && opts.verbose
const st = await ctx.vfs.lstat(from)
if (!st) {
ctx.console.error('cp: ' + from + ': No such file')
@@ -97,6 +100,7 @@ async function copyPath(ctx, from, to, recursive, followSymlink) {
if (!followSymlink) {
const t = await ctx.vfs.readlink(from)
await ctx.vfs.symlink(t, to)
if (verbose) ctx.console.log("'" + from + "' -> '" + to + "'")
return true
}
const fst = await ctx.vfs.stat(from)
@@ -106,7 +110,19 @@ async function copyPath(ctx, from, to, recursive, followSymlink) {
ctx.console.error('cp: cannot read ' + from)
return false
}
await ctx.vfs.writeFile(to, buf)
if (update) {
const dst = await ctx.vfs.lstat(to)
if (dst && dst.mtimeMs >= fst.mtimeMs) {
if (verbose) ctx.console.log('skipped: ' + to)
return true
}
}
const wopts =
preserve && typeof fst.mtimeMs === 'number'
? { mtimeMs: fst.mtimeMs, ctimeMs: fst.ctimeMs }
: {}
await ctx.vfs.writeFile(to, buf, wopts)
if (verbose) ctx.console.log("'" + from + "' -> '" + to + "'")
return true
}
if (fst.type === 'directory') {
@@ -120,8 +136,10 @@ async function copyPath(ctx, from, to, recursive, followSymlink) {
if (n === '.bareos_empty') continue
const f = from.replace(/\/+$/, '') + '/' + n
const t = to.replace(/\/+$/, '') + '/' + n
if (!(await copyPath(ctx, f, t, true, followSymlink))) return false
if (!(await copyPath(ctx, f, t, true, followSymlink, opts)))
return false
}
if (verbose) ctx.console.log("'" + from + "' -> '" + to + "'")
return true
}
ctx.console.error('cp: cannot copy special file ' + from)
@@ -133,7 +151,19 @@ async function copyPath(ctx, from, to, recursive, followSymlink) {
ctx.console.error('cp: cannot read ' + from)
return false
}
await ctx.vfs.writeFile(to, buf)
if (update) {
const dst = await ctx.vfs.lstat(to)
if (dst && dst.mtimeMs >= st.mtimeMs) {
if (verbose) ctx.console.log('skipped: ' + to)
return true
}
}
const wopts =
preserve && typeof st.mtimeMs === 'number'
? { mtimeMs: st.mtimeMs, ctimeMs: st.ctimeMs }
: {}
await ctx.vfs.writeFile(to, buf, wopts)
if (verbose) ctx.console.log("'" + from + "' -> '" + to + "'")
return true
}
if (st.type === 'directory') {
@@ -147,8 +177,9 @@ async function copyPath(ctx, from, to, recursive, followSymlink) {
if (n === '.bareos_empty') continue
const f = from.replace(/\/+$/, '') + '/' + n
const t = to.replace(/\/+$/, '') + '/' + n
if (!(await copyPath(ctx, f, t, true, followSymlink))) return false
if (!(await copyPath(ctx, f, t, true, followSymlink, opts))) return false
}
if (verbose) ctx.console.log("'" + from + "' -> '" + to + "'")
return true
}
return false
@@ -157,6 +188,9 @@ async function copyPath(ctx, from, to, recursive, followSymlink) {
async function run(ctx, argv) {
let recursive = false
let followSymlink = false
let update = false
let verbose = false
let preserveTime = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
@@ -172,6 +206,23 @@ async function run(ctx, argv) {
followSymlink = false
continue
}
if (a === '-u' || a === '--update') {
update = true
continue
}
if (a === '-v' || a === '--verbose') {
verbose = true
continue
}
if (a === '--preserve' || a === '-p') {
preserveTime = true
continue
}
if (a.startsWith('--preserve=')) {
const v = a.slice('--preserve='.length)
if (v === 'timestamps' || v === 'time' || v === 'all') preserveTime = true
continue
}
if (a === '--') {
paths.push(...argv.slice(i + 1))
break
@@ -184,7 +235,9 @@ async function run(ctx, argv) {
paths.push(a)
}
if (paths.length < 2) {
ctx.console.error('usage: cp [-R] [-L|-P] SOURCE... DEST')
ctx.console.error(
'usage: cp [-R] [-L|-P] [-u] [-v] [-p|--preserve[=timestamps]] SOURCE... DEST'
)
ctx.exitCode = 1
return
}
@@ -202,13 +255,14 @@ async function run(ctx, argv) {
ctx.exitCode = 1
return
}
const opts = { preserveTime, update, verbose }
for (const src of sources) {
const base = src.replace(/\/+$/, '').split('/').pop() || src
const target =
destIsDir || sources.length > 1
? dest.replace(/\/+$/, '') + '/' + base
: dest
if (!(await copyPath(ctx, src, target, recursive, followSymlink)))
if (!(await copyPath(ctx, src, target, recursive, followSymlink, opts)))
ctx.exitCode = 1
}
}
+149
View File
@@ -0,0 +1,149 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let human = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '--help') {
ctx.console.log(
'usage: df [-h] [FILE]...\nSynthetic disk free for Bare OS (Hyperdrive model; not block devices).'
)
return
}
if (a === '-h' || a === '--human-readable') {
human = true
continue
}
if (a.startsWith('-')) {
ctx.console.error('df: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
let totalK = 1024 * 1024
let usedK = 4096
try {
const buf = await ctx.vfs.readFile('/proc/bare_os_quotas')
if (buf) {
const j = JSON.parse(ctx.b4a.toString(buf))
if (j && typeof j.pipelineMaxBytes === 'number')
totalK = Math.max(1024, Math.ceil(j.pipelineMaxBytes / 1024) * 1024)
}
} catch {
/* ignore */
}
const freeK = Math.max(0, totalK - usedK)
const pct =
totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0
function fmt(n) {
if (!human) return String(n)
if (n < 1024) return n + 'K'
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + 'M'
return (n / 1024 / 1024).toFixed(1) + 'G'
}
const mount = paths.length ? paths[0] : '/'
ctx.console.log(
'Filesystem 1K-blocks Used Available Use% Mounted on'
)
ctx.console.log(
'bare-hyperdrive ' +
String(totalK).padStart(12) +
' ' +
String(usedK).padStart(8) +
' ' +
String(freeK).padStart(10) +
' ' +
String(pct).padStart(3) +
'% ' +
mount
)
}
+97
View File
@@ -0,0 +1,97 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
if (typeof ctx.runBinCommand !== 'function') {
ctx.console.error('dir: runBinCommand not available')
ctx.exitCode = 1
return
}
await ctx.runBinCommand(['ls', '-C'].concat(argv.slice(1)))
}
+177
View File
@@ -0,0 +1,177 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function parseUniformWidth(tabArg) {
const n = parseInt(String(tabArg).split(',')[0].trim(), 10)
return Number.isFinite(n) && n > 0 ? n : 8
}
function nextTabCol(col, w) {
return (Math.floor(col / w) + 1) * w
}
function expandLine(line, w) {
let col = 0
let out = ''
for (let i = 0; i < line.length; i++) {
const ch = line[i]
if (ch === '\t') {
const target = nextTabCol(col, w)
while (col < target) {
out += ' '
col++
}
} else {
out += ch
if (ch === '\n' || ch === '\r') col = 0
else col++
}
}
return out
}
async function run(ctx, argv) {
let tabArg = '8'
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: expand [-t N] [FILE]...\nConvert tabs to spaces (uniform tab width N, default 8).'
)
return
}
if (a === '-t' && argv[i + 1]) {
tabArg = argv[++i]
continue
}
if (a.startsWith('-t') && a.length > 2) {
tabArg = a.slice(2)
continue
}
if (a.startsWith('--tabs=')) {
tabArg = a.slice(7)
continue
}
if (a.startsWith('-')) {
ctx.console.error('expand: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const w = parseUniformWidth(tabArg)
const b4 = ctx.b4a
function proc(text) {
const lines = text.split('\n')
for (let li = 0; li < lines.length; li++) {
const isLast = li === lines.length - 1
const line = lines[li]
if (isLast && line === '' && lines.length > 1) continue
ctx.console.log(expandLine(line, w))
}
}
if (!paths.length) {
proc(bareStdin(ctx))
return
}
for (const p of paths) {
if (p === '-') {
proc(bareStdin(ctx))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('expand: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
proc(b4.toString(b))
}
}
+185
View File
@@ -0,0 +1,185 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
const tokens = argv.slice(1)
if (!tokens.length || tokens[0] === '--help' || tokens[0] === '-h') {
ctx.console.log(
'usage: expr EXPRESSION\nInteger arithmetic (+ - * / %), comparisons, and string = / !=.'
)
return
}
let i = 0
const peek = () => tokens[i]
const take = () => tokens[i++]
function parsePrimary() {
const t = take()
if (t === '(') {
const v = parseAdd()
if (take() !== ')') throw new Error('syntax error')
return v
}
if (/^-?\d+$/.test(t)) return { kind: 'n', v: parseInt(t, 10) }
return { kind: 's', v: t }
}
function parseMul() {
let left = parsePrimary()
while (peek() === '*' || peek() === '/' || peek() === '%') {
const op = take()
const right = parsePrimary()
if (left.kind !== 'n' || right.kind !== 'n') throw new Error('non-numeric')
if (op === '*') left = { kind: 'n', v: left.v * right.v }
else if (op === '/') {
if (right.v === 0) throw new Error('division by zero')
left = { kind: 'n', v: Math.trunc(left.v / right.v) }
} else {
if (right.v === 0) throw new Error('division by zero')
left = { kind: 'n', v: left.v % right.v }
}
}
return left
}
function parseAdd() {
let left = parseMul()
while (peek() === '+' || peek() === '-') {
const op = take()
const right = parseMul()
if (left.kind !== 'n' || right.kind !== 'n') throw new Error('non-numeric')
left = {
kind: 'n',
v: op === '+' ? left.v + right.v : left.v - right.v
}
}
return left
}
function parseCmp() {
let left = parseAdd()
const op = peek()
if (
op === '=' ||
op === '==' ||
op === '!=' ||
op === '<' ||
op === '<=' ||
op === '>' ||
op === '>='
) {
take()
const right = parseAdd()
if (op === '=' || op === '==') {
const eq =
left.kind === right.kind &&
(left.kind === 'n' ? left.v === right.v : left.v === right.v)
return { kind: 'n', v: eq ? 1 : 0 }
}
if (op === '!=') {
const eq =
left.kind === right.kind &&
(left.kind === 'n' ? left.v === right.v : left.v === right.v)
return { kind: 'n', v: eq ? 0 : 1 }
}
if (left.kind !== 'n' || right.kind !== 'n') throw new Error('non-numeric')
let ok = false
if (op === '<') ok = left.v < right.v
else if (op === '<=') ok = left.v <= right.v
else if (op === '>') ok = left.v > right.v
else if (op === '>=') ok = left.v >= right.v
return { kind: 'n', v: ok ? 1 : 0 }
}
return left
}
try {
const r = parseCmp()
if (i < tokens.length) throw new Error('syntax error')
ctx.console.log(String(r.kind === 'n' ? r.v : r.v))
} catch (e) {
ctx.console.error('expr: ' + (e.message || e))
ctx.exitCode = 2
}
}
+142
View File
@@ -0,0 +1,142 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function factorOne(n) {
const out = []
let x = n
let d = 2
while (d * d <= x) {
while (x % d === 0) {
out.push(d)
x /= d
}
d++
}
if (x > 1) out.push(x)
return out
}
async function run(ctx, argv) {
const nums = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log('usage: factor [NUMBER]...\nPrint prime factors (trial division).')
return
}
if (a.startsWith('-')) {
ctx.console.error('factor: unsupported option ' + a)
ctx.exitCode = 1
return
}
nums.push(a)
}
const parse = (s) => {
const n = parseInt(String(s), 10)
if (!Number.isFinite(n) || n < 0 || n > Number.MAX_SAFE_INTEGER) return null
return n
}
if (!nums.length) {
const lines = bareStdin(ctx).split(/\n')
for (const line of lines) {
const t = line.trim()
if (!t) continue
nums.push(t)
}
}
for (const s of nums) {
const n = parse(s)
if (n == null || n < 2) {
ctx.console.log(s + ':')
continue
}
const f = factorOne(n)
ctx.console.log(s + ': ' + f.join(' '))
}
}
+97 -1
View File
@@ -166,10 +166,12 @@ async function walk(ctx, dir, o, curDepth) {
const nameOk = !o.nameRe || o.nameRe.test(n)
let match = pathOk && nameOk && (!o.wantType || st.type === o.wantType)
if (match && o.mtimeSpec) match = match && findMatchMtime(st, o.mtimeSpec)
if (match && o.newerThanMs != null) match = match && st.mtimeMs > o.newerThanMs
if (match && o.newerThanMs != null)
match = match && st.mtimeMs > o.newerThanMs
if (match && o.wantEmpty) {
match = match && (await findIsEmpty(ctx, path, st))
}
if (match && o.regexPath && !o.regexPath.test(path)) match = false
if (match && gnuDepth >= o.minDepth) {
if (o.doDelete) {
if (ctx.vfs.env.BARE_OS_FIND_DELETE !== '1') {
@@ -189,6 +191,40 @@ async function walk(ctx, dir, o, curDepth) {
ctx.exitCode = 1
}
}
} else if (o.execTemplate && o.execTemplate.length) {
if (typeof ctx.runBinCommand !== 'function') {
const stw = /** @type {{ noRun?: boolean }} */ (o.deleteState)
if (!stw.noRun) {
ctx.console.error('find: -exec requires ctx.runBinCommand')
stw.noRun = true
}
ctx.exitCode = 1
} else if (o.execCount >= o.execMax) {
const stw = /** @type {{ execCap?: boolean }} */ (o.deleteState)
if (!stw.execCap) {
ctx.console.error(
'find: -exec/-ok: invocation limit (' +
o.execMax +
') exceeded (raise BARE_OS_FIND_EXEC_MAX)'
)
stw.execCap = true
}
ctx.exitCode = 1
} else {
const ok =
!o.execUseOk ||
ctx.vfs.env.BARE_OS_FIND_OK === '1' ||
ctx.vfs.env.BARE_OS_FIND_OK === 'true'
if (o.execUseOk && !ok) {
/* skip without running */
} else {
const subst = o.execTemplate.map((arg) =>
arg === '{}' ? path : arg.split('{}').join(path)
)
o.execCount++
await ctx.runBinCommand(subst)
}
}
} else {
findEmitLine(ctx, path, o.print0)
}
@@ -219,6 +255,11 @@ async function run(ctx, argv) {
let prunePath = null
let wantEmpty = false
let doDelete = false
/** @type {string[] | null} */
let execTemplate = null
let execUseOk = false
/** @type {string | null} */
let regexPathStr = null
const rest = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
@@ -281,6 +322,39 @@ async function run(ctx, argv) {
doDelete = true
continue
}
if ((a === '-exec' || a === '-ok') && argv[i + 1]) {
execUseOk = a === '-ok'
i++
const parts = []
while (i < argv.length && argv[i] !== ';') {
parts.push(argv[i++])
}
if (i >= argv.length || argv[i] !== ';') {
ctx.console.error('find: ' + a + ' must be terminated with ;')
ctx.exitCode = 1
return
}
i++
if (!parts.length) {
ctx.console.error('find: empty ' + a)
ctx.exitCode = 1
return
}
execTemplate = parts
continue
}
if (a === '-regex' && argv[i + 1]) {
const pat = argv[++i]
try {
new RegExp(pat)
regexPathStr = pat
} catch {
ctx.console.error('find: invalid -regex')
ctx.exitCode = 1
return
}
continue
}
if (a === '--') {
rest.push(...argv.slice(i + 1))
break
@@ -333,6 +407,23 @@ async function run(ctx, argv) {
if (prunePath) {
pruneAbs = ctx.vfs.resolveLogical(prunePath).replace(/\/+$/, '') || '/'
}
/** @type {RegExp | null} */
let regexPath = null
if (regexPathStr) {
try {
regexPath = new RegExp(regexPathStr)
} catch {
ctx.console.error('find: invalid -regex')
ctx.exitCode = 1
return
}
}
const execMaxRaw = ctx.vfs.env.BARE_OS_FIND_EXEC_MAX
let execMax = 64
if (execMaxRaw != null && String(execMaxRaw).trim() !== '') {
const n = Number.parseInt(String(execMaxRaw), 10)
if (Number.isFinite(n)) execMax = Math.min(4096, Math.max(1, n))
}
const abs = ctx.vfs.resolveLogical(root)
const o = {
maxDepth,
@@ -346,6 +437,11 @@ async function run(ctx, argv) {
pruneAbs,
wantEmpty,
doDelete,
regexPath,
execTemplate,
execUseOk,
execCount: 0,
execMax,
deleteState: {}
}
await walk(ctx, abs, o, 0)
+151
View File
@@ -0,0 +1,151 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let width = 75
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: fmt [-w WIDTH] [FILE]...\nSimple paragraph reflow: join non-empty lines, wrap at spaces.'
)
return
}
if ((a === '-w' || a === '--width') && argv[i + 1]) {
width = parseInt(argv[++i], 10)
if (!Number.isFinite(width) || width < 1) width = 75
continue
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('fmt: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
function flushPara(words) {
if (!words.length) return
let line = ''
for (const w of words) {
if (!line) line = w
else if (line.length + 1 + w.length <= width) line += ' ' + w
else {
ctx.console.log(line)
line = w
}
}
if (line) ctx.console.log(line)
}
function proc(text) {
const paras = text.split(/\n\n+/)
for (const para of paras) {
const words = para.replace(/\s+/g, ' ').trim().split(' ').filter(Boolean)
flushPara(words)
}
}
if (!paths.length) {
proc(bareStdin(ctx))
return
}
for (const p of paths) {
if (p === '-') {
proc(bareStdin(ctx))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('fmt: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
proc(b4.toString(b))
}
}
+149
View File
@@ -0,0 +1,149 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let width = 80
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log('usage: fold [-w WIDTH] [FILE]...\nWrap each input line to WIDTH columns.')
return
}
if ((a === '-w' || a === '--width') && argv[i + 1]) {
width = parseInt(argv[++i], 10)
if (!Number.isFinite(width) || width < 1) width = 80
continue
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('fold: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
function wrapLine(line) {
if (line.length <= width) return [line]
const rows = []
let rest = line
while (rest.length > width) {
rows.push(rest.slice(0, width))
rest = rest.slice(width)
}
if (rest.length) rows.push(rest)
return rows
}
function proc(text) {
const lines = text.split('\n')
for (let li = 0; li < lines.length; li++) {
const isLast = li === lines.length - 1
const line = lines[li]
if (isLast && line === '' && lines.length > 1) continue
for (const row of wrapLine(line)) ctx.console.log(row)
}
}
if (!paths.length) {
proc(bareStdin(ctx))
return
}
for (const p of paths) {
if (p === '-') {
proc(bareStdin(ctx))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('fold: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
proc(b4.toString(b))
}
}
+9 -1
View File
@@ -116,7 +116,15 @@ const CONF = {
/** Defaults for simulated pipelines (override with BARE_OS_PIPELINE_* env); see handbook §3. */
BARE_OS_PIPELINE_MAX_STAGES: '32',
BARE_OS_PIPELINE_MAX_BYTES: '2097152',
BARE_OS_PIPELINE_MAX_LINES: '50000'
BARE_OS_PIPELINE_MAX_LINES: '50000',
/** Default cap for find -exec/-ok invocations per run (override with env). */
BARE_OS_FIND_EXEC_MAX: '64',
/** Max lines `yes` prints before stopping (override with BARE_OS_YES_MAX_LINES). */
BARE_OS_YES_MAX_LINES: '100000',
/** Max input lines `shuf` will hold in memory (override with BARE_OS_SHUF_MAX_LINES). */
BARE_OS_SHUF_MAX_LINES: '50000',
/** Max output chunk files `split` may create (override with BARE_OS_SPLIT_MAX_FILES). */
BARE_OS_SPLIT_MAX_FILES: '10000'
}
async function run(ctx, argv) {
+106
View File
@@ -0,0 +1,106 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log('usage: groups [USER]\nPrint group memberships (session model; often one group).')
return
}
}
const e = ctx.vfs.env || {}
const u = e.USER || e.LOGNAME || 'guest'
const g = e.GROUP || u
const extra = e.GROUPS
if (extra && String(extra).trim()) {
ctx.console.log(String(extra).replace(/,/g, ' '))
return
}
ctx.console.log(g)
}
+3 -1
View File
@@ -87,9 +87,11 @@ function bareOsEmitRaw(ctx, chunk) {
return false
}
var BARE_OS_HELP_BIN_SPACED = "arch awk base32 base64 basename basenc cat chgrp chmod chown cksum clear comm cp crontab curl cut date df dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf git git-pear grep groups hdms head help hostid hostname id install join journalctl jq ln login logname logout ls man md5sum mkdir mkfifo mktemp mv nano nl nproc numfmt od paste pathchk pr printenv printf pwd readlink realpath rev rm rmdir savevault sed seq sha1sum sha256sum sha512sum shuf sleep sort split stat sum sync systemctl tac tail tee test theme time touch tr true truncate tsort tty uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs yes"
async function run(ctx, argv) {
ctx.console.log(
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du edit echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mktemp nano mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs'
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
BARE_OS_HELP_BIN_SPACED
)
ctx.console.log(
'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l'
+112
View File
@@ -0,0 +1,112 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log('usage: hostid\nPrint a numeric host identifier (session-derived stub).')
return
}
if (argv[i].startsWith('-')) {
ctx.console.error('hostid: unknown option ' + argv[i])
ctx.exitCode = 1
return
}
}
const e = ctx.vfs.env || {}
if (e.HOSTID && /^[0-9a-fA-F]{8}$/.test(e.HOSTID)) {
ctx.console.log(e.HOSTID.toLowerCase())
return
}
const seed = e.BARE_OS_SESSION_ID || e.HOSTNAME || 'bare-os'
let h = 0
for (let i = 0; i < seed.length; i++)
h = (Math.imul(31, h) + seed.charCodeAt(i)) >>> 0
ctx.console.log(h.toString(16).padStart(8, '0'))
}
+137
View File
@@ -0,0 +1,137 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let mode = null
const rest = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: install [-m MODE] SOURCE DEST\nCopy one file to DEST and optionally chmod.'
)
return
}
if (a === '-m' && argv[i + 1]) {
const m = parseInt(argv[++i], 8)
if (!Number.isFinite(m) || m < 0) {
ctx.console.error('install: invalid mode')
ctx.exitCode = 1
return
}
mode = m & 0o777
continue
}
if (a.startsWith('-')) {
ctx.console.error('install: unsupported option ' + a)
ctx.exitCode = 1
return
}
rest.push(a)
}
if (rest.length !== 2) {
ctx.console.error('usage: install [-m MODE] SOURCE DEST')
ctx.exitCode = 1
return
}
const [src, dest] = rest
const buf = await ctx.vfs.readFile(src)
if (!buf) {
ctx.console.error('install: cannot read ' + src)
ctx.exitCode = 1
return
}
try {
await ctx.vfs.writeFile(dest, buf)
if (mode != null) await ctx.vfs.chmod(dest, mode)
} catch (e) {
ctx.console.error('install: ' + (e.message || e))
ctx.exitCode = 1
}
}
+186
View File
@@ -0,0 +1,186 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function readLines(ctx, path) {
const b4 = ctx.b4a
if (path === '-') return bareStdin(ctx).split('\n')
const b = await ctx.vfs.readFile(path)
if (!b) return null
return b4.toString(b).split('\n')
}
function field(line, delim, n) {
if (delim === null) {
const parts = line.split(/\s+/)
return parts[n - 1] != null ? parts[n - 1] : ''
}
const parts = line.split(delim)
return parts[n - 1] != null ? parts[n - 1] : ''
}
function key(line, delim, n) {
return field(line, delim, n)
}
async function run(ctx, argv) {
let delim = null
let f1 = 1
let f2 = 1
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: join [-t CHAR] [-1 N] [-2 N] FILE1 FILE2\nJoin lines on equal join fields (sorted input).'
)
return
}
if (a === '-t' && argv[i + 1]) {
const d = argv[++i]
delim = d === '\\t' ? '\t' : d.slice(0, 1)
continue
}
if (a === '-1' && argv[i + 1]) {
f1 = parseInt(argv[++i], 10)
continue
}
if (a === '-2' && argv[i + 1]) {
f2 = parseInt(argv[++i], 10)
continue
}
if (a.startsWith('-')) {
ctx.console.error('join: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
if (paths.length !== 2) {
ctx.console.error('usage: join [-t CHAR] [-1 N] [-2 N] FILE1 FILE2')
ctx.exitCode = 1
return
}
const A = await readLines(ctx, paths[0])
const B = await readLines(ctx, paths[1])
if (!A || !B) {
ctx.console.error('join: missing input file')
ctx.exitCode = 1
return
}
const trim = (arr) => {
if (arr.length && arr[arr.length - 1] === '') arr.pop()
return arr
}
const a = trim(A.slice())
const b = trim(B.slice())
const sep = delim != null ? delim : ' '
let i = 0
let j = 0
while (i < a.length && j < b.length) {
const ka = key(a[i], delim, f1)
const kb = key(b[j], delim, f2)
const cmp = ka.localeCompare(kb)
if (cmp < 0) i++
else if (cmp > 0) j++
else {
const k = ka
let i1 = i
while (i1 < a.length && key(a[i1], delim, f1) === k) i1++
let j1 = j
while (j1 < b.length && key(b[j1], delim, f2) === k) j1++
for (let ii = i; ii < i1; ii++) {
for (let jj = j; jj < j1; jj++) {
ctx.console.log(a[ii] + sep + b[jj])
}
}
i = i1
j = j1
}
}
}
+75 -3
View File
@@ -499,6 +499,45 @@ function bareLsColorLongTail(name, arrow, st, on, ctx) {
return bareLsColorWrap(name, st, true, ctx) + arrow
}
/**
* @param {unknown} vfs
* @param {string[]} names
* @param {string} t
* @param {string | null} singleEntryPath
* @param {'time' | 'size' | null} sortBy
* @param {boolean} reverse
*/
async function bareLsSortNames(
vfs,
names,
t,
singleEntryPath,
sortBy,
reverse
) {
if (singleEntryPath != null) return names
if (!sortBy && !reverse) return names
const pairs = []
for (const n of names) {
const sub = t === '.' || t === './' ? n : t.replace(/\/$/, '') + '/' + n
let st = null
try {
st = await vfs.lstat(sub)
} catch {
st = null
}
pairs.push({ n, st })
}
pairs.sort((a, b) => {
let cmp = 0
if (sortBy === 'time') cmp = (b.st?.mtimeMs ?? 0) - (a.st?.mtimeMs ?? 0)
else if (sortBy === 'size') cmp = (b.st?.size ?? 0) - (a.st?.size ?? 0)
else cmp = a.n.localeCompare(b.n)
return reverse ? -cmp : cmp
})
return pairs.map((p) => p.n)
}
async function run(ctx, argv) {
const vfs = ctx.vfs
let showAll = false
@@ -507,6 +546,9 @@ async function run(ctx, argv) {
let singleColumn = false
/** @type {'never' | 'auto' | 'always'} */
let colorMode = 'auto'
/** @type {'time' | 'size' | null} */
let sortBy = null
let reverseSort = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
@@ -522,7 +564,8 @@ async function run(ctx, argv) {
if (a.startsWith('--color=')) {
const v = a.slice(8).toLowerCase()
if (v === 'never' || v === 'none' || v === 'no') colorMode = 'never'
else if (v === 'always' || v === 'yes' || v === 'force') colorMode = 'always'
else if (v === 'always' || v === 'yes' || v === 'force')
colorMode = 'always'
else colorMode = 'auto'
continue
}
@@ -530,6 +573,14 @@ async function run(ctx, argv) {
singleColumn = true
continue
}
if (a === '--sort=time' || a === '--sort=none') {
sortBy = a.endsWith('time') ? 'time' : null
continue
}
if (a === '--sort=size') {
sortBy = 'size'
continue
}
ctx.console.error('ls: unrecognized option ' + a)
ctx.exitCode = 2
return
@@ -540,6 +591,9 @@ async function run(ctx, argv) {
if (c === 'a') showAll = true
else if (c === 'l') longFmt = true
else if (c === '1') singleColumn = true
else if (c === 't') sortBy = 'time'
else if (c === 'S') sortBy = 'size'
else if (c === 'r') reverseSort = true
}
continue
}
@@ -547,8 +601,7 @@ async function run(ctx, argv) {
}
const targets = paths.length ? paths : ['.']
const useColor = bareLsUseColor(ctx, colorMode)
const onePerLine =
singleColumn || ctx.bareOsStdoutCaptured === true
const onePerLine = singleColumn || ctx.bareOsStdoutCaptured === true
for (const t of targets) {
if (targets.length > 1) ctx.console.log(t + ':')
@@ -569,6 +622,14 @@ async function run(ctx, argv) {
continue
}
if (!showAll) names = names.filter((n) => !n.startsWith('.'))
names = await bareLsSortNames(
vfs,
names,
t,
singleEntryPath,
sortBy,
reverseSort
)
if (!longFmt) {
if (onePerLine) {
for (const n of names) {
@@ -662,6 +723,17 @@ async function run(ctx, argv) {
st
})
}
if ((sortBy || reverseSort) && singleEntryPath == null) {
rows.sort((a, b) => {
let cmp = 0
const sta = a.st
const stb = b.st
if (sortBy === 'time') cmp = (stb?.mtimeMs ?? 0) - (sta?.mtimeMs ?? 0)
else if (sortBy === 'size') cmp = (stb?.size ?? 0) - (sta?.size ?? 0)
else cmp = a.name.localeCompare(b.name)
return reverseSort ? -cmp : cmp
})
}
if (singleEntryPath == null) ctx.console.log('total ' + totalBlocks)
for (const r of rows) {
const tail = bareLsColorLongTail(r.name, r.arrow, r.st, useColor, ctx)
+207
View File
@@ -0,0 +1,207 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/** RFC 1321 MD5 — no Web Crypto; used by md5sum. */
function bareMd5DigestBytes(u8) {
const n = u8.length
const bitLen = (BigInt(n) * 8n) & 0xffffffffffffffffn
const padLen = (56 - ((n + 1) % 64) + 64) % 64
const total = n + 1 + padLen + 8
const buf = new Uint8Array(total)
buf.set(u8)
buf[n] = 0x80
const view = new DataView(buf.buffer)
view.setUint32(total - 8, Number(bitLen & 0xffffffffn), true)
view.setUint32(total - 4, Number((bitLen >> 32n) & 0xffffffffn), true)
let a0 = 0x67452301
let b0 = 0xefcdab89
let c0 = 0x98badcfe
let d0 = 0x10325476
const s = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9,
14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
]
const K = new Uint32Array(64)
for (let i = 0; i < 64; i++) K[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000) >>> 0
const leftRotate = (x, c) => ((x << c) | (x >>> (32 - c))) >>> 0
for (let off = 0; off < total; off += 64) {
const M = new Uint32Array(16)
for (let i = 0; i < 16; i++) {
M[i] = view.getUint32(off + i * 4, true)
}
let A = a0
let B = b0
let C = c0
let D = d0
for (let i = 0; i < 64; i++) {
let F, g
if (i < 16) {
F = (B & C) | (~B & D)
g = i
} else if (i < 32) {
F = (D & B) | (~D & C)
g = (5 * i + 1) % 16
} else if (i < 48) {
F = B ^ C ^ D
g = (3 * i + 5) % 16
} else {
F = C ^ (B | ~D)
g = (7 * i) % 16
}
F = (F + A + K[i] + M[g]) >>> 0
A = D
D = C
C = B
B = (B + leftRotate(F, s[i])) >>> 0
}
a0 = (a0 + A) >>> 0
b0 = (b0 + B) >>> 0
c0 = (c0 + C) >>> 0
d0 = (d0 + D) >>> 0
}
const out = new Uint8Array(16)
const dv = new DataView(out.buffer)
dv.setUint32(0, a0, true)
dv.setUint32(4, b0, true)
dv.setUint32(8, c0, true)
dv.setUint32(12, d0, true)
return [...out].map((b) => b.toString(16).padStart(2, '0')).join('')
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: md5sum [FILE]...\n' +
'With no FILE, or when FILE is -, read standard input. Uses bundled MD5 (no Web Crypto MD5).'
)
ctx.exitCode = 0
return
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('md5sum: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
function one(name, buf) {
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
try {
const hex = bareMd5DigestBytes(u8)
ctx.console.log(hex + ' ' + name)
} catch (e) {
ctx.console.error('md5sum: ' + (e.message || e))
ctx.exitCode = 1
}
}
if (!paths.length || (paths.length === 1 && paths[0] === '-')) {
one('-', b4.from(bareStdin(ctx)))
return
}
for (const p of paths) {
if (p === '-') {
one('-', b4.from(bareStdin(ctx)))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('md5sum: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
one(p, b)
}
}
+122
View File
@@ -0,0 +1,122 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: nproc [--all]\nPrint number of processing units (from /proc/cpuinfo or 1). --all is accepted for compatibility.'
)
return
}
if (a === '--all') {
/* same count as default in this environment */
} else if (a.startsWith('-')) {
ctx.console.error('nproc: unknown option ' + a)
ctx.exitCode = 1
return
}
}
let n = 1
try {
const buf = await ctx.vfs.readFile('/proc/cpuinfo')
if (buf) {
const t = ctx.b4a.toString(buf)
const m = t.match(/^processor\s*:/gim)
if (m && m.length) n = m.length
}
} catch {
/* ignore */
}
const e = ctx.vfs.env || {}
const envN = e.BARE_OS_NPROC
if (envN && /^\d+$/.test(envN)) n = Math.max(1, parseInt(envN, 10))
ctx.console.log(String(n))
}
+147
View File
@@ -0,0 +1,147 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function fmtIec(n, si) {
const base = si ? 1000 : 1024
const units = si
? ['', 'k', 'M', 'G', 'T', 'P']
: ['', 'K', 'M', 'G', 'T', 'P']
if (n === 0) return '0'
let sign = n < 0 ? -1 : 1
let x = Math.abs(n)
let u = 0
while (x >= base && u < units.length - 1) {
x /= base
u++
}
const s =
x >= 10 || u === 0 ? Math.round(x * 10) / 10 : Math.round(x * 100) / 100
const t = String(s).replace(/\.0$/, '')
return (sign < 0 ? '-' : '') + t + units[u]
}
async function run(ctx, argv) {
let toIec = false
let toSi = false
const rest = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: numfmt [--to=iec|--to=si] [NUMBER]...\nFormat numbers; reads stdin lines if no operands.'
)
return
}
if (a === '--to=iec') toIec = true
else if (a === '--to=si') toSi = true
else if (a.startsWith('-')) {
ctx.console.error('numfmt: unsupported option ' + a)
ctx.exitCode = 1
return
} else rest.push(a)
}
if (!toIec && !toSi) toIec = true
const nums = []
if (!rest.length) {
for (const line of bareStdin(ctx).split('\n')) {
const t = line.trim()
if (!t) continue
nums.push(t)
}
} else nums.push(...rest)
for (const s of nums) {
const n = parseInt(s, 10)
if (!Number.isFinite(n)) {
ctx.console.error('numfmt: invalid number ' + s)
ctx.exitCode = 1
return
}
ctx.console.log(fmtIec(n, toSi))
}
}
+161
View File
@@ -0,0 +1,161 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function readLines(ctx, path) {
const b4 = ctx.b4a
if (path === '-') return bareStdin(ctx).split('\n')
const b = await ctx.vfs.readFile(path)
if (!b) return null
return b4.toString(b).split('\n')
}
async function run(ctx, argv) {
let delims = '\t'
let serial = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: paste [-d LIST] [-s] [FILE]...\nMerge corresponding lines; -s pastes one file per line.'
)
return
}
if ((a === '-d' || a === '--delimiters') && argv[i + 1]) {
delims = argv[++i]
continue
}
if (a === '-s' || a === '--serial') {
serial = true
continue
}
if (a.startsWith('-')) {
ctx.console.error('paste: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
if (!paths.length) paths.push('-')
const files = []
for (const p of paths) {
const L = await readLines(ctx, p)
if (L == null) {
ctx.console.error('paste: ' + p + ': No such file')
ctx.exitCode = 1
return
}
const trim = L.length && L[L.length - 1] === '' ? L.slice(0, -1) : L
files.push(trim)
}
if (serial) {
for (const lines of files) {
let out = ''
for (let r = 0; r < lines.length; r++) {
if (r) out += delims[0] || '\t'
out += lines[r]
}
ctx.console.log(out)
}
return
}
const maxR = Math.max(...files.map((f) => f.length), 0)
for (let r = 0; r < maxR; r++) {
const parts = []
for (let c = 0; c < files.length; c++) {
parts.push(files[c][r] != null ? files[c][r] : '')
}
let line = ''
for (let c = 0; c < parts.length; c++) {
if (c) line += delims[c % delims.length] || '\t'
line += parts[c]
}
ctx.console.log(line)
}
}
+154
View File
@@ -0,0 +1,154 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let width = 72
let numberLines = false
let sep = '\t'
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: pr [-w WIDTH] [-n] [-s CHAR] [FILE]...\nMinimal print: merge files side by side with optional line numbers.'
)
return
}
if ((a === '-w' || a === '--width') && argv[i + 1]) {
width = parseInt(argv[++i], 10)
if (!Number.isFinite(width) || width < 1) width = 72
continue
}
if (a === '-n' || a === '--number') {
numberLines = true
continue
}
if ((a === '-s' || a === '--separator') && argv[i + 1]) {
sep = argv[++i] || '\t'
continue
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('pr: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
const files = []
if (!paths.length) {
files.push(bareStdin(ctx).split('\n'))
} else {
for (const p of paths) {
if (p === '-') files.push(bareStdin(ctx).split('\n'))
else {
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('pr: ' + p + ': No such file')
ctx.exitCode = 1
return
}
files.push(b4.toString(b).split('\n'))
}
}
}
const maxRows = Math.max(...files.map((f) => f.length), 0)
for (let r = 0; r < maxRows; r++) {
const parts = []
for (let c = 0; c < files.length; c++) {
let cell = files[c][r] != null ? files[c][r] : ''
if (numberLines && c === 0 && r < files[0].length)
cell = String(r + 1).padStart(6, ' ') + '\t' + cell
parts.push(cell)
}
let line = parts.join(sep)
if (line.length > width) line = line.slice(0, width)
ctx.console.log(line)
}
}
+144
View File
@@ -0,0 +1,144 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* realpath — print resolved absolute path (VFS logical resolution).
*/
async function run(ctx, argv) {
let missingOk = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: realpath [-m] FILE...\n' +
' -m do not fail if the path does not exist (resolve only)'
)
ctx.exitCode = 0
return
}
if (a === '-m' || a === '--canonicalize-missing') {
missingOk = true
continue
}
if (a.startsWith('-')) {
ctx.console.error('realpath: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
if (!paths.length) {
ctx.console.error('usage: realpath [-m] FILE...')
ctx.exitCode = 1
return
}
const vfs = ctx.vfs
for (const p of paths) {
try {
if (!missingOk) {
const st = await vfs.stat(p)
if (!st) {
ctx.console.error('realpath: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
}
const resolved = vfs.resolveLogical(p)
ctx.console.log(resolved)
} catch (e) {
if (missingOk) {
ctx.console.log(vfs.resolveLogical(p))
} else {
ctx.console.error('realpath: ' + p + ': ' + (e.message || e))
ctx.exitCode = 1
}
}
}
}
+132
View File
@@ -0,0 +1,132 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log('usage: rev [FILE]...\nReverse characters on each line.')
return
}
if (a.startsWith('-')) {
ctx.console.error('rev: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
function procText(text) {
const endsNl = text.endsWith('\n')
const lines = text.split('\n')
if (endsNl && lines.length && lines[lines.length - 1] === '') lines.pop()
for (const line of lines) {
ctx.console.log(line.split('').reverse().join(''))
}
}
if (!paths.length) {
procText(bareStdin(ctx))
return
}
for (const p of paths) {
let text
if (p === '-') text = bareStdin(ctx)
else {
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('rev: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
text = b4.toString(b)
}
procText(text)
}
}
+33 -1
View File
@@ -89,12 +89,13 @@ function bareOsEmitRaw(ctx, chunk) {
/**
* rm — remove files or directories.
* Flags: -r -R --recursive, -f --force, -- ; bundled e.g. -rf
* Flags: -r -R --recursive, -f --force, -d --dir, -- ; bundled e.g. -rf
*/
async function run(ctx, argv) {
const vfs = ctx.vfs
let recursive = false
let force = false
let dirEmptyOnly = false
const files = []
let dash = false
for (let i = 1; i < argv.length; i++) {
@@ -115,11 +116,16 @@ async function run(ctx, argv) {
force = true
continue
}
if (a === '--dir' || a === '-d') {
dirEmptyOnly = true
continue
}
if (a.startsWith('-') && a.length > 1) {
for (let j = 1; j < a.length; j++) {
const c = a[j]
if (c === 'r' || c === 'R') recursive = true
else if (c === 'f') force = true
else if (c === 'd') dirEmptyOnly = true
}
continue
}
@@ -130,6 +136,11 @@ async function run(ctx, argv) {
ctx.exitCode = 1
return
}
if (dirEmptyOnly && recursive) {
ctx.console.error('rm: cannot combine -d and -r')
ctx.exitCode = 1
return
}
const doRm =
vfs && typeof vfs.rm === 'function'
? (p) => vfs.rm(p, { recursive, force })
@@ -139,6 +150,27 @@ async function run(ctx, argv) {
}
for (const f of files) {
try {
if (dirEmptyOnly) {
const st = await vfs.lstat(f)
if (!st) {
if (!force) {
ctx.console.error('rm: ' + f + ': No such file')
ctx.exitCode = 1
}
continue
}
if (st.type !== 'directory') {
ctx.console.error('rm: cannot remove ' + f + ': Not a directory')
ctx.exitCode = 1
continue
}
if (typeof vfs.rmdir === 'function') await vfs.rmdir(f)
else {
ctx.console.error('rm: rmdir not supported by this VFS')
ctx.exitCode = 1
}
continue
}
await doRm(f)
} catch (e) {
if (force) continue
+148
View File
@@ -0,0 +1,148 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function sha1Hex(u8) {
const subtle = globalThis.crypto?.subtle
if (!subtle || typeof subtle.digest !== 'function') {
throw new Error('crypto.subtle.digest (SHA-1) is not available')
}
const hash = await subtle.digest('SHA-1', u8)
return [...new Uint8Array(hash)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: sha1sum [FILE]...\n' +
'With no FILE, or when FILE is -, read standard input.'
)
ctx.exitCode = 0
return
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('sha1sum: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
async function one(name, buf) {
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
try {
const hex = await sha1Hex(u8)
ctx.console.log(hex + ' ' + name)
} catch (e) {
ctx.console.error('sha1sum: ' + (e.message || e))
ctx.exitCode = 1
}
}
if (!paths.length || (paths.length === 1 && paths[0] === '-')) {
await one('-', b4.from(bareStdin(ctx)))
return
}
for (const p of paths) {
if (p === '-') {
await one('-', b4.from(bareStdin(ctx)))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('sha1sum: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
await one(p, b)
}
}
+152
View File
@@ -0,0 +1,152 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* sha256sum — compute SHA-256 checksums (hex), GNU-like output line.
*/
async function sha256Hex(u8) {
const subtle = globalThis.crypto?.subtle
if (!subtle || typeof subtle.digest !== 'function') {
throw new Error('crypto.subtle.digest (SHA-256) is not available')
}
const hash = await subtle.digest('SHA-256', u8)
return [...new Uint8Array(hash)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: sha256sum [FILE]...\n' +
'With no FILE, or when FILE is -, read standard input.'
)
ctx.exitCode = 0
return
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('sha256sum: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
async function one(name, buf) {
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
try {
const hex = await sha256Hex(u8)
ctx.console.log(hex + ' ' + name)
} catch (e) {
ctx.console.error('sha256sum: ' + (e.message || e))
ctx.exitCode = 1
}
}
if (!paths.length || (paths.length === 1 && paths[0] === '-')) {
const buf = b4.from(bareStdin(ctx))
await one('-', buf)
return
}
for (const p of paths) {
if (p === '-') {
await one('-', b4.from(bareStdin(ctx)))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('sha256sum: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
await one(p, b)
}
}
+148
View File
@@ -0,0 +1,148 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function sha512Hex(u8) {
const subtle = globalThis.crypto?.subtle
if (!subtle || typeof subtle.digest !== 'function') {
throw new Error('crypto.subtle.digest (SHA-512) is not available')
}
const hash = await subtle.digest('SHA-512', u8)
return [...new Uint8Array(hash)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: sha512sum [FILE]...\n' +
'With no FILE, or when FILE is -, read standard input.'
)
ctx.exitCode = 0
return
}
if (a.startsWith('-') && a !== '-') {
ctx.console.error('sha512sum: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
async function one(name, buf) {
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
try {
const hex = await sha512Hex(u8)
ctx.console.log(hex + ' ' + name)
} catch (e) {
ctx.console.error('sha512sum: ' + (e.message || e))
ctx.exitCode = 1
}
}
if (!paths.length || (paths.length === 1 && paths[0] === '-')) {
await one('-', b4.from(bareStdin(ctx)))
return
}
for (const p of paths) {
if (p === '-') {
await one('-', b4.from(bareStdin(ctx)))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('sha512sum: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
await one(p, b)
}
}
+144
View File
@@ -0,0 +1,144 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function readLines(ctx, path) {
const b4 = ctx.b4a
if (path === '-') return bareStdin(ctx).split('\n')
const b = await ctx.vfs.readFile(path)
if (!b) return null
return b4.toString(b).split('\n')
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: shuf [FILE]...\nShuffle lines; memory-capped via BARE_OS_SHUF_MAX_LINES.'
)
return
}
if (a.startsWith('-')) {
ctx.console.error('shuf: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const cap =
parseInt(ctx.vfs.env.BARE_OS_SHUF_MAX_LINES || '50000', 10) || 50000
if (!paths.length) paths.push('-')
const b4 = ctx.b4a
const lines = []
for (const p of paths) {
const L = await readLines(ctx, p)
if (L == null) {
ctx.console.error('shuf: ' + p + ': No such file')
ctx.exitCode = 1
return
}
const trim = L.length && L[L.length - 1] === '' ? L.slice(0, -1) : L
for (const ln of trim) {
if (lines.length >= cap) {
ctx.console.error('shuf: input exceeds line cap')
ctx.exitCode = 1
return
}
lines.push(ln)
}
}
for (let i = lines.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const t = lines[i]
lines[i] = lines[j]
lines[j] = t
}
for (const ln of lines) ctx.console.log(ln)
}
+207
View File
@@ -0,0 +1,207 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function splitSuffix(prefix, i) {
const a = 'abcdefghijklmnopqrstuvwxyz'
const hi = Math.floor(i / 26) % 26
const lo = i % 26
return prefix + a[hi] + a[lo]
}
async function run(ctx, argv) {
let lineCount = 1000
let byteCount = null
let prefix = 'x'
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: split [-l N] [-b N] [INPUT [PREFIX]]\nSplit INPUT into pieces (default -l 1000).'
)
return
}
if (a === '-l' && argv[i + 1]) {
lineCount = parseInt(argv[++i], 10)
byteCount = null
if (!Number.isFinite(lineCount) || lineCount < 1) lineCount = 1000
continue
}
if (a.startsWith('-l') && a.length > 2) {
lineCount = parseInt(a.slice(2), 10)
byteCount = null
continue
}
if (a === '-b' && argv[i + 1]) {
byteCount = parseInt(argv[++i], 10)
lineCount = null
if (!Number.isFinite(byteCount) || byteCount < 1) byteCount = 512
continue
}
if (a.startsWith('-')) {
ctx.console.error('split: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const maxFiles =
parseInt(ctx.vfs.env.BARE_OS_SPLIT_MAX_FILES || '10000', 10) || 10000
let inputPath = '-'
if (paths.length === 1) inputPath = paths[0]
else if (paths.length >= 2) {
inputPath = paths[0]
prefix = paths[1]
}
const b4 = ctx.b4a
let text
if (inputPath === '-') text = bareStdin(ctx)
else {
const b = await ctx.vfs.readFile(inputPath)
if (!b) {
ctx.console.error('split: cannot read ' + inputPath)
ctx.exitCode = 1
return
}
text = b4.toString(b)
}
if (byteCount != null) {
const u8 = b4.from(text)
let off = 0
let idx = 0
while (off < u8.length) {
if (idx >= maxFiles) {
ctx.console.error('split: too many output files')
ctx.exitCode = 1
return
}
const chunk = u8.subarray(off, off + byteCount)
off += byteCount
await ctx.vfs.writeFile(splitSuffix(prefix, idx), chunk)
idx++
}
return
}
const endsNl = text.endsWith('\n')
const lines = text.split('\n')
if (endsNl && lines.length && lines[lines.length - 1] === '') lines.pop()
let idx = 0
let batch = []
function flush(forceNl) {
if (!batch.length) return
if (idx >= maxFiles) {
ctx.console.error('split: too many output files')
ctx.exitCode = 1
return
}
const body =
batch.join('\n') + (forceNl || batch.length === lineCount ? '\n' : '')
const p = splitSuffix(prefix, idx)
idx++
batch = []
return ctx.vfs.writeFile(p, b4.from(body))
}
for (let li = 0; li < lines.length; li++) {
batch.push(lines[li])
if (batch.length >= lineCount) {
await flush(true)
if (ctx.exitCode) return
}
}
if (batch.length) {
const tailNl = endsNl
const body = batch.join('\n') + (tailNl ? '\n' : '')
if (idx >= maxFiles) {
ctx.console.error('split: too many output files')
ctx.exitCode = 1
return
}
await ctx.vfs.writeFile(splitSuffix(prefix, idx), b4.from(body))
}
}
+8 -5
View File
@@ -97,8 +97,7 @@ function statApplyFormat(st, displayPath, fmt) {
for (let i = 0; i < fmt.length; i++) {
if (fmt[i] === '%' && i + 1 < fmt.length) {
const c = fmt[++i]
if (c === 'n')
out += displayPath.split('/').pop() || displayPath
if (c === 'n') out += displayPath.split('/').pop() || displayPath
else if (c === 'N') out += displayPath
else if (c === 's') out += String(st.size ?? 0)
else if (c === 'Y')
@@ -107,13 +106,17 @@ function statApplyFormat(st, displayPath, fmt) {
(typeof st.mtimeMs === 'number' ? st.mtimeMs : Date.now()) / 1000
)
)
else if (c === 'A')
out += bareFormatModeString(st.mode, st.type)
else if (c === 'A') out += bareFormatModeString(st.mode, st.type)
else if (c === 'U') out += String(st.user ?? '')
else if (c === 'G') out += String(st.group ?? '')
else if (c === 'u') out += String(st.uid ?? 0)
else if (c === 'g') out += String(st.gid ?? 0)
else if (c === '%') out += '%'
else if (c === 'F') {
const ty = st.type
if (ty === 'directory') out += 'directory'
else if (ty === 'symlink') out += 'symbolic link'
else out += 'regular file'
} else if (c === '%') out += '%'
else out += '%' + c
} else {
out += fmt[i]
+151
View File
@@ -0,0 +1,151 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function sumSysv(u8) {
let crc = 0
for (let i = 0; i < u8.length; i++) crc += u8[i]
crc = (crc & 0xffff) + ((crc >> 16) & 0xffff)
crc = (crc & 0xffff) + ((crc >> 16) & 0xffff)
return crc & 0xffff
}
function sumBsd(u8) {
let cksum = 0
for (let i = 0; i < u8.length; i++) {
cksum = (cksum >> 1) + ((cksum & 1) << 15)
cksum = (cksum + u8[i]) & 0xffff
}
return cksum
}
async function run(ctx, argv) {
let bsd = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: sum [-r] [FILE]...\n' +
' -r BSD algorithm (default is SysV / CRC16-style sum)'
)
ctx.exitCode = 0
return
}
if (a === '-r') bsd = true
else if (a.startsWith('-') && a !== '-') {
ctx.console.error('sum: unsupported option ' + a)
ctx.exitCode = 1
return
} else paths.push(a)
}
const b4 = ctx.b4a
function one(name, buf) {
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
const blocks = Math.ceil(u8.length / 512) || 1
const v = bsd ? sumBsd(u8) : sumSysv(u8)
ctx.console.log(v + '\t' + blocks + '\t' + name)
}
if (!paths.length || (paths.length === 1 && paths[0] === '-')) {
one('-', b4.from(bareStdin(ctx)))
return
}
for (const p of paths) {
if (p === '-') {
one('-', b4.from(bareStdin(ctx)))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('sum: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
one(p, b)
}
}
+102
View File
@@ -0,0 +1,102 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log('usage: sync\nFlush filesystem buffers (no-op on Bare OS; exits 0).')
return
}
if (argv[i].startsWith('-')) {
ctx.console.error('sync: unknown option ' + argv[i])
ctx.exitCode = 1
return
}
}
}
+134
View File
@@ -0,0 +1,134 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log('usage: tac [FILE]...\nConcatenate and print lines in reverse order.')
return
}
if (a.startsWith('-')) {
ctx.console.error('tac: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
function tacText(text) {
const raw = text.endsWith('\n') ? text.slice(0, -1) : text
if (raw === '') {
ctx.console.log('')
return
}
const lines = raw.split('\n')
lines.reverse()
ctx.console.log(lines.join('\n'))
}
if (!paths.length) {
tacText(bareStdin(ctx))
return
}
for (const p of paths) {
let text
if (p === '-') text = bareStdin(ctx)
else {
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('tac: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
text = b4.toString(b)
}
tacText(text)
}
}
+144
View File
@@ -0,0 +1,144 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let size = null
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: truncate -s SIZE FILE\nSet file length to SIZE bytes (padded with zeros if growing).'
)
return
}
if ((a === '-s' || a === '--size') && argv[i + 1]) {
const raw = argv[++i]
if (raw.startsWith('+') || raw.startsWith('-')) {
ctx.console.error('truncate: relative sizes not supported')
ctx.exitCode = 1
return
}
size = parseInt(raw, 10)
if (!Number.isFinite(size) || size < 0) {
ctx.console.error('truncate: invalid size')
ctx.exitCode = 1
return
}
continue
}
if (a.startsWith('-')) {
ctx.console.error('truncate: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
if (size == null || paths.length !== 1) {
ctx.console.error('usage: truncate -s SIZE FILE')
ctx.exitCode = 1
return
}
const file = paths[0]
let cur = new Uint8Array(0)
try {
const b = await ctx.vfs.readFile(file)
if (b) cur = b instanceof Uint8Array ? b : new Uint8Array(b)
} catch {
/* new file */
}
const out = new Uint8Array(size)
out.set(cur.subarray(0, Math.min(cur.length, size)))
try {
await ctx.vfs.writeFile(file, out)
} catch (e) {
ctx.console.error('truncate: ' + (e.message || e))
ctx.exitCode = 1
}
}
+165
View File
@@ -0,0 +1,165 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: tsort [FILE]\nTopological sort of directed edges (one pair per line: A B).'
)
return
}
if (a.startsWith('-')) {
ctx.console.error('tsort: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
let text
if (!paths.length || paths[0] === '-') text = bareStdin(ctx)
else {
const b = await ctx.vfs.readFile(paths[0])
if (!b) {
ctx.console.error('tsort: cannot read ' + paths[0])
ctx.exitCode = 1
return
}
text = b4.toString(b)
}
const edges = []
const nodes = new Set()
for (const line of text.split('\n')) {
const t = line.trim()
if (!t) continue
const parts = t.split(/\s+/)
if (parts.length < 2) continue
const u = parts[0]
const v = parts[1]
edges.push([u, v])
nodes.add(u)
nodes.add(v)
}
const indeg = new Map()
const adj = new Map()
for (const n of nodes) {
indeg.set(n, 0)
adj.set(n, [])
}
for (const [u, v] of edges) {
indeg.set(v, (indeg.get(v) || 0) + 1)
adj.get(u).push(v)
}
const q = []
for (const [n, d] of indeg) {
if (d === 0) q.push(n)
}
q.sort()
const out = []
while (q.length) {
const u = q.shift()
out.push(u)
for (const v of adj.get(u) || []) {
indeg.set(v, indeg.get(v) - 1)
if (indeg.get(v) === 0) {
q.push(v)
q.sort()
}
}
}
if (out.length !== nodes.size) {
ctx.console.error('tsort: cycle in input')
ctx.exitCode = 1
return
}
for (const n of out) ctx.console.log(n)
}
+197
View File
@@ -0,0 +1,197 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
function parseWidth(tabArg) {
const n = parseInt(String(tabArg).split(',')[0].trim(), 10)
return Number.isFinite(n) && n > 0 ? n : 8
}
function unexpandLine(line, w) {
let out = ''
let col = 0
let i = 0
while (i < line.length) {
if (line[i] === ' ') {
let j = i
while (j < line.length && line[j] === ' ') j++
const n = j - i
const posMod = col % w
if (posMod === 0 && n >= w) {
const tabs = Math.floor(n / w)
const rest = n % w
for (let k = 0; k < tabs; k++) out += '\t'
col += tabs * w
i += tabs * w
for (let k = 0; k < rest; k++) {
out += ' '
col++
i++
}
continue
}
const toNext = posMod === 0 ? w : w - posMod
if (n >= toNext && toNext > 0 && posMod !== 0) {
out += '\t'
col += toNext
i += toNext
continue
}
out += line.slice(i, j)
col += n
i = j
continue
}
const ch = line[i]
out += ch
if (ch === '\t') col = (Math.floor(col / w) + 1) * w
else if (ch === '\n' || ch === '\r') col = 0
else col++
i++
}
return out
}
async function run(ctx, argv) {
let w = 8
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: unexpand [-t N] [FILE]...\nConvert runs of spaces to tabs (width N, default 8).'
)
return
}
if (a === '-t' && argv[i + 1]) {
w = parseWidth(argv[++i])
continue
}
if (a.startsWith('-t') && a.length > 2) {
w = parseWidth(a.slice(2))
continue
}
if (a.startsWith('--tabs=')) {
w = parseWidth(a.slice(7))
continue
}
if (a.startsWith('-')) {
ctx.console.error('unexpand: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
const b4 = ctx.b4a
function proc(text) {
const lines = text.split('\n')
for (let li = 0; li < lines.length; li++) {
const isLast = li === lines.length - 1
const line = lines[li]
if (isLast && line === '' && lines.length > 1) continue
ctx.console.log(unexpandLine(line, w))
}
}
if (!paths.length) {
proc(bareStdin(ctx))
return
}
for (const p of paths) {
if (p === '-') {
proc(bareStdin(ctx))
continue
}
const b = await ctx.vfs.readFile(p)
if (!b) {
ctx.console.error('unexpand: ' + p + ': No such file')
ctx.exitCode = 1
continue
}
proc(b4.toString(b))
}
}
+167
View File
@@ -0,0 +1,167 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* uniq — filter adjacent duplicate lines from sorted input.
*/
async function run(ctx, argv) {
let count = false
let onlyDup = false
let onlyUniq = false
const paths = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: uniq [-c] [-d] [-u] [INPUT [OUTPUT]]\n' +
' -c prefix lines with repeat count\n' +
' -d only print duplicate lines (one of each group)\n' +
' -u only print lines that are not repeated\n' +
'Reads stdin if INPUT omitted; OUTPUT is ignored (VFS single-output via redirect).'
)
ctx.exitCode = 0
return
}
if (a === '-c' || a === '--count') {
count = true
continue
}
if (a === '-d' || a === '--repeated') {
onlyDup = true
continue
}
if (a === '-u' || a === '--unique') {
onlyUniq = true
continue
}
if (a.startsWith('-')) {
ctx.console.error('uniq: unsupported option ' + a)
ctx.exitCode = 1
return
}
paths.push(a)
}
if (onlyDup && onlyUniq) {
ctx.console.error('uniq: -d and -u are mutually exclusive')
ctx.exitCode = 1
return
}
let text
if (!paths.length) {
text = bareStdin(ctx)
} else {
const b = await ctx.vfs.readFile(paths[0])
if (!b) {
ctx.console.error('uniq: cannot read ' + paths[0])
ctx.exitCode = 1
return
}
text = ctx.b4a.toString(b)
}
const raw = text.replace(/\r\n/g, '\n')
const lines = raw.length ? raw.split('\n') : []
if (lines.length && lines[lines.length - 1] === '') lines.pop()
let i = 0
while (i < lines.length) {
let j = i + 1
while (j < lines.length && lines[j] === lines[i]) j++
const reps = j - i
if (onlyDup && reps === 1) {
i = j
continue
}
if (onlyUniq && reps > 1) {
i = j
continue
}
if (count) ctx.console.log(String(reps) + ' ' + lines[i])
else ctx.console.log(lines[i])
i = j
}
}
+112
View File
@@ -0,0 +1,112 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
if (args.length === 1 && (args[0] === '-h' || args[0] === '--help')) {
ctx.console.log('usage: unlink FILE\nCall unlink(2) on one file.')
return
}
if (args.length !== 1) {
ctx.console.error('usage: unlink FILE')
ctx.exitCode = 1
return
}
if (args[0].startsWith('-')) {
ctx.console.error('unlink: unsupported option ' + args[0])
ctx.exitCode = 1
return
}
try {
await ctx.vfs.unlink(args[0])
} catch (e) {
ctx.console.error('unlink: ' + (e.message || e))
ctx.exitCode = 1
}
}
+142
View File
@@ -0,0 +1,142 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log('usage: uptime\nPrint load average and uptime from /proc/uptime when available.')
return
}
if (argv[i].startsWith('-')) {
ctx.console.error('uptime: unknown option ' + argv[i])
ctx.exitCode = 1
return
}
}
let up = 0
let idle = 0
try {
const buf = await ctx.vfs.readFile('/proc/uptime')
if (buf) {
const parts = ctx.b4a.toString(buf).trim().split(/\s+/)
up = parseFloat(parts[0]) || 0
idle = parseFloat(parts[1]) || 0
}
} catch {
/* ignore */
}
const now = new Date()
const timeStr = now.toTimeString().slice(0, 8)
const days = Math.floor(up / 86400)
const hrs = Math.floor((up % 86400) / 3600)
const mins = Math.floor((up % 3600) / 60)
let upStr =
days > 0
? days + ' day' + (days === 1 ? '' : 's') + ', '
: ''
upStr += String(hrs).padStart(2, '0') + ':' + String(mins).padStart(2, '0')
let load = '0.00 0.00 0.00'
try {
const la = await ctx.vfs.readFile('/proc/loadavg')
if (la) {
const t = ctx.b4a.toString(la).trim().split(/\s+/)
if (t.length >= 3) load = t[0] + ' ' + t[1] + ' ' + t[2]
}
} catch {
/* ignore */
}
void idle
ctx.console.log(
timeStr +
' up ' +
upStr +
', load average: ' +
load
)
}
+104
View File
@@ -0,0 +1,104 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log('usage: users\nPrint login names (single-session stub).')
return
}
if (argv[i].startsWith('-')) {
ctx.console.error('users: unknown option ' + argv[i])
ctx.exitCode = 1
return
}
}
const e = ctx.vfs.env || {}
ctx.console.log(e.USER || e.LOGNAME || 'guest')
}
+97
View File
@@ -0,0 +1,97 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
if (typeof ctx.runBinCommand !== 'function') {
ctx.console.error('vdir: runBinCommand not available')
ctx.exitCode = 1
return
}
await ctx.runBinCommand(['ls', '-l'].concat(argv.slice(1)))
}
+123
View File
@@ -0,0 +1,123 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
let wantHeader = false
const rest = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
ctx.console.log(
'usage: who [OPTION]...\nMinimal session listing (not full utmp).'
)
return
}
if (a === '-H' || a === '--heading') wantHeader = true
else if (a.startsWith('-')) {
ctx.console.error('who: unknown option ' + a)
ctx.exitCode = 1
return
} else rest.push(a)
}
const e = ctx.vfs.env || {}
const user = e.USER || e.LOGNAME || 'guest'
const term = e.TTY || e.SSH_TTY || 'ttyS0'
const host = e.HOSTNAME || e.NAME || 'bare-os'
if (wantHeader) {
ctx.console.log('NAME LINE TIME COMMENT')
}
const t = new Date().toISOString().replace('T', ' ').slice(0, 19)
if (rest.length === 0) {
ctx.console.log(user + ' ' + term + ' ' + t + ' (' + host + ')')
} else {
for (const _ of rest) {
ctx.console.log(user + ' ' + term + ' ' + t + ' (' + host + ')')
}
}
}
+103
View File
@@ -0,0 +1,103 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
async function run(ctx, argv) {
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '-h' || argv[i] === '--help') {
ctx.console.log(
'usage: yes [STRING]\nRepeatedly print STRING (default y); capped by BARE_OS_YES_MAX_LINES.'
)
return
}
}
const msg = argv.slice(1).join(' ') || 'y'
const cap =
parseInt(ctx.vfs.env.BARE_OS_YES_MAX_LINES || '100000', 10) || 100000
for (let n = 0; n < cap; n++) ctx.console.log(msg)
}
+210 -210
View File
@@ -1,18 +1,18 @@
{
"version": 1,
"bundles": [
{
"path": "/lib/bare/bundles/safetyCatch.js",
"keys": [
"safetyCatch"
]
},
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{
"path": "/lib/bare/bundles/safetyCatch.js",
"keys": [
"safetyCatch"
]
},
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
@@ -25,90 +25,72 @@
"compactEncoding"
]
},
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{
"path": "/lib/bare/bundles/bareUrl.js",
"keys": [
"bareUrl"
]
},
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{
"path": "/lib/bare/bundles/bareEncoding.js",
"keys": [
"bareEncoding"
]
},
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
"bareAbort"
]
},
{
"path": "/lib/bare/bundles/bareAbortController.js",
"keys": [
"bareAbortController"
]
},
{
"path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [
"bareAddonResolve"
]
},
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{
"path": "/lib/bare/bundles/bareAbortController.js",
"keys": [
"bareAbortController"
]
},
{
"path": "/lib/bare/bundles/bareReadline.js",
"keys": [
"bareReadline"
]
},
{
"path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [
"bareAddonResolve"
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"keys": [
"bareCrypto"
]
},
{
"path": "/lib/bare/bundles/bareApk.js",
"keys": [
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
@@ -122,9 +104,15 @@
]
},
{
"path": "/lib/bare/bundles/bareBmp.js",
"path": "/lib/bare/bundles/bareApk.js",
"keys": [
"bareBmp"
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
@@ -133,6 +121,24 @@
"bareAtomics"
]
},
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{
"path": "/lib/bare/bundles/bareBmp.js",
"keys": [
"bareBmp"
]
},
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{
"path": "/lib/bare/bundles/bareBuffer.js",
"keys": [
@@ -145,12 +151,6 @@
"bareBundle"
]
},
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{
"path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [
@@ -163,12 +163,6 @@
"bareBundleEvaluate"
]
},
{
"path": "/lib/bare/bundles/bareBoot.js",
"keys": [
"bareBoot"
]
},
{
"path": "/lib/bare/bundles/bareConsole.js",
"keys": [
@@ -181,6 +175,18 @@
"bareBundleId"
]
},
{
"path": "/lib/bare/bundles/bareBoot.js",
"keys": [
"bareBoot"
]
},
{
"path": "/lib/bare/bundles/bareDaemon.js",
"keys": [
"bareDaemon"
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
@@ -193,12 +199,6 @@
"bareDebugLog"
]
},
{
"path": "/lib/bare/bundles/bareDaemon.js",
"keys": [
"bareDaemon"
]
},
{
"path": "/lib/bare/bundles/bareDelta.js",
"keys": [
@@ -229,24 +229,12 @@
"bareEnv"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareDgram.js",
"keys": [
"bareDgram"
]
},
{
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
"keys": [
"bareFfmpegEncodings"
]
},
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
@@ -254,9 +242,21 @@
]
},
{
"path": "/lib/bare/bundles/bareFormData.js",
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareFormData"
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
"keys": [
"bareFfmpegEncodings"
]
},
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
]
},
{
@@ -272,15 +272,9 @@
]
},
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"path": "/lib/bare/bundles/bareFormData.js",
"keys": [
"bareFileLogger"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareHrtime"
"bareFormData"
]
},
{
@@ -289,24 +283,30 @@
"bareHeif"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareHrtime"
]
},
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [
"bareHttpParser"
]
},
{
"path": "/lib/bare/bundles/bareGtk.js",
"keys": [
"bareGtk"
]
},
{
"path": "/lib/bare/bundles/bareFs.js",
"keys": [
"bareFs"
]
},
{
"path": "/lib/bare/bundles/bareGtk.js",
"keys": [
"bareGtk"
]
},
{
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
@@ -319,6 +319,12 @@
"bareImageResample"
]
},
{
"path": "/lib/bare/bundles/bareHttps.js",
"keys": [
"bareHttps"
]
},
{
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
@@ -331,12 +337,6 @@
"bareInspect"
]
},
{
"path": "/lib/bare/bundles/bareHttps.js",
"keys": [
"bareHttps"
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"keys": [
@@ -367,18 +367,18 @@
"bareLogger"
]
},
{
"path": "/lib/bare/bundles/bareLink.js",
"keys": [
"bareLink"
]
},
{
"path": "/lib/bare/bundles/bareInspector.js",
"keys": [
"bareInspector"
]
},
{
"path": "/lib/bare/bundles/bareLink.js",
"keys": [
"bareLink"
]
},
{
"path": "/lib/bare/bundles/bareMake.js",
"keys": [
@@ -415,24 +415,30 @@
"bareNdk"
]
},
{
"path": "/lib/bare/bundles/bareNative.js",
"keys": [
"bareNative"
]
},
{
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/bareNative.js",
"keys": [
"bareNative"
]
},
{
"path": "/lib/bare/bundles/bareNet.js",
"keys": [
"bareNet"
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"keys": [
"bareOpen"
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
@@ -445,18 +451,6 @@
"bareOs"
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"keys": [
"bareOpen"
]
},
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
@@ -469,6 +463,12 @@
"barePerformance"
]
},
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{
"path": "/lib/bare/bundles/barePng.js",
"keys": [
@@ -482,9 +482,9 @@
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"path": "/lib/bare/bundles/barePunycode.js",
"keys": [
"bareDev"
"barePunycode"
]
},
{
@@ -493,18 +493,24 @@
"bareNodeRuntime"
]
},
{
"path": "/lib/bare/bundles/barePunycode.js",
"keys": [
"barePunycode"
]
},
{
"path": "/lib/bare/bundles/barePrebuild.js",
"keys": [
"barePrebuild"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
@@ -517,18 +523,18 @@
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareRealm.js",
"keys": [
"bareRealm"
]
},
{
"path": "/lib/bare/bundles/barePromClient.js",
"keys": [
"barePromClient"
]
},
{
"path": "/lib/bare/bundles/bareRuntime.js",
"keys": [
@@ -547,36 +553,12 @@
"bareSemver"
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{
"path": "/lib/bare/bundles/barePromClient.js",
"keys": [
"barePromClient"
]
},
{
"path": "/lib/bare/bundles/bareRepl.js",
"keys": [
"bareRepl"
]
},
{
"path": "/lib/bare/bundles/bareSignals.js",
"keys": [
"bareSignals"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
@@ -584,15 +566,21 @@
]
},
{
"path": "/lib/bare/bundles/bareStorage.js",
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareStorage"
"bareRpc"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"path": "/lib/bare/bundles/bareRepl.js",
"keys": [
"bareStringDecoder"
"bareRepl"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
@@ -601,12 +589,24 @@
"bareStdio"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStream"
]
},
{
"path": "/lib/bare/bundles/bareStorage.js",
"keys": [
"bareStorage"
]
},
{
"path": "/lib/bare/bundles/bareSvg.js",
"keys": [
@@ -625,12 +625,6 @@
"bareSystemLogger"
]
},
{
"path": "/lib/bare/bundles/bareTap.js",
"keys": [
"bareTap"
]
},
{
"path": "/lib/bare/bundles/bareSubprocess.js",
"keys": [
@@ -643,6 +637,18 @@
"bareTiff"
]
},
{
"path": "/lib/bare/bundles/bareTap.js",
"keys": [
"bareTap"
]
},
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
@@ -655,18 +661,6 @@
"bareThread"
]
},
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
@@ -674,15 +668,9 @@
]
},
{
"path": "/lib/bare/bundles/bareUiKit.js",
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareUiKit"
]
},
{
"path": "/lib/bare/bundles/bareUnpack.js",
"keys": [
"bareUnpack"
"bareTpl"
]
},
{
@@ -698,15 +686,21 @@
]
},
{
"path": "/lib/bare/bundles/bareV8.js",
"path": "/lib/bare/bundles/bareUnpack.js",
"keys": [
"bareV8"
"bareUnpack"
]
},
{
"path": "/lib/bare/bundles/bareVm.js",
"path": "/lib/bare/bundles/bareUiKit.js",
"keys": [
"bareVm"
"bareUiKit"
]
},
{
"path": "/lib/bare/bundles/bareV8.js",
"keys": [
"bareV8"
]
},
{
@@ -722,15 +716,21 @@
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareWebKit"
"bareVm"
]
},
{
"path": "/lib/bare/bundles/bareWhich.js",
"path": "/lib/bare/bundles/bareUtils.js",
"keys": [
"bareWhich"
"bareUtils"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
@@ -740,9 +740,9 @@
]
},
{
"path": "/lib/bare/bundles/bareUtils.js",
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareUtils"
"bareWhich"
]
},
{
@@ -775,12 +775,6 @@
"bareZlib"
]
},
{
"path": "/lib/bare/bundles/bareZmq.js",
"keys": [
"bareZmq"
]
},
{
"path": "/lib/bare/bundles/bareWs.js",
"keys": [
@@ -792,6 +786,12 @@
"keys": [
"bareWorker"
]
},
{
"path": "/lib/bare/bundles/bareZmq.js",
"keys": [
"bareZmq"
]
}
],
"bundleStats": {
File diff suppressed because one or more lines are too long