meta-flow: Correctly set destination MAC in mf_set_flow_value().
[sliver-openvswitch.git] / ovsdb / ovsdb-doc.in
index 90de452..26fba3e 100755 (executable)
@@ -16,9 +16,9 @@ argv0 = sys.argv[0]
 def textToNroff(s, font=r'\fR'):
     def escape(match):
         c = match.group(0)
-        if c == '-':
-            if font == r'\fB':
-                return r'\-'
+        if c.startswith('-'):
+            if c != '-' or font == r'\fB':
+                return '\\' + c
             else:
                 return '-'
         if c == '\\':
@@ -31,7 +31,7 @@ def textToNroff(s, font=r'\fR'):
             raise error.Error("bad escape")
 
     # Escape - \ " ' as needed by nroff.
-    s = re.sub('([-"\'\\\\])', escape, s)
+    s = re.sub('(-[0-9]|[-"\'\\\\])', escape, s)
     if s.startswith('.'):
         s = '\\' + s
     return s
@@ -43,7 +43,7 @@ def inlineXmlToNroff(node, font):
     if node.nodeType == node.TEXT_NODE:
         return textToNroff(node.data, font)
     elif node.nodeType == node.ELEMENT_NODE:
-        if node.tagName == 'code' or node.tagName == 'em':
+        if node.tagName in ['code', 'em', 'option']:
             s = r'\fB'
             for child in node.childNodes:
                 s += inlineXmlToNroff(child, r'\fB')
@@ -52,12 +52,14 @@ def inlineXmlToNroff(node, font):
             s = r'\fB'
             if node.hasAttribute('column'):
                 s += node.attributes['column'].nodeValue
+                if node.hasAttribute('key'):
+                    s += ':' + node.attributes['key'].nodeValue
             elif node.hasAttribute('table'):
                 s += node.attributes['table'].nodeValue
             elif node.hasAttribute('group'):
                 s += node.attributes['group'].nodeValue
             else:
-                raise error.Error("'ref' lacks column and table attributes")
+                raise error.Error("'ref' lacks required attributes: %s" % node.attributes.keys())
             return s + font
         elif node.tagName == 'var':
             s = r'\fI'
@@ -76,17 +78,23 @@ def blockXmlToNroff(nodes, para='.PP'):
             s += textToNroff(node.data)
             s = s.lstrip()
         elif node.nodeType == node.ELEMENT_NODE:
-            if node.tagName == 'ul':
+            if node.tagName in ['ul', 'ol']:
                 if s != "":
                     s += "\n"
                 s += ".RS\n"
+                i = 0
                 for liNode in node.childNodes:
                     if (liNode.nodeType == node.ELEMENT_NODE
                         and liNode.tagName == 'li'):
-                        s += ".IP \\(bu\n" + blockXmlToNroff(liNode.childNodes, ".IP")
+                        i += 1
+                        if node.tagName == 'ul':
+                            s += ".IP \\(bu\n"
+                        else:
+                            s += ".IP %d. .25in\n" % i
+                        s += blockXmlToNroff(liNode.childNodes, ".IP")
                     elif (liNode.nodeType != node.TEXT_NODE
                           or not liNode.data.isspace()):
-                        raise error.Error("<ul> element may only have <li> children")
+                        raise error.Error("<%s> element may only have <li> children" % node.tagName)
                 s += ".RE\n"
             elif node.tagName == 'dl':
                 if s != "":
@@ -117,6 +125,15 @@ def blockXmlToNroff(nodes, para='.PP'):
                         s += "\n"
                     s += para + "\n"
                 s += blockXmlToNroff(node.childNodes, para)
+            elif node.tagName in ('h1', 'h2', 'h3'):
+                if s != "":
+                    if not s.endswith("\n"):
+                        s += "\n"
+                nroffTag = {'h1': 'SH', 'h2': 'SS', 'h3': 'ST'}[node.tagName]
+                s += ".%s " % nroffTag
+                for child_node in node.childNodes:
+                    s += inlineXmlToNroff(child_node, r'\fR')
+                s += "\n"
             else:
                 s += inlineXmlToNroff(node, r'\fR')
         else:
@@ -127,17 +144,14 @@ def blockXmlToNroff(nodes, para='.PP'):
 
 def typeAndConstraintsToNroff(column):
     type = column.type.toEnglish(escapeNroffLiteral)
-    constraints = column.type.constraintsToEnglish(escapeNroffLiteral)
+    constraints = column.type.constraintsToEnglish(escapeNroffLiteral,
+                                                   textToNroff)
     if constraints:
         type += ", " + constraints
