// // A provider to display man pages // // Authors: // Johannes Roith // Jonathan Pryor // // (C) 2008 Novell, Inc. namespace Monodoc { using System; using System.Collections; using System.Diagnostics; using System.IO; using System.Text; using System.Xml; // // The simple provider generates the information source // public class ManProvider : Provider { string[] tocFiles; public ManProvider (string[] handbookTocFiles) { tocFiles = handbookTocFiles; // huh... if (!File.Exists (tocFiles[0])) throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFiles[0])); } public override void PopulateTree (Tree tree) { foreach(string TocFile in tocFiles) { XmlDocument doc = new XmlDocument(); doc.Load(TocFile); XmlNodeList nodeList = doc.GetElementsByTagName("manpage"); Node nodeToAddChildrenTo = tree; foreach(XmlNode node in nodeList) { XmlAttribute name = node.Attributes["name"]; XmlAttribute page = node.Attributes["page"]; if (name == null || page == null) continue; if (!File.Exists (page.Value)) continue; string target = "man:" + name.Value; nodeToAddChildrenTo.CreateNode (name.Value, target); if (File.Exists(page.Value)) nodeToAddChildrenTo.tree.HelpSource.PackFile (page.Value, name.Value); } } } public override void CloseTree (HelpSource hs, Tree tree) { } } // // The HelpSource is used during the rendering phase. // public class ManHelpSource : HelpSource { public ManHelpSource (string base_file, bool create) : base (base_file, create) {} protected const string MAN_PREFIX = "man:"; public override string GetText (string url, out Node match_node) { match_node = null; if (url.IndexOf (MAN_PREFIX) > -1) return GetTextFromUrl (url); if (url == "root:") { // display an index of sub-nodes. StringBuilder buf = new StringBuilder (); buf.Append ("

Mono Documentation Library

"); buf.Append ("

Available man pages:

").Append ("
"); foreach (Node n in Tree.Nodes) { buf.Append ("") .Append (n.Caption).Append ("
"); } buf.Append ("
"); return buf.ToString (); } return null; } protected string GetTextFromUrl (string url) { // Remove "man:" prefix including any help-source id on the front. int prefixStart = url.IndexOf(MAN_PREFIX); if (prefixStart > -1) url = url.Substring (prefixStart + 4); if (url == null || url.Length == 0) { Message (TraceLevel.Warning, "Warning, NULL url!"); return "url was null"; } Stream stream = GetHelpStream (url); return GetTextFromStream (stream); } public static string GetTextFromStream (Stream stream) { if (stream == null) return "url was null"; StreamReader file = new StreamReader(stream); string line; StateInfo s = new StateInfo (); while ((line = file.ReadLine ()) != null) { ProcessLine (line, s); } return s.output.ToString (); } enum ListState { None, Start, Title, } class StateInfo { public ListState ls; public Stack tags = new Stack (); public StringBuilder output = new StringBuilder (); } private static void ProcessLine (string line, StateInfo s) { string[] parts = SplitLine (line); switch (parts [0]) { case ".\\\"": // comments case ".de": // define macro case ".if": // if case ".ne": // ??? case "..": // end macro // ignore break; case ".I": s.output.Append (""); Translate (parts, 1, s.output); s.output.Append (""); break; case ".B": s.output.Append (""); Translate (parts, 1, s.output); s.output.Append (""); break; case ".br": Translate (parts, 1, s.output); s.output.Append ("
"); break; case ".nf": Expect (s, "

