Browse Source

Improved options handling, some files moved and renamed.

Users who are upgrading must delete the 'opt' directory in their previous
installation before installing.  On a Linux system, this is typically
located at:

	/usr/local/lib/python2.7/dist-packages/mmgen/opt
philemon 10 years ago
parent
commit
c7056a7ba2

+ 10 - 9
mmgen/addr.py

@@ -30,6 +30,7 @@ from mmgen.bitcoin import numtowif
 from mmgen.util import *
 from mmgen.tx import is_mmgen_idx,is_mmgen_seed_id,is_btc_addr,is_wif,get_wif2addr_f
 import mmgen.config as g
+import mmgen.opt as opt
 
 addrmsgs = {
 	'addrfile_header': """
@@ -60,13 +61,13 @@ def test_for_keyconv():
 	return True
 
 
-def generate_addrs(seed, addrnums, opts):
+def generate_addrs(seed, addrnums):
 
 	from util import make_chksum_8
 	seed_id = make_chksum_8(seed) # Must do this before seed gets clobbered
 
-	if 'a' in opts['gen_what']:
-		if g.no_keyconv or test_for_keyconv() == False:
+	if 'a' in opt.gen_what:
+		if opt.no_keyconv or test_for_keyconv() == False:
 			msg("Using (slow) internal ECDSA library for address generation")
 			from mmgen.bitcoin import privnum2addr
 			keyconv = False
@@ -81,7 +82,7 @@ def generate_addrs(seed, addrnums, opts):
 		'ka': ('key/address pair','s'),
 		'k':  ('key','s'),
 		'a':  ('address','es')
-	}[opts['gen_what']]
+	}[opt.gen_what]
 
 	from mmgen.addr import AddrInfoEntry,AddrInfo
 
@@ -89,7 +90,7 @@ def generate_addrs(seed, addrnums, opts):
 		seed = sha512(seed).digest()
 		num += 1 # round
 
-		if g.debug: print "Seed round %s: %s" % (num, hexlify(seed))
+		if opt.debug: print "Seed round %s: %s" % (num, hexlify(seed))
 		if num != addrnums[pos]: continue
 
 		pos += 1
@@ -103,20 +104,20 @@ def generate_addrs(seed, addrnums, opts):
 		sec = sha256(sha256(seed).digest()).hexdigest()
 		wif = numtowif(int(sec,16))
 
-		if 'a' in opts['gen_what']:
+		if 'a' in opt.gen_what:
 			if keyconv:
 				e.addr = check_output([keyconv, wif]).split()[1]
 			else:
 				e.addr = privnum2addr(int(sec,16))
 
-		if 'k' in opts['gen_what']: e.wif = wif
-		if 'b16' in opts: e.sec = sec
+		if 'k' in opt.gen_what: e.wif = wif
+		if opt.b16: e.sec = sec
 
 		out.append(e)
 
 	m = w[0] if t_addrs == 1 else w[0]+w[1]
 	qmsg("\r%s: %s %s generated%s" % (seed_id,t_addrs,m," "*15))
-	a = AddrInfo(has_keys='k' in opts['gen_what'])
+	a = AddrInfo(has_keys='k' in opt.gen_what)
 	a.initialize(seed_id,out)
 	return a
 

+ 37 - 35
mmgen/config.py

@@ -21,23 +21,38 @@ config.py:  Constants and configuration options for the MMGen suite
 """
 
 import sys, os
-prog_name = os.path.basename(sys.argv[0])
 
-author = "Philemon"
-email = "<mmgen-py@yandex.com>"
-Cdates = '2013-2015'
-version = '0.7.9'
+# Variables - these might be altered at runtime:
 
-quiet,verbose,no_keyconv = False,False,False
+user_entropy   = ""
+hash_preset    = '3'
+usr_randchars  = 30
+use_urandchars = False
 
-min_screen_width = 80
-max_tx_comment_len = 72
+# returns None if env var unset
+debug                = os.getenv("MMGEN_DEBUG")
+no_license           = os.getenv("MMGEN_NOLICENSE")
+bogus_wallet_data    = os.getenv("MMGEN_BOGUS_WALLET_DATA")
+disable_hold_protect = os.getenv("MMGEN_DISABLE_HOLD_PROTECT")
 
 from decimal import Decimal
 tx_fee        = Decimal("0.00005")
 max_tx_fee    = Decimal("0.01")
 
-proj_name     = "MMGen"
+seed_len     = 256
+http_timeout = 30
+
+# Constants - these don't change at runtime
+
+proj_name = "MMGen"
+prog_name = os.path.basename(sys.argv[0])
+author    = "Philemon"
+email     = "<mmgen-py@yandex.com>"
+Cdates    = '2013-2015'
+version   = '0.7.9'
+
+min_screen_width = 80
+max_tx_comment_len = 72
 
 wallet_ext    = "mmdat"
 seed_ext      = "mmseed"
@@ -48,49 +63,37 @@ incog_hex_ext = "mmincox"
 
 seedfile_exts = wallet_ext, seed_ext, mn_ext, brain_ext, incog_ext
 
-rawtx_ext    = "raw"
-sigtx_ext    = "sig"
-addrfile_ext = "addrs"
+rawtx_ext           = "raw"
+sigtx_ext           = "sig"
+addrfile_ext        = "addrs"
 addrfile_chksum_ext = "chk"
-keyfile_ext  = "keys"
-keyaddrfile_ext  = "akeys"
-mmenc_ext    = "mmenc"
+keyfile_ext         = "keys"
+keyaddrfile_ext     = "akeys"
+mmenc_ext           = "mmenc"
 
 default_wl    = "electrum"
 #default_wl    = "tirosh"
 
-# Global value sets user opt
-dfl_vars = "seed_len","hash_preset"
+# If user opt is set, different global opt is set to 'True'
+usr_set_vars = { "usr_randchars": "use_urandchars" }
 
-# User opt sets global value
-usr_set_vars = "no_keyconv","verbose","quiet","usr_randchars"
+# Global value sets user opt
+dfl_vars = "seed_len","hash_preset","usr_randchars","debug"
 
 seed_lens = 128,192,256
-seed_len  = 256
 
 mn_lens = [i / 32 * 3 for i in seed_lens]
 
-http_timeout = 30
-
 keyconv_exec = "keyconv"
 
-# returns None if env var unset
-debug             = os.getenv("MMGEN_DEBUG")
-no_license        = os.getenv("MMGEN_NOLICENSE")
-bogus_wallet_data = os.getenv("MMGEN_BOGUS_WALLET_DATA")
-disable_hold_protect = os.getenv("MMGEN_DISABLE_HOLD_PROTECT")
-
-mins_per_block = 8.5
+mins_per_block   = 8.5
 passwd_max_tries = 5
 
-usr_randchars = 30
 max_urandchars,min_urandchars = 80,10
-use_urandchars = False
 
-salt_len    = 16
-aesctr_iv_len  = 16
+salt_len      = 16
+aesctr_iv_len = 16
 
-hash_preset = '3'
 hash_presets = {
 #   Scrypt params:
 #   ID    N   p  r
@@ -114,7 +117,6 @@ max_addr_label_len = 32
 wallet_label_symbols = addr_label_symbols
 max_wallet_label_len = 32
 
-user_entropy = ""
 #addr_label_punc = ".","_",",","-"," ","(",")"
 #addr_label_symbols = tuple(ascii_letters + digits) + addr_label_punc
 #wallet_label_punc = addr_label_punc

+ 45 - 46
mmgen/crypto.py

@@ -25,6 +25,7 @@ from binascii import hexlify
 from hashlib import sha256
 
 import mmgen.config as g
+import mmgen.opt as opt
 from mmgen.util import *
 from mmgen.term import get_char
 
@@ -77,7 +78,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
 		if compare_checksums(chk2,"of decrypted seed",seed_id,"in header"):
 			qmsg("Passphrase is OK")
 		else:
-			if not g.debug:
+			if not opt.debug:
 				msg_r("Checking key ID...")
 				if compare_checksums(chk1, "of key", key_id, "in header"):
 					msg("Key ID is correct but decryption of seed failed")
@@ -89,7 +90,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
 #	else:
 #		qmsg("Generated IDs (Seed/Key): %s/%s" % (chk2,chk1))
 
-	if g.debug: print "Decrypted seed: %s" % hexlify(dec_seed)
+	if opt.debug: print "Decrypted seed: %s" % hexlify(dec_seed)
 
 	vmsg("OK")
 	return dec_seed
@@ -153,18 +154,18 @@ def make_key(passwd,salt,hash_preset,
 		what="encryption key",from_what="passphrase",verbose=False):
 
 	if from_what: what += " from "
-	if g.verbose or verbose:
+	if opt.verbose or verbose:
 		msg_r("Generating %s%s.\nPlease wait..." % (what,from_what))
 	key = scrypt_hash_passphrase(passwd, salt, hash_preset)
-	if g.verbose or verbose:
+	if opt.verbose or verbose:
 		msg("done")
-	if g.debug: print "Key: %s" % hexlify(key)
+	if opt.debug: print "Key: %s" % hexlify(key)
 	return key
 
 
 def get_random_data_from_user(uchars):
 
-	if g.quiet: msg("Enter %s random symbols" % uchars)
+	if opt.quiet: msg("Enter %s random symbols" % uchars)
 	else:       msg(crmsg['usr_rand_notice'] % uchars)
 
 	prompt = "You may begin typing.  %s symbols left: "
@@ -184,12 +185,12 @@ def get_random_data_from_user(uchars):
 		time_data.append(now - saved_time)
 		saved_time = now
 
-	if g.quiet: msg_r("\r")
+	if opt.quiet: msg_r("\r")
 	else: msg_r("\rThank you.  That's enough.%s\n\n" % (" "*18))
 
 	fmt_time_data = ["{:.22f}".format(i) for i in time_data]
 
-	if g.debug:
+	if opt.debug:
 		msg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
 				(key_data,"\n".join(fmt_time_data)))
 
@@ -206,7 +207,7 @@ def get_random(length):
 		from_what = "OS random data"
 		if not g.user_entropy:
 			g.user_entropy = \
-				sha256(get_random_data_from_user(g.usr_randchars)).digest()
+				sha256(get_random_data_from_user(opt.usr_randchars)).digest()
 			from_what += " plus user-supplied entropy"
 		else:
 			from_what += " plus saved user-supplied entropy"
@@ -218,7 +219,6 @@ def get_random(length):
 
 def get_seed_from_wallet(
 		infile,
-		opts,
 		prompt_info="{} wallet".format(g.proj_name),
 		silent=False
 		):
@@ -226,22 +226,22 @@ def get_seed_from_wallet(
 	wdata = get_data_from_wallet(infile,silent=silent)
 	label,metadata,hash_preset,salt,enc_seed = wdata
 
-	if g.debug: display_control_data(*wdata)
+	if opt.debug: display_control_data(*wdata)
 
-	padd = " "+infile if g.quiet else ""
-	passwd = get_mmgen_passphrase(prompt_info+padd,opts)
+	padd = " "+infile if opt.quiet else ""
+	passwd = get_mmgen_passphrase(prompt_info+padd)
 
 	key = make_key(passwd, salt, hash_preset)
 
 	return decrypt_seed(enc_seed, key, metadata[0], metadata[1])
 
 
-def get_hidden_incog_data(opts):
+def get_hidden_incog_data():
 		# Already sanity-checked:
-		fname,offset,seed_len = opts['from_incog_hidden'].split(",")
+		fname,offset,seed_len = opt.from_incog_hidden.split(",")
 		qmsg("Getting hidden incog data from file '%s'" % fname)
 
-		z = 0 if 'old_incog_fmt' in opts else 8
+		z = 0 if opt.old_incog_fmt else 8
 		dlen = g.aesctr_iv_len + g.salt_len + (int(seed_len)/8) + z
 
 		fsize = check_data_fits_file_at_offset(fname,int(offset),dlen,"read")
@@ -265,13 +265,12 @@ def confirm_old_format():
 		elif reply in 'nN': msg("\nExiting at user request"); sys.exit(1)
 		elif reply in 'mM': msg(""); return True
 		else:
-			if g.verbose: msg("\nInvalid reply")
+			if opt.verbose: msg("\nInvalid reply")
 			else: msg_r("\r")
 
 
 def get_seed_from_incog_wallet(
 		infile,
-		opts,
 		prompt_info="{} incognito wallet".format(g.proj_name),
 		silent=False,
 		hex_input=False
@@ -279,8 +278,8 @@ def get_seed_from_incog_wallet(
 
 	what = "incognito wallet data"
 
-	if "from_incog_hidden" in opts:
-		d = get_hidden_incog_data(opts)
+	if opt.from_incog_hidden:
+		d = get_hidden_incog_data()
 	else:
 		d = get_data_from_file(infile,what)
 		if hex_input:
@@ -290,7 +289,7 @@ def get_seed_from_incog_wallet(
 				msg("Data in file '%s' is not in hexadecimal format" % infile)
 				sys.exit(2)
 		# File could be of invalid length, so check:
-		z = 0 if 'old_incog_fmt' in opts else 8
+		z = 0 if opt.old_incog_fmt else 8
 		valid_dlens = [i/8 + g.aesctr_iv_len + g.salt_len + z for i in g.seed_lens]
 		# New fmt: [56, 64, 72]. Old fmt: [48, 56, 64].
 		if len(d) not in valid_dlens:
@@ -308,11 +307,11 @@ def get_seed_from_incog_wallet(
 	incog_id = make_iv_chksum(iv)
 	msg("Incog ID: %s (IV ID: %s)" % (incog_id,make_chksum_8(iv)))
 	qmsg("Check the applicable value against your records.")
-	vmsg(crmsg['incog_iv_id_hidden' if "from_incog_hidden" in opts
+	vmsg(crmsg['incog_iv_id_hidden' if opt.from_incog_hidden
 			else 'incog_iv_id'])
 
 	while True:
-		passwd = get_mmgen_passphrase(prompt_info+" "+incog_id,opts)
+		passwd = get_mmgen_passphrase(prompt_info+" "+incog_id)
 
 		qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets)))
 		hp = get_hash_preset_from_user(what="incog wallet")
@@ -333,7 +332,7 @@ def get_seed_from_incog_wallet(
 			m = "Seed ID: %s.  Is the Seed ID correct?" % sid
 			return keypress_confirm(m, True)
 
-		if 'old_incog_fmt' in opts:
+		if opt.old_incog_fmt:
 			if confirm_correct_seed_id(old_fmt_sid):
 				break
 		else:
@@ -349,7 +348,7 @@ def get_seed_from_incog_wallet(
 	return seed
 
 
-def _get_seed(infile,opts,silent=False,seed_id=""):
+def _get_seed(infile,silent=False,seed_id=""):
 
 	ext = get_extension(infile)
 
@@ -359,10 +358,10 @@ def _get_seed(infile,opts,silent=False,seed_id=""):
 	elif ext == g.wallet_ext:       source = "wallet"
 	elif ext == g.incog_ext:        source = "incognito wallet"
 	elif ext == g.incog_hex_ext:    source = "incognito wallet"
-	elif 'from_mnemonic'  in opts: source = "mnemonic"
-	elif 'from_brain'     in opts: source = "brainwallet"
-	elif 'from_seed'      in opts: source = "seed"
-	elif 'from_incog'     in opts: source = "incognito wallet"
+	elif opt.from_mnemonic : source = "mnemonic"
+	elif opt.from_brain    : source = "brainwallet"
+	elif opt.from_seed     : source = "seed"
+	elif opt.from_incog    : source = "incognito wallet"
 	else:
 		if infile: msg(
 			"Invalid file extension for file: %s\nValid extensions: '.%s'" %
@@ -373,26 +372,26 @@ def _get_seed(infile,opts,silent=False,seed_id=""):
 	seed_id_str = " for seed ID "+seed_id if seed_id else ""
 	if source == "mnemonic":
 		prompt = "Enter mnemonic%s: " % seed_id_str
-		words = get_words(infile,"mnemonic data",prompt,opts)
+		words = get_words(infile,"mnemonic data",prompt)
 		wl = get_default_wordlist()
 		from mmgen.mnemonic import get_seed_from_mnemonic
 		seed = get_seed_from_mnemonic(words,wl)
 	elif source == "brainwallet":
-		if 'from_brain' not in opts:
+		if not opt.from_brain:
 			msg("'--from-brain' parameters must be specified for brainwallet file")
 			sys.exit(2)
 		prompt = "Enter brainwallet passphrase%s: " % seed_id_str
-		words = get_words(infile,"brainwallet data",prompt,opts)
-		seed = _get_seed_from_brain_passphrase(words,opts)
+		words = get_words(infile,"brainwallet data",prompt)
+		seed = _get_seed_from_brain_passphrase(words)
 	elif source == "seed":
 		prompt = "Enter seed%s in %s format: " % (seed_id_str,g.seed_ext)
-		words = get_words(infile,"seed data",prompt,opts)
+		words = get_words(infile,"seed data",prompt)
 		seed = get_seed_from_seed_data(words)
 	elif source == "wallet":
-		seed = get_seed_from_wallet(infile, opts, silent=silent)
+		seed = get_seed_from_wallet(infile, silent=silent)
 	elif source == "incognito wallet":
-		h = ext == g.incog_hex_ext or 'from_incog_hex' in opts
-		seed = get_seed_from_incog_wallet(infile, opts, silent=silent, hex_input=h)
+		h = (ext == g.incog_hex_ext) or opt.from_incog_hex
+		seed = get_seed_from_incog_wallet(infile, silent=silent, hex_input=h)
 
 
 	if infile and not seed and (
@@ -400,25 +399,25 @@ def _get_seed(infile,opts,silent=False,seed_id=""):
 		msg("Invalid %s file '%s'" % (source,infile))
 		sys.exit(2)
 
-	if g.debug: print "Seed: %s" % hexlify(seed)
+	if opt.debug: print "Seed: %s" % hexlify(seed)
 
 	return seed
 
 
 # Repeat if entered data is invalid
-def get_seed_retry(infile,opts,seed_id=""):
+def get_seed_retry(infile,seed_id=""):
 	silent = False
 	while True:
-		seed = _get_seed(infile,opts,silent=silent,seed_id=seed_id)
+		seed = _get_seed(infile,silent=silent,seed_id=seed_id)
 		silent = True
 		if seed: return seed
 
 
-def _get_seed_from_brain_passphrase(words,opts):
+def _get_seed_from_brain_passphrase(words):
 	bp = " ".join(words)
-	if g.debug: print "Sanitized brain passphrase: %s" % bp
-	seed_len,hash_preset = get_from_brain_opt_params(opts)
-	if g.debug: print "Brainwallet l = %s, p = %s" % (seed_len,hash_preset)
+	if opt.debug: print "Sanitized brain passphrase: %s" % bp
+	seed_len,hash_preset = get_from_brain_opt_params()
+	if opt.debug: print "Brainwallet l = %s, p = %s" % (seed_len,hash_preset)
 	vmsg_r("Hashing brainwallet data.  Please wait...")
 	# Use buflen arg of scrypt.hash() to get seed of desired length
 	seed = scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
@@ -429,7 +428,7 @@ def _get_seed_from_brain_passphrase(words,opts):
 # Vars for mmgen_*crypt functions only
 salt_len,sha256_len,nonce_len = 32,32,32
 
-def mmgen_encrypt(data,what="data",hash_preset='',opts={}):
+def mmgen_encrypt(data,what="data",hash_preset=''):
 	salt,iv,nonce = get_random(salt_len),\
 					get_random(g.aesctr_iv_len), \
 					get_random(nonce_len)
@@ -451,7 +450,7 @@ def mmgen_decrypt(data,what="data",hash_preset=""):
 	hp = hash_preset or get_hash_preset_from_user('3',what)
 	m = "default" if hp == '3' else "user-requested"
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
-	passwd = get_mmgen_passphrase(what,{})
+	passwd = get_mmgen_passphrase(what)
 	key = make_key(passwd, salt, hp)
 	dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), what)
 	if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():

+ 2 - 1
mmgen/license.py

@@ -24,6 +24,7 @@ import sys
 from mmgen.util import msg, msg_r
 from mmgen.term import get_char
 import mmgen.config as g
+import mmgen.opt as opt
 
 gpl = {
 	'warning': """
@@ -589,7 +590,7 @@ copy of the Program in return for a fee.
 
 def do_license_msg(immed=False):
 
-	if g.quiet or g.no_license: return
+	if opt.quiet or g.no_license: return
 
 	msg(gpl['warning'])
 	prompt = "%s " % gpl['prompt'].strip()

+ 27 - 29
mmgen/main_addrgen.py

@@ -24,7 +24,7 @@ mmgen-addrgen: Generate a series or range of addresses from an MMGen
 import sys
 
 import mmgen.config as g
-from mmgen.Opts import *
+import mmgen.opt as opt
 from mmgen.license import *
 from mmgen.util import *
 from mmgen.crypto import *
@@ -32,8 +32,7 @@ from mmgen.addr import *
 
 what = "keys" if sys.argv[0].split("-")[-1] == "keygen" else "addresses"
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc': """Generate a range or list of {} from an {g.proj_name} wallet,
                   mnemonic, seed or password""".format(what,g=g),
 	'usage':"[opts] [infile] <address range or list>",
@@ -114,25 +113,24 @@ UNENCRYPTED form.  Generate only the key(s) you need and guard them carefully.
 """.format(g.proj_name),
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data,add_opts=["b16"])
 
-if 'show_hash_presets' in opts: show_hash_presets()
-if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
-	opts['from_incog'] = True
+if opt.show_hash_presets: show_hash_presets()
+if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
 
-if g.debug: show_opts_and_cmd_args(opts,cmd_args)
+if opt.debug: opt.opts.show_opts_and_cmd_args(cmd_args)
 
 if len(cmd_args) == 1 and (
-			'from_mnemonic' in opts
-			or 'from_brain' in opts
-			or 'from_seed' in opts
-			or 'from_incog_hidden' in opts
+			opt.from_mnemonic
+			or opt.from_brain
+			or opt.from_seed
+			or opt.from_incog_hidden
 		):
 	infile,addr_idx_arg = "",cmd_args[0]
 elif len(cmd_args) == 2:
 	infile,addr_idx_arg = cmd_args
 	check_infile(infile)
-else: usage(help_data)
+else: opt.opts.usage(opts_data)
 
 addr_idxs = parse_addr_idxs(addr_idx_arg)
 
@@ -141,46 +139,46 @@ if not addr_idxs: sys.exit(2)
 do_license_msg()
 
 # Interact with user:
-if what == "keys" and not g.quiet:
+if what == "keys" and not opt.quiet:
 	confirm_or_exit(wmsg['unencrypted_secret_keys'], 'continue')
 
 # Generate data:
 
-seed    = get_seed_retry(infile,opts)
+seed    = get_seed_retry(infile)
 
-opts['gen_what'] = "a" if what == "addresses" else (
-	"k" if 'no_addresses' in opts else "ka")
+opt.gen_what = "a" if what == "addresses" else (
+	"k" if opt.no_addresses else "ka")
 
-ainfo = generate_addrs(seed, addr_idxs, opts)
+ainfo = generate_addrs(seed, addr_idxs)
 
 addrdata_str = ainfo.fmt_data()
 outfile_base = "{}[{}]".format(make_chksum_8(seed), ainfo.idxs_fmt)
 
-if 'a' in opts['gen_what']:
-	w = "key-address" if 'k' in opts['gen_what'] else "address"
+if 'a' in opt.gen_what:
+	w = "key-address" if 'k' in opt.gen_what else "address"
 	qmsg("Checksum for %s data %s: %s" % (w,outfile_base,ainfo.checksum))
-	if 'save_checksum' in opts:
+	if opt.save_checksum:
 		write_to_file(outfile_base+"."+g.addrfile_chksum_ext,
-			ainfo.checksum+"\n",opts,"%s data checksum" % w,True,True,False)
+			ainfo.checksum+"\n","%s data checksum" % w,True,True,False)
 	else:
 		qmsg("This checksum will be used to verify the %s file in the future."%w)
 		qmsg("Record it to a safe location.")
 
-if 'k' in opts['gen_what'] and keypress_confirm("Encrypt key list?"):
-	addrdata_str = mmgen_encrypt(addrdata_str,"new key list","",opts)
+if 'k' in opt.gen_what and keypress_confirm("Encrypt key list?"):
+	addrdata_str = mmgen_encrypt(addrdata_str,"new key list","")
 	enc_ext = "." + g.mmenc_ext
 else: enc_ext = ""
 
 # Output data:
-if 'stdout' in opts or not sys.stdout.isatty():
+if opt.stdout or not sys.stdout.isatty():
 	if enc_ext and sys.stdout.isatty():
 		msg("Cannot write encrypted data to screen.  Exiting")
 		sys.exit(2)
 	write_to_stdout(addrdata_str,what,
-		(what=="keys"and not g.quiet and sys.stdout.isatty()))
+		(what=="keys"and not opt.quiet and sys.stdout.isatty()))
 else:
 	outfile = "%s.%s%s" % (outfile_base, (
-		g.keyaddrfile_ext if "ka" in opts['gen_what'] else (
-		g.keyfile_ext if "k" in opts['gen_what'] else
+		g.keyaddrfile_ext if "ka" in opt.gen_what else (
+		g.keyfile_ext if "k" in opt.gen_what else
 		g.addrfile_ext)), enc_ext)
-	write_to_file(outfile,addrdata_str,opts,what,not g.quiet,True)
+	write_to_file(outfile,addrdata_str,what,not opt.quiet,True)

+ 13 - 14
mmgen/main_addrimport.py

@@ -21,14 +21,14 @@ mmgen-addrimport: Import addresses into a MMGen bitcoind tracking wallet
 """
 
 import sys, time
-from mmgen.Opts   import *
+import mmgen.config as g
+import mmgen.opt as opt
 from mmgen.license import *
 from mmgen.util import *
 from mmgen.tx import connect_to_bitcoind
 from mmgen.addr import AddrInfo,AddrInfoEntry
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc': """Import addresses (both {pnm} and non-{pnm}) into a bitcoind
                      tracking wallet""".format(pnm=g.proj_name),
 	'usage':"[opts] [mmgen address file]",
@@ -47,12 +47,12 @@ in the tracking wallet.
 """
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
 if len(cmd_args) == 1:
 	infile = cmd_args[0]
 	check_infile(infile)
-	if 'addrlist' in opts:
+	if opt.addrlist:
 		lines = get_lines_from_file(
 			infile,"non-{} addresses".format(g.proj_name),trim_comments=True)
 		ai,adata = AddrInfo(),[]
@@ -62,7 +62,7 @@ if len(cmd_args) == 1:
 			adata.append(a)
 		ai.initialize(None,adata)
 	else:
-		ai = AddrInfo(infile,has_keys='keyaddr_file' in opts)
+		ai = AddrInfo(infile,has_keys=opt.keyaddr_file)
 else:
 	msg("""
 "You must specify an mmgen address file (or a list of non-%s addresses
@@ -80,10 +80,9 @@ for e in ai.addrdata:
 m = (" from seed ID %s" % ai.seed_id) if ai.seed_id else ""
 qmsg("OK. %s addresses%s" % (ai.num_addrs,m))
 
-import mmgen.config as g
 g.http_timeout = 3600
 
-if not 'test' in opts:
+if not opt.test:
 	c = connect_to_bitcoind()
 
 m = """
@@ -92,30 +91,30 @@ necessary only if an address you're importing is already on the blockchain,
 has a balance and is not already in your tracking wallet.  Note that the
 rescanning process is very slow (>30 min. for each imported address on a
 low-powered computer).
-	""".strip() if "rescan" in opts else """
+	""".strip() if opt.rescan else """
 WARNING: If any of the addresses you're importing is already on the blockchain,
 has a balance and is not already in your tracking wallet, you must exit the
 program now and rerun it using the '--rescan' option.  Otherwise you may ignore
 this message and continue.
 """.strip()
 
-if g.quiet: m = ""
+if opt.quiet: m = ""
 confirm_or_exit(m, "continue", expect="YES")
 
 err_flag = False
 
 def import_address(addr,label,rescan):
 	try:
-		if not 'test' in opts:
+		if not opt.test:
 			c.importaddress(addr,label,rescan)
 	except:
 		global err_flag
 		err_flag = True
 
 w_n_of_m = len(str(ai.num_addrs)) * 2 + 2
-w_mmid   = "" if 'addrlist' in opts else len(str(max(ai.idxs()))) + 12
+w_mmid   = "" if opt.addrlist else len(str(max(ai.idxs()))) + 12
 
-if "rescan" in opts:
+if opt.rescan:
 	import threading
 	msg_fmt = "\r%s %-{}s %-34s %s".format(w_n_of_m)
 else:
@@ -128,7 +127,7 @@ for n,e in enumerate(ai.addrdata):
 		if e.comment: label += " " + e.comment
 	else: label = "non-%s" % g.proj_name
 
-	if "rescan" in opts:
+	if opt.rescan:
 		t = threading.Thread(target=import_address, args=(e.addr,label,True))
 		t.daemon = True
 		t.start()

+ 17 - 18
mmgen/main_passchg.py

@@ -22,13 +22,12 @@ mmgen-passchg: Change an MMGen deterministic wallet's passphrase, label or
 """
 
 import sys
-from mmgen.Opts import *
 from mmgen.util import *
 from mmgen.crypto import *
 import mmgen.config as g
+import mmgen.opt as opt
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':  """Change the passphrase, hash preset or label of an {}
                   deterministic wallet""".format(g.proj_name),
 	'usage':   "[opts] [filename]",
@@ -55,9 +54,9 @@ NOTE: The key ID will change if either the passphrase or hash preset are
 """
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
-if 'show_hash_presets' in opts: show_hash_presets()
+if opt.show_hash_presets: show_hash_presets()
 
 if len(cmd_args) != 1:
 	msg("One input file must be specified")
@@ -71,36 +70,36 @@ seed_id,key_id = metadata[:2]
 # Repeat on incorrect pw entry
 while True:
 	p = "{} wallet".format(g.proj_name)
-	passwd = get_mmgen_passphrase(p,{},not 'keep_old_passphrase' in opts)
+	passwd = get_mmgen_passphrase(p,not opt.keep_old_passphrase)
 	key = make_key(passwd, salt, hash_preset)
 	seed = decrypt_seed(enc_seed, key, seed_id, key_id)
 	if seed: break
 
 changed = {}
 
-if 'label' in opts:
-	if opts['label'] != label:
-		msg("Label changed: '%s' -> '%s'" % (label, opts['label']))
+if opt.label:
+	if opt.label != label:
+		msg("Label changed: '%s' -> '%s'" % (label, opt.label))
 		changed['label'] = True
 	else:
 		msg("Label is unchanged: '%s'" % (label))
-else: opts['label'] = label  # Copy the old label
+else: opt.label = label  # Copy the old label
 
-if 'hash_preset' in opts:
-	if hash_preset != opts['hash_preset']:
+if opt.hash_preset:
+	if hash_preset != opt.hash_preset:
 		qmsg("Hash preset has changed (%s -> %s)" %
-			(hash_preset, opts['hash_preset']))
+			(hash_preset, opt.hash_preset))
 		changed['preset'] = True
 	else:
 		msg("Hash preset is unchanged")
 else:
-	opts['hash_preset'] = hash_preset
+	opt.hash_preset = hash_preset
 
-if 'keep_old_passphrase' in opts:
+if opt.keep_old_passphrase:
 	msg("Keeping old passphrase by user request")
 else:
 	new_passwd = get_new_passphrase(
-			"{} wallet".format(g.proj_name), opts, True)
+			"{} wallet".format(g.proj_name), True)
 
 	if new_passwd == passwd:
 		qmsg("Passphrase is unchanged")
@@ -115,7 +114,7 @@ if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
 	from hashlib import sha256
 
 	salt = sha256(salt + get_random(128)).digest()[:g.salt_len]
-	key = make_key(passwd, salt, opts['hash_preset'])
+	key = make_key(passwd, salt, opt.hash_preset)
 	new_key_id = make_chksum_8(key)
 	qmsg("Key ID changed: %s -> %s" % (key_id,new_key_id))
 	key_id = new_key_id
@@ -124,4 +123,4 @@ elif not 'label' in changed:
 	msg("Data unchanged.  No file will be written")
 	sys.exit(2)
 
-write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts)
+write_wallet_to_file(seed, passwd, key_id, salt, enc_seed)

+ 4 - 7
mmgen/main_tool.py

@@ -23,11 +23,10 @@ mmgen-tool:  Perform various MMGen- and Bitcoin-related operations.
 
 import sys
 import mmgen.config as g
+import mmgen.opt as opt
 import mmgen.tool as tool
-from mmgen.Opts import *
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':    "Perform various MMGen- and Bitcoin-related operations",
 	'usage':   "[opts] <command> <command args>",
 	'options': """
@@ -46,10 +45,10 @@ command
 """.format(tool.cmd_help,g.prog_name)
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
 if len(cmd_args) < 1:
-	usage(help_data)
+	opt.opts.usage(opts_data)
 	sys.exit(1)
 
 command = cmd_args.pop(0)
@@ -64,6 +63,4 @@ if cmd_args and cmd_args[0] == '--help':
 
 args,kwargs = tool.process_args(g.prog_name, command, cmd_args)
 
-tool.opts = opts
-
 tool.__dict__[command](*args,**kwargs)

+ 18 - 18
mmgen/main_txcreate.py

@@ -25,12 +25,11 @@ import sys
 from decimal import Decimal
 
 import mmgen.config as g
-from mmgen.Opts import *
+import mmgen.opt as opt
 from mmgen.license import *
 from mmgen.tx import *
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':    "Create a BTC transaction with outputs to specified addresses",
 	'usage':   "[opts]  <addr,amt> ... [change addr] [addr file] ...",
 	'options': """
@@ -124,7 +123,7 @@ def format_unspent_outputs_for_printing(out,sort_info,total):
 	)
 
 
-def sort_and_view(unspent,opts):
+def sort_and_view(unspent):
 
 	def s_amt(i):   return i.amount
 	def s_txid(i):  return "%s %03s" % (i.txid,i.vout)
@@ -239,7 +238,7 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 			elif reply == 'p':
 				d = format_unspent_outputs_for_printing(unsp,sort_info,total)
 				of = "listunspent[%s].out" % ",".join(sort_info)
-				write_to_file(of, d, opts,"",False,False)
+				write_to_file(of, d, "",False,False)
 				write_to_file_msg = "Data written to '%s'\n\n" % of
 			elif reply == 'v':
 				do_pager("\n".join(out))
@@ -333,16 +332,16 @@ def mmaddr2btcaddr(c,mmaddr,acct_data,ail):
 	return btcaddr
 
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
-if g.debug: show_opts_and_cmd_args(opts,cmd_args)
+if opt.debug: show_opts_and_cmd_args(cmd_args)
 
-if 'comment_file' in opts:
-	comment = get_tx_comment_from_file(opts['comment_file'])
+if opt.comment_file:
+	comment = get_tx_comment_from_file(opt.comment_file)
 
 c = connect_to_bitcoind()
 
-if not 'info' in opts:
+if not opt.info:
 	do_license_msg(immed=True)
 
 	tx_out,acct_data,change_addr = {},{},""
@@ -389,30 +388,31 @@ if not 'info' in opts:
 		msg("At least one output must be specified on the command line")
 		sys.exit(2)
 
-	tx_fee = opts['tx_fee'] if 'tx_fee' in opts else g.tx_fee
+	tx_fee = opt.tx_fee if opt.tx_fee else g.tx_fee
 	tx_fee = normalize_btc_amt(tx_fee)
 	if tx_fee > g.max_tx_fee:
 		msg("Transaction fee too large: %s > %s" % (tx_fee,g.max_tx_fee))
 		sys.exit(2)
 
-if g.debug: show_opts_and_cmd_args(opts,cmd_args)
+if opt.debug: show_opts_and_cmd_args(cmd_args)
 
 if g.bogus_wallet_data:  # for debugging purposes only
+	import mmgen.rpc.data
 	us = eval(get_data_from_file(g.bogus_wallet_data))
 else:
 	us = c.listunspent()
-#	write_to_file("bogus_unspent.json", repr(us), opts); sys.exit()
+#	write_to_file("bogus_unspent.json", repr(us)); sys.exit()
 
 if not us: msg(wmsg['no_spendable_outputs']); sys.exit(2)
 for o in us:
 	o.mmid,o.comment = parse_mmgen_label(o.account)
 	del o.account
-unspent = sort_and_view(us,opts)
+unspent = sort_and_view(us)
 
 total = trim_exponent(sum([i.amount for i in unspent]))
 
 msg("Total unspent: %s BTC (%s outputs)" % (total, len(unspent)))
-if 'info' in opts: sys.exit(0)
+if opt.info: sys.exit(0)
 
 send_amt = sum([tx_out[i] for i in tx_out.keys()])
 msg("Total amount to spend: %s%s" % (
@@ -458,11 +458,11 @@ if change > 0: tx_out[change_addr] = float(change)
 
 tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
 
-if g.debug:
+if opt.debug:
 	print "tx_in:", repr(tx_in)
 	print "tx_out:", repr(tx_out)
 
-if 'comment_file' in opts:
+if opt.comment_file:
 	if keypress_confirm("Edit comment?",False):
 		comment = get_tx_comment_from_user(comment)
 else:
@@ -493,6 +493,6 @@ if keypress_confirm("Save transaction?",default_yes=False):
 	outfile = "tx_%s[%s].%s" % (tx_id,amt,g.rawtx_ext)
 	data = make_tx_data("{} {} {}".format(*metadata),
 				tx_hex,sel_unspent,b2m_map,comment)
-	write_to_file(outfile,data,opts,"transaction",False,True)
+	write_to_file(outfile,data,"transaction",False,True)
 else:
 	msg("Transaction not saved")

+ 7 - 8
mmgen/main_txsend.py

@@ -23,13 +23,12 @@ mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
 import sys
 
 import mmgen.config as g
-from mmgen.Opts import *
+import mmgen.opt as opt
 from mmgen.license import *
 from mmgen.tx import *
 from mmgen.util import msg,check_infile,get_lines_from_file,confirm_or_exit
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':    "Send a Bitcoin transaction signed by {}-txsign".format(g.proj_name.lower()),
 	'usage':   "[opts] <signed transaction file>",
 	'options': """
@@ -39,11 +38,11 @@ help_data = {
 """
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
 if len(cmd_args) == 1:
 	infile = cmd_args[0]; check_infile(infile)
-else: usage(help_data)
+else: opt.opts.usage(opts_data)
 
 # Begin execution
 
@@ -66,13 +65,13 @@ if keypress_confirm("Edit transaction comment?"):
 				inputs_data, b2m_map, comment)
 	w = "signed transaction with edited comment"
 	outfile = infile
-	write_to_file(outfile,data,opts,w,False,True,True)
+	write_to_file(outfile,data,w,False,True,True)
 
 warn   = "Once this transaction is sent, there's no taking it back!"
 what   = "broadcast this transaction to the network"
 expect =  "YES, I REALLY WANT TO DO THIS"
 
-if g.quiet: warn,expect = "","YES"
+if opt.quiet: warn,expect = "","YES"
 
 confirm_or_exit(warn, what, expect)
 
@@ -87,4 +86,4 @@ except:
 msg("Transaction sent: %s" % tx_id)
 
 of = "tx_{}[{}].txid".format(*metadata[:2])
-write_to_file(of, tx_id+"\n",opts,"transaction ID",True,True)
+write_to_file(of, tx_id+"\n","transaction ID",True,True)

+ 38 - 40
mmgen/main_txsign.py

@@ -23,12 +23,11 @@ mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
 import sys
 
 import mmgen.config as g
-from mmgen.Opts import *
+import mmgen.opt as opt
 from mmgen.license import *
 from mmgen.tx import *
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':    "Sign Bitcoin transactions generated by {}-txcreate".format(g.proj_name.lower()),
 	'usage':   "[opts] <transaction file> .. [mmgen wallet/seed/words/brainwallet file] ..",
 	'options': """
@@ -103,7 +102,7 @@ Removed %s duplicate wif key%s from keylist (also in {MMG} key-address file
 """.strip().format(MMG=g.proj_name),
 }
 
-def get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts):
+def get_seed_for_seed_id(seed_id,infiles,saved_seeds):
 
 	if seed_id in saved_seeds.keys():
 		return saved_seeds[seed_id]
@@ -112,11 +111,10 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts):
 
 	while True:
 		if infiles:
-			seed = get_seed_retry(infiles.pop(0),opts)
-		elif "from_brain" in opts or "from_mnemonic" in opts \
-			or "from_seed" in opts or "from_incog" in opts:
+			seed = get_seed_retry(infiles.pop(0))
+		elif opt.from_brain or opt.from_mnemonic or opt.from_seed or opt.from_incog:
 			qmsg("Need seed data for seed ID %s" % seed_id)
-			seed = get_seed_retry("",opts,seed_id)
+			seed = get_seed_retry("",seed_id)
 			msg("User input produced seed ID %s" % make_chksum_8(seed))
 		else:
 			msg("ERROR: No seed source found for seed ID: %s" % seed_id)
@@ -128,7 +126,7 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts):
 		if sid == seed_id: return seed
 
 
-def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds,opts):
+def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds):
 
 	seed_ids = set([i[:8] for i in mmgen_addrs])
 	vmsg("Need seed%s: %s" % (suf(seed_ids,"k")," ".join(seed_ids)))
@@ -137,9 +135,10 @@ def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds,opts):
 	from mmgen.addr import generate_addrs
 	for seed_id in seed_ids:
 		# Returns only if seed is found
-		seed = get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts)
+		seed = get_seed_for_seed_id(seed_id,infiles,saved_seeds)
 		addr_nums = [int(i[9:]) for i in mmgen_addrs if i[:8] == seed_id]
-		ai = generate_addrs(seed,addr_nums,{'gen_what':"ka"})
+		opt.gen_what = "ka"
+		ai = generate_addrs(seed,addr_nums)
 		d += [("{}:{}".format(seed_id,e.idx),e.addr,e.wif) for e in ai.addrdata]
 	return d
 
@@ -148,7 +147,7 @@ def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None):
 
 	if keys:
 		qmsg("Passing %s key%s to bitcoind" % (len(keys),suf(keys,"k")))
-		if g.debug: print "Keys:\n  %s" % "\n  ".join(keys)
+		if opt.debug: print "Keys:\n  %s" % "\n  ".join(keys)
 
 	msg_r("Signing transaction{}...".format(tx_num_str))
 	from mmgen.rpc import exceptions
@@ -161,7 +160,7 @@ def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None):
 	return sig_tx
 
 
-def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys,opts):
+def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys):
 
 	try:
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
@@ -170,7 +169,7 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys,opts):
 		msg("Using keys in wallet.dat as per user request")
 		prompt = "Enter passphrase for bitcoind wallet: "
 		while True:
-			passwd = get_bitcoind_passphrase(prompt,opts)
+			passwd = get_bitcoind_passphrase(prompt)
 
 			try:
 				c.walletpassphrase(passwd, 9999)
@@ -190,11 +189,11 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys,opts):
 	return sig_tx
 
 
-def check_maps_from_seeds(maplist,label,infiles,saved_seeds,opts,return_keys=False):
+def check_maps_from_seeds(maplist,label,infiles,saved_seeds,return_keys=False):
 
 	if not maplist: return []
 	qmsg("Checking MMGen -> BTC address mappings for %ss (from seeds)" % label)
-	d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds,opts)
+	d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds)
 #	0=mmaddr 1=addr 2=wif
 	m = dict([(e[0],e[1]) for e in d])
 	for a,b in zip(sorted(m),sorted(maplist)):
@@ -214,9 +213,9 @@ for the following non-{} address{}:\n    {}""".format(
 	g.proj_name,suf(addrs,"a"),"\n    ".join(addrs)).strip()
 
 
-def parse_mmgen_keyaddr_file(opts):
+def parse_mmgen_keyaddr_file():
 	from mmgen.addr import AddrInfo
-	ai = AddrInfo(opts['mmgen_keys_from_file'],has_keys=True)
+	ai = AddrInfo(opt.mmgen_keys_from_file,has_keys=True)
 	vmsg("Found %s wif key%s for seed ID %s" %
 			(ai.num_addrs, suf(ai.num_addrs,"k"), ai.seed_id))
 	# idx: (0=addr, 1=comment 2=wif) -> mmaddr: (0=addr, 1=wif)
@@ -224,8 +223,8 @@ def parse_mmgen_keyaddr_file(opts):
 		[("%s:%s"%(ai.seed_id,e.idx), (e.addr,e.wif)) for e in ai.addrdata])
 
 
-def parse_keylist(opts,from_file):
-	fn = opts['keys_from_file']
+def parse_keylist(from_file):
+	fn = opt.keys_from_file
 	d = get_data_from_file(fn,"non-%s keylist" % g.proj_name)
 	enc_ext = get_extension(fn) == g.mmenc_ext
 	if enc_ext or not is_utf8(d):
@@ -279,17 +278,16 @@ def get_keys_from_keylist(kldata,other_addrs):
 	return ret
 
 
-opts,infiles = parse_opts(sys.argv,help_data)
+infiles = opt.opts.init(opts_data,add_opts=["b16"])
 
 for l in (
 ('tx_id', 'info'),
 ('tx_id', 'terse_info'),
-): warn_incompatible_opts(opts,l)
+): opt.opts.warn_incompatible_opts(l)
 
-if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
-	opts['from_incog'] = True
+if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
 
-if not infiles: usage(help_data)
+if not infiles: opt.opts.usage(opts_data)
 for i in infiles: check_infile(i)
 
 c = connect_to_bitcoind()
@@ -298,14 +296,14 @@ saved_seeds = {}
 tx_files   = [i for i in infiles if get_extension(i) == g.rawtx_ext]
 seed_files = [i for i in infiles if get_extension(i) != g.rawtx_ext]
 
-if not "info" in opts and not "terse_info" in opts:
+if not opt.info and not opt.terse_info:
 	do_license_msg(immed=True)
 
 from_file = { 'mmdata':{}, 'kldata':{} }
-if 'mmgen_keys_from_file' in opts:
-	from_file['mmdata'] = parse_mmgen_keyaddr_file(opts) or {}
-if 'keys_from_file' in opts:
-	from_file['kldata'] = parse_keylist(opts,from_file) or {}
+if opt.mmgen_keys_from_file:
+	from_file['mmdata'] = parse_mmgen_keyaddr_file() or {}
+if opt.keys_from_file:
+	from_file['kldata'] = parse_keylist(from_file) or {}
 
 tx_num_str = ""
 for tx_num,tx_file in enumerate(tx_files,1):
@@ -313,19 +311,19 @@ for tx_num,tx_file in enumerate(tx_files,1):
 		msg("\nTransaction #%s of %s:" % (tx_num,len(tx_files)))
 		tx_num_str = " #%s" % tx_num
 
-	m = "" if 'tx_id' in opts else "transaction data"
+	m = "" if opt.tx_id else "transaction data"
 	tx_data = get_lines_from_file(tx_file,m)
 
 	metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,tx_file)
 	vmsg("Successfully opened transaction file '%s'" % tx_file)
 
-	if 'tx_id' in opts:
+	if opt.tx_id:
 		msg(metadata[0])
 		sys.exit(0)
 
-	if 'info' in opts or 'terse_info' in opts:
+	if opt.info or opt.terse_info:
 		view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pause=False,
-				terse='terse_info' in opts)
+				terse=opt.terse_info)
 		sys.exit(0)
 
 	prompt_and_view_tx_data(c,"View data for transaction{}?".format(tx_num_str),
@@ -336,7 +334,7 @@ for tx_num,tx_file in enumerate(tx_files,1):
 
 	keys = get_keys_from_keylist(from_file['kldata'],other_addrs)
 
-	if other_addrs and not 'use_wallet_dat' in opts:
+	if other_addrs and not opt.use_wallet_dat:
 		missing_keys_errormsg(other_addrs)
 		sys.exit(2)
 
@@ -347,8 +345,8 @@ for tx_num,tx_file in enumerate(tx_files,1):
 	keys += check_maps_from_kafile(imap,"input",from_file['mmdata'],True)
 	check_maps_from_kafile(omap,"output",from_file['mmdata'])
 
-	keys += check_maps_from_seeds(imap,"input",seed_files,saved_seeds,opts,True)
-	check_maps_from_seeds(omap,"output",seed_files,saved_seeds,opts)
+	keys += check_maps_from_seeds(imap,"input",seed_files,saved_seeds,True)
+	check_maps_from_seeds(omap,"output",seed_files,saved_seeds)
 
 	extra_sids = set(saved_seeds.keys()) - sids
 	if extra_sids:
@@ -360,9 +358,9 @@ for tx_num,tx_file in enumerate(tx_files,1):
 		{"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
 			for i in inputs_data]
 
-	if 'use_wallet_dat' in opts:
+	if opt.use_wallet_dat:
 		sig_tx = sign_tx_with_bitcoind_wallet(
-				c,tx_hex,tx_num_str,sig_data,keys,opts)
+				c,tx_hex,tx_num_str,sig_data,keys)
 	else:
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 
@@ -374,7 +372,7 @@ for tx_num,tx_file in enumerate(tx_files,1):
 		data = make_tx_data("{} {} {t}".format(*metadata[:2], t=make_timestamp()),
 				sig_tx['hex'], inputs_data, b2m_map, comment)
 		w = "signed transaction{}".format(tx_num_str)
-		write_to_file(outfile,data,opts,w,(not g.quiet),True,False)
+		write_to_file(outfile,data,w,(not opt.quiet),True,False)
 	else:
 		msg_r("failed\nSome keys were missing.  ")
 		msg("Transaction %scould not be signed." % tx_num_str)

+ 30 - 31
mmgen/main_walletchk.py

@@ -23,12 +23,11 @@ mmgen-walletchk: Check integrity of an MMGen deterministic wallet, display
 
 import sys
 import mmgen.config as g
-from mmgen.Opts import *
+import mmgen.opt as opt
 from mmgen.util import *
 from mmgen.crypto import *
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':  """Check integrity of an {} deterministic wallet, display
                     its information, and export seed and mnemonic data.
              """.format(g.proj_name),
@@ -61,14 +60,14 @@ to disable this option, then specify '-r0' on the command line.
 """
 }
 
