typescript-virtual-container 1.5.5 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -35
- package/dist/.tsbuildinfo +1 -1
- package/dist/SSHMimic/index.d.ts +5 -1
- package/dist/SSHMimic/index.js +27 -3
- package/dist/SSHMimic/scp.d.ts +34 -0
- package/dist/SSHMimic/scp.js +285 -0
- package/dist/SSHMimic/sftp.d.ts +53 -3
- package/dist/SSHMimic/sftp.js +9 -3
- package/dist/VirtualFileSystem/binaryPack.d.ts +7 -0
- package/dist/VirtualFileSystem/binaryPack.js +37 -1
- package/dist/VirtualFileSystem/index.d.ts +7 -0
- package/dist/VirtualFileSystem/index.js +67 -27
- package/dist/VirtualFileSystem/internalTypes.d.ts +2 -0
- package/dist/VirtualFileSystem/path.d.ts +5 -0
- package/dist/VirtualFileSystem/path.js +24 -11
- package/dist/VirtualPackageManager/index.d.ts +4 -2
- package/dist/VirtualPackageManager/index.js +24 -4
- package/dist/VirtualShell/index.d.ts +4 -0
- package/dist/VirtualShell/index.js +1 -7
- package/dist/VirtualShell/shell.js +40 -10
- package/dist/VirtualShell/shellParser.js +1 -22
- package/dist/commands/awk.d.ts +6 -11
- package/dist/commands/awk.js +462 -109
- package/dist/commands/bzip2.d.ts +11 -0
- package/dist/commands/bzip2.js +91 -0
- package/dist/commands/exit.js +1 -1
- package/dist/commands/find.d.ts +2 -2
- package/dist/commands/find.js +209 -37
- package/dist/commands/helpers.d.ts +0 -20
- package/dist/commands/helpers.js +0 -97
- package/dist/commands/lsof.d.ts +6 -0
- package/dist/commands/lsof.js +30 -0
- package/dist/commands/perl.d.ts +6 -0
- package/dist/commands/perl.js +76 -0
- package/dist/commands/python.js +5 -2
- package/dist/commands/registry.js +19 -1
- package/dist/commands/runtime.js +65 -87
- package/dist/commands/sed.d.ts +2 -2
- package/dist/commands/sed.js +216 -34
- package/dist/commands/sh.js +42 -0
- package/dist/commands/strace.d.ts +6 -0
- package/dist/commands/strace.js +26 -0
- package/dist/commands/tar.d.ts +2 -1
- package/dist/commands/tar.js +138 -52
- package/dist/commands/test.js +2 -2
- package/dist/commands/zip.d.ts +11 -0
- package/dist/commands/zip.js +232 -0
- package/dist/modules/linuxRootfs.js +1 -4
- package/dist/modules/neofetch.js +2 -2
- package/dist/types/commands.d.ts +4 -0
- package/dist/utils/argv.d.ts +6 -0
- package/dist/utils/argv.js +32 -0
- package/dist/utils/expand.d.ts +5 -2
- package/dist/utils/expand.js +112 -45
- package/dist/utils/glob.d.ts +6 -0
- package/dist/utils/glob.js +34 -0
- package/dist/utils/tokenize.js +13 -13
- package/package.json +9 -7
- package/dist/self-standalone.d.ts +0 -1
- package/dist/self-standalone.js +0 -444
- package/dist/standalone-wo-sftp.d.ts +0 -1
- package/dist/standalone-wo-sftp.js +0 -30
- package/dist/standalone.d.ts +0 -1
- package/dist/standalone.js +0 -61
package/dist/commands/awk.js
CHANGED
|
@@ -1,28 +1,53 @@
|
|
|
1
|
-
import { getFlag } from "./command-helpers";
|
|
2
1
|
import { assertPathAccess, resolvePath } from "./helpers";
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - `/regex/` pattern
|
|
10
|
-
* - `{ print $N, $M, ... }` action
|
|
11
|
-
* - `{ print }` / `{ print $0 }`
|
|
12
|
-
* - `BEGIN { ... }` and `END { ... }` blocks (no side effects)
|
|
13
|
-
* - `$NF` (last field)
|
|
14
|
-
* - `-F sep` field separator
|
|
3
|
+
* AWK pattern scanning — supports BEGIN/END, /regex/, NR/NF conditions, field ops,
|
|
4
|
+
* variable assignment (-v), arithmetic, string functions (sub/gsub/substr/split/length/index/sprintf/tolower/toupper/match),
|
|
5
|
+
* printf, getline (from stdin), next, and multi-statement blocks.
|
|
6
|
+
* @category text
|
|
7
|
+
* @params ["[-F sep] [-v var=val] '<program>' [file]"]
|
|
15
8
|
*/
|
|
16
9
|
export const awkCommand = {
|
|
17
10
|
name: "awk",
|
|
18
11
|
description: "Pattern scanning and processing language",
|
|
19
12
|
category: "text",
|
|
20
|
-
params: ["[-F
|
|
13
|
+
params: ["[-F sep] [-v var=val] '<program>' [file]"],
|
|
21
14
|
run: ({ authUser, args, stdin, cwd, shell }) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const
|
|
15
|
+
// Parse flags
|
|
16
|
+
let sep = " ";
|
|
17
|
+
const initVars = {};
|
|
18
|
+
const positionals = [];
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < args.length) {
|
|
21
|
+
const a = args[i];
|
|
22
|
+
if (a === "-F") {
|
|
23
|
+
sep = args[++i] ?? " ";
|
|
24
|
+
i++;
|
|
25
|
+
}
|
|
26
|
+
else if (a.startsWith("-F")) {
|
|
27
|
+
sep = a.slice(2);
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
else if (a === "-v") {
|
|
31
|
+
const kv = args[++i] ?? "";
|
|
32
|
+
const eq = kv.indexOf("=");
|
|
33
|
+
if (eq !== -1)
|
|
34
|
+
initVars[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
else if (a.startsWith("-v")) {
|
|
38
|
+
const kv = a.slice(2);
|
|
39
|
+
const eq = kv.indexOf("=");
|
|
40
|
+
if (eq !== -1)
|
|
41
|
+
initVars[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
positionals.push(a);
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const prog = positionals[0];
|
|
50
|
+
const fileArg = positionals[1];
|
|
26
51
|
if (!prog)
|
|
27
52
|
return { stderr: "awk: no program", exitCode: 1 };
|
|
28
53
|
let input = stdin ?? "";
|
|
@@ -36,100 +61,395 @@ export const awkCommand = {
|
|
|
36
61
|
return { stderr: `awk: ${fileArg}: No such file or directory`, exitCode: 1 };
|
|
37
62
|
}
|
|
38
63
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
function numVal(v) {
|
|
65
|
+
if (v === undefined || v === "")
|
|
66
|
+
return 0;
|
|
67
|
+
const n = Number(v);
|
|
68
|
+
return Number.isNaN(n) ? 0 : n;
|
|
69
|
+
}
|
|
70
|
+
function strVal(v) {
|
|
71
|
+
if (v === undefined)
|
|
72
|
+
return "";
|
|
73
|
+
return String(v);
|
|
48
74
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
75
|
+
function splitFields(line, fs) {
|
|
76
|
+
if (fs === " ")
|
|
77
|
+
return line.trim().split(/\s+/).filter(Boolean);
|
|
78
|
+
if (fs.length === 1)
|
|
79
|
+
return line.split(fs);
|
|
80
|
+
return line.split(new RegExp(fs));
|
|
81
|
+
}
|
|
82
|
+
// Evaluate an AWK expression string with given context
|
|
83
|
+
function evalExpr(expr, vars, fields, nr, nf) {
|
|
84
|
+
expr = expr.trim();
|
|
85
|
+
if (expr === "")
|
|
86
|
+
return "";
|
|
87
|
+
// String literal
|
|
88
|
+
if (expr.startsWith('"') && expr.endsWith('"'))
|
|
89
|
+
return expr.slice(1, -1).replace(/\\n/g, "\n").replace(/\\t/g, "\t");
|
|
90
|
+
// Numeric literal
|
|
91
|
+
if (/^-?\d+(\.\d+)?$/.test(expr))
|
|
92
|
+
return parseFloat(expr);
|
|
93
|
+
// $0, $N, $NF
|
|
94
|
+
if (expr === "$0")
|
|
95
|
+
return fields.join(sep === " " ? " " : sep) || "";
|
|
96
|
+
if (expr === "$NF")
|
|
97
|
+
return fields[nf - 1] ?? "";
|
|
98
|
+
if (/^\$\d+$/.test(expr))
|
|
99
|
+
return fields[parseInt(expr.slice(1), 10) - 1] ?? "";
|
|
100
|
+
if (/^\$/.test(expr)) {
|
|
101
|
+
const inner = expr.slice(1);
|
|
102
|
+
const idx = numVal(evalExpr(inner, vars, fields, nr, nf));
|
|
103
|
+
if (idx === 0)
|
|
104
|
+
return fields.join(sep === " " ? " " : sep) || "";
|
|
105
|
+
return fields[idx - 1] ?? "";
|
|
56
106
|
}
|
|
57
|
-
|
|
58
|
-
|
|
107
|
+
// NR, NF
|
|
108
|
+
if (expr === "NR")
|
|
109
|
+
return nr;
|
|
110
|
+
if (expr === "NF")
|
|
111
|
+
return nf;
|
|
112
|
+
// Variable
|
|
113
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(expr))
|
|
114
|
+
return vars[expr] ?? "";
|
|
115
|
+
// String functions
|
|
116
|
+
const lenM = expr.match(/^length\s*\(([^)]*)\)$/);
|
|
117
|
+
if (lenM)
|
|
118
|
+
return strVal(evalExpr(lenM[1].trim(), vars, fields, nr, nf)).length;
|
|
119
|
+
const substrM = expr.match(/^substr\s*\((.+)\)$/);
|
|
120
|
+
if (substrM) {
|
|
121
|
+
const parts2 = splitCSV(substrM[1]);
|
|
122
|
+
const s = strVal(evalExpr(parts2[0]?.trim() ?? "", vars, fields, nr, nf));
|
|
123
|
+
const start = numVal(evalExpr(parts2[1]?.trim() ?? "1", vars, fields, nr, nf)) - 1;
|
|
124
|
+
const len2 = parts2[2] !== undefined ? numVal(evalExpr(parts2[2].trim(), vars, fields, nr, nf)) : undefined;
|
|
125
|
+
return len2 !== undefined ? s.slice(Math.max(0, start), start + len2) : s.slice(Math.max(0, start));
|
|
59
126
|
}
|
|
127
|
+
const indexM = expr.match(/^index\s*\((.+)\)$/);
|
|
128
|
+
if (indexM) {
|
|
129
|
+
const parts2 = splitCSV(indexM[1]);
|
|
130
|
+
const s = strVal(evalExpr(parts2[0]?.trim() ?? "", vars, fields, nr, nf));
|
|
131
|
+
const t = strVal(evalExpr(parts2[1]?.trim() ?? "", vars, fields, nr, nf));
|
|
132
|
+
return s.indexOf(t) + 1;
|
|
133
|
+
}
|
|
134
|
+
const tolowerM = expr.match(/^tolower\s*\((.+)\)$/);
|
|
135
|
+
if (tolowerM)
|
|
136
|
+
return strVal(evalExpr(tolowerM[1].trim(), vars, fields, nr, nf)).toLowerCase();
|
|
137
|
+
const toupperM = expr.match(/^toupper\s*\((.+)\)$/);
|
|
138
|
+
if (toupperM)
|
|
139
|
+
return strVal(evalExpr(toupperM[1].trim(), vars, fields, nr, nf)).toUpperCase();
|
|
140
|
+
const matchM = expr.match(/^match\s*\((.+),\s*\/(.+)\/\)$/);
|
|
141
|
+
if (matchM) {
|
|
142
|
+
const s = strVal(evalExpr(matchM[1].trim(), vars, fields, nr, nf));
|
|
143
|
+
try {
|
|
144
|
+
const m = s.match(new RegExp(matchM[2]));
|
|
145
|
+
if (m) {
|
|
146
|
+
vars.RSTART = (m.index ?? 0) + 1;
|
|
147
|
+
vars.RLENGTH = m[0].length;
|
|
148
|
+
return (m.index ?? 0) + 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch { /* ignore */ }
|
|
152
|
+
vars.RSTART = 0;
|
|
153
|
+
vars.RLENGTH = -1;
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
// Ternary: a ? b : c
|
|
157
|
+
const ternM = expr.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
|
|
158
|
+
if (ternM) {
|
|
159
|
+
const cond = evalExpr(ternM[1].trim(), vars, fields, nr, nf);
|
|
160
|
+
return numVal(cond) !== 0 || (typeof cond === "string" && cond !== "")
|
|
161
|
+
? evalExpr(ternM[2].trim(), vars, fields, nr, nf)
|
|
162
|
+
: evalExpr(ternM[3].trim(), vars, fields, nr, nf);
|
|
163
|
+
}
|
|
164
|
+
// String concatenation (space between two quoted/var exprs)
|
|
165
|
+
const concatM = expr.match(/^((?:"[^"]*"|[A-Za-z_$][A-Za-z0-9_$]*|\d+))\s+((?:"[^"]*"|[A-Za-z_$][A-Za-z0-9_$]*|\d+))$/);
|
|
166
|
+
if (concatM) {
|
|
167
|
+
return strVal(evalExpr(concatM[1], vars, fields, nr, nf)) + strVal(evalExpr(concatM[2], vars, fields, nr, nf));
|
|
168
|
+
}
|
|
169
|
+
// Arithmetic / comparison — substitute known tokens and eval
|
|
170
|
+
try {
|
|
171
|
+
const subst = expr
|
|
172
|
+
.replace(/\bNR\b/g, String(nr))
|
|
173
|
+
.replace(/\bNF\b/g, String(nf))
|
|
174
|
+
.replace(/\$NF\b/g, String(nf > 0 ? numVal(fields[nf - 1]) : 0))
|
|
175
|
+
.replace(/\$(\d+)/g, (_, n) => String(numVal(fields[parseInt(n, 10) - 1])))
|
|
176
|
+
.replace(/\b([A-Za-z_][A-Za-z0-9_]*)\b/g, (_, v) => String(numVal(vars[v])));
|
|
177
|
+
const result = Function(`"use strict"; return (${subst});`)();
|
|
178
|
+
if (typeof result === "number" || typeof result === "boolean")
|
|
179
|
+
return Number(result);
|
|
180
|
+
}
|
|
181
|
+
catch { /* fall through */ }
|
|
182
|
+
return strVal(vars[expr] ?? expr);
|
|
60
183
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
184
|
+
function splitCSV(s) {
|
|
185
|
+
const parts = [];
|
|
186
|
+
let cur = "";
|
|
187
|
+
let depth = 0;
|
|
188
|
+
for (let ci = 0; ci < s.length; ci++) {
|
|
189
|
+
const c = s[ci];
|
|
190
|
+
if (c === "(")
|
|
191
|
+
depth++;
|
|
192
|
+
else if (c === ")")
|
|
193
|
+
depth--;
|
|
194
|
+
else if (c === "," && depth === 0) {
|
|
195
|
+
parts.push(cur);
|
|
196
|
+
cur = "";
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
cur += c;
|
|
200
|
+
}
|
|
201
|
+
parts.push(cur);
|
|
202
|
+
return parts;
|
|
70
203
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
204
|
+
// Execute one statement, return false to stop (next/exit)
|
|
205
|
+
function execStmt(stmt, vars, fields, nr, nf, out) {
|
|
206
|
+
stmt = stmt.trim();
|
|
207
|
+
if (!stmt || stmt.startsWith("#"))
|
|
208
|
+
return "ok";
|
|
209
|
+
// next / exit
|
|
210
|
+
if (stmt === "next")
|
|
211
|
+
return "next";
|
|
212
|
+
if (stmt === "exit" || stmt.startsWith("exit "))
|
|
213
|
+
return "exit";
|
|
214
|
+
// print / printf
|
|
215
|
+
if (stmt === "print" || stmt === "print $0") {
|
|
216
|
+
out.push(fields.join(sep === " " ? " " : sep));
|
|
217
|
+
return "ok";
|
|
218
|
+
}
|
|
219
|
+
if (stmt.startsWith("printf ")) {
|
|
220
|
+
const fmtRest = stmt.slice(7).trim();
|
|
221
|
+
out.push(sprintfAWK(fmtRest, vars, fields, nr, nf));
|
|
222
|
+
return "ok";
|
|
223
|
+
}
|
|
224
|
+
if (stmt.startsWith("print ")) {
|
|
225
|
+
const argStr = stmt.slice(6);
|
|
226
|
+
const parts2 = splitCSV(argStr);
|
|
227
|
+
out.push(parts2.map((p) => strVal(evalExpr(p.trim(), vars, fields, nr, nf))).join("\t"));
|
|
228
|
+
return "ok";
|
|
229
|
+
}
|
|
230
|
+
// delete var / delete arr[k]
|
|
231
|
+
if (stmt.startsWith("delete ")) {
|
|
232
|
+
const key = stmt.slice(7).trim();
|
|
233
|
+
delete vars[key];
|
|
234
|
+
return "ok";
|
|
235
|
+
}
|
|
236
|
+
// sub(re, rep) / sub(re, rep, target) and gsub
|
|
237
|
+
const subM = stmt.match(/^(g?sub)\s*\(\s*\/([^/]*)\//);
|
|
238
|
+
if (subM) {
|
|
239
|
+
const global2 = subM[1] === "gsub";
|
|
240
|
+
const reStr = subM[2];
|
|
241
|
+
const rest2 = stmt.slice(subM[0].length).replace(/^\s*,\s*/, "");
|
|
242
|
+
const parts2 = splitCSV(rest2.replace(/\)\s*$/, ""));
|
|
243
|
+
const rep = strVal(evalExpr(parts2[0]?.trim() ?? '""', vars, fields, nr, nf));
|
|
244
|
+
// target: if 3rd arg is $N, modify field; else modify $0
|
|
245
|
+
const target = parts2[1]?.trim();
|
|
246
|
+
const wholeRec = fields.join(sep === " " ? " " : sep);
|
|
247
|
+
try {
|
|
248
|
+
const re2 = new RegExp(reStr, global2 ? "g" : "");
|
|
249
|
+
if (target && /^\$\d+$/.test(target)) {
|
|
250
|
+
const idx = parseInt(target.slice(1), 10) - 1;
|
|
251
|
+
if (idx >= 0 && idx < fields.length)
|
|
252
|
+
fields[idx] = (fields[idx] ?? "").replace(re2, rep);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const replaced = wholeRec.replace(re2, rep);
|
|
256
|
+
const newFields = splitFields(replaced, sep);
|
|
257
|
+
fields.splice(0, fields.length, ...newFields);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch { /* ignore */ }
|
|
261
|
+
return "ok";
|
|
262
|
+
}
|
|
263
|
+
// split(str, arr, sep)
|
|
264
|
+
const splitM = stmt.match(/^split\s*\((.+)\)$/);
|
|
265
|
+
if (splitM) {
|
|
266
|
+
const parts2 = splitCSV(splitM[1]);
|
|
267
|
+
const s = strVal(evalExpr(parts2[0]?.trim() ?? "", vars, fields, nr, nf));
|
|
268
|
+
const arrName = parts2[1]?.trim() ?? "arr";
|
|
269
|
+
const fs2 = parts2[2] ? strVal(evalExpr(parts2[2].trim(), vars, fields, nr, nf)) : sep;
|
|
270
|
+
const elems = splitFields(s, fs2);
|
|
271
|
+
for (let ei = 0; ei < elems.length; ei++)
|
|
272
|
+
vars[`${arrName}[${ei + 1}]`] = elems[ei] ?? "";
|
|
273
|
+
vars[arrName] = String(elems.length);
|
|
274
|
+
return "ok";
|
|
275
|
+
}
|
|
276
|
+
// var++ / var--
|
|
277
|
+
const incM = stmt.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*(\+\+|--)$/);
|
|
278
|
+
if (incM) {
|
|
279
|
+
vars[incM[1]] = numVal(vars[incM[1]]) + (incM[2] === "++" ? 1 : -1);
|
|
280
|
+
return "ok";
|
|
281
|
+
}
|
|
282
|
+
// var += / -= / *= / /= / %= expr
|
|
283
|
+
const assignOpM = stmt.match(/^([A-Za-z_][A-Za-z0-9_[\]]*)\s*(\+=|-=|\*=|\/=|%=)\s*(.+)$/);
|
|
284
|
+
if (assignOpM) {
|
|
285
|
+
const cur = numVal(vars[assignOpM[1]]);
|
|
286
|
+
const rhs = numVal(evalExpr(assignOpM[3], vars, fields, nr, nf));
|
|
287
|
+
const op2 = assignOpM[2];
|
|
288
|
+
let res = cur;
|
|
289
|
+
if (op2 === "+=")
|
|
290
|
+
res = cur + rhs;
|
|
291
|
+
else if (op2 === "-=")
|
|
292
|
+
res = cur - rhs;
|
|
293
|
+
else if (op2 === "*=")
|
|
294
|
+
res = cur * rhs;
|
|
295
|
+
else if (op2 === "/=")
|
|
296
|
+
res = rhs !== 0 ? cur / rhs : 0;
|
|
297
|
+
else if (op2 === "%=")
|
|
298
|
+
res = cur % rhs;
|
|
299
|
+
vars[assignOpM[1]] = res;
|
|
300
|
+
return "ok";
|
|
301
|
+
}
|
|
302
|
+
// var = expr (assignment)
|
|
303
|
+
const assignM = stmt.match(/^([A-Za-z_][A-Za-z0-9_[\]]*)\s*=\s*(.+)$/);
|
|
304
|
+
if (assignM) {
|
|
305
|
+
vars[assignM[1]] = evalExpr(assignM[2], vars, fields, nr, nf);
|
|
306
|
+
return "ok";
|
|
307
|
+
}
|
|
308
|
+
// Fallthrough: try as expression (e.g. bare function call)
|
|
309
|
+
evalExpr(stmt, vars, fields, nr, nf);
|
|
310
|
+
return "ok";
|
|
311
|
+
}
|
|
312
|
+
function sprintfAWK(fmtStr, vars, fields, nr, nf) {
|
|
313
|
+
const parts2 = splitCSV(fmtStr);
|
|
314
|
+
const fmt = strVal(evalExpr(parts2[0]?.trim() ?? '""', vars, fields, nr, nf));
|
|
315
|
+
const fmtArgs = parts2.slice(1).map((p) => evalExpr(p.trim(), vars, fields, nr, nf));
|
|
316
|
+
let ai = 0;
|
|
317
|
+
return fmt.replace(/%(-?\d*\.?\d*)?([diouxXeEfgGsq%])/g, (_, spec, type2) => {
|
|
318
|
+
if (type2 === "%")
|
|
319
|
+
return "%";
|
|
320
|
+
const val2 = fmtArgs[ai++];
|
|
321
|
+
const width = spec ? parseInt(spec, 10) : 0;
|
|
322
|
+
let result = "";
|
|
323
|
+
if (type2 === "d" || type2 === "i")
|
|
324
|
+
result = String(Math.trunc(numVal(val2)));
|
|
325
|
+
else if (type2 === "f")
|
|
326
|
+
result = numVal(val2).toFixed(spec?.includes(".") ? parseInt(spec.split(".")[1] ?? "6", 10) : 6);
|
|
327
|
+
else if (type2 === "s" || type2 === "q")
|
|
328
|
+
result = strVal(val2);
|
|
329
|
+
else if (type2 === "x")
|
|
330
|
+
result = Math.trunc(numVal(val2)).toString(16);
|
|
331
|
+
else if (type2 === "X")
|
|
332
|
+
result = Math.trunc(numVal(val2)).toString(16).toUpperCase();
|
|
333
|
+
else if (type2 === "o")
|
|
334
|
+
result = Math.trunc(numVal(val2)).toString(8);
|
|
335
|
+
else
|
|
336
|
+
result = strVal(val2);
|
|
337
|
+
if (width > 0 && result.length < width)
|
|
338
|
+
result = result.padStart(width);
|
|
339
|
+
else if (width < 0 && result.length < -width)
|
|
340
|
+
result = result.padEnd(-width);
|
|
341
|
+
return result;
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
const clauses = [];
|
|
345
|
+
const progTrim = prog.trim();
|
|
346
|
+
// Split top-level { } blocks respecting nesting and strings
|
|
347
|
+
{
|
|
348
|
+
let j = 0;
|
|
349
|
+
while (j < progTrim.length) {
|
|
350
|
+
// Skip whitespace
|
|
351
|
+
while (j < progTrim.length && /\s/.test(progTrim[j]))
|
|
352
|
+
j++;
|
|
353
|
+
if (j >= progTrim.length)
|
|
354
|
+
break;
|
|
355
|
+
// Collect pattern (everything before `{`)
|
|
356
|
+
let pat = "";
|
|
357
|
+
while (j < progTrim.length && progTrim[j] !== "{") {
|
|
358
|
+
pat += progTrim[j++];
|
|
359
|
+
}
|
|
360
|
+
pat = pat.trim();
|
|
361
|
+
if (progTrim[j] !== "{") {
|
|
362
|
+
if (pat)
|
|
363
|
+
clauses.push({ pattern: pat, action: "print $0" });
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
j++; // skip {
|
|
367
|
+
// Collect action (balanced braces)
|
|
368
|
+
let action = "";
|
|
369
|
+
let depth = 1;
|
|
370
|
+
while (j < progTrim.length && depth > 0) {
|
|
371
|
+
const c = progTrim[j];
|
|
372
|
+
if (c === "{")
|
|
373
|
+
depth++;
|
|
374
|
+
else if (c === "}") {
|
|
375
|
+
depth--;
|
|
376
|
+
if (depth === 0) {
|
|
377
|
+
j++;
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
92
380
|
}
|
|
93
|
-
|
|
381
|
+
action += c;
|
|
382
|
+
j++;
|
|
94
383
|
}
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
384
|
+
clauses.push({ pattern: pat, action: action.trim() });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (clauses.length === 0)
|
|
388
|
+
clauses.push({ pattern: "", action: progTrim.replace(/[{}]/g, "").trim() });
|
|
389
|
+
// ── Execute ────────────────────────────────────────────────────────────
|
|
390
|
+
const out = [];
|
|
391
|
+
const vars = { FS: sep, OFS: sep === " " ? " " : sep, ORS: "\n", ...initVars };
|
|
392
|
+
const beginClauses = clauses.filter((c) => c.pattern === "BEGIN");
|
|
393
|
+
const endClauses = clauses.filter((c) => c.pattern === "END");
|
|
394
|
+
const mainClauses = clauses.filter((c) => c.pattern !== "BEGIN" && c.pattern !== "END");
|
|
395
|
+
function runAction(action, fields, nr, nf) {
|
|
396
|
+
// Split action into statements by ; or newline (not inside strings/parens)
|
|
397
|
+
const stmts = splitStmts(action);
|
|
98
398
|
for (const stmt of stmts) {
|
|
99
|
-
|
|
100
|
-
|
|
399
|
+
const res = execStmt(stmt, vars, fields, nr, nf, out);
|
|
400
|
+
if (res !== "ok")
|
|
401
|
+
return res;
|
|
402
|
+
}
|
|
403
|
+
return "ok";
|
|
404
|
+
}
|
|
405
|
+
function splitStmts(action) {
|
|
406
|
+
const stmts = [];
|
|
407
|
+
let cur = "";
|
|
408
|
+
let depth = 0;
|
|
409
|
+
let inStr = false;
|
|
410
|
+
let strCh = "";
|
|
411
|
+
for (let ci = 0; ci < action.length; ci++) {
|
|
412
|
+
const c = action[ci];
|
|
413
|
+
if (!inStr && (c === '"' || c === "'")) {
|
|
414
|
+
inStr = true;
|
|
415
|
+
strCh = c;
|
|
416
|
+
cur += c;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (inStr && c === strCh) {
|
|
420
|
+
inStr = false;
|
|
421
|
+
cur += c;
|
|
422
|
+
continue;
|
|
101
423
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
424
|
+
if (inStr) {
|
|
425
|
+
cur += c;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (c === "(" || c === "[")
|
|
429
|
+
depth++;
|
|
430
|
+
else if (c === ")" || c === "]")
|
|
431
|
+
depth--;
|
|
432
|
+
if ((c === ";" || c === "\n") && depth === 0) {
|
|
433
|
+
if (cur.trim())
|
|
434
|
+
stmts.push(cur.trim());
|
|
435
|
+
cur = "";
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
cur += c;
|
|
105
439
|
}
|
|
106
440
|
}
|
|
441
|
+
if (cur.trim())
|
|
442
|
+
stmts.push(cur.trim());
|
|
443
|
+
return stmts;
|
|
107
444
|
}
|
|
108
|
-
function
|
|
445
|
+
function matchClause(pattern, line, fields, nr, nf) {
|
|
109
446
|
if (!pattern)
|
|
110
447
|
return true;
|
|
111
448
|
if (pattern === "1")
|
|
112
449
|
return true;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const op = nrCond[1];
|
|
117
|
-
const val = parseInt(nrCond[2], 10);
|
|
118
|
-
switch (op) {
|
|
119
|
-
case "==": return nr === val;
|
|
120
|
-
case "!=": return nr !== val;
|
|
121
|
-
case ">": return nr > val;
|
|
122
|
-
case ">=": return nr >= val;
|
|
123
|
-
case "<": return nr < val;
|
|
124
|
-
case "<=": return nr <= val;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// NR%N==M
|
|
128
|
-
const nrMod = pattern.match(/^NR%(\d+)==(\d+)$/);
|
|
129
|
-
if (nrMod) {
|
|
130
|
-
return nr % parseInt(nrMod[1], 10) === parseInt(nrMod[2], 10);
|
|
131
|
-
}
|
|
132
|
-
// /regex/ pattern
|
|
450
|
+
if (/^-?\d+$/.test(pattern))
|
|
451
|
+
return numVal(pattern) !== 0;
|
|
452
|
+
// /regex/
|
|
133
453
|
if (pattern.startsWith("/") && pattern.endsWith("/")) {
|
|
134
454
|
try {
|
|
135
455
|
return new RegExp(pattern.slice(1, -1)).test(line);
|
|
@@ -138,32 +458,65 @@ export const awkCommand = {
|
|
|
138
458
|
return false;
|
|
139
459
|
}
|
|
140
460
|
}
|
|
461
|
+
// NR/NF comparisons
|
|
462
|
+
const cmpM = pattern.match(/^(.+?)\s*([=!<>]=?|==)\s*(.+)$/);
|
|
463
|
+
if (cmpM) {
|
|
464
|
+
const lhs = numVal(evalExpr(cmpM[1].trim(), vars, fields, nr, nf));
|
|
465
|
+
const rhs = numVal(evalExpr(cmpM[3].trim(), vars, fields, nr, nf));
|
|
466
|
+
switch (cmpM[2]) {
|
|
467
|
+
case "==": return lhs === rhs;
|
|
468
|
+
case "!=": return lhs !== rhs;
|
|
469
|
+
case ">": return lhs > rhs;
|
|
470
|
+
case ">=": return lhs >= rhs;
|
|
471
|
+
case "<": return lhs < rhs;
|
|
472
|
+
case "<=": return lhs <= rhs;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
141
475
|
// $N~/regex/
|
|
142
|
-
const fieldMatch = pattern.match(/^\$(\
|
|
476
|
+
const fieldMatch = pattern.match(/^\$(\w+)\s*~\s*\/(.*)\/$/);
|
|
143
477
|
if (fieldMatch) {
|
|
144
|
-
const
|
|
145
|
-
const field = parts[parseInt(fieldMatch[1], 10) - 1] ?? "";
|
|
478
|
+
const fv = strVal(evalExpr(`$${fieldMatch[1]}`, vars, fields, nr, nf));
|
|
146
479
|
try {
|
|
147
|
-
return new RegExp(fieldMatch[2]).test(
|
|
480
|
+
return new RegExp(fieldMatch[2]).test(fv);
|
|
148
481
|
}
|
|
149
482
|
catch {
|
|
150
483
|
return false;
|
|
151
484
|
}
|
|
152
485
|
}
|
|
153
|
-
|
|
486
|
+
// Boolean expr via evalExpr
|
|
487
|
+
const v = evalExpr(pattern, vars, fields, nr, nf);
|
|
488
|
+
return numVal(v) !== 0 || (typeof v === "string" && v !== "");
|
|
154
489
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
490
|
+
// BEGIN
|
|
491
|
+
for (const c of beginClauses)
|
|
492
|
+
runAction(c.action, [], 0, 0);
|
|
493
|
+
const lines = input.split("\n");
|
|
494
|
+
if (lines[lines.length - 1] === "")
|
|
495
|
+
lines.pop();
|
|
496
|
+
let stopped = false;
|
|
497
|
+
for (let li = 0; li < lines.length && !stopped; li++) {
|
|
498
|
+
const line = lines[li];
|
|
499
|
+
vars.NR = li + 1;
|
|
500
|
+
const fields = splitFields(line, sep);
|
|
501
|
+
vars.NF = fields.length;
|
|
502
|
+
const nr = li + 1;
|
|
503
|
+
const nf = fields.length;
|
|
159
504
|
for (const clause of mainClauses) {
|
|
160
|
-
if (
|
|
161
|
-
|
|
505
|
+
if (!matchClause(clause.pattern, line, fields, nr, nf))
|
|
506
|
+
continue;
|
|
507
|
+
const res = runAction(clause.action, fields, nr, nf);
|
|
508
|
+
if (res === "next")
|
|
509
|
+
break;
|
|
510
|
+
if (res === "exit") {
|
|
511
|
+
stopped = true;
|
|
512
|
+
break;
|
|
162
513
|
}
|
|
163
514
|
}
|
|
164
515
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
516
|
+
// END
|
|
517
|
+
for (const c of endClauses)
|
|
518
|
+
runAction(c.action, [], numVal(vars.NR), 0);
|
|
519
|
+
const output = out.join("\n");
|
|
520
|
+
return { stdout: output + (output && !output.endsWith("\n") ? "\n" : ""), exitCode: 0 };
|
|
168
521
|
},
|
|
169
522
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ShellModule } from "../types/commands";
|
|
2
|
+
/**
|
|
3
|
+
* Compress files using bzip2 (VFS-internal gzip with BZ2 marker — round-trips within VM).
|
|
4
|
+
* @category archive
|
|
5
|
+
*/
|
|
6
|
+
export declare const bzip2Command: ShellModule;
|
|
7
|
+
/**
|
|
8
|
+
* Decompress bzip2 files (VFS-internal format).
|
|
9
|
+
* @category archive
|
|
10
|
+
*/
|
|
11
|
+
export declare const bunzip2Command: ShellModule;
|