"); s.output.Append ("
\n");
				s.tags.Push ("
"); break; case ".fi": Expect (s, ""); break; case ".PP": Expect (s, "

", "", ""); goto case ".Sp"; case ".Sp": Expect (s, "

"); s.output.Append ("

"); Translate (parts, 1, s.output); s.tags.Push ("

"); break; case ".RS": Expect (s, "

"); s.output.Append ("
"); s.tags.Push ("
"); break; case ".RE": ClearUntil (s, ""); break; case ".SH": ClearAll (s); s.output.Append ("

"); Translate (parts, 1, s.output); s.output.Append ("

") .Append ("
"); s.tags.Push ("
"); break; case ".SS": s.output.Append ("

"); Translate (parts, 1, s.output); s.output.Append ("

"); break; case ".TH": { ClearAll (s); string name = "", extra = ""; if (parts.Length >= 4 && parts [2].Trim ().Length == 0) { name = parts [1] + "(" + parts [3] + ")"; if (parts.Length > 4) { int start = 4; if (parts [start].Trim ().Length == 0) ++start; extra = string.Join ("", parts, start, parts.Length-start); } } else name = string.Join ("", parts, 1, parts.Length-1); s.output.Append ("" + "\n" + "
Manual Pages

"); Translate (name, s.output); s.output.Append ("

"); Translate (extra, s.output); s.output.Append ("
"); break; } case ".TP": Expect (s, "

"); if (s.tags.Count > 0 && s.tags.Peek ().ToString () != "") { s.output.Append ("
"); s.tags.Push ("
"); } else Expect (s, ""); s.output.Append ("
"); s.tags.Push ("
"); s.ls = ListState.Start; break; default: Translate (line, s.output); break; } if (s.ls == ListState.Start) s.ls = ListState.Title; else if (s.ls == ListState.Title) { Expect (s, ""); s.output.Append ("
"); s.tags.Push ("
"); s.ls = ListState.None; } s.output.Append ("\n"); } private static string[] SplitLine (string line) { if (line.Length > 1 && line [0] != '.') return new string[]{null, line}; int i; for (i = 0; i < line.Length; ++i) { if (char.IsWhiteSpace (line, i)) break; } if (i == line.Length) return new string[]{line}; ArrayList pieces = new ArrayList (); pieces.Add (line.Substring (0, i)); bool inQuotes = false; bool prevWs = true; ++i; int start = i; for ( ; i < line.Length; ++i) { char c = line [i]; if (inQuotes) { if (c == '"') { Add (pieces, line, start, i); start = i+1; inQuotes = false; } } else { if (prevWs && c == '"') { Add (pieces, line, start, i); start = i+1; inQuotes = true; } else if (char.IsWhiteSpace (c)) { if (!prevWs) { Add (pieces, line, start, i); start = i; } prevWs = true; } else { if (prevWs) { Add (pieces, line, start, i); start = i; } prevWs = false; } } } if (start > 0 && start != line.Length) pieces.Add (line.Substring (start, line.Length-start)); return (string[]) pieces.ToArray (typeof(string)); } private static void Add (ArrayList pieces, string line, int start, int end) { if (start == end) return; pieces.Add (line.Substring (start, end-start)); } private static void Expect (StateInfo s, params string[] expected) { string e; while (s.tags.Count > 0 && Array.IndexOf (expected, (e = s.tags.Peek ().ToString ())) >= 0) { s.output.Append (s.tags.Pop ().ToString ()); } } private static void ClearUntil (StateInfo s, string required) { string e; while (s.tags.Count > 0 && (e = s.tags.Peek ().ToString ()) != required) { s.output.Append (s.tags.Pop ().ToString ()); } if (e == required) s.output.Append (s.tags.Pop ().ToString ()); } private static void ClearAll (StateInfo s) { while (s.tags.Count > 0) s.output.Append (s.tags.Pop ().ToString ()); } private static void Translate (string[] lines, int startIndex, StringBuilder output) { if (lines.Length <= startIndex) return; do { Translate (lines [startIndex++], output); if (startIndex == lines.Length) break; } while (startIndex < lines.Length); } private static void Translate (string line, StringBuilder output) { string span = null; int start = output.Length; for (int i = 0; i < line.Length; ++i) { switch (line [i]) { case '\\': { if ((i+2) < line.Length && line [i+1] == 'f') { if (line [i+2] == 'I') { output.Append (""); span = ""; } else if (line [i+2] == 'B') { output.Append (""); span = ""; } else if (line [i+2] == 'R' || line [i+2] == 'P') { output.Append (span); } else goto default; i += 2; } else if ((i+1) < line.Length) { output.Append (line [i+1]); ++i; } else goto default; break; } case '<': output.Append ("<"); break; case '>': output.Append (">"); break; case '&': output.Append ("&"); break; default: output.Append (line [i]); break; } } } } }