rohrpost

A commandline mail client to change the world as we see it.
git clone git://r-36.net/rohrpost
Log | Files | Refs | README | LICENSE

+kdPOk1u0M2Yt8hPuAh (16412B)


      1 Return-Path: <moore@cs.utk.edu>
      2 Received: from thumper.bellcore.com by greenbush.bellcore.com (4.1/4.7)
      3 	id <AA11412> for nsb; Thu, 9 Jan 92 20:46:13 EST
      4 Received: from wilma.cs.utk.edu by thumper.bellcore.com (4.1/4.7)
      5 	id <AA27929> for nsb@greenbush; Thu, 9 Jan 92 20:46:10 EST
      6 Received: from LOCALHOST by wilma.cs.utk.edu with SMTP (5.61++/2.7c-UTK)
      7 	id AA02750; Thu, 9 Jan 92 20:45:55 -0500
      8 Message-Id: <9201100145.AA02750@wilma.cs.utk.edu>
      9 From: Keith Moore <moore@cs.utk.edu>
     10 To: Nathaniel Borenstein <nsb@thumper.bellcore.com>
     11 Cc: moore@cs.utk.edu
     12 Subject: comments on mime draft, esp. richtext
     13 In-Reply-To: Your message of "Tue, 07 Jan 92 15:14:49 EST."
     14              <sdOUctK0M2YtA_0OoM@thumper.bellcore.com>
     15 Content-Type: multipart/mixed; boundary="xyzzy"
     16 Date: Thu, 09 Jan 92 20:45:54 EST
     17 Sender: moore@cs.utk.edu
     18 
     19 --xyzzy
     20 content-type: text/richtext
     21 content-transfer-encoding: quoted-printable
     22 
     23 <flushleft>A few random things: </flushleft><paragraph>(a) it looks like t=
     24 here are strange font changes in the PostScript version. it switches from t=
     25 imes-roman to helvetica to helvetica bold to courier at random places.</par=
     26 agraph>
     27 <paragraph>(b) on page 25 of the postscript version, near the end of the se=
     28 cond paragraph, "but when soft line breaks immediately follow a <lt>newline=
     29 > or <lt>/paragraph>...", I assume you mean "a <fixed><lt>nl></fixed> or <f=
     30 ixed><lt>/paragraph></fixed>..."? (This rule seems a bit odd; I would prefe=
     31 r that either newlines are always ignored, or always ignored whenever they =
     32 immediately follow a command.) </paragraph>
     33 <paragraph>(c) I implemented a richtext-to-groff converter.  Using gxditvie=
     34 w, I now have the abiltity to read richtext messages on my X window display=
     35 . I noticed some oddities with some of the messages that you sent to the li=
     36 st. In particular, your mail composer generates things like:  <example><lt=
     37 >excerpt><italic>random text</italic><lt>newline><lt>/excerpt><lt>excerpt><=
     38 italic>more text</italic><lt>newline><lt>/excerpt></example>...where every =
     39 line of an excerpt is put in its own <fixed><lt>excerpt></fixed>...<fixed><=
     40 lt>/excerpt></fixed> block.  My initial implementation set off each excerpt=
     41  from the surrounding text with vertical white space ... which would be rea=
     42 sonable if all of the excerpted text appeared together in a single <fixed><=
     43 lt>excerpt></fixed>...<fixed><lt>/excerpt></fixed> block.  So this seems a =
     44 bit odd.</paragraph>
     45 <paragraph>In general it's not clear whether certain commands should cause =
     46 a "break" in running text.  I assume the following:  <flushleft>These caus=
     47 e a break:<nl><nl>
     48 <fixed>center<nl>
     49 flushleft<nl>
     50 flushright<nl>
     51 indent<nl>
     52 indentright<nl>
     53 outdent<nl>
     54 outdentright<nl>
     55 samepage<nl>
     56 excerpt<nl>
     57 paragraph<nl>
     58 signature<nl>
     59 nl<nl>
     60 np<nl>
     61 </fixed><nl>
     62 These do not cause a break: <nl><nl>
     63 <fixed>bold<nl>
     64 italic<nl>
     65 fixed<nl>
     66 smaller<nl>
     67 bigger<nl>
     68 underline<nl>
     69 subscript<nl>
     70 superscript<nl>
     71 heading<nl>
     72 footing<nl>
     73 iso-8859-x<nl>
     74 us-ascii<nl>
     75 comment<nl>
     76 no-op<nl>
     77 lt<nl>
     78 </fixed></flushleft></paragraph>
     79 <paragraph>Also, what is the initial state of the richtext interpreter?  Is=
     80  text to be wrapped and filled?  Justified left, right, or both?  what is t=
     81 he default font?  (fixed or ordinary roman).</paragraph>
     82 <paragraph>My richtext-to-groff converter is just a one-evening hack, but l=
     83 ots of questions cropped up, and I had only the messages you sent to the li=
     84 st as examples.  (I have enclosed a copy.)  So the question is, is the spec=
     85  in RFC-MIME precise enough to ensure good interoperability?  This message =
     86 looks pretty good on my screen.  How does it look on yours?</paragraph>
     87 <flushleft>-Keith</flushleft>
     88 --xyzzy
     89 content-type: text/plain; charset="us-ascii"
     90 x-content-type: application/octet-stream; file="richtogroff.c"
     91 content-description: richtext-to-groff converter
     92 content-transfer-encoding: quoted-printable
     93 
     94 /*
     95  * text/richtext to groff converter
     96  * Copyright =A9 January 1992 by Keith Moore <moore@cs.utk.edu>
     97  *
     98  * This program converts mail message bodies of content-type: text/richtex=
     99 t
    100  * to documents suitable for input to groff.  The output can be either
    101  * printed or viewed on an X window system display using gxditview.
    102  *
    103  * This program is free software; you can redistribute it and/or modify
    104  * it under the terms of the GNU General Public License as published by
    105  * the Free Software Foundation; either version 2 of the License, or
    106  * (at your option) any later version.
    107  * =
    108 
    109  * This program is distributed in the hope that it will be useful,
    110  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    111  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    112  * GNU General Public License for more details.
    113  *
    114  * You should have received a copy of the GNU General Public License
    115  * along with this program; if not, write to the Free Software
    116  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    117  */
    118 
    119 #include <stdio.h>
    120 #include <ctype.h>
    121 
    122 int AtBOL =3D 0;=09=09=09/* true if at start of output line */
    123 int Broken =3D 0;=09=09=09/* true if we have emitted a break */
    124 int lineNumber =3D 1;=09=09/* current source line # */
    125 int PageWidth =3D ((5*72)+36);=09/* (in points) 5.5 inches by default */
    126 int PageHeight =3D 8*72;
    127 int LeftMargin =3D 0;
    128 int RightMargin =3D 0;
    129 int PageOffset =3D 0;
    130 
    131 void
    132 forceNewLine ()
    133 {
    134     if (!AtBOL)
    135 =09printf ("\n");
    136     AtBOL =3D 1;
    137 }
    138 
    139 void
    140 forceBreak ()
    141 {
    142     if (Broken)
    143 =09return;
    144     forceNewLine ();
    145     printf (".br\n");
    146     Broken =3D 1;
    147 }
    148 
    149 struct state {
    150     int font;
    151 #define FontPlain=0900
    152 #define FontBold=0901
    153 #define FontItalic=0902
    154 #define FontFixed=0904
    155     int adjust;
    156 #define AdjustBoth=0900
    157 #define AdjustLeft=0901
    158 #define AdjustRight=0902
    159 #define AdjustCenter=0903
    160     int leftMargin;
    161     int rightMargin;
    162 };
    163 
    164 struct state currentState =3D { FontPlain, AdjustBoth, 0, 0 };
    165 
    166 /*
    167  * font names for groff, assuming PostScript or X11 drivers
    168  */
    169 
    170 char *fontnames[] =3D {
    171 =09=09=09=09/* FIB */
    172     "TR",=09=09=09/* 000 (Times-Roman) */
    173     "TB",=09=09=09/* 001 (Times-Bold) */
    174     "TI",=09=09=09/* 010 (Times-Italic) */
    175     "TBI",=09=09=09/* 011 (Times-BoldItalic) */
    176     "CR",=09=09=09/* 100 (Courier-Roman) */
    177     "CB",=09=09=09/* 101 (Courier-Bold) */
    178     "CI",=09=09=09/* 110 (Courier-Italic) */
    179     "CBI",=09=09=09/* 111 (Courier-BoldItalic) */
    180 };
    181 
    182 char *adjust[] =3D {
    183     ".ad b\n.fi\n",=09=09/* AdjustBoth */
    184     ".ad l\n.fi\n",=09=09/* AdjustLeft */
    185     ".ad r\n.fi\n",=09=09/* AdjustRight */
    186     ".nf\n.ce 10000\n",=09=09/* AdjustCenter */
    187 };
    188 
    189 
    190 void
    191 changeState (oldState, newState, undo)
    192 struct state *oldState, *newState;
    193 int undo;
    194 {
    195     if (undo =3D=3D 0) {
    196 =09if (oldState->font !=3D newState->font) {
    197 =09    printf ("\\f[%s]", fontnames[newState->font]);
    198 =09    AtBOL =3D 0;
    199 =09    Broken =3D 0;
    200 =09}
    201     }
    202     if (oldState->leftMargin !=3D newState->leftMargin) {
    203 =09forceBreak ();
    204 =09printf (".in %4.2fi\n", (float) newState->leftMargin / 72.0);
    205 =09AtBOL =3D 1;
    206     }
    207     if (oldState->rightMargin !=3D newState->rightMargin) {
    208 =09forceBreak ();
    209 =09printf (".ll %4.2fi\n",
    210 =09=09(float) (PageWidth - newState->rightMargin) / 72.0);
    211 =09AtBOL =3D 1;
    212     }
    213     if (oldState->adjust !=3D newState->adjust) {
    214 =09forceBreak ();
    215 =09if (oldState->adjust =3D=3D AdjustCenter)
    216 =09    printf (".ce 0\n");
    217 =09printf ("%s", adjust[newState->adjust]);
    218 =09AtBOL =3D 1;
    219     }
    220     if (undo =3D=3D 1) {
    221 =09if (oldState->font !=3D newState->font) {
    222 =09    printf ("\\f[%s]", fontnames[newState->font]);
    223 =09    AtBOL =3D 0;
    224 =09    Broken =3D 0;
    225 =09}
    226     }
    227     currentState =3D *newState;
    228 }
    229 
    230 void
    231 changeFont (font)
    232 {
    233     struct state newState;
    234 
    235     newState =3D currentState;
    236     newState.font |=3D font;
    237     changeState (&currentState, &newState, 0);
    238 }
    239 
    240 void
    241 smaller (size)
    242 {
    243     printf ("\\s-%d", size);
    244     AtBOL =3D 0;
    245     Broken =3D 0;
    246 }
    247 
    248 void
    249 bigger (size)
    250 {
    251     printf ("\\s+%d", size);
    252     AtBOL =3D 0;
    253     Broken =3D 0;
    254 }
    255 
    256 
    257 void
    258 beginBlock (adjust)
    259 {
    260     struct state newState;
    261 
    262     newState =3D currentState;
    263     =
    264 
    265     forceBreak ();
    266     printf (".ne 3\n");
    267     AtBOL =3D 1;
    268     newState.adjust =3D adjust;
    269     changeState (&currentState, &newState, 0);
    270 }
    271 
    272 void
    273 beginExcerpt (x)
    274 int x;
    275 {
    276     struct state newState;
    277 
    278     newState =3D currentState;
    279     newState.font =3D FontItalic;
    280     changeState (&currentState, &newState, 0);
    281 }
    282 
    283 void
    284 beginExample (x)
    285 int x;
    286 {
    287     struct state newState;
    288 
    289     newState =3D currentState;
    290     newState.font =3D FontFixed;
    291     newState.adjust =3D AdjustLeft;
    292     newState.rightMargin =3D 0;
    293     changeState (&currentState, &newState, 0);
    294 }
    295 
    296 void
    297 indentLeft (x)
    298 int x;
    299 {
    300     struct state newState;
    301 
    302     newState =3D currentState;
    303     newState.leftMargin =3D currentState.leftMargin + x;
    304     if (newState.leftMargin < 0)
    305 =09newState.leftMargin =3D 0;
    306     changeState (&currentState, &newState, 0);
    307 }
    308 
    309 void
    310 indentRight (x)
    311 int x;
    312 {
    313     struct state newState;
    314 
    315     newState =3D currentState;
    316     newState.rightMargin =3D currentState.rightMargin + x;
    317     if (newState.rightMargin < 0)
    318 =09newState.rightMargin =3D 0;
    319     changeState (&currentState, &newState, 0);
    320 }
    321 
    322 
    323 void
    324 beginUnderline ()
    325 {
    326     forceNewLine ();
    327     printf (".ul 10000\n");
    328 }
    329 
    330 void
    331 endUnderline ()
    332 {
    333     forceNewLine ();
    334     printf (".ul 0\n");
    335 }
    336 
    337 void
    338 beginSub ()
    339 {
    340     printf ("\\d\\s-2");
    341 }
    342 
    343 void
    344 endSub ()
    345 {
    346     printf ("\\s+2\\u");
    347 }
    348 
    349 void
    350 beginSuper ()
    351 {
    352     printf ("\\u\\s-2");
    353 }
    354 
    355 void
    356 endSuper ()
    357 {
    358     printf ("\\s+2\\d");
    359 }
    360 
    361 struct stk {
    362     char *cmdName;
    363     int (*proc)();
    364     int arg;
    365     struct state savedState;
    366     struct stk *next;
    367 };
    368 
    369 struct stk *stack =3D NULL;
    370 
    371 char *
    372 strsave (s)
    373 char *s;
    374 {
    375     char *p =3D (char *) malloc (strlen (s) + 1);
    376     strcpy (p, s);
    377     return p;
    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 =3D (struct stk *) malloc (sizeof (struct stk));
    388 
    389     ptr->cmdName =3D strsave (cmdName);
    390     ptr->proc =3D proc;
    391     ptr->arg =3D arg;
    392     ptr->savedState =3D *state;
    393     ptr->next =3D stack;
    394     stack =3D ptr;
    395 }
    396 
    397 void
    398 popStack ()
    399 {
    400     struct stk *ptr;
    401 
    402     if (stack) {
    403 =09ptr =3D stack->next;
    404 =09free (stack->cmdName);
    405 =09free (stack);
    406 =09stack =3D ptr;
    407     }
    408 }
    409 
    410 struct cmd {
    411     char *cmdName;
    412     void (*enter)();
    413     void (*leave)();
    414     int arg;
    415 } cmds[] =3D {
    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 
    427     { "indent", indentLeft, NULL, 18 },
    428     { "indentright", indentRight, NULL, 18 },
    429     { "outdent", indentLeft, NULL, -18 },
    430     { "outdentright", indentRight, NULL, -18 },
    431 /*  { "samepage", noOp, NULL, 0 }, */
    432     { "subscript", beginSub, endSub, 0 },
    433     { "superscript", beginSuper, endSuper, 0 },
    434 /*  { "heading", noOp, NULL, 0 }, */
    435 /*  { "footing", noOp, NULL, 0 }, */
    436     { "excerpt", beginExcerpt, NULL, 0 },
    437     { "paragraph", beginBlock, NULL, AdjustBoth },
    438 /*  { "signature", noOp, NULL, 0 }, */
    439 };
    440 
    441 void
    442 doCommand (s)
    443 char *s;
    444 {
    445     int i;
    446 
    447     if (*s =3D=3D '/') {=09=09/* leave command */
    448 =09++s;
    449 =09if (stack =3D=3D NULL)
    450 =09    fprintf (stderr, "%d:stack underflow\n", lineNumber);
    451 =09else if (strcmp (s, stack->cmdName) =3D=3D 0) {
    452 =09    if (stack->proc)
    453 =09=09(*(stack->proc))(stack->arg);
    454 =09    changeState (&currentState, &(stack->savedState), 1);
    455 =09    popStack ();
    456 =09}
    457 =09else {
    458 =09    fprintf (stderr, "%d: incorrect nesting: found %s expected %s\n",
    459 =09=09     lineNumber, s, stack->cmdName);
    460 =09}
    461     }
    462     else {=09=09=09/* enter command */
    463 =09struct cmd *p;
    464 =09for (i =3D 0; i < sizeof cmds / sizeof *cmds; ++i) {
    465 =09    p =3D &(cmds[i]);
    466 =09    if (strcmp (s, p->cmdName) =3D=3D 0) {
    467 =09=09pushStack (p->cmdName, p->leave, p->arg, &currentState);
    468 =09=09if (p->enter)
    469 =09=09    (*(p->enter))(p->arg);
    470 =09=09return;
    471 =09    }
    472 =09}
    473 =09pushStack (s, NULL, 0, &currentState);
    474     }
    475 }
    476 
    477 char *
    478 getCmd ()
    479 {
    480     static char buf[52];
    481     char *ptr;
    482     int c;
    483 
    484     ptr =3D buf;
    485     while ((c =3D getchar ()) !=3D '>') {
    486 =09if (c =3D=3D EOF) {
    487 =09    fprintf (stderr, "%d: premature EOF\n", lineNumber);
    488 =09    exit (1);
    489 =09}
    490 =09if (ptr >=3D buf + 51) {
    491 =09    fprintf (stderr, "%d: no closing '>'\n", lineNumber);
    492 =09    exit (1);
    493 =09}
    494 =09if (isupper (c))
    495 =09    c =3D tolower (c);
    496 =09*ptr++ =3D c;
    497     }
    498     *ptr =3D '\0';
    499     return buf;
    500 }
    501 
    502 init ()
    503 {
    504     currentState.font =3D FontPlain;
    505     currentState.adjust =3D AdjustBoth;
    506     currentState.leftMargin =3D LeftMargin;
    507     currentState.rightMargin =3D RightMargin;
    508 
    509     printf (".pl %4.2fi\n", PageHeight / 72.0);=09/* 8 inches tall */
    510     printf (".po %4.2fi\n", PageOffset / 72.0);=09/* 0 page offset */
    511     printf (".ll %4.2fi\n",
    512 =09    (float) (PageWidth - currentState.rightMargin) / 72.0);
    513     printf (".lt %4.2fi\n",
    514 =09    (float) (PageWidth - currentState.rightMargin) / 72.0);
    515     printf (".de NP\n");=09/* do page breaks */
    516     printf ("'sp .3i\n");
    517     printf ("'bp\n");
    518     printf ("'sp .3i\n");
    519     printf ("..\n");
    520     printf (".wh -.4i NP\n");
    521     printf (".ft TR\n");=09/* Times-Roman font */
    522     printf (".nh\n");=09=09/* no hyphenation */
    523     printf (".ad l\n");=09=09/* justify both sides ? */
    524     printf (".fi\n");=09=09/* fill lines */
    525     AtBOL =3D 1;
    526     Broken =3D 1;
    527 }
    528 
    529 main(argc, argv)
    530 char **argv;
    531 {
    532     int c, i;
    533     char *command;
    534     double atof();
    535     int nestedComments =3D 0;
    536 
    537     /*
    538      * XXX add command-line parsing for page-width and page-length options
    539      */
    540 
    541     for (i =3D 1; i < argc; ++i) {
    542 =09if (strncmp (argv[i], "width=3D", 6) =3D=3D 0)
    543 =09    PageWidth =3D (int) (atof (argv[i] + 6) * 72.0);
    544 =09else if (strncmp (argv[i], "height=3D", 7) =3D=3D 0)
    545 =09    PageHeight =3D (int) (atof (argv[i] + 7) * 72.0);
    546 =09else if (strncmp (argv[i], "left=3D", 5) =3D=3D 0)
    547 =09    LeftMargin =3D (int) (atof (argv[i] + 5) * 72.0);
    548 =09else if (strncmp (argv[i], "right=3D", 6) =3D=3D 0)
    549 =09    RightMargin =3D (int) (atof (argv[i] + 6) * 72.0);
    550 =09else if (strncmp (argv[i], "offset=3D", 7) =3D=3D 0)
    551 =09    PageOffset =3D (int) (atof (argv[i] + 7) * 72.0);
    552     }
    553     init ();
    554     while ((c =3D getchar ()) !=3D EOF) {
    555 =09switch (c) {
    556 =09case '<':
    557 =09    command =3D getCmd ();
    558 
    559 =09    /*
    560 =09     * hack to make sure a newline following <nl> or
    561 =09     * </paragraph> is ignored.
    562 =09     */
    563 =09    if (strcmp (command, "nl") =3D=3D 0 ||
    564 =09=09strcmp (command, "/paragraph") =3D=3D 0) {
    565 =09=09if ((c =3D getchar ()) =3D=3D EOF)
    566 =09=09    break;
    567 =09=09else if (c !=3D '\n')
    568 =09=09    ungetc (c, stdin);
    569 =09    }
    570 
    571 =09    if (strcmp (command, "comment") =3D=3D 0) {
    572 =09=09nestedComments++;
    573 =09=09do {
    574                     while ((c =3D getchar ()) !=3D '<')
    575 =09=09=09if (c =3D=3D EOF) {
    576 =09=09=09    fprintf (stderr, "%d: premature EOF in comment\n",
    577 =09=09=09=09     lineNumber);
    578 =09=09=09    exit (1);
    579 =09=09=09}
    580 =09=09    command =3D getCmd ();
    581 =09=09    if (strcmp (command, "comment") =3D=3D 0)
    582 =09=09=09++nestedComments;
    583 =09=09    else if (strcmp (command, "/comment") =3D=3D 0)
    584 =09=09=09--nestedComments;
    585                 } while (nestedComments > 0);
    586 =09=09continue;
    587 =09    }
    588             else if (strcmp (command, "lt") =3D=3D 0) {
    589                 putchar ('<');
    590                 AtBOL =3D 0;
    591             }
    592 =09    else if (strcmp (command, "nl") =3D=3D 0) {
    593 =09=09if (Broken) {
    594 =09=09    printf ("\n");
    595 =09=09    AtBOL =3D 1;
    596 =09=09}
    597 =09=09else
    598 =09=09    forceBreak ();
    599             }
    600 =09    else if (strcmp (command, "np") =3D=3D 0) {
    601 =09=09forceNewLine ();
    602 =09=09printf (".NP\n");
    603                 AtBOL =3D 1;
    604             }
    605 =09    else
    606 =09=09doCommand (command);
    607 =09    break;
    608 =09case '\n':=09=09/* special for newline */
    609 =09    lineNumber++;
    610 =09    if (!AtBOL) {
    611 =09=09printf (" ");
    612 =09=09AtBOL =3D 0;
    613 =09=09Broken =3D 0;
    614 =09    }
    615 =09    break;
    616 =09case '\\':=09=09/* special for backslash */
    617 =09    printf ("\\e");
    618 =09    AtBOL =3D 0;
    619 =09    Broken =3D 0;
    620 =09    break;
    621 =09case '.':=09=09/* special for '.' at start of line */
    622 =09    if (AtBOL)
    623 =09=09printf ("\\.");
    624 =09    else
    625 =09=09printf (".");
    626 =09    AtBOL =3D 0;
    627 =09    Broken =3D 0;
    628 =09    break;
    629 =09default:
    630             putchar (c);
    631             AtBOL =3D 0;
    632 =09    Broken =3D 0;
    633 =09    break;
    634 =09}
    635     }
    636 }
    637 --xyzzy--
    638