• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   texttotext
3  *
4  *   Filter to print text files on text-only printers. The filter has
5  *   several configuration options controlled by a PPD file so that it
6  *   should work with most printer models.
7  *
8  *   Copyright 2007-2011 by Apple Inc.
9  *   Copyright 1997-2006 by Easy Software Products.
10  *   Copyright 2011-2016 by Till Kamppeter
11  *
12  *   Distribution and use rights are outlined in the file "COPYING"
13  *   which should have been included with this file.
14  */
15 
16 /*
17  * Include necessary headers...
18  */
19 
20 #include <config.h>
21 #include <cups/cups.h>
22 #include <cups/ppd.h>
23 #include <cups/file.h>
24 #include <signal.h>
25 #include <sys/wait.h>
26 #include <limits.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <iconv.h>
33 #include <cupsfilters/image-private.h>
34 
35 /*
36  * Type definitions
37  */
38 
39 typedef enum overlong_line_e {
40   TRUNCATE = 0,
41   WORDWRAP = 1,
42   WRAPATWIDTH = 2
43 } overlong_line_t;
44 
45 typedef enum newline_char_e {
46   LF = 0,
47   CR = 1,
48   CRLF = 2
49 } newline_char_t;
50 
51 
52 /*
53  * Local functions...
54  */
55 
56 static int              is_true(const char *value);
57 static int              is_false(const char *value);
58 static int		check_range(char *page_ranges, int even_pages,
59 				    int odd_pages, int page);
60 static void		cancel_job(int sig);
61 
62 
63 /*
64  * Local globals...
65  */
66 
67 static int		job_canceled = 0;
68 
69 
70 /*
71  * 'main()' - Main entry for filter...
72  */
73 
74 int					/* O - Exit status */
main(int argc,char * argv[])75 main(int  argc,				/* I - Number of command-line args */
76      char *argv[])			/* I - Command-line arguments */
77 {
78   int		i, j;			/* Looping vars */
79   char          *p;
80   int		exit_status = 0;	/* Exit status */
81   int		fd = 0;			/* Copy file descriptor */
82   char		*filename,		/* Text file to convert */
83 		tempfile[1024];		/* Temporary file */
84   char		buffer[8192];		/* Copy buffer */
85   int		bytes;			/* Bytes copied */
86   int           num_copies;             /* Number of copies */
87   ppd_file_t	*ppd;			/* PPD file */
88   int		num_options;		/* Number of options */
89   cups_option_t	*options;		/* Options */
90   const char	*val, *val2;			/* Option value */
91   ppd_attr_t    *ppd_attr;              /* Attribute taken from the PPD */
92   int           num_lines = 66,         /* Lines per page */
93                 num_columns = 80;       /* Characters per line */
94   int           page_left = 0,          /* Page margins */
95                 page_right = 0,
96                 page_top = 0,
97                 page_bottom = 0;
98   int           text_width,             /* Width of the text area on the page */
99                 text_height;            /* Height of the text area on the
100 					   page */
101   char          encoding[64];           /* The printer'a encoding, to which
102 					   the incoming UTF-8 is converted,
103 					   must be 1-byte-per-character */
104   overlong_line_t overlong_lines = WRAPATWIDTH;
105                                         /* How to treat overlong lines */
106   int           tab_width = 8;          /* Turn tabs to spaces with given
107 					   width */
108   int           pagination = 1;         /* Paginate text to allow margins
109                                            and page management */
110   int           send_ff = 1;            /* Send form-feed char at the end of
111 					   each page */
112   newline_char_t newline_char = CRLF;   /* Character to send at end of line */
113   char          *newline_char_str;
114   char          *page_ranges = NULL;    /* Selection of pages to print */
115   int           even_pages = 1;         /* Print the even pages */
116   int           odd_pages = 1;          /* Print the odd pages */
117   int           reverse_order = 0;      /* Ouput pages in reverse order? */
118   int           collate = 1;            /* Collate multiple copies? */
119   int           page_size;              /* Number of bytes needed for a page,
120 					   to allocate the memory for the
121 					   output page buffer */
122   char          *out_page = NULL;       /* Output page buffer */
123   cups_array_t  *page_array = NULL;     /* Array to hold the output pages
124 					   for collated copies and reverse
125 					   output order */
126   iconv_t       cd;                     /* Conversion descriptor, describes
127 					   between which encodings iconv
128 					   should convert */
129   char          outbuf[4096];           /* Output buffer for iconv */
130   char          inbuf[2048];            /* Input buffer for iconv */
131   size_t        outsize;                /* Space left in outbuf */
132   size_t        insize = 0;             /* Bytes still to be converted in
133 					   inbuf */
134   char          *outptr = outbuf;       /* Pointer for next converted
135 					   character to be dropped */
136   char          *inptr = inbuf;         /* Pointer to next character to be
137 					   converted */
138   size_t        nread;                  /* Number of bytes read from file */
139   size_t        nconv;                  /* -1 on conversion error */
140   int           incomplete_char = 0;    /* Was last character in input buffer
141 					   incomplete */
142   int           result = 0;             /* Conversion result (-1 on error) */
143   char          *procptr,               /* Pointer into conversion output
144                                            to indicate next byte to process
145 					   for output page creation */
146                 *destptr;               /* Pointer into output page buffer
147                                            where next character will be put */
148   int           page,                   /* Number of current output page */
149                 line, column;           /* Character coordiantes on output
150 					   page */
151   int           page_empty;             /* Is the current output page still
152 					   empty (no visible characters)? */
153   int           previous_is_cr;         /* Is the previous character processed
154 					   a Carriage Return? */
155   int           skip_rest_of_line = 0;  /* Are we truncating an overlong
156 					   line? */
157   int           skip_spaces = 0;        /* Are we skipping spaces at a line
158 					   break when word-wrapping? */
159   char          *wrapped_word = NULL;   /* Section of a word wrapped over
160 					   to the next line */
161   int           num_pages = 0;          /* Number of pages which get actually
162 					   printed */
163 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
164   struct sigaction action;		/* Actions for POSIX signals */
165 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
166 
167  /*
168   * Make sure status messages are not buffered...
169   */
170 
171   setbuf(stderr, NULL);
172 
173  /*
174   * Ignore broken pipe signals...
175   */
176 
177   signal(SIGPIPE, SIG_IGN);
178 
179  /*
180   * Make sure we have the right number of arguments for CUPS!
181   */
182 
183   if (argc < 6 || argc > 7)
184   {
185     fprintf(stderr, "Usage: %s job user title copies options [file]\n",
186 	    argv[0]);
187     return (1);
188   }
189 
190  /*
191   * Register a signal handler to cleanly cancel a job.
192   */
193 
194 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
195   sigset(SIGTERM, cancel_job);
196 #elif defined(HAVE_SIGACTION)
197   memset(&action, 0, sizeof(action));
198 
199   sigemptyset(&action.sa_mask);
200   action.sa_handler = cancel_job;
201   sigaction(SIGTERM, &action, NULL);
202 #else
203   signal(SIGTERM, cancel_job);
204 #endif /* HAVE_SIGSET */
205 
206  /*
207   * Copy stdin if needed...
208   */
209 
210   if (argc == 6)
211   {
212    /*
213     * Copy stdin to a temp file...
214     */
215 
216     if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
217     {
218       fprintf(stderr, "ERROR: Unable to copy input text file");
219       goto error;
220     }
221 
222     fprintf(stderr, "DEBUG: sys5ippprinter - copying to temp print file \"%s\"\n",
223             tempfile);
224 
225     while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
226       bytes = write(fd, buffer, bytes);
227 
228     close(fd);
229 
230     filename = tempfile;
231   }
232   else
233   {
234    /*
235     * Use the filename on the command-line...
236     */
237 
238     filename    = argv[6];
239     tempfile[0] = '\0';
240   }
241 
242  /*
243   * Number of copies
244   */
245 
246   num_copies = atoi(argv[4]);
247   if (num_copies < 1)
248     num_copies = 1;
249 
250  /*
251   * Get the options from the fifth command line argument
252   */
253 
254   num_options = cupsParseOptions(argv[5], 0, &options);
255 
256  /*
257   * Load the PPD file and mark options...
258   */
259 
260   ppd = ppdOpenFile(getenv("PPD"));
261   if (ppd)
262   {
263     ppdMarkDefaults(ppd);
264     cupsMarkOptions(ppd, num_options, options);
265   }
266 
267  /*
268   * Parse the options
269   */
270 
271   /* With the "PageSize"/"PageRegion" options we only determine the number
272      of lines and columns of a page, we do not use the geometry defined by
273      "PaperDimension" and "ImageableArea" in the PPD */
274   if ((val = cupsGetOption("PageSize", num_options, options)) != NULL ||
275       (val = cupsGetOption("PageRegion", num_options, options)) != NULL ||
276       (ppd_attr = ppdFindAttr(ppd, "DefaultPageSize", NULL)) != NULL ||
277       (ppd_attr = ppdFindAttr(ppd, "DefaultPageRegion", NULL)) != NULL) {
278     if (val == NULL)
279       val = ppd_attr->value;
280     fprintf(stderr, "DEBUG: PageSize: %s\n", val);
281     snprintf(buffer, sizeof(buffer), "Default%sNumLines", val);
282     if ((val2 = cupsGetOption(buffer + 7, num_options, options)) != NULL ||
283 	(ppd_attr = ppdFindAttr(ppd, buffer, NULL)) != NULL) {
284       if (val2 == NULL)
285 	val2 = ppd_attr->value;
286       if (!strncasecmp(val2, "Custom.", 7))
287 	val2 += 7;
288       num_lines = atoi(val2);
289     }
290     snprintf(buffer, sizeof(buffer), "Default%sNumColumns", val);
291     if ((val2 = cupsGetOption(buffer + 7, num_options, options)) != NULL ||
292 	(ppd_attr = ppdFindAttr(ppd, buffer, NULL)) != NULL) {
293       if (val2 == NULL)
294 	val2 = ppd_attr->value;
295       if (!strncasecmp(val2, "Custom.", 7))
296 	val2 += 7;
297       num_columns = atoi(val2);
298     }
299     if (num_lines <= 0) {
300       fprintf(stderr, "DEBUG: Invalid number of lines %d, using default: 66\n",
301 	      num_lines);
302       num_lines = 66;
303     }
304     if (num_columns <= 0) {
305       fprintf(stderr, "DEBUG: Invalid number of columns %d, using default: 80\n",
306 	      num_columns);
307       num_columns = 80;
308     }
309   }
310 
311   /* Direct specification of number of lines/columns, mainly for debugging
312      and development */
313   if ((val = cupsGetOption("page-height", num_options, options)) != NULL) {
314     i = atoi(val);
315     if (i > 0)
316       num_lines = i;
317     else
318       fprintf(stderr, "DEBUG: Invalid number of lines %d, using default value: %d\n",
319 	      i, num_lines);
320   }
321   if ((val = cupsGetOption("page-width", num_options, options)) != NULL) {
322     i = atoi(val);
323     if (i > 0)
324       num_columns = i;
325     else
326       fprintf(stderr, "DEBUG: Invalid number of columns %d, using default value: %d\n",
327 	      i, num_columns);
328   }
329 
330   fprintf(stderr, "DEBUG: Lines per page: %d; Characters per line: %d\n",
331 	  num_lines, num_columns);
332 
333   if ((val = cupsGetOption("page-left", num_options, options)) != NULL ||
334       (ppd_attr = ppdFindAttr(ppd, "Defaultpage-left", NULL)) != NULL) {
335     if (val == NULL)
336       val = ppd_attr->value;
337     if (!strncasecmp(val, "Custom.", 7))
338       val += 7;
339     page_left = atoi(val);
340     if (page_left < 0 || page_left > num_columns - 1) {
341       fprintf(stderr, "DEBUG: Invalid left margin %d, setting to 0.\n",
342 	      page_left);
343       page_left = 0;
344     }
345   }
346   if ((val = cupsGetOption("page-right", num_options, options)) != NULL ||
347       (ppd_attr = ppdFindAttr(ppd, "Defaultpage-right", NULL)) != NULL) {
348     if (val == NULL)
349       val = ppd_attr->value;
350     if (!strncasecmp(val, "Custom.", 7))
351       val += 7;
352     page_right = atoi(val);
353     if (page_right < 0 || page_right > num_columns - page_left - 1) {
354       fprintf(stderr, "DEBUG: Invalid right margin %d, setting to 0.\n",
355 	      page_right);
356       page_right = 0;
357     }
358   }
359   if ((val = cupsGetOption("page-top", num_options, options)) != NULL ||
360       (ppd_attr = ppdFindAttr(ppd, "Defaultpage-top", NULL)) != NULL) {
361     if (val == NULL)
362       val = ppd_attr->value;
363     if (!strncasecmp(val, "Custom.", 7))
364       val += 7;
365     page_top = atoi(val);
366     if (page_top < 0 || page_top > num_lines - 1) {
367       fprintf(stderr, "DEBUG: Invalid top margin %d, setting to 0.\n",
368 	      page_top);
369       page_top = 0;
370     }
371   }
372   if ((val = cupsGetOption("page-bottom", num_options, options)) != NULL ||
373       (ppd_attr = ppdFindAttr(ppd, "Defaultpage-bottom", NULL)) != NULL) {
374     if (val == NULL)
375       val = ppd_attr->value;
376     if (!strncasecmp(val, "Custom.", 7))
377       val += 7;
378     page_bottom = atoi(val);
379     if (page_bottom < 0 || page_bottom > num_lines - page_top - 1) {
380       fprintf(stderr, "DEBUG: Invalid bottom margin %d, setting to 0.\n",
381 	      page_bottom);
382       page_bottom = 0;
383     }
384   }
385   fprintf(stderr, "DEBUG: Margins: Left (Columns): %d; Right (Columns): %d; Top (Lines): %d; Bottom (Lines): %d\n",
386 	  page_left, page_right, page_top, page_bottom);
387 
388   text_width = num_columns - page_left - page_right;
389   text_height = num_lines - page_top - page_bottom;
390   fprintf(stderr, "DEBUG: Text area: Lines per page: %d; Characters per line: %d\n",
391 	  text_height, text_width);
392 
393   strcpy(encoding, "ASCII//IGNORE");
394   if ((val = cupsGetOption("PrinterEncoding", num_options, options)) != NULL ||
395       (ppd_attr = ppdFindAttr(ppd, "DefaultPrinterEncoding", NULL)) != NULL) {
396     if (val == NULL)
397       val = ppd_attr->value;
398     if (!strncasecmp(val, "Custom.", 7))
399       val += 7;
400     if (val[0] != '\0') {
401       snprintf(encoding, sizeof(encoding), "%s//IGNORE", val);
402       for (p = encoding; *p; p ++)
403 	*p = toupper(*p);
404     }
405   }
406   fprintf(stderr, "DEBUG: Output encoding: %s\n", encoding);
407 
408   if ((val = cupsGetOption("OverlongLines", num_options, options)) != NULL ||
409       (ppd_attr = ppdFindAttr(ppd, "DefaultOverlongLines", NULL)) != NULL) {
410     if (val == NULL)
411       val = ppd_attr->value;
412     if (!strcasecmp(val, "Truncate"))
413       overlong_lines = TRUNCATE;
414     else if (!strcasecmp(val, "WordWrap"))
415       overlong_lines = WORDWRAP;
416     else if (!strcasecmp(val, "WrapAtWidth"))
417       overlong_lines = WRAPATWIDTH;
418     else
419       fprintf(stderr, "DEBUG: Invalid value for OverlongLines: %s, using default value.\n",
420 	      val);
421   }
422   fprintf(stderr, "DEBUG: Handling of overlong lines: %s\n",
423 	  (overlong_lines == TRUNCATE ? "Truncate at maximum width" :
424 	   (overlong_lines == WORDWRAP ? "Word-wrap" :
425 	    "Wrap exactly at maximum width")));
426 
427   if ((val = cupsGetOption("TabWidth", num_options, options)) != NULL ||
428       (ppd_attr = ppdFindAttr(ppd, "DefaultTabWidth", NULL)) != NULL) {
429     if (val == NULL)
430       val = ppd_attr->value;
431     if (!strncasecmp(val, "Custom.", 7))
432       val += 7;
433     i = atoi(val);
434     if (i > 0)
435       tab_width = i;
436     else
437       fprintf(stderr, "DEBUG: Invalid tab width %d, using default value: %d\n",
438 	      i, tab_width);
439   }
440   fprintf(stderr, "DEBUG: Tab width: %d\n", tab_width);
441 
442   if ((val = cupsGetOption("Pagination", num_options, options)) != NULL ||
443       (ppd_attr = ppdFindAttr(ppd, "DefaultPagination", NULL)) != NULL) {
444     if (val == NULL)
445       val = ppd_attr->value;
446     if (is_true(val))
447       pagination = 1;
448     else if (is_false(val))
449       pagination = 0;
450     else
451       fprintf(stderr, "DEBUG: Invalid value for Pagination: %s, using default value.\n",
452 	      val);
453   }
454   fprintf(stderr, "DEBUG: Pagination (Print in defined pages): %s\n",
455 	  (pagination ? "Yes" : "No"));
456 
457   if ((val = cupsGetOption("SendFF", num_options, options)) != NULL ||
458       (ppd_attr = ppdFindAttr(ppd, "DefaultSendFF", NULL)) != NULL) {
459     if (val == NULL)
460       val = ppd_attr->value;
461     if (is_true(val))
462       send_ff = 1;
463     else if (is_false(val))
464       send_ff = 0;
465     else
466       fprintf(stderr, "DEBUG: Invalid value for SendFF: %s, using default value.\n",
467 	      val);
468   }
469   fprintf(stderr, "DEBUG: Send Form Feed character at end of page: %s\n",
470 	  (send_ff ? "Yes" : "No"));
471 
472   if ((val = cupsGetOption("NewlineCharacters", num_options, options)) !=
473       NULL ||
474       (ppd_attr = ppdFindAttr(ppd, "DefaultNewlineCharacters", NULL)) != NULL) {
475     if (val == NULL)
476       val = ppd_attr->value;
477     if (!strcasecmp(val, "LF"))
478       newline_char = LF;
479     else if (!strcasecmp(val, "CR"))
480       newline_char = CR;
481     else if (!strcasecmp(val, "CRLF"))
482       newline_char = CRLF;
483     else
484       fprintf(stderr, "DEBUG: Invalid value for NewlineCharacters: %s, using default value.\n",
485 	      val);
486   }
487   fprintf(stderr, "DEBUG: Characters sent to make printer start a new line: %s\n",
488 	  (newline_char == LF ? "Line Feed (LF)" :
489 	   (newline_char == CR ? "Carriage Return (CR)" :
490 	    "Carriage Return (CR) and Line Feed (LF)")));
491 
492   if ((val = cupsGetOption("page-ranges", num_options, options)) !=
493       NULL) {
494     if (val[0] != '\0')
495       page_ranges = strdup(val);
496   }
497   if (page_ranges)
498     fprintf(stderr, "DEBUG: Page selection: %s\n", page_ranges);
499 
500   if ((val = cupsGetOption("page-set", num_options, options)) !=
501       NULL) {
502     if (!strcasecmp(val, "even")) {
503       even_pages = 1;
504       odd_pages = 0;
505     } else if (!strcasecmp(val, "odd")) {
506       even_pages = 0;
507       odd_pages = 1;
508     } else if (!strcasecmp(val, "all")) {
509       even_pages = 1;
510       odd_pages = 1;
511     } else
512       fprintf(stderr, "DEBUG: Invalid value for page-set: %s, using default value.\n",
513 	      val);
514   }
515   if (!even_pages || !odd_pages)
516     fprintf(stderr, "DEBUG: Print %s.\n",
517 	    (even_pages ? "only the even pages" :
518 	     (odd_pages ? "only the odd pages" :
519 	      "no pages")));
520 
521   if ((val = cupsGetOption("output-order", num_options, options)) !=
522       NULL) {
523     if (!strcasecmp(val, "reverse"))
524       reverse_order = 1;
525     else
526       fprintf(stderr, "DEBUG: Invalid value for OutputOrder: %s, using default value.\n",
527 	      val);
528   }
529   fprintf(stderr, "DEBUG: Print pages in reverse order: %s\n",
530 	  (reverse_order ? "Yes" : "No"));
531 
532   if ((val = cupsGetOption("Collate", num_options, options)) != NULL) {
533     if (is_true(val))
534       collate = 1;
535     else if (is_false(val))
536       collate = 0;
537     else
538       fprintf(stderr, "DEBUG: Invalid value for Collate: %s, using default value.\n",
539 	      val);
540   }
541   fprintf(stderr, "DEBUG: Collate copies: %s\n",
542 	  (collate ? "Yes" : "No"));
543 
544   /* Create a string to insert as the newline mark */
545   if (newline_char == LF)
546     newline_char_str = "\n";
547   else if (newline_char == CR)
548     newline_char_str = "\r";
549   else if (newline_char == CRLF)
550     newline_char_str = "\r\n";
551 
552   /* Size of the output page in bytes (only 1-byte-per-character supported)
553      in the worst case, no blank lines at top and bottom, no blank right
554      margin, 2 characters for newline (CR + LF) plus form feed plus closing
555      zero */
556   page_size = (num_columns + 2) * num_lines + 2;
557 
558   /* Allocate output page buffer */
559   out_page = calloc(page_size, sizeof(char));
560 
561   /* Set conversion mode of iconv */
562   cd = iconv_open(encoding, "UTF-8");
563   if (cd == (iconv_t) -1) {
564     /* Something went wrong.  */
565     if (errno == EINVAL)
566       fprintf(stderr, "ERROR: Conversion from UTF-8 to %s not available\n",
567 	      encoding);
568     else
569       fprintf(stderr, "ERROR: Error setting up conversion from UTF-8 to %s\n",
570 	      encoding);
571     goto error;
572   }
573 
574   /* Open the input file */
575   fd = open(filename, O_RDONLY);
576   if (fd < 0) {
577     fprintf(stderr, "ERROR: Unable to open input text file %s\n", filename);
578     goto error;
579   }
580 
581   /* Create an array to hold the output pages for collated copies or
582      reverse output order (only when printing paginated) */
583   if (pagination &&
584       ((num_copies != 1 && collate) || reverse_order)) {
585     /* Create page array */
586     page_array = cupsArrayNew(NULL, NULL);
587   }
588 
589   /* Main loop for readin the input file, converting the encoding, formatting
590      the output, and printing the pages */
591   destptr = out_page;
592   page_empty = 1;
593   page = 1;
594   line = 0;
595   column = 0;
596   previous_is_cr = 0;
597   insize = 0;
598   do {
599     /* Reset input pointer */
600     inptr = inbuf;
601 
602     /* Mark the output buffer empty */
603     outsize = sizeof(outbuf);
604     outptr = outbuf;
605 
606     /* Read from the input file */
607     nread = read (fd, inbuf + insize, sizeof (inbuf) - insize);
608     if (nread == 0) {
609       /* When we come here the file is completely read.
610 	 This still could mean there are some unused
611 	 characters in the inbuf, meaning that the file
612          ends with an incomplete UTF-8 character. Log
613          this fact. */
614       if (insize > 0 && incomplete_char)
615 	fprintf(stderr, "DEBUG: Input text file ends with incomplete UTF-8 character sequence, file possibly incomplete, but printing the successfully read part anyway.\n");
616 
617       /* Now write out the byte sequence to get into the
618 	 initial state if this is necessary.  */
619       iconv (cd, NULL, NULL, &outptr, &outsize);
620     }
621 
622     insize += nread;
623 
624     /* Convert the incoming UTF-8-encoded text to the printer's encoding */
625     if (insize > 0) { /* Do we have data to convert? */
626       /* Do the conversion.  */
627       nconv = iconv (cd, &inptr, &insize, &outptr, &outsize);
628       if (nconv == (size_t) -1) {
629 	/* Not everything went right. It might only be
630 	   an unfinished byte sequence at the end of the
631 	   buffer. Or it is a real problem. */
632 	if (errno == EINVAL || errno == E2BIG) {
633 	  /* This is harmless.  Simply move the unused
634 	     bytes to the beginning of the buffer so that
635 	     they can be used in the next round. */
636 	  if (errno == EINVAL)
637 	    incomplete_char = 1;
638 	  memmove (inbuf, inptr, insize);
639 	} else {
640 	  /* We found an illegal UTF-8 byte sequence here,
641 	     so error out at this point. */
642 	  fprintf(stderr, "ERROR: Illegal UTF-8 sequence found. Input file perhaps not UTF-8-encoded\n");
643 	  result = -1;
644 	  break;
645 	}
646       }
647     }
648 
649     /* Process the output to generate the output pages */
650     if (outptr == outbuf) /* End of input file */
651       *(outptr ++) = '\0';
652     for (procptr = outbuf; procptr < outptr; procptr ++) {
653       if ((column >= text_width && /* Current line is full */
654 	   *procptr != '\n' && *procptr != '\r' && *procptr != '\f') ||
655 	                           /* Next character is not newline or
656 				      formfeed */
657 	  (outbuf[0] == '\0' && column > 0)) {    /* End of input file */
658 	if (overlong_lines == TRUNCATE && outbuf[0] != '\0')
659 	  skip_rest_of_line = 1;
660 	else {
661 	  if (overlong_lines == WORDWRAP && outbuf[0] != '\0') {
662 	    if (*procptr > ' ') {
663 	      *destptr = '\0';
664 	      for (p = destptr - 1, i = column - 1; *p != ' ' && i >= 0;
665 		   p --, i--);
666 	      if (i >= 0 && i < column - 1) {
667 		wrapped_word = strdup(p + 1);
668 		for (; *p == ' ' && i >= 0; p --, i--);
669 		if (*p != ' ' && i >= 0)
670 		  destptr = p + 1;
671 		else {
672 		  free(wrapped_word);
673 		  wrapped_word = NULL;
674 		}
675 	      }
676 	    } else
677 	      skip_spaces = 1;
678 	  }
679 	  /* Remove trailing whitespace */
680 	  while (destptr > out_page && *(destptr - 1) == ' ')
681 	    destptr --;
682 	  /* Put newline character(s) */
683 	  for (j = 0; newline_char_str[j]; j ++)
684 	    *(destptr ++) = newline_char_str[j];
685 	  /* Position cursor in next line */
686 	  line ++;
687 	  column = 0;
688 	}
689       }
690       if ((line >= text_height && /* Current page is full */
691 	   *procptr != '\f') ||   /* Next character is not formfeed */
692 	  outbuf[0] == '\0' ) {   /* End of input file */
693 	/* Do we actually print this page? */
694 	if (!pagination ||
695 	    check_range(page_ranges, even_pages, odd_pages, page)) {
696 	  /* Finalize the page */
697 	  if (pagination) {
698 	    if (send_ff) {
699 	      if (page_empty)
700 		destptr = out_page; /* Remove unneeded white space */
701 	      /* Send Form Feed */
702 	      *(destptr ++) = '\f';
703 	    } else if (outbuf[0] != '\0') {
704 	      /* Fill up page with blank lines */
705 	      for (i = 0; i < page_bottom; i ++)
706 		for (j = 0; newline_char_str[j]; j ++)
707 		  *(destptr ++) = newline_char_str[j];
708 	    }
709 	  }
710 	  /* Allow to handle the finished page as a C string */
711 	  *(destptr ++) = '\0';
712 	  /* Count pages which will actually get printed */
713 	  num_pages ++;
714 	  if (!pagination) {
715 	    /* Send out the output page buffer content */
716 	    printf("%s", out_page);
717 	    /* Log the page output (only once, when printing the first buffer
718 	       load) */
719 	    if (num_pages == 1)
720 	      fprintf(stderr, "PAGE: 1 1\n");
721 	  } else if ((num_copies == 1 || !collate) && !reverse_order) {
722 	    /* Send out the page */
723 	    for (i = 0; i < num_copies; i ++)
724 	      printf("%s", out_page);
725 	    /* Log the page output */
726 	    fprintf(stderr, "PAGE: %d %d\n", num_pages, num_copies);
727 	  } else {
728 	    /* Save the page in the page array */
729 	    cupsArrayAdd(page_array, strdup(out_page));
730 	  }
731 	}
732 	/* Reset for next page */
733 	destptr = out_page;
734 	page_empty = 1;
735 	line = 0;
736 	column = 0;
737 	page ++;
738       }
739       if (outbuf[0] == '\0') /* End of input file */
740 	break;
741       if (column == 0) { /* Start of new line */
742 	if (line == 0 && pagination)   /* Start of new page */
743 	  for (i = 0; i < page_top; i ++)
744 	    for (j = 0; newline_char_str[j]; j ++)
745 	      *(destptr ++) = newline_char_str[j];
746 	for (i = 0; i < page_left; i ++)
747 	  *(destptr ++) = ' ';
748 	/* Did we wrap a word from the previous line? */
749 	if (wrapped_word) {
750 	  for (p = wrapped_word; *p != '\0'; p ++, column ++)
751 	    *(destptr ++) = *p;
752 	  free(wrapped_word);
753 	  wrapped_word = NULL;
754 	  page_empty = 0;
755 	  skip_spaces = 0;
756 	}
757       }
758       if (*procptr == '\r' || *procptr == '\n') { /* CR or LF */
759 	/* Only write newline if we are not on the LF of a CR+LF */
760 	if (*procptr == '\r' || previous_is_cr == 0) {
761 	  /* Remove trailing whitespace */
762 	  while (destptr > out_page && *(destptr - 1) == ' ')
763 	    destptr --;
764 	  /* Put newline character(s) */
765 	  for (j = 0; newline_char_str[j]; j ++)
766 	    *(destptr ++) = newline_char_str[j];
767 	  /* Position cursor in next line */
768 	  line ++;
769 	  column = 0;
770 	  /* Finished truncating an overlong line */
771 	  skip_rest_of_line = 0;
772 	  skip_spaces = 0;
773 	}
774 	if (*procptr == '\r')
775 	  previous_is_cr = 1;
776 	else
777 	  previous_is_cr = 0;
778       } else {
779 	previous_is_cr = 0;
780 	if (*procptr == '\t') { /* Tab character */
781 	  if (!skip_rest_of_line && !skip_spaces) {
782 	    *(destptr ++) = ' '; /* Always at least one space */
783 	    column ++;
784 	    /* Add spaces to reach next multiple of the tab width */
785 	    for (; column % tab_width != 0 && column < text_width; column ++)
786 	      *(destptr ++) = ' ';
787 	  }
788 	} else if (*procptr == '\f') { /* Form feed */
789 	  /* Skip to end of page */
790 	  if (send_ff)
791 	    /* Mark page full */
792 	    line = text_height;
793 	  else if (pagination)
794 	    /* Fill page with newlines */
795 	    for (; line < text_height; line ++)
796 	      for (j = 0; newline_char_str[j]; j ++)
797 		*(destptr ++) = newline_char_str[j];
798 	  column = 0;
799 	  /* Finished truncating an overlong line */
800 	  skip_rest_of_line = 0;
801 	  skip_spaces = 0;
802 	} else if (*procptr == ' ') { /* Space */
803 	  if (!skip_rest_of_line && !skip_spaces) {
804 	    *(destptr ++) = *procptr;
805 	    column ++;
806 	  }
807 	} else if (*procptr > ' ' || *procptr < '\0') { /* Regular character */
808 	  if (!skip_rest_of_line) {
809 	    *(destptr ++) = *procptr;
810 	    column ++;
811 	    page_empty = 0;
812 	    skip_spaces = 0;
813 	  }
814 	}
815       }
816     }
817   } while (outbuf[0] != '\0'); /* End of input file */
818 
819   close(fd);
820 
821   if (iconv_close (cd) != 0)
822     fprintf (stderr, "DEBUG: Error closing iconv encoding conversion session\n");
823 
824   /* Error out on an illegal UTF-8 sequence in the input file */
825   if (result < 0)
826     goto error;
827 
828   /* Print out the page array if we created one */
829   if (pagination &&
830       ((num_copies != 1 && collate) || reverse_order)) {
831     /* If we print collated copies, the outer loop (i) goes through the
832        copies, if we do not collate, the inner loop (j) goes through the
833        copies. The other loop only runs for one cycle. */
834     for (i = 0; i < (collate ? num_copies : 1); i ++)
835       for (page = (reverse_order ? num_pages : 1);
836 	   (reverse_order ? (page >= 1) : (page <= num_pages));
837 	   page += (reverse_order ? -1 : 1)) {
838 	p = (char *)cupsArrayIndex(page_array, page - 1);
839 	for (j = 0; j < (collate ? 1 : num_copies); j ++)
840 	  printf("%s", p);
841 	fprintf(stderr, "PAGE: %d %d\n", page, (collate ? 1 : num_copies));
842       }
843     /* Clean up */
844     for (page = 0; page < num_pages; page ++) {
845       p = (char *)cupsArrayIndex(page_array, page);
846       free(p);
847     }
848     cupsArrayDelete(page_array);
849   }
850 
851  /*
852   * Cleanup and exit...
853   */
854 
855  error:
856 
857   free(page_ranges);
858   free(out_page);
859 
860   if (tempfile[0])
861     unlink(tempfile);
862 
863   return (exit_status);
864 }
865 
866 
867 /*
868  * 'is_true()' - Check option value for boolean true
869  */
870 
is_true(const char * value)871 static int is_true(const char *value)
872 {
873   if (!value)
874     return 0;
875   return (strcasecmp(value, "yes") == 0) ||
876     (strcasecmp(value, "on") == 0) ||
877     (strcasecmp(value, "true") == 0) ||
878     (strcasecmp(value, "1") == 0);
879 }
880 
881 
882 /*
883  * 'is_false()' - Check option value for boolean false
884  */
885 
is_false(const char * value)886 static int is_false(const char *value)
887 {
888   if (!value)
889     return 0;
890   return (strcasecmp(value, "no") == 0) ||
891     (strcasecmp(value, "off") == 0) ||
892     (strcasecmp(value, "false") == 0) ||
893     (strcasecmp(value, "0") == 0);
894 }
895 
896 
897 /*
898  * 'check_range()' - Check to see if the current page is selected for
899  *                   printing.
900  */
901 
902 static int				/* O - 1 if selected, 0 otherwise */
check_range(char * page_ranges,int even_pages,int odd_pages,int page)903 check_range(char *page_ranges,          /* I - Selection of pages to print */
904 	    int even_pages,             /* I - Print the even pages */
905 	    int odd_pages,              /* I - Print the odd pages */
906             int page)		        /* I - Page number */
907 {
908   const char	*range;			/* Pointer into range string */
909   int		lower, upper;		/* Lower and upper page numbers */
910 
911  /*
912   * See if we only print even or odd pages...
913   */
914 
915   if (!odd_pages && (page & 1))
916     return (0);
917 
918   if (!even_pages && !(page & 1))
919     return (0);
920 
921  /*
922   * page-ranges option
923   */
924 
925   if (!page_ranges || page_ranges[0] == '\0')
926     return (1);				/* No range, print all pages... */
927 
928   for (range = page_ranges; *range != '\0';)
929   {
930     if (*range == '-')
931     {
932       lower = 1;
933       range ++;
934       upper = (int)strtol(range, (char **)&range, 10);
935     }
936     else
937     {
938       lower = (int)strtol(range, (char **)&range, 10);
939 
940       if (*range == '-')
941       {
942         range ++;
943 	if (!isdigit(*range & 255))
944 	  upper = 65535;
945 	else
946 	  upper = (int)strtol(range, (char **)&range, 10);
947       }
948       else
949         upper = lower;
950     }
951 
952     if (page >= lower && page <= upper)
953       return (1);
954 
955     if (*range == ',')
956       range ++;
957     else
958       break;
959   }
960 
961   return (0);
962 }
963 
964 
965 /*
966  * 'cancel_job()' - Flag the job as canceled.
967  */
968 
969 static void
cancel_job(int sig)970 cancel_job(int sig)			/* I - Signal number (unused) */
971 {
972   (void)sig;
973 
974   job_canceled = 1;
975 }
976 
977 
978 /*
979  * End
980  */
981