Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ 18e2b6e4

History | View | Annotate | Download (17.7 kB)

1 4f3d5b76 Michael Hanselmann
#!/usr/bin/python
2 4f3d5b76 Michael Hanselmann
#
3 4f3d5b76 Michael Hanselmann
4 4f3d5b76 Michael Hanselmann
# Copyright (C) 2009 Google Inc.
5 4f3d5b76 Michael Hanselmann
#
6 4f3d5b76 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 4f3d5b76 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 4f3d5b76 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 4f3d5b76 Michael Hanselmann
# (at your option) any later version.
10 4f3d5b76 Michael Hanselmann
#
11 4f3d5b76 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 4f3d5b76 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 4f3d5b76 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 4f3d5b76 Michael Hanselmann
# General Public License for more details.
15 4f3d5b76 Michael Hanselmann
#
16 4f3d5b76 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 4f3d5b76 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 4f3d5b76 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 4f3d5b76 Michael Hanselmann
# 02110-1301, USA.
20 4f3d5b76 Michael Hanselmann
21 4f3d5b76 Michael Hanselmann
22 b4086ce8 Michael Hanselmann
"""Script to generate bash_completion script for Ganeti.
23 b4086ce8 Michael Hanselmann
24 b4086ce8 Michael Hanselmann
"""
25 b4086ce8 Michael Hanselmann
26 b4086ce8 Michael Hanselmann
# pylint: disable-msg=C0103
27 b4086ce8 Michael Hanselmann
# [C0103] Invalid name build-bash-completion
28 b4086ce8 Michael Hanselmann
29 4f3d5b76 Michael Hanselmann
import os
30 4f3d5b76 Michael Hanselmann
import re
31 4f3d5b76 Michael Hanselmann
from cStringIO import StringIO
32 4f3d5b76 Michael Hanselmann
33 4f3d5b76 Michael Hanselmann
from ganeti import constants
34 4f3d5b76 Michael Hanselmann
from ganeti import cli
35 4f3d5b76 Michael Hanselmann
from ganeti import utils
36 e948770c Michael Hanselmann
from ganeti import build
37 4f3d5b76 Michael Hanselmann
38 4f3d5b76 Michael Hanselmann
# _autoconf shouldn't be imported from anywhere except constants.py, but we're
39 4f3d5b76 Michael Hanselmann
# making an exception here because this script is only used at build time.
40 4f3d5b76 Michael Hanselmann
from ganeti import _autoconf
41 4f3d5b76 Michael Hanselmann
42 4f3d5b76 Michael Hanselmann
43 4f3d5b76 Michael Hanselmann
def WritePreamble(sw):
44 4f3d5b76 Michael Hanselmann
  """Writes the script preamble.
45 4f3d5b76 Michael Hanselmann
46 4f3d5b76 Michael Hanselmann
  Helper functions should be written here.
47 4f3d5b76 Michael Hanselmann
48 4f3d5b76 Michael Hanselmann
  """
49 4f3d5b76 Michael Hanselmann
  sw.Write("# This script is automatically generated at build time.")
50 4f3d5b76 Michael Hanselmann
  sw.Write("# Do not modify manually.")
51 4f3d5b76 Michael Hanselmann
52 5b0ca9d4 Michael Hanselmann
  sw.Write("_ganeti_dbglog() {")
53 5b0ca9d4 Michael Hanselmann
  sw.IncIndent()
54 5b0ca9d4 Michael Hanselmann
  try:
55 5b0ca9d4 Michael Hanselmann
    sw.Write("if [[ -n \"$GANETI_COMPL_LOG\" ]]; then")
56 5b0ca9d4 Michael Hanselmann
    sw.IncIndent()
57 5b0ca9d4 Michael Hanselmann
    try:
58 5b0ca9d4 Michael Hanselmann
      sw.Write("{")
59 5b0ca9d4 Michael Hanselmann
      sw.IncIndent()
60 5b0ca9d4 Michael Hanselmann
      try:
61 5b0ca9d4 Michael Hanselmann
        sw.Write("echo ---")
62 5b0ca9d4 Michael Hanselmann
        sw.Write("echo \"$@\"")
63 5b0ca9d4 Michael Hanselmann
        sw.Write("echo")
64 5b0ca9d4 Michael Hanselmann
      finally:
65 5b0ca9d4 Michael Hanselmann
        sw.DecIndent()
66 5b0ca9d4 Michael Hanselmann
      sw.Write("} >> $GANETI_COMPL_LOG")
67 5b0ca9d4 Michael Hanselmann
    finally:
68 5b0ca9d4 Michael Hanselmann
      sw.DecIndent()
69 5b0ca9d4 Michael Hanselmann
    sw.Write("fi")
70 5b0ca9d4 Michael Hanselmann
  finally:
71 5b0ca9d4 Michael Hanselmann
    sw.DecIndent()
72 5b0ca9d4 Michael Hanselmann
  sw.Write("}")
73 5b0ca9d4 Michael Hanselmann
74 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_nodes() {")
75 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
76 4f3d5b76 Michael Hanselmann
  try:
77 4f3d5b76 Michael Hanselmann
    node_list_path = os.path.join(constants.DATA_DIR, "ssconf_node_list")
78 5a78e2e7 Michael Hanselmann
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(node_list_path))
79 4f3d5b76 Michael Hanselmann
  finally:
80 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
81 4f3d5b76 Michael Hanselmann
  sw.Write("}")
82 4f3d5b76 Michael Hanselmann
83 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_instances() {")
84 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
85 4f3d5b76 Michael Hanselmann
  try:
86 4f3d5b76 Michael Hanselmann
    instance_list_path = os.path.join(constants.DATA_DIR,
87 4f3d5b76 Michael Hanselmann
                                      "ssconf_instance_list")
88 5a78e2e7 Michael Hanselmann
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(instance_list_path))
89 4f3d5b76 Michael Hanselmann
  finally:
