comparison cutils/treesum.py @ 190:7e0c25a31757

First implementation of "treeview info" to print some information from the treeview digest files
author Franz Glasner <fzglas.hg@dom66.de>
date Thu, 16 Jan 2025 11:29:36 +0100
parents 959c6d37b014
children 1b8bc876146a
comparison
equal deleted inserted replaced
189:959c6d37b014 190:7e0c25a31757
125 "--size-only", action="store_true", 125 "--size-only", action="store_true",
126 help="""Print only the size of files and for each directory its 126 help="""Print only the size of files and for each directory its
127 accumulated directory size. Digests are not computed.""") 127 accumulated directory size. Digests are not computed.""")
128 gp.add_argument( 128 gp.add_argument(
129 "directories", nargs="*", metavar="DIRECTORY") 129 "directories", nargs="*", metavar="DIRECTORY")
130
131 def _populate_info_arguments(ip):
132 ip.add_argument(
133 "--debug", action="store_true",
134 help="Activate debug logging to stderr")
135 ip.add_argument(
136 "--last", action="store_true", dest="print_only_last_block",
137 help="Print only the last block of every given input file")
138 ip.add_argument(
139 "digest_files", nargs="+", metavar="TREESUM-DIGEST-FILE")
130 140
131 parser = argparse.ArgumentParser( 141 parser = argparse.ArgumentParser(
132 description="Generate and verify checksums for directory trees.", 142 description="Generate and verify checksums for directory trees.",
133 fromfile_prefix_chars='@', 143 fromfile_prefix_chars='@',
134 add_help=False) 144 add_help=False)
159 "is listed below:", 169 "is listed below:",
160 metavar="COMMAND") 170 metavar="COMMAND")
161 171
162 genparser = subparsers.add_parser( 172 genparser = subparsers.add_parser(
163 "generate", 173 "generate",
164 help="Generate checksums for directory trees.", 174 help="Generate checksums for directory trees",
165 description="Generate checksums for directory trees") 175 description="Generate checksums for directory trees.")
166 _populate_generate_arguments(genparser) 176 _populate_generate_arguments(genparser)
167 # And an alias for "generate" 177 # And an alias for "generate"
168 genparser2 = subparsers.add_parser( 178 genparser2 = subparsers.add_parser(
169 "gen", 179 "gen",
170 help="Alias for \"generate\"", 180 help="Alias for \"generate\"",
171 description="Generate checksums for directory trees. " 181 description="Generate checksums for directory trees. "
172 "This is an alias to \"generate\".") 182 "This is an alias to \"generate\".")
173 _populate_generate_arguments(genparser2) 183 _populate_generate_arguments(genparser2)
184
185 infoparser = subparsers.add_parser(
186 "info",
187 help="Print some information from given treesum digest file",
188 description="""Print some informations from given treesum digest files
189 to stdout."""
190 )
191 _populate_info_arguments(infoparser)
174 192
175 hparser = subparsers.add_parser( 193 hparser = subparsers.add_parser(
176 "help", 194 "help",
177 help="Show this help message or a subcommand's help and exit", 195 help="Show this help message or a subcommand's help and exit",
178 description="Show this help message or a subcommand's help and exit.") 196 description="Show this help message or a subcommand's help and exit.")
196 else: 214 else:
197 if opts.help_command == "generate": 215 if opts.help_command == "generate":
198 genparser.print_help() 216 genparser.print_help()
199 elif opts.help_command == "gen": 217 elif opts.help_command == "gen":
200 genparser2.print_help() 218 genparser2.print_help()
219 elif opts.help_command == "info":
220 infoparser.print_help()
201 elif opts.help_command == "version": 221 elif opts.help_command == "version":
202 vparser.print_help() 222 vparser.print_help()
203 elif opts.help_command == "help": 223 elif opts.help_command == "help":
204 hparser.print_help() 224 hparser.print_help()
205 else: 225 else:
252 print_size=print_size, 272 print_size=print_size,
253 size_only=size_only) 273 size_only=size_only)
254 return opts 274 return opts
255 275
256 276
277 def gen_info_opts(digest_files=[], last=False):
278 opts = argparse.Namespace(
279 digest_files=digest_files,
280 print_only_last_block=last)
281 return opts
282
283
257 def treesum(opts): 284 def treesum(opts):
258 # XXX TBD: opts.check and opts.checklist (as in shasum.py) 285 # XXX TBD: opts.check and opts.checklist (as in shasum.py)
259 if opts.subcommand in ("generate", "gen"): 286 if opts.subcommand in ("generate", "gen"):
260 return generate_treesum(opts) 287 return generate_treesum(opts)
288 elif opts.subcommand == "info":
289 return print_treesum_digestfile_infos(opts)
261 else: 290 else:
262 raise RuntimeError( 291 raise RuntimeError(
263 "command `{}' not yet handled".format(opts.subcommand)) 292 "command `{}' not yet handled".format(opts.subcommand))
264 293
265 294
815 self._current_algo_name = algo_name 844 self._current_algo_name = algo_name
816 self._current_algo_digest_size = h.digest_size 845 self._current_algo_digest_size = h.digest_size
817 return self._current_algo_digest_size 846 return self._current_algo_digest_size
818 847
819 848
849 def print_treesum_digestfile_infos(opts):
850 print_infos_for_digestfile(opts.digest_files, opts.print_only_last_block)
851
852
853 def print_infos_for_digestfile(digest_files, print_only_last_block=True):
854 for fn in digest_files:
855 if fn == "-":
856 if util.PY2:
857 reader = TreesumReader.from_binary_buffer(sys.stdin)
858 else:
859 reader = TreesumReader.from_binary_buffer(sys.stdin.buffer)
860 else:
861 reader = TreesumReader.from_path(fn)
862
863 with reader:
864 root = flags = algorithm = digest = size = None
865 comments = []
866 in_block = False
867 block_no = 0
868 for record in reader:
869 if record[0] == "VERSION":
870 assert record[1] == "1"
871 # start a new block
872 in_block = True
873 block_no += 1
874 root = flags = algorithm = digest = size = None
875 comments = []
876 elif record[0] == "FLAGS":
877 flags = record[1]
878 elif record[0] == "ROOT":
879 root = record[1]
880 elif record[0] == "COMMENT":
881 comments.append(record[1])
882 elif record[0] in ("TIMESTAMP", "ISOTIMESTAMP"):
883 pass
884 elif record[0] == "CRC32":
885 pass
886 # in_block = False
887 else:
888 if not in_block:
889 continue
890 # digest line or size line
891 if not record[1] or record[1] == b"./@":
892 if record[0] == "SIZE":
893 algorithm = "SIZE"
894 digest = None
895 size = record[2]
896 else:
897 algorithm = record[0]
898 digest = record[2]
899 size = record[3]
900 if not print_only_last_block:
901 print_block_data(
902 block_no,
903 root, flags, comments, algorithm, digest, size)
904 root = flags = algorithm = digest = size = None
905 in_block = False
906 if print_only_last_block:
907 if not in_block:
908 if digest is not None or size is not None:
909 print_block_data(
910 block_no,
911 root, flags, comments, algorithm, digest, size)
912 else:
913 logging.warning("missing block end")
914
915
916 def print_block_data(block_no, tag, flags, comments, algorithm, digest, size):
917 digeststr = util.n(binascii.hexlify(digest)) if digest else "<no digest>"
918 sizestr = str(size) if size is not None else "<no size>"
919 print("BLOCK No %d:" % (block_no,))
920 print(" Tag:", tag)
921 print(" Flags:", flags if flags else "<none>")
922 print(" Comments:", comments if comments else "")
923 print(" Algorithm:", algorithm)
924 if algorithm != "SIZE":
925 print(" Digest:", digeststr)
926 print(" Size:", sizestr)
927
928
820 if __name__ == "__main__": 929 if __name__ == "__main__":
821 sys.exit(main()) 930 sys.exit(main())