Compare commits
7 Commits
7105a98785
...
1a6c6af759
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a6c6af759 | |||
| cb5b03aa20 | |||
| c78dbacd0e | |||
| c33655a12c | |||
| 08a04c3862 | |||
| ed85f32837 | |||
| 09b2349152 |
74
README.rst
74
README.rst
@@ -18,64 +18,50 @@ In case of corrupted or no-dos images, message will be shown.
|
||||
Requirements
|
||||
------------
|
||||
|
||||
It requires ``unadf`` utility from `ADFlib <https://github.com/lclevy/ADFlib>`_
|
||||
repository.
|
||||
This script is using ``unadf`` v1.2 utility from `ADFlib
|
||||
<https://github.com/lclevy/ADFlib>`_ package in version 0.8. Version of unadf
|
||||
can be check by simply issuing unadf without arguments:
|
||||
|
||||
If it turns out that your distribution doesn't provide proper version of ADFlib,
|
||||
there will be a need for building it by hand.
|
||||
.. code:: shell-session
|
||||
|
||||
$ unadf
|
||||
|
||||
If it turns out that your distribution doesn't provide proper version of
|
||||
ADFlib, there will be a need for building it by hand.
|
||||
|
||||
It may be done by using following steps:
|
||||
|
||||
#. Grab the `sources
|
||||
<http://http.debian.net/debian/pool/main/u/unadf/unadf_0.7.11a.orig.tar.gz>`_
|
||||
and `patches
|
||||
<http://http.debian.net/debian/pool/main/u/unadf/unadf_0.7.11a-3.debian.tar.gz>`_
|
||||
from `Debian repository <http://packages.debian.org/sid/unadf>`_.
|
||||
#. Extract ``unadf_0.7.11a-3.debian.tar.gz`` and ``unadf_0.7.11a.orig.tar.gz``
|
||||
into some temporary directory::
|
||||
#. Grab the `sources <https://github.com/lclevy/ADFlib>`_
|
||||
#. Build and install it, using instructions from `INSTALL
|
||||
<https://github.com/lclevy/ADFlib/blob/master/INSTALL>`_ file.
|
||||
|
||||
$ mkdir temp
|
||||
$ cd temp
|
||||
$ tar zxf ~/Downloads/unadf_0.7.11a-3.debian.tar.gz
|
||||
$ tar zxf ~/Downloads/unadf_0.7.11a.orig.tar.gz
|
||||
$ cd unadf-0.7.11a
|
||||
|
||||
#. Apply Debian patches::
|
||||
|
||||
$ for i in `cat ../debian/patches/series`; do
|
||||
> patch -Np1 < "../debian/patches/${i}"
|
||||
> done
|
||||
|
||||
#. Apply the patch from extras directory::
|
||||
|
||||
$ patch -Np1 < [path_to_this_repo]/extras/unadf_separate_comment.patch
|
||||
$ make
|
||||
$ cp Demo/unadf [destination_path]
|
||||
|
||||
#. Place ``unadf`` binary under directory reachable by ``$PATH``.
|
||||
|
||||
For optional dms support, `xdms <http://zakalwe.fi/~shd/foss/xdms/>`_ utility is
|
||||
needed.
|
||||
For optional dms support, `xdms <http://zakalwe.fi/~shd/foss/xdms/>`_ utility
|
||||
is needed.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
* install `extfslib`_
|
||||
* copy ``uadf`` 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.ini``:
|
||||
|
||||
# adf
|
||||
type/^Amiga\ .* disk
|
||||
Open=%cd %p/uadf://
|
||||
View=%view{ascii} unadf -lr %f
|
||||
.. code:: ini
|
||||
|
||||
# adz
|
||||
regex/\.([aA][dD][zZ])$
|
||||
Open=%cd %p/uadf://
|
||||
[adf]
|
||||
Type=^Amiga\ .* disk
|
||||
Open=%cd %p/uadf://
|
||||
View=%view{ascii} unadf -lrm %f 2>/dev/null
|
||||
|
||||
[adz]
|
||||
Regex=\.adz$
|
||||
View=%view{ascii} t=$(mktemp --suffix .adf); zcat %f > ${t}; unadf -lrm ${t} 2</dev/null; rm ${t}
|
||||
Open=%cd %p/uadf://
|
||||
|
||||
[dms]
|
||||
Regex=\.dms$
|
||||
View=%view{ascii} t=$(mktemp --suffix .adf); xdms u %f "+${t}" 2>/dev/null; unadf -lrm ${t} 2</dev/null; rm ${t}
|
||||
Open=%cd %p/uadf://
|
||||
|
||||
# dms
|
||||
regex/\.([dD][mM][sS])$
|
||||
Open=%cd %p/uadf://
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
141
uadf
141
uadf
@@ -20,43 +20,48 @@ The patched sources are available from: https://github.com/lclevy/ADFlib
|
||||
It also requires xdms utility, for optional dms support.
|
||||
|
||||
Changelog:
|
||||
1.4 Adapt to unadf 1.2 and use Latin1 as default encoding
|
||||
1.3 Switch to Python3
|
||||
1.2 Added failsafe for filenames in archive with spaces and nodos message.
|
||||
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.3
|
||||
Date: 2023-10-19
|
||||
Version: 1.4
|
||||
Licence: BSD
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import gzip
|
||||
from subprocess import check_output, check_call, CalledProcessError
|
||||
from tempfile import mkstemp, mkdtemp
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from extfslib import Archive, parse_args
|
||||
import extfslib
|
||||
|
||||
|
||||
class UAdf(Archive):
|
||||
BANNER_PAT = re.compile(r'unADF v\d.\d : a unzip like for .ADF files, '
|
||||
r'powered by ADFlib (.*)\n\n')
|
||||
|
||||
|
||||
class UAdf(extfslib.Archive):
|
||||
"""
|
||||
Class for interact with c1541 program and MC
|
||||
Class for interact with ADF/DMS images and MC
|
||||
"""
|
||||
LINE_PAT = re.compile(b'\s*(?P<size>\d+)?'
|
||||
b'\s{2}(?P<date>\d{4}/\d{2}/\d{2})'
|
||||
b'\s{2}\s?(?P<time>\d+:\d{2}:\d{2})'
|
||||
b'\s{2}(?P<fpath>.*)')
|
||||
ARCHIVER = b"unadf"
|
||||
DMS = b"xdms"
|
||||
CMDS = {"list": b"-lr",
|
||||
"read": b"r",
|
||||
"write": b"w",
|
||||
"delete": b"d"}
|
||||
DATETIME = b"%s-%s-%s %02d:%s"
|
||||
LINE_PAT = re.compile(r'\s*(?P<size>\d+)?'
|
||||
r'\s{2}(?P<date>\d{4}/\d{2}/\d{2})'
|
||||
r'\s{2}\s?(?P<time>\d+:\d{2}:\d{2})'
|
||||
r'\s{2}(?P<fpath>.*)')
|
||||
ARCHIVER = "unadf"
|
||||
DMS = "xdms"
|
||||
CMDS = {"list": "-lr",
|
||||
"read": "r",
|
||||
"write": "w",
|
||||
"delete": "d"}
|
||||
DATETIME = "%s-%s-%s %02d:%s"
|
||||
|
||||
def __init__(self, fname):
|
||||
"""Prepare archive content for operations"""
|
||||
@@ -81,14 +86,14 @@ class UAdf(Archive):
|
||||
|
||||
def _parse_dt(self, date, time):
|
||||
"""Return parsed datetime which fulfill extfs standards date."""
|
||||
year, month, day = date.split(b"/")
|
||||
hours, minutes, _unused = time.split(b":")
|
||||
year, month, day = date.split("/")
|
||||
hours, minutes, _unused = time.split(":")
|
||||
return self.DATETIME % (month, day, year, int(hours), minutes)
|
||||
|
||||
def _ungzip(self):
|
||||
"""Create temporary file for ungzipped adf file since unadf does not
|
||||
accept gzipped content in any way including reading from stdin."""
|
||||
fdesc, tmp_fname = mkstemp(suffix=".adf")
|
||||
fdesc, tmp_fname = tempfile.mkstemp(suffix=".adf")
|
||||
os.close(fdesc)
|
||||
|
||||
with gzip.open(self._arch) as gobj:
|
||||
@@ -99,27 +104,42 @@ class UAdf(Archive):
|
||||
|
||||
def _undms(self):
|
||||
"""Create temporary adf file extracted from dms."""
|
||||
fdesc, tmp_fname = mkstemp(suffix=".adf")
|
||||
fdesc, tmp_fname = tempfile.mkstemp(suffix=".adf")
|
||||
os.close(fdesc)
|
||||
|
||||
try:
|
||||
check_call([self.DMS, b'-q', b'u', self._arch, "+" + tmp_fname])
|
||||
result = subprocess.run([self.DMS, '-q', 'u', self._arch,
|
||||
"+" + tmp_fname])
|
||||
if result.returncode == 0:
|
||||
self._arch = tmp_fname
|
||||
self._clean = False
|
||||
except (CalledProcessError, OSError):
|
||||
pass
|
||||
|
||||
def _parse_banner(self, string):
|
||||
match = BANNER_PAT.match(string)
|
||||
if not match:
|
||||
return
|
||||
|
||||
if match.end() == len(string):
|
||||
return
|
||||
|
||||
return string[match.end():]
|
||||
|
||||
def _map_name(self, name):
|
||||
if name.startswith(" "):
|
||||
new_name = "".join(["~", name[1:]])
|
||||
return new_name
|
||||
return name
|
||||
|
||||
def _get_dir(self):
|
||||
"""Retrieve directory"""
|
||||
contents = []
|
||||
with open(os.devnull, "w") as fnull:
|
||||
try:
|
||||
out = check_output([self.ARCHIVER, self.CMDS['list'],
|
||||
self._arch], stderr=fnull)
|
||||
except CalledProcessError:
|
||||
return contents
|
||||
out = subprocess.run([self.ARCHIVER, self.CMDS['list'], self._arch],
|
||||
capture_output=True, encoding="latin-1")
|
||||
|
||||
for line in out.split(b"\n"):
|
||||
error_msg = self._parse_banner(out.stderr)
|
||||
if error_msg:
|
||||
sys.stderr.write(error_msg)
|
||||
|
||||
for line in out.stdout.split("\n"):
|
||||
match = self.LINE_PAT.match(line)
|
||||
if not match:
|
||||
continue
|
||||
@@ -127,17 +147,17 @@ class UAdf(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'perms'] = b"-rw-r--r--"
|
||||
if not entry[b'size']:
|
||||
entry[b'perms'] = b"drwxr-xr-x"
|
||||
entry[b'size'] = b"0"
|
||||
entry[b'display_name'] = self._map_name(entry[b'fpath'])
|
||||
entry[b'datetime'] = self._parse_dt(entry[b'date'], entry[b'time'])
|
||||
entry[b'uid'] = bytes(str(self._uid), 'utf-8')
|
||||
entry[b'gid'] = bytes(str(self._gid), 'utf-8')
|
||||
entry['perms'] = "-rw-r--r--"
|
||||
if not entry['size']:
|
||||
entry['perms'] = "drwxr-xr-x"
|
||||
entry['size'] = "0"
|
||||
entry['display_name'] = self._map_name(entry['fpath'])
|
||||
entry['datetime'] = self._parse_dt(entry['date'], entry['time'])
|
||||
entry['uid'] = str(self._uid)
|
||||
entry['gid'] = str(self._gid)
|
||||
contents.append(entry)
|
||||
|
||||
return contents
|
||||
@@ -149,39 +169,46 @@ class UAdf(Archive):
|
||||
Add suffix to show user what kind of file do he dealing with.
|
||||
"""
|
||||
if not self._contents:
|
||||
sys.stderr.write("Nodos or archive error\n")
|
||||
sys.stderr.write("Nodos or image error\n")
|
||||
return 1
|
||||
|
||||
for entry in self._contents:
|
||||
sys.stdout.buffer.write(self.ITEM % entry)
|
||||
print(self.ITEM.decode('utf-8')[:-1] % entry)
|
||||
return 0
|
||||
|
||||
def copyout(self, src, dst):
|
||||
"""Copy file form the adf image."""
|
||||
real_src = self._get_real_name(src)
|
||||
real_src = [e['display_name'] for e in self._contents
|
||||
if e['display_name'] == src]
|
||||
|
||||
if not real_src:
|
||||
raise IOError("No such file or directory")
|
||||
|
||||
real_src = real_src[0].encode('latin-1')
|
||||
|
||||
if b" " in real_src:
|
||||
sys.stderr.write("unadf is unable to operate on filepath with "
|
||||
"space inside.\nUse affs to mount image and than"
|
||||
" extract desired files.\n")
|
||||
return 1
|
||||
|
||||
extract_dir = mkdtemp()
|
||||
cmd = [self.ARCHIVER, self._arch, real_src, b"-d", extract_dir]
|
||||
if check_call(cmd, stdout=open(os.devnull, 'wb'),
|
||||
stderr=open(os.devnull, 'wb')) != 0:
|
||||
extract_dir = tempfile.mkdtemp()
|
||||
cmd = [self.ARCHIVER, "-d", extract_dir, self._arch, real_src]
|
||||
result = subprocess.run(cmd, capture_output=True)
|
||||
|
||||
error_msg = self._parse_banner(result.stderr.decode('utf-8'))
|
||||
if error_msg:
|
||||
sys.stderr.write("unadf returned with error:\n")
|
||||
sys.stderr.write(error_msg)
|
||||
shutil.rmtree(extract_dir)
|
||||
sys.stderr.write("unadf returned with nonzero exit code\n")
|
||||
return 1
|
||||
|
||||
shutil.move(os.path.join(bytes(extract_dir, "utf8"), real_src),
|
||||
bytes(dst, "utf8"))
|
||||
# use subprocess, as shutil will crash on binary encoded filenames
|
||||
subprocess.run([b'mv', os.path.join(extract_dir.encode("latin-1"),
|
||||
real_src), dst.encode('latin-1')])
|
||||
shutil.rmtree(extract_dir)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(parse_args(UAdf))
|
||||
sys.exit(extfslib.parse_args(UAdf))
|
||||
|
||||
Reference in New Issue
Block a user