-def wallet_to_incog_data(infile,opts):
+def wallet_to_incog_data(infile):
 
 	d = get_data_from_wallet(infile,silent=True)
 	seed_id,key_id,preset,salt,enc_seed = \
 			d[1][0], d[1][1], d[2].split(":")[0], d[3], d[4]
 
 	while True:
-		passwd = get_mmgen_passphrase("{} wallet".format(g.proj_name),opts)
+		passwd = get_mmgen_passphrase("{} wallet".format(g.proj_name))
 		key = make_key(passwd, salt, preset, "main key")
 		seed = decrypt_seed(enc_seed, key, seed_id, key_id)
 		if seed: break
@@ -77,7 +76,7 @@ def wallet_to_incog_data(infile,opts):
 	iv_id = make_iv_chksum(iv)
 	msg("Incog ID: %s" % iv_id)
 
-	if not 'old_incog_fmt' in opts:
+	if not opt.old_incog_fmt:
 		salt = get_random(g.salt_len)
 		key = make_key(passwd, salt, preset, "incog wallet key")
 		key_id = make_chksum_8(key)
@@ -92,13 +91,13 @@ def wallet_to_incog_data(infile,opts):
 	return iv+wrap_enc,seed_id,key_id,iv_id,preset
 
 
-def export_to_hidden_incog(incog_enc,opts):
-	outfile,offset = opts['export_incog_hidden'].split(",") #Already sanity-checked
-	if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile)
+def export_to_hidden_incog(incog_enc):
+	outfile,offset = opt.export_incog_hidden.split(",") #Already sanity-checked
+	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 	check_data_fits_file_at_offset(outfile,int(offset),len(incog_enc),"write")
 
