1
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2
/*
3
 * Copyright © 2004,2006 Red Hat, Inc.
4
 *
5
 * Permission to use, copy, modify, distribute, and sell this software
6
 * and its documentation for any purpose is hereby granted without
7
 * fee, provided that the above copyright notice appear in all copies
8
 * and that both that copyright notice and this permission notice
9
 * appear in supporting documentation, and that the name of
10
 * Red Hat, Inc. not be used in advertising or publicity pertaining to
11
 * distribution of the software without specific, written prior
12
 * permission. Red Hat, Inc. makes no representations about the
13
 * suitability of this software for any purpose.  It is provided "as
14
 * is" without express or implied warranty.
15
 *
16
 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18
 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
19
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
22
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
 *
24
 * Author: Carl D. Worth <cworth@cworth.org>
25
 */
26

            
27
#include "cairo-boilerplate-private.h"
28
#include "cairo-boilerplate-scaled-font.h"
29
#include "cairo-malloc-private.h"
30

            
31
#include <pixman.h>
32

            
33
#include <cairo-ctype-inline.h>
34
#include <cairo-types-private.h>
35
#include <cairo-scaled-font-private.h>
36

            
37
#if CAIRO_HAS_SCRIPT_SURFACE
38
#include <cairo-script.h>
39
#endif
40

            
41
#include <stddef.h>
42
#include <stdlib.h>
43
#include <assert.h>
44
#include <errno.h>
45

            
46
#if HAVE_DLFCN_H
47
#include <dlfcn.h>
48
#endif
49

            
50
#if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_UN_H
51
#include <unistd.h>
52
#include <fcntl.h>
53
#include <signal.h>
54
#include <sys/stat.h>
55
#include <sys/socket.h>
56
#include <sys/un.h>
57

            
58
#define HAS_DAEMON 1
59
#define SOCKET_PATH "./.any2ppm"
60
#endif
61

            
62
cairo_content_t
63
9013
cairo_boilerplate_content (cairo_content_t content)
64
{
65
9013
    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
66
	content = CAIRO_CONTENT_COLOR_ALPHA;
67

            
68
9013
    return content;
69
}
70

            
71
const char *
72
7291
cairo_boilerplate_content_name (cairo_content_t content)
73
{
74
    /* For the purpose of the content name, we don't distinguish the
75
     * flattened content value.
76
     */
77
7291
    switch (cairo_boilerplate_content (content)) {
78
4966
    case CAIRO_CONTENT_COLOR:
79
4966
	return "rgb24";
80
2325
    case CAIRO_CONTENT_COLOR_ALPHA:
81
2325
	return "argb32";
82
    case CAIRO_CONTENT_ALPHA:
83
    default:
84
	assert (0); /* not reached */
85
	return "---";
86
    }
87
}
88

            
89
static const char *
90
_cairo_boilerplate_content_visible_name (cairo_content_t content)
91
{
92
    switch (cairo_boilerplate_content (content)) {
93
    case CAIRO_CONTENT_COLOR:
94
	return "rgb";
95
    case CAIRO_CONTENT_COLOR_ALPHA:
96
	return "rgba";
97
    case CAIRO_CONTENT_ALPHA:
98
	return "a";
99
    default:
100
	assert (0); /* not reached */
101
	return "---";
102
    }
103
}
104

            
105
cairo_format_t
106
cairo_boilerplate_format_from_content (cairo_content_t content)
107
{
108
    cairo_format_t format;
109

            
110
    switch (content) {
111
    case CAIRO_CONTENT_COLOR:
112
        format = CAIRO_FORMAT_RGB24;
113
        break;
114
    case CAIRO_CONTENT_COLOR_ALPHA:
115
        format = CAIRO_FORMAT_ARGB32;
116
        break;
117
    case CAIRO_CONTENT_ALPHA:
118
        format = CAIRO_FORMAT_A8;
119
        break;
120
    default:
121
        assert (0); /* not reached */
122
        format = CAIRO_FORMAT_INVALID;
123
        break;
124
    }
125

            
126
    return format;
127
}
128

            
129
static cairo_surface_t *
130
1150
_cairo_boilerplate_image_create_surface (const char		   *name,
131
					 cairo_content_t	    content,
132
					 double 		    width,
133
					 double 		    height,
134
					 double 		    max_width,
135
					 double 		    max_height,
136
					 cairo_boilerplate_mode_t   mode,
137
					 void			  **closure)
