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 "
166 case 'f' : /* Form */
169 opt
+= strlen(opt
) - 1;
177 _cupsLangPrintf(stderr
, _("%s: Error - expected form after \"-f\" option."), argv
[0]);
182 _cupsLangPrintf(stderr
, _("%s: Warning - form option ignored."), argv
[0]);
185 case 'h' : /* Destination host */
188 cupsSetServer(opt
+ 1);
189 opt
+= strlen(opt
) - 1;
197 _cupsLangPrintf(stderr
, _("%s: Error - expected hostname after \"-h\" option."), argv
[0]);
201 cupsSetServer(argv
[i
]);
205 case 'i' : /* Change job */
209 opt
+= strlen(opt
) - 1;
217 _cupsLangPrintf(stderr
, _("%s: Expected job ID after \"-i\" option."), argv
[0]);
226 _cupsLangPrintf(stderr
, _("%s: Error - cannot print files and alter jobs simultaneously."), argv
[0]);
230 if (strrchr(val
, '-') != NULL
)
231 job_id
= atoi(strrchr(val
, '-') + 1);
237 _cupsLangPrintf(stderr
, _("%s: Error - bad job ID."), argv
[0]);
242 case 'm' : /* Send email when job is done */
244 case 'p' : /* Notify on completion */
246 case 'w' : /* Write to console or email */
248 char email
[1024]; /* EMail address */
251 snprintf(email
, sizeof(email
), "mailto:%s@%s", cupsUser(), httpGetHostname(NULL
, buffer
, sizeof(buffer
)));
252 num_options
= cupsAddOption("notify-recipient-uri", email
, num_options
, &options
);
258 case 'n' : /* Number of copies */
261 num_copies
= atoi(opt
+ 1);
262 opt
+= strlen(opt
) - 1;
270 _cupsLangPrintf(stderr
, _("%s: Error - expected copies after \"-n\" option."), argv
[0]);
274 num_copies
= atoi(argv
[i
]);
279 _cupsLangPrintf(stderr
, _("%s: Error - copies must be 1 or more."), argv
[0]);
283 sprintf(buffer
, "%d", num_copies
);
284 num_options
= cupsAddOption("copies", buffer
, num_options
,
288 case 'o' : /* Option */
291 num_options
= cupsParseOptions(opt
+ 1, num_options
, &options
);
292 opt
+= strlen(opt
) - 1;
300 _cupsLangPrintf(stderr
, _("%s: Error - expected option=value after \"-o\" option."), argv
[0]);
304 num_options
= cupsParseOptions(argv
[i
], num_options
, &options
);
309 case 'p' : /* Queue priority */
311 case 'q' : /* Queue priority */
314 priority
= atoi(opt
+ 1);
315 opt
+= strlen(opt
) - 1;
321 _cupsLangPrintf(stderr
, _("%s: Error - expected priority after \"-%c\" option."), argv
[0], *opt
);
327 priority
= atoi(argv
[i
]);
331 * For 100% Solaris compatibility, need to add:
333 * priority = 99 * (39 - priority) / 39 + 1;
335 * However, to keep CUPS lp the same across all platforms
336 * we will break compatibility this far...
339 if (priority
< 1 || priority
> 100)
341 _cupsLangPrintf(stderr
, _("%s: Error - priority must be between 1 and 100."), argv
[0]);
345 sprintf(buffer
, "%d", priority
);
346 num_options
= cupsAddOption("job-priority", buffer
, num_options
,
350 case 's' : /* Silent */
354 case 't' : /* Title */
358 opt
+= strlen(opt
) - 1;
366 _cupsLangPrintf(stderr
, _("%s: Error - expected title after \"-t\" option."), argv
[0]);
374 case 'y' : /* mode-list */
377 opt
+= strlen(opt
) - 1;
385 _cupsLangPrintf(stderr
, _("%s: Error - expected mode list after \"-y\" option."), argv
[0]);
390 _cupsLangPrintf(stderr
, _("%s: Warning - mode option ignored."), argv
[0]);
393 case 'H' : /* Hold job */
397 opt
+= strlen(opt
) - 1;
405 _cupsLangPrintf(stderr
, _("%s: Error - expected hold name after \"-H\" option."), argv
[0]);
412 if (!strcmp(val
, "hold"))
413 num_options
= cupsAddOption("job-hold-until", "indefinite", num_options
, &options
);
414 else if (!strcmp(val
, "resume") || !strcmp(val
, "release"))
415 num_options
= cupsAddOption("job-hold-until", "no-hold", num_options
, &options
);
416 else if (!strcmp(val
, "immediate"))
418 num_options
= cupsAddOption("job-hold-until", "no-hold", num_options
, &options
);
419 num_options
= cupsAddOption("job-priority", "100", num_options
, &options
);
421 else if (!strcmp(val
, "restart"))
425 _cupsLangPrintf(stderr
, _("%s: Need job ID (\"-i jobid\") before \"-H restart\"."), argv
[0]);
429 if (restart_job(argv
[0], job_id
))
433 num_options
= cupsAddOption("job-hold-until", val
, num_options
, &options
);
436 case 'P' : /* Page list */
440 opt
+= strlen(opt
) - 1;
448 _cupsLangPrintf(stderr
, _("%s: Error - expected page list after \"-P\" option."), argv
[0]);
455 num_options
= cupsAddOption("page-ranges", val
, num_options
, &options
);
458 case 'S' : /* character set */
461 opt
+= strlen(opt
) - 1;
469 _cupsLangPrintf(stderr
, _("%s: Error - expected character set after \"-S\" option."), argv
[0]);
474 _cupsLangPrintf(stderr
, _("%s: Warning - character set option ignored."), argv
[0]);
477 case 'T' : /* Content-Type */
480 opt
+= strlen(opt
) - 1;
488 _cupsLangPrintf(stderr
, _("%s: Error - expected content type after \"-T\" option."), argv
[0]);
493 _cupsLangPrintf(stderr
, _("%s: Warning - content type option ignored."), argv
[0]);
496 case '-' : /* Stop processing options */
499 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%s\"."), argv
[0], argv
[i
]);
507 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%c\"."), argv
[0], *opt
);
512 else if (!strcmp(argv
[i
], "-"))
514 if (num_files
|| job_id
)
516 _cupsLangPrintf(stderr
,
517 _("%s: Error - cannot print from stdin if files or a "
518 "job ID are provided."), argv
[0]);
524 else if (num_files
< 1000 && job_id
== 0)
530 if (access(argv
[i
], R_OK
) != 0)
532 _cupsLangPrintf(stderr
, _("%s: Error - unable to access \"%s\" - %s"), argv
[0], argv
[i
], strerror(errno
));
536 files
[num_files
] = argv
[i
];
541 if ((title
= strrchr(argv
[i
], '/')) != NULL
)
549 _cupsLangPrintf(stderr
, _("%s: Error - too many files - \"%s\"."), argv
[0], argv
[i
]);
554 * See if we are altering an existing job...
558 return (set_job_attrs(argv
[0], job_id
, num_options
, options
));
561 * See if we have any files to print; if not, print from stdin...
566 if ((dest
= cupsGetNamedDest(NULL
, NULL
, NULL
)) != NULL
)
568 printer
= dest
->name
;
570 for (j
= 0; j
< dest
->num_options
; j
++)
571 if (cupsGetOption(dest
->options
[j
].name
, num_options
, options
) == NULL
)
572 num_options
= cupsAddOption(dest
->options
[j
].name
,
573 dest
->options
[j
].value
,
574 num_options
, &options
);
576 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
577 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
579 _cupsLangPrintf(stderr
,
580 _("%s: Error - add '/version=1.1' to server "
588 if (!cupsGetNamedDest(NULL
, NULL
, NULL
) && cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND
)
589 _cupsLangPrintf(stderr
, _("%s: Error - %s"), argv
[0], cupsLastErrorString());
591 _cupsLangPrintf(stderr
, _("%s: Error - scheduler not responding."), argv
[0]);
597 job_id
= cupsPrintFiles(printer
, num_files
, files
, title
, num_options
, options
);
598 else if ((job_id
= cupsCreateJob(CUPS_HTTP_DEFAULT
, printer
,
599 title
? title
: "(stdin)",
600 num_options
, options
)) > 0)
602 http_status_t status
; /* Write status */
603 const char *format
; /* Document format */
604 ssize_t bytes
; /* Bytes read */
606 if (cupsGetOption("raw", num_options
, options
))
607 format
= CUPS_FORMAT_RAW
;
608 else if ((format
= cupsGetOption("document-format", num_options
,
610 format
= CUPS_FORMAT_AUTO
;
612 status
= cupsStartDocument(CUPS_HTTP_DEFAULT
, printer
, job_id
, NULL
,
615 while (status
== HTTP_CONTINUE
&&
616 (bytes
= read(0, buffer
, sizeof(buffer
))) > 0)
617 status
= cupsWriteRequestData(CUPS_HTTP_DEFAULT
, buffer
, (size_t)bytes
);
619 if (status
!= HTTP_CONTINUE
)
621 _cupsLangPrintf(stderr
, _("%s: Error - unable to queue from stdin - %s."),
622 argv
[0], httpStatus(status
));
623 cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
);
624 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
628 if (cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
) != IPP_OK
)
630 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
631 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
638 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
642 _cupsLangPrintf(stdout
, _("request id is %s-%d (%d file(s))"),
643 printer
, job_id
, num_files
);
650 * 'restart_job()' - Restart a job.
653 static int /* O - Exit status */
654 restart_job(const char *command
, /* I - Command name */
655 int job_id
) /* I - Job ID */
657 ipp_t
*request
; /* IPP request */
658 char uri
[HTTP_MAX_URI
]; /* URI for job */
661 request
= ippNewRequest(IPP_RESTART_JOB
);
663 sprintf(uri
, "ipp://localhost/jobs/%d", job_id
);
665 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
666 "job-uri", NULL
, uri
);
668 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
669 "requesting-user-name", NULL
, cupsUser());
671 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
673 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
674 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
676 _cupsLangPrintf(stderr
,
677 _("%s: Error - add '/version=1.1' to server "
681 else if (cupsLastError() > IPP_OK_CONFLICT
)
683 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
692 * 'set_job_attrs()' - Set job attributes.
695 static int /* O - Exit status */
697 const char *command
, /* I - Command name */
698 int job_id
, /* I - Job ID */
699 int num_options
, /* I - Number of options */
700 cups_option_t
*options
) /* I - Options */
702 ipp_t
*request
; /* IPP request */
703 char uri
[HTTP_MAX_URI
]; /* URI for job */
706 if (num_options
== 0)
709 request
= ippNewRequest(IPP_SET_JOB_ATTRIBUTES
);
711 sprintf(uri
, "ipp://localhost/jobs/%d", job_id
);
713 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
714 "job-uri", NULL
, uri
);
716 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
717 "requesting-user-name", NULL
, cupsUser());
719 cupsEncodeOptions(request
, num_options
, options
);
721 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
723 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
724 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
726 _cupsLangPrintf(stderr
,
727 _("%s: Error - add '/version=1.1' to server "
731 else if (cupsLastError() > IPP_OK_CONFLICT
)
733 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
742 * 'usage()' - Show program usage and exit.
748 _cupsLangPuts(stdout
, _("Usage: lp [options] [--] [file(s)]\n"
749 " lp [options] -i id"));
750 _cupsLangPuts(stdout
, _("Options:"));
751 _cupsLangPuts(stdout
, _("-c Make a copy of the print file(s)"));
752 _cupsLangPuts(stdout
, _("-d destination Specify the destination"));
753 _cupsLangPuts(stdout
, _("-E Encrypt the connection to the server"));
754 _cupsLangPuts(stdout
, _("-h server[:port] Connect to the named server and port"));
755 _cupsLangPuts(stdout
, _("-H HH:MM Hold the job until the specified UTC time"));
756 _cupsLangPuts(stdout
, _("-H hold Hold the job until released/resumed"));
757 _cupsLangPuts(stdout
, _("-H immediate Print the job as soon as possible"));
758 _cupsLangPuts(stdout
, _("-H restart Reprint the job"));
759 _cupsLangPuts(stdout
, _("-H resume Resume a held job"));
760 _cupsLangPuts(stdout
, _("-i id Specify an existing job ID to modify"));
761 _cupsLangPuts(stdout
, _("-m Send an email notification when the job completes"));
762 _cupsLangPuts(stdout
, _("-n num-copies Specify the number of copies to print"));
763 _cupsLangPuts(stdout
, _("-o option[=value] Specify a printer-specific option"));
764 _cupsLangPuts(stdout
, _("-o job-sheets=standard Print a banner page with the job"));
765 _cupsLangPuts(stdout
, _("-o media=size Specify the media size to use"));
766 _cupsLangPuts(stdout
, _("-o number-up=N Specify that input pages should be printed N-up (1, 2, 4, 6, 9, and 16 are supported)"));
767 _cupsLangPuts(stdout
, _("-o orientation-requested=N\n"
768 " Specify portrait (3) or landscape (4) orientation"));
769 _cupsLangPuts(stdout
, _("-o print-quality=N Specify the print quality - draft (3), normal (4), or best (5)"));
770 _cupsLangPuts(stdout
, _("-o sides=one-sided Specify 1-sided printing"));
771 _cupsLangPuts(stdout
, _("-o sides=two-sided-long-edge\n"
772 " Specify 2-sided portrait printing"));
773 _cupsLangPuts(stdout
, _("-o sides=two-sided-short-edge\n"
774 " Specify 2-sided landscape printing"));
775 _cupsLangPuts(stdout
, _("-P page-list Specify a list of pages to print"));
776 _cupsLangPuts(stdout
, _("-q priority Specify the priority from low (1) to high (100)"));
777 _cupsLangPuts(stdout
, _("-s Be silent"));
778 _cupsLangPuts(stdout
, _("-t title Specify the job title"));
779 _cupsLangPuts(stdout
, _("-U username Specify the username to use for authentication"));