#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char *title;
char *string;
int offset, length;
int partype, nexttype;
int blanklines;
char peek;
int quotedepth;
int stylestack[3], stylecount;
int inited;
int inblock, inverse, reopen, tailpar, indiv;
int forcenewline;
int footnotecount, oldcount, infootnotes;
char footnoteref[3*sizeof(int) + 1];
char code[5];
int codelength;
int startofline;
int oldchar;

char *styles[] = {"i", "strong", "b"};
char *accent[1 << (8*sizeof(char))];

char *codefrom[] =
{
  "''", "<", ">",
  "**", "o", "L", "p", "-", "P", "ss", "(r)",
  "(c)", "TM", "AE", "oo", "+-", "<=", ">=", "Y",
  "mu", "d", "S", "Pi", "pi", "I", "a_", "o_",
  "O", "ae", "?", "!", "-.", "./", "f", "~~",
  "D", "OE", "oe", "/", "<>", ">0<", "++", ".",
  "%%" /* "@" */,
  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
};

char *codeto[] =
{
  "\"", "&lt;", "&gt;",
  "&dagger;", "&deg;", "&pound;", "&sect;",
  "&bull;", "&para;", "&szlig;", "&reg;",
  "&copy;", "&trade;", "&AElig;", "&infin;",
  "&plusmn;", "&le;", "&ge;", "&yen;",
  "&mu;", "&part;", "&Sigma;", "&Pi;",
  "&pi;", "&int;", "&ordf;", "&ordm;",
  "&Omega;", "&aelig;", "&iquest;", "&iexcl;",
  "&not;", "&radic;", "&fnof;", "&asymp;",
  "&Delta;", "&OElig;", "&oelig;", "&divide;",
  "&loz;", "&curren;", "&#8225;", "&middot;",
  "&permil;" /* apple logo */,
  "<sup>0</sup>",
  "<sup>1</sup>",
  "<sup>2</sup>",
  "<sup>3</sup>",
  "<sup>4</sup>",
  "<sup>5</sup>",
  "<sup>6</sup>",
  "<sup>7</sup>",
  "<sup>8</sup>",
  "<sup>9</sup>"
};

#define CODECOUNT ((int) (sizeof(codefrom)/sizeof(char *)))

char *header1 =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
      "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
    "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n"
    "<head>\n\n"
    "<title>",
  *header2 =
    "</title>\n\n"
    "<meta name=\"viewport\" content=\"user-scalable=no,width=device-width\">\n"
    "<style type=\"text/css\" media=\"all\"><!--\n"
    "body {font-size: 16px; line-height: 1.3em; max-width: 28em;"
    " padding: 2.5em 1em 2em 1em; margin: auto;}\n"
    "strong {font-variant: small-caps; font-weight: normal;}\n"
    "p {text-align: left; text-indent: 1em; margin: 0em;}\n"
    "@media all and (min-width: 30em) {p {text-align: justify}}\n"
    "p:first-child {text-indent: 0em;}\n"
    "p.center {text-align: center; text-indent: 0em;}\n"
    "p.right {text-align: right; text-indent: 0em;}\n",
  *header3 =
    "div {margin-bottom: 1.3em;}\n"
    "div.noskip {margin-bottom: 0em;}\n"
    "blockquote {margin: 0em 2em 0em 2em; line-height: 1.3em;}\n"
    "blockquote.verse p {text-align: left; text-indent: 0em;}\n"
    "span.indent {margin-left: 1em;}\n"
    "sup {line-height: 0em; font-size: 75%;}\n"
    "a {text-decoration: none;}\n"
    "hr {margin: 2.5em -1em 2.4em -1em; height: 1px;"
    " border: 0em; background-color: #999;}\n"
    "tt {font-size: 14px;}\n"
    "--></style>\n"
    "</head>\n"
    "<body>\n\n",
  *footer =
    "</body>\n"
    "</html>\n";

