Skip to content

Commit cf4a975

Browse files
author
Kazuki Suzuki
authored
Add files via upload
1 parent 76199ac commit cf4a975

File tree

3 files changed

+75
-157
lines changed

3 files changed

+75
-157
lines changed

neofile.ini

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[config]
2+
default = PyNeoFile
3+
4+
[PyNeoFile]
5+
magic = PyNeoFile
6+
ver = 001
7+
delimiter = \x00
8+
newstyle = true
9+
extension = .neo

neofile.py

Lines changed: 64 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -5,82 +5,32 @@
55
from __future__ import absolute_import, division, print_function, unicode_literals
66

77
"""
8-
neofile.py
9-
A lightweight CLI re-creation of archivefile.py that uses the *alt* implementation
10-
(pyneofile) you asked for, including:
11-
- INI-based format defaults (auto-fallback)
12-
- stdlib compression with size-based "auto" (zlib/gzip/bz2, xz on Py3 when available)
13-
- robust checksums (CRC-32 padded), JSON/header/content verification
14-
- Python 2 and 3 compatibility
15-
- Optional conversion from ZIP/TAR (stdlib), and **RAR/7z** when extra libs are installed:
16-
* RAR: pip install rarfile
17-
* 7z: pip install py7zr
18-
19-
Operations:
20-
-c / --create pack files/dirs into an archive
21-
-e / --extract extract an archive
22-
-r / --repack repack (optionally change compression)
23-
-l / --list list entries (fast, header-only)
24-
-v / --validate validate checksums
25-
26-
Convert support:
27-
Use -t/--convert with --create/--repack/--list/--validate to convert a foreign archive
28-
(zip/tar are stdlib; rar/7z need optional libs) into the alt ArchiveFile format first.
8+
neofile.py — CLI for the PyNeoFile format (.neo).
9+
Uses the pyneofile wrappers (which call into pyneoarc_alt) and prefers pyneofile.ini.
10+
Supports converting from zip/tar (stdlib), and rar/7z when optional libs are installed.
2911
"""
3012

31-
import os
32-
import sys
33-
import argparse
34-
import tempfile
13+
import os, sys, argparse, tempfile
14+
import pyneofile as N
3515

36-
# Graceful SIGPIPE on non-Windows
37-
if os.name != 'nt':
38-
try:
39-
import signal
40-
if hasattr(signal, 'SIGPIPE'):
41-
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
42-
except Exception:
43-
pass
44-
45-
# Import alt core
46-
try:
47-
import pyneofile as A
48-
except Exception as e:
49-
sys.stderr.write("Failed to import pyneofile: %s\n" % (e,))
50-
sys.exit(2)
51-
52-
__program_name__ = "neofile"
53-
__version__ = "0.2.0"
16+
__program_name__ = "pyneofile"
17+
__version__ = "0.1.0"
5418

5519
def _build_formatspecs_from_args(args):
56-
"""Create a formatspecs dict or return None to use INI auto-fallback."""
20+
# Allow explicit override; otherwise let wrappers auto-load pyneofile.ini
5721
if args.format is None or args.format.lower() == "auto":
58-
return None # let alt core load INI or defaults
59-
magic = args.format
60-
ver = args.formatver if args.formatver is not None else "001"
61-
delim = args.delimiter if args.delimiter is not None else "\x00"
22+
return None
6223
return {
63-
"format_name": magic,
64-
"format_magic": magic,
65-
"format_ver": ver, # alt core keeps digits as-is
66-
"format_delimiter": delim,
24+
"format_name": args.format,
25+
"format_magic": args.format,
26+
"format_ver": (args.formatver or "001"),
27+
"format_delimiter": (args.delimiter or "\x00"),
6728
"new_style": True,
6829
}
6930

