diff options
author | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
commit | 487ca4806cc046076293cf6cc5fbba0db282bac7 (patch) | |
tree | 847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/seetexk/sdecode.c | |
parent | a3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff) |
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/seetexk/sdecode.c')
-rw-r--r-- | Build/source/texk/seetexk/sdecode.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/Build/source/texk/seetexk/sdecode.c b/Build/source/texk/seetexk/sdecode.c new file mode 100644 index 00000000000..02fe746da08 --- /dev/null +++ b/Build/source/texk/seetexk/sdecode.c @@ -0,0 +1,689 @@ +/* + * Copyright (c) 1989 University of Maryland + * Department of Computer Science. All rights reserved. + * Permission to copy for any purpose is hereby granted + * so long as this copyright notice remains intact. + */ + +#ifndef lint +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 $"; +#endif + +#include <stdio.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +#include "types.h" +#include "error.h" +#include "sdecode.h" + +static char cclass[256]; /* XXX assumes 8-bit char */ +#define CCL_SPACE 0x01 /* white space */ +#define CCL_SEMI 0x02 /* `semicolon' (statement separator) */ +#define isstopc(c) (cclass[c] != 0) /* both space and semi char */ +static int sdset; /* flag says whether cclass[] set up */ + +static void args(), badarg(); +static int scan(struct decode_info *di, char **fmt, ...), scan_i(), scan_d(); + +extern char *strsave(); + +extern char *DVIFileName; + +#ifndef BLOCK_COPY +/* + * Copy the text at `src' `downward' in memory for `len' bytes. + * This is like strcpy or memcpy, but handles one direction of + * overlap (when dst < src). + */ +static void +movedown(src, dst, len) + register char *src, *dst; + register int len; +{ + + while (--len >= 0) + *dst++ = *src++; +} +#endif + +/* + * We have a number of states to do word interpretation. Outside words, + * we skip blanks and `semicolons'. The first non-blank, non-`semi' enters + * WORD state; in WORD state, double quote characters switch to + * QUOTE state, after which another double quote switches back to + * WORD state, and blanks and `semi' end WORD state, switching to DONE. + * While in WORD or QUOTE states, `c produces c for all characters c; + * to do this, we use two more states. + */ +#define S_DULL 0 /* not in a word */ +#define S_WORD 1 /* in a word */ +#define S_QUOTE 2 /* inside "" in a word */ +#define S_BKQT1 3 /* not in "", but just read one ` */ +#define S_BKQT2 4 /* in "", but just read one ` */ +#define S_DONE 5 /* found end of word */ + +/* + * Structure used to communicate between the various functions. + */ +struct decode_info { + /* data used to read from input file */ + int di_nch; /* number of characters remaining in buffer */ + char *di_cp; /* next character to be read from buffer */ + long di_nfilech; /* number of characters remaining in file */ + FILE *di_file; /* the file itself */ + + /* information used and/or returned by the canon routine */ + int di_lexstate; /* lexical state */ + int di_call; /* set when we reach terminating `semicolon' */ + + /* information used more globally */ + char *di_word; /* last word from word() */ + char *di_kw; /* the keyword */ + int di_bkw; /* true iff keyword is in static buffer */ +}; + +/* + * `Edit' the text in the buffer in-place. + * Return the count of characters resulting from the edit (e.g., + * from `foo`"bar' the count would be 7). + * Set di_call if we reach state S_DONE by encountering a `semicolon'. + * The edited text begins wherever di_cp points. + * We begin the edit in the state given by di_lexstate. + */ +static int +canon(di) + register struct decode_info *di; +{ + register int state, c, n; + register char *cp, *outp, *s; + + state = di->di_lexstate; + n = di->di_nch; + cp = outp = di->di_cp; + while (--n >= 0) { + c = *cp++; + switch (state) { + + case S_DULL: + if (isstopc(c)) + continue; + state = S_WORD; + /* FALLTHROUGH */ + + case S_WORD: + if (c == '"') { + state = S_QUOTE; + continue; + } + if (c == '`') { + state = S_BKQT1; + continue; + } + /* look for `semicolon' (call) characters */ + if (cclass[c] & CCL_SEMI) + di->di_call = 1; + if (isstopc(c)) { + state = S_DONE; + goto done; + } + break; + + case S_QUOTE: + if (c == '`') { + state = S_BKQT2; + continue; + } + if (c == '"') { + state = S_WORD; + continue; + } + break; + + case S_BKQT1: + state = S_WORD; + break; + + case S_BKQT2: + state = S_QUOTE; + break; + + default: + panic("sdecode canon"); + /* NOTREACHED */ + } + *outp++ = c; + } + n = 0; +done: + c = outp - di->di_cp; /* wrote this many chars */ + di->di_lexstate = state;/* save new state */ + di->di_nch = n; /* and new counts and pointers */ + di->di_cp = cp; + return (c); +} + +#define WDMAX BUFSIZ /* when testing, make this small */ + +/* + * Read a word from the input file. + * Returns NULL (and sets di_call) at end of \special. + * + * We gather up to WDMAX + k characters at a time into an input + * buffer, and lex them using `canon' above. The result is at most + * as long as the original text. Since a single legal word might + * be up to WDMAX bytes long, k must be at least 1 (for the trailing + * NUL). If the input word is longer, we flush the trailing part, + * using the k `extra' bytes (and again k must be at least 1). + * + * If there is a current keyword and it is in the current buffer + * (di_bkw != 0), and we have to move existing buffer text around + * or write over it with new file text, we save the keyword and + * clear di_bkw. + */ +static char * +word(di, quietly) + register struct decode_info *di; + int quietly; +{ + register int n; + register char *p; + register int len, trunc = 0; + static char inbuf[WDMAX + 20]; /* k=20 */ + + len = 0; /* no word bytes yet */ + p = di->di_cp; /* where canon() writes (nil if first time) */ + di->di_lexstate = S_DULL; + /* + * Loop invariants: + * len is in [0..WDMAX) + * di_lexstate != S_DONE + */ + do { + /* if the buffer is empty, refill it */ + if (di->di_nch == 0) { + /* compute remaining buffer space */ + n = sizeof inbuf - len; + /* read n bytes or rest of string, whichever smaller */ + if (di->di_nfilech < n && (n = di->di_nfilech) == 0) { + /* `eof': fake up a call-terminator */ + di->di_call = 1; + if (di->di_lexstate == S_DULL) + return (di->di_word = NULL); + break; /* take partial word */ + } + /* save keyword if this will clobber it */ + if (di->di_bkw) { + di->di_kw = strsave(di->di_kw); + di->di_bkw = 0; + } + /* if there is some canon text, move it to front */ + if (len && p > inbuf) { +#ifdef BLOCK_COPY + bcopy(p, inbuf, len); +#else + movedown(p, inbuf, len); +#endif + } + /* canon text is (or will be) at front now */ + p = inbuf; + di->di_cp = p + len; + if (fread(di->di_cp, 1, n, di->di_file) != n) { + if (ferror(di->di_file)) + error(1, -1, "error reading %s", + DVIFileName); + GripeUnexpectedDVIEOF(); + } + di->di_nfilech -= n; + di->di_nch = n; + } + n = canon(di); + if ((len += n) > WDMAX) { + len = WDMAX; + trunc = 1; + } + } while (di->di_lexstate != S_DONE); + p[len] = 0; + + /* + * If the word was too long (and hence truncated), complain + * (unless we are quietly skipping over the remains of a + * broken \special). + */ + if (trunc && !quietly) { + /* word was too long; complain */ + error(0, 0, +#if WDMAX > 20 + "over-long word in \\special: `%.20s...'", +#else + "over-long word in \\special: `%s...'", +#endif + p); + error(0, 0, "(truncated to %d characters)", WDMAX); + } + return (di->di_word = p); +} + +/* + * Initialise the character class table. + */ +void +SDsetclass(spaces, semis) + register char *spaces; + register char *semis; +{ + register int c; + + if (spaces == NULL) + spaces = "\b\t\n\f\r "; + if (semis == NULL) + semis = ";\n"; + + for (c = 0; c < sizeof cclass / sizeof *cclass; c++) + cclass[c] = 0; + while ((c = *spaces++) != 0) + cclass[c] |= CCL_SPACE; + while ((c = *semis++) != 0) + cclass[c] |= CCL_SEMI; + sdset = 1; +} + +/* + * Perform a binary search for the given string in the given table. + */ +static struct sdecode * +lookup(str, p, max) + register char *str; + register struct sdecode *p; + register int max; +{ + register struct sdecode *m; + register int dir; + + while (max > 0) { + /* + * Look at the median; pick the right hand one if + * there are two such entries (when `max' is even). + * Set dir positive if str is `greater than' m->sd_name, + * negative if it is `less'. If not equal, move + * left or right accordingly. When max is even and + * we move right, we have to use (max-1)/2 since we + * broke the tie towards the right. + */ + m = p + (max >> 1); + if ((dir = *str - *m->sd_name) == 0) + dir = strcmp(str, m->sd_name); + if (dir == 0) /* found */ + return (m); + if (dir > 0) { /* str > mid: move right */ + p = m + 1; + max = (max - 1) >> 1; + } else /* str < mid: move left */ + max >>= 1; + } + return (NULL); +} + +/* + * Decode a series of \special keywords. + * + * The table points to a sorted list of keywords to match. + * On a match, the appropriate function is called with appropriate + * arguments. + * + * Characters in `semi' (if not NULL) act like `statement separators'. + * For instance, decoding with semi=>";\n" means that + * + * \special{foo 1; bar 2 4^^Jbaz 3} + * + * tries to call foo(1), bar(2,4), and baz(3) (in that order). + */ +void +SDecode(fp, len, table, tsize) + FILE *fp; + register i32 len; + struct sdecode *table; + int tsize; +{ + register char *cp; + register struct sdecode *tp; + register int h; + struct decode_info di; + + /* set defaults, if necessary */ + if (!sdset) + SDsetclass((char *)NULL, (char *)NULL); + + di.di_nch = 0; + di.di_cp = NULL; + di.di_nfilech = len; + di.di_file = fp; + di.di_bkw = 0; + for (;;) { + di.di_call = 0; + if ((cp = word(&di, 0)) == NULL) + return; /* (all done) */ + tp = lookup(cp, table, tsize); + if (tp == NULL) { + error(0, 0, + "Warning: unrecognised \\special keyword `%s' ignored", cp); + /* di_call is not set, so fall through */ + } else { + di.di_kw = cp; + di.di_bkw = 1; + args(tp, &di); + if (!di.di_call) + error(0, 0, + "Warning: extra arguments to \\special `%s' ignored", di.di_kw); + if (!di.di_bkw) + free(di.di_kw); + di.di_bkw = 0; + } + /* eat extra arguments, or arguments to an unknown function */ + while (!di.di_call) + (void) word(&di, 1); + } +} + +/* + * Gather up the arguments to the given function, then (if all goes well) + * call that function with those arguments. On return, if di_call is not + * set, there were extra arguments left over. + */ +static void +args(tp, di) + register struct sdecode *tp; + register struct decode_info *di; +{ + char *fmt; + int n, room, f1; + i32 *ip, i[4]; + double d[2]; + + switch (tp->sd_args) { + + case sda_none: + (*tp->sd_fn)(tp->sd_name); + return; + + case sda_s: + fmt = "s"; + if (scan(di, &fmt)) + break; + (*tp->sd_fn)(tp->sd_name, di->di_word); + return; + + case sda_d: + fmt = "d"; + if (scan(di, &fmt, &i[0])) + break; + (*tp->sd_fn)(tp->sd_name, i[0]); + return; + + case sda_f: + fmt = "f"; + if (scan(di, &fmt, &d[0])) + break; + (*tp->sd_fn)(tp->sd_name, d[0]); + return; + + case sda_dd: + fmt = "dd"; + if (scan(di, &fmt, &i[0], &i[1])) + break; + (*tp->sd_fn)(tp->sd_name, i[0], i[1]); + return; + + case sda_ff: + fmt = "ff"; + if (scan(di, &fmt, &d[0], &d[1])) + break; + (*tp->sd_fn)(tp->sd_name, d[0], d[1]); + return; + + case sda_ddddff: + fmt = "ddddff"; + if (scan(di, &fmt, &i[0], &i[1], &i[2], &i[3], &d[0], &d[1])) + break; + (*tp->sd_fn)(tp->sd_name, i[0], i[1], i[2], i[3], d[0], d[1]); + return; + + case sda_nd: + f1 = 'd'; + goto array; + + case sda_nx: + f1 = 'x'; + goto array; + + case sda_rest: + (*tp->sd_fn)(tp->sd_name, + (i32)di->di_nch, di->di_cp, (i32)di->di_nfilech); + di->di_call = 1; + di->di_nch = 0; + di->di_nfilech = 0; + return; + + default: + panic("sdecode args(): sd_argtype"); + /* NOTREACHED */ + } + + /* + * If we get here, there is a missing or incorrect argument. + */ + badarg(*fmt, di); + return; + +array: + ip = (i32 *)malloc((unsigned)(room = 10) * sizeof(i32)); + if (ip == NULL) + goto nomem; + n = 0; + while (!di->di_call && word(di, 0) != NULL) { + if (scan_i(di->di_word, f1, &i[0])) { + badarg(f1, di); + free((char *)ip); + return; + } + if (n == room) { + room += 10; + ip = (i32 *)realloc((char *)ip, + (unsigned)room * sizeof(i32)); + if (ip == NULL) + goto nomem; + } + ip[n++] = i[0]; + } + (*tp->sd_fn)(tp->sd_name, n, ip); + free((char *)ip); + return; +nomem: + /* + * might as well stop: if malloc() failed, we will probably get + * nowhere fast anyway. + */ + error(1, -1, + "ran out of memory allocating %d bytes for \\special `%s'", + n * sizeof(i32), di->di_kw); + /* NOTREACHED */ + +} + +/* + * Complain about an argument. Scan() has set di_word to NULL if + * it was missing; otherwise it is incorrect. + */ +static void +badarg(c, di) + int c; + struct decode_info *di; +{ + char *s; + + switch (c) { + + case 'd': + s = "decimal"; + break; + + case 'f': + s = "floating"; + break; + + case 's': + s = "string"; /* must be `missing', not `bad' */ + break; + + case 'x': + s = "hexadecimal"; + break; + + default: + panic("sdecode badarg(%c)", c); + /* NOTREACHED */ + } + if (di->di_word == NULL) + error(0, 0, "missing %s argument for \\special `%s'", + s, di->di_kw); + else { + error(0, 0, "bad %s argument to \\special `%s': `%s'", + s, di->di_kw, di->di_word); + error(0, 0, "(this part of the \\special will be ignored)"); + while (!di->di_call) + (void) word(di, 1); + } +} + +#ifdef WIN32 +static int +scan(struct decode_info *di, char **fmtp, va_list ap) +{ + register int c; + char *fmt = *fmtp; + + va_start(ap, fmt); + while ((c = *fmt++) != 0) { + if (di->di_call || word(di, 0) == NULL) { + di->di_word = NULL; + goto out; + } + switch (c) { + + /* BEWARE, can only handle one string */ + case 's': /* always accepted */ + break; + + case 'd': + case 'x': + if (scan_i(di->di_word, c, va_arg(ap, i32 *))) + goto out; + break; + + case 'f': + if (scan_d(di->di_word, va_arg(ap, double *))) + goto out; + break; + + default: + panic("sdecode scan() *fmt=%c", c); + /* NOTREACHED */ + } + } + c = 0; +out: + va_end(ap); + return (c); +} + +#else +static int +scan(va_alist) + va_dcl +{ + register struct decode_info *di; + register char *fmt; + register int c; + char **fmtp; + va_list ap; + + va_start(ap); + di = va_arg(ap, struct decode_info *); + fmtp = va_arg(ap, char **); + fmt = *fmtp; + while ((c = *fmt++) != 0) { + if (di->di_call || word(di, 0) == NULL) { + di->di_word = NULL; + goto out; + } + switch (c) { + + /* BEWARE, can only handle one string */ + case 's': /* always accepted */ + break; + + case 'd': + case 'x': + if (scan_i(di->di_word, c, va_arg(ap, i32 *))) + goto out; + break; + + case 'f': + if (scan_d(di->di_word, va_arg(ap, double *))) + goto out; + break; + + default: + panic("sdecode scan() *fmt=%c", c); + /* NOTREACHED */ + } + } + c = 0; +out: + va_end(ap); + *fmtp = fmt - 1; + return (c); +} +#endif +static int +scan_i(p, c, result) + char *p; + int c; + i32 *result; +{ +#ifdef HAVE_STRTOL + char *end; + long strtol(); + + *result = strtol(p, &end, c == 'x' ? 16 : 10); + return (*end); +#else + long l; + char junk; + + if (sscanf(p, c == 'x' ? "%lx%c" : "%ld%c", &l, &junk) != 1) + return (1); + *result = l; + return (0); +#endif +} + +static int +scan_d(p, result) + char *p; + double *result; +{ +#ifdef HAVE_STRTOD + char *end; + double strtod(); + + *result = strtod(p, &end); + return (*end); +#else + char junk; + + return (sscanf(p, "%lf%c", result, &junk) != 1); +#endif +} |