Compare commits

5 Commits

Author SHA1 Message Date
81e444809d Fix February dict key. 2026-01-21 07:56:06 +01:00
892583d4d3 Removed _map_name method. 2023-10-23 18:16:22 +02:00
a212514bbf Use latin1 encoding as a default for Amiga file names. 2023-10-21 09:49:33 +02:00
158bc83a14 Clean up code. 2023-10-20 18:37:43 +02:00
103714e9c6 Fix the strings/bytes mess with shutil 2023-10-20 18:36:47 +02:00
2 changed files with 70 additions and 64 deletions

View File

@@ -29,12 +29,15 @@ Installation
* install `extfslib`_ * install `extfslib`_
* copy ``ulzx`` to ``~/.local/share/mc/extfs.d/`` * copy ``ulzx`` 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``:
# lzx .. code:: ini
regex/\.[lL][zZ][xX]$
Open=%cd %p/ulzx:// [lzx]
View=%view{ascii} unlzx -v %f Regex=\.lzx$
RegexIgnoreCase=true
Open=%cd %p/ulzx://
View=%view{ascii} unlzx -v %f
License License
======= =======

121
ulzx
View File

@@ -3,51 +3,52 @@
Read only, Amiga LZX[1] archiver Virtual filesystem executive for Midnight Read only, Amiga LZX[1] archiver Virtual filesystem executive for Midnight
Commander. Commander.
Tested against python 3.6, unlzx[1] 1.1 and mc 4.8.22 Tested against python 3.8, unlzx[1] 1.1 and mc 4.8.22
[1] ftp://us.aminet.net/pub/aminet/misc/unix/unlzx.c.gz.readme [1] ftp://us.aminet.net/pub/aminet/misc/unix/unlzx.c.gz.readme
Changelog: Changelog:
1.3 Code cleanup
1.2 Use python3 1.2 Use python3
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-20
Version: 1.2 Version: 1.3
Licence: BSD Licence: BSD
""" """
import os import os
import sys
import re import re
import shutil import shutil
from subprocess import call, CalledProcessError import subprocess
from tempfile import mkdtemp, mkstemp import sys
import tempfile
from extfslib import Archive, parse_args import extfslib
class ULzx(Archive): class ULzx(extfslib.Archive):
"""Archive handle. Provides interface to MC's extfs subsystem""" """LZX archive handle. Provides interface to MC's extfs subsystem"""
LINE_PAT = re.compile(b"^\s+(?P<size>\d+)\s+" LINE_PAT = re.compile(r"^\s+(?P<size>\d+)\s+"
b"((n/a)|\d+)\s" r"((n/a)|\d+)\s"
b"(?P<time>\d{2}:\d{2}:\d{2})\s+" r"(?P<time>\d{2}:\d{2}:\d{2})\s+"
b"(?P<date>\d+-[a-z]{3}-\d{4})\s" r"(?P<date>\d+-[a-z]{3}-\d{4})\s"
b"(?P<perms>[h-][s-][p-][a-][r-][w-][e-][d-])\s" r"(?P<perms>[h-][s-][p-][a-][r-][w-][e-][d-])\s"
b"\"(?P<fpath>.*)\"") r"\"(?P<fpath>.*)\"")
ARCHIVER = b"unlzx" ARCHIVER = "unlzx"
CMDS = {"list": b"-v", CMDS = {"list": "-v",
"read": b"-x"} "read": "-x"}
DATETIME = b"%02d-%02d-%s %02d:%02d" DATETIME = "%02d-%02d-%s %02d:%02d"
def _get_date(self, time, date): def _get_date(self, time, date):
"""Return MM-DD-YYYY hh:mm formatted date out of time and date """Return MM-DD-YYYY hh:mm formatted date out of time and date
strings""" strings"""
month_list = [b"jan", b"feb", b"mar", b"apr", b"may", b"jun", b"jul", month_list = ["jan", "feb", "mar", "apr", "may", "jun", "jul",
b"aug", b"sep", b"oct", b"nov", b"dec"] "aug", "sep", "oct", "nov", "dec"]
day, month, year = date.split(b"-") day, month, year = date.split("-")
month = month_list.index(month) + 1 month = month_list.index(month) + 1
hours, minutes, dummy = time.split(b":") hours, minutes, dummy = time.split(":")
return self.DATETIME % (month, int(day), year, int(hours), return self.DATETIME % (month, int(day), year, int(hours),
int(minutes)) int(minutes))
@@ -55,11 +56,12 @@ class ULzx(Archive):
"""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="latin-1")
return if out.stderr:
sys.stderr.write(out.stderr)
for line in out.split(b"\n"): for line in out.stdout.split("\n"):
match = self.LINE_PAT.match(line) match = self.LINE_PAT.match(line)
if not match: if not match:
continue continue
@@ -67,14 +69,14 @@ class ULzx(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
entry[b'datetime'] = self._get_date(entry[b'time'], entry[b'date']) entry['datetime'] = self._get_date(entry['time'], entry['date'])
entry[b'display_name'] = self._map_name(entry[b'fpath']) entry['display_name'] = self._map_name(entry['fpath'])
entry[b'perms'] = b"-rw-r--r--" # lzx doesn't store empty dirs entry['perms'] = "-rw-r--r--" # lzx doesn't store empty dirs
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)
contents.append(entry) contents.append(entry)
return contents return contents
@@ -82,12 +84,12 @@ class ULzx(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 run(self, dst): def run(self, dst):
"""Execute file out of archive""" """Execute file out of archive"""
fdesc, tmp_file = mkstemp() fdesc, tmp_file = tempfile.mkstemp()
os.close(fdesc) os.close(fdesc)
result = 0 result = 0
@@ -96,40 +98,41 @@ class ULzx(Archive):
os.chmod(tmp_file, int("700", 8)) os.chmod(tmp_file, int("700", 8))
try: with open(os.devnull, "w") as fnull:
result = call([tmp_file]) result = subprocess.run([tmp_file], stderr=fnull)
finally: os.unlink(tmp_file)
try:
os.unlink(tmp_file)
except OSError:
pass
return result return result.returncode
def copyout(self, src, dst): def copyout(self, src, dst):
"""Unfortunately, to copy one file out entire LZX archive have to be """Unfortunately, to copy one file out entire LZX archive have to be
extracted. For small archives is not a problem, but in relatively big extracted. For small archives is not a problem, but in relatively big
one it could be a performance issue.""" one it could be a performance issue."""
tmp_dir = mkdtemp() tmp_dir = tempfile.mkdtemp()
src = self._get_real_name(src) src = [e['display_name'] for e in self._contents
if e['display_name'] == src]
if not src:
raise IOError("No such file or directory")
src = src[0].encode('latin-1')
current_dir = os.path.abspath(os.curdir) current_dir = os.path.abspath(os.curdir)
os.chdir(tmp_dir) os.chdir(tmp_dir)
try: with open(os.devnull, "w") as fnull:
with open(os.devnull, "w") as fnull: result = subprocess.run([self.ARCHIVER, self.CMDS['read'],
result = call([self.ARCHIVER, self.CMDS['read'], os.path.join(current_dir, self._arch)],
os.path.join(current_dir, self._arch)], stdout=fnull, stderr=fnull)
stdout=fnull, stderr=fnull) if result.returncode == 0:
if result == 0: # use subprocess, as shutil.copy2 will complain about mixing
shutil.copy2(src, dst) # strings with bytes
except CalledProcessError: subprocess.run(['cp', src, dst])
return 1
finally:
shutil.rmtree(tmp_dir)
os.chdir(current_dir)
return result shutil.rmtree(tmp_dir)
os.chdir(current_dir)
return result.returncode
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(parse_args(ULzx)) sys.exit(extfslib.parse_args(ULzx))