-	if not g.quiet: confirm_or_exit("","alter file '%s'" % outfile)
+	if not opt.quiet: confirm_or_exit("","alter file '%s'" % outfile)
 	import os
 	f = os.open(outfile,os.O_RDWR)
 	os.lseek(f, int(offset), os.SEEK_SET)
@@ -108,60 +107,60 @@ def export_to_hidden_incog(incog_enc,opts):
 			(os.path.relpath(outfile),offset))
 
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
-if 'export_incog_hidden' in opts or 'export_incog_hex' in opts:
-	opts['export_incog'] = True
+if opt.export_incog_hidden or opt.export_incog_hex:
+	opt.export_incog = True
 
-if len(cmd_args) != 1: usage(help_data)
+if len(cmd_args) != 1: opt.opts.usage(opts_data)
 
 check_infile(cmd_args[0])
 
-if set(['outdir','export_incog_hidden']) <= set(opts.keys()):
+if opt.outdir and opt.export_incog_hidden:
 	msg("Warning: '--outdir' option is ignored when exporting hidden incog data")
 
 g.use_urandchars = True
 
-if 'export_mnemonic' in opts:
+if opt.export_mnemonic:
 	qmsg("Exporting mnemonic data to file by user request")
-elif 'export_seed' in opts:
+elif opt.export_seed:
 	qmsg("Exporting seed data to file by user request")
