1 /*
2  * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it either under the terms of the GNU Lesser General Public
6  * License version 2.1 as published by the Free Software Foundation
7  * (the "LGPL") or, at your option, under the terms of the Mozilla
8  * Public License Version 1.1 (the "MPL"). If you do not alter this
9  * notice, a recipient may use your version of this file under either
10  * the MPL or the LGPL.
11  *
12  * You should have received a copy of the LGPL along with this library
13  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15  * You should have received a copy of the MPL along with this library
16  * in the file COPYING-MPL-1.1
17  *
18  * The contents of this file are subject to the Mozilla Public License
19  * Version 1.1 (the "License"); you may not use this file except in
20  * compliance with the License. You may obtain a copy of the License at
21  * http://www.mozilla.org/MPL/
22  *
23  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25  * the specific language governing rights and limitations.
26  *
27  * The Original Code is the cairo graphics library.
28  *
29  * The Initial Developer of the Original Code is Chris Wilson.
30  *
31  * Contributor(s):
32  *	Chris Wilson <chris@chris-wilson.co.uk>
33  */
34 
35 #include "config.h"
36 
37 #include "cairo-script-private.h"
38 
39 #include <limits.h> /* INT_MAX */
40 #include <string.h>
41 
42 csi_status_t
csi_array_new(csi_t * ctx,csi_integer_t initial_size,csi_object_t * obj)43 csi_array_new (csi_t *ctx,
44 	       csi_integer_t initial_size,
45 	       csi_object_t *obj)
46 
47 {
48     csi_array_t *array;
49 
50     if (ctx->free_array == NULL ||
51 	ctx->free_array->stack.size <= initial_size)
52     {
53 	csi_status_t status;
54 
55 	array = _csi_slab_alloc (ctx, sizeof (csi_array_t));
56 	if (_csi_unlikely (array == NULL))
57 	    return _csi_error (CSI_STATUS_NO_MEMORY);
58 
59 	status = _csi_stack_init (ctx, &array->stack,
60 				  initial_size ? initial_size : 32);
61 	if (_csi_unlikely (status)) {
62 	    _csi_slab_free (ctx, array, sizeof (csi_array_t));
63 	    return status;
64 	}
65     } else {
66 	array = ctx->free_array;
67 	ctx->free_array = NULL;
68     }
69 
70     array->base.type = CSI_OBJECT_TYPE_ARRAY;
71     array->base.ref = 1;
72 
73     obj->type = CSI_OBJECT_TYPE_ARRAY;
74     obj->datum.array = array;
75 
76     return CSI_STATUS_SUCCESS;
77 }
78 
79 csi_status_t
csi_array_put(csi_t * ctx,csi_array_t * array,csi_integer_t elem,csi_object_t * value)80 csi_array_put (csi_t *ctx,
81 	       csi_array_t *array,
82 	       csi_integer_t elem,
83 	       csi_object_t *value)
84 {
85     if (_csi_unlikely (elem < 0))
86 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
87 
88     if (_csi_unlikely (elem >= array->stack.len)) {
89 	csi_status_t status;
90 
91 	status = _csi_stack_grow (ctx, &array->stack, elem + 1);
92 	if (_csi_unlikely (status))
93 	    return status;
94 
95 	memset (array->stack.objects + array->stack.len,
96 		0, (elem - array->stack.len + 1) * sizeof (csi_object_t));
97 	array->stack.len = elem + 1;
98     }
99 
100     csi_object_free (ctx, &array->stack.objects[elem]);
101     array->stack.objects[elem] = *csi_object_reference (value);
102 
103     return CSI_STATUS_SUCCESS;
104 }
105 
106 csi_status_t
csi_array_get(csi_t * ctx,csi_array_t * array,csi_integer_t elem,csi_object_t * value)107 csi_array_get (csi_t *ctx,
108 	       csi_array_t *array,
109 	       csi_integer_t elem,
110 	       csi_object_t *value)
111 {
112     if (_csi_unlikely (elem < 0))
113 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
114 
115     if (_csi_unlikely (elem > array->stack.len))
116 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
117 
118     *value = array->stack.objects[elem];
119     return CSI_STATUS_SUCCESS;
120 }
121 
122 csi_status_t
csi_array_append(csi_t * ctx,csi_array_t * array,csi_object_t * obj)123 csi_array_append (csi_t *ctx,
124 		  csi_array_t *array,
125 		  csi_object_t *obj)
126 {
127     return _csi_stack_push (ctx, &array->stack, csi_object_reference (obj));
128 }
129 
130 inline csi_status_t
_csi_array_execute(csi_t * ctx,csi_array_t * array)131 _csi_array_execute (csi_t *ctx, csi_array_t *array)
132 {
133     csi_integer_t i;
134     csi_status_t status;
135 
136     if (_csi_unlikely (array->stack.len == 0))
137 	return CSI_STATUS_SUCCESS;
138 
139     for (i = 0; i < array->stack.len; i++) {
140 	csi_object_t *obj = &array->stack.objects[i];
141 
142 	if (obj->type & CSI_OBJECT_ATTR_EXECUTABLE) {
143 	    if (obj->type == (CSI_OBJECT_TYPE_ARRAY |
144 			      CSI_OBJECT_ATTR_EXECUTABLE))
145 	    {
146 		status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
147 	    }
148 	    else
149 		status = csi_object_execute (ctx, &array->stack.objects[i]);
150 	} else
151 	    status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
152 	if (_csi_unlikely (status))
153 	    return status;
154     }
155 
156     return CSI_STATUS_SUCCESS;
157 }
158 
159 void
csi_array_free(csi_t * ctx,csi_array_t * array)160 csi_array_free (csi_t *ctx, csi_array_t *array)
161 {
162 #if CSI_DEBUG_MALLOC
163     _csi_stack_fini (ctx, &array->stack);
164     _csi_slab_free (ctx, array, sizeof (csi_array_t));
165 #else
166     csi_integer_t n;
167 
168     for (n = 0; n < array->stack.len; n++)
169 	csi_object_free (ctx, &array->stack.objects[n]);
170     array->stack.len = 0;
171 
172     if (ctx->free_array != NULL) {
173 	if (array->stack.size > ctx->free_array->stack.size) {
174 	    csi_array_t *tmp = ctx->free_array;
175 	    ctx->free_array = array;
176 	    array = tmp;
177 	}
178 
179 	_csi_stack_fini (ctx, &array->stack);
180 	_csi_slab_free (ctx, array, sizeof (csi_array_t));
181     } else
182 	ctx->free_array = array;
183 #endif
184 }
185 
186 static cairo_bool_t
_dictionary_name_equal(const void * _a,const void * _b)187 _dictionary_name_equal (const void *_a, const void *_b)
188 {
189     return TRUE;
190 }
191 
192 csi_status_t
csi_dictionary_new(csi_t * ctx,csi_object_t * obj)193 csi_dictionary_new (csi_t *ctx,
194 		    csi_object_t *obj)
195 
196 {
197     csi_dictionary_t *dict;
198 
199     if (ctx->free_dictionary != NULL) {
200 	dict = ctx->free_dictionary;
201 	ctx->free_dictionary = NULL;
202     } else {
203 	csi_status_t status;
204 
205 	dict = _csi_slab_alloc (ctx, sizeof (csi_dictionary_t));
206 	if (_csi_unlikely (dict == NULL))
207 	    return _csi_error (CSI_STATUS_NO_MEMORY);
208 
209 	status = _csi_hash_table_init (&dict->hash_table,
210 				       _dictionary_name_equal);
211 	if (_csi_unlikely (status)) {
212 	    _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
213 	    return status;
214 	}
215     }
216 
217     dict->base.type = CSI_OBJECT_TYPE_DICTIONARY;
218     dict->base.ref = 1;
219 
220     obj->type = CSI_OBJECT_TYPE_DICTIONARY;
221     obj->datum.dictionary = dict;
222 
223     return CSI_STATUS_SUCCESS;
224 }
225 
226 struct _dictionary_entry_pluck {
227     csi_t *ctx;
228     csi_hash_table_t *hash_table;
229 };
230 
231 static void
_dictionary_entry_pluck(void * entry,void * data)232 _dictionary_entry_pluck (void *entry, void *data)
233 {
234     csi_dictionary_entry_t *dict_entry;
235     struct _dictionary_entry_pluck *pluck_data;
236 
237     dict_entry = entry;
238     pluck_data = data;
239 
240     _csi_hash_table_remove (pluck_data->hash_table, entry);
241     csi_object_free (pluck_data->ctx, &dict_entry->value);
242     _csi_slab_free (pluck_data->ctx, entry, sizeof (csi_dictionary_entry_t));
243 }
244 
245 void
csi_dictionary_free(csi_t * ctx,csi_dictionary_t * dict)246 csi_dictionary_free (csi_t *ctx,
247 		     csi_dictionary_t *dict)
248 {
249     struct _dictionary_entry_pluck data;
250 
251     data.ctx = ctx;
252     data.hash_table = &dict->hash_table;
253     _csi_hash_table_foreach (&dict->hash_table,
254 			     _dictionary_entry_pluck,
255 			     &data);
256 
257 #if CSI_DEBUG_MALLOC
258     _csi_hash_table_fini (&dict->hash_table);
259     _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
260 #else
261     if (ctx->free_dictionary != NULL) {
262 	_csi_hash_table_fini (&dict->hash_table);
263 	_csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
264     } else
265 	ctx->free_dictionary = dict;
266 #endif
267 }
268 
269 csi_status_t
csi_dictionary_put(csi_t * ctx,csi_dictionary_t * dict,csi_name_t name,csi_object_t * value)270 csi_dictionary_put (csi_t *ctx,
271 		    csi_dictionary_t *dict,
272 		    csi_name_t name,
273 		    csi_object_t *value)
274 {
275     csi_dictionary_entry_t *entry;
276     csi_status_t status;
277 
278     entry = _csi_hash_table_lookup (&dict->hash_table,
279 				    (csi_hash_entry_t *) &name);
280     if (entry != NULL) {
281 	/* replace the existing entry */
282 	csi_object_free (ctx, &entry->value);
283 	entry->value = *csi_object_reference (value);
284 	return CSI_STATUS_SUCCESS;
285     }
286 
287     entry = _csi_slab_alloc (ctx, sizeof (*entry));
288     if (_csi_unlikely (entry == NULL))
289 	return _csi_error (CSI_STATUS_NO_MEMORY);
290 
291     entry->hash_entry.hash = name;
292     status = _csi_hash_table_insert (&dict->hash_table, &entry->hash_entry);
293     if (_csi_unlikely (status)) {
294 	_csi_slab_free (ctx, entry, sizeof (*entry));
295 	return status;
296     }
297 
298     entry->value = *csi_object_reference (value);
299 
300     return CSI_STATUS_SUCCESS;
301 }
302 
303 csi_status_t
csi_dictionary_get(csi_t * ctx,csi_dictionary_t * dict,csi_name_t name,csi_object_t * value)304 csi_dictionary_get (csi_t *ctx,
305 		    csi_dictionary_t *dict,
306 		    csi_name_t name,
307 		    csi_object_t *value)
308 {
309     csi_dictionary_entry_t *entry;
310 
311     entry = _csi_hash_table_lookup (&dict->hash_table,
312 				    (csi_hash_entry_t *) &name);
313     if (_csi_unlikely (entry == NULL))
314 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
315 
316     *value = entry->value;
317     return CSI_STATUS_SUCCESS;
318 }
319 
320 csi_boolean_t
csi_dictionary_has(csi_dictionary_t * dict,csi_name_t name)321 csi_dictionary_has (csi_dictionary_t *dict,
322 		    csi_name_t name)
323 {
324     return _csi_hash_table_lookup (&dict->hash_table,
325 				   (csi_hash_entry_t *) &name) != NULL;
326 }
327 
328 void
csi_dictionary_remove(csi_t * ctx,csi_dictionary_t * dict,csi_name_t name)329 csi_dictionary_remove (csi_t *ctx,
330 		       csi_dictionary_t *dict,
331 		       csi_name_t name)
332 {
333     csi_dictionary_entry_t *entry;
334 
335     entry = _csi_hash_table_lookup (&dict->hash_table,
336 				    (csi_hash_entry_t *) &name);
337     if (entry != NULL) {
338 	_csi_hash_table_remove (&dict->hash_table, &entry->hash_entry);
339 	csi_object_free (ctx, &entry->value);
340 	_csi_slab_free (ctx, entry, sizeof (csi_dictionary_entry_t));
341     }
342 }
343 
344 csi_status_t
csi_matrix_new(csi_t * ctx,csi_object_t * obj)345 csi_matrix_new (csi_t *ctx,
346 		csi_object_t *obj)
347 {
348     csi_matrix_t *matrix;
349 
350     matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
351     if (_csi_unlikely (matrix == NULL))
352 	return _csi_error (CSI_STATUS_NO_MEMORY);
353 
354     matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
355     matrix->base.ref = 1;
356     cairo_matrix_init_identity (&matrix->matrix);
357 
358     obj->type = CSI_OBJECT_TYPE_MATRIX;
359     obj->datum.matrix = matrix;
360 
361     return CSI_STATUS_SUCCESS;
362 }
363 
364 csi_status_t
csi_matrix_new_from_array(csi_t * ctx,csi_object_t * obj,csi_array_t * array)365 csi_matrix_new_from_array (csi_t *ctx,
366 			   csi_object_t *obj,
367 			   csi_array_t *array)
368 {
369     csi_matrix_t *matrix;
370 
371     if (_csi_unlikely (array->stack.len != 6))
372 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
373 
374     matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
375     if (_csi_unlikely (matrix == NULL))
376 	return _csi_error (CSI_STATUS_NO_MEMORY);
377 
378     matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
379     matrix->base.ref = 1;
380     cairo_matrix_init (&matrix->matrix,
381 		       csi_number_get_value (&array->stack.objects[0]),
382 		       csi_number_get_value (&array->stack.objects[1]),
383 		       csi_number_get_value (&array->stack.objects[2]),
384 		       csi_number_get_value (&array->stack.objects[3]),
385 		       csi_number_get_value (&array->stack.objects[4]),
386 		       csi_number_get_value (&array->stack.objects[5]));
387 
388     obj->type = CSI_OBJECT_TYPE_MATRIX;
389     obj->datum.matrix = matrix;
390 
391     return CSI_STATUS_SUCCESS;
392 }
393 
394 csi_status_t
csi_matrix_new_from_matrix(csi_t * ctx,csi_object_t * obj,const cairo_matrix_t * m)395 csi_matrix_new_from_matrix (csi_t *ctx,
396 			    csi_object_t *obj,
397 			    const cairo_matrix_t *m)
398 {
399     csi_matrix_t *matrix;
400 
401     matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
402     if (_csi_unlikely (matrix == NULL))
403 	return _csi_error (CSI_STATUS_NO_MEMORY);
404 
405     matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
406     matrix->base.ref = 1;
407     matrix->matrix = *m;
408 
409     obj->type = CSI_OBJECT_TYPE_MATRIX;
410     obj->datum.matrix = matrix;
411 
412     return CSI_STATUS_SUCCESS;
413 }
414 
415 csi_status_t
csi_matrix_new_from_values(csi_t * ctx,csi_object_t * obj,double v[6])416 csi_matrix_new_from_values (csi_t *ctx,
417 			    csi_object_t *obj,
418 			    double v[6])
419 {
420     csi_matrix_t *matrix;
421 
422     matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
423     if (_csi_unlikely (matrix == NULL))
424 	return _csi_error (CSI_STATUS_NO_MEMORY);
425 
426     matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
427     matrix->base.ref = 1;
428     cairo_matrix_init (&matrix->matrix, v[0], v[1], v[2], v[3], v[4], v[5]);
429 
430     obj->type = CSI_OBJECT_TYPE_MATRIX;
431     obj->datum.matrix = matrix;
432 
433     return CSI_STATUS_SUCCESS;
434 }
435 
436 void
csi_matrix_free(csi_t * ctx,csi_matrix_t * obj)437 csi_matrix_free (csi_t *ctx,
438 		 csi_matrix_t *obj)
439 {
440     _csi_slab_free (ctx, obj, sizeof (csi_matrix_t));
441 }
442 
443 
444 csi_status_t
csi_name_new(csi_t * ctx,csi_object_t * obj,const char * str,int len)445 csi_name_new (csi_t *ctx,
446 	      csi_object_t *obj,
447 	      const char *str,
448 	      int len)
449 {
450     csi_status_t status;
451 
452     status = _csi_intern_string (ctx, &str, len);
453     if (_csi_unlikely (status))
454 	return status;
455 
456     obj->type = CSI_OBJECT_TYPE_NAME;
457     obj->datum.name = (csi_name_t) str;
458 
459     return CSI_STATUS_SUCCESS;
460 }
461 
462 csi_status_t
csi_name_new_static(csi_t * ctx,csi_object_t * obj,const char * str)463 csi_name_new_static (csi_t *ctx,
464 		     csi_object_t *obj,
465 		     const char *str)
466 {
467     csi_status_t status;
468 
469     status = _csi_intern_string (ctx, &str, strlen (str));
470     if (_csi_unlikely (status))
471 	return status;
472 
473     obj->type = CSI_OBJECT_TYPE_NAME;
474     obj->datum.name = (csi_name_t) str;
475 
476     return CSI_STATUS_SUCCESS;
477 }
478 
479 csi_status_t
csi_string_new(csi_t * ctx,csi_object_t * obj,const char * str,int len)480 csi_string_new (csi_t *ctx,
481 		csi_object_t *obj,
482 		const char *str,
483 		int len)
484 {
485     csi_string_t *string;
486 
487     if (len < 0)
488 	len = strlen (str);
489     if (_csi_unlikely (len >= INT_MAX))
490 	return _csi_error (CSI_STATUS_NO_MEMORY);
491 
492     if (ctx->free_string == NULL || ctx->free_string->len <= len) {
493 	string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
494 	if (_csi_unlikely (string == NULL))
495 	    return _csi_error (CSI_STATUS_NO_MEMORY);
496 
497 	string->string = _csi_alloc (ctx, len + 1);
498 	if (_csi_unlikely (string->string == NULL)) {
499 	    _csi_slab_free (ctx, string, sizeof (csi_string_t));
500 	    return _csi_error (CSI_STATUS_NO_MEMORY);
501 	}
502     } else {
503 	string = ctx->free_string;
504 	ctx->free_string = NULL;
505     }
506 
507     if (str != NULL) {
508 	memcpy (string->string, str, len);
509 	string->string[len] = '\0';
510     }
511     string->len = len;
512     string->deflate = 0;
513     string->method = NONE;
514 
515     string->base.type = CSI_OBJECT_TYPE_STRING;
516     string->base.ref = 1;
517 
518     obj->type = CSI_OBJECT_TYPE_STRING;
519     obj->datum.string = string;
520 
521     return CSI_STATUS_SUCCESS;
522 }
523 
524 csi_status_t
csi_string_deflate_new(csi_t * ctx,csi_object_t * obj,void * bytes,int in_len,int out_len)525 csi_string_deflate_new (csi_t *ctx,
526 			csi_object_t *obj,
527 			void *bytes,
528 			int in_len,
529 			int out_len)
530 {
531     csi_status_t status;
532     csi_string_t *string;
533 
534     status = csi_string_new (ctx, obj, bytes, in_len);
535     if (_csi_unlikely (status))
536 	return status;
537 
538     string = obj->datum.string;
539     string->deflate = out_len;
540     string->method = ZLIB;
541 
542     return CSI_STATUS_SUCCESS;
543 }
544 
545 csi_status_t
csi_string_new_from_bytes(csi_t * ctx,csi_object_t * obj,char * bytes,unsigned int len)546 csi_string_new_from_bytes (csi_t *ctx,
547 	                   csi_object_t *obj,
548 			   char *bytes,
549 			   unsigned int len)
550 {
551     csi_string_t *string;
552 
553     if (_csi_unlikely (len >= INT_MAX))
554 	return _csi_error (CSI_STATUS_NO_MEMORY);
555 
556     string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
557     if (_csi_unlikely (string == NULL))
558 	return _csi_error (CSI_STATUS_NO_MEMORY);
559 
560     string->string = bytes;
561     string->len = len;
562     string->deflate = 0;
563     string->method = NONE;
564 
565     string->base.type = CSI_OBJECT_TYPE_STRING;
566     string->base.ref = 1;
567 
568     obj->type = CSI_OBJECT_TYPE_STRING;
569     obj->datum.string = string;
570 
571     return CSI_STATUS_SUCCESS;
572 }
573 
574 static inline csi_status_t
_csi_string_execute(csi_t * ctx,csi_string_t * string)575 _csi_string_execute (csi_t *ctx, csi_string_t *string)
576 {
577     csi_status_t status;
578     csi_object_t obj;
579 
580     if (_csi_unlikely (string->len == 0))
581 	return CSI_STATUS_SUCCESS;
582 
583     status = csi_file_new_for_bytes (ctx, &obj, string->string, string->len);
584     if (_csi_unlikely (status))
585 	return status;
586 
587     status = _csi_scan_file (ctx, obj.datum.file);
588     csi_object_free (ctx, &obj);
589 
590     return status;
591 }
592 
593 void
csi_string_free(csi_t * ctx,csi_string_t * string)594 csi_string_free (csi_t *ctx, csi_string_t *string)
595 {
596 #if CSI_DEBUG_MALLOC
597     _csi_free (ctx, string->string);
598     _csi_slab_free (ctx, string, sizeof (csi_string_t));
599 #else
600     if (ctx->free_string != NULL) {
601 	if (string->len > ctx->free_string->len) {
602 	    csi_string_t *tmp = ctx->free_string;
603 	    ctx->free_string = string;
604 	    string = tmp;
605 	}
606 
607 	_csi_free (ctx, string->string);
608 	_csi_slab_free (ctx, string, sizeof (csi_string_t));
609     } else
610 	ctx->free_string = string;
611 #endif
612 }
613 
614 csi_status_t
csi_object_execute(csi_t * ctx,csi_object_t * obj)615 csi_object_execute (csi_t *ctx, csi_object_t *obj)
616 {
617     csi_status_t status;
618     csi_object_t indirect;
619 
620  INDIRECT:
621     switch (obj->type & CSI_OBJECT_TYPE_MASK) {
622     case CSI_OBJECT_TYPE_NAME:
623 	status = _csi_name_lookup (ctx, obj->datum.name, &indirect);
624 	if (_csi_unlikely (status))
625 	    return status;
626 	if (indirect.type & CSI_OBJECT_ATTR_EXECUTABLE) {
627 	    obj = &indirect;
628 	    goto INDIRECT;
629 	} else
630 	    return _csi_push_ostack_copy (ctx, &indirect);
631 
632     case CSI_OBJECT_TYPE_OPERATOR:
633 	return obj->datum.op (ctx);
634 
635     case CSI_OBJECT_TYPE_ARRAY:
636 	return _csi_array_execute (ctx, obj->datum.array);
637     case CSI_OBJECT_TYPE_FILE:
638 	return _csi_file_execute (ctx, obj->datum.file);
639     case CSI_OBJECT_TYPE_STRING:
640 	return _csi_string_execute (ctx, obj->datum.string);
641 
642     default:
643 	return _csi_push_ostack_copy (ctx, obj);
644     }
645 }
646 
647 csi_object_t *
csi_object_reference(csi_object_t * obj)648 csi_object_reference (csi_object_t *obj)
649 {
650     if (CSI_OBJECT_IS_CAIRO (obj)) {
651 	switch (obj->type & CSI_OBJECT_TYPE_MASK) {
652 	case CSI_OBJECT_TYPE_CONTEXT:
653 	    cairo_reference (obj->datum.cr);
654 	    break;
655 	case CSI_OBJECT_TYPE_FONT:
656 	    cairo_font_face_reference (obj->datum.font_face);
657 	    break;
658 	case CSI_OBJECT_TYPE_PATTERN:
659 	    cairo_pattern_reference (obj->datum.pattern);
660 	    break;
661 	case CSI_OBJECT_TYPE_SCALED_FONT:
662 	    cairo_scaled_font_reference (obj->datum.scaled_font);
663 	    break;
664 	case CSI_OBJECT_TYPE_SURFACE:
665 	    cairo_surface_reference (obj->datum.surface);
666 	    break;
667 	}
668     } else if (CSI_OBJECT_IS_COMPOUND (obj)) {
669 	obj->datum.object->ref++;
670     }
671 
672     return obj;
673 }
674 
675 void
csi_object_free(csi_t * ctx,csi_object_t * obj)676 csi_object_free (csi_t *ctx,
677 		 csi_object_t *obj)
678 {
679     if (CSI_OBJECT_IS_CAIRO (obj)) {
680 	switch (obj->type & CSI_OBJECT_TYPE_MASK) {
681 	case CSI_OBJECT_TYPE_CONTEXT:
682 	    cairo_destroy (obj->datum.cr);
683 	    break;
684 	case CSI_OBJECT_TYPE_FONT:
685 	    cairo_font_face_destroy (obj->datum.font_face);
686 	    break;
687 	case CSI_OBJECT_TYPE_PATTERN:
688 	    cairo_pattern_destroy (obj->datum.pattern);
689 	    break;
690 	case CSI_OBJECT_TYPE_SCALED_FONT:
691 	    cairo_scaled_font_destroy (obj->datum.scaled_font);
692 	    break;
693 	case CSI_OBJECT_TYPE_SURFACE:
694 	    cairo_surface_destroy (obj->datum.surface);
695 	    break;
696 	}
697     } else if (CSI_OBJECT_IS_COMPOUND (obj)) {
698 	if (--obj->datum.object->ref)
699 	    return;
700 
701 	switch (obj->type & CSI_OBJECT_TYPE_MASK) {
702 	case CSI_OBJECT_TYPE_ARRAY:
703 	    csi_array_free (ctx, obj->datum.array);
704 	    break;
705 	case CSI_OBJECT_TYPE_DICTIONARY:
706 	    csi_dictionary_free (ctx, obj->datum.dictionary);
707 	    break;
708 	case CSI_OBJECT_TYPE_FILE:
709 	    _csi_file_free (ctx, obj->datum.file);
710 	    break;
711 	case CSI_OBJECT_TYPE_MATRIX:
712 	    csi_matrix_free (ctx, obj->datum.matrix);
713 	    break;
714 	case CSI_OBJECT_TYPE_STRING:
715 	    csi_string_free (ctx, obj->datum.string);
716 	    break;
717 	default:
718 	    break;
719 	}
720     }
721 }
722 
723 csi_status_t
csi_object_as_file(csi_t * ctx,csi_object_t * src,csi_object_t * file)724 csi_object_as_file (csi_t *ctx,
725 		    csi_object_t *src,
726 		    csi_object_t *file)
727 {
728     int type = csi_object_get_type (src);
729     switch (type) {
730     case CSI_OBJECT_TYPE_FILE:
731 	*file = *csi_object_reference (src);
732 	return CSI_STATUS_SUCCESS;
733     case CSI_OBJECT_TYPE_STRING:
734 	 return csi_file_new_from_string (ctx, file, src->datum.string);
735     case CSI_OBJECT_TYPE_ARRAY:
736 #if 0
737 	if (src->type & CSI_OBJECT_ATTR_EXECUTABLE)
738 	    return _csi_file_new_from_procedure (cs, src);
739 #endif
740     default:
741 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
742     }
743 }
744 
745 static int
lexcmp(void const * a,size_t alen,void const * b,size_t blen)746 lexcmp (void const *a, size_t alen,
747 	void const *b, size_t blen)
748 {
749     size_t len = alen < blen ? alen : blen;
750     int cmp = memcmp (a, b, len);
751     if (cmp)
752 	return cmp;
753     if (alen == blen)
754 	return 0;
755     return alen < blen ? -1 : +1;
756 }
757 
758 csi_boolean_t
csi_object_eq(csi_object_t * a,csi_object_t * b)759 csi_object_eq (csi_object_t *a,
760 	       csi_object_t *b)
761 {
762     csi_object_type_t atype = csi_object_get_type (a);
763     csi_object_type_t btype = csi_object_get_type (b);
764 
765     if (atype == btype) {
766 	switch (atype) {
767 	case CSI_OBJECT_TYPE_BOOLEAN:
768 	    return a->datum.boolean == b->datum.boolean;
769 	case CSI_OBJECT_TYPE_INTEGER:
770 	    return a->datum.integer == b->datum.integer;
771 	case CSI_OBJECT_TYPE_REAL:
772 	    return a->datum.real == b->datum.real;
773 	case CSI_OBJECT_TYPE_NAME:
774 	    return a->datum.name == b->datum.name;
775 	case CSI_OBJECT_TYPE_STRING:
776 	    return 0 == lexcmp (a->datum.string->string,
777 				a->datum.string->len,
778 				b->datum.string->string,
779 				b->datum.string->len);
780 	case CSI_OBJECT_TYPE_NULL:
781 	case CSI_OBJECT_TYPE_MARK:
782 	    return TRUE;
783 	case CSI_OBJECT_TYPE_OPERATOR:
784 	    return a->datum.op == b->datum.op;
785 	case CSI_OBJECT_TYPE_ARRAY:
786 	case CSI_OBJECT_TYPE_DICTIONARY:
787 	case CSI_OBJECT_TYPE_FILE:
788 	case CSI_OBJECT_TYPE_MATRIX:
789 	case CSI_OBJECT_TYPE_CONTEXT:
790 	case CSI_OBJECT_TYPE_FONT:
791 	case CSI_OBJECT_TYPE_PATTERN:
792 	case CSI_OBJECT_TYPE_SCALED_FONT:
793 	case CSI_OBJECT_TYPE_SURFACE:
794 	    return a->datum.ptr == b->datum.ptr;
795 	}
796     }
797 
798     if (atype < btype) {
799 	csi_object_t *c;
800 	csi_object_type_t ctype;
801 	c = a; a = b; b = c;
802 	ctype = atype; atype = btype; btype = ctype;
803     }
804 
805     switch ((int) atype) {
806     case CSI_OBJECT_TYPE_INTEGER:
807 	if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
808 	    return a->datum.integer == b->datum.boolean;
809 	}
810 	break;
811     case CSI_OBJECT_TYPE_REAL:
812 	if (btype == CSI_OBJECT_TYPE_INTEGER) {
813 	    return a->datum.real == b->datum.integer;
814 	}
815 	else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
816 	    return a->datum.real == b->datum.boolean;
817 	}
818 	break;
819 
820     case CSI_OBJECT_TYPE_STRING:
821 	if (btype == CSI_OBJECT_TYPE_NAME) {
822 	    const char *bstr = (const char *) b->datum.name;
823 	    return 0 == lexcmp (a->datum.string->string,
824 				a->datum.string->len,
825 				bstr,
826 				strlen (bstr));
827 	}
828 	break;
829 
830     default:
831 	break;
832     }
833 
834     return FALSE;
835 }
836 
837 csi_status_t
csi_object_compare(csi_object_t * a,csi_object_t * b,int * out)838 csi_object_compare (csi_object_t *a,
839 		    csi_object_t *b,
840 		    int *out)
841 {
842     csi_object_type_t atype = csi_object_get_type (a);
843     csi_object_type_t btype = csi_object_get_type (b);
844     int sign;
845 
846     if (csi_object_eq (a, b)){
847 	*out = 0;
848 	return CSI_STATUS_SUCCESS;
849     }
850 
851 #define CMP(x,y) ((x) < (y) ? -1 : +1)
852 
853     if (atype == btype) {
854 	switch (atype) {
855 	case CSI_OBJECT_TYPE_BOOLEAN:
856 	    *out = CMP (a->datum.boolean, b->datum.boolean);
857 	    return CSI_STATUS_SUCCESS;
858 	case CSI_OBJECT_TYPE_INTEGER:
859 	    *out = CMP (a->datum.integer, b->datum.integer);
860 	    return CSI_STATUS_SUCCESS;
861 	case CSI_OBJECT_TYPE_REAL:
862 	    *out = CMP (a->datum.real, b->datum.real);
863 	    return CSI_STATUS_SUCCESS;
864 	case CSI_OBJECT_TYPE_NAME: {
865 	    const char *x = (char const *) a->datum.name;
866 	    const char *y = (char const *) b->datum.name;
867 	    *out = lexcmp (x, strlen(x), y, strlen (y));
868 	    return CSI_STATUS_SUCCESS;
869 	}
870 	case CSI_OBJECT_TYPE_STRING:
871 	    *out = lexcmp (a->datum.string->string,
872 			   a->datum.string->len,
873 			   b->datum.string->string,
874 			   b->datum.string->len);
875 	    return CSI_STATUS_SUCCESS;
876 	case CSI_OBJECT_TYPE_NULL:
877 	case CSI_OBJECT_TYPE_MARK:
878 	case CSI_OBJECT_TYPE_OPERATOR:
879 	case CSI_OBJECT_TYPE_ARRAY:
880 	case CSI_OBJECT_TYPE_DICTIONARY:
881 	case CSI_OBJECT_TYPE_FILE:
882 	case CSI_OBJECT_TYPE_MATRIX:
883 	case CSI_OBJECT_TYPE_CONTEXT:
884 	case CSI_OBJECT_TYPE_FONT:
885 	case CSI_OBJECT_TYPE_PATTERN:
886 	case CSI_OBJECT_TYPE_SCALED_FONT:
887 	case CSI_OBJECT_TYPE_SURFACE:
888 	    goto TYPE_CHECK_ERROR;
889 	}
890     }
891 
892     sign = +1;
893     if (atype < btype) {
894 	csi_object_t *c;
895 	csi_object_type_t ctype;
896 	c = a; a = b; b = c;
897 	ctype = atype; atype = btype; btype = ctype;
898 	sign = -1;
899     }
900 
901     switch ((int) atype) {
902     case CSI_OBJECT_TYPE_INTEGER:
903 	if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
904 	    *out = sign * CMP (a->datum.integer, !!b->datum.boolean);
905 	    return CSI_STATUS_SUCCESS;
906 	}
907 	break;
908     case CSI_OBJECT_TYPE_REAL:
909 	if (btype == CSI_OBJECT_TYPE_INTEGER) {
910 	    *out = sign * CMP (a->datum.real, b->datum.integer);
911 	    return CSI_STATUS_SUCCESS;
912 	}
913 	else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
914 	    *out = sign * CMP (a->datum.real, !!b->datum.boolean);
915 	    return CSI_STATUS_SUCCESS;
916 	}
917 	break;
918 
919     case CSI_OBJECT_TYPE_STRING:
920 	if (btype == CSI_OBJECT_TYPE_NAME) {
921 	    const char *bstr = (const char *) b->datum.name;
922 	    *out = sign * lexcmp (a->datum.string->string,
923 				  a->datum.string->len,
924 				  bstr,
925 				  strlen (bstr));
926 	    return CSI_STATUS_SUCCESS;
927 	}
928 	break;
929 
930     default:
931 	break;
932     }
933 
934 #undef CMP
935 
936  TYPE_CHECK_ERROR:
937     return _csi_error (CSI_STATUS_SCRIPT_INVALID_TYPE);
938 }
939