1 /*
2 * "lpr" command for CUPS.
3 *
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include <cups/cups-private.h>
16
17
18 /*
19 * Local functions...
20 */
21
22 static void usage(void) _CUPS_NORETURN;
23
24
25 /*
26 * 'main()' - Parse options and send files for printing.
27 */
28
29 int
main(int argc,char * argv[])30 main(int argc, /* I - Number of command-line arguments */
31 char *argv[]) /* I - Command-line arguments */
32 {
33 int i, j; /* Looping var */
34 int job_id; /* Job ID */
35 char ch; /* Option character */
36 char *printer, /* Destination printer or class */
37 *instance, /* Instance */
38 *opt; /* Option pointer */
39 const char *title; /* Job title */
40 int num_copies; /* Number of copies per file */
41 int num_files; /* Number of files to print */
42 const char *files[1000]; /* Files to print */
43 cups_dest_t *dest; /* Selected destination */
44 int num_options; /* Number of options */
45 cups_option_t *options; /* Options */
46 int deletefile; /* Delete file after print? */
47 char buffer[8192]; /* Copy buffer */
48
49
50 _cupsSetLocale(argv);
51
52 deletefile = 0;
53 printer = NULL;
54 dest = NULL;
55 num_options = 0;
56 options = NULL;
57 num_files = 0;
58 title = NULL;
59
60 for (i = 1; i < argc; i ++)
61 {
62 if (!strcmp(argv[i], "--help"))
63 usage();
64 else if (argv[i][0] == '-')
65 {
66 for (opt = argv[i] + 1; *opt; opt ++)
67 {
68 switch (ch = *opt)
69 {
70 case 'E' : /* Encrypt */
71 #ifdef HAVE_SSL
72 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
73 #else
74 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
75 #endif /* HAVE_SSL */
76 break;
77
78 case 'U' : /* Username */
79 if (opt[1] != '\0')
80 {
81 cupsSetUser(opt + 1);
82 opt += strlen(opt) - 1;
83 }
84 else
85 {
86 i ++;
87 if (i >= argc)
88 {
89 _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
90 usage();
91 }
92
93 cupsSetUser(argv[i]);
94 }
95 break;
96
97 case 'H' : /* Connect to host */
98 if (opt[1] != '\0')
99 {
100 cupsSetServer(opt + 1);
101 opt += strlen(opt) - 1;
102 }
103 else
104 {
105 i ++;
106
107 if (i >= argc)
108 {
109 _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-H\" option."), argv[0]);
110 usage();
111 }
112 else
113 cupsSetServer(argv[i]);
114 }
115 break;
116
117 case '1' : /* TROFF font set 1 */
118 case '2' : /* TROFF font set 2 */
119 case '3' : /* TROFF font set 3 */
120 case '4' : /* TROFF font set 4 */
121 case 'i' : /* indent */
122 case 'w' : /* width */
123 if (opt[1] != '\0')
124 {
125 opt += strlen(opt) - 1;
126 }
127 else
128 {
129 i ++;
130
131 if (i >= argc)
132 {
133 _cupsLangPrintf(stderr,
134 _("%s: Error - expected value after \"-%c\" "
135 "option."), argv[0], ch);
136 usage();
137 }
138 }
139
140 case 'c' : /* CIFPLOT */
141 case 'd' : /* DVI */
142 case 'f' : /* FORTRAN */
143 case 'g' : /* plot */
144 case 'n' : /* Ditroff */
145 case 't' : /* Troff */
146 case 'v' : /* Raster image */
147 _cupsLangPrintf(stderr, _("%s: Warning - \"%c\" format modifier not supported - output may not be correct."), argv[0], ch);
148 break;
149
150 case 'o' : /* Option */
151 if (opt[1] != '\0')
152 {
153 num_options = cupsParseOptions(opt + 1, num_options, &options);
154 opt += strlen(opt) - 1;
155 }
156 else
157 {
158 i ++;
159 if (i >= argc)
160 {
161 _cupsLangPrintf(stderr, _("%s: Error - expected option=value after \"-o\" option."), argv[0]);
162 usage();
163 }
164
165 num_options = cupsParseOptions(argv[i], num_options, &options);
166 }
167 break;
168
169 case 'l' : /* Literal/raw */
170 num_options = cupsAddOption("raw", "true", num_options, &options);
171 break;
172
173 case 'p' : /* Prettyprint */
174 num_options = cupsAddOption("prettyprint", "true", num_options, &options);
175 break;
176
177 case 'h' : /* Suppress burst page */
178 num_options = cupsAddOption("job-sheets", "none", num_options, &options);
179 break;
180
181 case 's' : /* Don't use symlinks */
182 break;
183
184 case 'm' : /* Mail on completion */
185 {
186 char email[1024]; /* EMail address */
187
188 snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(), httpGetHostname(NULL, buffer, sizeof(buffer)));
189 num_options = cupsAddOption("notify-recipient-uri", email, num_options, &options);
190 }
191 break;
192
193 case 'q' : /* Queue file but don't print */
194 num_options = cupsAddOption("job-hold-until", "indefinite", num_options, &options);
195 break;
196
197 case 'r' : /* Remove file after printing */
198 deletefile = 1;
199 break;
200
201 case 'P' : /* Destination printer or class */
202 if (opt[1] != '\0')
203 {
204 printer = opt + 1;
205 opt += strlen(opt) - 1;
206 }
207 else
208 {
209 i ++;
210 if (i >= argc)
211 {
212 _cupsLangPrintf(stderr, _("%s: Error - expected destination after \"-P\" option."), argv[0]);
213 usage();
214 }
215
216 printer = argv[i];
217 }
218
219 if ((instance = strrchr(printer, '/')) != NULL)
220 *instance++ = '\0';
221
222 if ((dest = cupsGetNamedDest(NULL, printer, instance)) != NULL)
223 {
224 for (j = 0; j < dest->num_options; j ++)
225 if (cupsGetOption(dest->options[j].name, num_options,
226 options) == NULL)
227 num_options = cupsAddOption(dest->options[j].name,
228 dest->options[j].value,
229 num_options, &options);
230 }
231 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
232 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
233 {
234 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
235 return (1);
236 }
237 break;
238
239 case '#' : /* Number of copies */
240 if (opt[1] != '\0')
241 {
242 num_copies = atoi(opt + 1);
243 opt += strlen(opt) - 1;
244 }
245 else
246 {
247 i ++;
248 if (i >= argc)
249 {
250 _cupsLangPrintf(stderr, _("%s: Error - expected copies after \"-#\" option."), argv[0]);
251 usage();
252 }
253
254 num_copies = atoi(argv[i]);
255 }
256
257 if (num_copies < 1)
258 {
259 _cupsLangPrintf(stderr, _("%s: Error - copies must be 1 or more."), argv[0]);
260 return (1);
261 }
262
263 sprintf(buffer, "%d", num_copies);
264 num_options = cupsAddOption("copies", buffer, num_options, &options);
265 break;
266
267 case 'C' : /* Class */
268 case 'J' : /* Job name */
269 case 'T' : /* Title */
270 if (opt[1] != '\0')
271 {
272 title = opt + 1;
273 opt += strlen(opt) - 1;
274 }
275 else
276 {
277 i ++;
278 if (i >= argc)
279 {
280 _cupsLangPrintf(stderr, _("%s: Error - expected name after \"-%c\" option."), argv[0], ch);
281 usage();
282 }
283
284 title = argv[i];
285 }
286 break;
287
288 default :
289 _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], *opt);
290 return (1);
291 }
292 }
293 }
294 else if (num_files < 1000)
295 {
296 /*
297 * Print a file...
298 */
299
300 if (access(argv[i], R_OK) != 0)
301 {
302 _cupsLangPrintf(stderr,
303 _("%s: Error - unable to access \"%s\" - %s"),
304 argv[0], argv[i], strerror(errno));
305 return (1);
306 }
307
308 files[num_files] = argv[i];
309 num_files ++;
310
311 if (title == NULL)
312 {
313 if ((title = strrchr(argv[i], '/')) != NULL)
314 title ++;
315 else
316 title = argv[i];
317 }
318 }
319 else
320 {
321 _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"."), argv[0], argv[i]);
322 }
323 }
324
325 /*
326 * See if we have any files to print; if not, print from stdin...
327 */
328
329 if (printer == NULL)
330 {
331 if ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL)
332 {
333 printer = dest->name;
334
335 for (j = 0; j < dest->num_options; j ++)
336 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
337 num_options = cupsAddOption(dest->options[j].name,
338 dest->options[j].value,
339 num_options, &options);
340 }
341 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
342 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
343 {
344 _cupsLangPrintf(stderr,
345 _("%s: Error - add '/version=1.1' to server "
346 "name."), argv[0]);
347 return (1);
348 }
349 }
350
351 if (printer == NULL)
352 {
353 if (!cupsGetNamedDest(NULL, NULL, NULL) && cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND)
354 _cupsLangPrintf(stderr, _("%s: Error - %s"), argv[0], cupsLastErrorString());
355 else
356 _cupsLangPrintf(stderr, _("%s: Error - scheduler not responding."), argv[0]);
357
358 return (1);
359 }
360
361 if (num_files > 0)
362 {
363 job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
364
365 if (deletefile && job_id > 0)
366 {
367 /*
368 * Delete print files after printing...
369 */
370
371 for (i = 0; i < num_files; i ++)
372 unlink(files[i]);
373 }
374 }
375 else if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, printer,
376 title ? title : "(stdin)",
377 num_options, options)) > 0)
378 {
379 http_status_t status; /* Write status */
380 const char *format; /* Document format */
381 ssize_t bytes; /* Bytes read */
382
383 if (cupsGetOption("raw", num_options, options))
384 format = CUPS_FORMAT_RAW;
385 else if ((format = cupsGetOption("document-format", num_options,
386 options)) == NULL)
387 format = CUPS_FORMAT_AUTO;
388
389 status = cupsStartDocument(CUPS_HTTP_DEFAULT, printer, job_id, NULL,
390 format, 1);
391
392 while (status == HTTP_CONTINUE &&
393 (bytes = read(0, buffer, sizeof(buffer))) > 0)
394 status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes);
395
396 if (status != HTTP_CONTINUE)
397 {
398 _cupsLangPrintf(stderr, _("%s: Error - unable to queue from stdin - %s."),
399 argv[0], httpStatus(status));
400 cupsFinishDocument(CUPS_HTTP_DEFAULT, printer);
401 cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
402 return (1);
403 }
404
405 if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
406 {
407 _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
408 cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
409 return (1);
410 }
411 }
412
413 if (job_id < 1)
414 {
415 _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
416 return (1);
417 }
418
419 return (0);
420 }
421
422
423 /*
424 * 'usage()' - Show program usage and exit.
425 */
426
427 static void
usage(void)428 usage(void)
429 {
430 _cupsLangPuts(stdout, _("Usage: lpr [options] [file(s)]"));
431 _cupsLangPuts(stdout, _("Options:"));
432 _cupsLangPuts(stdout, _("-# num-copies Specify the number of copies to print"));
433 _cupsLangPuts(stdout, _("-E Encrypt the connection to the server"));
434 _cupsLangPuts(stdout, _("-H server[:port] Connect to the named server and port"));
435 _cupsLangPuts(stdout, _("-m Send an email notification when the job completes"));
436 _cupsLangPuts(stdout, _("-o option[=value] Specify a printer-specific option"));
437 _cupsLangPuts(stdout, _("-o job-sheets=standard Print a banner page with the job"));
438 _cupsLangPuts(stdout, _("-o media=size Specify the media size to use"));
439 _cupsLangPuts(stdout, _("-o number-up=N Specify that input pages should be printed N-up (1, 2, 4, 6, 9, and 16 are supported)"));
440 _cupsLangPuts(stdout, _("-o orientation-requested=N\n"
441 " Specify portrait (3) or landscape (4) orientation"));
442 _cupsLangPuts(stdout, _("-o print-quality=N Specify the print quality - draft (3), normal (4), or best (5)"));
443 _cupsLangPuts(stdout, _("-o sides=one-sided Specify 1-sided printing"));
444 _cupsLangPuts(stdout, _("-o sides=two-sided-long-edge\n"
445 " Specify 2-sided portrait printing"));
446 _cupsLangPuts(stdout, _("-o sides=two-sided-short-edge\n"
447 " Specify 2-sided landscape printing"));
448 _cupsLangPuts(stdout, _("-P destination Specify the destination"));
449 _cupsLangPuts(stdout, _("-q Specify the job should be held for printing"));
450 _cupsLangPuts(stdout, _("-r Remove the file(s) after submission"));
451 _cupsLangPuts(stdout, _("-T title Specify the job title"));
452 _cupsLangPuts(stdout, _("-U username Specify the username to use for authentication"));
453
454 exit(1);
455 }
456