138
{
139
    cairo_format_t format;
140

            
141
1150
    *closure = NULL;
142

            
143
1150
    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
144
575
	format = CAIRO_FORMAT_ARGB32;
145
575
    } else if (content == CAIRO_CONTENT_COLOR) {
146
575
	format = CAIRO_FORMAT_RGB24;
147
    } else {
148
	assert (0); /* not reached */
149
	return NULL;
150
    }
151

            
152
1150
    return cairo_image_surface_create (format, ceil (width), ceil (height));
153
}
154

            
155
static const cairo_user_data_key_t key;
156

            
157
static cairo_surface_t *
158
_cairo_boilerplate_image_create_similar (cairo_surface_t *other,
159
					 cairo_content_t content,
160
					 int width, int height)
161
{
162
    cairo_format_t format;
163
    cairo_surface_t *surface;
164
    int stride;
165
    void *ptr;
166

            
167
    switch (content) {
168
    case CAIRO_CONTENT_ALPHA:
169
        format = CAIRO_FORMAT_A8;
170
        break;
171
    case CAIRO_CONTENT_COLOR:
172
        format = CAIRO_FORMAT_RGB24;
173
        break;
174
    case CAIRO_CONTENT_COLOR_ALPHA:
175
    default:
176
        format = CAIRO_FORMAT_ARGB32;
177
        break;
178
    }
179

            
180
    stride = cairo_format_stride_for_width(format, width);
181
    ptr = _cairo_malloc (stride * height);
182

            
183
    surface = cairo_image_surface_create_for_data (ptr, format,
184
						   width, height, stride);
185
    cairo_surface_set_user_data (surface, &key, ptr, free);
186

            
187
    return surface;
188
}
189

            
190
static cairo_surface_t *
191
575
_cairo_boilerplate_image16_create_surface (const char		     *name,
192
					   cairo_content_t	      content,
193
					   double		      width,
194
					   double		      height,
195
					   double		      max_width,
196
					   double		      max_height,
197
					   cairo_boilerplate_mode_t   mode,
198
					   void			    **closure)
199
{
200
575
    *closure = NULL;
201

            
202
    /* XXX force CAIRO_CONTENT_COLOR */
203
575
    return cairo_image_surface_create (CAIRO_FORMAT_RGB16_565, ceil (width), ceil (height));
204
}
205

            
206
static cairo_surface_t *
207
_cairo_boilerplate_image16_create_similar (cairo_surface_t *other,
208
					   cairo_content_t content,
209
					   int width, int height)
210
{
211
    cairo_format_t format;
212
    cairo_surface_t *surface;
213
    int stride;
214
    void *ptr;
215

            
216
    switch (content) {
217
    case CAIRO_CONTENT_ALPHA:
218
        format = CAIRO_FORMAT_A8;
219
        break;
220
    case CAIRO_CONTENT_COLOR:
221
        format = CAIRO_FORMAT_RGB16_565;
222
        break;
223
    case CAIRO_CONTENT_COLOR_ALPHA:
224
    default:
225
        format = CAIRO_FORMAT_ARGB32;
226
        break;
227
    }
228

            
229
    stride = cairo_format_stride_for_width(format, width);
230
    ptr = _cairo_malloc (stride * height);
231

            
232
    surface = cairo_image_surface_create_for_data (ptr, format,
233
						   width, height, stride);
234
    cairo_surface_set_user_data (surface, &key, ptr, free);
235

            
236
    return surface;
237
}
238

            
239
static char *
240
_cairo_boilerplate_image_describe (void *closure)
241
{
242
    char *s;
243

            
244
    xasprintf (&s, "pixman %s", pixman_version_string ());
245

            
246
    return s;
247
}
248

            
249
#if CAIRO_HAS_RECORDING_SURFACE
250
static cairo_surface_t *
251
_cairo_boilerplate_recording_create_surface (const char 	       *name,
252
					     cairo_content_t		content,
253
					     double			width,
254
					     double			height,
255
					     double			max_width,
256
					     double			max_height,
257
					     cairo_boilerplate_mode_t	mode,
258
					     void		      **closure)
259
{
260
    cairo_rectangle_t extents;
261

            
262
    extents.x = 0;
263
    extents.y = 0;
264
    extents.width = width;
265
    extents.height = height;
266
    return *closure = cairo_surface_reference (cairo_recording_surface_create (content, &extents));
267
}
268

            
269
static void
270
_cairo_boilerplate_recording_surface_cleanup (void *closure)
271
{
272
    cairo_surface_finish (closure);
273
    cairo_surface_destroy (closure);
274
}
275
#endif
276

            
277
const cairo_user_data_key_t cairo_boilerplate_output_basename_key;
278

            
279
cairo_surface_t *
280
1677
_cairo_boilerplate_get_image_surface (cairo_surface_t *src,
281
				      int	       page,
282
				      int	       width,
283
				      int	       height)
