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