90 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
91 4f3d5b76 Michael Hanselmann
  sw.Write("}")
92 4f3d5b76 Michael Hanselmann
93 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_jobs() {")
94 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
95 4f3d5b76 Michael Hanselmann
  try:
96 4f3d5b76 Michael Hanselmann
    # FIXME: this is really going into the internals of the job queue
97 5a78e2e7 Michael Hanselmann
    sw.Write(("local jlist=$( shopt -s nullglob &&"
98 5a78e2e7 Michael Hanselmann
              " cd %s 2>/dev/null && echo job-* || : )"),
99 4f3d5b76 Michael Hanselmann
             utils.ShellQuote(constants.QUEUE_DIR))
100 5a78e2e7 Michael Hanselmann
    sw.Write('echo "${jlist//job-/}"')
101 4f3d5b76 Michael Hanselmann
  finally:
102 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
103 4f3d5b76 Michael Hanselmann
  sw.Write("}")
104 4f3d5b76 Michael Hanselmann
105 30d44392 Michael Hanselmann
  for (fnname, paths) in [
106 30d44392 Michael Hanselmann
      ("os", constants.OS_SEARCH_PATH),
107 30d44392 Michael Hanselmann
      ("iallocator", constants.IALLOCATOR_SEARCH_PATH),
108 30d44392 Michael Hanselmann
      ]:
109 30d44392 Michael Hanselmann
    sw.Write("_ganeti_%s() {", fnname)
110 30d44392 Michael Hanselmann
    sw.IncIndent()
111 30d44392 Michael Hanselmann
    try:
112 30d44392 Michael Hanselmann
      # FIXME: Make querying the master for all OSes cheap
113 30d44392 Michael Hanselmann
      for path in paths:
114 30d44392 Michael Hanselmann
        sw.Write("( shopt -s nullglob && cd %s 2>/dev/null && echo * || : )",
115 30d44392 Michael Hanselmann
                 utils.ShellQuote(path))
116 30d44392 Michael Hanselmann
    finally:
117 30d44392 Michael Hanselmann
      sw.DecIndent()
118 30d44392 Michael Hanselmann
    sw.Write("}")
119 4f3d5b76 Michael Hanselmann
120 36e247e1 Guido Trotter
  sw.Write("_ganeti_nodegroup() {")
121 36e247e1 Guido Trotter
  sw.IncIndent()
122 36e247e1 Guido Trotter
  try:
123 36e247e1 Guido Trotter
    nodegroups_path = os.path.join(constants.DATA_DIR, "ssconf_nodegroups")
124 36e247e1 Guido Trotter
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(nodegroups_path))
125 36e247e1 Guido Trotter
  finally:
126 36e247e1 Guido Trotter
    sw.DecIndent()
127 36e247e1 Guido Trotter
  sw.Write("}")
128 36e247e1 Guido Trotter
129 4f3d5b76 Michael Hanselmann
  # Params: <offset> <options with values> <options without values>
130 4f3d5b76 Michael Hanselmann
  # Result variable: $first_arg_idx
131 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_find_first_arg() {")
132 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
133 4f3d5b76 Michael Hanselmann
  try:
134 4f3d5b76 Michael Hanselmann
    sw.Write("local w i")
135 4f3d5b76 Michael Hanselmann
136 4f3d5b76 Michael Hanselmann
    sw.Write("first_arg_idx=")
137 4f3d5b76 Michael Hanselmann
    sw.Write("for (( i=$1; i < COMP_CWORD; ++i )); do")
138 4f3d5b76 Michael Hanselmann
    sw.IncIndent()
139 4f3d5b76 Michael Hanselmann
    try:
140 4f3d5b76 Michael Hanselmann
      sw.Write("w=${COMP_WORDS[$i]}")
141 4f3d5b76 Michael Hanselmann
142 4f3d5b76 Michael Hanselmann
      # Skip option value
143 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ -n "$2" && "$w" == @($2) ]]; then let ++i""")
144 4f3d5b76 Michael Hanselmann
145 4f3d5b76 Michael Hanselmann
      # Skip
146 4f3d5b76 Michael Hanselmann
      sw.Write("""elif [[ -n "$3" && "$w" == @($3) ]]; then :""")
147 4f3d5b76 Michael Hanselmann
148 4f3d5b76 Michael Hanselmann
      # Ah, we found the first argument
149 4f3d5b76 Michael Hanselmann
      sw.Write("else first_arg_idx=$i; break;")
150 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
151 4f3d5b76 Michael Hanselmann
    finally:
152 4f3d5b76 Michael Hanselmann
      sw.DecIndent()
153 4f3d5b76 Michael Hanselmann
    sw.Write("done")
154 4f3d5b76 Michael Hanselmann
  finally:
155 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
156 4f3d5b76 Michael Hanselmann
  sw.Write("}")
157 4f3d5b76 Michael Hanselmann
158 4f3d5b76 Michael Hanselmann
  # Params: <list of options separated by space>
159 4f3d5b76 Michael Hanselmann
  # Input variable: $first_arg_idx
160 4f3d5b76 Michael Hanselmann
  # Result variables: $arg_idx, $choices
161 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_list_options() {")
162 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
163 4f3d5b76 Michael Hanselmann
  try:
164 4f3d5b76 Michael Hanselmann
    sw.Write("""if [[ -z "$first_arg_idx" ]]; then""")
165 4f3d5b76 Michael Hanselmann
    sw.IncIndent()
166 4f3d5b76 Michael Hanselmann
    try:
167 4f3d5b76 Michael Hanselmann
      sw.Write("arg_idx=0")
168 4f3d5b76 Michael Hanselmann
      # Show options only if the current word starts with a dash
169 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ "$cur" == -* ]]; then""")
170 4f3d5b76 Michael Hanselmann
      sw.IncIndent()
