CHANGES-1.5.txt
---------------
+CHANGES IN CUPS V1.5.4
+
+ - Multiple libusb backend fixes (STR #4098, STR #4100)
+ - The IPP backend no longer tries to get the job status for printers
+ that do not implement the required operation (STR #4083)
+ - Sending a document in an unsupported format to an IPP printer now
+ automatically cancels the job (STR #4093)
+ - Fix some error reporting issues when printing from /dev/null and
+ other unusual situations (STR #4015)
+ - The scheduler now sets the CUPS_MAX_MESSAGE environment variable for
+ filters (STR #4074)
+ - Fixed a build issue when using older versions of autoconf (STR #4084)
+ - The IPP backend now treats the client-error-not-possible status code
+ as a job history issue, allowing IPP printing to Windows to work
+ (STR #4047)
+ - The IPP backend incorrectly included the document-format and
+ compression attributes in Create-Job requests (STR #4086)
+ - The libusb-based USB backend did not work on non-Linux platforms
+ (STR #4088)
+
+
CHANGES IN CUPS V1.5.3
+ - httpReconnect() did not reset the read/write buffers (STR #4065)
- Compiling without threading support failed (STR #4060)
- Fixed compile problem with old versions of OpenSSL (STR #4036)
- The network backends did not check SNMP supply levels regularly
-CHANGES.txt - 1.6b1 - 2012-04-17
+CHANGES.txt - 1.6b1 - 2012-05-22
--------------------------------
CHANGES IN CUPS V1.6b1
- - Documentation updates (STR #3927, STR #3980, STR #4010)
+ - Documentation updates (STR #3927, STR #3980, STR #4010, STR #4068)
+ - The scheduler now consolidates all PPD updates from filters at the
+ end of the job (STR #4075)
+ - CUPS now supports color management using colord (STR #3808)
- CUPS now supports Bonjour using Avahi (STR #3066)
- The PreserveJobFiles and PreserveJobHistory directives now support
specification of a time interval (STR #3143)
-INSTALL - CUPS v1.6.0 - 2012-04-23
+INSTALL - CUPS v1.6.0 - 2012-05-23
----------------------------------
This file describes how to compile and install CUPS from source code. For more
onepage-letter.pdf
onepage-letter.ps
testfile.jpg
+ testfile.pcl
testfile.pdf
testfile.ps
testfile.txt
CXX = @LIBTOOL@ @CXX@
DSO = @DSO@
DSOXX = @DSOXX@
-HTMLDOC = @HTMLDOC@
+GZIP = @GZIP@
INSTALL = @INSTALL@
LD = @LD@
LIBTOOL = @LIBTOOL@
#
INSTALL_BIN = $(LIBTOOL) $(INSTALL) -c -m 555 @INSTALL_STRIP@
-INSTALL_CONFIG = $(INSTALL) -c -m @CUPS_CONFIG_FILE_PERM@
INSTALL_COMPDATA = $(INSTALL) -c -m 444 @INSTALL_GZIP@
+INSTALL_CONFIG = $(INSTALL) -c -m @CUPS_CONFIG_FILE_PERM@
INSTALL_DATA = $(INSTALL) -c -m 444
INSTALL_DIR = $(INSTALL) -d
INSTALL_LIB = $(LIBTOOL) $(INSTALL) -c -m 555 @INSTALL_STRIP@
#
# Top-level Makefile for CUPS.
#
-# Copyright 2007-2010 by Apple Inc.
+# Copyright 2007-2012 by Apple Inc.
# Copyright 1997-2007 by Easy Software Products, all rights reserved.
#
# These coded instructions, statements, and computer programs are the
doc/help/api-*.tokens
$(RM) doc/help/api-*.tokens
echo Indexing docset...
- /Developer/usr/bin/docsetutil index org.cups.docset
+ /Applications/Xcode.app/Contents/Developer/usr/bin/docsetutil index org.cups.docset
echo Generating docset archive and feed...
$(RM) org.cups.docset.atom
- /Developer/usr/bin/docsetutil package --output org.cups.docset.xar \
+ /Applications/Xcode.app/Contents/Developer/usr/bin/docsetutil package --output org.cups.docset.xar \
--atom org.cups.docset.atom \
--download-url http://www.cups.org/org.cups.docset.xar \
org.cups.docset
-README - CUPS v1.6.0 - 2012-04-23
+README - CUPS v1.6.0 - 2012-05-23
----------------------------------
Looking for compile instructions? Read the file "INSTALL.txt"
#ifdef HAVE_AVAHI
if ((simple_poll = avahi_simple_poll_new()) == NULL)
{
- fputs("DEBUG: Unable to create avahi simple poll object.\n", stderr);
+ fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr);
return (1);
}
0, client_callback, simple_poll, &error);
if (!client)
{
- fputs("DEBUG: Unable to create avahi client.\n", stderr);
+ fputs("DEBUG: Unable to create Avahi client.\n", stderr);
return (1);
}
"interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
"regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
sdRef, flags, interfaceIndex, errorCode,
- serviceName ? serviceName : "(null)",
- regtype ? regtype : "(null)",
- replyDomain ? replyDomain : "(null)",
- context);
+ serviceName, regtype, replyDomain, context);
/*
* Only process "add" data...
"interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
"regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
sdRef, flags, interfaceIndex, errorCode,
- serviceName ? serviceName : "(null)",
- regtype ? regtype : "(null)",
- replyDomain ? replyDomain : "(null)",
- context);
+ serviceName, regtype, replyDomain, context);
/*
* Only process "add" data...
replyDomain);
#else /* HAVE_AVAHI */
avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
- serviceName, regtype, replyDomain);
+ serviceName, regtype, replyDomain);
#endif /* HAVE_DNSSD */
free(device->fullName);
*resource; /* Resource path */
int port, /* Port number */
version, /* IPP version */
- job_id; /* Job ID for submitted job */
+ job_id, /* Job ID for submitted job */
+ get_job_attrs; /* Support Get-Job-Attributes? */
const char *job_name; /* Job name for submitted job */
http_encryption_t encryption; /* Use encryption? */
ipp_jstate_t job_state; /* Current job state */
ipp_attribute_t *printer_state; /* printer-state attribute */
ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
int create_job = 0, /* Does printer support Create-Job? */
+ get_job_attrs = 0, /* Does printer support Get-Job-Attributes? */
send_document = 0, /* Does printer support Send-Document? */
validate_job = 0; /* Does printer support Validate-Job? */
int copies, /* Number of copies for job */
create_job = 1;
else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT)
send_document = 1;
+ else if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
+ get_job_attrs = 1;
}
if (!send_document)
fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
stderr);
create_job = 0;
+
+ update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
+ "cups-ipp-missing-send-document");
}
if (!validate_job)
monitor.port = port;
monitor.version = version;
monitor.job_id = 0;
+ monitor.get_job_attrs = get_job_attrs;
monitor.encryption = cupsEncryption();
monitor.job_state = IPP_JOB_PENDING;
monitor.printer_state = IPP_PRINTER_IDLE;
_cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
sleep(10);
}
+ else if (ipp_status == IPP_DOCUMENT_FORMAT)
+ goto cleanup;
else if (ipp_status == IPP_FORBIDDEN ||
ipp_status == IPP_AUTHENTICATION_CANCELED)
{
"cups-ipp-missing-validate-job");
break;
}
- else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
+ else if (ipp_status < IPP_REDIRECTION_OTHER_SITE ||
+ ipp_status == IPP_BAD_REQUEST)
break;
}
* Wait for the job to complete...
*/
- if (!job_id || !waitjob)
+ if (!job_id || !waitjob || !get_job_attrs)
continue;
_cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
response = cupsDoRequest(http, request, resource);
ipp_status = cupsLastError();
- if (ipp_status == IPP_NOT_FOUND)
+ if (ipp_status == IPP_NOT_FOUND || ipp_status == IPP_NOT_POSSIBLE)
{
/*
* Job has gone away and/or the server has no job history...
else
{
if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
- ipp_status != IPP_NOT_POSSIBLE &&
ipp_status != IPP_PRINTER_BUSY)
{
ippDelete(response);
return (CUPS_BACKEND_AUTH_REQUIRED);
else if (ipp_status == IPP_INTERNAL_ERROR)
return (CUPS_BACKEND_STOP);
- else if (ipp_status == IPP_DOCUMENT_FORMAT ||
- ipp_status == IPP_CONFLICT)
+ else if (ipp_status == IPP_CONFLICT)
return (CUPS_BACKEND_FAILED);
- else if (ipp_status == IPP_REQUEST_VALUE || job_canceled < 0)
+ else if (ipp_status == IPP_REQUEST_VALUE ||
+ ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
{
if (ipp_status == IPP_REQUEST_VALUE)
_cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
+ else if (ipp_status == IPP_DOCUMENT_FORMAT)
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Printer cannot print supplied content."));
else
_cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
* Check the status of the job itself...
*/
- job_op = monitor->job_id > 0 ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
+ job_op = (monitor->job_id > 0 && monitor->get_job_attrs) ?
+ IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
request = ippNewRequest(job_op);
request->request.op.version[0] = monitor->version / 10;
request->request.op.version[1] = monitor->version % 10;
fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
}
- if (format)
+ if (format && op != IPP_CREATE_JOB)
{
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
"document-format", NULL, format);
}
#ifdef HAVE_LIBZ
- if (compression)
+ if (compression && op != IPP_CREATE_JOB)
{
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"compression", NULL, compression);
* When talking to another CUPS server, send all options...
*/
- cupsEncodeOptions(request, num_options, options);
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
}
if (copies > 1 && (!pc || copies <= pc->max_copies))
#define CUPS_CLEANER_NEAR_EOL 0x0400 /* Proposed JPS3 */
#define CUPS_CLEANER_LIFE_OVER 0x0800 /* Proposed JPS3 */
+#define CUPS_SNMP_NONE 0x0000
+#define CUPS_SNMP_CAPACITY 0x0001 /* Supply levels reported as percentages */
+
/*
* Local structures...
static int current_state = -1;
/* Current device state bits */
static int charset = -1; /* Character set for supply names */
+static unsigned quirks = CUPS_SNMP_NONE;
+ /* Quirks we have to work around */
static int num_supplies = 0;
/* Number of supplies found */
static backend_supplies_t supplies[CUPS_MAX_SUPPLIES];
{
if (supplies[i].max_capacity > 0 && supplies[i].level >= 0)
percent = 100 * supplies[i].level / supplies[i].max_capacity;
+ else if (supplies[i].level >= 0 && supplies[i].level <= 100 &&
+ (quirks & CUPS_SNMP_CAPACITY))
+ percent = supplies[i].level;
else
percent = 50;
if (i)
*ptr++ = ',';
- if (supplies[i].max_capacity > 0 && supplies[i].level >= 0)
+ if ((supplies[i].max_capacity > 0 || (quirks & CUPS_SNMP_CAPACITY)) &&
+ supplies[i].level >= 0)
sprintf(ptr, "%d", percent);
else
strcpy(ptr, "-1");
return;
}
+ if ((ppdattr = ppdFindAttr(ppd, "cupsSNMPQuirks", NULL)) != NULL)
+ {
+ if (!_cups_strcasecmp(ppdattr->value, "capacity"))
+ quirks |= CUPS_SNMP_CAPACITY;
+ }
+
ppdClose(ppd);
/*
supplies[i - 1].level = packet->object_value.integer;
}
- else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity))
+ else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity) &&
+ !(quirks & CUPS_SNMP_CAPACITY))
{
/*
* Get max capacity...
/*
* If it didn't exit abort the pending read and wait an additional second...
*/
-
+
if (!g.read_thread_done)
{
- fputs("DEBUG: Read thread still active, aborting the pending read...\n",
+ fputs("DEBUG: Read thread still active, aborting the pending read...\n",
stderr);
g.wait_eof = 0;
gettimeofday(&tv, NULL);
cond_timeout.tv_sec = tv.tv_sec + 1;
cond_timeout.tv_nsec = tv.tv_usec * 1000;
-
+
while (!g.read_thread_done)
{
if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
int number; /* Interface number */
- libusb_get_device_descriptor (printer->device, &devdesc);
- libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
+ libusb_get_device_descriptor(printer->device, &devdesc);
+ libusb_get_config_descriptor(printer->device, printer->conf, &confptr);
number = confptr->interface[printer->iface].
altsetting[printer->altset].bInterfaceNumber;
libusb_release_interface(printer->handle, number);
* a printer...
*/
- libusb_get_device_descriptor (device, &devdesc);
+ libusb_get_device_descriptor(device, &devdesc);
if (!devdesc.bNumConfigurations || !devdesc.idVendor ||
!devdesc.idProduct)
for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
{
- if (libusb_get_config_descriptor (device, conf, &confptr) < 0)
+ if (libusb_get_config_descriptor(device, conf, &confptr) < 0)
continue;
for (iface = 0, ifaceptr = confptr->interface;
iface < confptr->bNumInterfaces;
if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
if ((sern = cupsGetOption("SN", num_values, values)) == NULL &&
- ((libusb_get_device_descriptor (printer->device, &devdesc) >= 0) &&
+ ((libusb_get_device_descriptor(printer->device, &devdesc) >= 0) &&
devdesc.iSerialNumber))
{
/*
if (libusb_open(printer->device, &printer->handle) < 0)
return (-1);
+ printer->usblp_attached = 0;
+
if (verbose)
fputs("STATE: +connecting-to-device\n", stderr);
+ if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0)
+ {
+ fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n",
+ errcode);
+ goto error;
+ }
+
+ /*
+ * Get the "usblp" kernel module out of the way. This backend only
+ * works without the module attached.
+ */
+
+ errcode = libusb_kernel_driver_active(printer->handle, printer->iface);
+ if (errcode == 0)
+ printer->usblp_attached = 0;
+ else if (errcode == 1)
+ {
+ printer->usblp_attached = 1;
+ if ((errcode =
+ libusb_detach_kernel_driver(printer->handle, printer->iface)) < 0)
+ {
+ fprintf(stderr, "DEBUG: Failed to detach \"usblp\" module from %04x:%04x\n",
+ devdesc.idVendor, devdesc.idProduct);
+ goto error;
+ }
+ }
+ else
+ {
+ printer->usblp_attached = 0;
+ fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" kernel module attached\n",
+ devdesc.idVendor, devdesc.idProduct);
+ goto error;
+ }
+
/*
* Set the desired configuration, but only if it needs changing. Some
* printers (e.g., Samsung) don't like libusb_set_configuration. It will
0, 0, (unsigned char *)¤t, 1, 5000) < 0)
current = 0; /* Assume not configured */
- libusb_get_device_descriptor (printer->device, &devdesc);
- libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
+ libusb_get_device_descriptor(printer->device, &devdesc);
+ libusb_get_config_descriptor(printer->device, printer->conf, &confptr);
number1 = confptr->bConfigurationValue;
if (number1 != current)
else
{
printer->usblp_attached = 0;
- fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" kernel module attached\n",
- devdesc.idVendor, devdesc.idProduct);
- goto error;
+
+ if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
+ {
+ fprintf(stderr,
+ "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
+ "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
+ goto error;
+ }
}
/*
while ((errcode = libusb_claim_interface(printer->handle, number1)) < 0)
{
if (errcode != LIBUSB_ERROR_BUSY)
+ {
fprintf(stderr,
"DEBUG: Failed to claim interface %d for %04x:%04x: %s\n",
number1, devdesc.idVendor, devdesc.idProduct, strerror(errno));
- goto error;
+ goto error;
+ }
}
/*
< 0)
{
if (errcode != LIBUSB_ERROR_BUSY)
+ {
fprintf(stderr,
"DEBUG: Failed to set alternate interface %d for %04x:%04x: "
"%s\n",
number2, devdesc.idVendor, devdesc.idProduct, strerror(errno));
- goto error;
+ goto error;
+ }
}
}
cupsSetUser(argv[i]);
}
break;
-
+
case 'H' : /* Connect to host */
if (argv[i][2] != '\0')
cupsSetServer(argv[i] + 2);
if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
{
+ _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
- job_id = 0;
+ return (1);
}
}
*
* Xcode documentation set generator.
*
- * Copyright 2007-2011 by Apple Inc.
+ * Copyright 2007-2012 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products.
*
* These coded instructions, statements, and computer programs are the
* Include necessary headers...
*/
-#include "cgi.h"
+#include "cgi-private.h"
#include <errno.h>
AC_PATH_PROG(AR,ar)
AC_PATH_PROG(CHMOD,chmod)
AC_PATH_PROG(GZIP,gzip)
-AC_PATH_PROG(HTMLDOC,htmldoc)
AC_PATH_PROG(LD,ld)
AC_PATH_PROG(LN,ln)
AC_PATH_PROG(MV,mv)
Darwin*)
# Darwin and MacOS X...
AC_DEFINE(HAVE_DNSSD)
- AC_DEFINE(HAVE_COREFOUNDATION)
- AC_DEFINE(HAVE_SYSTEMCONFIGURATION)
DNSSDLIBS="-framework CoreFoundation -framework SystemConfiguration"
DNSSD_BACKEND="dnssd"
;;
dnl Check for the OpenSSL library last...
if test $have_ssl = 0 -a "x$enable_openssl" != "xno"; then
- AC_CHECK_HEADER(openssl/ssl.h,
+ AC_CHECK_HEADER(openssl/ssl.h,[
dnl Save the current libraries so the crypto stuff isn't always
dnl included...
SAVELIBS="$LIBS"
$libcrypto)
if test "x${SSLLIBS}" != "x"; then
- LIBS="$SAVELIBS $SSLLIBS"
- AC_CHECK_FUNC(SSL_set_tlsext_host_name,
- AC_DEFINE(HAVE_SSL_SET_TLSEXT_HOST_NAME))
break
fi
done
- LIBS="$SAVELIBS")
+ if test "x${SSLLIBS}" != "x"; then
+ LIBS="$SAVELIBS $SSLLIBS"
+ AC_CHECK_FUNCS(SSL_set_tlsext_host_name)
+ fi
+
+ LIBS="$SAVELIBS"])
fi
fi
#define CUPS_GHOSTSCRIPT "/usr/bin/gs"
-/*
- * Do we have Darwin's CoreFoundation and SystemConfiguration frameworks?
- */
-
-#undef HAVE_COREFOUNDATION
-#undef HAVE_SYSTEMCONFIGURATION
-
-
/*
* Do we have CoreFoundation public and private headers?
*/
* millimeters */
} _cups_media_db_t;
+typedef struct _cups_dconstres_s /* Constraint/resolver */
+{
+ char *name; /* Name of resolver */
+ ipp_t *collection; /* Collection containing attrs */
+} _cups_dconstres_t;
+
struct _cups_dinfo_s /* Destination capability and status
* information */
{
const char *uri; /* Printer URI */
char *resource; /* Resource path */
ipp_t *attrs; /* Printer attributes */
+ int num_defaults; /* Number of default options */
+ cups_option_t *defaults; /* Default options */
cups_array_t *constraints; /* Job constraints */
+ cups_array_t *resolvers; /* Job resolvers */
cups_array_t *localizations; /* Localization information */
cups_array_t *media_db; /* Media database */
_cups_media_db_t min_size, /* Minimum size */
if (title)
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
title);
- cupsEncodeOptions(request, num_options, options);
+
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
/*
* Send the request and get the job-id...
*
* Contents:
*
- * cupsLocalizeDestOption() - Get the localized string for a destination
- * option.
- * cupsLocalizeDestValue() - Get the localized string for a destination
- * option+value pair.
+ * cupsLocalizeDestOption() - Get the localized string for a destination
+ * option.
+ * cupsLocalizeDestValue() - Get the localized string for a destination
+ * option+value pair.
+ * cups_create_localizations() - Create the localizations array for a
+ * destination.
+ * cups_read_strings() - Read a pair of strings from a .strings file.
+ * cups_scan_strings() - Scan a quoted string.
*/
/*
#include "cups-private.h"
+/*
+ * Local functions...
+ */
+
+static void cups_create_localizations(http_t *http, cups_dinfo_t *dinfo);
+static int cups_read_strings(cups_file_t *fp, char *buffer, size_t bufsize,
+ char **id, char **str);
+static char *cups_scan_strings(char *buffer);
+
+
/*
* 'cupsLocalizeDestOption()' - Get the localized string for a destination
* option.
*
- * The returned string is stored in the localization array and will become
- * invalid if the localization array is deleted.
+ * The returned string is stored in the destination information and will become
+ * invalid if the destination information is deleted.
*
* @since CUPS 1.6/OS X 10.8@
*/
cups_dinfo_t *dinfo, /* I - Destination information */
const char *option) /* I - Option to localize */
{
- return (option);
+ _cups_message_t key, /* Search key */
+ *match; /* Matching entry */
+
+
+ if (!http || !dest || !dinfo)
+ return (option);
+
+ if (!dinfo->localizations)
+ cups_create_localizations(http, dinfo);
+
+ if (cupsArrayCount(dinfo->localizations) == 0)
+ return (option);
+
+ key.id = (char *)option;
+ if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
+ &key)) != NULL)
+ return (match->str);
+ else
+ return (option);
}
* 'cupsLocalizeDestValue()' - Get the localized string for a destination
* option+value pair.
*
- * The returned string is stored in the localization array and will become
- * invalid if the localization array is deleted.
+ * The returned string is stored in the destination information and will become
+ * invalid if the destination information is deleted.
*
* @since CUPS 1.6/OS X 10.8@
*/
const char *option, /* I - Option to localize */
const char *value) /* I - Value to localize */
{
- return (value);
+ _cups_message_t key, /* Search key */
+ *match; /* Matching entry */
+ char pair[256]; /* option.value pair */
+
+
+ if (!http || !dest || !dinfo)
+ return (value);
+
+ if (!dinfo->localizations)
+ cups_create_localizations(http, dinfo);
+
+ if (cupsArrayCount(dinfo->localizations) == 0)
+ return (value);
+
+ snprintf(pair, sizeof(pair), "%s.%s", option, value);
+ key.id = pair;
+ if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
+ &key)) != NULL)
+ return (match->str);
+ else
+ return (value);
+}
+
+
+/*
+ * 'cups_create_localizations()' - Create the localizations array for a
+ * destination.
+ */
+
+static void
+cups_create_localizations(
+ http_t *http, /* I - Connection to destination */
+ cups_dinfo_t *dinfo) /* I - Destination informations */
+{
+ http_t *http2; /* Connection for strings file */
+ http_status_t status; /* Request status */
+ ipp_attribute_t *attr; /* "printer-strings-uri" attribute */
+ char scheme[32], /* URI scheme */
+ userpass[256], /* Username/password info */
+ hostname[256], /* Hostname */
+ resource[1024], /* Resource */
+ http_hostname[256],
+ /* Hostname of connection */
+ tempfile[1024]; /* Temporary filename */
+ int port; /* Port number */
+ http_encryption_t encryption; /* Encryption to use */
+ cups_file_t *temp; /* Temporary file */
+
+
+ /*
+ * Create an empty message catalog...
+ */
+
+ dinfo->localizations = _cupsMessageNew(NULL);
+
+ /*
+ * See if there are any localizations...
+ */
+
+ if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri",
+ IPP_TAG_URI)) == NULL)
+ {
+ /*
+ * Nope...
+ */
+
+ DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) "
+ "value.");
+ return; /* Nope */
+ }
+
+ /*
+ * Pull apart the URI and determine whether we need to try a different
+ * server...
+ */
+
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text,
+ scheme, sizeof(scheme), userpass, sizeof(userpass),
+ hostname, sizeof(hostname), &port, resource,
+ sizeof(resource)) < HTTP_URI_OK)
+ {
+ DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value "
+ "\"%s\".", attr->values[0].string.text));
+ return;
+ }
+
+ httpGetHostname(http, http_hostname, sizeof(http_hostname));
+
+ if (!_cups_strcasecmp(http_hostname, hostname) &&
+ port == _httpAddrPort(http->hostaddr))
+ {
+ /*
+ * Use the same connection...
+ */
+
+ http2 = http;
+ }
+ else
+ {
+ /*
+ * Connect to the alternate host...
+ */
+
+ if (!strcmp(scheme, "https"))
+ encryption = HTTP_ENCRYPT_ALWAYS;
+ else
+ encryption = HTTP_ENCRYPT_IF_REQUESTED;
+
+ if ((http2 = httpConnectEncrypt(hostname, port, encryption)) == NULL)
+ {
+ DEBUG_printf(("4cups_create_localizations: Unable to connect to "
+ "%s:%d: %s", hostname, port, cupsLastErrorString()));
+ return;
+ }
+ }
+
+ /*
+ * Get a temporary file...
+ */
+
+ if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
+ {
+ DEBUG_printf(("4cups_create_localizations: Unable to create temporary "
+ "file: %s", cupsLastErrorString()));
+ if (http2 != http)
+ httpClose(http2);
+ return;
+ }
+
+ status = cupsGetFd(http2, resource, cupsFileNumber(temp));
+
+ DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource,
+ httpStatus(status)));
+
+ if (status == HTTP_OK)
+ {
+ /*
+ * Got the file, read it...
+ */
+
+ char buffer[8192], /* Message buffer */
+ *id, /* ID string */
+ *str; /* Translated message */
+ _cups_message_t *m; /* Current message */
+
+ lseek(cupsFileNumber(temp), 0, SEEK_SET);
+
+ while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str))
+ {
+ if ((m = malloc(sizeof(_cups_message_t))) == NULL)
+ break;
+
+ m->id = strdup(id);
+ m->str = strdup(str);
+
+ if (m->id && m->str)
+ cupsArrayAdd(dinfo->localizations, m);
+ else
+ {
+ if (m->id)
+ free(m->id);
+
+ if (m->str)
+ free(m->str);
+
+ free(m);
+ break;
+ }
+ }
+ }
+
+ DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
+ cupsArrayCount(dinfo->localizations)));
+
+ /*
+ * Cleanup...
+ */
+
+ unlink(tempfile);
+ cupsFileClose(temp);
+
+ if (http2 != http)
+ httpClose(http2);
+}
+
+
+/*
+ * 'cups_read_strings()' - Read a pair of strings from a .strings file.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+cups_read_strings(cups_file_t *strings, /* I - .strings file */
+ char *buffer, /* I - Line buffer */
+ size_t bufsize, /* I - Size of line buffer */
+ char **id, /* O - Pointer to ID string */
+ char **str) /* O - Pointer to translation string */
+{
+ char *bufptr; /* Pointer into buffer */
+
+
+ while (cupsFileGets(strings, buffer, bufsize))
+ {
+ if (buffer[0] != '\"')
+ continue;
+
+ *id = buffer + 1;
+ bufptr = cups_scan_strings(buffer);
+
+ if (*bufptr != '\"')
+ continue;
+
+ *bufptr++ = '\0';
+
+ while (*bufptr && *bufptr != '\"')
+ bufptr ++;
+
+ if (!*bufptr)
+ continue;
+
+ *str = bufptr + 1;
+ bufptr = cups_scan_strings(bufptr);
+
+ if (*bufptr != '\"')
+ continue;
+
+ *bufptr = '\0';
+
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'cups_scan_strings()' - Scan a quoted string.
+ */
+
+static char * /* O - End of string */
+cups_scan_strings(char *buffer) /* I - Start of string */
+{
+ char *bufptr; /* Pointer into string */
+
+
+ for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++)
+ {
+ if (*bufptr == '\\')
+ {
+ if (bufptr[1] >= '0' && bufptr[1] <= '3' &&
+ bufptr[2] >= '0' && bufptr[2] <= '7' &&
+ bufptr[3] >= '0' && bufptr[3] <= '7')
+ {
+ /*
+ * Decode \nnn octal escape...
+ */
+
+ *bufptr = ((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) |
+ (bufptr[3] - '0');
+ _cups_strcpy(bufptr + 1, bufptr + 4);
+ }
+ else
+ {
+ /*
+ * Decode \C escape...
+ */
+
+ _cups_strcpy(bufptr, bufptr + 1);
+ if (*bufptr == 'n')
+ *bufptr = '\n';
+ else if (*bufptr == 'r')
+ *bufptr = '\r';
+ else if (*bufptr == 't')
+ *bufptr = '\t';
+ }
+ }
+ }
+
+ return (bufptr);
}
+
/*
* End of "$Id$".
*/
*
* Contents:
*
-* cupsCheckDestSupported() - Check that the option and value are supported
- * by the destination.
- * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
- * option/value pair.
- * cupsCopyDestInfo() - Get the supported values/capabilities for the
- * destination.
- * cupsFreeDestInfo() - Free destination information obtained using
- * @link cupsCopyDestInfo@.
- * cupsGetDestMediaByName() - Get media names, dimensions, and margins.
- * cupsGetDestMediaBySize() - Get media names, dimensions, and margins.
- * cups_compare_media_db() - Compare two media entries.
- * cups_copy_media_db() - Copy a media entry.
- * cups_create_media_db() - Create the media database.
- * cups_free_media_cb() - Free a media entry.
- * cups_get_media_db() - Lookup the media entry for a given size.
- * cups_is_close_media_db() - Compare two media entries to see if they are
- * close to the same size.
+ * cupsCheckDestSupported() - Check that the option and value are supported
+ * by the destination.
+ * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
+ * option/value pair.
+ * cupsCopyDestInfo() - Get the supported values/capabilities for the
+ * destination.
+ * cupsFreeDestInfo() - Free destination information obtained using
+ * @link cupsCopyDestInfo@.
+ * cupsGetDestMediaByName() - Get media names, dimensions, and margins.
+ * cupsGetDestMediaBySize() - Get media names, dimensions, and margins.
+ * cups_add_dconstres() - Add a constraint or resolver to an array.
+ * cups_compare_dconstres() - Compare to resolver entries.
+ * cups_compare_media_db() - Compare two media entries.
+ * cups_copy_media_db() - Copy a media entry.
+ * cups_create_constraints() - Create the constraints and resolvers arrays.
+ * cups_create_defaults() - Create the -default option array.
+ * cups_create_media_db() - Create the media database.
+ * cups_free_media_cb() - Free a media entry.
+ * cups_get_media_db() - Lookup the media entry for a given size.
+ * cups_is_close_media_db() - Compare two media entries to see if they are
+ * close to the same size.
+ * cups_test_constraints() - Test constraints.
*/
/*
* Local functions...
*/
+static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
+static int cups_compare_dconstres(_cups_dconstres_t *a,
+ _cups_dconstres_t *b);
static int cups_compare_media_db(_cups_media_db_t *a,
_cups_media_db_t *b);
static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
+static void cups_create_constraints(cups_dinfo_t *dinfo);
+static void cups_create_defaults(cups_dinfo_t *dinfo);
static void cups_create_media_db(cups_dinfo_t *dinfo);
static void cups_free_media_db(_cups_media_db_t *mdb);
static int cups_get_media_db(cups_dinfo_t *dinfo,
cups_size_t *size);
static int cups_is_close_media_db(_cups_media_db_t *a,
_cups_media_db_t *b);
+static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
+ const char *new_option,
+ const char *new_value,
+ int num_options,
+ cups_option_t *options,
+ int *num_conflicts,
+ cups_option_t **conflicts);
+
/*
* 'cupsCheckDestSupported()' - Check that the option and value are supported
case IPP_TAG_BOOLEAN :
return (attr->values[0].boolean);
+ case IPP_TAG_RANGE :
+ int_value = atoi(value);
+
+ for (i = 0; i < attr->num_values; i ++)
+ if (int_value >= attr->values[i].range.lower &&
+ int_value <= attr->values[i].range.upper)
+ return (1);
+ break;
+
case IPP_TAG_RESOLUTION :
if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
{
* user. "new_option" and "new_value" are the setting the user has just
* changed.
*
- * Returns 1 if there is a conflict and 0 otherwise.
+ * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
+ * there was an unrecoverable error such as a resolver loop.
*
* If "num_conflicts" and "conflicts" are not NULL, they are set to contain the
* list of conflicting option/value pairs. Similarly, if "num_resolved" and
* @since CUPS 1.6/OS X 10.8@
*/
-int /* O - 1 if there is a conflict */
+int /* O - 1 if there is a conflict, 0 if none, -1 on error */
cupsCopyDestConflicts(
http_t *http, /* I - Connection to destination */
cups_dest_t *dest, /* I - Destination */
int *num_resolved, /* O - Number of options to resolve */
cups_option_t **resolved) /* O - Resolved options */
{
+ int i, /* Looping var */
+ have_conflicts = 0, /* Do we have conflicts? */
+ changed, /* Did we change something? */
+ tries, /* Number of tries for resolution */
+ num_myconf = 0, /* My number of conflicting options */
+ num_myres = 0; /* My number of resolved options */
+ cups_option_t *myconf = NULL, /* My conflicting options */
+ *myres = NULL, /* My resolved options */
+ *myoption, /* My current option */
+ *option; /* Current option */
+ cups_array_t *active, /* Active conflicts */
+ *pass = NULL, /* Resolvers for this pass */
+ *resolvers = NULL, /* Resolvers we have used */
+ *test; /* Test array for conflicts */
+ _cups_dconstres_t *c, /* Current constraint */
+ *r; /* Current resolver */
+ ipp_attribute_t *attr; /* Current attribute */
+ char value[2048]; /* Current attribute value as string */
+ const char *myvalue; /* Current value of an option */
+
+
/*
* Clear returned values...
*/
* Range check input...
*/
- if (!http || !dest || !dinfo || !new_option || !new_value ||
+ if (!http || !dest || !dinfo ||
(num_conflicts != NULL) != (conflicts != NULL) ||
(num_resolved != NULL) != (resolved != NULL))
return (0);
/*
- * Check for and resolve any conflicts...
+ * Load constraints as needed...
*/
- /* TODO: implement me! */
+ if (!dinfo->constraints)
+ cups_create_constraints(dinfo);
- return (0);
+ if (cupsArrayCount(dinfo->constraints) == 0)
+ return (0);
+
+ if (!dinfo->num_defaults)
+ cups_create_defaults(dinfo);
+
+ /*
+ * If we are resolving, create a shadow array...
+ */
+
+ if (num_resolved)
+ {
+ for (i = num_options, option = options; i > 0; i --, option ++)
+ num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
+
+ if (new_option && new_value)
+ num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
+ }
+ else
+ {
+ num_myres = num_options;
+ myres = options;
+ }
+
+ /*
+ * Check for any conflicts...
+ */
+
+ if (num_resolved)
+ pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
+
+ for (tries = 0; tries < 100; tries ++)
+ {
+ /*
+ * Check for any conflicts...
+ */
+
+ if (num_conflicts || num_resolved)
+ {
+ cupsFreeOptions(num_myconf, myconf);
+
+ num_myconf = 0;
+ myconf = NULL;
+ active = cups_test_constraints(dinfo, new_option, new_value,
+ num_myres, myres, &num_myconf,
+ &myconf);
+ }
+ else
+ active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
+ myres, NULL, NULL);
+
+ have_conflicts = (active != NULL);
+
+ if (!active || !num_resolved)
+ break; /* All done */
+
+ /*
+ * Scan the constraints that were triggered to apply resolvers...
+ */
+
+ if (!resolvers)
+ resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
+
+ for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
+ c;
+ c = (_cups_dconstres_t *)cupsArrayNext(active))
+ {
+ if (cupsArrayFind(pass, c))
+ continue; /* Already applied this resolver... */
+
+ if (cupsArrayFind(resolvers, c))
+ {
+ DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
+ c->name));
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
+ {
+ DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
+ c->name));
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Add the options from the resolver...
+ */
+
+ cupsArrayAdd(pass, r);
+ cupsArrayAdd(resolvers, r);
+
+ for (attr = ippFirstAttribute(r->collection);
+ attr;
+ attr = ippNextAttribute(r->collection))
+ {
+ if (new_option && !strcmp(attr->name, new_option))
+ continue; /* Ignore this if we just changed it */
+
+ if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
+ continue; /* Ignore if the value is too long */
+
+ if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
+ myres, NULL, NULL)) == NULL)
+ {
+ /*
+ * That worked, flag it...
+ */
+
+ changed = 1;
+ }
+ else
+ cupsArrayDelete(test);
+
+ /*
+ * Add the option/value from the resolver regardless of whether it
+ * worked; this makes sure that we can cascade several changes to
+ * make things resolve...
+ */
+
+ num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
+ }
+ }
+
+ if (!changed)
+ {
+ DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ cupsArrayClear(pass);
+
+ cupsArrayDelete(active);
+ active = NULL;
+ }
+
+ if (tries >= 0)
+ {
+ DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Copy resolved options as needed...
+ */
+
+ if (num_resolved)
+ {
+ for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
+ {
+ if ((myvalue = cupsGetOption(myoption->name, num_options,
+ options)) == NULL ||
+ strcmp(myvalue, myoption->value))
+ {
+ if (new_option && !strcmp(new_option, myoption->name) &&
+ new_value && !strcmp(new_value, myoption->value))
+ continue;
+
+ *num_resolved = cupsAddOption(myoption->name, myoption->value,
+ *num_resolved, resolved);
+ }
+ }
+ }
+
+ /*
+ * Clean up...
+ */
+
+ cleanup:
+
+ cupsArrayDelete(active);
+ cupsArrayDelete(pass);
+ cupsArrayDelete(resolvers);
+
+ if (num_resolved)
+ {
+ /*
+ * Free shadow copy of options...
+ */
+
+ cupsFreeOptions(num_myres, myres);
+ }
+
+ if (num_conflicts)
+ {
+ /*
+ * Return conflicting options to caller...
+ */
+
+ *num_conflicts = num_myconf;
+ *conflicts = myconf;
+ }
+ else
+ {
+ /*
+ * Free conflicting options...
+ */
+
+ cupsFreeOptions(num_myconf, myconf);
+ }
+
+ return (have_conflicts);
}
cups_dinfo_t *dinfo; /* Destination information */
ipp_t *request, /* Get-Printer-Attributes request */
*response; /* Supported attributes */
+ int tries, /* Number of tries so far */
+ delay, /* Current retry delay */
+ prev_delay; /* Next retry delay */
const char *uri; /* Printer URI */
char resource[1024]; /* Resource path */
int version; /* IPP version */
* Get the supported attributes...
*/
- version = 20;
+ delay = 1;
+ prev_delay = 1;
+ tries = 0;
+ version = 20;
do
{
if (status == IPP_VERSION_NOT_SUPPORTED && version > 11)
version = 11;
+ else if (status == IPP_PRINTER_BUSY)
+ {
+ sleep(delay);
+
+ delay = _cupsNextDelay(delay, &prev_delay);
+ }
else
return (NULL);
}
+
+ tries ++;
}
- while (!response);
+ while (!response && tries < 10);
+
+ if (!response)
+ return (NULL);
/*
* Allocate a cups_dinfo_t structure and return it...
_cupsStrFree(dinfo->resource);
- ippDelete(dinfo->attrs);
-
cupsArrayDelete(dinfo->constraints);
+ cupsArrayDelete(dinfo->resolvers);
cupsArrayDelete(dinfo->localizations);
cupsArrayDelete(dinfo->media_db);
+ ippDelete(dinfo->attrs);
+
free(dinfo);
}
}
+/*
+ * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
+ */
+
+static void
+cups_add_dconstres(
+ cups_array_t *a, /* I - Array */
+ ipp_t *collection) /* I - Collection value */
+{
+ ipp_attribute_t *attr; /* Attribute */
+ _cups_dconstres_t *temp; /* Current constraint/resolver */
+
+
+ if ((attr = ippFindAttribute(collection, "resolver-name",
+ IPP_TAG_NAME)) == NULL)
+ return;
+
+ if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
+ return;
+
+ temp->name = attr->values[0].string.text;
+ temp->collection = collection;
+
+ cupsArrayAdd(a, temp);
+}
+
+
+/*
+ * 'cups_compare_dconstres()' - Compare to resolver entries.
+ */
+
+static int /* O - Result of comparison */
+cups_compare_dconstres(
+ _cups_dconstres_t *a, /* I - First resolver */
+ _cups_dconstres_t *b) /* I - Second resolver */
+{
+ return (strcmp(a->name, b->name));
+}
+
+
/*
* 'cups_compare_media_db()' - Compare two media entries.
*/
}
+/*
+ * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
+ */
+
+static void
+cups_create_constraints(
+ cups_dinfo_t *dinfo) /* I - Destination information */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* Attribute */
+ _ipp_value_t *val; /* Current value */
+
+
+ dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
+ (cups_afree_func_t)free);
+ dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
+ NULL, NULL, 0, NULL,
+ (cups_afree_func_t)free);
+
+ if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
+ cups_add_dconstres(dinfo->constraints, val->collection);
+ }
+
+ if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
+ cups_add_dconstres(dinfo->resolvers, val->collection);
+ }
+}
+
+
+/*
+ * 'cups_create_defaults()' - Create the -default option array.
+ *
+ * TODO: Need to support collection defaults...
+ */
+
+static void
+cups_create_defaults(
+ cups_dinfo_t *dinfo) /* I - Destination information */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ char name[IPP_MAX_NAME + 1],
+ /* Current name */
+ *nameptr, /* Pointer into current name */
+ value[2048]; /* Current value */
+
+
+ /*
+ * Iterate through the printer attributes looking for xxx-default and adding
+ * xxx=value to the defaults option array.
+ */
+
+ for (attr = ippFirstAttribute(dinfo->attrs);
+ attr;
+ attr = ippNextAttribute(dinfo->attrs))
+ {
+ if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
+ continue;
+
+ if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
+ continue; /* TODO: STR #4096 */
+
+ if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
+ strcmp(nameptr, "-default"))
+ continue;
+
+ strlcpy(name, attr->name, sizeof(name));
+ if ((nameptr = name + strlen(name) - 8) <= name ||
+ strcmp(nameptr, "-default"))
+ continue;
+
+ *nameptr = '\0';
+
+ if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
+ continue;
+
+ dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
+ &dinfo->defaults);
+ }
+}
+
+
/*
* 'cups_create_media_db()' - Create the media database.
*/
}
+/*
+ * 'cups_test_constraints()' - Test constraints.
+ *
+ * TODO: STR #4096 - Need to properly support media-col contraints...
+ */
+
+static cups_array_t * /* O - Active constraints */
+cups_test_constraints(
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *new_option, /* I - Newly selected option */
+ const char *new_value, /* I - Newly selected value */
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ int *num_conflicts, /* O - Number of conflicting options */
+ cups_option_t **conflicts) /* O - Conflicting options */
+{
+ int i, /* Looping var */
+ match; /* Value matches? */
+ int num_matching; /* Number of matching options */
+ cups_option_t *matching; /* Matching options */
+ _cups_dconstres_t *c; /* Current constraint */
+ cups_array_t *active = NULL; /* Active constraints */
+ ipp_attribute_t *attr; /* Current attribute */
+ _ipp_value_t *attrval; /* Current attribute value */
+ const char *value; /* Current value */
+ char temp[1024]; /* Temporary string */
+ int int_value; /* Integer value */
+ int xres_value, /* Horizontal resolution */
+ yres_value; /* Vertical resolution */
+ ipp_res_t units_value; /* Resolution units */
+
+
+ for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
+ c;
+ c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
+ {
+ num_matching = 0;
+ matching = NULL;
+
+ for (attr = ippFirstAttribute(c->collection);
+ attr;
+ attr = ippNextAttribute(c->collection))
+ {
+ if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
+ break; /* TODO: STR #4096 */
+
+ /*
+ * Get the value for the current attribute in the constraint...
+ */
+
+ if (new_option && new_value && !strcmp(attr->name, new_option))
+ value = new_value;
+ else if ((value = cupsGetOption(attr->name, num_options,
+ options)) == NULL)
+ value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
+
+ if (!value)
+ {
+ /*
+ * Not set so this constraint does not apply...
+ */
+
+ break;
+ }
+
+ match = 0;
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ int_value = atoi(value);
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->integer == int_value)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ int_value = !strcmp(value, "true");
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->boolean == int_value)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ int_value = atoi(value);
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (int_value >= attrval->range.lower &&
+ int_value <= attrval->range.upper)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
+ {
+ if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
+ break;
+
+ yres_value = xres_value;
+ }
+
+ if (!strcmp(temp, "dpi"))
+ units_value = IPP_RES_PER_INCH;
+ else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
+ units_value = IPP_RES_PER_CM;
+ else
+ break;
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->resolution.xres == xres_value &&
+ attrval->resolution.yres == yres_value &&
+ attrval->resolution.units == units_value)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_MIMETYPE :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (!strcmp(attrval->string.text, value))
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ default :
+ break;
+ }
+
+ if (!match)
+ break;
+
+ num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
+ }
+
+ if (!attr)
+ {
+ if (!active)
+ active = cupsArrayNew(NULL, NULL);
+
+ cupsArrayAdd(active, c);
+
+ if (num_conflicts && conflicts)
+ {
+ cups_option_t *moption; /* Matching option */
+
+ for (i = num_matching, moption = matching; i > 0; i --, moption ++)
+ *num_conflicts = cupsAddOption(moption->name, moption->value,
+ *num_conflicts, conflicts);
+ }
+ }
+
+ cupsFreeOptions(num_matching, matching);
+ }
+
+ return (active);
+}
+
+
/*
* End of "$Id$".
*/
* cups_block_cb() - Enumeration callback for block API.
* cups_compare_dests() - Compare two destinations.
* cups_dnssd_browse_cb() - Browse for printers.
+ * cups_dnssd_browse_cb() - Browse for printers.
+ * cups_dnssd_client_cb() - Avahi client callback function.
* cups_dnssd_compare_device() - Compare two devices.
* cups_dnssd_free_device() - Free the memory used by a device.
* cups_dnssd_get_device() - Lookup a device and create it as needed.
* cups_dnssd_local_cb() - Browse for local printers.
+ * cups_dnssd_poll_cb() - Wait for input on the specified file
+ * descriptors.
* cups_dnssd_query_cb() - Process query data.
- * cups_dnssd_resolve() - Resolve a Bonjour printer URI.
- * cups_dnssd_resolve_cb() - See if we should continue resolving.
+ * cups_dnssd_resolve() - Resolve a Bonjour printer URI.
+ * cups_dnssd_resolve_cb() - See if we should continue resolving.
* cups_dnssd_unquote() - Unquote a name string.
* cups_find_dest() - Find a destination using a binary search.
* cups_get_default() - Get the default destination from an
# include <dns_sd.h>
#endif /* HAVE_DNSSD */
+#ifdef HAVE_AVAHI
+# include <avahi-client/client.h>
+# include <avahi-client/lookup.h>
+# include <avahi-common/simple-watch.h>
+# include <avahi-common/domain.h>
+# include <avahi-common/error.h>
+# include <avahi-common/malloc.h>
+#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
+#endif /* HAVE_AVAHI */
+
/*
* Constants...
* Types...
*/
-#ifdef HAVE_DNSSD
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
typedef enum _cups_dnssd_state_e /* Enumerated device state */
{
_CUPS_DNSSD_NEW,
_CUPS_DNSSD_PENDING,
_CUPS_DNSSD_ACTIVE,
_CUPS_DNSSD_LOCAL,
+ _CUPS_DNSSD_INCOMPATIBLE,
_CUPS_DNSSD_ERROR
} _cups_dnssd_state_t;
typedef struct _cups_dnssd_data_s /* Enumeration data */
{
+# ifdef HAVE_DNSSD
DNSServiceRef main_ref; /* Main service reference */
+# else /* HAVE_AVAHI */
+ AvahiSimplePoll *simple_poll; /* Polling interface */
+ AvahiClient *client; /* Client information */
+ int got_data; /* Did we get data? */
+# endif /* HAVE_DNSSD */
cups_dest_cb_t cb; /* Callback */
void *user_data; /* User data pointer */
cups_ptype_t type, /* Printer type filter */
typedef struct _cups_dnssd_device_s /* Enumerated device */
{
_cups_dnssd_state_t state; /* State of device listing */
+# ifdef HAVE_DNSSD
DNSServiceRef ref; /* Service reference for query */
+# else /* HAVE_AVAHI */
+ AvahiRecordBrowser *ref; /* Browser for query */
+# endif /* HAVE_DNSSD */
char *domain, /* Domain name */
*fullName, /* Full name */
*regtype; /* Registration type */
static CFArrayRef appleCopyLocations(void);
static CFStringRef appleCopyNetwork(void);
static char *appleGetPaperSize(char *name, int namesize);
-static CFStringRef appleGetPrinter(CFArrayRef locations, CFStringRef network,
- CFIndex *locindex);
+static CFStringRef appleGetPrinter(CFArrayRef locations,
+ CFStringRef network, CFIndex *locindex);
#endif /* __APPLE__ */
static cups_dest_t *cups_add_dest(const char *name, const char *instance,
int *num_dests, cups_dest_t **dests);
cups_dest_t *dest);
#endif /* __BLOCKS__ */
static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b);
-#ifdef HAVE_DNSSD
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+# ifdef HAVE_DNSSD
static void cups_dnssd_browse_cb(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
const char *regtype,
const char *replyDomain,
void *context);
+# else /* HAVE_AVAHI */
+static void cups_dnssd_browse_cb(AvahiServiceBrowser *browser,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ AvahiLookupResultFlags flags,
+ void *context);
+static void cups_dnssd_client_cb(AvahiClient *client,
+ AvahiClientState state,
+ void *context);
+# endif /* HAVE_DNSSD */
static int cups_dnssd_compare_devices(_cups_dnssd_device_t *a,
_cups_dnssd_device_t *b);
static void cups_dnssd_free_device(_cups_dnssd_device_t *device,
const char *serviceName,
const char *regtype,
const char *replyDomain);
+# ifdef HAVE_DNSSD
static void cups_dnssd_local_cb(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
uint16_t rrtype, uint16_t rrclass,
uint16_t rdlen, const void *rdata,
uint32_t ttl, void *context);
+# else /* HAVE_AVAHI */
+static int cups_dnssd_poll_cb(struct pollfd *pollfds,
+ unsigned int num_pollfds,
+ int timeout, void *context);
+static void cups_dnssd_query_cb(AvahiRecordBrowser *browser,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name, uint16_t rrclass,
+ uint16_t rrtype, const void *rdata,
+ size_t rdlen,
+ AvahiLookupResultFlags flags,
+ void *context);
+# endif /* HAVE_DNSSD */
static const char *cups_dnssd_resolve(cups_dest_t *dest, const char *uri,
int msec, int *cancel,
cups_dest_cb_t cb, void *user_data);
static int cups_dnssd_resolve_cb(void *context);
static void cups_dnssd_unquote(char *dst, const char *src,
size_t dstsize);
-#endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
static int cups_find_dest(const char *name, const char *instance,
int num_dests, cups_dest_t *dests, int prev,
int *rdiff);
return (NULL);
}
-#ifdef HAVE_DNSSD
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (strstr(uri, "._tcp"))
{
if ((uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb,
user_data)) == NULL)
return (NULL);
}
-#endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
userpass, sizeof(userpass), hostname, sizeof(hostname),
num_dests; /* Number of destinations */
cups_dest_t *dests = NULL, /* Destinations */
*dest; /* Current destination */
-#ifdef HAVE_DNSSD
- int nfds, /* Number of files responded */
- count, /* Number of queries started */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ int count, /* Number of queries started */
remaining; /* Remainder of timeout */
_cups_dnssd_data_t data; /* Data for callback */
_cups_dnssd_device_t *device; /* Current device */
- int main_fd; /* File descriptor for lookups */
+# ifdef HAVE_DNSSD
+ int nfds, /* Number of files responded */
+ main_fd; /* File descriptor for lookups */
DNSServiceRef ipp_ref, /* IPP browser */
local_ipp_ref; /* Local IPP browser */
-# ifdef HAVE_SSL
+# ifdef HAVE_SSL
DNSServiceRef ipps_ref, /* IPPS browser */
local_ipps_ref; /* Local IPPS browser */
-# endif /* HAVE_SSL */
-# ifdef HAVE_POLL
+# endif /* HAVE_SSL */
+# ifdef HAVE_POLL
struct pollfd pfd; /* Polling data */
-# else
+# else
fd_set input; /* Input set for select() */
struct timeval timeout; /* Timeout for select() */
-# endif /* HAVE_POLL */
-#endif /* HAVE_DNSSD */
-
+# endif /* HAVE_POLL */
+# else /* HAVE_AVAHI */
+ int error; /* Error value */
+ AvahiServiceBrowser *ipp_ref; /* IPP browser */
+# ifdef HAVE_SSL
+ AvahiServiceBrowser *ipps_ref; /* IPPS browser */
+# endif /* HAVE_SSL */
+# endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
/*
* Range check input...
if (i > 0 || msec == 0)
return (1);
-#ifdef HAVE_DNSSD
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
/*
* Get Bonjour-shared printers...
*/
NULL, NULL, 0, NULL,
(cups_afree_func_t)cups_dnssd_free_device);
+# ifdef HAVE_DNSSD
if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)
return (0);
ipp_ref = data.main_ref;
DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
- "_ipp._tcp,_cups", NULL,
+ "_ipp._tcp", NULL,
(DNSServiceBrowseReply)cups_dnssd_browse_cb, &data);
local_ipp_ref = data.main_ref;
DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
kDNSServiceInterfaceIndexLocalOnly,
- "_ipp._tcp,_cups", NULL,
+ "_ipp._tcp", NULL,
(DNSServiceBrowseReply)cups_dnssd_local_cb, &data);
-# ifdef HAVE_SSL
+# ifdef HAVE_SSL
ipps_ref = data.main_ref;
DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
- "_ipps._tcp,_cups", NULL,
+ "_ipps._tcp", NULL,
(DNSServiceBrowseReply)cups_dnssd_browse_cb, &data);
local_ipps_ref = data.main_ref;
DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
kDNSServiceInterfaceIndexLocalOnly,
- "_ipps._tcp,_cups", NULL,
+ "_ipps._tcp", NULL,
(DNSServiceBrowseReply)cups_dnssd_local_cb, &data);
-# endif /* HAVE_SSL */
+# endif /* HAVE_SSL */
+
+# else /* HAVE_AVAHI */
+ if ((data.simple_poll = avahi_simple_poll_new()) == NULL)
+ {
+ DEBUG_puts("cupsEnumDests: Unable to create Avahi simple poll object.");
+ return (1);
+ }
+
+ avahi_simple_poll_set_func(data.simple_poll, cups_dnssd_poll_cb, &data);
+
+ data.client = avahi_client_new(avahi_simple_poll_get(data.simple_poll),
+ 0, cups_dnssd_client_cb, &data,
+ &error);
+ if (!data.client)
+ {
+ DEBUG_puts("cupsEnumDests: Unable to create Avahi client.");
+ avahi_simple_poll_free(data.simple_poll);
+ return (1);
+ }
+
+ ipp_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, "_ipp._tcp", NULL,
+ 0, cups_dnssd_browse_cb, &data);
+# ifdef HAVE_SSL
+ ipps_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, "_ipps._tcp", NULL,
+ 0, cups_dnssd_browse_cb, &data);
+# endif /* HAVE_SSL */
+# endif /* HAVE_DNSSD */
if (msec < 0)
remaining = INT_MAX;
* Check for input...
*/
-# ifdef HAVE_POLL
+# ifdef HAVE_DNSSD
+# ifdef HAVE_POLL
pfd.fd = main_fd;
pfd.events = POLLIN;
nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining);
-# else
+# else
FD_ZERO(&input);
FD_SET(main_fd, &input);
timeout.tv_usec = remaining > 250 ? 250000 : remaining * 1000;
nfds = select(main_fd + 1, &input, NULL, NULL, &timeout);
-# endif /* HAVE_POLL */
+# endif /* HAVE_POLL */
if (nfds > 0)
DNSServiceProcessResult(data.main_ref);
else if (nfds == 0)
remaining -= 250;
+# else /* HAVE_AVAHI */
+ data.got_data = 0;
+
+ if ((error = avahi_simple_poll_iterate(data.simple_poll, 250)) > 0)
+ {
+ /*
+ * We've been told to exit the loop. Perhaps the connection to
+ * Avahi failed.
+ */
+
+ break;
+ }
+
+ if (!data.got_data)
+ remaining -= 250;
+# endif /* HAVE_DNSSD */
+
for (device = (_cups_dnssd_device_t *)cupsArrayFirst(data.devices),
count = 0;
device;
if (!device->ref && device->state == _CUPS_DNSSD_NEW)
{
- device->ref = data.main_ref;
-
DEBUG_printf(("1cupsEnumDests: Querying '%s'.", device->fullName));
+# ifdef HAVE_DNSSD
+ device->ref = data.main_ref;
+
if (DNSServiceQueryRecord(&(device->ref),
kDNSServiceFlagsShareConnection,
0, device->fullName,
DEBUG_puts("1cupsEnumDests: Query failed.");
}
+
+# else /* HAVE_AVAHI */
+ if ((device->ref = avahi_record_browser_new(data.client,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ device->fullName,
+ AVAHI_DNS_CLASS_IN,
+ AVAHI_DNS_TYPE_TXT,
+ 0,
+ cups_dnssd_query_cb,
+ &data)) != NULL)
+ {
+ count ++;
+ }
+ else
+ {
+ device->state = _CUPS_DNSSD_ERROR;
+
+ DEBUG_printf(("1cupsEnumDests: Query failed: %s",
+ avahi_strerror(avahi_client_errno(data.client))));
+ }
+# endif /* HAVE_DNSSD */
}
else if (device->ref && device->state == _CUPS_DNSSD_PENDING)
{
- if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest))
- {
- remaining = -1;
- break;
- }
+ if ((device->type & mask) == type)
+ {
+ if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest))
+ {
+ remaining = -1;
+ break;
+ }
+ }
device->state = _CUPS_DNSSD_ACTIVE;
}
cupsArrayDelete(data.devices);
+# ifdef HAVE_DNSSD
DNSServiceRefDeallocate(ipp_ref);
DNSServiceRefDeallocate(local_ipp_ref);
-# ifdef HAVE_SSL
+# ifdef HAVE_SSL
DNSServiceRefDeallocate(ipp_ref);
DNSServiceRefDeallocate(local_ipp_ref);
-# endif /* HAVE_SSL */
+# endif /* HAVE_SSL */
DNSServiceRefDeallocate(data.main_ref);
-#endif /* HAVE_DNSSD */
+
+# else /* HAVE_AVAHI */
+ avahi_service_browser_free(ipp_ref);
+# ifdef HAVE_SSL
+ avahi_service_browser_free(ipps_ref);
+# endif /* HAVE_SSL */
+
+ avahi_client_free(data.client);
+ avahi_simple_poll_free(data.simple_poll);
+# endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_DNSSD */
return (1);
}
}
-#ifdef HAVE_DNSSD
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+# ifdef HAVE_DNSSD
/*
* 'cups_dnssd_browse_cb()' - Browse for printers.
*/
}
+# else /* HAVE_AVAHI */
+/*
+ * 'cups_dnssd_browse_cb()' - Browse for printers.
+ */
+
+static void
+cups_dnssd_browse_cb(
+ AvahiServiceBrowser *browser, /* I - Browser */
+ AvahiIfIndex interface, /* I - Interface index (unused) */
+ AvahiProtocol protocol, /* I - Network protocol (unused) */
+ AvahiBrowserEvent event, /* I - What happened */
+ const char *name, /* I - Service name */
+ const char *type, /* I - Registration type */
+ const char *domain, /* I - Domain */
+ AvahiLookupResultFlags flags, /* I - Flags */
+ void *context) /* I - Devices array */
+{
+ AvahiClient *client = avahi_service_browser_get_client(browser);
+ /* Client information */
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+
+
+ (void)interface;
+ (void)protocol;
+ (void)context;
+
+ switch (event)
+ {
+ case AVAHI_BROWSER_FAILURE:
+ DEBUG_printf(("cups_dnssd_browse_cb: %s",
+ avahi_strerror(avahi_client_errno(client))));
+ avahi_simple_poll_quit(data->simple_poll);
+ break;
+
+ case AVAHI_BROWSER_NEW:
+ /*
+ * This object is new on the network.
+ */
+
+ if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+ {
+ /*
+ * This comes from the local machine so ignore it.
+ */
+
+ DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".",
+ name));
+ }
+ else
+ {
+ /*
+ * Create a device entry for it if it doesn't yet exist.
+ */
+
+ cups_dnssd_get_device(data, name, type, domain);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ break;
+ }
+}
+
+
+/*
+ * 'cups_dnssd_client_cb()' - Avahi client callback function.
+ */
+
+static void
+cups_dnssd_client_cb(
+ AvahiClient *client, /* I - Client information (unused) */
+ AvahiClientState state, /* I - Current state */
+ void *context) /* I - User data (unused) */
+{
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+
+
+ (void)client;
+
+ /*
+ * If the connection drops, quit.
+ */
+
+ if (state == AVAHI_CLIENT_FAILURE)
+ {
+ DEBUG_puts("cups_dnssd_client_cb: Avahi connection failed.");
+ avahi_simple_poll_quit(data->simple_poll);
+ }
+}
+# endif /* HAVE_DNSSD */
+
+
/*
* 'cups_dnssd_compare_device()' - Compare two devices.
*/
DEBUG_printf(("5cups_dnssd_free_device(device=%p(%s), data=%p)", device,
device->dest.name, data));
+# ifdef HAVE_DNSSD
if (device->ref)
DNSServiceRefDeallocate(device->ref);
+# else /* HAVE_AVAHI */
+ if (device->ref)
+ avahi_record_browser_free(device->ref);
+# endif /* HAVE_DNSSD */
_cupsStrFree(device->domain);
_cupsStrFree(device->fullName);
* Set the "full name" of this service, which is used for queries...
*/
+# ifdef HAVE_DNSSD
DNSServiceConstructFullName(fullName, device->dest.name, device->regtype,
device->domain);
+# else /* HAVE_AVAHI */
+ avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName,
+ regtype, replyDomain);
+# endif /* HAVE_DNSSD */
+
_cupsStrFree(device->fullName);
device->fullName = _cupsStrAlloc(fullName);
if (device->ref)
{
+# ifdef HAVE_DNSSD
DNSServiceRefDeallocate(device->ref);
+# else /* HAVE_AVAHI */
+ avahi_record_browser_free(device->ref);
+# endif /* HAVE_DNSSD */
+
device->ref = 0;
}
}
+# ifdef HAVE_DNSSD
/*
* 'cups_dnssd_local_cb()' - Browse for local printers.
*/
device->state = _CUPS_DNSSD_LOCAL;
}
+# endif /* HAVE_DNSSD */
+
+
+# ifdef HAVE_AVAHI
+/*
+ * 'cups_dnssd_poll_cb()' - Wait for input on the specified file descriptors.
+ *
+ * Note: This function is needed because avahi_simple_poll_iterate is broken
+ * and always uses a timeout of 0 (!) milliseconds.
+ * (Avahi Ticket #364)
+ */
+
+static int /* O - Number of file descriptors matching */
+cups_dnssd_poll_cb(
+ struct pollfd *pollfds, /* I - File descriptors */
+ unsigned int num_pollfds, /* I - Number of file descriptors */
+ int timeout, /* I - Timeout in milliseconds (unused) */
+ void *context) /* I - User data (unused) */
+{
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+ int val; /* Return value */
+
+
+ (void)timeout;
+
+ val = poll(pollfds, num_pollfds, 250);
+
+ if (val < 0)
+ {
+ DEBUG_printf(("cups_dnssd_poll_cb: %s", strerror(errno)));
+ }
+ else if (val > 0)
+ data->got_data = 1;
+
+ return (val);
+}
+# endif /* HAVE_AVAHI */
/*
* 'cups_dnssd_query_cb()' - Process query data.
*/
+# ifdef HAVE_DNSSD
static void
cups_dnssd_query_cb(
DNSServiceRef sdRef, /* I - Service reference */
uint32_t ttl, /* I - Time-to-live */
void *context) /* I - Enumeration data */
{
+# else /* HAVE_AVAHI */
+static void
+cups_dnssd_query_cb(
+ AvahiRecordBrowser *browser, /* I - Record browser */
+ AvahiIfIndex interfaceIndex,
+ /* I - Interface index (unused) */
+ AvahiProtocol protocol, /* I - Network protocol (unused) */
+ AvahiBrowserEvent event, /* I - What happened? */
+ const char *fullName, /* I - Service name */
+ uint16_t rrclass, /* I - Record class */
+ uint16_t rrtype, /* I - Record type */
+ const void *rdata, /* I - TXT record */
+ size_t rdlen, /* I - Length of TXT record */
+ AvahiLookupResultFlags flags, /* I - Flags */
+ void *context) /* I - Enumeration data */
+{
+ AvahiClient *client = avahi_record_browser_get_client(browser);
+ /* Client information */
+# endif /* HAVE_DNSSD */
_cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
/* Enumeration data */
char name[1024], /* Service name */
*device; /* Device */
+# ifdef HAVE_DNSSD
DEBUG_printf(("5cups_dnssd_query_cb(sdRef=%p, flags=%x, "
"interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
"rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
return;
+# else /* HAVE_AVAHI */
+ DEBUG_printf(("5cups_dnssd_query_cb(browser=%p, interfaceIndex=%d, "
+ "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
+ "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)",
+ browser, interfaceIndex, protocol, event, fullName, rrclass,
+ rrtype, rdata, (unsigned)rdlen, flags, context));
+
+ /*
+ * Only process "add" data...
+ */
+
+ if (event != AVAHI_BROWSER_NEW)
+ {
+ if (event == AVAHI_BROWSER_FAILURE)
+ DEBUG_printf(("cups_dnssd_query_cb: %s",
+ avahi_strerror(avahi_client_errno(client))));
+
+ return;
+ }
+# endif /* HAVE_DNSSD */
+
/*
* Lookup the service in the devices array.
*/
if ((device = cupsArrayFind(data->devices, &dkey)) != NULL)
{
/*
- * Found it, pull out the priority and make and model from the TXT
- * record and save it...
+ * Found it, pull out the make and model from the TXT record and save it...
*/
const uint8_t *txt, /* Pointer into data */
model[256], /* Model */
uriname[1024], /* Name for URI */
uri[1024]; /* Printer URI */
+ cups_ptype_t type = CUPS_PRINTER_REMOTE | CUPS_PRINTER_BW;
+ /* Printer type */
+ int saw_printer_type = 0;
+ /* Did we see a printer-type key? */
device->state = _CUPS_DNSSD_PENDING;
make_and_model[0] = '\0';
if ((ptr = strchr(model, ',')) != NULL)
*ptr = '\0';
}
+ else if (!_cups_strcasecmp(key, "note"))
+ device->dest.num_options = cupsAddOption("printer-location", value,
+ device->dest.num_options,
+ &device->dest.options);
+ else if (!_cups_strcasecmp(key, "pdl"))
+ {
+ /*
+ * Look for PDF-capable printers; only PDF-capable printers are shown.
+ */
+
+ const char *start, *next; /* Pointer into value */
+ int have_pdf = 0; /* Have PDF? */
+
+ for (start = value; start && *start; start = next)
+ {
+ if (!_cups_strncasecmp(start, "application/pdf", 15) &&
+ (!start[15] || start[15] == ','))
+ {
+ have_pdf = 1;
+ break;
+ }
+
+ if ((next = strchr(start, ',')) != NULL)
+ next ++;
+ }
+
+ if (!have_pdf)
+ device->state = _CUPS_DNSSD_INCOMPATIBLE;
+ }
else if (!_cups_strcasecmp(key, "printer-type"))
{
- device->dest.num_options = cupsAddOption("printer-type", value,
- device->dest.num_options,
- &device->dest.options);
+ /*
+ * Value is either NNNN or 0xXXXX
+ */
+
+ saw_printer_type = 1;
+ type = strtol(value, NULL, 0);
+ }
+ else if (!saw_printer_type)
+ {
+ if (!_cups_strcasecmp(key, "air") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_AUTHENTICATED;
+ else if (!_cups_strcasecmp(key, "bind") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_BIND;
+ else if (!_cups_strcasecmp(key, "collate") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_COLLATE;
+ else if (!_cups_strcasecmp(key, "color") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_COLOR;
+ else if (!_cups_strcasecmp(key, "copies") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_COPIES;
+ else if (!_cups_strcasecmp(key, "duplex") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_DUPLEX;
+ else if (!_cups_strcasecmp(key, "fax") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_MFP;
+ else if (!_cups_strcasecmp(key, "papercustom") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_VARIABLE;
+ else if (!_cups_strcasecmp(key, "papermax"))
+ {
+ if (!_cups_strcasecmp(value, "legal-a4"))
+ type |= CUPS_PRINTER_SMALL;
+ else if (!_cups_strcasecmp(value, "isoc-a2"))
+ type |= CUPS_PRINTER_MEDIUM;
+ else if (!_cups_strcasecmp(value, ">isoc-a2"))
+ type |= CUPS_PRINTER_LARGE;
+ }
+ else if (!_cups_strcasecmp(key, "punch") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_PUNCH;
+ else if (!_cups_strcasecmp(key, "scan") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_MFP;
+ else if (!_cups_strcasecmp(key, "sort") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_SORT;
+ else if (!_cups_strcasecmp(key, "staple") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_STAPLE;
}
}
/*
- * Save the make-and-model...
+ * Save the printer-xxx values...
*/
+ device->dest.num_options = cupsAddOption("printer-info", name,
+ device->dest.num_options,
+ &device->dest.options);
+
if (make_and_model[0])
{
strlcat(make_and_model, " ", sizeof(make_and_model));
device->dest.num_options,
&device->dest.options);
+ device->type = type;
+ snprintf(value, sizeof(value), "%u", type);
+ device->dest.num_options = cupsAddOption("printer-type", value,
+ device->dest.num_options,
+ &device->dest.options);
+
/*
* Save the URI...
*/
cups_dnssd_unquote(uriname, device->fullName, sizeof(uriname));
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri),
!strcmp(device->regtype, "_ipps._tcp") ? "ipps" : "ipp",
- NULL, uriname, 0, "/cups");
+ NULL, uriname, 0, saw_printer_type ? "/cups" : "/");
DEBUG_printf(("6cups_dnssd_query: printer-uri-supported=\"%s\"", uri));
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif /* HAVE_RESOLV_H */
-#ifdef HAVE_COREFOUNDATION
+#ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
-#endif /* HAVE_COREFOUNDATION */
-#ifdef HAVE_SYSTEMCONFIGURATION
# include <SystemConfiguration/SystemConfiguration.h>
-#endif /* HAVE_SYSTEMCONFIGURATION */
+#endif /* __APPLE__ */
/*
{
if (http->data_encoding == HTTP_ENCODE_CHUNKED)
httpGets(len, sizeof(len), http);
-
- if (http->data_encoding != HTTP_ENCODE_CHUNKED)
- {
- if (http->state == HTTP_POST_RECV)
- http->state ++;
- else
- http->state = HTTP_WAITING;
- }
+ else if (http->state == HTTP_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_WAITING;
}
return (bytes);
http->fd = -1;
}
+ /*
+ * Reset all state (except fields, which may be reused)...
+ */
+
+ http->state = HTTP_WAITING;
+ http->status = HTTP_CONTINUE;
+ http->version = HTTP_1_1;
+ http->keep_alive = HTTP_KEEPALIVE_OFF;
+ memset(&http->_hostaddr, 0, sizeof(http->_hostaddr));
+ http->data_encoding = HTTP_ENCODE_LENGTH;
+ http->_data_remaining = 0;
+ http->used = 0;
+ http->expect = 0;
+ http->data_remaining = 0;
+ http->hostaddr = NULL;
+ http->wused = 0;
+
/*
* Connect to the server...
*/
http->hostaddr = &(addr->addr);
http->error = 0;
- http->status = HTTP_CONTINUE;
- http->state = HTTP_WAITING;
#ifdef HAVE_SSL
if (http->encryption == HTTP_ENCRYPT_ALWAYS)
http->tls = SSL_new(context);
SSL_set_bio(http->tls, bio, bio);
+# ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
SSL_set_tlsext_host_name(http->tls, hostname);
+# endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
if (SSL_connect(http->tls) != 1)
{
*/
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP extension tag larger than 0x7FFFFFFF."), 1);
- DEBUG_printf(("1ippReadIO: bad name length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
else
_cupsSetError(IPP_INTERNAL_ERROR,
_("IPP enum value not 4 bytes."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad integer value length %d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
{
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP boolean value not 1 byte."),
1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
case IPP_TAG_CHARSET :
case IPP_TAG_LANGUAGE :
case IPP_TAG_MIMETYPE :
- if ((*cb)(src, buffer, n) < n)
- {
- DEBUG_puts("1ippReadIO: unable to read string value.");
- _cupsBufferRelease((char *)buffer);
- return (IPP_ERROR);
+ if (n > 0)
+ {
+ if ((*cb)(src, buffer, n) < n)
+ {
+ DEBUG_puts("1ippReadIO: unable to read string value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_ERROR);
+ }
}
buffer[n] = '\0';
if (n != 11)
{
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP date value not 11 bytes."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad date value length %d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
{
_cupsSetError(IPP_INTERNAL_ERROR,
_("IPP resolution value not 9 bytes."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
{
_cupsSetError(IPP_INTERNAL_ERROR,
_("IPP rangeOfInteger value not 8 bytes."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length "
+ "%d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
_cupsSetError(IPP_INTERNAL_ERROR,
_("IPP nameWithLanguage value less than "
"minimum 4 bytes."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad stringWithLanguage value "
+ "length %d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
{
_cupsSetError(IPP_INTERNAL_ERROR,
_("IPP language length overflows value."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad language value length %d.",
+ n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
{
_cupsSetError(IPP_INTERNAL_ERROR,
_("IPP string length overflows value."), 1);
- DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ DEBUG_printf(("1ippReadIO: bad string value length %d.", n));
_cupsBufferRelease((char *)buffer);
return (IPP_ERROR);
}
* we need to carry over...
*/
- if ((*cb)(src, buffer, n) < n)
+ if (n == 0)
+ {
+ _cupsSetError(IPP_INTERNAL_ERROR,
+ _("IPP memberName value is empty."), 1);
+ DEBUG_puts("1ippReadIO: Empty member name value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_ERROR);
+ }
+ else if ((*cb)(src, buffer, n) < n)
{
DEBUG_puts("1ippReadIO: Unable to read member name value.");
_cupsBufferRelease((char *)buffer);
ipp_uchar_t *buffer, /* O - Buffer for data */
size_t length) /* I - Total length */
{
- int tbytes, /* Total bytes read */
- bytes; /* Bytes read this pass */
- char len[32]; /* Length string */
+ int tbytes, /* Total bytes read */
+ bytes; /* Bytes read this pass */
DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)",
if (http->state == HTTP_WAITING)
break;
- if (http->used > 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
+ if (http->used == 0 && !http->blocking)
{
/*
- * Do "fast read" from HTTP buffer directly...
+ * Wait up to 10 seconds for more data on non-blocking sockets...
*/
- if (http->used > (int)(length - tbytes))
- bytes = (int)(length - tbytes);
- else
- bytes = http->used;
-
- if (bytes == 1)
- buffer[0] = http->buffer[0];
- else
- memcpy(buffer, http->buffer, bytes);
-
- http->used -= bytes;
- http->data_remaining -= bytes;
-
- if (http->data_remaining <= INT_MAX)
- http->_data_remaining = (int)http->data_remaining;
- else
- http->_data_remaining = INT_MAX;
-
- if (http->used > 0)
- memmove(http->buffer, http->buffer + bytes, http->used);
-
- if (http->data_remaining == 0)
- {
- if (http->data_encoding == HTTP_ENCODE_CHUNKED)
- {
- /*
- * Get the trailing CR LF after the chunk...
- */
-
- if (!httpGets(len, sizeof(len), http))
- return (-1);
- }
-
- if (http->data_encoding != HTTP_ENCODE_CHUNKED)
- {
- if (http->state == HTTP_POST_RECV)
- http->state ++;
- else
- http->state = HTTP_WAITING;
- }
- }
- }
- else
- {
- if (!http->blocking)
+ if (!httpWait(http, 10000))
{
/*
- * Wait up to 10 seconds for more data on non-blocking sockets...
+ * Signal no data...
*/
- if (!httpWait(http, 10000))
- {
- /*
- * Signal no data...
- */
-
- bytes = -1;
- break;
- }
+ bytes = -1;
+ break;
}
+ }
- if ((bytes = httpRead2(http, (char *)buffer, length - tbytes)) < 0)
- {
+ if ((bytes = httpRead2(http, (char *)buffer, length - tbytes)) < 0)
+ {
#ifdef WIN32
- break;
+ break;
#else
- if (errno != EAGAIN && errno != EINTR)
- break;
+ if (errno != EAGAIN && errno != EINTR)
+ break;
- bytes = 0;
+ bytes = 0;
#endif /* WIN32 */
- }
- else if (bytes == 0)
- break;
}
+ else if (bytes == 0)
+ break;
}
/*
extern void _cupsMessageFree(cups_array_t *a);
extern cups_array_t *_cupsMessageLoad(const char *filename, int unquote);
extern const char *_cupsMessageLookup(cups_array_t *a, const char *m);
+extern cups_array_t *_cupsMessageNew(void *context);
extern void _cupsSetLocale(char *argv[]);
* Contents:
*
* _cupsAppleLanguage() - Get the Apple language identifier associated with
- * a locale ID.
+ * a locale ID.
* _cupsEncodingName() - Return the character encoding name string for the
- * given encoding enumeration.
- * cupsLangDefault() - Return the default language.
- * cupsLangEncoding() - Return the character encoding (us-ascii, etc.) for
- * the given language.
- * cupsLangFlush() - Flush all language data out of the cache.
- * cupsLangFree() - Free language data.
- * cupsLangGet() - Get a language.
- * _cupsLangString() - Get a message string.
+ * given encoding enumeration.
+ * cupsLangDefault() - Return the default language.
+ * cupsLangEncoding() - Return the character encoding (us-ascii, etc.)
+ * for the given language.
+ * cupsLangFlush() - Flush all language data out of the cache.
+ * cupsLangFree() - Free language data.
+ * cupsLangGet() - Get a language.
+ * _cupsLangString() - Get a message string.
* _cupsMessageFree() - Free a messages array.
* _cupsMessageLoad() - Load a .po file into a messages array.
* _cupsMessageLookup() - Lookup a message string.
+ * _cupsMessageNew() - Make a new message catalog array.
* appleLangDefault() - Get the default locale string.
* appleMessageLoad() - Load a message catalog from a localizable bundle.
* cups_cache_lookup() - Lookup a language in the cache...
* cups_message_compare() - Compare two messages.
* cups_message_free() - Free a message.
* cups_message_load() - Load the message catalog for a language.
- * cups_unquote() - Unquote characters in strings...
+ * cups_unquote() - Unquote characters in strings...
*/
/*
* Create an array to hold the messages...
*/
- if ((a = cupsArrayNew3((cups_array_func_t)cups_message_compare, NULL,
- (cups_ahash_func_t)NULL, 0,
- (cups_acopy_func_t)NULL,
- (cups_afree_func_t)cups_message_free)) == NULL)
+ if ((a = _cupsMessageNew(NULL)) == NULL)
{
DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
return (NULL);
}
+/*
+ * '_cupsMessageNew()' - Make a new message catalog array.
+ */
+
+cups_array_t * /* O - Array */
+_cupsMessageNew(void *context) /* I - User data */
+{
+ return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
+ (cups_ahash_func_t)NULL, 0,
+ (cups_acopy_func_t)NULL,
+ (cups_afree_func_t)cups_message_free));
+}
+
+
#ifdef __APPLE__
/*
* 'appleLangDefault()' - Get the default locale string.
* plist as the user data.
*/
- return (cupsArrayNew3((cups_array_func_t)cups_message_compare, (void *)plist,
- (cups_ahash_func_t)NULL, 0,
- (cups_acopy_func_t)NULL,
- (cups_afree_func_t)cups_message_free));
+ return (_cupsMessageNew((void *)plist));
}
# endif /* CUPS_BUNDLEDIR */
#endif /* __APPLE__ */
/*
- * 'ppdPageSize()' - Get the page size record for the given size.
+ * 'ppdPageSize()' - Get the page size record for the named size.
*/
ppd_size_t * /* O - Size record for page or NULL */
*
* Contents:
*
- * _pwgGenerateSize() - Generate a PWG size keyword.
- * _pwgInitSize() - Initialize a PWG size using IPP job template
- * attributes.
- * _pwgMediaForLegacy() - Find a PWG media size by ISO/IPP legacy name.
- * _pwgMediaForPPD() - Find a PWG media size by Adobe PPD name.
- * _pwgMediaForPWG() - Find a PWG media size by 5101.1 self-describing
- * name.
- * _pwgMediaForSize() - Get the PWG media name for a given size.
- * pwg_compare_legacy() - Compare two sizes using the legacy names.
- * pwg_compare_ppd() - Compare two sizes using the PPD names.
- * pwg_compare_pwg() - Compare two sizes using the PWG names.
*/
/*
};
+/*
+ * '_pwgFormatInches()' - Convert and format PWG units as inches.
+ */
+
+char * /* O - String */
+_pwgFormatInches(char *buf, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ int val) /* I - Value in hundredths of millimeters */
+{
+ int thousandths, /* Thousandths of inches */
+ integer, /* Integer portion */
+ fraction; /* Fractional portion */
+
+
+ /*
+ * Convert hundredths of millimeters to thousandths of inches and round to
+ * the nearest thousandth.
+ */
+
+ thousandths = (val * 1000 + 1270) / 2540;
+ integer = thousandths / 1000;
+ fraction = thousandths % 1000;
+
+ /*
+ * Format as a pair of integers (avoids locale stuff), avoiding trailing
+ * zeros...
+ */
+
+ if (fraction == 0)
+ snprintf(buf, bufsize, "%d", integer);
+ else if (fraction % 10)
+ snprintf(buf, bufsize, "%d.%03d", integer, fraction);
+ else if (fraction % 100)
+ snprintf(buf, bufsize, "%d.%02d", integer, fraction / 10);
+ else
+ snprintf(buf, bufsize, "%d.%01d", integer, fraction / 100);
+
+ return (buf);
+}
+
+
+/*
+ * '_pwgFormatMillimeters()' - Convert and format PWG units as millimeters.
+ */
+
+char * /* O - String */
+_pwgFormatMillimeters(char *buf, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ int val) /* I - Value in hundredths of millimeters */
+{
+ int integer, /* Integer portion */
+ fraction; /* Fractional portion */
+
+
+ /*
+ * Convert hundredths of millimeters to integer and fractional portions.
+ */
+
+ integer = val / 100;
+ fraction = val % 100;
+
+ /*
+ * Format as a pair of integers (avoids locale stuff), avoiding trailing
+ * zeros...
+ */
+
+ if (fraction == 0)
+ snprintf(buf, bufsize, "%d", integer);
+ else if (fraction % 10)
+ snprintf(buf, bufsize, "%d.%02d", integer, fraction);
+ else
+ snprintf(buf, bufsize, "%d.%01d", integer, fraction / 10);
+
+ return (buf);
+}
+
+
/*
* '_pwgGenerateSize()' - Generate a PWG size keyword.
*/
int width, /* I - Width of page in 2540ths */
int length) /* I - Length of page in 2540ths */
{
- struct lconv *loc; /* Locale conversion data */
- double uwidth, /* Width in inches or millimeters */
- ulength; /* Height in inches or millimeters */
const char *units; /* Units to report */
char usize[12 + 1 + 12 + 3], /* Unit size: NNNNNNNNNNNNxNNNNNNNNNNNNuu */
*uptr; /* Pointer into unit size */
+ char *(*format)(char *, size_t, int);
+ /* Formatting function */
- loc = localeconv();
-
if ((width % 635) == 0 && (length % 635) == 0)
{
/*
* Use inches since the size is a multiple of 1/4 inch.
*/
- uwidth = width / 2540.0;
- ulength = length / 2540.0;
units = "in";
+ format = _pwgFormatInches;
if (!prefix)
prefix = "oe";
* Use millimeters since the size is not a multiple of 1/4 inch.
*/
- uwidth = width * 0.01;
- ulength = length * 0.01;
units = "mm";
+ format = _pwgFormatMillimeters;
if (!prefix)
prefix = "om";
}
uptr = usize;
- _cupsStrFormatd(uptr, uptr + 12, uwidth, loc);
+ (*format)(uptr, sizeof(usize) - (uptr - usize), width);
uptr += strlen(uptr);
*uptr++ = 'x';
- _cupsStrFormatd(uptr, uptr + 12, ulength, loc);
+ (*format)(uptr, sizeof(usize) - (uptr - usize), length);
uptr += strlen(uptr);
/*
* Safe because usize can hold up to 12 + 1 + 12 + 4 bytes.
*/
- strcpy(uptr, units);
+ memcpy(uptr, units, 3);
if (!name)
name = usize;
* Functions...
*/
+extern char *_pwgFormatInches(char *buf, size_t bufsize, int val);
+extern char *_pwgFormatMillimeters(char *buf, size_t bufsize,
+ int val);
extern void _pwgGenerateSize(char *keyword, size_t keysize,
const char *prefix,
const char *name,
extern _pwg_media_t *_pwgMediaForPWG(const char *pwg);
extern _pwg_media_t *_pwgMediaForSize(int width, int length);
-
# ifdef __cplusplus
}
# endif /* __cplusplus */
ippDelete(response);
response = NULL;
- _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
http->status = status = HTTP_ERROR;
- http->error = EIO;
+ http->error = EINVAL;
}
}
else if (status != HTTP_ERROR)
attr ? attr->values[0].string.text :
ippErrorString(response->request.status.status_code), 0);
}
- else if (status == HTTP_ERROR)
- _cupsSetError(IPP_INTERNAL_ERROR, strerror(http->error), 0);
- else if (status != HTTP_OK)
- _cupsSetHTTPError(status);
return (response);
}
int num_jobs; /* Number of jobs for queue */
cups_job_t *jobs; /* Jobs for queue */
+
if (argc > 1)
{
if (!strcmp(argv[1], "enum"))
{
- int msec; /* Timeout in milliseconds */
-
- if (argc >= 3)
- msec = atoi(argv[2]) * 1000;
- else
- msec = 0;
-
- cupsEnumDests(CUPS_DEST_FLAGS_NONE, msec, NULL, 0, 0, enum_cb, NULL);
+ 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"))
{
unsigned flags, /* I - Destination flags */
cups_dest_t *dest) /* I - Destination */
{
+ int i; /* Looping var */
+ cups_option_t *option; /* Current option */
+
+
if (flags & CUPS_DEST_FLAGS_REMOVED)
- printf("Removed '%s'.\n", dest->name);
+ printf("Removed '%s':\n", dest->name);
else
- printf("Added '%s'.\n", dest->name);
+ 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);
}
if (title)
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
title);
- cupsEncodeOptions(request, num_options, options);
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
/*
* Send the request and get the job-id...
<li><a href="#cups_job_s" title="Job">cups_job_s</a></li>
<li><a href="#cups_option_s" title="Printer Options">cups_option_s</a></li>
<li><a href="#cups_size_s" title="Media Size ">cups_size_s</a></li>
+ <li><a href="#pollfd" title="User data (unused)">pollfd</a></li>
</ul></li>
<li><a href="#VARIABLES">Variables</a><ul class="code">
<li><a href="#CF_RETURNS_RETAINED" title="Get the Apple language identifier associated with a
<dd class="description">Top margin in hundredths of
millimeters</dd>
</dl>
+<h3 class="struct"><a name="pollfd">pollfd</a></h3>
+<p class="description">User data (unused)</p>
+<p class="code">struct pollfd *pollfds, unsigned int num_pollfds, int timeout, void *context) {<br>
+ _cups_dnssd_data_t *data;<br>
+ else if(val 0) data - got_data;<br>
+ void) timeout;<br>
+ int val;<br>
+};</p>
+<h4 class="members">Members</h4>
+<dl>
+<dt>data </dt>
+<dd class="description">Enumeration data</dd>
+<dt>got_data </dt>
+<dt>timeout </dt>
+<dt>val </dt>
+<dd class="description">Return value</dd>
+</dl>
<h2 class="title"><a name="VARIABLES">Variables</a></h2>
<h3 class="variable"><a name="CF_RETURNS_RETAINED">CF_RETURNS_RETAINED</a></h3>
<p class="description">Get the Apple language identifier associated with a
<li><a href="#ppdOpenFd" title="Read a PPD file into memory.">ppdOpenFd</a></li>
<li><a href="#ppdOpenFile" title="Read a PPD file into memory.">ppdOpenFile</a></li>
<li><a href="#ppdPageLength" title="Get the page length for the given size.">ppdPageLength</a></li>
- <li><a href="#ppdPageSize" title="Get the page size record for the given size.">ppdPageSize</a></li>
+ <li><a href="#ppdPageSize" title="Get the page size record for the named size.">ppdPageSize</a></li>
<li><a href="#ppdPageSizeLimits" title="Return the custom page size limits.">ppdPageSizeLimits</a></li>
<li><a href="#ppdPageWidth" title="Get the page width for the given size.">ppdPageWidth</a></li>
<li><a href="#ppdSetConformance" title="Set the conformance level for PPD files.">ppdSetConformance</a></li>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Length of page in points or 0.0</p>
<h3 class="function"><a name="ppdPageSize">ppdPageSize</a></h3>
-<p class="description">Get the page size record for the given size.</p>
+<p class="description">Get the page size record for the named size.</p>
<p class="code">
<a href="#ppd_size_t">ppd_size_t</a> *ppdPageSize (<br>
<a href="#ppd_file_t">ppd_file_t</a> *ppd,<br>
</dt>
<dd>Requires the EXPECT attribute to use the specified value tag(s).
</dd>
+<dt>REPEAT-LIMIT number
+</dt>
+<dd></dd>
+<dd>Specifies the maximum number of times to repeat. The default value is 1000.
+</dd>
<dt>REPEAT-MATCH
</dt>
<dd></dd>
</dt>
<dd>Makes the STATUS apply only if the specified variable is not defined.
</dd>
+<dt>REPEAT-LIMIT number
+</dt>
+<dd></dd>
+<dd>Specifies the maximum number of times to repeat. The default value is 1000.
+</dd>
<dt>REPEAT-MATCH
</dt>
<dd></dd>
can be specified to listen on multiple ports.</P>
+<H2 CLASS="title"><SPAN CLASS="info">CUPS 1.6</SPAN><A NAME="StrictConformance">StrictConformance</A></H2>
+
+<H3>Examples</H3>
+
+<PRE CLASS="command">
+StrictConformance No
+StrictConformance Yes
+</PRE>
+
+<H3>Description</H3>
+
+<P>The <CODE>StrictConformance</CODE> directive specifies whether the scheduler
+requires strict IPP conformance for client requests, for example to not allow
+document attributes in a Create-Job request. The default is
+<code>No</code>.</P>
+
+
<H2 CLASS="title"><SPAN CLASS="info">CUPS 1.5</SPAN><A NAME="SubscriptionPrivateAccess">SubscriptionPrivateAccess</A></H2>
<H3>Examples</H3>
<P>This developer guide documents the guidelines and processes we use when developing and maintaining CUPS and related software. Our goal is to provide reliable and efficient software and documentation that addresses the needs of our users.</P>
+<H2 CLASS="title"><A NAME="OVERVIEW">Overview</A></H2>
+
+<P>CUPS is developed by Apple Inc. and distributed as open source software under a combination of GNU GPL2 and GNU LGPL2 licenses with exceptions to allow linking to OpenSSL (which has a GPL-incompatible license) and for developers on Apple's operating systems to develop CUPS-based software until alternate license terms. Significant contributions to CUPS must be licensed to Apple using the <A HREF="http://www.cups.org/AppleContributorAgreement_2011-03-10.pdf">Apple Contributor Agreement</A>.</P>
+
+<P>Apple releases updates to the CUPS software approximately every three months. Each release has a version number consisting of the major version (currently 1), minor version (currently 6), and patch version (starting at 0) separated by the period, for example "1.6.0". Releases where only the patch version number changes will contain only bug fixes to the previous release, for example "1.6.1" includes bug fixes for the "1.6.0" release. New features require the major or minor version numbers to change, for example "1.6.0" release contains new features compared to the "1.5.3" release. Multiple beta and "candidate" releases generally precede each new feature release, for example "1.5b1", "1.5b2", and "1.5rc1" preceded the "1.5.0" release. Finally, we also post regular Subversion snapshot releases, for example "1.6svn-r10486", which represent a snapshot of the development for the next feature release.</P>
+
+<P>CUPS interfaces, including the C APIs and command-line arguments, environment variables, configuration files, and output format, are stable across patch versions and are generally backwards-compatible with interfaces used in prior major and minor versions. However, program interfaces such as those used by the scheduler to run filter, port monitor, and backend processes for job processing should only be considered stable from the point of view of a filter, port monitor, or backend. Software that simulates the scheduler in order to run those programs outside of CUPS must necessarily be updated when the corresponding interface is changed in a subsequent CUPS release, otherwise undefined behavior can occur.</P>
+
+<P>CUPS C APIs starting with an underscore (_) are considered to be private to CUPS and are not subject to the normal guarantees of stability between CUPS releases and must <em>never</em> be used in non-CUPS source code. Similarly, configuration and state files written by CUPS are considered private if a corresponding man page is not provided with the CUPS release. <em>Never</em> rely on undocumented files or formats when developing software for CUPS. <em>Always</em> use a published C API to access data stored in a file to avoid compatibility problems in the future.</P>
+
+
<H2 CLASS="title"><A NAME="COMMUNICATION">Communication</A></H2>
<H3><A NAME="CONTACT">How to Contact the Developers</A></H3>
-<P>The <A HREF="http://www.cups.org/newsgroups.php">CUPS
-Forums</A> are the primary means of asking questions and
-informally discussing issues and feature requests with the CUPS
-developers. Table 1 shows the available forums and their
-focus:</P>
+<P>The <A HREF="http://lists.cups.org/">CUPS Mailing Lists</A> are the primary means of asking questions and informally discussing issues and feature requests with the CUPS developers and other experienced CUPS users and developers. Table 1 shows the available mailing lists and their focus:</P>
<DIV CLASS="table"><TABLE SUMMARY="CUPS Forums">
-<CAPTION>Table 1: CUPS Forums</CAPTION>
+<CAPTION>Table 1: CUPS Mailing Lists</CAPTION>
<TR>
- <TH>Forum</TH>
+ <TH>List</TH>
<TH>Focus/Purpose</TH>
</TR>
<TR>
- <TD>cups.bugs</TD>
- <TD>Discussion of bugs and issues in the CUPS
- software</TD>
-</TR>
-<TR>
- <TD>cups.commit</TD>
- <TD>Report of all commits to the Subversion repository
- (read-only)</TD>
+ <TD>cups</TD>
+ <TD>General discussion and usage questions for the CUPS software</TD>
</TR>
<TR>
- <TD>cups.ddk</TD>
- <TD>Usage and development questions for the CUPS Driver
- Development Kit</TD>
+ <TD>cups-bugs</TD>
+ <TD>Discussion of bugs and issues in the CUPS software</TD>
</TR>
<TR>
- <TD>cups.development</TD>
- <TD>Development questions and discussion of new features
- in the CUPS software</TD>
+ <TD>cups-commit</TD>
+ <TD>Report of all commits to the Subversion repository (read-only)</TD>
</TR>
<TR>
- <TD>cups.general</TD>
- <TD>Usage questions for the CUPS software</TD>
+ <TD>cups-dev</TD>
+ <TD>Development questions and discussion of new features in the CUPS software</TD>
</TR>
</TABLE></DIV>
+
<H3><A NAME="SUBMIT">How to Submit a Bug Report or Feature Request</A></H3>
-<P>The CUPS "<A HREF="http://www.cups.org/str.php">Bugs &
-Features</A>" page provides access to the CUPS <em>software
-trouble report</em> database and is the formal way to submit a
-bug report or feature request to the CUPS developers. Please
-note, however, that we <em>do not</em> provide answers to usage
-questions or resolve problems in third-party software on this
-page - use the CUPS Forums for that instead.</P>
-
-<P>Unlike discussions that occur on the CUPS Forums, formal bug
-reports and feature requests must be acted on by the CUPS
-developers. This does not mean that every bug report is resolved
-or every feature request is implemented, but we do respond and
-keep track of them all for posterity.</P>
-
-<BLOCKQUOTE>Please use the search feature of the Bugs &
-Features page before submitting a new bug report or feature
-request. If you see an existing report that matches your issue,
-please post a message to that report ("I have this issue as
-well", "I would also like to see", etc.) rather than submitting a
-new report. This helps speed the resolution of your issue by
-reducing the CUPS developers' work load.</BLOCKQUOTE>
+<P>The CUPS "<A HREF="http://www.cups.org/str.php">Bugs & Features</A>" page provides access to the CUPS <em>Software Trouble Report</em> (STR) database and is the formal way to submit a bug report or feature request to the CUPS developers. Please note, however, that we <em>do not</em> provide answers to usage questions or resolve problems in third-party software on this page - use the <A HREF="#CONTACT">CUPS Mailing Lists</A> for that instead.</P>
+
+<P>Unlike discussions that occur on the CUPS Mailing Lists, formal bug reports and feature requests must be acted on by the CUPS developers. This does not mean that every bug report is resolved or every feature request is implemented, but we do respond and keep track of them all for posterity.</P>
+
+<BLOCKQUOTE>Please use the search feature of the Bugs & Features page before submitting a new bug report or feature request. If you see an existing report that matches your issue, please post a message to that report ("I have this issue as well", "I would also like to see", etc.) rather than submitting a new report. This helps speed the resolution of your issue by reducing the CUPS developers' work load and identifying popular issues.</BLOCKQUOTE>
+
<H3><A NAME="PATCH">How to Prepare a Patch</A></H3>
-<P>When submitting a bug report or feature request, you can
-include patch files that resolve the bug or implement the feature
-to speed the inclusion of that bug fix or feature in a new CUPS
-release. For changes to existing files, we prefer a unified diff
-against the current Subversion <VAR>trunk</VAR> branch, which can
-be generated easily using the following Subversion command:</P>
+<P>When submitting a bug report or feature request, you can include patch files that resolve the bug or implement the feature to speed the inclusion of that bug fix or feature in a new CUPS release. For changes to existing files, we prefer a unified diff against the current Subversion <VAR>trunk</VAR> branch, which can be generated easily using the following Subversion command:</P>
<PRE CLASS="command">
svn diff >filename.patch
</PRE>
-<P>If you produce a patch using a released source archive, use
-one of the following commands instead:</P>
+<P>If you produce a patch using a released source archive, use one of the following commands instead:</P>
<PRE CLASS="command">
diff -u oldfilename filename >filename.patch
diff -urN olddirectory directory >filename.patch
</PRE>
-<P>New files and files with significant changes can be submitted
-in their entirety, however that may delay the adoption of your
-changes.</P>
+<P>New files and files with significant changes can be submitted in their entirety, however that may delay the adoption of your changes.</P>
+
+<BLOCKQUOTE><B>Note:</B>
-<BLOCKQUOTE>Patches and files must conform to the standards outlined in the
-"<A HREF="#CODING">Coding Guidelines</A>" and "<A HREF="#MAKEFILES">Makefile
-Guidelines</A>" sections in this document. In addition, since Apple Inc.
-provides CUPS under multiple licenses, we require that you assign the copyright
-for your changes and files to us for inclusion in CUPS.</BLOCKQUOTE>
+<P>Patches and files must conform to the standards outlined in the "<A HREF="#CODING">Coding Guidelines</A>" and "<A HREF="#MAKEFILES">Makefile Guidelines</A>" sections in this document. In addition, since Apple Inc. provides CUPS under multiple licenses, we require that you <A HREF="http://www.cups.org/AppleContributorAgreement_2011-03-10.pdf">license</A> significant changes and files to us for inclusion in CUPS. The CUPS developers will inform you if licensing is required.</P></BLOCKQUOTE>
<H2 CLASS="title"><A NAME="PRACTICES">Software Development Practices</A></H2>
<H3><A NAME="VERSIONS">Version Numbering</A></H3>
-<P>CUPS uses a three-part version number separated by periods to
-represent the major, minor, and patch release numbers. Major
-release numbers indicate large design changes or
-backwards-incompatible changes to the CUPS API or CUPS Imaging
-API. Minor release numbers indicate new features and other
-smaller changes which are backwards-compatible with previous CUPS
-releases. Patch numbers indicate bug fixes to the previous
-release.</P>
-
-<BLOCKQUOTE>When we talk about compatibility, we are talking
-about binary compatibility for public APIs and output format
-compatibility for program interfaces. Changes to configuration
-file formats or the default behavior of programs are not
-generally considered incompatible as the upgrade process can
-normally address such changes gracefully.</BLOCKQUOTE>
+<P>CUPS uses a three-part version number separated by periods to represent the major, minor, and patch release numbers. Major release numbers indicate large design changes or backwards-incompatible changes to the CUPS API or CUPS Imaging API. Minor release numbers indicate new features and other smaller changes which are backwards-compatible with previous CUPS releases. Patch numbers indicate bug fixes to the previous feature release.</P>
+
+<BLOCKQUOTE><B>Note:</B>
+
+<P>When we talk about compatibility, we are talking about binary compatibility for public APIs and output format compatibility for program interfaces. Changes to configuration file formats or the default behavior of programs are not generally considered incompatible as the upgrade process can normally address such changes gracefully.</P></BLOCKQUOTE>
<P>Production releases use the plain version numbers:</P>
2.0.0
</PRE>
-<P>The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is
-called a feature release. Feature releases are the only releases that may
-contain new features. Subsequent production releases in a MAJOR.MINOR series may
-only contain bug fixes.</P>
+<P>The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is called a feature release. Feature releases are the only releases that may contain new features. Subsequent production releases in a MAJOR.MINOR series may only contain bug fixes.</P>
+
+<BLOCKQUOTE><B>Note:</B>
-<BLOCKQUOTE>We did not hold to this limitation in the CUPS 1.1 series for a
-variety of reasons. Starting with CUPS 1.2, the "no new features in a patch
-release" policy has been strictly enforced. The policy has also resulted in
-fewer new features (and interactions!) to validate/test in the subsequence
-feature releases.</BLOCKQUOTE>
+<P>We did not hold to this limitation in the CUPS 1.1 series for a variety of reasons. Starting with CUPS 1.2, the "no new features in a patch release" policy has been strictly enforced. The policy has also resulted in fewer new features (and interactions!) to validate/test in the subsequence feature releases.</P></BLOCKQUOTE>
-<P>Beta-test releases are identified by appending the letter B to the major and
-minor version numbers followed by the beta release number:</P>
+<P>Beta-test releases are identified by appending the letter B to the major and minor version numbers followed by the beta release number:</P>
<PRE CLASS="command">
MAJOR.MINORbNUMBER
1.2b1
</PRE>
-<P>Release candidates are identified by appending the letters RC to the major
-and minor version numbers followed by the release candidate number:</P>
+<P>Release candidates are identified by appending the letters RC to the major and minor version numbers followed by the release candidate number:</P>
<PRE CLASS="command">
MAJOR.MINORrcNUMBER
1.2rc1
</PRE>
-<P>Developer snapshots are identified by appending the letters SVN-R to the
-major and minor version numbers followed by the revision number:</P>
+<P>Developer snapshots are identified by appending the letters SVN-R to the major and minor version numbers followed by the revision number:</P>
<PRE CLASS="command">
MAJOR.MINORsvn-rREV
1.2svn-r1234
</PRE>
-<P>Beta-test releases, release candidates, and developer snapshots are only
-created for new minor releases. Once a production release has been made
-(MAJOR.MINOR.0), subsequent patch releases are issues without preliminary beta
-or release testing.</P>
+<P>Beta-test releases, release candidates, and developer snapshots are only created for new minor releases. Once a production release has been made (MAJOR.MINOR.0), subsequent patch releases are issued without preliminary beta or release testing.</P>
<H3>Version Control (Subversion)</H3>
-<P>The CUPS source files are managed by the Subversion ("SVN")
-software, available at:</P>
+<P>The CUPS source files are managed by the Subversion ("SVN") software, available at:</P>
<PRE CLASS="command">
-<A HREF="http://subversion.tigris.org/" TARGET="_blank">subversion.tigris.org</A>
+<A HREF="http://subversion.apache.org/" TARGET="_blank">subversion.apache.org</A>
</PRE>
-<P>Source files are "checked in" with each change so that
-modifications can be tracked, and each checkin must reference any
-applicable STRs. The following format <em>must</em> be used for
-commit log messages:</P>
+<P>Source files are "checked in" with each change so that modifications can be tracked, and each checkin must reference any applicable STRs. The following format <em>must</em> be used for commit log messages:</P>
<PRE CLASS="command">
Summary of the change on one line followed by bug number (STR #NNNN)
Detailed list of changes.
</PRE>
-<P>Primary development occurs on the <var>trunk</var> branch,
-with changes merged back to release branches as needed. Table 2
-shows the URLs developers use for the various CUPS sub-projects
-and branches:</P>
+<P>Primary development occurs on the <var>trunk</var> branch, with changes merged back to release branches as needed. Table 2 shows the URLs developers use for the various CUPS sub-projects and branches:</P>
<DIV CLASS="table"><TABLE SUMMARY="CUPS Subversion URLs">
<CAPTION>Table 2: CUPS Subversion URLs</CAPTION>
<TH>Purpose</TH>
</TR>
<TR>
- <TD><A HREF="http://svn.easysw.com/public/cups/trunk/">https://svn.easysw.com/public/cups/trunk/</A></TD>
+ <TD><A HREF="http://svn.cups.org/public/cups/trunk/">https://svn.cups.org/public/cups/trunk/</A></TD>
<TD>Primary CUPS development branch</TD>
</TR>
<TR>
- <TD><A HREF="http://svn.easysw.com/public/cups/branches/">https://svn.easysw.com/public/cups/branches/</A></TD>
+ <TD><A HREF="http://svn.cups.org/public/cups/branches/">https://svn.cups.org/public/cups/branches/</A></TD>
<TD>CUPS maintenance branches (merge-only)</TD>
</TR>
<TR>
- <TD><A HREF="http://svn.easysw.com/public/cups/tags/">https://svn.easysw.com/public/cups/tags/</A></TD>
+ <TD><A HREF="http://svn.cups.org/public/cups/tags/">https://svn.cups.org/public/cups/tags/</A></TD>
<TD>CUPS release tags (read-only)</TD>
</TR>
-<TR>
- <TD><A HREF="http://svn.easysw.com/public/windows/trunk/">https://svn.easysw.com/public/windows/trunk/</A></TD>
- <TD>Primary CUPS Windows Driver development branch</TD>
-</TR>
-<TR>
- <TD><A HREF="http://svn.easysw.com/public/windows/branches/">https://svn.easysw.com/public/windows/branches/</A></TD>
- <TD>CUPS Windows Driver maintenance branches (merge-only)</TD>
-</TR>
-<TR>
- <TD><A HREF="http://svn.easysw.com/public/windows/tags/">https://svn.easysw.com/public/windows/tags/</A></TD>
- <TD>CUPS Windows Driver release tags (read-only)</TD>
-</TR>
</TABLE></DIV>
-<P>The branch for a MAJOR.MINOR release are created when the
-first production release (MAJOR.MINOR.0) is made using the name
-"branch-MAJOR.MINOR". Release tags are created for every beta,
-candidate, and production release using the name
-"release-MAJOR.MINOR.PATCHbNUMBER",
-"release-MAJOR.MINOR.PATCHrcNUMBER", or
-"release-MAJOR.MINOR.PATCH", respectively. No release tags are
-created for developer snapshots.</P>
+<P>The branch for a MAJOR.MINOR release are created when the first production release (MAJOR.MINOR.0) is made using the name "branch-MAJOR.MINOR". Release tags are created for every beta, candidate, and production release using the name "release-MAJOR.MINORbNUMBER", "release-MAJOR.MINORrcNUMBER", or "release-MAJOR.MINOR.PATCH", respectively. No release tags are created for developer snapshots.</P>
<H3>Files and Directories</H3>
-<P>File and directory names may not exceed 16 characters in
-length to ensure compatibility with older UNIX filesystems. In
-addition, to avoid problems with case-insensitive filesystems,
-you may not use names which differ only by case, for example
-"ReadMe" and "README" are not allowed in the same directory.</P>
+<P>File and directory names may not exceed 16 characters in length to ensure compatibility with older UNIX filesystems. In addition, to avoid problems with case-insensitive filesystems, you may not use names which differ only by case, for example "ReadMe" and "README" are not allowed in the same directory.</P>
-<P>Source files must be documented and formatted as described in
-"<A HREF="#CODING">Coding Requirements</A>". Make files must
-follow the guidelines in "<A HREF="#MAKEFILE">Makefile
-Guidelines</A>".</P>
+<P>Source files must be documented and formatted as described in "<A HREF="#CODING">Coding Requirements</A>". Makefiles must follow the guidelines in "<A HREF="#MAKEFILE">Makefile Guidelines</A>".</P>
<H3>Build System</H3>
-<P>The CUPS build system uses <A
-HREF="http://www.gnu.org/software/autoconf/">GNU autoconf</A> to
-tailor the library to the local operating system. Project files
-for major IDEs are also provided for Microsoft
-Windows<SUP>®</SUP>. To improve portability, makefiles must
-not make use of the unique features offered by <A
-HREF="http://www.gnu.org/software/make/">GNU make</A>. See the <A
-HREF="#MAKEFILES">Makefile Guidelines</A> section for a
-description of the allowed make features and makefile
-guidelines.</P>
-
-<P>Additional GNU build programs such as <A
-HREF="http://www.gnu.org/software/automake">GNU automake</A> and
-<A HREF="http://www.gnu.org/software/libtool">GNU libtool</A>
-must not be used. GNU automake produces non-portable makefiles
-which depend on GNU-specific extensions, and GNU libtool is not
-portable or reliable enough for CUPS.</P>
+<P>The CUPS build system uses <A HREF="http://www.gnu.org/software/autoconf/">GNU autoconf</A> to tailor the library to the local operating system. Project files for the current release of Visual C++ are also provided for Microsoft Windows<SUP>®</SUP>. To improve portability, makefiles must not make use of features unique to <A HREF="http://www.gnu.org/software/make/">GNU make</A>. See the <A HREF="#MAKEFILES">Makefile Guidelines</A> section for a description of the allowed make features and makefile guidelines.</P>
+
+<P>Additional GNU build programs such as <A HREF="http://www.gnu.org/software/automake">GNU automake</A> and <A HREF="http://www.gnu.org/software/libtool">GNU libtool</A> must not be used. GNU automake produces non-portable makefiles which depend on GNU-specific extensions, and GNU libtool is not portable or reliable enough for CUPS.</P>
<H3><A NAME="PACKAGING">Packaging</A></H3>
-<P>Source packages are created using the
-<VAR>tools/makesrcdist</VAR> script in the Subversion repository.
-The script optionally uses a version number argument:</P>
+<P>Source packages are created using the <VAR>tools/makesrcdist</VAR> script in the Subversion repository. The script optionally uses a version number argument:</P>
<PRE CLASS="command">
tools/makesrcdist
tools/makesrcdist <I>version</I>
</PRE>
-<P>When run with no arguments, the script creates a snapshot of
-the current working copy and names it using the highest revision
-number in the WC, for example
-"/tmp/cups-1.3svn-r1234-source.tar.bz2" and
-"/tmp/cups-1.3svn-r1234-source.tar.gz". When run with two
-arguments, the script creates a release tag in the repository and
-exports that tag, creating the files
-"/tmp/cups-<I>version</I>-source.tar.bz2" and
-"/tmp/cups-<I>version</I>-source.tar.gz".</P>
-
-<P>Binary packages are not generally distributed by the CUPS
-team, however the <VAR>packaging/cups.spec</VAR> and
-<VAR>packaging/cups.list</VAR> files may be used to create binary
-packages on Linux, OS X, and UNIX. The
-<VAR>packaging/cups.spec</VAR> file produces a binary package
-using the <CODE>rpmbuild(8)</CODE> software:</P>
+<P>When run with no arguments, the script creates a snapshot of the current working copy and names it using the highest revision number in the WC, for example "/tmp/cups-1.3svn-r1234-source.tar.bz2" and "/tmp/cups-1.3svn-r1234-source.tar.gz". When run with two arguments, the script creates a release tag in the repository and exports that tag, creating the files
+"/tmp/cups-<I>version</I>-source.tar.bz2" and "/tmp/cups-<I>version</I>-source.tar.gz".</P>
+
+<P>Binary packages are not generally distributed by the CUPS team, however the <VAR>packaging/cups.spec</VAR> and <VAR>packaging/cups.list</VAR> files may be used to create binary packages on Linux, OS X, and UNIX. The <VAR>packaging/cups.spec</VAR> file produces a binary package using the <CODE>rpmbuild(8)</CODE> software:</P>
<PRE CLASS="command">
rpmbuild -ta cups-<I>version</I>-source.tar.gz
</PRE>
-<P>The <VAR>cups.list</VAR> file is generated by the
-<VAR>configure</VAR> script and produces binary packages for many
-platforms using the <A HREF="http://www.easysw.com/epm/"
-TARGET="_blank">EPM</A> software. Table 3 shows the targets that
-are available for each type of binary package:</P>
+<P>The <VAR>cups.list</VAR> file is generated by the <VAR>configure</VAR> script and produces binary packages for many platforms using the <A HREF="http://www.epmhome.org/" TARGET="_blank">EPM</A> software. Table 3 shows the targets that are available for each type of binary package:</P>
<DIV CLASS="table"><TABLE SUMMARY="Binary Package Targets">
<CAPTION>Table 3: Binary Package Targets</CAPTION>
<TD>inst</TD>
<TD>IRIX inst/tardist</TD>
</TR>
-<TR>
- <TD>osx</TD>
- <TD>OS X Install</TD>
-</TR>
<TR>
<TD>pkg</TD>
<TD>Solaris pkgadd</TD>
</TR>
</TABLE></DIV>
-<P>Finally, the <VAR>tools/testrpm</VAR> and
-<VAR>tools/testosx</VAR> scripts can be used to create binary
-packages from the current working copy for testing on Linux and
-OS X, respectively:</P>
+<P>Finally, the <VAR>tools/testrpm</VAR> and <VAR>tools/testosx</VAR> scripts can be used to create binary packages from the current working copy for testing on Linux and OS X, respectively:</P>
<PRE CLASS="command">
tools/testrpm
<H3><A NAME="TESTING">Testing</A></H3>
-<P>Software testing is conducted according to the <A
-HREF="spec-stp.html">CUPS Software Test Plan</A>. This testing is
-automated via the top-level makefile <VAR>test</VAR> target:</P>
+<P>Software testing is conducted according to the <A HREF="spec-stp.html">CUPS Software Test Plan</A>. This testing is automated via the top-level makefile <VAR>test</VAR> target:</P>
<PRE CLASS="command">
make test
</PRE>
-<P>The test environment allows for both short-term automated
-testing and long-term testing and development without the
-automated test script.</P>
+<P>The test environment allows for both short-term automated testing and long-term testing and development without the automated test script.</P>
<H2 CLASS="title"><A NAME="STR">Trouble Report Processing</A></H2>
-<P>A Software Trouble Report ("STR") must be submitted every time
-a user or vendor experiences a problem with the CUPS software.
-Trouble reports are maintained on the <A
-HREF="http://www.cups.org/str.php" TARGET="_blank">Bugs &
-Features</A> page with one of the following states:</P>
+<P>A Software Trouble Report ("STR") must be submitted every time a user or vendor experiences a problem with the CUPS software. Trouble reports are maintained on the <A HREF="http://www.cups.org/str.php" TARGET="_blank">Bugs & Features</A> page with one of the following states:</P>
<OL>
<P>Trouble reports are processed using the following steps.</P>
-<OL>
+<H3>1. Classification</H3>
-<LI>Classification
-
-<P>When a trouble report is received it must be classified at one
-of the following priority levels:</P>
+<P>When a trouble report is received it must be classified at one of the following priority levels:</P>
<OL>
- <LI>Request for enhancement, e.g. asking for a
- feature
+ <LI>Request for enhancement, e.g. asking for a feature
- <LI>Low, e.g. a documentation error or undocumented
- side-effect
+ <LI>Low, e.g. a documentation error or undocumented side-effect
- <LI>Moderate, e.g. unable to print a file or unable to
- compile the software
+ <LI>Moderate, e.g. unable to print a file or unable to compile the software
- <LI>High, e.g. unable to print to a printer or key
- functionality not working
+ <LI>High, e.g. unable to print to a printer or key functionality not working
<LI>Critical, e.g. unable to print at all
</OL>
-<P>Level 4 and 5 trouble reports must be resolved in the next
-software release. Level 2 and 3 trouble reports are scheduled for
-resolution in a specific release at the discretion of the release
-coordinator. Level 1 trouble reports are scheduled for resolution
-in a future feature release.</P>
+<P>Level 4 and 5 trouble reports must be resolved in the next software release. Level 2 and 3 trouble reports are scheduled for resolution in a specific release at the discretion of the release coordinator. Level 1 trouble reports are scheduled for resolution in a future feature release.</P>
<P>The scope of the problem is also determined as:</P>
</OL>
-<LI>Identification
-
-<P>Once the level and scope of the trouble report is determined
-the software sub-system(s) involved with the problem are
-determined. This may involve additional communication with the
-user or vendor to isolate the problem to a specific cause.</P>
+<H3>2. Identification</H3>
-<P>When the sub-system(s) involved have been identified, an
-engineer will then determine the change(s) needed and estimate
-the time required for the change(s).</P>
+<P>Once the level and scope of the trouble report is determined the software sub-system(s) involved with the problem are determined. This may involve additional communication with the user or vendor to isolate the problem to a specific cause.</P>
-<LI>Correction
+<P>When the sub-system(s) involved have been identified, an engineer will then determine the change(s) needed and estimate the time required for the change(s).</P>
-<P>Corrections are scheduled based upon the severity and
-complexity of the problem. Once all changes have been made,
-documented, and tested successfully a new software release
-snapshot is generated. Additional tests are added as necessary
-for proper testing of the changes.</P>
+<H3>3. Correction</H3>
-<LI>Notification
+<P>Corrections are scheduled based upon the severity and complexity of the problem. Once all changes have been made, documented, and tested successfully a new software release snapshot is generated. Additional tests are added as necessary for proper testing of the changes.</P>
-<P>The user or vendor is notified when the fix is available or if
-the problem was caused by user error.</P>
+<H3>4. Notification</H3>
-</OL>
+<P>The user or vendor is notified when the fix is available or if the problem was caused by user error.</P>
<H2 CLASS="title"><A NAME="RELEASES">Release Management</A></H2>
-<P>When testing has been completed successfully, a new source
-package is created using the <VAR>tools/makesrcdist</VAR> script.
-Three types of releases, beta, candidate, and production, are
-created and released to the public using the basic schedule in
-Table 4. At least one beta and one release candidate must be
-created prior to a production release, and there must be at least
-two weeks between the last beta and first candidate and last
-candidate and first production release.</P>
+<P>When testing has been completed successfully, a new source package is created using the <VAR>tools/makesrcdist</VAR> script. Three types of releases - beta, candidate, and production - are created and released to the public using the basic schedule in Table 4. At least one beta and one release candidate must be created prior to a production release, and there must be at least two weeks between the last beta and first candidate and last candidate and first production release.</P>
<DIV CLASS="table"><TABLE SUMMARY="CUPS Basic Release Schedule">
<CAPTION>Table: CUPS Basic Release Schedule</CAPTION>
<H2 CLASS="title"><A NAME="CODING">Coding Guidelines</A></H2>
-<P>These coding guidelines provide detailed information on source
-file formatting and documentation content and must be applied to
-all C and C++ source files provided with CUPS. Source code for
-other languages should conform to these guidelines as allowed by
-the language.</P>
+<P>These coding guidelines provide detailed information on source file formatting and documentation content and must be applied to all C and C++ source files provided with CUPS. Source code for other languages should conform to these guidelines as allowed by the language.</P>
+
<H3>Source Files</H3>
-<P>All source files names shall be 16 characters or less in
-length to ensure compatibility with older UNIX filesystems.
-Source files containing functions shall have an extension of ".c"
-for ANSI C and ".cxx" for C++ source files. All other "include"
-files shall have an extension of ".h".</P>
+<P>All source files names must be 16 characters or less in length to ensure compatibility with older UNIX filesystems. Source files containing functions have an extension of ".c" for ANSI C and ".cxx" for C++ source files. All other "include" files have an extension of ".h". Tabs are set to 8 characters.</P>
-<P>The top of each source file shall contain a header giving the
-name of the file, the purpose or nature of the source file, the
-copyright and licensing notice, and the functions contained in
-the file. The file name and revision information is provided by
-the Subversion "$Id$" tag:</P>
+<BLOCKQUOTE><B>Note:</B>
+
+<P>The ".cxx" extension is used because it is the only common C++ extension between Linux, OS X, UNIX, and Windows.</P></BLOCKQUOTE>
+
+<P>The top of each source file contains a header giving the name of the file, the purpose or nature of the source file, the copyright and licensing notice, and the functions contained in the file. The file name and revision information is provided by the Subversion "$Id$" tag:</P>
<PRE CLASS="command">
/*
*
* Description of file contents.
*
- * Copyright 2010 by Apple Inc.
+ * Copyright 2012 by Apple Inc.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
*/
</PRE>
-<P>For source files that are subject to the Apple OS-Developed
-Software exception, the following additional comment should
-appear after the contact information:</P>
+<P>For source files that are subject to the Apple OS-Developed Software exception, the following additional comment appears after the contact information:</P>
<PRE CLASS="command">
* This file is subject to the Apple OS-Developed Software exception.
</PRE>
-<P>The bottom of each source file shall contain a trailer giving
-the name of the file using the Subversion "$Id$" tag. The
-primary purpose of this is to mark the end of a source file; if
-the trailer is missing it is possible that code has been lost
-near the end of the file:</P>
+<P>The bottom of each source file contains a trailer giving the name of the file using the Subversion "$Id$" tag. The primary purpose of this is to mark the end of a source file; if the trailer is missing it is possible that code has been lost near the end of the file:</P>
<PRE CLASS="command">
/*
*/
</PRE>
+
+<H3>Comments</H3>
+
+<P>All source code utilizes block comments within functions to describe the operations being performed by a group of statements; avoid putting a comment per line unless absolutely necessary, and then consider refactoring the code so that it is not necessary. C source files use the block comment format ("/* comment */") since many vendor C compilers still do not support C99/C++ comments ("// comment"):</P>
+
+<PRE CLASS="command">
+ /*
+ * Clear the state array before we begin...
+ */
+
+ for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
+ array[i] = CUPS_STATE_IDLE;
+
+ /*
+ * Wait for state changes on another thread...
+ */
+
+ do
+ {
+ for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
+ if (array[i] != CUPS_STATE_IDLE)
+ break;
+
+ if (i == (sizeof(array) / sizeof(array[0])))
+ sleep(1);
+ } while (i == (sizeof(array) / sizeof(array[0])));
+</PRE>
+
+<H3>Indentation</H3>
+
+<P>All code blocks enclosed by brackets begin with the opening brace on a new line. The code then follows starting on a new line after the brace and is indented 2 spaces. The closing brace is then placed on a new line following the code at the original indentation:</P>
+
+<PRE CLASS="command">
+{
+ int i; /* Looping var */
+
+
+ /*
+ * Process foobar values from 0 to 999...
+ */
+
+ for (i = 0; i < 1000; i ++)
+ {
+ do_this(i);
+ do_that(i);
+ }
+}
+</PRE>
+
+<P>Single-line statements following "do", "else", "for", "if", and "while" are indented 2 spaces as well. Blocks of code in a "switch" block are indented 4 spaces after each "case" and "default" case:</P>
+
+<PRE CLASS="command">
+switch (array[i])
+{
+ case CUPS_STATE_IDLE :
+ do_this(i);
+ do_that(i);
+ break;
+ default :
+ do_nothing(i);
+ break;
+}
+</PRE>
+
+
+<H3>Spacing</H3>
+
+<P>A space follows each reserved word such as "if", "while", etc. Spaces are not inserted between a function name and the arguments in parenthesis.</P>
+
+
+<H3>Return Values</H3>
+
+<P>Parenthesis surround values returned from a function:</P>
+
+<PRE CLASS="command">
+return (CUPS_STATE_IDLE);
+</PRE>
+
+
<H3>Functions</H3>
-<P>Functions with a global scope shall have a lowercase prefix
-followed by capitalized words ("cupsDoThis", "cupsDoThat",
-"cupsDoSomethingElse", etc.) Private global functions shall begin
-with a leading underscore ("_cupsDoThis", "_cupsDoThat",
-etc.)</P>
+<P>Functions with a global scope have a lowercase prefix followed by capitalized words, e.g., "cupsDoThis", "cupsDoThat", "cupsDoSomethingElse", etc. Private global functions begin with a leading underscore, e.g., "_cupsDoThis", "_cupsDoThat", etc.</P>
-<P>Functions with a local scope shall be declared "static" and be
-lowercase with underscores between words ("do_this", "do_that",
-"do_something_else", etc.)</P>
+<P>Functions with a local scope are declared "static" with lowercase names and underscores between words, e.g., "do_this", "do_that", "do_something_else", etc.</P>
-<P>Each function shall begin with a comment header describing
-what the function does, the possible input limits (if any), and
-the possible output values (if any), and any special information
-needed:</P>
+<P>Each function begins with a comment header describing what the function does, the possible input limits (if any), and the possible output values (if any), and any special information needed:</P>
<PRE CLASS="command">
/*
}
</PRE>
-<P>Return/output values are indicated using an "O" prefix, input
-values are indicated using the "I" prefix, and values that are
-both input and output use the "IO" prefix for the corresponding
-in-line comment.</P>
+<P>Return/output values are indicated using an "O" prefix, input values are indicated using the "I" prefix, and values that are both input and output use the "IO" prefix for the corresponding in-line comment.</P>
-<P>The Mini-XML documentation generator also understands the following
-special text in the function description comment:</P>
+<P>The Mini-XML documentation generator also understands the following special text in the function description comment:</P>
<UL>
- <LI><CODE>@deprecated@</CODE> - Marks the function as
- deprecated (not recommended for new development and
- scheduled for removal)</LI>
+ <LI><CODE>@deprecated@</CODE> - Marks the function as deprecated (not recommended for new development and scheduled for removal)</LI>
- <LI><CODE>@since CUPS <I>version</I>@</CODE> - Marks the
- function as new in the specified version of CUPS.</LI>
+ <LI><CODE>@since CUPS <I>version</I>@</CODE> - Marks the function as new in the specified version of CUPS.</LI>
- <LI><CODE>@private@</CODE> - Marks the function as private.</LI>
+ <LI><CODE>@private@</CODE> - Marks the function as private (same as starting the function name with an underscore)</LI>
</UL>
+
<H3>Variables</H3>
-<P>Variables with a global scope shall be capitalized
-("ThisVariable", "ThatVariable", "ThisStateVariable", etc.) The
-only exception to this rule shall be the CUPS interface library
-global variables which must begin with the prefix "cups"
-("cupsThisVariable", "cupsThatVariable", etc.) Global variables
-shall be replaced by function arguments whenever possible.</P>
+<P>Variables with a global scope are capitalized, e.g., "ThisVariable", "ThatVariable", "ThisStateVariable", etc. Globals in CUPS libraries are either part of the per-thread global values managed by the "_cupsGlobals()" function or are suitably protected for concurrent access. Global variables should be replaced by function arguments whenever possible.</P>
-<P>Variables with a local scope shall be lowercase with
-underscores between words ("this_variable", "that_variable",
-etc.) Any local variables shared by functions within a source
-file shall be declared "static".</P>
+<P>Variables with a local scope are lowercase with underscores between words, e.g., "this_variable", "that_variable", etc. Any "local global" variables shared by functions within a source file are declared "static". As for global variables, local static variables are suitably protected for concurrent access.</P>
-<P>Each variable shall be declared on a separate line and shall
-be immediately followed by a comment block describing the
-variable:</P>
+<P>Each variable is declared on a separate line and is immediately followed by a comment block describing the variable:</P>
<PRE CLASS="command">
-int this_variable; /* The current state of this */
-int that_variable; /* The current state of that */
+int ThisVariable; /* The current state of this */
+static int that_variable; /* The current state of that */
</PRE>
+
<H3>Types</H3>
-<P>All type names shall be lowercase with underscores between
-words and "_t" appended to the end of the name
-("cups_this_type_t", "cups_that_type_t", etc.) Type names must
-start with a prefix, typically "cups" or the name of the program,
-to avoid conflicts with system types. Private type names must
-start with an underscore ("_cups_this_t", "_cups_that_t",
-etc.)</P>
+<P>All type names are lowercase with underscores between words and "_t" appended to the end of the name, e.g., "cups_this_type_t", "cups_that_type_t", etc. Type names start with a prefix, typically "cups" or the name of the program, to avoid conflicts with system types. Private type names start with an underscore, e.g., "_cups_this_t", "_cups_that_t", etc.</P>
-<P>Each type shall have a comment block immediately after the
-typedef:</P>
+<P>Each type has a comment block immediately after the typedef:</P>
<PRE CLASS="command">
-typedef int cups_this_type_t; /* This type is for CUPS foobar options. */
+typedef int cups_this_type_t; /* This type is for CUPS foobar options. */
</PRE>
+
<H3>Structures</H3>
-<P>All structure names shall be lowercase with underscores
-between words and "_s" appended to the end of the name
-("cups_this_s", "cups_that_s", etc.) Structure names must start
-with a prefix, typically "cups" or the name of the program, to
-avoid conflicts with system types. Private structure names must
-start with an underscore ("_cups_this_s", "_cups_that_s",
-etc.)</P>
+<P>All structure names are lowercase with underscores between words and "_s" appended to the end of the name, e.g., "cups_this_s", "cups_that_s", etc. Structure names start with a prefix, typically "cups" or the name of the program, to avoid conflicts with system types. Private structure names start with an underscore, e.g., "_cups_this_s", "_cups_that_s", etc.</P>
-<P>Each structure shall have a comment block immediately after
-the struct and each member shall be documented in accordance with
-the variable naming policy above:</P>
+<P>Each structure has a comment block immediately after the struct and each member is documented similar to the variable naming policy above:</P>
<PRE CLASS="command">
-struct cups_this_struct_s /* This structure is for CUPS foobar options. */
+struct cups_this_struct_s /* This structure is for CUPS foobar options. */
{
- int this_member; /* Current state for this */
- int that_member; /* Current state for that */
+ int this_member; /* Current state for this */
+ int that_member; /* Current state for that */
};
</PRE>
+
<H3>Constants</H3>
-<P>All constant names shall be uppercase with underscored between
-words ("CUPS_THIS_CONSTANT", "CUPS_THAT_CONSTANT", etc.)
-Constants must begin with an uppercase prefix, typically "CUPS"
-or the program name.</P>
+<P>All constant names are uppercase with underscores between words, e.g., "CUPS_THIS_CONSTANT", "CUPS_THAT_CONSTANT", etc. Constants begin with an uppercase prefix, typically "CUPS" or the program name. Private constants start with an underscore, e.g., "_CUPS_THIS_CONSTANT", "_CUPS_THAT_CONSTANT", etc.</P>
-<P>Typed enumerations shall be used whenever possible to allow
-for type checking by the compiler.</P>
+<P>Typed enumerations should be used whenever possible to allow for type checking by the compiler.</P>
-<P>Comment blocks shall immediately follow each constant:</P>
+<P>Comment blocks immediately follow each constant:</P>
<PRE CLASS="command">
enum
};
</PRE>
-<H3>Code</H3>
-
-<P>All source code shall utilize block comments within functions
-to describe the operations being performed by a group of
-statements; avoid putting a comment per line unless absolutely
-necessary, and then consider refactoring the code so that it is
-not necessary:</P>
-
-<PRE CLASS="command">
-/*
- * Clear the state array before we begin...
- */
-
-for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
- array[i] = STATE_IDLE;
-
-/*
- * Wait for state changes...
- */
-
-do
-{
- for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
- if (array[i] != STATE_IDLE)
- break;
-
- if (i == (sizeof(array) / sizeof(array[0])))
- sleep(1);
-} while (i == (sizeof(array) / sizeof(array[0])));
-</PRE>
-
-<H3>Indentation</H3>
-
-<P>All code blocks enclosed by brackets shall begin with the
-opening brace on a new line. The code then follows starting on a
-new line after the brace and is indented 2 spaces. The closing
-brace is then placed on a new line following the code at the
-original indentation:</P>
-
-<PRE CLASS="command">
-{
- int i; /* Looping var */
-
- /*
- * Process foobar values from 0 to 999...
- */
-
- for (i = 0; i < 1000; i ++)
- {
- do_this(i);
- do_that(i);
- }
-}
-</PRE>
-
-<P>Single-line statements following "do", "else", "for", "if",
-and "while" shall be indented 2 spaces as well. Blocks of code
-in a "switch" block shall be indented 4 spaces after each "case"
-and "default" case:</P>
-
-<PRE CLASS="command">
-switch (array[i])
-{
- case STATE_IDLE :
- do_this(i);
- do_that(i);
- break;
- default :
- do_nothing(i);
- break;
-}
-</PRE>
-
-<H3>Spacing</H3>
-
-<P>A space shall follow each reserved word ("if", "while", etc.)
-Spaces shall not be inserted between a function name and the
-arguments in parenthesis.</P>
-
-<H3>Return Values</H3>
-
-<P>Parenthesis shall surround values returned from a function
-using "return":</P>
-
-<PRE CLASS="command">
-return (CUPS_STATE_IDLE);
-</PRE>
-
-<H3>Loops</H3>
-
-<P>Whenever convenient loops should count downward to zero to
-improve program performance:</P>
-
-<PRE CLASS="command">
-for (i = sizeof(array) / sizeof(array[0]) - 1; i >= 0; i --)
- array[i] = CUPS_STATE_IDLE;
-</PRE>
<H2 CLASS="title"><A NAME="MAKEFILES">Makefile Guidelines</A></H2>
-<P>The following is a guide to the makefile-based build system
-used by CUPS. These standards have been developed over the years
-to allow CUPS to be built on as many systems and environments as
-possible.</P>
+<P>The following is a guide to the makefile-based build system used by CUPS. These standards have been developed over the years to allow CUPS to be built on as many systems and environments as possible.</P>
+
<H3>General Organization</H3>
-<P>The CUPS source code is organized functionally into a
-top-level makefile, include file, and subdirectories each with
-their own makefile and dependencies files. The ".in" files are
-template files for the <CODE>autoconf</CODE> software and are
-used to generate a static version of the corresponding file.</P>
+<P>The CUPS source code is organized functionally into a top-level makefile, include file, and subdirectories each with their own makefile and dependencies files. The ".in" files are template files for the <CODE>autoconf</CODE> software and are used to generate a static version of the corresponding file.</P>
+
<H3>Makefile Documentation</H3>
-<P>Each make file must start with the standard CUPS header
-containing the Subversion "$Id$" keyword, description of the
-file, and CUPS copyright and license notice:</P>
+<P>Each makefile starts with the standard CUPS header containing the Subversion "$Id$" keyword, description of the file, and CUPS copyright and license notice:</P>
<PRE CLASS="command">
#
#
# Makefile for ...
#
-# Copyright 2007 by Apple Inc.
+# Copyright 2012 by Apple Inc.
#
# These coded instructions, statements, and computer programs are the
# property of Apple Inc. and are protected by Federal copyright
#
</PRE>
-<P>The end of each makefile must have a comment saying:</P>
+<P>The end of each makefile has a comment saying:</P>
<PRE CLASS="command">
#
#
</PRE>
-<P>The purpose of the trailer is to indicate the end of the
-makefile so that truncations are immediately obvious.</P>
+<P>The purpose of the trailer is to indicate the end of the makefile so that truncations are immediately obvious.</P>
+
<H3>Portable Makefile Construction</H3>
-<P>CUPS uses a common subset of make program syntax to ensure
-that the software can be compiled "out of the box" on as many
-systems as possible. The following is a list of assumptions we
-follow when constructing makefiles:</P>
+<P>CUPS uses a common subset of make program syntax to ensure that the software can be compiled "out of the box" on as many systems as possible. The following is a list of assumptions we follow when constructing makefiles:</P>
<UL>
- <LI><b>Targets</b>; we assume that the make program
- supports the notion of simple targets of the form
- "name:" that perform tab-indented commands that follow
- the target, e.g.:
+ <LI><b>Targets</b>; we assume that the make program supports the notion of simple targets of the form "name:" that perform tab-indented commands that follow the target, e.g.:
<PRE CLASS="command">
target:
→ target commands</PRE></LI>
- <LI><b>Dependencies</b>; we assume that the make program
- supports recursive dependencies on targets, e.g.:
+ <LI><b>Dependencies</b>; we assume that the make program supports recursive dependencies on targets, e.g.:
<PRE CLASS="command">
target: foo bar
→ target commands
bla:
→ bla commands</PRE></LI>
- <LI><b>Variable Definition</b>; we assume that the make program
- supports variable definition on the command-line or in the makefile
- using the following form:
+ <LI><b>Variable Definition</b>; we assume that the make program supports variable definition on the command-line or in the makefile using the following form:
<PRE CLASS="command">
name=value</PRE>
- <LI><b>Variable Substitution</b>; we assume that the make program
- supports variable substitution using the following forms:
+ <LI><b>Variable Substitution</b>; we assume that the make program supports variable substitution using the following forms:
<UL>
<LI><CODE>$(name)</CODE>; substitutes the value of "name",</LI>
- <LI><CODE>($name:.old=.new)</CODE>; substitutes the value of "name"
- with the filename extensions ".old" changed to ".new",</LI>
- <LI><CODE>$(MAKEFLAGS)</CODE>; substitutes the
- command-line options passed to the program
- without the leading hyphen (-),</LI>
+ <LI><CODE>($name:.old=.new)</CODE>; substitutes the value of "name" with the filename extension ".old" changed to ".new",</LI>
+ <LI><CODE>$(MAKEFLAGS)</CODE>; substitutes the command-line options passed to the program without the leading hyphen (-),</LI>
<LI><CODE>$$</CODE>; substitutes a single <CODE>$</CODE> character,</LI>
<LI><CODE>$<</CODE>; substitutes the current source file or dependency, and</LI>
<LI><CODE>$@</CODE>; substitutes the current target name.</LI>
</UL></LI>
- <LI><b>Suffixes</b>; we assume that the make program
- supports filename suffixes with assumed dependencies, e.g.:
+ <LI><b>Suffixes</b>; we assume that the make program supports filename suffixes with assumed dependencies, e.g.:
<PRE CLASS="command">
.SUFFIXES: .c .o
.c.o:
include ../Makedefs
include Dependencies</PRE></LI>
- <LI><b>Comments</b>; we assume that comments begin with
- a <CODE>#</CODE> character and proceed to the end of the
- current line.</LI>
+ <LI><b>Comments</b>; we assume that comments begin with a <CODE>#</CODE> character and proceed to the end of the current line.</LI>
- <LI><b>Line Length</b>; we assume that there is no
- practical limit to the length of lines.</LI>
+ <LI><b>Line Length</b>; we assume that there is no practical limit to the length of lines.</LI>
- <LI><b>Continuation of long lines</b>; we assume that
- the <CODE>\</CODE> character may be placed at the end of a
- line to concatenate two or more lines in a
- makefile to form a single long line.</LI>
+ <LI><b>Continuation of long lines</b>; we assume that the <CODE>\</CODE> character may be placed at the end of a line to concatenate two or more lines in a makefile to form a single long line.</LI>
- <LI><b>Shell</b>; we assume a POSIX-compatible shell is
- present on the build system.</LI>
+ <LI><b>Shell</b>; we assume a POSIX-compatible shell is present on the build system.</LI>
</UL>
+
<H3>Standard Variables</H3>
-<P>The following variables are defined in the "Makedefs" file
-generated by the <CODE>autoconf</CODE> software:</P>
+<P>The following variables are defined in the "Makedefs" file generated by the <CODE>autoconf</CODE> software:</P>
<UL>
- <LI><CODE>AR</CODE>; the library archiver command,</LI>
+ <LI><CODE>ALL_CFLAGS</CODE>; the combined C compiler options,</LI>
- <LI><CODE>ARFLAGS</CODE>; options for the library archiver command,</LI>
-
- <LI><CODE>BUILDROOT</CODE>; optional installation prefix,</LI>
+ <LI><CODE>ALL_CXXFLAGS</CODE>; the combined C++ compiler options,</LI>
- <LI><CODE>MAN1EXT</CODE>; extension for man pages in section 1,</LI>
+ <LI><CODE>AMANDIR</CODE>; the administrative man page installation directory (section 8/1m depending on the platform),</LI>
- <LI><CODE>MAN3EXT</CODE>; extension for man pages in section 3,</LI>
+ <LI><CODE>AR</CODE>; the library archiver command,</LI>
- <LI><CODE>MAN5EXT</CODE>; extension for man pages in section 5,</LI>
+ <LI><CODE>ARFLAGS</CODE>; options for the library archiver command,</LI>
- <LI><CODE>MAN7EXT</CODE>; extension for man pages in section 7,</LI>
+ <LI><CODE>AWK</CODE>; the local awk command,</LI>
- <LI><CODE>MAN8DIR</CODE>; subdirectory for man pages in section 8,</LI>
+ <LI><CODE>BINDIR</CODE>; the binary installation directory,</LI>
- <LI><CODE>MAN8EXT</CODE>; extension for man pages in section 8,</LI>
+ <LI><CODE>BUILDROOT</CODE>; optional installation prefix (defaults to DSTROOT),</LI>
<LI><CODE>CC</CODE>; the C compiler command,</LI>
<LI><CODE>CFLAGS</CODE>; options for the C compiler command,</LI>
+ <LI><CODE>CHMOD</CODE>; the chmod command,</LI>
+
<LI><CODE>CXX</CODE>; the C++ compiler command,</LI>
<LI><CODE>CXXFLAGS</CODE>; options for the C++ compiler command,</LI>
- <LI><CODE>DSOCOMMAND</CODE>; the shared library building command,</LI>
+ <LI><CODE>DATADIR</CODE>; the data file installation directory,</LI>
+
+ <LI><CODE>DSO</CODE>; the C shared library building command,</LI>
+
+ <LI><CODE>DSOXX</CODE>; the C++ shared library building command,</LI>
<LI><CODE>DSOFLAGS</CODE>; options for the shared library building command,</LI>
+ <LI><CODE>INCLUDEDIR</CODE>; the public header file installation directory,</LI>
+
<LI><CODE>INSTALL</CODE>; the <CODE>install</CODE> command,</LI>
<LI><CODE>INSTALL_BIN</CODE>; the program installation command,</LI>
+ <LI><CODE>INSTALL_COMPDATA</CODE>; the compressed data file installation command,</LI>
+
+ <LI><CODE>INSTALL_CONFIG</CODE>; the configuration file installation command,</LI>
+
<LI><CODE>INSTALL_DATA</CODE>; the data file installation command,</LI>
<LI><CODE>INSTALL_DIR</CODE>; the directory installation command,</LI>
<LI><CODE>INSTALL_SCRIPT</CODE>; the shell script installation command,</LI>
+ <LI><CODE>LD</CODE>; the linker command,</LI>
+
<LI><CODE>LDFLAGS</CODE>; options for the linker,</LI>
+ <LI><CODE>LIBDIR</CODE>; the library installation directory,</LI>
+
<LI><CODE>LIBS</CODE>; libraries for all programs,</LI>
<LI><CODE>LN</CODE>; the <CODE>ln</CODE> command,</LI>
- <LI><CODE>OPTIM</CODE>; common compiler optimization options,</LI>
+ <LI><CODE>MAN1EXT</CODE>; extension for man pages in section 1,</LI>
- <LI><CODE>RM</CODE>; the <CODE>rm</CODE> command,</LI>
+ <LI><CODE>MAN3EXT</CODE>; extension for man pages in section 3,</LI>
- <LI><CODE>SHELL</CODE>; the <CODE>sh</CODE> (POSIX shell) command,</LI>
+ <LI><CODE>MAN5EXT</CODE>; extension for man pages in section 5,</LI>
- <LI><CODE>STRIP</CODE>; the <CODE>strip</CODE> command,</LI>
+ <LI><CODE>MAN7EXT</CODE>; extension for man pages in section 7,</LI>
- <LI><CODE>bindir</CODE>; the binary installation directory,</LI>
+ <LI><CODE>MAN8DIR</CODE>; subdirectory for man pages in section 8,</LI>
+
+ <LI><CODE>MAN8EXT</CODE>; extension for man pages in section 8,</LI>
- <LI><CODE>datadir</CODE>; the data file installation directory,</LI>
+ <LI><CODE>MANDIR</CODE>; the man page installation directory,</LI>
- <LI><CODE>exec_prefix</CODE>; the installation prefix for executable files,</LI>
+ <LI><CODE>OPTIM</CODE>; common compiler optimization options,</LI>
+
+ <LI><CODE>PRIVATEINCLUDE</CODE>; the private header file installation directory,</LI>
- <LI><CODE>libdir</CODE>; the library installation directory,</LI>
+ <LI><CODE>RM</CODE>; the <CODE>rm</CODE> command,</LI>
- <LI><CODE>mandir</CODE>; the man page installation directory,</LI>
+ <LI><CODE>SHELL</CODE>; the <CODE>sh</CODE> (POSIX shell) command,</LI>
- <LI><CODE>prefix</CODE>; the installation prefix for non-executable files, and</LI>
+ <LI><CODE>STRIP</CODE>; the <CODE>strip</CODE> command,</LI>
<LI><CODE>srcdir</CODE>; the source directory.</LI>
</UL>
+
<H3>Standard Targets</H3>
-<P>The following standard targets must be defined in each
-makefile:</P>
+<P>The following standard targets are defined in each makefile:</P>
<UL>
- <LI><CODE>all</CODE>; creates all target programs,
- libraries, and documentation files,</LI>
+ <LI><CODE>all</CODE>; creates all target programs, libraries, and documentation files,</LI>
+
+ <LI><CODE>clean</CODE>; removes all target programs libraries, documentation files, and object files,</LI>
+
+ <LI><CODE>depend</CODE>; generates automatic dependencies for any C or C++ source files (also see <A HREF="#DEPEND_TARGET">"Dependencies"</A>),</LI>
+
+ <LI><CODE>distclean</CODE>; removes autoconf-generated files in addition to those removed by the "clean" target,</LI>
+
+ <LI><CODE>install</CODE>; installs all distribution files in their corresponding locations (also see <A HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>),</LI>
- <LI><CODE>clean</CODE>; removes all target programs,
- libraries, documentation files, and object files,</LI>
+ <LI><CODE>install-data</CODE>; installs all data files in their corresponding locations (also see <A HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>),</LI>
- <LI><CODE>depend</CODE>; generates automatic dependencies
- for any C or C++ source files (also see <A
- HREF="#DEPEND_TARGET">"Dependencies"</A>),</LI>
+ <LI><CODE>install-exec</CODE>; installs all executable files in their corresponding locations (also see <A HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>),</LI>
- <LI><CODE>distclean</CODE>; removes autoconf-generated files
- in addition to those removed by the "clean" target,</LI>
+ <LI><CODE>install-headers</CODE>; installs all include files in their corresponding locations (also see <A HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>),</LI>
- <LI><CODE>install</CODE>; installs all distribution files in
- their corresponding locations (also see <A
- HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>), </LI>
+ <LI><CODE>install-libs</CODE>; installs all library files in their corresponding locations (also see <A HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>),</LI>
- <LI><CODE>uninstall</CODE>; removes all distribution files from
- their corresponding locations (also see <A
- HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>), and</LI>
+ <LI><CODE>uninstall</CODE>; removes all distribution files from their corresponding locations (also see <A HREF="#INSTALL_TARGET">"Install/Uninstall Support"</A>), and</LI>
</UL>
<H3>Object Files</H3>
-<P>Object files (the result of compiling a C or C++ source file)
-have the extension ".o".</P>
+<P>Object files (the result of compiling a C or C++ source file) have the extension ".o".</P>
+
<H3>Programs</H3>
-<P>Program files are the result of linking object files and
-libraries together to form an executable file. A typical
-program target looks like:</P>
+<P>Program files are the result of linking object files and libraries together to form an executable file. A typical program target looks like:</P>
<PRE CLASS="command">
program: $(OBJS)
→ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
</PRE>
+
<H3>Static Libraries</H3>
-<P>Static libraries have a prefix of "lib" and the extension
-".a". A typical static library target looks like:</P>
+<P>Static libraries have a prefix of "lib" and the extension ".a". A typical static library target looks like:</P>
<PRE CLASS="command">
libname.a: $(OBJECTS)
→ $(RANLIB) $@
</PRE>
+
<H3>Shared Libraries</H3>
-<P>Shared libraries have a prefix of "lib" and the extension
-".dylib", ".sl", ".so", or "_s.a" depending on the operating
-system. A typical shared library is composed of several targets
-that look like:</P>
+<P>Shared libraries have a prefix of "lib" and the extension ".dylib", ".sl", ".so", or "_s.a" depending on the operating system. A typical shared library is composed of several targets that look like:</P>
<PRE CLASS="command">
libname.so: $(OBJECTS)
<H3>Dependencies</H3>
-<P>Static dependencies are expressed in each makefile following the
-target, for example:</P>
+<P>Static dependencies are expressed in each makefile following the target, for example:</P>
<PRE CLASS="command">
foo: bar
</PRE>
-<P>Static dependencies shall only be used when it is not
-possible to automatically generate them. Automatic dependencies
-are stored in a file named "Dependencies" and included at the
-end of the makefile. The following "depend" target rule shall be
-used to create the automatic dependencies:
+<P>Static dependencies are only used when it is not possible to automatically generate them. Automatic dependencies are stored in a file named "Dependencies" and included at the end of the makefile. The following "depend" target rule is used to create the automatic dependencies:
<PRE CLASS="command">
depend:
-→ $(MAKEDEPEND) -Y -I.. -f Dependencies $(OBJS:.o=.c)
+→ $(CC) -MM $(ALL_CFLAGS) $(OBJS:.o=.c) >Dependencies
</PRE>
-<P>We only regenerate the automatic dependencies on a Linux
-system and express any non-Linux dependencies statically in the
-makefile.</P>
+<P>We regenerate the automatic dependencies on an OS X system and express any non-OS X dependencies statically in the makefile.</P>
+
<H3><A NAME="TARGET_INSTALL">Install/Uninstall Support</A></H3>
-<P>All makefiles must contain install and uninstall rules which
-install or remove the corresponding software. These rules must
-use the <CODE>$(BUILDROOT)</CODE> variable as a prefix to any
-installation directory so that CUPS can be installed in a
-temporary location for packaging by programs like
-<CODE>rpmbuild</CODE>.</P>
-
-<P>The <CODE>$(INSTALL_BIN)</CODE>, <CODE>$(INSTALL_DATA)</CODE>,
-<CODE>$(INSTALL_DIR)</CODE>, <CODE>$(INSTALL_LIB)</CODE>,
-<CODE>$(INSTALL_MAN)</CODE>, and <CODE>$(INSTALL_SCRIPT)</CODE>
-variables must be used when installing files so that the proper
-ownership and permissions are set on the installed files.</P>
-
-<P>The <CODE>$(RANLIB)</CODE> command must be run on any static
-libraries after installation since the symbol table is
-invalidated when the library is copied on some platforms.</P>
+<P>All makefiles contains install and uninstall rules which install or remove the corresponding software. These rules must use the <CODE>$(BUILDROOT)</CODE> variable as a prefix to any installation directory so that CUPS can be installed in a temporary location for packaging by programs like <CODE>rpmbuild</CODE>.</P>
+
+<P>The <CODE>$(INSTALL_BIN)</CODE>, <CODE>$(INSTALL_COMPDATA)</CODE>, <CODE>$(INSTALL_CONFIG)</CODE>, <CODE>$(INSTALL_DATA)</CODE>, <CODE>$(INSTALL_DIR)</CODE>, <CODE>$(INSTALL_LIB)</CODE>, <CODE>$(INSTALL_MAN)</CODE>, and <CODE>$(INSTALL_SCRIPT)</CODE> variables must be used when installing files so that the proper ownership and permissions are set on the installed files.</P>
+
+<P>The <CODE>$(RANLIB)</CODE> command must be run on any static libraries after installation since the symbol table is invalidated when the library is copied on some platforms.</P>
</BODY>
</HTML>
<li><a href="#cupsMediaQualifier3">cupsMediaQualifier3</a></li>
<li><a href="#cupsMinSize">cupsMinSize</a></li>
<li><a href="#cupsMaxSize">cupsMaxSize</a></li>
+ <li><a href="#cupsPageSizeCategory">cupsPageSizeCategory</a></li>
</ul></li>
<li><a href="#ATTRIBUTES">General Attributes</a><ul class="subcontents">
<li><a href="#cupsBackSide">cupsBackSide</a></li>
</pre>
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsPageSizeCategory'>cupsPageSizeCategory</a></h3>
+
+<p class="summary">*cupsPageSizeCategory name/text: "name name2 ... nameN"</p>
+
+<p>This keyword lists related paper size names that should be grouped together in the Print or Page Setup dialogs. The "name" portion of the keyword specifies the root/default size for the grouping. On OS X the grouped paper sizes are shown in a submenu of the main paper size. When omitted, sizes with the same dimensions are automatically grouped together, for example "Letter" and "Letter.Borderless".</p>
+
+<p>Example:</p>
+
+<pre class="command">
+<em>*% Specify grouping of borderless/non-borderless sizes</em>
+*cupsPageSizeCategory Letter/US Letter: "Letter Letter.Borderless"
+*cupsPageSizeCategory A4/A4: "A4 A4.Borderless"
+</pre>
+
+
<h2 class='title'><a name='ATTRIBUTES'>General Attributes</a></h2>
<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsBackSide'>cupsBackSide</a></h3>
*cupsModelNumber: 1234
</pre>
+
<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsPJLCharset'>cupsPJLCharset</a></h3>
<p class='summary'>*cupsPJLCharset: "ISO character set name"</p>
<ul>
+ <li>Added <a href="#cupsPageSizeCategory"><tt>cupsPageSizeCategory</tt></a> keyword (originally defined in CUPS 1.4).</li>
+
<li>Added <a href="#cupsMaxCopies"><tt>cupsMaxCopies</tt></a> keyword.</li>
<li>Documented <a href="#JCLToPDFInterpreter"><tt>JCLToPDFInterpreter</tt></a> keyword.</li>
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Example code for encoding and decoding large amounts of data in a PPD file.
+ * This would typically be used in a driver to save configuration/state
+ * information that could be used by an application.
+ *
+ * Copyright 2012 by Apple Inc.
+ *
+ * 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/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdxReadData() - Read encoded data from a ppd_file_t *.
+ * ppdxWriteData() - Writes encoded data to stderr using PPD: messages.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ppdx.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h> /* For compression of the data */
+
+
+/*
+ * Constants...
+ */
+
+#define PPDX_MAX_VALUE (PPD_MAX_LINE - PPD_MAX_NAME - 4)
+ /* Max value length with delimiters + nul */
+#define PPDX_MAX_CHUNK (PPDX_MAX_VALUE * 3 / 4)
+ /* Max length of each chunk when Base64-encoded */
+
+
+/*
+ * 'ppdxReadData()' - Read encoded data from a ppd_file_t *.
+ *
+ * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns
+ * an allocated pointer to the data (which is nul-terminated for convenience)
+ * along with the length of the data in the variable pointed to by "datasize",
+ * which can be NULL to indicate the caller doesn't need the length.
+ *
+ * Returns NULL if no data is present in the PPD with the prefix.
+ */
+
+void * /* O - Data or NULL */
+ppdxReadData(ppd_file_t *ppd, /* I - PPD file */
+ const char *name, /* I - Keyword prefix */
+ size_t *datasize) /* O - Size of data or NULL for don't care */
+{
+ char keyword[PPD_MAX_NAME], /* Keyword name */
+ decoded[PPDX_MAX_CHUNK + 1];
+ /* Decoded string */
+ unsigned chunk = 0; /* Current chunk number */
+ int len; /* Length of current chunk */
+ ppd_attr_t *attr; /* Keyword/value from PPD file */
+ Bytef *data; /* Pointer to data */
+ size_t alloc_size; /* Allocated size of data buffer */
+ z_stream decomp; /* Decompressor stream */
+ int error; /* Error/status from inflate() */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (datasize)
+ *datasize = 0;
+
+ if (!ppd || !name)
+ return (NULL);
+
+ /*
+ * First see if there are any instances of the named keyword in the PPD...
+ */
+
+ snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk);
+ if ((attr = ppdFindAttr(ppd, keyword, NULL)) == NULL)
+ return (NULL);
+
+ /*
+ * Allocate some memory and start decoding...
+ */
+
+ data = malloc(257);
+ alloc_size = 256;
+
+ memset(&decomp, 0, sizeof(decomp));
+ decomp.next_out = data;
+ decomp.avail_out = 256;
+
+ inflateInit(&decomp);
+
+ do
+ {
+ /*
+ * Grab the data from the current attribute and decode it...
+ */
+
+ len = sizeof(decoded);
+ if (!httpDecode64_2(decoded, &len, attr->value) || len == 0)
+ break;
+
+// printf("chunk %04x has length %d\n", chunk, len);
+
+ /*
+ * Decompress this chunk...
+ */
+
+ decomp.next_in = decoded;
+ decomp.avail_in = len;
+
+ do
+ {
+ Bytef *temp; /* Temporary pointer */
+ size_t temp_size; /* Temporary allocation size */
+
+// printf("Before inflate: avail_in=%d, avail_out=%d\n", decomp.avail_in,
+// decomp.avail_out);
+
+ if ((error = inflate(&decomp, Z_NO_FLUSH)) < Z_OK)
+ {
+ fprintf(stderr, "ERROR: inflate returned %d (%s)\n", error, decomp.msg);
+ break;
+ }
+
+// printf("After inflate: avail_in=%d, avail_out=%d, error=%d\n",
+// decomp.avail_in, decomp.avail_out, error);
+
+ if (decomp.avail_out == 0)
+ {
+ if (alloc_size < 2048)
+ temp_size = alloc_size * 2;
+ else if (alloc_size < PPDX_MAX_DATA)
+ temp_size = alloc_size + 2048;
+ else
+ break;
+
+ if ((temp = realloc(data, temp_size + 1)) == NULL)
+ {
+ free(data);
+ return (NULL);
+ }
+
+ decomp.next_out = temp + (decomp.next_out - data);
+ decomp.avail_out = temp_size - alloc_size;
+ data = temp;
+ alloc_size = temp_size;
+ }
+ }
+ while (decomp.avail_in > 0);
+
+ chunk ++;
+ snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk);
+ }
+ while ((attr = ppdFindAttr(ppd, keyword, NULL)) != NULL);
+
+ inflateEnd(&decomp);
+
+ /*
+ * Nul-terminate the data (usually a string)...
+ */
+
+ *(decomp.next_out) = '\0';
+
+ if (datasize)
+ *datasize = decomp.next_out - data;
+
+ return (data);
+}
+
+
+/*
+ * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages.
+ *
+ * Writes chunked data to the PPD file using PPD: messages sent to stderr for
+ * cupsd. "name" must be a valid PPD keyword string whose length is less than
+ * 37 characters to allow for chunk numbering. "data" provides a pointer to the
+ * data to be written, and "datasize" provides the length.
+ */
+
+extern void
+ppdxWriteData(const char *name, /* I - Base name of keyword */
+ const void *data, /* I - Data to write */
+ size_t datasize) /* I - Number of bytes in data */
+{
+ char buffer[PPDX_MAX_CHUNK], /* Chunk buffer */
+ encoded[PPDX_MAX_VALUE + 1],
+ /* Encoded data */
+ pair[PPD_MAX_LINE], /* name=value pair */
+ line[PPDX_MAX_STATUS], /* Line buffer */
+ *lineptr, /* Current position in line buffer */
+ *lineend; /* End of line buffer */
+ unsigned chunk = 0; /* Current chunk number */
+ int len; /* Length of current chunk */
+ z_stream comp; /* Compressor stream */
+ int error; /* Error/status from deflate() */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!name || (!data && datasize > 0) || datasize > PPDX_MAX_DATA)
+ return;
+
+ strlcpy(line, "PPD:", sizeof(line));
+ lineptr = line + 4;
+ lineend = line + sizeof(line) - 2;
+
+ if (datasize > 0)
+ {
+ /*
+ * Compress and encode output...
+ */
+
+ memset(&comp, 0, sizeof(comp));
+ comp.next_in = (Bytef *)data;
+ comp.avail_in = datasize;
+
+ deflateInit(&comp, 9);
+
+ do
+ {
+ /*
+ * Compress a chunk...
+ */
+
+ comp.next_out = buffer;
+ comp.avail_out = sizeof(buffer);
+
+ if ((error = deflate(&comp, Z_FINISH)) < Z_OK)
+ {
+ fprintf(stderr, "ERROR: deflate returned %d (%s)\n", error, comp.msg);
+ break;
+ }
+
+ /*
+ * Write a chunk...
+ */
+
+ len = sizeof(buffer) - comp.avail_out;
+ httpEncode64_2(encoded, sizeof(encoded), buffer, len);
+
+ len = (int)snprintf(pair, sizeof(pair), " %s%04x=%s", name, chunk,
+ encoded);
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: *%s%04x: \"%s\"\n", name, chunk, encoded);
+#endif /* DEBUG */
+
+ if ((lineptr + len) >= lineend)
+ {
+ *lineptr++ = '\n';
+ *lineptr = '\0';
+
+ fputs(line, stderr);
+ lineptr = line + 4;
+ }
+
+ strlcpy(lineptr, pair, lineend - lineptr);
+ lineptr += len;
+
+ /*
+ * Setup for the next one...
+ */
+
+ chunk ++;
+ }
+ while (comp.avail_out == 0);
+ }
+
+ deflateEnd(&comp);
+
+ /*
+ * Write a trailing empty chunk to signal EOD...
+ */
+
+ len = (int)snprintf(pair, sizeof(pair), " %s%04x=\"\"", name, chunk);
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: *%s%04x: \"\"\n", name, chunk);
+#endif /* DEBUG */
+
+ if ((lineptr + len) >= lineend)
+ {
+ *lineptr++ = '\n';
+ *lineptr = '\0';
+
+ fputs(line, stderr);
+ lineptr = line + 4;
+ }
+
+ strlcpy(lineptr, pair, lineend - lineptr);
+ lineptr += len;
+
+ *lineptr++ = '\n';
+ *lineptr = '\0';
+
+ fputs(line, stderr);
+}
+
+
+/*
+ * End of "$Id$".
+ */
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Header for PPD data encoding example code.
+ *
+ * Copyright 2012 by Apple Inc.
+ *
+ * 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/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ */
+
+#ifndef _PPDX_H_
+# define _PPDX_H_
+
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/ppd.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Maximum amount of data to encode/decode...
+ */
+
+# define PPDX_MAX_STATUS 1024 /* Limit on log messages in 10.6 */
+# define PPDX_MAX_DATA 16777216/* 16MiB */
+
+
+/*
+ * 'ppdxReadData()' - Read encoded data from a ppd_file_t *.
+ *
+ * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns
+ * an allocated pointer to the data (which is nul-terminated for convenience)
+ * along with the length of the data in the variable pointed to by "datasize",
+ * which can be NULL to indicate the caller doesn't need the length.
+ *
+ * Returns NULL if no data is present in the PPD with the prefix.
+ */
+
+extern void *ppdxReadData(ppd_file_t *ppd, const char *name,
+ size_t *datasize);
+
+
+/*
+ * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages.
+ *
+ * Writes chunked data to the PPD file using PPD: messages sent to stderr for
+ * cupsd. "name" must be a valid PPD keyword string whose length is less than
+ * 37 characters to allow for chunk numbering. "data" provides a pointer to the
+ * data to be written, and "datasize" provides the length.
+ */
+
+extern void ppdxWriteData(const char *name, const void *data,
+ size_t datasize);
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_PPDX_H */
+
+/*
+ * End of "$Id$".
+ */
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Test program for PPD data encoding example code.
+ *
+ * Compile with:
+ *
+ * gcc -o testppdx -D_PPD_DEPRECATED="" -g testppdx.c ppdx.c -lcups -lz
+ *
+ * Copyright 2012 by Apple Inc.
+ *
+ * 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/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Read data from a test PPD file and write out new chunks.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "ppdx.h"
+
+
+/*
+ * 'main()' - Read data from a test PPD file and write out new chunks.
+ */
+
+int /* O - Exit status */
+main(void)
+{
+ int status = 0; /* Exit status */
+ FILE *fp; /* File to read */
+ char contents[8193], /* Contents of file */
+ *data; /* Data from PPD */
+ size_t contsize, /* File size */
+ datasize; /* Data size */
+ ppd_file_t *ppd; /* Test PPD */
+
+
+ /*
+ * Open the PPD and get the data from it...
+ */
+
+ ppd = ppdOpenFile("testppdx.ppd");
+ data = ppdxReadData(ppd, "EXData", &datasize);
+
+ /*
+ * Open this source file and read it...
+ */
+
+ fp = fopen("testppdx.c", "r");
+ if (fp)
+ {
+ contsize = fread(contents, 1, sizeof(contents) - 1, fp);
+ fclose(fp);
+ contents[contsize] = '\0';
+ }
+ else
+ {
+ contents[0] = '\0';
+ contsize = 0;
+ }
+
+ /*
+ * Compare data...
+ */
+
+ if (data)
+ {
+ if (contsize != datasize)
+ {
+ fprintf(stderr, "ERROR: PPD has %ld bytes, test file is %ld bytes.\n",
+ (long)datasize, (long)contsize);
+ status = 1;
+ }
+ else if (strcmp(contents, data))
+ {
+ fputs("ERROR: PPD and test file are not the same.\n", stderr);
+ status = 1;
+ }
+
+ if (status)
+ {
+ if ((fp = fopen("testppdx.dat", "wb")) != NULL)
+ {
+ fwrite(data, 1, datasize, fp);
+ fclose(fp);
+ fputs("ERROR: See testppdx.dat for data from PPD.\n", stderr);
+ }
+ else
+ perror("Unable to open 'testppdx.dat'");
+ }
+
+ free(data);
+ }
+
+ printf("Encoding %ld bytes for PPD...\n", (long)contsize);
+
+ ppdxWriteData("EXData", contents, contsize);
+
+ return (1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
--- /dev/null
+*PPD-Adobe: "4.3"
+*%
+*% "$Id$"
+*%
+*% Test PPD file for data encoding example.
+*%
+*% This file cannot be used with any known printers.
+*%
+*% Copyright 2007-2012 by Apple Inc.
+*% Copyright 2002-2006 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/".
+*FormatVersion: "4.3"
+*FileVersion: "1.3"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "TESTPPDX.PPD"
+*Manufacturer: "Apple"
+*Product: "(Test PPDX)"
+*cupsVersion: 1.6
+*ModelName: "Test PPDX"
+*ShortNickName: "Test PPDX"
+*NickName: "Test PPDX for CUPS"
+*PSVersion: "(3010.000) 0"
+*LanguageLevel: "3"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*TTRasterizer: Type42
+*cupsFilter: "application/vnd.cups-raster 0 -"
+*RequiresPageRegion All: True
+
+*% For PageSize, we have put all of the translations in-line...
+*OpenUI *PageSize/Page Size: PickOne
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter/US Letter: "PageSize=Letter"
+*PageSize Letter.Banner/US Letter Banner: "PageSize=Letter.Banner"
+*PageSize Letter.Fullbleed/US Letter Borderless: "PageSize=Letter.Fullbleed"
+*PageSize A4/A4: "PageSize=A4"
+*PageSize Env10/#10 Envelope: "PageSize=Env10"
+*CloseUI: *PageSize
+
+*% For PageRegion, we have separated the translations...
+*OpenUI *PageRegion/Page Region: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter/US Letter: "PageRegion=Letter"
+*PageRegion Letter.Banner/US Letter Banner: "PageRegion=Letter.Fullbleed"
+*PageRegion Letter.Fullbleed/US Letter Borderless: "PageRegion=Letter.Fullbleed"
+*PageRegion A4/A4: "PageRegion=A4"
+*PageRegion Env10/#10 Envelope: "PageRegion=Env10"
+*CloseUI: *PageRegion
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter: "18 36 594 756"
+*ImageableArea Letter.Banner: "18 0 594 792"
+*ImageableArea Letter.Fullbleed: "0 0 612 792"
+*ImageableArea A4: "18 36 577 806"
+*ImageableArea Env10: "18 36 279 648"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter: "612 792"
+*PaperDimension Letter.Banner: "612 792"
+*PaperDimension Letter.Fullbleed: "612 792"
+*PaperDimension A4: "595 842"
+*PaperDimension Env10: "297 684"
+
+*DefaultFont: Courier
+*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM
+*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM
+*Font Bookman-Demi: Standard "(001.004S)" Standard ROM
+*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM
+*Font Bookman-Light: Standard "(001.004S)" Standard ROM
+*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM
+*Font Courier: Standard "(002.004S)" Standard ROM
+*Font Courier-Bold: Standard "(002.004S)" Standard ROM
+*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM
+*Font Courier-Oblique: Standard "(002.004S)" Standard ROM
+*Font Helvetica: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM
+*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM
+*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM
+*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
+*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
+*Font Symbol: Special "(001.007S)" Special ROM
+*Font Times-Bold: Standard "(001.007S)" Standard ROM
+*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
+*Font Times-Italic: Standard "(001.007S)" Standard ROM
+*Font Times-Roman: Standard "(001.007S)" Standard ROM
+*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM
+*Font ZapfDingbats: Special "(001.004S)" Standard ROM
+*%
+*% End of "$Id$".
+*%
+*EXData0000: "eNqlVm1v2zYQ/hz/ipvWznLgl6T7siXrgCJ2AANBEyQpNmAtAlqiLG4SKZBU7LTIf9/dUW9Og32ZAFsieffcc88dKS2OR3AM0Zt1+ibCJxoA3EvnobJma0UJmbFwc7OEVHgBUicmVXoLci/KqpCAQznvHC9MWSmc3Smfn3WzdG2TBGYGPCJXVbqH2fIBQR+Wq5vb1cWH+9XyfRTBbNs"
+*EXData0001: "GcwTaG6zIqkrh7evgzDVk1Xb3MO7k9N3sHmCDxWRWeuk53KfSxf4paC087ZOvDLaTcF54WUptcdnoVO0KavaS9um7EBYCT6XAQhnK2n9E5hsEIc9yQ6XvUw8RkEelzKVVhQI2TAMEIXYzQGWClmoTU002L1GgmwVIpraF0ozXYoOGUkZXa0vVh/vVnO/91FA2+UqycHlpi5SyMWjhI0"
+*EXData0002: "lBq9kqKmbEl9BFCOETDwOuuHoFxAaQZQKueooljmVJRiK1NUSHL+UKhEaiQpPES599XZYrHb7eZUkLmx20U0lLsPAK7e/I2agDcME1S7vpst5aMsUM0U7kzmd5S03CeyIkVaqAujPdWm759SKB1PYAa3UqShDzNrShDcLdycHJgk3VnlWUnQcgdJXut/HCEvRqMFw62DSricSOeEfYI"
+*EXData0003: "HFGldfN5Y/djIyT85nyqzDz//WDKolgHcxE3ah6N2hDjQHj8vxkr7Y/oWhzDNYKt9spz89aOljnKo1HpZPRtBMDGzep7ODlntxcuAJfrq9XR0XFWnQfcS6KBZbJElC2SXNijo6Spwl+/nP7685cp27aVoY3A9NEesSi9Bm3ZZUopMpxTX+WDZzx6nPZRaRggCIFG5z1Iuwh0DDxQNAQ"
+*EXData0004: "OcbnJtJ9qyPpNAISHpW/rmTYOrRC4m6l53FfAuW50OzH4IBq4Y1ciVYcdUcQ/kWTc7Rib7baU0GJYIyDKUSrP2kQTeGnNgd0eEmHNoSpbSL7orPch1SyCkNkuDn0gEKC0JENLFQGcVZN8InKjYVqJCU3wovbmk3hdMoCmqybo+2Ds+h/zs5ZUhgn427c1btF/YKw488nY1p+xp8snDw"
+*EXData0005: "CM3WJ0OzASUA7kH27eSgtwNteZJqmDklRnN9ajTTYf3wHjpxeTXYkGK4F30W4zaV1lI1bm+vb8+4+Llw8LagMxm1nIZt155O3fz8s46mDVh7xYXR20kbcNqMWzKNWgDdRjsNM8/8TyIxeTwmkrIaVITze0m/9i4ekqbO6JmSVNqE/nWilMwWQrL/waMTMCwdxqT5+NVOQ4LUa7tNNJk"
+*EXData0006: "kOQfP11dTRqvb51GGZ9WXC3usl6nrpNe667v072jN8wgNn9mpMPj49V0n5t7043hwrezNTaOPmmxCacZ5QbjIf44mhwIhDsmpDFpmxSabopW7TdO1yftNxA2LZP6rifCSbL/g8Th06E/GPoWOLS30tdWQ3yKw+fu1bHCDsDTNXyPhbfAvwxF0gE="
+*EXData0007: ""
</pre>
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsPageSizeCategory'>cupsPageSizeCategory</a></h3>
+
+<p class="summary">*cupsPageSizeCategory name/text: "name name2 ... nameN"</p>
+
+<p>This keyword lists related paper size names that should be grouped together in the Print or Page Setup dialogs. The "name" portion of the keyword specifies the root/default size for the grouping. On OS X the grouped paper sizes are shown in a submenu of the main paper size. When omitted, sizes with the same dimensions are automatically grouped together, for example "Letter" and "Letter.Borderless".</p>
+
+<p>Example:</p>
+
+<pre class="command">
+<em>*% Specify grouping of borderless/non-borderless sizes</em>
+*cupsPageSizeCategory Letter/US Letter: "Letter Letter.Borderless"
+*cupsPageSizeCategory A4/A4: "A4 A4.Borderless"
+</pre>
+
+
<h2 class='title'><a name='ATTRIBUTES'>General Attributes</a></h2>
<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsBackSide'>cupsBackSide</a></h3>
*cupsModelNumber: 1234
</pre>
+
<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsPJLCharset'>cupsPJLCharset</a></h3>
<p class='summary'>*cupsPJLCharset: "ISO character set name"</p>
<ul>
+ <li>Added <a href="#cupsPageSizeCategory"><tt>cupsPageSizeCategory</tt></a> keyword (originally defined in CUPS 1.4).</li>
+
<li>Added <a href="#cupsMaxCopies"><tt>cupsMaxCopies</tt></a> keyword.</li>
<li>Documented <a href="#JCLToPDFInterpreter"><tt>JCLToPDFInterpreter</tt></a> keyword.</li>
dst=""
dir_arg=""
-function gzipcp {
+gzipcp() {
# gzipcp from to
$gzipprog -9 <"$1" >"$2"
}
.\" 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/".
.\"
-.TH cupsd.conf 5 "CUPS" "15 February 2012" "Apple Inc."
+.TH cupsd.conf 5 "CUPS" "18 May 2012" "Apple Inc."
.SH NAME
cupsd.conf \- server configuration file for cups
.SH DESCRIPTION
.br
Listens on the specified port for encrypted connections.
.TP 5
+StrictConformance Yes
+.TP 5
+StrictConformance No
+.br
+Specifies whether the scheduler requires clients to strictly adhere to the IPP
+specifications. The default is No.
+.TP 5
SubscriptionPrivateAccess all
.TP 5
SubscriptionPrivateAccess default
.\" 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/".
.\"
-.TH filter 7 "CUPS" "13 April 2012" "Apple Inc."
+.TH filter 7 "CUPS" "18 May 2012" "Apple Inc."
.SH NAME
filter \- cups file conversion filter interface
.SH SYNOPSIS
The type of file being printed: "job-sheet" for a banner page and "document"
for a regular print file.
.TP 5
+CUPS_MAX_MESSAGE
+.br
+The maximum size of a message sent to stderr, including any leading prefix and
+the trailing newline.
+.TP 5
CUPS_SERVERROOT
.br
The root directory of the server.
.\" 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/".
.\"
-.TH ipptoolfile 5 "CUPS" "14 March 2012" "Apple Inc."
+.TH ipptoolfile 5 "CUPS" "11 May 2012" "Apple Inc."
.SH NAME
ipptoolfile \- ipptool file format
OF-TYPE tag[,tag,...]
Requires the EXPECT attribute to use the specified value tag(s).
.TP 5
+REPEAT-LIMIT number
+.br
+Specifies the maximum number of times to repeat. The default value is 1000.
+.TP 5
REPEAT-MATCH
.TP 5
REPEAT-NO-MATCH
IF-NOT-DEFINED variable-name
Makes the STATUS apply only if the specified variable is not defined.
.TP 5
+REPEAT-LIMIT number
+.br
+Specifies the maximum number of times to repeat. The default value is 1000.
+.TP 5
REPEAT-MATCH
.TP 5
REPEAT-NO-MATCH
f 0444 root sys $DATADIR/ipptool test/document-*.p*
f 0444 root sys $DATADIR/ipptool test/ipp-*.test
f 0444 root sys $DATADIR/ipptool test/onepage-*.p*
+f 0444 root sys $DATADIR/ipptool test/testfile.*
f 0444 root sys $DATADIR/ipptool/color.jpg test/color.jpg
f 0444 root sys $DATADIR/ipptool/gray.jpg test/gray.jpg
cert.o \
classes.o \
client.o \
+ colorman.o \
conf.o \
dirsvc.o \
env.o \
* Check if the hostname is something.local (Bonjour); if so, allow it.
*/
- if ((end = strrchr(host, '.')) != NULL &&
- (!_cups_strcasecmp(end, ".local") || !_cups_strncasecmp(end, ".local:", 7) ||
- !_cups_strcasecmp(end, ".local.") || !_cups_strncasecmp(end, ".local.:", 8)))
+ if ((end = strrchr(host, '.')) != NULL && end > host &&
+ (!end[1] || end[1] == ':'))
+ {
+ /*
+ * "." on end, work back to second-to-last "."...
+ */
+ for (end --; end > host && *end != '.'; end --);
+ }
+
+ if (end && (!_cups_strcasecmp(end, ".local") ||
+ !_cups_strncasecmp(end, ".local:", 7) ||
+ !_cups_strcasecmp(end, ".local.") ||
+ !_cups_strncasecmp(end, ".local.:", 8)))
return (1);
#endif /* HAVE_DNSSD */
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Color management routines for the CUPS scheduler.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * 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/".
+ *
+ * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <cups/ppd-private.h>
+
+#ifdef __APPLE__
+# include <ApplicationServices/ApplicationServices.h>
+extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
+# include <CoreFoundation/CoreFoundation.h>
+#elif defined(HAVE_DBUS)
+# include <dbus/dbus.h>
+
+/*
+ * Defines used by colord. See the reference docs for further details:
+ *
+ * http://colord.hughsie.com/api/ref-dbus.html
+ */
+
+# define COLORD_SCOPE_NORMAL "normal"
+ /* System scope */
+# define COLORD_SCOPE_TEMP "temp" /* Process scope */
+# define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
+
+# define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
+# define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
+
+# define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
+# define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
+# define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
+# define COLORD_SPACE_UNKNOWN "unknown"
+ /* Unknown colorspace */
+
+# define COLORD_MODE_PHYSICAL "physical"
+ /* Actual device */
+# define COLORD_MODE_VIRTUAL "virtual"
+ /* Virtual device with no hardware */
+
+# define COLORD_KIND_PRINTER "printer"
+ /* printing output device */
+
+# define COLORD_DBUS_MSG(p,m) dbus_message_new_method_call(\
+ "org.freedesktop.ColorManager", (p),\
+ "org.freedesktop.ColorManager", (m))
+ /* Macro to make new colord messages */
+# define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
+ /* Path for color management system */
+# define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
+#endif /* __APPLE__ */
+
+
+/*
+ * Local globals...
+ */
+
+#if !defined(__APPLE__) && defined(HAVE_DBUS)
+static DBusConnection *colord_con = NULL;
+ /* DBUS connection for colord */
+#endif /* !__APPLE__ && HAVE_DBUS */
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef __APPLE__
+static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
+ CFMutableDictionaryRef profile,
+ unsigned id, const char *name,
+ const char *text, const char *iccfile);
+static void apple_register_profiles(cupsd_printer_t *p);
+static void apple_unregister_profiles(cupsd_printer_t *p);
+
+#elif defined(HAVE_DBUS)
+static void colord_create_device(cupsd_printer_t *p, ppd_file_t *ppd,
+ cups_array_t *profiles,
+ const char *colorspace, char **format,
+ const char *relation, const char *scope);
+static void colord_create_profile(cups_array_t *profiles,
+ const char *printer_name,
+ const char *qualifier,
+ const char *colorspace,
+ char **format, const char *iccfile,
+ const char *scope);
+static void colord_delete_device(const char *device_id);
+static void colord_device_add_profile(const char *device_path,
+ const char *profile_path,
+ const char *relation);
+static void colord_dict_add_strings(DBusMessageIter *dict,
+ const char *key, const char *value);
+static char *colord_find_device(const char *device_id);
+static void colord_get_qualifier_format(ppd_file_t *ppd, char *format[3]);
+static void colord_register_printer(cupsd_printer_t *p);
+static void colord_unregister_printer(cupsd_printer_t *p);
+#endif /* __APPLE__ */
+
+
+/*
+ * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
+ */
+
+void
+cupsdRegisterColor(cupsd_printer_t *p) /* I - Printer */
+{
+#ifdef __APPLE__
+ apple_unregister_profiles(p);
+ apple_register_profiles(p);
+
+#elif defined(HAVE_DBUS)
+ colord_unregister_printer(p);
+ colord_register_printer(p);
+#endif /* __APPLE__ */
+}
+
+
+/*
+ * 'cupsdStartColor()' - Initialize color management.
+ */
+
+void
+cupsdStartColor(void)
+{
+#if !defined(__APPLE__) && defined(HAVE_DBUS)
+ cupsd_printer_t *p; /* Current printer */
+
+
+ colord_con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+ p;
+ p = (cupsd_printer_t *)cupsArrayNext(Printers))
+ cupsdRegisterColor(p);
+#endif /* !__APPLE__ && HAVE_DBUS */
+}
+
+
+/*
+ * 'cupsdStopColor()' - Shutdown color management.
+ */
+
+void
+cupsdStopColor(void)
+{
+#if !defined(__APPLE__) && defined(HAVE_DBUS)
+ dbus_connection_unref(colord_con);
+ colord_con = NULL;
+#endif /* !__APPLE__ && HAVE_DBUS */
+}
+
+
+/*
+ * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
+ */
+
+void
+cupsdUnregisterColor(cupsd_printer_t *p)/* I - Printer */
+{
+#ifdef __APPLE__
+ apple_unregister_profiles(p);
+
+#elif defined(HAVE_DBUS)
+ colord_unregister_printer(p);
+#endif /* __APPLE__ */
+}
+
+
+#ifdef __APPLE__
+/*
+ * 'apple_init_profile()' - Initialize a color profile.
+ */
+
+static void
+apple_init_profile(
+ ppd_file_t *ppd, /* I - PPD file */
+ cups_array_t *languages, /* I - Languages in the PPD file */
+ CFMutableDictionaryRef profile, /* I - Profile dictionary */
+ unsigned id, /* I - Profile ID */
+ const char *name, /* I - Profile name */
+ const char *text, /* I - Profile UI text */
+ const char *iccfile) /* I - ICC filename */
+{
+ CFURLRef url; /* URL for profile filename */
+ CFMutableDictionaryRef dict; /* Dictionary for name */
+ char *language; /* Current language */
+ ppd_attr_t *attr; /* Profile attribute */
+ CFStringRef cflang, /* Language string */
+ cftext; /* Localized text */
+
+
+ (void)id;
+
+ /*
+ * Build the profile name dictionary...
+ */
+
+ dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (!dict)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
+ iccfile);
+ return;
+ }
+
+ cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
+ kCFStringEncodingUTF8);
+
+ if (cftext)
+ {
+ CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
+ CFRelease(cftext);
+ }
+
+ if (languages)
+ {
+ /*
+ * Find localized names for the color profiles...
+ */
+
+ cupsArraySave(ppd->sorted_attrs);
+
+ for (language = (char *)cupsArrayFirst(languages);
+ language;
+ language = (char *)cupsArrayNext(languages))
+ {
+ if (iccfile)
+ {
+ if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
+ language)) == NULL)
+ attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
+ }
+ else
+ attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
+
+ if (attr && attr->text[0])
+ {
+ cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
+ kCFStringEncodingUTF8);
+ cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
+ kCFStringEncodingUTF8);
+
+ if (cflang && cftext)
+ CFDictionarySetValue(dict, cflang, cftext);
+
+ if (cflang)
+ CFRelease(cflang);
+
+ if (cftext)
+ CFRelease(cftext);
+ }
+ }
+
+ cupsArrayRestore(ppd->sorted_attrs);
+ }
+
+ /*
+ * Fill in the profile data...
+ */
+
+ if (iccfile)
+ {
+ url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (const UInt8 *)iccfile,
+ strlen(iccfile), false);
+
+ if (url)
+ {
+ CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
+ CFRelease(url);
+ }
+ }
+
+ CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
+ CFRelease(dict);
+}
+
+
+/*
+ * 'apple_register_profiles()' - Register color profiles for a printer.
+ */
+
+static void
+apple_register_profiles(
+ cupsd_printer_t *p) /* I - Printer */
+{
+ int i; /* Looping var */
+ char ppdfile[1024], /* PPD filename */
+ iccfile[1024], /* ICC filename */
+ selector[PPD_MAX_NAME];
+ /* Profile selection string */
+ ppd_file_t *ppd; /* PPD file */
+ ppd_attr_t *attr, /* Profile attributes */
+ *profileid_attr,/* cupsProfileID attribute */
+ *q1_attr, /* ColorModel (or other) qualifier */
+ *q2_attr, /* MediaType (or other) qualifier */
+ *q3_attr; /* Resolution (or other) qualifier */
+ char q_keyword[PPD_MAX_NAME];
+ /* Qualifier keyword */
+ const char *q1_choice, /* ColorModel (or other) choice */
+ *q2_choice, /* MediaType (or other) choice */
+ *q3_choice; /* Resolution (or other) choice */
+ ppd_option_t *cm_option; /* Color model option */
+ ppd_choice_t *cm_choice; /* Color model choice */
+ int num_profiles; /* Number of profiles */
+ OSStatus error = 0; /* Last error */
+ unsigned device_id, /* Printer device ID */
+ profile_id = 0, /* Profile ID */
+ default_profile_id = 0;
+ /* Default profile ID */
+ CFMutableDictionaryRef device_name; /* Printer device name dictionary */
+ CFStringRef printer_name; /* Printer name string */
+ cups_array_t *languages; /* Languages array */
+ CFMutableDictionaryRef profiles, /* Dictionary of profiles */
+ profile; /* Current profile info dictionary */
+ CFStringRef dict_key; /* Key in factory profile dictionary */
+
+
+ /*
+ * Make sure ColorSync is available...
+ */
+
+ if (ColorSyncRegisterDevice == NULL)
+ return;
+
+ /*
+ * Try opening the PPD file for this printer...
+ */
+
+ snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
+ if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
+ return;
+
+ /*
+ * See if we have any profiles...
+ */
+
+ for (num_profiles = 0, attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+ if (attr->spec[0] && attr->value && attr->value[0])
+ {
+ if (attr->value[0] != '/')
+ snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
+ attr->value);
+ else
+ strlcpy(iccfile, attr->value, sizeof(iccfile));
+
+ if (access(iccfile, 0))
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "%s: ICC Profile \"%s\" does not exist.", p->name,
+ iccfile);
+ cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
+ continue;
+ }
+
+ num_profiles ++;
+ }
+
+ /*
+ * Create a dictionary for the factory profiles...
+ */
+
+ profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (!profiles)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for factory profiles.");
+ ppdClose(ppd);
+ return;
+ }
+
+ /*
+ * If we have profiles, add them...
+ */
+
+ if (num_profiles > 0)
+ {
+ /*
+ * For CUPS PPDs, figure out the default profile selector values...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
+ q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
+ }
+ else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
+ q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
+
+ if (q1_attr && q1_attr->value && q1_attr->value[0])
+ q1_choice = q1_attr->value;
+ else
+ q1_choice = "";
+
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
+ q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
+ }
+ else
+ q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
+
+ if (q2_attr && q2_attr->value && q2_attr->value[0])
+ q2_choice = q2_attr->value;
+ else
+ q2_choice = NULL;
+
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
+ q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
+ }
+ else
+ q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
+
+ if (q3_attr && q3_attr->value && q3_attr->value[0])
+ q3_choice = q3_attr->value;
+ else
+ q3_choice = NULL;
+
+ /*
+ * Loop through the profiles listed in the PPD...
+ */
+
+ languages = _ppdGetLanguages(ppd);
+
+ for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+ if (attr->spec[0] && attr->value && attr->value[0])
+ {
+ /*
+ * Add this profile...
+ */
+
+ if (attr->value[0] != '/')
+ snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
+ attr->value);
+ else
+ strlcpy(iccfile, attr->value, sizeof(iccfile));
+
+ if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
+ cupsdLogFCMessage, p))
+ continue;
+
+ cupsArraySave(ppd->sorted_attrs);
+
+ if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
+ attr->spec)) != NULL &&
+ profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
+ profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
+ else
+ profile_id = _ppdHashName(attr->spec);
+
+ cupsArrayRestore(ppd->sorted_attrs);
+
+ profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (!profile)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for color profile.");
+ CFRelease(profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
+ attr->text[0] ? attr->text : attr->spec, iccfile);
+
+ dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
+ CFSTR("%u"), profile_id);
+ if (dict_key)
+ {
+ CFDictionarySetValue(profiles, dict_key, profile);
+ CFRelease(dict_key);
+ }
+
+ CFRelease(profile);
+
+ /*
+ * See if this is the default profile...
+ */
+
+ if (!default_profile_id && q1_choice && q2_choice && q3_choice)
+ {
+ snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
+ q3_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+
+ if (!default_profile_id && q1_choice && q2_choice)
+ {
+ snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+
+ if (!default_profile_id && q1_choice && q3_choice)
+ {
+ snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+
+ if (!default_profile_id && q1_choice)
+ {
+ snprintf(selector, sizeof(selector), "%s..", q1_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+
+ if (!default_profile_id && q2_choice && q3_choice)
+ {
+ snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+
+ if (!default_profile_id && q2_choice)
+ {
+ snprintf(selector, sizeof(selector), ".%s.", q2_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+
+ if (!default_profile_id && q3_choice)
+ {
+ snprintf(selector, sizeof(selector), "..%s", q3_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+ }
+
+ _ppdFreeLanguages(languages);
+ }
+ else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
+ {
+ /*
+ * Extract profiles from ColorModel option...
+ */
+
+ const char *profile_name; /* Name of generic profile */
+
+
+ num_profiles = cm_option->num_choices;
+
+ for (i = cm_option->num_choices, cm_choice = cm_option->choices;
+ i > 0;
+ i --, cm_choice ++)
+ {
+ if (!strcmp(cm_choice->choice, "Gray") ||
+ !strcmp(cm_choice->choice, "Black"))
+ profile_name = "Gray";
+ else if (!strcmp(cm_choice->choice, "RGB") ||
+ !strcmp(cm_choice->choice, "CMY"))
+ profile_name = "RGB";
+ else if (!strcmp(cm_choice->choice, "CMYK") ||
+ !strcmp(cm_choice->choice, "KCMY"))
+ profile_name = "CMYK";
+ else
+ profile_name = "DeviceN";
+
+ snprintf(selector, sizeof(selector), "%s..", profile_name);
+ profile_id = _ppdHashName(selector);
+
+ profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (!profile)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for color profile.");
+ CFRelease(profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
+ cm_choice->text, NULL);
+
+ dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
+ CFSTR("%u"), profile_id);
+ if (dict_key)
+ {
+ CFDictionarySetValue(profiles, dict_key, profile);
+ CFRelease(dict_key);
+ }
+
+ CFRelease(profile);
+
+ if (cm_choice->marked)
+ default_profile_id = profile_id;
+ }
+ }
+ else
+ {
+ /*
+ * Use the default colorspace...
+ */
+
+ attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
+
+ num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
+
+ /*
+ * Add the grayscale profile first. We always have a grayscale profile.
+ */
+
+ profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (!profile)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for color profile.");
+ CFRelease(profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ profile_id = _ppdHashName("Gray..");
+ apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
+
+ dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
+ profile_id);
+ if (dict_key)
+ {
+ CFDictionarySetValue(profiles, dict_key, profile);
+ CFRelease(dict_key);
+ }
+
+ CFRelease(profile);
+
+ /*
+ * Then add the RGB/CMYK/DeviceN color profile...
+ */
+
+ profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (!profile)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for color profile.");
+ CFRelease(profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ switch (ppd->colorspace)
+ {
+ default :
+ case PPD_CS_RGB :
+ case PPD_CS_CMY :
+ profile_id = _ppdHashName("RGB..");
+ apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
+ NULL);
+ break;
+
+ case PPD_CS_RGBK :
+ case PPD_CS_CMYK :
+ profile_id = _ppdHashName("CMYK..");
+ apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
+ NULL);
+ break;
+
+ case PPD_CS_GRAY :
+ if (attr)
+ break;
+
+ case PPD_CS_N :
+ profile_id = _ppdHashName("DeviceN..");
+ apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
+ "DeviceN", NULL);
+ break;
+ }
+
+ if (CFDictionaryGetCount(profile) > 0)
+ {
+ dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
+ CFSTR("%u"), profile_id);
+ if (dict_key)
+ {
+ CFDictionarySetValue(profiles, dict_key, profile);
+ CFRelease(dict_key);
+ }
+ }
+
+ CFRelease(profile);
+ }
+
+ if (num_profiles > 0)
+ {
+ /*
+ * Make sure we have a default profile ID...
+ */
+
+ if (!default_profile_id)
+ default_profile_id = profile_id; /* Last profile */
+
+ dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
+ default_profile_id);
+ if (dict_key)
+ {
+ CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
+ dict_key);
+ CFRelease(dict_key);
+ }
+
+ /*
+ * Get the device ID hash and pathelogical name dictionary.
+ */
+
+ cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
+ p->name);
+
+ device_id = _ppdHashName(p->name);
+ device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
+ p->name, kCFStringEncodingUTF8);
+
+ if (device_name && printer_name)
+ {
+ /*
+ * Register the device with ColorSync...
+ */
+
+ CFTypeRef deviceDictKeys[] =
+ { /* Device keys */
+ kColorSyncDeviceDescriptions,
+ kColorSyncFactoryProfiles,
+ kColorSyncDeviceUserScope,
+ kColorSyncDeviceHostScope
+ };
+ CFTypeRef deviceDictVals[] =
+ { /* Device values */
+ device_name,
+ profiles,
+ kCFPreferencesAnyUser,
+ kCFPreferencesCurrentHost
+ };
+ CFDictionaryRef deviceDict; /* Device dictionary */
+ CFUUIDRef deviceUUID; /* Device UUID */
+
+ CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
+
+ deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **)deviceDictKeys,
+ (const void **)deviceDictVals,
+ sizeof(deviceDictKeys) /
+ sizeof(deviceDictKeys[0]),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
+
+ if (!deviceDict || !deviceUUID ||
+ !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
+ deviceDict))
+ error = 1001;
+
+ if (deviceUUID)
+ CFRelease(deviceUUID);
+
+ if (deviceDict)
+ CFRelease(deviceDict);
+ }
+ else
+ error = 1000;
+
+ /*
+ * Clean up...
+ */
+
+ if (error != noErr)
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to register ICC color profiles for \"%s\": %d",
+ p->name, (int)error);
+
+ if (printer_name)
+ CFRelease(printer_name);
+
+ if (device_name)
+ CFRelease(device_name);
+ }
+
+ /*
+ * Free any memory we used...
+ */
+
+ CFRelease(profiles);
+
+ ppdClose(ppd);
+}
+
+
+/*
+ * 'apple_unregister_profiles()' - Remove color profiles for the specified
+ * printer.
+ */
+
+static void
+apple_unregister_profiles(
+ cupsd_printer_t *p) /* I - Printer */
+{
+ /*
+ * Make sure ColorSync is available...
+ */
+
+ if (ColorSyncUnregisterDevice != NULL)
+ {
+ CFUUIDRef deviceUUID; /* Device UUID */
+
+ deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
+ if (deviceUUID)
+ {
+ ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
+ CFRelease(deviceUUID);
+ }
+ }
+}
+
+
+#elif defined(HAVE_DBUS)
+/*
+ * 'colord_create_device()' - Create a device and register profiles.
+ */
+
+static void
+colord_create_device(
+ cupsd_printer_t *p, /* I - Printer */
+ ppd_file_t *ppd, /* I - PPD file */
+ cups_array_t *profiles, /* I - Profiles array */
+ const char *colorspace, /* I - Device colorspace, e.g. 'rgb' */
+ char **format, /* I - Device qualifier format */
+ const char *relation, /* I - Profile relation, either 'soft'
+ or 'hard' */
+ const char *scope) /* I - The scope of the device, e.g.
+ 'normal', 'temp' or 'disk' */
+{
+ DBusMessage *message = NULL; /* D-Bus request */
+ DBusMessage *reply = NULL; /* D-Bus reply */
+ DBusMessageIter args; /* D-Bus method arguments */
+ DBusMessageIter dict; /* D-Bus method arguments */
+ DBusError error; /* D-Bus error */
+ const char *device_path; /* Device object path */
+ const char *profile_path; /* Profile path */
+ char *default_profile_path = NULL;
+ /* Default profile path */
+ char device_id[1024]; /* Device ID as understood by colord */
+ char format_str[1024]; /* Qualifier format as a string */
+
+
+ /*
+ * Create the device...
+ */
+
+ snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
+ device_path = device_id;
+
+ message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "CreateDevice");
+
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_path);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
+
+ snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
+ format[2]);
+
+ dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
+ colord_dict_add_strings(&dict, "Colorspace", colorspace);
+ colord_dict_add_strings(&dict, "Mode", COLORD_MODE_PHYSICAL);
+ if (ppd->manufacturer)
+ colord_dict_add_strings(&dict, "Vendor", ppd->manufacturer);
+ if (ppd->modelname)
+ colord_dict_add_strings(&dict, "Model", ppd->modelname);
+ if (p->sanitized_device_uri)
+ colord_dict_add_strings(&dict, "Serial", p->sanitized_device_uri);
+ colord_dict_add_strings(&dict, "Format", format_str);
+ colord_dict_add_strings(&dict, "Kind", COLORD_KIND_PRINTER);
+ dbus_message_iter_close_container(&args, &dict);
+
+ /*
+ * Send the CreateDevice request synchronously...
+ */
+
+ dbus_error_init(&error);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateDevice(%s,%s)", device_id,
+ scope);
+ reply = dbus_connection_send_with_reply_and_block(colord_con, message,
+ COLORD_DBUS_TIMEOUT,
+ &error);
+ if (!reply)
+ {
+ cupsdLogMessage(CUPSD_LOG_WARN, "CreateDevice failed: %s:%s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+ /*
+ * Get reply data...
+ */
+
+ dbus_message_iter_init(reply, &args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ {
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "CreateDevice failed: Incorrect reply type.");
+ goto out;
+ }
+
+ dbus_message_iter_get_basic(&args, &device_path);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Created device \"%s\".", device_path);
+
+ /*
+ * Add profiles...
+ */
+
+ for (profile_path = cupsArrayFirst(profiles);
+ profile_path;
+ profile_path = cupsArrayNext(profiles))
+ {
+ colord_device_add_profile(device_path, profile_path, relation);
+ }
+
+out:
+
+ if (default_profile_path)
+ free(default_profile_path);
+
+ if (message)
+ dbus_message_unref(message);
+
+ if (reply)
+ dbus_message_unref(reply);
+}
+
+
+/*
+ * 'colord_create_profile()' - Create a color profile for a printer.
+ */
+
+static void
+colord_create_profile(
+ cups_array_t *profiles, /* I - Profiles array */
+ const char *printer_name, /* I - Printer name */
+ const char *qualifier, /* I - Profile qualifier */
+ const char *colorspace, /* I - Profile colorspace */
+ char **format, /* I - Profile qualifier format */
+ const char *iccfile, /* I - ICC filename */
+ const char *scope) /* I - The scope of the profile, e.g.
+ 'normal', 'temp' or 'disk' */
+{
+ DBusMessage *message = NULL; /* D-Bus request */
+ DBusMessage *reply = NULL; /* D-Bus reply */
+ DBusMessageIter args; /* D-Bus method arguments */
+ DBusMessageIter dict; /* D-Bus method arguments */
+ DBusError error; /* D-Bus error */
+ char *idstr; /* Profile ID string */
+ size_t idstrlen; /* Profile ID allocated length */
+ const char *profile_path; /* Device object path */
+ char format_str[1024]; /* Qualifier format as a string */
+
+
+ /*
+ * Create the profile...
+ */
+
+ message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "CreateProfile");
+
+ idstrlen = strlen(printer_name) + 1 + strlen(qualifier) + 1;
+ if ((idstr = malloc(idstrlen)) == NULL)
+ goto out;
+ snprintf(idstr, idstrlen, "%s-%s", printer_name, qualifier);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Using profile ID \"%s\".", idstr);
+
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &idstr);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
+
+ snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
+ format[2]);
+
+ dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
+ colord_dict_add_strings(&dict, "Qualifier", qualifier);
+ colord_dict_add_strings(&dict, "Format", format_str);
+ colord_dict_add_strings(&dict, "Colorspace", colorspace);
+ if (iccfile)
+ colord_dict_add_strings(&dict, "Filename", iccfile);
+ dbus_message_iter_close_container(&args, &dict);
+
+ /*
+ * Send the CreateProfile request synchronously...
+ */
+
+ dbus_error_init(&error);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateProfile(%s,%s)", idstr,
+ scope);
+ reply = dbus_connection_send_with_reply_and_block(colord_con, message,
+ COLORD_DBUS_TIMEOUT,
+ &error);
+ if (!reply)
+ {
+ cupsdLogMessage(CUPSD_LOG_WARN, "CreateProfile failed: %s:%s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+ /*
+ * Get reply data...
+ */
+
+ dbus_message_iter_init(reply, &args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ {
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "CreateProfile failed: Incorrect reply type.");
+ goto out;
+ }
+
+ dbus_message_iter_get_basic(&args, &profile_path);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Created profile \"%s\".", profile_path);
+ cupsArrayAdd(profiles, strdup(profile_path));
+
+out:
+
+ if (message)
+ dbus_message_unref(message);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (idstr)
+ free(idstr);
+}
+
+
+/*
+ * 'colord_delete_device()' - Delete a device
+ */
+
+static void
+colord_delete_device(
+ const char *device_id) /* I - Device ID string */
+{
+ DBusMessage *message = NULL; /* D-Bus request */
+ DBusMessage *reply = NULL; /* D-Bus reply */
+ DBusMessageIter args; /* D-Bus method arguments */
+ DBusError error; /* D-Bus error */
+ char *device_path; /* Device object path */
+
+
+ /*
+ * Find the device...
+ */
+
+ if ((device_path = colord_find_device(device_id)) == NULL)
+ goto out;
+
+ /*
+ * Delete the device...
+ */
+
+ message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "DeleteDevice");
+
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
+
+ /*
+ * Send the DeleteDevice request synchronously...
+ */
+
+ dbus_error_init(&error);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling DeleteDevice(%s)", device_id);
+ reply = dbus_connection_send_with_reply_and_block(colord_con, message,
+ COLORD_DBUS_TIMEOUT,
+ &error);
+ if (!reply)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "DeleteDevice failed: %s:%s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+out:
+
+ if (device_path)
+ free(device_path);
+
+ if (message)
+ dbus_message_unref(message);
+
+ if (reply)
+ dbus_message_unref(reply);
+}
+
+
+/*
+ * 'colord_device_add_profile()' - Assign a profile to a device.
+ */
+
+static void
+colord_device_add_profile(
+ const char *device_path, /* I - Device object path */
+ const char *profile_path, /* I - Profile object path */
+ const char *relation) /* I - Device relation, either
+ 'soft' or 'hard' */
+{
+ DBusMessage *message = NULL; /* D-Bus request */
+ DBusMessage *reply = NULL; /* D-Bus reply */
+ DBusMessageIter args; /* D-Bus method arguments */
+ DBusError error; /* D-Bus error */
+
+
+ message = COLORD_DBUS_MSG(device_path, "AddProfile");
+
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &relation);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &profile_path);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling %s:AddProfile(%s) [%s]",
+ device_path, profile_path, relation);
+
+ /*
+ * Send the AddProfile request synchronously...
+ */
+
+ dbus_error_init(&error);
+ reply = dbus_connection_send_with_reply_and_block(colord_con, message,
+ COLORD_DBUS_TIMEOUT,
+ &error);
+ if (!reply)
+ {
+ cupsdLogMessage(CUPSD_LOG_WARN, "AddProfile failed: %s:%s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+out:
+
+ if (message)
+ dbus_message_unref(message);
+
+ if (reply)
+ dbus_message_unref(reply);
+}
+
+
+/*
+ * 'colord_dict_add_strings()' - Add two strings to a dictionary.
+ */
+
+static void
+colord_dict_add_strings(
+ DBusMessageIter *dict, /* I - Dictionary */
+ const char *key, /* I - Key string */
+ const char *value) /* I - Value string */
+{
+ DBusMessageIter entry; /* Entry to add */
+
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+
+/*
+ * 'colord_find_device()' - Finds a device
+ */
+
+static char * /* O - Device path or NULL */
+colord_find_device(
+ const char *device_id) /* I - Device ID string */
+{
+ DBusMessage *message = NULL; /* D-Bus request */
+ DBusMessage *reply = NULL; /* D-Bus reply */
+ DBusMessageIter args; /* D-Bus method arguments */
+ DBusError error; /* D-Bus error */
+ const char *device_path_tmp; /* Device object path */
+ char *device_path = NULL; /* Device object path */
+
+
+ message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "FindDeviceById");
+
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
+
+ /*
+ * Send the FindDeviceById request synchronously...
+ */
+
+ dbus_error_init(&error);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling FindDeviceById(%s)", device_id);
+ reply = dbus_connection_send_with_reply_and_block(colord_con, message,
+ COLORD_DBUS_TIMEOUT,
+ &error);
+ if (!reply)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "FindDeviceById failed: %s:%s",
+ error.name, error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+ /*
+ * Get reply data...
+ */
+
+ dbus_message_iter_init(reply, &args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ {
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "FindDeviceById failed: Incorrect reply type.");
+ goto out;
+ }
+
+ dbus_message_iter_get_basic(&args, &device_path_tmp);
+ if (device_path_tmp)
+ device_path = strdup(device_path_tmp);
+
+out:
+
+ if (message)
+ dbus_message_unref(message);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return (device_path);
+}
+
+
+/*
+ * 'colord_get_qualifier_format()' - Get the qualifier format.
+ *
+ * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
+ */
+
+static void
+colord_get_qualifier_format(
+ ppd_file_t *ppd, /* I - PPD file data */
+ char *format[3]) /* I - Format tuple */
+{
+ const char *tmp; /* Temporary string */
+ ppd_attr_t *attr; /* Profile attributes */
+
+
+ /*
+ * Get 1st section...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL)
+ tmp = attr->value;
+ else if (ppdFindAttr(ppd, "DefaultColorModel", NULL))
+ tmp = "ColorModel";
+ else if (ppdFindAttr(ppd, "DefaultColorSpace", NULL))
+ tmp = "ColorSpace";
+ else
+ tmp = "";
+
+ format[0] = strdup(tmp);
+
+ /*
+ * Get 2nd section...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL)
+ tmp = attr->value;
+ else
+ tmp = "MediaType";
+
+ format[1] = strdup(tmp);
+
+ /*
+ * Get 3rd section...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL)
+ tmp = attr->value;
+ else
+ tmp = "Resolution";
+
+ format[2] = strdup(tmp);
+}
+
+
+/*
+ * 'colord_register_printer()' - Register profiles for a printer.
+ */
+
+static void
+colord_register_printer(
+ cupsd_printer_t *p) /* I - printer */
+{
+ char ppdfile[1024], /* PPD filename */
+ iccfile[1024]; /* ICC filename */
+ ppd_file_t *ppd; /* PPD file */
+ cups_array_t *profiles; /* Profile paths array */
+ ppd_attr_t *attr; /* Profile attributes */
+ const char *device_colorspace; /* Device colorspace */
+ char *format[3]; /* Qualifier format tuple */
+
+
+ /*
+ * Ensure we have a D-Bus connection...
+ */
+
+ if (!colord_con)
+ return;
+
+ /*
+ * Try opening the PPD file for this printer...
+ */
+
+ snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
+ if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
+ return;
+
+ /*
+ * Find out the qualifier format
+ */
+
+ colord_get_qualifier_format(ppd, format);
+
+ /*
+ * See if we have any embedded profiles...
+ */
+
+ profiles = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup,
+ (cups_afree_func_t)free);
+ for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+ if (attr->spec[0] && attr->value && attr->value[0])
+ {
+ if (attr->value[0] != '/')
+ snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
+ attr->value);
+ else
+ strlcpy(iccfile, attr->value, sizeof(iccfile));
+
+ if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
+ cupsdLogFCMessage, p))
+ continue;
+
+ colord_create_profile(profiles, p->name, attr->spec, COLORD_SPACE_UNKNOWN,
+ format, iccfile, COLORD_SCOPE_TEMP);
+ }
+
+ /*
+ * Add the grayscale profile first. We always have a grayscale profile.
+ */
+
+ colord_create_profile(profiles, p->name, "Gray..", COLORD_SPACE_GRAY,
+ format, NULL, COLORD_SCOPE_TEMP);
+
+ /*
+ * Then add the RGB/CMYK/DeviceN color profile...
+ */
+
+ device_colorspace = "unknown";
+ switch (ppd->colorspace)
+ {
+ case PPD_CS_RGB :
+ case PPD_CS_CMY :
+ device_colorspace = COLORD_SPACE_RGB;
+ colord_create_profile(profiles, p->name, "RGB..", COLORD_SPACE_RGB,
+ format, NULL, COLORD_SCOPE_TEMP);
+ break;
+
+ case PPD_CS_RGBK :
+ case PPD_CS_CMYK :
+ device_colorspace = COLORD_SPACE_CMYK;
+ colord_create_profile(profiles, p->name, "CMYK..", COLORD_SPACE_CMYK,
+ format, NULL, COLORD_SCOPE_TEMP);
+ break;
+
+ case PPD_CS_GRAY :
+ device_colorspace = COLORD_SPACE_GRAY;
+ break;
+
+ case PPD_CS_N :
+ colord_create_profile(profiles, p->name, "DeviceN..",
+ COLORD_SPACE_UNKNOWN, format, NULL,
+ COLORD_SCOPE_TEMP);
+ break;
+ }
+
+ /*
+ * Register the device with colord.
+ */
+
+ cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\".",
+ p->name);
+ colord_create_device(p, ppd, profiles, device_colorspace, format,
+ COLORD_RELATION_SOFT, COLORD_SCOPE_TEMP);
+
+ /*
+ * Free any memory we used...
+ */
+
+ cupsArrayDelete(profiles);
+
+ free(format[0]);
+ free(format[1]);
+ free(format[2]);
+
+ ppdClose(ppd);
+}
+
+
+/*
+ * 'colord_unregister_printer()' - Unregister profiles for a printer.
+ */
+
+static void
+colord_unregister_printer(
+ cupsd_printer_t *p) /* I - printer */
+{
+ char device_id[1024]; /* Device ID as understood by colord */
+
+
+ /*
+ * Ensure we have a D-Bus connection...
+ */
+
+ if (!colord_con)
+ return;
+
+ /*
+ * Just delete the device itself, and leave the profiles registered
+ */
+
+ snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
+ colord_delete_device(device_id);
+}
+#endif /* __APPLE__ */
+
+
+/*
+ * End of "$Id$".
+ */
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Color management definitions for the CUPS scheduler.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * 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/".
+ */
+
+/*
+ * Prototypes...
+ */
+
+extern void cupsdRegisterColor(cupsd_printer_t *p);
+extern void cupsdStartColor(void);
+extern void cupsdStopColor(void);
+extern void cupsdUnregisterColor(cupsd_printer_t *p);
+
+
+/*
+ * End of "$Id$".
+ */
{ "ServerRoot", &ServerRoot, CUPSD_VARTYPE_PATHNAME },
{ "SMBConfigFile", &SMBConfigFile, CUPSD_VARTYPE_STRING },
{ "StateDir", &StateDir, CUPSD_VARTYPE_STRING },
+ { "StrictConformance", &StrictConformance, CUPSD_VARTYPE_BOOLEAN },
#ifdef HAVE_AUTHORIZATION_H
{ "SystemGroupAuthKey", &SystemGroupAuthKey, CUPSD_VARTYPE_STRING },
#endif /* HAVE_AUTHORIZATION_H */
MaxLogSize = 1024 * 1024;
MaxRequestSize = 0;
MultipleOperationTimeout = DEFAULT_TIMEOUT;
+ NumSystemGroups = 0;
ReloadTimeout = DEFAULT_KEEPALIVE;
RootCertDuration = 300;
+ StrictConformance = FALSE;
Timeout = DEFAULT_TIMEOUT;
- NumSystemGroups = 0;
WebInterface = CUPS_DEFAULT_WEBIF;
BrowseLocalProtocols = parse_protocols(CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS);
/* Amount of automatic debug history */
FatalErrors VALUE(CUPSD_FATAL_CONFIG),
/* Which errors are fatal? */
+ StrictConformance VALUE(FALSE),
+ /* Require strict IPP conformance? */
LogFilePerm VALUE(0644);
/* Permissions for log files */
VAR cupsd_loglevel_t LogLevel VALUE(CUPSD_LOG_WARN);
for (total = 0; total < curinfo.st_size; total += bytes)
{
- if ((bytes = (curinfo.st_size - total)) > sizeof(buffer))
+ if ((size_t)(bytes = (curinfo.st_size - total)) > sizeof(buffer))
bytes = sizeof(buffer);
if ((bytes = cupsFileRead(fp, buffer, bytes)) < 0)
cups_array_t *products, /* Product array */
*psversions, /* PSVersion array */
*cups_languages; /* cupsLanguages array */
- int new_ppd = !ppd; /* Is this a new PPD? */
+ int new_ppd; /* Is this a new PPD? */
struct /* LanguageVersion translation table */
{
const char *version, /* LanguageVersion string */
*
* Main header file for the CUPS scheduler.
*
- * Copyright 2007-2011 by Apple Inc.
+ * Copyright 2007-2012 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
#include "printers.h"
#include "classes.h"
#include "job.h"
+#include "colorman.h"
#include "conf.h"
#include "banners.h"
#include "dirsvc.h"
#if defined(HAVE_DNSSD) && defined(__APPLE__)
# include <nameser.h>
-# ifdef HAVE_COREFOUNDATION
-# include <CoreFoundation/CoreFoundation.h>
-# endif /* HAVE_COREFOUNDATION */
-# ifdef HAVE_SYSTEMCONFIGURATION
-# include <SystemConfiguration/SystemConfiguration.h>
-# endif /* HAVE_SYSTEMCONFIGURATION */
+# include <CoreFoundation/CoreFoundation.h>
+# include <SystemConfiguration/SystemConfiguration.h>
#endif /* HAVE_DNSSD && __APPLE__ */
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-# ifdef HAVE_COREFOUNDATION
+# ifdef __APPLE__
static void dnssdAddAlias(const void *key, const void *value,
void *context);
-# endif /* HAVE_COREFOUNDATION */
+# endif /* __APPLE__ */
static cupsd_txt_t dnssdBuildTxtRecord(cupsd_printer_t *p, int for_lpd);
static void dnssdDeregisterInstance(cupsd_srv_t *srv);
static void dnssdDeregisterPrinter(cupsd_printer_t *p,
* Announce the deletion...
*/
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
dnssdDeregisterPrinter(p, 1);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
}
(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
return;
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
dnssdRegisterPrinter(p);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
}
cupsdUpdateDNSSDName(void)
{
char webif[1024]; /* Web interface share name */
-# ifdef HAVE_SYSTEMCONFIGURATION
+# ifdef __APPLE__
SCDynamicStoreRef sc; /* Context for dynamic store */
CFDictionaryRef btmm; /* Back-to-My-Mac domains */
CFStringEncoding nameEncoding; /* Encoding of computer name */
CFStringRef nameRef; /* Host name CFString */
char nameBuffer[1024]; /* C-string buffer */
-# endif /* HAVE_SYSTEMCONFIGURATION */
+# endif /* __APPLE__ */
/*
* Get the computer name as a c-string...
*/
-# ifdef HAVE_SYSTEMCONFIGURATION
+# ifdef __APPLE__
sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL);
if (sc)
CFRelease(sc);
}
else
-# endif /* HAVE_SYSTEMCONFIGURATION */
+# endif /* __APPLE__ */
# ifdef HAVE_AVAHI
{
cupsdSetString(&DNSSDComputerName, avahi_client_get_host_name(DNSSDClient));
}
-# ifdef HAVE_COREFOUNDATION
+# ifdef __APPLE__
/*
* 'dnssdAddAlias()' - Add a DNS-SD alias name.
*/
cupsdLogMessage(CUPSD_LOG_ERROR,
"Bad Back to My Mac domain in dynamic store!");
}
-# endif /* HAVE_COREFOUNDATION */
+# endif /* __APPLE__ */
/*
#if defined(__APPLE__)
/*
- * Add special voodoo magic for MacOS X - this allows MacOS X
+ * Add special voodoo magic for MacOS X - this allows MacOS X
* programs to access their bundle resources properly...
*
* This string is replaced in cupsdStartProcess()...
set_if_undefined("TZ", NULL);
set_if_undefined("USER", "root");
set_if_undefined("VG_ARGS", NULL);
+
+ cupsdSetEnvf("CUPS_MAX_MESSAGE", "%d", CUPSD_SB_BUFFER_SIZE - 1);
}
* based upon the printer state...
* add_queued_job_count() - Add the "queued-job-count" attribute for the
* specified printer or class.
- * apple_init_profile() - Initialize a color profile.
- * apple_register_profiles() - Register color profiles for a printer.
- * apple_unregister_profiles() - Remove color profiles for the specified
- * printer.
* apply_printer_defaults() - Apply printer default options to a job.
* authenticate_job() - Set job authentication info.
* cancel_all_jobs() - Cancel all or selected print jobs.
#include <cups/ppd-private.h>
#ifdef __APPLE__
-# include <ApplicationServices/ApplicationServices.h>
+/*# include <ApplicationServices/ApplicationServices.h>
extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
-# include <CoreFoundation/CoreFoundation.h>
+# include <CoreFoundation/CoreFoundation.h>*/
# ifdef HAVE_MEMBERSHIP_H
# include <membership.h>
# endif /* HAVE_MEMBERSHIP_H */
static void add_printer_state_reasons(cupsd_client_t *con,
cupsd_printer_t *p);
static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
-#ifdef __APPLE__
-static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
- CFMutableDictionaryRef profile,
- unsigned id, const char *name,
- const char *text, const char *iccfile);
-static void apple_register_profiles(cupsd_printer_t *p);
-static void apple_unregister_profiles(cupsd_printer_t *p);
-#endif /* __APPLE__ */
static void apply_printer_defaults(cupsd_printer_t *printer,
cupsd_job_t *job);
static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
cupsdSetPrinterReasons(printer, "none");
-#ifdef __APPLE__
/*
* (Re)register color profiles...
*/
- if (!RunUser)
- {
- apple_unregister_profiles(printer);
- apple_register_profiles(printer);
- }
-#endif /* __APPLE__ */
+ cupsdRegisterColor(printer);
}
/*
}
-#ifdef __APPLE__
-/*
- * 'apple_init_profile()' - Initialize a color profile.
- */
-
-static void
-apple_init_profile(
- ppd_file_t *ppd, /* I - PPD file */
- cups_array_t *languages, /* I - Languages in the PPD file */
- CFMutableDictionaryRef profile, /* I - Profile dictionary */
- unsigned id, /* I - Profile ID */
- const char *name, /* I - Profile name */
- const char *text, /* I - Profile UI text */
- const char *iccfile) /* I - ICC filename */
-{
- CFURLRef url; /* URL for profile filename */
- CFMutableDictionaryRef dict; /* Dictionary for name */
- char *language; /* Current language */
- ppd_attr_t *attr; /* Profile attribute */
- CFStringRef cflang, /* Language string */
- cftext; /* Localized text */
-
-
- (void)id;
-
- /*
- * Build the profile name dictionary...
- */
-
- dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- if (!dict)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
- iccfile);
- return;
- }
-
- cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
- kCFStringEncodingUTF8);
-
- if (cftext)
- {
- CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
- CFRelease(cftext);
- }
-
- if (languages)
- {
- /*
- * Find localized names for the color profiles...
- */
-
- cupsArraySave(ppd->sorted_attrs);
-
- for (language = (char *)cupsArrayFirst(languages);
- language;
- language = (char *)cupsArrayNext(languages))
- {
- if (iccfile)
- {
- if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
- language)) == NULL)
- attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
- }
- else
- attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
-
- if (attr && attr->text[0])
- {
- cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
- kCFStringEncodingUTF8);
- cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
- kCFStringEncodingUTF8);
-
- if (cflang && cftext)
- CFDictionarySetValue(dict, cflang, cftext);
-
- if (cflang)
- CFRelease(cflang);
-
- if (cftext)
- CFRelease(cftext);
- }
- }
-
- cupsArrayRestore(ppd->sorted_attrs);
- }
-
- /*
- * Fill in the profile data...
- */
-
- if (iccfile)
- {
- url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
- (const UInt8 *)iccfile,
- strlen(iccfile), false);
-
- if (url)
- {
- CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
- CFRelease(url);
- }
- }
-
- CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
- CFRelease(dict);
-}
-
-
-/*
- * 'apple_register_profiles()' - Register color profiles for a printer.
- */
-
-static void
-apple_register_profiles(
- cupsd_printer_t *p) /* I - Printer */
-{
- int i; /* Looping var */
- char ppdfile[1024], /* PPD filename */
- iccfile[1024], /* ICC filename */
- selector[PPD_MAX_NAME];
- /* Profile selection string */
- ppd_file_t *ppd; /* PPD file */
- ppd_attr_t *attr, /* Profile attributes */
- *profileid_attr,/* cupsProfileID attribute */
- *q1_attr, /* ColorModel (or other) qualifier */
- *q2_attr, /* MediaType (or other) qualifier */
- *q3_attr; /* Resolution (or other) qualifier */
- char q_keyword[PPD_MAX_NAME];
- /* Qualifier keyword */
- const char *q1_choice, /* ColorModel (or other) choice */
- *q2_choice, /* MediaType (or other) choice */
- *q3_choice; /* Resolution (or other) choice */
- const char *profile_key; /* Profile keyword */
- ppd_option_t *cm_option; /* Color model option */
- ppd_choice_t *cm_choice; /* Color model choice */
- int num_profiles; /* Number of profiles */
- OSStatus error = 0; /* Last error */
- unsigned device_id, /* Printer device ID */
- profile_id = 0, /* Profile ID */
- default_profile_id = 0;
- /* Default profile ID */
- CFMutableDictionaryRef device_name; /* Printer device name dictionary */
- CFStringRef printer_name; /* Printer name string */
- cups_array_t *languages; /* Languages array */
- CFMutableDictionaryRef profiles, /* Dictionary of profiles */
- profile; /* Current profile info dictionary */
- CFStringRef dict_key; /* Key in factory profile dictionary */
-
-
- /*
- * Make sure ColorSync is available...
- */
-
- if (ColorSyncRegisterDevice == NULL)
- return;
-
- /*
- * Try opening the PPD file for this printer...
- */
-
- snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
- if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
- return;
-
- /*
- * See if we have any profiles...
- */
-
- if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL)
- profile_key = "APTiogaProfile";
- else
- {
- attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
- profile_key = "cupsICCProfile";
- }
-
- for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL))
- if (attr->spec[0] && attr->value && attr->value[0])
- {
- if (attr->value[0] != '/')
- snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
- attr->value);
- else
- strlcpy(iccfile, attr->value, sizeof(iccfile));
-
- if (access(iccfile, 0))
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "%s: ICC Profile \"%s\" does not exist.", p->name,
- iccfile);
- cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
- continue;
- }
-
- num_profiles ++;
- }
-
- /*
- * Create a dictionary for the factory profiles...
- */
-
- profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- if (!profiles)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to allocate memory for factory profiles.");
- ppdClose(ppd);
- return;
- }
-
- /*
- * If we have profiles, add them...
- */
-
- if (num_profiles > 0)
- {
- if (profile_key[0] == 'A')
- {
- /*
- * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile
- * attribute...
- */
-
- if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL &&
- attr->value)
- default_profile_id = atoi(attr->value);
-
- q1_choice = q2_choice = q3_choice = NULL;
- }
- else
- {
- /*
- * For CUPS PPDs, figure out the default profile selector values...
- */
-
- if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
- attr->value && attr->value[0])
- {
- snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
- q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
- }
- else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
- q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
-
- if (q1_attr && q1_attr->value && q1_attr->value[0])
- q1_choice = q1_attr->value;
- else
- q1_choice = "";
-
- if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
- attr->value && attr->value[0])
- {
- snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
- q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
- }
- else
- q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
-
- if (q2_attr && q2_attr->value && q2_attr->value[0])
- q2_choice = q2_attr->value;
- else
- q2_choice = NULL;
-
- if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
- attr->value && attr->value[0])
- {
- snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
- q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
- }
- else
- q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
-
- if (q3_attr && q3_attr->value && q3_attr->value[0])
- q3_choice = q3_attr->value;
- else
- q3_choice = NULL;
- }
-
- /*
- * Loop through the profiles listed in the PPD...
- */
-
- languages = _ppdGetLanguages(ppd);
-
- for (attr = ppdFindAttr(ppd, profile_key, NULL);
- attr;
- attr = ppdFindNextAttr(ppd, profile_key, NULL))
- if (attr->spec[0] && attr->value && attr->value[0])
- {
- /*
- * Add this profile...
- */
-
- if (attr->value[0] != '/')
- snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
- attr->value);
- else
- strlcpy(iccfile, attr->value, sizeof(iccfile));
-
- if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
- cupsdLogFCMessage, p))
- continue;
-
- if (profile_key[0] == 'c')
- {
- cupsArraySave(ppd->sorted_attrs);
-
- if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
- attr->spec)) != NULL &&
- profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
- profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
- else
- profile_id = _ppdHashName(attr->spec);
-
- cupsArrayRestore(ppd->sorted_attrs);
- }
- else
- profile_id = atoi(attr->spec);
-
- profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- if (!profile)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to allocate memory for color profile.");
- CFRelease(profiles);
- ppdClose(ppd);
- return;
- }
-
- apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
- attr->text[0] ? attr->text : attr->spec, iccfile);
-
- dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%u"), profile_id);
- if (dict_key)
- {
- CFDictionarySetValue(profiles, dict_key, profile);
- CFRelease(dict_key);
- }
-
- CFRelease(profile);
-
- /*
- * See if this is the default profile...
- */
-
- if (!default_profile_id && q1_choice && q2_choice && q3_choice)
- {
- snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
- q3_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
-
- if (!default_profile_id && q1_choice && q2_choice)
- {
- snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
-
- if (!default_profile_id && q1_choice && q3_choice)
- {
- snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
-
- if (!default_profile_id && q1_choice)
- {
- snprintf(selector, sizeof(selector), "%s..", q1_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
-
- if (!default_profile_id && q2_choice && q3_choice)
- {
- snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
-
- if (!default_profile_id && q2_choice)
- {
- snprintf(selector, sizeof(selector), ".%s.", q2_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
-
- if (!default_profile_id && q3_choice)
- {
- snprintf(selector, sizeof(selector), "..%s", q3_choice);
- if (!strcmp(selector, attr->spec))
- default_profile_id = profile_id;
- }
- }
-
- _ppdFreeLanguages(languages);
- }
- else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
- {
- /*
- * Extract profiles from ColorModel option...
- */
-
- const char *profile_name; /* Name of generic profile */
-
-
- num_profiles = cm_option->num_choices;
-
- for (i = cm_option->num_choices, cm_choice = cm_option->choices;
- i > 0;
- i --, cm_choice ++)
- {
- if (!strcmp(cm_choice->choice, "Gray") ||
- !strcmp(cm_choice->choice, "Black"))
- profile_name = "Gray";
- else if (!strcmp(cm_choice->choice, "RGB") ||
- !strcmp(cm_choice->choice, "CMY"))
- profile_name = "RGB";
- else if (!strcmp(cm_choice->choice, "CMYK") ||
- !strcmp(cm_choice->choice, "KCMY"))
- profile_name = "CMYK";
- else
- profile_name = "DeviceN";
-
- snprintf(selector, sizeof(selector), "%s..", profile_name);
- profile_id = _ppdHashName(selector);
-
- profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- if (!profile)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to allocate memory for color profile.");
- CFRelease(profiles);
- ppdClose(ppd);
- return;
- }
-
- apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
- cm_choice->text, NULL);
-
- dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%u"), profile_id);
- if (dict_key)
- {
- CFDictionarySetValue(profiles, dict_key, profile);
- CFRelease(dict_key);
- }
-
- CFRelease(profile);
-
- if (cm_choice->marked)
- default_profile_id = profile_id;
- }
- }
- else
- {
- /*
- * Use the default colorspace...
- */
-
- attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
-
- num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
-
- /*
- * Add the grayscale profile first. We always have a grayscale profile.
- */
-
- profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
-
- if (!profile)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to allocate memory for color profile.");
- CFRelease(profiles);
- ppdClose(ppd);
- return;
- }
-
- profile_id = _ppdHashName("Gray..");
- apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
-
- dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
- profile_id);
- if (dict_key)
- {
- CFDictionarySetValue(profiles, dict_key, profile);
- CFRelease(dict_key);
- }
-
- CFRelease(profile);
-
- /*
- * Then add the RGB/CMYK/DeviceN color profile...
- */
-
- profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
-
- if (!profile)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to allocate memory for color profile.");
- CFRelease(profiles);
- ppdClose(ppd);
- return;
- }
-
- switch (ppd->colorspace)
- {
- default :
- case PPD_CS_RGB :
- case PPD_CS_CMY :
- profile_id = _ppdHashName("RGB..");
- apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
- NULL);
- break;
-
- case PPD_CS_RGBK :
- case PPD_CS_CMYK :
- profile_id = _ppdHashName("CMYK..");
- apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
- NULL);
- break;
-
- case PPD_CS_GRAY :
- if (attr)
- break;
-
- case PPD_CS_N :
- profile_id = _ppdHashName("DeviceN..");
- apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
- "DeviceN", NULL);
- break;
- }
-
- if (CFDictionaryGetCount(profile) > 0)
- {
- dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%u"), profile_id);
- if (dict_key)
- {
- CFDictionarySetValue(profiles, dict_key, profile);
- CFRelease(dict_key);
- }
- }
-
- CFRelease(profile);
- }
-
- if (num_profiles > 0)
- {
- /*
- * Make sure we have a default profile ID...
- */
-
- if (!default_profile_id)
- default_profile_id = profile_id; /* Last profile */
-
- dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
- default_profile_id);
- if (dict_key)
- {
- CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
- dict_key);
- CFRelease(dict_key);
- }
-
- /*
- * Get the device ID hash and pathelogical name dictionary.
- */
-
- cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
- p->name);
-
- device_id = _ppdHashName(p->name);
- device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
- p->name, kCFStringEncodingUTF8);
-
- if (device_name && printer_name)
- {
- /*
- * Register the device with ColorSync...
- */
-
- CFTypeRef deviceDictKeys[] =
- { /* Device keys */
- kColorSyncDeviceDescriptions,
- kColorSyncFactoryProfiles,
- kColorSyncDeviceUserScope,
- kColorSyncDeviceHostScope
- };
- CFTypeRef deviceDictVals[] =
- { /* Device values */
- device_name,
- profiles,
- kCFPreferencesAnyUser,
- kCFPreferencesCurrentHost
- };
- CFDictionaryRef deviceDict; /* Device dictionary */
- CFUUIDRef deviceUUID; /* Device UUID */
-
- CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
-
- deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
- (const void **)deviceDictKeys,
- (const void **)deviceDictVals,
- sizeof(deviceDictKeys) /
- sizeof(deviceDictKeys[0]),
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
-
- if (!deviceDict || !deviceUUID ||
- !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
- deviceDict))
- error = 1001;
-
- if (deviceUUID)
- CFRelease(deviceUUID);
-
- if (deviceDict)
- CFRelease(deviceDict);
- }
- else
- error = 1000;
-
- /*
- * Clean up...
- */
-
- if (error != noErr)
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to register ICC color profiles for \"%s\": %d",
- p->name, (int)error);
-
- if (printer_name)
- CFRelease(printer_name);
-
- if (device_name)
- CFRelease(device_name);
- }
-
- /*
- * Free any memory we used...
- */
-
- CFRelease(profiles);
-
- ppdClose(ppd);
-}
-
-
-/*
- * 'apple_unregister_profiles()' - Remove color profiles for the specified
- * printer.
- */
-
-static void
-apple_unregister_profiles(
- cupsd_printer_t *p) /* I - Printer */
-{
- /*
- * Make sure ColorSync is available...
- */
-
- if (ColorSyncUnregisterDevice != NULL)
- {
- /*
- * Because we may have registered the printer profiles using a prior device
- * ID-based UUID, remove both the old style UUID and current UUID for the
- * printer.
- */
-
- CFUUIDRef deviceUUID; /* Device UUID */
-
- deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
- if (deviceUUID)
- {
- ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
- CFRelease(deviceUUID);
- }
- }
-}
-#endif /* __APPLE__ */
-
-
/*
* 'apply_printer_defaults()' - Apply printer default options to a job.
*/
create_job(cupsd_client_t *con, /* I - Client connection */
ipp_attribute_t *uri) /* I - Printer URI */
{
+ int i; /* Looping var */
cupsd_printer_t *printer; /* Printer */
cupsd_job_t *job; /* New job */
+ static const char * const forbidden_attrs[] =
+ { /* List of forbidden attributes */
+ "compression",
+ "document-format",
+ "document-name",
+ "document-natural-language"
+ };
cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
return;
}
+ /*
+ * Check for invalid Create-Job attributes and log a warning or error depending
+ * on whether cupsd is running in "strict conformance" mode...
+ */
+
+ for (i = 0;
+ i < (int)(sizeof(forbidden_attrs) / sizeof(forbidden_attrs[0]));
+ i ++)
+ if (ippFindAttribute(con->request, forbidden_attrs[i], IPP_TAG_ZERO))
+ {
+ if (StrictConformance)
+ {
+ send_ipp_status(con, IPP_BAD_REQUEST,
+ _("The '%s' operation attribute cannot be supplied in a "
+ "Create-Job request."), forbidden_attrs[i]);
+ return;
+ }
+
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "Unexpected '%s' operation attribute in a Create-Job "
+ "request.", forbidden_attrs[i]);
+ }
+
/*
* Create the job object...
*/
snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
unlink(filename);
-#ifdef __APPLE__
/*
* Unregister color profiles...
*/
- apple_unregister_profiles(printer);
-#endif /* __APPLE__ */
+ cupsdUnregisterColor(printer);
if (dtype & CUPS_PRINTER_CLASS)
{
time_t curtime; /* Current time */
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
- cupsArrayCount(ActiveJobs), Sleeping, NeedReload);
-
curtime = time(NULL);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d, "
+ "curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping,
+ NeedReload, (long)curtime);
+
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
job;
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
{
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdCheckJobs: Job %d - dest=\"%s\", printer=%p, "
+ "state=%d, cancel_time=%ld, hold_until=%ld, kill_time=%ld, "
+ "pending_cost=%d, pending_timeout=%ld", job->id, job->dest,
+ job->printer, job->state_value, (long)job->cancel_time,
+ (long)job->hold_until, (long)job->kill_time,
+ job->pending_cost, (long)job->pending_timeout);
+
/*
* Kill jobs if they are unresponsive...
*/
cupsd_client_t *con; /* Current client connection */
-
for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
con;
con = (cupsd_client_t *)cupsArrayNext(Clients))
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
}
- if (printer->state == IPP_PRINTER_IDLE)
+ if (!printer->job && printer->state == IPP_PRINTER_IDLE)
{
/*
* Start the job...
cupsArrayRemove(PrintingJobs, job);
+ /*
+ * Apply any PPD updates...
+ */
+
+ if (job->num_keywords)
+ {
+ if (cupsdUpdatePrinterPPD(job->printer, job->num_keywords, job->keywords))
+ cupsdSetPrinterAttrs(job->printer);
+
+ cupsFreeOptions(job->num_keywords, job->keywords);
+
+ job->num_keywords = 0;
+ job->keywords = NULL;
+ }
+
/*
* Clear the printer <-> job association...
*/
* Set attribute(s)...
*/
- int num_keywords; /* Number of keywords */
- cups_option_t *keywords; /* Keywords */
-
-
cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message);
- num_keywords = cupsParseOptions(message, 0, &keywords);
-
- if (cupsdUpdatePrinterPPD(job->printer, num_keywords, keywords))
- cupsdSetPrinterAttrs(job->printer);
-
- cupsFreeOptions(num_keywords, keywords);
+ job->num_keywords = cupsParseOptions(message, job->num_keywords,
+ &job->keywords);
}
else
{
void *profile; /* Security profile */
cups_array_t *history; /* Debug log history */
int progress; /* Printing progress */
+ int num_keywords; /* Number of PPD keywords */
+ cups_option_t *keywords; /* PPD keywords */
};
typedef struct cupsd_joblog_s /**** Job log message ****/
}
}
+ /*
+ * media-size-supported
+ */
+
+ num_media = p->pc->num_sizes;
+ if (p->pc->custom_min_keyword)
+ num_media ++;
+
+ if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
+ "media-size-supported", num_media,
+ NULL)) != NULL)
+ {
+ val = attr->values;
+
+ for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
+ i > 0;
+ i --, pwgsize ++, val ++)
+ {
+ val->collection = ippNew();
+ ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "x-dimension", pwgsize->width);
+ ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "y-dimension", pwgsize->length);
+ }
+
+ if (p->pc->custom_min_keyword)
+ {
+ val->collection = ippNew();
+ ippAddRange(val->collection, IPP_TAG_PRINTER, "x-dimension",
+ p->pc->custom_min_width, p->pc->custom_max_width);
+ ippAddRange(val->collection, IPP_TAG_PRINTER, "y-dimension",
+ p->pc->custom_min_length, p->pc->custom_max_length);
+ }
+ }
+
/*
* media-source-supported
*/
else if (!strncmp(p->device_uri, "ipp://", 6) &&
(strstr(p->device_uri, "/printers/") != NULL ||
strstr(p->device_uri, "/classes/") != NULL ||
- (strstr(p->device_uri, "._ipp.") != NULL &&
+ ((strstr(p->device_uri, "._ipp.") != NULL ||
+ strstr(p->device_uri, "._ipps.") != NULL) &&
!strcmp(p->device_uri + strlen(p->device_uri) - 5,
"/cups"))))
{
message = "Printer does not support REQUIRED Validate-Job operation.";
else if (!strcmp(reason, "missing-get-printer-attributes"))
message = "Printer does not support REQUIRED Get-Printer-Attributes operation.";
+ else if (!strcmp(reason, "missing-send-document"))
+ message = "Printer supports Create-Job but not Send-Document operation.";
else if (!strcmp(reason, "missing-job-history"))
message = "Printer does not provide REQUIRED job history.";
else if (!strcmp(reason, "missing-job-id"))
const char *s);
extern void cupsdSetPrinterState(cupsd_printer_t *p, ipp_pstate_t s,
int update);
-#define cupsdStartPrinter(p,u) cupsdSetPrinterState((p), IPP_PRINTER_IDLE, (u))
+#define cupsdStartPrinter(p,u) cupsdSetPrinterState((p), \
+ IPP_PRINTER_IDLE, (u))
extern void cupsdStopPrinter(cupsd_printer_t *p, int update);
extern int cupsdUpdatePrinterPPD(cupsd_printer_t *p,
int num_keywords,
*
* Server start/stop routines for the CUPS scheduler.
*
- * Copyright 2007-2011 by Apple Inc.
+ * Copyright 2007-2012 by Apple Inc.
* Copyright 1997-2006 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
void
cupsdStartServer(void)
{
+ /*
+ * Start color management (as needed)...
+ */
+
+ cupsdStartColor();
+
/*
* Create the default security profile...
*/
return;
/*
- * Close all network clients and stop all jobs...
+ * Stop color management (as needed)...
+ */
+
+ cupsdStopColor();
+
+ /*
+ * Close all network clients...
*/
cupsdCloseAllClients();
p = (cupsd_printer_t *)cupsArrayNext(Printers))
cupsdDeregisterPrinter(p, 1);
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
/*
* Update the computer name and BTMM domain list...
*/
cupsdUpdateDNSSDName();
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
/*
* Now re-register them...
/* Server name */
CFMutableDictionaryRef query = NULL; /* Query qualifiers */
CFArrayRef list = NULL; /* Keychain list */
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
char localname[1024];/* Local hostname */
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
# elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
SecPolicyRef policy = NULL; /* Policy ref */
SecPolicySearchRef policy_search = NULL;
err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (err && DNSSDHostName)
{
/*
err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
}
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
if (err)
{
err = SecIdentitySearchCopyNext(search, &identity);
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (err && DNSSDHostName)
{
/*
err = SecIdentitySearchCopyNext(search, &identity);
}
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
if (err)
{
*envp[MAX_ENV + 1], /* Environment variables */
keychain[1024], /* Keychain argument */
infofile[1024], /* Type-in information for cert */
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
localname[1024], /* Local hostname */
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
*servername; /* Name of server in cert */
cups_file_t *fp; /* Seed/info file */
int infofd; /* Info file descriptor */
+# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
{
snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
servername = localname;
}
else
+# endif /* HAVE_DNSSD || HAVE_AVAHI */
servername = con->servername;
/*
int i, j; /* Looping vars */
int job_id; /* Job ID */
char *printer, /* Printer name */
- *instance, /* Instance name */
+ *instance, /* Instance name */
*val, /* Option value */
*title; /* Job title */
int priority; /* Job priority (1-100) */
cupsSetUser(argv[i]);
}
break;
-
+
case 'c' : /* Copy to spool dir (always enabled) */
break;
if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
{
+ _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
- job_id = 0;
+ return (1);
}
}
<TBODY>
{[job_id]
<TR VALIGN="TOP">
-<TD><A HREF="{job_printer_uri}">{job_printer_name}</A>-{job_id}{?phone? ({phone})} </TD>
+<TD><A HREF="{job_printer_uri}">{job_printer_name}</A>-{job_id}{?phone? ({phone}):} </TD>
<TD>{?job_name=?Unknown:{job_name}} </TD>
<TD>{?job_originating_user_name=?Withheld:{job_originating_user_name}} </TD>
<TD>{job_k_octets}k </TD>
#
# Test the lpadmin command.
#
-# Copyright 2007-2009 by Apple Inc.
+# Copyright 2007-2012 by Apple Inc.
# Copyright 1997-2005 by Easy Software Products, all rights reserved.
#
# These coded instructions, statements, and computer programs are the
fi
echo ""
+echo "Add Shared Printer Test"
+echo ""
+echo " lpadmin -p Test3 -E -v ipp://localhost:8631/printers/Test2 -m raw"
+$VALGRIND ../systemv/lpadmin -p Test3 -E -v ipp://localhost:8631/printers/Test2 -m raw 2>&1
+if test $? != 0; then
+ echo " FAILED"
+ exit 1
+else
+ echo " PASSED"
+fi
+echo ""
+
#
# End of "$Id: 5.1-lpadmin.sh 7494 2008-04-25 18:36:46Z mike $".
#
#
# Test the lp command.
#
-# Copyright 2007-2011 by Apple Inc.
+# Copyright 2007-2012 by Apple Inc.
# Copyright 1997-2005 by Easy Software Products, all rights reserved.
#
# These coded instructions, statements, and computer programs are the
echo "LP Destination Test"
echo ""
-echo " lp -d Test2 testfile.jpg"
-$VALGRIND ../systemv/lp -d Test2 testfile.jpg 2>&1
+echo " lp -d Test3 -o fit-to-page testfile.jpg"
+$VALGRIND ../systemv/lp -d Test3 -o fit-to-page testfile.jpg 2>&1
if test $? != 0; then
echo " FAILED"
exit 1
#
# Test the lpr command.
#
-# Copyright 2007-2011 by Apple Inc.
+# Copyright 2007-2012 by Apple Inc.
# Copyright 1997-2005 by Easy Software Products, all rights reserved.
#
# These coded instructions, statements, and computer programs are the
echo "LPR Destination Test"
echo ""
-echo " lpr -P Test2 testfile.jpg"
-$VALGRIND ../berkeley/lpr -P Test2 testfile.jpg 2>&1
+echo " lpr -P Test3 -o fit-to-page testfile.jpg"
+$VALGRIND ../berkeley/lpr -P Test3 -o fit-to-page testfile.jpg 2>&1
if test $? != 0; then
echo " FAILED"
exit 1
onepage-letter.pdf \
onepage-letter.ps \
testfile.jpg \
+ testfile.pcl \
testfile.pdf \
testfile.ps \
testfile.txt
STATUS successful-ok
STATUS client-error-document-format-not-supported
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
ATTR name requesting-user-name $user
STATUS successful-ok
- EXPECT job-state OF-TYPE unknown|enum IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >6 REPEAT-NO-MATCH
+ EXPECT job-state OF-TYPE unknown|enum IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >6 REPEAT-NO-MATCH REPEAT-LIMIT 30
DISPLAY job-state
}
STATUS successful-ok
STATUS client-error-document-format-not-supported
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS client-error-document-format-not-supported
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
STATUS successful-ok
STATUS server-error-job-canceled
- STATUS server-error-busy REPEAT-MATCH
+ STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10
EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
*define_match, /* Variable to define on match */
*define_no_match, /* Variable to define on no-match */
*define_value; /* Variable to define with value */
- int repeat_match, /* Repeat test on match */
+ int repeat_limit, /* Maximum number of times to repeat */
+ repeat_match, /* Repeat test on match */
repeat_no_match, /* Repeat test on no match */
with_flags, /* WITH flags */
count; /* Expected count if > 0 */
ipp_status_t status; /* Expected status code */
char *if_defined, /* Only if variable is defined */
*if_not_defined; /* Only if variable is not defined */
- int repeat_match, /* Repeat the test when it does not match */
+ int repeat_limit, /* Maximum number of times to repeat */
+ repeat_match, /* Repeat the test when it does not match */
repeat_no_match; /* Repeat the test when it matches */
} _cups_status_t;
set_variable(&vars, "filetype", "text/html");
else if (!_cups_strcasecmp(ext, ".jpg"))
set_variable(&vars, "filetype", "image/jpeg");
+ else if (!_cups_strcasecmp(ext, ".pcl") ||
+ !_cups_strcasecmp(ext, ".pcl.gz"))
+ set_variable(&vars, "filetype", "application/vnd.hp-PCL");
else if (!_cups_strcasecmp(ext, ".pdf"))
set_variable(&vars, "filetype", "application/pdf");
else if (!_cups_strcasecmp(ext, ".png"))
show_header = 1, /* Show the test header? */
ignore_errors, /* Ignore test failures? */
skip_previous = 0, /* Skip on previous test failure? */
+ repeat_count, /* Repeat count */
+ repeat_interval, /* Repeat interval */
+ repeat_prev, /* Previous repeat interval */
repeat_test; /* Repeat a test? */
http_t *http = NULL; /* HTTP connection to server */
FILE *fp = NULL; /* Test file */
_cups_strcasecmp(token, "IF-NOT-DEFINED") &&
_cups_strcasecmp(token, "IN-GROUP") &&
_cups_strcasecmp(token, "OF-TYPE") &&
+ _cups_strcasecmp(token, "REPEAT-LIMIT") &&
_cups_strcasecmp(token, "REPEAT-MATCH") &&
_cups_strcasecmp(token, "REPEAT-NO-MATCH") &&
_cups_strcasecmp(token, "SAME-COUNT-AS") &&
if (_cups_strcasecmp(token, "IF-DEFINED") &&
_cups_strcasecmp(token, "IF-NOT-DEFINED") &&
+ _cups_strcasecmp(token, "REPEAT-LIMIT") &&
_cups_strcasecmp(token, "REPEAT-MATCH") &&
_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
last_status = NULL;
last_status->if_defined = NULL;
last_status->if_not_defined = NULL;
+ last_status->repeat_limit = 1000;
last_status->repeat_match = 0;
last_status->repeat_no_match = 0;
}
num_expects ++;
memset(last_expect, 0, sizeof(_cups_expect_t));
+ last_expect->repeat_limit = 1000;
if (token[0] == '!')
{
goto test_exit;
}
}
+ else if (!_cups_strcasecmp(token, "REPEAT-LIMIT"))
+ {
+ if (!get_token(fp, token, sizeof(token), &linenum))
+ {
+ print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum);
+ pass = 0;
+ goto test_exit;
+ }
+ else if (atoi(token) <= 0)
+ {
+ print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum);
+ pass = 0;
+ goto test_exit;
+ }
+
+ if (last_status)
+ last_status->repeat_limit = atoi(token);
+ else if (last_expect)
+ last_expect->repeat_limit = atoi(token);
+ else
+ {
+ print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
+ "on line %d.", linenum);
+ pass = 0;
+ goto test_exit;
+ }
+ }
else if (!_cups_strcasecmp(token, "REPEAT-MATCH"))
{
if (last_status)
goto skip_error;
}
+ repeat_count = 0;
+ repeat_interval = 1;
+ repeat_prev = 1;
+
do
{
+ repeat_count ++;
+
status = HTTP_OK;
if (transfer == _CUPS_TRANSFER_CHUNKED ||
status = httpGetStatus(http);
}
- if (!Cancel && status == HTTP_ERROR &&
+ if (!Cancel && status == HTTP_ERROR && http->error != EINVAL &&
#ifdef WIN32
http->error != WSAETIMEDOUT)
#else
}
}
- if (!Cancel && status == HTTP_ERROR &&
+ if (!Cancel && status == HTTP_ERROR && http->error != EINVAL &&
#ifdef WIN32
http->error != WSAETIMEDOUT)
#else
if (response->request.status.status_code == statuses[i].status)
{
- if (statuses[i].repeat_match)
+ if (statuses[i].repeat_match &&
+ repeat_count < statuses[i].repeat_limit)
repeat_test = 1;
break;
}
- else if (statuses[i].repeat_no_match)
+ else if (statuses[i].repeat_no_match &&
+ repeat_count < statuses[i].repeat_limit)
repeat_test = 1;
}
}
}
- if (expect->repeat_no_match)
+ if (expect->repeat_no_match &&
+ repeat_count < expect->repeat_limit)
repeat_test = 1;
continue;
buffer, sizeof(buffer));
}
- if (expect->repeat_no_match)
+ if (expect->repeat_no_match &&
+ repeat_count < expect->repeat_limit)
repeat_test = 1;
continue;
expect->count, found->num_values);
}
- if (expect->repeat_no_match)
+ if (expect->repeat_no_match &&
+ repeat_count < expect->repeat_limit)
repeat_test = 1;
continue;
expect->same_count_as, attrptr->num_values);
}
- if (expect->repeat_no_match)
+ if (expect->repeat_no_match &&
+ repeat_count < expect->repeat_limit)
repeat_test = 1;
continue;
if (found && expect->define_value)
set_variable(vars, expect->define_value, buffer);
- if (found && expect->repeat_match)
+ if (found && expect->repeat_match &&
+ repeat_count < expect->repeat_limit)
repeat_test = 1;
}
}
*/
if (repeat_test)
- sleep(1);
+ {
+ if (Output == _CUPS_OUTPUT_TEST)
+ {
+ printf("%04d]\n", repeat_count);
+ fflush(stdout);
+ }
+
+ sleep(repeat_interval);
+ repeat_interval = _cupsNextDelay(repeat_interval, &repeat_prev);
+
+ if (Output == _CUPS_OUTPUT_TEST)
+ {
+ printf(" %-68.68s [", name);
+ fflush(stdout);
+ }
+ }
}
while (repeat_test);
# Perform the complete set of IPP compliance tests specified in the
# CUPS Software Test Plan.
#
-# Copyright 2007-2011 by Apple Inc.
+# Copyright 2007-2012 by Apple Inc.
# Copyright 1997-2007 by Easy Software Products, all rights reserved.
#
# These coded instructions, statements, and computer programs are the
argcount=$#
+#
+# Don't allow "make check" or "make test" to be run by root...
+#
+
+if test "x`id -u`" = x0; then
+ echo Please run this as a normal user. Not supported when run as root.
+ exit 1
+fi
+
+#
+# Force the permissions of the files we create...
+#
+
+umask 022
+
#
# Make the IPP test program...
#
fi
cat >/tmp/cups-$user/cupsd.conf <<EOF
+StrictConformance Yes
Browsing Off
FileDevice yes
Printcap
-Listen 127.0.0.1:$port
+Listen localhost:$port
User $user
ServerRoot /tmp/cups-$user
StateDir /tmp/cups-$user
PreserveJobHistory Yes
<Policy default>
<Limit All>
-Order Deny,Allow
-Deny from all
-Allow from 127.0.0.1
+Order Allow,Deny
$encryption
</Limit>
</Policy>
echo "<PRE>" >>$strfile
fail=0
-for file in 4*.test; do
+for file in 4*.test ipp-2.1.test; do
echo $ac_n "Performing $file: $ac_c"
echo "" >>$strfile
- $VALGRIND ./ipptool -tI ipp://localhost:$port/printers $file >> $strfile
+ if test $file = ipp-2.1.test; then
+ uri="ipp://localhost:$port/printers/Test1"
+ options="-V 2.1 -d NOPRINT=1 -f testfile.ps"
+ else
+ uri="ipp://localhost:$port/printers"
+ options=""
+ fi
+ $VALGRIND ./ipptool -tI $options $uri $file >> $strfile
status=$?
if test $status != 0; then
echo "<P>PASS: Printer 'Test2' correctly produced $count page(s).</P>" >>$strfile
fi
+# Paged printed on Test3
+count=`$GREP '^Test3 ' /tmp/cups-$user/log/page_log | grep -v total | awk 'BEGIN{count=0}{count=count+$7}END{print count}'`
+expected=2
+if test $count != $expected; then
+ echo "FAIL: Printer 'Test3' produced $count page(s), expected $expected."
+ echo "<P>FAIL: Printer 'Test3' produced $count page(s), expected $expected.</P>" >>$strfile
+ fail=`expr $fail + 1`
+else
+ echo "PASS: Printer 'Test3' correctly produced $count page(s)."
+ echo "<P>PASS: Printer 'Test3' correctly produced $count page(s).</P>" >>$strfile
+fi
+
# Requests logged
count=`wc -l /tmp/cups-$user/log/access_log | awk '{print $1}'`
-expected=`expr 37 + 18 + $pjobs \* 8 + $pprinters \* $pjobs \* 4`
+expected=`expr 37 + 18 + 28 + $pjobs \* 8 + $pprinters \* $pjobs \* 4`
if test $count != $expected; then
echo "FAIL: $count requests logged, expected $expected."
echo "<P>FAIL: $count requests logged, expected $expected.</P>" >>$strfile
# Error log messages
count=`$GREP '^E ' /tmp/cups-$user/log/error_log | wc -l | awk '{print $1}'`
-if test $count != 18; then
- echo "FAIL: $count error messages, expected 18."
+if test $count != 33; then
+ echo "FAIL: $count error messages, expected 33."
$GREP '^E ' /tmp/cups-$user/log/error_log
- echo "<P>FAIL: $count error messages, expected 18.</P>" >>$strfile
+ echo "<P>FAIL: $count error messages, expected 33.</P>" >>$strfile
echo "<PRE>" >>$strfile
$GREP '^E ' /tmp/cups-$user/log/error_log | sed -e '1,$s/&/&/g' -e '1,$s/</</g' >>$strfile
echo "</PRE>" >>$strfile
--- /dev/null
+\eEAll work and no play makes Johhny a dull boy. All work and no\r
+play makes Johhny a dull boy. All work and no play makes Johhny\r
+a dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy. All\r
+work and no play makes Johhny a dull boy. All work and no play\r
+makes Johhny a dull boy. All work and no play makes Johhny a\r
+dull boy. All work and no play makes Johhny a dull boy.\f\eE\r
--- /dev/null
+/*
+ * Simple test program that lists the Back to My Mac domains on a Mac.
+ *
+ * Compile with:
+ *
+ * clang -o testbtmm -g testbtmm.c -framework SystemConfiguration -framework CoreFoundation
+ */
+
+#include <stdio.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+
+/*
+ * 'dnssdAddAlias()' - Add a DNS-SD alias name.
+ */
+
+static void
+show_domain(const void *key, /* I - Key */
+ const void *value, /* I - Value (domain) */
+ void *context) /* I - Unused */
+{
+ char valueStr[1024]; /* Domain string */
+
+
+ (void)key;
+ (void)context;
+
+ if (CFGetTypeID((CFStringRef)value) == CFStringGetTypeID() &&
+ CFStringGetCString((CFStringRef)value, valueStr, sizeof(valueStr),
+ kCFStringEncodingUTF8))
+ printf("Back to My Mac domain: \"%s\"\n", valueStr);
+ else
+ puts("Bad Back to My Mac domain in dynamic store.");
+}
+
+
+int
+main(void)
+{
+ SCDynamicStoreRef sc; /* Context for dynamic store */
+ CFDictionaryRef btmm; /* Back-to-My-Mac domains */
+
+
+ sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cups"), NULL, NULL);
+
+ if (!sc)
+ {
+ puts("Unable to open dynamic store.");
+ exit(1);
+ }
+
+ btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac"));
+ if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID())
+ {
+ printf("%d Back to My Mac domains.\n", (int)CFDictionaryGetCount(btmm));
+ CFDictionaryApplyFunction(btmm, show_domain, NULL);
+ }
+ else if (btmm)
+ puts("Bad Back to My Mac data in dynamic store.");
+ else
+ puts("No Back to My Mac domains.");
+
+ return (1);
+}
728FB7EC1536161C005426E1 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = ../../../../../usr/lib/libz.dylib; sourceTree = "<group>"; };
728FB7EF1536167A005426E1 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = ../../../../../usr/lib/libiconv.dylib; sourceTree = "<group>"; };
728FB7F01536167A005426E1 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = ../../../../../usr/lib/libresolv.dylib; sourceTree = "<group>"; };
+ 72A4332F155844CF002E172D /* libcups_static.a */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcups_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
72C16CB8137B195D007E4BF4 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = file.c; path = ../scheduler/file.c; sourceTree = SOURCE_ROOT; };
- 72F75A4C1336F31B004BB496 /* libcups_static.a */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; name = libcups_static.a; path = "/Users/msweet/c/cups-trunk/xcode/build/Release/libcups_static.a"; sourceTree = "<absolute>"; };
72F75A521336F950004BB496 /* cupstestppd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cupstestppd; sourceTree = BUILT_PRODUCTS_DIR; };
72F75A5B1336F988004BB496 /* cupstestppd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cupstestppd.c; path = ../systemv/cupstestppd.c; sourceTree = "<group>"; };
72F75A611336F9A3004BB496 /* libcupsimage.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcupsimage.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
270CCDA7135E3C9E00007BE2 /* testmime */,
726AD6F7135E88F0002C930D /* ippserver */,
278C58CB136B640300836530 /* testhttp */,
+ 72A4332F155844CF002E172D /* libcups_static.a */,
);
name = Products;
sourceTree = "<group>";
);
name = libcups_static;
productName = libcups;
- productReference = 72F75A4C1336F31B004BB496 /* libcups_static.a */;
+ productReference = 72A4332F155844CF002E172D /* libcups_static.a */;
productType = "com.apple.product-type.library.dynamic";
};
2766835B1337A9B6000D33D0 /* cupsctl */ = {
72BF96371333042100B1EAD7 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0420;
+ LastUpgradeCheck = 0440;
ORGANIZATIONNAME = "Apple Inc.";
};
buildConfigurationList = 72BF963A1333042100B1EAD7 /* Build configuration list for PBXProject "CUPS" */;