5757
5858
5959def add (ename , encode = None , decode = None , pattern = None , text = True , add_to_codecs = False , ** kwargs ):
60- """
61- This adds a new codec to the codecs module setting its encode and/or decode functions, eventually dynamically naming
62- the encoding with a pattern and with file handling.
60+ """ This adds a new codec to the codecs module setting its encode and/or decode functions, eventually dynamically
61+ naming the encoding with a pattern and with file handling.
6362
6463 :param ename: encoding name
6564 :param encode: encoding function or None
@@ -165,10 +164,9 @@ class StreamReader(Codec, codecs.StreamReader):
165164
166165
167166def add_map (ename , encmap , repl_char = "?" , sep = "" , ignore_case = None , no_error = False , intype = None , outype = None , ** kwargs ):
168- """
169- This adds a new mapping codec (that is, declarable with a simple character mapping dictionary) to the codecs module
170- dynamically setting its encode and/or decode functions, eventually dynamically naming the encoding with a pattern
171- and with file handling (if text is True).
167+ """ This adds a new mapping codec (that is, declarable with a simple character mapping dictionary) to the codecs
168+ module dynamically setting its encode and/or decode functions, eventually dynamically naming the encoding with
169+ a pattern and with file handling (if text is True).
172170
173171 :param ename: encoding name
174172 :param encmap: characters encoding map ; can be a dictionary of encoding maps (for use with the first capture
@@ -197,11 +195,10 @@ def add_map(ename, encmap, repl_char="?", sep="", ignore_case=None, no_error=Fal
197195
198196 def __generic_code (decode = False ):
199197 def _wrapper (param ):
200- """
201- The parameter for wrapping comes from the encoding regex pattern ; e.g.
202- [no pattern] => param will be None everytime
203- r"barbie[-_]?([1-4])$" => param could be int 1, 2, 3 or 4
204- r"^morse(|[-_]?.{3})$" => param could be None, "-ABC" (for mapping to ".-/")
198+ """ The parameter for wrapping comes from the encoding regex pattern ; e.g.
199+ [no pattern] => param will be None everytime
200+ r"barbie[-_]?([1-4])$" => param could be int 1, 2, 3 or 4
201+ r"^morse(|[-_]?.{3})$" => param could be None, "-ABC" (for mapping to ".-/")
205202
206203 In order of precedence:
207204 1. when param is a key in mapdict or mapdict is a list of encoding maps (hence in the case of "barbie...",
@@ -544,7 +541,7 @@ def ensure_str(s, encoding='utf-8', errors='strict'):
544541# make conversion functions compatible with input/output strings/bytes
545542def fix_inout_formats (f ):
546543 """ This decorator ensures that the first output of f will have the same text format as the first input (str or
547- bytes). """
544+ bytes). """
548545 @wraps (f )
549546 def _wrapper (* args , ** kwargs ):
550547 a0 = args [0 ]
@@ -562,7 +559,7 @@ def _wrapper(*args, **kwargs):
562559# alphabet generation function from a given mask
563560def get_alphabet_from_mask (mask ):
564561 """ This function generates an alphabet from the given mask. The style used is similar to Hashcat ; group keys are
565- marked with a heading "?". """
562+ marked with a heading "?". """
566563 i , alphabet = 0 , ""
567564 while i < len (mask ):
568565 c = mask [i ]
@@ -579,8 +576,7 @@ def get_alphabet_from_mask(mask):
579576
580577# generic error handling function
581578def handle_error (ename , errors , sep = "" , repl_char = "?" , repl_minlen = 1 , decode = False , item = "position" ):
582- """
583- This shortcut function allows to handle error modes given some tuning parameters.
579+ """ This shortcut function allows to handle error modes given some tuning parameters.
584580
585581 :param ename: encoding name
586582 :param errors: error handling mode
@@ -597,8 +593,7 @@ def handle_error(ename, errors, sep="", repl_char="?", repl_minlen=1, decode=Fal
597593 exec ("class %s(ValueError): pass" % exc , glob )
598594
599595 def _handle_error (token , position ):
600- """
601- This handles an encoding/decoding error according to the selected handling mode.
596+ """ This handles an encoding/decoding error according to the selected handling mode.
602597
603598 :param token: input token to be encoded/decoded
604599 :param position: token position index
@@ -661,9 +656,8 @@ def lookup(encoding):
661656
662657
663658def register (search_function , add_to_codecs = False ):
664- """
665- Register function for registering new codecs in the local registry of this module and, if required, in the native
666- codecs registry (for use with the built-in 'open' function).
659+ """ Register function for registering new codecs in the local registry of this module and, if required, in the
660+ native codecs registry (for use with the built-in 'open' function).
667661
668662 :param search_function: search function for the codecs registry
669663 :param add_to_codecs: also add the search function to the native registry
@@ -862,36 +856,43 @@ def _flag(x):
862856stopfunc .flag = _flag
863857
864858
865- def __guess (input , stop_func , depth , max_depth , codecs , result , found = (), exclude = () ):
859+ def __guess (input , stop_func , depth , max_depth , codec_categories , exclude , result , found = (), stop = True , show = False ):
866860 """ Perform a breadth-first tree search using a ranking logic to select and prune the list of codecs. """
867861 if depth > 0 and stop_func (input ):
862+ if not stop and show :
863+ s = "[*] %s: %s" % (", " .join (found ), ensure_str (input ))
864+ print (s if len (s ) <= 80 else s [:77 ] + "..." )
868865 result .append ((input , found ))
869866 return
870867 if depth >= max_depth or len (result ) > 0 :
871868 return
872- # format 1: when 'exclude' is a string, take it as the only encoding to be excluded at any depth
873- if isinstance (exclude , string_types ):
874- e = (exclude , )
875- # format 2: when 'exclude' is a tuple, consider it as the encodings to be excluded at any depth
876- elif isinstance (exclude , tuple ):
877- e = exclude
878- # format 3: when 'exclude' is a list, consider it as the list of tuples of encodings to be exlucded with the order
879- # number corresponding to the applicable depth
880- elif isinstance (exclude , list ):
881- try :
882- e = exclude [depth ]
883- if e is None :
884- e = ()
885- elif isinstance (e , string_types ):
886- e = (e , )
887- except IndexError :
888- e = ()
889- else :
890- raise ValueError ("Bad exclude format %s" % exclude )
891- for new_input , encoding in __rank (input , codecs ):
869+ # compute included and excluded codecs for this depth
870+ def __expand (items , descr = None , transform = None ):
871+ # format 1: when string, take it as the only items at any depth
872+ if isinstance (items , string_types ):
873+ r = (items , )
874+ # format 2: when tuple, consider it as a list of items at any depth
875+ elif isinstance (items , tuple ):
876+ r = items
877+ # format 3: when list, consider it as the list of tuples of items with the order number corresponding to the
878+ # applicable depth
879+ elif isinstance (items , list ):
880+ try :
881+ r = items [depth ] or ()
882+ if isinstance (r , string_types ):
883+ r = (r , )
884+ except IndexError :
885+ r = ()
886+ else :
887+ raise ValueError ("Bad %sformat %s" % (["%s " % descr , "" ][descr is None ], items ))
888+ return r if transform is None else transform (* r )
889+ # parse valid encodings, expanding included/excluded codecs
890+ c , e = __expand (codec_categories , "codec_categories" , list_encodings ), __expand (exclude , "exclude" )
891+ for new_input , encoding in __rank (input , c ):
892892 if encoding in e :
893893 continue
894- __guess (new_input , stop_func , depth + 1 , max_depth , codecs , result , found + (encoding , ))
894+ __guess (new_input , stop_func , depth + 1 , max_depth , codec_categories , exclude , result , found + (encoding , ), stop ,
895+ show )
895896
896897
897898def __rank (input , codecs ):
@@ -924,7 +925,8 @@ def __score(input, codec):
924925 yield score , new_input , encoding
925926
926927
927- def guess (input , stop_func = stopfunc .printables , max_depth = 5 , codec_categories = None , found = (), exclude = None ):
928+ def guess (input , stop_func = stopfunc .printables , max_depth = 5 , codec_categories = None , exclude = None , found = (), stop = True ,
929+ show = False ):
928930 """ Try decoding without the knowledge of the encoding(s). """
929931 if max_depth <= 0 :
930932 raise ValueError ("Depth must be a non-null positive integer" )
@@ -934,18 +936,12 @@ def guess(input, stop_func=stopfunc.printables, max_depth=5, codec_categories=No
934936 if isinstance (stop_func , string_types ):
935937 p = stop_func
936938 stop_func = lambda s : re .search (ensure_str (p ).lower (), ensure_str (s ).lower ()) is not None
937- if codec_categories is None :
938- codecs = list_encodings ()
939- elif isinstance (codec_categories , string_types ):
940- codecs = list_encodings (codec_categories )
941- elif isinstance (codec_categories , (tuple , list , set )):
942- codecs = list_encodings (* codec_categories )
943939 if len (input ) > 0 :
944940 result = []
945941 for d in range (max_depth ):
946- __guess (input , stop_func , 0 , d + 1 , codecs , result , tuple (found ), exclude or [] )
947- if len (result ) > 0 :
948- return result [ 0 ]
949- return None , None
942+ __guess (input , stop_func , 0 , d + 1 , codec_categories or [], exclude or [], result , tuple (found ), stop , show )
943+ if stop and len (result ) > 0 :
944+ return result
945+ return result
950946codecs .guess = guess
951947
0 commit comments