1#! /usr/bin/env python3 2# __ __ _ 3# ___\ \/ /_ __ __ _| |_ 4# / _ \\ /| '_ \ / _` | __| 5# | __// \| |_) | (_| | |_ 6# \___/_/\_\ .__/ \__,_|\__| 7# |_| XML parser 8# 9# Copyright (c) 2019-2026 Sebastian Pipping <sebastian@pipping.org> 10# Copyright (c) 2021 Tim Bray <tbray@textuality.com> 11# Licensed under the MIT license: 12# 13# Permission is hereby granted, free of charge, to any person obtaining 14# a copy of this software and associated documentation files (the 15# "Software"), to deal in the Software without restriction, including 16# without limitation the rights to use, copy, modify, merge, publish, 17# distribute, sublicense, and/or sell copies of the Software, and to permit 18# persons to whom the Software is furnished to do so, subject to the 19# following conditions: 20# 21# The above copyright notice and this permission notice shall be included 22# in all copies or substantial portions of the Software. 23# 24# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 27# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 28# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 29# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 30# USE OR OTHER DEALINGS IN THE SOFTWARE. 31# 32# SPDX-License-Identifier: MIT 33 34import argparse 35from textwrap import dedent 36 37epilog = dedent( 38 """ 39 environment variables: 40 EXPAT_ACCOUNTING_DEBUG=(0|1|2|3) 41 Control verbosity of accounting debugging (default: 0) 42 EXPAT_ENTITY_DEBUG=(0|1|2) 43 Control verbosity of entity debugging (default: 0) 44 EXPAT_ENTROPY_DEBUG=(0|1) 45 Control verbosity of entropy debugging (default: 0) 46 EXPAT_MALLOC_DEBUG=(0|1|2) 47 Control verbosity of allocation tracker (default: 0) 48 49 exit status: 50 0 the input files are well-formed and the output (if requested) was written successfully 51 1 could not allocate data structures, signals a serious problem with execution environment 52 2 one or more input files were not well-formed 53 3 one or more output files could not be written 54 4 command-line argument error 55 56 xmlwf of libexpat is software libre, licensed under the MIT license. 57 Please report bugs at https://github.com/libexpat/libexpat/issues -- thank you! 58 """ 59) 60 61usage = """ 62 %(prog)s [OPTIONS] [FILE ...] 63 %(prog)s -h|--help 64 %(prog)s -v|--version 65""" 66 67parser = argparse.ArgumentParser( 68 prog="xmlwf", 69 add_help=False, 70 usage=usage, 71 description="xmlwf - Determines if an XML document is well-formed", 72 formatter_class=argparse.RawTextHelpFormatter, 73 epilog=epilog, 74) 75 76input_related = parser.add_argument_group("input control arguments") 77input_related.add_argument( 78 "-s", action="store_true", help="print an error if the document is not [s]tandalone" 79) 80input_related.add_argument( 81 "-n", action="store_true", help="enable [n]amespace processing" 82) 83input_related.add_argument( 84 "-p", 85 action="store_true", 86 help="enable processing of external DTDs and [p]arameter entities", 87) 88input_related.add_argument( 89 "-x", 90 action="store_true", 91 help=( 92 "enable processing of e[x]ternal entities" 93 "\n" 94 "(CAREFUL! This makes xmlwf vulnerable to external entity attacks (XXE).)" 95 ), 96) 97input_related.add_argument( 98 "-e", 99 action="store", 100 metavar="ENCODING", 101 help="override any in-document [e]ncoding declaration", 102) 103input_related.add_argument( 104 "-w", action="store_true", help="enable support for [W]indows code pages" 105) 106input_related.add_argument( 107 "-r", 108 action="store_true", 109 help="disable memory-mapping and use [r]ead calls instead", 110) 111input_related.add_argument( 112 "-g", 113 metavar="BYTES", 114 help="buffer size to request per call pair to XML_[G]etBuffer and read (default: 8 KiB)", 115) 116input_related.add_argument( 117 "-k", 118 action="store_true", 119 help=( 120 "when processing multiple files, [k]eep processing after first file with error" 121 ", and make the last error determine the exit code." 122 ), 123) 124 125output_related = parser.add_argument_group("output control arguments") 126output_related.add_argument( 127 "-d", action="store", metavar="DIRECTORY", help="output [d]estination directory" 128) 129output_mode = output_related.add_mutually_exclusive_group() 130output_mode.add_argument( 131 "-c", action="store_true", help="write a [c]opy of input XML, not canonical XML" 132) 133output_mode.add_argument( 134 "-m", action="store_true", help="write [m]eta XML, not canonical XML" 135) 136output_mode.add_argument( 137 "-t", action="store_true", help="write no XML output for [t]iming of plain parsing" 138) 139output_related.add_argument( 140 "-N", action="store_true", help="enable adding doctype and [n]otation declarations" 141) 142 143billion_laughs = parser.add_argument_group( 144 "amplification attack protection (e.g. billion laughs)", 145 description=( 146 "NOTE: " 147 "If you ever need to increase these values " 148 "for non-attack payload, please file a bug report." 149 ), 150) 151billion_laughs.add_argument( 152 "-a", 153 metavar="FACTOR", 154 help="set maximum tolerated [a]mplification factor (default: 100.0)", 155) 156billion_laughs.add_argument( 157 "-b", 158 metavar="BYTES", 159 help="set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)", 160) 161 162reparse_deferral = parser.add_argument_group("reparse deferral") 163reparse_deferral.add_argument( 164 "-q", 165 action="store_true", 166 help="disable reparse deferral, and allow [q]uadratic parse runtime with large tokens", 167) 168 169parser.add_argument( 170 "files", metavar="FILE", nargs="*", help="file to process (default: STDIN)" 171) 172 173info = parser.add_argument_group("info arguments") 174info = info.add_mutually_exclusive_group() 175info.add_argument( 176 "-h", "--help", action="store_true", help="show this [h]elp message and exit" 177) 178info.add_argument( 179 "-v", 180 "--version", 181 action="store_true", 182 help="show program's [v]ersion number and exit", 183) 184 185 186if __name__ == "__main__": 187 parser.print_help() 188