New utils
This commit is contained in:
+4
-1
@@ -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 shell’s 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 shell’s 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
@@ -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'
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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'
|
||||
|
||||
@@ -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'))
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user