mirror of
https://github.com/elliotnunn/machfs.git
synced 2025-02-19 11:31:05 +00:00
Use Unicode strings instead of bytes
This commit is contained in:
parent
73f01d3e81
commit
e3d3623de9
@ -32,3 +32,8 @@ def chunkify(b, blksize):
|
||||
ab = b[i:i+blksize]
|
||||
if len(ab) < blksize: ab += bytes(blksize-len(ab))
|
||||
yield ab
|
||||
|
||||
|
||||
def pstring(orig):
|
||||
macroman = orig.encode('mac_roman')
|
||||
return bytes([len(macroman)]) + macroman
|
||||
|
@ -1,13 +1,6 @@
|
||||
import collections
|
||||
|
||||
|
||||
_CASE = list(range(256)) # cheating, fix this!
|
||||
|
||||
|
||||
def _to_lower(orig):
|
||||
return bytes(_CASE[x] for x in orig)
|
||||
|
||||
|
||||
class AbstractFolder(collections.MutableMapping):
|
||||
def __init__(self, from_dict=()):
|
||||
self._prefdict = {} # lowercase to preferred
|
||||
@ -16,33 +9,35 @@ class AbstractFolder(collections.MutableMapping):
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
try:
|
||||
key = key.encode('mac_roman')
|
||||
key = key.decode('mac_roman')
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if len(key) > 31:
|
||||
raise ValueError('Max filename length = 31')
|
||||
key.encode('mac_roman')
|
||||
|
||||
lower = _to_lower(key)
|
||||
if not (1 <= len(key) <= 31):
|
||||
raise ValueError('Filename range 1-31 chars')
|
||||
|
||||
lower = key.lower()
|
||||
self._prefdict[lower] = key
|
||||
self._maindict[lower] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
key = key.encode('mac_roman')
|
||||
key = key.decode('mac_roman')
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
lower = _to_lower(key)
|
||||
lower = key.lower()
|
||||
return self._maindict[lower]
|
||||
|
||||
def __delitem__(self, key):
|
||||
try:
|
||||
value = value.encode('mac_roman')
|
||||
value = value.decode('mac_roman')
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
lower = _to_lower(key)
|
||||
lower = key.lower()
|
||||
del self._maindict[lower]
|
||||
del self._prefdict[lower]
|
||||
|
||||
@ -58,7 +53,6 @@ class AbstractFolder(collections.MutableMapping):
|
||||
|
||||
def iter_paths(self):
|
||||
for name, child in self.items():
|
||||
print(name, child)
|
||||
yield ((name,), child)
|
||||
try:
|
||||
childs_children = child.iter_paths()
|
||||
|
@ -1,5 +1,4 @@
|
||||
import struct
|
||||
import collections
|
||||
from . import btree, bitmanip, directory
|
||||
|
||||
|
||||
@ -101,19 +100,9 @@ class Volume(directory.AbstractFolder):
|
||||
self.bootblocks = bytes(1024) # optional; for booting HFS volumes
|
||||
self.crdate = 0 # date and time of volume creation
|
||||
self.lsmod = 0 # date and time of last modification
|
||||
self._name = b'Untitled'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
if len(value) > 27:
|
||||
raise ValueError('Max volume name length = 27')
|
||||
self._name = value
|
||||
self.name = 'Untitled'
|
||||
|
||||
def read(self, from_volume):
|
||||
self._dirtree = {}
|
||||
self.bootblocks = from_volume[:1024]
|
||||
|
||||
drSigWord, drCrDate, drLsMod, drAtrb, drNmFls, \
|
||||
@ -127,7 +116,7 @@ class Volume(directory.AbstractFolder):
|
||||
|
||||
self.crdate = drCrDate
|
||||
self.lsmod = drLsMod
|
||||
self.name = drVN
|
||||
self.name = drVN.decode('mac_roman')
|
||||
|
||||
block2offset = lambda block: 512*drAlBlSt + drAlBlkSiz*block
|
||||
extent2bytes = lambda firstblk, blkcnt: from_volume[block2offset(firstblk):block2offset(firstblk+blkcnt)]
|
||||
@ -140,7 +129,7 @@ class Volume(directory.AbstractFolder):
|
||||
pass
|
||||
|
||||
cnids = {}
|
||||
childrenof = collections.defaultdict(dict)
|
||||
childlist = [] # list of (parent_cnid, child_name, child_object) tuples
|
||||
|
||||
for rec in btree.dump_btree(extrec2bytes(drCTExtRec)):
|
||||
# create a directory tree from the catalog file
|
||||
@ -156,9 +145,9 @@ class Volume(directory.AbstractFolder):
|
||||
datatype = (None, 'dir', 'file', 'dthread', 'fthread')[val[0]]
|
||||
datarec = val[2:]
|
||||
|
||||
print(datatype + '\t' + repr(key))
|
||||
print('\t', datarec)
|
||||
print()
|
||||
# print(datatype + '\t' + repr(key))
|
||||
# print('\t', datarec)
|
||||
# print()
|
||||
|
||||
if datatype == 'dir':
|
||||
dirFlags, dirVal, dirDirID, dirCrDat, dirMdDat, dirBkDat, dirUsrInfo, dirFndrInfo \
|
||||
@ -166,7 +155,7 @@ class Volume(directory.AbstractFolder):
|
||||
|
||||
f = Folder()
|
||||
cnids[dirDirID] = f
|
||||
childrenof[ckrParID][ckrCName] = f
|
||||
childlist.append((ckrParID, ckrCName, f))
|
||||
|
||||
f.crdat, f.mddat, f.bkdat = dirCrDat, dirMdDat, dirBkDat
|
||||
|
||||
@ -181,7 +170,7 @@ class Volume(directory.AbstractFolder):
|
||||
|
||||
f = File()
|
||||
cnids[filFlNum] = f
|
||||
childrenof[ckrParID][ckrCName] = f
|
||||
childlist.append((ckrParID, ckrCName, f))
|
||||
|
||||
f.crdat, f.mddat, f.bkdat = filCrDat, filMdDat, filBkDat
|
||||
f.type, f.creator, f.flags, f.x, f.y = struct.unpack_from('>4s4sHHH', filUsrWds)
|
||||
@ -206,9 +195,10 @@ class Volume(directory.AbstractFolder):
|
||||
# elif datatype == 4:
|
||||
# print('fil thread:', rec)
|
||||
|
||||
for parent, children in childrenof.items():
|
||||
if parent != 1: # not the mythical parent of root!
|
||||
cnids[parent].update(children)
|
||||
for parent_cnid, child_name, child_obj in childlist:
|
||||
if parent_cnid != 1:
|
||||
parent_obj = cnids[parent_cnid]
|
||||
parent_obj[child_name] = child_obj
|
||||
|
||||
self.update(cnids[2])
|
||||
|
||||
@ -290,8 +280,9 @@ class Volume(directory.AbstractFolder):
|
||||
if wrap.cnid == 1: continue
|
||||
|
||||
obj = wrap.of
|
||||
pstrname = bitmanip.pstring(path[-1])
|
||||
|
||||
mainrec_key = struct.pack('>LB', path2wrap[path[:-1]].cnid, len(path[-1])) + path[-1]
|
||||
mainrec_key = struct.pack('>L', path2wrap[path[:-1]].cnid) + pstrname
|
||||
|
||||
if isinstance(wrap.of, File):
|
||||
drFilCnt += 1
|
||||
@ -339,7 +330,7 @@ class Volume(directory.AbstractFolder):
|
||||
|
||||
thdrec_key = struct.pack('>Lx', wrap.cnid)
|
||||
thdrec_val_type = 4 if isinstance(wrap.of, File) else 3
|
||||
thdrec_val = struct.pack('>BxxxxxxxxxLB', thdrec_val_type, path2wrap[path[:-1]].cnid, len(path[-1])) + path[-1]
|
||||
thdrec_val = struct.pack('>BxxxxxxxxxL', thdrec_val_type, path2wrap[path[:-1]].cnid) + pstrname
|
||||
|
||||
catalog.append((thdrec_key, thdrec_val))
|
||||
|
||||
@ -371,13 +362,16 @@ class Volume(directory.AbstractFolder):
|
||||
drWrCnt = 0 # ????volume write count
|
||||
drVCSize = drVBMCSize = drCtlCSize = 0
|
||||
drAtrb = 1<<8 # volume attributes (hwlock, swlock, CLEANUNMOUNT, badblocks)
|
||||
drVN = self.name
|
||||
drVN = self.name.encode('mac_roman')
|
||||
drVolBkUp = 0 # date and time of last backup
|
||||
drVSeqNum = 0 # volume backup sequence number
|
||||
drFndrInfo = bytes(32) # information used by the Finder
|
||||
drCrDate = self.crdate
|
||||
drLsMod = self.lsmod
|
||||
|
||||
if not (1 <= len(drVN) <= 27):
|
||||
raise ValueError('Volume name range 1-27 chars')
|
||||
|
||||
vib = struct.pack('>2sLLHHHHHLLHLH28pLHLLLHLL32sHHHLHHxxxxxxxxLHHxxxxxxxx',
|
||||
drSigWord, drCrDate, drLsMod, drAtrb, drNmFls,
|
||||
drVBMSt, drAllocPtr, drNmAlBlks, drAlBlkSiz, drClpSiz, drAlBlSt,
|
||||
|
23
test_all.py
23
test_all.py
@ -2,10 +2,16 @@ from machfs import *
|
||||
import os
|
||||
import time
|
||||
|
||||
def test_upperlower():
|
||||
h = Volume()
|
||||
h['alpha'] = File()
|
||||
assert h['alpha'] is h['ALPHA']
|
||||
assert list(h.keys()) == ['alpha']
|
||||
|
||||
def test_roundtrip():
|
||||
h = Volume()
|
||||
f = File()
|
||||
h[b'single file'] = f
|
||||
h['single file'] = f
|
||||
f.data = f.rsrc = b'1234' * 4096
|
||||
|
||||
copies = [h.write(800*1024)]
|
||||
@ -16,21 +22,18 @@ def test_roundtrip():
|
||||
|
||||
assert copies[0] == copies[1]
|
||||
assert copies[1] == copies[2]
|
||||
assert f.data in copies[-1]
|
||||
|
||||
def test_macos_mount():
|
||||
h = Volume()
|
||||
h.name = b'ElmoTest'
|
||||
h.name = 'ElmoTest'
|
||||
hf = File()
|
||||
hf.data = b'12345' * 10
|
||||
for i in reversed(range(100)):
|
||||
last = b'testfile-%03d' % i
|
||||
last = 'testfile-%03d' % i
|
||||
h[last] = hf
|
||||
ser = h.write(10*1024*1024)
|
||||
|
||||
h2 = Volume()
|
||||
h2.read(ser)
|
||||
assert h2[b'testfile-000'].data == hf.data
|
||||
|
||||
open('/tmp/SMALL.dmg','wb').write(ser)
|
||||
os.system('hdiutil attach /tmp/SMALL.dmg')
|
||||
n = 10
|
||||
@ -44,6 +47,10 @@ def test_macos_mount():
|
||||
pass
|
||||
else:
|
||||
break
|
||||
recovered = open('/Volumes/ElmoTest/' + last.decode('ascii'),'rb').read()
|
||||
recovered = open('/Volumes/ElmoTest/' + last,'rb').read()
|
||||
os.system('umount /Volumes/ElmoTest')
|
||||
assert recovered == hf.data
|
||||
|
||||
# h2 = Volume()
|
||||
# h2.read(ser)
|
||||
# assert h2['testfile-000'].data == hf.data
|
||||
|
Loading…
x
Reference in New Issue
Block a user