2 * "$Id: lp.c 11558 2014-02-06 18:33:34Z msweet $"
4 * "lp" command for CUPS.
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
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/".
17 * Include necessary headers...
20 #include <cups/cups-private.h>
27 int restart_job(const char *command
, int job_id
);
28 int set_job_attrs(const char *command
, int job_id
, int num_options
,
29 cups_option_t
*options
);
33 * 'main()' - Parse options and send files for printing.
37 main(int argc
, /* I - Number of command-line arguments */
38 char *argv
[]) /* I - Command-line arguments */
40 int i
, j
; /* Looping vars */
41 int job_id
; /* Job ID */
42 char *printer
, /* Printer name */
43 *instance
, /* Instance name */
44 *val
, /* Option value */
45 *title
; /* Job title */
46 int priority
; /* Job priority (1-100) */
47 int num_copies
; /* Number of copies per file */
48 int num_files
; /* Number of files to print */
49 const char *files
[1000]; /* Files to print */
50 cups_dest_t
*dest
; /* Selected destination */
51 int num_options
; /* Number of options */
52 cups_option_t
*options
; /* Options */
53 int end_options
; /* No more options? */
54 int silent
; /* Silent or verbose output? */
55 char buffer
[8192]; /* Copy buffer */
60 * Solaris does some rather strange things to re-queue remote print
61 * jobs. On bootup, the "lp" command is run as "printd" to re-spool
62 * any remote jobs in /var/spool/print. Since CUPS doesn't need this
63 * nonsense, we just need to add the necessary check here to prevent
64 * lp from causing boot problems...
67 if ((val
= strrchr(argv
[0], '/')) != NULL
)
72 if (!strcmp(val
, "printd"))
88 for (i
= 1; i
< argc
; i
++)
89 if (argv
[i
][0] == '-' && argv
[i
][1] && !end_options
)
92 case 'E' : /* Encrypt */
94 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
96 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
101 case 'U' : /* Username */
102 if (argv
[i
][2] != '\0')
103 cupsSetUser(argv
[i
] + 2);
109 _cupsLangPrintf(stderr
,
110 _("%s: Error - expected username after \"-U\" "
111 "option."), argv
[0]);
115 cupsSetUser(argv
[i
]);
119 case 'c' : /* Copy to spool dir (always enabled) */
122 case 'd' : /* Destination printer or class */
123 if (argv
[i
][2] != '\0')
124 printer
= argv
[i
] + 2;
131 _cupsLangPrintf(stderr
,
132 _("%s: Error - expected destination after "
133 "\"-d\" option."), argv
[0]);
140 if ((instance
= strrchr(printer
, '/')) != NULL
)
143 if ((dest
= cupsGetNamedDest(CUPS_HTTP_DEFAULT
, printer
,
146 for (j
= 0; j
< dest
->num_options
; j
++)
147 if (cupsGetOption(dest
->options
[j
].name
, num_options
,
149 num_options
= cupsAddOption(dest
->options
[j
].name
,
150 dest
->options
[j
].value
,
151 num_options
, &options
);
153 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
154 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
156 _cupsLangPrintf(stderr
,
157 _("%s: Error - add '/version=1.1' to server "
163 case 'f' : /* Form */
170 _cupsLangPrintf(stderr
,
171 _("%s: Error - expected form after \"-f\" "
178 _cupsLangPrintf(stderr
, _("%s: Warning - form option ignored."),
182 case 'h' : /* Destination host */
183 if (argv
[i
][2] != '\0')
184 cupsSetServer(argv
[i
] + 2);
191 _cupsLangPrintf(stderr
,
192 _("%s: Error - expected hostname after "
193 "\"-h\" option."), argv
[0]);
197 cupsSetServer(argv
[i
]);
201 case 'i' : /* Change job */
210 _cupsLangPrintf(stderr
,
211 _("%s: Expected job ID after \"-i\" option."),
221 _cupsLangPrintf(stderr
,
222 _("%s: Error - cannot print files and alter "
223 "jobs simultaneously."), argv
[0]);
227 if (strrchr(val
, '-') != NULL
)
228 job_id
= atoi(strrchr(val
, '-') + 1);
234 _cupsLangPrintf(stderr
, _("%s: Error - bad job ID."), argv
[0]);
239 case 'm' : /* Send email when job is done */
241 case 'p' : /* Notify on completion */
243 case 'w' : /* Write to console or email */
245 char email
[1024]; /* EMail address */
248 snprintf(email
, sizeof(email
), "mailto:%s@%s", cupsUser(),
249 httpGetHostname(NULL
, buffer
, sizeof(buffer
)));
250 num_options
= cupsAddOption("notify-recipient-uri", email
,
251 num_options
, &options
);
257 case 'n' : /* Number of copies */
258 if (argv
[i
][2] != '\0')
259 num_copies
= atoi(argv
[i
] + 2);
266 _cupsLangPrintf(stderr
,
267 _("%s: Error - expected copies after "
268 "\"-n\" option."), argv
[0]);
272 num_copies
= atoi(argv
[i
]);
275 sprintf(buffer
, "%d", num_copies
);
276 num_options
= cupsAddOption("copies", buffer
, num_options
,
280 case 'o' : /* Option */
281 if (argv
[i
][2] != '\0')
282 num_options
= cupsParseOptions(argv
[i
] + 2, num_options
,
290 _cupsLangPrintf(stderr
,
291 _("%s: Error - expected option=value after "
292 "\"-o\" option."), argv
[0]);
296 num_options
= cupsParseOptions(argv
[i
], num_options
, &options
);
301 case 'p' : /* Queue priority */
303 case 'q' : /* Queue priority */
304 if (argv
[i
][2] != '\0')
305 priority
= atoi(argv
[i
] + 2);
310 _cupsLangPrintf(stderr
,
311 _("%s: Error - expected priority after "
312 "\"-%c\" option."), argv
[0], argv
[i
][1]);
318 priority
= atoi(argv
[i
]);
322 * For 100% Solaris compatibility, need to add:
324 * priority = 99 * (39 - priority) / 39 + 1;
326 * However, to keep CUPS lp the same across all platforms
327 * we will break compatibility this far...
330 if (priority
< 1 || priority
> 100)
332 _cupsLangPrintf(stderr
,
333 _("%s: Error - priority must be between 1 and "
338 sprintf(buffer
, "%d", priority
);
339 num_options
= cupsAddOption("job-priority", buffer
, num_options
,
343 case 's' : /* Silent */
347 case 't' : /* Title */
348 if (argv
[i
][2] != '\0')
356 _cupsLangPrintf(stderr
,
357 _("%s: Error - expected title after "
358 "\"-t\" option."), argv
[0]);
366 case 'y' : /* mode-list */
373 _cupsLangPrintf(stderr
,
374 _("%s: Error - expected mode list after "
375 "\"-y\" option."), argv
[0]);
380 _cupsLangPrintf(stderr
,
381 _("%s: Warning - mode option ignored."), argv
[0]);
384 case 'H' : /* Hold job */
393 _cupsLangPrintf(stderr
,
394 _("%s: Error - expected hold name after "
395 "\"-H\" option."), argv
[0]);
402 if (!strcmp(val
, "hold"))
403 num_options
= cupsAddOption("job-hold-until", "indefinite",
404 num_options
, &options
);
405 else if (!strcmp(val
, "resume") ||
406 !strcmp(val
, "release"))
407 num_options
= cupsAddOption("job-hold-until", "no-hold",
408 num_options
, &options
);
409 else if (!strcmp(val
, "immediate"))
411 num_options
= cupsAddOption("job-hold-until", "no-hold",
412 num_options
, &options
);
413 num_options
= cupsAddOption("job-priority", "100",
414 num_options
, &options
);
416 else if (!strcmp(val
, "restart"))
420 _cupsLangPrintf(stderr
,
421 _("%s: Need job ID (\"-i jobid\") before "
422 "\"-H restart\"."), argv
[0]);
426 if (restart_job(argv
[0], job_id
))
430 num_options
= cupsAddOption("job-hold-until", val
,
431 num_options
, &options
);
434 case 'P' : /* Page list */
443 _cupsLangPrintf(stderr
,
444 _("%s: Error - expected page list after "
445 "\"-P\" option."), argv
[0]);
452 num_options
= cupsAddOption("page-ranges", val
, num_options
,
456 case 'S' : /* character set */
463 _cupsLangPrintf(stderr
,
464 _("%s: Error - expected character set after "
465 "\"-S\" option."), argv
[0]);
470 _cupsLangPrintf(stderr
,
471 _("%s: Warning - character set option ignored."),
475 case 'T' : /* Content-Type */
482 _cupsLangPrintf(stderr
,
483 _("%s: Error - expected content type after "
484 "\"-T\" option."), argv
[0]);
489 _cupsLangPrintf(stderr
,
490 _("%s: Warning - content type option ignored."),
494 case '-' : /* Stop processing options */
497 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%s\"."),
506 _cupsLangPrintf(stderr
, _("%s: Error - unknown option \"%c\"."),
507 argv
[0], argv
[i
][1]);
510 else if (!strcmp(argv
[i
], "-"))
512 if (num_files
|| job_id
)
514 _cupsLangPrintf(stderr
,
515 _("%s: Error - cannot print from stdin if files or a "
516 "job ID are provided."), argv
[0]);
522 else if (num_files
< 1000 && job_id
== 0)
528 if (access(argv
[i
], R_OK
) != 0)
530 _cupsLangPrintf(stderr
, _("%s: Error - unable to access \"%s\" - %s"),
531 argv
[0], argv
[i
], strerror(errno
));
535 files
[num_files
] = argv
[i
];
540 if ((title
= strrchr(argv
[i
], '/')) != NULL
)
547 _cupsLangPrintf(stderr
, _("%s: Error - too many files - \"%s\"."),
551 * See if we are altering an existing job...
555 return (set_job_attrs(argv
[0], job_id
, num_options
, options
));
558 * See if we have any files to print; if not, print from stdin...
563 if ((dest
= cupsGetNamedDest(NULL
, NULL
, NULL
)) != NULL
)
565 printer
= dest
->name
;
567 for (j
= 0; j
< dest
->num_options
; j
++)
568 if (cupsGetOption(dest
->options
[j
].name
, num_options
, options
) == NULL
)
569 num_options
= cupsAddOption(dest
->options
[j
].name
,
570 dest
->options
[j
].value
,
571 num_options
, &options
);
573 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
574 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
576 _cupsLangPrintf(stderr
,
577 _("%s: Error - add '/version=1.1' to server "
587 if ((printer
= getenv("LPDEST")) == NULL
)
589 if ((printer
= getenv("PRINTER")) != NULL
)
591 if (!strcmp(printer
, "lp"))
600 if (printer
&& !cupsGetNamedDest(NULL
, printer
, NULL
))
601 _cupsLangPrintf(stderr
,
602 _("%s: Error - %s environment variable names "
603 "non-existent destination \"%s\"."), argv
[0], val
,
605 else if (cupsLastError() == IPP_NOT_FOUND
)
606 _cupsLangPrintf(stderr
,
607 _("%s: Error - no default destination available."),
610 _cupsLangPrintf(stderr
, _("%s: Error - scheduler not responding."),
617 job_id
= cupsPrintFiles(printer
, num_files
, files
, title
, num_options
, options
);
618 else if ((job_id
= cupsCreateJob(CUPS_HTTP_DEFAULT
, printer
,
619 title
? title
: "(stdin)",
620 num_options
, options
)) > 0)
622 http_status_t status
; /* Write status */
623 const char *format
; /* Document format */
624 ssize_t bytes
; /* Bytes read */
626 if (cupsGetOption("raw", num_options
, options
))
627 format
= CUPS_FORMAT_RAW
;
628 else if ((format
= cupsGetOption("document-format", num_options
,
630 format
= CUPS_FORMAT_AUTO
;
632 status
= cupsStartDocument(CUPS_HTTP_DEFAULT
, printer
, job_id
, NULL
,
635 while (status
== HTTP_CONTINUE
&&
636 (bytes
= read(0, buffer
, sizeof(buffer
))) > 0)
637 status
= cupsWriteRequestData(CUPS_HTTP_DEFAULT
, buffer
, (size_t)bytes
);
639 if (status
!= HTTP_CONTINUE
)
641 _cupsLangPrintf(stderr
, _("%s: Error - unable to queue from stdin - %s."),
642 argv
[0], httpStatus(status
));
643 cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
);
644 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
648 if (cupsFinishDocument(CUPS_HTTP_DEFAULT
, printer
) != IPP_OK
)
650 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
651 cupsCancelJob2(CUPS_HTTP_DEFAULT
, printer
, job_id
, 0);
658 _cupsLangPrintf(stderr
, "%s: %s", argv
[0], cupsLastErrorString());
662 _cupsLangPrintf(stdout
, _("request id is %s-%d (%d file(s))"),
663 printer
, job_id
, num_files
);
670 * 'restart_job()' - Restart a job.
673 int /* O - Exit status */
674 restart_job(const char *command
, /* I - Command name */
675 int job_id
) /* I - Job ID */
677 ipp_t
*request
; /* IPP request */
678 char uri
[HTTP_MAX_URI
]; /* URI for job */
681 request
= ippNewRequest(IPP_RESTART_JOB
);
683 sprintf(uri
, "ipp://localhost/jobs/%d", job_id
);
685 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
686 "job-uri", NULL
, uri
);
688 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
689 "requesting-user-name", NULL
, cupsUser());
691 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
693 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
694 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
696 _cupsLangPrintf(stderr
,
697 _("%s: Error - add '/version=1.1' to server "
701 else if (cupsLastError() > IPP_OK_CONFLICT
)
703 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
712 * 'set_job_attrs()' - Set job attributes.
715 int /* O - Exit status */
716 set_job_attrs(const char *command
, /* I - Command name */
717 int job_id
, /* I - Job ID */
718 int num_options
,/* I - Number of options */
719 cups_option_t
*options
) /* I - Options */
721 ipp_t
*request
; /* IPP request */
722 char uri
[HTTP_MAX_URI
]; /* URI for job */
725 if (num_options
== 0)
728 request
= ippNewRequest(IPP_SET_JOB_ATTRIBUTES
);
730 sprintf(uri
, "ipp://localhost/jobs/%d", job_id
);
732 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
733 "job-uri", NULL
, uri
);
735 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
736 "requesting-user-name", NULL
, cupsUser());
738 cupsEncodeOptions(request
, num_options
, options
);
740 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT
, request
, "/jobs"));
742 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST
||
743 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
745 _cupsLangPrintf(stderr
,
746 _("%s: Error - add '/version=1.1' to server "
750 else if (cupsLastError() > IPP_OK_CONFLICT
)
752 _cupsLangPrintf(stderr
, "%s: %s", command
, cupsLastErrorString());
761 * End of "$Id: lp.c 11558 2014-02-06 18:33:34Z msweet $".