+    if column.unique:
+        type += " (must be unique within table)"
     return type
 
-def columnToNroff(columnName, column, node):
-    type = typeAndConstraintsToNroff(column)
-    s = '.IP "\\fB%s\\fR: %s"\n' % (columnName, type)
-    s += blockXmlToNroff(node.childNodes, '.IP') + "\n"
-    return s
-
 def columnGroupToNroff(table, groupXml):
     introNodes = []
     columnNodes = []
@@ -146,6 +160,10 @@ def columnGroupToNroff(table, groupXml):
             and node.tagName in ('column', 'group')):
             columnNodes += [node]
         else:
+            if (columnNodes
+                and not (node.nodeType == node.TEXT_NODE
+                         and node.data.isspace())):
+                raise error.Error("text follows <column> or <group> inside <group>: %s" % node)
             introNodes += [node]
 
     summary = []
@@ -153,10 +171,44 @@ def columnGroupToNroff(table, groupXml):
     body = ''
     for node in columnNodes:
         if node.tagName == 'column':
-            columnName = node.attributes['name'].nodeValue
-            column = table.columns[columnName]
-            body += columnToNroff(columnName, column, node)
-            summary += [('column', columnName, column)]
+            name = node.attributes['name'].nodeValue
+            column = table.columns[name]
+            if node.hasAttribute('key'):
+                key = node.attributes['key'].nodeValue
+                if node.hasAttribute('type'):
+                    type_string = node.attributes['type'].nodeValue
+                    type_json = ovs.json.from_string(str(type_string))
+                    if type(type_json) in (str, unicode):
+                        raise error.Error("%s %s:%s has invalid 'type': %s" 
+                                          % (table.name, name, key, type_json))
+                    type_ = ovs.db.types.BaseType.from_json(type_json)
+                else:
+                    type_ = column.type.value
+
+                nameNroff = "%s : %s" % (name, key)
+                typeNroff = "optional %s" % column.type.value.toEnglish(
+                    escapeNroffLiteral)
+                if (column.type.value.type == ovs.db.types.StringType and
+                    type_.type == ovs.db.types.BooleanType):
+                    # This is a little more explicit and helpful than
+                    # "containing a boolean"
+                    typeNroff += r", either \fBtrue\fR or \fBfalse\fR"
+                else:
+                    if type_.type != column.type.value.type:
+                        type_english = type_.toEnglish(escapeNroffLiteral)
+                        if type_english[0] in 'aeiou':
+                            typeNroff += ", containing an %s" % type_english
+                        else:
+                            typeNroff += ", containing a %s" % type_english
+                    constraints = type_.constraintsToEnglish(escapeNroffLiteral)
+                    if constraints:
+                        typeNroff += ", %s" % constraints
+            else:
+                nameNroff = name
+                typeNroff = typeAndConstraintsToNroff(column)
+            body += '.IP "\\fB%s\\fR: %s"\n' % (nameNroff, typeNroff)
+            body += blockXmlToNroff(node.childNodes, '.IP') + "\n"
+            summary += [('column', nameNroff, typeNroff)]
         elif node.tagName == 'group':
             title = node.attributes["title"].nodeValue
             subSummary, subIntro, subBody = columnGroupToNroff(table, node)
@@ -171,19 +223,11 @@ def tableSummaryToNroff(summary, level=0):
     s = ""
     for type, name, arg in summary:
         if type == 'column':
-            
-            s += "%s\\fB%s\\fR\tT{\n%s\nT}\n" % (
-                r'\ \ ' * level, name, typeAndConstraintsToNroff(arg))
+            s += ".TQ %.2fin\n\\fB%s\\fR\n%s\n" % (3 - level * .25, name, arg)
         else:
-            if s != "":
-                s += "_\n"
-            s += """.T&
-li | s
-l | l.
-%s%s
-_
-""" % (r'\ \ ' * level, name)
+            s += ".TQ .25in\n\\fI%s:\\fR\n.RS .25in\n" % name
             s += tableSummaryToNroff(arg, level + 1)
+            s += ".RE\n"
     return s
 
 def tableToNroff(schema, tableXml):
@@ -191,41 +235,30 @@ def tableToNroff(schema, tableXml):
     table = schema.tables[tableName]
 
     s = """.bp
-.SS "%s Table"
+.SH "%s TABLE"
 """ % tableName
     summary, intro, body = columnGroupToNroff(table, tableXml)
     s += intro
