1 /*
2  * "lp" 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 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;
25 
26 
27 /*
28  * 'main()' - Parse options and send files for printing.
29  */
30 
31 int
main(int argc,char * argv[])32 main(int  argc,				/* I - Number of command-line arguments */
33      char *argv[])			/* I - Command-line arguments */
34 {
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 */
52 
53 
54 #ifdef __sun
55  /*
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...
61   */
62 
63   if ((val = strrchr(argv[0], '/')) != NULL)
64     val ++;
65   else
66     val = argv[0];
67 
68   if (!strcmp(val, "printd"))
69     return (0);
70 #endif /* __sun */
71 
72   _cupsSetLocale(argv);
73 
74   silent      = 0;
75   printer     = NULL;
76   dest        = NULL;
77   num_options = 0;
78   options     = NULL;
79   num_files   = 0;
80   title       = NULL;
81   job_id      = 0;
82   end_options = 0;
83 
84   for (i = 1; i < argc; i ++)
85   {
86     if (!strcmp(argv[i], "--help"))
87       usage();
88     else if (argv[i][0] == '-' && argv[i][1] && !end_options)
89     {
90       for (opt = argv[i] + 1; *opt; opt ++)
91       {
92         switch (*opt)
93 	{
94 	  case 'E' : /* Encrypt */
95 #ifdef HAVE_SSL
96 	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
97 #else
98 	      _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
99 #endif /* HAVE_SSL */
100 	      break;
101 
102 	  case 'U' : /* Username */
103 	      if (opt[1] != '\0')
104 	      {
105 		cupsSetUser(opt + 1);
106 		opt += strlen(opt) - 1;
107 	      }
108 	      else
109 	      {
110 		i ++;
111 		if (i >= argc)
112 		{
113 		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
114 		  usage();
115 		}
116 
117 		cupsSetUser(argv[i]);
118 	      }
119 	      break;
120 
121 	  case 'c' : /* Copy to spool dir (always enabled) */
122 	      break;
123 
124 	  case 'd' : /* Destination printer or class */
125 	      if (opt[1] != '\0')
126 	      {
127 		printer = opt + 1;
128 		opt += strlen(opt) - 1;
129 	      }
130 	      else
131 	      {
132 		i ++;
133 
134 		if (i >= argc)
135 		{
136 		  _cupsLangPrintf(stderr, _("%s: Error - expected destination after \"-d\" option."), argv[0]);
137 		  usage();
138 		}
139 
140 		printer = argv[i];
141 	      }
142 
143 	      if ((instance = strrchr(printer, '/')) != NULL)
144 		*instance++ = '\0';
145 
146 	      if ((dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer,
147 					   instance)) != NULL)
148 	      {
149 		for (j = 0; j < dest->num_options; j ++)
150 		  if (cupsGetOption(dest->options[j].name, num_options,
151 				    options) == NULL)
152 		    num_options = cupsAddOption(dest->options[j].name,
153 						dest->options[j].value,
154 						num_options, &options);
155 	      }
156 	      else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
157 		       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
158 	      {
159 		_cupsLangPrintf(stderr,
160 				_("%s: Error - add '/version=1.1' to server "
161 				  "name."), argv[0]);
162 		return (1);
163 	      }
164 	      break;
165 
166 	  case 'f' : /* Form */
167 	      if (opt[1] != '\0')
168 	      {
169 	        opt += strlen(opt) - 1;
170 	      }
171 	      else
172 	      {
173 		i ++;
174 
175 		if (i >= argc)
176 		{
177 		  _cupsLangPrintf(stderr, _("%s: Error - expected form after \"-f\" option."), argv[0]);
178 		  usage();
179 		}
180 	      }
181 
182 	      _cupsLangPrintf(stderr, _("%s: Warning - form option ignored."), argv[0]);
183 	      break;
184 
185 	  case 'h' : /* Destination host */
186 	      if (opt[1] != '\0')
187 	      {
188 		cupsSetServer(opt + 1);
189 	        opt += strlen(opt) - 1;
190 	      }
191 	      else
192 	      {
193 		i ++;
194 
195 		if (i >= argc)
196 		{
197 		  _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
198 		  usage();
199 		}
200 
201 		cupsSetServer(argv[i]);
202 	      }
203 	      break;
204 
205 	  case 'i' : /* Change job */
206 	      if (opt[1] != '\0')
207 	      {
208 		val = opt + 1;
209 		opt += strlen(opt) - 1;
210 	      }
211 	      else
212 	      {
213 		i ++;
214 
215 		if (i >= argc)
216 		{
217 		  _cupsLangPrintf(stderr, _("%s: Expected job ID after \"-i\" option."), argv[0]);
218 		  usage();
219 		}
220 
221 		val = argv[i];
222 	      }
223 
224 	      if (num_files > 0)
225 	      {
226 		_cupsLangPrintf(stderr, _("%s: Error - cannot print files and alter jobs simultaneously."), argv[0]);
227 		return (1);
228 	      }
229 
230 	      if (strrchr(val, '-') != NULL)
231 		job_id = atoi(strrchr(val, '-') + 1);
232 	      else
233 		job_id = atoi(val);
234 
235 	      if (job_id < 0)
236 	      {
237 		_cupsLangPrintf(stderr, _("%s: Error - bad job ID."), argv[0]);
238 		break;
239 	      }
240 	      break;
241 
242 	  case 'm' : /* Send email when job is done */
243 #ifdef __sun
244 	  case 'p' : /* Notify on completion */
245 #endif /* __sun */
246 	  case 'w' : /* Write to console or email */
247 	      {
248 		char	email[1024];	/* EMail address */
249 
250 
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);
253 	      }
254 
255 	      silent = 1;
256 	      break;
257 
258 	  case 'n' : /* Number of copies */
259 	      if (opt[1] != '\0')
260 	      {
261 		num_copies = atoi(opt + 1);
262 		opt += strlen(opt) - 1;
263 	      }
264 	      else
265 	      {
266 		i ++;
267 
268 		if (i >= argc)
269 		{
270 		  _cupsLangPrintf(stderr, _("%s: Error - expected copies after \"-n\" option."), argv[0]);
271 		  usage();
272 		}
273 
274 		num_copies = atoi(argv[i]);
275 	      }
276 
277 	      if (num_copies < 1)
278 	      {
279 		_cupsLangPrintf(stderr, _("%s: Error - copies must be 1 or more."), argv[0]);
280 		return (1);
281 	      }
282 
283 	      sprintf(buffer, "%d", num_copies);
284 	      num_options = cupsAddOption("copies", buffer, num_options,
285 					  &options);
286 	      break;
287 
288 	  case 'o' : /* Option */
289 	      if (opt[1] != '\0')
290 	      {
291 		num_options = cupsParseOptions(opt + 1, num_options, &options);
292 		opt += strlen(opt) - 1;
293 	      }
294 	      else
295 	      {
296 		i ++;
297 
298 		if (i >= argc)
299 		{
300 		  _cupsLangPrintf(stderr, _("%s: Error - expected option=value after \"-o\" option."), argv[0]);
301 		  usage();
302 		}
303 
304 		num_options = cupsParseOptions(argv[i], num_options, &options);
305 	      }
306 	      break;
307 
308 #ifndef __sun
309 	  case 'p' : /* Queue priority */
310 #endif /* !__sun */
311 	  case 'q' : /* Queue priority */
312 	      if (opt[1] != '\0')
313 	      {
314 		priority = atoi(opt + 1);
315 		opt += strlen(opt) - 1;
316 	      }
317 	      else
318 	      {
319 		if ((i + 1) >= argc)
320 		{
321 		  _cupsLangPrintf(stderr, _("%s: Error - expected priority after \"-%c\" option."), argv[0], *opt);
322 		  usage();
323 		}
324 
325 		i ++;
326 
327 		priority = atoi(argv[i]);
328 	      }
329 
330 	     /*
331 	      * For 100% Solaris compatibility, need to add:
332 	      *
333 	      *   priority = 99 * (39 - priority) / 39 + 1;
334 	      *
335 	      * However, to keep CUPS lp the same across all platforms
336 	      * we will break compatibility this far...
337 	      */
338 
339 	      if (priority < 1 || priority > 100)
340 	      {
341 		_cupsLangPrintf(stderr, _("%s: Error - priority must be between 1 and 100."), argv[0]);
342 		return (1);
343 	      }
344 
345 	      sprintf(buffer, "%d", priority);
346 	      num_options = cupsAddOption("job-priority", buffer, num_options,
347 					  &options);
348 	      break;
349 
350 	  case 's' : /* Silent */
351 	      silent = 1;
352 	      break;
353 
354 	  case 't' : /* Title */
355 	      if (opt[1] != '\0')
356 	      {
357 		title = opt + 1;
358 		opt += strlen(opt) - 1;
359 	      }
360 	      else
361 	      {
362 		i ++;
363 
364 		if (i >= argc)
365 		{
366 		  _cupsLangPrintf(stderr, _("%s: Error - expected title after \"-t\" option."), argv[0]);
367 		  usage();
368 		}
369 
370 		title = argv[i];
371 	      }
372 	      break;
373 
374 	  case 'y' : /* mode-list */
375 	      if (opt[1] != '\0')
376 	      {
377 		opt += strlen(opt) - 1;
378 	      }
379 	      else
380 	      {
381 		i ++;
382 
383 		if (i >= argc)
384 		{
385 		  _cupsLangPrintf(stderr, _("%s: Error - expected mode list after \"-y\" option."), argv[0]);
386 		  usage();
387 		}
388 	      }
389 
390 	      _cupsLangPrintf(stderr, _("%s: Warning - mode option ignored."), argv[0]);
391 	      break;
392 
393 	  case 'H' : /* Hold job */
394 	      if (opt[1] != '\0')
395 	      {
396 		val = opt + 1;
397 		opt += strlen(opt) - 1;
398 	      }
399 	      else
400 	      {
401 		i ++;
402 
403 		if (i >= argc)
404 		{
405 		  _cupsLangPrintf(stderr, _("%s: Error - expected hold name after \"-H\" option."), argv[0]);
406 		  usage();
407 		}
408 
409 		val = argv[i];
410 	      }
411 
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"))
417 	      {
418 		num_options = cupsAddOption("job-hold-until", "no-hold", num_options, &options);
419 		num_options = cupsAddOption("job-priority", "100", num_options, &options);
420 	      }
421 	      else if (!strcmp(val, "restart"))
422 	      {
423 		if (job_id < 1)
424 		{
425 		  _cupsLangPrintf(stderr, _("%s: Need job ID (\"-i jobid\") before \"-H restart\"."), argv[0]);
426 		  return (1);
427 		}
428 
429 		if (restart_job(argv[0], job_id))
430 		  return (1);
431 	      }
432 	      else
433 		num_options = cupsAddOption("job-hold-until", val, num_options, &options);
434 	      break;
435 
436 	  case 'P' : /* Page list */
437 	      if (opt[1] != '\0')
438 	      {
439 		val = opt + 1;
440 		opt += strlen(opt) - 1;
441 	      }
442 	      else
443 	      {
444 		i ++;
445 
446 		if (i >= argc)
447 		{
448 		  _cupsLangPrintf(stderr, _("%s: Error - expected page list after \"-P\" option."), argv[0]);
449 		  usage();
450 		}
451 
452 		val = argv[i];
453 	      }
454 
455 	      num_options = cupsAddOption("page-ranges", val, num_options, &options);
456 	      break;
457 
458 	  case 'S' : /* character set */
459 	      if (opt[1] != '\0')
460 	      {
461 		opt += strlen(opt) - 1;
462 	      }
463 	      else
464 	      {
465 		i ++;
466 
467 		if (i >= argc)
468 		{
469 		  _cupsLangPrintf(stderr, _("%s: Error - expected character set after \"-S\" option."), argv[0]);
470 		  usage();
471 		}
472 	      }
473 
474 	      _cupsLangPrintf(stderr, _("%s: Warning - character set option ignored."), argv[0]);
475 	      break;
476 
477 	  case 'T' : /* Content-Type */
478 	      if (opt[1] != '\0')
479 	      {
480 		opt += strlen(opt) - 1;
481 	      }
482 	      else
483 	      {
484 		i ++;
485 
486 		if (i >= argc)
487 		{
488 		  _cupsLangPrintf(stderr, _("%s: Error - expected content type after \"-T\" option."), argv[0]);
489 		  usage();
490 		}
491 	      }
492 
493 	      _cupsLangPrintf(stderr, _("%s: Warning - content type option ignored."), argv[0]);
494 	      break;
495 
496 	  case '-' : /* Stop processing options */
497 	      if (opt[1] != '\0')
498 	      {
499 		_cupsLangPrintf(stderr, _("%s: Error - unknown option \"%s\"."), argv[0], argv[i]);
500 		usage();
501 	      }
502 
503 	      end_options = 1;
504 	      break;
505 
506 	  default :
507 	      _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], *opt);
508 	      usage();
509 	}
510       }
511     }
512     else if (!strcmp(argv[i], "-"))
513     {
514       if (num_files || job_id)
515       {
516         _cupsLangPrintf(stderr,
517 			_("%s: Error - cannot print from stdin if files or a "
518 		          "job ID are provided."), argv[0]);
519 	return (1);
520       }
521 
522       break;
523     }
524     else if (num_files < 1000 && job_id == 0)
525     {
526      /*
527       * Print a file...
528       */
529 
530       if (access(argv[i], R_OK) != 0)
531       {
532         _cupsLangPrintf(stderr, _("%s: Error - unable to access \"%s\" - %s"), argv[0], argv[i], strerror(errno));
533         return (1);
534       }
535 
536       files[num_files] = argv[i];
537       num_files ++;
538 
539       if (title == NULL)
540       {
541         if ((title = strrchr(argv[i], '/')) != NULL)
542 	  title ++;
543 	else
544           title = argv[i];
545       }
546     }
547     else
548     {
549       _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"."), argv[0], argv[i]);
550     }
551   }
552 
553  /*
554   * See if we are altering an existing job...
555   */
556 
557   if (job_id)
558     return (set_job_attrs(argv[0], job_id, num_options, options));
559 
560  /*
561   * See if we have any files to print; if not, print from stdin...
562   */
563 
564   if (printer == NULL)
565   {
566     if ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL)
567     {
568       printer = dest->name;
569 
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);
575     }
576     else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
577 	     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
578     {
579       _cupsLangPrintf(stderr,
580 		      _("%s: Error - add '/version=1.1' to server "
581 			"name."), argv[0]);
582       return (1);
583     }
584   }
585 
586   if (printer == NULL)
587   {
588     if (!cupsGetNamedDest(NULL, NULL, NULL) && cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND)
589       _cupsLangPrintf(stderr, _("%s: Error - %s"), argv[0], cupsLastErrorString());
590     else
591       _cupsLangPrintf(stderr, _("%s: Error - scheduler not responding."), argv[0]);
592 
593     return (1);
594   }
595 
596   if (num_files > 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)
601   {
602     http_status_t	status;		/* Write status */
603     const char		*format;	/* Document format */
604     ssize_t		bytes;		/* Bytes read */
605 
606     if (cupsGetOption("raw", num_options, options))
607       format = CUPS_FORMAT_RAW;
608     else if ((format = cupsGetOption("document-format", num_options,
609                                      options)) == NULL)
610       format = CUPS_FORMAT_AUTO;
611 
612     status = cupsStartDocument(CUPS_HTTP_DEFAULT, printer, job_id, NULL,
613                                format, 1);
614 
615     while (status == HTTP_CONTINUE &&
616            (bytes = read(0, buffer, sizeof(buffer))) > 0)
617       status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes);
618 
619     if (status != HTTP_CONTINUE)
620     {
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);
625       return (1);
626     }
627 
628     if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
629     {
630       _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
631       cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
632       return (1);
633     }
634   }
635 
636   if (job_id < 1)
637   {
638     _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
639     return (1);
640   }
641   else if (!silent)
642     _cupsLangPrintf(stdout, _("request id is %s-%d (%d file(s))"),
643 		    printer, job_id, num_files);
644 
645   return (0);
646 }
647 
648 
649 /*
650  * 'restart_job()' - Restart a job.
651  */
652 
653 static int				/* O - Exit status */
restart_job(const char * command,int job_id)654 restart_job(const char *command,	/* I - Command name */
655             int        job_id)		/* I - Job ID */
656 {
657   ipp_t		*request;		/* IPP request */
658   char		uri[HTTP_MAX_URI];	/* URI for job */
659 
660 
661   request = ippNewRequest(IPP_RESTART_JOB);
662 
663   sprintf(uri, "ipp://localhost/jobs/%d", job_id);
664 
665   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
666                "job-uri", NULL, uri);
667 
668   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
669                "requesting-user-name", NULL, cupsUser());
670 
671   ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/jobs"));
672 
673   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
674       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
675   {
676     _cupsLangPrintf(stderr,
677 		    _("%s: Error - add '/version=1.1' to server "
678 		      "name."), command);
679     return (1);
680   }
681   else if (cupsLastError() > IPP_OK_CONFLICT)
682   {
683     _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
684     return (1);
685   }
686 
687   return (0);
688 }
689 
690 
691 /*
692  * 'set_job_attrs()' - Set job attributes.
693  */
694 
695 static int				/* O - Exit status */
set_job_attrs(const char * command,int job_id,int num_options,cups_option_t * options)696 set_job_attrs(
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 */
701 {
702   ipp_t		*request;		/* IPP request */
703   char		uri[HTTP_MAX_URI];	/* URI for job */
704 
705 
706   if (num_options == 0)
707     return (0);
708 
709   request = ippNewRequest(IPP_SET_JOB_ATTRIBUTES);
710 
711   sprintf(uri, "ipp://localhost/jobs/%d", job_id);
712 
713   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
714                "job-uri", NULL, uri);
715 
716   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
717                "requesting-user-name", NULL, cupsUser());
718 
719   cupsEncodeOptions(request, num_options, options);
720 
721   ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/jobs"));
722 
723   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
724       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
725   {
726     _cupsLangPrintf(stderr,
727 		    _("%s: Error - add '/version=1.1' to server "
728 		      "name."), command);
729     return (1);
730   }
731   else if (cupsLastError() > IPP_OK_CONFLICT)
732   {
733     _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
734     return (1);
735   }
736 
737   return (0);
738 }
739 
740 
741 /*
742  * 'usage()' - Show program usage and exit.
743  */
744 
745 static void
usage(void)746 usage(void)
747 {
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"));
780 
781 
782   exit(1);
783 }
784