-elif 'export_incog' in opts:
+elif opt.export_incog:
 	qmsg("Exporting wallet to incognito format by user request")
 	incog_enc,seed_id,key_id,iv_id,preset = \
-		wallet_to_incog_data(cmd_args[0],opts)
+		wallet_to_incog_data(cmd_args[0])
 
-	if "export_incog_hidden" in opts:
-		export_to_hidden_incog(incog_enc,opts)
+	if opt.export_incog_hidden:
+		export_to_hidden_incog(incog_enc)
 	else:
-		z = 0 if 'old_incog_fmt' in opts else 8
+		z = 0 if opt.old_incog_fmt else 8
 		seed_len = (len(incog_enc)-g.salt_len-g.aesctr_iv_len-z)*8
 		fn = "%s-%s-%s[%s,%s].%s" % (
 			seed_id, key_id, iv_id, seed_len, preset,
-			g.incog_hex_ext if "export_incog_hex" in opts else g.incog_ext
+			g.incog_hex_ext if opt.export_incog_hex else g.incog_ext
 		)
 		data = pretty_hexdump(incog_enc,2,8,line_nums=False) \
-					if "export_incog_hex" in opts else incog_enc
-		write_to_file_or_stdout(fn, data, opts, "incognito wallet data")
+					if opt.export_incog_hex else incog_enc
+		write_to_file_or_stdout(fn, data, "incognito wallet data")
 
 	sys.exit()
 
