]> git.ipfire.org Git - thirdparty/cups.git/blob - systemv/lp.c
Load cups into easysw/current.
[thirdparty/cups.git] / systemv / lp.c
1 /*
2 * "$Id: lp.c 5235 2006-03-06 13:02:23Z mike $"
3 *
4 * "lp" command for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * main() - Parse options and send files for printing.
27 * restart_job() - Restart a job.
28 * set_job_attrs() - Set job attributes.
29 * sighandler() - Signal catcher for when we print from stdin...
30 */
31
32 /*
33 * Include necessary headers...
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <cups/string.h>
40 #include <cups/cups.h>
41 #include <cups/i18n.h>
42
43
44 #ifndef WIN32
45 # include <unistd.h>
46 # include <signal.h>
47
48
49 /*
50 * Local functions.
51 */
52
53 void sighandler(int);
54 #endif /* !WIN32 */
55 int restart_job(const char *command, int job_id);
56 int set_job_attrs(const char *command, int job_id, int num_options,
57 cups_option_t *options);
58
59
60 /*
61 * Globals...
62 */
63
64 char tempfile[1024]; /* Temporary file for printing from stdin */
65
66
67 /*
68 * 'main()' - Parse options and send files for printing.
69 */
70
71 int
72 main(int argc, /* I - Number of command-line arguments */
73 char *argv[]) /* I - Command-line arguments */
74 {
75 int i, j; /* Looping vars */
76 int job_id; /* Job ID */
77 char *printer, /* Printer name */
78 *instance, /* Instance name */
79 *val, /* Option value */
80 *title; /* Job title */
81 int priority; /* Job priority (1-100) */
82 int num_copies; /* Number of copies per file */
83 int num_files; /* Number of files to print */
84 const char *files[1000]; /* Files to print */
85 int num_dests; /* Number of destinations */
86 cups_dest_t *dests, /* Destinations */
87 *dest; /* Selected destination */
88 int num_options; /* Number of options */
89 cups_option_t *options; /* Options */
90 int end_options; /* No more options? */
91 int silent; /* Silent or verbose output? */
92 char buffer[8192]; /* Copy buffer */
93 ssize_t bytes; /* Bytes copied */
94 off_t filesize; /* Size of temp file */
95 int temp; /* Temporary file descriptor */
96 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
97 struct sigaction action; /* Signal action */
98 struct sigaction oldaction; /* Old signal action */
99 #endif /* HAVE_SIGACTION && !HAVE_SIGSET*/
100
101
102 #ifdef __sun
103 /*
104 * Solaris does some rather strange things to re-queue remote print
105 * jobs. On bootup, the "lp" command is run as "printd" to re-spool
106 * any remote jobs in /var/spool/print. Since CUPS doesn't need this
107 * nonsense, we just need to add the necessary check here to prevent
108 * lp from causing boot problems...
109 */
110
111 if ((val = strrchr(argv[0], '/')) != NULL)
112 val ++;
113 else
114 val = argv[0];
115
116 if (!strcmp(val, "printd"))
117 return (0);
118 #endif /* __sun */
119
120 silent = 0;
121 printer = NULL;
122 num_dests = 0;
123 dests = NULL;
124 num_options = 0;
125 options = NULL;
126 num_files = 0;
127 title = NULL;
128 job_id = 0;
129 end_options = 0;
130
131 for (i = 1; i < argc; i ++)
132 if (argv[i][0] == '-' && argv[i][1] && !end_options)
133 switch (argv[i][1])
134 {
135 case 'E' : /* Encrypt */
136 #ifdef HAVE_SSL
137 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
138 #else
139 _cupsLangPrintf(stderr,
140 _("%s: Sorry, no encryption support compiled in!\n"),
141 argv[0]);
142 #endif /* HAVE_SSL */
143 break;
144
145 case 'U' : /* Username */
146 if (argv[i][2] != '\0')
147 cupsSetUser(argv[i] + 2);
148 else
149 {
150 i ++;
151 if (i >= argc)
152 {
153 _cupsLangPrintf(stderr,
154 _("%s: Error - expected username after "
155 "\'-U\' option!\n"),
156 argv[0]);
157 return (1);
158 }
159
160 cupsSetUser(argv[i]);
161 }
162 break;
163
164 case 'c' : /* Copy to spool dir (always enabled) */
165 break;
166
167 case 'd' : /* Destination printer or class */
168 if (argv[i][2] != '\0')
169 printer = argv[i] + 2;
170 else
171 {
172 i ++;
173
174 if (i >= argc)
175 {
176 _cupsLangPrintf(stderr,
177 _("%s: Error - expected destination after "
178 "\'-d\' option!\n"),
179 argv[0]);
180 return (1);
181 }
182
183 printer = argv[i];
184 }
185
186 if ((instance = strrchr(printer, '/')) != NULL)
187 *instance++ = '\0';
188
189 if (num_dests == 0)
190 num_dests = cupsGetDests(&dests);
191
192 if ((dest = cupsGetDest(printer, instance, num_dests, dests)) != NULL)
193 {
194 for (j = 0; j < dest->num_options; j ++)
195 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
196 num_options = cupsAddOption(dest->options[j].name,
197 dest->options[j].value,
198 num_options, &options);
199 }
200 break;
201
202 case 'f' : /* Form */
203 if (!argv[i][2])
204 {
205 i ++;
206
207 if (i >= argc)
208 {
209 _cupsLangPrintf(stderr,
210 _("%s: Error - expected form after \'-f\' "
211 "option!\n"),
212 argv[0]);
213 return (1);
214 }
215 }
216
217 _cupsLangPrintf(stderr, _("%s: Warning - form option ignored!\n"),
218 argv[0]);
219 break;
220
221 case 'h' : /* Destination host */
222 if (argv[i][2] != '\0')
223 cupsSetServer(argv[i] + 2);
224 else
225 {
226 i ++;
227
228 if (i >= argc)
229 {
230 _cupsLangPrintf(stderr,
231 _("%s: Error - expected hostname after "
232 "\'-h\' option!\n"),
233 argv[0]);
234 return (1);
235 }
236
237 cupsSetServer(argv[i]);
238 }
239 break;
240
241 case 'i' : /* Change job */
242 if (argv[i][2])
243 val = argv[i] + 2;
244 else
245 {
246 i ++;
247
248 if (i >= argc)
249 {
250 _cupsLangPrintf(stderr,
251 _("%s: Expected job ID after \'-i\' option!\n"),
252 argv[0]);
253 return (1);
254 }
255
256 val = argv[i];
257 }
258
259 if (num_files > 0)
260 {
261 _cupsLangPrintf(stderr,
262 _("%s: Error - cannot print files and alter "
263 "jobs simultaneously!\n"),
264 argv[0]);
265 return (1);
266 }
267
268 if (strrchr(val, '-') != NULL)
269 job_id = atoi(strrchr(val, '-') + 1);
270 else
271 job_id = atoi(val);
272
273 if (job_id < 0)
274 {
275 _cupsLangPrintf(stderr, _("%s: Error - bad job ID!\n"), argv[0]);
276 break;
277 }
278 break;
279
280 case 'm' : /* Send email when job is done */
281 #ifdef __sun
282 case 'p' : /* Notify on completion */
283 #endif /* __sun */
284 case 'w' : /* Write to console or email */
285 {
286 char email[1024]; /* EMail address */
287
288
289 snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(),
290 httpGetHostname(NULL, buffer, sizeof(buffer)));
291 num_options = cupsAddOption("notify-recipient", email,
292 num_options, &options);
293 }
294
295 silent = 1;
296 break;
297
298 case 'n' : /* Number of copies */
299 if (argv[i][2] != '\0')
300 num_copies = atoi(argv[i] + 2);
301 else
302 {
303 i ++;
304
305 if (i >= argc)
306 {
307 _cupsLangPrintf(stderr,
308 _("%s: Error - expected copies after "
309 "\'-n\' option!\n"),
310 argv[0]);
311 return (1);
312 }
313
314 num_copies = atoi(argv[i]);
315 }
316
317 sprintf(buffer, "%d", num_copies);
318 num_options = cupsAddOption("copies", buffer, num_options, &options);
319 break;
320
321 case 'o' : /* Option */
322 if (argv[i][2] != '\0')
323 num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
324 else
325 {
326 i ++;
327
328 if (i >= argc)
329 {
330 _cupsLangPrintf(stderr,
331 _("%s: Error - expected option string after "
332 "\'-o\' option!\n"),
333 argv[0]);
334 return (1);
335 }
336
337 num_options = cupsParseOptions(argv[i], num_options, &options);
338 }
339 break;
340
341 #ifndef __sun
342 case 'p' : /* Queue priority */
343 #endif /* !__sun */
344 case 'q' : /* Queue priority */
345 if (argv[i][2] != '\0')
346 priority = atoi(argv[i] + 2);
347 else
348 {
349 if ((i + 1) >= argc)
350 {
351 _cupsLangPrintf(stderr,
352 _("%s: Error - expected priority after "
353 "\'-%c\' option!\n"),
354 argv[0], argv[i][1]);
355 return (1);
356 }
357
358 i ++;
359
360 priority = atoi(argv[i]);
361 }
362
363 /*
364 * For 100% Solaris compatibility, need to add:
365 *
366 * priority = 99 * (39 - priority) / 39 + 1;
367 *
368 * However, to keep CUPS lp the same across all platforms
369 * we will break compatibility this far...
370 */
371
372 if (priority < 1 || priority > 100)
373 {
374 _cupsLangPrintf(stderr,
375 _("%s: Error - priority must be between 1 and "
376 "100.\n"),
377 argv[0]);
378 return (1);
379 }
380
381 sprintf(buffer, "%d", priority);
382 num_options = cupsAddOption("job-priority", buffer, num_options, &options);
383 break;
384
385 case 's' : /* Silent */
386 silent = 1;
387 break;
388
389 case 't' : /* Title */
390 if (argv[i][2] != '\0')
391 title = argv[i] + 2;
392 else
393 {
394 i ++;
395
396 if (i >= argc)
397 {
398 _cupsLangPrintf(stderr,
399 _("%s: Error - expected title after "
400 "\'-t\' option!\n"),
401 argv[0]);
402 return (1);
403 }
404
405 title = argv[i];
406 }
407 break;
408
409 case 'y' : /* mode-list */
410 if (!argv[i][2])
411 {
412 i ++;
413
414 if (i >= argc)
415 {
416 _cupsLangPrintf(stderr,
417 _("%s: Error - expected mode list after "
418 "\'-y\' option!\n"),
419 argv[0]);
420 return (1);
421 }
422 }
423
424 _cupsLangPrintf(stderr,
425 _("%s: Warning - mode option ignored!\n"),
426 argv[0]);
427 break;
428
429 case 'H' : /* Hold job */
430 if (argv[i][2])
431 val = argv[i] + 2;
432 else
433 {
434 i ++;
435
436 if (i >= argc)
437 {
438 _cupsLangPrintf(stderr,
439 _("%s: Error - expected hold name after "
440 "\'-H\' option!\n"),
441 argv[0]);
442 return (1);
443 }
444
445 val = argv[i];
446 }
447
448 if (!strcmp(val, "hold"))
449 num_options = cupsAddOption("job-hold-until", "indefinite",
450 num_options, &options);
451 else if (!strcmp(val, "resume") ||
452 !strcmp(val, "release"))
453 num_options = cupsAddOption("job-hold-until", "no-hold",
454 num_options, &options);
455 else if (!strcmp(val, "immediate"))
456 num_options = cupsAddOption("job-priority", "100",
457 num_options, &options);
458 else if (!strcmp(val, "restart"))
459 {
460 if (job_id < 1)
461 {
462 _cupsLangPrintf(stderr,
463 _("%s: Need job ID (\'-i jobid\') before "
464 "\'-H restart\'!\n"),
465 argv[0]);
466 return (1);
467 }
468
469 if (restart_job(argv[0], job_id))
470 return (1);
471 }
472 else
473 num_options = cupsAddOption("job-hold-until", val,
474 num_options, &options);
475 break;
476
477 case 'P' : /* Page list */
478 if (argv[i][2])
479 val = argv[i] + 2;
480 else
481 {
482 i ++;
483
484 if (i >= argc)
485 {
486 _cupsLangPrintf(stderr,
487 _("%s: Error - expected page list after "
488 "\'-P\' option!\n"),
489 argv[0]);
490 return (1);
491 }
492
493 val = argv[i];
494 }
495
496 num_options = cupsAddOption("page-ranges", val, num_options,
497 &options);
498 break;
499
500 case 'S' : /* character set */
501 if (!argv[i][2])
502 {
503 i ++;
504
505 if (i >= argc)
506 {
507 _cupsLangPrintf(stderr,
508 _("%s: Error - expected character set after "
509 "\'-S\' option!\n"),
510 argv[0]);
511 return (1);
512 }
513 }
514
515 _cupsLangPrintf(stderr,
516 _("%s: Warning - character set option ignored!\n"),
517 argv[0]);
518 break;
519
520 case 'T' : /* Content-Type */
521 if (!argv[i][2])
522 {
523 i ++;
524
525 if (i >= argc)
526 {
527 _cupsLangPrintf(stderr,
528 _("%s: Error - expected content type after "
529 "\'-T\' option!\n"),
530 argv[0]);
531 return (1);
532 }
533 }
534
535 _cupsLangPrintf(stderr,
536 _("%s: Warning - content type option ignored!\n"),
537 argv[0]);
538 break;
539
540 case '-' : /* Stop processing options */
541 end_options = 1;
542 break;
543
544 default :
545 _cupsLangPrintf(stderr, _("%s: Error - unknown option \'%c\'!\n"),
546 argv[0], argv[i][1]);
547 return (1);
548 }
549 else if (!strcmp(argv[i], "-"))
550 {
551 if (num_files || job_id)
552 {
553 _cupsLangPrintf(stderr,
554 _("%s: Error - cannot print from stdin if files or a "
555 "job ID are provided!\n"),
556 argv[0]);
557 return (1);
558 }
559
560 break;
561 }
562 else if (num_files < 1000 && job_id == 0)
563 {
564 /*
565 * Print a file...
566 */
567
568 if (access(argv[i], R_OK) != 0)
569 {
570 _cupsLangPrintf(stderr, _("%s: Error - unable to access \"%s\" - %s\n"),
571 argv[0], argv[i], strerror(errno));
572 return (1);
573 }
574
575 files[num_files] = argv[i];
576 num_files ++;
577
578 if (title == NULL)
579 {
580 if ((title = strrchr(argv[i], '/')) != NULL)
581 title ++;
582 else
583 title = argv[i];
584 }
585 }
586 else
587 _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"\n"),
588 argv[0], argv[i]);
589
590 /*
591 * See if we are altering an existing job...
592 */
593
594 if (job_id)
595 return (set_job_attrs(argv[0], job_id, num_options, options));
596
597 /*
598 * See if we have any files to print; if not, print from stdin...
599 */
600
601 if (printer == NULL)
602 {
603 if (num_dests == 0)
604 num_dests = cupsGetDests(&dests);
605
606 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
607 {
608 printer = dest->name;
609
610 for (j = 0; j < dest->num_options; j ++)
611 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
612 num_options = cupsAddOption(dest->options[j].name,
613 dest->options[j].value,
614 num_options, &options);
615 }
616 }
617
618 if (printer == NULL)
619 {
620 val = NULL;
621
622 if ((printer = getenv("LPDEST")) == NULL)
623 {
624 if ((printer = getenv("PRINTER")) != NULL)
625 {
626 if (!strcmp(printer, "lp"))
627 printer = NULL;
628 else
629 val = "PRINTER";
630 }
631 }
632 else
633 val = "LPDEST";
634
635 if (printer && !cupsGetDest(printer, NULL, num_dests, dests))
636 _cupsLangPrintf(stderr,
637 _("%s: Error - %s environment variable names "
638 "non-existent destination \"%s\"!\n"),
639 argv[0], val, printer);
640 else if (cupsLastError() == IPP_NOT_FOUND)
641 _cupsLangPrintf(stderr,
642 _("%s: Error - no default destination available.\n"),
643 argv[0]);
644 else
645 _cupsLangPrintf(stderr,
646 _("%s: Error - scheduler not responding!\n"),
647 argv[0]);
648
649 return (1);
650 }
651
652 if (num_files > 0)
653 job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
654 else
655 {
656 num_files = 1;
657
658 #ifndef WIN32
659 # if defined(HAVE_SIGSET)
660 sigset(SIGHUP, sighandler);
661 if (sigset(SIGINT, sighandler) == SIG_IGN)
662 sigset(SIGINT, SIG_IGN);
663 sigset(SIGTERM, sighandler);
664 # elif defined(HAVE_SIGACTION)
665 memset(&action, 0, sizeof(action));
666 action.sa_handler = sighandler;
667
668 sigaction(SIGHUP, &action, NULL);
669 sigaction(SIGINT, NULL, &oldaction);
670 if (oldaction.sa_handler != SIG_IGN)
671 sigaction(SIGINT, &action, NULL);
672 sigaction(SIGTERM, &action, NULL);
673 # else
674 signal(SIGHUP, sighandler);
675 if (signal(SIGINT, sighandler) == SIG_IGN)
676 signal(SIGINT, SIG_IGN);
677 signal(SIGTERM, sighandler);
678 # endif
679 #endif /* !WIN32 */
680
681 temp = cupsTempFd(tempfile, sizeof(tempfile));
682
683 if (temp < 0)
684 {
685 _cupsLangPrintf(stderr,
686 _("%s: Error - unable to create temporary file \"%s\" - %s\n"),
687 argv[0], tempfile, strerror(errno));
688 return (1);
689 }
690
691 while ((bytes = read(0, buffer, sizeof(buffer))) > 0)
692 if (write(temp, buffer, bytes) < 0)
693 {
694 _cupsLangPrintf(stderr,
695 _("%s: Error - unable to write to temporary file "
696 "\"%s\" - %s\n"),
697 argv[0], tempfile, strerror(errno));
698 close(temp);
699 unlink(tempfile);
700 return (1);
701 }
702
703 filesize = lseek(temp, 0, SEEK_CUR);
704 close(temp);
705
706 if (filesize <= 0)
707 {
708 _cupsLangPrintf(stderr,
709 _("%s: Error - stdin is empty, so no job has been sent.\n"),
710 argv[0]);
711 unlink(tempfile);
712 return (1);
713 }
714
715 if (title)
716 job_id = cupsPrintFile(printer, tempfile, title, num_options, options);
717 else
718 job_id = cupsPrintFile(printer, tempfile, "(stdin)", num_options, options);
719
720 unlink(tempfile);
721 }
722
723 if (job_id < 1)
724 {
725 _cupsLangPrintf(stderr, "%s: %s\n", argv[0], cupsLastErrorString());
726 return (1);
727 }
728 else if (!silent)
729 _cupsLangPrintf(stdout, _("request id is %s-%d (%d file(s))\n"),
730 printer, job_id, num_files);
731
732 return (0);
733 }
734
735
736 /*
737 * 'restart_job()' - Restart a job.
738 */
739
740 int /* O - Exit status */
741 restart_job(const char *command, /* I - Command name */
742 int job_id) /* I - Job ID */
743 {
744 http_t *http; /* HTTP connection to server */
745 ipp_t *request; /* IPP request */
746 char uri[HTTP_MAX_URI]; /* URI for job */
747
748
749 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
750
751 request = ippNewRequest(IPP_RESTART_JOB);
752
753 sprintf(uri, "ipp://localhost/jobs/%d", job_id);
754
755 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
756 "job-uri", NULL, uri);
757
758 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
759 "requesting-user-name", NULL, cupsUser());
760
761 ippDelete(cupsDoRequest(http, request, "/jobs"));
762
763 if (cupsLastError() > IPP_OK_CONFLICT)
764 {
765 _cupsLangPrintf(stderr, "%s: %s\n", command, cupsLastErrorString());
766 return (1);
767 }
768
769 return (0);
770 }
771
772
773 /*
774 * 'set_job_attrs()' - Set job attributes.
775 */
776
777 int /* O - Exit status */
778 set_job_attrs(const char *command, /* I - Command name */
779 int job_id, /* I - Job ID */
780 int num_options,/* I - Number of options */
781 cups_option_t *options) /* I - Options */
782 {
783 http_t *http; /* HTTP connection to server */
784 ipp_t *request; /* IPP request */
785 char uri[HTTP_MAX_URI]; /* URI for job */
786
787
788 if (num_options == 0)
789 return (0);
790
791 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
792
793 request = ippNewRequest(IPP_SET_JOB_ATTRIBUTES);
794
795 sprintf(uri, "ipp://localhost/jobs/%d", job_id);
796
797 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
798 "job-uri", NULL, uri);
799
800 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
801 "requesting-user-name", NULL, cupsUser());
802
803 cupsEncodeOptions(request, num_options, options);
804
805 ippDelete(cupsDoRequest(http, request, "/jobs"));
806
807 if (cupsLastError() > IPP_OK_CONFLICT)
808 {
809 _cupsLangPrintf(stderr, "%s: %s\n", command, cupsLastErrorString());
810 return (1);
811 }
812
813 return (0);
814 }
815
816
817 #ifndef WIN32
818 /*
819 * 'sighandler()' - Signal catcher for when we print from stdin...
820 */
821
822 void
823 sighandler(int s) /* I - Signal number */
824 {
825 /*
826 * Remove the temporary file we're using to print from stdin...
827 */
828
829 unlink(tempfile);
830
831 /*
832 * Exit...
833 */
834
835 exit(s);
836 }
837 #endif /* !WIN32 */
838
839
840 /*
841 * End of "$Id: lp.c 5235 2006-03-06 13:02:23Z mike $".
842 */