/* * @(#)PostScript.h 9.5 88/01/19 */ /*- * PostScript.h: Definitions for the Postscript interpreter. * * (C) Copyright 1987 Sun Microsystems, Inc. * */ #include "cscript.h" #ifndef cs_movecursor #include "cursor.h" #endif #ifndef PSFILE #include "psio.h" #endif #define VERSION "1.1" /* The current server version */ /* A small utility macro to aid in tricking the C preprocessor: EXPAND(fu)bar becomes fubar */ #define EXPAND(c) c #define CPPCONCAT(a,b) EXPAND(a)b /* All the possible Postscript data types */ enum type { null_type, fixed_type, real_type, boolean_type, cons_type, color_type, marker_type, operator_type, called_operator_type, magic_variable_type, keyword_type, string_type, array_type, dictionary_type, font_id_type, event_type, file_type, canvas_type, process_type, shape_type, monitor_type, graphicsstate_type, cursor_type, }; /* A postscript object. These are the things that get stuck into other structures -- they don't contain the bodies of strings or arrays. Objects are never reference counted, they just get copied. The bodies they point to do have reference counts. */ struct object { /* * The next four fields are very carefully laid out to take up exactly * 32 bits. Their bit positions within the word are also very * carefully set. */ #ifndef LITTLEENDIAN /* For big-endian machines like the 68K */ enum type type:6; /* All objects have a type field in the * top five bits of the first word */ unsigned offset:24; /* If this object has a body part, then * "offset<<2" points to it. It is * positioned here in the structure so * that it can be unpacked with no * shifting. This implies that all body * parts must be allocated on a mod 4 * boundary */ unsigned executable:1; unsigned pad:1; #else /* For little-endian machines line the 80386 and the VAX we reverse the order of the bitfields so that the record punning still works. */ unsigned pad:1; unsigned executable:1; unsigned offset:24; /* If this object has a body part, then * "offset<<2" points to it. It is * positioned here in the structure so * that it can be unpacked with no * shifting. This implies that all body * parts must be allocated on a mod 4 * boundary */ enum type type:6; /* All objects have a type field in the * top five bits of the first word */ #endif union { int fixed; /* type == fixed_type, boolean_type */ float real; /* == real_type */ struct operator_def *def;/* == operator_type */ struct { /* == string_type */ unsigned short start, length; } substring; struct { /* == array_type */ unsigned short start, length; } subarray; PSFILE *Yfile; /* type == file_type */ struct pixrect *canvas; /* type == canvas_type */ struct { /* type == cons_type */ unsigned refcnt:8; unsigned cdr:24; } cons; struct shape *shape; /* type == shape_type */ struct object *next; struct color color; /* type == color_type */ } value; }; #define type_shift 26 #define ValidObjectBits (~3) #define object_word0(p) *((int *)(p)) #define object_word1(p) *(((int *)(p))+1) #define object_has_body(p) ((unsigned) (p)->type >= (unsigned) keyword_type) #define object_has_refcnt(p) ((unsigned) (p)->type >= (unsigned) string_type) #ifdef PROCEDURIZE_HASH #define hash_object(hash, obj) ((hash) = hash_object_proc(obj)) #else /* PROCEDURIZE_HASH */ #define hash_object(hash,obj) ((hash) = ((object_word0(obj) & ValidObjectBits) \ ^ object_word1(obj)), \ (hash) = (hash) ^ ((hash)>>16)) #define hash_index(hash,mod) ((((unsigned short) hash) % ((unsigned short) mod))*2) #endif /* PROCEDURIZE_HASH */ #define set_fixed_object(p,n) ((object_word0(p) = ((unsigned) fixed_type)<value.fixed = (n)) #define set_boolean_object(p,n) ((object_word0(p) = ((unsigned) boolean_type)<value.fixed = (n)) #define set_typed_object(p, type) (object_word0(p) = ((unsigned) (type))<cons.cdr = (int) (b)) #define l_car(p) ((struct object *) (object_word0(p)&BodyPointerMask) #define l_cdr(p) ((struct object *) (p)->cdr) #define l_incref(p) ((p)->refcnt != 0377 ? (p)->refcnt++ : 0) #define l_decref(p) ((p)->refcnt != 0377 && --(p)->refcnt == 0 ? (p)->next = free_cons, free_cons = (p) : 0) #define l_newcons(p) (free_cons ? p = free_cons, free_cons = p->next : p = new_cons_bag()) /* The kinds of match an interest-event's name or action may require */ #define DirectMatch 0 #define SimpleAnyMatch 1 #define DictAnyMatch 2 /* The body part assigned to an object */ struct body { enum type type:8; /* This type corresponds to the type in the objects that refer to this body */ unsigned int refcnt:8; /* The number of of objects that refer to this body */ unsigned readonly:1; /* True iff this object is read only */ union { struct { /* == keyword_type */ char *name; /* The name of this keyword */ int hash; /* Its hashed value */ struct body *same_bucket; /* The next keyword that hashed into the same bucket */ unsigned short namelen; /* length in bytes of the name */ #ifdef undef int system_only:8; /* true iff it only appears in the system dictionary */ unsigned hardcore:8;/* its hardcore index [hardcore procs are implemented in C and are executed very quickly] */ int dict_mask; /* mask indicating the set of dictionaries that this keyword is in */ short last_pos; /* The position in the dictionary where this keyword was last found */ #endif } keyword; /* Growable types: array, dict, string. Fields should be identical */ struct { /* Growable type: array */ unsigned short size; /* Actual slots allocated are 0..size-1 */ unsigned short used; /* Actual valid substripts are 0..used-1 */ unsigned short max_size;/* Maximum array size */ unsigned short max_use; /* Maximum used before growing array */ struct object *objects; #ifdef PERFORMANCE unsigned exec_count; /* Number of times executed */ #endif /* PERFORMANCE */ } array; struct { /* Growable type: dict */ unsigned short size; /* Actual slots allocated are 0..size-1 */ unsigned short used; /* Actual valid substripts are 0..used-1 */ unsigned short max_size;/* Maximum array size */ unsigned short max_use; /* Maximum used before growing array */ struct object *objects; /* Change this someday to be KV_pairs */ } dict; struct { /* Growable type: string */ unsigned short size; /* Actual slots allocated are 0..size-1 */ unsigned short used; /* Actual valid substripts are 0..used-1 */ unsigned short max_size;/* Maximum array size */ unsigned short max_use; /* Maximum used before growing array */ char *chars; } string; struct { PSFILE *inbuf; /* The input side of this IO channel */ PSFILE *outbuf; /* The output side of this IO channel */ struct body *name; /* The name of the file */ struct object *tok; /* Compressed tokens declared for this file */ short ntok; /* The number of such tokens */ } file; struct { /* type == canvas_type */ struct pixrect *canvas; struct body *interest; } canvas; struct shape *shape; /* type == shape_type */ struct { struct execution_environment *env; struct object terminal_value; } process; struct { /* type == event_type */ struct object name; struct object action; struct object client_data; struct object runnable_match; struct canvas *canvas; struct execution_environment *process; /* process interested in this event, or intended recipient */ struct body *interest; /* interest matched by this event */ struct body *keystate; /* array of down keys */ struct body *next; /* next event in queue */ struct body *next_int; /* The next interest for this process */ struct fpoint pos; /* locator position */ unsigned serial; /* event #, or interest's last match */ fract time; /* minutes since startup */ fract priority; /* applies only to interests */ unsigned is_interest:1; /* This event is in an interest list */ unsigned is_exclusive:1; /* This interest gobbles events it matches */ unsigned is_queued:1; /* This interest is in the eventq */ unsigned int name_match:2; /* was /AnyValue specified in an */ unsigned int action_match:2; /* interest name or action, & how */ } event; struct { struct execution_environment *first, *last, *holder; short reentry_depth; /* Number of times the current holder has entered the same monitor */ } monitor; struct psfont *font; /* type == fontid_type */ struct graphics_context graphicsstate; struct cursor_shape cursor; } body; }; /* Macros for dealing with reference counts */ #define maximum_refcnt 0377 /* The maximum value that a refcnt can have */ #ifdef undef #define incref(p) ((p)->refcntrefcnt++ : 0) #define decref(p) ((p)->refcntrefcnt<=0 ? free_body(p) : 0) #define object_incref(p) (object_has_refcnt(p) ? incref(body_of(p)) : 0) #define object_decref(p) (object_has_refcnt(p) ? decref(body_of(p)) : 0) #else #define incref(p) { \ if ((p)->refcnt!=maximum_refcnt) (p)->refcnt++; }; #define object_incref(p) { \ if(object_has_refcnt(p)) { \ register struct body *bdp = body_of(p); \ incref(bdp); }}; #define decref(p) { \ if ((p)->refcnt!=maximum_refcnt && (unsigned char)(--(p)->refcnt)==0) \ free_body(p); }; #define object_decref(p) { \ if(object_has_refcnt(p)) { \ register struct body *bdp = body_of(p); \ decref(bdp); }}; #endif struct operator_def { /* The definition descriptor of a built-in * operator */ int index; /* The magic number for this operator (it's irrelevant for called operators) */ char *pname; /* Its print name */ short args_used; /* The minimum number of arguments that * will be used */ short growth; /* The minimum amount by which this * operator will cause the stack to grow * (if it grows by more than this amount, * the operator will have to check for * stack overflow itself) */ int (*function) (); /* The function implementing this operator */ }; /* define_operator is called to define a new intrinsic operator */ #define define_operator(name, function, index, args, results) { \ static struct operator_def d = {index, name, args, results - args, function}; \ set_body_of(&define_operator_name, get_name(d.pname, -1)); \ define_operator_obj.value.def = &d; \ define_object_in_dictionary(define_operator_name, define_operator_obj, system_dictionary, 0); \ } #define define_magic(name, dict, function, index, args, results) { \ static struct operator_def d = {index, name, args, results - args, function}; \ set_body_of(&define_operator_name, get_name(d.pname, -1)); \ magic_variable_obj.value.def = &d; \ define_object_in_dictionary(define_operator_name, magic_variable_obj, dict, 0); \ } #define define_object(dict, name, object) { \ set_body_of(&define_operator_name, get_name(name, -1)); \ define_object_in_dictionary(define_operator_name, object, dict, 0); \ } extern struct object define_operator_name, define_operator_obj, magic_variable_obj; /* Next three macros used to call get/set functions for magic variables */ #define GET_MAGIC(ix) (((ix)<<1)|0) #define SET_MAGIC(ix) (((ix)<<1)|1) #define IS_SET_MAGIC(i) (i&1) extern struct object *free_cons;/* Free list of cons cells */ extern struct pixrect *empty_pixrect; extern struct body *system_dictionary; extern struct body *stdio_file; extern struct body *dict_table[];/* Maps types to corresponding "magic" dictionaries */ extern struct body *type_table[];/* type to name mapping table */ extern char delim[]; /* A table indicating which characters are delimiters */ extern char translate_digit[]; /* A table which maps characters to their value as a digit */ #define hash_table_size 1023 /* A moderately large prime */ extern struct body *hash_table[]; #define BACKSLASH '\\' char *decode_body (/* p */); struct body *alloc_body (/* size */); #define body_size(t) (((int)&((struct body *)0)->body) \ + sizeof ((struct body *)0)->body.t) #define new_body(t) alloc_body(body_size(t)) struct object make_growable_object( /* type, where, numelements */ ); #define make_array(n) make_growable_object(array_type, 0, (n)) #define make_string(n, str) make_growable_object(string_type, (str), (n)) struct body *new_growable_body( /* type, numelements */ ); #define new_array_body(n) new_growable_body(array_type, (n)) #define new_string_body(n) new_growable_body(string_type, (n)) #define new_dict_body(n) new_growable_body(dictionary_type, (n)) #define new_dict(n) new_growable_body(dictionary_type, (n)) /* XXX Next two defs are obsolete and should be removed soon */ /* #define make_array(t, w, n) make_growable_object((t), (w), (n))*/ /* #define make_array_body(t, w, n) make_growable_body((t), (w), (n))*/ struct body *new_name (/* hash, name, length */); struct body *get_name(/* name, length */); enum error_type define_object_in_dictionary(/* key, value, dict, real_obj */); struct object *find_object_in_dictionary (/* key, dict */); struct object parse_file (/* f */); struct object parse_string (/* str */); struct object open_file (/* fn, len, dir */); struct execution_environment *create_process(); struct object *result_matrix(/*matrix object*/); struct object *arg_matrix(/*matrix object*/); /* The execution stack contains all information needed to run the interpreter at each level of the call stack: it has return information & indications of the type of operation in progress (eg. `for' and `repeat' statements) */ enum execution_type { array_body_execution, file_execution, fixed_for_execution, real_for_execution, stopped_execution, forall_execution, pathforall_execution, undetermined_execution, monitor_execution, buildimage_execution, token_execution, send_execution, }; enum error_type { no_error_code, accept_error_code, dictfull_error_code, dictstackoverflow_error_code, dictstackunderflow_error_code, execstackoverflow_error_code, interrupt_error_code, invalidaccess_error_code, invalidexit_error_code, invalidfileaccess_error_code, invalidfont_error_code, invalidrestore_error_code, ioerror_error_code, limitcheck_error_code, nocurrentpoint_error_code, rangecheck_error_code, stackoverflow_error_code, stackunderflow_error_code, syntaxerror_error_code, timeout_error_code, typecheck_error_code, undefined_error_code, undefinedfilename_error_code, undefinedresult_error_code, unimplemented_error_code, unmatchedmark_error_code, unregistered_error_code, VMerror_error_code, killprocess_error_code, }; extern char *error_names[]; /* Printable versions of the error codes */ struct execution_stack { enum execution_type type:16; struct object executed; /* The object being executed */ union { struct { /* An array object being executed */ int left; struct object *ip; } array; struct { /* A file or string being executed */ PSFILE *file; } file; struct { /* fixed for */ fract initial, increment, limit; short stackpos; } ifor; struct { /* real for */ float initial, increment, limit; } rfor; struct { int pos; int end; int bottom; fract lastop; } pathforall; struct object forall; struct object monitor; struct { struct pixrect *image; short remrow, first; int remaining; int pos; } image; struct { short bottomdict, topdict; } send; } env; }; /* An entry in a dictionary stack */ struct dictstack_ent { struct body *body; /* The actual dictionary */ struct body *obj; /* non-zero if the dictionary is really a front for a non-dictionary object */ }; /* The environment in which PostScript executes. It's the context for a single thread of control. Supposedly PostScript() should be able to switch between execution environments to effect timeslicing [eg. for when errors occur] */ struct execution_environment { struct execution_environment *next, /* The next process in the set of * runable processes */ *pgnext, /* A doubly linked list of processes in a process group */ *pgprev; enum error_type error_code; /* the error that is currently being * processed */ char *error_detail; /* More detailed description of what exactly happened */ struct body *stdprnt; /* The standard print file for this execution_environment -- this is where output usually goes */ struct body *stddiag; /* Where diagnostics usually go */ short restart_state; /* the state in which the interpreter * should be restarted when its time to * start interpreting this environment */ char autobind; /* true=>keywords that correspond to systemdict names will be automatically bound to their definition by the reader */ char winevents; /* 0=>no window events enabled, 1=>window events enabled in current window, 2=>could be events in many windows */ short event; /* The event being waited for, >0 => that * it is a file id */ char seeking_token; /* True iff the token primitive is being executed */ char detail_desired; /* The amount of error reporting detail * desired in the $error directory */ struct object last_executed;/* The last thing executed in this context * before the error */ char *static_buffer; /* A scratch buffer for the parser */ int bufsize; /* The size of the parsers buffer */ int fill; /* The parse buffer fill pointer */ int ofill; /* The old fill pointer: the start of the * current name or string */ int arrstart; /* The start of the current {} construct */ struct object execee; /* The thing currently being executed */ struct graphics_context gontext; struct body *operand_stack; struct dictstack_ent *dictionary_stack, *dict_top, /* The top valid entry in dictstack */ *dict_limit; /* The first non-allocated entry in dictstack */ struct object *underflow, /* The bottom of the operand stack */ *overflow, /* The limit of the operand stack */ *optop; /* The top of the operand stack -- optop[-1] is the top element, optop[-2] is the one below it... */ struct execution_stack *execution_stack, *limit, *pos; struct body *body_handle; /* The process id variable accessible by PostScript routines */ struct body *interests; /* The events that are interesting to this process */ struct body *eq_head, /* the queue of events waiting to be */ *eq_tail; /* processed by this process */ }; /* Ensure that the dictionary stack of process "ee" has room for "n" new entries; fails only if we're running out of memory */ #define have_dict_space(ee, n) \ ((ee)->dict_top+(n) >= (ee)->dict_limit ? enlarge_dict_space(ee, n) : 1) /* The various events that a process can be waiting for and states that it can be in */ #define fileio_event_wait(fd) ((fd)+1) #define runnable_process 0 #define input_event_wait -1 /* some sort of button change or locator motion */ #define dead_process -2 /* dead, and its carcass is reusable */ #define zombie_process -3 /* dead, but some process id handle is outstanding, so the carcass is being kept around until the PostScript program uses it */ #define process_activity_wait -4 #define suspended_process -5 /* A process which has stopped, normally for debugging */ #define monitor_wait -6 /* Waiting for a monitor to become available */ extern struct execution_environment *process_free_list;/* process carcasses that are waiting to have new life breathed into them */ extern struct execution_environment *current_process;/* The currently executing process */ extern struct execution_environment *waiting_procs;/* The set of processes waiting for activity in another process */ extern struct execution_environment *process_set;/* The PostScript run Q */ extern int process_population; /* The number of processes that currently exist */ /* The set of processes waiting for a read to complete. Indexed by file ID */ extern struct execution_environment *process_readers[]; extern struct object syscommon_vec[];/* The set of globally defined compressed input tokens */ extern short fract_overflows; /* Incremented by vfract primitives whenever an operation overflows */ extern int errno; /* Unix system call error number */ /* NOT USED ??? struct font *default_font; */ extern int verbose; /* true iff PostScript should print out various tidbits about what it is currently up to */ #define object_type_pair(o1,o2) ((((int) (o1).type)<<6)+(int)(o2).type) #define body_type_pair(b1,b2) ((((int) (b1)->type)<<6)+(int)(b2)->type) #define tp(t1,t2) ((((int) EXPAND(t1)_type)<<6)+(int)EXPAND(t2)_type) /* Temporary hack to use X-style initialization */ #define pr_open(x) private_pr_open(x) #define TemporaryInitHack /* For masks that can be greater than 32 bits: */ extern void add_selectable_file( /* fd, read_flag */ ); extern void remove_selectable_file( /* fd, read_flag */ ); extern int maximum_fd; /* 1 + the highest interesting fd */