284
{
285
    cairo_surface_t *surface, *image;
286
    cairo_t *cr;
287
    cairo_status_t status;
288
    cairo_format_t format;
289

            
290
1677
    if (cairo_surface_status (src))
291
	return cairo_surface_reference (src);
292

            
293
1677
    if (page != 0)
294
	return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
295

            
296
    /* extract sub-surface */
297
1677
    switch (cairo_surface_get_content (src)) {
298
    case CAIRO_CONTENT_ALPHA:
299
	format = CAIRO_FORMAT_A8;
300
	break;
301
1118
    case CAIRO_CONTENT_COLOR:
302
1118
	format = CAIRO_FORMAT_RGB24;
303
1118
	break;
304
559
    default:
305
    case CAIRO_CONTENT_COLOR_ALPHA:
306
559
	format = CAIRO_FORMAT_ARGB32;
307
559
	break;
308
    }
309
1677
    surface = cairo_image_surface_create (format, width, height);
310
1677
    assert (cairo_surface_get_content (surface) == cairo_surface_get_content (src));
311
1677
    image = cairo_surface_reference (surface);
312

            
313
    /* open a logging channel (only interesting for recording surfaces) */
314
#if CAIRO_HAS_SCRIPT_SURFACE && CAIRO_HAS_RECORDING_SURFACE
315
1677
    if (cairo_surface_get_type (src) == CAIRO_SURFACE_TYPE_RECORDING) {
316
	const char *test_name;
317

            
318
	test_name = cairo_surface_get_user_data (src,
319
						 &cairo_boilerplate_output_basename_key);
320
	if (test_name != NULL) {
321
	    cairo_device_t *ctx;
322
	    char *filename;
323

            
324
	    cairo_surface_destroy (surface);
325

            
326
	    xasprintf (&filename, "%s.out.trace", test_name);
327
	    ctx = cairo_script_create (filename);
328
	    surface = cairo_script_surface_create_for_target (ctx, image);
329
	    cairo_device_destroy (ctx);
330
	    free (filename);
331
	}
332
    }
333
#endif
334

            
335
1677
    cr = cairo_create (surface);
336
1677
    cairo_surface_destroy (surface);
337
1677
    cairo_set_source_surface (cr, src, 0, 0);
338
1677
    cairo_paint (cr);
339

            
340
1677
    status = cairo_status (cr);
341
1677
    if (status) {
342
	cairo_surface_destroy (image);
343
	image = cairo_surface_reference (cairo_get_target (cr));
344
    }
345
1677
    cairo_destroy (cr);
346

            
347
1677
    return image;
348
}
349

            
350
cairo_surface_t *
351
cairo_boilerplate_get_image_surface_from_png (const char   *filename,
352
					      int	    width,
353
					      int	    height,
354
					      cairo_bool_t  flatten)