-seed = get_seed_retry(cmd_args[0], opts)
+seed = get_seed_retry(cmd_args[0])
 if seed: msg("Wallet is OK")
 else:
 	msg("Error opening wallet")
 	sys.exit(2)
 
-if 'export_mnemonic' in opts:
+if opt.export_mnemonic:
 	wl = get_default_wordlist()
 	from mmgen.mnemonic import get_mnemonic_from_seed
-	mn = get_mnemonic_from_seed(seed, wl, g.default_wl, g.debug)
+	mn = get_mnemonic_from_seed(seed, wl, g.default_wl, opt.debug)
 	fn = "%s.%s" % (make_chksum_8(seed).upper(), g.mn_ext)
-	write_to_file_or_stdout(fn, " ".join(mn)+"\n", opts, "mnemonic data")
+	write_to_file_or_stdout(fn, " ".join(mn)+"\n", "mnemonic data")
 
-elif 'export_seed' in opts:
+elif opt.export_seed:
 	from mmgen.bitcoin import b58encode_pad
 	data = col4(b58encode_pad(seed))
 	chk = make_chksum_6(b58encode_pad(seed))
 	fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
-	write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), opts, "seed data")
+	write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), "seed data")

+ 18 - 22
mmgen/main_walletgen.py

@@ -24,13 +24,12 @@ import sys, os
 from hashlib import sha256
 
 import mmgen.config as g
-from mmgen.Opts import *
+import mmgen.opt as opt
 from mmgen.license import *
 from mmgen.util import *
 from mmgen.crypto import *
 
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc':    "Generate an {} deterministic wallet".format(g.proj_name),
 	'usage':   "[opts] [infile]",
 	'options': """
@@ -121,11 +120,12 @@ future, you must continue using these same parameters
 """,
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+import mmgen.opt as opt
+cmd_args = opt.opts.init(opts_data)
 
-if 'show_hash_presets' in opts: show_hash_presets()
+if opt.show_hash_presets: show_hash_presets()
 
-if g.debug: show_opts_and_cmd_args(opts,cmd_args)
+if opt.debug: opt.opts.show_opts_and_cmd_args(cmd_args)
 
 if len(cmd_args) == 1:
 	infile = cmd_args[0]
@@ -140,7 +140,7 @@ if len(cmd_args) == 1:
 		sys.exit(1)
 elif len(cmd_args) == 0:
 	infile = ""
-else: usage(help_data)
+else: opt.opts.usage(opts_data)
 
 g.use_urandchars = True
 
@@ -148,31 +148,27 @@ g.use_urandchars = True
 
 do_license_msg()
 
-if 'from_brain' in opts and not g.quiet:
+if opt.from_brain and not opt.quiet:
 	confirm_or_exit(wmsg['brain_warning'].format(
-			g.proj_name, *get_from_brain_opt_params(opts)),
+			g.proj_name, *get_from_brain_opt_params()),
 		"continue")
 
-for i in 'from_mnemonic','from_brain','from_seed','from_incog':
-	if infile or (i in opts):
-		seed = get_seed_retry(infile,opts)
-#		if "from_incog" in opts or get_extension(infile) == g.incog_ext:
-#			qmsg(cmessages['incog'] % make_chksum_8(seed))
-#		else: qmsg("")
-		qmsg("")
-		break
+if infile or (opt.from_mnemonic or opt.from_brain
+				or opt.from_seed or opt.from_incog):
+	seed = get_seed_retry(infile)
+	qmsg("")
 else:
 	# Truncate random data for smaller seed lengths
-	seed = sha256(get_random(128)).digest()[:opts['seed_len']/8]
+	seed = sha256(get_random(128)).digest()[:opt.seed_len/8]
 
 salt = sha256(get_random(128)).digest()[:g.salt_len]
 
-qmsg(wmsg['choose_wallet_passphrase'] % opts['hash_preset'])
+qmsg(wmsg['choose_wallet_passphrase'] % opt.hash_preset)
 
-passwd = get_new_passphrase("new {} wallet".format(g.proj_name), opts)
+passwd = get_new_passphrase("new {} wallet".format(g.proj_name))
 
-key = make_key(passwd, salt, opts['hash_preset'])
+key = make_key(passwd, salt, opt.hash_preset)
 
 enc_seed = encrypt_seed(seed, key)
 
-write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)
+write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed)

+ 1 - 0
mmgen/mnemonic.py

@@ -25,6 +25,7 @@ from binascii import hexlify
 from mmgen.util import msg,msg_r,make_chksum_8,Vmsg
 from mmgen.crypto import get_random
 import mmgen.config as g
+import mmgen.opt as opt
 
 wl_checksums = {
 	"electrum": '5ca31424',

+ 22 - 0
mmgen/opt.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+#
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
+# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+opt.py: an empty namespace for the global opt variables
+"""
+import opts

+ 78 - 58
mmgen/Opts.py → mmgen/opts.py

@@ -17,15 +17,18 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Opts.py:  Option handling routines for the MMGen suite
+opts.py:  Further options processing after mmgen.share.Opts
 """
-
 import sys
+
 import mmgen.config as g
-import mmgen.opt.Opts
-from mmgen.util import msg,check_infile,check_outfile,check_outdir,msgrepr_exit,msgrepr
+import mmgen.share.Opts
+import opt
+from mmgen.util import msg,msgrepr_exit,msgrepr
 
-def usage(hd): mmgen.opt.Opts.usage(hd)
+def usage(opts_data):
+	print "USAGE: %s %s" % (opts_data['prog_name'], opts_data['usage'])
+	sys.exit(2)
 
 def print_version_info():
 	print """
@@ -33,19 +36,38 @@ def print_version_info():
 Copyright (C) {g.Cdates} by {g.author} {g.email}.
 """.format(g=g).strip()
 
-def warn_incompatible_opts(opts,incompat_list):
-	bad = [k for k in opts.keys() if k in incompat_list]
+def warn_incompatible_opts(incompat_list):
+	bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in incompat_list]
 	if len(bad) > 1:
 		msg("Mutually exclusive options: %s" % " ".join(
 					["--"+b.replace("_","-") for b in bad]))
 		sys.exit(1)
 
-def parse_opts(argv,help_data):
+def typeconvert_from_dfl(opts,opt):
+
+	vtype = type(g.__dict__[opt])
+	if g.debug: print "Override opt: %-15s [%s]" % (opt,vtype)
+
+	try:
+		opts[opt] = vtype(opts[opt])
+	except:
+		d = {
+			'int':   'an integer',
+			'str':   'a string',
+			'float': 'a float',
+			'bool':  'a boolean value',
+		}
+		m = [d[k] for k in d if __builtins__[k] == vtype][0]
+		msg("'%s': invalid parameter for '--%s' option (not %s)" %
+				(opts[opt],opt.replace("_","-"),m))
+		sys.exit(1)
+
+def init(opts_data,add_opts=[]):
 
-	if len(argv) == 2 and argv[1] == '--version':
+	if len(sys.argv) == 2 and sys.argv[1] == '--version':
 		print_version_info(); sys.exit()
 
-	opts,args,short_opts,long_opts = mmgen.opt.Opts.parse_opts(argv,help_data)
+	opts,args,short_opts,long_opts = mmgen.share.Opts.parse_opts(sys.argv,opts_data)
 
 	if g.debug:
 		print "short opts: %s" % repr(short_opts)
@@ -53,46 +75,58 @@ def parse_opts(argv,help_data):
 		print "user-selected opts: %s" % repr(opts)
 		print "cmd args:           %s" % repr(args)
 
-	for l in (
-	('from_incog_hidden','from_incog','from_seed','from_mnemonic','from_brain'),
-	('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic',
-	'export_seed'),
-	('quiet','verbose')
-	): warn_incompatible_opts(opts,l)
-
-	if 'usr_randchars' in opts: g.use_urandchars = True
-
-	# check opts[] dictionary without modifying it
+	# check opts without modifying them
 	if not check_opts(opts,long_opts): sys.exit(1)
 
+	# If user opt is set, an opt in mmgen.config is set to 'True'
+	for v in g.usr_set_vars:
+		if v in opts:
+			g.__dict__[g.usr_set_vars[v]] = True
+
 	# If user opt is unset, set it to default value in mmgen.config (g):
+	# If set, convert its type based on value in mmgen.config
 	for v in g.dfl_vars:
-		if v in opts: typeconvert_override_var(opts,v)
+		if v in opts: typeconvert_from_dfl(opts,v)
 		else: opts[v] = g.__dict__[v]
 
-	# Opposite of above: set the value in mmgen.config (g) from user opt:
-	for k in g.usr_set_vars:
-		if k in opts:
-			v = opts[k]
-			try: v = type(g.__dict__[k])(v)
-			except:
-				msg(
-	"Argument '%s' for option '--%s' cannot be converted to target type %s" %
-		(v,k.replace("_","-"),type(g.__dict__[k]))
-				)
-				sys.exit(1)
-			g.__dict__[k] = v
-
 	if g.debug: print "opts after typeconvert: %s" % opts
 
-	return opts,args
+	# A hack, but harmless
+	extra_opts = [
+		"quiet","verbose","debug",
+		"outdir","echo_passphrase","passwd_file"
+	] + add_opts
+
+	# Transfer opts into our custom namespace
+	for o in [s.rstrip("=") for s in long_opts] + extra_opts:
+		opt.__dict__[o] = opts[o] if o in opts else None
+
+	for l in (
+	('from_incog_hidden','from_incog','from_seed','from_mnemonic','from_brain'),
+	('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic',
+	'export_seed'),
+	('quiet','verbose')
+	): warn_incompatible_opts(l)
+
+	del mmgen.share.Opts
+	return args
 
+def show_opts_and_cmd_args(cmd_args):
+	print "Processed options:"
+	d = opt.__dict__
+	for k in d:
+		if k[:2] != "__" and k != "opts" and d[k] != None:
+			msg("%-20s: %s" % (k, d[k]))
+	print "Cmd args: %s" % repr(cmd_args)
 
