Cleanup code, use latin1 encoding for amiga file names.

This commit is contained in:
2023-10-22 18:59:47 +02:00
parent 0d529baa8b
commit b1c2a496c8
2 changed files with 71 additions and 63 deletions

View File

@@ -14,19 +14,32 @@ on my Amiga. Both reading from and writing into archive was implemented.
Requirements Requirements
------------ ------------
ULha requires `free lha <http://lha.sourceforge.jp>`_ implementation to work. ULha requires free `lha`_ implementation to work. There is another lha
implementation under development - `lhasa`_, although it is currently limited
to list and extract only.
Limitations
-----------
For filenames which contain other characters outside of ASCII set, there is no
way to operate on them as `lha`_ simply ignore those files. Extracting full
archive will extract also those files, although names would be incorrectly
decoded, and hence, unusable on the AmigaOS. Looking forward for the `lhasa`_
to be completed, and then will switch to it.
Installation Installation
------------ ------------
* install `extfslib`_ * install `extfslib`_
* copy ``ulha`` to ``~/.local/share/mc/extfs.d/`` * copy ``ulha`` to ``~/.local/share/mc/extfs.d/``
* add or change entry for files handle in ``~/.config/mc/mc.ext``:: * add or change entry for files handle in ``~/.config/mc/mc.ext``:
# lha .. code::ini
regex/\.[lL]([Hh][aA]|[Zz][hH])$
[lha]
Type=^LHa\ .*archive
Open=%cd %p/ulha:// Open=%cd %p/ulha://
View=%view{ascii} lha l %f View=%view{ascii} /usr/libexec/mc/ext.d/archive.sh view lha
License License
======= =======
@@ -36,3 +49,5 @@ details.
.. _extfslib: https://github.com/gryf/mc_extfslib .. _extfslib: https://github.com/gryf/mc_extfslib
.. _lha: http://lha.sourceforge.jp
.. _lhasa: https://github.com/fragglet/lhasa

101
ulha
View File

