Merge pull request #487 from mayerwin/patch-1
[mono.git] / mcs / tools / monkeydoc / Monkeydoc / generators / html / Man2Html.cs
1 using System;
2 using System.IO;
3 using System.Text;
4 using System.Collections.Generic;
5
6 using MonkeyDoc;
7 using MonkeyDoc.Generators;
8
9 namespace MonkeyDoc.Generators.Html
10 {
11         public class Man2Html : IHtmlExporter
12         {
13                 public string CssCode {
14                         get {
15                                 return string.Empty;
16                         }
17                 }
18
19                 public string Export (Stream input, Dictionary<string, string> extraArgs)
20                 {
21                         if (input == null)
22                                 return null;
23                         return GetTextFromReader (new StreamReader (input));
24                 }
25
26                 public string Export (string input, Dictionary<string, string> extraArgs)
27                 {
28                         if (string.IsNullOrEmpty (input))
29                                 return null;
30                         return GetTextFromReader (new StringReader (input));
31                 }
32
33                 public static string GetTextFromReader (TextReader file)
34                 {
35                         string line;
36                         StateInfo s = new StateInfo ();
37
38                         while ((line = file.ReadLine ()) != null)
39                                 ProcessLine (line, s);
40
41                         return s.output.ToString ();
42                 }
43
44                 enum ListState {
45                         None,
46                         Start,
47                         Title,
48                 }
49
50                 class StateInfo {
51                         public ListState ls;
52                         public Stack<string> tags = new Stack<string> ();
53                         public StringBuilder output = new StringBuilder ();
54                 }
55
56                 static void ProcessLine (string line, StateInfo s)
57                 {
58                         string[] parts = SplitLine (line);
59                         switch (parts [0]) {
60                         case ".\\\"": // comments
61                         case ".de":   // define macro
62                         case ".if":   // if
63                         case ".ne":   // ???
64                         case "..":    // end macro
65                                 // ignore
66                                 break;
67                         case ".I":
68                                 s.output.Append ("<i>");
69                                 Translate (parts, 1, s.output);
70                                 s.output.Append ("</i>");
71                                 break;
72                         case ".B":
73                                 s.output.Append ("<b>");
74                                 Translate (parts, 1, s.output);
75                                 s.output.Append ("</b>");
76                                 break;
77                         case ".br":
78                                 Translate (parts, 1, s.output);
79                                 s.output.Append ("<br />");
80                                 break;
81                         case ".nf":
82                                 Expect (s, "</p>");
83                                 s.output.Append ("<pre>\n");
84                                 s.tags.Push ("</pre>");
85                                 break;
86                         case ".fi":
87                                 Expect (s, "</pre>");
88                                 break;
89                         case ".PP":
90                                 Expect (s, "</p>", "</dd>", "</dl>");
91                                 goto case ".Sp";
92                         case ".Sp":
93                                 Expect (s, "</p>");
94                                 s.output.Append ("<p>");
95                                 Translate (parts, 1, s.output);
96                                 s.tags.Push ("</p>");
97                                 break;
98                         case ".RS":
99                                 Expect (s, "</p>");
100                                 s.output.Append ("<blockquote>");
101                                 s.tags.Push ("</blockquote>");
102                                 break;
103                         case ".RE":
104                                 ClearUntil (s, "</blockquote>");
105                                 break;
106                         case ".SH":
107                                 ClearAll (s);
108                                 s.output.Append ("<h2>");
109                                 Translate (parts, 1, s.output);
110                                 s.output.Append ("</h2>")
111                                         .Append ("<blockquote>");
112                                 s.tags.Push ("</blockquote>");
113                                 break;
114                         case ".SS":
115                                 s.output.Append ("<h3>");
116                                 Translate (parts, 1, s.output);
117                                 s.output.Append ("</h3>");
118                                 break;
119                         case ".TH": {
120                                 ClearAll (s);
121                                 string name = "", extra = "";
122                                 if (parts.Length >= 4 && parts [2].Trim ().Length == 0) {
123                                         name = parts [1] + "(" + parts [3] + ")";
124                                         if (parts.Length > 4) {
125                                                 int start = 4;
126                                                 if (parts [start].Trim ().Length == 0)
127                                                         ++start;
128                                                 extra = string.Join ("", parts, start, parts.Length-start);
129                                         }
130                                 }
131                                 else
132                                         name = string.Join ("", parts, 1, parts.Length-1);
133                                 s.output.Append ("<table width=\"100%\" bgcolor=\"#b0c4da\">" + 
134                                                  "<tr colspan=\"2\"><td>Manual Pages</td></tr>\n" +
135                                                  "<tr><td><h3>");
136                                 Translate (name, s.output);
137                                 s.output.Append ("</h3></td><td align=\"right\">");
138                                 Translate (extra, s.output);
139                                 s.output.Append ("</td></tr></table>");
140                                 break;
141                         }
142                         case ".TP":
143                                 Expect (s, "</p>");
144                                 if (s.tags.Count > 0 && s.tags.Peek ().ToString () != "</dd>") {
145                                         s.output.Append ("<dl>");
146                                         s.tags.Push ("</dl>");
147                                 }
148                                 else
149                                         Expect (s, "</dd>");
150                                 s.output.Append ("<dt>");
151                                 s.tags.Push ("</dt>");
152                                 s.ls = ListState.Start;
153                                 break;
154                         default:
155                                 Translate (line, s.output);
156                                 break;
157                         }
158                         if (s.ls == ListState.Start)
159                                 s.ls = ListState.Title;
160                         else if (s.ls == ListState.Title) {
161                                 Expect (s, "</dt>");
162                                 s.output.Append ("<dd>");
163                                 s.tags.Push ("</dd>");
164                                 s.ls = ListState.None;
165                         }
166                         s.output.Append ("\n");
167                 }
168
169                 static string[] SplitLine (string line)
170                 {
171                         if (line.Length > 1 && line [0] != '.')
172                                 return new string[]{null, line};
173
174                         int i;
175                         for (i = 0; i < line.Length; ++i) {
176                                 if (char.IsWhiteSpace (line, i))
177                                         break;
178                         }
179
180                         if (i == line.Length)
181                                 return new string[]{line};
182
183                         var pieces = new List<string> ();
184                         pieces.Add (line.Substring (0, i));
185                         bool inQuotes = false;
186                         bool prevWs   = true;
187                         ++i;
188                         int start = i;
189                         for ( ; i < line.Length; ++i) {
190                                 char c = line [i];
191                                 if (inQuotes) {
192                                         if (c == '"') {
193                                                 Add (pieces, line, start, i);
194                                                 start = i+1;
195                                                 inQuotes = false;
196                                         }
197                                 }
198                                 else {
199                                         if (prevWs && c == '"') {
200                                                 Add (pieces, line, start, i);
201                                                 start = i+1;
202                                                 inQuotes = true;
203                                         }
204                                         else if (char.IsWhiteSpace (c)) {
205                                                 if (!prevWs) {
206                                                         Add (pieces, line, start, i);
207                                                         start = i;
208                                                 }
209                                                 prevWs = true;
210                                         }
211                                         else {
212                                                 if (prevWs) {
213                                                         Add (pieces, line, start, i);
214                                                         start = i;
215                                                 }
216                                                 prevWs = false;
217                                         }
218                                 }
219                         }
220                         if (start > 0 && start != line.Length)
221                                 pieces.Add (line.Substring (start, line.Length-start));
222                         return pieces.ToArray ();
223                 }
224
225                 static void Add (List<string> pieces, string line, int start, int end)
226                 {
227                         if (start == end)
228                                 return;
229                         pieces.Add (line.Substring (start, end-start));
230                 }
231
232                 static void Expect (StateInfo s, params string[] expected)
233                 {
234                         string e;
235                         while (s.tags.Count > 0 && 
236                                Array.IndexOf (expected, (e = s.tags.Peek ().ToString ())) >= 0) {
237                                 s.output.Append (s.tags.Pop ().ToString ());
238                         }
239                 }
240
241                 static void ClearUntil (StateInfo s, string required)
242                 {
243                         string e;
244                         while (s.tags.Count > 0 && 
245                                (e = s.tags.Peek ().ToString ()) != required) {
246                                 s.output.Append (s.tags.Pop ().ToString ());
247                         }
248                         if (e == required)
249                                 s.output.Append (s.tags.Pop ().ToString ());
250                 }
251
252                 static void ClearAll (StateInfo s)
253                 {
254                         while (s.tags.Count > 0)
255                                 s.output.Append (s.tags.Pop ().ToString ());
256                 }
257
258                 static void Translate (string[] lines, int startIndex, StringBuilder output)
259                 {
260                         if (lines.Length <= startIndex)
261                                 return;
262                         do {
263                                 Translate (lines [startIndex++], output);
264                                 if (startIndex == lines.Length)
265                                         break;
266                         } while (startIndex < lines.Length);
267                 }
268
269                 static void Translate (string line, StringBuilder output)
270                 {
271                         string span = null;
272                         int start = output.Length;
273                         for (int i = 0; i < line.Length; ++i) {
274                                 switch (line [i]) {
275                                 case '\\': {
276                                         if ((i+2) < line.Length && line [i+1] == 'f') {
277                                                 if (line [i+2] == 'I') {
278                                                         output.Append ("<i>");
279                                                         span = "</i>";
280                                                 }
281                                                 else if (line [i+2] == 'B') {
282                                                         output.Append ("<b>");
283                                                         span = "</b>";
284                                                 }
285                                                 else if (line [i+2] == 'R' || line [i+2] == 'P') {
286                                                         output.Append (span);
287                                                 }
288                                                 else
289                                                         goto default;
290                                                 i += 2;
291                                         }
292                                         else if ((i+1) < line.Length) {
293                                                 output.Append (line [i+1]);
294                                                 ++i;
295                                         }
296                                         else
297                                                 goto default;
298                                         break;
299                                 }
300                                 case '<':
301                                         output.Append ("&lt;");
302                                         break;
303                                 case '>':
304                                         output.Append ("&gt;");
305                                         break;
306                                 case '&':
307                                         output.Append ("&amp;");
308                                         break;
309                                 default:
310                                         output.Append (line [i]);
311                                         break;
312                                 }
313                         }
314                 }
315         }
316 }