70-
def _read_listfile(path):
71-
items = []
72-
with open(path, 'r') as f:
73-
for line in f:
74-
s = line.strip()
75-
if not s or s.startswith('#'):
76-
continue
77-
items.append(s)
78-
return items
79-
8031
def _convert_or_fail(infile, outpath, formatspecs, checksum, compression, level):
81-
"""Call core convert function and show friendly messages for missing deps."""
8232
try:
83-
A.convert_foreign_to_alt(infile, outpath, formatspecs=formatspecs,
33+
N.convert_foreign_to_neo(infile, outpath, formatspecs=formatspecs,
8434
checksumtypes=(checksum, checksum, checksum),
8535
compression=compression, compression_level=level)
8636
return True
@@ -93,148 +43,107 @@ def _convert_or_fail(infile, outpath, formatspecs, checksum, compression, level)
9343
else:
9444
sys.stderr.write("convert error: %s\n" % msg)
9545
return False
96-
except ValueError as e:
97-
# Unsupported format (not zip/tar/rar/7z)
98-
sys.stderr.write("convert error: %s\n" % e)
99-
return False
10046
except Exception as e:
101-
sys.stderr.write("unexpected convert error: %s\n" % e)
47+
sys.stderr.write("convert error: %s\n" % e)
10248
return False
10349

10450
def main(argv=None):
105-
p = argparse.ArgumentParser(description="Manipulate ArchiveFile (alt) archives.", conflict_handler="resolve", add_help=True)
106-
107-
p.add_argument("-V", "--version", action="version", version=__program_name__ + " " + __version__)
108-
109-
# IO
110-
p.add_argument("-i", "--input", nargs="+", required=True, help="Input file(s) or archive file.")
111-
p.add_argument("-o", "--output", default=None, help="Output file or directory.")
51+
p = argparse.ArgumentParser(description="PyNeoFile (.neo) archiver", add_help=True)
52+
p.add_argument("-V","--version", action="version", version=__program_name__ + " " + __version__)
11253

113-
# Ops
114-
p.add_argument("-c", "--create", action="store_true", help="Create an archive from input files/dirs.")
115-
p.add_argument("-e", "--extract", action="store_true", help="Extract an archive to --output (directory).")
116-
p.add_argument("-r", "--repack", action="store_true", help="Repack an existing archive (can change compression).")
117-
p.add_argument("-l", "--list", action="store_true", help="List archive entries.")
118-
p.add_argument("-v", "--validate", action="store_true", help="Validate archive checksums.")
119-
p.add_argument("-t", "--convert", action="store_true", help="Treat input as foreign (zip/tar/rar/7z) and convert to ArchiveFile first.")
54+
p.add_argument("-i","--input", nargs="+", required=True, help="Input files/dirs or archive file")
55+
p.add_argument("-o","--output", default=None, help="Output file or directory")
12056

121-
# Format & delimiter
122-
p.add_argument("-F", "--format", default="auto", help="Format magic to use (or 'auto' to read INI).")
123-
p.add_argument("-D", "--delimiter", default=None, help="Delimiter to use when --format is not 'auto'.")
124-
p.add_argument("-m", "--formatver", default=None, help="Format version digits (e.g. 001).")
57+
p.add_argument("-c","--create", action="store_true", help="Create a .neo archive from inputs")
58+
p.add_argument("-e","--extract", action="store_true", help="Extract an archive to --output")
59+
p.add_argument("-r","--repack", action="store_true", help="Repack an archive (change compression)")
60+
p.add_argument("-l","--list", action="store_true", help="List entries")
61+
p.add_argument("-v","--validate", action="store_true", help="Validate checksums")
62+
p.add_argument("-t","--convert", action="store_true", help="Convert zip/tar/rar/7z → .neo first")
12563

126-
# Compression
127-
p.add_argument("-P", "--compression", default="auto", help="Compression: none|zlib|gzip|bz2|xz|auto")
128-
p.add_argument("-L", "--level", default=None, help="Compression level/preset (int).")
129-
p.add_argument("-W", "--wholefile", action="store_true", help="(Ignored; CLI compatibility).")
64+
p.add_argument("-F","--format", default="auto", help="Format magic (default 'auto' via pyneofile.ini)")
65+
p.add_argument("-D","--delimiter", default=None, help="Delimiter (when not using 'auto')")
66+
p.add_argument("-m","--formatver", default=None, help="Version digits (e.g. 001)")
13067

131-
# Validation & extraction behavior
132-
p.add_argument("-C", "--checksum", default="crc32", help="Checksum algorithm (header/content/json).")
133-
p.add_argument("-s", "--skipchecksum", action="store_true", help="Skip checksum verification while reading.")
134-
p.add_argument("-p", "--preserve", action="store_false", help="Do not preserve permissions/times (kept for compatibility).")
135-
136-
# Misc
137-
p.add_argument("-d", "--verbose", action="store_true", help="Verbose logging.")
138-
p.add_argument("-T", "--text", action="store_true", help="Treat the first input argument as a text file containing paths (one per line).")
68+
p.add_argument("-P","--compression", default="auto", help="Compression: none|zlib|gzip|bz2|xz|auto")
69+
p.add_argument("-L","--level", default=None, help="Compression level/preset")
70+
p.add_argument("-C","--checksum", default="crc32", help="Checksum algorithm")
71+
p.add_argument("-s","--skipchecksum", action="store_true", help="Skip checks while reading")
72+
p.add_argument("-d","--verbose", action="store_true", help="Verbose listing")
13973

14074
args = p.parse_args(argv)
14175

142-
# Choose primary action
143-
actions = ['create', 'extract', 'repack', 'list', 'validate']
144-
active = next((name for name in actions if getattr(args, name)), None)
145-
if not active:
146-
p.error("one of --create/--extract/--repack/--list/--validate is required")
147-
148-
# formatspecs (None => INI auto-fallback inside the alt core)
14976
formatspecs = _build_formatspecs_from_args(args)
150-
151-
# Inputs
15277
inputs = args.input
15378
infile0 = inputs[0]
154-
155-
# Compression/level
15679
compression = args.compression
15780
level = None if args.level in (None, "",) else int(args.level)
158-
159-
# Checksum triple
16081
checksum = args.checksum
161-
checks = (checksum, checksum, checksum)
16282

163-
if active == 'create':
164-
if args.text:
165-
inputs = _read_listfile(infile0)
83+
if args.create:
16684
if args.convert:
167-
if not args.output:
168-
p.error("--output is required when using --convert")
85+
if not args.output: p.error("--output is required")
16986
ok = _convert_or_fail(infile0, args.output, formatspecs, checksum, compression, level)
17087
return 0 if ok else 1
171-
if not args.output:
172-
p.error("--output is required for --create")
173-
A.pack_alt(inputs if not args.text else inputs, args.output, formatspecs=formatspecs,
174-
checksumtypes=checks, compression=compression, compression_level=level)
175-
if args.verbose:
176-
sys.stderr.write("created: %s\n" % args.output)
88+
if not args.output: p.error("--output is required")
89+
N.pack_neo(inputs, args.output, formatspecs=formatspecs,
90+
checksumtypes=(checksum, checksum, checksum),
91+
compression=compression, compression_level=level)
92+
if args.verbose: sys.stderr.write("created: %s\n" % args.output)
17793
return 0
17894

179-
if active == 'repack':
95+
if args.repack:
18096
if args.convert:
181-
if not args.output:
182-
p.error("--output is required when using --repack --convert")
97+
if not args.output: p.error("--output is required")
18398
ok = _convert_or_fail(infile0, args.output, formatspecs, checksum, compression, level)
18499
return 0 if ok else 1
185-
if not args.output:
186-
p.error("--output is required for --repack")
187-
A.repack_alt(infile0, args.output, formatspecs=formatspecs,
188-
checksumtypes=checks, compression=compression, compression_level=level)
189-
if args.verbose:
190-
sys.stderr.write("repacked: %s -> %s\n" % (infile0, args.output))
100+
if not args.output: p.error("--output is required")
101+
N.repack_neo(infile0, args.output, formatspecs=formatspecs,
102+
checksumtypes=(checksum, checksum, checksum),
103+
compression=compression, compression_level=level)
104+
if args.verbose: sys.stderr.write("repacked: %s\n" % args.output)
191105
return 0
192106

193-
if active == 'extract':
107+
if args.extract:
194108
outdir = args.output or "."
195109
if args.convert:
196-
tmp_arc = os.path.join(tempfile.gettempdir(), "af_alt_convert.arc")
110+
tmp_arc = os.path.join(tempfile.gettempdir(), "pyneofile_convert.neo")
197111
ok = _convert_or_fail(infile0, tmp_arc, formatspecs, checksum, compression, level)
198-
if not ok:
199-
return 1
200-
infile0_use = tmp_arc
112+
if not ok: return 1
113+
use = tmp_arc
201114
else:
202-
infile0_use = infile0
203-
A.unpack_alt(infile0_use, outdir, formatspecs=formatspecs, skipchecksum=args.skipchecksum, uncompress=True)
204-
if args.verbose:
205-
sys.stderr.write("extracted to: %s\n" % outdir)
115+
use = infile0
116+
N.unpack_neo(use, outdir, formatspecs=formatspecs, skipchecksum=args.skipchecksum, uncompress=True)
117+
if args.verbose: sys.stderr.write("extracted → %s\n" % outdir)
206118
return 0
207119

208-
if active == 'list':
120+
if args.list:
209121
if args.convert:
210-
tmp_arc = os.path.join(tempfile.gettempdir(), "af_alt_convert.arc")
122+
tmp_arc = os.path.join(tempfile.gettempdir(), "pyneofile_convert.neo")
211123
ok = _convert_or_fail(infile0, tmp_arc, formatspecs, checksum, compression, level)
212-
if not ok:
213-
return 1
124+
if not ok: return 1
214125
use = tmp_arc
215126
else:
216127
use = infile0
217-
names = A.archivefilelistfiles_alt(use, formatspecs=formatspecs, advanced=args.verbose, include_dirs=True)
128+
names = N.archivefilelistfiles_neo(use, formatspecs=formatspecs, advanced=args.verbose, include_dirs=True)
218129
if not args.verbose:
219-
for n in names:
220-
sys.stdout.write(n + "\n")
130+
for n in names: sys.stdout.write(n + "\n")
221131
else:
222132
for ent in names:
223133
sys.stdout.write("%s\t%s\t%s\t%s\n" % (
224134
ent['type'], ent['compression'], ent['size'], ent['name']
225135
))
226136
return 0
227137

228-
if active == 'validate':
138+
if args.validate:
229139
if args.convert:
230-
tmp_arc = os.path.join(tempfile.gettempdir(), "af_alt_convert.arc")
140+
tmp_arc = os.path.join(tempfile.gettempdir(), "pyneofile_convert.neo")
231141
ok = _convert_or_fail(infile0, tmp_arc, formatspecs, checksum, compression, level)
232-
if not ok:
233-
return 1
142+
if not ok: return 1
234143
use = tmp_arc
235144
else:
236145
use = infile0
237-
ok, details = A.archivefilevalidate_alt(use, formatspecs=formatspecs, verbose=args.verbose, return_details=True)
146+
ok, details = N.archivefilevalidate_neo(use, formatspecs=formatspecs, verbose=args.verbose, return_details=True)
238147
if not args.verbose:
239148
sys.stdout.write("valid: %s\n" % ("yes" if ok else "no"))
240149
else:
@@ -245,7 +154,7 @@ def main(argv=None):
245154
))
246155
return 0
247156

248-
return 0
157+
p.error("one of --create/--extract/--repack/--list/--validate is required")
249158

250159
if __name__ == "__main__":
251160
sys.exit(main())

pyneofile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,10 @@ def _load_formatspecs_from_ini(paths=None, prefer_section=None):
189189
cands.append(paths)
190190
else:
191191
cands.extend(paths)
192-
envp = os.environ.get('PYARCHIVE_INI')
192+
envp = os.environ.get('PYNEOFILE_INI')
193193
if envp:
194194
cands.append(envp)
195-
cands.extend(['archivefile.ini', 'catfile.ini', 'foxfile.ini'])
195+
cands.extend(['neofile.ini'])
196196

197197
picked = None
198198
for p in cands:

0 commit comments

Comments
 (0)