+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 (¤tState, &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 (¤tState, &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 (¤tState, &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 (¤tState, &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 (¤tState, &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 (¤tState, &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 (¤tState, &(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, ¤tState); 468 =09=09if (p->enter) 469 =09=09 (*(p->enter))(p->arg); 470 =09=09return; 471 =09 } 472 =09} 473 =09pushStack (s, NULL, 0, ¤tState); 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