|
|
/*
|
|
|
* CUPS API test program for CUPS.
|
|
|
*
|
|
|
* Copyright © 2007-2018 by Apple Inc.
|
|
|
* Copyright © 2007 by Easy Software Products.
|
|
|
*
|
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more information.
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
* Include necessary headers...
|
|
|
*/
|
|
|
|
|
|
#undef _CUPS_NO_DEPRECATED
|
|
|
#include "cups-private.h"
|
|
|
#include "ppd.h"
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Local functions...
|
|
|
*/
|
|
|
|
|
|
static int dests_equal(cups_dest_t *a, cups_dest_t *b);
|
|
|
static int enum_cb(void *user_data, unsigned flags, cups_dest_t *dest);
|
|
|
static void show_diffs(cups_dest_t *a, cups_dest_t *b);
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'main()' - Main entry.
|
|
|
*/
|
|
|
|
|
|
int /* O - Exit status */
|
|
|
main(int argc, /* I - Number of command-line arguments */
|
|
|
char *argv[]) /* I - Command-line arguments */
|
|
|
{
|
|
|
http_t *http, /* First HTTP connection */
|
|
|
*http2; /* Second HTTP connection */
|
|
|
int status = 0, /* Exit status */
|
|
|
i, /* Looping var */
|
|
|
num_dests; /* Number of destinations */
|
|
|
cups_dest_t *dests, /* Destinations */
|
|
|
*dest, /* Current destination */
|
|
|
*named_dest; /* Current named destination */
|
|
|
const char *dest_name, /* Destination name */
|
|
|
*dval, /* Destination value */
|
|
|
*ppdfile; /* PPD file */
|
|
|
ppd_file_t *ppd; /* PPD file data */
|
|
|
int num_jobs; /* Number of jobs for queue */
|
|
|
cups_job_t *jobs; /* Jobs for queue */
|
|
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
{
|
|
|
if (!strcmp(argv[1], "enum"))
|
|
|
{
|
|
|
cups_ptype_t mask = CUPS_PRINTER_LOCAL,
|
|
|
/* Printer type mask */
|
|
|
type = CUPS_PRINTER_LOCAL;
|
|
|
/* Printer type */
|
|
|
int msec = 0; /* Timeout in milliseconds */
|
|
|
|
|
|
|
|
|
for (i = 2; i < argc; i ++)
|
|
|
if (isdigit(argv[i][0] & 255) || argv[i][0] == '.')
|
|
|
msec = (int)(atof(argv[i]) * 1000);
|
|
|
else if (!_cups_strcasecmp(argv[i], "bw"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_BW;
|
|
|
type |= CUPS_PRINTER_BW;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "color"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_COLOR;
|
|
|
type |= CUPS_PRINTER_COLOR;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "mono"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_COLOR;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "duplex"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_DUPLEX;
|
|
|
type |= CUPS_PRINTER_DUPLEX;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "simplex"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_DUPLEX;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "staple"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_STAPLE;
|
|
|
type |= CUPS_PRINTER_STAPLE;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "copies"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_COPIES;
|
|
|
type |= CUPS_PRINTER_COPIES;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "collate"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_COLLATE;
|
|
|
type |= CUPS_PRINTER_COLLATE;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "punch"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_PUNCH;
|
|
|
type |= CUPS_PRINTER_PUNCH;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "cover"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_COVER;
|
|
|
type |= CUPS_PRINTER_COVER;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "bind"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_BIND;
|
|
|
type |= CUPS_PRINTER_BIND;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "sort"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_SORT;
|
|
|
type |= CUPS_PRINTER_SORT;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "mfp"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_MFP;
|
|
|
type |= CUPS_PRINTER_MFP;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "printer"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_MFP;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "large"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_LARGE;
|
|
|
type |= CUPS_PRINTER_LARGE;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "medium"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_MEDIUM;
|
|
|
type |= CUPS_PRINTER_MEDIUM;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(argv[i], "small"))
|
|
|
{
|
|
|
mask |= CUPS_PRINTER_SMALL;
|
|
|
type |= CUPS_PRINTER_SMALL;
|
|
|
}
|
|
|
else
|
|
|
fprintf(stderr, "Unknown argument \"%s\" ignored...\n", argv[i]);
|
|
|
|
|
|
cupsEnumDests(CUPS_DEST_FLAGS_NONE, msec, NULL, type, mask, enum_cb, NULL);
|
|
|
}
|
|
|
else if (!strcmp(argv[1], "password"))
|
|
|
{
|
|
|
const char *pass = cupsGetPassword("Password:");
|
|
|
/* Password string */
|
|
|
|
|
|
if (pass)
|
|
|
printf("Password entered: %s\n", pass);
|
|
|
else
|
|
|
puts("No password entered.");
|
|
|
}
|
|
|
else if (!strcmp(argv[1], "ppd") && argc == 3)
|
|
|
{
|
|
|
/*
|
|
|
* ./testcups ppd printer
|
|
|
*/
|
|
|
|
|
|
http_status_t http_status; /* Status */
|
|
|
char buffer[1024]; /* PPD filename */
|
|
|
time_t modtime = 0; /* Last modified */
|
|
|
|
|
|
if ((http_status = cupsGetPPD3(CUPS_HTTP_DEFAULT, argv[2], &modtime,
|
|
|
buffer, sizeof(buffer))) != HTTP_STATUS_OK)
|
|
|
printf("Unable to get PPD: %d (%s)\n", (int)http_status,
|
|
|
cupsLastErrorString());
|
|
|
else
|
|
|
puts(buffer);
|
|
|
}
|
|
|
else if (!strcmp(argv[1], "print") && argc == 5)
|
|
|
{
|
|
|
/*
|
|
|
* ./testcups print printer file interval
|
|
|
*/
|
|
|
|
|
|
int interval, /* Interval between writes */
|
|
|
job_id; /* Job ID */
|
|
|
cups_file_t *fp; /* Print file */
|
|
|
char buffer[16384]; /* Read/write buffer */
|
|
|
ssize_t bytes; /* Bytes read/written */
|
|
|
|
|
|
if ((fp = cupsFileOpen(argv[3], "r")) == NULL)
|
|
|
{
|
|
|
printf("Unable to open \"%s\": %s\n", argv[2], strerror(errno));
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, argv[2], "testcups", 0,
|
|
|
NULL)) <= 0)
|
|
|
{
|
|
|
printf("Unable to create print job on %s: %s\n", argv[1],
|
|
|
cupsLastErrorString());
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
interval = atoi(argv[4]);
|
|
|
|
|
|
if (cupsStartDocument(CUPS_HTTP_DEFAULT, argv[1], job_id, argv[2],
|
|
|
CUPS_FORMAT_AUTO, 1) != HTTP_STATUS_CONTINUE)
|
|
|
{
|
|
|
puts("Unable to start document!");
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
|
|
|
{
|
|
|
printf("Writing %d bytes...\n", (int)bytes);
|
|
|
|
|
|
if (cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes) != HTTP_STATUS_CONTINUE)
|
|
|
{
|
|
|
puts("Unable to write bytes!");
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
if (interval > 0)
|
|
|
sleep((unsigned)interval);
|
|
|
}
|
|
|
|
|
|
cupsFileClose(fp);
|
|
|
|
|
|
if (cupsFinishDocument(CUPS_HTTP_DEFAULT,
|
|
|
argv[1]) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
|
|
|
{
|
|
|
puts("Unable to finish document!");
|
|
|
return (1);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
puts("Usage:");
|
|
|
puts("");
|
|
|
puts("Run basic unit tests:");
|
|
|
puts("");
|
|
|
puts(" ./testcups");
|
|
|
puts("");
|
|
|
puts("Enumerate printers (for N seconds, -1 for indefinitely):");
|
|
|
puts("");
|
|
|
puts(" ./testcups enum [seconds]");
|
|
|
puts("");
|
|
|
puts("Ask for a password:");
|
|
|
puts("");
|
|
|
puts(" ./testcups password");
|
|
|
puts("");
|
|
|
puts("Get the PPD file:");
|
|
|
puts("");
|
|
|
puts(" ./testcups ppd printer");
|
|
|
puts("");
|
|
|
puts("Print a file (interval controls delay between buffers in seconds):");
|
|
|
puts("");
|
|
|
puts(" ./testcups print printer file interval");
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* _cupsConnect() connection reuse...
|
|
|
*/
|
|
|
|
|
|
fputs("_cupsConnect: ", stdout);
|
|
|
http = _cupsConnect();
|
|
|
http2 = _cupsConnect();
|
|
|
|
|
|
if (http == http2)
|
|
|
{
|
|
|
puts("PASS");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
puts("FAIL (different connections)");
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* cupsGetDests()
|
|
|
*/
|
|
|
|
|
|
fputs("cupsGetDests: ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
num_dests = cupsGetDests(&dests);
|
|
|
|
|
|
if (num_dests == 0)
|
|
|
{
|
|
|
puts("FAIL");
|
|
|
return (1);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
printf("PASS (%d dests)\n", num_dests);
|
|
|
|
|
|
for (i = num_dests, dest = dests; i > 0; i --, dest ++)
|
|
|
{
|
|
|
printf(" %s", dest->name);
|
|
|
|
|
|
if (dest->instance)
|
|
|
printf(" /%s", dest->instance);
|
|
|
|
|
|
if (dest->is_default)
|
|
|
puts(" ***DEFAULT***");
|
|
|
else
|
|
|
putchar('\n');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* cupsGetDest(NULL)
|
|
|
*/
|
|
|
|
|
|
fputs("cupsGetDest(NULL): ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL)
|
|
|
{
|
|
|
for (i = num_dests, dest = dests; i > 0; i --, dest ++)
|
|
|
if (dest->is_default)
|
|
|
break;
|
|
|
|
|
|
if (i)
|
|
|
{
|
|
|
status = 1;
|
|
|
puts("FAIL");
|
|
|
}
|
|
|
else
|
|
|
puts("PASS (no default)");
|
|
|
|
|
|
dest = NULL;
|
|
|
}
|
|
|
else
|
|
|
printf("PASS (%s)\n", dest->name);
|
|
|
|
|
|
/*
|
|
|
* cupsGetNamedDest(NULL, NULL, NULL)
|
|
|
*/
|
|
|
|
|
|
fputs("cupsGetNamedDest(NULL, NULL, NULL): ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
if ((named_dest = cupsGetNamedDest(NULL, NULL, NULL)) == NULL ||
|
|
|
!dests_equal(dest, named_dest))
|
|
|
{
|
|
|
if (!dest)
|
|
|
puts("PASS (no default)");
|
|
|
else if (named_dest)
|
|
|
{
|
|
|
puts("FAIL (different values)");
|
|
|
show_diffs(dest, named_dest);
|
|
|
status = 1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
puts("FAIL (no default)");
|
|
|
status = 1;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
printf("PASS (%s)\n", named_dest->name);
|
|
|
|
|
|
if (named_dest)
|
|
|
cupsFreeDests(1, named_dest);
|
|
|
|
|
|
/*
|
|
|
* cupsGetDest(printer)
|
|
|
*/
|
|
|
|
|
|
for (i = 0, dest_name = NULL; i < num_dests; i ++)
|
|
|
{
|
|
|
if ((dval = cupsGetOption("printer-is-temporary", dests[i].num_options, dest[i].options)) != NULL && !strcmp(dval, "false"))
|
|
|
{
|
|
|
dest_name = dests[i].name;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
printf("cupsGetDest(\"%s\"): ", dest_name ? dest_name : "(null)");
|
|
|
fflush(stdout);
|
|
|
|
|
|
if ((dest = cupsGetDest(dest_name, NULL, num_dests, dests)) == NULL)
|
|
|
{
|
|
|
puts("FAIL");
|
|
|
return (1);
|
|
|
}
|
|
|
else
|
|
|
puts("PASS");
|
|
|
|
|
|
/*
|
|
|
* cupsGetNamedDest(NULL, printer, instance)
|
|
|
*/
|
|
|
|
|
|
printf("cupsGetNamedDest(NULL, \"%s\", \"%s\"): ", dest->name,
|
|
|
dest->instance ? dest->instance : "(null)");
|
|
|
fflush(stdout);
|
|
|
|
|
|
if ((named_dest = cupsGetNamedDest(NULL, dest->name, dest->instance)) == NULL ||
|
|
|
!dests_equal(dest, named_dest))
|
|
|
{
|
|
|
if (named_dest)
|
|
|
{
|
|
|
puts("FAIL (different values)");
|
|
|
show_diffs(dest, named_dest);
|
|
|
}
|
|
|
else
|
|
|
puts("FAIL (no destination)");
|
|
|
|
|
|
|
|
|
status = 1;
|
|
|
}
|
|
|
else
|
|
|
puts("PASS");
|
|
|
|
|
|
if (named_dest)
|
|
|
cupsFreeDests(1, named_dest);
|
|
|
|
|
|
/*
|
|
|
* cupsPrintFile()
|
|
|
*/
|
|
|
|
|
|
fputs("cupsPrintFile: ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
if (cupsPrintFile(dest->name, "../test/testfile.pdf", "Test Page",
|
|
|
dest->num_options, dest->options) <= 0)
|
|
|
{
|
|
|
printf("FAIL (%s)\n", cupsLastErrorString());
|
|
|
return (1);
|
|
|
}
|
|
|
else
|
|
|
puts("PASS");
|
|
|
|
|
|
/*
|
|
|
* cupsGetPPD(printer)
|
|
|
*/
|
|
|
|
|
|
fputs("cupsGetPPD: ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
if ((ppdfile = cupsGetPPD(dest->name)) == NULL)
|
|
|
{
|
|
|
puts("FAIL");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
puts("PASS");
|
|
|
|
|
|
/*
|
|
|
* ppdOpenFile()
|
|
|
*/
|
|
|
|
|
|
fputs("ppdOpenFile: ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
if ((ppd = ppdOpenFile(ppdfile)) == NULL)
|
|
|
{
|
|
|
puts("FAIL");
|
|
|
return (1);
|
|
|
}
|
|
|
else
|
|
|
puts("PASS");
|
|
|
|
|
|
ppdClose(ppd);
|
|
|
unlink(ppdfile);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* cupsGetJobs()
|
|
|
*/
|
|
|
|
|
|
fputs("cupsGetJobs: ", stdout);
|
|
|
fflush(stdout);
|
|
|
|
|
|
num_jobs = cupsGetJobs(&jobs, NULL, 0, -1);
|
|
|
|
|
|
if (num_jobs == 0)
|
|
|
{
|
|
|
puts("FAIL");
|
|
|
return (1);
|
|
|
}
|
|
|
else
|
|
|
puts("PASS");
|
|
|
|
|
|
cupsFreeJobs(num_jobs, jobs);
|
|
|
cupsFreeDests(num_dests, dests);
|
|
|
|
|
|
return (status);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'dests_equal()' - Determine whether two destinations are equal.
|
|
|
*/
|
|
|
|
|
|
static int /* O - 1 if equal, 0 if not equal */
|
|
|
dests_equal(cups_dest_t *a, /* I - First destination */
|
|
|
cups_dest_t *b) /* I - Second destination */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
cups_option_t *aoption; /* Current option */
|
|
|
const char *bval; /* Option value */
|
|
|
|
|
|
|
|
|
if (a == b)
|
|
|
return (1);
|
|
|
|
|
|
if (!a || !b)
|
|
|
return (0);
|
|
|
|
|
|
if (_cups_strcasecmp(a->name, b->name) ||
|
|
|
(a->instance && !b->instance) ||
|
|
|
(!a->instance && b->instance) ||
|
|
|
(a->instance && _cups_strcasecmp(a->instance, b->instance)) ||
|
|
|
a->num_options != b->num_options)
|
|
|
return (0);
|
|
|
|
|
|
for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++)
|
|
|
if ((bval = cupsGetOption(aoption->name, b->num_options,
|
|
|
b->options)) == NULL ||
|
|
|
strcmp(aoption->value, bval))
|
|
|
return (0);
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'enum_cb()' - Report additions and removals.
|
|
|
*/
|
|
|
|
|
|
static int /* O - 1 to continue, 0 to stop */
|
|
|
enum_cb(void *user_data, /* I - User data (unused) */
|
|
|
unsigned flags, /* I - Destination flags */
|
|
|
cups_dest_t *dest) /* I - Destination */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
cups_option_t *option; /* Current option */
|
|
|
|
|
|
|
|
|
(void)user_data;
|
|
|
|
|
|
if (flags & CUPS_DEST_FLAGS_REMOVED)
|
|
|
printf("Removed '%s':\n", dest->name);
|
|
|
else
|
|
|
printf("Added '%s':\n", dest->name);
|
|
|
|
|
|
for (i = dest->num_options, option = dest->options; i > 0; i --, option ++)
|
|
|
printf(" %s=\"%s\"\n", option->name, option->value);
|
|
|
|
|
|
putchar('\n');
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'show_diffs()' - Show differences between two destinations.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
show_diffs(cups_dest_t *a, /* I - First destination */
|
|
|
cups_dest_t *b) /* I - Second destination */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
cups_option_t *aoption; /* Current option */
|
|
|
cups_option_t *boption; /* Current option */
|
|
|
const char *bval; /* Option value */
|
|
|
|
|
|
|
|
|
if (!a || !b)
|
|
|
return;
|
|
|
|
|
|
puts(" Item cupsGetDest cupsGetNamedDest");
|
|
|
puts(" -------------------- ------------------------ ------------------------");
|
|
|
|
|
|
if (_cups_strcasecmp(a->name, b->name))
|
|
|
printf(" name %-24.24s %-24.24s\n", a->name, b->name);
|
|
|
|
|
|
if ((a->instance && !b->instance) ||
|
|
|
(!a->instance && b->instance) ||
|
|
|
(a->instance && _cups_strcasecmp(a->instance, b->instance)))
|
|
|
printf(" instance %-24.24s %-24.24s\n",
|
|
|
a->instance ? a->instance : "(null)",
|
|
|
b->instance ? b->instance : "(null)");
|
|
|
|
|
|
if (a->num_options != b->num_options)
|
|
|
printf(" num_options %-24d %-24d\n", a->num_options,
|
|
|
b->num_options);
|
|
|
|
|
|
for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++)
|
|
|
if ((bval = cupsGetOption(aoption->name, b->num_options,
|
|
|
b->options)) == NULL ||
|
|
|
strcmp(aoption->value, bval))
|
|
|
printf(" %-20.20s %-24.24s %-24.24s\n", aoption->name,
|
|
|
aoption->value, bval ? bval : "(null)");
|
|
|
|
|
|
for (i = b->num_options, boption = b->options; i > 0; i --, boption ++)
|
|
|
if (!cupsGetOption(boption->name, a->num_options, a->options))
|
|
|
printf(" %-20.20s %-24.24s %-24.24s\n", boption->name,
|
|
|
boption->value, "(null)");
|
|
|
}
|