1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: gdevpdfo.c 9607 2009-03-31 14:30:14Z ken $ */
15 /* Cos object support */
16 #include "memory_.h"
17 #include "gx.h"
18 #include "gserrors.h"
19 #include "gsparam.h"
20 #include "gsutil.h"		/* for bytes_compare */
21 #include "gdevpdfx.h"
22 #include "gdevpdfo.h"
23 #include "strimpl.h"
24 #include "sa85x.h"
25 #include "sarc4.h"
26 
27 #define CHECK(expr)\
28   BEGIN if ((code = (expr)) < 0) return code; END
29 
30 /* ---------------- Structure definitions ---------------- */
31 
32 /*
33  * Define the generic structure for elements of arrays and
34  * dictionaries/streams.
35  */
36 #define cos_element_common(etype)\
37     etype *next
38 struct cos_element_s {
39     cos_element_common(cos_element_t);
40 };
41 #define private_st_cos_element()	/* in gdevpdfo.c */\
42   gs_private_st_ptrs1(st_cos_element, cos_element_t, "cos_element_t",\
43     cos_element_enum_ptrs, cos_element_reloc_ptrs, next)
44 #define cos_element_num_ptrs 1
45 
46 /*
47  * Define the structure for a piece of stream contents.
48  */
49 struct cos_stream_piece_s {
50     cos_element_common(cos_stream_piece_t);
51     long position;		/* in streams file */
52     uint size;
53 };
54 #define private_st_cos_stream_piece()	/* in gdevpdfo.c */\
55   gs_private_st_suffix_add0_local(st_cos_stream_piece, cos_stream_piece_t,\
56     "cos_stream_piece_t", cos_element_enum_ptrs, cos_element_reloc_ptrs,\
57     st_cos_element)
58 
59 /*
60  * Define Cos arrays, dictionaries, and streams.
61  */
62      /* array */
63 struct cos_array_element_s {
64     cos_element_common(cos_array_element_t);
65     long index;
66     cos_value_t value;
67 };
68 #define private_st_cos_array_element()	/* in gdevpdfo.c */\
69   gs_private_st_composite(st_cos_array_element, cos_array_element_t,\
70     "cos_array_element_t", cos_array_element_enum_ptrs, cos_array_element_reloc_ptrs)
71     /* dict */
72 struct cos_dict_element_s {
73     cos_element_common(cos_dict_element_t);
74     gs_string key;
75     bool owns_key;	/* if false, key is shared, do not trace or free */
76     cos_value_t value;
77 };
78 #define private_st_cos_dict_element()	/* in gdevpdfo.c */\
79   gs_private_st_composite(st_cos_dict_element, cos_dict_element_t,\
80     "cos_dict_element_t", cos_dict_element_enum_ptrs, cos_dict_element_reloc_ptrs)
81 
82 /* GC descriptors */
83 private_st_cos_element();
84 private_st_cos_stream_piece();
85 private_st_cos_object();
86 private_st_cos_value();
87 private_st_cos_array_element();
88 private_st_cos_dict_element();
89 
90 /* GC procedures */
91 static
92 ENUM_PTRS_WITH(cos_value_enum_ptrs, cos_value_t *pcv) return 0;
93  case 0:
94     switch (pcv->value_type) {
95     case COS_VALUE_SCALAR:
96 	return ENUM_STRING(&pcv->contents.chars);
97     case COS_VALUE_CONST:
98 	break;
99     case COS_VALUE_OBJECT:
100     case COS_VALUE_RESOURCE:
101 	return ENUM_OBJ(pcv->contents.object);
102     }
103     return 0;
104 ENUM_PTRS_END
105 static
RELOC_PTRS_WITH(cos_value_reloc_ptrs,cos_value_t * pcv)106 RELOC_PTRS_WITH(cos_value_reloc_ptrs, cos_value_t *pcv)
107 {
108     switch (pcv->value_type) {
109     case COS_VALUE_SCALAR:
110 	RELOC_STRING_VAR(pcv->contents.chars);
111     case COS_VALUE_CONST:
112 	break;
113     case COS_VALUE_OBJECT:
114     case COS_VALUE_RESOURCE:
115 	RELOC_VAR(pcv->contents.object);
116 	break;
117     }
118 }
119 RELOC_PTRS_END
120 static
ENUM_PTRS_WITH(cos_array_element_enum_ptrs,cos_array_element_t * pcae)121 ENUM_PTRS_WITH(cos_array_element_enum_ptrs, cos_array_element_t *pcae)
122 {
123     return (index < cos_element_num_ptrs ?
124 	    ENUM_USING_PREFIX(st_cos_element, 0) :
125 	    ENUM_USING(st_cos_value, &pcae->value, sizeof(cos_value_t),
126 		       index - cos_element_num_ptrs));
127 }
128 ENUM_PTRS_END
129 static
RELOC_PTRS_WITH(cos_array_element_reloc_ptrs,cos_array_element_t * pcae)130 RELOC_PTRS_WITH(cos_array_element_reloc_ptrs, cos_array_element_t *pcae)
131 {
132     RELOC_PREFIX(st_cos_element);
133     RELOC_USING(st_cos_value, &pcae->value, sizeof(cos_value_t));
134 }
135 RELOC_PTRS_END
136 static
ENUM_PTRS_WITH(cos_dict_element_enum_ptrs,cos_dict_element_t * pcde)137 ENUM_PTRS_WITH(cos_dict_element_enum_ptrs, cos_dict_element_t *pcde)
138 {
139     return (index < cos_element_num_ptrs ?
140 	    ENUM_USING_PREFIX(st_cos_element, 0) :
141 	    (index -= cos_element_num_ptrs) > 0 ?
142 	    ENUM_USING(st_cos_value, &pcde->value, sizeof(cos_value_t),
143 		       index - 1) :
144 	    pcde->owns_key ? ENUM_STRING(&pcde->key) : ENUM_OBJ(NULL));
145 }
146 ENUM_PTRS_END
147 static
RELOC_PTRS_WITH(cos_dict_element_reloc_ptrs,cos_dict_element_t * pcde)148 RELOC_PTRS_WITH(cos_dict_element_reloc_ptrs, cos_dict_element_t *pcde)
149 {
150     RELOC_PREFIX(st_cos_element);
151     if (pcde->owns_key)
152 	RELOC_STRING_VAR(pcde->key);
153     RELOC_USING(st_cos_value, &pcde->value, sizeof(cos_value_t));
154 }
155 RELOC_PTRS_END
156 
157 /* ---------------- Generic support ---------------- */
158 
159 /* Initialize a just-allocated cos object. */
160 static void
cos_object_init(cos_object_t * pco,gx_device_pdf * pdev,const cos_object_procs_t * procs)161 cos_object_init(cos_object_t *pco, gx_device_pdf *pdev,
162 		const cos_object_procs_t *procs)
163 {
164     if (pco) {
165 	pco->cos_procs = procs;
166 	pco->id = 0;
167 	pco->elements = 0;
168 	pco->pieces = 0;
169 	pco->pdev = pdev;
170 	pco->pres = 0;
171 	pco->is_open = true;
172 	pco->is_graphics = false;
173 	pco->written = false;
174  	pco->length = 0;
175  	pco->input_strm = 0;
176     }
177 }
178 
179 /* Get the allocator for a Cos object. */
180 gs_memory_t *
cos_object_memory(const cos_object_t * pco)181 cos_object_memory(const cos_object_t *pco)
182 {
183     return pco->pdev->pdf_memory;
184 }
185 
186 /* Change a generic cos object into one of a specific type. */
187 int
cos_become(cos_object_t * pco,cos_type_t cotype)188 cos_become(cos_object_t *pco, cos_type_t cotype)
189 {
190     if (cos_type(pco) != cos_type_generic)
191 	return_error(gs_error_typecheck);
192     cos_type(pco) = cotype;
193     return 0;
194 }
195 
196 /* Release a cos object. */
197 cos_proc_release(cos_release);	/* check prototype */
198 void
cos_release(cos_object_t * pco,client_name_t cname)199 cos_release(cos_object_t *pco, client_name_t cname)
200 {
201     pco->cos_procs->release(pco, cname);
202 }
203 
204 /* Free a cos object. */
205 void
cos_free(cos_object_t * pco,client_name_t cname)206 cos_free(cos_object_t *pco, client_name_t cname)
207 {
208     cos_release(pco, cname);
209     gs_free_object(cos_object_memory(pco), pco, cname);
210 }
211 
212 /* Write a cos object on the output. */
213 cos_proc_write(cos_write);	/* check prototype */
214 int
cos_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)215 cos_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
216 {
217     return pco->cos_procs->write(pco, pdev, object_id);
218 }
219 
220 /* Write a cos object as a PDF object. */
221 int
cos_write_object(cos_object_t * pco,gx_device_pdf * pdev)222 cos_write_object(cos_object_t *pco, gx_device_pdf *pdev)
223 {
224     int code;
225 
226     if (pco->id == 0 || pco->written)
227 	return_error(gs_error_Fatal);
228     pdf_open_separate(pdev, pco->id);
229     code = cos_write(pco, pdev, pco->id);
230     pdf_end_separate(pdev);
231     pco->written = true;
232     return code;
233 }
234 
235 /* Make a value to store into a composite object. */
236 const cos_value_t *
cos_string_value(cos_value_t * pcv,const byte * data,uint size)237 cos_string_value(cos_value_t *pcv, const byte *data, uint size)
238 {
239     /*
240      * It's OK to break const here, because the value will be copied
241      * before being stored in the collection.
242      */
243     pcv->contents.chars.data = (byte *)data;
244     pcv->contents.chars.size = size;
245     pcv->value_type = COS_VALUE_SCALAR;
246     return pcv;
247 }
248 const cos_value_t *
cos_c_string_value(cos_value_t * pcv,const char * str)249 cos_c_string_value(cos_value_t *pcv, const char *str)
250 {
251     /*
252      * We shouldn't break const here, because the value will not be copied
253      * or freed (or traced), but that would require a lot of bothersome
254      * casting elsewhere.
255      */
256     pcv->contents.chars.data = (byte *)str;
257     pcv->contents.chars.size = strlen(str);
258     pcv->value_type = COS_VALUE_CONST;
259     return pcv;
260 }
261 const cos_value_t *
cos_object_value(cos_value_t * pcv,cos_object_t * pco)262 cos_object_value(cos_value_t *pcv, cos_object_t *pco)
263 {
264     pcv->contents.object = pco;
265     pcv->value_type = COS_VALUE_OBJECT;
266     return pcv;
267 }
268 const cos_value_t *
cos_resource_value(cos_value_t * pcv,cos_object_t * pco)269 cos_resource_value(cos_value_t *pcv, cos_object_t *pco)
270 {
271     pcv->contents.object = pco;
272     pcv->value_type = COS_VALUE_RESOURCE;
273     return pcv;
274 }
275 
276 /* Free a value. */
277 void
cos_value_free(const cos_value_t * pcv,const cos_object_t * pco,client_name_t cname)278 cos_value_free(const cos_value_t *pcv, const cos_object_t *pco,
279 	       client_name_t cname)
280 {
281     switch (pcv->value_type) {
282     case COS_VALUE_SCALAR:
283 	gs_free_string(cos_object_memory(pco), pcv->contents.chars.data,
284 		       pcv->contents.chars.size, cname);
285     case COS_VALUE_CONST:
286 	break;
287     case COS_VALUE_OBJECT:
288 	/* Free the object if this is the only reference to it. */
289 	if (pcv->contents.object != NULL) /* see cos_dict_objects_delete. */
290 	    if (!pcv->contents.object->id)
291 		cos_free(pcv->contents.object, cname);
292     case COS_VALUE_RESOURCE:
293 	break;
294     }
295 }
296 
297 /* Write a value on the output. */
298 static int
cos_value_write_spaced(const cos_value_t * pcv,gx_device_pdf * pdev,bool do_space,gs_id object_id)299 cos_value_write_spaced(const cos_value_t *pcv, gx_device_pdf *pdev,
300 		       bool do_space, gs_id object_id)
301 {
302     stream *s = pdev->strm;
303 
304     switch (pcv->value_type) {
305     case COS_VALUE_SCALAR:
306     case COS_VALUE_CONST:
307 	if (do_space)
308 	    switch (pcv->contents.chars.data[0]) {
309 	    case '/': case '(': case '<': break;
310 	    default: stream_putc(s, ' ');
311 	    }
312 	return pdf_write_value(pdev, pcv->contents.chars.data,
313 			pcv->contents.chars.size, object_id);
314     case COS_VALUE_RESOURCE:
315 	pprintld1(s, "/R%ld", pcv->contents.object->id);
316 	break;
317     case COS_VALUE_OBJECT: {
318 	const cos_object_t *pco = pcv->contents.object;
319 
320 	if (!pco->id) {
321 	    if (do_space &&
322 		!(pco->cos_procs == cos_type_array ||
323 		  pco->cos_procs == cos_type_dict)
324 		) {
325 		/* Arrays and dictionaries (only) are self-delimiting. */
326 		stream_putc(s, ' ');
327 	    }
328 	    return cos_write(pco, pdev, object_id);
329 	}
330 	if (do_space)
331 	    stream_putc(s, ' ');
332 	pprintld1(s, "%ld 0 R", pco->id);
333 	break;
334     }
335     default:			/* can't happen */
336 	return_error(gs_error_Fatal);
337     }
338     return 0;
339 }
340 int
cos_value_write(const cos_value_t * pcv,gx_device_pdf * pdev)341 cos_value_write(const cos_value_t *pcv, gx_device_pdf *pdev)
342 {
343     return cos_value_write_spaced(pcv, pdev, false, 0);
344 }
345 
346 /* Copy a value if necessary for putting into an array or dictionary. */
347 static int
cos_copy_element_value(cos_value_t * pcv,gs_memory_t * mem,const cos_value_t * pvalue,bool copy)348 cos_copy_element_value(cos_value_t *pcv, gs_memory_t *mem,
349 		       const cos_value_t *pvalue, bool copy)
350 {
351     *pcv = *pvalue;
352     if (pvalue->value_type == COS_VALUE_SCALAR && copy) {
353 	byte *value_data = gs_alloc_string(mem, pvalue->contents.chars.size,
354 					   "cos_copy_element_value");
355 
356 	if (value_data == 0)
357 	    return_error(gs_error_VMerror);
358 	memcpy(value_data, pvalue->contents.chars.data,
359 	       pvalue->contents.chars.size);
360 	pcv->contents.chars.data = value_data;
361     }
362     return 0;
363 }
364 
365 /* Release a value copied for putting, if the operation fails. */
366 static void
cos_uncopy_element_value(cos_value_t * pcv,gs_memory_t * mem,bool copy)367 cos_uncopy_element_value(cos_value_t *pcv, gs_memory_t *mem, bool copy)
368 {
369     if (pcv->value_type == COS_VALUE_SCALAR && copy)
370 	gs_free_string(mem, pcv->contents.chars.data, pcv->contents.chars.size,
371 		       "cos_uncopy_element_value");
372 }
373 
374 /* Compare 2 cos values for equality. */
375 static int
cos_value_equal(const cos_value_t * pcv0,const cos_value_t * pcv1,gx_device_pdf * pdev)376 cos_value_equal(const cos_value_t *pcv0, const cos_value_t *pcv1, gx_device_pdf *pdev)
377 {
378     if (pcv0->value_type != pcv1->value_type)
379 	return false;
380     switch (pcv0->value_type) {
381 	case COS_VALUE_SCALAR:
382 	case COS_VALUE_CONST:
383 	    if (bytes_compare(pcv0->contents.chars.data, pcv0->contents.chars.size,
384 			      pcv1->contents.chars.data, pcv1->contents.chars.size))
385 		return false;
386 	    break;
387 	case COS_VALUE_OBJECT:
388 	    if (pcv0->contents.object != pcv1->contents.object) {
389 		int code = pcv0->contents.object->cos_procs->equal(
390 			pcv0->contents.object, pcv1->contents.object, pdev);
391 
392 		if (code < 0)
393 		    return code;
394 		if (!code)
395 		    return false;
396 	    }
397 	    break;
398 	case COS_VALUE_RESOURCE:
399 	    if (pcv0->contents.object != pcv1->contents.object)
400 		return false;
401 	    break;
402     }
403     return true;
404 }
405 
406 /* ---------------- Specific object types ---------------- */
407 
408 /* ------ Generic objects ------ */
409 
410 static cos_proc_release(cos_generic_release);
411 static cos_proc_write(cos_generic_write);
412 static cos_proc_equal(cos_generic_equal);
413 const cos_object_procs_t cos_generic_procs = {
414     cos_generic_release, cos_generic_write, cos_generic_equal
415 };
416 
417 cos_object_t *
cos_object_alloc(gx_device_pdf * pdev,client_name_t cname)418 cos_object_alloc(gx_device_pdf *pdev, client_name_t cname)
419 {
420     gs_memory_t *mem = pdev->pdf_memory;
421     cos_object_t *pco =
422 	gs_alloc_struct(mem, cos_object_t, &st_cos_object, cname);
423 
424     cos_object_init(pco, pdev, &cos_generic_procs);
425     return pco;
426 }
427 
428 static void
cos_generic_release(cos_object_t * pco,client_name_t cname)429 cos_generic_release(cos_object_t *pco, client_name_t cname)
430 {
431     /* Do nothing. */
432 }
433 
434 static int
cos_generic_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)435 cos_generic_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
436 {
437     return_error(gs_error_Fatal);
438 }
439 
440 static int
cos_generic_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)441 cos_generic_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
442 {
443     return_error(gs_error_Fatal);
444 }
445 
446 /* ------ Arrays ------ */
447 
448 static cos_proc_release(cos_array_release);
449 static cos_proc_write(cos_array_write);
450 static cos_proc_equal(cos_array_equal);
451 const cos_object_procs_t cos_array_procs = {
452     cos_array_release, cos_array_write, cos_array_equal
453 };
454 
455 cos_array_t *
cos_array_alloc(gx_device_pdf * pdev,client_name_t cname)456 cos_array_alloc(gx_device_pdf *pdev, client_name_t cname)
457 {
458     gs_memory_t *mem = pdev->pdf_memory;
459     cos_array_t *pca =
460 	gs_alloc_struct(mem, cos_array_t, &st_cos_object, cname);
461 
462     cos_object_init((cos_object_t *)pca, pdev, &cos_array_procs);
463     return pca;
464 }
465 
466 cos_array_t *
cos_array_from_floats(gx_device_pdf * pdev,const float * pf,uint size,client_name_t cname)467 cos_array_from_floats(gx_device_pdf *pdev, const float *pf, uint size,
468 		      client_name_t cname)
469 {
470     cos_array_t *pca = cos_array_alloc(pdev, cname);
471     uint i;
472 
473     if (pca == 0)
474 	return 0;
475     for (i = 0; i < size; ++i) {
476 	int code = cos_array_add_real(pca, pf[i]);
477 
478 	if (code < 0) {
479 	    COS_FREE(pca, cname);
480 	    return 0;
481 	}
482     }
483     return pca;
484 }
485 
486 static void
cos_array_release(cos_object_t * pco,client_name_t cname)487 cos_array_release(cos_object_t *pco, client_name_t cname)
488 {
489     gs_memory_t *mem = cos_object_memory(pco);
490     cos_array_t *const pca = (cos_array_t *)pco;
491     cos_array_element_t *cur;
492     cos_array_element_t *next;
493 
494     for (cur = pca->elements; cur; cur = next) {
495 	next = cur->next;
496 	cos_value_free(&cur->value, pco, cname);
497 	gs_free_object(mem, cur, cname);
498     }
499     pca->elements = 0;
500 }
501 
502 static cos_array_element_t *cos_array_reorder(const cos_array_t *pca,
503 					       cos_array_element_t *first);
504 static int
cos_array_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)505 cos_array_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
506 {
507     stream *s = pdev->strm;
508     const cos_array_t *const pca = (const cos_array_t *)pco;
509     cos_array_element_t *first = cos_array_reorder(pca, NULL);
510     cos_array_element_t *pcae;
511     uint last_index = 0;
512 
513     stream_puts(s, "[");
514     for (pcae = first; pcae; ++last_index, pcae = pcae->next) {
515 	if (pcae != first)
516 	    stream_putc(s, '\n');
517 	for (; pcae->index > last_index; ++last_index)
518 	    stream_puts(s, "null\n");
519 	cos_value_write_spaced(&pcae->value, pdev, false, object_id);
520     }
521     DISCARD(cos_array_reorder(pca, first));
522     stream_puts(s, "]");
523     if (pdev->PDFA)
524         stream_puts(s, "\n");
525     return 0;
526 }
527 
528 static int
cos_array_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)529 cos_array_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
530 {
531     const cos_array_t *const pca0 = (const cos_array_t *)pco0;
532     const cos_array_t *const pca1 = (const cos_array_t *)pco1;
533     cos_array_element_t *first0 = pca0->elements;
534     cos_array_element_t *first1 = pca1->elements;
535     cos_array_element_t *pcae0, *pcae1;
536     int code;
537 
538     for (pcae0 = first0, pcae1 = first1; pcae0 && pcae1;
539 	    pcae0 = pcae0->next, pcae1 = pcae1->next) {
540 	if (pcae0->index != pcae1->index)
541 	    return false;
542 	code = cos_value_equal(&pcae0->value, &pcae1->value, pdev);
543 	if (code < 0)
544 	    return code;
545 	if (!code)
546 	    return false;
547     }
548     if (pcae0 || pcae1)
549 	return false;
550     return true;
551 }
552 
553 
554 /* Put/add an element in/to an array. */
555 int
cos_array_put(cos_array_t * pca,long index,const cos_value_t * pvalue)556 cos_array_put(cos_array_t *pca, long index, const cos_value_t *pvalue)
557 {
558     gs_memory_t *mem = COS_OBJECT_MEMORY(pca);
559     cos_value_t value;
560     int code = cos_copy_element_value(&value, mem, pvalue, true);
561 
562     if (code >= 0) {
563 	code = cos_array_put_no_copy(pca, index, &value);
564 	if (code < 0)
565 	    cos_uncopy_element_value(&value, mem, true);
566     }
567     return code;
568 }
569 int
cos_array_put_no_copy(cos_array_t * pca,long index,const cos_value_t * pvalue)570 cos_array_put_no_copy(cos_array_t *pca, long index, const cos_value_t *pvalue)
571 {
572     gs_memory_t *mem = COS_OBJECT_MEMORY(pca);
573     cos_array_element_t **ppcae = &pca->elements;
574     cos_array_element_t *pcae;
575     cos_array_element_t *next;
576 
577     while ((next = *ppcae) != 0 && next->index > index)
578 	ppcae = &next->next;
579     if (next && next->index == index) {
580 	/* We're replacing an existing element. */
581 	cos_value_free(&next->value, COS_OBJECT(pca),
582 		       "cos_array_put(old value)");
583 	pcae = next;
584     } else {
585 	/* Create a new element. */
586 	pcae = gs_alloc_struct(mem, cos_array_element_t, &st_cos_array_element,
587 			       "cos_array_put(element)");
588 	if (pcae == 0)
589 	    return_error(gs_error_VMerror);
590 	pcae->index = index;
591 	pcae->next = next;
592 	*ppcae = pcae;
593     }
594     pcae->value = *pvalue;
595     return 0;
596 }
597 static long
cos_array_next_index(const cos_array_t * pca)598 cos_array_next_index(const cos_array_t *pca)
599 {
600     return (pca->elements ? pca->elements->index + 1 : 0L);
601 }
602 int
cos_array_add(cos_array_t * pca,const cos_value_t * pvalue)603 cos_array_add(cos_array_t *pca, const cos_value_t *pvalue)
604 {
605     return cos_array_put(pca, cos_array_next_index(pca), pvalue);
606 }
607 int
cos_array_add_no_copy(cos_array_t * pca,const cos_value_t * pvalue)608 cos_array_add_no_copy(cos_array_t *pca, const cos_value_t *pvalue)
609 {
610     return cos_array_put_no_copy(pca, cos_array_next_index(pca), pvalue);
611 }
612 int
cos_array_add_c_string(cos_array_t * pca,const char * str)613 cos_array_add_c_string(cos_array_t *pca, const char *str)
614 {
615     cos_value_t value;
616 
617     return cos_array_add(pca, cos_c_string_value(&value, str));
618 }
619 int
cos_array_add_int(cos_array_t * pca,int i)620 cos_array_add_int(cos_array_t *pca, int i)
621 {
622     char str[sizeof(int) * 8 / 3 + 3]; /* sign, rounding, 0 terminator */
623     cos_value_t v;
624 
625     sprintf(str, "%d", i);
626     return cos_array_add(pca, cos_string_value(&v, (byte *)str, strlen(str)));
627 }
628 int
cos_array_add_real(cos_array_t * pca,floatp r)629 cos_array_add_real(cos_array_t *pca, floatp r)
630 {
631     byte str[50];		/****** ADHOC ******/
632     stream s;
633     cos_value_t v;
634 
635     s_init(&s, NULL);
636     swrite_string(&s, str, sizeof(str));
637     pprintg1(&s, "%g", r);
638     return cos_array_add(pca, cos_string_value(&v, str, stell(&s)));
639 }
640 int
cos_array_add_object(cos_array_t * pca,cos_object_t * pco)641 cos_array_add_object(cos_array_t *pca, cos_object_t *pco)
642 {
643     cos_value_t value;
644 
645     value.contents.chars.size = 0; /* Quiet a warning appeared with MSVC6 inline optimization. */
646     return cos_array_add(pca, cos_object_value(&value, pco));
647 }
648 
649 /*
650  * Remove and return the last element of an array.  Since this is intended
651  * specifically for arrays used as stacks, it gives an error if there is a
652  * gap in indices between the last element and the element before it.
653  */
654 int
cos_array_unadd(cos_array_t * pca,cos_value_t * pvalue)655 cos_array_unadd(cos_array_t *pca, cos_value_t *pvalue)
656 {
657     cos_array_element_t *pcae = pca->elements;
658 
659     if (pcae == 0 ||
660 	pcae->index != (pcae->next == 0 ? 0 : pcae->next->index + 1)
661 	)
662 	return_error(gs_error_rangecheck);
663     *pvalue = pcae->value;
664     pca->elements = pcae->next;
665     gs_free_object(COS_OBJECT_MEMORY(pca), pcae, "cos_array_unadd");
666     return 0;
667 }
668 
669 /* Get the first / next element for enumerating an array. */
670 const cos_array_element_t *
cos_array_element_first(const cos_array_t * pca)671 cos_array_element_first(const cos_array_t *pca)
672 {
673     return pca->elements;
674 }
675 const cos_array_element_t *
cos_array_element_next(const cos_array_element_t * pca,long * pindex,const cos_value_t ** ppvalue)676 cos_array_element_next(const cos_array_element_t *pca, long *pindex,
677 		       const cos_value_t **ppvalue)
678 {
679     *pindex = pca->index;
680     *ppvalue = &pca->value;
681     return pca->next;
682 }
683 
684 /*
685  * Reorder the elements of an array for writing or after writing.  Usage:
686  *	first_element = cos_array_reorder(pca, NULL);
687  *	...
688  *	cos_array_reorder(pca, first_element);
689  */
690 static cos_array_element_t *
cos_array_reorder(const cos_array_t * pca,cos_array_element_t * first)691 cos_array_reorder(const cos_array_t *pca, cos_array_element_t *first)
692 {
693     cos_array_element_t *last;
694     cos_array_element_t *next;
695     cos_array_element_t *pcae;
696 
697     for (pcae = (first ? first : pca->elements), last = NULL; pcae;
698 	 pcae = next)
699 	next = pcae->next, pcae->next = last, last = pcae;
700     return last;
701 }
702 
703 /* ------ Dictionaries ------ */
704 
705 static cos_proc_release(cos_dict_release);
706 static cos_proc_write(cos_dict_write);
707 static cos_proc_equal(cos_dict_equal);
708 const cos_object_procs_t cos_dict_procs = {
709     cos_dict_release, cos_dict_write, cos_dict_equal
710 };
711 
712 cos_dict_t *
cos_dict_alloc(gx_device_pdf * pdev,client_name_t cname)713 cos_dict_alloc(gx_device_pdf *pdev, client_name_t cname)
714 {
715     gs_memory_t *mem = pdev->pdf_memory;
716     cos_dict_t *pcd =
717 	gs_alloc_struct(mem, cos_dict_t, &st_cos_object, cname);
718 
719     cos_object_init((cos_object_t *)pcd, pdev, &cos_dict_procs);
720     return pcd;
721 }
722 
723 static void
cos_dict_element_free(cos_dict_t * pcd,cos_dict_element_t * pcde,client_name_t cname)724 cos_dict_element_free(cos_dict_t *pcd, cos_dict_element_t *pcde,
725 		      client_name_t cname)
726 {
727     gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
728 
729     cos_value_free(&pcde->value, COS_OBJECT(pcd), cname);
730     if (pcde->owns_key)
731 	gs_free_string(mem, pcde->key.data, pcde->key.size, cname);
732     gs_free_object(mem, pcde, cname);
733 }
734 
735 static void
cos_dict_release(cos_object_t * pco,client_name_t cname)736 cos_dict_release(cos_object_t *pco, client_name_t cname)
737 {
738     cos_dict_t *const pcd = (cos_dict_t *)pco;
739     cos_dict_element_t *cur;
740     cos_dict_element_t *next;
741 
742     for (cur = pcd->elements; cur; cur = next) {
743 	next = cur->next;
744 	cos_dict_element_free(pcd, cur, cname);
745     }
746     pcd->elements = 0;
747 }
748 
749 /* Write the elements of a dictionary. */
750 static int
cos_elements_write(stream * s,const cos_dict_element_t * pcde,gx_device_pdf * pdev,bool do_space,gs_id object_id)751 cos_elements_write(stream *s, const cos_dict_element_t *pcde,
752 		   gx_device_pdf *pdev, bool do_space, gs_id object_id)
753 {
754     if (pcde) {
755 	/* Temporarily replace the output stream in pdev. */
756 	stream *save = pdev->strm;
757 
758 	pdev->strm = s;
759 	for (;;) {
760 	    gs_id object_id1 = (pdev->NoEncrypt.size == 0 ||
761 				bytes_compare(pdev->NoEncrypt.data, pdev->NoEncrypt.size,
762 				    pcde->key.data, pcde->key.size)
763 				? object_id : (gs_id)-1);
764 
765 	    pdf_write_value(pdev, pcde->key.data, pcde->key.size, object_id1);
766 	    cos_value_write_spaced(&pcde->value, pdev, true, object_id1);
767 	    pcde = pcde->next;
768 	    if (pcde || do_space)
769 		stream_putc(s, '\n');
770 	    if (!pcde)
771 		break;
772 	}
773 	pdev->strm = save;
774     }
775     return 0;
776 }
777 int
cos_dict_elements_write(const cos_dict_t * pcd,gx_device_pdf * pdev)778 cos_dict_elements_write(const cos_dict_t *pcd, gx_device_pdf *pdev)
779 {
780     return cos_elements_write(pdev->strm, pcd->elements, pdev, true, pcd->id);
781 }
782 
783 static int
cos_dict_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)784 cos_dict_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
785 {
786     stream *s = pdev->strm;
787 
788     stream_puts(s, "<<");
789     cos_elements_write(s, ((const cos_dict_t *)pco)->elements, pdev, false, object_id);
790     stream_puts(s, ">>");
791     if (pdev->PDFA)
792         stream_puts(s, "\n");
793     return 0;
794 }
795 
796 /* Write/delete definitions of named objects. */
797 /* This is a special-purpose facility for pdf_close. */
798 int
cos_dict_objects_write(const cos_dict_t * pcd,gx_device_pdf * pdev)799 cos_dict_objects_write(const cos_dict_t *pcd, gx_device_pdf *pdev)
800 {
801     const cos_dict_element_t *pcde = pcd->elements;
802 
803     for (; pcde; pcde = pcde->next)
804 	if (COS_VALUE_IS_OBJECT(&pcde->value) &&
805 	    pcde->value.contents.object->id  &&
806 	    !pcde->value.contents.object->written /* ForOPDFRead only. */)
807 	    cos_write_object(pcde->value.contents.object, pdev);
808     return 0;
809 }
810 int
cos_dict_objects_delete(cos_dict_t * pcd)811 cos_dict_objects_delete(cos_dict_t *pcd)
812 {
813     cos_dict_element_t *pcde = pcd->elements;
814 
815     /*
816      * Delete duplicate references to prevent a dual object freeing.
817      * Delete the objects' IDs so that freeing the dictionary will
818      * free them.
819      */
820     for (; pcde; pcde = pcde->next) {
821 	if (pcde->value.contents.object) {
822 	    cos_dict_element_t *pcde1 = pcde->next;
823 
824 	    for (; pcde1; pcde1 = pcde1->next)
825 		if (pcde->value.contents.object == pcde1->value.contents.object)
826 		    pcde1->value.contents.object = NULL;
827 	    pcde->value.contents.object->id = 0;
828 	}
829     }
830     return 0;
831 }
832 
833 /* Put an element in a dictionary. */
834 #define DICT_COPY_KEY 1
835 #define DICT_COPY_VALUE 2
836 #define DICT_FREE_KEY 4
837 #define DICT_COPY_ALL (DICT_COPY_KEY | DICT_COPY_VALUE | DICT_FREE_KEY)
838 static int
cos_dict_put_copy(cos_dict_t * pcd,const byte * key_data,uint key_size,const cos_value_t * pvalue,int flags)839 cos_dict_put_copy(cos_dict_t *pcd, const byte *key_data, uint key_size,
840 		  const cos_value_t *pvalue, int flags)
841 {
842     gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
843     cos_dict_element_t **ppcde = &pcd->elements;
844     cos_dict_element_t *pcde;
845     cos_dict_element_t *next;
846     cos_value_t value;
847     int code;
848 
849     while ((next = *ppcde) != 0 &&
850 	   bytes_compare(next->key.data, next->key.size, key_data, key_size)
851 	   )
852 	ppcde = &next->next;
853     if (next) {
854 	/* We're replacing an existing element. */
855 	if ((pvalue->value_type == COS_VALUE_SCALAR ||
856 	     pvalue->value_type == COS_VALUE_CONST) &&
857 	    pvalue->value_type == next->value.value_type &&
858 	    !bytes_compare(pvalue->contents.chars.data, pvalue->contents.chars.size,
859 		next->value.contents.chars.data, next->value.contents.chars.size))
860 	    return 0; /* Same as old value. */
861 	if ((pvalue->value_type == COS_VALUE_OBJECT ||
862 	     pvalue->value_type == COS_VALUE_RESOURCE) &&
863 	    pvalue->value_type == next->value.value_type &&
864 	    pvalue->contents.object == next->value.contents.object)
865 	    return 0; /* Same as old value. */
866 	code = cos_copy_element_value(&value, mem, pvalue,
867 				      (flags & DICT_COPY_VALUE) != 0);
868 	if (code < 0)
869 	    return code;
870 	if (flags & DICT_FREE_KEY)
871 	    gs_free_const_string(mem, key_data, key_size,
872 				 "cos_dict_put(new key)");
873 	cos_value_free(&next->value, COS_OBJECT(pcd),
874 		       "cos_dict_put(old value)");
875 	pcde = next;
876     } else {
877 	/* Create a new element. */
878 	byte *copied_key_data;
879 
880 	if (flags & DICT_COPY_KEY) {
881 	    copied_key_data = gs_alloc_string(mem, key_size,
882 					      "cos_dict_put(key)");
883 	    if (copied_key_data == 0)
884 		return_error(gs_error_VMerror);
885 	    memcpy(copied_key_data, key_data, key_size);
886 	} else
887 	    copied_key_data = (byte *)key_data;	/* OK to break const */
888 	pcde = gs_alloc_struct(mem, cos_dict_element_t, &st_cos_dict_element,
889 			       "cos_dict_put(element)");
890 	code = cos_copy_element_value(&value, mem, pvalue,
891 				      (flags & DICT_COPY_VALUE) != 0);
892 	if (pcde == 0 || code < 0) {
893 	    if (code >= 0)
894 		cos_uncopy_element_value(&value, mem,
895 					 (flags & DICT_COPY_VALUE) != 0);
896 	    gs_free_object(mem, pcde, "cos_dict_put(element)");
897 	    if (flags & DICT_COPY_KEY)
898 		gs_free_string(mem, copied_key_data, key_size,
899 			       "cos_dict_put(key)");
900 	    return (code < 0 ? code : gs_note_error(gs_error_VMerror));
901 	}
902 	pcde->key.data = copied_key_data;
903 	pcde->key.size = key_size;
904 	pcde->owns_key = (flags & DICT_FREE_KEY) != 0;
905 	pcde->next = next;
906 	*ppcde = pcde;
907     }
908     pcde->value = value;
909     return 0;
910 }
911 int
cos_dict_put(cos_dict_t * pcd,const byte * key_data,uint key_size,const cos_value_t * pvalue)912 cos_dict_put(cos_dict_t *pcd, const byte *key_data, uint key_size,
913 	     const cos_value_t *pvalue)
914 {
915     return cos_dict_put_copy(pcd, key_data, key_size, pvalue, DICT_COPY_ALL);
916 }
917 int
cos_dict_put_no_copy(cos_dict_t * pcd,const byte * key_data,uint key_size,const cos_value_t * pvalue)918 cos_dict_put_no_copy(cos_dict_t *pcd, const byte *key_data, uint key_size,
919 		     const cos_value_t *pvalue)
920 {
921     return cos_dict_put_copy(pcd, key_data, key_size, pvalue,
922 			     DICT_COPY_KEY | DICT_FREE_KEY);
923 }
924 int
cos_dict_put_c_key(cos_dict_t * pcd,const char * key,const cos_value_t * pvalue)925 cos_dict_put_c_key(cos_dict_t *pcd, const char *key, const cos_value_t *pvalue)
926 {
927     return cos_dict_put_copy(pcd, (const byte *)key, strlen(key), pvalue,
928 			     DICT_COPY_VALUE);
929 }
930 int
cos_dict_put_c_key_string(cos_dict_t * pcd,const char * key,const byte * data,uint size)931 cos_dict_put_c_key_string(cos_dict_t *pcd, const char *key,
932 			  const byte *data, uint size)
933 {
934     cos_value_t value;
935 
936     cos_string_value(&value, data, size);
937     return cos_dict_put_c_key(pcd, key, &value);
938 }
939 int
cos_dict_put_c_key_int(cos_dict_t * pcd,const char * key,int value)940 cos_dict_put_c_key_int(cos_dict_t *pcd, const char *key, int value)
941 {
942     char str[sizeof(int) * 8 / 3 + 3]; /* sign, rounding, 0 terminator */
943 
944     sprintf(str, "%d", value);
945     return cos_dict_put_c_key_string(pcd, key, (byte *)str, strlen(str));
946 }
947 int
cos_dict_put_c_key_bool(cos_dict_t * pcd,const char * key,bool value)948 cos_dict_put_c_key_bool(cos_dict_t *pcd, const char *key, bool value)
949 {
950     return cos_dict_put_c_key_string(pcd, key,
951 		(const byte *)(value ? "true" : "false"),
952 			      (value ? 4 : 5));
953 }
954 int
cos_dict_put_c_key_real(cos_dict_t * pcd,const char * key,floatp value)955 cos_dict_put_c_key_real(cos_dict_t *pcd, const char *key, floatp value)
956 {
957     byte str[50];		/****** ADHOC ******/
958     stream s;
959 
960     s_init(&s, NULL);
961     swrite_string(&s, str, sizeof(str));
962     pprintg1(&s, "%g", value);
963     return cos_dict_put_c_key_string(pcd, key, str, stell(&s));
964 }
965 int
cos_dict_put_c_key_floats(cos_dict_t * pcd,const char * key,const float * pf,uint size)966 cos_dict_put_c_key_floats(cos_dict_t *pcd, const char *key, const float *pf,
967 			  uint size)
968 {
969     cos_array_t *pca = cos_array_from_floats(pcd->pdev, pf, size,
970 					     "cos_dict_put_c_key_floats");
971     int code;
972 
973     if (pca == 0)
974 	return_error(gs_error_VMerror);
975     code = cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca));
976     if (code < 0)
977 	COS_FREE(pca, "cos_dict_put_c_key_floats");
978     return code;
979 }
980 int
cos_dict_put_c_key_object(cos_dict_t * pcd,const char * key,cos_object_t * pco)981 cos_dict_put_c_key_object(cos_dict_t *pcd, const char *key, cos_object_t *pco)
982 {
983     cos_value_t value;
984 
985     return cos_dict_put_c_key(pcd, key, cos_object_value(&value, pco));
986 }
987 int
cos_dict_put_string(cos_dict_t * pcd,const byte * key_data,uint key_size,const byte * value_data,uint value_size)988 cos_dict_put_string(cos_dict_t *pcd, const byte *key_data, uint key_size,
989 		    const byte *value_data, uint value_size)
990 {
991     cos_value_t cvalue;
992 
993     return cos_dict_put(pcd, key_data, key_size,
994 			cos_string_value(&cvalue, value_data, value_size));
995 }
996 int
cos_dict_put_string_copy(cos_dict_t * pcd,const char * key,const char * value)997 cos_dict_put_string_copy(cos_dict_t *pcd, const char *key, const char *value)
998 {
999     return cos_dict_put_c_key_string(pcd, key, (byte *)value, strlen(value));
1000 }
1001 int
cos_dict_put_c_strings(cos_dict_t * pcd,const char * key,const char * value)1002 cos_dict_put_c_strings(cos_dict_t *pcd, const char *key, const char *value)
1003 {
1004     cos_value_t cvalue;
1005 
1006     return cos_dict_put_c_key(pcd, key, cos_c_string_value(&cvalue, value));
1007 }
1008 
1009 /* Move all the elements from one dict to another. */
1010 int
cos_dict_move_all(cos_dict_t * pcdto,cos_dict_t * pcdfrom)1011 cos_dict_move_all(cos_dict_t *pcdto, cos_dict_t *pcdfrom)
1012 {
1013     cos_dict_element_t *pcde = pcdfrom->elements;
1014     cos_dict_element_t *head = pcdto->elements;
1015 
1016     while (pcde) {
1017 	cos_dict_element_t *next = pcde->next;
1018 
1019 	if (cos_dict_find(pcdto, pcde->key.data, pcde->key.size)) {
1020 	    /* Free the element, which has been superseded. */
1021 	    cos_dict_element_free(pcdfrom, pcde, "cos_dict_move_all_from");
1022 	} else {
1023 	    /* Move the element. */
1024 	    pcde->next = head;
1025 	    head = pcde;
1026 	}
1027 	pcde = next;
1028     }
1029     pcdto->elements = head;
1030     pcdfrom->elements = 0;
1031     return 0;
1032 }
1033 
1034 /* Look up a key in a dictionary. */
1035 const cos_value_t *
cos_dict_find(const cos_dict_t * pcd,const byte * key_data,uint key_size)1036 cos_dict_find(const cos_dict_t *pcd, const byte *key_data, uint key_size)
1037 {
1038     cos_dict_element_t *pcde = pcd->elements;
1039 
1040     for (; pcde; pcde = pcde->next)
1041 	if (!bytes_compare(key_data, key_size, pcde->key.data, pcde->key.size))
1042 	    return &pcde->value;
1043     return 0;
1044 }
1045 const cos_value_t *
cos_dict_find_c_key(const cos_dict_t * pcd,const char * key)1046 cos_dict_find_c_key(const cos_dict_t *pcd, const char *key)
1047 {
1048     return cos_dict_find(pcd, (const byte *)key, strlen(key));
1049 }
1050 
1051 /* Compare two dictionaries. */
1052 int
cos_dict_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)1053 cos_dict_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
1054 {
1055     const cos_dict_t *pcd0 = (const cos_dict_t *)pco0;
1056     const cos_dict_t *pcd1 = (const cos_dict_t *)pco1;
1057     cos_dict_element_t *pcde0 = pcd0->elements;
1058     cos_dict_element_t *pcde1 = pcd1->elements;
1059 
1060     for (; pcde1; pcde1 = pcde1->next) {
1061 	if (cos_dict_find(pcd0, pcde1->key.data, pcde1->key.size) == NULL)
1062 	    return false;
1063     }
1064     for (; pcde0; pcde0 = pcde0->next) {
1065 	const cos_value_t *v = cos_dict_find(pcd1, pcde0->key.data, pcde0->key.size);
1066 	int code;
1067 
1068 	if (v == NULL)
1069 	    return false;
1070 	code = cos_value_equal(&pcde0->value, v, pdev);
1071 	if (code < 0)
1072 	    return code;
1073 	if (!code)
1074 	    return false;
1075     }
1076     return true;
1077 }
1078 
1079 /* Process all entries in a dictionary. */
1080 int
cos_dict_forall(const cos_dict_t * pcd,void * client_data,int (* proc)(void * client_data,const byte * key_data,uint key_size,const cos_value_t * v))1081 cos_dict_forall(const cos_dict_t *pcd, void *client_data,
1082 		int (*proc)(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v))
1083 {
1084     cos_dict_element_t *pcde = pcd->elements;
1085 
1086     for (; pcde; pcde = pcde->next) {
1087 	int code = proc(client_data, pcde->key.data, pcde->key.size, &pcde->value);
1088 
1089 	if (code != 0)
1090 	    return code;
1091     }
1092     return 0;
1093 }
1094 
1095 /* Set up a parameter list that writes into a Cos dictionary. */
1096 
1097 /* We'll implement the other printers later if we have to. */
1098 static param_proc_xmit_typed(cos_param_put_typed);
1099 static const gs_param_list_procs cos_param_list_writer_procs = {
1100     cos_param_put_typed,
1101     NULL /* begin_collection */ ,
1102     NULL /* end_collection */ ,
1103     NULL /* get_next_key */ ,
1104     gs_param_request_default,
1105     gs_param_requested_default
1106 };
1107 static int
cos_param_put_typed(gs_param_list * plist,gs_param_name pkey,gs_param_typed_value * pvalue)1108 cos_param_put_typed(gs_param_list * plist, gs_param_name pkey,
1109 		    gs_param_typed_value * pvalue)
1110 {
1111     cos_param_list_writer_t *const pclist =
1112 	(cos_param_list_writer_t *)plist;
1113     gx_device_pdf *pdev = pclist->pcd->pdev;
1114     gs_memory_t *mem = pclist->memory;
1115     cos_value_t value;
1116     cos_array_t *pca;
1117     int key_len = strlen(pkey);
1118     byte key_chars[100];		/****** ADHOC ******/
1119     int code;
1120 
1121     if (key_len > sizeof(key_chars) - 1)
1122 	return_error(gs_error_limitcheck);
1123     switch (pvalue->type) {
1124     default: {
1125 	param_printer_params_t ppp;
1126 	printer_param_list_t pplist;
1127 	stream s;
1128 	int len, skip;
1129 	byte *str;
1130 
1131 	s_init(&s, NULL);
1132         ppp = param_printer_params_default;
1133 	ppp.prefix = ppp.suffix = ppp.item_prefix = ppp.item_suffix = 0;
1134 	ppp.print_ok = pclist->print_ok;
1135 	s_init_param_printer(&pplist, &ppp, &s);
1136 	swrite_position_only(&s);
1137 	param_write_typed((gs_param_list *)&pplist, "", pvalue);
1138 	len = stell(&s);
1139 	str = gs_alloc_string(mem, len, "cos_param_put(string)");
1140 	if (str == 0)
1141 	    return_error(gs_error_VMerror);
1142 	swrite_string(&s, str, len);
1143 	param_write_typed((gs_param_list *)&pplist, "", pvalue);
1144 	/*
1145 	 * The string starts with an initial / or /<space>, which
1146 	 * we need to remove.
1147 	 */
1148 	skip = (str[1] == ' ' ? 2 : 1);
1149 	memmove(str, str + skip, len - skip);
1150 	str = gs_resize_string(mem, str, len, len - skip,
1151 			       "cos_param_put(string)");
1152 	cos_string_value(&value, str, len - skip);
1153     }
1154 	break;
1155     case gs_param_type_int_array: {
1156 	uint i;
1157 
1158 	pca = cos_array_alloc(pdev, "cos_param_put(array)");
1159 	if (pca == 0)
1160 	    return_error(gs_error_VMerror);
1161 	for (i = 0; i < pvalue->value.ia.size; ++i)
1162 	    CHECK(cos_array_add_int(pca, pvalue->value.ia.data[i]));
1163     }
1164     av:
1165 	cos_object_value(&value, COS_OBJECT(pca));
1166 	break;
1167     case gs_param_type_float_array: {
1168 	uint i;
1169 
1170 	pca = cos_array_alloc(pdev, "cos_param_put(array)");
1171 	if (pca == 0)
1172 	    return_error(gs_error_VMerror);
1173 	for (i = 0; i < pvalue->value.ia.size; ++i)
1174 	    CHECK(cos_array_add_real(pca, pvalue->value.fa.data[i]));
1175     }
1176 	goto av;
1177     case gs_param_type_string_array:
1178     case gs_param_type_name_array:
1179 	/****** NYI ******/
1180 	return_error(gs_error_typecheck);
1181     }
1182     memcpy(key_chars + 1, pkey, key_len);
1183     key_chars[0] = '/';
1184     return cos_dict_put_no_copy(pclist->pcd, key_chars, key_len + 1, &value);
1185 }
1186 
1187 int
cos_param_list_writer_init(cos_param_list_writer_t * pclist,cos_dict_t * pcd,int print_ok)1188 cos_param_list_writer_init(cos_param_list_writer_t *pclist, cos_dict_t *pcd,
1189 			   int print_ok)
1190 {
1191     gs_param_list_init((gs_param_list *)pclist, &cos_param_list_writer_procs,
1192 		       COS_OBJECT_MEMORY(pcd));
1193     pclist->pcd = pcd;
1194     pclist->print_ok = print_ok;
1195     return 0;
1196 }
1197 
1198 /* ------ Streams ------ */
1199 
1200 static cos_proc_release(cos_stream_release);
1201 static cos_proc_write(cos_stream_write);
1202 static cos_proc_equal(cos_stream_equal);
1203 const cos_object_procs_t cos_stream_procs = {
1204     cos_stream_release, cos_stream_write, cos_stream_equal
1205 };
1206 
1207 cos_stream_t *
cos_stream_alloc(gx_device_pdf * pdev,client_name_t cname)1208 cos_stream_alloc(gx_device_pdf *pdev, client_name_t cname)
1209 {
1210     gs_memory_t *mem = pdev->pdf_memory;
1211     cos_stream_t *pcs =
1212 	gs_alloc_struct(mem, cos_stream_t, &st_cos_object, cname);
1213 
1214     cos_object_init((cos_object_t *)pcs, pdev, &cos_stream_procs);
1215     return pcs;
1216 }
1217 
1218 static void
cos_stream_release(cos_object_t * pco,client_name_t cname)1219 cos_stream_release(cos_object_t *pco, client_name_t cname)
1220 {
1221     gs_memory_t *mem = cos_object_memory(pco);
1222     cos_stream_t *const pcs = (cos_stream_t *)pco;
1223     cos_stream_piece_t *cur;
1224     cos_stream_piece_t *next;
1225 
1226     for (cur = pcs->pieces; cur; cur = next) {
1227 	next = cur->next;
1228 	gs_free_object(mem, cur, cname);
1229     }
1230     pcs->pieces = 0;
1231     cos_dict_release(pco, cname);
1232 }
1233 
1234 static int
cos_stream_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)1235 cos_stream_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
1236 {
1237     const cos_stream_t *pcs0 = (const cos_stream_t *)pco0;
1238     const cos_stream_t *pcs1 = (const cos_stream_t *)pco1;
1239     bool result = false;
1240     int code;
1241 
1242     code = cos_dict_equal(pco0, pco1, pdev);
1243     if (code < 0)
1244 	return code;
1245     if (!code)
1246 	return false;
1247     {
1248 	/* fixme : this assumes same segmentation for both streams.
1249 	   In general it is not true. */
1250 	FILE *sfile = pdev->streams.file;
1251 	cos_stream_piece_t *pcsp0 = pcs0->pieces, *pcsp1 = pcs1->pieces;
1252 	long position_save = ftell(sfile);
1253 
1254 	for (; pcsp0 && pcsp1; pcsp0 = pcsp0->next, pcsp1 = pcsp1->next) {
1255 	    long position0 = pcsp0->position;
1256 	    long position1 = pcsp1->position;
1257 	    uint size0 = pcsp0->size;
1258 	    uint size1 = pcsp1->size;
1259 	    byte buf0[512], buf1[sizeof(buf0)];
1260 
1261 	    if (size0 != size1)
1262 		goto notequal;
1263 	    for(; size0; position0 += size1, position1 += size1, size0 -= size1) {
1264 		size1 = min(sizeof(buf0), size0);
1265 		fseek(sfile, position0, SEEK_SET);
1266 		if (fread(buf0, 1, size1, sfile) != size1) {
1267 		    result = gs_note_error(gs_error_ioerror);
1268 		    goto notequal;
1269 		}
1270 		fseek(sfile, position1, SEEK_SET);
1271 		if (fread(buf1, 1, size1, sfile) != size1) {
1272 		    result = gs_note_error(gs_error_ioerror);
1273 		    goto notequal;
1274 		}
1275 		if (memcmp(buf0, buf1, size1))
1276 		    goto notequal;
1277 	    }
1278 	}
1279 	if (pcsp0 || pcsp1)
1280 	    goto notequal;
1281 	result = true;
1282 notequal:
1283 	fseek(sfile, position_save, SEEK_SET);
1284 	return result;
1285     }
1286 }
1287 
1288 /* Find the total length of a stream. */
1289 long
cos_stream_length(const cos_stream_t * pcs)1290 cos_stream_length(const cos_stream_t *pcs)
1291 {
1292     return pcs->length;
1293 }
1294 
1295 /* Write the (dictionary) elements of a stream. */
1296 /* (This procedure is exported.) */
1297 int
cos_stream_elements_write(const cos_stream_t * pcs,gx_device_pdf * pdev)1298 cos_stream_elements_write(const cos_stream_t *pcs, gx_device_pdf *pdev)
1299 {
1300     return cos_elements_write(pdev->strm, pcs->elements, pdev, true, pcs->id);
1301 }
1302 
1303 /* Write the contents of a stream.  (This procedure is exported.) */
1304 int
cos_stream_contents_write(const cos_stream_t * pcs,gx_device_pdf * pdev)1305 cos_stream_contents_write(const cos_stream_t *pcs, gx_device_pdf *pdev)
1306 {
1307     stream *s = pdev->strm;
1308     cos_stream_piece_t *pcsp;
1309     cos_stream_piece_t *last;
1310     cos_stream_piece_t *next;
1311     FILE *sfile = pdev->streams.file;
1312     long end_pos;
1313     bool same_file = (pdev->sbstack_depth > 0);
1314     int code;
1315     stream_arcfour_state sarc4, *ss = NULL;
1316 
1317     if (pdev->KeyLength) {
1318 	code = pdf_encrypt_init(pdev, pcs->id, &sarc4);
1319 	if (code < 0)
1320 	    return code;
1321 	ss = &sarc4;
1322     }
1323     sflush(s);
1324     sflush(pdev->streams.strm);
1325 
1326     /* Reverse the elements temporarily. */
1327     for (pcsp = pcs->pieces, last = NULL; pcsp; pcsp = next)
1328 	next = pcsp->next, pcsp->next = last, last = pcsp;
1329     for (pcsp = last, code = 0; pcsp && code >= 0; pcsp = pcsp->next) {
1330 	if (same_file)
1331 	    pdf_copy_data_safe(s, sfile, pcsp->position, pcsp->size);
1332 	else {
1333 	    end_pos = ftell(sfile);
1334 	    fseek(sfile, pcsp->position, SEEK_SET);
1335 	    pdf_copy_data(s, sfile, pcsp->size, ss);
1336 	    fseek(sfile, end_pos, SEEK_SET);
1337 	}
1338     }
1339     /* Reverse the elements back. */
1340     for (pcsp = last, last = NULL; pcsp; pcsp = next)
1341 	next = pcsp->next, pcsp->next = last, last = pcsp;
1342 
1343     return code;
1344 }
1345 
1346 static int
cos_stream_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)1347 cos_stream_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
1348 {
1349     stream *s = pdev->strm;
1350     const cos_stream_t *const pcs = (const cos_stream_t *)pco;
1351     int code;
1352 
1353     if (pcs->input_strm != NULL) {
1354 	stream *s = pco->input_strm;
1355 	int status = s_close_filters(&s, NULL);
1356 
1357 	if (status < 0)
1358 	    return_error(gs_error_ioerror);
1359 	/* We have to break const here to clear the input_strm. */
1360 	((cos_object_t *)pco)->input_strm = 0;
1361     }
1362     stream_puts(s, "<<");
1363     cos_elements_write(s, pcs->elements, pdev, false, object_id);
1364     pprintld1(s, "/Length %ld>>stream\n", cos_stream_length(pcs));
1365     code = cos_stream_contents_write(pcs, pdev);
1366     stream_puts(s, "\nendstream\n");
1367 
1368     return code;
1369 }
1370 
1371 /* Return a stream's dictionary (just a cast). */
1372 cos_dict_t *
cos_stream_dict(cos_stream_t * pcs)1373 cos_stream_dict(cos_stream_t *pcs)
1374 {
1375     return (cos_dict_t *)pcs;
1376 }
1377 
1378 /* Add a contents piece to a stream object: size bytes just written on */
1379 /* streams.strm. */
1380 int
cos_stream_add(cos_stream_t * pcs,uint size)1381 cos_stream_add(cos_stream_t *pcs, uint size)
1382 {
1383     gx_device_pdf *pdev = pcs->pdev;
1384     stream *s = pdev->streams.strm;
1385     long position = stell(s);
1386     cos_stream_piece_t *prev = pcs->pieces;
1387 
1388     /* Check for consecutive writing -- just an optimization. */
1389     if (prev != 0 && prev->position + prev->size + size == position) {
1390 	prev->size += size;
1391     } else {
1392 	gs_memory_t *mem = pdev->pdf_memory;
1393 	cos_stream_piece_t *pcsp =
1394 	    gs_alloc_struct(mem, cos_stream_piece_t, &st_cos_stream_piece,
1395 			    "cos_stream_add");
1396 
1397 	if (pcsp == 0)
1398 	    return_error(gs_error_VMerror);
1399 	pcsp->position = position - size;
1400 	pcsp->size = size;
1401 	pcsp->next = pcs->pieces;
1402 	pcs->pieces = pcsp;
1403     }
1404     pcs->length += size;
1405     return 0;
1406 }
1407 
1408 /* Add bytes to a stream object. */
1409 int
cos_stream_add_bytes(cos_stream_t * pcs,const byte * data,uint size)1410 cos_stream_add_bytes(cos_stream_t *pcs, const byte *data, uint size)
1411 {
1412     stream_write(pcs->pdev->streams.strm, data, size);
1413     return cos_stream_add(pcs, size);
1414 }
1415 
1416 /* Add the contents of a stream to a stream object. */
1417 int
cos_stream_add_stream_contents(cos_stream_t * pcs,stream * s)1418 cos_stream_add_stream_contents(cos_stream_t *pcs, stream *s)
1419 {
1420     int code = 0;
1421     byte sbuff[200];	/* arbitrary */
1422     uint cnt;
1423     int status = sseek(s, 0);
1424 
1425     if (status < 0)
1426 	return_error(gs_error_ioerror);
1427     do {
1428 	status = sgets(s, sbuff, sizeof(sbuff), &cnt);
1429 
1430 	if (cnt == 0) {
1431 	    if (status == EOFC)
1432 		break;
1433 	    return_error(gs_error_ioerror);
1434 	}
1435     } while ((code = cos_stream_add_bytes(pcs, sbuff, cnt)) >= 0);
1436     return code;
1437 }
1438 
1439 /* Release the last contents piece of a stream object. */
1440 /* Warning : this function can't release pieces if another stream is written after them. */
1441 int
cos_stream_release_pieces(cos_stream_t * pcs)1442 cos_stream_release_pieces(cos_stream_t *pcs)
1443 {
1444     gx_device_pdf *pdev = pcs->pdev;
1445     stream *s = pdev->streams.strm;
1446     long position = stell(s), position0 = position;
1447     gs_memory_t *mem = cos_object_memory((cos_object_t *)pcs);
1448 
1449     while (pcs->pieces != NULL &&
1450 		position == pcs->pieces->position + pcs->pieces->size) {
1451 	cos_stream_piece_t *p = pcs->pieces;
1452 
1453 	position -= p->size;
1454 	pcs->pieces = p->next;
1455 	gs_free_object(mem, p, "cos_stream_release_pieces");
1456     }
1457     if (position0 != position)
1458 	if (sseek(s, position) < 0)
1459 	    return_error(gs_error_ioerror);
1460     return 0;
1461 }
1462 
1463 /* Create a stream that writes into a Cos stream. */
1464 /* Closing the stream will free it. */
1465 /* Note that this is not a filter. */
1466 typedef struct cos_write_stream_state_s {
1467     stream_state_common;
1468     cos_stream_t *pcs;
1469     gx_device_pdf *pdev;
1470     stream *s;			/* pointer back to stream */
1471     stream *target;		/* use this instead of strm */
1472 } cos_write_stream_state_t;
1473 gs_private_st_suffix_add4(st_cos_write_stream_state, cos_write_stream_state_t,
1474 			  "cos_write_stream_state_t",
1475 			  cos_ws_state_enum_ptrs, cos_ws_state_reloc_ptrs,
1476 			  st_stream_state, pcs, pdev, s, target);
1477 
1478 static int
cos_write_stream_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)1479 cos_write_stream_process(stream_state * st, stream_cursor_read * pr,
1480 			 stream_cursor_write * ignore_pw, bool last)
1481 {
1482     uint count = pr->limit - pr->ptr;
1483     cos_write_stream_state_t *ss = (cos_write_stream_state_t *)st;
1484     gx_device_pdf *pdev = ss->pdev;
1485     stream *target = ss->target;
1486     long start_pos = stell(pdev->streams.strm);
1487     int code;
1488 
1489     stream_write(target, pr->ptr + 1, count);
1490     pr->ptr = pr->limit;
1491     sflush(target);
1492     code = cos_stream_add(ss->pcs, (uint)(stell(pdev->streams.strm) - start_pos));
1493     return (code < 0 ? ERRC : 0);
1494 }
1495 static int
cos_write_stream_close(stream * s)1496 cos_write_stream_close(stream *s)
1497 {
1498     cos_write_stream_state_t *ss = (cos_write_stream_state_t *)s->state;
1499     int status;
1500 
1501     sflush(s);
1502     status = s_close_filters(&ss->target, ss->pdev->streams.strm);
1503     return (status < 0 ? status : s_std_close(s));
1504 }
1505 
1506 static const stream_procs cos_s_procs = {
1507     s_std_noavailable, s_std_noseek, s_std_write_reset,
1508     s_std_write_flush, cos_write_stream_close, cos_write_stream_process
1509 };
1510 static const stream_template cos_write_stream_template = {
1511     &st_cos_write_stream_state, 0, cos_write_stream_process, 1, 1
1512 };
1513 stream *
cos_write_stream_alloc(cos_stream_t * pcs,gx_device_pdf * pdev,client_name_t cname)1514 cos_write_stream_alloc(cos_stream_t *pcs, gx_device_pdf *pdev,
1515 		       client_name_t cname)
1516 {
1517     gs_memory_t *mem = pdev->pdf_memory;
1518     stream *s = s_alloc(mem, cname);
1519     cos_write_stream_state_t *ss = (cos_write_stream_state_t *)
1520 	s_alloc_state(mem, &st_cos_write_stream_state, cname);
1521 #define CWS_BUF_SIZE 512	/* arbitrary */
1522     byte *buf = gs_alloc_bytes(mem, CWS_BUF_SIZE, cname);
1523 
1524     if (s == 0 || ss == 0 || buf == 0)
1525 	goto fail;
1526     ss->template = &cos_write_stream_template;
1527     ss->pcs = pcs;
1528     ss->pdev = pdev;
1529     ss->s = s;
1530     ss->target = pdev->streams.strm; /* not s->strm */
1531     s_std_init(s, buf, CWS_BUF_SIZE, &cos_s_procs, s_mode_write);
1532     s->state = (stream_state *)ss;
1533     return s;
1534 #undef CWS_BUF_SIZE
1535  fail:
1536     gs_free_object(mem, buf, cname);
1537     gs_free_object(mem, ss, cname);
1538     gs_free_object(mem, s, cname);
1539     return 0;
1540 }
1541 
1542 /* Get cos stream from pipeline. */
1543 cos_stream_t *
cos_stream_from_pipeline(stream * s)1544 cos_stream_from_pipeline(stream *s)
1545 {
1546     cos_write_stream_state_t *ss;
1547 
1548     while(s->procs.process != cos_s_procs.process)
1549 	s = s->strm;
1550     ss = (cos_write_stream_state_t *)s->state;
1551     return ss->pcs;
1552 }
1553 
1554 /* Get cos write stream from pipeline. */
1555 stream *
cos_write_stream_from_pipeline(stream * s)1556 cos_write_stream_from_pipeline(stream *s)
1557 {
1558     while(s->procs.process != cos_s_procs.process)
1559 	s = s->strm;
1560     return s;
1561 }
1562 
1563