Opts.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. import sys, getopt
  19. import mmgen.config as g
  20. def usage(hd):
  21. print "USAGE: %s %s" % (hd['prog_name'], hd['usage'])
  22. sys.exit(2)
  23. def print_version_info(): # MMGen only
  24. print """
  25. '{g.prog_name}' version {g.version}. Part of the {g.proj_name} suite.
  26. Copyright (C) {g.Cdates} by {g.author} {g.email}.
  27. """.format(g=g).strip()
  28. def print_help(help_data):
  29. pn = help_data['prog_name']
  30. pn_len = str(len(pn)+2)
  31. print (" %-"+pn_len+"s %s") % (pn.upper()+":", help_data['desc'].strip())
  32. print (" %-"+pn_len+"s %s %s")%("USAGE:", pn, help_data['usage'].strip())
  33. sep = "\n "
  34. print " OPTIONS:"+sep+"%s" % sep.join(help_data['options'].strip().split("\n"))
  35. if "notes" in help_data:
  36. print " %s" % "\n ".join(help_data['notes'][1:-1].split("\n"))
  37. def process_opts(argv,help_data,short_opts,long_opts):
  38. if len(argv) == 2 and argv[1] == '--version': # MMGen only!
  39. print_version_info(); sys.exit()
  40. if g.debug:
  41. print "Short opts: %s" % repr(short_opts)
  42. print "Long opts: %s" % repr(long_opts)
  43. long_opts = [i.replace("_","-") for i in long_opts]
  44. try: cl_opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
  45. except getopt.GetoptError as err:
  46. print str(err); sys.exit(2)
  47. opts,short_opts_l = {},[]
  48. for i in short_opts:
  49. if i == ":": short_opts_l[-1] += i
  50. else: short_opts_l += i
  51. for opt, arg in cl_opts:
  52. if opt in ("-h","--help"): print_help(help_data); sys.exit()
  53. elif opt[:2] == "--" and opt[2:] in long_opts:
  54. opts[opt[2:].replace("-","_")] = True
  55. elif opt[:2] == "--" and opt[2:]+"=" in long_opts:
  56. opts[opt[2:].replace("-","_")] = arg
  57. elif opt[0] == "-" and opt[1] in short_opts_l:
  58. opts[long_opts[short_opts_l.index(opt[1:])].replace("-","_")] = True
  59. elif opt[0] == "-" and opt[1:]+":" in short_opts_l:
  60. opts[long_opts[short_opts_l.index(opt[1:]+":")][:-1].replace("-","_")] = arg
  61. else: assert False, "Invalid option"
  62. if g.debug: print "User-selected options: %s" % repr(opts)
  63. return opts,args
  64. def parse_opts(argv,help_data):
  65. lines = help_data['options'].strip().split("\n")
  66. import re
  67. pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{1,64})(=| )(.+)"
  68. rep = r"-{0}, --{1}{w}{3}"
  69. opt_data = [list(m.groups()) for m in [re.match(pat,l) for l in lines] if m]
  70. # for o in opt_data: print o
  71. # sys.exit()
  72. for d in opt_data:
  73. if d[2] == " ": d[2] = ""
  74. short_opts = "".join([d[0]+d[2].replace("=",":") for d in opt_data])
  75. long_opts = [d[1].replace("-","_")+d[2] for d in opt_data]
  76. help_data['options'] = "\n".join(
  77. [rep.format(w=" ", *m.groups())
  78. if m else k for m,k in [(re.match(pat,l),l) for l in lines]]
  79. )
  80. opts,infiles = process_opts(argv,help_data,short_opts,long_opts)
  81. # check_opts() doesn't touch opts[]
  82. if not check_opts(opts,long_opts): sys.exit(1)
  83. # If unset, set these to the default values in mmgen.config:
  84. for v in g.cl_override_vars:
  85. if v in opts: typeconvert_override_var(opts,v)
  86. else: opts[v] = eval("g."+v)
  87. if g.debug: print "processed opts: %s" % opts
  88. return opts,infiles
  89. def show_opts_and_cmd_args(opts,cmd_args):
  90. print "Processed options: %s" % repr(opts)
  91. print "Cmd args: %s" % repr(cmd_args)
  92. # Everything below here is MMGen-specific:
  93. from mmgen.util import msg,check_infile
  94. def check_opts(opts,long_opts):
  95. for opt,val in opts.items():
  96. what = "parameter for '--%s' option" % opt.replace("_","-")
  97. # Check for file existence and readability
  98. if opt in ('keys_from_file','all_keys_from_file','addrlist',
  99. 'passwd_file','keysforaddrs'):
  100. check_infile(val) # exits on error
  101. continue
  102. if opt == 'outdir':
  103. what = "output directory"
  104. import os
  105. if os.path.isdir(val):
  106. if os.access(val, os.W_OK|os.X_OK):
  107. opts[opt] = os.path.normpath(val)
  108. else:
  109. msg("Requested %s '%s' is unwritable by you" % (what,val))
  110. return False
  111. else:
  112. msg("Requested %s '%s' does not exist" % (what,val))
  113. return False
  114. elif opt == 'label':
  115. if len(val) > g.max_wallet_label_len:
  116. msg("Label must be %s characters or less" %
  117. g.max_wallet_label_len)
  118. return False
  119. for ch in list(val):
  120. chs = g.wallet_label_symbols
  121. if ch not in chs:
  122. msg("'%s': ERROR: label contains an illegal symbol" % val)
  123. msg("The following symbols are permitted:\n%s" % "".join(chs))
  124. return False
  125. elif opt == 'export_incog_hidden' or opt == 'from_incog_hidden':
  126. try:
  127. if opt == 'export_incog_hidden':
  128. outfile,offset = val.split(",")
  129. else:
  130. outfile,offset,seed_len = val.split(",")
  131. except:
  132. msg("'%s': invalid %s" % (val,what))
  133. return False
  134. try:
  135. o = int(offset)
  136. except:
  137. msg("'%s': invalid 'o' %s (not an integer)" % (offset,what))
  138. return False
  139. if o < 0:
  140. msg("'%s': invalid 'o' %s (less than zero)" % (offset,what))
  141. return False
  142. if opt == 'from_incog_hidden':
  143. try:
  144. sl = int(seed_len)
  145. except:
  146. msg("'%s': invalid 'l' %s (not an integer)" % (sl,what))
  147. return False
  148. if sl not in g.seed_lens:
  149. msg("'%s': invalid 'l' %s (valid choices: %s)" %
  150. (sl,what," ".join(str(i) for i in g.seed_lens)))
  151. return False
  152. import os, stat
  153. try: mode = os.stat(outfile).st_mode
  154. except:
  155. msg("Unable to stat requested %s '%s'" % (what,outfile))
  156. return False
  157. if not (stat.S_ISREG(mode) or stat.S_ISBLK(mode)):
  158. msg("Requested %s '%s' is not a file or block device" %
  159. (what,outfile))
  160. return False
  161. ac,m = (os.W_OK,"writ") \
  162. if "export_incog_hidden" in opts else (os.R_OK,"read")
  163. if not os.access(outfile, ac):
  164. msg("Requested %s '%s' is un%sable by you" % (what,outfile,m))
  165. return False
  166. elif opt == 'from_brain':
  167. try:
  168. l,p = val.split(",")
  169. except:
  170. msg("'%s': invalid %s" % (val,what))
  171. return False
  172. try:
  173. int(l)
  174. except:
  175. msg("'%s': invalid 'l' %s (not an integer)" % (l,what))
  176. return False
  177. if int(l) not in g.seed_lens:
  178. msg("'%s': invalid 'l' %s. Options: %s" %
  179. (l, what, ", ".join([str(i) for i in g.seed_lens])))
  180. return False
  181. if p not in g.hash_presets:
  182. hps = ", ".join([i for i in sorted(g.hash_presets.keys())])
  183. msg("'%s': invalid 'p' %s. Options: %s" % (p, what, hps))
  184. return False
  185. elif opt == 'seed_len':
  186. if val not in g.seed_lens:
  187. msg("'%s': invalid %s. Options: %s"
  188. % (val,what,", ".join([str(i) for i in g.seed_lens])))
  189. return False
  190. elif opt == 'hash_preset':
  191. if val not in g.hash_presets:
  192. msg("'%s': invalid %s. Options: %s"
  193. % (val,what,", ".join(sorted(g.hash_presets.keys()))))
  194. return False
  195. elif opt == 'usr_randchars':
  196. try: v = int(val)
  197. except:
  198. msg("'%s': invalid value for %s (not an integer)" % (val,what))
  199. return False
  200. if v != 0 and not (g.min_urandchars <= v <= g.max_urandchars):
  201. msg("'%s': invalid %s (must be >= %s and <= %s (or zero))"
  202. % (v,what,g.min_urandchars,g.max_urandchars))
  203. return False
  204. else:
  205. if g.debug: print "check_opts(): No test for opt '%s'" % opt
  206. return True
  207. def typeconvert_override_var(opts,opt):
  208. vtype = type(eval("g."+opt))
  209. if g.debug: print "Override opt: %-15s [%s]" % (opt,vtype)
  210. if vtype == int: f,t = int,"an integer"
  211. elif vtype == str: f,t = str,"a string"
  212. elif vtype == float: f,t = float,"a float"
  213. try:
  214. opts[opt] = f(opts[opt])
  215. except:
  216. msg("'%s': invalid parameter for '--%s' option (not %s)" %
  217. (opts[opt],opt.replace("_","-"),t))
  218. sys.exit(1)