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