171 4f3d5b76 Michael Hanselmann
      try:
172 4f3d5b76 Michael Hanselmann
        sw.Write("choices=$1")
173 4f3d5b76 Michael Hanselmann
      finally:
174 4f3d5b76 Michael Hanselmann
        sw.DecIndent()
175 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
176 4f3d5b76 Michael Hanselmann
      sw.Write("return")
177 4f3d5b76 Michael Hanselmann
    finally:
178 4f3d5b76 Michael Hanselmann
      sw.DecIndent()
179 4f3d5b76 Michael Hanselmann
    sw.Write("fi")
180 4f3d5b76 Michael Hanselmann
181 4f3d5b76 Michael Hanselmann
    # Calculate position of current argument
182 4f3d5b76 Michael Hanselmann
    sw.Write("arg_idx=$(( COMP_CWORD - first_arg_idx ))")
183 4f3d5b76 Michael Hanselmann
    sw.Write("choices=")
184 4f3d5b76 Michael Hanselmann
  finally:
185 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
186 4f3d5b76 Michael Hanselmann
  sw.Write("}")
187 4f3d5b76 Michael Hanselmann
188 d4b94fe8 Michael Hanselmann
  # Params: <long options with equal sign> <all options>
189 d4b94fe8 Michael Hanselmann
  # Result variable: $optcur
190 d4b94fe8 Michael Hanselmann
  sw.Write("_ganeti_checkopt() {")
191 d4b94fe8 Michael Hanselmann
  sw.IncIndent()
192 d4b94fe8 Michael Hanselmann
  try:
193 d4b94fe8 Michael Hanselmann
    sw.Write("""if [[ -n "$1" && "$cur" == @($1) ]]; then""")
194 d4b94fe8 Michael Hanselmann
    sw.IncIndent()
195 d4b94fe8 Michael Hanselmann
    try:
196 d4b94fe8 Michael Hanselmann
      sw.Write("optcur=\"${cur#--*=}\"")
197 d4b94fe8 Michael Hanselmann
      sw.Write("return 0")
198 d4b94fe8 Michael Hanselmann
    finally:
199 d4b94fe8 Michael Hanselmann
      sw.DecIndent()
200 d4b94fe8 Michael Hanselmann
    sw.Write("""elif [[ -n "$2" && "$prev" == @($2) ]]; then""")
201 d4b94fe8 Michael Hanselmann
    sw.IncIndent()
202 d4b94fe8 Michael Hanselmann
    try:
203 d4b94fe8 Michael Hanselmann
      sw.Write("optcur=\"$cur\"")
204 d4b94fe8 Michael Hanselmann
      sw.Write("return 0")
205 d4b94fe8 Michael Hanselmann
    finally:
206 d4b94fe8 Michael Hanselmann
      sw.DecIndent()
207 d4b94fe8 Michael Hanselmann
    sw.Write("fi")
208 d4b94fe8 Michael Hanselmann
209 5b0ca9d4 Michael Hanselmann
    sw.Write("_ganeti_dbglog optcur=\"'$optcur'\"")
210 5b0ca9d4 Michael Hanselmann
211 d4b94fe8 Michael Hanselmann
    sw.Write("return 1")
212 d4b94fe8 Michael Hanselmann
  finally:
213 d4b94fe8 Michael Hanselmann
    sw.DecIndent()
214 d4b94fe8 Michael Hanselmann
  sw.Write("}")
215 d4b94fe8 Michael Hanselmann
216 a10caf87 Michael Hanselmann
  # Params: <compgen options>
217 a10caf87 Michael Hanselmann
  # Result variable: $COMPREPLY
218 a10caf87 Michael Hanselmann
  sw.Write("_ganeti_compgen() {")
219 a10caf87 Michael Hanselmann
  sw.IncIndent()
220 a10caf87 Michael Hanselmann
  try:
221 a10caf87 Michael Hanselmann
    sw.Write("""COMPREPLY=( $(compgen "$@") )""")
222 a10caf87 Michael Hanselmann
    sw.Write("_ganeti_dbglog COMPREPLY=\"${COMPREPLY[@]}\"")
223 a10caf87 Michael Hanselmann
  finally:
224 a10caf87 Michael Hanselmann
    sw.DecIndent()
225 a10caf87 Michael Hanselmann
  sw.Write("}")
226 a10caf87 Michael Hanselmann
227 4f3d5b76 Michael Hanselmann
228 632d5090 Michael Hanselmann
def WriteCompReply(sw, args, cur="\"$cur\""):
229 a10caf87 Michael Hanselmann
  sw.Write("_ganeti_compgen %s -- %s", args, cur)
230 4f3d5b76 Michael Hanselmann
  sw.Write("return")
231 4f3d5b76 Michael Hanselmann
232 4f3d5b76 Michael Hanselmann
233 4f3d5b76 Michael Hanselmann
class CompletionWriter:
234 4f3d5b76 Michael Hanselmann
  """Command completion writer class.
235 4f3d5b76 Michael Hanselmann
236 4f3d5b76 Michael Hanselmann
  """
237 4f3d5b76 Michael Hanselmann
  def __init__(self, arg_offset, opts, args):
238 4f3d5b76 Michael Hanselmann
    self.arg_offset = arg_offset
239 4f3d5b76 Michael Hanselmann
    self.opts = opts
240 4f3d5b76 Michael Hanselmann
    self.args = args
241 4f3d5b76 Michael Hanselmann
242 4f3d5b76 Michael Hanselmann
    for opt in opts:
243 f59418db Michael Hanselmann
      # While documented, these variables aren't seen as public attributes by
244 f59418db Michael Hanselmann
      # pylint. pylint: disable-msg=W0212