355
{
356
    cairo_surface_t *surface;
357

            
358
    surface = cairo_image_surface_create_from_png (filename);
359
    if (cairo_surface_status (surface))
360
	return surface;
361

            
362
    if (flatten) {
363
	cairo_t *cr;
364
	cairo_surface_t *flattened;
365

            
366
	flattened = cairo_image_surface_create (cairo_image_surface_get_format (surface),
367
						width,
368
						height);
369
	cr = cairo_create (flattened);
370
	cairo_surface_destroy (flattened);
371

            
372
	cairo_set_source_rgb (cr, 1, 1, 1);
373
	cairo_paint (cr);
374

            
375
	cairo_set_source_surface (cr, surface,
376
				  width - cairo_image_surface_get_width (surface),
377
				  height - cairo_image_surface_get_height (surface));
378
	cairo_paint (cr);
379

            
380
	cairo_surface_destroy (surface);
381
	surface = cairo_surface_reference (cairo_get_target (cr));
382
	cairo_destroy (cr);
383
    } else if (cairo_image_surface_get_width (surface) != width ||
384
	       cairo_image_surface_get_height (surface) != height)
385
    {
386
	cairo_t *cr;
387
	cairo_surface_t *sub;
388

            
389
	sub = cairo_image_surface_create (cairo_image_surface_get_format (surface),
390
					  width,
391
					  height);
392
	cr = cairo_create (sub);
393
	cairo_surface_destroy (sub);
394

            
395
	cairo_set_source_surface (cr, surface,
396
				  width - cairo_image_surface_get_width (surface),
397
				  height - cairo_image_surface_get_height (surface));
398
	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
399
	cairo_paint (cr);
400

            
401
	cairo_surface_destroy (surface);
402
	surface = cairo_surface_reference (cairo_get_target (cr));
403
	cairo_destroy (cr);
404
    }
405

            
406
    return surface;
407
}
408

            
409
static const cairo_boilerplate_target_t builtin_targets[] = {
410
    /* I'm uncompromising about leaving the image backend as 0
411
     * for tolerance. There shouldn't ever be anything that is out of
412
     * our control here. */
413
    {
414
	"image", "image", NULL, NULL,
415
	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0,
416
	NULL,
417
	_cairo_boilerplate_image_create_surface,
418
	_cairo_boilerplate_image_create_similar,
419
	NULL, NULL,
420
	_cairo_boilerplate_get_image_surface,
421
	cairo_surface_write_to_png,
422
	NULL, NULL,
423
        _cairo_boilerplate_image_describe,
424
	TRUE, FALSE, FALSE
425
    },
426
    {
427
	"image", "image", NULL, NULL,
428
	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
429
	NULL,
430
	_cairo_boilerplate_image_create_surface,
431
	_cairo_boilerplate_image_create_similar,
432
	NULL, NULL,
433
	_cairo_boilerplate_get_image_surface,
434
	cairo_surface_write_to_png,
435
	NULL, NULL,
436
        _cairo_boilerplate_image_describe,
437
	FALSE, FALSE, FALSE
438
    },
439
    {
440
	"image16", "image", NULL, NULL,
441
	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
442
	NULL,
443
	_cairo_boilerplate_image16_create_surface,
444
	_cairo_boilerplate_image16_create_similar,
445
	NULL, NULL,
446
	_cairo_boilerplate_get_image_surface,
447
	cairo_surface_write_to_png,
448
	NULL, NULL,
449
        _cairo_boilerplate_image_describe,
450
	TRUE, FALSE, FALSE
451
    },
452
#if CAIRO_HAS_RECORDING_SURFACE
453
    {
454
	"recording", "image", NULL, NULL,
455
	CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR_ALPHA, 0,
456
	"cairo_recording_surface_create",
457
	_cairo_boilerplate_recording_create_surface,
458
	cairo_surface_create_similar,
459
	NULL, NULL,
460
	_cairo_boilerplate_get_image_surface,
461
	cairo_surface_write_to_png,
462
	_cairo_boilerplate_recording_surface_cleanup,
463
	NULL, NULL,
464
	FALSE, FALSE, TRUE
465
    },
466
    {
467
	"recording", "image", NULL, NULL,
468
	CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
469
	"cairo_recording_surface_create",
470
	_cairo_boilerplate_recording_create_surface,
471
	cairo_surface_create_similar,
472
	NULL, NULL,
473
	_cairo_boilerplate_get_image_surface,
474
	cairo_surface_write_to_png,
475
	_cairo_boilerplate_recording_surface_cleanup,
476
	NULL, NULL,
477
	FALSE, FALSE, TRUE
478
    },
479
#endif
480
};
481
1
CAIRO_BOILERPLATE (builtin, builtin_targets)
482

            
483
static struct cairo_boilerplate_target_list {
484
    struct cairo_boilerplate_target_list *next;
485
    const cairo_boilerplate_target_t *target;
486
} *cairo_boilerplate_targets;
487

            
488
static cairo_bool_t
489
26
probe_target (const cairo_boilerplate_target_t *target)
490
{
491
26
    if (target->probe == NULL)
492
3
	return TRUE;
493

            
494
#if HAVE_DLSYM
495
    return dlsym (NULL, target->probe) != NULL;
496
#else
497
23
    return TRUE;
498
#endif
499
}
500

            
501
void
502
7
_cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
503
				     unsigned int		       count)
504
{
505
7
    targets += count;
506
33
    while (count--) {
507
	struct cairo_boilerplate_target_list *list;
508

            
509
26
	--targets;
510
26
	if (! probe_target (targets))
511
	    continue;
512

            
513
26
	list = xmalloc (sizeof (*list));
514
26
	list->next = cairo_boilerplate_targets;
515
26
	list->target = targets;
516
26
	cairo_boilerplate_targets = list;
517
    }
518
7
}
519

            
520
static cairo_bool_t
521
_cairo_boilerplate_target_format_matches_name (const cairo_boilerplate_target_t *target,
522
					const char *tcontent_name,
523
					const char *tcontent_end)