@@ -2,19 +2,20 @@
""" """
Lha Virtual filesystem executive for Midnight Commander. Lha Virtual filesystem executive for Midnight Commander.
Tested against python 3.6, lha[1] 1.14 and mc 4.8.22 Tested against python 3.8, lha[1] 1.14 and mc 4.8.29
[1] http://lha.sourceforge.jp [1] http://lha.sourceforge.jp
Changelog: Changelog:
1.4 Cleanup code, use latin1
1.3 Switch to python3 1.3 Switch to python3
1.2 Moved item pattern to extfslib module 1.2 Moved item pattern to extfslib module
1.1 Moved common code into extfslib library 1.1 Moved common code into extfslib library
1.0 Initial release 1.0 Initial release
Author: Roman 'gryf' Dobosz <gryf73@gmail.com> Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
Date: 2019-06-30 Date: 2023-10-22
Version: 1.3 Version: 1.4
Licence: BSD Licence: BSD
""" """
import os import os
@@ -30,37 +31,39 @@ import extfslib
class ULha(extfslib.Archive): class ULha(extfslib.Archive):
"""Archive handle. Provides interface to MC's extfs subsystem""" """Archive handle. Provides interface to MC's extfs subsystem"""
LINE_PAT = re.compile(b"^((?P<perms>[d-][rswx-]{9})|(\[generic\])|" LINE_PAT = re.compile(r"^((?P<perms>[d-][rswx-]{9})|(\[generic\])|"
b"(\[unknown\]))" r"(\[unknown\]))"
b"((\s+\d+/\d+\s+)|(\s+))" r"((\s+\d+/\d+\s+)|(\s+))"
b"(?P<uid>)(?P<gid>)" # just for the record r"(?P<uid>)(?P<gid>)" # just for the record
b"(?P<size>\d+)" r"(?P<size>\d+)"
b"\s+(\*{6}|\d+\.\d%)" r"\s+(\*{6}|\d+\.\d%)"
b"\s(?P<month>[JFMASOND][a-z]{2})\s+" # month r"\s(?P<month>[JFMASOND][a-z]{2})\s+" # month
b"(?P<day>\d+)\s+" # day r"(?P<day>\d+)\s+" # day
b"(?P<yh>\d{4}|(\d{2}:\d{2}))" # year/hour r"(?P<yh>\d{4}|(\d{2}:\d{2}))" # year/hour
b"\s(?P<fpath>.*)") r"\s(?P<fpath>.*)")
ARCHIVER = b"lha" ARCHIVER = "lha"
CMDS = {"list": b"lq", CMDS = {"list": "lq",
"read": b"pq", "read": "pq",
"write": b"aq", "write": "aq",
"delete": b"dq"} "delete": "dq"}
DATETIME = b"%(month)s %(day)s %(yh)s" DATETIME = "%(month)s %(day)s %(yh)s"
def _get_dir(self): def _get_dir(self):
"""Prepare archive file listing""" """Prepare archive file listing"""
contents = [] contents = []
out = self._call_command("list") out = subprocess.run([self.ARCHIVER, self.CMDS['list'], self._arch],
if not out: capture_output=True, encoding="Latin1")
if out.returncode != 0:
sys.stderr.write(out.stderr)
return return
for line in out.split(b"\n"): for line in out.stdout.split("\n"):
# -lhd- can store empty directories # -lhd- can store empty directories
perms = b"-rw-r--r--" perms = "-rw-r--r--"
if line.endswith(bytes(os.path.sep, 'utf-8')): if line.endswith(os.path.sep):
line = line[:-1] line = line[:-1]
perms = b"drw-r--r--" perms = "drw-r--r--"
match = self.LINE_PAT.match(line) match = self.LINE_PAT.match(line)
if not match: if not match:
@@ -69,21 +72,21 @@ class ULha(extfslib.Archive):
match_entry = match.groupdict() match_entry = match.groupdict()
entry = {} entry = {}
for key in match_entry: for key in match_entry:
entry[bytes(key, 'utf-8')] = match_entry[key] entry[key] = match_entry[key]
del match_entry del match_entry
# UID and GID sometimes can have strange values depending on # UID and GID sometimes can have strange values depending on
# the information that was written into archive. Most of the # the information that was written into archive. Most of the
# times I was dealing with Amiga lha archives, so that i don't # times I was dealing with Amiga lha archives, so that i don't
# really care about real user/group # really care about real user/group
entry[b'uid'] = bytes(str(self._uid), 'utf-8') entry['uid'] = str(self._uid)
entry[b'gid'] = bytes(str(self._gid), 'utf-8') entry['gid'] = str(self._gid)
entry[b'datetime'] = self.DATETIME % entry entry['datetime'] = self.DATETIME % entry
if not entry[b'perms']: if not entry['perms']:
entry[b'perms'] = perms entry['perms'] = perms
entry[b'display_name'] = self._map_name(entry[b'fpath']) entry['display_name'] = self._map_name(entry['fpath'])
contents.append(entry) contents.append(entry)
return contents return contents
@@ -91,7 +94,7 @@ class ULha(extfslib.Archive):
def list(self): def list(self):
"""Output contents of the archive to stdout""" """Output contents of the archive to stdout"""
for entry in self._contents: for entry in self._contents:
sys.stdout.buffer.write(self.ITEM % entry) sys.stdout.write(self.ITEM.decode('utf-8') % entry)
return 0 return 0
def rm(self, dst): def rm(self, dst):
@@ -107,12 +110,12 @@ class ULha(extfslib.Archive):
"""Remove empty directory""" """Remove empty directory"""
dst = self._get_real_name(dst) dst = self._get_real_name(dst)
if not dst.endswith(bytes(os.path.sep, 'utf-8')): if not dst.endswith(os.path.sep):
dst += bytes(os.path.sep, 'utf-8') dst += os.path.sep
if self._call_command('delete', dst=dst) is None: res = subprocess.run([self.ARCHIVER, self.CMDS['delete'], dst],
return 1 capture_output=True)
return 0 return res.returncode
def run(self, dst): def run(self, dst):
"""Execute file out of archive""" """Execute file out of archive"""
@@ -153,29 +156,19 @@ class ULha(extfslib.Archive):
else: else:
os.makedirs(dst) os.makedirs(dst)
try: res = subprocess.run([self.ARCHIVER, self.CMDS["write"], arch_abspath,
result = subprocess.check_call([self.ARCHIVER.decode('utf-8'), dst], capture_output=True, encoding='utf-8')
self.CMDS["write"].decode('utf-8'),
arch_abspath, dst])
except subprocess.CalledProcessError:
return 1
finally:
os.chdir(current_dir) os.chdir(current_dir)
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
return result return res.returncode
def copyout(self, src, dst): def copyout(self, src, dst):
"""Copy file out form archive.""" """Copy file out form archive."""
src = self._get_real_name(src) src = self._get_real_name(src)
fobj = open(dst, "wb") with open(dst, "wb") as fobj:
try: res = subprocess.run([self.ARCHIVER, self.CMDS['read'], self._arch,
result = subprocess.check_call([self.ARCHIVER, self.CMDS['read'], self._arch,
src], stdout=fobj) src], stdout=fobj)
except subprocess.CalledProcessError: return res.returncode
return 1
finally:
fobj.close()
return result
if __name__ == "__main__": if __name__ == "__main__":