-def show_opts_and_cmd_args(opts,cmd_args):
-	print "Processed options: %s" % repr(opts)
-	print "Cmd args:          %s" % repr(cmd_args)
+def show_all_opts():
+	msg("Processed options:")
+	d = opt.__dict__
+	for k in d:
+		if k[:2] != "__" and k != "opts":
+			msg("%-20s: %s" % (k, d[k]))
 
-def check_opts(opts,long_opts):
+def check_opts(opts,long_opts):       # Returns false if any check fails
 
 	def opt_splits(val,sep,n,what):
 		sepword = "comma" if sep == "," else (
@@ -136,10 +170,12 @@ def check_opts(opts,long_opts):
 		# Check for file existence and readability
 		if opt in ('keys_from_file','mmgen_keys_from_file',
 				'passwd_file','keysforaddrs','comment_file'):
+			from mmgen.util import check_infile
 			check_infile(val)  # exits on error
 			continue
 
 		if opt == 'outdir':
+			from mmgen.util import check_outdir
 			check_outdir(val)  # exits on error
 		elif opt == 'label':
 			if not opt_compares(len(val),"<=",g.max_wallet_label_len,"label length"):
@@ -155,11 +191,13 @@ def check_opts(opts,long_opts):
 			if opt == 'from_incog_hidden':
 				if not opt_splits(val,",",3,what): return False
 				infile,offset,seed_len = val.split(",")
+				from mmgen.util import check_infile
 				check_infile(infile)
 				w = "seed length " + what
 				if not opt_is_int(seed_len,w): return False
 				if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False
 			else:
+				from mmgen.util import check_outfile
 				if not opt_splits(val,",",2,what): return False
 				outfile,offset = val.split(",")
 				check_outfile(outfile)
@@ -184,24 +222,6 @@ def check_opts(opts,long_opts):
 			if not opt_compares(val,">=",g.min_urandchars,what): return False
 			if not opt_compares(val,"<=",g.max_urandchars,what): return False
 		else:
-			if g.debug: print "check_opts(): No test for opt '%s'" % opt
+			if 'debug' in opts: print "check_opts(): No test for opt '%s'" % opt
 
 	return True
-
-
-def typeconvert_override_var(opts,opt):
-
-	vtype = type(eval("g."+opt))
-	if g.debug: print "Override opt: %-15s [%s]" % (opt,vtype)
-
-	if   vtype == int:   f,t = int,"an integer"
-	elif vtype == str:   f,t = str,"a string"
-	elif vtype == float: f,t = float,"a float"
-	elif vtype == bool:  f,t = bool,"a boolean value"
-
-	try:
-		opts[opt] = f(opts[opt])
-	except:
-		msg("'%s': invalid parameter for '--%s' option (not %s)" %
-				(opts[opt],opt.replace("_","-"),t))
-		sys.exit(1)

+ 17 - 15
mmgen/opt/Opts.py → mmgen/share/Opts.py

@@ -18,23 +18,25 @@
 
 import sys, getopt
 
-def usage(hd):
-	print "USAGE: %s %s" % (hd['prog_name'], hd['usage'])
+def usage(opts_data):
+	print "USAGE: %s %s" % (opts_data['prog_name'], opts_data['usage'])
 	sys.exit(2)
 
-def print_help(help_data):
-	pn = help_data['prog_name']
+def print_help(opts_data):
+	pn = opts_data['prog_name']
 	pn_len = str(len(pn)+2)
-	print ("  %-"+pn_len+"s %s") % (pn.upper()+":", help_data['desc'].strip())
-	print ("  %-"+pn_len+"s %s %s")%("USAGE:", pn, help_data['usage'].strip())
+	print ("  %-"+pn_len+"s %s") % (pn.upper()+":", opts_data['desc'].strip())
+	print ("  %-"+pn_len+"s %s %s")%("USAGE:", pn, opts_data['usage'].strip())
 	sep = "\n    "
-	print "  OPTIONS:"+sep+"%s" % sep.join(help_data['options'].strip().split("\n"))
-	if "notes" in help_data:
-		print "  %s" % "\n  ".join(help_data['notes'][1:-1].split("\n"))
+	print "  OPTIONS:"+sep+"%s" % sep.join(opts_data['options'].strip().split("\n"))
+	if "notes" in opts_data:
+		print "  %s" % "\n  ".join(opts_data['notes'][1:-1].split("\n"))
 
 
-def process_opts(argv,help_data,short_opts,long_opts):
+def process_opts(argv,opts_data,short_opts,long_opts):
 
+	import os
+	opts_data['prog_name'] = os.path.split(sys.argv[0])[1]
 	long_opts  = [i.replace("_","-") for i in long_opts]
 
 	try: cl_opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
@@ -48,7 +50,7 @@ def process_opts(argv,help_data,short_opts,long_opts):
 		else:        short_opts_l     += i
 
 	for opt, arg in cl_opts:
-		if   opt in ("-h","--help"): print_help(help_data); sys.exit()
+		if   opt in ("-h","--help"): print_help(opts_data); sys.exit()
 		elif opt[:2] == "--" and opt[2:] in long_opts:
 			opts[opt[2:].replace("-","_")] = True
 		elif opt[:2] == "--" and opt[2:]+"=" in long_opts:
@@ -63,9 +65,9 @@ def process_opts(argv,help_data,short_opts,long_opts):
 	return opts,args
 
 
-def parse_opts(argv,help_data):
+def parse_opts(argv,opts_data):
 
-	lines = help_data['options'].strip().split("\n")
+	lines = opts_data['options'].strip().split("\n")
 	import re
 	pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{1,64})(=| )(.+)"
 	rep = r"-{0}, --{1}{w}{3}"
@@ -75,10 +77,10 @@ def parse_opts(argv,help_data):
 		if d[2] == " ": d[2] = ""
 	short_opts = "".join([d[0]+d[2].replace("=",":") for d in opt_data])
 	long_opts = [d[1].replace("-","_")+d[2] for d in opt_data]
-	help_data['options'] = "\n".join(
+	opts_data['options'] = "\n".join(
 		[rep.format(w=" ", *m.groups())
 			if m else k for m,k in [(re.match(pat,l),l) for l in lines]]
 	)
-	opts,args = process_opts(argv,help_data,short_opts,long_opts)
+	opts,args = process_opts(argv,opts_data,short_opts,long_opts)
 
 	return opts,args,short_opts,long_opts

BIN
mmgen/share/Opts.pyc


+ 0 - 0
mmgen/opt/__init__.py → mmgen/share/__init__.py


BIN
mmgen/share/__init__.pyc


+ 1 - 0
mmgen/term.py

@@ -22,6 +22,7 @@ term.py:  Terminal-handling routines for the MMGen suite
 
 import sys, os, struct
 import mmgen.config as g
+import opt
 from mmgen.util import msg, msg_r
 
 def _kb_hold_protect_unix():

+ 3 - 3
mmgen/test.py

@@ -23,7 +23,7 @@ addr.py:  Shared routines for the test suites
 import sys,os
 from binascii import hexlify
 from mmgen.util import msg,write_to_file
-import mmgen.config as g
+import mmgen.opt as opt
 
 _red,_grn,_yel,_cya,_reset = (
 	["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0"]
@@ -58,7 +58,7 @@ def get_tmpfile_fn(cfg,fn):
 	return os.path.join(cfg['tmpdir'],fn)
 
 def write_to_tmpfile(cfg,fn,data):
-	write_to_file(os.path.join(cfg['tmpdir'],fn),data,{},silent=True)
+	write_to_file(os.path.join(cfg['tmpdir'],fn),data,silent=True)
 
 def read_from_tmpfile(cfg,fn):
 	from mmgen.util import get_data_from_file
@@ -69,7 +69,7 @@ def read_from_file(fn):
 	return get_data_from_file(fn,silent=True)
 
 def ok():
-	if g.verbose or g.exact_output:
+	if opt.verbose or opt.exact_output:
 		sys.stderr.write(green("OK\n"))
 	else: msg(" OK")
 

+ 6 - 6
mmgen/tool.py

@@ -25,11 +25,11 @@ import mmgen.bitcoin as bitcoin
 import binascii as ba
 
 import mmgen.config as g
+import mmgen.opt as opt
 from mmgen.crypto import *
 from mmgen.util import *
 from mmgen.tx import *
 
-opts = {}
 from collections import OrderedDict
 cmd_data = OrderedDict([
 	("help",         []),
@@ -230,7 +230,7 @@ def print_convert_results(indata,enc,dec,dtype):
 
 	error = False if are_equal(indata,dec,dtype) else True
 
-	if error or g.verbose:
+	if error or opt.verbose:
 		Msg("Input:         %s" % repr(indata))
 		Msg("Encoded data:  %s" % repr(enc))
 		Msg("Recoded data:  %s" % repr(dec))
@@ -470,13 +470,13 @@ def hex2wif(hexpriv,compressed=False):
 
 def encrypt(infile,outfile="",hash_preset=""):
 	data = get_data_from_file(infile,"data for encryption")
-	enc_d = mmgen_encrypt(data,"user data",hash_preset,opts)
+	enc_d = mmgen_encrypt(data,"user data",hash_preset)
 	if outfile == '-':
 		write_to_stdout(enc_d,"encrypted data",confirm=True)
 	else:
 		if not outfile:
 			outfile = os.path.basename(infile) + "." + g.mmenc_ext
-		write_to_file(outfile, enc_d, opts,"encrypted data",True,True)
+		write_to_file(outfile,enc_d,"encrypted data",True,True)
 
 
 def decrypt(infile,outfile="",hash_preset=""):
@@ -494,7 +494,7 @@ def decrypt(infile,outfile="",hash_preset=""):
 				outfile = outfile[:-len(g.mmenc_ext)-1]
 			else:
 				outfile = outfile + ".dec"
-		write_to_file(outfile, dec_d, opts,"decrypted data",True,True)
+		write_to_file(outfile, dec_d, "decrypted data",True,True)
 
 
 def find_incog_data(filename,iv_id,keep_searching=False):
@@ -553,7 +553,7 @@ def rand2file(outfile, nbytes, threads=4, silent=False):
 	from threading import Thread
 	bsize = 2**20
 	roll = bsize * 4
-	if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile)
+	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 	f = open(outfile,"w")
 
 	from Crypto.Cipher import AES

+ 3 - 2
mmgen/tx.py

@@ -26,6 +26,7 @@ from decimal import Decimal
 from collections import OrderedDict
 
 import mmgen.config as g
+import mmgen.opt as opt
 from mmgen.util import *
 from mmgen.term import do_pager
 
@@ -45,7 +46,7 @@ def normalize_btc_amt(amt):
 		msg("%s: Invalid amount" % amt)
 		return False
 
-	if g.debug:
+	if opt.debug:
 		print "Decimal(amt): %s\nAs tuple: %s" % (amt,repr(ret.as_tuple()))
 
 	if ret.as_tuple()[-1] < -8:
@@ -260,7 +261,7 @@ def wiftoaddr_keyconv(wif):
 		return wiftoaddr(wif)
 
 def get_wif2addr_f():
-	if g.no_keyconv: return wiftoaddr
+	if opt.no_keyconv: return wiftoaddr
 	from mmgen.addr import test_for_keyconv
 	return wiftoaddr_keyconv if test_for_keyconv() else wiftoaddr
 

+ 63 - 51
mmgen/util.py

@@ -26,27 +26,37 @@ from binascii import hexlify,unhexlify
 
 import mmgen.config as g
 
-def msg(s):    sys.stderr.write(s + "\n")
+_red,_grn,_yel,_cya,_reset = (
+	["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0"]
+)
+def red(s):    return _red+s+_reset
+def green(s):  return _grn+s+_reset
+def yellow(s): return _yel+s+_reset
+def cyan(s):   return _cya+s+_reset
+
+def msgred(s):    sys.stderr.write(red(s+"\n"))
+
+def msg(s):    sys.stderr.write(s+"\n")
 def msg_r(s):  sys.stderr.write(s)
 def qmsg(s,alt=False):
-	if g.quiet:
+	if opt.quiet:
 		if alt != False: sys.stderr.write(alt + "\n")
 	else: sys.stderr.write(s + "\n")
 def qmsg_r(s,alt=False):
-	if g.quiet:
+	if opt.quiet:
 		if alt != False: sys.stderr.write(alt)
 	else: sys.stderr.write(s)
 def vmsg(s):
-	if g.verbose: sys.stderr.write(s + "\n")
+	if opt.verbose: sys.stderr.write(s + "\n")
 def vmsg_r(s):
-	if g.verbose: sys.stderr.write(s)
+	if opt.verbose: sys.stderr.write(s)
 
 def Msg(s):    sys.stdout.write(s + "\n")
 def Msg_r(s):  sys.stdout.write(s)
 def Vmsg(s):
-	if g.verbose: sys.stdout.write(s + "\n")
+	if opt.verbose: sys.stdout.write(s + "\n")
 def Vmsg_r(s):
-	if g.verbose: sys.stdout.write(s)
+	if opt.verbose: sys.stdout.write(s)
 
 
 def msgrepr(*args):
@@ -122,8 +132,10 @@ def is_utf8(s):
 def match_ext(addr,ext):
 	return addr.split(".")[-1] == ext
 
-def get_from_brain_opt_params(opts):
-	l,p = opts['from_brain'].split(",")
+import opt as opt
+
+def get_from_brain_opt_params():
+	l,p = opt.from_brain.split(",")
 	return(int(l),p)
 
 def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
@@ -165,7 +177,7 @@ def compare_checksums(chksum1, desc1, chksum2, desc2):
 		vmsg("OK (%s)" % chksum1.upper())
 		return True
 	else:
-		if g.debug:
+		if opt.debug:
 			print \
 	"ERROR!\nComputed checksum %s (%s) doesn't match checksum %s (%s)" \
 			% (desc1,chksum1,desc2,chksum2)
@@ -266,18 +278,18 @@ def parse_addr_idxs(arg,sep=","):
 	return sorted(set(ret))
 
 
-def get_new_passphrase(what, opts, passchg=False):
+def get_new_passphrase(what,  passchg=False):
 
 	w = "{}passphrase for {}".format("new " if passchg else "", what)
-	if 'passwd_file' in opts:
-		pw = " ".join(_get_words_from_file(opts['passwd_file'],w))
-	elif 'echo_passphrase' in opts:
-		pw = " ".join(_get_words_from_user("Enter {}: ".format(w), opts))
+	if opt.passwd_file:
+		pw = " ".join(_get_words_from_file(opt.passwd_file,w))
+	elif opt.echo_passphrase:
+		pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
 	else:
 		for i in range(g.passwd_max_tries):
-			pw = " ".join(_get_words_from_user("Enter {}: ".format(w),opts))
-			pw2 = " ".join(_get_words_from_user("Repeat passphrase: ",opts))
-			if g.debug: print "Passphrases: [%s] [%s]" % (pw,pw2)
+			pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
+			pw2 = " ".join(_get_words_from_user("Repeat passphrase: "))
+			if opt.debug: print "Passphrases: [%s] [%s]" % (pw,pw2)
 			if pw == pw2:
 				vmsg("Passphrases match"); break
 			else: msg("Passphrases do not match.  Try again.")
@@ -324,9 +336,9 @@ def write_to_stdout(data, what, confirm=True):
 	sys.stdout.write(data)
 
 
-def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True,silent=False):
+def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True,silent=False):
 
-	if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile)
+	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 	from os import stat
 	try:    stat(outfile)
