Return-Path: <moore@cs.utk.edu>
Received: from thumper.bellcore.com by greenbush.bellcore.com (4.1/4.7)
	id <AA11412> for nsb; Thu, 9 Jan 92 20:46:13 EST
Received: from wilma.cs.utk.edu by thumper.bellcore.com (4.1/4.7)
	id <AA27929> for nsb@greenbush; Thu, 9 Jan 92 20:46:10 EST
Received: from LOCALHOST by wilma.cs.utk.edu with SMTP (5.61++/2.7c-UTK)
	id AA02750; Thu, 9 Jan 92 20:45:55 -0500
Message-Id: <9201100145.AA02750@wilma.cs.utk.edu>
From: Keith Moore <moore@cs.utk.edu>
To: Nathaniel Borenstein <nsb@thumper.bellcore.com>
Cc: moore@cs.utk.edu
Subject: comments on mime draft, esp. richtext
In-Reply-To: Your message of "Tue, 07 Jan 92 15:14:49 EST."
             <sdOUctK0M2YtA_0OoM@thumper.bellcore.com>
Content-Type: multipart/mixed; boundary="xyzzy"
Date: Thu, 09 Jan 92 20:45:54 EST
Sender: moore@cs.utk.edu

--xyzzy
content-type: text/richtext
content-transfer-encoding: quoted-printable

<flushleft>A few random things: </flushleft><paragraph>(a) it looks like t=
here are strange font changes in the PostScript version. it switches from t=
imes-roman to helvetica to helvetica bold to courier at random places.</par=
agraph>
<paragraph>(b) on page 25 of the postscript version, near the end of the se=
cond paragraph, "but when soft line breaks immediately follow a <lt>newline=
> or <lt>/paragraph>...", I assume you mean "a <fixed><lt>nl></fixed> or <f=
ixed><lt>/paragraph></fixed>..."? (This rule seems a bit odd; I would prefe=
r that either newlines are always ignored, or always ignored whenever they =
immediately follow a command.) </paragraph>
<paragraph>(c) I implemented a richtext-to-groff converter.  Using gxditvie=
w, I now have the abiltity to read richtext messages on my X window display=
. I noticed some oddities with some of the messages that you sent to the li=
st. In particular, your mail composer generates things like:  <example><lt=
>excerpt><italic>random text</italic><lt>newline><lt>/excerpt><lt>excerpt><=
italic>more text</italic><lt>newline><lt>/excerpt></example>...where every =
line of an excerpt is put in its own <fixed><lt>excerpt></fixed>...<fixed><=
lt>/excerpt></fixed> block.  My initial implementation set off each excerpt=
 from the surrounding text with vertical white space ... which would be rea=
sonable if all of the excerpted text appeared together in a single <fixed><=
lt>excerpt></fixed>...<fixed><lt>/excerpt></fixed> block.  So this seems a =
bit odd.</paragraph>
<paragraph>In general it's not clear whether certain commands should cause =
a "break" in running text.  I assume the following:  <flushleft>These caus=
e a break:<nl><nl>
<fixed>center<nl>
flushleft<nl>
flushright<nl>
indent<nl>
indentright<nl>
outdent<nl>
outdentright<nl>
samepage<nl>
excerpt<nl>
paragraph<nl>
signature<nl>
nl<nl>
np<nl>
</fixed><nl>
These do not cause a break: <nl><nl>
<fixed>bold<nl>
italic<nl>
fixed<nl>
smaller<nl>
bigger<nl>
underline<nl>
subscript<nl>
superscript<nl>
heading<nl>
footing<nl>
iso-8859-x<nl>
us-ascii<nl>
comment<nl>
no-op<nl>
lt<nl>
</fixed></flushleft></paragraph>
<paragraph>Also, what is the initial state of the richtext interpreter?  Is=
 text to be wrapped and filled?  Justified left, right, or both?  what is t=
he default font?  (fixed or ordinary roman).</paragraph>
<paragraph>My richtext-to-groff converter is just a one-evening hack, but l=
ots of questions cropped up, and I had only the messages you sent to the li=
st as examples.  (I have enclosed a copy.)  So the question is, is the spec=
 in RFC-MIME precise enough to ensure good interoperability?  This message =
looks pretty good on my screen.  How does it look on yours?</paragraph>
<flushleft>-Keith</flushleft>
--xyzzy
content-type: text/plain; charset="us-ascii"
x-content-type: application/octet-stream; file="richtogroff.c"
content-description: richtext-to-groff converter
content-transfer-encoding: quoted-printable