245 4f3d5b76 Michael Hanselmann
      opt.all_names = sorted(opt._short_opts + opt._long_opts)
246 4f3d5b76 Michael Hanselmann
247 4f3d5b76 Michael Hanselmann
  def _FindFirstArgument(self, sw):
248 4f3d5b76 Michael Hanselmann
    ignore = []
249 4f3d5b76 Michael Hanselmann
    skip_one = []
250 4f3d5b76 Michael Hanselmann
251 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
252 4f3d5b76 Michael Hanselmann
      if opt.takes_value():
253 4f3d5b76 Michael Hanselmann
        # Ignore value
254 4f3d5b76 Michael Hanselmann
        for i in opt.all_names:
255 580ef58d Michael Hanselmann
          if i.startswith("--"):
256 580ef58d Michael Hanselmann
            ignore.append("%s=*" % utils.ShellQuote(i))
257 4f3d5b76 Michael Hanselmann
          skip_one.append(utils.ShellQuote(i))
258 4f3d5b76 Michael Hanselmann
      else:
259 4f3d5b76 Michael Hanselmann
        ignore.extend([utils.ShellQuote(i) for i in opt.all_names])
260 4f3d5b76 Michael Hanselmann
261 4f3d5b76 Michael Hanselmann
    ignore = sorted(utils.UniqueSequence(ignore))
262 4f3d5b76 Michael Hanselmann
    skip_one = sorted(utils.UniqueSequence(skip_one))
263 4f3d5b76 Michael Hanselmann
264 4f3d5b76 Michael Hanselmann
    if ignore or skip_one:
265 4f3d5b76 Michael Hanselmann
      # Try to locate first argument
266 4f3d5b76 Michael Hanselmann
      sw.Write("_ganeti_find_first_arg %s %s %s",
267 4f3d5b76 Michael Hanselmann
               self.arg_offset + 1,
268 4f3d5b76 Michael Hanselmann
               utils.ShellQuote("|".join(skip_one)),
269 4f3d5b76 Michael Hanselmann
               utils.ShellQuote("|".join(ignore)))
270 4f3d5b76 Michael Hanselmann
    else:
271 4f3d5b76 Michael Hanselmann
      # When there are no options the first argument is always at position
272 4f3d5b76 Michael Hanselmann
      # offset + 1
273 4f3d5b76 Michael Hanselmann
      sw.Write("first_arg_idx=%s", self.arg_offset + 1)
274 4f3d5b76 Michael Hanselmann
275 4f3d5b76 Michael Hanselmann
  def _CompleteOptionValues(self, sw):
276 4f3d5b76 Michael Hanselmann
    # Group by values
277 4f3d5b76 Michael Hanselmann
    # "values" -> [optname1, optname2, ...]
278 4f3d5b76 Michael Hanselmann
    values = {}
279 4f3d5b76 Michael Hanselmann
280 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
281 4f3d5b76 Michael Hanselmann
      if not opt.takes_value():
282 4f3d5b76 Michael Hanselmann
        continue
283 4f3d5b76 Michael Hanselmann
284 4f3d5b76 Michael Hanselmann
      # Only static choices implemented so far (e.g. no node list)
285 4f3d5b76 Michael Hanselmann
      suggest = getattr(opt, "completion_suggest", None)
286 4f3d5b76 Michael Hanselmann
287 e7b61bb0 Iustin Pop
      # our custom option type
288 e7b61bb0 Iustin Pop
      if opt.type == "bool":
289 e7b61bb0 Iustin Pop
        suggest = ["yes", "no"]
290 e7b61bb0 Iustin Pop
291 4f3d5b76 Michael Hanselmann
      if not suggest:
292 4f3d5b76 Michael Hanselmann
        suggest = opt.choices
293 4f3d5b76 Michael Hanselmann
294 63d44c55 Michael Hanselmann
      if (isinstance(suggest, (int, long)) and
295 63d44c55 Michael Hanselmann
          suggest in cli.OPT_COMPL_ALL):
296 63d44c55 Michael Hanselmann
        key = suggest
297 63d44c55 Michael Hanselmann
      elif suggest:
298 63d44c55 Michael Hanselmann
        key = " ".join(sorted(suggest))
299 4f3d5b76 Michael Hanselmann
      else:
300 63d44c55 Michael Hanselmann
        key = ""
301 4f3d5b76 Michael Hanselmann
302 63d44c55 Michael Hanselmann
      values.setdefault(key, []).extend(opt.all_names)
303 4f3d5b76 Michael Hanselmann
304 4f3d5b76 Michael Hanselmann
    # Don't write any code if there are no option values
305 4f3d5b76 Michael Hanselmann
    if not values:
306 4f3d5b76 Michael Hanselmann
      return
307 4f3d5b76 Michael Hanselmann
308 d4b94fe8 Michael Hanselmann
    cur = "\"$optcur\""
309 d4b94fe8 Michael Hanselmann
310 d4b94fe8 Michael Hanselmann
    wrote_opt = False
311 d4b94fe8 Michael Hanselmann
312 d4b94fe8 Michael Hanselmann
    for (suggest, allnames) in values.iteritems():
313 d4b94fe8 Michael Hanselmann
      longnames = [i for i in allnames if i.startswith("--")]
314 d4b94fe8 Michael Hanselmann
315 d4b94fe8 Michael Hanselmann
      if wrote_opt:
316 d4b94fe8 Michael Hanselmann
        condcmd = "elif"
317 d4b94fe8 Michael Hanselmann
      else:
318 d4b94fe8 Michael Hanselmann
        condcmd = "if"
319 d4b94fe8 Michael Hanselmann
320 d4b94fe8 Michael Hanselmann
      sw.Write("%s _ganeti_checkopt %s %s; then", condcmd,
321 d4b94fe8 Michael Hanselmann
               utils.ShellQuote("|".join(["%s=*" % i for i in longnames])),
322 d4b94fe8 Michael Hanselmann
               utils.ShellQuote("|".join(allnames)))
