xref: /plan9/sys/src/cmd/gs/src/gscparam.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1995, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gscparam.c,v 1.10 2004/08/04 19:36:12 stefan Exp $ */
18 /* Default implementation of parameter lists */
19 #include "memory_.h"
20 #include "string_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsparam.h"
24 #include "gsstruct.h"
25 
26 /* Forward references */
27 typedef union c_param_value_s {
28     GS_PARAM_VALUE_UNION(gs_c_param_list);
29 } gs_c_param_value;
30 /*typedef struct gs_c_param_s gs_c_param; *//* in gsparam.h */
31 
32 /* Define the GC type for a parameter list. */
33 private_st_c_param_list();
34 
35 /* Lengths corresponding to various gs_param_type_xxx types */
36 const byte gs_param_type_sizes[] = {
37     GS_PARAM_TYPE_SIZES(sizeof(gs_c_param_list))
38 };
39 
40 /* Lengths of *actual* data-containing type pointed to or contained by gs_param_type_xxx's */
41 const byte gs_param_type_base_sizes[] = {
42     GS_PARAM_TYPE_BASE_SIZES(0)
43 };
44 
45 /*
46  * Define a parameter list element.  We use gs_param_type_any to identify
47  * elements that have been requested but not yet written.  The reading
48  * procedures must recognize such elements as undefined, and ignore them.
49  */
50 struct gs_c_param_s {
51     gs_c_param *next;
52     gs_param_key_t key;
53     bool free_key;
54     gs_c_param_value value;
55     gs_param_type type;
56     void *alternate_typed_data;
57 };
58 
59 /* GC descriptor and procedures */
60 gs_private_st_composite(st_c_param, gs_c_param, "gs_c_param",
61 			c_param_enum_ptrs, c_param_reloc_ptrs);
ENUM_PTRS_WITH(c_param_enum_ptrs,gs_c_param * param)62 ENUM_PTRS_WITH(c_param_enum_ptrs, gs_c_param *param) {
63     index -= 3;
64     switch (param->type) {
65 	/* Only the aggregate types are handled specially. */
66     case gs_param_type_dict:
67     case gs_param_type_dict_int_keys:
68     case gs_param_type_array:
69 	return ENUM_USING(st_c_param_list, &param->value.d,
70 			  sizeof(param->value.d), index);
71     default: {
72 	gs_param_typed_value value;
73 
74 	value.value = *(const gs_param_value *)&param->value;
75 	value.type = param->type;
76 	return gs_param_typed_value_enum_ptrs(mem, &value, sizeof(value), index,
77 					      pep, NULL, gcst);
78     }
79     }
80 }
81 case 0: return ENUM_OBJ(param->next);
82 case 1: return ENUM_OBJ(param->alternate_typed_data);
83 case 2:
84     if (!param->key.persistent) {
85 	gs_const_string key;
86 
87 	key.data = param->key.data;
88 	key.size = param->key.size;
89 	return ENUM_STRING(&key);
90     } else
91 	return ENUM_OBJ(0);	/* keep going */
92 ENUM_PTRS_END
RELOC_PTRS_WITH(c_param_reloc_ptrs,gs_c_param * param)93 RELOC_PTRS_WITH(c_param_reloc_ptrs, gs_c_param *param) {
94     RELOC_VAR(param->next);
95     RELOC_VAR(param->alternate_typed_data);
96     if (!param->key.persistent) {
97 	gs_const_string key;
98 
99 	key.data = param->key.data;
100 	key.size = param->key.size;
101 	RELOC_CONST_STRING_VAR(key);
102 	param->key.data = key.data;
103     }
104     switch (param->type) {
105 	/* Only the aggregate types are handled specially. */
106     case gs_param_type_dict:
107     case gs_param_type_dict_int_keys:
108     case gs_param_type_array:
109 	RELOC_USING(st_c_param_list, &param->value.d, sizeof(param->value.d));
110 	break;
111     default: {
112 	gs_param_typed_value value;
113 
114 	value.value = *(gs_param_value *)&param->value;
115 	value.type = param->type;
116 	gs_param_typed_value_reloc_ptrs(&value, sizeof(value), NULL, gcst);
117 	*(gs_param_value *)&param->value = value.value;
118     }
119     }
120 }
121 RELOC_PTRS_END
122 
123 /* ---------------- Utilities ---------------- */
124 
125 gs_c_param_list *
gs_c_param_list_alloc(gs_memory_t * mem,client_name_t cname)126 gs_c_param_list_alloc(gs_memory_t *mem, client_name_t cname)
127 {
128     return gs_alloc_struct(mem, gs_c_param_list, &st_c_param_list, cname);
129 }
130 
131 private gs_c_param *
c_param_find(const gs_c_param_list * plist,gs_param_name pkey,bool any)132 c_param_find(const gs_c_param_list *plist, gs_param_name pkey, bool any)
133 {
134     gs_c_param *pparam = plist->head;
135     uint len = strlen(pkey);
136 
137     for (; pparam != 0; pparam = pparam->next)
138 	if (pparam->key.size == len && !memcmp(pparam->key.data, pkey, len))
139 	    return (pparam->type != gs_param_type_any || any ? pparam : 0);
140     return 0;
141 }
142 
143 /* ---------------- Writing parameters to a list ---------------- */
144 
145 private param_proc_begin_xmit_collection(c_param_begin_write_collection);
146 private param_proc_end_xmit_collection(c_param_end_write_collection);
147 private param_proc_xmit_typed(c_param_write_typed);
148 private param_proc_request(c_param_request);
149 private param_proc_requested(c_param_requested);
150 private const gs_param_list_procs c_write_procs =
151 {
152     c_param_write_typed,
153     c_param_begin_write_collection,
154     c_param_end_write_collection,
155     NULL,			/* get_next_key */
156     c_param_request,
157     c_param_requested
158 };
159 
160 /* Initialize a list for writing. */
161 void
gs_c_param_list_write(gs_c_param_list * plist,gs_memory_t * mem)162 gs_c_param_list_write(gs_c_param_list * plist, gs_memory_t * mem)
163 {
164     plist->memory = mem;
165     plist->head = 0;
166     plist->target = 0;		/* not used for writing */
167     plist->count = 0;
168     plist->any_requested = false;
169     plist->persistent_keys = true;
170     gs_c_param_list_write_more(plist);
171 }
172 
173 /* Set the target of a list.  Only relevant for reading. */
174 void
gs_c_param_list_set_target(gs_c_param_list * plist,gs_param_list * target)175 gs_c_param_list_set_target(gs_c_param_list *plist, gs_param_list *target)
176 {
177     plist->target = target;
178 }
179 
180 /* Re-enable a list for writing, without clearing it. */
181 /* gs_c_param_list_write must have been called previously. */
182 void
gs_c_param_list_write_more(gs_c_param_list * plist)183 gs_c_param_list_write_more(gs_c_param_list * plist)
184 {
185     plist->procs = &c_write_procs;
186     plist->coll_type = gs_param_collection_dict_any;
187 }
188 
189 /* Release a list. */
190 void
gs_c_param_list_release(gs_c_param_list * plist)191 gs_c_param_list_release(gs_c_param_list * plist)
192 {
193     gs_memory_t *mem = plist->memory;
194     gs_c_param *pparam;
195 
196     while ((pparam = plist->head) != 0) {
197 	gs_c_param *next = pparam->next;
198 
199 	switch (pparam->type) {
200 	    case gs_param_type_dict:
201 	    case gs_param_type_dict_int_keys:
202 	    case gs_param_type_array:
203 		gs_c_param_list_release(&pparam->value.d);
204 		break;
205 	    case gs_param_type_string:
206 	    case gs_param_type_name:
207 	    case gs_param_type_int_array:
208 	    case gs_param_type_float_array:
209 	    case gs_param_type_string_array:
210 	    case gs_param_type_name_array:
211 		if (!pparam->value.s.persistent)
212 		    gs_free_const_object(mem, pparam->value.s.data,
213 					 "gs_c_param_list_release data");
214 		break;
215 	    default:
216 		break;
217 	}
218 	if (pparam->free_key) {
219 	    /* We allocated this, so we must free it. */
220 	    gs_free_const_string(mem, pparam->key.data, pparam->key.size,
221 				 "gs_c_param_list_release key");
222 	}
223 	gs_free_object(mem, pparam->alternate_typed_data,
224 		       "gs_c_param_list_release alternate data");
225 	gs_free_object(mem, pparam,
226 		       "gs_c_param_list_release entry");
227 	plist->head = next;
228 	plist->count--;
229     }
230 }
231 
232 /* Add an entry to a list.  Doesn't set: value, type, plist->head. */
233 private gs_c_param *
c_param_add(gs_c_param_list * plist,gs_param_name pkey)234 c_param_add(gs_c_param_list * plist, gs_param_name pkey)
235 {
236     gs_c_param *pparam =
237 	gs_alloc_struct(plist->memory, gs_c_param, &st_c_param,
238 			"c_param_add entry");
239     uint len = strlen(pkey);
240 
241     if (pparam == 0)
242 	return 0;
243     pparam->next = plist->head;
244     if (!plist->persistent_keys) {
245 	/* We must copy the key. */
246 	byte *str = gs_alloc_string(plist->memory, len, "c_param_add key");
247 
248 	if (str == 0) {
249 	    gs_free_object(plist->memory, pparam, "c_param_add entry");
250 	    return 0;
251 	}
252 	memcpy(str, pkey, len);
253 	pparam->key.data = str;
254 	pparam->key.persistent = false; /* we will free it */
255 	pparam->free_key = true;
256     } else {
257 	pparam->key.data = (const byte *)pkey;
258 	pparam->key.persistent = true;
259 	pparam->free_key = false;
260     }
261     pparam->key.size = len;
262     pparam->alternate_typed_data = 0;
263     return pparam;
264 }
265 
266 /*  Write a dynamically typed parameter to a list. */
267 private int
c_param_write(gs_c_param_list * plist,gs_param_name pkey,void * pvalue,gs_param_type type)268 c_param_write(gs_c_param_list * plist, gs_param_name pkey, void *pvalue,
269 	      gs_param_type type)
270 {
271     unsigned top_level_sizeof = 0;
272     unsigned second_level_sizeof = 0;
273     gs_c_param *pparam = c_param_add(plist, pkey);
274 
275     if (pparam == 0)
276 	return_error(gs_error_VMerror);
277     memcpy(&pparam->value, pvalue, gs_param_type_sizes[(int)type]);
278     pparam->type = type;
279 
280     /* Need deeper copies of data if it's not persistent */
281     switch (type) {
282 	    gs_param_string const *curr_string;
283 	    gs_param_string const *end_string;
284 
285 	case gs_param_type_string_array:
286 	case gs_param_type_name_array:
287 	    /* Determine how much mem needed to hold actual string data */
288 	    curr_string = pparam->value.sa.data;
289 	    end_string = curr_string + pparam->value.sa.size;
290 	    for (; curr_string < end_string; ++curr_string)
291 		if (!curr_string->persistent)
292 		    second_level_sizeof += curr_string->size;
293 	    /* fall thru */
294 
295 	case gs_param_type_string:
296 	case gs_param_type_name:
297 	case gs_param_type_int_array:
298 	case gs_param_type_float_array:
299 	    if (!pparam->value.s.persistent) {	/* Allocate & copy object pointed to by array or string */
300 		byte *top_level_memory = NULL;
301 
302 		top_level_sizeof =
303 		    pparam->value.s.size * gs_param_type_base_sizes[type];
304 		if (top_level_sizeof + second_level_sizeof > 0) {
305 		    top_level_memory =
306 			gs_alloc_bytes_immovable(plist->memory,
307 				     top_level_sizeof + second_level_sizeof,
308 					     "c_param_write data");
309 		    if (top_level_memory == 0) {
310 			gs_free_object(plist->memory, pparam, "c_param_write entry");
311 			return_error(gs_error_VMerror);
312 		    }
313 		    memcpy(top_level_memory, pparam->value.s.data, top_level_sizeof);
314 		}
315 		pparam->value.s.data = top_level_memory;
316 
317 		/* String/name arrays need to copy actual str data */
318 
319 		if (second_level_sizeof > 0) {
320 		    byte *second_level_memory =
321 		    top_level_memory + top_level_sizeof;
322 
323 		    curr_string = pparam->value.sa.data;
324 		    end_string = curr_string + pparam->value.sa.size;
325 		    for (; curr_string < end_string; ++curr_string)
326 			if (!curr_string->persistent) {
327 			    memcpy(second_level_memory,
328 				   curr_string->data, curr_string->size);
329 			    ((gs_param_string *) curr_string)->data
330 				= second_level_memory;
331 			    second_level_memory += curr_string->size;
332 			}
333 		}
334 	    }
335 	    break;
336 	default:
337 	    break;
338     }
339 
340     plist->head = pparam;
341     plist->count++;
342     return 0;
343 }
344 
345 /* Individual writing routines. */
346 private int
c_param_begin_write_collection(gs_param_list * plist,gs_param_name pkey,gs_param_dict * pvalue,gs_param_collection_type_t coll_type)347 c_param_begin_write_collection(gs_param_list * plist, gs_param_name pkey,
348 	       gs_param_dict * pvalue, gs_param_collection_type_t coll_type)
349 {
350     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
351     gs_c_param_list *dlist =
352 	gs_c_param_list_alloc(cplist->memory,
353 			      "c_param_begin_write_collection");
354 
355     if (dlist == 0)
356 	return_error(gs_error_VMerror);
357     gs_c_param_list_write(dlist, cplist->memory);
358     dlist->coll_type = coll_type;
359     pvalue->list = (gs_param_list *) dlist;
360     return 0;
361 }
362 private int
c_param_end_write_collection(gs_param_list * plist,gs_param_name pkey,gs_param_dict * pvalue)363 c_param_end_write_collection(gs_param_list * plist, gs_param_name pkey,
364 			     gs_param_dict * pvalue)
365 {
366     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
367     gs_c_param_list *dlist = (gs_c_param_list *) pvalue->list;
368 
369     return c_param_write(cplist, pkey, pvalue->list,
370 		    (dlist->coll_type == gs_param_collection_dict_int_keys ?
371 		     gs_param_type_dict_int_keys :
372 		     dlist->coll_type == gs_param_collection_array ?
373 		     gs_param_type_array : gs_param_type_dict));
374 }
375 private int
c_param_write_typed(gs_param_list * plist,gs_param_name pkey,gs_param_typed_value * pvalue)376 c_param_write_typed(gs_param_list * plist, gs_param_name pkey,
377 		    gs_param_typed_value * pvalue)
378 {
379     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
380     gs_param_collection_type_t coll_type;
381 
382     switch (pvalue->type) {
383 	case gs_param_type_dict:
384 	    coll_type = gs_param_collection_dict_any;
385 	    break;
386 	case gs_param_type_dict_int_keys:
387 	    coll_type = gs_param_collection_dict_int_keys;
388 	    break;
389 	case gs_param_type_array:
390 	    coll_type = gs_param_collection_array;
391 	    break;
392 	default:
393 	    return c_param_write(cplist, pkey, &pvalue->value, pvalue->type);
394     }
395     return c_param_begin_write_collection
396 	(plist, pkey, &pvalue->value.d, coll_type);
397 }
398 
399 /* Other procedures */
400 
401 private int
c_param_request(gs_param_list * plist,gs_param_name pkey)402 c_param_request(gs_param_list * plist, gs_param_name pkey)
403 {
404     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
405     gs_c_param *pparam;
406 
407     cplist->any_requested = true;
408     if (c_param_find(cplist, pkey, true))
409 	return 0;
410     pparam = c_param_add(cplist, pkey);
411     if (pparam == 0)
412 	return_error(gs_error_VMerror);
413     pparam->type = gs_param_type_any; /* mark as undefined */
414     cplist->head = pparam;
415     return 0;
416 }
417 
418 private int
c_param_requested(const gs_param_list * plist,gs_param_name pkey)419 c_param_requested(const gs_param_list * plist, gs_param_name pkey)
420 {
421     const gs_c_param_list *const cplist = (const gs_c_param_list *)plist;
422     gs_param_list *target = cplist->target;
423     int code;
424 
425     if (!cplist->any_requested)
426 	return (target ? param_requested(target, pkey) : -1);
427     if (c_param_find(cplist, pkey, true) != 0)
428 	return 1;
429     if (!target)
430 	return 0;
431     code = param_requested(target, pkey);
432     return (code < 0 ? 0 : 1);
433 }
434 
435 /* ---------------- Reading from a list to parameters ---------------- */
436 
437 private param_proc_begin_xmit_collection(c_param_begin_read_collection);
438 private param_proc_end_xmit_collection(c_param_end_read_collection);
439 private param_proc_xmit_typed(c_param_read_typed);
440 private param_proc_next_key(c_param_get_next_key);
441 private param_proc_get_policy(c_param_read_get_policy);
442 private param_proc_signal_error(c_param_read_signal_error);
443 private param_proc_commit(c_param_read_commit);
444 private const gs_param_list_procs c_read_procs =
445 {
446     c_param_read_typed,
447     c_param_begin_read_collection,
448     c_param_end_read_collection,
449     c_param_get_next_key,
450     NULL,			/* request, N/A */
451     NULL,			/* requested, N/A */
452     c_param_read_get_policy,
453     c_param_read_signal_error,
454     c_param_read_commit
455 };
456 
457 /* Switch a list from writing to reading. */
458 void
gs_c_param_list_read(gs_c_param_list * plist)459 gs_c_param_list_read(gs_c_param_list * plist)
460 {
461     plist->procs = &c_read_procs;
462 }
463 
464 /* Generic routine for reading a parameter from a list. */
465 
466 private int
c_param_read_typed(gs_param_list * plist,gs_param_name pkey,gs_param_typed_value * pvalue)467 c_param_read_typed(gs_param_list * plist, gs_param_name pkey,
468 		   gs_param_typed_value * pvalue)
469 {
470     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
471     gs_param_type req_type = pvalue->type;
472     gs_c_param *pparam = c_param_find(cplist, pkey, false);
473     int code;
474 
475     if (pparam == 0)
476 	return (cplist->target ?
477 		param_read_typed(cplist->target, pkey, pvalue) : 1);
478     pvalue->type = pparam->type;
479     switch (pvalue->type) {
480 	case gs_param_type_dict:
481 	case gs_param_type_dict_int_keys:
482 	case gs_param_type_array:
483 	    gs_c_param_list_read(&pparam->value.d);
484 	    pvalue->value.d.list = (gs_param_list *) & pparam->value.d;
485 	    pvalue->value.d.size = pparam->value.d.count;
486 	    return 0;
487 	default:
488 	    break;
489     }
490     memcpy(&pvalue->value, &pparam->value,
491 	   gs_param_type_sizes[(int)pparam->type]);
492     code = param_coerce_typed(pvalue, req_type, NULL);
493 /****** SHOULD LET param_coerce_typed DO THIS ******/
494     if (code == gs_error_typecheck &&
495 	req_type == gs_param_type_float_array &&
496 	pvalue->type == gs_param_type_int_array
497 	) {
498 	/* Convert int array to float dest */
499 	gs_param_float_array fa;
500 	int element;
501 
502 	fa.size = pparam->value.ia.size;
503 	fa.persistent = false;
504 
505 	if (pparam->alternate_typed_data == 0) {
506 	    if ((pparam->alternate_typed_data
507 		 = (void *)gs_alloc_bytes_immovable(cplist->memory,
508 						    fa.size * sizeof(float),
509 			     "gs_c_param_read alternate float array")) == 0)
510 		      return_error(gs_error_VMerror);
511 
512 	    for (element = 0; element < fa.size; ++element)
513 		((float *)(pparam->alternate_typed_data))[element]
514 		    = (float)pparam->value.ia.data[element];
515 	}
516 	fa.data = (float *)pparam->alternate_typed_data;
517 
518 	pvalue->value.fa = fa;
519 	return 0;
520     }
521     return code;
522 }
523 
524 /* Individual reading routines. */
525 private int
c_param_begin_read_collection(gs_param_list * plist,gs_param_name pkey,gs_param_dict * pvalue,gs_param_collection_type_t coll_type)526 c_param_begin_read_collection(gs_param_list * plist, gs_param_name pkey,
527 	       gs_param_dict * pvalue, gs_param_collection_type_t coll_type)
528 {
529     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
530     gs_c_param *pparam = c_param_find(cplist, pkey, false);
531 
532     if (pparam == 0)
533 	return
534 	    (cplist->target ?
535 	     param_begin_read_collection(cplist->target,
536 					 pkey, pvalue, coll_type) :
537 	     1);
538     switch (pparam->type) {
539 	case gs_param_type_dict:
540 	    if (coll_type != gs_param_collection_dict_any)
541 		return_error(gs_error_typecheck);
542 	    break;
543 	case gs_param_type_dict_int_keys:
544 	    if (coll_type == gs_param_collection_array)
545 		return_error(gs_error_typecheck);
546 	    break;
547 	case gs_param_type_array:
548 	    break;
549 	default:
550 	    return_error(gs_error_typecheck);
551     }
552     gs_c_param_list_read(&pparam->value.d);
553     pvalue->list = (gs_param_list *) & pparam->value.d;
554     pvalue->size = pparam->value.d.count;
555     return 0;
556 }
557 private int
c_param_end_read_collection(gs_param_list * plist,gs_param_name pkey,gs_param_dict * pvalue)558 c_param_end_read_collection(gs_param_list * plist, gs_param_name pkey,
559 			    gs_param_dict * pvalue)
560 {
561     return 0;
562 }
563 
564 /* Other procedures */
565 private int			/* ret 0 ok, 1 if EOF, or -ve err */
c_param_get_next_key(gs_param_list * plist,gs_param_enumerator_t * penum,gs_param_key_t * key)566 c_param_get_next_key(gs_param_list * plist, gs_param_enumerator_t * penum,
567 		     gs_param_key_t * key)
568 {
569     gs_c_param_list *const cplist = (gs_c_param_list *)plist;
570     gs_c_param *pparam =
571     (penum->pvoid ? ((gs_c_param *) (penum->pvoid))->next :
572      cplist->head);
573 
574     if (pparam == 0)
575 	return 1;
576     penum->pvoid = pparam;
577     *key = pparam->key;
578     return 0;
579 }
580 private int
c_param_read_get_policy(gs_param_list * plist,gs_param_name pkey)581 c_param_read_get_policy(gs_param_list * plist, gs_param_name pkey)
582 {
583     return gs_param_policy_ignore;
584 }
585 private int
c_param_read_signal_error(gs_param_list * plist,gs_param_name pkey,int code)586 c_param_read_signal_error(gs_param_list * plist, gs_param_name pkey, int code)
587 {
588     return code;
589 }
590 private int
c_param_read_commit(gs_param_list * plist)591 c_param_read_commit(gs_param_list * plist)
592 {
593     return 0;
594 }
595