Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ d72ff6c3

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 667dbd6b Adeodato Simo
        elif isinstance(arg, cli.ArgGroup):
438 667dbd6b Adeodato Simo
          choices = "$(_ganeti_nodegroup)"
439 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgJobId):
440 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_jobs)"
441 f9faf9c3 Renรฉ Nussbaumer
        elif isinstance(arg, cli.ArgOs):
442 f9faf9c3 Renรฉ Nussbaumer
          choices = "$(_ganeti_os)"
443 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgFile):
444 4f3d5b76 Michael Hanselmann
          choices = ""
445 4f3d5b76 Michael Hanselmann
          compgenargs.append("-f")
446 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgCommand):
447 4f3d5b76 Michael Hanselmann
          choices = ""
448 4f3d5b76 Michael Hanselmann
          compgenargs.append("-c")
449 83ec7961 Michael Hanselmann
        elif isinstance(arg, cli.ArgHost):
450 83ec7961 Michael Hanselmann
          choices = ""
451 83ec7961 Michael Hanselmann
          compgenargs.append("-A hostname")
452 4f3d5b76 Michael Hanselmann
        else:
453 4f3d5b76 Michael Hanselmann
          raise Exception("Unknown argument type %r" % arg)
454 4f3d5b76 Michael Hanselmann
455 4f3d5b76 Michael Hanselmann
        if arg.min == 1 and arg.max == 1:
456 4f3d5b76 Michael Hanselmann
          cmpcode = """"$arg_idx" == %d""" % (last_arg_end)
457 8adfb141 Michael Hanselmann
        elif arg.max is None:
458 8adfb141 Michael Hanselmann
          cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
459 5431eff1 Michael Hanselmann
        elif arg.min <= arg.max:
460 4f3d5b76 Michael Hanselmann
          cmpcode = (""""$arg_idx" -ge %d && "$arg_idx" -lt %d""" %
461 4f3d5b76 Michael Hanselmann
                     (last_arg_end, last_arg_end + arg.max))
462 4f3d5b76 Michael Hanselmann
        else:
463 4f3d5b76 Michael Hanselmann
          raise Exception("Unable to generate argument position condition")
464 4f3d5b76 Michael Hanselmann
465 4f3d5b76 Michael Hanselmann
        last_arg_end += arg.min
466 4f3d5b76 Michael Hanselmann
467 4f3d5b76 Michael Hanselmann
        if choices or compgenargs:
468 4f3d5b76 Michael Hanselmann
          if wrote_arg:
469 4f3d5b76 Michael Hanselmann
            condcmd = "elif"
470 4f3d5b76 Michael Hanselmann
          else:
471 4f3d5b76 Michael Hanselmann
            condcmd = "if"
472 4f3d5b76 Michael Hanselmann
473 4f3d5b76 Michael Hanselmann
          sw.Write("""%s [[ %s ]]; then""", condcmd, cmpcode)
474 4f3d5b76 Michael Hanselmann
          sw.IncIndent()
475 4f3d5b76 Michael Hanselmann
          try:
476 4f3d5b76 Michael Hanselmann
            if choices:
477 4f3d5b76 Michael Hanselmann
              sw.Write("""choices="$choices "%s""", choices)
478 4f3d5b76 Michael Hanselmann
            if compgenargs:
479 8adfb141 Michael Hanselmann
              sw.Write("compgenargs=%s",
480 8adfb141 Michael Hanselmann
                       utils.ShellQuote(" ".join(compgenargs)))
481 4f3d5b76 Michael Hanselmann
          finally:
482 4f3d5b76 Michael Hanselmann
            sw.DecIndent()
483 4f3d5b76 Michael Hanselmann
484 4f3d5b76 Michael Hanselmann
          wrote_arg = True
485 4f3d5b76 Michael Hanselmann
486 4f3d5b76 Michael Hanselmann
      if wrote_arg:
487 4f3d5b76 Michael Hanselmann
        sw.Write("fi")
488 4f3d5b76 Michael Hanselmann
489 4f3d5b76 Michael Hanselmann
    if self.args:
490 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, """-W "$choices" $compgenargs""")
491 4f3d5b76 Michael Hanselmann
    else:
492 4f3d5b76 Michael Hanselmann
      # $compgenargs exists only if there are arguments
493 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, '-W "$choices"')
494 4f3d5b76 Michael Hanselmann
495 4f3d5b76 Michael Hanselmann
  def WriteTo(self, sw):
496 4f3d5b76 Michael Hanselmann
    self._FindFirstArgument(sw)
497 4f3d5b76 Michael Hanselmann
    self._CompleteOptionValues(sw)
498 4f3d5b76 Michael Hanselmann
    self._CompleteArguments(sw)
499 4f3d5b76 Michael Hanselmann
500 4f3d5b76 Michael Hanselmann
501 4f3d5b76 Michael Hanselmann
def WriteCompletion(sw, scriptname, funcname,
502 4f3d5b76 Michael Hanselmann
                    commands=None,
503 4f3d5b76 Michael Hanselmann
                    opts=None, args=None):