524
{
525
	char const *content_name;
526
	const char *content_end = tcontent_end;
527
	size_t content_len;
528

            
529
	content_name = _cairo_boilerplate_content_visible_name (target->content);
530
	if (tcontent_end)
531
		content_len = content_end - tcontent_name;
532
	else
533
		content_len = strlen(tcontent_name);
534
	if (strlen(content_name) != content_len)
535
		return FALSE;
536
	if (0 == strncmp (content_name, tcontent_name, content_len))
537
		return TRUE;
538

            
539
	return FALSE;
540
}
541

            
542
static cairo_bool_t
543
52
_cairo_boilerplate_target_matches_name (const cairo_boilerplate_target_t *target,
544
					const char			 *tname,
545
					const char			 *end)
546
{
547
    char const *content_name;
548
52
    const char *content_start = strpbrk (tname, ".");
549
52
    const char *content_end = end;
550
    size_t name_len;
551
    size_t content_len;
552

            
553
52
    if (content_start >= end)
554
	content_start = NULL;
555
52
    if (content_start != NULL)
556
	end = content_start++;
557

            
558
52
    name_len = end - tname;
559

            
560
    /* Check name. */
561
52
    if (! (name_len == 1 && 0 == strncmp (tname, "?", 1))) { /* wildcard? */
562
52
	if (0 != strncmp (target->name, tname, name_len)) /* exact match? */
563
48
	    return FALSE;
564
4
	if (_cairo_isalnum (target->name[name_len]))
565
1
	    return FALSE;
566
    }
567

            
568
    /* Check optional content. */
569
3
    if (content_start == NULL)	/* none given? */
570
3
	return TRUE;
571

            
572
    /* Exact content match? */
573
    content_name = _cairo_boilerplate_content_visible_name (target->content);
574
    content_len = content_end - content_start;
575
    if (strlen(content_name) != content_len)
576
	return FALSE;
577
    if (0 == strncmp (content_name, content_start, content_len))
578
	return TRUE;
579

            
580
    return FALSE;
581
}
582

            
583
const cairo_boilerplate_target_t **
584
1
cairo_boilerplate_get_targets (int	    *pnum_targets,
585
			       cairo_bool_t *plimited_targets)
