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