home *** CD-ROM | disk | FTP | other *** search
- /*
- * text/richtext to groff converter
- * Copyright ⌐ January 1992 by Keith Moore <moore@cs.utk.edu>
- *
- * This program converts mail message bodies of content-type: text/richtext
- * to documents suitable for input to groff. The output can be either
- * printed or viewed on an X window system display using gxditview.
- *
- * I use this program with metamail as follows: my .mailcap file contains
- * the line:
- *
- * text/richtext; showrichtext %t ; bkm ; copiousoutput
- *
- * where "showrichtext" looks like this:
- *
- * #!/bin/sh
- * case "$DISPLAY" in
- * *) richtogroff | groff -TX100 ;;
- * "") richtogroff | groff -Tascii >& 2;;
- * esac
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * A copy of the GNU General Public License can be obtained via anonymous
- * ftp to prep.ai.mit.edu, directory "pub/gnu", file "COPYING-2".
- * Otherwise, write to the Free Software Foundation, Inc., 675 Mass Ave,
- * Cambridge, MA 02139, USA.
- */
-
- #include <stdio.h>
- #include <ctype.h>
-
- int AtBOL = 0; /* true if at start of output line */
- int Broken = 0; /* true if we have emitted a break */
- int lineNumber = 1; /* current source line # */
- int PageWidth = ((6*72)+36); /* (in points) 6.5 inches by default */
- int PageHeight = 11*72; /* (in points) 11 inches by default */
- int LeftMargin = 0;
- int RightMargin = 0;
- int PageOffset = 1*72;
-
- /*
- * Make sure we are at the beginning of a line in the groff output
- */
-
- void
- forceNewLine ()
- {
- if (!AtBOL)
- printf ("\n");
- AtBOL = 1;
- }
-
- /*
- * Emit a .br command if we haven't done so already.
- */
- void
- forceBreak ()
- {
- if (Broken)
- return;
- forceNewLine ();
- printf (".br\n");
- Broken = 1;
- }
-
- struct state {
- int font; /* font attribute bits, ORed together */
- #define FontPlain 00
- #define FontBold 01
- #define FontItalic 02
- #define FontFixed 04
- int adjust; /* adjust style */
- #define AdjustBoth 00
- #define AdjustLeft 01
- #define AdjustRight 02
- #define AdjustCenter 03
- int leftMargin; /* distance from left edge, in points */
- int rightMargin; /* distance from right edge, in points */
- };
-
- struct state currentState = { FontPlain, AdjustBoth, 0, 0 };
-
- /*
- * font names for groff, assuming PostScript or X11 drivers
- */
-
- char *fontnames[] = {
- /* FIB */
- "TR", /* 000 (Times-Roman) */
- "TB", /* 001 (Times-Bold) */
- "TI", /* 010 (Times-Italic) */
- "TBI", /* 011 (Times-BoldItalic) */
- "CR", /* 100 (Courier-Roman) */
- "CB", /* 101 (Courier-Bold) */
- "CI", /* 110 (Courier-Italic) */
- "CBI", /* 111 (Courier-BoldItalic) */
- };
-
- char *adjust[] = {
- ".ad b\n.fi\n", /* AdjustBoth */
- ".ad l\n.fi\n", /* AdjustLeft */
- ".ad r\n.fi\n", /* AdjustRight */
- ".nf\n.ce 10000\n", /* AdjustCenter */
- };
-
-
- /*
- * Come here when we need to change the state of the interpreter
- * oldState is probably equal to currentState.
- * newState is what we want the state to be.
- * undo is true iff we are exiting an environment.
- */
-
- void
- changeState (oldState, newState, undo)
- struct state *oldState, *newState;
- int undo;
- {
- if (undo == 0) {
- if (oldState->font != newState->font) {
- printf ("\\f[%s]", fontnames[newState->font]);
- AtBOL = 0;
- Broken = 0;
- }
- }
- if (oldState->leftMargin != newState->leftMargin) {
- forceBreak ();
- printf (".in %4.2fi\n", (float) newState->leftMargin / 72.0);
- AtBOL = 1;
- }
- if (oldState->rightMargin != newState->rightMargin) {
- forceBreak ();
- printf (".ll %4.2fi\n",
- (float) (PageWidth - newState->rightMargin) / 72.0);
- AtBOL = 1;
- }
- if (oldState->adjust != newState->adjust) {
- forceBreak ();
- if (oldState->adjust == AdjustCenter)
- printf (".ce 0\n");
- printf ("%s", adjust[newState->adjust]);
- AtBOL = 1;
- }
- if (undo == 1) {
- if (oldState->font != newState->font) {
- printf ("\\f[%s]", fontnames[newState->font]);
- AtBOL = 0;
- Broken = 0;
- }
- }
- currentState = *newState;
- }
-
- /*
- * Change to a different font.
- *
- * This assumes that we have fonts for all combinations of character
- * attributes - bold, italic, and fixed. This assumption doesn't
- * necessarily hold for other troffs (e.g. C/A/T troff) or for other
- * output devices (besides gxditview and PostScript).
- */
-
- void
- changeFont (font)
- {
- struct state newState;
-
- newState = currentState;
- newState.font |= font;
- changeState (¤tState, &newState, 0);
- }
-
- /*
- * decrease size. Must be called with size >= 0.
- *
- * BUG: doesn't adjust line height, and it probably should.
- */
-
- void
- smaller (size)
- {
- printf ("\\s-%d", size);
- AtBOL = 0;
- Broken = 0;
- }
-
- /*
- * increase size. Must be called with size >= 0.
- *
- * BUG: doesn't adjust line height, and it probably should.
- */
-
- void
- bigger (size)
- {
- printf ("\\s+%d", size);
- AtBOL = 0;
- Broken = 0;
- }
-
-
- /*
- * start a new paragraph of some kind. Note that this includes
- * flushLeft, flushRight, and centered paragraphs as well as
- * ordinary paragraphs.
- */
-
- void
- beginBlock (adjust)
- {
- struct state newState;
-
- newState = currentState;
-
- forceBreak ();
- printf (".ne 3\n");
- AtBOL = 1;
- newState.adjust = adjust;
- changeState (¤tState, &newState, 0);
- }
-
- /*
- * begin an excerpt (quotation). Put in an italic font and indent.
- */
-
- void
- beginExcerpt (x)
- int x;
- {
- struct state newState;
-
- newState = currentState;
- newState.font |= FontItalic;
- newState.rightMargin += 10;
- newState.leftMargin += 10;
- changeState (¤tState, &newState, 0);
- }
-
- /*
- * begin an example. Put in a fixed font, flush left, and let
- * output lines go all the way to the right margin.
- */
-
- void
- beginExample (x)
- int x;
- {
- struct state newState;
-
- newState = currentState;
- newState.font |= FontFixed;
- newState.adjust = AdjustLeft;
- newState.rightMargin = 0;
- changeState (¤tState, &newState, 0);
- }
-
- /*
- * indent the left margin. x may be either positive or negative.
- */
-
- void
- indentLeft (x)
- int x;
- {
- struct state newState;
-
- newState = currentState;
- newState.leftMargin = currentState.leftMargin + x;
- if (newState.leftMargin < 0)
- newState.leftMargin = 0;
- changeState (¤tState, &newState, 0);
- }
-
- /*
- * indent the right margin. x may be either positive or negative.
- */
-
- void
- indentRight (x)
- int x;
- {
- struct state newState;
-
- newState = currentState;
- newState.rightMargin = currentState.rightMargin + x;
- if (newState.rightMargin < 0)
- newState.rightMargin = 0;
- changeState (¤tState, &newState, 0);
- }
-
-
- /*
- * turn underlining on/off
- */
-
- void
- beginUnderline ()
- {
- forceNewLine ();
- printf (".ul 10000\n");
- }
-
- void
- endUnderline ()
- {
- forceNewLine ();
- printf (".ul 0\n");
- }
-
- /*
- * start/end subscript
- */
-
- void
- beginSub ()
- {
- printf ("\\d\\s-2");
- }
-
- void
- endSub ()
- {
- printf ("\\s+2\\u");
- }
-
- /*
- * start/end superscript
- */
-
- void
- beginSuper ()
- {
- printf ("\\u\\s-2");
- }
-
- void
- endSuper ()
- {
- printf ("\\s+2\\d");
- }
-
- struct stk {
- char *cmdName;
- int (*proc)();
- int arg;
- struct state savedState;
- struct stk *next;
- };
-
- struct stk *stack = NULL;
-
- char *
- strsave (s)
- char *s;
- {
- char *p = (char *) malloc (strlen (s) + 1);
- strcpy (p, s);
- return p;
- }
-
- /*
- * push a new environment on the stack. cmdName is the name of the
- * command that did this; it should match the closing command that
- * ends the environment. proc is the function that will undo the
- * changes when we leave the environment. Arg is the argument to pass
- * to the leave proc. state is the current state, to be restored when
- * we leave this environment.
- */
-
- void
- pushStack (cmdName, proc, arg, state)
- char *cmdName;
- int (*proc)();
- int arg;
- struct state *state;
- {
- struct stk *ptr = (struct stk *) malloc (sizeof (struct stk));
-
- ptr->cmdName = strsave (cmdName);
- ptr->proc = proc;
- ptr->arg = arg;
- ptr->savedState = *state;
- ptr->next = stack;
- stack = ptr;
- }
-
- void
- popStack ()
- {
- struct stk *ptr;
-
- if (stack) {
- ptr = stack->next;
- free (stack->cmdName);
- free (stack);
- stack = ptr;
- }
- }
-
- struct cmd {
- char *cmdName;
- void (*enter)();
- void (*leave)();
- int arg;
- } cmds[] = {
- { "bold", changeFont, NULL, FontBold },
- { "italic", changeFont, NULL, FontItalic },
- { "fixed", changeFont, NULL, FontFixed },
- { "smaller", smaller, bigger, 2 },
- { "bigger", bigger, smaller, 2 },
- { "example", beginExample, NULL, 0 },
- { "underline", beginUnderline, endUnderline, 0 },
- { "center", beginBlock, NULL, AdjustCenter },
- { "flushleft", beginBlock, NULL, AdjustLeft },
- { "flushright", beginBlock, NULL, AdjustRight },
- { "indent", indentLeft, NULL, 18 },
- { "indentright", indentRight, NULL, 18 },
- { "outdent", indentLeft, NULL, -18 },
- { "outdentright", indentRight, NULL, -18 },
- /* { "samepage", noOp, NULL, 0 }, */
- { "subscript", beginSub, endSub, 0 },
- { "superscript", beginSuper, endSuper, 0 },
- /* { "heading", noOp, NULL, 0 }, */
- /* { "footing", noOp, NULL, 0 }, */
- { "excerpt", beginExcerpt, NULL, 0 },
- { "paragraph", beginBlock, NULL, AdjustBoth },
- /* { "signature", noOp, NULL, 0 }, */
- };
-
- /*
- * This is called when we see a new command. If we are leaving an
- * environment, make sure its name matches the one we are currently
- * in.
- */
-
- void
- doCommand (s)
- char *s;
- {
- int i;
-
- if (*s == '/') {
- /*
- * leave environment. restore old state and pop stack
- */
- ++s;
- if (stack == NULL)
- fprintf (stderr, "%d:stack underflow\n", lineNumber);
- else if (strcmp (s, stack->cmdName) == 0) {
- if (stack->proc)
- (*(stack->proc))(stack->arg);
- changeState (¤tState, &(stack->savedState), 1);
- popStack ();
- }
- else {
- fprintf (stderr, "%d: incorrect nesting: found %s expected %s\n",
- lineNumber, s, stack->cmdName);
- }
- }
- else {
- /*
- * enter environment. save current command and state on stack.
- */
- struct cmd *p;
- for (i = 0; i < sizeof cmds / sizeof *cmds; ++i) {
- p = &(cmds[i]);
- if (strcmp (s, p->cmdName) == 0) {
- pushStack (p->cmdName, p->leave, p->arg, ¤tState);
- if (p->enter)
- (*(p->enter))(p->arg);
- return;
- }
- }
- pushStack (s, NULL, 0, ¤tState);
- }
- }
-
- char *
- getCmd (ignore)
- int ignore;
- {
- static char buf[52];
- char *ptr;
- int c;
-
- ptr = buf;
- while ((c = getchar ()) != '>') {
- if (ptr < buf + 51 &&
- c != EOF &&
- (isalpha (c) || isdigit (c) || c == '-' ||
- (c == '/' && ptr == buf)))
- *ptr++ = c;
- else {
- /*
- * found an illegal character in the "command"; treat
- * it as text, and output along with the initial '<'.
- * Note that everything that's already in the buffer
- * is necessarily either a letter, digit, or hyphen,
- * so it's not necessary to scan for another '<' or
- * to filter it specially for groff.
- */
- if (!ignore) {
- *ptr = '\0';
- printf ("<%s", buf);
- if (c != EOF)
- ungetc (c, stdin);
- }
- return NULL;
- }
- }
- *ptr = '\0';
- for (ptr = buf; *ptr; ++ptr)
- if (isupper (*ptr))
- *ptr = tolower (*ptr);
- return buf;
- }
-
- init ()
- {
- currentState.font = FontPlain;
- currentState.adjust = AdjustBoth;
- currentState.leftMargin = LeftMargin;
- currentState.rightMargin = RightMargin;
-
- printf (".pl %4.2fi\n", PageHeight / 72.0); /* 8 inches tall */
- printf (".po %4.2fi\n", PageOffset / 72.0); /* 0 page offset */
- printf (".ll %4.2fi\n",
- (float) (PageWidth - currentState.rightMargin) / 72.0);
- printf (".lt %4.2fi\n",
- (float) (PageWidth - currentState.rightMargin) / 72.0);
- printf (".de NP\n"); /* do page breaks */
- printf ("'sp .3i\n");
- printf ("'bp\n");
- printf ("'sp .3i\n");
- printf ("..\n");
- printf (".wh -.4i NP\n");
- printf (".ft TR\n"); /* Times-Roman font */
- printf (".nh\n"); /* no hyphenation */
- printf (".ad l\n"); /* justify both sides ? */
- printf (".fi\n"); /* fill lines */
- AtBOL = 1;
- Broken = 1;
- }
-
- main(argc, argv)
- char **argv;
- {
- int c, i;
- char *command;
- double atof();
- int nestedComments = 0;
-
- for (i = 1; i < argc; ++i) {
- if (strncmp (argv[i], "-width=", 7) == 0)
- PageWidth = (int) (atof (argv[i] + 7) * 72.0);
- else if (strncmp (argv[i], "-height=", 8) == 0)
- PageHeight = (int) (atof (argv[i] + 8) * 72.0);
- else if (strncmp (argv[i], "-left=", 6) == 0)
- LeftMargin = (int) (atof (argv[i] + 6) * 72.0);
- else if (strncmp (argv[i], "-right=", 7) == 0)
- RightMargin = (int) (atof (argv[i] + 7) * 72.0);
- else if (strncmp (argv[i], "-offset=", 8) == 0)
- PageOffset = (int) (atof (argv[i] + 8) * 72.0);
- }
- #if 0
- fprintf (stderr, "PageWidth = %d\n", PageWidth);
- fprintf (stderr, "PageHeight = %d\n", PageHeight);
- fprintf (stderr, "LeftMargin = %d\n", LeftMargin);
- fprintf (stderr, "RightMargin = %d\n", RightMargin);
- fprintf (stderr, "PageOffset = %d\n", PageOffset);
- #endif
- init ();
- while ((c = getchar ()) != EOF) {
- switch (c) {
- case '<':
- if ((command = getCmd (0)) == NULL)
- continue;
-
- /*
- * hack to make sure a newline following <nl> or
- * </paragraph> is ignored.
- */
- if (strcmp (command, "nl") == 0 ||
- strcmp (command, "/paragraph") == 0) {
- if ((c = getchar ()) == EOF)
- break;
- else if (c != '\n')
- ungetc (c, stdin);
- }
-
- if (strcmp (command, "comment") == 0) {
- nestedComments++;
- do {
- while ((c = getchar ()) != '<')
- if (c == EOF) {
- fprintf (stderr, "%d: premature EOF in comment\n",
- lineNumber);
- exit (1);
- }
- if ((command = getCmd (1)) == NULL)
- ;
- else if (strcmp (command, "comment") == 0)
- ++nestedComments;
- else if (strcmp (command, "/comment") == 0)
- --nestedComments;
- } while (nestedComments > 0);
- continue;
- }
- else if (strcmp (command, "lt") == 0) {
- putchar ('<');
- AtBOL = 0;
- }
- else if (strcmp (command, "nl") == 0) {
- if (Broken) {
- printf ("\n");
- AtBOL = 1;
- }
- else
- forceBreak ();
- }
- else if (strcmp (command, "np") == 0) {
- forceNewLine ();
- printf (".NP\n");
- AtBOL = 1;
- }
- else
- doCommand (command);
- break;
- case '\n': /* special for newline */
- lineNumber++;
- if (!AtBOL) {
- printf (" ");
- AtBOL = 0;
- Broken = 0;
- }
- break;
- case '\\': /* special for backslash */
- printf ("\\e");
- AtBOL = 0;
- Broken = 0;
- break;
- case '.': /* special for '.' at start of line */
- if (AtBOL)
- printf ("\\.");
- else
- printf (".");
- AtBOL = 0;
- Broken = 0;
- break;
- default:
- putchar (c);
- AtBOL = 0;
- Broken = 0;
- break;
- }
- }
- if (!AtBOL)
- putchar ('\n');
- }
-