Added support for Python 3 for ulha
ulzx and uadf currently are broken. Fix for both of them is on the way.
This commit is contained in:
73
extfslib.py
73
extfslib.py
@@ -2,9 +2,10 @@
|
||||
extfslib is a library which contains Archive class to support writing extfs
|
||||
plugins for Midnight Commander.
|
||||
|
||||
Tested against python 2.7 and mc 4.8.7
|
||||
Tested against python 3.6 and mc 4.8.22
|
||||
|
||||
Changelog:
|
||||
2.0 Switch to python3
|
||||
1.1 Added item pattern, and common git/uid attrs
|
||||
1.0 Initial release
|
||||
|
||||
@@ -13,6 +14,7 @@ Date: 2013-05-12
|
||||
Version: 1.1
|
||||
Licence: BSD
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
@@ -33,8 +35,8 @@ class Archive(object):
|
||||
"read": "r",
|
||||
"write": "w",
|
||||
"delete": "d"}
|
||||
ITEM = ("%(perms)s 1 %(uid)-8s %(gid)-8s %(size)8s %(datetime)s "
|
||||
"%(display_name)s\n")
|
||||
ITEM = (b"%(perms)s 1 %(uid)-8s %(gid)-8s %(size)8s %(datetime)s "
|
||||
b"%(display_name)s\n")
|
||||
|
||||
def __init__(self, fname):
|
||||
"""Prepare archive content for operations"""
|
||||
@@ -43,13 +45,14 @@ class Archive(object):
|
||||
self._uid = os.getuid()
|
||||
self._gid = os.getgid()
|
||||
self._arch = fname
|
||||
self.name_map = {}
|
||||
self._contents = self._get_dir()
|
||||
|
||||
def _map_name(self, name):
|
||||
"""MC still have a bug in extfs subsystem, in case of filepaths with
|
||||
leading space. This is workaround to this bug, which replaces leading
|
||||
space with tilda."""
|
||||
if name.startswith(" "):
|
||||
if name.startswith(b" "):
|
||||
new_name = "".join(["~", name[1:]])
|
||||
return new_name
|
||||
return name
|
||||
@@ -58,8 +61,9 @@ class Archive(object):
|
||||
"""Get real filepath of the file. See _map_name docstring for
|
||||
details."""
|
||||
for item in self._contents:
|
||||
if item['display_name'] == name:
|
||||
return item['fpath']
|
||||
if item[b'display_name'] == name.encode('utf-8',
|
||||
'surrogateescape'):
|
||||
return item[b'fpath']
|
||||
return None
|
||||
|
||||
def _get_dir(self):
|
||||
@@ -72,7 +76,7 @@ class Archive(object):
|
||||
if not out:
|
||||
return
|
||||
|
||||
for line in out.split("\n"):
|
||||
for line in out.split(b"\n"):
|
||||
match = self.LINE_PAT.match(line)
|
||||
if not match:
|
||||
continue
|
||||
@@ -147,6 +151,61 @@ def usage():
|
||||
{"prg": sys.argv[0]})
|
||||
|
||||
|
||||
def _parse_args(arch_class):
|
||||
"""Use ArgumentParser to check for script arguments and execute."""
|
||||
|
||||
CALL_MAP = {'list': lambda a: arch_class(a.arch).list(),
|
||||
'copyin': lambda a: arch_class(a.arch).copyin(a.src, a.dst),
|
||||
'copyout': lambda a: arch_class(a.arch).copyout(a.src, a.dst),
|
||||
'mkdir': lambda a: arch_class(a.arch).mkdir(a.dst),
|
||||
'rm': lambda a: arch_class(a.arch).rm(a.dst),
|
||||
'run': lambda a: arch_class(a.arch).run(a.dst)}
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(help='supported commands')
|
||||
parser_list = subparsers.add_parser('list', help="List contents of "
|
||||
"archive")
|
||||
parser_copyin = subparsers.add_parser('copyin', help="Copy file into "
|
||||
"archive")
|
||||
parser_copyout = subparsers.add_parser('copyout', help="Copy file out of "
|
||||
"archive")
|
||||
parser_rm = subparsers.add_parser('rm', help="Delete file from archive")
|
||||
parser_mkdir = subparsers.add_parser('mkdir', help="Create directory in "
|
||||
"archive")
|
||||
parser_run = subparsers.add_parser('run', help="Execute archived file")
|
||||
|
||||
parser_list.add_argument('arch', help="Archive filename")
|
||||
parser_list.set_defaults(func=CALL_MAP['list'])
|
||||
|
||||
parser_copyin.add_argument('arch', help="Archive filename")
|
||||
parser_copyin.add_argument('src', help="Source filename")
|
||||
parser_copyin.add_argument('dst', help="Destination filename (to be "
|
||||
"written into archive)")
|
||||
parser_copyin.set_defaults(func=CALL_MAP['copyin'])
|
||||
|
||||
parser_copyout.add_argument('arch', help="D64 Image filename")
|
||||
parser_copyout.add_argument('src', help="Source filename (to be read from"
|
||||
" archive")
|
||||
parser_copyout.add_argument('dst', help="Destination filename")
|
||||
parser_copyout.set_defaults(func=CALL_MAP['copyout'])
|
||||
|
||||
parser_rm.add_argument('arch', help="D64 Image filename")
|
||||
parser_rm.add_argument('dst', help="File inside archive to be deleted")
|
||||
parser_rm.set_defaults(func=CALL_MAP['rm'])
|
||||
|
||||
parser_mkdir.add_argument('arch', help="archive filename")
|
||||
parser_mkdir.add_argument('dst', help="Directory name inside archive to "
|
||||
"be created")
|
||||
parser_mkdir.set_defaults(func=CALL_MAP['mkdir'])
|
||||
|
||||
parser_run.add_argument('arch', help="archive filename")
|
||||
parser_run.add_argument('dst', help="File to be executed")
|
||||
parser_run.set_defaults(func=CALL_MAP['run'])
|
||||
|
||||
args = parser.parse_args()
|
||||
return args.func(args)
|
||||
|
||||
|
||||
def parse_args(arch_class):
|
||||
"""Retrive and parse arguments from commandline and apply them into passed
|
||||
arch_class class object."""
|
||||
|
||||
67
ulha
67
ulha
@@ -1,12 +1,13 @@
|
||||
#! /usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Lha Virtual filesystem executive for Midnight Commander.
|
||||
|
||||
Tested against python 2.7, lha[1] 1.14 and mc 4.8.7
|
||||
Tested against python 3.6, lha[1] 1.14 and mc 4.8.22
|
||||
|
||||
[1] http://lha.sourceforge.jp
|
||||
|
||||
Changelog:
|
||||
2.0 Switch to python3
|
||||
1.2 Moved item pattern to extfslib module
|
||||
1.1 Moved common code into extfslib library
|
||||
1.0 Initial release
|
||||
@@ -29,22 +30,22 @@ from extfslib import Archive, parse_args
|
||||
class ULha(Archive):
|
||||
"""Archive handle. Provides interface to MC's extfs subsystem"""
|
||||
|
||||
LINE_PAT = re.compile("^((?P<perms>[d-][rswx-]{9})|(\[generic\])|"
|
||||
"(\[unknown\]))"
|
||||
"((\s+\d+/\d+\s+)|(\s+))"
|
||||
"(?P<uid>)(?P<gid>)" # just for the record
|
||||
"(?P<size>\d+)"
|
||||
"\s+(\*{6}|\d+\.\d%)"
|
||||
"\s(?P<month>[JFMASOND][a-z]{2})\s+" # month
|
||||
"(?P<day>\d+)\s+" # day
|
||||
"(?P<yh>\d{4}|(\d{2}:\d{2}))" # year/hour
|
||||
"\s(?P<fpath>.*)")
|
||||
ARCHIVER = "lha"
|
||||
CMDS = {"list": "lq",
|
||||
"read": "pq",
|
||||
"write": "aq",
|
||||
"delete": "dq"}
|
||||
DATETIME = "%(month)s %(day)s %(yh)s"
|
||||
LINE_PAT = re.compile(b"^((?P<perms>[d-][rswx-]{9})|(\[generic\])|"
|
||||
b"(\[unknown\]))"
|
||||
b"((\s+\d+/\d+\s+)|(\s+))"
|
||||
b"(?P<uid>)(?P<gid>)" # just for the record
|
||||
b"(?P<size>\d+)"
|
||||
b"\s+(\*{6}|\d+\.\d%)"
|
||||
b"\s(?P<month>[JFMASOND][a-z]{2})\s+" # month
|
||||
b"(?P<day>\d+)\s+" # day
|
||||
b"(?P<yh>\d{4}|(\d{2}:\d{2}))" # year/hour
|
||||
b"\s(?P<fpath>.*)")
|
||||
ARCHIVER = b"lha"
|
||||
CMDS = {"list": b"lq",
|
||||
"read": b"pq",
|
||||
"write": b"aq",
|
||||
"delete": b"dq"}
|
||||
DATETIME = b"%(month)s %(day)s %(yh)s"
|
||||
|
||||
def _get_dir(self):
|
||||
"""Prepare archive file listing"""
|
||||
@@ -54,37 +55,43 @@ class ULha(Archive):
|
||||
if not out:
|
||||
return
|
||||
|
||||
for line in out.split("\n"):
|
||||
for line in out.split(b"\n"):
|
||||
# -lhd- can store empty directories
|
||||
perms = "-rw-r--r--"
|
||||
if line.endswith(os.path.sep):
|
||||
perms = b"-rw-r--r--"
|
||||
if line.endswith(bytes(os.path.sep, 'utf-8')):
|
||||
line = line[:-1]
|
||||
perms = "drw-r--r--"
|
||||
perms = b"drw-r--r--"
|
||||
|
||||
match = self.LINE_PAT.match(line)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
entry = match.groupdict()
|
||||
match_entry = match.groupdict()
|
||||
entry = {}
|
||||
for key in match_entry:
|
||||
entry[bytes(key, 'utf-8')] = match_entry[key]
|
||||
del match_entry
|
||||
# UID and GID sometimes can have strange values depending on
|
||||
# the information that was written into archive. Most of the
|
||||
# times I was dealing with Amiga lha archives, so that i don't
|
||||
# really care about real user/group
|
||||
entry['uid'] = self._uid
|
||||
entry['gid'] = self._gid
|
||||
entry['datetime'] = self.DATETIME % entry
|
||||
|
||||
if not entry['perms']:
|
||||
entry['perms'] = perms
|
||||
entry[b'uid'] = bytes(str(self._uid), 'utf-8')
|
||||
entry[b'gid'] = bytes(str(self._gid), 'utf-8')
|
||||
entry[b'datetime'] = self.DATETIME % entry
|
||||
|
||||
entry['display_name'] = self._map_name(entry['fpath'])
|
||||
if not entry[b'perms']:
|
||||
entry[b'perms'] = perms
|
||||
|
||||
entry[b'display_name'] = self._map_name(entry[b'fpath'])
|
||||
contents.append(entry)
|
||||
|
||||
return contents
|
||||
|
||||
def list(self):
|
||||
"""Output contents of the archive to stdout"""
|
||||
for entry in self._contents:
|
||||
sys.stdout.write(self.ITEM % entry)
|
||||
sys.stdout.buffer.write(self.ITEM % entry)
|
||||
return 0
|
||||
|
||||
def rm(self, dst):
|
||||
|
||||
Reference in New Issue
Block a user