|  | @@ -1,109 +1,205 @@
 | 
	
		
			
				|  |  | -#!/usr/bin/python
 | 
	
		
			
				|  |  | +#!/usr/bin/python3
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  """generate word compound passwords 
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  usage: pwgen -c [number_of_phrases default=1]
 | 
	
		
			
				|  |  | -or     pwgen [len default=20] [number_of_phrases default=1]    // no conjunctions
 | 
	
		
			
				|  |  | -	-c   use conjunction
 | 
	
		
			
				|  |  | +or     pwgen [-s | -S | -n | -p | -/] [-L len default=20] [number_of_phrases default=1]  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  """
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  import random, sys
 | 
	
		
			
				|  |  | +from enum import Enum
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class Padding(Enum):
 | 
	
		
			
				|  |  | +  spaces = 0
 | 
	
		
			
				|  |  | +  dots = 1
 | 
	
		
			
				|  |  | +  punct = 2
 | 
	
		
			
				|  |  | +  numbers = 3
 | 
	
		
			
				|  |  | +  camel = 4
 | 
	
		
			
				|  |  | +  snake = 5
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class Config:
 | 
	
		
			
				|  |  | +  def __init__(self):
 | 
	
		
			
				|  |  | +    self.padding = Padding.spaces
 | 
	
		
			
				|  |  | +    self.first_punct  = False
 | 
	
		
			
				|  |  | +    self.last_punct   = False
 | 
	
		
			
				|  |  | +    self.first_number = False
 | 
	
		
			
				|  |  | +    self.last_number  = False
 | 
	
		
			
				|  |  | +    self.capitalize   = False
 | 
	
		
			
				|  |  | +    self.use_conjunctions = False
 | 
	
		
			
				|  |  | +    self.target_len = 20    # default pw len
 | 
	
		
			
				|  |  | +    self.number    = 1
 | 
	
		
			
				|  |  | +  def __repr__(self):
 | 
	
		
			
				|  |  | +    return(str(list((repr(self.padding),
 | 
	
		
			
				|  |  | +      self.first_punct, self.last_punct,
 | 
	
		
			
				|  |  | +      self.first_number, self.last_number,
 | 
	
		
			
				|  |  | +      self.capitalize,
 | 
	
		
			
				|  |  | +      self.target_len, self.number,
 | 
	
		
			
				|  |  | +      self.use_conjunctions))))
 | 
	
		
			
				|  |  | + 
 | 
	
		
			
				|  |  | +SPACE        = " "
 | 
	
		
			
				|  |  | +DOTS         = ".,"
 | 
	
		
			
				|  |  | +PUNCTS       = """ !@#$%^&*()-_=+[{]}\\|;:/?.>,<~"""
 | 
	
		
			
				|  |  | +NUMBERS      = "0123456789"
 | 
	
		
			
				|  |  | +CONJUNCTIONS =  ["and", "and a", "and the","or", "or the", "or a", 
 | 
	
		
			
				|  |  | +    "with a", "with", "with the", "by a", "by the", 
 | 
	
		
			
				|  |  | +    "on a", "on the","on", "in a", "in the", "in", 
 | 
	
		
			
				|  |  | +    "for", "for a", "for the"]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +# delete these
 | 
	
		
			
				|  |  | +number = 1
 | 
	
		
			
				|  |  | +targetLen = 20
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -targetLen = 20		# default pw len
 | 
	
		
			
				|  |  |  useConjunction = False
 | 
	
		
			
				|  |  | -number = 1
 | 
	
		
			
				|  |  | -punc = " !@#$%^&*()-_=+[{]}\|;:/?.>,<`~"
 | 
	
		
			
				|  |  | -number_punc = "0123456789"
 | 
	
		
			
				|  |  | +add_punc       = False
 | 
	
		
			
				|  |  | +add_number     = False
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def toInt(s, chkRange, errStr = "sorry, that's not a number", rangeStr = "sorry, I can't do that number"):
 | 
	
		
			
				|  |  | -	try:
 | 
	
		
			
				|  |  | -		t = int(s)
 | 
	
		
			
				|  |  | -	except:
 | 
	
		
			
				|  |  | -		print errStr
 | 
	
		
			
				|  |  | -		sys.exit()
 | 
	
		
			
				|  |  | -	if t<chkRange[0] or t>chkRange[1]:
 | 
	
		
			
				|  |  | -		print rangeStr
 | 
	
		
			
				|  |  | -		sys.exit()
 | 
	
		
			
				|  |  | -	return t
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -narg = 1
 | 
	
		
			
				|  |  | -if len(sys.argv)>1:
 | 
	
		
			
				|  |  | -	if sys.argv[1]=="-h" or sys.argv[1]=="--help":
 | 
	
		
			
				|  |  | -		print "pwgen.py  v1.3  generate passphrase; inspired by xkcd/936 and SteveGibson\n"
 | 
	
		
			
				|  |  | -		print "Usage: pwgen.py [-p | -P | -n | -N] [length [num_phrases]] "
 | 
	
		
			
				|  |  | -		print "       pwgen.py -c [num_phrases]"
 | 
	
		
			
				|  |  | -		print "       pwgen.py -h | --help \n"
 | 
	
		
			
				|  |  | -		print "     -p           pad with only spaces (for smartphone)"
 | 
	
		
			
				|  |  | -		print "     -P           pad with spaces, period and comma (for smartphone)"
 | 
	
		
			
				|  |  | -		print "     -n           pad with numbers"
 | 
	
		
			
				|  |  | -		print "     -N           pad with numbers, spaces, period, comma like -P -n"
 | 
	
		
			
				|  |  | -		print "     length       make pass phrases padded with punctuation filler; default=20"
 | 
	
		
			
				|  |  | -		print "     -c           make 2 word pass phrases with a conjuction filler"
 | 
	
		
			
				|  |  | -		print "     num_phrases  make multiple pass phrases, one per line; default=1\n"
 | 
	
		
			
				|  |  | -		sys.exit()
 | 
	
		
			
				|  |  | -	elif sys.argv[1]=="-c":	# conjunction mode
 | 
	
		
			
				|  |  | -		useConjunction = True
 | 
	
		
			
				|  |  | -		narg = narg + 1
 | 
	
		
			
				|  |  | -	elif sys.argv[1]=='-n': # number fill mode
 | 
	
		
			
				|  |  | -		punc = number_punc
 | 
	
		
			
				|  |  | -		narg = narg + 1
 | 
	
		
			
				|  |  | -	elif sys.argv[1]=='-N':
 | 
	
		
			
				|  |  | -		punc = number_punc + " ,."
 | 
	
		
			
				|  |  | -		narg = narg + 1
 | 
	
		
			
				|  |  | -	elif sys.argv[1]=="-p": # use only space
 | 
	
		
			
				|  |  | -		punc = " "
 | 
	
		
			
				|  |  | -		narg = narg + 1
 | 
	
		
			
				|  |  | -	elif sys.argv[1]=="-P":
 | 
	
		
			
				|  |  | -		punc = " .,"
 | 
	
		
			
				|  |  | -		narg = narg + 1
 | 
	
		
			
				|  |  | -	if not useConjunction:
 | 
	
		
			
				|  |  | -		if len(sys.argv)>narg:
 | 
	
		
			
				|  |  | -			targetLen = toInt(sys.argv[narg],[12,36],rangeStr = "Sorry, I can't build phrases that long")
 | 
	
		
			
				|  |  | -			narg = narg + 1
 | 
	
		
			
				|  |  | -	if len(sys.argv)>narg:
 | 
	
		
			
				|  |  | -		number = toInt(sys.argv[narg],[0,40])  # pick off the number of phrases
 | 
	
		
			
				|  |  | - 
 | 
	
		
			
				|  |  | -f = open("/usr/share/dict/words")
 | 
	
		
			
				|  |  | -#f = open("/usr/share/dict/corncob_lowercase.txt")
 | 
	
		
			
				|  |  | -d = f.read().splitlines()
 | 
	
		
			
				|  |  | -f.close()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -numWords = len(d)	
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -for i in range(number):
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	w=['','','']
 | 
	
		
			
				|  |  | -	for i in range(3):
 | 
	
		
			
				|  |  | -		w[i] = d[random.randint(0,numWords-1)]
 | 
	
		
			
				|  |  | -		while (len(w[i]) > 10) or (w[i].endswith("'s")):
 | 
	
		
			
				|  |  | -			w[i] = d[random.randint(0,numWords-1)]
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | -	whole = len(w[0])+len(w[1])
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	thisPunct = punc[int(random.random() * len(punc))] 
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	conjunctions = ["and", "and a", "and the","or", "or the", "or a", 
 | 
	
		
			
				|  |  | -		"with a", "with", "with the", "by a", "by the", 
 | 
	
		
			
				|  |  | -		"on a", "on the","on", "in a", "in the", "in", 
 | 
	
		
			
				|  |  | -		"for", "for a", "for the"]
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	if useConjunction == False:
 | 
	
		
			
				|  |  | -		if whole + 6 >= targetLen:	# if 2 words is enough
 | 
	
		
			
				|  |  | -			r = max(1,targetLen - whole)
 | 
	
		
			
				|  |  | -			pw = w[0] + thisPunct*r + w[1]
 | 
	
		
			
				|  |  | -		else:		# otherwise use 3
 | 
	
		
			
				|  |  | -			whole = whole + len(w[2])
 | 
	
		
			
				|  |  | -			r = targetLen - whole
 | 
	
		
			
				|  |  | -			if r<2:
 | 
	
		
			
				|  |  | -				r = 2
 | 
	
		
			
				|  |  | -			pw = w[0] + thisPunct*(r/2) + w[1] + thisPunct*(r-r/2) + w[2]
 | 
	
		
			
				|  |  | -	else:
 | 
	
		
			
				|  |  | -		conj = conjunctions[random.randint(0,len(conjunctions)-1)]
 | 
	
		
			
				|  |  | -		if w[1][0].lower() in ['a','A','e','E','i','I','o','O','u','U']:
 | 
	
		
			
				|  |  | -			if conj[-2:]==" a":
 | 
	
		
			
				|  |  | -				conj = conj+'n'
 | 
	
		
			
				|  |  | -		pw = w[0] + ' ' + conj + ' ' + w[1]
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	print pw
 | 
	
		
			
				|  |  | +  try:
 | 
	
		
			
				|  |  | +    t = int(s)
 | 
	
		
			
				|  |  | +  except:
 | 
	
		
			
				|  |  | +    print (errStr)
 | 
	
		
			
				|  |  | +    sys.exit()
 | 
	
		
			
				|  |  | +  if t<chkRange[0] or t>chkRange[1]:
 | 
	
		
			
				|  |  | +    print (rangeStr)
 | 
	
		
			
				|  |  | +    sys.exit()
 | 
	
		
			
				|  |  | +  return t
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def get_args(conf):
 | 
	
		
			
				|  |  | +  narg = 1
 | 
	
		
			
				|  |  | +  nargs = len(sys.argv)
 | 
	
		
			
				|  |  | +  while narg<nargs:
 | 
	
		
			
				|  |  | +    a = sys.argv[narg]
 | 
	
		
			
				|  |  | +    if a=="-h" or a=="--help" or a=="--version":
 | 
	
		
			
				|  |  | +      print ("pwgen.py  v1.3  generate passphrase; inspired by xkcd/936 and SteveGibson\n")
 | 
	
		
			
				|  |  | +      print ("""Usage: pwgen.py [-sSpPkKnNcCaMX] [-L length] [num_phrases] 
 | 
	
		
			
				|  |  | +         pwgen.py -h | --help | --version
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +       -s           pad with only spaces (default; for smartphone)
 | 
	
		
			
				|  |  | +       -S           pad with period and comma (for smartphone)
 | 
	
		
			
				|  |  | +       -p           pad with special characters
 | 
	
		
			
				|  |  | +       -P           pad with numbers
 | 
	
		
			
				|  |  | +       -k           connect the words as CamelCase
 | 
	
		
			
				|  |  | +       -K           connect the words as snake_case
 | 
	
		
			
				|  |  | +       -n           add a number at the beginning
 | 
	
		
			
				|  |  | +       -N           add a number at the end
 | 
	
		
			
				|  |  | +       -c           add a special character at the begnning
 | 
	
		
			
				|  |  | +       -C           add a special character at the end
 | 
	
		
			
				|  |  | +       -M           capitalize the words
 | 
	
		
			
				|  |  | +       -a           connect the words with a conjuction filler
 | 
	
		
			
				|  |  | +       -X           same as -MNCS (capital, periods & commas, extra number & char)
 | 
	
		
			
				|  |  | +       -L <n>       make pass phrases at least this long; default=20
 | 
	
		
			
				|  |  | +       num_phrases  make multiple pass phrases, one per line; default=1\n""")
 | 
	
		
			
				|  |  | +      sys.exit()
 | 
	
		
			
				|  |  | +    elif a[0]=='-':
 | 
	
		
			
				|  |  | +      for b in a[1:]:
 | 
	
		
			
				|  |  | +        if b=='c':
 | 
	
		
			
				|  |  | +          conf.first_punct = True
 | 
	
		
			
				|  |  | +        elif b=="C":
 | 
	
		
			
				|  |  | +          conf.last_punct = True
 | 
	
		
			
				|  |  | +        elif b=="n":
 | 
	
		
			
				|  |  | +          conf.first_number = True
 | 
	
		
			
				|  |  | +        elif b=='N': # add number
 | 
	
		
			
				|  |  | +          conf.last_number = True
 | 
	
		
			
				|  |  | +        elif b=='s':
 | 
	
		
			
				|  |  | +          conf.padding = Padding.spaces
 | 
	
		
			
				|  |  | +        elif b=='S':
 | 
	
		
			
				|  |  | +          conf.padding = Padding.dots
 | 
	
		
			
				|  |  | +        elif b=='p':
 | 
	
		
			
				|  |  | +          conf.padding = Padding.punct
 | 
	
		
			
				|  |  | +        elif b=='P':
 | 
	
		
			
				|  |  | +          conf.padding = Padding.numbers
 | 
	
		
			
				|  |  | +        elif b=='k':
 | 
	
		
			
				|  |  | +          conf.padding = Padding.camel
 | 
	
		
			
				|  |  | +        elif b=='K':
 | 
	
		
			
				|  |  | +          conf.padding = Padding.snake
 | 
	
		
			
				|  |  | +        elif b=='M': 
 | 
	
		
			
				|  |  | +          conf.capitalize = True  
 | 
	
		
			
				|  |  | +        elif b=='X':
 | 
	
		
			
				|  |  | +          conf.capitalize = True
 | 
	
		
			
				|  |  | +          conf.last_punct = True
 | 
	
		
			
				|  |  | +          conf.last_number = True
 | 
	
		
			
				|  |  | +          conf.padding = Padding.dots
 | 
	
		
			
				|  |  | +        elif b=="a":
 | 
	
		
			
				|  |  | +          conf.use_conjunctions = True;
 | 
	
		
			
				|  |  | +        elif b=="L":
 | 
	
		
			
				|  |  | +          narg = narg + 1
 | 
	
		
			
				|  |  | +          if narg < nargs:
 | 
	
		
			
				|  |  | +            conf.target_len = toInt(sys.argv[narg],[12,66],rangeStr = "Sorry, I can't build phrases that long")
 | 
	
		
			
				|  |  | +      
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +      if narg < nargs:
 | 
	
		
			
				|  |  | +        conf.number = toInt(sys.argv[narg],[0,40],rangeStr = "Sorry, I can't print that many; try a smaller number")  # pick off the number of phrases
 | 
	
		
			
				|  |  | +    narg = narg + 1
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def load_words():
 | 
	
		
			
				|  |  | +  f = open("/usr/share/dict/words")
 | 
	
		
			
				|  |  | +  #f = open("/usr/share/dict/corncob_lowercase.txt")
 | 
	
		
			
				|  |  | +  d = f.read().splitlines()
 | 
	
		
			
				|  |  | +  f.close()
 | 
	
		
			
				|  |  | +  d = list(filter(lambda x : len(x)<10 and not x.endswith("'s"), d))
 | 
	
		
			
				|  |  | +  return d
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def get_word(conf, data):
 | 
	
		
			
				|  |  | +  m = data[random.randint(0,len(data)-1)]
 | 
	
		
			
				|  |  | +  if conf.capitalize or conf.padding == Padding.camel:
 | 
	
		
			
				|  |  | +    m = m.capitalize()
 | 
	
		
			
				|  |  | +  return m
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def pwgen(conf, data):
 | 
	
		
			
				|  |  | +  w = [get_word(conf, data)]
 | 
	
		
			
				|  |  | +  nExtraChars = conf.first_punct+conf.last_punct+conf.first_number+conf.last_number
 | 
	
		
			
				|  |  | +  # TODO: we need a length estimator for this loop
 | 
	
		
			
				|  |  | +  while sum(len(a) for a in w) + (len(w)-1 if not conf.padding==Padding.camel else 0) + nExtraChars < conf.target_len:
 | 
	
		
			
				|  |  | +    if conf.use_conjunctions == True:
 | 
	
		
			
				|  |  | +      c = CONJUNCTIONS[random.randint(0,len(CONJUNCTIONS)-1)]
 | 
	
		
			
				|  |  | +      w.extend(c.split())
 | 
	
		
			
				|  |  | +    w.append(get_word(conf, data))    
 | 
	
		
			
				|  |  | +    if w[-2] == 'a' and w[-1][0] in "aeiouAEIOU":
 | 
	
		
			
				|  |  | +      w[-2] = "an"
 | 
	
		
			
				|  |  | +  # change so that two-word conjunctions are multiple list elements
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  if conf.padding == Padding.camel:
 | 
	
		
			
				|  |  | +    ret = "".join(w)
 | 
	
		
			
				|  |  | +  elif conf.padding == Padding.spaces:
 | 
	
		
			
				|  |  | +    ret = SPACE.join(w)
 | 
	
		
			
				|  |  | +  elif conf.padding == Padding.dots:
 | 
	
		
			
				|  |  | +    p = DOTS[random.randint(0,len(DOTS)-1)]
 | 
	
		
			
				|  |  | +    ret = p.join(w)
 | 
	
		
			
				|  |  | +  elif conf.padding == Padding.numbers:
 | 
	
		
			
				|  |  | +    n = NUMBERS[random.randint(0,len(NUMBERS)-1)]
 | 
	
		
			
				|  |  | +    ret = n.join(w)
 | 
	
		
			
				|  |  | +  elif conf.padding == Padding.punct:
 | 
	
		
			
				|  |  | +    p = PUNCTS[random.randint(0,len(PUNCTS)-1)]
 | 
	
		
			
				|  |  | +    ret = p.join(w)
 | 
	
		
			
				|  |  | +  elif conf.padding == Padding.snake:
 | 
	
		
			
				|  |  | +    ret = '_'.join(w)  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  n = NUMBERS[random.randint(0,len(NUMBERS)-1)]
 | 
	
		
			
				|  |  | +  if conf.first_number:
 | 
	
		
			
				|  |  | +    ret = n + ret
 | 
	
		
			
				|  |  | +  if conf.last_number:
 | 
	
		
			
				|  |  | +    ret = ret + n    
 | 
	
		
			
				|  |  | +  p = PUNCTS[random.randint(0,len(PUNCTS)-1)]
 | 
	
		
			
				|  |  | +  if conf.first_punct:
 | 
	
		
			
				|  |  | +    ret = p + ret
 | 
	
		
			
				|  |  | +  if conf.last_punct:
 | 
	
		
			
				|  |  | +    ret = ret + p    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return ret
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +if __name__ == "__main__":
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  conf = Config()
 | 
	
		
			
				|  |  | +  get_args(conf)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  data = load_words()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for i in range(conf.number):
 | 
	
		
			
				|  |  | +    pw = pwgen(conf, data)
 | 
	
		
			
				|  |  | +    print(pw)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  """ 
 | 
	
	
		
			
				|  | @@ -112,5 +208,46 @@ although this
 | 
	
		
			
				|  |  |  read n; i="1"; while [ $i -le $n ]; do cat /etc/dictionaries-common/words | shuf | head -1; i=$(( $i + 1 )); done
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  will do the job almost as well
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +NEW PLAN
 | 
	
		
			
				|  |  | +  -s spaces (default)
 | 
	
		
			
				|  |  | +  -S comma+period
 | 
	
		
			
				|  |  | +  -p punctuation
 | 
	
		
			
				|  |  | +  -P number 
 | 
	
		
			
				|  |  | +  -k CamelCase 
 | 
	
		
			
				|  |  | +  -K SnakeCase (only one of these 6 allowed)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  -c special char at beginning
 | 
	
		
			
				|  |  | +  -C special char at end
 | 
	
		
			
				|  |  | +  -n number at beginning
 | 
	
		
			
				|  |  | +  -N number at end
 | 
	
		
			
				|  |  | +  -M capitalize all words
 | 
	
		
			
				|  |  | +  -a fill with conjunctions
 | 
	
		
			
				|  |  | +  -X equivalent to -SCNM
 | 
	
		
			
				|  |  | +  -L <n> length
 | 
	
		
			
				|  |  | +  <n> number of samples
 | 
	
		
			
				|  |  | +  -k -a not recommended
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  -s    dog cat mouse
 | 
	
		
			
				|  |  | +  -S    dog.cat.mouse
 | 
	
		
			
				|  |  | +  -p    dog#cat#mouse
 | 
	
		
			
				|  |  | +  -P    dog4cat4mouse
 | 
	
		
			
				|  |  | +  -c    !dog cat mouse
 | 
	
		
			
				|  |  | +  -C    dog cat mouse#
 | 
	
		
			
				|  |  | +  -n    4dog cat mouse
 | 
	
		
			
				|  |  | +  -N    dog cat mouse4
 | 
	
		
			
				|  |  | +  -cnCN $4dog cat mouse4$
 | 
	
		
			
				|  |  | +  -M    Dog Cat Mouse
 | 
	
		
			
				|  |  | +  -k    DogCatMouse
 | 
	
		
			
				|  |  | +  -kN   DogCatMouse2
 | 
	
		
			
				|  |  | +  -K    dog_cat_mouse
 | 
	
		
			
				|  |  | +  -KMN  Dog_Cat_Mouse3
 | 
	
		
			
				|  |  | +  -a    dog and a cat with mouse
 | 
	
		
			
				|  |  | +  -Sa   dog.and.a.cat.with.mouse
 | 
	
		
			
				|  |  | +  -pa   dog#and#a#cat#with#mouse
 | 
	
		
			
				|  |  | +  -paCN dog^and^a^cat^with^mouse5%
 | 
	
		
			
				|  |  | +  -X    Dog.Cat.Mouse6&
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +leet encoder a->4A e->3 i->I o->0 s->5$ t->7 
 | 
	
		
			
				|  |  |  """
 | 
	
		
			
				|  |  |  
 |