1/*
2 * "$Id: plist.c,v 1.18 2008/07/04 14:29:28 rlk Exp $"
3 *
4 *   Print plug-in for the GIMP.
5 *
6 *   Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
7 *	Robert Krawitz (rlk@alum.mit.edu)
8 *
9 *   This program is free software; you can redistribute it and/or modify it
10 *   under the terms of the GNU General Public License as published by the Free
11 *   Software Foundation; either version 2 of the License, or (at your option)
12 *   any later version.
13 *
14 *   This program is distributed in the hope that it will be useful, but
15 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 *   for more details.
18 *
19 *   You should have received a copy of the GNU General Public License
20 *   along with this program; if not, write to the Free Software
21 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <gutenprint/gutenprint-intl-internal.h>
29#include <gutenprint/gutenprint.h>
30#include <gutenprintui2/gutenprintui.h>
31#include "gutenprintui-internal.h"
32
33#include <unistd.h>
34#include <ctype.h>
35#include <stdio.h>
36#include <string.h>
37#include <errno.h>
38#include <locale.h>
39
40#include <sys/types.h>
41#include <signal.h>
42#include <sys/wait.h>
43
44typedef enum
45{
46  PRINTERS_NONE,
47  PRINTERS_LPC,
48  PRINTERS_LPSTAT
49} printer_system_t;
50
51static int	compare_printers (stpui_plist_t *p1, stpui_plist_t *p2);
52
53int		stpui_plist_current = 0,	/* Current system printer */
54		stpui_plist_count = 0;	/* Number of system printers */
55stpui_plist_t	*stpui_plist;			/* System printers */
56int             stpui_show_all_paper_sizes = 0;
57static char *printrc_name = NULL;
58static char *image_type;
59static gint image_raw_channels = 0;
60static gint image_channel_depth = 8;
61static stp_string_list_t *default_parameters = NULL;
62stp_string_list_t *stpui_system_print_queues;
63static const char *copy_count_name = "STPUICopyCount";
64
65#define SAFE_FREE(x)			\
66do						\
67{						\
68  if ((x))					\
69    g_free((char *)(x));			\
70  ((x)) = NULL;					\
71} while (0)
72
73typedef struct
74{
75  const char *printing_system_name;
76  const char *printing_system_text;
77  const char *print_command;
78  const char *queue_select;
79  const char *raw_flag;
80  const char *key_file;
81  const char *scan_command;
82  const char *copy_count_command;
83} print_system_t;
84
85/*
86 * Generic printing system, based on SysV lp
87 *
88 * CAUTION: Do not use lpstat -t or lpstat -p.
89 * See bug 742187 (huge delays with lpstat -d -p) for an explanation.
90 */
91static const print_system_t default_printing_system =
92  { "SysV", N_("System V lp"), "lp -s", "-d", "-oraw", "/usr/bin/lp",
93    "/usr/bin/lpstat -v | awk '/^device for /i {sub(\":\", \"\", $3); print $3}'",
94  "-n" };
95
96static print_system_t known_printing_systems[] =
97{
98  { "CUPS", N_("CUPS"), "lp -s", "-d", "-oraw", "/usr/sbin/cupsd",
99    "/usr/bin/lpstat -v | awk '/^device for /i {sub(\":\", \"\", $3); print $3}'",
100    "-n" },
101  { "SysV", N_("System V lp"), "lp -s", "-d", "-oraw", "/usr/bin/lp",
102    "/usr/bin/lpstat -v | awk '/^device for /i {sub(\":\", \"\", $3); print $3}'",
103    "-n" },
104  { "lpd", N_("Berkeley lpd (/etc/lpc)"), "lpr", "-P", "-l", "/etc/lpc",
105    "/etc/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'",
106    "-#" },
107  { "lpd", N_("Berkeley lpd (/usr/bsd/lpc)"), "lpr", "-P", "-l", "/usr/bsd/lpc",
108    "/usr/bsd/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'",
109    "-#" },
110  { "lpd", N_("Berkeley lpd (/usr/etc/lpc"), "lpr", "-P", "-l", "/usr/etc/lpc",
111    "/usr/etc/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'",
112    "-#" },
113  { "lpd", N_("Berkeley lpd (/usr/libexec/lpc)"), "lpr", "-P", "-l", "/usr/libexec/lpc",
114    "/usr/libexec/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'",
115    "-#" },
116  { "lpd", N_("Berkeley lpd (/usr/sbin/lpc)"), "lpr", "-P", "-l", "/usr/sbin/lpc",
117    "/usr/sbin/lpc status | awk '/^...*:/ {sub(\":.*\", \"\"); print}'",
118    "-#" },
119};
120
121static unsigned print_system_count = sizeof(known_printing_systems) / sizeof(print_system_t);
122
123static const print_system_t *global_printing_system = NULL;
124
125static void
126initialize_default_parameters(void)
127{
128  default_parameters = stp_string_list_create();
129  stp_string_list_add_string(default_parameters, "PrintingSystem", "Autodetect");
130  stp_string_list_add_string(default_parameters, "PrintCommand", "");
131  stp_string_list_add_string(default_parameters, "QueueSelect", "");
132  stp_string_list_add_string(default_parameters, "RawOutputFlag", "");
133  stp_string_list_add_string(default_parameters, "ScanOnStartup", "False");
134  stp_string_list_add_string(default_parameters, "ScanPrintersCommand", "");
135}
136
137void
138stpui_set_global_parameter(const char *param, const char *value)
139{
140  stp_string_list_remove_string(default_parameters, param);
141  stp_string_list_add_string(default_parameters, param, value);
142}
143
144const char *
145stpui_get_global_parameter(const char *param)
146{
147  stp_param_string_t *ps = stp_string_list_find(default_parameters, param);
148  if (ps)
149    return ps->text;
150  else
151    return NULL;
152}
153
154static const print_system_t *
155identify_print_system(void)
156{
157  int i;
158  if (!global_printing_system)
159    {
160      for (i = 0; i < print_system_count; i++)
161	{
162	  if (!access(known_printing_systems[i].key_file, R_OK))
163	    {
164	      global_printing_system = &(known_printing_systems[i]);
165	      break;
166	    }
167	}
168      if (!global_printing_system)
169	global_printing_system = &default_printing_system;
170    }
171  return global_printing_system;
172}
173
174char *
175stpui_build_standard_print_command(const stpui_plist_t *plist,
176				   const stp_printer_t *printer)
177{
178  const char *queue_name = stpui_plist_get_queue_name(plist);
179  const char *extra_options = stpui_plist_get_extra_printer_options(plist);
180  const char *family = stp_printer_get_family(printer);
181  int copy_count = stpui_plist_get_copy_count(plist);
182  int raw = 0;
183  char *print_cmd;
184  char *count_string = NULL;
185  char *quoted_queue_name = NULL;
186  if (!queue_name)
187    queue_name = "";
188  identify_print_system();
189  if (strcmp(family, "ps") == 0)
190    raw = 0;
191  else
192    raw = 1;
193
194  if (copy_count > 1)
195    stp_asprintf(&count_string, "%s %d ",
196		 global_printing_system->copy_count_command, copy_count);
197
198  if (queue_name[0])
199    quoted_queue_name = g_shell_quote(queue_name);
200
201  stp_asprintf(&print_cmd, "%s %s %s %s %s%s%s",
202	       global_printing_system->print_command,
203	       queue_name[0] ? global_printing_system->queue_select : "",
204	       queue_name[0] ? quoted_queue_name : "",
205	       count_string ? count_string : "",
206	       raw ? global_printing_system->raw_flag : "",
207	       extra_options ? " " : "",
208	       extra_options ? extra_options : "");
209  SAFE_FREE(count_string);
210  SAFE_FREE(quoted_queue_name);
211  return print_cmd;
212}
213
214static void
215append_external_options(char **command, const stp_vars_t *v)
216{
217  stp_string_list_t *external_options;
218  if (! command || ! *command)
219    return;
220  external_options = stp_get_external_options(v);
221  if (external_options)
222    {
223      int count = stp_string_list_count(external_options);
224      int i;
225      for (i = 0; i < count; i++)
226	{
227	  stp_param_string_t *param = stp_string_list_param(external_options, i);
228	  char *quoted_name=g_shell_quote(param->name);
229	  char *quoted_value=g_shell_quote(param->text);
230	  stp_catprintf(command, " -o%s=%s", quoted_name, quoted_value);
231	  SAFE_FREE(quoted_name);
232	  SAFE_FREE(quoted_value);
233	}
234      stp_string_list_destroy(external_options);
235    }
236}
237
238
239void
240stpui_set_printrc_file(const char *name)
241{
242  if (name && name == printrc_name)
243    return;
244  SAFE_FREE(printrc_name);
245  if (name)
246    printrc_name = g_strdup(name);
247  else
248    printrc_name = g_build_filename(g_get_home_dir(), ".gutenprintrc", NULL);
249}
250
251const char *
252stpui_get_printrc_file(void)
253{
254  if (!printrc_name)
255    stpui_set_printrc_file(NULL);
256  return printrc_name;
257}
258
259#define PLIST_ACCESSORS(name)						\
260void									\
261stpui_plist_set_##name(stpui_plist_t *p, const char *val)		\
262{									\
263  if (p->name == val)							\
264    return;								\
265  SAFE_FREE(p->name);							\
266  p->name = g_strdup(val);						\
267}									\
268									\
269void									\
270stpui_plist_set_##name##_n(stpui_plist_t *p, const char *val, int n)	\
271{									\
272  if (p->name == val)							\
273    return;								\
274  SAFE_FREE(p->name);							\
275  p->name = g_strndup(val, n);						\
276}									\
277									\
278const char *								\
279stpui_plist_get_##name(const stpui_plist_t *p)				\
280{									\
281  return p->name;							\
282}
283
284PLIST_ACCESSORS(output_filename)
285PLIST_ACCESSORS(name)
286PLIST_ACCESSORS(queue_name)
287PLIST_ACCESSORS(extra_printer_options)
288PLIST_ACCESSORS(custom_command)
289PLIST_ACCESSORS(current_standard_command)
290
291void
292stpui_plist_set_command_type(stpui_plist_t *p, command_t val)
293{
294  switch (val)
295    {
296    case COMMAND_TYPE_DEFAULT:
297    case COMMAND_TYPE_CUSTOM:
298    case COMMAND_TYPE_FILE:
299      p->command_type = val;
300      break;
301    default:
302      p->command_type = COMMAND_TYPE_DEFAULT;
303    }
304}
305
306command_t
307stpui_plist_get_command_type(const stpui_plist_t *p)
308{
309  return p->command_type;
310}
311
312void
313stpui_plist_set_copy_count(stpui_plist_t *p, gint copy_count)
314{
315  if (copy_count > 0)
316    stp_set_int_parameter(p->v, copy_count_name, copy_count);
317}
318
319gint
320stpui_plist_get_copy_count(const stpui_plist_t *p)
321{
322  if (stp_check_int_parameter(p->v, copy_count_name, STP_PARAMETER_ACTIVE))
323    return stp_get_int_parameter(p->v, copy_count_name);
324  else
325    return 1;
326}
327
328void
329stpui_set_image_type(const char *itype)
330{
331  image_type = g_strdup(itype);
332}
333
334void
335stpui_set_image_raw_channels(gint channels)
336{
337  image_raw_channels = channels;
338}
339
340void
341stpui_set_image_channel_depth(gint depth)
342{
343  image_channel_depth = depth;
344}
345
346static void
347writefunc(void *file, const char *buf, size_t bytes)
348{
349  FILE *prn = (FILE *)file;
350  fwrite(buf, 1, bytes, prn);
351}
352
353static void
354stpui_errfunc(void *file, const char *buf, size_t bytes)
355{
356  g_message("%s",buf);
357}
358
359void
360stpui_printer_initialize(stpui_plist_t *printer)
361{
362  char tmp[32];
363  stpui_plist_set_name(printer, "");
364  stpui_plist_set_output_filename(printer, "");
365  stpui_plist_set_queue_name(printer, "");
366  stpui_plist_set_extra_printer_options(printer, "");
367  stpui_plist_set_custom_command(printer, "");
368  stpui_plist_set_current_standard_command(printer, "");
369  printer->command_type = COMMAND_TYPE_DEFAULT;
370  printer->scaling = 100.0;
371  printer->orientation = ORIENT_AUTO;
372  printer->auto_size_roll_feed_paper = 0;
373  printer->unit = 0;
374  printer->v = stp_vars_create();
375  stp_set_errfunc(printer->v, writefunc);
376  stp_set_errdata(printer->v, stderr);
377  stpui_plist_set_copy_count(printer, 1);
378  stp_set_string_parameter(printer->v, "InputImageType", image_type);
379  if (image_raw_channels)
380    {
381      (void) sprintf(tmp, "%d", image_raw_channels);
382      stp_set_string_parameter(printer->v, "RawChannels", tmp);
383    }
384  if (image_channel_depth)
385    {
386      (void) sprintf(tmp, "%d", image_channel_depth);
387      stp_set_string_parameter(printer->v, "ChannelBitDepth", tmp);
388    }
389  printer->invalid_mask = INVALID_TOP | INVALID_LEFT;
390}
391
392static void
393stpui_plist_destroy(stpui_plist_t *printer)
394{
395  SAFE_FREE(printer->name);
396  SAFE_FREE(printer->queue_name);
397  SAFE_FREE(printer->extra_printer_options);
398  SAFE_FREE(printer->custom_command);
399  SAFE_FREE(printer->current_standard_command);
400  SAFE_FREE(printer->output_filename);
401  stp_vars_destroy(printer->v);
402}
403
404void
405stpui_plist_copy(stpui_plist_t *vd, const stpui_plist_t *vs)
406{
407  if (vs == vd)
408    return;
409  stp_vars_copy(vd->v, vs->v);
410  vd->scaling = vs->scaling;
411  vd->orientation = vs->orientation;
412  vd->auto_size_roll_feed_paper = vs->auto_size_roll_feed_paper;
413  vd->unit = vs->unit;
414  vd->invalid_mask = vs->invalid_mask;
415  vd->command_type = vs->command_type;
416  stpui_plist_set_name(vd, stpui_plist_get_name(vs));
417  stpui_plist_set_queue_name(vd, stpui_plist_get_queue_name(vs));
418  stpui_plist_set_extra_printer_options(vd, stpui_plist_get_extra_printer_options(vs));
419  stpui_plist_set_custom_command(vd, stpui_plist_get_custom_command(vs));
420  stpui_plist_set_current_standard_command(vd, stpui_plist_get_current_standard_command(vs));
421  stpui_plist_set_output_filename(vd, stpui_plist_get_output_filename(vs));
422  stpui_plist_set_copy_count(vd, stpui_plist_get_copy_count(vs));
423}
424
425static stpui_plist_t *
426allocate_stpui_plist_copy(const stpui_plist_t *vs)
427{
428  stpui_plist_t *rep = g_malloc(sizeof(stpui_plist_t));
429  memset(rep, 0, sizeof(stpui_plist_t));
430  rep->v = stp_vars_create();
431  stpui_plist_copy(rep, vs);
432  return rep;
433}
434
435static void
436check_plist(int count)
437{
438  static int current_plist_size = 0;
439  int i;
440  if (count <= current_plist_size)
441    return;
442  else if (current_plist_size == 0)
443    {
444      current_plist_size = count;
445      stpui_plist = g_malloc(current_plist_size * sizeof(stpui_plist_t));
446      for (i = 0; i < current_plist_size; i++)
447	{
448	  memset(&(stpui_plist[i]), 0, sizeof(stpui_plist_t));
449	  stpui_printer_initialize(&(stpui_plist[i]));
450	}
451    }
452  else
453    {
454      int old_plist_size = current_plist_size;
455      current_plist_size *= 2;
456      if (current_plist_size < count)
457	current_plist_size = count;
458      stpui_plist = g_realloc(stpui_plist, current_plist_size * sizeof(stpui_plist_t));
459      for (i = old_plist_size; i < current_plist_size; i++)
460	{
461	  memset(&(stpui_plist[i]), 0, sizeof(stpui_plist_t));
462	  stpui_printer_initialize(&(stpui_plist[i]));
463	}
464    }
465}
466
467#define GET_MANDATORY_INTERNAL_STRING_PARAM(param)		\
468do {								\
469  if ((commaptr = strchr(lineptr, ',')) == NULL)		\
470    continue;							\
471  stpui_plist_set_##param##_n(&key, lineptr, commaptr - line);	\
472  lineptr = commaptr + 1;					\
473} while (0)
474
475#define GET_MANDATORY_STRING_PARAM(param)		\
476do {							\
477  if ((commaptr = strchr(lineptr, ',')) == NULL)	\
478    continue;						\
479  stp_set_##param##_n(key.v, lineptr, commaptr - line);	\
480  lineptr = commaptr + 1;				\
481} while (0)
482
483static int
484get_mandatory_string_param(stp_vars_t *v, const char *param, char **lineptr)
485{
486  char *commaptr = strchr(*lineptr, ',');
487  if (commaptr == NULL)
488    return 0;
489  stp_set_string_parameter_n(v, param, *lineptr, commaptr - *lineptr);
490  *lineptr = commaptr + 1;
491  return 1;
492}
493
494static int
495get_mandatory_file_param(stp_vars_t *v, const char *param, char **lineptr)
496{
497  char *commaptr = strchr(*lineptr, ',');
498  if (commaptr == NULL)
499    return 0;
500  stp_set_file_parameter_n(v, param, *lineptr, commaptr - *lineptr);
501  *lineptr = commaptr + 1;
502  return 1;
503}
504
505#define GET_MANDATORY_INT_PARAM(param)			\
506do {							\
507  if ((commaptr = strchr(lineptr, ',')) == NULL)	\
508    continue;						\
509  stp_set_##param(key.v, atoi(lineptr));		\
510  lineptr = commaptr + 1;				\
511} while (0)
512
513#define GET_MANDATORY_INTERNAL_INT_PARAM(param)		\
514do {							\
515  if ((commaptr = strchr(lineptr, ',')) == NULL)	\
516    continue;						\
517  key.param = atoi(lineptr);				\
518  lineptr = commaptr + 1;				\
519} while (0)
520
521static void
522get_optional_string_param(stp_vars_t *v, const char *param,
523			  char **lineptr, int *keepgoing)
524{
525  if (*keepgoing)
526    {
527      char *commaptr = strchr(*lineptr, ',');
528      if (commaptr == NULL)
529	{
530	  stp_set_string_parameter(v, param, *lineptr);
531	  *keepgoing = 0;
532	}
533      else
534	{
535	  stp_set_string_parameter_n(v, param, *lineptr, commaptr - *lineptr);
536	  *lineptr = commaptr + 1;
537	}
538    }
539}
540
541#define GET_OPTIONAL_INT_PARAM(param)					\
542do {									\
543  if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL))	\
544    {									\
545      keepgoing = 0;							\
546    }									\
547  else									\
548    {									\
549      stp_set_##param(key.v, atoi(lineptr));				\
550      lineptr = commaptr + 1;						\
551    }									\
552} while (0)
553
554#define GET_OPTIONAL_INTERNAL_INT_PARAM(param)				\
555do {									\
556  if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL))	\
557    {									\
558      keepgoing = 0;							\
559    }									\
560  else									\
561    {									\
562      key.param = atoi(lineptr);					\
563      lineptr = commaptr + 1;						\
564    }									\
565} while (0)
566
567#define IGNORE_OPTIONAL_PARAM(param)					\
568do {									\
569  if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL))	\
570    {									\
571      keepgoing = 0;							\
572    }									\
573  else									\
574    {									\
575      lineptr = commaptr + 1;						\
576    }									\
577} while (0)
578
579static void
580get_optional_float_param(stp_vars_t *v, const char *param,
581			 char **lineptr, int *keepgoing)
582{
583  if (*keepgoing)
584    {
585      char *commaptr = strchr(*lineptr, ',');
586      if (commaptr == NULL)
587	{
588	  stp_set_float_parameter(v, param, atof(*lineptr));
589	  *keepgoing = 0;
590	}
591      else
592	{
593	  stp_set_float_parameter(v, param, atof(*lineptr));
594	  *lineptr = commaptr + 1;
595	}
596    }
597}
598
599#define GET_OPTIONAL_INTERNAL_FLOAT_PARAM(param)			\
600do {									\
601  if ((keepgoing == 0) || ((commaptr = strchr(lineptr, ',')) == NULL))	\
602    {									\
603      keepgoing = 0;							\
604    }									\
605  else									\
606    {									\
607      key.param = atof(lineptr);					\
608    }									\
609} while (0)
610
611static void *
612psearch(const void *key, void *base, size_t nmemb, size_t size,
613	int (*compar)(const void *, const void *))
614{
615  int i;
616  char *cbase = (char *) base;
617  for (i = 0; i < nmemb; i++)
618    {
619      if ((*compar)(key, (const void *) cbase) == 0)
620	return (void *) cbase;
621      cbase += size;
622    }
623  return NULL;
624}
625
626stpui_plist_t *
627stpui_plist_create(const char *name, const char *driver)
628{
629  stpui_plist_t key;
630  stpui_plist_t *answer = NULL;
631  memset(&key, 0, sizeof(key));
632  stpui_printer_initialize(&key);
633  key.invalid_mask = 0;
634  stpui_plist_set_name(&key, name);
635  stp_set_driver(key.v, driver);
636  if (stpui_plist_add(&key, 0))
637    answer = psearch(&key, stpui_plist, stpui_plist_count,
638		     sizeof(stpui_plist_t),
639		     (int (*)(const void *, const void *)) compare_printers);
640  SAFE_FREE(key.name);
641  SAFE_FREE(key.queue_name);
642  SAFE_FREE(key.extra_printer_options);
643  SAFE_FREE(key.custom_command);
644  SAFE_FREE(key.current_standard_command);
645  SAFE_FREE(key.output_filename);
646  stp_vars_destroy(key.v);
647  return answer;
648}
649
650int
651stpui_plist_add(const stpui_plist_t *key, int add_only)
652{
653  /*
654   * The format of the list is the File printer followed by a qsort'ed list
655   * of system printers. So, if we want to update the file printer, it is
656   * always first in the list, else call psearch.
657   */
658  stpui_plist_t *p;
659  if (!stp_get_printer(key->v))
660    stp_set_driver(key->v, "ps2");
661  if (stp_get_printer(key->v))
662    {
663      p = psearch(key, stpui_plist, stpui_plist_count,
664		  sizeof(stpui_plist_t),
665		  (int (*)(const void *, const void *)) compare_printers);
666      if (p == NULL)
667	{
668#ifdef DEBUG
669	  fprintf(stderr, "Adding new printer from printrc file: %s\n",
670		  key->name);
671#endif
672	  check_plist(stpui_plist_count + 1);
673	  p = stpui_plist + stpui_plist_count;
674	  stpui_plist_count++;
675	  stpui_plist_copy(p, key);
676	  if (strlen(stpui_plist_get_queue_name(p)) == 0 &&
677	      stp_string_list_is_present(stpui_system_print_queues,
678					 stpui_plist_get_name(p)))
679	    stpui_plist_set_queue_name(p, stpui_plist_get_name(p));
680	}
681      else
682	{
683	  if (add_only)
684	    return 0;
685#ifdef DEBUG
686	  fprintf(stderr, "Updating printer %s.\n", key->name);
687#endif
688	  stpui_plist_copy(p, key);
689	}
690      return 1;
691    }
692  else
693    {
694      fprintf(stderr, "No printer found!\n");
695      return 0;
696    }
697}
698
699static void
700stpui_printrc_load_v0(FILE *fp)
701{
702  char		line[1024];	/* Line in printrc file */
703  char		*lineptr;	/* Pointer in line */
704  char		*commaptr;	/* Pointer to next comma */
705  stpui_plist_t	key;		/* Search key */
706  int keepgoing = 1;
707  (void) memset(line, 0, 1024);
708  (void) memset(&key, 0, sizeof(stpui_plist_t));
709  stpui_printer_initialize(&key);
710  key.name = g_strdup(_("File"));
711  while (fgets(line, sizeof(line), fp) != NULL)
712    {
713      /*
714       * Read old format printrc lines...
715       */
716
717      stpui_printer_initialize(&key);
718      key.invalid_mask = 0;
719      lineptr = line;
720
721      /*
722       * Read the command-delimited printer definition data.  Note that
723       * we can't use sscanf because %[^,] fails if the string is empty...
724       */
725
726      GET_MANDATORY_INTERNAL_STRING_PARAM(name);
727      GET_MANDATORY_INTERNAL_STRING_PARAM(custom_command);
728      GET_MANDATORY_STRING_PARAM(driver);
729
730      if (! stp_get_printer(key.v))
731	continue;
732
733      if (!get_mandatory_file_param(key.v, "PPDFile", &lineptr))
734	continue;
735      if ((commaptr = strchr(lineptr, ',')) != NULL)
736	{
737	  switch (atoi(lineptr))
738	    {
739	    case 1:
740	      stp_set_string_parameter(key.v, "PrintingMode", "Color");
741	      break;
742	    case 0:
743	    default:
744	      stp_set_string_parameter(key.v, "PrintingMode", "BW");
745	      break;
746	    }
747	}
748      else
749	continue;
750
751      if (!get_mandatory_string_param(key.v, "Resolution", &lineptr))
752	continue;
753      if (!get_mandatory_string_param(key.v, "PageSize", &lineptr))
754	continue;
755      if (!get_mandatory_string_param(key.v, "MediaType", &lineptr))
756	continue;
757
758      get_optional_string_param(key.v, "InputSlot", &lineptr, &keepgoing);
759      get_optional_float_param(key.v, "Brightness", &lineptr, &keepgoing);
760
761      GET_OPTIONAL_INTERNAL_FLOAT_PARAM(scaling);
762      GET_OPTIONAL_INTERNAL_INT_PARAM(orientation);
763      GET_OPTIONAL_INT_PARAM(left);
764      GET_OPTIONAL_INT_PARAM(top);
765      get_optional_float_param(key.v, "Gamma", &lineptr, &keepgoing);
766      get_optional_float_param(key.v, "Contrast", &lineptr, &keepgoing);
767      get_optional_float_param(key.v, "Cyan", &lineptr, &keepgoing);
768      get_optional_float_param(key.v, "Magenta", &lineptr, &keepgoing);
769      get_optional_float_param(key.v, "Yellow", &lineptr, &keepgoing);
770      IGNORE_OPTIONAL_PARAM(linear);
771      IGNORE_OPTIONAL_PARAM(image_type);
772      get_optional_float_param(key.v, "Saturation", &lineptr, &keepgoing);
773      get_optional_float_param(key.v, "Density", &lineptr, &keepgoing);
774      get_optional_string_param(key.v, "InkType", &lineptr, &keepgoing);
775      get_optional_string_param(key.v,"DitherAlgorithm",&lineptr,&keepgoing);
776      GET_OPTIONAL_INTERNAL_INT_PARAM(unit);
777      stpui_plist_add(&key, 0);
778      g_free(key.name);
779      stp_vars_destroy(key.v);
780    }
781  stpui_plist_current = 0;
782}
783
784static void
785stpui_printrc_load_v1(FILE *fp)
786{
787  char		line[1024];	/* Line in printrc file */
788  stpui_plist_t	key;		/* Search key */
789  char *	current_printer = 0; /* printer to select */
790  (void) memset(line, 0, 1024);
791  (void) memset(&key, 0, sizeof(stpui_plist_t));
792  stpui_printer_initialize(&key);
793  key.name = g_strdup(_("File"));
794  while (fgets(line, sizeof(line), fp) != NULL)
795    {
796      /*
797       * Read new format printrc lines...
798       */
799
800      char *keyword, *end, *value;
801
802      keyword = line;
803      for (keyword = line; g_ascii_isspace(*keyword); keyword++)
804	{
805	  /* skip initial spaces... */
806	}
807      if (!g_ascii_isalpha(*keyword))
808	continue;
809      for (end = keyword; g_ascii_isalnum(*end) || *end == '-'; end++)
810	{
811	  /* find end of keyword... */
812	}
813      value = end;
814      while (g_ascii_isspace(*value))
815	{
816	  /* skip over white space... */
817	  value++;
818	}
819      if (*value != ':')
820	continue;
821      value++;
822      *end = '\0';
823      while (g_ascii_isspace(*value))
824	{
825	  /* skip over white space... */
826	  value++;
827	}
828      for (end = value; *end && *end != '\n'; end++)
829	{
830	  /* find end of line... */
831	}
832      *end = '\0';
833#ifdef DEBUG
834      fprintf(stderr, "Keyword = `%s', value = `%s'\n", keyword, value);
835#endif
836      if (strcasecmp("current-printer", keyword) == 0)
837	{
838	  SAFE_FREE(current_printer);
839	  current_printer = g_strdup(value);
840	}
841      else if (strcasecmp("printer", keyword) == 0)
842	{
843	  /* Switch to printer named VALUE */
844	  stpui_plist_add(&key, 0);
845#ifdef DEBUG
846	  fprintf(stderr,
847		  "output_to is now %s\n", stpui_plist_get_output_to(&key));
848#endif
849
850	  stp_vars_destroy(key.v);
851	  stpui_printer_initialize(&key);
852	  key.invalid_mask = 0;
853	  stpui_plist_set_name(&key, value);
854	}
855      else if (strcasecmp("destination", keyword) == 0)
856	stpui_plist_set_custom_command(&key, value);
857      else if (strcasecmp("driver", keyword) == 0)
858	stp_set_driver(key.v, value);
859      else if (strcasecmp("ppd-file", keyword) == 0)
860	stp_set_file_parameter(key.v, "PPDFile", value);
861      else if (strcasecmp("output-type", keyword) == 0)
862	{
863	  switch (atoi(value))
864	    {
865	    case 1:
866	      stp_set_string_parameter(key.v, "PrintingMode", "Color");
867	      break;
868	    case 0:
869	    default:
870	      stp_set_string_parameter(key.v, "PrintingMode", "BW");
871	      break;
872	    }
873	}
874      else if (strcasecmp("media-size", keyword) == 0)
875	stp_set_string_parameter(key.v, "PageSize", value);
876      else if (strcasecmp("media-type", keyword) == 0)
877	stp_set_string_parameter(key.v, "MediaType", value);
878      else if (strcasecmp("media-source", keyword) == 0)
879	stp_set_float_parameter(key.v, "Brightness", atof(value));
880      else if (strcasecmp("scaling", keyword) == 0)
881	key.scaling = atof(value);
882      else if (strcasecmp("orientation", keyword) == 0)
883	key.orientation = atoi(value);
884      else if (strcasecmp("left", keyword) == 0)
885	stp_set_left(key.v, atoi(value));
886      else if (strcasecmp("top", keyword) == 0)
887	stp_set_top(key.v, atoi(value));
888      else if (strcasecmp("linear", keyword) == 0)
889	/* Ignore linear */
890	;
891      else if (strcasecmp("image-type", keyword) == 0)
892	/* Ignore image type */
893	;
894      else if (strcasecmp("unit", keyword) == 0)
895	key.unit = atoi(value);
896      else if (strcasecmp("custom-page-width", keyword) == 0)
897	stp_set_page_width(key.v, atoi(value));
898      else if (strcasecmp("custom-page-height", keyword) == 0)
899	stp_set_page_height(key.v, atoi(value));
900      /* Special case Ink-Type and Dither-Algorithm */
901      else if (strcasecmp("ink-type", keyword) == 0)
902	stp_set_string_parameter(key.v, "InkType", value);
903      else if (strcasecmp("dither-algorithm", keyword) == 0)
904	stp_set_string_parameter(key.v, "DitherAlgorithm", value);
905      else
906	{
907	  stp_parameter_t desc;
908	  stp_curve_t *curve;
909	  stp_describe_parameter(key.v, keyword, &desc);
910	  switch (desc.p_type)
911	    {
912	    case STP_PARAMETER_TYPE_STRING_LIST:
913	      stp_set_string_parameter(key.v, keyword, value);
914	      break;
915	    case STP_PARAMETER_TYPE_FILE:
916	      stp_set_file_parameter(key.v, keyword, value);
917	      break;
918	    case STP_PARAMETER_TYPE_DOUBLE:
919	      stp_set_float_parameter(key.v, keyword, atof(value));
920	      break;
921	    case STP_PARAMETER_TYPE_INT:
922	      stp_set_int_parameter(key.v, keyword, atoi(value));
923	      break;
924	    case STP_PARAMETER_TYPE_BOOLEAN:
925	      stp_set_boolean_parameter(key.v, keyword, atoi(value));
926	      break;
927	    case STP_PARAMETER_TYPE_CURVE:
928	      curve = stp_curve_create_from_string(value);
929	      if (curve)
930		{
931		  stp_set_curve_parameter(key.v, keyword, curve);
932		  stp_curve_destroy(curve);
933		}
934	      break;
935	    default:
936	      if (strlen(value))
937		{
938		  char buf[1024];
939		  snprintf(buf, sizeof(buf),
940			   "Unrecognized keyword `%s' in printrc; value `%s' (%d)\n",
941			   keyword, value, desc.p_type);
942		}
943	    }
944	  stp_parameter_description_destroy(&desc);
945	}
946    }
947  if (strlen(key.name) > 0)
948    {
949      stpui_plist_add(&key, 0);
950      stp_vars_destroy(key.v);
951      g_free(key.name);
952    }
953  if (current_printer)
954    {
955      int i;
956      for (i = 0; i < stpui_plist_count; i ++)
957	if (strcmp(current_printer, stpui_plist[i].name) == 0)
958	  stpui_plist_current = i;
959    }
960}
961
962char *stpui_printrc_current_printer = NULL;
963extern FILE *yyin;
964extern int yyparse(void);
965
966static void
967stpui_printrc_load_v2(FILE *fp)
968{
969  int retval;
970  char *locale;
971  yyin = fp;
972
973  stpui_printrc_current_printer = NULL;
974#ifdef HAVE_LOCALE_H
975  locale = g_strdup(setlocale(LC_NUMERIC, NULL));
976  setlocale(LC_NUMERIC, "C");
977#endif
978  retval = yyparse();
979#ifdef HAVE_LOCALE_H
980  setlocale(LC_NUMERIC, locale);
981  SAFE_FREE(locale);
982#endif
983  if (stpui_printrc_current_printer)
984    {
985      int i;
986      for (i = 0; i < stpui_plist_count; i ++)
987	{
988	  if (strcmp(stpui_printrc_current_printer, stpui_plist[i].name) == 0)
989	    stpui_plist_current = i;
990	  if (!stp_check_boolean_parameter(stpui_plist[i].v,
991					   "PageSizeExtended",
992					   STP_PARAMETER_ACTIVE))
993	    stp_set_boolean_parameter(stpui_plist[i].v, "PageSizeExtended", 0);
994	}
995      SAFE_FREE(stpui_printrc_current_printer);
996    }
997}
998
999/*
1000 * 'stpui_printrc_load()' - Load the printer resource configuration file.
1001 */
1002void
1003stpui_printrc_load(void)
1004{
1005  FILE		*fp;		/* Printrc file */
1006  char		line[1024];	/* Line in printrc file */
1007  int		format = 0;	/* rc file format version */
1008  const char *filename = stpui_get_printrc_file();
1009
1010  initialize_default_parameters();
1011  check_plist(1);
1012
1013 /*
1014  * Get the printer list...
1015  */
1016
1017  stpui_get_system_printers();
1018
1019  if ((fp = fopen(filename, "r")) != NULL)
1020    {
1021      (void) memset(line, 0, 1024);
1022      if (fgets(line, sizeof(line), fp) != NULL)
1023	{
1024#ifdef HAVE_LOCALE_H
1025	  char *locale = g_strdup(setlocale(LC_NUMERIC, NULL));
1026	  setlocale(LC_NUMERIC, "C");
1027#endif
1028	  if (strncmp("#PRINTRCv", line, 9) == 0)
1029	    {
1030	      /* Force locale to "C", so that numbers scan correctly */
1031#ifdef DEBUG
1032	      fprintf(stderr, "Found printrc version tag: `%s'\n", line);
1033	      fprintf(stderr, "Version number: `%s'\n", &(line[9]));
1034#endif
1035	      (void) sscanf(&(line[9]), "%d", &format);
1036	    }
1037#ifdef HAVE_LOCALE_H
1038	  setlocale(LC_NUMERIC, locale);
1039	  SAFE_FREE(locale);
1040#endif
1041	}
1042      rewind(fp);
1043      switch (format)
1044	{
1045	case 0:
1046	  stpui_printrc_load_v0(fp);
1047	  break;
1048	case 1:
1049	  stpui_printrc_load_v1(fp);
1050	  break;
1051	case 2:
1052	case 3:
1053	case 4:
1054	  stpui_printrc_load_v2(fp);
1055	  break;
1056	}
1057      (void) fclose(fp);
1058    }
1059  if (stpui_plist_count == 0)
1060    stpui_plist_create(_("Printer"), "ps2");
1061}
1062
1063/*
1064 * 'stpui_printrc_save()' - Save the current printer resource configuration.
1065 */
1066void
1067stpui_printrc_save(void)
1068{
1069  FILE		*fp;		/* Printrc file */
1070  int		i;		/* Looping var */
1071  size_t global_settings_count = stp_string_list_count(default_parameters);
1072  stpui_plist_t	*p;		/* Current printer */
1073  const char *filename = stpui_get_printrc_file();
1074
1075
1076  if ((fp = fopen(filename, "w")) != NULL)
1077    {
1078      /*
1079       * Write the contents of the printer list...
1080       */
1081
1082      /* Force locale to "C", so that numbers print correctly */
1083#ifdef HAVE_LOCALE_H
1084      char *locale = g_strdup(setlocale(LC_NUMERIC, NULL));
1085      setlocale(LC_NUMERIC, "C");
1086#endif
1087#ifdef DEBUG
1088      fprintf(stderr, "Number of printers: %d\n", stpui_plist_count);
1089#endif
1090
1091      fputs("#PRINTRCv4 written by Gutenprint " PLUG_IN_VERSION "\n\n", fp);
1092
1093      fprintf(fp, "Global-Settings:\n");
1094      fprintf(fp, "  Current-Printer: \"%s\"\n",
1095	      stpui_plist[stpui_plist_current].name);
1096      fprintf(fp, "  Show-All-Paper-Sizes: %s\n",
1097	      stpui_show_all_paper_sizes ? "True" : "False");
1098      for (i = 0; i < global_settings_count; i++)
1099	{
1100	  stp_param_string_t *ps = stp_string_list_param(default_parameters, i);
1101	  fprintf(fp, "  %s \"%s\"\n", ps->name, ps->text);
1102	}
1103      fprintf(fp, "End-Global-Settings:\n");
1104
1105      for (i = 0, p = stpui_plist; i < stpui_plist_count; i ++, p ++)
1106	{
1107	  int count;
1108	  int j;
1109	  stp_parameter_list_t *params = stp_get_parameter_list(p->v);
1110	  count = stp_parameter_list_count(params);
1111	  fprintf(fp, "\nPrinter: \"%s\" \"%s\"\n",
1112		  p->name, stp_get_driver(p->v));
1113	  fprintf(fp, "  Command-Type: %d\n", p->command_type);
1114	  fprintf(fp, "  Queue-Name: \"%s\"\n", p->queue_name);
1115	  fprintf(fp, "  Output-Filename: \"%s\"\n", p->output_filename);
1116	  fprintf(fp, "  Extra-Printer-Options: \"%s\"\n", p->extra_printer_options);
1117	  fprintf(fp, "  Custom-Command: \"%s\"\n", p->custom_command);
1118	  fprintf(fp, "  Scaling: %.3f\n", p->scaling);
1119	  fprintf(fp, "  Orientation: %d\n", p->orientation);
1120	  fprintf(fp, "  Autosize-Roll-Paper: %d\n", p->auto_size_roll_feed_paper);
1121	  fprintf(fp, "  Unit: %d\n", p->unit);
1122
1123	  fprintf(fp, "  Left: %d\n", stp_get_left(p->v));
1124	  fprintf(fp, "  Top: %d\n", stp_get_top(p->v));
1125	  fprintf(fp, "  Custom_Page_Width: %d\n", stp_get_page_width(p->v));
1126	  fprintf(fp, "  Custom_Page_Height: %d\n", stp_get_page_height(p->v));
1127	  fprintf(fp, "  Parameter %s Int True %d\n", copy_count_name,
1128		  stpui_plist_get_copy_count(p));
1129
1130	  for (j = 0; j < count; j++)
1131	    {
1132	      const stp_parameter_t *param = stp_parameter_list_param(params, j);
1133	      if (strcmp(param->name, "AppGamma") == 0)
1134		continue;
1135	      switch (param->p_type)
1136		{
1137		case STP_PARAMETER_TYPE_STRING_LIST:
1138		  if (stp_check_string_parameter(p->v, param->name,
1139						 STP_PARAMETER_INACTIVE))
1140		    fprintf(fp, "  Parameter %s String %s \"%s\"\n",
1141			    param->name,
1142			    ((stp_get_string_parameter_active
1143			      (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
1144			     "True" : "False"),
1145			    stp_get_string_parameter(p->v, param->name));
1146		  break;
1147		case STP_PARAMETER_TYPE_FILE:
1148		  if (stp_check_file_parameter(p->v, param->name,
1149						 STP_PARAMETER_INACTIVE))
1150		    fprintf(fp, "  Parameter %s File %s \"%s\"\n", param->name,
1151			    ((stp_get_file_parameter_active
1152			      (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
1153			     "True" : "False"),
1154			    stp_get_file_parameter(p->v, param->name));
1155		  break;
1156		case STP_PARAMETER_TYPE_DOUBLE:
1157		  if (stp_check_float_parameter(p->v, param->name,
1158						 STP_PARAMETER_INACTIVE))
1159		    fprintf(fp, "  Parameter %s Double %s %f\n", param->name,
1160			    ((stp_get_float_parameter_active
1161			      (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
1162			     "True" : "False"),
1163			    stp_get_float_parameter(p->v, param->name));
1164		  break;
1165		case STP_PARAMETER_TYPE_DIMENSION:
1166		  if (stp_check_dimension_parameter(p->v, param->name,
1167						    STP_PARAMETER_INACTIVE))
1168		    fprintf(fp, "  Parameter %s Dimension %s %d\n", param->name,
1169			    ((stp_get_dimension_parameter_active
1170			      (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
1171			     "True" : "False"),
1172			    stp_get_dimension_parameter(p->v, param->name));
1173		  break;
1174		case STP_PARAMETER_TYPE_INT:
1175		  if (stp_check_int_parameter(p->v, param->name,
1176						 STP_PARAMETER_INACTIVE))
1177		    fprintf(fp, "  Parameter %s Int %s %d\n", param->name,
1178			    ((stp_get_int_parameter_active
1179			      (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
1180			     "True" : "False"),
1181			    stp_get_int_parameter(p->v, param->name));
1182		  break;
1183		case STP_PARAMETER_TYPE_BOOLEAN:
1184		  if (stp_check_boolean_parameter(p->v, param->name,
1185						 STP_PARAMETER_INACTIVE))
1186		    fprintf(fp, "  Parameter %s Boolean %s %s\n", param->name,
1187			    ((stp_get_boolean_parameter_active
1188			      (p->v, param->name) == STP_PARAMETER_ACTIVE) ?
1189			     "True" : "False"),
1190			    (stp_get_boolean_parameter(p->v, param->name) ?
1191			     "True" : "False"));
1192		  break;
1193		case STP_PARAMETER_TYPE_CURVE:
1194		  if (stp_check_curve_parameter(p->v, param->name,
1195						 STP_PARAMETER_INACTIVE))
1196		    {
1197		      const stp_curve_t *curve =
1198			stp_get_curve_parameter(p->v, param->name);
1199		      if (curve)
1200			{
1201			  fprintf(fp, "  Parameter %s Curve %s '",
1202				  param->name,
1203				  ((stp_get_curve_parameter_active
1204				    (p->v, param->name) ==
1205				    STP_PARAMETER_ACTIVE) ?
1206				   "True" : "False"));
1207			  stp_curve_write(fp, curve);
1208			  fprintf(fp, "'\n");
1209			}
1210		    }
1211		  break;
1212		default:
1213		  break;
1214		}
1215	    }
1216	  stp_parameter_list_destroy(params);
1217#ifdef DEBUG
1218	  fprintf(stderr, "Wrote printer %d: %s\n", i, p->name);
1219#endif
1220	}
1221#ifdef HAVE_LOCALE_H
1222      setlocale(LC_NUMERIC, locale);
1223      SAFE_FREE(locale);
1224#endif
1225      fclose(fp);
1226    }
1227  else
1228    fprintf(stderr, "could not open printrc file \"%s\"\n",filename);
1229}
1230
1231/*
1232 * 'compare_printers()' - Compare system printer names for qsort().
1233 */
1234
1235static int
1236compare_printers(stpui_plist_t *p1, stpui_plist_t *p2)
1237{
1238  return (strcmp(p1->name, p2->name));
1239}
1240
1241/*
1242 * 'stpui_get_system_printers()' - Get a complete list of printers from the spooler.
1243 */
1244
1245void
1246stpui_get_system_printers(void)
1247{
1248  FILE *pfile;			/* Pipe to status command */
1249  char  line[1025];		/* Line from status command */
1250
1251  stpui_system_print_queues = stp_string_list_create();
1252  stp_string_list_add_string(stpui_system_print_queues, "",
1253			     _("(Default Printer)"));
1254
1255 /*
1256  * Run the command, if any, to get the available printers...
1257  */
1258
1259  identify_print_system();
1260  if (global_printing_system)
1261  {
1262    const char *old_locale = getenv("LC_ALL");
1263    const char *old_lc_messages = getenv("LC_MESSAGES");
1264    const char *old_lang = getenv("LANG");
1265    (void) setenv("LC_ALL", "C", 1);
1266    (void) setenv("LC_MESSAGES", "C", 1);
1267    (void) setenv("LANG", "C", 1);
1268    if ((pfile = popen(global_printing_system->scan_command, "r")) != NULL)
1269    {
1270     /*
1271      * Read input as needed...
1272      */
1273
1274      while (fgets(line, sizeof(line), pfile) != NULL)
1275	{
1276	  char *tmp_ptr;
1277	  if ((tmp_ptr = strchr(line, '\n')))
1278	    tmp_ptr[0] = '\0';
1279	  if ((tmp_ptr = strchr(line, '\r')))
1280	    tmp_ptr[0] = '\0';
1281	  if (strlen(line) > 0)
1282	    {
1283	      if (!stp_string_list_is_present(stpui_system_print_queues, line))
1284		stp_string_list_add_string(stpui_system_print_queues,
1285					   line, line);
1286	    }
1287	}
1288      pclose(pfile);
1289      if (old_locale)
1290	setenv("LC_ALL", old_locale, 1);
1291      else
1292	unsetenv("LC_ALL");
1293      if (old_lc_messages)
1294	setenv("LC_MESSAGES", old_lc_messages, 1);
1295      else
1296	unsetenv("LC_MESSAGES");
1297      if (old_lang)
1298	setenv("LANG", old_lang, 1);
1299      else
1300	unsetenv("LANG");
1301    }
1302  }
1303}
1304
1305const stpui_plist_t *
1306stpui_get_current_printer(void)
1307{
1308  return &(stpui_plist[stpui_plist_current]);
1309}
1310
1311/*
1312 * 'usr1_handler()' - Make a note when we receive SIGUSR1.
1313 */
1314
1315static volatile int usr1_interrupt;
1316
1317static void
1318usr1_handler (int sig)
1319{
1320  usr1_interrupt = 1;
1321}
1322
1323/*
1324 *
1325 * Process control for actually printing.  Documented 20040821
1326 * by Robert Krawitz.
1327 *
1328 * In addition to the print command itself and the output generator,
1329 * we spawn two additional processes to monitor the print job and clean
1330 * up.  We do this because the GIMP is very unfriendly about how it
1331 * terminates plugins when the user cancels an operation: it sends a
1332 * SIGKILL, which prevents the plugin from cleaning up.  Since the
1333 * plugin is sending data to an lpr process, this SIGKILL closes off
1334 * the input to lpr.  lpr doesn't know that its parent has died
1335 * inappropriately, and happily proceeds to print the partial job.
1336 *
1337 * (The child may not actually be lpr, of course, but we'll just use
1338 * that nomenclature for convenience.)
1339 *
1340 * The first such process is the "lpr monitor".  Its job is to
1341 * watch the parent (the actual data generator).  If its parent dies,
1342 * it kills the print command.  Notice that it must keep the file
1343 * descriptor used to write to the lpr process open, since as soon as
1344 * the last writer to this pipe is closed, the lpr process sees its
1345 * input close.  Therefore, it first kills the child with SIGTERM and
1346 * then closes the pipe.  Perhaps a more robust method would be to
1347 * send a SIGTERM followed by a SIGKILL, but we can worry about that
1348 * later.  The lpr monitor process is killed with SIGUSR1 by the
1349 * master upon successful completion, at which point it exits.  The lpr
1350 * monitor itself detects that the master has finished by periodically
1351 * sending it a kill 0 (a null signal).  When the parent exits, this
1352 * attempt to signal will return failure.  This has a potential race
1353 * condition if another process is created with the same PID between
1354 * checks.  A more robust (but more complicated) solution would involve
1355 * IPC of some kind.
1356 *
1357 * The second such process (the "error monitor") monitors the stderr
1358 * (and stdout) of the lpr process, to send any error messages back
1359 * to the user.  Since the GIMP is normally not launched from a
1360 * terminal window, any errors would get lost.  The error monitor
1361 * captures this output and reports it back.  It stays around until
1362 * its input is closed (normally by the lpr process exiting), at
1363 * which point it reports to the master that it has finished, and the
1364 * master can clean up and return.
1365 *
1366 * The actual master process spawns the lpr monitor, which spawns
1367 * the process that will later run the lpr command, which itself
1368 * spawns the error monitor.
1369 *
1370 * This architecture is perhaps unnecessarily complex; the lpr monitor
1371 * and error monitor could perhaps be combined into a single process
1372 * that watches both for the parent to go away and for the error messages.
1373 *
1374 * The following diagrams illustrate the control flow during the normal
1375 * case and also when the print job is cancelled.  The notation for file
1376 * descriptors is a number prefixed with < for an input file descriptor
1377 * and suffixed > for an output file descriptor.  For example, <0 means
1378 * input file descriptor 0 and 1> means output file descriptor 1.  The
1379 * key to the file descriptors is given below.  A file descriptor named
1380 * x,y refers to file descriptor y duplicated onto file descriptor x.
1381 * So "<0,3" means input file descriptor 3 (pipefd[0]) dup2'ed onto
1382 * file descriptor 0.
1383 *
1384 * fd0 = fd 0
1385 * fd1 = fd 1
1386 * fd2 = fd 2
1387 * fd3 = pipefd[0]
1388 * fd4 = pipefd[1]
1389 * fd5 = syncfd[0]
1390 * fd6 = syncfd[1]
1391 * fd7 = errfd[0]
1392 * fd8 = errfd[1]
1393 *
1394 *
1395 *                            NORMAL CASE
1396 *
1397 * PARENT             CHILD 1              CHILD 2          CHILD 3
1398 * (print generator)  (lpr monitor)        (print command)  (error monitor)
1399 * |
1400 * stpui_print
1401 * |
1402 * | <0 1> 2>
1403 * |
1404 * | pipe(syncfd)
1405 * |
1406 * | <0 1> 2> <5 6>
1407 * |
1408 * | pipe(pipefd)
1409 * |
1410 * | <0 1> 2> <3 4>
1411 * |    <5 6>
1412 * |
1413 * | fork =============|
1414 * |                   |
1415 * | close(63)         | close(syncfd[0])
1416 * |                   | <0 1> 2> <3 4>
1417 * | <0 1> 2> 4> <5    | 6>
1418 * |                   |
1419 * |                   | fork =============|
1420 * |                   |                   |
1421 * |                   | close(01263)      | dup2(pipefd[0], 0)
1422 * |                   |                   | close(pipefd[0]
1423 * |                   | 4>                | close(pipefd[1]
1424 * |                   |                   |
1425 * |                   |                   | 1> 2> <0,3 6>
1426 * |                   |                   |
1427 * |                   |                   | pipe(errfd)
1428 * |                   |                   | 1> 2> <0,3 6>
1429 * |                   |                   | <7 8>
1430 * |                   |                   |
1431 * |                   |                   | fork =============|
1432 * |                   |                   |                   | close(012348)
1433 * |                   |                   | close(12)         | 6> <7
1434 * |                   |                   |                   |
1435 * |                   |                   | <0,3 6> <7 8>     |
1436 * |                   |                   |                   |
1437 * |                   |                   | dup2(errfd[1],1)  |
1438 * |                   |                   | dup2(errfd[1],2)  |
1439 * |                   |                   | close(errfd[1])   |
1440 * |                   |                   | close(pipefd[0])  |
1441 * |                   |                   | close(pipefd[1])  |
1442 * |                   |                   | close(syncfd[1])  |
1443 * |<<<<<<<<<<<<<<<<<<<|kill(0,0)          * EXEC lpr          |
1444 * |>>>>>>>>>>>>>>>>>>>|OK                 | <0,3 1,8> 2,8>    |
1445 * |                   |                   |                   |
1446 * | write>>>>>>>>>>>>>+>>>>>>>>>>>>>>>>>>>|                   |
1447 * |                   |                   | write(2,8)??>>>>>>|read(<7)->warn
1448 * |                   |                   |                   |
1449 * | close(4)>>>>>>>>>>+>>>>>>>>>>>>>>>>>>>|                   |
1450 * | <0 1> 2> <5       |                   |                   |
1451 * | kill>>>>>>>>>>>>>>|                   |                   |
1452 * |                   | close(4)>>>>>>>>>>| eof(<0,3)         |
1453 * |                   |                   |                   |
1454 * |                   | *no open fd*      | 1,8> 2,8>         |
1455 * |                   | exit              |                   |
1456 * |                   |                   | exit>>>>>>>>>>>>>>| eof(<7)
1457 * | wait<<<<<<<<<<<<<<X                   X                   | 6>
1458 * |                                                           |
1459 * | read(<5)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<| write(6>)
1460 * |                                                           |
1461 * | close(5)                                                  | exit
1462 * |                                                           X
1463 * | 0< 1> 2>
1464 * |
1465 * | return
1466 * X
1467 *
1468 *
1469 *                            ERROR CASE (job cancelled)
1470 *
1471 * PARENT             CHILD 1              CHILD 2          CHILD 3
1472 * (print generator)  (lpr monitor)        (print command)  (error monitor)
1473 * |
1474 * stpui_print
1475 * |
1476 * | <0 1> 2>
1477 * |
1478 * | pipe(syncfd)
1479 * |
1480 * | <0 1> 2> <5 6>
1481 * |
1482 * | pipe(pipefd)
1483 * |
1484 * | <0 1> 2> <3 4>
1485 * |    <5 6>
1486 * |
1487 * | fork =============|
1488 * |                   |
1489 * | close(63)         | close(syncfd[0]
1490 * |                   | <0 1> 2> <3 4>
1491 * | <0 1> 2> 4> <5    | 6>
1492 * |                   |
1493 * |                   | fork =============|
1494 * |                   |                   |
1495 * |                   | close(01263)      | dup2(pipefd[0], 0)
1496 * |                   |                   | close(pipefd[0]
1497 * |                   | 4>                | close(pipefd[1]
1498 * |                   |                   |
1499 * |                   |                   | 1> 2> <3,0 6>
1500 * |                   |                   |
1501 * |                   |                   | pipe(errfd)
1502 * |                   |                   | 1> 2> <3,0 6>
1503 * |                   |                   | <7 8>
1504 * |                   |                   |
1505 * |                   |                   | fork =============|
1506 * |                   |                   |                   | close(012348)
1507 * |                   |                   | close(12)         | 6> <7
1508 * |                   |                   |                   |
1509 * |                   |                   | <3,0 6> <7 8>     |
1510 * |                   |                   |                   |
1511 * |                   |                   | dup2(errfd[1],1)  |
1512 * |                   |                   | dup2(errfd[1],2)  |
1513 * |                   |                   | close(3468)       |
1514 * |<<<<<<<<<<<<<<<<<<<|kill(0,0)          * EXEC lpr          |
1515 * |>>>>>>>>>>>>>>>>>>>|OK                 | <0,3 1,8> 2,8>    |
1516 * |                   |                   |                   |
1517 * | write>>>>>>>>>>>>>+>>>>>>>>>>>>>>>>>>>|                   |
1518 * |                   |                   | write(2,8)??>>>>>>|read(<7)->warn
1519 * | KILLED            |                   |                   |
1520 * | close(01245)>>>>>>+>>>>>>>>>>>>>>>>>>>|                   |
1521 * X                   |                   |                   |
1522 *  <<<<<<<<<<<<<<<<<<<|kill(0,0)          |                   |
1523 *  >>>>>>>>>>>>>>>>>>>|DEAD!              |                   |
1524 *                     |kill(2)>>>>>>>>>>>>|Terminated         |
1525 *                     |close(4>)>>>>>>>>>>|eof(0,3)           |
1526 *                     |                   | 1,8> 2,8>         |
1527 *                     | *no open fd*      |exit>>>>>>>>>>>>>>>|eof(7)
1528 *                     | exit              X                   |
1529 *                     X                                       | 6>
1530 *                                                            <| write(6)
1531 *                                                             | exit/SIGPIPE
1532 *                                                             X
1533 */
1534
1535int
1536stpui_print(const stpui_plist_t *printer, stpui_image_t *image)
1537{
1538  int		ppid = getpid (), /* PID of plugin */
1539		opid,		/* PID of output process */
1540		cpid = 0,	/* PID of control/monitor process */
1541		pipefd[2],	/* Fds of the pipe connecting all the above */
1542		errfd[2],	/* Message logger from lp command */
1543		syncfd[2];	/* Sync the logger */
1544  FILE		*prn = NULL;	/* Print file/command */
1545  int		do_sync = 0;
1546  int		print_status = 0;
1547  int		dummy;
1548
1549  /*
1550   * Open the file/execute the print command...
1551   */
1552
1553  if (stpui_plist_get_command_type(printer) == COMMAND_TYPE_DEFAULT ||
1554      stpui_plist_get_command_type(printer) == COMMAND_TYPE_CUSTOM)
1555    {
1556      /*
1557       * The following IPC code is only necessary because the GIMP kills
1558       * plugins with SIGKILL if its "Cancel" button is pressed; this
1559       * gives the plugin no chance whatsoever to clean up after itself.
1560       */
1561      do_sync = 1;
1562      usr1_interrupt = 0;
1563      signal (SIGUSR1, usr1_handler);
1564      if (pipe (syncfd) != 0)
1565	{
1566	  do_sync = 0;
1567	}
1568      if (pipe (pipefd) != 0)
1569	{
1570	  prn = NULL;
1571	}
1572      else
1573	{
1574	  cpid = fork ();
1575	  if (cpid < 0)		/* Error */
1576	    {
1577	      do_sync = 0;
1578	      prn = NULL;
1579	    }
1580	  else if (cpid == 0)	/* Child 1 (lpr monitor and printer command) */
1581	    {
1582	      /* LPR monitor process.  Printer output is piped to us. */
1583	      close(syncfd[0]);
1584	      opid = fork ();
1585	      if (opid < 0)
1586		{
1587		  /* Errors will cause the plugin to get a SIGPIPE.  */
1588		  exit (1);
1589		}
1590	      else if (opid == 0) /* Child 2 (printer command) */
1591		{
1592		  dup2 (pipefd[0], 0);
1593		  close (pipefd[0]);
1594		  close (pipefd[1]);
1595		  if (pipe(errfd) == 0)
1596		    {
1597		      opid = fork();
1598		      if (opid < 0)
1599			_exit(1);
1600		      else if (opid == 0) /* Child 3 (monitors stderr) */
1601			{
1602			  stp_outfunc_t errfunc = stpui_get_errfunc();
1603			  void *errdata = stpui_get_errdata();
1604			  /* calls g_message on anything it sees */
1605			  char buf[4096];
1606
1607			  close (pipefd[0]);
1608			  close (pipefd[1]);
1609			  close (0);
1610			  close (1);
1611			  close (2);
1612			  close (errfd[1]);
1613			  while (1)
1614			    {
1615			      ssize_t bytes = read(errfd[0], buf, 4095);
1616			      if (bytes > 0)
1617				{
1618				  buf[bytes] = '\0';
1619				  (*errfunc)(errdata, buf, bytes);
1620				}
1621			      else
1622				{
1623				  if (bytes < 0)
1624				    {
1625				      snprintf(buf, 4095,
1626					       "Read messages failed: %s\n",
1627					       strerror(errno));
1628				      (*errfunc)(errdata, buf, strlen(buf));
1629				    }
1630				  write(syncfd[1], "Done", 5);
1631				  _exit(0);
1632				}
1633			    }
1634			  write(syncfd[1], "Done", 5);
1635			  _exit(0);
1636			}
1637		      else	/* Child 2 (printer command) */
1638			{
1639			  char *command;
1640			  char *locale;
1641			  if (stpui_plist_get_command_type(printer) ==
1642			      COMMAND_TYPE_DEFAULT)
1643			    {
1644			      command =
1645				stpui_build_standard_print_command
1646				(printer, stp_get_printer(printer->v));
1647			      append_external_options(&command, printer->v);
1648			    }
1649			  else
1650			    command =
1651			      (char *) stpui_plist_get_custom_command(printer);
1652			  (void) close(2);
1653			  (void) close(1);
1654			  dup2 (errfd[1], 2);
1655			  dup2 (errfd[1], 1);
1656			  close(errfd[1]);
1657			  close (pipefd[0]);
1658			  close (pipefd[1]);
1659			  close(syncfd[1]);
1660#ifdef HAVE_LOCALE_H
1661			  locale = g_strdup(setlocale(LC_NUMERIC, NULL));
1662			  setlocale(LC_NUMERIC, "C");
1663#endif
1664			  execl("/bin/sh", "/bin/sh", "-c", command, NULL);
1665			  /* NOTREACHED */
1666			  _exit (1);
1667			}
1668		      /* NOTREACHED */
1669		      _exit(1);
1670		    }
1671		  else		/* pipe() failed! */
1672		    {
1673		      _exit(1);
1674		    }
1675		}
1676	      else		/* Child 1 (lpr monitor) */
1677		{
1678		  /*
1679		   * If the print plugin gets SIGKILLed by gimp, we kill lpr
1680		   * in turn.  If the plugin signals us with SIGUSR1 that it's
1681		   * finished printing normally, we close our end of the pipe,
1682		   * and go away.
1683		   *
1684		   * Note that we keep pipefd[1] -- which is the pipe from
1685		   * the print plugin to the lpr process -- open during this.
1686		   * If we don't, and the parent gets killed, lpr will notice
1687		   * its stdin getting closed off and start printing.
1688		   * This way its stdin stays open until we kill it.
1689		   */
1690		  close (0);
1691		  close (1);
1692		  close (2);
1693		  close (syncfd[1]);
1694		  close (pipefd[0]);
1695		  while (usr1_interrupt == 0)
1696		    {
1697		      /*
1698		       * Note potential race condition, if some other process
1699		       * happens to get the same pid!
1700		       */
1701		      if (kill (ppid, 0) < 0)
1702			{
1703			  /*
1704			   * The print plugin has been killed!
1705			   * Note that there is no possibility of the print
1706			   * job sending us a SIGUSR1 and then exiting;
1707			   * the parent (print plugin) stays around after
1708			   * sending us the SIGUSR1, and then waits
1709			   * for us to die.
1710			   */
1711			  kill (opid, SIGTERM);
1712			  waitpid (opid, &dummy, 0);
1713			  close (pipefd[1]);
1714			  /*
1715			   * We do not want to allow cleanup before exiting.
1716			   * The exiting parent has already closed the
1717			   * connection  to the X server; if we try to clean
1718			   * up, we'll notice that fact and complain.
1719			   */
1720			  _exit (0);
1721			}
1722		      sleep (5);
1723		    }
1724		  /* We got SIGUSR1.  */
1725		  close (pipefd[1]);
1726		  /*
1727		   * We do not want to allow cleanup before exiting.
1728		   * The exiting parent has already closed the connection
1729		   * to the X server; if we try to clean up, we'll notice
1730		   * that fact and complain.
1731		   */
1732		  _exit (0);
1733		}
1734	    }
1735	  else			/* Parent (actually generates the output) */
1736	    {
1737	      close (syncfd[1]);
1738	      close (pipefd[0]);
1739	      /* Parent process.  We generate the printer output. */
1740	      prn = fdopen (pipefd[1], "w");
1741	      /* and fall through... */
1742	    }
1743	}
1744    }
1745  else
1746    prn = fopen (stpui_plist_get_output_filename(printer), "wb");
1747
1748  if (prn != NULL)
1749    {
1750      char tmp[32];
1751      stpui_plist_t *np = allocate_stpui_plist_copy(printer);
1752      const stp_vars_t *current_vars =
1753	stp_printer_get_defaults(stp_get_printer(np->v));
1754      int orientation;
1755      stp_merge_printvars(np->v, current_vars);
1756      stp_set_string_parameter(np->v, "InputImageType", image_type);
1757      if (image_raw_channels)
1758	{
1759	  sprintf(tmp, "%d", image_raw_channels);
1760	  stp_set_string_parameter(np->v, "RawChannels", tmp);
1761	}
1762      sprintf(tmp, "%d", image_channel_depth);
1763      stp_set_string_parameter(np->v, "ChannelBitDepth", tmp);
1764
1765      /*
1766       * Set up the orientation
1767       */
1768      orientation = np->orientation;
1769      if (orientation == ORIENT_AUTO)
1770	orientation = stpui_compute_orientation();
1771      switch (orientation)
1772	{
1773	case ORIENT_PORTRAIT:
1774	  break;
1775	case ORIENT_LANDSCAPE:
1776	  if (image->rotate_cw)
1777	    (image->rotate_cw)(image);
1778	  break;
1779	case ORIENT_UPSIDEDOWN:
1780	  if (image->rotate_180)
1781	    (image->rotate_180)(image);
1782	  break;
1783	case ORIENT_SEASCAPE:
1784	  if (image->rotate_ccw)
1785	    (image->rotate_ccw)(image);
1786	  break;
1787	}
1788
1789      /*
1790       * Finally, call the print driver to send the image to the printer
1791       * and close the output file/command...
1792       */
1793
1794      stp_set_outfunc(np->v, writefunc);
1795      stp_set_errfunc(np->v, stpui_get_errfunc());
1796      stp_set_outdata(np->v, prn);
1797      stp_set_errdata(np->v, stpui_get_errdata());
1798      print_status = stp_print(np->v, &(image->im));
1799
1800      /*
1801       * Note that we do not use popen() to create the output, therefore
1802       * we do not use pclose() to close it.  See bug 1013565.
1803       */
1804      (void) fclose(prn);
1805      if (stpui_plist_get_command_type(printer) == COMMAND_TYPE_DEFAULT ||
1806	  stpui_plist_get_command_type(printer) == COMMAND_TYPE_CUSTOM)
1807	{
1808	  /*
1809	   * It is important for us to first close off the lpr process,
1810	   * then kill the lpr monitor (child 1), and then wait for it
1811	   * to die before exiting.
1812	   */
1813	  kill (cpid, SIGUSR1);
1814	  waitpid (cpid, &dummy, 0);
1815	}
1816
1817      /*
1818       * Make sure that any errors have been reported back to the user
1819       * prior to completion.  In addition, explicitly close off the
1820       * synchronization file descriptor since we're merely returning,
1821       * not exiting, and don't want to leave any pollution around.
1822       */
1823      if (do_sync)
1824	{
1825	  char buf[8];
1826	  (void) read(syncfd[0], buf, 8);
1827	  (void) close(syncfd[0]);
1828	}
1829      stpui_plist_destroy(np);
1830      g_free(np);
1831      return 1;
1832    }
1833  return 0;
1834}
1835
1836/*
1837 * End of "$Id: plist.c,v 1.18 2008/07/04 14:29:28 rlk Exp $".
1838 */
1839