323 d4b94fe8 Michael Hanselmann
      sw.IncIndent()
324 d4b94fe8 Michael Hanselmann
      try:
325 63d44c55 Michael Hanselmann
        if suggest == cli.OPT_COMPL_MANY_NODES:
326 63d44c55 Michael Hanselmann
          # TODO: Implement comma-separated values
327 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W ''", cur=cur)
328 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_NODE:
329 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_nodes)\"", cur=cur)
330 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_INSTANCE:
331 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_instances)\"", cur=cur)
332 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_OS:
333 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_os)\"", cur=cur)
334 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_IALLOCATOR:
335 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_iallocator)\"", cur=cur)
336 36e247e1 Guido Trotter
        elif suggest == cli.OPT_COMPL_ONE_NODEGROUP:
337 36e247e1 Guido Trotter
          WriteCompReply(sw, "-W \"$(_ganeti_nodegroup)\"", cur=cur)
338 2d3ed64b Michael Hanselmann
        elif suggest == cli.OPT_COMPL_INST_ADD_NODES:
339 2d3ed64b Michael Hanselmann
          sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"")
340 2d3ed64b Michael Hanselmann
341 2d3ed64b Michael Hanselmann
          sw.Write("if [[ \"$optcur\" == *:* ]]; then")
342 2d3ed64b Michael Hanselmann
          sw.IncIndent()
343 2d3ed64b Michael Hanselmann
          try:
344 2d3ed64b Michael Hanselmann
            sw.Write("node1=\"${optcur%%:*}\"")
345 2d3ed64b Michael Hanselmann
346 2d3ed64b Michael Hanselmann
            sw.Write("if [[ \"$COMP_WORDBREAKS\" != *:* ]]; then")
347 2d3ed64b Michael Hanselmann
            sw.IncIndent()
348 2d3ed64b Michael Hanselmann
            try:
349 2d3ed64b Michael Hanselmann
              sw.Write("pfx=\"$node1:\"")
350 2d3ed64b Michael Hanselmann
            finally:
351 2d3ed64b Michael Hanselmann
              sw.DecIndent()
352 2d3ed64b Michael Hanselmann
            sw.Write("fi")
353 2d3ed64b Michael Hanselmann
          finally:
354 2d3ed64b Michael Hanselmann
            sw.DecIndent()
355 2d3ed64b Michael Hanselmann
          sw.Write("fi")
356 2d3ed64b Michael Hanselmann
357 2d3ed64b Michael Hanselmann
          sw.Write("_ganeti_dbglog pfx=\"'$pfx'\" curvalue=\"'$curvalue'\""
358 2d3ed64b Michael Hanselmann
                   " node1=\"'$node1'\"")
359 2d3ed64b Michael Hanselmann
360 2d3ed64b Michael Hanselmann
          sw.Write("for i in $(_ganeti_nodes); do")
361 2d3ed64b Michael Hanselmann
          sw.IncIndent()
362 2d3ed64b Michael Hanselmann
          try:
363 2d3ed64b Michael Hanselmann
            sw.Write("if [[ -z \"$node1\" ]]; then")
364 2d3ed64b Michael Hanselmann
            sw.IncIndent()
365 2d3ed64b Michael Hanselmann
            try:
366 2d3ed64b Michael Hanselmann
              sw.Write("tmp=\"$tmp $i $i:\"")
367 2d3ed64b Michael Hanselmann
            finally:
368 2d3ed64b Michael Hanselmann
              sw.DecIndent()
369 2d3ed64b Michael Hanselmann
            sw.Write("elif [[ \"$i\" != \"$node1\" ]]; then")
370 2d3ed64b Michael Hanselmann
            sw.IncIndent()
371 2d3ed64b Michael Hanselmann
            try:
372 2d3ed64b Michael Hanselmann
              sw.Write("tmp=\"$tmp $i\"")
373 2d3ed64b Michael Hanselmann
            finally:
374 2d3ed64b Michael Hanselmann
              sw.DecIndent()
375 2d3ed64b Michael Hanselmann
            sw.Write("fi")
376 2d3ed64b Michael Hanselmann
          finally:
377 2d3ed64b Michael Hanselmann
            sw.DecIndent()
378 2d3ed64b Michael Hanselmann
          sw.Write("done")
379 2d3ed64b Michael Hanselmann
380 2d3ed64b Michael Hanselmann
          WriteCompReply(sw, "-P \"$pfx\" -W \"$tmp\"", cur="\"$curvalue\"")
381 63d44c55 Michael Hanselmann
        else:
382 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W %s" % utils.ShellQuote(suggest), cur=cur)
383 d4b94fe8 Michael Hanselmann
      finally:
384 d4b94fe8 Michael Hanselmann
        sw.DecIndent()
385 d4b94fe8 Michael Hanselmann
386 d4b94fe8 Michael Hanselmann
      wrote_opt = True
387 d4b94fe8 Michael Hanselmann
388 d4b94fe8 Michael Hanselmann
    if wrote_opt:
389 d4b94fe8 Michael Hanselmann
      sw.Write("fi")
390 d4b94fe8 Michael Hanselmann
391 d4b94fe8 Michael Hanselmann
    return
392 632d5090 Michael Hanselmann
393 4f3d5b76 Michael Hanselmann
  def _CompleteArguments(self, sw):
394 4f3d5b76 Michael Hanselmann
    if not (self.opts or self.args):
395 4f3d5b76 Michael Hanselmann
      return
396 4f3d5b76 Michael Hanselmann
397 4f3d5b76 Michael Hanselmann
    all_option_names = []
398 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
399 4f3d5b76 Michael Hanselmann
      all_option_names.extend(opt.all_names)
