using System; using System.IO; using System.Text; using System.Collections.Generic; using Monodoc; using Monodoc.Generators; namespace Monodoc.Generators.Html { public class Man2Html : IHtmlExporter { public string CssCode { get { return string.Empty; } } public string Export (Stream input, Dictionary extraArgs) { if (input == null) return null; return GetTextFromReader (new StreamReader (input)); } public string Export (string input, Dictionary extraArgs) { if (string.IsNullOrEmpty (input)) return null; return GetTextFromReader (new StringReader (input)); } public static string GetTextFromReader (TextReader file) { 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 (); } 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"); } 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}; var pieces = new List (); 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 pieces.ToArray (); } static void Add (List pieces, string line, int start, int end) { if (start == end) return; pieces.Add (line.Substring (start, end-start)); } 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 ()); } } 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 ()); } static void ClearAll (StateInfo s) { while (s.tags.Count > 0) s.output.Append (s.tags.Pop ().ToString ()); } 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); } 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; } } } } }