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