Tools for GNS3 GUI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

0005-Tools-Option-for-confirmation-of-closing-terminal-wi.patch 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. From 88e52ddd2c4bde97c86e3345d7e379195ce3dcce Mon Sep 17 00:00:00 2001
  2. From: Bernhard Ehlers <be@bernhard-ehlers.de>
  3. Date: Wed, 24 Jan 2018 10:29:47 +0100
  4. Subject: [PATCH 5/7] Tools - Option for confirmation of closing terminal window
  5. ---
  6. gns3/tool.py | 78 +++++++++++++++++++++++++++++++++++++++---------------------
  7. tools.md | 15 +++++++-----
  8. 2 files changed, 60 insertions(+), 33 deletions(-)
  9. diff --git a/gns3/tool.py b/gns3/tool.py
  10. index fca4e8c..f567213 100644
  11. --- a/gns3/tool.py
  12. +++ b/gns3/tool.py
  13. @@ -37,15 +37,18 @@ log = logging.getLogger(__name__)
  14. # sh_quote inspired by compat_shlex_quote in youtube-dl:youtube_dl/compat.py
  15. if sys.platform.startswith("win"):
  16. - def sh_quote(arg):
  17. - """ quote argument for safe use in windows CMD """
  18. + def arg_quote(arg):
  19. + """ quote argument for safe use in windows """
  20. if not re.match(r'^[-._\w]+$', arg):
  21. arg = '"' + arg.replace('"', '\\"') + '"'
  22. - arg = re.sub(r'([()%!^"<>&|])', r'^\1', arg)
  23. return arg
  24. +
  25. + def cmd_quote(arg):
  26. + """ quote argument for safe use in CMD """
  27. + return re.sub(r'([()%!^"<>&|])', r'^\1', arg)
  28. else:
  29. - def sh_quote(arg):
  30. - """ quote argument for safe use in sh """
  31. + def arg_quote(arg):
  32. + """ quote argument for safe use in unix """
  33. if not re.match(r'^[-_\w./]+$', arg):
  34. arg = "'" + arg.replace("'", "'\"'\"'") + "'"
  35. return arg
  36. @@ -58,6 +61,13 @@ class ContextState(Enum):
  37. node = 2
  38. +class ConfirmCloseState(Enum):
  39. + """ enum for confirm_close option """
  40. + disable = 0
  41. + enable = 1
  42. + on_error = 2
  43. +
  44. +
  45. class Tool:
  46. """ tool class """
  47. @@ -66,20 +76,26 @@ class Tool:
  48. def __init__(self, name, path, options):
  49. self._name = name
  50. self._path = path
  51. - self.menu = options.get('menu', True)
  52. - context = options.get('context', ContextState.enable)
  53. + self.confirm_close = self._enum_opt(options, 'confirm_close',
  54. + ConfirmCloseState,
  55. + ConfirmCloseState.enable)
  56. + self.context = self._enum_opt(options, 'context',
  57. + ContextState, ContextState.enable)
  58. + self.menu = bool(options.get('menu', True))
  59. + self.terminal = bool(options.get('terminal', False))
  60. +
  61. + def _enum_opt(self, options, opt_name, enum_class, default):
  62. + """ get enum option """
  63. + value = options.get(opt_name, default)
  64. try:
  65. - if isinstance(context, ContextState):
  66. - pass
  67. - elif isinstance(context, int):
  68. - context = ContextState(context)
  69. - else:
  70. - context = ContextState[context]
  71. + return enum_class(value)
  72. except (KeyError, TypeError, ValueError):
  73. - log.warning("Tool %s: unknown context option '%s'", name, context)
  74. - context = ContextState.enable
  75. - self.context = context
  76. - self.terminal = options.get('terminal', False)
  77. + try:
  78. + return enum_class[value]
  79. + except (KeyError, TypeError, ValueError):
  80. + log.warning("Tool %s: unknown %s option '%s'",
  81. + self.name(), opt_name, value)
  82. + return default
  83. def name(self):
  84. """ tool name """
  85. @@ -88,11 +104,15 @@ class Tool:
  86. def run_in_terminal(self, argv):
  87. """ run command in terminal window """
  88. + cmd = " ".join(arg_quote(arg) for arg in argv)
  89. if sys.platform.startswith("win"):
  90. cmd_exe = os.environ.get('ComSpec', "cmd.exe")
  91. - cmd_string = " ".join(sh_quote(arg) for arg in argv)
  92. - subprocess.Popen("{} /c {} & pause".format(cmd_exe, cmd_string),
  93. - env=os.environ)
  94. + cmd = cmd_quote(cmd)
  95. + if self.confirm_close == ConfirmCloseState.enable:
  96. + cmd += " & pause"
  97. + elif self.confirm_close == ConfirmCloseState.on_error:
  98. + cmd += " || pause"
  99. + subprocess.Popen(cmd_exe + " /c " + cmd, env=os.environ)
  100. elif sys.platform.startswith("darwin"):
  101. osascript = r"""
  102. on run (args)
  103. @@ -115,10 +135,12 @@ on run (args)
  104. return
  105. end run
  106. """
  107. - cmd = "PATH=" + sh_quote(os.environ['PATH']) + "; " + \
  108. - "cd " + sh_quote(os.getcwd()) + "; " + \
  109. - " ".join(sh_quote(arg) for arg in argv) + \
  110. - "; echo; read -p Close... var"
  111. + cmd = "PATH=" + arg_quote(os.environ['PATH']) + "; " + \
  112. + "cd " + arg_quote(os.getcwd()) + "; " + cmd
  113. + if self.confirm_close == ConfirmCloseState.enable:
  114. + cmd += "; read -p '\nClose...' var"
  115. + elif self.confirm_close == ConfirmCloseState.on_error:
  116. + cmd += " || read -p '\nClose...' var"
  117. prog = os.path.splitext(os.path.basename(argv[0]))[0]
  118. subprocess.Popen(["osascript", "-e", osascript, "--", prog, cmd],
  119. env=os.environ)
  120. @@ -132,9 +154,11 @@ end run
  121. else:
  122. log.error("Tool %s: No terminal emulator found", self._name)
  123. return
  124. - cmd = " ".join(sh_quote(arg) for arg in argv) + \
  125. - "; echo; read -p Close... var"
  126. - cmd = "sh -c " + sh_quote(cmd)
  127. + if self.confirm_close == ConfirmCloseState.enable:
  128. + cmd += "; read -p '\nClose...' var"
  129. + elif self.confirm_close == ConfirmCloseState.on_error:
  130. + cmd += " || read -p '\nClose...' var"
  131. + cmd = "sh -c " + arg_quote(cmd)
  132. subprocess.Popen([Tool._terminal_app, "-e", cmd],
  133. env=os.environ)
  134. diff --git a/tools.md b/tools.md
  135. index dd1f2ce..f60652a 100644
  136. --- a/tools.md
  137. +++ b/tools.md
  138. @@ -24,15 +24,18 @@ same base name as the tool, but with the .json extension.
  139. It can set the following options:
  140. -| Option | Meaning | Allowed Values | Default |
  141. -|----------|----------------------|-------------------------------|-----------|
  142. -| name | name of tool | any string | base name |
  143. -| menu | show in main menu | false / true | true |
  144. -| context | show in context menu | "disable" / "enable" / "node" | "enable" |
  145. -| terminal | run in terminal | false / true | false |
  146. +| Option | Meaning | Allowed Values | Default |
  147. +|---------------|--------------------------|-----------------------------------|-----------|
  148. +| name | name of tool | any string | base name |
  149. +| menu | show in main menu | false / true | true |
  150. +| context | show in context menu | "disable" / "enable" / "node" | "enable" |
  151. +| terminal | run in terminal window | false / true | false |
  152. +| confirm_close | confirm closing terminal | "disable" / "enable" / "on_error" | "enable" |
  153. With the context option "node" a tool is only shown in
  154. the context menu, when at least one node is selected.
  155. +Instead of "disable" or "enable" the boolean values
  156. +false / true can be used.
  157. Example of a .json file:
  158. --
  159. 2.10.1 (Apple Git-78)