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