#!/usr/bin/env python3 import argparse DESC = """roun is a program that takes numbers and turns them into human-readable names, and vice versa. A 'roun' is the human-readable name. For example: roun = hidor-kahih ip = 68.107.97.20 hex = 0x446b6114 int = 1147887892 The names are inspired by proquints . Currently the algorithm is the same, but I would like to modify the algorithm at some point to be less odd than what proquints currently is. When I get time. """ def _char_list_to_dict(char_list): return {c: k for (c, k) in zip(char_list, range(len(char_list)))} UINT_TO_CONSONANT = "bdfghjklmnprstvz" UINT_TO_VOWEL = "aiou" CONSONANT_TO_UINT = _char_list_to_dict(UINT_TO_CONSONANT) VOWEL_TO_UINT = _char_list_to_dict(UINT_TO_VOWEL) MASK_LAST4 = 0xF MASK_LAST2 = 0x3 CHARS_PER_CHUNK = 5 def _uint16_to_roun(uint16_val): val = uint16_val res = ["?"] * CHARS_PER_CHUNK for i in range(CHARS_PER_CHUNK): if i & 1: res[-i - 1] = UINT_TO_VOWEL[val & MASK_LAST2] val >>= 2 else: res[-i - 1] = UINT_TO_CONSONANT[val & MASK_LAST4] val >>= 4 return "".join(res) def uint_to_roun(uint_val, separator="-"): """Convert 32-bit integer value into corresponding roun string identifier. >>> uint_to_roun(0x7F000001, '-') lusab-babad :param uint_val: 32-bit integer value to encode :param separator: string to separate character rounets :return: roun string identifier """ if uint_val < 0 or uint_val > 0xFFFFFFFF: raise ValueError("uint_val should be in range 0-0xFFFFFFFF") return _uint16_to_roun(uint_val >> 16) + separator + _uint16_to_roun(uint_val) def roun_to_uint(roun): """Convert roun string identifier into corresponding 32-bit integer value. >>> hex(roun_to_uint('lusab-babad')) '0x7F000001' :param roun: roun string identifier to decode :return: 32-bit integer representation of the roun encoded value """ nchar = len(roun) if nchar < 10 or nchar > 11: raise ValueError("roun should be in form of two rounets + optional separator") res = 0 for i, c in enumerate(roun): mag = CONSONANT_TO_UINT.get(c) if mag is not None: res <<= 4 res += mag else: mag = VOWEL_TO_UINT.get(c) if mag is not None: res <<= 2 res += mag elif i != 5: raise ValueError("bad roun format") return res def ip2uint_str(ipv4_str): """Convert IPv4 string to 32-bit integer value""" parts = ipv4_str.split(".") if len(parts) != 4: raise ValueError( "Expected IPv4 address in form A.B.C.D, got {}".format(ipv4_str) ) ip = [0] * 4 for i, part in enumerate(parts): try: int_part = int(part) except ValueError: raise ValueError("Part {} of IPv4 address is not an integer".format(i)) if int_part < 0 or int_part > 255: raise ValueError("Part {} of IPv4 address is not in range 0-255".format(i)) ip[i] = int_part return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3] def uint_to_ip_str(uint_val): "Covert 32-bit integer value to IPv4 string" return "{}.{}.{}.{}".format( (uint_val >> 24) & 0xFF, (uint_val >> 16) & 0xFF, (uint_val >> 8) & 0xFF, uint_val & 0xFF, ) def uint_to_roun_str(uint_str, separator="-"): return uint_to_roun(int(uint_str), separator) def roun_to_uint_str(roun): return str(roun_to_uint(roun)) def hex2roun_str(hex_str, separator="-"): return uint_to_roun(int(hex_str, 16), separator) def roun2hex_str(roun): return hex(roun_to_uint(roun)) def convert(str_val, target=None): """Convert between roun, integer, hex or IPv4 string representations. Tries to guess the representation from input. :param str_val: input representation (string) :return: output representation (string) """ if target is not None and target not in {"uint", "hex", "ip"}: raise ValueError("Convert target should be one of: uint, hex, ip") if target == "uint": return roun_to_uint_str(str_val) if target == "hex": return roun2hex_str(str_val) if target == "ip": return uint_to_ip_str(roun_to_uint(str_val)) # try to guess the representation try: return roun_to_uint_str(str_val) except ValueError: pass try: return uint_to_roun_str(str_val) except ValueError: pass try: return hex2roun_str(str_val) except ValueError: pass try: return uint_to_roun_str(ip2uint_str(str_val)) except ValueError: pass raise ValueError("Unrecognized input format: {}".format(str_val)) if __name__ == "__main__": parser = argparse.ArgumentParser( description=DESC, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "-n", "--uint", action="store_true", help="convert from roun to 32-bit integer", required=False, ) parser.add_argument( "-x", "--hex", action="store_true", help="convert from roun to hexadecimal", required=False, ) parser.add_argument( "-i", "--ip", action="store_true", help="convert from roun to IPv4", required=False, ) parser.add_argument( "val", nargs="?", type=str, default=None, help="value to convert (if not specified, " "IP address of the current host is printed)", ) args = parser.parse_args() target = None if args.uint: target = "uint" elif args.hex: target = "hex" elif args.ip: target = "ip" res = convert(args.val, target) print("{}".format(res))