/*
- * "$Id: lpr.c 6649 2007-07-11 21:46:42Z mike $"
+ * "lpr" command for CUPS.
*
- * "lpr" command for the Common UNIX Printing System (CUPS).
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2007 by Easy Software Products.
*
- * Copyright 2007 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products.
- *
- * These coded instructions, statements, and computer programs are the
- * property of Apple Inc. and are protected by Federal copyright
- * law. Distribution and use rights are outlined in the file "LICENSE.txt"
- * which should have been included with this file. If this file is
- * file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * Contents:
- *
- * main() - Parse options and send files for printing.
- * sighandler() - Signal catcher for when we print from stdin...
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more
+ * information.
*/
/*
* Include necessary headers...
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cups/string.h>
-#include <cups/cups.h>
-#include <cups/i18n.h>
-
-#ifndef WIN32
-# include <unistd.h>
-# include <signal.h>
+#include <cups/cups-private.h>
/*
- * Local functions.
+ * Local functions...
*/
-void sighandler(int);
-#endif /* !WIN32 */
-
-
-/*
- * Globals...
- */
-
-char tempfile[1024]; /* Temporary file for printing from stdin */
+static void usage(void) _CUPS_NORETURN;
/*
int job_id; /* Job ID */
char ch; /* Option character */
char *printer, /* Destination printer or class */
- *instance; /* Instance */
- const char *title, /* Job title */
- *val; /* Environment variable name */
+ *instance, /* Instance */
+ *opt; /* Option pointer */
+ const char *title; /* Job title */
int num_copies; /* Number of copies per file */
int num_files; /* Number of files to print */
const char *files[1000]; /* Files to print */
- int num_dests; /* Number of destinations */
- cups_dest_t *dests, /* Destinations */
- *dest; /* Selected destination */
+ cups_dest_t *dest; /* Selected destination */
int num_options; /* Number of options */
cups_option_t *options; /* Options */
int deletefile; /* Delete file after print? */
char buffer[8192]; /* Copy buffer */
- ssize_t bytes; /* Bytes copied */
- off_t filesize; /* Size of temp file */
- int temp; /* Temporary file descriptor */
-#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
- struct sigaction action; /* Signal action */
- struct sigaction oldaction; /* Old signal action */
-#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
_cupsSetLocale(argv);
deletefile = 0;
printer = NULL;
- num_dests = 0;
- dests = NULL;
+ dest = NULL;
num_options = 0;
options = NULL;
num_files = 0;
title = NULL;
for (i = 1; i < argc; i ++)
- if (argv[i][0] == '-')
- switch (ch = argv[i][1])
+ {
+ if (!strcmp(argv[i], "--help"))
+ usage();
+ else if (argv[i][0] == '-')
+ {
+ for (opt = argv[i] + 1; *opt; opt ++)
{
- case 'E' : /* Encrypt */
+ switch (ch = *opt)
+ {
+ case 'E' : /* Encrypt */
#ifdef HAVE_SSL
- cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
+ cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
#else
- _cupsLangPrintf(stderr,
- _("%s: Sorry, no encryption support compiled in!\n"),
- argv[0]);
+ _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
#endif /* HAVE_SSL */
- break;
-
- case 'U' : /* Username */
- if (argv[i][2] != '\0')
- cupsSetUser(argv[i] + 2);
- else
- {
- i ++;
- if (i >= argc)
+ break;
+
+ case 'U' : /* Username */
+ if (opt[1] != '\0')
{
- _cupsLangPrintf(stderr,
- _("%s: Error - expected username after "
- "\'-U\' option!\n"),
- argv[0]);
- return (1);
+ cupsSetUser(opt + 1);
+ opt += strlen(opt) - 1;
}
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
+ usage();
+ }
+
+ cupsSetUser(argv[i]);
+ }
+ break;
- cupsSetUser(argv[i]);
- }
- break;
-
- case 'H' : /* Connect to host */
- if (argv[i][2] != '\0')
- cupsSetServer(argv[i] + 2);
- else
- {
- i ++;
-
- if (i >= argc)
+ case 'H' : /* Connect to host */
+ if (opt[1] != '\0')
{
- _cupsLangPrintf(stderr,
- _("%s: Error - expected hostname after "
- "\'-H\' option!\n"),
- argv[0]);
- return (1);
- }
+ cupsSetServer(opt + 1);
+ opt += strlen(opt) - 1;
+ }
else
- cupsSetServer(argv[i]);
- }
- break;
-
- case '1' : /* TROFF font set 1 */
- case '2' : /* TROFF font set 2 */
- case '3' : /* TROFF font set 3 */
- case '4' : /* TROFF font set 4 */
- case 'i' : /* indent */
- case 'w' : /* width */
- if (argv[i][2] == '\0')
- {
- i ++;
-
- if (i >= argc)
{
- _cupsLangPrintf(stderr,
- _("%s: Error - expected value after \'-%c\' "
- "option!\n"), argv[0], ch);
- return (1);
+ i ++;
+
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-H\" option."), argv[0]);
+ usage();
+ }
+ else
+ cupsSetServer(argv[i]);
}
- }
-
- case 'c' : /* CIFPLOT */
- case 'd' : /* DVI */
- case 'f' : /* FORTRAN */
- case 'g' : /* plot */
- case 'n' : /* Ditroff */
- case 't' : /* Troff */
- case 'v' : /* Raster image */
- _cupsLangPrintf(stderr,
- _("%s: Warning - \'%c\' format modifier not "
- "supported - output may not be correct!\n"),
- argv[0], ch);
- break;
-
- case 'o' : /* Option */
- if (argv[i][2] != '\0')
- num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
- else
- {
- i ++;
- if (i >= argc)
+ break;
+
+ case '1' : /* TROFF font set 1 */
+ case '2' : /* TROFF font set 2 */
+ case '3' : /* TROFF font set 3 */
+ case '4' : /* TROFF font set 4 */
+ case 'i' : /* indent */
+ case 'w' : /* width */
+ if (opt[1] != '\0')
{
- _cupsLangPrintf(stderr,
- _("%s: error - expected option=value after "
- "\'-o\' option!\n"),
- argv[0]);
- return (1);
+ opt += strlen(opt) - 1;
+ }
+ else
+ {
+ i ++;
+
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr,
+ _("%s: Error - expected value after \"-%c\" "
+ "option."), argv[0], ch);
+ usage();
+ }
}
- num_options = cupsParseOptions(argv[i], num_options, &options);
- }
- break;
-
- case 'l' : /* Literal/raw */
- num_options = cupsAddOption("raw", "", num_options, &options);
- break;
-
- case 'p' : /* Prettyprint */
- num_options = cupsAddOption("prettyprint", "", num_options,
- &options);
- break;
-
- case 'h' : /* Suppress burst page */
- num_options = cupsAddOption("job-sheets", "none", num_options,
- &options);
- break;
-
- case 's' : /* Don't use symlinks */
- break;
-
- case 'm' : /* Mail on completion */
- {
- char email[1024]; /* EMail address */
-
-
- snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(),
- httpGetHostname(NULL, buffer, sizeof(buffer)));
- num_options = cupsAddOption("notify-recipient-uri", email,
- num_options, &options);
- }
- break;
-
- case 'q' : /* Queue file but don't print */
- num_options = cupsAddOption("job-hold-until", "indefinite",
- num_options, &options);
- break;
-
- case 'r' : /* Remove file after printing */
- deletefile = 1;
- break;
-
- case 'P' : /* Destination printer or class */
- if (argv[i][2] != '\0')
- printer = argv[i] + 2;
- else
- {
- i ++;
- if (i >= argc)
+ case 'c' : /* CIFPLOT */
+ case 'd' : /* DVI */
+ case 'f' : /* FORTRAN */
+ case 'g' : /* plot */
+ case 'n' : /* Ditroff */
+ case 't' : /* Troff */
+ case 'v' : /* Raster image */
+ _cupsLangPrintf(stderr, _("%s: Warning - \"%c\" format modifier not supported - output may not be correct."), argv[0], ch);
+ break;
+
+ case 'o' : /* Option */
+ if (opt[1] != '\0')
{
- _cupsLangPrintf(stderr,
- _("%s: Error - expected destination after "
- "\'-P\' option!\n"),
- argv[0]);
- return (1);
+ num_options = cupsParseOptions(opt + 1, num_options, &options);
+ opt += strlen(opt) - 1;
+ }
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - expected option=value after \"-o\" option."), argv[0]);
+ usage();
+ }
+
+ num_options = cupsParseOptions(argv[i], num_options, &options);
+ }
+ break;
+
+ case 'l' : /* Literal/raw */
+ num_options = cupsAddOption("raw", "true", num_options, &options);
+ break;
+
+ case 'p' : /* Prettyprint */
+ num_options = cupsAddOption("prettyprint", "true", num_options, &options);
+ break;
+
+ case 'h' : /* Suppress burst page */
+ num_options = cupsAddOption("job-sheets", "none", num_options, &options);
+ break;
+
+ case 's' : /* Don't use symlinks */
+ break;
+
+ case 'm' : /* Mail on completion */
+ {
+ char email[1024]; /* EMail address */
+
+ snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(), httpGetHostname(NULL, buffer, sizeof(buffer)));
+ num_options = cupsAddOption("notify-recipient-uri", email, num_options, &options);
+ }
+ break;
+
+ case 'q' : /* Queue file but don't print */
+ num_options = cupsAddOption("job-hold-until", "indefinite", num_options, &options);
+ break;
+
+ case 'r' : /* Remove file after printing */
+ deletefile = 1;
+ break;
+
+ case 'P' : /* Destination printer or class */
+ if (opt[1] != '\0')
+ {
+ printer = opt + 1;
+ opt += strlen(opt) - 1;
+ }
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - expected destination after \"-P\" option."), argv[0]);
+ usage();
+ }
+
+ printer = argv[i];
}
- printer = argv[i];
- }
-
- if ((instance = strrchr(printer, '/')) != NULL)
- *instance++ = '\0';
-
- if (num_dests == 0)
- num_dests = cupsGetDests(&dests);
-
- if ((dest = cupsGetDest(printer, instance, num_dests, dests)) != NULL)
- {
- for (j = 0; j < dest->num_options; j ++)
- if (cupsGetOption(dest->options[j].name, num_options,
- options) == NULL)
- num_options = cupsAddOption(dest->options[j].name,
- dest->options[j].value,
- num_options, &options);
- }
- break;
-
- case '#' : /* Number of copies */
- if (argv[i][2] != '\0')
- num_copies = atoi(argv[i] + 2);
- else
- {
- i ++;
- if (i >= argc)
+ if ((instance = strrchr(printer, '/')) != NULL)
+ *instance++ = '\0';
+
+ if ((dest = cupsGetNamedDest(NULL, printer, instance)) != NULL)
+ {
+ for (j = 0; j < dest->num_options; j ++)
+ if (cupsGetOption(dest->options[j].name, num_options,
+ options) == NULL)
+ num_options = cupsAddOption(dest->options[j].name,
+ dest->options[j].value,
+ num_options, &options);
+ }
+ else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
+ cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
{
- _cupsLangPrintf(stderr,
- _("%s: Error - expected copy count after "
- "\'-#\' option!\n"),
- argv[0]);
+ _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
return (1);
}
+ break;
+
+ case '#' : /* Number of copies */
+ if (opt[1] != '\0')
+ {
+ num_copies = atoi(opt + 1);
+ opt += strlen(opt) - 1;
+ }
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - expected copies after \"-#\" option."), argv[0]);
+ usage();
+ }
+
+ num_copies = atoi(argv[i]);
+ }
- num_copies = atoi(argv[i]);
- }
-
- sprintf(buffer, "%d", num_copies);
- num_options = cupsAddOption("copies", buffer, num_options, &options);
- break;
-
- case 'C' : /* Class */
- case 'J' : /* Job name */
- case 'T' : /* Title */
- if (argv[i][2] != '\0')
- title = argv[i] + 2;
- else
- {
- i ++;
- if (i >= argc)
+ if (num_copies < 1)
{
- _cupsLangPrintf(stderr,
- _("%s: Error - expected name after \'-%c\' "
- "option!\n"), argv[0], ch);
+ _cupsLangPrintf(stderr, _("%s: Error - copies must be 1 or more."), argv[0]);
return (1);
}
- title = argv[i];
- }
- break;
+ sprintf(buffer, "%d", num_copies);
+ num_options = cupsAddOption("copies", buffer, num_options, &options);
+ break;
- default :
- _cupsLangPrintf(stderr,
- _("%s: Error - unknown option \'%c\'!\n"),
- argv[0], argv[i][1]);
- return (1);
+ case 'C' : /* Class */
+ case 'J' : /* Job name */
+ case 'T' : /* Title */
+ if (opt[1] != '\0')
+ {
+ title = opt + 1;
+ opt += strlen(opt) - 1;
+ }
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - expected name after \"-%c\" option."), argv[0], ch);
+ usage();
+ }
+
+ title = argv[i];
+ }
+ break;
+
+ default :
+ _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], *opt);
+ return (1);
+ }
}
+ }
else if (num_files < 1000)
{
/*
if (access(argv[i], R_OK) != 0)
{
_cupsLangPrintf(stderr,
- _("%s: Error - unable to access \"%s\" - %s\n"),
+ _("%s: Error - unable to access \"%s\" - %s"),
argv[0], argv[i], strerror(errno));
return (1);
}
}
}
else
- _cupsLangPrintf(stderr,
- _("%s: Error - too many files - \"%s\"\n"),
- argv[0], argv[i]);
+ {
+ _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"."), argv[0], argv[i]);
+ }
+ }
+
/*
* See if we have any files to print; if not, print from stdin...
*/
if (printer == NULL)
{
- if (num_dests == 0)
- num_dests = cupsGetDests(&dests);
-
- if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
+ if ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL)
{
printer = dest->name;
dest->options[j].value,
num_options, &options);
}
+ else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
+ cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
+ {
+ _cupsLangPrintf(stderr,
+ _("%s: Error - add '/version=1.1' to server "
+ "name."), argv[0]);
+ return (1);
+ }
}
if (printer == NULL)
{
- val = NULL;
-
- if ((printer = getenv("LPDEST")) == NULL)
- {
- if ((printer = getenv("PRINTER")) != NULL)
- {
- if (!strcmp(printer, "lp"))
- printer = NULL;
- else
- val = "PRINTER";
- }
- }
- else
- val = "LPDEST";
-
- if (printer && !cupsGetDest(printer, NULL, num_dests, dests))
- _cupsLangPrintf(stderr,
- _("%s: Error - %s environment variable names "
- "non-existent destination \"%s\"!\n"),
- argv[0], val, printer);
- else if (cupsLastError() == IPP_NOT_FOUND)
- _cupsLangPrintf(stderr,
- _("%s: Error - no default destination available.\n"),
- argv[0]);
+ if (!cupsGetNamedDest(NULL, NULL, NULL) && cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND)
+ _cupsLangPrintf(stderr, _("%s: Error - %s"), argv[0], cupsLastErrorString());
else
- _cupsLangPrintf(stderr,
- _("%s: Error - scheduler not responding!\n"),
- argv[0]);
+ _cupsLangPrintf(stderr, _("%s: Error - scheduler not responding."), argv[0]);
return (1);
}
unlink(files[i]);
}
}
- else
+ else if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, printer,
+ title ? title : "(stdin)",
+ num_options, options)) > 0)
{
- num_files = 1;
-
-#ifndef WIN32
-# if defined(HAVE_SIGSET)
- sigset(SIGHUP, sighandler);
- if (sigset(SIGINT, sighandler) == SIG_IGN)
- sigset(SIGINT, SIG_IGN);
- sigset(SIGTERM, sighandler);
-# elif defined(HAVE_SIGACTION)
- memset(&action, 0, sizeof(action));
- action.sa_handler = sighandler;
-
- sigaction(SIGHUP, &action, NULL);
- sigaction(SIGINT, NULL, &oldaction);
- if (oldaction.sa_handler != SIG_IGN)
- sigaction(SIGINT, &action, NULL);
- sigaction(SIGTERM, &action, NULL);
-# else
- signal(SIGHUP, sighandler);
- if (signal(SIGINT, sighandler) == SIG_IGN)
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, sighandler);
-# endif
-#endif /* !WIN32 */
-
- if ((temp = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
- {
- _cupsLangPrintf(stderr,
- _("%s: Error - unable to create temporary file "
- "\"%s\" - %s\n"),
- argv[0], tempfile, strerror(errno));
- return (1);
- }
+ http_status_t status; /* Write status */
+ const char *format; /* Document format */
+ ssize_t bytes; /* Bytes read */
- while ((bytes = read(0, buffer, sizeof(buffer))) > 0)
- if (write(temp, buffer, bytes) < 0)
- {
- _cupsLangPrintf(stderr,
- _("%s: Error - unable to write to temporary file "
- "\"%s\" - %s\n"),
- argv[0], tempfile, strerror(errno));
- close(temp);
- unlink(tempfile);
- return (1);
- }
+ if (cupsGetOption("raw", num_options, options))
+ format = CUPS_FORMAT_RAW;
+ else if ((format = cupsGetOption("document-format", num_options,
+ options)) == NULL)
+ format = CUPS_FORMAT_AUTO;
+
+ status = cupsStartDocument(CUPS_HTTP_DEFAULT, printer, job_id, NULL,
+ format, 1);
- filesize = lseek(temp, 0, SEEK_CUR);
- close(temp);
+ while (status == HTTP_CONTINUE &&
+ (bytes = read(0, buffer, sizeof(buffer))) > 0)
+ status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes);
- if (filesize <= 0)
+ if (status != HTTP_CONTINUE)
{
- _cupsLangPrintf(stderr,
- _("%s: Error - stdin is empty, so no job has been sent.\n"),
- argv[0]);
- unlink(tempfile);
+ _cupsLangPrintf(stderr, _("%s: Error - unable to queue from stdin - %s."),
+ argv[0], httpStatus(status));
+ cupsFinishDocument(CUPS_HTTP_DEFAULT, printer);
+ cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
return (1);
}
- if (title)
- job_id = cupsPrintFile(printer, tempfile, title, num_options, options);
- else
- job_id = cupsPrintFile(printer, tempfile, "(stdin)", num_options, options);
-
- unlink(tempfile);
+ if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
+ {
+ _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
+ cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
+ return (1);
+ }
}
if (job_id < 1)
{
- _cupsLangPrintf(stderr, "%s: %s\n", argv[0], cupsLastErrorString());
+ _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
return (1);
}
}
-#ifndef WIN32
/*
- * 'sighandler()' - Signal catcher for when we print from stdin...
+ * 'usage()' - Show program usage and exit.
*/
-void
-sighandler(int s) /* I - Signal number */
+static void
+usage(void)
{
- /*
- * Remove the temporary file we're using to print from stdin...
- */
-
- unlink(tempfile);
-
- /*
- * Exit...
- */
-
- exit(s);
+ _cupsLangPuts(stdout, _("Usage: lpr [options] [file(s)]"));
+ _cupsLangPuts(stdout, _("Options:"));
+ _cupsLangPuts(stdout, _("-# num-copies Specify the number of copies to print"));
+ _cupsLangPuts(stdout, _("-E Encrypt the connection to the server"));
+ _cupsLangPuts(stdout, _("-H server[:port] Connect to the named server and port"));
+ _cupsLangPuts(stdout, _("-m Send an email notification when the job completes"));
+ _cupsLangPuts(stdout, _("-o option[=value] Specify a printer-specific option"));
+ _cupsLangPuts(stdout, _("-o job-sheets=standard Print a banner page with the job"));
+ _cupsLangPuts(stdout, _("-o media=size Specify the media size to use"));
+ _cupsLangPuts(stdout, _("-o number-up=N Specify that input pages should be printed N-up (1, 2, 4, 6, 9, and 16 are supported)"));
+ _cupsLangPuts(stdout, _("-o orientation-requested=N\n"
+ " Specify portrait (3) or landscape (4) orientation"));
+ _cupsLangPuts(stdout, _("-o print-quality=N Specify the print quality - draft (3), normal (4), or best (5)"));
+ _cupsLangPuts(stdout, _("-o sides=one-sided Specify 1-sided printing"));
+ _cupsLangPuts(stdout, _("-o sides=two-sided-long-edge\n"
+ " Specify 2-sided portrait printing"));
+ _cupsLangPuts(stdout, _("-o sides=two-sided-short-edge\n"
+ " Specify 2-sided landscape printing"));
+ _cupsLangPuts(stdout, _("-q Specify the job should be held for printing"));
+ _cupsLangPuts(stdout, _("-r Remove the file(s) after submission"));
+ _cupsLangPuts(stdout, _("-P destination Specify the destination"));
+ _cupsLangPuts(stdout, _("-T title Specify the job title"));
+ _cupsLangPuts(stdout, _("-U username Specify the username to use for authentication"));
+
+ exit(1);
}
-#endif /* !WIN32 */
-
-
-/*
- * End of "$Id: lpr.c 6649 2007-07-11 21:46:42Z mike $".
- */