586
{
587
    size_t i, num_targets;
588
1
    cairo_bool_t limited_targets = FALSE;
589
    const char *tname;
590
    const cairo_boilerplate_target_t **targets_to_test;
591
    struct cairo_boilerplate_target_list *list;
592

            
593
1
    if (cairo_boilerplate_targets == NULL)
594
1
	_cairo_boilerplate_register_all ();
595

            
596
2
    if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL && *tname) {
597
	/* check the list of targets specified by the user */
598
1
	limited_targets = TRUE;
599

            
600
1
	num_targets = 0;
601
1
	targets_to_test = NULL;
602

            
603
3
	while (*tname) {
604
2
	    int found = 0;
605
2
	    const char *end = strpbrk (tname, " \t\r\n;:,");
606
2
	    if (!end)
607
1
		end = tname + strlen (tname);
608

            
609
2
	    if (end == tname) {
610
		tname = end + 1;
611
		continue;
612
	    }
613

            
614
2
	    for (list = cairo_boilerplate_targets;
615
54
		 list != NULL;
616
52
		 list = list->next)
617
	    {
618
52
		    const cairo_boilerplate_target_t *target = list->target;
619
		    const char *tcontent_name;
620
		    const char *tcontent_end;
621
52
		    if (_cairo_boilerplate_target_matches_name (target, tname, end)) {
622
3
			    if ((tcontent_name = getenv ("CAIRO_TEST_TARGET_FORMAT")) != NULL && *tcontent_name) {
623
				    while(tcontent_name) {
624
					    tcontent_end = strpbrk (tcontent_name, " \t\r\n;:,");
625
					    if (tcontent_end == tcontent_name) {
626
						    tcontent_name = tcontent_end + 1;
627
						    continue;
628
					    }
629
					    if(_cairo_boilerplate_target_format_matches_name (target,
630
								    tcontent_name, tcontent_end)) {
631
						    /* realloc isn't exactly the best thing here, but meh. */
632
						    targets_to_test = xrealloc (targets_to_test,
633
								    sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
634
						    targets_to_test[num_targets++] = target;
635
						    found = 1;
636
					    }
637

            
638
					    if (tcontent_end)
639
						    tcontent_end++;
640
					    tcontent_name = tcontent_end;
641
				    }
642
			    } else {
643
				    /* realloc isn't exactly the best thing here, but meh. */
644
3
				    targets_to_test = xrealloc (targets_to_test,
645
3
						    sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
646
3
				    targets_to_test[num_targets++] = target;
647
3
				    found = 1;
648
			    }
649
		    }
650
	    }
651

            
652
2
	    if (!found) {
653
		const char *last_name = NULL;
654

            
655
		fprintf (stderr, "Cannot find target '%.*s'.\n",
656
			 (int)(end - tname), tname);
657
		fprintf (stderr, "Known targets:");
658
		for (list = cairo_boilerplate_targets;
659
		     list != NULL;
660
		     list = list->next)
661
		{
662
		    const cairo_boilerplate_target_t *target = list->target;
663
		    if (last_name != NULL) {
664
			if (strcmp (target->name, last_name) == 0) {
665
			    /* filter out repeats that differ in content */
666
			    continue;
667
			}
668
			fprintf (stderr, ",");
669
		    }
670
		    fprintf (stderr, " %s", target->name);
671
		    last_name = target->name;
672
		}
673
		fprintf (stderr, "\n");
674
		exit(-1);
675
	    }
676

            
677
2
	    if (*end)
678
1
	      end++;
679
2
	    tname = end;
680
	}
681
    } else {
682
	    int found = 0;
683
	    int not_found_targets = 0;
684
	    num_targets = 0;
685
	    targets_to_test = xmalloc (sizeof(cairo_boilerplate_target_t*) * num_targets);
686
	    for (list = cairo_boilerplate_targets; list != NULL; list = list->next)
687
	    {
688
		    const cairo_boilerplate_target_t *target = list->target;
689
		    const char *tcontent_name;
690
		    const char *tcontent_end;
691
		    if ((tcontent_name = getenv ("CAIRO_TEST_TARGET_FORMAT")) != NULL && *tcontent_name) {
692
			    while(tcontent_name) {
693
				    tcontent_end = strpbrk (tcontent_name, " \t\r\n;:,");
694
				    if (tcontent_end == tcontent_name) {
695
					    tcontent_name = tcontent_end + 1;
696
					    continue;
697
				    }
698
				    if (_cairo_boilerplate_target_format_matches_name (target,
699
							    tcontent_name, tcontent_end)) {
700
					    /* realloc isn't exactly the best thing here, but meh. */
701
					    targets_to_test = xrealloc (targets_to_test,
702
							    sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
703
					    targets_to_test[num_targets++] = target;
704
					    found =1;
705
				    }
706
				    else
707
				    {
708
					    not_found_targets++;
709
				    }
710

            
711
				    if (tcontent_end)
712
					    tcontent_end++;
713

            
714
				    tcontent_name = tcontent_end;
715
			    }
716
		    }
717
		    else
718
		    {
719
			    num_targets++;
720
		    }
721
	    }
722
	    if (!found)
723
	    {
724
		    /* check all compiled in targets */
725
		    num_targets = num_targets + not_found_targets;
726
		    targets_to_test = xrealloc (targets_to_test,
727
				    sizeof(cairo_boilerplate_target_t*) * num_targets);
728
		    num_targets = 0;
729
		    for (list = cairo_boilerplate_targets;
730
				    list != NULL;
731
				    list = list->next)
732
		    {
733
			    const cairo_boilerplate_target_t *target = list->target;
734
			    targets_to_test[num_targets++] = target;
735
		    }
736
	    }
737

            
738
    }
739

            
740
    /* exclude targets as specified by the user */
741
1
    if ((tname = getenv ("CAIRO_TEST_TARGET_EXCLUDE")) != NULL && *tname) {
742
	limited_targets = TRUE;
743

            
744
	while (*tname) {
745
	    int j;
746
	    const char *end = strpbrk (tname, " \t\r\n;:,");
747
	    if (!end)
748
		end = tname + strlen (tname);
749

            
750
	    if (end == tname) {
751
		tname = end + 1;
752
		continue;
753
	    }
754

            
755
	    for (i = j = 0; i < num_targets; i++) {
756
		const cairo_boilerplate_target_t *target = targets_to_test[i];
757
		if (! _cairo_boilerplate_target_matches_name (target,
758
							      tname, end))
759
		{
760
		    targets_to_test[j++] = targets_to_test[i];
761
		}
762
	    }
763
	    num_targets = j;
764

            
765
	    if (*end)
766
	      end++;
767
	    tname = end;
768
	}
769
    }
770

            
771
1
    if (pnum_targets)
772
1
	*pnum_targets = num_targets;
773

            
774
1
    if (plimited_targets)
775
1
	*plimited_targets = limited_targets;
776

            
777
1
    return targets_to_test;
778
}
779

            
780
const cairo_boilerplate_target_t *
781
cairo_boilerplate_get_image_target (cairo_content_t content)
782
{
783
    if (cairo_boilerplate_targets == NULL)
784
	_cairo_boilerplate_register_all ();
785

            
786
    switch (content) {
787
    case CAIRO_CONTENT_COLOR:
788
        return &builtin_targets[1];
789
    case CAIRO_CONTENT_COLOR_ALPHA:
790
        return &builtin_targets[0];
791
    case CAIRO_CONTENT_ALPHA:
792
    default:
793
        return NULL;
794
    }
795
}
796

            
797
const cairo_boilerplate_target_t *
798
cairo_boilerplate_get_target_by_name (const char      *name,
799
				      cairo_content_t  content)
800
{
801
    struct cairo_boilerplate_target_list *list;
802

            
803
    if (cairo_boilerplate_targets == NULL)
804
	_cairo_boilerplate_register_all ();
805

            
806
    /* first return an exact match */
807
    for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
808
	const cairo_boilerplate_target_t *target = list->target;
809
	if (strcmp (target->name, name) == 0 &&
810
	    target->content == content)
811
	{
812
	    return target;
813
	}
814
    }
815

            
816
    /* otherwise just return a match that may differ in content */
817
    for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
818
	const cairo_boilerplate_target_t *target = list->target;
819
	if (strcmp (target->name, name) == 0)
820
	    return target;
821
    }
822

            
823
    return NULL;
824
}
825

            
826
void
827
1
cairo_boilerplate_free_targets (const cairo_boilerplate_target_t **targets)
828
{
829
1
    free (targets);
830
1
}
831

            
832
cairo_surface_t *
833
cairo_boilerplate_surface_create_in_error (cairo_status_t status)
834
{
835
    cairo_surface_t *surface = NULL;
836
    int loop = 5;
837

            
838
    do {
839
	cairo_surface_t *intermediate;
840
	cairo_t *cr;
841
	cairo_path_t path;
842

            
843
	intermediate = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0);
844
	cr = cairo_create (intermediate);
845
	cairo_surface_destroy (intermediate);
846

            
847
	path.status = status;
848
	cairo_append_path (cr, &path);
849

            
850
	cairo_surface_destroy (surface);
851
	surface = cairo_surface_reference (cairo_get_target (cr));
852
	cairo_destroy (cr);
853
    } while (cairo_surface_status (surface) != status && --loop);
