2 * "lp" command for CUPS.
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include <cups/cups-private.h>
22 static int restart_job(const char *command
, int job_id
);
23 static int set_job_attrs(const char *command
, int job_id
, int num_options
, cups_option_t
*options
);
24 static void usage(void) _CUPS_NORETURN
;
28 * 'main()' - Parse options and send files for printing.
32 main(int argc
, /* I - Number of command-line arguments */
33 char *argv
[]) /* I - Command-line arguments */
35 int i
, j
; /* Looping vars */
36 int job_id
; /* Job ID */
37 char *printer
, /* Printer name */
38 *instance
, /* Instance name */
39 *opt
, /* Option pointer */
40 *val
, /* Option value */
41 *title
; /* Job title */
42 int priority
; /* Job priority (1-100) */
43 int num_copies
; /* Number of copies per file */
44 int num_files
; /* Number of files to print */
45 const char *files
[1000]; /* Files to print */
46 cups_dest_t
*dest
; /* Selected destination */
47 int num_options
; /* Number of options */
48 cups_option_t
*options
; /* Options */
49 int end_options
; /* No more options? */
50 int silent
; /* Silent or verbose output? */
51 char buffer
[8192]; /* Copy buffer */
56 * Solaris does some rather strange things to re-queue remote print
57 * jobs. On bootup, the "lp" command is run as "printd" to re-spool
58 * any remote jobs in /var/spool/print. Since CUPS doesn't need this
59 * nonsense, we just need to add the necessary check here to prevent
60 * lp from causing boot problems...
63 if ((val
= strrchr(argv
[0], '/')) != NULL
)
68 if (!strcmp(val
, "printd"))
84 for (i
= 1; i
< argc
; i
++)
86 if (!strcmp(argv
[i
], "--help"))
88 else if (argv
[i
][0] == '-' && argv
[i
][1] && !end_options
)
90 for (opt
= argv
[i
] + 1; *opt
; opt
++)
94 case 'E' : /* Encrypt */
96 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
98 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."), argv
[0]);
102 case 'U' : /* Username */
105 cupsSetUser(opt
+ 1);
106 opt
+= strlen(opt
) - 1;
113 _cupsLangPrintf(stderr
, _("%s: Error - expected username after \"-U\" option."), argv
[0]);
117 cupsSetUser(argv
[i
]);
121 case 'c' : /* Copy to spool dir (always enabled) */
124 case 'd' : /* Destination printer or class */
128 opt
+= strlen(opt
) - 1;
136 _cupsLangPrintf(stderr
, _("%s: Error - expected destination after \"-d\" option."), argv
[0]);
143 if ((instance
= strrchr(printer
, '/')) != NULL
)
146 if ((dest
= cupsGetNamedDest(CUPS_HTTP_DEFAULT
, printer
,
149 for (j
= 0; j
< dest
->num_options
; j
++)
150 if (cupsGetOption(dest
->options
[j
].name
, num_options
,
152 num_options
= cupsAddOption(dest
->options
[j
].name
,
153 dest
->options
[j
].value
,
154 num_options
, &options
);
156 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
157 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
159 _cupsLangPrintf(stderr
,
160 _("%s: Error - add '/version=1.1' to server "
164 else if (cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND
)
166 _cupsLangPrintf(stderr
,
167 _("%s: Error - The printer or class does not exist."), argv
[0]);
172 case 'f' : /* Form */
175 opt
+= strlen(opt
) - 1;
183 _cupsLangPrintf(stderr
, _("%s: Error - expected form after \"-f\" option."), argv
[0]);
188 _cupsLangPrintf(stderr
, _("%s: Warning - form option ignored."), argv
[0]);
191 case 'h' : /* Destination host */
194 cupsSetServer(opt
+ 1);
195 opt
+= strlen(opt
) - 1;
203 _cupsLangPrintf(stderr
, _("%s: Error - expected hostname after \"-h\" option."), argv
[0]);
207 cupsSetServer(argv
[i
]);
211 case 'i' : /* Change job */
215 opt
+= strlen(opt
) - 1;
223 _cupsLangPrintf(stderr
, _("%s: Expected job ID after \"-i\" option."), argv
[0]);
232 _cupsLangPrintf(stderr
, _("%s: Error - cannot print files and alter jobs simultaneously."), argv
[0]);
236 if (strrchr(val
, '-') != NULL
)
237 job_id
= atoi(strrchr(val
, '-') + 1);
243 _cupsLangPrintf(stderr
, _("%s: Error - bad job ID."), argv
[0]);
248 case 'm' : /* Send email when job is done */
250 case 'p' : /* Notify on completion */
252 case 'w' : /* Write to console or email */
254 char email
[1024]; /* EMail address */
257 snprintf(email
, sizeof(email
), "mailto:%s@%s", cupsUser(), httpGetHostname(NULL
, buffer
, sizeof(buffer
)));
258 num_options
= cupsAddOption("notify-recipient-uri", email
, num_options
, &options
);
264 case 'n' : /* Number of copies */
267 num_copies
= atoi(opt
+ 1);
268 opt
+= strlen(opt
) - 1;
276 _cupsLangPrintf(stderr
, _("%s: Error - expected copies after \"-n\" option."), argv
[0]);
280 num_copies
= atoi(argv
[i
]);
285 _cupsLangPrintf(stderr
, _("%s: Error - copies must be 1 or more."), argv
[0]);
289 num_options
= cupsAddIntegerOption("copies", num_copies
, num_options
, &options
);
292 case 'o' : /* Option */
295 num_options
= cupsParseOptions(opt
+ 1, num_options
, &options
);
296 opt
+= strlen(opt
) - 1;
304 _cupsLangPrintf(stderr
, _("%s: Error - expected option=value after \"-o\" option."), argv
[0]);
308 num_options
= cupsParseOptions(argv
[i
], num_options
, &options
);
313 case 'p' : /* Queue priority */
315 case 'q' : /* Queue priority */
318 priority
= atoi(opt
+ 1);
319 opt
+= strlen(opt
) - 1;
325 _cupsLangPrintf(stderr
, _("%s: Error - expected priority after \"-%c\" option."), argv
[0], *opt
);
331 priority
= atoi(argv
[i
]);
335 * For 100% Solaris compatibility, need to add:
337 * priority = 99 * (39 - priority) / 39 + 1;
339 * However, to keep CUPS lp the same across all platforms
340 * we will break compatibility this far...
343 if (priority
< 1 || priority
> 100)
345 _cupsLangPrintf(stderr
, _("%s: Error - priority must be between 1 and 100."), argv
[0]);
349 num_options
= cupsAddIntegerOption("job-priority", priority
, num_options
, &options
);
352 case 's' : /* Silent */
356 case 't' : /* Title */
360 opt
+= strlen(opt
) - 1;
368 _cupsLangPrintf(stderr
, _("%s: Error - expected title after \"-t\" option."), argv
[0]);
376 case 'y' : /* mode-list */
379 opt
+= strlen(opt
) - 1;
387 _cupsLangPrintf(stderr
, _("%s: Error - expected mode list after \"-y\" option."), argv
[0]);
392 _cupsLangPrintf(stderr
, _("%s: Warning - mode option ignored."), argv
[0]);
395 case 'H' : /* Hold job */
399 opt
+= strlen(opt
) - 1;
407 _cupsLangPrintf(stderr
, _("%s: Error - expected hold name after \"-H\" option."), argv
[0]);
414 if (!strcmp(val
, "hold"))
415 num_options
= cupsAddOption("job-hold-until", "indefinite", num_options
, &options
);
416 else if (!strcmp(val
, "resume") || !strcmp(val
, "release"))
417 num_options
= cupsAddOption("job-hold-until", "no-hold", num_options
, &options
);
418 else if (!strcmp(val
, "immediate"))
420 num_options
= cupsAddOption("job-hold-until", "no-hold", num_options
, &options
);
421 num_options
= cupsAddOption("job-priority", "100", num_options
, &options
);
423 else if (!strcmp(val
, "restart"))
427 _cupsLangPrintf(stderr
, _("%s: Need job ID (\"-i jobid\") before \"-H restart\"."), argv
[0]);
431 if (restart_job(argv
[0], job_id
))
435 num_options
= cupsAddOption("job-hold-until", val
, num_options
, &options
);
438 case 'P' : /* Page list */
442 opt
+= strlen(opt
) - 1;
450 _cupsLangPrintf(stderr
, _("%s: Error - expected page list after \"-P\" option."), argv
[0]);
457 num_options
= cupsAddOption("page-ranges", val
, num_options
, &options
);
460 case 'S' : /* character set */
463 opt
+= strlen(opt
) - 1;
471 _cupsLangPrintf(stderr
, _("%s: Error - expected character set after \"-S\" option."), argv
[0]);
476 _cupsLangPrintf(stderr
, _("%s: Warning - character set option ignored."), argv
[0]);
479 case 'T' : /* Content-Type */
482 opt
+= strlen(opt
) - 1;
490 _cupsLangPrintf(stderr
, _("%s: Error - expected content type after \"-T\" option."), argv
[0]);
495 _cupsLangPrintf(stderr
, _("%s: Warning - content type option ignored."), argv
[0]);
498 case '-' : /* Stop processing options */
501 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%s\"."), argv
[0], argv
[i
]);
509 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%c\"."), argv
[0], *opt
);
514 else if (!strcmp(argv
[i
], "-"))
516 if (num_files
|| job_id
)
518 _cupsLangPrintf(stderr
,
519 _("%s: Error - cannot print from stdin if files or a "
520 "job ID are provided."), argv
[0]);
526 else if (num_files
< 1000 && job_id
== 0)
532 if (access(argv
[i
], R_OK
) != 0)
534 _cupsLangPrintf(stderr
, _("%s: Error - unable to access \"%s\" - %s"), argv
[0], argv
[i
], strerror(errno
));
538 files
[num_files
] = argv
[i
];
543 if ((title
= strrchr(argv
[i
], '/')) != NULL
)
551 _cupsLangPrintf(stderr
, _("%s: Error - too many files - \"%s\"."), argv
[0], argv
[i
]);
556 * See if we are altering an existing job...
560 return (set_job_attrs(argv
[0], job_id
, num_options
, options
));
563 * See if we have any files to print; if not, print from stdin...
568 if ((dest
= cupsGetNamedDest(NULL
, NULL
, NULL
)) != NULL
)
570 printer
= dest
->name
;
572 for (j
= 0; j
< dest
->num_options
; j
++)
573 if (cupsGetOption(dest
->options
[j
].name
, num_options
, options
) == NULL
)
574 num_options
= cupsAddOption(dest
->options
[j
].name
,
575 dest
->options
[j
].value
,
576 num_options
, &options
);
578 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
579 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
581 _cupsLangPrintf(stderr
,
582 _("%s: Error - add '/version=1.1' to server "
590 if (!cupsGetNamedDest(NULL
, NULL
, NULL
) && cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND
)
591 _cupsLangPrintf(stderr
, _("%s: Error - %s"), argv
[0], cupsLastErrorString());
593 _cupsLangPrintf(stderr
, _("%s: Error - scheduler not responding."), argv
[0]);
599 job_id
= cupsPrintFiles(printer
, num_files
, files
, title
, num_options
, options
);
600 else if ((job_id
= cupsCreateJob(CUPS_HTTP_DEFAULT
, printer
,
601 title
? title
: "(stdin)",
602 num_options
, options
)) > 0)
604 http_status_t status
; /* Write status */
605 const char *format
; /* Document format */
606 ssize_t bytes
; /* Bytes read */
608 if (cupsGetOption("raw", num_options
, options
))
609 format
= CUPS_FORMAT_RAW
;
610 else if ((format
= cupsGetOption("document-format", num_options
,
612 format
= CUPS_FORMAT_AUTO
;
614 status
= cupsStartDocument(CUPS_HTTP_DEFAULT
, printer
, job_id
, NULL
,
617 while (status
== HTTP_CONTINUE
&&
618 (bytes
= read(0, buffer
, sizeof(buffer
))) > 0)
619 status
= cupsWriteRequestData(CUPS_HTTP_DEFAULT
, buffer
, (size_t)bytes
);
621 if (status
!= HTTP_CONTINUE
)
623 _cupsLangPrintf(stderr
, _("%s: Error - unable to queue from stdin - %s."),
624 argv
[0], httpStatus(status
));
625 cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
);
626 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
630 if (cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
) != IPP_OK
)
632 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
633 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
640 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
644 _cupsLangPrintf(stdout
, _("request id is %s-%d (%d file(s))"),
645 printer
, job_id
, num_files
);
652 * 'restart_job()' - Restart a job.
655 static int /* O - Exit status */
656 restart_job(const char *command
, /* I - Command name */
657 int job_id
) /* I - Job ID */
659 ipp_t
*request
; /* IPP request */
660 char uri
[HTTP_MAX_URI
]; /* URI for job */
663 request
= ippNewRequest(IPP_RESTART_JOB
);
665 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", job_id
);
667 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
668 "job-uri", NULL
, uri
);
670 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
671 "requesting-user-name", NULL
, cupsUser());
673 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
675 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
676 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
678 _cupsLangPrintf(stderr
,
679 _("%s: Error - add '/version=1.1' to server "
683 else if (cupsLastError() > IPP_OK_CONFLICT
)
685 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
694 * 'set_job_attrs()' - Set job attributes.
697 static int /* O - Exit status */
699 const char *command
, /* I - Command name */
700 int job_id
, /* I - Job ID */
701 int num_options
, /* I - Number of options */
702 cups_option_t
*options
) /* I - Options */
704 ipp_t
*request
; /* IPP request */
705 char uri
[HTTP_MAX_URI
]; /* URI for job */
708 if (num_options
== 0)
711 request
= ippNewRequest(IPP_SET_JOB_ATTRIBUTES
);
713 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", job_id
);
715 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
716 "job-uri", NULL
, uri
);
718 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
719 "requesting-user-name", NULL
, cupsUser());
721 cupsEncodeOptions(request
, num_options
, options
);
723 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
725 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
726 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
728 _cupsLangPrintf(stderr
,
729 _("%s: Error - add '/version=1.1' to server "
733 else if (cupsLastError() > IPP_OK_CONFLICT
)
735 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
744 * 'usage()' - Show program usage and exit.
750 _cupsLangPuts(stdout
, _("Usage: lp [options] [--] [file(s)]\n"
751 " lp [options] -i id"));
752 _cupsLangPuts(stdout
, _("Options:"));
753 _cupsLangPuts(stdout
, _("-c Make a copy of the print file(s)"));
754 _cupsLangPuts(stdout
, _("-d destination Specify the destination"));
755 _cupsLangPuts(stdout
, _("-E Encrypt the connection to the server"));
756 _cupsLangPuts(stdout
, _("-h server[:port] Connect to the named server and port"));
757 _cupsLangPuts(stdout
, _("-H HH:MM Hold the job until the specified UTC time"));
758 _cupsLangPuts(stdout
, _("-H hold Hold the job until released/resumed"));
759 _cupsLangPuts(stdout
, _("-H immediate Print the job as soon as possible"));
760 _cupsLangPuts(stdout
, _("-H restart Reprint the job"));
761 _cupsLangPuts(stdout
, _("-H resume Resume a held job"));
762 _cupsLangPuts(stdout
, _("-i id Specify an existing job ID to modify"));
763 _cupsLangPuts(stdout
, _("-m Send an email notification when the job completes"));
764 _cupsLangPuts(stdout
, _("-n num-copies Specify the number of copies to print"));
765 _cupsLangPuts(stdout
, _("-o option[=value] Specify a printer-specific option"));
766 _cupsLangPuts(stdout
, _("-o job-sheets=standard Print a banner page with the job"));
767 _cupsLangPuts(stdout
, _("-o media=size Specify the media size to use"));
768 _cupsLangPuts(stdout
, _("-o number-up=N Specify that input pages should be printed N-up (1, 2, 4, 6, 9, and 16 are supported)"));
769 _cupsLangPuts(stdout
, _("-o orientation-requested=N\n"
770 " Specify portrait (3) or landscape (4) orientation"));
771 _cupsLangPuts(stdout
, _("-o print-quality=N Specify the print quality - draft (3), normal (4), or best (5)"));
772 _cupsLangPuts(stdout
, _("-o sides=one-sided Specify 1-sided printing"));
773 _cupsLangPuts(stdout
, _("-o sides=two-sided-long-edge\n"
774 " Specify 2-sided portrait printing"));
775 _cupsLangPuts(stdout
, _("-o sides=two-sided-short-edge\n"
776 " Specify 2-sided landscape printing"));
777 _cupsLangPuts(stdout
, _("-P page-list Specify a list of pages to print"));
778 _cupsLangPuts(stdout
, _("-q priority Specify the priority from low (1) to high (100)"));
779 _cupsLangPuts(stdout
, _("-s Be silent"));
780 _cupsLangPuts(stdout
, _("-t title Specify the job title"));
781 _cupsLangPuts(stdout
, _("-U username Specify the username to use for authentication"));