home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / metamail / contrib / ezview / unscribe.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-17  |  32.1 KB  |  1,049 lines

  1. /*
  2. Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
  3.  
  4. Permission to use, copy, modify, and distribute this material 
  5. for any purpose and without fee is hereby granted, provided 
  6. that the above copyright notice and this permission notice 
  7. appear in all copies, and that the name of Bellcore not be 
  8. used in advertising or publicity pertaining to this 
  9. material without the specific, prior written permission 
  10. of an authorized representative of Bellcore.  BELLCORE 
  11. MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 
  12. OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", 
  13. WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  14. */
  15. /* ********************************************************************** *\
  16.  *         Copyright IBM Corporation 1988,1989 - All Rights Reserved      *
  17.  *        For full copyright information see:'andrew/config/COPYRITE'     *
  18. \* ********************************************************************** */
  19. /*
  20.     unscribe.c -- Remove WriteScribe formatting for vanilla interpretation
  21.  
  22. This function is an amazingly cheap rendition of a Scribe compilation with @device(File).
  23.  
  24. It also has a function, PrintQuotingFormatting, which can be used to take
  25. unformatted text (e.g. headers you are forwarding) and quote it past a given
  26. version of the datastream interpretation.  
  27.  
  28. */
  29.  
  30. #include <stdio.h>
  31. #include <andrewos.h> /* strings.h */
  32. #include <ctype.h>
  33. #include <util.h>
  34. #include "mail.h"
  35. #include <config.h>
  36.  
  37. #ifndef    MIN
  38. #define    MIN(a,b) (((a)<(b))?(a):(b))
  39. #endif
  40.  
  41. /* #define SPECIALFACES 1 */
  42.  
  43. #define Version1        0
  44. #define Version2        1
  45. #define Version10        10
  46. #define    Version12        12
  47.  
  48. #define v1StateInit        0
  49. #define v1StateSeenAt    1
  50.  
  51. #define v2StateInit        0
  52. #define v2StateSeenAt    1
  53. #define v2StateSeenNL    2
  54. #define v2StateSeen2NL    3
  55.  
  56. #define v10StateInit        0
  57. #define    v10StateSeenBS        1   /* BackSlash */
  58. #define v10StateInKeyword    2
  59. #define v10StateNukeStyle    3
  60. #define v10StateBufferStyle    4
  61. #define v10StateDropChar    5
  62. #define    v10StateSeenNL        6   /* Newline */
  63. #define    v10StateSeen2NL        7   /* Second newline */
  64. #define    v10State2Deep        8   /* begindatacount >=2 */
  65. #define    v10State2DeepSeenBS    9
  66. #define    v10State2DeepInKeyword 10
  67.  
  68. #define LINEMAX  72
  69. #define LINEFUDGE  7
  70. #define MAXNETMAILLINE 255 /* Things longer than this will be crudely wrapped by unscribe */
  71. #define MAXLONGLINE 1000 /* ... unless in a literal style, which will wrap lines at this length */
  72. #define KEYWORDSIZE 200
  73. #define TABINTERVAL 8
  74.  
  75. #define INDENT_NOCHANGE (-LINEMAX)
  76. #define JUSTIFY_NOCHANGE -1
  77. #define JUSTIFY_LEFT 0
  78. #define JUSTIFY_CENTER 1
  79. #define JUSTIFY_RIGHT 2
  80.  
  81. /* This macro exists to avoid a function call on every character */
  82. #define ADDCHAR(State, fp, chr) \
  83.     if ((State)->lmargin + (State)->writecount \
  84.     + (State)->specials > (State)->rmargin) \
  85.         WriteCount += WriteFrag((State), (fp), (chr)); \
  86.     else { \
  87.     if ((State)->writecount == 0) StartFrag(State); \
  88.     (State)->linefrag[(State)->writecount++] = (chr); \
  89.     }
  90.  
  91. struct StateVector {
  92.     short lmargin;
  93.     short rmargin;
  94.     short indent;     /* how much to indent (or outdent if neg) the first line */
  95.     short face;       /* 0=plain, 1=italics, 2=bold */
  96.     short justify;    /*-1=don't change, 0=leftj, 1=center, 2=rightj */
  97.     short quotedepth;
  98.     short style;
  99.     short newpara;    /* flag: indicates that the first line of the paragraph is yet to be printed */
  100.     struct StateVector *next;
  101. };
  102.  
  103. struct styletable {
  104.     char *name;
  105.     long hash;
  106.     struct StateVector vec;
  107. } Styles[] =
  108. {
  109.     { "define",        0, { 0,    0, 0, 0, 0, 0,} },    /* first pseudo-style */
  110.     { "template",    0, { 0, 0, 0, 0, 0, 0,} },
  111.     { "textdsversion",    0, { 0, 0, 0, 0, 0, 0,} },
  112.     { "enddata",    0, { 0, 0, 0, 0, 0, 0,} },
  113.     { "dsversion",    0, { 0, 0, 0, 0, 0, 0,} },
  114.     { "begindata",    0, { 0, 0, 0, 0, 0, 0,} },
  115.     { "view",        0, { 0,    0, 0, 0, 0, 0,} },    /* last pseudo-style */
  116.     { "literal",        0, { 0, MAXLONGLINE, 0, 0, 0, 0,} },      /* no formatting style */
  117.     { "italic",        0, { 0, 0,INDENT_NOCHANGE, 1,JUSTIFY_NOCHANGE, 0,} },
  118.     { "bold",        0, { 0, 0,INDENT_NOCHANGE, 2,JUSTIFY_NOCHANGE, 0,} },
  119.     { "chapter",    0, { 0, 0, 0, 2,JUSTIFY_LEFT, 0,} },
  120.     { "section",    0, { 0, 0, 0, 2,JUSTIFY_LEFT, 0,} },
  121.     { "subsection",    0, { 0, 0, 0, 2,JUSTIFY_LEFT, 0,} },
  122.     { "paragraph",    0, { 0, 0, 0, 1,JUSTIFY_LEFT, 0,} },
  123.     { "bigger",        0, { 0, 0,INDENT_NOCHANGE, 0,JUSTIFY_NOCHANGE, 0,} },
  124.     { "indent",        0, { 4, 4, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  125.     { "typewriter",    0, { 0, 0,INDENT_NOCHANGE, 0,JUSTIFY_NOCHANGE, 0,} },
  126.     { "display",    0, { 4, 4, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  127.     { "example",    0, { 4, 0, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  128.     { "itemize",    0, { 4, 0, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  129.     { "description",    0, { 4, 0,-4, 0,JUSTIFY_NOCHANGE, 0,} },
  130.     { "enumerate",    0, { 4, 0, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  131.     { "programexample", 0, { 4, 0, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  132.     { "quotation",    0, { 0, 0,INDENT_NOCHANGE, 0,JUSTIFY_NOCHANGE, 1,} },
  133.     { "subscript",    0, { 0, 0,INDENT_NOCHANGE, 0,JUSTIFY_NOCHANGE, 0,} },
  134.     { "superscript",    0, { 0, 0,INDENT_NOCHANGE, 0,JUSTIFY_NOCHANGE, 0,} },
  135.     { "smaller",    0, { 0, 0,INDENT_NOCHANGE, 0,JUSTIFY_NOCHANGE, 0,} },
  136.     { "heading",    0, { 0, 0, 0, 1,JUSTIFY_LEFT, 0,} },
  137.     { "majorheading",    0, { 0, 0, 0, 0,JUSTIFY_CENTER, 0,} },
  138.     { "formatnote",    0, { 0, 0, 0, 0,JUSTIFY_LEFT, 0,} },
  139.     { "subheading",    0, { 0, 0, 0, 2,JUSTIFY_LEFT, 0,} },
  140.     { "center",        0, { 0, 0, 0, 0,JUSTIFY_CENTER, 0,} },
  141.     { "flushleft",    0, { 0, 0, 0, 0,JUSTIFY_LEFT, 0,} },
  142.     { "flushright",    0, { 0, 0, 0, 0,JUSTIFY_RIGHT, 0,} },
  143.     { "underline",    0, { 0, 0,INDENT_NOCHANGE, 1,JUSTIFY_NOCHANGE, 0,} },
  144.     { "leftindent",    0, { 4, 0, 0, 0,JUSTIFY_NOCHANGE, 0,} },
  145.     { 0,        0, { 0, 0, 0, 0, 0, 0,} }
  146. };
  147.  
  148. static int usVersion(val)
  149. char *val;
  150. { /* Return the VersionXXX value corresponding to the probe, or -1 if there's no match. */
  151.     int numVal;
  152.     char *Src;
  153.  
  154.     while (*val != '\0' && index(" \t\r\n", *val) != NULL) ++val;
  155.     if (ULstrcmp(val, "Yes") == 0) {
  156.     return Version1;
  157.     } else if (ULstrcmp(val, "2") == 0) {
  158.     return Version2;
  159.     } else if (ULstrcmp(val, "10") == 0) {
  160.     return Version10;
  161.     } else if (ULstrcmp(val, "12") == 0) {
  162.     return Version12;    /* ***Check next clause when adding a new type*** */
  163.     } else {
  164.     numVal = 0;    /* Interpret any type >= 12 as Version12 */
  165.     for (Src = val; *Src != '\0'; ++Src) if (! isdigit(*Src)) {numVal = 1; break;}
  166.     if (numVal == 0) {
  167.         numVal = atoi(val);
  168.         if (numVal >= 12) return Version12;
  169.     }
  170.     return -1;        /* don't understand header value */
  171.     }
  172. }
  173.  
  174. static long
  175. hash(str)
  176. char *str;
  177. {
  178.     unsigned long h = 0;
  179.     unsigned long g;
  180.  
  181.     for ( ; *str; str++) {
  182.     h = (h << 4) + *str;
  183.     if (g = h & 0xf0000000) {
  184.         h = h ^ (g >> 24);
  185.         h = h ^ g;
  186.     }
  187.     }
  188.     return h;
  189. }
  190.  
  191. static struct styletable *
  192. findstyle(str)
  193. char *str;
  194. {
  195.     struct styletable *stp;
  196.     long strhash;
  197.  
  198.     strhash = hash(str);
  199.     for (stp = Styles; stp->name; stp++) {
  200.     if (strhash == stp->hash && !strcmp(stp->name, str)) return stp;
  201.     }
  202.     return 0;
  203. }
  204.  
  205. int UnScribeInit(fieldvalue, refstate)
  206. char *fieldvalue;
  207. struct ScribeState **refstate;
  208. {
  209.     /* Pass it the value of the X-Andrew-ScribeFormat: header to see if the package can handle this format.  Returns a code value >= 0 for OK, < 0 for error.  Remember this code value to pass to UnScribe.  In addition, pass the address of an integer variable to hold the UnScribe state between calls.  This variable will be initialized with the UnScribeInit call.
  210.  */
  211.     int Vers;
  212.  
  213.     /* Initialize keyword table, but only once */
  214.     if (Styles->hash == 0) {
  215.     struct styletable *stp;
  216.  
  217.     for (stp = Styles; stp->name; stp++) stp->hash = hash(stp->name);
  218.     }
  219.  
  220.     *refstate = (struct ScribeState *) malloc(sizeof(struct ScribeState));
  221.     if (!*refstate) return -2;
  222.     (*refstate)->writecount = 0;
  223.     (*refstate)->begindatacount = 0;
  224.     (*refstate)->keywordpos = 0;
  225.     (*refstate)->linefrag = NULL;
  226.     (*refstate)->keyword = NULL;
  227.     (*refstate)->stylebuf = NULL;
  228.     (*refstate)->vector = NULL;
  229.     Vers = usVersion(fieldvalue);
  230.     switch (Vers) {
  231.     case Version1:
  232.         (*refstate)->statecode = v1StateInit;    /* initialize it */
  233.         break;
  234.     case Version2:
  235.         (*refstate)->statecode = v2StateInit;
  236.         break;
  237.     case Version10:
  238.     case Version12:
  239.         (*refstate)->statecode = v10StateInit;
  240.         (*refstate)->linefrag = (char *) malloc(MAXLONGLINE+2);
  241.         (*refstate)->keyword = (char *) malloc(KEYWORDSIZE);
  242.         (*refstate)->stylebuf = (char *) malloc(KEYWORDSIZE);
  243.         (*refstate)->lmargin = 0;
  244.         (*refstate)->rmargin = LINEMAX;
  245.         (*refstate)->indent = 0;
  246.         (*refstate)->justify = JUSTIFY_LEFT;
  247.         (*refstate)->specials = 0;
  248.         (*refstate)->face = 0;
  249.         (*refstate)->newpara = 1;
  250.         (*refstate)->vector = (struct StateVector *) malloc(sizeof(struct StateVector));
  251.         if ((*refstate)->linefrag == NULL
  252.           || (*refstate)->keyword == NULL
  253.           || (*refstate)->stylebuf == NULL
  254.           || (*refstate)->vector == NULL) {
  255.             if ((*refstate)->linefrag != NULL) free((*refstate)->linefrag);
  256.         if ((*refstate)->keyword != NULL) free((*refstate)->keyword);
  257.         if ((*refstate)->stylebuf != NULL) free((*refstate)->stylebuf);
  258.         if ((*refstate)->vector != NULL) free((*refstate)->vector);
  259.         free(*refstate);
  260.         return -2;
  261.         }
  262.         (*refstate)->vector->next = NULL;
  263.         (*refstate)->vector->lmargin = 0;
  264.         (*refstate)->vector->rmargin = LINEMAX;
  265.         (*refstate)->vector->indent = 0;
  266.         (*refstate)->vector->justify = JUSTIFY_LEFT;
  267.         (*refstate)->vector->face = 0;
  268.         (*refstate)->vector->newpara = 1;
  269.         (*refstate)->vector->quotedepth = 0;
  270.         break;
  271.     default:
  272.         free(*refstate);
  273.         *refstate = NULL;
  274.         return -1;        /* don't understand header value */
  275.     }
  276.     return Vers;
  277. }
  278.  
  279. static void
  280. StartFrag(State)
  281. struct ScribeState *State;
  282. {
  283. #ifdef SPECIALFACES
  284.     int oldfaces;
  285.     int newfaces;
  286. #endif /* SPECIALFACES */
  287.  
  288.     /* set current state from stack */
  289.     State->lmargin = State->vector->lmargin;
  290.     State->rmargin = State->vector->rmargin;
  291.     State->justify = State->vector->justify;
  292.     State->indent = State->vector->indent;
  293.  
  294. #ifdef SPECIALFACES
  295.     oldfaces = State->face & State->vector->face;
  296.     newfaces = State->vector->face ^ oldfaces;
  297.  
  298.     /* incorporate specials into start of string */
  299.     if (oldfaces & 1) State->linefrag[State->writecount++] = '_';
  300.     if (oldfaces & 2) State->linefrag[State->writecount++] = '*';
  301. /*
  302.     if (oldfaces & 4) State->linefrag[State->writecount++] = '-';
  303.     if (oldfaces & 8) State->linefrag[State->writecount++] = '+';
  304. */
  305.  
  306.     if (newfaces & 1) State->linefrag[State->writecount++] = '_';
  307.     if (newfaces & 2) State->linefrag[State->writecount++] = '*';
  308. /*
  309.     if (newfaces & 4) State->linefrag[State->writecount++] = '-';
  310.     if (newfaces & 8) State->linefrag[State->writecount++] = '+';
  311. */
  312. #endif /* SPECIALFACES */
  313. }
  314.  
  315. static int
  316. WriteFrag(State, fPtr, Chr)
  317. struct ScribeState *State;
  318. FILE *fPtr;
  319. char Chr;
  320. {
  321.     int    NumWritten = 1;    /* at least a NewLine */
  322.     char *tail;
  323. #ifdef TRIMTRAILINGWHITESPACE
  324.     char *c;
  325. #endif /* TRIMTRAILINGWHITESPACE */
  326.     int i;
  327.     int len;
  328.     int width;
  329.     int quotedepth;
  330.  
  331.     if (Chr == '\n' && State->writecount == 0) {
  332.     putc('\n', fPtr);
  333.     State->newpara = 1;  /* Yes, this is a new paragraph */
  334.     return 1;
  335.     } 
  336.  
  337.     /* do we really _have_ to wrap it? */
  338.     if (Chr != '\n' && State->newpara && ((State)->lmargin + (State)->writecount + (State)->specials < ((State)->rmargin + LINEFUDGE)) ) {
  339.     /* if we're outputting the one and only line of a paragraph,
  340.        we can let the line grow a little longer. */
  341.     (State)->linefrag[(State)->writecount++] = (Chr);
  342.     return 0;
  343.     } /* if (State->newpara ... ) */
  344.  
  345.     /* find a break point in the string */
  346.     if (Chr != '\n') {
  347.         State->linefrag[State->writecount] = '\0';
  348.         tail = State->linefrag + MIN((State)->rmargin - (State)->lmargin - (State)->specials + 1, (State)->writecount);
  349.  
  350.     while (tail-- > State->linefrag && *tail != ' ') ;
  351.     if (*tail != ' ') {
  352.                 /* couldn't find a break point */
  353.         tail = State->linefrag + State->writecount;
  354.         len = State->writecount;
  355.     } else {
  356.                 /* found a break point */
  357.         *tail++ = '\0';
  358.         len = tail - State->linefrag - 1;
  359.     }
  360.     } else {
  361.         tail = State->linefrag + State->writecount;
  362.     *tail = '\0';
  363.     len = State->writecount;
  364.     }
  365.  
  366. #ifdef TRIMTRAILINGWHITESPACE
  367.     /* remove trailing white space */
  368.     for (c = State->linefrag + len; *--c == ' '; len--) ;
  369.     *++c = '\0';
  370. #endif /* TRIMTRAILINGWHITESPACE */
  371.  
  372.     /* quote marks :-) */
  373.     /* putting quote mark handling here means that quotes appear at the left margin only--no more in-line quotes */
  374.     for (quotedepth = State->vector->quotedepth; quotedepth; quotedepth--, NumWritten++) putc('>', fPtr);
  375.     if (State->vector->quotedepth) {
  376.       putc(' ', fPtr);
  377.       NumWritten++;
  378.     }
  379.  
  380.     /* left margin */
  381.     if (State->newpara) {
  382.       for (i = State->lmargin + State->indent; i; i--, NumWritten++) putc(' ', fPtr);
  383.     } else {
  384.       for (i = State->lmargin; i; i--, NumWritten++) putc(' ', fPtr);
  385.     }
  386.  
  387.     /* justification */
  388.     width = State->rmargin - State->lmargin + 1;
  389.     len += State->specials;
  390.     if (State->justify == JUSTIFY_CENTER) i = (width - len) / 2;
  391.     else if (State->justify == JUSTIFY_RIGHT) i = width - len;
  392.     else i = 0;        /* default to JUSTIFY_LEFT */
  393.     while ((i--) > 0) { putc(' ', fPtr); NumWritten++; }
  394.  
  395.     /* shove out the actual text */
  396.     if (len) {
  397.     fputs(State->linefrag, fPtr);
  398.  
  399. #ifdef SPECIALFACES
  400. /*
  401.     if (State->vector->face & 8) putc('+', fPtr);
  402.     if (State->vector->face & 4) putc('-', fPtr);
  403. */
  404.     if (State->vector->face & 2) putc('*', fPtr);
  405.     if (State->vector->face & 1) putc('_', fPtr);
  406. #endif /* SPECIALFACES */
  407.     NumWritten += len;
  408.     }
  409.     putc('\n', fPtr);
  410.     State->writecount = 0;
  411.     State->face = State->vector->face;
  412.     if (Chr != '\n') {
  413.         State->newpara = 0;        /* no longer */
  414.     StartFrag(State);
  415.     len = strlen(tail);
  416.     strcpy(State->linefrag + State->writecount, tail);
  417.     State->writecount += len;
  418.     State->linefrag[State->writecount++] = Chr;
  419.     } else {
  420.       State->newpara = 1;  /* Yes, this is a new paragraph */
  421.     }
  422.  
  423.     return NumWritten;
  424. }
  425.  
  426. static int
  427. HandleKeyword(State, fPtr)
  428. struct ScribeState *State;
  429. FILE *fPtr;
  430. {
  431.     struct styletable *style;
  432.     short idx = -1;
  433.     struct StateVector *vec;
  434.     int WriteCount = 0;
  435. #ifdef SPECIALFACES
  436.     unsigned int bitvec;
  437. #endif /* SPECIALFACES */
  438.  
  439.     style = findstyle(State->keyword);
  440.     if (style) idx = style - Styles;
  441.  
  442.     switch (idx) {
  443.     case -1:    /* unknown keyword, ignore */
  444.         State->statecode = State->previous_state;
  445.         vec = (struct StateVector *) malloc(sizeof(struct StateVector));
  446.         *vec = *(State->vector);
  447.         vec->next = State->vector;
  448.         State->vector = vec;
  449.         vec->style = idx;
  450.         break;
  451.     case 0:    case 1:    case 2:    case 3:    case 4:    /* garbage keywords */
  452.         State->statecode = v10StateNukeStyle;
  453.         break;
  454.     case 5:        /* begindata */
  455.         State->statecode = v10StateNukeStyle;
  456.         State->begindatacount++;
  457.         break;
  458.     case 6:        /* view */
  459.         State->statecode = v10StateBufferStyle;
  460.         State->keywordpos = 0;
  461.         break;
  462.     default:    /* a normal style */
  463.         State->statecode = State->previous_state;
  464.  
  465.         /* do silly things to mark beginning of environment */
  466.         if (State->writecount) {
  467. #ifdef INLINEQUOTING
  468.         if (style->vec.quotedepth) {
  469.             ADDCHAR(State, fPtr, '>');
  470.             ADDCHAR(State, fPtr, ' ');
  471.         }
  472. #endif /* INLINEQUOTING */
  473. #ifdef SPECIALFACES
  474.         if (style->vec.face & 1) ADDCHAR(State, fPtr, '_');
  475.         if (style->vec.face & 2) ADDCHAR(State, fPtr, '*');
  476. /*
  477.         if (style->vec.face & 4) ADDCHAR(State, fPtr, '-');
  478.         if (style->vec.face & 8) ADDCHAR(State, fPtr, '+');
  479. */
  480. #endif /* SPECIALFACES */
  481.         }
  482.  
  483.         /* push to top of stack */
  484.         vec = (struct StateVector *) malloc(sizeof(struct StateVector));
  485.         *vec = *(State->vector);
  486.         vec->next = State->vector;
  487.         State->vector = vec;
  488.  
  489.         /* play style over vector */
  490.         vec->style = idx;
  491.         vec->lmargin += style->vec.lmargin;
  492.         if (style->vec.rmargin != MAXLONGLINE) {
  493.           vec->rmargin -= style->vec.rmargin;
  494.         } else {
  495.           vec->rmargin = MAXLONGLINE;
  496.         }
  497.         vec->face |= style->vec.face;
  498.         if ((style->vec.indent) != INDENT_NOCHANGE) vec->indent = style->vec.indent;
  499.         if ((style->vec.justify) != JUSTIFY_NOCHANGE) vec->justify = style->vec.justify;
  500.         vec->quotedepth += style->vec.quotedepth;
  501.  
  502.         /* play style over current state */
  503.         State->specials = 0;
  504. #ifdef SPECIALFACES
  505.         bitvec = vec->face;
  506.         while (bitvec) {
  507.         if (bitvec & 1) State->specials++;
  508.         bitvec >>= 1;
  509.         }
  510. #endif /* SPECIALFACES */
  511.     }
  512.     return WriteCount;
  513. }
  514.  
  515. static int
  516. HandleClose(State, fPtr)
  517. struct ScribeState *State;
  518. FILE *fPtr;
  519. {
  520.     struct StateVector *temp;
  521.     int WriteCount = 0;
  522. #ifdef SPECIALFACES
  523.     struct styletable *style;
  524.     int bitvec;
  525. #endif /* SPECIALFACES */
  526.  
  527.     if (State->vector->next) {
  528.     /* pop top of stack */
  529.     temp = State->vector;
  530.     State->vector = State->vector->next;
  531.  
  532.     /* play new top over current state */
  533.     State->specials = 0;
  534. #ifdef SPECIALFACES
  535.     bitvec = State->vector->face;
  536.     while (bitvec) {
  537.         if (bitvec & 1) State->specials++;
  538.         bitvec >>= 1;
  539.     }
  540. #endif /* SPECIALFACES */
  541.  
  542.     /* close the old environment and nuke it */
  543. #ifdef SPECIALFACES
  544.     if (temp->style >= 0 && State->writecount) {
  545.         style = Styles + temp->style;
  546. /*
  547.         if (style->vec.face & 8) ADDCHAR(State, fPtr, '+');
  548.         if (style->vec.face & 4) ADDCHAR(State, fPtr, '-');
  549. */
  550.         if (style->vec.face & 2) ADDCHAR(State, fPtr, '*');
  551.         if (style->vec.face & 1) ADDCHAR(State, fPtr, '_');
  552.     }
  553. #endif /* SPECIALFACES */
  554.     free(temp);
  555.     } else {
  556.     fprintf(stderr, "Popped too far!\n");
  557.     ADDCHAR(State, fPtr, '}');
  558.     }
  559.     return WriteCount;
  560. }
  561.  
  562. int UnScribe(code, refstate, text, textlen, fileptr)
  563. int code, textlen;
  564. struct ScribeState **refstate;
  565. char *text;
  566. FILE *fileptr;
  567. {
  568.     /* Pass it the initial UnScribeInit return value and the address of the integer state variable.  Also pass the address of the text to be unscribed, and the number of bytes at that address.  The unscribed text will be written onto stdio file *fileptr.  Return values are something like fwrite: >= 0 for OK, < 0 for errors, with a code either in the return value or in errno.  Basically, if -1 is returned, errno is valid; otherwise, the code is specific to this procedure.
  569.     */
  570.     register int Char;
  571.     struct ScribeState *MyState;
  572.     int    ReadCount;
  573.     int WriteCount;
  574.     char *Src;
  575.  
  576.     MyState = *refstate;
  577.     ReadCount = textlen;
  578.     Src = text;
  579.     WriteCount = 0;
  580.     if (ReadCount < 0) return -2;
  581.     switch (code) {
  582.     case Version1:
  583.         if (MyState->statecode != v1StateInit && MyState->statecode != v1StateSeenAt) return -2;
  584.         while (--ReadCount >= 0) {
  585.         Char = *Src++;
  586.         switch (MyState->statecode) {
  587.             case v1StateInit:
  588.             if (Char == '@') MyState->statecode = v1StateSeenAt;
  589.             else {
  590.                 WriteCount++;
  591.                 if (putc(Char, fileptr) == EOF) return -1;
  592.                 if (Char == '\n') MyState->writecount = -1;
  593.                 if (++MyState->writecount > MAXNETMAILLINE) {
  594.                 MyState->writecount = 0; WriteCount++;
  595.                 if(putc('\n', fileptr) == EOF) return -1;
  596.                 }
  597.             }
  598.             break;
  599.             case v1StateSeenAt:
  600.             if (Char == '@') {        /* write a single at-sign for ``@@'' */
  601.                 WriteCount++;
  602.                 if (putc(Char, fileptr) == EOF) return -1;
  603.                 if (++MyState->writecount > MAXNETMAILLINE) {
  604.                 MyState->writecount = 0; WriteCount++;
  605.                 if(putc('\n', fileptr) == EOF) return -1;
  606.                 }
  607.                 /* write nothing for ``@*'' */
  608.             } else if (Char != '*') {    /* write the at-sign and the character */
  609.                 WriteCount += 2;
  610.                 if (putc('@', fileptr) == EOF) return -1;
  611.                 if (putc(Char, fileptr) == EOF) return -1;
  612.                 if (Char == '\n') MyState->writecount = -1;
  613.                 if (++MyState->writecount > MAXNETMAILLINE) {
  614.                 MyState->writecount = 0; WriteCount++;
  615.                 if(putc('\n', fileptr) == EOF) return -1;
  616.                 }
  617.             }
  618.             MyState->statecode = v1StateInit;
  619.             break;
  620.             default:
  621.             return -3;
  622.         }
  623.         }
  624.         break;
  625.  
  626.     case Version2:
  627.         if (MyState->statecode != v2StateInit &&
  628.         MyState->statecode != v2StateSeenAt &&
  629.         MyState->statecode != v2StateSeenNL &&
  630.         MyState->statecode != v2StateSeen2NL) return -2;
  631.         while (--ReadCount >= 0) {
  632.         Char = *Src++;
  633.         switch (MyState->statecode) {
  634.             case v2StateInit:
  635.             WriteCount++;
  636.             if (putc(Char, fileptr) == EOF) return -1;
  637.             if (Char == '\n') MyState->writecount = -1;
  638.             if (++MyState->writecount > MAXNETMAILLINE) {
  639.                 MyState->writecount = 0; WriteCount++;
  640.                 if(putc('\n', fileptr) == EOF) return -1;
  641.             }
  642.             if (Char == '@') MyState->statecode = v2StateSeenAt;
  643.             else if (Char == '\n') MyState->statecode = v2StateSeenNL;
  644.             break;
  645.             case v2StateSeenAt:
  646.             if (Char == '@') {        /* write a single at-sign for ``@@'' */
  647.                 MyState->statecode = v2StateInit;
  648.             } else {    /* write the at-sign and the character */
  649.                 WriteCount++;
  650.                 if (putc(Char, fileptr) == EOF) return -1;
  651.                 if (Char == '\n') MyState->writecount = -1;
  652.                 if (++MyState->writecount > MAXNETMAILLINE) {
  653.                 MyState->writecount = 0; WriteCount++;
  654.                 if(putc('\n', fileptr) == EOF) return -1;
  655.                 }
  656.                 if (Char == '\n') MyState->statecode = v2StateSeenNL;
  657.                 else MyState->statecode = v2StateInit;
  658.             }
  659.             break;
  660.             case v2StateSeenNL:
  661.             if (Char == '\n') MyState->statecode = v2StateSeen2NL;    /* consume NL */
  662.             else {
  663.                 WriteCount++;
  664.                 if (putc(Char, fileptr) == EOF) return -1;
  665.                 if (Char == '\n') MyState->writecount = -1;
  666.                 if (++MyState->writecount > MAXNETMAILLINE) {
  667.                 MyState->writecount = 0; WriteCount++;
  668.                 if(putc('\n', fileptr) == EOF) return -1;
  669.                 }
  670.                 MyState->statecode = (Char == '@' ? v2StateSeenAt : v2StateInit);
  671.             }
  672.             break;
  673.             case v2StateSeen2NL:
  674.             WriteCount++;
  675.             if (putc(Char, fileptr) == EOF) return -1;
  676.             if (Char == '\n') MyState->writecount = -1;
  677.             if (++MyState->writecount > MAXNETMAILLINE) {
  678.                 MyState->writecount = 0; WriteCount++;
  679.                 if(putc('\n', fileptr) == EOF) return -1;
  680.             }
  681.             if (Char != '\n') MyState->statecode = (Char == '@' ? v2StateSeenAt : v2StateInit);
  682.             break;
  683.             default:
  684.             return -3;
  685.         }
  686.         }
  687.         break;
  688.  
  689.     case Version12:    /* Slight modification to V10 */
  690.     case Version10:
  691.         if (MyState->linefrag == NULL ||
  692.         MyState->keyword == NULL) return -5;
  693.         while (--ReadCount >= 0) {
  694.         Char = *Src++;
  695.         switch (MyState->statecode) {
  696.         case v10StateSeen2NL:
  697.         case v10StateSeenNL:
  698.         case v10StateInit:
  699.           switch (Char) {
  700.           case '\n':
  701.             switch(MyState->statecode) {
  702.             case v10StateSeen2NL:
  703.               WriteCount += WriteFrag(MyState, fileptr, Char);
  704.               break;
  705.             case v10StateSeenNL:
  706.               WriteCount += WriteFrag(MyState, fileptr, Char);
  707.               MyState->statecode = v10StateSeen2NL;
  708.               break;
  709.             case v10StateInit:
  710.               if (code == Version12) {
  711.             MyState->statecode = v10StateSeenNL;
  712.               } else {
  713.             WriteCount += WriteFrag(MyState, fileptr, Char);
  714.               }
  715.             }
  716.             break;
  717.           case '\\':
  718.             MyState->previous_state = MyState->statecode;
  719.             MyState->statecode = v10StateSeenBS;
  720.             break;
  721.           case '}':
  722.             WriteCount += HandleClose(MyState, fileptr);
  723.             break;
  724.           case '\t':
  725.             if (MyState->lmargin > 0) {
  726.               do {
  727.             ADDCHAR(MyState, fileptr, ' ');
  728.               } while ((MyState->lmargin + MyState->writecount) % TABINTERVAL);
  729.               break;
  730.             }                  
  731.           default:
  732.             ADDCHAR(MyState, fileptr, Char);
  733.           }
  734.                 /* turn off NL flags */
  735.           if (((Char != '\n') && (Char != '}')) &&
  736.               ((MyState->statecode == v10StateSeenNL) ||
  737.               (MyState->statecode == v10StateSeen2NL))) {
  738.             MyState->statecode = v10StateInit;
  739.           }
  740.           break;
  741.         case v10StateSeenBS:
  742.           switch (Char) {
  743.           case '\\': case '{': case '}':
  744.             MyState->statecode = v10StateInit;
  745.             ADDCHAR(MyState, fileptr, Char);
  746.             break;
  747.           case '\n':    /* only present in version12 */
  748.             MyState->statecode = v10StateInit;
  749. #ifdef BOGUS
  750.                 /* I don't know why this is here.
  751.                    As far as I can tell, it shouldn't be. */
  752.             WriteCount += WriteFrag(MyState, fileptr, Char);
  753. #endif /* BOGUS */
  754.             break;
  755.           default:
  756.             MyState->statecode = v10StateInKeyword;
  757.             MyState->keywordpos = 0;
  758.             MyState->keyword[MyState->keywordpos++] = Char;
  759.           }
  760.           break;
  761.         case v10StateInKeyword:
  762.           if (Char != '{')
  763.             MyState->keyword[MyState->keywordpos++] = Char;
  764.           else {
  765.             MyState->keyword[MyState->keywordpos] = 0;
  766.             WriteCount += HandleKeyword(MyState, fileptr);
  767.           }
  768.           break;
  769.         case v10StateNukeStyle:
  770.           if (Char == '}') {
  771.             MyState->statecode = v10StateDropChar;
  772.             if (!strcmp(MyState->keyword, "enddata")) MyState->begindatacount--;
  773.           }
  774.           break;
  775.         case v10StateBufferStyle:
  776.           if (Char != '}') MyState->stylebuf[MyState->keywordpos++] = Char;
  777.           else {
  778.             char *c, *d;
  779.             
  780.             for (c = MyState->stylebuf; *c && *c != ','; c++) ;
  781.             *c = '\0';
  782.             c = MyState->stylebuf;
  783.             MyState->statecode = v10StateInit;
  784.             MyState->stylebuf[MyState->keywordpos] = 0;
  785.             if (!strcmp(c, "bpv")) {
  786.               ADDCHAR(MyState, fileptr, '\f');
  787.             } else {
  788.               d = "[An Andrew ToolKit view (";
  789.               while (*d) { ADDCHAR(MyState, fileptr, *d); d++; }
  790.               if (!strcmp(c, "fadview")) c = "an animated drawing";
  791.               else if (!strcmp(c, "rasterview")) c = "a raster image";
  792.               else if (!strcmp(c, "zipview")) c = "a drawing";
  793.               else if (!strcmp(c, "textview")) c = "embedded text";
  794.               else if (!strcmp(c, "spread")) c = "a spreadsheet";
  795.               else if (!strcmp(c, "fnotev")) c = "a footnote";
  796.               else if (!strcmp(c, "linkview")) c = "a hyperlink";
  797.               while (*c) { ADDCHAR(MyState, fileptr, *c); c++; }
  798.               c = ") was included here, but could not be displayed.]";
  799.               while (*c) { ADDCHAR(MyState, fileptr, *c); c++; }
  800.             }
  801.           }
  802.           break;
  803.         case v10StateDropChar:
  804.           if (MyState->begindatacount < 2) MyState->statecode = v10StateInit;
  805.           else MyState->statecode = v10State2Deep;
  806.           break;
  807.         case v10State2Deep:
  808.           if (Char == '\\') MyState->statecode = v10State2DeepSeenBS;
  809.           break;
  810.         case v10State2DeepSeenBS:
  811.           switch (Char) {
  812.           case 'b': case 'e':        /* begindata/enddata */
  813.             MyState->statecode = v10State2DeepInKeyword;
  814.             MyState->keywordpos = 0;
  815.             MyState->keyword[MyState->keywordpos++] = Char;
  816.             break;
  817.           default:
  818.             MyState->statecode = v10State2Deep;
  819.           }
  820.           break;
  821.         case v10State2DeepInKeyword:
  822.           if (Char != '{') {
  823.             MyState->keyword[MyState->keywordpos++] = Char;
  824.             if (MyState->keywordpos > sizeof("begindata") + 2)
  825.               MyState->statecode = v10State2Deep;
  826.           } else {
  827.             MyState->keyword[MyState->keywordpos] = 0;
  828.             if (!strcmp(MyState->keyword, "enddata")) {
  829.               MyState->statecode = v10StateNukeStyle;
  830.             } else if (!strcmp(MyState->keyword, "begindata")) {
  831.               MyState->statecode = v10StateNukeStyle;
  832.               MyState->begindatacount++;
  833.             } else {
  834.               MyState->statecode = v10State2Deep;
  835.             }
  836.           }
  837.           break;
  838.         default:
  839.           return -3;
  840.         }
  841.         if (MyState->keywordpos >= (KEYWORDSIZE - 1)) return -6;
  842.         }
  843.         MyState->linefrag[MyState->writecount] = '\0';
  844.         break;
  845.  
  846.     default:
  847.         return -4;
  848.     }
  849.     return WriteCount;
  850. }
  851.  
  852. int UnScribeFlush(code, refstate, fileptr)
  853. int code;
  854. struct ScribeState **refstate;
  855. FILE *fileptr;
  856. {/* Unbuffer data from an UnScribe sequence.  Return just like fflush(). */
  857.     int Res = 0;
  858.  
  859.     if (refstate != NULL) {
  860.     if (*refstate != NULL) {
  861.         if ((*refstate)->linefrag != NULL) {
  862.         if ((*refstate)->writecount) WriteFrag((*refstate), fileptr, '\n');
  863.         Res = fflush(fileptr);
  864.         free((*refstate)->linefrag);
  865.         }
  866.         if ((*refstate)->keyword != NULL) free((*refstate)->keyword);
  867.         if ((*refstate)->stylebuf != NULL) free((*refstate)->stylebuf);
  868.         while ((*refstate)->vector != NULL) {
  869.         struct StateVector *vp;
  870.  
  871.         vp = (*refstate)->vector;
  872.         (*refstate)->vector = (*refstate)->vector->next;
  873.         free(vp);
  874.         }
  875.         free(*refstate);
  876.         *refstate = NULL;
  877.     }
  878.     }
  879.     return Res;
  880. }
  881.  
  882. int UnScribeAbort(code, refstate)
  883. int code;
  884. struct ScribeState **refstate;
  885. {/* Close off the UnScribeInit state without needing a valid file to send to. */
  886.  
  887.     if (refstate != NULL) {
  888.     if (*refstate != NULL) {
  889.         if ((*refstate)->linefrag != NULL) free((*refstate)->linefrag);
  890.         if ((*refstate)->keyword != NULL) free((*refstate)->keyword);
  891.         if ((*refstate)->stylebuf != NULL) free((*refstate)->stylebuf);
  892.         while ((*refstate)->vector != NULL) {
  893.         struct StateVector *vp;
  894.  
  895.         vp = (*refstate)->vector;
  896.         (*refstate)->vector = (*refstate)->vector->next;
  897.         free(vp);
  898.         }
  899.         free(*refstate);
  900.         *refstate = NULL;
  901.     }
  902.     }
  903.     return 0;
  904. }
  905.  
  906. int PrintMaybeFoldQuotingFormatting(fp, text, format, len, DoFold)
  907. FILE *fp;
  908. char *text, *format;
  909. int len, DoFold;
  910. { /* return the number of characters written, or negative error value */
  911.     int whichformat;
  912.     register char *s;
  913.     int numWritten, WasNL, Buffered, Column, CurrentLineMax;
  914.     char *newNL, oldC, BreakCode;
  915.     char LineBuf[2*MAXLONGLINE + 10], *LBP;
  916.  
  917.     if (!format || !*format) {
  918.     whichformat = -1;
  919.     } else {
  920.     whichformat = usVersion(format);
  921.     }
  922.     CurrentLineMax = (DoFold ? LINEMAX : MAXLONGLINE);
  923.     numWritten = 0; LBP = LineBuf;
  924.     WasNL = 1; Buffered = Column = 0;
  925.     for (s = text; *s && len--; ++s) {
  926.     switch(*s) {
  927.         case '\\': case '{': case '}':    /* sometimes need quoting */
  928.         if (whichformat == Version10 || whichformat == Version12)
  929.             {*LBP++ = '\\'; ++Buffered;}
  930.         goto NormalChar;
  931.         case '@':            /* sometimes needs quoting */
  932.         if (whichformat == Version1 || whichformat == Version2)
  933.             {*LBP++ = '@'; ++Buffered;}
  934.         goto NormalChar;
  935.         case '\n':
  936.         if (WasNL == 0) {        /* First newline in a sequence */
  937.             *LBP++ = '\0';    /* Unbuffer what's been buffered. */
  938.             if (fputs(LineBuf, fp) == EOF) return -1;
  939.             numWritten += Buffered;
  940.             LBP = LineBuf; Buffered = Column = 0;
  941.             switch (whichformat) {    /* Handle the end-of-line as format dictates. */
  942.         case Version1:    /* Scribe newline convention */
  943.             if (putc('@', fp) == EOF) return -1;
  944.             if (putc('*', fp) == EOF) return -1;
  945.             numWritten += 2;
  946.             break;
  947.         case Version2:
  948.         case Version12:    /* add one NL to a sequence of newlines */
  949.             if (putc('\n', fp) == EOF) return -1;
  950.             ++numWritten;
  951.             break;
  952.         case Version10:    /* no special treatment (BOGUS!) */
  953.         default:        /* if not formatted, do nothing. */
  954.             break;
  955.             }
  956.         }
  957.         if (putc('\n', fp) == EOF) return -1;    /* write the newline itself. */
  958.         ++numWritten;
  959.         WasNL = 1;
  960.         break;
  961.         default:  NormalChar:
  962.         *LBP++ = *s;
  963.         ++Buffered;
  964.         if (*s == '\t') Column |= 07;    /* Hack for 8-character tab stops */
  965.         ++Column;
  966.         if (Buffered >= CurrentLineMax || Column >= CurrentLineMax) {
  967.             *LBP = '\0';    /* Do newline processing in LineBuf. */
  968.             newNL = LBP - 1;
  969.             while (newNL > LineBuf && *newNL != ' ')
  970.                 --newNL;
  971.             if (*newNL == ' ') { /* have a space to use */
  972.                 while (*newNL == ' ') ++newNL;
  973.                 if (whichformat < 0) --newNL;
  974.                 BreakCode = 1;
  975.             } else {        /* no space at which to break it */
  976.                 newNL = LBP;
  977.                 BreakCode = 0;
  978.             }
  979.             oldC = *newNL;
  980.             *newNL = '\0';
  981.             if (fputs(LineBuf, fp) == EOF) return -1;
  982.             *newNL = oldC;
  983.             numWritten += (newNL - LineBuf);
  984.             if (BreakCode == 0) {    /* Trying to break a word in the middle */
  985.                 if (whichformat == Version12) {
  986.                 if (fputs("\\\n", fp) == EOF) return -1;
  987.                 numWritten += 2;
  988.                 }
  989.             } else {
  990.                 while (*newNL == ' ') ++newNL;
  991.                 if (putc('\n', fp) == EOF) return -1;
  992.                 ++numWritten;
  993.             }
  994.             strcpy(LineBuf, newNL);
  995.             Buffered = LBP - newNL;
  996.             LBP = &LineBuf[Buffered];
  997.             Column = 0;
  998.             for (newNL = LineBuf; newNL < LBP; ++newNL) {
  999.                 if (*newNL == '\t') Column |= 07;
  1000.                 ++Column;    /* Get column count straight */
  1001.             }
  1002.         }
  1003.         WasNL = 0;
  1004.         break;
  1005.     }
  1006.     }
  1007.     if (Buffered > 0) {
  1008.     *LBP++ = '\0';    /* Unbuffer what's been buffered. */
  1009.     if (fputs(LineBuf, fp) == EOF) return -1;
  1010.     numWritten += Buffered;
  1011.     }
  1012.     return numWritten;
  1013. }
  1014.  
  1015. int PrintQuotingFormatting(fp, text, format, len)
  1016. FILE *fp;
  1017. char *text, *format;
  1018. int len;
  1019. { /* return the number of characters written, or negative error value */
  1020.     return PrintMaybeFoldQuotingFormatting(fp, text, format, len, 0);
  1021. }
  1022.  
  1023. #ifdef TESTINGONLYTESTING
  1024. main(argc, argv)
  1025. int argc;
  1026. char *argv[];
  1027. {
  1028.   int version, err;
  1029.   char buf[500];
  1030.   struct ScribeState *ussp;
  1031.   FILE *fptr;
  1032.  
  1033.   if (argc == 1) {
  1034.     fptr = stdin;
  1035.   } else {
  1036.     if ((fptr = fopen(argv[1], "r")) == NULL) {
  1037.       fprintf(stderr, "Could not open file '%s'.\n", argv[1]);
  1038.       exit(1);
  1039.     }
  1040.   }
  1041.   version = UnScribeInit(" 12", &ussp);
  1042.   while (fgets(buf,sizeof(buf)-1,fptr)) {
  1043.     err = UnScribe(version, &ussp, buf, strlen(buf), stdout);
  1044.     if (err < 0) fprintf(stderr, "Error: UnScribe == %d.\n", err);
  1045.   }
  1046.  
  1047. }
  1048. #endif /* TESTINGONLYTESTING */
  1049.