854

            
855
    return surface;
856
}
857

            
858
void
859
3
cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled_font,
860
						     int		  max_glyphs)
861
{
862
    /* XXX CAIRO_DEBUG */
863
3
}
864

            
865
#if HAS_DAEMON
866
static int
867
any2ppm_daemon_exists (void)
868
{
869
    struct stat st;
870
    int fd;
871
    char buf[80];
872
    int pid;
873
    int ret;
874

            
875
    if (stat (SOCKET_PATH, &st) < 0)
876
	return 0;
877

            
878
    fd = open (SOCKET_PATH ".pid", O_RDONLY);
879
    if (fd < 0)
880
	return 0;
881

            
882
    pid = 0;
883
    ret = read (fd, buf, sizeof (buf) - 1);
884
    if (ret > 0) {
885
	buf[ret] = '\0';
886
	pid = atoi (buf);
887
    }
888
    close (fd);
889

            
890
    return pid > 0 && kill (pid, 0) != -1;
891
}
892
#endif
893

            
894
FILE *
895
cairo_boilerplate_open_any2ppm (const char   *filename,
896
				int	      page,
897
				unsigned int  flags,
898
				int        (**close_cb) (FILE *))
899
{
900
    char command[4096];
901
    const char *any2ppm;
902
#if HAS_DAEMON
903
    int sk;
904
    struct sockaddr_un addr;
905
    int len;
906
#endif
907

            
908
    any2ppm = getenv ("ANY2PPM");
909
    if (any2ppm == NULL)
910
	any2ppm = "./any2ppm";
911

            
912
#if HAS_DAEMON
913
    if (flags & CAIRO_BOILERPLATE_OPEN_NO_DAEMON)
914
	goto POPEN;
915

            
916
    if (! any2ppm_daemon_exists ()) {
917
	if (system (any2ppm) != 0)
918
	    goto POPEN;
919
    }
920

            
921
    sk = socket (PF_UNIX, SOCK_STREAM, 0);
922
    if (sk == -1)
923
	goto POPEN;
924

            
925
    memset (&addr, 0, sizeof (addr));
926
    addr.sun_family = AF_UNIX;
927
    strcpy (addr.sun_path, SOCKET_PATH);
928

            
929
    if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
930
	close (sk);
931
	goto POPEN;
932
    }