int
getchar1()
{
  int c;

  if ((c = oldchar) >= -1)
  {
    oldchar = -2;
    return c;
  }
  c = getchar();
  if (startofline)
  {
    if (infootnotes)
    {
      while (c != '|')
      {
        while ((c = getchar()) != EOF && c != '\n')
          ;
        if (c == EOF)
          break;
        c = getchar();
      }
      if (c != EOF)
      {
        c = getchar();
        if (c == ' ')
          c = getchar();
      }
    }
    else
    {
      while (c == '|' || c == '\t')
      {
        while ((c = getchar()) != EOF && c != '\n')
          ;
        if (c == EOF)
          break;
        c = getchar();
      }
    }
  }
  startofline = (c == '\n');
  return c;
}

void
ungetc1(c)
  int c;
{
  oldchar = c;
}

void
peekchar()
{
  peek = getchar1();
  ungetc1(peek);
}

void
error(s)
  char *s;
{
  fprintf(stderr, "%s\n", s);
  exit(1);
}

void
init()
{
  oldchar = -2;
  startofline = 1;
  length = 1;
  string = malloc(length);
  if (!string)
    error("out of memory");
  offset = 0;
  string[offset] = 0;
  accent['\''] = "acute";
  accent['`'] = "grave";
  accent['"'] = "uml";
  accent['^'] = "circ";
  accent['/'] = "slash";
  accent[','] = "cedil";
  accent['~'] = "tilde";
  accent['o'] = "ring";
}

void
addchar(c)
  char c;
{
  while (offset + 1 >= length)
  {
    length *= 2;
    string = realloc(string, length);
    if (!string)
      error("out of memory");
  }
  string[offset++] = c;
  string[offset] = 0;
}

void
addstring(s)
  char *s;
{
  char c;

  while ((c = *s++))
  {
    while (offset + 1 >= length)
    {
      length *= 2;
      string = realloc(string, length);
      if (!string)
        error("out of memory");
    }
    string[offset++] = c;
  }
  string[offset] = 0;
}

void
addchar1(c)
  char c;
{
  switch (c)
  {
  case '<':
    addstring("&lt;");
    break;
  case '>':
    addstring("&gt;");
    break;
  case '&':
    addstring("&amp;");
    break;
  default:
    addchar(c);
  }
}

void
reset()
{
  int i;

  offset = 0;
  string[offset] = 0;
  for (i = 0; i < stylecount; i++)
  {
    addstring("<");
    addstring(styles[stylestack[i]]);
    addstring(">");
  }
}

void
addstyle(o, i)
  int o, i;
{
  addstring(o ? "</" : "<");
  addstring(styles[i]);
  addchar('>');
}

void
dostyle(c)
  int c;
{
  int i, j, k;

  for (i = 0; i < stylecount; i++)
    if (stylestack[i] == c)
      break;
  if (i < stylecount)
  {
    for (j = stylecount - 1; j > i; j--)
      addstyle(1, stylestack[j]);
    addstyle(1, c);
    for (j = i; j < stylecount - 1; j++)
    {
      k = stylestack[j + 1];
      stylestack[j] = k;
      addstyle(0, k);
    }
    stylecount = j;
  }
  else
  {
    stylestack[stylecount++] = c;
    addstyle(0, c);
  }
}

