Files
Jonas Devlieghere ffeffce925 [lldb] Consistently use "null-terminated" across LLDB (NFC) (#224801)
It appears that both spellings are correct, but "null terminator" is far
more common, while "NUL terminator" is technically precise regarding the
ASCII character name. Most common in LLDB was "NULL terminated" which is
the worst of both worlds. This rallies around "null-terminated".

- Adjective -> null-terminated
- Verb -> null-terminate
- Nouns left unhyphenated but lowercased: null terminator, null
termination
2026-09-22 18:35:16 -07:00

225 lines
7.9 KiB
Python
Executable File

#!/usr/bin/env python3
import string
import struct
import sys
class FileExtract:
"""Decode binary data from a file"""
def __init__(self, f, b="="):
"""Initialize with an open binary file and optional byte order"""
self.file = f
self.byte_order = b
self.offsets = list()
def set_byte_order(self, b):
'''Set the byte order, valid values are "big", "little", "swap", "native", "<", ">", "@", "="'''
if b == "big":
self.byte_order = ">"
elif b == "little":
self.byte_order = "<"
elif b == "swap":
# swap what ever the current byte order is
self.byte_order = swap_unpack_char()
elif b == "native":
self.byte_order = "="
elif b == "<" or b == ">" or b == "@" or b == "=":
self.byte_order = b
else:
print("error: invalid byte order specified: '%s'" % b)
def is_in_memory(self):
return False
def seek(self, offset, whence=0):
if self.file:
return self.file.seek(offset, whence)
raise ValueError
def tell(self):
if self.file:
return self.file.tell()
raise ValueError
def read_size(self, byte_size):
s = self.file.read(byte_size)
if len(s) != byte_size:
return None
return s
def push_offset_and_seek(self, offset):
'''Push the current file offset and seek to "offset"'''
self.offsets.append(self.file.tell())
self.file.seek(offset, 0)
def pop_offset_and_seek(self):
"""Pop a previously pushed file offset, or do nothing if there were no previously pushed offsets"""
if len(self.offsets) > 0:
self.file.seek(self.offsets.pop())
def get_sint8(self, fail_value=0):
"""Extract a single int8_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(1)
if s:
(v,) = struct.unpack(self.byte_order + "b", s)
return v
else:
return fail_value
def get_uint8(self, fail_value=0):
"""Extract a single uint8_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(1)
if s:
(v,) = struct.unpack(self.byte_order + "B", s)
return v
else:
return fail_value
def get_sint16(self, fail_value=0):
"""Extract a single int16_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(2)
if s:
(v,) = struct.unpack(self.byte_order + "h", s)
return v
else:
return fail_value
def get_uint16(self, fail_value=0):
"""Extract a single uint16_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(2)
if s:
(v,) = struct.unpack(self.byte_order + "H", s)
return v
else:
return fail_value
def get_sint32(self, fail_value=0):
"""Extract a single int32_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(4)
if s:
(v,) = struct.unpack(self.byte_order + "i", s)
return v
else:
return fail_value
def get_uint32(self, fail_value=0):
"""Extract a single uint32_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(4)
if s:
(v,) = struct.unpack(self.byte_order + "I", s)
return v
else:
return fail_value
def get_sint64(self, fail_value=0):
"""Extract a single int64_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(8)
if s:
(v,) = struct.unpack(self.byte_order + "q", s)
return v
else:
return fail_value
def get_uint64(self, fail_value=0):
"""Extract a single uint64_t from the binary file at the current file position, returns a single integer"""
s = self.read_size(8)
if s:
(v,) = struct.unpack(self.byte_order + "Q", s)
return v
else:
return fail_value
def get_fixed_length_c_string(
self, n, fail_value="", isprint_only_with_space_padding=False
):
"""Extract a single fixed length C string from the binary file at the current file position, returns a single C string"""
s = self.read_size(n)
if s:
(cstr,) = struct.unpack(self.byte_order + ("%i" % n) + "s", s)
# Strip trialing NULLs
cstr = string.strip(cstr, "\0")
if isprint_only_with_space_padding:
for c in cstr:
if c in string.printable or ord(c) == 0:
continue
return fail_value
return cstr
else:
return fail_value
def get_c_string(self):
"""Extract a single null-terminated C string from the binary file at the current file position, returns a single C string"""
cstr = ""
byte = self.get_uint8()
while byte != 0:
cstr += "%c" % byte
byte = self.get_uint8()
return cstr
def get_n_sint8(self, n, fail_value=0):
"""Extract "n" int8_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "b", s)
else:
return (fail_value,) * n
def get_n_uint8(self, n, fail_value=0):
"""Extract "n" uint8_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "B", s)
else:
return (fail_value,) * n
def get_n_sint16(self, n, fail_value=0):
"""Extract "n" int16_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(2 * n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "h", s)
else:
return (fail_value,) * n
def get_n_uint16(self, n, fail_value=0):
"""Extract "n" uint16_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(2 * n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "H", s)
else:
return (fail_value,) * n
def get_n_sint32(self, n, fail_value=0):
"""Extract "n" int32_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(4 * n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "i", s)
else:
return (fail_value,) * n
def get_n_uint32(self, n, fail_value=0):
"""Extract "n" uint32_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(4 * n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "I", s)
else:
return (fail_value,) * n
def get_n_sint64(self, n, fail_value=0):
"""Extract "n" int64_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(8 * n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "q", s)
else:
return (fail_value,) * n
def get_n_uint64(self, n, fail_value=0):
"""Extract "n" uint64_t integers from the binary file at the current file position, returns a list of integers"""
s = self.read_size(8 * n)
if s:
return struct.unpack(self.byte_order + ("%u" % n) + "Q", s)
else:
return (fail_value,) * n