123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- #-----------------------------------------------------------------------------
- # Copyright (c) 2013-2021, PyInstaller Development Team.
- #
- # Distributed under the terms of the GNU General Public License (version 2
- # or later) with exception for distributing the bootloader.
- #
- # The full license is in the file COPYING.txt, distributed with this software.
- #
- # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
- #-----------------------------------------------------------------------------
- """
- Viewer for archives packaged by archive.py.
- """
- import argparse
- import os
- import pprint
- import sys
- import tempfile
- import zlib
- import PyInstaller.log
- from PyInstaller.archive.readers import CArchiveReader, NotAnArchiveError
- from PyInstaller.compat import stdin_input
- from PyInstaller.loader import pyimod02_archive
- stack = []
- cleanup = []
- def main(name, brief, debug, rec_debug, **unused_options):
- global stack
- if not os.path.isfile(name):
- print(name, "is an invalid file name!", file=sys.stderr)
- return 1
- arch = get_archive(name)
- stack.append((name, arch))
- if debug or brief:
- show_log(arch, rec_debug, brief)
- raise SystemExit(0)
- else:
- show(name, arch)
- while 1:
- try:
- toks = stdin_input('? ').split(None, 1)
- except EOFError:
- # Ctrl-D
- print(file=sys.stderr) # Clear line.
- break
- if not toks:
- usage()
- continue
- if len(toks) == 1:
- cmd = toks[0]
- arg = ''
- else:
- cmd, arg = toks
- cmd = cmd.upper()
- if cmd == 'U':
- if len(stack) > 1:
- arch = stack[-1][1]
- del stack[-1]
- name, arch = stack[-1]
- show(name, arch)
- elif cmd == 'O':
- if not arg:
- arg = stdin_input('open name? ')
- arg = arg.strip()
- try:
- arch = get_archive(arg)
- except NotAnArchiveError as e:
- print(e, file=sys.stderr)
- continue
- if arch is None:
- print(arg, "not found", file=sys.stderr)
- continue
- stack.append((arg, arch))
- show(arg, arch)
- elif cmd == 'X':
- if not arg:
- arg = stdin_input('extract name? ')
- arg = arg.strip()
- data = get_data(arg, arch)
- if data is None:
- print("Not found", file=sys.stderr)
- continue
- filename = stdin_input('to filename? ')
- if not filename:
- print(repr(data))
- else:
- with open(filename, 'wb') as fp:
- fp.write(data)
- elif cmd == 'Q':
- break
- else:
- usage()
- do_cleanup()
- def do_cleanup():
- global stack, cleanup
- stack = []
- for filename in cleanup:
- try:
- os.remove(filename)
- except Exception as e:
- print("could not delete", filename, e.args, file=sys.stderr)
- cleanup = []
- def usage():
- print("U: go up one level", file=sys.stderr)
- print("O <name>: open embedded archive name", file=sys.stderr)
- print("X <name>: extract name", file=sys.stderr)
- print("Q: quit", file=sys.stderr)
- def get_archive(name):
- if not stack:
- if name[-4:].lower() == '.pyz':
- return ZlibArchive(name)
- return CArchiveReader(name)
- parent = stack[-1][1]
- try:
- return parent.openEmbedded(name)
- except KeyError:
- return None
- except (ValueError, RuntimeError):
- ndx = parent.toc.find(name)
- dpos, dlen, ulen, flag, typcd, name = parent.toc[ndx]
- x, data = parent.extract(ndx)
- tempfilename = tempfile.mktemp()
- cleanup.append(tempfilename)
- with open(tempfilename, 'wb') as fp:
- fp.write(data)
- if typcd == 'z':
- return ZlibArchive(tempfilename)
- else:
- return CArchiveReader(tempfilename)
- def get_data(name, arch):
- if isinstance(arch.toc, dict):
- (ispkg, pos, length) = arch.toc.get(name, (0, None, 0))
- if pos is None:
- return None
- with arch.lib:
- arch.lib.seek(arch.start + pos)
- return zlib.decompress(arch.lib.read(length))
- ndx = arch.toc.find(name)
- dpos, dlen, ulen, flag, typcd, name = arch.toc[ndx]
- x, data = arch.extract(ndx)
- return data
- def show(name, arch):
- if isinstance(arch.toc, dict):
- print(" Name: (ispkg, pos, len)")
- toc = arch.toc
- else:
- print(" pos, length, uncompressed, iscompressed, type, name")
- toc = arch.toc.data
- pprint.pprint(toc)
- def get_content(arch, recursive, brief, output):
- if isinstance(arch.toc, dict):
- toc = arch.toc
- if brief:
- for name, _ in toc.items():
- output.append(name)
- else:
- output.append(toc)
- else:
- toc = arch.toc.data
- for el in toc:
- if brief:
- output.append(el[5])
- else:
- output.append(el)
- if recursive:
- if el[4] in ('z', 'a'):
- get_content(get_archive(el[5]), recursive, brief, output)
- stack.pop()
- def show_log(arch, recursive, brief):
- output = []
- get_content(arch, recursive, brief, output)
- # first print all TOCs
- for out in output:
- if isinstance(out, dict):
- pprint.pprint(out)
- # then print the other entries
- pprint.pprint([out for out in output if not isinstance(out, dict)])
- def get_archive_content(filename):
- """
- Get a list of the (recursive) content of archive `filename`.
- This function is primary meant to be used by runtests.
- """
- archive = get_archive(filename)
- stack.append((filename, archive))
- output = []
- get_content(archive, recursive=True, brief=True, output=output)
- do_cleanup()
- return output
- class ZlibArchive(pyimod02_archive.ZlibArchiveReader):
- def checkmagic(self):
- """
- Overridable.
- Check to see if the file object self.lib actually has a file we understand.
- """
- self.lib.seek(self.start) # default - magic is at start of file.
- if self.lib.read(len(self.MAGIC)) != self.MAGIC:
- raise RuntimeError("%s is not a valid %s archive file" % (self.path, self.__class__.__name__))
- if self.lib.read(len(self.pymagic)) != self.pymagic:
- print("Warning: pyz is from a different Python version", file=sys.stderr)
- self.lib.read(4)
- def run():
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '-l',
- '--log',
- default=False,
- action='store_true',
- dest='debug',
- help='Print an archive log (default: %(default)s)',
- )
- parser.add_argument(
- '-r',
- '--recursive',
- default=False,
- action='store_true',
- dest='rec_debug',
- help='Recursively print an archive log (default: %(default)s). Can be combined with -r',
- )
- parser.add_argument(
- '-b',
- '--brief',
- default=False,
- action='store_true',
- dest='brief',
- help='Print only file name. (default: %(default)s). Can be combined with -r',
- )
- PyInstaller.log.__add_options(parser)
- parser.add_argument(
- 'name',
- metavar='pyi_archive',
- help="PyInstaller archive to show content of",
- )
- args = parser.parse_args()
- PyInstaller.log.__process_options(parser, args)
- try:
- raise SystemExit(main(**vars(args)))
- except KeyboardInterrupt:
- raise SystemExit("Aborted by user request.")
- if __name__ == '__main__':
- run()
|