int
noskip(type)
  int type;
{
  int o, c, n, r, savedoldchar, savedstartofline;

  o = ftell(stdin);
  savedoldchar = oldchar;
  savedstartofline = startofline;
  if (inblock)
  {
    while ((c = getchar1()) != EOF)
    {
      if (c == '\n')
      {
        n = 0;
        while ((c = getchar1()) != EOF && c == ' ')
          n++;
        if (n == 0 && c != '\n')
          continue;
        if (n != type &&
          !((n == 4 && type == 6) || (n == 6 && type == 4)) &&
          !((n == 5 && type == 10) || (n == 10 && type == 5)))
        {
          r = (n != 0);
          break;
        }
        if (c == EOF)
          break;
      }
    }
    if (c == EOF)
      r = 0;
  }
  else
  {
    while ((c = getchar1()) != EOF)
    {
      if (c == '\n')
      {
        n = 0;
        while ((c = getchar1()) != EOF && c == ' ')
          n++;
        if (n == 0 && c != '\n')
          continue;
        if (n == 0 || (n >= 4 && n <= 6))
        {
          r = (n != 0);
          break;
        }
        if (c == EOF)
          break;
      }
    }
    if (c == EOF)
      r = 0;
  }
  oldchar = savedoldchar;
  startofline = savedstartofline;
  fseek(stdin, o, SEEK_SET);
  return r;
}

void
printpar()
{
  int i;

  for (i = stylecount - 1; i >= 0; i--)
  {
    addstring("</");
    addstring(styles[stylestack[i]]);
    addstring(">");
  }
  if (partype >= 4 && partype <= 6)
  {
    if (partype != inblock &&
      !((partype == 4 && inblock == 6) || (partype == 6 && inblock == 4)) &&
      !((partype == 5 && inblock == 10) || (partype == 10 && inblock == 5)))
    {
      printf("<blockquote");
      if (partype != 5)
        printf(" class=\"verse\"");
      printf(">");
    }
    inblock = partype;
  }
  if (!indiv)
  {
    printf("<div");
    if (!blanklines && ((inblock && (nexttype != inblock &&
          !((nexttype == 4 && inblock == 6) ||
            (nexttype == 6 && inblock == 4)) &&
          !((nexttype == 10 && inblock == 5) ||
            (nexttype == 5 && inblock == 10)))) ||
        (!inblock && nexttype >= 4 && nexttype <= 6)
          || noskip(partype)))
      printf(" class=\"noskip\"");
    printf(">");
    tailpar = 0;
    indiv = 1;
  }
  if (!inverse || reopen)
  {
    printf("<p");
    inverse = (partype == 4 || partype == 6);
    reopen = 0;
    switch (partype)
    {
    case 8:
      printf(" class=\"center\"");
      break;
    case 10:
      printf(" class=\"right\"");
      break;
/*
    default:
      if (!tailpar && !inverse)
        printf(" class=\"noindent\"");
*/
    }
    printf(">");
  }
  if (inverse)
    printf("<span%s>", partype == 4 ? "" : " class=\"indent\"");
  printf("%s", string);
  if (inverse)
    printf("</span>");
  if (inverse && (nexttype == 4 || nexttype == 6) && !blanklines)
    printf("<br/>");
  else
  {
    printf("</p>");
    tailpar = (partype == nexttype);
    if (blanklines || (!inblock && nexttype >= 4 && nexttype <= 6))
    {
      printf("</div>");
      indiv = 0;
    }
    if (inverse)
      reopen = 1;
  }
  if (inblock && (blanklines > 1 || (partype != nexttype &&
    !((partype == 4 && nexttype == 6) || (partype == 6 && nexttype == 4)) &&
    !((partype == 5 && nexttype == 10) || (partype == 10 && nexttype == 5)))))
  {
    if (indiv)
    {
      printf("</div>");
      indiv = 0;
    }
    printf("</blockquote>");
    inblock = 0;
    inverse = 0;
  }
  printf("\n");
  if (blanklines)
  {
    printf("\n");
    blanklines--;
  }
  if (blanklines)
  {
    if (infootnotes)
      blanklines--;
    else
      printf("<hr/>\n");
  }
  while (blanklines)
  {
    printf("\n");
    blanklines--;
  }
  blanklines = 0;
  reset();
  partype = nexttype;
  nexttype = 0;
  forcenewline = 0;
}