504 4f3d5b76 Michael Hanselmann
  """Writes the completion code for one command.
505 4f3d5b76 Michael Hanselmann
506 4f3d5b76 Michael Hanselmann
  @type sw: ShellWriter
507 4f3d5b76 Michael Hanselmann
  @param sw: Script writer
508 4f3d5b76 Michael Hanselmann
  @type scriptname: string
509 4f3d5b76 Michael Hanselmann
  @param scriptname: Name of command line program
510 4f3d5b76 Michael Hanselmann
  @type funcname: string
511 4f3d5b76 Michael Hanselmann
  @param funcname: Shell function name
512 4f3d5b76 Michael Hanselmann
  @type commands: list
513 4f3d5b76 Michael Hanselmann
  @param commands: List of all subcommands in this program
514 4f3d5b76 Michael Hanselmann
515 4f3d5b76 Michael Hanselmann
  """
516 4f3d5b76 Michael Hanselmann
  sw.Write("%s() {", funcname)
517 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
518 4f3d5b76 Michael Hanselmann
  try:
519 580ef58d Michael Hanselmann
    sw.Write("local "
520 632d5090 Michael Hanselmann
             ' cur="${COMP_WORDS[COMP_CWORD]}"'
521 580ef58d Michael Hanselmann
             ' prev="${COMP_WORDS[COMP_CWORD-1]}"'
522 d4b94fe8 Michael Hanselmann
             ' i first_arg_idx choices compgenargs arg_idx optcur')
523 580ef58d Michael Hanselmann
524 5b0ca9d4 Michael Hanselmann
    sw.Write("_ganeti_dbglog cur=\"$cur\" prev=\"$prev\"")
525 5b0ca9d4 Michael Hanselmann
    sw.Write("[[ -n \"$GANETI_COMPL_LOG\" ]] &&"
526 5b0ca9d4 Michael Hanselmann
             " _ganeti_dbglog \"$(set | grep ^COMP_)\"")
527 4f3d5b76 Michael Hanselmann
528 4f3d5b76 Michael Hanselmann
    sw.Write("COMPREPLY=()")
529 4f3d5b76 Michael Hanselmann
530 4f3d5b76 Michael Hanselmann
    if opts is not None and args is not None:
531 4f3d5b76 Michael Hanselmann
      assert not commands
532 4f3d5b76 Michael Hanselmann
      CompletionWriter(0, opts, args).WriteTo(sw)
533 4f3d5b76 Michael Hanselmann
534 4f3d5b76 Michael Hanselmann
    else:
535 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ "$COMP_CWORD" == 1 ]]; then""")
536 4f3d5b76 Michael Hanselmann
      sw.IncIndent()
537 4f3d5b76 Michael Hanselmann
      try:
538 4f3d5b76 Michael Hanselmann
        # Complete the command name
539 4f3d5b76 Michael Hanselmann
        WriteCompReply(sw,
540 4f3d5b76 Michael Hanselmann
                       ("-W %s" %
541 4f3d5b76 Michael Hanselmann
                        utils.ShellQuote(" ".join(sorted(commands.keys())))))
542 4f3d5b76 Michael Hanselmann
      finally:
543 4f3d5b76 Michael Hanselmann
        sw.DecIndent()
544 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
545 4f3d5b76 Michael Hanselmann
546 4f3d5b76 Michael Hanselmann
      # We're doing options and arguments to commands
547 4f3d5b76 Michael Hanselmann
      sw.Write("""case "${COMP_WORDS[1]}" in""")
548 4f3d5b76 Michael Hanselmann
      for cmd, (_, argdef, optdef, _, _) in commands.iteritems():
549 4f3d5b76 Michael Hanselmann
        if not (argdef or optdef):
550 4f3d5b76 Michael Hanselmann
          continue
551 4f3d5b76 Michael Hanselmann
552 4f3d5b76 Michael Hanselmann
        # TODO: Group by arguments and options
553 4f3d5b76 Michael Hanselmann
        sw.Write("%s)", utils.ShellQuote(cmd))
554 4f3d5b76 Michael Hanselmann
        sw.IncIndent()
555 4f3d5b76 Michael Hanselmann
        try:
556 4f3d5b76 Michael Hanselmann
          CompletionWriter(1, optdef, argdef).WriteTo(sw)
557 4f3d5b76 Michael Hanselmann
        finally:
558 4f3d5b76 Michael Hanselmann
          sw.DecIndent()
559 4f3d5b76 Michael Hanselmann
560 4f3d5b76 Michael Hanselmann
        sw.Write(";;")
561 4f3d5b76 Michael Hanselmann
      sw.Write("esac")
562 4f3d5b76 Michael Hanselmann
  finally:
563 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
564 4f3d5b76 Michael Hanselmann
  sw.Write("}")
565 4f3d5b76 Michael Hanselmann
566 4f3d5b76 Michael Hanselmann
  sw.Write("complete -F %s -o filenames %s",
567 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(funcname),
568 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(scriptname))
569 4f3d5b76 Michael Hanselmann
570 4f3d5b76 Michael Hanselmann
571 4f3d5b76 Michael Hanselmann
def GetFunctionName(name):
572 4f3d5b76 Michael Hanselmann
  return "_" + re.sub(r"[^a-z0-9]+", "_", name.lower())
573 4f3d5b76 Michael Hanselmann
574 4f3d5b76 Michael Hanselmann
575 4f3d5b76 Michael Hanselmann
def GetCommands(filename, module):
576 4f3d5b76 Michael Hanselmann
  """Returns the commands defined in a module.
