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

#ifdef macintosh
#include <CursorCtl.h>
#endif

#define IT 1
#define SC 2
#define BLD 4
#define SUP 8
#define SMALL 16

char *code[256] =
{
  "\"^@\"", "\"^A\"", "\"^B\"", "\"^C\"", "\"^D\"", "\"^E\"", "\"^F\"", "\"^G\"",
  "\"^H\"", "\t", "\n", "\"^K\"", "\"^L\"", "\n", "\"^N\"", "\"^O\"",
  "\"^P\"", "\"^Q\"", "\"^R\"", "\"^S\"", "\"^T\"", "\"^U\"", "\"^V\"", "\"^W\"",
  "\"^X\"", "\"^Y\"", "\"^Z\"", "\"^[\"", "\"^\\\"", "\"^]\"", "\"^^\"", "\"^_\"",
  " ", "!", "\"''\"", "\"#\"", "\"$\"", "\"%\"", "&", "\"'\"",
  "(", ")", "\"*\"", "\"+\"", ",", "-", ".", "/",
  "0", "1", "2", "3", "4", "5", "6", "7",
  "8", "9", ":", ";", "\"<\"", "\"=\"", "\">\"", "?",
  "@", "A", "B", "C", "D", "E", "F", "G",
  "H", "I", "J", "K", "L", "M", "N", "O",
  "P", "Q", "R", "S", "T", "U", "V", "W",
  "X", "Y", "Z", "\"[\"", "\"\\\"", "\"]\"", "\"^\"", "\"_\"",
  "`", "a", "b", "c", "d", "e", "f", "g",
  "h", "i", "j", "k", "l", "m", "n", "o",
  "p", "q", "r", "s", "t", "u", "v", "w",
  "x", "y", "z", "\"{\"", "\"|\"", "\"}\"", "\"~\"", "\"^?\"",
  "<A\">", "<Ao>", "<C,>", "<E'>", "<N~>", "<O\">", "<U\">", "<a'>",
  "<a`>", "<a^>", "<a\">", "<a~>", "<ao>", "<c,>", "<e'>", "<e`>",
  "<e^>", "<e\">", "<i'>", "<i`>", "<i^>", "<i\">", "<n~>", "<o'>",
  "<o`>", "<o^>", "<o\">", "<o~>", "<u'>", "<u`>", "<u^>", "<u\">",
  "\"**\"", "\"o\"", "<c/>", "\"L\"", "\"p\"", "\"-\"", "\"P\"", "\"ss\"",
  "\"(r)\"", "\"(c)\"", "\"TM\"", "< '>", "< \">", "<=/>", "\"AE\"", "<O/>",
  "\"oo\"", "\"+-\"", "\"<=\"", "\">=\"", "\"Y\"", "\"mu\"", "\"d\"", "\"S\"",
  "\"Pi\"", "\"pi\"", "\"I\"", "\"a_\"", "\"o_\"", "\"O\"", "\"ae\"", "<o/>",
  "\"?\"", "\"!\"", "\"-.\"", "\"./\"", "\"f\"", "\"~~\"", "\"D\"", "`",
  "'", "...", " ", "<A`>", "<A~>", "<O~>", "\"OE\"", "\"oe\"",
  "--", "---", "`", "'", "`", "'", "\"/\"", "\"<>\"",
  "<y\">", "<Y\">", "< />", "\">0<\"", "`", "'", "fi", "fl",
  "\"++\"", "\".\"", "`", "`", "\"%%\"", "<A^>", "<E^>", "<A'>",
  "<E\">", "<E`>", "<I'>", "<I^>", "<I\">", "<I`>", "<O'>", "<O^>",
  "\"@\"", "<O`>", "<U'>", "<U^>", "<U`>", "<i >", "< ^>", "< ~>",
  "< ->", "< _>", "< .>", "< o>", "< ,>", "< =>", "< ;>", "< v>"
};

