PageRenderTime 55ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/tools/monodoc/Monodoc/man-provider.cs

https://github.com/sdether/mono
C# | 408 lines | 348 code | 42 blank | 18 comment | 86 complexity | ac0ac2b5dff1a9d17db3ffddcafb74a1 MD5 | raw file
  1. //
  2. // A provider to display man pages
  3. //
  4. // Authors:
  5. // Johannes Roith <johannes@roith.de>
  6. // Jonathan Pryor <jpryor@novell.com>
  7. //
  8. // (C) 2008 Novell, Inc.
  9. namespace Monodoc {
  10. using System;
  11. using System.Collections;
  12. using System.Diagnostics;
  13. using System.IO;
  14. using System.Text;
  15. using System.Xml;
  16. //
  17. // The simple provider generates the information source
  18. //
  19. public class ManProvider : Provider {
  20. string[] tocFiles;
  21. public ManProvider (string[] handbookTocFiles)
  22. {
  23. tocFiles = handbookTocFiles;
  24. // huh...
  25. if (!File.Exists (tocFiles[0]))
  26. throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFiles[0]));
  27. }
  28. public override void PopulateTree (Tree tree)
  29. {
  30. foreach(string TocFile in tocFiles) {
  31. XmlDocument doc = new XmlDocument();
  32. doc.Load(TocFile);
  33. XmlNodeList nodeList = doc.GetElementsByTagName("manpage");
  34. Node nodeToAddChildrenTo = tree;
  35. foreach(XmlNode node in nodeList) {
  36. XmlAttribute name = node.Attributes["name"];
  37. XmlAttribute page = node.Attributes["page"];
  38. if (name == null || page == null) continue;
  39. if (!File.Exists (page.Value))
  40. continue;
  41. string target = "man:" + name.Value;
  42. nodeToAddChildrenTo.CreateNode (name.Value, target);
  43. if (File.Exists(page.Value))
  44. nodeToAddChildrenTo.tree.HelpSource.PackFile (page.Value, name.Value);
  45. }
  46. }
  47. }
  48. public override void CloseTree (HelpSource hs, Tree tree)
  49. {
  50. }
  51. }
  52. //
  53. // The HelpSource is used during the rendering phase.
  54. //
  55. public class ManHelpSource : HelpSource {
  56. public ManHelpSource (string base_file, bool create) : base (base_file, create) {}
  57. protected const string MAN_PREFIX = "man:";
  58. public override string GetText (string url, out Node match_node)
  59. {
  60. match_node = null;
  61. string c = GetCachedText (url);
  62. if (c != null)
  63. return c;
  64. if (url.IndexOf (MAN_PREFIX) > -1)
  65. return GetTextFromUrl (url);
  66. if (url == "root:") {
  67. // display an index of sub-nodes.
  68. StringBuilder buf = new StringBuilder ();
  69. buf.Append ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
  70. buf.Append ("<p>Available man pages:</p>").Append ("<blockquote>");
  71. foreach (Node n in Tree.Nodes) {
  72. buf.Append ("<a href=\"").Append (n.Element).Append ("\">")
  73. .Append (n.Caption).Append ("</a><br/>");
  74. }
  75. buf.Append ("</blockquote>");
  76. return buf.ToString ();
  77. }
  78. return null;
  79. }
  80. protected string GetTextFromUrl (string url)
  81. {
  82. // Remove "man:" prefix including any help-source id on the front.
  83. int prefixStart = url.IndexOf(MAN_PREFIX);
  84. if (prefixStart > -1)
  85. url = url.Substring (prefixStart + 4);
  86. if (url == null || url.Length == 0)
  87. {
  88. Message (TraceLevel.Warning, "Warning, NULL url!");
  89. return null;
  90. }
  91. Stream stream = GetHelpStream (url);
  92. return GetTextFromStream (stream);
  93. }
  94. public static string GetTextFromStream (Stream stream)
  95. {
  96. if (stream == null)
  97. return null;
  98. StreamReader file = new StreamReader(stream);
  99. string line;
  100. StateInfo s = new StateInfo ();
  101. while ((line = file.ReadLine ()) != null) {
  102. ProcessLine (line, s);
  103. }
  104. return s.output.ToString ();
  105. }
  106. enum ListState {
  107. None,
  108. Start,
  109. Title,
  110. }
  111. class StateInfo {
  112. public ListState ls;
  113. public Stack tags = new Stack ();
  114. public StringBuilder output = new StringBuilder ();
  115. }
  116. private static void ProcessLine (string line, StateInfo s)
  117. {
  118. string[] parts = SplitLine (line);
  119. switch (parts [0]) {
  120. case ".\\\"": // comments
  121. case ".de": // define macro
  122. case ".if": // if
  123. case ".ne": // ???
  124. case "..": // end macro
  125. // ignore
  126. break;
  127. case ".I":
  128. s.output.Append ("<i>");
  129. Translate (parts, 1, s.output);
  130. s.output.Append ("</i>");
  131. break;
  132. case ".B":
  133. s.output.Append ("<b>");
  134. Translate (parts, 1, s.output);
  135. s.output.Append ("</b>");
  136. break;
  137. case ".br":
  138. Translate (parts, 1, s.output);
  139. s.output.Append ("<br />");
  140. break;
  141. case ".nf":
  142. Expect (s, "</p>");
  143. s.output.Append ("<pre>\n");
  144. s.tags.Push ("</pre>");
  145. break;
  146. case ".fi":
  147. Expect (s, "</pre>");
  148. break;
  149. case ".PP":
  150. Expect (s, "</p>", "</dd>", "</dl>");
  151. goto case ".Sp";
  152. case ".Sp":
  153. Expect (s, "</p>");
  154. s.output.Append ("<p>");
  155. Translate (parts, 1, s.output);
  156. s.tags.Push ("</p>");
  157. break;
  158. case ".RS":
  159. Expect (s, "</p>");
  160. s.output.Append ("<blockquote>");
  161. s.tags.Push ("</blockquote>");
  162. break;
  163. case ".RE":
  164. ClearUntil (s, "</blockquote>");
  165. break;
  166. case ".SH":
  167. ClearAll (s);
  168. s.output.Append ("<h2>");
  169. Translate (parts, 1, s.output);
  170. s.output.Append ("</h2>")
  171. .Append ("<blockquote>");
  172. s.tags.Push ("</blockquote>");
  173. break;
  174. case ".SS":
  175. s.output.Append ("<h3>");
  176. Translate (parts, 1, s.output);
  177. s.output.Append ("</h3>");
  178. break;
  179. case ".TH": {
  180. ClearAll (s);
  181. string name = "", extra = "";
  182. if (parts.Length >= 4 && parts [2].Trim ().Length == 0) {
  183. name = parts [1] + "(" + parts [3] + ")";
  184. if (parts.Length > 4) {
  185. int start = 4;
  186. if (parts [start].Trim ().Length == 0)
  187. ++start;
  188. extra = string.Join ("", parts, start, parts.Length-start);
  189. }
  190. }
  191. else
  192. name = string.Join ("", parts, 1, parts.Length-1);
  193. s.output.Append ("<table width=\"100%\" bgcolor=\"#b0c4da\">" +
  194. "<tr colspan=\"2\"><td>Manual Pages</td></tr>\n" +
  195. "<tr><td><h3>");
  196. Translate (name, s.output);
  197. s.output.Append ("</h3></td><td align=\"right\">");
  198. Translate (extra, s.output);
  199. s.output.Append ("</td></tr></table>");
  200. break;
  201. }
  202. case ".TP":
  203. Expect (s, "</p>");
  204. if (s.tags.Count > 0 && s.tags.Peek ().ToString () != "</dd>") {
  205. s.output.Append ("<dl>");
  206. s.tags.Push ("</dl>");
  207. }
  208. else
  209. Expect (s, "</dd>");
  210. s.output.Append ("<dt>");
  211. s.tags.Push ("</dt>");
  212. s.ls = ListState.Start;
  213. break;
  214. default:
  215. Translate (line, s.output);
  216. break;
  217. }
  218. if (s.ls == ListState.Start)
  219. s.ls = ListState.Title;
  220. else if (s.ls == ListState.Title) {
  221. Expect (s, "</dt>");
  222. s.output.Append ("<dd>");
  223. s.tags.Push ("</dd>");
  224. s.ls = ListState.None;
  225. }
  226. s.output.Append ("\n");
  227. }
  228. private static string[] SplitLine (string line)
  229. {
  230. if (line.Length > 1 && line [0] != '.')
  231. return new string[]{null, line};
  232. int i;
  233. for (i = 0; i < line.Length; ++i) {
  234. if (char.IsWhiteSpace (line, i))
  235. break;
  236. }
  237. if (i == line.Length)
  238. return new string[]{line};
  239. ArrayList pieces = new ArrayList ();
  240. pieces.Add (line.Substring (0, i));
  241. bool inQuotes = false;
  242. bool prevWs = true;
  243. ++i;
  244. int start = i;
  245. for ( ; i < line.Length; ++i) {
  246. char c = line [i];
  247. if (inQuotes) {
  248. if (c == '"') {
  249. Add (pieces, line, start, i);
  250. start = i+1;
  251. inQuotes = false;
  252. }
  253. }
  254. else {
  255. if (prevWs && c == '"') {
  256. Add (pieces, line, start, i);
  257. start = i+1;
  258. inQuotes = true;
  259. }
  260. else if (char.IsWhiteSpace (c)) {
  261. if (!prevWs) {
  262. Add (pieces, line, start, i);
  263. start = i;
  264. }
  265. prevWs = true;
  266. }
  267. else {
  268. if (prevWs) {
  269. Add (pieces, line, start, i);
  270. start = i;
  271. }
  272. prevWs = false;
  273. }
  274. }
  275. }
  276. if (start > 0 && start != line.Length)
  277. pieces.Add (line.Substring (start, line.Length-start));
  278. return (string[]) pieces.ToArray (typeof(string));
  279. }
  280. private static void Add (ArrayList pieces, string line, int start, int end)
  281. {
  282. if (start == end)
  283. return;
  284. pieces.Add (line.Substring (start, end-start));
  285. }
  286. private static void Expect (StateInfo s, params string[] expected)
  287. {
  288. string e;
  289. while (s.tags.Count > 0 &&
  290. Array.IndexOf (expected, (e = s.tags.Peek ().ToString ())) >= 0) {
  291. s.output.Append (s.tags.Pop ().ToString ());
  292. }
  293. }
  294. private static void ClearUntil (StateInfo s, string required)
  295. {
  296. string e;
  297. while (s.tags.Count > 0 &&
  298. (e = s.tags.Peek ().ToString ()) != required) {
  299. s.output.Append (s.tags.Pop ().ToString ());
  300. }
  301. if (e == required)
  302. s.output.Append (s.tags.Pop ().ToString ());
  303. }
  304. private static void ClearAll (StateInfo s)
  305. {
  306. while (s.tags.Count > 0)
  307. s.output.Append (s.tags.Pop ().ToString ());
  308. }
  309. private static void Translate (string[] lines, int startIndex, StringBuilder output)
  310. {
  311. if (lines.Length <= startIndex)
  312. return;
  313. do {
  314. Translate (lines [startIndex++], output);
  315. if (startIndex == lines.Length)
  316. break;
  317. } while (startIndex < lines.Length);
  318. }
  319. private static void Translate (string line, StringBuilder output)
  320. {
  321. string span = null;
  322. int start = output.Length;
  323. for (int i = 0; i < line.Length; ++i) {
  324. switch (line [i]) {
  325. case '\\': {
  326. if ((i+2) < line.Length && line [i+1] == 'f') {
  327. if (line [i+2] == 'I') {
  328. output.Append ("<i>");
  329. span = "</i>";
  330. }
  331. else if (line [i+2] == 'B') {
  332. output.Append ("<b>");
  333. span = "</b>";
  334. }
  335. else if (line [i+2] == 'R' || line [i+2] == 'P') {
  336. output.Append (span);
  337. }
  338. else
  339. goto default;
  340. i += 2;
  341. }
  342. else if ((i+1) < line.Length) {
  343. output.Append (line [i+1]);
  344. ++i;
  345. }
  346. else
  347. goto default;
  348. break;
  349. }
  350. case '<':
  351. output.Append ("&lt;");
  352. break;
  353. case '>':
  354. output.Append ("&gt;");
  355. break;
  356. case '&':
  357. output.Append ("&amp;");
  358. break;
  359. default:
  360. output.Append (line [i]);
  361. break;
  362. }
  363. }
  364. }
  365. }
  366. }