Upload Tizen2.0 source
[framework/graphics/cairo.git] / test / any2ppm.c
1 /*
2  * Copyright © 2008 Chris Wilson
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of
9  * Chris Wilson not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior
11  * permission. Chris Wilson makes no representations about the
12  * suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
18  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: Chris Wilson <chris@chris-wilson.co.uk>
24  *
25  * Contributor(s):
26  *      Carlos Garcia Campos <carlosgc@gnome.org>
27  *
28  * Adapted from pdf2png.c:
29  * Copyright © 2005 Red Hat, Inc.
30  *
31  * Permission to use, copy, modify, distribute, and sell this software
32  * and its documentation for any purpose is hereby granted without
33  * fee, provided that the above copyright notice appear in all copies
34  * and that both that copyright notice and this permission notice
35  * appear in supporting documentation, and that the name of
36  * Red Hat, Inc. not be used in advertising or publicity pertaining to
37  * distribution of the software without specific, written prior
38  * permission. Red Hat, Inc. makes no representations about the
39  * suitability of this software for any purpose.  It is provided "as
40  * is" without express or implied warranty.
41  *
42  * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
43  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
44  * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
45  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
46  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
47  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
48  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49  *
50  * Author: Kristian Høgsberg <krh@redhat.com>
51  */
52
53 #if HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56
57 #if HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63
64 #include <cairo.h>
65 #include <cairo-script-interpreter.h>
66
67 #if CAIRO_CAN_TEST_PDF_SURFACE
68 #include <poppler.h>
69 #endif
70
71 #if CAIRO_CAN_TEST_SVG_SURFACE
72 #include <librsvg/rsvg.h>
73 #include <librsvg/rsvg-cairo.h>
74 #endif
75
76 #if CAIRO_HAS_SPECTRE
77 #include <libspectre/spectre.h>
78 #endif
79
80 #include <errno.h>
81
82 #if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_POLL_H && HAVE_SYS_UN_H
83 #include <fcntl.h>
84 #include <signal.h>
85 #include <sys/stat.h>
86 #include <sys/socket.h>
87 #include <sys/poll.h>
88 #include <sys/un.h>
89
90 #define SOCKET_PATH "./.any2ppm"
91 #define TIMEOUT 60000 /* 60 seconds */
92
93 #define CAN_RUN_AS_DAEMON 1
94 #endif
95
96 #define ARRAY_LENGTH(A) (sizeof (A) / sizeof (A[0]))
97
98 static int
99 _cairo_writen (int fd, char *buf, int len)
100 {
101     while (len) {
102         int ret;
103
104         ret = write (fd, buf, len);
105         if (ret == -1) {
106             int err = errno;
107             switch (err) {
108             case EINTR:
109             case EAGAIN:
110                 continue;
111             default:
112                 return 0;
113             }
114         }
115         len -= ret;
116         buf += ret;
117     }
118
119     return 1;
120 }
121
122 static int
123 _cairo_write (int fd,
124         char *buf, int maxlen, int buflen,
125         const unsigned char *src, int srclen)
126 {
127     if (buflen < 0)
128         return buflen;
129
130     while (srclen) {
131         int len;
132
133         len = buflen + srclen;
134         if (len > maxlen)
135             len = maxlen;
136         len -= buflen;
137
138         memcpy (buf + buflen, src, len);
139         buflen += len;
140         srclen -= len;
141         src += len;
142
143         if (buflen == maxlen) {
144             if (! _cairo_writen (fd, buf, buflen))
145                 return -1;
146
147             buflen = 0;
148         }
149     }
150
151     return buflen;
152 }
153
154 static const char *
155 write_ppm (cairo_surface_t *surface, int fd)
156 {
157     char buf[4096];
158     cairo_format_t format;
159     const char *format_str;
160     const unsigned char *data;
161     int len;
162     int width, height, stride;
163     int i, j;
164
165     data = cairo_image_surface_get_data (surface);
166     height = cairo_image_surface_get_height (surface);
167     width = cairo_image_surface_get_width (surface);
168     stride = cairo_image_surface_get_stride (surface);
169     format = cairo_image_surface_get_format (surface);
170     if (format == CAIRO_FORMAT_ARGB32) {
171         /* see if we can convert to a standard ppm type and trim a few bytes */
172         const unsigned char *alpha = data;
173         for (j = height; j--; alpha += stride) {
174             for (i = 0; i < width; i++) {
175                 if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000)
176                     goto done;
177             }
178         }
179         format = CAIRO_FORMAT_RGB24;
180  done: ;
181     }
182
183     switch (format) {
184     case CAIRO_FORMAT_ARGB32:
185         /* XXX need true alpha for svg */
186         format_str = "P7";
187         break;
188     case CAIRO_FORMAT_RGB24:
189         format_str = "P6";
190         break;
191     case CAIRO_FORMAT_A8:
192         format_str = "P5";
193         break;
194     case CAIRO_FORMAT_A1:
195     case CAIRO_FORMAT_RGB16_565:
196     case CAIRO_FORMAT_RGB30:
197     case CAIRO_FORMAT_INVALID:
198     default:
199         return "unhandled image format";
200     }
201
202     len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
203     for (j = 0; j < height; j++) {
204         const unsigned int *row = (unsigned int *) (data + stride * j);
205
206         switch ((int) format) {
207         case CAIRO_FORMAT_ARGB32:
208             len = _cairo_write (fd,
209                           buf, sizeof (buf), len,
210                           (unsigned char *) row, 4 * width);
211             break;
212         case CAIRO_FORMAT_RGB24:
213             for (i = 0; i < width; i++) {
214                 unsigned char rgb[3];
215                 unsigned int p = *row++;
216                 rgb[0] = (p & 0xff0000) >> 16;
217                 rgb[1] = (p & 0x00ff00) >> 8;
218                 rgb[2] = (p & 0x0000ff) >> 0;
219                 len = _cairo_write (fd,
220                               buf, sizeof (buf), len,
221                               rgb, 3);
222             }
223             break;
224         case CAIRO_FORMAT_A8:
225             len = _cairo_write (fd,
226                           buf, sizeof (buf), len,
227                           (unsigned char *) row, width);
228             break;
229         }
230         if (len < 0)
231             return "write failed";
232     }
233
234     if (len && ! _cairo_writen (fd, buf, len))
235         return "write failed";
236
237     return NULL;
238 }
239
240 static cairo_surface_t *
241 _create_image (void *closure,
242                cairo_content_t content,
243                double width, double height,
244                long uid)
245 {
246     cairo_surface_t **out = closure;
247     cairo_format_t format;
248     switch (content) {
249     case CAIRO_CONTENT_ALPHA:
250         format = CAIRO_FORMAT_A8;
251         break;
252     case CAIRO_CONTENT_COLOR:
253         format = CAIRO_FORMAT_RGB24;
254         break;
255     default:
256     case CAIRO_CONTENT_COLOR_ALPHA:
257         format = CAIRO_FORMAT_ARGB32;
258         break;
259     }
260     *out = cairo_image_surface_create (format, width, height);
261     return cairo_surface_reference (*out);
262 }
263
264 #if CAIRO_HAS_INTERPRETER
265 static const char *
266 _cairo_script_render_page (const char *filename,
267                            cairo_surface_t **surface_out)
268 {
269     cairo_script_interpreter_t *csi;
270     cairo_surface_t *surface = NULL;
271     cairo_status_t status;
272     const cairo_script_interpreter_hooks_t hooks = {
273         &surface,
274         _create_image,
275         NULL, /* surface_destroy */
276         NULL, /* context_create */
277         NULL, /* context_destroy */
278         NULL, /* show_page */
279         NULL  /* copy_page */
280     };
281
282     csi = cairo_script_interpreter_create ();
283     cairo_script_interpreter_install_hooks (csi, &hooks);
284     status = cairo_script_interpreter_run (csi, filename);
285     if (status) {
286         cairo_surface_destroy (surface);
287         surface = NULL;
288     }
289     status = cairo_script_interpreter_destroy (csi);
290     if (surface == NULL)
291         return "cairo-script interpreter failed";
292
293     if (status == CAIRO_STATUS_SUCCESS)
294         status = cairo_surface_status (surface);
295     if (status) {
296         cairo_surface_destroy (surface);
297         return cairo_status_to_string (status);
298     }
299
300     *surface_out = surface;
301     return NULL;
302 }
303
304 static const char *
305 cs_convert (char **argv, int fd)
306 {
307     const char *err;
308     cairo_surface_t *surface = NULL; /* silence compiler warning */
309
310     err = _cairo_script_render_page (argv[0], &surface);
311     if (err != NULL)
312         return err;
313
314     err = write_ppm (surface, fd);
315     cairo_surface_destroy (surface);
316
317     return err;
318 }
319 #else
320 static const char *
321 cs_convert (char **argv, int fd)
322 {
323     return "compiled without CairoScript support.";
324 }
325 #endif
326
327 #if CAIRO_CAN_TEST_PDF_SURFACE
328 /* adapted from pdf2png.c */
329 static const char *
330 _poppler_render_page (const char *filename,
331                       const char *page_label,
332                       cairo_surface_t **surface_out)
333 {
334     PopplerDocument *document;
335     PopplerPage *page;
336     double width, height;
337     GError *error = NULL;
338     gchar *absolute, *uri;
339     cairo_surface_t *surface;
340     cairo_t *cr;
341     cairo_status_t status;
342
343     if (g_path_is_absolute (filename)) {
344         absolute = g_strdup (filename);
345     } else {
346         gchar *dir = g_get_current_dir ();
347         absolute = g_build_filename (dir, filename, (gchar *) 0);
348         g_free (dir);
349     }
350
351     uri = g_filename_to_uri (absolute, NULL, &error);
352     g_free (absolute);
353     if (uri == NULL)
354         return error->message; /* XXX g_error_free (error) */
355
356     document = poppler_document_new_from_file (uri, NULL, &error);
357     g_free (uri);
358     if (document == NULL)
359         return error->message; /* XXX g_error_free (error) */
360
361     page = poppler_document_get_page_by_label (document, page_label);
362     g_object_unref (document);
363     if (page == NULL)
364         return "page not found";
365
366     poppler_page_get_size (page, &width, &height);
367
368     surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
369     cr = cairo_create (surface);
370
371     cairo_set_source_rgb (cr, 1., 1., 1.);
372     cairo_paint (cr);
373     cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
374
375     poppler_page_render (page, cr);
376     g_object_unref (page);
377
378     cairo_pop_group_to_source (cr);
379     cairo_paint (cr);
380
381     status = cairo_status (cr);
382     cairo_destroy (cr);
383
384     if (status) {
385         cairo_surface_destroy (surface);
386         return  cairo_status_to_string (status);
387     }
388
389     *surface_out = surface;
390     return NULL;
391 }
392
393 static const char *
394 pdf_convert (char **argv, int fd)
395 {
396     const char *err;
397     cairo_surface_t *surface = NULL; /* silence compiler warning */
398
399     err = _poppler_render_page (argv[0], argv[1], &surface);
400     if (err != NULL)
401         return err;
402
403     err = write_ppm (surface, fd);
404     cairo_surface_destroy (surface);
405
406     return err;
407 }
408 #else
409 static const char *
410 pdf_convert (char **argv, int fd)
411 {
412     return "compiled without PDF support.";
413 }
414 #endif
415
416 #if CAIRO_CAN_TEST_SVG_SURFACE
417 static const char *
418 _rsvg_render_page (const char *filename,
419                    cairo_surface_t **surface_out)
420 {
421     RsvgHandle *handle;
422     RsvgDimensionData dimensions;
423     GError *error = NULL;
424     cairo_surface_t *surface;
425     cairo_t *cr;
426     cairo_status_t status;
427
428     handle = rsvg_handle_new_from_file (filename, &error);
429     if (handle == NULL)
430         return error->message; /* XXX g_error_free */
431
432     rsvg_handle_get_dimensions (handle, &dimensions);
433     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
434                                           dimensions.width,
435                                           dimensions.height);
436     cr = cairo_create (surface);
437
438     rsvg_handle_render_cairo (handle, cr);
439     g_object_unref (handle);
440
441     status = cairo_status (cr);
442     cairo_destroy (cr);
443
444     if (status) {
445         cairo_surface_destroy (surface);
446         return  cairo_status_to_string (status);
447     }
448
449     *surface_out = surface;
450     return NULL;
451 }
452
453 static const char *
454 svg_convert (char **argv, int fd)
455 {
456     const char *err;
457     cairo_surface_t *surface = NULL; /* silence compiler warning */
458
459     err = _rsvg_render_page (argv[0], &surface);
460     if (err != NULL)
461         return err;
462
463     err = write_ppm (surface, fd);
464     cairo_surface_destroy (surface);
465
466     return err;
467 }
468 #else
469 static const char *
470 svg_convert (char **argv, int fd)
471 {
472     return "compiled without SVG support.";
473 }
474 #endif
475
476 #if CAIRO_HAS_SPECTRE
477 static const char *
478 _spectre_render_page (const char *filename,
479                       const char *page_label,
480                       cairo_surface_t **surface_out)
481 {
482     static const cairo_user_data_key_t key;
483
484     SpectreDocument *document;
485     SpectreStatus status;
486     int width, height, stride;
487     unsigned char *pixels;
488     cairo_surface_t *surface;
489
490     document = spectre_document_new ();
491     spectre_document_load (document, filename);
492     status = spectre_document_status (document);
493     if (status) {
494         spectre_document_free (document);
495         return spectre_status_to_string (status);
496     }
497
498     if (page_label) {
499         SpectrePage *page;
500         SpectreRenderContext *rc;
501
502         page = spectre_document_get_page_by_label (document, page_label);
503         spectre_document_free (document);
504         if (page == NULL)
505             return "page not found";
506
507         spectre_page_get_size (page, &width, &height);
508         rc = spectre_render_context_new ();
509         spectre_render_context_set_page_size (rc, width, height);
510         spectre_page_render (page, rc, &pixels, &stride);
511         spectre_render_context_free (rc);
512         status = spectre_page_status (page);
513         spectre_page_free (page);
514         if (status) {
515             free (pixels);
516             return spectre_status_to_string (status);
517         }
518     } else {
519         spectre_document_get_page_size (document, &width, &height);
520         spectre_document_render (document, &pixels, &stride);
521         spectre_document_free (document);
522     }
523
524     surface = cairo_image_surface_create_for_data (pixels,
525                                                    CAIRO_FORMAT_RGB24,
526                                                    width, height,
527                                                    stride);
528     cairo_surface_set_user_data (surface, &key,
529                                  pixels, (cairo_destroy_func_t) free);
530     *surface_out = surface;
531     return NULL;
532 }
533
534 static const char *
535 ps_convert (char **argv, int fd)
536 {
537     const char *err;
538     cairo_surface_t *surface = NULL; /* silence compiler warning */
539
540     err = _spectre_render_page (argv[0], argv[1], &surface);
541     if (err != NULL)
542         return err;
543
544     err = write_ppm (surface, fd);
545     cairo_surface_destroy (surface);
546
547     return err;
548 }
549 #else
550 static const char *
551 ps_convert (char **argv, int fd)
552 {
553     return "compiled without PostScript support.";
554 }
555 #endif
556
557 static const char *
558 convert (char **argv, int fd)
559 {
560     static const struct converter {
561         const char *type;
562         const char *(*func) (char **, int);
563     } converters[] = {
564         { "cs", cs_convert },
565         { "pdf", pdf_convert },
566         { "ps", ps_convert },
567         { "svg", svg_convert },
568         { NULL, NULL }
569     };
570     const struct converter *converter = converters;
571     char *type;
572
573     type = strrchr (argv[0], '.');
574     if (type == NULL)
575         return "no file extension";
576     type++;
577
578     while (converter->type) {
579         if (strcmp (type, converter->type) == 0)
580             return converter->func (argv, fd);
581         converter++;
582     }
583     return "no converter";
584 }
585
586 #if CAN_RUN_AS_DAEMON
587 static int
588 _getline (int fd, char **linep, size_t *lenp)
589 {
590     char *line;
591     size_t len, i;
592     ssize_t ret;
593
594     line = *linep;
595     if (line == NULL) {
596         line = malloc (1024);
597         if (line == NULL)
598             return -1;
599         line[0] = '\0';
600         len = 1024;
601     } else
602         len = *lenp;
603
604     /* XXX simple, but ugly! */
605     i = 0;
606     do {
607         if (i == len - 1) {
608             char *nline;
609
610             nline = realloc (line, len + 1024);
611             if (nline == NULL)
612                 goto out;
613
614             line = nline;
615             len += 1024;
616         }
617
618         ret = read (fd, line + i, 1);
619         if (ret == -1 || ret == 0)
620             goto out;
621     } while (line[i++] != '\n');
622
623 out:
624     line[i] = '\0';
625     *linep = line;
626     *lenp = len;
627     return i-1;
628 }
629
630 static int
631 split_line (char *line, char *argv[], int max_argc)
632 {
633     int i = 0;
634
635     max_argc--; /* leave one spare for the trailing NULL */
636
637     argv[i++] = line;
638     while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
639         *line++ = '\0';
640         argv[i++] = line;
641     }
642
643     /* chomp the newline */
644     line = strchr (argv[i-1], '\n');
645     if (line != NULL)
646         *line = '\0';
647
648     argv[i] = NULL;
649
650     return i;
651 }
652
653 static int
654 any2ppm_daemon_exists (void)
655 {
656     struct stat st;
657     int fd;
658     char buf[80];
659     int pid;
660     int ret;
661
662     if (stat (SOCKET_PATH, &st) < 0)
663         return 0;
664
665     fd = open (SOCKET_PATH ".pid", O_RDONLY);
666     if (fd < 0)
667         return 0;
668
669     pid = 0;
670     ret = read (fd, buf, sizeof (buf) - 1);
671     if (ret > 0) {
672         buf[ret] = '\0';
673         pid = atoi (buf);
674     }
675     close (fd);
676
677     return pid > 0 && kill (pid, 0) == 0;
678 }
679
680 static int
681 write_pid_file (void)
682 {
683     int fd;
684     char buf[80];
685     int ret;
686
687     fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
688     if (fd < 0)
689         return 0;
690
691     ret = sprintf (buf, "%d\n", getpid ());
692     ret = write (fd, buf, ret) == ret;
693     close (fd);
694
695     return ret;
696 }
697
698 static int
699 open_devnull_to_fd (int want_fd, int flags)
700 {
701     int error;
702     int got_fd;
703
704     close (want_fd);
705
706     got_fd = open("/dev/null", flags | O_CREAT, 0700);
707     if (got_fd == -1)
708         return -1;
709
710     error = dup2 (got_fd, want_fd);
711     close (got_fd);
712
713     return error;
714 }
715
716 static int
717 daemonize (void)
718 {
719     void (*oldhup) (int);
720
721     /* Let the parent go. */
722     switch (fork ()) {
723     case -1: return -1;
724     case 0: break;
725     default: _exit (0);
726     }
727
728     /* Become session leader. */
729     if (setsid () == -1)
730         return -1;
731
732     /* Refork to yield session leadership. */
733     oldhup = signal (SIGHUP, SIG_IGN);
734
735     switch (fork ()) {          /* refork to yield session leadership. */
736     case -1: return -1;
737     case 0: break;
738     default: _exit (0);
739     }
740
741     signal (SIGHUP, oldhup);
742
743     /* Establish stdio. */
744     if (open_devnull_to_fd (0, O_RDONLY) == -1)
745         return -1;
746     if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
747         return -1;
748     if (dup2 (1, 2) == -1)
749         return -1;
750
751     return 0;
752 }
753
754 static const char *
755 any2ppm_daemon (void)
756 {
757     int timeout = TIMEOUT;
758     struct pollfd pfd;
759     int sk, fd;
760     long flags;
761     struct sockaddr_un addr;
762     char *line = NULL;
763     size_t len = 0;
764
765 #ifdef SIGPIPE
766     signal (SIGPIPE, SIG_IGN);
767 #endif
768
769     /* XXX racy! */
770     if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
771         return "any2ppm daemon already running";
772
773     unlink (SOCKET_PATH);
774
775     sk = socket (PF_UNIX, SOCK_STREAM, 0);
776     if (sk == -1)
777         return "unable to create socket";
778
779     memset (&addr, 0, sizeof (addr));
780     addr.sun_family = AF_UNIX;
781     strcpy (addr.sun_path, SOCKET_PATH);
782     if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
783         close (sk);
784         return "unable to bind socket";
785     }
786
787     flags = fcntl (sk, F_GETFL);
788     if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
789         close (sk);
790         return "unable to set socket to non-blocking";
791     }
792
793     if (listen (sk, 5) == -1) {
794         close (sk);
795         return "unable to listen on socket";
796     }
797
798     /* ready for client connection - detach from parent/terminal */
799     if (getenv ("ANY2PPM_NODAEMON") == NULL && daemonize () == -1) {
800         close (sk);
801         return "unable to detach from parent";
802     }
803
804     if (! write_pid_file ()) {
805         close (sk);
806         return "unable to write pid file";
807     }
808
809     if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
810         timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
811         if (timeout == 0)
812             timeout = -1;
813         if (timeout > 0)
814             timeout *= 1000; /* convert env (in seconds) to milliseconds */
815     }
816
817     pfd.fd = sk;
818     pfd.events = POLLIN;
819     pfd.revents = 0; /* valgrind */
820     while (poll (&pfd, 1, timeout) > 0) {
821         while ((fd = accept (sk, NULL, NULL)) != -1) {
822             if (_getline (fd, &line, &len) != -1) {
823                 char *argv[10];
824
825                 if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) {
826                     const char *err;
827
828                     err = convert (argv, fd);
829                     if (err != NULL) {
830                         FILE *file = fopen (".any2ppm.errors", "a");
831                         if (file != NULL) {
832                             fprintf (file,
833                                      "Failed to convert '%s': %s\n",
834                                      argv[0], err);
835                             fclose (file);
836                         }
837                     }
838                 }
839             }
840             close (fd);
841         }
842     }
843     close (sk);
844     unlink (SOCKET_PATH);
845     unlink (SOCKET_PATH ".pid");
846
847     free (line);
848     return NULL;
849 }
850 #else
851 static const char *
852 any2ppm_daemon (void)
853 {
854     return "daemon not compiled in.";
855 }
856 #endif
857
858 int
859 main (int argc, char **argv)
860 {
861     const char *err;
862
863 #if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE
864     g_type_init ();
865 #endif
866
867 #if CAIRO_CAN_TEST_SVG_SURFACE
868     rsvg_init ();
869     rsvg_set_default_dpi (72.0);
870 #endif
871
872     if (argc == 1)
873         err = any2ppm_daemon ();
874     else
875         err = convert (argv + 1, 1);
876     if (err != NULL) {
877         fprintf (stderr, "Failed to run converter: %s\n", err);
878         return EXIT_FAILURE;
879     }
880
881     return EXIT_SUCCESS;
882 }