@poe-platform/safe-bash 0.1.511 → 0.1.513

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.
Files changed (68) hide show
  1. package/dist/safe-bash/chunks/{chunk-5HT35GTV.js → chunk-5MSOBYRG.js} +2 -2
  2. package/dist/safe-bash/chunks/{chunk-B637PJBS.js → chunk-5OCQTFXT.js} +1 -1
  3. package/dist/safe-bash/chunks/{chunk-B637PJBS.js.map → chunk-5OCQTFXT.js.map} +2 -2
  4. package/dist/safe-bash/chunks/{chunk-4OKU6JXB.js → chunk-NYHAKXQ7.js} +2 -2
  5. package/dist/safe-bash/commands/cmp.d.ts +3 -0
  6. package/dist/safe-bash/commands/cmp.d.ts.map +1 -0
  7. package/dist/safe-bash/commands/cmp.js +595 -0
  8. package/dist/safe-bash/commands/cmp.js.map +1 -0
  9. package/dist/safe-bash/commands/fmt.d.ts +3 -0
  10. package/dist/safe-bash/commands/fmt.d.ts.map +1 -0
  11. package/dist/safe-bash/commands/fmt.js +839 -0
  12. package/dist/safe-bash/commands/fmt.js.map +1 -0
  13. package/dist/safe-bash/commands/metadata/index.d.ts.map +1 -1
  14. package/dist/safe-bash/commands/metadata/index.js +2 -1
  15. package/dist/safe-bash/commands/metadata/index.js.map +1 -1
  16. package/dist/safe-bash/commands/numfmt.d.ts +3 -0
  17. package/dist/safe-bash/commands/numfmt.d.ts.map +1 -0
  18. package/dist/safe-bash/commands/numfmt.js +1180 -0
  19. package/dist/safe-bash/commands/numfmt.js.map +1 -0
  20. package/dist/safe-bash/commands/regex-execution/bounded-provider.d.ts +1 -1
  21. package/dist/safe-bash/commands/regex-execution/bounded-provider.d.ts.map +1 -1
  22. package/dist/safe-bash/commands/regex-execution/bounded-provider.js +7 -35
  23. package/dist/safe-bash/commands/regex-execution/bounded-provider.js.map +1 -1
  24. package/dist/safe-bash/commands/regex-execution/ere/matcher.d.ts +2 -0
  25. package/dist/safe-bash/commands/regex-execution/ere/matcher.d.ts.map +1 -1
  26. package/dist/safe-bash/commands/regex-execution/ere/matcher.js +55 -1
  27. package/dist/safe-bash/commands/regex-execution/ere/matcher.js.map +1 -1
  28. package/dist/safe-bash/commands/regex-execution/ere/syntax.js +1 -1
  29. package/dist/safe-bash/commands/regex-execution/ere/syntax.js.map +1 -1
  30. package/dist/safe-bash/commands/regex-execution/ere/types.d.ts +1 -0
  31. package/dist/safe-bash/commands/regex-execution/ere/types.d.ts.map +1 -1
  32. package/dist/safe-bash/commands/regex-execution/utf8.d.ts +3 -0
  33. package/dist/safe-bash/commands/regex-execution/utf8.d.ts.map +1 -0
  34. package/dist/safe-bash/commands/regex-execution/utf8.js +24 -0
  35. package/dist/safe-bash/commands/regex-execution/utf8.js.map +1 -0
  36. package/dist/safe-bash/commands/shuf.d.ts +3 -0
  37. package/dist/safe-bash/commands/shuf.d.ts.map +1 -0
  38. package/dist/safe-bash/commands/shuf.js +768 -0
  39. package/dist/safe-bash/commands/shuf.js.map +1 -0
  40. package/dist/safe-bash/commands/standard.d.ts.map +1 -1
  41. package/dist/safe-bash/commands/standard.js +5 -0
  42. package/dist/safe-bash/commands/standard.js.map +1 -1
  43. package/dist/safe-bash/commands/truncate.d.ts +4 -0
  44. package/dist/safe-bash/commands/truncate.d.ts.map +1 -0
  45. package/dist/safe-bash/commands/truncate.js +668 -0
  46. package/dist/safe-bash/commands/truncate.js.map +1 -0
  47. package/dist/safe-bash/commands/which/which.d.ts.map +1 -1
  48. package/dist/safe-bash/commands/which/which.js +1 -0
  49. package/dist/safe-bash/commands/which/which.js.map +1 -1
  50. package/dist/safe-bash/commands/xml/index.browser.js +2 -2
  51. package/dist/safe-bash/commands/yq/index.browser.js +2 -2
  52. package/dist/safe-bash/contracts/command.d.ts +11 -1
  53. package/dist/safe-bash/contracts/command.d.ts.map +1 -1
  54. package/dist/safe-bash/contracts/command.js.map +1 -1
  55. package/dist/safe-bash/contracts/filesystem.d.ts +1 -1
  56. package/dist/safe-bash/contracts/filesystem.d.ts.map +1 -1
  57. package/dist/safe-bash/core.browser.js +9481 -789
  58. package/dist/safe-bash/core.browser.js.map +4 -4
  59. package/dist/safe-bash/shell/input.d.ts +15 -4
  60. package/dist/safe-bash/shell/input.d.ts.map +1 -1
  61. package/dist/safe-bash/shell/input.js +211 -9
  62. package/dist/safe-bash/shell/input.js.map +1 -1
  63. package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
  64. package/dist/safe-bash/shell/runtime.js +16 -4
  65. package/dist/safe-bash/shell/runtime.js.map +1 -1
  66. package/package.json +2 -2
  67. /package/dist/safe-bash/chunks/{chunk-5HT35GTV.js.map → chunk-5MSOBYRG.js.map} +0 -0
  68. /package/dist/safe-bash/chunks/{chunk-4OKU6JXB.js.map → chunk-NYHAKXQ7.js.map} +0 -0
