/* * 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, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gutenprintui-internal.h" #include #include #include #include #include #include #include #include #include #include #include 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 * * CAUTION: Do not use lpstat -t or lpstat -p. * See bug 742187 (huge delays with lpstat -d -p) for an explanation. */ static const print_system_t default_printing_system = { "SysV", N_("System V lp"), "lp -s", "-d", "-oraw", "/usr/bin/lp", "/usr/bin/lpstat -v | awk '/^device for /i {sub(\":\", \"\", $3); print $3}'", "-n" }; static print_system_t known_printing_systems[] = { { "CUPS", N_("CUPS"), "lp -s", "-d", "-oraw", "/usr/sbin/cupsd", "/usr/bin/lpstat -v | awk '/^device for /i {sub(\":\", \"\", $3); print $3}'", "-n" }, { "SysV", N_("System V lp"), "lp -s", "-d", "-oraw", "/usr/bin/lp", "/usr/bin/lpstat -v | awk '/^device for /i {sub(\":\", \"\", $3); print $3}'", "-n" }, { "lpd", N_("Berkeley lpd (/etc/lpc)"), "lpr", "-P", "-l", "/etc/lpc", "/etc/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'", "-#" }, { "lpd", N_("Berkeley lpd (/usr/bsd/lpc)"), "lpr", "-P", "-l", "/usr/bsd/lpc", "/usr/bsd/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'", "-#" }, { "lpd", N_("Berkeley lpd (/usr/etc/lpc"), "lpr", "-P", "-l", "/usr/etc/lpc", "/usr/etc/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'", "-#" }, { "lpd", N_("Berkeley lpd (/usr/libexec/lpc)"), "lpr", "-P", "-l", "/usr/libexec/lpc", "/usr/libexec/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'", "-#" }, { "lpd", N_("Berkeley lpd (/usr/sbin/lpc)"), "lpr", "-P", "-l", "/usr/sbin/lpc", "/usr/sbin/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'", "-#" }, }; 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; char *quoted_queue_name = 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); if (queue_name[0]) quoted_queue_name = g_shell_quote(queue_name); 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] ? quoted_queue_name : "", count_string ? count_string : "", raw ? global_printing_system->raw_flag : "", extra_options ? " " : "", extra_options ? extra_options : ""); SAFE_FREE(count_string); SAFE_FREE(quoted_queue_name); return print_cmd; } static void append_external_options(char **command, const stp_vars_t *v) { stp_string_list_t *external_options; if (! command || ! *command) return; external_options = stp_get_external_options(v); if (external_options) { int count = stp_string_list_count(external_options); int i; for (i = 0; i < count; i++) { stp_param_string_t *param = stp_string_list_param(external_options, i); char *quoted_name=g_shell_quote(param->name); char *quoted_value=g_shell_quote(param->text); stp_catprintf(command, " -o%s=%s", quoted_name, quoted_value); SAFE_FREE(quoted_name); SAFE_FREE(quoted_value); } stp_string_list_destroy(external_options); } } 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; } static void writefunc(void *file, const char *buf, size_t bytes) { FILE *prn = (FILE *)file; fwrite(buf, 1, bytes, prn); } 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(); stp_set_errfunc(printer->v, writefunc); stp_set_errdata(printer->v, stderr); stpui_plist_set_copy_count(printer, 1); stp_set_string_parameter(printer->v, "InputImageType", image_type); stp_set_string_parameter(printer->v, "JobMode", "Page"); 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)) stp_set_driver(key->v, "ps2"); 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; } else { fprintf(stderr, "No printer found!\n"); return 0; } } 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) { SAFE_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_DIMENSION: stp_set_dimension_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) { char *locale; yyin = fp; stpui_printrc_current_printer = NULL; #ifdef HAVE_LOCALE_H locale = g_strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); #endif (void) yyparse(); #ifdef HAVE_LOCALE_H setlocale(LC_NUMERIC, locale); SAFE_FREE(locale); #endif 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) { #ifdef HAVE_LOCALE_H char *locale = g_strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); #endif if (strncmp("#PRINTRCv", line, 9) == 0) { /* Force locale to "C", so that numbers scan correctly */ #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); } #ifdef HAVE_LOCALE_H setlocale(LC_NUMERIC, locale); SAFE_FREE(locale); #endif } 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: case 5: 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 */ #ifdef HAVE_LOCALE_H char *locale = g_strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); #endif #ifdef DEBUG fprintf(stderr, "Number of printers: %d\n", stpui_plist_count); #endif fputs("#PRINTRCv5 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: %.6f\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: %f\n", stp_get_left(p->v)); fprintf(fp, " Top: %f\n", stp_get_top(p->v)); fprintf(fp, " Custom_Page_Width: %f\n", stp_get_page_width(p->v)); fprintf(fp, " Custom_Page_Height: %f\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 %f\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 } #ifdef HAVE_LOCALE_H setlocale(LC_NUMERIC, locale); SAFE_FREE(locale); #endif 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_unsafe(stpui_system_print_queues, "", _("(Default Printer)")); /* * Run the command, if any, to get the available printers... */ identify_print_system(); if (global_printing_system) { const char *old_locale = getenv("LC_ALL"); const char *old_lc_messages = getenv("LC_MESSAGES"); const char *old_lang = getenv("LANG"); (void) setenv("LC_ALL", "C", 1); (void) setenv("LC_MESSAGES", "C", 1); (void) setenv("LANG", "C", 1); 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_unsafe(stpui_system_print_queues, line, line); } } pclose(pfile); if (old_locale) setenv("LC_ALL", old_locale, 1); else unsetenv("LC_ALL"); if (old_lc_messages) setenv("LC_MESSAGES", old_lc_messages, 1); else unsetenv("LC_MESSAGES"); if (old_lang) setenv("LANG", old_lang, 1); else unsetenv("LANG"); } } } 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; } /* * * 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<<<<<<<<<<<<< * | | * | 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); } } write(syncfd[1], "Done", 5); _exit(0); } else /* Child 2 (printer command) */ { char *command; if (stpui_plist_get_command_type(printer) == COMMAND_TYPE_DEFAULT) { command = stpui_build_standard_print_command (printer, stp_get_printer(printer->v)); append_external_options(&command, printer->v); } else command = cast_safe(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]); #ifdef HAVE_LOCALE_H setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); #endif 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()); stp_start_job(np->v, &(image->im)); print_status = stp_print(np->v, &(image->im)); stp_end_job(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 print_status; } return 0; }