| Index: tools/nixysa/third_party/gflags-1.0/python/gflags2man.py
|
| ===================================================================
|
| --- tools/nixysa/third_party/gflags-1.0/python/gflags2man.py (revision 0)
|
| +++ tools/nixysa/third_party/gflags-1.0/python/gflags2man.py (revision 0)
|
| @@ -0,0 +1,536 @@
|
| +#!/usr/bin/env python
|
| +#
|
| +# Copyright (c) 2007, Google Inc.
|
| +# All rights reserved.
|
| +#
|
| +# Redistribution and use in source and binary forms, with or without
|
| +# modification, are permitted provided that the following conditions are
|
| +# met:
|
| +#
|
| +# * Redistributions of source code must retain the above copyright
|
| +# notice, this list of conditions and the following disclaimer.
|
| +# * Redistributions in binary form must reproduce the above
|
| +# copyright notice, this list of conditions and the following disclaimer
|
| +# in the documentation and/or other materials provided with the
|
| +# distribution.
|
| +# * Neither the name of Google Inc. nor the names of its
|
| +# contributors may be used to endorse or promote products derived from
|
| +# this software without specific prior written permission.
|
| +#
|
| +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| +
|
| +"""gflags2man runs a Google flags base program and generates a man page.
|
| +
|
| +Run the program, parse the output, and then format that into a man
|
| +page.
|
| +
|
| +Usage:
|
| + gflags2man <program> [program] ...
|
| +"""
|
| +
|
| +# TODO(csilvers): work with windows paths (\) as well as unix (/)
|
| +
|
| +# This may seem a bit of an end run, but it: doesn't bloat flags, can
|
| +# support python/java/C++, supports older executables, and can be
|
| +# extended to other document formats.
|
| +# Inspired by help2man.
|
| +
|
| +__author__ = 'Dan Christian'
|
| +
|
| +import os
|
| +import re
|
| +import sys
|
| +import stat
|
| +import time
|
| +
|
| +import gflags
|
| +
|
| +_VERSION = '0.1'
|
| +
|
| +
|
| +def _GetDefaultDestDir():
|
| + home = os.environ.get('HOME', '')
|
| + homeman = os.path.join(home, 'man', 'man1')
|
| + if home and os.path.exists(homeman):
|
| + return homeman
|
| + else:
|
| + return os.environ.get('TMPDIR', '/tmp')
|
| +
|
| +FLAGS = gflags.FLAGS
|
| +gflags.DEFINE_string('dest_dir', _GetDefaultDestDir(),
|
| + 'Directory to write resulting manpage to.'
|
| + ' Specify \'-\' for stdout')
|
| +gflags.DEFINE_string('help_flag', '--help',
|
| + 'Option to pass to target program in to get help')
|
| +gflags.DEFINE_integer('v', 0, 'verbosity level to use for output')
|
| +
|
| +_MIN_VALID_USAGE_MSG = 9 # if fewer lines than this, help is suspect
|
| +
|
| +
|
| +class Logging:
|
| + """A super-simple logging class"""
|
| + def error(self, msg): print >>sys.stderr, "ERROR: ", msg
|
| + def warn(self, msg): print >>sys.stderr, "WARNING: ", msg
|
| + def info(self, msg): print msg
|
| + def debug(self, msg): self.vlog(1, msg)
|
| + def vlog(self, level, msg):
|
| + if FLAGS.v >= level: print msg
|
| +logging = Logging()
|
| +
|
| +
|
| +def GetRealPath(filename):
|
| + """Given an executable filename, find in the PATH or find absolute path.
|
| + Args:
|
| + filename An executable filename (string)
|
| + Returns:
|
| + Absolute version of filename.
|
| + None if filename could not be found locally, absolutely, or in PATH
|
| + """
|
| + if os.path.isabs(filename): # already absolute
|
| + return filename
|
| +
|
| + if filename.startswith('./') or filename.startswith('../'): # relative
|
| + return os.path.abspath(filename)
|
| +
|
| + path = os.getenv('PATH', '')
|
| + for directory in path.split(':'):
|
| + tryname = os.path.join(directory, filename)
|
| + if os.path.exists(tryname):
|
| + if not os.path.isabs(directory): # relative directory
|
| + return os.path.abspath(tryname)
|
| + return tryname
|
| + if os.path.exists(filename):
|
| + return os.path.abspath(filename)
|
| + return None # could not determine
|
| +
|
| +class Flag(object):
|
| + """The information about a single flag."""
|
| +
|
| + def __init__(self, flag_desc, help):
|
| + """Create the flag object.
|
| + Args:
|
| + flag_desc The command line forms this could take. (string)
|
| + help The help text (string)
|
| + """
|
| + self.desc = flag_desc # the command line forms
|
| + self.help = help # the help text
|
| + self.default = '' # default value
|
| + self.tips = '' # parsing/syntax tips
|
| +
|
| +
|
| +class ProgramInfo(object):
|
| + """All the information gleaned from running a program with --help."""
|
| +
|
| + # Match a module block start, for python scripts --help
|
| + # "goopy.logging:"
|
| + module_py_re = re.compile(r'(\S.+):$')
|
| + # match the start of a flag listing
|
| + # " -v,--verbosity: Logging verbosity"
|
| + flag_py_re = re.compile(r'\s+(-\S+):\s+(.*)$')
|
| + # " (default: '0')"
|
| + flag_default_py_re = re.compile(r'\s+\(default:\s+\'(.*)\'\)$')
|
| + # " (an integer)"
|
| + flag_tips_py_re = re.compile(r'\s+\((.*)\)$')
|
| +
|
| + # Match a module block start, for c++ programs --help
|
| + # "google/base/commandlineflags"
|
| + module_c_re = re.compile(r'\s+Flags from (\S.+):$')
|
| + # match the start of a flag listing
|
| + # " -v,--verbosity: Logging verbosity"
|
| + flag_c_re = re.compile(r'\s+(-\S+)\s+(.*)$')
|
| +
|
| + # Match a module block start, for java programs --help
|
| + # "com.google.common.flags"
|
| + module_java_re = re.compile(r'\s+Flags for (\S.+):$')
|
| + # match the start of a flag listing
|
| + # " -v,--verbosity: Logging verbosity"
|
| + flag_java_re = re.compile(r'\s+(-\S+)\s+(.*)$')
|
| +
|
| + def __init__(self, executable):
|
| + """Create object with executable.
|
| + Args:
|
| + executable Program to execute (string)
|
| + """
|
| + self.long_name = executable
|
| + self.name = os.path.basename(executable) # name
|
| + # Get name without extension (PAR files)
|
| + (self.short_name, self.ext) = os.path.splitext(self.name)
|
| + self.executable = GetRealPath(executable) # name of the program
|
| + self.output = [] # output from the program. List of lines.
|
| + self.desc = [] # top level description. List of lines
|
| + self.modules = {} # { section_name(string), [ flags ] }
|
| + self.module_list = [] # list of module names in their original order
|
| + self.date = time.localtime(time.time()) # default date info
|
| +
|
| + def Run(self):
|
| + """Run it and collect output.
|
| +
|
| + Returns:
|
| + 1 (true) If everything went well.
|
| + 0 (false) If there were problems.
|
| + """
|
| + if not self.executable:
|
| + logging.error('Could not locate "%s"' % self.long_name)
|
| + return 0
|
| +
|
| + finfo = os.stat(self.executable)
|
| + self.date = time.localtime(finfo[stat.ST_MTIME])
|
| +
|
| + logging.info('Running: %s %s </dev/null 2>&1'
|
| + % (self.executable, FLAGS.help_flag))
|
| + # --help output is often routed to stderr, so we combine with stdout.
|
| + # Re-direct stdin to /dev/null to encourage programs that
|
| + # don't understand --help to exit.
|
| + (child_stdin, child_stdout_and_stderr) = os.popen4(
|
| + [self.executable, FLAGS.help_flag])
|
| + child_stdin.close() # '</dev/null'
|
| + self.output = child_stdout_and_stderr.readlines()
|
| + child_stdout_and_stderr.close()
|
| + if len(self.output) < _MIN_VALID_USAGE_MSG:
|
| + logging.error('Error: "%s %s" returned only %d lines: %s'
|
| + % (self.name, FLAGS.help_flag,
|
| + len(self.output), self.output))
|
| + return 0
|
| + return 1
|
| +
|
| + def Parse(self):
|
| + """Parse program output."""
|
| + (start_line, lang) = self.ParseDesc()
|
| + if start_line < 0:
|
| + return
|
| + if 'python' == lang:
|
| + self.ParsePythonFlags(start_line)
|
| + elif 'c' == lang:
|
| + self.ParseCFlags(start_line)
|
| + elif 'java' == lang:
|
| + self.ParseJavaFlags(start_line)
|
| +
|
| + def ParseDesc(self, start_line=0):
|
| + """Parse the initial description.
|
| +
|
| + This could be Python or C++.
|
| +
|
| + Returns:
|
| + (start_line, lang_type)
|
| + start_line Line to start parsing flags on (int)
|
| + lang_type Either 'python' or 'c'
|
| + (-1, '') if the flags start could not be found
|
| + """
|
| + exec_mod_start = self.executable + ':'
|
| +
|
| + after_blank = 0
|
| + start_line = 0 # ignore the passed-in arg for now (?)
|
| + for start_line in range(start_line, len(self.output)): # collect top description
|
| + line = self.output[start_line].rstrip()
|
| + # Python flags start with 'flags:\n'
|
| + if ('flags:' == line
|
| + and len(self.output) > start_line+1
|
| + and '' == self.output[start_line+1].rstrip()):
|
| + start_line += 2
|
| + logging.debug('Flags start (python): %s' % line)
|
| + return (start_line, 'python')
|
| + # SWIG flags just have the module name followed by colon.
|
| + if exec_mod_start == line:
|
| + logging.debug('Flags start (swig): %s' % line)
|
| + return (start_line, 'python')
|
| + # C++ flags begin after a blank line and with a constant string
|
| + if after_blank and line.startswith(' Flags from '):
|
| + logging.debug('Flags start (c): %s' % line)
|
| + return (start_line, 'c')
|
| + # java flags begin with a constant string
|
| + if line == 'where flags are':
|
| + logging.debug('Flags start (java): %s' % line)
|
| + start_line += 2 # skip "Standard flags:"
|
| + return (start_line, 'java')
|
| +
|
| + logging.debug('Desc: %s' % line)
|
| + self.desc.append(line)
|
| + after_blank = (line == '')
|
| + else:
|
| + logging.warn('Never found the start of the flags section for "%s"!'
|
| + % self.long_name)
|
| + return (-1, '')
|
| +
|
| + def ParsePythonFlags(self, start_line=0):
|
| + """Parse python/swig style flags."""
|
| + modname = None # name of current module
|
| + modlist = []
|
| + flag = None
|
| + for line_num in range(start_line, len(self.output)): # collect flags
|
| + line = self.output[line_num].rstrip()
|
| + if not line: # blank
|
| + continue
|
| +
|
| + mobj = self.module_py_re.match(line)
|
| + if mobj: # start of a new module
|
| + modname = mobj.group(1)
|
| + logging.debug('Module: %s' % line)
|
| + if flag:
|
| + modlist.append(flag)
|
| + self.module_list.append(modname)
|
| + self.modules.setdefault(modname, [])
|
| + modlist = self.modules[modname]
|
| + flag = None
|
| + continue
|
| +
|
| + mobj = self.flag_py_re.match(line)
|
| + if mobj: # start of a new flag
|
| + if flag:
|
| + modlist.append(flag)
|
| + logging.debug('Flag: %s' % line)
|
| + flag = Flag(mobj.group(1), mobj.group(2))
|
| + continue
|
| +
|
| + if not flag: # continuation of a flag
|
| + logging.error('Flag info, but no current flag "%s"' % line)
|
| + mobj = self.flag_default_py_re.match(line)
|
| + if mobj: # (default: '...')
|
| + flag.default = mobj.group(1)
|
| + logging.debug('Fdef: %s' % line)
|
| + continue
|
| + mobj = self.flag_tips_py_re.match(line)
|
| + if mobj: # (tips)
|
| + flag.tips = mobj.group(1)
|
| + logging.debug('Ftip: %s' % line)
|
| + continue
|
| + if flag and flag.help:
|
| + flag.help += line # multiflags tack on an extra line
|
| + else:
|
| + logging.info('Extra: %s' % line)
|
| + if flag:
|
| + modlist.append(flag)
|
| +
|
| + def ParseCFlags(self, start_line=0):
|
| + """Parse C style flags."""
|
| + modname = None # name of current module
|
| + modlist = []
|
| + flag = None
|
| + for line_num in range(start_line, len(self.output)): # collect flags
|
| + line = self.output[line_num].rstrip()
|
| + if not line: # blank lines terminate flags
|
| + if flag: # save last flag
|
| + modlist.append(flag)
|
| + flag = None
|
| + continue
|
| +
|
| + mobj = self.module_c_re.match(line)
|
| + if mobj: # start of a new module
|
| + modname = mobj.group(1)
|
| + logging.debug('Module: %s' % line)
|
| + if flag:
|
| + modlist.append(flag)
|
| + self.module_list.append(modname)
|
| + self.modules.setdefault(modname, [])
|
| + modlist = self.modules[modname]
|
| + flag = None
|
| + continue
|
| +
|
| + mobj = self.flag_c_re.match(line)
|
| + if mobj: # start of a new flag
|
| + if flag: # save last flag
|
| + modlist.append(flag)
|
| + logging.debug('Flag: %s' % line)
|
| + flag = Flag(mobj.group(1), mobj.group(2))
|
| + continue
|
| +
|
| + # append to flag help. type and default are part of the main text
|
| + if flag:
|
| + flag.help += ' ' + line.strip()
|
| + else:
|
| + logging.info('Extra: %s' % line)
|
| + if flag:
|
| + modlist.append(flag)
|
| +
|
| + def ParseJavaFlags(self, start_line=0):
|
| + """Parse Java style flags (com.google.common.flags)."""
|
| + # The java flags prints starts with a "Standard flags" "module"
|
| + # that doesn't follow the standard module syntax.
|
| + modname = 'Standard flags' # name of current module
|
| + self.module_list.append(modname)
|
| + self.modules.setdefault(modname, [])
|
| + modlist = self.modules[modname]
|
| + flag = None
|
| +
|
| + for line_num in range(start_line, len(self.output)): # collect flags
|
| + line = self.output[line_num].rstrip()
|
| + logging.vlog(2, 'Line: "%s"' % line)
|
| + if not line: # blank lines terminate module
|
| + if flag: # save last flag
|
| + modlist.append(flag)
|
| + flag = None
|
| + continue
|
| +
|
| + mobj = self.module_java_re.match(line)
|
| + if mobj: # start of a new module
|
| + modname = mobj.group(1)
|
| + logging.debug('Module: %s' % line)
|
| + if flag:
|
| + modlist.append(flag)
|
| + self.module_list.append(modname)
|
| + self.modules.setdefault(modname, [])
|
| + modlist = self.modules[modname]
|
| + flag = None
|
| + continue
|
| +
|
| + mobj = self.flag_java_re.match(line)
|
| + if mobj: # start of a new flag
|
| + if flag: # save last flag
|
| + modlist.append(flag)
|
| + logging.debug('Flag: %s' % line)
|
| + flag = Flag(mobj.group(1), mobj.group(2))
|
| + continue
|
| +
|
| + # append to flag help. type and default are part of the main text
|
| + if flag:
|
| + flag.help += ' ' + line.strip()
|
| + else:
|
| + logging.info('Extra: %s' % line)
|
| + if flag:
|
| + modlist.append(flag)
|
| +
|
| + def Filter(self):
|
| + """Filter parsed data to create derived fields."""
|
| + if not self.desc:
|
| + self.short_desc = ''
|
| + return
|
| +
|
| + for i in range(len(self.desc)): # replace full path with name
|
| + if self.desc[i].find(self.executable) >= 0:
|
| + self.desc[i] = self.desc[i].replace(self.executable, self.name)
|
| +
|
| + self.short_desc = self.desc[0]
|
| + word_list = self.short_desc.split(' ')
|
| + all_names = [ self.name, self.short_name, ]
|
| + # Since the short_desc is always listed right after the name,
|
| + # trim it from the short_desc
|
| + while word_list and (word_list[0] in all_names
|
| + or word_list[0].lower() in all_names):
|
| + del word_list[0]
|
| + self.short_desc = '' # signal need to reconstruct
|
| + if not self.short_desc and word_list:
|
| + self.short_desc = ' '.join(word_list)
|
| +
|
| +
|
| +class GenerateDoc(object):
|
| + """Base class to output flags information."""
|
| +
|
| + def __init__(self, proginfo, directory='.'):
|
| + """Create base object.
|
| + Args:
|
| + proginfo A ProgramInfo object
|
| + directory Directory to write output into
|
| + """
|
| + self.info = proginfo
|
| + self.dirname = directory
|
| +
|
| + def Output(self):
|
| + """Output all sections of the page."""
|
| + self.Open()
|
| + self.Header()
|
| + self.Body()
|
| + self.Footer()
|
| +
|
| + def Open(self): raise NotImplementedError # define in subclass
|
| + def Header(self): raise NotImplementedError # define in subclass
|
| + def Body(self): raise NotImplementedError # define in subclass
|
| + def Footer(self): raise NotImplementedError # define in subclass
|
| +
|
| +
|
| +class GenerateMan(GenerateDoc):
|
| + """Output a man page."""
|
| +
|
| + def __init__(self, proginfo, directory='.'):
|
| + """Create base object.
|
| + Args:
|
| + proginfo A ProgramInfo object
|
| + directory Directory to write output into
|
| + """
|
| + GenerateDoc.__init__(self, proginfo, directory)
|
| +
|
| + def Open(self):
|
| + if self.dirname == '-':
|
| + logging.info('Writing to stdout')
|
| + self.fp = sys.stdout
|
| + else:
|
| + self.file_path = '%s.1' % os.path.join(self.dirname, self.info.name)
|
| + logging.info('Writing: %s' % self.file_path)
|
| + self.fp = open(self.file_path, 'w')
|
| +
|
| + def Header(self):
|
| + self.fp.write(
|
| + '.\\" DO NOT MODIFY THIS FILE! It was generated by gflags2man %s\n'
|
| + % _VERSION)
|
| + self.fp.write(
|
| + '.TH %s "1" "%s" "%s" "User Commands"\n'
|
| + % (self.info.name, time.strftime('%x', self.info.date), self.info.name))
|
| + self.fp.write(
|
| + '.SH NAME\n%s \\- %s\n' % (self.info.name, self.info.short_desc))
|
| + self.fp.write(
|
| + '.SH SYNOPSIS\n.B %s\n[\\fIFLAGS\\fR]...\n' % self.info.name)
|
| +
|
| + def Body(self):
|
| + self.fp.write(
|
| + '.SH DESCRIPTION\n.\\" Add any additional description here\n.PP\n')
|
| + for ln in self.info.desc:
|
| + self.fp.write('%s\n' % ln)
|
| + self.fp.write(
|
| + '.SH OPTIONS\n')
|
| + # This shows flags in the original order
|
| + for modname in self.info.module_list:
|
| + if modname.find(self.info.executable) >= 0:
|
| + mod = modname.replace(self.info.executable, self.info.name)
|
| + else:
|
| + mod = modname
|
| + self.fp.write('\n.P\n.I %s\n' % mod)
|
| + for flag in self.info.modules[modname]:
|
| + help_string = flag.help
|
| + if flag.default or flag.tips:
|
| + help_string += '\n.br\n'
|
| + if flag.default:
|
| + help_string += ' (default: \'%s\')' % flag.default
|
| + if flag.tips:
|
| + help_string += ' (%s)' % flag.tips
|
| + self.fp.write(
|
| + '.TP\n%s\n%s\n' % (flag.desc, help_string))
|
| +
|
| + def Footer(self):
|
| + self.fp.write(
|
| + '.SH COPYRIGHT\nCopyright \(co %s Google.\n'
|
| + % time.strftime('%Y', self.info.date))
|
| + self.fp.write('Gflags2man created this page from "%s %s" output.\n'
|
| + % (self.info.name, FLAGS.help_flag))
|
| + self.fp.write('\nGflags2man was written by Dan Christian. '
|
| + ' Note that the date on this'
|
| + ' page is the modification date of %s.\n' % self.info.name)
|
| +
|
| +
|
| +def main(argv):
|
| + argv = FLAGS(argv) # handles help as well
|
| + if len(argv) <= 1:
|
| + print >>sys.stderr, __doc__
|
| + print >>sys.stderr, "flags:"
|
| + print >>sys.stderr, str(FLAGS)
|
| + return 1
|
| +
|
| + for arg in argv[1:]:
|
| + prog = ProgramInfo(arg)
|
| + if not prog.Run():
|
| + continue
|
| + prog.Parse()
|
| + prog.Filter()
|
| + doc = GenerateMan(prog, FLAGS.dest_dir)
|
| + doc.Output()
|
| + return 0
|
| +
|
| +if __name__ == '__main__':
|
| + main(sys.argv)
|
|
|
| Property changes on: tools/nixysa/third_party/gflags-1.0/python/gflags2man.py
|
| ___________________________________________________________________
|
| Added: svn:executable
|
| + *
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|