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