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
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
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
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
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
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
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
187
_dictionary_name_equal (const void *_a, const void *_b)
188
{
189
    return TRUE;
190
}
191

            
192
csi_status_t
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
}