home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / metamail / contrib / misc / richtogroff.c next >
Encoding:
C/C++ Source or Header  |  1992-09-11  |  14.8 KB  |  670 lines

  1. /*
  2.  * text/richtext to groff converter
  3.  * Copyright ⌐ January 1992 by Keith Moore <moore@cs.utk.edu>
  4.  *
  5.  * This program converts mail message bodies of content-type: text/richtext
  6.  * to documents suitable for input to groff.  The output can be either
  7.  * printed or viewed on an X window system display using gxditview.
  8.  *
  9.  * I use this program with metamail as follows:  my .mailcap file contains
  10.  * the line:
  11.  *
  12.  * text/richtext; showrichtext %t ; bkm ; copiousoutput
  13.  *
  14.  * where "showrichtext" looks like this:
  15.  *
  16.  * #!/bin/sh
  17.  * case "$DISPLAY" in
  18.  *    *) richtogroff | groff -TX100 ;;
  19.  *     "") richtogroff | groff -Tascii >& 2;;
  20.  * esac
  21.  *
  22.  *
  23.  * This program is free software; you can redistribute it and/or modify
  24.  * it under the terms of the GNU General Public License as published by
  25.  * the Free Software Foundation; either version 2 of the License, or
  26.  * (at your option) any later version.
  27.  * 
  28.  * This program is distributed in the hope that it will be useful,
  29.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  30.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31.  * GNU General Public License for more details.
  32.  *
  33.  * A copy of the GNU General Public License can be obtained via anonymous
  34.  * ftp to prep.ai.mit.edu, directory "pub/gnu", file "COPYING-2".
  35.  * Otherwise, write to the Free Software Foundation, Inc., 675 Mass Ave,
  36.  * Cambridge, MA 02139, USA.
  37.  */
  38.  
  39. #include <stdio.h>
  40. #include <ctype.h>
  41.  
  42. int AtBOL = 0;            /* true if at start of output line */
  43. int Broken = 0;            /* true if we have emitted a break */
  44. int lineNumber = 1;        /* current source line # */
  45. int PageWidth = ((6*72)+36);    /* (in points) 6.5 inches by default */
  46. int PageHeight = 11*72;        /* (in points) 11 inches by default */
  47. int LeftMargin = 0;
  48. int RightMargin = 0;
  49. int PageOffset = 1*72;
  50.  
  51. /*
  52.  * Make sure we are at the beginning of a line in the groff output
  53.  */
  54.  
  55. void
  56. forceNewLine ()
  57. {
  58.     if (!AtBOL)
  59.     printf ("\n");
  60.     AtBOL = 1;
  61. }
  62.  
  63. /*
  64.  * Emit a .br command if we haven't done so already.
  65.  */
  66. void
  67. forceBreak ()
  68. {
  69.     if (Broken)
  70.     return;
  71.     forceNewLine ();
  72.     printf (".br\n");
  73.     Broken = 1;
  74. }
  75.  
  76. struct state {
  77.     int font;            /* font attribute bits, ORed together */
  78. #define FontPlain    00
  79. #define FontBold    01
  80. #define FontItalic    02
  81. #define FontFixed    04
  82.     int adjust;            /* adjust style */
  83. #define AdjustBoth    00
  84. #define AdjustLeft    01
  85. #define AdjustRight    02
  86. #define AdjustCenter    03
  87.     int leftMargin;        /* distance from left edge, in points */
  88.     int rightMargin;        /* distance from right edge, in points */
  89. };
  90.  
  91. struct state currentState = { FontPlain, AdjustBoth, 0, 0 };
  92.  
  93. /*
  94.  * font names for groff, assuming PostScript or X11 drivers
  95.  */
  96.  
  97. char *fontnames[] = {
  98.                 /* FIB */
  99.     "TR",            /* 000 (Times-Roman) */
  100.     "TB",            /* 001 (Times-Bold) */
  101.     "TI",            /* 010 (Times-Italic) */
  102.     "TBI",            /* 011 (Times-BoldItalic) */
  103.     "CR",            /* 100 (Courier-Roman) */
  104.     "CB",            /* 101 (Courier-Bold) */
  105.     "CI",            /* 110 (Courier-Italic) */
  106.     "CBI",            /* 111 (Courier-BoldItalic) */
  107. };
  108.  
  109. char *adjust[] = {
  110.     ".ad b\n.fi\n",        /* AdjustBoth */
  111.     ".ad l\n.fi\n",        /* AdjustLeft */
  112.     ".ad r\n.fi\n",        /* AdjustRight */
  113.     ".nf\n.ce 10000\n",        /* AdjustCenter */
  114. };
  115.  
  116.  
  117. /*
  118.  * Come here when we need to change the state of the interpreter
  119.  * oldState is probably equal to currentState.
  120.  * newState is what we want the state to be.
  121.  * undo is true iff we are exiting an environment.
  122.  */
  123.  
  124. void
  125. changeState (oldState, newState, undo)
  126. struct state *oldState, *newState;
  127. int undo;
  128. {
  129.     if (undo == 0) {
  130.     if (oldState->font != newState->font) {
  131.         printf ("\\f[%s]", fontnames[newState->font]);
  132.         AtBOL = 0;
  133.         Broken = 0;
  134.     }
  135.     }
  136.     if (oldState->leftMargin != newState->leftMargin) {
  137.     forceBreak ();
  138.     printf (".in %4.2fi\n", (float) newState->leftMargin / 72.0);
  139.     AtBOL = 1;
  140.     }
  141.     if (oldState->rightMargin != newState->rightMargin) {
  142.     forceBreak ();
  143.     printf (".ll %4.2fi\n",
  144.         (float) (PageWidth - newState->rightMargin) / 72.0);
  145.     AtBOL = 1;
  146.     }
  147.     if (oldState->adjust != newState->adjust) {
  148.     forceBreak ();
  149.     if (oldState->adjust == AdjustCenter)
  150.         printf (".ce 0\n");
  151.     printf ("%s", adjust[newState->adjust]);
  152.     AtBOL = 1;
  153.     }
  154.     if (undo == 1) {
  155.     if (oldState->font != newState->font) {
  156.         printf ("\\f[%s]", fontnames[newState->font]);
  157.         AtBOL = 0;
  158.         Broken = 0;
  159.     }
  160.     }
  161.     currentState = *newState;
  162. }
  163.  
  164. /*
  165.  * Change to a different font.
  166.  *
  167.  * This assumes that we have fonts for all combinations of character
  168.  * attributes - bold, italic, and fixed.  This assumption doesn't
  169.  * necessarily hold for other troffs (e.g. C/A/T troff) or for other
  170.  * output devices (besides gxditview and PostScript).
  171.  */
  172.  
  173. void
  174. changeFont (font)
  175. {
  176.     struct state newState;
  177.  
  178.     newState = currentState;
  179.     newState.font |= font;
  180.     changeState (¤tState, &newState, 0);
  181. }
  182.  
  183. /*
  184.  * decrease size.  Must be called with size >= 0.
  185.  *
  186.  * BUG: doesn't adjust line height, and it probably should. 
  187.  */
  188.  
  189. void
  190. smaller (size)
  191. {
  192.     printf ("\\s-%d", size);
  193.     AtBOL = 0;
  194.     Broken = 0;
  195. }
  196.  
  197. /*
  198.  * increase size.  Must be called with size >= 0.
  199.  *
  200.  * BUG: doesn't adjust line height, and it probably should. 
  201.  */
  202.  
  203. void
  204. bigger (size)
  205. {
  206.     printf ("\\s+%d", size);
  207.     AtBOL = 0;
  208.     Broken = 0;
  209. }
  210.  
  211.  
  212. /*
  213.  * start a new paragraph of some kind.  Note that this includes
  214.  * flushLeft, flushRight, and centered paragraphs as well as
  215.  * ordinary paragraphs.
  216.  */
  217.  
  218. void
  219. beginBlock (adjust)
  220. {
  221.     struct state newState;
  222.  
  223.     newState = currentState;
  224.     
  225.     forceBreak ();
  226.     printf (".ne 3\n");
  227.     AtBOL = 1;
  228.     newState.adjust = adjust;
  229.     changeState (¤tState, &newState, 0);
  230. }
  231.  
  232. /*
  233.  * begin an excerpt (quotation).  Put in an italic font and indent.
  234.  */
  235.  
  236. void
  237. beginExcerpt (x)
  238. int x;
  239. {
  240.     struct state newState;
  241.  
  242.     newState = currentState;
  243.     newState.font |= FontItalic;
  244.     newState.rightMargin += 10;
  245.     newState.leftMargin += 10;
  246.     changeState (¤tState, &newState, 0);
  247. }
  248.  
  249. /* 
  250.  * begin an example.  Put in a fixed font, flush left, and let
  251.  * output lines go all the way to the right margin.
  252.  */
  253.  
  254. void
  255. beginExample (x)
  256. int x;
  257. {
  258.     struct state newState;
  259.  
  260.     newState = currentState;
  261.     newState.font |= FontFixed;
  262.     newState.adjust = AdjustLeft;
  263.     newState.rightMargin = 0;
  264.     changeState (¤tState, &newState, 0);
  265. }
  266.  
  267. /* 
  268.  * indent the left margin.  x may be either positive or negative.
  269.  */
  270.  
  271. void
  272. indentLeft (x)
  273. int x;
  274. {
  275.     struct state newState;
  276.  
  277.     newState = currentState;
  278.     newState.leftMargin = currentState.leftMargin + x;
  279.     if (newState.leftMargin < 0)
  280.     newState.leftMargin = 0;
  281.     changeState (¤tState, &newState, 0);
  282. }
  283.  
  284. /* 
  285.  * indent the right margin.  x may be either positive or negative.
  286.  */
  287.  
  288. void
  289. indentRight (x)
  290. int x;
  291. {
  292.     struct state newState;
  293.  
  294.     newState = currentState;
  295.     newState.rightMargin = currentState.rightMargin + x;
  296.     if (newState.rightMargin < 0)
  297.     newState.rightMargin = 0;
  298.     changeState (¤tState, &newState, 0);
  299. }
  300.  
  301.  
  302. /*
  303.  * turn underlining on/off
  304.  */
  305.  
  306. void
  307. beginUnderline ()
  308. {
  309.     forceNewLine ();
  310.     printf (".ul 10000\n");
  311. }
  312.  
  313. void
  314. endUnderline ()
  315. {
  316.     forceNewLine ();
  317.     printf (".ul 0\n");
  318. }
  319.  
  320. /*
  321.  * start/end subscript
  322.  */
  323.  
  324. void
  325. beginSub ()
  326. {
  327.     printf ("\\d\\s-2");
  328. }
  329.  
  330. void
  331. endSub ()
  332. {
  333.     printf ("\\s+2\\u");
  334. }
  335.  
  336. /*
  337.  * start/end superscript
  338.  */
  339.  
  340. void
  341. beginSuper ()
  342. {
  343.     printf ("\\u\\s-2");
  344. }
  345.  
  346. void
  347. endSuper ()
  348. {
  349.     printf ("\\s+2\\d");
  350. }
  351.  
  352. struct stk {
  353.     char *cmdName;
  354.     int (*proc)();
  355.     int arg;
  356.     struct state savedState;
  357.     struct stk *next;
  358. };
  359.  
  360. struct stk *stack = NULL;
  361.  
  362. char *
  363. strsave (s)
  364. char *s;
  365. {
  366.     char *p = (char *) malloc (strlen (s) + 1);
  367.     strcpy (p, s);
  368.     return p;
  369. }
  370.  
  371. /*
  372.  * push a new environment on the stack.  cmdName is the name of the
  373.  * command that did this; it should match the closing command that
  374.  * ends the environment.  proc is the function that will undo the
  375.  * changes when we leave the environment.  Arg is the argument to pass
  376.  * to the leave proc.  state is the current state, to be restored when
  377.  * we leave this environment.
  378.  */
  379.  
  380. void
  381. pushStack (cmdName, proc, arg, state)
  382. char *cmdName;
  383. int (*proc)();
  384. int arg;
  385. struct state *state;
  386. {
  387.     struct stk *ptr = (struct stk *) malloc (sizeof (struct stk));
  388.  
  389.     ptr->cmdName = strsave (cmdName);
  390.     ptr->proc = proc;
  391.     ptr->arg = arg;
  392.     ptr->savedState = *state;
  393.     ptr->next = stack;
  394.     stack = ptr;
  395. }
  396.  
  397. void
  398. popStack ()
  399. {
  400.     struct stk *ptr;
  401.  
  402.     if (stack) {
  403.     ptr = stack->next;
  404.     free (stack->cmdName);
  405.     free (stack);
  406.     stack = ptr;
  407.     }
  408. }
  409.  
  410. struct cmd {
  411.     char *cmdName;
  412.     void (*enter)();
  413.     void (*leave)();
  414.     int arg;
  415. } cmds[] = {
  416.     { "bold", changeFont, NULL, FontBold },
  417.     { "italic", changeFont, NULL, FontItalic },
  418.     { "fixed", changeFont, NULL, FontFixed },
  419.     { "smaller", smaller, bigger, 2 },
  420.     { "bigger", bigger, smaller, 2 },
  421.     { "example", beginExample, NULL, 0 },
  422.     { "underline", beginUnderline, endUnderline, 0 },
  423.     { "center", beginBlock, NULL, AdjustCenter },
  424.     { "flushleft", beginBlock, NULL, AdjustLeft },
  425.     { "flushright", beginBlock, NULL, AdjustRight },    
  426.     { "indent", indentLeft, NULL, 18 },
  427.     { "indentright", indentRight, NULL, 18 },
  428.     { "outdent", indentLeft, NULL, -18 },
  429.     { "outdentright", indentRight, NULL, -18 },
  430. /*  { "samepage", noOp, NULL, 0 }, */
  431.     { "subscript", beginSub, endSub, 0 },
  432.     { "superscript", beginSuper, endSuper, 0 },
  433. /*  { "heading", noOp, NULL, 0 }, */
  434. /*  { "footing", noOp, NULL, 0 }, */
  435.     { "excerpt", beginExcerpt, NULL, 0 },
  436.     { "paragraph", beginBlock, NULL, AdjustBoth },
  437. /*  { "signature", noOp, NULL, 0 }, */
  438. };
  439.  
  440. /*
  441.  * This is called when we see a new command.  If we are leaving an
  442.  * environment, make sure its name matches the one we are currently
  443.  * in.
  444.  */
  445.  
  446. void
  447. doCommand (s)
  448. char *s;
  449. {
  450.     int i;
  451.  
  452.     if (*s == '/') {
  453.     /*
  454.      * leave environment.  restore old state and pop stack
  455.      */
  456.     ++s;
  457.     if (stack == NULL)
  458.         fprintf (stderr, "%d:stack underflow\n", lineNumber);
  459.     else if (strcmp (s, stack->cmdName) == 0) {
  460.         if (stack->proc)
  461.         (*(stack->proc))(stack->arg);
  462.         changeState (¤tState, &(stack->savedState), 1);
  463.         popStack ();
  464.     }
  465.     else {
  466.         fprintf (stderr, "%d: incorrect nesting: found %s expected %s\n",
  467.              lineNumber, s, stack->cmdName);
  468.     }
  469.     }
  470.     else {
  471.     /*
  472.      * enter environment.  save current command and state on stack.
  473.      */
  474.     struct cmd *p;
  475.     for (i = 0; i < sizeof cmds / sizeof *cmds; ++i) {
  476.         p = &(cmds[i]);
  477.         if (strcmp (s, p->cmdName) == 0) {
  478.         pushStack (p->cmdName, p->leave, p->arg, ¤tState);
  479.         if (p->enter)
  480.             (*(p->enter))(p->arg);
  481.         return;
  482.         }
  483.     }
  484.     pushStack (s, NULL, 0, ¤tState);
  485.     }
  486. }
  487.  
  488. char *
  489. getCmd (ignore)
  490. int ignore;
  491. {
  492.     static char buf[52];
  493.     char *ptr;
  494.     int c;
  495.  
  496.     ptr = buf;
  497.     while ((c = getchar ()) != '>') {
  498.     if (ptr < buf + 51 &&
  499.         c != EOF &&
  500.         (isalpha (c) || isdigit (c) || c == '-' ||
  501.          (c == '/' && ptr == buf)))
  502.         *ptr++ = c;
  503.     else {
  504.         /*
  505.          * found an illegal character in the "command"; treat
  506.          * it as text, and output along with the initial '<'.
  507.          * Note that everything that's already in the buffer 
  508.          * is necessarily either a letter, digit, or hyphen,
  509.          * so it's not necessary to scan for another '<' or
  510.          * to filter it specially for groff.
  511.          */
  512.         if (!ignore) {
  513.         *ptr = '\0';
  514.         printf ("<%s", buf);
  515.         if (c != EOF)
  516.             ungetc (c, stdin);
  517.         }
  518.         return NULL;
  519.     }
  520.     }
  521.     *ptr = '\0';
  522.     for (ptr = buf; *ptr; ++ptr)
  523.     if (isupper (*ptr))
  524.         *ptr = tolower (*ptr);
  525.     return buf;
  526. }
  527.  
  528. init ()
  529. {
  530.     currentState.font = FontPlain;
  531.     currentState.adjust = AdjustBoth;
  532.     currentState.leftMargin = LeftMargin;
  533.     currentState.rightMargin = RightMargin;
  534.  
  535.     printf (".pl %4.2fi\n", PageHeight / 72.0);    /* 8 inches tall */
  536.     printf (".po %4.2fi\n", PageOffset / 72.0);    /* 0 page offset */
  537.     printf (".ll %4.2fi\n",
  538.         (float) (PageWidth - currentState.rightMargin) / 72.0);
  539.     printf (".lt %4.2fi\n",
  540.         (float) (PageWidth - currentState.rightMargin) / 72.0);
  541.     printf (".de NP\n");    /* do page breaks */
  542.     printf ("'sp .3i\n");
  543.     printf ("'bp\n");
  544.     printf ("'sp .3i\n");
  545.     printf ("..\n");
  546.     printf (".wh -.4i NP\n");
  547.     printf (".ft TR\n");    /* Times-Roman font */
  548.     printf (".nh\n");        /* no hyphenation */
  549.     printf (".ad l\n");        /* justify both sides ? */
  550.     printf (".fi\n");        /* fill lines */
  551.     AtBOL = 1;
  552.     Broken = 1;
  553. }
  554.  
  555. main(argc, argv)
  556. char **argv;
  557. {
  558.     int c, i;
  559.     char *command;
  560.     double atof();
  561.     int nestedComments = 0;
  562.  
  563.     for (i = 1; i < argc; ++i) {
  564.     if (strncmp (argv[i], "-width=", 7) == 0)
  565.         PageWidth = (int) (atof (argv[i] + 7) * 72.0);
  566.     else if (strncmp (argv[i], "-height=", 8) == 0)
  567.         PageHeight = (int) (atof (argv[i] + 8) * 72.0);
  568.     else if (strncmp (argv[i], "-left=", 6) == 0)
  569.         LeftMargin = (int) (atof (argv[i] + 6) * 72.0);
  570.     else if (strncmp (argv[i], "-right=", 7) == 0)
  571.         RightMargin = (int) (atof (argv[i] + 7) * 72.0);
  572.     else if (strncmp (argv[i], "-offset=", 8) == 0)
  573.         PageOffset = (int) (atof (argv[i] + 8) * 72.0);
  574.     }
  575. #if 0
  576.     fprintf (stderr, "PageWidth = %d\n", PageWidth);
  577.     fprintf (stderr, "PageHeight = %d\n", PageHeight);
  578.     fprintf (stderr, "LeftMargin = %d\n", LeftMargin);
  579.     fprintf (stderr, "RightMargin = %d\n", RightMargin);
  580.     fprintf (stderr, "PageOffset = %d\n", PageOffset);
  581. #endif
  582.     init ();
  583.     while ((c = getchar ()) != EOF) {
  584.     switch (c) {
  585.     case '<':
  586.         if ((command = getCmd (0)) == NULL)
  587.         continue;
  588.  
  589.         /*
  590.          * hack to make sure a newline following <nl> or
  591.          * </paragraph> is ignored.
  592.          */
  593.         if (strcmp (command, "nl") == 0 ||
  594.         strcmp (command, "/paragraph") == 0) {
  595.         if ((c = getchar ()) == EOF)
  596.             break;
  597.         else if (c != '\n')
  598.             ungetc (c, stdin);
  599.         }
  600.  
  601.         if (strcmp (command, "comment") == 0) {
  602.         nestedComments++;
  603.         do {
  604.                     while ((c = getchar ()) != '<')
  605.             if (c == EOF) {
  606.                 fprintf (stderr, "%d: premature EOF in comment\n",
  607.                      lineNumber);
  608.                 exit (1);
  609.             }
  610.             if ((command = getCmd (1)) == NULL)
  611.             ;
  612.             else if (strcmp (command, "comment") == 0)
  613.             ++nestedComments;
  614.             else if (strcmp (command, "/comment") == 0)
  615.             --nestedComments;
  616.                 } while (nestedComments > 0);
  617.         continue;
  618.         }
  619.             else if (strcmp (command, "lt") == 0) {
  620.                 putchar ('<');
  621.                 AtBOL = 0;
  622.             }
  623.         else if (strcmp (command, "nl") == 0) {
  624.         if (Broken) {
  625.             printf ("\n");
  626.             AtBOL = 1;
  627.         }
  628.         else
  629.             forceBreak ();
  630.             }
  631.         else if (strcmp (command, "np") == 0) {
  632.         forceNewLine ();
  633.         printf (".NP\n");
  634.                 AtBOL = 1;
  635.             }
  636.         else
  637.         doCommand (command);
  638.         break;
  639.     case '\n':        /* special for newline */
  640.         lineNumber++;
  641.         if (!AtBOL) {
  642.         printf (" ");
  643.         AtBOL = 0;
  644.         Broken = 0;
  645.         }
  646.         break;
  647.     case '\\':        /* special for backslash */
  648.         printf ("\\e");
  649.         AtBOL = 0;
  650.         Broken = 0;
  651.         break;
  652.     case '.':        /* special for '.' at start of line */
  653.         if (AtBOL)
  654.         printf ("\\.");
  655.         else
  656.         printf (".");
  657.         AtBOL = 0;
  658.         Broken = 0;
  659.         break;
  660.     default:
  661.             putchar (c);
  662.             AtBOL = 0;
  663.         Broken = 0;
  664.         break;
  665.     }
  666.     }
  667.     if (!AtBOL)
  668.     putchar ('\n');
  669. }
  670.