@@ -32,7 +32,7 @@ import {
32
32
  wellFormed,
33
33
  writeBytes,
34
34
  yieldTurn
35
- } from "./chunk-B637PJBS.js";
35
+ } from "./chunk-5OCQTFXT.js";
36
36
 
37
37
  // packages/safe-bash/src/commands/yq/index.ts
38
38
  init_platform();
@@ -5276,4 +5276,4 @@ export {
5276
5276
  createYqCommands,
5277
5277
  yqCommands
5278
5278
  };
5279
- //# sourceMappingURL=chunk-4OKU6JXB.js.map
5279
+ //# sourceMappingURL=chunk-NYHAKXQ7.js.map
@@ -0,0 +1,3 @@
1
+ import { type CommandDefinition } from "../contracts/index.js";
2
+ export declare function cmpCommand(): CommandDefinition;
3
+ //# sourceMappingURL=cmp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmp.d.ts","sourceRoot":"","sources":["../../src/commands/cmp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiF,KAAK,iBAAiB,EAAsC,MAAM,uBAAuB,CAAC;AAgclL,wBAAgB,UAAU,IAAI,iBAAiB,CA2B9C"}
@@ -0,0 +1,595 @@
1
+ import { FsError, getCommandArguments, isFsError } from "../contracts/index.js";
2
+ import { yieldTurn } from "../contracts/yield.js";
3
+ import { assertCommandRequirements } from "../contracts/command-requirements.js";
4
+ import { PublicDiagnostic, publicDiagnosticMessage } from "../diagnostics.js";
5
+ import { ByteInputBudget } from "./bytes/input-budget.js";
6
+ import { bufferLimit, output, pathOf, UsageError } from "./internal.js";
7
+ import { inputRequirements } from "./portable-requirements.js";
8
+ import { compareCopyIdentity, compareObservedEntries } from "./copy-identity.js";
9
+ const blockBytes = 4096;
10
+ const maxEmptyChunks = 65536;
11
+ function byteCount(text, option = "bytes") {
12
+ const invalid = () => { throw new UsageError(`invalid --${option} value '${text}'`); };
13
+ let position = 0;
14
+ while (text[position] === " " || text.charCodeAt(position) >= 9 && text.charCodeAt(position) <= 13)
15
+ position++;
16
+ if (text[position] === "+")
17
+ position++;
18
+ let base = 10;
19
+ if (text[position] === "0") {
20
+ base = 8;
21
+ if (text[position + 1]?.toLowerCase() === "x") {
22
+ base = 16;
23
+ position += 2;
24
+ }
25
+ }
26
+ const start = position;
27
+ let count = 0n;
28
+ const maximum = option === "bytes" ? 18446744073709551615n : 9223372036854775807n;
29
+ while (position < text.length) {
30
+ const digit = "0123456789abcdef".indexOf(text[position].toLowerCase());
31
+ if (digit < 0 || digit >= base)
32
+ break;
33
+ count = count * BigInt(base) + BigInt(digit);
34
+ if (count > maximum)
35
+ invalid();
36
+ position++;
37
+ }
38
+ const suffix = text.slice(position);
39
+ if (position === start) {
40
+ if (position !== 0 || !"kKMGTPEZY".includes(suffix[0] ?? " "))
41
+ invalid();
42
+ count = 1n;
43
+ }
44
+ if (suffix) {
45
+ const power = "KMGTPEZY".indexOf(suffix[0] === "k" ? "K" : suffix[0]) + 1;
46
+ if (!power || !["", "B", "iB"].includes(suffix.slice(1)))
47
+ invalid();
48
+ count *= (suffix.endsWith("B") && !suffix.endsWith("iB") ? 1000n : 1024n) ** BigInt(power);
49
+ }
50
+ if (count > maximum)
51
+ invalid();
52
+ return Number(count);
53
+ }
54
+ function parse(context) {
55
+ const parsed = { names: [], skips: [0, 0], limit: Infinity, silent: false, verbose: false, printBytes: false };
56
+ const long = { "print-bytes": "b", "print-chars": "c", "ignore-initial": "i", verbose: "l", bytes: "n", silent: "s", quiet: "s", version: "v", help: "help" };
57
+ const operands = [];
58
+ const argumentsWithBytes = getCommandArguments(context);
59
+ let size = 0;
60
+ const args = context.args.map((_argument, index) => {
61
+ const bytes = argumentsWithBytes.bytes(index);
62
+ size += bytes.length;
63
+ if (size > 65536)
64
+ throw new UsageError("argument limit exceeded");
65
+ return Array.from(bytes, byte => String.fromCharCode(byte)).join("");
66
+ });
67
+ let ended = false;
68
+ const apply = (key, argument) => {
69
+ if (key === "s" || key === "l") {
70
+ if (key === "s" ? parsed.verbose : parsed.silent)
71
+ throw new UsageError("options -l and -s are incompatible");
72
+ if (key === "s")
73
+ parsed.silent = true;
74
+ else
75
+ parsed.verbose = true;
76
+ }
77
+ else if (key === "b" || key === "c")
78
+ parsed.printBytes = true;
79
+ else if (key === "n")
80
+ parsed.limit = Math.min(parsed.limit, byteCount(argument));
81
+ else if (key === "i") {
82
+ const delimiter = argument.indexOf(":");
83
+ const first = delimiter < 0 ? argument : argument.slice(0, delimiter);
84
+ try {
85
+ parsed.skips[0] = Math.max(parsed.skips[0], byteCount(first, "ignore-initial"));
86
+ }
87
+ catch {
88
+ throw new UsageError(`invalid --ignore-initial value '${argument}'`);
89
+ }
90
+ parsed.skips[1] = Math.max(parsed.skips[1], delimiter < 0 ? parsed.skips[0] : byteCount(argument.slice(delimiter + 1), "ignore-initial"));
91
+ }
92
+ else
93
+ parsed.information = key === "v" ? "version" : "help";
94
+ };
95
+ for (let index = 0; index < args.length; index++) {
96
+ const argument = args[index];
97
+ if (ended || argument === "-" || !argument.startsWith("-")) {
98
+ operands.push(argument);
99
+ if (Object.hasOwn(context.env, "POSIXLY_CORRECT"))
100
+ ended = true;
101
+ }
102
+ else if (argument === "--")
103
+ ended = true;
104
+ else if (argument.startsWith("--")) {
105
+ const equals = argument.indexOf("=");
106
+ const name = argument.slice(2, equals < 0 ? undefined : equals);
107
+ const matches = Object.keys(long).filter(candidate => candidate.startsWith(name));
108
+ const selected = Object.hasOwn(long, name) ? name : matches[0];
109
+ if (!Object.hasOwn(long, name) && matches.length > 1 && new Set(matches.map(match => long[match])).size > 1) {
110
+ throw new UsageError(`option '${argument}' is ambiguous; possibilities: ${matches.map(match => `'--${match}'`).join(" ")}`);
111
+ }
112
+ if (!selected)
113
+ throw new UsageError(`unrecognized option '${argument}'`);
114
+ const key = long[selected];
115
+ let value;
116
+ if (key === "i" || key === "n") {
117
+ value = equals < 0 ? args[++index] : argument.slice(equals + 1);
118
+ if (value === undefined)
119
+ throw new UsageError(`option '--${selected}' requires an argument`);
120
+ }
121
+ else if (equals >= 0)
122
+ throw new UsageError(`option '--${selected}' doesn't allow an argument`);
123
+ apply(key, value);
124
+ }
125
+ else {
126
+ for (let offset = 1; offset < argument.length; offset++) {
127
+ const key = argument[offset];
128
+ if (!"bcilnsv".includes(key))
129
+ throw new UsageError(`invalid option -- '${key}'`);
130
+ let value;
131
+ if (key === "i" || key === "n") {
132
+ value = argument.slice(offset + 1) || args[++index];
133
+ if (value === undefined)
134
+ throw new UsageError(`option requires an argument -- '${key}'`);
135
+ offset = argument.length;
136
+ }
137
+ apply(key, value);
138
+ if (parsed.information)
139
+ return parsed;
140
+ }
141
+ }
142
+ if (parsed.information)
143
+ return parsed;
144
+ }
145
+ if (!operands.length)
146
+ throw new UsageError(`missing operand after '${args.at(-1) ?? "cmp"}'`);
147
+ parsed.names = [operands[0], operands[1] ?? "-"];
148
+ for (const index of [0, 1]) {
149
+ if (operands[index + 2] !== undefined)
150
+ parsed.skips[index] = Math.max(parsed.skips[index], byteCount(operands[index + 2], "ignore-initial"));
151
+ }
152
+ if (operands.length > 4)
153
+ throw new UsageError(`extra operand '${operands[4]}'`);
154
+ return parsed;
155
+ }
156
+ class InputError extends PublicDiagnostic {
157
+ suppressInSilent;
158
+ constructor(message, suppressInSilent) {
159
+ super(message);
160
+ this.suppressInSilent = suppressInSilent;
161
+ }
162
+ }
163
+ function inputError(error, name, opening = false) {
164
+ if (!(error instanceof FsError))
165
+ return error;
166
+ const messages = {
167
+ EACCES: "Permission denied", EBADF: "Bad file descriptor", EIO: "Input/output error", EISDIR: "Is a directory",
168
+ ELOOP: "Too many levels of symbolic links", ENAMETOOLONG: "File name too long", ENOENT: "No such file or directory",
169
+ ENOTDIR: "Not a directory", ENOTSUP: "Operation not supported", EPERM: "Operation not permitted",
170
+ };
171
+ const detail = messages[error.code];
172
+ return detail ? new InputError(`${name}: ${detail}`, opening) : error;
173
+ }
174
+ function printByte(byte) {
175
+ const prefix = byte >= 128 ? "M-" : "";
176
+ const character = byte & 127;
177
+ return prefix + (character < 32 ? `^${String.fromCharCode(character + 64)}` : character === 127 ? "^?" : String.fromCharCode(character));
178
+ }
179
+ async function discardsOutput(context) {
180
+ if (!context.stdoutFile)
181
+ return false;
182
+ const options = { signal: context.signal };
183
+ try {
184
+ const paths = [context.stdoutFile.path, "/dev/null"];
185
+ for (const path of paths) {
186
+ const capabilities = await context.fs.capabilitiesFor?.(path, options) ?? context.fs.capabilities;
187
+ context.signal.throwIfAborted();
188
+ if (capabilities.stat === false)
189
+ return false;
190
+ }
191
+ const target = await context.fs.stat(paths[0], options);
192
+ const empty = await context.fs.stat(paths[1], options);
193
+ return await compareObservedEntries(context.fs, paths[0], target, context.fs, paths[1], empty, options) === "same";
194
+ }
195
+ catch (error) {
196
+ context.signal.throwIfAborted();
197
+ if (error instanceof FsError)
198
+ return false;
199
+ throw error;
200
+ }
201
+ }
202
+ class Cursor {
203
+ name;
204
+ context;
205
+ budget;
206
+ signal;
207
+ chargeChunk;
208
+ bytes = new Uint8Array();
209
+ offset = 0;
210
+ size = Infinity;
211
+ stat;
212
+ position = 0;
213
+ skip = 0;
214
+ requestBytes = blockBytes;
215
+ reader;
216
+ iterator;
217
+ source;
218
+ finished = false;
219
+ retirement;
220
+ handle;
221
+ acquisition;
222
+ handleClosing;
223
+ operations = new Set();
224
+ constructor(name, context, budget, signal, chargeChunk) {
225
+ this.name = name;
226
+ this.context = context;
227
+ this.budget = budget;
228
+ this.signal = signal;
229
+ this.chargeChunk = chargeChunk;
230
+ }
231
+ async open(limit, skip) {
232
+ this.signal.throwIfAborted();
233
+ this.skip = skip;
234
+ if (this.name === "-") {
235
+ const input = this.context.stdinInput;
236
+ this.stat = input?.stat;
237
+ this.position = input?.position ?? 0;
238
+ if (this.stat?.type === "file" && Number.isSafeInteger(this.stat.size) && this.stat.size >= 0)
239
+ this.size = Math.max(0, this.stat.size - this.position - skip);
240
+ this.source = input ? () => ({ [Symbol.asyncIterator]: () => ({ next: () => input.read(this.requestBytes, this.signal) }) }) : () => this.context.stdin;
241
+ }
242
+ else {
243
+ let name;
244
+ try {
245
+ name = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Uint8Array.from(this.name, unit => unit.charCodeAt(0)));
246
+ }
247
+ catch {
248
+ throw new FsError("ENOENT");
249
+ }
250
+ const path = pathOf(this.context, name);
251
+ const capabilities = await this.context.fs.capabilitiesFor?.(path, { signal: this.signal }) ?? this.context.fs.capabilities;
252
+ this.signal.throwIfAborted();
253
+ assertCommandRequirements({ ...this.context, signal: this.signal }, inputRequirements, ["file"], capabilities);
254
+ let stat;
255
+ if (capabilities.stat !== false) {
256
+ try {
257
+ stat = await this.context.fs.stat(path, { signal: this.signal });
258
+ }
259
+ catch (error) {
260
+ this.signal.throwIfAborted();
261
+ if (!isFsError(error, "ENOTSUP"))
262
+ throw error;
263
+ }
264
+ }
265
+ this.signal.throwIfAborted();
266
+ if (stat?.type !== "directory" && capabilities.retainedRead === true && this.context.fs.openReadFile) {
267
+ this.acquisition = this.context.fs.openReadFile(path, { signal: this.signal }).then(handle => { this.handle = handle; return handle; });
268
+ try {
269
+ const handle = await this.acquisition;
270
+ this.signal.throwIfAborted();
271
+ stat = await this.work(() => handle.stat({ signal: this.signal }));
272
+ }
273
+ catch (error) {
274
+ this.signal.throwIfAborted();
275
+ if (this.handle || !isFsError(error, "ENOTSUP"))
276
+ throw error;
277
+ }
278
+ }
279
+ if (!this.handle && capabilities.access !== false) {
280
+ try {
281
+ await this.context.fs.access(path, 4, { signal: this.signal });
282
+ }
283
+ catch (error) {
284
+ this.signal.throwIfAborted();
285
+ if (!isFsError(error, "ENOTSUP"))
286
+ throw error;
287
+ }
288
+ }
289
+ this.signal.throwIfAborted();
290
+ if (!stat && limit === 0)
291
+ throw new FsError("ENOTSUP", { path, message: "zero-byte comparison requires metadata admission" });
292
+ this.stat = stat;
293
+ this.position = skip;
294
+ if (stat?.type === "file" && Number.isSafeInteger(stat.size) && stat.size >= 0)
295
+ this.size = Math.max(0, stat.size - skip);
296
+ if (limit === 0 && (this.handle || Number.isFinite(this.size) || this.skip === 0)) {
297
+ this.skip = 0;
298
+ return;
299
+ }
300
+ if (this.handle) {
301
+ const handle = this.handle;
302
+ let position = Number.isFinite(this.size) ? Math.min(skip, stat.size) : Math.min(skip, Number.MAX_SAFE_INTEGER);
303
+ this.skip = 0;
304
+ this.source = () => ({ [Symbol.asyncIterator]: () => ({ next: async () => {
305
+ const bytes = await this.work(() => handle.read(position, this.requestBytes, { signal: this.signal }));
306
+ if (!(bytes instanceof Uint8Array) || bytes.length > this.requestBytes)
307
+ throw new FsError("EIO", { path, message: "invalid retained read size" });
308
+ position += bytes.length;
309
+ return bytes.length ? { done: false, value: bytes } : { done: true, value: undefined };
310
+ } }) });
311
+ }
312
+ else if (this.context.fs.readStream && capabilities.streamingRead !== false) {
313
+ const start = Number.isFinite(this.size) ? Math.min(skip, stat.size) : 0;
314
+ if (Number.isFinite(this.size))
315
+ this.skip = 0;
316
+ this.source = () => this.context.fs.readStream(path, { signal: this.signal, chunkSize: Math.min(blockBytes, limit || this.skip),
317
+ ...(start ? { start } : {}),
318
+ ...(limit === Infinity || this.skip ? {} : { endExclusive: Math.min(Number.MAX_SAFE_INTEGER, start + limit) }) });
319
+ }
320
+ else {
321
+ if (capabilities.read === false)
322
+ throw new FsError("ENOTSUP", { syscall: "readFile", path });
323
+ const context = this.context;
324
+ const signal = this.signal;
325
+ this.source = () => ({ async *[Symbol.asyncIterator]() {
326
+ yield await context.fs.readFile(path, { signal, maxBytes: bufferLimit });
327
+ } });
328
+ }
329
+ }
330
+ }
331
+ async prepare() {
332
+ const input = this.name === "-" ? this.context.stdinInput : undefined;
333
+ if (input?.seek) {
334
+ this.signal.throwIfAborted();
335
+ this.position = Math.min(Number.MAX_SAFE_INTEGER, input.position + this.skip);
336
+ if (this.skip) {
337
+ try {
338
+ await input.seek(this.position, this.signal);
339
+ }
340
+ catch (error) {
341
+ throw inputError(error, this.name);
342
+ }
343
+ }
344
+ this.signal.throwIfAborted();
345
+ this.skip = 0;
346
+ if (this.stat?.type === "file")
347
+ this.size = Math.max(0, this.stat.size - this.position);
348
+ }
349
+ }
350
+ async fill(remaining) {
351
+ this.signal.throwIfAborted();
352
+ if (remaining === 0 && this.skip === 0)
353
+ return false;
354
+ if (!this.reader) {
355
+ try {
356
+ this.iterator = this.source()[Symbol.asyncIterator]();
357
+ }
358
+ catch (error) {
359
+ throw inputError(error, this.name);
360
+ }
361
+ this.reader = this.budget.read({ [Symbol.asyncIterator]: () => ({
362
+ next: async () => {
363
+ let result;
364
+ try {
365
+ result = await this.iterator.next();
366
+ }
367
+ catch (error) {
368
+ throw inputError(error, this.name);
369
+ }
370
+ if (result.done)
371
+ this.finished = true;
372
+ return result;
373
+ },
374
+ return: async () => { await this.retire(); return { done: true, value: undefined }; },
375
+ }) }, this.signal)[Symbol.asyncIterator]();
376
+ }
377
+ while (this.offset === this.bytes.length) {
378
+ this.signal.throwIfAborted();
379
+ if (remaining === 0 && this.skip === 0)
380
+ return false;
381
+ this.requestBytes = Math.min(blockBytes, this.skip || remaining);
382
+ const item = await this.reader.next();
383
+ if (item.done)
384
+ return false;
385
+ await this.chargeChunk(item.value.length);
386
+ this.bytes = new Uint8Array(item.value);
387
+ this.offset = Math.min(this.skip, this.bytes.length);
388
+ this.skip -= this.offset;
389
+ }
390
+ return true;
391
+ }
392
+ retire() {
393
+ this.retirement ??= Promise.resolve().then(async () => {
394
+ try {
395
+ if (!this.finished)
396
+ await this.iterator?.return?.();
397
+ }
398
+ catch (error) {
399
+ throw inputError(error, this.name);
400
+ }
401
+ });
402
+ return this.retirement;
403
+ }
404
+ work(operation) {
405
+ this.signal.throwIfAborted();
406
+ if (this.handleClosing)
407
+ throw new FsError("EBADF", { path: this.name });
408
+ const pending = Promise.resolve().then(() => {
409
+ this.signal.throwIfAborted();
410
+ return operation();
411
+ });
412
+ this.operations.add(pending);
413
+ void pending.then(() => { this.operations.delete(pending); }, () => { this.operations.delete(pending); });
414
+ return pending;
415
+ }
416
+ async close() {
417
+ this.handleClosing ??= Promise.resolve().then(async () => {
418
+ await this.acquisition?.catch(() => undefined);
419
+ await Promise.allSettled(this.operations);
420
+ try {
421
+ await this.handle?.close();
422
+ }
423
+ catch (error) {
424
+ throw inputError(error, this.name);
425
+ }
426
+ });
427
+ const results = await Promise.allSettled([this.retire(), this.reader?.return?.(), this.handleClosing]);
428
+ this.bytes = new Uint8Array();
429
+ for (const result of results)
430
+ if (result.status === "rejected")
431
+ throw result.reason;
432
+ }
433
+ }
434
+ async function compare(context, parsed) {
435
+ const { names, skips, limit, silent, verbose, printBytes } = parsed;
436
+ const controller = new AbortController();
437
+ const signal = AbortSignal.any([context.signal, controller.signal]);
438
+ const budget = new ByteInputBudget(bufferLimit);
439
+ let chunks = 0;
440
+ let emptyChunks = 0;
441
+ const chargeChunk = async (size) => {
442
+ if (size === 0 && ++emptyChunks > maxEmptyChunks)
443
+ throw new FsError("EFBIG", { message: "cmp empty input chunk limit exceeded" });
444
+ if (++chunks % 64 === 0)
445
+ await yieldTurn(signal);
446
+ };
447
+ const cursors = names.map(name => new Cursor(name, context, budget, signal, chargeChunk));
448
+ let closing;
449
+ const cleanupErrors = [];
450
+ const close = () => {
451
+ if (!closing) {
452
+ controller.abort(new FsError("EPIPE", { message: "cmp input closed" }));
453
+ closing = Promise.allSettled(cursors.map(cursor => cursor.close())).then(results => {
454
+ for (const result of results)
455
+ if (result.status === "rejected")
456
+ cleanupErrors.push(result.reason);
457
+ });
458
+ }
459
+ return closing;
460
+ };
461
+ context.registerCleanup?.(close);
462
+ let duplicateStdinClose = false;
463
+ const run = async () => {
464
+ assertCommandRequirements({ ...context, signal }, inputRequirements, [names.some(name => name !== "-") ? "file" : "stdin"]);
465
+ const sharedStdin = names.every(name => name === "-");
466
+ if (sharedStdin && (skips[0] === skips[1] || !context.stdinInput?.seek))
467
+ return 0;
468
+ for (const index of [0, 1]) {
469
+ const cursor = cursors[index];
470
+ try {
471
+ await cursor.open(limit, skips[index]);
472
+ }
473
+ catch (error) {
474
+ throw inputError(error, cursor.name, true);
475
+ }
476
+ if (index === 0 && names[0] === names[1] && skips[0] === skips[1] && cursor.stat)
477
+ return 0;
478
+ }
479
+ const [left, right] = cursors;
480
+ for (const cursor of cursors)
481
+ await cursor.prepare();
482
+ if (!sharedStdin && left.position === right.position && compareCopyIdentity(left.stat, right.stat) === "same")
483
+ return 0;
484
+ if (silent && Number.isFinite(left.size) && Number.isFinite(right.size) && left.size !== right.size && Math.min(left.size, right.size) < limit)
485
+ return 1;
486
+ for (const cursor of cursors)
487
+ if (cursor.stat?.type === "directory")
488
+ throw inputError(new FsError("EISDIR"), cursor.name);
489
+ const noStdout = !silent && await discardsOutput(context);
490
+ const known = Math.min(left.size, right.size, limit);
491
+ const width = Number.isFinite(known) ? String(known).length : 19;
492
+ duplicateStdinClose = sharedStdin;
493
+ let compared = 0;
494
+ let newlines = 0;
495
+ let lastByte = -1;
496
+ let different = false;
497
+ let report = "";
498
+ const flush = async () => {
499
+ if (report) {
500
+ await output(context, report);
501
+ report = "";
502
+ }
503
+ };
504
+ for (const cursor of cursors)
505
+ await cursor.fill(0);
506
+ while (compared < limit) {
507
+ const hasLeft = await left.fill(limit - compared);
508
+ const hasRight = await right.fill(limit - compared);
509
+ if (!hasLeft || !hasRight) {
510
+ await flush();
511
+ if (hasLeft === hasRight)
512
+ return different ? 1 : 0;
513
+ if (!silent) {
514
+ const shorter = hasLeft ? right.name : left.name;
515
+ const detail = compared === 0 ? "which is empty" : `after byte ${compared}${verbose || noStdout ? "" : `, ${lastByte === 10 ? "" : "in "}line ${newlines + (lastByte === 10 ? 0 : 1)}`}`;
516
+ await output({ ...context, stdout: context.stderr }, Uint8Array.from(`cmp: EOF on ${shorter} ${detail}\n`, unit => unit.charCodeAt(0)));
517
+ }
518
+ return 1;
519
+ }
520
+ const count = Math.min(left.bytes.length - left.offset, right.bytes.length - right.offset, limit - compared, blockBytes - compared % blockBytes);
521
+ for (let index = 0; index < count; index++) {
522
+ const leftByte = left.bytes[left.offset++];
523
+ const rightByte = right.bytes[right.offset++];
524
+ compared++;
525
+ if (leftByte !== rightByte) {
526
+ different = true;
527
+ if (silent || noStdout)
528
+ return 1;
529
+ if (!verbose) {
530
+ const locale = context.env.LC_ALL || context.env.LC_MESSAGES || context.env.LANG;
531
+ const unit = !printBytes && (!locale || locale === "C" || locale === "POSIX") ? "char" : "byte";
532
+ const detail = printBytes ? ` is ${leftByte.toString(8).padStart(3)} ${printByte(leftByte)} ${rightByte.toString(8).padStart(3)} ${printByte(rightByte)}` : "";
533
+ await output(context, Uint8Array.from(`${left.name} ${right.name} differ: ${unit} ${compared}, line ${newlines + 1}${detail}\n`, character => character.charCodeAt(0)));
534
+ return 1;
535
+ }
536
+ report += `${String(compared).padStart(width)} ${leftByte.toString(8).padStart(3)} ${printBytes ? `${printByte(leftByte).padEnd(4)} ` : ""}${rightByte.toString(8).padStart(3)}${printBytes ? ` ${printByte(rightByte)}` : ""}\n`;
537
+ if (report.length >= 16384)
538
+ await flush();
539
+ }
540
+ if (leftByte === 10)
541
+ newlines++;
542
+ lastByte = leftByte;
543
+ }
544
+ if (compared % blockBytes === 0)
545
+ await yieldTurn(signal);
546
+ }
547
+ await flush();
548
+ return different ? 1 : 0;
549
+ };
550
+ let result;
551
+ try {
552
+ result = await run();
553
+ }
554
+ catch (error) {
555
+ await close();
556
+ throw error;
557
+ }
558
+ await close();
559
+ if (cleanupErrors.length)
560
+ throw cleanupErrors[0];
561
+ if (duplicateStdinClose)
562
+ throw inputError(new FsError("EBADF"), "-");
563
+ return result;
564
+ }
565
+ export function cmpCommand() {
566
+ return { name: "cmp", filesystemRequirements: inputRequirements, async execute(context) {
567
+ context.signal.throwIfAborted();
568
+ let silent = false;
569
+ try {
570
+ let argumentBytes = 0;
571
+ for (const argument of context.args) {
572
+ argumentBytes += argument.length;
573
+ if (argumentBytes > 65536 || context.args.length > 4096)
574
+ throw new UsageError("argument limit exceeded");
575
+ }
576
+ const parsed = parse(context);
577
+ if (parsed.information) {
578
+ await output(context, parsed.information === "version" ? "cmp (virtual-bash)\n" : "Usage: cmp [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\nCompare bytes in the virtual filesystem; omitted FILE2 or '-' reads stdin.\n -s, --silent, --quiet report status only\n -l, --verbose list differing bytes in octal\n -b, -c, --print-bytes also display byte characters\n -i, --ignore-initial=SKIP[:SKIP2] skip initial bytes\n -n, --bytes=LIMIT compare at most LIMIT bytes\n --help, -v, --version show virtual-bash command information\nInput is bounded to 32 MiB per invocation; empty-chunk and argument limits apply.\n");
579
+ return { exitCode: 0 };
580
+ }
581
+ silent = parsed.silent;
582
+ return { exitCode: await compare(context, parsed) };
583
+ }
584
+ catch (error) {
585
+ context.signal.throwIfAborted();
586
+ if (!silent || !(error instanceof InputError) || !error.suppressInSilent) {
587
+ const message = publicDiagnosticMessage(error, context.onInternalError);
588
+ const diagnostic = `cmp: ${message}\n${error instanceof UsageError ? "cmp: Try 'cmp --help' for more information.\n" : ""}`;
589
+ await output({ ...context, stdout: context.stderr }, error instanceof UsageError || error instanceof InputError ? Uint8Array.from(diagnostic, unit => unit.charCodeAt(0)) : diagnostic);
590
+ }
591
+ return { exitCode: 2 };
592
+ }
593
+ } };
594
+ }
595
+ //# sourceMappingURL=cmp.js.map