[selinux-policy: 253/3172] initial commit of xml->html conversion

Daniel J Walsh dwalsh at fedoraproject.org
Thu Oct 7 19:26:47 UTC 2010


commit 36e54b81f790075495420b76844d4231cfb089e9
Author: Chris PeBenito <cpebenito at tresys.com>
Date:   Thu Jun 2 20:39:32 2005 +0000

    initial commit of xml->html conversion

 refpolicy/Makefile                                 |   28 +-
 refpolicy/doc/doctools/footer.html                 |    2 +
 refpolicy/doc/doctools/header.html                 |    8 +
 refpolicy/doc/doctools/policy.dtd                  |   17 +
 refpolicy/doc/doctools/src/Converter.java          |  247 +++++++++
 refpolicy/doc/doctools/src/Docgen.java             |  565 ++++++++++++++++++++
 refpolicy/doc/doctools/src/policy/Interface.java   |   37 ++
 .../doc/doctools/src/policy/InterfaceType.java     |   12 +
 refpolicy/doc/doctools/src/policy/Layer.java       |   34 ++
 refpolicy/doc/doctools/src/policy/Module.java      |   34 ++
 refpolicy/doc/doctools/src/policy/Parameter.java   |   28 +
 refpolicy/doc/doctools/src/policy/Policy.java      |   35 ++
 .../doc/doctools/src/policy/PolicyElement.java     |  114 ++++
 refpolicy/doc/doctools/style.css                   |  152 ++++++
 14 files changed, 1308 insertions(+), 5 deletions(-)
---
diff --git a/refpolicy/Makefile b/refpolicy/Makefile
index 631a2e3..2c7ba7e 100644
--- a/refpolicy/Makefile
+++ b/refpolicy/Makefile
@@ -50,7 +50,6 @@ LOADPOLICY := $(SBINDIR)/load_policy
 SETFILES := $(SBINDIR)/setfiles
 
 XMLLINT := $(BINDIR)/xmllint
-XMLDTD := policy.dtd
 
 # enable MLS if requested.
 ifeq ($(MLS),y)
