home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / s / stex2-18.zip / SeeTeX / libtex / sdecode.c < prev    next >
C/C++ Source or Header  |  1990-07-10  |  14KB  |  643 lines

  1. /*
  2.  * Copyright (c) 1989 University of Maryland
  3.  * Department of Computer Science.  All rights reserved.
  4.  * Permission to copy for any purpose is hereby granted
  5.  * so long as this copyright notice remains intact.
  6.  */
  7.  
  8. #ifndef lint
  9. static char rcsid[] = "$Header: /usr/src/local/tex/local/mctex/lib/RCS/sdecode.c,v 3.4 89/11/06 15:01:07 chris Exp $";
  10. #endif
  11.  
  12. #include <stdio.h>
  13. #include <varargs.h>
  14. #include "types.h"
  15. #include "sdecode.h"
  16.  
  17. static char cclass[256];    /* XXX assumes 8-bit char */
  18. #define    CCL_SPACE    0x01    /* white space */
  19. #define    CCL_SEMI    0x02    /* `semicolon' (statement separator) */
  20. #define    isstopc(c) (cclass[c] != 0)    /* both space and semi char */
  21. static int sdset;        /* flag says whether cclass[] set up */
  22.  
  23. static void args(), badarg();
  24. static int scan(), scan_i(), scan_d();
  25.  
  26. extern char *strsave();
  27.  
  28. extern char *DVIFileName;
  29.  
  30. #ifndef BLOCK_COPY
  31. /*
  32.  * Copy the text at `src' `downward' in memory for `len' bytes.
  33.  * This is like strcpy or memcpy, but handles one direction of
  34.  * overlap (when dst < src).
  35.  */
  36. static void
  37. movedown(src, dst, len)
  38.     register char *src, *dst;
  39.     register int len;
  40. {
  41.  
  42.     while (--len >= 0)
  43.         *dst++ = *src++;
  44. }
  45. #endif
  46.  
  47. /*
  48.  * We have a number of states to do word interpretation.  Outside words,
  49.  * we skip blanks and `semicolons'.  The first non-blank, non-`semi' enters
  50.  * WORD state; in WORD state, double quote characters switch to
  51.  * QUOTE state, after which another double quote switches back to
  52.  * WORD state, and blanks and `semi' end WORD state, switching to DONE.
  53.  * While in WORD or QUOTE states, `c produces c for all characters c;
  54.  * to do this, we use two more states.
  55.  */
  56. #define    S_DULL    0        /* not in a word */
  57. #define    S_WORD    1        /* in a word */
  58. #define    S_QUOTE    2        /* inside "" in a word */
  59. #define    S_BKQT1    3        /* not in "", but just read one ` */
  60. #define    S_BKQT2    4        /* in "", but just read one ` */
  61. #define    S_DONE    5        /* found end of word */
  62.  
  63. /*
  64.  * Structure used to communicate between the various functions.
  65.  */
  66. struct decode_info {
  67.     /* data used to read from input file */
  68.     int    di_nch;        /* number of characters remaining in buffer */
  69.     char    *di_cp;        /* next character to be read from buffer */
  70.     long    di_nfilech;    /* number of characters remaining in file */
  71.     FILE    *di_file;    /* the file itself */
  72.  
  73.     /* information used and/or returned by the canon routine */
  74.     int    di_lexstate;    /* lexical state */
  75.     int    di_call;    /* set when we reach terminating `semicolon' */
  76.  
  77.     /* information used more globally */
  78.     char    *di_word;    /* last word from word() */
  79.     char    *di_kw;        /* the keyword */
  80.     int    di_bkw;        /* true iff keyword is in static buffer */
  81. };
  82.  
  83. /*
  84.  * `Edit' the text in the buffer in-place.
  85.  * Return the count of characters resulting from the edit (e.g.,
  86.  * from `foo`"bar' the count would be 7).
  87.  * Set di_call if we reach state S_DONE by encountering a `semicolon'.
  88.  * The edited text begins wherever di_cp points.
  89.  * We begin the edit in the state given by di_lexstate.
  90.  */
  91. static int
  92. canon(di)
  93.     register struct decode_info *di;
  94. {
  95.     register int state, c, n;
  96.     register char *cp, *outp, *s;
  97.  
  98.     state = di->di_lexstate;
  99.     n = di->di_nch;
  100.     cp = outp = di->di_cp;
  101.     while (--n >= 0) {
  102.         c = *cp++;
  103.         switch (state) {
  104.  
  105.         case S_DULL:
  106.             if (isstopc(c))
  107.                 continue;
  108.             state = S_WORD;
  109.             /* FALLTHROUGH */
  110.  
  111.         case S_WORD:
  112.             if (c == '"') {
  113.                 state = S_QUOTE;
  114.                 continue;
  115.             }
  116.             if (c == '`') {
  117.                 state = S_BKQT1;
  118.                 continue;
  119.             }
  120.             /* look for `semicolon' (call) characters */
  121.             if (cclass[c] & CCL_SEMI)
  122.                 di->di_call = 1;
  123.             if (isstopc(c)) {
  124.                 state = S_DONE;
  125.                 goto done;
  126.             }
  127.             break;
  128.  
  129.         case S_QUOTE:
  130.             if (c == '`') {
  131.                 state = S_BKQT2;
  132.                 continue;
  133.             }
  134.             if (c == '"') {
  135.                 state = S_WORD;
  136.                 continue;
  137.             }
  138.             break;
  139.  
  140.         case S_BKQT1:
  141.             state = S_WORD;
  142.             break;
  143.  
  144.         case S_BKQT2:
  145.             state = S_QUOTE;
  146.             break;
  147.  
  148.         default:
  149.             panic("sdecode canon");
  150.             /* NOTREACHED */
  151.         }
  152.         *outp++ = c;
  153.     }
  154.     n = 0;
  155. done:
  156.     c = outp - di->di_cp;    /* wrote this many chars */
  157.     di->di_lexstate = state;/* save new state */
  158.     di->di_nch = n;        /* and new counts and pointers */
  159.     di->di_cp = cp;
  160.     return (c);
  161. }
  162.  
  163. #define    WDMAX    BUFSIZ        /* when testing, make this small */
  164.  
  165. /*
  166.  * Read a word from the input file.
  167.  * Returns NULL (and sets di_call) at end of \special.
  168.  *
  169.  * We gather up to WDMAX + k characters at a time into an input
  170.  * buffer, and lex them using `canon' above.  The result is at most
  171.  * as long as the original text.  Since a single legal word might
  172.  * be up to WDMAX bytes long, k must be at least 1 (for the trailing
  173.  * NUL).  If the input word is longer, we flush the trailing part,
  174.  * using the k `extra' bytes (and again k must be at least 1).
  175.  *
  176.  * If there is a current keyword and it is in the current buffer
  177.  * (di_bkw != 0), and we have to move existing buffer text around
  178.  * or write over it with new file text, we save the keyword and
  179.  * clear di_bkw.
  180.  */
  181. static char *
  182. word(di, quietly)
  183.     register struct decode_info *di;
  184.     int quietly;
  185. {
  186.     register int n;
  187.     register char *p;
  188.     register int len, trunc = 0;
  189.     static char inbuf[WDMAX + 20];    /* k=20 */
  190.  
  191.     len = 0;        /* no word bytes yet */
  192.     p = di->di_cp;        /* where canon() writes (nil if first time) */
  193.     di->di_lexstate = S_DULL;
  194.     /*
  195.      * Loop invariants:
  196.      *    len is in [0..WDMAX)
  197.      *    di_lexstate != S_DONE
  198.      */
  199.     do {
  200.         /* if the buffer is empty, refill it */
  201.         if (di->di_nch == 0) {
  202.             /* compute remaining buffer space */
  203.             n = sizeof inbuf - len;
  204.             /* read n bytes or rest of string, whichever smaller */
  205.             if (di->di_nfilech < n && (n = di->di_nfilech) == 0) {
  206.                 /* `eof': fake up a call-terminator */
  207.                 di->di_call = 1;
  208.                 if (di->di_lexstate == S_DULL)
  209.                     return (di->di_word = NULL);
  210.                 break;    /* take partial word */
  211.             }
  212.             /* save keyword if this will clobber it */
  213.             if (di->di_bkw) {
  214.                 di->di_kw = strsave(di->di_kw);
  215.                 di->di_bkw = 0;
  216.             }
  217.             /* if there is some canon text, move it to front */
  218.             if (len && p > inbuf) {
  219. #ifdef BLOCK_COPY
  220.                 bcopy(p, inbuf, len);
  221. #else
  222.                 movedown(p, inbuf, len);
  223. #endif
  224.             }
  225.             /* canon text is (or will be) at front now */
  226.             p = inbuf;
  227.             di->di_cp = p + len;
  228.             if (fread(di->di_cp, 1, n, di->di_file) != n) {
  229.                 if (ferror(di->di_file))
  230.                     error(1, -1, "error reading %s",
  231.                         DVIFileName);
  232.                 GripeUnexpectedDVIEOF();
  233.             }
  234.             di->di_nfilech -= n;
  235.             di->di_nch = n;
  236.         }
  237.         n = canon(di);
  238.         if ((len += n) > WDMAX) {
  239.             len = WDMAX;
  240.             trunc = 1;
  241.         }
  242.     } while (di->di_lexstate != S_DONE);
  243.     p[len] = 0;
  244.  
  245.     /*
  246.      * If the word was too long (and hence truncated), complain
  247.      * (unless we are quietly skipping over the remains of a
  248.      * broken \special).
  249.      */
  250.     if (trunc && !quietly) {
  251.         /* word was too long; complain */
  252.         error(0, 0,
  253. #if WDMAX > 20
  254.             "over-long word in \\special: `%.20s...'",
  255. #else
  256.             "over-long word in \\special: `%s...'",
  257. #endif
  258.             p);
  259.         error(0, 0, "(truncated to %d characters)", WDMAX);
  260.     }
  261.     return (di->di_word = p);
  262. }
  263.  
  264. /*
  265.  * Initialise the character class table.
  266.  */
  267. void
  268. SDsetclass(spaces, semis)
  269.     register char *spaces;
  270.     register char *semis;
  271. {
  272.     register int c;
  273.  
  274.     if (spaces == NULL)
  275.         spaces = "\b\t\n\f\r ";
  276.     if (semis == NULL)
  277.         semis = ";\n";
  278.  
  279.     for (c = 0; c < sizeof cclass / sizeof *cclass; c++)
  280.         cclass[c] = 0;
  281.     while ((c = *spaces++) != 0)
  282.         cclass[c] |= CCL_SPACE;
  283.     while ((c = *semis++) != 0)
  284.         cclass[c] |= CCL_SEMI;
  285.     sdset = 1;
  286. }
  287.  
  288. /*
  289.  * Perform a binary search for the given string in the given table.
  290.  */
  291. static struct sdecode *
  292. lookup(str, p, max)
  293.     register char *str;
  294.     register struct sdecode *p;
  295.     register int max;
  296. {
  297.     register struct sdecode *m;
  298.     register int dir;
  299.  
  300.     while (max > 0) {
  301.         /*
  302.          * Look at the median; pick the right hand one if
  303.          * there are two such entries (when `max' is even).
  304.          * Set dir positive if str is `greater than' m->sd_name,
  305.          * negative if it is `less'.  If not equal, move
  306.          * left or right accordingly.  When max is even and
  307.          * we move right, we have to use (max-1)/2 since we
  308.          * broke the tie towards the right.
  309.          */
  310.         m = p + (max >> 1);
  311.         if ((dir = *str - *m->sd_name) == 0)
  312.             dir = strcmp(str, m->sd_name);
  313.         if (dir == 0)    /* found */
  314.             return (m);
  315.         if (dir > 0) {    /* str > mid: move right */
  316.             p = m + 1;
  317.             max = (max - 1) >> 1;
  318.         } else        /* str < mid: move left */
  319.             max >>= 1;
  320.     }
  321.     return (NULL);
  322. }
  323.  
  324. /*
  325.  * Decode a series of \special keywords.
  326.  *
  327.  * The table points to a sorted list of keywords to match.
  328.  * On a match, the appropriate function is called with appropriate
  329.  * arguments.
  330.  *
  331.  * Characters in `semi' (if not NULL) act like `statement separators'.
  332.  * For instance, decoding with semi=>";\n" means that
  333.  *
  334.  *    \special{foo 1; bar 2 4^^Jbaz 3}
  335.  *
  336.  * tries to call foo(1), bar(2,4), and baz(3) (in that order).
  337.  */
  338. void
  339. SDecode(fp, len, table, tsize)
  340.     FILE *fp;
  341.     register i32 len;
  342.     struct sdecode *table;
  343.     int tsize;
  344. {
  345.     register char *cp;
  346.     register struct sdecode *tp;
  347.     register int h;
  348.     struct decode_info di;
  349.  
  350.     /* set defaults, if necessary */
  351.     if (!sdset)
  352.         SDsetclass((char *)NULL, (char *)NULL);
  353.  
  354.     di.di_nch = 0;
  355.     di.di_cp = NULL;
  356.     di.di_nfilech = len;
  357.     di.di_file = fp;
  358.     di.di_bkw = 0;
  359.     for (;;) {
  360.         di.di_call = 0;
  361.         if ((cp = word(&di, 0)) == NULL)
  362.             return;    /* (all done) */
  363.         tp = lookup(cp, table, tsize);
  364.         if (tp == NULL) {
  365.             error(0, 0,
  366.     "Warning: unrecognised \\special keyword `%s' ignored", cp);
  367.             /* di_call is not set, so fall through */
  368.         } else {
  369.             di.di_kw = cp;
  370.             di.di_bkw = 1;
  371.             args(tp, &di);
  372.             if (!di.di_call)
  373.                 error(0, 0,
  374.     "Warning: extra arguments to \\special `%s' ignored", di.di_kw);
  375.             if (!di.di_bkw)
  376.                 free(di.di_kw);
  377.             di.di_bkw = 0;
  378.         }
  379.         /* eat extra arguments, or arguments to an unknown function */
  380.         while (!di.di_call)
  381.             (void) word(&di, 1);
  382.     }
  383. }
  384.  
  385. /*
  386.  * Gather up the arguments to the given function, then (if all goes well)
  387.  * call that function with those arguments.  On return, if di_call is not
  388.  * set, there were extra arguments left over.
  389.  */
  390. static void
  391. args(tp, di)
  392.     register struct sdecode *tp;
  393.     register struct decode_info *di;
  394. {
  395.     char *fmt;
  396.     int n, room, f1;
  397.     i32 *ip, i[4];
  398.     double d[2];
  399.  
  400.     switch (tp->sd_args) {
  401.  
  402.     case sda_none:
  403.         (*tp->sd_fn)(tp->sd_name);
  404.         return;
  405.  
  406.     case sda_s:
  407.         fmt = "s";
  408.         if (scan(di, &fmt))
  409.             break;
  410.         (*tp->sd_fn)(tp->sd_name, di->di_word);
  411.         return;
  412.  
  413.     case sda_d:
  414.         fmt = "d";
  415.         if (scan(di, &fmt, &i[0]))
  416.             break;
  417.         (*tp->sd_fn)(tp->sd_name, i[0]);
  418.         return;
  419.  
  420.     case sda_f:
  421.         fmt = "f";
  422.         if (scan(di, &fmt, &d[0]))
  423.             break;
  424.         (*tp->sd_fn)(tp->sd_name, d[0]);
  425.         return;
  426.  
  427.     case sda_dd:
  428.         fmt = "dd";
  429.         if (scan(di, &fmt, &i[0], &i[1]))
  430.             break;
  431.         (*tp->sd_fn)(tp->sd_name, i[0], i[1]);
  432.         return;
  433.  
  434.     case sda_ff:
  435.         fmt = "ff";
  436.         if (scan(di, &fmt, &d[0], &d[1]))
  437.             break;
  438.         (*tp->sd_fn)(tp->sd_name, d[0], d[1]);
  439.         return;
  440.  
  441.     case sda_ddddff:
  442.         fmt = "ddddff";
  443.         if (scan(di, &fmt, &i[0], &i[1], &i[2], &i[3], &d[0], &d[1]))
  444.             break;
  445.         (*tp->sd_fn)(tp->sd_name, i[0], i[1], i[2], i[3], d[0], d[1]);
  446.         return;
  447.  
  448.     case sda_nd:
  449.         f1 = 'd';
  450.         goto array;
  451.  
  452.     case sda_nx:
  453.         f1 = 'x';
  454.         goto array;
  455.  
  456.     case sda_rest:
  457.         (*tp->sd_fn)(tp->sd_name,
  458.             (i32)di->di_nch, di->di_cp, (i32)di->di_nfilech);
  459.         di->di_call = 1;
  460.         di->di_nch = 0;
  461.         di->di_nfilech = 0;
  462.         return;
  463.  
  464.     default:
  465.         panic("sdecode args(): sd_argtype");
  466.         /* NOTREACHED */
  467.     }
  468.  
  469.     /*
  470.      * If we get here, there is a missing or incorrect argument.
  471.      */
  472.     badarg(*fmt, di);
  473.     return;
  474.  
  475. array:
  476.     ip = (i32 *)malloc((unsigned)(room = 10) * sizeof(i32));
  477.     if (ip == NULL)
  478.         goto nomem;
  479.     n = 0;
  480.     while (!di->di_call && word(di, 0) != NULL) {
  481.         if (scan_i(di->di_word, f1, &i[0])) {
  482.             badarg(f1, di);
  483.             free((char *)ip);
  484.             return;
  485.         }
  486.         if (n == room) {
  487.             room += 10;
  488.             ip = (i32 *)realloc((char *)ip,
  489.                 (unsigned)room * sizeof(i32));
  490.             if (ip == NULL)
  491.                 goto nomem;
  492.         }
  493.         ip[n++] = i[0];
  494.     }
  495.     (*tp->sd_fn)(tp->sd_name, n, ip);
  496.     free((char *)ip);
  497.     return;
  498. nomem:
  499.     /*
  500.      * might as well stop: if malloc() failed, we will probably get
  501.      * nowhere fast anyway.
  502.      */
  503.     error(1, -1,
  504.         "ran out of memory allocating %d bytes for \\special `%s'",
  505.         n * sizeof(i32), di->di_kw);
  506.     /* NOTREACHED */
  507.  
  508. }
  509.  
  510. /*
  511.  * Complain about an argument.  Scan() has set di_word to NULL if
  512.  * it was missing; otherwise it is incorrect.
  513.  */
  514. static void
  515. badarg(c, di)
  516.     int c;
  517.     struct decode_info *di;
  518. {
  519.     char *s;
  520.  
  521.     switch (c) {
  522.  
  523.     case 'd':
  524.         s = "decimal";
  525.         break;
  526.  
  527.     case 'f':
  528.         s = "floating";
  529.         break;
  530.  
  531.     case 's':
  532.         s = "string";    /* must be `missing', not `bad' */
  533.         break;
  534.  
  535.     case 'x':
  536.         s = "hexadecimal";
  537.         break;
  538.  
  539.     default:
  540.         panic("sdecode badarg(%c)", c);
  541.         /* NOTREACHED */
  542.     }
  543.     if (di->di_word == NULL)
  544.         error(0, 0, "missing %s argument for \\special `%s'",
  545.             s, di->di_kw);
  546.     else {
  547.         error(0, 0, "bad %s argument to \\special `%s': `%s'",
  548.             s, di->di_kw, di->di_word);
  549.         error(0, 0, "(this part of the \\special will be ignored)");
  550.         while (!di->di_call)
  551.             (void) word(di, 1);
  552.     }
  553. }
  554.  
  555. static int
  556. scan(va_alist)
  557.     va_dcl
  558. {
  559.     register struct decode_info *di;
  560.     register char *fmt;
  561.     register int c;
  562.     char **fmtp;
  563.     va_list ap;
  564.  
  565.     va_start(ap);
  566.     di = va_arg(ap, struct decode_info *);
  567.     fmtp = va_arg(ap, char **);
  568.     fmt = *fmtp;
  569.     while ((c = *fmt++) != 0) {
  570.         if (di->di_call || word(di, 0) == NULL) {
  571.             di->di_word = NULL;
  572.             goto out;
  573.         }
  574.         switch (c) {
  575.  
  576.         /* BEWARE, can only handle one string */
  577.         case 's':    /* always accepted */
  578.             break;
  579.  
  580.         case 'd':
  581.         case 'x':
  582.             if (scan_i(di->di_word, c, va_arg(ap, i32 *)))
  583.                 goto out;
  584.             break;
  585.  
  586.         case 'f':
  587.             if (scan_d(di->di_word, va_arg(ap, double *)))
  588.                 goto out;
  589.             break;
  590.  
  591.         default:
  592.             panic("sdecode scan() *fmt=%c", c);
  593.             /* NOTREACHED */
  594.         }
  595.     }
  596.     c = 0;
  597. out:
  598.     va_end(ap);
  599.     *fmtp = fmt - 1;
  600.     return (c);
  601. }
  602.  
  603. static int
  604. scan_i(p, c, result)
  605.     char *p;
  606.     int c;
  607.     i32 *result;
  608. {
  609. #ifdef HAVE_STRTOL
  610.     char *end;
  611.     long strtol();
  612.  
  613.     *result = strtol(p, &end, c == 'x' ? 16 : 10);
  614.     return (*end);
  615. #else
  616.     long l;
  617.     char junk;
  618.  
  619.     if (sscanf(p, c == 'x' ? "%lx%c" : "%ld%c", &l, &junk) != 1)
  620.         return (1);
  621.     *result = l;
  622.     return (0);
  623. #endif
  624. }
  625.  
  626. static int
  627. scan_d(p, result)
  628.     char *p;
  629.     double *result;
  630. {
  631. #ifdef HAVE_STRTOD
  632.     char *end;
  633.     double strtod();
  634.  
  635.     *result = strtod(p, &end);
  636.     return (*end);
  637. #else
  638.     char junk;
  639.  
  640.     return (sscanf(p, "%lf%c", result, &junk) != 1);
  641. #endif
  642. }
  643.