dmg-builder 26.0.17 → 26.0.19
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/out/dmg.d.ts +33 -1
- package/out/dmg.js +7 -170
- package/out/dmg.js.map +1 -1
- package/out/dmgUtil.d.ts +16 -2
- package/out/dmgUtil.js +152 -1
- package/out/dmgUtil.js.map +1 -1
- package/out/hdiuil.js +7 -2
- package/out/hdiuil.js.map +1 -1
- package/package.json +3 -4
- package/vendor/biplist/__init__.py +75 -75
- package/vendor/dmgbuild/__init__.py +5 -0
- package/vendor/dmgbuild/__main__.py +67 -0
- package/vendor/dmgbuild/badge.py +104 -78
- package/vendor/dmgbuild/colors.py +239 -229
- package/vendor/dmgbuild/core.py +945 -274
- package/vendor/dmgbuild/licensing.py +329 -0
- package/vendor/dmgbuild/resources/builtin-arrow.tiff +0 -0
- package/vendor/ds_store/__init__.py +3 -1
- package/vendor/ds_store/__main__.py +102 -0
- package/vendor/ds_store/buddy.py +142 -134
- package/vendor/ds_store/store.py +333 -322
- package/vendor/mac_alias/__init__.py +43 -23
- package/vendor/mac_alias/alias.py +334 -224
- package/vendor/mac_alias/bookmark.py +283 -278
- package/vendor/mac_alias/osx.py +406 -302
- package/vendor/mac_alias/utils.py +10 -8
- package/vendor/run_dmgbuild.py +14 -0
package/out/hdiuil.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hdiuil.js","sourceRoot":"","sources":["../src/hdiuil.ts"],"names":[],"mappings":";;;AAmBA,kDAoBC;AAcD,
|
|
1
|
+
{"version":3,"file":"hdiuil.js","sourceRoot":"","sources":["../src/hdiuil.ts"],"names":[],"mappings":";;;AAmBA,kDAoBC;AAcD,0BAOC;AA5DD,+CAA+C;AAE/C;;;;;;;;;;;;;;GAcG;AACU,QAAA,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAEzE,SAAgB,mBAAmB,CAAC,SAAiB;;IACnD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAA;IACjC,MAAM,QAAQ,GAA2B;QACvC,GAAG,EAAE,uDAAuD;QAC5D,GAAG,EAAE,2GAA2G;QAChH,GAAG,EAAE,gEAAgE;QACrE,GAAG,EAAE,oGAAoG;QACzG,GAAG,EAAE,8DAA8D;QACnE,IAAI,EAAE,gFAAgF;QACtF,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,+EAA+E;QACrF,KAAK,EAAE,kEAAkE;QACzE,KAAK,EAAE,0EAA0E;QACjF,OAAO,EAAE,qEAAqE;QAC9E,OAAO,EAAE,oFAAoF;QAC7F,OAAO,EAAE,gFAAgF;KAC1F,CAAA;IAED,OAAO,MAAA,QAAQ,CAAC,IAAI,CAAC,mCAAI,uBAAuB,IAAI,+GAA+G,CAAA;AACrK,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC,KAAU,EAAE,EAAE;;IACrD,MAAM,IAAI,GAAG,MAAA,KAAK,CAAC,IAAI,mCAAI,CAAC,CAAC,CAAA;IAC7B,MAAM,MAAM,GAAG,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,QAAQ,EAAE,KAAI,EAAE,CAAA;IAC7C,MAAM,MAAM,GAAG,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,QAAQ,EAAE,KAAI,EAAE,CAAA;IAC7C,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,CAAA;IAE3C,MAAM,SAAS,GAAG,iCAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IAChE,kBAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,kBAAkB,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAE1F,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAEM,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,OAAO,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,IAAA,mBAAI,EAAC,SAAS,EAAE,IAAI,CAAC,EAAE;QACxC,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC;KAC/B,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { exec, log, retry } from \"builder-util\"\n\n/**\n * Table of hdiutil error codes that are transient and can be retried.\n * These codes are typically related to resource availability or temporary issues.\n *\n| Code | Meaning | Why Retry? |\n| ------- | -------------------------------- | ---------------------------------------------------- |\n| `1` | Generic error | Can occur from brief race conditions or temp issues. |\n| `16` | **Resource busy** | Volume is in use — wait and retry often works. |\n| `35` | **Operation timed out** | System delay or timeout — retry after a short delay. |\n| `256` | Volume in use or unmount failure | Same as 16 — usually resolves after retry. |\n| `49153` | Volume not mounted yet | Attach may be too fast — retry after delay. |\n| `-5341` | Disk image too small | Retry *after fixing* with a larger `-size`. |\n| `-5342` | Specified size too small | Same as above — retry if size is corrected. |\n *\n */\nexport const hdiutilTransientExitCodes = new Set([1, 16, 35, 256, 49153])\n\nexport function explainHdiutilError(errorCode: number): string {\n const code = errorCode.toString()\n const messages: Record<string, string> = {\n \"0\": \"Success: The hdiutil command completed without error.\",\n \"1\": \"Generic error: The operation failed, but the reason is not specific. Check command syntax or permissions.\",\n \"2\": \"No such file or directory: Check if the specified path exists.\",\n \"6\": \"Disk image to resize is not currently attached or not recognized as a valid block device by macOS.\",\n \"8\": \"Exec format error: The file might not be a valid disk image.\",\n \"16\": \"Resource busy: The volume is in use. Try closing files or processes and retry.\",\n \"22\": \"Invalid argument: One or more arguments passed to hdiutil are incorrect.\",\n \"35\": \"Operation timed out: The system was too slow or unresponsive. Try again.\",\n \"36\": \"I/O error: There was a problem reading or writing to disk. Check disk health.\",\n \"100\": \"Image-related error: The disk image may be corrupted or invalid.\",\n \"256\": \"Volume is busy or could not be unmounted. Try again after closing files.\",\n \"49153\": \"Volume not mounted yet: The image may not have been fully attached.\",\n \"-5341\": \"Disk image too small: hdiutil could not fit the contents. Increase the image size.\",\n \"-5342\": \"Specified size too small: Disk image creation failed due to insufficient size.\",\n }\n\n return messages[code] ?? `Unknown error (code ${code}): Refer to hdiutil documentation or run with -verbose for details by rerunning with env var DEBUG_DEMB=true.`\n}\n\nconst shouldRetry = (args: string[]) => (error: any) => {\n const code = error.code ?? -1\n const stderr = error.stderr?.toString() || \"\"\n const stdout = error.stdout?.toString() || \"\"\n const output = `${stdout} ${stderr}`.trim()\n\n const willRetry = hdiutilTransientExitCodes.has(code.toString())\n log.warn({ willRetry, args, code, output }, `hdiutil error: ${explainHdiutilError(code)}`)\n\n return willRetry\n}\n\nexport async function hdiUtil(args: string[]): Promise<string | null> {\n return retry(() => exec(\"hdiutil\", args), {\n retries: 5,\n interval: 5000,\n backoff: 2000,\n shouldRetry: shouldRetry(args),\n })\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dmg-builder",
|
|
3
|
-
"version": "26.0.
|
|
3
|
+
"version": "26.0.19",
|
|
4
4
|
"main": "out/dmgUtil.js",
|
|
5
5
|
"author": "Vladimir Krivosheev",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
"fs-extra": "^10.1.0",
|
|
21
21
|
"iconv-lite": "^0.6.2",
|
|
22
22
|
"js-yaml": "^4.1.0",
|
|
23
|
-
"app-builder-lib": "26.0.
|
|
24
|
-
"builder-util": "26.0.
|
|
25
|
-
"builder-util-runtime": "9.3.2"
|
|
23
|
+
"app-builder-lib": "26.0.19",
|
|
24
|
+
"builder-util": "26.0.19"
|
|
26
25
|
},
|
|
27
26
|
"optionalDependencies": {
|
|
28
27
|
"dmg-license": "^1.0.11"
|
|
@@ -15,12 +15,12 @@ Data object. The value must be a string.
|
|
|
15
15
|
|
|
16
16
|
Date values can only be datetime.datetime objects.
|
|
17
17
|
|
|
18
|
-
The exceptions InvalidPlistException and NotBinaryPlistException may be
|
|
18
|
+
The exceptions InvalidPlistException and NotBinaryPlistException may be
|
|
19
19
|
thrown to indicate that the data cannot be serialized or deserialized as
|
|
20
20
|
a binary plist.
|
|
21
21
|
|
|
22
22
|
Plist generation example:
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
from biplist import *
|
|
25
25
|
from datetime import datetime
|
|
26
26
|
plist = {'aKey':'aValue',
|
|
@@ -84,24 +84,24 @@ class Uid(object):
|
|
|
84
84
|
integer = 0
|
|
85
85
|
def __init__(self, integer):
|
|
86
86
|
self.integer = integer
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
def __repr__(self):
|
|
89
89
|
return "Uid(%d)" % self.integer
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
def __eq__(self, other):
|
|
92
92
|
if isinstance(self, Uid) and isinstance(other, Uid):
|
|
93
93
|
return self.integer == other.integer
|
|
94
94
|
return False
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
def __cmp__(self, other):
|
|
97
97
|
return self.integer - other.integer
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
def __lt__(self, other):
|
|
100
100
|
return self.integer < other.integer
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
def __hash__(self):
|
|
103
103
|
return self.integer
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
def __int__(self):
|
|
106
106
|
return int(self.integer)
|
|
107
107
|
|
|
@@ -224,22 +224,22 @@ class PlistReader(object):
|
|
|
224
224
|
currentOffset = 0
|
|
225
225
|
# Used to detect recursive object references.
|
|
226
226
|
offsetsStack = []
|
|
227
|
-
|
|
227
|
+
|
|
228
228
|
def __init__(self, fileOrStream):
|
|
229
229
|
"""Raises NotBinaryPlistException."""
|
|
230
230
|
self.reset()
|
|
231
231
|
self.file = fileOrStream
|
|
232
|
-
|
|
232
|
+
|
|
233
233
|
def parse(self):
|
|
234
234
|
return self.readRoot()
|
|
235
|
-
|
|
235
|
+
|
|
236
236
|
def reset(self):
|
|
237
237
|
self.trailer = None
|
|
238
238
|
self.contents = ''
|
|
239
239
|
self.offsets = []
|
|
240
240
|
self.currentOffset = 0
|
|
241
241
|
self.offsetsStack = []
|
|
242
|
-
|
|
242
|
+
|
|
243
243
|
def readRoot(self):
|
|
244
244
|
result = None
|
|
245
245
|
self.reset()
|
|
@@ -253,38 +253,38 @@ class PlistReader(object):
|
|
|
253
253
|
trailerContents = self.contents[-32:]
|
|
254
254
|
try:
|
|
255
255
|
self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents))
|
|
256
|
-
|
|
256
|
+
|
|
257
257
|
if pow(2, self.trailer.offsetSize*8) < self.trailer.offsetTableOffset:
|
|
258
258
|
raise InvalidPlistException("Offset size insufficient to reference all objects.")
|
|
259
|
-
|
|
259
|
+
|
|
260
260
|
if pow(2, self.trailer.objectRefSize*8) < self.trailer.offsetCount:
|
|
261
261
|
raise InvalidPlistException("Too many offsets to represent in size of object reference representation.")
|
|
262
|
-
|
|
262
|
+
|
|
263
263
|
offset_size = self.trailer.offsetSize * self.trailer.offsetCount
|
|
264
264
|
offset = self.trailer.offsetTableOffset
|
|
265
|
-
|
|
265
|
+
|
|
266
266
|
if offset + offset_size > pow(2, 64):
|
|
267
267
|
raise InvalidPlistException("Offset table is excessively long.")
|
|
268
|
-
|
|
268
|
+
|
|
269
269
|
if self.trailer.offsetSize > 16:
|
|
270
270
|
raise InvalidPlistException("Offset size is greater than maximum integer size.")
|
|
271
|
-
|
|
271
|
+
|
|
272
272
|
if self.trailer.objectRefSize == 0:
|
|
273
273
|
raise InvalidPlistException("Object reference size is zero.")
|
|
274
|
-
|
|
274
|
+
|
|
275
275
|
if offset >= len(self.contents) - 32:
|
|
276
276
|
raise InvalidPlistException("Offset table offset is too large.")
|
|
277
|
-
|
|
277
|
+
|
|
278
278
|
if offset < len("bplist00x"):
|
|
279
279
|
raise InvalidPlistException("Offset table offset is too small.")
|
|
280
|
-
|
|
280
|
+
|
|
281
281
|
if self.trailer.topLevelObjectNumber >= self.trailer.offsetCount:
|
|
282
282
|
raise InvalidPlistException("Top level object number is larger than the number of objects.")
|
|
283
|
-
|
|
283
|
+
|
|
284
284
|
offset_contents = self.contents[offset:offset+offset_size]
|
|
285
285
|
offset_i = 0
|
|
286
286
|
offset_table_length = len(offset_contents)
|
|
287
|
-
|
|
287
|
+
|
|
288
288
|
while offset_i < self.trailer.offsetCount:
|
|
289
289
|
begin = self.trailer.offsetSize*offset_i
|
|
290
290
|
end = begin+self.trailer.offsetSize
|
|
@@ -299,25 +299,25 @@ class PlistReader(object):
|
|
|
299
299
|
except TypeError as e:
|
|
300
300
|
raise InvalidPlistException(e)
|
|
301
301
|
return result
|
|
302
|
-
|
|
302
|
+
|
|
303
303
|
def setCurrentOffsetToObjectNumber(self, objectNumber):
|
|
304
304
|
if objectNumber > len(self.offsets) - 1:
|
|
305
305
|
raise InvalidPlistException("Invalid offset number: %d" % objectNumber)
|
|
306
306
|
self.currentOffset = self.offsets[objectNumber]
|
|
307
307
|
if self.currentOffset in self.offsetsStack:
|
|
308
308
|
raise InvalidPlistException("Recursive data structure detected in object: %d" % objectNumber)
|
|
309
|
-
|
|
309
|
+
|
|
310
310
|
def beginOffsetProtection(self):
|
|
311
311
|
self.offsetsStack.append(self.currentOffset)
|
|
312
312
|
return self.currentOffset
|
|
313
|
-
|
|
313
|
+
|
|
314
314
|
def endOffsetProtection(self, offset):
|
|
315
315
|
try:
|
|
316
316
|
index = self.offsetsStack.index(offset)
|
|
317
317
|
self.offsetsStack = self.offsetsStack[:index]
|
|
318
318
|
except ValueError as e:
|
|
319
319
|
pass
|
|
320
|
-
|
|
320
|
+
|
|
321
321
|
def readObject(self):
|
|
322
322
|
protection = self.beginOffsetProtection()
|
|
323
323
|
result = None
|
|
@@ -328,12 +328,12 @@ class PlistReader(object):
|
|
|
328
328
|
format = (marker_byte >> 4) & 0x0f
|
|
329
329
|
extra = marker_byte & 0x0f
|
|
330
330
|
self.currentOffset += 1
|
|
331
|
-
|
|
331
|
+
|
|
332
332
|
def proc_extra(extra):
|
|
333
333
|
if extra == 0b1111:
|
|
334
334
|
extra = self.readObject()
|
|
335
335
|
return extra
|
|
336
|
-
|
|
336
|
+
|
|
337
337
|
# bool, null, or fill byte
|
|
338
338
|
if format == 0b0000:
|
|
339
339
|
if extra == 0b0000:
|
|
@@ -382,11 +382,11 @@ class PlistReader(object):
|
|
|
382
382
|
elif format == 0b1101:
|
|
383
383
|
extra = proc_extra(extra)
|
|
384
384
|
result = self.readDict(extra)
|
|
385
|
-
else:
|
|
385
|
+
else:
|
|
386
386
|
raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra)))
|
|
387
387
|
self.endOffsetProtection(protection)
|
|
388
388
|
return result
|
|
389
|
-
|
|
389
|
+
|
|
390
390
|
def readContents(self, length, description="Object contents"):
|
|
391
391
|
end = self.currentOffset + length
|
|
392
392
|
if end >= len(self.contents) - 32:
|
|
@@ -395,12 +395,12 @@ class PlistReader(object):
|
|
|
395
395
|
raise InvalidPlistException("%s length is less than zero" % length)
|
|
396
396
|
data = self.contents[self.currentOffset:end]
|
|
397
397
|
return data
|
|
398
|
-
|
|
398
|
+
|
|
399
399
|
def readInteger(self, byteSize):
|
|
400
400
|
data = self.readContents(byteSize, "Integer")
|
|
401
401
|
self.currentOffset = self.currentOffset + byteSize
|
|
402
402
|
return self.getSizedInteger(data, byteSize, as_number=True)
|
|
403
|
-
|
|
403
|
+
|
|
404
404
|
def readReal(self, length):
|
|
405
405
|
to_read = pow(2, length)
|
|
406
406
|
data = self.readContents(to_read, "Real")
|
|
@@ -411,8 +411,8 @@ class PlistReader(object):
|
|
|
411
411
|
else:
|
|
412
412
|
raise InvalidPlistException("Unknown Real of length %d bytes" % to_read)
|
|
413
413
|
return result
|
|
414
|
-
|
|
415
|
-
def readRefs(self, count):
|
|
414
|
+
|
|
415
|
+
def readRefs(self, count):
|
|
416
416
|
refs = []
|
|
417
417
|
i = 0
|
|
418
418
|
while i < count:
|
|
@@ -422,7 +422,7 @@ class PlistReader(object):
|
|
|
422
422
|
self.currentOffset += self.trailer.objectRefSize
|
|
423
423
|
i += 1
|
|
424
424
|
return refs
|
|
425
|
-
|
|
425
|
+
|
|
426
426
|
def readArray(self, count):
|
|
427
427
|
if not isinstance(count, (int, long)):
|
|
428
428
|
raise InvalidPlistException("Count of entries in dict isn't of integer type.")
|
|
@@ -435,7 +435,7 @@ class PlistReader(object):
|
|
|
435
435
|
result.append(value)
|
|
436
436
|
i += 1
|
|
437
437
|
return result
|
|
438
|
-
|
|
438
|
+
|
|
439
439
|
def readDict(self, count):
|
|
440
440
|
if not isinstance(count, (int, long)):
|
|
441
441
|
raise InvalidPlistException("Count of keys/values in dict isn't of integer type.")
|
|
@@ -451,7 +451,7 @@ class PlistReader(object):
|
|
|
451
451
|
result[key] = value
|
|
452
452
|
i += 1
|
|
453
453
|
return result
|
|
454
|
-
|
|
454
|
+
|
|
455
455
|
def readAsciiString(self, length):
|
|
456
456
|
if not isinstance(length, (int, long)):
|
|
457
457
|
raise InvalidPlistException("Length of ASCII string isn't of integer type.")
|
|
@@ -459,7 +459,7 @@ class PlistReader(object):
|
|
|
459
459
|
result = unpack("!%ds" % length, data)[0]
|
|
460
460
|
self.currentOffset += length
|
|
461
461
|
return str(result.decode('ascii'))
|
|
462
|
-
|
|
462
|
+
|
|
463
463
|
def readUnicode(self, length):
|
|
464
464
|
if not isinstance(length, (int, long)):
|
|
465
465
|
raise InvalidPlistException("Length of Unicode string isn't of integer type.")
|
|
@@ -467,7 +467,7 @@ class PlistReader(object):
|
|
|
467
467
|
data = self.readContents(actual_length, "Unicode string")
|
|
468
468
|
self.currentOffset += actual_length
|
|
469
469
|
return data.decode('utf_16_be')
|
|
470
|
-
|
|
470
|
+
|
|
471
471
|
def readDate(self):
|
|
472
472
|
data = self.readContents(8, "Date")
|
|
473
473
|
x = unpack(">d", data)[0]
|
|
@@ -483,19 +483,19 @@ class PlistReader(object):
|
|
|
483
483
|
result = datetime.datetime.min
|
|
484
484
|
self.currentOffset += 8
|
|
485
485
|
return result
|
|
486
|
-
|
|
486
|
+
|
|
487
487
|
def readData(self, length):
|
|
488
488
|
if not isinstance(length, (int, long)):
|
|
489
489
|
raise InvalidPlistException("Length of data isn't of integer type.")
|
|
490
490
|
result = self.readContents(length, "Data")
|
|
491
491
|
self.currentOffset += length
|
|
492
492
|
return Data(result)
|
|
493
|
-
|
|
493
|
+
|
|
494
494
|
def readUid(self, length):
|
|
495
495
|
if not isinstance(length, (int, long)):
|
|
496
496
|
raise InvalidPlistException("Uid length isn't of integer type.")
|
|
497
497
|
return Uid(self.readInteger(length+1))
|
|
498
|
-
|
|
498
|
+
|
|
499
499
|
def getSizedInteger(self, data, byteSize, as_number=False):
|
|
500
500
|
"""Numbers of 8 bytes are signed integers when they refer to numbers, but unsigned otherwise."""
|
|
501
501
|
result = 0
|
|
@@ -555,16 +555,16 @@ class FloatWrapper(object):
|
|
|
555
555
|
|
|
556
556
|
class StringWrapper(object):
|
|
557
557
|
__instances = {}
|
|
558
|
-
|
|
558
|
+
|
|
559
559
|
encodedValue = None
|
|
560
560
|
encoding = None
|
|
561
|
-
|
|
561
|
+
|
|
562
562
|
def __new__(cls, value):
|
|
563
563
|
'''Ensure we only have a only one instance for any string,
|
|
564
564
|
and that we encode ascii as 1-byte-per character when possible'''
|
|
565
|
-
|
|
565
|
+
|
|
566
566
|
encodedValue = None
|
|
567
|
-
|
|
567
|
+
|
|
568
568
|
for encoding in ('ascii', 'utf_16_be'):
|
|
569
569
|
try:
|
|
570
570
|
encodedValue = value.encode(encoding)
|
|
@@ -575,26 +575,26 @@ class StringWrapper(object):
|
|
|
575
575
|
cls.__instances[encodedValue].encodedValue = encodedValue
|
|
576
576
|
cls.__instances[encodedValue].encoding = encoding
|
|
577
577
|
return cls.__instances[encodedValue]
|
|
578
|
-
|
|
578
|
+
|
|
579
579
|
raise ValueError('Unable to get ascii or utf_16_be encoding for %s' % repr(value))
|
|
580
|
-
|
|
580
|
+
|
|
581
581
|
def __len__(self):
|
|
582
582
|
'''Return roughly the number of characters in this string (half the byte length)'''
|
|
583
583
|
if self.encoding == 'ascii':
|
|
584
584
|
return len(self.encodedValue)
|
|
585
585
|
else:
|
|
586
586
|
return len(self.encodedValue)//2
|
|
587
|
-
|
|
587
|
+
|
|
588
588
|
def __lt__(self, other):
|
|
589
589
|
return self.encodedValue < other.encodedValue
|
|
590
|
-
|
|
590
|
+
|
|
591
591
|
@property
|
|
592
592
|
def encodingMarker(self):
|
|
593
593
|
if self.encoding == 'ascii':
|
|
594
594
|
return 0b0101
|
|
595
595
|
else:
|
|
596
596
|
return 0b0110
|
|
597
|
-
|
|
597
|
+
|
|
598
598
|
def __repr__(self):
|
|
599
599
|
return '<StringWrapper (%s): %s>' % (self.encoding, self.encodedValue)
|
|
600
600
|
|
|
@@ -610,7 +610,7 @@ class PlistWriter(object):
|
|
|
610
610
|
wrappedFalse = None
|
|
611
611
|
# Used to detect recursive object references.
|
|
612
612
|
objectsStack = []
|
|
613
|
-
|
|
613
|
+
|
|
614
614
|
def __init__(self, file):
|
|
615
615
|
self.reset()
|
|
616
616
|
self.file = file
|
|
@@ -620,21 +620,21 @@ class PlistWriter(object):
|
|
|
620
620
|
def reset(self):
|
|
621
621
|
self.byteCounts = PlistByteCounts(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
|
622
622
|
self.trailer = PlistTrailer(0, 0, 0, 0, 0)
|
|
623
|
-
|
|
623
|
+
|
|
624
624
|
# A set of all the uniques which have been computed.
|
|
625
625
|
self.computedUniques = set()
|
|
626
626
|
# A list of all the uniques which have been written.
|
|
627
627
|
self.writtenReferences = {}
|
|
628
628
|
# A dict of the positions of the written uniques.
|
|
629
629
|
self.referencePositions = {}
|
|
630
|
-
|
|
630
|
+
|
|
631
631
|
self.objectsStack = []
|
|
632
|
-
|
|
632
|
+
|
|
633
633
|
def positionOfObjectReference(self, obj):
|
|
634
634
|
"""If the given object has been written already, return its
|
|
635
635
|
position in the offset table. Otherwise, return None."""
|
|
636
636
|
return self.writtenReferences.get(obj)
|
|
637
|
-
|
|
637
|
+
|
|
638
638
|
def writeRoot(self, root):
|
|
639
639
|
"""
|
|
640
640
|
Strategy is:
|
|
@@ -657,7 +657,7 @@ class PlistWriter(object):
|
|
|
657
657
|
self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))})
|
|
658
658
|
self.writeObjectReference(wrapped_root, output)
|
|
659
659
|
output = self.writeObject(wrapped_root, output, setReferencePosition=True)
|
|
660
|
-
|
|
660
|
+
|
|
661
661
|
# output size at this point is an upper bound on how big the
|
|
662
662
|
# object reference offsets need to be.
|
|
663
663
|
self.trailer = self.trailer._replace(**{
|
|
@@ -666,18 +666,18 @@ class PlistWriter(object):
|
|
|
666
666
|
'offsetTableOffset':len(output),
|
|
667
667
|
'topLevelObjectNumber':0
|
|
668
668
|
})
|
|
669
|
-
|
|
669
|
+
|
|
670
670
|
output = self.writeOffsetTable(output)
|
|
671
671
|
output += pack('!xxxxxxBBQQQ', *self.trailer)
|
|
672
672
|
self.file.write(output)
|
|
673
|
-
|
|
673
|
+
|
|
674
674
|
def beginRecursionProtection(self, obj):
|
|
675
675
|
if not isinstance(obj, (set, dict, list, tuple)):
|
|
676
676
|
return
|
|
677
677
|
if id(obj) in self.objectsStack:
|
|
678
678
|
raise InvalidPlistException("Recursive containers are not allowed in plists.")
|
|
679
679
|
self.objectsStack.append(id(obj))
|
|
680
|
-
|
|
680
|
+
|
|
681
681
|
def endRecursionProtection(self, obj):
|
|
682
682
|
if not isinstance(obj, (set, dict, list, tuple)):
|
|
683
683
|
return
|
|
@@ -690,7 +690,7 @@ class PlistWriter(object):
|
|
|
690
690
|
def wrapRoot(self, root):
|
|
691
691
|
result = None
|
|
692
692
|
self.beginRecursionProtection(root)
|
|
693
|
-
|
|
693
|
+
|
|
694
694
|
if isinstance(root, bool):
|
|
695
695
|
if root is True:
|
|
696
696
|
result = self.wrappedTrue
|
|
@@ -722,7 +722,7 @@ class PlistWriter(object):
|
|
|
722
722
|
result = Data(root)
|
|
723
723
|
else:
|
|
724
724
|
result = root
|
|
725
|
-
|
|
725
|
+
|
|
726
726
|
self.endRecursionProtection(root)
|
|
727
727
|
return result
|
|
728
728
|
|
|
@@ -737,7 +737,7 @@ class PlistWriter(object):
|
|
|
737
737
|
raise InvalidPlistException('Data cannot be dictionary keys in plists.')
|
|
738
738
|
elif not isinstance(key, StringWrapper):
|
|
739
739
|
raise InvalidPlistException('Keys must be strings.')
|
|
740
|
-
|
|
740
|
+
|
|
741
741
|
def proc_size(size):
|
|
742
742
|
if size > 0b1110:
|
|
743
743
|
size += self.intSize(size)
|
|
@@ -749,7 +749,7 @@ class PlistWriter(object):
|
|
|
749
749
|
return
|
|
750
750
|
else:
|
|
751
751
|
self.computedUniques.add(obj)
|
|
752
|
-
|
|
752
|
+
|
|
753
753
|
if obj is None:
|
|
754
754
|
self.incrementByteCount('nullBytes')
|
|
755
755
|
elif isinstance(obj, BoolWrapper):
|
|
@@ -763,7 +763,7 @@ class PlistWriter(object):
|
|
|
763
763
|
elif isinstance(obj, FloatWrapper):
|
|
764
764
|
size = self.realSize(obj)
|
|
765
765
|
self.incrementByteCount('realBytes', incr=1+size)
|
|
766
|
-
elif isinstance(obj, datetime.datetime):
|
|
766
|
+
elif isinstance(obj, datetime.datetime):
|
|
767
767
|
self.incrementByteCount('dateBytes', incr=2)
|
|
768
768
|
elif isinstance(obj, Data):
|
|
769
769
|
size = proc_size(len(obj))
|
|
@@ -823,15 +823,15 @@ class PlistWriter(object):
|
|
|
823
823
|
else:
|
|
824
824
|
result += pack('!B', (format << 4) | length)
|
|
825
825
|
return result
|
|
826
|
-
|
|
826
|
+
|
|
827
827
|
def timedelta_total_seconds(td):
|
|
828
828
|
# Shim for Python 2.6 compatibility, which doesn't have total_seconds.
|
|
829
829
|
# Make one argument a float to ensure the right calculation.
|
|
830
830
|
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10.0**6) / 10.0**6
|
|
831
|
-
|
|
831
|
+
|
|
832
832
|
if setReferencePosition:
|
|
833
833
|
self.referencePositions[obj] = len(output)
|
|
834
|
-
|
|
834
|
+
|
|
835
835
|
if obj is None:
|
|
836
836
|
output += pack('!B', 0b00000000)
|
|
837
837
|
elif isinstance(obj, BoolWrapper):
|
|
@@ -875,7 +875,7 @@ class PlistWriter(object):
|
|
|
875
875
|
output += proc_variable_length(0b1100, len(obj))
|
|
876
876
|
else:
|
|
877
877
|
output += proc_variable_length(0b1010, len(obj))
|
|
878
|
-
|
|
878
|
+
|
|
879
879
|
objectsToWrite = []
|
|
880
880
|
for objRef in sorted(obj) if isinstance(obj, set) else obj:
|
|
881
881
|
(isNew, output) = self.writeObjectReference(objRef, output)
|
|
@@ -902,7 +902,7 @@ class PlistWriter(object):
|
|
|
902
902
|
for objRef in objectsToWrite:
|
|
903
903
|
output = self.writeObject(objRef, output, setReferencePosition=True)
|
|
904
904
|
return output
|
|
905
|
-
|
|
905
|
+
|
|
906
906
|
def writeOffsetTable(self, output):
|
|
907
907
|
"""Writes all of the object reference offsets."""
|
|
908
908
|
all_positions = []
|
|
@@ -922,12 +922,12 @@ class PlistWriter(object):
|
|
|
922
922
|
output += self.binaryInt(position, self.trailer.offsetSize)
|
|
923
923
|
all_positions.append(position)
|
|
924
924
|
return output
|
|
925
|
-
|
|
925
|
+
|
|
926
926
|
def binaryReal(self, obj):
|
|
927
927
|
# just use doubles
|
|
928
928
|
result = pack('>d', obj.value)
|
|
929
929
|
return result
|
|
930
|
-
|
|
930
|
+
|
|
931
931
|
def binaryInt(self, obj, byteSize=None, as_number=False):
|
|
932
932
|
result = b''
|
|
933
933
|
if byteSize is None:
|
|
@@ -951,7 +951,7 @@ class PlistWriter(object):
|
|
|
951
951
|
else:
|
|
952
952
|
raise InvalidPlistException("Core Foundation can't handle integers with size greater than 16 bytes.")
|
|
953
953
|
return result
|
|
954
|
-
|
|
954
|
+
|
|
955
955
|
def intSize(self, obj):
|
|
956
956
|
"""Returns the number of bytes necessary to store the given integer."""
|
|
957
957
|
# SIGNED
|
|
@@ -972,6 +972,6 @@ class PlistWriter(object):
|
|
|
972
972
|
return 16
|
|
973
973
|
else:
|
|
974
974
|
raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.")
|
|
975
|
-
|
|
975
|
+
|
|
976
976
|
def realSize(self, obj):
|
|
977
977
|
return 8
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
|
|
4
|
+
from .core import build_dmg
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
parser = argparse.ArgumentParser(description="Construct a disk image file.")
|
|
9
|
+
parser.add_argument(
|
|
10
|
+
"volume_name",
|
|
11
|
+
metavar="volume-name",
|
|
12
|
+
help="The name to give to the volume (this will appear in the title bar when the user mounts the disk image).",
|
|
13
|
+
)
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"filename",
|
|
16
|
+
metavar="output.dmg",
|
|
17
|
+
help="The filename of the disk image to create.",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument("-s", "--settings", help="The path of the settings file.")
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"-D",
|
|
22
|
+
dest="defines",
|
|
23
|
+
action="append",
|
|
24
|
+
default=[],
|
|
25
|
+
help="Define a value for the settings file (e.g. -Dfoo=bar).",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--no-hidpi",
|
|
29
|
+
dest="lookForHiDPI",
|
|
30
|
+
action="store_false",
|
|
31
|
+
default=True,
|
|
32
|
+
help="Do not search for HiDPI versions of the background image (if specified)",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--detach-retries",
|
|
36
|
+
dest="detachRetries",
|
|
37
|
+
type=int,
|
|
38
|
+
default=5,
|
|
39
|
+
choices=range(1, 31),
|
|
40
|
+
help="Number of attempts to detach disk volume (1 sec. interval)",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
args = parser.parse_args()
|
|
44
|
+
|
|
45
|
+
defines = {}
|
|
46
|
+
for d in args.defines:
|
|
47
|
+
k, v = d.split("=", 1)
|
|
48
|
+
k = k.strip()
|
|
49
|
+
v = v.strip()
|
|
50
|
+
if (v.startswith("'") and v.endswith("'")) or (
|
|
51
|
+
v.startswith('"') and v.endswith('"')
|
|
52
|
+
):
|
|
53
|
+
v = v[1:-1]
|
|
54
|
+
defines[k] = v
|
|
55
|
+
|
|
56
|
+
build_dmg(
|
|
57
|
+
args.filename,
|
|
58
|
+
args.volume_name,
|
|
59
|
+
args.settings,
|
|
60
|
+
defines=defines,
|
|
61
|
+
lookForHiDPI=args.lookForHiDPI,
|
|
62
|
+
detach_retries=args.detachRetries,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
main()
|