Created
October 29, 2024 18:05
-
-
Save mej/e5ed8726f3561eea8b2db9ccb22262a0 to your computer and use it in GitHub Desktop.
Python script to create Bash hashmaps for, and/or provide translations among, `errno`, symbolic error identifiers, and `strerror()`-provided error strings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/python3 | |
| ### -*- Mode: Python; fill-column: 132; comment-auto-fill-only-comments: t; tab-width: 4; eval: (local-set-key "\C-i" 'indent-according-to-mode); eval: (auto-fill-mode 1); -*- | |
| # | |
| # Map `errno` names or codes to num+name+strerror tuples and/or generate Bash | |
| # arrays that do the same mappings. For use in MEJSH, NHC, and/or daily life. | |
| # | |
| # Michael Jennings <[email protected]> | |
| # 23 Sep 2024 | |
| # | |
| import argparse as ap | |
| import errno | |
| import os | |
| import sys | |
| global verbose | |
| ### Function Definitions | |
| def error_resolve(error: str) -> list: | |
| global verbose | |
| if error.isdigit(): | |
| num = int(error) | |
| else: | |
| error = error.upper() | |
| try: | |
| if not errno.__dict__.__contains__(error): | |
| if errno.__dict__.__contains__("E" + error): | |
| error = 'E' + error | |
| except: | |
| pass | |
| try: | |
| num = int(errno.__dict__[error]) | |
| except KeyError: | |
| print("Invalid errno value: %s" % (error)) | |
| raise KeyError | |
| message = os.strerror(num) | |
| symname = errno.errorcode[num] | |
| if verbose >= 1: | |
| print('%s(): Resolved "%s" to errno tuple: [ num = %d, symname = "%s", message = "%s" ]' % ('error_resolve', error, num, symname, message), file=sys.stderr) | |
| return [num, symname, message] | |
| def main() -> int: | |
| global verbose | |
| codes = [] | |
| errinfo = {} | |
| maxnum = 0 | |
| maxsym = 0 | |
| ### Command-Line Arguments | |
| parser = ap.ArgumentParser(prog='errno', description='Resolve errno numeric constants, symbolic names, and canonical messages in various ways', | |
| allow_abbrev=True, epilog='', fromfile_prefix_chars='@', prefix_chars='-+') # NOPE: argument_default=ap.SUPPRESS | |
| parser.add_argument('--version', '-V', action='version', version='%(prog)s 0.2') | |
| parser.add_argument('--verbose', '-v', action='count', default=0) | |
| parser.add_argument('--bash', '-b', action=ap.BooleanOptionalAction, default=False, help='Output shell code to generate assoc. arrays (maps/dicts) in Bash') | |
| parser.add_argument('--sort', '-s', action=ap.BooleanOptionalAction, default=True, dest='sorted', help='Sort output lines by numeric `errno` value') | |
| parser.add_argument('--symsort', '-S', action=ap.BooleanOptionalAction, default=False, help='Sort output lines by symbolic error names (e.g., EPERM)') | |
| parser.add_argument('--tabs', '-t', action=ap.BooleanOptionalAction, default=False, help='Generate TSV output (format: code<TAB>symbol<TAB>strerror<EOL>)') | |
| parser.add_argument('errors', nargs='*') | |
| opts = parser.parse_args() | |
| verbose = opts.verbose | |
| args = opts.errors | |
| # Remove the script/command path from the arg list. | |
| #args = sys.argv | |
| #args.pop(0) | |
| #print(args) | |
| if len(args) <= 0: | |
| args = sorted(errno.errorcode) | |
| for arg in args: | |
| if verbose >= 2: | |
| print('Looping over %d args; now processing "%s"' % (args.__len__(), arg), file=sys.stderr) | |
| try: | |
| (e, c, m) = error_resolve(str(arg)) | |
| except KeyError: | |
| continue | |
| if opts.symsort: | |
| codes.append(c) | |
| errinfo[c] = (e, c, m) | |
| else: | |
| codes.append(e) | |
| errinfo[e] = (e, c, m) | |
| maxnum = max(maxnum, len(str(e))) | |
| maxsym = max(maxsym, len(c)) | |
| if opts.sorted or opts.symsort: | |
| codes.sort() | |
| if opts.bash: | |
| sh_err = "typeset -A ERRNO=(\n" | |
| sh_str = "typeset -A STRERR=(\n" | |
| for key in codes: | |
| (e, c, m) = errinfo[key] | |
| if opts.bash: | |
| sh_err += (" [%d]='%s' ['%s']=%d\n" % (e, c, c, e)) | |
| sh_str += (" ['%s']='%s' ['%s']='%s'\n" % (e, m, c, m)) | |
| elif opts.tabs: | |
| print("%d\t%s\t%s" % (e, c, m)) | |
| else: | |
| fmt = '%' + str(maxnum) + 'd %-' + str(maxsym) + 's => "%s"' | |
| print(fmt % (e, c, m)) | |
| if opts.bash: | |
| sh_err += ")" | |
| sh_str += ")" | |
| #print("%s\n%s\n\n" % (sh_err, sh_str)) | |
| print("%s %s\n\n" % (sh_err, sh_str.removeprefix('typeset -A '))) | |
| return 0 | |
| if __name__ == '__main__': | |
| sys.exit(main()) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is the first time I've ever attempted to write something useful, by myself, from scratch, in Python. Please be gentle!
๐ ๐