Snap for 4667902 from 985548d9a7de8cde72ea0b5a81f6f0854d049454 to pi-release

Change-Id: Id6d0019fdc494019a23ee520e9ca2fe30ed31827
diff --git a/config.h b/config.h
index 2decf31..28fe034 100644
--- a/config.h
+++ b/config.h
@@ -17,8 +17,8 @@
 #ifndef _CUPS_CONFIG_H_
 #define _CUPS_CONFIG_H_
 
-#define CUPS_SVERSION "CUPS v2.2.3"
-#define CUPS_MINIMAL "CUPS/2.2.3"
+#define CUPS_SVERSION "CUPS v2.2.6"
+#define CUPS_MINIMAL "CUPS/2.2.6"
 #define CUPS_DEFAULT_PRINTOPERATOR_AUTH "@SYSTEM"
 #define CUPS_DEFAULT_LOG_LEVEL "warn"
 #define CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS "dnssd"
diff --git a/cups/Makefile b/cups/Makefile
index 1df7d85..9a220c8 100644
--- a/cups/Makefile
+++ b/cups/Makefile
@@ -1,7 +1,7 @@
 #
-# API library Makefile for CUPS.
+# Library Makefile for CUPS.
 #
-# Copyright 2007-2016 by Apple Inc.
+# Copyright 2007-2017 by Apple Inc.
 # Copyright 1997-2006 by Easy Software Products, all rights reserved.
 #
 # These coded instructions, statements, and computer programs are the
@@ -89,6 +89,7 @@
 		testcups.o \
 		testdest.o \
 		testfile.o \
+		testgetdests.o \
 		testhttp.o \
 		testi18n.o \
 		testipp.o \
@@ -158,6 +159,7 @@
 		testcups \
 		testdest \
 		testfile \
+		testgetdests \
 		testhttp \
 		testi18n \
 		testipp \
@@ -344,7 +346,7 @@
 
 libcups.la:    $(LIBOBJS)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS:.o=.lo) \
+	$(LD_CC) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS:.o=.lo) \
 		-rpath $(LIBDIR) -version-info 2:12 $(LIBGSSAPI) $(SSLLIBS) \
 		$(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
@@ -383,7 +385,7 @@
 
 testadmin:	testadmin.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testadmin.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testadmin.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -393,7 +395,7 @@
 
 testarray:	testarray.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testarray.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testarray.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running array API tests...
 	./testarray
@@ -405,7 +407,7 @@
 
 testcache:	testcache.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testcache.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testcache.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -415,7 +417,7 @@
 
 testconflicts:	testconflicts.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testconflicts.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testconflicts.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -425,7 +427,7 @@
 
 testcreds:	testcreds.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testcreds.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testcreds.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -435,7 +437,7 @@
 
 testcups:	testcups.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testcups.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testcups.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -445,7 +447,7 @@
 
 testdest:	testdest.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testdest.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testdest.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -455,19 +457,29 @@
 
 testfile:	testfile.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testfile.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testfile.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running file API tests...
 	./testfile
 
 
 #
+# testgetdests (dependency on static CUPS library is intentional)
+#
+
+testgetdests:	testgetdests.o $(LIBCUPSSTATIC)
+	echo Linking $@...
+	$(LD_CC) $(LDFLAGS) -o $@ testgetdests.o $(LIBCUPSSTATIC) \
+		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
 # testhttp (dependency on static CUPS library is intentional)
 #
 
 testhttp:	testhttp.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testhttp.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testhttp.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running HTTP API tests...
 	./testhttp
@@ -479,7 +491,7 @@
 
 testipp:	testipp.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testipp.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testipp.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running IPP API tests...
 	./testipp
@@ -491,7 +503,7 @@
 
 testi18n:	testi18n.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testi18n.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testi18n.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running internationalization API tests...
 	./testi18n
@@ -503,10 +515,22 @@
 
 testlang:	testlang.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testlang.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testlang.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	echo Creating locale directory structure...
+	$(RM) -r locale
+	$(MKDIR) locale/en
+	echo 'msgid "No"' > locale/en/cups_en.po
+	echo 'msgstr "No"' >> locale/en/cups_en.po
+	echo 'msgid "Yes"' >> locale/en/cups_en.po
+	echo 'msgstr "Yes"' >> locale/en/cups_en.po
+	for po in ../locale/cups_*.po; do \
+		lang=`basename $$po .po | sed -e '1,$$s/^cups_//'`; \
+		$(MKDIR) locale/$$lang; \
+		$(LN) ../../$$po locale/$$lang; \
+	done
 	echo Running language API tests...
-	./testlang
+	LOCALEDIR=locale ./testlang
 
 
 #
@@ -515,7 +539,7 @@
 
 testoptions:	testoptions.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testoptions.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testoptions.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running option API tests...
 	./testoptions
@@ -527,7 +551,7 @@
 
 testppd:	testppd.o $(LIBCUPSSTATIC) test.ppd test2.ppd
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testppd.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testppd.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running PPD API tests...
 	./testppd
@@ -539,7 +563,7 @@
 
 testpwg:	testpwg.o $(LIBCUPSSTATIC) test.ppd
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testpwg.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testpwg.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 	echo Running PWG API tests...
 	./testpwg test.ppd
@@ -551,7 +575,7 @@
 
 testsnmp:	testsnmp.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testsnmp.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testsnmp.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -561,7 +585,7 @@
 
 tlscheck:	tlscheck.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ tlscheck.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ tlscheck.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -571,56 +595,27 @@
 
 apihelp:
 	echo Generating CUPS API help files...
-	mxmldoc --section "Programming" \
-		--title "Introduction to CUPS Programming" \
-		--css ../doc/cups-printable.css \
-		--header api-overview.header --intro api-overview.shtml \
-		>../doc/help/api-overview.html
+	$(RM) cupspm.xml
+	mxmldoc --section "Programming" --body cupspm.md \
+		cupspm.xml \
+		auth.c cups.h dest*.c encode.c http.h http*.c ipp.h ipp*.c \
+		options.c tls-darwin.c usersys.c util.c \
+		--coverimage cupspm.png \
+		--epub ../doc/help/cupspm.epub
+	mxmldoc --section "Programming" --body cupspm.md \
+		cupspm.xml > ../doc/help/cupspm.html
+	$(RM) cupspm.xml
 	mxmldoc --section "Programming" --title "Administration APIs" \
 		--css ../doc/cups-printable.css \
 		--header api-admin.header --intro api-admin.shtml \
 		api-admin.xml \
 		adminutil.c adminutil.h getdevices.c >../doc/help/api-admin.html
-	mxmldoc --tokens help/api-admin.html api-admin.xml >../doc/help/api-admin.tokens
 	$(RM) api-admin.xml
-	mxmldoc --section "Programming" --title "Array API" \
-		--css ../doc/cups-printable.css \
-		--header api-array.header --intro api-array.shtml \
-		api-array.xml \
-		array.h array.c >../doc/help/api-array.html
-	mxmldoc --tokens help/api-array.html api-array.xml >../doc/help/api-array.tokens
-	$(RM) api-array.xml
-	mxmldoc --section "Programming" --title "CUPS API" \
-		--css ../doc/cups-printable.css \
-		--header api-cups.header --intro api-cups.shtml \
-		api-cups.xml \
-		cups.h pwg.h adminutil.c dest*.c language.c notify.c \
-		options.c pwg-media.c tempfile.c usersys.c \
-		util.c >../doc/help/api-cups.html
-	mxmldoc --tokens help/api-cups.html api-cups.xml >../doc/help/api-cups.tokens
-	$(RM) api-cups.xml
-	mxmldoc --section "Programming" --title "File and Directory APIs" \
-		--css ../doc/cups-printable.css \
-		--header api-filedir.header --intro api-filedir.shtml \
-		api-filedir.xml \
-		file.h file.c dir.h dir.c >../doc/help/api-filedir.html
-	mxmldoc --tokens api-filedir.xml >../doc/help/api-filedir.tokens
-	$(RM) api-filedir.xml
 	mxmldoc --section "Programming" --title "PPD API (DEPRECATED)" \
 		--css ../doc/cups-printable.css \
 		--header api-ppd.header --intro api-ppd.shtml \
 		api-ppd.xml ppd.h ppd-*.c >../doc/help/api-ppd.html
-	mxmldoc --tokens help/api-ppd.html api-ppd.xml >../doc/help/api-ppd.tokens
 	$(RM) api-ppd.xml
-	mxmldoc --section "Programming" --title "HTTP and IPP APIs" \
-		--css ../doc/cups-printable.css \
-		--header api-httpipp.header --intro api-httpipp.shtml \
-		api-httpipp.xml \
-		http.h ipp.h auth.c getdevices.c getputfile.c encode.c \
-		http.c http-addr.c http-support.c ipp.c ipp-support.c \
-		md5passwd.c request.c >../doc/help/api-httpipp.html
-	mxmldoc --tokens help/api-httpipp.html api-httpipp.xml >../doc/help/api-httpipp.tokens
-	$(RM) api-httpipp.xml
 	mxmldoc --section "Programming" \
 		--title "Filter and Backend Programming" \
 		--css ../doc/cups-printable.css \
@@ -628,7 +623,6 @@
 		api-filter.xml \
 		backchannel.c backend.h backend.c sidechannel.c sidechannel.h \
 		>../doc/help/api-filter.html
-	mxmldoc --tokens help/api-filter.html api-filter.xml >../doc/help/api-filter.tokens
 	$(RM) api-filter.xml
 
 
@@ -637,8 +631,6 @@
 #
 
 sloc:
-	echo "libcupslite: \c"
-	sloccount $(LITEOBJS:.o=.c) 2>/dev/null | grep "Total Physical" | awk '{print $$9}'
 	echo "libcups: \c"
 	sloccount $(LIBOBJS:.o=.c) 2>/dev/null | grep "Total Physical" | awk '{print $$9}'
 
diff --git a/cups/api-array.header b/cups/api-array.header
deleted file mode 100644
index 557833e..0000000
--- a/cups/api-array.header
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
-  Array API header for CUPS.
-
-  Copyright 2008-2011 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/".
--->
-
-<h1 class='title'>Array API</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Header</th>
-	<th>cups/array.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html' target='_top'>Introduction to CUPS Programming</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-array.shtml b/cups/api-array.shtml
deleted file mode 100644
index 374ef5b..0000000
--- a/cups/api-array.shtml
+++ /dev/null
@@ -1,194 +0,0 @@
-<!--
-  Array API introduction for CUPS.
-
-  Copyright 2007-2011 by Apple Inc.
-  Copyright 1997-2006 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/".
--->
-
-<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
-
-<p>The CUPS array API provides a high-performance generic array container.
-The contents of the array container can be sorted and the container itself is
-designed for optimal speed and memory usage under a wide variety of conditions.
-Sorted arrays use a binary search algorithm from the last found or inserted
-element to quickly find matching elements in the array. Arrays created with the
-optional hash function can often find elements with a single lookup. The
-<a href='#cups_array_t'><code>cups_array_t</code></a> type is used when
-referring to a CUPS array.</p>
-
-<p>The CUPS scheduler (<tt>cupsd</tt>) and many of the CUPS API
-functions use the array API to efficiently manage large lists of
-data.</p>
-
-<h3><a name='MANAGING_ARRAYS'>Managing Arrays</a></h3>
-
-<p>Arrays are created using either the
-<a href='#cupsArrayNew'><code>cupsArrayNew</code></a>,
-<a href='#cupsArrayNew2'><code>cupsArrayNew2</code></a>, or
-<a href='#cupsArrayNew2'><code>cupsArrayNew3</code></a> functions. The
-first function creates a new array with the specified callback function
-and user data pointer:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-static int compare_func(void *first, void *second, void *user_data);
-
-void *user_data;
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>(compare_func, user_data);
-</pre>
-
-<p>The comparison function (type
-<a href="#cups_arrayfunc_t"><code>cups_arrayfunc_t</code></a>) is called
-whenever an element is added to the array and can be <code>NULL</code> to
-create an unsorted array. The function returns -1 if the first element should
-come before the second, 0 if the first and second elements should have the same
-ordering, and 1 if the first element should come after the second.</p>
-
-<p>The "user_data" pointer is passed to your comparison function. Pass
-<code>NULL</code> if you do not need to associate the elements in your array
-with additional information.</p>
-
-<p>The <a href='#cupsArrayNew2'><code>cupsArrayNew2</code></a> function adds
-two more arguments to support hashed lookups, which can potentially provide
-instantaneous ("O(1)") lookups in your array:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-#define HASH_SIZE 512 /* Size of hash table */
-
-static int compare_func(void *first, void *second, void *user_data);
-static int hash_func(void *element, void *user_data);
-
-void *user_data;
-<a href='#cups_array_t'>cups_array_t</a> *hash_array = <a href='#cupsArrayNew2'>cupsArrayNew2</a>(compare_func, user_data, hash_func, HASH_SIZE);
-</pre>
-
-<p>The hash function (type
-<a href="#cups_ahash_func_t"><code>cups_ahash_func_t</code></a>) should return a
-number from 0 to (hash_size-1) that (hopefully) uniquely identifies the
-element and is called whenever you look up an element in the array with
-<a href='#cupsArrayFind'><code>cupsArrayFind</code></a>. The hash size is
-only limited by available memory, but generally should not be larger than
-16384 to realize any performance improvement.</p>
-
-<p>The <a href='#cupsArrayNew3'><code>cupsArrayNew3</code></a> function adds
-copy and free callbacks to support basic memory management of elements:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-#define HASH_SIZE 512 /* Size of hash table */
-
-static int compare_func(void *first, void *second, void *user_data);
-static void *copy_func(void *element, void *user_data);
-static void free_func(void *element, void *user_data);
-static int hash_func(void *element, void *user_data);
-
-void *user_data;
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew3'>cupsArrayNew3</a>(compare_func, user_data, NULL, 0, copy_func, free_func);
-
-<a href='#cups_array_t'>cups_array_t</a> *hash_array = <a href='#cupsArrayNew3'>cupsArrayNew3</a>(compare_func, user_data, hash_func, HASH_SIZE, copy_func, free_func);
-</pre>
-
-<p>Once you have created the array, you add elements using the
-<a href='#cupsArrayAdd'><code>cupsArrayAdd</code></a>
-<a href='#cupsArrayInsert'><code>cupsArrayInsert</code></a> functions.
-The first function adds an element to the array, adding the new element
-after any elements that have the same order, while the second inserts the
-element before others with the same order. For unsorted arrays,
-<a href='#cupsArrayAdd'><code>cupsArrayAdd</code></a> appends the element to
-the end of the array while
-<a href='#cupsArrayInsert'><code>cupsArrayInsert</code></a> inserts the
-element at the beginning of the array. For example, the following code
-creates a sorted array of character strings:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-/* Use strcmp() to compare strings - it will ignore the user_data pointer */
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
-
-/* Add four strings to the array */
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
-</pre>
-
-<p>Elements are removed using the
-<a href='#cupsArrayRemove'><code>cupsArrayRemove</code></a> function, for
-example:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-/* Use strcmp() to compare strings - it will ignore the user_data pointer */
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
-
-/* Add four strings to the array */
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
-
-/* Remove "Red Fish" */
-<a href='#cupsArrayRemove'>cupsArrayRemove</a>(array, "Red Fish");
-</pre>
-
-<p>Finally, you free the memory used by the array using the
-<a href='#cupsArrayDelete'><code>cupsArrayDelete</code></a> function. All
-of the memory for the array and hash table (if any) is freed, however <em>CUPS
-does not free the elements unless you provide copy and free functions</em>.</p>
-
-<h3><a name='FINDING_AND_ENUMERATING'>Finding and Enumerating Elements</a></h3>
-
-<p>CUPS provides several functions to find and enumerate elements in an
-array. Each one sets or updates a "current index" into the array, such that
-future lookups will start where the last one left off:</p>
-
-<dl>
-	<dt><a href='#cupsArrayFind'><code>cupsArrayFind</code></a></dt>
-	<dd>Returns the first matching element.</dd>
-	<dt><a href='#cupsArrayFirst'><code>cupsArrayFirst</code></a></dt>
-	<dd>Returns the first element in the array.</dd>
-	<dt><a href='#cupsArrayIndex'><code>cupsArrayIndex</code></a></dt>
-	<dd>Returns the Nth element in the array, starting at 0.</dd>
-	<dt><a href='#cupsArrayLast'><code>cupsArrayLast</code></a></dt>
-	<dd>Returns the last element in the array.</dd>
-	<dt><a href='#cupsArrayNext'><code>cupsArrayNext</code></a></dt>
-	<dd>Returns the next element in the array.</dd>
-	<dt><a href='#cupsArrayPrev'><code>cupsArrayPrev</code></a></dt>
-	<dd>Returns the previous element in the array.</dd>
-</dl>
-
-<p>Each of these functions returns <code>NULL</code> when there is no
-corresponding element.  For example, a simple <code>for</code> loop using the
-<a href='#cupsArrayFirst'><code>cupsArrayFirst</code></a> and
-<a href='#cupsArrayNext'><code>cupsArrayNext</code></a> functions will
-enumerate all of the strings in our previous example:</p> 
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-/* Use strcmp() to compare strings - it will ignore the user_data pointer */
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
-
-/* Add four strings to the array */
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
-
-/* Show all of the strings in the array */
-char *s;
-for (s = (char *)<a href='#cupsArrayFirst'>cupsArrayFirst</a>(array); s != NULL; s = (char *)<a href='#cupsArrayNext'>cupsArrayNext</a>(array))
-  puts(s);
-</pre>
diff --git a/cups/api-cups.header b/cups/api-cups.header
deleted file mode 100644
index 23b3794..0000000
--- a/cups/api-cups.header
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
-  CUPS API header for CUPS.
-
-  Copyright 2008-2011 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/".
--->
-
-<h1 class='title'>CUPS API</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Header</th>
-	<th>cups/cups.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html' target='_top'>Introduction to CUPS Programming</a><br>
-	Programming: <a href='api-array.html' target='_top'>Array API</a><br>
-	Programming: <a href='api-filedir.html' target='_top'>File and Directory APIs</a><br>
-	Programming: <a href='api-filter.html' target='_top'>Filter and Backend Programming</a><br>
-	Programming: <a href='api-httpipp.html' target='_top'>HTTP and IPP APIs</a><br>
-	Programming: <a href='api-ppd.html' target='_top'>PPD API</a><br>
-	Programming: <a href='api-raster.html' target='_top'>Raster API</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-cups.shtml b/cups/api-cups.shtml
deleted file mode 100644
index 918efe7..0000000
--- a/cups/api-cups.shtml
+++ /dev/null
@@ -1,441 +0,0 @@
-<!--
-  API introduction for CUPS.
-
-  Copyright 2007-2013 by Apple Inc.
-  Copyright 1997-2006 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/".
--->
-
-<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
-
-<p>The CUPS API provides the convenience functions needed to support
-applications, filters, printer drivers, and backends that need to interface
-with the CUPS scheduler.</p>
-
-<h3><a name='CLIENTS_AND_SERVERS'>Clients and Servers</a></h3>
-
-<p>CUPS is based on the Internet Printing Protocol ("IPP"), which allows
-clients (applications) to communicate with a server (the scheduler) to get a
-list of printers, send print jobs, and so forth. You identify which server
-you want to communicate with using a pointer to the opaque structure
-<code>http_t</code>. All of the examples in this document use the
-<code>CUPS_HTTP_DEFAULT</code> constant, referring to the default connection
-to the scheduler. The <a href='api-httpipp.html' target='_top'>HTTP and IPP
-APIs</a> document provides more information on server connections.</p>
-
-<h3><a name='PRINTERS_AND_CLASSES'>Printers and Classes</a></h3>
-
-<p>Printers and classes (collections of printers) are accessed through
-the <a href="#cups_dest_t"><code>cups_dest_t</code></a> structure which
-includes the name (<code>name</code>), instance (<code>instance</code> -
-a way of selecting certain saved options/settings), and the options and
-attributes associated with that destination (<code>num_options</code> and
-<code>options</code>). Destinations are created using the
-<a href="#cupsGetDests"><code>cupsGetDests</code></a> function and freed
-using the <a href='#cupsFreeDests'><code>cupsFreeDests</code></a> function.
-The <a href='#cupsGetDest'><code>cupsGetDest</code></a> function finds a
-specific destination for printing:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dests;
-int num_dests = <a href='#cupsGetDests'>cupsGetDests</a>(&amp;dests);
-<a href='#cups_dest_t'>cups_dest_t</a> *dest = <a href='#cupsGetDest'>cupsGetDest</a>("name", NULL, num_dests, dests);
-
-/* do something with dest */
-
-<a href='#cupsFreeDests'>cupsFreeDests</a>(num_dests, dests);
-</pre>
-
-<p>Passing <code>NULL</code> to
-<a href='#cupsGetDest'><code>cupsGetDest</code></a> for the destination name
-will return the default destination. Similarly, passing a <code>NULL</code>
-instance will return the default instance for that destination.</p>
-
-<div class='table'><table summary='Table 1: Printer Attributes' width='80%'>
-<caption>Table 1: <a name='TABLE1'>Printer Attributes</a></caption>
-<thead>
-<tr>
-	<th>Attribute Name</th>
-	<th>Description</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<td>"auth-info-required"</td>
-	<td>The type of authentication required for printing to this
-	destination: "none", "username,password", "domain,username,password",
-	or "negotiate" (Kerberos)</td>
-</tr>
-<tr>
-	<td>"printer-info"</td>
-	<td>The human-readable description of the destination such as "My
-	Laser Printer".</td>
-</tr>
-<tr>
-	<td>"printer-is-accepting-jobs"</td>
-	<td>"true" if the destination is accepting new jobs, "false" if
-	not.</td>
-</tr>
-<tr>
-	<td>"printer-is-shared"</td>
-	<td>"true" if the destination is being shared with other computers,
-	"false" if not.</td>
-</tr>
-<tr>
-	<td>"printer-location"</td>
-	<td>The human-readable location of the destination such as "Lab 4".</td>
-</tr>
-<tr>
-	<td>"printer-make-and-model"</td>
-	<td>The human-readable make and model of the destination such as "HP
-	LaserJet 4000 Series".</td>
-</tr>
-<tr>
-	<td>"printer-state"</td>
-	<td>"3" if the destination is idle, "4" if the destination is printing
-	a job, and "5" if the destination is stopped.</td>
-</tr>
-<tr>
-	<td>"printer-state-change-time"</td>
-	<td>The UNIX time when the destination entered the current state.</td>
-</tr>
-<tr>
-	<td>"printer-state-reasons"</td>
-	<td>Additional comma-delimited state keywords for the destination
-	such as "media-tray-empty-error" and "toner-low-warning".</td>
-</tr>
-<tr>
-	<td>"printer-type"</td>
-	<td>The <a href='#cups_printer_t'><code>cups_printer_t</code></a>
-	value associated with the destination.</td>
-</tr>
-</tbody>
-</table></div>
-
-<h3><a name='OPTIONS'>Options</a></h3>
-
-<p>Options are stored in arrays of
-<a href='#cups_option_t'><code>cups_option_t</code></a> structures. Each
-option has a name (<code>name</code>) and value (<code>value</code>)
-associated with it. The <a href='#cups_dest_t'><code>cups_dest_t</code></a>
-<code>num_options</code> and <code>options</code> members contain the
-default options for a particular destination, along with several informational
-attributes about the destination as shown in <a href='#TABLE1'>Table 1</a>.
-The <a href='#cupsGetOption'><code>cupsGetOption</code></a> function gets
-the value for the named option. For example, the following code lists the
-available destinations and their human-readable descriptions:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dests;
-int num_dests = <a href='#cupsGetDests'>cupsGetDests</a>(&amp;dests);
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int i;
-const char *value;
-
-for (i = num_dests, dest = dests; i > 0; i --, dest ++)
-  if (dest->instance == NULL)
-  {
-    value = <a href='#cupsGetOption'>cupsGetOption</a>("printer-info", dest->num_options, dest->options);
-    printf("%s (%s)\n", dest->name, value ? value : "no description");
-  }
-
-<a href='#cupsFreeDests'>cupsFreeDests</a>(num_dests, dests);
-</pre>
-
-<p>You can create your own option arrays using the
-<a href='#cupsAddOption'><code>cupsAddOption</code></a> function, which
-adds a single named option to an array:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-int num_options = 0;
-<a href='#cups_option_t'>cups_option_t</a> *options = NULL;
-
-/* The returned num_options value is updated as needed */
-num_options = <a href='#cupsAddOption'>cupsAddOption</a>("first", "value", num_options, &amp;options);
-
-/* This adds a second option value */
-num_options = <a href='#cupsAddOption'>cupsAddOption</a>("second", "value", num_options, &amp;options);
-
-/* This replaces the first option we added */
-num_options = <a href='#cupsAddOption'>cupsAddOption</a>("first", "new value", num_options, &amp;options);
-</pre>
-
-<p>Use a <code>for</code> loop to copy the options from a destination:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-int i;
-int num_options = 0;
-<a href='#cups_option_t'>cups_option_t</a> *options = NULL;
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-
-for (i = 0; i &lt; dest->num_options; i ++)
-  num_options = <a href='#cupsAddOption'>cupsAddOption</a>(dest->options[i].name, dest->options[i].value,
-                              num_options, &amp;options);
-</pre>
-
-<p>Use the <a href='#cupsFreeOptions'><code>cupsFreeOptions</code></a>
-function to free the options array when you are done using it:</p>
-
-<pre class='example'>
-<a href='#cupsFreeOptions'>cupsFreeOptions</a>(num_options, options);
-</pre>
-
-<h3><a name='PRINT_JOBS'>Print Jobs</a></h3>
-
-<p>Print jobs are identified by a locally-unique job ID number from 1 to
-2<sup>31</sup>-1 and have options and one or more files for printing to a
-single destination. The <a href='#cupsPrintFile'><code>cupsPrintFile</code></a>
-function creates a new job with one file. The following code prints the CUPS
-test page file:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int num_options;
-<a href='#cups_option_t'>cups_option_t</a> *options;
-int job_id;
-
-/* Print a single file */
-job_id = <a href='#cupsPrintFile'>cupsPrintFile</a>(dest->name, "/usr/share/cups/data/testprint.ps",
-                        "Test Print", num_options, options);
-</pre>
-
-<p>The <a href='#cupsPrintFiles'><code>cupsPrintFiles</code></a> function
-creates a job with multiple files. The files are provided in a
-<code>char *</code> array:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int num_options;
-<a href='#cups_option_t'>cups_option_t</a> *options;
-int job_id;
-char *files[3] = { "file1.pdf", "file2.pdf", "file3.pdf" };
-
-/* Print three files */
-job_id = <a href='#cupsPrintFiles'>cupsPrintFiles</a>(dest->name, 3, files, "Test Print", num_options, options);
-</pre>
-
-<p>Finally, the <a href='#cupsCreateJob'><code>cupsCreateJob</code></a>
-function creates a new job with no files in it. Files are added using the
-<a href='#cupsStartDocument'><code>cupsStartDocument</code></a>,
-<a href='api-httpipp.html#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>,
-and <a href='#cupsFinishDocument'><code>cupsFinishDocument</code></a> functions.
-The following example creates a job with 10 text files for printing:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int num_options;
-<a href='#cups_option_t'>cups_option_t</a> *options;
-int job_id;
-int i;
-char buffer[1024];
-
-/* Create the job */
-job_id = <a href='#cupsCreateJob'>cupsCreateJob</a>(CUPS_HTTP_DEFAULT, dest->name, "10 Text Files",
-                       num_options, options);
-
-/* If the job is created, add 10 files */
-if (job_id > 0)
-{
-  for (i = 1; i &lt;= 10; i ++)
-  {
-    snprintf(buffer, sizeof(buffer), "file%d.txt", i);
-
-    <a href='#cupsStartDocument'>cupsStartDocument</a>(CUPS_HTTP_DEFAULT, dest->name, job_id, buffer,
-                      CUPS_FORMAT_TEXT, i == 10);
-
-    snprintf(buffer, sizeof(buffer),
-             "File %d\n"
-             "\n"
-             "One fish,\n"
-             "Two fish,\n
-             "Red fish,\n
-             "Blue fish\n", i);
-
-    /* cupsWriteRequestData can be called as many times as needed */
-    <a href='#cupsWriteRequestData'>cupsWriteRequestData</a>(CUPS_HTTP_DEFAULT, buffer, strlen(buffer));
-
-    <a href='#cupsFinishDocument'>cupsFinishDocument</a>(CUPS_HTTP_DEFAULT, dest->name);
-  }
-}
-</pre>
-
-<p>Once you have created a job, you can monitor its status using the
-<a href='#cupsGetJobs'><code>cupsGetJobs</code></a> function, which returns
-an array of <a href='#cups_job_t'><code>cups_job_t</code></a> structures.
-Each contains the job ID (<code>id</code>), destination name
-(<code>dest</code>), title (<code>title</code>), and other information
-associated with the job. The job array is freed using the
-<a href='#cupsFreeJobs'><code>cupsFreeJobs</code></a> function. The following
-example monitors a specific job ID, showing the current job state once every
-5 seconds until the job is completed:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int job_id;
-int num_jobs;
-<a href='#cups_job_t'>cups_job_t</a> *jobs;
-int i;
-ipp_jstate_t job_state = IPP_JOB_PENDING;
-
-while (job_state &lt; IPP_JOB_STOPPED)
-{
-  /* Get my jobs (1) with any state (-1) */
-  num_jobs = <a href='#cupsGetJobs'>cupsGetJobs</a>(&amp;jobs, dest->name, 1, -1);
-
-  /* Loop to find my job */
-  job_state = IPP_JOB_COMPLETED;
-
-  for (i = 0; i &lt; num_jobs; i ++)
-    if (jobs[i].id == job_id)
-    {
-      job_state = jobs[i].state;
-      break;
-    }
-
-  /* Free the job array */
-  <a href='#cupsFreeJobs'>cupsFreeJobs</a>(num_jobs, jobs);
-
-  /* Show the current state */
-  switch (job_state)
-  {
-    case IPP_JOB_PENDING :
-        printf("Job %d is pending.\n", job_id);
-        break;
-    case IPP_JOB_HELD :
-        printf("Job %d is held.\n", job_id);
-        break;
-    case IPP_JOB_PROCESSING :
-        printf("Job %d is processing.\n", job_id);
-        break;
-    case IPP_JOB_STOPPED :
-        printf("Job %d is stopped.\n", job_id);
-        break;
-    case IPP_JOB_CANCELED :
-        printf("Job %d is canceled.\n", job_id);
-        break;
-    case IPP_JOB_ABORTED :
-        printf("Job %d is aborted.\n", job_id);
-        break;
-    case IPP_JOB_COMPLETED :
-        printf("Job %d is completed.\n", job_id);
-        break;
-  }
-
-  /* Sleep if the job is not finished */
-  if (job_state &lt; IPP_JOB_STOPPED)
-    sleep(5);
-}
-</pre>
-
-<p>To cancel a job, use the
-<a href='#cupsCancelJob'><code>cupsCancelJob</code></a> function with the
-job ID:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int job_id;
-
-<a href='#cupsCancelJob'>cupsCancelJob</a>(dest->name, job_id);
-</pre>
-
-<h3><a name='ERROR_HANDLING'>Error Handling</a></h3>
-
-<p>If any of the CUPS API printing functions returns an error, the reason for
-that error can be found by calling the
-<a href='#cupsLastError'><code>cupsLastError</code></a> and
-<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> functions.
-<a href='#cupsLastError'><code>cupsLastError</code></a> returns the last IPP
-error code
-(<a href='api-httpipp.html#ipp_status_t'><code>ipp_status_t</code></a>)
-that was encountered, while
-<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> returns
-a (localized) human-readable string that can be shown to the user. For example,
-if any of the job creation functions returns a job ID of 0, you can use
-<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> to show
-the reason why the job could not be created:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-int job_id;
-
-if (job_id == 0)
-  puts(cupsLastErrorString());
-</pre>
-
-<h3><a name='PASSWORDS_AND_AUTHENTICATION'>Passwords and Authentication</a></h3>
-
-<p>CUPS supports authentication of any request, including submission of print
-jobs. The default mechanism for getting the username and password is to use the
-login user and a password from the console.</p>
-
-<p>To support other types of applications, in particular Graphical User
-Interfaces ("GUIs"), the CUPS API provides functions to set the default
-username and to register a callback function that returns a password string.</p>
-
-<p>The <a href="#cupsSetPasswordCB"><code>cupsSetPasswordCB</code></a>
-function is used to set a password callback in your program. Only one
-function can be used at any time.</p>
-
-<p>The <a href="#cupsSetUser"><code>cupsSetUser</code></a> function sets the
-current username for authentication. This function can be called by your
-password callback function to change the current username as needed.</p>
-
-<p>The following example shows a simple password callback that gets a
-username and password from the user:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-const char *
-my_password_cb(const char *prompt)
-{
-  char	user[65];
-
-
-  puts(prompt);
-
-  /* Get a username from the user */
-  printf("Username: ");
-  if (fgets(user, sizeof(user), stdin) == NULL)
-    return (NULL);
-
-  /* Strip the newline from the string and set the user */
-  user[strlen(user) - 1] = '\0';
-
-  <a href='#cupsSetUser'>cupsSetUser</a>(user);
-
-  /* Use getpass() to ask for the password... */
-  return (getpass("Password: "));
-}
-
-<a href='#cupsSetPasswordCB'>cupsSetPasswordCB</a>(my_password_cb);
-</pre>
-
-<p>Similarly, a GUI could display the prompt string in a window with input
-fields for the username and password. The username should default to the
-string returned by the <a href="#cupsUser"><code>cupsUser</code></a>
-function.</p>
diff --git a/cups/api-filedir.header b/cups/api-filedir.header
deleted file mode 100644
index 87744ee..0000000
--- a/cups/api-filedir.header
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-  File and Directory API header for CUPS.
-
-  Copyright 2008-2011 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/".
--->
-
-<h1 class='title'>File and Directory APIs</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Headers</th>
-	<th>cups/file.h<br>
-	cups/dir.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html' target='_top'>Introduction to CUPS Programming</a><br>
-	Programming: <a href='api-cups.html' target='_top'>CUPS API</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-filedir.shtml b/cups/api-filedir.shtml
deleted file mode 100644
index 8fdbee6..0000000
--- a/cups/api-filedir.shtml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-  File and directory API introduction for CUPS.
-
-  Copyright 2007-2011 by Apple Inc.
-  Copyright 1997-2005 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/".
--->
-
-<h2 class='title'><a name="OVERVIEW">Overview</a></h2>
-
-<p>The CUPS file and directory APIs provide portable interfaces
-for manipulating files and listing files and directories. Unlike
-stdio <code>FILE</code> streams, the <code>cupsFile</code> functions
-allow you to open more than 256 files at any given time. They
-also manage the platform-specific details of locking, large file
-support, line endings (CR, LF, or CR LF), and reading and writing
-files using Flate ("gzip") compression. Finally, you can also
-connect, read from, and write to network connections using the
-<code>cupsFile</code> functions.</p>
-
-<p>The <code>cupsDir</code> functions manage the platform-specific
-details of directory access/listing and provide a convenient way
-to get both a list of files and the information (permissions,
-size, timestamp, etc.) for each of those files.</p>
diff --git a/cups/api-httpipp.header b/cups/api-httpipp.header
deleted file mode 100644
index cbede8f..0000000
--- a/cups/api-httpipp.header
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
-  HTTP and IPP API header for CUPS.
-
-  Copyright 2007-2016 by Apple Inc.
-  Copyright 1997-2006 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/".
--->
-
-<h1 class='title'>HTTP and IPP APIs</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Headers</th>
-	<th>cups/cups.h<br>
-	cups/http.h<br>
-	cups/ipp.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html'>Introduction to CUPS Programming</a><br>
-	Programming: <a href='api-cups.html'>CUPS API</a><br>
-	References: <a href='spec-ipp.html'>CUPS Implementation of IPP</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-httpipp.shtml b/cups/api-httpipp.shtml
deleted file mode 100644
index 33bf5ad..0000000
--- a/cups/api-httpipp.shtml
+++ /dev/null
@@ -1,315 +0,0 @@
-<!--
-  HTTP and IPP API introduction for CUPS.
-
-  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
-  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/".
--->
-
-<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
-
-<p>The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP
-protocols and CUPS scheduler. They are typically used by monitoring and
-administration programs to perform specific functions not supported by the
-high-level CUPS API functions.</p>
-
-<p>The HTTP APIs use an opaque structure called
-<a href='#http_t'><code>http_t</code></a> to manage connections to
-a particular HTTP or IPP server. The
-<a href='#httpConnectEncrypt'><code>httpConnectEncrypt</code></a> function is
-used to create an instance of this structure for a particular server.
-The constant <code>CUPS_HTTP_DEFAULT</code> can be used with all of the
-<code>cups</code> functions to refer to the default CUPS server - the functions
-create a per-thread <a href='#http_t'><code>http_t</code></a> as needed.</p>
-
-<p>The IPP APIs use two opaque structures for requests (messages sent to the CUPS scheduler) and responses (messages sent back to your application from the scheduler). The <a href='#ipp_t'><code>ipp_t</code></a> type holds a complete request or response and is allocated using the <a href='#ippNew'><code>ippNew</code></a> or <a href='#ippNewRequest'><code>ippNewRequest</code></a> functions and freed using the <a href='#ippDelete'><code>ippDelete</code></a> function.</p>
-
-<p>The second opaque structure is called <a href='#ipp_attribute_t'><code>ipp_attribute_t</code></a> and holds a single IPP attribute which consists of a group tag (<a href='#ippGetGroupTag'><code>ippGetGroupTag</code></a>), a value type tag (<a href='#ippGetValueTag'><code>ippGetValueTag</code></a>), the attribute name (<a href='#ippGetName'><code>ippGetName</code></a>), and 1 or more values (<a href='#ippGetCount'><code>ippGetCount</code></a>, <a href='#ippGetBoolean'><code>ippGetBoolean</code></a>, <a href='#ippGetCollection'><code>ippGetCollection</code></a>, <a href='#ippGetDate'><code>ippGetDate</code></a>, <a href='#ippGetInteger'><code>ippGetInteger</code></a>, <a href='#ippGetRange'><code>ippGetRange</code></a>, <a href='#ippGetResolution'><code>ippGetResolution</code></a>, and <a href='#ippGetString'><code>ippGetString</code></a>). Attributes are added to an <a href='#ipp_t'><code>ipp_t</code></a> pointer using one of the <code>ippAdd</code> functions. For example, use <a href='#ippAddString'><code>ippAddString</code></a> to add the "printer-uri" and "requesting-user-name" string attributes to a request:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-             NULL, "ipp://localhost/printers/");
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-             NULL, cupsUser());
-</pre>
-
-<p>Once you have created an IPP request, use the <code>cups</code> functions to send the request to and read the response from the server. For example, the <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function can be used for simple query operations that do not involve files:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-
-<a href='#ipp_t'>ipp_t</a> *<a name='get_jobs'>get_jobs</a>(void)
-{
-  <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
-
-  <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, "ipp://localhost/printers/");
-  <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-               NULL, cupsUser());
-
-  return (<a href='#cupsDoRequest'>cupsDoRequest</a>(CUPS_HTTP_DEFAULT, request, "/"));
-}
-</pre>
-
-<p>The <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function frees the request and returns an IPP response or <code>NULL</code> pointer if the request could not be sent to the server. Once you have a response from the server, you can either use the <a href='#ippFindAttribute'><code>ippFindAttribute</code></a> and <a href='#ippFindNextAttribute'><code>ippFindNextAttribute</code></a> functions to find specific attributes, for example:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *response;
-<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
-
-attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
-</pre>
-
-<p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *response;
-<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
-
-for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
-  if (ippGetName(attr) == NULL)
-    puts("--SEPARATOR--");
-  else
-    puts(ippGetName(attr));
-</pre>
-
-<p>The <code>for</code> loop approach is normally used when collecting attributes for multiple objects (jobs, printers, etc.) in a response. Attributes with <code>NULL</code> names indicate a separator between the attributes of each object. For example, the following code will list the jobs returned from our previous <a href='#get_jobs'><code>get_jobs</code></a> example code:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
-
-if (response != NULL)
-{
-  <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
-  const char *attrname;
-  int job_id = 0;
-  const char *job_name = NULL;
-  const char *job_originating_user_name = NULL;
-
-  puts("Job ID  Owner             Title");
-  puts("------  ----------------  ---------------------------------");
-
-  for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
-  {
-   /* Attributes without names are separators between jobs */
-    attrname = ippGetName(attr);
-    if (attrname == NULL)
-    {
-      if (job_id > 0)
-      {
-        if (job_name == NULL)
-          job_name = "(withheld)";
-
-        if (job_originating_user_name == NULL)
-          job_originating_user_name = "(withheld)";
-
-        printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
-      }
-
-      job_id = 0;
-      job_name = NULL;
-      job_originating_user_name = NULL;
-      continue;
-    }
-    else if (!strcmp(attrname, "job-id") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_INTEGER)
-      job_id = ippGetInteger(attr, 0);
-    else if (!strcmp(attrname, "job-name") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_NAME)
-      job_name = ippGetString(attr, 0, NULL);
-    else if (!strcmp(attrname, "job-originating-user-name") &amp;&amp;
-             ippGetValueTag(attr) == IPP_TAG_NAME)
-      job_originating_user_name = ippGetString(attr, 0, NULL);
-  }
-
-  if (job_id > 0)
-  {
-    if (job_name == NULL)
-      job_name = "(withheld)";
-
-    if (job_originating_user_name == NULL)
-      job_originating_user_name = "(withheld)";
-
-    printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
-  }
-}
-</pre>
-
-<h3><a name='CREATING_URI_STRINGS'>Creating URI Strings</a></h3>
-
-<p>To ensure proper encoding, the
-<a href='#httpAssembleURIf'><code>httpAssembleURIf</code></a> function must be
-used to format a "printer-uri" string for all printer-based requests:</p>
-
-<pre class='example'>
-const char *name = "Foo";
-char uri[1024];
-<a href='#ipp_t'>ipp_t</a> *request;
-
-<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
-                 ippPort(), "/printers/%s", name);
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
-</pre>
-
-<h3><a name='SENDING_REQUESTS_WITH_FILES'>Sending Requests with Files</a></h3>
-
-<p>The <a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> and
-<a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> functions are
-used for requests involving files. The
-<a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> function
-attaches the named file to a request and is typically used when sending a print
-file or changing a printer's PPD file:</p>
-
-<pre class='example'>
-const char *filename = "/usr/share/cups/data/testprint.ps";
-const char *name = "Foo";
-char uri[1024];
-char resource[1024];
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_PRINT_JOB);
-<a href='#ipp_t'>ipp_t</a> *response;
-
-/* Use httpAssembleURIf for the printer-uri string */
-<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
-                 ippPort(), "/printers/%s", name);
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-             NULL, cupsUser());
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
-             NULL, "testprint.ps");
-
-/* Use snprintf for the resource path */
-snprintf(resource, sizeof(resource), "/printers/%s", name);
-
-response = <a href='#cupsDoFileRequest'>cupsDoFileRequest</a>(CUPS_HTTP_DEFAULT, request, resource, filename);
-</pre>
-
-<p>The <a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> function
-optionally attaches a file to the request and optionally saves a file in the
-response from the server. It is used when using a pipe for the request
-attachment or when using a request that returns a file, currently only
-<code>CUPS_GET_DOCUMENT</code> and <code>CUPS_GET_PPD</code>. For example,
-the following code will download the PPD file for the sample HP LaserJet
-printer driver:</p>
-
-<pre class='example'>
-char tempfile[1024];
-int tempfd;
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
-<a href='#ipp_t'>ipp_t</a> *response;
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
-             NULL, "laserjet.ppd");
-
-tempfd = cupsTempFd(tempfile, sizeof(tempfile));
-
-response = <a href='#cupsDoIORequest'>cupsDoIORequest</a>(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
-</pre>
-
-<p>The example passes <code>-1</code> for the input file descriptor to specify
-that no file is to be attached to the request. The PPD file attached to the
-response is written to the temporary file descriptor we created using the
-<code>cupsTempFd</code> function.</p>
-
-<h3><a name='ASYNCHRONOUS_REQUEST_PROCESSING'>Asynchronous Request Processing</a></h3>
-
-<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> and
-<a href='#cupsGetResponse'><code>cupsGetResponse</code></a> support
-asynchronous communications with the server. Unlike the other request
-functions, the IPP request is not automatically freed, so remember to
-free your request with the <a href='#ippDelete'><code>ippDelete</code></a>
-function.</p>
-
-<p>File data is attached to the request using the
-<a href='#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>
-function, while file data returned from the server is read using the
-<a href='#cupsReadResponseData'><code>cupsReadResponseData</code></a>
-function. We can rewrite the previous <code>CUPS_GET_PPD</code> example
-to use the asynchronous functions quite easily:</p>
-
-<pre class='example'>
-char tempfile[1024];
-int tempfd;
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
-<a href='#ipp_t'>ipp_t</a> *response;
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
-             NULL, "laserjet.ppd");
-
-tempfd = cupsTempFd(tempfile, sizeof(tempfile));
-
-if (<a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
-{
-  response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
-
-  if (response != NULL)
-  {
-    ssize_t bytes;
-    char buffer[8192];
-
-    while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
-      write(tempfd, buffer, bytes);
-  }
-}
-
-/* Free the request! */
-<a href='#ippDelete'>ippDelete</a>(request);
-</pre>
-
-<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> function
-returns the initial HTTP request status, typically either
-<code>HTTP_CONTINUE</code> or <code>HTTP_UNAUTHORIZED</code>. The latter status
-is returned when the request requires authentication of some sort. The
-<a href='#cupsDoAuthentication'><code>cupsDoAuthentication</code></a> function
-must be called when your see <code>HTTP_UNAUTHORIZED</code> and the request
-re-sent. We can add authentication support to our example code by using a
-<code>do ... while</code> loop:</p>
-
-<pre class='example'>
-char tempfile[1024];
-int tempfd;
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
-<a href='#ipp_t'>ipp_t</a> *response;
-http_status_t status;
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
-             NULL, "laserjet.ppd");
-
-tempfd = cupsTempFd(tempfile, sizeof(tempfile));
-
-/* Loop for authentication */
-do
-{
-  status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
-
-  if (status == HTTP_UNAUTHORIZED)
-  {
-    /* Try to authenticate, break out of the loop if that fails */
-    if (<a href='#cupsDoAuthentication'>cupsDoAuthentication</a>(CUPS_HTTP_DEFAULT, "POST", "/"))
-      break;
-  }
-}
-while (status != HTTP_CONTINUE &amp;&amp; status != HTTP_UNAUTHORIZED);
-
-if (status == HTTP_CONTINUE)
-{
-  response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
-
-  if (response != NULL)
-  {
-    ssize_t bytes;
-    char buffer[8192];
-
-    while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
-      write(tempfd, buffer, bytes);
-  }
-}
-
-/* Free the request! */
-<a href='#ippDelete'>ippDelete</a>(request);
-</pre>
diff --git a/cups/auth.c b/cups/auth.c
index 8348a2c..c051c86 100644
--- a/cups/auth.c
+++ b/cups/auth.c
@@ -54,7 +54,7 @@
 #    else
 #      define GSS_AUTH_IDENTITY_TYPE_1 1
 #      define gss_acquire_cred_ex_f __ApplePrivate_gss_acquire_cred_ex_f
-typedef struct gss_auth_identity
+typedef struct gss_auth_identity /* @private@ */
 {
   uint32_t type;
   uint32_t flags;
diff --git a/cups/cups-private.h b/cups/cups-private.h
index 264fd01..6fd66a9 100644
--- a/cups/cups-private.h
+++ b/cups/cups-private.h
@@ -1,7 +1,7 @@
 /*
  * Private definitions for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -105,7 +105,7 @@
   int			need_res_init;	/* Need to reinitialize resolver? */
 
   /* ipp.c */
-  ipp_uchar_t		ipp_date[11];	/* RFC-1903 date/time data */
+  ipp_uchar_t		ipp_date[11];	/* RFC-2579 date/time data */
   _cups_buffer_t	*cups_buffers;	/* Buffer list */
 
   /* ipp-support.c */
diff --git a/cups/cups.h b/cups/cups.h
index 3205dd8..8f5c818 100644
--- a/cups/cups.h
+++ b/cups/cups.h
@@ -47,10 +47,10 @@
  * Constants...
  */
 
-#  define CUPS_VERSION			2.0203
+#  define CUPS_VERSION			2.0206
 #  define CUPS_VERSION_MAJOR		2
 #  define CUPS_VERSION_MINOR		2
-#  define CUPS_VERSION_PATCH		3
+#  define CUPS_VERSION_PATCH		6
 
 #  define CUPS_BC_FD			3
 					/* Back-channel file descriptor for
@@ -78,7 +78,7 @@
 #  define CUPS_DEST_FLAGS_NONE		0x00
 					/* No flags are set */
 #  define CUPS_DEST_FLAGS_UNCONNECTED	0x01
-					/* There is not connection */
+					/* There is no connection */
 #  define CUPS_DEST_FLAGS_MORE		0x02
 					/* There are more destinations */
 #  define CUPS_DEST_FLAGS_REMOVED	0x04
@@ -92,6 +92,8 @@
 					/* A connection is being established */
 #  define CUPS_DEST_FLAGS_CANCELED	0x40
 					/* Operation was canceled */
+#  define CUPS_DEST_FLAGS_DEVICE        0x80
+                                        /* For @link cupsConnectDest@: Connect to device */
 
 /* Flags for cupsGetDestMediaByName/Size */
 #  define CUPS_MEDIA_FLAGS_DEFAULT 	0x00
@@ -207,38 +209,37 @@
   CUPS_PRINTER_REMOTE = 0x0002,		/* Remote printer or class */
   CUPS_PRINTER_BW = 0x0004,		/* Can do B&W printing */
   CUPS_PRINTER_COLOR = 0x0008,		/* Can do color printing */
-  CUPS_PRINTER_DUPLEX = 0x0010,		/* Can do duplexing */
+  CUPS_PRINTER_DUPLEX = 0x0010,		/* Can do two-sided printing */
   CUPS_PRINTER_STAPLE = 0x0020,		/* Can staple output */
-  CUPS_PRINTER_COPIES = 0x0040,		/* Can do copies */
-  CUPS_PRINTER_COLLATE = 0x0080,	/* Can collage copies */
+  CUPS_PRINTER_COPIES = 0x0040,		/* Can do copies in hardware */
+  CUPS_PRINTER_COLLATE = 0x0080,	/* Can quickly collate copies */
   CUPS_PRINTER_PUNCH = 0x0100,		/* Can punch output */
   CUPS_PRINTER_COVER = 0x0200,		/* Can cover output */
   CUPS_PRINTER_BIND = 0x0400,		/* Can bind output */
   CUPS_PRINTER_SORT = 0x0800,		/* Can sort output */
-  CUPS_PRINTER_SMALL = 0x1000,		/* Can do Letter/Legal/A4 */
-  CUPS_PRINTER_MEDIUM = 0x2000,		/* Can do Tabloid/B/C/A3/A2 */
-  CUPS_PRINTER_LARGE = 0x4000,		/* Can do D/E/A1/A0 */
-  CUPS_PRINTER_VARIABLE = 0x8000,	/* Can do variable sizes */
+  CUPS_PRINTER_SMALL = 0x1000,		/* Can print on Letter/Legal/A4-size media */
+  CUPS_PRINTER_MEDIUM = 0x2000,		/* Can print on Tabloid/B/C/A3/A2-size media */
+  CUPS_PRINTER_LARGE = 0x4000,		/* Can print on D/E/A1/A0-size media */
+  CUPS_PRINTER_VARIABLE = 0x8000,	/* Can print on rolls and custom-size media */
   CUPS_PRINTER_IMPLICIT = 0x10000,	/* Implicit class @private@
 					 * @since Deprecated@ */
   CUPS_PRINTER_DEFAULT = 0x20000,	/* Default printer on network */
   CUPS_PRINTER_FAX = 0x40000,		/* Fax queue */
   CUPS_PRINTER_REJECTING = 0x80000,	/* Printer is rejecting jobs */
   CUPS_PRINTER_DELETE = 0x100000,	/* Delete printer
-					 * @since CUPS 1.2/macOS 10.5@ */
+					 * @deprecated@ @exclude all@ */
   CUPS_PRINTER_NOT_SHARED = 0x200000,	/* Printer is not shared
 					 * @since CUPS 1.2/macOS 10.5@ */
   CUPS_PRINTER_AUTHENTICATED = 0x400000,/* Printer requires authentication
 					 * @since CUPS 1.2/macOS 10.5@ */
   CUPS_PRINTER_COMMANDS = 0x800000,	/* Printer supports maintenance commands
 					 * @since CUPS 1.2/macOS 10.5@ */
-  CUPS_PRINTER_DISCOVERED = 0x1000000,	/* Printer was automatically discovered
-					 * and added @private@ */
+  CUPS_PRINTER_DISCOVERED = 0x1000000,	/* Printer was discovered @since CUPS 1.2/macOS 10.5@ */
   CUPS_PRINTER_SCANNER = 0x2000000,	/* Scanner-only device
-					 * @since CUPS 1.4/macOS 10.6@ */
+					 * @since CUPS 1.4/macOS 10.6@ @private@ */
   CUPS_PRINTER_MFP = 0x4000000,		/* Printer with scanning capabilities
-					 * @since CUPS 1.4/macOS 10.6@ */
-  CUPS_PRINTER_3D = 0x8000000,		/* Printer with 3D capabilities @private@ */
+					 * @since CUPS 1.4/macOS 10.6@ @private@ */
+  CUPS_PRINTER_3D = 0x8000000,		/* Printer with 3D capabilities @exclude all@ @private@ */
   CUPS_PRINTER_OPTIONS = 0x6fffc	/* ~(CLASS | REMOTE | IMPLICIT |
 					 * DEFAULT | FAX | REJECTING | DELETE |
 					 * NOT_SHARED | AUTHENTICATED |
@@ -269,7 +270,7 @@
   int		id;			/* The job ID */
   char		*dest;			/* Printer or class name */
   char		*title;			/* Title/job name */
-  char		*user;			/* User the submitted the job */
+  char		*user;			/* User that submitted the job */
   char		*format;		/* Document format */
   ipp_jstate_t	state;			/* Job state */
   int		size;			/* Size in kilobytes */
@@ -309,11 +310,12 @@
 #  ifdef __BLOCKS__
 typedef int (^cups_dest_block_t)(unsigned flags, cups_dest_t *dest);
 			      		/* Destination enumeration block
-					 * @since CUPS 1.6/macOS 10.8@ */
+					 * @since CUPS 1.6/macOS 10.8@
+                                         * @exclude all@ */
 #  endif /* __BLOCKS__ */
 
 typedef const char *(*cups_password_cb_t)(const char *prompt);
-					/* Password callback */
+					/* Password callback @exclude all@ */
 
 typedef const char *(*cups_password_cb2_t)(const char *prompt, http_t *http,
 					   const char *method,
@@ -340,11 +342,11 @@
 			               const char *resource);
 extern http_encryption_t cupsEncryption(void);
 extern void		cupsFreeJobs(int num_jobs, cups_job_t *jobs);
-extern int		cupsGetClasses(char ***classes) _CUPS_DEPRECATED_MSG("Use cupsGetDests instead.");
+extern int		cupsGetClasses(char ***classes) _CUPS_DEPRECATED_MSG("Use cupsEnumDests instead.");
 extern const char	*cupsGetDefault(void);
 extern int		cupsGetJobs(cups_job_t **jobs, const char *name,
 			            int myjobs, int whichjobs);
-extern int		cupsGetPrinters(char ***printers) _CUPS_DEPRECATED_MSG("Use cupsGetDests instead.");
+extern int		cupsGetPrinters(char ***printers) _CUPS_DEPRECATED_MSG("Use cupsEnumDests instead.");
 extern ipp_status_t	cupsLastError(void);
 extern int		cupsPrintFile(const char *name, const char *filename,
 			              const char *title, int num_options,
@@ -600,6 +602,10 @@
 /* New in CUPS 2.2/macOS 10.12 */
 extern ssize_t		cupsHashData(const char *algorithm, const void *data, size_t datalen, unsigned char *hash, size_t hashsize) _CUPS_API_2_2;
 
+/* New in CUPS 2.2.4 */
+extern int		cupsAddIntegerOption(const char *name, int value, int num_options, cups_option_t **options) _CUPS_API_2_2_4;
+extern int		cupsGetIntegerOption(const char *name, int num_options, cups_option_t *options) _CUPS_API_2_2_4;
+
 #  ifdef __cplusplus
 }
 #  endif /* __cplusplus */
diff --git a/cups/cupspm-icon.png b/cups/cupspm-icon.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cups/cupspm-icon.png
diff --git a/cups/cupspm.md b/cups/cupspm.md
new file mode 100644
index 0000000..cb381ac
--- /dev/null
+++ b/cups/cupspm.md
@@ -0,0 +1,994 @@
+---
+title: CUPS Programming Manual
+author: Michael R Sweet
+copyright: Copyright © 2007-2017 by Apple Inc. All Rights Reserved.
+version: 2.2.5
+...
+
+> Please [file issues on Github](https://github.com/apple/cups/issues) to
+> provide feedback on this document.
+
+
+# Introduction
+
+CUPS provides the "cups" library to talk to the different parts of CUPS and with
+Internet Printing Protocol (IPP) printers. The "cups" library functions are
+accessed by including the `<cups/cups.h>` header.
+
+CUPS is based on the Internet Printing Protocol ("IPP"), which allows clients
+(applications) to communicate with a server (the scheduler, printers, etc.) to
+get a list of destinations, send print jobs, and so forth.  You identify which
+server you want to communicate with using a pointer to the opaque structure
+`http_t`.  The `CUPS_HTTP_DEFAULT` constant can be used when you want to talk to
+the CUPS scheduler.
+
+
+## Guidelines
+
+When writing software that uses the "cups" library:
+
+- Do not use undocumented or deprecated APIs,
+- Do not rely on pre-configured printers,
+- Do not assume that printers support specific features or formats, and
+- Do not rely on implementation details (PPDs, etc.)
+
+CUPS is designed to insulate users and developers from the implementation
+details of printers and file formats.  The goal is to allow an application to
+supply a print file in a standard format with the user intent ("print four
+copies, two-sided on A4 media, and staple each copy") and have the printing
+system manage the printer communication and format conversion needed.
+
+Similarly, printer and job management applications can use standard query
+operations to obtain the status information in a common, generic form and use
+standard management operations to control the state of those printers and jobs.
+
+
+## Terms Used in This Document
+
+A *Destination* is a printer or print queue that accepts print jobs.  A
+*Print Job* is one or more documents that are processed by a destination
+using options supplied when creating the job.  A *Document* is a file (JPEG
+image, PDF file, etc.) suitable for printing.  An *Option* controls some aspect
+of printing, such as the media used. *Media* is the sheets or roll that is
+printed on.  An *Attribute* is an option encoded for an Internet Printing
+Protocol (IPP) request.
+
+
+## Compiling Programs That Use the CUPS API
+
+The CUPS libraries can be used from any C, C++, or Objective C program.
+The method of compiling against the libraries varies depending on the
+operating system and installation of CUPS. The following sections show how
+to compile a simple program (shown below) in two common environments.
+
+The following simple program lists the available destinations:
+
+    #include <stdio.h>
+    #include <cups/cups.h>
+
+    int print_dest(void *user_data, unsigned flags, cups_dest_t *dest)
+    {
+      if (dest->instance)
+        printf("%s/%s\n", dest->name, dest->instance);
+      else
+        puts(dest->name);
+
+      return (1);
+    }
+
+    int main(void)
+    {
+      cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, print_dest, NULL);
+
+      return (0);
+    }
+
+
+### Compiling with Xcode
+
+In Xcode, choose *New Project...* from the *File* menu (or press SHIFT+CMD+N),
+then select the *Command Line Tool* under the macOS Application project type.
+Click *Next* and enter a name for the project, for example "firstcups".  Click
+*Next* and choose a project directory. The click *Next* to create the project.
+
+In the project window, click on the *Build Phases* group and expand the
+*Link Binary with Libraries* section. Click *+*, type "libcups" to show the
+library, and then double-click on `libcups.tbd`.
+
+Finally, click on the `main.c` file in the sidebar and copy the example program
+to the file.  Build and run (CMD+R) to see the list of destinations.
+
+
+### Compiling with GCC
+
+From the command-line, create a file called `sample.c` using your favorite
+editor, copy the example to this file, and save.  Then run the following command
+to compile it with GCC and run it:
+
+    gcc -o simple `cups-config --cflags` simple.c `cups-config --libs`
+    ./simple
+
+The `cups-config` command provides the compiler flags (`cups-config --cflags`)
+and libraries (`cups-config --libs`) needed for the local system.
+
+
+# Working with Destinations
+
+Destinations, which in CUPS represent individual printers or classes
+(collections or pools) of printers, are represented by the `cups_dest_t`
+structure which includes the name \(`name`), instance \(`instance`, saved
+options/settings), whether the destination is the default for the user
+\(`is_default`), and the options and basic information associated with that
+destination \(`num_options` and `options`).
+
+Historically destinations have been manually maintained by the administrator of
+a system or network, but CUPS also supports dynamic discovery of destinations on
+the current network.
+
+
+## Finding Available Destinations
+
+The `cupsEnumDests` function finds all of the available destinations:
+
+     int
+     cupsEnumDests(unsigned flags, int msec, int *cancel,
+                   cups_ptype_t type, cups_ptype_t mask,
+                   cups_dest_cb_t cb, void *user_data)
+
+The `flags` argument specifies enumeration options, which at present must be
+`CUPS_DEST_FLAGS_NONE`.
+
+The `msec` argument specifies the maximum amount of time that should be used for
+enumeration in milliseconds - interactive applications should keep this value to
+5000 or less when run on the main thread.
+
+The `cancel` argument points to an integer variable that, when set to a non-zero
+value, will cause enumeration to stop as soon as possible.  It can be `NULL` if
+not needed.
+
+The `type` and `mask` arguments are bitfields that allow the caller to filter
+the destinations based on categories and/or capabilities.  The destination's
+"printer-type" value is masked by the `mask` value and compared to the `type`
+value when filtering.  For example, to only enumerate destinations that are
+hosted on the local system, pass `CUPS_PRINTER_LOCAL` for the `type` argument
+and `CUPS_PRINTER_DISCOVERED` for the `mask` argument.  The following constants
+can be used for filtering:
+
+- `CUPS_PRINTER_CLASS`: A collection of destinations.
+- `CUPS_PRINTER_FAX`: A facsimile device.
+- `CUPS_PRINTER_LOCAL`: A local printer or class.  This constant has the value 0
+  (no bits set) and is only used for the `type` argument and is paired with the
+  `CUPS_PRINTER_REMOTE` or `CUPS_PRINTER_DISCOVERED` constant passed in the
+  `mask` argument.
+- `CUPS_PRINTER_REMOTE`: A remote (shared) printer or class.
+- `CUPS_PRINTER_DISCOVERED`: An available network printer or class.
+- `CUPS_PRINTER_BW`: Can do B&W printing.
+- `CUPS_PRINTER_COLOR`: Can do color printing.
+- `CUPS_PRINTER_DUPLEX`: Can do two-sided printing.
+- `CUPS_PRINTER_STAPLE`: Can staple output.
+- `CUPS_PRINTER_COLLATE`: Can quickly collate copies.
+- `CUPS_PRINTER_PUNCH`: Can punch output.
+- `CUPS_PRINTER_COVER`: Can cover output.
+- `CUPS_PRINTER_BIND`: Can bind output.
+- `CUPS_PRINTER_SORT`: Can sort output (mailboxes, etc.)
+- `CUPS_PRINTER_SMALL`: Can print on Letter/Legal/A4-size media.
+- `CUPS_PRINTER_MEDIUM`: Can print on Tabloid/B/C/A3/A2-size media.
+- `CUPS_PRINTER_LARGE`: Can print on D/E/A1/A0-size media.
+- `CUPS_PRINTER_VARIABLE`: Can print on rolls and custom-size media.
+
+The `cb` argument specifies a function to call for every destination that is
+found:
+
+    typedef int (*cups_dest_cb_t)(void *user_data,
+                                  unsigned flags,
+                                  cups_dest_t *dest);
+
+The callback function receives a copy of the `user_data` argument along with a
+bitfield \(`flags`) and the destination that was found.  The `flags` argument
+can have any of the following constant (bit) values set:
+
+- `CUPS_DEST_FLAGS_MORE`: There are more destinations coming.
+- `CUPS_DEST_FLAGS_REMOVED`: The destination has gone away and should be removed
+  from the list of destinations a user can select.
+- `CUPS_DEST_FLAGS_ERROR`: An error occurred.  The reason for the error can be
+  found by calling the `cupsLastError` and/or `cupsLastErrorString` functions.
+
+The callback function returns 0 to stop enumeration or 1 to continue.
+
+> Note that the callback function will likely be called multiple times for the
+> same destination, so it is up to the caller to suppress any duplicate
+> destinations.
+
+The following example shows how to use `cupsEnumDests` to get a filtered array
+of destinations:
+
+    typedef struct
+    {
+      int num_dests;
+      cups_dest_t *dests;
+    } my_user_data_t;
+
+    int
+    my_dest_cb(my_user_data_t *user_data, unsigned flags,
+               cups_dest_t *dest)
+    {
+      if (flags & CUPS_DEST_FLAGS_REMOVED)
+      {
+       /*
+        * Remove destination from array...
+        */
+
+        user_data->num_dests =
+            cupsRemoveDest(dest->name, dest->instance,
+                           user_data->num_dests,
+                           &(user_data->dests));
+      }
+      else
+      {
+       /*
+        * Add destination to array...
+        */
+
+        user_data->num_dests =
+            cupsCopyDest(dest, user_data->num_dests,
+                         &(user_data->dests));
+      }
+
+      return (1);
+    }
+
+    int
+    my_get_dests(cups_ptype_t type, cups_ptype_t mask,
+                 cups_dest_t **dests)
+    {
+      my_user_data_t user_data = { 0, NULL };
+
+      if (!cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, type,
+                         mask, (cups_dest_cb_t)my_dest_cb,
+                         &user_data))
+      {
+       /*
+        * An error occurred, free all of the destinations and
+        * return...
+        */
+
+        cupsFreeDests(user_data.num_dests, user_dasta.dests);
+
+        *dests = NULL;
+
+        return (0);
+      }
+
+     /*
+      * Return the destination array...
+      */
+
+      *dests = user_data.dests;
+
+      return (user_data.num_dests);
+    }
+
+
+## Basic Destination Information
+
+The `num_options` and `options` members of the `cups_dest_t` structure provide
+basic attributes about the destination in addition to the user default options
+and values for that destination.  The following names are predefined for various
+destination attributes:
+
+- "auth-info-required": The type of authentication required for printing to this
+  destination: "none", "username,password", "domain,username,password", or
+  "negotiate" (Kerberos).
+- "printer-info": The human-readable description of the destination such as "My
+  Laser Printer".
+- "printer-is-accepting-jobs": "true" if the destination is accepting new jobs,
+  "false" otherwise.
+- "printer-is-shared": "true" if the destination is being shared with other
+  computers, "false" otherwise.
+- "printer-location": The human-readable location of the destination such as
+  "Lab 4".
+- "printer-make-and-model": The human-readable make and model of the destination
+  such as "ExampleCorp LaserPrinter 4000 Series".
+- "printer-state": "3" if the destination is idle, "4" if the destination is
+  printing a job, and "5" if the destination is stopped.
+- "printer-state-change-time": The UNIX time when the destination entered the
+  current state.
+- "printer-state-reasons": Additional comma-delimited state keywords for the
+  destination such as "media-tray-empty-error" and "toner-low-warning".
+- "printer-type": The `cups_ptype_t` value associated with the destination.
+- "printer-uri-supported": The URI associated with the destination; if not set,
+  this destination was discovered but is not yet setup as a local printer.
+
+Use the `cupsGetOption` function to retrieve the value.  For example, the
+following code gets the make and model of a destination:
+
+    const char *model = cupsGetOption("printer-make-and-model",
+                                      dest->num_options,
+                                      dest->options);
+
+
+## Detailed Destination Information
+
+Once a destination has been chosen, the `cupsCopyDestInfo` function can be used
+to gather detailed information about the destination:
+
+    cups_dinfo_t *
+    cupsCopyDestInfo(http_t *http, cups_dest_t *dest);
+
+The `http` argument specifies a connection to the CUPS scheduler and is
+typically the constant `CUPS_HTTP_DEFAULT`.  The `dest` argument specifies the
+destination to query.
+
+The `cups_dinfo_t` structure that is returned contains a snapshot of the
+supported options and their supported, ready, and default values.  It also can
+report constraints between different options and values, and recommend changes
+to resolve those constraints.
+
+
+### Getting Supported Options and Values
+
+The `cupsCheckDestSupported` function can be used to test whether a particular
+option or option and value is supported:
+
+    int
+    cupsCheckDestSupported(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *info,
+                           const char *option,
+                           const char *value);
+
+The `option` argument specifies the name of the option to check.  The following
+constants can be used to check the various standard options:
+
+- `CUPS_COPIES`: Controls the number of copies that are produced.
+- `CUPS_FINISHINGS`: A comma-delimited list of integer constants that control
+  the finishing processes that are applied to the job, including stapling,
+  punching, and folding.
+- `CUPS_MEDIA`: Controls the media size that is used, typically one of the
+  following: `CUPS_MEDIA_3X5`, `CUPS_MEDIA_4X6`, `CUPS_MEDIA_5X7`,
+  `CUPS_MEDIA_8X10`, `CUPS_MEDIA_A3`, `CUPS_MEDIA_A4`, `CUPS_MEDIA_A5`,
+  `CUPS_MEDIA_A6`, `CUPS_MEDIA_ENV10`, `CUPS_MEDIA_ENVDL`, `CUPS_MEDIA_LEGAL`,
+  `CUPS_MEDIA_LETTER`, `CUPS_MEDIA_PHOTO_L`, `CUPS_MEDIA_SUPERBA3`, or
+  `CUPS_MEDIA_TABLOID`.
+- `CUPS_MEDIA_SOURCE`: Controls where the media is pulled from, typically either
+  `CUPS_MEDIA_SOURCE_AUTO` or `CUPS_MEDIA_SOURCE_MANUAL`.
+- `CUPS_MEDIA_TYPE`: Controls the type of media that is used, typically one of
+  the following: `CUPS_MEDIA_TYPE_AUTO`, `CUPS_MEDIA_TYPE_ENVELOPE`,
+  `CUPS_MEDIA_TYPE_LABELS`, `CUPS_MEDIA_TYPE_LETTERHEAD`,
+  `CUPS_MEDIA_TYPE_PHOTO`, `CUPS_MEDIA_TYPE_PHOTO_GLOSSY`,
+  `CUPS_MEDIA_TYPE_PHOTO_MATTE`, `CUPS_MEDIA_TYPE_PLAIN`, or
+  `CUPS_MEDIA_TYPE_TRANSPARENCY`.
+- `CUPS_NUMBER_UP`: Controls the number of document pages that are placed on
+  each media side.
+- `CUPS_ORIENTATION`: Controls the orientation of document pages placed on the
+  media: `CUPS_ORIENTATION_PORTRAIT` or `CUPS_ORIENTATION_LANDSCAPE`.
+- `CUPS_PRINT_COLOR_MODE`: Controls whether the output is in color
+  \(`CUPS_PRINT_COLOR_MODE_COLOR`), grayscale
+  \(`CUPS_PRINT_COLOR_MODE_MONOCHROME`), or either
+  \(`CUPS_PRINT_COLOR_MODE_AUTO`).
+- `CUPS_PRINT_QUALITY`: Controls the generate quality of the output:
+  `CUPS_PRINT_QUALITY_DRAFT`, `CUPS_PRINT_QUALITY_NORMAL`, or
+  `CUPS_PRINT_QUALITY_HIGH`.
+- `CUPS_SIDES`: Controls whether prints are placed on one or both sides of the
+  media: `CUPS_SIDES_ONE_SIDED`, `CUPS_SIDES_TWO_SIDED_PORTRAIT`, or
+  `CUPS_SIDES_TWO_SIDED_LANDSCAPE`.
+
+If the `value` argument is `NULL`, the `cupsCheckDestSupported` function returns
+whether the option is supported by the destination.  Otherwise, the function
+returns whether the specified value of the option is supported.
+
+The `cupsFindDestSupported` function returns the IPP attribute containing the
+supported values for a given option:
+
+     ipp_attribute_t *
+     cupsFindDestSupported(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *dinfo,
+                           const char *option);
+
+For example, the following code prints the supported finishing processes for a
+destination, if any, to the standard output:
+
+    cups_dinfo_t *info = cupsCopyDestInfo(CUPS_HTTP_DEFAULT,
+                                          dest);
+
+    if (cupsCheckDestSupported(CUPS_HTTP_DEFAULT, dest, info,
+                               CUPS_FINISHINGS, NULL))
+    {
+      ipp_attribute_t *finishings =
+          cupsFindDestSupported(CUPS_HTTP_DEFAULT, dest, info,
+                                CUPS_FINISHINGS);
+      int i, count = ippGetCount(finishings);
+
+      puts("finishings supported:");
+      for (i = 0; i < count; i ++)
+        printf("  %d\n", ippGetInteger(finishings, i));
+    }
+    else
+      puts("finishings not supported.");
+
+The "job-creation-attributes" option can be queried to get a list of supported
+options.  For example, the following code prints the list of supported options
+to the standard output:
+
+    ipp_attribute_t *attrs =
+        cupsFindDestSupported(CUPS_HTTP_DEFAULT, dest, info,
+                              "job-creation-attributes");
+    int i, count = ippGetCount(attrs);
+
+    for (i = 0; i < count; i ++)
+      puts(ippGetString(attrs, i, NULL));
+
+
+### Getting Default Values
+
+There are two sets of default values - user defaults that are available via the
+`num_options` and `options` members of the `cups_dest_t` structure, and
+destination defaults that available via the `cups_dinfo_t` structure and the
+`cupsFindDestDefault` function which returns the IPP attribute containing the
+default value(s) for a given option:
+
+    ipp_attribute_t *
+    cupsFindDestDefault(http_t *http, cups_dest_t *dest,
+                        cups_dinfo_t *dinfo,
+                        const char *option);
+
+The user defaults from `cupsGetOption` should always take preference over the
+destination defaults.  For example, the following code prints the default
+finishings value(s) to the standard output:
+
+    const char *def_value =
+        cupsGetOption(CUPS_FINISHINGS, dest->num_options,
+                      dest->options);
+    ipp_attribute_t *def_attr =
+        cupsFindDestDefault(CUPS_HTTP_DEFAULT, dest, info,
+                            CUPS_FINISHINGS);
+
+    if (def_value != NULL)
+    {
+      printf("Default finishings: %s\n", def_value);
+    }
+    else
+    {
+      int i, count = ippGetCount(def_attr);
+
+      printf("Default finishings: %d",
+             ippGetInteger(def_attr, 0));
+      for (i = 1; i < count; i ++)
+        printf(",%d", ippGetInteger(def_attr, i));
+      putchar('\n');
+    }
+
+
+### Getting Ready (Loaded) Values
+
+The finishings and media options also support queries for the ready, or loaded,
+values.  For example, a printer may have punch and staple finishers installed
+but be out of staples - the supported values will list both punch and staple
+finishing processes but the ready values will only list the punch processes.
+Similarly, a printer may support hundreds of different sizes of media but only
+have a single size loaded at any given time - the ready values are limited to
+the media that is actually in the printer.
+
+The `cupsFindDestReady` function finds the IPP attribute containing the ready
+values for a given option:
+
+    ipp_attribute_t *
+    cupsFindDestReady(http_t *http, cups_dest_t *dest,
+                      cups_dinfo_t *dinfo, const char *option);
+
+For example, the following code lists the ready finishing processes:
+
+    ipp_attribute_t *ready_finishings =
+        cupsFindDestReady(CUPS_HTTP_DEFAULT, dest, info,
+                          CUPS_FINISHINGS);
+
+    if (ready_finishings != NULL)
+    {
+      int i, count = ippGetCount(ready_finishings);
+
+      puts("finishings ready:");
+      for (i = 0; i < count; i ++)
+        printf("  %d\n", ippGetInteger(ready_finishings, i));
+    }
+    else
+      puts("no finishings are ready.");
+
+
+### Media Size Options
+
+CUPS provides functions for querying the dimensions and margins for each of the
+supported media size options.  The `cups_size_t` structure is used to describe a
+media size:
+
+    typedef struct cups_size_s
+    {
+      char media[128];
+      int width, length;
+      int bottom, left, right, top;
+    } cups_size_t;
+
+The `width` and `length` members specify the dimensions of the media in
+hundredths of millimeters (1/2540th of an inch).  The `bottom`, `left`, `right`,
+and `top` members specify the margins of the printable area, also in hundredths
+of millimeters.
+
+The `cupsGetDestMediaByName` and `cupsGetDestMediaBySize` functions lookup the
+media size information using a standard media size name or dimensions in
+hundredths of millimeters:
+
+    int
+    cupsGetDestMediaByName(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *dinfo,
+                           const char *media,
+                           unsigned flags, cups_size_t *size);
+
+    int
+    cupsGetDestMediaBySize(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *dinfo,
+                           int width, int length,
+                           unsigned flags, cups_size_t *size);
+
+The `media`, `width`, and `length` arguments specify the size to lookup.  The
+`flags` argument specifies a bitfield controlling various lookup options:
+
+- `CUPS_MEDIA_FLAGS_DEFAULT`: Find the closest size supported by the printer.
+- `CUPS_MEDIA_FLAGS_BORDERLESS`: Find a borderless size.
+- `CUPS_MEDIA_FLAGS_DUPLEX`: Find a size compatible with two-sided printing.
+- `CUPS_MEDIA_FLAGS_EXACT`: Find an exact match for the size.
+- `CUPS_MEDIA_FLAGS_READY`: If the printer supports media sensing or
+  configuration of the media in each tray/source, find the size amongst the
+  "ready" media.
+
+If a matching size is found for the destination, the size information is stored
+in the structure pointed to by the `size` argument and 1 is returned.  Otherwise
+0 is returned.
+
+For example, the following code prints the margins for two-sided printing on US
+Letter media:
+
+    cups_size_t size;
+
+    if (cupsGetDestMediaByName(CUPS_HTTP_DEFAULT, dest, info,
+                               CUPS_MEDIA_LETTER,
+                               CUPS_MEDIA_FLAGS_DUPLEX, &size))
+    {
+      puts("Margins for duplex US Letter:");
+      printf("  Bottom: %.2fin\n", size.bottom / 2540.0);
+      printf("    Left: %.2fin\n", size.left / 2540.0);
+      printf("   Right: %.2fin\n", size.right / 2540.0);
+      printf("     Top: %.2fin\n", size.top / 2540.0);
+    }
+    else
+      puts("Margins for duplex US Letter are not available.");
+
+You can also enumerate all of the sizes that match a given `flags` value using
+the `cupsGetDestMediaByIndex` and `cupsGetDestMediaCount` functions:
+
+    int
+    cupsGetDestMediaByIndex(http_t *http, cups_dest_t *dest,
+                            cups_dinfo_t *dinfo, int n,
+                            unsigned flags, cups_size_t *size);
+
+    int
+    cupsGetDestMediaCount(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *dinfo, unsigned flags);
+
+For example, the following code prints the list of ready media and corresponding
+margins:
+
+    cups_size_t size;
+    int i;
+    int count = cupsGetDestMediaCount(CUPS_HTTP_DEFAULT,
+                                      dest, info,
+                                      CUPS_MEDIA_FLAGS_READY);
+
+    for (i = 0; i < count; i ++)
+    {
+      if (cupsGetDestMediaByIndex(CUPS_HTTP_DEFAULT, dest, info,
+                                  i, CUPS_MEDIA_FLAGS_READY,
+                                  &size))
+      {
+        printf("%s:\n", size.name);
+        printf("   Width: %.2fin\n", size.width / 2540.0);
+        printf("  Length: %.2fin\n", size.length / 2540.0);
+        printf("  Bottom: %.2fin\n", size.bottom / 2540.0);
+        printf("    Left: %.2fin\n", size.left / 2540.0);
+        printf("   Right: %.2fin\n", size.right / 2540.0);
+        printf("     Top: %.2fin\n", size.top / 2540.0);
+      }
+    }
+
+Finally, the `cupsGetDestMediaDefault` function returns the default media size:
+
+    int
+    cupsGetDestMediaDefault(http_t *http, cups_dest_t *dest,
+                            cups_dinfo_t *dinfo, unsigned flags,
+                            cups_size_t *size);
+
+
+### Localizing Options and Values
+
+CUPS provides three functions to get localized, human-readable strings in the
+user's current locale for options and values: `cupsLocalizeDestMedia`,
+`cupsLocalizeDestOption`, and `cupsLocalizeDestValue`:
+
+    const char *
+    cupsLocalizeDestMedia(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *info, unsigned flags,
+                          cups_size_t *size);
+
+    const char *
+    cupsLocalizeDestOption(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *info,
+                           const char *option);
+
+    const char *
+    cupsLocalizeDestValue(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *info,
+                          const char *option, const char *value);
+
+
+## Submitting a Print Job
+
+Once you are ready to submit a print job, you create a job using the
+`cupsCreateDestJob` function:
+
+    ipp_status_t
+    cupsCreateDestJob(http_t *http, cups_dest_t *dest,
+                      cups_dinfo_t *info, int *job_id,
+                      const char *title, int num_options,
+                      cups_option_t *options);
+
+The `title` argument specifies a name for the print job such as "My Document".
+The `num_options` and `options` arguments specify the options for the print
+job which are allocated using the `cupsAddOption` function.
+
+When successful, the job's numeric identifier is stored in the integer pointed
+to by the `job_id` argument and `IPP_STATUS_OK` is returned.  Otherwise, an IPP
+error status is returned.
+
+For example, the following code creates a new job that will print 42 copies of a
+two-sided US Letter document:
+
+    int job_id = 0;
+    int num_options = 0;
+    cups_option_t *options = NULL;
+
+    num_options = cupsAddOption(CUPS_COPIES, "42",
+                                num_options, &options);
+    num_options = cupsAddOption(CUPS_MEDIA, CUPS_MEDIA_LETTER,
+                                num_options, &options);
+    num_options = cupsAddOption(CUPS_SIDES,
+                                CUPS_SIDES_TWO_SIDED_PORTRAIT,
+                                num_options, &options);
+
+    if (cupsCreateDestJob(CUPS_HTTP_DEFAULT, dest, info,
+                          &job_id, "My Document", num_options,
+                          options) == IPP_STATUS_OK)
+      printf("Created job: %d\n", job_id);
+    else
+      printf("Unable to create job: %s\n",
+             cupsLastErrorString());
+
+Once the job is created, you submit documents for the job using the
+`cupsStartDestDocument`, `cupsWriteRequestData`, and `cupsFinishDestDocument`
+functions:
+
+    http_status_t
+    cupsStartDestDocument(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *info, int job_id,
+                          const char *docname,
+                          const char *format,
+                          int num_options,
+                          cups_option_t *options,
+                          int last_document);
+
+    http_status_t
+    cupsWriteRequestData(http_t *http, const char *buffer,
+                         size_t length);
+
+    ipp_status_t
+    cupsFinishDestDocument(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *info);
+
+The `docname` argument specifies the name of the document, typically the
+original filename.  The `format` argument specifies the MIME media type of the
+document, including the following constants:
+
+- `CUPS_FORMAT_JPEG`: "image/jpeg"
+- `CUPS_FORMAT_PDF`: "application/pdf"
+- `CUPS_FORMAT_POSTSCRIPT`: "application/postscript"
+- `CUPS_FORMAT_TEXT`: "text/plain"
+
+The `num_options` and `options` arguments specify per-document print options,
+which at present must be 0 and `NULL`.  The `last_document` argument specifies
+whether this is the last document in the job.
+
+For example, the following code submits a PDF file to the job that was just
+created:
+
+    FILE *fp = fopen("filename.pdf", "rb");
+    size_t bytes;
+    char buffer[65536];
+
+    if (cupsStartDestDocument(CUPS_HTTP_DEFAULT, dest, info,
+                              job_id, "filename.pdf", 0, NULL,
+                              1) == HTTP_STATUS_CONTINUE)
+    {
+      while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+        if (cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer,
+                                 bytes) != HTTP_STATUS_CONTINUE)
+          break;
+
+      if (cupsFinishDestDocument(CUPS_HTTP_DEFAULT, dest,
+                                 info) == IPP_STATUS_OK)
+        puts("Document send succeeded.");
+      else
+        printf("Document send failed: %s\n",
+               cupsLastErrorString());
+    }
+
+    fclose(fp);
+
+
+# Sending IPP Requests
+
+CUPS provides a rich API for sending IPP requests to the scheduler or printers,
+typically from management or utility applications whose primary purpose is not
+to send print jobs.
+
+
+## Connecting to the Scheduler or Printer
+
+The connection to the scheduler or printer is represented by the HTTP connection
+type `http_t`.  The `cupsConnectDest` function connects to the scheduler or
+printer associated with the destination:
+
+    http_t *
+    cupsConnectDest(cups_dest_t *dest, unsigned flags, int msec,
+                    int *cancel, char *resource,
+                    size_t resourcesize, cups_dest_cb_t cb,
+                    void *user_data);
+
+The `dest` argument specifies the destination to connect to.
+
+The `flags` argument specifies whether you want to connect to the scheduler
+(`CUPS_DEST_FLAGS_NONE`) or device/printer (`CUPS_DEST_FLAGS_DEVICE`) associated
+with the destination.
+
+The `msec` argument specifies how long you are willing to wait for the
+connection to be established in milliseconds.  Specify a value of `-1` to wait
+indefinitely.
+
+The `cancel` argument specifies the address of an integer variable that can be
+set to a non-zero value to cancel the connection.  Specify a value of `NULL`
+to not provide a cancel variable.
+
+The `resource` and `resourcesize` arguments specify the address and size of a
+character string array to hold the path to use when sending an IPP request.
+
+The `cb` and `user_data` arguments specify a destination callback function that
+returns 1 to continue connecting or 0 to stop.  The destination callback work
+the same way as the one used for the `cupsEnumDests` function.
+
+On success, a HTTP connection is returned that can be used to send IPP requests
+and get IPP responses.
+
+For example, the following code connects to the printer associated with a
+destination with a 30 second timeout:
+
+    char resource[256];
+    http_t *http = cupsConnectDest(dest, CUPS_DEST_FLAGS_DEVICE,
+                                   30000, NULL, resource,
+                                   sizeof(resource), NULL, NULL);
+
+
+## Creating an IPP Request
+
+IPP requests are represented by the IPP message type `ipp_t` and each IPP
+attribute in the request is representing using the type `ipp_attribute_t`.  Each
+IPP request includes an operation code (`IPP_OP_CREATE_JOB`,
+`IPP_OP_GET_PRINTER_ATTRIBUTES`, etc.) and a 32-bit integer identifier.
+
+The `ippNewRequest` function creates a new IPP request:
+
+    ipp_t *
+    ippNewRequest(ipp_op_t op);
+
+The `op` argument specifies the IPP operation code for the request.  For
+example, the following code creates an IPP Get-Printer-Attributes request:
+
+    ipp_t *request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+
+The request identifier is automatically set to a unique value for the current
+process.
+
+Each IPP request starts with two IPP attributes, "attributes-charset" and
+"attributes-natural-language", followed by IPP attribute(s) that specify the
+target of the operation.  The `ippNewRequest` automatically adds the correct
+"attributes-charset" and "attributes-natural-language" attributes, but you must
+add the target attribute(s).  For example, the following code adds the
+"printer-uri" attribute to the IPP Get-Printer-Attributes request to specify
+which printer is being queried:
+
+    const char *printer_uri = cupsGetOption("device-uri",
+                                            dest->num_options,
+                                            dest->options);
+
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                 "printer-uri", NULL, printer_uri);
+
+> Note: If we wanted to query the scheduler instead of the device, we would look
+> up the "printer-uri-supported" option instead of the "device-uri" value.
+
+The `ippAddString` function adds the "printer-uri" attribute the the IPP
+request.  The `IPP_TAG_OPERATION` argument specifies that the attribute is part
+of the operation.  The `IPP_TAG_URI` argument specifies that the value is a
+Universal Resource Identifier (URI) string.  The `NULL` argument specifies there
+is no language (English, French, Japanese, etc.) associated with the string, and
+the `printer_uri` argument specifies the string value.
+
+The IPP Get-Printer-Attributes request also supports an IPP attribute called
+"requested-attributes" that lists the attributes and values you are interested
+in.  For example, the following code requests the printer state attributes:
+
+    static const char * const requested_attributes[] =
+    {
+      "printer-state",
+      "printer-state-message",
+      "printer-state-reasons"
+    };
+
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                  "requested-attributes", 3, NULL,
+                  requested_attributes);
+
+The `ippAddStrings` function adds an attribute with one or more strings, in this
+case three.  The `IPP_TAG_KEYWORD` argument specifies that the strings are
+keyword values, which are used for attribute names.  All strings use the same
+language (`NULL`), and the attribute will contain the three strings in the
+array `requested_attributes`.
+
+CUPS provides many functions to adding attributes of different types:
+
+- `ippAddBoolean` adds a boolean (`IPP_TAG_BOOLEAN`) attribute with one value.
+- `ippAddInteger` adds an enum (`IPP_TAG_ENUM`) or integer (`IPP_TAG_INTEGER`)
+  attribute with one value.
+- `ippAddIntegers` adds an enum or integer attribute with one or more values.
+- `ippAddOctetString` adds an octetString attribute with one value.
+- `ippAddOutOfBand` adds a admin-defined (`IPP_TAG_ADMINDEFINE`), default
+  (`IPP_TAG_DEFAULT`), delete-attribute (`IPP_TAG_DELETEATTR`), no-value
+  (`IPP_TAG_NOVALUE`), not-settable (`IPP_TAG_NOTSETTABLE`), unknown
+  (`IPP_TAG_UNKNOWN`), or unsupported (`IPP_TAG_UNSUPPORTED_VALUE`) out-of-band
+  attribute.
+- `ippAddRange` adds a rangeOfInteger attribute with one range.
+- `ippAddRanges` adds a rangeOfInteger attribute with one or more ranges.
+- `ippAddResolution` adds a resolution attribute with one resolution.
+- `ippAddResolutions` adds a resolution attribute with one or more resolutions.
+- `ippAddString` adds a charset (`IPP_TAG_CHARSET`), keyword (`IPP_TAG_KEYWORD`),
+  mimeMediaType (`IPP_TAG_MIMETYPE`), name (`IPP_TAG_NAME` and
+  `IPP_TAG_NAMELANG`), naturalLanguage (`IPP_TAG_NATURAL_LANGUAGE`), text
+  (`IPP_TAG_TEXT` and `IPP_TAG_TEXTLANG`), uri (`IPP_TAG_URI`), or uriScheme
+  (`IPP_TAG_URISCHEME`) attribute with one value.
+- `ippAddStrings` adds a charset, keyword, mimeMediaType, name, naturalLanguage,
+  text, uri, or uriScheme attribute with one or more values.
+
+
+## Sending the IPP Request
+
+Once you have created the IPP request, you can send it using the
+`cupsDoRequest` function.  For example, the following code sends the IPP
+Get-Printer-Attributes request to the destination and saves the response:
+
+    ipp_t *response = cupsDoRequest(http, request, resource);
+
+For requests like Send-Document that include a file, the `cupsDoFileRequest`
+function should be used:
+
+    ipp_t *response = cupsDoFileRequest(http, request, resource,
+                                        filename);
+
+Both `cupsDoRequest` and `cupsDoFileRequest` free the IPP request.  If a valid
+IPP response is received, it is stored in a new IPP message (`ipp_t`) and
+returned to the caller.  Otherwise `NULL` is returned.
+
+The status from the most recent request can be queried using the `cupsLastError`
+function, for example:
+
+    if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST)
+    {
+      /* request failed */
+    }
+
+A human-readable error message is also available using the `cupsLastErrorString`
+function:
+
+    if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST)
+    {
+      /* request failed */
+      printf("Request failed: %s\n", cupsLastErrorString());
+    }
+
+
+## Processing the IPP Response
+
+Each response to an IPP request is also an IPP message (`ipp_t`) with its own
+IPP attributes (`ipp_attribute_t`) that includes a status code (`IPP_STATUS_OK`,
+`IPP_STATUS_ERROR_BAD_REQUEST`, etc.) and the corresponding 32-bit integer
+identifier from the request.
+
+For example, the following code finds the printer state attributes and prints
+their values:
+
+    ipp_attribute_t *attr;
+
+    if ((attr = ippFindAttribute(response, "printer-state",
+                                 IPP_TAG_ENUM)) != NULL)
+    {
+      printf("printer-state=%s\n",
+             ippEnumString("printer-state", ippGetInteger(attr, 0)));
+    }
+    else
+      puts("printer-state=unknown");
+
+    if ((attr = ippFindAttribute(response, "printer-state-message",
+                                 IPP_TAG_TEXT)) != NULL)
+    {
+      printf("printer-state-message=\"%s\"\n",
+             ippGetString(attr, 0, NULL)));
+    }
+
+    if ((attr = ippFindAttribute(response, "printer-state-reasons",
+                                 IPP_TAG_KEYWORD)) != NULL)
+    {
+      int i, count = ippGetCount(attr);
+
+      puts("printer-state-reasons=");
+      for (i = 0; i < count; i ++)
+        printf("    %s\n", ippGetString(attr, i, NULL)));
+    }
+
+The `ippGetCount` function returns the number of values in an attribute.
+
+The `ippGetInteger` and `ippGetString` functions return a single integer or
+string value from an attribute.
+
+The `ippEnumString` function converts a enum value to its keyword (string)
+equivalent.
+
+Once you are done using the IPP response message, free it using the `ippDelete`
+function:
+
+    ippDelete(response);
+
+
+## Authentication
+
+CUPS normally handles authentication through the console.  GUI applications
+should set a password callback using the `cupsSetPasswordCB2` function:
+
+    void
+    cupsSetPasswordCB2(cups_password_cb2_t cb, void *user_data);
+
+The password callback will be called when needed and is responsible for setting
+the current user name using `cupsSetUser` and returning a string:
+
+    const char *
+    cups_password_cb2(const char *prompt, http_t *http,
+                      const char *method, const char *resource,
+                      void *user_data);
+
+The `prompt` argument is a string from CUPS that should be displayed to the
+user.
+
+The `http` argument is the connection hosting the request that is being
+authenticated.  The password callback can call the `httpGetField` and
+`httpGetSubField` functions to look for additional details concerning the
+authentication challenge.
+
+The `method` argument specifies the HTTP method used for the request and is
+typically "POST".
+
+The `resource` argument specifies the path used for the request.
+
+The `user_data` argument provides the user data pointer from the
+`cupsSetPasswordCB2` call.
diff --git a/cups/cupspm.opacity b/cups/cupspm.opacity
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cups/cupspm.opacity
diff --git a/cups/cupspm.png b/cups/cupspm.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cups/cupspm.png
diff --git a/cups/dest-job.c b/cups/dest-job.c
index b0d89b6..f12b1dc 100644
--- a/cups/dest-job.c
+++ b/cups/dest-job.c
@@ -1,7 +1,7 @@
 /*
  * Destination job support for CUPS.
  *
- * Copyright 2012-2016 by Apple Inc.
+ * Copyright 2012-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -25,13 +25,13 @@
  * The "job_id" is the number returned by cupsCreateDestJob.
  *
  * Returns @code IPP_STATUS_OK@ on success and
- * @code IPP_STATUS_ERRPR_NOT_AUTHORIZED@ or
+ * @code IPP_STATUS_ERROR_NOT_AUTHORIZED@ or
  * @code IPP_STATUS_ERROR_FORBIDDEN@ on failure.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
 
-ipp_status_t
+ipp_status_t                            /* O - Status of cancel operation */
 cupsCancelDestJob(http_t      *http,	/* I - Connection to destination */
                   cups_dest_t *dest,	/* I - Destination */
                   int         job_id)	/* I - Job ID */
@@ -84,6 +84,13 @@
   DEBUG_printf(("cupsCloseDestJob(http=%p, dest=%p(%s/%s), info=%p, job_id=%d)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, job_id));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -172,6 +179,13 @@
                 "job_id=%p, title=\"%s\", num_options=%d, options=%p)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, (void *)job_id, title, num_options, (void *)options));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -252,6 +266,13 @@
   DEBUG_printf(("cupsFinishDestDocument(http=%p, dest=%p(%s/%s), info=%p)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -307,6 +328,13 @@
   DEBUG_printf(("cupsStartDestDocument(http=%p, dest=%p(%s/%s), info=%p, job_id=%d, docname=\"%s\", format=\"%s\", num_options=%d, options=%p, last_document=%d)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, job_id, docname, format, num_options, (void *)options, last_document));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
diff --git a/cups/dest-localization.c b/cups/dest-localization.c
index 6d75a97..fceaad2 100644
--- a/cups/dest-localization.c
+++ b/cups/dest-localization.c
@@ -60,12 +60,15 @@
 			*ltype;		/* Localized media type */
 
 
+  DEBUG_printf(("cupsLocalizeDestMedia(http=%p, dest=%p, dinfo=%p, flags=%x, size=%p(\"%s\"))", (void *)http, (void *)dest, (void *)dinfo, flags, (void *)size, size ? size->media : "(null)"));
+
  /*
   * Range check input...
   */
 
   if (!http || !dest || !dinfo || !size)
   {
+    DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL.");
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
     return (NULL);
   }
@@ -79,13 +82,24 @@
 
   key.id = size->media;
   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
+  {
+    DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
     return (match->str);
+  }
 
  /*
   * If not, get the localized size, source, and type strings...
   */
 
   lang = cupsLangDefault();
+
+  snprintf(temp, sizeof(temp), "media.%s", size->media);
+  if ((lsize = _cupsLangString(lang, temp)) != NULL && strcmp(lsize, temp))
+  {
+    DEBUG_printf(("1cupsLocalizeDestMedia: Returning standard localization \"%s\".", lsize));
+    return (lsize);
+  }
+
   pwg  = pwgMediaForSize(size->width, size->length);
 
   if (pwg->ppd)
@@ -101,7 +115,7 @@
       * Use inches since the size is a multiple of 1/4 inch.
       */
 
-      snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g")), size->width / 2540.0, size->length / 2540.0);
+      snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g \"")), size->width / 2540.0, size->length / 2540.0);
     }
     else
     {
@@ -189,6 +203,8 @@
 
   cupsArrayAdd(dinfo->localizations, match);
 
+  DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
+
   return (match->str);
 }
 
@@ -212,21 +228,23 @@
 {
   _cups_message_t	key,		/* Search key */
 			*match;		/* Matching entry */
+  const char            *localized;     /* Localized string */
 
 
+  DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option));
+
   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 if ((localized = _cupsLangString(cupsLangDefault(), option)) != NULL)
+    return (localized);
   else
     return (option);
 }
@@ -253,22 +271,40 @@
   _cups_message_t	key,		/* Search key */
 			*match;		/* Matching entry */
   char			pair[256];	/* option.value pair */
+  const char            *localized;     /* Localized string */
 
 
+  DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option, value));
+
   if (!http || !dest || !dinfo)
     return (value);
 
+  if (!strcmp(option, "media"))
+  {
+    pwg_media_t *media = pwgMediaForPWG(value);
+    cups_size_t size;
+
+    strlcpy(size.media, value, sizeof(size.media));
+    size.width  = media ? media->width : 0;
+    size.length = media ? media->length : 0;
+    size.left   = 0;
+    size.right  = 0;
+    size.bottom = 0;
+    size.top    = 0;
+
+    return (cupsLocalizeDestMedia(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size));
+  }
+
   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 if ((localized = _cupsLangString(cupsLangDefault(), pair)) != NULL && strcmp(localized, pair))
+    return (localized);
   else
     return (value);
 }
diff --git a/cups/dest-options.c b/cups/dest-options.c
index bf9020b..51705a5 100644
--- a/cups/dest-options.c
+++ b/cups/dest-options.c
@@ -1,7 +1,7 @@
 /*
  * Destination option/media support for CUPS.
  *
- * Copyright 2012-2016 by Apple Inc.
+ * Copyright 2012-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -73,7 +73,7 @@
     cups_dest_t  *dest,			/* I - Destination */
     cups_dinfo_t *dinfo,		/* I - Destination information */
     const char   *option,		/* I - Option */
-    const char   *value)		/* I - Value */
+    const char   *value)		/* I - Value or @code NULL@ */
 {
   int			i;		/* Looping var */
   char			temp[1024];	/* Temporary string */
@@ -86,10 +86,17 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
-  if (!http || !dest || !dinfo || !option || !value)
+  if (!http || !dest || !dinfo || !option)
     return (0);
 
  /*
@@ -107,7 +114,10 @@
   if (!attr)
     return (0);
 
- /*
+  if (!value)
+    return (1);
+
+/*
   * Compare values...
   */
 
@@ -316,6 +326,13 @@
     *resolved = NULL;
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -575,6 +592,13 @@
   DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : ""));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -686,6 +710,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -727,6 +758,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -770,6 +808,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -791,6 +836,8 @@
 /*
  * 'cupsFreeDestInfo()' - Free destination information obtained using
  *                        @link cupsCopyDestInfo@.
+ *
+ * @since CUPS 1.6/macOS 10.8@
  */
 
 void
@@ -852,6 +899,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -940,6 +994,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -1008,6 +1069,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -1059,6 +1127,13 @@
     unsigned     flags)			/* I - Media flags */
 {
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -1104,6 +1179,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
diff --git a/cups/dest.c b/cups/dest.c
index b06a9ee..57a8dc9 100644
--- a/cups/dest.c
+++ b/cups/dest.c
@@ -1,7 +1,7 @@
 /*
  * User-defined destination (and option) support for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -60,6 +60,13 @@
 #  define kUseLastPrinter	CFSTR("UseLastPrinter")
 #endif /* __APPLE__ */
 
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+#  define _CUPS_DNSSD_GET_DESTS 250     /* Milliseconds for cupsGetDests */
+#  define _CUPS_DNSSD_MAXTIME	50	/* Milliseconds for maximum quantum of time */
+#else
+#  define _CUPS_DNSSD_GET_DESTS 0       /* Milliseconds for cupsGetDests */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
 
 /*
  * Types...
@@ -85,6 +92,7 @@
   AvahiSimplePoll	*simple_poll;	/* Polling interface */
   AvahiClient		*client;	/* Client information */
   int			got_data;	/* Did we get data? */
+  int			browsers;	/* How many browsers are running? */
 #  endif /* HAVE_DNSSD */
   cups_dest_cb_t	cb;		/* Callback */
   void			*user_data;	/* User data pointer */
@@ -101,9 +109,9 @@
 #  else /* HAVE_AVAHI */
   AvahiRecordBrowser	*ref;		/* Browser for query */
 #  endif /* HAVE_DNSSD */
-  char			*domain,	/* Domain name */
-			*fullName,	/* Full name */
-			*regtype;	/* Registration type */
+  char			*fullName,	/* Full name */
+			*regtype,	/* Registration type */
+			*domain;	/* Domain name */
   cups_ptype_t		type;		/* Device registration type */
   cups_dest_t		dest;		/* Destination record */
 } _cups_dnssd_device_t;
@@ -115,6 +123,18 @@
 } _cups_dnssd_resolve_t;
 #endif /* HAVE_DNSSD */
 
+typedef struct _cups_getdata_s
+{
+  int         num_dests;                /* Number of destinations */
+  cups_dest_t *dests;                   /* Destinations */
+} _cups_getdata_t;
+
+typedef struct _cups_namedata_s
+{
+  const char  *name;                    /* Named destination */
+  cups_dest_t *dest;                    /* Destination */
+} _cups_namedata_t;
+
 
 /*
  * Local functions...
@@ -208,10 +228,13 @@
 static int		cups_dnssd_resolve_cb(void *context);
 static void		cups_dnssd_unquote(char *dst, const char *src,
 			                   size_t dstsize);
+static int		cups_elapsed(struct timeval *t);
 #endif /* HAVE_DNSSD || HAVE_AVAHI */
+static int              cups_enum_dests(http_t *http, unsigned flags, int msec, int *cancel, cups_ptype_t type, cups_ptype_t mask, cups_dest_cb_t cb, void *user_data);
 static int		cups_find_dest(const char *name, const char *instance,
 				       int num_dests, cups_dest_t *dests, int prev,
 				       int *rdiff);
+static int              cups_get_cb(_cups_getdata_t *data, unsigned flags, cups_dest_t *dest);
 static char		*cups_get_default(const char *filename, char *namebuf,
 					  size_t namesize, const char **instance);
 static int		cups_get_dests(const char *filename, const char *match_name,
@@ -219,6 +242,8 @@
 				       int num_dests, cups_dest_t **dests);
 static char		*cups_make_string(ipp_attribute_t *attr, char *buffer,
 			                  size_t bufsize);
+static int              cups_name_cb(_cups_namedata_t *data, unsigned flags, cups_dest_t *dest);
+static void		cups_queue_name(char *name, const char *serviceName, size_t namesize);
 
 
 /*
@@ -549,18 +574,24 @@
 
 
 /*
- * 'cupsConnectDest()' - Connect to the server for a destination.
+ * 'cupsConnectDest()' - Open a conection to the destination.
  *
- * Connect to the destination, returning a new http_t connection object and
- * optionally the resource path to use for the destination.  These calls will
- * block until a connection is made, the timeout expires, the integer pointed
- * to by "cancel" is non-zero, or the callback function (or block) returns 0,
- * The caller is responsible for calling httpClose() on the returned object.
+ * Connect to the destination, returning a new @code http_t@ connection object
+ * and optionally the resource path to use for the destination.  These calls
+ * will block until a connection is made, the timeout expires, the integer
+ * pointed to by "cancel" is non-zero, or the callback function (or block)
+ * returns 0.  The caller is responsible for calling @link httpClose@ on the
+ * returned connection.
+ *
+ * Starting with CUPS 2.2.4, the caller can pass  @code CUPS_DEST_FLAGS_DEVICE@
+ * for the "flags" argument to connect directly to the device associated with
+ * the destination.  Otherwise, the connection is made to the CUPS scheduler
+ * associated with the destination.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
 
-http_t *				/* O - Connection to server or @code NULL@ */
+http_t *				/* O - Connection to destination or @code NULL@ */
 cupsConnectDest(
     cups_dest_t    *dest,		/* I - Destination */
     unsigned       flags,		/* I - Connection flags */
@@ -608,17 +639,24 @@
   * Grab the printer URI...
   */
 
-  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
+  if (flags & CUPS_DEST_FLAGS_DEVICE)
   {
-    if ((uri = cupsGetOption("resolved-device-uri", dest->num_options, dest->options)) == NULL)
+    if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
     {
-      if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
-      {
 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-        if (strstr(uri, "._tcp"))
-          uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
+      if (strstr(uri, "._tcp"))
+        uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
 #endif /* HAVE_DNSSD || HAVE_AVAHI */
-      }
+    }
+  }
+  else if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
+  {
+    if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
+    {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      if (strstr(uri, "._tcp"))
+        uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
     }
 
     if (uri)
@@ -725,18 +763,23 @@
 
 #ifdef __BLOCKS__
 /*
- * 'cupsConnectDestBlock()' - Connect to the server for a destination.
+ * 'cupsConnectDestBlock()' - Open a connection to the destination.
  *
- * Connect to the destination, returning a new http_t connection object and
- * optionally the resource path to use for the destination.  These calls will
- * block until a connection is made, the timeout expires, the integer pointed
- * to by "cancel" is non-zero, or the callback function (or block) returns 0,
- * The caller is responsible for calling httpClose() on the returned object.
+ * Connect to the destination, returning a new @code http_t@ connection object
+ * and optionally the resource path to use for the destination.  These calls
+ * will block until a connection is made, the timeout expires, the integer
+ * pointed to by "cancel" is non-zero, or the block returns 0.  The caller is
+ * responsible for calling @link httpClose@ on the returned connection.
  *
- * @since CUPS 1.6/macOS 10.8@
+ * Starting with CUPS 2.2.4, the caller can pass  @code CUPS_DEST_FLAGS_DEVICE@
+ * for the "flags" argument to connect directly to the device associated with
+ * the destination.  Otherwise, the connection is made to the CUPS scheduler
+ * associated with the destination.
+ *
+ * @since CUPS 1.6/macOS 10.8@ @exclude all@
  */
 
-http_t *				/* O - Connection to server or @code NULL@ */
+http_t *				/* O - Connection to destination or @code NULL@ */
 cupsConnectDestBlock(
     cups_dest_t       *dest,		/* I - Destination */
     unsigned          flags,		/* I - Connection flags */
@@ -762,10 +805,10 @@
  * @since CUPS 1.6/macOS 10.8@
  */
 
-int
-cupsCopyDest(cups_dest_t *dest,
-             int         num_dests,
-             cups_dest_t **dests)
+int                                     /* O  - New number of destinations */
+cupsCopyDest(cups_dest_t *dest,         /* I  - Destination to copy */
+             int         num_dests,     /* I  - Number of destinations */
+             cups_dest_t **dests)       /* IO - Destination array */
 {
   int		i;			/* Looping var */
   cups_dest_t	*new_dest;		/* New destination pointer */
@@ -907,334 +950,36 @@
 /*
  * 'cupsEnumDests()' - Enumerate available destinations with a callback function.
  *
- * Destinations are enumerated from one or more sources. The callback function
- * receives the @code user_data@ pointer, destination name, instance, number of
- * options, and options which can be used as input to the @link cupsAddDest@
- * function.  The function must return 1 to continue enumeration or 0 to stop.
+ * Destinations are enumerated from one or more sources.  The callback function
+ * receives the @code user_data@ pointer and the destination pointer which can
+ * be used as input to the @link cupsCopyDest@ function.  The function must
+ * return 1 to continue enumeration or 0 to stop.
+ *
+ * The @code type@ and @code mask@ arguments allow the caller to filter the
+ * destinations that are enumerated.  Passing 0 for both will enumerate all
+ * printers.  The constant @code CUPS_PRINTER_DISCOVERED@ is used to filter on
+ * destinations that are available but have not yet been added locally.
  *
  * Enumeration happens on the current thread and does not return until all
  * destinations have been enumerated or the callback function returns 0.
  *
+ * Note: The callback function will likely receive multiple updates for the same
+ * destinations - it is up to the caller to suppress any duplicate destinations.
+ *
  * @since CUPS 1.6/macOS 10.8@
  */
 
 int					/* O - 1 on success, 0 on failure */
 cupsEnumDests(
-    unsigned       flags,		/* I - Enumeration flags */
-    int            msec,		/* I - Timeout in milliseconds,
-					 *     -1 for indefinite */
-    int            *cancel,		/* I - Pointer to "cancel" variable */
-    cups_ptype_t   type,		/* I - Printer type bits */
-    cups_ptype_t   mask,		/* I - Mask for printer type bits */
-    cups_dest_cb_t cb,			/* I - Callback function */
-    void           *user_data)		/* I - User data */
+  unsigned       flags,			/* I - Enumeration flags */
+  int            msec,			/* I - Timeout in milliseconds, -1 for indefinite */
+  int            *cancel,		/* I - Pointer to "cancel" variable */
+  cups_ptype_t   type,			/* I - Printer type bits */
+  cups_ptype_t   mask,			/* I - Mask for printer type bits */
+  cups_dest_cb_t cb,			/* I - Callback function */
+  void           *user_data)		/* I - User data */
 {
-  int			i,		/* Looping var */
-			num_dests;	/* Number of destinations */
-  cups_dest_t		*dests = NULL,	/* Destinations */
-			*dest;		/* Current destination */
-  const char		*defprinter;	/* Default printer */
-  char			name[1024],	/* Copy of printer name */
-			*instance,	/* Pointer to instance name */
-			*user_default;	/* User default printer */
-#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 */
-#  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
-  DNSServiceRef		ipps_ref,	/* IPPS browser */
-			local_ipps_ref;	/* Local IPPS browser */
-#    endif /* HAVE_SSL */
-#    ifdef HAVE_POLL
-  struct pollfd		pfd;		/* Polling data */
-#    else
-  fd_set		input;		/* Input set for select() */
-  struct timeval	timeout;	/* Timeout for select() */
-#    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...
-  */
-
-  (void)flags;
-
-  if (!cb)
-    return (0);
-
- /*
-  * Get the list of local printers and pass them to the callback function...
-  */
-
-  num_dests = _cupsGetDests(CUPS_HTTP_DEFAULT, IPP_OP_CUPS_GET_PRINTERS, NULL,
-                            &dests, type, mask | CUPS_PRINTER_3D);
-
-  if ((user_default = _cupsUserDefault(name, sizeof(name))) != NULL)
-    defprinter = name;
-  else if ((defprinter = cupsGetDefault2(CUPS_HTTP_DEFAULT)) != NULL)
-  {
-    strlcpy(name, defprinter, sizeof(name));
-    defprinter = name;
-  }
-
-  if (defprinter)
-  {
-   /*
-    * Separate printer and instance name...
-    */
-
-    if ((instance = strchr(name, '/')) != NULL)
-      *instance++ = '\0';
-
-   /*
-    * Lookup the printer and instance and make it the default...
-    */
-
-    if ((dest = cupsGetDest(name, instance, num_dests, dests)) != NULL)
-      dest->is_default = 1;
-  }
-
-  for (i = num_dests, dest = dests;
-       i > 0 && (!cancel || !*cancel);
-       i --, dest ++)
-    if (!(*cb)(user_data, i > 1 ? CUPS_DEST_FLAGS_MORE : CUPS_DEST_FLAGS_NONE,
-               dest))
-      break;
-
-  cupsFreeDests(num_dests, dests);
-
-  if (i > 0 || msec == 0)
-    return (1);
-
-#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
- /*
-  * Get Bonjour-shared printers...
-  */
-
-  data.type      = type;
-  data.mask      = mask;
-  data.cb        = cb;
-  data.user_data = user_data;
-  data.devices   = cupsArrayNew3((cups_array_func_t)cups_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)cups_dnssd_free_device);
-
-#  ifdef HAVE_DNSSD
-  if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)
-    return (0);
-
-  main_fd = DNSServiceRefSockFD(data.main_ref);
-
-  ipp_ref = data.main_ref;
-  DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
-                   "_ipp._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data);
-
-  local_ipp_ref = data.main_ref;
-  DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
-                   kDNSServiceInterfaceIndexLocalOnly,
-                   "_ipp._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_local_cb, &data);
-
-#    ifdef HAVE_SSL
-  ipps_ref = data.main_ref;
-  DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
-                   "_ipps._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data);
-
-  local_ipps_ref = data.main_ref;
-  DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
-                   kDNSServiceInterfaceIndexLocalOnly,
-                   "_ipps._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_local_cb, &data);
-#    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;
-  else
-    remaining = msec;
-
-  while (remaining > 0 && (!cancel || !*cancel))
-  {
-   /*
-    * Check for input...
-    */
-
-#  ifdef HAVE_DNSSD
-#    ifdef HAVE_POLL
-    pfd.fd     = main_fd;
-    pfd.events = POLLIN;
-
-    nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining);
-
-#    else
-    FD_ZERO(&input);
-    FD_SET(main_fd, &input);
-
-    timeout.tv_sec  = 0;
-    timeout.tv_usec = remaining > 250 ? 250000 : remaining * 1000;
-
-    nfds = select(main_fd + 1, &input, NULL, NULL, &timeout);
-#    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;
-         device = (_cups_dnssd_device_t *)cupsArrayNext(data.devices))
-    {
-      if (device->ref)
-        count ++;
-
-      if (!device->ref && device->state == _CUPS_DNSSD_NEW)
-      {
-	DEBUG_printf(("1cupsEnumDests: Querying '%s'.", device->fullName));
-
-#  ifdef HAVE_DNSSD
-        device->ref = data.main_ref;
-
-	if (DNSServiceQueryRecord(&(device->ref),
-				  kDNSServiceFlagsShareConnection,
-				  0, device->fullName,
-				  kDNSServiceType_TXT,
-				  kDNSServiceClass_IN,
-				  (DNSServiceQueryRecordReply)cups_dnssd_query_cb,
-				  &data) == kDNSServiceErr_NoError)
-	{
-	  count ++;
-	}
-	else
-	{
-	  device->ref   = 0;
-	  device->state = _CUPS_DNSSD_ERROR;
-
-	  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 ((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
-  DNSServiceRefDeallocate(ipp_ref);
-  DNSServiceRefDeallocate(local_ipp_ref);
-#    endif /* HAVE_SSL */
-
-  DNSServiceRefDeallocate(data.main_ref);
-
-#  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);
+  return (cups_enum_dests(CUPS_HTTP_DEFAULT, flags, msec, cancel, type, mask, cb, user_data));
 }
 
 
@@ -1242,15 +987,23 @@
 /*
  * 'cupsEnumDestsBlock()' - Enumerate available destinations with a block.
  *
- * Destinations are enumerated from one or more sources. The block receives the
- * destination name, instance, number of options, and options which can be used
- * as input to the @link cupsAddDest@ function.  The block must return 1 to
+ * Destinations are enumerated from one or more sources.  The block receives the
+ * @code user_data@ pointer and the destination pointer which can be used as
+ * input to the @link cupsCopyDest@ function.  The block must return 1 to
  * continue enumeration or 0 to stop.
  *
+ * The @code type@ and @code mask@ arguments allow the caller to filter the
+ * destinations that are enumerated.  Passing 0 for both will enumerate all
+ * printers.  The constant @code CUPS_PRINTER_DISCOVERED@ is used to filter on
+ * destinations that are available but have not yet been added locally.
+ *
  * Enumeration happens on the current thread and does not return until all
  * destinations have been enumerated or the block returns 0.
  *
- * @since CUPS 1.6/macOS 10.8@
+ * Note: The block will likely receive multiple updates for the same
+ * destinations - it is up to the caller to suppress any duplicate destinations.
+ *
+ * @since CUPS 1.6/macOS 10.8@ @exclude all@
  */
 
 int					/* O - 1 on success, 0 on failure */
@@ -1298,7 +1051,7 @@
 /*
  * 'cupsGetDest()' - Get the named destination from the list.
  *
- * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
+ * Use the @link cupsEnumDests@ or @link cupsGetDests2@ functions to get a
  * list of supported destinations for the current user.
  */
 
@@ -1363,6 +1116,8 @@
   int		port;			/* Port number */
 
 
+  DEBUG_printf(("_cupsGetDestResource(dest=%p(%s), resource=%p, resourcesize=%d)", (void *)dest, dest->name, (void *)resource, (int)resourcesize));
+
  /*
   * Range check input...
   */
@@ -1380,34 +1135,59 @@
   * Grab the printer URI...
   */
 
-  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options,
-                           dest->options)) == NULL)
+  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
   {
-    if (resource)
-      *resource = '\0';
+    if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
+    {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      if (strstr(uri, "._tcp"))
+        uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+    }
 
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+    if (uri)
+    {
+      DEBUG_printf(("1_cupsGetDestResource: Resolved printer-uri-supported=\"%s\"", uri));
 
-    return (NULL);
-  }
+      uri = _cupsCreateDest(dest->name, cupsGetOption("printer-info", dest->num_options, dest->options), NULL, uri, resource, resourcesize);
+    }
 
-#ifdef HAVE_DNSSD
-  if (strstr(uri, "._tcp"))
-  {
-    if ((uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL)) == NULL)
+    if (uri)
+    {
+      DEBUG_printf(("1_cupsGetDestResource: Local printer-uri-supported=\"%s\"", uri));
+
+      dest->num_options = cupsAddOption("printer-uri-supported", uri, dest->num_options, &dest->options);
+
+      uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
+    }
+    else
+    {
+      DEBUG_puts("1_cupsGetDestResource: No printer-uri-supported found.");
+
+      if (resource)
+        *resource = '\0';
+
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+
       return (NULL);
+    }
   }
-#endif /* HAVE_DNSSD */
-
-  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
-                      userpass, sizeof(userpass), hostname, sizeof(hostname),
-                      &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
+  else
   {
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
+    DEBUG_printf(("1_cupsGetDestResource: printer-uri-supported=\"%s\"", uri));
 
-    return (NULL);
+    if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
+                        userpass, sizeof(userpass), hostname, sizeof(hostname),
+                        &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
+    {
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
+
+      return (NULL);
+    }
   }
 
+  DEBUG_printf(("1_cupsGetDestResource: resource=\"%s\"", resource));
+
   return (uri);
 }
 
@@ -1434,6 +1214,7 @@
 		hostname[256],		/* Hostname from URI */
 		resource[1024],		/* Resource path from URI */
 		*ptr;			/* Pointer into string */
+  const char	*info;			/* printer-info string */
   int		port;			/* Port number from URI */
 
 
@@ -1455,7 +1236,11 @@
     return (NULL);
   }
 
-  if (!name)
+  if (name)
+  {
+    info = name;
+  }
+  else
   {
    /*
     * Create the name from the URI...
@@ -1467,24 +1252,29 @@
       * Use the service instance name...
       */
 
-      if ((ptr = strchr(hostname, '.')) != NULL)
+      if ((ptr = strstr(hostname, "._")) != NULL)
         *ptr = '\0';
 
-      name = hostname;
+      cups_queue_name(temp, hostname, sizeof(temp));
+      name = temp;
+      info = hostname;
     }
     else if (!strncmp(resource, "/classes/", 9))
     {
       snprintf(temp, sizeof(temp), "%s @ %s", resource + 9, hostname);
-      name = temp;
+      name = resource + 9;
+      info = temp;
     }
     else if (!strncmp(resource, "/printers/", 10))
     {
       snprintf(temp, sizeof(temp), "%s @ %s", resource + 10, hostname);
-      name = temp;
+      name = resource + 10;
+      info = temp;
     }
     else
     {
       name = hostname;
+      info = hostname;
     }
   }
 
@@ -1500,7 +1290,7 @@
 
   dest->name        = _cupsStrAlloc(name);
   dest->num_options = cupsAddOption("device-uri", uri, dest->num_options, &(dest->options));
-  dest->num_options = cupsAddOption("printer-info", name, dest->num_options, &(dest->options));
+  dest->num_options = cupsAddOption("printer-info", info, dest->num_options, &(dest->options));
 
   return (dest);
 }
@@ -1574,6 +1364,7 @@
 		  "printer-info",
 		  "printer-is-accepting-jobs",
 		  "printer-is-shared",
+                  "printer-is-temporary",
 		  "printer-location",
 		  "printer-make-and-model",
 		  "printer-mandatory-job-attributes",
@@ -1586,12 +1377,15 @@
 		};
 
 
+  DEBUG_printf(("_cupsGetDests(http=%p, op=%x(%s), name=\"%s\", dests=%p, type=%x, mask=%x)", (void *)http, op, ippOpString(op), name, (void *)dests, type, mask));
+
 #ifdef __APPLE__
  /*
   * Get the default paper size...
   */
 
   appleGetPaperSize(media_default, sizeof(media_default));
+  DEBUG_printf(("1_cupsGetDests: Default media is '%s'.", media_default));
 #endif /* __APPLE__ */
 
  /*
@@ -1678,7 +1472,8 @@
 	    !strcmp(attr->name, "marker-types") ||
 	    !strcmp(attr->name, "printer-commands") ||
 	    !strcmp(attr->name, "printer-info") ||
-	    !strcmp(attr->name, "printer-is-shared") ||
+            !strcmp(attr->name, "printer-is-shared") ||
+            !strcmp(attr->name, "printer-is-temporary") ||
 	    !strcmp(attr->name, "printer-make-and-model") ||
 	    !strcmp(attr->name, "printer-mandatory-job-attributes") ||
 	    !strcmp(attr->name, "printer-state") ||
@@ -1699,7 +1494,7 @@
 				      num_options, &options);
 	}
 #ifdef __APPLE__
-	else if (!strcmp(attr->name, "media-supported"))
+	else if (!strcmp(attr->name, "media-supported") && media_default[0])
 	{
 	 /*
 	  * See if we can set a default media size...
@@ -1710,8 +1505,8 @@
 	  for (i = 0; i < attr->num_values; i ++)
 	    if (!_cups_strcasecmp(media_default, attr->values[i].string.text))
 	    {
-	      num_options = cupsAddOption("media", media_default, num_options,
-	                                  &options);
+              DEBUG_printf(("1_cupsGetDests: Setting media to '%s'.", media_default));
+	      num_options = cupsAddOption("media", media_default, num_options, &options);
               break;
 	    }
 	}
@@ -1720,7 +1515,8 @@
 	         attr->value_tag == IPP_TAG_NAME)
 	  printer_name = attr->values[0].string.text;
         else if (strncmp(attr->name, "notify-", 7) &&
-	         (attr->value_tag == IPP_TAG_BOOLEAN ||
+                 strncmp(attr->name, "print-quality-", 14) &&
+                 (attr->value_tag == IPP_TAG_BOOLEAN ||
 		  attr->value_tag == IPP_TAG_ENUM ||
 		  attr->value_tag == IPP_TAG_INTEGER ||
 		  attr->value_tag == IPP_TAG_KEYWORD ||
@@ -1735,12 +1531,8 @@
           strlcpy(optname, attr->name, sizeof(optname));
 	  optname[ptr - attr->name] = '\0';
 
-	  if (_cups_strcasecmp(optname, "media") ||
-	      !cupsGetOption("media", num_options, options))
-	    num_options = cupsAddOption(optname,
-					cups_make_string(attr, value,
-							 sizeof(value)),
-					num_options, &options);
+	  if (_cups_strcasecmp(optname, "media") || !cupsGetOption("media", num_options, options))
+	    num_options = cupsAddOption(optname, cups_make_string(attr, value, sizeof(value)), num_options, &options);
 	}
       }
 
@@ -1785,15 +1577,23 @@
  * 'cupsGetDests()' - Get the list of destinations from the default server.
  *
  * Starting with CUPS 1.2, the returned list of destinations include the
- * printer-info, printer-is-accepting-jobs, printer-is-shared,
- * printer-make-and-model, printer-state, printer-state-change-time,
- * printer-state-reasons, and printer-type attributes as options.  CUPS 1.4
- * adds the marker-change-time, marker-colors, marker-high-levels,
- * marker-levels, marker-low-levels, marker-message, marker-names,
- * marker-types, and printer-commands attributes as well.
+ * "printer-info", "printer-is-accepting-jobs", "printer-is-shared",
+ * "printer-make-and-model", "printer-state", "printer-state-change-time",
+ * "printer-state-reasons", "printer-type", and "printer-uri-supported"
+ * attributes as options.
+ *
+ * CUPS 1.4 adds the "marker-change-time", "marker-colors",
+ * "marker-high-levels", "marker-levels", "marker-low-levels", "marker-message",
+ * "marker-names", "marker-types", and "printer-commands" attributes as options.
+ *
+ * CUPS 2.2 adds accessible IPP printers to the list of destinations that can
+ * be used.  The "printer-uri-supported" option will be present for those IPP
+ * printers that have been recently used.
  *
  * Use the @link cupsFreeDests@ function to free the destination list and
  * the @link cupsGetDest@ function to find a particular destination.
+ *
+ * @exclude all@
  */
 
 int					/* O - Number of destinations */
@@ -1807,12 +1607,18 @@
  * 'cupsGetDests2()' - Get the list of destinations from the specified server.
  *
  * Starting with CUPS 1.2, the returned list of destinations include the
- * printer-info, printer-is-accepting-jobs, printer-is-shared,
- * printer-make-and-model, printer-state, printer-state-change-time,
- * printer-state-reasons, and printer-type attributes as options.  CUPS 1.4
- * adds the marker-change-time, marker-colors, marker-high-levels,
- * marker-levels, marker-low-levels, marker-message, marker-names,
- * marker-types, and printer-commands attributes as well.
+ * "printer-info", "printer-is-accepting-jobs", "printer-is-shared",
+ * "printer-make-and-model", "printer-state", "printer-state-change-time",
+ * "printer-state-reasons", "printer-type", and "printer-uri-supported"
+ * attributes as options.
+ *
+ * CUPS 1.4 adds the "marker-change-time", "marker-colors",
+ * "marker-high-levels", "marker-levels", "marker-low-levels", "marker-message",
+ * "marker-names", "marker-types", and "printer-commands" attributes as options.
+ *
+ * CUPS 2.2 adds accessible IPP printers to the list of destinations that can
+ * be used.  The "printer-uri-supported" option will be present for those IPP
+ * printers that have been recently used.
  *
  * Use the @link cupsFreeDests@ function to free the destination list and
  * the @link cupsGetDest@ function to find a particular destination.
@@ -1824,8 +1630,8 @@
 cupsGetDests2(http_t      *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
               cups_dest_t **dests)	/* O - Destinations */
 {
-  int		num_dests;		/* Number of destinations */
-  cups_dest_t	*dest;			/* Destination pointer */
+  _cups_getdata_t data;                 /* Enumeration data */
+  cups_dest_t   *dest;                  /* Current destination */
   const char	*home;			/* HOME environment variable */
   char		filename[1024];		/* Local ~/.cups/lpoptions file */
   const char	*defprinter;		/* Default printer */
@@ -1837,41 +1643,70 @@
   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
 
 
- /*
+  DEBUG_printf(("cupsGetDests2(http=%p, dests=%p)", (void *)http, (void *)dests));
+
+/*
   * Range check the input...
   */
 
   if (!dests)
   {
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad NULL dests pointer"), 1);
+    DEBUG_puts("1cupsGetDests2: NULL dests pointer, returning 0.");
     return (0);
   }
 
  /*
+  * Connect to the server as needed...
+  */
+
+  if (!http)
+  {
+    if ((http = _cupsConnect()) == NULL)
+    {
+      *dests = NULL;
+
+      return (0);
+    }
+  }
+
+ /*
   * Grab the printers and classes...
   */
 
-  *dests    = (cups_dest_t *)0;
-  num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, dests, 0, CUPS_PRINTER_3D);
+  data.num_dests = 0;
+  data.dests     = NULL;
 
-  if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+  if (!httpAddrLocalhost(httpGetAddress(http)))
   {
-    cupsFreeDests(num_dests, *dests);
-    *dests = (cups_dest_t *)0;
-    return (0);
+   /*
+    * When talking to a remote cupsd, just enumerate printers on the remote
+    * cupsd.
+    */
+
+    cups_enum_dests(http, 0, _CUPS_DNSSD_GET_DESTS, NULL, 0, CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)cups_get_cb, &data);
+  }
+  else
+  {
+   /*
+    * When talking to a local cupsd, enumerate both local printers and ones we
+    * can find on the network...
+    */
+
+    cups_enum_dests(http, 0, _CUPS_DNSSD_GET_DESTS, NULL, 0, 0, (cups_dest_cb_t)cups_get_cb, &data);
   }
 
  /*
   * Make a copy of the "real" queues for a later sanity check...
   */
 
-  if (num_dests > 0)
+  if (data.num_dests > 0)
   {
-    num_reals = num_dests;
+    num_reals = data.num_dests;
     reals     = calloc((size_t)num_reals, sizeof(cups_dest_t));
 
     if (reals)
-      memcpy(reals, *dests, (size_t)num_reals * sizeof(cups_dest_t));
+      memcpy(reals, data.dests, (size_t)num_reals * sizeof(cups_dest_t));
     else
       num_reals = 0;
   }
@@ -1906,7 +1741,7 @@
     * Lookup the printer and instance and make it the default...
     */
 
-    if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
+    if ((dest = cupsGetDest(name, instance, data.num_dests, data.dests)) != NULL)
       dest->is_default = 1;
   }
   else
@@ -1917,15 +1752,13 @@
   */
 
   snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
-  num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
-                             num_dests, dests);
+  data.num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL, data.num_dests, &data.dests);
 
   if ((home = getenv("HOME")) != NULL)
   {
     snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
 
-    num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
-                               num_dests, dests);
+    data.num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL, data.num_dests, &data.dests);
   }
 
  /*
@@ -1940,7 +1773,7 @@
     * See if we have a default printer...
     */
 
-    if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
+    if ((dest = cupsGetDest(NULL, NULL, data.num_dests, data.dests)) != NULL)
     {
      /*
       * Have a default; see if it is real...
@@ -1953,8 +1786,7 @@
         * going to an unexpected printer... (<rdar://problem/14216472>)
         */
 
-        num_dests = cupsRemoveDest(dest->name, dest->instance, num_dests,
-                                   dests);
+        data.num_dests = cupsRemoveDest(dest->name, dest->instance, data.num_dests, &data.dests);
       }
     }
 
@@ -1969,10 +1801,14 @@
   * Return the number of destinations...
   */
 
-  if (num_dests > 0)
+  *dests = data.dests;
+
+  if (data.num_dests > 0)
     _cupsSetError(IPP_STATUS_OK, NULL, 0);
 
-  return (num_dests);
+  DEBUG_printf(("1cupsGetDests2: Returning %d destinations.", data.num_dests));
+
+  return (data.num_dests);
 }
 
 
@@ -1980,10 +1816,10 @@
  * 'cupsGetNamedDest()' - Get options for the named destination.
  *
  * This function is optimized for retrieving a single destination and should
- * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
- * know the name of the destination or want to print to the default destination.
- * If @code NULL@ is returned, the destination does not exist or there is no
- * default destination.
+ * be used instead of @link cupsGetDests2@ and @link cupsGetDest@ when you
+ * either know the name of the destination or want to print to the default
+ * destination.  If @code NULL@ is returned, the destination does not exist or
+ * there is no default destination.
  *
  * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
  * server will be used.
@@ -2002,6 +1838,7 @@
                  const char *name,	/* I - Destination name or @code NULL@ for the default destination */
                  const char *instance)	/* I - Instance name or @code NULL@ */
 {
+  const char    *dest_name;             /* Working destination name */
   cups_dest_t	*dest;			/* Destination */
   char		filename[1024],		/* Path to lpoptions */
 		defname[256];		/* Default printer name */
@@ -2012,16 +1849,20 @@
   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
 
 
+  DEBUG_printf(("cupsGetNamedDest(http=%p, name=\"%s\", instance=\"%s\")", (void *)http, name, instance));
+
  /*
   * If "name" is NULL, find the default destination...
   */
 
-  if (!name)
+  dest_name = name;
+
+  if (!dest_name)
   {
     set_as_default = 1;
-    name           = _cupsUserDefault(defname, sizeof(defname));
+    dest_name      = _cupsUserDefault(defname, sizeof(defname));
 
-    if (name)
+    if (dest_name)
     {
       char	*ptr;			/* Temporary pointer... */
 
@@ -2041,36 +1882,60 @@
 
       snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
 
-      name = cups_get_default(filename, defname, sizeof(defname), &instance);
+      dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
     }
 
-    if (!name)
+    if (!dest_name)
     {
      /*
       * Still not there?  Try the system lpoptions file...
       */
 
-      snprintf(filename, sizeof(filename), "%s/lpoptions",
-	       cg->cups_serverroot);
-      name = cups_get_default(filename, defname, sizeof(defname), &instance);
+      snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+      dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
     }
 
-    if (!name)
+    if (!dest_name)
     {
      /*
       * No locally-set default destination, ask the server...
       */
 
       op = IPP_OP_CUPS_GET_DEFAULT;
+
+      DEBUG_puts("1cupsGetNamedDest: Asking server for default printer...");
     }
+    else
+      DEBUG_printf(("1cupsGetNamedDest: Using name=\"%s\"...", name));
   }
 
  /*
   * Get the printer's attributes...
   */
 
-  if (!_cupsGetDests(http, op, name, &dest, 0, CUPS_PRINTER_3D))
-    return (NULL);
+  if (!_cupsGetDests(http, op, dest_name, &dest, 0, 0))
+  {
+    if (name)
+    {
+      _cups_namedata_t  data;           /* Callback data */
+
+      DEBUG_puts("1cupsGetNamedDest: No queue found for printer, looking on network...");
+
+      data.name = name;
+      data.dest = NULL;
+
+      cupsEnumDests(0, 1000, NULL, 0, 0, (cups_dest_cb_t)cups_name_cb, &data);
+
+      if (!data.dest)
+        return (NULL);
+
+      dest = data.dest;
+    }
+    else
+      return (NULL);
+  }
+
+  DEBUG_printf(("1cupsGetNamedDest: Got dest=%p", (void *)dest));
 
   if (instance)
     dest->instance = _cupsStrAlloc(instance);
@@ -2083,13 +1948,13 @@
   */
 
   snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
-  cups_get_dests(filename, name, instance, 1, 1, &dest);
+  cups_get_dests(filename, dest_name, instance, 1, 1, &dest);
 
   if (home)
   {
     snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
 
-    cups_get_dests(filename, name, instance, 1, 1, &dest);
+    cups_get_dests(filename, dest_name, instance, 1, 1, &dest);
   }
 
  /*
@@ -2193,6 +2058,8 @@
  *
  * This function saves the destinations to /etc/cups/lpoptions when run
  * as root and ~/.cups/lpoptions when run as a normal user.
+ *
+ * @exclude all@
  */
 
 void
@@ -2245,7 +2112,7 @@
   * Get the server destinations...
   */
 
-  num_temps = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &temps, 0, CUPS_PRINTER_3D);
+  num_temps = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &temps, 0, 0);
 
   if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
   {
@@ -2839,11 +2706,12 @@
   (void)protocol;
   (void)context;
 
+  DEBUG_printf(("cups_dnssd_browse_cb(..., name=\"%s\", type=\"%s\", domain=\"%s\", ...);", name, type, domain));
+
   switch (event)
   {
     case AVAHI_BROWSER_FAILURE:
-	DEBUG_printf(("cups_dnssd_browse_cb: %s",
-		      avahi_strerror(avahi_client_errno(client))));
+	DEBUG_printf(("cups_dnssd_browse_cb: %s", avahi_strerror(avahi_client_errno(client))));
 	avahi_simple_poll_quit(data->simple_poll);
 	break;
 
@@ -2858,8 +2726,7 @@
 	  * This comes from the local machine so ignore it.
 	  */
 
-	  DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".",
-	                name));
+	  DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".", name));
 	}
 	else
 	{
@@ -2871,9 +2738,13 @@
 	}
 	break;
 
-    case AVAHI_BROWSER_REMOVE:
-    case AVAHI_BROWSER_ALL_FOR_NOW:
-    case AVAHI_BROWSER_CACHE_EXHAUSTED:
+    case AVAHI_BROWSER_REMOVE :
+    case AVAHI_BROWSER_CACHE_EXHAUSTED :
+        break;
+
+    case AVAHI_BROWSER_ALL_FOR_NOW :
+        DEBUG_puts("cups_dnssd_browse_cb: ALL_FOR_NOW");
+        data->browsers --;
         break;
   }
 }
@@ -2895,6 +2766,8 @@
 
   (void)client;
 
+  DEBUG_printf(("cups_dnssd_client_cb(client=%p, state=%d, context=%p)", client, state, context));
+
  /*
   * If the connection drops, quit.
   */
@@ -2964,8 +2837,9 @@
 {
   _cups_dnssd_device_t	key,		/* Search key */
 			*device;	/* Device */
-  char			fullName[kDNSServiceMaxDomainName];
+  char			fullName[kDNSServiceMaxDomainName],
 					/* Full name for query */
+			name[128];	/* Queue name */
 
 
   DEBUG_printf(("5cups_dnssd_get_device(data=%p, serviceName=\"%s\", regtype=\"%s\", replyDomain=\"%s\")", (void *)data, serviceName, regtype, replyDomain));
@@ -2974,7 +2848,9 @@
   * See if this is an existing device...
   */
 
-  key.dest.name = (char *)serviceName;
+  cups_queue_name(name, serviceName, sizeof(name));
+
+  key.dest.name = name;
 
   if ((device = cupsArrayFind(data->devices, &key)) != NULL)
   {
@@ -3035,10 +2911,12 @@
                   replyDomain));
 
     device            = calloc(sizeof(_cups_dnssd_device_t), 1);
-    device->dest.name = _cupsStrAlloc(serviceName);
+    device->dest.name = _cupsStrAlloc(name);
     device->domain    = _cupsStrAlloc(replyDomain);
     device->regtype   = _cupsStrAlloc(regtype);
 
+    device->dest.num_options = cupsAddOption("printer-info", serviceName, 0, &device->dest.options);
+
     cupsArrayAdd(data->devices, device);
   }
 
@@ -3047,11 +2925,9 @@
   */
 
 #  ifdef HAVE_DNSSD
-  DNSServiceConstructFullName(fullName, device->dest.name, device->regtype,
-			      device->domain);
+  DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
 #  else /* HAVE_AVAHI */
-  avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName,
-                          regtype, replyDomain);
+  avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain);
 #  endif /* HAVE_DNSSD */
 
   _cupsStrFree(device->fullName);
@@ -3070,6 +2946,8 @@
 
   if (device->state == _CUPS_DNSSD_ACTIVE)
   {
+    DEBUG_printf(("6cups_dnssd_get_device: Remove callback for \"%s\".", device->dest.name));
+
     (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
     device->state = _CUPS_DNSSD_NEW;
   }
@@ -3128,7 +3006,10 @@
   }
 
   if (device->state == _CUPS_DNSSD_ACTIVE)
+  {
+    DEBUG_printf(("6cups_dnssd_local_cb: Remove callback for \"%s\".", device->dest.name));
     (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
+  }
 
   device->state = _CUPS_DNSSD_LOCAL;
 }
@@ -3141,7 +3022,9 @@
  *
  * Note: This function is needed because avahi_simple_poll_iterate is broken
  *       and always uses a timeout of 0 (!) milliseconds.
- *       (Avahi Ticket #364)
+ *       (https://github.com/lathiat/avahi/issues/127)
+ *
+ * @private@
  */
 
 static int				/* O - Number of file descriptors matching */
@@ -3156,16 +3039,22 @@
   int			val;		/* Return value */
 
 
+  DEBUG_printf(("cups_dnssd_poll_cb(pollfds=%p, num_pollfds=%d, timeout=%d, context=%p)", pollfds, num_pollfds, timeout, context));
+
   (void)timeout;
 
-  val = poll(pollfds, num_pollfds, 250);
+  val = poll(pollfds, num_pollfds, _CUPS_DNSSD_MAXTIME);
+
+  DEBUG_printf(("cups_dnssd_poll_cb: poll() returned %d", val));
 
   if (val < 0)
   {
     DEBUG_printf(("cups_dnssd_poll_cb: %s", strerror(errno)));
   }
   else if (val > 0)
+  {
     data->got_data = 1;
+  }
 
   return (val);
 }
@@ -3214,7 +3103,8 @@
 #  endif /* HAVE_DNSSD */
   _cups_dnssd_data_t	*data = (_cups_dnssd_data_t *)context;
 					/* Enumeration data */
-  char			name[1024],	/* Service name */
+  char			serviceName[256],/* Service name */
+			name[128],	/* Queue name */
 			*ptr;		/* Pointer into string */
   _cups_dnssd_device_t	dkey,		/* Search key */
 			*device;	/* Device */
@@ -3231,11 +3121,7 @@
     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));
+  DEBUG_printf(("cups_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...
@@ -3244,8 +3130,7 @@
   if (event != AVAHI_BROWSER_NEW)
   {
     if (event == AVAHI_BROWSER_FAILURE)
-      DEBUG_printf(("cups_dnssd_query_cb: %s",
-		    avahi_strerror(avahi_client_errno(client))));
+      DEBUG_printf(("cups_dnssd_query_cb: %s", avahi_strerror(avahi_client_errno(client))));
 
     return;
   }
@@ -3255,14 +3140,16 @@
   * Lookup the service in the devices array.
   */
 
-  dkey.dest.name = name;
+  cups_dnssd_unquote(serviceName, fullName, sizeof(serviceName));
 
-  cups_dnssd_unquote(name, fullName, sizeof(name));
-
-  if ((ptr = strstr(name, "._")) != NULL)
+  if ((ptr = strstr(serviceName, "._")) != NULL)
     *ptr = '\0';
 
-  if ((device = cupsArrayFind(data->devices, &dkey)) != NULL)
+  cups_queue_name(name, serviceName, sizeof(name));
+
+  dkey.dest.name = name;
+
+  if ((device = cupsArrayFind(data->devices, &dkey)) != NULL && device->state == _CUPS_DNSSD_NEW)
   {
    /*
     * Found it, pull out the make and model from the TXT record and save it...
@@ -3279,7 +3166,7 @@
 			model[256],	/* Model */
 			uriname[1024],	/* Name for URI */
 			uri[1024];	/* Printer URI */
-    cups_ptype_t	type = CUPS_PRINTER_REMOTE | CUPS_PRINTER_BW;
+    cups_ptype_t	type = CUPS_PRINTER_DISCOVERED | CUPS_PRINTER_BW;
 					/* Printer type */
     int			saw_printer_type = 0;
 					/* Did we see a printer-type key? */
@@ -3398,7 +3285,7 @@
         */
 
 	saw_printer_type = 1;
-        type             = (cups_ptype_t)strtol(value, NULL, 0);
+        type             = (cups_ptype_t)strtol(value, NULL, 0) | CUPS_PRINTER_DISCOVERED;
       }
       else if (!saw_printer_type)
       {
@@ -3454,8 +3341,6 @@
     * 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));
@@ -3543,9 +3428,9 @@
   * Save the resolved URI...
   */
 
-  dest->num_options = cupsAddOption("resolved-device-uri", uri, dest->num_options, &dest->options);
+  dest->num_options = cupsAddOption("device-uri", uri, dest->num_options, &dest->options);
 
-  return (cupsGetOption("resolved-device-uri", dest->num_options, dest->options));
+  return (cupsGetOption("device-uri", dest->num_options, dest->options));
 }
 
 
@@ -3620,6 +3505,476 @@
 #endif /* HAVE_DNSSD */
 
 
+#if defined(HAVE_AVAHI) || defined(HAVE_DNSSD)
+/*
+ * 'cups_elapsed()' - Return the elapsed time in milliseconds.
+ */
+
+static int				/* O  - Elapsed time in milliseconds */
+cups_elapsed(struct timeval *t)		/* IO - Previous time */
+{
+  int			msecs;		/* Milliseconds */
+  struct timeval	nt;		/* New time */
+
+
+  gettimeofday(&nt, NULL);
+
+  msecs = (int)(1000 * (nt.tv_sec - t->tv_sec) + (nt.tv_usec - t->tv_usec) / 1000);
+
+  *t = nt;
+
+  return (msecs);
+}
+#endif /* HAVE_AVAHI || HAVE_DNSSD */
+
+
+/*
+ * 'cups_enum_dests()' - Enumerate destinations from a specific server.
+ */
+
+static int                              /* O - 1 on success, 0 on failure */
+cups_enum_dests(
+  http_t         *http,                 /* I - Connection to scheduler */
+  unsigned       flags,                 /* I - Enumeration flags */
+  int            msec,                  /* I - Timeout in milliseconds, -1 for indefinite */
+  int            *cancel,               /* I - Pointer to "cancel" variable */
+  cups_ptype_t   type,                  /* I - Printer type bits */
+  cups_ptype_t   mask,                  /* I - Mask for printer type bits */
+  cups_dest_cb_t cb,                    /* I - Callback function */
+  void           *user_data)            /* I - User data */
+{
+  int           i,                      /* Looping var */
+                num_dests;              /* Number of destinations */
+  cups_dest_t   *dests = NULL,          /* Destinations */
+                *dest;                  /* Current destination */
+  const char    *defprinter;            /* Default printer */
+  char          name[1024],             /* Copy of printer name */
+                *instance,              /* Pointer to instance name */
+                *user_default;          /* User default printer */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  int           count,                  /* Number of queries started */
+                completed,              /* Number of completed queries */
+                remaining;              /* Remainder of timeout */
+  struct timeval curtime;               /* Current time */
+  _cups_dnssd_data_t data;              /* Data for callback */
+  _cups_dnssd_device_t *device;         /* Current device */
+#  ifdef HAVE_DNSSD
+  int           nfds,                   /* Number of files responded */
+                main_fd;                /* File descriptor for lookups */
+  DNSServiceRef ipp_ref = NULL,         /* IPP browser */
+                local_ipp_ref = NULL;   /* Local IPP browser */
+#    ifdef HAVE_SSL
+  DNSServiceRef ipps_ref = NULL,        /* IPPS browser */
+                local_ipps_ref = NULL;  /* Local IPPS browser */
+#    endif /* HAVE_SSL */
+#    ifdef HAVE_POLL
+  struct pollfd pfd;                    /* Polling data */
+#    else
+  fd_set        input;                  /* Input set for select() */
+  struct timeval timeout;               /* Timeout for select() */
+#    endif /* HAVE_POLL */
+#  else /* HAVE_AVAHI */
+  int           error;                  /* Error value */
+  AvahiServiceBrowser *ipp_ref = NULL;  /* IPP browser */
+#    ifdef HAVE_SSL
+  AvahiServiceBrowser *ipps_ref = NULL; /* IPPS browser */
+#    endif /* HAVE_SSL */
+#  endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+
+  DEBUG_printf(("cups_enum_dests(flags=%x, msec=%d, cancel=%p, type=%x, mask=%x, cb=%p, user_data=%p)", flags, msec, (void *)cancel, type, mask, (void *)cb, (void *)user_data));
+
+ /*
+  * Range check input...
+  */
+
+  (void)flags;
+
+  if (!cb)
+  {
+    DEBUG_puts("1cups_enum_dests: No callback, returning 0.");
+    return (0);
+  }
+
+ /*
+  * Get ready to enumerate...
+  */
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  memset(&data, 0, sizeof(data));
+
+  data.type      = type;
+  data.mask      = mask;
+  data.cb        = cb;
+  data.user_data = user_data;
+  data.devices   = cupsArrayNew3((cups_array_func_t)cups_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)cups_dnssd_free_device);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+  if (!(mask & CUPS_PRINTER_DISCOVERED) || !(type & CUPS_PRINTER_DISCOVERED))
+  {
+   /*
+    * Get the list of local printers and pass them to the callback function...
+    */
+
+    num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &dests, type, mask);
+
+    if ((user_default = _cupsUserDefault(name, sizeof(name))) != NULL)
+      defprinter = name;
+    else if ((defprinter = cupsGetDefault2(http)) != NULL)
+    {
+      strlcpy(name, defprinter, sizeof(name));
+      defprinter = name;
+    }
+
+    if (defprinter)
+    {
+     /*
+      * Separate printer and instance name...
+      */
+
+      if ((instance = strchr(name, '/')) != NULL)
+        *instance++ = '\0';
+
+     /*
+      * Lookup the printer and instance and make it the default...
+      */
+
+      if ((dest = cupsGetDest(name, instance, num_dests, dests)) != NULL)
+        dest->is_default = 1;
+    }
+
+    for (i = num_dests, dest = dests;
+         i > 0 && (!cancel || !*cancel);
+         i --, dest ++)
+    {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      const char *device_uri;    /* Device URI */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+      if (!(*cb)(user_data, i > 1 ? CUPS_DEST_FLAGS_MORE : CUPS_DEST_FLAGS_NONE, dest))
+        break;
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      if (!dest->instance && (device_uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL && !strncmp(device_uri, "dnssd://", 8))
+      {
+       /*
+        * Add existing queue using service name, etc. so we don't list it again...
+        */
+
+        char    scheme[32],             /* URI scheme */
+                userpass[32],           /* Username:password */
+                serviceName[256],       /* Service name (host field) */
+                resource[256],          /* Resource (options) */
+                *regtype,               /* Registration type */
+                *replyDomain;           /* Registration domain */
+        int     port;                   /* Port number (not used) */
+
+        if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), serviceName, sizeof(serviceName), &port, resource, sizeof(resource)) >= HTTP_URI_STATUS_OK)
+        {
+          if ((regtype = strstr(serviceName, "._ipp")) != NULL)
+          {
+            *regtype++ = '\0';
+
+            if ((replyDomain = strstr(regtype, "._tcp.")) != NULL)
+            {
+              replyDomain[5] = '\0';
+              replyDomain += 6;
+
+              if ((device = cups_dnssd_get_device(&data, serviceName, regtype, replyDomain)) != NULL)
+                device->state = _CUPS_DNSSD_ACTIVE;
+            }
+          }
+        }
+      }
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+    }
+
+    cupsFreeDests(num_dests, dests);
+
+    if (i > 0 || msec == 0)
+      goto enum_finished;
+  }
+
+ /*
+  * Return early if the caller doesn't want to do discovery...
+  */
+
+  if ((mask & CUPS_PRINTER_DISCOVERED) && !(type & CUPS_PRINTER_DISCOVERED))
+    goto enum_finished;
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ /*
+  * Get Bonjour-shared printers...
+  */
+
+  gettimeofday(&curtime, NULL);
+
+#  ifdef HAVE_DNSSD
+  if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create service browser, returning 0.");
+    return (0);
+  }
+
+  main_fd = DNSServiceRefSockFD(data.main_ref);
+
+  ipp_ref = data.main_ref;
+  if (DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0, "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create IPP browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+
+  local_ipp_ref = data.main_ref;
+  if (DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_local_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create local IPP browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+
+#    ifdef HAVE_SSL
+  ipps_ref = data.main_ref;
+  if (DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0, "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create IPPS browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+
+  local_ipps_ref = data.main_ref;
+  if (DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_local_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create local IPPS browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+#    endif /* HAVE_SSL */
+
+#  else /* HAVE_AVAHI */
+  if ((data.simple_poll = avahi_simple_poll_new()) == NULL)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create Avahi poll, returning 0.");
+    return (0);
+  }
+
+  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("1cups_enum_dests: Unable to create Avahi client, returning 0.");
+    avahi_simple_poll_free(data.simple_poll);
+    return (0);
+  }
+
+  data.browsers = 1;
+  if ((ipp_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ipp._tcp", NULL, 0, cups_dnssd_browse_cb, &data)) == NULL)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create Avahi IPP browser, returning 0.");
+
+    avahi_client_free(data.client);
+    avahi_simple_poll_free(data.simple_poll);
+    return (0);
+  }
+
+#    ifdef HAVE_SSL
+  data.browsers ++;
+  if ((ipps_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ipps._tcp", NULL, 0, cups_dnssd_browse_cb, &data)) == NULL)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create Avahi IPPS browser, returning 0.");
+
+    avahi_service_browser_free(ipp_ref);
+    avahi_client_free(data.client);
+    avahi_simple_poll_free(data.simple_poll);
+    return (0);
+  }
+#    endif /* HAVE_SSL */
+#  endif /* HAVE_DNSSD */
+
+  if (msec < 0)
+    remaining = INT_MAX;
+  else
+    remaining = msec;
+
+  while (remaining > 0 && (!cancel || !*cancel))
+  {
+   /*
+    * Check for input...
+    */
+
+    DEBUG_printf(("1cups_enum_dests: remaining=%d", remaining));
+
+    cups_elapsed(&curtime);
+
+#  ifdef HAVE_DNSSD
+#    ifdef HAVE_POLL
+    pfd.fd     = main_fd;
+    pfd.events = POLLIN;
+
+    nfds = poll(&pfd, 1, remaining > _CUPS_DNSSD_MAXTIME ? _CUPS_DNSSD_MAXTIME : remaining);
+
+#    else
+    FD_ZERO(&input);
+    FD_SET(main_fd, &input);
+
+    timeout.tv_sec  = 0;
+    timeout.tv_usec = 1000 * (remaining > _CUPS_DNSSD_MAXTIME ? _CUPS_DNSSD_MAXTIME : remaining);
+
+    nfds = select(main_fd + 1, &input, NULL, NULL, &timeout);
+#    endif /* HAVE_POLL */
+
+    if (nfds > 0)
+      DNSServiceProcessResult(data.main_ref);
+    else if (nfds < 0 && errno != EINTR && errno != EAGAIN)
+      break;
+
+#  else /* HAVE_AVAHI */
+    data.got_data = 0;
+
+    if ((error = avahi_simple_poll_iterate(data.simple_poll, _CUPS_DNSSD_MAXTIME)) > 0)
+    {
+     /*
+      * We've been told to exit the loop.  Perhaps the connection to
+      * Avahi failed.
+      */
+
+      break;
+    }
+
+    DEBUG_printf(("1cups_enum_dests: got_data=%d", data.got_data));
+#  endif /* HAVE_DNSSD */
+
+    remaining -= cups_elapsed(&curtime);
+
+    for (device = (_cups_dnssd_device_t *)cupsArrayFirst(data.devices),
+             count = 0, completed = 0;
+         device;
+         device = (_cups_dnssd_device_t *)cupsArrayNext(data.devices))
+    {
+      if (device->ref)
+        count ++;
+
+      if (device->state == _CUPS_DNSSD_ACTIVE)
+        completed ++;
+
+      if (!device->ref && device->state == _CUPS_DNSSD_NEW)
+      {
+        DEBUG_printf(("1cups_enum_dests: Querying '%s'.", device->fullName));
+
+#  ifdef HAVE_DNSSD
+        device->ref = data.main_ref;
+
+        if (DNSServiceQueryRecord(&(device->ref), kDNSServiceFlagsShareConnection, 0, device->fullName, kDNSServiceType_TXT, kDNSServiceClass_IN, (DNSServiceQueryRecordReply)cups_dnssd_query_cb, &data) == kDNSServiceErr_NoError)
+        {
+          count ++;
+        }
+        else
+        {
+          device->ref   = 0;
+          device->state = _CUPS_DNSSD_ERROR;
+
+          DEBUG_puts("1cups_enum_dests: 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)
+        {
+          DEBUG_printf(("1cups_enum_dests: Query ref=%p", device->ref));
+          count ++;
+        }
+        else
+        {
+          device->state = _CUPS_DNSSD_ERROR;
+
+          DEBUG_printf(("1cups_enum_dests: Query failed: %s", avahi_strerror(avahi_client_errno(data.client))));
+        }
+#  endif /* HAVE_DNSSD */
+      }
+      else if (device->ref && device->state == _CUPS_DNSSD_PENDING)
+      {
+        completed ++;
+
+        DEBUG_printf(("1cups_enum_dests: Query for \"%s\" is complete.", device->fullName));
+
+        if ((device->type & mask) == type)
+        {
+          DEBUG_printf(("1cups_enum_dests: Add callback for \"%s\".", device->dest.name));
+          if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest))
+          {
+            remaining = -1;
+            break;
+          }
+        }
+
+        device->state = _CUPS_DNSSD_ACTIVE;
+      }
+    }
+
+#  ifdef HAVE_AVAHI
+    DEBUG_printf(("1cups_enum_dests: remaining=%d, browsers=%d, completed=%d, count=%d, devices count=%d", remaining, data.browsers, completed, count, cupsArrayCount(data.devices)));
+
+    if (data.browsers == 0 && completed == cupsArrayCount(data.devices))
+      break;
+#  else
+    DEBUG_printf(("1cups_enum_dests: remaining=%d, completed=%d, count=%d, devices count=%d", remaining, completed, count, cupsArrayCount(data.devices)));
+
+    if (completed == cupsArrayCount(data.devices))
+      break;
+#  endif /* HAVE_AVAHI */
+  }
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ /*
+  * Return...
+  */
+
+  enum_finished:
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  cupsArrayDelete(data.devices);
+
+#  ifdef HAVE_DNSSD
+  if (ipp_ref)
+    DNSServiceRefDeallocate(ipp_ref);
+  if (local_ipp_ref)
+    DNSServiceRefDeallocate(local_ipp_ref);
+
+#    ifdef HAVE_SSL
+  if (ipps_ref)
+    DNSServiceRefDeallocate(ipps_ref);
+  if (local_ipps_ref)
+    DNSServiceRefDeallocate(local_ipps_ref);
+#    endif /* HAVE_SSL */
+
+  if (data.main_ref)
+    DNSServiceRefDeallocate(data.main_ref);
+
+#  else /* HAVE_AVAHI */
+  if (ipp_ref)
+    avahi_service_browser_free(ipp_ref);
+#    ifdef HAVE_SSL
+  if (ipps_ref)
+    avahi_service_browser_free(ipps_ref);
+#    endif /* HAVE_SSL */
+
+  if (data.client)
+    avahi_client_free(data.client);
+  if (data.simple_poll)
+    avahi_simple_poll_free(data.simple_poll);
+#  endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+  DEBUG_puts("1cups_enum_dests: Returning 1.");
+
+  return (1);
+}
+
+
 /*
  * 'cups_find_dest()' - Find a destination using a binary search.
  */
@@ -3724,6 +4079,36 @@
 
 
 /*
+ * 'cups_get_cb()' - Collect enumerated destinations.
+ */
+
+static int                              /* O - 1 to continue, 0 to stop */
+cups_get_cb(_cups_getdata_t *data,      /* I - Data from cupsGetDests */
+            unsigned        flags,      /* I - Enumeration flags */
+            cups_dest_t     *dest)      /* I - Destination */
+{
+  if (flags & CUPS_DEST_FLAGS_REMOVED)
+  {
+   /*
+    * Remove destination from array...
+    */
+
+    data->num_dests = cupsRemoveDest(dest->name, dest->instance, data->num_dests, &data->dests);
+  }
+  else
+  {
+   /*
+    * Add destination to array...
+    */
+
+    data->num_dests = cupsCopyDest(dest, data->num_dests, &data->dests);
+  }
+
+  return (1);
+}
+
+
+/*
  * 'cups_get_default()' - Get the default destination from an lpoptions file.
  */
 
@@ -4027,3 +4412,58 @@
 
   return (buffer);
 }
+
+
+/*
+ * 'cups_name_cb()' - Find an enumerated destination.
+ */
+
+static int                              /* O - 1 to continue, 0 to stop */
+cups_name_cb(_cups_namedata_t *data,    /* I - Data from cupsGetNamedDest */
+             unsigned         flags,    /* I - Enumeration flags */
+             cups_dest_t      *dest)    /* I - Destination */
+{
+  DEBUG_printf(("2cups_name_cb(data=%p(%s), flags=%x, dest=%p(%s)", (void *)data, data->name, flags, (void *)dest, dest->name));
+
+  if (!(flags & CUPS_DEST_FLAGS_REMOVED) && !dest->instance && !strcasecmp(data->name, dest->name))
+  {
+   /*
+    * Copy destination and stop enumeration...
+    */
+
+    cupsCopyDest(dest, 0, &data->dest);
+    return (0);
+  }
+
+  return (1);
+}
+
+
+/*
+ * 'cups_queue_name()' - Create a local queue name based on the service name.
+ */
+
+static void
+cups_queue_name(
+    char       *name,			/* I - Name buffer */
+    const char *serviceName,		/* I - Service name */
+    size_t     namesize)		/* I - Size of name buffer */
+{
+  const char	*ptr;			/* Pointer into serviceName */
+  char		*nameptr;		/* Pointer into name */
+
+
+  for (nameptr = name, ptr = serviceName; *ptr && nameptr < (name + namesize - 1); ptr ++)
+  {
+   /*
+    * Sanitize the printer name...
+    */
+
+    if (_cups_isalnum(*ptr))
+      *nameptr++ = *ptr;
+    else if (nameptr == name || nameptr[-1] != '_')
+      *nameptr++ = '_';
+  }
+
+  *nameptr = '\0';
+}
diff --git a/cups/dir.c b/cups/dir.c
index 65b8c4f..b7cd400 100644
--- a/cups/dir.c
+++ b/cups/dir.c
@@ -3,7 +3,7 @@
  *
  * This set of APIs abstracts enumeration of directory entries.
  *
- * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2005 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -338,10 +338,6 @@
 {
   struct dirent	*entry;			/* Pointer to entry */
   char		filename[1024];		/* Full filename */
-#  ifdef HAVE_PTHREAD_H
-  char		buffer[sizeof(struct dirent) + 1024];
-					/* Directory entry buffer */
-#  endif /* HAVE_PTHREAD_H */
 
 
   DEBUG_printf(("2cupsDirRead(dp=%p)", (void *)dp));
@@ -359,29 +355,8 @@
 
   for (;;)
   {
-#  ifdef HAVE_PTHREAD_H
    /*
-    * Read the next entry using the reentrant version of readdir...
-    */
-
-    if (readdir_r(dp->dir, (struct dirent *)buffer, &entry))
-    {
-      DEBUG_printf(("3cupsDirRead: readdir_r() failed - %s\n", strerror(errno)));
-      return (NULL);
-    }
-
-    if (!entry)
-    {
-      DEBUG_puts("3cupsDirRead: readdir_r() returned a NULL pointer!");
-      return (NULL);
-    }
-
-    DEBUG_printf(("4cupsDirRead: readdir_r() returned \"%s\"...",
-                  entry->d_name));
-
-#  else
-   /*
-    * Read the next entry using the original version of readdir...
+    * Read the next entry...
     */
 
     if ((entry = readdir(dp->dir)) == NULL)
@@ -392,8 +367,6 @@
 
     DEBUG_printf(("4cupsDirRead: readdir() returned \"%s\"...", entry->d_name));
 
-#  endif /* HAVE_PTHREAD_H */
-
    /*
     * Skip "." and ".."...
     */
diff --git a/cups/encode.c b/cups/encode.c
index e60aec0..94695d0 100644
--- a/cups/encode.c
+++ b/cups/encode.c
@@ -1,7 +1,7 @@
 /*
  * Option encoding routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -329,7 +329,7 @@
  * 'cupsEncodeOptions()' - Encode printer options into IPP attributes.
  *
  * This function adds operation, job, and then subscription attributes,
- * in that order. Use the cupsEncodeOptions2() function to add attributes
+ * in that order. Use the @link cupsEncodeOptions2@ function to add attributes
  * for a single group.
  */
 
@@ -354,7 +354,7 @@
  * 'cupsEncodeOptions2()' - Encode printer options into IPP attributes for a group.
  *
  * This function only adds attributes for a single group. Call this
- * function multiple times for each group, or use cupsEncodeOptions()
+ * function multiple times for each group, or use @link cupsEncodeOptions@
  * to add the standard groups.
  *
  * @since CUPS 1.2/macOS 10.5@
diff --git a/cups/file-private.h b/cups/file-private.h
index b8ca431..1f4db79 100644
--- a/cups/file-private.h
+++ b/cups/file-private.h
@@ -4,9 +4,9 @@
  * Since stdio files max out at 256 files on many systems, we have to
  * write similar functions without this limit.  At the same time, using
  * our own file functions allows us to provide transparent support of
- * gzip'd print files, PPD files, etc.
+ * different line endings, gzip'd print files, PPD files, etc.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
diff --git a/cups/file.c b/cups/file.c
index a027df4..5d15054 100644
--- a/cups/file.c
+++ b/cups/file.c
@@ -4,9 +4,9 @@
  * Since stdio files max out at 256 files on many systems, we have to
  * write similar functions without this limit.  At the same time, using
  * our own file functions allows us to provide transparent support of
- * gzip'd print files, PPD files, etc.
+ * different line endings, gzip'd print files, PPD files, etc.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -321,7 +321,6 @@
   int	fd;				/* File descriptor */
   char	mode;				/* Open mode */
   int	status;				/* Return status */
-  int	is_stdio;			/* Is a stdio file? */
 
 
   DEBUG_printf(("cupsFileClose(fp=%p)", (void *)fp));
@@ -410,12 +409,19 @@
 #endif /* HAVE_LIBZ */
 
  /*
+  * If this is one of the cupsFileStdin/out/err files, return now and don't
+  * actually free memory or close (these last the life of the process...)
+  */
+
+  if (fp->is_stdio)
+    return (status);
+
+/*
   * Save the file descriptor we used and free memory...
   */
 
-  fd       = fp->fd;
-  mode     = fp->mode;
-  is_stdio = fp->is_stdio;
+  fd   = fp->fd;
+  mode = fp->mode;
 
   if (fp->printf_buffer)
     free(fp->printf_buffer);
@@ -431,11 +437,8 @@
     if (httpAddrClose(NULL, fd) < 0)
       status = -1;
   }
-  else if (!is_stdio)
-  {
-    if (close(fd) < 0)
-      status = -1;
-  }
+  else if (close(fd) < 0)
+    status = -1;
 
   return (status);
 }
@@ -1384,7 +1387,11 @@
   {
     memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
     fp->ptr += bytes;
-    return ((int)bytes);
+
+    if (fp->is_stdio && cupsFileFlush(fp))
+      return (-1);
+    else
+      return ((int)bytes);
   }
 }
 
@@ -1563,7 +1570,11 @@
   {
     memcpy(fp->ptr, s, (size_t)bytes);
     fp->ptr += bytes;
-    return ((int)bytes);
+
+    if (fp->is_stdio && cupsFileFlush(fp))
+      return (-1);
+    else
+      return ((int)bytes);
   }
 }
 
@@ -2466,6 +2477,8 @@
 	* file header...
 	*/
 
+        inflateEnd(&fp->stream);
+
 	fp->compressed = 0;
       }
       else if (status < Z_OK)
diff --git a/cups/file.h b/cups/file.h
index 177c2e9..42abe20 100644
--- a/cups/file.h
+++ b/cups/file.h
@@ -4,9 +4,9 @@
  * Since stdio files max out at 256 files on many systems, we have to
  * write similar functions without this limit.  At the same time, using
  * our own file functions allows us to provide transparent support of
- * gzip'd print files, PPD files, etc.
+ * different line endings, gzip'd print files, PPD files, etc.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
diff --git a/cups/http-addr.c b/cups/http-addr.c
index 12d13a6..61c8638 100644
--- a/cups/http-addr.c
+++ b/cups/http-addr.c
@@ -58,9 +58,9 @@
  * 'httpAddrClose()' - Close a socket created by @link httpAddrConnect@ or
  *                     @link httpAddrListen@.
  *
- * Pass @code NULL@ for sockets created with @link httpAddrConnect@ and the
- * listen address for sockets created with @link httpAddrListen@. This will
- * ensure that domain sockets are removed when closed.
+ * Pass @code NULL@ for sockets created with @link httpAddrConnect2@ and the
+ * listen address for sockets created with @link httpAddrListen@.  This function
+ * ensures that domain sockets are removed when closed.
  *
  * @since CUPS 2.0/OS 10.10@
  */
@@ -648,6 +648,10 @@
 /*
  * 'httpGetAddress()' - Get the address of the connected peer of a connection.
  *
+ * For connections created with @link httpConnect2@, the address is for the
+ * server.  For connections created with @link httpAccept@, the address is for
+ * the client.
+ *
  * Returns @code NULL@ if the socket is currently unconnected.
  *
  * @since CUPS 2.0/OS 10.10@
@@ -667,7 +671,7 @@
  * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return
  *                         address records for the specified name.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 struct hostent *			/* O - Host entry */
diff --git a/cups/http-addrlist.c b/cups/http-addrlist.c
index 723bf02..e5fc940 100644
--- a/cups/http-addrlist.c
+++ b/cups/http-addrlist.c
@@ -1,7 +1,7 @@
 /*
  * HTTP address list routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -32,7 +32,7 @@
 /*
  * 'httpAddrConnect()' - Connect to any of the addresses in the list.
  *
- * @since CUPS 1.2/macOS 10.5@
+ * @since CUPS 1.2/macOS 10.5@ @exclude all@
  */
 
 http_addrlist_t *			/* O - Connected address or NULL on failure */
@@ -65,7 +65,7 @@
   int			flags;		/* Socket flags */
 #endif /* !WIN32 */
   int			remaining;	/* Remaining timeout */
-  int			i,		/* Looping var */
+  int			i, j,		/* Looping vars */
 			nfds,		/* Number of file descriptors */
 			fds[100],	/* Socket file descriptors */
 			result;		/* Result from select() or poll() */
@@ -323,6 +323,8 @@
 	  if (!getpeername(fds[i], (struct sockaddr *)&peer, &len))
 	    DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer)));
 #  endif /* DEBUG */
+
+          break;
 	}
 #  ifdef HAVE_POLL
 	else if (pfds[i].revents & (POLLERR | POLLHUP))
@@ -346,7 +348,20 @@
       }
 
       if (connaddr)
+      {
+       /*
+        * Connected on one address, close all of the other sockets we have so
+        * far and return...
+        */
+
+        for (j = 0; j < i; j ++)
+          httpAddrClose(NULL, fds[j]);
+
+        for (j ++; j < nfds; j ++)
+          httpAddrClose(NULL, fds[j]);
+
         return (connaddr);
+      }
     }
 #endif /* O_NONBLOCK */
 
diff --git a/cups/http-private.h b/cups/http-private.h
index ec908a6..f71e564 100644
--- a/cups/http-private.h
+++ b/cups/http-private.h
@@ -1,7 +1,7 @@
 /*
  * Private HTTP definitions for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -172,18 +172,20 @@
  * Constants...
  */
 
+#  define _HTTP_MAX_SBUFFER	65536	/* Size of (de)compression buffer */
+#  define _HTTP_RESOLVE_DEFAULT	0	/* Just resolve with default options */
+#  define _HTTP_RESOLVE_STDERR	1	/* Log resolve progress to stderr */
+#  define _HTTP_RESOLVE_FQDN	2	/* Resolve to a FQDN */
+#  define _HTTP_RESOLVE_FAXOUT	4	/* Resolve FaxOut service? */
 
-#define _HTTP_MAX_SBUFFER	65536	/* Size of (de)compression buffer */
-#define _HTTP_RESOLVE_DEFAULT	0	/* Just resolve with default options */
-#define _HTTP_RESOLVE_STDERR	1	/* Log resolve progress to stderr */
-#define _HTTP_RESOLVE_FQDN	2	/* Resolve to a FQDN */
-#define _HTTP_RESOLVE_FAXOUT	4	/* Resolve FaxOut service? */
-
-#define _HTTP_TLS_NONE		0	/* No TLS options */
-#define _HTTP_TLS_ALLOW_RC4	1	/* Allow RC4 cipher suites */
-#define _HTTP_TLS_ALLOW_SSL3	2	/* Allow SSL 3.0 */
-#define _HTTP_TLS_ALLOW_DH	4	/* Allow DH/DHE key negotiation */
-#define _HTTP_TLS_DENY_TLS10	16	/* Deny TLS 1.0 */
+#  define _HTTP_TLS_NONE	0	/* No TLS options */
+#  define _HTTP_TLS_ALLOW_RC4	1	/* Allow RC4 cipher suites */
+#  define _HTTP_TLS_ALLOW_SSL3	2	/* Allow SSL 3.0 */
+#  define _HTTP_TLS_ALLOW_DH	4	/* Allow DH/DHE key negotiation */
+#  define _HTTP_TLS_DENY_TLS10	16	/* Deny TLS 1.0 */
+#  define _HTTP_TLS_DENY_CBC	32	/* Deny CBC cipher suites */
+#  define _HTTP_TLS_ONLY_TLS10  64      /* Only use TLS 1.0 */
+#  define _HTTP_TLS_SET_DEFAULT 128     /* Setting the default TLS options */
 
 
 /*
diff --git a/cups/http-support.c b/cups/http-support.c
index 1ca01b2..76dbb7d 100644
--- a/cups/http-support.c
+++ b/cups/http-support.c
@@ -1,7 +1,7 @@
 /*
  * HTTP support routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -543,7 +543,7 @@
  * This function is deprecated. Use the httpDecode64_2() function instead
  * which provides buffer length arguments.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 char *					/* O - Decoded string */
@@ -566,6 +566,10 @@
 /*
  * 'httpDecode64_2()' - Base64-decode a string.
  *
+ * The caller must initialize "outlen" to the maximum size of the decoded
+ * string before calling @code httpDecode64_2@.  On return "outlen" contains the
+ * decoded length of the string.
+ *
  * @since CUPS 1.1.21/macOS 10.4@
  */
 
@@ -671,7 +675,7 @@
  * This function is deprecated. Use the httpEncode64_2() function instead
  * which provides buffer length arguments.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 char *					/* O - Encoded string */
@@ -690,7 +694,7 @@
 
 char *					/* O - Encoded string */
 httpEncode64_2(char       *out,		/* I - String to write to */
-	       int        outlen,	/* I - Size of output string */
+	       int        outlen,	/* I - Maximum size of output string */
                const char *in,		/* I - String to read from */
 	       int        inlen)	/* I - Size of input string */
 {
@@ -778,11 +782,11 @@
 /*
  * 'httpGetDateString()' - Get a formatted date/time string from a time value.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 const char *				/* O - Date/time string */
-httpGetDateString(time_t t)		/* I - UNIX time */
+httpGetDateString(time_t t)		/* I - Time in seconds */
 {
   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
 
@@ -798,7 +802,7 @@
  */
 
 const char *				/* O - Date/time string */
-httpGetDateString2(time_t t,		/* I - UNIX time */
+httpGetDateString2(time_t t,		/* I - Time in seconds */
                    char   *s,		/* I - String buffer */
 		   int    slen)		/* I - Size of string buffer */
 {
@@ -819,7 +823,7 @@
  * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
  */
 
-time_t					/* O - UNIX time */
+time_t					/* O - Time in seconds */
 httpGetDateTime(const char *s)		/* I - Date/time string */
 {
   int		i;			/* Looping var */
@@ -888,7 +892,7 @@
  *
  * This function is deprecated; use the httpSeparateURI() function instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 void
@@ -912,7 +916,7 @@
  * This function is deprecated; use the httpSeparateURI() function instead.
  *
  * @since CUPS 1.1.21/macOS 10.4@
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 void
@@ -1419,7 +1423,7 @@
  * 'httpStatus()' - Return a short string describing a HTTP status code.
  *
  * The returned string is localized to the current POSIX locale and is based
- * on the status strings defined in RFC 2616.
+ * on the status strings defined in RFC 7231.
  */
 
 const char *				/* O - Localized status string */
@@ -2310,6 +2314,8 @@
  * Note: This function is needed because avahi_simple_poll_iterate is broken
  *       and always uses a timeout of 0 (!) milliseconds.
  *       (Avahi Ticket #364)
+ *
+ * @private@
  */
 
 static int				/* O - Number of file descriptors matching */
diff --git a/cups/http.c b/cups/http.c
index 7925513..61b88c9 100644
--- a/cups/http.c
+++ b/cups/http.c
@@ -1,7 +1,7 @@
 /*
  * HTTP routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * This file contains Kerberos support code, copyright 2006 by
@@ -417,7 +417,7 @@
  *
  * This function is deprecated - use @link httpConnect2@ instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 http_t *				/* O - New HTTP connection */
@@ -439,7 +439,7 @@
 httpConnect2(
     const char        *host,		/* I - Host to connect to */
     int               port,		/* I - Port number */
-    http_addrlist_t   *addrlist,	/* I - List of addresses or NULL to lookup */
+    http_addrlist_t   *addrlist,	/* I - List of addresses or @code NULL@ to lookup */
     int               family,		/* I - Address family to use or @code AF_UNSPEC@ for any */
     http_encryption_t encryption,	/* I - Type of encryption to use */
     int               blocking,		/* I - 1 for blocking connection, 0 for non-blocking */
@@ -482,7 +482,7 @@
  * This function is now deprecated. Please use the @link httpConnect2@ function
  * instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 http_t *				/* O - New HTTP connection */
@@ -609,7 +609,7 @@
 
 
 /*
- * 'httpFlush()' - Flush data from a HTTP connection.
+ * 'httpFlush()' - Flush data read from a HTTP connection.
  */
 
 void
@@ -679,7 +679,7 @@
 
 
 /*
- * 'httpFlushWrite()' - Flush data in write buffer.
+ * 'httpFlushWrite()' - Flush data written to a HTTP connection.
  *
  * @since CUPS 1.2/macOS 10.5@
  */
@@ -751,7 +751,7 @@
 /*
  * 'httpGetActivity()' - Get the most recent activity for a connection.
  *
- * The return value is the UNIX time of the last read or write.
+ * The return value is the time in seconds of the last read or write.
  *
  * @since CUPS 2.0/OS 10.10@
  */
@@ -766,10 +766,10 @@
 /*
  * 'httpGetAuthString()' - Get the current authorization string.
  *
- * The authorization string is set by cupsDoAuthentication() and
- * httpSetAuthString().  Use httpGetAuthString() to retrieve the
- * string to use with httpSetField() for the HTTP_FIELD_AUTHORIZATION
- * value.
+ * The authorization string is set by @link cupsDoAuthentication@ and
+ * @link httpSetAuthString@.  Use @link httpGetAuthString@ to retrieve the
+ * string to use with @link httpSetField@ for the
+ * @code HTTP_FIELD_AUTHORIZATION@ value.
  *
  * @since CUPS 1.3/macOS 10.5@
  */
@@ -891,7 +891,7 @@
  * @since CUPS 1.1.19/macOS 10.3@
  */
 
-const char *				/* O - Cookie data or NULL */
+const char *				/* O - Cookie data or @code NULL@ */
 httpGetCookie(http_t *http)		/* I - HTTP connection */
 {
   return (http ? http->cookie : NULL);
@@ -1006,7 +1006,7 @@
  * This function is deprecated and will not return lengths larger than
  * 2^31 - 1; use httpGetLength2() instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Content length */
@@ -1150,7 +1150,7 @@
  * 'httpGets()' - Get a line of text from a HTTP connection.
  */
 
-char *					/* O - Line or NULL */
+char *					/* O - Line or @code NULL@ */
 httpGets(char   *line,			/* I - Line to read into */
          int    length,			/* I - Max length of buffer */
 	 http_t *http)			/* I - HTTP connection */
@@ -1346,10 +1346,10 @@
 /*
  * 'httpGetSubField()' - Get a sub-field value.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
-char *					/* O - Value or NULL */
+char *					/* O - Value or @code NULL@ */
 httpGetSubField(http_t       *http,	/* I - HTTP connection */
                 http_field_t field,	/* I - Field index */
                 const char   *name,	/* I - Name of sub-field */
@@ -1365,7 +1365,7 @@
  * @since CUPS 1.2/macOS 10.5@
  */
 
-char *					/* O - Value or NULL */
+char *					/* O - Value or @code NULL@ */
 httpGetSubField2(http_t       *http,	/* I - HTTP connection */
                  http_field_t field,	/* I - Field index */
                  const char   *name,	/* I - Name of sub-field */
@@ -1616,7 +1616,7 @@
  *
  * This function copies available data from the given HTTP connection, reading
  * a buffer as needed.  The data is still available for reading using
- * @link httpRead@ or @link httpRead2@.
+ * @link httpRead2@.
  *
  * For non-blocking connections the usual timeouts apply.
  *
@@ -1939,7 +1939,7 @@
  * This function is deprecated. Use the httpRead2() function which can
  * read more than 2GB of data.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of bytes read */
@@ -2326,7 +2326,7 @@
  * This function is deprecated. Please use the @link httpReconnect2@ function
  * instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - 0 on success, non-zero on failure */
@@ -2467,9 +2467,10 @@
  * 'httpSetAuthString()' - Set the current authorization string.
  *
  * This function just stores a copy of the current authorization string in
- * the HTTP connection object.  You must still call httpSetField() to set
- * HTTP_FIELD_AUTHORIZATION prior to issuing a HTTP request using httpGet(),
- * httpHead(), httpOptions(), httpPost, or httpPut().
+ * the HTTP connection object.  You must still call @link httpSetField@ to set
+ * @code HTTP_FIELD_AUTHORIZATION@ prior to issuing a HTTP request using
+ * @link httpGet@, @link httpHead@, @link httpOptions@, @link httpPost@, or
+ * @link httpPut@.
  *
  * @since CUPS 1.3/macOS 10.5@
  */
@@ -2820,7 +2821,7 @@
     http_t            *http,		/* I - HTTP connection */
     double            timeout,		/* I - Number of seconds for timeout,
                                                must be greater than 0 */
-    http_timeout_cb_t cb,		/* I - Callback function or NULL */
+    http_timeout_cb_t cb,		/* I - Callback function or @code NULL@ */
     void              *user_data)	/* I - User data pointer */
 {
   if (!http || timeout <= 0.0)
@@ -2864,6 +2865,8 @@
 
 /*
  * 'httpTrace()' - Send an TRACE request to the server.
+ *
+ * @exclude all@
  */
 
 int					/* O - Status of call (0 = success) */
@@ -3259,7 +3262,7 @@
  * This function is deprecated. Use the httpWrite2() function which can
  * write more than 2GB of data.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of bytes written */
@@ -3898,7 +3901,7 @@
 http_create(
     const char        *host,		/* I - Hostname */
     int               port,		/* I - Port number */
-    http_addrlist_t   *addrlist,	/* I - Address list or NULL */
+    http_addrlist_t   *addrlist,	/* I - Address list or @code NULL@ */
     int               family,		/* I - Address family or AF_UNSPEC */
     http_encryption_t encryption,	/* I - Encryption to use */
     int               blocking,		/* I - 1 for blocking mode */
diff --git a/cups/http.h b/cups/http.h
index ccbf77e..c61a79e 100644
--- a/cups/http.h
+++ b/cups/http.h
@@ -1,7 +1,7 @@
 /*
  * Hyper-Text Transport Protocol definitions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -109,7 +109,7 @@
  * Types and structures...
  */
 
-typedef enum http_auth_e		/**** HTTP authentication types ****/
+typedef enum http_auth_e		/**** HTTP authentication types @exclude all@ ****/
 {
   HTTP_AUTH_NONE,			/* No authentication in use */
   HTTP_AUTH_BASIC,			/* Basic authentication in use */
@@ -393,7 +393,7 @@
   HTTP_URI_CODING_RFC6874 = 16		/* Use RFC 6874 address format */
 } http_uri_coding_t;
 
-typedef enum http_version_e		/**** HTTP version numbers ****/
+typedef enum http_version_e		/**** HTTP version numbers @exclude all@ ****/
 {
   HTTP_VERSION_0_9 = 9,			/* HTTP/0.9 */
   HTTP_VERSION_1_0 = 100,		/* HTTP/1.0 */
@@ -427,6 +427,7 @@
 					 **** used to enumerate all of the
 					 **** addresses that are associated
 					 **** with a hostname. @since CUPS 1.2/macOS 10.5@
+                                         **** @exclude all@
 					 ****/
 {
   struct http_addrlist_s *next;		/* Pointer to next address in list */
@@ -435,7 +436,7 @@
 
 typedef struct _http_s http_t;		/**** HTTP connection type ****/
 
-typedef struct http_credential_s	/**** HTTP credential data @since CUPS 1.5/macOS 10.7@ ****/
+typedef struct http_credential_s	/**** HTTP credential data @since CUPS 1.5/macOS 10.7@ @exclude all@ ****/
 {
   void		*data;			/* Pointer to credential data */
   size_t	datalen;		/* Credential length */
diff --git a/cups/ipp-support.c b/cups/ipp-support.c
index b49ac0d..675e5f3 100644
--- a/cups/ipp-support.c
+++ b/cups/ipp-support.c
@@ -1,7 +1,7 @@
 /*
  * Internet Printing Protocol support functions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -2243,7 +2243,7 @@
 /*
  * 'ippTagString()' - Return the tag name corresponding to a tag value.
  *
- * The returned names are defined in RFC 2911 and 3382.
+ * The returned names are defined in RFC 8011 and the IANA IPP Registry.
  *
  * @since CUPS 1.4/macOS 10.6@
  */
@@ -2263,7 +2263,7 @@
 /*
  * 'ippTagValue()' - Return the tag value corresponding to a tag name.
  *
- * The tag names are defined in RFC 2911 and 3382.
+ * The tag names are defined in RFC 8011 and the IANA IPP Registry.
  *
  * @since CUPS 1.4/macOS 10.6@
  */
diff --git a/cups/ipp.c b/cups/ipp.c
index 817c9d5..772b2f0 100644
--- a/cups/ipp.c
+++ b/cups/ipp.c
@@ -316,7 +316,7 @@
 
 
 /*
- * 'ippAddDate()' - Add a date attribute to an IPP message.
+ * 'ippAddDate()' - Add a dateTime attribute to an IPP message.
  *
  * The @code ipp@ parameter refers to an IPP message previously created using
  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
@@ -1380,7 +1380,7 @@
  *                         specified string value.
  *
  * Returns non-zero when the attribute contains a matching charset, keyword,
- * language, mimeMediaType, name, text, URI, or URI scheme value.
+ * naturalLanguage, mimeMediaType, name, text, uri, or uriScheme value.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -1509,6 +1509,16 @@
         dstattr = ippAddSeparator(dst);
 	break;
 
+    case IPP_TAG_UNSUPPORTED_VALUE :
+    case IPP_TAG_DEFAULT :
+    case IPP_TAG_UNKNOWN :
+    case IPP_TAG_NOVALUE :
+    case IPP_TAG_NOTSETTABLE :
+    case IPP_TAG_DELETEATTR :
+    case IPP_TAG_ADMINDEFINE :
+        dstattr = ippAddOutOfBand(dst, srcattr->group_tag, srcattr->value_tag & ~IPP_TAG_CUPS_CONST, srcattr->name);
+        break;
+
     case IPP_TAG_INTEGER :
     case IPP_TAG_ENUM :
         dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
@@ -1759,12 +1769,12 @@
 
 
 /*
- * 'ippDateToTime()' - Convert from RFC 1903 Date/Time format to UNIX time
- *                     in seconds.
+ * 'ippDateToTime()' - Convert from RFC 2579 Date/Time format to time in
+ *                     seconds.
  */
 
 time_t					/* O - UNIX time value */
-ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 1903 date info */
+ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 2579 date info */
 {
   struct tm	unixdate;		/* UNIX date/time info */
   time_t	t;			/* Computed time */
@@ -1776,7 +1786,7 @@
   memset(&unixdate, 0, sizeof(unixdate));
 
  /*
-  * RFC-1903 date/time format is:
+  * RFC-2579 date/time format is:
   *
   *    Byte(s)  Description
   *    -------  -----------
@@ -1828,12 +1838,19 @@
 
   ipp->use --;
   if (ipp->use > 0)
+  {
+    DEBUG_printf(("4debug_retain: %p IPP message (use=%d)", (void *)ipp, ipp->use));
     return;
+  }
+
+  DEBUG_printf(("4debug_free: %p IPP message", (void *)ipp));
 
   for (attr = ipp->attrs; attr != NULL; attr = next)
   {
     next = attr->next;
 
+    DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
+
     ipp_free_values(attr, 0, attr->num_values);
 
     if (attr->name)
@@ -1870,6 +1887,8 @@
   if (!attr)
     return;
 
+  DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
+
  /*
   * Find the attribute in the list...
   */
@@ -2152,7 +2171,7 @@
  * 'ippGetBoolean()' - Get a boolean value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2181,7 +2200,7 @@
  * 'ippGetCollection()' - Get a collection value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2232,15 +2251,15 @@
 
 
 /*
- * 'ippGetDate()' - Get a date value for an attribute.
+ * 'ippGetDate()' - Get a dateTime value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
 
-const ipp_uchar_t *			/* O - Date value or @code NULL@ */
+const ipp_uchar_t *			/* O - dateTime value or @code NULL@ */
 ippGetDate(ipp_attribute_t *attr,	/* I - IPP attribute */
            int             element)	/* I - Value number (0-based) */
 {
@@ -2288,7 +2307,7 @@
  * 'ippGetInteger()' - Get the integer/enum value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2341,7 +2360,7 @@
  * 'ippGetOctetString()' - Get an octetString value from an IPP attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -2404,7 +2423,7 @@
  * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2466,7 +2485,7 @@
  * 'ippGetResolution()' - Get a resolution value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2560,7 +2579,7 @@
  * 'ippGetString()' - Get the string and optionally the language code for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2625,7 +2644,7 @@
 
 int					/* O - Major version number or 0 on error */
 ippGetVersion(ipp_t *ipp,		/* I - IPP message */
-              int   *minor)		/* O - Minor version number or @code NULL@ */
+              int   *minor)		/* O - Minor version number or @code NULL@ for don't care */
 {
  /*
   * Range check input...
@@ -2705,6 +2724,8 @@
     * Set default version - usually 2.0...
     */
 
+    DEBUG_printf(("4debug_alloc: %p IPP message", (void *)temp));
+
     if (cg->server_version == 0)
       _cupsSetDefaults();
 
@@ -2722,9 +2743,9 @@
 /*
  *  'ippNewRequest()' - Allocate a new IPP request message.
  *
- * The new request message is initialized with the attributes-charset and
- * attributes-natural-language attributes added. The
- * attributes-natural-language value is derived from the current locale.
+ * The new request message is initialized with the "attributes-charset" and
+ * "attributes-natural-language" attributes added. The
+ * "attributes-natural-language" value is derived from the current locale.
  *
  * @since CUPS 1.2/macOS 10.5@
  */
@@ -2786,11 +2807,11 @@
 /*
  * 'ippNewResponse()' - Allocate a new IPP response message.
  *
- * The new response message is initialized with the same version-number,
- * request-id, attributes-charset, and attributes-natural-language as the
- * provided request message.  If the attributes-charset or
- * attributes-natural-language attributes are missing from the request,
- * "utf-8" and a value derived from the current locale are substituted,
+ * The new response message is initialized with the same "version-number",
+ * "request-id", "attributes-charset", and "attributes-natural-language" as the
+ * provided request message.  If the "attributes-charset" or
+ * "attributes-natural-language" attributes are missing from the request,
+ * 'utf-8' and a value derived from the current locale are substituted,
  * respectively.
  *
  * @since CUPS 1.7/macOS 10.9@
@@ -3706,7 +3727,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3748,7 +3769,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3789,7 +3810,7 @@
 
 
 /*
- * 'ippSetDate()' - Set a date value in an attribute.
+ * 'ippSetDate()' - Set a dateTime value in an attribute.
  *
  * The @code ipp@ parameter refers to an IPP message previously created using
  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
@@ -3797,7 +3818,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3806,7 +3827,7 @@
 ippSetDate(ipp_t             *ipp,	/* I  - IPP message */
            ipp_attribute_t   **attr,	/* IO - IPP attribute */
            int               element,	/* I  - Value number (0-based) */
-           const ipp_uchar_t *datevalue)/* I  - Date value */
+           const ipp_uchar_t *datevalue)/* I  - dateTime value */
 {
   _ipp_value_t	*value;			/* Current value */
 
@@ -3854,7 +3875,7 @@
     ipp_tag_t       group_tag)		/* I  - Group tag */
 {
  /*
-  * Range check input - group tag must be 0x01 to 0x0F, per RFC 2911...
+  * Range check input - group tag must be 0x01 to 0x0F, per RFC 8011...
   */
 
   if (!ipp || !attr || !*attr ||
@@ -3881,7 +3902,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3966,7 +3987,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -4084,7 +4105,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -4164,7 +4185,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -4272,7 +4293,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -4334,7 +4355,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * The @code format@ parameter uses formatting characters compatible with the
  * printf family of standard functions.  Additional arguments follow it as
@@ -4372,7 +4393,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * The @code format@ parameter uses formatting characters compatible with the
  * printf family of standard functions.  Additional arguments follow it as
@@ -4715,19 +4736,19 @@
 
 
 /*
- * 'ippTimeToDate()' - Convert from UNIX time to RFC 1903 format.
+ * 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format.
  */
 
-const ipp_uchar_t *			/* O - RFC-1903 date/time data */
-ippTimeToDate(time_t t)			/* I - UNIX time value */
+const ipp_uchar_t *			/* O - RFC-2579 date/time data */
+ippTimeToDate(time_t t)			/* I - Time in seconds */
 {
   struct tm	*unixdate;		/* UNIX unixdate/time info */
   ipp_uchar_t	*date = _cupsGlobals()->ipp_date;
-					/* RFC-1903 date/time data */
+					/* RFC-2579 date/time data */
 
 
  /*
-  * RFC-1903 date/time format is:
+  * RFC-2579 date/time format is:
   *
   *    Byte(s)  Description
   *    -------  -----------
@@ -4767,7 +4788,7 @@
  *
  * This function validates the contents of an attribute based on the name and
  * value tag.  1 is returned if the attribute is valid, 0 otherwise.  On
- * failure, cupsLastErrorString() is set to a human-readable message.
+ * failure, @link cupsLastErrorString@ is set to a human-readable message.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -4823,7 +4844,7 @@
   {
     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
                   _("\"%s\": Bad attribute name - invalid character "
-		    "(RFC 2911 section 4.1.3)."), attr->name);
+		    "(RFC 8011 section 5.1.4)."), attr->name);
     return (0);
   }
 
@@ -4831,7 +4852,7 @@
   {
     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
                   _("\"%s\": Bad attribute name - bad length %d "
-		    "(RFC 2911 section 4.1.3)."), attr->name,
+		    "(RFC 8011 section 5.1.4)."), attr->name,
 		  (int)(ptr - attr->name));
     return (0);
   }
@@ -4849,7 +4870,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
                           _("\"%s\": Bad boolen value %d "
-			    "(RFC 2911 section 4.1.11)."), attr->name,
+			    "(RFC 8011 section 5.1.21)."), attr->name,
 			  attr->values[i].boolean);
 	    return (0);
 	  }
@@ -4863,7 +4884,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad enum value %d - out of range "
-			    "(RFC 2911 section 4.1.4)."), attr->name,
+			    "(RFC 8011 section 5.1.5)."), attr->name,
 			    attr->values[i].integer);
             return (0);
 	  }
@@ -4877,7 +4898,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad octetString value - bad length %d "
-			    "(RFC 2911 section 4.1.10)."), attr->name,
+			    "(RFC 8011 section 5.1.20)."), attr->name,
 			    attr->values[i].unknown.length);
 	    return (0);
 	  }
@@ -4893,7 +4914,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime month %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[2]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[2]);
 	    return (0);
 	  }
 
@@ -4901,7 +4922,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime day %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[3]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[3]);
 	    return (0);
 	  }
 
@@ -4909,7 +4930,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime hours %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[4]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[4]);
 	    return (0);
 	  }
 
@@ -4917,7 +4938,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime minutes %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[5]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[5]);
 	    return (0);
 	  }
 
@@ -4925,7 +4946,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime seconds %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[6]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[6]);
 	    return (0);
 	  }
 
@@ -4933,7 +4954,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime deciseconds %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[7]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[7]);
 	    return (0);
 	  }
 
@@ -4941,7 +4962,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime UTC sign '%c' "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[8]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[8]);
 	    return (0);
 	  }
 
@@ -4949,7 +4970,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime UTC hours %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[9]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[9]);
 	    return (0);
 	  }
 
@@ -4957,7 +4978,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime UTC minutes %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[10]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[10]);
 	    return (0);
 	  }
 	}
@@ -4971,7 +4992,7 @@
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad resolution value %dx%d%s - cross "
 			    "feed resolution must be positive "
-			    "(RFC 2911 section 4.1.15)."), attr->name,
+			    "(RFC 8011 section 5.1.16)."), attr->name,
 			  attr->values[i].resolution.xres,
 			  attr->values[i].resolution.yres,
 			  attr->values[i].resolution.units ==
@@ -4986,7 +5007,7 @@
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad resolution value %dx%d%s - feed "
 			    "resolution must be positive "
-			    "(RFC 2911 section 4.1.15)."), attr->name,
+			    "(RFC 8011 section 5.1.16)."), attr->name,
 			  attr->values[i].resolution.xres,
 			  attr->values[i].resolution.yres,
 			  attr->values[i].resolution.units ==
@@ -5001,7 +5022,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad resolution value %dx%d%s - bad "
-			    "units value (RFC 2911 section 4.1.15)."),
+			    "units value (RFC 8011 section 5.1.16)."),
 			  attr->name, attr->values[i].resolution.xres,
 			  attr->values[i].resolution.yres,
 			  attr->values[i].resolution.units ==
@@ -5020,7 +5041,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad rangeOfInteger value %d-%d - lower "
-			    "greater than upper (RFC 2911 section 4.1.13)."),
+			    "greater than upper (RFC 8011 section 5.1.14)."),
 			  attr->name, attr->values[i].range.lower,
 			  attr->values[i].range.upper);
 	    return (0);
@@ -5082,7 +5103,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad text value \"%s\" - bad UTF-8 "
-			    "sequence (RFC 2911 section 4.1.1)."), attr->name,
+			    "sequence (RFC 8011 section 5.1.2)."), attr->name,
 			  attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5091,7 +5112,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad text value \"%s\" - bad length %d "
-			    "(RFC 2911 section 4.1.1)."), attr->name,
+			    "(RFC 8011 section 5.1.2)."), attr->name,
 			  attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5140,7 +5161,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad name value \"%s\" - bad UTF-8 "
-			    "sequence (RFC 2911 section 4.1.2)."), attr->name,
+			    "sequence (RFC 8011 section 5.1.3)."), attr->name,
 			  attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5149,7 +5170,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad name value \"%s\" - bad length %d "
-			    "(RFC 2911 section 4.1.2)."), attr->name,
+			    "(RFC 8011 section 5.1.3)."), attr->name,
 			  attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5169,7 +5190,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad keyword value \"%s\" - invalid "
-			    "character (RFC 2911 section 4.1.3)."),
+			    "character (RFC 8011 section 5.1.4)."),
 			  attr->name, attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5178,7 +5199,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad keyword value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.3)."),
+			    "length %d (RFC 8011 section 5.1.4)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5200,7 +5221,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad URI value \"%s\" - %s "
-			    "(RFC 2911 section 4.1.5)."), attr->name,
+			    "(RFC 8011 section 5.1.6)."), attr->name,
 			  attr->values[i].string.text,
 			  uri_status_strings[uri_status -
 					     HTTP_URI_STATUS_OVERFLOW]);
@@ -5211,7 +5232,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad URI value \"%s\" - bad length %d "
-			    "(RFC 2911 section 4.1.5)."), attr->name,
+			    "(RFC 8011 section 5.1.6)."), attr->name,
 			  attr->values[i].string.text,
 			  (int)strlen(attr->values[i].string.text));
 	  }
@@ -5234,7 +5255,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.6)."),
+			    "characters (RFC 8011 section 5.1.7)."),
 			  attr->name, attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5243,7 +5264,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.6)."),
+			    "length %d (RFC 8011 section 5.1.7)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5263,7 +5284,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad charset value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.7)."),
+			    "characters (RFC 8011 section 5.1.8)."),
 			  attr->name, attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5272,7 +5293,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad charset value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.7)."),
+			    "length %d (RFC 8011 section 5.1.8)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5318,7 +5339,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.8)."),
+			    "characters (RFC 8011 section 5.1.9)."),
 			  attr->name, attr->values[i].string.text);
 	    regfree(&re);
 	    return (0);
@@ -5328,7 +5349,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.8)."),
+			    "length %d (RFC 8011 section 5.1.9)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)strlen(attr->values[i].string.text));
 	    regfree(&re);
@@ -5372,7 +5393,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.9)."),
+			    "characters (RFC 8011 section 5.1.10)."),
 			  attr->name, attr->values[i].string.text);
 	    regfree(&re);
 	    return (0);
@@ -5382,7 +5403,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.9)."),
+			    "length %d (RFC 8011 section 5.1.10)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)strlen(attr->values[i].string.text));
 	    regfree(&re);
@@ -5405,8 +5426,8 @@
  * 'ippValidateAttributes()' - Validate all attributes in an IPP message.
  *
  * This function validates the contents of the IPP message, including each
- * attribute.  Like @link ippValidateAttribute@, cupsLastErrorString() is set
- * to a human-readable message on failure.
+ * attribute.  Like @link ippValidateAttribute@, @link cupsLastErrorString@ is
+ * set to a human-readable message on failure.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -6402,6 +6423,8 @@
     * Initialize attribute...
     */
 
+    DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values));
+
     if (name)
       attr->name = _cupsStrAlloc(name);
 
@@ -6958,6 +6981,9 @@
     * Reset pointers in the list...
     */
 
+    DEBUG_printf(("4debug_free: %p %s", (void *)*attr, temp->name));
+    DEBUG_printf(("4debug_alloc: %p %s %s%s (%d)", (void *)temp, temp->name, temp->num_values > 1 ? "1setOf " : "", ippTagString(temp->value_tag), temp->num_values));
+
     if (ipp->current == *attr && ipp->prev)
     {
      /*
diff --git a/cups/ipp.h b/cups/ipp.h
index 54d9d4b..2a4f546 100644
--- a/cups/ipp.h
+++ b/cups/ipp.h
@@ -1,7 +1,7 @@
 /*
  * Internet Printing Protocol definitions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -77,7 +77,7 @@
  * Types and structures...
  */
 
-typedef enum ipp_dstate_e		/**** Document states ****/
+typedef enum ipp_dstate_e		/**** Document states @exclude all@ ****/
 {
   IPP_DOCUMENT_PENDING = 3,		/* Document is pending */
   IPP_DOCUMENT_PROCESSING = 5,		/* Document is processing */
@@ -94,10 +94,10 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_dstate_t;
 
-typedef enum ipp_finishings_e		/**** Finishings ****/
+typedef enum ipp_finishings_e		/**** Finishings values ****/
 {
   IPP_FINISHINGS_NONE = 3,		/* No finishing */
-  IPP_FINISHINGS_STAPLE,		/* Staple (any location) */
+  IPP_FINISHINGS_STAPLE,		/* Staple (any location/method) */
   IPP_FINISHINGS_PUNCH,			/* Punch (any location/count) */
   IPP_FINISHINGS_COVER,			/* Add cover */
   IPP_FINISHINGS_BIND,			/* Bind */
@@ -169,38 +169,38 @@
 
   /* CUPS extensions for finishings (pre-standard versions of values above) */
   IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT = 0x40000046,
-					/* Punch 1 hole top left */
-  IPP_FINISHINGS_CUPS_PUNCH_BOTTOM_LEFT,/* Punch 1 hole bottom left */
-  IPP_FINISHINGS_CUPS_PUNCH_TOP_RIGHT,	/* Punch 1 hole top right */
+					/* Punch 1 hole top left @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_BOTTOM_LEFT,/* Punch 1 hole bottom left @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_TOP_RIGHT,	/* Punch 1 hole top right @exclude all@ */
   IPP_FINISHINGS_CUPS_PUNCH_BOTTOM_RIGHT,
-					/* Punch 1 hole bottom right */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_LEFT,	/* Punch 2 holes left side */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_TOP,	/* Punch 2 holes top edge */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_RIGHT,	/* Punch 2 holes right side */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_BOTTOM,/* Punch 2 holes bottom edge */
-  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_LEFT,/* Punch 3 holes left side */
-  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_TOP,	/* Punch 3 holes top edge */
+					/* Punch 1 hole bottom right @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_LEFT,	/* Punch 2 holes left side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_TOP,	/* Punch 2 holes top edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_RIGHT,	/* Punch 2 holes right side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_BOTTOM,/* Punch 2 holes bottom edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_LEFT,/* Punch 3 holes left side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_TOP,	/* Punch 3 holes top edge @exclude all@ */
   IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_RIGHT,
-					/* Punch 3 holes right side */
+					/* Punch 3 holes right side @exclude all@ */
   IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_BOTTOM,
-					/* Punch 3 holes bottom edge */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_LEFT,	/* Punch 4 holes left side */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_TOP,	/* Punch 4 holes top edge */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_RIGHT,	/* Punch 4 holes right side */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM,/* Punch 4 holes bottom edge */
+					/* Punch 3 holes bottom edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_LEFT,	/* Punch 4 holes left side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_TOP,	/* Punch 4 holes top edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_RIGHT,	/* Punch 4 holes right side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM,/* Punch 4 holes bottom edge @exclude all@ */
 
   IPP_FINISHINGS_CUPS_FOLD_ACCORDIAN = 0x4000005A,
-					/* Accordian-fold the paper vertically into four sections */
-  IPP_FINISHINGS_CUPS_FOLD_DOUBLE_GATE,	/* Fold the top and bottom quarters of the paper towards the midline, then fold in half vertically */
-  IPP_FINISHINGS_CUPS_FOLD_GATE,	/* Fold the top and bottom quarters of the paper towards the midline */
-  IPP_FINISHINGS_CUPS_FOLD_HALF,	/* Fold the paper in half vertically */
-  IPP_FINISHINGS_CUPS_FOLD_HALF_Z,	/* Fold the paper in half horizontally, then Z-fold the paper vertically */
-  IPP_FINISHINGS_CUPS_FOLD_LEFT_GATE,	/* Fold the top quarter of the paper towards the midline */
-  IPP_FINISHINGS_CUPS_FOLD_LETTER,	/* Fold the paper into three sections vertically; sometimes also known as a C fold*/
-  IPP_FINISHINGS_CUPS_FOLD_PARALLEL,	/* Fold the paper in half vertically two times, yielding four sections */
-  IPP_FINISHINGS_CUPS_FOLD_POSTER,	/* Fold the paper in half horizontally and vertically; sometimes also called a cross fold */
-  IPP_FINISHINGS_CUPS_FOLD_RIGHT_GATE,	/* Fold the bottom quarter of the paper towards the midline */
-  IPP_FINISHINGS_CUPS_FOLD_Z		/* Fold the paper vertically into three sections, forming a Z */
+					/* Accordian-fold the paper vertically into four sections @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_DOUBLE_GATE,	/* Fold the top and bottom quarters of the paper towards the midline, then fold in half vertically @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_GATE,	/* Fold the top and bottom quarters of the paper towards the midline @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_HALF,	/* Fold the paper in half vertically @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_HALF_Z,	/* Fold the paper in half horizontally, then Z-fold the paper vertically @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_LEFT_GATE,	/* Fold the top quarter of the paper towards the midline @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_LETTER,	/* Fold the paper into three sections vertically; sometimes also known as a C fold @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_PARALLEL,	/* Fold the paper in half vertically two times, yielding four sections @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_POSTER,	/* Fold the paper in half horizontally and vertically; sometimes also called a cross fold @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_RIGHT_GATE,	/* Fold the bottom quarter of the paper towards the midline @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_Z		/* Fold the paper vertically into three sections, forming a Z @exclude all@ */
 } ipp_finishings_t;
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_FINISHINGS_JOB_OFFSET	IPP_FINISHINGS_JOG_OFFSET
@@ -208,7 +208,7 @@
 typedef enum ipp_finishings_e ipp_finish_t;
 #  endif /* !_CUPS_NO_DEPRECATED */
 
-typedef enum ipp_jcollate_e		/**** Job collation types ****/
+typedef enum ipp_jcollate_e		/**** Job collation types @deprecated@ @exclude all@ ****/
 {
   IPP_JCOLLATE_UNCOLLATED_SHEETS = 3,
   IPP_JCOLLATE_COLLATED_DOCUMENTS,
@@ -248,98 +248,98 @@
 {
   IPP_OP_CUPS_INVALID = -1,		/* Invalid operation name for @link ippOpValue@ */
   IPP_OP_CUPS_NONE = 0,			/* No operation @private@ */
-  IPP_OP_PRINT_JOB = 0x0002,		/* Print a single file */
-  IPP_OP_PRINT_URI,			/* Print a single URL */
-  IPP_OP_VALIDATE_JOB,			/* Validate job options */
-  IPP_OP_CREATE_JOB,			/* Create an empty print job */
-  IPP_OP_SEND_DOCUMENT,			/* Add a file to a job */
-  IPP_OP_SEND_URI,			/* Add a URL to a job */
-  IPP_OP_CANCEL_JOB,			/* Cancel a job */
-  IPP_OP_GET_JOB_ATTRIBUTES,		/* Get job attributes */
-  IPP_OP_GET_JOBS,			/* Get a list of jobs */
-  IPP_OP_GET_PRINTER_ATTRIBUTES,	/* Get printer attributes */
-  IPP_OP_HOLD_JOB,			/* Hold a job for printing */
-  IPP_OP_RELEASE_JOB,			/* Release a job for printing */
-  IPP_OP_RESTART_JOB,			/* Reprint a job */
-  IPP_OP_PAUSE_PRINTER = 0x0010,	/* Stop a printer */
-  IPP_OP_RESUME_PRINTER,		/* Start a printer */
-  IPP_OP_PURGE_JOBS,			/* Cancel all jobs */
-  IPP_OP_SET_PRINTER_ATTRIBUTES,	/* Set printer attributes */
-  IPP_OP_SET_JOB_ATTRIBUTES,		/* Set job attributes */
-  IPP_OP_GET_PRINTER_SUPPORTED_VALUES,	/* Get supported attribute values */
-  IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,	/* Create one or more printer subscriptions @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_CREATE_JOB_SUBSCRIPTIONS,	/* Create one of more job subscriptions @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES,	/* Get subscription attributes @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_GET_SUBSCRIPTIONS,		/* Get list of subscriptions @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_RENEW_SUBSCRIPTION,		/* Renew a printer subscription @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_CANCEL_SUBSCRIPTION,		/* Cancel a subscription @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_GET_NOTIFICATIONS,		/* Get notification events @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_SEND_NOTIFICATIONS,		/* Send notification events @private@ */
-  IPP_OP_GET_RESOURCE_ATTRIBUTES,	/* Get resource attributes @private@ */
-  IPP_OP_GET_RESOURCE_DATA,		/* Get resource data @private@ */
-  IPP_OP_GET_RESOURCES,			/* Get list of resources @private@ */
-  IPP_OP_GET_PRINT_SUPPORT_FILES,	/* Get printer support files @private@ */
-  IPP_OP_ENABLE_PRINTER,		/* Start a printer */
-  IPP_OP_DISABLE_PRINTER,		/* Stop a printer */
+  IPP_OP_PRINT_JOB = 0x0002,		/* Print-Job: Print a single file */
+  IPP_OP_PRINT_URI,			/* Print-URI: Print a single URL @exclude all@ */
+  IPP_OP_VALIDATE_JOB,			/* Validate-Job: Validate job values prior to submission */
+  IPP_OP_CREATE_JOB,			/* Create-Job: Create an empty print job */
+  IPP_OP_SEND_DOCUMENT,			/* Send-Document: Add a file to a job */
+  IPP_OP_SEND_URI,			/* Send-URI: Add a URL to a job @exclude all@ */
+  IPP_OP_CANCEL_JOB,			/* Cancel-Job: Cancel a job */
+  IPP_OP_GET_JOB_ATTRIBUTES,		/* Get-Job-Attribute: Get information about a job */
+  IPP_OP_GET_JOBS,			/* Get-Jobs: Get a list of jobs */
+  IPP_OP_GET_PRINTER_ATTRIBUTES,	/* Get-Printer-Attributes: Get information about a printer */
+  IPP_OP_HOLD_JOB,			/* Hold-Job: Hold a job for printing */
+  IPP_OP_RELEASE_JOB,			/* Release-Job: Release a job for printing */
+  IPP_OP_RESTART_JOB,			/* Restart-Job: Reprint a job @deprecated@ */
+  IPP_OP_PAUSE_PRINTER = 0x0010,	/* Pause-Printer: Stop a printer */
+  IPP_OP_RESUME_PRINTER,		/* Resume-Printer: Start a printer */
+  IPP_OP_PURGE_JOBS,			/* Purge-Jobs: Delete all jobs @deprecated@ @exclude all@ */
+  IPP_OP_SET_PRINTER_ATTRIBUTES,	/* Set-Printer-Attributes: Set printer values */
+  IPP_OP_SET_JOB_ATTRIBUTES,		/* Set-Job-Attributes: Set job values */
+  IPP_OP_GET_PRINTER_SUPPORTED_VALUES,	/* Get-Printer-Supported-Values: Get supported values */
+  IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,	/* Create-Printer-Subscriptions: Create one or more printer subscriptions @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_CREATE_JOB_SUBSCRIPTIONS,	/* Create-Job-Subscriptions: Create one of more job subscriptions @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES,	/* Get-Subscription-Attributes: Get subscription information @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_GET_SUBSCRIPTIONS,		/* Get-Subscriptions: Get list of subscriptions @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_RENEW_SUBSCRIPTION,		/* Renew-Subscription: Renew a printer subscription @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_CANCEL_SUBSCRIPTION,		/* Cancel-Subscription: Cancel a subscription @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_GET_NOTIFICATIONS,		/* Get-Notifications: Get notification events @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_SEND_NOTIFICATIONS,		/* Send-Notifications: Send notification events @private@ */
+  IPP_OP_GET_RESOURCE_ATTRIBUTES,	/* Get-Resource-Attributes: Get resource information @private@ */
+  IPP_OP_GET_RESOURCE_DATA,		/* Get-Resource-Data: Get resource data @private@ @deprecated@ */
+  IPP_OP_GET_RESOURCES,			/* Get-Resources: Get list of resources @private@ */
+  IPP_OP_GET_PRINT_SUPPORT_FILES,	/* Get-Printer-Support-Files: Get printer support files @private@ */
+  IPP_OP_ENABLE_PRINTER,		/* Enable-Printer: Accept new jobs for a printer */
+  IPP_OP_DISABLE_PRINTER,		/* Disable-Printer: Reject new jobs for a printer */
   IPP_OP_PAUSE_PRINTER_AFTER_CURRENT_JOB,
-					/* Stop printer after the current job */
-  IPP_OP_HOLD_NEW_JOBS,			/* Hold new jobs */
-  IPP_OP_RELEASE_HELD_NEW_JOBS,		/* Release new jobs */
-  IPP_OP_DEACTIVATE_PRINTER,		/* Stop a printer */
-  IPP_OP_ACTIVATE_PRINTER,		/* Start a printer */
-  IPP_OP_RESTART_PRINTER,		/* Restart a printer */
-  IPP_OP_SHUTDOWN_PRINTER,		/* Turn a printer off */
-  IPP_OP_STARTUP_PRINTER,		/* Turn a printer on */
-  IPP_OP_REPROCESS_JOB,			/* Reprint a job */
-  IPP_OP_CANCEL_CURRENT_JOB,		/* Cancel the current job */
-  IPP_OP_SUSPEND_CURRENT_JOB,		/* Suspend the current job */
-  IPP_OP_RESUME_JOB,			/* Resume the current job */
-  IPP_OP_PROMOTE_JOB,			/* Promote a job to print sooner */
-  IPP_OP_SCHEDULE_JOB_AFTER,		/* Schedule a job to print after another */
-  IPP_OP_CANCEL_DOCUMENT = 0x0033,	/* Cancel-Document */
-  IPP_OP_GET_DOCUMENT_ATTRIBUTES,	/* Get-Document-Attributes */
-  IPP_OP_GET_DOCUMENTS,			/* Get-Documents */
-  IPP_OP_DELETE_DOCUMENT,		/* Delete-Document */
-  IPP_OP_SET_DOCUMENT_ATTRIBUTES,	/* Set-Document-Attributes */
-  IPP_OP_CANCEL_JOBS,			/* Cancel-Jobs */
-  IPP_OP_CANCEL_MY_JOBS,		/* Cancel-My-Jobs */
-  IPP_OP_RESUBMIT_JOB,			/* Resubmit-Job */
-  IPP_OP_CLOSE_JOB,			/* Close-Job */
-  IPP_OP_IDENTIFY_PRINTER,		/* Identify-Printer */
-  IPP_OP_VALIDATE_DOCUMENT,		/* Validate-Document */
-  IPP_OP_ADD_DOCUMENT_IMAGES,		/* Add-Document-Images */
-  IPP_OP_ACKNOWLEDGE_DOCUMENT,		/* Acknowledge-Document */
-  IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER,	/* Acknowledge-Identify-Printer */
-  IPP_OP_ACKNOWLEDGE_JOB,		/* Acknowledge-Job */
-  IPP_OP_FETCH_DOCUMENT,		/* Fetch-Document */
-  IPP_OP_FETCH_JOB,			/* Fetch-Job */
-  IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES,	/* Get-Output-Device-Attributes */
-  IPP_OP_UPDATE_ACTIVE_JOBS,		/* Update-Active-Jobs */
-  IPP_OP_DEREGISTER_OUTPUT_DEVICE,	/* Deregister-Output-Device */
-  IPP_OP_UPDATE_DOCUMENT_STATUS,	/* Update-Document-Status */
-  IPP_OP_UPDATE_JOB_STATUS,		/* Update-Job-Status */
+					/* Pause-Printer-After-Current-Job: Stop printer after the current job */
+  IPP_OP_HOLD_NEW_JOBS,			/* Hold-New-Jobs: Hold new jobs */
+  IPP_OP_RELEASE_HELD_NEW_JOBS,		/* Release-Held-New-Jobs: Release new jobs that were previously held */
+  IPP_OP_DEACTIVATE_PRINTER,		/* Deactivate-Printer: Stop a printer and do not accept jobs @deprecated@ @exclude all@ */
+  IPP_OP_ACTIVATE_PRINTER,		/* Activate-Printer: Start a printer and accept jobs @deprecated@ @exclude all@ */
+  IPP_OP_RESTART_PRINTER,		/* Restart-Printer: Restart a printer @exclude all@ */
+  IPP_OP_SHUTDOWN_PRINTER,		/* Shutdown-Printer: Turn a printer off @exclude all@ */
+  IPP_OP_STARTUP_PRINTER,		/* Startup-Printer: Turn a printer on @exclude all@ */
+  IPP_OP_REPROCESS_JOB,			/* Reprocess-Job: Reprint a job @deprecated@ @exclude all@*/
+  IPP_OP_CANCEL_CURRENT_JOB,		/* Cancel-Current-Job: Cancel the current job */
+  IPP_OP_SUSPEND_CURRENT_JOB,		/* Suspend-Current-Job: Suspend the current job */
+  IPP_OP_RESUME_JOB,			/* Resume-Job: Resume the current job */
+  IPP_OP_PROMOTE_JOB,			/* Promote-Job: Promote a job to print sooner */
+  IPP_OP_SCHEDULE_JOB_AFTER,		/* Schedule-Job-After: Schedule a job to print after another */
+  IPP_OP_CANCEL_DOCUMENT = 0x0033,	/* Cancel-Document: Cancel a document @exclude all@ */
+  IPP_OP_GET_DOCUMENT_ATTRIBUTES,	/* Get-Document-Attributes: Get document information @exclude all@ */
+  IPP_OP_GET_DOCUMENTS,			/* Get-Documents: Get a list of documents in a job @exclude all@ */
+  IPP_OP_DELETE_DOCUMENT,		/* Delete-Document: Delete a document @deprecated@  @exclude all@ */
+  IPP_OP_SET_DOCUMENT_ATTRIBUTES,	/* Set-Document-Attributes: Set document values @exclude all@ */
+  IPP_OP_CANCEL_JOBS,			/* Cancel-Jobs: Cancel all jobs (administrative) */
+  IPP_OP_CANCEL_MY_JOBS,		/* Cancel-My-Jobs: Cancel a user's jobs */
+  IPP_OP_RESUBMIT_JOB,			/* Resubmit-Job: Copy and reprint a job @exclude all@ */
+  IPP_OP_CLOSE_JOB,			/* Close-Job: Close a job and start printing */
+  IPP_OP_IDENTIFY_PRINTER,		/* Identify-Printer: Make the printer beep, flash, or display a message for identification */
+  IPP_OP_VALIDATE_DOCUMENT,		/* Validate-Document: Validate document values prior to submission @exclude all@ */
+  IPP_OP_ADD_DOCUMENT_IMAGES,		/* Add-Document-Images: Add image(s) from the specified scanner source @exclude all@ */
+  IPP_OP_ACKNOWLEDGE_DOCUMENT,		/* Acknowledge-Document: Acknowledge processing of a document @exclude all@ */
+  IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER,	/* Acknowledge-Identify-Printer: Acknowledge action on an Identify-Printer request @exclude all@ */
+  IPP_OP_ACKNOWLEDGE_JOB,		/* Acknowledge-Job: Acknowledge processing of a job @exclude all@ */
+  IPP_OP_FETCH_DOCUMENT,		/* Fetch-Document: Fetch a document for processing @exclude all@ */
+  IPP_OP_FETCH_JOB,			/* Fetch-Job: Fetch a job for processing @exclude all@ */
+  IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES,	/* Get-Output-Device-Attributes: Get printer information for a specific output device @exclude all@ */
+  IPP_OP_UPDATE_ACTIVE_JOBS,		/* Update-Active-Jobs: Update the list of active jobs that a proxy has processed @exclude all@ */
+  IPP_OP_DEREGISTER_OUTPUT_DEVICE,	/* Deregister-Output-Device: Remove an output device @exclude all@ */
+  IPP_OP_UPDATE_DOCUMENT_STATUS,	/* Update-Document-Status: Update document values @exclude all@ */
+  IPP_OP_UPDATE_JOB_STATUS,		/* Update-Job-Status: Update job values @exclude all@ */
   IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES,
-					/* Update-Output-Device-Attributes */
-  IPP_OP_GET_NEXT_DOCUMENT_DATA,	/* Get-Next-Document-Data */
+					/* Update-Output-Device-Attributes: Update output device values @exclude all@ */
+  IPP_OP_GET_NEXT_DOCUMENT_DATA,	/* Get-Next-Document-Data: Scan more document data @exclude all@ */
 
   IPP_OP_PRIVATE = 0x4000,		/* Reserved @private@ */
-  IPP_OP_CUPS_GET_DEFAULT,		/* Get the default printer */
-  IPP_OP_CUPS_GET_PRINTERS,		/* Get a list of printers and/or classes */
-  IPP_OP_CUPS_ADD_MODIFY_PRINTER,	/* Add or modify a printer */
-  IPP_OP_CUPS_DELETE_PRINTER,		/* Delete a printer */
-  IPP_OP_CUPS_GET_CLASSES,		/* Get a list of classes @deprecated@ */
-  IPP_OP_CUPS_ADD_MODIFY_CLASS,		/* Add or modify a class */
-  IPP_OP_CUPS_DELETE_CLASS,		/* Delete a class */
-  IPP_OP_CUPS_ACCEPT_JOBS,		/* Accept new jobs on a printer */
-  IPP_OP_CUPS_REJECT_JOBS,		/* Reject new jobs on a printer */
-  IPP_OP_CUPS_SET_DEFAULT,		/* Set the default printer */
-  IPP_OP_CUPS_GET_DEVICES,		/* Get a list of supported devices @deprecated@ */
-  IPP_OP_CUPS_GET_PPDS,			/* Get a list of supported drivers @deprecated@ */
-  IPP_OP_CUPS_MOVE_JOB,			/* Move a job to a different printer */
-  IPP_OP_CUPS_AUTHENTICATE_JOB,		/* Authenticate a job @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_CUPS_GET_PPD,			/* Get a PPD file @deprecated@ */
-  IPP_OP_CUPS_GET_DOCUMENT = 0x4027,	/* Get a document file @since CUPS 1.4/macOS 10.6@ */
-  IPP_OP_CUPS_CREATE_LOCAL_PRINTER	/* Create a local (temporary) printer @since CUPS 2.2 */
+  IPP_OP_CUPS_GET_DEFAULT,		/* CUPS-Get-Default: Get the default printer */
+  IPP_OP_CUPS_GET_PRINTERS,		/* CUPS-Get-Printers: Get a list of printers and/or classes */
+  IPP_OP_CUPS_ADD_MODIFY_PRINTER,	/* CUPS-Add-Modify-Printer: Add or modify a printer */
+  IPP_OP_CUPS_DELETE_PRINTER,		/* CUPS-Delete-Printer: Delete a printer */
+  IPP_OP_CUPS_GET_CLASSES,		/* CUPS-Get-Classes: Get a list of classes @deprecated@ @exclude all@ */
+  IPP_OP_CUPS_ADD_MODIFY_CLASS,		/* CUPS-Add-Modify-Class: Add or modify a class */
+  IPP_OP_CUPS_DELETE_CLASS,		/* CUPS-Delete-Class: Delete a class */
+  IPP_OP_CUPS_ACCEPT_JOBS,		/* CUPS-Accept-Jobs: Accept new jobs on a printer @exclude all@ */
+  IPP_OP_CUPS_REJECT_JOBS,		/* CUPS-Reject-Jobs: Reject new jobs on a printer @exclude all@ */
+  IPP_OP_CUPS_SET_DEFAULT,		/* CUPS-Set-Default: Set the default printer */
+  IPP_OP_CUPS_GET_DEVICES,		/* CUPS-Get-Devices: Get a list of supported devices @deprecated@ */
+  IPP_OP_CUPS_GET_PPDS,			/* CUPS-Get-PPDs: Get a list of supported drivers @deprecated@ */
+  IPP_OP_CUPS_MOVE_JOB,			/* CUPS-Move-Job: Move a job to a different printer */
+  IPP_OP_CUPS_AUTHENTICATE_JOB,		/* CUPS-Authenticate-Job: Authenticate a job @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_CUPS_GET_PPD,			/* CUPS-Get-PPD: Get a PPD file @deprecated@ */
+  IPP_OP_CUPS_GET_DOCUMENT = 0x4027,	/* CUPS-Get-Document: Get a document file @since CUPS 1.4/macOS 10.6@ */
+  IPP_OP_CUPS_CREATE_LOCAL_PRINTER	/* CUPS-Create-Local-Printer: Create a local (temporary) printer @since CUPS 2.2@ */
 
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_PRINT_JOB			IPP_OP_PRINT_JOB
@@ -364,7 +364,7 @@
 #    define IPP_CREATE_PRINTER_SUBSCRIPTION	IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
 #    define IPP_CREATE_JOB_SUBSCRIPTION		IPP_OP_CREATE_JOB_SUBSCRIPTIONS
 #    define IPP_OP_CREATE_PRINTER_SUBSCRIPTION	IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
-#    define IPP_OP_CREATE_JOB_SUBSCRIPTION		IPP_OP_CREATE_JOB_SUBSCRIPTIONS
+#    define IPP_OP_CREATE_JOB_SUBSCRIPTION	IPP_OP_CREATE_JOB_SUBSCRIPTIONS
 #    define IPP_GET_SUBSCRIPTION_ATTRIBUTES	IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
 #    define IPP_GET_SUBSCRIPTIONS		IPP_OP_GET_SUBSCRIPTIONS
 #    define IPP_RENEW_SUBSCRIPTION		IPP_OP_RENEW_SUBSCRIPTION
@@ -442,7 +442,7 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_orient_t;
 
-typedef enum ipp_pstate_e		/**** Printer states ****/
+typedef enum ipp_pstate_e		/**** Printer state values ****/
 {
   IPP_PSTATE_IDLE = 3,			/* Printer is idle */
   IPP_PSTATE_PROCESSING,		/* Printer is working */
@@ -455,7 +455,7 @@
 #  endif /* _CUPS_NO_DEPRECATED */
 } ipp_pstate_t;
 
-typedef enum ipp_quality_e		/**** Qualities ****/
+typedef enum ipp_quality_e		/**** Print quality values ****/
 {
   IPP_QUALITY_DRAFT = 3,		/* Draft quality */
   IPP_QUALITY_NORMAL,			/* Normal quality */
@@ -468,7 +468,7 @@
   IPP_RES_PER_CM			/* Pixels per centimeter */
 } ipp_res_t;
 
-typedef enum ipp_state_e		/**** IPP states ****/
+typedef enum ipp_state_e		/**** ipp_t state values ****/
 {
   IPP_STATE_ERROR = -1,			/* An error occurred */
   IPP_STATE_IDLE,			/* Nothing is happening/request completed */
@@ -485,7 +485,7 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_state_t;
 
-typedef enum ipp_status_e		/**** IPP status codes ****/
+typedef enum ipp_status_e		/**** IPP status code values ****/
 {
   IPP_STATUS_CUPS_INVALID = -1,		/* Invalid status name for @link ippErrorValue@ */
   IPP_STATUS_OK = 0x0000,		/* successful-ok */
@@ -498,7 +498,7 @@
   IPP_STATUS_OK_EVENTS_COMPLETE,	/* successful-ok-events-complete */
   IPP_STATUS_REDIRECTION_OTHER_SITE = 0x0200,
 					/* redirection-other-site @private@ */
-  IPP_STATUS_CUPS_SEE_OTHER = 0x0280,	/* cups-see-other */
+  IPP_STATUS_CUPS_SEE_OTHER = 0x0280,	/* cups-see-other @private@ */
   IPP_STATUS_ERROR_BAD_REQUEST = 0x0400,/* client-error-bad-request */
   IPP_STATUS_ERROR_FORBIDDEN,		/* client-error-forbidden */
   IPP_STATUS_ERROR_NOT_AUTHENTICATED,	/* client-error-not-authenticated */
@@ -574,7 +574,7 @@
   IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED = 0x1000,
 					/* cups-authentication-canceled - Authentication canceled by user @since CUPS 1.5/macOS 10.7@ */
   IPP_STATUS_ERROR_CUPS_PKI,		/* cups-pki-error - Error negotiating a secure connection @since CUPS 1.5/macOS 10.7@ */
-  IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED/* cups-upgrade-required - TLS upgrade required */
+  IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED/* cups-upgrade-required - TLS upgrade required @since CUPS 1.5/macOS 10.7@ */
 
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_OK				IPP_STATUS_OK
@@ -636,7 +636,7 @@
 #  endif /* _CUPS_NO_DEPRECATED */
 } ipp_status_t;
 
-typedef enum ipp_tag_e			/**** Format tags for attributes ****/
+typedef enum ipp_tag_e			/**** Value and group tag values for attributes ****/
 {
   IPP_TAG_CUPS_INVALID = -1,		/* Invalid tag name for @link ippTagValue@ */
   IPP_TAG_ZERO = 0x00,			/* Zero tag - used for separators */
@@ -648,7 +648,7 @@
   IPP_TAG_SUBSCRIPTION,			/* Subscription group */
   IPP_TAG_EVENT_NOTIFICATION,		/* Event group */
   IPP_TAG_RESOURCE,			/* Resource group @private@ */
-  IPP_TAG_DOCUMENT,			/* Document group */
+  IPP_TAG_DOCUMENT,			/* Document group @exclude all@ */
   IPP_TAG_UNSUPPORTED_VALUE = 0x10,	/* Unsupported value */
   IPP_TAG_DEFAULT,			/* Default value */
   IPP_TAG_UNKNOWN,			/* Unknown value */
@@ -663,10 +663,10 @@
   IPP_TAG_DATE,				/* Date/time value */
   IPP_TAG_RESOLUTION,			/* Resolution value */
   IPP_TAG_RANGE,			/* Range value */
-  IPP_TAG_BEGIN_COLLECTION,		/* Beginning of collection value */
+  IPP_TAG_BEGIN_COLLECTION,		/* Beginning of collection value @exclude all@ */
   IPP_TAG_TEXTLANG,			/* Text-with-language value */
   IPP_TAG_NAMELANG,			/* Name-with-language value */
-  IPP_TAG_END_COLLECTION,		/* End of collection value */
+  IPP_TAG_END_COLLECTION,		/* End of collection value @exclude all@ */
   IPP_TAG_TEXT = 0x41,			/* Text value */
   IPP_TAG_NAME,				/* Name value */
   IPP_TAG_RESERVED_STRING,		/* Reserved for future string value @private@ */
@@ -676,8 +676,8 @@
   IPP_TAG_CHARSET,			/* Character set value */
   IPP_TAG_LANGUAGE,			/* Language value */
   IPP_TAG_MIMETYPE,			/* MIME media type value */
-  IPP_TAG_MEMBERNAME,			/* Collection member name value */
-  IPP_TAG_EXTENSION = 0x7f,		/* Extension point for 32-bit tags */
+  IPP_TAG_MEMBERNAME,			/* Collection member name value @exclude all@ */
+  IPP_TAG_EXTENSION = 0x7f,		/* Extension point for 32-bit tags @exclude all@ */
   IPP_TAG_CUPS_MASK = 0x7fffffff,	/* Mask for copied attribute values @private@ */
   /* The following expression is used to avoid compiler warnings with +/-0x80000000 */
   IPP_TAG_CUPS_CONST = -0x7fffffff-1	/* Bitflag for copied/const attribute values @private@ */
@@ -688,17 +688,18 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_tag_t;
 
-typedef unsigned char ipp_uchar_t;	/**** Unsigned 8-bit integer/character ****/
+typedef unsigned char ipp_uchar_t;	/**** Unsigned 8-bit integer/character @exclude all@ ****/
 typedef struct _ipp_s ipp_t;		/**** IPP request/response data ****/
 typedef struct _ipp_attribute_s ipp_attribute_t;
 					/**** IPP attribute ****/
 
 /**** New in CUPS 1.2/macOS 10.5 ****/
 typedef ssize_t	(*ipp_iocb_t)(void *context, ipp_uchar_t *buffer, size_t bytes);
-					/**** IPP IO Callback Function @since CUPS 1.2/macOS 10.5@ ****/
+					/**** ippReadIO/ippWriteIO callback function @since CUPS 1.2/macOS 10.5@ ****/
 
 /**** New in CUPS 1.6/macOS 10.8 ****/
 typedef int (*ipp_copycb_t)(void *context, ipp_t *dst, ipp_attribute_t *attr);
+                                        /**** ippCopyAttributes callback function @since CUPS 1.6/macOS 10.8 ****/
 
 
 /*
@@ -805,7 +806,7 @@
 } _ipp_value_t;
 typedef _ipp_value_t ipp_value_t;	/**** Convenience typedef that will be removed @private@ ****/
 
-struct _ipp_attribute_s			/**** Attribute ****/
+struct _ipp_attribute_s			/**** IPP attribute ****/
 {
   ipp_attribute_t *next;		/* Next attribute in list */
   ipp_tag_t	group_tag,		/* Job/Printer/Operation group tag */
diff --git a/cups/language.c b/cups/language.c
index f3a3496..e6d2d44 100644
--- a/cups/language.c
+++ b/cups/language.c
@@ -123,7 +123,9 @@
   { "nb",         "no" },
   { "nb_NO",      "no" },
   { "zh-Hans",    "zh_CN" },
+  { "zh_HANS",    "zh_CN" },
   { "zh-Hant",    "zh_TW" },
+  { "zh_HANT",    "zh_TW" },
   { "zh-Hant_CN", "zh_TW" }
 };
 #endif /* __APPLE__ */
@@ -254,8 +256,16 @@
 {
   int		i;			/* Looping var */
   CFStringRef	localeName;		/* Locale as a CF string */
+#ifdef DEBUG
+  char          temp[1024];             /* Temporary string */
 
 
+  if (!CFStringGetCString(languageName, temp, (CFIndex)sizeof(temp), kCFStringEncodingASCII))
+    temp[0] = '\0';
+
+  DEBUG_printf(("_cupsAppleLocale(languageName=%p(%s), locale=%p, localsize=%d)", (void *)languageName, temp, (void *)locale, (int)localesize));
+#endif /* DEBUG */
+
   localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, languageName);
 
   if (localeName)
@@ -267,6 +277,8 @@
     if (!CFStringGetCString(localeName, locale, (CFIndex)localesize, kCFStringEncodingASCII))
       *locale = '\0';
 
+    DEBUG_printf(("_cupsAppleLocale: locale=\"%s\"", locale));
+
     CFRelease(localeName);
 
    /*
@@ -278,8 +290,12 @@
 		   sizeof(apple_language_locale[0]));
 	 i ++)
     {
-      if (!strcmp(locale, apple_language_locale[i].language))
+      size_t len = strlen(apple_language_locale[i].language);
+
+      if (!strcmp(locale, apple_language_locale[i].language) ||
+          (!strncmp(locale, apple_language_locale[i].language, len) && (locale[len] == '_' || locale[len] == '-')))
       {
+        DEBUG_printf(("_cupsAppleLocale: Updating locale to \"%s\".", apple_language_locale[i].locale));
 	strlcpy(locale, apple_language_locale[i].locale, localesize);
 	break;
       }
@@ -296,7 +312,10 @@
   }
 
   if (!*locale)
+  {
+    DEBUG_puts("_cupsAppleLocale: Returning NULL.");
     return (NULL);
+  }
 
  /*
   * Convert language subtag into region subtag...
@@ -304,10 +323,14 @@
 
   if (locale[2] == '-')
     locale[2] = '_';
+  else if (locale[3] == '-')
+    locale[3] = '_';
 
   if (!strchr(locale, '.'))
     strlcat(locale, ".UTF-8", localesize);
 
+  DEBUG_printf(("_cupsAppleLocale: Returning \"%s\".", locale));
+
   return (locale);
 }
 #endif /* __APPLE__ */
@@ -669,6 +692,15 @@
           *ptr++ = (char)toupper(*language & 255);
 
       *ptr = '\0';
+
+     /*
+      * Map Chinese region codes to legacy country codes.
+      */
+
+      if (!strcmp(language, "zh") && !strcmp(country, "HANS"))
+        strlcpy(country, "CN", sizeof(country));
+      if (!strcmp(language, "zh") && !strcmp(country, "HANT"))
+        strlcpy(country, "TW", sizeof(country));
     }
 
     if (*language == '.' && !charset[0])
@@ -688,7 +720,7 @@
     * Force a POSIX locale for an invalid language name...
     */
 
-    if (strlen(langname) != 2)
+    if (strlen(langname) != 2 && strlen(langname) != 3)
     {
       strlcpy(langname, "C", sizeof(langname));
       country[0] = '\0';
@@ -828,6 +860,9 @@
 {
   const char *s;			/* Localized message */
 
+
+  DEBUG_printf(("_cupsLangString(lang=%p, message=\"%s\")", (void *)lang, message));
+
  /*
   * Range check input...
   */
@@ -1126,6 +1161,8 @@
 			*match;		/* Matching message */
 
 
+  DEBUG_printf(("_cupsMessageLookup(a=%p, m=\"%s\")", (void *)a, m));
+
  /*
   * Lookup the message string; if it doesn't exist in the catalog,
   * then return the message that was passed to us...
@@ -1336,11 +1373,13 @@
 {
   char			filename[1024],	/* Path to cups.strings file */
 			applelang[256],	/* Apple language ID */
-			baselang[3];	/* Base language */
+			baselang[4];	/* Base language */
   CFURLRef		url;		/* URL to cups.strings file */
   CFReadStreamRef	stream = NULL;	/* File stream */
   CFPropertyListRef	plist = NULL;	/* Localization file */
 #ifdef DEBUG
+  const char            *cups_strings = getenv("CUPS_STRINGS");
+                                        /* Test strings file */
   CFErrorRef		error = NULL;	/* Error when opening file */
 #endif /* DEBUG */
 
@@ -1351,6 +1390,15 @@
   * Load the cups.strings file...
   */
 
+#ifdef DEBUG
+  if (cups_strings)
+  {
+    DEBUG_puts("1appleMessageLoad: Using debug CUPS_STRINGS file.");
+    strlcpy(filename, cups_strings, sizeof(filename));
+  }
+  else
+#endif /* DEBUG */
+
   snprintf(filename, sizeof(filename),
            CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
 	   _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
@@ -1363,6 +1411,7 @@
     * Try with original locale string...
     */
 
+    DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
     snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
   }
 
@@ -1374,18 +1423,23 @@
     * Try with just the language code...
     */
 
+    DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+
     strlcpy(baselang, locale, sizeof(baselang));
+    if (baselang[3] == '-' || baselang[3] == '_')
+      baselang[3] = '\0';
+
     snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", baselang);
   }
 
-  DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
-
   if (access(filename, 0))
   {
    /*
     * Try alternate lproj directory names...
     */
 
+    DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+
     if (!strncmp(locale, "en", 2))
       locale = "English";
     else if (!strncmp(locale, "nb", 2))
@@ -1402,7 +1456,7 @@
       locale = "Japanese";
     else if (!strncmp(locale, "es", 2))
       locale = "Spanish";
-    else if (!strcmp(locale, "zh_HK") || !strncmp(locale, "zh-Hant", 7))
+    else if (!strcmp(locale, "zh_HK") || !strncasecmp(locale, "zh-Hant", 7) || !strncasecmp(locale, "zh_Hant", 7))
     {
      /*
       * <rdar://problem/22130168>
@@ -1423,14 +1477,18 @@
       */
 
       strlcpy(baselang, locale, sizeof(baselang));
+      if (baselang[2] == '-' || baselang[2] == '_')
+        baselang[2] = '\0';
+
       locale = baselang;
     }
 
     snprintf(filename, sizeof(filename),
 	     CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
-    DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename));
   }
 
+  DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
+
   url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
                                                 (UInt8 *)filename,
 						(CFIndex)strlen(filename), false);
diff --git a/cups/language.h b/cups/language.h
index 0a3da77..c96cbfe 100644
--- a/cups/language.h
+++ b/cups/language.h
@@ -32,7 +32,7 @@
  * Types...
  */
 
-typedef enum cups_encoding_e		/**** Language Encodings ****/
+typedef enum cups_encoding_e		/**** Language Encodings @exclude all@ ****/
 {
   CUPS_AUTO_ENCODING = -1,		/* Auto-detect the encoding @private@ */
   CUPS_US_ASCII,			/* US ASCII */
diff --git a/cups/options.c b/cups/options.c
index aa70992..db14bec 100644
--- a/cups/options.c
+++ b/cups/options.c
@@ -1,7 +1,7 @@
 /*
  * Option routines for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -30,6 +30,31 @@
 
 
 /*
+ * 'cupsAddIntegerOption()' - Add an integer option to an option array.
+ *
+ * New option arrays can be initialized simply by passing 0 for the
+ * "num_options" parameter.
+ *
+ * @since CUPS 2.2.4/macOS 10.13@
+ */
+
+int					/* O  - Number of options */
+cupsAddIntegerOption(
+    const char    *name,		/* I  - Name of option */
+    int           value,		/* I  - Value of option */
+    int           num_options,		/* I  - Number of options */
+    cups_option_t **options)		/* IO - Pointer to options */
+{
+  char	strvalue[32];			/* String value */
+
+
+  snprintf(strvalue, sizeof(strvalue), "%d", value);
+
+  return (cupsAddOption(name, strvalue, num_options, options));
+}
+
+
+/*
  * 'cupsAddOption()' - Add an option to an option array.
  *
  * New option arrays can be initialized simply by passing 0 for the
@@ -55,6 +80,11 @@
     return (num_options);
   }
 
+  if (!_cups_strcasecmp(name, "cupsPrintQuality"))
+    num_options = cupsRemoveOption("print-quality", num_options, options);
+  else if (!_cups_strcasecmp(name, "print-quality"))
+    num_options = cupsRemoveOption("cupsPrintQuality", num_options, options);
+
  /*
   * Look for an existing option with the same name...
   */
@@ -155,6 +185,38 @@
 
 
 /*
+ * 'cupsGetIntegerOption()' - Get an integer option value.
+ *
+ * INT_MIN is returned when the option does not exist, is not an integer, or
+ * exceeds the range of values for the "int" type.
+ *
+ * @since CUPS 2.2.4/macOS 10.13@
+ */
+
+int					/* O - Option value or @code INT_MIN@ */
+cupsGetIntegerOption(
+    const char    *name,		/* I - Name of option */
+    int           num_options,		/* I - Number of options */
+    cups_option_t *options)		/* I - Options */
+{
+  const char	*value = cupsGetOption(name, num_options, options);
+					/* String value of option */
+  char		*ptr;			/* Pointer into string value */
+  long		intvalue;		/* Integer value */
+
+
+  if (!value || !*value)
+    return (INT_MIN);
+
+  intvalue = strtol(value, &ptr, 10);
+  if (intvalue < INT_MIN || intvalue > INT_MAX || *ptr)
+    return (INT_MIN);
+
+  return ((int)intvalue);
+}
+
+
+/*
  * 'cupsGetOption()' - Get an option value.
  */
 
diff --git a/cups/ppd-cache.c b/cups/ppd-cache.c
index e712341..925ab80 100644
--- a/cups/ppd-cache.c
+++ b/cups/ppd-cache.c
@@ -78,6 +78,8 @@
   int		num_finishings = 0,	/* Number of finishing values */
 		finishings[10];		/* Finishing enum values */
   ppd_choice_t	*choice;		/* Marked choice */
+  int           finishings_copies = copies;
+                                        /* Number of copies for finishings */
 
 
  /*
@@ -366,13 +368,13 @@
   {
     ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
 
-    if (copies > 1 && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
+    if (copies != finishings_copies && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
     {
      /*
       * Send job-pages-per-set attribute to apply finishings correctly...
       */
 
-      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / copies);
+      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / finishings_copies);
     }
   }
 
@@ -2084,11 +2086,16 @@
 
   DEBUG_printf(("_ppdCacheGetFinishingValues(pc=%p, num_options=%d, options=%p, max_values=%d, values=%p)", pc, num_options, options, max_values, values));
 
-  if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
+  if (!pc || max_values < 1 || !values)
   {
     DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
     return (0);
   }
+  else if (!pc->finishings)
+  {
+    DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0.");
+    return (0);
+  }
 
  /*
   * Go through the finishings options and see what is set...
@@ -2114,7 +2121,7 @@
 
     if (i == 0)
     {
-      DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d.", f->value));
+      DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d (%s)", f->value, ippEnumString("finishings", f->value)));
 
       values[num_values ++] = f->value;
 
@@ -2123,6 +2130,17 @@
     }
   }
 
+  if (num_values == 0)
+  {
+   /*
+    * Always have at least "finishings" = 'none'...
+    */
+
+    DEBUG_puts("_ppdCacheGetFinishingValues: Adding 3 (none).");
+    values[0] = IPP_FINISHINGS_NONE;
+    num_values ++;
+  }
+
   DEBUG_printf(("_ppdCacheGetFinishingValues: Returning %d.", num_values));
 
   return (num_values);
@@ -2949,6 +2967,8 @@
 			is_pwg = 0;	/* Does the printer support PWG Raster? */
   pwg_media_t		*pwg;		/* PWG media size */
   int			xres, yres;	/* Resolution values */
+  int                   resolutions[1000];
+                                        /* Array of resolution indices */
   cups_lang_t		*lang = cupsLangDefault();
 					/* Localization info */
   struct lconv		*loc = localeconv();
@@ -3581,8 +3601,8 @@
   * ColorModel...
   */
 
-  if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
-    if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
+  if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
+    if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
       if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
         attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD);
 
@@ -3595,7 +3615,7 @@
       const char *keyword = ippGetString(attr, i, NULL);
 					/* Keyword for color/bit depth */
 
-      if (!strcmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
+      if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
       {
         if (!default_color)
 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
@@ -3606,7 +3626,7 @@
         if (!default_color)
 	  default_color = "FastGray";
       }
-      else if (!strcmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
+      else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
       {
         if (!default_color)
 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
@@ -3617,7 +3637,7 @@
         if (!default_color || !strcmp(default_color, "FastGray"))
 	  default_color = "Gray";
       }
-      else if (!strcmp(keyword, "srgb_8") || !strcmp(keyword, "SRGB24") || !strcmp(keyword, "color"))
+      else if (!strcasecmp(keyword, "srgb_8") || !strcmp(keyword, "SRGB24") || !strcmp(keyword, "color"))
       {
         if (!default_color)
 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
@@ -3627,7 +3647,7 @@
 
 	default_color = "RGB";
       }
-      else if (!strcmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48"))
+      else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48"))
       {
         if (!default_color)
 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
@@ -3661,7 +3681,36 @@
 		       "*Duplex DuplexTumble/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
 		       "*CloseUI: *Duplex\n", _cupsLangString(lang, _("2-Sided Printing")), _cupsLangString(lang, _("Off (1-Sided)")), _cupsLangString(lang, _("Long-Edge (Portrait)")), _cupsLangString(lang, _("Short-Edge (Landscape)")));
 
-    if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
+    if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
+    {
+      for (i = 0, count = ippGetCount(attr); i < count; i ++)
+      {
+        const char *dm = ippGetString(attr, i, NULL);
+                                        /* DM value */
+
+        if (!_cups_strcasecmp(dm, "DM1"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: Normal\n");
+          break;
+        }
+        else if (!_cups_strcasecmp(dm, "DM2"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
+          break;
+        }
+        else if (!_cups_strcasecmp(dm, "DM3"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
+          break;
+        }
+        else if (!_cups_strcasecmp(dm, "DM4"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
+          break;
+        }
+      }
+    }
+    else if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
     {
       const char *keyword = ippGetString(attr, 0, NULL);
 					/* Keyword value */
@@ -3675,35 +3724,6 @@
       else
         cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
     }
-    else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
-    {
-      for (i = 0, count = ippGetCount(attr); i < count; i ++)
-      {
-	const char *dm = ippGetString(attr, i, NULL);
-					  /* DM value */
-
-	if (!_cups_strcasecmp(dm, "DM1"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: Normal\n");
-	  break;
-	}
-	else if (!_cups_strcasecmp(dm, "DM2"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
-	  break;
-	}
-	else if (!_cups_strcasecmp(dm, "DM3"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
-	  break;
-	}
-	else if (!_cups_strcasecmp(dm, "DM4"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
-	  break;
-	}
-      }
-    }
   }
 
  /*
@@ -3962,42 +3982,14 @@
 
   quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
 
-  if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+  if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
   {
-    count = ippGetCount(attr);
-
-    pwg_ppdize_resolution(attr, count / 2, &xres, &yres, ppdname, sizeof(ppdname));
-    cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
-
-    cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
-		       "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
-		       "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
-    if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
-    {
-      pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
-      cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
-    }
-    pwg_ppdize_resolution(attr, count / 2, &xres, &yres, NULL, 0);
-    cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
-    if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
-    {
-      if (count > 1)
-        pwg_ppdize_resolution(attr, count - 1, &xres, &yres, NULL, 0);
-      else
-        pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
-      cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
-    }
-
-    cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
-  }
-  else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
-  {
-    int lowdpi = 0, hidpi = 0;		/* Lower and higher resolution */
+    int lowdpi = 0, hidpi = 0;    /* Lower and higher resolution */
 
     for (i = 0, count = ippGetCount(attr); i < count; i ++)
     {
       const char *rs = ippGetString(attr, i, NULL);
-					/* RS value */
+          /* RS value */
 
       if (_cups_strncasecmp(rs, "RS", 2))
         continue;
@@ -4027,18 +4019,81 @@
       cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
 
       cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
-			 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
-			 "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
+       "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
+       "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
       if ((lowdpi & 1) == 0)
-	cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi / 2);
+  cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi / 2);
       else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
-	cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi);
+  cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi);
       cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), lowdpi, lowdpi);
       if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
-	cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), hidpi, hidpi);
+  cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), hidpi, hidpi);
       cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
     }
   }
+  else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+  {
+   /*
+    * Make a sorted list of resolutions.
+    */
+
+    count = ippGetCount(attr);
+    if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
+      count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
+
+    for (i = 0; i < count; i ++)
+      resolutions[i] = i;
+
+    for (i = 0; i < (count - 1); i ++)
+    {
+      for (j = i + 1; j < count; j ++)
+      {
+        int       ix, iy,               /* First X and Y resolution */
+                  jx, jy,               /* Second X and Y resolution */
+                  temp;                 /* Swap variable */
+        ipp_res_t units;                /* Resolution units */
+
+        ix = ippGetResolution(attr, resolutions[i], &iy, &units);
+        jx = ippGetResolution(attr, resolutions[j], &jy, &units);
+
+        if (ix > jx || (ix == jx && iy > jy))
+        {
+         /*
+          * Swap these two resolutions...
+          */
+
+          temp           = resolutions[i];
+          resolutions[i] = resolutions[j];
+          resolutions[j] = temp;
+        }
+      }
+    }
+
+   /*
+    * Generate print quality options...
+    */
+
+    pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
+    cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
+
+    cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
+           "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
+           "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
+    if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
+    {
+      pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
+      cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
+    }
+    pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
+    cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
+    if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
+    {
+      pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
+      cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
+    }
+
+    cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
+  }
   else if (is_apple || is_pwg)
     goto bad_ppd;
   else
diff --git a/cups/ppd-mark.c b/cups/ppd-mark.c
index 9fdaf0b..464c09a 100644
--- a/cups/ppd-mark.c
+++ b/cups/ppd-mark.c
@@ -1,7 +1,7 @@
 /*
  * Option marking routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -253,6 +253,7 @@
   */
 
   for (i = num_options, optptr = options; i > 0; i --, optptr ++)
+  {
     if (!_cups_strcasecmp(optptr->name, "media") ||
         !_cups_strcasecmp(optptr->name, "output-bin") ||
 	!_cups_strcasecmp(optptr->name, "output-mode") ||
@@ -341,6 +342,19 @@
       ppd_mark_option(ppd, "MirrorPrint", optptr->value);
     else
       ppd_mark_option(ppd, optptr->name, optptr->value);
+  }
+
+  if (print_quality)
+  {
+    int pq = atoi(print_quality);       /* print-quaity value */
+
+    if (pq == IPP_QUALITY_DRAFT)
+      ppd_mark_option(ppd, "cupsPrintQuality", "Draft");
+    else if (pq == IPP_QUALITY_HIGH)
+      ppd_mark_option(ppd, "cupsPrintQuality", "High");
+    else
+      ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
+  }
 
   ppd_debug_marked(ppd, "After...");
 
diff --git a/cups/pwg-media.c b/cups/pwg-media.c
index 73356fd..62455ea 100644
--- a/cups/pwg-media.c
+++ b/cups/pwg-media.c
@@ -189,6 +189,7 @@
   _PWG_MEDIA_MM("jis_b1_728x1030mm", "jis-b1", "B1", 728, 1030),
   _PWG_MEDIA_MM("jis_b0_1030x1456mm", "jis-b0", "B0", 1030, 1456),
   _PWG_MEDIA_MM("jis_exec_216x330mm", NULL, "216x330mm", 216, 330),
+  _PWG_MEDIA_MM("jpn_kaku1_270x382mm", NULL, "EnvKaku1", 270, 382),
   _PWG_MEDIA_MM("jpn_kaku2_240x332mm", NULL, "EnvKaku2", 240, 332),
   _PWG_MEDIA_MM("jpn_kaku3_216x277mm", NULL, "EnvKaku3", 216, 277),
   _PWG_MEDIA_MM("jpn_kaku4_197x267mm", NULL, "EnvKaku4", 197, 267),
diff --git a/cups/pwg.h b/cups/pwg.h
index 2269dbe..4204050 100644
--- a/cups/pwg.h
+++ b/cups/pwg.h
@@ -1,7 +1,7 @@
 /*
  * PWG media API definitions for CUPS.
  *
- * Copyright 2009-2013 by Apple Inc.
+ * Copyright 2009-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -39,7 +39,7 @@
  * Types and structures...
  */
 
-typedef struct pwg_map_s		/**** Map element - PPD to/from PWG */
+typedef struct pwg_map_s		/**** Map element - PPD to/from PWG @exclude all@ */
 {
   char		*pwg,			/* PWG media keyword */
 		*ppd;			/* PPD option keyword */
@@ -54,7 +54,7 @@
 		length;			/* Length in 2540ths */
 } pwg_media_t;
 
-typedef struct pwg_size_s		/**** Size element - PPD to/from PWG */
+typedef struct pwg_size_s		/**** Size element - PPD to/from PWG @exclude all@ */
 {
   pwg_map_t	map;			/* Map element */
   int		width,			/* Width in 2540ths */
diff --git a/cups/request.c b/cups/request.c
index 39cbe6c..efbc06e 100644
--- a/cups/request.c
+++ b/cups/request.c
@@ -1,7 +1,7 @@
 /*
  * IPP utilities for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -131,13 +131,12 @@
   * Get the default connection as needed...
   */
 
-  if (!http)
-    if ((http = _cupsConnect()) == NULL)
-    {
-      ippDelete(request);
+  if (!http && (http = _cupsConnect()) == NULL)
+  {
+    ippDelete(request);
 
-      return (NULL);
-    }
+    return (NULL);
+  }
 
  /*
   * See if we have a file to send...
@@ -151,9 +150,7 @@
       * Can't get file information!
       */
 
-      _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED,
-                    NULL, 0);
-
+      _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, NULL, 0);
       ippDelete(request);
 
       return (NULL);
@@ -169,9 +166,8 @@
       * Can't send a directory...
       */
 
-      ippDelete(request);
-
       _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0);
+      ippDelete(request);
 
       return (NULL);
     }
@@ -186,8 +182,7 @@
   else
     length = ippLength(request);
 
-  DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld",
-                (long)ippLength(request), (long)length));
+  DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld", (long)ippLength(request), (long)length));
 
  /*
   * Clear any "Local" authentication data since it is probably stale...
@@ -596,6 +591,7 @@
   int			got_status;	/* Did we get the status? */
   ipp_state_t		state;		/* State of IPP processing */
   http_status_t		expect;		/* Expect: header to use */
+  char                  date[256];      /* Date: header value */
 
 
   DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", length=" CUPS_LLFMT ")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource, CUPS_LLCAST length));
@@ -615,9 +611,8 @@
   * Get the default connection as needed...
   */
 
-  if (!http)
-    if ((http = _cupsConnect()) == NULL)
-      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+  if (!http && (http = _cupsConnect()) == NULL)
+    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
 
  /*
   * If the prior request was not flushed out, do so now...
@@ -685,6 +680,7 @@
     httpClearFields(http);
     httpSetExpect(http, expect);
     httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+    httpSetField(http, HTTP_FIELD_DATE, httpGetDateString2(time(NULL), date, (int)sizeof(date)));
     httpSetLength(http, length);
 
 #ifdef HAVE_GSSAPI
@@ -996,7 +992,11 @@
     */
 
     if (strcmp(cg->http->hostname, cg->server) ||
+#ifdef AF_LOCAL
+        (httpAddrFamily(cg->http->hostaddr) != AF_LOCAL && cg->ipp_port != httpAddrPort(cg->http->hostaddr)) ||
+#else
         cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
+#endif /* AF_LOCAL */
         (cg->http->encryption != cg->encryption &&
 	 cg->http->encryption == HTTP_ENCRYPTION_NEVER))
     {
diff --git a/cups/snmp.c b/cups/snmp.c
index fffa218..7958b93 100644
--- a/cups/snmp.c
+++ b/cups/snmp.c
@@ -739,6 +739,10 @@
   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
 
 
+#ifdef __clang_analyzer__ /* Suppress bogus clang error */
+  memset(string, 0, sizeof(string));
+#endif /* __clang_analyzer__ */
+
   if (cg->snmp_debug <= 0)
     return;
 
diff --git a/cups/string.c b/cups/string.c
index 39a787f..0d4ed0f 100644
--- a/cups/string.c
+++ b/cups/string.c
@@ -695,10 +695,11 @@
   */
 
   dstlen = strlen(dst);
-  size   -= dstlen + 1;
 
-  if (!size)
-    return (dstlen);		/* No room, return immediately... */
+  if (size < (dstlen + 1))
+    return (dstlen);		        /* No room, return immediately... */
+
+  size -= dstlen + 1;
 
  /*
   * Figure out how much room is needed...
diff --git a/cups/testcups.c b/cups/testcups.c
index aa58766..f4931c5 100644
--- a/cups/testcups.c
+++ b/cups/testcups.c
@@ -1,7 +1,7 @@
 /*
  * CUPS API test program for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -47,7 +47,9 @@
   cups_dest_t	*dests,			/* Destinations */
 		*dest,			/* Current destination */
 		*named_dest;		/* Current named destination */
-  const char	*ppdfile;		/* PPD file */
+  const char	*dest_name,             /* Destination name */
+                *dval,                  /* Destination value */
+                *ppdfile;		/* PPD file */
   ppd_file_t	*ppd;			/* PPD file data */
   int		num_jobs;		/* Number of jobs for queue */
   cups_job_t	*jobs;			/* Jobs for queue */
@@ -360,11 +362,19 @@
   * cupsGetDest(printer)
   */
 
-  printf("cupsGetDest(\"%s\"): ", dests[num_dests / 2].name);
+  for (i = 0, dest_name = NULL; i < num_dests; i ++)
+  {
+    if ((dval = cupsGetOption("printer-is-temporary", dests[i].num_options, dest[i].options)) != NULL && !strcmp(dval, "false"))
+    {
+      dest_name = dests[i].name;
+      break;
+    }
+  }
+
+  printf("cupsGetDest(\"%s\"): ", dest_name ? dest_name : "(null)");
   fflush(stdout);
 
-  if ((dest = cupsGetDest(dests[num_dests / 2].name, NULL, num_dests,
-                          dests)) == NULL)
+  if ((dest = cupsGetDest(dest_name, NULL, num_dests, dests)) == NULL)
   {
     puts("FAIL");
     return (1);
@@ -380,8 +390,7 @@
          dest->instance ? dest->instance : "(null)");
   fflush(stdout);
 
-  if ((named_dest = cupsGetNamedDest(NULL, dest->name,
-                                     dest->instance)) == NULL ||
+  if ((named_dest = cupsGetNamedDest(NULL, dest->name, dest->instance)) == NULL ||
       !dests_equal(dest, named_dest))
   {
     if (named_dest)
@@ -408,7 +417,7 @@
   fputs("cupsPrintFile: ", stdout);
   fflush(stdout);
 
-  if (cupsPrintFile(dest->name, "../data/testprint", "Test Page",
+  if (cupsPrintFile(dest->name, "../test/testfile.pdf", "Test Page",
                     dest->num_options, dest->options) <= 0)
   {
     printf("FAIL (%s)\n", cupsLastErrorString());
@@ -421,7 +430,7 @@
   * cupsGetPPD(printer)
   */
 
-  fputs("cupsGetPPD(): ", stdout);
+  fputs("cupsGetPPD: ", stdout);
   fflush(stdout);
 
   if ((ppdfile = cupsGetPPD(dest->name)) == NULL)
@@ -436,7 +445,7 @@
     * ppdOpenFile()
     */
 
-    fputs("ppdOpenFile(): ", stdout);
+    fputs("ppdOpenFile: ", stdout);
     fflush(stdout);
 
     if ((ppd = ppdOpenFile(ppdfile)) == NULL)
@@ -550,33 +559,39 @@
 {
   int		i;			/* Looping var */
   cups_option_t	*aoption;		/* Current option */
+  cups_option_t	*boption;		/* Current option */
   const char	*bval;			/* Option value */
 
 
   if (!a || !b)
     return;
 
-  puts("    Item                  cupsGetDest           cupsGetNamedDest");
-  puts("    --------------------  --------------------  --------------------");
+  puts("    Item                  cupsGetDest               cupsGetNamedDest");
+  puts("    --------------------  ------------------------  ------------------------");
 
   if (_cups_strcasecmp(a->name, b->name))
-    printf("    name                  %-20.20s  %-20.20s\n", a->name, b->name);
+    printf("    name                  %-24.24s  %-24.24s\n", a->name, b->name);
 
   if ((a->instance && !b->instance) ||
       (!a->instance && b->instance) ||
       (a->instance && _cups_strcasecmp(a->instance, b->instance)))
-    printf("    instance              %-20.20s  %-20.20s\n",
+    printf("    instance              %-24.24s  %-24.24s\n",
            a->instance ? a->instance : "(null)",
 	   b->instance ? b->instance : "(null)");
 
   if (a->num_options != b->num_options)
-    printf("    num_options           %-20d  %-20d\n", a->num_options,
+    printf("    num_options           %-24d  %-24d\n", a->num_options,
            b->num_options);
 
   for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++)
     if ((bval = cupsGetOption(aoption->name, b->num_options,
                               b->options)) == NULL ||
         strcmp(aoption->value, bval))
-      printf("    %-20.20s  %-20.20s  %-20.20s\n", aoption->name,
+      printf("    %-20.20s  %-24.24s  %-24.24s\n", aoption->name,
              aoption->value, bval ? bval : "(null)");
+
+  for (i = b->num_options, boption = b->options; i > 0; i --, boption ++)
+    if (!cupsGetOption(boption->name, a->num_options, a->options))
+      printf("    %-20.20s  %-24.24s  %-24.24s\n", boption->name,
+             boption->value, "(null)");
 }
diff --git a/cups/testdest.c b/cups/testdest.c
index 945e3e1..c5c2052 100644
--- a/cups/testdest.c
+++ b/cups/testdest.c
@@ -1,7 +1,7 @@
 /*
  * CUPS destination API test program for CUPS.
  *
- * Copyright 2012-2016 by Apple Inc.
+ * Copyright 2012-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -105,6 +105,14 @@
   }
   else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7))
     dest = cupsGetDestWithURI(NULL, argv[1]);
+  else if (!strcmp(argv[1], "default"))
+  {
+    dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
+    if (dest && dest->instance)
+      printf("default is \"%s/%s\".\n", dest->name, dest->instance);
+    else
+      printf("default is \"%s\".\n", dest->name);
+  }
   else
     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[1], NULL);
 
@@ -218,9 +226,9 @@
   (void)flags;
 
   if (dest->instance)
-    printf("%s/%s:\n", dest->name, dest->instance);
+    printf("%s%s/%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name, dest->instance);
   else
-    printf("%s:\n", dest->name);
+    printf("%s%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name);
 
   for (i = 0; i < dest->num_options; i ++)
     printf("    %s=\"%s\"\n", dest->options[i].name, dest->options[i].value);
@@ -463,10 +471,37 @@
 	     cups_dinfo_t *dinfo,	/* I - Destination information */
 	     const char  *option)	/* I - Option */
 {
-  (void)http;
-  (void)dest;
-  (void)dinfo;
-  (void)option;
+  if (!strcmp(option, "media"))
+  {
+   /*
+    * Show default media option...
+    */
+
+    cups_size_t size;                   /* Media size information */
+
+    if (cupsGetDestMediaDefault(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size))
+      printf("%s (%.2fx%.2fmm, margins=[%.2f %.2f %.2f %.2f])\n", size.media, size.width * 0.01, size.length * 0.01, size.left * 0.01, size.bottom * 0.01, size.right * 0.01, size.top * 0.01);
+     else
+       puts("FAILED");
+  }
+  else
+  {
+   /*
+    * Show default other option...
+    */
+
+    ipp_attribute_t *defattr;           /* Default attribute */
+
+    if ((defattr = cupsFindDestDefault(http, dest, dinfo, option)) != NULL)
+    {
+      char value[1024];                 /* Value of default attribute */
+
+      ippAttributeString(defattr, value, sizeof(value));
+      puts(value);
+    }
+    else
+      puts("FAILED");
+  }
 }
 
 
@@ -594,7 +629,8 @@
   }
   else if (!value)
   {
-    puts(option);
+    printf("%s (%s - %s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option), cupsCheckDestSupported(http, dest, dinfo, option, NULL) ? "supported" : "not-supported");
+
     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
     {
       count = ippGetCount(attr);
@@ -608,7 +644,13 @@
 
         case IPP_TAG_ENUM :
 	    for (i = 0; i < count; i ++)
-              printf("  %s\n", ippEnumString(option, ippGetInteger(attr, i)));
+	    {
+	      int val = ippGetInteger(attr, i);
+	      char valstr[256];
+
+              snprintf(valstr, sizeof(valstr), "%d", val);
+              printf("  %s (%s)\n", ippEnumString(option, ippGetInteger(attr, i)), cupsLocalizeDestValue(http, dest, dinfo, option, valstr));
+            }
 	    break;
 
         case IPP_TAG_RANGE :
@@ -634,11 +676,15 @@
 	    }
 	    break;
 
+	case IPP_TAG_KEYWORD :
+	    for (i = 0; i < count; i ++)
+              printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
+	    break;
+
 	case IPP_TAG_TEXTLANG :
 	case IPP_TAG_NAMELANG :
 	case IPP_TAG_TEXT :
 	case IPP_TAG_NAME :
-	case IPP_TAG_KEYWORD :
 	case IPP_TAG_URI :
 	case IPP_TAG_URISCHEME :
 	case IPP_TAG_CHARSET :
diff --git a/cups/testgetdests.c b/cups/testgetdests.c
new file mode 100644
index 0000000..459998f
--- /dev/null
+++ b/cups/testgetdests.c
@@ -0,0 +1,51 @@
+/*
+ * CUPS cupsGetDests API test program for CUPS.
+ *
+ * Copyright 2017 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
+ * missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include "cups.h"
+#include <sys/time.h>
+
+
+/*
+ * 'main()' - Loop calling cupsGetDests.
+ */
+
+int                                     /* O - Exit status */
+main(void)
+{
+  int           num_dests;              /* Number of destinations */
+  cups_dest_t   *dests;                 /* Destinations */
+  struct timeval start, end;            /* Start and stop time */
+  double        secs;                   /* Total seconds to run cupsGetDests */
+
+
+  for (;;)
+  {
+    gettimeofday(&start, NULL);
+    num_dests = cupsGetDests(&dests);
+    gettimeofday(&end, NULL);
+    secs = end.tv_sec - start.tv_sec + 0.000001 * (end.tv_usec - start.tv_usec);
+
+    printf("Found %d printers in %.3f seconds...\n", num_dests, secs);
+
+    cupsFreeDests(num_dests, dests);
+    sleep(1);
+  }
+
+  return (0);
+}
diff --git a/cups/testipp.c b/cups/testipp.c
index 017ee9d..ea8f9d7 100644
--- a/cups/testipp.c
+++ b/cups/testipp.c
@@ -1,7 +1,7 @@
 /*
  * IPP test program for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2005 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -810,88 +810,9 @@
 print_attributes(ipp_t *ipp,		/* I - IPP request */
                  int   indent)		/* I - Indentation */
 {
-  int			i;		/* Looping var */
   ipp_tag_t		group;		/* Current group */
   ipp_attribute_t	*attr;		/* Current attribute */
-  _ipp_value_t		*val;		/* Current value */
-  static const char * const tags[] =	/* Value/group tag strings */
-			{
-			  "reserved-00",
-			  "operation-attributes-tag",
-			  "job-attributes-tag",
-			  "end-of-attributes-tag",
-			  "printer-attributes-tag",
-			  "unsupported-attributes-tag",
-			  "subscription-attributes-tag",
-			  "event-attributes-tag",
-			  "reserved-08",
-			  "reserved-09",
-			  "reserved-0A",
-			  "reserved-0B",
-			  "reserved-0C",
-			  "reserved-0D",
-			  "reserved-0E",
-			  "reserved-0F",
-			  "unsupported",
-			  "default",
-			  "unknown",
-			  "no-value",
-			  "reserved-14",
-			  "not-settable",
-			  "delete-attr",
-			  "admin-define",
-			  "reserved-18",
-			  "reserved-19",
-			  "reserved-1A",
-			  "reserved-1B",
-			  "reserved-1C",
-			  "reserved-1D",
-			  "reserved-1E",
-			  "reserved-1F",
-			  "reserved-20",
-			  "integer",
-			  "boolean",
-			  "enum",
-			  "reserved-24",
-			  "reserved-25",
-			  "reserved-26",
-			  "reserved-27",
-			  "reserved-28",
-			  "reserved-29",
-			  "reserved-2a",
-			  "reserved-2b",
-			  "reserved-2c",
-			  "reserved-2d",
-			  "reserved-2e",
-			  "reserved-2f",
-			  "octetString",
-			  "dateTime",
-			  "resolution",
-			  "rangeOfInteger",
-			  "begCollection",
-			  "textWithLanguage",
-			  "nameWithLanguage",
-			  "endCollection",
-			  "reserved-38",
-			  "reserved-39",
-			  "reserved-3a",
-			  "reserved-3b",
-			  "reserved-3c",
-			  "reserved-3d",
-			  "reserved-3e",
-			  "reserved-3f",
-			  "reserved-40",
-			  "textWithoutLanguage",
-			  "nameWithoutLanguage",
-			  "reserved-43",
-			  "keyword",
-			  "uri",
-			  "uriScheme",
-			  "charset",
-			  "naturalLanguage",
-			  "mimeMediaType",
-			  "memberName"
-			};
+  char                  buffer[2048];   /* Value string */
 
 
   for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
@@ -907,83 +828,12 @@
     {
       group = attr->group_tag;
 
-      printf("\n%*s%s:\n\n", indent - 4, "", tags[group]);
+      printf("\n%*s%s:\n\n", indent - 4, "", ippTagString(group));
     }
 
-    printf("%*s%s (", indent, "", attr->name ? attr->name : "(null)");
-    if (attr->num_values > 1)
-      printf("1setOf ");
-    printf("%s):", tags[attr->value_tag]);
+    ippAttributeString(attr, buffer, sizeof(buffer));
 
-    switch (attr->value_tag)
-    {
-      case IPP_TAG_ENUM :
-      case IPP_TAG_INTEGER :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %d", val->integer);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_BOOLEAN :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %s", val->boolean ? "true" : "false");
-          putchar('\n');
-          break;
-
-      case IPP_TAG_RANGE :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %d-%d", val->range.lower, val->range.upper);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_DATE :
-          {
-	    char	vstring[256];	/* Formatted time */
-
-	    for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	      printf(" (%s)", _cupsStrDate(vstring, sizeof(vstring), ippDateToTime(val->date)));
-          }
-          putchar('\n');
-          break;
-
-      case IPP_TAG_RESOLUTION :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %dx%d%s", val->resolution.xres, val->resolution.yres,
-	           val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
-          putchar('\n');
-          break;
-
-      case IPP_TAG_STRING :
-      case IPP_TAG_TEXTLANG :
-      case IPP_TAG_NAMELANG :
-      case IPP_TAG_TEXT :
-      case IPP_TAG_NAME :
-      case IPP_TAG_KEYWORD :
-      case IPP_TAG_URI :
-      case IPP_TAG_URISCHEME :
-      case IPP_TAG_CHARSET :
-      case IPP_TAG_LANGUAGE :
-      case IPP_TAG_MIMETYPE :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" \"%s\"", val->string.text);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_BEGIN_COLLECTION :
-          putchar('\n');
-
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	  {
-	    if (i)
-	      putchar('\n');
-	    print_attributes(val->collection, indent + 4);
-	  }
-          break;
-
-      default :
-          printf("UNKNOWN (%d values)\n", attr->num_values);
-          break;
-    }
+    printf("%*s%s (%s%s): %s\n", indent, "", attr->name ? attr->name : "(null)", attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), buffer);
   }
 }
 
diff --git a/cups/testlang.c b/cups/testlang.c
index 6aa49ab..3f140d9 100644
--- a/cups/testlang.c
+++ b/cups/testlang.c
@@ -1,7 +1,7 @@
 /*
  * Localization test program for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -19,6 +19,16 @@
 
 #include "cups-private.h"
 #include "ppd-private.h"
+#ifdef __APPLE__
+#  include <CoreFoundation/CoreFoundation.h>
+#endif /* __APPLE__ */
+
+
+/*
+ * Local functions...
+ */
+
+static int  test_string(cups_lang_t *language, const char *msgid);
 
 
 /*
@@ -71,8 +81,9 @@
 
   printf("Language = \"%s\"\n", language->language);
   printf("Encoding = \"%s\"\n", _cupsEncodingName(language->encoding));
-  printf("No       = \"%s\"\n", _cupsLangString(language, "No"));
-  printf("Yes      = \"%s\"\n", _cupsLangString(language, "Yes"));
+
+  errors += test_string(language, "No");
+  errors += test_string(language, "Yes");
 
   if (language != language2)
   {
@@ -138,9 +149,141 @@
         }
       }
 
+      printf("media-empty: %s\n", ppdLocalizeIPPReason(ppd, "media-empty", NULL, buffer, sizeof(buffer)));
+
       ppdClose(ppd);
     }
   }
+#ifdef __APPLE__
+  else
+  {
+   /*
+    * Test all possible language IDs for compatibility with _cupsAppleLocale...
+    */
+
+    CFIndex     j,                      /* Looping var */
+                num_locales;            /* Number of locales */
+    CFArrayRef  locales;                /* Locales */
+    CFStringRef locale_id,              /* Current locale ID */
+                language_id;            /* Current language ID */
+    char        locale_str[256],        /* Locale ID C string */
+                language_str[256],      /* Language ID C string */
+                *bufptr;                /* Pointer to ".UTF-8" in POSIX locale */
+    size_t      buflen;                 /* Length of POSIX locale */
+#  if TEST_COUNTRY_CODES
+    CFIndex     k,                      /* Looping var */
+                num_country_codes;      /* Number of country codes */
+    CFArrayRef  country_codes;          /* Country codes */
+    CFStringRef country_code,           /* Current country code */
+                temp_id;                /* Temporary language ID */
+    char        country_str[256];       /* Country code C string */
+#  endif /* TEST_COUNTRY_CODES */
+
+    locales     = CFLocaleCopyAvailableLocaleIdentifiers();
+    num_locales = CFArrayGetCount(locales);
+
+#  if TEST_COUNTRY_CODES
+    country_codes     = CFLocaleCopyISOCountryCodes();
+    num_country_codes = CFArrayGetCount(country_codes);
+#  endif /* TEST_COUNTRY_CODES */
+
+    printf("%d locales are available:\n", (int)num_locales);
+
+    for (j = 0; j < num_locales; j ++)
+    {
+      locale_id   = CFArrayGetValueAtIndex(locales, j);
+      language_id = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, locale_id);
+
+      if (!locale_id || !CFStringGetCString(locale_id, locale_str, (CFIndex)sizeof(locale_str), kCFStringEncodingASCII))
+      {
+        printf("%d: FAIL (unable to get locale ID string)\n", (int)j + 1);
+        errors ++;
+        continue;
+      }
+
+      if (!language_id || !CFStringGetCString(language_id, language_str, (CFIndex)sizeof(language_str), kCFStringEncodingASCII))
+      {
+        printf("%d %s: FAIL (unable to get language ID string)\n", (int)j + 1, locale_str);
+        errors ++;
+        continue;
+      }
+
+      if (!_cupsAppleLocale(language_id, buffer, sizeof(buffer)))
+      {
+        printf("%d %s(%s): FAIL (unable to convert language ID string to POSIX locale)\n", (int)j + 1, locale_str, language_str);
+        errors ++;
+        continue;
+      }
+
+      if ((bufptr = strstr(buffer, ".UTF-8")) != NULL)
+        buflen = (size_t)(bufptr - buffer);
+      else
+        buflen = strlen(buffer);
+
+      if ((language = cupsLangGet(buffer)) == NULL)
+      {
+        printf("%d %s(%s): FAIL (unable to load POSIX locale \"%s\")\n", (int)j + 1, locale_str, language_str, buffer);
+        errors ++;
+        continue;
+      }
+
+      if (strncasecmp(language->language, buffer, buflen))
+      {
+        printf("%d %s(%s): FAIL (unable to load POSIX locale \"%s\", got \"%s\")\n", (int)j + 1, locale_str, language_str, buffer, language->language);
+        errors ++;
+        continue;
+      }
+
+      printf("%d %s(%s): PASS (POSIX locale is \"%s\")\n", (int)j + 1, locale_str, language_str, buffer);
+    }
+
+    CFRelease(locales);
+
+#  if TEST_COUNTRY_CODES
+    CFRelease(country_codes);
+#  endif /* TEST_COUNTRY_CODES */
+  }
+#endif /* __APPLE__ */
+
+  if (errors == 0)
+    puts("ALL TESTS PASSED");
 
   return (errors > 0);
 }
+
+
+/*
+ * 'test_string()' - Test the localization of a string.
+ */
+
+static int                            /* O - 1 on failure, 0 on success */
+test_string(cups_lang_t *language,    /* I - Language */
+            const char  *msgid)       /* I - Message */
+{
+  const char  *msgstr;                /* Localized string */
+
+
+ /*
+  * Get the localized string and then see if we got what we expected.
+  *
+  * For the POSIX locale, the string pointers should be the same.
+  * For any other locale, the string pointers should be different.
+  */
+
+  msgstr = _cupsLangString(language, msgid);
+  if (strcmp(language->language, "C") && msgid == msgstr)
+  {
+    printf("%-8s = \"%s\" (FAIL - no message catalog loaded)\n", msgid, msgstr);
+    return (1);
+  }
+  else if (!strcmp(language->language, "C") && msgid != msgstr)
+  {
+    printf("%-8s = \"%s\" (FAIL - POSIX locale is localized)\n", msgid, msgstr);
+    return (1);
+  }
+
+  printf("%-8s = \"%s\" (PASS)\n", msgid, msgstr);
+
+  return (0);
+}
+
diff --git a/cups/thread-private.h b/cups/thread-private.h
index ca4ef4c..79d2438 100644
--- a/cups/thread-private.h
+++ b/cups/thread-private.h
@@ -1,7 +1,7 @@
 /*
  * Private threading definitions for CUPS.
  *
- * Copyright 2009-2016 by Apple Inc.
+ * Copyright 2009-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -99,6 +99,7 @@
 extern void	_cupsRWUnlock(_cups_rwlock_t *rwlock);
 extern void	_cupsThreadCancel(_cups_thread_t thread);
 extern _cups_thread_t _cupsThreadCreate(_cups_thread_func_t func, void *arg);
+extern void     _cupsThreadDetach(_cups_thread_t thread);
 extern void	*_cupsThreadWait(_cups_thread_t thread);
 
 #  ifdef __cplusplus
diff --git a/cups/thread.c b/cups/thread.c
index 77b4442..65257aa 100644
--- a/cups/thread.c
+++ b/cups/thread.c
@@ -1,7 +1,7 @@
 /*
  * Threading primitives for CUPS.
  *
- * Copyright 2009-2016 by Apple Inc.
+ * Copyright 2009-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -173,6 +173,17 @@
 
 
 /*
+ * '_cupsThreadDetach()' - Tell the OS that the thread is running independently.
+ */
+
+void
+_cupsThreadDetach(_cups_thread_t thread)/* I - Thread ID */
+{
+  pthread_detach(thread);
+}
+
+
+/*
  * '_cupsThreadWait()' - Wait for a thread to exit.
  */
 
@@ -344,6 +355,18 @@
 
 
 /*
+ * '_cupsThreadDetach()' - Tell the OS that the thread is running independently.
+ */
+
+void
+_cupsThreadDetach(_cups_thread_t thread)/* I - Thread ID */
+{
+  // TODO: Implement me
+  (void)thread;
+}
+
+
+/*
  * '_cupsThreadWait()' - Wait for a thread to exit.
  */
 
diff --git a/cups/tls-darwin.c b/cups/tls-darwin.c
index b6e88b0..92430ac 100644
--- a/cups/tls-darwin.c
+++ b/cups/tls-darwin.c
@@ -1,7 +1,7 @@
 /*
  * TLS support code for CUPS on macOS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -21,7 +21,7 @@
 
 #include <spawn.h>
 
-extern char **environ;
+extern char **environ; /* @private@ */
 
 
 /*
@@ -1141,7 +1141,8 @@
 void
 _httpTLSSetOptions(int options)		/* I - Options */
 {
-  tls_options = options;
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+    tls_options = options;
 }
 
 
@@ -1227,6 +1228,12 @@
 
     error = SSLSetProtocolVersionMin(http->tls, minProtocol);
     DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", minProtocol, (int)error));
+
+    if (!error && (tls_options & _HTTP_TLS_ONLY_TLS10))
+    {
+      error = SSLSetProtocolVersionMax(http->tls, kTLSProtocol1);
+      DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMax(kTLSProtocol1), error=%d", (int)error));
+    }
   }
 
 #  if HAVE_SSLSETENABLEDCIPHERS
@@ -1324,7 +1331,6 @@
           case TLS_DHE_RSA_WITH_AES_256_CBC_SHA :
           case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA :
           case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA :
-//          case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
           case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA :
           case TLS_DH_DSS_WITH_AES_128_CBC_SHA256 :
           case TLS_DH_RSA_WITH_AES_128_CBC_SHA256 :
@@ -1337,6 +1343,14 @@
           case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA :
           case TLS_DHE_PSK_WITH_AES_128_CBC_SHA :
           case TLS_DHE_PSK_WITH_AES_256_CBC_SHA :
+          case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
+          case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
+	      if (tls_options & _HTTP_TLS_DENY_CBC)
+	      {
+	        DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
+	        break;
+	      }
+
 //          case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
 //          case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
           case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 :
@@ -1347,15 +1361,31 @@
           case TLS_DH_DSS_WITH_AES_256_GCM_SHA384 :
           case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 :
           case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 :
-          case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
-          case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
               if (tls_options & _HTTP_TLS_ALLOW_DH)
 	        enabled[num_enabled ++] = supported[i];
 	      else
 		DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i]));
               break;
 
-          /* Anything else we'll assume is secure */
+          case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
+          case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_RSA_WITH_3DES_EDE_CBC_SHA :
+          case TLS_RSA_WITH_AES_128_CBC_SHA :
+          case TLS_RSA_WITH_AES_256_CBC_SHA :
+              if (tls_options & _HTTP_TLS_DENY_CBC)
+	      {
+	        DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
+	        break;
+	      }
+
+          /* Anything else we'll assume is "secure" */
           default :
 	      enabled[num_enabled ++] = supported[i];
 	      break;
diff --git a/cups/tls-gnutls.c b/cups/tls-gnutls.c
index 7193d79..2dcb7fe 100644
--- a/cups/tls-gnutls.c
+++ b/cups/tls-gnutls.c
@@ -1,7 +1,7 @@
 /*
  * TLS support code for CUPS using GNU TLS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -397,7 +397,7 @@
         for (i = 0; i < count; i ++)
 	{
 	  rserial_size = sizeof(rserial);
-          if (!gnutls_x509_crl_get_crt_serial(tls_crl, i, rserial, &rserial_size, NULL) && cserial_size == rserial_size && !memcmp(cserial, rserial, rserial_size))
+          if (!gnutls_x509_crl_get_crt_serial(tls_crl, (unsigned)i, rserial, &rserial_size, NULL) && cserial_size == rserial_size && !memcmp(cserial, rserial, rserial_size))
 	  {
 	    result = 0;
 	    break;
@@ -1226,7 +1226,8 @@
 void
 _httpTLSSetOptions(int options)		/* I - Options */
 {
-  tls_options = options;
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+    tls_options = options;
 }
 
 
@@ -1242,7 +1243,7 @@
   int			status;		/* Status of handshake */
   gnutls_certificate_credentials_t *credentials;
 					/* TLS credentials */
-  char			priority_string[1024];
+  char			priority_string[2048];
 					/* Priority string */
 
 
@@ -1508,15 +1509,21 @@
   if (tls_options & _HTTP_TLS_DENY_TLS10)
     strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-TLS1.0:-VERS-SSL3.0", sizeof(priority_string));
   else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
-    strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
+    strlcat(priority_string, ":+VERS-TLS-ALL:+VERS-SSL3.0", sizeof(priority_string));
+  else if (tls_options & _HTTP_TLS_ONLY_TLS10)
+    strlcat(priority_string, ":-VERS-TLS-ALL:-VERS-SSL3.0:+VERS-TLS1.0", sizeof(priority_string));
   else
     strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-SSL3.0", sizeof(priority_string));
 
-  if (!(tls_options & _HTTP_TLS_ALLOW_RC4))
-    strlcat(priority_string, ":-ARCFOUR-128", sizeof(priority_string));
+  if (tls_options & _HTTP_TLS_ALLOW_RC4)
+    strlcat(priority_string, ":+ARCFOUR-128", sizeof(priority_string));
+  else
+    strlcat(priority_string, ":!ARCFOUR-128", sizeof(priority_string));
 
-  if (!(tls_options & _HTTP_TLS_ALLOW_DH))
-    strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
+  strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
+
+  if (tls_options & _HTTP_TLS_DENY_CBC)
+    strlcat(priority_string, ":!AES-128-CBC:!AES-256-CBC:!CAMELLIA-128-CBC:!CAMELLIA-256-CBC:!3DES-CBC", sizeof(priority_string));
 
 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
   gnutls_priority_set_direct(http->tls, priority_string, NULL);
diff --git a/cups/tls-sspi.c b/cups/tls-sspi.c
index 46f6e79..6eaec4c 100644
--- a/cups/tls-sspi.c
+++ b/cups/tls-sspi.c
@@ -2,7 +2,7 @@
  * TLS support for CUPS on Windows using the Security Support Provider
  * Interface (SSPI).
  *
- * Copyright 2010-2015 by Apple Inc.
+ * Copyright 2010-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -913,7 +913,8 @@
 void
 _httpTLSSetOptions(int options)		/* I - Options */
 {
-  tls_options = options;
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+    tls_options = options;
 }
 
 
@@ -1795,7 +1796,7 @@
   }
 #endif /* SP_PROT_TLS1_2_SERVER */
 
-  /* TODO: Support _HTTP_TLS_ALLOW_RC4 and _HTTP_TLS_ALLOW_DH options; right now we'll rely on Windows registry to enable/disable RC4/DH... */
+  /* TODO: Support _HTTP_TLS_ALLOW_RC4, _HTTP_TLS_ALLOW_DH, and _HTTP_TLS_DENY_CBC options; right now we'll rely on Windows registry to enable/disable RC4/DH/CBC... */
 
  /*
   * Create an SSPI credential.
diff --git a/cups/tlscheck.c b/cups/tlscheck.c
index 32cbcca..997e7aa 100644
--- a/cups/tlscheck.c
+++ b/cups/tlscheck.c
@@ -1,7 +1,7 @@
 /*
  * TLS check program for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -82,10 +82,18 @@
     {
       tls_options |= _HTTP_TLS_ALLOW_DH;
     }
+    else if (!strcmp(argv[i], "--no-cbc"))
+    {
+      tls_options |= _HTTP_TLS_DENY_CBC;
+    }
     else if (!strcmp(argv[i], "--no-tls10"))
     {
       tls_options |= _HTTP_TLS_DENY_TLS10;
     }
+    else if (!strcmp(argv[i], "--tls10"))
+    {
+      tls_options |= _HTTP_TLS_ONLY_TLS10;
+    }
     else if (!strcmp(argv[i], "--rc4"))
     {
       tls_options |= _HTTP_TLS_ALLOW_RC4;
@@ -729,8 +737,10 @@
   puts("");
   puts("Options:");
   puts("  --dh        Allow DH/DHE key exchange");
+  puts("  --no-cbc    Disable CBC cipher suites");
   puts("  --no-tls10  Disable TLS/1.0");
   puts("  --rc4       Allow RC4 encryption");
+  puts("  --tls10     Only use TLS/1.0");
   puts("  --verbose   Be verbose");
   puts("  -4          Connect using IPv4 addresses only");
   puts("  -6          Connect using IPv6 addresses only");
diff --git a/cups/usersys.c b/cups/usersys.c
index 333d21e..2a004b5 100644
--- a/cups/usersys.c
+++ b/cups/usersys.c
@@ -1,7 +1,7 @@
 /*
  * User, system, and password routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -132,6 +132,8 @@
  * thread in a program. Multi-threaded programs that override the setting via
  * the @link cupsSetPasswordCB@ or @link cupsSetPasswordCB2@ functions need to
  * do so in each thread for the same function to be used.
+ *
+ * @exclude all@
  */
 
 const char *				/* O - Password */
@@ -145,7 +147,7 @@
 
 
 /*
- * 'cupsGetPassword2()' - Get a password from the user using the advanced
+ * 'cupsGetPassword2()' - Get a password from the user using the current
  *                        password callback.
  *
  * Uses the current password callback function. Returns @code NULL@ if the
@@ -153,8 +155,8 @@
  *
  * Note: The current password callback function is tracked separately for each
  * thread in a program. Multi-threaded programs that override the setting via
- * the @link cupsSetPasswordCB@ or @link cupsSetPasswordCB2@ functions need to
- * do so in each thread for the same function to be used.
+ * the @link cupsSetPasswordCB2@ function need to do so in each thread for the
+ * same function to be used.
  *
  * @since CUPS 1.4/macOS 10.6@
  */
@@ -297,6 +299,8 @@
  * Note: The current password callback is tracked separately for each thread
  * in a program. Multi-threaded programs that override the callback need to do
  * so in each thread for the same callback to be used.
+ *
+ * @exclude all@
  */
 
 void
@@ -953,7 +957,7 @@
     cg->validate_certs = cc.validate_certs;
 
 #ifdef HAVE_SSL
-  _httpTLSSetOptions(cc.ssl_options);
+  _httpTLSSetOptions(cc.ssl_options | _HTTP_TLS_SET_DEFAULT);
 #endif /* HAVE_SSL */
 }
 
@@ -1171,7 +1175,7 @@
   * everything...)
   */
 
-#ifdef __APPLE__
+#if defined(__APPLE__) && defined(HAVE_SSL)
   char	sval[1024];			/* String value */
   int	bval;				/* Boolean value */
 
@@ -1192,7 +1196,7 @@
 
   if (cups_apple_get_boolean(kValidateCertsKey, &bval))
     cc->validate_certs = bval;
-#endif /* __APPLE__ */
+#endif /* __APPLE__ && HAVE_SSL */
 }
 
 
@@ -1363,6 +1367,8 @@
       options |= _HTTP_TLS_ALLOW_SSL3;
     else if (!_cups_strcasecmp(start, "AllowDH"))
       options |= _HTTP_TLS_ALLOW_DH;
+    else if (!_cups_strcasecmp(start, "DenyCBC"))
+      options |= _HTTP_TLS_DENY_CBC;
     else if (!_cups_strcasecmp(start, "DenyTLS1.0"))
       options |= _HTTP_TLS_DENY_TLS10;
     else if (!_cups_strcasecmp(start, "None"))
diff --git a/cups/util.c b/cups/util.c
index 5db2fc6..2f5ebdf 100644
--- a/cups/util.c
+++ b/cups/util.c
@@ -1,7 +1,7 @@
 /*
  * Printing utilities for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -28,6 +28,19 @@
 
 
 /*
+ * Enumeration data and callback...
+ */
+
+typedef struct _cups_createdata_s
+{
+  const char  *name;                    /* Destination name */
+  cups_dest_t *dest;                    /* Matching destination */
+} _cups_createdata_t;
+
+static int  cups_create_cb(_cups_createdata_t *data, unsigned flags, cups_dest_t *dest);
+
+
+/*
  * 'cupsCancelJob()' - Cancel a print job on the default server.
  *
  * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
@@ -35,6 +48,8 @@
  *
  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
  * the cause of any failure.
+ *
+ * @exclude all@
  */
 
 int					/* O - 1 on success, 0 on failure */
@@ -58,7 +73,7 @@
  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
  * the cause of any failure.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 ipp_status_t				/* O - IPP status */
@@ -146,7 +161,7 @@
  * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
  * instead.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -157,12 +172,10 @@
     int           num_options,		/* I - Number of options */
     cups_option_t *options)		/* I - Options */
 {
-  char		printer_uri[1024],	/* Printer URI */
-		resource[1024];		/* Printer resource */
-  ipp_t		*request,		/* Create-Job request */
-		*response;		/* Create-Job response */
-  ipp_attribute_t *attr;		/* job-id attribute */
   int		job_id = 0;		/* job-id value */
+  ipp_status_t  status;                 /* Create-Job status */
+  _cups_createdata_t data;              /* Enumeration data */
+  cups_dinfo_t  *info;                  /* Destination information */
 
 
   DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, title, num_options, (void *)options));
@@ -178,46 +191,47 @@
   }
 
  /*
-  * Build a Create-Job request...
+  * Lookup the destination...
   */
 
-  if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
+  data.name = name;
+  data.dest = NULL;
+
+  cupsEnumDests(0, 1000, NULL, 0, 0, (cups_dest_cb_t)cups_create_cb, &data);
+
+  if (!data.dest)
   {
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
+    DEBUG_puts("1cupsCreateJob: Destination not found.");
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
     return (0);
   }
 
-  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
-                   NULL, "localhost", ippPort(), "/printers/%s", name);
-  snprintf(resource, sizeof(resource), "/printers/%s", name);
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, printer_uri);
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-               NULL, cupsUser());
-  if (title)
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
-                 title);
-  cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
-  cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
-  cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
-
  /*
-  * Send the request and get the job-id...
+  * Query dest information and create the job...
   */
 
-  response = cupsDoRequest(http, request, resource);
+  DEBUG_puts("1cupsCreateJob: Querying destination info.");
+  if ((info = cupsCopyDestInfo(http, data.dest)) == NULL)
+  {
+    DEBUG_puts("1cupsCreateJob: Query failed.");
+    cupsFreeDests(1, data.dest);
+    return (0);
+  }
 
-  if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
-    job_id = attr->values[0].integer;
+  status = cupsCreateDestJob(http, data.dest, info, &job_id, title, num_options, options);
+  DEBUG_printf(("1cupsCreateJob: cupsCreateDestJob returned %04x (%s)", status, ippErrorString(status)));
 
-  ippDelete(response);
+  cupsFreeDestInfo(info);
+  cupsFreeDests(1, data.dest);
 
  /*
-  * Return it...
+  * Return the job...
   */
 
-  return (job_id);
+  if (status >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+    return (0);
+  else
+    return (job_id);
 }
 
 
@@ -226,7 +240,7 @@
  *
  * The document must have been started using @link cupsStartDocument@.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 ipp_status_t				/* O - Status of document submission */
@@ -277,7 +291,7 @@
  * This function is deprecated and no longer returns a list of printer
  * classes - use @link cupsGetDests@ instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of classes */
@@ -299,6 +313,8 @@
  * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
  * functions to get the user-defined default printer, as this function does
  * not support the lpoptions-defined default printer.
+ *
+ * @exclude all@
  */
 
 const char *				/* O - Default printer or @code NULL@ */
@@ -322,7 +338,7 @@
  * functions to get the user-defined default printer, as this function does
  * not support the lpoptions-defined default printer.
  *
- * @since CUPS 1.1.21/macOS 10.4@
+ * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
  */
 
 const char *				/* O - Default printer or @code NULL@ */
@@ -388,6 +404,8 @@
  * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
  * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
  * jobs that are stopped, canceled, aborted, or completed.
+ *
+ * @exclude all@
  */
 
 int					/* O - Number of jobs */
@@ -683,7 +701,7 @@
  * This function is deprecated and no longer returns a list of printers - use
  * @link cupsGetDests@ instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of printers */
@@ -698,6 +716,8 @@
 
 /*
  * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
+ *
+ * @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -718,7 +738,7 @@
  * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
  *                      server.
  *
- * @since CUPS 1.1.21/macOS 10.4@
+ * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -740,6 +760,8 @@
 /*
  * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
  *                      default server.
+ *
+ * @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -766,7 +788,7 @@
  * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
  *                       specified server.
  *
- * @since CUPS 1.1.21/macOS 10.4@
+ * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -896,7 +918,7 @@
  * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
  * any supported MIME type string can be supplied.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 http_status_t				/* O - HTTP status of request */
@@ -951,3 +973,26 @@
 
   return (status);
 }
+
+
+/*
+ * 'cups_create_cb()' - Find the destination for printing.
+ */
+
+static int                              /* O - 0 on match */
+cups_create_cb(
+    _cups_createdata_t *data,           /* I - Data from cupsCreateJob call */
+    unsigned           flags,           /* I - Enumeration flags */
+    cups_dest_t        *dest)           /* I - Destination */
+{
+  DEBUG_printf(("2cups_create_cb(data=%p(%s), flags=%08x, dest=%p(%s))", (void *)data, data->name, flags, (void *)dest, dest->name));
+
+  (void)flags;
+
+  if (dest->instance || strcasecmp(data->name, dest->name))
+    return (1);
+
+  cupsCopyDest(dest, 0, &data->dest);
+
+  return (0);
+}
diff --git a/cups/versioning.h b/cups/versioning.h
index 2e92e6b..5a000c0 100644
--- a/cups/versioning.h
+++ b/cups/versioning.h
@@ -1,7 +1,7 @@
 /*
  * API versioning definitions for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -17,11 +17,10 @@
 
 /*
  * This header defines several constants - _CUPS_DEPRECATED,
- * _CUPS_DEPRECATED_MSG, _CUPS_INTERNAL_MSG, _CUPS_API_1_1, _CUPS_API_1_1_19,
- * _CUPS_API_1_1_20, _CUPS_API_1_1_21, _CUPS_API_1_2, _CUPS_API_1_3,
- * _CUPS_API_1_4, _CUPS_API_1_5, _CUPS_API_1_6, _CUPS_API_1_7, and
- * _CUPS_API_2_0 - which add compiler-specific attributes that flag functions
- * that are deprecated, added in particular releases, or internal to CUPS.
+ * _CUPS_DEPRECATED_MSG, _CUPS_INTERNAL_MSG, _CUPS_API_major_minor, and
+ * _CUPS_API_major_minor_patch - which add compiler-specific attributes that
+ * flag functions that are deprecated, added in particular releases, or internal
+ * to CUPS.
  *
  * On macOS, the _CUPS_API_* constants are defined based on the values of
  * the MAC_OS_X_VERSION_MIN_ALLOWED and MAC_OS_X_VERSION_MAX_ALLOWED constants
@@ -57,6 +56,9 @@
 #    ifndef AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
 #      define AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER __attribute__((unavailable))
 #    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER */
+#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
+#      define AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER __attribute__((unavailable))
+#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER */
 #    define _CUPS_API_1_1_19 AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
 #    define _CUPS_API_1_1_20 AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER
 #    define _CUPS_API_1_1_21 AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER
@@ -68,6 +70,7 @@
 #    define _CUPS_API_1_7 AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER
 #    define _CUPS_API_2_0 AVAILABLE_MAC_OS_X_VERSION_10_10_AND_LATER
 #    define _CUPS_API_2_2 AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
+#    define _CUPS_API_2_2_4 AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
 #  else
 #    define _CUPS_API_1_1_19
 #    define _CUPS_API_1_1_20
@@ -80,6 +83,7 @@
 #    define _CUPS_API_1_7
 #    define _CUPS_API_2_0
 #    define _CUPS_API_2_2
+#    define _CUPS_API_2_2_4
 #  endif /* __APPLE__ && !_CUPS_SOURCE */
 
 /*
diff --git a/filter/Makefile b/filter/Makefile
index 7da764d..02aab14 100644
--- a/filter/Makefile
+++ b/filter/Makefile
@@ -1,7 +1,7 @@
 #
 # Filter makefile for CUPS.
 #
-# Copyright 2007-2012 by Apple Inc.
+# Copyright 2007-2017 by Apple Inc.
 # Copyright 1997-2006 by Easy Software Products.
 #
 # These coded instructions, statements, and computer programs are the
@@ -29,6 +29,7 @@
 		libcupsimage.a
 UNITTARGETS =	\
 		rasterbench \
+		testclient \
 		testraster
 TARGETS	=	\
 		$(LIBTARGETS) \
@@ -38,7 +39,7 @@
 OBJS	=	$(IMAGEOBJS) \
 		commandtops.o gziptoany.o common.o pstops.o \
 		rasterbench.o rastertoepson.o rastertohp.o rastertolabel.o \
-		rastertopwg.o testraster.o
+		rastertopwg.o testclient.o testraster.o
 
 
 #
@@ -216,7 +217,7 @@
 
 commandtops:	commandtops.o ../cups/$(LIBCUPS)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ commandtops.o $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ commandtops.o $(LIBS)
 
 
 #
@@ -225,7 +226,7 @@
 
 gziptoany:	gziptoany.o ../Makedefs ../cups/$(LIBCUPS)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ gziptoany.o $(LIBZ) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ gziptoany.o $(LIBZ) $(LIBS)
 
 
 #
@@ -295,7 +296,7 @@
 
 pstops:	pstops.o common.o ../cups/$(LIBCUPS)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
 
 
 #
@@ -304,7 +305,7 @@
 
 rastertoepson:	rastertoepson.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertoepson.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertoepson.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
 
 
 #
@@ -313,7 +314,7 @@
 
 rastertohp:	rastertohp.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertohp.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertohp.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
 
 
 #
@@ -322,7 +323,7 @@
 
 rastertolabel:	rastertolabel.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertolabel.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertolabel.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
 
 
 #
@@ -331,22 +332,33 @@
 
 rastertopwg:	rastertopwg.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertopwg.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertopwg.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
 
 rastertopwg-static:	rastertopwg.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertopwg.o libcupsimage.a \
+	$(LD_CC) $(LDFLAGS) -o $@ rastertopwg.o libcupsimage.a \
 		../cups/$(LIBCUPSSTATIC) $(IMGLIBS) $(DSOLIBS) $(COMMONLIBS) \
 		$(SSLLIBS) $(DNSSDLIBS) $(LIBGSSAPI)
 
 
 #
+# testclient (dependency on static libraries is intentional)
+#
+
+testclient:	testclient.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
+	echo Linking $@...
+	$(LD_CC) $(LDFLAGS) -o $@ testclient.o \
+		libcupsimage.a ../cups/$(LIBCUPSSTATIC) \
+		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
 # testraster
 #
 
 testraster:	testraster.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testraster.o libcupsimage.a \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testraster.o libcupsimage.a \
 		../cups/$(LIBCUPSSTATIC) $(IMGLIBS) $(DSOLIBS) $(COMMONLIBS) \
 		$(SSLLIBS) $(DNSSDLIBS) $(LIBGSSAPI)
 	echo Running raster API tests...
@@ -359,7 +371,7 @@
 
 rasterbench:	rasterbench.o libcupsimage.a
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rasterbench.o libcupsimage.a $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rasterbench.o libcupsimage.a $(LIBS)
 
 
 #
diff --git a/filter/testclient.c b/filter/testclient.c
new file mode 100644
index 0000000..3a9c032
--- /dev/null
+++ b/filter/testclient.c
@@ -0,0 +1,960 @@
+/*
+ * Simulated client test program for CUPS.
+ *
+ * Copyright 2017 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
+ * missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+#include <cups/raster.h>
+#include <cups/string-private.h>
+#include <cups/thread-private.h>
+
+
+/*
+ * Local types...
+ */
+
+typedef struct _client_monitor_s
+{
+  const char		*uri,		/* Printer URI */
+			*hostname,	/* Hostname */
+			*user,		/* Username */
+			*resource;	/* Resource path */
+  int			port;		/* Port number */
+  http_encryption_t	encryption;	/* Use encryption? */
+  ipp_pstate_t		printer_state;	/* Current printer state */
+  char                  printer_state_reasons[1024];
+                                        /* Current printer-state-reasons */
+  int			job_id; 	/* Job ID for submitted job */
+  ipp_jstate_t		job_state;	/* Current job state */
+  char                  job_state_reasons[1024];
+                                        /* Current job-state-reasons */
+} _client_monitor_t;
+
+
+/*
+ * Local functions...
+ */
+
+static const char       *make_raster_file(ipp_t *response, int grayscale, char *tempname, size_t tempsize, const char **format);
+static void	        *monitor_printer(_client_monitor_t *monitor);
+static void             show_attributes(const char *title, int request, ipp_t *ipp);
+static void             show_capabilities(ipp_t *response);
+static void             usage(void);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int					/* O - Exit status */
+main(int  argc,				/* I - Number of command-line arguments */
+     char *argv[])			/* I - Command-line arguments */
+{
+  int                   i;              /* Looping var */
+  const char            *opt,           /* Current option */
+                        *uri = NULL,    /* Printer URI */
+                        *printfile = NULL,
+                                        /* Print file */
+                        *printformat = NULL;
+                                        /* Print format */
+  int                   keepfile = 0,   /* Keep temp file? */
+                        grayscale = 0,  /* Force grayscale? */
+                        verbosity = 0;  /* Verbosity */
+  char                  tempfile[1024] = "",
+                                        /* Temporary file (if any) */
+                        scheme[32],     /* URI scheme */
+                        userpass[256],  /* Username:password */
+                        hostname[256],  /* Hostname */
+                        resource[256];  /* Resource path */
+  int                   port;           /* Port number */
+  http_encryption_t     encryption;     /* Encryption mode */
+  _client_monitor_t     monitor;        /* Monitoring data */
+  http_t                *http;          /* HTTP connection */
+  ipp_t                 *request,       /* IPP request */
+                        *response;      /* IPP response */
+  ipp_attribute_t       *attr;          /* IPP attribute */
+  static const char * const pattrs[] =  /* Printer attributes we are interested in */
+  {
+    "job-template",
+    "printer-defaults",
+    "printer-description",
+    "media-col-database",
+    "media-col-ready"
+  };
+
+
+ /*
+  * Parse command-line options...
+  */
+
+  for (i = 1; i < argc; i ++)
+  {
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+      {
+        switch (*opt)
+        {
+          case 'd' : /* -d document-format */
+              if (printformat)
+              {
+                puts("Document format can only be specified once.");
+                usage();
+                return (1);
+              }
+
+              i ++;
+              if (i >= argc)
+              {
+                puts("Expected document format after '-d'.");
+                usage();
+                return (1);
+              }
+
+              printformat = argv[i];
+              break;
+
+          case 'f' : /* -f print-file */
+              if (printfile)
+              {
+                puts("Print file can only be specified once.");
+                usage();
+                return (1);
+              }
+
+              i ++;
+              if (i >= argc)
+              {
+                puts("Expected print file after '-f'.");
+                usage();
+                return (1);
+              }
+
+              printfile = argv[i];
+              break;
+
+          case 'g' :
+              grayscale = 1;
+              break;
+
+          case 'k' :
+              keepfile = 1;
+              break;
+
+          case 'v' :
+              verbosity ++;
+              break;
+
+          default :
+              printf("Unknown option '-%c'.\n", *opt);
+              usage();
+              return (1);
+        }
+      }
+    }
+    else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7)))
+    {
+      printf("Unknown command-line argument '%s'.\n", argv[i]);
+      usage();
+      return (1);
+    }
+    else
+      uri = argv[i];
+  }
+
+ /*
+  * Make sure we have everything we need.
+  */
+
+  if (!uri)
+  {
+    puts("Expected printer URI.");
+    usage();
+    return (1);
+  }
+
+ /*
+  * Connect to the printer...
+  */
+
+  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+  {
+    printf("Bad printer URI '%s'.\n", uri);
+    return (1);
+  }
+
+  if (!port)
+    port = IPP_PORT;
+
+  if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
+    encryption = HTTP_ENCRYPTION_ALWAYS;
+  else
+    encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+
+  if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL)
+  {
+    printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
+    return (1);
+  }
+
+ /*
+  * Query printer status and capabilities...
+  */
+
+  request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+  response = cupsDoRequest(http, request, resource);
+
+  if (verbosity)
+    show_capabilities(response);
+
+ /*
+  * Now figure out what we will be printing...
+  */
+
+  if (printfile)
+  {
+   /*
+    * User specified a print file, figure out the format...
+    */
+
+    if ((opt = strrchr(printfile, '.')) != NULL)
+    {
+     /*
+      * Guess the format from the extension...
+      */
+
+      if (!strcmp(opt, ".jpg"))
+        printformat = "image/jpeg";
+      else if (!strcmp(opt, ".pdf"))
+        printformat = "application/pdf";
+      else if (!strcmp(opt, ".ps"))
+        printformat = "application/postscript";
+      else if (!strcmp(opt, ".pwg"))
+        printformat = "image/pwg-raster";
+      else if (!strcmp(opt, ".urf"))
+        printformat = "image/urf";
+      else
+        printformat = "application/octet-stream";
+    }
+    else
+    {
+     /*
+      * Tell the printer to auto-detect...
+      */
+
+      printformat = "application/octet-stream";
+    }
+  }
+  else
+  {
+   /*
+    * No file specified, make something to test with...
+    */
+
+    if ((printfile = make_raster_file(response, grayscale, tempfile, sizeof(tempfile), &printformat)) == NULL)
+      return (1);
+  }
+
+  ippDelete(response);
+
+ /*
+  * Start monitoring the printer in the background...
+  */
+
+  memset(&monitor, 0, sizeof(monitor));
+
+  monitor.uri           = uri;
+  monitor.hostname      = hostname;
+  monitor.resource      = resource;
+  monitor.port          = port;
+  monitor.encryption    = encryption;
+
+  _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
+
+ /*
+  * Create the job and wait for completion...
+  */
+
+  request = ippNewRequest(IPP_OP_CREATE_JOB);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+
+  if ((opt = strrchr(printfile, '/')) != NULL)
+    opt ++;
+  else
+    opt = printfile;
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, opt);
+
+  if (verbosity)
+    show_attributes("Create-Job request", 1, request);
+
+  response = cupsDoRequest(http, request, resource);
+
+  if (verbosity)
+    show_attributes("Create-Job response", 0, response);
+
+  if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+  {
+    printf("Unable to create print job: %s\n", cupsLastErrorString());
+
+    monitor.job_state = IPP_JSTATE_ABORTED;
+
+    goto cleanup;
+  }
+
+  if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
+  {
+    puts("No job-id returned in Create-Job request.");
+
+    monitor.job_state = IPP_JSTATE_ABORTED;
+
+    goto cleanup;
+  }
+
+  monitor.job_id = ippGetInteger(attr, 0);
+
+  printf("CREATED JOB %d, sending %s of type %s\n", monitor.job_id, printfile, printformat);
+
+  ippDelete(response);
+
+  request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat);
+  ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+
+  if (verbosity)
+    show_attributes("Send-Document request", 1, request);
+
+  response = cupsDoFileRequest(http, request, resource, printfile);
+
+  if (verbosity)
+    show_attributes("Send-Document response", 0, response);
+
+  if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+  {
+    printf("Unable to print file: %s\n", cupsLastErrorString());
+
+    monitor.job_state = IPP_JSTATE_ABORTED;
+
+    goto cleanup;
+  }
+
+  puts("WAITING FOR JOB TO COMPLETE");
+
+  while (monitor.job_state < IPP_JSTATE_CANCELED)
+    sleep(1);
+
+ /*
+  * Cleanup after ourselves...
+  */
+
+  cleanup:
+
+  httpClose(http);
+
+  if (tempfile[0] && !keepfile)
+    unlink(tempfile);
+
+  return (monitor.job_state == IPP_JSTATE_COMPLETED);
+}
+
+
+/*
+ * 'make_raster_file()' - Create a temporary raster file.
+ */
+
+static const char *                     /* O - Print filename */
+make_raster_file(ipp_t      *response,  /* I - Printer attributes */
+                 int        grayscale,  /* I - Force grayscale? */
+                 char       *tempname,  /* I - Temporary filename buffer */
+                 size_t     tempsize,   /* I - Size of temp file buffer */
+                 const char **format)   /* O - Print format */
+{
+  int                   i,              /* Looping var */
+                        count;          /* Number of values */
+  ipp_attribute_t       *attr;          /* Printer attribute */
+  const char            *type = NULL;   /* Raster type (colorspace + bits) */
+  pwg_media_t           *media = NULL;  /* Media size */
+  int                   xdpi = 0,       /* Horizontal resolution */
+                        ydpi = 0;       /* Vertical resolution */
+  int                   fd;             /* Temporary file */
+  cups_mode_t           mode;           /* Raster mode */
+  cups_raster_t         *ras;           /* Raster stream */
+  cups_page_header2_t   header;         /* Page header */
+  unsigned char         *line,          /* Line of raster data */
+                        *lineptr;       /* Pointer into line */
+  unsigned              y,              /* Current position on page */
+                        xcount, ycount, /* Current count for X and Y */
+                        xrep, yrep,     /* Repeat count for X and Y */
+                        xoff, yoff,     /* Offsets for X and Y */
+                        yend;           /* End Y value */
+  int                   temprow,        /* Row in template */
+                        tempcolor;      /* Template color */
+  const char            *template;      /* Pointer into template */
+  const unsigned char   *color;         /* Current color */
+  static const unsigned char colors[][3] =
+  {                                     /* Colors for test */
+    { 191, 191, 191 },
+    { 127, 127, 127 },
+    {  63,  63,  63 },
+    {   0,   0,   0 },
+    { 255,   0,   0 },
+    { 255, 127,   0 },
+    { 255, 255,   0 },
+    { 127, 255,   0 },
+    {   0, 255,   0 },
+    {   0, 255, 127 },
+    {   0, 255, 255 },
+    {   0, 127, 255 },
+    {   0,   0, 255 },
+    { 127,   0, 255 },
+    { 255,   0, 255 }
+  };
+  static const char * const templates[] =
+  {                                     /* Raster template */
+    " CCC   U   U  PPPP    SSS          TTTTT  EEEEE   SSS   TTTTT          000     1     222    333      4   55555   66    77777   888    999   ",
+    "C   C  U   U  P   P  S   S           T    E      S   S    T           0   0   11    2   2  3   3  4  4   5      6          7  8   8  9   9  ",
+    "C      U   U  P   P  S               T    E      S        T           0   0    1        2      3  4  4   5      6         7   8   8  9   9  ",
+    "C      U   U  PPPP    SSS   -----    T    EEEE    SSS     T           0 0 0    1      22    333   44444   555   6666      7    888    9999  ",
+    "C      U   U  P          S           T    E          S    T           0   0    1     2         3     4       5  6   6    7    8   8      9  ",
+    "C   C  U   U  P      S   S           T    E      S   S    T           0   0    1    2      3   3     4   5   5  6   6    7    8   8      9  ",
+    " CCC    UUU   P       SSS            T    EEEEE   SSS     T            000    111   22222   333      4    555    666     7     888     99   ",
+    "                                                                                                                                            "
+  };
+
+
+ /*
+  * Figure out the output format...
+  */
+
+  if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) == NULL)
+  {
+    puts("No supported document formats, aborting.");
+    return (NULL);
+  }
+
+  if (*format)
+  {
+    if (!ippContainsString(attr, *format))
+    {
+      printf("Printer does not support document-format '%s'.\n", *format);
+      return (NULL);
+    }
+
+    if (!strcmp(*format, "image/urf"))
+      mode = CUPS_RASTER_WRITE_APPLE;
+    else if (!strcmp(*format, "image/pwg-raster"))
+      mode = CUPS_RASTER_WRITE_PWG;
+    else
+    {
+      printf("Unable to generate document-format '%s'.\n", *format);
+      return (NULL);
+    }
+  }
+  else if (ippContainsString(attr, "image/urf"))
+  {
+   /*
+    * Apple Raster format...
+    */
+
+    *format = "image/urf";
+    mode    = CUPS_RASTER_WRITE_APPLE;
+  }
+  else if (ippContainsString(attr, "image/pwg-raster"))
+  {
+   /*
+    * PWG Raster format...
+    */
+
+    *format = "image/pwg-raster";
+    mode    = CUPS_RASTER_WRITE_PWG;
+  }
+  else
+  {
+   /*
+    * No supported raster format...
+    */
+
+    puts("Printer does not support Apple or PWG raster files, aborting.");
+    return (NULL);
+  }
+
+ /*
+  * Figure out the the media, resolution, and color mode...
+  */
+
+  if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL)
+  {
+   /*
+    * Use default media...
+    */
+
+    media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
+  }
+  else if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL)
+  {
+   /*
+    * Use ready media...
+    */
+
+    if (ippContainsString(attr, "na_letter_8.5x11in"))
+      media = pwgMediaForPWG("na_letter_8.5x11in");
+    else if (ippContainsString(attr, "iso_a4_210x297mm"))
+      media = pwgMediaForPWG("iso_a4_210x297mm");
+    else
+      media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
+  }
+  else
+  {
+    puts("No default or ready media reported by printer, aborting.");
+    return (NULL);
+  }
+
+  if (mode == CUPS_RASTER_WRITE_APPLE && (attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
+  {
+    for (i = 0, count = ippGetCount(attr); i < count; i ++)
+    {
+      const char *val = ippGetString(attr, i, NULL);
+
+      if (!strncmp(val, "RS", 2))
+        xdpi = ydpi = atoi(val + 2);
+      else if (!strncmp(val, "W8", 2) && !type)
+        type = "sgray_8";
+      else if (!strncmp(val, "SRGB24", 6) && !grayscale)
+        type = "srgb_8";
+    }
+  }
+  else if (mode == CUPS_RASTER_WRITE_PWG && (attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+  {
+    for (i = 0, count = ippGetCount(attr); i < count; i ++)
+    {
+      int tempxdpi, tempydpi;
+      ipp_res_t tempunits;
+
+      tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits);
+
+      if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi)
+      {
+        xdpi = tempxdpi;
+        ydpi = tempydpi;
+      }
+    }
+
+    if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL)
+    {
+      if (!grayscale && ippContainsString(attr, "srgb_8"))
+        type = "srgb_8";
+      else if (ippContainsString(attr, "sgray_8"))
+        type = "sgray_8";
+    }
+  }
+
+  if (xdpi < 72 || ydpi < 72)
+  {
+    puts("No supported raster resolutions, aborting.");
+    return (NULL);
+  }
+
+  if (!type)
+  {
+    puts("No supported color spaces or bit depths, aborting.");
+    return (NULL);
+  }
+
+ /*
+  * Make the raster context and details...
+  */
+
+  if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL))
+  {
+    printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
+    return (NULL);
+  }
+
+  header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1;
+
+  if (header.cupsWidth > (4 * header.HWResolution[0]))
+  {
+    xoff = header.HWResolution[0] / 2;
+    yoff = header.HWResolution[1] / 2;
+  }
+  else
+  {
+    xoff = 0;
+    yoff = 0;
+  }
+
+  xrep = (header.cupsWidth - 2 * xoff) / 140;
+  yrep = xrep * header.HWResolution[1] / header.HWResolution[0];
+  yend = header.cupsHeight - yoff;
+
+ /*
+  * Prepare the raster file...
+  */
+
+  if ((line = malloc(header.cupsBytesPerLine)) == NULL)
+  {
+    printf("Unable to allocate %u bytes for raster output: %s\n", header.cupsBytesPerLine, strerror(errno));
+    return (NULL);
+  }
+
+  if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0)
+  {
+    printf("Unable to create temporary print file: %s\n", strerror(errno));
+    return (NULL);
+  }
+
+  if ((ras = cupsRasterOpen(fd, mode)) == NULL)
+  {
+    printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
+    close(fd);
+    return (NULL);
+  }
+
+ /*
+  * Write a single page consisting of the template dots repeated over the page.
+  */
+
+  cupsRasterWriteHeader2(ras, &header);
+
+  memset(line, 0xff, header.cupsBytesPerLine);
+
+  for (y = 0; y < yoff; y ++)
+    cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+
+  for (temprow = 0, tempcolor = 0; y < yend;)
+  {
+    template = templates[temprow];
+    color    = colors[tempcolor];
+
+    temprow ++;
+    if (temprow >= (int)(sizeof(templates) / sizeof(templates[0])))
+    {
+      temprow = 0;
+      tempcolor ++;
+      if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0])))
+        tempcolor = 0;
+      else if (tempcolor > 3 && header.cupsColorSpace == CUPS_CSPACE_SW)
+        tempcolor = 0;
+    }
+
+    memset(line, 0xff, header.cupsBytesPerLine);
+
+    if (header.cupsColorSpace == CUPS_CSPACE_SW)
+    {
+     /*
+      * Do grayscale output...
+      */
+
+      for (lineptr = line + xoff; *template; template ++)
+      {
+        if (*template != ' ')
+        {
+          for (xcount = xrep; xcount > 0; xcount --)
+            *lineptr++ = *color;
+        }
+        else
+        {
+          lineptr += xrep;
+        }
+      }
+    }
+    else
+    {
+     /*
+      * Do color output...
+      */
+
+      for (lineptr = line + 3 * xoff; *template; template ++)
+      {
+        if (*template != ' ')
+        {
+          for (xcount = xrep; xcount > 0; xcount --, lineptr += 3)
+            memcpy(lineptr, color, 3);
+        }
+        else
+        {
+          lineptr += 3 * xrep;
+        }
+      }
+    }
+
+    for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++)
+      cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+  }
+
+  memset(line, 0xff, header.cupsBytesPerLine);
+
+  for (y = 0; y < header.cupsHeight; y ++)
+    cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+
+  cupsRasterClose(ras);
+
+  close(fd);
+
+  printf("PRINT FILE: %s\n", tempname);
+
+  return (tempname);
+}
+
+
+/*
+ * 'monitor_printer()' - Monitor the job and printer states.
+ */
+
+static void *				/* O - Thread exit code */
+monitor_printer(
+    _client_monitor_t *monitor)		/* I - Monitoring data */
+{
+  http_t	*http;			/* Connection to printer */
+  ipp_t		*request,		/* IPP request */
+		*response;		/* IPP response */
+  ipp_attribute_t *attr;		/* Attribute in response */
+  ipp_pstate_t	printer_state;		/* Printer state */
+  char          printer_state_reasons[1024];
+                                        /* Printer state reasons */
+  ipp_jstate_t	job_state;		/* Job state */
+  char          job_state_reasons[1024];/* Printer state reasons */
+  static const char * const jattrs[] =  /* Job attributes we want */
+  {
+    "job-state",
+    "job-state-reasons"
+  };
+  static const char * const pattrs[] =  /* Printer attributes we want */
+  {
+    "printer-state",
+    "printer-state-reasons"
+  };
+
+
+ /*
+  * Open a connection to the printer...
+  */
+
+  http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, monitor->encryption, 1, 0, NULL);
+
+ /*
+  * Loop until the job is canceled, aborted, or completed.
+  */
+
+  printer_state            = (ipp_pstate_t)0;
+  printer_state_reasons[0] = '\0';
+
+  job_state            = (ipp_jstate_t)0;
+  job_state_reasons[0] = '\0';
+
+  while (monitor->job_state < IPP_JSTATE_CANCELED)
+  {
+   /*
+    * Reconnect to the printer as needed...
+    */
+
+    if (httpGetFd(http) < 0)
+      httpReconnect2(http, 30000, NULL);
+
+    if (httpGetFd(http) >= 0)
+    {
+     /*
+      * Connected, so check on the printer state...
+      */
+
+      request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+      response = cupsDoRequest(http, request, monitor->resource);
+
+      if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+        printer_state = (ipp_pstate_t)ippGetInteger(attr, 0);
+
+      if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
+        ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons));
+
+      if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons))
+      {
+        printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons);
+
+        monitor->printer_state = printer_state;
+        strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons));
+      }
+
+      ippDelete(response);
+
+      if (monitor->job_id > 0)
+      {
+       /*
+        * Check the status of the job itself...
+        */
+
+        request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
+        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
+        ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id);
+        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
+
+        response = cupsDoRequest(http, request, monitor->resource);
+
+        if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
+          job_state = (ipp_jstate_t)ippGetInteger(attr, 0);
+
+        if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
+          ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons));
+
+        if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons))
+        {
+          printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons);
+
+          monitor->job_state = job_state;
+          strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons));
+        }
+
+        ippDelete(response);
+      }
+    }
+
+    if (monitor->job_state < IPP_JSTATE_CANCELED)
+    {
+     /*
+      * Sleep for 5 seconds...
+      */
+
+      sleep(5);
+    }
+  }
+
+ /*
+  * Cleanup and return...
+  */
+
+  httpClose(http);
+
+  return (NULL);
+}
+
+
+/*
+ * 'show_attributes()' - Show attributes in a request or response.
+ */
+
+static void
+show_attributes(const char *title,      /* I - Title */
+                int        request,     /* I - 1 for request, 0 for response */
+                ipp_t      *ipp)        /* I - IPP request/response */
+{
+  int                   minor, major = ippGetVersion(ipp, &minor);
+                                        /* IPP version number */
+  ipp_tag_t             group = IPP_TAG_ZERO;
+                                        /* Current group tag */
+  ipp_attribute_t       *attr;          /* Current attribute */
+  const char            *name;          /* Attribute name */
+  char                  buffer[1024];   /* Value */
+
+
+  printf("%s:\n", title);
+  printf("  version=%d.%d\n", major, minor);
+  printf("  request-id=%d\n", ippGetRequestId(ipp));
+  if (!request)
+    printf("  status-code=%s\n", ippErrorString(ippGetStatusCode(ipp)));
+
+  for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp))
+  {
+    if (group != ippGetGroupTag(attr))
+    {
+      group = ippGetGroupTag(attr);
+      if (group)
+        printf("  %s:\n", ippTagString(group));
+    }
+
+    if ((name = ippGetName(attr)) != NULL)
+    {
+      ippAttributeString(attr, buffer, sizeof(buffer));
+      printf("    %s(%s%s)=%s\n", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), buffer);
+    }
+  }
+}
+
+
+/*
+ * 'show_capabilities()' - Show printer capabilities.
+ */
+
+static void
+show_capabilities(ipp_t *response)      /* I - Printer attributes */
+{
+  int                   i;              /* Looping var */
+  ipp_attribute_t       *attr;          /* Attribute */
+  char                  buffer[1024];   /* Attribute value buffer */
+  static const char * const pattrs[] =  /* Attributes we want to show */
+  {
+    "copies-default",
+    "copies-supported",
+    "finishings-default",
+    "finishings-ready",
+    "finishings-supported",
+    "media-default",
+    "media-ready",
+    "media-supported",
+    "output-bin-default",
+    "output-bin-supported",
+    "print-color-mode-default",
+    "print-color-mode-supported",
+    "sides-default",
+    "sides-supported",
+    "document-format-default",
+    "document-format-supported",
+    "pwg-raster-document-resolution-supported",
+    "pwg-raster-document-type-supported",
+    "urf-supported"
+  };
+
+
+  puts("CAPABILITIES:");
+  for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++)
+  {
+     if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL)
+     {
+       ippAttributeString(attr, buffer, sizeof(buffer));
+       printf("  %s=%s\n", pattrs[i], buffer);
+     }
+  }
+}
+
+
+/*
+ * 'usage()' - Show program usage...
+ */
+
+static void
+usage(void)
+{
+  puts("Usage: ./testclient printer-uri [options]");
+  puts("Options:");
+  puts("  -d document-format  Generate the specified format");
+  puts("  -f print-file       Print the named file");
+  puts("  -g                  Force grayscale printing");
+  puts("  -k                  Keep temporary files");
+  puts("  -v                  Be more verbose");
+}
diff --git a/libcups_version b/libcups_version
index 0b1f88b..437a354 100644
--- a/libcups_version
+++ b/libcups_version
@@ -1 +1 @@
-v2.2.3
+v2.2.6