#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <cups/cups.h>
#include <cups/string.h>
#include <cups/language.h>
#ifndef WIN32
# include <unistd.h>
# include <signal.h>
void sighandler(int);
#endif
int restart_job(int job_id);
int set_job_attrs(int job_id, int num_options, cups_option_t *options);
char tempfile[1024];
int
main(int argc,
char *argv[])
{
int i, j;
int job_id;
char *printer,
*instance,
*val,
*title;
int priority;
int num_copies;
int num_files;
const char *files[1000];
int num_dests;
cups_dest_t *dests,
*dest;
int num_options;
cups_option_t *options;
int silent,
opt_end;
char buffer[8192];
int temp;
off_t size;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
struct sigaction oldaction;
#endif
#ifdef __sun
if ((val = strrchr(argv[0], '/')) != NULL)
val ++;
else
val = argv[0];
if (strcmp(val, "printd") == 0)
return (0);
#endif
opt_end = 0;
silent = 0;
printer = NULL;
num_dests = 0;
dests = NULL;
num_options = 0;
options = NULL;
num_files = 0;
title = NULL;
job_id = 0;
for (i = 1; i < argc; i ++)
if (argv[i][0] == '-' && argv[i][1] && !opt_end)
switch (argv[i][1])
{
case 'E' :
#ifdef HAVE_SSL
cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
#else
fprintf(stderr, "%s: Sorry, no encryption support compiled in!\n",
argv[0]);
#endif
break;
case 'c' :
break;
case 'd' :
if (argv[i][2] != '\0')
printer = argv[i] + 2;
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected destination after -d option!\n", stderr);
return (1);
}
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 'f' :
if (!argv[i][2])
{
i ++;
if (i >= argc)
{
fputs("lp: Expected form after -f option!\n", stderr);
return (1);
}
}
fputs("lp: Warning - form option ignored!\n", stderr);
break;
case 'h' :
if (argv[i][2] != '\0')
cupsSetServer(argv[i] + 2);
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected hostname after -h option!\n", stderr);
return (1);
}
cupsSetServer(argv[i]);
}
break;
case 'i' :
if (argv[i][2])
val = argv[i] + 2;
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected job ID after -i option!\n", stderr);
return (1);
}
val = argv[i];
}
if (num_files > 0)
{
fputs("lp: Error - cannot print files and alter jobs simultaneously!\n", stderr);
return (1);
}
if (strrchr(val, '-') != NULL)
job_id = atoi(strrchr(val, '-') + 1);
else
job_id = atoi(val);
if (job_id < 0)
{
fputs("lp: Error - bad job ID!\n", stderr);
break;
}
break;
case 'm' :
#ifdef __sun
case 'p' :
#endif
break;
case 'w' :
silent = 0;
break;
case 'n' :
if (argv[i][2] != '\0')
num_copies = atoi(argv[i] + 2);
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected copies after -n option!\n", stderr);
return (1);
}
num_copies = atoi(argv[i]);
}
sprintf(buffer, "%d", num_copies);
num_options = cupsAddOption("copies", buffer, num_options, &options);
break;
case 'o' :
if (argv[i][2] != '\0')
num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected option string after -o option!\n", stderr);
return (1);
}
num_options = cupsParseOptions(argv[i], num_options, &options);
}
break;
#ifndef __sun
case 'p' :
#endif
case 'q' :
if (argv[i][2] != '\0')
priority = atoi(argv[i] + 2);
else
{
i ++;
if (i >= argc)
{
fprintf(stderr, "lp: Expected priority after -%c option!\n",
argv[i - 1][1]);
return (1);
}
priority = atoi(argv[i]);
}
if (priority < 1 || priority > 100)
{
fputs("lp: Priority must be between 1 and 100.\n", stderr);
return (1);
}
sprintf(buffer, "%d", priority);
num_options = cupsAddOption("job-priority", buffer, num_options, &options);
break;
case 's' :
silent = 1;
break;
case 't' :
if (argv[i][2] != '\0')
title = argv[i] + 2;
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected title after -t option!\n", stderr);
return (1);
}
title = argv[i];
}
break;
case 'y' :
if (!argv[i][2])
{
i ++;
if (i >= argc)
{
fputs("lp: Expected mode list after -y option!\n", stderr);
return (1);
}
}
fputs("lp: Warning - mode option ignored!\n", stderr);
break;
case 'H' :
if (argv[i][2])
val = argv[i] + 2;
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected hold name after -H option!\n", stderr);
return (1);
}
val = argv[i];
}
if (strcmp(val, "hold") == 0)
num_options = cupsAddOption("job-hold-until", "indefinite",
num_options, &options);
else if (strcmp(val, "resume") == 0 ||
strcmp(val, "release") == 0)
num_options = cupsAddOption("job-hold-until", "no-hold",
num_options, &options);
else if (strcmp(val, "immediate") == 0)
num_options = cupsAddOption("job-priority", "100",
num_options, &options);
else if (strcmp(val, "restart") == 0)
{
if (job_id < 1)
{
fputs("lp: Need job ID (-i) before \"-H restart\"!\n", stderr);
return (1);
}
if (restart_job(job_id))
return (1);
}
else
num_options = cupsAddOption("job-hold-until", val,
num_options, &options);
break;
case 'P' :
if (argv[i][2])
val = argv[i] + 2;
else
{
i ++;
if (i >= argc)
{
fputs("lp: Expected page list after -P option!\n", stderr);
return (1);
}
val = argv[i];
}
num_options = cupsAddOption("page-ranges", val, num_options,
&options);
break;
case 'S' :
if (!argv[i][2])
{
i ++;
if (i >= argc)
{
fputs("lp: Expected character set after -S option!\n", stderr);
return (1);
}
}
fputs("lp: Warning - character set option ignored!\n", stderr);
break;
case 'T' :
if (!argv[i][2])
{
i ++;
if (i >= argc)
{
fputs("lp: Expected content type after -T option!\n", stderr);
return (1);
}
}
fputs("lp: Warning - content type option ignored!\n", stderr);
break;
case '-' :
if (argv[i][2])
{
fputs("lp: Unexpected character after -- option!\n", stderr);
return (1);
}
opt_end = 1;
break;
default :
fprintf(stderr, "lp: Unknown option \'%c\'!\n", argv[i][1]);
return (1);
}
else if (!strcmp(argv[i], "-"))
{
if (num_files || job_id)
{
fputs("lp: Error - cannot print from stdin if files or a job ID are "
"provided!\n", stderr);
return (1);
}
break;
}
else if (num_files < 1000 && job_id == 0)
{
if (access(argv[i], R_OK) != 0)
{
fprintf(stderr, "lp: Unable to access \"%s\" - %s\n", 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
fprintf(stderr, "lp: Too many files - \"%s\"\n", argv[i]);
if (job_id)
return (set_job_attrs(job_id, num_options, options));
if (printer == NULL)
{
if (num_dests == 0)
num_dests = cupsGetDests(&dests);
if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != 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);
}
}
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))
fprintf(stderr, "lp: error - %s environment variable names non-existent destination \"%s\"!\n",
val, printer);
else if (cupsLastError() == IPP_NOT_FOUND)
fputs("lp: error - no default destination available.\n", stderr);
else
fputs("lp: error - scheduler not responding!\n", stderr);
return (1);
}
if (num_files > 0)
job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
else
{
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
temp = cupsTempFd(tempfile, sizeof(tempfile));
if (temp < 0)
{
fprintf(stderr, "lp: unable to create temporary file \"%s\" - %s\n",
tempfile, strerror(errno));
return (1);
}
while ((i = read(0, buffer, sizeof(buffer))) > 0)
write(temp, buffer, i);
size = lseek(temp, 0, SEEK_CUR);
close(temp);
if (size == 0)
{
fputs("lp: stdin is empty, so no job has been sent.\n", stderr);
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 (job_id < 1)
{
fprintf(stderr, "lp: unable to print file: %s\n",
ippErrorString(cupsLastError()));
return (1);
}
else if (!silent)
printf("request id is %s-%d (%d file(s))\n", printer, job_id, num_files);
return (0);
}
int
restart_job(int job_id)
{
http_t *http;
ipp_t *request,
*response;
cups_lang_t *language;
char uri[HTTP_MAX_URI];
http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
language = cupsLangDefault();
request = ippNew();
request->request.op.operation_id = IPP_RESTART_JOB;
request->request.op.request_id = 1;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, language->language);
sprintf(uri, "ipp://localhost/jobs/%d", job_id);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"job-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
{
if (response->request.status.status_code > IPP_OK_CONFLICT)
{
fprintf(stderr, "lp: restart-job failed: %s\n",
ippErrorString(response->request.status.status_code));
ippDelete(response);
return (1);
}
ippDelete(response);
}
else
{
fprintf(stderr, "lp: restart-job failed: %s\n",
ippErrorString(cupsLastError()));
return (1);
}
return (0);
}
int
set_job_attrs(int job_id,
int num_options,
cups_option_t *options)
{
http_t *http;
ipp_t *request,
*response;
cups_lang_t *language;
char uri[HTTP_MAX_URI];
if (num_options == 0)
return (0);
http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
language = cupsLangDefault();
request = ippNew();
request->request.op.operation_id = IPP_SET_JOB_ATTRIBUTES;
request->request.op.request_id = 1;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, language->language);
sprintf(uri, "ipp://localhost/jobs/%d", job_id);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"job-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
cupsEncodeOptions(request, num_options, options);
if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
{
if (response->request.status.status_code > IPP_OK_CONFLICT)
{
fprintf(stderr, "lp: set-job-attributes failed: %s\n",
ippErrorString(response->request.status.status_code));
ippDelete(response);
return (1);
}
ippDelete(response);
}
else
{
fprintf(stderr, "lp: set-job-attributes failed: %s\n",
ippErrorString(cupsLastError()));
return (1);
}
return (0);
}
#ifndef WIN32
void
sighandler(int s)
{
unlink(tempfile);
exit(s);
}
#endif