/*
 * text/richtext to groff converter
 * Copyright =A9 January 1992 by Keith Moore <moore@cs.utk.edu>
 *
 * This program converts mail message bodies of content-type: text/richtex=
t
 * to documents suitable for input to groff.  The output can be either
 * printed or viewed on an X window system display using gxditview.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

int AtBOL =3D 0;=09=09=09/* true if at start of output line */
int Broken =3D 0;=09=09=09/* true if we have emitted a break */
int lineNumber =3D 1;=09=09/* current source line # */
int PageWidth =3D ((5*72)+36);=09/* (in points) 5.5 inches by default */
int PageHeight =3D 8*72;
int LeftMargin =3D 0;
int RightMargin =3D 0;
int PageOffset =3D 0;

void
forceNewLine ()
{
    if (!AtBOL)
=09printf ("\n");
    AtBOL =3D 1;
}

void
forceBreak ()
{
    if (Broken)
=09return;
    forceNewLine ();
    printf (".br\n");
    Broken =3D 1;
}

struct state {
    int font;
#define FontPlain=0900
#define FontBold=0901
#define FontItalic=0902
#define FontFixed=0904
    int adjust;
#define AdjustBoth=0900
#define AdjustLeft=0901
#define AdjustRight=0902
#define AdjustCenter=0903
    int leftMargin;
    int rightMargin;
};

struct state currentState =3D { FontPlain, AdjustBoth, 0, 0 };

/*
 * font names for groff, assuming PostScript or X11 drivers
 */

char *fontnames[] =3D {
=09=09=09=09/* FIB */
    "TR",=09=09=09/* 000 (Times-Roman) */
    "TB",=09=09=09/* 001 (Times-Bold) */
    "TI",=09=09=09/* 010 (Times-Italic) */
    "TBI",=09=09=09/* 011 (Times-BoldItalic) */
    "CR",=09=09=09/* 100 (Courier-Roman) */
    "CB",=09=09=09/* 101 (Courier-Bold) */
    "CI",=09=09=09/* 110 (Courier-Italic) */
    "CBI",=09=09=09/* 111 (Courier-BoldItalic) */
};

char *adjust[] =3D {
    ".ad b\n.fi\n",=09=09/* AdjustBoth */
    ".ad l\n.fi\n",=09=09/* AdjustLeft */
    ".ad r\n.fi\n",=09=09/* AdjustRight */
    ".nf\n.ce 10000\n",=09=09/* AdjustCenter */
};


void
changeState (oldState, newState, undo)
struct state *oldState, *newState;
int undo;
{
    if (undo =3D=3D 0) {
=09if (oldState->font !=3D newState->font) {
=09    printf ("\\f[%s]", fontnames[newState->font]);
=09    AtBOL =3D 0;
=09    Broken =3D 0;
=09}
    }
    if (oldState->leftMargin !=3D newState->leftMargin) {
=09forceBreak ();
=09printf (".in %4.2fi\n", (float) newState->leftMargin / 72.0);
=09AtBOL =3D 1;
    }
    if (oldState->rightMargin !=3D newState->rightMargin) {
=09forceBreak ();
=09printf (".ll %4.2fi\n",
=09=09(float) (PageWidth - newState->rightMargin) / 72.0);
=09AtBOL =3D 1;
    }
    if (oldState->adjust !=3D newState->adjust) {
=09forceBreak ();
=09if (oldState->adjust =3D=3D AdjustCenter)
=09    printf (".ce 0\n");
=09printf ("%s", adjust[newState->adjust]);
=09AtBOL =3D 1;
    }
    if (undo =3D=3D 1) {
=09if (oldState->font !=3D newState->font) {
=09    printf ("\\f[%s]", fontnames[newState->font]);
=09    AtBOL =3D 0;
=09    Broken =3D 0;
=09}
    }
    currentState =3D *newState;
}

void
changeFont (font)
{
    struct state newState;

    newState =3D currentState;
    newState.font |=3D font;
    changeState (&currentState, &newState, 0);
}

void
smaller (size)
{
    printf ("\\s-%d", size);
    AtBOL =3D 0;
    Broken =3D 0;
}

void
bigger (size)
{
    printf ("\\s+%d", size);
    AtBOL =3D 0;
    Broken =3D 0;
}


void
beginBlock (adjust)
{
    struct state newState;

    newState =3D currentState;
    =

    forceBreak ();
    printf (".ne 3\n");
    AtBOL =3D 1;
    newState.adjust =3D adjust;
    changeState (&currentState, &newState, 0);
}

