@poe-platform/safe-bash 0.1.656 → 0.1.657
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/dist/safe-bash/chunks/{xz-U4QDTPZL.js → xz-4V5FXSPG.js} +555 -514
- package/dist/safe-bash/chunks/xz-4V5FXSPG.js.map +7 -0
- package/dist/safe-bash/commands/archive/index.d.ts +1 -1
- package/dist/safe-bash/commands/archive/index.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/internal.d.ts +32 -0
- package/dist/safe-bash/commands/archive/internal.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/internal.js.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/arguments.d.ts +3 -0
- package/dist/safe-bash/commands/archive/unzip/arguments.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/arguments.js +31 -5
- package/dist/safe-bash/commands/archive/unzip/arguments.js.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/safety.d.ts +4 -0
- package/dist/safe-bash/commands/archive/unzip/safety.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/safety.js +3 -0
- package/dist/safe-bash/commands/archive/unzip/safety.js.map +1 -1
- package/dist/safe-bash/commands/archive/unzip.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/unzip.js +159 -112
- package/dist/safe-bash/commands/archive/unzip.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.d.ts +2 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.js +3 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.d.ts +2 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.js +3 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes.d.ts +18 -0
- package/dist/safe-bash/commands/archive/zip/aes.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes.js +157 -0
- package/dist/safe-bash/commands/archive/zip/aes.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts +16 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/crypto.js +85 -0
- package/dist/safe-bash/commands/archive/zip/crypto.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/grow.d.ts +4 -0
- package/dist/safe-bash/commands/archive/zip/grow.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/grow.js +3 -0
- package/dist/safe-bash/commands/archive/zip/grow.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/help.d.ts +4 -2
- package/dist/safe-bash/commands/archive/zip/help.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/help.js +98 -3
- package/dist/safe-bash/commands/archive/zip/help.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/line-endings.d.ts +1 -0
- package/dist/safe-bash/commands/archive/zip/line-endings.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/line-endings.js +158 -0
- package/dist/safe-bash/commands/archive/zip/line-endings.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/log.d.ts +23 -0
- package/dist/safe-bash/commands/archive/zip/log.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/log.js +159 -0
- package/dist/safe-bash/commands/archive/zip/log.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/lzma.d.ts +10 -0
- package/dist/safe-bash/commands/archive/zip/lzma.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/lzma.js +41 -0
- package/dist/safe-bash/commands/archive/zip/lzma.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/names.d.ts +3 -0
- package/dist/safe-bash/commands/archive/zip/names.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/names.js +34 -0
- package/dist/safe-bash/commands/archive/zip/names.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/options.d.ts +7 -0
- package/dist/safe-bash/commands/archive/zip/options.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/options.js +88 -17
- package/dist/safe-bash/commands/archive/zip/options.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/repair.d.ts +11 -0
- package/dist/safe-bash/commands/archive/zip/repair.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/repair.js +392 -0
- package/dist/safe-bash/commands/archive/zip/repair.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/safety.d.ts +23 -4
- package/dist/safe-bash/commands/archive/zip/safety.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/safety.js +73 -23
- package/dist/safe-bash/commands/archive/zip/safety.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/sfx.d.ts +5 -0
- package/dist/safe-bash/commands/archive/zip/sfx.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/sfx.js +56 -0
- package/dist/safe-bash/commands/archive/zip/sfx.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/test-command.d.ts +6 -0
- package/dist/safe-bash/commands/archive/zip/test-command.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/test-command.js +101 -0
- package/dist/safe-bash/commands/archive/zip/test-command.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/volumes.d.ts +22 -0
- package/dist/safe-bash/commands/archive/zip/volumes.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/volumes.js +265 -0
- package/dist/safe-bash/commands/archive/zip/volumes.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/zip64.d.ts +20 -1
- package/dist/safe-bash/commands/archive/zip/zip64.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/zip64.js +127 -17
- package/dist/safe-bash/commands/archive/zip/zip64.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.d.ts +30 -5
- package/dist/safe-bash/commands/archive/zip-format.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.js +489 -91
- package/dist/safe-bash/commands/archive/zip-format.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip.js +729 -128
- package/dist/safe-bash/commands/archive/zip.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts +7 -0
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec-loader.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec-loader.js +8 -1
- package/dist/safe-bash/commands/bytes/compression/codec-loader.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/native/generated/xz.mjs +1 -1
- package/dist/safe-bash/commands/bytes/compression/native/sources.json +2 -2
- package/dist/safe-bash/commands/bytes/compression/native/types.d.ts +1 -0
- package/dist/safe-bash/commands/bytes/compression/native/types.d.ts.map +1 -1
- package/dist/safe-bash/commands/text.js +1 -1
- package/dist/safe-bash/commands/text.js.map +1 -1
- package/dist/safe-bash/core.browser.js +3106 -717
- package/dist/safe-bash/core.browser.js.map +4 -4
- package/package.json +11 -3
- package/dist/safe-bash/chunks/xz-U4QDTPZL.js.map +0 -7
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import { zipDosName } from "./zip/names.js";
|
|
2
|
+
import { readZipSfx } from "./zip/sfx.js";
|
|
3
|
+
import { adjustZipSfx, repairZip } from "./zip/repair.js";
|
|
4
|
+
import { zipGrowRecords } from "./zip/grow.js";
|
|
5
|
+
import { ZipLog } from "./zip/log.js";
|
|
6
|
+
import { parseZipTestCommand, testZipCommand } from "./zip/test-command.js";
|
|
1
7
|
import { inspectZipMoveSource, removeZipSources } from "./zip/move.js";
|
|
2
|
-
import {
|
|
8
|
+
import { readZipPassword, ZipHostFailure } from "./zip/crypto.js";
|
|
9
|
+
import { zipEncryptionProfile } from "./zip/aes.js";
|
|
10
|
+
import { zipToCrlf, zipFromCrlf } from "./zip/line-endings.js";
|
|
3
11
|
import { parseZipDate, zipDateMatches, zipLatestTime } from "./zip/dates.js";
|
|
4
12
|
import { zipEnvironmentArguments } from "./zip/environment.js";
|
|
5
13
|
import { readZipComment, ZipCommentInput } from "./zip/comments.js";
|
|
@@ -12,14 +20,25 @@ import { publicDiagnosticMessage } from "../../diagnostics.js";
|
|
|
12
20
|
import { escapeText } from "../../escaping.js";
|
|
13
21
|
import { Budget, checkPath, display, fail, hasIdentity, sameIdentity, settings, text, vfsPath } from "./internal.js";
|
|
14
22
|
import { decodeZipEntry, makeZipEntry, readZipArchive, writeZipArchive, streamZipArchive, updateZipExtras, setZipEntryComment } from "./zip-format.js";
|
|
15
|
-
import { zipHelp, zipExtendedHelp } from "./zip/help.js";
|
|
16
|
-
import { publishZip, ZipScope } from "./zip/safety.js";
|
|
23
|
+
import { zipHelp, zipExtendedHelp, zipVersion, zipLicense } from "./zip/help.js";
|
|
24
|
+
import { publishZip, stageZip, ZipScope } from "./zip/safety.js";
|
|
25
|
+
import { splitSize, splitZipVolumes, resolveZipVolumes, publishZipVolumes, volumeName } from "./zip/volumes.js";
|
|
17
26
|
import { Selection } from "./unzip/arguments.js";
|
|
18
|
-
import { normalizeZipOption, ZipFailure } from "./zip/options.js";
|
|
27
|
+
import { normalizeZipOption, reservedZipShortOptions, ZipFailure, parseZipDotSize, zipLongOptions, zipNegatableOptions, zipDisplaySize, zipPublicText, zipPasswordArgument } from "./zip/options.js";
|
|
19
28
|
const defaultStoreSuffixes = [".Z", ".zip", ".zoo", ".arc", ".lzh", ".arj"];
|
|
20
|
-
async function parse(scope, limits) {
|
|
29
|
+
async function parse(scope, limits, defaults) {
|
|
21
30
|
const context = scope.context;
|
|
22
31
|
const args = zipEnvironmentArguments(context.env, context.args, limits);
|
|
32
|
+
const rawArguments = context.argumentValues ? getCommandArguments(context) : undefined;
|
|
33
|
+
const passwordArguments = new Set();
|
|
34
|
+
const defaultsCount = args.length - context.args.length;
|
|
35
|
+
const validateArgumentText = () => {
|
|
36
|
+
if (rawArguments)
|
|
37
|
+
for (const [index, value] of rawArguments.values.entries()) {
|
|
38
|
+
if (!passwordArguments.has(index))
|
|
39
|
+
text(shellValueBytes(value));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
23
42
|
if (args.length > limits.maxArgumentBytes)
|
|
24
43
|
fail("argument count limit exceeded");
|
|
25
44
|
let bytes = 0;
|
|
@@ -38,10 +57,11 @@ async function parse(scope, limits) {
|
|
|
38
57
|
fail("argument byte limit exceeded");
|
|
39
58
|
rawBytes += size;
|
|
40
59
|
}
|
|
41
|
-
for (const value of argumentsValue.values)
|
|
42
|
-
text(shellValueBytes(value));
|
|
43
60
|
}
|
|
44
61
|
let archive;
|
|
62
|
+
let password;
|
|
63
|
+
let encrypt = defaults?.encryption !== undefined;
|
|
64
|
+
let aes = zipEncryptionProfile(defaults?.encryption ?? "zipcrypto");
|
|
45
65
|
let output;
|
|
46
66
|
let action = "add";
|
|
47
67
|
let recursive = false;
|
|
@@ -49,24 +69,55 @@ async function parse(scope, limits) {
|
|
|
49
69
|
let noWild = false;
|
|
50
70
|
let stopAtDirectories = false;
|
|
51
71
|
let quiet = false;
|
|
72
|
+
let verbose = false;
|
|
73
|
+
let showFiles;
|
|
74
|
+
let showCommand = false;
|
|
75
|
+
let showOptions = false;
|
|
76
|
+
let debug = false;
|
|
77
|
+
let displayBytes = false;
|
|
78
|
+
let displayCounts = false;
|
|
79
|
+
let displayUsize = false;
|
|
80
|
+
let displayVolume = false;
|
|
81
|
+
let dotSize = 0;
|
|
82
|
+
let dotsSet = false;
|
|
83
|
+
let globalDots = false;
|
|
84
|
+
const commandOptions = [];
|
|
85
|
+
const commandPaths = [];
|
|
52
86
|
let junkPaths = false;
|
|
87
|
+
let dosNames = false;
|
|
88
|
+
let fifo = false;
|
|
53
89
|
let omitDirectories = false;
|
|
54
90
|
let storeLinks = false;
|
|
55
91
|
let test = false;
|
|
92
|
+
let testCommand;
|
|
93
|
+
let difference = false;
|
|
94
|
+
let grow = false;
|
|
95
|
+
let tempPath;
|
|
96
|
+
let junkSfx = false;
|
|
97
|
+
let repair;
|
|
98
|
+
let adjust = false;
|
|
99
|
+
let logPath;
|
|
100
|
+
let logAppend = false;
|
|
101
|
+
let logInfo = false;
|
|
56
102
|
let mustMatch = false;
|
|
57
103
|
let filesync = false;
|
|
58
104
|
let archiveComment = false;
|
|
59
105
|
let entryComments = false;
|
|
60
106
|
let latestTime = false;
|
|
61
107
|
let toCrlf = false;
|
|
108
|
+
let fromCrlf = false;
|
|
62
109
|
let move = false;
|
|
63
110
|
let fromDate;
|
|
64
111
|
let beforeDate;
|
|
65
112
|
let descriptors = false;
|
|
113
|
+
let split;
|
|
114
|
+
let splitPause = false, splitVerbose = false, splitBell = false;
|
|
66
115
|
let zip64;
|
|
67
116
|
let metadata = "default";
|
|
68
117
|
let level = 6;
|
|
69
|
-
let method = "deflate";
|
|
118
|
+
let method = defaults?.compression ?? "deflate";
|
|
119
|
+
if (method !== "store" && method !== "deflate" && method !== "bzip2" && method !== "lzma")
|
|
120
|
+
fail("ZIP unsupported compression method");
|
|
70
121
|
let suffixes = defaultStoreSuffixes;
|
|
71
122
|
let stdinNames = false;
|
|
72
123
|
let literal = false;
|
|
@@ -75,25 +126,212 @@ async function parse(scope, limits) {
|
|
|
75
126
|
const includes = [];
|
|
76
127
|
const excludes = [];
|
|
77
128
|
for (let index = 0; index < args.length; index++) {
|
|
129
|
+
const startIndex = index;
|
|
130
|
+
let listOption = false;
|
|
78
131
|
const original = args[index];
|
|
79
132
|
checkPath(original, limits);
|
|
133
|
+
if (!literal && (original === "--encryption" || original.startsWith("--encryption="))) {
|
|
134
|
+
const value = original === "--encryption" ? args[++index] : original.slice(13);
|
|
135
|
+
if (value === undefined)
|
|
136
|
+
fail("ZIP encryption option requires a profile");
|
|
137
|
+
aes = zipEncryptionProfile(value);
|
|
138
|
+
encrypt = true;
|
|
139
|
+
commandOptions.push(...args.slice(startIndex, index + 1));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
80
142
|
const argument = literal ? original : normalizeZipOption(original);
|
|
81
143
|
if (!literal && argument === "--") {
|
|
82
144
|
if (archive === undefined)
|
|
83
145
|
throw new ZipFailure(16, "Invalid command arguments", "can't use -- before archive name");
|
|
84
146
|
literal = true;
|
|
147
|
+
commandPaths.push(original);
|
|
85
148
|
}
|
|
86
149
|
else if (!literal && argument.startsWith("-") && argument !== "-") {
|
|
150
|
+
commandOptions.push(original);
|
|
151
|
+
if (argument === "--version") {
|
|
152
|
+
validateArgumentText();
|
|
153
|
+
return { information: "version" };
|
|
154
|
+
}
|
|
87
155
|
for (let offset = 1; offset < argument.length; offset++) {
|
|
156
|
+
if (reservedZipShortOptions.has(argument.slice(offset, offset + 2))) {
|
|
157
|
+
throw new ZipFailure(16, "Invalid command arguments", `unsupported option: -${argument.slice(offset, offset + 2)}`);
|
|
158
|
+
}
|
|
88
159
|
const flag = argument[offset];
|
|
89
|
-
|
|
160
|
+
const pair = argument.slice(offset, offset + 2);
|
|
161
|
+
if (pair === "sp" || pair === "sv" || pair === "sb") {
|
|
162
|
+
if (pair === "sp") {
|
|
163
|
+
splitPause = true;
|
|
164
|
+
descriptors = true;
|
|
165
|
+
}
|
|
166
|
+
if (pair === "sv")
|
|
167
|
+
splitVerbose = true;
|
|
168
|
+
if (pair === "sb")
|
|
169
|
+
splitBell = true;
|
|
170
|
+
offset++;
|
|
171
|
+
}
|
|
172
|
+
else if (flag === "s" && !["sc", "sd", "sf", "so"].includes(pair)) {
|
|
173
|
+
let value = argument.slice(offset + 1) || args[++index];
|
|
174
|
+
if (value?.startsWith("="))
|
|
175
|
+
value = value.slice(1);
|
|
176
|
+
if (!value)
|
|
177
|
+
throw new ZipFailure(16, "Invalid command arguments", "split size requires a value");
|
|
178
|
+
split = splitSize(value);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
else if (pair === "FI") {
|
|
182
|
+
fifo = argument[offset + 2] !== "-";
|
|
183
|
+
offset += fifo ? 1 : 2;
|
|
184
|
+
}
|
|
185
|
+
else if (pair === "RE") {
|
|
186
|
+
// Unix bracket lists are already enabled in the bounded glob matcher.
|
|
187
|
+
if (argument[offset + 2] === "-")
|
|
188
|
+
throw new ZipFailure(16, "Invalid command arguments", "option RE is not negatable");
|
|
189
|
+
offset++;
|
|
190
|
+
}
|
|
191
|
+
else if (flag === "k") {
|
|
192
|
+
if (argument[offset + 1] === "-")
|
|
193
|
+
throw new ZipFailure(16, "Invalid command arguments", "option k is not negatable");
|
|
194
|
+
dosNames = true;
|
|
195
|
+
}
|
|
196
|
+
else if (pair === "DF") {
|
|
197
|
+
difference = true;
|
|
198
|
+
offset++;
|
|
199
|
+
}
|
|
200
|
+
else if (pair === "la" || pair === "li") {
|
|
201
|
+
if (pair === "la")
|
|
202
|
+
logAppend = true;
|
|
203
|
+
else
|
|
204
|
+
logInfo = true;
|
|
205
|
+
offset++;
|
|
206
|
+
}
|
|
207
|
+
else if (pair === "TT" || pair === "lf" || flag === "b") {
|
|
208
|
+
const option = flag === "b" ? "b" : pair;
|
|
209
|
+
if (option !== "b")
|
|
210
|
+
offset++;
|
|
211
|
+
let value = argument.slice(offset + 1) || args[++index];
|
|
212
|
+
if (value?.startsWith("="))
|
|
213
|
+
value = value.slice(1);
|
|
214
|
+
if (!value)
|
|
215
|
+
throw new ZipFailure(16, "Invalid command arguments", `option ${option} requires a value`);
|
|
216
|
+
if (option === "b")
|
|
217
|
+
tempPath = value;
|
|
218
|
+
else if (option === "lf")
|
|
219
|
+
logPath = value;
|
|
220
|
+
else
|
|
221
|
+
testCommand = parseZipTestCommand(value);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
else if (flag === "g")
|
|
225
|
+
grow = true;
|
|
226
|
+
else if (flag === "J")
|
|
227
|
+
junkSfx = true;
|
|
228
|
+
else if (flag === "A")
|
|
229
|
+
adjust = true;
|
|
230
|
+
else if (flag === "F" && pair !== "FS" && pair !== "FI") {
|
|
231
|
+
repair = pair === "FF" ? "FF" : "F";
|
|
232
|
+
if (pair === "FF")
|
|
233
|
+
offset++;
|
|
234
|
+
}
|
|
235
|
+
else if (flag === "P") {
|
|
236
|
+
let value = argument.slice(offset + 1);
|
|
237
|
+
const attached = value.length > 0;
|
|
238
|
+
if (!value) {
|
|
239
|
+
value = args[++index];
|
|
240
|
+
if (value === undefined)
|
|
241
|
+
throw new ZipFailure(16, "Invalid command arguments", "password option requires a value");
|
|
242
|
+
}
|
|
243
|
+
else if (value.startsWith("="))
|
|
244
|
+
value = value.slice(1);
|
|
245
|
+
const rawIndex = index - defaultsCount;
|
|
246
|
+
passwordArguments.add(rawIndex);
|
|
247
|
+
const raw = rawArguments?.bytes(rawIndex);
|
|
248
|
+
const prefix = attached ? original.startsWith("--") ? original.indexOf("=") + 1 : offset + 1 + (original[offset + 1] === "=" ? 1 : 0) : 0;
|
|
249
|
+
password = raw ? new Uint8Array(raw.subarray(prefix)) : Buffer.from(value);
|
|
250
|
+
if (password.includes(0))
|
|
251
|
+
fail("ZIP invalid password bytes");
|
|
252
|
+
if (!password.length)
|
|
253
|
+
throw new ZipFailure(16, "Invalid command arguments", "zero length password not allowed");
|
|
254
|
+
encrypt = true;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
else if (flag === "e")
|
|
258
|
+
encrypt = true;
|
|
259
|
+
else if (["db", "dc", "dd", "dg", "du", "dv", "ds", "sc", "sd", "sf", "so"].includes(pair)) {
|
|
260
|
+
offset++;
|
|
261
|
+
const negated = argument[offset + 1] === "-";
|
|
262
|
+
if (negated && !zipNegatableOptions.has(pair))
|
|
263
|
+
throw new ZipFailure(16, "Invalid command arguments", `option ${pair} is not negatable`);
|
|
264
|
+
if (negated)
|
|
265
|
+
offset++;
|
|
266
|
+
if (pair === "ds") {
|
|
267
|
+
let value = argument.slice(offset + 1) || args[++index];
|
|
268
|
+
if (value === undefined)
|
|
269
|
+
throw new ZipFailure(16, "Invalid command arguments", "option ds requires a value");
|
|
270
|
+
if (value.startsWith("="))
|
|
271
|
+
value = value.slice(1);
|
|
272
|
+
dotSize = parseZipDotSize(value);
|
|
273
|
+
dotsSet = true;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
if (pair === "db")
|
|
277
|
+
displayBytes = !negated;
|
|
278
|
+
else if (pair === "dc")
|
|
279
|
+
displayCounts = !negated;
|
|
280
|
+
else if (pair === "du")
|
|
281
|
+
displayUsize = !negated;
|
|
282
|
+
else if (pair === "dv")
|
|
283
|
+
displayVolume = !negated;
|
|
284
|
+
else if (pair === "sf")
|
|
285
|
+
showFiles = !negated;
|
|
286
|
+
else if (pair === "sc")
|
|
287
|
+
showCommand = true;
|
|
288
|
+
else if (pair === "sd")
|
|
289
|
+
debug = true;
|
|
290
|
+
else if (pair === "so")
|
|
291
|
+
showOptions = true;
|
|
292
|
+
else if (pair === "dd") {
|
|
293
|
+
globalDots = false;
|
|
294
|
+
if (negated)
|
|
295
|
+
dotsSet = false;
|
|
296
|
+
else {
|
|
297
|
+
if (!dotsSet)
|
|
298
|
+
dotSize = 10 * 1024 ** 2;
|
|
299
|
+
dotsSet = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
else if (pair === "dg") {
|
|
303
|
+
globalDots = !negated;
|
|
304
|
+
if (!negated) {
|
|
305
|
+
if (!dotsSet)
|
|
306
|
+
dotSize = 10 * 1024 ** 2;
|
|
307
|
+
dotsSet = true;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else if (flag === "L" || flag === "v") {
|
|
312
|
+
if (argument[offset + 1] === "-")
|
|
313
|
+
throw new ZipFailure(16, "Invalid command arguments", `option ${flag} is not negatable`);
|
|
314
|
+
if (flag === "L") {
|
|
315
|
+
validateArgumentText();
|
|
316
|
+
return { information: "license" };
|
|
317
|
+
}
|
|
318
|
+
if (args.length === 1 && original === "-v") {
|
|
319
|
+
validateArgumentText();
|
|
320
|
+
return { information: "version" };
|
|
321
|
+
}
|
|
322
|
+
verbose = true;
|
|
323
|
+
quiet = false;
|
|
324
|
+
}
|
|
325
|
+
else if (flag === "h") {
|
|
90
326
|
if (argument[offset + 1] === "-")
|
|
91
327
|
throw new ZipFailure(16, "Invalid command arguments", "option h is not negatable");
|
|
92
328
|
if (argument[offset + 1] === "2") {
|
|
93
329
|
if (argument[offset + 2] === "-")
|
|
94
330
|
throw new ZipFailure(16, "Invalid command arguments", "option h2 is not negatable");
|
|
331
|
+
validateArgumentText();
|
|
95
332
|
return { information: "more-help" };
|
|
96
333
|
}
|
|
334
|
+
validateArgumentText();
|
|
97
335
|
return { information: "help" };
|
|
98
336
|
}
|
|
99
337
|
else if (flag === "r" || flag === "R") {
|
|
@@ -150,9 +388,10 @@ async function parse(scope, limits) {
|
|
|
150
388
|
else if (flag === "o")
|
|
151
389
|
latestTime = true;
|
|
152
390
|
else if (flag === "l") {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
391
|
+
fromCrlf = argument[offset + 1] === "l";
|
|
392
|
+
toCrlf = !fromCrlf;
|
|
393
|
+
if (fromCrlf)
|
|
394
|
+
offset++;
|
|
156
395
|
}
|
|
157
396
|
else if (flag === "m")
|
|
158
397
|
move = true;
|
|
@@ -223,15 +462,16 @@ async function parse(scope, limits) {
|
|
|
223
462
|
fail("suffix count limit exceeded");
|
|
224
463
|
}
|
|
225
464
|
else {
|
|
226
|
-
const matches = ["store", "deflate", "bzip2"].filter(name => name.startsWith(value.toLowerCase()));
|
|
465
|
+
const matches = ["store", "deflate", "bzip2", "lzma"].filter(name => name.startsWith(value.toLowerCase()));
|
|
227
466
|
const selected = matches.length === 1 ? matches[0] : undefined;
|
|
228
467
|
if (!selected)
|
|
229
468
|
throw new ZipFailure(16, "Invalid command arguments", "Option -Z (--compression-method): unknown method");
|
|
230
|
-
method = selected
|
|
469
|
+
method = selected;
|
|
231
470
|
}
|
|
232
471
|
break;
|
|
233
472
|
}
|
|
234
473
|
else if (flag === "i" || flag === "x") {
|
|
474
|
+
listOption = offset + 1 === argument.length;
|
|
235
475
|
const patterns = flag === "i" ? includes : excludes;
|
|
236
476
|
const before = patterns.length;
|
|
237
477
|
const append = (pattern) => {
|
|
@@ -239,7 +479,7 @@ async function parse(scope, limits) {
|
|
|
239
479
|
checkPath(pattern, limits);
|
|
240
480
|
if (includes.length + excludes.length >= limits.maxMembers)
|
|
241
481
|
fail("pattern count limit exceeded");
|
|
242
|
-
patterns.push(pattern);
|
|
482
|
+
patterns.push(dosNames ? zipDosName(pattern) : pattern);
|
|
243
483
|
};
|
|
244
484
|
const attached = offset + 1 < argument.length;
|
|
245
485
|
if (attached) {
|
|
@@ -262,19 +502,38 @@ async function parse(scope, limits) {
|
|
|
262
502
|
break;
|
|
263
503
|
}
|
|
264
504
|
else
|
|
265
|
-
throw new ZipFailure(16, "Invalid command arguments", `unsupported option:
|
|
505
|
+
throw new ZipFailure(16, "Invalid command arguments", `unsupported option: -${flag}`);
|
|
266
506
|
}
|
|
507
|
+
commandOptions.push(...args.slice(startIndex + 1, index + 1));
|
|
508
|
+
if (listOption && args[index] !== "@")
|
|
509
|
+
commandOptions.push("@");
|
|
267
510
|
}
|
|
268
|
-
else if (archive === undefined)
|
|
511
|
+
else if (archive === undefined) {
|
|
269
512
|
archive = argument;
|
|
513
|
+
commandPaths.push(original);
|
|
514
|
+
}
|
|
270
515
|
else {
|
|
271
516
|
if (operands.length >= limits.maxMembers)
|
|
272
517
|
fail("operand limit exceeded");
|
|
273
518
|
if (firstOperand < 0)
|
|
274
519
|
firstOperand = index;
|
|
275
520
|
operands.push(argument);
|
|
521
|
+
commandPaths.push(original);
|
|
276
522
|
}
|
|
277
523
|
}
|
|
524
|
+
validateArgumentText();
|
|
525
|
+
if (repair || adjust) {
|
|
526
|
+
if (repair && adjust || junkSfx || grow || move || split !== undefined || operands.length || stdinNames || includes.length || excludes.length || action !== "add" || archiveComment || entryComments || latestTime || filesync || encrypt || showFiles !== undefined || difference || toCrlf || fromCrlf || recursive || recursivePatterns)
|
|
527
|
+
throw new ZipFailure(16, "Invalid command arguments", "repair/adjust cannot be combined with archive modification or selection");
|
|
528
|
+
if (!archive || archive === "-" || repair && (!output || output === "-"))
|
|
529
|
+
throw new ZipFailure(16, "Invalid command arguments", "recovery requires a named source and a separate --out file");
|
|
530
|
+
}
|
|
531
|
+
if (showCommand)
|
|
532
|
+
return { information: "command", command: [context.command, ...commandOptions, ...commandPaths], debug };
|
|
533
|
+
if (showOptions)
|
|
534
|
+
return { information: "options", debug };
|
|
535
|
+
if (verbose && !dotsSet && !dotSize)
|
|
536
|
+
dotSize = 10 * 1024 ** 2;
|
|
278
537
|
if (archive === undefined) {
|
|
279
538
|
if (includes.length || excludes.length)
|
|
280
539
|
throw new ZipFailure(16, "Invalid command arguments", "nothing to select from");
|
|
@@ -294,7 +553,9 @@ async function parse(scope, limits) {
|
|
|
294
553
|
if (action === "add" && archive !== "-" && !operands.length && !stdinNames)
|
|
295
554
|
action = "copy";
|
|
296
555
|
}
|
|
297
|
-
if (
|
|
556
|
+
if (showFiles !== undefined && !operands.length && !stdinNames && (includes.length || excludes.length))
|
|
557
|
+
throw new ZipFailure(16, "Invalid command arguments", "nothing to select from");
|
|
558
|
+
if (action === "copy" && output === undefined && showFiles === undefined)
|
|
298
559
|
throw new ZipFailure(16, "Invalid command arguments", "-U (--copy) requires -O (--out)");
|
|
299
560
|
const names = [];
|
|
300
561
|
if (stdinNames) {
|
|
@@ -325,9 +586,16 @@ async function parse(scope, limits) {
|
|
|
325
586
|
}
|
|
326
587
|
if (recursivePatterns && !names.length && !operands.length)
|
|
327
588
|
throw new ZipFailure(16, "Invalid command arguments", "nothing to select from");
|
|
328
|
-
if (
|
|
589
|
+
if (difference && (archive === "-" || output === undefined || action === "copy" || action === "delete"))
|
|
590
|
+
throw new ZipFailure(16, "Invalid command arguments", "difference archive requires separate output and cannot use delete or copy");
|
|
591
|
+
if (logPath !== undefined && !logPath.slice(logPath.lastIndexOf("/") + 1).includes("."))
|
|
592
|
+
logPath += ".log";
|
|
593
|
+
if (filesync && (action !== "add" || grow))
|
|
329
594
|
throw new ZipFailure(16, "Invalid command arguments", "can't use -d, -f, -u, -U, or -g with filesync -FS\n");
|
|
330
|
-
|
|
595
|
+
const diagnosticArgs = args.map((value, index) => passwordArguments.has(index - defaultsCount) ? "[redacted]" : zipPublicText(value));
|
|
596
|
+
if (split && (archive === "-" || grow))
|
|
597
|
+
throw new ZipFailure(16, "Invalid command arguments", "split output requires a file and cannot grow");
|
|
598
|
+
return { repair, adjust, split, splitPause, splitVerbose, splitBell, password, encrypt, ...(aes ? { aes } : {}), args: diagnosticArgs, action, archive, output, recursive, recursivePatterns, noWild, stopAtDirectories, quiet, verbose, showFiles, debug, displayBytes, displayCounts, displayUsize, displayVolume, dotSize, globalDots, junkPaths, dosNames, fifo, omitDirectories, storeLinks: storeLinks && !dosNames, test, testCommand, difference, grow, tempPath, junkSfx, logPath, logAppend, logInfo, mustMatch, filesync, archiveComment, entryComments, latestTime, toCrlf, fromCrlf, move, fromDate, beforeDate, descriptors, zip64, metadata, includes, excludes, level, method, suffixes, operands: [...names, ...operands], firstOperand };
|
|
331
599
|
}
|
|
332
600
|
function memberName(path, limits) {
|
|
333
601
|
if (path.startsWith("//") && !path.startsWith("///"))
|
|
@@ -374,13 +642,23 @@ async function filterName(name, selection, includeCount) {
|
|
|
374
642
|
}
|
|
375
643
|
return included;
|
|
376
644
|
}
|
|
377
|
-
async function prepare(scope, parsed, budget) {
|
|
645
|
+
async function prepare(scope, parsed, budget, log, host) {
|
|
378
646
|
const context = scope.context;
|
|
379
647
|
const limits = budget.limits;
|
|
380
648
|
if (parsed.archive === "-" && parsed.action !== "add")
|
|
381
649
|
throw new ZipFailure(16, "Invalid command arguments", "can't use -d, -f, -u, -U, or -g on stdout\n");
|
|
650
|
+
let temporary;
|
|
651
|
+
if (parsed.tempPath !== undefined && parsed.showFiles === undefined) {
|
|
652
|
+
const name = vfsPath(context.cwd, parsed.tempPath);
|
|
653
|
+
checkPath(name, limits);
|
|
654
|
+
const parent = await scope.operation(() => context.fs.realpath(name, { signal: context.signal }));
|
|
655
|
+
const parentStat = await scope.operation(() => context.fs.lstat(parent, { signal: context.signal }));
|
|
656
|
+
if (parentStat.type !== "directory" || !hasIdentity(parentStat))
|
|
657
|
+
fail("ZIP temporary path requires a directory with known backing identity");
|
|
658
|
+
temporary = { name, parent, parentStat };
|
|
659
|
+
}
|
|
382
660
|
let publication;
|
|
383
|
-
if (parsed.archive !== "-") {
|
|
661
|
+
if (parsed.archive !== "-" && parsed.showFiles === undefined) {
|
|
384
662
|
const outputName = vfsPath(context.cwd, parsed.output ?? parsed.archive);
|
|
385
663
|
const parentName = dirname(outputName);
|
|
386
664
|
const parent = await scope.operation(() => context.fs.realpath(parentName, { signal: context.signal }));
|
|
@@ -389,11 +667,17 @@ async function prepare(scope, parsed, budget) {
|
|
|
389
667
|
checkPath(output, limits);
|
|
390
668
|
const existing = await scope.stat(output);
|
|
391
669
|
publication = { output, parentName, parent, parentStat, existing };
|
|
670
|
+
if (temporary)
|
|
671
|
+
publication = { ...publication, stagingName: temporary.name, stagingParent: temporary.parent, stagingParentStat: temporary.parentStat };
|
|
392
672
|
}
|
|
393
|
-
const output = publication?.output;
|
|
673
|
+
const output = publication?.output ?? (parsed.output === undefined ? undefined : vfsPath(context.cwd, parsed.output));
|
|
394
674
|
let input = publication?.output;
|
|
395
675
|
let existing = publication?.existing;
|
|
396
|
-
if (parsed.
|
|
676
|
+
if (parsed.showFiles !== undefined && parsed.archive !== "-") {
|
|
677
|
+
input = vfsPath(context.cwd, parsed.archive);
|
|
678
|
+
existing = await scope.stat(input);
|
|
679
|
+
}
|
|
680
|
+
if (parsed.output !== undefined && parsed.showFiles === undefined) {
|
|
397
681
|
if (parsed.archive === "-") {
|
|
398
682
|
input = undefined;
|
|
399
683
|
existing = undefined;
|
|
@@ -415,7 +699,7 @@ async function prepare(scope, parsed, budget) {
|
|
|
415
699
|
throw new ZipFailure(16, "Invalid command arguments", `--out path must be different than in path: ${parsed.archive}`);
|
|
416
700
|
}
|
|
417
701
|
}
|
|
418
|
-
if ((parsed.action === "copy" || parsed.output !== undefined && parsed.archive !== "-") && !existing)
|
|
702
|
+
if ((parsed.action === "copy" || parsed.showFiles !== undefined && !parsed.operands.length || parsed.output !== undefined && parsed.archive !== "-") && !existing)
|
|
419
703
|
throw new ZipFailure(18, "File not found or no read permission", parsed.archive);
|
|
420
704
|
if (publication?.existing && (publication.existing.type !== "file" || !hasIdentity(publication.existing) || publication.existing.nlink !== 1))
|
|
421
705
|
fail("output archive requires a regular, single-link file with known backing identity");
|
|
@@ -425,46 +709,110 @@ async function prepare(scope, parsed, budget) {
|
|
|
425
709
|
fail("updating archive requires a regular, single-link file with known backing identity; archive aliases are unsupported");
|
|
426
710
|
let archive = { entries: [], comment: new Uint8Array() };
|
|
427
711
|
let originalBytes;
|
|
712
|
+
let inputPaths = [];
|
|
713
|
+
let inputSplit;
|
|
428
714
|
if (existing && input) {
|
|
429
715
|
if (!Number.isSafeInteger(existing.size) || existing.size < 0 || existing.size > limits.maxArchiveBytes)
|
|
430
716
|
fail("archive byte limit exceeded");
|
|
431
717
|
const bytes = await collectBytes(scope.input(input), { maxBytes: limits.maxArchiveBytes, signal: context.signal });
|
|
432
718
|
if (bytes.length !== existing.size)
|
|
433
719
|
fail("archive changed while reading");
|
|
434
|
-
|
|
435
|
-
|
|
720
|
+
const resolved = parsed.repair || parsed.adjust ? { bytes, paths: [input] } : await resolveZipVolumes(scope, input, bytes, host);
|
|
721
|
+
inputPaths = resolved.paths;
|
|
722
|
+
await log?.protect(inputPaths);
|
|
723
|
+
if (resolved.disks && resolved.disks.starts.length > 1)
|
|
724
|
+
inputSplit = Math.max(65536, ...resolved.disks.lengths.slice(0, -1));
|
|
725
|
+
if (publication && parsed.output !== undefined)
|
|
726
|
+
for (const path of inputPaths) {
|
|
727
|
+
const stat = await scope.stat(path);
|
|
728
|
+
if (publication.output === path || stat && publication.existing && sameIdentity(stat, publication.existing))
|
|
729
|
+
fail("ZIP output aliases an input volume");
|
|
730
|
+
}
|
|
731
|
+
if ((parsed.split || resolved.disks) && parsed.output === undefined && parsed.showFiles === undefined)
|
|
732
|
+
throw new ZipFailure(16, "Invalid command arguments", "split archives require a separate --out destination");
|
|
733
|
+
if (parsed.repair || parsed.adjust) {
|
|
734
|
+
if (!publication)
|
|
735
|
+
fail("ZIP repair requires a file destination");
|
|
736
|
+
let outputBytes, partial = false;
|
|
737
|
+
if (parsed.repair) {
|
|
738
|
+
const recovered = await repairZip(bytes, parsed.repair, limits, context.signal, parsed.password);
|
|
739
|
+
partial = recovered.partial;
|
|
740
|
+
outputBytes = await writeZipArchive(recovered.archive, limits, context.signal);
|
|
741
|
+
const verified = await readZipArchive(outputBytes, limits, context.signal);
|
|
742
|
+
for (const entry of verified.entries)
|
|
743
|
+
for await (const chunk of decodeZipEntry(entry, limits, context.signal, parsed.password)) {
|
|
744
|
+
void chunk;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
archive = await readZipArchive(bytes, limits, context.signal, { prefix: true });
|
|
749
|
+
outputBytes = await adjustZipSfx(bytes, archive, limits, context.signal, parsed.password);
|
|
750
|
+
}
|
|
751
|
+
const current = await scope.stat(input);
|
|
752
|
+
if (!current || !unchanged(existing, current))
|
|
753
|
+
fail("archive changed while reading");
|
|
754
|
+
const progress = [];
|
|
755
|
+
if (!parsed.quiet)
|
|
756
|
+
progress.push(parsed.repair ? `Fix archive (-${parsed.repair}) - verified recovered members\n` : "Zip entry offsets adjusted\n");
|
|
757
|
+
if (partial)
|
|
758
|
+
progress.push("\tzip warning: partial recovery; some entries were not recovered\n");
|
|
759
|
+
return { kind: "file", inputSplit, inputPaths, testRequired: parsed.test, publication, bytes: outputBytes, progress, exitCode: 0, moves: [] };
|
|
760
|
+
}
|
|
761
|
+
archive = parsed.junkSfx ? await readZipSfx(bytes, limits, context.signal, parsed.password) : await readZipArchive(resolved.bytes, limits, context.signal, { grow: parsed.grow, ...(resolved.disks ? { disks: resolved.disks } : {}) });
|
|
762
|
+
if (!resolved.disks && (parsed.latestTime || parsed.test))
|
|
436
763
|
originalBytes = bytes;
|
|
437
764
|
const current = await scope.stat(input);
|
|
438
765
|
if (!current || !unchanged(existing, current))
|
|
439
766
|
fail("archive changed while reading");
|
|
440
767
|
}
|
|
768
|
+
const logStat = parsed.logPath === undefined ? undefined : await scope.stat(vfsPath(context.cwd, parsed.logPath));
|
|
441
769
|
const old = new Map(archive.entries.map(entry => [entry.name, entry]));
|
|
770
|
+
const liveProfile = !parsed.split && !existing && !parsed.toCrlf && !parsed.fromCrlf && !parsed.archiveComment && !parsed.entryComments
|
|
771
|
+
&& !parsed.test && !parsed.latestTime && parsed.tempPath === undefined;
|
|
442
772
|
if (!archive.entries.length && !parsed.quiet && (parsed.action === "update" || parsed.action === "freshen"))
|
|
443
|
-
await budget.output(`\tzip warning: ${parsed.archive} not found or empty\n`);
|
|
773
|
+
await budget.output(`\tzip warning: ${zipPublicText(parsed.archive)} not found or empty\n`);
|
|
774
|
+
const dosConvertedNames = new Set();
|
|
444
775
|
const selected = new Map();
|
|
776
|
+
const conversionWarnings = new Map();
|
|
777
|
+
const listed = new Map();
|
|
445
778
|
const moves = new Map();
|
|
446
779
|
const deleted = new Set();
|
|
447
780
|
const synchronized = new Map();
|
|
448
781
|
const commentNames = new Set();
|
|
449
782
|
const selection = new Selection([...parsed.includes, ...parsed.excludes], limits, context.signal, { noWild: parsed.noWild, stopAtDirectories: parsed.stopAtDirectories });
|
|
450
|
-
const recursiveSelection = parsed.recursivePatterns ? new Selection(parsed.operands, limits, context.signal, { noWild: parsed.noWild, stopAtDirectories: parsed.stopAtDirectories, trailingComponents: true }) : undefined;
|
|
783
|
+
const recursiveSelection = parsed.recursivePatterns ? new Selection(parsed.dosNames ? parsed.operands.map(zipDosName) : parsed.operands, limits, context.signal, { noWild: parsed.noWild, stopAtDirectories: parsed.stopAtDirectories, trailingComponents: true }) : undefined;
|
|
451
784
|
const ancestors = [];
|
|
452
785
|
let visits = 0;
|
|
453
786
|
let work = 0;
|
|
454
787
|
let compressedBytes = 0;
|
|
455
|
-
const encodeSelected = async (source, name,
|
|
788
|
+
const encodeSelected = async (source, name, input, attributes, expectedSize) => {
|
|
789
|
+
const live = !(input instanceof Uint8Array);
|
|
790
|
+
let bytes = live ? new Uint8Array() : input;
|
|
791
|
+
const sourceSize = live ? expectedSize ?? 0 : bytes.length;
|
|
792
|
+
const originalBytes = bytes;
|
|
456
793
|
const store = parsed.method === "store" || parsed.level !== 9 && parsed.suffixes.some(suffix => name.endsWith(suffix));
|
|
457
794
|
if (!store && parsed.level === 0 && bytes.length && !attributes.directory && !attributes.symlink)
|
|
458
795
|
throw new ZipFailure(5, "Internal logic error", "bad pack level");
|
|
459
|
-
if (parsed.toCrlf && !attributes.directory && !attributes.symlink) {
|
|
796
|
+
if ((parsed.toCrlf || parsed.fromCrlf) && !attributes.directory && !attributes.symlink) {
|
|
797
|
+
if (parsed.fromCrlf && parsed.method === "bzip2" && !store)
|
|
798
|
+
fail("from-crlf BZIP2 native read profile unsupported");
|
|
460
799
|
const originalSize = bytes.length;
|
|
461
|
-
bytes = await zipToCrlf(bytes, store, Math.min(limits.maxEntryBytes, limits.maxTotalBytes - budget.totalBytes + originalSize), context.signal);
|
|
800
|
+
bytes = parsed.fromCrlf ? await zipFromCrlf(bytes, store, context.signal, parsed.level) : await zipToCrlf(bytes, store, Math.min(limits.maxEntryBytes, limits.maxTotalBytes - budget.totalBytes + originalSize), context.signal);
|
|
462
801
|
budget.totalBytes += bytes.length - originalSize;
|
|
463
802
|
}
|
|
464
803
|
const level = store ? 0 : parsed.level;
|
|
465
|
-
let entry = await makeZipEntry(name, bytes, attributes, limits, context.signal, level, !store && (parsed.archive === "-" || parsed.descriptors && bytes.length > 0), parsed.method === "
|
|
804
|
+
let entry = await makeZipEntry(name, bytes, attributes, limits, context.signal, level, !store && (parsed.archive === "-" && parsed.tempPath === undefined || parsed.descriptors && bytes.length > 0), parsed.method === "store" ? "deflate" : parsed.method);
|
|
805
|
+
if (live) {
|
|
806
|
+
entry = { ...entry, data: new Uint8Array(), size: expectedSize ?? 0, source: input, level,
|
|
807
|
+
method: store || expectedSize === 0 && parsed.archive !== "-" ? 0 : parsed.method === "lzma" ? 14 : parsed.method === "bzip2" ? 12 : 8, ...(parsed.method === "lzma" && !store && !(expectedSize === 0 && parsed.archive !== "-") ? { flags: 0x802 } : {}), ...(expectedSize === undefined ? {} : { expectedSize }) };
|
|
808
|
+
}
|
|
809
|
+
if (parsed.fromCrlf && sourceSize > 0 && !store && entry.internalAttributes === 0 && !attributes.directory && !attributes.symlink && !parsed.quiet) {
|
|
810
|
+
conversionWarnings.set(name, `\tzip warning: ${bytes === originalBytes ? "has binary so -ll ignored" : "-ll used on binary file - corrupted?"}\n`);
|
|
811
|
+
}
|
|
466
812
|
if (parsed.descriptors)
|
|
467
813
|
entry.descriptors = true;
|
|
814
|
+
if (parsed.zip64 === false)
|
|
815
|
+
entry.zip64 = false;
|
|
468
816
|
if (parsed.zip64 === true || parsed.zip64 === undefined && source === "-")
|
|
469
817
|
entry.zip64 = true;
|
|
470
818
|
const prior = old.get(name);
|
|
@@ -475,7 +823,12 @@ async function prepare(scope, parsed, budget) {
|
|
|
475
823
|
if (entry.data.length > limits.maxArchiveBytes - compressedBytes)
|
|
476
824
|
fail("archive byte limit exceeded");
|
|
477
825
|
compressedBytes += entry.data.length;
|
|
478
|
-
|
|
826
|
+
if (dosConvertedNames.has(name)) {
|
|
827
|
+
entry = { ...entry, mode: attributes.directory ? 0o040755 : 0o100644, versionMadeBy: 30, externalAttributes: attributes.directory ? 16 : 0, flags: (entry.flags ?? 0) & ~0x800 | (prior?.comment?.some(byte => byte >= 128) ? (prior.flags ?? 0) & 0x800 : 0) };
|
|
828
|
+
}
|
|
829
|
+
if (parsed.encryption && !entry.directory)
|
|
830
|
+
entry.encryption = parsed.encryption;
|
|
831
|
+
selected.set(name, { entry, source, sourceSize });
|
|
479
832
|
if (parsed.entryComments)
|
|
480
833
|
commentNames.add(name);
|
|
481
834
|
};
|
|
@@ -490,7 +843,7 @@ async function prepare(scope, parsed, budget) {
|
|
|
490
843
|
if (depth > limits.maxDepth)
|
|
491
844
|
fail("archive recursion depth limit exceeded");
|
|
492
845
|
if (source === "-") {
|
|
493
|
-
const previous = selected.get(name);
|
|
846
|
+
const previous = selected.get(name) ?? listed.get(name);
|
|
494
847
|
if (previous && previous.source !== source)
|
|
495
848
|
throw new ZipFailure(16, "Invalid command arguments", "cannot repeat names in zip file");
|
|
496
849
|
if (previous || !await filterName(name, selection, parsed.includes.length))
|
|
@@ -508,8 +861,14 @@ async function prepare(scope, parsed, budget) {
|
|
|
508
861
|
}
|
|
509
862
|
if (parsed.action === "freshen" && !prior || prior && (parsed.action === "update" || parsed.action === "freshen") && Math.floor(modified.getTime() / 1000) <= Math.floor(prior.modified.getTime() / 1000))
|
|
510
863
|
return;
|
|
511
|
-
|
|
512
|
-
|
|
864
|
+
if (parsed.showFiles !== undefined) {
|
|
865
|
+
await budget.member(0);
|
|
866
|
+
listed.set(name, { name, size: 0, source });
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
const live = liveProfile;
|
|
870
|
+
const bytes = live ? scope.stdin : await collectBytes(scope.stdin, { maxBytes: Math.min(limits.maxEntryBytes, limits.maxTotalBytes - budget.totalBytes), signal: context.signal });
|
|
871
|
+
await budget.member(bytes instanceof Uint8Array ? bytes.length : 0);
|
|
513
872
|
await encodeSelected(source, name, bytes, { modified, mode: 0o010660, directory: false, symlink: false });
|
|
514
873
|
return;
|
|
515
874
|
}
|
|
@@ -545,20 +904,21 @@ async function prepare(scope, parsed, budget) {
|
|
|
545
904
|
commentNames.add(name);
|
|
546
905
|
if (parsed.mustMatch && !storedName) {
|
|
547
906
|
if (!parsed.quiet)
|
|
548
|
-
await budget.output(`\tzip warning: name not matched: ${source}\n`);
|
|
907
|
+
await budget.output(`\tzip warning: name not matched: ${zipPublicText(source)}\n`);
|
|
549
908
|
throw new ZipFailure(18, "File not found or no read permission", source);
|
|
550
909
|
}
|
|
551
910
|
if (!parsed.quiet && !storedName)
|
|
552
|
-
await budget.output(`\tzip warning: name not matched: ${source}\n`);
|
|
911
|
+
await budget.output(`\tzip warning: name not matched: ${zipPublicText(source)}\n`);
|
|
553
912
|
return;
|
|
554
913
|
}
|
|
555
914
|
checkPath(canonical, limits);
|
|
556
915
|
if (canonical === output || canonical === input || (existing && sameIdentity(existing, stat)) || (publication?.existing && sameIdentity(publication.existing, stat)))
|
|
557
916
|
return;
|
|
558
917
|
const symlink = parsed.storeLinks && stat.type === "symlink";
|
|
559
|
-
|
|
918
|
+
const fifoSource = (stat.mode & 0o170000) === 0o010000;
|
|
919
|
+
if (fifoSource && !parsed.fifo || stat.type !== "file" && stat.type !== "directory" && !symlink && !fifoSource) {
|
|
560
920
|
if (!parsed.quiet)
|
|
561
|
-
await budget.output(`\tzip warning: ignoring special file: ${source}\n`);
|
|
921
|
+
await budget.output(`\tzip warning: ignoring special file: ${zipPublicText(source)}\n`);
|
|
562
922
|
return;
|
|
563
923
|
}
|
|
564
924
|
if ((existing || publication?.existing) && !hasIdentity(stat))
|
|
@@ -569,63 +929,101 @@ async function prepare(scope, parsed, budget) {
|
|
|
569
929
|
const dateIncluded = zipDateMatches(new Date(stat.mtimeMs), parsed.fromDate, parsed.beforeDate);
|
|
570
930
|
const nameIncluded = (dateIncluded || parsed.entryComments) && await filterName(name, selection, parsed.includes.length) && (!recursiveSelection || await recursiveSelection.matches(name, true));
|
|
571
931
|
const included = dateIncluded && nameIncluded;
|
|
932
|
+
if (logStat && included && (!hasIdentity(stat) || sameIdentity(logStat, stat))) {
|
|
933
|
+
if (log)
|
|
934
|
+
log.failed = true;
|
|
935
|
+
throw new ZipFailure(16, "Invalid command arguments", "ZIP log may not alias a selected source");
|
|
936
|
+
}
|
|
572
937
|
const sourceName = name;
|
|
573
938
|
if (parsed.junkPaths && !storedName)
|
|
574
939
|
name = directory ? "" : name.slice(name.lastIndexOf("/") + 1);
|
|
940
|
+
if (parsed.dosNames && !storedName && name && included) {
|
|
941
|
+
name = zipDosName(name);
|
|
942
|
+
if (!name || (directory ? name.slice(0, -1) : name).split("/").some(component => !component))
|
|
943
|
+
throw new ZipFailure(16, "Invalid command arguments", "DOS conversion produces an empty component");
|
|
944
|
+
dosConvertedNames.add(name);
|
|
945
|
+
}
|
|
575
946
|
const prior = old.get(name);
|
|
576
947
|
if (parsed.entryComments && prior && nameIncluded)
|
|
577
948
|
commentNames.add(name);
|
|
578
|
-
if (parsed.filesync && name && included && !(directory && parsed.omitDirectories)) {
|
|
949
|
+
if ((parsed.filesync || parsed.difference) && name && included && !(directory && parsed.omitDirectories)) {
|
|
579
950
|
if (synchronized.has(name) && synchronized.get(name) !== source)
|
|
580
951
|
throw new ZipFailure(16, "Invalid command arguments", "cannot repeat names in zip file");
|
|
581
952
|
synchronized.set(name, source);
|
|
582
953
|
}
|
|
583
|
-
|
|
584
|
-
if (!moves.has(path))
|
|
585
|
-
moves.set(path, await inspectZipMoveSource(scope, path, source));
|
|
586
|
-
}
|
|
587
|
-
const current = parsed.filesync && prior && prior.size === (directory ? 0 : stat.size)
|
|
954
|
+
const current = !fifoSource && (parsed.filesync || parsed.difference) && prior && prior.size === (directory ? 0 : stat.size)
|
|
588
955
|
&& Math.ceil(Math.floor(stat.mtimeMs / 1000) / 2) === Math.ceil(Math.floor(prior.modified.getTime() / 1000) / 2);
|
|
589
956
|
const eligible = parsed.action !== "update" && parsed.action !== "freshen"
|
|
590
957
|
|| (prior ? Math.floor(stat.mtimeMs / 1000) > Math.floor(prior.modified.getTime() / 1000) : parsed.action === "update");
|
|
591
|
-
if (
|
|
958
|
+
if (parsed.showFiles === undefined && parsed.move && (!parsed.difference || !current && eligible) && name && included && !(directory && parsed.omitDirectories) && (prior || parsed.action !== "freshen")) {
|
|
959
|
+
if (!moves.has(path))
|
|
960
|
+
moves.set(path, await inspectZipMoveSource(scope, path, source));
|
|
961
|
+
}
|
|
962
|
+
if (name && included && eligible && (!current || parsed.showFiles !== undefined) && !(directory && parsed.omitDirectories)) {
|
|
592
963
|
checkPath(name, limits);
|
|
593
|
-
const previous = selected.get(name);
|
|
964
|
+
const previous = selected.get(name) ?? listed.get(name);
|
|
594
965
|
if (previous && previous.source !== source) {
|
|
595
966
|
if (!parsed.quiet)
|
|
596
|
-
await budget.output(`\tzip warning: first full name: ${previous.source}\n second full name: ${source}\n name in zip file repeated: ${name}\n`);
|
|
967
|
+
await budget.output(`\tzip warning: first full name: ${zipPublicText(previous.source)}\n second full name: ${zipPublicText(source)}\n name in zip file repeated: ${zipPublicText(name)}\n`);
|
|
597
968
|
throw new ZipFailure(16, "Invalid command arguments", "cannot repeat names in zip file");
|
|
598
969
|
}
|
|
599
970
|
if (!previous) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (!context.fs.readlink)
|
|
606
|
-
fail("filesystem does not support reading symbolic links");
|
|
607
|
-
const target = await scope.operation(() => context.fs.readlink(path, { signal: context.signal }));
|
|
608
|
-
if (Buffer.byteLength(target) > stat.size)
|
|
609
|
-
fail(`source changed while reading: ${source}`);
|
|
610
|
-
bytes = Buffer.from(target);
|
|
971
|
+
if (fifoSource && parsed.move)
|
|
972
|
+
fail("FIFO move is unsupported: producer streams have no removable file snapshot");
|
|
973
|
+
await budget.member(directory || fifoSource ? 0 : stat.size);
|
|
974
|
+
if (parsed.showFiles !== undefined) {
|
|
975
|
+
listed.set(name, { name, size: directory ? 0 : stat.size, source });
|
|
611
976
|
}
|
|
612
977
|
else {
|
|
613
|
-
|
|
614
|
-
|
|
978
|
+
const store = parsed.method === "store" || parsed.level !== 9 && parsed.suffixes.some(suffix => name.endsWith(suffix));
|
|
979
|
+
if (fifoSource) {
|
|
980
|
+
const bytes = await collectBytes(scope.input(path, true), { maxBytes: Math.min(limits.maxEntryBytes, limits.maxTotalBytes - budget.totalBytes), signal: context.signal });
|
|
981
|
+
const current = await inspectSource(scope, path, parsed.storeLinks);
|
|
982
|
+
if (current.canonical !== canonical || !unchanged(stat, current.stat))
|
|
983
|
+
fail(`source changed while reading: ${source}`);
|
|
984
|
+
budget.totalBytes += bytes.length;
|
|
985
|
+
await encodeSelected(source, name, bytes, { modified: new Date(stat.mtimeMs), mode: 0o100644, directory: false, symlink: false });
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
if (liveProfile && !directory && !symlink && (parsed.archive === "-" || store || parsed.descriptors)) {
|
|
989
|
+
const input = (async function* () {
|
|
990
|
+
yield* scope.input(path);
|
|
991
|
+
const current = await inspectSource(scope, path, parsed.storeLinks);
|
|
992
|
+
if (current.canonical !== canonical || !unchanged(stat, current.stat))
|
|
993
|
+
fail(`source changed while reading: ${source}`);
|
|
994
|
+
})();
|
|
995
|
+
await encodeSelected(source, name, input, { modified: new Date(stat.mtimeMs), mode: stat.mode, directory, symlink }, stat.size);
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
let bytes;
|
|
999
|
+
if (directory)
|
|
1000
|
+
bytes = new Uint8Array();
|
|
1001
|
+
else if (symlink) {
|
|
1002
|
+
if (!context.fs.readlink)
|
|
1003
|
+
fail("filesystem does not support reading symbolic links");
|
|
1004
|
+
const target = await scope.operation(() => context.fs.readlink(path, { signal: context.signal }));
|
|
1005
|
+
if (Buffer.byteLength(target) > stat.size)
|
|
1006
|
+
fail(`source changed while reading: ${source}`);
|
|
1007
|
+
bytes = Buffer.from(target);
|
|
615
1008
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
1009
|
+
else {
|
|
1010
|
+
try {
|
|
1011
|
+
bytes = await collectBytes(scope.input(path), { maxBytes: stat.size, signal: context.signal });
|
|
1012
|
+
}
|
|
1013
|
+
catch (error) {
|
|
1014
|
+
context.signal.throwIfAborted();
|
|
1015
|
+
if (parsed.mustMatch && typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "EACCES"))
|
|
1016
|
+
throw new ZipFailure(18, "File not found or no read permission", `was zipping ${source}`);
|
|
1017
|
+
throw error;
|
|
1018
|
+
}
|
|
621
1019
|
}
|
|
1020
|
+
if (!directory && bytes.length !== stat.size)
|
|
1021
|
+
fail(`source changed while reading: ${source}`);
|
|
1022
|
+
const current = await inspectSource(scope, path, parsed.storeLinks);
|
|
1023
|
+
if (current.canonical !== canonical || !unchanged(stat, current.stat))
|
|
1024
|
+
fail(`source changed while reading: ${source}`);
|
|
1025
|
+
await encodeSelected(source, name, bytes, { modified: new Date(stat.mtimeMs), mode: stat.mode, directory, symlink });
|
|
622
1026
|
}
|
|
623
|
-
if (!directory && bytes.length !== stat.size)
|
|
624
|
-
fail(`source changed while reading: ${source}`);
|
|
625
|
-
const current = await inspectSource(scope, path, parsed.storeLinks);
|
|
626
|
-
if (current.canonical !== canonical || !unchanged(stat, current.stat))
|
|
627
|
-
fail(`source changed while reading: ${source}`);
|
|
628
|
-
await encodeSelected(source, name, bytes, { modified: new Date(stat.mtimeMs), mode: stat.mode, directory, symlink });
|
|
629
1027
|
}
|
|
630
1028
|
}
|
|
631
1029
|
if (directory && !storedName && (parsed.recursive || parsed.recursivePatterns)) {
|
|
@@ -657,7 +1055,7 @@ async function prepare(scope, parsed, budget) {
|
|
|
657
1055
|
for (const entry of archive.entries) {
|
|
658
1056
|
if (await operands.matches(entry.name) && await filterName(entry.name, selection, parsed.includes.length) && zipDateMatches(entry.modified, parsed.fromDate, parsed.beforeDate)) {
|
|
659
1057
|
await budget.member(entry.size);
|
|
660
|
-
selected.set(entry.name, { entry, source: entry.name });
|
|
1058
|
+
selected.set(entry.name, { entry, source: entry.name, sourceSize: entry.size });
|
|
661
1059
|
}
|
|
662
1060
|
}
|
|
663
1061
|
}
|
|
@@ -665,7 +1063,7 @@ async function prepare(scope, parsed, budget) {
|
|
|
665
1063
|
if (!parsed.quiet && (parsed.recursive || parsed.archiveComment || parsed.entryComments || parsed.move))
|
|
666
1064
|
await budget.output("\tzip warning: invalid option(s) used with -d; ignored.\n");
|
|
667
1065
|
if (!parsed.quiet && !archive.entries.length)
|
|
668
|
-
await budget.output(`\tzip warning: ${parsed.archive} not found or empty\n`);
|
|
1066
|
+
await budget.output(`\tzip warning: ${zipPublicText(parsed.archive)} not found or empty\n`);
|
|
669
1067
|
const operands = new Selection(parsed.recursivePatterns ? [] : parsed.operands, limits, context.signal, { noWild: parsed.noWild, stopAtDirectories: parsed.stopAtDirectories });
|
|
670
1068
|
if (parsed.operands.length && !parsed.recursivePatterns) {
|
|
671
1069
|
for (const entry of archive.entries) {
|
|
@@ -682,18 +1080,32 @@ async function prepare(scope, parsed, budget) {
|
|
|
682
1080
|
if (!parsed.quiet) {
|
|
683
1081
|
for (const [index, operand] of parsed.operands.entries()) {
|
|
684
1082
|
if (!operands.matched.has(index))
|
|
685
|
-
await budget.output(`\tzip warning: name not matched: ${operand}\n`);
|
|
1083
|
+
await budget.output(`\tzip warning: name not matched: ${zipPublicText(operand)}\n`);
|
|
686
1084
|
}
|
|
687
1085
|
}
|
|
688
1086
|
}
|
|
689
1087
|
else {
|
|
690
|
-
const operands = parsed.operands.length || parsed.action === "add" ? parsed.operands : archive.entries.map(entry => entry.name);
|
|
1088
|
+
const operands = parsed.showFiles !== undefined && !parsed.operands.length ? [] : parsed.operands.length || parsed.action === "add" ? parsed.operands : archive.entries.map(entry => entry.name);
|
|
691
1089
|
if (parsed.recursivePatterns)
|
|
692
1090
|
await visit(".", "", 0);
|
|
693
1091
|
else
|
|
694
1092
|
for (const operand of operands)
|
|
695
1093
|
await visit(operand, memberName(operand, limits), 0);
|
|
696
1094
|
}
|
|
1095
|
+
if (!publication || !(parsed.split ?? inputSplit))
|
|
1096
|
+
await log?.start();
|
|
1097
|
+
if (parsed.showFiles !== undefined) {
|
|
1098
|
+
const contains = !parsed.operands.length && !parsed.includes.length && !parsed.excludes.length;
|
|
1099
|
+
const listing = contains ? archive.entries : parsed.action === "delete" ? archive.entries.filter(entry => deleted.has(entry.name)) : parsed.action === "copy" ? [...selected.values()].map(item => item.entry) : [...listed.values()];
|
|
1100
|
+
const title = contains ? "Archive contains" : parsed.action === "delete" ? "Would Delete" : parsed.action === "freshen" ? "Would Freshen" : parsed.action === "copy" ? "Would Copy" : "Would Add/Update";
|
|
1101
|
+
if (!parsed.quiet && parsed.showFiles) {
|
|
1102
|
+
await budget.output(`${title}:\n`);
|
|
1103
|
+
for (const entry of listing)
|
|
1104
|
+
await budget.output(` ${escapeText(zipPublicText(entry.name), "display")}\n`);
|
|
1105
|
+
}
|
|
1106
|
+
await budget.output(`Total ${listing.length} entries (${listing.reduce((total, entry) => total + entry.size, 0)} bytes)\n`);
|
|
1107
|
+
return { kind: "current", exitCode: 0, moves: [] };
|
|
1108
|
+
}
|
|
697
1109
|
if (parsed.filesync) {
|
|
698
1110
|
if (!synchronized.size)
|
|
699
1111
|
throw new ZipFailure(12, "Nothing to do!", parsed.archive);
|
|
@@ -708,15 +1120,16 @@ async function prepare(scope, parsed, budget) {
|
|
|
708
1120
|
}
|
|
709
1121
|
}
|
|
710
1122
|
const changed = selected.size > 0 || deleted.size > 0;
|
|
1123
|
+
const testRequired = parsed.test && !(parsed.filesync && !changed);
|
|
711
1124
|
let exitCode = 0;
|
|
712
|
-
if (!selected.size && !((editComment || editEntries || parsed.test) && archive.entries.length) && (parsed.action === "freshen" || parsed.action === "update" && (existing || !parsed.includes.length))) {
|
|
1125
|
+
if (!parsed.difference && !selected.size && !((editComment || editEntries || parsed.junkSfx || parsed.test) && archive.entries.length) && (parsed.action === "freshen" || parsed.action === "update" && (existing || !parsed.includes.length))) {
|
|
713
1126
|
if (!parsed.latestTime || !archive.entries.length)
|
|
714
1127
|
return moves.size ? { kind: "current", exitCode: 12, moves: [...moves.values()] } : undefined;
|
|
715
1128
|
exitCode = 12;
|
|
716
1129
|
}
|
|
717
1130
|
if (parsed.latestTime && !changed && !archive.entries.length && parsed.archive !== "-")
|
|
718
1131
|
throw new ZipFailure(13, "Missing or empty zip file", parsed.archive);
|
|
719
|
-
if (!selected.size && !deleted.size && !((editComment || editEntries || parsed.latestTime || parsed.test) && archive.entries.length) && (parsed.action === "delete" || parsed.action === "copy" || parsed.recursivePatterns || parsed.fromDate !== undefined || parsed.beforeDate !== undefined || !parsed.includes.length)) {
|
|
1132
|
+
if (!parsed.difference && !selected.size && !deleted.size && !((editComment || editEntries || parsed.junkSfx || parsed.latestTime || parsed.test) && archive.entries.length) && (parsed.action === "delete" || parsed.action === "copy" || parsed.recursivePatterns || parsed.fromDate !== undefined || parsed.beforeDate !== undefined || !parsed.includes.length)) {
|
|
720
1133
|
const detail = parsed.action !== "delete" && parsed.recursive && parsed.firstOperand >= 0
|
|
721
1134
|
? `try: zip ${parsed.args.slice(0, parsed.firstOperand).join(" ")} . -i ${parsed.args.slice(parsed.firstOperand).join(" ")}`
|
|
722
1135
|
: parsed.archive;
|
|
@@ -734,22 +1147,49 @@ async function prepare(scope, parsed, budget) {
|
|
|
734
1147
|
progressBytes += size;
|
|
735
1148
|
progress.push(message);
|
|
736
1149
|
};
|
|
1150
|
+
const counted = parsed.action === "delete" ? archive.entries.filter(entry => deleted.has(entry.name)) : [...selected.values()].map(item => item.entry);
|
|
1151
|
+
let remainingBytes = counted.reduce((total, entry) => total + (parsed.action === "delete" || parsed.action === "copy" ? entry.data.length : selected.get(entry.name)?.sourceSize ?? entry.size), 0);
|
|
1152
|
+
let doneBytes = 0;
|
|
1153
|
+
let done = 0;
|
|
1154
|
+
const stats = (entry) => {
|
|
1155
|
+
let prefix = parsed.displayVolume ? "1>1: " : "";
|
|
1156
|
+
if (parsed.displayCounts)
|
|
1157
|
+
prefix += `${String(done).padStart(3)}/${String(counted.length - done).padStart(3)} `;
|
|
1158
|
+
if (parsed.displayBytes)
|
|
1159
|
+
prefix += `[${zipDisplaySize(doneBytes).padStart(4)}/${zipDisplaySize(remainingBytes).padStart(4)}] `;
|
|
1160
|
+
const size = parsed.action === "delete" || parsed.action === "copy" ? entry.data.length : selected.get(entry.name)?.sourceSize ?? entry.size;
|
|
1161
|
+
done++;
|
|
1162
|
+
doneBytes += size;
|
|
1163
|
+
remainingBytes -= size;
|
|
1164
|
+
return prefix;
|
|
1165
|
+
};
|
|
737
1166
|
const append = (entry, update) => {
|
|
738
|
-
|
|
739
|
-
|
|
1167
|
+
if (parsed.quiet)
|
|
1168
|
+
return;
|
|
1169
|
+
const compressedSize = entry.compressedSize ?? entry.data.length;
|
|
1170
|
+
const percentage = entry.size ? Math.trunc((Math.trunc(200 * (entry.size - compressedSize) / entry.size) + 1) / 2) : 0;
|
|
1171
|
+
const prefix = stats(entry);
|
|
1172
|
+
const usize = parsed.displayUsize ? ` (${zipDisplaySize(selected.get(entry.name)?.sourceSize ?? entry.size)})` : "";
|
|
1173
|
+
const verbose = parsed.verbose ? `${entry.method === 0 ? "" : " "}\t(in=${entry.size}) (out=${compressedSize})` : "";
|
|
1174
|
+
const dotCount = !parsed.globalDots && parsed.dotSize ? Math.floor(entry.size / parsed.dotSize) : 0;
|
|
1175
|
+
if (dotCount > limits.maxTextBytes - budget.textBytes - progressBytes)
|
|
1176
|
+
fail("text output limit exceeded");
|
|
1177
|
+
const dots = ".".repeat(dotCount);
|
|
1178
|
+
const warning = conversionWarnings.get(entry.name);
|
|
1179
|
+
queue(`${prefix}${update ? parsed.action === "freshen" ? "freshening:" : "updating:" : " adding:"} ${escapeText(zipPublicText(entry.name), "display")}${usize}${verbose}${warning ? `\n${warning}` : ""} ${dots ? `${dots} ` : ""}(${entry.method === 14 ? `lzma ${percentage}%` : entry.method === 12 ? `bzipped ${percentage}%` : entry.method === 8 ? `deflated ${percentage}%` : "stored 0%"})\n`);
|
|
740
1180
|
};
|
|
741
1181
|
for (const entry of archive.entries) {
|
|
742
1182
|
if (++work > limits.maxPatternSteps)
|
|
743
1183
|
fail("archive work limit exceeded");
|
|
744
1184
|
if (deleted.has(entry.name)) {
|
|
745
|
-
queue(
|
|
1185
|
+
queue(`${stats(entry)}deleting: ${escapeText(zipPublicText(entry.name), "display")}\n`);
|
|
746
1186
|
continue;
|
|
747
1187
|
}
|
|
748
1188
|
const replacement = selected.get(entry.name);
|
|
749
1189
|
if (parsed.action === "copy") {
|
|
750
1190
|
if (replacement) {
|
|
751
1191
|
entries.push(replacement.entry);
|
|
752
|
-
queue(
|
|
1192
|
+
queue(`${stats(entry)} copying: ${escapeText(zipPublicText(entry.name), "display")}\n`);
|
|
753
1193
|
selected.delete(entry.name);
|
|
754
1194
|
}
|
|
755
1195
|
continue;
|
|
@@ -759,15 +1199,39 @@ async function prepare(scope, parsed, budget) {
|
|
|
759
1199
|
append(replacement.entry, true);
|
|
760
1200
|
selected.delete(entry.name);
|
|
761
1201
|
}
|
|
762
|
-
else {
|
|
1202
|
+
else if (!parsed.difference) {
|
|
763
1203
|
await budget.member(entry.size);
|
|
764
1204
|
entries.push(entry);
|
|
765
1205
|
}
|
|
766
1206
|
}
|
|
1207
|
+
if (parsed.grow && (parsed.zip64 !== undefined || deleted.size || archive.entries.some(entry => !entries.includes(entry)))) {
|
|
1208
|
+
for (const entry of archive.entries)
|
|
1209
|
+
zipGrowRecords.delete(entry);
|
|
1210
|
+
}
|
|
1211
|
+
const pendingProgress = [];
|
|
767
1212
|
for (const { entry } of selected.values()) {
|
|
768
1213
|
entries.push(entry);
|
|
769
|
-
|
|
1214
|
+
if (entry.source)
|
|
1215
|
+
pendingProgress.push(entry);
|
|
1216
|
+
else
|
|
1217
|
+
append(entry, false);
|
|
770
1218
|
}
|
|
1219
|
+
const summary = () => {
|
|
1220
|
+
if (!parsed.verbose || parsed.quiet)
|
|
1221
|
+
return;
|
|
1222
|
+
const size = entries.reduce((total, entry) => total + entry.size, 0);
|
|
1223
|
+
const compressed = entries.reduce((total, entry) => total + (entry.compressedSize ?? entry.data.length), 0);
|
|
1224
|
+
queue(`total bytes=${size}, compressed=${compressed} -> ${size ? Math.round(100 * (size - compressed) / size) : 0}% savings\n`);
|
|
1225
|
+
};
|
|
1226
|
+
const finishProgress = () => {
|
|
1227
|
+
if (!pendingProgress.length)
|
|
1228
|
+
return;
|
|
1229
|
+
for (const entry of pendingProgress.splice(0))
|
|
1230
|
+
append(entry, false);
|
|
1231
|
+
summary();
|
|
1232
|
+
};
|
|
1233
|
+
if (!pendingProgress.length)
|
|
1234
|
+
summary();
|
|
771
1235
|
if (!entries.length)
|
|
772
1236
|
queue("\tzip warning: zip file empty\n");
|
|
773
1237
|
let comment = archive.comment;
|
|
@@ -783,7 +1247,7 @@ async function prepare(scope, parsed, budget) {
|
|
|
783
1247
|
if (!commentNames.has(entry.name))
|
|
784
1248
|
continue;
|
|
785
1249
|
if (!parsed.quiet)
|
|
786
|
-
await budget.output(`Enter comment for ${entry.name}:\n`);
|
|
1250
|
+
await budget.output(`Enter comment for ${zipPublicText(entry.name)}:\n`);
|
|
787
1251
|
const line = await commentInput.readLine();
|
|
788
1252
|
if (line !== undefined)
|
|
789
1253
|
entries[index] = setZipEntryComment(entry, line.at(-1) === 10 ? line.subarray(0, -1) : line, limits);
|
|
@@ -802,7 +1266,9 @@ async function prepare(scope, parsed, budget) {
|
|
|
802
1266
|
comment = await readZipComment(commentInput, limits, context.signal);
|
|
803
1267
|
}
|
|
804
1268
|
if (!publication)
|
|
805
|
-
return { kind: "stream", archive: { entries, comment }, progress, moves: [...moves.values()] };
|
|
1269
|
+
return { kind: "stream", temporary, archive: { entries, comment }, progress, finishProgress, moves: [...moves.values()] };
|
|
1270
|
+
if (entries.some(entry => entry.source))
|
|
1271
|
+
return { kind: "staged-stream", publication, archive: { entries, comment }, progress, finishProgress, exitCode, moves: [...moves.values()] };
|
|
806
1272
|
let latestWarning;
|
|
807
1273
|
if (parsed.latestTime) {
|
|
808
1274
|
const mtimeMs = await zipLatestTime(entries, context.signal);
|
|
@@ -813,44 +1279,37 @@ async function prepare(scope, parsed, budget) {
|
|
|
813
1279
|
else
|
|
814
1280
|
publication = { ...publication, mtimeMs };
|
|
815
1281
|
}
|
|
816
|
-
const bytes = originalBytes && !changed && !editComment && !editEntries ? originalBytes
|
|
817
|
-
: await writeZipArchive({ entries, comment }, limits, context.signal, false, parsed.zip64 === true);
|
|
818
|
-
if (
|
|
1282
|
+
const bytes = originalBytes && !changed && !editComment && !editEntries && !parsed.junkSfx && !parsed.difference ? originalBytes
|
|
1283
|
+
: await writeZipArchive({ entries, comment }, limits, context.signal, false, parsed.zip64 === true, parsed.zip64 !== false);
|
|
1284
|
+
if (testRequired && parsed.archive !== "-") {
|
|
819
1285
|
for (const message of progress)
|
|
820
1286
|
await budget.output(message);
|
|
821
1287
|
progress.length = 0;
|
|
822
1288
|
progressBytes = 0;
|
|
823
|
-
try {
|
|
824
|
-
const tested = await readZipArchive(bytes, limits, context.signal);
|
|
825
|
-
if (!tested.entries.length)
|
|
826
|
-
fail("empty ZIP archive");
|
|
827
|
-
let decoded = 0;
|
|
828
|
-
for (const entry of tested.entries) {
|
|
829
|
-
for await (const chunk of decodeZipEntry(entry, limits, context.signal)) {
|
|
830
|
-
if (chunk.length > limits.maxTotalBytes - decoded)
|
|
831
|
-
fail("actual decompressed byte limit exceeded");
|
|
832
|
-
decoded += chunk.length;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
catch {
|
|
837
|
-
context.signal.throwIfAborted();
|
|
838
|
-
if (!parsed.quiet)
|
|
839
|
-
await budget.output(`test of ${parsed.archive} FAILED\n`);
|
|
840
|
-
throw new ZipFailure(8, "Zip file invalid, could not spawn unzip, or wrong unzip", "original files unmodified");
|
|
841
|
-
}
|
|
842
|
-
if (!parsed.quiet) {
|
|
843
|
-
queue(`test of ${parsed.archive} OK\n`);
|
|
844
|
-
}
|
|
845
1289
|
}
|
|
846
1290
|
if (latestWarning !== undefined)
|
|
847
1291
|
queue(latestWarning);
|
|
848
|
-
if (!changed && originalBytes && !editComment && !editEntries && (parsed.output === undefined || parsed.action === "copy" && parsed.test) && (latestWarning !== undefined || parsed.test && !parsed.latestTime)) {
|
|
1292
|
+
if (!changed && originalBytes && !editComment && !editEntries && !parsed.junkSfx && !parsed.difference && (parsed.output === undefined || parsed.action === "copy" && parsed.test) && (latestWarning !== undefined || parsed.test && !parsed.latestTime)) {
|
|
1293
|
+
if (testRequired && input)
|
|
1294
|
+
await testZipCommand(scope, parsed.testCommand, input, budget, parsed.archive, parsed.quiet, parsed.password);
|
|
849
1295
|
for (const message of progress)
|
|
850
1296
|
await budget.output(message);
|
|
851
1297
|
return { kind: "current", exitCode, moves: [...moves.values()] };
|
|
852
1298
|
}
|
|
853
|
-
|
|
1299
|
+
const sourcePaths = [];
|
|
1300
|
+
if ((parsed.split ?? inputSplit) && parsed.action !== "copy" && parsed.action !== "delete")
|
|
1301
|
+
for (const value of selected.values()) {
|
|
1302
|
+
if (value.source === "-")
|
|
1303
|
+
continue;
|
|
1304
|
+
const path = vfsPath(context.cwd, value.source);
|
|
1305
|
+
if (value.entry.symlink) {
|
|
1306
|
+
const parent = await scope.operation(() => context.fs.realpath(dirname(path), { signal: context.signal }));
|
|
1307
|
+
sourcePaths.push(`${parent === "/" ? "" : parent}/${path.slice(path.lastIndexOf("/") + 1)}`);
|
|
1308
|
+
}
|
|
1309
|
+
else
|
|
1310
|
+
sourcePaths.push(await scope.operation(() => context.fs.realpath(path, { signal: context.signal })));
|
|
1311
|
+
}
|
|
1312
|
+
return { kind: "file", inputSplit, inputPaths: [...inputPaths, ...sourcePaths], testRequired, publication, bytes, progress, exitCode, moves: [...moves.values()] };
|
|
854
1313
|
}
|
|
855
1314
|
export function createZipCommand(options = {}) {
|
|
856
1315
|
const limits = settings(options);
|
|
@@ -859,54 +1318,196 @@ export function createZipCommand(options = {}) {
|
|
|
859
1318
|
const scope = new ZipScope(original, limits);
|
|
860
1319
|
const context = scope.context;
|
|
861
1320
|
let budget = new Budget(context, limits);
|
|
1321
|
+
let log;
|
|
862
1322
|
try {
|
|
863
|
-
|
|
1323
|
+
let parsed = await parse(scope, limits, options.zip);
|
|
864
1324
|
if ("information" in parsed) {
|
|
865
|
-
|
|
1325
|
+
if (parsed.debug)
|
|
1326
|
+
await budget.output("sd: Command line read\n");
|
|
1327
|
+
if (parsed.information === "command") {
|
|
1328
|
+
await budget.output("command line:\n");
|
|
1329
|
+
let redactNext = false;
|
|
1330
|
+
for (const argument of parsed.command ?? []) {
|
|
1331
|
+
const value = zipPublicText(redactNext ? "[redacted]" : argument);
|
|
1332
|
+
redactNext = zipPasswordArgument(argument) === "separate";
|
|
1333
|
+
const escaped = escapeText(value, "display", size => { if (size > limits.maxTextBytes - budget.textBytes)
|
|
1334
|
+
fail("text output limit exceeded"); }).split("'").join("'\\''");
|
|
1335
|
+
await budget.output(`'${escaped}' `);
|
|
1336
|
+
}
|
|
1337
|
+
await budget.output("\n\nzip error: Interrupted (show command line)\n");
|
|
1338
|
+
return { exitCode: 9 };
|
|
1339
|
+
}
|
|
1340
|
+
if (parsed.information === "options") {
|
|
1341
|
+
await budget.output("available options:\nVirtual implementation; unsupported native options are omitted.\n sh long val neg\n");
|
|
1342
|
+
for (const [name, short] of Object.entries(zipLongOptions)) {
|
|
1343
|
+
const value = ["i", "x"].includes(short) ? "list" : ["s", "P", "n", "Z", "t", "tt", "O", "ds", "b", "lf", "TT"].includes(short) ? "req" : "";
|
|
1344
|
+
await budget.output(` ${(short === "version" ? "" : short).padEnd(3)} ${name.padEnd(20)} ${value.padEnd(4)} ${zipNegatableOptions.has(short) ? "neg" : ""}\n`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
else
|
|
1348
|
+
await budget.output(parsed.information === "more-help" ? zipExtendedHelp : parsed.information === "version" ? zipVersion : parsed.information === "license" ? zipLicense : zipHelp);
|
|
866
1349
|
return { exitCode: 0 };
|
|
867
1350
|
}
|
|
868
|
-
if (parsed.
|
|
869
|
-
|
|
870
|
-
|
|
1351
|
+
if (parsed.logPath !== undefined) {
|
|
1352
|
+
log = new ZipLog(scope, parsed.logInfo);
|
|
1353
|
+
await log.open(parsed.logPath, parsed.logAppend, [parsed.archive, parsed.output, ...(parsed.action === "copy" || parsed.action === "delete" ? [] : parsed.operands)].filter((path) => path !== undefined));
|
|
1354
|
+
}
|
|
1355
|
+
const quietDisplay = parsed.quiet;
|
|
1356
|
+
if (log && quietDisplay)
|
|
1357
|
+
parsed = { ...parsed, quiet: false };
|
|
1358
|
+
const progressContext = log ? { ...context, stdout: log.sink(context.stdout, quietDisplay), stderr: log.sink(context.stderr, quietDisplay) } : context;
|
|
1359
|
+
budget = new Budget(parsed.archive === "-" ? { ...progressContext, stdout: log ? log.sink(context.stderr, quietDisplay) : context.stderr } : progressContext, limits);
|
|
1360
|
+
if (parsed.encrypt) {
|
|
1361
|
+
if (parsed.password === undefined)
|
|
1362
|
+
parsed.password = await scope.operation(() => readZipPassword(options.zipHost, limits.maxArgumentBytes, context.signal, true));
|
|
1363
|
+
if (log)
|
|
1364
|
+
log.password = parsed.password;
|
|
1365
|
+
parsed.encryption = { ...(parsed.aes ? { aes: parsed.aes } : {}), password: parsed.password, entropy: (length, signal) => scope.operation(() => {
|
|
1366
|
+
if (!options.zipHost?.entropy)
|
|
1367
|
+
throw new ZipHostFailure("entropy");
|
|
1368
|
+
return options.zipHost.entropy(length, signal);
|
|
1369
|
+
}) };
|
|
1370
|
+
}
|
|
1371
|
+
if (parsed.debug)
|
|
1372
|
+
await budget.output("sd: Command line read\nsd: Reading virtual archive and selecting files\n");
|
|
1373
|
+
if (parsed.split && parsed.splitPause && !options.zipHost?.volumePrompt)
|
|
1374
|
+
throw new ZipFailure(16, "Invalid command arguments", "split pause requires an explicit volume prompt capability");
|
|
1375
|
+
const prepared = await prepare(scope, parsed, budget, log, options.zipHost);
|
|
871
1376
|
if (!prepared)
|
|
872
1377
|
return { exitCode: 12 };
|
|
873
1378
|
if (prepared.kind === "current") {
|
|
874
1379
|
await removeZipSources(scope, prepared.moves, budget, parsed.quiet);
|
|
875
1380
|
return { exitCode: prepared.exitCode };
|
|
876
1381
|
}
|
|
1382
|
+
if (parsed.debug)
|
|
1383
|
+
await budget.output("sd: Writing virtual archive\n");
|
|
1384
|
+
let globalBytes = 0;
|
|
1385
|
+
const dots = async (size) => {
|
|
1386
|
+
if (!parsed.globalDots || !parsed.dotSize)
|
|
1387
|
+
return;
|
|
1388
|
+
if (!globalBytes)
|
|
1389
|
+
await budget.output(" ");
|
|
1390
|
+
const count = Math.floor((globalBytes + size) / parsed.dotSize) - Math.floor(globalBytes / parsed.dotSize);
|
|
1391
|
+
globalBytes += size;
|
|
1392
|
+
for (let index = 0; index < count; index++)
|
|
1393
|
+
await budget.output(".");
|
|
1394
|
+
};
|
|
877
1395
|
if (prepared.kind === "file") {
|
|
878
1396
|
const publication = prepared.publication;
|
|
879
1397
|
if (!publication)
|
|
880
1398
|
fail("ZIP missing file publication");
|
|
881
|
-
await
|
|
1399
|
+
await dots(prepared.bytes.length);
|
|
1400
|
+
const split = parsed.split ?? prepared.inputSplit;
|
|
1401
|
+
if (split) {
|
|
1402
|
+
if (parsed.splitPause && !options.zipHost?.volumePrompt)
|
|
1403
|
+
throw new ZipFailure(16, "Invalid command arguments", "split pause requires an explicit volume prompt capability");
|
|
1404
|
+
const parts = await splitZipVolumes(prepared.bytes, split, limits, context.signal);
|
|
1405
|
+
await log?.protect(parts.map((_, disk) => volumeName(publication.output, disk, parts.length)));
|
|
1406
|
+
await log?.start();
|
|
1407
|
+
if (prepared.testRequired) {
|
|
1408
|
+
await scope.operation(() => stageZip(scope, { ...publication, bytes: prepared.bytes, reservedPath: publication.output }, staging => testZipCommand(scope, parsed.testCommand, staging.file.path, budget, parsed.archive, parsed.quiet, parsed.password)));
|
|
1409
|
+
}
|
|
1410
|
+
await scope.operation(() => publishZipVolumes(scope, publication, parts, prepared.inputPaths, async (path, disk) => {
|
|
1411
|
+
if (parsed.splitVerbose)
|
|
1412
|
+
await budget.output(`split ${disk + 1}/${parts.length}: ${zipPublicText(path)} (${parts[disk].length} bytes)\n`);
|
|
1413
|
+
if (disk && parsed.splitPause) {
|
|
1414
|
+
if (parsed.splitBell)
|
|
1415
|
+
await budget.output("\u0007");
|
|
1416
|
+
if (!await scope.operation(() => options.zipHost.volumePrompt({ path, disk, disks: parts.length, signal: context.signal })))
|
|
1417
|
+
fail("ZIP split volume prompt cancelled");
|
|
1418
|
+
}
|
|
1419
|
+
}));
|
|
1420
|
+
}
|
|
1421
|
+
else
|
|
1422
|
+
await writeFileOutput(context, prepared.bytes, () => scope.operation(() => publishZip(scope, { ...publication, bytes: prepared.bytes, ...(prepared.testRequired ? { validate: (path) => testZipCommand(scope, parsed.testCommand, path, budget, parsed.archive, parsed.quiet, parsed.password) } : {}) })));
|
|
882
1423
|
for (const message of prepared.progress)
|
|
883
1424
|
await budget.output(message);
|
|
884
1425
|
}
|
|
885
|
-
else {
|
|
1426
|
+
else if (prepared.kind === "staged-stream") {
|
|
1427
|
+
const source = (async function* () {
|
|
1428
|
+
for await (const chunk of streamZipArchive(prepared.archive, limits, context.signal, true, parsed.zip64 === true, parsed.zip64 !== false)) {
|
|
1429
|
+
yield chunk;
|
|
1430
|
+
try {
|
|
1431
|
+
await dots(chunk.length);
|
|
1432
|
+
}
|
|
1433
|
+
catch (error) {
|
|
1434
|
+
void scope.closeInputs().catch(() => { });
|
|
1435
|
+
throw error;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
prepared.finishProgress();
|
|
1439
|
+
})();
|
|
1440
|
+
await scope.operation(() => publishZip(scope, { ...prepared.publication, source }));
|
|
1441
|
+
prepared.finishProgress();
|
|
886
1442
|
for (const message of prepared.progress)
|
|
887
1443
|
await budget.output(message);
|
|
1444
|
+
}
|
|
1445
|
+
else {
|
|
888
1446
|
const output = createOutputOperation(context, context.stdout);
|
|
889
1447
|
try {
|
|
890
|
-
|
|
891
|
-
|
|
1448
|
+
const source = streamZipArchive(prepared.archive, limits, output.signal, !prepared.temporary || parsed.descriptors, parsed.zip64 === true, parsed.zip64 !== false);
|
|
1449
|
+
const emit = async (input) => {
|
|
1450
|
+
for await (const chunk of input) {
|
|
1451
|
+
try {
|
|
1452
|
+
await writeBytes(output.output, chunk, output.signal);
|
|
1453
|
+
await dots(chunk.length);
|
|
1454
|
+
}
|
|
1455
|
+
catch (error) {
|
|
1456
|
+
void scope.closeInputs().catch(() => { });
|
|
1457
|
+
throw error;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
if (prepared.temporary) {
|
|
1462
|
+
const temporary = prepared.temporary;
|
|
1463
|
+
await scope.operation(() => stageZip(scope, { ...temporary, source }, async (staging) => {
|
|
1464
|
+
const current = await scope.operation(() => context.fs.realpath(temporary.name, { signal: context.signal }));
|
|
1465
|
+
if (current !== temporary.parent)
|
|
1466
|
+
fail("ZIP temporary path changed before output");
|
|
1467
|
+
await emit(scope.input(staging.file.path));
|
|
1468
|
+
}));
|
|
892
1469
|
}
|
|
1470
|
+
else
|
|
1471
|
+
await emit(source);
|
|
893
1472
|
}
|
|
894
1473
|
finally {
|
|
895
1474
|
await output.close();
|
|
896
1475
|
}
|
|
1476
|
+
prepared.finishProgress();
|
|
1477
|
+
for (const message of prepared.progress)
|
|
1478
|
+
await budget.output(message);
|
|
897
1479
|
}
|
|
1480
|
+
if (parsed.globalDots)
|
|
1481
|
+
await budget.output("\n");
|
|
1482
|
+
if (parsed.debug)
|
|
1483
|
+
await budget.output("sd: Virtual archive complete\n");
|
|
898
1484
|
await removeZipSources(scope, prepared.moves, budget, parsed.quiet);
|
|
899
|
-
return { exitCode: prepared.kind === "file" ? prepared.exitCode : 0 };
|
|
1485
|
+
return { exitCode: prepared.kind === "file" || prepared.kind === "staged-stream" ? prepared.exitCode : 0 };
|
|
900
1486
|
}
|
|
901
1487
|
catch (error) {
|
|
902
1488
|
original.signal.throwIfAborted();
|
|
903
1489
|
context.signal.throwIfAborted();
|
|
1490
|
+
// Incomplete selection has not proved the log is disjoint from sources.
|
|
1491
|
+
// Report to the screen rather than mutating an unvisited source file.
|
|
1492
|
+
if (log && !log.started)
|
|
1493
|
+
log.failed = true;
|
|
1494
|
+
if (error instanceof ZipHostFailure && (error.code === "password-empty" || error.code === "password-confirm")) {
|
|
1495
|
+
await budget.output(`\nzip error: Invalid command arguments (${error.message})\n`);
|
|
1496
|
+
return { exitCode: 16 };
|
|
1497
|
+
}
|
|
904
1498
|
if (error instanceof ZipFailure) {
|
|
905
|
-
|
|
1499
|
+
const diagnostic = `\nzip error: ${error.label} (${zipPublicText(error.message)})\n`;
|
|
1500
|
+
if (log?.failed)
|
|
1501
|
+
await writeBytes(context.stderr, Buffer.from(diagnostic).subarray(0, limits.maxDiagnosticBytes), context.signal);
|
|
1502
|
+
else
|
|
1503
|
+
await budget.output(diagnostic);
|
|
906
1504
|
return { exitCode: error.status };
|
|
907
1505
|
}
|
|
908
|
-
|
|
909
|
-
|
|
1506
|
+
if (log?.failed)
|
|
1507
|
+
budget = new Budget(context, limits);
|
|
1508
|
+
const detail = display(publicDiagnosticMessage(error, context.onInternalError).slice(0, 1024));
|
|
1509
|
+
const message = escapeText(error instanceof ZipHostFailure ? detail : zipPublicText(detail), "diagnostic");
|
|
1510
|
+
await writeBytes(log && !log.failed ? log.sink(context.stderr) : context.stderr, Buffer.from(`zip: ${message}\n`).subarray(0, limits.maxDiagnosticBytes), context.signal);
|
|
910
1511
|
return { exitCode: 2 };
|
|
911
1512
|
}
|
|
912
1513
|
finally {
|