Refactor Make/DumpHFS into Python library

This commit is contained in:
Elliot Nunn 2018-11-06 08:38:59 +08:00
parent 34eecddc28
commit 350f11b0c0
3 changed files with 139 additions and 144 deletions

View File

@ -1,16 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import os from machfs import Volume
import os.path as path
from machfs import Volume, Folder, File
import macresources
def any_exists(at_path):
if path.exists(at_path): return True
if path.exists(at_path + '.rdump'): return True
if path.exists(at_path + '.idump'): return True
return False
args = argparse.ArgumentParser() args = argparse.ArgumentParser()
@ -23,39 +14,4 @@ with open(args.src[0], 'rb') as f:
v = Volume() v = Volume()
v.read(f.read()) v.read(f.read())
written = [] v.write_folder(args.dir[0])
for p, obj in v.iter_paths():
nativepath = path.join(args.dir[0], *(comp.replace(path.sep, ':') for comp in p))
if isinstance(obj, Folder):
os.makedirs(nativepath, exist_ok=True)
elif obj.mddate != obj.bkdate or not any_exists(nativepath):
data = obj.data
if obj.type == b'TEXT':
data = data.decode('mac_roman').replace('\r', os.linesep).encode('utf8')
rsrc = obj.rsrc
if rsrc:
rsrc = macresources.parse_file(rsrc)
rsrc = macresources.make_rez_code(rsrc, ascii_clean=True)
info = obj.type + obj.creator
if info == b'????????': info = b''
for thing, suffix in ((data, ''), (rsrc, '.rdump'), (info, '.idump')):
wholepath = nativepath + suffix
if thing or (suffix == '' and not rsrc):
written.append(wholepath)
with open(written[-1], 'wb') as f:
f.write(thing)
else:
try:
os.remove(wholepath)
except FileNotFoundError:
pass
if written:
t = path.getmtime(written[-1])
for f in written:
os.utime(f, (t, t))

View File

@ -1,12 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import os
import os.path as path
import re
from datetime import datetime from datetime import datetime
from machfs import Volume, Folder, File from machfs import Volume
import macresources
######################################################################## ########################################################################
@ -76,99 +72,8 @@ args = args.parse_args()
vol = Volume() vol = Volume()
vol.name = args.name vol.name = args.name
vol.crdate = vol.mddate = vol.bkdate = args.date
######################################################################## if args.dir: vol.read_folder(args.dir, date=args.date, mpw_dates=args.mpw_dates)
def includefilter(n):
if n.startswith('.'): return False
if n.endswith('.rdump'): return True
if n.endswith('.idump'): return True
return True
def swapsep(n):
return n.replace(':', path.sep)
def mkbasename(n):
base, ext = path.splitext(n)
if ext in ('.rdump', '.idump'):
return base
else:
return n
if args.dir is not None:
tmptree = {args.dir: vol}
for dirpath, dirnames, filenames in os.walk(args.dir):
dirnames[:] = [swapsep(x) for x in dirnames if includefilter(x)]
filenames[:] = [swapsep(x) for x in filenames if includefilter(x)]
for dn in dirnames:
newdir = Folder()
newdir.crdate = newdir.mddate = newdir.bkdate = args.date
tmptree[dirpath][dn] = newdir
tmptree[path.join(dirpath, dn)] = newdir
for fn in filenames:
basename = mkbasename(fn)
fullbase = path.join(dirpath, basename)
fullpath = path.join(dirpath, fn)
try:
thefile = tmptree[fullbase]
except KeyError:
thefile = File()
thefile.real_t = 0 # for the MPW hack
thefile.crdate = thefile.mddate = thefile.bkdate = args.date
thefile.contributors = []
tmptree[fullbase] = thefile
if fn.endswith('.idump'):
with open(fullpath, 'rb') as f:
thefile.type = f.read(4)
thefile.creator = f.read(4)
elif fn.endswith('rdump'):
rez = open(fullpath, 'rb').read()
resources = macresources.parse_rez_code(rez)
resfork = macresources.make_file(resources, align=4)
thefile.rsrc = resfork
else:
thefile.data = open(fullpath, 'rb').read()
thefile.contributors.append(fullpath)
if args.mpw_dates:
thefile.real_t = max(thefile.real_t, path.getmtime(fullpath))
tmptree[dirpath][basename] = thefile
for pathtpl, obj in vol.iter_paths():
try:
if obj.type == b'TEXT':
obj.data = obj.data.decode('utf8').replace('\r\n', '\r').replace('\n', '\r').encode('mac_roman')
except AttributeError:
pass
########################################################################
if args.mpw_dates:
all_real_times = set()
for pathtpl, obj in vol.iter_paths():
try:
all_real_times.add(obj.real_t)
except AttributeError:
pass
ts2idx = {ts: idx for (idx, ts) in enumerate(sorted(set(all_real_times)))}
for pathtpl, obj in vol.iter_paths():
try:
real_t = obj.real_t
except AttributeError:
pass
else:
fake_t = obj.crdate + 60 * ts2idx[real_t]
obj.crdate = obj.mddate = obj.bkdate = fake_t
########################################################################
image = vol.write(args.size, startapp=args.app) image = vol.write(args.size, startapp=args.app)
with open(args.dest[0], 'wb') as f: with open(args.dest[0], 'wb') as f:

View File

@ -1,5 +1,7 @@
import struct import struct
from macresources import Resource, make_file, parse_file import os
from os import path
from macresources import Resource, make_file, parse_file, make_rez_code, parse_rez_code
from . import btree, bitmanip, directory from . import btree, bitmanip, directory
@ -494,3 +496,135 @@ class Volume(directory.AbstractFolder):
finalchunks.append(vib) finalchunks.append(vib)
finalchunks.append(bytes(512)) finalchunks.append(bytes(512))
return b''.join(finalchunks) return b''.join(finalchunks)
def read_folder(self, folder_path, date=0, mpw_dates=False):
def includefilter(n):
if n.startswith('.'): return False
if n.endswith('.rdump'): return True
if n.endswith('.idump'): return True
return True
def swapsep(n):
return n.replace(':', path.sep)
def mkbasename(n):
base, ext = path.splitext(n)
if ext in ('.rdump', '.idump'):
return base
else:
return n
self.crdate = self.mddate = self.bkdate = date
tmptree = {folder_path: self}
for dirpath, dirnames, filenames in os.walk(folder_path):
dirnames[:] = [swapsep(x) for x in dirnames if includefilter(x)]
filenames[:] = [swapsep(x) for x in filenames if includefilter(x)]
for dn in dirnames:
newdir = Folder()
newdir.crdate = newdir.mddate = newdir.bkdate = date
tmptree[dirpath][dn] = newdir
tmptree[path.join(dirpath, dn)] = newdir
for fn in filenames:
basename = mkbasename(fn)
fullbase = path.join(dirpath, basename)
fullpath = path.join(dirpath, fn)
try:
thefile = tmptree[fullbase]
except KeyError:
thefile = File()
thefile.real_t = 0 # for the MPW hack
thefile.crdate = thefile.mddate = thefile.bkdate = date
thefile.contributors = []
tmptree[fullbase] = thefile
if fn.endswith('.idump'):
with open(fullpath, 'rb') as f:
thefile.type = f.read(4)
thefile.creator = f.read(4)
elif fn.endswith('rdump'):
rez = open(fullpath, 'rb').read()
resources = parse_rez_code(rez)
resfork = make_file(resources, align=4)
thefile.rsrc = resfork
else:
thefile.data = open(fullpath, 'rb').read()
thefile.contributors.append(fullpath)
if mpw_dates:
thefile.real_t = max(thefile.real_t, path.getmtime(fullpath))
tmptree[dirpath][basename] = thefile
for pathtpl, obj in self.iter_paths():
try:
if obj.type == b'TEXT':
obj.data = obj.data.decode('utf8').replace('\r\n', '\r').replace('\n', '\r').encode('mac_roman')
except AttributeError:
pass
if mpw_dates:
all_real_times = set()
for pathtpl, obj in self.iter_paths():
try:
all_real_times.add(obj.real_t)
except AttributeError:
pass
ts2idx = {ts: idx for (idx, ts) in enumerate(sorted(set(all_real_times)))}
for pathtpl, obj in self.iter_paths():
try:
real_t = obj.real_t
except AttributeError:
pass
else:
fake_t = obj.crdate + 60 * ts2idx[real_t]
obj.crdate = obj.mddate = obj.bkdate = fake_t
def write_folder(self, folder_path):
def any_exists(at_path):
if path.exists(at_path): return True
if path.exists(at_path + '.rdump'): return True
if path.exists(at_path + '.idump'): return True
return False
written = []
for p, obj in self.iter_paths():
nativepath = path.join(folder_path, *(comp.replace(path.sep, ':') for comp in p))
if isinstance(obj, Folder):
os.makedirs(nativepath, exist_ok=True)
elif obj.mddate != obj.bkdate or not any_exists(nativepath):
data = obj.data
if obj.type == b'TEXT':
data = data.decode('mac_roman').replace('\r', os.linesep).encode('utf8')
rsrc = obj.rsrc
if rsrc:
rsrc = parse_file(rsrc)
rsrc = make_rez_code(rsrc, ascii_clean=True)
info = obj.type + obj.creator
if info == b'????????': info = b''
for thing, suffix in ((data, ''), (rsrc, '.rdump'), (info, '.idump')):
wholepath = nativepath + suffix
if thing or (suffix == '' and not rsrc):
written.append(wholepath)
with open(written[-1], 'wb') as f:
f.write(thing)
else:
try:
os.remove(wholepath)
except FileNotFoundError:
pass
if written:
t = path.getmtime(written[-1])
for w in written:
os.utime(w, (t, t))