Expand bounded awk/expr/test toward Issue 7; refresh man, profile 1.0.18,
posix matrix/dashboard, and syscalls/process_table schema alignment (v8). Booter: replication_operator_sketch/corestore hints, HRPC allowlist tests, Protomux cap channel 65536-byte bound + export, Wasm posix_profile_peek, swarm-disk and security_posture docs. Coreutils/kernel: pkg-swarm-index pathCapabilityEnvelopeVerify on get; pathcap-verify --trusted failure hint; rebuild bins and sync seeder. Docs: KERNEL_CONTRACT, kernel-extensions, capabilities index, environment appendix (warm-cache tuning, cap channel, Wasm env), handbook observability, vault threat model (multisig), developer-guide ctx/HRPC/Wasm, DOCUMENTATION release-checklist note, release-checklist optional tier1 drift. Changelog maintenance in bare-os-booter and bare-os-protocol.
This commit is contained in:
+171
-95
@@ -91,7 +91,8 @@ function bareOsEmitRaw(ctx, chunk) {
|
||||
* 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),
|
||||
* delete arr[idx], next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
||||
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
||||
* split, sprintf, sub, gsub, match, int, atan2, cos, sin, exp, log, sqrt, tolower, toupper, rand, srand),
|
||||
* getline from stdin, files (getline x < \"path\"), unary minus,
|
||||
* optional fixed-width fields via FIELDWIDTHS when BARE_OS_AWK_FIELDWIDTHS is enabled,
|
||||
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
||||
* GNU-style empty fields: consecutive FS delimiters still advance $1..$NF where the grammar allows.
|
||||
@@ -670,7 +671,11 @@ class BareAwkParser {
|
||||
}
|
||||
|
||||
parseUnary() {
|
||||
if (this.peek().value === '!' || this.peek().value === '+') {
|
||||
if (
|
||||
this.peek().value === '!' ||
|
||||
this.peek().value === '+' ||
|
||||
this.peek().value === '-'
|
||||
) {
|
||||
const op = String(this.peek().value)
|
||||
this.p++
|
||||
return { t: 'unop', op, a: this.parseUnary() }
|
||||
@@ -819,6 +824,8 @@ class BareAwkRuntime {
|
||||
this._stdinIx = 0
|
||||
/** @type {{ path: string, data: string, append: boolean }[]} */
|
||||
this.pendingWrites = []
|
||||
/** @type {Map<string, { lines: string[], i: number }>} */
|
||||
this._getlineFileState = new Map()
|
||||
}
|
||||
|
||||
splitFields() {
|
||||
@@ -889,14 +896,14 @@ class BareAwkRuntime {
|
||||
/**
|
||||
* @param {BareAwkNode | null} pat
|
||||
*/
|
||||
patternMatch(pat) {
|
||||
async patternMatch(pat) {
|
||||
if (pat == null) return true
|
||||
if (pat.t === 'pat') return false
|
||||
if (pat.t === 'patRe') {
|
||||
pat.rx.lastIndex = 0
|
||||
return pat.rx.test(this.$0)
|
||||
}
|
||||
if (pat.t === 'patExpr') return this.truthy(this.evalExpr(pat.e))
|
||||
if (pat.t === 'patExpr') return this.truthy(await this.evalExpr(pat.e))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -910,7 +917,7 @@ class BareAwkRuntime {
|
||||
/**
|
||||
* @param {BareAwkNode} n
|
||||
*/
|
||||
evalExpr(n) {
|
||||
async evalExpr(n) {
|
||||
if (!n) return ''
|
||||
switch (n.t) {
|
||||
case 'num':
|
||||
@@ -932,9 +939,9 @@ class BareAwkRuntime {
|
||||
return this.vars[name] != null ? this.vars[name] : ''
|
||||
}
|
||||
case 'field':
|
||||
return this.fieldNum(this.evalExpr(n.e))
|
||||
return this.fieldNum(await this.evalExpr(n.e))
|
||||
case 'index': {
|
||||
const k = String(this.evalExpr(n.idx))
|
||||
const k = String(await this.evalExpr(n.idx))
|
||||
if (n.name === 'ENVIRON') {
|
||||
const v = this.environ[k]
|
||||
return v != null ? v : ''
|
||||
@@ -944,8 +951,8 @@ class BareAwkRuntime {
|
||||
return arr[k] != null ? arr[k] : ''
|
||||
}
|
||||
case 'binop': {
|
||||
const a = this.evalExpr(n.a)
|
||||
const b = this.evalExpr(n.b)
|
||||
const a = await this.evalExpr(n.a)
|
||||
const b = await this.evalExpr(n.b)
|
||||
const an = Number(a)
|
||||
const bn = Number(b)
|
||||
switch (n.op) {
|
||||
@@ -978,77 +985,78 @@ class BareAwkRuntime {
|
||||
}
|
||||
}
|
||||
case 'unop': {
|
||||
const a = this.evalExpr(n.a)
|
||||
const a = await this.evalExpr(n.a)
|
||||
if (n.op === '!') return this.truthy(a) ? 0 : 1
|
||||
if (n.op === '+') return Number(a) || 0
|
||||
if (n.op === '-') return -(Number(a) || 0)
|
||||
return 0
|
||||
}
|
||||
case '||':
|
||||
return this.truthy(this.evalExpr(n.a))
|
||||
return this.truthy(await this.evalExpr(n.a))
|
||||
? 1
|
||||
: this.truthy(this.evalExpr(n.b))
|
||||
: this.truthy(await this.evalExpr(n.b))
|
||||
? 1
|
||||
: 0
|
||||
case '&&':
|
||||
return this.truthy(this.evalExpr(n.a)) &&
|
||||
this.truthy(this.evalExpr(n.b))
|
||||
return this.truthy(await this.evalExpr(n.a)) &&
|
||||
this.truthy(await this.evalExpr(n.b))
|
||||
? 1
|
||||
: 0
|
||||
case 'match': {
|
||||
const s = String(this.evalExpr(n.a))
|
||||
const s = String(await this.evalExpr(n.a))
|
||||
const rhs = n.b
|
||||
let rx
|
||||
if (rhs.t === 'rxLit') rx = rhs.rx
|
||||
else {
|
||||
const t = String(this.evalExpr(rhs))
|
||||
const t = String(await this.evalExpr(rhs))
|
||||
rx = bareAwkMakeRx(t, '')
|
||||
}
|
||||
rx.lastIndex = 0
|
||||
return rx.test(s) ? 1 : 0
|
||||
}
|
||||
case 'nmatch': {
|
||||
const s = String(this.evalExpr(n.a))
|
||||
const s = String(await this.evalExpr(n.a))
|
||||
const rhs = n.b
|
||||
let rx
|
||||
if (rhs.t === 'rxLit') rx = rhs.rx
|
||||
else {
|
||||
const t = String(this.evalExpr(rhs))
|
||||
const t = String(await this.evalExpr(rhs))
|
||||
rx = bareAwkMakeRx(t, '')
|
||||
}
|
||||
rx.lastIndex = 0
|
||||
return rx.test(s) ? 0 : 1
|
||||
}
|
||||
case '?:':
|
||||
return this.truthy(this.evalExpr(n.n))
|
||||
? this.evalExpr(n.a)
|
||||
: this.evalExpr(n.b)
|
||||
return this.truthy(await this.evalExpr(n.n))
|
||||
? await this.evalExpr(n.a)
|
||||
: await this.evalExpr(n.b)
|
||||
case 'assign': {
|
||||
const v = this.evalExpr(n.right)
|
||||
this.assign(n.left, n.op, v)
|
||||
const v = await this.evalExpr(n.right)
|
||||
await this.assign(n.left, n.op, v)
|
||||
return v
|
||||
}
|
||||
case 'pre': {
|
||||
const cur = Number(this.evalExpr(n.x))
|
||||
const cur = Number(await this.evalExpr(n.x))
|
||||
const next = n.op === '++' ? cur + 1 : cur - 1
|
||||
this.assignScalar(n.x, next)
|
||||
await this.assignScalar(n.x, next)
|
||||
return next
|
||||
}
|
||||
case 'post': {
|
||||
const cur = Number(this.evalExpr(n.x))
|
||||
const cur = Number(await this.evalExpr(n.x))
|
||||
const next = n.op === '++' ? cur + 1 : cur - 1
|
||||
this.assignScalar(n.x, next)
|
||||
await this.assignScalar(n.x, next)
|
||||
return cur
|
||||
}
|
||||
case 'call':
|
||||
return this.callBuiltin(n.name, n.args)
|
||||
return await this.callBuiltin(n.name, n.args)
|
||||
case 'getline':
|
||||
return this.doGetline(n)
|
||||
return await this.doGetline(n)
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
assignScalar(x, v) {
|
||||
async assignScalar(x, v) {
|
||||
if (x.t === 'var') {
|
||||
const name = x.name
|
||||
if (name === 'FS') this.FS = String(v)
|
||||
@@ -1057,7 +1065,7 @@ class BareAwkRuntime {
|
||||
else if (name === 'RS') this.RS = String(v)
|
||||
else this.vars[name] = v
|
||||
} else if (x.t === 'field') {
|
||||
this.setField(this.evalExpr(x.e), v)
|
||||
this.setField(await this.evalExpr(x.e), v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,10 +1074,10 @@ class BareAwkRuntime {
|
||||
* @param {string} op
|
||||
* @param {unknown} v
|
||||
*/
|
||||
assign(left, op, v) {
|
||||
async assign(left, op, v) {
|
||||
let base = v
|
||||
if (op !== '=') {
|
||||
const cur = Number(this.evalExpr(left))
|
||||
const cur = Number(await this.evalExpr(left))
|
||||
const nv = Number(v)
|
||||
if (op === '+=') base = cur + nv
|
||||
else if (op === '-=') base = cur - nv
|
||||
@@ -1080,32 +1088,52 @@ class BareAwkRuntime {
|
||||
if (left.t === 'index') {
|
||||
const arr =
|
||||
this.arrays[left.name] || (this.arrays[left.name] = Object.create(null))
|
||||
arr[String(this.evalExpr(left.idx))] = base
|
||||
arr[String(await this.evalExpr(left.idx))] = base
|
||||
return
|
||||
}
|
||||
this.assignScalar(left, base)
|
||||
await this.assignScalar(left, base)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
async _ensureGetlineFile(path) {
|
||||
let st = this._getlineFileState.get(path)
|
||||
if (st) return st
|
||||
const buf = await this.io.readFile(path)
|
||||
const text =
|
||||
buf && this.io.bytesToString ? this.io.bytesToString(buf) : ''
|
||||
const lines = text.split(/\r?\n/)
|
||||
if (lines.length && lines[lines.length - 1] === '') lines.pop()
|
||||
st = { lines, i: 0 }
|
||||
this._getlineFileState.set(path, st)
|
||||
return st
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {BareAwkNode[]} args
|
||||
*/
|
||||
callBuiltin(name, args) {
|
||||
const a = (i) => (args[i] ? this.evalExpr(args[i]) : '')
|
||||
async callBuiltin(name, args) {
|
||||
const a = async (i) =>
|
||||
args[i] != null ? await this.evalExpr(args[i]) : ''
|
||||
switch (name) {
|
||||
case 'length':
|
||||
if (!args.length) return this.$0.length
|
||||
return String(a(0)).length
|
||||
case 'substr':
|
||||
return String(a(0)).slice(
|
||||
Math.max(0, Math.trunc(Number(a(1))) - 1),
|
||||
Math.max(0, Math.trunc(Number(a(1))) - 1 + Math.trunc(Number(a(2))))
|
||||
)
|
||||
return String(await a(0)).length
|
||||
case 'substr': {
|
||||
const s = String(await a(0))
|
||||
const start = Math.max(0, Math.trunc(Number(await a(1))) - 1)
|
||||
if (args.length < 3) return s.slice(start)
|
||||
const ln = Math.trunc(Number(await a(2)))
|
||||
if (!Number.isFinite(ln) || ln < 0) return ''
|
||||
return s.slice(start, start + ln)
|
||||
}
|
||||
case 'index':
|
||||
return String(a(0)).indexOf(String(a(1))) + 1 || 0
|
||||
return String(await a(0)).indexOf(String(await a(1))) + 1 || 0
|
||||
case 'split': {
|
||||
const s = String(a(0))
|
||||
const fs = args[1] ? String(this.evalExpr(args[1])) : this.FS
|
||||
const s = String(await a(0))
|
||||
const fs = args[1] ? String(await this.evalExpr(args[1])) : this.FS
|
||||
let rx = fs.length === 1 ? null : bareAwkMakeRx(fs, '')
|
||||
const parts = rx ? s.split(rx) : s.split(fs)
|
||||
const aname = /** @type {BareAwkNode} */ (args[2])
|
||||
@@ -1116,17 +1144,35 @@ class BareAwkRuntime {
|
||||
}
|
||||
return parts.length
|
||||
}
|
||||
case 'sprintf':
|
||||
return bareAwkSprintf(
|
||||
String(a(0)),
|
||||
args.slice(1).map((x) => this.evalExpr(x))
|
||||
)
|
||||
case 'sprintf': {
|
||||
const fmt = String(await a(0))
|
||||
const rest = []
|
||||
for (let j = 1; j < args.length; j++)
|
||||
rest.push(await this.evalExpr(args[j]))
|
||||
return bareAwkSprintf(fmt, rest)
|
||||
}
|
||||
case 'int':
|
||||
return Math.trunc(Number(a(0)))
|
||||
return Math.trunc(Number(await a(0)))
|
||||
case 'log': {
|
||||
const x = Number(await a(0))
|
||||
return x > 0 ? Math.log(x) : Number.NaN
|
||||
}
|
||||
case 'sqrt': {
|
||||
const x = Number(await a(0))
|
||||
return x >= 0 ? Math.sqrt(x) : Number.NaN
|
||||
}
|
||||
case 'sin':
|
||||
return Math.sin(Number(await a(0)))
|
||||
case 'cos':
|
||||
return Math.cos(Number(await a(0)))
|
||||
case 'exp':
|
||||
return Math.exp(Number(await a(0)))
|
||||
case 'atan2':
|
||||
return Math.atan2(Number(await a(0)), Number(await a(1)))
|
||||
case 'tolower':
|
||||
return String(a(0)).toLowerCase()
|
||||
return String(await a(0)).toLowerCase()
|
||||
case 'toupper':
|
||||
return String(a(0)).toUpperCase()
|
||||
return String(await a(0)).toUpperCase()
|
||||
case 'rand': {
|
||||
this.randSeed = (this.randSeed * 1103515245 + 12345) & 0x7fffffff
|
||||
return this.randSeed / 0x7fffffff
|
||||
@@ -1140,9 +1186,11 @@ class BareAwkRuntime {
|
||||
case 'sub':
|
||||
case 'gsub': {
|
||||
const rx =
|
||||
args[0].t === 'rxLit' ? args[0].rx : bareAwkMakeRx(String(a(0)), '')
|
||||
const rep = String(a(1))
|
||||
let target = args[2] ? String(a(2)) : this.$0
|
||||
args[0].t === 'rxLit'
|
||||
? args[0].rx
|
||||
: bareAwkMakeRx(String(await a(0)), '')
|
||||
const rep = String(await a(1))
|
||||
let target = args[2] ? String(await a(2)) : this.$0
|
||||
let n = 0
|
||||
if (name === 'sub') {
|
||||
rx.lastIndex = 0
|
||||
@@ -1167,9 +1215,11 @@ class BareAwkRuntime {
|
||||
return n
|
||||
}
|
||||
case 'match': {
|
||||
const s = String(a(0))
|
||||
const s = String(await a(0))
|
||||
const rx =
|
||||
args[1].t === 'rxLit' ? args[1].rx : bareAwkMakeRx(String(a(1)), '')
|
||||
args[1].t === 'rxLit'
|
||||
? args[1].rx
|
||||
: bareAwkMakeRx(String(await a(1)), '')
|
||||
rx.lastIndex = 0
|
||||
const m = rx.exec(s)
|
||||
if (!m) {
|
||||
@@ -1183,7 +1233,7 @@ class BareAwkRuntime {
|
||||
}
|
||||
default:
|
||||
if (this.funcs.has(name)) {
|
||||
return this.callUser(name, args)
|
||||
return await this.callUser(name, args)
|
||||
}
|
||||
bareAwkError('unknown function ' + name)
|
||||
}
|
||||
@@ -1193,19 +1243,19 @@ class BareAwkRuntime {
|
||||
* @param {string} name
|
||||
* @param {BareAwkNode[]} args
|
||||
*/
|
||||
callUser(name, args) {
|
||||
async callUser(name, args) {
|
||||
const f = this.funcs.get(name)
|
||||
if (!f) return ''
|
||||
const frame = { ...this.vars }
|
||||
for (let i = 0; i < f.params.length; i++) {
|
||||
frame[f.params[i]] = args[i] ? this.evalExpr(args[i]) : ''
|
||||
frame[f.params[i]] = args[i] ? await this.evalExpr(args[i]) : ''
|
||||
}
|
||||
const prev = this.vars
|
||||
this.vars = frame
|
||||
let ret = ''
|
||||
try {
|
||||
for (const st of f.body) {
|
||||
const r = this.execStmt(st)
|
||||
const r = await this.execStmt(st)
|
||||
if (r && r.t === 'return') {
|
||||
ret = r.v != null ? r.v : ''
|
||||
break
|
||||
@@ -1220,12 +1270,27 @@ class BareAwkRuntime {
|
||||
/**
|
||||
* @param {BareAwkNode} n
|
||||
*/
|
||||
doGetline(n) {
|
||||
async doGetline(n) {
|
||||
let line = null
|
||||
if (n.from) {
|
||||
const path = String(this.evalExpr(n.from))
|
||||
/* sync read — awk.js should preload or use async wrapper; runtime uses promise in run loop */
|
||||
return 0
|
||||
const path = String(await this.evalExpr(n.from))
|
||||
const st = await this._ensureGetlineFile(path)
|
||||
if (st.i < st.lines.length) line = st.lines[st.i++]
|
||||
else line = null
|
||||
if (line == null) {
|
||||
if (n.var) this.vars[n.var] = ''
|
||||
else {
|
||||
this.$0 = ''
|
||||
this.splitFields()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if (n.var) this.vars[n.var] = line
|
||||
else {
|
||||
this.$0 = line
|
||||
this.splitFields()
|
||||
}
|
||||
return 1
|
||||
}
|
||||
if (this._stdinIx < this.io.stdinLines.length) {
|
||||
line = this.io.stdinLines[this._stdinIx++]
|
||||
@@ -1245,23 +1310,24 @@ class BareAwkRuntime {
|
||||
|
||||
/**
|
||||
* @param {BareAwkNode} st
|
||||
* @returns {{ t: string, v?: unknown } | void}
|
||||
* @returns {Promise<{ t: string, v?: unknown } | void>}
|
||||
*/
|
||||
execStmt(st) {
|
||||
async execStmt(st) {
|
||||
switch (st.t) {
|
||||
case 'block':
|
||||
for (const s of st.stmts) {
|
||||
const r = this.execStmt(s)
|
||||
const r = await this.execStmt(s)
|
||||
if (r) return r
|
||||
}
|
||||
break
|
||||
case 'if':
|
||||
if (this.truthy(this.evalExpr(st.cond))) return this.execStmt(st.then)
|
||||
if (st.else) return this.execStmt(st.else)
|
||||
if (this.truthy(await this.evalExpr(st.cond)))
|
||||
return await this.execStmt(st.then)
|
||||
if (st.else) return await this.execStmt(st.else)
|
||||
break
|
||||
case 'while':
|
||||
while (this.truthy(this.evalExpr(st.cond))) {
|
||||
const r = this.execStmt(st.body)
|
||||
while (this.truthy(await this.evalExpr(st.cond))) {
|
||||
const r = await this.execStmt(st.body)
|
||||
if (r?.t === 'break') break
|
||||
if (r?.t === 'continue') continue
|
||||
if (r?.t === 'next' || r?.t === 'nextfile' || r?.t === 'exit')
|
||||
@@ -1269,25 +1335,25 @@ class BareAwkRuntime {
|
||||
}
|
||||
break
|
||||
case 'for':
|
||||
if (st.init) this.evalExpr(st.init)
|
||||
if (st.init) await this.evalExpr(st.init)
|
||||
while (true) {
|
||||
if (st.cond && !this.truthy(this.evalExpr(st.cond))) break
|
||||
const r = this.execStmt(st.body)
|
||||
if (st.cond && !this.truthy(await this.evalExpr(st.cond))) break
|
||||
const r = await this.execStmt(st.body)
|
||||
if (r?.t === 'break') break
|
||||
if (r?.t === 'continue') {
|
||||
if (st.step) this.evalExpr(st.step)
|
||||
if (st.step) await this.evalExpr(st.step)
|
||||
continue
|
||||
}
|
||||
if (r?.t === 'next' || r?.t === 'nextfile' || r?.t === 'exit')
|
||||
return r
|
||||
if (st.step) this.evalExpr(st.step)
|
||||
if (st.step) await this.evalExpr(st.step)
|
||||
}
|
||||
break
|
||||
case 'forin': {
|
||||
const arr = this.arrays[st.arr] || {}
|
||||
for (const k of Object.keys(arr)) {
|
||||
this.vars[st.iv] = k
|
||||
const r = this.execStmt(st.body)
|
||||
const r = await this.execStmt(st.body)
|
||||
if (r?.t === 'break') break
|
||||
if (r?.t === 'continue') continue
|
||||
if (r?.t === 'next' || r?.t === 'nextfile' || r?.t === 'exit')
|
||||
@@ -1298,7 +1364,7 @@ class BareAwkRuntime {
|
||||
case 'delete': {
|
||||
const tbl =
|
||||
this.arrays[st.arr] || (this.arrays[st.arr] = Object.create(null))
|
||||
const ky = String(this.evalExpr(st.key))
|
||||
const ky = String(await this.evalExpr(st.key))
|
||||
delete tbl[ky]
|
||||
break
|
||||
}
|
||||
@@ -1311,19 +1377,23 @@ class BareAwkRuntime {
|
||||
case 'continue':
|
||||
return { t: 'continue' }
|
||||
case 'exit':
|
||||
this.exitCode = Math.trunc(Number(this.evalExpr(st.code))) || 0
|
||||
this.exitCode =
|
||||
Math.trunc(Number(await this.evalExpr(st.code))) || 0
|
||||
this.exitPending = true
|
||||
return { t: 'exit' }
|
||||
case 'return':
|
||||
return { t: 'return', v: st.e ? this.evalExpr(st.e) : '' }
|
||||
return {
|
||||
t: 'return',
|
||||
v: st.e ? await this.evalExpr(st.e) : ''
|
||||
}
|
||||
case 'print':
|
||||
this.queuePrint(st.args, st.redir, false)
|
||||
await this.queuePrint(st.args, st.redir, false)
|
||||
break
|
||||
case 'printf':
|
||||
this.queuePrint([st.fmt, ...st.args], st.redir, true)
|
||||
await this.queuePrint([st.fmt, ...st.args], st.redir, true)
|
||||
break
|
||||
case 'expr':
|
||||
this.evalExpr(st.e)
|
||||
await this.evalExpr(st.e)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1333,17 +1403,23 @@ class BareAwkRuntime {
|
||||
* @param {{ op: string, file: BareAwkNode } | null} redir
|
||||
* @param {boolean} isPrintf
|
||||
*/
|
||||
queuePrint(args, redir, isPrintf) {
|
||||
async queuePrint(args, redir, isPrintf) {
|
||||
let s = ''
|
||||
if (isPrintf) {
|
||||
const fmt = String(this.evalExpr(args[0]))
|
||||
const rest = args.slice(1).map((x) => this.evalExpr(x))
|
||||
const fmt = String(await this.evalExpr(args[0]))
|
||||
const rest = []
|
||||
for (let j = 1; j < args.length; j++)
|
||||
rest.push(await this.evalExpr(args[j]))
|
||||
s = bareAwkSprintf(fmt, rest)
|
||||
} else if (!args.length) s = this.$0
|
||||
else s = args.map((x) => String(this.evalExpr(x))).join(this.OFS)
|
||||
else {
|
||||
const parts = []
|
||||
for (const x of args) parts.push(String(await this.evalExpr(x)))
|
||||
s = parts.join(this.OFS)
|
||||
}
|
||||
s += this.ORS
|
||||
if (redir) {
|
||||
const path = String(this.evalExpr(redir.file))
|
||||
const path = String(await this.evalExpr(redir.file))
|
||||
this.pendingWrites.push({
|
||||
path,
|
||||
data: s,
|
||||
@@ -1406,7 +1482,7 @@ async function bareAwkRun(program, opts, io) {
|
||||
rule.pattern.k === 'BEGIN'
|
||||
) {
|
||||
for (const st of rule.stmts) {
|
||||
const r = rt.execStmt(st)
|
||||
const r = await rt.execStmt(st)
|
||||
await rt.flushWrites()
|
||||
if (r?.t === 'exit') return rt.exitCode
|
||||
}
|
||||
@@ -1432,9 +1508,9 @@ async function bareAwkRun(program, opts, io) {
|
||||
rt.splitFields()
|
||||
for (const rule of ast.rules) {
|
||||
if (rule.pattern && rule.pattern.t === 'pat') continue
|
||||
if (!rt.patternMatch(rule.pattern)) continue
|
||||
if (!(await rt.patternMatch(rule.pattern))) continue
|
||||
for (const st of rule.stmts) {
|
||||
const r = rt.execStmt(st)
|
||||
const r = await rt.execStmt(st)
|
||||
await rt.flushWrites()
|
||||
if (r?.t === 'next') break
|
||||
if (r?.t === 'nextfile') break lines
|
||||
@@ -1460,7 +1536,7 @@ async function bareAwkRun(program, opts, io) {
|
||||
for (const rule of ast.rules) {
|
||||
if (rule.pattern && rule.pattern.t === 'pat' && rule.pattern.k === 'END') {
|
||||
for (const st of rule.stmts) {
|
||||
rt.execStmt(st)
|
||||
await rt.execStmt(st)
|
||||
await rt.flushWrites()
|
||||
}
|
||||
}
|
||||
|
||||
+23
-3
@@ -91,7 +91,7 @@ async function run(ctx, argv) {
|
||||
const tokens = argv.slice(1)
|
||||
if (!tokens.length || tokens[0] === '--help' || tokens[0] === '-h') {
|
||||
ctx.console.log(
|
||||
'usage: expr EXPRESSION\nInteger arithmetic (+ - * / %), comparisons, and string = / !=.'
|
||||
'usage: expr EXPRESSION\nInteger arithmetic (+ - * / %), comparisons, string = / !=, and POSIX : (regex match length; pattern is ECMA ^(?:…))'
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -108,11 +108,31 @@ async function run(ctx, argv) {
|
||||
if (/^-?\d+$/.test(t)) return { kind: 'n', v: parseInt(t, 10) }
|
||||
return { kind: 's', v: t }
|
||||
}
|
||||
function parseMul() {
|
||||
/** POSIX-style `:` regex match length (pattern is ECMA RegExp body after ^). */
|
||||
function parseColon() {
|
||||
let left = parsePrimary()
|
||||
while (peek() === ':') {
|
||||
take()
|
||||
const right = parsePrimary()
|
||||
const s = left.kind === 'n' ? String(left.v) : String(left.v)
|
||||
const pat = right.kind === 'n' ? String(right.v) : String(right.v)
|
||||
let n = 0
|
||||
try {
|
||||
const re = new RegExp('^(?:' + pat + ')')
|
||||
const m = re.exec(s)
|
||||
if (m) n = m[0].length
|
||||
} catch (_) {
|
||||
n = 0
|
||||
}
|
||||
left = { kind: 'n', v: n }
|
||||
}
|
||||
return left
|
||||
}
|
||||
function parseMul() {
|
||||
let left = parseColon()
|
||||
while (peek() === '*' || peek() === '/' || peek() === '%') {
|
||||
const op = take()
|
||||
const right = parsePrimary()
|
||||
const right = parseColon()
|
||||
if (left.kind !== 'n' || right.kind !== 'n') throw new Error('non-numeric')
|
||||
if (op === '*') left = { kind: 'n', v: left.v * right.v }
|
||||
else if (op === '/') {
|
||||
|
||||
@@ -172,5 +172,10 @@ async function run(ctx, argv) {
|
||||
return
|
||||
}
|
||||
ctx.console.error('pathcap-verify: FAIL ' + r.reason)
|
||||
if (trusted) {
|
||||
ctx.console.error(
|
||||
'pathcap-verify: hint: extend BARE_OS_PATH_CAPABILITY_TRUSTED_PUBKEYS_HEX with the issuer Ed25519 pubkey (64 hex) when the envelope is otherwise well-formed.'
|
||||
)
|
||||
}
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
|
||||
@@ -191,6 +191,27 @@ ${topic ? 'Topic pin prefix: ' + topic.slice(0, 16) + '…' : 'Topic pin unset.'
|
||||
return
|
||||
}
|
||||
const r = await pkgIndexLookup(ctx, keyArg)
|
||||
const ent =
|
||||
r && typeof r === 'object' && r.entry && typeof r.entry === 'object'
|
||||
? r.entry
|
||||
: null
|
||||
if (
|
||||
ent &&
|
||||
ent.pathCapabilityEnvelope != null &&
|
||||
typeof ctx.bareOsVerifyPathCapabilityEnvelope === 'function'
|
||||
) {
|
||||
try {
|
||||
r.pathCapabilityEnvelopeVerify =
|
||||
ctx.bareOsVerifyPathCapabilityEnvelope(
|
||||
ent.pathCapabilityEnvelope
|
||||
)
|
||||
} catch (e) {
|
||||
r.pathCapabilityEnvelopeVerify = {
|
||||
ok: false,
|
||||
error: (e && e.message) || String(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.console.log(JSON.stringify(r, null, 2))
|
||||
if (!r.ok) ctx.exitCode = 1
|
||||
return
|
||||
|
||||
@@ -156,6 +156,25 @@ async function evalTest(ctx, args) {
|
||||
}
|
||||
if (op === '=') return a === b
|
||||
if (op === '!=') return a !== b
|
||||
if (op === '-nt' || op === '-ot' || op === '-ef') {
|
||||
const s1 = await ctx.vfs.stat(a)
|
||||
const s2 = await ctx.vfs.stat(b)
|
||||
if (!s1 || !s2) return false
|
||||
if (op === '-ef') {
|
||||
const i1 = s1.ino != null ? String(s1.ino) : ''
|
||||
const i2 = s2.ino != null ? String(s2.ino) : ''
|
||||
const d1 = s1.dev != null ? String(s1.dev) : ''
|
||||
const d2 = s2.dev != null ? String(s2.dev) : ''
|
||||
if (i1 && i2 && d1 && d2) return i1 === i2 && d1 === d2
|
||||
return a === b
|
||||
}
|
||||
const t1 = Number(s1.mtimeMs)
|
||||
const t2 = Number(s2.mtimeMs)
|
||||
const m1 = Number.isFinite(t1) ? t1 : 0
|
||||
const m2 = Number.isFinite(t2) ? t2 : 0
|
||||
if (op === '-nt') return m1 > m2
|
||||
if (op === '-ot') return m1 < m2
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (args.length === 2) {
|
||||
|
||||
Reference in New Issue
Block a user