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