char *prelude[] =
{
  "{\\rtf1\\mac\\deff3{\\fonttbl{\\f3\\fswiss Geneva;}{\\f20\\froman Times;}{\\f8837\\fnil Times SC;}}\n",
  "\\paperw11900\\paperh16840\\margl2840\\margr2840\\margt1420\\margb2560\\deftab31680\n",
  "\\ftnbj\\sectd\\sbknone\\footery2260{\\footer\\pard\\s2\\qc\\sl-300\\plain\\f20{\\chpgn}}\n",
  0
};

char *postlude[] =
{
  "}",
  0
};

char *escape[256] =
{
  "\\'00", "\\'01", "\\'02", "\\'03", "\\'04", "\\'05", "\\'06", "\\'07",
  "\\'08", "\\'09", "\\'0a", "\\'0b", "\\'0c", "\\'0d", "\\'0e", "\\'0f",
  "\\'10", "\\'11", "\\'12", "\\'13", "\\'14", "\\'15", "\\'16", "\\'17",
  "\\'18", "\\'19", "\\'1a", "\\'1b", "\\'1c", "\\'1d", "\\'1e", "\\'1f",
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, "\\\\", 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, "\\{", 0, "\\}", 0, "\\'7f",
  "\\'80", "\\'81", "\\'82", "\\'83", "\\'84", "\\'85", "\\'86", "\\'87",
  "\\'88", "\\'89", "\\'8a", "\\'8b", "\\'8c", "\\'8d", "\\'8e", "\\'8f",
  "\\'90", "\\'91", "\\'92", "\\'93", "\\'94", "\\'95", "\\'96", "\\'97",
  "\\'98", "\\'99", "\\'9a", "\\'9b", "\\'9c", "\\'9d", "\\'9e", "\\'9f",
  "\\'a0", "\\'a1", "\\'a2", "\\'a3", "\\'a4", "\\'a5", "\\'a6", "\\'a7",
  "\\'a8", "\\'a9", "\\'aa", "\\'ab", "\\'ac", "\\'ad", "\\'ae", "\\'af",
  "\\'b0", "\\'b1", "\\'b2", "\\'b3", "\\'b4", "\\'b5", "\\'b6", "\\'b7",
  "\\'b8", "\\'b9", "\\'ba", "\\'bb", "\\'bc", "\\'bd", "\\'be", "\\'bf",
  "\\'c0", "\\'c1", "\\'c2", "\\'c3", "\\'c4", "\\'c5", "\\'c6", "\\'c7",
  "\\'c8", "\\'c9", "\\'ca", "\\'cb", "\\'cc", "\\'cd", "\\'ce", "\\'cf",
  "\\'d0", "\\'d1", "\\'d2", "\\'d3", "\\'d4", "\\'d5", "\\'d6", "\\'d7",
  "\\'d8", "\\'d9", "\\'da", "\\'db", "\\'dc", "\\'dd", "\\'de", "\\'df",
  "\\'e0", "\\'e1", "\\'e2", "\\'e3", "\\'e4", "\\'e5", "\\'e6", "\\'e7",
  "\\'e8", "\\'e9", "\\'ea", "\\'eb", "\\'ec", "\\'ed", "\\'ee", "\\'ef",
  "\\'f0", "\\'f1", "\\'f2", "\\'f3", "\\'f4", "\\'f5", "\\'f6", "\\'f7",
  "\\'f8", "\\'f9", "\\'fa", "\\'fb", "\\'fc", "\\'fd", "\\'fe", "\\'ff"
};

char *car[] =
{
  "\\qj",
  0,
  "\\qj",
  0,
  "\\ql\\fi-840\\li1400",
  "\\qj\\li280\\ri280",
  "\\ql\\fi-560\\li1400",
  0,
  "\\qc",
  0,
  "\\qr"
};
  
char *cdr[] =
{
  0,
  0,
  "\\qj\\fi280",
  0,
  0,
  "\\qj\\fi280\\li280\\ri280",
  0,
  0,
  0,
  0,
  0
};

struct state
{
  FILE *text;
  int line, start, parity, old, new, forced, changed, prev, second;
}
cur, body, notes;

char *prog, *file, buf[6];
int realcaps, phase, flushing = 0, silent = 0, white = 0;

