Rizin
unix-like reverse engineering framework and cli tools
cmd_descs_generate.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 # SPDX-FileCopyrightText: 2020-2021 ret2libc <sirmy15@gmail.com>
3 # SPDX-License-Identifier: LGPL-3.0-only
4 
5 import argparse
6 import os
7 import sys
8 
9 import yaml
10 from cmd_descs_util import (
11  CD_ARG_LAST_TYPES,
12  CD_TYPE_ARGV,
13  CD_TYPE_ARGV_MODES,
14  CD_TYPE_ARGV_STATE,
15  CD_TYPE_FAKE,
16  CD_TYPE_GROUP,
17  CD_TYPE_INNER,
18  CD_TYPE_OLDINPUT,
19  CD_VALID_TYPES,
20  compute_cname,
21  get_handler_cname,
22 )
23 
24 CMDDESCS_C_TEMPLATE = """// SPDX-FileCopyrightText: 2021 RizinOrg <info@rizin.re>
25 // SPDX-License-Identifier: LGPL-3.0-only
26 //
27 // WARNING: This file was auto-generated by cmd_descs_generate.py script. Do not
28 // modify it manually. Look at cmd_descs.yaml if you want to update commands.
29 //
30 
31 #include <cmd_descs.h>
32 
33 {helps_declarations}
34 
35 {helps}
36 RZ_IPI void rzshell_cmddescs_init(RzCore *core) {{
37 \tRzCmdDesc *root_cd = rz_cmd_get_root(core->rcmd);
38 \trz_cmd_batch_start(core->rcmd);
39 {init_code}
40 \trz_cmd_batch_end(core->rcmd);
41 }}
42 """
43 
44 CMDDESCS_H_TEMPLATE = """// SPDX-FileCopyrightText: 2021 RizinOrg <info@rizin.re>
45 // SPDX-License-Identifier: LGPL-3.0-only
46 //
47 // WARNING: This file was auto-generated by cmd_descs_generate.py script. Do not
48 // modify it manually. Look at cmd_descs.yaml if you want to update commands.
49 //
50 
51 #include <rz_cmd.h>
52 #include <rz_core.h>
53 #include <rz_util.h>
54 
55 // Command handlers, manually defined somewhere else
56 {handlers_declarations}
57 
58 // Main function that initialize the entire commands tree
59 RZ_IPI void rzshell_cmddescs_init(RzCore *core);
60 """
61 
62 DESC_HELP_DETAIL_ENTRY_TEMPLATE = (
63  """\t{{ .text = {text}, .arg_str = {arg_str}, .comment = {comment} }}"""
64 )
65 DESC_HELP_DETAIL_ENTRIES_TEMPLATE = """static const RzCmdDescDetailEntry {cname}[] = {{
66 {entry}
67 }};
68 """
69 
70 DESC_HELP_DETAIL_TEMPLATE = """\t{{ .name = {name}, .entries = {entries} }}"""
71 DESC_HELP_DETAILS_TEMPLATE = """static const RzCmdDescDetail {cname}[] = {{
72 {details}
73 }};
74 """
75 DECL_DESC_HELP_DETAILS_TEMPLATE = "static const RzCmdDescDetail {cname}[{size}];"
76 
77 DESC_HELP_ARG_CHOICES = "static const char *{cname}[] = {{ {choices} }};\n"
78 DESC_HELP_ARG_UNION_CHOICES = "\t\t.choices = {choices},\n"
79 DESC_HELP_ARG_UNION_CHOICES_CB = "\t\t.choices_cb = {choices_cb},\n"
80 DESC_HELP_ARG_TEMPLATE_FLAGS = "\t\t.flags = {flags},\n"
81 DESC_HELP_ARG_TEMPLATE_OPTIONAL = "\t\t.optional = {optional},\n"
82 DESC_HELP_ARG_TEMPLATE_NO_SPACE = "\t\t.no_space = {no_space},\n"
83 DESC_HELP_ARG_TEMPLATE_DEFAULT_VALUE = "\t\t.default_value = {default_value},\n"
84 DESC_HELP_ARG_TEMPLATE = """\t{{
85 \t\t.name = {name},
86 \t\t.type = {type},
87 {flags}{optional}{no_space}{default_value}{union}
88 \t}}"""
89 DESC_HELP_ARGS_TEMPLATE = """static const RzCmdDescArg {cname}[] = {{
90 {args}
91 }};
92 """
93 DECL_DESC_HELP_ARGS_TEMPLATE = "static const RzCmdDescArg {cname}[{size}];"
94 
95 DESC_HELP_TEMPLATE_DESCRIPTION = "\t.description = {description},\n"
96 DESC_HELP_TEMPLATE_ARGS_STR = "\t.args_str = {args_str},\n"
97 DESC_HELP_TEMPLATE_USAGE = "\t.usage = {usage},\n"
98 DESC_HELP_TEMPLATE_SORT_SUBCOMMANDS = "\t.sort_subcommands = {sort_subcommands},\n"
99 DESC_HELP_TEMPLATE_OPTIONS = "\t.options = {options},\n"
100 DESC_HELP_TEMPLATE_DETAILS = "\t.details = {details},\n"
101 DESC_HELP_TEMPLATE_DETAILS_CB = "\t.details_cb = {details_cb},\n"
102 DESC_HELP_TEMPLATE_ARGS = "\t.args = {args},\n"
103 DESC_HELP_TEMPLATE = """static const RzCmdDescHelp {cname} = {{
104 \t.summary = {summary},
105 {description}{args_str}{usage}{options}{details}{details_cb}{args}{sort_subcommands}}};
106 """
107 
108 DEFINE_OLDINPUT_TEMPLATE = """
109 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_oldinput_new(core->rcmd, {parent_cname}_cd, {name}, {handler_cname}, &{help_cname});
110 \trz_warn_if_fail({cname}_cd);"""
111 DEFINE_ARGV_TEMPLATE = """
112 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_argv_new(core->rcmd, {parent_cname}_cd, {name}, {handler_cname}, &{help_cname});
113 \trz_warn_if_fail({cname}_cd);"""
114 DEFINE_ARGV_MODES_TEMPLATE = """
115 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_argv_modes_new(core->rcmd, {parent_cname}_cd, {name}, {modes}, {handler_cname}, &{help_cname});
116 \trz_warn_if_fail({cname}_cd);"""
117 DEFINE_ARGV_STATE_TEMPLATE = """
118 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_argv_state_new(core->rcmd, {parent_cname}_cd, {name}, {modes}, {handler_cname}, &{help_cname});
119 \trz_warn_if_fail({cname}_cd);"""
120 DEFINE_GROUP_TEMPLATE = """
121 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_group_new(core->rcmd, {parent_cname}_cd, {name}, {handler_cname}, {help_cname_ref}, &{group_help_cname});
122 \trz_warn_if_fail({cname}_cd);"""
123 DEFINE_GROUP_MODES_TEMPLATE = """
124 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_group_modes_new(core->rcmd, {parent_cname}_cd, {name}, {modes}, {handler_cname}, {help_cname_ref}, &{group_help_cname});
125 \trz_warn_if_fail({cname}_cd);"""
126 DEFINE_GROUP_STATE_TEMPLATE = """
127 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_group_state_new(core->rcmd, {parent_cname}_cd, {name}, {modes}, {handler_cname}, {help_cname_ref}, &{group_help_cname});
128 \trz_warn_if_fail({cname}_cd);"""
129 DEFINE_INNER_TEMPLATE = """
130 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_inner_new(core->rcmd, {parent_cname}_cd, {name}, &{help_cname});
131 \trz_warn_if_fail({cname}_cd);"""
132 DEFINE_FAKE_TEMPLATE = """
133 \tRzCmdDesc *{cname}_cd = rz_cmd_desc_fake_new(core->rcmd, {parent_cname}_cd, {name}, &{help_cname});
134 \trz_warn_if_fail({cname}_cd);"""
135 
136 SET_DEFAULT_MODE_TEMPLATE = """
137 \trz_cmd_desc_set_default_mode({cname}_cd, {default_mode});"""
138 
139 
140 def _escape(s):
141  return s.replace("\\", "\\\\").replace('"', '\\"')
142 
143 
144 def strornull(s):
145  return '"' + _escape(s) + '"' if s is not None else "NULL"
146 
147 
148 def strip(s):
149  return s.strip("\n") if s is not None else None
150 
151 
152 class Arg:
153  def __init__(self, cd, c):
154  if "name" not in c or "type" not in c:
155  print("Argument of %s should have `name`/`type` fields" % (cd.name,))
156  sys.exit(1)
157 
158  self.cdcd = cd
159  # RzCmdDescArg fields
160  self.namename = c.pop("name")
161  self.flagsflags = c.pop("flags", None)
162  self.optionaloptional = c.pop("optional", None)
163  self.no_spaceno_space = c.pop("no_space", None)
164  self.typetype = c.pop("type")
165  self.default_valuedefault_value = (
166  str(c.pop("default_value")) if "default_value" in c else None
167  )
168  self.choiceschoices = c.pop("choices", None)
169  self.choices_cbchoices_cb = c.pop("choices_cb", None)
170  if c.keys():
171  print(
172  "Argument %s for command %s has unrecognized properties: %s."
173  % (self.namename, self.cdcd.name, c.keys())
174  )
175  sys.exit(1)
176 
177  if self.default_valuedefault_value is not None and self.optionaloptional is not None:
178  print(
179  "Argument %s for command %s has both optional and default_value."
180  % (self.namename, self.cdcd.name)
181  )
182  sys.exit(1)
183 
185  if self.typetype == "RZ_CMD_ARG_TYPE_CHOICES":
186  return self.cdcd.cname + "_" + compute_cname(self.namename) + "_choices"
187 
188  raise Exception("_get_choices_cname should be called on ARG_TYPE_CHOICES only")
189 
190  def _get_union(self):
191  if self.typetype == "RZ_CMD_ARG_TYPE_CHOICES":
192  if self.choices_cbchoices_cb is None:
193  return DESC_HELP_ARG_UNION_CHOICES.format(
194  choices=self._get_choices_cname_get_choices_cname()
195  )
196  return DESC_HELP_ARG_UNION_CHOICES_CB.format(choices_cb=self.choices_cbchoices_cb)
197 
198  return ""
199 
200  def __str__(self):
201  flags = (
202  DESC_HELP_ARG_TEMPLATE_FLAGS.format(flags=self.flagsflags)
203  if self.flagsflags is not None and self.flagsflags != ""
204  else ""
205  )
206  optional = (
207  DESC_HELP_ARG_TEMPLATE_OPTIONAL.format(
208  optional="true" if self.optionaloptional else "false"
209  )
210  if self.optionaloptional is not None
211  else ""
212  )
213  no_space = (
214  DESC_HELP_ARG_TEMPLATE_NO_SPACE.format(
215  no_space="true" if self.no_spaceno_space else "false"
216  )
217  if self.no_spaceno_space is not None
218  else ""
219  )
220  default_value = (
221  DESC_HELP_ARG_TEMPLATE_DEFAULT_VALUE.format(
222  default_value=strornull(self.default_valuedefault_value)
223  )
224  if self.default_valuedefault_value is not None
225  else ""
226  )
227  return DESC_HELP_ARG_TEMPLATE.format(
228  name=strornull(self.namename),
229  type=self.typetype,
230  flags=flags,
231  optional=optional,
232  no_space=no_space,
233  default_value=default_value,
234  union=self._get_union_get_union(),
235  )
236 
237  def get_cstructure(self):
238  if self.typetype == "RZ_CMD_ARG_TYPE_CHOICES" and self.choices_cbchoices_cb is None:
239  return DESC_HELP_ARG_CHOICES.format(
240  cname=self._get_choices_cname_get_choices_cname(),
241  choices=", ".join(
242  [
243  '"%s"' % (x,) if x != "NULL" else x
244  for x in self.choiceschoices + ["NULL"]
245  ]
246  ),
247  )
248  return ""
249 
250  def decl(self):
251  if self.typetype == "RZ_CMD_ARG_TYPE_CHOICES" and self.choices_cbchoices_cb is not None:
252  return "RZ_IPI char **%s(RzCore *core);" % (self.choices_cbchoices_cb,)
253  return None
254 
255 
257  if "text" not in c or "comment" not in c:
258  print("No `text`/`comment` fields for DetailEntry %s" % (c,))
259  sys.exit(1)
260 
261  text = strip(c["text"])
262  comment = strip(c["comment"])
263  arg_str = strip(c.get("arg_str"))
264 
265  return DESC_HELP_DETAIL_ENTRY_TEMPLATE.format(
266  text=strornull(text),
267  arg_str=strornull(arg_str),
268  comment=strornull(comment),
269  )
270 
271 
272 class Detail:
273  def __init__(self, cd, c):
274  if "name" not in c or "entries" not in c:
275  print("No `name`/`entries` fields for Detail %s" % (c,))
276  sys.exit(1)
277 
278  self.cdcd = cd
279  # RzCmdDescDetail fields
280  self.namename = strip(c.pop("name"))
281  self.entriesentries = [format_detail_entry(x) for x in c.pop("entries")]
282  if c.keys():
283  print(
284  "Detail %s for command %s has unrecognized properties: %s."
285  % (self.namename, self.cdcd.name, c.keys())
286  )
287  sys.exit(1)
288 
290  return self.cdcd.cname + "_" + compute_cname(self.namename) + "_detail_entries"
291 
292  def __str__(self):
293  return DESC_HELP_DETAIL_TEMPLATE.format(
294  name=strornull(self.namename),
295  entries=self.get_detail_entries_cnameget_detail_entries_cname(),
296  )
297 
298  def get_cstructure(self):
299  return DESC_HELP_DETAIL_ENTRIES_TEMPLATE.format(
300  cname=self.get_detail_entries_cnameget_detail_entries_cname(),
301  entry=",\n".join([str(e) for e in self.entriesentries] + ["\t{ 0 },"]),
302  )
303 
304 
305 class CmdDesc:
306  c_cds = {}
307  c_handlers = {}
308  c_args = {}
309  c_details = {}
310 
311  def _process_details(self, c):
312  if "details" in c and isinstance(c["details"], list):
313  self.detailsdetails = [Detail(self, x) for x in c.pop("details", [])]
314  elif "details" in c and isinstance(c["details"], str):
315  self.details_aliasdetails_alias = c.pop("details")
316  if "details_cb" in c and isinstance(c["details_cb"], str):
317  self.details_cbdetails_cb = c.pop("details_cb")
318 
319  def _process_args(self, c):
320  if "args" in c and isinstance(c["args"], list):
321  self.argsargs = [Arg(self, x) for x in c.pop("args", [])]
322  if (
323  self.argsargs
324  and self.argsargs[-1].type in CD_ARG_LAST_TYPES
325  and self.argsargs[-1].flags is None
326  ):
327  self.argsargs[-1].flags = "RZ_CMD_ARG_FLAG_LAST"
328  elif "args" in c and isinstance(c["args"], str):
329  self.args_aliasargs_alias = c.pop("args")
330 
331  def _set_type(self, c):
332  if "type" in c:
333  self.typetype = c.pop("type")
334  elif c.get("subcommands"):
335  self.typetype = CD_TYPE_GROUP
336  elif self.modesmodes:
337  self.typetype = CD_TYPE_ARGV_MODES
338  else:
339  self.typetype = CD_TYPE_ARGV
340 
341  def _set_subcommands(self, c, yamls):
342  if "subcommands" in c and isinstance(c["subcommands"], list):
343  # The list of subcommands is embedded in the current file
344  self.subcommandssubcommands = [
345  CmdDesc(yamls, x, self, i)
346  for i, x in enumerate(c.pop("subcommands", []))
347  ]
348  elif "subcommands" in c and isinstance(c["subcommands"], str):
349  # The list of subcommands is in another file
350  subcommands_name = c.pop("subcommands")
351  if subcommands_name not in yamls:
352  print(
353  "Command %s referenced another YAML file (%s) that is not passed as arg to cmd_descs_generate.py."
354  % (self.namename, subcommands_name)
355  )
356  sys.exit(1)
357 
358  external_c = yamls[subcommands_name]
359  self.subcommandssubcommands = [
360  CmdDesc(yamls, x, self, i) for i, x in enumerate(external_c)
361  ]
362 
363  # handle the exec_cd, which is a cd that has the same name as its parent
364  if (
365  self.subcommandssubcommands
366  and self.subcommandssubcommands[0].name == self.namename
367  and self.subcommandssubcommands[0].type not in [CD_TYPE_INNER, CD_TYPE_FAKE]
368  ):
369  self.exec_cdexec_cd = self.subcommandssubcommands[0]
370 
371  def __init__(self, yamls, c, parent=None, pos=0):
372  self.pospos = pos
373 
374  if not c:
375  # used only for root node
376  self.namename = ""
377  self.cnamecname = "root"
378  self.summarysummary = ""
379  self.typetype = CD_TYPE_GROUP
380  return
381 
382  if not c.get("name") or not c.get("summary"):
383  print("No `name`/`summary` fields in", c)
384  sys.exit(1)
385 
386  # RzCmdDesc fields
387  self.namename = c.pop("name")
388  self.cnamecname = c.pop("cname", None) or compute_cname(self.namename)
389  self.typetype = None
390  self.parentparent = parent
391  self.subcommandssubcommands = None
392  self.exec_cdexec_cd = None
393  self.modesmodes = c.pop("modes", None)
394  self.handlerhandler = c.pop("handler", None)
395  self.default_modedefault_mode = c.pop("default_mode", None)
396  # RzCmdDescHelp fields
397  self.summarysummary = strip(c.pop("summary"))
398  self.descriptiondescription = strip(c.pop("description", None))
399  self.args_strargs_str = strip(c.pop("args_str", None))
400  self.usageusage = strip(c.pop("usage", None))
401  self.optionsoptions = strip(c.pop("options", None))
402  self.sort_subcommandssort_subcommands = c.pop("sort_subcommands", None)
403 
404  self.detailsdetails = None
405  self.details_aliasdetails_alias = None
406  self.details_cbdetails_cb = None
407  self._process_details_process_details(c)
408 
409  self.argsargs = None
410  self.args_aliasargs_alias = None
411  self._process_args_process_args(c)
412 
413  # determine type before parsing subcommands, so children can check type of parent
414  self._set_type_set_type(c)
415 
416  self._set_subcommands_set_subcommands(c, yamls)
417 
418  self._validate_validate(c)
419  CmdDesc.c_cds[self.cnamecname] = self
420  if self.get_handler_cnameget_handler_cname():
421  CmdDesc.c_handlers[self.get_handler_cnameget_handler_cname()] = self
422  if self.argsargs:
423  CmdDesc.c_args[CmdDesc.get_arg_cname(self)] = self
424  if self.detailsdetails:
425  CmdDesc.c_details[CmdDesc.get_detail_cname(self)] = self
426 
427  def _validate(self, c):
428  if c.keys():
429  print("Command %s has unrecognized properties: %s." % (self.namename, c.keys()))
430  sys.exit(1)
431 
432  if self.typetype not in CD_VALID_TYPES:
433  print("Command %s does not have a valid type." % (self.namename,))
434  sys.exit(1)
435 
436  if (
437  self.typetype
438  in [CD_TYPE_ARGV, CD_TYPE_ARGV_MODES, CD_TYPE_ARGV_STATE, CD_TYPE_OLDINPUT]
439  and not self.cnamecname
440  ):
441  print("Command %s does not have cname field" % (self.namename,))
442  sys.exit(1)
443 
444  if (
445  self.parentparent
446  and self.parentparent.name == self.namename
447  and self.pospos != 0
448  and self.typetype not in [CD_TYPE_INNER, CD_TYPE_FAKE]
449  ):
450  print(
451  "If a command has the same name as its parent, it can only be the first child. See parent of Command %s"
452  % (self.cnamecname,)
453  )
454  sys.exit(1)
455 
456  if self.parentparent and self.parentparent.type not in [
457  CD_TYPE_GROUP,
458  CD_TYPE_INNER,
459  CD_TYPE_OLDINPUT,
460  ]:
461  print("The parent of %s is of the wrong type" % (self.cnamecname,))
462  sys.exit(1)
463 
464  if self.cnamecname in CmdDesc.c_cds:
465  print("Another command already has the same cname as %s" % (self.cnamecname,))
466  sys.exit(1)
467 
468  if (
469  self.typetype in [CD_TYPE_ARGV, CD_TYPE_ARGV_MODES, CD_TYPE_ARGV_STATE]
470  and self.argsargs is None
471  and self.args_aliasargs_alias is None
472  ):
473  print("Specify arguments for command %s" % (self.namename,))
474  sys.exit(1)
475 
476  def get_handler_cname(self):
477  if self.typetype not in [
478  CD_TYPE_OLDINPUT,
479  CD_TYPE_ARGV,
480  CD_TYPE_ARGV_MODES,
481  CD_TYPE_ARGV_STATE,
482  ]:
483  return None
484  return get_handler_cname(self.typetype, self.handlerhandler, self.cnamecname)
485 
486  @classmethod
487  def get_arg_cname(cls, cd):
488  return cd.cname + "_args"
489 
490  @classmethod
491  def get_detail_cname(cls, cd):
492  return cd.cname + "_details"
493 
494  def get_help_cname(self):
495  return self.cnamecname + "_help"
496 
497  def __str__(self):
498  out = ""
499  details_cname = None
500  args_cname = None
501 
502  if self.detailsdetails is not None:
503  out += "\n".join([d.get_cstructure() for d in self.detailsdetails])
504  out += DESC_HELP_DETAILS_TEMPLATE.format(
505  cname=CmdDesc.get_detail_cname(self),
506  details=",\n".join([str(d) for d in self.detailsdetails] + ["\t{ 0 },"]),
507  )
508  details_cname = CmdDesc.get_detail_cname(self)
509  elif self.details_aliasdetails_alias is not None:
510  details_cname = self.details_aliasdetails_alias + "_details"
511 
512  if self.argsargs is not None:
513  out += "\n".join(
514  [a.get_cstructure() for a in self.argsargs if a.get_cstructure() != ""]
515  )
516  out += DESC_HELP_ARGS_TEMPLATE.format(
517  cname=CmdDesc.get_arg_cname(self),
518  args=",\n".join([str(a) for a in self.argsargs] + ["\t{ 0 },"]),
519  )
520  args_cname = CmdDesc.get_arg_cname(self)
521  elif self.args_aliasargs_alias is not None:
522  args_cname = self.args_aliasargs_alias + "_args"
523 
524  description = (
525  DESC_HELP_TEMPLATE_DESCRIPTION.format(
526  description=strornull(self.descriptiondescription)
527  )
528  if self.descriptiondescription is not None
529  else ""
530  )
531  args_str = (
532  DESC_HELP_TEMPLATE_ARGS_STR.format(args_str=strornull(self.args_strargs_str))
533  if self.args_strargs_str is not None
534  else ""
535  )
536  usage = (
537  DESC_HELP_TEMPLATE_USAGE.format(usage=strornull(self.usageusage))
538  if self.usageusage is not None
539  else ""
540  )
541  sort_subcommands = (
542  DESC_HELP_TEMPLATE_SORT_SUBCOMMANDS.format(
543  sort_subcommands="true" if self.sort_subcommandssort_subcommands else "false"
544  )
545  if self.sort_subcommandssort_subcommands is not None
546  else ""
547  )
548  options = (
549  DESC_HELP_TEMPLATE_OPTIONS.format(options=strornull(self.optionsoptions))
550  if self.optionsoptions is not None
551  else ""
552  )
553  details = (
554  DESC_HELP_TEMPLATE_DETAILS.format(details=details_cname)
555  if details_cname is not None
556  else ""
557  )
558  details_cb = (
559  DESC_HELP_TEMPLATE_DETAILS_CB.format(details_cb=self.details_cbdetails_cb)
560  if self.details_cbdetails_cb is not None
561  else ""
562  )
563  arguments = (
564  DESC_HELP_TEMPLATE_ARGS.format(args=args_cname)
565  if args_cname is not None
566  else ""
567  )
568  out += DESC_HELP_TEMPLATE.format(
569  cname=self.get_help_cnameget_help_cname(),
570  summary=strornull(self.summarysummary),
571  description=description,
572  args_str=args_str,
573  usage=usage,
574  options=options,
575  details=details,
576  details_cb=details_cb,
577  args=arguments,
578  sort_subcommands=sort_subcommands,
579  )
580 
581  if self.subcommandssubcommands:
582  out += "\n".join([str(child) for child in self.subcommandssubcommands])
583  return out
584 
585  def str_tab(self, tab=0):
586  spaces = " " * tab
587  out = ""
588  out += spaces + "Name: %s\n" % (self.namename,)
589  out += spaces + "CName: %s\n" % (self.cnamecname,)
590  out += spaces + "Summary: %s\n" % (self.summarysummary,)
591  if self.descriptiondescription:
592  out += spaces + "Description: %s\n" % (self.descriptiondescription,)
593  if self.subcommandssubcommands:
594  out += spaces + "Subcommands:\n"
595  for c in self.subcommandssubcommands:
596  out += c.str_tab(tab + 4)
597  out += "\n"
598 
599  return out
600 
601  def __repr__(self):
602  return self.str_tabstr_tab()
603 
604 
606  if cd.exec_cd and cd.exec_cd.type == CD_TYPE_ARGV_MODES:
607  formatted_string = DEFINE_GROUP_MODES_TEMPLATE.format(
608  cname=cd.cname,
609  parent_cname=cd.parent.cname,
610  name=strornull(cd.name),
611  modes=" | ".join(cd.exec_cd.modes),
612  handler_cname=cd.exec_cd.get_handler_cname(),
613  help_cname_ref="&" + cd.exec_cd.get_help_cname(),
614  group_help_cname=cd.get_help_cname(),
615  )
616  if cd.exec_cd.default_mode is not None:
617  formatted_string += SET_DEFAULT_MODE_TEMPLATE.format(
618  cname=cd.cname,
619  default_mode=cd.exec_cd.default_mode,
620  )
621  formatted_string += "\n".join(
622  [createcd(child) for child in cd.subcommands[1:] or []]
623  )
624  elif cd.exec_cd and cd.exec_cd.type == CD_TYPE_ARGV_STATE:
625  formatted_string = DEFINE_GROUP_STATE_TEMPLATE.format(
626  cname=cd.cname,
627  parent_cname=cd.parent.cname,
628  name=strornull(cd.name),
629  modes=" | ".join(cd.exec_cd.modes),
630  handler_cname=cd.exec_cd.get_handler_cname(),
631  help_cname_ref="&" + cd.exec_cd.get_help_cname(),
632  group_help_cname=cd.get_help_cname(),
633  )
634  if cd.exec_cd.default_mode is not None:
635  formatted_string += SET_DEFAULT_MODE_TEMPLATE.format(
636  cname=cd.cname,
637  default_mode=cd.exec_cd.default_mode,
638  )
639  formatted_string += "\n".join(
640  [createcd(child) for child in cd.subcommands[1:] or []]
641  )
642  else:
643  formatted_string = DEFINE_GROUP_TEMPLATE.format(
644  cname=cd.cname,
645  parent_cname=cd.parent.cname,
646  name=strornull(cd.name),
647  handler_cname=(cd.exec_cd and cd.exec_cd.get_handler_cname()) or "NULL",
648  help_cname_ref=(cd.exec_cd and "&" + cd.exec_cd.get_help_cname()) or "NULL",
649  group_help_cname=cd.get_help_cname(),
650  )
651  subcommands = (
652  cd.exec_cd and cd.subcommands and cd.subcommands[1:]
653  ) or cd.subcommands
654  formatted_string += "\n".join([createcd(child) for child in subcommands or []])
655 
656  return formatted_string
657 
658 
659 def createcd(cd):
660  formatted_string = None
661 
662  if cd.type == CD_TYPE_ARGV:
663  formatted_string = DEFINE_ARGV_TEMPLATE.format(
664  cname=cd.cname,
665  parent_cname=cd.parent.cname,
666  name=strornull(cd.name),
667  handler_cname=cd.get_handler_cname(),
668  help_cname=cd.get_help_cname(),
669  )
670  elif cd.type == CD_TYPE_ARGV_MODES:
671  formatted_string = DEFINE_ARGV_MODES_TEMPLATE.format(
672  cname=cd.cname,
673  parent_cname=cd.parent.cname,
674  name=strornull(cd.name),
675  modes=" | ".join(cd.modes),
676  handler_cname=cd.get_handler_cname(),
677  help_cname=cd.get_help_cname(),
678  )
679  if cd.default_mode is not None:
680  formatted_string += SET_DEFAULT_MODE_TEMPLATE.format(
681  cname=cd.cname,
682  default_mode=cd.default_mode,
683  )
684  elif cd.type == CD_TYPE_ARGV_STATE:
685  formatted_string = DEFINE_ARGV_STATE_TEMPLATE.format(
686  cname=cd.cname,
687  parent_cname=cd.parent.cname,
688  name=strornull(cd.name),
689  modes=" | ".join(cd.modes),
690  handler_cname=cd.get_handler_cname(),
691  help_cname=cd.get_help_cname(),
692  )
693  if cd.default_mode is not None:
694  formatted_string += SET_DEFAULT_MODE_TEMPLATE.format(
695  cname=cd.cname,
696  default_mode=cd.default_mode,
697  )
698  elif cd.type == CD_TYPE_FAKE:
699  formatted_string = DEFINE_FAKE_TEMPLATE.format(
700  cname=cd.cname,
701  parent_cname=cd.parent.cname,
702  name=strornull(cd.name),
703  help_cname=cd.get_help_cname(),
704  )
705  elif cd.type == CD_TYPE_INNER:
706  formatted_string = DEFINE_INNER_TEMPLATE.format(
707  cname=cd.cname,
708  parent_cname=cd.parent.cname,
709  name=strornull(cd.name),
710  help_cname=cd.get_help_cname(),
711  )
712  formatted_string += "\n".join(
713  [createcd(child) for child in cd.subcommands or []]
714  )
715  elif cd.type == CD_TYPE_OLDINPUT:
716  formatted_string = DEFINE_OLDINPUT_TEMPLATE.format(
717  cname=cd.cname,
718  parent_cname=cd.parent.cname,
719  name=strornull(cd.name),
720  handler_cname=cd.get_handler_cname(),
721  help_cname=cd.get_help_cname(),
722  )
723  formatted_string += "\n".join(
724  [createcd(child) for child in cd.subcommands or []]
725  )
726  elif cd.type == CD_TYPE_GROUP:
727  formatted_string = createcd_typegroup(cd)
728  else:
729  raise Exception("Not handled cd type")
730 
731  return formatted_string
732 
733 
734 def arg2decl(cd):
735  return DECL_DESC_HELP_ARGS_TEMPLATE.format(
736  cname=CmdDesc.get_arg_cname(cd), size=len(cd.args) + 1
737  )
738 
739 
740 def detail2decl(cd):
741  return DECL_DESC_HELP_DETAILS_TEMPLATE.format(
742  cname=CmdDesc.get_detail_cname(cd), size=len(cd.details) + 1
743  )
744 
745 
746 def handler2decl(cd, cd_type, handler_name):
747  out = []
748  if cd_type == CD_TYPE_ARGV:
749  out.append(
750  "RZ_IPI RzCmdStatus %s(RzCore *core, int argc, const char **argv);"
751  % (handler_name,)
752  )
753  if cd_type == CD_TYPE_ARGV_MODES:
754  out.append(
755  "RZ_IPI RzCmdStatus %s(RzCore *core, int argc, const char **argv, RzOutputMode mode);"
756  % (handler_name,)
757  )
758  if cd_type == CD_TYPE_ARGV_STATE:
759  out.append(
760  "RZ_IPI RzCmdStatus %s(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);"
761  % (handler_name,)
762  )
763  if cd_type == CD_TYPE_OLDINPUT:
764  out.append("RZ_IPI int %s(void *data, const char *input);" % (handler_name,))
765 
766  if cd.details_cb is not None:
767  out.append(
768  "RZ_IPI RzCmdDescDetail *%s(RzCore *core, int argc, const char **argv);"
769  % (cd.details_cb,)
770  )
771 
772  if isinstance(cd.args, list):
773  for arg in cd.args:
774  d = arg.decl()
775  if d is not None:
776  out.append(d)
777 
778  return "\n".join(out) if out else None
779 
780 
781 parser = argparse.ArgumentParser(
782  description="Generate .c/.h files from Command Descriptors YAML file."
783 )
784 parser.add_argument(
785  "--src-output-dir", type=str, required=False, help="Source output directory"
786 )
787 parser.add_argument("--output-dir", type=str, required=True, help="Output directory")
788 parser.add_argument(
789  "yaml_files",
790  type=argparse.FileType("r"),
791  nargs="+",
792  help="Input YAML files containing commands descriptions. One should be named 'root'.",
793 )
794 
795 args = parser.parse_args()
796 
797 commands_yml_arr = [yaml.safe_load(f) for f in args.yaml_files]
798 commands_yml = {c["name"]: c["commands"] for c in commands_yml_arr}
799 
800 root_cd = CmdDesc(commands_yml, None)
801 root_cds = [CmdDesc(commands_yml, c, root_cd) for c in commands_yml["root"]]
802 
803 arg_decls = [arg2decl(cd) for cd in CmdDesc.c_args.values()]
804 detail_decls = [detail2decl(cd) for cd in CmdDesc.c_details.values()]
805 helps = [str(cd) for cd in root_cds]
806 init_code = [createcd(cd) for cd in root_cds]
807 
808 cf_text = CMDDESCS_C_TEMPLATE.format(
809  helps_declarations="\n".join(detail_decls + arg_decls),
810  helps="\n".join(helps),
811  init_code="\n".join(init_code),
812 )
813 with open(os.path.join(args.output_dir, "cmd_descs.c"), "w", encoding="utf8") as f:
814  f.write(cf_text)
815 if args.src_output_dir:
816  with open(
817  os.path.join(args.src_output_dir, "cmd_descs.c"), "w", encoding="utf8"
818  ) as f:
819  f.write(cf_text)
820 
821 handlers_decls = filter(
822  lambda th: th[2] is not None,
823  [(cd, cd.type, cd.get_handler_cname()) for cd in CmdDesc.c_cds.values()],
824 )
825 
826 hf_text = CMDDESCS_H_TEMPLATE.format(
827  handlers_declarations="\n".join(
828  [handler2decl(cd, t, h) for cd, t, h in handlers_decls]
829  ),
830 )
831 with open(os.path.join(args.output_dir, "cmd_descs.h"), "w", encoding="utf8") as f:
832  f.write(hf_text)
833 if args.src_output_dir:
834  with open(
835  os.path.join(args.src_output_dir, "cmd_descs.h"), "w", encoding="utf8"
836  ) as f:
837  f.write(hf_text)
size_t len
Definition: 6502dis.c:15
def __init__(self, yamls, c, parent=None, pos=0)
def _set_subcommands(self, c, yamls)
def handler2decl(cd, cd_type, handler_name)
def compute_cname(name)
int replace(char *string, const char *token, const char *fmt,...)
Definition: tms320_dasm.c:325