12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright (c) 2006, Google Inc.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# All rights reserved.
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Redistribution and use in source and binary forms, with or without
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# modification, are permitted provided that the following conditions are
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# met:
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Redistributions of source code must retain the above copyright
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# notice, this list of conditions and the following disclaimer.
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Redistributions in binary form must reproduce the above
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# copyright notice, this list of conditions and the following disclaimer
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# in the documentation and/or other materials provided with the
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distribution.
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Neither the name of Google Inc. nor the names of its
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# contributors may be used to endorse or promote products derived from
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# this software without specific prior written permission.
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""gflags2man runs a Google flags base program and generates a man page.
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
352da489cd246702bee5938545b18a6f710ed214bcJamie GennisRun the program, parse the output, and then format that into a man
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennispage.
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
382da489cd246702bee5938545b18a6f710ed214bcJamie GennisUsage:
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  gflags2man <program> [program] ...
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# TODO(csilvers): work with windows paths (\) as well as unix (/)
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# This may seem a bit of an end run, but it:  doesn't bloat flags, can
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# support python/java/C++, supports older executables, and can be
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# extended to other document formats.
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Inspired by help2man.
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport os
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport sys
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport stat
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport time
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport gflags
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_VERSION = '0.1'
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _GetDefaultDestDir():
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  home = os.environ.get('HOME', '')
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  homeman = os.path.join(home, 'man', 'man1')
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if home and os.path.exists(homeman):
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return homeman
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  else:
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return os.environ.get('TMPDIR', '/tmp')
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
702da489cd246702bee5938545b18a6f710ed214bcJamie GennisFLAGS = gflags.FLAGS
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennisgflags.DEFINE_string('dest_dir', _GetDefaultDestDir(),
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'Directory to write resulting manpage to.'
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    ' Specify \'-\' for stdout')
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennisgflags.DEFINE_string('help_flag', '--help',
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'Option to pass to target program in to get help')
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennisgflags.DEFINE_integer('v', 0, 'verbosity level to use for output')
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_MIN_VALID_USAGE_MSG = 9         # if fewer lines than this, help is suspect
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Logging:
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """A super-simple logging class"""
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def error(self, msg): print >>sys.stderr, "ERROR: ", msg
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def warn(self, msg): print >>sys.stderr, "WARNING: ", msg
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def info(self, msg): print msg
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def debug(self, msg): self.vlog(1, msg)
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def vlog(self, level, msg):
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if FLAGS.v >= level: print msg
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennislogging = Logging()
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass App:
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def usage(self, shorthelp=0):
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    print >>sys.stderr, __doc__
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    print >>sys.stderr, "flags:"
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    print >>sys.stderr, str(FLAGS)
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def run(self):
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    main(sys.argv)
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennisapp = App()
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef GetRealPath(filename):
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Given an executable filename, find in the PATH or find absolute path.
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    filename  An executable filename (string)
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Absolute version of filename.
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    None if filename could not be found locally, absolutely, or in PATH
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if os.path.isabs(filename):                # already absolute
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return filename
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if filename.startswith('./') or  filename.startswith('../'): # relative
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return os.path.abspath(filename)
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  path = os.getenv('PATH', '')
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  for directory in path.split(':'):
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    tryname = os.path.join(directory, filename)
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if os.path.exists(tryname):
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not os.path.isabs(directory):  # relative directory
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return os.path.abspath(tryname)
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return tryname
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if os.path.exists(filename):
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return os.path.abspath(filename)
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return None                         # could not determine
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Flag(object):
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """The information about a single flag."""
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, flag_desc, help):
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Create the flag object.
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      flag_desc  The command line forms this could take. (string)
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      help       The help text (string)
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.desc = flag_desc               # the command line forms
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.help = help                    # the help text
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.default = ''                   # default value
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.tips = ''                      # parsing/syntax tips
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass ProgramInfo(object):
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """All the information gleaned from running a program with --help."""
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Match a module block start, for python scripts --help
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # "goopy.logging:"
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  module_py_re = re.compile(r'(\S.+):$')
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # match the start of a flag listing
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # " -v,--verbosity:  Logging verbosity"
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  flag_py_re         = re.compile(r'\s+(-\S+):\s+(.*)$')
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # "   (default: '0')"
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  flag_default_py_re = re.compile(r'\s+\(default:\s+\'(.*)\'\)$')
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # "   (an integer)"
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  flag_tips_py_re    = re.compile(r'\s+\((.*)\)$')
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Match a module block start, for c++ programs --help
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # "google/base/commandlineflags":
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  module_c_re = re.compile(r'\s+Flags from (\S.+):$')
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # match the start of a flag listing
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # " -v,--verbosity:  Logging verbosity"
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  flag_c_re         = re.compile(r'\s+(-\S+)\s+(.*)$')
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Match a module block start, for java programs --help
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # "com.google.common.flags"
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  module_java_re = re.compile(r'\s+Flags for (\S.+):$')
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # match the start of a flag listing
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # " -v,--verbosity:  Logging verbosity"
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  flag_java_re         = re.compile(r'\s+(-\S+)\s+(.*)$')
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, executable):
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Create object with executable.
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      executable  Program to execute (string)
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.long_name = executable
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.name = os.path.basename(executable)  # name
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Get name without extension (PAR files)
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    (self.short_name, self.ext) = os.path.splitext(self.name)
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.executable = GetRealPath(executable)  # name of the program
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.output = []          # output from the program.  List of lines.
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.desc = []            # top level description.  List of lines
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.modules = {}         # { section_name(string), [ flags ] }
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.module_list = []     # list of module names in their original order
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.date = time.localtime(time.time())   # default date info
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Run(self):
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Run it and collect output.
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      1 (true)   If everything went well.
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      0 (false)  If there were problems.
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not self.executable:
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.error('Could not locate "%s"' % self.long_name)
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return 0
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    finfo = os.stat(self.executable)
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.date = time.localtime(finfo[stat.ST_MTIME])
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logging.info('Running: %s %s </dev/null 2>&1'
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                 % (self.executable, FLAGS.help_flag))
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # --help output is often routed to stderr, so we combine with stdout.
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Re-direct stdin to /dev/null to encourage programs that
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # don't understand --help to exit.
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    (child_stdin, child_stdout_and_stderr) = os.popen4(
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      [self.executable, FLAGS.help_flag])
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    child_stdin.close()       # '</dev/null'
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.output = child_stdout_and_stderr.readlines()
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    child_stdout_and_stderr.close()
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if len(self.output) < _MIN_VALID_USAGE_MSG:
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.error('Error: "%s %s" returned only %d lines: %s'
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    % (self.name, FLAGS.help_flag,
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                       len(self.output), self.output))
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return 0
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return 1
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Parse(self):
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Parse program output."""
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    (start_line, lang) = self.ParseDesc()
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if start_line < 0:
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if 'python' == lang:
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.ParsePythonFlags(start_line)
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif 'c' == lang:
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.ParseCFlags(start_line)
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif 'java' == lang:
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.ParseJavaFlags(start_line)
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def ParseDesc(self, start_line=0):
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Parse the initial description.
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    This could be Python or C++.
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      (start_line, lang_type)
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        start_line  Line to start parsing flags on (int)
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        lang_type   Either 'python' or 'c'
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis       (-1, '')  if the flags start could not be found
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    exec_mod_start = self.executable + ':'
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    after_blank = 0
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    start_line = 0             # ignore the passed-in arg for now (?)
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for start_line in range(start_line, len(self.output)): # collect top description
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      line = self.output[start_line].rstrip()
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Python flags start with 'flags:\n'
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if ('flags:' == line
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          and len(self.output) > start_line+1
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          and '' == self.output[start_line+1].rstrip()):
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        start_line += 2
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flags start (python): %s' % line)
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return (start_line, 'python')
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # SWIG flags just have the module name followed by colon.
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if exec_mod_start == line:
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flags start (swig): %s' % line)
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return (start_line, 'python')
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # C++ flags begin after a blank line and with a constant string
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if after_blank and line.startswith('  Flags from '):
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flags start (c): %s' % line)
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return (start_line, 'c')
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # java flags begin with a constant string
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if line == 'where flags are':
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flags start (java): %s' % line)
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        start_line += 2                        # skip "Standard flags:"
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return (start_line, 'java')
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.debug('Desc: %s' % line)
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.desc.append(line)
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      after_blank = (line == '')
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.warn('Never found the start of the flags section for "%s"!'
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                   % self.long_name)
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return (-1, '')
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def ParsePythonFlags(self, start_line=0):
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Parse python/swig style flags."""
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    modname = None                      # name of current module
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    modlist = []
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    flag = None
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for line_num in range(start_line, len(self.output)): # collect flags
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      line = self.output[line_num].rstrip()
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not line:                      # blank
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.module_py_re.match(line)
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # start of a new module
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        modname = mobj.group(1)
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Module: %s' % line)
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.module_list.append(modname)
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.modules.setdefault(modname, [])
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        modlist = self.modules[modname]
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag = None
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.flag_py_re.match(line)
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # start of a new flag
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flag: %s' % line)
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag = Flag(mobj.group(1),  mobj.group(2))
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not flag:                    # continuation of a flag
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.error('Flag info, but no current flag "%s"' % line)
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.flag_default_py_re.match(line)
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # (default: '...')
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag.default = mobj.group(1)
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Fdef: %s' % line)
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.flag_tips_py_re.match(line)
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # (tips)
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag.tips = mobj.group(1)
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Ftip: %s' % line)
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if flag and flag.help:
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag.help += line              # multiflags tack on an extra line
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.info('Extra: %s' % line)
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if flag:
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      modlist.append(flag)
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def ParseCFlags(self, start_line=0):
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Parse C style flags."""
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    modname = None                      # name of current module
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    modlist = []
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    flag = None
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for line_num in range(start_line, len(self.output)):  # collect flags
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      line = self.output[line_num].rstrip()
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not line:                      # blank lines terminate flags
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:                        # save last flag
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          flag = None
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.module_c_re.match(line)
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # start of a new module
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        modname = mobj.group(1)
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Module: %s' % line)
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.module_list.append(modname)
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.modules.setdefault(modname, [])
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        modlist = self.modules[modname]
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag = None
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.flag_c_re.match(line)
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # start of a new flag
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:                        # save last flag
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flag: %s' % line)
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag = Flag(mobj.group(1),  mobj.group(2))
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # append to flag help.  type and default are part of the main text
3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if flag:
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag.help += ' ' + line.strip()
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.info('Extra: %s' % line)
3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if flag:
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      modlist.append(flag)
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def ParseJavaFlags(self, start_line=0):
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Parse Java style flags (com.google.common.flags)."""
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # The java flags prints starts with a "Standard flags" "module"
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # that doesn't follow the standard module syntax.
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    modname = 'Standard flags'          # name of current module
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.module_list.append(modname)
3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.modules.setdefault(modname, [])
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    modlist = self.modules[modname]
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    flag = None
3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for line_num in range(start_line, len(self.output)): # collect flags
3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      line = self.output[line_num].rstrip()
3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.vlog(2, 'Line: "%s"' % line)
3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not line:                      # blank lines terminate module
3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:                        # save last flag
3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          flag = None
3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.module_java_re.match(line)
3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # start of a new module
3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        modname = mobj.group(1)
3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Module: %s' % line)
3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:
3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.module_list.append(modname)
3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.modules.setdefault(modname, [])
3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        modlist = self.modules[modname]
3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag = None
3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      mobj = self.flag_java_re.match(line)
3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if mobj:                          # start of a new flag
3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag:                        # save last flag
3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          modlist.append(flag)
3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.debug('Flag: %s' % line)
4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag = Flag(mobj.group(1),  mobj.group(2))
4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        continue
4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # append to flag help.  type and default are part of the main text
4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if flag:
4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        flag.help += ' ' + line.strip()
4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.info('Extra: %s' % line)
4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if flag:
4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      modlist.append(flag)
4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Filter(self):
4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Filter parsed data to create derived fields."""
4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not self.desc:
4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.short_desc = ''
4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return
4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for i in range(len(self.desc)):   # replace full path with name
4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self.desc[i].find(self.executable) >= 0:
4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.desc[i] = self.desc[i].replace(self.executable, self.name)
4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.short_desc = self.desc[0]
4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    word_list = self.short_desc.split(' ')
4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    all_names = [ self.name, self.short_name, ]
4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Since the short_desc is always listed right after the name,
4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    #  trim it from the short_desc
4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    while word_list and (word_list[0] in all_names
4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                         or word_list[0].lower() in all_names):
4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      del word_list[0]
4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.short_desc = ''              # signal need to reconstruct
4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not self.short_desc and word_list:
4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.short_desc = ' '.join(word_list)
4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass GenerateDoc(object):
4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Base class to output flags information."""
4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, proginfo, directory='.'):
4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Create base object.
4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      proginfo   A ProgramInfo object
4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      directory  Directory to write output into
4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.info = proginfo
4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.dirname = directory
4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Output(self):
4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Output all sections of the page."""
4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.Open()
4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.Header()
4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.Body()
4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.Footer()
4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Open(self): raise NotImplementedError    # define in subclass
4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Header(self): raise NotImplementedError  # define in subclass
4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Body(self): raise NotImplementedError    # define in subclass
4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Footer(self): raise NotImplementedError  # define in subclass
4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass GenerateMan(GenerateDoc):
4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Output a man page."""
4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, proginfo, directory='.'):
4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Create base object.
4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      proginfo   A ProgramInfo object
4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      directory  Directory to write output into
4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    GenerateDoc.__init__(self, proginfo, directory)
4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Open(self):
4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.dirname == '-':
4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.info('Writing to stdout')
4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.fp = sys.stdout
4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.file_path = '%s.1' % os.path.join(self.dirname, self.info.name)
4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      logging.info('Writing: %s' % self.file_path)
4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.fp = open(self.file_path, 'w')
4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Header(self):
4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.\\" DO NOT MODIFY THIS FILE!  It was generated by gflags2man %s\n'
4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      % _VERSION)
4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.TH %s "1" "%s" "%s" "User Commands"\n'
4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      % (self.info.name, time.strftime('%x', self.info.date), self.info.name))
4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.SH NAME\n%s \\- %s\n' % (self.info.name, self.info.short_desc))
4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.SH SYNOPSIS\n.B %s\n[\\fIFLAGS\\fR]...\n' % self.info.name)
4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Body(self):
4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.SH DESCRIPTION\n.\\" Add any additional description here\n.PP\n')
4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for ln in self.info.desc:
4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.fp.write('%s\n' % ln)
4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.SH OPTIONS\n')
4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # This shows flags in the original order
4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for modname in self.info.module_list:
5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if modname.find(self.info.executable) >= 0:
5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        mod = modname.replace(self.info.executable, self.info.name)
5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        mod = modname
5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.fp.write('\n.P\n.I %s\n' % mod)
5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for flag in self.info.modules[modname]:
5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        help_string = flag.help
5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag.default or flag.tips:
5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          help_string += '\n.br\n'
5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag.default:
5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          help_string += '  (default: \'%s\')' % flag.default
5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if flag.tips:
5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          help_string += '  (%s)' % flag.tips
5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.fp.write(
5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          '.TP\n%s\n%s\n' % (flag.desc, help_string))
5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Footer(self):
5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write(
5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      '.SH COPYRIGHT\nCopyright \(co %s Google.\n'
5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      % time.strftime('%Y', self.info.date))
5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write('Gflags2man created this page from "%s %s" output.\n'
5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  % (self.info.name, FLAGS.help_flag))
5222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.fp.write('\nGflags2man was written by Dan Christian. '
5232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  ' Note that the date on this'
5242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  ' page is the modification date of %s.\n' % self.info.name)
5252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5272da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef main(argv):
5282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  argv = FLAGS(argv)           # handles help as well
5292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if len(argv) <= 1:
5302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    app.usage(shorthelp=1)
5312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return 1
5322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  for arg in argv[1:]:
5342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    prog = ProgramInfo(arg)
5352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not prog.Run():
5362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      continue
5372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    prog.Parse()
5382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    prog.Filter()
5392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    doc = GenerateMan(prog, FLAGS.dest_dir)
5402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    doc.Output()
5412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return 0
5422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5432da489cd246702bee5938545b18a6f710ed214bcJamie Gennisif __name__ == '__main__':
5442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  app.run()
545