blob: 6076d15ece46488c34c513ca617fe0df69719fd3 [file] [log] [blame]
/* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
Distributed by Free Software Foundation, Inc.
This file is part of Ghostscript.
Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer
to the Ghostscript General Public License for full details.
Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License. A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
copies. */
/* iscan.c */
/* Token scanner for Ghostscript interpreter */
#include <ctype.h>
#include "memory_.h"
#include "ghost.h"
#include "arch.h"
#include "alloc.h"
#include "dict.h" /* for //name lookup */
#include "errors.h"
#include "name.h"
#include "store.h"
#include "stream.h"
#include "scanchar.h"
#include <stdint.h>
/* Array packing flag */
int array_packing;
/* Forward references */
private int scan_hex_string(P2(stream *, ref *)),
scan_int(P4(stream *, int, long *, double *)),
scan_string(P3(stream *, int, ref *));
/* Import the dictionary stack for //name lookup */
extern ref dstack[];
extern ref *dsp;
/* Import the operand stack for accumulating procedure bodies */
extern ref *osp, *ostop;
/* Static constants */
private ref left_bracket;
private ref right_bracket;
private ref empty_name;
/* Define the character scanning table (see scanchar.h). */
byte scan_char_array[257];
/* A structure for dynamically growable objects */
typedef struct dynamic_area_s {
byte *base;
byte *next;
uint num_elts;
uint elt_size;
int is_dynamic; /* false if using fixed buffer */
byte *limit;
} dynamic_area;
/* Begin a dynamic object. */
/* dynamic_begin returns the value of alloc_dynamic, which may be 0: */
/* the invoker of dynamic_begin must test the value against 0. */
#define dynamic_begin(pda, dnum, desize)\
((pda)->base = alloc_dynamic((pda)->num_elts = (dnum),\
(pda)->elt_size = (desize), "scanner"),\
(pda)->limit = (pda)->base + (dnum) * (desize),\
(pda)->is_dynamic = 1,\
(pda)->next = (pda)->base)
/* Grow a dynamic object */
private int
dynamic_grow(register dynamic_area *pda)
{ uint num = pda->num_elts;
uintptr_t size = num * pda->elt_size;
uintptr_t new_num;
uint pos = pda->next - pda->base;
size = (size < 10 ? 20 : size >= (max_uint >> 1) ? UINTPTR_MAX : size << 1);
new_num = size / pda->elt_size;
if ( pda->is_dynamic )
{ pda->base = alloc_grow(pda->base, num, new_num, pda->elt_size, "scanner");
if ( pda->base == 0 ) return 0;
pda->num_elts = new_num;
pda->limit = pda->base + size;
}
else
{ byte *base = pda->base;
uint old_size = size;
if ( !dynamic_begin(pda, new_num, pda->elt_size) ) return 0;
memcpy(pda->base, base, old_size);
pda->is_dynamic = 1;
}
pda->next = pda->base + pos;
return 1;
}
/* Get rid of an unwanted dynamic object */
#define dynamic_free(pda)\
if ( (pda)->is_dynamic )\
alloc_free((char *)((pda)->base), (pda)->num_elts, (pda)->elt_size, "scanner")
/* Initialize the scanner. */
void
scan_init()
{ register byte *decoder = scan_char_decoder;
/* Precompute left and right bracket and empty name tokens */
{ name_ref((byte *)"[", 1, &left_bracket, 0);
name_ref((byte *)"]", 1, &right_bracket, 0);
name_ref((byte *)"", 0, &empty_name, 0);
}
/* Initialize decoder array */
{ static char stop_chars[] = "()<>[]{}/%";
static char space_chars[] = " \f\t\n\r";
register int i;
char *p;
decoder[-1] = ctype_eof;
memset(decoder, ctype_name, 256);
for ( p = space_chars; *p; p++ )
decoder[*p] = ctype_space;
decoder[char_NULL] = decoder[char_VT] =
decoder[char_DOS_EOF] = ctype_space;
for ( p = stop_chars; *p; p++ )
decoder[*p] = ctype_other;
for ( i = 0; i < 10; i++ )
decoder['0' + i] = i;
for ( i = 0; i < max_radix - 10; i++ )
decoder['A' + i] = decoder['a' + i] = i + 10;
}
array_packing = 0;
}
/* Read a token from a stream. */
/* Return 1 for end-of-stream, 0 if a token was read, */
/* or a (negative) error code. */
/* If the token required a terminating character (i.e., was a name or */
/* number) and the next character was whitespace, read and discard */
/* that character: see the description of the 'token' operator on */
/* p. 232 of the Red Book. */
/* from_string indicates reading from a string vs. a file, */
/* because \ escapes are not recognized in the former case. */
/* (See the footnote on p. 23 of the Red Book.) */
int
scan_token(register stream *s, int from_string, ref *pref)
{ ref *myref = pref;
dynamic_area proc_da;
int pstack = 0; /* offset from proc_da.base */
int retcode = 0;
register int c;
int name_type; /* number of /'s preceding */
int try_number;
register byte *decoder = scan_char_decoder;
/******
****** Newer P*stScr*pt interpreters don't use from_string.
******/
from_string = 0;
top: c = sgetc(s);
#ifdef DEBUG
if ( gs_debug['s'] )
printf((c >= 32 && c <= 126 ? "`%c'" : "`%03o'"), c);
#endif
switch ( c )
{
case ' ': case '\f': case '\t': case '\n': case '\r':
case char_NULL: case char_VT: case char_DOS_EOF:
goto top;
case '[':
*myref = left_bracket;
r_set_attrs(myref, a_executable);
break;
case ']':
*myref = right_bracket;
r_set_attrs(myref, a_executable);
break;
case '<':
retcode = scan_hex_string(s, myref);
break;
case '(':
retcode = scan_string(s, from_string, myref);
break;
case '{':
if ( pstack == 0 )
{ /* Use the operand stack to accumulate procedures. */
myref = osp + 1;
proc_da.base = (byte *)myref;
proc_da.limit = (byte *)(ostop + 1);
proc_da.is_dynamic = 0;
proc_da.elt_size = sizeof(ref);
proc_da.num_elts = ostop - osp;
}
if ( proc_da.limit - (byte *)myref < 2 * sizeof(ref) )
{ proc_da.next = (byte *)myref;
if ( !dynamic_grow(&proc_da) )
return e_VMerror;
myref = (ref *)proc_da.next;
}
myref->size = pstack;
myref++;
pstack = (byte *)myref - proc_da.base;
goto top;
case '>':
case ')':
retcode = e_syntaxerror;
break;
case '}':
if ( pstack == 0 )
{ retcode = e_syntaxerror;
break;
}
{ ref *ref0 = (ref *)(proc_da.base + pstack);
uint size = myref - ref0;
ref *aref;
myref = ref0 - 1;
pstack = myref->size;
if ( pstack == 0 && proc_da.is_dynamic )
{ /* Top-level procedure, shrink in place. */
memcpy(myref, ref0, size * sizeof(ref));
aref = (ref *)alloc_shrink((byte *)myref, proc_da.num_elts, size, sizeof(ref), "scanner(top proc)");
if ( aref == 0 ) return e_VMerror;
myref = pref;
}
else
{ /* Not top-level, or in ostack: copy it. */
aref = (ref *)alloc(size, sizeof(ref), "scanner(proc)");
if ( aref == 0 ) return e_VMerror;
memcpy(aref, ref0, size * sizeof(ref));
if ( pstack == 0 ) myref = pref;
}
if ( array_packing )
make_tasv(myref, t_packedarray, a_executable + a_read + a_execute, size, refs, aref);
else
make_tasv(myref, t_array, a_executable + a_all, size, refs, aref);
}
break;
case '/':
c = sgetc(s);
if ( c == '/' )
{ name_type = 2;
c = sgetc(s);
}
else
name_type = 1;
try_number = 0;
switch ( decoder[c] )
{
case ctype_name:
default:
goto do_name;
case ctype_eof:
/* Empty name: bizarre but legitimate. */
*myref = empty_name;
goto have_name;
case ctype_other:
switch ( c )
{
case '[': /* only special as first character */
*myref = left_bracket;
goto have_name;
case ']': /* ditto */
*myref = right_bracket;
goto have_name;
default:
/* Empty name: bizarre but legitimate. */
*myref = empty_name;
sputback(s);
goto have_name;
}
case ctype_space:
/* Empty name: bizarre but legitimate. */
*myref = empty_name;
/* Check for \r\n */
if ( c == '\r' && (c = sgetc(s)) != '\n' && c != EOFC )
sputback(s);
goto have_name;
}
/* NOTREACHED */
case '%':
{ int c1;
do { c = sgetc(s); }
while ( c != '\f' && c != '\n' && c != '\r' && c != EOFC );
if ( c == '\r' && (c1 = sgetc(s)) != '\n' && c1 != EOFC )
sputback(s);
if ( c != EOFC ) goto top;
} /* falls through */
case EOFC:
retcode = (pstack != 0 ? e_syntaxerror : 1);
break;
/* Handle separately the names that might be a number */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.': case '+': case '-':
try_number = 1;
name_type = 0;
goto do_name;
/* Check for a binary object */
default: /* ****** NYI ****** */
/* Handle the common cases (letters and _) explicitly, */
/* rather than going through the default test. */
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':
case 'n': case 'o': case 'p': case 'q': case 'r': case 's':
case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S':
case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
case '_':
try_number = 0;
name_type = 0;
/* Common code for scanning a name. */
/* try_number and name_type are already set. */
/* We know c has ctype_name or is a digit. */
do_name:
{ dynamic_area da;
/* Try to scan entirely within the stream buffer. */
/* We stop 1 character early, so we don't switch buffers */
/* looking ahead if the name is terminated by \r\n. */
register byte *ptr = sbufptr(s);
byte *end = sbufend(s) - 1;
da.base = ptr - 1;
da.is_dynamic = 0;
do
{ if ( ptr >= end )
{ ssetbufptr(s, ptr);
/* Initialize the dynamic area. */
/* We have to do this before the next */
/* sgetc, which will overwrite the buffer. */
da.next = da.limit = ptr;
da.num_elts = ptr - da.base;
da.elt_size = 1;
if ( !dynamic_grow(&da) ) return e_VMerror;
ptr = da.next;
goto dyn_name;
}
c = *ptr++;
}
while ( decoder[c] <= ctype_name ); /* digit or name */
/* Name ended within the buffer. */
ssetbufptr(s, ptr);
ptr--;
goto nx;
/* Name overran buffer. */
dyn_name: while ( decoder[c = sgetc(s)] <= ctype_name )
{ if ( ptr == da.limit )
{ da.next = ptr;
if ( !dynamic_grow(&da) )
return e_VMerror;
ptr = da.next;
}
*ptr++ = c;
}
nx: switch ( decoder[c] )
{
case ctype_other:
sputback(s);
case ctype_space:
/* Check for \r\n */
if ( c == '\r' && (c = sgetc(s)) != '\n' && c != EOFC )
sputback(s);
case ctype_eof: ;
}
/* Check for a number */
if ( try_number )
{ stream nst;
stream *ns = &nst;
sread_string(ns, da.base, (uint)(ptr - da.base));
retcode = scan_number(ns, myref);
if ( retcode != e_syntaxerror )
{ dynamic_free(&da);
goto have_name; /* might be e_limitcheck */
}
}
retcode = name_ref(da.base, (uint)(ptr - da.base), myref, 1);
dynamic_free(&da);
}
/* Done scanning. Check for preceding /'s. */
have_name: if ( retcode < 0 ) return retcode;
switch ( name_type )
{
case 0: /* ordinary executable name */
if ( r_type(myref) == t_name ) /* i.e., not a number */
r_set_attrs(myref, a_executable);
case 1: /* quoted name */
break;
case 2: /* immediate lookup */
{ ref *pvalue;
if ( dict_lookup(dstack, dsp, myref, &pvalue) <= 0 )
return e_undefined;
store(myref, *pvalue);
}
}
}
/* If we are the top level, return the object, otherwise keep going */
if ( pstack == 0 || retcode < 0 )
return retcode;
if ( proc_da.limit - (byte *)myref < 2 * sizeof(ref) )
{ proc_da.next = (byte *)myref;
if ( !dynamic_grow(&proc_da) )
return e_VMerror; /* out of room */
myref = (ref *)proc_da.next;
}
myref++;
goto top;
}
/* The internal scanning procedures return 0 on success, */
/* or a (negative) error code on failure. */
/* Procedure to scan a number. This is also called by cvi and cvr. */
int
scan_number(register stream *s, ref *pref)
{ /* Powers of 10 up to 6 can be represented accurately as */
/* a single-precision float. */
#define num_powers_10 6
static float powers_10[num_powers_10+1] =
{ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6 };
int sign = 0;
long ival;
double dval;
int exp10 = 0;
int code;
register int c;
switch ( c = sgetc(s) )
{
case '+': sign = 1; c = sgetc(s); break;
case '-': sign = -1; c = sgetc(s); break;
}
if ( !isdigit(c) )
{ if ( c != '.' ) return e_syntaxerror;
c = sgetc(s);
if ( !isdigit(c) ) return e_syntaxerror;
ival = 0;
goto fi;
}
sputback(s);
if ( (code = scan_int(s, 10, &ival, &dval)) != 0 )
{ if ( code < 0 ) return code; /* e_syntaxerror */
/* Code == 1, i.e., the integer overflowed. */
switch ( c = sgetc(s) )
{
default: return e_syntaxerror; /* not terminated properly */
case '.': c = sgetc(s); goto fd;
case EOFC: /* return a float */
make_real(pref, (float)(sign < 0 ? -dval : dval));
return 0;
}
}
switch ( c = sgetc(s) )
{
default: return e_syntaxerror; /* not terminated properly */
case '.': c = sgetc(s); goto fi;
case '#':
if ( sign || ival < min_radix || ival > max_radix )
return e_syntaxerror;
code = scan_int(s, (int)ival, &ival, NULL);
if ( code ) return code;
if ( sgetc(s) != EOFC ) return e_syntaxerror;
case EOFC: ;
}
/* Return an integer */
make_int(pref, (sign < 0 ? -ival : ival));
return 0;
/* Handle a real. We just saw the decimal point. */
/* Enter here if we are still accumulating an integer in ival. */
fi: while ( isdigit(c) )
{ /* Check for overflowing ival */
if ( ival >= (max_ulong >> 1) / 10 - 1 )
{ dval = ival;
goto fd;
}
ival = ival * 10 + (c - '0');
c = sgetc(s);
exp10--;
}
if ( sign < 0 ) ival = -ival;
/* Take a shortcut for the common case */
if ( !(c == 'e' || c == 'E' || exp10 < -num_powers_10) )
{ make_real(pref, (float)(ival / powers_10[-exp10]));
return 0;
}
dval = ival;
goto fe;
/* Now we are accumulating a double in dval. */
fd: while ( isdigit(c) )
{ dval = dval * 10 + (c - '0');
c = sgetc(s);
exp10--;
}
if ( sign < 0 ) dval = -dval;
fe: /* dval contains the value, negated if necessary */
if ( c == 'e' || c == 'E' )
{ /* Check for a following exponent. */
int esign = 0;
long eexp;
switch ( c = sgetc(s) )
{
case '+': break;
case '-': esign = 1; break;
default: sputback(s);
}
code = scan_int(s, 10, &eexp, NULL);
if ( code < 0 ) return code;
if ( code > 0 || eexp > 999 )
return e_limitcheck; /* semi-arbitrary */
if ( esign )
exp10 -= (int)eexp;
else
exp10 += (int)eexp;
c = sgetc(s);
}
if ( c != EOFC ) return e_syntaxerror;
/* Compute dval * 10^exp10. */
if ( exp10 > 0 )
{ while ( exp10 > num_powers_10 )
dval *= powers_10[num_powers_10],
exp10 -= num_powers_10;
if ( exp10 > 0 )
dval *= powers_10[exp10];
}
else if ( exp10 < 0 )
{ while ( exp10 < -num_powers_10 )
dval /= powers_10[num_powers_10],
exp10 += num_powers_10;
if ( exp10 < 0 )
dval /= powers_10[-exp10];
}
make_real(pref, (float)dval);
return 0;
}
/* Internal subroutine to scan an integer. */
/* Return 0, e_limitcheck, or e_syntaxerror. */
/* (The only syntax error is no digits encountered.) */
/* If the integer won't fit in a long, then: */
/* if pdval == NULL, return e_limitcheck; */
/* if pdval != NULL, return 1 and store a double value in *pdval. */
/* Put back the terminating character. */
private int
scan_int(register stream *s, int radix, long *pval, double *pdval)
{ int ival = 0, imax, irem;
#if ints_are_short
long lval, lmax;
int lrem;
#else
# define lval ival /* for overflowing into double */
#endif
double dval;
register int c, d;
register byte *decoder = scan_char_decoder;
/* Avoid the long divisions when radix = 10 */
#define set_max(vmax, vrem, big)\
if ( radix == 10 ) vmax = (big) / 10, vrem = (big) % 10;\
else vmax = (big) / radix, vrem = (big) % radix
set_max(imax, irem, max_uint >> 1);
#define convert_digit_fails(c, d)\
(d = decoder[c]) >= radix
while ( 1 )
{ c = sgetc(s);
if ( convert_digit_fails(c, d) )
{ if ( c != EOFC ) sputback(s);
*pval = ival;
return 0;
}
if ( ival >= imax && (ival > imax || d > irem) )
break; /* overflow */
ival = ival * radix + d;
}
#if ints_are_short
/* Short integer overflowed. Accumulate in a long. */
lval = (long)ival * radix + d;
set_max(lmax, lrem, max_ulong >> 1);
while ( 1 )
{ c = sgetc(s);
if ( convert_digit_fails(c, d) )
{ if ( c != EOFC ) sputback(s);
*pval = lval;
return 0;
}
if ( lval >= lmax && (lval > lmax || d > lrem) )
break; /* overflow */
lval = lval * radix + d;
}
#endif
/* Integer overflowed. Accumulate the result as a double. */
if ( pdval == NULL ) return e_limitcheck;
dval = (double)lval * radix + d;
while ( 1 )
{ c = sgetc(s);
if ( convert_digit_fails(c, d) )
{ if ( c != EOFC ) sputback(s);
*pdval = dval;
return 1;
}
dval = dval * radix + d;
}
/* Control doesn't get here */
}
/* Make a string */
private int
mk_string(ref *pref, dynamic_area *pda)
{ uint size = pda->next - pda->base;
byte *body = alloc_shrink(pda->base, pda->num_elts, size, 1, "scanner(string)");
if ( body == 0 ) return e_VMerror;
make_tasv(pref, t_string, a_all, size, bytes, body);
return 0;
}
/* Internal procedure to scan a string. */
private int
scan_string(register stream *s, int from_string, ref *pref)
{ dynamic_area da;
register int c;
register byte *ptr = dynamic_begin(&da, 100, 1);
int plevel = 0;
if ( ptr == 0 ) return e_VMerror;
top: while ( 1 )
{ c = sgetc(s);
if ( c == EOFC ) return e_syntaxerror;
else if ( c == '(' ) plevel++;
else if ( c == ')' ) { if ( --plevel < 0 ) break; }
else if ( c == '\\' && !from_string )
{ c = sgetc(s);
switch ( c )
{
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case '\n': goto top; /* ignore */
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
{ int d = sgetc(s);
c -= '0';
if ( d >= '0' && d <= '7' )
{ c = (c << 3) + d - '0';
d = sgetc(s);
if ( d >= '0' && d <= '7' )
{ c = (c << 3) + d - '0';
break;
}
}
if ( d == EOFC ) return e_syntaxerror;
sputback(s);
}
break;
default: ; /* ignore the \ */
}
}
if ( ptr == da.limit )
{ da.next = ptr;
if ( !dynamic_grow(&da) )
return e_VMerror;
ptr = da.next;
}
*ptr++ = c;
}
da.next = ptr;
return mk_string(pref, &da);
}
/* Internal procedure to scan a hex string. */
private int
scan_hex_string(stream *s, ref *pref)
{ dynamic_area da;
int c1, c2, val1, val2;
byte *ptr = dynamic_begin(&da, 100, 1);
register byte *decoder = scan_char_decoder;
if ( ptr == 0 ) return e_VMerror;
l1: do
{ c1 = sgetc(s);
if ( (val1 = decoder[c1]) < 0x10 )
{ do
{ c2 = sgetc(s);
if ( (val2 = decoder[c2]) < 0x10 )
{ if ( ptr == da.limit )
{ da.next = ptr;
if ( !dynamic_grow(&da) )
return e_VMerror;
ptr = da.next;
}
*ptr++ = (val1 << 4) + val2;
goto l1;
}
}
while ( val2 == ctype_space );
if ( c2 != '>' ) return e_syntaxerror;
if ( ptr == da.limit )
{ da.next = ptr;
if ( !dynamic_grow(&da) )
return e_VMerror;
ptr = da.next;
}
*ptr++ = val1 << 4; /* no 2nd char */
goto lx;
}
}
while ( val1 == ctype_space );
if ( c1 != '>' ) return e_syntaxerror;
lx: da.next = ptr;
return mk_string(pref, &da);
}