933

            
934
    len = sprintf (command, "%s %d\n", filename, page);
935
    if (write (sk, command, len) != len) {
936
	close (sk);
937
	goto POPEN;
938
    }
939

            
940
    *close_cb = fclose;
941
    return fdopen (sk, "rb");
942

            
943
POPEN:
944
#endif
945

            
946
    *close_cb = pclose;
947
    sprintf (command, "%s %s %d", any2ppm, filename, page);
948
    return popen (command, "r");
949
}
950

            
951
static cairo_bool_t
952
freadn (unsigned char *buf,
953
	int	       len,
954
	FILE	      *file)
955
{
956
    int ret;
957

            
958
    while (len) {
959
	ret = fread (buf, 1, len, file);
960
	if (ret != len) {
961
	    if (ferror (file) || feof (file))
962
		return FALSE;
963
	}
964
	len -= ret;
965
	buf += len;
966
    }
967

            
968
    return TRUE;
969
}
970

            
971
cairo_surface_t *
972
cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file)
973
{
974
    char format;
975
    int width, height;
976
    ptrdiff_t stride;
977
    int x, y;
978
    unsigned char *data;
979
    cairo_surface_t *image = NULL;
980

            
981
    if (fscanf (file, "P%c %d %d 255\n", &format, &width, &height) != 3)
982
	goto FAIL;
983

            
984
    switch (format) {
985
    case '7': /* XXX */
986
	image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
987
	break;
988
    case '6':
989
	image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
990
	break;
991
    case '5':
992
	image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
993
	break;
994
    default:
995
	goto FAIL;
996
    }
997
    if (cairo_surface_status (image))
998
	return image;
999

            
    data = cairo_image_surface_get_data (image);
    stride = cairo_image_surface_get_stride (image);
    for (y = 0; y < height; y++) {
	unsigned char *buf = data + y*stride;
	switch (format) {
	case '7':
	    if (! freadn (buf, 4 * width, file))
		goto FAIL;
	    break;
	case '6':
	    if (! freadn (buf, 3*width, file))
		goto FAIL;
	    buf += 3*width;
	    for (x = width; x--; ) {
		buf -= 3;
		((uint32_t *) (data + y*stride))[x] =
		    (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
	    }
	    break;
	case '5':
	    if (! freadn (buf, width, file))
		goto FAIL;
	    break;
	}
    }
    cairo_surface_mark_dirty (image);
    return image;
FAIL:
    cairo_surface_destroy (image);
    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
}
cairo_surface_t *
cairo_boilerplate_convert_to_image (const char *filename,
				    int 	page)
{
    FILE *file;
    unsigned int flags = 0;
    cairo_surface_t *image;
    int (*close_cb) (FILE *);
    int ret;
    if (getenv ("CAIRO_BOILERPLATE_OPEN_NO_DAEMON") != NULL) {
	flags |= CAIRO_BOILERPLATE_OPEN_NO_DAEMON;
    }
  RETRY:
    file = cairo_boilerplate_open_any2ppm (filename, page, flags, &close_cb);
    if (file == NULL) {
	switch (errno) {
	case ENOMEM:
	    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
	default:
	    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
	}
    }
    image = cairo_boilerplate_image_surface_create_from_ppm_stream (file);
    ret = close_cb (file);
    /* check for fatal errors from the interpreter */
    if (ret) { /* any2pmm should never die... */
	cairo_surface_destroy (image);
	if (getenv ("CAIRO_BOILERPLATE_DO_NOT_CRASH_ON_ANY2PPM_ERROR") != NULL) {
	    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR);
	} else {
	    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_INVALID_STATUS);
	}
    }
    if (ret == 0 && cairo_surface_status (image) == CAIRO_STATUS_READ_ERROR) {
	if (flags == 0) {
	    /* Try again in a standalone process. */
	    cairo_surface_destroy (image);
	    flags = CAIRO_BOILERPLATE_OPEN_NO_DAEMON;
	    goto RETRY;
	}
    }
    return image;
}
int
cairo_boilerplate_version (void)
{
    return CAIRO_VERSION;
}
const char*
cairo_boilerplate_version_string (void)
{
    return CAIRO_VERSION_STRING;
}
void
608
cairo_boilerplate_fini (void)
{
634
    while (cairo_boilerplate_targets != NULL) {
	struct cairo_boilerplate_target_list *next;
26
	next = cairo_boilerplate_targets->next;
26
	free (cairo_boilerplate_targets);
26
	cairo_boilerplate_targets = next;
    }
608
}