-
-    s += r"""
-.sp
-.ce 1
-\fB%s\fR Table Columns:
-.TS
-center box;
-l | l.
-Column Type
-=
-""" % tableName
+    s += '.SS "Summary:\n'
     s += tableSummaryToNroff(summary)
-    s += ".TE\n"
-
+    s += '.SS "Details:\n'
     s += body
     return s
 
 def docsToNroff(schemaFile, xmlFile, erFile, title=None):
     schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile))
     doc = xml.dom.minidom.parse(xmlFile).documentElement
-    
+
     schemaDate = os.stat(schemaFile).st_mtime
     xmlDate = os.stat(xmlFile).st_mtime
     d = date.fromtimestamp(max(schemaDate, xmlDate))
-    
+
     if title == None:
         title = schema.name
 
-    # Putting '\" pt as the first line tells "man" that the manpage
-    # needs to be preprocessed by "pic" and "tbl".
-    s = r''''\" pt
+    # Putting '\" p as the first line tells "man" that the manpage
+    # needs to be preprocessed by "pic".
+    s = r''''\" p
 .TH %s 5 "%s" "Open vSwitch" "Open vSwitch Manual"
 .\" -*- nroff -*-
 .de TQ
@@ -239,9 +272,9 @@ def docsToNroff(schemaFile, xmlFile, erFile, title=None):
 .  I "\\$1"
 .  RE
 ..
-''' % (title, d.strftime("%B %Y"))
-
-    s += '.SH "%s DATABASE"\n' % schema.name
+.SH NAME
+%s \- %s database schema
+''' % (title, d.strftime("%B %Y"), textToNroff(title), schema.name)
 
     tables = ""
     introNodes = []
@@ -262,38 +295,45 @@ def docsToNroff(schemaFile, xmlFile, erFile, title=None):
             introNodes += [dbNode]
 
     s += blockXmlToNroff(introNodes) + "\n"
-    tableSummary = r"""
-.sp
-.ce 1
-\fB%s\fR Database Tables:
-.TS
-center box;
-l | l
-lb | l.
-Table  Purpose
-=
+
+    s += r"""
+.SH "TABLE SUMMARY"
+.PP
+The following list summarizes the purpose of each of the tables in the
+\fB%s\fR database.  Each table is described in more detail on a later
+page.
+.IP "Table" 1in
+Purpose
 """ % schema.name
     for name, title in summary:
-        tableSummary += "%s\t%s\n" % (name, textToNroff(title))
-    tableSummary += '.TE\n'
-    s += tableSummary
+        s += r"""
+.TQ 1in
+\fB%s\fR
+%s
+""" % (name, textToNroff(title))
 
     if erFile:
         s += """
-.sp 1
+.\\" check if in troff mode (TTY)
+.if t \{
+.bp
 .SH "TABLE RELATIONSHIPS"
 .PP
 The following diagram shows the relationship among tables in the
-database.  Each node represents a table.  Each edge leads from the
+database.  Each node represents a table.  Tables that are part of the
+``root set'' are shown with double borders.  Each edge leads from the
 table that contains it and points to the table that its value
-represents.  Edges are labeled with their column names.
+represents.  Edges are labeled with their column names, followed by a
+constraint on the number of allowed values: \\fB?\\fR for zero or one,
+\\fB*\\fR for zero or more, \\fB+\\fR for one or more.  Thick lines
+represent strong references; thin lines represent weak references.
 .RS -1in
 """
         erStream = open(erFile, "r")
         for line in erStream:
             s += line + '\n'
         erStream.close()
-        s += ".RE\n"
+        s += ".RE\\}\n"
 
     for node in tableNodes:
         s += tableToNroff(schema, node) + "\n"
@@ -338,19 +378,19 @@ if __name__ == "__main__":
                 print "ovsdb-doc (Open vSwitch) @VERSION@"
             else:
                 sys.exit(0)
-            
+
         if len(args) != 2:
             sys.stderr.write("%s: exactly 2 non-option arguments required "
                              "(use --help for help)\n" % argv0)
             sys.exit(1)
-        
+
         # XXX we should warn about undocumented tables or columns
         s = docsToNroff(args[0], args[1], er_diagram)
         for line in s.split("\n"):
             line = line.strip()
             if len(line):
                 print line
-            
+
     except error.Error, e:
         sys.stderr.write("%s: %s\n" % (argv0, e.msg))
         sys.exit(1)