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