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