400 4f3d5b76 Michael Hanselmann
    all_option_names.sort()
401 4f3d5b76 Michael Hanselmann
402 4f3d5b76 Michael Hanselmann
    # List options if no argument has been specified yet
403 4f3d5b76 Michael Hanselmann
    sw.Write("_ganeti_list_options %s",
404 4f3d5b76 Michael Hanselmann
             utils.ShellQuote(" ".join(all_option_names)))
405 4f3d5b76 Michael Hanselmann
406 4f3d5b76 Michael Hanselmann
    if self.args:
407 4f3d5b76 Michael Hanselmann
      last_idx = len(self.args) - 1
408 4f3d5b76 Michael Hanselmann
      last_arg_end = 0
409 4f3d5b76 Michael Hanselmann
      varlen_arg_idx = None
410 4f3d5b76 Michael Hanselmann
      wrote_arg = False
411 4f3d5b76 Michael Hanselmann
412 4f3d5b76 Michael Hanselmann
      # Write some debug comments
413 4f3d5b76 Michael Hanselmann
      for idx, arg in enumerate(self.args):
414 4f3d5b76 Michael Hanselmann
        sw.Write("# %s: %r", idx, arg)
415 4f3d5b76 Michael Hanselmann
416 4f3d5b76 Michael Hanselmann
      sw.Write("compgenargs=")
417 4f3d5b76 Michael Hanselmann
418 4f3d5b76 Michael Hanselmann
      for idx, arg in enumerate(self.args):
419 4f3d5b76 Michael Hanselmann
        assert arg.min is not None and arg.min >= 0
420 4f3d5b76 Michael Hanselmann
        assert not (idx < last_idx and arg.max is None)
421 4f3d5b76 Michael Hanselmann
422 4f3d5b76 Michael Hanselmann
        if arg.min != arg.max or arg.max is None:
423 4f3d5b76 Michael Hanselmann
          if varlen_arg_idx is not None:
424 4f3d5b76 Michael Hanselmann
            raise Exception("Only one argument can have a variable length")
425 4f3d5b76 Michael Hanselmann
          varlen_arg_idx = idx
426 4f3d5b76 Michael Hanselmann
427 4f3d5b76 Michael Hanselmann
        compgenargs = []
428 4f3d5b76 Michael Hanselmann
429 4f3d5b76 Michael Hanselmann
        if isinstance(arg, cli.ArgUnknown):
430 4f3d5b76 Michael Hanselmann
          choices = ""
431 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgSuggest):
432 4f3d5b76 Michael Hanselmann
          choices = utils.ShellQuote(" ".join(arg.choices))
433 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgInstance):
434 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_instances)"
435 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgNode):
436 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_nodes)"
437 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgJobId):
438 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_jobs)"
439 f9faf9c3 René Nussbaumer
        elif isinstance(arg, cli.ArgOs):
440 f9faf9c3 René Nussbaumer
          choices = "$(_ganeti_os)"
441 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgFile):
442 4f3d5b76 Michael Hanselmann
          choices = ""
443 4f3d5b76 Michael Hanselmann
          compgenargs.append("-f")
444 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgCommand):
445 4f3d5b76 Michael Hanselmann
          choices = ""
446 4f3d5b76 Michael Hanselmann
          compgenargs.append("-c")
447 83ec7961 Michael Hanselmann
        elif isinstance(arg, cli.ArgHost):
448 83ec7961 Michael Hanselmann
          choices = ""
449 83ec7961 Michael Hanselmann
          compgenargs.append("-A hostname")
450 4f3d5b76 Michael Hanselmann
        else:
451 4f3d5b76 Michael Hanselmann
          raise Exception("Unknown argument type %r" % arg)
452 4f3d5b76 Michael Hanselmann
453 4f3d5b76 Michael Hanselmann
        if arg.min == 1 and arg.max == 1:
454 4f3d5b76 Michael Hanselmann
          cmpcode = """"$arg_idx" == %d""" % (last_arg_end)
455 8adfb141 Michael Hanselmann
        elif arg.max is None:
456 8adfb141 Michael Hanselmann
          cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
457 5431eff1 Michael Hanselmann
        elif arg.min <= arg.max:
458 4f3d5b76 Michael Hanselmann
          cmpcode = (""""$arg_idx" -ge %d && "$arg_idx" -lt %d""" %
459 4f3d5b76 Michael Hanselmann
                     (last_arg_end, last_arg_end + arg.max))
460 4f3d5b76 Michael Hanselmann
        else:
461 4f3d5b76 Michael Hanselmann
          raise Exception("Unable to generate argument position condition")
462 4f3d5b76 Michael Hanselmann
463 4f3d5b76 Michael Hanselmann
        last_arg_end += arg.min
464 4f3d5b76 Michael Hanselmann
465 4f3d5b76 Michael Hanselmann
        if choices or compgenargs:
466 4f3d5b76 Michael Hanselmann
          if wrote_arg:
467 4f3d5b76 Michael Hanselmann
            condcmd = "elif"
468 4f3d5b76 Michael Hanselmann
          else:
469 4f3d5b76 Michael Hanselmann
            condcmd = "if"
470 4f3d5b76 Michael Hanselmann
471 4f3d5b76 Michael Hanselmann
          sw.Write("""%s [[ %s ]]; then""", condcmd, cmpcode)
472 4f3d5b76 Michael Hanselmann
          sw.IncIndent()
473 4f3d5b76 Michael Hanselmann
          try:
474 4f3d5b76 Michael Hanselmann
            if choices:
475 4f3d5b76 Michael Hanselmann
              sw.Write("""choices="$choices "%s""", choices)
476 4f3d5b76 Michael Hanselmann
            if compgenargs:
