aboutsummaryrefslogtreecommitdiffstats
path: root/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py')
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py772
1 files changed, 772 insertions, 0 deletions
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py
new file mode 100644
index 000000000..bf34fb01b
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py
@@ -0,0 +1,772 @@
+import datetime
+import io
+import os
+import os.path
+import struct
+import sys
+from unicodedata import normalize
+
+if sys.platform == "darwin":
+ from . import osx
+
+from .utils import mac_epoch
+
+ALIAS_KIND_FILE = 0
+ALIAS_KIND_FOLDER = 1
+
+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
+ALIAS_400KB_FLOPPY_DISK = 2
+ALIAS_800KB_FLOPPY_DISK = 3
+ALIAS_1_44MB_FLOPPY_DISK = 4
+ALIAS_EJECTABLE_DISK = 5
+
+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")
+
+
+def decode_utf8(s):
+ if isinstance(s, bytes):
+ return s.decode("utf-8")
+ return s
+
+
+class AppleShareInfo:
+ def __init__(self, zone=None, server=None, user=None):
+ #: The AppleShare zone
+ self.zone = zone
+ #: The AFP server
+ self.server = server
+ #: The username
+ self.user = user
+
+ def __repr__(self):
+ 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
+ #: (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
+ #: * ALIAS_NETWORK_DISK
+ #: * ALIAS_400KB_FLOPPY_DISK
+ #: * ALIAS_800KB_FLOPPY_DISK
+ #: * ALIAS_1_44MB_FLOPPY_DISK
+ #: * ALIAS_EJECTABLE_DISK
+ self.disk_type = disk_type
+
+ #: Filesystem attribute flags (from HFS volume header)
+ self.attribute_flags = attribute_flags
+
+ #: Filesystem identifier
+ self.fs_id = fs_id
+
+ #: AppleShare information (for automatic remounting of network shares)
+ #: *(optional)*
+ self.appleshare_info = appleshare_info
+
+ #: Driver name (*probably* contains a disk driver name on older Macs)
+ #: *(optional)*
+ self.driver_name = driver_name
+
+ #: POSIX path of the mount point of the target's volume
+ #: *(optional)*
+ self.posix_path = posix_path
+
+ #: :class:`Alias` object pointing at the disk image on which the
+ #: target's volume resides *(optional)*
+ self.disk_image_alias = disk_image_alias
+
+ #: Dialup information (for automatic establishment of dialup connections)
+ self.dialup_info = dialup_info
+
+ #: 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",
+ ]
+ 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",
+ ]
+ for a in kwargs:
+ v = getattr(self, a)
+ if v is not 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
+
+ #: The CNID (Catalog Node ID) of the target's containing folder;
+ #: CNIDs are similar to but different than traditional UNIX inode
+ #: numbers
+ self.folder_cnid = folder_cnid
+
+ #: The CNID (Catalog Node ID) of the target
+ self.cnid = cnid
+
+ #: The target's *creation* date.
+ self.creation_date = creation_date
+
+ #: The target's Mac creator code (a four-character binary string)
+ self.creator_code = creator_code
+
+ #: The target's Mac type code (a four-character binary string)
+ self.type_code = type_code
+
+ #: The depth of the alias? Always seems to be -1 on OS X.
+ self.levels_from = levels_from
+
+ #: The depth of the target? Always seems to be -1 on OS X.
+ self.levels_to = levels_to
+
+ #: The (POSIX) name of the target's containing folder. *(optional)*
+ self.folder_name = folder_name
+
+ #: The path from the volume root as a sequence of CNIDs. *(optional)*
+ self.cnid_path = cnid_path
+
+ #: The Carbon path of the target *(optional)*
+ self.carbon_path = carbon_path
+
+ #: The POSIX path of the target relative to the volume root. Note
+ #: that this may or may not have a leading '/' character, but it is
+ #: always relative to the containing volume. *(optional)*
+ self.posix_path = posix_path
+
+ #: If the path points into a user's home folder, the number of folders
+ #: deep that we go before we get to that home folder. *(optional)*
+ self.user_home_prefix_len = user_home_prefix_len
+
+ def __repr__(self):
+ 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)
+ 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",
+ ]
+ for a in kwargs:
+ v = getattr(self, a)
+ values.append(f"{a}={v!r}")
+
+ return "TargetInfo(%s)" % ",".join(values)
+
+
+TAG_CARBON_FOLDER_NAME = 0
+TAG_CNID_PATH = 1
+TAG_CARBON_PATH = 2
+TAG_APPLESHARE_ZONE = 3
+TAG_APPLESHARE_SERVER_NAME = 4
+TAG_APPLESHARE_USERNAME = 5
+TAG_DRIVER_NAME = 6
+TAG_NETWORK_MOUNT_INFO = 9
+TAG_DIALUP_INFO = 10
+TAG_UNICODE_FILENAME = 14
+TAG_UNICODE_VOLUME_NAME = 15
+TAG_HIGH_RES_VOLUME_CREATION_DATE = 16
+TAG_HIGH_RES_CREATION_DATE = 17
+TAG_POSIX_PATH = 18
+TAG_POSIX_PATH_TO_MOUNTPOINT = 19
+TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE = 20
+TAG_USER_HOME_LENGTH_PREFIX = 21
+
+
+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 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
+
+ #: A list of extra `(tag, value)` pairs
+ self.extra = list(extra)
+
+ @classmethod
+ def _from_fd(cls, b):
+ appinfo, recsize, version = struct.unpack(b">4shh", b.read(8))
+
+ if recsize < 150:
+ 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.target.levels_from = levels_from
+ alias.target.levels_to = levels_to
+
+ tag = struct.unpack(b">h", b.read(2))[0]
+
+ while tag != -1:
+ 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("/", ":")
+ elif tag == TAG_CNID_PATH:
+ 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:
+ if alias.volume.appleshare_info is None:
+ alias.volume.appleshare_info = AppleShareInfo()
+ alias.volume.appleshare_info.zone = value
+ elif tag == TAG_APPLESHARE_SERVER_NAME:
+ if alias.volume.appleshare_info is None:
+ alias.volume.appleshare_info = AppleShareInfo()
+ alias.volume.appleshare_info.server = value
+ elif tag == TAG_APPLESHARE_USERNAME:
+ if alias.volume.appleshare_info is None:
+ alias.volume.appleshare_info = AppleShareInfo()
+ alias.volume.appleshare_info.user = value
+ elif tag == TAG_DRIVER_NAME:
+ alias.volume.driver_name = value
+ elif tag == TAG_NETWORK_MOUNT_INFO:
+ alias.volume.network_mount_info = value
+ elif tag == TAG_DIALUP_INFO:
+ alias.volume.dialup_info = value
+ elif tag == TAG_UNICODE_FILENAME:
+ alias.target.filename = value[2:].decode("utf-16be")
+ elif tag == TAG_UNICODE_VOLUME_NAME:
+ 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
+ )
+ 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
+ )
+ elif tag == TAG_POSIX_PATH:
+ alias.target.posix_path = value.decode()
+ elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT:
+ alias.volume.posix_path = value.decode()
+ 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]
+ else:
+ alias.extra.append((tag, value))
+
+ 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."""
+ with io.BytesIO(bytes) as b:
+ return cls._from_fd(b)
+
+ @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)")
+
+ 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]
+ 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,
+ ]
+ info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW)
+
+ if info[0] == osx.VDIR:
+ kind = ALIAS_KIND_FOLDER
+ else:
+ kind = ALIAS_KIND_FILE
+
+ cnid = info[3]
+ folder_cnid = info[4]
+
+ dirname, filename = os.path.split(path)
+
+ 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)
+ else:
+ 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.folder_name = foldername
+ a.volume.posix_path = vol_path
+
+ rel_path = os.path.relpath(path, vol_path)
+
+ # Leave off the initial '/' if vol_path is '/' (no idea why)
+ if vol_path == b"/":
+ a.target.posix_path = rel_path
+ else:
+ a.target.posix_path = b"/" + rel_path
+
+ # Construct the Carbon and CNID paths
+ carbon_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.append(info[0])
+ 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)
+
+ 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))
+
+ 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()
+
+ 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)))
+ b.write(carbon_foldername)
+ if len(carbon_foldername) & 1:
+ b.write(b"\0")
+
+ b.write(
+ struct.pack(
+ b">hhQhhQ",
+ TAG_HIGH_RES_VOLUME_CREATION_DATE,
+ 8,
+ int(voldate * 65536),
+ TAG_HIGH_RES_CREATION_DATE,
+ 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)))
+ 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)))
+ b.write(carbon_path)
+ if len(carbon_path) & 1:
+ 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(ai.zone)
+ if len(ai.zone) & 1:
+ b.write(b"\0")
+ if 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")
+ if 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")
+
+ 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(driver_name)
+ if len(driver_name) & 1:
+ 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(self.volume.network_mount_info)
+ if len(self.volume.network_mount_info) & 1:
+ 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(self.volume.network_mount_info)
+ if len(self.volume.network_mount_info) & 1:
+ 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)
+ )
+ 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
+ )
+ )
+ 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(posix_path)
+ if len(posix_path) & 1:
+ 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(posix_path)
+ if len(posix_path) & 1:
+ 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(d)
+ if len(d) & 1:
+ 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(v)
+ if len(v) & 1:
+ b.write(b"\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))
+
+ def to_bytes(self):
+ """Returns the binary representation for this :class:`Alias`."""
+ with io.BytesIO() as b:
+ self._to_fd(b)
+ return b.getvalue()
+
+ def __str__(self):
+ 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.version != 2:
+ values.append("version=%r" % self.version)
+ if self.volume is not None:
+ values.append("volume=%r" % self.volume)
+ if self.target is not None:
+ values.append("target=%r" % self.target)
+ if self.extra:
+ values.append("extra=%r" % self.extra)
+ return "Alias(%s)" % ",".join(values)