Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions Lib/plistlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
__all__ = [
"readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
"Data", "InvalidFileException", "FMT_XML", "FMT_BINARY",
"load", "dump", "loads", "dumps"
"load", "dump", "loads", "dumps", "UID"
]

import binascii
Expand Down Expand Up @@ -175,6 +175,21 @@ def __repr__(self):
#


class UID:
def __init__(self, data):
if not isinstance(data, int):
raise TypeError("data must be an int")
self.data = data

def __index__(self):
return self.data

def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, repr(self.data))

def __reduce__(self):
return (self.__class__, (self.data, ))

#
# XML support
#
Expand All @@ -187,6 +202,10 @@ def __repr__(self):
"""


# XML key for reading UID data
_PLISTUIDKEY = 'CF$UID'


# Regex to find any control chars, except for \t \n and \r
_controlCharPat = re.compile(
r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
Expand Down Expand Up @@ -248,6 +267,7 @@ class _PlistParser:
def __init__(self, use_builtin_types, dict_type):
self.stack = []
self.current_key = None
self.last_dict_key = None
self.root = None
self._use_builtin_types = use_builtin_types
self._dict_type = dict_type
Expand Down Expand Up @@ -299,14 +319,22 @@ def get_data(self):

def begin_dict(self, attrs):
d = self._dict_type()
self.last_dict_key = self.current_key
self.add_object(d)
self.stack.append(d)

def end_dict(self):
if self.current_key:
raise ValueError("missing value for key '%s' at line %d" %
(self.current_key,self.parser.CurrentLineNumber))
self.stack.pop()
last_dict = self.stack.pop()
if len(last_dict.keys()) == 1 and list(last_dict.keys())[0] == _PLISTUIDKEY:
uid = UID(last_dict[_PLISTUIDKEY])
if isinstance(self.stack[-1], type([])):
self.stack[-1][-1] = uid
elif isinstance(self.stack[-1], type({})):
self.stack[-1][self.last_dict_key] = uid
self.last_dict_key = None

def end_key(self):
if self.current_key or not isinstance(self.stack[-1], type({})):
Expand Down Expand Up @@ -427,6 +455,9 @@ def write_value(self, value):
elif isinstance(value, Data):
self.write_data(value)

elif isinstance(value, UID):
self.write_dict({_PLISTUIDKEY: value.data})

elif isinstance(value, (bytes, bytearray)):
self.write_bytes(value)

Expand Down Expand Up @@ -649,8 +680,9 @@ def _read_object(self, ref):
s = self._get_size(tokenL)
result = self._fp.read(s * 2).decode('utf-16be')

# tokenH == 0x80 is documented as 'UID' and appears to be used for
# keyed-archiving, not in plists.
elif tokenH == 0x80: # UID
# used by Key-Archiver plist files
result = UID(int.from_bytes(self._fp.read(1 + tokenL), 'big'))

elif tokenH == 0xA0: # array
s = self._get_size(tokenL)
Expand Down Expand Up @@ -874,6 +906,9 @@ def _write_object(self, value):

self._fp.write(t)

elif isinstance(value, UID):
self._fp.write(struct.pack('>BB', 0x80, value))

elif isinstance(value, (list, tuple)):
refs = [self._getrefnum(o) for o in value]
s = len(refs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Allow plistlib to read binary plist files that were created as a Key-Archive
file. Specifically, this allows the binary reader to process 0x80 (UID)
elements as integers.