2 * "lp" command for CUPS.
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * Include necessary headers...
18 #include <cups/cups-private.h>
25 int restart_job(const char *command
, int job_id
);
26 int set_job_attrs(const char *command
, int job_id
, int num_options
,
27 cups_option_t
*options
);
31 * 'main()' - Parse options and send files for printing.
35 main(int argc
, /* I - Number of command-line arguments */
36 char *argv
[]) /* I - Command-line arguments */
38 int i
, j
; /* Looping vars */
39 int job_id
; /* Job ID */
40 char *printer
, /* Printer name */
41 *instance
, /* Instance name */
42 *val
, /* Option value */
43 *title
; /* Job title */
44 int priority
; /* Job priority (1-100) */
45 int num_copies
; /* Number of copies per file */
46 int num_files
; /* Number of files to print */
47 const char *files
[1000]; /* Files to print */
48 cups_dest_t
*dest
; /* Selected destination */
49 int num_options
; /* Number of options */
50 cups_option_t
*options
; /* Options */
51 int end_options
; /* No more options? */
52 int silent
; /* Silent or verbose output? */
53 char buffer
[8192]; /* Copy buffer */
58 * Solaris does some rather strange things to re-queue remote print
59 * jobs. On bootup, the "lp" command is run as "printd" to re-spool
60 * any remote jobs in /var/spool/print. Since CUPS doesn't need this
61 * nonsense, we just need to add the necessary check here to prevent
62 * lp from causing boot problems...
65 if ((val
= strrchr(argv
[0], '/')) != NULL
)
70 if (!strcmp(val
, "printd"))
86 for (i
= 1; i
< argc
; i
++)
87 if (argv
[i
][0] == '-' && argv
[i
][1] && !end_options
)
90 case 'E' : /* Encrypt */
92 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
94 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
99 case 'U' : /* Username */
100 if (argv
[i
][2] != '\0')
101 cupsSetUser(argv
[i
] + 2);
107 _cupsLangPrintf(stderr
,
108 _("%s: Error - expected username after \"-U\" "
109 "option."), argv
[0]);
113 cupsSetUser(argv
[i
]);
117 case 'c' : /* Copy to spool dir (always enabled) */
120 case 'd' : /* Destination printer or class */
121 if (argv
[i
][2] != '\0')
122 printer
= argv
[i
] + 2;
129 _cupsLangPrintf(stderr
,
130 _("%s: Error - expected destination after "
131 "\"-d\" option."), argv
[0]);
138 if ((instance
= strrchr(printer
, '/')) != NULL
)
141 if ((dest
= cupsGetNamedDest(CUPS_HTTP_DEFAULT
, printer
,
144 for (j
= 0; j
< dest
->num_options
; j
++)
145 if (cupsGetOption(dest
->options
[j
].name
, num_options
,
147 num_options
= cupsAddOption(dest
->options
[j
].name
,
148 dest
->options
[j
].value
,
149 num_options
, &options
);
151 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
152 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
154 _cupsLangPrintf(stderr
,
155 _("%s: Error - add '/version=1.1' to server "
161 case 'f' : /* Form */
168 _cupsLangPrintf(stderr
,
169 _("%s: Error - expected form after \"-f\" "
176 _cupsLangPrintf(stderr
, _("%s: Warning - form option ignored."),
180 case 'h' : /* Destination host */
181 if (argv
[i
][2] != '\0')
182 cupsSetServer(argv
[i
] + 2);
189 _cupsLangPrintf(stderr
,
190 _("%s: Error - expected hostname after "
191 "\"-h\" option."), argv
[0]);
195 cupsSetServer(argv
[i
]);
199 case 'i' : /* Change job */
208 _cupsLangPrintf(stderr
,
209 _("%s: Expected job ID after \"-i\" option."),
219 _cupsLangPrintf(stderr
,
220 _("%s: Error - cannot print files and alter "
221 "jobs simultaneously."), argv
[0]);
225 if (strrchr(val
, '-') != NULL
)
226 job_id
= atoi(strrchr(val
, '-') + 1);
232 _cupsLangPrintf(stderr
, _("%s: Error - bad job ID."), argv
[0]);
237 case 'm' : /* Send email when job is done */
239 case 'p' : /* Notify on completion */
241 case 'w' : /* Write to console or email */
243 char email
[1024]; /* EMail address */
246 snprintf(email
, sizeof(email
), "mailto:%s@%s", cupsUser(),
247 httpGetHostname(NULL
, buffer
, sizeof(buffer
)));
248 num_options
= cupsAddOption("notify-recipient-uri", email
,
249 num_options
, &options
);
255 case 'n' : /* Number of copies */
256 if (argv
[i
][2] != '\0')
257 num_copies
= atoi(argv
[i
] + 2);
264 _cupsLangPrintf(stderr
,
265 _("%s: Error - expected copies after "
266 "\"-n\" option."), argv
[0]);
270 num_copies
= atoi(argv
[i
]);
273 sprintf(buffer
, "%d", num_copies
);
274 num_options
= cupsAddOption("copies", buffer
, num_options
,
278 case 'o' : /* Option */
279 if (argv
[i
][2] != '\0')
280 num_options
= cupsParseOptions(argv
[i
] + 2, num_options
,
288 _cupsLangPrintf(stderr
,
289 _("%s: Error - expected option=value after "
290 "\"-o\" option."), argv
[0]);
294 num_options
= cupsParseOptions(argv
[i
], num_options
, &options
);
299 case 'p' : /* Queue priority */
301 case 'q' : /* Queue priority */
302 if (argv
[i
][2] != '\0')
303 priority
= atoi(argv
[i
] + 2);
308 _cupsLangPrintf(stderr
,
309 _("%s: Error - expected priority after "
310 "\"-%c\" option."), argv
[0], argv
[i
][1]);
316 priority
= atoi(argv
[i
]);
320 * For 100% Solaris compatibility, need to add:
322 * priority = 99 * (39 - priority) / 39 + 1;
324 * However, to keep CUPS lp the same across all platforms
325 * we will break compatibility this far...
328 if (priority
< 1 || priority
> 100)
330 _cupsLangPrintf(stderr
,
331 _("%s: Error - priority must be between 1 and "
336 sprintf(buffer
, "%d", priority
);
337 num_options
= cupsAddOption("job-priority", buffer
, num_options
,
341 case 's' : /* Silent */
345 case 't' : /* Title */
346 if (argv
[i
][2] != '\0')
354 _cupsLangPrintf(stderr
,
355 _("%s: Error - expected title after "
356 "\"-t\" option."), argv
[0]);
364 case 'y' : /* mode-list */
371 _cupsLangPrintf(stderr
,
372 _("%s: Error - expected mode list after "
373 "\"-y\" option."), argv
[0]);
378 _cupsLangPrintf(stderr
,
379 _("%s: Warning - mode option ignored."), argv
[0]);
382 case 'H' : /* Hold job */
391 _cupsLangPrintf(stderr
,
392 _("%s: Error - expected hold name after "
393 "\"-H\" option."), argv
[0]);
400 if (!strcmp(val
, "hold"))
401 num_options
= cupsAddOption("job-hold-until", "indefinite",
402 num_options
, &options
);
403 else if (!strcmp(val
, "resume") ||
404 !strcmp(val
, "release"))
405 num_options
= cupsAddOption("job-hold-until", "no-hold",
406 num_options
, &options
);
407 else if (!strcmp(val
, "immediate"))
409 num_options
= cupsAddOption("job-hold-until", "no-hold",
410 num_options
, &options
);
411 num_options
= cupsAddOption("job-priority", "100",
412 num_options
, &options
);
414 else if (!strcmp(val
, "restart"))
418 _cupsLangPrintf(stderr
,
419 _("%s: Need job ID (\"-i jobid\") before "
420 "\"-H restart\"."), argv
[0]);
424 if (restart_job(argv
[0], job_id
))
428 num_options
= cupsAddOption("job-hold-until", val
,
429 num_options
, &options
);
432 case 'P' : /* Page list */
441 _cupsLangPrintf(stderr
,
442 _("%s: Error - expected page list after "
443 "\"-P\" option."), argv
[0]);
450 num_options
= cupsAddOption("page-ranges", val
, num_options
,
454 case 'S' : /* character set */
461 _cupsLangPrintf(stderr
,
462 _("%s: Error - expected character set after "
463 "\"-S\" option."), argv
[0]);
468 _cupsLangPrintf(stderr
,
469 _("%s: Warning - character set option ignored."),
473 case 'T' : /* Content-Type */
480 _cupsLangPrintf(stderr
,
481 _("%s: Error - expected content type after "
482 "\"-T\" option."), argv
[0]);
487 _cupsLangPrintf(stderr
,
488 _("%s: Warning - content type option ignored."),
492 case '-' : /* Stop processing options */
495 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%s\"."),
504 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%c\"."),
505 argv
[0], argv
[i
][1]);
508 else if (!strcmp(argv
[i
], "-"))
510 if (num_files
|| job_id
)
512 _cupsLangPrintf(stderr
,
513 _("%s: Error - cannot print from stdin if files or a "
514 "job ID are provided."), argv
[0]);
520 else if (num_files
< 1000 && job_id
== 0)
526 if (access(argv
[i
], R_OK
) != 0)
528 _cupsLangPrintf(stderr
, _("%s: Error - unable to access \"%s\" - %s"),
529 argv
[0], argv
[i
], strerror(errno
));
533 files
[num_files
] = argv
[i
];
538 if ((title
= strrchr(argv
[i
], '/')) != NULL
)
545 _cupsLangPrintf(stderr
, _("%s: Error - too many files - \"%s\"."),
549 * See if we are altering an existing job...
553 return (set_job_attrs(argv
[0], job_id
, num_options
, options
));
556 * See if we have any files to print; if not, print from stdin...
561 if ((dest
= cupsGetNamedDest(NULL
, NULL
, NULL
)) != NULL
)
563 printer
= dest
->name
;
565 for (j
= 0; j
< dest
->num_options
; j
++)
566 if (cupsGetOption(dest
->options
[j
].name
, num_options
, options
) == NULL
)
567 num_options
= cupsAddOption(dest
->options
[j
].name
,
568 dest
->options
[j
].value
,
569 num_options
, &options
);
571 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
572 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
574 _cupsLangPrintf(stderr
,
575 _("%s: Error - add '/version=1.1' to server "
585 if ((printer
= getenv("LPDEST")) == NULL
)
587 if ((printer
= getenv("PRINTER")) != NULL
)
589 if (!strcmp(printer
, "lp"))
598 if (printer
&& !cupsGetNamedDest(NULL
, printer
, NULL
))
599 _cupsLangPrintf(stderr
,
600 _("%s: Error - %s environment variable names "
601 "non-existent destination \"%s\"."), argv
[0], val
,
603 else if (cupsLastError() == IPP_NOT_FOUND
)
604 _cupsLangPrintf(stderr
,
605 _("%s: Error - no default destination available."),
608 _cupsLangPrintf(stderr
, _("%s: Error - scheduler not responding."),
615 job_id
= cupsPrintFiles(printer
, num_files
, files
, title
, num_options
, options
);
616 else if ((job_id
= cupsCreateJob(CUPS_HTTP_DEFAULT
, printer
,
617 title
? title
: "(stdin)",
618 num_options
, options
)) > 0)
620 http_status_t status
; /* Write status */
621 const char *format
; /* Document format */
622 ssize_t bytes
; /* Bytes read */
624 if (cupsGetOption("raw", num_options
, options
))
625 format
= CUPS_FORMAT_RAW
;
626 else if ((format
= cupsGetOption("document-format", num_options
,
628 format
= CUPS_FORMAT_AUTO
;
630 status
= cupsStartDocument(CUPS_HTTP_DEFAULT
, printer
, job_id
, NULL
,
633 while (status
== HTTP_CONTINUE
&&
634 (bytes
= read(0, buffer
, sizeof(buffer
))) > 0)
635 status
= cupsWriteRequestData(CUPS_HTTP_DEFAULT
, buffer
, (size_t)bytes
);
637 if (status
!= HTTP_CONTINUE
)
639 _cupsLangPrintf(stderr
, _("%s: Error - unable to queue from stdin - %s."),
640 argv
[0], httpStatus(status
));
641 cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
);
642 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
646 if (cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
) != IPP_OK
)
648 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
649 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
656 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
660 _cupsLangPrintf(stdout
, _("request id is %s-%d (%d file(s))"),
661 printer
, job_id
, num_files
);
668 * 'restart_job()' - Restart a job.
671 int /* O - Exit status */
672 restart_job(const char *command
, /* I - Command name */
673 int job_id
) /* I - Job ID */
675 ipp_t
*request
; /* IPP request */
676 char uri
[HTTP_MAX_URI
]; /* URI for job */
679 request
= ippNewRequest(IPP_RESTART_JOB
);
681 sprintf(uri
, "ipp://localhost/jobs/%d", job_id
);
683 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
684 "job-uri", NULL
, uri
);
686 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
687 "requesting-user-name", NULL
, cupsUser());
689 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
691 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
692 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
694 _cupsLangPrintf(stderr
,
695 _("%s: Error - add '/version=1.1' to server "
699 else if (cupsLastError() > IPP_OK_CONFLICT
)
701 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
710 * 'set_job_attrs()' - Set job attributes.
713 int /* O - Exit status */
714 set_job_attrs(const char *command
, /* I - Command name */
715 int job_id
, /* I - Job ID */
716 int num_options
,/* I - Number of options */
717 cups_option_t
*options
) /* I - Options */
719 ipp_t
*request
; /* IPP request */
720 char uri
[HTTP_MAX_URI
]; /* URI for job */
723 if (num_options
== 0)
726 request
= ippNewRequest(IPP_SET_JOB_ATTRIBUTES
);
728 sprintf(uri
, "ipp://localhost/jobs/%d", job_id
);
730 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
731 "job-uri", NULL
, uri
);
733 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
734 "requesting-user-name", NULL
, cupsUser());
736 cupsEncodeOptions(request
, num_options
, options
);
738 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
740 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
741 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
743 _cupsLangPrintf(stderr
,
744 _("%s: Error - add '/version=1.1' to server "
748 else if (cupsLastError() > IPP_OK_CONFLICT
)
750 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());