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`_
* 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
regex/\.[lL][zZ][xX]$
Open=%cd %p/ulzx://
View=%view{ascii} unlzx -v %f
.. code:: ini
[lzx]
Regex=\.lzx$
RegexIgnoreCase=true
Open=%cd %p/ulzx://
View=%view{ascii} unlzx -v %f
License
=======

121
ulzx
View File

@@ -3,51 +3,52 @@
Read only, Amiga LZX[1] archiver Virtual filesystem executive for Midnight
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
Changelog:
1.3 Code cleanup
1.2 Use python3
1.1 Moved common code into extfslib library
1.0 Initial release
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
Date: 2019-06-30
Version: 1.2
Date: 2023-10-20
Version: 1.3
Licence: BSD
"""
import os
import sys
import re
import shutil
from subprocess import call, CalledProcessError
from tempfile import mkdtemp, mkstemp
import subprocess
import sys
import tempfile
from extfslib import Archive, parse_args
import extfslib
class ULzx(Archive):
"""Archive handle. Provides interface to MC's extfs subsystem"""
LINE_PAT = re.compile(b"^\s+(?P<size>\d+)\s+"
b"((n/a)|\d+)\s"
b"(?P<time>\d{2}:\d{2}:\d{2})\s+"
b"(?P<date>\d+-[a-z]{3}-\d{4})\s"
b"(?P<perms>[h-][s-][p-][a-][r-][w-][e-][d-])\s"
b"\"(?P<fpath>.*)\"")
ARCHIVER = b"unlzx"
CMDS = {"list": b"-v",
"read": b"-x"}
DATETIME = b"%02d-%02d-%s %02d:%02d"
class ULzx(extfslib.Archive):
"""LZX archive handle. Provides interface to MC's extfs subsystem"""
LINE_PAT = re.compile(r"^\s+(?P<size>\d+)\s+"
r"((n/a)|\d+)\s"
r"(?P<time>\d{2}:\d{2}:\d{2})\s+"
r"(?P<date>\d+-[a-z]{3}-\d{4})\s"
r"(?P<perms>[h-][s-][p-][a-][r-][w-][e-][d-])\s"
r"\"(?P<fpath>.*)\"")
ARCHIVER = "unlzx"
CMDS = {"list": "-v",
"read": "-x"}
DATETIME = "%02d-%02d-%s %02d:%02d"
def _get_date(self, time, date):
"""Return MM-DD-YYYY hh:mm formatted date out of time and date
strings"""
month_list = [b"jan", b"feb", b"mar", b"apr", b"may", b"jun", b"jul",
b"aug", b"sep", b"oct", b"nov", b"dec"]
day, month, year = date.split(b"-")
month_list = ["jan", "feb", "mar", "apr", "may", "jun", "jul",
"aug", "sep", "oct", "nov", "dec"]
day, month, year = date.split("-")
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),
int(minutes))
@@ -55,11 +56,12 @@ class ULzx(Archive):
"""Prepare archive file listing"""
contents = []
out = self._call_command("list")
if not out:
return
out = subprocess.run([self.ARCHIVER, self.CMDS['list'], self._arch],
capture_output=True, encoding="latin-1")
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)
if not match:
continue
@@ -67,14 +69,14 @@ class ULzx(Archive):
match_entry = match.groupdict()
entry = {}
for key in match_entry:
entry[bytes(key, 'utf-8')] = match_entry[key]
entry[key] = match_entry[key]
del match_entry
entry[b'datetime'] = self._get_date(entry[b'time'], entry[b'date'])
entry[b'display_name'] = self._map_name(entry[b'fpath'])
entry[b'perms'] = b"-rw-r--r--" # lzx doesn't store empty dirs
entry[b'uid'] = bytes(str(self._uid), 'utf-8')
entry[b'gid'] = bytes(str(self._gid), 'utf-8')
entry['datetime'] = self._get_date(entry['time'], entry['date'])
entry['display_name'] = self._map_name(entry['fpath'])
entry['perms'] = "-rw-r--r--" # lzx doesn't store empty dirs
entry['uid'] = str(self._uid)
entry['gid'] = str(self._gid)
contents.append(entry)
return contents
@@ -82,12 +84,12 @@ class ULzx(Archive):
def list(self):
"""Output contents of the archive to stdout"""
for entry in self._contents:
sys.stdout.buffer.write(self.ITEM % entry)
sys.stdout.write(self.ITEM.decode('utf-8') % entry)
return 0
def run(self, dst):
"""Execute file out of archive"""
fdesc, tmp_file = mkstemp()
fdesc, tmp_file = tempfile.mkstemp()
os.close(fdesc)
result = 0
@@ -96,40 +98,41 @@ class ULzx(Archive):
os.chmod(tmp_file, int("700", 8))
try:
result = call([tmp_file])
finally:
try:
os.unlink(tmp_file)
except OSError:
pass
with open(os.devnull, "w") as fnull:
result = subprocess.run([tmp_file], stderr=fnull)
os.unlink(tmp_file)
return result
return result.returncode
def copyout(self, src, dst):
"""Unfortunately, to copy one file out entire LZX archive have to be
extracted. For small archives is not a problem, but in relatively big
one it could be a performance issue."""
tmp_dir = mkdtemp()
src = self._get_real_name(src)
tmp_dir = tempfile.mkdtemp()
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)
os.chdir(tmp_dir)
try:
with open(os.devnull, "w") as fnull:
result = call([self.ARCHIVER, self.CMDS['read'],
os.path.join(current_dir, self._arch)],
stdout=fnull, stderr=fnull)
if result == 0:
shutil.copy2(src, dst)
except CalledProcessError:
return 1
finally:
shutil.rmtree(tmp_dir)
os.chdir(current_dir)
with open(os.devnull, "w") as fnull:
result = subprocess.run([self.ARCHIVER, self.CMDS['read'],
os.path.join(current_dir, self._arch)],
stdout=fnull, stderr=fnull)
if result.returncode == 0:
# use subprocess, as shutil.copy2 will complain about mixing
# strings with bytes
subprocess.run(['cp', src, dst])
return result
shutil.rmtree(tmp_dir)
os.chdir(current_dir)
return result.returncode
if __name__ == "__main__":
sys.exit(parse_args(ULzx))
sys.exit(extfslib.parse_args(ULzx))