core utils updates
This commit is contained in:
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* POSIX-oriented awk interpreter for Bare OS (no import; concatenated before src/awk.js).
|
||||
* Supports: BEGIN/END, /re/, line patterns, {}, print/printf, if/else, while, for(;;), for(x in a),
|
||||
|
||||
+85
-11
@@ -60,20 +60,94 @@ 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 parts = argv.slice(1).filter((a) => a !== '--')
|
||||
if (!parts.length) {
|
||||
let all = false
|
||||
/** @type {string | null} */
|
||||
let suffix = null
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-a' || a === '--multiple') {
|
||||
all = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-s' || a === '--suffix') {
|
||||
const next = argv[i + 1]
|
||||
if (next == null) {
|
||||
ctx.console.error('basename: option requires an argument -- s')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
suffix = String(next)
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('basename: unsupported option: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
const paths = argv.slice(i).filter((x) => x !== '--')
|
||||
if (!paths.length) {
|
||||
ctx.console.error('basename: missing operand')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const path = parts[0]
|
||||
const suffix = parts[1] || ''
|
||||
let base = path.replace(/\/+$/, '')
|
||||
const slash = base.lastIndexOf('/')
|
||||
base = slash === -1 ? base : base.slice(slash + 1)
|
||||
if (!base) base = path
|
||||
if (suffix && base.endsWith(suffix) && base.length > suffix.length) {
|
||||
base = base.slice(0, -suffix.length)
|
||||
if (!all && paths.length > 2) {
|
||||
ctx.console.error('basename: extra operand (use -a for multiple paths)')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
function take(path, suf) {
|
||||
let base = path.replace(/\/+$/, '')
|
||||
const slash = base.lastIndexOf('/')
|
||||
base = slash === -1 ? base : base.slice(slash + 1)
|
||||
if (!base) base = path
|
||||
const s = suf != null ? suf : ''
|
||||
if (s && base.endsWith(s) && base.length > s.length) base = base.slice(0, -s.length)
|
||||
ctx.console.log(base)
|
||||
}
|
||||
|
||||
if (!all && paths.length === 2 && suffix == null) {
|
||||
take(paths[0], paths[1])
|
||||
return
|
||||
}
|
||||
for (const p of paths) {
|
||||
take(p, suffix)
|
||||
}
|
||||
ctx.console.log(base)
|
||||
}
|
||||
|
||||
+182
-3
@@ -60,20 +60,199 @@ 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 bareCatOut(ctx, s) {
|
||||
const w = globalThis.process?.stdout?.write
|
||||
if (typeof w === 'function') {
|
||||
w.call(globalThis.process.stdout, s)
|
||||
} else {
|
||||
ctx.console.log(s)
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} line @param {{ showTabs: boolean, showEnds: boolean, showNonprinting: boolean }} o */
|
||||
function catTransformLine(line, o) {
|
||||
let out = ''
|
||||
for (let k = 0; k < line.length; k++) {
|
||||
const ch = line[k]
|
||||
const code = line.charCodeAt(k)
|
||||
if (o.showTabs && ch === '\t') {
|
||||
out += '^I'
|
||||
continue
|
||||
}
|
||||
if (o.showNonprinting && code < 32 && ch !== '\t') {
|
||||
out += '^' + String.fromCharCode(code + 64)
|
||||
continue
|
||||
}
|
||||
if (o.showNonprinting && code === 127) {
|
||||
out += '^?'
|
||||
continue
|
||||
}
|
||||
if (o.showNonprinting && code > 127) {
|
||||
out += 'M-' + String.fromCharCode(code - 128)
|
||||
continue
|
||||
}
|
||||
out += ch
|
||||
}
|
||||
if (o.showEnds) out += '$'
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {{ numberLines: boolean, numberNonblank: boolean, showTabs: boolean, showEnds: boolean, showNonprinting: boolean }} opt
|
||||
*/
|
||||
function catFormat(text, opt) {
|
||||
const t = {
|
||||
showTabs: opt.showTabs,
|
||||
showEnds: opt.showEnds,
|
||||
showNonprinting: opt.showNonprinting
|
||||
}
|
||||
const useNum = opt.numberLines || opt.numberNonblank
|
||||
let n = 1
|
||||
let out = ''
|
||||
let i = 0
|
||||
while (i < text.length) {
|
||||
const j = text.indexOf('\n', i)
|
||||
const end = j === -1 ? text.length : j
|
||||
const rawLine = text.slice(i, end)
|
||||
const blank = rawLine.length === 0
|
||||
const lineOut = catTransformLine(rawLine, t)
|
||||
if (useNum) {
|
||||
const numThis =
|
||||
opt.numberNonblank && blank ? false : opt.numberLines || opt.numberNonblank
|
||||
if (numThis) {
|
||||
out += String(n).padStart(6, ' ') + '\t' + lineOut
|
||||
n++
|
||||
} else {
|
||||
out += lineOut
|
||||
}
|
||||
} else {
|
||||
out += lineOut
|
||||
}
|
||||
if (j === -1) break
|
||||
out += '\n'
|
||||
i = j + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** @returns {{ error?: string, opt: ReturnType<typeof defaultCatOpt>, files: string[] }} */
|
||||
function parseCatArgv(argv) {
|
||||
const opt = {
|
||||
numberLines: false,
|
||||
numberNonblank: false,
|
||||
showTabs: false,
|
||||
showEnds: false,
|
||||
showNonprinting: false
|
||||
}
|
||||
const files = []
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
files.push(...argv.slice(i + 1))
|
||||
return { opt, files }
|
||||
}
|
||||
if (!a.startsWith('-')) {
|
||||
files.push(a)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
const body = a.slice(1)
|
||||
if (body === '') {
|
||||
files.push('-')
|
||||
i++
|
||||
continue
|
||||
}
|
||||
for (let k = 0; k < body.length; k++) {
|
||||
const c = body[k]
|
||||
if (c === 'n') opt.numberLines = true
|
||||
else if (c === 'b') opt.numberNonblank = true
|
||||
else if (c === 'A') {
|
||||
opt.showTabs = true
|
||||
opt.showEnds = true
|
||||
opt.showNonprinting = true
|
||||
} else if (c === 'e') {
|
||||
opt.showEnds = true
|
||||
opt.showNonprinting = true
|
||||
} else if (c === 't') {
|
||||
opt.showTabs = true
|
||||
opt.showNonprinting = true
|
||||
} else if (c === 'E') opt.showEnds = true
|
||||
else if (c === 'T') opt.showTabs = true
|
||||
else if (c === 'v') opt.showNonprinting = true
|
||||
else return { error: 'cat: invalid option -- ' + c, opt, files: [] }
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (opt.numberNonblank) opt.numberLines = false
|
||||
return { opt, files }
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const parsed = parseCatArgv(argv)
|
||||
if (parsed.error) {
|
||||
ctx.console.error(parsed.error)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const { opt, files } = parsed
|
||||
const vfs = ctx.vfs
|
||||
const files = argv.slice(1)
|
||||
const anyOpt =
|
||||
opt.numberLines ||
|
||||
opt.numberNonblank ||
|
||||
opt.showTabs ||
|
||||
opt.showEnds ||
|
||||
opt.showNonprinting
|
||||
|
||||
async function emitOne(text) {
|
||||
const s = anyOpt ? catFormat(text, opt) : text
|
||||
bareCatOut(ctx, s)
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
const s = bareStdin(ctx)
|
||||
ctx.console.log(s)
|
||||
await emitOne(s)
|
||||
return
|
||||
}
|
||||
for (const f of files) {
|
||||
if (f === '-') {
|
||||
await emitOne(bareStdin(ctx))
|
||||
continue
|
||||
}
|
||||
const buf = await vfs.readFile(f)
|
||||
if (!buf) {
|
||||
ctx.console.error('cat: ' + f + ': No such file or directory')
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
ctx.console.log(ctx.b4a.toString(buf))
|
||||
await emitOne(ctx.b4a.toString(buf))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* chgrp — change file group (Hyperdrive metadata on writable paths).
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 bareChmodSymbolic(curPerm, spec) {
|
||||
let m = curPerm & 0o777
|
||||
const parts = spec.split(',')
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* chown — change file owner and group (Hyperdrive metadata on writable paths).
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/** POSIX cksum CRC (Open Group / npm `cksum` reference). */
|
||||
const BARE_CKSUM_TAB = new Uint32Array([
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 seq = '\x1b[H\x1b[2J\x1b[3J'
|
||||
if (typeof ctx.writeScreen === 'function') {
|
||||
|
||||
+73
-7
@@ -60,16 +60,72 @@ function barePosixBlocks(size) {
|
||||
return Math.ceil(Number(size) / 512) || 0
|
||||
}
|
||||
|
||||
async function copyPath(ctx, from, to, recursive) {
|
||||
/**
|
||||
* 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 copyPath(ctx, from, to, recursive, followSymlink) {
|
||||
const st = await ctx.vfs.lstat(from)
|
||||
if (!st) {
|
||||
ctx.console.error('cp: ' + from + ': No such file')
|
||||
return false
|
||||
}
|
||||
if (st.type === 'symlink') {
|
||||
const t = await ctx.vfs.readlink(from)
|
||||
await ctx.vfs.symlink(t, to)
|
||||
return true
|
||||
if (!followSymlink) {
|
||||
const t = await ctx.vfs.readlink(from)
|
||||
await ctx.vfs.symlink(t, to)
|
||||
return true
|
||||
}
|
||||
const fst = await ctx.vfs.stat(from)
|
||||
if (fst.type === 'file') {
|
||||
const buf = await ctx.vfs.readFile(from)
|
||||
if (!buf) {
|
||||
ctx.console.error('cp: cannot read ' + from)
|
||||
return false
|
||||
}
|
||||
await ctx.vfs.writeFile(to, buf)
|
||||
return true
|
||||
}
|
||||
if (fst.type === 'directory') {
|
||||
if (!recursive) {
|
||||
ctx.console.error('cp: ' + from + ': Is a directory')
|
||||
return false
|
||||
}
|
||||
await ctx.vfs.mkdir(to, { recursive: true })
|
||||
const names = await ctx.vfs.readdir(from)
|
||||
for (const n of names) {
|
||||
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
|
||||
}
|
||||
return true
|
||||
}
|
||||
ctx.console.error('cp: cannot copy special file ' + from)
|
||||
return false
|
||||
}
|
||||
if (st.type === 'file') {
|
||||
const buf = await ctx.vfs.readFile(from)
|
||||
@@ -91,7 +147,7 @@ async function copyPath(ctx, from, to, recursive) {
|
||||
if (n === '.bareos_empty') continue
|
||||
const f = from.replace(/\/+$/, '') + '/' + n
|
||||
const t = to.replace(/\/+$/, '') + '/' + n
|
||||
if (!(await copyPath(ctx, f, t, true))) return false
|
||||
if (!(await copyPath(ctx, f, t, true, followSymlink))) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -100,6 +156,7 @@ async function copyPath(ctx, from, to, recursive) {
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let recursive = false
|
||||
let followSymlink = false
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
@@ -107,6 +164,14 @@ async function run(ctx, argv) {
|
||||
recursive = true
|
||||
continue
|
||||
}
|
||||
if (a === '-L' || a === '--dereference') {
|
||||
followSymlink = true
|
||||
continue
|
||||
}
|
||||
if (a === '-P' || a === '--no-dereference') {
|
||||
followSymlink = false
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
paths.push(...argv.slice(i + 1))
|
||||
break
|
||||
@@ -119,7 +184,7 @@ async function run(ctx, argv) {
|
||||
paths.push(a)
|
||||
}
|
||||
if (paths.length < 2) {
|
||||
ctx.console.error('usage: cp [-R] SOURCE... DEST')
|
||||
ctx.console.error('usage: cp [-R] [-L|-P] SOURCE... DEST')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -143,6 +208,7 @@ async function run(ctx, argv) {
|
||||
destIsDir || sources.length > 1
|
||||
? dest.replace(/\/+$/, '') + '/' + base
|
||||
: dest
|
||||
if (!(await copyPath(ctx, src, target, recursive))) ctx.exitCode = 1
|
||||
if (!(await copyPath(ctx, src, target, recursive, followSymlink)))
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 vfs = ctx.vfs
|
||||
const home = vfs.env?.HOME || '/home/guest'
|
||||
|
||||
+45
-4
@@ -60,9 +60,37 @@ 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 delim = '\t'
|
||||
let fieldsSpec = ''
|
||||
let suppressNoDelim = false
|
||||
const files = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
@@ -74,6 +102,10 @@ async function run(ctx, argv) {
|
||||
fieldsSpec = argv[++i] || ''
|
||||
continue
|
||||
}
|
||||
if (a === '-s' || a === '--only-delimited') {
|
||||
suppressNoDelim = true
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
files.push(...argv.slice(i + 1))
|
||||
break
|
||||
@@ -86,7 +118,7 @@ async function run(ctx, argv) {
|
||||
files.push(a)
|
||||
}
|
||||
if (!fieldsSpec) {
|
||||
ctx.console.error('usage: cut -f LIST [-d DELIM] [FILE...]')
|
||||
ctx.console.error('usage: cut -f LIST [-d DELIM] [-s] [FILE...]')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -110,17 +142,26 @@ async function run(ctx, argv) {
|
||||
return
|
||||
}
|
||||
|
||||
const delimChar = delim === '\\t' ? '\t' : delim
|
||||
|
||||
async function cutLines(text) {
|
||||
const lines = text.split(/\r?\n/)
|
||||
for (const line of lines) {
|
||||
if (line === '' && lines.length === 1) continue
|
||||
const cols =
|
||||
delim === '' ? [...line] : line.split(delim === '\\t' ? '\t' : delim)
|
||||
if (
|
||||
suppressNoDelim &&
|
||||
delim !== '' &&
|
||||
delimChar !== '' &&
|
||||
!line.includes(delimChar)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const cols = delim === '' ? [...line] : line.split(delimChar)
|
||||
const out = []
|
||||
for (const n of [...want].sort((a, b) => a - b)) {
|
||||
out.push(cols[n - 1] != null ? cols[n - 1] : '')
|
||||
}
|
||||
ctx.console.log(out.join(delim === '\\t' ? '\t' : delim))
|
||||
ctx.console.log(out.join(delimChar))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+134
@@ -60,9 +60,143 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* date — print current date/time; optional +FORMAT (strftime-like subset).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} fmt
|
||||
* @param {Date} d
|
||||
* @param {boolean} utc
|
||||
*/
|
||||
function bareDateFormat(fmt, d, utc) {
|
||||
const pad2 = (n) => String(n).padStart(2, '0')
|
||||
const pad3 = (n) => String(n).padStart(3, '0')
|
||||
const Y = utc ? d.getUTCFullYear() : d.getFullYear()
|
||||
const m = utc ? d.getUTCMonth() + 1 : d.getMonth() + 1
|
||||
const day = utc ? d.getUTCDate() : d.getDate()
|
||||
const H = utc ? d.getUTCHours() : d.getHours()
|
||||
const M = utc ? d.getUTCMinutes() : d.getMinutes()
|
||||
const S = utc ? d.getUTCSeconds() : d.getSeconds()
|
||||
const ms = utc ? d.getUTCMilliseconds() : d.getMilliseconds()
|
||||
|
||||
let o = ''
|
||||
for (let i = 0; i < fmt.length; i++) {
|
||||
if (fmt[i] !== '%') {
|
||||
o += fmt[i]
|
||||
continue
|
||||
}
|
||||
if (fmt[i + 1] === '%') {
|
||||
o += '%'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
const spec = fmt[i + 1] || ''
|
||||
i++
|
||||
switch (spec) {
|
||||
case 'Y':
|
||||
o += String(Y).padStart(4, '0')
|
||||
break
|
||||
case 'm':
|
||||
o += pad2(m)
|
||||
break
|
||||
case 'd':
|
||||
o += pad2(day)
|
||||
break
|
||||
case 'H':
|
||||
o += pad2(H)
|
||||
break
|
||||
case 'M':
|
||||
o += pad2(M)
|
||||
break
|
||||
case 'S':
|
||||
o += pad2(S)
|
||||
break
|
||||
case 's':
|
||||
o += String(Math.floor(d.getTime() / 1000))
|
||||
break
|
||||
case '3N':
|
||||
o += pad3(ms)
|
||||
break
|
||||
case 'z': {
|
||||
if (utc) {
|
||||
o += '+0000'
|
||||
break
|
||||
}
|
||||
const offMin = -d.getTimezoneOffset()
|
||||
const sign = offMin >= 0 ? '+' : '-'
|
||||
const abs = Math.abs(offMin)
|
||||
const oh = Math.floor(abs / 60)
|
||||
const om = abs % 60
|
||||
o += sign + pad2(oh) + pad2(om)
|
||||
break
|
||||
}
|
||||
case 'a': {
|
||||
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
o += days[utc ? d.getUTCDay() : d.getDay()]
|
||||
break
|
||||
}
|
||||
case 'b': {
|
||||
const mons = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec'
|
||||
]
|
||||
o += mons[(utc ? d.getUTCMonth() : d.getMonth()) || 0]
|
||||
break
|
||||
}
|
||||
default:
|
||||
o += '%' + spec
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const utc = argv.includes('-u') || argv.includes('--utc')
|
||||
const rest = argv.filter((a) => a !== '-u' && a !== '--utc')
|
||||
const plus = rest.find((a) => a.startsWith('+') && a.length > 1)
|
||||
const d = new Date()
|
||||
if (plus) {
|
||||
ctx.console.log(bareDateFormat(plus.slice(1), d, utc))
|
||||
return
|
||||
}
|
||||
ctx.console.log(
|
||||
utc
|
||||
? d
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* GNU-style LS_COLORS string + dircolors(5) database parsing and file classification.
|
||||
* Consumed by bare-os-booter (ESM import). Prepended into /bin/ls and /bin/dircolors by bare-os-coreutils build (no import there).
|
||||
|
||||
+71
-10
@@ -60,17 +60,78 @@ function barePosixBlocks(size) {
|
||||
return Math.ceil(Number(size) / 512) || 0
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const parts = argv.slice(1).filter((a) => a !== '--')
|
||||
if (!parts.length) {
|
||||
ctx.console.error('dirname: missing operand')
|
||||
/**
|
||||
* 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 dirnameEmit(ctx, out, nullSep) {
|
||||
if (!nullSep) {
|
||||
ctx.console.log(out)
|
||||
return
|
||||
}
|
||||
for (const path of parts) {
|
||||
const cleaned = path.replace(/\/+$/, '') || '/'
|
||||
const i = cleaned.lastIndexOf('/')
|
||||
const out =
|
||||
i <= 0 ? (cleaned[0] === '/' ? '/' : '.') : cleaned.slice(0, i) || '/'
|
||||
ctx.console.log(out)
|
||||
if (!bareOsEmitRaw(ctx, out + '\0')) {
|
||||
ctx.console.error(
|
||||
'dirname: -z requires process.stdout.write or ctx.bareOsBinWrite'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let nullSep = false
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-z' || a === '--zero') {
|
||||
nullSep = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('dirname: unsupported option: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
const paths = argv.slice(i).filter((x) => x !== '--')
|
||||
if (!paths.length) {
|
||||
ctx.console.error('dirname: missing operand')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
for (const path of paths) {
|
||||
const cleaned = path.replace(/\/+$/, '') || '/'
|
||||
const j = cleaned.lastIndexOf('/')
|
||||
const out =
|
||||
j <= 0 ? (cleaned[0] === '/' ? '/' : '.') : cleaned.slice(0, j) || '/'
|
||||
dirnameEmit(ctx, out, nullSep)
|
||||
}
|
||||
}
|
||||
|
||||
+117
-6
@@ -60,8 +60,60 @@ function barePosixBlocks(size) {
|
||||
return Math.ceil(Number(size) / 512) || 0
|
||||
}
|
||||
|
||||
async function duPath(ctx, path, blockSize) {
|
||||
const st = await ctx.vfs.lstat(path)
|
||||
/**
|
||||
* 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 duBytes(ctx, path, followSymlinks) {
|
||||
const st = followSymlinks
|
||||
? await ctx.vfs.stat(path)
|
||||
: await ctx.vfs.lstat(path)
|
||||
if (!st) return 0
|
||||
if (st.type === 'file' || st.type === 'symlink') {
|
||||
return st.size || 0
|
||||
}
|
||||
let total = st.size || 0
|
||||
let names = []
|
||||
try {
|
||||
names = await ctx.vfs.readdir(path)
|
||||
} catch {
|
||||
return total
|
||||
}
|
||||
for (const n of names) {
|
||||
if (n === '.bareos_empty') continue
|
||||
const sub = path.replace(/\/+$/, '') + '/' + n
|
||||
total += await duBytes(ctx, sub, followSymlinks)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
async function duPath(ctx, path, blockSize, followSymlinks) {
|
||||
const st = followSymlinks
|
||||
? await ctx.vfs.stat(path)
|
||||
: await ctx.vfs.lstat(path)
|
||||
if (!st) return 0
|
||||
if (st.type === 'file' || st.type === 'symlink') {
|
||||
return Math.ceil((st.size || 0) / blockSize) || 1
|
||||
@@ -76,24 +128,83 @@ async function duPath(ctx, path, blockSize) {
|
||||
for (const n of names) {
|
||||
if (n === '.bareos_empty') continue
|
||||
const sub = path.replace(/\/+$/, '') + '/' + n
|
||||
total += await duPath(ctx, sub, blockSize)
|
||||
total += await duPath(ctx, sub, blockSize, followSymlinks)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
/** @param {number} bytes */
|
||||
function duHuman(bytes) {
|
||||
if (!Number.isFinite(bytes) || bytes < 0) bytes = 0
|
||||
const units = ['K', 'M', 'G', 'T', 'P']
|
||||
if (bytes < 1024) return String(bytes)
|
||||
let u = -1
|
||||
let v = bytes
|
||||
while (v >= 1024 && u < units.length - 1) {
|
||||
v /= 1024
|
||||
u++
|
||||
}
|
||||
const rounded = v >= 10 ? Math.round(v) : Math.round(v * 10) / 10
|
||||
const s = String(rounded).replace(/\.0$/, '')
|
||||
return s + units[u]
|
||||
}
|
||||
|
||||
async function duPrintAll(ctx, path, human, blockSize, followSymlinks) {
|
||||
const st = followSymlinks
|
||||
? await ctx.vfs.stat(path)
|
||||
: await ctx.vfs.lstat(path)
|
||||
if (!st) return
|
||||
const bytes = st.size || 0
|
||||
const blocks =
|
||||
human || blockSize <= 0
|
||||
? null
|
||||
: Math.ceil(bytes / blockSize) || (bytes === 0 ? 0 : 1)
|
||||
const label = human ? duHuman(bytes) : String(blocks)
|
||||
ctx.console.log(label + '\t' + path)
|
||||
if (st.type !== 'directory') return
|
||||
let names = []
|
||||
try {
|
||||
names = await ctx.vfs.readdir(path)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
for (const n of names) {
|
||||
if (n === '.bareos_empty') continue
|
||||
const sub = path.replace(/\/+$/, '') + '/' + n
|
||||
await duPrintAll(ctx, sub, human, blockSize, followSymlinks)
|
||||
}
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let blockSize = 512
|
||||
let human = false
|
||||
let allFiles = false
|
||||
let followSymlinks = false
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
if (argv[i] === '-k') blockSize = 1024
|
||||
else if (argv[i] !== '--') paths.push(argv[i])
|
||||
else if (argv[i] === '-h' || argv[i] === '--human-readable') human = true
|
||||
else if (argv[i] === '-a' || argv[i] === '--all') allFiles = true
|
||||
else if (argv[i] === '-L' || argv[i] === '--dereference') followSymlinks = true
|
||||
else if (argv[i] === '-s' || argv[i] === '--summarize') {
|
||||
/* Bare du already prints one total per path (GNU -s default for us). */
|
||||
} else if (argv[i] !== '--') paths.push(argv[i])
|
||||
}
|
||||
const targets = paths.length ? paths : ['.']
|
||||
for (const p of targets) {
|
||||
try {
|
||||
const abs = ctx.vfs.resolveLogical(p)
|
||||
const kb = await duPath(ctx, abs, blockSize)
|
||||
ctx.console.log(String(kb) + '\t' + p)
|
||||
if (allFiles) {
|
||||
await duPrintAll(ctx, abs, human, blockSize, followSymlinks)
|
||||
continue
|
||||
}
|
||||
if (human) {
|
||||
const b = await duBytes(ctx, abs, followSymlinks)
|
||||
ctx.console.log(duHuman(b) + '\t' + p)
|
||||
} else {
|
||||
const kb = await duPath(ctx, abs, blockSize, followSymlinks)
|
||||
ctx.console.log(String(kb) + '\t' + p)
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.console.error('du: ' + p + ': ' + ((e && e.message) || e))
|
||||
ctx.exitCode = 1
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 parts = argv.slice(1)
|
||||
let n = false
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/** ANSI helpers for /bin/edit (preamble; no import in src). */
|
||||
|
||||
const EDIT_ANSI_RESET = '\x1b[0m'
|
||||
|
||||
+94
-3
@@ -60,9 +60,100 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse leading NAME=value assignments; first non-assignment starts the utility argv.
|
||||
* @param {string[]} rest
|
||||
* @returns {{ assigns: Record<string, string>, utility: string[] }}
|
||||
*/
|
||||
function parseEnvLeadingAssigns(rest) {
|
||||
const assigns = {}
|
||||
let i = 0
|
||||
for (; i < rest.length; i++) {
|
||||
const a = rest[i]
|
||||
const eq = a.indexOf('=')
|
||||
if (eq <= 0) break
|
||||
const name = a.slice(0, eq)
|
||||
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) break
|
||||
assigns[name] = a.slice(eq + 1)
|
||||
}
|
||||
return { assigns, utility: rest.slice(i) }
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const e = ctx.vfs.env
|
||||
for (const k of Object.keys(e).sort()) {
|
||||
ctx.console.log(k + '=' + (e[k] ?? ''))
|
||||
let ignore = false
|
||||
let i = 1
|
||||
for (; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-i' || a === '--ignore-environment') {
|
||||
ignore = true
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('env: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
const rest = argv.slice(i)
|
||||
const { assigns, utility } = parseEnvLeadingAssigns(rest)
|
||||
const base = ignore ? {} : { ...ctx.vfs.env }
|
||||
const newEnv = Object.assign(base, assigns)
|
||||
|
||||
if (!utility.length) {
|
||||
for (const k of Object.keys(newEnv).sort()) {
|
||||
ctx.console.log(k + '=' + (newEnv[k] ?? ''))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof ctx.runBinCommand !== 'function') {
|
||||
ctx.console.error(
|
||||
'env: ctx.runBinCommand is not available (requires a Bare OS booter session)'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
const oldEnv = ctx.vfs.env
|
||||
ctx.vfs.env = newEnv
|
||||
if (ctx.env && typeof ctx.env === 'object') ctx.env = newEnv
|
||||
try {
|
||||
ctx.exitCode = 0
|
||||
await ctx.runBinCommand(utility)
|
||||
} finally {
|
||||
ctx.vfs.env = oldEnv
|
||||
if (ctx.env && typeof ctx.env === 'object') ctx.env = oldEnv
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/** Drive-resident exit: ends the booter session (ctx.requestBooterExit from bare-os-booter). */
|
||||
async function run(ctx, argv) {
|
||||
let ec = 0
|
||||
|
||||
@@ -60,6 +60,33 @@ 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) {
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
|
||||
+197
-30
@@ -60,21 +60,91 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* curDepth is distance from the search root directory (0 at the initial path).
|
||||
* Printed paths use gnu-like depth curDepth + 1 (immediate children of the root are depth 1).
|
||||
*/
|
||||
async function walk(
|
||||
ctx,
|
||||
dir,
|
||||
nameRe,
|
||||
pathRe,
|
||||
wantType,
|
||||
maxDepth,
|
||||
minDepth,
|
||||
curDepth
|
||||
) {
|
||||
if (maxDepth >= 0 && curDepth > maxDepth) return
|
||||
function findEmitLine(ctx, path, print0) {
|
||||
if (!print0) {
|
||||
ctx.console.log(path)
|
||||
return
|
||||
}
|
||||
if (!bareOsEmitRaw(ctx, path + '\0')) {
|
||||
ctx.console.error(
|
||||
'find: -print0 requires process.stdout.write or ctx.bareOsBinWrite'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
function parseMtimeSpec(s) {
|
||||
const t = String(s)
|
||||
let op = ''
|
||||
let num = t
|
||||
if (t.startsWith('+')) {
|
||||
op = '+'
|
||||
num = t.slice(1)
|
||||
} else if (t.startsWith('-') && t.length > 1) {
|
||||
op = '-'
|
||||
num = t.slice(1)
|
||||
}
|
||||
const n = Number.parseFloat(num)
|
||||
if (!Number.isFinite(n)) return null
|
||||
return { op, n }
|
||||
}
|
||||
|
||||
/** @param {{ op: string, n: number }} spec */
|
||||
function findMatchMtime(st, spec) {
|
||||
const days = (Date.now() - st.mtimeMs) / 86400000
|
||||
if (spec.op === '+') return days > spec.n
|
||||
if (spec.op === '-') return days < spec.n
|
||||
return days >= spec.n && days < spec.n + 1
|
||||
}
|
||||
|
||||
async function findIsEmpty(ctx, path, st) {
|
||||
if (st.type === 'file' || st.type === 'symlink') return (st.size || 0) === 0
|
||||
if (st.type !== 'directory') return false
|
||||
const names = await ctx.vfs.readdir(path)
|
||||
const rest = names.filter((n) => n !== '.bareos_empty')
|
||||
return rest.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} dir
|
||||
* @param {Record<string, unknown>} o
|
||||
* @param {number} curDepth
|
||||
*/
|
||||
async function walk(ctx, dir, o, curDepth) {
|
||||
if (o.maxDepth >= 0 && curDepth > o.maxDepth) return
|
||||
let names
|
||||
try {
|
||||
names = await ctx.vfs.readdir(dir)
|
||||
@@ -92,30 +162,46 @@ async function walk(
|
||||
}
|
||||
if (!st) continue
|
||||
const gnuDepth = curDepth + 1
|
||||
const pathOk = !pathRe || pathRe.test(path)
|
||||
const nameOk = !nameRe || nameRe.test(n)
|
||||
if (pathOk && nameOk) {
|
||||
if (!wantType || st.type === wantType) {
|
||||
if (gnuDepth >= minDepth) ctx.console.log(path)
|
||||
const pathOk = !o.pathRe || o.pathRe.test(path)
|
||||
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.wantEmpty) {
|
||||
match = match && (await findIsEmpty(ctx, path, st))
|
||||
}
|
||||
if (match && gnuDepth >= o.minDepth) {
|
||||
if (o.doDelete) {
|
||||
if (ctx.vfs.env.BARE_OS_FIND_DELETE !== '1') {
|
||||
const stw = /** @type {{ warned?: boolean }} */ (o.deleteState)
|
||||
if (!stw.warned) {
|
||||
ctx.console.error(
|
||||
'find: -delete disabled (set BARE_OS_FIND_DELETE=1 to confirm)'
|
||||
)
|
||||
stw.warned = true
|
||||
}
|
||||
ctx.exitCode = 1
|
||||
} else {
|
||||
try {
|
||||
await ctx.vfs.rm(path, { recursive: true, force: true })
|
||||
} catch (e) {
|
||||
ctx.console.error('find: ' + path + ': ' + (e.message || e))
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
findEmitLine(ctx, path, o.print0)
|
||||
}
|
||||
}
|
||||
if (st.type === 'directory')
|
||||
await walk(
|
||||
ctx,
|
||||
path,
|
||||
nameRe,
|
||||
pathRe,
|
||||
wantType,
|
||||
maxDepth,
|
||||
minDepth,
|
||||
curDepth + 1
|
||||
)
|
||||
if (st.type !== 'directory') continue
|
||||
const absPath = ctx.vfs.resolveLogical(path)
|
||||
if (o.pruneAbs && absPath === o.pruneAbs) continue
|
||||
await walk(ctx, path, o, curDepth + 1)
|
||||
}
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let maxDepth = -1
|
||||
/** Minimum path depth below the search root (1 = default; same as GNU -mindepth 1 for tree walks). */
|
||||
let minDepth = 1
|
||||
/** @type {string | null} */
|
||||
let nameGlob = null
|
||||
@@ -123,6 +209,16 @@ async function run(ctx, argv) {
|
||||
let pathGlob = null
|
||||
/** @type {'file' | 'directory' | 'symlink' | null} */
|
||||
let wantType = null
|
||||
let nameIgnoreCase = false
|
||||
let print0 = false
|
||||
/** @type {{ op: string, n: number } | null} */
|
||||
let mtimeSpec = null
|
||||
/** @type {string | null} */
|
||||
let newerPath = null
|
||||
/** @type {string | null} */
|
||||
let prunePath = null
|
||||
let wantEmpty = false
|
||||
let doDelete = false
|
||||
const rest = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
@@ -141,6 +237,12 @@ async function run(ctx, argv) {
|
||||
}
|
||||
if (a === '-name' && argv[i + 1]) {
|
||||
nameGlob = argv[++i]
|
||||
nameIgnoreCase = false
|
||||
continue
|
||||
}
|
||||
if (a === '-iname' && argv[i + 1]) {
|
||||
nameGlob = argv[++i]
|
||||
nameIgnoreCase = true
|
||||
continue
|
||||
}
|
||||
if (a === '-type' && argv[i + 1]) {
|
||||
@@ -150,6 +252,35 @@ async function run(ctx, argv) {
|
||||
else if (t === 'l') wantType = 'symlink'
|
||||
continue
|
||||
}
|
||||
if (a === '-print0') {
|
||||
print0 = true
|
||||
continue
|
||||
}
|
||||
if (a === '-mtime' && argv[i + 1]) {
|
||||
mtimeSpec = parseMtimeSpec(argv[++i])
|
||||
if (!mtimeSpec) {
|
||||
ctx.console.error('find: invalid -mtime')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (a === '-newer' && argv[i + 1]) {
|
||||
newerPath = argv[++i]
|
||||
continue
|
||||
}
|
||||
if (a === '-prune' && argv[i + 1]) {
|
||||
prunePath = argv[++i]
|
||||
continue
|
||||
}
|
||||
if (a === '-empty') {
|
||||
wantEmpty = true
|
||||
continue
|
||||
}
|
||||
if (a === '-delete') {
|
||||
doDelete = true
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
rest.push(...argv.slice(i + 1))
|
||||
break
|
||||
@@ -168,7 +299,7 @@ async function run(ctx, argv) {
|
||||
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
||||
.replace(/\*/g, '.*')
|
||||
.replace(/\?/g, '.')
|
||||
nameRe = new RegExp('^' + esc + '$')
|
||||
nameRe = new RegExp('^' + esc + '$', nameIgnoreCase ? 'i' : '')
|
||||
}
|
||||
let pathRe = null
|
||||
if (pathGlob) {
|
||||
@@ -180,6 +311,42 @@ async function run(ctx, argv) {
|
||||
.replace(/\0GLOBSTAR\0/g, '.*')
|
||||
pathRe = new RegExp('^' + esc + '$')
|
||||
}
|
||||
/** @type {number | null} */
|
||||
let newerThanMs = null
|
||||
if (newerPath) {
|
||||
try {
|
||||
const st = await ctx.vfs.stat(newerPath)
|
||||
if (!st) {
|
||||
ctx.console.error('find: ' + newerPath + ': No such file')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
newerThanMs = st.mtimeMs
|
||||
} catch (e) {
|
||||
ctx.console.error('find: ' + newerPath + ': ' + (e.message || e))
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
/** @type {string | null} */
|
||||
let pruneAbs = null
|
||||
if (prunePath) {
|
||||
pruneAbs = ctx.vfs.resolveLogical(prunePath).replace(/\/+$/, '') || '/'
|
||||
}
|
||||
const abs = ctx.vfs.resolveLogical(root)
|
||||
await walk(ctx, abs, nameRe, pathRe, wantType, maxDepth, minDepth, 0)
|
||||
const o = {
|
||||
maxDepth,
|
||||
minDepth,
|
||||
nameRe,
|
||||
pathRe,
|
||||
wantType,
|
||||
print0,
|
||||
mtimeSpec,
|
||||
newerThanMs,
|
||||
pruneAbs,
|
||||
wantEmpty,
|
||||
doDelete,
|
||||
deleteState: {}
|
||||
}
|
||||
await walk(ctx, abs, o, 0)
|
||||
}
|
||||
|
||||
+32
-1
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Subset of POSIX.1-2017 getconf — fixed values for Bare OS (no host sysconf).
|
||||
* Unknown names exit with status 1 (matches common getconf for invalid var).
|
||||
@@ -85,7 +112,11 @@ const CONF = {
|
||||
_POSIX_JOB_CONTROL: '0',
|
||||
_POSIX_SAVED_IDS: '0',
|
||||
/** Bare shell line length (reasonable REPL limit, not a hard kernel cap). */
|
||||
BARE_OS_INPUT_LINE_MAX: '8192'
|
||||
BARE_OS_INPUT_LINE_MAX: '8192',
|
||||
/** 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'
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 sub = argv[1]
|
||||
if (sub === 'help' || sub === undefined) {
|
||||
|
||||
+271
-6
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Subset of POSIX/GNU grep behavior using JavaScript RegExp / string search.
|
||||
* Not bit-identical to GNU grep (no PCRE, different escaping, UTF-16 strings).
|
||||
@@ -83,13 +110,18 @@ async function run(ctx, argv) {
|
||||
let onlyMatching = false
|
||||
/** @type {number} */
|
||||
let maxMatchLines = Number.POSITIVE_INFINITY
|
||||
let afterCtx = 0
|
||||
let beforeCtx = 0
|
||||
/** @type {'never' | 'always' | 'auto'} */
|
||||
let colorMode = 'never'
|
||||
let recursive = false
|
||||
|
||||
const args = argv.slice(1)
|
||||
let i = 0
|
||||
|
||||
function usage() {
|
||||
ctx.console.error(
|
||||
'usage: grep [-E|-F] [-i] [-v] [-w] [-x] [-n] [-c] [-l] [-o] [-m NUM] [-q] [-s] [-H|-h] [-e pat] ... [-f file] ... [pattern] [file...]'
|
||||
'usage: grep [-E|-F] [-r] [-i] [-v] [-w] [-x] [-n] [-c] [-l] [-o] [-m NUM] [-A|-B|-C N] [--color=never|always|auto] [-q] [-s] [-H|-h] [-e pat] ... [-f file] ... [pattern] [file...]'
|
||||
)
|
||||
ctx.exitCode = 2
|
||||
}
|
||||
@@ -103,11 +135,89 @@ async function run(ctx, argv) {
|
||||
if (a === '-' || !a.startsWith('-')) break
|
||||
|
||||
if (a.startsWith('--')) {
|
||||
if (a === '--color' || a.startsWith('--color=')) {
|
||||
const v =
|
||||
a === '--color'
|
||||
? args[++i] || 'auto'
|
||||
: a.slice('--color='.length) || 'auto'
|
||||
if (v === 'never' || v === 'always' || v === 'auto') colorMode = v
|
||||
else {
|
||||
ctx.console.error('grep: invalid --color value')
|
||||
ctx.exitCode = 2
|
||||
return
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '--after-context' || a.startsWith('--after-context=')) {
|
||||
const raw =
|
||||
a === '--after-context'
|
||||
? args[++i]
|
||||
: a.slice('--after-context='.length)
|
||||
const n = Number.parseInt(raw, 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
afterCtx = n
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '--before-context' || a.startsWith('--before-context=')) {
|
||||
const raw =
|
||||
a === '--before-context'
|
||||
? args[++i]
|
||||
: a.slice('--before-context='.length)
|
||||
const n = Number.parseInt(raw, 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
beforeCtx = n
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '--recursive') {
|
||||
recursive = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
ctx.console.error('grep: unknown option ' + a)
|
||||
ctx.exitCode = 2
|
||||
return
|
||||
}
|
||||
|
||||
if (a === '-A' || /^-A\d+$/.test(a)) {
|
||||
const n = a === '-A' ? Number.parseInt(args[++i], 10) : Number.parseInt(a.slice(2), 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
afterCtx = n
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-B' || /^-B\d+$/.test(a)) {
|
||||
const n = a === '-B' ? Number.parseInt(args[++i], 10) : Number.parseInt(a.slice(2), 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
beforeCtx = n
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-C' || /^-C\d+$/.test(a)) {
|
||||
const n = a === '-C' ? Number.parseInt(args[++i], 10) : Number.parseInt(a.slice(2), 10)
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
beforeCtx = afterCtx = n
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (a === '-m') {
|
||||
if (i + 1 >= args.length) {
|
||||
usage()
|
||||
@@ -210,6 +320,10 @@ async function run(ctx, argv) {
|
||||
case 'o':
|
||||
onlyMatching = true
|
||||
break
|
||||
case 'r':
|
||||
case 'R':
|
||||
recursive = true
|
||||
break
|
||||
case 'm': {
|
||||
let num = ''
|
||||
let jj = j + 1
|
||||
@@ -264,8 +378,35 @@ async function run(ctx, argv) {
|
||||
patterns.push(rest.shift())
|
||||
}
|
||||
|
||||
const fileArgs = rest
|
||||
const useStdin = fileArgs.length === 0
|
||||
let fatal = false
|
||||
/** @type {string[]} */
|
||||
let fileArgs = rest
|
||||
if (recursive) {
|
||||
if (fileArgs.length === 0) fileArgs = ['.']
|
||||
const acc = []
|
||||
for (const p of fileArgs) {
|
||||
try {
|
||||
const st = await vfs.lstat(p)
|
||||
if (!st) {
|
||||
if (!suppressErrors) ctx.console.error('grep: ' + p + ': No such file')
|
||||
fatal = true
|
||||
continue
|
||||
}
|
||||
if (st.type === 'directory') {
|
||||
await grepWalkFiles(ctx, p, acc, suppressErrors)
|
||||
} else {
|
||||
acc.push(p)
|
||||
}
|
||||
} catch (e) {
|
||||
if (!suppressErrors)
|
||||
ctx.console.error('grep: ' + p + ': ' + (e.message || e))
|
||||
fatal = true
|
||||
}
|
||||
}
|
||||
fileArgs = acc
|
||||
}
|
||||
|
||||
const useStdin = fileArgs.length === 0 && !recursive
|
||||
const inputs = useStdin
|
||||
? [{ label: null, path: null }]
|
||||
: fileArgs.map((p) => ({ label: p, path: p }))
|
||||
@@ -283,8 +424,24 @@ async function run(ctx, argv) {
|
||||
const showName =
|
||||
!noFilename && (multiFile || forceFilename || (useStdin && forceFilename))
|
||||
|
||||
const shellEnv = (ctx.vfs && ctx.vfs.env) || ctx.env || {}
|
||||
const useColor =
|
||||
colorMode === 'always' ||
|
||||
(colorMode === 'auto' &&
|
||||
globalThis.process?.stdout?.isTTY === true &&
|
||||
shellEnv.NO_COLOR !== '1' &&
|
||||
shellEnv.NO_COLOR !== 'true')
|
||||
const C_RED = '\x1b[01;31m'
|
||||
const C_RST = '\x1b[0m'
|
||||
|
||||
const useContext =
|
||||
(afterCtx > 0 || beforeCtx > 0) &&
|
||||
!countOnly &&
|
||||
!listFiles &&
|
||||
!quiet &&
|
||||
!onlyMatching
|
||||
|
||||
let anyMatch = false
|
||||
let fatal = false
|
||||
let matchingLinesTotal = 0
|
||||
|
||||
for (const { label, path } of inputs) {
|
||||
@@ -314,6 +471,50 @@ async function run(ctx, argv) {
|
||||
let fileMatched = false
|
||||
const out = []
|
||||
|
||||
if (useContext) {
|
||||
const n = lines.length
|
||||
/** @type {boolean[]} */
|
||||
const isMatch = new Array(n).fill(false)
|
||||
for (let li = 0; li < n; li++) {
|
||||
if (matchingLinesTotal >= maxMatchLines) break
|
||||
const line = stripCr(lines[li])
|
||||
const matched = matchers.some((fn) => fn(line))
|
||||
const hit = invert ? !matched : matched
|
||||
isMatch[li] = hit
|
||||
if (!hit) continue
|
||||
matchingLinesTotal++
|
||||
fileMatched = true
|
||||
anyMatch = true
|
||||
count++
|
||||
}
|
||||
if (quiet) continue
|
||||
/** @type {number[][]} */
|
||||
const iv = []
|
||||
for (let li = 0; li < n; li++) {
|
||||
if (!isMatch[li]) continue
|
||||
iv.push([
|
||||
Math.max(0, li - beforeCtx),
|
||||
Math.min(n - 1, li + afterCtx)
|
||||
])
|
||||
}
|
||||
const merged = grepMergeIntervals(iv)
|
||||
for (let mi = 0; mi < merged.length; mi++) {
|
||||
if (mi > 0) ctx.console.log('--')
|
||||
for (let li = merged[mi][0]; li <= merged[mi][1]; li++) {
|
||||
const line = stripCr(lines[li])
|
||||
const lineNum = li + 1
|
||||
let body = line
|
||||
if (useColor && isMatch[li] && !invert) body = C_RED + line + C_RST
|
||||
let chunk = numbers ? lineNum + ':' + body : body
|
||||
if (showName && label != null) chunk = label + ':' + chunk
|
||||
else if (showName && label == null && useStdin)
|
||||
chunk = '(standard input):' + chunk
|
||||
ctx.console.log(chunk)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for (let li = 0; li < lines.length; li++) {
|
||||
if (matchingLinesTotal >= maxMatchLines) break
|
||||
const line = stripCr(lines[li])
|
||||
@@ -337,6 +538,7 @@ async function run(ctx, argv) {
|
||||
})
|
||||
for (const part of parts) {
|
||||
let chunk = part
|
||||
if (useColor) chunk = C_RED + chunk + C_RST
|
||||
if (numbers) chunk = lineNum + ':' + chunk
|
||||
if (showName && label != null) chunk = label + ':' + chunk
|
||||
else if (showName && label == null && useStdin)
|
||||
@@ -344,8 +546,9 @@ async function run(ctx, argv) {
|
||||
out.push(chunk)
|
||||
}
|
||||
} else {
|
||||
let chunk = line
|
||||
if (numbers) chunk = lineNum + ':' + chunk
|
||||
let body = line
|
||||
if (useColor && !invert) body = C_RED + line + C_RST
|
||||
let chunk = numbers ? lineNum + ':' + body : body
|
||||
if (showName && label != null) chunk = label + ':' + chunk
|
||||
else if (showName && label == null && useStdin)
|
||||
chunk = '(standard input):' + chunk
|
||||
@@ -378,6 +581,68 @@ async function run(ctx, argv) {
|
||||
else ctx.exitCode = anyMatch ? 0 : 1
|
||||
}
|
||||
|
||||
const GREP_RECURSE_MAX_DEPTH = 64
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} dir
|
||||
* @param {string[]} acc
|
||||
* @param {boolean} suppressErrors
|
||||
*/
|
||||
async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
||||
const vfs = ctx.vfs
|
||||
/** @param {string} d @param {number} depth */
|
||||
const walk = async (d, depth) => {
|
||||
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
||||
let names = []
|
||||
try {
|
||||
names = await vfs.readdir(d)
|
||||
} catch (e) {
|
||||
if (!suppressErrors)
|
||||
ctx.console.error('grep: ' + d + ': ' + (e.message || e))
|
||||
return
|
||||
}
|
||||
for (const n of names) {
|
||||
if (n === '.' || n === '..' || n === '.git') continue
|
||||
const sub = d.endsWith('/') ? d + n : d + '/' + n
|
||||
let st
|
||||
try {
|
||||
st = await vfs.lstat(sub)
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
if (!st) continue
|
||||
if (st.type === 'directory') await walk(sub, depth + 1)
|
||||
else if (st.type === 'file') acc.push(sub)
|
||||
else if (st.type === 'symlink') {
|
||||
try {
|
||||
const ft = await vfs.stat(sub)
|
||||
if (ft && ft.type === 'file') acc.push(sub)
|
||||
} catch {
|
||||
/* skip */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await walk(dir, 0)
|
||||
}
|
||||
|
||||
/** @param {number[][]} iv */
|
||||
function grepMergeIntervals(iv) {
|
||||
if (!iv.length) return []
|
||||
const copy = iv.map((x) => [x[0], x[1]])
|
||||
copy.sort((a, b) => a[0] - b[0])
|
||||
/** @type {number[][]} */
|
||||
const out = [[copy[0][0], copy[0][1]]]
|
||||
for (let k = 1; k < copy.length; k++) {
|
||||
const cur = copy[k]
|
||||
const last = out[out.length - 1]
|
||||
if (cur[0] <= last[1] + 1) last[1] = Math.max(last[1], cur[1])
|
||||
else out.push([cur[0], cur[1]])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} patterns
|
||||
* @param {{ fixed: boolean, icase: boolean, word?: boolean, fullLine?: boolean }} o
|
||||
|
||||
@@ -60,6 +60,33 @@ 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.runHdms === 'function') {
|
||||
await ctx.runHdms(argv)
|
||||
|
||||
+122
-11
@@ -60,27 +60,138 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* head — first lines or bytes of files.
|
||||
*/
|
||||
|
||||
function bareHeadBytesSlice(u8, n) {
|
||||
if (!u8 || !u8.byteLength || n <= 0) return new Uint8Array(0)
|
||||
const end = Math.min(u8.byteLength, n)
|
||||
return u8.subarray(0, end)
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const vfs = ctx.vfs
|
||||
let n = 10
|
||||
let start = 1
|
||||
if (argv[1] === '-n' && argv[2]) {
|
||||
n = parseInt(argv[2], 10) || 10
|
||||
start = 3
|
||||
} else if (argv[1] && /^-\d+$/.test(argv[1])) {
|
||||
n = parseInt(argv[1].slice(1), 10) || 10
|
||||
start = 2
|
||||
/** @type {'lines' | 'bytes'} */
|
||||
let mode = 'lines'
|
||||
let i = 1
|
||||
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-n' || a === '--lines') {
|
||||
const v = argv[i + 1]
|
||||
if (v == null) {
|
||||
ctx.console.error('head: option requires an argument -- n')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
n = Number.parseInt(v, 10) || 10
|
||||
mode = 'lines'
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-n') && a.length > 2) {
|
||||
n = Number.parseInt(a.slice(2), 10) || 10
|
||||
mode = 'lines'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-c' || a === '--bytes') {
|
||||
const v = argv[i + 1]
|
||||
if (v == null) {
|
||||
ctx.console.error('head: option requires an argument -- c')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
n = Number.parseInt(v, 10) || 0
|
||||
mode = 'bytes'
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-c') && a.length > 2) {
|
||||
n = Number.parseInt(a.slice(2), 10) || 0
|
||||
mode = 'bytes'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a && /^-\d+$/.test(a)) {
|
||||
n = Number.parseInt(a.slice(1), 10) || 10
|
||||
mode = 'lines'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('head: unrecognized option: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
const files = argv.slice(start).filter((a) => a !== '--')
|
||||
const take = (s) => {
|
||||
|
||||
const files = argv.slice(i).filter((x) => x !== '--')
|
||||
|
||||
const takeString = (s) => {
|
||||
if (mode === 'bytes') {
|
||||
const u8 = ctx.b4a.from(s, 'utf8')
|
||||
const slice = bareHeadBytesSlice(u8, n)
|
||||
const out = ctx.b4a.toString(slice)
|
||||
if (out) ctx.console.log(out)
|
||||
return
|
||||
}
|
||||
const lines = s.split('\n')
|
||||
const out = lines.slice(0, n).join('\n')
|
||||
ctx.console.log(
|
||||
out + (out && !out.endsWith('\n') && lines.length > n ? '\n' : '')
|
||||
)
|
||||
}
|
||||
|
||||
const takeU8 = (u8) => {
|
||||
if (mode === 'bytes') {
|
||||
const slice = bareHeadBytesSlice(
|
||||
u8 instanceof Uint8Array ? u8 : ctx.b4a.from(u8),
|
||||
n
|
||||
)
|
||||
const out = ctx.b4a.toString(slice)
|
||||
if (out) ctx.console.log(out)
|
||||
return
|
||||
}
|
||||
takeString(ctx.b4a.toString(u8))
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
take(bareStdin(ctx))
|
||||
takeString(bareStdin(ctx))
|
||||
return
|
||||
}
|
||||
for (const f of files) {
|
||||
@@ -90,6 +201,6 @@ async function run(ctx, argv) {
|
||||
continue
|
||||
}
|
||||
if (files.length > 1) ctx.console.log('==> ' + f + ' <==')
|
||||
take(ctx.b4a.toString(buf))
|
||||
takeU8(buf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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) {
|
||||
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'
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 h =
|
||||
globalThis.process?.env?.HOSTNAME ||
|
||||
|
||||
+40
-7
@@ -60,6 +60,33 @@ 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 e = ctx.vfs.env
|
||||
const u = e.USER || e.LOGNAME || 'guest'
|
||||
@@ -67,22 +94,28 @@ async function run(ctx, argv) {
|
||||
const gid = e.GID || (e.BARE_OS_IDENTITY === 'guest' ? '65534' : '1000')
|
||||
const g = e.GROUP || u
|
||||
const rest = argv.slice(1)
|
||||
const wantG = rest.includes('-g') || rest.includes('--group')
|
||||
const wantU = rest.includes('-u') || rest.includes('--user')
|
||||
const wantN = rest.includes('-n') || rest.includes('--name')
|
||||
if (wantU && wantN) {
|
||||
const wantName = rest.includes('-n') || rest.includes('--name')
|
||||
const wantPrimaryGroup = rest.includes('-g') || rest.includes('--group')
|
||||
const wantAllGroups = rest.includes('-G') || rest.includes('--groups')
|
||||
const wantUser = rest.includes('-u') || rest.includes('--user')
|
||||
|
||||
if (wantUser && wantName) {
|
||||
ctx.console.log(u)
|
||||
return
|
||||
}
|
||||
if (wantG && wantN) {
|
||||
if (wantPrimaryGroup && wantName) {
|
||||
ctx.console.log(g)
|
||||
return
|
||||
}
|
||||
if (wantU) {
|
||||
if (wantUser) {
|
||||
ctx.console.log(uid)
|
||||
return
|
||||
}
|
||||
if (wantG) {
|
||||
if (wantPrimaryGroup) {
|
||||
ctx.console.log(gid)
|
||||
return
|
||||
}
|
||||
if (wantAllGroups) {
|
||||
ctx.console.log(gid)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,7 +60,35 @@ 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
|
||||
}
|
||||
|
||||
// Bare OS: vendored from @sscots/[email protected] (https://github.com/sscots/jqjs). ISC License. Top-level compile() and prettyPrint(); not the C jq binary (https://github.com/jqlang/jq).
|
||||
// Triage: treat bug reports as jqjs semantic gaps; prefer golden tests before expanding builtins. Large JSON is bounded by host memory and stdin string caps (see handbook §3).
|
||||
// jqjs - jq JSON query language in JavaScript
|
||||
// Copyright (C) 2018-2023 Michael Homer
|
||||
/*
|
||||
|
||||
+80
-2
@@ -60,8 +60,63 @@ 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 bareParentLogical(p) {
|
||||
const t = p.replace(/\/+$/, '') || '/'
|
||||
if (t === '/') return '/'
|
||||
const i = t.lastIndexOf('/')
|
||||
return i <= 0 ? '/' : t.slice(0, i) || '/'
|
||||
}
|
||||
|
||||
function bareJoinUnder(parent, rel) {
|
||||
if (rel.startsWith('/')) return rel
|
||||
const base = parent.replace(/\/+$/, '') || '/'
|
||||
if (base === '/') return '/' + rel.replace(/^\/+/, '')
|
||||
return base + '/' + rel.replace(/^\/+/, '')
|
||||
}
|
||||
|
||||
function bareRelativeLogical(fromDirAbs, toAbs) {
|
||||
const from = fromDirAbs.replace(/\/+$/, '').split('/').filter(Boolean)
|
||||
const to = toAbs.replace(/\/+$/, '').split('/').filter(Boolean)
|
||||
let i = 0
|
||||
while (i < from.length && i < to.length && from[i] === to[i]) i++
|
||||
const up = from.length - i
|
||||
const parts = []
|
||||
for (let k = 0; k < up; k++) parts.push('..')
|
||||
parts.push(...to.slice(i))
|
||||
return parts.length ? parts.join('/') : '.'
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let sym = false
|
||||
let force = false
|
||||
let relative = false
|
||||
const rest = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
@@ -69,6 +124,14 @@ async function run(ctx, argv) {
|
||||
sym = true
|
||||
continue
|
||||
}
|
||||
if (a === '-f' || a === '--force') {
|
||||
force = true
|
||||
continue
|
||||
}
|
||||
if (a === '-r' || a === '--relative') {
|
||||
relative = true
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
rest.push(...argv.slice(i + 1))
|
||||
break
|
||||
@@ -86,12 +149,27 @@ async function run(ctx, argv) {
|
||||
return
|
||||
}
|
||||
if (rest.length !== 2) {
|
||||
ctx.console.error('usage: ln -s TARGET LINK_NAME')
|
||||
ctx.console.error('usage: ln -s [-f] [-r] TARGET LINK_NAME')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
let target = rest[0]
|
||||
const linkName = rest[1]
|
||||
try {
|
||||
await ctx.vfs.symlink(rest[0], rest[1])
|
||||
if (relative) {
|
||||
const linkAbs = ctx.vfs.resolveLogical(linkName)
|
||||
const linkDir = bareParentLogical(linkAbs)
|
||||
const tgtAbs = ctx.vfs.resolveLogical(target)
|
||||
target = bareRelativeLogical(linkDir, tgtAbs)
|
||||
}
|
||||
if (force) {
|
||||
try {
|
||||
await ctx.vfs.unlink(linkName)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
await ctx.vfs.symlink(target, linkName)
|
||||
} catch (e) {
|
||||
ctx.console.error('ln: ' + ((e && e.message) || e))
|
||||
ctx.exitCode = 1
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 rest = argv.slice(1)
|
||||
let createNew = false
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 u = ctx.vfs.env.LOGNAME || ctx.vfs.env.USER || 'guest'
|
||||
ctx.console.log(u)
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 save = argv.includes('--save')
|
||||
const logout = ctx.applyLogout
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* GNU-style LS_COLORS string + dircolors(5) database parsing and file classification.
|
||||
* Consumed by bare-os-booter (ESM import). Prepended into /bin/ls and /bin/dircolors by bare-os-coreutils build (no import there).
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/** Plain-text manual formatter (prepended before src/man.js; no import in /bin/man). */
|
||||
|
||||
function bareManParseWidth(env) {
|
||||
|
||||
+57
-2
@@ -60,8 +60,44 @@ 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 parseModeOctal(s) {
|
||||
const t = String(s).trim()
|
||||
const n = Number.parseInt(t, 8)
|
||||
if (!Number.isFinite(n) || n < 0) return null
|
||||
return n & 0o777
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let parents = false
|
||||
/** @type {number | null} */
|
||||
let modeOpt = null
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
@@ -69,6 +105,23 @@ async function run(ctx, argv) {
|
||||
parents = true
|
||||
continue
|
||||
}
|
||||
if (a === '-m') {
|
||||
const next = argv[i + 1]
|
||||
if (next === undefined) {
|
||||
ctx.console.error('mkdir: option requires an argument -- m')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const m = parseModeOctal(next)
|
||||
if (m == null) {
|
||||
ctx.console.error('mkdir: invalid mode ' + JSON.stringify(next))
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
modeOpt = m
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
paths.push(...argv.slice(i + 1))
|
||||
break
|
||||
@@ -81,13 +134,15 @@ async function run(ctx, argv) {
|
||||
paths.push(a)
|
||||
}
|
||||
if (!paths.length) {
|
||||
ctx.console.error('usage: mkdir [-p] DIRECTORY...')
|
||||
ctx.console.error('usage: mkdir [-p] [-m MODE] DIRECTORY...')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
for (const p of paths) {
|
||||
try {
|
||||
await ctx.vfs.mkdir(p, { recursive: parents })
|
||||
const mkOpts = { recursive: parents }
|
||||
if (modeOpt != null) mkOpts.mode = modeOpt
|
||||
await ctx.vfs.mkdir(p, mkOpts)
|
||||
} catch (e) {
|
||||
ctx.console.error('mkdir: ' + p + ': ' + ((e && e.message) || e))
|
||||
ctx.exitCode = 1
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 path = argv[1]
|
||||
if (!path) {
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 wantDir = false
|
||||
/** @type {string | null} */
|
||||
|
||||
+84
-9
@@ -60,12 +60,61 @@ function barePosixBlocks(size) {
|
||||
return Math.ceil(Number(size) / 512) || 0
|
||||
}
|
||||
|
||||
async function mvCopyPath(ctx, from, to, recursive) {
|
||||
/**
|
||||
* 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 mvCopyPath(ctx, from, to, recursive, followSymlink) {
|
||||
const st = await ctx.vfs.lstat(from)
|
||||
if (!st) return false
|
||||
if (st.type === 'symlink') {
|
||||
await ctx.vfs.symlink(await ctx.vfs.readlink(from), to)
|
||||
return true
|
||||
if (!followSymlink) {
|
||||
await ctx.vfs.symlink(await ctx.vfs.readlink(from), to)
|
||||
return true
|
||||
}
|
||||
const fst = await ctx.vfs.stat(from)
|
||||
if (fst.type === 'file') {
|
||||
const buf = await ctx.vfs.readFile(from)
|
||||
if (!buf) return false
|
||||
await ctx.vfs.writeFile(to, buf)
|
||||
return true
|
||||
}
|
||||
if (fst.type === 'directory') {
|
||||
if (!recursive) return false
|
||||
await ctx.vfs.mkdir(to, { recursive: true })
|
||||
const names = await ctx.vfs.readdir(from)
|
||||
for (const n of names) {
|
||||
if (n === '.bareos_empty') continue
|
||||
const f = from.replace(/\/+$/, '') + '/' + n
|
||||
const t = to.replace(/\/+$/, '') + '/' + n
|
||||
if (!(await mvCopyPath(ctx, f, t, true, followSymlink))) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (st.type === 'file') {
|
||||
const buf = await ctx.vfs.readFile(from)
|
||||
@@ -81,7 +130,7 @@ async function mvCopyPath(ctx, from, to, recursive) {
|
||||
if (n === '.bareos_empty') continue
|
||||
const f = from.replace(/\/+$/, '') + '/' + n
|
||||
const t = to.replace(/\/+$/, '') + '/' + n
|
||||
if (!(await mvCopyPath(ctx, f, t, true))) return false
|
||||
if (!(await mvCopyPath(ctx, f, t, true, followSymlink))) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -89,9 +138,31 @@ async function mvCopyPath(ctx, from, to, recursive) {
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const paths = argv.slice(1).filter((a) => a && a !== '--')
|
||||
let followSymlink = false
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-L' || a === '--dereference') {
|
||||
followSymlink = true
|
||||
continue
|
||||
}
|
||||
if (a === '-P' || a === '--no-dereference') {
|
||||
followSymlink = false
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
paths.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('mv: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
paths.push(a)
|
||||
}
|
||||
if (paths.length < 2) {
|
||||
ctx.console.error('usage: mv SOURCE... DEST')
|
||||
ctx.console.error('usage: mv [-L|-P] SOURCE... DEST')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -117,8 +188,12 @@ async function run(ctx, argv) {
|
||||
: dest
|
||||
try {
|
||||
const st = await ctx.vfs.lstat(src)
|
||||
const singleFileToFile =
|
||||
sources.length === 1 && !destIsDir && st && st.type === 'file'
|
||||
let followReg = st && st.type === 'file'
|
||||
if (!followReg && followSymlink && st && st.type === 'symlink') {
|
||||
const fs = await ctx.vfs.stat(src).catch(() => null)
|
||||
followReg = !!(fs && fs.type === 'file')
|
||||
}
|
||||
const singleFileToFile = sources.length === 1 && !destIsDir && followReg
|
||||
if (singleFileToFile) {
|
||||
const buf = await ctx.vfs.readFile(src)
|
||||
if (!buf) throw new Error('cannot read source')
|
||||
@@ -126,7 +201,7 @@ async function run(ctx, argv) {
|
||||
await ctx.vfs.unlink(src)
|
||||
continue
|
||||
}
|
||||
if (!(await mvCopyPath(ctx, src, target, true))) {
|
||||
if (!(await mvCopyPath(ctx, src, target, true, followSymlink))) {
|
||||
throw new Error('cannot copy')
|
||||
}
|
||||
await ctx.vfs.rm(src, { recursive: true, force: true })
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/** ANSI helpers for /bin/edit (preamble; no import in src). */
|
||||
|
||||
const EDIT_ANSI_RESET = '\x1b[0m'
|
||||
|
||||
+71
-4
@@ -60,9 +60,71 @@ 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 vfs = ctx.vfs
|
||||
const files = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
let bodyType = 'a'
|
||||
let width = 6
|
||||
let sep = '\t'
|
||||
let start = 1
|
||||
const files = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-b' && argv[i + 1]) {
|
||||
const b = argv[++i]
|
||||
bodyType = b === 't' ? 't' : 'a'
|
||||
continue
|
||||
}
|
||||
if (a === '-w' && argv[i + 1]) {
|
||||
width = Number.parseInt(argv[++i], 10) || 6
|
||||
continue
|
||||
}
|
||||
if (a === '-s' && argv[i + 1]) {
|
||||
const s = argv[++i]
|
||||
sep = s === '\\t' ? '\t' : s
|
||||
continue
|
||||
}
|
||||
if (a === '-v' && argv[i + 1]) {
|
||||
start = Number.parseInt(argv[++i], 10) || 1
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
files.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('nl: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
files.push(a)
|
||||
}
|
||||
let body = ''
|
||||
if (!files.length) {
|
||||
body = bareStdin(ctx)
|
||||
@@ -80,11 +142,16 @@ async function run(ctx, argv) {
|
||||
}
|
||||
const lines = body.split('\n')
|
||||
if (lines.length && lines[lines.length - 1] === '') lines.pop()
|
||||
let n = 1
|
||||
let n = start
|
||||
for (const line of lines) {
|
||||
const blank = line.trim() === ''
|
||||
if (bodyType === 't' && blank) {
|
||||
ctx.console.log(line)
|
||||
continue
|
||||
}
|
||||
const num = String(n)
|
||||
const pad = ' '.repeat(Math.max(0, 6 - num.length)) + num
|
||||
ctx.console.log(pad + '\t' + line)
|
||||
const pad = ' '.repeat(Math.max(0, width - num.length)) + num
|
||||
ctx.console.log(pad + sep + line)
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
+68
-9
@@ -60,12 +60,63 @@ 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 hexByte(b) {
|
||||
return b.toString(16).padStart(2, '0')
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const paths = argv.slice(1).filter((a) => a && !a.startsWith('-'))
|
||||
let showAddr = true
|
||||
/** @type {'both' | 'hex' | 'char'} */
|
||||
let style = 'both'
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-An') {
|
||||
showAddr = false
|
||||
continue
|
||||
}
|
||||
if (a === '-A' && argv[i + 1] === 'n') {
|
||||
showAddr = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-x') {
|
||||
style = 'hex'
|
||||
continue
|
||||
}
|
||||
if (a === '-c') {
|
||||
style = 'char'
|
||||
continue
|
||||
}
|
||||
if (!a.startsWith('-')) paths.push(a)
|
||||
}
|
||||
let buf
|
||||
if (!paths.length) {
|
||||
buf = ctx.b4a.from(bareStdin(ctx))
|
||||
@@ -86,14 +137,22 @@ async function run(ctx, argv) {
|
||||
const asc = [...chunk]
|
||||
.map((x) => (x >= 32 && x < 127 ? String.fromCharCode(x) : '.'))
|
||||
.join('')
|
||||
ctx.console.log(
|
||||
off.toString(8).padStart(7, '0') +
|
||||
' ' +
|
||||
hex.padEnd(47, ' ') +
|
||||
' |' +
|
||||
asc +
|
||||
'|'
|
||||
)
|
||||
let line = ''
|
||||
if (showAddr) line += off.toString(8).padStart(7, '0') + ' '
|
||||
if (style === 'hex') line += hex
|
||||
else if (style === 'char')
|
||||
line +=
|
||||
[...chunk]
|
||||
.map((x) => {
|
||||
if (x === 9) return '\\t'
|
||||
if (x === 10) return '\\n'
|
||||
if (x === 13) return '\\r'
|
||||
if (x >= 32 && x < 127) return String.fromCharCode(x)
|
||||
return '\\' + x.toString(8).padStart(3, '0')
|
||||
})
|
||||
.join(' ')
|
||||
else line += hex.padEnd(47, ' ') + ' |' + asc + '|'
|
||||
ctx.console.log(line.trimEnd())
|
||||
off += 16
|
||||
}
|
||||
}
|
||||
|
||||
+71
-1
@@ -60,24 +60,94 @@ 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 = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
let posix = false
|
||||
let strict = false
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-p') {
|
||||
posix = true
|
||||
continue
|
||||
}
|
||||
if (a === '-P') {
|
||||
strict = true
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
paths.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('pathchk: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
paths.push(a)
|
||||
}
|
||||
if (!paths.length) {
|
||||
ctx.console.error('pathchk: missing operand')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
for (const p of paths) {
|
||||
if (strict && !p.length) {
|
||||
ctx.console.error('pathchk: empty path name')
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
if (!p.length) {
|
||||
ctx.console.error('pathchk: empty path name')
|
||||
continue
|
||||
}
|
||||
if (p.length > 4096) {
|
||||
ctx.console.error('pathchk: path too long')
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
if (p.includes('\0')) {
|
||||
ctx.console.error('pathchk: NUL in path')
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
if (strict && p.startsWith('-')) {
|
||||
ctx.console.error('pathchk: leading hyphen in path')
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
if (posix) {
|
||||
const base = p.split('/').pop() || p
|
||||
if (base !== '.' && base !== '..' && !/^[A-Za-z0-9._-]+$/.test(base)) {
|
||||
ctx.console.error('pathchk: non-portable file name ' + base)
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+60
-1
@@ -60,9 +60,68 @@ 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 e = ctx.vfs.env
|
||||
const names = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
const nulOut =
|
||||
argv.includes('-0') ||
|
||||
argv.includes('--null') ||
|
||||
argv.some((a) => a === '-0' || /^--null(=|$)/.test(a))
|
||||
const names = argv
|
||||
.slice(1)
|
||||
.filter((a) => a !== '-0' && a !== '--null' && !a.startsWith('--null='))
|
||||
if (nulOut) {
|
||||
if (!names.length) {
|
||||
for (const k of Object.keys(e).sort()) {
|
||||
if (!bareOsEmitRaw(ctx, k + '=' + (e[k] ?? '') + '\0')) {
|
||||
ctx.console.error(
|
||||
'printenv: -0 requires process.stdout.write or ctx.bareOsBinWrite'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for (const n of names) {
|
||||
if (e[n] != null) {
|
||||
if (!bareOsEmitRaw(ctx, String(e[n]) + '\0')) {
|
||||
ctx.console.error(
|
||||
'printenv: -0 requires process.stdout.write or ctx.bareOsBinWrite'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
} else ctx.exitCode = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!names.length) {
|
||||
for (const k of Object.keys(e).sort()) {
|
||||
ctx.console.log(k + '=' + (e[k] ?? ''))
|
||||
|
||||
@@ -60,6 +60,57 @@ 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
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
function barePrintfBackslashArg(s) {
|
||||
let o = ''
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
if (s[i] !== '\\') {
|
||||
o += s[i]
|
||||
continue
|
||||
}
|
||||
const c = s[++i]
|
||||
if (c === undefined) break
|
||||
if (c === 'n') o += '\n'
|
||||
else if (c === 't') o += '\t'
|
||||
else if (c === 'r') o += '\r'
|
||||
else if (c === '\\') o += '\\'
|
||||
else if (c >= '0' && c <= '7') {
|
||||
let oct = c
|
||||
while (i + 1 < s.length && /[0-7]/.test(s[i + 1]) && oct.length < 3)
|
||||
oct += s[++i]
|
||||
o += String.fromCharCode(Number.parseInt(oct, 8) & 0xff)
|
||||
} else o += c
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
function barePrintfFormat(fmt, args) {
|
||||
let ai = 0
|
||||
let o = ''
|
||||
@@ -86,6 +137,10 @@ function barePrintfFormat(fmt, args) {
|
||||
else if (spec === 'o') o += (Math.trunc(Number(arg)) >>> 0).toString(8)
|
||||
else if (spec === 'c') o += String.fromCharCode(Number(arg) || 0)
|
||||
else if (spec === 'f') o += String(Number(arg))
|
||||
else if (spec === 'e') o += Number(arg).toExponential(6)
|
||||
else if (spec === 'E')
|
||||
o += Number(arg).toExponential(6).replace(/e/g, 'E')
|
||||
else if (spec === 'b') o += barePrintfBackslashArg(String(arg))
|
||||
else o += String(arg)
|
||||
i = j
|
||||
}
|
||||
|
||||
@@ -60,6 +60,51 @@ 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 (const a of argv.slice(1)) {
|
||||
if (
|
||||
a === '-P' ||
|
||||
a === '--physical' ||
|
||||
a === '-L' ||
|
||||
a === '--logical'
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('pwd: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
ctx.console.error('pwd: too many arguments')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
ctx.console.log(ctx.vfs.getcwd())
|
||||
}
|
||||
|
||||
+83
-9
@@ -60,23 +60,97 @@ 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 bareParentLogical(p) {
|
||||
const t = p.replace(/\/+$/, '') || '/'
|
||||
if (t === '/') return '/'
|
||||
const i = t.lastIndexOf('/')
|
||||
return i <= 0 ? '/' : t.slice(0, i) || '/'
|
||||
}
|
||||
|
||||
function bareJoinUnder(parent, rel) {
|
||||
if (rel.startsWith('/')) return rel
|
||||
const base = parent.replace(/\/+$/, '') || '/'
|
||||
if (base === '/') return '/' + rel.replace(/^\/+/, '')
|
||||
return base + '/' + rel.replace(/^\/+/, '')
|
||||
}
|
||||
|
||||
const READLINK_MAX_SYMLINKS = 48
|
||||
|
||||
async function bareReadlinkCanon(ctx, start) {
|
||||
let cur = ctx.vfs.resolveLogical(start)
|
||||
for (let d = 0; d < READLINK_MAX_SYMLINKS; d++) {
|
||||
const st = await ctx.vfs.lstat(cur)
|
||||
if (!st) return null
|
||||
if (st.type !== 'symlink') return cur
|
||||
const raw = await ctx.vfs.readlink(cur)
|
||||
const par = bareParentLogical(cur)
|
||||
cur = ctx.vfs.resolveLogical(bareJoinUnder(par, raw))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let noNewline = false
|
||||
let canonical = false
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
if (argv[i] === '-n') noNewline = true
|
||||
else if (argv[i] !== '--') paths.push(argv[i])
|
||||
const a = argv[i]
|
||||
if (a === '-n') noNewline = true
|
||||
else if (a === '-f' || a === '--canonicalize') canonical = true
|
||||
else if (a !== '--') paths.push(a)
|
||||
}
|
||||
if (!paths.length) {
|
||||
ctx.console.error('usage: readlink [-n] FILE')
|
||||
ctx.console.error('usage: readlink [-fn] FILE...')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
try {
|
||||
const t = await ctx.vfs.readlink(paths[0])
|
||||
ctx.console.log(noNewline ? t : t)
|
||||
} catch (e) {
|
||||
ctx.console.error('readlink: ' + ((e && e.message) || e))
|
||||
ctx.exitCode = 1
|
||||
const w = globalThis.process?.stdout?.write
|
||||
for (let pi = 0; pi < paths.length; pi++) {
|
||||
const p = paths[pi]
|
||||
try {
|
||||
const out = canonical ? await bareReadlinkCanon(ctx, p) : await ctx.vfs.readlink(p)
|
||||
if (canonical && out == null) {
|
||||
ctx.console.error('readlink: ' + p + ': Invalid argument')
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
const line = String(out)
|
||||
if (noNewline && typeof w === 'function') {
|
||||
w.call(globalThis.process.stdout, line)
|
||||
if (pi < paths.length - 1) w.call(globalThis.process.stdout, '\n')
|
||||
} else {
|
||||
ctx.console.log(line)
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.console.error('readlink: ' + ((e && e.message) || e))
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* rm — remove files or directories.
|
||||
* Flags: -r -R --recursive, -f --force, -- ; bundled e.g. -rf
|
||||
|
||||
+72
-3
@@ -60,16 +60,85 @@ 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 bareParentLogical(p) {
|
||||
const t = p.replace(/\/+$/, '') || '/'
|
||||
if (t === '/') return '/'
|
||||
const i = t.lastIndexOf('/')
|
||||
return i <= 0 ? '/' : t.slice(0, i) || '/'
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const paths = argv.slice(1).filter((a) => a && a !== '--')
|
||||
let parents = false
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-p' || a === '--parents') {
|
||||
parents = true
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
paths.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('rmdir: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
paths.push(a)
|
||||
}
|
||||
if (!paths.length) {
|
||||
ctx.console.error('usage: rmdir DIRECTORY...')
|
||||
ctx.console.error('usage: rmdir [-p] DIRECTORY...')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
for (const p of paths) {
|
||||
try {
|
||||
await ctx.vfs.rmdir(p)
|
||||
if (!parents) {
|
||||
await ctx.vfs.rmdir(p)
|
||||
continue
|
||||
}
|
||||
let cur = ctx.vfs.resolveLogical(p).replace(/\/+$/, '') || '/'
|
||||
/** @type {string[]} */
|
||||
const stack = []
|
||||
while (cur !== '/' && cur !== '') {
|
||||
stack.push(cur)
|
||||
cur = bareParentLogical(cur)
|
||||
}
|
||||
for (const d of stack) {
|
||||
try {
|
||||
await ctx.vfs.rmdir(d)
|
||||
} catch {
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.console.error('rmdir: ' + p + ': ' + ((e && e.message) || e))
|
||||
ctx.exitCode = 1
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 save = ctx.saveVault
|
||||
if (typeof save !== 'function') {
|
||||
|
||||
+33
-2
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* POSIX-oriented sed engine for Bare OS /bin/sed (no import; concatenated before src/sed.js).
|
||||
* Covers: -n -e -f -E, addresses (#,$,/re/,n,m,n~s), s///[ngp0-9], y///, d D p P n N,
|
||||
@@ -841,8 +868,12 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
scripts.push(a)
|
||||
files.push(...argv.slice(i + 1))
|
||||
if (!scripts.length) {
|
||||
scripts.push(a)
|
||||
files.push(...argv.slice(i + 1))
|
||||
} else {
|
||||
files.push(...argv.slice(i))
|
||||
}
|
||||
break
|
||||
}
|
||||
if (!scripts.length) {
|
||||
|
||||
+63
-3
@@ -60,8 +60,59 @@ 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.startsWith('-'))
|
||||
let sep = '\n'
|
||||
let equalWidth = false
|
||||
const args = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-s' && argv[i + 1]) {
|
||||
const s = argv[++i]
|
||||
sep = s === '\\n' ? '\n' : s
|
||||
continue
|
||||
}
|
||||
if (a === '-w') {
|
||||
equalWidth = true
|
||||
continue
|
||||
}
|
||||
if (a === '--') {
|
||||
args.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('seq: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
args.push(a)
|
||||
}
|
||||
let a = 1
|
||||
let step = 1
|
||||
let b = 1
|
||||
@@ -80,9 +131,18 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
/** @type {string[]} */
|
||||
const out = []
|
||||
if (step > 0) {
|
||||
for (let x = a; x <= b; x += step) ctx.console.log(String(x))
|
||||
for (let x = a; x <= b; x += step) out.push(String(x))
|
||||
} else {
|
||||
for (let x = a; x >= b; x += step) ctx.console.log(String(x))
|
||||
for (let x = a; x >= b; x += step) out.push(String(x))
|
||||
}
|
||||
const w = equalWidth && out.length ? Math.max(...out.map((s) => s.length)) : 0
|
||||
const fmt = (s) => (w ? s.padStart(w, '0') : s)
|
||||
if (sep === '\n') {
|
||||
for (const s of out) ctx.console.log(fmt(s))
|
||||
} else {
|
||||
ctx.console.log(out.map(fmt).join(sep))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 sec = parseFloat(argv[1] || '0')
|
||||
if (Number.isNaN(sec) || sec < 0) {
|
||||
|
||||
+222
-2
@@ -60,9 +60,179 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} line
|
||||
* @param {string | null} delim
|
||||
* @param {number} start1
|
||||
* @param {number | null} end1
|
||||
*/
|
||||
function sortKeySlice(line, delim, start1, end1) {
|
||||
/** @type {string[]} */
|
||||
let fields
|
||||
if (delim != null) {
|
||||
if (delim === '') fields = line.split('')
|
||||
else fields = line.split(delim)
|
||||
} else {
|
||||
const t = line.trim()
|
||||
fields = t.length ? t.split(/\s+/) : []
|
||||
}
|
||||
const i0 = Math.max(0, start1 - 1)
|
||||
const i1 = end1 != null ? end1 : fields.length
|
||||
const slice = fields.slice(i0, i1)
|
||||
if (delim != null) return slice.join(delim)
|
||||
return slice.join(' ')
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const vfs = ctx.vfs
|
||||
const files = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
let numeric = false
|
||||
let reverse = false
|
||||
let uniq = false
|
||||
let fold = false
|
||||
/** @type {string | undefined} */
|
||||
let delim
|
||||
/** @type {number | null} */
|
||||
let keyStart = null
|
||||
/** @type {number | null} */
|
||||
let keyEnd = null
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-n' || a === '--numeric-sort' || a === '-g') {
|
||||
numeric = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-r' || a === '--reverse') {
|
||||
reverse = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-u' || a === '--unique') {
|
||||
uniq = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-f' || a === '--ignore-case') {
|
||||
fold = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-t' || a === '--field-separator') {
|
||||
const d = argv[++i]
|
||||
if (d === undefined) {
|
||||
ctx.console.error('sort: option requires an argument -- t')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
delim = d === '\\t' ? '\t' : d
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-t') && a.length > 2) {
|
||||
delim = a.slice(2) === 't' ? '\t' : a.slice(2)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-k' || a === '--key') {
|
||||
const spec = argv[++i]
|
||||
if (spec === undefined) {
|
||||
ctx.console.error('sort: option requires an argument -- k')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const parts = String(spec).split(',')
|
||||
keyStart = Number.parseInt(parts[0], 10)
|
||||
keyEnd =
|
||||
parts[1] !== undefined && parts[1] !== ''
|
||||
? Number.parseInt(parts[1], 10)
|
||||
: null
|
||||
if (!Number.isFinite(keyStart) || keyStart < 1) {
|
||||
ctx.console.error('sort: invalid key specification')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
if (
|
||||
keyEnd != null &&
|
||||
(!Number.isFinite(keyEnd) || keyEnd < keyStart)
|
||||
) {
|
||||
ctx.console.error('sort: invalid key specification')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (/^-k\d/.test(a)) {
|
||||
const spec = a.slice(2)
|
||||
const parts = String(spec).split(',')
|
||||
keyStart = Number.parseInt(parts[0], 10)
|
||||
keyEnd =
|
||||
parts[1] !== undefined && parts[1] !== ''
|
||||
? Number.parseInt(parts[1], 10)
|
||||
: null
|
||||
if (!Number.isFinite(keyStart) || keyStart < 1) {
|
||||
ctx.console.error('sort: invalid key specification')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-') && a.length > 1) {
|
||||
let j = 1
|
||||
let ok = true
|
||||
while (j < a.length) {
|
||||
const c = a[j++]
|
||||
if (c === 'n') numeric = true
|
||||
else if (c === 'r') reverse = true
|
||||
else if (c === 'u') uniq = true
|
||||
else if (c === 'f') fold = true
|
||||
else {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
const files = argv.slice(i).filter((x) => x !== '--')
|
||||
/** @type {string[]} */
|
||||
let lines = []
|
||||
if (!files.length) {
|
||||
@@ -71,6 +241,13 @@ async function run(ctx, argv) {
|
||||
if (lines.length && lines[lines.length - 1] === '') lines.pop()
|
||||
} else {
|
||||
for (const f of files) {
|
||||
if (f === '-') {
|
||||
const s = bareStdin(ctx)
|
||||
const part = s.length ? s.split('\n') : []
|
||||
if (part.length && part[part.length - 1] === '') part.pop()
|
||||
lines.push(...part)
|
||||
continue
|
||||
}
|
||||
const buf = await vfs.readFile(f)
|
||||
if (!buf) {
|
||||
ctx.console.error('sort: ' + f + ': No such file')
|
||||
@@ -82,6 +259,49 @@ async function run(ctx, argv) {
|
||||
lines.push(...part)
|
||||
}
|
||||
}
|
||||
lines.sort((x, y) => (x < y ? -1 : x > y ? 1 : 0))
|
||||
|
||||
const dForKey = delim === undefined ? null : delim
|
||||
|
||||
/** @param {string} s */
|
||||
function sortKey(s) {
|
||||
const keyText =
|
||||
keyStart != null
|
||||
? sortKeySlice(s, dForKey, keyStart, keyEnd)
|
||||
: s
|
||||
if (numeric) {
|
||||
const m = String(keyText).match(
|
||||
/^\s*(-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)/
|
||||
)
|
||||
if (m) return { n: Number(m[1]), raw: keyText }
|
||||
return { n: Number.POSITIVE_INFINITY, raw: keyText }
|
||||
}
|
||||
if (fold) return { n: 0, raw: keyText.toLowerCase() }
|
||||
return { n: 0, raw: keyText }
|
||||
}
|
||||
|
||||
lines.sort((x, y) => {
|
||||
const kx = sortKey(x)
|
||||
const ky = sortKey(y)
|
||||
if (numeric) {
|
||||
if (kx.n !== ky.n) {
|
||||
const ord = kx.n < ky.n ? -1 : 1
|
||||
return reverse ? -ord : ord
|
||||
}
|
||||
}
|
||||
const cmp =
|
||||
kx.raw < ky.raw ? -1 : kx.raw > ky.raw ? 1 : x < y ? -1 : x > y ? 1 : 0
|
||||
return reverse ? -cmp : cmp
|
||||
})
|
||||
|
||||
if (uniq) {
|
||||
const out = []
|
||||
let prev = null
|
||||
for (const ln of lines) {
|
||||
if (ln !== prev) out.push(ln)
|
||||
prev = ln
|
||||
}
|
||||
lines = out
|
||||
}
|
||||
|
||||
if (lines.length) ctx.console.log(lines.join('\n'))
|
||||
}
|
||||
|
||||
+92
-2
@@ -60,10 +60,96 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} st
|
||||
* @param {string} displayPath
|
||||
* @param {string} fmt
|
||||
*/
|
||||
function statApplyFormat(st, displayPath, fmt) {
|
||||
let out = ''
|
||||
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
|
||||
else if (c === 'N') out += displayPath
|
||||
else if (c === 's') out += String(st.size ?? 0)
|
||||
else if (c === 'Y')
|
||||
out += String(
|
||||
Math.floor(
|
||||
(typeof st.mtimeMs === 'number' ? st.mtimeMs : Date.now()) / 1000
|
||||
)
|
||||
)
|
||||
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 out += '%' + c
|
||||
} else {
|
||||
out += fmt[i]
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const paths = argv.slice(1).filter((a) => a && !a.startsWith('-'))
|
||||
/** @type {string | null} */
|
||||
let formatStr = null
|
||||
/** @type {string[]} */
|
||||
const paths = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '-c' && argv[i + 1]) {
|
||||
formatStr = argv[++i]
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('--format=')) {
|
||||
formatStr = a.slice('--format='.length)
|
||||
continue
|
||||
}
|
||||
if (a === '--format' && argv[i + 1]) {
|
||||
formatStr = argv[++i]
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('stat: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
paths.push(a)
|
||||
}
|
||||
if (!paths.length) {
|
||||
ctx.console.error('usage: stat FILE...')
|
||||
ctx.console.error('usage: stat [-c FORMAT | --format=FORMAT] FILE...')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -75,6 +161,10 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
continue
|
||||
}
|
||||
if (formatStr != null) {
|
||||
ctx.console.log(statApplyFormat(st, p, formatStr))
|
||||
continue
|
||||
}
|
||||
const modeStr = bareFormatModeString(st.mode, st.type)
|
||||
ctx.console.log(
|
||||
'File: ' +
|
||||
|
||||
+373
-15
@@ -60,25 +60,338 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* tail — last lines/bytes of files; -f/--follow with vfs.watch or poll.
|
||||
* Test/limit: BARE_OS_TAIL_F_POLL_MS, BARE_OS_TAIL_F_MAX_ROUNDS (empty = unlimited).
|
||||
*/
|
||||
|
||||
function bareTailSleep(ms) {
|
||||
return new Promise((r) => setTimeout(r, ms))
|
||||
}
|
||||
|
||||
function bareTailEnv(ctx) {
|
||||
return (ctx.vfs && ctx.vfs.env) || ctx.env || {}
|
||||
}
|
||||
|
||||
function bareTailPollMs(env) {
|
||||
const n = Number.parseInt(String(env.BARE_OS_TAIL_F_POLL_MS || '1000'), 10)
|
||||
return Number.isFinite(n) && n >= 0 ? n : 1000
|
||||
}
|
||||
|
||||
function bareTailMaxRounds(env) {
|
||||
const raw = env.BARE_OS_TAIL_F_MAX_ROUNDS
|
||||
if (raw == null || raw === '') return Number.POSITIVE_INFINITY
|
||||
const n = Number.parseInt(String(raw), 10)
|
||||
return Number.isFinite(n) && n >= 0 ? n : Number.POSITIVE_INFINITY
|
||||
}
|
||||
|
||||
function bareTailUseWatch(ctx, vfs) {
|
||||
if (typeof vfs.watch !== 'function') return false
|
||||
const caps = ctx.bareOsRuntimeCaps
|
||||
if (caps && caps.features && caps.features.vfsWatch === false) return false
|
||||
return true
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
function bareTailSplitLines(s) {
|
||||
const lines = s.split('\n')
|
||||
if (s.endsWith('\n') && lines.length && lines[lines.length - 1] === '')
|
||||
lines.pop()
|
||||
return lines
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} u8
|
||||
* @param {'end' | 'start'} which
|
||||
* @param {number} n count or 1-based start (+ mode)
|
||||
*/
|
||||
function bareTailBytesSlice(u8, which, n, fromPlus) {
|
||||
if (!u8 || !u8.byteLength) return new Uint8Array(0)
|
||||
if (which === 'end') {
|
||||
if (n <= 0) return new Uint8Array(0)
|
||||
const start = Math.max(0, u8.byteLength - n)
|
||||
return u8.subarray(start)
|
||||
}
|
||||
/* from start (+): GNU tail -c +K starts at byte K (1-based) */
|
||||
const idx = fromPlus ? Math.max(0, n - 1) : 0
|
||||
return idx >= u8.byteLength ? new Uint8Array(0) : u8.subarray(idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {'end' | 'start'} which
|
||||
* @param {number} n
|
||||
* @param {boolean} fromPlus
|
||||
* @param {*} b4a
|
||||
*/
|
||||
function bareTailLinesText(s, which, n, fromPlus, b4a) {
|
||||
const lines = bareTailSplitLines(s)
|
||||
if (which === 'end') {
|
||||
if (n <= 0) return ''
|
||||
const slice = lines.length <= n ? lines : lines.slice(-n)
|
||||
return slice.join('\n')
|
||||
}
|
||||
/* +N: from line N (1-based) */
|
||||
const start = fromPlus ? Math.max(0, n - 1) : 0
|
||||
const slice = lines.slice(start)
|
||||
return slice.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit follow chunks as full lines via console.log (tests capture log).
|
||||
* @param {string} chunk
|
||||
* @param {{ partial: string }} state
|
||||
*/
|
||||
function bareTailFollowEmit(ctx, chunk, state) {
|
||||
const combined = state.partial + chunk
|
||||
const lines = combined.split('\n')
|
||||
state.partial = lines.pop() || ''
|
||||
for (const ln of lines) ctx.console.log(ln)
|
||||
}
|
||||
|
||||
function bareTailFollowFlush(ctx, state) {
|
||||
if (state.partial !== '') {
|
||||
ctx.console.log(state.partial)
|
||||
state.partial = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} ctx
|
||||
* @param {string} path
|
||||
* @param {*} vfs
|
||||
* @param {number} startOffset byte offset after initial read (next byte to emit)
|
||||
*/
|
||||
async function bareTailFollow(ctx, path, vfs, startOffset) {
|
||||
const env = bareTailEnv(ctx)
|
||||
const pollMs = bareTailPollMs(env)
|
||||
const maxRounds = bareTailMaxRounds(env)
|
||||
const state = { partial: '' }
|
||||
let offset = startOffset
|
||||
let rounds = 0
|
||||
|
||||
const pumpOnce = async () => {
|
||||
const buf = await vfs.readFile(path)
|
||||
if (!buf) {
|
||||
offset = 0
|
||||
return
|
||||
}
|
||||
const u8 = buf instanceof Uint8Array ? buf : ctx.b4a.from(buf)
|
||||
if (u8.byteLength < offset) {
|
||||
offset = 0
|
||||
}
|
||||
if (u8.byteLength > offset) {
|
||||
const piece = u8.subarray(offset)
|
||||
offset = u8.byteLength
|
||||
bareTailFollowEmit(ctx, ctx.b4a.toString(piece), state)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
bareTailUseWatch(ctx, vfs) &&
|
||||
maxRounds === Number.POSITIVE_INFINITY
|
||||
) {
|
||||
let handle = null
|
||||
try {
|
||||
handle = await vfs.watch(path)
|
||||
} catch {
|
||||
handle = null
|
||||
}
|
||||
if (handle && handle.watcher) {
|
||||
try {
|
||||
for await (const _ of handle.watcher) {
|
||||
await pumpOnce()
|
||||
}
|
||||
} finally {
|
||||
if (typeof handle.destroy === 'function') handle.destroy()
|
||||
}
|
||||
bareTailFollowFlush(ctx, state)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
while (rounds < maxRounds) {
|
||||
await bareTailSleep(pollMs)
|
||||
await pumpOnce()
|
||||
rounds++
|
||||
}
|
||||
bareTailFollowFlush(ctx, state)
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const vfs = ctx.vfs
|
||||
let n = 10
|
||||
let start = 1
|
||||
if (argv[1] === '-n' && argv[2]) {
|
||||
n = parseInt(argv[2], 10) || 10
|
||||
start = 3
|
||||
let follow = false
|
||||
/** @type {'lines' | 'bytes'} */
|
||||
let mode = 'lines'
|
||||
let count = 10
|
||||
let fromPlus = false
|
||||
let i = 1
|
||||
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-f' || a === '--follow') {
|
||||
follow = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-F') {
|
||||
follow = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-n' || a === '--lines') {
|
||||
const v = argv[i + 1]
|
||||
if (v == null) {
|
||||
ctx.console.error('tail: option requires an argument -- n')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
i++
|
||||
mode = 'lines'
|
||||
if (v.startsWith('+')) {
|
||||
fromPlus = true
|
||||
count = Number.parseInt(v.slice(1), 10) || 1
|
||||
} else {
|
||||
fromPlus = false
|
||||
count = Number.parseInt(v, 10) || 10
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-n') && a.length > 2) {
|
||||
const v = a.slice(2)
|
||||
mode = 'lines'
|
||||
if (v.startsWith('+')) {
|
||||
fromPlus = true
|
||||
count = Number.parseInt(v.slice(1), 10) || 1
|
||||
} else {
|
||||
fromPlus = false
|
||||
count = Number.parseInt(v, 10) || 10
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-c' || a === '--bytes') {
|
||||
const v = argv[i + 1]
|
||||
if (v == null) {
|
||||
ctx.console.error('tail: option requires an argument -- c')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
i++
|
||||
mode = 'bytes'
|
||||
if (v.startsWith('+')) {
|
||||
fromPlus = true
|
||||
count = Number.parseInt(v.slice(1), 10) || 1
|
||||
} else {
|
||||
fromPlus = false
|
||||
count = Number.parseInt(v, 10) || 0
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-c') && a.length > 2) {
|
||||
const v = a.slice(2)
|
||||
mode = 'bytes'
|
||||
if (v.startsWith('+')) {
|
||||
fromPlus = true
|
||||
count = Number.parseInt(v.slice(1), 10) || 1
|
||||
} else {
|
||||
fromPlus = false
|
||||
count = Number.parseInt(v, 10) || 0
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-') && /^-\d+$/.test(a)) {
|
||||
mode = 'lines'
|
||||
fromPlus = false
|
||||
count = Number.parseInt(a.slice(1), 10) || 10
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('tail: unrecognized option: ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
const files = argv.slice(start).filter((a) => a !== '--')
|
||||
const take = (s) => {
|
||||
let lines = s.split('\n')
|
||||
if (s.endsWith('\n') && lines[lines.length - 1] === '') lines.pop()
|
||||
const slice = lines.length <= n ? lines : lines.slice(-n)
|
||||
ctx.console.log(slice.join('\n'))
|
||||
}
|
||||
if (!files.length) {
|
||||
take(bareStdin(ctx))
|
||||
|
||||
const files = argv.slice(i).filter((x) => x !== '--')
|
||||
|
||||
if (follow && !files.length) {
|
||||
ctx.console.error(
|
||||
'tail: --follow is not supported for stdin on Bare OS (stdin is a captured string)'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
if (follow && files.length > 1) {
|
||||
ctx.console.error(
|
||||
'tail: following multiple files is not supported (use one path)'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
const s = bareStdin(ctx)
|
||||
if (mode === 'bytes') {
|
||||
const u8 = ctx.b4a.from(s, 'utf8')
|
||||
const slice = bareTailBytesSlice(
|
||||
u8,
|
||||
fromPlus ? 'start' : 'end',
|
||||
count,
|
||||
fromPlus
|
||||
)
|
||||
const out = ctx.b4a.toString(slice)
|
||||
if (out) ctx.console.log(out)
|
||||
} else {
|
||||
const out = bareTailLinesText(
|
||||
s,
|
||||
fromPlus ? 'start' : 'end',
|
||||
count,
|
||||
fromPlus,
|
||||
ctx.b4a
|
||||
)
|
||||
if (out) ctx.console.log(out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for (const f of files) {
|
||||
const buf = await vfs.readFile(f)
|
||||
if (!buf) {
|
||||
@@ -86,6 +399,51 @@ async function run(ctx, argv) {
|
||||
continue
|
||||
}
|
||||
if (files.length > 1) ctx.console.log('==> ' + f + ' <==')
|
||||
take(ctx.b4a.toString(buf))
|
||||
const u8 = buf instanceof Uint8Array ? buf : ctx.b4a.from(buf)
|
||||
const text = ctx.b4a.toString(u8)
|
||||
if (!follow) {
|
||||
if (mode === 'bytes') {
|
||||
const slice = bareTailBytesSlice(
|
||||
u8,
|
||||
fromPlus ? 'start' : 'end',
|
||||
count,
|
||||
fromPlus
|
||||
)
|
||||
const out = ctx.b4a.toString(slice)
|
||||
if (out) ctx.console.log(out)
|
||||
} else {
|
||||
const out = bareTailLinesText(
|
||||
text,
|
||||
fromPlus ? 'start' : 'end',
|
||||
count,
|
||||
fromPlus,
|
||||
ctx.b4a
|
||||
)
|
||||
if (out) ctx.console.log(out)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (mode === 'bytes') {
|
||||
const slice = bareTailBytesSlice(
|
||||
u8,
|
||||
fromPlus ? 'start' : 'end',
|
||||
count,
|
||||
fromPlus
|
||||
)
|
||||
const out = ctx.b4a.toString(slice)
|
||||
if (out) ctx.console.log(out)
|
||||
await bareTailFollow(ctx, f, vfs, u8.byteLength)
|
||||
} else {
|
||||
const out = bareTailLinesText(
|
||||
text,
|
||||
fromPlus ? 'start' : 'end',
|
||||
count,
|
||||
fromPlus,
|
||||
ctx.b4a
|
||||
)
|
||||
if (out) ctx.console.log(out)
|
||||
const startOffset = u8.byteLength
|
||||
await bareTailFollow(ctx, f, vfs, startOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-3
@@ -60,12 +60,42 @@ 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 append = false
|
||||
let unbuf = false
|
||||
const files = []
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
if (argv[i] === '-a' || argv[i] === '--append') append = true
|
||||
else if (argv[i] !== '--') files.push(argv[i])
|
||||
const a = argv[i]
|
||||
if (a === '-a' || a === '--append') append = true
|
||||
else if (a === '-u') unbuf = true
|
||||
else if (a !== '--') files.push(a)
|
||||
}
|
||||
const text = bareStdin(ctx)
|
||||
const data = ctx.b4a.from(text)
|
||||
@@ -83,5 +113,11 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
ctx.console.log(text.replace(/\n$/, ''))
|
||||
const out = text.endsWith('\n') ? text : text + '\n'
|
||||
const pw = globalThis.process?.stdout?.write
|
||||
if (unbuf && typeof pw === 'function') {
|
||||
pw.call(globalThis.process.stdout, out)
|
||||
} else {
|
||||
ctx.console.log(text.replace(/\n$/, ''))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 evalTest(ctx, args) {
|
||||
if (!args.length) return false
|
||||
if (args[0] === '!') {
|
||||
@@ -68,6 +95,27 @@ async function evalTest(ctx, args) {
|
||||
}
|
||||
if (args.length === 3) {
|
||||
const [a, op, b] = args
|
||||
if (op === '-eq' || op === '-ne' || op === '-lt' || op === '-le' || op === '-gt' || op === '-ge') {
|
||||
const la = Number.parseInt(String(a), 10)
|
||||
const lb = Number.parseInt(String(b), 10)
|
||||
if (!Number.isFinite(la) || !Number.isFinite(lb)) return false
|
||||
switch (op) {
|
||||
case '-eq':
|
||||
return la === lb
|
||||
case '-ne':
|
||||
return la !== lb
|
||||
case '-lt':
|
||||
return la < lb
|
||||
case '-le':
|
||||
return la <= lb
|
||||
case '-gt':
|
||||
return la > lb
|
||||
case '-ge':
|
||||
return la >= lb
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (op === '=') return a === b
|
||||
if (op === '!=') return a !== b
|
||||
return false
|
||||
@@ -75,6 +123,10 @@ async function evalTest(ctx, args) {
|
||||
if (args.length === 2) {
|
||||
const op = args[0]
|
||||
const p = args[1]
|
||||
if (op === '-h' || op === '-L') {
|
||||
const st = await ctx.vfs.lstat(p)
|
||||
return st != null && st.type === 'symlink'
|
||||
}
|
||||
const st = await ctx.vfs.stat(p)
|
||||
if (op === '-e' || op === '-a') return st != null
|
||||
if (op === '-f') return st != null && st.type === 'file'
|
||||
|
||||
@@ -60,6 +60,33 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme switcher: list presets, show current, set preset (updates ~/.barerc), re-apply.
|
||||
* Uses ctx.bareOsListThemes / ctx.bareOsApplyTheme when provided by the booter.
|
||||
|
||||
+44
-3
@@ -60,10 +60,43 @@ 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 cmd = argv.slice(1)
|
||||
let portable = false
|
||||
let i = 1
|
||||
if (argv[1] === '-p') {
|
||||
portable = true
|
||||
i = 2
|
||||
}
|
||||
const cmd = argv.slice(i)
|
||||
if (!cmd.length) {
|
||||
ctx.console.error('usage: time COMMAND [ARG...]')
|
||||
ctx.console.error('usage: time [-p] COMMAND [ARG...]')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -76,6 +109,14 @@ async function run(ctx, argv) {
|
||||
}
|
||||
} finally {
|
||||
const ms = Date.now() - t0
|
||||
ctx.console.error('real\t' + (ms / 1000).toFixed(3) + 's')
|
||||
if (portable) {
|
||||
ctx.console.error(
|
||||
'real ' +
|
||||
(ms / 1000).toFixed(2) +
|
||||
'\nuser 0.00\nsys 0.00'
|
||||
)
|
||||
} else {
|
||||
ctx.console.error('real\t' + (ms / 1000).toFixed(3) + 's')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+175
-3
@@ -60,17 +60,189 @@ 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 parseTouchDate(s) {
|
||||
const t = String(s || '').trim()
|
||||
if (t.startsWith('@')) {
|
||||
const sec = Number(t.slice(1))
|
||||
if (Number.isFinite(sec)) return Math.round(sec * 1000)
|
||||
return null
|
||||
}
|
||||
const ms = Date.parse(t)
|
||||
return Number.isFinite(ms) ? ms : null
|
||||
}
|
||||
|
||||
function parseTouchArgv(argv) {
|
||||
let onlyA = false
|
||||
let onlyM = false
|
||||
/** @type {{ kind: 'd', s: string } | { kind: 'r', path: string } | null} */
|
||||
let timeSpec = null
|
||||
const files = []
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
files.push(...argv.slice(i + 1))
|
||||
return { onlyA, onlyM, timeSpec, files }
|
||||
}
|
||||
if (a === '-a') {
|
||||
onlyA = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-m') {
|
||||
onlyM = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-d' || a === '--date') {
|
||||
i++
|
||||
if (i >= argv.length) {
|
||||
return { err: 'touch: option requires an argument -- ' + a.slice(1) }
|
||||
}
|
||||
timeSpec = { kind: 'd', s: argv[i] }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-r' || a === '--reference') {
|
||||
i++
|
||||
if (i >= argv.length) {
|
||||
return { err: 'touch: option requires an argument -- reference' }
|
||||
}
|
||||
timeSpec = { kind: 'r', path: argv[i] }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
return { err: 'touch: unsupported option ' + a }
|
||||
}
|
||||
files.push(a)
|
||||
i++
|
||||
}
|
||||
return { onlyA, onlyM, timeSpec, files }
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const files = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
const p = parseTouchArgv(argv)
|
||||
if (p.err) {
|
||||
ctx.console.error(p.err)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const { onlyA, onlyM, timeSpec, files } = p
|
||||
if (!files.length) {
|
||||
ctx.console.error('touch: missing file operand')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
/** @type {number | null} */
|
||||
let T = null
|
||||
if (timeSpec) {
|
||||
if (timeSpec.kind === 'd') {
|
||||
const ms = parseTouchDate(timeSpec.s)
|
||||
if (ms == null) {
|
||||
ctx.console.error('touch: invalid date ' + JSON.stringify(timeSpec.s))
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
T = ms
|
||||
} else {
|
||||
try {
|
||||
const st = await ctx.vfs.lstat(timeSpec.path)
|
||||
if (!st) {
|
||||
ctx.console.error(
|
||||
'touch: failed to get attributes of ' +
|
||||
timeSpec.path +
|
||||
': No such file'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
T =
|
||||
typeof st.mtimeMs === 'number' && Number.isFinite(st.mtimeMs)
|
||||
? st.mtimeMs
|
||||
: null
|
||||
} catch (e) {
|
||||
ctx.console.error(
|
||||
'touch: ' + timeSpec.path + ': ' + ((e && e.message) || String(e))
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if (T == null) T = Date.now()
|
||||
let changeMtime = true
|
||||
let changeCtime = true
|
||||
if (onlyA && !onlyM) {
|
||||
changeMtime = false
|
||||
changeCtime = true
|
||||
} else if (onlyM && !onlyA) {
|
||||
changeMtime = true
|
||||
changeCtime = false
|
||||
}
|
||||
|
||||
const vfs = ctx.vfs
|
||||
for (const f of files) {
|
||||
try {
|
||||
const existing = await ctx.vfs.readFile(f)
|
||||
await ctx.vfs.writeFile(f, existing || ctx.b4a.from(''))
|
||||
const existingBuf = await vfs.readFile(f)
|
||||
let st = null
|
||||
try {
|
||||
st = await vfs.lstat(f)
|
||||
} catch {
|
||||
st = null
|
||||
}
|
||||
const buf = existingBuf || ctx.b4a.from('')
|
||||
let newM
|
||||
let newC
|
||||
if (!st) {
|
||||
newM = T
|
||||
newC = T
|
||||
} else {
|
||||
const pm =
|
||||
typeof st.mtimeMs === 'number' && Number.isFinite(st.mtimeMs)
|
||||
? st.mtimeMs
|
||||
: T
|
||||
const pc =
|
||||
typeof st.ctimeMs === 'number' && Number.isFinite(st.ctimeMs)
|
||||
? st.ctimeMs
|
||||
: T
|
||||
newM = changeMtime ? T : pm
|
||||
newC = changeCtime ? T : pc
|
||||
}
|
||||
await vfs.writeFile(f, buf, {
|
||||
bumpMtime: false,
|
||||
touchCtime: false,
|
||||
mtimeMs: newM,
|
||||
ctimeMs: newC
|
||||
})
|
||||
} catch (e) {
|
||||
ctx.console.error('touch: ' + f + ': ' + (e.message || e))
|
||||
ctx.exitCode = 1
|
||||
|
||||
+54
-3
@@ -60,6 +60,57 @@ 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 TR_CLASS = {
|
||||
'[:alnum:]':
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
|
||||
'[:alpha:]': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'[:blank:]': ' \t',
|
||||
'[:cntrl:]': [...Array(32).keys()].map((i) => String.fromCharCode(i)).join('') + '\x7f',
|
||||
'[:digit:]': '0123456789',
|
||||
'[:lower:]': 'abcdefghijklmnopqrstuvwxyz',
|
||||
'[:print:]': [...Array(95).keys()].map((i) => String.fromCharCode(i + 32)).join(''),
|
||||
'[:punct:]': '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~',
|
||||
'[:space:]': ' \t\n\r\v\f',
|
||||
'[:upper:]': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'[:xdigit:]': '0123456789abcdefABCDEF'
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
function trExpandClasses(s) {
|
||||
let out = s
|
||||
for (const [name, chars] of Object.entries(TR_CLASS)) {
|
||||
out = out.split(name).join(chars)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
let del = false
|
||||
const sets = []
|
||||
@@ -74,14 +125,14 @@ async function run(ctx, argv) {
|
||||
}
|
||||
const s = bareStdin(ctx)
|
||||
if (del) {
|
||||
const kill = new Set(sets[0].split(''))
|
||||
const kill = new Set(trExpandClasses(sets[0]).split(''))
|
||||
let o = ''
|
||||
for (const ch of s) if (!kill.has(ch)) o += ch
|
||||
ctx.console.log(o)
|
||||
return
|
||||
}
|
||||
const from = sets[0]
|
||||
const to = sets[1]
|
||||
const from = trExpandClasses(sets[0])
|
||||
const to = trExpandClasses(sets[1])
|
||||
const map = Object.create(null)
|
||||
const n = Math.max(from.length, to.length)
|
||||
for (let i = 0; i < n; i++) {
|
||||
|
||||
@@ -60,6 +60,33 @@ 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) {
|
||||
ctx.exitCode = 0
|
||||
}
|
||||
|
||||
@@ -60,6 +60,33 @@ 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 tty = globalThis.process?.stdout?.isTTY
|
||||
if (tty) {
|
||||
|
||||
+69
-10
@@ -60,15 +60,74 @@ 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 flagArgs = argv.slice(1).filter((a) => a.startsWith('-') && a !== '--')
|
||||
const all = argv.includes('-a')
|
||||
const noFlag = flagArgs.length === 0
|
||||
const wantS = all || argv.includes('-s') || noFlag
|
||||
const wantN = all || argv.includes('-n')
|
||||
const wantR = all || argv.includes('-r')
|
||||
const wantM = all || argv.includes('-m')
|
||||
const wantV = all || argv.includes('-v')
|
||||
const e = ctx.vfs.env || {}
|
||||
const nodename = e.HOSTNAME || e.NAME || 'bare-os'
|
||||
let all = false
|
||||
let wantS = false
|
||||
let wantN = false
|
||||
let wantR = false
|
||||
let wantM = false
|
||||
let wantV = false
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '--') break
|
||||
if (a === '-a' || a === '--all') {
|
||||
all = true
|
||||
continue
|
||||
}
|
||||
if (!a.startsWith('-') || a === '-') {
|
||||
ctx.console.error('uname: unexpected argument ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
for (let j = 1; j < a.length; j++) {
|
||||
const c = a[j]
|
||||
if (c === 'a') all = true
|
||||
else if (c === 's') wantS = true
|
||||
else if (c === 'n') wantN = true
|
||||
else if (c === 'r') wantR = true
|
||||
else if (c === 'm') wantM = true
|
||||
else if (c === 'v') wantV = true
|
||||
else {
|
||||
ctx.console.error('uname: invalid option -- ' + c)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if (all) {
|
||||
wantS = wantN = wantR = wantM = wantV = true
|
||||
} else if (!wantS && !wantN && !wantR && !wantM && !wantV) {
|
||||
wantS = true
|
||||
}
|
||||
|
||||
let name = 'BareOS'
|
||||
let version = '0.1'
|
||||
@@ -85,10 +144,10 @@ async function run(ctx, argv) {
|
||||
|
||||
const parts = []
|
||||
if (wantS) parts.push(name)
|
||||
if (wantN) parts.push('bare-os')
|
||||
if (wantN) parts.push(nodename)
|
||||
if (wantR) parts.push(version)
|
||||
if (wantV) parts.push('bare-userland')
|
||||
if (wantM) parts.push('unknown')
|
||||
if (wantV) parts.push('bare-userland')
|
||||
|
||||
ctx.console.log(parts.join(' '))
|
||||
}
|
||||
|
||||
+127
-13
@@ -60,39 +60,153 @@ function barePosixBlocks(size) {
|
||||
return Math.ceil(Number(size) / 512) || 0
|
||||
}
|
||||
|
||||
function count(s, b4a) {
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* wc — line, word, and byte counts.
|
||||
* Default (no flags): -l -w -c. Flags are combinable.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | Uint8Array} input
|
||||
* @param {*} b4a
|
||||
*/
|
||||
function wcCount(input, b4a) {
|
||||
const bytes =
|
||||
input instanceof Uint8Array
|
||||
? input.byteLength
|
||||
: b4a.from(String(input), 'utf8').length
|
||||
const s =
|
||||
input instanceof Uint8Array ? b4a.toString(input) : String(input)
|
||||
const lines = (s.match(/\n/g) || []).length
|
||||
const words = s.trim() ? s.trim().split(/\s+/).length : 0
|
||||
const bytes = b4a.from(s, 'utf8').length
|
||||
const words = (s.match(/\S+/g) || []).length
|
||||
return { lines, words, bytes }
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const vfs = ctx.vfs
|
||||
const files = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
if (!files.length) {
|
||||
let wantL = false
|
||||
let wantW = false
|
||||
let wantC = false
|
||||
const paths = []
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
i++
|
||||
break
|
||||
}
|
||||
if (a === '-l' || a === '--lines') {
|
||||
wantL = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-w' || a === '--words') {
|
||||
wantW = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-c' || a === '--bytes' || a === '-m') {
|
||||
wantC = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-') && a.length > 1) {
|
||||
let j = 1
|
||||
let ok = true
|
||||
while (j < a.length) {
|
||||
const c = a[j++]
|
||||
if (c === 'l') wantL = true
|
||||
else if (c === 'w') wantW = true
|
||||
else if (c === 'c' || c === 'm') wantC = true
|
||||
else {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
}
|
||||
paths.push(a)
|
||||
i++
|
||||
}
|
||||
if (!wantL && !wantW && !wantC) {
|
||||
wantL = true
|
||||
wantW = true
|
||||
wantC = true
|
||||
}
|
||||
|
||||
function fmt(c, name) {
|
||||
const parts = []
|
||||
if (wantL) parts.push(String(c.lines))
|
||||
if (wantW) parts.push(String(c.words))
|
||||
if (wantC) parts.push(String(c.bytes))
|
||||
let s = ' ' + parts.join(' ')
|
||||
if (name != null) s += ' ' + name
|
||||
return s
|
||||
}
|
||||
|
||||
if (!paths.length) {
|
||||
const s = bareStdin(ctx)
|
||||
const c = count(s, ctx.b4a)
|
||||
ctx.console.log(' ' + c.lines + ' ' + c.words + ' ' + c.bytes)
|
||||
const c = wcCount(s, ctx.b4a)
|
||||
ctx.console.log(fmt(c, null))
|
||||
return
|
||||
}
|
||||
let tLines = 0
|
||||
let tWords = 0
|
||||
let tBytes = 0
|
||||
for (const f of files) {
|
||||
for (const f of paths) {
|
||||
if (f === '-') {
|
||||
const s = bareStdin(ctx)
|
||||
const c = wcCount(s, ctx.b4a)
|
||||
tLines += c.lines
|
||||
tWords += c.words
|
||||
tBytes += c.bytes
|
||||
ctx.console.log(fmt(c, '-'))
|
||||
continue
|
||||
}
|
||||
const buf = await vfs.readFile(f)
|
||||
if (!buf) {
|
||||
ctx.console.error('wc: ' + f + ': No such file')
|
||||
continue
|
||||
}
|
||||
const s = ctx.b4a.toString(buf)
|
||||
const c = count(s, ctx.b4a)
|
||||
const u8 = buf instanceof Uint8Array ? buf : ctx.b4a.from(buf)
|
||||
const c = wcCount(u8, ctx.b4a)
|
||||
tLines += c.lines
|
||||
tWords += c.words
|
||||
tBytes += c.bytes
|
||||
ctx.console.log(' ' + c.lines + ' ' + c.words + ' ' + c.bytes + ' ' + f)
|
||||
ctx.console.log(fmt(c, f))
|
||||
}
|
||||
if (files.length > 1) {
|
||||
ctx.console.log(' ' + tLines + ' ' + tWords + ' ' + tBytes + ' total')
|
||||
if (paths.length > 1) {
|
||||
ctx.console.log(
|
||||
fmt({ lines: tLines, words: tWords, bytes: tBytes }, 'total')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+44
-1
@@ -60,6 +60,33 @@ 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 joinDirFile(dir, name) {
|
||||
if (!dir || dir === '.') return name
|
||||
const d = dir.endsWith('/') ? dir.slice(0, -1) : dir
|
||||
@@ -67,7 +94,23 @@ function joinDirFile(dir, name) {
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const names = argv.slice(1).filter((a) => !a.startsWith('-'))
|
||||
/** @type {string[]} */
|
||||
const names = []
|
||||
let i = 1
|
||||
while (i < argv.length) {
|
||||
const a = argv[i]
|
||||
if (a === '--') {
|
||||
names.push(...argv.slice(i + 1))
|
||||
break
|
||||
}
|
||||
if (a.startsWith('-')) {
|
||||
ctx.console.error('which: unsupported option ' + a)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
names.push(a)
|
||||
i++
|
||||
}
|
||||
if (!names.length) {
|
||||
ctx.console.error('which: missing argument')
|
||||
ctx.exitCode = 1
|
||||
|
||||
@@ -60,6 +60,33 @@ 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) {
|
||||
ctx.console.log(ctx.vfs.env.USER || ctx.vfs.env.LOGNAME || 'guest')
|
||||
}
|
||||
|
||||
+68
-2
@@ -60,10 +60,38 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Bounded xargs for Bare OS: invokes ctx.runBinCommand only (no host spawn).
|
||||
* Limits: stdin 256KiB, 4096 whitespace/null tokens, 128 args per invocation,
|
||||
* 64 invocations per run. Exceeding limits is a fatal error (exit 125).
|
||||
* Supports -0/--null, -n, -I repl (replace repl in utility argv; implies -n 1 unless -n given).
|
||||
*/
|
||||
|
||||
const MAX_STDIN = 256 * 1024
|
||||
@@ -82,6 +110,9 @@ async function run(ctx, argv) {
|
||||
const args = argv.slice(1)
|
||||
let nullSep = false
|
||||
let maxBatch = MAX_PER_INVOCATION
|
||||
/** @type {string | null} */
|
||||
let repl = null
|
||||
let nExplicit = false
|
||||
let i = 0
|
||||
|
||||
while (i < args.length && args[i].startsWith('-')) {
|
||||
@@ -103,20 +134,38 @@ async function run(ctx, argv) {
|
||||
return
|
||||
}
|
||||
maxBatch = Math.min(Number(n), MAX_PER_INVOCATION)
|
||||
nExplicit = true
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-n') && a.length > 2 && /^\d+$/.test(a.slice(2))) {
|
||||
const n = Number(a.slice(2))
|
||||
maxBatch = Math.min(n, MAX_PER_INVOCATION)
|
||||
nExplicit = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (a === '-I' || a === '-i') {
|
||||
repl = args[i + 1] != null ? String(args[i + 1]) : '{}'
|
||||
i += 2
|
||||
if (!nExplicit) maxBatch = 1
|
||||
continue
|
||||
}
|
||||
if (
|
||||
(a.startsWith('-I') || a.startsWith('-i')) &&
|
||||
a.length > 2 &&
|
||||
a[2] !== '-'
|
||||
) {
|
||||
repl = a.slice(2) || '{}'
|
||||
i++
|
||||
if (!nExplicit) maxBatch = 1
|
||||
continue
|
||||
}
|
||||
ctx.console.error('xargs: unsupported option: ' + a)
|
||||
ctx.console.error(
|
||||
'xargs: Bare OS supports: -0/--null, -n N (max ' +
|
||||
MAX_PER_INVOCATION +
|
||||
' per run)'
|
||||
' per run), -I repl'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
@@ -151,8 +200,25 @@ async function run(ctx, argv) {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} batch
|
||||
*/
|
||||
const runOne = async (batch) => {
|
||||
await ctx.runBinCommand(cmd.concat(batch))
|
||||
const subst = batch.join(' ')
|
||||
/** @type {string[]} */
|
||||
const toRun =
|
||||
repl == null
|
||||
? cmd.concat(batch)
|
||||
: cmd.map((c) => {
|
||||
let o = c
|
||||
let guard = 0
|
||||
while (o.includes(repl) && guard < 4096) {
|
||||
o = o.split(repl).join(subst)
|
||||
guard++
|
||||
}
|
||||
return o
|
||||
})
|
||||
await ctx.runBinCommand(toRun)
|
||||
}
|
||||
|
||||
if (pieces.length === 0) {
|
||||
|
||||
+182
-182
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"version": 1,
|
||||
"bundles": [
|
||||
{
|
||||
"path": "/lib/bare/bundles/b4a.js",
|
||||
"keys": [
|
||||
"b4a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||
"keys": [
|
||||
"safetyCatch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/b4a.js",
|
||||
"keys": [
|
||||
"b4a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
||||
"keys": [
|
||||
@@ -25,18 +25,18 @@
|
||||
"compactEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUrl.js",
|
||||
"keys": [
|
||||
"bareUrl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/protomux.js",
|
||||
"keys": [
|
||||
"protomux"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUrl.js",
|
||||
"keys": [
|
||||
"bareUrl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"keys": [
|
||||
@@ -67,12 +67,6 @@
|
||||
"bareAbortController"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
|
||||
"keys": [
|
||||
"bareAnsiEscapes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
@@ -80,15 +74,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareReadline.js",
|
||||
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
|
||||
"keys": [
|
||||
"bareReadline"
|
||||
"bareAnsiEscapes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAppKit.js",
|
||||
"path": "/lib/bare/bundles/bareReadline.js",
|
||||
"keys": [
|
||||
"bareAppKit"
|
||||
"bareReadline"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -109,6 +103,12 @@
|
||||
"bareAssert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/fetch.js",
|
||||
"keys": [
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
||||
"keys": [
|
||||
@@ -116,15 +116,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"path": "/lib/bare/bundles/bareAppKit.js",
|
||||
"keys": [
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/fetch.js",
|
||||
"keys": [
|
||||
"fetch"
|
||||
"bareAppKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -134,9 +128,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleCompile.js",
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"keys": [
|
||||
"bareBundleCompile"
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBuffer.js",
|
||||
"keys": [
|
||||
"bareBuffer"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -146,9 +146,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBuffer.js",
|
||||
"path": "/lib/bare/bundles/bareBundleCompile.js",
|
||||
"keys": [
|
||||
"bareBuffer"
|
||||
"bareBundleCompile"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -163,6 +163,12 @@
|
||||
"bareBundleEvaluate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBoot.js",
|
||||
"keys": [
|
||||
"bareBoot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareConsole.js",
|
||||
"keys": [
|
||||
@@ -176,15 +182,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBoot.js",
|
||||
"path": "/lib/bare/bundles/bareChannel.js",
|
||||
"keys": [
|
||||
"bareBoot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||
"keys": [
|
||||
"bareDaemon"
|
||||
"bareChannel"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -194,9 +194,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareChannel.js",
|
||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||
"keys": [
|
||||
"bareChannel"
|
||||
"bareDaemon"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -217,6 +217,12 @@
|
||||
"bareDns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareExif.js",
|
||||
"keys": [
|
||||
"bareExif"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareEnv.js",
|
||||
"keys": [
|
||||
@@ -229,24 +235,12 @@
|
||||
"bareCov"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareExif.js",
|
||||
"keys": [
|
||||
"bareExif"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDgram.js",
|
||||
"keys": [
|
||||
"bareDgram"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||
"keys": [
|
||||
"bareFfmpeg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||
"keys": [
|
||||
@@ -254,15 +248,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFormData.js",
|
||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||
"keys": [
|
||||
"bareFormData"
|
||||
"bareFfmpeg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFileLogger.js",
|
||||
"path": "/lib/bare/bundles/bareFormData.js",
|
||||
"keys": [
|
||||
"bareFileLogger"
|
||||
"bareFormData"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -278,15 +272,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHrtime.js",
|
||||
"path": "/lib/bare/bundles/bareFileLogger.js",
|
||||
"keys": [
|
||||
"bareHrtime"
|
||||
"bareFileLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareGtk.js",
|
||||
"path": "/lib/bare/bundles/bareHrtime.js",
|
||||
"keys": [
|
||||
"bareGtk"
|
||||
"bareHrtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -302,15 +296,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFs.js",
|
||||
"path": "/lib/bare/bundles/bareGtk.js",
|
||||
"keys": [
|
||||
"bareFs"
|
||||
"bareGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareImageResample.js",
|
||||
"path": "/lib/bare/bundles/bareFs.js",
|
||||
"keys": [
|
||||
"bareImageResample"
|
||||
"bareFs"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -320,9 +314,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspect.js",
|
||||
"path": "/lib/bare/bundles/bareImageResample.js",
|
||||
"keys": [
|
||||
"bareInspect"
|
||||
"bareImageResample"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -331,6 +325,12 @@
|
||||
"bareHttp1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspect.js",
|
||||
"keys": [
|
||||
"bareInspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttps.js",
|
||||
"keys": [
|
||||
@@ -349,12 +349,6 @@
|
||||
"bareIntl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLogger.js",
|
||||
"keys": [
|
||||
"bareLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareIpc.js",
|
||||
"keys": [
|
||||
@@ -367,6 +361,12 @@
|
||||
"bareLief"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLogger.js",
|
||||
"keys": [
|
||||
"bareLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLink.js",
|
||||
"keys": [
|
||||
@@ -385,18 +385,18 @@
|
||||
"bareMake"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
||||
"keys": [
|
||||
"bareModuleLexer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||
"keys": [
|
||||
"bareModuleResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
||||
"keys": [
|
||||
"bareModuleLexer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
@@ -415,12 +415,6 @@
|
||||
"bareNdk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNative.js",
|
||||
"keys": [
|
||||
@@ -428,21 +422,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareMedia.js",
|
||||
"keys": [
|
||||
"bareMedia"
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -452,15 +434,21 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePerformance.js",
|
||||
"path": "/lib/bare/bundles/bareMedia.js",
|
||||
"keys": [
|
||||
"barePerformance"
|
||||
"bareMedia"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePack.js",
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"barePack"
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -470,9 +458,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePipe.js",
|
||||
"path": "/lib/bare/bundles/barePack.js",
|
||||
"keys": [
|
||||
"barePipe"
|
||||
"barePack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePerformance.js",
|
||||
"keys": [
|
||||
"barePerformance"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -481,6 +475,12 @@
|
||||
"barePng"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePipe.js",
|
||||
"keys": [
|
||||
"barePipe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
@@ -517,36 +517,24 @@
|
||||
"bareQueueMicrotask"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRealm.js",
|
||||
"keys": [
|
||||
"bareRealm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"keys": [
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRealm.js",
|
||||
"keys": [
|
||||
"bareRealm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRuntime.js",
|
||||
"keys": [
|
||||
"bareRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRpc.js",
|
||||
"keys": [
|
||||
"bareRpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePromClient.js",
|
||||
"keys": [
|
||||
"barePromClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSdl.js",
|
||||
"keys": [
|
||||
@@ -560,15 +548,21 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"path": "/lib/bare/bundles/bareRpc.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
"bareRpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRun.js",
|
||||
"path": "/lib/bare/bundles/barePromClient.js",
|
||||
"keys": [
|
||||
"bareRun"
|
||||
"barePromClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -578,15 +572,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||
"path": "/lib/bare/bundles/bareRun.js",
|
||||
"keys": [
|
||||
"bareSidecar"
|
||||
"bareRun"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||
"keys": [
|
||||
"bareStringDecoder"
|
||||
"bareSidecar"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -596,15 +590,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||
"keys": [
|
||||
"bareSvg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
"bareStringDecoder"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -614,9 +602,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareSystemLogger"
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"keys": [
|
||||
"bareSvg"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -626,15 +620,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTap.js",
|
||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||
"keys": [
|
||||
"bareTap"
|
||||
"bareSystemLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"path": "/lib/bare/bundles/bareTap.js",
|
||||
"keys": [
|
||||
"bareTiff"
|
||||
"bareTap"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -644,15 +638,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareThread.js",
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"keys": [
|
||||
"bareThread"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTpl.js",
|
||||
"keys": [
|
||||
"bareTpl"
|
||||
"bareTiff"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -661,30 +649,30 @@
|
||||
"bareTimers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareThread.js",
|
||||
"keys": [
|
||||
"bareThread"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTcp.js",
|
||||
"keys": [
|
||||
"bareTcp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTpl.js",
|
||||
"keys": [
|
||||
"bareTpl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareType.js",
|
||||
"keys": [
|
||||
"bareType"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTls.js",
|
||||
"keys": [
|
||||
"bareTls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTty.js",
|
||||
"keys": [
|
||||
"bareTty"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUiKit.js",
|
||||
"keys": [
|
||||
@@ -697,6 +685,18 @@
|
||||
"bareUnpack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTls.js",
|
||||
"keys": [
|
||||
"bareTls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTty.js",
|
||||
"keys": [
|
||||
"bareTty"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8.js",
|
||||
"keys": [
|
||||
@@ -721,30 +721,24 @@
|
||||
"bareUnionBundle"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
"bareWebp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKit.js",
|
||||
"keys": [
|
||||
"bareWebKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWhich.js",
|
||||
"keys": [
|
||||
"bareWhich"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
"bareWebp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUtils.js",
|
||||
"keys": [
|
||||
@@ -752,9 +746,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareV8ToIstanbul"
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -763,6 +757,12 @@
|
||||
"bareWinUi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||
"keys": [
|
||||
"bareV8ToIstanbul"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareXdiff.js",
|
||||
"keys": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user