void
process()
{
  int c, d, n, i, j;
  char *s;

  while ((c = getchar1()) != EOF)
  {
    if (c == '\t')
    {
      while ((c = getchar1()) != '\n')
        if (c == EOF)
        {
          ungetc1(c);
          break;
        }
      continue;
    }
    if (c == '\n')
    {
      blanklines++;
      continue;
    }
    if (c == ' ')
    {
      nexttype = 1;
      while ((c = getchar1()) != EOF)
      {
        if (c != ' ')
          break;
        nexttype++;
      }
      if (inited)
        printpar();
      else
      {
        inited = 1;
        blanklines = 0;
        partype = nexttype;
        nexttype = 0;
      }
      ungetc1(c);
    }
    else
    {
      addchar('\n');
      ungetc1(c);
    }
    while ((c = getchar1()) != EOF)
    {
      if (c == '\n')
        break;
      else
      switch (c)
      {
      case ' ':
        n = 1;
        while ((c = getchar1()) == ' ')
          n++;
        ungetc1(c);
        if (n < 3)
        {
          if (forcenewline)
            n--;
          while (n--)
            addchar(' ');
        }
        else
          addstring("&emsp;");
        if (forcenewline)
        {
          addchar('\n');
          forcenewline = 0;
        }
        break;
      case '-':
        peekchar();
        switch (peek)
        {
        case '-':
          (void) getchar1();
          peekchar();
          if (peek == '-')
          {
            (void) getchar1();
            addstring("&mdash;");
          }
          else
            addstring("&ndash;");
          break;
        case ' ':
          addchar('-');
          n = 0;
          while ((c = getchar1()) == ' ')
            n++;
          if (c == EOF)
            ungetc1(c);
          else
          if (c == '\n')
          {
            if (n == 1)
              forcenewline = 1;
            else
              addchar(c);
          }
          else
          {
            while (n--)
              addchar(' ');
            ungetc1(c);
          }
          break;
        case '\n':
          (void) getchar1();
          forcenewline = 1;
          break;
        default:
          addchar('-');
        }
        break;
      case '`':
        n = 1;
        peekchar();
        while (peek == '~')
        {
          quotedepth--;
          (void) getchar1();
          peekchar();
          if (peek == '`')
          {
            n++;
            (void) getchar1();
            peekchar();
          }
          else
            break;
        }
        while (n--)
        {
          addstring(quotedepth & 1 ? "&ldquo;" : "&lsquo;");
          quotedepth++;
        }
        break;
      case '\'':
        peekchar();
        if (isalnum(peek) || peek == '<' || peek == '"' || peek == '~')
          addstring("&rsquo;");
        else
        {
          if (quotedepth > 0)
            quotedepth--;
          else
            fprintf(stderr, "negative quote depth?\n");
          addstring(quotedepth & 1 ? "&rdquo;" : "&rsquo;");
        }
        break;
      case '_':
      case '=':
      case '*':
        dostyle(c == '_' ? 0 : c == '=' ? 1 : 2);
        break;
      case '<':
        while ((c = getchar1()) != '>')
        {
          if (c == EOF || c == '\n')
          {
            ungetc1(c);
            break;
          }
          d = getchar1();
          if (d == '>')
            break;
          else
          if (d == EOF || d == '\n')
          {
            ungetc1(d);
            break;
          }
          s = accent[d];
          if (s)
          {
            addchar('&');
            addchar(c);
            addstring(s);
            addchar(';');
          }
          else
          {
            addchar(c);
            addchar(d);
          }
        }
        break;
      case '"':
        i = 0;
        while ((c = getchar1()) != '"')
        {
          if (c == EOF || c == '\n')
          {
            ungetc1(c);
            break;
          }
          code[i++] = c;
          if (i > 4)
            break;
        }
        if (i < 4)
        {
          code[i] = 0;
          for (j = 0; j < CODECOUNT; j++)
            if (strcmp(code, codefrom[j]) == 0)
            {
              addstring(codeto[j]);
              break;
            }
          if (j >= CODECOUNT)
            for (j = 0; j < i; j++)
              addchar1(code[j]);
        }
        else
          for (j = 0; j < i; j++)
            addchar1(code[j]);
        break;
      case '~':
        break;
      case '&':
        addstring("&amp;");
        break;
      case '^':
        while ((c = getchar1()) != EOF)
        {
          if (c == '^')
            break;
          if (c == '"')
          {
            while ((c = getchar1()) != EOF)
              if (c == '\n' || c == '"')
                break;
          }
          else
          if (c == '<')
          {
            while ((c = getchar1()) != EOF)
              if (c == '\n' || c == '>')
                break;
          }
          if (c == '\n' || c == EOF)
          {
            fprintf(stderr, "unfinished footnote reference?\n");
            ungetc1(c);
            break;
          }
        }
        sprintf(footnoteref, "%d", ++footnotecount);
        if (infootnotes)
        {
          addstring("<a href=\"#r");
          addstring(footnoteref);
          addstring("\" id=\"f");
          addstring(footnoteref);
          addstring("\">");
          addstring(footnoteref);
          addstring(".</a> ");
        }
        else
        {
          addstring("<a href=\"#f");
          addstring(footnoteref);
          addstring("\" id=\"r");
          addstring(footnoteref);
          addstring("\"><sup>");
          addstring(footnoteref);
          addstring("</sup></a>");
        }
        break;
      case '{':
        addstring("<tt>");
        n = 1;
        peekchar();
        if (peek == '\n')
          (void) getchar1();
        else
        {
          while (peek == '{')
          {
            n++;
            (void) getchar1();
            peekchar();
          }
          if (n > 1 && peek == '[')
          {
            (void) getchar1();
            if (peek == '\n')
              (void) getchar1();
          }
          else
            for (i = 0; i < n - 1; i++)
              addchar('{');
        }
        while (n > 0)
        {
          if (oldchar >= -1)
          {
            c = oldchar;
            oldchar = -2;
          }
          else
            c = getchar();
          switch (c)
          {
          case '{':
            n++;
            addchar(c);
            break;
          case '}':
            n--;
            if (n > 0)
              addchar(c);
            break;
          case ']':
            i = 0;
            peekchar();
            while (n > 0 && peek == '}')
            {
              i++;
              n--;
              (void) getchar1();
              peekchar();
            }
            if (n > 0)
            {
              addchar(']');
              for (j = 0; j < i; j++)
                addchar('}');
            }
            if (i == 1)
              addchar(']');
            break;
          case '\n':
            addstring("<br/>");
            break;
          case ' ':
            addstring("&nbsp;");
            break;
          default:
            addchar1(c);
          }
        }
        startofline = 0;
        addstring("</tt>");
        break;
      default:
        addchar(c);
      }
    }
  }
  blanklines = 1;
  printpar();
  if (quotedepth != 0)
    fprintf(stderr, "non-zero quote depth at end?\n");
  if (stylecount != 0)
    fprintf(stderr, "non-zero style count at end?\n");
}

int
main(argc, argv)
  int argc;
  char **argv;
{
  if (argc != 3)
    error("wrong number of arguments");
  title = argv[2];
  if (!freopen(argv[1], "r", stdin))
    error("could not open file");
  init();
  printf("%s%s%s%s", header1, title, header2, header3);
  process();
  if (footnotecount)
  {
    fseek(stdin, 0, SEEK_SET);
    oldchar = -2;
    startofline = 1;
    stylecount = 0;
    oldcount = footnotecount;
    footnotecount = 0;
    infootnotes = 1;
    indiv = 0;
    printf("<hr/>\n\n");
    reset();
    inited = 0;
    process();
    if (footnotecount != oldcount)
      fprintf(stderr, "footnote counts don't match?\n");
  }
  printf("%s", footer);
  return 0;
}