@@ -355,18 +367,18 @@ def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=
 	return True
 
 
-def write_to_file_or_stdout(outfile, data, opts, what="data"):
+def write_to_file_or_stdout(outfile, data,  what="data"):
 
-	if 'stdout' in opts or not sys.stdout.isatty():
+	if opt.stdout or not sys.stdout.isatty():
 		write_to_stdout(data, what, confirm=True)
 	else:
-		write_to_file(outfile,data,opts,what,not g.quiet,True)
+		write_to_file(outfile,data,what,not opt.quiet,True)
 
 
 from mmgen.bitcoin import b58decode_pad,b58encode_pad
 
 def display_control_data(label,metadata,hash_preset,salt,enc_seed):
-	msg("WALLET DATA")
+	print "WALLET DATA"
 	fs = "  {:18} {}"
 	pw_empty = "yes" if metadata[3] == "E" else "no"
 	for i in (
@@ -379,7 +391,7 @@ def display_control_data(label,metadata,hash_preset,salt,enc_seed):
 				" ".join([str(i) for i in get_hash_params(hash_preset)]))),
 		("Passphrase empty?", pw_empty.capitalize()),
 		("Timestamp:",           "%s UTC" % metadata[4]),
-	): msg(fs.format(*i))
+	): print fs.format(*i)
 
 	fs = "  {:6} {}"
 	for i in (
@@ -389,16 +401,16 @@ def display_control_data(label,metadata,hash_preset,salt,enc_seed):
 		("Encrypted seed:", ""),
 		("  b58:",      b58encode_pad(enc_seed)),
 		("  hex:",      hexlify(enc_seed))
-	): msg(fs.format(*i))
+	): print fs.format(*i)
 
 
-def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
+def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
 
 	seed_id = make_chksum_8(seed)
 	seed_len = str(len(seed)*8)
 	pw_status = "NE" if len(passwd) else "E"
-	hash_preset = opts['hash_preset']
-	label = opts['label'] if 'label' in opts else "No Label"
+	hash_preset = opt.hash_preset
+	label = opt.label if opt.label else "No Label"
 	metadata = seed_id.lower(),key_id.lower(),seed_len,\
 		pw_status,make_timestamp()
 	sf  = b58encode_pad(salt)
@@ -417,9 +429,9 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
 		seed_id,key_id,seed_len,hash_preset,g.wallet_ext)
 
 	d = "\n".join((chk,)+lines)+"\n"
-	write_to_file(outfile,d,opts,"wallet",not g.quiet,True)
+	write_to_file(outfile,d,"wallet",not opt.quiet,True)
 
-	if g.debug:
+	if opt.debug:
 		display_control_data(label,metadata,hash_preset,salt,enc_seed)
 
 
@@ -467,7 +479,7 @@ def _check_chksum_6(chk,val,desc,infile):
 		msg("%s checksum incorrect in file '%s'!" % (desc,infile))
 		msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
 		sys.exit(2)
-	elif g.debug:
+	elif opt.debug:
 		print "%s checksum passed: %s" % (desc.capitalize(),chk)
 
 
@@ -475,7 +487,7 @@ def get_data_from_wallet(infile,silent=False):
 
 	# Don't make this a qmsg: User will be prompted for passphrase and must see
 	# the filename.
-	if not silent and not g.quiet:
+	if not silent and not opt.quiet:
 		msg("Getting {} wallet data from file '{}'".format(g.proj_name,infile))
 
 	f = open_file_or_exit(infile, 'r')
@@ -515,10 +527,10 @@ def get_data_from_wallet(infile,silent=False):
 	return label,metadata,hash_preset,res['salt'],res['enc_seed']
 
 
-def _get_words_from_user(prompt, opts):
+def _get_words_from_user(prompt):
 	# split() also strips
-	words = my_raw_input(prompt, echo='echo_passphrase' in opts).split()
-	if g.debug: print "Sanitized input: [%s]" % " ".join(words)
+	words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
+	if opt.debug: print "Sanitized input: [%s]" % " ".join(words)
 	return words
 
 
@@ -528,15 +540,15 @@ def _get_words_from_file(infile,what):
 	# split() also strips
 	words = f.read().split()
 	f.close()
-	if g.debug: print "Sanitized input: [%s]" % " ".join(words)
+	if opt.debug: print "Sanitized input: [%s]" % " ".join(words)
 	return words
 
 
-def get_words(infile,what,prompt,opts):
+def get_words(infile,what,prompt):
 	if infile:
 		return _get_words_from_file(infile,what)
 	else:
-		return _get_words_from_user(prompt,opts)
+		return _get_words_from_user(prompt)
 
 def remove_comments(lines):
 	import re
@@ -594,31 +606,31 @@ def get_seed_from_seed_data(words):
 
 passwd_file_used = False
 
-def mark_passwd_file_as_used(opts):
+def mark_passwd_file_as_used():
 	global passwd_file_used
 	if passwd_file_used:
-		msg_r("WARNING: Reusing passphrase from file '%s'." % opts['passwd_file'])
+		msg_r("WARNING: Reusing passphrase from file '%s'." % opt.passwd_file)
 		msg(" This may not be what you want!")
 	passwd_file_used = True
 
 
-def get_mmgen_passphrase(prompt_info,opts,passchg=False):
+def get_mmgen_passphrase(prompt_info,passchg=False):
 	prompt = "Enter {}passphrase for {}: ".format(
 			"old " if passchg else "",prompt_info)
-	if 'passwd_file' in opts:
-		mark_passwd_file_as_used(opts)
-		return " ".join(_get_words_from_file(opts['passwd_file'],"passphrase"))
+	if opt.passwd_file:
+		mark_passwd_file_as_used()
+		return " ".join(_get_words_from_file(opt.passwd_file,"passphrase"))
 	else:
-		return " ".join(_get_words_from_user(prompt,opts))
+		return " ".join(_get_words_from_user(prompt))
 
 
-def get_bitcoind_passphrase(prompt,opts):
-	if 'passwd_file' in opts:
-		mark_passwd_file_as_used(opts)
-		return get_data_from_file(opts['passwd_file'],
+def get_bitcoind_passphrase(prompt):
+	if opt.passwd_file:
+		mark_passwd_file_as_used()
+		return get_data_from_file(opt.passwd_file,
 				"passphrase").strip("\r\n")
 	else:
-		return my_raw_input(prompt, echo='echo_passphrase' in opts)
+		return my_raw_input(prompt, echo=opt.echo_passphrase)
 
 
 def check_data_fits_file_at_offset(fname,offset,dlen,action):

+ 25 - 3
setup.py

@@ -1,12 +1,33 @@
 #!/usr/bin/env python
+#
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
+# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 from distutils.core import setup
 
 setup(
 		name         = 'mmgen',
+		decription   = 'A complete Bitcoin cold-storage solution for the command line',
 		version      = '0.7.9',
 		author       = 'Philemon',
 		author_email = 'mmgen-py@yandex.com',
 		url          = 'https://github.com/mmgen/mmgen',
+		license      = 'GNU GPL v3',
+		platforms    = 'Linux, MS Windows',
+		keywords     = 'Bitcoin, wallet, cold storage, offline storage, open-source, command-line, Python, Bitcoin Core, bitcoind',
 		py_modules = [
 			'mmgen.__init__',
 			'mmgen.addr',
@@ -17,7 +38,8 @@ setup(
 			'mmgen.mn_electrum',
 			'mmgen.mnemonic',
 			'mmgen.mn_tirosh',
-			'mmgen.Opts',
+			'mmgen.opts',
+			'mmgen.opt',
 			'mmgen.term',
 			'mmgen.test',
 			'mmgen.tool',
@@ -36,8 +58,8 @@ setup(
 			'mmgen.main_walletchk',
 			'mmgen.main_walletgen',
 
-			'mmgen.opt.__init__',
-			'mmgen.opt.Opts',
+			'mmgen.share.__init__',
+			'mmgen.share.Opts',
 
 			'mmgen.rpc.__init__',
 			'mmgen.rpc.config',

+ 49 - 51
test/test.py

@@ -9,6 +9,7 @@ os.chdir(os.path.join(pn,os.pardir))
 sys.path.__setitem__(0,os.path.abspath(os.curdir))
 
 import mmgen.config as g
+import mmgen.opt as opt
 from mmgen.util import msgrepr,msgrepr_exit,Msg
 from mmgen.test import *
 
@@ -202,15 +203,14 @@ meta_cmds = OrderedDict([
 	['tool',   (9,("tool_encrypt","tool_decrypt","tool_encrypt_ref","tool_decrypt_ref"))],
 ])
 
-from mmgen.Opts import *
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc': "Test suite for the MMGen suite",
 	'usage':"[options] [command or metacommand]",
 	'options': """
 -h, --help         Print this help message
 -b, --buf-keypress Use buffered keypresses as with real human input
 -d, --debug        Produce debugging output
+-D, --direct-exec  Bypass pexpect and execute a command directly (for debugging only)
 -e, --exact-output Show the exact output of the MMGen script(s) being run
 -l, --list-cmds    List and describe the tests and commands in the test suite
 -p, --pause        Pause between tests, resuming on keypress
@@ -224,47 +224,43 @@ If no command is given, the whole suite of tests is run.
 """
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data)
 
-if 'system' in opts: sys.path.pop(0)
+if opt.system: sys.path.pop(0)
 
-env = os.environ
-if 'buf_keypress' in opts:
+if opt.buf_keypress:
 	send_delay = 0.3
 else:
 	send_delay = 0
-	env["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
+	os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
 
-for k in 'debug','verbose','exact_output','pause','quiet':
-	g.__dict__[k] = True if k in opts else False
+if opt.debug: opt.verbose = True
 
-if g.debug: g.verbose = True
-
-if g.exact_output:
+if opt.exact_output:
 	def msg(s): pass
 	vmsg = vmsg_r = msg_r = msg
 else:
 	def msg(s): sys.stderr.write(s+"\n")
 	def vmsg(s):
-		if g.verbose: sys.stderr.write(s+"\n")
+		if opt.verbose: sys.stderr.write(s+"\n")
 	def msg_r(s): sys.stderr.write(s)
 	def vmsg_r(s):
-		if g.verbose: sys.stderr.write(s)
+		if opt.verbose: sys.stderr.write(s)
 
 stderr_save = sys.stderr
 
 def silence():
-	if not (g.verbose or g.exact_output):
+	if not (opt.verbose or opt.exact_output):
 		sys.stderr = open("/dev/null","a")
 
 def end_silence():
-	if not (g.verbose or g.exact_output):
+	if not (opt.verbose or opt.exact_output):
 		sys.stderr = stderr_save
 
 def errmsg(s): stderr_save.write(s+"\n")
 def errmsg_r(s): stderr_save.write(s)
 
-if "list_cmds" in opts:
+if opt.list_cmds:
 	fs = "  {:<{w}} - {}"
 	Msg("Available commands:")
 	w = max([len(i) for i in cmd_data])
@@ -281,15 +277,14 @@ if "list_cmds" in opts:
 	sys.exit()
 
 import pexpect,time,re
-import mmgen.config as g
 from mmgen.util import get_data_from_file,write_to_file,get_lines_from_file
 
 def my_send(p,t,delay=send_delay,s=False):
 	if delay: time.sleep(delay)
 	ret = p.send(t) # returns num bytes written
 	if delay: time.sleep(delay)
-	if g.verbose:
-		ls = "" if g.debug or not s else " "
+	if opt.verbose:
+		ls = "" if opt.debug or not s else " "
 		es = "" if s else "  "
 		msg("%sSEND %s%s" % (ls,es,yellow("'%s'"%t.replace('\n',r'\n'))))
 	return ret
@@ -297,7 +292,7 @@ def my_send(p,t,delay=send_delay,s=False):
 def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
 	quo = "'" if type(s) == str else ""
 
-	if g.verbose: msg_r("EXPECT %s" % yellow(quo+str(s)+quo))
+	if opt.verbose: msg_r("EXPECT %s" % yellow(quo+str(s)+quo))
 	else:       msg_r("+")
 
 	try:
@@ -309,7 +304,7 @@ def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
 		errmsg(red("\nERROR.  Expect %s%s%s timed out.  Exiting" % (quo,s,quo)))
 		sys.exit(1)
 
-	if g.debug or (g.verbose and type(s) != str): msg_r(" ==> %s " % ret)
+	if opt.debug or (opt.verbose and type(s) != str): msg_r(" ==> %s " % ret)
 
 	if ret == -1:
 		errmsg("Error.  Expect returned %s" % ret)
@@ -329,7 +324,7 @@ def get_file_with_ext(ext,mydir,delete=True):
 
 	if len(flist) > 1:
 		if delete:
-			if not g.quiet:
+			if not opt.quiet:
 				msg("Multiple *.%s files in '%s' - deleting" % (ext,mydir))
 			for f in flist: os.unlink(f)
 		return False
@@ -341,7 +336,7 @@ def get_addrfile_checksum(display=False):
 	silence()
 	from mmgen.addr import AddrInfo
 	chk = AddrInfo(addrfile).checksum
-	if g.verbose and display: msg("Checksum: %s" % cyan(chk))
+	if opt.verbose and display: msg("Checksum: %s" % cyan(chk))
 	end_silence()
 	return chk
 
@@ -354,21 +349,24 @@ def verify_checksum_or_exit(checksum,chk):
 
 class MMGenExpect(object):
 
-	def __init__(self,name,mmgen_cmd,cmd_args=[],env=env):
-		if not 'system' in opts:
+	def __init__(self,name,mmgen_cmd,cmd_args=[]):
+		if not opt.system:
 			mmgen_cmd = os.path.join(os.curdir,mmgen_cmd)
 		desc = cmd_data[name][1]
-		if g.verbose or g.exact_output:
+		if opt.verbose or opt.exact_output:
 			sys.stderr.write(
 				green("Testing %s\nExecuting " % desc) +
 				cyan("'%s %s'\n" % (mmgen_cmd," ".join(cmd_args)))
 			)
 		else:
 			msg_r("Testing %s " % (desc+":"))
-#		msgrepr(mmgen_cmd,cmd_args); msg("")
-		if env: self.p = pexpect.spawn(mmgen_cmd,cmd_args,env=env)
-		else:   self.p = pexpect.spawn(mmgen_cmd,cmd_args)
-		if g.exact_output: self.p.logfile = sys.stdout
+
+		if opt.direct_exec:
+			os.system(" ".join([mmgen_cmd] + cmd_args))
+			sys.exit()
+		else:
+			self.p = pexpect.spawn(mmgen_cmd,cmd_args)
+			if opt.exact_output: self.p.logfile = sys.stdout
 
 	def license(self):
 		p = "'w' for conditions and warranty info, or 'c' to continue: "
@@ -381,7 +379,7 @@ class MMGenExpect(object):
 			vmsg_r("SEND ")
 			while self.p.expect('left: ',0.1) == 0:
 				ch = rand_chars.pop(0)
-				msg_r(yellow(ch)+" " if g.verbose else "+")
+				msg_r(yellow(ch)+" " if opt.verbose else "+")
 				self.p.send(ch)
 		except:
 			vmsg("EOT")
@@ -476,12 +474,12 @@ def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input='')
 		btcaddr = privnum2addr(privnum,compressed=True)
 		of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
 		write_to_file(of, hextowif("{:064x}".format(privnum),
-					compressed=True)+"\n",{},"compressed bitcoin key")
+					compressed=True)+"\n","compressed bitcoin key")
 
 		add_fake_unspent_entry(out,btcaddr,"Non-MMGen address")
 
 #	msg("\n".join([repr(o) for o in out])); sys.exit()
-	write_to_file(unspent_data_file,repr(out),{},"Unspent outputs",verbose=True)
+	write_to_file(unspent_data_file,repr(out),"Unspent outputs",verbose=True)
 
 
 def add_comments_to_addr_file(addrfile,tfile):
@@ -503,18 +501,18 @@ def make_brainwallet_file(fn):
 		return "".join([ws_list[getrandnum(1)%len(ws_list)] for i in range(nchars)])
 	rand_pairs = [wl[getrandnum(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
 	d = "".join(rand_pairs).rstrip() + "\n"
-	if g.verbose: msg_r("Brainwallet password:\n%s" % cyan(d))
-	write_to_file(fn,d,{},"brainwallet password")
+	if opt.verbose: msg_r("Brainwallet password:\n%s" % cyan(d))
+	write_to_file(fn,d,"brainwallet password")
 
 def do_between():
-	if g.pause:
+	if opt.pause:
 		from mmgen.util import keypress_confirm
 		if keypress_confirm(green("Continue?"),default_yes=True):
-			if g.verbose or g.exact_output: sys.stderr.write("\n")
+			if opt.verbose or opt.exact_output: sys.stderr.write("\n")
 		else:
 			errmsg("Exiting at user request")
 			sys.exit()
-	elif g.verbose or g.exact_output:
+	elif opt.verbose or opt.exact_output:
 		sys.stderr.write("\n")
 
 
@@ -569,7 +567,7 @@ def refcheck(what,chk,refchk):
 	if chk == refchk:
 		ok()
 	else:
-		if not g.verbose: errmsg("")
+		if not opt.verbose: errmsg("")
 		errmsg(red("""
 Fatal error - %s '%s' does not match reference value '%s'.  Aborting test
 """.strip() % (what,chk,refchk)))
@@ -586,7 +584,7 @@ def check_deps(ts,name,cmds):
 		msg("'%s': unrecognized command" % cmd)
 		sys.exit(1)
 
-	if not g.quiet:
+	if not opt.quiet:
 		msg("Checking dependencies for '%s'" % (cmd))
 
 	check_needs_rerun(ts,cmd,build=False)
@@ -603,8 +601,8 @@ def clean(dirs=[]):
 	ts = MMGenTestSuite()
 	dirlist = ts.list_tmp_dirs()
 	if not dirs: dirs = dirlist.keys()
-	for d in dirs:
-		if d in sorted(dirlist):
+	for d in sorted(dirs):
+		if d in dirlist:
 			cleandir(dirlist[d])
 		else:
 			msg("%s: invalid directory number" % d)
@@ -741,7 +739,7 @@ class MMGenTestSuite(object):
 		self.txcreate_common(name,sources=['1'])
 
 	def txcreate_common(self,name,sources=['1'],non_mmgen_input=''):
-		if g.verbose or g.exact_output:
+		if opt.verbose or opt.exact_output:
 			sys.stderr.write(green("Generating fake transaction info\n"))
 		silence()
 		from mmgen.addr import AddrInfo,AddrInfoList
@@ -784,11 +782,11 @@ class MMGenTestSuite(object):
 
 		for num in tx_data: cmd_args += [tx_data[num]['addrfile']]
 
-		env["MMGEN_BOGUS_WALLET_DATA"] = unspent_data_file
+		os.environ["MMGEN_BOGUS_WALLET_DATA"] = unspent_data_file
 		end_silence()
-		if g.verbose or g.exact_output: sys.stderr.write("\n")
+		if opt.verbose or opt.exact_output: sys.stderr.write("\n")
 
-		t = MMGenExpect(name,"mmgen-txcreate",cmd_args,env)
+		t = MMGenExpect(name,"mmgen-txcreate",cmd_args)
 		t.license()
 		for num in tx_data.keys():
 			t.expect_getend("Getting address data from file ")
@@ -871,7 +869,7 @@ class MMGenTestSuite(object):
 	def export_incog_hidden(self,name,walletfile):
 		rf,rd = os.path.join(cfg['tmpdir'],hincog_fn),os.urandom(hincog_bytes)
 		vmsg(green("Writing %s bytes of data to file '%s'" % (hincog_bytes,rf)))
-		write_to_file(rf,rd,{},verbose=g.verbose)
+		write_to_file(rf,rd,verbose=opt.verbose)
 		t = self.export_incog(name,walletfile,args=["-G","%s,%s"%(rf,hincog_offset)])
 		t.written_to_file("Data",query="")
 		ok()
@@ -1033,7 +1031,7 @@ class MMGenTestSuite(object):
 
 	def tool_encrypt_ref(self,name):
 		infn = get_tmpfile_fn(cfg,cfg['tool_enc_ref_infn'])
-		write_to_file(infn,cfg['tool_enc_reftext'],{},silent=True)
+		write_to_file(infn,cfg['tool_enc_reftext'],silent=True)
 		self.tool_encrypt(name,infn)
 
 	# Two deps produced by one prog is broken - TODO
@@ -1051,7 +1049,7 @@ class MMGenTestSuite(object):
 		self.tool_decrypt(name,f1,f2)
 
 # main()
-if g.pause:
+if opt.pause:
 	import termios,atexit
 	fd = sys.stdin.fileno()
 	old = termios.tcgetattr(fd)

+ 8 - 14
test/tooltest.py

@@ -8,7 +8,7 @@ pn = os.path.dirname(sys.argv[0])
 os.chdir(os.path.join(pn,os.pardir))
 sys.path.__setitem__(0,os.path.abspath(os.curdir))
 
-import mmgen.config as g
+import mmgen.opt as opt
 from mmgen.util import msg,msg_r,vmsg,vmsg_r,Msg,msgrepr, msgrepr_exit
 from collections import OrderedDict
 
@@ -74,9 +74,7 @@ cfg = {
 	'tmpdir_num':    10,
 }
 
-from mmgen.Opts import *
-help_data = {
-	'prog_name': g.prog_name,
+opts_data = {
 	'desc': "Test suite for the 'mmgen-tool' utility",
 	'usage':"[options] [command]",
 	'options': """
@@ -92,18 +90,15 @@ If no command is given, the whole suite of tests is run.
 """
 }
 
-opts,cmd_args = parse_opts(sys.argv,help_data)
+cmd_args = opt.opts.init(opts_data,add_opts=["exact_output"])
 
-if 'system' in opts: sys.path.pop(0)
+if opt.system: sys.path.pop(0)
 
 env = os.environ
 
-for k in 'debug','verbose','quiet','exact_output':
-	g.__dict__[k] = True if k in opts else False
+if opt.debug: opt.verbose = True
 
-if g.debug: g.verbose = True
-
-if "list_cmds" in opts:
+if opt.list_cmds:
 	fs = "  {:<{w}} - {}"
 	Msg("Available commands:")
 	w = max([len(i) for i in cmd_data])
@@ -114,7 +109,6 @@ if "list_cmds" in opts:
 	sys.exit()
 
 import binascii
-import mmgen.config as g
 from mmgen.test import *
 from mmgen.util import get_data_from_file,write_to_file,get_lines_from_file
 from mmgen.tx import is_wif,is_btc_addr,is_b58_str
@@ -162,14 +156,14 @@ class MMGenToolTestSuite(object):
 
 	def run_cmd(self,name,tool_args,kwargs="",extra_msg="",silent=False):
 		mmgen_tool = "mmgen-tool"
-		if not 'system' in opts:
+		if not opt.system:
 			mmgen_tool = os.path.join(os.curdir,mmgen_tool)
 
 		sys_cmd = [mmgen_tool, "-d",cfg['tmpdir'], name] + tool_args + kwargs.split()
 		if extra_msg: extra_msg = "(%s)" % extra_msg
 		full_name = " ".join([name]+kwargs.split()+extra_msg.split())
 		if not silent:
-			if g.verbose:
+			if opt.verbose:
 				sys.stderr.write(green("Testing %s\nExecuting " % full_name))
 				sys.stderr.write("%s\n" % cyan(repr(sys_cmd)))
 			else: