xref: /plan9-contrib/sys/src/cmd/gs/src/zfunc4.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
13ff48bf5SDavid du Colombier /* Copyright (C) 1999, 2000 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: zfunc4.c,v 1.12 2004/08/04 19:36:13 stefan Exp $ */
187dd7cddfSDavid du Colombier /* PostScript language support for FunctionType 4 (PS Calculator) Functions */
197dd7cddfSDavid du Colombier #include "memory_.h"
207dd7cddfSDavid du Colombier #include "ghost.h"
217dd7cddfSDavid du Colombier #include "oper.h"
227dd7cddfSDavid du Colombier #include "opextern.h"
237dd7cddfSDavid du Colombier #include "gsfunc.h"
243ff48bf5SDavid du Colombier #include "gsfunc4.h"
253ff48bf5SDavid du Colombier #include "gsutil.h"
267dd7cddfSDavid du Colombier #include "idict.h"
277dd7cddfSDavid du Colombier #include "ifunc.h"
283ff48bf5SDavid du Colombier #include "iname.h"
293ff48bf5SDavid du Colombier #include "dstack.h"
30*593dc095SDavid du Colombier #include "ialloc.h"
317dd7cddfSDavid du Colombier /*
323ff48bf5SDavid du Colombier  * FunctionType 4 functions are not defined in the PostScript language.  We
333ff48bf5SDavid du Colombier  * provide support for them because they are needed for PDF 1.3.  In
343ff48bf5SDavid du Colombier  * addition to the standard FunctionType, Domain, and Range keys, they have
353ff48bf5SDavid du Colombier  * a Function key whose value is a procedure in a restricted subset of the
363ff48bf5SDavid du Colombier  * PostScript language.  Specifically, the procedure must (recursively)
373ff48bf5SDavid du Colombier  * contain only integer, real, Boolean, and procedure constants (only as
383ff48bf5SDavid du Colombier  * literal operands of if and and ifelse), and operators chosen from the set
393ff48bf5SDavid du Colombier  * given below.  Note that names other than true and false are not allowed:
403ff48bf5SDavid du Colombier  * the procedure must be 'bound'.
417dd7cddfSDavid du Colombier  *
427dd7cddfSDavid du Colombier  * The following list is taken directly from the PDF 1.3 documentation.
437dd7cddfSDavid du Colombier  */
44*593dc095SDavid du Colombier #define XOP(zfn) int zfn(i_ctx_t *)
457dd7cddfSDavid du Colombier XOP(zabs); XOP(zand); XOP(zatan); XOP(zbitshift);
467dd7cddfSDavid du Colombier XOP(zceiling); XOP(zcos); XOP(zcvi); XOP(zcvr);
477dd7cddfSDavid du Colombier XOP(zdiv); XOP(zexp); XOP(zfloor); XOP(zidiv);
487dd7cddfSDavid du Colombier XOP(zln); XOP(zlog); XOP(zmod); XOP(zmul);
497dd7cddfSDavid du Colombier XOP(zneg); XOP(znot); XOP(zor); XOP(zround);
507dd7cddfSDavid du Colombier XOP(zsin); XOP(zsqrt); XOP(ztruncate); XOP(zxor);
517dd7cddfSDavid du Colombier XOP(zeq); XOP(zge); XOP(zgt); XOP(zle); XOP(zlt); XOP(zne);
523ff48bf5SDavid du Colombier XOP(z2copy);
537dd7cddfSDavid du Colombier #undef XOP
543ff48bf5SDavid du Colombier typedef struct calc_op_s {
553ff48bf5SDavid du Colombier     op_proc_t proc;
563ff48bf5SDavid du Colombier     gs_PtCr_opcode_t opcode;
573ff48bf5SDavid du Colombier } calc_op_t;
583ff48bf5SDavid du Colombier static const calc_op_t calc_ops[] = {
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier     /* Arithmetic operators */
617dd7cddfSDavid du Colombier 
623ff48bf5SDavid du Colombier     {zabs, PtCr_abs},
633ff48bf5SDavid du Colombier     {zadd, PtCr_add},
643ff48bf5SDavid du Colombier     {zand, PtCr_and},
653ff48bf5SDavid du Colombier     {zatan, PtCr_atan},
663ff48bf5SDavid du Colombier     {zbitshift, PtCr_bitshift},
673ff48bf5SDavid du Colombier     {zceiling, PtCr_ceiling},
683ff48bf5SDavid du Colombier     {zcos, PtCr_cos},
693ff48bf5SDavid du Colombier     {zcvi, PtCr_cvi},
703ff48bf5SDavid du Colombier     {zcvr, PtCr_cvr},
713ff48bf5SDavid du Colombier     {zdiv, PtCr_div},
723ff48bf5SDavid du Colombier     {zexp, PtCr_exp},
733ff48bf5SDavid du Colombier     {zfloor, PtCr_floor},
743ff48bf5SDavid du Colombier     {zidiv, PtCr_idiv},
753ff48bf5SDavid du Colombier     {zln, PtCr_ln},
763ff48bf5SDavid du Colombier     {zlog, PtCr_log},
773ff48bf5SDavid du Colombier     {zmod, PtCr_mod},
783ff48bf5SDavid du Colombier     {zmul, PtCr_mul},
793ff48bf5SDavid du Colombier     {zneg, PtCr_neg},
803ff48bf5SDavid du Colombier     {znot, PtCr_not},
813ff48bf5SDavid du Colombier     {zor, PtCr_or},
823ff48bf5SDavid du Colombier     {zround, PtCr_round},
833ff48bf5SDavid du Colombier     {zsin, PtCr_sin},
843ff48bf5SDavid du Colombier     {zsqrt, PtCr_sqrt},
853ff48bf5SDavid du Colombier     {zsub, PtCr_sub},
863ff48bf5SDavid du Colombier     {ztruncate, PtCr_truncate},
873ff48bf5SDavid du Colombier     {zxor, PtCr_xor},
883ff48bf5SDavid du Colombier 
893ff48bf5SDavid du Colombier     /* Comparison operators */
903ff48bf5SDavid du Colombier 
913ff48bf5SDavid du Colombier     {zeq, PtCr_eq},
923ff48bf5SDavid du Colombier     {zge, PtCr_ge},
933ff48bf5SDavid du Colombier     {zgt, PtCr_gt},
943ff48bf5SDavid du Colombier     {zle, PtCr_le},
953ff48bf5SDavid du Colombier     {zlt, PtCr_lt},
963ff48bf5SDavid du Colombier     {zne, PtCr_ne},
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier     /* Stack operators */
993ff48bf5SDavid du Colombier 
1003ff48bf5SDavid du Colombier     {zcopy, PtCr_copy},
1013ff48bf5SDavid du Colombier     {z2copy, PtCr_copy},
1023ff48bf5SDavid du Colombier     {zdup, PtCr_dup},
1033ff48bf5SDavid du Colombier     {zexch, PtCr_exch},
1043ff48bf5SDavid du Colombier     {zindex, PtCr_index},
1053ff48bf5SDavid du Colombier     {zpop, PtCr_pop},
1063ff48bf5SDavid du Colombier     {zroll, PtCr_roll}
1073ff48bf5SDavid du Colombier 
1083ff48bf5SDavid du Colombier     /* Special operators */
1093ff48bf5SDavid du Colombier 
1103ff48bf5SDavid du Colombier     /*{zif, PtCr_if},*/
1113ff48bf5SDavid du Colombier     /*{zifelse, PtCr_ifelse},*/
1123ff48bf5SDavid du Colombier     /*{ztrue, PtCr_true},*/
1133ff48bf5SDavid du Colombier     /*{zfalse, PtCr_false}*/
1147dd7cddfSDavid du Colombier };
1157dd7cddfSDavid du Colombier 
1163ff48bf5SDavid du Colombier /* Fix up an if or ifelse forward reference. */
1173ff48bf5SDavid du Colombier private void
psc_fixup(byte * p,byte * to)1183ff48bf5SDavid du Colombier psc_fixup(byte *p, byte *to)
1199a747e4fSDavid du Colombier {
1203ff48bf5SDavid du Colombier     int skip = to - (p + 3);
1219a747e4fSDavid du Colombier 
1223ff48bf5SDavid du Colombier     p[1] = (byte)(skip >> 8);
1233ff48bf5SDavid du Colombier     p[2] = (byte)skip;
1247dd7cddfSDavid du Colombier }
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier /*
1273ff48bf5SDavid du Colombier  * Check a calculator function for validity, optionally storing its encoded
1283ff48bf5SDavid du Colombier  * representation and add the size of the encoded representation to *psize.
1293ff48bf5SDavid du Colombier  * Note that we arbitrarily limit the depth of procedure nesting.  pref is
1303ff48bf5SDavid du Colombier  * known to be a procedure.
1317dd7cddfSDavid du Colombier  */
1323ff48bf5SDavid du Colombier #define MAX_PSC_FUNCTION_NESTING 10
1337dd7cddfSDavid du Colombier private int
check_psc_function(i_ctx_t * i_ctx_p,const ref * pref,int depth,byte * ops,int * psize)1343ff48bf5SDavid du Colombier check_psc_function(i_ctx_t *i_ctx_p, const ref *pref, int depth, byte *ops, int *psize)
1357dd7cddfSDavid du Colombier {
1367dd7cddfSDavid du Colombier     long i;
1373ff48bf5SDavid du Colombier     uint size = r_size(pref);
1387dd7cddfSDavid du Colombier 
1393ff48bf5SDavid du Colombier     for (i = 0; i < size; ++i) {
1403ff48bf5SDavid du Colombier 	byte no_ops[1 + max(sizeof(int), sizeof(float))];
1413ff48bf5SDavid du Colombier 	byte *p = (ops ? ops + *psize : no_ops);
1423ff48bf5SDavid du Colombier 	ref elt, elt2, elt3;
1433ff48bf5SDavid du Colombier 	ref * delp;
1449a747e4fSDavid du Colombier 	int code;
1459a747e4fSDavid du Colombier 
146*593dc095SDavid du Colombier 	array_get(imemory, pref, i, &elt);
1473ff48bf5SDavid du Colombier 	switch (r_btype(&elt)) {
1483ff48bf5SDavid du Colombier 	case t_integer: {
1493ff48bf5SDavid du Colombier 	    int i = elt.value.intval;
1503ff48bf5SDavid du Colombier 
1513ff48bf5SDavid du Colombier #if ARCH_SIZEOF_INT < ARCH_SIZEOF_LONG
1523ff48bf5SDavid du Colombier 	    if (i != elt.value.intval) /* check for truncation */
1533ff48bf5SDavid du Colombier 		return_error(e_rangecheck);
1543ff48bf5SDavid du Colombier #endif
1553ff48bf5SDavid du Colombier 	    if (i == (byte)i) {
1563ff48bf5SDavid du Colombier 		*p = PtCr_byte;
1573ff48bf5SDavid du Colombier 		p[1] = (byte)i;
1583ff48bf5SDavid du Colombier 		*psize += 2;
1593ff48bf5SDavid du Colombier 	    } else {
1603ff48bf5SDavid du Colombier 		*p = PtCr_int;
1613ff48bf5SDavid du Colombier 		memcpy(p + 1, &i, sizeof(i));
1623ff48bf5SDavid du Colombier 		*psize += 1 + sizeof(int);
1633ff48bf5SDavid du Colombier 	    }
1643ff48bf5SDavid du Colombier 	    break;
1653ff48bf5SDavid du Colombier 	}
1663ff48bf5SDavid du Colombier 	case t_real: {
1673ff48bf5SDavid du Colombier 	    float f = elt.value.realval;
1683ff48bf5SDavid du Colombier 
1693ff48bf5SDavid du Colombier 	    *p = PtCr_float;
1703ff48bf5SDavid du Colombier 	    memcpy(p + 1, &f, sizeof(f));
1713ff48bf5SDavid du Colombier 	    *psize += 1 + sizeof(float);
1723ff48bf5SDavid du Colombier 	    break;
1733ff48bf5SDavid du Colombier 	}
1743ff48bf5SDavid du Colombier 	case t_boolean:
1753ff48bf5SDavid du Colombier 	    *p = (elt.value.boolval ? PtCr_true : PtCr_false);
1763ff48bf5SDavid du Colombier 	    ++*psize;
1773ff48bf5SDavid du Colombier 	    break;
1783ff48bf5SDavid du Colombier 	case t_name:
1793ff48bf5SDavid du Colombier 	    if (!r_has_attr(&elt, a_executable))
1803ff48bf5SDavid du Colombier 		return_error(e_rangecheck);
181*593dc095SDavid du Colombier 	    name_string_ref(imemory, &elt, &elt);
1823ff48bf5SDavid du Colombier 	    if (!bytes_compare(elt.value.bytes, r_size(&elt),
1833ff48bf5SDavid du Colombier 			       (const byte *)"true", 4)) {
1843ff48bf5SDavid du Colombier 		*p = PtCr_true;
1853ff48bf5SDavid du Colombier 	        ++*psize;
1863ff48bf5SDavid du Colombier 	        break;
1873ff48bf5SDavid du Colombier 	    }
1883ff48bf5SDavid du Colombier 	    if (!bytes_compare(elt.value.bytes, r_size(&elt),
1893ff48bf5SDavid du Colombier 				      (const byte *)"false", 5)) {
1903ff48bf5SDavid du Colombier 		*p = PtCr_false;
1913ff48bf5SDavid du Colombier 	        ++*psize;
1923ff48bf5SDavid du Colombier 	        break;
1933ff48bf5SDavid du Colombier 	    }
1943ff48bf5SDavid du Colombier 	    /* Check if the name is a valid operator in systemdict */
1953ff48bf5SDavid du Colombier 	    if (dict_find(systemdict, &elt, &delp) <= 0)
1963ff48bf5SDavid du Colombier 		return_error(e_undefined);
1973ff48bf5SDavid du Colombier 	    if (r_btype(delp) != t_operator)
1983ff48bf5SDavid du Colombier 		return_error(e_typecheck);
1993ff48bf5SDavid du Colombier 	    if (!r_has_attr(delp, a_executable))
2003ff48bf5SDavid du Colombier 		return_error(e_rangecheck);
2013ff48bf5SDavid du Colombier 	    elt = *delp;
2023ff48bf5SDavid du Colombier 	    /* Fall into the operator case */
2033ff48bf5SDavid du Colombier 	case t_operator: {
2043ff48bf5SDavid du Colombier 	    int j;
2053ff48bf5SDavid du Colombier 
2063ff48bf5SDavid du Colombier 	    for (j = 0; j < countof(calc_ops); ++j)
2073ff48bf5SDavid du Colombier 		if (elt.value.opproc == calc_ops[j].proc) {
2083ff48bf5SDavid du Colombier 		    *p = calc_ops[j].opcode;
2093ff48bf5SDavid du Colombier 		    ++*psize;
2103ff48bf5SDavid du Colombier 		    goto next;
2113ff48bf5SDavid du Colombier 		}
2123ff48bf5SDavid du Colombier 	    return_error(e_rangecheck);
2133ff48bf5SDavid du Colombier 	}
2143ff48bf5SDavid du Colombier 	default: {
2153ff48bf5SDavid du Colombier 	    if (!r_is_proc(&elt))
2163ff48bf5SDavid du Colombier 		return_error(e_typecheck);
2173ff48bf5SDavid du Colombier 	    if (depth == MAX_PSC_FUNCTION_NESTING)
2183ff48bf5SDavid du Colombier 		return_error(e_limitcheck);
219*593dc095SDavid du Colombier 	    if ((code = array_get(imemory, pref, ++i, &elt2)) < 0)
2203ff48bf5SDavid du Colombier 		return code;
2213ff48bf5SDavid du Colombier 	    *psize += 3;
2223ff48bf5SDavid du Colombier 	    code = check_psc_function(i_ctx_p, &elt, depth + 1, ops, psize);
2237dd7cddfSDavid du Colombier 	    if (code < 0)
2247dd7cddfSDavid du Colombier 		return code;
2253ff48bf5SDavid du Colombier 	    /* Check for {proc} if | {proc1} {proc2} ifelse */
2263ff48bf5SDavid du Colombier #define R_IS_OPER(pref, proc)\
2273ff48bf5SDavid du Colombier   (r_btype(pref) == t_operator && r_has_attr(pref, a_executable) &&\
2283ff48bf5SDavid du Colombier    (pref)->value.opproc == proc)
2293ff48bf5SDavid du Colombier 	    if (R_IS_OPER(&elt2, zif)) {
2303ff48bf5SDavid du Colombier 		if (ops) {
2313ff48bf5SDavid du Colombier 		    *p = PtCr_if;
2323ff48bf5SDavid du Colombier 		    psc_fixup(p, ops + *psize);
2333ff48bf5SDavid du Colombier 		}
2343ff48bf5SDavid du Colombier 	    } else if (!r_is_proc(&elt2))
2353ff48bf5SDavid du Colombier 		return_error(e_rangecheck);
236*593dc095SDavid du Colombier 	    else if ((code == array_get(imemory, pref, ++i, &elt3)) < 0)
2373ff48bf5SDavid du Colombier 		return code;
2383ff48bf5SDavid du Colombier 	    else if (R_IS_OPER(&elt3, zifelse)) {
2393ff48bf5SDavid du Colombier 		if (ops) {
2403ff48bf5SDavid du Colombier 		    *p = PtCr_if;
2413ff48bf5SDavid du Colombier 		    psc_fixup(p, ops + *psize + 3);
2423ff48bf5SDavid du Colombier 		    p = ops + *psize;
2433ff48bf5SDavid du Colombier 		    *p = PtCr_else;
2443ff48bf5SDavid du Colombier 		}
2453ff48bf5SDavid du Colombier 		*psize += 3;
2463ff48bf5SDavid du Colombier 		code = check_psc_function(i_ctx_p, &elt2, depth + 1, ops, psize);
2473ff48bf5SDavid du Colombier 		if (code < 0)
2483ff48bf5SDavid du Colombier 		    return code;
2493ff48bf5SDavid du Colombier 		if (ops)
2503ff48bf5SDavid du Colombier 		    psc_fixup(p, ops + *psize);
2513ff48bf5SDavid du Colombier 	    } else
2523ff48bf5SDavid du Colombier 		return_error(e_rangecheck);
2533ff48bf5SDavid du Colombier #undef R_IS_OPER
2543ff48bf5SDavid du Colombier 	}
2553ff48bf5SDavid du Colombier 	}
2563ff48bf5SDavid du Colombier     next:
2573ff48bf5SDavid du Colombier 	DO_NOTHING;
2587dd7cddfSDavid du Colombier     }
2597dd7cddfSDavid du Colombier     return 0;
2607dd7cddfSDavid du Colombier }
2617dd7cddfSDavid du Colombier #undef MAX_PSC_FUNCTION_NESTING
2627dd7cddfSDavid du Colombier 
2637dd7cddfSDavid du Colombier /* Check prototype */
2647dd7cddfSDavid du Colombier build_function_proc(gs_build_function_4);
2657dd7cddfSDavid du Colombier 
2667dd7cddfSDavid du Colombier /* Finish building a FunctionType 4 (PostScript Calculator) function. */
2677dd7cddfSDavid du Colombier int
gs_build_function_4(i_ctx_t * i_ctx_p,const ref * op,const gs_function_params_t * mnDR,int depth,gs_function_t ** ppfn,gs_memory_t * mem)2683ff48bf5SDavid du Colombier gs_build_function_4(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t * mnDR,
2697dd7cddfSDavid du Colombier 		    int depth, gs_function_t ** ppfn, gs_memory_t *mem)
2707dd7cddfSDavid du Colombier {
2713ff48bf5SDavid du Colombier     gs_function_PtCr_params_t params;
2727dd7cddfSDavid du Colombier     ref *proc;
2733ff48bf5SDavid du Colombier     int code;
2743ff48bf5SDavid du Colombier     byte *ops;
2753ff48bf5SDavid du Colombier     int size;
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier     *(gs_function_params_t *)&params = *mnDR;
2783ff48bf5SDavid du Colombier     params.ops.data = 0;	/* in case of failure */
2793ff48bf5SDavid du Colombier     params.ops.size = 0;	/* ditto */
2803ff48bf5SDavid du Colombier     if (dict_find_string(op, "Function", &proc) <= 0) {
2813ff48bf5SDavid du Colombier 	code = gs_note_error(e_rangecheck);
2827dd7cddfSDavid du Colombier 	goto fail;
2833ff48bf5SDavid du Colombier     }
2843ff48bf5SDavid du Colombier     if (!r_is_proc(proc)) {
2853ff48bf5SDavid du Colombier 	code = gs_note_error(e_typecheck);
2863ff48bf5SDavid du Colombier 	goto fail;
2873ff48bf5SDavid du Colombier     }
2883ff48bf5SDavid du Colombier     size = 0;
2893ff48bf5SDavid du Colombier     code = check_psc_function(i_ctx_p, proc, 0, NULL, &size);
2907dd7cddfSDavid du Colombier     if (code < 0)
2917dd7cddfSDavid du Colombier 	goto fail;
2923ff48bf5SDavid du Colombier     ops = gs_alloc_string(mem, size + 1, "gs_build_function_4(ops)");
2933ff48bf5SDavid du Colombier     if (ops == 0) {
2947dd7cddfSDavid du Colombier 	code = gs_note_error(e_VMerror);
2957dd7cddfSDavid du Colombier 	goto fail;
2967dd7cddfSDavid du Colombier     }
2973ff48bf5SDavid du Colombier     size = 0;
2983ff48bf5SDavid du Colombier     check_psc_function(i_ctx_p, proc, 0, ops, &size); /* can't fail */
2993ff48bf5SDavid du Colombier     ops[size] = PtCr_return;
3003ff48bf5SDavid du Colombier     params.ops.data = ops;
3013ff48bf5SDavid du Colombier     params.ops.size = size + 1;
3023ff48bf5SDavid du Colombier     code = gs_function_PtCr_init(ppfn, &params, mem);
3037dd7cddfSDavid du Colombier     if (code >= 0)
3047dd7cddfSDavid du Colombier 	return 0;
3053ff48bf5SDavid du Colombier     /* free_params will free the ops string */
3067dd7cddfSDavid du Colombier fail:
3073ff48bf5SDavid du Colombier     gs_function_PtCr_free_params(&params, mem);
3087dd7cddfSDavid du Colombier     return (code < 0 ? code : gs_note_error(e_rangecheck));
3097dd7cddfSDavid du Colombier }
310