using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using Monodoc;
using Monodoc.Generators;
namespace Monodoc.Generators.Html
{
public class Man2Html : IHtmlExporter
{
public string CssCode {
get {
return string.Empty;
}
}
public string Export (Stream input, Dictionary extraArgs)
{
if (input == null)
return null;
return GetTextFromReader (new StreamReader (input));
}
public string Export (string input, Dictionary extraArgs)
{
if (string.IsNullOrEmpty (input))
return null;
return GetTextFromReader (new StringReader (input));
}
public static string GetTextFromReader (TextReader file)
{
string line;
StateInfo s = new StateInfo ();
while ((line = file.ReadLine ()) != null)
ProcessLine (line, s);
return s.output.ToString ();
}
enum ListState {
None,
Start,
Title,
}
class StateInfo {
public ListState ls;
public Stack tags = new Stack ();
public StringBuilder output = new StringBuilder ();
}
static void ProcessLine (string line, StateInfo s)
{
string[] parts = SplitLine (line);
switch (parts [0]) {
case ".\\\"": // comments
case ".de": // define macro
case ".if": // if
case ".ne": // ???
case "..": // end macro
// ignore
break;
case ".I":
s.output.Append ("");
Translate (parts, 1, s.output);
s.output.Append ("");
break;
case ".B":
s.output.Append ("");
Translate (parts, 1, s.output);
s.output.Append ("");
break;
case ".br":
Translate (parts, 1, s.output);
s.output.Append ("
");
break;
case ".nf":
Expect (s, "
");
s.output.Append ("\n");
s.tags.Push ("
");
break;
case ".fi":
Expect (s, "");
break;
case ".PP":
Expect (s, "", "", "");
goto case ".Sp";
case ".Sp":
Expect (s, "");
s.output.Append ("");
Translate (parts, 1, s.output);
s.tags.Push ("
");
break;
case ".RS":
Expect (s, "");
s.output.Append ("");
s.tags.Push ("
");
break;
case ".RE":
ClearUntil (s, "");
break;
case ".SH":
ClearAll (s);
s.output.Append ("");
Translate (parts, 1, s.output);
s.output.Append ("
")
.Append ("");
s.tags.Push ("
");
break;
case ".SS":
s.output.Append ("");
Translate (parts, 1, s.output);
s.output.Append ("
");
break;
case ".TH": {
ClearAll (s);
string name = "", extra = "";
if (parts.Length >= 4 && parts [2].Trim ().Length == 0) {
name = parts [1] + "(" + parts [3] + ")";
if (parts.Length > 4) {
int start = 4;
if (parts [start].Trim ().Length == 0)
++start;
extra = string.Join ("", parts, start, parts.Length-start);
}
}
else
name = string.Join ("", parts, 1, parts.Length-1);
s.output.Append ("" +
"Manual Pages |
\n" +
"");
Translate (name, s.output);
s.output.Append (" | ");
Translate (extra, s.output);
s.output.Append (" |
");
break;
}
case ".TP":
Expect (s, "");
if (s.tags.Count > 0 && s.tags.Peek ().ToString () != "") {
s.output.Append ("");
s.tags.Push ("
");
}
else
Expect (s, "");
s.output.Append ("");
s.tags.Push ("");
s.ls = ListState.Start;
break;
default:
Translate (line, s.output);
break;
}
if (s.ls == ListState.Start)
s.ls = ListState.Title;
else if (s.ls == ListState.Title) {
Expect (s, "");
s.output.Append ("");
s.tags.Push ("");
s.ls = ListState.None;
}
s.output.Append ("\n");
}
static string[] SplitLine (string line)
{
if (line.Length > 1 && line [0] != '.')
return new string[]{null, line};
int i;
for (i = 0; i < line.Length; ++i) {
if (char.IsWhiteSpace (line, i))
break;
}
if (i == line.Length)
return new string[]{line};
var pieces = new List ();
pieces.Add (line.Substring (0, i));
bool inQuotes = false;
bool prevWs = true;
++i;
int start = i;
for ( ; i < line.Length; ++i) {
char c = line [i];
if (inQuotes) {
if (c == '"') {
Add (pieces, line, start, i);
start = i+1;
inQuotes = false;
}
}
else {
if (prevWs && c == '"') {
Add (pieces, line, start, i);
start = i+1;
inQuotes = true;
}
else if (char.IsWhiteSpace (c)) {
if (!prevWs) {
Add (pieces, line, start, i);
start = i;
}
prevWs = true;
}
else {
if (prevWs) {
Add (pieces, line, start, i);
start = i;
}
prevWs = false;
}
}
}
if (start > 0 && start != line.Length)
pieces.Add (line.Substring (start, line.Length-start));
return pieces.ToArray ();
}
static void Add (List pieces, string line, int start, int end)
{
if (start == end)
return;
pieces.Add (line.Substring (start, end-start));
}
static void Expect (StateInfo s, params string[] expected)
{
string e;
while (s.tags.Count > 0 &&
Array.IndexOf (expected, (e = s.tags.Peek ().ToString ())) >= 0) {
s.output.Append (s.tags.Pop ().ToString ());
}
}
static void ClearUntil (StateInfo s, string required)
{
string e = null;
while (s.tags.Count > 0 &&
(e = s.tags.Peek ().ToString ()) != required) {
s.output.Append (s.tags.Pop ().ToString ());
}
if (e == required)
s.output.Append (s.tags.Pop ().ToString ());
}
static void ClearAll (StateInfo s)
{
while (s.tags.Count > 0)
s.output.Append (s.tags.Pop ().ToString ());
}
static void Translate (string[] lines, int startIndex, StringBuilder output)
{
if (lines.Length <= startIndex)
return;
do {
Translate (lines [startIndex++], output);
if (startIndex == lines.Length)
break;
} while (startIndex < lines.Length);
}
static void Translate (string line, StringBuilder output)
{
string span = null;
int start = output.Length;
for (int i = 0; i < line.Length; ++i) {
switch (line [i]) {
case '\\': {
if ((i+2) < line.Length && line [i+1] == 'f') {
if (line [i+2] == 'I') {
output.Append ("");
span = "";
}
else if (line [i+2] == 'B') {
output.Append ("");
span = "";
}
else if (line [i+2] == 'R' || line [i+2] == 'P') {
output.Append (span);
}
else
goto default;
i += 2;
}
else if ((i+1) < line.Length) {
output.Append (line [i+1]);
++i;
}
else
goto default;
break;
}
case '<':
output.Append ("<");
break;
case '>':
output.Append (">");
break;
case '&':
output.Append ("&");
break;
default:
output.Append (line [i]);
break;
}
}
}
}
}