__init__.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2025 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. indent, col1_w = {
  69. 'help': (2, len(gc.prog_name) + 1),
  70. 'user': (0, len('USAGE:')),
  71. }[caller]
  72. def gen():
  73. ulbl = 'USAGE:'
  74. for line in [cfg._usage_data.strip()] if isinstance(cfg._usage_data, str) else cfg._usage_data:
  75. yield '{a:{w}} {b} {c}'.format(
  76. a = ulbl,
  77. b = gc.prog_name,
  78. c = cfg._usage_code(*gen_arg_tuple(cfg, cfg._usage_code, line)) if cfg._usage_code else line,
  79. w = col1_w)
  80. ulbl = ''
  81. return ('\n' + (' ' * indent)).join(gen())
  82. def usage(cfg):
  83. print(make_usage_str(cfg, caller='user'))
  84. sys.exit(0)
  85. class Help:
  86. def make(self, cfg, opts):
  87. def gen_output():
  88. yield ' {} {}'.format(gc.prog_name.upper() + ':', opts.opts_data['text']['desc'].strip())
  89. yield make_usage_str(cfg, caller='help')
  90. yield self.help_type.upper().replace('_', ' ') + ':'
  91. # process code for options
  92. opts_text = nl.join(self.gen_text(opts))
  93. if 'options' in code:
  94. yield code['options'](*gen_arg_tuple(cfg, code['options'], opts_text))
  95. else:
  96. yield opts_text
  97. # process code for notes
  98. if 'notes' in text:
  99. if 'notes' in code:
  100. yield from code['notes'](*gen_arg_tuple(cfg, code['notes'], text['notes'])).splitlines()
  101. else:
  102. yield from text['notes'].splitlines()
  103. text = getattr(opts, self.data_desc)['text']
  104. code = getattr(opts, self.data_desc).get('code', {})
  105. nl = '\n '
  106. return nl.join(gen_output()) + '\n'
  107. class CmdHelp_v1(Help):
  108. help_type = 'options'
  109. data_desc = 'opts_data'
  110. def gen_text(self, opts):
  111. from ..opts import cmd_opts_v1_pat
  112. skipping = False
  113. for line in opts.opts_data['text']['options'].strip().splitlines():
  114. if m := cmd_opts_v1_pat.match(line):
  115. yield '{} --{} {}'.format(
  116. (f'-{m[1]},', ' ')[m[1] == '-'],
  117. m[2],
  118. m[4])
  119. elif not skipping:
  120. yield line
  121. class CmdHelp_v2(CmdHelp_v1):
  122. def gen_text(self, opts):
  123. from ..opts import cmd_opts_v2_help_pat
  124. skipping = False
  125. coin_codes = opts.global_filter_codes.coin
  126. cmd_codes = opts.opts_data['filter_codes']
  127. for line in opts.opts_data['text']['options'][1:].rstrip().splitlines():
  128. m = cmd_opts_v2_help_pat.match(line)
  129. if m[1] == '+':
  130. if not skipping:
  131. yield line[6:]
  132. elif (coin_codes is None or m[1] in coin_codes) and m[2] in cmd_codes:
  133. yield '{} --{} {}'.format(
  134. (f'-{m[3]},', ' ')[m[3] == '-'],
  135. m[4],
  136. m[6]
  137. ) if m[4] else m[6]
  138. skipping = False
  139. else:
  140. skipping = True
  141. class GlobalHelp(Help):
  142. help_type = 'global_options'
  143. data_desc = 'global_opts_data'
  144. def gen_text(self, opts):
  145. from ..opts import global_opts_help_pat
  146. skipping = False
  147. coin_codes = opts.global_filter_codes.coin
  148. cmd_codes = opts.global_filter_codes.cmd
  149. for line in opts.global_opts_data['text']['options'][1:].rstrip().splitlines():
  150. m = global_opts_help_pat.match(line)
  151. if m[1] == '+':
  152. if not skipping:
  153. yield line[4:]
  154. elif (coin_codes is None or m[1] in coin_codes) and (cmd_codes is None or m[2] in cmd_codes):
  155. yield ' --{} {}'.format(m[3], m[5]) if m[3] else m[5]
  156. skipping = False
  157. else:
  158. skipping = True
  159. def print_help(cfg, opts):
  160. if cfg.help:
  161. help_cls = CmdHelp_v2 if 'filter_codes' in opts.opts_data else CmdHelp_v1
  162. else:
  163. help_cls = GlobalHelp
  164. from ..ui import do_pager
  165. do_pager(help_cls().make(cfg, opts))
  166. sys.exit(0)