| /* |
| * "lpr" command for CUPS. |
| * |
| * Copyright © 2007-2019 by Apple Inc. |
| * Copyright © 1997-2007 by Easy Software Products. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more |
| * information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/cups-private.h> |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static void usage(void) _CUPS_NORETURN; |
| |
| |
| /* |
| * 'main()' - Parse options and send files for printing. |
| */ |
| |
| int |
| main(int argc, /* I - Number of command-line arguments */ |
| char *argv[]) /* I - Command-line arguments */ |
| { |
| int i, j; /* Looping var */ |
| int job_id; /* Job ID */ |
| char ch; /* Option character */ |
| char *printer, /* Destination printer or class */ |
| *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 */ |
| 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 */ |
| |
| |
| _cupsSetLocale(argv); |
| |
| deletefile = 0; |
| printer = NULL; |
| dest = NULL; |
| num_options = 0; |
| options = NULL; |
| num_files = 0; |
| title = NULL; |
| |
| for (i = 1; i < argc; i ++) |
| { |
| if (!strcmp(argv[i], "--help")) |
| usage(); |
| else if (argv[i][0] == '-') |
| { |
| for (opt = argv[i] + 1; *opt; opt ++) |
| { |
| switch (ch = *opt) |
| { |
| case 'E' : /* Encrypt */ |
| #ifdef HAVE_SSL |
| cupsSetEncryption(HTTP_ENCRYPT_REQUIRED); |
| #else |
| _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]); |
| #endif /* HAVE_SSL */ |
| break; |
| |
| case 'U' : /* Username */ |
| if (opt[1] != '\0') |
| { |
| 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; |
| |
| case 'H' : /* Connect to host */ |
| if (opt[1] != '\0') |
| { |
| cupsSetServer(opt + 1); |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| |
| if (i >= argc) |
| { |
| _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-H\" option."), argv[0]); |
| usage(); |
| } |
| 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 (opt[1] != '\0') |
| { |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| |
| if (i >= argc) |
| { |
| _cupsLangPrintf(stderr, |
| _("%s: Error - expected value after \"-%c\" " |
| "option."), argv[0], ch); |
| usage(); |
| } |
| } |
| |
| 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') |
| { |
| 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]; |
| } |
| |
| 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 - 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]); |
| } |
| |
| if (num_copies < 1) |
| { |
| _cupsLangPrintf(stderr, _("%s: Error - copies must be 1 or more."), argv[0]); |
| return (1); |
| } |
| |
| 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 (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) |
| { |
| /* |
| * Print a file... |
| */ |
| |
| if (access(argv[i], R_OK) != 0) |
| { |
| _cupsLangPrintf(stderr, |
| _("%s: Error - unable to access \"%s\" - %s"), |
| argv[0], argv[i], strerror(errno)); |
| return (1); |
| } |
| |
| files[num_files] = argv[i]; |
| num_files ++; |
| |
| if (title == NULL) |
| { |
| if ((title = strrchr(argv[i], '/')) != NULL) |
| title ++; |
| else |
| title = argv[i]; |
| } |
| } |
| else |
| { |
| _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 ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL) |
| { |
| printer = dest->name; |
| |
| 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 - add '/version=1.1' to server " |
| "name."), argv[0]); |
| return (1); |
| } |
| } |
| |
| if (printer == NULL) |
| { |
| 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."), argv[0]); |
| |
| return (1); |
| } |
| |
| if (num_files > 0) |
| { |
| job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options); |
| |
| if (deletefile && job_id > 0) |
| { |
| /* |
| * Delete print files after printing... |
| */ |
| |
| for (i = 0; i < num_files; i ++) |
| unlink(files[i]); |
| } |
| } |
| else if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, printer, |
| title ? title : "(stdin)", |
| num_options, options)) > 0) |
| { |
| http_status_t status; /* Write status */ |
| const char *format; /* Document format */ |
| ssize_t bytes; /* Bytes read */ |
| |
| 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); |
| |
| while (status == HTTP_CONTINUE && |
| (bytes = read(0, buffer, sizeof(buffer))) > 0) |
| status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes); |
| |
| if (status != HTTP_CONTINUE) |
| { |
| _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 (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", argv[0], cupsLastErrorString()); |
| return (1); |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'usage()' - Show program usage and exit. |
| */ |
| |
| static void |
| usage(void) |
| { |
| _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, _("-P destination Specify the destination")); |
| _cupsLangPuts(stdout, _("-q Specify the job should be held for printing")); |
| _cupsLangPuts(stdout, _("-r Remove the file(s) after submission")); |
| _cupsLangPuts(stdout, _("-T title Specify the job title")); |
| _cupsLangPuts(stdout, _("-U username Specify the username to use for authentication")); |
| |
| exit(1); |
| } |