__init__.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2026 The MMGen Project <mmgen@tuta.io>
  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. """
  19. help: help notes for MMGen suite commands
  20. """
  21. import sys
  22. from ..cfg import gc
  23. def version(cfg):
  24. from ..util import fmt
  25. print(fmt(f"""
  26. {gc.prog_name.upper()} version {gc.version}
  27. Part of {gc.proj_name} Wallet, an online/offline cryptocurrency wallet for the
  28. command line. Copyright (C){gc.Cdates} {gc.author} {gc.email}
  29. """, indent=' ').rstrip())
  30. sys.exit(0)
  31. def list_daemon_ids(cfg):
  32. from ..daemon import CoinDaemon
  33. from ..util import msg, fmt_list
  34. msg(' {} {}'.format('Coin', 'Daemon IDs'))
  35. msg(' {} {}'.format('----', '----------'))
  36. for k, v in CoinDaemon.coins.items():
  37. msg(' {} {}'.format(k, fmt_list(v.daemon_ids, fmt='barest')))
  38. sys.exit(0)
  39. def show_hash_presets(cfg):
  40. fs = ' {:<6} {:<3} {:<2} {}'
  41. from ..util import msg
  42. from ..crypto import Crypto
  43. msg(' Available parameters for scrypt.hash():')
  44. msg(fs.format('Preset', 'N', 'r', 'p'))
  45. for i in sorted(Crypto.hash_presets.keys()):
  46. msg(fs.format(i, *Crypto.hash_presets[i]))
  47. msg(' N = memory usage (power of two)\n p = iterations (rounds)')
  48. sys.exit(0)
  49. def gen_arg_tuple(cfg, func, text):
  50. def help_notes(k, *args, **kwargs):
  51. import importlib
  52. return getattr(importlib.import_module(
  53. f'{cfg._help_pkg}.help_notes').help_notes(proto, cfg), k)(*args, **kwargs)
  54. def help_mod(modname):
  55. import importlib
  56. return importlib.import_module(
  57. f'{cfg._opts.help_pkg}.{modname}').help(proto, cfg)
  58. from ..protocol import init_proto_from_cfg
  59. proto = init_proto_from_cfg(cfg, need_amt=True)
  60. d = {
  61. 'proto': proto,
  62. 'help_notes': help_notes,
  63. 'help_mod': help_mod,
  64. 'cfg': cfg}
  65. for arg in func.__code__.co_varnames:
  66. yield d[arg] if arg in d else text
  67. def make_usage_str(cfg, *, caller):
  68. def gen():
  69. single_line = isinstance(cfg._usage_data, str)
  70. ulbl = 'USAGE:' + (' ' if single_line else join_str)
  71. for line in [cfg._usage_data.strip()] if single_line else cfg._usage_data:
  72. yield '{a}{b}{c} {d}'.format(
  73. a = ulbl,
  74. b = gc.prog_name,
  75. c = '' if cfg.coin in (None, 'BTC') else f' --coin={cfg.coin.lower()}',
  76. d = cfg._usage_code(*gen_arg_tuple(cfg, cfg._usage_code, line))
  77. if cfg._usage_code else line)
  78. ulbl = ''
  79. join_str = {'help': '\n ', 'user': '\n '}[caller]
  80. return join_str.join(gen())
  81. def usage(cfg):
  82. print(make_usage_str(cfg, caller='user'))
  83. sys.exit(0)
  84. class Help:
  85. def make(self, cfg, opts):
  86. def gen_output():
  87. yield ' {} {}'.format(gc.prog_name.upper() + ':', opts.opts_data['text']['desc'].strip())
  88. yield ''
  89. yield make_usage_str(cfg, caller='help')
  90. yield ''
  91. yield self.help_type.upper().replace('_', ' ') + ':'
  92. # process code for options
  93. opts_text = nl.join(self.gen_text(opts))
  94. if 'options' in code:
  95. yield code['options'](*gen_arg_tuple(cfg, code['options'], opts_text))
  96. else:
  97. yield opts_text
  98. # process code for notes
  99. if 'notes' in text:
  100. if 'notes' in code:
  101. yield from code['notes'](*gen_arg_tuple(cfg, code['notes'], text['notes'])).splitlines()
  102. else:
  103. yield from text['notes'].splitlines()
  104. text = getattr(opts, self.data_desc)['text']
  105. code = getattr(opts, self.data_desc).get('code', {})
  106. nl = '\n '
  107. return nl.join(gen_output()) + '\n'
  108. class CmdHelp_v1(Help):
  109. help_type = 'options'
  110. data_desc = 'opts_data'
  111. def gen_text(self, opts):
  112. from ..opts import cmd_opts_v1_pat
  113. skipping = False
  114. for line in opts.opts_data['text']['options'].strip().splitlines():
  115. if m := cmd_opts_v1_pat.match(line):
  116. yield '{} --{} {}'.format(
  117. (f'-{m[1]},', ' ')[m[1] == '-'],
  118. m[2],
  119. m[4])
  120. elif not skipping:
  121. yield line
  122. class CmdHelp_v2(CmdHelp_v1):
  123. def gen_text(self, opts):
  124. from ..opts import cmd_opts_v2_help_pat
  125. skipping = False
  126. coin_codes = opts.global_filter_codes.coin
  127. cmd_codes = opts.opts_data['filter_codes']
  128. for line in opts.opts_data['text']['options'][1:].rstrip().splitlines():
  129. m = cmd_opts_v2_help_pat.match(line)
  130. if m[1] == '+':
  131. if not skipping:
  132. yield line[6:]
  133. elif (coin_codes is None or m[1] in coin_codes) and m[2] in cmd_codes:
  134. yield '{} --{} {}'.format(
  135. (f'-{m[3]},', ' ')[m[3] == '-'],
  136. m[4],
  137. m[6]
  138. ) if m[4] else m[6]
  139. skipping = False
  140. else:
  141. skipping = True
  142. class GlobalHelp(Help):
  143. help_type = 'global_options'
  144. data_desc = 'global_opts_data'
  145. def gen_text(self, opts):
  146. from ..opts import global_opts_help_pat
  147. skipping = False
  148. coin_codes = opts.global_filter_codes.coin
  149. cmd_codes = opts.global_filter_codes.cmd
  150. for line in opts.global_opts_data['text']['options'][1:].rstrip().splitlines():
  151. m = global_opts_help_pat.match(line)
  152. if m[1] == '+':
  153. if not skipping:
  154. yield line[4:]
  155. elif (coin_codes is None or m[1] in coin_codes) and (cmd_codes is None or m[2] in cmd_codes):
  156. yield ' --{} {}'.format(m[3], m[5]) if m[3] else m[5]
  157. skipping = False
  158. else:
  159. skipping = True
  160. def print_help(cfg, opts):
  161. if cfg.help:
  162. help_cls = CmdHelp_v2 if 'filter_codes' in opts.opts_data else CmdHelp_v1
  163. else:
  164. help_cls = GlobalHelp
  165. from ..ui import do_pager
  166. do_pager(help_cls().make(cfg, opts))
  167. sys.exit(0)