@@ -108,6 +107,15 @@ ALL_FC_FILES := $(foreach dir,$(ALL_LAYERS),$(wildcard $(dir)/*.fc))
 
 POLICY_SECTIONS := tmp/pre_te_files.conf tmp/generated_definitions.conf tmp/all_interfaces.conf tmp/all_attrs_types.conf tmp/only_te_rules.conf tmp/all_post.conf
 
+DOCTOOLS = doctools
+XMLDTD = $(DOCTOOLS)/policy.dtd
+HTMLHEAD = $(DOCTOOLS)/header.html
+HTMLFOOT = $(DOCTOOLS)/footer.html
+HTMLCSS = $(DOCTOOLS)/style.css
+HTMLOUT = $(DOCTOOLS)/html
+JAVASRC = $(wildcard $(DOCTOOLS)/src/*.java) $(wildcard $(DOCTOOLS)/src/policy/*.java)
+JAVABYTE = $(patsubst %.java,%.class,$(JAVASRC))
+
 ########################################
 #
 # default action: build policy locally
@@ -280,10 +288,9 @@ relabel:  $(FC) $(SETFILES)
 # Documentation generation
 #
 
-xml: policy.xml
-
-policy.xml: $(ALL_INTERFACES) tmp/generated_definitions.conf
+tmp/policy.xml: $(ALL_INTERFACES) tmp/generated_definitions.conf
 	@echo "Creating $@"
+	@cp $(XMLDTD) tmp
 	$(QUIET) echo '<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>' > $@
 	$(QUIET) echo '<!DOCTYPE policy SYSTEM "policy.dtd">' >> $@
 	$(QUIET) echo "<policy>" >> $@
@@ -295,6 +302,15 @@ policy.xml: $(ALL_INTERFACES) tmp/generated_definitions.conf
 		$(XMLLINT) --noout --dtdvalid $(XMLDTD) $@ ;\
 	fi
 
+$(JAVABYTE) doctool: $(JAVASRC)
+	javac $(JAVASRC)
+
+html: tmp/policy.xml $(JAVABYTE) $(HTMLHEAD) $(HTMLFOOT)
+	@mkdir -p $(DOCTOOLS)/html
+	$(QUIET) java -cp $(DOCTOOLS)/src/ Docgen -xf tmp/policy.xml \
+		-hf $(HTMLHEAD) -ff $(HTMLFOOT) -od $(HTMLOUT)
+	$(QUIET) cp $(HTMLCSS) $(HTMLOUT)
+
 ########################################
 #
 # Runtime binary policy patching of users
@@ -369,5 +385,7 @@ clean:
 	rm -f policy.conf
 	rm -f policy.$(PV)
 	rm -f $(FC)
+	rm -fR $(HTMLOUT)
+	find $(DOCTOOLS)/src -iname "*.class" | xargs rm -f
 
-.PHONY: default policy install reload enableaudit checklabels restorelabels relabel xml clean
+.PHONY: default policy install reload enableaudit checklabels restorelabels relabel html clean
diff --git a/refpolicy/doc/doctools/footer.html b/refpolicy/doc/doctools/footer.html
new file mode 100644
index 0000000..308b1d0
--- /dev/null
+++ b/refpolicy/doc/doctools/footer.html
@@ -0,0 +1,2 @@
+</body>
+</html>
diff --git a/refpolicy/doc/doctools/header.html b/refpolicy/doc/doctools/header.html
new file mode 100644
index 0000000..daf0904
--- /dev/null
+++ b/refpolicy/doc/doctools/header.html
@@ -0,0 +1,8 @@
+ 
+<html>
+<head>
+<title>Security Enhanced Linux Reference Policy</title>
+<style type="text/css" media="all">@import "style.css";</style>
+</head>
+<body>
+<div id="Header">Security Enhanced Linux Reference Policy</div>
diff --git a/refpolicy/doc/doctools/policy.dtd b/refpolicy/doc/doctools/policy.dtd
new file mode 100644
index 0000000..6d21a1b
--- /dev/null
+++ b/refpolicy/doc/doctools/policy.dtd
@@ -0,0 +1,17 @@
+<!ELEMENT policy (module+)>
+<!ELEMENT module (summary,interface+)>
+<!ATTLIST module 
+      name CDATA #REQUIRED
+      layer CDATA #REQUIRED>
+<!ELEMENT summary (#PCDATA)>
+<!ELEMENT interface (description,parameter+,infoflow)>
+<!ATTLIST interface name CDATA #REQUIRED>
+<!ELEMENT description (#PCDATA)>
+<!ELEMENT parameter (#PCDATA)>
+<!ATTLIST parameter 
+      name CDATA #REQUIRED
+      optional (true|false) "false">
+<!ELEMENT infoflow EMPTY>
+<!ATTLIST infoflow 
+      type CDATA #REQUIRED
+      weight CDATA #IMPLIED>
diff --git a/refpolicy/doc/doctools/src/Converter.java b/refpolicy/doc/doctools/src/Converter.java
new file mode 100644
index 0000000..91378fd
--- /dev/null
+++ b/refpolicy/doc/doctools/src/Converter.java
@@ -0,0 +1,247 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Converter.java: The reference policy documentation converter		
+ * Version: @version@ 
+ */
+import policy.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import java.util.Map;
+
+/**
+ * The reference policy documentation generator and xml analyzer class.
+ * It pulls in XML describing reference policy, transmogrifies it,
+ * and spits it back out in some other arbitrary format.
+ */
+public class Converter{
+	private Policy policy;
+	private static final String HTMLEXT = ".html";
+	private static final String indexContent = 
+		"<div id=\"Content\"><h2>Welcome to the reference policy API!</h2><p/>" +
+		"Please choose from the navigation options to the left.</div>";
+
+	private StringBuffer headerOutput = new StringBuffer();
+	private StringBuffer footerOutput = new StringBuffer();
+	private File outDir;
+	
+	public Converter(Policy pol, File headerFile, File footerFile) throws IOException{
+		policy = pol;
+
+		/*
+		 * Setup header and footer. 
+		 */
+		FileReader headIn = new FileReader(headerFile);
+
+		BufferedReader br = new BufferedReader(headIn);
+	    String line = null; //not declared within while loop
+	    while (( line = br.readLine()) != null){
+	        headerOutput.append(line);
+	        headerOutput.append(System.getProperty("line.separator"));
+	    }
+		
+		FileReader footerIn = new FileReader(footerFile);
+				
+		br = new BufferedReader(footerIn);
+	    line = null; //not declared within while loop
+	    while (( line = br.readLine()) != null){
+	        footerOutput.append(line);
+	        footerOutput.append(System.getProperty("line.separator"));
+	    }
+	}
+	
+	public void Convert(File _outDir) throws IOException{
+		outDir = _outDir;
+
+		// write the index document
+		FileWrite("index" + HTMLEXT, headerOutput.toString() 
+				+ Menu().toString() + indexContent + footerOutput.toString());
+		
+		// walk the policy and write pages for each module
+		for(Map.Entry<String,Layer> lay:policy.Children.entrySet()){
+			Layer layer = lay.getValue();
+			// the base output contains the menu and layer content header
+			StringBuffer baseOutput = new StringBuffer();
+			// the layer output will be filled with modules and summarys for content
+			StringBuffer layerOutput = new StringBuffer();
+			
+			// create the navigation menu
+			baseOutput.append(Menu(layer.Name));
+
+			baseOutput.append("<div id=\"Content\">\n");
+			baseOutput.append("\t<h1>Layer: " + layer.Name + "</h1><p/>\n");
+		
+			layerOutput.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" width=\"75%\">\n");
+			layerOutput.append("<tr><th class=\"title\">Module&nbsp;Name</th>" + 
+					"<th class=\"title\">Summary</th></tr>");
+			
+			for(Map.Entry<String,Module> mod:layer.Children.entrySet()){
+				// module output will be filled with in-depth module info.
+				StringBuffer moduleOutput = new StringBuffer();
+				Module module = mod.getValue();
+				
+				// get the content for the module's document
+				moduleOutput.append(moduleContent(mod.getValue()).toString() + "</div>");
+				
+				// get the summary and name for the layer's document
+
+				layerOutput.append("<tr><td><a href=\"" + layer.Name + "_" + module.Name + HTMLEXT 
+						+ "\">" + module.Name + "</a></td>" +
+						"\n<td>" + module.PCDATA + "</td></tr>\n");
+				
+				// write module document
+				FileWrite(layer.Name + "_" + module.Name + HTMLEXT, 
+					headerOutput.toString() + "\n" + baseOutput.toString() + moduleOutput.toString() + footerOutput.toString());
+			}
+			// write layer document
+			FileWrite(layer.Name + HTMLEXT, 
+				headerOutput.toString() + "\n" + baseOutput.toString() 
+				+ layerOutput.toString() + "</div>" + footerOutput.toString());
+
+		}
+	}
+	
+	private StringBuffer Menu(String key){
+		StringBuffer out = new StringBuffer();
+		out.append("<div id=\"Menu\">\n");
+		for(Map.Entry<String,Layer> layer:policy.Children.entrySet()){
+			String layerName = layer.getKey();
+			
+			
+			// show the modules for the current key
+			if (layerName.equals(key)){
+				out.append("\t<a href=\"" + layerName 
+						+ HTMLEXT + "\">" + layerName + "</a><br />\n");
+				out.append("\t<div id=\"subitem\">\n");
+				for(Map.Entry<String,Module> module:layer.getValue().Children.entrySet()){
+					String moduleName = module.getKey();
+					out.append("\t\t-&nbsp;<a href=\"" + layerName + "_" + moduleName + HTMLEXT 
+							+ "\">" + moduleName + "</a><br />\n");
+				}
+				out.append("\t</div>\n");
+			} else {
+				out.append("\t<a href=\"" + layerName +
+						HTMLEXT + "\">+&nbsp;" + layerName + "</a><br />\n");
+			}
+		}
+		out.append("</div>");
+		return out;
+	}
+
+	private StringBuffer Menu(){
+		StringBuffer out = new StringBuffer();
+		out.append("<div id=\"Menu\">\n");
+		for(Map.Entry<String,Layer> layer:policy.Children.entrySet()){
+			String layerName = layer.getKey();
+			
+			
+			out.append("\t<a href=\"" + layerName + HTMLEXT + "\">" + layerName + "</a><br />\n");
+			out.append("\t<div id=\"subitem\">\n");
+			for(Map.Entry<String,Module> module:layer.getValue().Children.entrySet()){
+				String moduleName = module.getKey();
+				out.append("\t\t-&nbsp;<a href=\"" + layerName + "_" + moduleName + HTMLEXT 
+						+ "\">" + moduleName + "</a><br />\n");
+			}
+			out.append("\t</div>\n");
+		}
+		out.append("</div>");
+		return out;
+	}
+
+	private StringBuffer moduleContent(Module module){
+		StringBuffer out = new StringBuffer();
+
+		out.append("\t<h2>Module:&nbsp;"+ module.Name + "<br />\n");
+		out.append("\tSummary:&nbsp;" + module.PCDATA + "</h2>\n");
+		for (Map.Entry<String,Interface> inter:module.Children.entrySet()){
+			Interface iface = inter.getValue();
+			// main table header
+			out.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" width=\"75%\">\n");
+			out.append("<tr><th class=\"title\" colspan=\"3\">Interface</th></tr>\n");
+			
+			// only show weight when type isnt none
+			if (iface.Type != InterfaceType.None){
+				out.append("<tr><td>Name</td><td colspan=\"2\">" + iface.Name + "</td></tr>\n" +
+						"<tr><td>Flow&nbsp;Type</td><td colspan=\"2\">" + iface.Type.toString() + "</td></tr>\n");
+				out.append("\t<td>Flow&nbsp;Weight</td><td colspan=\"2\">" + iface.Weight + "</td></tr>\n");
+			} else {
+				out.append("<tr><td>Name</td><td colspan=\"2\">" + iface.Name + "</td></tr>\n");
+				out.append("<tr><td>Flow&nbsp;Type</td><td colspan=\"2\">" + iface.Type.toString() + "</td></tr>\n");
+				
+			}
+			out.append("<tr><td>Description</td><td colspan=\"2\">" + iface.PCDATA + "</td></tr>\n");
+			
+			out.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" width=\"75%\">\n" 
+					+ "<tr><th class=\"title\">Parameter</th><th class=\"title\">Description</th>" 
+					+ "<th class=\"title\">Optional</th></tr>");
+			for (Map.Entry<String,Parameter> param:iface.Children.entrySet()){
+				Parameter parameter = param.getValue();
+				out.append("\t<tr><td> " + parameter.Name + "</td>\n");
+				out.append("\t<td>" + parameter.PCDATA + "</td>\n");
+				String opt = parameter.GetAttribute("optional");
+				if (opt != null && opt.equalsIgnoreCase("true")){
+					out.append("\t<td><center>Yes</center></td></tr>\n");
+				} else {
+					out.append("\t<td><center>No</center></td></tr>\n");
+				}
+			}
+			out.append("\n</table></table><br/><br/>\n");
+		}
+		return out;
+	}
+	
+	/**
+	 * Write to sub directory.
+	 * @param path
+	 * @param outFilename
+	 * @param content
+	 * @return
+	 */
+	private void FileWrite (String path, String outFilename, String content){
+		try {
+			// create parent if necessary
+			File outParent = new File(outDir, path );
+			File outFile = new File(outParent, outFilename );
+			if (outParent.exists() && !outParent.isDirectory()){
+				throw new IOException("Output directory not really a directory");
+			} else if (!outParent.exists()){
+				outParent.mkdirs();
+			} 
+			PrintStream stream = new PrintStream(new FileOutputStream(outFile));
+			stream.println(content);
+			stream.flush();
+			stream.close();
+		} catch (Exception e){
+			System.err.println (e.getMessage());
+			System.exit(1);
+		}
+	}
+	
+	/**
+	 * Write to output directory directly.
+	 * 
+	 * @param path
+	 * @param outFilename
+	 * @param content
+	 * @return
+	 */
+	private void FileWrite (String outFilename, String content){
+		try {
+			File out = new File(outDir, outFilename );
+			PrintStream stream = new PrintStream(new FileOutputStream(out));
+			stream.println(content);
+			stream.flush();
+			stream.close();
+		} catch (Exception e){
+			System.err.println (e.getMessage());
+			System.exit(1);
+		}
+	}
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/Docgen.java b/refpolicy/doc/doctools/src/Docgen.java
new file mode 100644
index 0000000..12e38bf
--- /dev/null
+++ b/refpolicy/doc/doctools/src/Docgen.java
@@ -0,0 +1,565 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Docgen.java: The reference policy xml analyzer and documentation generator		
+ */
+import policy.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.xml.parsers.DocumentBuilder; 
+import javax.xml.parsers.DocumentBuilderFactory;  
+import javax.xml.parsers.ParserConfigurationException;
+
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Schema;
+
+import javax.xml.XMLConstants;
+
+import org.xml.sax.*;
+import org.w3c.dom.*;
+
+/**
+ * The reference policy documentation generator and xml analyzer class.
+ * It pulls in XML describing reference policy, transmogrifies it,
+ * and spits it back out in some other arbitrary format.
+ */
+public class Docgen{
+	// store the PIs here
+	private static Vector procInstr = new Vector();
+	private static boolean verbose = false;
+	// the policy structure built after xml is parsed
+	private Policy policy = null;
+	// the xml document
+	private Document dom = null;
+	
+	// the files/directories passed in from the command line
+	private static File xmlFile;
+	private static File headerFile;
+	private static File footerFile;
+	private static File outputDir;
+		
+	private static void printUsage(){
+		System.out.println("Reference Policy Documentation Compiler usage:");
+		System.out.println("\tjava -cp ./src Docgen [-h] [-v] -xf xmlFileIn -hf headerFile -ff footerFile -od outDirectory");
+		System.out.println("-h display this message and exit");
+		System.out.println("-xf XML file to parse");
+		System.out.println("-hf header file for HTML output");
+		System.out.println("-ff footer file for HTML output");
+		System.out.println("-od output directory");
+		System.exit(1);
+	}
+	
+	/**
+	 * Docgen constructor
+	 * 
+	 * @param output	Filename to setup for output
+	 */
+	public Docgen(String output) 
+	throws FileNotFoundException {
+	}
+	
+	/**
+	 * The main() driver for the policy documentation generator.
+	 * @param argv	Arguments, takes 1 filename parameter
+	 */
+	public static void main(String argv[]) {
+		if (argv.length == 0){
+			printUsage();
+			System.exit(1);
+		}
+		// hacked up version of getopt()
+		for (int x=0; x < argv.length; x++){
+			if (argv[x].equals("-xf")){
+				x++;
+				if (x<argv.length){
+					xmlFile = new File(argv[x]);
+					if (!xmlFile.isFile()){
+						printUsage();
+						System.err.println("XML file is not really a file!");
+						System.exit(1);
+					}
+				} else {
+					printUsage();
+					System.exit(1);
+				}
+			} else if (argv[x].equals("-hf")){
+				x++;
+				if (x<argv.length){
+					headerFile = new File(argv[x]);
+					if (!headerFile.isFile()){
+						printUsage();
+						System.err.println("Header file is not really a file!");
+						System.exit(1);
+					}
+				} else {
+					printUsage();
+					System.exit(1);
+				}
+			} else if (argv[x].equals("-ff")){
+				x++;
+				if (x<argv.length){
+					footerFile = new File(argv[x]);
+					if (!footerFile.isFile()){
+						printUsage();
+						System.err.println("Footer file is not really a file!");
+						System.exit(1);
+					}
+				} else {
+					printUsage();
+					System.exit(1);
+				}
+			} else if (argv[x].equals("-od")){
+				x++;
+				if (x<argv.length){
+					outputDir = new File(argv[x]);
+					if (!outputDir.isDirectory()){
+						printUsage();
+						System.err.println("Output directory is not really a directory!");
+						System.exit(1);
+					}
+				} else {
+					printUsage();
+					System.exit(1);
+				}
+			} else if (argv[x].equals("-h")){
+				printUsage();
+				System.exit(1);
+			} else if (argv[x].equals("-v")){
+				verbose = true;
+			} else {
+				printUsage();
+				System.out.println("Error unknown argument: " + argv[x]);
+				System.exit(1);
+			}
+		}
+		
+		try {
+			// create document factory 
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+			Schema schema = schemaFactory.newSchema();
+			
+			factory.setValidating(true);   
+			factory.setNamespaceAware(true);
+
+			// in order for this setting to hold factory must be validating
+			factory.setIgnoringElementContentWhitespace(true);
+
+			// get builder from factory
+			DocumentBuilder builder = factory.newDocumentBuilder();
+				
+			// create an anonymous error handler for parsing errors
+			builder.setErrorHandler(
+					new org.xml.sax.ErrorHandler() {
+						// fatal errors
+						public void fatalError(SAXParseException exception)
+						throws SAXException {
+							throw exception;
+						}
+						
+						// parse exceptions will be fatal
+						public void error(SAXParseException parseErr)
+						throws SAXParseException
+						{
+							// Error generated by the parser
+							System.err.println("\nPARSE ERROR: line " + parseErr.getLineNumber() 
+									+  ", URI " + parseErr.getSystemId());
+							System.err.println("PARSE ERROR: " + parseErr.getMessage() );
+							
+							// check the wrapped exception
+							Exception  x = parseErr;
+							if (parseErr.getException() != null)
+								x = parseErr.getException();
+							x.printStackTrace();					}
+						
+						// dump warnings too
+						public void warning(SAXParseException err)
+						throws SAXParseException
+						{
+							System.err.println("\nPARSE WARNING: line " + err.getLineNumber()
+									+ ", URI " + err.getSystemId());
+							System.err.println("PARSE WARNING:   " + err.getMessage());
+						}
+					}
+			);
+			
+			Docgen redoc = new Docgen(argv[1]);
+
+			redoc.dom = builder.parse(xmlFile);
+			
+			// do our own transformations
+			redoc.processDocumentNode();
+			
+			// build our own converter then convert
+			Converter converter = new Converter(redoc.policy, headerFile, footerFile);
+			converter.Convert(outputDir);
+		// TODO: figure out which of these is taken care of by the anonymous error handler above
+		} catch (SAXException saxErr) {
+			// sax error
+			Exception  x = saxErr;
+			if (saxErr.getException() != null)
+				x = saxErr.getException();
+			x.printStackTrace();
+		} catch (ParserConfigurationException parseConfigErr) {
+			// Sometimes we can't build the parser with the specified options
+			parseConfigErr.printStackTrace();
+		} catch (IOException ioe) {
+			// I/O error
+			ioe.printStackTrace();
+		} catch (Exception err) {
+			err.printStackTrace();
+		}
+		
+
+	} // main
+
+	public static void Debug(String msg){
+		if (verbose)
+			System.out.println(msg);
+	}
+	
+	void processDocumentNode() throws SAXException{
+		Element docNode = dom.getDocumentElement();
+		
+		if (docNode != null && docNode.getNodeName().equals("policy")){
+			policy = new Policy("policy");
+			
+			NodeList children = docNode.getChildNodes();
+			int len = children.getLength();
+		
+			for (int index = 0; index < len; index++){
+				processNode(children.item(index), policy);
+			}
+		} else {
+			System.err.println("Failed to find document/policy node!");
+			System.exit(1);
+		}
+	}
+	
+	/**
+	 * Process children of the policy node (aka modules).
+	 * 
+	 * @param node		A child node of the policy.
+	 * @param parent	The parent PolicyElement.
+	 */
+	void processNode(Node node, Policy parent) throws SAXException{
+		Layer layer = null;
+		Module module = null;
+		
+		// save us from null pointer de-referencing
+		if (node == null){
+			System.err.println(
+			"Nothing to do, node is null");
+			return;
+		}
+		
+		// snag the name
+		String nodeName = node.getNodeName();
+		
+		// validity check and pull layer attribute
+		if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("module")){
+			// display the name which might be generic
+			Docgen.Debug("Encountered node: " + nodeName);
+			NamedNodeMap attrList =	node.getAttributes();
+			
+			// the required attributes
+			int attrLen = 0;
+			if(attrList != null){
+				attrLen = attrList.getLength();
+			} else{
+				fatalNodeError("Missing attributes in module. \""  
+						+ "\".  \"layer\" and \"name\" are required attributes.");
+			}
+
+			Node moduleNode = attrList.getNamedItem("name");
+			Node layerNode = attrList.getNamedItem("layer");
+			
+			if (moduleNode == null || layerNode == null)
+				fatalNodeError("Missing attributes in module element.  \"layer\" and \"name\" are required attributes.");
+
+			String moduleName = moduleNode.getNodeValue();
+			String layerName = layerNode.getNodeValue();
+		
+			// check to see if this is a new layer or a pre-existing layer
+			layer = parent.Children.get(layerName);
+			if (layer == null){
+					Docgen.Debug("Adding new layer: " + layerName);
+					layer = new Layer(layerName, parent);	
+			} else {
+				Docgen.Debug("Lookup succeeded for: " + layerName);
+			}
+			
+			if (layer.Children.containsKey(moduleName)){
+				Docgen.Debug("Reusing previously defined module: " + moduleName);
+				module = layer.Children.get(moduleName);
+			} else {
+				Docgen.Debug("Creating module: " + moduleName);
+				module = new Module(moduleName, layer);
+			}
+			
+			// take care of the attributes
+			for(int i = 0; i < attrLen; i++){
+				Node attrNode = attrList.item(i);
+				String attrName = attrNode.getNodeName();
+				String attrValue = attrNode.getNodeValue();
+				if (!attrName.equals("layer") && !attrName.equals("name")){
+					Docgen.Debug("\tAdding attribute: " + attrNode.getNodeName()
+							+ "=" + attrValue);
+					module.AddAttribute(attrName,attrValue);
+				}
+			}
+		} else if (!isEmptyTextNode(node)){
+			fatalNodeError("Unexpected child \"" + nodeName 
+					+"\" node of parent \"" + parent.Name + "\".");
+		}
+		
+		// recurse over children if both module and layer defined
+		if (module != null && layer != null){
+			// the containsKey check verified no duplicate module
+			layer.Children.put(module.Name, module);
+			parent.Children.put(layer.Name, layer);
+
+			NodeList children = node.getChildNodes();
+			if (children != null){
+				int len = children.getLength();
+				for (int index = 0; index < len; index++){
+					processNode(children.item(index), module);
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Process children of the module node (aka interfaces).
+	 * 
+	 * @param node		A child node of the policy.
+	 * @param parent	The parent PolicyElement.
+	 */
+	void processNode(Node node, Module parent) throws SAXException{
+		Interface iface = null;
+		
+		// save us from null pointer de-referencing
+		if (node == null){
+			System.err.println(
+			"Nothing to do, node is null");
+			return;
+		}
+		
+		// snag the name
+		String nodeName = node.getNodeName();
+		
+		// if summary node
+		if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("summary")){
+			// unfortunately we still need to snag the PCDATA child node for the actual text
+			Docgen.Debug("Encountered node: " + nodeName);
+			NodeList children = node.getChildNodes();
+			if (children != null && children.getLength() == 1){
+				if (children.item(0).getNodeType() == Node.TEXT_NODE){
+					parent.PCDATA = children.item(0).getNodeValue();
+					return;
+				} 
+			}
+			fatalNodeError("Unexpected child \"" + nodeName 
+					+"\" node of parent \"" + parent.Name + "\".");
+		// if interface node
+		} else if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("interface")){
+			NamedNodeMap attrList =	node.getAttributes();
+			// the required attributes
+			int attrLen = 0;
+			if(attrList != null){
+				attrLen = attrList.getLength();
+			} else{
+				fatalNodeError("Missing attribute in interface.  " 
+						+ "\"name\" is a required attribute.");
+			}
+
+			Node nameNode = attrList.getNamedItem("name");
+						
+			if (nameNode == null )
+				fatalNodeError("Missing attribute in interface.  " 
+						+ "\"name\" is a required attribute.");
+
+
+			String iName = nameNode.getNodeValue();
+		
+			Docgen.Debug("Creating interface: " + iName);
+			iface = new Interface(iName, parent);
+			
+			// take care of the attributes
+			for(int i = 0; i < attrLen; i++){
+				Node attrNode = attrList.item(i);
+				String attrName = attrNode.getNodeName();
+				String attrValue = attrNode.getNodeValue();
+				if (!attrName.equals("name")){
+					Docgen.Debug("\tAdding attribute: " + attrNode.getNodeName()
+							+ "=" + attrValue);
+					iface.AddAttribute(attrName,attrValue);
+				}
+			}
+		} else if (!isEmptyTextNode(node)){
+			fatalNodeError("Unexpected child \"" + nodeName 
+					+"\" node of parent \"" + parent.Name + "\".");
+		}
+		
+		// recurse over children if both module and layer defined
+		if (iface != null && parent != null){
+			// FIXME: containsKey() check for duplicate
+			parent.Children.put(iface.Name, iface);
+
+			NodeList children = node.getChildNodes();
+			if (children != null){
+				int len = children.getLength();
+				for (int index = 0; index < len; index++){
+					processNode(children.item(index), iface);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Process children of the interface node (aka parameters, desc., infoflow).
+	 * 
+	 * @param node		A child node of the policy.
+	 * @param parent	The parent PolicyElement.
+	 */
+	void processNode(Node node, Interface parent) throws SAXException{
+		Parameter param = null;
+		
+		// save us from null pointer de-referencing
+		if (node == null){
+			System.err.println(
+			"Nothing to do, node is null");
+			return;
+		}
+		
+		// snag the name
+		String nodeName = node.getNodeName();
+		
+		// if description node
+		if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("description")){
+			// unfortunately we still need to snag the PCDATA child node for the actual text
+			NodeList children = node.getChildNodes();
+			if (children != null && children.getLength() == 1){
+				if (children.item(0).getNodeType() == Node.TEXT_NODE){
+					parent.PCDATA = children.item(0).getNodeValue();
+					return;
+				} 
+			}
+			fatalNodeError("Unexpected child \"" + nodeName 
+					+"\" node of parent \"" + parent.Name + "\".");
+		// if infoflow node
+		} else if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("infoflow")){
+			NamedNodeMap attrList =	node.getAttributes();
+			// the required attributes
+			int attrLen = 0;
+			if(attrList != null){
+				attrLen = attrList.getLength();
+			} else{
+				fatalNodeError("Missing attribute in infoflow." 
+						+ "  \"type\" and \"weight\" are required attributes.");
+			}
+
+			Node typeNode = attrList.getNamedItem("type");
+			Node weightNode = attrList.getNamedItem("weight");
+				
+			String type = typeNode.getNodeValue();
+			if (typeNode == null || 
+					(!type.equals("none") && weightNode == null))
+				fatalNodeError("Missing attribute in infoflow." 
+						+ "  \"type\" and \"weight\" are required attributes (unless type is none).");
+
+			if (type.equals("read")){
+				parent.Type = InterfaceType.Read;
+				parent.Weight = Integer.parseInt(weightNode.getNodeValue());
+			}else if (type.equals("write")){
+				parent.Type = InterfaceType.Write;
+				parent.Weight = Integer.parseInt(weightNode.getNodeValue());
+			}else if (type.equals("both")){
+				parent.Type = InterfaceType.Both;
+				parent.Weight = Integer.parseInt(weightNode.getNodeValue());
+			}else if (type.equals("none")){
+				parent.Type = InterfaceType.None;
+				parent.Weight = -1;
+			} else {
+				System.err.println("Infoflow type must be read, write, both, or none!"); 
+			}
+			
+		} else if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("parameter")){
+			NamedNodeMap attrList =	node.getAttributes();
+			// the required attributes
+			int attrLen = 0;
+			if(attrList != null){
+				attrLen = attrList.getLength();
+			} else{
+				fatalNodeError("Missing attribute in parameter \"" 
+						+ "\".  \"name\" is a required attribute.");
+			}
+
+			Node nameNode = attrList.getNamedItem("name");
+						
+			if (nameNode == null )
+				fatalNodeError("Missing attribute in parameter \"" 
+						+ "\".  \"name\" is a required attribute.");
+
+			String paramName = nameNode.getNodeValue();
+		
+			Docgen.Debug("Creating parameter: " + paramName);
+			param = new Parameter(paramName, parent);
+
+			// unfortunately we still need to snag the PCDATA child node for the actual text
+			NodeList children = node.getChildNodes();
+			if (children != null && children.getLength() == 1){
+				if (children.item(0).getNodeType() == Node.TEXT_NODE){
+					param.PCDATA = children.item(0).getNodeValue();
+				} 
+			} else {
+				fatalNodeError("Unexpected child \"" 
+						+"\" node of parameter.");
+			}
+				
+			// take care of the attributes
+			for(int i = 0; i < attrLen; i++){
+				Node attrNode = attrList.item(i);
+				String attrName = attrNode.getNodeName();
+				String attrValue = attrNode.getNodeValue();
+				if (!attrName.equals("name")){
+					Docgen.Debug("\tAdding attribute: " + attrNode.getNodeName()
+							+ "=" + attrValue);
+					param.AddAttribute(attrName,attrValue);
+				}
+			}
+		} else if (!isEmptyTextNode(node)){
+			fatalNodeError("Unexpected child \"" + nodeName 
+					+"\" node of parent \"" + parent.Name + "\".");
+		}
+		
+		// recurse over children if both parent and param defined
+		if (param != null && parent != null){
+			// the containsKey check verified no duplicate module
+			// FIXME: containsKey() check for duplicate
+			parent.Children.put(param.Name, param);
+		}
+	}
+
+	public boolean isEmptyTextNode(Node node){
+		/*
+		 * FIXME: remove once properly validating
+		 * Since we aren't validating yet we needed our
+		 * own pointless whitespace remover.
+		 */
+
+		if (node.getNodeType() == Node.TEXT_NODE &&
+				node.getNodeValue().trim().length() == 0)
+				return true;
+		return false;
+	}	
+	
+	public void fatalNodeError(String msg) 
+	throws SAXException {
+		// FIXME: figure out how to throw SAXParseException w/ location
+		throw new SAXException(msg);
+	}
+}
diff --git a/refpolicy/doc/doctools/src/policy/Interface.java b/refpolicy/doc/doctools/src/policy/Interface.java
new file mode 100644
index 0000000..6014adf
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/Interface.java
@@ -0,0 +1,37 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Interface.java: The reference policy interfaces
+ * Version: @version@ 
+ */
+package policy;
+
+import java.util.Map;
+import java.util.TreeMap;
+ 
+/**
+ * Each reference policy interface is represented by this class.
+ * 
+ * @see Layer
+ * @see Module
+ * @see Parameter
+ */
+public class Interface extends PolicyElement {
+	/** the children of this element */
+	public final Map<String,Parameter> Children;
+
+	public InterfaceType Type;
+	public int Weight;
+	
+	/**
+	 * Default constructor assigns name to module.
+	 * 
+	 * @param _name		The name of the module.
+	 * @param _Parent 	The reference to the parent element.
+	 */
+	public Interface(String _name, Module _Parent){
+		super(_name, _Parent);
+		Children = new TreeMap<String,Parameter>();
+	}	
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/policy/InterfaceType.java b/refpolicy/doc/doctools/src/policy/InterfaceType.java
new file mode 100644
index 0000000..0a102c5
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/InterfaceType.java
@@ -0,0 +1,12 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Interface.java: The reference policy interface types
+ * Version: @version@ 
+ */
+package policy;
+
+public enum InterfaceType {
+	Read, Write, Both, None;
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/policy/Layer.java b/refpolicy/doc/doctools/src/policy/Layer.java
new file mode 100644
index 0000000..b232a38
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/Layer.java
@@ -0,0 +1,34 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Layer.java: The reference policy layers		
+ * Version: @version@ 
+ */
+package policy;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Each reference policy layer is represented by this class.
+ * 
+ * @see Module
+ * @see Interface
+ * @see Parameter
+ */
+public class Layer extends PolicyElement {
+	/** the children of this element */
+	public final Map<String,Module> Children;
+	
+	/**
+	 * Default constructor assigns name to layer.
+	 * 
+	 * @param _name		The name of the layer.
+	 * @param _Parent 	The reference to the parent element.
+	 */
+	public Layer(String _name, Policy _Parent){
+		super(_name, _Parent);
+		Children = new TreeMap<String, Module>();
+	}
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/policy/Module.java b/refpolicy/doc/doctools/src/policy/Module.java
new file mode 100644
index 0000000..3bb0c22
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/Module.java
@@ -0,0 +1,34 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Module.java: The reference policy module		
+ * Version: @version@ 
+ */
+package policy;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Each reference policy layer is represented by this class.
+ * 
+ * @see Layer
+ * @see Interface
+ * @see Parameter
+ */
+public class Module extends PolicyElement {
+	/** the children of this element */
+	public final Map<String,Interface> Children;
+		
+	/**
+	 * Default constructor assigns name to module.
+	 * 
+	 * @param _name		The name of the module.
+	 * @param _Parent 	The reference to the parent element.
+	 */
+	public Module(String _name, Layer _Parent){
+		super(_name, _Parent);
+		Children = new TreeMap<String,Interface>();
+	}
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/policy/Parameter.java b/refpolicy/doc/doctools/src/policy/Parameter.java
new file mode 100644
index 0000000..0f0ee45
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/Parameter.java
@@ -0,0 +1,28 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Parameter.java: The reference policy interface parameters
+ * Version: @version@ 
+ */
+package policy;
+ 
+/**
+ * Each reference policy layer is represented by this class.
+ * 
+ * @see Layer
+ * @see Module
+ * @see Interface
+ */
+public class Parameter extends PolicyElement{
+	
+	/**
+	 * Default constructor assigns name to parameter.
+	 * 
+	 * @param _name		The name of the parameter.
+	 * @param _Parent 	The reference to the parent element.
+	 */
+	public Parameter(String _name, Interface _Parent){
+		super(_name, _Parent);
+	}
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/policy/Policy.java b/refpolicy/doc/doctools/src/policy/Policy.java
new file mode 100644
index 0000000..e17ff1f
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/Policy.java
@@ -0,0 +1,35 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * Policy.java: The reference policy api		
+ * Version: @version@ 
+ */
+package policy;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Each reference policy layer is represented by this class.
+ * 
+ * @see Module
+ * @see Interface
+ * @see Parameter
+ */
+public class Policy extends PolicyElement {
+	/** the children of this element */
+	public final Map<String,Layer> Children;
+	
+	/**
+	 * Default constructor assigns name to layer.
+	 * 
+	 * @param _name		The name of the layer.
+	 * @param _Parent 	The reference to the parent element.
+	 */
+	public Policy(String _name){
+		// the policy is the root element so parent==null
+		super(_name, null);
+		Children = new TreeMap<String,Layer>();
+	}	
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/src/policy/PolicyElement.java b/refpolicy/doc/doctools/src/policy/PolicyElement.java
new file mode 100644
index 0000000..f5fc913
--- /dev/null
+++ b/refpolicy/doc/doctools/src/policy/PolicyElement.java
@@ -0,0 +1,114 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * License: refer to COPYING file for license information.
+ * Authors: Spencer Shimko <sshimko at tresys.com>
+ * 
+ * PolicyElement.java: The reference policy base class		
+ * Version: @version@ 
+ */
+package policy;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Reference policy elements have certain similarties which
+ * should be inherited from a base class.  This includes a name
+ * attribute that will be used for hashing, sorting, and converting
+ * to a string.  
+ * 
+ * @see Layer
+ * @see Module
+ * @see Interface
+ * @see Parameter
+ */
+public abstract class PolicyElement {
+	/** the string identifying the element */
+	public final String Name;
+	/** the parent component */
+	public final PolicyElement Parent;
+
+	/** the attributes of this element */
+	private final Map<String,String> attributes;
+	
+	public String PCDATA;
+
+	/**
+	 * Default constructor assigns name.
+	 * 
+	 * @param _name		The name of the element.
+	 * @param _Parent 	The reference to the parent element.
+	 */
+	public PolicyElement(String _name, PolicyElement _Parent){
+		Name = _name;
+		Parent = _Parent;
+		attributes = new TreeMap<String,String>();
+	}
+	
+	/**
+	 * Add attribute to policy element.
+	 * 
+	 * @param aName		String to identify attribute
+	 * @param aString 	Value of attribute identified by string aName
+	 * @return 			<code>true</code> when attribute added to element,
+	 * 					<code>false</code> when attribute already defined for element 
+	 */
+	public boolean AddAttribute(String aName, String aString){
+		if (attributes.containsKey(aName)){
+			return false;
+		}
+		attributes.put(aName,aString);
+		return true;
+	}
+	
+	/**
+	 * Get attribute from policy element.
+	 * 
+	 * @param aName		String to identify attribute
+	 * @return 			String value associated with named attribute
+	 * 					or <code>null</code> if no attribute defined
+	 * 					for aName. 
+	 */
+	public String GetAttribute(String aName){
+		return attributes.get(aName);
+	}
+
+	/**
+	 * Overridden equals method
+	 * 
+	 * @param obj	Object for comparison
+	 */
+	@Override public boolean equals(Object obj){
+		return obj.toString().equals(this.Name);
+	}
+
+	/**
+	 * Return a hashcode for the element. Currently implemented as a 
+	 * call to the String hashCode() method.
+	 * 
+	 * @return integer hashCode of this instance assuring two objects that
+	 * 		   are == will return same hashcode.
+	 */
+	@Override public int hashCode() {
+		return this.toString().hashCode();
+	}
+	
+	/**
+	 * Overridden toString() method.
+	 * 
+	 * @return String representation of instance (it's name).
+	 */
+	@Override public String toString(){
+			return Name;
+	}		
+	
+	/**
+	 * Since Component might be used as a key in some type of sort Compare
+	 * is implemented.
+	 * 
+	 * @return	0 if this==that, <0 if this<that, >0 if this>that
+	 * @see String.compareTo
+	 */
+	public int compareTo(Layer that){
+		return Name.compareTo(that.Name);
+	}
+}
\ No newline at end of file
diff --git a/refpolicy/doc/doctools/style.css b/refpolicy/doc/doctools/style.css
new file mode 100644
index 0000000..3805bf8
--- /dev/null
+++ b/refpolicy/doc/doctools/style.css
@@ -0,0 +1,152 @@
+body {
+	margin:0px;
+	padding:0px;
+	font-family:verdana, arial, helvetica, sans-serif;
+	color:#333;
+	background-color:white;
+	}
+h1 {
+	margin:0px 0px 15px 0px;
+	padding:0px;
+	font-size:28px;
+	line-height:28px;
+	font-weight:900;
+	color:#ccc;
+	}
+h2 {
+	font-size:100%;
+	}
+h3 {
+	font-size:75%;
+	}
+h4 {
+	font-size:67%;
+	}
+li {
+	font:11px/20px verdana, arial, helvetica, sans-serif;
+	margin:0px 0px 0px 0px;
+	padding:0px;
+	}
+p {
+	/* normal */
+	font:11px/20px verdana, arial, helvetica, sans-serif;
+	margin:0px 0px 16px 0px;
+	padding:0px;
+	}
+        
+tt {
+	/* inline code */
+	font-family: monospace;
+	}
+        
+table {
+        background-color:#eee;
+        border:1px dashed #999;
+        /*background-color: white;*/
+        color: black;
+        text-align: left;
+	font:11px/20px verdana, arial, helvetica, sans-serif;
+        margin-left: 10%;
+        margin-right: 10%;
+}
+
+th {
+        background-color: #ccccff;
+        text-align: center;
+}
+
+td.header {
+        font-weight: bold;
+}
+        
+#Content>p {margin:0px;}
+#Content>p+p {text-indent:30px;}
+a {
+	color:#09c;
+	font-size:11px;
+	text-decoration:none;
+	font-weight:600;
+	font-family:verdana, arial, helvetica, sans-serif;
+	}
+a:link {color:#09c;}
+a:visited {color:#07a;}
+a:hover {background-color:#eee;}
+
+#Codeblock {
+	margin:5px 50px 5px 50px;
+	padding:5px 0px 5px 15px;
+	border-style:solid;
+	border-color:black;
+	border-width:1px 1px 1px 1px;
+	background-color:#f8f8f8;
+	font-size:11px;
+	font-weight:600;
+	text-decoration:none;
+	font-family:courier;
+}
+pre {
+	font-size:11px;
+	font-weight:600;
+	text-decoration:none;
+	font-family:courier;
+}
+pre.codeblock {
+        /* code block (bordered, slight gray background) */
+	border-style:solid;
+	border-color:black;
+	border-width:1px 1px 1px 1px;
+	background-color:#f8f8f8;
+        margin-left: 10%;
+        margin-right: 10%;
+}
+dl {
+	/* definition text block */
+	font:11px/20px verdana, arial, helvetica, sans-serif;
+	margin:0px 0px 16px 0px;
+	padding:0px;
+	}
+dt {
+	/* definition term */
+        font-weight: bold;
+	}
+
+#Header {
+	margin:50px 0px 10px 0px;
+	padding:17px 0px 0px 20px;
+	/* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */
+	height:33px; /* 14px + 17px + 2px = 33px */
+	border-style:solid;
+	border-color:black;
+	border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */
+	line-height:11px;
+	font-size:110%;
+	background-color:#eee;
+	voice-family: "\"}\"";
+	voice-family:inherit;
+	height:14px; /* the correct height */
+	}
+body>#Header {height:14px;}
+#Content {
+	margin:0px 50px 50px 200px;
+	padding:10px;
+	}
+
+#Menu {
+	position:absolute;
+	top:100px;
+	left:20px;
+	width:162px;
+	padding:10px;
+	background-color:#eee;
+	border:1px dashed #999;
+	line-height:17px;
+	text-align:left;	
+	voice-family: "\"}\"";
+	voice-family:inherit;
+	width:160px;
+	}
+#Menu subitem {
+	font-size: 5px;
+}
+
+body>#Menu {width:160px;}


More information about the scm-commits mailing list