| /* |
| * Copyright © 2008 Chris Wilson |
| * |
| * Permission to use, copy, modify, distribute, and sell this software |
| * and its documentation for any purpose is hereby granted without |
| * fee, provided that the above copyright notice appear in all copies |
| * and that both that copyright notice and this permission notice |
| * appear in supporting documentation, and that the name of |
| * Chris Wilson not be used in advertising or publicity pertaining to |
| * distribution of the software without specific, written prior |
| * permission. Chris Wilson makes no representations about the |
| * suitability of this software for any purpose. It is provided "as |
| * is" without express or implied warranty. |
| * |
| * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS |
| * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL, |
| * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER |
| * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR |
| * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| * Author: Chris Wilson <chris@chris-wilson.co.uk> |
| * |
| * Contributor(s): |
| * Carlos Garcia Campos <carlosgc@gnome.org> |
| * |
| * Adapted from pdf2png.c: |
| * Copyright © 2005 Red Hat, Inc. |
| * |
| * Permission to use, copy, modify, distribute, and sell this software |
| * and its documentation for any purpose is hereby granted without |
| * fee, provided that the above copyright notice appear in all copies |
| * and that both that copyright notice and this permission notice |
| * appear in supporting documentation, and that the name of |
| * Red Hat, Inc. not be used in advertising or publicity pertaining to |
| * distribution of the software without specific, written prior |
| * permission. Red Hat, Inc. makes no representations about the |
| * suitability of this software for any purpose. It is provided "as |
| * is" without express or implied warranty. |
| * |
| * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS |
| * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, |
| * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER |
| * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR |
| * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| * Author: Kristian Høgsberg <krh@redhat.com> |
| */ |
| |
| #if HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <cairo.h> |
| #include <cairo-script-interpreter.h> |
| |
| #if CAIRO_CAN_TEST_PDF_SURFACE |
| #include <poppler.h> |
| #endif |
| |
| #if CAIRO_CAN_TEST_SVG_SURFACE |
| #include <librsvg/rsvg.h> |
| #include <librsvg/rsvg-cairo.h> |
| #endif |
| |
| #if CAIRO_HAS_SPECTRE |
| #include <libspectre/spectre.h> |
| #endif |
| |
| #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 |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <sys/poll.h> |
| #include <sys/un.h> |
| #include <errno.h> |
| |
| #define SOCKET_PATH "./.any2ppm" |
| #define TIMEOUT 60000 /* 60 seconds */ |
| |
| #define CAN_RUN_AS_DAEMON 1 |
| #endif |
| |
| #define ARRAY_LENGTH(A) (sizeof (A) / sizeof (A[0])) |
| |
| static int |
| _writen (int fd, char *buf, int len) |
| { |
| while (len) { |
| int ret; |
| |
| ret = write (fd, buf, len); |
| if (ret == -1) { |
| int err = errno; |
| switch (err) { |
| case EINTR: |
| case EAGAIN: |
| continue; |
| default: |
| return 0; |
| } |
| } |
| len -= ret; |
| buf += ret; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| _write (int fd, |
| char *buf, int maxlen, int buflen, |
| const unsigned char *src, int srclen) |
| { |
| if (buflen < 0) |
| return buflen; |
| |
| while (srclen) { |
| int len; |
| |
| len = buflen + srclen; |
| if (len > maxlen) |
| len = maxlen; |
| len -= buflen; |
| |
| memcpy (buf + buflen, src, len); |
| buflen += len; |
| srclen -= len; |
| src += len; |
| |
| if (buflen == maxlen) { |
| if (! _writen (fd, buf, buflen)) |
| return -1; |
| |
| buflen = 0; |
| } |
| } |
| |
| return buflen; |
| } |
| |
| static const char * |
| write_ppm (cairo_surface_t *surface, int fd) |
| { |
| char buf[4096]; |
| cairo_format_t format; |
| const char *format_str; |
| const unsigned char *data; |
| int len; |
| int width, height, stride; |
| int i, j; |
| |
| data = cairo_image_surface_get_data (surface); |
| height = cairo_image_surface_get_height (surface); |
| width = cairo_image_surface_get_width (surface); |
| stride = cairo_image_surface_get_stride (surface); |
| format = cairo_image_surface_get_format (surface); |
| if (format == CAIRO_FORMAT_ARGB32) { |
| /* see if we can convert to a standard ppm type and trim a few bytes */ |
| const unsigned char *alpha = data; |
| for (j = height; j--; alpha += stride) { |
| for (i = 0; i < width; i++) { |
| if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000) |
| goto done; |
| } |
| } |
| format = CAIRO_FORMAT_RGB24; |
| done: ; |
| } |
| |
| switch (format) { |
| case CAIRO_FORMAT_ARGB32: |
| /* XXX need true alpha for svg */ |
| format_str = "P7"; |
| break; |
| case CAIRO_FORMAT_RGB24: |
| format_str = "P6"; |
| break; |
| case CAIRO_FORMAT_A8: |
| format_str = "P5"; |
| break; |
| case CAIRO_FORMAT_A1: |
| case CAIRO_FORMAT_RGB16_565: |
| case CAIRO_FORMAT_INVALID: |
| default: |
| return "unhandled image format"; |
| } |
| |
| len = sprintf (buf, "%s %d %d 255\n", format_str, width, height); |
| for (j = 0; j < height; j++) { |
| const unsigned int *row = (unsigned int *) (data + stride * j); |
| |
| switch ((int) format) { |
| case CAIRO_FORMAT_ARGB32: |
| len = _write (fd, |
| buf, sizeof (buf), len, |
| (unsigned char *) row, 4 * width); |
| break; |
| case CAIRO_FORMAT_RGB24: |
| for (i = 0; i < width; i++) { |
| unsigned char rgb[3]; |
| unsigned int p = *row++; |
| rgb[0] = (p & 0xff0000) >> 16; |
| rgb[1] = (p & 0x00ff00) >> 8; |
| rgb[2] = (p & 0x0000ff) >> 0; |
| len = _write (fd, |
| buf, sizeof (buf), len, |
| rgb, 3); |
| } |
| break; |
| case CAIRO_FORMAT_A8: |
| len = _write (fd, |
| buf, sizeof (buf), len, |
| (unsigned char *) row, width); |
| break; |
| } |
| if (len < 0) |
| return "write failed"; |
| } |
| |
| if (len && ! _writen (fd, buf, len)) |
| return "write failed"; |
| |
| return NULL; |
| } |
| |
| static cairo_surface_t * |
| _create_image (void *closure, |
| cairo_content_t content, |
| double width, double height, |
| long uid) |
| { |
| cairo_surface_t **out = closure; |
| cairo_format_t format; |
| switch (content) { |
| case CAIRO_CONTENT_ALPHA: |
| format = CAIRO_FORMAT_A8; |
| break; |
| case CAIRO_CONTENT_COLOR: |
| format = CAIRO_FORMAT_RGB24; |
| break; |
| default: |
| case CAIRO_CONTENT_COLOR_ALPHA: |
| format = CAIRO_FORMAT_ARGB32; |
| break; |
| } |
| *out = cairo_image_surface_create (format, width, height); |
| return cairo_surface_reference (*out); |
| } |
| |
| #if CAIRO_HAS_INTERPRETER |
| static const char * |
| _cairo_script_render_page (const char *filename, |
| cairo_surface_t **surface_out) |
| { |
| cairo_script_interpreter_t *csi; |
| cairo_surface_t *surface = NULL; |
| cairo_status_t status; |
| const cairo_script_interpreter_hooks_t hooks = { |
| .closure = &surface, |
| .surface_create = _create_image, |
| }; |
| |
| csi = cairo_script_interpreter_create (); |
| cairo_script_interpreter_install_hooks (csi, &hooks); |
| status = cairo_script_interpreter_run (csi, filename); |
| if (status) { |
| cairo_surface_destroy (surface); |
| surface = NULL; |
| } |
| status = cairo_script_interpreter_destroy (csi); |
| if (surface == NULL) |
| return "cairo-script interpreter failed"; |
| |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = cairo_surface_status (surface); |
| if (status) { |
| cairo_surface_destroy (surface); |
| return cairo_status_to_string (status); |
| } |
| |
| *surface_out = surface; |
| return NULL; |
| } |
| |
| static const char * |
| cs_convert (char **argv, int fd) |
| { |
| const char *err; |
| cairo_surface_t *surface = NULL; /* silence compiler warning */ |
| |
| err = _cairo_script_render_page (argv[0], &surface); |
| if (err != NULL) |
| return err; |
| |
| err = write_ppm (surface, fd); |
| cairo_surface_destroy (surface); |
| |
| return err; |
| } |
| #else |
| static const char * |
| cs_convert (char **argv, int fd) |
| { |
| return "compiled without CairoScript support."; |
| } |
| #endif |
| |
| #if CAIRO_CAN_TEST_PDF_SURFACE |
| /* adapted from pdf2png.c */ |
| static const char * |
| _poppler_render_page (const char *filename, |
| const char *page_label, |
| cairo_surface_t **surface_out) |
| { |
| PopplerDocument *document; |
| PopplerPage *page; |
| double width, height; |
| GError *error = NULL; |
| gchar *absolute, *uri; |
| cairo_surface_t *surface; |
| cairo_t *cr; |
| cairo_status_t status; |
| |
| if (g_path_is_absolute (filename)) { |
| absolute = g_strdup (filename); |
| } else { |
| gchar *dir = g_get_current_dir (); |
| absolute = g_build_filename (dir, filename, (gchar *) 0); |
| g_free (dir); |
| } |
| |
| uri = g_filename_to_uri (absolute, NULL, &error); |
| g_free (absolute); |
| if (uri == NULL) |
| return error->message; /* XXX g_error_free (error) */ |
| |
| document = poppler_document_new_from_file (uri, NULL, &error); |
| g_free (uri); |
| if (document == NULL) |
| return error->message; /* XXX g_error_free (error) */ |
| |
| page = poppler_document_get_page_by_label (document, page_label); |
| g_object_unref (document); |
| if (page == NULL) |
| return "page not found"; |
| |
| poppler_page_get_size (page, &width, &height); |
| |
| surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); |
| cr = cairo_create (surface); |
| |
| cairo_set_source_rgb (cr, 1., 1., 1.); |
| cairo_paint (cr); |
| cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); |
| |
| poppler_page_render (page, cr); |
| g_object_unref (page); |
| |
| cairo_pop_group_to_source (cr); |
| cairo_paint (cr); |
| |
| status = cairo_status (cr); |
| cairo_destroy (cr); |
| |
| if (status) { |
| cairo_surface_destroy (surface); |
| return cairo_status_to_string (status); |
| } |
| |
| *surface_out = surface; |
| return NULL; |
| } |
| |
| static const char * |
| pdf_convert (char **argv, int fd) |
| { |
| const char *err; |
| cairo_surface_t *surface = NULL; /* silence compiler warning */ |
| |
| err = _poppler_render_page (argv[0], argv[1], &surface); |
| if (err != NULL) |
| return err; |
| |
| err = write_ppm (surface, fd); |
| cairo_surface_destroy (surface); |
| |
| return err; |
| } |
| #else |
| static const char * |
| pdf_convert (char **argv, int fd) |
| { |
| return "compiled without PDF support."; |
| } |
| #endif |
| |
| #if CAIRO_CAN_TEST_SVG_SURFACE |
| static const char * |
| _rsvg_render_page (const char *filename, |
| cairo_surface_t **surface_out) |
| { |
| RsvgHandle *handle; |
| RsvgDimensionData dimensions; |
| GError *error = NULL; |
| cairo_surface_t *surface; |
| cairo_t *cr; |
| cairo_status_t status; |
| |
| handle = rsvg_handle_new_from_file (filename, &error); |
| if (handle == NULL) |
| return error->message; /* XXX g_error_free */ |
| |
| rsvg_handle_get_dimensions (handle, &dimensions); |
| surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, |
| dimensions.width, |
| dimensions.height); |
| cr = cairo_create (surface); |
| |
| rsvg_handle_render_cairo (handle, cr); |
| g_object_unref (handle); |
| |
| status = cairo_status (cr); |
| cairo_destroy (cr); |
| |
| if (status) { |
| cairo_surface_destroy (surface); |
| return cairo_status_to_string (status); |
| } |
| |
| *surface_out = surface; |
| return NULL; |
| } |
| |
| static const char * |
| svg_convert (char **argv, int fd) |
| { |
| const char *err; |
| cairo_surface_t *surface = NULL; /* silence compiler warning */ |
| |
| err = _rsvg_render_page (argv[0], &surface); |
| if (err != NULL) |
| return err; |
| |
| err = write_ppm (surface, fd); |
| cairo_surface_destroy (surface); |
| |
| return err; |
| } |
| #else |
| static const char * |
| svg_convert (char **argv, int fd) |
| { |
| return "compiled without SVG support."; |
| } |
| #endif |
| |
| #if CAIRO_HAS_SPECTRE |
| static const char * |
| _spectre_render_page (const char *filename, |
| const char *page_label, |
| cairo_surface_t **surface_out) |
| { |
| static const cairo_user_data_key_t key; |
| |
| SpectreDocument *document; |
| SpectreStatus status; |
| int width, height, stride; |
| unsigned char *pixels; |
| cairo_surface_t *surface; |
| |
| document = spectre_document_new (); |
| spectre_document_load (document, filename); |
| status = spectre_document_status (document); |
| if (status) { |
| spectre_document_free (document); |
| return spectre_status_to_string (status); |
| } |
| |
| if (page_label) { |
| SpectrePage *page; |
| SpectreRenderContext *rc; |
| |
| page = spectre_document_get_page_by_label (document, page_label); |
| spectre_document_free (document); |
| if (page == NULL) |
| return "page not found"; |
| |
| spectre_page_get_size (page, &width, &height); |
| rc = spectre_render_context_new (); |
| spectre_render_context_set_page_size (rc, width, height); |
| spectre_page_render (page, rc, &pixels, &stride); |
| spectre_render_context_free (rc); |
| status = spectre_page_status (page); |
| spectre_page_free (page); |
| if (status) { |
| free (pixels); |
| return spectre_status_to_string (status); |
| } |
| } else { |
| spectre_document_get_page_size (document, &width, &height); |
| spectre_document_render (document, &pixels, &stride); |
| spectre_document_free (document); |
| } |
| |
| surface = cairo_image_surface_create_for_data (pixels, |
| CAIRO_FORMAT_RGB24, |
| width, height, |
| stride); |
| cairo_surface_set_user_data (surface, &key, |
| pixels, (cairo_destroy_func_t) free); |
| *surface_out = surface; |
| return NULL; |
| } |
| |
| static const char * |
| ps_convert (char **argv, int fd) |
| { |
| const char *err; |
| cairo_surface_t *surface = NULL; /* silence compiler warning */ |
| |
| err = _spectre_render_page (argv[0], argv[1], &surface); |
| if (err != NULL) |
| return err; |
| |
| err = write_ppm (surface, fd); |
| cairo_surface_destroy (surface); |
| |
| return err; |
| } |
| #else |
| static const char * |
| ps_convert (char **argv, int fd) |
| { |
| return "compiled without PostScript support."; |
| } |
| #endif |
| |
| static const char * |
| convert (char **argv, int fd) |
| { |
| static const struct converter { |
| const char *type; |
| const char *(*func) (char **, int); |
| } converters[] = { |
| { "cs", cs_convert }, |
| { "pdf", pdf_convert }, |
| { "ps", ps_convert }, |
| { "svg", svg_convert }, |
| { NULL, NULL } |
| }; |
| const struct converter *converter = converters; |
| char *type; |
| |
| type = strrchr (argv[0], '.'); |
| if (type == NULL) |
| return "no file extension"; |
| type++; |
| |
| while (converter->type) { |
| if (strcmp (type, converter->type) == 0) |
| return converter->func (argv, fd); |
| converter++; |
| } |
| return "no converter"; |
| } |
| |
| #if CAN_RUN_AS_DAEMON |
| static int |
| _getline (int fd, char **linep, size_t *lenp) |
| { |
| char *line; |
| size_t len, i; |
| ssize_t ret; |
| |
| line = *linep; |
| if (line == NULL) { |
| line = malloc (1024); |
| if (line == NULL) |
| return -1; |
| line[0] = '\0'; |
| len = 1024; |
| } else |
| len = *lenp; |
| |
| /* XXX simple, but ugly! */ |
| i = 0; |
| do { |
| if (i == len - 1) { |
| char *nline; |
| |
| nline = realloc (line, len + 1024); |
| if (nline == NULL) |
| goto out; |
| |
| line = nline; |
| len += 1024; |
| } |
| |
| ret = read (fd, line + i, 1); |
| if (ret == -1 || ret == 0) |
| goto out; |
| } while (line[i++] != '\n'); |
| |
| out: |
| line[i] = '\0'; |
| *linep = line; |
| *lenp = len; |
| return i-1; |
| } |
| |
| static int |
| split_line (char *line, char *argv[], int max_argc) |
| { |
| int i = 0; |
| |
| max_argc--; /* leave one spare for the trailing NULL */ |
| |
| argv[i++] = line; |
| while (i < max_argc && (line = strchr (line, ' ')) != NULL) { |
| *line++ = '\0'; |
| argv[i++] = line; |
| } |
| |
| /* chomp the newline */ |
| line = strchr (argv[i-1], '\n'); |
| if (line != NULL) |
| *line = '\0'; |
| |
| argv[i] = NULL; |
| |
| return i; |
| } |
| |
| static int |
| any2ppm_daemon_exists (void) |
| { |
| struct stat st; |
| int fd; |
| char buf[80]; |
| int pid; |
| int ret; |
| |
| if (stat (SOCKET_PATH, &st) < 0) |
| return 0; |
| |
| fd = open (SOCKET_PATH ".pid", O_RDONLY); |
| if (fd < 0) |
| return 0; |
| |
| pid = 0; |
| ret = read (fd, buf, sizeof (buf) - 1); |
| if (ret > 0) { |
| buf[ret] = '\0'; |
| pid = atoi (buf); |
| } |
| close (fd); |
| |
| return pid > 0 && kill (pid, 0) == 0; |
| } |
| |
| static int |
| write_pid_file (void) |
| { |
| int fd; |
| char buf[80]; |
| int ret; |
| |
| fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666); |
| if (fd < 0) |
| return 0; |
| |
| ret = sprintf (buf, "%d\n", getpid ()); |
| ret = write (fd, buf, ret) == ret; |
| close (fd); |
| |
| return ret; |
| } |
| |
| static int |
| open_devnull_to_fd (int want_fd, int flags) |
| { |
| int error; |
| int got_fd; |
| |
| close (want_fd); |
| |
| got_fd = open("/dev/null", flags | O_CREAT, 0700); |
| if (got_fd == -1) |
| return -1; |
| |
| error = dup2 (got_fd, want_fd); |
| close (got_fd); |
| |
| return error; |
| } |
| |
| static int |
| daemonize (void) |
| { |
| void (*oldhup) (int); |
| |
| /* Let the parent go. */ |
| switch (fork ()) { |
| case -1: return -1; |
| case 0: break; |
| default: _exit (0); |
| } |
| |
| /* Become session leader. */ |
| if (setsid () == -1) |
| return -1; |
| |
| /* Refork to yield session leadership. */ |
| oldhup = signal (SIGHUP, SIG_IGN); |
| |
| switch (fork ()) { /* refork to yield session leadership. */ |
| case -1: return -1; |
| case 0: break; |
| default: _exit (0); |
| } |
| |
| signal (SIGHUP, oldhup); |
| |
| /* Establish stdio. */ |
| if (open_devnull_to_fd (0, O_RDONLY) == -1) |
| return -1; |
| if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1) |
| return -1; |
| if (dup2 (1, 2) == -1) |
| return -1; |
| |
| return 0; |
| } |
| |
| static const char * |
| any2ppm_daemon (void) |
| { |
| int timeout = TIMEOUT; |
| struct pollfd pfd; |
| int sk, fd; |
| long flags; |
| struct sockaddr_un addr; |
| char *line = NULL; |
| size_t len = 0; |
| |
| #ifdef SIGPIPE |
| signal (SIGPIPE, SIG_IGN); |
| #endif |
| |
| /* XXX racy! */ |
| if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ()) |
| return "any2ppm daemon already running"; |
| |
| unlink (SOCKET_PATH); |
| |
| sk = socket (PF_UNIX, SOCK_STREAM, 0); |
| if (sk == -1) |
| return "unable to create socket"; |
| |
| memset (&addr, 0, sizeof (addr)); |
| addr.sun_family = AF_UNIX; |
| strcpy (addr.sun_path, SOCKET_PATH); |
| if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) { |
| close (sk); |
| return "unable to bind socket"; |
| } |
| |
| flags = fcntl (sk, F_GETFL); |
| if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) { |
| close (sk); |
| return "unable to set socket to non-blocking"; |
| } |
| |
| if (listen (sk, 5) == -1) { |
| close (sk); |
| return "unable to listen on socket"; |
| } |
| |
| /* ready for client connection - detach from parent/terminal */ |
| if (getenv ("ANY2PPM_NODAEMON") == NULL && daemonize () == -1) { |
| close (sk); |
| return "unable to detach from parent"; |
| } |
| |
| if (! write_pid_file ()) { |
| close (sk); |
| return "unable to write pid file"; |
| } |
| |
| if (getenv ("ANY2PPM_TIMEOUT") != NULL) { |
| timeout = atoi (getenv ("ANY2PPM_TIMEOUT")); |
| if (timeout == 0) |
| timeout = -1; |
| if (timeout > 0) |
| timeout *= 1000; /* convert env (in seconds) to milliseconds */ |
| } |
| |
| pfd.fd = sk; |
| pfd.events = POLLIN; |
| pfd.revents = 0; /* valgrind */ |
| while (poll (&pfd, 1, timeout) > 0) { |
| while ((fd = accept (sk, NULL, NULL)) != -1) { |
| if (_getline (fd, &line, &len) != -1) { |
| char *argv[10]; |
| |
| if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) { |
| const char *err; |
| |
| err = convert (argv, fd); |
| if (err != NULL) { |
| FILE *file = fopen (".any2ppm.errors", "a"); |
| if (file != NULL) { |
| fprintf (file, |
| "Failed to convert '%s': %s\n", |
| argv[0], err); |
| fclose (file); |
| } |
| } |
| } |
| } |
| close (fd); |
| } |
| } |
| close (sk); |
| unlink (SOCKET_PATH); |
| unlink (SOCKET_PATH ".pid"); |
| |
| free (line); |
| return NULL; |
| } |
| #else |
| static const char * |
| any2ppm_daemon (void) |
| { |
| return "daemon not compiled in."; |
| } |
| #endif |
| |
| int |
| main (int argc, char **argv) |
| { |
| const char *err; |
| |
| #if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE |
| g_type_init (); |
| #endif |
| |
| #if CAIRO_CAN_TEST_SVG_SURFACE |
| rsvg_init (); |
| rsvg_set_default_dpi (72.0); |
| #endif |
| |
| if (argc == 1) |
| err = any2ppm_daemon (); |
| else |
| err = convert (argv + 1, 1); |
| if (err != NULL) { |
| fprintf (stderr, "Failed to run converter: %s\n", err); |
| return EXIT_FAILURE; |
| } |
| |
| return EXIT_SUCCESS; |
| } |