477 8adfb141 Michael Hanselmann
              sw.Write("compgenargs=%s",
478 8adfb141 Michael Hanselmann
                       utils.ShellQuote(" ".join(compgenargs)))
479 4f3d5b76 Michael Hanselmann
          finally:
480 4f3d5b76 Michael Hanselmann
            sw.DecIndent()
481 4f3d5b76 Michael Hanselmann
482 4f3d5b76 Michael Hanselmann
          wrote_arg = True
483 4f3d5b76 Michael Hanselmann
484 4f3d5b76 Michael Hanselmann
      if wrote_arg:
485 4f3d5b76 Michael Hanselmann
        sw.Write("fi")
486 4f3d5b76 Michael Hanselmann
487 4f3d5b76 Michael Hanselmann
    if self.args:
488 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, """-W "$choices" $compgenargs""")
489 4f3d5b76 Michael Hanselmann
    else:
490 4f3d5b76 Michael Hanselmann
      # $compgenargs exists only if there are arguments
491 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, '-W "$choices"')
492 4f3d5b76 Michael Hanselmann
493 4f3d5b76 Michael Hanselmann
  def WriteTo(self, sw):
494 4f3d5b76 Michael Hanselmann
    self._FindFirstArgument(sw)
495 4f3d5b76 Michael Hanselmann
    self._CompleteOptionValues(sw)
496 4f3d5b76 Michael Hanselmann
    self._CompleteArguments(sw)
497 4f3d5b76 Michael Hanselmann
498 4f3d5b76 Michael Hanselmann
499 4f3d5b76 Michael Hanselmann
def WriteCompletion(sw, scriptname, funcname,
500 4f3d5b76 Michael Hanselmann
                    commands=None,
501 4f3d5b76 Michael Hanselmann
                    opts=None, args=None):
502 4f3d5b76 Michael Hanselmann
  """Writes the completion code for one command.
503 4f3d5b76 Michael Hanselmann
504 4f3d5b76 Michael Hanselmann
  @type sw: ShellWriter
505 4f3d5b76 Michael Hanselmann
  @param sw: Script writer
506 4f3d5b76 Michael Hanselmann
  @type scriptname: string
507 4f3d5b76 Michael Hanselmann
  @param scriptname: Name of command line program
508 4f3d5b76 Michael Hanselmann
  @type funcname: string
509 4f3d5b76 Michael Hanselmann
  @param funcname: Shell function name
510 4f3d5b76 Michael Hanselmann
  @type commands: list
511 4f3d5b76 Michael Hanselmann
  @param commands: List of all subcommands in this program
512 4f3d5b76 Michael Hanselmann
513 4f3d5b76 Michael Hanselmann
  """
514 4f3d5b76 Michael Hanselmann
  sw.Write("%s() {", funcname)
515 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
516 4f3d5b76 Michael Hanselmann
  try:
517 580ef58d Michael Hanselmann
    sw.Write("local "
518 632d5090 Michael Hanselmann
             ' cur="${COMP_WORDS[COMP_CWORD]}"'
519 580ef58d Michael Hanselmann
             ' prev="${COMP_WORDS[COMP_CWORD-1]}"'
520 d4b94fe8 Michael Hanselmann
             ' i first_arg_idx choices compgenargs arg_idx optcur')
521 580ef58d Michael Hanselmann
522 5b0ca9d4 Michael Hanselmann
    sw.Write("_ganeti_dbglog cur=\"$cur\" prev=\"$prev\"")
523 5b0ca9d4 Michael Hanselmann
    sw.Write("[[ -n \"$GANETI_COMPL_LOG\" ]] &&"
524 5b0ca9d4 Michael Hanselmann
             " _ganeti_dbglog \"$(set | grep ^COMP_)\"")
525 4f3d5b76 Michael Hanselmann
526 4f3d5b76 Michael Hanselmann
    sw.Write("COMPREPLY=()")
527 4f3d5b76 Michael Hanselmann
528 4f3d5b76 Michael Hanselmann
    if opts is not None and args is not None:
529 4f3d5b76 Michael Hanselmann
      assert not commands
530 4f3d5b76 Michael Hanselmann
      CompletionWriter(0, opts, args).WriteTo(sw)
531 4f3d5b76 Michael Hanselmann
532 4f3d5b76 Michael Hanselmann
    else:
533 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ "$COMP_CWORD" == 1 ]]; then""")
534 4f3d5b76 Michael Hanselmann
      sw.IncIndent()
535 4f3d5b76 Michael Hanselmann
      try:
536 4f3d5b76 Michael Hanselmann
        # Complete the command name
537 4f3d5b76 Michael Hanselmann
        WriteCompReply(sw,
538 4f3d5b76 Michael Hanselmann
                       ("-W %s" %
539 4f3d5b76 Michael Hanselmann
                        utils.ShellQuote(" ".join(sorted(commands.keys())))))
540 4f3d5b76 Michael Hanselmann
      finally:
541 4f3d5b76 Michael Hanselmann
        sw.DecIndent()
542 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
543 4f3d5b76 Michael Hanselmann
544 4f3d5b76 Michael Hanselmann
      # We're doing options and arguments to commands
545 4f3d5b76 Michael Hanselmann
      sw.Write("""case "${COMP_WORDS[1]}" in""")
546 4f3d5b76 Michael Hanselmann
      for cmd, (_, argdef, optdef, _, _) in commands.iteritems():
547 4f3d5b76 Michael Hanselmann
        if not (argdef or optdef):
548 4f3d5b76 Michael Hanselmann
          continue
549 4f3d5b76 Michael Hanselmann
550 4f3d5b76 Michael Hanselmann
        # TODO: Group by arguments and options
551 4f3d5b76 Michael Hanselmann
        sw.Write("%s)", utils.ShellQuote(cmd))
552 4f3d5b76 Michael Hanselmann
        sw.IncIndent()
553 4f3d5b76 Michael Hanselmann
        try:
554 4f3d5b76 Michael Hanselmann
          CompletionWriter(1, optdef, argdef).WriteTo(sw)
555 4f3d5b76 Michael Hanselmann
        finally:
556 4f3d5b76 Michael Hanselmann
          sw.DecIndent()
557 4f3d5b76 Michael Hanselmann
558 4f3d5b76 Michael Hanselmann
        sw.Write(";;")
559 4f3d5b76 Michael Hanselmann
      sw.Write("esac")
560 4f3d5b76 Michael Hanselmann
  finally:
561 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
562 4f3d5b76 Michael Hanselmann
  sw.Write("}")
563 4f3d5b76 Michael Hanselmann
564 4f3d5b76 Michael Hanselmann
  sw.Write("complete -F %s -o filenames %s",
565 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(funcname),
566 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(scriptname))
567 4f3d5b76 Michael Hanselmann
568 4f3d5b76 Michael Hanselmann
569 4f3d5b76 Michael Hanselmann
def GetFunctionName(name):
570 4f3d5b76 Michael Hanselmann
  return "_" + re.sub(r"[^a-z0-9]+", "_", name.lower())
571 4f3d5b76 Michael Hanselmann
572 4f3d5b76 Michael Hanselmann
573 4f3d5b76 Michael Hanselmann
def GetCommands(filename, module):
574 4f3d5b76 Michael Hanselmann
  """Returns the commands defined in a module.
