diff options
31 files changed, 2224 insertions, 2670 deletions
diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index fbdcc991d..e47bd74f0 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -9,7 +9,7 @@ endif() # emulating the same layout we will have after the installation # copy & install python packages -set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python2.7/site-packages/) +set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python3.9/site-packages/) file(GLOB_RECURSE _SITE_PACKAGES_RELATIVE RELATIVE "${_SITE_PACKAGES_DIR}" "${_SITE_PACKAGES_DIR}/*.py") set(_SITE_PACKAGES_SRC ${_SITE_PACKAGES_RELATIVE}) diff --git a/share/share.qbs b/share/share.qbs index 8f2347a67..75e1e83ba 100644 --- a/share/share.qbs +++ b/share/share.qbs @@ -24,7 +24,7 @@ Product { fileTags: ["qbs resources"] qbs.install: true qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share/qbs/python" - qbs.installSourceBase: "../src/3rdparty/python/lib/python2.7/site-packages" + qbs.installSourceBase: "../src/3rdparty/python/lib/python3.9/site-packages" } Group { diff --git a/src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE b/src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE deleted file mode 100644 index 1c7ba6cc1..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2010, Andrew Wooster -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of biplist nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py deleted file mode 100644 index f9d5836dd..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py +++ /dev/null @@ -1,977 +0,0 @@ -"""biplist -- a library for reading and writing binary property list files. - -Binary Property List (plist) files provide a faster and smaller serialization -format for property lists on OS X. This is a library for generating binary -plists which can be read by OS X, iOS, or other clients. - -The API models the plistlib API, and will call through to plistlib when -XML serialization or deserialization is required. - -To generate plists with UID values, wrap the values with the Uid object. The -value must be an int. - -To generate plists with NSData/CFData values, wrap the values with the -Data object. The value must be a string. - -Date values can only be datetime.datetime objects. - -The exceptions InvalidPlistException and NotBinaryPlistException may be -thrown to indicate that the data cannot be serialized or deserialized as -a binary plist. - -Plist generation example: - - from biplist import * - from datetime import datetime - plist = {'aKey':'aValue', - '0':1.322, - 'now':datetime.now(), - 'list':[1,2,3], - 'tuple':('a','b','c') - } - try: - writePlist(plist, "example.plist") - except (InvalidPlistException, NotBinaryPlistException), e: - print "Something bad happened:", e - -Plist parsing example: - - from biplist import * - try: - plist = readPlist("example.plist") - print plist - except (InvalidPlistException, NotBinaryPlistException), e: - print "Not a plist:", e -""" - -from collections import namedtuple -import datetime -import io -import math -import plistlib -from struct import pack, unpack, unpack_from -from struct import error as struct_error -import sys -import time - -try: - unicode - unicodeEmpty = r'' -except NameError: - unicode = str - unicodeEmpty = '' -try: - long -except NameError: - long = int -try: - {}.iteritems - iteritems = lambda x: x.iteritems() -except AttributeError: - iteritems = lambda x: x.items() - -__all__ = [ - 'Uid', 'Data', 'readPlist', 'writePlist', 'readPlistFromString', - 'writePlistToString', 'InvalidPlistException', 'NotBinaryPlistException' -] - -# Apple uses Jan 1, 2001 as a base for all plist date/times. -apple_reference_date = datetime.datetime.utcfromtimestamp(978307200) - -class Uid(object): - """Wrapper around integers for representing UID values. This - is used in keyed archiving.""" - integer = 0 - def __init__(self, integer): - self.integer = integer - - def __repr__(self): - return "Uid(%d)" % self.integer - - def __eq__(self, other): - if isinstance(self, Uid) and isinstance(other, Uid): - return self.integer == other.integer - return False - - def __cmp__(self, other): - return self.integer - other.integer - - def __lt__(self, other): - return self.integer < other.integer - - def __hash__(self): - return self.integer - - def __int__(self): - return int(self.integer) - -class Data(bytes): - """Wrapper around bytes to distinguish Data values.""" - -class InvalidPlistException(Exception): - """Raised when the plist is incorrectly formatted.""" - -class NotBinaryPlistException(Exception): - """Raised when a binary plist was expected but not encountered.""" - -def readPlist(pathOrFile): - """Raises NotBinaryPlistException, InvalidPlistException""" - didOpen = False - result = None - if isinstance(pathOrFile, (bytes, unicode)): - pathOrFile = open(pathOrFile, 'rb') - didOpen = True - try: - reader = PlistReader(pathOrFile) - result = reader.parse() - except NotBinaryPlistException as e: - try: - pathOrFile.seek(0) - result = None - if hasattr(plistlib, 'loads'): - contents = None - if isinstance(pathOrFile, (bytes, unicode)): - with open(pathOrFile, 'rb') as f: - contents = f.read() - else: - contents = pathOrFile.read() - result = plistlib.loads(contents) - else: - result = plistlib.readPlist(pathOrFile) - result = wrapDataObject(result, for_binary=True) - except Exception as e: - raise InvalidPlistException(e) - finally: - if didOpen: - pathOrFile.close() - return result - -def wrapDataObject(o, for_binary=False): - if isinstance(o, Data) and not for_binary: - v = sys.version_info - if not (v[0] >= 3 and v[1] >= 4): - o = plistlib.Data(o) - elif isinstance(o, (bytes, plistlib.Data)) and for_binary: - if hasattr(o, 'data'): - o = Data(o.data) - elif isinstance(o, tuple): - o = wrapDataObject(list(o), for_binary) - o = tuple(o) - elif isinstance(o, list): - for i in range(len(o)): - o[i] = wrapDataObject(o[i], for_binary) - elif isinstance(o, dict): - for k in o: - o[k] = wrapDataObject(o[k], for_binary) - return o - -def writePlist(rootObject, pathOrFile, binary=True): - if not binary: - rootObject = wrapDataObject(rootObject, binary) - if hasattr(plistlib, "dump"): - if isinstance(pathOrFile, (bytes, unicode)): - with open(pathOrFile, 'wb') as f: - return plistlib.dump(rootObject, f) - else: - return plistlib.dump(rootObject, pathOrFile) - else: - return plistlib.writePlist(rootObject, pathOrFile) - else: - didOpen = False - if isinstance(pathOrFile, (bytes, unicode)): - pathOrFile = open(pathOrFile, 'wb') - didOpen = True - writer = PlistWriter(pathOrFile) - result = writer.writeRoot(rootObject) - if didOpen: - pathOrFile.close() - return result - -def readPlistFromString(data): - return readPlist(io.BytesIO(data)) - -def writePlistToString(rootObject, binary=True): - if not binary: - rootObject = wrapDataObject(rootObject, binary) - if hasattr(plistlib, "dumps"): - return plistlib.dumps(rootObject) - elif hasattr(plistlib, "writePlistToBytes"): - return plistlib.writePlistToBytes(rootObject) - else: - return plistlib.writePlistToString(rootObject) - else: - ioObject = io.BytesIO() - writer = PlistWriter(ioObject) - writer.writeRoot(rootObject) - return ioObject.getvalue() - -def is_stream_binary_plist(stream): - stream.seek(0) - header = stream.read(7) - if header == b'bplist0': - return True - else: - return False - -PlistTrailer = namedtuple('PlistTrailer', 'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber, offsetTableOffset') -PlistByteCounts = namedtuple('PlistByteCounts', 'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes, stringBytes, uidBytes, arrayBytes, setBytes, dictBytes') - -class PlistReader(object): - file = None - contents = '' - offsets = None - trailer = None - currentOffset = 0 - # Used to detect recursive object references. - offsetsStack = [] - - def __init__(self, fileOrStream): - """Raises NotBinaryPlistException.""" - self.reset() - self.file = fileOrStream - - def parse(self): - return self.readRoot() - - def reset(self): - self.trailer = None - self.contents = '' - self.offsets = [] - self.currentOffset = 0 - self.offsetsStack = [] - - def readRoot(self): - result = None - self.reset() - # Get the header, make sure it's a valid file. - if not is_stream_binary_plist(self.file): - raise NotBinaryPlistException() - self.file.seek(0) - self.contents = self.file.read() - if len(self.contents) < 32: - raise InvalidPlistException("File is too short.") - trailerContents = self.contents[-32:] - try: - self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents)) - - if pow(2, self.trailer.offsetSize*8) < self.trailer.offsetTableOffset: - raise InvalidPlistException("Offset size insufficient to reference all objects.") - - if pow(2, self.trailer.objectRefSize*8) < self.trailer.offsetCount: - raise InvalidPlistException("Too many offsets to represent in size of object reference representation.") - - offset_size = self.trailer.offsetSize * self.trailer.offsetCount - offset = self.trailer.offsetTableOffset - - if offset + offset_size > pow(2, 64): - raise InvalidPlistException("Offset table is excessively long.") - - if self.trailer.offsetSize > 16: - raise InvalidPlistException("Offset size is greater than maximum integer size.") - - if self.trailer.objectRefSize == 0: - raise InvalidPlistException("Object reference size is zero.") - - if offset >= len(self.contents) - 32: - raise InvalidPlistException("Offset table offset is too large.") - - if offset < len("bplist00x"): - raise InvalidPlistException("Offset table offset is too small.") - - if self.trailer.topLevelObjectNumber >= self.trailer.offsetCount: - raise InvalidPlistException("Top level object number is larger than the number of objects.") - - offset_contents = self.contents[offset:offset+offset_size] - offset_i = 0 - offset_table_length = len(offset_contents) - - while offset_i < self.trailer.offsetCount: - begin = self.trailer.offsetSize*offset_i - end = begin+self.trailer.offsetSize - if end > offset_table_length: - raise InvalidPlistException("End of object is at invalid offset %d in offset table of length %d" % (end, offset_table_length)) - tmp_contents = offset_contents[begin:end] - tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize) - self.offsets.append(tmp_sized) - offset_i += 1 - self.setCurrentOffsetToObjectNumber(self.trailer.topLevelObjectNumber) - result = self.readObject() - except TypeError as e: - raise InvalidPlistException(e) - return result - - def setCurrentOffsetToObjectNumber(self, objectNumber): - if objectNumber > len(self.offsets) - 1: - raise InvalidPlistException("Invalid offset number: %d" % objectNumber) - self.currentOffset = self.offsets[objectNumber] - if self.currentOffset in self.offsetsStack: - raise InvalidPlistException("Recursive data structure detected in object: %d" % objectNumber) - - def beginOffsetProtection(self): - self.offsetsStack.append(self.currentOffset) - return self.currentOffset - - def endOffsetProtection(self, offset): - try: - index = self.offsetsStack.index(offset) - self.offsetsStack = self.offsetsStack[:index] - except ValueError as e: - pass - - def readObject(self): - protection = self.beginOffsetProtection() - result = None - tmp_byte = self.contents[self.currentOffset:self.currentOffset+1] - if len(tmp_byte) != 1: - raise InvalidPlistException("No object found at offset: %d" % self.currentOffset) - marker_byte = unpack("!B", tmp_byte)[0] - format = (marker_byte >> 4) & 0x0f - extra = marker_byte & 0x0f - self.currentOffset += 1 - - def proc_extra(extra): - if extra == 0b1111: - extra = self.readObject() - return extra - - # bool, null, or fill byte - if format == 0b0000: - if extra == 0b0000: - result = None - elif extra == 0b1000: - result = False - elif extra == 0b1001: - result = True - elif extra == 0b1111: - pass # fill byte - else: - raise InvalidPlistException("Invalid object found at offset: %d" % (self.currentOffset - 1)) - # int - elif format == 0b0001: - result = self.readInteger(pow(2, extra)) - # real - elif format == 0b0010: - result = self.readReal(extra) - # date - elif format == 0b0011 and extra == 0b0011: - result = self.readDate() - # data - elif format == 0b0100: - extra = proc_extra(extra) - result = self.readData(extra) - # ascii string - elif format == 0b0101: - extra = proc_extra(extra) - result = self.readAsciiString(extra) - # Unicode string - elif format == 0b0110: - extra = proc_extra(extra) - result = self.readUnicode(extra) - # uid - elif format == 0b1000: - result = self.readUid(extra) - # array - elif format == 0b1010: - extra = proc_extra(extra) - result = self.readArray(extra) - # set - elif format == 0b1100: - extra = proc_extra(extra) - result = set(self.readArray(extra)) - # dict - elif format == 0b1101: - extra = proc_extra(extra) - result = self.readDict(extra) - else: - raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra))) - self.endOffsetProtection(protection) - return result - - def readContents(self, length, description="Object contents"): - end = self.currentOffset + length - if end >= len(self.contents) - 32: - raise InvalidPlistException("%s extends into trailer" % description) - elif length < 0: - raise InvalidPlistException("%s length is less than zero" % length) - data = self.contents[self.currentOffset:end] - return data - - def readInteger(self, byteSize): - data = self.readContents(byteSize, "Integer") - self.currentOffset = self.currentOffset + byteSize - return self.getSizedInteger(data, byteSize, as_number=True) - - def readReal(self, length): - to_read = pow(2, length) - data = self.readContents(to_read, "Real") - if length == 2: # 4 bytes - result = unpack('>f', data)[0] - elif length == 3: # 8 bytes - result = unpack('>d', data)[0] - else: - raise InvalidPlistException("Unknown Real of length %d bytes" % to_read) - return result - - def readRefs(self, count): - refs = [] - i = 0 - while i < count: - fragment = self.readContents(self.trailer.objectRefSize, "Object reference") - ref = self.getSizedInteger(fragment, len(fragment)) - refs.append(ref) - self.currentOffset += self.trailer.objectRefSize - i += 1 - return refs - - def readArray(self, count): - if not isinstance(count, (int, long)): - raise InvalidPlistException("Count of entries in dict isn't of integer type.") - result = [] - values = self.readRefs(count) - i = 0 - while i < len(values): - self.setCurrentOffsetToObjectNumber(values[i]) - value = self.readObject() - result.append(value) - i += 1 - return result - - def readDict(self, count): - if not isinstance(count, (int, long)): - raise InvalidPlistException("Count of keys/values in dict isn't of integer type.") - result = {} - keys = self.readRefs(count) - values = self.readRefs(count) - i = 0 - while i < len(keys): - self.setCurrentOffsetToObjectNumber(keys[i]) - key = self.readObject() - self.setCurrentOffsetToObjectNumber(values[i]) - value = self.readObject() - result[key] = value - i += 1 - return result - - def readAsciiString(self, length): - if not isinstance(length, (int, long)): - raise InvalidPlistException("Length of ASCII string isn't of integer type.") - data = self.readContents(length, "ASCII string") - result = unpack("!%ds" % length, data)[0] - self.currentOffset += length - return str(result.decode('ascii')) - - def readUnicode(self, length): - if not isinstance(length, (int, long)): - raise InvalidPlistException("Length of Unicode string isn't of integer type.") - actual_length = length*2 - data = self.readContents(actual_length, "Unicode string") - self.currentOffset += actual_length - return data.decode('utf_16_be') - - def readDate(self): - data = self.readContents(8, "Date") - x = unpack(">d", data)[0] - if math.isnan(x): - raise InvalidPlistException("Date is NaN") - # Use timedelta to workaround time_t size limitation on 32-bit python. - try: - result = datetime.timedelta(seconds=x) + apple_reference_date - except OverflowError: - if x > 0: - result = datetime.datetime.max - else: - result = datetime.datetime.min - self.currentOffset += 8 - return result - - def readData(self, length): - if not isinstance(length, (int, long)): - raise InvalidPlistException("Length of data isn't of integer type.") - result = self.readContents(length, "Data") - self.currentOffset += length - return Data(result) - - def readUid(self, length): - if not isinstance(length, (int, long)): - raise InvalidPlistException("Uid length isn't of integer type.") - return Uid(self.readInteger(length+1)) - - def getSizedInteger(self, data, byteSize, as_number=False): - """Numbers of 8 bytes are signed integers when they refer to numbers, but unsigned otherwise.""" - result = 0 - if byteSize == 0: - raise InvalidPlistException("Encountered integer with byte size of 0.") - # 1, 2, and 4 byte integers are unsigned - elif byteSize == 1: - result = unpack('>B', data)[0] - elif byteSize == 2: - result = unpack('>H', data)[0] - elif byteSize == 4: - result = unpack('>L', data)[0] - elif byteSize == 8: - if as_number: - result = unpack('>q', data)[0] - else: - result = unpack('>Q', data)[0] - elif byteSize <= 16: - # Handle odd-sized or integers larger than 8 bytes - # Don't naively go over 16 bytes, in order to prevent infinite loops. - result = 0 - if hasattr(int, 'from_bytes'): - result = int.from_bytes(data, 'big') - else: - for byte in data: - if not isinstance(byte, int): # Python3.0-3.1.x return ints, 2.x return str - byte = unpack_from('>B', byte)[0] - result = (result << 8) | byte - else: - raise InvalidPlistException("Encountered integer longer than 16 bytes.") - return result - -class HashableWrapper(object): - def __init__(self, value): - self.value = value - def __repr__(self): - return "<HashableWrapper: %s>" % [self.value] - -class BoolWrapper(object): - def __init__(self, value): - self.value = value - def __repr__(self): - return "<BoolWrapper: %s>" % self.value - -class FloatWrapper(object): - _instances = {} - def __new__(klass, value): - # Ensure FloatWrapper(x) for a given float x is always the same object - wrapper = klass._instances.get(value) - if wrapper is None: - wrapper = object.__new__(klass) - wrapper.value = value - klass._instances[value] = wrapper - return wrapper - def __repr__(self): - return "<FloatWrapper: %s>" % self.value - -class StringWrapper(object): - __instances = {} - - encodedValue = None - encoding = None - - def __new__(cls, value): - '''Ensure we only have a only one instance for any string, - and that we encode ascii as 1-byte-per character when possible''' - - encodedValue = None - - for encoding in ('ascii', 'utf_16_be'): - try: - encodedValue = value.encode(encoding) - except: pass - if encodedValue is not None: - if encodedValue not in cls.__instances: - cls.__instances[encodedValue] = super(StringWrapper, cls).__new__(cls) - cls.__instances[encodedValue].encodedValue = encodedValue - cls.__instances[encodedValue].encoding = encoding - return cls.__instances[encodedValue] - - raise ValueError('Unable to get ascii or utf_16_be encoding for %s' % repr(value)) - - def __len__(self): - '''Return roughly the number of characters in this string (half the byte length)''' - if self.encoding == 'ascii': - return len(self.encodedValue) - else: - return len(self.encodedValue)//2 - - def __lt__(self, other): - return self.encodedValue < other.encodedValue - - @property - def encodingMarker(self): - if self.encoding == 'ascii': - return 0b0101 - else: - return 0b0110 - - def __repr__(self): - return '<StringWrapper (%s): %s>' % (self.encoding, self.encodedValue) - -class PlistWriter(object): - header = b'bplist00bybiplist1.0' - file = None - byteCounts = None - trailer = None - computedUniques = None - writtenReferences = None - referencePositions = None - wrappedTrue = None - wrappedFalse = None - # Used to detect recursive object references. - objectsStack = [] - - def __init__(self, file): - self.reset() - self.file = file - self.wrappedTrue = BoolWrapper(True) - self.wrappedFalse = BoolWrapper(False) - - def reset(self): - self.byteCounts = PlistByteCounts(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - self.trailer = PlistTrailer(0, 0, 0, 0, 0) - - # A set of all the uniques which have been computed. - self.computedUniques = set() - # A list of all the uniques which have been written. - self.writtenReferences = {} - # A dict of the positions of the written uniques. - self.referencePositions = {} - - self.objectsStack = [] - - def positionOfObjectReference(self, obj): - """If the given object has been written already, return its - position in the offset table. Otherwise, return None.""" - return self.writtenReferences.get(obj) - - def writeRoot(self, root): - """ - Strategy is: - - write header - - wrap root object so everything is hashable - - compute size of objects which will be written - - need to do this in order to know how large the object refs - will be in the list/dict/set reference lists - - write objects - - keep objects in writtenReferences - - keep positions of object references in referencePositions - - write object references with the length computed previously - - computer object reference length - - write object reference positions - - write trailer - """ - output = self.header - wrapped_root = self.wrapRoot(root) - self.computeOffsets(wrapped_root, asReference=True, isRoot=True) - self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))}) - self.writeObjectReference(wrapped_root, output) - output = self.writeObject(wrapped_root, output, setReferencePosition=True) - - # output size at this point is an upper bound on how big the - # object reference offsets need to be. - self.trailer = self.trailer._replace(**{ - 'offsetSize':self.intSize(len(output)), - 'offsetCount':len(self.computedUniques), - 'offsetTableOffset':len(output), - 'topLevelObjectNumber':0 - }) - - output = self.writeOffsetTable(output) - output += pack('!xxxxxxBBQQQ', *self.trailer) - self.file.write(output) - - def beginRecursionProtection(self, obj): - if not isinstance(obj, (set, dict, list, tuple)): - return - if id(obj) in self.objectsStack: - raise InvalidPlistException("Recursive containers are not allowed in plists.") - self.objectsStack.append(id(obj)) - - def endRecursionProtection(self, obj): - if not isinstance(obj, (set, dict, list, tuple)): - return - try: - index = self.objectsStack.index(id(obj)) - self.objectsStack = self.objectsStack[:index] - except ValueError as e: - pass - - def wrapRoot(self, root): - result = None - self.beginRecursionProtection(root) - - if isinstance(root, bool): - if root is True: - result = self.wrappedTrue - else: - result = self.wrappedFalse - elif isinstance(root, float): - result = FloatWrapper(root) - elif isinstance(root, set): - n = set() - for value in root: - n.add(self.wrapRoot(value)) - result = HashableWrapper(n) - elif isinstance(root, dict): - n = {} - for key, value in iteritems(root): - n[self.wrapRoot(key)] = self.wrapRoot(value) - result = HashableWrapper(n) - elif isinstance(root, list): - n = [] - for value in root: - n.append(self.wrapRoot(value)) - result = HashableWrapper(n) - elif isinstance(root, tuple): - n = tuple([self.wrapRoot(value) for value in root]) - result = HashableWrapper(n) - elif isinstance(root, (str, unicode)) and not isinstance(root, Data): - result = StringWrapper(root) - elif isinstance(root, bytes): - result = Data(root) - else: - result = root - - self.endRecursionProtection(root) - return result - - def incrementByteCount(self, field, incr=1): - self.byteCounts = self.byteCounts._replace(**{field:self.byteCounts.__getattribute__(field) + incr}) - - def computeOffsets(self, obj, asReference=False, isRoot=False): - def check_key(key): - if key is None: - raise InvalidPlistException('Dictionary keys cannot be null in plists.') - elif isinstance(key, Data): - raise InvalidPlistException('Data cannot be dictionary keys in plists.') - elif not isinstance(key, StringWrapper): - raise InvalidPlistException('Keys must be strings.') - - def proc_size(size): - if size > 0b1110: - size += self.intSize(size) - return size - # If this should be a reference, then we keep a record of it in the - # uniques table. - if asReference: - if obj in self.computedUniques: - return - else: - self.computedUniques.add(obj) - - if obj is None: - self.incrementByteCount('nullBytes') - elif isinstance(obj, BoolWrapper): - self.incrementByteCount('boolBytes') - elif isinstance(obj, Uid): - size = self.intSize(obj.integer) - self.incrementByteCount('uidBytes', incr=1+size) - elif isinstance(obj, (int, long)): - size = self.intSize(obj) - self.incrementByteCount('intBytes', incr=1+size) - elif isinstance(obj, FloatWrapper): - size = self.realSize(obj) - self.incrementByteCount('realBytes', incr=1+size) - elif isinstance(obj, datetime.datetime): - self.incrementByteCount('dateBytes', incr=2) - elif isinstance(obj, Data): - size = proc_size(len(obj)) - self.incrementByteCount('dataBytes', incr=1+size) - elif isinstance(obj, StringWrapper): - size = proc_size(len(obj)) - self.incrementByteCount('stringBytes', incr=1+size) - elif isinstance(obj, HashableWrapper): - obj = obj.value - if isinstance(obj, set): - size = proc_size(len(obj)) - self.incrementByteCount('setBytes', incr=1+size) - for value in obj: - self.computeOffsets(value, asReference=True) - elif isinstance(obj, (list, tuple)): - size = proc_size(len(obj)) - self.incrementByteCount('arrayBytes', incr=1+size) - for value in obj: - asRef = True - self.computeOffsets(value, asReference=True) - elif isinstance(obj, dict): - size = proc_size(len(obj)) - self.incrementByteCount('dictBytes', incr=1+size) - for key, value in iteritems(obj): - check_key(key) - self.computeOffsets(key, asReference=True) - self.computeOffsets(value, asReference=True) - else: - raise InvalidPlistException("Unknown object type: %s (%s)" % (type(obj).__name__, repr(obj))) - - def writeObjectReference(self, obj, output): - """Tries to write an object reference, adding it to the references - table. Does not write the actual object bytes or set the reference - position. Returns a tuple of whether the object was a new reference - (True if it was, False if it already was in the reference table) - and the new output. - """ - position = self.positionOfObjectReference(obj) - if position is None: - self.writtenReferences[obj] = len(self.writtenReferences) - output += self.binaryInt(len(self.writtenReferences) - 1, byteSize=self.trailer.objectRefSize) - return (True, output) - else: - output += self.binaryInt(position, byteSize=self.trailer.objectRefSize) - return (False, output) - - def writeObject(self, obj, output, setReferencePosition=False): - """Serializes the given object to the output. Returns output. - If setReferencePosition is True, will set the position the - object was written. - """ - def proc_variable_length(format, length): - result = b'' - if length > 0b1110: - result += pack('!B', (format << 4) | 0b1111) - result = self.writeObject(length, result) - else: - result += pack('!B', (format << 4) | length) - return result - - def timedelta_total_seconds(td): - # Shim for Python 2.6 compatibility, which doesn't have total_seconds. - # Make one argument a float to ensure the right calculation. - return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10.0**6) / 10.0**6 - - if setReferencePosition: - self.referencePositions[obj] = len(output) - - if obj is None: - output += pack('!B', 0b00000000) - elif isinstance(obj, BoolWrapper): - if obj.value is False: - output += pack('!B', 0b00001000) - else: - output += pack('!B', 0b00001001) - elif isinstance(obj, Uid): - size = self.intSize(obj.integer) - output += pack('!B', (0b1000 << 4) | size - 1) - output += self.binaryInt(obj.integer) - elif isinstance(obj, (int, long)): - byteSize = self.intSize(obj) - root = math.log(byteSize, 2) - output += pack('!B', (0b0001 << 4) | int(root)) - output += self.binaryInt(obj, as_number=True) - elif isinstance(obj, FloatWrapper): - # just use doubles - output += pack('!B', (0b0010 << 4) | 3) - output += self.binaryReal(obj) - elif isinstance(obj, datetime.datetime): - try: - timestamp = (obj - apple_reference_date).total_seconds() - except AttributeError: - timestamp = timedelta_total_seconds(obj - apple_reference_date) - output += pack('!B', 0b00110011) - output += pack('!d', float(timestamp)) - elif isinstance(obj, Data): - output += proc_variable_length(0b0100, len(obj)) - output += obj - elif isinstance(obj, StringWrapper): - output += proc_variable_length(obj.encodingMarker, len(obj)) - output += obj.encodedValue - elif isinstance(obj, bytes): - output += proc_variable_length(0b0101, len(obj)) - output += obj - elif isinstance(obj, HashableWrapper): - obj = obj.value - if isinstance(obj, (set, list, tuple)): - if isinstance(obj, set): - output += proc_variable_length(0b1100, len(obj)) - else: - output += proc_variable_length(0b1010, len(obj)) - - objectsToWrite = [] - for objRef in sorted(obj) if isinstance(obj, set) else obj: - (isNew, output) = self.writeObjectReference(objRef, output) - if isNew: - objectsToWrite.append(objRef) - for objRef in objectsToWrite: - output = self.writeObject(objRef, output, setReferencePosition=True) - elif isinstance(obj, dict): - output += proc_variable_length(0b1101, len(obj)) - keys = [] - values = [] - objectsToWrite = [] - for key, value in sorted(iteritems(obj)): - keys.append(key) - values.append(value) - for key in keys: - (isNew, output) = self.writeObjectReference(key, output) - if isNew: - objectsToWrite.append(key) - for value in values: - (isNew, output) = self.writeObjectReference(value, output) - if isNew: - objectsToWrite.append(value) - for objRef in objectsToWrite: - output = self.writeObject(objRef, output, setReferencePosition=True) - return output - - def writeOffsetTable(self, output): - """Writes all of the object reference offsets.""" - all_positions = [] - writtenReferences = list(self.writtenReferences.items()) - writtenReferences.sort(key=lambda x: x[1]) - for obj,order in writtenReferences: - # Porting note: Elsewhere we deliberately replace empty unicdoe strings - # with empty binary strings, but the empty unicode string - # goes into writtenReferences. This isn't an issue in Py2 - # because u'' and b'' have the same hash; but it is in - # Py3, where they don't. - if bytes != str and obj == unicodeEmpty: - obj = b'' - position = self.referencePositions.get(obj) - if position is None: - raise InvalidPlistException("Error while writing offsets table. Object not found. %s" % obj) - output += self.binaryInt(position, self.trailer.offsetSize) - all_positions.append(position) - return output - - def binaryReal(self, obj): - # just use doubles - result = pack('>d', obj.value) - return result - - def binaryInt(self, obj, byteSize=None, as_number=False): - result = b'' - if byteSize is None: - byteSize = self.intSize(obj) - if byteSize == 1: - result += pack('>B', obj) - elif byteSize == 2: - result += pack('>H', obj) - elif byteSize == 4: - result += pack('>L', obj) - elif byteSize == 8: - if as_number: - result += pack('>q', obj) - else: - result += pack('>Q', obj) - elif byteSize <= 16: - try: - result = pack('>Q', 0) + pack('>Q', obj) - except struct_error as e: - raise InvalidPlistException("Unable to pack integer %d: %s" % (obj, e)) - else: - raise InvalidPlistException("Core Foundation can't handle integers with size greater than 16 bytes.") - return result - - def intSize(self, obj): - """Returns the number of bytes necessary to store the given integer.""" - # SIGNED - if obj < 0: # Signed integer, always 8 bytes - return 8 - # UNSIGNED - elif obj <= 0xFF: # 1 byte - return 1 - elif obj <= 0xFFFF: # 2 bytes - return 2 - elif obj <= 0xFFFFFFFF: # 4 bytes - return 4 - # SIGNED - # 0x7FFFFFFFFFFFFFFF is the max. - elif obj <= 0x7FFFFFFFFFFFFFFF: # 8 bytes signed - return 8 - elif obj <= 0xffffffffffffffff: # 8 bytes unsigned - return 16 - else: - raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.") - - def realSize(self, obj): - return 8 diff --git a/src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json b/src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json deleted file mode 100644 index 266e9dd89..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Id": "biplist", - "Name": "biplist", - "QDocModule": "qbs", - "QtUsage": "Used in the qbs dmg module for building Apple disk images.", - "Description": "biplist is a library for reading/writing binary plists.", - "Homepage": "https://bitbucket.org/wooster/biplist", - "Version": "1.0.2", - "License": "BSD 3-clause \"New\" or \"Revised\" License", - "LicenseId": "BSD-3-Clause", - "LicenseFile": "LICENSE", - "Copyright": "Copyright (c) 2010, Andrew Wooster" -} diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py deleted file mode 100644 index a6b812104..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .store import DSStore, DSStoreEntry - -__all__ = ['DSStore', 'DSStoreEntry'] diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py deleted file mode 100644 index 7eb314107..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -from .alias import * -from .bookmark import * - -__all__ = [ 'ALIAS_KIND_FILE', 'ALIAS_KIND_FOLDER', - 'ALIAS_HFS_VOLUME_SIGNATURE', - 'ALIAS_FIXED_DISK', 'ALIAS_NETWORK_DISK', 'ALIAS_400KB_FLOPPY_DISK', - 'ALIAS_800KB_FLOPPY_DISK', 'ALIAS_1_44MB_FLOPPY_DISK', - 'ALIAS_EJECTABLE_DISK', - 'ALIAS_NO_CNID', - 'kBookmarkPath', 'kBookmarkCNIDPath', 'kBookmarkFileProperties', - 'kBookmarkFileName', 'kBookmarkFileID', 'kBookmarkFileCreationDate', - 'kBookmarkTOCPath', 'kBookmarkVolumePath', - 'kBookmarkVolumeURL', 'kBookmarkVolumeName', 'kBookmarkVolumeUUID', - 'kBookmarkVolumeSize', 'kBookmarkVolumeCreationDate', - 'kBookmarkVolumeProperties', 'kBookmarkContainingFolder', - 'kBookmarkUserName', 'kBookmarkUID', 'kBookmarkWasFileReference', - 'kBookmarkCreationOptions', 'kBookmarkURLLengths', - 'kBookmarkSecurityExtension', - 'AppleShareInfo', - 'VolumeInfo', - 'TargetInfo', - 'Alias', - 'Bookmark', - 'Data', - 'URL' ] - - diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py deleted file mode 100644 index 0de6b9404..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py +++ /dev/null @@ -1,665 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file implements the Apple "bookmark" format, which is the replacement -# for the old-fashioned alias format. The details of this format were -# reverse engineered; some things are still not entirely clear. -# -from __future__ import unicode_literals, print_function - -import struct -import uuid -import datetime -import os -import sys -import pprint - -try: - from urlparse import urljoin -except ImportError: - from urllib.parse import urljoin - -if sys.platform == 'darwin': - from . import osx - -def iteritems(x): - return x.iteritems() - -try: - unicode -except NameError: - unicode = str - long = int - xrange = range - def iteritems(x): - return x.items() - -from .utils import * - -BMK_DATA_TYPE_MASK = 0xffffff00 -BMK_DATA_SUBTYPE_MASK = 0x000000ff - -BMK_STRING = 0x0100 -BMK_DATA = 0x0200 -BMK_NUMBER = 0x0300 -BMK_DATE = 0x0400 -BMK_BOOLEAN = 0x0500 -BMK_ARRAY = 0x0600 -BMK_DICT = 0x0700 -BMK_UUID = 0x0800 -BMK_URL = 0x0900 -BMK_NULL = 0x0a00 - -BMK_ST_ZERO = 0x0000 -BMK_ST_ONE = 0x0001 - -BMK_BOOLEAN_ST_FALSE = 0x0000 -BMK_BOOLEAN_ST_TRUE = 0x0001 - -# Subtypes for BMK_NUMBER are really CFNumberType values -kCFNumberSInt8Type = 1 -kCFNumberSInt16Type = 2 -kCFNumberSInt32Type = 3 -kCFNumberSInt64Type = 4 -kCFNumberFloat32Type = 5 -kCFNumberFloat64Type = 6 -kCFNumberCharType = 7 -kCFNumberShortType = 8 -kCFNumberIntType = 9 -kCFNumberLongType = 10 -kCFNumberLongLongType = 11 -kCFNumberFloatType = 12 -kCFNumberDoubleType = 13 -kCFNumberCFIndexType = 14 -kCFNumberNSIntegerType = 15 -kCFNumberCGFloatType = 16 - -# Resource property flags (from CFURLPriv.h) -kCFURLResourceIsRegularFile = 0x00000001 -kCFURLResourceIsDirectory = 0x00000002 -kCFURLResourceIsSymbolicLink = 0x00000004 -kCFURLResourceIsVolume = 0x00000008 -kCFURLResourceIsPackage = 0x00000010 -kCFURLResourceIsSystemImmutable = 0x00000020 -kCFURLResourceIsUserImmutable = 0x00000040 -kCFURLResourceIsHidden = 0x00000080 -kCFURLResourceHasHiddenExtension = 0x00000100 -kCFURLResourceIsApplication = 0x00000200 -kCFURLResourceIsCompressed = 0x00000400 -kCFURLResourceIsSystemCompressed = 0x00000400 -kCFURLCanSetHiddenExtension = 0x00000800 -kCFURLResourceIsReadable = 0x00001000 -kCFURLResourceIsWriteable = 0x00002000 -kCFURLResourceIsExecutable = 0x00004000 -kCFURLIsAliasFile = 0x00008000 -kCFURLIsMountTrigger = 0x00010000 - -# Volume property flags (from CFURLPriv.h) -kCFURLVolumeIsLocal = 0x1 # -kCFURLVolumeIsAutomount = 0x2 # -kCFURLVolumeDontBrowse = 0x4 # -kCFURLVolumeIsReadOnly = 0x8 # -kCFURLVolumeIsQuarantined = 0x10 -kCFURLVolumeIsEjectable = 0x20 # -kCFURLVolumeIsRemovable = 0x40 # -kCFURLVolumeIsInternal = 0x80 # -kCFURLVolumeIsExternal = 0x100 # -kCFURLVolumeIsDiskImage = 0x200 # -kCFURLVolumeIsFileVault = 0x400 -kCFURLVolumeIsLocaliDiskMirror = 0x800 -kCFURLVolumeIsiPod = 0x1000 # -kCFURLVolumeIsiDisk = 0x2000 -kCFURLVolumeIsCD = 0x4000 -kCFURLVolumeIsDVD = 0x8000 -kCFURLVolumeIsDeviceFileSystem = 0x10000 -kCFURLVolumeSupportsPersistentIDs = 0x100000000 -kCFURLVolumeSupportsSearchFS = 0x200000000 -kCFURLVolumeSupportsExchange = 0x400000000 -# reserved 0x800000000 -kCFURLVolumeSupportsSymbolicLinks = 0x1000000000 -kCFURLVolumeSupportsDenyModes = 0x2000000000 -kCFURLVolumeSupportsCopyFile = 0x4000000000 -kCFURLVolumeSupportsReadDirAttr = 0x8000000000 -kCFURLVolumeSupportsJournaling = 0x10000000000 -kCFURLVolumeSupportsRename = 0x20000000000 -kCFURLVolumeSupportsFastStatFS = 0x40000000000 -kCFURLVolumeSupportsCaseSensitiveNames = 0x80000000000 -kCFURLVolumeSupportsCasePreservedNames = 0x100000000000 -kCFURLVolumeSupportsFLock = 0x200000000000 -kCFURLVolumeHasNoRootDirectoryTimes = 0x400000000000 -kCFURLVolumeSupportsExtendedSecurity = 0x800000000000 -kCFURLVolumeSupports2TBFileSize = 0x1000000000000 -kCFURLVolumeSupportsHardLinks = 0x2000000000000 -kCFURLVolumeSupportsMandatoryByteRangeLocks = 0x4000000000000 -kCFURLVolumeSupportsPathFromID = 0x8000000000000 -# reserved 0x10000000000000 -kCFURLVolumeIsJournaling = 0x20000000000000 -kCFURLVolumeSupportsSparseFiles = 0x40000000000000 -kCFURLVolumeSupportsZeroRuns = 0x80000000000000 -kCFURLVolumeSupportsVolumeSizes = 0x100000000000000 -kCFURLVolumeSupportsRemoteEvents = 0x200000000000000 -kCFURLVolumeSupportsHiddenFiles = 0x400000000000000 -kCFURLVolumeSupportsDecmpFSCompression = 0x800000000000000 -kCFURLVolumeHas64BitObjectIDs = 0x1000000000000000 -kCFURLVolumePropertyFlagsAll = 0xffffffffffffffff - -BMK_URL_ST_ABSOLUTE = 0x0001 -BMK_URL_ST_RELATIVE = 0x0002 - -# Bookmark keys -# = 0x1003 -kBookmarkPath = 0x1004 # Array of path components -kBookmarkCNIDPath = 0x1005 # Array of CNIDs -kBookmarkFileProperties = 0x1010 # (CFURL rp flags, - # CFURL rp flags asked for, - # 8 bytes NULL) -kBookmarkFileName = 0x1020 -kBookmarkFileID = 0x1030 -kBookmarkFileCreationDate = 0x1040 -# = 0x1054 # ? -# = 0x1055 # ? -# = 0x1056 # ? -# = 0x1101 # ? -# = 0x1102 #Â ? -kBookmarkTOCPath = 0x2000 # A list of (TOC id, ?) pairs -kBookmarkVolumePath = 0x2002 -kBookmarkVolumeURL = 0x2005 -kBookmarkVolumeName = 0x2010 -kBookmarkVolumeUUID = 0x2011 # Stored (perversely) as a string -kBookmarkVolumeSize = 0x2012 -kBookmarkVolumeCreationDate = 0x2013 -kBookmarkVolumeProperties = 0x2020 # (CFURL vp flags, - # CFURL vp flags asked for, - # 8 bytes NULL) -kBookmarkVolumeIsRoot = 0x2030 # True if volume is FS root -kBookmarkVolumeBookmark = 0x2040 # Embedded bookmark for disk image (TOC id) -kBookmarkVolumeMountPoint = 0x2050 # A URL -# = 0x2070 -kBookmarkContainingFolder = 0xc001 # Index of containing folder in path -kBookmarkUserName = 0xc011 # User that created bookmark -kBookmarkUID = 0xc012 # UID that created bookmark -kBookmarkWasFileReference = 0xd001 # True if the URL was a file reference -kBookmarkCreationOptions = 0xd010 -kBookmarkURLLengths = 0xe003 # See below -# = 0xf017 # Localized name? -# = 0xf022 -kBookmarkSecurityExtension = 0xf080 -# = 0xf081 - -# kBookmarkURLLengths is an array that is set if the URL encoded by the -# bookmark had a base URL; in that case, each entry is the length of the -# base URL in question. Thus a URL -# -# file:///foo/bar/baz blam/blat.html -# -# will result in [3, 2], while the URL -# -# file:///foo bar/baz blam blat.html -# -# would result in [1, 2, 1, 1] - - -class Data (object): - def __init__(self, bytedata=None): - #: The bytes, stored as a byte string - self.bytes = bytes(bytedata) - - def __repr__(self): - return 'Data(%r)' % self.bytes - -class URL (object): - def __init__(self, base, rel=None): - if rel is not None: - #: The base URL, if any (a :class:`URL`) - self.base = base - #: The rest of the URL (a string) - self.relative = rel - else: - self.base = None - self.relative = base - - @property - def absolute(self): - """Return an absolute URL.""" - if self.base is None: - return self.relative - else: - base_abs = self.base.absolute - return urljoin(self.base.absolute, self.relative) - - def __repr__(self): - return 'URL(%r)' % self.absolute - -class Bookmark (object): - def __init__(self, tocs=None): - if tocs is None: - #: The TOCs for this Bookmark - self.tocs = [] - else: - self.tocs = tocs - - @classmethod - def _get_item(cls, data, hdrsize, offset): - offset += hdrsize - if offset > len(data) - 8: - raise ValueError('Offset out of range') - - length,typecode = struct.unpack(b'<II', data[offset:offset+8]) - - if len(data) - offset < 8 + length: - raise ValueError('Data item truncated') - - databytes = data[offset+8:offset+8+length] - - dsubtype = typecode & BMK_DATA_SUBTYPE_MASK - dtype = typecode & BMK_DATA_TYPE_MASK - - if dtype == BMK_STRING: - return databytes.decode('utf-8') - elif dtype == BMK_DATA: - return Data(databytes) - elif dtype == BMK_NUMBER: - if dsubtype == kCFNumberSInt8Type: - return ord(databytes[0]) - elif dsubtype == kCFNumberSInt16Type: - return struct.unpack(b'<h', databytes)[0] - elif dsubtype == kCFNumberSInt32Type: - return struct.unpack(b'<i', databytes)[0] - elif dsubtype == kCFNumberSInt64Type: - return struct.unpack(b'<q', databytes)[0] - elif dsubtype == kCFNumberFloat32Type: - return struct.unpack(b'<f', databytes)[0] - elif dsubtype == kCFNumberFloat64Type: - return struct.unpack(b'<d', databytes)[0] - elif dtype == BMK_DATE: - # Yes, dates really are stored as *BIG-endian* doubles; everything - # else is little-endian - secs = datetime.timedelta(seconds=struct.unpack(b'>d', databytes)[0]) - return osx_epoch + secs - elif dtype == BMK_BOOLEAN: - if dsubtype == BMK_BOOLEAN_ST_TRUE: - return True - elif dsubtype == BMK_BOOLEAN_ST_FALSE: - return False - elif dtype == BMK_UUID: - return uuid.UUID(bytes=databytes) - elif dtype == BMK_URL: - if dsubtype == BMK_URL_ST_ABSOLUTE: - return URL(databytes.decode('utf-8')) - elif dsubtype == BMK_URL_ST_RELATIVE: - baseoff,reloff = struct.unpack(b'<II', databytes) - base = cls._get_item(data, hdrsize, baseoff) - rel = cls._get_item(data, hdrsize, reloff) - return URL(base, rel) - elif dtype == BMK_ARRAY: - result = [] - for aoff in xrange(offset+8,offset+8+length,4): - eltoff, = struct.unpack(b'<I', data[aoff:aoff+4]) - result.append(cls._get_item(data, hdrsize, eltoff)) - return result - elif dtype == BMK_DICT: - result = {} - for eoff in xrange(offset+8,offset+8+length,8): - keyoff,valoff = struct.unpack(b'<II', data[eoff:eoff+8]) - key = cls._get_item(data, hdrsize, keyoff) - val = cls._get_item(data, hdrsize, valoff) - result[key] = val - return result - elif dtype == BMK_NULL: - return None - - print('Unknown data type %08x' % typecode) - return (typecode, databytes) - - @classmethod - def from_bytes(cls, data): - """Create a :class:`Bookmark` given byte data.""" - - if len(data) < 16: - raise ValueError('Not a bookmark file (too short)') - - if isinstance(data, bytearray): - data = bytes(data) - - magic,size,dummy,hdrsize = struct.unpack(b'<4sIII', data[0:16]) - - if magic != b'book': - raise ValueError('Not a bookmark file (bad magic) %r' % magic) - - if hdrsize < 16: - raise ValueError('Not a bookmark file (header size too short)') - - if hdrsize > size: - raise ValueError('Not a bookmark file (header size too large)') - - if size != len(data): - raise ValueError('Not a bookmark file (truncated)') - - tocoffset, = struct.unpack(b'<I', data[hdrsize:hdrsize+4]) - - tocs = [] - - while tocoffset != 0: - tocbase = hdrsize + tocoffset - if tocoffset > size - hdrsize \ - or size - tocbase < 20: - raise ValueError('TOC offset out of range') - - tocsize,tocmagic,tocid,nexttoc,toccount \ - = struct.unpack(b'<IIIII', - data[tocbase:tocbase+20]) - - if tocmagic != 0xfffffffe: - break - - tocsize += 8 - - if size - tocbase < tocsize: - raise ValueError('TOC truncated') - - if tocsize < 12 * toccount: - raise ValueError('TOC entries overrun TOC size') - - toc = {} - for n in xrange(0,toccount): - ebase = tocbase + 20 + 12 * n - eid,eoffset,edummy = struct.unpack(b'<III', - data[ebase:ebase+12]) - - if eid & 0x80000000: - eid = cls._get_item(data, hdrsize, eid & 0x7fffffff) - - toc[eid] = cls._get_item(data, hdrsize, eoffset) - - tocs.append((tocid, toc)) - - tocoffset = nexttoc - - return cls(tocs) - - def __getitem__(self, key): - for tid,toc in self.tocs: - if key in toc: - return toc[key] - raise KeyError('Key not found') - - def __setitem__(self, key, value): - if len(self.tocs) == 0: - self.tocs = [(1, {})] - self.tocs[0][1][key] = value - - def get(self, key, default=None): - """Lookup the value for a given key, returning a default if not - present.""" - for tid,toc in self.tocs: - if key in toc: - return toc[key] - return default - - @classmethod - def _encode_item(cls, item, offset): - if item is True: - result = struct.pack(b'<II', 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_TRUE) - elif item is False: - result = struct.pack(b'<II', 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_FALSE) - elif isinstance(item, unicode): - encoded = item.encode('utf-8') - result = (struct.pack(b'<II', len(encoded), BMK_STRING | BMK_ST_ONE) - + encoded) - elif isinstance(item, bytes): - result = (struct.pack(b'<II', len(item), BMK_STRING | BMK_ST_ONE) - + item) - elif isinstance(item, Data): - result = (struct.pack(b'<II', len(item.bytes), - BMK_DATA | BMK_ST_ONE) - + bytes(item.bytes)) - elif isinstance(item, bytearray): - result = (struct.pack(b'<II', len(item), - BMK_DATA | BMK_ST_ONE) - + bytes(item)) - elif isinstance(item, int) or isinstance(item, long): - if item > -0x80000000 and item < 0x7fffffff: - result = struct.pack(b'<IIi', 4, - BMK_NUMBER | kCFNumberSInt32Type, item) - else: - result = struct.pack(b'<IIq', 8, - BMK_NUMBER | kCFNumberSInt64Type, item) - elif isinstance(item, float): - result = struct.pack(b'<IId', 8, - BMK_NUMBER | kCFNumberFloat64Type, item) - elif isinstance(item, datetime.datetime): - secs = item - osx_epoch - result = struct.pack(b'<II', 8, BMK_DATE | BMK_ST_ZERO) \ - + struct.pack(b'>d', float(secs.total_seconds())) - elif isinstance(item, uuid.UUID): - result = struct.pack(b'<II', 16, BMK_UUID | BMK_ST_ONE) \ - + item.bytes - elif isinstance(item, URL): - if item.base: - baseoff = offset + 16 - reloff, baseenc = cls._encode_item(item.base, baseoff) - xoffset, relenc = cls._encode_item(item.relative, reloff) - result = b''.join([ - struct.pack(b'<IIII', 8, BMK_URL | BMK_URL_ST_RELATIVE, - baseoff, reloff), - baseenc, - relenc]) - else: - encoded = item.relative.encode('utf-8') - result = struct.pack(b'<II', len(encoded), - BMK_URL | BMK_URL_ST_ABSOLUTE) + encoded - elif isinstance(item, list): - ioffset = offset + 8 + len(item) * 4 - result = [struct.pack(b'<II', len(item) * 4, BMK_ARRAY | BMK_ST_ONE)] - enc = [] - for elt in item: - result.append(struct.pack(b'<I', ioffset)) - ioffset, ienc = cls._encode_item(elt, ioffset) - enc.append(ienc) - result = b''.join(result + enc) - elif isinstance(item, dict): - ioffset = offset + 8 + len(item) * 8 - result = [struct.pack(b'<II', len(item) * 8, BMK_DICT | BMK_ST_ONE)] - enc = [] - for k,v in iteritems(item): - result.append(struct.pack(b'<I', ioffset)) - ioffset, ienc = cls._encode_item(k, ioffset) - enc.append(ienc) - result.append(struct.pack(b'<I', ioffset)) - ioffset, ienc = cls._encode_item(v, ioffset) - enc.append(ienc) - result = b''.join(result + enc) - elif item is None: - result = struct.pack(b'<II', 0, BMK_NULL | BMK_ST_ONE) - else: - raise ValueError('Unknown item type when encoding: %s' % item) - - offset += len(result) - - # Pad to a multiple of 4 bytes - if offset & 3: - extra = 4 - (offset & 3) - result += b'\0' * extra - offset += extra - - return (offset, result) - - def to_bytes(self): - """Convert this :class:`Bookmark` to a byte representation.""" - - result = [] - tocs = [] - offset = 4 # For the offset to the first TOC - - # Generate the data and build the TOCs - for tid,toc in self.tocs: - entries = [] - - for k,v in iteritems(toc): - if isinstance(k, (str, unicode)): - noffset = offset - voffset, enc = self._encode_item(k, offset) - result.append(enc) - offset, enc = self._encode_item(v, voffset) - result.append(enc) - entries.append((noffset | 0x80000000, voffset)) - else: - entries.append((k, offset)) - offset, enc = self._encode_item(v, offset) - result.append(enc) - - # TOC entries must be sorted - CoreServicesInternal does a - # binary search to find data - entries.sort() - - tocs.append((tid, b''.join([struct.pack(b'<III',k,o,0) - for k,o in entries]))) - - first_toc_offset = offset - - # Now generate the TOC headers - for ndx,toc in enumerate(tocs): - tid, data = toc - if ndx == len(tocs) - 1: - next_offset = 0 - else: - next_offset = offset + 20 + len(data) - - result.append(struct.pack(b'<IIIII', len(data) - 8, - 0xfffffffe, - tid, - next_offset, - len(data) // 12)) - result.append(data) - - offset += 20 + len(data) - - # Finally, add the header (and the first TOC offset, which isn't part - # of the header, but goes just after it) - header = struct.pack(b'<4sIIIQQQQI', b'book', - offset + 48, - 0x10040000, - 48, - 0, 0, 0, 0, first_toc_offset) - - result.insert(0, header) - - return b''.join(result) - - @classmethod - def for_file(cls, path): - """Construct a :class:`Bookmark` for a given file.""" - - # Find the filesystem - st = osx.statfs(path) - vol_path = st.f_mntonname.decode('utf-8') - - # Grab its attributes - attrs = [osx.ATTR_CMN_CRTIME, - osx.ATTR_VOL_SIZE - | osx.ATTR_VOL_NAME - | osx.ATTR_VOL_UUID, - 0, 0, 0] - volinfo = osx.getattrlist(vol_path, attrs, 0) - - vol_crtime = volinfo[0] - vol_size = volinfo[1] - vol_name = volinfo[2] - vol_uuid = volinfo[3] - - # Also grab various attributes of the file - attrs = [(osx.ATTR_CMN_OBJTYPE - | osx.ATTR_CMN_CRTIME - | osx.ATTR_CMN_FILEID), 0, 0, 0, 0] - info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW) - - cnid = info[2] - crtime = info[1] - - if info[0] == osx.VREG: - flags = kCFURLResourceIsRegularFile - elif info[0] == osx.VDIR: - flags = kCFURLResourceIsDirectory - elif info[0] == osx.VLNK: - flags = kCFURLResourceIsSymbolicLink - else: - flags = kCFURLResourceIsRegularFile - - dirname, filename = os.path.split(path) - - relcount = 0 - if not os.path.isabs(dirname): - curdir = os.getcwd() - head, tail = os.path.split(curdir) - relcount = 0 - while head and tail: - relcount += 1 - head, tail = os.path.split(head) - dirname = os.path.join(curdir, dirname) - - foldername = os.path.basename(dirname) - - rel_path = os.path.relpath(path, vol_path) - - # Build the path arrays - name_path = [] - cnid_path = [] - head, tail = os.path.split(rel_path) - if not tail: - head, tail = os.path.split(head) - while head or tail: - if head: - attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0] - info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0) - cnid_path.insert(0, info[0]) - head, tail = os.path.split(head) - name_path.insert(0, tail) - else: - head, tail = os.path.split(head) - name_path.append(filename) - cnid_path.append(cnid) - - url_lengths = [relcount, len(name_path) - relcount] - - fileprops = Data(struct.pack(b'<QQQ', flags, 0x0f, 0)) - volprops = Data(struct.pack(b'<QQQ', 0x81 | kCFURLVolumeSupportsPersistentIDs, - 0x13ef | kCFURLVolumeSupportsPersistentIDs, 0)) - - toc = { - kBookmarkPath: name_path, - kBookmarkCNIDPath: cnid_path, - kBookmarkFileCreationDate: crtime, - kBookmarkFileProperties: fileprops, - kBookmarkContainingFolder: len(name_path) - 2, - kBookmarkVolumePath: vol_path, - kBookmarkVolumeIsRoot: vol_path == '/', - kBookmarkVolumeURL: URL('file://' + vol_path), - kBookmarkVolumeName: vol_name, - kBookmarkVolumeSize: vol_size, - kBookmarkVolumeCreationDate: vol_crtime, - kBookmarkVolumeUUID: str(vol_uuid).upper(), - kBookmarkVolumeProperties: volprops, - kBookmarkCreationOptions: 512, - kBookmarkWasFileReference: True, - kBookmarkUserName: 'unknown', - kBookmarkUID: 99, - } - - if relcount: - toc[kBookmarkURLLengths] = url_lengths - - return Bookmark([(1, toc)]) - - def __repr__(self): - result = ['Bookmark(['] - for tid,toc in self.tocs: - result.append('(0x%x, {\n' % tid) - for k,v in iteritems(toc): - if isinstance(k, (str, unicode)): - kf = repr(k) - else: - kf = '0x%04x' % k - result.append(' %s: %r\n' % (kf, v)) - result.append('}),\n') - result.append('])') - - return ''.join(result) diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py deleted file mode 100644 index 6a7d0a121..000000000 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import datetime - -ZERO = datetime.timedelta(0) -class UTC (datetime.tzinfo): - def utcoffset(self, dt): - return ZERO - def dst(self, dt): - return ZERO - def tzname(self, dt): - return 'UTC' - -utc = UTC() -mac_epoch = datetime.datetime(1904,1,1,0,0,0,0,utc) -unix_epoch = datetime.datetime(1970,1,1,0,0,0,0,utc) -osx_epoch = datetime.datetime(2001,1,1,0,0,0,0,utc) diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/LICENSE b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/LICENSE index e91f4eb38..e91f4eb38 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/LICENSE +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/LICENSE diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__init__.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__init__.py index 5fba876a7..5fba876a7 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__init__.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__init__.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__main__.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__main__.py index 908d959f1..908d959f1 100755 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__main__.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__main__.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/badge.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/badge.py index 3408c30b9..3408c30b9 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/badge.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/badge.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/colors.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/colors.py index 70b295c2a..70b295c2a 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/colors.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/colors.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/core.py index b514f77e9..b514f77e9 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/core.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/licensing.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/licensing.py index 1bd00b653..1bd00b653 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/licensing.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/licensing.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/qt_attribution.json b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/qt_attribution.json index d9f97ae2c..d9f97ae2c 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/qt_attribution.json +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/qt_attribution.json diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/resources.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/resources.py index 03e2a43f3..03e2a43f3 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/resources.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/resources.py diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/LICENSE b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/LICENSE index e91f4eb38..e91f4eb38 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/LICENSE +++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/LICENSE diff --git a/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py new file mode 100644 index 000000000..bc8702fab --- /dev/null +++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py @@ -0,0 +1,5 @@ +from .store import DSStore, DSStoreEntry + +__version__ = "1.3.1" + +__all__ = ["__version__", "DSStore", "DSStoreEntry"] diff --git a/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py new file mode 100644 index 000000000..cf17ab5f6 --- /dev/null +++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# ds_store: Examine .DS_Store files. +# + +import argparse +import os +import os.path +import pprint +import re +import sys + +from ds_store import DSStore +from ds_store.buddy import BuddyError + +_not_printable_re = re.compile(rb"[\x00-\x1f\x7f-\x9f]") + + +def usage(): + print(main.__doc__) + sys.exit(0) + + +def chunks(iterable, length): + for i in range(0, len(iterable), length): + yield i, iterable[i : i + length] + + +def pretty(value): + if isinstance(value, dict): + return f"{{\n {pprint.pformat(value, indent=4)[1:-1]}\n}}" + elif isinstance(value, bytearray): + lines = ["["] + for offset, chunk in chunks(value, 16): + printable_chunk = _not_printable_re.sub(b".", chunk).decode("latin-1") + hex_line = " ".join([f"{b:02x}" for b in chunk]) + line = f" {offset:08x} {hex_line:<47s} {printable_chunk}" + lines.append(line) + lines.append("]") + return "\n".join(lines) + elif isinstance(value, bytes): + return value.decode("latin-1") + else: + return value + + +def main(argv): + """Display the contents of the .DS_Store file at the specified path. + + If you specify just a directory, ds_store will inspect the .DS_Store + file in that directory. If no paths are specified, ds_store will + display the contents of the .DS_Store file in the current directory. + """ + + parser = argparse.ArgumentParser(description=main.__doc__) + parser.add_argument("paths", nargs="*") + args = parser.parse_args(argv) + + if len(args.paths) == 0: + args.paths = ["."] + + failed = False + for path in args.paths: + if os.path.isdir(path): + path = os.path.join(path, ".DS_Store") + + if not os.path.exists(path) or not os.path.isfile(path): + print(f"ds_store: {path} not found", file=sys.stderr) + failed = True + continue + + try: + with DSStore.open(path, "r") as d: + print(path) + print("") + + max_name_len = 0 + for entry in d: + name_len = len(entry.filename) + if name_len > max_name_len: + max_name_len = name_len + + for entry in d: + print( + "{:<{width}} {} {}".format( + entry.filename, + entry.code.decode("latin-1"), + pretty(entry.value), + width=max_name_len, + ) + ) + print("") + except BuddyError as e: + print(f"ds_store: {path}: {e}") + failed = True + + if failed: + sys.exit(1) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/buddy.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/buddy.py index 320768cd3..27b11b3f7 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/buddy.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/buddy.py @@ -1,23 +1,14 @@ -# -*- coding: utf-8 -*- -import os +import binascii import bisect +import os import struct -import binascii -try: - {}.iterkeys - iterkeys = lambda x: x.iterkeys() -except AttributeError: - iterkeys = lambda x: x.keys() -try: - unicode -except NameError: - unicode = str class BuddyError(Exception): pass -class Block(object): + +class Block: def __init__(self, allocator, offset, size): self._allocator = allocator self._offset = offset @@ -25,7 +16,7 @@ class Block(object): self._value = bytearray(allocator.read(offset, size)) self._pos = 0 self._dirty = False - + def __len__(self): return self._size @@ -34,7 +25,7 @@ class Block(object): def __exit__(self, exc_type, exc_value, traceback): self.close() - + def close(self): if self._dirty: self.flush() @@ -46,13 +37,13 @@ class Block(object): def invalidate(self): self._dirty = False - + def zero_fill(self): len = self._size - self._pos - zeroes = b'\0' * len - self._value[self._pos:self._size] = zeroes + zeroes = b"\0" * len + self._value[self._pos : self._size] = zeroes self._dirty = True - + def tell(self): return self._pos @@ -63,12 +54,12 @@ class Block(object): pos = self._size - pos if pos < 0 or pos > self._size: - raise ValueError('Seek out of range in Block instance') + raise ValueError("Seek out of range in Block instance") self._pos = pos def read(self, size_or_format): - if isinstance(size_or_format, (str, unicode, bytes)): + if isinstance(size_or_format, (str, bytes)): size = struct.calcsize(size_or_format) fmt = size_or_format else: @@ -76,11 +67,11 @@ class Block(object): fmt = None if self._size - self._pos < size: - raise BuddyError('Unable to read %lu bytes in block' % size) + raise BuddyError("Unable to read %lu bytes in block" % size) - data = self._value[self._pos:self._pos + size] + data = self._value[self._pos : self._pos + size] self._pos += size - + if fmt is not None: if isinstance(data, bytearray): return struct.unpack_from(fmt, bytes(data)) @@ -96,11 +87,11 @@ class Block(object): data = data_or_format if self._pos + len(data) > self._size: - raise ValueError('Attempt to write past end of Block') + raise ValueError("Attempt to write past end of Block") - self._value[self._pos:self._pos + len(data)] = data + self._value[self._pos : self._pos + len(data)] = data self._pos += len(data) - + self._dirty = True def insert(self, data_or_format, *args): @@ -109,78 +100,80 @@ class Block(object): else: data = data_or_format - del self._value[-len(data):] - self._value[self._pos:self._pos] = data + del self._value[-len(data) :] + self._value[self._pos : self._pos] = data self._pos += len(data) self._dirty = True def delete(self, size): if self._pos + size > self._size: - raise ValueError('Attempt to delete past end of Block') - del self._value[self._pos:self._pos + size] - self._value += b'\0' * size + raise ValueError("Attempt to delete past end of Block") + del self._value[self._pos : self._pos + size] + self._value += b"\0" * size self._dirty = True - + def __str__(self): return binascii.b2a_hex(self._value) - -class Allocator(object): + + +class Allocator: def __init__(self, the_file): self._file = the_file self._dirty = False self._file.seek(0) - + # Read the header - magic1, magic2, offset, size, offset2, self._unknown1 \ - = self.read(-4, '>I4sIII16s') + (magic1, magic2, offset, size, offset2, self._unknown1) = self.read( + -4, ">I4sIII16s" + ) - if magic2 != b'Bud1' or magic1 != 1: - raise BuddyError('Not a buddy file') + if magic2 != b"Bud1" or magic1 != 1: + raise BuddyError("Not a buddy file") if offset != offset2: - raise BuddyError('Root addresses differ') + raise BuddyError("Root addresses differ") self._root = Block(self, offset, size) # Read the block offsets - count, self._unknown2 = self._root.read('>II') + count, self._unknown2 = self._root.read(">II") self._offsets = [] c = (count + 255) & ~255 while c: - self._offsets += self._root.read('>256I') + self._offsets += self._root.read(">256I") c -= 256 self._offsets = self._offsets[:count] - + # Read the TOC self._toc = {} - count = self._root.read('>I')[0] + count = self._root.read(">I")[0] for n in range(count): - nlen = self._root.read('B')[0] + nlen = self._root.read("B")[0] name = bytes(self._root.read(nlen)) - value = self._root.read('>I')[0] + value = self._root.read(">I")[0] self._toc[name] = value # Read the free lists self._free = [] for n in range(32): - count = self._root.read('>I') - self._free.append(list(self._root.read('>%uI' % count))) - + count = self._root.read(">I") + self._free.append(list(self._root.read(">%uI" % count))) + @classmethod - def open(cls, file_or_name, mode='r+'): - if isinstance(file_or_name, (str, unicode)): - if not 'b' in mode: - mode = mode[:1] + 'b' + mode[1:] + def open(cls, file_or_name, mode="r+"): + if isinstance(file_or_name, str): + if "b" not in mode: + mode = mode[:1] + "b" + mode[1:] f = open(file_or_name, mode) else: f = file_or_name - if 'w' in mode: + if "w" in mode: # Create an empty file in this case f.truncate() - + # An empty root block needs 1264 bytes: # # 0 4 offset count @@ -190,7 +183,7 @@ class Allocator(object): # 1032 4 toc count (0) # 1036 228 free list # total 1264 - + # The free list will contain the following: # # 0 5 * 4 no blocks of width less than 5 @@ -208,28 +201,38 @@ class Allocator(object): # located at offset 2**n where n is its width.) # Write the header - header = struct.pack(b'>I4sIII16s', - 1, b'Bud1', - 2048, 1264, 2048, - b'\x00\x00\x10\x0c' - b'\x00\x00\x00\x87' - b'\x00\x00\x20\x0b' - b'\x00\x00\x00\x00') + header = struct.pack( + b">I4sIII16s", + 1, + b"Bud1", + 2048, + 1264, + 2048, + b"\x00\x00\x10\x0c" + b"\x00\x00\x00\x87" + b"\x00\x00\x20\x0b" + b"\x00\x00\x00\x00", + ) f.write(header) - f.write(b'\0' * 2016) - + f.write(b"\0" * 2016) + # Write the root block - free_list = [struct.pack(b'>5I', 0, 0, 0, 0, 0)] + free_list = [struct.pack(b">5I", 0, 0, 0, 0, 0)] for n in range(5, 11): - free_list.append(struct.pack(b'>II', 1, 2**n)) - free_list.append(struct.pack(b'>I', 0)) + free_list.append(struct.pack(b">II", 1, 2**n)) + free_list.append(struct.pack(b">I", 0)) for n in range(12, 31): - free_list.append(struct.pack(b'>II', 1, 2**n)) - free_list.append(struct.pack(b'>I', 0)) - - root = b''.join([struct.pack(b'>III', 1, 0, 2048 | 5), - struct.pack(b'>I', 0) * 255, - struct.pack(b'>I', 0)] + free_list) + free_list.append(struct.pack(b">II", 1, 2**n)) + free_list.append(struct.pack(b">I", 0)) + + root = b"".join( + [ + struct.pack(b">III", 1, 0, 2048 | 5), + struct.pack(b">I", 0) * 255, + struct.pack(b">I", 0), + ] + + free_list + ) f.write(root) return Allocator(f) @@ -239,7 +242,7 @@ class Allocator(object): def __exit__(self, exc_type, exc_value, traceback): self.close() - + def close(self): self.flush() self._file.close() @@ -252,51 +255,57 @@ class Allocator(object): self._write_root_block_into(rblk) addr = self._offsets[0] - offset = addr & ~0x1f - size = 1 << (addr & 0x1f) + offset = addr & ~0x1F + size = 1 << (addr & 0x1F) self._file.seek(0, os.SEEK_SET) - self._file.write(struct.pack(b'>I4sIII16s', - 1, b'Bud1', - offset, size, offset, - self._unknown1)) + self._file.write( + struct.pack( + b">I4sIII16s", 1, b"Bud1", offset, size, offset, self._unknown1 + ) + ) self._dirty = False - + self._file.flush() - + def read(self, offset, size_or_format): - """Read data at `offset', or raise an exception. `size_or_format' - may either be a byte count, in which case we return raw data, - or a format string for `struct.unpack', in which case we - work out the size and unpack the data before returning it.""" + """Read data at `offset', or raise an exception. + + `size_or_format' may either be a byte count, in which case we + return raw data, or a format string for `struct.unpack', in + which case we work out the size and unpack the data before + returning it. + """ # N.B. There is a fixed offset of four bytes(!) self._file.seek(offset + 4, os.SEEK_SET) - if isinstance(size_or_format, (str, unicode)): + if isinstance(size_or_format, str): size = struct.calcsize(size_or_format) fmt = size_or_format else: size = size_or_format fmt = None - + ret = self._file.read(size) if len(ret) < size: - ret += b'\0' * (size - len(ret)) + ret += b"\0" * (size - len(ret)) if fmt is not None: if isinstance(ret, bytearray): ret = struct.unpack_from(fmt, bytes(ret)) else: ret = struct.unpack(fmt, ret) - + return ret def write(self, offset, data_or_format, *args): - """Write data at `offset', or raise an exception. `data_or_format' - may either be the data to write, or a format string for `struct.pack', - in which case we pack the additional arguments and write the - resulting data.""" + """Write data at `offset', or raise an exception. + + `data_or_format' may either be the data to write, or a format + string for `struct.pack', in which case we pack the additional + arguments and write the resulting data. + """ # N.B. There is a fixed offset of four bytes(!) self._file.seek(offset + 4, os.SEEK_SET) @@ -313,11 +322,11 @@ class Allocator(object): except IndexError: return None - offset = addr & ~0x1f - size = 1 << (addr & 0x1f) + offset = addr & ~0x1F + size = 1 << (addr & 0x1F) return Block(self, offset, size) - + def _root_block_size(self): """Return the number of bytes required by the root block.""" # Offsets @@ -330,62 +339,62 @@ class Allocator(object): # Free list size += sum([4 + 4 * len(fl) for fl in self._free]) - + return size def _write_root_block_into(self, block): # Offsets - block.write('>II', len(self._offsets), self._unknown2) - block.write('>%uI' % len(self._offsets), *self._offsets) + block.write(">II", len(self._offsets), self._unknown2) + block.write(">%uI" % len(self._offsets), *self._offsets) extra = len(self._offsets) & 255 if extra: - block.write(b'\0\0\0\0' * (256 - extra)) + block.write(b"\0\0\0\0" * (256 - extra)) # TOC keys = list(self._toc.keys()) keys.sort() - block.write('>I', len(keys)) + block.write(">I", len(keys)) for k in keys: - block.write('B', len(k)) + block.write("B", len(k)) block.write(k) - block.write('>I', self._toc[k]) + block.write(">I", self._toc[k]) # Free list for w, f in enumerate(self._free): - block.write('>I', len(f)) + block.write(">I", len(f)) if len(f): - block.write('>%uI' % len(f), *f) + block.write(">%uI" % len(f), *f) def _buddy(self, offset, width): f = self._free[width] b = offset ^ (1 << width) - + try: ndx = f.index(b) except ValueError: ndx = None - + return (f, b, ndx) def _release(self, offset, width): # Coalesce while True: - f,b,ndx = self._buddy(offset, width) + f, b, ndx = self._buddy(offset, width) if ndx is None: break - + offset &= b width += 1 del f[ndx] - + # Add to the list bisect.insort(f, offset) # Mark as dirty self._dirty = True - + def _alloc(self, width): w = width while not self._free[w]: @@ -407,15 +416,15 @@ class Allocator(object): except ValueError: block = len(self._offsets) self._offsets.append(0) - + # Compute block width width = max(bytes.bit_length(), 5) addr = self._offsets[block] - offset = addr & ~0x1f - + offset = addr & ~0x1F + if addr: - blkwidth = addr & 0x1f + blkwidth = addr & 0x1F if blkwidth == width: return block self._release(offset, width) @@ -424,13 +433,13 @@ class Allocator(object): offset = self._alloc(width) self._offsets[block] = offset | width return block - + def release(self, block): addr = self._offsets[block] if addr: - width = addr & 0x1f - offset = addr & ~0x1f + width = addr & 0x1F + offset = addr & ~0x1F self._release(offset, width) if block == len(self._offsets): @@ -442,37 +451,36 @@ class Allocator(object): return len(self._toc) def __getitem__(self, key): - if not isinstance(key, (str, unicode)): - raise TypeError('Keys must be of string type') + if not isinstance(key, str): + raise TypeError("Keys must be of string type") if not isinstance(key, bytes): - key = key.encode('latin_1') + key = key.encode("latin_1") return self._toc[key] def __setitem__(self, key, value): - if not isinstance(key, (str, unicode)): - raise TypeError('Keys must be of string type') + if not isinstance(key, str): + raise TypeError("Keys must be of string type") if not isinstance(key, bytes): - key = key.encode('latin_1') + key = key.encode("latin_1") self._toc[key] = value self._dirty = True def __delitem__(self, key): - if not isinstance(key, (str, unicode)): - raise TypeError('Keys must be of string type') + if not isinstance(key, str): + raise TypeError("Keys must be of string type") if not isinstance(key, bytes): - key = key.encode('latin_1') + key = key.encode("latin_1") del self._toc[key] self._dirty = True def iterkeys(self): - return iterkeys(self._toc) + return self._toc.keys() def keys(self): - return iterkeys(self._toc) + return self._toc.keys() def __iter__(self): - return iterkeys(self._toc) + return self._toc.keys() def __contains__(self, key): return key in self._toc - diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/qt_attribution.json b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/qt_attribution.json index a4854d1ed..a4854d1ed 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/qt_attribution.json +++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/qt_attribution.json diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/store.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/store.py index b6f805b24..58e9e1bce 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/store.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/store.py @@ -1,48 +1,36 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division - -import binascii +import plistlib import struct -import biplist -import mac_alias -try: - next -except NameError: - next = lambda x: x.next() -try: - unicode -except NameError: - unicode = str +import mac_alias from . import buddy -class ILocCodec(object): + +class ILocCodec: @staticmethod def encode(point): - return struct.pack(b'>IIII', point[0], point[1], - 0xffffffff, 0xffff0000) + return struct.pack(b">IIII", point[0], point[1], 0xFFFFFFFF, 0xFFFF0000) @staticmethod def decode(bytesData): if isinstance(bytesData, bytearray): - x, y = struct.unpack_from(b'>II', bytes(bytesData[:8])) + x, y = struct.unpack_from(b">II", bytes(bytesData[:8])) else: - x, y = struct.unpack(b'>II', bytesData[:8]) + x, y = struct.unpack(b">II", bytesData[:8]) return (x, y) -class PlistCodec(object): + +class PlistCodec: @staticmethod def encode(plist): - return biplist.writePlistToString(plist) + return plistlib.dumps(plist, fmt=plistlib.FMT_BINARY) @staticmethod def decode(bytes): - return biplist.readPlistFromString(bytes) + return plistlib.loads(bytes) -class BookmarkCodec(object): + +class BookmarkCodec: @staticmethod def encode(bmk): return bmk.to_bytes() @@ -51,70 +39,73 @@ class BookmarkCodec(object): def decode(bytes): return mac_alias.Bookmark.from_bytes(bytes) + # This list tells the code how to decode particular kinds of entry in the # .DS_Store file. This is really a convenience, and we currently only # support a tiny subset of the possible entry types. codecs = { - b'Iloc': ILocCodec, - b'bwsp': PlistCodec, - b'lsvp': PlistCodec, - b'lsvP': PlistCodec, - b'icvp': PlistCodec, - b'pBBk': BookmarkCodec - } - -class DSStoreEntry(object): + b"Iloc": ILocCodec, + b"bwsp": PlistCodec, + b"lsvp": PlistCodec, + b"lsvP": PlistCodec, + b"icvp": PlistCodec, + b"pBBk": BookmarkCodec, +} + + +class DSStoreEntry: """Holds the data from an entry in a ``.DS_Store`` file. Note that this is - not meant to represent the entry itself---i.e. if you change the type - or value, your changes will *not* be reflected in the underlying file. + not meant to represent the entry itself---i.e. if you change the type or + value, your changes will *not* be reflected in the underlying file. - If you want to make a change, you should either use the :class:`DSStore` - object's :meth:`DSStore.insert` method (which will replace a key if it - already exists), or the mapping access mode for :class:`DSStore` (often - simpler anyway). + If you want to make a change, you should either use the + :class:`DSStore` object's :meth:`DSStore.insert` method (which will + replace a key if it already exists), or the mapping access mode for + :class:`DSStore` (often simpler anyway). """ + def __init__(self, filename, code, typecode, value=None): if str != bytes and type(filename) == bytes: - filename = filename.decode('utf-8') + filename = filename.decode("utf-8") if not isinstance(code, bytes): - code = code.encode('latin_1') + code = code.encode("latin_1") self.filename = filename self.code = code self.type = typecode self.value = value - + @classmethod def read(cls, block): - """Read a ``.DS_Store`` entry from the containing Block""" + """Read a ``.DS_Store`` entry from the containing Block.""" # First read the filename - nlen = block.read(b'>I')[0] - filename = block.read(2 * nlen).decode('utf-16be') + nlen = block.read(b">I")[0] + filename = block.read(2 * nlen).decode("utf-16be") # Next, read the code and type - code, typecode = block.read(b'>4s4s') + code, typecode = block.read(b">4s4s") # Finally, read the data - if typecode == b'bool': - value = block.read(b'>?')[0] - elif typecode == b'long' or typecode == b'shor': - value = block.read(b'>I')[0] - elif typecode == b'blob': - vlen = block.read(b'>I')[0] + if typecode == b"bool": + value = block.read(b">?")[0] + elif typecode == b"long" or typecode == b"shor": + value = block.read(b">I")[0] + elif typecode == b"blob": + vlen = block.read(b">I")[0] value = block.read(vlen) codec = codecs.get(code, None) if codec: value = codec.decode(value) typecode = codec - elif typecode == b'ustr': - vlen = block.read(b'>I')[0] - value = block.read(2 * vlen).decode('utf-16be') - elif typecode == b'type': - value = block.read(b'>4s')[0] - elif typecode == b'comp' or typecode == b'dutc': - value = block.read(b'>Q')[0] + elif typecode == b"ustr": + vlen = block.read(b">I")[0] + value = block.read(2 * vlen).decode("utf-16be") + elif typecode == b"type": + value = block.read(b">4s")[0] + elif typecode == b"comp" or typecode == b"dutc": + value = block.read(b">Q")[0] else: raise ValueError('Unknown type code "%s"' % typecode) @@ -122,152 +113,138 @@ class DSStoreEntry(object): def __lt__(self, other): if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') + raise TypeError("Can only compare against other DSStoreEntry objects") sfl = self.filename.lower() ofl = other.filename.lower() - return (sfl < ofl - or (self.filename == other.filename - and self.code < other.code)) + return sfl < ofl or (self.filename == other.filename and self.code < other.code) def __le__(self, other): if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') + raise TypeError("Can only compare against other DSStoreEntry objects") sfl = self.filename.lower() ofl = other.filename.lower() - return (sfl < ofl - or (sfl == ofl - and self.code <= other.code)) + return sfl < ofl or (sfl == ofl and self.code <= other.code) def __eq__(self, other): if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') + raise TypeError("Can only compare against other DSStoreEntry objects") sfl = self.filename.lower() ofl = other.filename.lower() - return (sfl == ofl - and self.code == other.code) + return sfl == ofl and self.code == other.code def __ne__(self, other): if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') + raise TypeError("Can only compare against other DSStoreEntry objects") sfl = self.filename.lower() ofl = other.filename.lower() - return (sfl != ofl - or self.code != other.code) + return sfl != ofl or self.code != other.code def __gt__(self, other): if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') + raise TypeError("Can only compare against other DSStoreEntry objects") sfl = self.filename.lower() ofl = other.filename.lower() - + selfCode = self.code if str != bytes and type(selfCode) is bytes: - selfCode = selfCode.decode('utf-8') + selfCode = selfCode.decode("utf-8") otherCode = other.code if str != bytes and type(otherCode) is bytes: - otherCode = otherCode.decode('utf-8') - - return (sfl > ofl or (sfl == ofl and selfCode > otherCode)) + otherCode = otherCode.decode("utf-8") + + return sfl > ofl or (sfl == ofl and selfCode > otherCode) def __ge__(self, other): if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') + raise TypeError("Can only compare against other DSStoreEntry objects") sfl = self.filename.lower() ofl = other.filename.lower() - return (sfl > ofl - or (sfl == ofl - and self.code >= other.code)) + return sfl > ofl or (sfl == ofl and self.code >= other.code) - def __cmp__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - r = cmp(self.filename.lower(), other.filename.lower()) - if r: - return r - return cmp(self.code, other.code) - def byte_length(self): - """Compute the length of this entry, in bytes""" - utf16 = self.filename.encode('utf-16be') - l = 4 + len(utf16) + 8 + """Compute the length of this entry, in bytes.""" + utf16 = self.filename.encode("utf-16be") + length = 4 + len(utf16) + 8 - if isinstance(self.type, unicode): - entry_type = self.type.encode('latin_1') + if isinstance(self.type, str): + entry_type = self.type.encode("latin_1") value = self.value elif isinstance(self.type, (bytes, str)): entry_type = self.type value = self.value else: - entry_type = b'blob' + entry_type = b"blob" value = self.type.encode(self.value) - - if entry_type == b'bool': - l += 1 - elif entry_type == b'long' or entry_type == b'shor': - l += 4 - elif entry_type == b'blob': - l += 4 + len(value) - elif entry_type == b'ustr': - utf16 = value.encode('utf-16be') - l += 4 + len(utf16) - elif entry_type == b'type': - l += 4 - elif entry_type == b'comp' or entry_type == b'dutc': - l += 8 + + if entry_type == b"bool": + length += 1 + elif entry_type == b"long" or entry_type == b"shor": + length += 4 + elif entry_type == b"blob": + length += 4 + len(value) + elif entry_type == b"ustr": + utf16 = value.encode("utf-16be") + length += 4 + len(utf16) + elif entry_type == b"type": + length += 4 + elif entry_type == b"comp" or entry_type == b"dutc": + length += 8 else: raise ValueError('Unknown type code "%s"' % entry_type) - return l - + return length + def write(self, block, insert=False): - """Write this entry to the specified Block""" + """Write this entry to the specified Block.""" if insert: w = block.insert else: w = block.write - if isinstance(self.type, unicode): - entry_type = self.type.encode('latin_1') + if isinstance(self.type, str): + entry_type = self.type.encode("latin_1") value = self.value elif isinstance(self.type, (bytes, str)): entry_type = self.type value = self.value else: - entry_type = b'blob' + entry_type = b"blob" value = self.type.encode(self.value) - utf16 = self.filename.encode('utf-16be') - w(b'>I', len(utf16) // 2) + utf16 = self.filename.encode("utf-16be") + w(b">I", len(utf16) // 2) w(utf16) - w(b'>4s4s', self.code, entry_type) - - if entry_type == b'bool': - w(b'>?', value) - elif entry_type == b'long' or entry_type == b'shor': - w(b'>I', value) - elif entry_type == b'blob': - w(b'>I', len(value)) + w(b">4s4s", self.code, entry_type) + + if entry_type == b"bool": + w(b">?", value) + elif entry_type == b"long" or entry_type == b"shor": + w(b">I", value) + elif entry_type == b"blob": + w(b">I", len(value)) w(value) - elif entry_type == b'ustr': - utf16 = value.encode('utf-16be') - w(b'>I', len(utf16) // 2) + elif entry_type == b"ustr": + utf16 = value.encode("utf-16be") + w(b">I", len(utf16) // 2) w(utf16) - elif entry_type == b'type': - if isinstance(value, unicode): - value = value.encode('latin_1') - w(b'>4s', value) - elif entry_type == b'comp' or entry_type == b'dutc': - w(b'>Q', value) + elif entry_type == b"type": + if isinstance(value, str): + value = value.encode("latin_1") + w(b">4s", value) + elif entry_type == b"comp" or entry_type == b"dutc": + w(b">Q", value) else: raise ValueError('Unknown type code "%s"' % entry_type) - + def __repr__(self): - return '<%s %s>' % (self.filename, self.code) + return f"<{self.filename} {self.code}>" + + +class DSStore: + """Python interface to a ``.DS_Store`` file. Works by manipulating the + file on the disk---so this code will work with ``.DS_Store`` files for. -class DSStore(object): - """Python interface to a ``.DS_Store`` file. Works by manipulating the file - on the disk---so this code will work with ``.DS_Store`` files for *very* - large directories. + *very* large directories. A :class:`DSStore` object can be used as if it was a mapping, e.g.:: @@ -280,7 +257,7 @@ class DSStore(object): Currently, we know how to decode "Iloc", "bwsp", "lsvp", "lsvP" and "icvp" blobs. "Iloc" decodes to an (x, y) tuple, while the others are all decoded - using ``biplist``. + using ``biplist`` or ``plistlib`` depending on Python version. Assignment also works, e.g.:: @@ -292,38 +269,48 @@ class DSStore(object): This is usually going to be the most convenient interface, though occasionally (for instance when creating a new ``.DS_Store`` file) you - may wish to drop down to using :class:`DSStoreEntry` objects directly.""" + may wish to drop down to using :class:`DSStoreEntry` objects directly. + """ + def __init__(self, store): self._store = store - self._superblk = self._store['DSDB'] + self._superblk = self._store["DSDB"] with self._get_block(self._superblk) as s: - self._rootnode, self._levels, self._records, \ - self._nodes, self._page_size = s.read(b'>IIIII') + ( + self._rootnode, + self._levels, + self._records, + self._nodes, + self._page_size, + ) = s.read(b">IIIII") self._min_usage = 2 * self._page_size // 3 self._dirty = False - + @classmethod - def open(cls, file_or_name, mode='r+', initial_entries=None): + def open(cls, file_or_name, mode="r+", initial_entries=None): """Open a ``.DS_Store`` file; pass either a Python file object, or a - filename in the ``file_or_name`` argument and a file access mode in - the ``mode`` argument. If you are creating a new file using the "w" - or "w+" modes, you may also specify a list of entries with which - to initialise the file.""" + filename in the ``file_or_name`` argument and a file access mode in the + ``mode`` argument. + + If you are creating a new file using the "w" or "w+" modes, you + may also specify a list of entries with which to initialise the + file. + """ store = buddy.Allocator.open(file_or_name, mode) - - if mode == 'w' or mode == 'w+': + + if mode == "w" or mode == "w+": superblk = store.allocate(20) - store['DSDB'] = superblk + store["DSDB"] = superblk page_size = 4096 - + if not initial_entries: root = store.allocate(page_size) - + with store.get_block(root) as rootblk: rootblk.zero_fill() with store.get_block(superblk) as s: - s.write(b'>IIIII', root, 0, 0, 1, page_size) + s.write(b">IIIII", root, 0, 0, 1, page_size) else: # Make sure they're in sorted order initial_entries = list(initial_entries) @@ -357,48 +344,54 @@ class DSStore(object): if len(nodes) == 1: break - + current_level = next_level next_level = [] ptr_size = 4 # Allocate nodes ptrs = [store.allocate(page_size) for n in range(node_count)] - + # Generate nodes pointers = [] prev_pointers = None for level in levels: ppndx = 0 - lptrs = ptrs[-len(level):] - del ptrs[-len(level):] + lptrs = ptrs[-len(level) :] + del ptrs[-len(level) :] for node in level: ndx = lptrs.pop(0) if prev_pointers is None: with store.get_block(ndx) as block: - block.write(b'>II', 0, len(node)) + block.write(b">II", 0, len(node)) for e in node: e.write(block) else: next_node = prev_pointers[ppndx + len(node)] - node_ptrs = prev_pointers[ppndx:ppndx+len(node)] - + node_ptrs = prev_pointers[ppndx : ppndx + len(node)] + with store.get_block(ndx) as block: - block.write(b'>II', next_node, len(node)) + block.write(b">II", next_node, len(node)) for ptr, e in zip(node_ptrs, node): - block.write(b'>I', ptr) + block.write(b">I", ptr) e.write(block) - + pointers.append(ndx) prev_pointers = pointers pointers = [] - + root = prev_pointers[0] with store.get_block(superblk) as s: - s.write(b'>IIIII', root, len(levels), len(initial_entries), - node_count, page_size) - + s.write( + b">IIIII", + root, + len(levels), + len(initial_entries), + node_count, + page_size, + ) + return DSStore(store) def _get_block(self, number): @@ -410,25 +403,31 @@ class DSStore(object): self._dirty = False with self._get_block(self._superblk) as s: - s.write(b'>IIIII', self._rootnode, self._levels, self._records, - self._nodes, self._page_size) + s.write( + b">IIIII", + self._rootnode, + self._levels, + self._records, + self._nodes, + self._page_size, + ) self._store.flush() def close(self): """Flush dirty data and close the underlying file.""" self.flush() self._store.close() - + def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close() - + # Internal B-Tree nodes look like this: # # [ next | count | (ptr0 | rec0) | (ptr1 | rec1) ... (ptrN | recN) ] - + # Leaf nodes look like this: # # [ 0 | count | rec0 | rec1 ... recN ] @@ -438,10 +437,10 @@ class DSStore(object): if node is None: node = self._rootnode with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") if next_node: for n in range(count): - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] for e in self._traverse(ptr): yield e e = DSStoreEntry.read(block) @@ -456,23 +455,30 @@ class DSStore(object): # Display the data in `node' def _dump_node(self, node): with self._get_block(node) as block: - next_node, count = block.read(b'>II') - print('next: %u\ncount: %u\n' % (next_node, count)) + next_node, count = block.read(b">II") + print("next: %u\ncount: %u\n" % (next_node, count)) for n in range(count): if next_node: - ptr = block.read(b'>I')[0] - print('%8u ' % ptr, end=' ') + ptr = block.read(b">I")[0] + print("%8u " % ptr, end=" ") else: - print(' ', end=' ') + print(" ", end=" ") e = DSStoreEntry.read(block) - print(e, ' (%u)' % e.byte_length()) - print('used: %u' % block.tell()) + print(e, " (%u)" % e.byte_length()) + print("used: %u" % block.tell()) # Display the data in the super block def _dump_super(self): - print('root: %u\nlevels: %u\nrecords: %u\nnodes: %u\npage-size: %u' \ - % (self._rootnode, self._levels, self._records, - self._nodes, self._page_size)) + print( + "root: %u\nlevels: %u\nrecords: %u\nnodes: %u\npage-size: %u" + % ( + self._rootnode, + self._levels, + self._records, + self._nodes, + self._page_size, + ) + ) # Splits entries across two blocks, returning one pivot # @@ -480,9 +486,9 @@ class DSStore(object): def _split2(self, blocks, entries, pointers, before, internal): left_block = blocks[0] right_block = blocks[1] - + count = len(entries) - + # Find the feasible splits best_split = None best_diff = None @@ -492,7 +498,7 @@ class DSStore(object): # We can use a *single* node for this best_split = count else: - # Split into two nodes + # Split into two nodes for n in range(1, count - 1): left_size = 8 + before[n] right_size = 8 + total - before[n + 1] @@ -501,7 +507,7 @@ class DSStore(object): break if right_size > self._page_size: continue - + diff = abs(left_size - right_size) if best_split is None or diff < best_diff: @@ -510,54 +516,53 @@ class DSStore(object): if best_split is None: return None - + # Write the nodes left_block.seek(0) if internal: next_node = pointers[best_split] else: next_node = 0 - left_block.write(b'>II', next_node, best_split) + left_block.write(b">II", next_node, best_split) for n in range(best_split): if internal: - left_block.write(b'>I', pointers[n]) + left_block.write(b">I", pointers[n]) entries[n].write(left_block) left_block.zero_fill() if best_split == count: return [] - + right_block.seek(0) if internal: next_node = pointers[count] else: next_node = 0 - right_block.write(b'>II', next_node, count - best_split - 1) + right_block.write(b">II", next_node, count - best_split - 1) for n in range(best_split + 1, count): if internal: - right_block.write(b'>I', pointers[n]) + right_block.write(b">I", pointers[n]) entries[n].write(right_block) right_block.zero_fill() - + pivot = entries[best_split] return [pivot] - + def _split(self, node, entry, right_ptr=0): self._nodes += 1 self._dirty = True new_right = self._store.allocate(self._page_size) - with self._get_block(node) as block, \ - self._get_block(new_right) as right_block: + with self._get_block(node) as block, self._get_block(new_right) as right_block: # First, measure and extract all the elements entry_size = entry.byte_length() - entry_pos = None - next_node, count = block.read(b'>II') + # ?? entry_pos = None + next_node, count = block.read(b">II") if next_node: entry_size += 4 pointers = [] @@ -567,11 +572,11 @@ class DSStore(object): for n in range(count): pos = block.tell() if next_node: - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] pointers.append(ptr) e = DSStoreEntry.read(block) if e > entry: - entry_pos = n + # ?? entry_pos = n entries.append(entry) pointers.append(right_ptr) before.append(total) @@ -583,10 +588,10 @@ class DSStore(object): if next_node: pointers.append(next_node) - pivot = self._split2([block, right_block], - entries, pointers, before, - bool(next_node))[0] - + pivot = self._split2( + [block, right_block], entries, pointers, before, bool(next_node) + )[0] + self._records += 1 self._nodes += 1 self._dirty = True @@ -598,7 +603,7 @@ class DSStore(object): def _new_root(self, left, pivot, right): new_root = self._store.allocate(self._page_size) with self._get_block(new_root) as block: - block.write(b'>III', right, 1, left) + block.write(b">III", right, 1, left) pivot.write(block) self._rootnode = new_root self._levels += 1 @@ -610,21 +615,21 @@ class DSStore(object): # pointer (inserted to the RIGHT of `entry') def _insert_inner(self, path, node, entry, right_ptr): with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") insert_pos = None insert_ndx = None n = 0 while n < count: pos = block.tell() - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] e = DSStoreEntry.read(block) if e == entry: if n == count - 1: right_ptr = next_node next_node = ptr - block_seek(pos) + block.seek(pos) else: - right_ptr = block.read(b'>I')[0] + right_ptr = block.read(b">I")[0] block.seek(pos + 4) insert_pos = pos insert_ndx = n @@ -651,32 +656,32 @@ class DSStore(object): else: if insert_ndx == count: block.seek(insert_pos) - block.write(b'>I', next_node) + block.write(b">I", next_node) entry.write(block) next_node = right_ptr else: block.seek(insert_pos + 4) entry.write(block, True) - block.insert('>I', right_ptr) + block.insert(">I", right_ptr) block.seek(0) count += 1 - block.write(b'>II', next_node, count) + block.write(b">II", next_node, count) self._records += 1 self._dirty = True # Insert `entry' into the leaf node `node' def _insert_leaf(self, path, node, entry): with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") insert_pos = None - insert_ndx = None + # ?? insert_ndx = None n = 0 while n < count: pos = block.tell() e = DSStoreEntry.read(block) if e == entry: insert_pos = pos - insert_ndx = n + # ?? insert_ndx = n block.seek(pos) block.delete(e.byte_length()) count -= 1 @@ -685,11 +690,11 @@ class DSStore(object): continue elif insert_pos is None and e > entry: insert_pos = pos - insert_ndx = n + # ?? insert_ndx = n n += 1 if insert_pos is None: insert_pos = block.tell() - insert_ndx = count + # ?? insert_ndx = count remaining = self._page_size - block.tell() if remaining < entry.byte_length(): @@ -703,21 +708,21 @@ class DSStore(object): entry.write(block, True) block.seek(0) count += 1 - block.write(b'>II', next_node, count) + block.write(b">II", next_node, count) self._records += 1 self._dirty = True def insert(self, entry): - """Insert ``entry`` (which should be a :class:`DSStoreEntry`) - into the B-Tree.""" + """Insert ``entry`` (which should be a :class:`DSStoreEntry`) into the + B-Tree.""" path = [] node = self._rootnode while True: with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") if next_node: for n in range(count): - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] e = DSStoreEntry.read(block) if entry < e: next_node = ptr @@ -735,12 +740,12 @@ class DSStore(object): # Return usage information for the specified `node' def _block_usage(self, node): with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") for n in range(count): if next_node: - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) + block.read(b">I")[0] + DSStoreEntry.read(block) used = block.tell() @@ -773,14 +778,14 @@ class DSStore(object): continue diff = abs(left_size - mid_size) * abs(right_size - mid_size) - + if best_split is None or diff < best_diff: best_split = (n, m, count) best_diff = diff if best_split is None: return None - + # Write the nodes prev_split = -1 for block, split in zip(blocks, best_split): @@ -789,15 +794,15 @@ class DSStore(object): next_node = pointers[split] else: next_node = 0 - block.write(b'>II', next_node, split) + block.write(b">II", next_node, split) for n in range(prev_split + 1, split): if internal: - block.write(b'>I', pointers[n]) + block.write(b">I", pointers[n]) entries[n].write(block) block.zero_fill() - + prev_split = split return (entries[best_split[0]], entries[best_split[1]]) @@ -811,13 +816,13 @@ class DSStore(object): before = [] total = 0 ppivots = pivots + [None] - for b,p in zip(blocks, ppivots): + for b, p in zip(blocks, ppivots): b.seek(0) - next_node, count = b.read(b'>II') + next_node, count = b.read(b">II") for n in range(count): pos = b.tell() if next_node: - ptr = b.read(b'>I')[0] + ptr = b.read(b">I")[0] pointers.append(ptr) e = DSStoreEntry.read(b) entries.append(e) @@ -842,11 +847,11 @@ class DSStore(object): return with self._get_block(node) as block: - next_node, count = block.read(b'>II') - + next_node, count = block.read(b">II") + with self._get_block(path[-1]) as parent: # Find the left and right siblings and respective pivots - parent_next, parent_count = parent.read(b'>II') + parent_next, parent_count = parent.read(b">II") left_pos = None left_node = None left_pivot = None @@ -857,7 +862,7 @@ class DSStore(object): prev_e = prev_ptr = prev_pos = None for n in range(parent_count): pos = parent.tell() - ptr = parent.read(b'>I')[0] + ptr = parent.read(b">I")[0] e = DSStoreEntry.read(parent) if ptr == node: @@ -870,7 +875,7 @@ class DSStore(object): right_node = ptr right_pos = pos break - + prev_e = e prev_ptr = ptr prev_pos = pos @@ -884,24 +889,27 @@ class DSStore(object): right_node = parent_next right_pos = parent.tell() - parent_used = parent.tell() - + _ = parent.tell() + if left_node and right_node: - with self._get_block(left_node) as left, \ - self._get_block(right_node) as right: + with self._get_block(left_node) as left, self._get_block( + right_node + ) as right: blocks = [left, block, right] pivots = [left_pivot, right_pivot] - + entries, pointers, before = self._extract(blocks, pivots) # If there's a chance that we could use two pages instead # of three, go for it - pivots = self._split2(blocks, entries, pointers, - before, bool(next_node)) + pivots = self._split2( + blocks, entries, pointers, before, bool(next_node) + ) if pivots is None: ptrs = [left_node, node, right_node] - pivots = self._split3(blocks, entries, pointers, - before, bool(next_node)) + pivots = self._split3( + blocks, entries, pointers, before, bool(next_node) + ) else: if pivots: ptrs = [left_node, node] @@ -913,7 +921,7 @@ class DSStore(object): self._store.release(right_node) self._nodes -= 1 self._dirty = True - + # Remove the pivots from the parent with self._get_block(path[-1]) as parent: if right_node == parent_next: @@ -925,11 +933,11 @@ class DSStore(object): parent.delete(right_pos - left_pos) parent.seek(0) parent_count -= 2 - parent.write(b'>II', parent_next, parent_count) + parent.write(b">II", parent_next, parent_count) self._records -= 2 - + # Replace with those in pivots - for e,rp in zip(pivots, ptrs[1:]): + for e, rp in zip(pivots, ptrs[1:]): self._insert_inner(path[:-1], path[-1], e, rp) elif left_node: with self._get_block(left_node) as left: @@ -938,8 +946,9 @@ class DSStore(object): entries, pointers, before = self._extract(blocks, pivots) - pivots = self._split2(blocks, entries, pointers, - before, bool(next_node)) + pivots = self._split2( + blocks, entries, pointers, before, bool(next_node) + ) # Remove the pivot from the parent with self._get_block(path[-1]) as parent: @@ -952,7 +961,7 @@ class DSStore(object): parent.delete(node_pos - left_pos) parent.seek(0) parent_count -= 1 - parent.write(b'>II', parent_next, parent_count) + parent.write(b">II", parent_next, parent_count) self._records -= 1 # Replace the pivot @@ -965,8 +974,9 @@ class DSStore(object): entries, pointers, before = self._extract(blocks, pivots) - pivots = self._split2(blocks, entries, pointers, - before, bool(next_node)) + pivots = self._split2( + blocks, entries, pointers, before, bool(next_node) + ) # Remove the pivot from the parent with self._get_block(path[-1]) as parent: @@ -979,13 +989,12 @@ class DSStore(object): parent.delete(right_pos - node_pos) parent.seek(0) parent_count -= 1 - parent.write(b'>II', parent_next, parent_count) + parent.write(b">II", parent_next, parent_count) self._records -= 1 # Replace the pivot if pivots: - self._insert_inner(path[:-1], path[-1], pivots[0], - right_node) + self._insert_inner(path[:-1], path[-1], pivots[0], right_node) if not path and not parent_count: self._store.release(path[-1]) @@ -994,7 +1003,7 @@ class DSStore(object): self._rootnode = node else: count, used = self._block_usage(path[-1]) - + if used < self._page_size // 2: self._rebalance(path[:-1], path[-1]) @@ -1002,31 +1011,32 @@ class DSStore(object): # lower-cased. def _delete_leaf(self, node, filename_lc, code): found = False - + with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") for n in range(count): pos = block.tell() e = DSStoreEntry.read(block) - if e.filename.lower() == filename_lc \ - and (code is None or e.code == code): + if e.filename.lower() == filename_lc and ( + code is None or e.code == code + ): block.seek(pos) block.delete(e.byte_length()) found = True - + # This does not affect the loop; THIS IS NOT A BUG count -= 1 self._records -= 1 self._dirty = True - + if found: used = block.tell() - + block.seek(0) - block.write(b'>II', next_node, count) - + block.write(b">II", next_node, count) + return used < self._page_size // 2 else: return False @@ -1040,7 +1050,7 @@ class DSStore(object): rebalance = None while True: with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") if next_node: path.append(node) @@ -1053,7 +1063,7 @@ class DSStore(object): count -= 1 block.seek(0) - block.write(b'>II', next_node, count) + block.write(b">II", next_node, count) if pos < self._page_size // 2: rebalance = (path, node) @@ -1064,16 +1074,17 @@ class DSStore(object): # Delete an entry from an inner node, `node' def _delete_inner(self, path, node, filename_lc, code): rebalance = False - + with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") for n in range(count): pos = block.tell() - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] e = DSStoreEntry.read(block) - if e.filename.lower() == filename_lc \ - and (code is None or e.code == code): + if e.filename.lower() == filename_lc and ( + code is None or e.code == code + ): # Take the largest from the left subtree rebalance, largest = self._take_largest(path, ptr) @@ -1083,20 +1094,20 @@ class DSStore(object): next_node = ptr block.seek(pos) else: - right_ptr = block.read(b'>I')[0] + right_ptr = block.read(b">I")[0] block.seek(pos + 4) - + block.delete(e.byte_length() + 4) count -= 1 block.seek(0) - block.write(b'>II', next_node, count) + block.write(b">II", next_node, count) self._records -= 1 self._dirty = True - + break - + # Replace the pivot value self._insert_inner(path, node, largest, right_ptr) @@ -1107,16 +1118,16 @@ class DSStore(object): return False def delete(self, filename, code): - """Delete an item, identified by ``filename`` and ``code`` - from the B-Tree.""" + """Delete an item, identified by ``filename`` and ``code`` from the + B-Tree.""" if isinstance(filename, DSStoreEntry): code = filename.code filename = filename.filename # If we're deleting *every* node for "filename", we must recurse if code is None: - ###TODO: Fix this so we can do bulk deletes - raise ValueError('You must delete items individually. Sorry') + # TODO: Fix this so we can do bulk deletes + raise ValueError("You must delete items individually. Sorry") # Otherwise, we're deleting *one* specific node filename_lc = filename.lower() @@ -1124,14 +1135,15 @@ class DSStore(object): node = self._rootnode while True: with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") if next_node: for n in range(count): - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] e = DSStoreEntry.read(block) e_lc = e.filename.lower() - if filename_lc < e_lc \ - or (filename_lc == e_lc and code < e.code): + if filename_lc < e_lc or ( + filename_lc == e_lc and code < e.code + ): next_node = ptr break elif filename_lc == e_lc and code == e.code: @@ -1146,13 +1158,13 @@ class DSStore(object): # Find implementation def _find(self, node, filename_lc, code=None): - if not isinstance(code, bytes): - code = code.encode('latin_1') + if code is not None and not isinstance(code, bytes): + code = code.encode("latin_1") with self._get_block(node) as block: - next_node, count = block.read(b'>II') + next_node, count = block.read(b">II") if next_node: for n in range(count): - ptr = block.read(b'>I')[0] + ptr = block.read(b">I")[0] e = DSStoreEntry.read(block) if filename_lc < e.filename.lower(): for e in self._find(ptr, filename_lc, code): @@ -1176,16 +1188,16 @@ class DSStore(object): yield e elif code < e.code: return - + def find(self, filename, code=None): - """Returns a generator that will iterate over matching entries in - the B-Tree.""" + """Returns a generator that will iterate over matching entries in the + B-Tree.""" if isinstance(filename, DSStoreEntry): code = filename.code filename = filename.filename filename_lc = filename.lower() - + return self._find(self._rootnode, filename_lc, code) def __len__(self): @@ -1194,36 +1206,36 @@ class DSStore(object): def __iter__(self): return self._traverse(self._rootnode) - class Partial(object): + class Partial: """This is used to implement indexing.""" + def __init__(self, store, filename): self._store = store self._filename = filename def __getitem__(self, code): if code is None: - raise KeyError('no such key - [%s][None]' % self._filename) + raise KeyError("no such key - [%s][None]" % self._filename) if not isinstance(code, bytes): - code = code.encode('latin_1') + code = code.encode("latin_1") try: item = next(self._store.find(self._filename, code)) except StopIteration: - raise KeyError('no such key - [%s][%s]' % (self._filename, - code)) + raise KeyError(f"no such key - [{self._filename}][{code}]") - if not isinstance(item.type, (bytes, str, unicode)): + if not isinstance(item.type, (bytes, str)): return item.value - + return (item.type, item.value) - + def __setitem__(self, code, value): if code is None: - raise KeyError('bad key - [%s][None]' % self._filename) + raise KeyError("bad key - [%s][None]" % self._filename) if not isinstance(code, bytes): - code = code.encode('latin_1') + code = code.encode("latin_1") codec = codecs.get(code, None) if codec: @@ -1232,20 +1244,19 @@ class DSStore(object): else: entry_type = value[0] entry_value = value[1] - - self._store.insert(DSStoreEntry(self._filename, code, - entry_type, entry_value)) + + self._store.insert( + DSStoreEntry(self._filename, code, entry_type, entry_value) + ) def __delitem__(self, code): if code is None: - raise KeyError('no such key - [%s][None]' % self._filename) + raise KeyError("no such key - [%s][None]" % self._filename) self._store.delete(self._filename, code) def __iter__(self): - for item in self._store.find(self._filename): - yield item + yield from self._store.find(self._filename) def __getitem__(self, filename): return self.Partial(self, filename) - diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/LICENSE b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/LICENSE index e91f4eb38..e91f4eb38 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/LICENSE +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/LICENSE diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py new file mode 100644 index 000000000..0f925e7d1 --- /dev/null +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py @@ -0,0 +1,47 @@ +# flake8: noqa +from .alias import * +from .bookmark import * + +__version__ = "2.2.2" + +__all__ = [ + "__version__", + "ALIAS_KIND_FILE", + "ALIAS_KIND_FOLDER", + "ALIAS_HFS_VOLUME_SIGNATURE", + "ALIAS_FIXED_DISK", + "ALIAS_NETWORK_DISK", + "ALIAS_400KB_FLOPPY_DISK", + "ALIAS_800KB_FLOPPY_DISK", + "ALIAS_1_44MB_FLOPPY_DISK", + "ALIAS_EJECTABLE_DISK", + "ALIAS_NO_CNID", + "kBookmarkPath", + "kBookmarkCNIDPath", + "kBookmarkFileProperties", + "kBookmarkFileName", + "kBookmarkFileID", + "kBookmarkFileCreationDate", + "kBookmarkTOCPath", + "kBookmarkVolumePath", + "kBookmarkVolumeURL", + "kBookmarkVolumeName", + "kBookmarkVolumeUUID", + "kBookmarkVolumeSize", + "kBookmarkVolumeCreationDate", + "kBookmarkVolumeProperties", + "kBookmarkContainingFolder", + "kBookmarkUserName", + "kBookmarkUID", + "kBookmarkWasFileReference", + "kBookmarkCreationOptions", + "kBookmarkURLLengths", + "kBookmarkSecurityExtension", + "AppleShareInfo", + "VolumeInfo", + "TargetInfo", + "Alias", + "Bookmark", + "Data", + "URL", +] diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/alias.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py index f74b949ea..bf34fb01b 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/alias.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py @@ -1,30 +1,29 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from __future__ import division - -import struct import datetime import io -import re import os import os.path -import stat +import struct import sys +from unicodedata import normalize -if sys.platform == 'darwin': +if sys.platform == "darwin": from . import osx -try: - long -except NameError: - long = int +from .utils import mac_epoch -from .utils import * - ALIAS_KIND_FILE = 0 ALIAS_KIND_FOLDER = 1 -ALIAS_HFS_VOLUME_SIGNATURE = b'H+' +ALIAS_HFS_VOLUME_SIGNATURE = b"H+" + +ALIAS_FILESYSTEM_UDF = "UDF (CD/DVD)" +ALIAS_FILESYSTEM_FAT32 = "FAT32" +ALIAS_FILESYSTEM_EXFAT = "exFAT" +ALIAS_FILESYSTEM_HFSX = "HFSX" +ALIAS_FILESYSTEM_HFSPLUS = "HFS+" +ALIAS_FILESYSTEM_FTP = "FTP" +ALIAS_FILESYSTEM_NTFS = "NTFS" +ALIAS_FILESYSTEM_UNKNOWN = "unknown" ALIAS_FIXED_DISK = 0 ALIAS_NETWORK_DISK = 1 @@ -33,19 +32,36 @@ ALIAS_800KB_FLOPPY_DISK = 3 ALIAS_1_44MB_FLOPPY_DISK = 4 ALIAS_EJECTABLE_DISK = 5 -ALIAS_NO_CNID = 0xffffffff +ALIAS_NO_CNID = 0xFFFFFFFF + +ALIAS_FSTYPE_MAP = { + # Version 2 aliases + b"HX": ALIAS_FILESYSTEM_HFSX, + b"H+": ALIAS_FILESYSTEM_HFSPLUS, + # Version 3 aliases + b"BDcu": ALIAS_FILESYSTEM_UDF, + b"BDIS": ALIAS_FILESYSTEM_FAT32, + b"BDxF": ALIAS_FILESYSTEM_EXFAT, + b"HX\0\0": ALIAS_FILESYSTEM_HFSX, + b"H+\0\0": ALIAS_FILESYSTEM_HFSPLUS, + b"KG\0\0": ALIAS_FILESYSTEM_FTP, + b"NTcu": ALIAS_FILESYSTEM_NTFS, +} + def encode_utf8(s): if isinstance(s, bytes): return s - return s.encode('utf-8') + return s.encode("utf-8") + def decode_utf8(s): if isinstance(s, bytes): - return s.decode('utf-8') + return s.decode("utf-8") return s -class AppleShareInfo (object): + +class AppleShareInfo: def __init__(self, zone=None, server=None, user=None): #: The AppleShare zone self.zone = zone @@ -55,22 +71,38 @@ class AppleShareInfo (object): self.user = user def __repr__(self): - return 'AppleShareInfo(%r,%r,%r)' % (self.zone, self.server, self.user) - -class VolumeInfo (object): - def __init__(self, name, creation_date, fs_type, disk_type, - attribute_flags, fs_id, appleshare_info=None, - driver_name=None, posix_path=None, disk_image_alias=None, - dialup_info=None, network_mount_info=None): + return "AppleShareInfo({!r},{!r},{!r})".format( + self.zone, self.server, self.user + ) + + +class VolumeInfo: + def __init__( + self, + name, + creation_date, + fs_type, + disk_type, + attribute_flags, + fs_id, + appleshare_info=None, + driver_name=None, + posix_path=None, + disk_image_alias=None, + dialup_info=None, + network_mount_info=None, + ): #: The name of the volume on which the target resides self.name = name - + #: The creation date of the target's volume self.creation_date = creation_date - - #: The filesystem type (a two character code, e.g. ``b'H+'`` for HFS+) + + #: The filesystem type + #: (for v2 aliases, this is a 2-character code; for v3 aliases, a + #: 4-character code). self.fs_type = fs_type - + #: The type of disk; should be one of #: #: * ALIAS_FIXED_DISK @@ -109,30 +141,60 @@ class VolumeInfo (object): #: Network mount information (for automatic remounting) self.network_mount_info = network_mount_info + @property + def filesystem_type(self): + return ALIAS_FSTYPE_MAP.get(self.fs_type, ALIAS_FILESYSTEM_UNKNOWN) + def __repr__(self): - args = ['name', 'creation_date', 'fs_type', 'disk_type', - 'attribute_flags', 'fs_id'] + args = [ + "name", + "creation_date", + "fs_type", + "disk_type", + "attribute_flags", + "fs_id", + ] values = [] for a in args: v = getattr(self, a) values.append(repr(v)) - - kwargs = ['appleshare_info', 'driver_name', 'posix_path', - 'disk_image_alias', 'dialup_info', 'network_mount_info'] + + kwargs = [ + "appleshare_info", + "driver_name", + "posix_path", + "disk_image_alias", + "dialup_info", + "network_mount_info", + ] for a in kwargs: v = getattr(self, a) if v is not None: - values.append('%s=%r' % (a, v)) - return 'VolumeInfo(%s)' % ','.join(values) - -class TargetInfo (object): - def __init__(self, kind, filename, folder_cnid, cnid, creation_date, - creator_code, type_code, levels_from=-1, levels_to=-1, - folder_name=None, cnid_path=None, carbon_path=None, - posix_path=None, user_home_prefix_len=None): + values.append(f"{a}={v!r}") + return "VolumeInfo(%s)" % ",".join(values) + + +class TargetInfo: + def __init__( + self, + kind, + filename, + folder_cnid, + cnid, + creation_date, + creator_code, + type_code, + levels_from=-1, + levels_to=-1, + folder_name=None, + cnid_path=None, + carbon_path=None, + posix_path=None, + user_home_prefix_len=None, + ): #: Either ALIAS_KIND_FILE or ALIAS_KIND_FOLDER self.kind = kind - + #: The filename of the target self.filename = filename @@ -178,26 +240,39 @@ class TargetInfo (object): self.user_home_prefix_len = user_home_prefix_len def __repr__(self): - args = ['kind', 'filename', 'folder_cnid', 'cnid', 'creation_date', - 'creator_code', 'type_code'] + args = [ + "kind", + "filename", + "folder_cnid", + "cnid", + "creation_date", + "creator_code", + "type_code", + ] values = [] for a in args: v = getattr(self, a) values.append(repr(v)) if self.levels_from != -1: - values.append('levels_from=%r' % self.levels_from) + values.append("levels_from=%r" % self.levels_from) if self.levels_to != -1: - values.append('levels_to=%r' % self.levels_to) - - kwargs = ['folder_name', 'cnid_path', 'carbon_path', - 'posix_path', 'user_home_prefix_len'] + values.append("levels_to=%r" % self.levels_to) + + kwargs = [ + "folder_name", + "cnid_path", + "carbon_path", + "posix_path", + "user_home_prefix_len", + ] for a in kwargs: v = getattr(self, a) - values.append('%s=%r' % (a, v)) + values.append(f"{a}={v!r}") + + return "TargetInfo(%s)" % ",".join(values) + - return 'TargetInfo(%s)' % ','.join(values) - TAG_CARBON_FOLDER_NAME = 0 TAG_CNID_PATH = 1 TAG_CARBON_PATH = 2 @@ -216,21 +291,28 @@ TAG_POSIX_PATH_TO_MOUNTPOINT = 19 TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE = 20 TAG_USER_HOME_LENGTH_PREFIX = 21 -class Alias (object): - def __init__(self, appinfo=b'\0\0\0\0', version=2, volume=None, - target=None, extra=[]): + +class Alias: + def __init__( + self, + appinfo=b"\0\0\0\0", + version=2, + volume=None, + target=None, + extra=[], + ): """Construct a new :class:`Alias` object with the specified contents.""" #: Application specific information (four byte byte-string) self.appinfo = appinfo - #: Version (we support only version 2) + #: Version (we support versions 2 and 3) self.version = version #: A :class:`VolumeInfo` object describing the target's volume self.volume = volume - + #: A :class:`TargetInfo` object describing the target self.target = target @@ -239,47 +321,91 @@ class Alias (object): @classmethod def _from_fd(cls, b): - appinfo, recsize, version = struct.unpack(b'>4shh', b.read(8)) + appinfo, recsize, version = struct.unpack(b">4shh", b.read(8)) if recsize < 150: - raise ValueError('Incorrect alias length') - - if version != 2: - raise ValueError('Unsupported alias version %u' % version) - - kind, volname, voldate, fstype, disktype, \ - folder_cnid, filename, cnid, crdate, creator_code, type_code, \ - levels_from, levels_to, volattrs, volfsid, reserved = \ - struct.unpack(b'>h28pI2shI64pII4s4shhI2s10s', b.read(142)) + raise ValueError("Incorrect alias length") + + if version not in (2, 3): + raise ValueError("Unsupported alias version %u" % version) + + if version == 2: + ( + kind, # h + volname, # 28p + voldate, # I + fstype, # 2s + disktype, # h + folder_cnid, # I + filename, # 64p + cnid, # I + crdate, # I + creator_code, # 4s + type_code, # 4s + levels_from, # h + levels_to, # h + volattrs, # I + volfsid, # 2s + reserved, # 10s + ) = struct.unpack(b">h28pI2shI64pII4s4shhI2s10s", b.read(142)) + else: + ( + kind, # h + voldate_hr, # Q + fstype, # 4s + disktype, # h + folder_cnid, # I + cnid, # I + crdate_hr, # Q + volattrs, # I + reserved, # 14s + ) = struct.unpack(b">hQ4shIIQI14s", b.read(46)) + + volname = b"" + filename = b"" + creator_code = None + type_code = None + voldate = voldate_hr / 65536.0 + crdate = crdate_hr / 65536.0 voldate = mac_epoch + datetime.timedelta(seconds=voldate) crdate = mac_epoch + datetime.timedelta(seconds=crdate) alias = Alias() alias.appinfo = appinfo - - alias.volume = VolumeInfo (volname.decode().replace('/',':'), - voldate, fstype, disktype, - volattrs, volfsid) - alias.target = TargetInfo (kind, filename.decode().replace('/',':'), - folder_cnid, cnid, - crdate, creator_code, type_code) + + alias.volume = VolumeInfo( + volname.decode().replace("/", ":"), + voldate, + fstype, + disktype, + volattrs, + volfsid, + ) + alias.target = TargetInfo( + kind, + filename.decode().replace("/", ":"), + folder_cnid, + cnid, + crdate, + creator_code, + type_code, + ) alias.target.levels_from = levels_from alias.target.levels_to = levels_to - - tag = struct.unpack(b'>h', b.read(2))[0] + + tag = struct.unpack(b">h", b.read(2))[0] while tag != -1: - length = struct.unpack(b'>h', b.read(2))[0] + length = struct.unpack(b">h", b.read(2))[0] value = b.read(length) if length & 1: b.read(1) if tag == TAG_CARBON_FOLDER_NAME: - alias.target.folder_name = value.decode().replace('/',':') + alias.target.folder_name = value.decode().replace("/", ":") elif tag == TAG_CNID_PATH: - alias.target.cnid_path = struct.unpack('>%uI' % (length // 4), - value) + alias.target.cnid_path = struct.unpack(">%uI" % (length // 4), value) elif tag == TAG_CARBON_PATH: alias.target.carbon_path = value elif tag == TAG_APPLESHARE_ZONE: @@ -301,17 +427,19 @@ class Alias (object): elif tag == TAG_DIALUP_INFO: alias.volume.dialup_info = value elif tag == TAG_UNICODE_FILENAME: - alias.target.filename = value[2:].decode('utf-16be') + alias.target.filename = value[2:].decode("utf-16be") elif tag == TAG_UNICODE_VOLUME_NAME: - alias.volume.name = value[2:].decode('utf-16be') + alias.volume.name = value[2:].decode("utf-16be") elif tag == TAG_HIGH_RES_VOLUME_CREATION_DATE: - seconds = struct.unpack(b'>Q', value)[0] / 65536.0 - alias.volume.creation_date \ - = mac_epoch + datetime.timedelta(seconds=seconds) + seconds = struct.unpack(b">Q", value)[0] / 65536.0 + alias.volume.creation_date = mac_epoch + datetime.timedelta( + seconds=seconds + ) elif tag == TAG_HIGH_RES_CREATION_DATE: - seconds = struct.unpack(b'>Q', value)[0] / 65536.0 - alias.target.creation_date \ - = mac_epoch + datetime.timedelta(seconds=seconds) + seconds = struct.unpack(b">Q", value)[0] / 65536.0 + alias.target.creation_date = mac_epoch + datetime.timedelta( + seconds=seconds + ) elif tag == TAG_POSIX_PATH: alias.target.posix_path = value.decode() elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT: @@ -319,14 +447,14 @@ class Alias (object): elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: alias.volume.disk_image_alias = Alias.from_bytes(value) elif tag == TAG_USER_HOME_LENGTH_PREFIX: - alias.target.user_home_prefix_len = struct.unpack(b'>h', value)[0] + alias.target.user_home_prefix_len = struct.unpack(b">h", value)[0] else: alias.extra.append((tag, value)) - tag = struct.unpack(b'>h', b.read(2))[0] - + tag = struct.unpack(b">h", b.read(2))[0] + return alias - + @classmethod def from_bytes(cls, bytes): """Construct an :class:`Alias` object given binary Alias data.""" @@ -336,32 +464,40 @@ class Alias (object): @classmethod def for_file(cls, path): """Create an :class:`Alias` that points at the specified file.""" - if sys.platform != 'darwin': - raise Exception('Not implemented (requires special support)') + if sys.platform != "darwin": + raise Exception("Not implemented (requires special support)") path = encode_utf8(path) - + a = Alias() # Find the filesystem st = osx.statfs(path) vol_path = st.f_mntonname - + + # File and folder names in HFS+ are normalized to a form similar to NFD. + # Must be normalized (NFD->NFC) before use to avoid unicode string comparison issues. + vol_path = normalize("NFC", vol_path.decode("utf-8")).encode("utf-8") + # Grab its attributes - attrs = [osx.ATTR_CMN_CRTIME, - osx.ATTR_VOL_NAME, - 0, 0, 0] + attrs = [osx.ATTR_CMN_CRTIME, osx.ATTR_VOL_NAME, 0, 0, 0] volinfo = osx.getattrlist(vol_path, attrs, 0) vol_crtime = volinfo[0] vol_name = encode_utf8(volinfo[1]) - + # Also grab various attributes of the file - attrs = [(osx.ATTR_CMN_OBJTYPE - | osx.ATTR_CMN_CRTIME - | osx.ATTR_CMN_FNDRINFO - | osx.ATTR_CMN_FILEID - | osx.ATTR_CMN_PARENTID), 0, 0, 0, 0] + attrs = [ + osx.ATTR_CMN_OBJTYPE + | osx.ATTR_CMN_CRTIME + | osx.ATTR_CMN_FNDRINFO + | osx.ATTR_CMN_FILEID + | osx.ATTR_CMN_PARENTID, + 0, + 0, + 0, + 0, + ] info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW) if info[0] == osx.VDIR: @@ -371,27 +507,27 @@ class Alias (object): cnid = info[3] folder_cnid = info[4] - + dirname, filename = os.path.split(path) - if dirname == b'' or dirname == b'.': + if dirname == b"" or dirname == b".": dirname = os.getcwd() foldername = os.path.basename(dirname) - + creation_date = info[1] if kind == ALIAS_KIND_FILE: - creator_code = struct.pack(b'I', info[2].fileInfo.fileCreator) - type_code = struct.pack(b'I', info[2].fileInfo.fileType) + creator_code = struct.pack(b"I", info[2].fileInfo.fileCreator) + type_code = struct.pack(b"I", info[2].fileInfo.fileType) else: - creator_code = b'\0\0\0\0' - type_code = b'\0\0\0\0' + creator_code = b"\0\0\0\0" + type_code = b"\0\0\0\0" - a.target = TargetInfo(kind, filename, folder_cnid, cnid, creation_date, - creator_code, type_code) - a.volume = VolumeInfo(vol_name, vol_crtime, b'H+', - ALIAS_FIXED_DISK, 0, b'\0\0') + a.target = TargetInfo( + kind, filename, folder_cnid, cnid, creation_date, creator_code, type_code + ) + a.volume = VolumeInfo(vol_name, vol_crtime, b"H+", ALIAS_FIXED_DISK, 0, b"\0\0") a.target.folder_name = foldername a.volume.posix_path = vol_path @@ -399,10 +535,10 @@ class Alias (object): rel_path = os.path.relpath(path, vol_path) # Leave off the initial '/' if vol_path is '/' (no idea why) - if vol_path == b'/': + if vol_path == b"/": a.target.posix_path = rel_path else: - a.target.posix_path = b'/' + rel_path + a.target.posix_path = b"/" + rel_path # Construct the Carbon and CNID paths carbon_path = [] @@ -415,173 +551,202 @@ class Alias (object): attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0] info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0) cnid_path.append(info[0]) - carbon_tail = tail.replace(b':',b'/') + carbon_tail = tail.replace(b":", b"/") carbon_path.insert(0, carbon_tail) head, tail = os.path.split(head) - carbon_path = vol_name + b':' + b':\0'.join(carbon_path) + carbon_path = vol_name + b":" + b":\0".join(carbon_path) a.target.carbon_path = carbon_path a.target.cnid_path = cnid_path return a - + def _to_fd(self, b): # We'll come back and fix the length when we're done pos = b.tell() - b.write(struct.pack(b'>4shh', self.appinfo, 0, self.version)) + b.write(struct.pack(b">4shh", self.appinfo, 0, self.version)) - carbon_volname = encode_utf8(self.volume.name).replace(b':',b'/') - carbon_filename = encode_utf8(self.target.filename).replace(b':',b'/') + carbon_volname = encode_utf8(self.volume.name).replace(b":", b"/") + carbon_filename = encode_utf8(self.target.filename).replace(b":", b"/") voldate = (self.volume.creation_date - mac_epoch).total_seconds() crdate = (self.target.creation_date - mac_epoch).total_seconds() - # NOTE: crdate should be in local time, but that's system dependent - # (so doing so is ridiculous, and nothing could rely on it). - b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', - self.target.kind, - carbon_volname, int(voldate), - self.volume.fs_type, - self.volume.disk_type, - self.target.folder_cnid, - carbon_filename, - self.target.cnid, - int(crdate), - self.target.creator_code, - self.target.type_code, - self.target.levels_from, - self.target.levels_to, - self.volume.attribute_flags, - self.volume.fs_id, - b'\0'*10)) + if self.version == 2: + # NOTE: crdate should be in local time, but that's system dependent + # (so doing so is ridiculous, and nothing could rely on it). + b.write( + struct.pack( + b">h28pI2shI64pII4s4shhI2s10s", + self.target.kind, # h + carbon_volname, # 28p + int(voldate), # I + self.volume.fs_type, # 2s + self.volume.disk_type, # h + self.target.folder_cnid, # I + carbon_filename, # 64p + self.target.cnid, # I + int(crdate), # I + self.target.creator_code, # 4s + self.target.type_code, # 4s + self.target.levels_from, # h + self.target.levels_to, # h + self.volume.attribute_flags, # I + self.volume.fs_id, # 2s + b"\0" * 10, # 10s + ) + ) + else: + b.write( + struct.pack( + b">hQ4shIIQI14s", + self.target.kind, # h + int(voldate * 65536), # Q + self.volume.fs_type, # 4s + self.volume.disk_type, # h + self.target.folder_cnid, # I + self.target.cnid, # I + int(crdate * 65536), # Q + self.volume.attribute_flags, # I + b"\0" * 14, # 14s + ) + ) # Excuse the odd order; we're copying Finder if self.target.folder_name: - carbon_foldername = encode_utf8(self.target.folder_name)\ - .replace(b':',b'/') - b.write(struct.pack(b'>hh', TAG_CARBON_FOLDER_NAME, - len(carbon_foldername))) + carbon_foldername = encode_utf8(self.target.folder_name).replace(b":", b"/") + b.write(struct.pack(b">hh", TAG_CARBON_FOLDER_NAME, len(carbon_foldername))) b.write(carbon_foldername) if len(carbon_foldername) & 1: - b.write(b'\0') + b.write(b"\0") - b.write(struct.pack(b'>hhQhhQ', + b.write( + struct.pack( + b">hhQhhQ", TAG_HIGH_RES_VOLUME_CREATION_DATE, - 8, int(voldate * 65536), + 8, + int(voldate * 65536), TAG_HIGH_RES_CREATION_DATE, - 8, int(crdate * 65536))) + 8, + int(crdate * 65536), + ) + ) if self.target.cnid_path: - cnid_path = struct.pack('>%uI' % len(self.target.cnid_path), - *self.target.cnid_path) - b.write(struct.pack(b'>hh', TAG_CNID_PATH, - len(cnid_path))) + cnid_path = struct.pack( + ">%uI" % len(self.target.cnid_path), *self.target.cnid_path + ) + b.write(struct.pack(b">hh", TAG_CNID_PATH, len(cnid_path))) b.write(cnid_path) if self.target.carbon_path: - carbon_path=encode_utf8(self.target.carbon_path) - b.write(struct.pack(b'>hh', TAG_CARBON_PATH, - len(carbon_path))) + carbon_path = encode_utf8(self.target.carbon_path) + b.write(struct.pack(b">hh", TAG_CARBON_PATH, len(carbon_path))) b.write(carbon_path) if len(carbon_path) & 1: - b.write(b'\0') + b.write(b"\0") if self.volume.appleshare_info: ai = self.volume.appleshare_info if ai.zone: - b.write(struct.pack(b'>hh', TAG_APPLESHARE_ZONE, - len(ai.zone))) + b.write(struct.pack(b">hh", TAG_APPLESHARE_ZONE, len(ai.zone))) b.write(ai.zone) if len(ai.zone) & 1: - b.write(b'\0') + b.write(b"\0") if ai.server: - b.write(struct.pack(b'>hh', TAG_APPLESHARE_SERVER_NAME, - len(ai.server))) + b.write(struct.pack(b">hh", TAG_APPLESHARE_SERVER_NAME, len(ai.server))) b.write(ai.server) if len(ai.server) & 1: - b.write(b'\0') + b.write(b"\0") if ai.username: - b.write(struct.pack(b'>hh', TAG_APPLESHARE_USERNAME, - len(ai.username))) + b.write(struct.pack(b">hh", TAG_APPLESHARE_USERNAME, len(ai.username))) b.write(ai.username) if len(ai.username) & 1: - b.write(b'\0') + b.write(b"\0") if self.volume.driver_name: driver_name = encode_utf8(self.volume.driver_name) - b.write(struct.pack(b'>hh', TAG_DRIVER_NAME, - len(driver_name))) + b.write(struct.pack(b">hh", TAG_DRIVER_NAME, len(driver_name))) b.write(driver_name) if len(driver_name) & 1: - b.write(b'\0') + b.write(b"\0") if self.volume.network_mount_info: - b.write(struct.pack(b'>hh', TAG_NETWORK_MOUNT_INFO, - len(self.volume.network_mount_info))) + b.write( + struct.pack( + b">hh", TAG_NETWORK_MOUNT_INFO, len(self.volume.network_mount_info) + ) + ) b.write(self.volume.network_mount_info) if len(self.volume.network_mount_info) & 1: - b.write(b'\0') + b.write(b"\0") if self.volume.dialup_info: - b.write(struct.pack(b'>hh', TAG_DIALUP_INFO, - len(self.volume.network_mount_info))) + b.write( + struct.pack( + b">hh", TAG_DIALUP_INFO, len(self.volume.network_mount_info) + ) + ) b.write(self.volume.network_mount_info) if len(self.volume.network_mount_info) & 1: - b.write(b'\0') + b.write(b"\0") - utf16 = decode_utf8(self.target.filename)\ - .replace(':','/').encode('utf-16-be') - b.write(struct.pack(b'>hhh', TAG_UNICODE_FILENAME, - len(utf16) + 2, - len(utf16) // 2)) + utf16 = decode_utf8(self.target.filename).replace(":", "/").encode("utf-16-be") + b.write( + struct.pack(b">hhh", TAG_UNICODE_FILENAME, len(utf16) + 2, len(utf16) // 2) + ) b.write(utf16) - utf16 = decode_utf8(self.volume.name)\ - .replace(':','/').encode('utf-16-be') - b.write(struct.pack(b'>hhh', TAG_UNICODE_VOLUME_NAME, - len(utf16) + 2, - len(utf16) // 2)) + utf16 = decode_utf8(self.volume.name).replace(":", "/").encode("utf-16-be") + b.write( + struct.pack( + b">hhh", TAG_UNICODE_VOLUME_NAME, len(utf16) + 2, len(utf16) // 2 + ) + ) b.write(utf16) if self.target.posix_path: posix_path = encode_utf8(self.target.posix_path) - b.write(struct.pack(b'>hh', TAG_POSIX_PATH, - len(posix_path))) + b.write(struct.pack(b">hh", TAG_POSIX_PATH, len(posix_path))) b.write(posix_path) if len(posix_path) & 1: - b.write(b'\0') + b.write(b"\0") if self.volume.posix_path: posix_path = encode_utf8(self.volume.posix_path) - b.write(struct.pack(b'>hh', TAG_POSIX_PATH_TO_MOUNTPOINT, - len(posix_path))) + b.write(struct.pack(b">hh", TAG_POSIX_PATH_TO_MOUNTPOINT, len(posix_path))) b.write(posix_path) if len(posix_path) & 1: - b.write(b'\0') + b.write(b"\0") if self.volume.disk_image_alias: d = self.volume.disk_image_alias.to_bytes() - b.write(struct.pack(b'>hh', TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE, - len(d))) + b.write(struct.pack(b">hh", TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE, len(d))) b.write(d) if len(d) & 1: - b.write(b'\0') + b.write(b"\0") if self.target.user_home_prefix_len is not None: - b.write(struct.pack(b'>hhh', TAG_USER_HOME_LENGTH_PREFIX, - 2, self.target.user_home_prefix_len)) - - for t,v in self.extra: - b.write(struct.pack(b'>hh', t, len(v))) + b.write( + struct.pack( + b">hhh", + TAG_USER_HOME_LENGTH_PREFIX, + 2, + self.target.user_home_prefix_len, + ) + ) + + for t, v in self.extra: + b.write(struct.pack(b">hh", t, len(v))) b.write(v) if len(v) & 1: - b.write(b'\0') + b.write(b"\0") + + b.write(struct.pack(b">hh", -1, 0)) - b.write(struct.pack(b'>hh', -1, 0)) - blen = b.tell() - pos b.seek(pos + 4, os.SEEK_SET) - b.write(struct.pack(b'>h', blen)) + b.write(struct.pack(b">h", blen)) def to_bytes(self): """Returns the binary representation for this :class:`Alias`.""" @@ -590,18 +755,18 @@ class Alias (object): return b.getvalue() def __str__(self): - return '<Alias target=%s>' % self.target.filename + return "<Alias target=%s>" % self.target.filename def __repr__(self): values = [] - if self.appinfo != b'\0\0\0\0': - values.append('appinfo=%r' % self.appinfo) + if self.appinfo != b"\0\0\0\0": + values.append("appinfo=%r" % self.appinfo) if self.version != 2: - values.append('version=%r' % self.version) + values.append("version=%r" % self.version) if self.volume is not None: - values.append('volume=%r' % self.volume) + values.append("volume=%r" % self.volume) if self.target is not None: - values.append('target=%r' % self.target) + values.append("target=%r" % self.target) if self.extra: - values.append('extra=%r' % self.extra) - return 'Alias(%s)' % ','.join(values) + values.append("extra=%r" % self.extra) + return "Alias(%s)" % ",".join(values) diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py new file mode 100644 index 000000000..00e16feb1 --- /dev/null +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py @@ -0,0 +1,677 @@ +# This file implements the Apple "bookmark" format, which is the replacement +# for the old-fashioned alias format. The details of this format were +# reverse engineered; some things are still not entirely clear. +# +import datetime +import os +import struct +import sys +import uuid +from urllib.parse import urljoin + +if sys.platform == "darwin": + from . import osx + +from .utils import osx_epoch + +BMK_DATA_TYPE_MASK = 0xFFFFFF00 +BMK_DATA_SUBTYPE_MASK = 0x000000FF + +BMK_STRING = 0x0100 +BMK_DATA = 0x0200 +BMK_NUMBER = 0x0300 +BMK_DATE = 0x0400 +BMK_BOOLEAN = 0x0500 +BMK_ARRAY = 0x0600 +BMK_DICT = 0x0700 +BMK_UUID = 0x0800 +BMK_URL = 0x0900 +BMK_NULL = 0x0A00 + +BMK_ST_ZERO = 0x0000 +BMK_ST_ONE = 0x0001 + +BMK_BOOLEAN_ST_FALSE = 0x0000 +BMK_BOOLEAN_ST_TRUE = 0x0001 + +# Subtypes for BMK_NUMBER are really CFNumberType values +kCFNumberSInt8Type = 1 +kCFNumberSInt16Type = 2 +kCFNumberSInt32Type = 3 +kCFNumberSInt64Type = 4 +kCFNumberFloat32Type = 5 +kCFNumberFloat64Type = 6 +kCFNumberCharType = 7 +kCFNumberShortType = 8 +kCFNumberIntType = 9 +kCFNumberLongType = 10 +kCFNumberLongLongType = 11 +kCFNumberFloatType = 12 +kCFNumberDoubleType = 13 +kCFNumberCFIndexType = 14 +kCFNumberNSIntegerType = 15 +kCFNumberCGFloatType = 16 + +# Resource property flags (from CFURLPriv.h) +kCFURLResourceIsRegularFile = 0x00000001 +kCFURLResourceIsDirectory = 0x00000002 +kCFURLResourceIsSymbolicLink = 0x00000004 +kCFURLResourceIsVolume = 0x00000008 +kCFURLResourceIsPackage = 0x00000010 +kCFURLResourceIsSystemImmutable = 0x00000020 +kCFURLResourceIsUserImmutable = 0x00000040 +kCFURLResourceIsHidden = 0x00000080 +kCFURLResourceHasHiddenExtension = 0x00000100 +kCFURLResourceIsApplication = 0x00000200 +kCFURLResourceIsCompressed = 0x00000400 +kCFURLResourceIsSystemCompressed = 0x00000400 +kCFURLCanSetHiddenExtension = 0x00000800 +kCFURLResourceIsReadable = 0x00001000 +kCFURLResourceIsWriteable = 0x00002000 +kCFURLResourceIsExecutable = 0x00004000 +kCFURLIsAliasFile = 0x00008000 +kCFURLIsMountTrigger = 0x00010000 + +# Volume property flags (from CFURLPriv.h) +kCFURLVolumeIsLocal = 0x1 +kCFURLVolumeIsAutomount = 0x2 +kCFURLVolumeDontBrowse = 0x4 +kCFURLVolumeIsReadOnly = 0x8 +kCFURLVolumeIsQuarantined = 0x10 +kCFURLVolumeIsEjectable = 0x20 +kCFURLVolumeIsRemovable = 0x40 +kCFURLVolumeIsInternal = 0x80 +kCFURLVolumeIsExternal = 0x100 +kCFURLVolumeIsDiskImage = 0x200 +kCFURLVolumeIsFileVault = 0x400 +kCFURLVolumeIsLocaliDiskMirror = 0x800 +kCFURLVolumeIsiPod = 0x1000 +kCFURLVolumeIsiDisk = 0x2000 +kCFURLVolumeIsCD = 0x4000 +kCFURLVolumeIsDVD = 0x8000 +kCFURLVolumeIsDeviceFileSystem = 0x10000 +kCFURLVolumeSupportsPersistentIDs = 0x100000000 +kCFURLVolumeSupportsSearchFS = 0x200000000 +kCFURLVolumeSupportsExchange = 0x400000000 +# reserved 0x800000000 +kCFURLVolumeSupportsSymbolicLinks = 0x1000000000 +kCFURLVolumeSupportsDenyModes = 0x2000000000 +kCFURLVolumeSupportsCopyFile = 0x4000000000 +kCFURLVolumeSupportsReadDirAttr = 0x8000000000 +kCFURLVolumeSupportsJournaling = 0x10000000000 +kCFURLVolumeSupportsRename = 0x20000000000 +kCFURLVolumeSupportsFastStatFS = 0x40000000000 +kCFURLVolumeSupportsCaseSensitiveNames = 0x80000000000 +kCFURLVolumeSupportsCasePreservedNames = 0x100000000000 +kCFURLVolumeSupportsFLock = 0x200000000000 +kCFURLVolumeHasNoRootDirectoryTimes = 0x400000000000 +kCFURLVolumeSupportsExtendedSecurity = 0x800000000000 +kCFURLVolumeSupports2TBFileSize = 0x1000000000000 +kCFURLVolumeSupportsHardLinks = 0x2000000000000 +kCFURLVolumeSupportsMandatoryByteRangeLocks = 0x4000000000000 +kCFURLVolumeSupportsPathFromID = 0x8000000000000 +# reserved 0x10000000000000 +kCFURLVolumeIsJournaling = 0x20000000000000 +kCFURLVolumeSupportsSparseFiles = 0x40000000000000 +kCFURLVolumeSupportsZeroRuns = 0x80000000000000 +kCFURLVolumeSupportsVolumeSizes = 0x100000000000000 +kCFURLVolumeSupportsRemoteEvents = 0x200000000000000 +kCFURLVolumeSupportsHiddenFiles = 0x400000000000000 +kCFURLVolumeSupportsDecmpFSCompression = 0x800000000000000 +kCFURLVolumeHas64BitObjectIDs = 0x1000000000000000 +kCFURLVolumePropertyFlagsAll = 0xFFFFFFFFFFFFFFFF + +BMK_URL_ST_ABSOLUTE = 0x0001 +BMK_URL_ST_RELATIVE = 0x0002 + +# Bookmark keys +kBookmarkURL = 0x1003 # A URL +kBookmarkPath = 0x1004 # Array of path components +kBookmarkCNIDPath = 0x1005 # Array of CNIDs +kBookmarkFileProperties = ( + 0x1010 # (CFURL rp flags, CFURL rp flags asked for, 8 bytes NULL) +) +kBookmarkFileName = 0x1020 +kBookmarkFileID = 0x1030 +kBookmarkFileCreationDate = 0x1040 +# = 0x1054 # ? +# = 0x1055 # ? +# = 0x1056 # ? +# = 0x1101 # ? +# = 0x1102 #Â ? +kBookmarkTOCPath = 0x2000 # A list of (TOC id, ?) pairs +kBookmarkVolumePath = 0x2002 +kBookmarkVolumeURL = 0x2005 +kBookmarkVolumeName = 0x2010 +kBookmarkVolumeUUID = 0x2011 # Stored (perversely) as a string +kBookmarkVolumeSize = 0x2012 +kBookmarkVolumeCreationDate = 0x2013 +kBookmarkVolumeProperties = ( + 0x2020 # (CFURL vp flags, CFURL vp flags asked for, 8 bytes NULL) +) +kBookmarkVolumeIsRoot = 0x2030 # True if volume is FS root +kBookmarkVolumeBookmark = 0x2040 # Embedded bookmark for disk image (TOC id) +kBookmarkVolumeMountPoint = 0x2050 # A URL +# = 0x2070 +kBookmarkContainingFolder = 0xC001 # Index of containing folder in path +kBookmarkUserName = 0xC011 # User that created bookmark +kBookmarkUID = 0xC012 # UID that created bookmark +kBookmarkWasFileReference = 0xD001 # True if the URL was a file reference +kBookmarkCreationOptions = 0xD010 +kBookmarkURLLengths = 0xE003 # See below +kBookmarkDisplayName = 0xF017 +kBookmarkIconData = 0xF020 +kBookmarkIconRef = 0xF021 +kBookmarkTypeBindingData = 0xF022 +kBookmarkCreationTime = 0xF030 +kBookmarkSandboxRwExtension = 0xF080 +kBookmarkSandboxRoExtension = 0xF081 +kBookmarkAliasData = 0xFE00 + +# Alias for backwards compatibility +kBookmarkSecurityExtension = kBookmarkSandboxRwExtension + +# kBookmarkURLLengths is an array that is set if the URL encoded by the +# bookmark had a base URL; in that case, each entry is the length of the +# base URL in question. Thus a URL +# +# file:///foo/bar/baz blam/blat.html +# +# will result in [3, 2], while the URL +# +# file:///foo bar/baz blam blat.html +# +# would result in [1, 2, 1, 1] + + +class Data: + def __init__(self, bytedata=None): + #: The bytes, stored as a byte string + self.bytes = bytes(bytedata) + + def __repr__(self): + return "Data(%r)" % self.bytes + + +class URL: + def __init__(self, base, rel=None): + if rel is not None: + #: The base URL, if any (a :class:`URL`) + self.base = base + #: The rest of the URL (a string) + self.relative = rel + else: + self.base = None + self.relative = base + + @property + def absolute(self): + """Return an absolute URL.""" + if self.base is None: + return self.relative + else: + return urljoin(self.base.absolute, self.relative) + + def __repr__(self): + return "URL(%r)" % self.absolute + + +class Bookmark: + def __init__(self, tocs=None): + if tocs is None: + #: The TOCs for this Bookmark + self.tocs = [] + else: + self.tocs = tocs + + @classmethod + def _get_item(cls, data, hdrsize, offset): + offset += hdrsize + if offset > len(data) - 8: + raise ValueError("Offset out of range") + + length, typecode = struct.unpack(b"<II", data[offset : offset + 8]) + + if len(data) - offset < 8 + length: + raise ValueError("Data item truncated") + + databytes = data[offset + 8 : offset + 8 + length] + + dsubtype = typecode & BMK_DATA_SUBTYPE_MASK + dtype = typecode & BMK_DATA_TYPE_MASK + + if dtype == BMK_STRING: + return databytes.decode("utf-8") + elif dtype == BMK_DATA: + return Data(databytes) + elif dtype == BMK_NUMBER: + if dsubtype == kCFNumberSInt8Type: + return ord(databytes[0]) + elif dsubtype == kCFNumberSInt16Type: + return struct.unpack(b"<h", databytes)[0] + elif dsubtype == kCFNumberSInt32Type: + return struct.unpack(b"<i", databytes)[0] + elif dsubtype == kCFNumberSInt64Type: + return struct.unpack(b"<q", databytes)[0] + elif dsubtype == kCFNumberFloat32Type: + return struct.unpack(b"<f", databytes)[0] + elif dsubtype == kCFNumberFloat64Type: + return struct.unpack(b"<d", databytes)[0] + elif dtype == BMK_DATE: + # Yes, dates really are stored as *BIG-endian* doubles; everything + # else is little-endian + secs = datetime.timedelta(seconds=struct.unpack(b">d", databytes)[0]) + return osx_epoch + secs + elif dtype == BMK_BOOLEAN: + if dsubtype == BMK_BOOLEAN_ST_TRUE: + return True + elif dsubtype == BMK_BOOLEAN_ST_FALSE: + return False + elif dtype == BMK_UUID: + return uuid.UUID(bytes=databytes) + elif dtype == BMK_URL: + if dsubtype == BMK_URL_ST_ABSOLUTE: + return URL(databytes.decode("utf-8")) + elif dsubtype == BMK_URL_ST_RELATIVE: + baseoff, reloff = struct.unpack(b"<II", databytes) + base = cls._get_item(data, hdrsize, baseoff) + rel = cls._get_item(data, hdrsize, reloff) + return URL(base, rel) + elif dtype == BMK_ARRAY: + result = [] + for aoff in range(offset + 8, offset + 8 + length, 4): + (eltoff,) = struct.unpack(b"<I", data[aoff : aoff + 4]) + result.append(cls._get_item(data, hdrsize, eltoff)) + return result + elif dtype == BMK_DICT: + result = {} + for eoff in range(offset + 8, offset + 8 + length, 8): + keyoff, valoff = struct.unpack(b"<II", data[eoff : eoff + 8]) + key = cls._get_item(data, hdrsize, keyoff) + val = cls._get_item(data, hdrsize, valoff) + result[key] = val + return result + elif dtype == BMK_NULL: + return None + + print("Unknown data type %08x" % typecode) + return (typecode, databytes) + + @classmethod + def from_bytes(cls, data): + """Create a :class:`Bookmark` given byte data.""" + + if len(data) < 16: + raise ValueError("Not a bookmark file (too short)") + + if isinstance(data, bytearray): + data = bytes(data) + + magic, size, dummy, hdrsize = struct.unpack(b"<4sIII", data[0:16]) + + if magic not in (b"book", b"alis"): + raise ValueError("Not a bookmark file (bad magic) %r" % magic) + + if hdrsize < 16: + raise ValueError("Not a bookmark file (header size too short)") + + if hdrsize > size: + raise ValueError("Not a bookmark file (header size too large)") + + if size != len(data): + raise ValueError("Not a bookmark file (truncated)") + + (tocoffset,) = struct.unpack(b"<I", data[hdrsize : hdrsize + 4]) + + tocs = [] + + while tocoffset != 0: + tocbase = hdrsize + tocoffset + if (tocoffset > size - hdrsize) or (size - tocbase < 20): + raise ValueError("TOC offset out of range") + + (tocsize, tocmagic, tocid, nexttoc, toccount) = struct.unpack( + b"<IIIII", data[tocbase : tocbase + 20] + ) + + if tocmagic != 0xFFFFFFFE: + break + + tocsize += 8 + + if size - tocbase < tocsize: + raise ValueError("TOC truncated") + + if tocsize < 12 * toccount: + raise ValueError("TOC entries overrun TOC size") + + toc = {} + for n in range(0, toccount): + ebase = tocbase + 20 + 12 * n + eid, eoffset, edummy = struct.unpack(b"<III", data[ebase : ebase + 12]) + + if eid & 0x80000000: + eid = cls._get_item(data, hdrsize, eid & 0x7FFFFFFF) + + toc[eid] = cls._get_item(data, hdrsize, eoffset) + + tocs.append((tocid, toc)) + + tocoffset = nexttoc + + return cls(tocs) + + def __getitem__(self, key): + for tid, toc in self.tocs: + if key in toc: + return toc[key] + raise KeyError("Key not found") + + def __setitem__(self, key, value): + if len(self.tocs) == 0: + self.tocs = [(1, {})] + self.tocs[0][1][key] = value + + def get(self, key, default=None): + """Lookup the value for a given key, returning a default if not + present.""" + for tid, toc in self.tocs: + if key in toc: + return toc[key] + return default + + @classmethod + def _encode_item(cls, item, offset): + if item is True: + result = struct.pack(b"<II", 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_TRUE) + elif item is False: + result = struct.pack(b"<II", 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_FALSE) + elif isinstance(item, str): + encoded = item.encode("utf-8") + result = ( + struct.pack(b"<II", len(encoded), BMK_STRING | BMK_ST_ONE) + encoded + ) + elif isinstance(item, bytes): + result = struct.pack(b"<II", len(item), BMK_STRING | BMK_ST_ONE) + item + elif isinstance(item, Data): + result = struct.pack( + b"<II", len(item.bytes), BMK_DATA | BMK_ST_ONE + ) + bytes(item.bytes) + elif isinstance(item, bytearray): + result = struct.pack(b"<II", len(item), BMK_DATA | BMK_ST_ONE) + bytes(item) + elif isinstance(item, int): + if item > -0x80000000 and item < 0x7FFFFFFF: + result = struct.pack(b"<IIi", 4, BMK_NUMBER | kCFNumberSInt32Type, item) + else: + result = struct.pack(b"<IIq", 8, BMK_NUMBER | kCFNumberSInt64Type, item) + elif isinstance(item, float): + result = struct.pack(b"<IId", 8, BMK_NUMBER | kCFNumberFloat64Type, item) + elif isinstance(item, datetime.datetime): + secs = item - osx_epoch + result = struct.pack(b"<II", 8, BMK_DATE | BMK_ST_ZERO) + struct.pack( + b">d", float(secs.total_seconds()) + ) + + elif isinstance(item, uuid.UUID): + result = struct.pack(b"<II", 16, BMK_UUID | BMK_ST_ONE) + item.bytes + elif isinstance(item, URL): + if item.base: + baseoff = offset + 16 + reloff, baseenc = cls._encode_item(item.base, baseoff) + xoffset, relenc = cls._encode_item(item.relative, reloff) + result = b"".join( + [ + struct.pack( + b"<IIII", 8, BMK_URL | BMK_URL_ST_RELATIVE, baseoff, reloff + ), + baseenc, + relenc, + ] + ) + else: + encoded = item.relative.encode("utf-8") + result = ( + struct.pack(b"<II", len(encoded), BMK_URL | BMK_URL_ST_ABSOLUTE) + + encoded + ) + elif isinstance(item, list): + ioffset = offset + 8 + len(item) * 4 + result = [struct.pack(b"<II", len(item) * 4, BMK_ARRAY | BMK_ST_ONE)] + enc = [] + for elt in item: + result.append(struct.pack(b"<I", ioffset)) + ioffset, ienc = cls._encode_item(elt, ioffset) + enc.append(ienc) + result = b"".join(result + enc) + elif isinstance(item, dict): + ioffset = offset + 8 + len(item) * 8 + result = [struct.pack(b"<II", len(item) * 8, BMK_DICT | BMK_ST_ONE)] + enc = [] + for k, v in item.items(): + result.append(struct.pack(b"<I", ioffset)) + ioffset, ienc = cls._encode_item(k, ioffset) + enc.append(ienc) + result.append(struct.pack(b"<I", ioffset)) + ioffset, ienc = cls._encode_item(v, ioffset) + enc.append(ienc) + result = b"".join(result + enc) + elif item is None: + result = struct.pack(b"<II", 0, BMK_NULL | BMK_ST_ONE) + else: + raise ValueError("Unknown item type when encoding: %s" % item) + + offset += len(result) + + # Pad to a multiple of 4 bytes + if offset & 3: + extra = 4 - (offset & 3) + result += b"\0" * extra + offset += extra + + return (offset, result) + + def to_bytes(self): + """Convert this :class:`Bookmark` to a byte representation.""" + + result = [] + tocs = [] + offset = 4 # For the offset to the first TOC + + # Generate the data and build the TOCs + for tid, toc in self.tocs: + entries = [] + + for k, v in toc.items(): + if isinstance(k, str): + noffset = offset + voffset, enc = self._encode_item(k, offset) + result.append(enc) + offset, enc = self._encode_item(v, voffset) + result.append(enc) + entries.append((noffset | 0x80000000, voffset)) + else: + entries.append((k, offset)) + offset, enc = self._encode_item(v, offset) + result.append(enc) + + # TOC entries must be sorted - CoreServicesInternal does a + # binary search to find data + entries.sort() + + tocs.append( + (tid, b"".join([struct.pack(b"<III", k, o, 0) for k, o in entries])) + ) + + first_toc_offset = offset + + # Now generate the TOC headers + for ndx, toc in enumerate(tocs): + tid, data = toc + if ndx == len(tocs) - 1: + next_offset = 0 + else: + next_offset = offset + 20 + len(data) + + result.append( + struct.pack( + b"<IIIII", + len(data) - 8, + 0xFFFFFFFE, + tid, + next_offset, + len(data) // 12, + ) + ) + result.append(data) + + offset += 20 + len(data) + + # Finally, add the header (and the first TOC offset, which isn't part + # of the header, but goes just after it) + header = struct.pack( + b"<4sIIIQQQQI", + b"book", + offset + 48, + 0x10040000, + 48, + 0, + 0, + 0, + 0, + first_toc_offset, + ) + + result.insert(0, header) + + return b"".join(result) + + @classmethod + def for_file(cls, path): + """Construct a :class:`Bookmark` for a given file.""" + + # Find the filesystem + st = osx.statfs(path) + vol_path = st.f_mntonname.decode("utf-8") + + # Grab its attributes + attrs = [ + osx.ATTR_CMN_CRTIME, + osx.ATTR_VOL_SIZE | osx.ATTR_VOL_NAME | osx.ATTR_VOL_UUID, + 0, + 0, + 0, + ] + volinfo = osx.getattrlist(vol_path, attrs, 0) + + vol_crtime = volinfo[0] + vol_size = volinfo[1] + vol_name = volinfo[2] + vol_uuid = volinfo[3] + + # Also grab various attributes of the file + attrs = [ + (osx.ATTR_CMN_OBJTYPE | osx.ATTR_CMN_CRTIME | osx.ATTR_CMN_FILEID), + 0, + 0, + 0, + 0, + ] + info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW) + + cnid = info[2] + crtime = info[1] + + if info[0] == osx.VREG: + flags = kCFURLResourceIsRegularFile + elif info[0] == osx.VDIR: + flags = kCFURLResourceIsDirectory + elif info[0] == osx.VLNK: + flags = kCFURLResourceIsSymbolicLink + else: + flags = kCFURLResourceIsRegularFile + + dirname, filename = os.path.split(path) + + relcount = 0 + if not os.path.isabs(dirname): + curdir = os.getcwd() + head, tail = os.path.split(curdir) + relcount = 0 + while head and tail: + relcount += 1 + head, tail = os.path.split(head) + dirname = os.path.join(curdir, dirname) + + # ?? foldername = os.path.basename(dirname) + + rel_path = os.path.relpath(path, vol_path) + + # Build the path arrays + name_path = [] + cnid_path = [] + head, tail = os.path.split(rel_path) + if not tail: + head, tail = os.path.split(head) + while head or tail: + if head: + attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0] + info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0) + cnid_path.insert(0, info[0]) + head, tail = os.path.split(head) + name_path.insert(0, tail) + else: + head, tail = os.path.split(head) + name_path.append(filename) + cnid_path.append(cnid) + + url_lengths = [relcount, len(name_path) - relcount] + + fileprops = Data(struct.pack(b"<QQQ", flags, 0x0F, 0)) + volprops = Data( + struct.pack( + b"<QQQ", + 0x81 | kCFURLVolumeSupportsPersistentIDs, + 0x13EF | kCFURLVolumeSupportsPersistentIDs, + 0, + ) + ) + + toc = { + kBookmarkPath: name_path, + kBookmarkCNIDPath: cnid_path, + kBookmarkFileCreationDate: crtime, + kBookmarkFileProperties: fileprops, + kBookmarkContainingFolder: len(name_path) - 2, + kBookmarkVolumePath: vol_path, + kBookmarkVolumeIsRoot: vol_path == "/", + kBookmarkVolumeURL: URL("file://" + vol_path), + kBookmarkVolumeName: vol_name, + kBookmarkVolumeSize: vol_size, + kBookmarkVolumeCreationDate: vol_crtime, + kBookmarkVolumeUUID: str(vol_uuid).upper(), + kBookmarkVolumeProperties: volprops, + kBookmarkCreationOptions: 512, + kBookmarkWasFileReference: True, + kBookmarkUserName: "unknown", + kBookmarkUID: 99, + } + + if relcount: + toc[kBookmarkURLLengths] = url_lengths + + return Bookmark([(1, toc)]) + + def __repr__(self): + result = ["Bookmark(["] + for tid, toc in self.tocs: + result.append("(0x%x, {\n" % tid) + for k, v in toc.items(): + if isinstance(k, str): + kf = repr(k) + else: + kf = "0x%04x" % k + result.append(f" {kf}: {v!r}\n") + result.append("}),\n") + result.append("])") + + return "".join(result) diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/osx.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/osx.py index d4af65d75..1b2feabaa 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/osx.py +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/osx.py @@ -1,247 +1,367 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from ctypes import * -import struct -import os import datetime +import os import uuid - -from .utils import * - -libc = cdll.LoadLibrary('/usr/lib/libc.dylib') +from ctypes import ( + POINTER, + Structure, + Union, + byref, + c_byte, + c_char, + c_char_p, + c_int, + c_long, + c_longlong, + c_short, + c_uint, + c_ulong, + c_ulonglong, + c_ushort, + c_void_p, + cdll, + create_string_buffer, + sizeof, +) + +from .utils import unix_epoch + +libc = cdll.LoadLibrary("/usr/lib/libc.dylib") # Constants -FSOPT_NOFOLLOW = 0x00000001 -FSOPT_NOINMEMUPDATE = 0x00000002 -FSOPT_REPORT_FULLSIZE = 0x00000004 +FSOPT_NOFOLLOW = 0x00000001 +FSOPT_NOINMEMUPDATE = 0x00000002 +FSOPT_REPORT_FULLSIZE = 0x00000004 FSOPT_PACK_INVAL_ATTRS = 0x00000008 +FSOPT_ATTR_CMN_EXTENDED = 0x00000020 +FSOPT_RETURN_REALDEV = 0x00000200 -VOL_CAPABILITIES_FORMAT = 0 +VOL_CAPABILITIES_FORMAT = 0 VOL_CAPABILITIES_INTERFACES = 1 -VOL_CAP_FMT_PERSISTENTOBJECTIDS = 0x00000001 -VOL_CAP_FMT_SYMBOLICLINKS = 0x00000002 -VOL_CAP_FMT_HARDLINKS = 0x00000004 -VOL_CAP_FMT_JOURNAL = 0x00000008 -VOL_CAP_FMT_JOURNAL_ACTIVE = 0x00000010 -VOL_CAP_FMT_NO_ROOT_TIMES = 0x00000020 -VOL_CAP_FMT_SPARSE_FILES = 0x00000040 -VOL_CAP_FMT_ZERO_RUNS = 0x00000080 -VOL_CAP_FMT_CASE_SENSITIVE = 0x00000100 -VOL_CAP_FMT_CASE_PRESERVING = 0x00000200 -VOL_CAP_FMT_FAST_STATFS = 0x00000400 -VOL_CAP_FMT_2TB_FILESIZE = 0x00000800 -VOL_CAP_FMT_OPENDENYMODES = 0x00001000 -VOL_CAP_FMT_HIDDEN_FILES = 0x00002000 -VOL_CAP_FMT_PATH_FROM_ID = 0x00004000 -VOL_CAP_FMT_NO_VOLUME_SIZES = 0x00008000 -VOL_CAP_FMT_DECMPFS_COMPRESSION = 0x00010000 -VOL_CAP_FMT_64BIT_OBJECT_IDS = 0x00020000 - -VOL_CAP_INT_SEARCHFS = 0x00000001 -VOL_CAP_INT_ATTRLIST = 0x00000002 -VOL_CAP_INT_NFSEXPORT = 0x00000004 -VOL_CAP_INT_READDIRATTR = 0x00000008 -VOL_CAP_INT_EXCHANGEDATA = 0x00000010 -VOL_CAP_INT_COPYFILE = 0x00000020 -VOL_CAP_INT_ALLOCATE = 0x00000040 -VOL_CAP_INT_VOL_RENAME = 0x00000080 -VOL_CAP_INT_ADVLOCK = 0x00000100 -VOL_CAP_INT_FLOCK = 0x00000200 -VOL_CAP_INT_EXTENDED_SECURITY = 0x00000400 -VOL_CAP_INT_USERACCESS = 0x00000800 -VOL_CAP_INT_MANLOCK = 0x00001000 -VOL_CAP_INT_NAMEDSTREAMS = 0x00002000 -VOL_CAP_INT_EXTENDED_ATTR = 0x00004000 - -ATTR_CMN_NAME = 0x00000001 -ATTR_CMN_DEVID = 0x00000002 -ATTR_CMN_FSID = 0x00000004 -ATTR_CMN_OBJTYPE = 0x00000008 -ATTR_CMN_OBJTAG = 0x00000010 -ATTR_CMN_OBJID = 0x00000020 -ATTR_CMN_OBJPERMANENTID = 0x00000040 -ATTR_CMN_PAROBJID = 0x00000080 -ATTR_CMN_SCRIPT = 0x00000100 -ATTR_CMN_CRTIME = 0x00000200 -ATTR_CMN_MODTIME = 0x00000400 -ATTR_CMN_CHGTIME = 0x00000800 -ATTR_CMN_ACCTIME = 0x00001000 -ATTR_CMN_BKUPTIME = 0x00002000 -ATTR_CMN_FNDRINFO = 0x00004000 -ATTR_CMN_OWNERID = 0x00008000 -ATTR_CMN_GRPID = 0x00010000 -ATTR_CMN_ACCESSMASK = 0x00020000 -ATTR_CMN_FLAGS = 0x00040000 -ATTR_CMN_USERACCESS = 0x00200000 -ATTR_CMN_EXTENDED_SECURITY = 0x00400000 -ATTR_CMN_UUID = 0x00800000 -ATTR_CMN_GRPUUID = 0x01000000 -ATTR_CMN_FILEID = 0x02000000 -ATTR_CMN_PARENTID = 0x04000000 -ATTR_CMN_FULLPATH = 0x08000000 -ATTR_CMN_ADDEDTIME = 0x10000000 -ATTR_CMN_RETURNED_ATTRS = 0x80000000 -ATTR_CMN_ALL_ATTRS = 0x9fe7ffff - -ATTR_VOL_FSTYPE = 0x00000001 -ATTR_VOL_SIGNATURE = 0x00000002 -ATTR_VOL_SIZE = 0x00000004 -ATTR_VOL_SPACEFREE = 0x00000008 -ATTR_VOL_SPACEAVAIL = 0x00000010 -ATTR_VOL_MINALLOCATION = 0x00000020 -ATTR_VOL_ALLOCATIONCLUMP = 0x00000040 -ATTR_VOL_IOBLOCKSIZE = 0x00000080 -ATTR_VOL_OBJCOUNT = 0x00000100 -ATTR_VOL_FILECOUNT = 0x00000200 -ATTR_VOL_DIRCOUNT = 0x00000400 -ATTR_VOL_MAXOBJCOUNT = 0x00000800 -ATTR_VOL_MOUNTPOINT = 0x00001000 -ATTR_VOL_NAME = 0x00002000 -ATTR_VOL_MOUNTFLAGS = 0x00004000 -ATTR_VOL_MOUNTEDDEVICE = 0x00008000 -ATTR_VOL_ENCODINGSUSED = 0x00010000 -ATTR_VOL_CAPABILITIES = 0x00020000 -ATTR_VOL_UUID = 0x00040000 -ATTR_VOL_ATTRIBUTES = 0x40000000 -ATTR_VOL_INFO = 0x80000000 -ATTR_VOL_ALL_ATTRS = 0xc007ffff - -ATTR_DIR_LINKCOUNT = 0x00000001 -ATTR_DIR_ENTRYCOUNT = 0x00000002 -ATTR_DIR_MOUNTSTATUS = 0x00000004 -DIR_MNTSTATUS_MNTPOINT = 0x00000001 -DIR_MNTSTATUS_TRIGGER = 0x00000002 -ATTR_DIR_ALL_ATTRS = 0x00000007 - -ATTR_FILE_LINKCOUNT = 0x00000001 -ATTR_FILE_TOTALSIZE = 0x00000002 -ATTR_FILE_ALLOCSIZE = 0x00000004 -ATTR_FILE_IOBLOCKSIZE = 0x00000008 -ATTR_FILE_DEVTYPE = 0x00000020 -ATTR_FILE_DATALENGTH = 0x00000200 -ATTR_FILE_DATAALLOCSIZE = 0x00000400 -ATTR_FILE_RSRCLENGTH = 0x00001000 -ATTR_FILE_RSRCALLOCSIZE = 0x00002000 - -ATTR_FILE_ALL_ATTRS = 0x0000362f - -ATTR_FORK_TOTALSIZE = 0x00000001 -ATTR_FORK_ALLOCSIZE = 0x00000002 -ATTR_FORK_ALL_ATTRS = 0x00000003 +VOL_CAP_FMT_PERSISTENTOBJECTIDS = 0x00000001 +VOL_CAP_FMT_SYMBOLICLINKS = 0x00000002 +VOL_CAP_FMT_HARDLINKS = 0x00000004 +VOL_CAP_FMT_JOURNAL = 0x00000008 +VOL_CAP_FMT_JOURNAL_ACTIVE = 0x00000010 +VOL_CAP_FMT_NO_ROOT_TIMES = 0x00000020 +VOL_CAP_FMT_SPARSE_FILES = 0x00000040 +VOL_CAP_FMT_ZERO_RUNS = 0x00000080 +VOL_CAP_FMT_CASE_SENSITIVE = 0x00000100 +VOL_CAP_FMT_CASE_PRESERVING = 0x00000200 +VOL_CAP_FMT_FAST_STATFS = 0x00000400 +VOL_CAP_FMT_2TB_FILESIZE = 0x00000800 +VOL_CAP_FMT_OPENDENYMODES = 0x00001000 +VOL_CAP_FMT_HIDDEN_FILES = 0x00002000 +VOL_CAP_FMT_PATH_FROM_ID = 0x00004000 +VOL_CAP_FMT_NO_VOLUME_SIZES = 0x00008000 +VOL_CAP_FMT_DECMPFS_COMPRESSION = 0x00010000 +VOL_CAP_FMT_64BIT_OBJECT_IDS = 0x00020000 + +VOL_CAP_INT_SEARCHFS = 0x00000001 +VOL_CAP_INT_ATTRLIST = 0x00000002 +VOL_CAP_INT_NFSEXPORT = 0x00000004 +VOL_CAP_INT_READDIRATTR = 0x00000008 +VOL_CAP_INT_EXCHANGEDATA = 0x00000010 +VOL_CAP_INT_COPYFILE = 0x00000020 +VOL_CAP_INT_ALLOCATE = 0x00000040 +VOL_CAP_INT_VOL_RENAME = 0x00000080 +VOL_CAP_INT_ADVLOCK = 0x00000100 +VOL_CAP_INT_FLOCK = 0x00000200 +VOL_CAP_INT_EXTENDED_SECURITY = 0x00000400 +VOL_CAP_INT_USERACCESS = 0x00000800 +VOL_CAP_INT_MANLOCK = 0x00001000 +VOL_CAP_INT_NAMEDSTREAMS = 0x00002000 +VOL_CAP_INT_EXTENDED_ATTR = 0x00004000 +VOL_CAP_INT_CLONE = 0x00010000 +VOL_CAP_INT_SNAPSHOT = 0x00020000 +VOL_CAP_INT_RENAME_SWAP = 0x00040000 +VOL_CAP_INT_RENAME_EXCL = 0x00080000 +VOL_CAP_INT_RENAME_OPENFAIL = 0x00100000 + +ATTR_CMN_NAME = 0x00000001 +ATTR_CMN_DEVID = 0x00000002 +ATTR_CMN_FSID = 0x00000004 +ATTR_CMN_OBJTYPE = 0x00000008 +ATTR_CMN_OBJTAG = 0x00000010 +ATTR_CMN_OBJID = 0x00000020 +ATTR_CMN_OBJPERMANENTID = 0x00000040 +ATTR_CMN_PAROBJID = 0x00000080 +ATTR_CMN_SCRIPT = 0x00000100 +ATTR_CMN_CRTIME = 0x00000200 +ATTR_CMN_MODTIME = 0x00000400 +ATTR_CMN_CHGTIME = 0x00000800 +ATTR_CMN_ACCTIME = 0x00001000 +ATTR_CMN_BKUPTIME = 0x00002000 +ATTR_CMN_FNDRINFO = 0x00004000 +ATTR_CMN_OWNERID = 0x00008000 +ATTR_CMN_GRPID = 0x00010000 +ATTR_CMN_ACCESSMASK = 0x00020000 +ATTR_CMN_FLAGS = 0x00040000 +ATTR_CMN_GEN_COUNT = 0x00080000 +ATTR_CMN_DOCUMENT_ID = 0x00100000 +ATTR_CMN_USERACCESS = 0x00200000 +ATTR_CMN_EXTENDED_SECURITY = 0x00400000 +ATTR_CMN_UUID = 0x00800000 +ATTR_CMN_GRPUUID = 0x01000000 +ATTR_CMN_FILEID = 0x02000000 +ATTR_CMN_PARENTID = 0x04000000 +ATTR_CMN_FULLPATH = 0x08000000 +ATTR_CMN_ADDEDTIME = 0x10000000 +ATTR_CMN_ERROR = 0x20000000 +ATTR_CMN_DATA_PROTECT_FLAGS = 0x40000000 +ATTR_CMN_RETURNED_ATTRS = 0x80000000 +ATTR_CMN_ALL_ATTRS = 0xFFFFFFFF + +ATTR_CMN_VALIDMASK = 0xFFFFFFFF +ATTR_CMN_SETMASK = 0x51C7FF00 +ATTR_CMN_VOLSETMASK = 0x00006700 + +ATTR_VOL_FSTYPE = 0x00000001 +ATTR_VOL_SIGNATURE = 0x00000002 +ATTR_VOL_SIZE = 0x00000004 +ATTR_VOL_SPACEFREE = 0x00000008 +ATTR_VOL_SPACEAVAIL = 0x00000010 +ATTR_VOL_MINALLOCATION = 0x00000020 +ATTR_VOL_ALLOCATIONCLUMP = 0x00000040 +ATTR_VOL_IOBLOCKSIZE = 0x00000080 +ATTR_VOL_OBJCOUNT = 0x00000100 +ATTR_VOL_FILECOUNT = 0x00000200 +ATTR_VOL_DIRCOUNT = 0x00000400 +ATTR_VOL_MAXOBJCOUNT = 0x00000800 +ATTR_VOL_MOUNTPOINT = 0x00001000 +ATTR_VOL_NAME = 0x00002000 +ATTR_VOL_MOUNTFLAGS = 0x00004000 +ATTR_VOL_MOUNTEDDEVICE = 0x00008000 +ATTR_VOL_ENCODINGSUSED = 0x00010000 +ATTR_VOL_CAPABILITIES = 0x00020000 +ATTR_VOL_UUID = 0x00040000 +ATTR_VOL_QUOTA_SIZE = 0x10000000 +ATTR_VOL_RESERVED_SIZE = 0x20000000 +ATTR_VOL_ATTRIBUTES = 0x40000000 +ATTR_VOL_INFO = 0x80000000 +ATTR_VOL_ALL_ATTRS = 0xF007FFFF + +ATTR_DIR_LINKCOUNT = 0x00000001 +ATTR_DIR_ENTRYCOUNT = 0x00000002 +ATTR_DIR_MOUNTSTATUS = 0x00000004 +DIR_MNTSTATUS_MNTPOINT = 0x00000001 +DIR_MNTSTATUS_TRIGGER = 0x00000002 +ATTR_DIR_ALLOCSIZE = 0x00000008 +ATTR_DIR_IOBLOCKSIZE = 0x00000010 +ATTR_DIR_DATALENGTH = 0x00000020 +ATTR_DIR_ALL_ATTRS = 0x0000003F + +ATTR_DIR_VALIDMASK = 0x0000003F +ATTR_DIR_SETMASK = 0x00000000 + +ATTR_FILE_LINKCOUNT = 0x00000001 +ATTR_FILE_TOTALSIZE = 0x00000002 +ATTR_FILE_ALLOCSIZE = 0x00000004 +ATTR_FILE_IOBLOCKSIZE = 0x00000008 +ATTR_FILE_DEVTYPE = 0x00000020 +ATTR_FILE_FORKCOUNT = 0x00000080 +ATTR_FILE_FORKLIST = 0x00000100 +ATTR_FILE_DATALENGTH = 0x00000200 +ATTR_FILE_DATAALLOCSIZE = 0x00000400 +ATTR_FILE_RSRCLENGTH = 0x00001000 +ATTR_FILE_RSRCALLOCSIZE = 0x00002000 +ATTR_FILE_ALL_ATTRS = 0x000037FF + +ATTR_FILE_VALIDMASK = 0x000037FF +ATTR_FILE_SETMASK = 0x00000020 + +# These are deprecated +ATTR_FORK_TOTALSIZE = 0x00000001 +ATTR_FORK_ALLOCSIZE = 0x00000002 +ATTR_FORK_ALL_ATTRS = 0x00000003 + +# These go in the fork attribute field +ATTR_CMNEXT_RELPATH = 0x00000004 +ATTR_CMNEXT_PRIVATESIZE = 0x00000008 +ATTR_CMNEXT_LINKID = 0x0000010 +ATTR_CMNEXT_NOFIRMLINKPATH = 0x00000020 +ATTR_CMNEXT_REALDEVID = 0x00000040 +ATTR_CMNEXT_REALFSID = 0x00000080 +ATTR_CMNEXT_CLONEID = 0x00000100 +ATTR_CMNEXT_EXT_FLAGS = 0x00000200 +ATTR_CMNEXT_RECURSIVE_GENCOUNT = 0x00000400 +ATTR_CMNEXT_ALL_ATTRS = 0x000007FC + +ATTR_CMNEXT_VALIDMASK = 0x000007FC +ATTR_CMNEXT_SETMASK = 0x00000000 + +ATTR_FORK_VALIDMASK = 0x00000003 +ATTR_FORK_SETMASK = 0x00000000 # These can't be used -ATTR_FILE_FORKCOUNT = 0x00000080 -ATTR_FILE_FORKLIST = 0x00000100 -ATTR_CMN_NAMEDATTRCOUNT = 0x00080000 -ATTR_CMN_NAMEDATTRLIST = 0x00100000 -ATTR_FILE_DATAEXTENTS = 0x00000800 -ATTR_FILE_RSRCEXTENTS = 0x00004000 -ATTR_FILE_CLUMPSIZE = 0x00000010 -ATTR_FILE_FILETYPE = 0x00000040 +ATTR_CMN_NAMEDATTRCOUNT = 0x00080000 +ATTR_CMN_NAMEDATTRLIST = 0x00100000 +ATTR_FILE_CLUMPSIZE = 0x00000010 +ATTR_FILE_FILETYPE = 0x00000040 +ATTR_FILE_DATAEXTENTS = 0x00000800 +ATTR_FILE_RSRCEXTENTS = 0x00004000 + class attrlist(Structure): - _fields_ = [('bitmapcount', c_ushort), - ('reserved', c_ushort), - ('commonattr', c_uint), - ('volattr', c_uint), - ('dirattr', c_uint), - ('fileattr', c_uint), - ('forkattr', c_uint)] + _fields_ = [ + ("bitmapcount", c_ushort), + ("reserved", c_ushort), + ("commonattr", c_uint), + ("volattr", c_uint), + ("dirattr", c_uint), + ("fileattr", c_uint), + ("forkattr", c_uint), + ] + class attribute_set_t(Structure): - _fields_ = [('commonattr', c_uint), - ('volattr', c_uint), - ('dirattr', c_uint), - ('fileattr', c_uint), - ('forkattr', c_uint)] + _fields_ = [ + ("commonattr", c_uint), + ("volattr", c_uint), + ("dirattr", c_uint), + ("fileattr", c_uint), + ("forkattr", c_uint), + ] + class fsobj_id_t(Structure): - _fields_ = [('fid_objno', c_uint), - ('fid_generation', c_uint)] + _fields_ = [ + ("fid_objno", c_uint), + ("fid_generation", c_uint), + ] + class timespec(Structure): - _fields_ = [('tv_sec', c_long), - ('tv_nsec', c_long)] + _fields_ = [ + ("tv_sec", c_long), + ("tv_nsec", c_long), + ] + class attrreference_t(Structure): - _fields_ = [('attr_dataoffset', c_int), - ('attr_length', c_uint)] + _fields_ = [ + ("attr_dataoffset", c_int), + ("attr_length", c_uint), + ] + class fsid_t(Structure): - _fields_ = [('val', c_uint * 2)] + _fields_ = [ + ("val", c_uint * 2), + ] + class guid_t(Structure): - _fields_ = [('g_guid', c_byte*16)] + _fields_ = [ + ("g_guid", c_byte * 16), + ] + class kauth_ace(Structure): - _fields_ = [('ace_applicable', guid_t), - ('ace_flags', c_uint)] + _fields_ = [ + ("ace_applicable", guid_t), + ("ace_flags", c_uint), + ] + class kauth_acl(Structure): - _fields_ = [('acl_entrycount', c_uint), - ('acl_flags', c_uint), - ('acl_ace', kauth_ace * 128)] + _fields_ = [ + ("acl_entrycount", c_uint), + ("acl_flags", c_uint), + ("acl_ace", kauth_ace * 128), + ] + class kauth_filesec(Structure): - _fields_ = [('fsec_magic', c_uint), - ('fsec_owner', guid_t), - ('fsec_group', guid_t), - ('fsec_acl', kauth_acl)] + _fields_ = [ + ("fsec_magic", c_uint), + ("fsec_owner", guid_t), + ("fsec_group", guid_t), + ("fsec_acl", kauth_acl), + ] + class diskextent(Structure): - _fields_ = [('startblock', c_uint), - ('blockcount', c_uint)] + _fields_ = [ + ("startblock", c_uint), + ("blockcount", c_uint), + ] + OSType = c_uint UInt16 = c_ushort SInt16 = c_short SInt32 = c_int + class Point(Structure): - _fields_ = [('x', SInt16), - ('y', SInt16)] + _fields_ = [ + ("x", SInt16), + ("y", SInt16), + ] + + class Rect(Structure): - _fields_ = [('x', SInt16), - ('y', SInt16), - ('w', SInt16), - ('h', SInt16)] + _fields_ = [ + ("x", SInt16), + ("y", SInt16), + ("w", SInt16), + ("h", SInt16), + ] + + class FileInfo(Structure): - _fields_ = [('fileType', OSType), - ('fileCreator', OSType), - ('finderFlags', UInt16), - ('location', Point), - ('reservedField', UInt16), - ('reserved1', SInt16 * 4), - ('extendedFinderFlags', UInt16), - ('reserved2', SInt16), - ('putAwayFolderID', SInt32)] + _fields_ = [ + ("fileType", OSType), + ("fileCreator", OSType), + ("finderFlags", UInt16), + ("location", Point), + ("reservedField", UInt16), + ("reserved1", SInt16 * 4), + ("extendedFinderFlags", UInt16), + ("reserved2", SInt16), + ("putAwayFolderID", SInt32), + ] + + class FolderInfo(Structure): - _fields_ = [('windowBounds', Rect), - ('finderFlags', UInt16), - ('location', Point), - ('reservedField', UInt16), - ('scrollPosition', Point), - ('reserved1', SInt32), - ('extendedFinderFlags', UInt16), - ('reserved2', SInt16), - ('putAwayFolderID', SInt32)] + _fields_ = [ + ("windowBounds", Rect), + ("finderFlags", UInt16), + ("location", Point), + ("reservedField", UInt16), + ("scrollPosition", Point), + ("reserved1", SInt32), + ("extendedFinderFlags", UInt16), + ("reserved2", SInt16), + ("putAwayFolderID", SInt32), + ] + + class FinderInfo(Union): - _fields_ = [('fileInfo', FileInfo), - ('folderInfo', FolderInfo)] + _fields_ = [ + ("fileInfo", FileInfo), + ("folderInfo", FolderInfo), + ] + extentrecord = diskextent * 8 - vol_capabilities_set_t = c_uint * 4 + class vol_capabilities_attr_t(Structure): - _fields_ = [('capabilities', vol_capabilities_set_t), - ('valid', vol_capabilities_set_t)] + _fields_ = [ + ("capabilities", vol_capabilities_set_t), + ("valid", vol_capabilities_set_t), + ] + class vol_attributes_attr_t(Structure): - _fields_ = [('validattr', attribute_set_t), - ('nativeattr', attribute_set_t)] + _fields_ = [ + ("validattr", attribute_set_t), + ("nativeattr", attribute_set_t), + ] + dev_t = c_uint @@ -298,28 +418,34 @@ int64_t = c_longlong uint64_t = c_ulonglong off_t = c_long size_t = c_ulong -uuid_t = c_byte*16 +uuid_t = c_byte * 16 NAME_MAX = 255 PATH_MAX = 1024 +FSTYPE_MAX = 16 + class struct_statfs(Structure): - _fields_ = [('f_bsize', uint32_t), - ('f_iosize', int32_t), - ('f_blocks', uint64_t), - ('f_bfree', uint64_t), - ('f_bavail', uint64_t), - ('f_files', uint64_t), - ('f_ffree', uint64_t), - ('f_fsid', fsid_t), - ('f_owner', uid_t), - ('f_type', uint32_t), - ('f_flags', uint32_t), - ('f_fssubtype', uint32_t), - ('f_fstypename', c_char * 16), - ('f_mntonname', c_char * PATH_MAX), - ('f_mntfromname', c_char * PATH_MAX), - ('f_reserved', uint32_t * 8)] + _fields_ = [ + ("f_bsize", uint32_t), + ("f_iosize", int32_t), + ("f_blocks", uint64_t), + ("f_bfree", uint64_t), + ("f_bavail", uint64_t), + ("f_files", uint64_t), + ("f_ffree", uint64_t), + ("f_fsid", fsid_t), + ("f_owner", uid_t), + ("f_type", uint32_t), + ("f_flags", uint32_t), + ("f_fssubtype", uint32_t), + ("f_fstypename", c_char * FSTYPE_MAX), + ("f_mntonname", c_char * PATH_MAX), + ("f_mntfromname", c_char * PATH_MAX), + ("f_flags_ext", uint32_t), + ("f_reserved", uint32_t * 7), + ] + # Calculate the maximum number of bytes required for the attribute buffer _attr_info = ( @@ -330,6 +456,7 @@ _attr_info = ( (0, ATTR_CMN_FSID, sizeof(fsid_t)), (0, ATTR_CMN_OBJTYPE, sizeof(fsobj_type_t)), (0, ATTR_CMN_OBJTAG, sizeof(fsobj_tag_t)), + (0, ATTR_CMN_OBJID, sizeof(fsobj_id_t)), (0, ATTR_CMN_OBJPERMANENTID, sizeof(fsobj_id_t)), (0, ATTR_CMN_PAROBJID, sizeof(fsobj_id_t)), (0, ATTR_CMN_SCRIPT, sizeof(text_encoding_t)), @@ -345,6 +472,8 @@ _attr_info = ( (0, ATTR_CMN_NAMEDATTRCOUNT, None), (0, ATTR_CMN_NAMEDATTRLIST, None), (0, ATTR_CMN_FLAGS, sizeof(uint32_t)), + (0, ATTR_CMN_GEN_COUNT, sizeof(uint32_t)), + (0, ATTR_CMN_DOCUMENT_ID, sizeof(uint32_t)), (0, ATTR_CMN_USERACCESS, sizeof(uint32_t)), (0, ATTR_CMN_EXTENDED_SECURITY, sizeof(attrreference_t) + sizeof(kauth_filesec)), (0, ATTR_CMN_UUID, sizeof(guid_t)), @@ -353,7 +482,7 @@ _attr_info = ( (0, ATTR_CMN_PARENTID, sizeof(uint64_t)), (0, ATTR_CMN_FULLPATH, sizeof(attrreference_t) + PATH_MAX), (0, ATTR_CMN_ADDEDTIME, sizeof(timespec)), - + (0, ATTR_CMN_DATA_PROTECT_FLAGS, sizeof(uint32_t)), # Volume attributes (1, ATTR_VOL_FSTYPE, sizeof(uint32_t)), (1, ATTR_VOL_SIGNATURE, sizeof(uint32_t)), @@ -374,13 +503,16 @@ _attr_info = ( (1, ATTR_VOL_ENCODINGSUSED, sizeof(c_ulonglong)), (1, ATTR_VOL_CAPABILITIES, sizeof(vol_capabilities_attr_t)), (1, ATTR_VOL_UUID, sizeof(uuid_t)), + (1, ATTR_VOL_QUOTA_SIZE, sizeof(off_t)), + (1, ATTR_VOL_RESERVED_SIZE, sizeof(off_t)), (1, ATTR_VOL_ATTRIBUTES, sizeof(vol_attributes_attr_t)), - # Directory attributes (2, ATTR_DIR_LINKCOUNT, sizeof(uint32_t)), (2, ATTR_DIR_ENTRYCOUNT, sizeof(uint32_t)), (2, ATTR_DIR_MOUNTSTATUS, sizeof(uint32_t)), - + (2, ATTR_DIR_ALLOCSIZE, sizeof(off_t)), + (2, ATTR_DIR_IOBLOCKSIZE, sizeof(uint32_t)), + (2, ATTR_DIR_DATALENGTH, sizeof(off_t)), # File attributes (3, ATTR_FILE_LINKCOUNT, sizeof(uint32_t)), (3, ATTR_FILE_TOTALSIZE, sizeof(off_t)), @@ -397,22 +529,33 @@ _attr_info = ( (3, ATTR_FILE_RSRCLENGTH, sizeof(off_t)), (3, ATTR_FILE_RSRCALLOCSIZE, sizeof(off_t)), (3, ATTR_FILE_RSRCEXTENTS, sizeof(extentrecord)), - # Fork attributes (4, ATTR_FORK_TOTALSIZE, sizeof(off_t)), - (4, ATTR_FORK_ALLOCSIZE, sizeof(off_t)) - ) - + (4, ATTR_FORK_ALLOCSIZE, sizeof(off_t)), + # Extended common attributes + (4, ATTR_CMNEXT_RELPATH, sizeof(attrreference_t) + PATH_MAX), + (4, ATTR_CMNEXT_PRIVATESIZE, sizeof(off_t)), + (4, ATTR_CMNEXT_LINKID, sizeof(uint64_t)), + (4, ATTR_CMNEXT_NOFIRMLINKPATH, sizeof(attrreference_t) + PATH_MAX), + (4, ATTR_CMNEXT_REALDEVID, sizeof(dev_t)), + (4, ATTR_CMNEXT_REALFSID, sizeof(fsid_t)), + (4, ATTR_CMNEXT_CLONEID, sizeof(uint64_t)), + (4, ATTR_CMNEXT_EXT_FLAGS, sizeof(uint64_t)), +) + + def _attrbuf_size(attrs): size = 4 for entry in _attr_info: if attrs[entry[0]] & entry[1]: if entry[2] is None: - raise ValueError('Unsupported attribute (%u, %x)' - % (entry[0], entry[1])) + raise ValueError( + "Unsupported attribute (%u, %x)" % (entry[0], entry[1]) + ) size += entry[2] return size + _getattrlist = libc.getattrlist _getattrlist.argtypes = [c_char_p, POINTER(attrlist), c_void_p, c_ulong, c_ulong] _getattrlist.restype = c_int @@ -421,23 +564,40 @@ _fgetattrlist = libc.fgetattrlist _fgetattrlist.argtypes = [c_int, POINTER(attrlist), c_void_p, c_ulong, c_ulong] _fgetattrlist.restype = c_int -_statfs = libc['statfs$INODE64'] -_statfs.argtypes = [c_char_p, POINTER(struct_statfs)] +try: + _statfs = libc["statfs$INODE64"] +except (KeyError, AttributeError): + _statfs = libc["statfs"] + +_statfs.argtypes = [ + c_char_p, + POINTER(struct_statfs), +] _statfs.restype = c_int -_fstatfs = libc['fstatfs$INODE64'] -_fstatfs.argtypes = [c_int, POINTER(struct_statfs)] +try: + _fstatfs = libc["fstatfs$INODE64"] +except (KeyError, AttributeError): + _fstatfs = libc["fstatfs"] + +_fstatfs.argtypes = [ + c_int, + POINTER(struct_statfs), +] _fstatfs.restype = c_int + def _datetime_from_timespec(ts): td = datetime.timedelta(seconds=ts.tv_sec + 1.0e-9 * ts.tv_nsec) return unix_epoch + td + def _decode_utf8_nul(sz): - nul = sz.find(b'\0') + nul = sz.find(b"\0") if nul > -1: sz = sz[:nul] - return sz.decode('utf-8') + return sz.decode("utf-8") + def _decode_attrlist_result(buf, attrs, options): result = [] @@ -452,14 +612,14 @@ def _decode_attrlist_result(buf, attrs, options): if attrs[0] & ATTR_CMN_RETURNED_ATTRS: a = attribute_set_t.from_buffer(buf, offset) result.append(a) - offset += sizeof (attribute_set_t) + offset += sizeof(attribute_set_t) if not (options & FSOPT_PACK_INVAL_ATTRS): attrs = [a.commonattr, a.volattr, a.dirattr, a.fileattr, a.forkattr] if attrs[0] & ATTR_CMN_NAME: a = attrreference_t.from_buffer(buf, offset) ofs = offset + a.attr_dataoffset - name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) + name = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) result.append(name) if attrs[0] & ATTR_CMN_DEVID: a = dev_t.from_buffer(buf, offset) @@ -533,6 +693,14 @@ def _decode_attrlist_result(buf, attrs, options): a = uint32_t.from_buffer(buf, offset) offset += sizeof(uint32_t) result.append(a.value) + if attrs[0] & ATTR_CMN_GEN_COUNT: + a = uint32_t.from_buffer(buf, offset) + offset += sizeof(uint32_t) + result.append(a.value) + if attrs[0] & ATTR_CMN_DOCUMENT_ID: + a = uint32_t.from_buffer(buf, offset) + offset += sizeof(uint32_t) + result.append(a.value) if attrs[0] & ATTR_CMN_USERACCESS: a = uint32_t.from_buffer(buf, offset) offset += sizeof(uint32_t) @@ -542,22 +710,29 @@ def _decode_attrlist_result(buf, attrs, options): ofs = offset + a.attr_dataoffset offset += sizeof(attrreference_t) ec = uint32_t.from_buffer(buf, ofs + 36).value + class kauth_acl(Structure): - _fields_ = [('acl_entrycount', c_uint), - ('acl_flags', c_uint), - ('acl_ace', kauth_ace * ec)] + _fields_ = [ + ("acl_entrycount", c_uint), + ("acl_flags", c_uint), + ("acl_ace", kauth_ace * ec), + ] + class kauth_filesec(Structure): - _fields_ = [('fsec_magic', c_uint), - ('fsec_owner', guid_t), - ('fsec_group', guid_t), - ('fsec_acl', kauth_acl)] + _fields_ = [ + ("fsec_magic", c_uint), + ("fsec_owner", guid_t), + ("fsec_group", guid_t), + ("fsec_acl", kauth_acl), + ] + a = kauth_filesec.from_buffer(buf, ofs) result.append(a) if attrs[0] & ATTR_CMN_UUID: - result.append(uuid.UUID(bytes=buf[offset:offset+16])) + result.append(uuid.UUID(bytes=buf[offset : offset + 16])) offset += sizeof(guid_t) if attrs[0] & ATTR_CMN_GRPUUID: - result.append(uuid.UUID(bytes=buf[offset:offset+16])) + result.append(uuid.UUID(bytes=buf[offset : offset + 16])) offset += sizeof(guid_t) if attrs[0] & ATTR_CMN_FILEID: a = uint64_t.from_buffer(buf, offset) @@ -570,13 +745,17 @@ def _decode_attrlist_result(buf, attrs, options): if attrs[0] & ATTR_CMN_FULLPATH: a = attrreference_t.from_buffer(buf, offset) ofs = offset + a.attr_dataoffset - path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) + path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) result.append(path) if attrs[0] & ATTR_CMN_ADDEDTIME: a = timespec.from_buffer(buf, offset) offset += sizeof(timespec) result.append(_datetime_from_timespec(a)) + if attrs[0] & ATTR_CMN_DATA_PROTECT_FLAGS: + a = uint32_t.from_buffer(buf, offset) + offset += sizeof(uint32_t) + result.append(a.value) # Volume attributes if attrs[1] & ATTR_VOL_FSTYPE: @@ -630,14 +809,14 @@ def _decode_attrlist_result(buf, attrs, options): if attrs[1] & ATTR_VOL_MOUNTPOINT: a = attrreference_t.from_buffer(buf, offset) ofs = offset + a.attr_dataoffset - path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) + path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) result.append(path) if attrs[1] & ATTR_VOL_NAME: a = attrreference_t.from_buffer(buf, offset) ofs = offset + a.attr_dataoffset - name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) + name = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) result.append(name) if attrs[1] & ATTR_VOL_MOUNTFLAGS: a = uint32_t.from_buffer(buf, offset) @@ -646,8 +825,8 @@ def _decode_attrlist_result(buf, attrs, options): if attrs[1] & ATTR_VOL_MOUNTEDDEVICE: a = attrreference_t.from_buffer(buf, offset) ofs = offset + a.attr_dataoffset - path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) + path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) result.append(path) if attrs[1] & ATTR_VOL_ENCODINGSUSED: a = c_ulonglong.from_buffer(buf, offset) @@ -658,8 +837,16 @@ def _decode_attrlist_result(buf, attrs, options): offset += sizeof(vol_capabilities_attr_t) result.append(a) if attrs[1] & ATTR_VOL_UUID: - result.append(uuid.UUID(bytes=buf[offset:offset+16])) + result.append(uuid.UUID(bytes=buf[offset : offset + 16])) offset += sizeof(uuid_t) + if attrs[1] & ATTR_VOL_QUOTA_SIZE: + a = off_t.from_buffer(buf, offset) + offset += sizeof(off_t) + result.append(a.value) + if attrs[1] & ATTR_VOL_RESERVED_SIZE: + a = off_t.from_buffer(buf, offset) + offset += sizeof(off_t) + result.append(a.value) if attrs[1] & ATTR_VOL_ATTRIBUTES: a = vol_attributes_attr_t.from_buffer(buf, offset) offset += sizeof(vol_attributes_attr_t) @@ -678,6 +865,18 @@ def _decode_attrlist_result(buf, attrs, options): a = uint32_t.from_buffer(buf, offset) offset += sizeof(uint32_t) result.append(a.value) + if attrs[2] & ATTR_DIR_ALLOCSIZE: + a = off_t.from_buffer(buf, offset) + offset += sizeof(off_t) + result.append(a.value) + if attrs[2] & ATTR_DIR_IOBLOCKSIZE: + a = uint32_t.from_buffer(buf, offset) + offset += sizeof(uint32_t) + result.append(a.value) + if attrs[2] & ATTR_DIR_DATALENGTH: + a = off_t.from_buffer(buf, offset) + offset += sizeof(off_t) + result.append(a.value) # File attributes if attrs[3] & ATTR_FILE_LINKCOUNT: @@ -746,34 +945,78 @@ def _decode_attrlist_result(buf, attrs, options): a = off_t.from_buffer(buf, offset) offset += sizeof(off_t) result.append(a.value) - + + # Extended common attributes + if attrs[4] & ATTR_CMNEXT_RELPATH: + a = attrreference_t.from_buffer(buf, offset) + ofs = offset + a.attr_dataoffset + path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) + result.append(path) + if attrs[4] & ATTR_CMNEXT_PRIVATESIZE: + a = off_t.from_buffer(buf, offset) + offset += sizeof(off_t) + result.append(a.value) + if attrs[4] & ATTR_CMNEXT_LINKID: + a = uint64_t.from_buffer(buf, offset) + offset += sizeof(uint64_t) + result.append(a.value) + if attrs[4] & ATTR_CMNEXT_NOFIRMLINKPATH: + a = attrreference_t.from_buffer(buf, offset) + ofs = offset + a.attr_dataoffset + path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length]) + offset += sizeof(attrreference_t) + result.append(path) + if attrs[4] & ATTR_CMNEXT_REALDEVID: + a = dev_t.from_buffer(buf, offset) + offset += sizeof(dev_t) + result.append(a.value) + if attrs[4] & ATTR_CMNEXT_REALFSID: + a = fsid_t.from_buffer(buf, offset) + offset += sizeof(fsid_t) + result.append(a.value) + if attrs[4] & ATTR_CMNEXT_CLONEID: + a = uint64_t.from_buffer(buf, offset) + offset += sizeof(uint64_t) + result.append(a.value) + if attrs[4] & ATTR_CMNEXT_EXT_FLAGS: + a = uint64_t.from_buffer(buf, offset) + offset += sizeof(uint64_t) + result.append(a.value) + return result + # Sadly, ctypes.get_errno() seems not to work __error = libc.__error __error.restype = POINTER(c_int) + def _get_errno(): return __error().contents.value - + + def getattrlist(path, attrs, options): if not isinstance(path, bytes): - path = path.encode('utf-8') + path = path.encode("utf-8") attrs = list(attrs) if attrs[1]: attrs[1] |= ATTR_VOL_INFO - alist = attrlist(bitmapcount=5, - commonattr=attrs[0], - volattr=attrs[1], - dirattr=attrs[2], - fileattr=attrs[3], - forkattr=attrs[4]) + alist = attrlist( + bitmapcount=5, + commonattr=attrs[0], + volattr=attrs[1], + dirattr=attrs[2], + fileattr=attrs[3], + forkattr=attrs[4], + ) bufsize = _attrbuf_size(attrs) buf = create_string_buffer(bufsize) - ret = _getattrlist(path, byref(alist), buf, bufsize, - options | FSOPT_REPORT_FULLSIZE) + ret = _getattrlist( + path, byref(alist), buf, bufsize, options | FSOPT_REPORT_FULLSIZE + ) if ret < 0: err = _get_errno() @@ -781,24 +1024,26 @@ def getattrlist(path, attrs, options): return _decode_attrlist_result(buf, attrs, options) + def fgetattrlist(fd, attrs, options): - if hasattr(fd, 'fileno'): + if hasattr(fd, "fileno"): fd = fd.fileno() attrs = list(attrs) if attrs[1]: attrs[1] |= ATTR_VOL_INFO - alist = attrlist(bitmapcount=5, - commonattr=attrs[0], - volattr=attrs[1], - dirattr=attrs[2], - fileattr=attrs[3], - forkattr=attrs[4]) + alist = attrlist( + bitmapcount=5, + commonattr=attrs[0], + volattr=attrs[1], + dirattr=attrs[2], + fileattr=attrs[3], + forkattr=attrs[4], + ) bufsize = _attrbuf_size(attrs) buf = create_string_buffer(bufsize) - ret = _fgetattrlist(fd, byref(alist), buf, bufsize, - options | FSOPT_REPORT_FULLSIZE) + ret = _fgetattrlist(fd, byref(alist), buf, bufsize, options | FSOPT_REPORT_FULLSIZE) if ret < 0: err = _get_errno() @@ -806,9 +1051,10 @@ def fgetattrlist(fd, attrs, options): return _decode_attrlist_result(buf, attrs, options) + def statfs(path): if not isinstance(path, bytes): - path = path.encode('utf-8') + path = path.encode("utf-8") result = struct_statfs() ret = _statfs(path, byref(result)) if ret < 0: @@ -816,8 +1062,9 @@ def statfs(path): raise OSError(err, os.strerror(err), path) return result + def fstatfs(fd): - if hasattr(fd, 'fileno'): + if hasattr(fd, "fileno"): fd = fd.fileno() result = struct_statfs() ret = _fstatfs(fd, byref(result)) diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/qt_attribution.json b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/qt_attribution.json index 968b6318c..968b6318c 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/qt_attribution.json +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/qt_attribution.json diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py new file mode 100644 index 000000000..3bf45155c --- /dev/null +++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py @@ -0,0 +1,20 @@ +import datetime + +ZERO = datetime.timedelta(0) + + +class UTC(datetime.tzinfo): + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + +utc = UTC() +mac_epoch = datetime.datetime(1904, 1, 1, 0, 0, 0, 0, utc) +unix_epoch = datetime.datetime(1970, 1, 1, 0, 0, 0, 0, utc) +osx_epoch = datetime.datetime(2001, 1, 1, 0, 0, 0, 0, utc) |