summaryrefslogtreecommitdiff
path: root/src/gutenprintui2/plist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gutenprintui2/plist.c')
-rw-r--r--src/gutenprintui2/plist.c1751
1 files changed, 1751 insertions, 0 deletions
diff --git a/src/gutenprintui2/plist.c b/src/gutenprintui2/plist.c
new file mode 100644
index 0000000..f895580
--- /dev/null
+++ b/src/gutenprintui2/plist.c
@@ -0,0 +1,1751 @@
+/*
+ * "$Id: plist.c,v 1.5 2005/06/14 02:47:13 rlk Exp $"
+ *
+ * Print plug-in for the GIMP.
+ *
+ * Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
+ * Robert Krawitz (rlk@alum.mit.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gutenprint/gutenprint-intl-internal.h>
+#include <gutenprint/gutenprint.h>
+#include <gutenprintui2/gutenprintui.h>
+#include "gutenprintui-internal.h"
+
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+
+typedef enum
+{
+ PRINTERS_NONE,
+ PRINTERS_LPC,
+ PRINTERS_LPSTAT
+} printer_system_t;
+
+static int compare_printers (stpui_plist_t *p1, stpui_plist_t *p2);
+
+int stpui_plist_current = 0, /* Current system printer */
+ stpui_plist_count = 0; /* Number of system printers */
+stpui_plist_t *stpui_plist; /* System printers */
+int stpui_show_all_paper_sizes = 0;
+static char *printrc_name = NULL;
+static char *image_type;
+static gint image_raw_channels = 0;
+static gint image_channel_depth = 8;
+static stp_string_list_t *default_parameters = NULL;
+stp_string_list_t *stpui_system_print_queues;
+static const char *copy_count_name = "STPUICopyCount";
+
+#define SAFE_FREE(x) \
+do \
+{ \
+ if ((x)) \
+ g_free((char *)(x)); \
+ ((x)) = NULL; \
+} while (0)
+
+typedef struct
+{
+ const char *printing_system_name;
+ const char *printing_system_text;
+ const char *print_command;
+ const char *queue_select;
+ const char *raw_flag;
+ const char *key_file;
+ const char *scan_command;
+ const char *copy_count_command;
+} print_system_t;
+
+/*
+ * Generic printing system, based on SysV lp
+ */
+static const print_system_t default_printing_system =
+ { "SysV", N_("System V lp"), "lp -s", "-d", "-oraw", "/usr/bin/lp",
+ "/usr/bin/lpstat -v | grep -i '^device for ' | awk '{print $3}' | sed 's/://'",
+ "-n" };
+
+static print_system_t known_printing_systems[] =
+{
+ { "CUPS", N_("CUPS"), "lp -s", "-d", "-oraw", "/usr/sbin/cupsd",
+ "/usr/bin/lpstat -v | grep -i '^device for ' | awk '{print $3}' | sed 's/://'",
+ "-n" },
+ { "SysV", N_("System V lp"), "lp -s", "-d", "-oraw", "/usr/bin/lp",
+ "/usr/bin/lpstat -v | grep -i '^device for ' | awk '{print $3}' | sed 's/://'",
+ "-n" },
+ { "lpd", N_("Berkeley lpd (/etc/lpc)"), "lpr", "-P", "-l", "/etc/lpc",
+ "/etc/lpc status | grep '^...*:' | sed 's/:.*//'",
+ "-#" },
+ { "lpd", N_("Berkeley lpd (/usr/bsd/lpc)"), "lpr", "-P", "-l", "/usr/bsd/lpc",
+ "/usr/bsd/lpc status | grep '^...*:' | sed 's/:.*//'",
+ "-#" },
+ { "lpd", N_("Berkeley lpd (/usr/etc/lpc"), "lpr", "-P", "-l", "/usr/etc/lpc",
+ "/usr/etc/lpc status | grep '^...*:' | sed 's/:.*//'",
+ "-#" },
+ { "lpd", N_("Berkeley lpd (/usr/libexec/lpc)"), "lpr", "-P", "-l", "/usr/libexec/lpc",
+ "/usr/libexec/lpc status | grep '^...*:' | sed 's/:.*//'",
+ "-#" },
+ { "lpd", N_("Berkeley lpd (/usr/sbin/lpc)"), "lpr", "-P", "-l", "/usr/sbin/lpc",
+ "/usr/sbin/lpc status | grep '^...*:' | sed 's/:.*//'",
+ "-#" },
+};
+
+static unsigned print_system_count = sizeof(known_printing_systems) / sizeof(print_system_t);
+
+static const print_system_t *global_printing_system = NULL;
+
+static void
+initialize_default_parameters(void)
+{
+ default_parameters = stp_string_list_create();
+ stp_string_list_add_string(default_parameters, "PrintingSystem", "Autodetect");
+ stp_string_list_add_string(default_parameters, "PrintCommand", "");
+ stp_string_list_add_string(default_parameters, "QueueSelect", "");
+ stp_string_list_add_string(default_parameters, "RawOutputFlag", "");
+ stp_string_list_add_string(default_parameters, "ScanOnStartup", "False");
+ stp_string_list_add_string(default_parameters, "ScanPrintersCommand", "");
+}
+
+void
+stpui_set_global_parameter(const char *param, const char *value)
+{
+ stp_string_list_remove_string(default_parameters, param);
+ stp_string_list_add_string(default_parameters, param, value);
+}
+
+const char *
+stpui_get_global_parameter(const char *param)
+{
+ stp_param_string_t *ps = stp_string_list_find(default_parameters, param);
+ if (ps)
+ return ps->text;
+ else
+ return NULL;
+}
+
+static const print_system_t *
+identify_print_system(void)
+{
+ int i;
+ if (!global_printing_system)
+ {
+ for (i = 0; i < print_system_count; i++)
+ {
+ if (!access(known_printing_systems[i].key_file, R_OK))
+ {
+ global_printing_system = &(known_printing_systems[i]);
+ break;
+ }
+ }
+ if (!global_printing_system)
+ global_printing_system = &default_printing_system;
+ }
+ return global_printing_system;
+}
+
+char *
+stpui_build_standard_print_command(const stpui_plist_t *plist,
+ const stp_printer_t *printer)
+{
+ const char *queue_name = stpui_plist_get_queue_name(plist);
+ const char *extra_options = stpui_plist_get_extra_printer_options(plist);
+ const char *family = stp_printer_get_family(printer);
+ int copy_count = stpui_plist_get_copy_count(plist);
+ int raw = 0;
+ char *print_cmd;
+ char *count_string = NULL;
+ if (!queue_name)
+ queue_name = "";
+ identify_print_system();
+ if (strcmp(family, "ps") == 0)
+ raw = 0;
+ else
+ raw = 1;
+
+ if (copy_count > 1)
+ stp_asprintf(&count_string, "%s %d ",
+ global_printing_system->copy_count_command, copy_count);
+
+ stp_asprintf(&print_cmd, "%s %s %s %s %s%s%s",
+ global_printing_system->print_command,
+ queue_name[0] ? global_printing_system->queue_select : "",
+ queue_name[0] ? g_shell_quote(queue_name) : "",
+ count_string ? count_string : "",
+ raw ? global_printing_system->raw_flag : "",
+ extra_options ? " " : "",
+ extra_options ? extra_options : "");
+ SAFE_FREE(count_string);
+ return print_cmd;
+}
+
+void
+stpui_set_printrc_file(const char *name)
+{
+ if (name && name == printrc_name)
+ return;
+ SAFE_FREE(printrc_name);
+ if (name)
+ printrc_name = g_strdup(name);
+ else
+ printrc_name = g_build_filename(g_get_home_dir(), ".gutenprintrc", NULL);
+}
+
+const char *
+stpui_get_printrc_file(void)
+{
+ if (!printrc_name)
+ stpui_set_printrc_file(NULL);
+ return printrc_name;
+}
+
+#define PLIST_ACCESSORS(name) \
+void \
+stpui_plist_set_##name(stpui_plist_t *p, const char *val) \
+{ \
+ if (p->name == val) \
+ return; \
+ SAFE_FREE(p->name); \
+ p->name = g_strdup(val); \
+} \
+ \
+void \
+stpui_plist_set_##name##_n(stpui_plist_t *p, const char *val, int n) \
+{ \
+ if (p->name == val) \
+ return; \
+ SAFE_FREE(p->name); \
+ p->name = g_strndup(val, n); \
+} \
+ \
+const char * \
+stpui_plist_get_##name(const stpui_plist_t *p) \
+{ \
+ return p->name; \
+}
+
+PLIST_ACCESSORS(output_filename)
+PLIST_ACCESSORS(name)
+PLIST_ACCESSORS(queue_name)
+PLIST_ACCESSORS(extra_printer_options)
+PLIST_ACCESSORS(custom_command)
+PLIST_ACCESSORS(current_standard_command)
+
+void
+stpui_plist_set_command_type(stpui_plist_t *p, command_t val)
+{
+ switch (val)
+ {
+ case COMMAND_TYPE_DEFAULT:
+ case COMMAND_TYPE_CUSTOM:
+ case COMMAND_TYPE_FILE:
+ p->command_type = val;
+ break;
+ default:
+ p->command_type = COMMAND_TYPE_DEFAULT;
+ }
+}
+
+command_t
+stpui_plist_get_command_type(const stpui_plist_t *p)
+{
+ return p->command_type;
+}
+
+void
+stpui_plist_set_copy_count(stpui_plist_t *p, gint copy_count)
+{
+ if (copy_count > 0)
+ stp_set_int_parameter(p->v, copy_count_name, copy_count);
+}
+
+gint
+stpui_plist_get_copy_count(const stpui_plist_t *p)
+{
+ if (stp_check_int_parameter(p->v, copy_count_name, STP_PARAMETER_ACTIVE))
+ return stp_get_int_parameter(p->v, copy_count_name);
+ else
+ return 1;
+}
+
+void
+stpui_set_image_type(const char *itype)
+{
+ image_type = g_strdup(itype);
+}
+
+void
+stpui_set_image_raw_channels(gint channels)
+{
+ image_raw_channels = channels;
+}
+
+void
+stpui_set_image_channel_depth(gint depth)
+{
+ image_channel_depth = depth;
+}
+
+void
+stpui_printer_initialize(stpui_plist_t *printer)
+{
+ char tmp[32];
+ stpui_plist_set_name(printer, "");
+ stpui_plist_set_output_filename(printer, "");
+ stpui_plist_set_queue_name(printer, "");
+ stpui_plist_set_extra_printer_options(printer, "");
+ stpui_plist_set_custom_command(printer, "");
+ stpui_plist_set_current_standard_command(printer, "");
+ printer->command_type = COMMAND_TYPE_DEFAULT;
+ printer->scaling = 100.0;
+ printer->orientation = ORIENT_AUTO;
+ printer->auto_size_roll_feed_paper = 0;
+ printer->unit = 0;
+ printer->v = stp_vars_create();
+ stpui_plist_set_copy_count(printer, 1);
+ stp_set_string_parameter(printer->v, "InputImageType", image_type);
+ if (image_raw_channels)
+ {
+ (void) sprintf(tmp, "%d", image_raw_channels);
+ stp_set_string_parameter(printer->v, "RawChannels", tmp);
+ }
+ if (image_channel_depth)
+ {
+ (void) sprintf(tmp, "%d", image_channel_depth);
+ stp_set_string_parameter(printer->v, "ChannelBitDepth", tmp);
+ }
+ printer->invalid_mask = INVALID_TOP | INVALID_LEFT;
+}
+
+static void
+stpui_plist_destroy(stpui_plist_t *printer)
+{
+ SAFE_FREE(printer->name);
+ SAFE_FREE(printer->queue_name);
+ SAFE_FREE(printer->extra_printer_options);
+ SAFE_FREE(printer->custom_command);
+ SAFE_FREE(printer->current_standard_command);
+ SAFE_FREE(printer->output_filename);
+ stp_vars_destroy(printer->v);
+}
+
+void
+stpui_plist_copy(stpui_plist_t *vd, const stpui_plist_t *vs)
+{
+ if (vs == vd)
+ return;
+ stp_vars_copy(vd->v, vs->v);
+ vd->scaling = vs->scaling;
+ vd->orientation = vs->orientation;
+ vd->auto_size_roll_feed_paper = vs->auto_size_roll_feed_paper;
+ vd->unit = vs->unit;
+ vd->invalid_mask = vs->invalid_mask;
+ vd->command_type = vs->command_type;
+ stpui_plist_set_name(vd, stpui_plist_get_name(vs));
+ stpui_plist_set_queue_name(vd, stpui_plist_get_queue_name(vs));
+ stpui_plist_set_extra_printer_options(vd, stpui_plist_get_extra_printer_options(vs));
+ stpui_plist_set_custom_command(vd, stpui_plist_get_custom_command(vs));
+ stpui_plist_set_current_standard_command(vd, stpui_plist_get_current_standard_command(vs));
+ stpui_plist_set_output_filename(vd, stpui_plist_get_output_filename(vs));
+ stpui_plist_set_copy_count(vd, stpui_plist_get_copy_count(vs));
+}
+
+static stpui_plist_t *
+allocate_stpui_plist_copy(const stpui_plist_t *vs)
+{
+ stpui_plist_t *rep = g_malloc(sizeof(stpui_plist_t));
+ memset(rep, 0, sizeof(stpui_plist_t));
+ rep->v = stp_vars_create();
+ stpui_plist_copy(rep, vs);
+ return rep;
+}
+
+static void
+check_plist(int count)
+{
+ static int current_plist_size = 0;
+ int i;
+ if (count <= current_plist_size)
+ return;
+ else if (current_plist_size == 0)
+ {
+ current_plist_size = count;
+ stpui_plist = g_malloc(current_plist_size * sizeof(stpui_plist_t));
+ for (i = 0; i < current_plist_size; i++)
+ {
+ memset(&(stpui_plist[i]), 0, sizeof(stpui_plist_t));
+ stpui_printer_initialize(&(stpui_plist[i]));
+ }
+ }
+ else
+ {
+ int old_plist_size = current_plist_size;
+ current_plist_size *= 2;
+ if (current_plist_size < count)
+ current_plist_size = count;
+ stpui_plist = g_realloc(stpui_plist, current_plist_size * sizeof(stpui_plist_t));
+ for (i = old_plist_size; i < current_plist_size; i++)
+ {
+ memset(&(stpui_plist[i]), 0, sizeof(stpui_plist_t));
+ stpui_printer_initialize(&(stpui_plist[i]));
+ }
+ }
+}
+
+#define GET_MANDATORY_INTERNAL_STRING_PARAM(param) \
+do { \
+ if ((commaptr = strchr(lineptr, ',')) == NULL) \
+ continue; \
+ stpui_plist_set_##param##_n(&key, lineptr, commaptr - line); \
+ lineptr = commaptr + 1; \
+} while (0)
+
+#define GET_MANDATORY_STRING_PARAM(param) \
+do { \
+ if ((commaptr = strchr(lineptr, ',')) == NULL) \
+ continue; \
+ stp_set_##param##_n(key.v, lineptr, commaptr - line); \
+ lineptr = commaptr + 1; \
+} while (0)
+
+static int
+get_mandatory_string_param(stp_vars_t *v, const char *param, char **lineptr)
+{
+ char *commaptr = strchr(*lineptr, ',');
+ if (commaptr == NULL)
+ return 0;
+ stp_set_string_parameter_n(v, param, *lineptr, commaptr - *lineptr);
+ *lineptr = commaptr + 1;
+ return 1;
+}
+
+static int
+get_mandatory_file_param(stp_vars_t *v, const char *param, char **lineptr)
+{
+ char *commaptr = strchr(*lineptr, ',');
+ if (commaptr == NULL)
+ return 0;
+ stp_set_file_parameter_n(v, param, *lineptr, commaptr - *lineptr);
+ *lineptr = commaptr + 1;
+ return 1;
+}
+
+#define GET_MANDATORY_INT_PARAM(param) \
+do { \
+ if ((commaptr = strchr(lineptr, ',')) == NULL) \
+ continue; \
+ stp_set_##param(key.v, atoi(lineptr)); \
+ lineptr = commaptr + 1; \
+} while (0)
+
+#define GET_MANDATORY_INTERNAL_INT_PARAM(param) \
+do { \
+ if ((commaptr = strchr(lineptr, ',')) == NULL) \
+ continue; \
+ key.param = atoi(lineptr); \
+ lineptr = commaptr + 1; \
+} while (0)
+
+static void
+get_optional_string_param(stp_vars_t *v, const char *param,
+ char **lineptr, int *keepgoing)
+{
+ if (*keepgoing)
+ {
+ char *commaptr = strchr(*lineptr, ',');
+ if (commaptr == NULL)
+ {
+ stp_set_string_parameter(v, param, *lineptr);
+ *keepgoing = 0;
+ }
+ else
+ {
+ stp_set_string_parameter_n(v, param, *lineptr, commaptr - *lineptr);
+ *lineptr = commaptr + 1;
+ }
+ }
+}
+
+#define GET_OPTIONAL_INT_PARAM(param) \
+do { \
+ if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL)) \
+ { \
+ keepgoing = 0; \
+ } \
+ else \
+ { \
+ stp_set_##param(key.v, atoi(lineptr)); \
+ lineptr = commaptr + 1; \
+ } \
+} while (0)
+
+#define GET_OPTIONAL_INTERNAL_INT_PARAM(param) \
+do { \
+ if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL)) \
+ { \
+ keepgoing = 0; \
+ } \
+ else \
+ { \
+ key.param = atoi(lineptr); \
+ lineptr = commaptr + 1; \
+ } \
+} while (0)
+
+#define IGNORE_OPTIONAL_PARAM(param) \
+do { \
+ if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL)) \
+ { \
+ keepgoing = 0; \
+ } \
+ else \
+ { \
+ lineptr = commaptr + 1; \
+ } \
+} while (0)
+
+static void
+get_optional_float_param(stp_vars_t *v, const char *param,
+ char **lineptr, int *keepgoing)
+{
+ if (*keepgoing)
+ {
+ char *commaptr = strchr(*lineptr, ',');
+ if (commaptr == NULL)
+ {
+ stp_set_float_parameter(v, param, atof(*lineptr));
+ *keepgoing = 0;
+ }
+ else
+ {
+ stp_set_float_parameter(v, param, atof(*lineptr));
+ *lineptr = commaptr + 1;
+ }
+ }
+}
+
+#define GET_OPTIONAL_INTERNAL_FLOAT_PARAM(param) \
+do { \
+ if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL)) \
+ { \
+ keepgoing = 0; \
+ } \
+ else \
+ { \
+ key.param = atof(lineptr); \
+ } \
+} while (0)
+
+static void *
+psearch(const void *key, void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *))
+{
+ int i;
+ char *cbase = (char *) base;
+ for (i = 0; i < nmemb; i++)
+ {
+ if ((*compar)(key, (const void *) cbase) == 0)
+ return (void *) cbase;
+ cbase += size;
+ }
+ return NULL;
+}
+
+stpui_plist_t *
+stpui_plist_create(const char *name, const char *driver)
+{
+ stpui_plist_t key;
+ stpui_plist_t *answer = NULL;
+ memset(&key, 0, sizeof(key));
+ stpui_printer_initialize(&key);
+ key.invalid_mask = 0;
+ stpui_plist_set_name(&key, name);
+ stp_set_driver(key.v, driver);
+ if (stpui_plist_add(&key, 0))
+ answer = psearch(&key, stpui_plist, stpui_plist_count,
+ sizeof(stpui_plist_t),
+ (int (*)(const void *, const void *)) compare_printers);
+ SAFE_FREE(key.name);
+ SAFE_FREE(key.queue_name);
+ SAFE_FREE(key.extra_printer_options);
+ SAFE_FREE(key.custom_command);
+ SAFE_FREE(key.current_standard_command);
+ SAFE_FREE(key.output_filename);
+ stp_vars_destroy(key.v);
+ return answer;
+}
+
+int
+stpui_plist_add(const stpui_plist_t *key, int add_only)
+{
+ /*
+ * The format of the list is the File printer followed by a qsort'ed list
+ * of system printers. So, if we want to update the file printer, it is
+ * always first in the list, else call psearch.
+ */
+ stpui_plist_t *p;
+ if (stp_get_printer(key->v))
+ {
+ p = psearch(key, stpui_plist, stpui_plist_count,
+ sizeof(stpui_plist_t),
+ (int (*)(const void *, const void *)) compare_printers);
+ if (p == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Adding new printer from printrc file: %s\n",
+ key->name);
+#endif
+ check_plist(stpui_plist_count + 1);
+ p = stpui_plist + stpui_plist_count;
+ stpui_plist_count++;
+ stpui_plist_copy(p, key);
+ if (strlen(stpui_plist_get_queue_name(p)) == 0 &&
+ stp_string_list_is_present(stpui_system_print_queues,
+ stpui_plist_get_name(p)))
+ stpui_plist_set_queue_name(p, stpui_plist_get_name(p));
+ }
+ else
+ {
+ if (add_only)
+ return 0;
+#ifdef DEBUG
+ fprintf(stderr, "Updating printer %s.\n", key->name);
+#endif
+ stpui_plist_copy(p, key);
+ }
+ }
+ return 1;
+}
+
+static void
+stpui_printrc_load_v0(FILE *fp)
+{
+ char line[1024]; /* Line in printrc file */
+ char *lineptr; /* Pointer in line */
+ char *commaptr; /* Pointer to next comma */
+ stpui_plist_t key; /* Search key */
+ int keepgoing = 1;
+ (void) memset(line, 0, 1024);
+ (void) memset(&key, 0, sizeof(stpui_plist_t));
+ stpui_printer_initialize(&key);
+ key.name = g_strdup(_("File"));
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Read old format printrc lines...
+ */
+
+ stpui_printer_initialize(&key);
+ key.invalid_mask = 0;
+ lineptr = line;
+
+ /*
+ * Read the command-delimited printer definition data. Note that
+ * we can't use sscanf because %[^,] fails if the string is empty...
+ */
+
+ GET_MANDATORY_INTERNAL_STRING_PARAM(name);
+ GET_MANDATORY_INTERNAL_STRING_PARAM(custom_command);
+ GET_MANDATORY_STRING_PARAM(driver);
+
+ if (! stp_get_printer(key.v))
+ continue;
+
+ if (!get_mandatory_file_param(key.v, "PPDFile", &lineptr))
+ continue;
+ if ((commaptr = strchr(lineptr, ',')) != NULL)
+ {
+ switch (atoi(lineptr))
+ {
+ case 1:
+ stp_set_string_parameter(key.v, "PrintingMode", "Color");
+ break;
+ case 0:
+ default:
+ stp_set_string_parameter(key.v, "PrintingMode", "BW");
+ break;
+ }
+ }
+ else
+ continue;
+
+ if (!get_mandatory_string_param(key.v, "Resolution", &lineptr))
+ continue;
+ if (!get_mandatory_string_param(key.v, "PageSize", &lineptr))
+ continue;
+ if (!get_mandatory_string_param(key.v, "MediaType", &lineptr))
+ continue;
+
+ get_optional_string_param(key.v, "InputSlot", &lineptr, &keepgoing);
+ get_optional_float_param(key.v, "Brightness", &lineptr, &keepgoing);
+
+ GET_OPTIONAL_INTERNAL_FLOAT_PARAM(scaling);
+ GET_OPTIONAL_INTERNAL_INT_PARAM(orientation);
+ GET_OPTIONAL_INT_PARAM(left);
+ GET_OPTIONAL_INT_PARAM(top);
+ get_optional_float_param(key.v, "Gamma", &lineptr, &keepgoing);
+ get_optional_float_param(key.v, "Contrast", &lineptr, &keepgoing);
+ get_optional_float_param(key.v, "Cyan", &lineptr, &keepgoing);
+ get_optional_float_param(key.v, "Magenta", &lineptr, &keepgoing);
+ get_optional_float_param(key.v, "Yellow", &lineptr, &keepgoing);
+ IGNORE_OPTIONAL_PARAM(linear);
+ IGNORE_OPTIONAL_PARAM(image_type);
+ get_optional_float_param(key.v, "Saturation", &lineptr, &keepgoing);
+ get_optional_float_param(key.v, "Density", &lineptr, &keepgoing);
+ get_optional_string_param(key.v, "InkType", &lineptr, &keepgoing);
+ get_optional_string_param(key.v,"DitherAlgorithm",&lineptr,&keepgoing);
+ GET_OPTIONAL_INTERNAL_INT_PARAM(unit);
+ stpui_plist_add(&key, 0);
+ g_free(key.name);
+ stp_vars_destroy(key.v);
+ }
+ stpui_plist_current = 0;
+}
+
+static void
+stpui_printrc_load_v1(FILE *fp)
+{
+ char line[1024]; /* Line in printrc file */
+ stpui_plist_t key; /* Search key */
+ char * current_printer = 0; /* printer to select */
+ (void) memset(line, 0, 1024);
+ (void) memset(&key, 0, sizeof(stpui_plist_t));
+ stpui_printer_initialize(&key);
+ key.name = g_strdup(_("File"));
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Read new format printrc lines...
+ */
+
+ char *keyword, *end, *value;
+
+ keyword = line;
+ for (keyword = line; g_ascii_isspace(*keyword); keyword++)
+ {
+ /* skip initial spaces... */
+ }
+ if (!g_ascii_isalpha(*keyword))
+ continue;
+ for (end = keyword; g_ascii_isalnum(*end) || *end == '-'; end++)
+ {
+ /* find end of keyword... */
+ }
+ value = end;
+ while (g_ascii_isspace(*value))
+ {
+ /* skip over white space... */
+ value++;
+ }
+ if (*value != ':')
+ continue;
+ value++;
+ *end = '\0';
+ while (g_ascii_isspace(*value))
+ {
+ /* skip over white space... */
+ value++;
+ }
+ for (end = value; *end && *end != '\n'; end++)
+ {
+ /* find end of line... */
+ }
+ *end = '\0';
+#ifdef DEBUG
+ fprintf(stderr, "Keyword = `%s', value = `%s'\n", keyword, value);
+#endif
+ if (strcasecmp("current-printer", keyword) == 0)
+ {
+ if (current_printer)
+ g_free (current_printer);
+ current_printer = g_strdup(value);
+ }
+ else if (strcasecmp("printer", keyword) == 0)
+ {
+ /* Switch to printer named VALUE */
+ stpui_plist_add(&key, 0);
+#ifdef DEBUG
+ fprintf(stderr,
+ "output_to is now %s\n", stpui_plist_get_output_to(&key));
+#endif
+
+ stp_vars_destroy(key.v);
+ stpui_printer_initialize(&key);
+ key.invalid_mask = 0;
+ stpui_plist_set_name(&key, value);
+ }
+ else if (strcasecmp("destination", keyword) == 0)
+ stpui_plist_set_custom_command(&key, value);
+ else if (strcasecmp("driver", keyword) == 0)
+ stp_set_driver(key.v, value);
+ else if (strcasecmp("ppd-file", keyword) == 0)
+ stp_set_file_parameter(key.v, "PPDFile", value);
+ else if (strcasecmp("output-type", keyword) == 0)
+ {
+ switch (atoi(value))
+ {
+ case 1:
+ stp_set_string_parameter(key.v, "PrintingMode", "Color");
+ break;
+ case 0:
+ default:
+ stp_set_string_parameter(key.v, "PrintingMode", "BW");
+ break;
+ }
+ }
+ else if (strcasecmp("media-size", keyword) == 0)
+ stp_set_string_parameter(key.v, "PageSize", value);
+ else if (strcasecmp("media-type", keyword) == 0)
+ stp_set_string_parameter(key.v, "MediaType", value);
+ else if (strcasecmp("media-source", keyword) == 0)
+ stp_set_float_parameter(key.v, "Brightness", atof(value));
+ else if (strcasecmp("scaling", keyword) == 0)
+ key.scaling = atof(value);
+ else if (strcasecmp("orientation", keyword) == 0)
+ key.orientation = atoi(value);
+ else if (strcasecmp("left", keyword) == 0)
+ stp_set_left(key.v, atoi(value));
+ else if (strcasecmp("top", keyword) == 0)
+ stp_set_top(key.v, atoi(value));
+ else if (strcasecmp("linear", keyword) == 0)
+ /* Ignore linear */
+ ;
+ else if (strcasecmp("image-type", keyword) == 0)
+ /* Ignore image type */
+ ;
+ else if (strcasecmp("unit", keyword) == 0)
+ key.unit = atoi(value);
+ else if (strcasecmp("custom-page-width", keyword) == 0)
+ stp_set_page_width(key.v, atoi(value));
+ else if (strcasecmp("custom-page-height", keyword) == 0)
+ stp_set_page_height(key.v, atoi(value));
+ /* Special case Ink-Type and Dither-Algorithm */
+ else if (strcasecmp("ink-type", keyword) == 0)
+ stp_set_string_parameter(key.v, "InkType", value);
+ else if (strcasecmp("dither-algorithm", keyword) == 0)
+ stp_set_string_parameter(key.v, "DitherAlgorithm", value);
+ else
+ {
+ stp_parameter_t desc;
+ stp_curve_t *curve;
+ stp_describe_parameter(key.v, keyword, &desc);
+ switch (desc.p_type)
+ {
+ case STP_PARAMETER_TYPE_STRING_LIST:
+ stp_set_string_parameter(key.v, keyword, value);
+ break;
+ case STP_PARAMETER_TYPE_FILE:
+ stp_set_file_parameter(key.v, keyword, value);
+ break;
+ case STP_PARAMETER_TYPE_DOUBLE:
+ stp_set_float_parameter(key.v, keyword, atof(value));
+ break;
+ case STP_PARAMETER_TYPE_INT:
+ stp_set_int_parameter(key.v, keyword, atoi(value));
+ break;
+ case STP_PARAMETER_TYPE_BOOLEAN:
+ stp_set_boolean_parameter(key.v, keyword, atoi(value));
+ break;
+ case STP_PARAMETER_TYPE_CURVE:
+ curve = stp_curve_create_from_string(value);
+ if (curve)
+ {
+ stp_set_curve_parameter(key.v, keyword, curve);
+ stp_curve_destroy(curve);
+ }
+ break;
+ default:
+ if (strlen(value))
+ {
+ char buf[1024];
+ snprintf(buf, sizeof(buf),
+ "Unrecognized keyword `%s' in printrc; value `%s' (%d)\n",
+ keyword, value, desc.p_type);
+ }
+ }
+ stp_parameter_description_destroy(&desc);
+ }
+ }
+ if (strlen(key.name) > 0)
+ {
+ stpui_plist_add(&key, 0);
+ stp_vars_destroy(key.v);
+ g_free(key.name);
+ }
+ if (current_printer)
+ {
+ int i;
+ for (i = 0; i < stpui_plist_count; i ++)
+ if (strcmp(current_printer, stpui_plist[i].name) == 0)
+ stpui_plist_current = i;
+ }
+}
+
+char *stpui_printrc_current_printer = NULL;
+extern FILE *yyin;
+extern int yyparse(void);
+
+static void
+stpui_printrc_load_v2(FILE *fp)
+{
+ int retval;
+ yyin = fp;
+
+ stpui_printrc_current_printer = NULL;
+ retval = yyparse();
+ if (stpui_printrc_current_printer)
+ {
+ int i;
+ for (i = 0; i < stpui_plist_count; i ++)
+ {
+ if (strcmp(stpui_printrc_current_printer, stpui_plist[i].name) == 0)
+ stpui_plist_current = i;
+ if (!stp_check_boolean_parameter(stpui_plist[i].v,
+ "PageSizeExtended",
+ STP_PARAMETER_ACTIVE))
+ stp_set_boolean_parameter(stpui_plist[i].v, "PageSizeExtended", 0);
+ }
+ SAFE_FREE(stpui_printrc_current_printer);
+ }
+}
+
+/*
+ * 'stpui_printrc_load()' - Load the printer resource configuration file.
+ */
+void
+stpui_printrc_load(void)
+{
+ FILE *fp; /* Printrc file */
+ char line[1024]; /* Line in printrc file */
+ int format = 0; /* rc file format version */
+ const char *filename = stpui_get_printrc_file();
+
+ initialize_default_parameters();
+ check_plist(1);
+
+ /*
+ * Get the printer list...
+ */
+
+ stpui_get_system_printers();
+
+ if ((fp = fopen(filename, "r")) != NULL)
+ {
+ (void) memset(line, 0, 1024);
+ if (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /* Force locale to "C", so that numbers scan correctly */
+ setlocale(LC_ALL, "C");
+ if (strncmp("#PRINTRCv", line, 9) == 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Found printrc version tag: `%s'\n", line);
+ fprintf(stderr, "Version number: `%s'\n", &(line[9]));
+#endif
+ (void) sscanf(&(line[9]), "%d", &format);
+ }
+ setlocale(LC_ALL, "");
+ }
+ rewind(fp);
+ switch (format)
+ {
+ case 0:
+ stpui_printrc_load_v0(fp);
+ break;
+ case 1:
+ stpui_printrc_load_v1(fp);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ stpui_printrc_load_v2(fp);
+ break;
+ }
+ (void) fclose(fp);
+ }
+ if (stpui_plist_count == 0)
+ stpui_plist_create(_("Printer"), "ps2");
+}
+
+/*
+ * 'stpui_printrc_save()' - Save the current printer resource configuration.
+ */
+void
+stpui_printrc_save(void)
+{
+ FILE *fp; /* Printrc file */
+ int i; /* Looping var */
+ size_t global_settings_count = stp_string_list_count(default_parameters);
+ stpui_plist_t *p; /* Current printer */
+ const char *filename = stpui_get_printrc_file();
+
+
+ if ((fp = fopen(filename, "w")) != NULL)
+ {
+ /*
+ * Write the contents of the printer list...
+ */
+
+ /* Force locale to "C", so that numbers print correctly */
+ setlocale(LC_ALL, "C");
+#ifdef DEBUG
+ fprintf(stderr, "Number of printers: %d\n", stpui_plist_count);
+#endif
+
+ fputs("#PRINTRCv4 written by Gutenprint " PLUG_IN_VERSION "\n\n", fp);
+
+ fprintf(fp, "Global-Settings:\n");
+ fprintf(fp, " Current-Printer: \"%s\"\n",
+ stpui_plist[stpui_plist_current].name);
+ fprintf(fp, " Show-All-Paper-Sizes: %s\n",
+ stpui_show_all_paper_sizes ? "True" : "False");
+ for (i = 0; i < global_settings_count; i++)
+ {
+ stp_param_string_t *ps = stp_string_list_param(default_parameters, i);
+ fprintf(fp, " %s \"%s\"\n", ps->name, ps->text);
+ }
+ fprintf(fp, "End-Global-Settings:\n");
+
+ for (i = 0, p = stpui_plist; i < stpui_plist_count; i ++, p ++)
+ {
+ int count;
+ int j;
+ stp_parameter_list_t *params = stp_get_parameter_list(p->v);
+ count = stp_parameter_list_count(params);
+ fprintf(fp, "\nPrinter: \"%s\" \"%s\"\n",
+ p->name, stp_get_driver(p->v));
+ fprintf(fp, " Command-Type: %d\n", p->command_type);
+ fprintf(fp, " Queue-Name: \"%s\"\n", p->queue_name);
+ fprintf(fp, " Output-Filename: \"%s\"\n", p->output_filename);
+ fprintf(fp, " Extra-Printer-Options: \"%s\"\n", p->extra_printer_options);
+ fprintf(fp, " Custom-Command: \"%s\"\n", p->custom_command);
+ fprintf(fp, " Scaling: %.3f\n", p->scaling);
+ fprintf(fp, " Orientation: %d\n", p->orientation);
+ fprintf(fp, " Autosize-Roll-Paper: %d\n", p->auto_size_roll_feed_paper);
+ fprintf(fp, " Unit: %d\n", p->unit);
+
+ fprintf(fp, " Left: %d\n", stp_get_left(p->v));
+ fprintf(fp, " Top: %d\n", stp_get_top(p->v));
+ fprintf(fp, " Custom_Page_Width: %d\n", stp_get_page_width(p->v));
+ fprintf(fp, " Custom_Page_Height: %d\n", stp_get_page_height(p->v));
+ fprintf(fp, " Parameter %s Int True %d\n", copy_count_name,
+ stpui_plist_get_copy_count(p));
+
+ for (j = 0; j < count; j++)
+ {
+ const stp_parameter_t *param = stp_parameter_list_param(params, j);
+ if (strcmp(param->name, "AppGamma") == 0)
+ continue;
+ switch (param->p_type)
+ {
+ case STP_PARAMETER_TYPE_STRING_LIST:
+ if (stp_check_string_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ fprintf(fp, " Parameter %s String %s \"%s\"\n",
+ param->name,
+ ((stp_get_string_parameter_active
+ (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
+ "True" : "False"),
+ stp_get_string_parameter(p->v, param->name));
+ break;
+ case STP_PARAMETER_TYPE_FILE:
+ if (stp_check_file_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ fprintf(fp, " Parameter %s File %s \"%s\"\n", param->name,
+ ((stp_get_file_parameter_active
+ (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
+ "True" : "False"),
+ stp_get_file_parameter(p->v, param->name));
+ break;
+ case STP_PARAMETER_TYPE_DOUBLE:
+ if (stp_check_float_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ fprintf(fp, " Parameter %s Double %s %f\n", param->name,
+ ((stp_get_float_parameter_active
+ (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
+ "True" : "False"),
+ stp_get_float_parameter(p->v, param->name));
+ break;
+ case STP_PARAMETER_TYPE_DIMENSION:
+ if (stp_check_dimension_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ fprintf(fp, " Parameter %s Dimension %s %d\n", param->name,
+ ((stp_get_dimension_parameter_active
+ (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
+ "True" : "False"),
+ stp_get_dimension_parameter(p->v, param->name));
+ break;
+ case STP_PARAMETER_TYPE_INT:
+ if (stp_check_int_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ fprintf(fp, " Parameter %s Int %s %d\n", param->name,
+ ((stp_get_int_parameter_active
+ (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
+ "True" : "False"),
+ stp_get_int_parameter(p->v, param->name));
+ break;
+ case STP_PARAMETER_TYPE_BOOLEAN:
+ if (stp_check_boolean_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ fprintf(fp, " Parameter %s Boolean %s %s\n", param->name,
+ ((stp_get_boolean_parameter_active
+ (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
+ "True" : "False"),
+ (stp_get_boolean_parameter(p->v, param->name) ?
+ "True" : "False"));
+ break;
+ case STP_PARAMETER_TYPE_CURVE:
+ if (stp_check_curve_parameter(p->v, param->name,
+ STP_PARAMETER_INACTIVE))
+ {
+ const stp_curve_t *curve =
+ stp_get_curve_parameter(p->v, param->name);
+ if (curve)
+ {
+ fprintf(fp, " Parameter %s Curve %s '",
+ param->name,
+ ((stp_get_curve_parameter_active
+ (p->v, param->name) ==
+ STP_PARAMETER_ACTIVE) ?
+ "True" : "False"));
+ stp_curve_write(fp, curve);
+ fprintf(fp, "'\n");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ stp_parameter_list_destroy(params);
+#ifdef DEBUG
+ fprintf(stderr, "Wrote printer %d: %s\n", i, p->name);
+#endif
+ }
+ setlocale(LC_ALL, "");
+ fclose(fp);
+ }
+ else
+ fprintf(stderr, "could not open printrc file \"%s\"\n",filename);
+}
+
+/*
+ * 'compare_printers()' - Compare system printer names for qsort().
+ */
+
+static int
+compare_printers(stpui_plist_t *p1, stpui_plist_t *p2)
+{
+ return (strcmp(p1->name, p2->name));
+}
+
+/*
+ * 'stpui_get_system_printers()' - Get a complete list of printers from the spooler.
+ */
+
+void
+stpui_get_system_printers(void)
+{
+ FILE *pfile; /* Pipe to status command */
+ char line[1025]; /* Line from status command */
+
+ stpui_system_print_queues = stp_string_list_create();
+ stp_string_list_add_string(stpui_system_print_queues, "",
+ _("(Default Printer)"));
+
+ /*
+ * Run the command, if any, to get the available printers...
+ */
+
+ identify_print_system();
+ if (global_printing_system)
+ {
+ if ((pfile = popen(global_printing_system->scan_command, "r")) != NULL)
+ {
+ /*
+ * Read input as needed...
+ */
+
+ while (fgets(line, sizeof(line), pfile) != NULL)
+ {
+ char *tmp_ptr;
+ if ((tmp_ptr = strchr(line, '\n')))
+ tmp_ptr[0] = '\0';
+ if ((tmp_ptr = strchr(line, '\r')))
+ tmp_ptr[0] = '\0';
+ if (strlen(line) > 0)
+ {
+ if (!stp_string_list_is_present(stpui_system_print_queues, line))
+ stp_string_list_add_string(stpui_system_print_queues,
+ line, line);
+ }
+ }
+ pclose(pfile);
+ }
+ }
+}
+
+const stpui_plist_t *
+stpui_get_current_printer(void)
+{
+ return &(stpui_plist[stpui_plist_current]);
+}
+
+/*
+ * 'usr1_handler()' - Make a note when we receive SIGUSR1.
+ */
+
+static volatile int usr1_interrupt;
+
+static void
+usr1_handler (int sig)
+{
+ usr1_interrupt = 1;
+}
+
+static void
+writefunc(void *file, const char *buf, size_t bytes)
+{
+ FILE *prn = (FILE *)file;
+ fwrite(buf, 1, bytes, prn);
+}
+
+static void
+stpui_errfunc(void *file, const char *buf, size_t bytes)
+{
+ g_message(buf);
+}
+
+/*
+ *
+ * Process control for actually printing. Documented 20040821
+ * by Robert Krawitz.
+ *
+ * In addition to the print command itself and the output generator,
+ * we spawn two additional processes to monitor the print job and clean
+ * up. We do this because the GIMP is very unfriendly about how it
+ * terminates plugins when the user cancels an operation: it sends a
+ * SIGKILL, which prevents the plugin from cleaning up. Since the
+ * plugin is sending data to an lpr process, this SIGKILL closes off
+ * the input to lpr. lpr doesn't know that its parent has died
+ * inappropriately, and happily proceeds to print the partial job.
+ *
+ * (The child may not actually be lpr, of course, but we'll just use
+ * that nomenclature for convenience.)
+ *
+ * The first such process is the "lpr monitor". Its job is to
+ * watch the parent (the actual data generator). If its parent dies,
+ * it kills the print command. Notice that it must keep the file
+ * descriptor used to write to the lpr process open, since as soon as
+ * the last writer to this pipe is closed, the lpr process sees its
+ * input close. Therefore, it first kills the child with SIGTERM and
+ * then closes the pipe. Perhaps a more robust method would be to
+ * send a SIGTERM followed by a SIGKILL, but we can worry about that
+ * later. The lpr monitor process is killed with SIGUSR1 by the
+ * master upon successful completion, at which point it exits. The lpr
+ * monitor itself detects that the master has finished by periodically
+ * sending it a kill 0 (a null signal). When the parent exits, this
+ * attempt to signal will return failure. This has a potential race
+ * condition if another process is created with the same PID between
+ * checks. A more robust (but more complicated) solution would involve
+ * IPC of some kind.
+ *
+ * The second such process (the "error monitor") monitors the stderr
+ * (and stdout) of the lpr process, to send any error messages back
+ * to the user. Since the GIMP is normally not launched from a
+ * terminal window, any errors would get lost. The error monitor
+ * captures this output and reports it back. It stays around until
+ * its input is closed (normally by the lpr process exiting), at
+ * which point it reports to the master that it has finished, and the
+ * master can clean up and return.
+ *
+ * The actual master process spawns the lpr monitor, which spawns
+ * the process that will later run the lpr command, which itself
+ * spawns the error monitor.
+ *
+ * This architecture is perhaps unnecessarily complex; the lpr monitor
+ * and error monitor could perhaps be combined into a single process
+ * that watches both for the parent to go away and for the error messages.
+ *
+ * The following diagrams illustrate the control flow during the normal
+ * case and also when the print job is cancelled. The notation for file
+ * descriptors is a number prefixed with < for an input file descriptor
+ * and suffixed > for an output file descriptor. For example, <0 means
+ * input file descriptor 0 and 1> means output file descriptor 1. The
+ * key to the file descriptors is given below. A file descriptor named
+ * x,y refers to file descriptor y duplicated onto file descriptor x.
+ * So "<0,3" means input file descriptor 3 (pipefd[0]) dup2'ed onto
+ * file descriptor 0.
+ *
+ * fd0 = fd 0
+ * fd1 = fd 1
+ * fd2 = fd 2
+ * fd3 = pipefd[0]
+ * fd4 = pipefd[1]
+ * fd5 = syncfd[0]
+ * fd6 = syncfd[1]
+ * fd7 = errfd[0]
+ * fd8 = errfd[1]
+ *
+ *
+ * NORMAL CASE
+ *
+ * PARENT CHILD 1 CHILD 2 CHILD 3
+ * (print generator) (lpr monitor) (print command) (error monitor)
+ * |
+ * stpui_print
+ * |
+ * | <0 1> 2>
+ * |
+ * | pipe(syncfd)
+ * |
+ * | <0 1> 2> <5 6>
+ * |
+ * | pipe(pipefd)
+ * |
+ * | <0 1> 2> <3 4>
+ * | <5 6>
+ * |
+ * | fork =============|
+ * | |
+ * | close(63) | close(syncfd[0])
+ * | | <0 1> 2> <3 4>
+ * | <0 1> 2> 4> <5 | 6>
+ * | |
+ * | | fork =============|
+ * | | |
+ * | | close(01263) | dup2(pipefd[0], 0)
+ * | | | close(pipefd[0]
+ * | | 4> | close(pipefd[1]
+ * | | |
+ * | | | 1> 2> <0,3 6>
+ * | | |
+ * | | | pipe(errfd)
+ * | | | 1> 2> <0,3 6>
+ * | | | <7 8>
+ * | | |
+ * | | | fork =============|
+ * | | | | close(012348)
+ * | | | close(12) | 6> <7
+ * | | | |
+ * | | | <0,3 6> <7 8> |
+ * | | | |
+ * | | | dup2(errfd[1],1) |
+ * | | | dup2(errfd[1],2) |
+ * | | | close(errfd[1]) |
+ * | | | close(pipefd[0]) |
+ * | | | close(pipefd[1]) |
+ * | | | close(syncfd[1]) |
+ * |<<<<<<<<<<<<<<<<<<<|kill(0,0) * EXEC lpr |
+ * |>>>>>>>>>>>>>>>>>>>|OK | <0,3 1,8> 2,8> |
+ * | | | |
+ * | write>>>>>>>>>>>>>+>>>>>>>>>>>>>>>>>>>| |
+ * | | | write(2,8)??>>>>>>|read(<7)->warn
+ * | | | |
+ * | close(4)>>>>>>>>>>+>>>>>>>>>>>>>>>>>>>| |
+ * | <0 1> 2> <5 | | |
+ * | kill>>>>>>>>>>>>>>| | |
+ * | | close(4)>>>>>>>>>>| eof(<0,3) |
+ * | | | |
+ * | | *no open fd* | 1,8> 2,8> |
+ * | | exit | |
+ * | | | exit>>>>>>>>>>>>>>| eof(<7)
+ * | wait<<<<<<<<<<<<<<X X | 6>
+ * | |
+ * | read(<5)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<| write(6>)
+ * | |
+ * | close(5) | exit
+ * | X
+ * | 0< 1> 2>
+ * |
+ * | return
+ * X
+ *
+ *
+ * ERROR CASE (job cancelled)
+ *
+ * PARENT CHILD 1 CHILD 2 CHILD 3
+ * (print generator) (lpr monitor) (print command) (error monitor)
+ * |
+ * stpui_print
+ * |
+ * | <0 1> 2>
+ * |
+ * | pipe(syncfd)
+ * |
+ * | <0 1> 2> <5 6>
+ * |
+ * | pipe(pipefd)
+ * |
+ * | <0 1> 2> <3 4>
+ * | <5 6>
+ * |
+ * | fork =============|
+ * | |
+ * | close(63) | close(syncfd[0]
+ * | | <0 1> 2> <3 4>
+ * | <0 1> 2> 4> <5 | 6>
+ * | |
+ * | | fork =============|
+ * | | |
+ * | | close(01263) | dup2(pipefd[0], 0)
+ * | | | close(pipefd[0]
+ * | | 4> | close(pipefd[1]
+ * | | |
+ * | | | 1> 2> <3,0 6>
+ * | | |
+ * | | | pipe(errfd)
+ * | | | 1> 2> <3,0 6>
+ * | | | <7 8>
+ * | | |
+ * | | | fork =============|
+ * | | | | close(012348)
+ * | | | close(12) | 6> <7
+ * | | | |
+ * | | | <3,0 6> <7 8> |
+ * | | | |
+ * | | | dup2(errfd[1],1) |
+ * | | | dup2(errfd[1],2) |
+ * | | | close(3468) |
+ * |<<<<<<<<<<<<<<<<<<<|kill(0,0) * EXEC lpr |
+ * |>>>>>>>>>>>>>>>>>>>|OK | <0,3 1,8> 2,8> |
+ * | | | |
+ * | write>>>>>>>>>>>>>+>>>>>>>>>>>>>>>>>>>| |
+ * | | | write(2,8)??>>>>>>|read(<7)->warn
+ * | KILLED | | |
+ * | close(01245)>>>>>>+>>>>>>>>>>>>>>>>>>>| |
+ * X | | |
+ * <<<<<<<<<<<<<<<<<<<|kill(0,0) | |
+ * >>>>>>>>>>>>>>>>>>>|DEAD! | |
+ * |kill(2)>>>>>>>>>>>>|Terminated |
+ * |close(4>)>>>>>>>>>>|eof(0,3) |
+ * | | 1,8> 2,8> |
+ * | *no open fd* |exit>>>>>>>>>>>>>>>|eof(7)
+ * | exit X |
+ * X | 6>
+ * <| write(6)
+ * | exit/SIGPIPE
+ * X
+ */
+
+int
+stpui_print(const stpui_plist_t *printer, stpui_image_t *image)
+{
+ int ppid = getpid (), /* PID of plugin */
+ opid, /* PID of output process */
+ cpid = 0, /* PID of control/monitor process */
+ pipefd[2], /* Fds of the pipe connecting all the above */
+ errfd[2], /* Message logger from lp command */
+ syncfd[2]; /* Sync the logger */
+ FILE *prn = NULL; /* Print file/command */
+ int do_sync = 0;
+ int print_status = 0;
+ int dummy;
+
+ /*
+ * Open the file/execute the print command...
+ */
+
+ if (stpui_plist_get_command_type(printer) == COMMAND_TYPE_DEFAULT ||
+ stpui_plist_get_command_type(printer) == COMMAND_TYPE_CUSTOM)
+ {
+ /*
+ * The following IPC code is only necessary because the GIMP kills
+ * plugins with SIGKILL if its "Cancel" button is pressed; this
+ * gives the plugin no chance whatsoever to clean up after itself.
+ */
+ do_sync = 1;
+ usr1_interrupt = 0;
+ signal (SIGUSR1, usr1_handler);
+ if (pipe (syncfd) != 0)
+ {
+ do_sync = 0;
+ }
+ if (pipe (pipefd) != 0)
+ {
+ prn = NULL;
+ }
+ else
+ {
+ cpid = fork ();
+ if (cpid < 0) /* Error */
+ {
+ do_sync = 0;
+ prn = NULL;
+ }
+ else if (cpid == 0) /* Child 1 (lpr monitor and printer command) */
+ {
+ /* LPR monitor process. Printer output is piped to us. */
+ close(syncfd[0]);
+ opid = fork ();
+ if (opid < 0)
+ {
+ /* Errors will cause the plugin to get a SIGPIPE. */
+ exit (1);
+ }
+ else if (opid == 0) /* Child 2 (printer command) */
+ {
+ dup2 (pipefd[0], 0);
+ close (pipefd[0]);
+ close (pipefd[1]);
+ if (pipe(errfd) == 0)
+ {
+ opid = fork();
+ if (opid < 0)
+ _exit(1);
+ else if (opid == 0) /* Child 3 (monitors stderr) */
+ {
+ stp_outfunc_t errfunc = stpui_get_errfunc();
+ void *errdata = stpui_get_errdata();
+ /* calls g_message on anything it sees */
+ char buf[4096];
+
+ close (pipefd[0]);
+ close (pipefd[1]);
+ close (0);
+ close (1);
+ close (2);
+ close (errfd[1]);
+ while (1)
+ {
+ ssize_t bytes = read(errfd[0], buf, 4095);
+ if (bytes > 0)
+ {
+ buf[bytes] = '\0';
+ (*errfunc)(errdata, buf, bytes);
+ }
+ else
+ {
+ if (bytes < 0)
+ {
+ snprintf(buf, 4095,
+ "Read messages failed: %s\n",
+ strerror(errno));
+ (*errfunc)(errdata, buf, strlen(buf));
+ }
+ write(syncfd[1], "Done", 5);
+ _exit(0);
+ }
+ }
+ /* NOTREACHED */
+ write(syncfd[1], "Done", 5);
+ _exit(0);
+ }
+ else /* Child 2 (printer command) */
+ {
+ const char *command;
+ if (stpui_plist_get_command_type(printer) ==
+ COMMAND_TYPE_DEFAULT)
+ command =
+ stpui_build_standard_print_command
+ (printer, stp_get_printer(printer->v));
+ else
+ command = stpui_plist_get_custom_command(printer);
+ (void) close(2);
+ (void) close(1);
+ dup2 (errfd[1], 2);
+ dup2 (errfd[1], 1);
+ close(errfd[1]);
+ close (pipefd[0]);
+ close (pipefd[1]);
+ close(syncfd[1]);
+ execl("/bin/sh", "/bin/sh", "-c", command, NULL);
+ /* NOTREACHED */
+ _exit (1);
+ }
+ /* NOTREACHED */
+ _exit(1);
+ }
+ else /* pipe() failed! */
+ {
+ _exit(1);
+ }
+ }
+ else /* Child 1 (lpr monitor) */
+ {
+ /*
+ * If the print plugin gets SIGKILLed by gimp, we kill lpr
+ * in turn. If the plugin signals us with SIGUSR1 that it's
+ * finished printing normally, we close our end of the pipe,
+ * and go away.
+ *
+ * Note that we keep pipefd[1] -- which is the pipe from
+ * the print plugin to the lpr process -- open during this.
+ * If we don't, and the parent gets killed, lpr will notice
+ * its stdin getting closed off and start printing.
+ * This way its stdin stays open until we kill it.
+ */
+ close (0);
+ close (1);
+ close (2);
+ close (syncfd[1]);
+ close (pipefd[0]);
+ while (usr1_interrupt == 0)
+ {
+ /*
+ * Note potential race condition, if some other process
+ * happens to get the same pid!
+ */
+ if (kill (ppid, 0) < 0)
+ {
+ /*
+ * The print plugin has been killed!
+ * Note that there is no possibility of the print
+ * job sending us a SIGUSR1 and then exiting;
+ * the parent (print plugin) stays around after
+ * sending us the SIGUSR1, and then waits
+ * for us to die.
+ */
+ kill (opid, SIGTERM);
+ waitpid (opid, &dummy, 0);
+ close (pipefd[1]);
+ /*
+ * We do not want to allow cleanup before exiting.
+ * The exiting parent has already closed the
+ * connection to the X server; if we try to clean
+ * up, we'll notice that fact and complain.
+ */
+ _exit (0);
+ }
+ sleep (5);
+ }
+ /* We got SIGUSR1. */
+ close (pipefd[1]);
+ /*
+ * We do not want to allow cleanup before exiting.
+ * The exiting parent has already closed the connection
+ * to the X server; if we try to clean up, we'll notice
+ * that fact and complain.
+ */
+ _exit (0);
+ }
+ }
+ else /* Parent (actually generates the output) */
+ {
+ close (syncfd[1]);
+ close (pipefd[0]);
+ /* Parent process. We generate the printer output. */
+ prn = fdopen (pipefd[1], "w");
+ /* and fall through... */
+ }
+ }
+ }
+ else
+ prn = fopen (stpui_plist_get_output_filename(printer), "wb");
+
+ if (prn != NULL)
+ {
+ char tmp[32];
+ stpui_plist_t *np = allocate_stpui_plist_copy(printer);
+ const stp_vars_t *current_vars =
+ stp_printer_get_defaults(stp_get_printer(np->v));
+ int orientation;
+ stp_merge_printvars(np->v, current_vars);
+ stp_set_string_parameter(np->v, "InputImageType", image_type);
+ if (image_raw_channels)
+ {
+ sprintf(tmp, "%d", image_raw_channels);
+ stp_set_string_parameter(np->v, "RawChannels", tmp);
+ }
+ sprintf(tmp, "%d", image_channel_depth);
+ stp_set_string_parameter(np->v, "ChannelBitDepth", tmp);
+
+ /*
+ * Set up the orientation
+ */
+ orientation = np->orientation;
+ if (orientation == ORIENT_AUTO)
+ orientation = stpui_compute_orientation();
+ switch (orientation)
+ {
+ case ORIENT_PORTRAIT:
+ break;
+ case ORIENT_LANDSCAPE:
+ if (image->rotate_cw)
+ (image->rotate_cw)(image);
+ break;
+ case ORIENT_UPSIDEDOWN:
+ if (image->rotate_180)
+ (image->rotate_180)(image);
+ break;
+ case ORIENT_SEASCAPE:
+ if (image->rotate_ccw)
+ (image->rotate_ccw)(image);
+ break;
+ }
+
+ /*
+ * Finally, call the print driver to send the image to the printer
+ * and close the output file/command...
+ */
+
+ stp_set_outfunc(np->v, writefunc);
+ stp_set_errfunc(np->v, stpui_get_errfunc());
+ stp_set_outdata(np->v, prn);
+ stp_set_errdata(np->v, stpui_get_errdata());
+ print_status = stp_print(np->v, &(image->im));
+
+ /*
+ * Note that we do not use popen() to create the output, therefore
+ * we do not use pclose() to close it. See bug 1013565.
+ */
+ (void) fclose(prn);
+ if (stpui_plist_get_command_type(printer) == COMMAND_TYPE_DEFAULT ||
+ stpui_plist_get_command_type(printer) == COMMAND_TYPE_CUSTOM)
+ {
+ /*
+ * It is important for us to first close off the lpr process,
+ * then kill the lpr monitor (child 1), and then wait for it
+ * to die before exiting.
+ */
+ kill (cpid, SIGUSR1);
+ waitpid (cpid, &dummy, 0);
+ }
+
+ /*
+ * Make sure that any errors have been reported back to the user
+ * prior to completion. In addition, explicitly close off the
+ * synchronization file descriptor since we're merely returning,
+ * not exiting, and don't want to leave any pollution around.
+ */
+ if (do_sync)
+ {
+ char buf[8];
+ (void) read(syncfd[0], buf, 8);
+ (void) close(syncfd[0]);
+ }
+ stpui_plist_destroy(np);
+ g_free(np);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * End of "$Id: plist.c,v 1.5 2005/06/14 02:47:13 rlk Exp $".
+ */