575 4f3d5b76 Michael Hanselmann
576 4f3d5b76 Michael Hanselmann
  Aliases are also added as commands.
577 4f3d5b76 Michael Hanselmann
578 4f3d5b76 Michael Hanselmann
  """
579 4f3d5b76 Michael Hanselmann
  try:
580 4f3d5b76 Michael Hanselmann
    commands = getattr(module, "commands")
581 f59418db Michael Hanselmann
  except AttributeError:
582 4f3d5b76 Michael Hanselmann
    raise Exception("Script %s doesn't have 'commands' attribute" %
583 4f3d5b76 Michael Hanselmann
                    filename)
584 4f3d5b76 Michael Hanselmann
585 1f4e391b Michael Hanselmann
  # Add the implicit "--help" option
586 1f4e391b Michael Hanselmann
  help_option = cli.cli_option("-h", "--help", default=False,
587 1f4e391b Michael Hanselmann
                               action="store_true")
588 1f4e391b Michael Hanselmann
589 5786c087 Michael Hanselmann
  for name, (_, _, optdef, _, _) in commands.items():
590 1f4e391b Michael Hanselmann
    if help_option not in optdef:
591 1f4e391b Michael Hanselmann
      optdef.append(help_option)
592 5786c087 Michael Hanselmann
    for opt in cli.COMMON_OPTS:
593 5786c087 Michael Hanselmann
      if opt in optdef:
594 5786c087 Michael Hanselmann
        raise Exception("Common option '%s' listed for command '%s' in %s" %
595 5786c087 Michael Hanselmann
                        (opt, name, filename))
596 5786c087 Michael Hanselmann
      optdef.append(opt)
597 1f4e391b Michael Hanselmann
598 4f3d5b76 Michael Hanselmann
  # Use aliases
599 4f3d5b76 Michael Hanselmann
  aliases = getattr(module, "aliases", {})
600 4f3d5b76 Michael Hanselmann
  if aliases:
601 4f3d5b76 Michael Hanselmann
    commands = commands.copy()
602 4f3d5b76 Michael Hanselmann
    for name, target in aliases.iteritems():
603 4f3d5b76 Michael Hanselmann
      commands[name] = commands[target]
604 4f3d5b76 Michael Hanselmann
605 4f3d5b76 Michael Hanselmann
  return commands
606 4f3d5b76 Michael Hanselmann
607 4f3d5b76 Michael Hanselmann
608 4f3d5b76 Michael Hanselmann
def main():
609 4f3d5b76 Michael Hanselmann
  buf = StringIO()
610 858905fb Michael Hanselmann
  sw = utils.ShellWriter(buf)
611 4f3d5b76 Michael Hanselmann
612 4f3d5b76 Michael Hanselmann
  WritePreamble(sw)
613 4f3d5b76 Michael Hanselmann
614 4f3d5b76 Michael Hanselmann
  # gnt-* scripts
615 4f3d5b76 Michael Hanselmann
  for scriptname in _autoconf.GNT_SCRIPTS:
616 4f3d5b76 Michael Hanselmann
    filename = "scripts/%s" % scriptname
617 4f3d5b76 Michael Hanselmann
618 4f3d5b76 Michael Hanselmann
    WriteCompletion(sw, scriptname,
619 4f3d5b76 Michael Hanselmann
                    GetFunctionName(scriptname),
620 d6f5892b Michael Hanselmann
                    commands=GetCommands(filename,
621 e948770c Michael Hanselmann
                                         build.LoadModule(filename)))
622 4f3d5b76 Michael Hanselmann
623 4f3d5b76 Michael Hanselmann
  # Burnin script
624 e948770c Michael Hanselmann
  burnin = build.LoadModule("tools/burnin")
625 4f3d5b76 Michael Hanselmann
  WriteCompletion(sw, "%s/burnin" % constants.TOOLSDIR, "_ganeti_burnin",
626 4f3d5b76 Michael Hanselmann
                  opts=burnin.OPTIONS, args=burnin.ARGUMENTS)
627 4f3d5b76 Michael Hanselmann
628 4f3d5b76 Michael Hanselmann
  print buf.getvalue()
629 4f3d5b76 Michael Hanselmann
630 4f3d5b76 Michael Hanselmann
631 4f3d5b76 Michael Hanselmann
if __name__ == "__main__":
632 4f3d5b76 Michael Hanselmann
  main()