577 4f3d5b76 Michael Hanselmann
578 4f3d5b76 Michael Hanselmann
  Aliases are also added as commands.
579 4f3d5b76 Michael Hanselmann
580 4f3d5b76 Michael Hanselmann
  """
581 4f3d5b76 Michael Hanselmann
  try:
582 4f3d5b76 Michael Hanselmann
    commands = getattr(module, "commands")
583 f59418db Michael Hanselmann
  except AttributeError:
584 4f3d5b76 Michael Hanselmann
    raise Exception("Script %s doesn't have 'commands' attribute" %
585 4f3d5b76 Michael Hanselmann
                    filename)
586 4f3d5b76 Michael Hanselmann
587 1f4e391b Michael Hanselmann
  # Add the implicit "--help" option
588 1f4e391b Michael Hanselmann
  help_option = cli.cli_option("-h", "--help", default=False,
589 1f4e391b Michael Hanselmann
                               action="store_true")
590 1f4e391b Michael Hanselmann
591 5786c087 Michael Hanselmann
  for name, (_, _, optdef, _, _) in commands.items():
592 1f4e391b Michael Hanselmann
    if help_option not in optdef:
593 1f4e391b Michael Hanselmann
      optdef.append(help_option)
594 5786c087 Michael Hanselmann
    for opt in cli.COMMON_OPTS:
595 5786c087 Michael Hanselmann
      if opt in optdef:
596 5786c087 Michael Hanselmann
        raise Exception("Common option '%s' listed for command '%s' in %s" %
597 5786c087 Michael Hanselmann
                        (opt, name, filename))
598 5786c087 Michael Hanselmann
      optdef.append(opt)
599 1f4e391b Michael Hanselmann
600 4f3d5b76 Michael Hanselmann
  # Use aliases
601 4f3d5b76 Michael Hanselmann
  aliases = getattr(module, "aliases", {})
602 4f3d5b76 Michael Hanselmann
  if aliases:
603 4f3d5b76 Michael Hanselmann
    commands = commands.copy()
604 4f3d5b76 Michael Hanselmann
    for name, target in aliases.iteritems():
605 4f3d5b76 Michael Hanselmann
      commands[name] = commands[target]
606 4f3d5b76 Michael Hanselmann
607 4f3d5b76 Michael Hanselmann
  return commands
608 4f3d5b76 Michael Hanselmann
609 4f3d5b76 Michael Hanselmann
610 4f3d5b76 Michael Hanselmann
def main():
611 4f3d5b76 Michael Hanselmann
  buf = StringIO()
612 858905fb Michael Hanselmann
  sw = utils.ShellWriter(buf)
613 4f3d5b76 Michael Hanselmann
614 4f3d5b76 Michael Hanselmann
  WritePreamble(sw)
615 4f3d5b76 Michael Hanselmann
616 4f3d5b76 Michael Hanselmann
  # gnt-* scripts
617 4f3d5b76 Michael Hanselmann
  for scriptname in _autoconf.GNT_SCRIPTS:
618 4f3d5b76 Michael Hanselmann
    filename = "scripts/%s" % scriptname
619 4f3d5b76 Michael Hanselmann
620 4f3d5b76 Michael Hanselmann
    WriteCompletion(sw, scriptname,
621 4f3d5b76 Michael Hanselmann
                    GetFunctionName(scriptname),
622 d6f5892b Michael Hanselmann
                    commands=GetCommands(filename,
623 e948770c Michael Hanselmann
                                         build.LoadModule(filename)))
624 4f3d5b76 Michael Hanselmann
625 4f3d5b76 Michael Hanselmann
  # Burnin script
626 e948770c Michael Hanselmann
  burnin = build.LoadModule("tools/burnin")
627 4f3d5b76 Michael Hanselmann
  WriteCompletion(sw, "%s/burnin" % constants.TOOLSDIR, "_ganeti_burnin",
628 4f3d5b76 Michael Hanselmann
                  opts=burnin.OPTIONS, args=burnin.ARGUMENTS)
629 4f3d5b76 Michael Hanselmann
630 4f3d5b76 Michael Hanselmann
  print buf.getvalue()
631 4f3d5b76 Michael Hanselmann
632 4f3d5b76 Michael Hanselmann
633 4f3d5b76 Michael Hanselmann
if __name__ == "__main__":
634 4f3d5b76 Michael Hanselmann
  main()