Compare commits
8 Commits
7105a98785
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b28a39f50 | |||
| 1a6c6af759 | |||
| cb5b03aa20 | |||
| c78dbacd0e | |||
| c33655a12c | |||
| 08a04c3862 | |||
| ed85f32837 | |||
| 09b2349152 |
68
README.rst
68
README.rst
@@ -18,65 +18,51 @@ In case of corrupted or no-dos images, message will be shown.
|
|||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
It requires ``unadf`` utility from `ADFlib <https://github.com/lclevy/ADFlib>`_
|
This script is using ``unadf`` v1.2 utility from `ADFlib
|
||||||
repository.
|
<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,
|
.. code:: shell-session
|
||||||
there will be a need for building it by hand.
|
|
||||||
|
$ 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:
|
It may be done by using following steps:
|
||||||
|
|
||||||
#. Grab the `sources
|
#. Grab the `sources <https://github.com/lclevy/ADFlib>`_
|
||||||
<http://http.debian.net/debian/pool/main/u/unadf/unadf_0.7.11a.orig.tar.gz>`_
|
#. Build and install it, using instructions from `INSTALL
|
||||||
and `patches
|
<https://github.com/lclevy/ADFlib/blob/master/INSTALL>`_ file.
|
||||||
<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::
|
|
||||||
|
|
||||||
$ mkdir temp
|
For optional dms support, `xdms <http://zakalwe.fi/~shd/foss/xdms/>`_ utility
|
||||||
$ cd temp
|
is needed.
|
||||||
$ 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.
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* install `extfslib`_
|
* install `extfslib`_
|
||||||
* copy ``uadf`` to ``~/.local/share/mc/extfs.d/``
|
* 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
|
.. code:: ini
|
||||||
type/^Amiga\ .* disk
|
|
||||||
|
[adf]
|
||||||
|
Type=^Amiga\ .* disk
|
||||||
Open=%cd %p/uadf://
|
Open=%cd %p/uadf://
|
||||||
View=%view{ascii} unadf -lr %f
|
View=%view{ascii} unadf -lrm %f 2>/dev/null
|
||||||
|
|
||||||
# adz
|
[adz]
|
||||||
regex/\.([aA][dD][zZ])$
|
Regex=\.adz$
|
||||||
|
View=%view{ascii} t=$(mktemp --suffix .adf); zcat %f > ${t}; unadf -lrm ${t} 2</dev/null; rm ${t}
|
||||||
Open=%cd %p/uadf://
|
Open=%cd %p/uadf://
|
||||||
|
|
||||||
# dms
|
[dms]
|
||||||
regex/\.([dD][mM][sS])$
|
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://
|
Open=%cd %p/uadf://
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
135
uadf
135
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.
|
It also requires xdms utility, for optional dms support.
|
||||||
|
|
||||||
Changelog:
|
Changelog:
|
||||||
|
1.4 Adapt to unadf 1.2 and use Latin1 as default encoding
|
||||||
1.3 Switch to Python3
|
1.3 Switch to Python3
|
||||||
1.2 Added failsafe for filenames in archive with spaces and nodos message.
|
1.2 Added failsafe for filenames in archive with spaces and nodos message.
|
||||||
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-19
|
||||||
Version: 1.3
|
Version: 1.4
|
||||||
Licence: BSD
|
Licence: BSD
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import gzip
|
import gzip
|
||||||
from subprocess import check_output, check_call, CalledProcessError
|
import os
|
||||||
from tempfile import mkstemp, mkdtemp
|
import re
|
||||||
import shutil
|
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+)?'
|
LINE_PAT = re.compile(r'\s*(?P<size>\d+)?'
|
||||||
b'\s{2}(?P<date>\d{4}/\d{2}/\d{2})'
|
r'\s{2}(?P<date>\d{4}/\d{2}/\d{2})'
|
||||||
b'\s{2}\s?(?P<time>\d+:\d{2}:\d{2})'
|
r'\s{2}\s?(?P<time>\d+:\d{2}:\d{2})'
|
||||||
b'\s{2}(?P<fpath>.*)')
|
r'\s{2}(?P<fpath>.*)')
|
||||||
ARCHIVER = b"unadf"
|
ARCHIVER = "unadf"
|
||||||
DMS = b"xdms"
|
DMS = "xdms"
|
||||||
CMDS = {"list": b"-lr",
|
CMDS = {"list": "-lr",
|
||||||
"read": b"r",
|
"read": "r",
|
||||||
"write": b"w",
|
"write": "w",
|
||||||
"delete": b"d"}
|
"delete": "d"}
|
||||||
DATETIME = b"%s-%s-%s %02d:%s"
|
DATETIME = "%s-%s-%s %02d:%s"
|
||||||
|
|
||||||
def __init__(self, fname):
|
def __init__(self, fname):
|
||||||
"""Prepare archive content for operations"""
|
"""Prepare archive content for operations"""
|
||||||
@@ -81,14 +86,14 @@ class UAdf(Archive):
|
|||||||
|
|
||||||
def _parse_dt(self, date, time):
|
def _parse_dt(self, date, time):
|
||||||
"""Return parsed datetime which fulfill extfs standards date."""
|
"""Return parsed datetime which fulfill extfs standards date."""
|
||||||
year, month, day = date.split(b"/")
|
year, month, day = date.split("/")
|
||||||
hours, minutes, _unused = time.split(b":")
|
hours, minutes, _unused = time.split(":")
|
||||||
return self.DATETIME % (month, day, year, int(hours), minutes)
|
return self.DATETIME % (month, day, year, int(hours), minutes)
|
||||||
|
|
||||||
def _ungzip(self):
|
def _ungzip(self):
|
||||||
"""Create temporary file for ungzipped adf file since unadf does not
|
"""Create temporary file for ungzipped adf file since unadf does not
|
||||||
accept gzipped content in any way including reading from stdin."""
|
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)
|
os.close(fdesc)
|
||||||
|
|
||||||
with gzip.open(self._arch) as gobj:
|
with gzip.open(self._arch) as gobj:
|
||||||
@@ -99,27 +104,36 @@ class UAdf(Archive):
|
|||||||
|
|
||||||
def _undms(self):
|
def _undms(self):
|
||||||
"""Create temporary adf file extracted from dms."""
|
"""Create temporary adf file extracted from dms."""
|
||||||
fdesc, tmp_fname = mkstemp(suffix=".adf")
|
fdesc, tmp_fname = tempfile.mkstemp(suffix=".adf")
|
||||||
os.close(fdesc)
|
os.close(fdesc)
|
||||||
|
|
||||||
try:
|
result = subprocess.run([self.DMS, '-q', 'u', self._arch,
|
||||||
check_call([self.DMS, b'-q', b'u', self._arch, "+" + tmp_fname])
|
"+" + tmp_fname])
|
||||||
|
if result.returncode == 0:
|
||||||
self._arch = tmp_fname
|
self._arch = tmp_fname
|
||||||
self._clean = False
|
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 _get_dir(self):
|
def _get_dir(self):
|
||||||
"""Retrieve directory"""
|
"""Retrieve directory"""
|
||||||
contents = []
|
contents = []
|
||||||
with open(os.devnull, "w") as fnull:
|
out = subprocess.run([self.ARCHIVER, self.CMDS['list'], self._arch],
|
||||||
try:
|
capture_output=True, encoding="latin-1")
|
||||||
out = check_output([self.ARCHIVER, self.CMDS['list'],
|
|
||||||
self._arch], stderr=fnull)
|
|
||||||
except CalledProcessError:
|
|
||||||
return contents
|
|
||||||
|
|
||||||
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)
|
match = self.LINE_PAT.match(line)
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
@@ -127,17 +141,17 @@ class UAdf(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'perms'] = b"-rw-r--r--"
|
entry['perms'] = "-rw-r--r--"
|
||||||
if not entry[b'size']:
|
if not entry['size']:
|
||||||
entry[b'perms'] = b"drwxr-xr-x"
|
entry['perms'] = "drwxr-xr-x"
|
||||||
entry[b'size'] = b"0"
|
entry['size'] = "0"
|
||||||
entry[b'display_name'] = self._map_name(entry[b'fpath'])
|
entry['display_name'] = self._map_name(entry['fpath'])
|
||||||
entry[b'datetime'] = self._parse_dt(entry[b'date'], entry[b'time'])
|
entry['datetime'] = self._parse_dt(entry['date'], entry['time'])
|
||||||
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
|
||||||
@@ -149,39 +163,46 @@ class UAdf(Archive):
|
|||||||
Add suffix to show user what kind of file do he dealing with.
|
Add suffix to show user what kind of file do he dealing with.
|
||||||
"""
|
"""
|
||||||
if not self._contents:
|
if not self._contents:
|
||||||
sys.stderr.write("Nodos or archive error\n")
|
sys.stderr.write("Nodos or image error\n")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
for entry in self._contents:
|
for entry in self._contents:
|
||||||
sys.stdout.buffer.write(self.ITEM % entry)
|
print(self.ITEM.decode('utf-8')[:-1] % entry)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def copyout(self, src, dst):
|
def copyout(self, src, dst):
|
||||||
"""Copy file form the adf image."""
|
"""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:
|
if not real_src:
|
||||||
raise IOError("No such file or directory")
|
raise IOError("No such file or directory")
|
||||||
|
|
||||||
|
real_src = real_src[0].encode('latin-1')
|
||||||
|
|
||||||
if b" " in real_src:
|
if b" " in real_src:
|
||||||
sys.stderr.write("unadf is unable to operate on filepath with "
|
sys.stderr.write("unadf is unable to operate on filepath with "
|
||||||
"space inside.\nUse affs to mount image and than"
|
"space inside.\nUse affs to mount image and than"
|
||||||
" extract desired files.\n")
|
" extract desired files.\n")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
extract_dir = mkdtemp()
|
extract_dir = tempfile.mkdtemp()
|
||||||
cmd = [self.ARCHIVER, self._arch, real_src, b"-d", extract_dir]
|
cmd = [self.ARCHIVER, "-d", extract_dir, self._arch, real_src]
|
||||||
if check_call(cmd, stdout=open(os.devnull, 'wb'),
|
result = subprocess.run(cmd, capture_output=True)
|
||||||
stderr=open(os.devnull, 'wb')) != 0:
|
|
||||||
|
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)
|
shutil.rmtree(extract_dir)
|
||||||
sys.stderr.write("unadf returned with nonzero exit code\n")
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
shutil.move(os.path.join(bytes(extract_dir, "utf8"), real_src),
|
# use subprocess, as shutil will crash on binary encoded filenames
|
||||||
bytes(dst, "utf8"))
|
subprocess.run([b'mv', os.path.join(extract_dir.encode("latin-1"),
|
||||||
|
real_src), dst.encode('latin-1')])
|
||||||
shutil.rmtree(extract_dir)
|
shutil.rmtree(extract_dir)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(parse_args(UAdf))
|
sys.exit(extfslib.parse_args(UAdf))
|
||||||
|
|||||||
Reference in New Issue
Block a user