void
beginExcerpt (x)
int x;
{
    struct state newState;

    newState =3D currentState;
    newState.font =3D FontItalic;
    changeState (&currentState, &newState, 0);
}

void
beginExample (x)
int x;
{
    struct state newState;

    newState =3D currentState;
    newState.font =3D FontFixed;
    newState.adjust =3D AdjustLeft;
    newState.rightMargin =3D 0;
    changeState (&currentState, &newState, 0);
}

void
indentLeft (x)
int x;
{
    struct state newState;

    newState =3D currentState;
    newState.leftMargin =3D currentState.leftMargin + x;
    if (newState.leftMargin < 0)
=09newState.leftMargin =3D 0;
    changeState (&currentState, &newState, 0);
}

void
indentRight (x)
int x;
{
    struct state newState;

    newState =3D currentState;
    newState.rightMargin =3D currentState.rightMargin + x;
    if (newState.rightMargin < 0)
=09newState.rightMargin =3D 0;
    changeState (&currentState, &newState, 0);
}


void
beginUnderline ()
{
    forceNewLine ();
    printf (".ul 10000\n");
}

void
endUnderline ()
{
    forceNewLine ();
    printf (".ul 0\n");
}

void
beginSub ()
{
    printf ("\\d\\s-2");
}

void
endSub ()
{
    printf ("\\s+2\\u");
}

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 =3D NULL;

char *
strsave (s)
char *s;
{
    char *p =3D (char *) malloc (strlen (s) + 1);
    strcpy (p, s);
    return p;
}

void
pushStack (cmdName, proc, arg, state)
char *cmdName;
int (*proc)();
int arg;
struct state *state;
{
    struct stk *ptr =3D (struct stk *) malloc (sizeof (struct stk));

    ptr->cmdName =3D strsave (cmdName);
    ptr->proc =3D proc;
    ptr->arg =3D arg;
    ptr->savedState =3D *state;
    ptr->next =3D stack;
    stack =3D ptr;
}

void
popStack ()
{
    struct stk *ptr;

    if (stack) {
=09ptr =3D stack->next;
=09free (stack->cmdName);
=09free (stack);
=09stack =3D ptr;
    }
}