void
bumpline()
{
  cur.line++;
#ifdef macintosh
  SpinCursor(1);
#endif
}

int
get()
{
  int c;

  c = getc(cur.text);
  if (cur.start)
  {
    bumpline();
    while ((phase < 2) == (c == '|') || c == '\t')
    {
      while (c != EOF && c != '\n')
        c = getc(cur.text);
      if (c == EOF)
        break;
      bumpline();
      c = getc(cur.text);
    }
    if (c == '|')
    {
      if ((c = getc(cur.text)) == ' ')
        c = getc(cur.text);
      else
      {
        if (c == '\n')
          (void) fprintf(stderr,
	    "%s: %s: line %d: note line only contains \"|\"\n",
	    prog, file, cur.line);
	else
          (void) fprintf(stderr,
	    "%s: %s: line %d: note line starts with \"|%c\"\n",
	    prog, file, cur.line, c);
      }
    }
  }
  cur.start = (c == '\n');
  return c;
}

void
unget(c)
  int c;
{
  (void) ungetc(c, cur.text);
  cur.start = 0;
}

void
putstr(s)
  char *s;
{
  if (fputs(s, stdout) < 0)
  {
    (void) fprintf(stderr, "%s: couldn't write (disk full?)\n", prog);
    exit(1);
  }
}

void
skipblanks()
{
  int c, n;
  char *s;

  n = 0;
  while ((c = getc(cur.text)) == ' ')
    n++;
  (void) ungetc(c, cur.text);
  if (n > 10)
    (void) fprintf(stderr,
      "%s: %s: line %d: paragraph starts with too much spaces\n",
      prog, file, cur.line);
  else
  if (!(s = car[n]))
    (void) fprintf(stderr,
      "%s: %s: line %d: paragraph starts with strange number of spaces\n",
      prog, file, cur.line);
  else
  if (n != cur.prev || (cur.second && (s = cdr[n])))
  {
    putstr("\\pard");
    putstr(s);
    if (phase < 2)
      putstr("\\sl-300");
    else
      putstr("\\sl-240");
    putstr(" ");
    cur.forced = 1;
  }
  cur.second = (n != cur.prev);
  cur.prev = n;
}

void
put(c)
  int c;
{
  char *s;

  if (silent)
    return;
  if (white && c == ' ')
    return;
  if (cur.forced || (cur.changed && cur.new != cur.old))
  {
    if (realcaps && (cur.new & SC))
      putstr("\\plain\\f8837");
    else
    if (cur.forced || cur.old)
      putstr("\\plain\\f20");
    if (cur.new & SUP)
    {
      if (cur.new & SMALL)
        putstr("\\fs14\\up6");
      else
        putstr("\\fs18\\up6");
    }
    else
    {
      if (cur.new & SMALL)
        putstr("\\fs20");
      else
      if (cur.old & (SUP + SMALL))
        putstr("\\fs24");
    }
    if (!realcaps && (cur.new & SC))
      putstr("\\scaps");
    if (cur.new & IT)
      putstr("\\i");
    if (cur.new & BLD)
      putstr("\\b");
    putstr(" ");
    cur.old = cur.new;
    cur.forced = 0;
    cur.changed = 0;
  }
  if ((s = escape[c]))
    putstr(s);
  else
    if (putchar(c) < 0)
    {
      (void) fprintf(stderr, "%s: couldn't write (disk full?)\n", prog);
      exit(1);
    }
  white = (c == ' ');
}

void
putcode(s)
  char *s;
{
  int n, saved;

  for (n = 0; n < 256 && strcmp(s, code[n]); n++)
    ;
  if (n < 256)
    put(n);
  else
  if (!s[3] && isdigit(s[1]))
  {
    saved = cur.new;
    cur.new |= SUP;
    cur.changed = 1;
    put(s[1]);
    cur.new = saved;
    cur.changed = 1;
  }
  else
  {
    (void) fprintf(stderr, "%s: %s: line %d: strange code %s\n",
      prog, file, cur.line, s);
    for (n = 0; s[n]; n++)
      if (s[n] != '"')
	put(s[n]);
  }
}

void
startnote()
{
  int c;

  body = cur;
  cur = notes;
  cur.prev = -2;
  cur.old = 0;
  cur.new = SMALL;
  cur.forced = 1;
  phase = 2;
  while ((c = get()) == '\n')
    ;
  unget(c);
  putstr("{\\plain\\f20\\fs18\\up6\\chftn{\\footnote ");
  skipblanks();
}

void
stopnote()
{
  putstr("}}");
  notes = cur;
  cur = body;
  cur.old = cur.new | SMALL;
  cur.forced = 1;
  phase = 0;
}

int
flushother()
{
  int c;

  if (phase < 2)
  {
    body = cur;
    cur = notes;
    phase = 2;
    while ((c = get()) == '\n')
      ;
    if (c == EOF)
      return 0;
    else
    {
      (void) fprintf(stderr, "%s: %s: line %d: unreferenced note line\n",
	prog, file, cur.line);
      phase = 2;
      unget(c);
      putstr("{\\plain\\f20\\fs18\\up6\\chftn{\\footnote ");
      skipblanks();
      flushing = 1;
    }
  }
  else
  {
    if (!flushing)
      (void) fprintf(stderr,
        "%s: %s: line %d: missing footnote or end of file inside footnote\n",
	prog, file, cur.line);
    putstr("}}");
    notes = cur;
    cur = body;
    phase = 0;
    if (flushing)
      return 0;
  }
  return 1;
}

void
putlines(a)
  char **a;
{
  char *s;
  
  for (; (s = *a); a++)
    putstr(s);
}

int
main(argc, argv)
  int argc;
  char **argv;
{
  int c, n;

#ifdef macintosh
  InitCursorCtl(NULL);
#endif
  prog = argv[0];
  if (argc != 2 && (argc != 3 || strcmp(argv[1], "-k")))
  {
    (void) fprintf(stderr, "%s: usage: %s [-k] file\n", prog, prog);
    exit(1);
  }
  realcaps = (argc == 3);
  file = argv[argc - 1];
  if (!(body.text = fopen(file, "r")))
  {
    (void) fprintf(stderr, "%s: %s: couldn't open\n", prog, file);
    exit(1);
  }
  if (!(notes.text = fopen(file, "r")))
  {
    (void) fprintf(stderr, "%s: %s: couldn't open twice\n", prog, file);
    exit(1);
  }
  body.line = notes.line = 0;
  body.start = notes.start = 1;
  body.parity = notes.parity = 0;
  body.old = body.new = 0;
  body.forced = notes.forced = 1;
  body.prev = notes.prev = -2;
  escape['\n'] = "\\par\n";
  putlines(prelude);
  cur = body;
  phase = 0;
  skipblanks();
  while (1)
  {
    c = get();
    switch (c)
    {
    case '\n':
      switch (phase)
      {
      case 0:
        break;
      case 1:
	(void) fprintf(stderr, "%s: %s: line %d: unterminated footnote reference\n",
	  prog, file, cur.line);
        phase = 0;
	break;
      case 2:
        break;
      case 3:
	(void) fprintf(stderr, "%s: %s: line %d: unterminated footnote number\n",
	  prog, file, cur.line);
        phase = 2;
	break;
      case 4:
        break;
      }
      c = get();
      if (c == ' ' || c == '\n' || c == EOF)
      {
	if (phase && c != ' ')
	  stopnote();
	else
	{
	  unget(c);
	  if (c != EOF)
	  {
            put('\n');
	    skipblanks();
	  }
	  if (cur.new)
	    cur.forced = 1;
	}
      }
      else
      {
        unget(c);
	put(' ');
      }
      break;
    case '-':
      n = 0;
      while ((c = get()) == ' ')
        n++;
      switch (c)
      {
      case '\n':
        if (n)
	  put('-');
        c = get();
	if (c == ' ')
        {
	  if (n)
	    put(' ');
          put('\n');
	  unget(c);
          skipblanks();
        }
        else
        {
          unget(c);
	  if (n > 1)
	    put(' ');
        }
        break;
      case '-':
        if (n)
	{
	  put('-');
	  put(' ');
	  unget(c);
	}
	else
	{
	  n = 2;
	  while ((c = get()) == '-')
	    n++;
	  unget(c);
	  switch(n)
	  {
	  case 2:
	    put(208);
	    break;
	  case 3:
	    put(209);
	    break;
	  default:
	    (void) fprintf(stderr, "%s: %s: line %d: more than 3 hyphens in a row\n",
	      prog, file, cur.line);
	    put(209);
	  }
	}
        break;
      default:
        unget(c);
	put('-');
	if (n)
	  put(' ');
      }
      break;
    case '"':
      n = 0;
      buf[n++] = c;
      while (1)
      {
	c = get();
	if (c == '\n')
	{
	  unget(c);
	  break;
	}
	buf[n++] = c;
	if (c == '"' || n == 5)
	  break;
      }
      buf[n++] = '\0';
      putcode(buf);
      break;
    case '<':
      buf[0] = '<';
      buf[3] = '>';
      buf[4] = '\0';
      while (1)
      {
        c = get();
	if (c == '>')
	  break;
	if (c == '\n')
	{
	  unget(c);
	  (void) fprintf(stderr, "%s: %s: line %d: unterminated \"<\" code\n",
	    prog, file, cur.line);
	  break;
	}
	buf[1] = c;
	c = get();
	if (c == '>' || c == '\n')
	{
	  unget(c);
	  (void) fprintf(stderr, "%s: %s: line %d: short \"<\" code\n",
	    prog, file, cur.line);
	  put(buf[1]);
	  break;
	}
	buf[2] = c;
	putcode(buf);
      }
      break;
    case '.':
      n = 1;
      while ((c = get()) == '.')
	n++;
      unget(c);
      if (n < 3)
        while (n--)
	  put('.');
      else
      if (n == 3)
        put(201);
      else
      {
	(void) fprintf(stderr, "%s: %s: line %d: more than 3 dots in a row\n",
	  prog, file, cur.line);
	put(201);
      }
      break;
    case '`':
      c = get();
      if (c == '~')
      {
        cur.parity = 0;
	while (1)
	{
	  put(cur.parity ? 210 : 212);
	  cur.parity = !cur.parity;
	  c = get();
	  if (c != '`')
	    break;
	  c = get();
	  if (c != '~')
	  {
	    put(cur.parity ? 210 : 212);
	    cur.parity = !cur.parity;
	    break;
	  }
	}
      }
      else
      {
	put(cur.parity ? 210 : 212);
	cur.parity = !cur.parity;
      }
      unget(c);
      break;
    case '\'':
      c = get();
      if (isalnum(c) || c == '<' || c == '"' || c == '~')
        put(213);
      else
      {
	cur.parity = !cur.parity;
        put(cur.parity ? 211 : 213);
      }
      unget(c);
      break;
    case '_':
      cur.new ^= IT;
      cur.changed = 1;
      break;
    case '=':
      cur.new ^= SC;
      cur.changed = 1;
      break;
    case '*':
      cur.new ^= BLD;
      cur.changed = 1;
      break;
    case '^':
      switch (phase)
      {
      case 0:
        phase++;
        silent = 1;
	break;
      case 1:
	startnote();
	/* phase = 2 */
        silent = 0;
	break;
      case 2:
        phase++;
        silent = 1;
	putstr("\\plain\\f20\\fs20{\\chftn .}");
	cur.old = SMALL;
	cur.forced = 0;
	cur.changed = 1;
	break;
      case 3:
        phase++;
        silent = 0;
	put(' ');
	break;
      case 4:
        (void) fprintf(stderr, "%s: %s: line %d: double numbered footnote?\n",
	  prog, file, cur.line);
        break;
      }
      break;
    case '~':
      break;
    case EOF:
      break;
    default:
      put(c);
    }
    if (c == EOF && !flushother())
      break;
  }
  putlines(postlude);
  return 0;
}
