Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/scheduler/cupsfilter.c
1090 views
1
/*
2
* Filtering program for CUPS.
3
*
4
* Copyright © 2021-2022 by OpenPrinting
5
* Copyright © 2007-2016 by Apple Inc.
6
* Copyright © 1997-2006 by Easy Software Products, all rights reserved.
7
*
8
* Licensed under Apache License v2.0. See the file "LICENSE" for more information.
9
*/
10
11
/*
12
* Include necessary headers...
13
*/
14
15
#include <cups/cups-private.h>
16
#include <cups/file-private.h>
17
#include <cups/ppd-private.h>
18
#include "mime.h"
19
#include <limits.h>
20
#include <unistd.h>
21
#include <fcntl.h>
22
#include <signal.h>
23
#include <sys/wait.h>
24
#if defined(__APPLE__)
25
# include <libgen.h>
26
#endif /* __APPLE__ */
27
28
29
/*
30
* Local globals...
31
*/
32
33
static char *DataDir = NULL;/* CUPS_DATADIR environment variable */
34
static mime_filter_t GZIPFilter = /* gziptoany filter */
35
{
36
NULL, /* Source type */
37
NULL, /* Destination type */
38
0, /* Cost */
39
"gziptoany" /* Filter program to run */
40
};
41
static char *Path = NULL; /* PATH environment variable */
42
static char *ServerBin = NULL;
43
/* CUPS_SERVERBIN environment variable */
44
static char *ServerRoot = NULL;
45
/* CUPS_SERVERROOT environment variable */
46
static char TempFile[1024] = "";
47
/* Temporary file */
48
49
50
/*
51
* Local functions...
52
*/
53
54
static void add_printer_filter(const char *command, mime_t *mime,
55
mime_type_t *printer_type,
56
const char *filter);
57
static mime_type_t *add_printer_filters(const char *command,
58
mime_t *mime, const char *printer,
59
const char *ppdfile,
60
mime_type_t **prefilter_type);
61
static void check_cb(void *context, _cups_fc_result_t result,
62
const char *message);
63
static int compare_pids(mime_filter_t *a, mime_filter_t *b);
64
static char *escape_options(int num_options, cups_option_t *options);
65
static int exec_filter(const char *filter, char **argv,
66
char **envp, int infd, int outfd);
67
static int exec_filters(mime_type_t *srctype,
68
cups_array_t *filters, const char *infile,
69
const char *outfile, const char *ppdfile,
70
const char *printer, const char *user,
71
const char *title, int num_options,
72
cups_option_t *options);
73
static void get_job_file(const char *job);
74
static int open_pipe(int *fds);
75
static int read_cups_files_conf(const char *filename);
76
static void set_string(char **s, const char *val);
77
static void sighandler(int sig);
78
static void usage(const char *opt) _CUPS_NORETURN;
79
80
81
/*
82
* 'main()' - Main entry for the test program.
83
*/
84
85
int /* O - Exit status */
86
main(int argc, /* I - Number of command-line args */
87
char *argv[]) /* I - Command-line arguments */
88
{
89
int i, /* Looping vars */
90
list_filters = 0; /* Just list the filters? */
91
const char *command, /* Command name */
92
*opt, /* Current option */
93
*printer; /* Printer name */
94
mime_type_t *printer_type, /* Printer MIME type */
95
*prefilter_type; /* Printer prefilter MIME type */
96
char *srctype, /* Source type */
97
*dsttype, /* Destination type */
98
super[MIME_MAX_SUPER], /* Super-type name */
99
type[MIME_MAX_TYPE]; /* Type name */
100
int compression; /* Compression of file */
101
int cost; /* Cost of filters */
102
mime_t *mime; /* MIME database */
103
char mimedir[1024]; /* MIME directory */
104
char *infile, /* File to filter */
105
*outfile; /* File to create */
106
char cupsfilesconf[1024]; /* cups-files.conf file */
107
const char *server_root; /* CUPS_SERVERROOT environment variable */
108
mime_type_t *src, /* Source type */
109
*dst; /* Destination type */
110
cups_array_t *filters; /* Filters for the file */
111
int num_options; /* Number of options */
112
cups_option_t *options; /* Options */
113
const char *ppdfile; /* PPD file */
114
const char *title, /* Title string */
115
*user; /* Username */
116
int all_filters, /* Use all filters */
117
removeppd, /* Remove PPD file */
118
removeinfile; /* Remove input file */
119
int status; /* Execution status */
120
121
122
/*
123
* Setup defaults...
124
*/
125
126
if ((command = strrchr(argv[0], '/')) != NULL)
127
command ++;
128
else
129
command = argv[0];
130
131
printer = !strcmp(command, "convert") ? "tofile" : "cupsfilter";
132
mime = NULL;
133
srctype = NULL;
134
compression = 0;
135
dsttype = "application/pdf";
136
infile = NULL;
137
outfile = NULL;
138
num_options = 0;
139
options = NULL;
140
ppdfile = NULL;
141
title = NULL;
142
user = cupsUser();
143
all_filters = 0;
144
removeppd = 0;
145
removeinfile = 0;
146
147
if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
148
server_root = CUPS_SERVERROOT;
149
150
snprintf(cupsfilesconf, sizeof(cupsfilesconf), "%s/cups-files.conf", server_root);
151
152
/*
153
* Process command-line arguments...
154
*/
155
156
_cupsSetLocale(argv);
157
158
for (i = 1; i < argc; i ++)
159
{
160
if (argv[i][0] == '-')
161
{
162
if (!strcmp(argv[i], "--list-filters"))
163
{
164
list_filters = 1;
165
}
166
else if (!strcmp(argv[i], "--"))
167
{
168
i ++;
169
if (i < argc && !infile)
170
infile = argv[i];
171
else
172
usage(NULL);
173
}
174
else
175
{
176
for (opt = argv[i] + 1; *opt; opt ++)
177
{
178
switch (*opt)
179
{
180
case 'a' : /* Specify option... */
181
i ++;
182
if (i < argc)
183
{
184
num_options = cupsParseOptions(argv[i], num_options, &options);
185
}
186
else
187
{
188
_cupsLangPrintf(stderr, _("%s: Error - expected name=value after \"-a\" option."), argv[0]);
189
usage(opt);
190
}
191
break;
192
193
case 'c' : /* Specify cups-files.conf file location... */
194
i ++;
195
if (i < argc)
196
{
197
if (!strcmp(command, "convert"))
198
num_options = cupsAddOption("copies", argv[i], num_options, &options);
199
else
200
strlcpy(cupsfilesconf, argv[i], sizeof(cupsfilesconf));
201
}
202
else
203
{
204
_cupsLangPrintf(stderr, _("%s: Error - expected filename after \"-c\" option."), argv[0]);
205
usage(NULL);
206
}
207
break;
208
209
case 'd' : /* Specify the real printer name */
210
i ++;
211
if (i < argc)
212
{
213
printer = argv[i];
214
}
215
else
216
{
217
_cupsLangPrintf(stderr, _("%s: Error - expected printer name after \"-d\" option."), argv[0]);
218
usage(NULL);
219
}
220
break;
221
222
case 'D' : /* Delete input file after conversion */
223
removeinfile = 1;
224
break;
225
226
case 'e' : /* Use every filter from the PPD file */
227
all_filters = 1;
228
break;
229
230
case 'f' : /* Specify input file... */
231
i ++;
232
if (i < argc && !infile)
233
{
234
infile = argv[i];
235
}
236
else
237
{
238
_cupsLangPrintf(stderr, _("%s: Error - expected input file after \"-f\" option."), argv[0]);
239
usage(NULL);
240
}
241
break;
242
243
case 'i' : /* Specify source MIME type... */
244
i ++;
245
if (i < argc)
246
{
247
if (sscanf(argv[i], "%15[^/]/%255s", super, type) != 2)
248
usage(opt);
249
250
srctype = argv[i];
251
}
252
else
253
{
254
_cupsLangPrintf(stderr, _("%s: Error - expected source MIME type after \"-i\" option."), argv[0]);
255
usage(NULL);
256
}
257
break;
258
259
case 'j' : /* Get job file or specify destination MIME type... */
260
if (strcmp(command, "convert"))
261
{
262
i ++;
263
if (i < argc)
264
{
265
get_job_file(argv[i]);
266
infile = TempFile;
267
}
268
else
269
{
270
_cupsLangPrintf(stdout, _("%s: Error - expected job-id after \"-j\" option."), argv[0]);
271
usage(NULL);
272
}
273
break;
274
}
275
276
case 'm' : /* Specify destination MIME type... */
277
i ++;
278
if (i < argc)
279
{
280
if (sscanf(argv[i], "%15[^/]/%255s", super, type) != 2)
281
usage(opt);
282
283
dsttype = argv[i];
284
}
285
else
286
{
287
_cupsLangPrintf(stderr, _("%s: Error - expected destination MIME type after \"-m\" option."), argv[0]);
288
usage(NULL);
289
}
290
break;
291
292
case 'n' : /* Specify number of copies... */
293
i ++;
294
if (i < argc)
295
{
296
num_options = cupsAddOption("copies", argv[i], num_options, &options);
297
}
298
else
299
{
300
_cupsLangPrintf(stderr, _("%s: Error - expected number of copies after \"-n\" option."), argv[0]);
301
usage(NULL);
302
}
303
break;
304
305
case 'o' : /* Specify option(s) or output filename */
306
i ++;
307
if (i < argc)
308
{
309
if (!strcmp(command, "convert"))
310
{
311
if (outfile)
312
usage(NULL);
313
else
314
outfile = argv[i];
315
}
316
else
317
{
318
num_options = cupsParseOptions(argv[i], num_options, &options);
319
}
320
}
321
else
322
{
323
_cupsLangPrintf(stderr, _("%s: Error - expected name=value after \"-o\" option."), argv[0]);
324
usage(NULL);
325
}
326
break;
327
328
case 'p' : /* Specify PPD file... */
329
case 'P' : /* Specify PPD file... */
330
i ++;
331
if (i < argc)
332
{
333
ppdfile = argv[i];
334
}
335
else
336
{
337
_cupsLangPrintf(stderr, _("%s: Error - expected PPD file after \"-%c\" option."), argv[0], *opt);
338
usage(NULL);
339
}
340
break;
341
342
case 't' : /* Specify title... */
343
case 'J' : /* Specify title... */
344
i ++;
345
if (i < argc)
346
{
347
title = argv[i];
348
}
349
else
350
{
351
_cupsLangPrintf(stderr, _("%s: Error - expected title after \"-%c\" option."), argv[0], *opt);
352
usage(NULL);
353
}
354
break;
355
356
case 'u' : /* Delete PPD file after conversion */
357
removeppd = 1;
358
break;
359
360
case 'U' : /* Specify username... */
361
i ++;
362
if (i < argc)
363
{
364
user = argv[i];
365
}
366
else
367
{
368
_cupsLangPrintf(stderr, _("%s: Error - expected \"username\" after \"-U\" option."), argv[0]);
369
usage(NULL);
370
}
371
break;
372
373
default : /* Something we don't understand... */
374
_cupsLangPrintf(stderr, _("%s: Error - unknown option \"-%c\"."), argv[0], *opt);
375
usage(NULL);
376
}
377
}
378
}
379
}
380
else if (!infile)
381
{
382
if (strcmp(command, "convert"))
383
infile = argv[i];
384
else
385
usage(NULL);
386
}
387
else
388
{
389
_cupsLangPuts(stderr, _("cupsfilter: Only one filename can be specified."));
390
usage(NULL);
391
}
392
}
393
394
if (!infile && !srctype)
395
usage(NULL);
396
397
if (!title)
398
{
399
if (!infile)
400
title = "(stdin)";
401
else if ((title = strrchr(infile, '/')) != NULL)
402
title ++;
403
else
404
title = infile;
405
}
406
407
/*
408
* Load the cups-files.conf file and create the MIME database...
409
*/
410
411
if (read_cups_files_conf(cupsfilesconf))
412
return (1);
413
414
snprintf(mimedir, sizeof(mimedir), "%s/mime", DataDir);
415
416
mime = mimeLoadTypes(NULL, mimedir);
417
mime = mimeLoadTypes(mime, ServerRoot);
418
mime = mimeLoadFilters(mime, mimedir, Path);
419
mime = mimeLoadFilters(mime, ServerRoot, Path);
420
421
if (!mime)
422
{
423
_cupsLangPrintf(stderr, _("%s: Unable to read MIME database from \"%s\" or \"%s\"."), command, mimedir, ServerRoot);
424
return (1);
425
}
426
427
prefilter_type = NULL;
428
429
if (all_filters)
430
printer_type = add_printer_filters(command, mime, printer, ppdfile,
431
&prefilter_type);
432
else
433
printer_type = mimeType(mime, "application", "vnd.cups-postscript");
434
435
/*
436
* Get the source and destination types...
437
*/
438
439
if (srctype)
440
{
441
/* sscanf return value already checked above */
442
sscanf(srctype, "%15[^/]/%255s", super, type);
443
if ((src = mimeType(mime, super, type)) == NULL)
444
{
445
_cupsLangPrintf(stderr,
446
_("%s: Unknown source MIME type %s/%s."),
447
command, super, type);
448
return (1);
449
}
450
}
451
else if ((src = mimeFileType(mime, infile, infile, &compression)) == NULL)
452
{
453
_cupsLangPrintf(stderr,
454
_("%s: Unable to determine MIME type of \"%s\"."),
455
command, infile);
456
return (1);
457
}
458
459
/* sscanf return value already checked above */
460
sscanf(dsttype, "%15[^/]/%255s", super, type);
461
if (!_cups_strcasecmp(super, "printer"))
462
dst = printer_type;
463
else if ((dst = mimeType(mime, super, type)) == NULL)
464
{
465
_cupsLangPrintf(stderr,
466
_("%s: Unknown destination MIME type %s/%s."),
467
command, super, type);
468
return (1);
469
}
470
471
/*
472
* Figure out how to filter the file...
473
*/
474
475
if (src == dst)
476
{
477
/*
478
* Special case - no filtering needed...
479
*/
480
481
filters = cupsArrayNew(NULL, NULL);
482
cupsArrayAdd(filters, &GZIPFilter);
483
GZIPFilter.src = src;
484
GZIPFilter.dst = dst;
485
}
486
else if ((filters = mimeFilter(mime, src, dst, &cost)) == NULL)
487
{
488
_cupsLangPrintf(stderr,
489
_("%s: No filter to convert from %s/%s to %s/%s."),
490
command, src->super, src->type, dst->super, dst->type);
491
return (1);
492
}
493
else if (compression)
494
cupsArrayInsert(filters, &GZIPFilter);
495
496
if (prefilter_type)
497
{
498
/*
499
* Add pre-filters...
500
*/
501
502
mime_filter_t *filter, /* Current filter */
503
*prefilter; /* Current pre-filter */
504
cups_array_t *prefilters = cupsArrayNew(NULL, NULL);
505
/* New filters array */
506
507
508
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
509
filter;
510
filter = (mime_filter_t *)cupsArrayNext(filters))
511
{
512
if ((prefilter = mimeFilterLookup(mime, filter->src,
513
prefilter_type)) != NULL)
514
cupsArrayAdd(prefilters, prefilter);
515
516
cupsArrayAdd(prefilters, filter);
517
}
518
519
cupsArrayDelete(filters);
520
filters = prefilters;
521
}
522
523
if (list_filters)
524
{
525
/*
526
* List filters...
527
*/
528
529
mime_filter_t *filter; /* Current filter */
530
531
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
532
filter;
533
filter = (mime_filter_t *)cupsArrayNext(filters))
534
if (strcmp(filter->filter, "-"))
535
_cupsLangPuts(stdout, filter->filter);
536
537
status = 0;
538
}
539
else
540
{
541
/*
542
* Run filters...
543
*/
544
545
status = exec_filters(src, filters, infile, outfile, ppdfile, printer, user,
546
title, num_options, options);
547
}
548
549
/*
550
* Remove files as needed, then exit...
551
*/
552
553
if (TempFile[0])
554
unlink(TempFile);
555
556
if (removeppd && ppdfile)
557
unlink(ppdfile);
558
559
if (removeinfile && infile)
560
unlink(infile);
561
562
return (status);
563
}
564
565
566
/*
567
* 'add_printer_filter()' - Add a single filters from a PPD file.
568
*/
569
570
static void
571
add_printer_filter(
572
const char *command, /* I - Command name */
573
mime_t *mime, /* I - MIME database */
574
mime_type_t *filtertype, /* I - Printer or prefilter MIME type */
575
const char *filter) /* I - Filter to add */
576
{
577
char super[MIME_MAX_SUPER], /* Super-type for filter */
578
type[MIME_MAX_TYPE], /* Type for filter */
579
dsuper[MIME_MAX_SUPER], /* Destination super-type for filter */
580
dtype[MIME_MAX_TYPE], /* Destination type for filter */
581
dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2],
582
/* Destination super/type */
583
program[1024]; /* Program/filter name */
584
int cost; /* Cost of filter */
585
size_t maxsize = 0; /* Maximum supported file size */
586
mime_type_t *temptype, /* MIME type looping var */
587
*desttype; /* Destination MIME type */
588
mime_filter_t *filterptr; /* MIME filter */
589
590
591
/*
592
* Parse the filter string; it should be in one of the following formats:
593
*
594
* source/type cost program
595
* source/type cost maxsize(nnnn) program
596
* source/type dest/type cost program
597
* source/type dest/type cost maxsize(nnnn) program
598
*/
599
600
if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
601
super, type, dsuper, dtype, &cost, program) == 6)
602
{
603
snprintf(dest, sizeof(dest), "%s/%s/%s", filtertype->type, dsuper, dtype);
604
605
if ((desttype = mimeType(mime, "printer", dest)) == NULL)
606
desttype = mimeAddType(mime, "printer", dest);
607
}
608
else
609
{
610
if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
611
program) == 4)
612
{
613
desttype = filtertype;
614
}
615
else
616
{
617
_cupsLangPrintf(stderr, _("%s: Invalid filter string \"%s\"."), command,
618
filter);
619
return;
620
}
621
}
622
623
if (!strncmp(program, "maxsize(", 8))
624
{
625
char *ptr; /* Pointer into maxsize(nnnn) program */
626
627
maxsize = (size_t)strtoll(program + 8, &ptr, 10);
628
629
if (*ptr != ')')
630
{
631
printf("testmime: Invalid filter string \"%s\".\n", filter);
632
return;
633
}
634
635
ptr ++;
636
while (_cups_isspace(*ptr))
637
ptr ++;
638
639
_cups_strcpy(program, ptr);
640
}
641
642
/*
643
* See if the filter program exists; if not, stop the printer and flag
644
* the error!
645
*/
646
647
if (strcmp(program, "-"))
648
{
649
char filename[1024]; /* Full path to program */
650
651
if (program[0] == '/')
652
strlcpy(filename, program, sizeof(filename));
653
else
654
snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program);
655
656
if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_PROGRAM, !geteuid(), check_cb,
657
(void *)command))
658
return;
659
}
660
661
/*
662
* Add the filter to the MIME database, supporting wildcards as needed...
663
*/
664
665
for (temptype = mimeFirstType(mime);
666
temptype;
667
temptype = mimeNextType(mime))
668
if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) ||
669
!_cups_strcasecmp(temptype->super, super)) &&
670
(type[0] == '*' || !_cups_strcasecmp(temptype->type, type)))
671
{
672
if (desttype != filtertype)
673
{
674
filterptr = mimeAddFilter(mime, temptype, desttype, cost, program);
675
676
if (!mimeFilterLookup(mime, desttype, filtertype))
677
mimeAddFilter(mime, desttype, filtertype, 0, "-");
678
}
679
else
680
filterptr = mimeAddFilter(mime, temptype, filtertype, cost, program);
681
682
if (filterptr)
683
filterptr->maxsize = maxsize;
684
}
685
}
686
687
688
/*
689
* 'add_printer_filters()' - Add filters from a PPD file.
690
*/
691
692
static mime_type_t * /* O - Printer type or NULL on error */
693
add_printer_filters(
694
const char *command, /* I - Command name */
695
mime_t *mime, /* I - MIME database */
696
const char *printer, /* I - Printer name */
697
const char *ppdfile, /* I - PPD file */
698
mime_type_t **prefilter_type) /* O - Prefilter type */
699
{
700
ppd_file_t *ppd; /* PPD file data */
701
_ppd_cache_t *pc; /* Cache data for PPD */
702
const char *value; /* Filter definition value */
703
mime_type_t *printer_type; /* Printer filter type */
704
705
706
if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_NONE)) == NULL)
707
{
708
ppd_status_t status; /* PPD load status */
709
int linenum; /* Line number */
710
711
status = ppdLastError(&linenum);
712
_cupsLangPrintf(stderr, _("%s: Unable to open PPD file: %s on line %d."),
713
command, ppdErrorString(status), linenum);
714
return (NULL);
715
}
716
717
pc = _ppdCacheCreateWithPPD(ppd);
718
if (!pc)
719
return (NULL);
720
721
printer_type = mimeAddType(mime, "printer", printer);
722
*prefilter_type = NULL;
723
724
if (pc->filters)
725
{
726
for (value = (const char *)cupsArrayFirst(pc->filters);
727
value;
728
value = (const char *)cupsArrayNext(pc->filters))
729
add_printer_filter(command, mime, printer_type, value);
730
}
731
else
732
{
733
add_printer_filter(command, mime, printer_type,
734
"application/vnd.cups-raw 0 -");
735
add_printer_filter(command, mime, printer_type,
736
"application/vnd.cups-postscript 0 -");
737
}
738
739
if (pc->prefilters)
740
{
741
*prefilter_type = mimeAddType(mime, "prefilter", printer);
742
743
for (value = (const char *)cupsArrayFirst(pc->prefilters);
744
value;
745
value = (const char *)cupsArrayNext(pc->prefilters))
746
add_printer_filter(command, mime, *prefilter_type, value);
747
}
748
749
return (printer_type);
750
}
751
752
753
/*
754
* 'check_cb()' - Callback function for _cupsFileCheck.
755
*/
756
757
static void
758
check_cb(void *context, /* I - Context (command name) */
759
_cups_fc_result_t result, /* I - Result of check */
760
const char *message) /* I - Localized message */
761
{
762
(void)result;
763
764
_cupsLangPrintf(stderr, _("%s: %s"), (char *)context, message);
765
}
766
767
768
/*
769
* 'compare_pids()' - Compare two filter PIDs...
770
*/
771
772
static int /* O - Result of comparison */
773
compare_pids(mime_filter_t *a, /* I - First filter */
774
mime_filter_t *b) /* I - Second filter */
775
{
776
/*
777
* Because we're particularly lazy, we store the process ID in the "cost"
778
* variable...
779
*/
780
781
return (a->cost - b->cost);
782
}
783
784
785
/*
786
* 'escape_options()' - Convert an options array to a string.
787
*/
788
789
static char * /* O - Option string */
790
escape_options(
791
int num_options, /* I - Number of options */
792
cups_option_t *options) /* I - Options */
793
{
794
int i; /* Looping var */
795
cups_option_t *option; /* Current option */
796
size_t bytes; /* Number of bytes needed */
797
char *s, /* Option string */
798
*sptr, /* Pointer into string */
799
*vptr; /* Pointer into value */
800
801
802
/*
803
* Figure out the worst-case number of bytes we need for the option string.
804
*/
805
806
for (i = num_options, option = options, bytes = 1; i > 0; i --, option ++)
807
bytes += 2 * (strlen(option->name) + strlen(option->value)) + 2;
808
809
if ((s = malloc(bytes)) == NULL)
810
return (NULL);
811
812
/*
813
* Copy the options to the string...
814
*/
815
816
for (i = num_options, option = options, sptr = s; i > 0; i --, option ++)
817
{
818
if (!strcmp(option->name, "copies"))
819
continue;
820
821
if (sptr > s)
822
*sptr++ = ' ';
823
824
strlcpy(sptr, option->name, bytes - (size_t)(sptr - s));
825
sptr += strlen(sptr);
826
*sptr++ = '=';
827
828
for (vptr = option->value; *vptr;)
829
{
830
if (strchr("\\ \t\n", *vptr))
831
*sptr++ = '\\';
832
833
*sptr++ = *vptr++;
834
}
835
}
836
837
*sptr = '\0';
838
839
return (s);
840
}
841
842
843
/*
844
* 'exec_filter()' - Execute a single filter.
845
*/
846
847
static int /* O - Process ID or -1 on error */
848
exec_filter(const char *filter, /* I - Filter to execute */
849
char **argv, /* I - Argument list */
850
char **envp, /* I - Environment list */
851
int infd, /* I - Stdin file descriptor */
852
int outfd) /* I - Stdout file descriptor */
853
{
854
int pid, /* Process ID */
855
fd; /* Temporary file descriptor */
856
#if defined(__APPLE__)
857
char processPath[1024], /* CFProcessPath environment variable */
858
linkpath[1024]; /* Link path for symlinks... */
859
int linkbytes; /* Bytes for link path */
860
861
862
/*
863
* Add special voodoo magic for macOS - this allows macOS
864
* programs to access their bundle resources properly...
865
*/
866
867
if ((linkbytes = readlink(filter, linkpath, sizeof(linkpath) - 1)) > 0)
868
{
869
/*
870
* Yes, this is a symlink to the actual program, nul-terminate and
871
* use it...
872
*/
873
874
linkpath[linkbytes] = '\0';
875
876
if (linkpath[0] == '/')
877
snprintf(processPath, sizeof(processPath), "CFProcessPath=%s",
878
linkpath);
879
else
880
snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s",
881
dirname((char *)filter), linkpath);
882
}
883
else
884
snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", filter);
885
886
envp[0] = processPath; /* Replace <CFProcessPath> string */
887
#endif /* __APPLE__ */
888
889
if ((pid = fork()) == 0)
890
{
891
/*
892
* Child process goes here...
893
*
894
* Update stdin/stdout/stderr as needed...
895
*/
896
897
if (infd != 0)
898
{
899
if (infd < 0)
900
infd = open("/dev/null", O_RDONLY);
901
902
if (infd > 0)
903
{
904
dup2(infd, 0);
905
close(infd);
906
}
907
}
908
909
if (outfd != 1)
910
{
911
if (outfd < 0)
912
outfd = open("/dev/null", O_WRONLY);
913
914
if (outfd > 1)
915
{
916
dup2(outfd, 1);
917
close(outfd);
918
}
919
}
920
921
if ((fd = open("/dev/null", O_RDWR)) > 3)
922
{
923
dup2(fd, 3);
924
close(fd);
925
}
926
fcntl(3, F_SETFL, O_NDELAY);
927
928
if ((fd = open("/dev/null", O_RDWR)) > 4)
929
{
930
dup2(fd, 4);
931
close(fd);
932
}
933
fcntl(4, F_SETFL, O_NDELAY);
934
935
/*
936
* Execute command...
937
*/
938
939
execve(filter, argv, envp);
940
941
perror(filter);
942
943
exit(errno);
944
}
945
946
return (pid);
947
}
948
949
950
/*
951
* 'exec_filters()' - Execute filters for the given file and options.
952
*/
953
954
static int /* O - 0 on success, 1 on error */
955
exec_filters(mime_type_t *srctype, /* I - Source type */
956
cups_array_t *filters, /* I - Array of filters to run */
957
const char *infile, /* I - File to filter */
958
const char *outfile, /* I - File to create */
959
const char *ppdfile, /* I - PPD file, if any */
960
const char *printer, /* I - Printer name */
961
const char *user, /* I - Username */
962
const char *title, /* I - Job title */
963
int num_options, /* I - Number of filter options */
964
cups_option_t *options) /* I - Filter options */
965
{
966
int i; /* Looping var */
967
const char *argv[8], /* Command-line arguments */
968
*envp[21], /* Environment variables */
969
*temp; /* Temporary string */
970
char *optstr, /* Filter options */
971
content_type[1024], /* CONTENT_TYPE */
972
cups_datadir[1024], /* CUPS_DATADIR */
973
cups_fontpath[1024], /* CUPS_FONTPATH */
974
cups_serverbin[1024], /* CUPS_SERVERBIN */
975
cups_serverroot[1024], /* CUPS_SERVERROOT */
976
final_content_type[1024] = "",
977
/* FINAL_CONTENT_TYPE */
978
lang[1024], /* LANG */
979
path[1024], /* PATH */
980
ppd[1024], /* PPD */
981
printer_info[255], /* PRINTER_INFO env variable */
982
printer_location[255], /* PRINTER_LOCATION env variable */
983
printer_name[255], /* PRINTER env variable */
984
userenv[1024], /* USER */
985
#if CUPS_SNAP
986
fontconfig_file[1024], /* FONTCONFIG_FILE */
987
fontconfig_path[1024], /* FONTCONFIG_PATH */
988
fontconfig_sysroot[1024],
989
/* FONTCONFIG_SYSROOT */
990
ld_library_path[2048], /* LD_LIBRARY_PATH */
991
#endif /* CUPS_SNAP */
992
program[1024]; /* Program to run */
993
mime_filter_t *filter, /* Current filter */
994
*next; /* Next filter */
995
int current, /* Current filter */
996
filterfds[2][2], /* Pipes for filters */
997
pid, /* Process ID of filter */
998
status, /* Exit status */
999
retval; /* Return value */
1000
cups_array_t *pids; /* Executed filters array */
1001
mime_filter_t key; /* Search key for filters */
1002
cups_lang_t *language; /* Current language */
1003
cups_dest_t *dest; /* Destination information */
1004
1005
1006
/*
1007
* Figure out the final content type...
1008
*/
1009
1010
for (filter = (mime_filter_t *)cupsArrayLast(filters);
1011
filter && filter->dst;
1012
filter = (mime_filter_t *)cupsArrayPrev(filters))
1013
if (strcmp(filter->dst->super, "printer"))
1014
break;
1015
1016
if (filter && filter->dst)
1017
{
1018
const char *ptr; /* Pointer in type name */
1019
1020
if ((ptr = strchr(filter->dst->type, '/')) != NULL)
1021
snprintf(final_content_type, sizeof(final_content_type),
1022
"FINAL_CONTENT_TYPE=%s", ptr + 1);
1023
else
1024
snprintf(final_content_type, sizeof(final_content_type),
1025
"FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
1026
filter->dst->type);
1027
}
1028
1029
/*
1030
* Remove NULL ("-") filters...
1031
*/
1032
1033
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
1034
filter;
1035
filter = (mime_filter_t *)cupsArrayNext(filters))
1036
if (!strcmp(filter->filter, "-"))
1037
cupsArrayRemove(filters, filter);
1038
1039
/*
1040
* Setup the filter environment and command-line...
1041
*/
1042
1043
optstr = escape_options(num_options, options);
1044
1045
snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
1046
srctype->super, srctype->type);
1047
snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
1048
snprintf(cups_serverbin, sizeof(cups_serverbin), "CUPS_SERVERBIN=%s",
1049
ServerBin);
1050
snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s",
1051
ServerRoot);
1052
language = cupsLangDefault();
1053
snprintf(lang, sizeof(lang), "LANG=%s.UTF8", language->language);
1054
snprintf(path, sizeof(path), "PATH=%s", Path);
1055
if (ppdfile)
1056
snprintf(ppd, sizeof(ppd), "PPD=%s", ppdfile);
1057
else if ((temp = getenv("PPD")) != NULL)
1058
snprintf(ppd, sizeof(ppd), "PPD=%s", temp);
1059
else
1060
#ifdef __APPLE__
1061
if (!access("/System/Library/Frameworks/ApplicationServices.framework/"
1062
"Versions/A/Frameworks/PrintCore.framework/Versions/A/"
1063
"Resources/English.lproj/Generic.ppd", 0))
1064
strlcpy(ppd, "PPD=/System/Library/Frameworks/ApplicationServices.framework/"
1065
"Versions/A/Frameworks/PrintCore.framework/Versions/A/"
1066
"Resources/English.lproj/Generic.ppd", sizeof(ppd));
1067
else
1068
strlcpy(ppd, "PPD=/System/Library/Frameworks/ApplicationServices.framework/"
1069
"Versions/A/Frameworks/PrintCore.framework/Versions/A/"
1070
"Resources/Generic.ppd", sizeof(ppd));
1071
#else
1072
snprintf(ppd, sizeof(ppd), "PPD=%s/model/laserjet.ppd", DataDir);
1073
#endif /* __APPLE__ */
1074
1075
snprintf(userenv, sizeof(userenv), "USER=%s", user);
1076
1077
if (printer &&
1078
(dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer, NULL)) != NULL)
1079
{
1080
if ((temp = cupsGetOption("printer-info", dest->num_options,
1081
dest->options)) != NULL)
1082
snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", temp);
1083
else
1084
snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", printer);
1085
1086
if ((temp = cupsGetOption("printer-location", dest->num_options,
1087
dest->options)) != NULL)
1088
snprintf(printer_location, sizeof(printer_location),
1089
"PRINTER_LOCATION=%s", temp);
1090
else
1091
strlcpy(printer_location, "PRINTER_LOCATION=Unknown",
1092
sizeof(printer_location));
1093
}
1094
else
1095
{
1096
snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s",
1097
printer ? printer : "Unknown");
1098
strlcpy(printer_location, "PRINTER_LOCATION=Unknown",
1099
sizeof(printer_location));
1100
}
1101
1102
snprintf(printer_name, sizeof(printer_name), "PRINTER=%s",
1103
printer ? printer : "Unknown");
1104
1105
argv[0] = (char *)printer;
1106
argv[1] = "1";
1107
argv[2] = user;
1108
argv[3] = title;
1109
argv[4] = cupsGetOption("copies", num_options, options);
1110
argv[5] = optstr;
1111
argv[6] = infile;
1112
argv[7] = NULL;
1113
1114
if (!argv[4])
1115
argv[4] = "1";
1116
1117
for (i = 0; argv[i]; i ++)
1118
fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
1119
1120
i = 0;
1121
#ifdef __APPLE__
1122
envp[i ++] = "<CFProcessPath>";
1123
#endif /* __APPLE__ */
1124
envp[i ++] = content_type;
1125
envp[i ++] = cups_datadir;
1126
envp[i ++] = cups_fontpath;
1127
envp[i ++] = cups_serverbin;
1128
envp[i ++] = cups_serverroot;
1129
envp[i ++] = lang;
1130
envp[i ++] = path;
1131
envp[i ++] = ppd;
1132
envp[i ++] = printer_info;
1133
envp[i ++] = printer_location;
1134
envp[i ++] = printer_name;
1135
envp[i ++] = userenv;
1136
envp[i ++] = "CHARSET=utf-8";
1137
if (final_content_type[0])
1138
envp[i ++] = final_content_type;
1139
1140
#if CUPS_SNAP
1141
if ((temp = getenv("FONTCONFIG_FILE")) != NULL)
1142
{
1143
snprintf(fontconfig_file, sizeof(fontconfig_file), "FONTCONFIG_FILE=%s", temp);
1144
envp[i ++] = fontconfig_file;
1145
}
1146
if ((temp = getenv("FONTCONFIG_PATH")) != NULL)
1147
{
1148
snprintf(fontconfig_path, sizeof(fontconfig_path), "FONTCONFIG_PATH=%s", temp);
1149
envp[i ++] = fontconfig_path;
1150
}
1151
if ((temp = getenv("FONTCONFIG_SYSROOT")) != NULL)
1152
{
1153
snprintf(fontconfig_sysroot, sizeof(fontconfig_sysroot), "FONTCONFIG_SYSROOT=%s", temp);
1154
envp[i ++] = fontconfig_sysroot;
1155
}
1156
if ((temp = getenv("LD_LIBRARY_PATH")) != NULL)
1157
{
1158
snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s", temp);
1159
envp[i ++] = ld_library_path;
1160
}
1161
#endif /* CUPS_SNAP */
1162
1163
envp[i] = NULL;
1164
1165
for (i = 0; envp[i]; i ++)
1166
fprintf(stderr, "DEBUG: envp[%d]=\"%s\"\n", i, envp[i]);
1167
1168
/*
1169
* Execute all of the filters...
1170
*/
1171
1172
pids = cupsArrayNew((cups_array_func_t)compare_pids, NULL);
1173
current = 0;
1174
filterfds[0][0] = -1;
1175
filterfds[0][1] = -1;
1176
filterfds[1][0] = -1;
1177
filterfds[1][1] = -1;
1178
1179
if (!infile)
1180
filterfds[0][0] = 0;
1181
1182
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
1183
filter;
1184
filter = next, current = 1 - current)
1185
{
1186
next = (mime_filter_t *)cupsArrayNext(filters);
1187
1188
if (filter->filter[0] == '/')
1189
strlcpy(program, filter->filter, sizeof(program));
1190
else
1191
snprintf(program, sizeof(program), "%s/filter/%s", ServerBin,
1192
filter->filter);
1193
1194
if (filterfds[!current][1] > 1)
1195
{
1196
close(filterfds[1 - current][0]);
1197
close(filterfds[1 - current][1]);
1198
1199
filterfds[1 - current][0] = -1;
1200
filterfds[1 - current][1] = -1;
1201
}
1202
1203
if (next)
1204
open_pipe(filterfds[1 - current]);
1205
else if (outfile)
1206
{
1207
filterfds[1 - current][1] = open(outfile, O_CREAT | O_TRUNC | O_WRONLY,
1208
0666);
1209
1210
if (filterfds[1 - current][1] < 0)
1211
fprintf(stderr, "ERROR: Unable to create \"%s\" - %s\n", outfile,
1212
strerror(errno));
1213
}
1214
else
1215
filterfds[1 - current][1] = 1;
1216
1217
pid = exec_filter(program, (char **)argv, (char **)envp,
1218
filterfds[current][0], filterfds[1 - current][1]);
1219
1220
if (pid > 0)
1221
{
1222
fprintf(stderr, "INFO: %s (PID %d) started.\n", filter->filter, pid);
1223
1224
filter->cost = pid;
1225
cupsArrayAdd(pids, filter);
1226
}
1227
else
1228
break;
1229
1230
argv[6] = NULL;
1231
}
1232
1233
/*
1234
* Close remaining pipes...
1235
*/
1236
1237
if (filterfds[0][1] > 1)
1238
{
1239
close(filterfds[0][0]);
1240
close(filterfds[0][1]);
1241
}
1242
1243
if (filterfds[1][1] > 1)
1244
{
1245
close(filterfds[1][0]);
1246
close(filterfds[1][1]);
1247
}
1248
1249
/*
1250
* Wait for the children to exit...
1251
*/
1252
1253
retval = 0;
1254
1255
while (cupsArrayCount(pids) > 0)
1256
{
1257
if ((pid = wait(&status)) < 0)
1258
continue;
1259
1260
key.cost = pid;
1261
if ((filter = (mime_filter_t *)cupsArrayFind(pids, &key)) != NULL)
1262
{
1263
cupsArrayRemove(pids, filter);
1264
1265
if (status)
1266
{
1267
if (WIFEXITED(status))
1268
fprintf(stderr, "ERROR: %s (PID %d) stopped with status %d\n",
1269
filter->filter, pid, WEXITSTATUS(status));
1270
else
1271
fprintf(stderr, "ERROR: %s (PID %d) crashed on signal %d\n",
1272
filter->filter, pid, WTERMSIG(status));
1273
1274
retval = 1;
1275
}
1276
else
1277
fprintf(stderr, "INFO: %s (PID %d) exited with no errors.\n",
1278
filter->filter, pid);
1279
}
1280
}
1281
1282
cupsArrayDelete(pids);
1283
1284
return (retval);
1285
}
1286
1287
1288
/*
1289
* 'get_job_file()' - Get the specified job file.
1290
*/
1291
1292
static void
1293
get_job_file(const char *job) /* I - Job ID */
1294
{
1295
long jobid, /* Job ID */
1296
docnum; /* Document number */
1297
const char *jobptr; /* Pointer into job ID string */
1298
char uri[1024]; /* job-uri */
1299
http_t *http; /* Connection to server */
1300
ipp_t *request; /* Request data */
1301
int tempfd; /* Temporary file */
1302
1303
1304
/*
1305
* Get the job ID and document number, if any...
1306
*/
1307
1308
if ((jobptr = strrchr(job, '-')) != NULL)
1309
jobptr ++;
1310
else
1311
jobptr = job;
1312
1313
jobid = strtol(jobptr, (char **)&jobptr, 10);
1314
1315
if (*jobptr == ',')
1316
docnum = strtol(jobptr + 1, NULL, 10);
1317
else
1318
docnum = 1;
1319
1320
if (jobid < 1 || jobid > INT_MAX)
1321
{
1322
_cupsLangPrintf(stderr, _("cupsfilter: Invalid job ID %d."), (int)jobid);
1323
exit(1);
1324
}
1325
1326
if (docnum < 1 || docnum > INT_MAX)
1327
{
1328
_cupsLangPrintf(stderr, _("cupsfilter: Invalid document number %d."),
1329
(int)docnum);
1330
exit(1);
1331
}
1332
1333
/*
1334
* Ask the server for the document file...
1335
*/
1336
1337
if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1338
cupsEncryption())) == NULL)
1339
{
1340
_cupsLangPrintf(stderr, _("%s: Unable to connect to server."),
1341
"cupsfilter");
1342
exit(1);
1343
}
1344
1345
request = ippNewRequest(CUPS_GET_DOCUMENT);
1346
1347
snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", (int)jobid);
1348
1349
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1350
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "document-number",
1351
(int)docnum);
1352
1353
if ((tempfd = cupsTempFd(TempFile, sizeof(TempFile))) == -1)
1354
{
1355
_cupsLangPrintError("ERROR", _("Unable to create temporary file"));
1356
httpClose(http);
1357
exit(1);
1358
}
1359
1360
signal(SIGTERM, sighandler);
1361
1362
ippDelete(cupsDoIORequest(http, request, "/", -1, tempfd));
1363
1364
close(tempfd);
1365
1366
httpClose(http);
1367
1368
if (cupsLastError() != IPP_OK)
1369
{
1370
_cupsLangPrintf(stderr, _("cupsfilter: Unable to get job file - %s"),
1371
cupsLastErrorString());
1372
unlink(TempFile);
1373
exit(1);
1374
}
1375
}
1376
1377
1378
/*
1379
* 'open_pipe()' - Create a pipe which is closed on exec.
1380
*/
1381
1382
static int /* O - 0 on success, -1 on error */
1383
open_pipe(int *fds) /* O - Pipe file descriptors (2) */
1384
{
1385
/*
1386
* Create the pipe...
1387
*/
1388
1389
if (pipe(fds))
1390
{
1391
fds[0] = -1;
1392
fds[1] = -1;
1393
1394
return (-1);
1395
}
1396
1397
/*
1398
* Set the "close on exec" flag on each end of the pipe...
1399
*/
1400
1401
if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
1402
{
1403
close(fds[0]);
1404
close(fds[1]);
1405
1406
fds[0] = -1;
1407
fds[1] = -1;
1408
1409
return (-1);
1410
}
1411
1412
if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
1413
{
1414
close(fds[0]);
1415
close(fds[1]);
1416
1417
fds[0] = -1;
1418
fds[1] = -1;
1419
1420
return (-1);
1421
}
1422
1423
/*
1424
* Return 0 indicating success...
1425
*/
1426
1427
return (0);
1428
}
1429
1430
1431
/*
1432
* 'read_cups_files_conf()' - Read the cups-files.conf file to get the filter settings.
1433
*/
1434
1435
static int /* O - 0 on success, 1 on error */
1436
read_cups_files_conf(
1437
const char *filename) /* I - File to read */
1438
{
1439
cups_file_t *fp; /* cups-files.conf file */
1440
const char *temp; /* Temporary string */
1441
char line[1024], /* Line from file */
1442
*ptr; /* Pointer into line */
1443
int linenum; /* Current line number */
1444
1445
1446
if ((temp = getenv("CUPS_DATADIR")) != NULL)
1447
set_string(&DataDir, temp);
1448
else
1449
set_string(&DataDir, CUPS_DATADIR);
1450
1451
if ((temp = getenv("CUPS_SERVERBIN")) != NULL)
1452
set_string(&ServerBin, temp);
1453
else
1454
set_string(&ServerBin, CUPS_SERVERBIN);
1455
1456
strlcpy(line, filename, sizeof(line));
1457
if ((ptr = strrchr(line, '/')) != NULL)
1458
*ptr = '\0';
1459
else
1460
getcwd(line, sizeof(line));
1461
1462
set_string(&ServerRoot, line);
1463
1464
if ((fp = cupsFileOpen(filename, "r")) != NULL)
1465
{
1466
linenum = 0;
1467
1468
while (cupsFileGetConf(fp, line, sizeof(line), &ptr, &linenum))
1469
{
1470
if (!_cups_strcasecmp(line, "DataDir"))
1471
set_string(&DataDir, ptr);
1472
else if (!_cups_strcasecmp(line, "ServerBin"))
1473
set_string(&ServerBin, ptr);
1474
else if (!_cups_strcasecmp(line, "ServerRoot"))
1475
set_string(&ServerRoot, ptr);
1476
}
1477
1478
cupsFileClose(fp);
1479
}
1480
1481
#if CUPS_SNAP
1482
if ((temp = getenv("PATH")) != NULL)
1483
snprintf(line, sizeof(line), "%s/filter:" CUPS_BINDIR ":" CUPS_SBINDIR ":%s", ServerBin, temp);
1484
else
1485
#endif /* CUPS_SNAP */
1486
snprintf(line, sizeof(line), "%s/filter:" CUPS_BINDIR ":" CUPS_SBINDIR ":/bin:/usr/bin", ServerBin);
1487
set_string(&Path, line);
1488
1489
return (0);
1490
}
1491
1492
1493
/*
1494
* 'set_string()' - Copy and set a string.
1495
*/
1496
1497
static void
1498
set_string(char **s, /* O - Copy of string */
1499
const char *val) /* I - String to copy */
1500
{
1501
if (*s)
1502
free(*s);
1503
1504
*s = strdup(val);
1505
}
1506
1507
1508
/*
1509
* 'sighandler()' - Signal catcher for when we print from stdin...
1510
*/
1511
1512
static void
1513
sighandler(int s) /* I - Signal number */
1514
{
1515
/*
1516
* Remove the temporary file we're using to print a job file...
1517
*/
1518
1519
if (TempFile[0])
1520
unlink(TempFile);
1521
1522
/*
1523
* Exit...
1524
*/
1525
1526
exit(s);
1527
}
1528
1529
1530
/*
1531
* 'usage()' - Show program usage...
1532
*/
1533
1534
static void
1535
usage(const char *opt) /* I - Incorrect option, if any */
1536
{
1537
if (opt)
1538
_cupsLangPrintf(stderr, _("%s: Unknown option \"%c\"."), "cupsfilter", *opt);
1539
1540
_cupsLangPuts(stdout, _("Usage: cupsfilter [ options ] [ -- ] filename"));
1541
_cupsLangPuts(stdout, _("Options:"));
1542
_cupsLangPuts(stdout, _(" --list-filters List filters that will be used."));
1543
_cupsLangPuts(stdout, _(" -D Remove the input file when finished."));
1544
_cupsLangPuts(stdout, _(" -P filename.ppd Set PPD file."));
1545
_cupsLangPuts(stdout, _(" -U username Specify username."));
1546
_cupsLangPuts(stdout, _(" -c cups-files.conf Set cups-files.conf file to use."));
1547
_cupsLangPuts(stdout, _(" -d printer Use the named printer."));
1548
_cupsLangPuts(stdout, _(" -e Use every filter from the PPD file."));
1549
_cupsLangPuts(stdout, _(" -i mime/type Set input MIME type (otherwise auto-typed)."));
1550
_cupsLangPuts(stdout, _(" -j job-id[,N] Filter file N from the specified job (default is file 1)."));
1551
_cupsLangPuts(stdout, _(" -m mime/type Set output MIME type (otherwise application/pdf)."));
1552
_cupsLangPuts(stdout, _(" -n copies Set number of copies."));
1553
_cupsLangPuts(stdout, _(" -o name=value Set option(s)."));
1554
_cupsLangPuts(stdout, _(" -p filename.ppd Set PPD file."));
1555
_cupsLangPuts(stdout, _(" -t title Set title."));
1556
_cupsLangPuts(stdout, _(" -u Remove the PPD file when finished."));
1557
1558
exit(1);
1559
}
1560
1561