struct cmd {
    char *cmdName;
    void (*enter)();
    void (*leave)();
    int arg;
} cmds[] =3D {
    { "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 }, */
};

void
doCommand (s)
char *s;
{
    int i;

    if (*s =3D=3D '/') {=09=09/* leave command */
=09++s;
=09if (stack =3D=3D NULL)
=09    fprintf (stderr, "%d:stack underflow\n", lineNumber);
=09else if (strcmp (s, stack->cmdName) =3D=3D 0) {
=09    if (stack->proc)
=09=09(*(stack->proc))(stack->arg);
=09    changeState (&currentState, &(stack->savedState), 1);
=09    popStack ();
=09}
=09else {
=09    fprintf (stderr, "%d: incorrect nesting: found %s expected %s\n",
=09=09     lineNumber, s, stack->cmdName);
=09}
    }
    else {=09=09=09/* enter command */
=09struct cmd *p;
=09for (i =3D 0; i < sizeof cmds / sizeof *cmds; ++i) {
=09    p =3D &(cmds[i]);
=09    if (strcmp (s, p->cmdName) =3D=3D 0) {
=09=09pushStack (p->cmdName, p->leave, p->arg, &currentState);
=09=09if (p->enter)
=09=09    (*(p->enter))(p->arg);
=09=09return;
=09    }
=09}
=09pushStack (s, NULL, 0, &currentState);
    }
}

char *
getCmd ()
{
    static char buf[52];
    char *ptr;
    int c;

    ptr =3D buf;
    while ((c =3D getchar ()) !=3D '>') {
=09if (c =3D=3D EOF) {
=09    fprintf (stderr, "%d: premature EOF\n", lineNumber);
=09    exit (1);
=09}
=09if (ptr >=3D buf + 51) {
=09    fprintf (stderr, "%d: no closing '>'\n", lineNumber);
=09    exit (1);
=09}
=09if (isupper (c))
=09    c =3D tolower (c);
=09*ptr++ =3D c;
    }
    *ptr =3D '\0';
    return buf;
}

init ()
{
    currentState.font =3D FontPlain;
    currentState.adjust =3D AdjustBoth;
    currentState.leftMargin =3D LeftMargin;
    currentState.rightMargin =3D RightMargin;

    printf (".pl %4.2fi\n", PageHeight / 72.0);=09/* 8 inches tall */
    printf (".po %4.2fi\n", PageOffset / 72.0);=09/* 0 page offset */
    printf (".ll %4.2fi\n",
=09    (float) (PageWidth - currentState.rightMargin) / 72.0);
    printf (".lt %4.2fi\n",
=09    (float) (PageWidth - currentState.rightMargin) / 72.0);
    printf (".de NP\n");=09/* 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");=09/* Times-Roman font */
    printf (".nh\n");=09=09/* no hyphenation */
    printf (".ad l\n");=09=09/* justify both sides ? */
    printf (".fi\n");=09=09/* fill lines */
    AtBOL =3D 1;
    Broken =3D 1;
}

main(argc, argv)
char **argv;
{
    int c, i;
    char *command;
    double atof();
    int nestedComments =3D 0;

    /*
     * XXX add command-line parsing for page-width and page-length options
     */

    for (i =3D 1; i < argc; ++i) {
=09if (strncmp (argv[i], "width=3D", 6) =3D=3D 0)
=09    PageWidth =3D (int) (atof (argv[i] + 6) * 72.0);
=09else if (strncmp (argv[i], "height=3D", 7) =3D=3D 0)
=09    PageHeight =3D (int) (atof (argv[i] + 7) * 72.0);
=09else if (strncmp (argv[i], "left=3D", 5) =3D=3D 0)
=09    LeftMargin =3D (int) (atof (argv[i] + 5) * 72.0);
=09else if (strncmp (argv[i], "right=3D", 6) =3D=3D 0)
=09    RightMargin =3D (int) (atof (argv[i] + 6) * 72.0);
=09else if (strncmp (argv[i], "offset=3D", 7) =3D=3D 0)
=09    PageOffset =3D (int) (atof (argv[i] + 7) * 72.0);
    }
    init ();
    while ((c =3D getchar ()) !=3D EOF) {
=09switch (c) {
=09case '<':
=09    command =3D getCmd ();

=09    /*
=09     * hack to make sure a newline following <nl> or
=09     * </paragraph> is ignored.
=09     */
=09    if (strcmp (command, "nl") =3D=3D 0 ||
=09=09strcmp (command, "/paragraph") =3D=3D 0) {
=09=09if ((c =3D getchar ()) =3D=3D EOF)
=09=09    break;
=09=09else if (c !=3D '\n')
=09=09    ungetc (c, stdin);
=09    }

=09    if (strcmp (command, "comment") =3D=3D 0) {
=09=09nestedComments++;
=09=09do {
                    while ((c =3D getchar ()) !=3D '<')
=09=09=09if (c =3D=3D EOF) {
=09=09=09    fprintf (stderr, "%d: premature EOF in comment\n",
=09=09=09=09     lineNumber);
=09=09=09    exit (1);
=09=09=09}
=09=09    command =3D getCmd ();
=09=09    if (strcmp (command, "comment") =3D=3D 0)
=09=09=09++nestedComments;
=09=09    else if (strcmp (command, "/comment") =3D=3D 0)
=09=09=09--nestedComments;
                } while (nestedComments > 0);
=09=09continue;
=09    }
            else if (strcmp (command, "lt") =3D=3D 0) {
                putchar ('<');
                AtBOL =3D 0;
            }
=09    else if (strcmp (command, "nl") =3D=3D 0) {
=09=09if (Broken) {
=09=09    printf ("\n");
=09=09    AtBOL =3D 1;
=09=09}
=09=09else
=09=09    forceBreak ();
            }
=09    else if (strcmp (command, "np") =3D=3D 0) {
=09=09forceNewLine ();
=09=09printf (".NP\n");
                AtBOL =3D 1;
            }
=09    else
=09=09doCommand (command);
=09    break;
=09case '\n':=09=09/* special for newline */
=09    lineNumber++;
=09    if (!AtBOL) {
=09=09printf (" ");
=09=09AtBOL =3D 0;
=09=09Broken =3D 0;
=09    }
=09    break;
=09case '\\':=09=09/* special for backslash */
=09    printf ("\\e");
=09    AtBOL =3D 0;
=09    Broken =3D 0;
=09    break;
=09case '.':=09=09/* special for '.' at start of line */
=09    if (AtBOL)
=09=09printf ("\\.");
=09    else
=09=09printf (".");
=09    AtBOL =3D 0;
=09    Broken =3D 0;
=09    break;
=09default:
            putchar (c);
            AtBOL =3D 0;
=09    Broken =3D 0;
=09    break;
=09}
    }
}
--xyzzy--

