summaryrefslogtreecommitdiff
path: root/cups
diff options
context:
space:
mode:
authorChris Liddell <chris.liddell@artifex.com>2013-07-23 16:24:19 +0100
committerChris Liddell <chris.liddell@artifex.com>2015-07-20 18:21:17 +0100
commit6948650efd3fb9e2a70b8cf16aca57e9d0b7eb0a (patch)
tree5c2a1c671c1d4521f8a770d1e69e3d4342718030 /cups
parent7fd9e0be26e67c36f87733bc89ea07dc26d9f839 (diff)
downloadghostpdl-6948650efd3fb9e2a70b8cf16aca57e9d0b7eb0a.tar.gz
Commit of build_consolidation branch
Squashed into one commit (see branch for details of the evolution of the branch). This brings gpcl6 and gxps into the Ghostscript build system, and a shared set of graphics library object files for all the interpreters. Also, brings the same configuration options to the pcl and xps products as we have for Ghostscript.
Diffstat (limited to 'cups')
-rw-r--r--cups/cups.mak38
-rw-r--r--cups/gdevcups.c5773
-rw-r--r--cups/libs/Makedefs293
-rw-r--r--cups/libs/configlinux.h710
-rw-r--r--cups/libs/configwin.h754
-rw-r--r--cups/libs/cups/Dependencies260
-rw-r--r--cups/libs/cups/Makefile643
-rw-r--r--cups/libs/cups/adminutil.c2341
-rw-r--r--cups/libs/cups/adminutil.h81
-rw-r--r--cups/libs/cups/api-array.header34
-rw-r--r--cups/libs/cups/api-array.shtml196
-rw-r--r--cups/libs/cups/api-cups.header40
-rw-r--r--cups/libs/cups/api-cups.shtml443
-rw-r--r--cups/libs/cups/api-filedir.header36
-rw-r--r--cups/libs/cups/api-filedir.shtml31
-rw-r--r--cups/libs/cups/api-filter.header41
-rw-r--r--cups/libs/cups/api-filter.shtml854
-rw-r--r--cups/libs/cups/api-httpipp.header37
-rw-r--r--cups/libs/cups/api-httpipp.shtml317
-rw-r--r--cups/libs/cups/api-overview.header53
-rw-r--r--cups/libs/cups/api-overview.shtml94
-rw-r--r--cups/libs/cups/api-ppd.header38
-rw-r--r--cups/libs/cups/api-ppd.shtml219
-rw-r--r--cups/libs/cups/array-private.h52
-rw-r--r--cups/libs/cups/array.c1366
-rw-r--r--cups/libs/cups/array.h92
-rw-r--r--cups/libs/cups/attr.c335
-rw-r--r--cups/libs/cups/auth.c892
-rw-r--r--cups/libs/cups/backchannel.c199
-rw-r--r--cups/libs/cups/backend.c154
-rw-r--r--cups/libs/cups/backend.h78
-rw-r--r--cups/libs/cups/conflicts.c1214
-rw-r--r--cups/libs/cups/cups-private.h285
-rw-r--r--cups/libs/cups/cups.h633
-rw-r--r--cups/libs/cups/custom.c122
-rw-r--r--cups/libs/cups/debug-private.h117
-rw-r--r--cups/libs/cups/debug.c665
-rw-r--r--cups/libs/cups/dest-job.c366
-rw-r--r--cups/libs/cups/dest-localization.c387
-rw-r--r--cups/libs/cups/dest-options.c2311
-rw-r--r--cups/libs/cups/dest.c3924
-rw-r--r--cups/libs/cups/dir.c472
-rw-r--r--cups/libs/cups/dir.h69
-rw-r--r--cups/libs/cups/emit.c1229
-rw-r--r--cups/libs/cups/encode.c851
-rw-r--r--cups/libs/cups/file-private.h139
-rw-r--r--cups/libs/cups/file.c2676
-rw-r--r--cups/libs/cups/file.h118
-rw-r--r--cups/libs/cups/getdevices.c284
-rw-r--r--cups/libs/cups/getifaddrs.c266
-rw-r--r--cups/libs/cups/getputfile.c522
-rw-r--r--cups/libs/cups/globals.c398
-rw-r--r--cups/libs/cups/http-addr.c760
-rw-r--r--cups/libs/cups/http-addrlist.c881
-rw-r--r--cups/libs/cups/http-private.h441
-rw-r--r--cups/libs/cups/http-support.c2386
-rw-r--r--cups/libs/cups/http.c5910
-rw-r--r--cups/libs/cups/http.h627
-rw-r--r--cups/libs/cups/ipp-private.h81
-rw-r--r--cups/libs/cups/ipp-support.c2258
-rw-r--r--cups/libs/cups/ipp.c7097
-rw-r--r--cups/libs/cups/ipp.h998
-rw-r--r--cups/libs/cups/langprintf.c352
-rw-r--r--cups/libs/cups/language-private.h86
-rw-r--r--cups/libs/cups/language.c1596
-rw-r--r--cups/libs/cups/language.h115
-rw-r--r--cups/libs/cups/libcups2.def411
-rw-r--r--cups/libs/cups/libcups2.rc75
-rw-r--r--cups/libs/cups/libcups_s.exp85
-rw-r--r--cups/libs/cups/localize.c779
-rw-r--r--cups/libs/cups/mark.c1101
-rw-r--r--cups/libs/cups/md5-private.h79
-rw-r--r--cups/libs/cups/md5.c346
-rw-r--r--cups/libs/cups/md5passwd.c142
-rw-r--r--cups/libs/cups/notify.c202
-rw-r--r--cups/libs/cups/options.c711
-rw-r--r--cups/libs/cups/page.c396
-rw-r--r--cups/libs/cups/ppd-cache.c2732
-rw-r--r--cups/libs/cups/ppd-private.h225
-rw-r--r--cups/libs/cups/ppd.c3401
-rw-r--r--cups/libs/cups/ppd.h478
-rw-r--r--cups/libs/cups/pwg-media.c1189
-rw-r--r--cups/libs/cups/pwg-private.h78
-rw-r--r--cups/libs/cups/pwg.h94
-rw-r--r--cups/libs/cups/raster-private.h66
-rw-r--r--cups/libs/cups/raster.h405
-rw-r--r--cups/libs/cups/request.c1209
-rw-r--r--cups/libs/cups/sidechannel.c642
-rw-r--r--cups/libs/cups/sidechannel.h147
-rw-r--r--cups/libs/cups/snmp-private.h146
-rw-r--r--cups/libs/cups/snmp.c1733
-rw-r--r--cups/libs/cups/snprintf.c366
-rw-r--r--cups/libs/cups/sspi-private.h82
-rw-r--r--cups/libs/cups/sspi.c1468
-rw-r--r--cups/libs/cups/string-private.h222
-rw-r--r--cups/libs/cups/string.c793
-rw-r--r--cups/libs/cups/tempfile.c233
-rw-r--r--cups/libs/cups/test.ppd262
-rw-r--r--cups/libs/cups/test2.ppd252
-rw-r--r--cups/libs/cups/testadmin.c121
-rw-r--r--cups/libs/cups/testarray.c562
-rw-r--r--cups/libs/cups/testconflicts.c138
-rw-r--r--cups/libs/cups/testcups.c593
-rw-r--r--cups/libs/cups/testfile.c821
-rw-r--r--cups/libs/cups/testhttp.c835
-rw-r--r--cups/libs/cups/testi18n.c619
-rw-r--r--cups/libs/cups/testipp.c1016
-rw-r--r--cups/libs/cups/testlang.c114
-rw-r--r--cups/libs/cups/testoptions.c116
-rw-r--r--cups/libs/cups/testppd.c1113
-rw-r--r--cups/libs/cups/testpwg.c570
-rw-r--r--cups/libs/cups/testsnmp.c304
-rw-r--r--cups/libs/cups/thread-private.h100
-rw-r--r--cups/libs/cups/thread.c338
-rw-r--r--cups/libs/cups/transcode.c720
-rw-r--r--cups/libs/cups/transcode.h81
-rw-r--r--cups/libs/cups/usersys.c1149
-rw-r--r--cups/libs/cups/utf8demo.txt213
-rw-r--r--cups/libs/cups/util.c1854
-rw-r--r--cups/libs/cups/versioning.h161
-rw-r--r--cups/libs/filter/Dependencies61
-rw-r--r--cups/libs/filter/Makefile402
-rw-r--r--cups/libs/filter/api-raster.header37
-rw-r--r--cups/libs/filter/api-raster.shtml160
-rw-r--r--cups/libs/filter/commandtops.c538
-rw-r--r--cups/libs/filter/common.c535
-rw-r--r--cups/libs/filter/common.h78
-rw-r--r--cups/libs/filter/error.c286
-rw-r--r--cups/libs/filter/gziptoany.c112
-rw-r--r--cups/libs/filter/interpret.c1690
-rw-r--r--cups/libs/filter/libcupsimage2.def14
-rw-r--r--cups/libs/filter/libcupsimage_s.exp16
-rw-r--r--cups/libs/filter/postscript-driver.header32
-rw-r--r--cups/libs/filter/postscript-driver.shtml276
-rw-r--r--cups/libs/filter/ppd-compiler.header40
-rw-r--r--cups/libs/filter/ppd-compiler.shtml883
-rw-r--r--cups/libs/filter/pstops.c3434
-rw-r--r--cups/libs/filter/raster-driver.header32
-rw-r--r--cups/libs/filter/raster-driver.shtml194
-rw-r--r--cups/libs/filter/raster.c1479
-rw-r--r--cups/libs/filter/rasterbench.c355
-rw-r--r--cups/libs/filter/rastertoepson.c1157
-rw-r--r--cups/libs/filter/rastertohp.c886
-rw-r--r--cups/libs/filter/rastertolabel.c1306
-rw-r--r--cups/libs/filter/rastertopwg.c461
-rw-r--r--cups/libs/filter/spec-ppd.header32
-rw-r--r--cups/libs/filter/spec-ppd.shtml2026
-rw-r--r--cups/libs/filter/testraster.c1078
148 files changed, 106818 insertions, 0 deletions
diff --git a/cups/cups.mak b/cups/cups.mak
new file mode 100644
index 000000000..efe51fe8b
--- /dev/null
+++ b/cups/cups.mak
@@ -0,0 +1,38 @@
+#
+#
+# CUPS driver makefile for Ghostscript.
+#
+# Copyright 2001-2005 by Easy Software Products.
+# Copyright 2007 Artifex Software, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# define the name of this makefile
+CUPS_MAK=$(LCUPSSRCDIR)$(D)cups.mak
+
+### ----------------- CUPS Ghostscript Driver ---------------------- ###
+
+cups_= $(GLOBJ)gdevcups.$(OBJ)
+
+# These are set in the toplevel Makefile via autoconf(1)
+# CUPSCFLAGS=`cups-config --cflags`
+# CUPSSERVERBIN=`cups-config --serverbin`
+# CUPSSERVERROOT=`cups-config --serverroot`
+# CUPSDATA=`cups-config --datadir`
+# CUPSPDFTORASTER= 1 if CUPS is new enough (cups-config --version)
+# CUPSDIR
+
+install:
diff --git a/cups/gdevcups.c b/cups/gdevcups.c
new file mode 100644
index 000000000..ccd3bcb42
--- /dev/null
+++ b/cups/gdevcups.c
@@ -0,0 +1,5773 @@
+/*
+ *
+ * GNU Ghostscript raster output driver for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1993-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products 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 please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org/
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * Contents:
+ *
+ * cups_close() - Close the output file.
+ * cups_decode_color() - Decode a color value.
+ * cups_encode_color() - Encode a color value.
+ * cups_get_color_comp_index()
+ * - Color component to index
+ * cups_get_color_mapping_procs()
+ * - Get the list of color mapping procedures.
+ * cups_get_matrix() - Generate the default page matrix.
+ * cups_get_params() - Get pagedevice parameters.
+ * cups_get_space_params() - Get space parameters from the RIP_CACHE env var.
+ * cups_map_cielab() - Map CIE Lab transformation...
+ * cups_map_cmyk() - Map a CMYK color value to device colors.
+ * cups_map_gray() - Map a grayscale value to device colors.
+ * cups_map_rgb() - Map a RGB color value to device colors.
+ * cups_map_cmyk_color() - Map a CMYK color to a color index.
+ * cups_map_color_rgb() - Map a color index to an RGB color.
+ * cups_map_rgb_color() - Map an RGB color to a color index. We map the
+ * RGB color to the output colorspace & bits (we
+ * figure out the format when we output a page).
+ * cups_open() - Open the output file and initialize things.
+ * cups_print_pages() - Send one or more pages to the output file.
+ * cups_put_params() - Set pagedevice parameters.
+ * cups_set_color_info() - Set the color information structure based on
+ * the required output.
+ * cups_sync_output() - Keep the user informed of our status...
+ * cups_print_chunked() - Print a page of chunked pixels.
+ * cups_print_banded() - Print a page of banded pixels.
+ * cups_print_planar() - Print a page of planar pixels.
+ */
+
+/* prevent gp.h redefining fopen */
+#define sprintf sprintf
+
+/*
+ * Include necessary headers...
+ */
+
+#include "std.h" /* to stop stdlib.h redefining types */
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "arch.h"
+#include "gsicc_manage.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/raster.h>
+#include <cups/ppd.h>
+#include <math.h>
+
+/* the extremely noisy DEBUG2 messages are now dependent on CUPS_DEBUG2 */
+/* this can be enabled during the 'make' or by uncommenting the following */
+/* #define CUPS_DEBUG2 */
+
+#undef private
+#define private
+
+#ifdef WIN32
+#define cbrt(arg) pow(arg, 1.0/3)
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#endif
+
+/* This should go into gdevprn.h, or, better yet, gdevprn should
+ acquire an API for changing resolution. */
+int gdev_prn_maybe_realloc_memory(gx_device_printer *pdev,
+ gdev_prn_space_params *old_space,
+ int old_width, int old_height,
+ bool old_page_uses_transparency);
+
+/*
+ * Check if we are compiling against CUPS 1.2. If so, enable
+ * certain extended attributes and use a different page header
+ * structure and write function...
+ */
+
+#ifdef CUPS_RASTER_SYNCv1
+# define cups_page_header_t cups_page_header2_t
+# define cupsRasterWriteHeader cupsRasterWriteHeader2
+#else
+/* The RGBW, SW, SRGB, and ADOBERGB colorspaces is not defined until
+ CUPS 1.2... */
+# define CUPS_CSPACE_RGBW 17
+# define CUPS_CSPACE_SW 18
+# define CUPS_CSPACE_SRGB 19
+# define CUPS_CSPACE_ADOBERGB 20
+#endif /* CUPS_RASTER_SYNCv1 */
+
+#if !defined(CUPS_RASTER_WRITE_PWG)
+ #define CUPS_RASTER_WRITE_PWG 3
+#endif
+
+/*
+ * CIE XYZ color constants...
+ */
+
+#define D65_X (0.412453 + 0.357580 + 0.180423)
+#define D65_Y (0.212671 + 0.715160 + 0.072169)
+#define D65_Z (0.019334 + 0.119193 + 0.950227)
+
+
+/*
+ * Size of a tile in pixels...
+ */
+
+#define CUPS_TILE_SIZE 256
+
+
+/*
+ * Size of profile LUTs...
+ */
+
+#ifdef dev_t_proc_encode_color
+# define CUPS_MAX_VALUE frac_1
+#else
+# define CUPS_MAX_VALUE gx_max_color_value
+#endif /* dev_t_proc_encode_color */
+
+
+/*
+ * Macros...
+ */
+
+#define x_dpi (pdev->HWResolution[0])
+#define y_dpi (pdev->HWResolution[1])
+#define cups ((gx_device_cups *)pdev)
+
+/*
+ * Macros from <macros.h>; we can't include <macros.h> because it also
+ * defines DEBUG, one of our flags to insert various debugging code.
+ */
+
+#ifndef max
+# define max(a,b) ((a)<(b) ? (b) : (a))
+#endif /* !max */
+
+#ifndef min
+# define min(a,b) ((a)>(b) ? (b) : (a))
+#endif /* !min */
+
+#ifndef abs
+# define abs(x) ((x)>=0 ? (x) : -(x))
+#endif /* !abs */
+
+
+/*
+ * Procedures
+ */
+
+private dev_proc_close_device(cups_close);
+private dev_proc_get_initial_matrix(cups_get_matrix);
+private int cups_get_params(gx_device *, gs_param_list *);
+private dev_proc_open_device(cups_open);
+private dev_proc_output_page(cups_output_page);
+private int cups_print_pages(gx_device_printer *, FILE *, int);
+private int cups_put_params(gx_device *, gs_param_list *);
+private int cups_set_color_info(gx_device *);
+private dev_proc_sync_output(cups_sync_output);
+private prn_dev_proc_get_space_params(cups_get_space_params);
+
+#ifdef dev_t_proc_encode_color
+private cm_map_proc_gray(cups_map_gray);
+private cm_map_proc_rgb(cups_map_rgb);
+private cm_map_proc_cmyk(cups_map_cmyk);
+private dev_proc_decode_color(cups_decode_color);
+private dev_proc_encode_color(cups_encode_color);
+private dev_proc_get_color_comp_index(cups_get_color_comp_index);
+private dev_proc_get_color_mapping_procs(cups_get_color_mapping_procs);
+
+static const gx_cm_color_map_procs cups_color_mapping_procs =
+{
+ cups_map_gray,
+ cups_map_rgb,
+ cups_map_cmyk
+};
+#else
+private dev_proc_map_cmyk_color(cups_map_cmyk_color);
+private dev_proc_map_color_rgb(cups_map_color_rgb);
+private dev_proc_map_rgb_color(cups_map_rgb_color);
+#endif /* dev_t_proc_encode_color */
+
+
+/*
+ * The device descriptors...
+ */
+
+typedef struct gx_device_cups_s
+{
+ gx_device_common; /* Standard GhostScript device stuff */
+ gx_prn_device_common; /* Standard printer device stuff */
+ int page; /* Page number */
+ cups_raster_t *stream; /* Raster stream */
+ cups_page_header_t header; /* PostScript page device info */
+ int landscape; /* Non-zero if this is landscape */
+ int lastpage;
+ int HaveProfile; /* Has a color profile been defined? */
+ char *Profile; /* Current simple color profile string */
+ ppd_file_t *PPD; /* PPD file for this device */
+ unsigned char RevLower1[16]; /* Lower 1-bit reversal table */
+ unsigned char RevUpper1[16]; /* Upper 1-bit reversal table */
+ unsigned char RevLower2[16]; /* Lower 2-bit reversal table */
+ unsigned char RevUpper2[16]; /* Upper 2-bit reversal table */
+#ifdef GX_COLOR_INDEX_TYPE
+ gx_color_value DecodeLUT[65536];/* Output color to RGB value LUT */
+#else
+ gx_color_value DecodeLUT[256]; /* Output color to RGB value LUT */
+#endif /* GX_COLOR_INDEX_TYPE */
+ unsigned short EncodeLUT[gx_max_color_value + 1];/* RGB value to output color LUT */
+ int Density[CUPS_MAX_VALUE + 1];/* Density LUT */
+ int Matrix[3][3][CUPS_MAX_VALUE + 1];/* Color transform matrix LUT */
+ int user_icc;
+ int cupsRasterVersion;
+
+ /* Used by cups_put_params(): */
+} gx_device_cups;
+
+private gx_device_procs cups_procs =
+{
+ cups_open,
+ cups_get_matrix,
+ cups_sync_output,
+ cups_output_page,
+ cups_close,
+#ifdef dev_t_proc_encode_color
+ NULL, /* map_rgb_color */
+ NULL, /* map_color_rgb */
+#else
+ cups_map_rgb_color,
+ cups_map_color_rgb,
+#endif /* dev_t_proc_encode_color */
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ gx_default_get_bits,
+ cups_get_params,
+ cups_put_params,
+#ifdef dev_t_proc_encode_color
+ NULL, /* map_cmyk_color */
+#else
+ cups_map_cmyk_color,
+#endif /* dev_t_proc_encode_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ NULL, /* get_alpha_bits */
+ NULL, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ NULL, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+#ifdef dev_t_proc_encode_color
+ cups_get_color_mapping_procs,
+ cups_get_color_comp_index,
+ cups_encode_color,
+ cups_decode_color,
+#else
+ NULL, /* get_color_mapping_procs */
+ NULL, /* get_color_comp_index */
+ NULL, /* encode_color */
+ NULL, /* decode_color */
+#endif /* dev_t_proc_encode_color */
+ NULL, /* pattern_manage */
+ NULL, /* fill_rectangle_hl_color */
+ NULL, /* include_color_space */
+ NULL, /* fill_linear_color_scanline */
+ NULL, /* fill_linear_color_trapezoid */
+ NULL, /* fill_linear_color_triangle */
+ NULL, /* update_spot_equivalent_colors */
+ NULL, /* ret_devn_params */
+ NULL, /* fillpage */
+ NULL, /* push_transparency_state */
+ NULL, /* pop_transparency_state */
+ NULL, /* put_image */
+
+};
+
+#define prn_device_body_copies(dtype, procs, dname, w10, h10, xdpi, ydpi, lo, to, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_pages)\
+ std_device_full_body_type(dtype, &procs, dname, &st_device_printer,\
+ (int)((long)(w10) * (xdpi) / 10),\
+ (int)((long)(h10) * (ydpi) / 10),\
+ xdpi, ydpi,\
+ ncomp, depth, mg, mc, dg, dc,\
+ -(lo) * (xdpi), -(to) * (ydpi),\
+ (lm) * 72.0, (bm) * 72.0,\
+ (rm) * 72.0, (tm) * 72.0\
+ ),\
+ prn_device_body_copies_rest_(print_pages)
+
+
+
+#ifdef CUPS_RASTER_SYNCv1
+#define RASTER_SYNCv1_ENTRIES \
+ ,\
+ 1, /* cupsNumColors */\
+ 1.0, /* cupsBorderlessScalingFactor */\
+ { 612.0, 792.0 }, /* cupsPageSize */\
+ { 0.0, 0.0, 612.0, 792.0 }, /* cupsImagingBBox */\
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* cupsInteger */\
+ { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, /* cupsReal */\
+ { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },\
+ /* cupsString */\
+ "", /* cupsMarkerType */\
+ "", /* cupsRenderingIntent */\
+ "" /* cupsPageSizeName */
+#else
+#define RASTER_SYNCv1_ENTRIES
+#endif /* CUPS_RASTER_SYNCv1 */
+
+#define gs_xxx_device(dname, mediaclass)\
+ prn_device_body_copies(gx_device_cups,/* type */\
+ cups_procs, /* procedures */\
+ dname, /* device name */\
+ 85, /* initial width */\
+ 110, /* initial height */\
+ 100, /* initial x resolution */\
+ 100, /* initial y resolution */\
+ 0, /* initial left offset */\
+ 0, /* initial top offset */\
+ 0, /* initial left margin */\
+ 0, /* initial bottom margin */\
+ 0, /* initial right margin */\
+ 0, /* initial top margin */\
+ 1, /* number of color components */\
+ 1, /* number of color bits */\
+ 1, /* maximum gray value */\
+ 0, /* maximum color value */\
+ 2, /* number of gray values */\
+ 0, /* number of color values */\
+ cups_print_pages),\
+ /* print procedure */\
+ 0, /* page */\
+ NULL, /* stream */\
+ { /* header */\
+ mediaclass, /* MediaClass */\
+ "", /* MediaColor */\
+ "", /* MediaType */\
+ "", /* OutputType */\
+ 0, /* AdvanceDistance */\
+ CUPS_ADVANCE_NONE, /* AdvanceMedia */\
+ CUPS_FALSE, /* Collate */\
+ CUPS_CUT_NONE, /* CutMedia */\
+ CUPS_FALSE, /* Duplex */\
+ { 100, 100 }, /* HWResolution */\
+ { 0, 0, 612, 792 }, /* ImagingBoundingBox */\
+ CUPS_FALSE, /* InsertSheet */\
+ CUPS_JOG_NONE, /* Jog */\
+ CUPS_EDGE_TOP, /* LeadingEdge */\
+ { 0, 0 }, /* Margins */\
+ CUPS_FALSE, /* ManualFeed */\
+ 0, /* MediaPosition */\
+ 0, /* MediaWeight */\
+ CUPS_FALSE, /* MirrorPrint */\
+ CUPS_FALSE, /* NegativePrint */\
+ 1, /* NumCopies */\
+ CUPS_ORIENT_0, /* Orientation */\
+ CUPS_FALSE, /* OutputFaceUp */\
+ { 612, 792 }, /* PageSize */\
+ CUPS_FALSE, /* Separations */\
+ CUPS_FALSE, /* TraySwitch */\
+ CUPS_FALSE, /* Tumble */\
+ 850, /* cupsWidth */\
+ 1100, /* cupsHeight */\
+ 0, /* cupsMediaType */\
+ 1, /* cupsBitsPerColor */\
+ 1, /* cupsBitsPerPixel */\
+ 107, /* cupsBytesPerLine */\
+ CUPS_ORDER_CHUNKED, /* cupsColorOrder */\
+ CUPS_CSPACE_K, /* cupsColorSpace */\
+ 0, /* cupsCompression */\
+ 0, /* cupsRowCount */\
+ 0, /* cupsRowFeed */\
+ 0 /* cupsRowStep */\
+ RASTER_SYNCv1_ENTRIES, /* See above */\
+ },\
+ 0, /* landscape */\
+ 0, /* lastpage */\
+ 0, /* HaveProfile */\
+ NULL, /* Profile */\
+ NULL, /* PPD */\
+ { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,\
+ 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f },/* RevLower1 */\
+ { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,\
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0 },/* RevUpper1 */\
+ { 0x00, 0x04, 0x08, 0x0c, 0x01, 0x05, 0x09, 0x0d,\
+ 0x02, 0x06, 0x0a, 0x0e, 0x03, 0x07, 0x0b, 0x0f },/* RevLower2 */\
+ { 0x00, 0x40, 0x80, 0xc0, 0x10, 0x50, 0x90, 0xd0,\
+ 0x20, 0x60, 0xa0, 0xe0, 0x30, 0x70, 0xb0, 0xf0 },/* RevUpper2 */\
+ {0x00}, /* DecodeLUT */\
+ {0x00}, /* EncodeLUT */\
+ {0x00}, /* Density */\
+ {{{0x00},{0x00},{0x00}},\
+ {{0x00},{0x00},{0x00}},\
+ {{0x00},{0x00},{0x00}}}, /* Matrix */\
+ 0, /* user_icc */\
+ 3 /* cupsRasterVersion */
+
+gx_device_cups gs_cups_device = { gs_xxx_device("cups", "") };
+gx_device_cups gs_pwgraster_device = { gs_xxx_device("pwgraster",
+ "PwgRaster") };
+
+/*
+ * Local functions...
+ */
+
+static double cups_map_cielab(double, double);
+static int cups_print_chunked(gx_device_printer *, unsigned char *,
+ unsigned char *, int);
+static int cups_print_banded(gx_device_printer *, unsigned char *,
+ unsigned char *, int);
+static int cups_print_planar(gx_device_printer *, unsigned char *,
+ unsigned char *, int);
+
+/*static void cups_set_margins(gx_device *);*/
+
+
+/*
+ * 'cups_close()' - Close the output file.
+ */
+
+private int
+cups_close(gx_device *pdev) /* I - Device info */
+{
+#ifdef CUPS_DEBUG2
+ dmprintf1(pdev->memory, "DEBUG2: cups_close(%p)\n", pdev);
+#endif /* CUPS_DEBUG2 */
+
+ dmprintf(pdev->memory, "INFO: Rendering completed\n");
+
+ if (cups->stream != NULL)
+ {
+ cupsRasterClose(cups->stream);
+ cups->stream = NULL;
+ }
+
+#if 0 /* Can't do this here because put_params() might close the device */
+ if (cups->PPD != NULL)
+ {
+ ppdClose(cups->PPD);
+ cups->PPD = NULL;
+ }
+
+ if (cups->Profile != NULL)
+ {
+ free(cups->Profile);
+ cups->Profile = NULL;
+ }
+#endif /* 0 */
+
+ return (gdev_prn_close(pdev));
+}
+
+
+#ifdef dev_t_proc_encode_color
+/*
+ * 'cups_decode_color()' - Decode a color value.
+ */
+
+private int /* O - Status (0 = OK) */
+cups_decode_color(gx_device *pdev, /* I - Device info */
+ gx_color_index ci, /* I - Color index */
+ gx_color_value *cv) /* O - Colors */
+{
+ int i; /* Looping var */
+ int shift; /* Bits to shift */
+ int mask; /* Bits to mask */
+
+
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
+ cups->header.cupsBitsPerColor == 1)
+ {
+ /*
+ * KCMYcm data is represented internally by Ghostscript as CMYK...
+ */
+
+ cv[0] = (ci & 0x20) ? frac_1 : frac_0;
+ cv[1] = (ci & 0x12) ? frac_1 : frac_0;
+ cv[2] = (ci & 0x09) ? frac_1 : frac_0;
+ cv[3] = (ci & 0x04) ? frac_1 : frac_0;
+ }
+ else
+ {
+ shift = cups->header.cupsBitsPerColor;
+ mask = (1 << shift) - 1;
+
+ for (i = cups->color_info.num_components - 1; i > 0; i --, ci >>= shift)
+ cv[i] = cups->DecodeLUT[ci & mask];
+
+ cv[0] = cups->DecodeLUT[ci & mask];
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'cups_encode_color()' - Encode a color value.
+ */
+
+private gx_color_index /* O - Color index */
+cups_encode_color(gx_device *pdev,
+ /* I - Device info */
+ const gx_color_value *cv)
+ /* I - Colors */
+{
+ int i; /* Looping var */
+ gx_color_index ci; /* Color index */
+ int shift; /* Bits to shift */
+
+
+ /*
+ * Encode the color index...
+ */
+
+ shift = cups->header.cupsBitsPerColor;
+
+ for (ci = cups->EncodeLUT[cv[0]], i = 1;
+ i < cups->color_info.num_components;
+ i ++)
+ ci = (ci << shift) | cups->EncodeLUT[cv[i]];
+
+#ifdef CUPS_DEBUG2
+ dmprintf2(pdev->memory, "DEBUG2: cv[0]=%d -> %llx\n", cv[0], ci);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Handle 6-color output...
+ */
+
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
+ cups->header.cupsBitsPerColor == 1)
+ {
+ /*
+ * Welcome to hackville, where we map CMYK data to the
+ * light inks in draft mode... Map blue to light magenta and
+ * cyan and green to light cyan and yellow...
+ */
+
+ ci <<= 2; /* Leave room for light inks */
+
+ if (ci == 0x18) /* Blue */
+ ci = 0x11; /* == cyan + light magenta */
+ else if (ci == 0x14) /* Green */
+ ci = 0x06; /* == light cyan + yellow */
+ }
+
+ /*
+ * Range check the return value...
+ */
+
+ if (ci == gx_no_color_index)
+ ci --;
+
+ /*
+ * Return the color index...
+ */
+
+ return (ci);
+}
+
+/*
+ * 'cups_get_color_comp_index()' - Color component to index
+ */
+
+#define compare_color_names(pname, name_size, name_str) \
+ (name_size == (int)strlen(name_str) && strncasecmp(pname, name_str, name_size) == 0)
+
+int /* O - Index of the named color in
+ the color space */
+cups_get_color_comp_index(gx_device * pdev, const char * pname,
+ int name_size, int component_type)
+{
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_K :
+ if (compare_color_names(pname, name_size, "Black") ||
+ compare_color_names(pname, name_size, "Gray") ||
+ compare_color_names(pname, name_size, "Grey"))
+ return 0;
+ else
+ return -1; /* Indicate that the component name is "unknown" */
+ break;
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ case CUPS_CSPACE_WHITE :
+ if (compare_color_names(pname, name_size, "White") ||
+ compare_color_names(pname, name_size, "Luminance") ||
+ compare_color_names(pname, name_size, "Gray") ||
+ compare_color_names(pname, name_size, "Grey"))
+ return 0;
+ else
+ return -1;
+ break;
+ case CUPS_CSPACE_RGBA :
+ if (compare_color_names(pname, name_size, "Alpha") ||
+ compare_color_names(pname, name_size, "Transparent") ||
+ compare_color_names(pname, name_size, "Transparency"))
+ return 3;
+ case CUPS_CSPACE_RGBW :
+ if (compare_color_names(pname, name_size, "Red"))
+ return 0;
+ if (compare_color_names(pname, name_size, "Green"))
+ return 1;
+ if (compare_color_names(pname, name_size, "Blue"))
+ return 2;
+ if (compare_color_names(pname, name_size, "White"))
+ return 3;
+ else
+ return -1;
+ break;
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ if (compare_color_names(pname, name_size, "Red"))
+ return 0;
+ if (compare_color_names(pname, name_size, "Green"))
+ return 1;
+ if (compare_color_names(pname, name_size, "Blue"))
+ return 2;
+ break;
+ case CUPS_CSPACE_CMYK :
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+# endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ if (compare_color_names(pname, name_size, "Black"))
+ return 3;
+ case CUPS_CSPACE_CMY :
+ if (compare_color_names(pname, name_size, "Cyan"))
+ return 0;
+ if (compare_color_names(pname, name_size, "Magenta"))
+ return 1;
+ if (compare_color_names(pname, name_size, "Yellow"))
+ return 2;
+ else
+ return -1;
+ break;
+ case CUPS_CSPACE_GMCS :
+ if (compare_color_names(pname, name_size, "Silver") ||
+ compare_color_names(pname, name_size, "Silver Foil"))
+ return 3;
+ case CUPS_CSPACE_GMCK :
+ if (compare_color_names(pname, name_size, "Gold") ||
+ compare_color_names(pname, name_size, "Gold Foil"))
+ return 0;
+ case CUPS_CSPACE_YMCK :
+ if (compare_color_names(pname, name_size, "Black"))
+ return 3;
+ case CUPS_CSPACE_YMC :
+ if (compare_color_names(pname, name_size, "Yellow"))
+ return 0;
+ if (compare_color_names(pname, name_size, "Magenta"))
+ return 1;
+ if (compare_color_names(pname, name_size, "Cyan"))
+ return 2;
+ else
+ return -1;
+ break;
+ case CUPS_CSPACE_KCMYcm :
+ if (compare_color_names(pname, name_size, "Light Cyan") ||
+ compare_color_names(pname, name_size, "Photo Cyan"))
+ return 4;
+ if (compare_color_names(pname, name_size, "Light Magenta") ||
+ compare_color_names(pname, name_size, "Photo Magenta"))
+ return 5;
+ case CUPS_CSPACE_KCMY :
+ if (compare_color_names(pname, name_size, "Black"))
+ return 0;
+ if (compare_color_names(pname, name_size, "Cyan"))
+ return 1;
+ if (compare_color_names(pname, name_size, "Magenta"))
+ return 2;
+ if (compare_color_names(pname, name_size, "Yellow"))
+ return 3;
+ else
+ return -1;
+ break;
+ case CUPS_CSPACE_GOLD :
+ if (compare_color_names(pname, name_size, "Gold") ||
+ compare_color_names(pname, name_size, "Gold Foil"))
+ return 0;
+ else
+ return -1;
+ break;
+ case CUPS_CSPACE_SILVER :
+ if (compare_color_names(pname, name_size, "Silver") ||
+ compare_color_names(pname, name_size, "Silver Foil"))
+ return 0;
+ else
+ return -1;
+ break;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/*
+ * 'cups_get_color_mapping_procs()' - Get the list of color mapping procedures.
+ */
+
+private const gx_cm_color_map_procs * /* O - List of device procedures */
+cups_get_color_mapping_procs(const gx_device *pdev)
+ /* I - Device info */
+{
+ return (&cups_color_mapping_procs);
+}
+#endif /* dev_t_proc_encode_color */
+
+
+/*
+ * 'cups_get_matrix()' - Generate the default page matrix.
+ */
+
+private void
+cups_get_matrix(gx_device *pdev, /* I - Device info */
+ gs_matrix *pmat) /* O - Physical transform matrix */
+{
+#ifdef CUPS_DEBUG2
+ dmprintf2(pdev->memory, "DEBUG2: cups_get_matrix(%p, %p)\n", pdev, pmat);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Set the raster width and height...
+ */
+
+ cups->header.cupsWidth = cups->width;
+ cups->header.cupsHeight = cups->height;
+
+ /*
+ * Set the transform matrix...
+ */
+
+ if (cups->landscape)
+ {
+ /*
+ * Do landscape orientation...
+ */
+#ifdef CUPS_DEBUG2
+ dprintf("DEBUG2: Landscape matrix: XX=0 XY=+1 YX=+1 YY=0\n");
+#endif /* CUPS_DEBUG2 */
+ pmat->xx = 0.0;
+ pmat->xy = (float)cups->header.HWResolution[1] / 72.0;
+ pmat->yx = (float)cups->header.HWResolution[0] / 72.0;
+ pmat->yy = 0.0;
+ pmat->tx = -(float)cups->header.HWResolution[0] * pdev->HWMargins[1] / 72.0;
+ pmat->ty = -(float)cups->header.HWResolution[1] * pdev->HWMargins[0] / 72.0;
+ }
+ else
+ {
+ /*
+ * Do portrait orientation...
+ */
+#ifdef CUPS_DEBUG2
+ dmprintf(pdev->memory, "DEBUG2: Portrait matrix: XX=+1 XY=0 YX=0 YY=-1\n");
+#endif /* CUPS_DEBUG2 */
+ pmat->xx = (float)cups->header.HWResolution[0] / 72.0;
+ pmat->xy = 0.0;
+ pmat->yx = 0.0;
+ pmat->yy = -(float)cups->header.HWResolution[1] / 72.0;
+ pmat->tx = -(float)cups->header.HWResolution[0] * pdev->HWMargins[0] / 72.0;
+ pmat->ty = (float)cups->header.HWResolution[1] *
+ ((float)cups->header.PageSize[1] - pdev->HWMargins[3]) / 72.0;
+ }
+
+#ifdef CUPS_RASTER_SYNCv1
+ if (cups->header.cupsBorderlessScalingFactor > 1.0)
+ {
+ pmat->xx *= cups->header.cupsBorderlessScalingFactor;
+ pmat->xy *= cups->header.cupsBorderlessScalingFactor;
+ pmat->yx *= cups->header.cupsBorderlessScalingFactor;
+ pmat->yy *= cups->header.cupsBorderlessScalingFactor;
+ pmat->tx *= cups->header.cupsBorderlessScalingFactor;
+ pmat->ty *= cups->header.cupsBorderlessScalingFactor;
+ }
+#endif /* CUPS_RASTER_SYNCv1 */
+
+#ifdef CUPS_DEBUG2
+ dmprintf2(pdev->memory, "DEBUG2: width = %d, height = %d\n", cups->header.cupsWidth,
+ cups->header.cupsHeight);
+ dmprintf4(pdev->memory, "DEBUG2: PageSize = [ %d %d ], HWResolution = [ %d %d ]\n",
+ cups->header.PageSize[0], cups->header.PageSize[1],
+ cups->header.HWResolution[0], cups->header.HWResolution[1]);
+ dmprintf4(pdev->memory, "DEBUG2: HWMargins = [ %.3f %.3f %.3f %.3f ]\n",
+ pdev->HWMargins[0], pdev->HWMargins[1], pdev->HWMargins[2],
+ pdev->HWMargins[3]);
+ dmprintf6(pdev->memory, "DEBUG2: matrix = [ %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
+#endif /* CUPS_DEBUG2 */
+}
+
+
+/*
+ * 'cups_get_params()' - Get pagedevice parameters.
+ */
+
+private int /* O - Error status */
+cups_get_params(gx_device *pdev, /* I - Device info */
+ gs_param_list *plist) /* I - Parameter list */
+{
+#ifdef CUPS_RASTER_SYNCv1
+ int i; /* Looping var */
+ char name[255]; /* Attribute name */
+#endif /* CUPS_RASTER_SYNCv1 */
+ int code; /* Return code */
+ gs_param_string s; /* Temporary string value */
+ bool b; /* Temporary boolean value */
+
+
+#ifdef CUPS_DEBUG2
+ dmprintf2(pdev->memory, "DEBUG2: cups_get_params(%p, %p)\n", pdev, plist);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * First process the "standard" page device parameters...
+ */
+
+#ifdef CUPS_DEBUG2
+ dmprintf(pdev->memory, "DEBUG2: before gdev_prn_get_params()\n");
+#endif /* CUPS_DEBUG2 */
+
+ if ((code = gdev_prn_get_params(pdev, plist)) < 0)
+ return (code);
+
+#ifdef CUPS_DEBUG2
+ dmprintf(pdev->memory, "DEBUG2: after gdev_prn_get_params()\n");
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Then write the CUPS parameters...
+ */
+
+ param_string_from_transient_string(s, cups->header.MediaClass);
+ if ((code = param_write_string(plist, "MediaClass", &s)) < 0)
+ return (code);
+
+ param_string_from_transient_string(s, cups->header.MediaColor);
+ if ((code = param_write_string(plist, "MediaColor", &s)) < 0)
+ return (code);
+
+ param_string_from_transient_string(s, cups->header.MediaType);
+ if ((code = param_write_string(plist, "MediaType", &s)) < 0)
+ return (code);
+
+ param_string_from_transient_string(s, cups->header.OutputType);
+ if ((code = param_write_string(plist, "OutputType", &s)) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "AdvanceDistance",
+ (int *)&(cups->header.AdvanceDistance))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "AdvanceMedia",
+ (int *)&(cups->header.AdvanceMedia))) < 0)
+ return (code);
+
+ b = cups->header.Collate;
+ if ((code = param_write_bool(plist, "Collate", &b)) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "CutMedia",
+ (int *)&(cups->header.CutMedia))) < 0)
+ return (code);
+
+ b = cups->header.Duplex;
+ if ((code = param_write_bool(plist, "Duplex", &b)) < 0)
+ return (code);
+
+ b = cups->header.InsertSheet;
+ if ((code = param_write_bool(plist, "InsertSheet", &b)) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "Jog",
+ (int *)&(cups->header.Jog))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "LeadingEdge",
+ (int *)&(cups->header.LeadingEdge))) < 0)
+ return (code);
+
+ b = cups->header.ManualFeed;
+ if ((code = param_write_bool(plist, "ManualFeed", &b)) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "MediaPosition",
+ (int *)&(cups->header.MediaPosition))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "MediaWeight",
+ (int *)&(cups->header.MediaWeight))) < 0)
+ return (code);
+
+ b = cups->header.MirrorPrint;
+ if ((code = param_write_bool(plist, "MirrorPrint", &b)) < 0)
+ return (code);
+
+ b = cups->header.NegativePrint;
+ if ((code = param_write_bool(plist, "NegativePrint", &b)) < 0)
+ return (code);
+
+ b = cups->header.OutputFaceUp;
+ if ((code = param_write_bool(plist, "OutputFaceUp", &b)) < 0)
+ return (code);
+
+ b = cups->header.Separations;
+ if ((code = param_write_bool(plist, "Separations", &b)) < 0)
+ return (code);
+
+ b = cups->header.TraySwitch;
+ if ((code = param_write_bool(plist, "TraySwitch", &b)) < 0)
+ return (code);
+
+ b = cups->header.Tumble;
+ if ((code = param_write_bool(plist, "Tumble", &b)) < 0)
+ return (code);
+
+#if 0 /* Don't include read-only parameters... */
+ if ((code = param_write_int(plist, "cupsWidth",
+ (int *)&(cups->header.cupsWidth))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsHeight",
+ (int *)&(cups->header.cupsHeight))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsBitsPerPixel",
+ (int *)&(cups->header.cupsBitsPerPixel))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsBytesPerLine",
+ (int *)&(cups->header.cupsBytesPerLine))) < 0)
+ return (code);
+#endif /* 0 */
+
+ if ((code = param_write_int(plist, "cupsMediaType",
+ (int *)&(cups->header.cupsMediaType))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsBitsPerColor",
+ (int *)&(cups->header.cupsBitsPerColor))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsColorOrder",
+ (int *)&(cups->header.cupsColorOrder))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsColorSpace",
+ (int *)&(cups->header.cupsColorSpace))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsCompression",
+ (int *)&(cups->header.cupsCompression))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsRowCount",
+ (int *)&(cups->header.cupsRowCount))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsRowFeed",
+ (int *)&(cups->header.cupsRowFeed))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsRowStep",
+ (int *)&(cups->header.cupsRowStep))) < 0)
+ return (code);
+
+#ifdef CUPS_RASTER_SYNCv1
+#if 0 /* Don't include read-only parameters... */
+ if ((code = param_write_int(plist, "cupsNumColors",
+ (int *)&(cups->header.cupsNumColors))) < 0)
+ return (code);
+#endif /* 0 */
+
+ if ((code = param_write_float(plist, "cupsBorderlessScalingFactor",
+ &(cups->header.cupsBorderlessScalingFactor))) < 0)
+ return (code);
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(name, "cupsInteger%d", i);
+ if ((code = param_write_int(plist, strdup(name),
+ (int *)(cups->header.cupsInteger + i))) < 0)
+ return (code);
+ }
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(name, "cupsReal%d", i);
+ if ((code = param_write_float(plist, strdup(name),
+ cups->header.cupsReal + i)) < 0)
+ return (code);
+ }
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(name, "cupsString%d", i);
+ param_string_from_transient_string(s, cups->header.cupsString[i]);
+ if ((code = param_write_string(plist, strdup(name), &s)) < 0)
+ return (code);
+ }
+
+ param_string_from_transient_string(s, cups->header.cupsMarkerType);
+ if ((code = param_write_string(plist, "cupsMarkerType", &s)) < 0)
+ return (code);
+
+ param_string_from_transient_string(s, cups->header.cupsRenderingIntent);
+ if ((code = param_write_string(plist, "cupsRenderingIntent", &s)) < 0)
+ return (code);
+
+ param_string_from_transient_string(s, cups->header.cupsPageSizeName);
+ if ((code = param_write_string(plist, "cupsPageSizeName", &s)) < 0)
+ return (code);
+#endif /* CUPS_RASTER_SYNCv1 */
+
+#ifdef CUPS_DEBUG2
+ dmprintf(pdev->memory, "DEBUG2: Leaving cups_get_params()\n");
+#endif /* CUPS_DEBUG2 */
+
+ return (0);
+}
+
+
+/*
+ * 'cups_get_space_params()' - Get space parameters from the RIP_CACHE env var.
+ */
+
+void
+cups_get_space_params(const gx_device_printer *pdev,
+ /* I - Printer device */
+ gdev_prn_space_params *space_params)
+ /* O - Space parameters */
+{
+ float cache_size; /* Size of tile cache in bytes */
+ char *cache_env, /* Cache size environment variable */
+ cache_units[255]; /* Cache size units */
+
+
+#ifdef CUPS_DEBUG2
+ dmprintf2(pdev->memory, "DEBUG2: cups_get_space_params(%p, %p)\n", pdev, space_params);
+#endif /* CUPS_DEBUG2 */
+
+ if ((cache_env = getenv("RIP_MAX_CACHE")) != NULL)
+ {
+ switch (sscanf(cache_env, "%f%254s", &cache_size, cache_units))
+ {
+ case 0 :
+ return;
+ case 1 :
+ cache_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE;
+ break;
+ case 2 :
+ if (tolower(cache_units[0]) == 'g')
+ cache_size *= 1024 * 1024 * 1024;
+ else if (tolower(cache_units[0]) == 'm')
+ cache_size *= 1024 * 1024;
+ else if (tolower(cache_units[0]) == 'k')
+ cache_size *= 1024;
+ else if (tolower(cache_units[0]) == 't')
+ cache_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE;
+ break;
+ }
+ }
+ else
+ return;
+
+ if (cache_size == 0)
+ return;
+
+#ifdef CUPS_DEBUG2
+ dmprintf1(pdev->memory, "DEBUG2: cache_size = %.0f\n", cache_size);
+#endif /* CUPS_DEBUG2 */
+
+ space_params->MaxBitmap = (long)cache_size;
+ space_params->BufferSpace = (long)cache_size;
+}
+
+
+/*
+ * 'cups_map_cielab()' - Map CIE Lab transformation...
+ */
+
+static double /* O - Adjusted color value */
+cups_map_cielab(double x, /* I - Raw color value */
+ double xn) /* I - Whitepoint color value */
+{
+ double x_xn; /* Fraction of whitepoint */
+
+
+ x_xn = x / xn;
+
+ if (x_xn > 0.008856)
+ return (cbrt(x_xn));
+ else
+ return (7.787 * x_xn + 16.0 / 116.0);
+}
+
+
+#ifdef dev_t_proc_encode_color
+/*
+ * 'cups_map_cmyk()' - Map a CMYK color value to device colors.
+ */
+
+private void
+cups_map_cmyk(gx_device *pdev, /* I - Device info */
+ frac c, /* I - Cyan value */
+ frac m, /* I - Magenta value */
+ frac y, /* I - Yellow value */
+ frac k, /* I - Black value */
+ frac *out) /* O - Device colors */
+{
+ int c0 = 0, c1 = 0,
+ c2 = 0, c3 = 0; /* Temporary color values */
+ float rr, rg, rb, /* Real RGB colors */
+ ciex, ciey, ciez, /* CIE XYZ colors */
+ ciey_yn, /* Normalized luminance */
+ ciel, ciea, cieb; /* CIE Lab colors */
+
+
+#ifdef CUPS_DEBUG2
+ dmprintf6(pdev->memory, "DEBUG2: cups_map_cmyk(%p, %d, %d, %d, %d, %p)\n",
+ pdev, c, m, y, k, out);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Convert the CMYK color to the destination colorspace...
+ */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ c0 = (c * 31 + m * 61 + y * 8) / 100 + k;
+
+ if (c0 < 0)
+ c0 = 0;
+ else if (c0 > frac_1)
+ c0 = frac_1;
+ out[0] = frac_1 - (frac)cups->Density[c0];
+ break;
+
+ case CUPS_CSPACE_RGBA :
+ out[3] = frac_1;
+
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ case CUPS_CSPACE_RGBW :
+ c0 = c + k;
+ c1 = m + k;
+ c2 = y + k;
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_RGBW) {
+ if ((k >= frac_1 - 1) ||
+ ((c0 >= frac_1) && (c1 >= frac_1) && (c2 >= frac_1))) {
+ c0 = frac_1;
+ c1 = frac_1;
+ c2 = frac_1;
+ c3 = frac_1;
+ } else
+ c3 = 0;
+ }
+
+ if (c0 < 0)
+ c0 = 0;
+ else if (c0 > frac_1)
+ c0 = frac_1;
+ out[0] = frac_1 - (frac)cups->Density[c0];
+
+ if (c1 < 0)
+ c1 = 0;
+ else if (c1 > frac_1)
+ c1 = frac_1;
+ out[1] = frac_1 - (frac)cups->Density[c1];
+
+ if (c2 < 0)
+ c2 = 0;
+ else if (c2 > frac_1)
+ c2 = frac_1;
+ out[2] = frac_1 - (frac)cups->Density[c2];
+
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_RGBW) {
+ if (c3 == 0)
+ out[3] = frac_1;
+ else if (c3 == frac_1)
+ out[3] = 0;
+ else
+ out[3] = frac_1;
+ }
+ break;
+
+ default :
+ case CUPS_CSPACE_K :
+ c0 = (c * 31 + m * 61 + y * 8) / 100 + k;
+
+ if (c0 < 0)
+ out[0] = 0;
+ else if (c0 > frac_1)
+ out[0] = (frac)cups->Density[frac_1];
+ else
+ out[0] = (frac)cups->Density[c0];
+ break;
+
+ case CUPS_CSPACE_CMY :
+ c0 = c + k;
+ c1 = m + k;
+ c2 = y + k;
+
+ if (c0 < 0)
+ out[0] = 0;
+ else if (c0 > frac_1)
+ out[0] = (frac)cups->Density[frac_1];
+ else
+ out[0] = (frac)cups->Density[c0];
+
+ if (c1 < 0)
+ out[1] = 0;
+ else if (c1 > frac_1)
+ out[1] = (frac)cups->Density[frac_1];
+ else
+ out[1] = (frac)cups->Density[c1];
+
+ if (c2 < 0)
+ out[2] = 0;
+ else if (c2 > frac_1)
+ out[2] = (frac)cups->Density[frac_1];
+ else
+ out[2] = (frac)cups->Density[c2];
+ break;
+
+ case CUPS_CSPACE_YMC :
+ c0 = y + k;
+ c1 = m + k;
+ c2 = c + k;
+
+ if (c0 < 0)
+ out[0] = 0;
+ else if (c0 > frac_1)
+ out[0] = (frac)cups->Density[frac_1];
+ else
+ out[0] = (frac)cups->Density[c0];
+
+ if (c1 < 0)
+ out[1] = 0;
+ else if (c1 > frac_1)
+ out[1] = (frac)cups->Density[frac_1];
+ else
+ out[1] = (frac)cups->Density[c1];
+
+ if (c2 < 0)
+ out[2] = 0;
+ else if (c2 > frac_1)
+ out[2] = (frac)cups->Density[frac_1];
+ else
+ out[2] = (frac)cups->Density[c2];
+ break;
+
+ case CUPS_CSPACE_CMYK :
+ if (c < 0)
+ out[0] = 0;
+ else if (c > frac_1)
+ out[0] = (frac)cups->Density[frac_1];
+ else
+ out[0] = (frac)cups->Density[c];
+
+ if (m < 0)
+ out[1] = 0;
+ else if (m > frac_1)
+ out[1] = (frac)cups->Density[frac_1];
+ else
+ out[1] = (frac)cups->Density[m];
+
+ if (y < 0)
+ out[2] = 0;
+ else if (y > frac_1)
+ out[2] = (frac)cups->Density[frac_1];
+ else
+ out[2] = (frac)cups->Density[y];
+
+ if (k < 0)
+ out[3] = 0;
+ else if (k > frac_1)
+ out[3] = (frac)cups->Density[frac_1];
+ else
+ out[3] = (frac)cups->Density[k];
+ break;
+
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ if (y < 0)
+ out[0] = 0;
+ else if (y > frac_1)
+ out[0] = (frac)cups->Density[frac_1];
+ else
+ out[0] = (frac)cups->Density[y];
+
+ if (m < 0)
+ out[1] = 0;
+ else if (m > frac_1)
+ out[1] = (frac)cups->Density[frac_1];
+ else
+ out[1] = (frac)cups->Density[m];
+
+ if (c < 0)
+ out[2] = 0;
+ else if (c > frac_1)
+ out[2] = (frac)cups->Density[frac_1];
+ else
+ out[2] = (frac)cups->Density[c];
+
+ if (k < 0)
+ out[3] = 0;
+ else if (k > frac_1)
+ out[3] = (frac)cups->Density[frac_1];
+ else
+ out[3] = (frac)cups->Density[k];
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ case CUPS_CSPACE_KCMY :
+ if (k < 0)
+ out[0] = 0;
+ else if (k > frac_1)
+ out[0] = (frac)cups->Density[frac_1];
+ else
+ out[0] = (frac)cups->Density[k];
+
+ if (c < 0)
+ out[1] = 0;
+ else if (c > frac_1)
+ out[1] = (frac)cups->Density[frac_1];
+ else
+ out[1] = (frac)cups->Density[c];
+
+ if (m < 0)
+ out[2] = 0;
+ else if (m > frac_1)
+ out[2] = (frac)cups->Density[frac_1];
+ else
+ out[2] = (frac)cups->Density[m];
+
+ if (y < 0)
+ out[3] = 0;
+ else if (y > frac_1)
+ out[3] = (frac)cups->Density[frac_1];
+ else
+ out[3] = (frac)cups->Density[y];
+ break;
+
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+ /*
+ * Convert CMYK to sRGB...
+ */
+
+ c0 = frac_1 - c - k;
+ c1 = frac_1 - m - k;
+ c2 = frac_1 - y - k;
+
+ if (c0 < 0)
+ c0 = 0;
+
+ if (c1 < 0)
+ c1 = 0;
+
+ if (c2 < 0)
+ c2 = 0;
+
+ /*
+ * Convert sRGB to linear RGB...
+ */
+
+ rr = pow(((double)c0 / (double)frac_1 + 0.055) / 1.055, 2.4);
+ rg = pow(((double)c1 / (double)frac_1 + 0.055) / 1.055, 2.4);
+ rb = pow(((double)c2 / (double)frac_1 + 0.055) / 1.055, 2.4);
+
+ /*
+ * Convert to CIE XYZ...
+ */
+
+ ciex = 0.412453 * rr + 0.357580 * rg + 0.180423 * rb;
+ ciey = 0.212671 * rr + 0.715160 * rg + 0.072169 * rb;
+ ciez = 0.019334 * rr + 0.119193 * rg + 0.950227 * rb;
+
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_CIEXYZ)
+ {
+ /*
+ * Convert to an integer XYZ color value...
+ */
+
+ if (cups->header.cupsBitsPerColor == 8)
+ {
+ if (ciex <= 0.0f)
+ c0 = 0;
+ else if (ciex < 1.1)
+ c0 = (int)(ciex * 231.8181 + 0.5);
+ else
+ c0 = 255;
+
+ if (ciey <= 0.0f)
+ c1 = 0;
+ else if (ciey < 1.1)
+ c1 = (int)(ciey * 231.8181 + 0.5);
+ else
+ c1 = 255;
+
+ if (ciez <= 0.0f)
+ c2 = 0;
+ else if (ciez < 1.1)
+ c2 = (int)(ciez * 231.8181 + 0.5);
+ else
+ c2 = 255;
+ }
+ else
+ {
+ if (ciex <= 0.0f)
+ c0 = 0;
+ else if (ciex < 1.1)
+ c0 = (int)(ciex * 59577.2727 + 0.5);
+ else
+ c0 = 65535;
+
+ if (ciey <= 0.0f)
+ c1 = 0;
+ else if (ciey < 1.1)
+ c1 = (int)(ciey * 59577.2727 + 0.5);
+ else
+ c1 = 65535;
+
+ if (ciez <= 0.0f)
+ c2 = 0;
+ else if (ciez < 1.1)
+ c2 = (int)(ciez * 59577.2727 + 0.5);
+ else
+ c2 = 65535;
+ }
+ }
+ else
+ {
+ /*
+ * Convert CIE XYZ to Lab...
+ */
+
+ ciey_yn = ciey / D65_Y;
+
+ if (ciey_yn > 0.008856)
+ ciel = 116 * cbrt(ciey_yn) - 16;
+ else
+ ciel = 903.3 * ciey_yn;
+
+ ciea = 500 * (cups_map_cielab(ciex, D65_X) -
+ cups_map_cielab(ciey, D65_Y));
+ cieb = 200 * (cups_map_cielab(ciey, D65_Y) -
+ cups_map_cielab(ciez, D65_Z));
+
+ if (cups->header.cupsBitsPerColor == 8)
+ {
+ /*
+ * Scale the L value and bias the a and b values by 128
+ * so that all values are in the range of 0 to 255.
+ */
+
+ ciel = ciel * 2.55 + 0.5;
+ ciea += 128.5;
+ cieb += 128.5;
+
+ if (ciel <= 0.0)
+ c0 = 0;
+ else if (ciel < 255.0)
+ c0 = (int)ciel;
+ else
+ c0 = 255;
+
+ if (ciea <= 0.0)
+ c1 = 0;
+ else if (ciea < 255.0)
+ c1 = (int)ciea;
+ else
+ c1 = 255;
+
+ if (cieb <= 0.0)
+ c2 = 0;
+ else if (cieb < 255.0)
+ c2 = (int)cieb;
+ else
+ c2 = 255;
+ }
+ else
+ {
+ /*
+ * Scale the L value and bias the a and b values by 128 so that all
+ * numbers are from 0 to 65535.
+ */
+
+ ciel = ciel * 655.35 + 0.5;
+ ciea = (ciea + 128.0) * 256.0 + 0.5;
+ cieb = (cieb + 128.0) * 256.0 + 0.5;
+
+ /*
+ * Output 16-bit values...
+ */
+
+ if (ciel <= 0.0)
+ c0 = 0;
+ else if (ciel < 65535.0)
+ c0 = (int)ciel;
+ else
+ c0 = 65535;
+
+ if (ciea <= 0.0)
+ c1 = 0;
+ else if (ciea < 65535.0)
+ c1 = (int)ciea;
+ else
+ c1 = 65535;
+
+ if (cieb <= 0.0)
+ c2 = 0;
+ else if (cieb < 65535.0)
+ c2 = (int)cieb;
+ else
+ c2 = 65535;
+ }
+ }
+
+ out[0] = cups->DecodeLUT[c0];
+ out[1] = cups->DecodeLUT[c1];
+ out[2] = cups->DecodeLUT[c2];
+ break;
+# endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ }
+
+#ifdef CUPS_DEBUG2
+ switch (cups->color_info.num_components)
+ {
+ default :
+ case 1 :
+ dmprintf1(pdev->memory, "DEBUG2: \\=== COLOR %d\n", out[0]);
+ break;
+
+ case 3 :
+ dmprintf3(pdev->memory, "DEBUG2: \\=== COLOR %d, %d, %d\n",
+ out[0], out[1], out[2]);
+ break;
+
+ case 4 :
+ dmprintf4(pdev->memory, "DEBUG2: \\=== COLOR %d, %d, %d, %d\n",
+ out[0], out[1], out[2], out[3]);
+ break;
+ }
+#endif /* CUPS_DEBUG2 */
+}
+
+
+/*
+ * 'cups_map_gray()' - Map a grayscale value to device colors.
+ */
+
+private void
+cups_map_gray(gx_device *pdev, /* I - Device info */
+ frac g, /* I - Grayscale value */
+ frac *out) /* O - Device colors */
+{
+#ifdef CUPS_DEBUG22
+ dmprintf3(pdev->memory, "DEBUG2: cups_map_gray(%p, %d, %p)\n",
+ pdev, g, out);
+#endif /* CUPS_DEBUG22 */
+
+ /*
+ * Just use the CMYK mapper...
+ */
+
+ cups_map_cmyk(pdev, 0, 0, 0, frac_1 - g, out);
+}
+
+
+/*
+ * 'cups_map_rgb()' - Map a RGB color value to device colors.
+ */
+
+private void
+cups_map_rgb(gx_device *pdev,
+ /* I - Device info */
+ const gs_imager_state *pis,/* I - Device state */
+ frac r, /* I - Red value */
+ frac g, /* I - Green value */
+ frac b, /* I - Blue value */
+ frac *out)/* O - Device colors */
+{
+ frac c, m, y, k; /* CMYK values */
+ frac mk; /* Maximum K value */
+ int tc, tm, ty; /* Temporary color values */
+
+
+#ifdef CUPS_DEBUG2
+ dmprintf6(pdev->memory, "DEBUG2: cups_map_rgb(%p, %p, %d, %d, %d, %p)\n",
+ pdev, pis, r, g, b, out);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Compute CMYK values...
+ */
+
+ c = frac_1 - r;
+ m = frac_1 - g;
+ y = frac_1 - b;
+ k = min(c, min(m, y));
+
+ if ((mk = max(c, max(m, y))) > k)
+ k = (int)((float)k * (float)k * (float)k / ((float)mk * (float)mk));
+
+ c -= k;
+ m -= k;
+ y -= k;
+
+ /*
+ * Do color correction as needed...
+ */
+
+ if (cups->HaveProfile)
+ {
+ /*
+ * Color correct CMY...
+ */
+
+ tc = cups->Matrix[0][0][c] +
+ cups->Matrix[0][1][m] +
+ cups->Matrix[0][2][y];
+ tm = cups->Matrix[1][0][c] +
+ cups->Matrix[1][1][m] +
+ cups->Matrix[1][2][y];
+ ty = cups->Matrix[2][0][c] +
+ cups->Matrix[2][1][m] +
+ cups->Matrix[2][2][y];
+
+ if (tc < 0)
+ c = 0;
+ else if (tc > frac_1)
+ c = frac_1;
+ else
+ c = (frac)tc;
+
+ if (tm < 0)
+ m = 0;
+ else if (tm > frac_1)
+ m = frac_1;
+ else
+ m = (frac)tm;
+
+ if (ty < 0)
+ y = 0;
+ else if (ty > frac_1)
+ y = frac_1;
+ else
+ y = (frac)ty;
+ }
+
+ /*
+ * Use the CMYK mapping function to produce the device colors...
+ */
+
+ cups_map_cmyk(pdev, c, m, y, k, out);
+}
+#else
+/*
+ * 'cups_map_cmyk_color()' - Map a CMYK color to a color index.
+ *
+ * This function is only called when a 4 or 6 color colorspace is
+ * selected for output. CMYK colors are *not* corrected but *are*
+ * density adjusted.
+ */
+
+private gx_color_index /* O - Color index */
+cups_map_cmyk_color(gx_device *pdev,
+ /* I - Device info */
+ const gx_color_value cv[4])/* I - CMYK color values */
+{
+ gx_color_index i; /* Temporary index */
+ gx_color_value c, m, y, k;
+ gx_color_value ic, im, iy, ik; /* Integral CMYK values */
+
+ c = cv[0];
+ m = cv[1];
+ y = cv[2];
+ k = cv[3];
+
+#ifdef CUPS_DEBUG2
+ dmprintf5(pdev->memory, "DEBUG2: cups_map_cmyk_color(%p, %d, %d, %d, %d)\n",
+ pdev, c, m, y, k);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Setup the color info data as needed...
+ */
+
+ if (pdev->color_info.num_components == 0) {
+ if (cups_set_color_info(pdev) < 0)
+ return(gx_no_color_index);
+ }
+
+ /*
+ * Density correct...
+ */
+
+ if (cups->HaveProfile)
+ {
+ c = cups->Density[c];
+ m = cups->Density[m];
+ y = cups->Density[y];
+ k = cups->Density[k];
+ }
+
+ ic = cups->EncodeLUT[c];
+ im = cups->EncodeLUT[m];
+ iy = cups->EncodeLUT[y];
+ ik = cups->EncodeLUT[k];
+
+ /*
+ * Convert the CMYK color to a color index...
+ */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((ic << 1) | im) << 1) | iy) << 1) | ik;
+ break;
+ case 2 :
+ i = (((((ic << 2) | im) << 2) | iy) << 2) | ik;
+ break;
+ case 4 :
+ i = (((((ic << 4) | im) << 4) | iy) << 4) | ik;
+ break;
+ case 8 :
+ i = (((((ic << 8) | im) << 8) | iy) << 8) | ik;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((ic << 16) | im) << 16) | iy) << 16) | ik;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((iy << 1) | im) << 1) | ic) << 1) | ik;
+ break;
+ case 2 :
+ i = (((((iy << 2) | im) << 2) | ic) << 2) | ik;
+ break;
+ case 4 :
+ i = (((((iy << 4) | im) << 4) | ic) << 4) | ik;
+ break;
+ case 8 :
+ i = (((((iy << 8) | im) << 8) | ic) << 8) | ik;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((iy << 16) | im) << 16) | ic) << 16) | ik;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+ if (ik)
+ i = 32;
+ else
+ i = 0;
+
+ if (ic && im)
+ i |= 17;
+ else if (ic && iy)
+ i |= 6;
+ else if (im && iy)
+ i |= 12;
+ else if (ic)
+ i |= 16;
+ else if (im)
+ i |= 8;
+ else if (iy)
+ i |= 4;
+ break;
+ }
+
+ case CUPS_CSPACE_KCMY :
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((ik << 1) | ic) << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((((ik << 2) | ic) << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((((ik << 4) | ic) << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((((ik << 8) | ic) << 8) | im) << 8) | iy;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((ik << 16) | ic) << 16) | im) << 16) | iy;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+ }
+
+#ifdef CUPS_DEBUG2
+ dmprintf9(pdev->memory, "DEBUG2: CMYK (%d,%d,%d,%d) -> CMYK %08x (%d,%d,%d,%d)\n",
+ c, m, y, k, (unsigned)i, ic, im, iy, ik);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Make sure we don't get a CMYK color of 255, 255, 255, 255...
+ */
+
+ if (i == gx_no_color_index)
+ i --;
+
+ return (i);
+}
+
+
+/*
+ * 'cups_map_color_rgb()' - Map a color index to an RGB color.
+ */
+
+private int
+cups_map_color_rgb(gx_device *pdev,/* I - Device info */
+ gx_color_index color,/* I - Color index */
+ gx_color_value prgb[3])
+ /* O - RGB values */
+{
+ unsigned char c0, c1, c2, c3; /* Color index components */
+ gx_color_value c, m, y, k, divk; /* Colors, Black & divisor */
+
+
+#ifdef CUPS_DEBUG2
+ dmprintf3(pdev->memory, "DEBUG2: cups_map_color_rgb(%p, %d, %p)\n", pdev,
+ (unsigned)color, prgb);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Setup the color info data as needed...
+ */
+
+ if (pdev->color_info.num_components == 0) {
+ if (cups_set_color_info(pdev) < 0)
+ return(gx_no_color_index);
+ }
+
+#ifdef CUPS_DEBUG2
+ dmprintf1(pdev->memory, "DEBUG2: COLOR %08x = ", (unsigned)color);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Extract the color components from the color index...
+ */
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ c3 = color & 1;
+ color >>= 1;
+ c2 = color & 1;
+ color >>= 1;
+ c1 = color & 1;
+ color >>= 1;
+ c0 = color;
+ break;
+ case 2 :
+ c3 = color & 3;
+ color >>= 2;
+ c2 = color & 3;
+ color >>= 2;
+ c1 = color & 3;
+ color >>= 2;
+ c0 = color;
+ break;
+ case 4 :
+ c3 = color & 15;
+ color >>= 4;
+ c2 = color & 15;
+ color >>= 4;
+ c1 = color & 15;
+ color >>= 4;
+ c0 = color;
+ break;
+ case 8 :
+ c3 = color & 255;
+ color >>= 8;
+ c2 = color & 255;
+ color >>= 8;
+ c1 = color & 255;
+ color >>= 8;
+ c0 = color;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ c3 = color & 0xffff;
+ color >>= 16;
+ c2 = color & 0xffff;
+ color >>= 16;
+ c1 = color & 0xffff;
+ color >>= 16;
+ c0 = color;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+
+ /*
+ * Convert the color components to RGB...
+ */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ prgb[0] =
+ prgb[1] =
+ prgb[2] = cups->DecodeLUT[c3];
+ break;
+
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ prgb[0] =
+ prgb[1] =
+ prgb[2] = cups->DecodeLUT[c3];
+ break;
+
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ prgb[0] = cups->DecodeLUT[c1];
+ prgb[1] = cups->DecodeLUT[c2];
+ prgb[2] = cups->DecodeLUT[c3];
+ break;
+
+ case CUPS_CSPACE_RGBA :
+ prgb[0] = cups->DecodeLUT[c0];
+ prgb[1] = cups->DecodeLUT[c1];
+ prgb[2] = cups->DecodeLUT[c2];
+ break;
+
+ case CUPS_CSPACE_CMY :
+ prgb[0] = cups->DecodeLUT[c1];
+ prgb[1] = cups->DecodeLUT[c2];
+ prgb[2] = cups->DecodeLUT[c3];
+ break;
+
+ case CUPS_CSPACE_YMC :
+ prgb[0] = cups->DecodeLUT[c3];
+ prgb[1] = cups->DecodeLUT[c2];
+ prgb[2] = cups->DecodeLUT[c1];
+ break;
+
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ k = cups->DecodeLUT[c0];
+ divk = gx_max_color_value - k;
+ if (divk == 0)
+ {
+ prgb[0] = 0;
+ prgb[1] = 0;
+ prgb[2] = 0;
+ }
+ else
+ {
+ prgb[0] = gx_max_color_value + divk -
+ gx_max_color_value * c1 / divk;
+ prgb[1] = gx_max_color_value + divk -
+ gx_max_color_value * c2 / divk;
+ prgb[2] = gx_max_color_value + divk -
+ gx_max_color_value * c3 / divk;
+ }
+ break;
+
+ case CUPS_CSPACE_RGBW :
+ /*
+ * cups->DecodeLUT actually maps to RGBW, not CMYK...
+ */
+
+ if (c3 == 0) {
+ c = 0;
+ m = 0;
+ y = 0;
+ } else {
+ c = cups->DecodeLUT[c0];
+ m = cups->DecodeLUT[c1];
+ y = cups->DecodeLUT[c2];
+ }
+
+ if (c > gx_max_color_value)
+ prgb[0] = gx_max_color_value;
+ else if (c < 0)
+ prgb[0] = 0;
+ else
+ prgb[0] = c;
+
+ if (m > gx_max_color_value)
+ prgb[1] = gx_max_color_value;
+ else if (m < 0)
+ prgb[1] = 0;
+ else
+ prgb[1] = m;
+
+ if (y > gx_max_color_value)
+ prgb[2] = gx_max_color_value;
+ else if (y < 0)
+ prgb[2] = 0;
+ else
+ prgb[2] = y;
+ break;
+
+ case CUPS_CSPACE_CMYK :
+ k = cups->DecodeLUT[c3];
+ divk = gx_max_color_value - k;
+ if (divk == 0)
+ {
+ prgb[0] = 0;
+ prgb[1] = 0;
+ prgb[2] = 0;
+ }
+ else
+ {
+ prgb[0] = gx_max_color_value + divk -
+ gx_max_color_value * c0 / divk;
+ prgb[1] = gx_max_color_value + divk -
+ gx_max_color_value * c1 / divk;
+ prgb[2] = gx_max_color_value + divk -
+ gx_max_color_value * c2 / divk;
+ }
+ break;
+
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ k = cups->DecodeLUT[c3];
+ divk = gx_max_color_value - k;
+ if (divk == 0)
+ {
+ prgb[0] = 0;
+ prgb[1] = 0;
+ prgb[2] = 0;
+ }
+ else
+ {
+ prgb[0] = gx_max_color_value + divk -
+ gx_max_color_value * c2 / divk;
+ prgb[1] = gx_max_color_value + divk -
+ gx_max_color_value * c1 / divk;
+ prgb[2] = gx_max_color_value + divk -
+ gx_max_color_value * c0 / divk;
+ }
+ break;
+
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+ break;
+# endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ }
+
+#ifdef CUPS_DEBUG2
+ dmprintf3(pdev->memory, "DEBUG2: RGB values: %d,%d,%d\n",
+ prgb[0], prgb[1], prgb[2]);
+#endif /* CUPS_DEBUG2 */
+
+ return (0);
+}
+
+
+/*
+ * 'cups_map_rgb_color()' - Map an RGB color to a color index. We map the
+ * RGB color to the output colorspace & bits (we
+ * figure out the format when we output a page).
+ */
+
+private gx_color_index /* O - Color index */
+cups_map_rgb_color(gx_device *pdev,/* I - Device info */
+ const gx_color_value cv[3])/* I - RGB color values */
+{
+ gx_color_index i; /* Temporary index */
+ gx_color_value r, g, b;
+ gx_color_value ic, im, iy, ik; /* Integral CMYK values */
+ gx_color_value mk; /* Maximum K value */
+ int tc, tm, ty; /* Temporary color values */
+ float rr, rg, rb, /* Real RGB colors */
+ ciex, ciey, ciez,
+ /* CIE XYZ colors */
+ ciey_yn, /* Normalized luminance */
+ ciel, ciea, cieb;
+ /* CIE Lab colors */
+
+ r = cv[0];
+ g = cv[1];
+ b = cv[2];
+
+#ifdef CUPS_DEBUG2
+ dmprintf4(pdev->memory, "DEBUG2: cups_map_rgb_color(%p, %d, %d, %d)\n",
+ pdev, r, g, b);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Setup the color info data as needed...
+ */
+
+ if (pdev->color_info.num_components == 0) {
+ if (cups_set_color_info(pdev) < 0)
+ return(gx_no_color_index);
+ }
+
+ /*
+ * Do color correction as needed...
+ */
+
+ if (cups->HaveProfile)
+ {
+ /*
+ * Compute CMYK values...
+ */
+
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if ((mk = max(ic, max(im, iy))) > ik)
+ ik = (int)((float)ik * (float)ik * (float)ik / ((float)mk * (float)mk));
+
+ ic -= ik;
+ im -= ik;
+ iy -= ik;
+
+ /*
+ * Color correct CMY...
+ */
+
+ tc = cups->Matrix[0][0][ic] +
+ cups->Matrix[0][1][im] +
+ cups->Matrix[0][2][iy] +
+ ik;
+ tm = cups->Matrix[1][0][ic] +
+ cups->Matrix[1][1][im] +
+ cups->Matrix[1][2][iy] +
+ ik;
+ ty = cups->Matrix[2][0][ic] +
+ cups->Matrix[2][1][im] +
+ cups->Matrix[2][2][iy] +
+ ik;
+
+ /*
+ * Density correct combined CMYK...
+ */
+
+ if (tc < 0)
+ r = gx_max_color_value;
+ else if (tc > gx_max_color_value)
+ r = gx_max_color_value - cups->Density[gx_max_color_value];
+ else
+ r = gx_max_color_value - cups->Density[tc];
+
+ if (tm < 0)
+ g = gx_max_color_value;
+ else if (tm > gx_max_color_value)
+ g = gx_max_color_value - cups->Density[gx_max_color_value];
+ else
+ g = gx_max_color_value - cups->Density[tm];
+
+ if (ty < 0)
+ b = gx_max_color_value;
+ else if (ty > gx_max_color_value)
+ b = gx_max_color_value - cups->Density[gx_max_color_value];
+ else
+ b = gx_max_color_value - cups->Density[ty];
+ }
+
+ /*
+ * Convert the RGB color to a color index...
+ */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ i = cups->EncodeLUT[(r * 31 + g * 61 + b * 8) / 100];
+ break;
+
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ ic = cups->EncodeLUT[r];
+ im = cups->EncodeLUT[g];
+ iy = cups->EncodeLUT[b];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((ic << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((ic << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((ic << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((ic << 8) | im) << 8) | iy;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((ic << 16) | im) << 16) | iy;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+ case CUPS_CSPACE_RGBW :
+ if (!r && !g && !b)
+ {
+ /*
+ * Map black to W...
+ */
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = 0x00;
+ break;
+ case 2 :
+ i = 0x00;
+ break;
+ case 4 :
+ i = 0x0000;
+ break;
+ case 8 :
+ i = 0x00000000;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = 0x0000000000000000;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+ }
+
+ case CUPS_CSPACE_RGBA :
+ ic = cups->EncodeLUT[r];
+ im = cups->EncodeLUT[g];
+ iy = cups->EncodeLUT[b];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((ic << 1) | im) << 1) | iy) << 1) | 0x01;
+ break;
+ case 2 :
+ i = (((((ic << 2) | im) << 2) | iy) << 2) | 0x03;
+ break;
+ case 4 :
+ i = (((((ic << 4) | im) << 4) | iy) << 4) | 0x0f;
+ break;
+ case 8 :
+ i = (((((ic << 8) | im) << 8) | iy) << 8) | 0xff;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((ic << 16) | im) << 16) | iy) << 16) | 0xffff;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+ default :
+ i = cups->EncodeLUT[gx_max_color_value - (r * 31 + g * 61 + b * 8) / 100];
+ break;
+
+ case CUPS_CSPACE_CMY :
+ ic = cups->EncodeLUT[gx_max_color_value - r];
+ im = cups->EncodeLUT[gx_max_color_value - g];
+ iy = cups->EncodeLUT[gx_max_color_value - b];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((ic << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((ic << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((ic << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((ic << 8) | im) << 8) | iy;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((ic << 16) | im) << 16) | iy;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+ case CUPS_CSPACE_YMC :
+ ic = cups->EncodeLUT[gx_max_color_value - r];
+ im = cups->EncodeLUT[gx_max_color_value - g];
+ iy = cups->EncodeLUT[gx_max_color_value - b];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((iy << 1) | im) << 1) | ic;
+ break;
+ case 2 :
+ i = (((iy << 2) | im) << 2) | ic;
+ break;
+ case 4 :
+ i = (((iy << 4) | im) << 4) | ic;
+ break;
+ case 8 :
+ i = (((iy << 8) | im) << 8) | ic;
+ break;
+ }
+ break;
+
+ case CUPS_CSPACE_CMYK :
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if ((mk = max(ic, max(im, iy))) > ik)
+ ik = (int)((float)ik * (float)ik * (float)ik /
+ ((float)mk * (float)mk));
+
+ ic = cups->EncodeLUT[ic - ik];
+ im = cups->EncodeLUT[im - ik];
+ iy = cups->EncodeLUT[iy - ik];
+ ik = cups->EncodeLUT[ik];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((ic << 1) | im) << 1) | iy) << 1) | ik;
+ break;
+ case 2 :
+ i = (((((ic << 2) | im) << 2) | iy) << 2) | ik;
+ break;
+ case 4 :
+ i = (((((ic << 4) | im) << 4) | iy) << 4) | ik;
+ break;
+ case 8 :
+ i = (((((ic << 8) | im) << 8) | iy) << 8) | ik;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((ic << 16) | im) << 16) | iy) << 16) | ik;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+
+#ifdef CUPS_DEBUG2
+ dmprintf8(pdev->memory, "DEBUG2: CMY (%d,%d,%d) -> CMYK %08x (%d,%d,%d,%d)\n",
+ r, g, b, (unsigned)i, ic, im, iy, ik);
+#endif /* CUPS_DEBUG2 */
+ break;
+
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if ((mk = max(ic, max(im, iy))) > ik)
+ ik = (int)((float)ik * (float)ik * (float)ik /
+ ((float)mk * (float)mk));
+
+ ic = cups->EncodeLUT[ic - ik];
+ im = cups->EncodeLUT[im - ik];
+ iy = cups->EncodeLUT[iy - ik];
+ ik = cups->EncodeLUT[ik];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((iy << 1) | im) << 1) | ic) << 1) | ik;
+ break;
+ case 2 :
+ i = (((((iy << 2) | im) << 2) | ic) << 2) | ik;
+ break;
+ case 4 :
+ i = (((((iy << 4) | im) << 4) | ic) << 4) | ik;
+ break;
+ case 8 :
+ i = (((((iy << 8) | im) << 8) | ic) << 8) | ik;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((iy << 16) | im) << 16) | ic) << 16) | ik;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if ((mk = max(ic, max(im, iy))) > ik)
+ ik = (int)((float)ik * (float)ik * (float)ik /
+ ((float)mk * (float)mk));
+
+ ic = cups->EncodeLUT[ic - ik];
+ im = cups->EncodeLUT[im - ik];
+ iy = cups->EncodeLUT[iy - ik];
+ ik = cups->EncodeLUT[ik];
+ if (ik)
+ i = 32;
+ else if (ic && im)
+ i = 17;
+ else if (ic && iy)
+ i = 6;
+ else if (im && iy)
+ i = 12;
+ else if (ic)
+ i = 16;
+ else if (im)
+ i = 8;
+ else if (iy)
+ i = 4;
+ else
+ i = 0;
+ break;
+ }
+
+ case CUPS_CSPACE_KCMY :
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if ((mk = max(ic, max(im, iy))) > ik)
+ ik = (int)((float)ik * (float)ik * (float)ik /
+ ((float)mk * (float)mk));
+
+ ic = cups->EncodeLUT[ic - ik];
+ im = cups->EncodeLUT[im - ik];
+ iy = cups->EncodeLUT[iy - ik];
+ ik = cups->EncodeLUT[ik];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((((ik << 1) | ic) << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((((ik << 2) | ic) << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((((ik << 4) | ic) << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((((ik << 8) | ic) << 8) | im) << 8) | iy;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((((ik << 16) | ic) << 16) | im) << 16) | iy;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+ /*
+ * Convert sRGB to linear RGB...
+ */
+
+ rr = pow(((double)r / (double)gx_max_color_value + 0.055) / 1.055, 2.4);
+ rg = pow(((double)g / (double)gx_max_color_value + 0.055) / 1.055, 2.4);
+ rb = pow(((double)b / (double)gx_max_color_value + 0.055) / 1.055, 2.4);
+
+ /*
+ * Convert to CIE XYZ...
+ */
+
+ ciex = 0.412453 * rr + 0.357580 * rg + 0.180423 * rb;
+ ciey = 0.212671 * rr + 0.715160 * rg + 0.072169 * rb;
+ ciez = 0.019334 * rr + 0.119193 * rg + 0.950227 * rb;
+
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_CIEXYZ)
+ {
+ /*
+ * Convert to an integer XYZ color value...
+ */
+
+ if (ciex > 1.1)
+ ic = 255;
+ else if (ciex > 0.0)
+ ic = (int)(ciex / 1.1 * 255.0 + 0.5);
+ else
+ ic = 0;
+
+ if (ciey > 1.1)
+ im = 255;
+ else if (ciey > 0.0)
+ im = (int)(ciey / 1.1 * 255.0 + 0.5);
+ else
+ im = 0;
+
+ if (ciez > 1.1)
+ iy = 255;
+ else if (ciez > 0.0)
+ iy = (int)(ciez / 1.1 * 255.0 + 0.5);
+ else
+ iy = 0;
+ }
+ else
+ {
+ /*
+ * Convert CIE XYZ to Lab...
+ */
+
+ ciey_yn = ciey / D65_Y;
+
+ if (ciey_yn > 0.008856)
+ ciel = 116 * cbrt(ciey_yn) - 16;
+ else
+ ciel = 903.3 * ciey_yn;
+
+ ciea = 500 * (cups_map_cielab(ciex, D65_X) -
+ cups_map_cielab(ciey, D65_Y));
+ cieb = 200 * (cups_map_cielab(ciey, D65_Y) -
+ cups_map_cielab(ciez, D65_Z));
+
+ /*
+ * Scale the L value and bias the a and b values by 128
+ * so that all values are in the range of 0 to 255.
+ */
+
+ ciel = ciel * 2.55 + 0.5;
+ ciea += 128.5;
+ cieb += 128.5;
+
+ /*
+ * Convert to 8-bit values...
+ */
+
+ if (ciel < 0.0)
+ ic = 0;
+ else if (ciel < 255.0)
+ ic = (int)ciel;
+ else
+ ic = 255;
+
+ if (ciea < 0.0)
+ im = 0;
+ else if (ciea < 255.0)
+ im = (int)ciea;
+ else
+ im = 255;
+
+ if (cieb < 0.0)
+ iy = 0;
+ else if (cieb < 255.0)
+ iy = (int)cieb;
+ else
+ iy = 255;
+ }
+
+ /*
+ * Put the final color value together...
+ */
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ i = (((ic << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((ic << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((ic << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((ic << 8) | im) << 8) | iy;
+ break;
+#ifdef GX_COLOR_INDEX_TYPE
+ case 16 :
+ i = (((ic << 16) | im) << 16) | iy;
+ break;
+#endif /* GX_COLOR_INDEX_TYPE */
+ }
+ break;
+# endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ }
+
+#ifdef CUPS_DEBUG2
+ dmprintf4(pdev->memory, "DEBUG2: RGB %d,%d,%d = %08x\n",
+ r, g, b, (unsigned)i);
+#endif /* CUPS_DEBUG2 */
+
+ return (i);
+}
+#endif /* dev_t_proc_encode_color */
+
+
+/*
+ * 'cups_open()' - Open the output file and initialize things.
+ */
+
+private int /* O - Error status */
+cups_open(gx_device *pdev) /* I - Device info */
+{
+ int code; /* Return status */
+
+#ifdef CUPS_DEBUG2
+ dmprintf1(pdev->memory, "DEBUG2: cups_open(%p)\n", pdev);
+#endif /* CUPS_DEBUG2 */
+
+ dmprintf(pdev->memory, "INFO: Start rendering...\n");
+ cups->printer_procs.get_space_params = cups_get_space_params;
+
+ if (cups->page == 0)
+ {
+ dmprintf(pdev->memory, "INFO: Processing page 1...\n");
+ cups->page = 1;
+ }
+
+ if ((code = cups_set_color_info(pdev)) < 0) {
+ return(code);
+ }
+
+ if ((code = gdev_prn_open(pdev)) != 0)
+ return (code);
+
+ if (cups->PPD == NULL)
+ cups->PPD = ppdOpenFile(getenv("PPD"));
+
+ return (0);
+}
+
+
+/*
+ * 'cups_output_page()' - Send one or more pages to the output file.
+ * The changes to the cups->page are done here for background printing
+ * but testing shows some regressions, so BGPrint is not used for now.
+ */
+
+private int /* O - 0 if everything is OK */
+cups_output_page(gx_device *pdev, int num_copies, int flush)
+{
+ int code = 0; /* Error code */
+
+ /* FIXME: We would like to support BGPrint=true and call gdev_prn_bg_output_page */
+ /* but there must still be other things that prevent this. */
+ if ((code = gdev_prn_output_page(pdev, num_copies, flush)) < 0)
+ return code;
+
+ cups->page ++;
+ dmprintf1(pdev->memory, "INFO: Processing page %d...\n", cups->page);
+
+ return (0);
+}
+
+
+/*
+ * 'cups_print_pages()' - Send one or more pages to the output file.
+ */
+
+private int /* O - 0 if everything is OK */
+cups_print_pages(gx_device_printer *pdev,
+ /* I - Device info */
+ FILE *fp, /* I - Output file */
+ int num_copies)
+ /* I - Number of copies */
+{
+ int code = 0; /* Error code */
+ int copy; /* Copy number */
+ int srcbytes; /* Byte width of scanline */
+ unsigned char *src, /* Scanline data */
+ *dst; /* Bitmap data */
+ ppd_attr_t *RasterVersion = NULL; /* CUPS Raster version read from PPD
+ file */
+
+ (void)fp; /* reference unused file pointer to prevent compiler warning */
+
+#ifdef CUPS_DEBUG2
+ dmprintf3(pdev->memory, "DEBUG2: cups_print_pages(%p, %p, %d)\n", pdev, fp,
+ num_copies);
+#endif /* CUPS_DEBUG2 */
+
+ /*
+ * Figure out the number of bytes per line...
+ */
+
+ switch (cups->header.cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerPixel *
+ cups->header.cupsWidth + 7) / 8;
+ break;
+
+ case CUPS_ORDER_BANDED :
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
+ cups->header.cupsBitsPerColor == 1)
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
+ cups->header.cupsWidth + 7) / 8 * 6;
+ else
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
+ cups->header.cupsWidth + 7) / 8 *
+ cups->color_info.num_components;
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
+ cups->header.cupsWidth + 7) / 8;
+ break;
+ }
+
+ /*
+ * Compute the width of a scanline and allocate input/output buffers...
+ */
+
+ srcbytes = gdev_prn_raster(pdev);
+
+#ifdef CUPS_DEBUG2
+ dmprintf4(pdev->memory, "DEBUG2: cupsBitsPerPixel = %d, cupsWidth = %d, cupsBytesPerLine = %d, srcbytes = %d\n",
+ cups->header.cupsBitsPerPixel, cups->header.cupsWidth,
+ cups->header.cupsBytesPerLine, srcbytes);
+#endif /* CUPS_DEBUG2 */
+
+ src = (unsigned char *)gs_malloc(pdev->memory->non_gc_memory, srcbytes, 1, "cups_print_pages");
+
+ if (src == NULL) /* can't allocate input buffer */
+ return_error(gs_error_VMerror);
+
+ memset(src, 0, srcbytes);
+
+ /*
+ * Need an output buffer, too...
+ */
+
+ dst = (unsigned char *)gs_malloc(pdev->memory->non_gc_memory, cups->header.cupsBytesPerLine, 2,
+ "cups_print_pages");
+
+ if (dst == NULL) /* can't allocate working area */
+ return_error(gs_error_VMerror);
+
+ memset(dst, 0, 2 * cups->header.cupsBytesPerLine);
+
+ /*
+ * See if the stream has been initialized yet...
+ */
+
+ if (cups->stream == NULL)
+ {
+ RasterVersion = ppdFindAttr(cups->PPD, "cupsRasterVersion", NULL);
+ if (RasterVersion) {
+#ifdef CUPS_DEBUG2
+ dmprintf1(pdev->memory, "DEBUG2: cupsRasterVersion = %s\n",
+ RasterVersion->value);
+#endif /* CUPS_DEBUG2 */
+ cups->cupsRasterVersion = atoi(RasterVersion->value);
+ if ((cups->cupsRasterVersion != 2) &&
+ (cups->cupsRasterVersion != 3)) {
+ dmprintf1(pdev->memory, "ERROR: Unsupported CUPS Raster Version: %s",
+ RasterVersion->value);
+ return_error(gs_error_unknownerror);
+ }
+ }
+ /* NOTE: PWG Raster output is only available with shared CUPS CUPS and
+ CUPS image libraries as the built-in libraries of Ghostscript do not
+ contain the new code needed for PWG Raster output. This conditional
+ is a temporary workaround for the time being until up-to-date CUPS
+ libraries get included. */
+ if ((cups->stream = cupsRasterOpen(fileno(cups->file),
+#if defined(CUPS_RASTER_HAVE_PWGRASTER)
+ (strcasecmp(cups->header.MediaClass,
+ "PwgRaster") == 0 ?
+ CUPS_RASTER_WRITE_PWG :
+ (cups->cupsRasterVersion == 3 ?
+ CUPS_RASTER_WRITE :
+ CUPS_RASTER_WRITE_COMPRESSED)))) ==
+ NULL)
+#else
+ (cups->cupsRasterVersion == 3 ?
+ CUPS_RASTER_WRITE :
+ CUPS_RASTER_WRITE_COMPRESSED))) == NULL)
+#endif
+ {
+ perror("ERROR: Unable to open raster stream - ");
+ return_error(gs_error_ioerror);
+ }
+ }
+
+ /*
+ * Output a page of graphics...
+ */
+
+ if (num_copies < 1)
+ num_copies = 1;
+
+ if (cups->PPD != NULL && !cups->PPD->manual_copies)
+ {
+ cups->header.NumCopies = num_copies;
+ num_copies = 1;
+ }
+
+#ifdef CUPS_DEBUG
+ dmprintf3(pdev->memory, "DEBUG2: cupsWidth = %d, cupsHeight = %d, cupsBytesPerLine = %d\n",
+ cups->header.cupsWidth, cups->header.cupsHeight,
+ cups->header.cupsBytesPerLine);
+#endif /* CUPS_DEBUG */
+
+ for (copy = num_copies; copy > 0; copy --)
+ {
+ cupsRasterWriteHeader(cups->stream, &(cups->header));
+
+ if (pdev->color_info.num_components == 1)
+ code = cups_print_chunked(pdev, src, dst, srcbytes);
+ else
+ switch (cups->header.cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ code = cups_print_chunked(pdev, src, dst, srcbytes);
+ break;
+ case CUPS_ORDER_BANDED :
+ code = cups_print_banded(pdev, src, dst, srcbytes);
+ break;
+ case CUPS_ORDER_PLANAR :
+ code = cups_print_planar(pdev, src, dst, srcbytes);
+ break;
+ }
+ if (code < 0)
+ break;
+ }
+
+ /*
+ * Free temporary storage and return...
+ */
+
+ gs_free(pdev->memory->non_gc_memory, (char *)src, srcbytes, 1, "cups_print_pages");
+ gs_free(pdev->memory->non_gc_memory, (char *)dst, cups->header.cupsBytesPerLine, 1, "cups_print_pages");
+
+ return (code);
+}
+
+
+/*
+ * 'cups_put_params()' - Set pagedevice parameters.
+ */
+
+private int /* O - Error status */
+cups_put_params(gx_device *pdev, /* I - Device info */
+ gs_param_list *plist) /* I - Parameter list */
+{
+ int i; /* Looping var */
+#ifdef CUPS_RASTER_SYNCv1
+ char name[255]; /* Name of attribute */
+ float sf; /* cupsBorderlessScalingFactor */
+#endif /* CUPS_RASTER_SYNCv1 */
+ float margins[4]; /* Physical margins of print */
+ ppd_size_t *size; /* Page size */
+ int code; /* Error code */
+ int intval; /* Integer value */
+ bool boolval; /* Boolean value */
+ float floatval; /* Floating point value */
+ gs_param_string stringval; /* String value */
+ gs_param_float_array arrayval; /* Float array value */
+ int margins_set; /* Were the margins set? */
+ int size_set; /* Was the size set? */
+ int color_set; /* Were the color attrs set? */
+ gdev_prn_space_params sp_old; /* Space parameter data */
+ int width, /* New width of page */
+ height, /* New height of page */
+ width_old = 0, /* Previous width of page */
+ height_old = 0; /* Previous height of page */
+ bool transp_old = 0; /* Previous transparency usage state */
+ ppd_attr_t *backside = NULL,
+ *backsiderequiresflippedmargins = NULL;
+ float swap;
+ int xflip = 0,
+ yflip = 0;
+ int found = 0;
+ long best_score = -1,
+ score = 0;
+ ppd_size_t *best_size = NULL;
+ int name_matched = 0,
+ size_matched = 0,
+ margins_matched = 0;
+ float long_edge_mismatch, short_edge_mismatch;
+ gs_param_string icc_pro_dummy;
+ int old_cmps = cups->color_info.num_components;
+ int old_depth = cups->color_info.depth;
+
+#ifdef CUPS_DEBUG
+ dmprintf2(pdev->memory, "DEBUG2: cups_put_params(%p, %p)\n", pdev, plist);
+#endif /* CUPS_DEBUG */
+
+ /*
+ * Process other options for CUPS...
+ */
+
+#define stringoption(name, sname) \
+ if ((code = param_read_string(plist, sname, &stringval)) < 0) \
+ { \
+ dmprintf1(pdev->memory, "ERROR: Error setting %s...\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ { \
+ strncpy(cups->header.name, (const char *)(stringval.data), \
+ stringval.size); \
+ cups->header.name[stringval.size] = '\0'; \
+ }
+
+#define intoption(name, sname, type) \
+ if ((code = param_read_int(plist, sname, &intval)) < 0) \
+ { \
+ dmprintf1(pdev->memory, "ERROR: Error setting %s ...\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ { \
+ cups->header.name = (type)intval; \
+ }
+
+#define floatoption(name, sname) \
+ if ((code = param_read_float(plist, sname, &floatval)) < 0) \
+ { \
+ dmprintf1(pdev->memory, "ERROR: Error setting %s ...\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ { \
+ cups->header.name = (float)floatval; \
+ }
+
+#define booloption(name, sname) \
+ if ((code = param_read_bool(plist, sname, &boolval)) < 0) \
+ { \
+ if ((code = param_read_null(plist, sname)) < 0) \
+ { \
+ dmprintf1(pdev->memory, "ERROR: Error setting %s ...\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ if (code == 0) \
+ cups->header.name = CUPS_FALSE; \
+ } \
+ else if (code == 0) \
+ { \
+ cups->header.name = (cups_bool_t)boolval; \
+ }
+
+#define arrayoption(name, sname, count) \
+ if ((code = param_read_float_array(plist, sname, &arrayval)) < 0) \
+ { \
+ if ((code = param_read_null(plist, sname)) < 0) \
+ { \
+ dmprintf1(pdev->memory, "ERROR: Error setting %s...\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ if (code == 0) \
+ { \
+ for (i = 0; i < count; i ++) \
+ cups->header.name[i] = 0; \
+ } \
+ } \
+ else if (code == 0) \
+ { \
+ for (i = 0; i < count; i ++) { \
+ cups->header.name[i] = (unsigned)(arrayval.data[i]); \
+ } \
+ }
+
+ sp_old = ((gx_device_printer *)pdev)->space_params;
+ width_old = pdev->width;
+ height_old = pdev->height;
+ transp_old = cups->page_uses_transparency;
+ size_set = param_read_float_array(plist, ".MediaSize", &arrayval) == 0 ||
+ param_read_float_array(plist, "PageSize", &arrayval) == 0;
+ margins_set = param_read_float_array(plist, "Margins", &arrayval) == 0;
+ color_set = param_read_int(plist, "cupsColorSpace", &intval) == 0 ||
+ param_read_int(plist, "cupsBitsPerColor", &intval) == 0;
+
+ if (!cups->user_icc) {
+ cups->user_icc = param_read_string(plist, "OutputICCProfile", &icc_pro_dummy) == 0;
+ }
+
+ /* We set the old dimensions to 1 if we have a color depth change, so
+ that memory reallocation gets forced. This is perhaps not the correct
+ approach to prevent crashes like in bug 690435. We keep it for the
+ time being until we decide finally */
+ if (color_set) {
+ width_old = 1;
+ height_old = 1;
+ }
+ /* We also recompute page size and margins if we simply get onto a new
+ page without necessarily having a page size change in the PostScript
+ code, as for some printers margins have to be flipped on the back sides of
+ the sheets (even pages) when printing duplex */
+ if (cups->page != cups->lastpage) {
+ size_set = 1;
+ cups->lastpage = cups->page;
+ }
+
+ stringoption(MediaClass, "MediaClass")
+ stringoption(MediaColor, "MediaColor")
+ stringoption(MediaType, "MediaType")
+ stringoption(OutputType, "OutputType")
+ intoption(AdvanceDistance, "AdvanceDistance", unsigned)
+ intoption(AdvanceMedia, "AdvanceMedia", cups_adv_t)
+ booloption(Collate, "Collate")
+ intoption(CutMedia, "CutMedia", cups_cut_t)
+ booloption(Duplex, "Duplex")
+ arrayoption(ImagingBoundingBox, "ImagingBoundingBox", 4)
+ booloption(InsertSheet, "InsertSheet")
+ intoption(Jog, "Jog", cups_jog_t)
+ intoption(LeadingEdge, "LeadingEdge", cups_edge_t)
+ arrayoption(Margins, "Margins", 2)
+ booloption(ManualFeed, "ManualFeed")
+ intoption(MediaPosition, "cupsMediaPosition", unsigned) /* Compatibility */
+ intoption(MediaPosition, "MediaPosition", unsigned)
+ intoption(MediaWeight, "MediaWeight", unsigned)
+ booloption(MirrorPrint, "MirrorPrint")
+ booloption(NegativePrint, "NegativePrint")
+ intoption(Orientation, "Orientation", cups_orient_t)
+ booloption(OutputFaceUp, "OutputFaceUp")
+ booloption(Separations, "Separations")
+ booloption(TraySwitch, "TraySwitch")
+ booloption(Tumble, "Tumble")
+ intoption(cupsMediaType, "cupsMediaType", unsigned)
+ intoption(cupsBitsPerColor, "cupsBitsPerColor", unsigned)
+ intoption(cupsColorOrder, "cupsColorOrder", cups_order_t)
+ intoption(cupsColorSpace, "cupsColorSpace", cups_cspace_t)
+ intoption(cupsCompression, "cupsCompression", unsigned)
+ intoption(cupsRowCount, "cupsRowCount", unsigned)
+ intoption(cupsRowFeed, "cupsRowFeed", unsigned)
+ intoption(cupsRowStep, "cupsRowStep", unsigned)
+
+#ifdef GX_COLOR_INDEX_TYPE
+ /*
+ * Support cupsPreferredBitsPerColor - basically, allows you to
+ * request 16-bits per color in a backwards-compatible way...
+ */
+
+ if (!param_read_int(plist, "cupsPreferredBitsPerColor", &intval))
+ if (intval > cups->header.cupsBitsPerColor && intval <= 16)
+ cups->header.cupsBitsPerColor = intval;
+#endif /* GX_COLOR_INDEX_TYPE */
+
+#ifdef CUPS_RASTER_SYNCv1
+ floatoption(cupsBorderlessScalingFactor, "cupsBorderlessScalingFactor");
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(name, "cupsInteger%d", i);
+ intoption(cupsInteger[i],strdup(name), unsigned)
+ }
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(name, "cupsReal%d", i);
+ floatoption(cupsReal[i], strdup(name))
+ }
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(name, "cupsString%d", i);
+ stringoption(cupsString[i], strdup(name))
+ }
+
+ stringoption(cupsMarkerType, "cupsMarkerType");
+ stringoption(cupsRenderingIntent, "cupsRenderingIntent");
+ stringoption(cupsPageSizeName, "cupsPageSizeName");
+#endif /* CUPS_RASTER_SYNCv1 */
+
+ if ((code = param_read_string(plist, "cupsProfile", &stringval)) < 0)
+ {
+ param_signal_error(plist, "cupsProfile", code);
+ return (code);
+ }
+ else if (code == 0)
+ {
+ if (cups->Profile != NULL)
+ free(cups->Profile);
+
+ cups->Profile = strdup((char *)stringval.data);
+ }
+
+ if ((code = cups_set_color_info(pdev)) < 0) {
+ return(code);
+ }
+
+ /*
+ * Then process standard page device options...
+ */
+
+ if ((code = gdev_prn_put_params(pdev, plist)) < 0)
+ return (code);
+
+ /* If cups_set_color_info() changed the color model of the device we want to
+ * force the raster memory to be recreated/reinitialized
+ */
+ if (cups->color_info.num_components != old_cmps || cups->color_info.depth != old_depth) {
+ width_old = 0;
+ height_old = 0;
+ }
+ else {
+ /* pdev->width/height may have been changed by the call to
+ * gdev_prn_put_params()
+ */
+ width_old = pdev->width;
+ height_old = pdev->height;
+ }
+ /*
+ * Update margins/sizes as needed...
+ */
+
+ if (size_set)
+ {
+ /*
+ * Compute the page margins...
+ */
+
+#ifdef CUPS_DEBUG
+ dmprintf2(pdev->memory, "DEBUG: Updating PageSize to [%.0f %.0f]...\n",
+ cups->MediaSize[0], cups->MediaSize[1]);
+#endif /* CUPS_DEBUG */
+
+ memset(margins, 0, sizeof(margins));
+
+ cups->landscape = 0;
+
+ if (cups->PPD != NULL)
+ {
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.Duplex = %d\n", cups->header.Duplex);
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.Tumble = %d\n", cups->header.Tumble);
+ dmprintf1(pdev->memory, "DEBUG2: cups->page = %d\n", cups->page);
+ dmprintf1(pdev->memory, "DEBUG2: cups->PPD = %p\n", cups->PPD);
+#endif /* CUPS_DEBUG */
+
+ backside = ppdFindAttr(cups->PPD, "cupsBackSide", NULL);
+ if (backside) {
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cupsBackSide = %s\n", backside->value);
+#endif /* CUPS_DEBUG */
+ cups->PPD->flip_duplex = 0;
+ }
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups->PPD->flip_duplex = %d\n", cups->PPD->flip_duplex);
+#endif /* CUPS_DEBUG */
+
+ backsiderequiresflippedmargins =
+ ppdFindAttr(cups->PPD, "APDuplexRequiresFlippedMargin", NULL);
+#ifdef CUPS_DEBUG
+ if (backsiderequiresflippedmargins)
+ dmprintf1(pdev->memory, "DEBUG2: APDuplexRequiresFlippedMargin = %s\n",
+ backsiderequiresflippedmargins->value);
+#endif /* CUPS_DEBUG */
+
+ if (cups->header.Duplex &&
+ (cups->header.Tumble &&
+ (backside && !strcasecmp(backside->value, "Flipped"))) &&
+ !(cups->page & 1))
+ {
+ xflip = 1;
+ if (backsiderequiresflippedmargins &&
+ !strcasecmp(backsiderequiresflippedmargins->value, "False")) {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (1) Flip: X=1 Y=0\n");
+#endif /* CUPS_DEBUG */
+ yflip = 0;
+ } else {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (1) Flip: X=1 Y=1\n");
+#endif /* CUPS_DEBUG */
+ yflip = 1;
+ }
+ }
+ else if (cups->header.Duplex &&
+ (!cups->header.Tumble &&
+ (backside && !strcasecmp(backside->value, "Flipped"))) &&
+ !(cups->page & 1))
+ {
+ xflip = 0;
+ if (backsiderequiresflippedmargins &&
+ !strcasecmp(backsiderequiresflippedmargins->value, "False")) {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (2) Flip: X=0 Y=1\n");
+#endif /* CUPS_DEBUG */
+ yflip = 1;
+ } else {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (2) Flip: X=0 Y=0\n");
+#endif /* CUPS_DEBUG */
+ yflip = 0;
+ }
+ }
+ else if (cups->header.Duplex &&
+ ((!cups->header.Tumble &&
+ (cups->PPD->flip_duplex ||
+ (backside && !strcasecmp(backside->value, "Rotated")))) ||
+ (cups->header.Tumble &&
+ (backside && !strcasecmp(backside->value, "ManualTumble")))) &&
+ !(cups->page & 1))
+ {
+ xflip = 1;
+ if (backsiderequiresflippedmargins &&
+ !strcasecmp(backsiderequiresflippedmargins->value, "True")) {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (3) Flip: X=1 Y=0\n");
+#endif /* CUPS_DEBUG */
+ yflip = 0;
+ } else {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (3) Flip: X=1 Y=1\n");
+#endif /* CUPS_DEBUG */
+ yflip = 1;
+ }
+ }
+ else
+ {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG2: (4) Flip: X=0 Y=0\n");
+#endif /* CUPS_DEBUG */
+ xflip = 0;
+ yflip = 0;
+ }
+
+#ifdef CUPS_RASTER_SYNCv1
+ /*
+ * Chack whether cupsPageSizeName has a valid value
+ */
+
+ if (strlen(cups->header.cupsPageSizeName) != 0) {
+ found = 0;
+ for (i = cups->PPD->num_sizes, size = cups->PPD->sizes;
+ i > 0;
+ i --, size ++)
+ if (strcasecmp(cups->header.cupsPageSizeName, size->name) == 0) {
+ found = 1;
+ break;
+ }
+ if (found == 0) cups->header.cupsPageSizeName[0] = '\0';
+ }
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.cupsPageSizeName = %s\n",
+ cups->header.cupsPageSizeName);
+#endif /* CUPS_DEBUG */
+#endif /* CUPS_RASTER_SYNCv1 */
+
+ /*
+ * Find the matching page size...
+ */
+
+#define LONG_EDGE_LENGTH_MATCH_LIMIT 0.07
+#define SHORT_EDGE_LENGTH_MATCH_LIMIT 0.05
+#define PAGESIZE_SCORE_NAME 3
+#define PAGESIZE_SCORE_SIZE_MARGINS 2
+#define PAGESIZE_SCORE_SIZE 1
+
+ best_score = -1;
+ best_size = NULL;
+ for (i = cups->PPD->num_sizes, size = cups->PPD->sizes;
+ i > 0;
+ i --, size ++)
+ {
+ if (size->length == 0 || size->width == 0) continue;
+
+ score = 0;
+ name_matched = 0;
+ size_matched = 0;
+ margins_matched = 0;
+
+#ifdef CUPS_RASTER_SYNCv1
+ /* Match the size requested as default (cupsPageSizeName) */
+ if ((strlen(cups->header.cupsPageSizeName) != 0) &&
+ (strcasecmp(cups->header.cupsPageSizeName, size->name) == 0))
+ name_matched = 1;
+#endif
+
+ long_edge_mismatch =
+ fabs(cups->MediaSize[1] - size->length)/size->length;
+ short_edge_mismatch =
+ fabs(cups->MediaSize[0] - size->width)/size->width;
+ if (size->length < size->width)
+ {
+ swap = long_edge_mismatch;
+ long_edge_mismatch = short_edge_mismatch;
+ short_edge_mismatch = swap;
+ }
+
+ if (long_edge_mismatch < LONG_EDGE_LENGTH_MATCH_LIMIT &&
+ short_edge_mismatch < SHORT_EDGE_LENGTH_MATCH_LIMIT)
+ {
+ size_matched = 1;
+ /* If two sizes match within the limits, take the one with less
+ mismatch */
+ score = (long)(9999.0 -
+ long_edge_mismatch * short_edge_mismatch * 9999.0 /
+ LONG_EDGE_LENGTH_MATCH_LIMIT /
+ SHORT_EDGE_LENGTH_MATCH_LIMIT);
+ if (score < 0) score = 0;
+ /* We check whether all 4 margins match with the margin info
+ of the page size in the PPD. Here we check also for swapped
+ left/right and top/bottom margins as the cups->HWMargins
+ info can be from the previous page and there the margins
+ can be swapped due to duplex printing requirements */
+ if (!margins_set ||
+ (((fabs(cups->HWMargins[0] - size->left) < 1.0 &&
+ fabs(cups->HWMargins[2] - size->width + size->right) < 1.0) ||
+ (fabs(cups->HWMargins[0] - size->width + size->right) < 1.0 &&
+ fabs(cups->HWMargins[2] - size->left) < 1.0)) &&
+ ((fabs(cups->HWMargins[1] - size->bottom) < 1.0 &&
+ fabs(cups->HWMargins[3] - size->length + size->top) < 1.0) ||
+ (fabs(cups->HWMargins[1] - size->length + size->top) < 1.0 &&
+ fabs(cups->HWMargins[3] - size->bottom) < 1.0))))
+ margins_matched = 1;
+ }
+
+ if (name_matched && size_matched)
+ score += PAGESIZE_SCORE_NAME * 10000;
+ else if (margins_matched)
+ score += PAGESIZE_SCORE_SIZE_MARGINS * 10000;
+ else if (size_matched)
+ score += PAGESIZE_SCORE_SIZE * 10000;
+
+ if (score > best_score)
+ {
+ best_score = score;
+ if (score > 0)
+ best_size = size;
+ }
+ }
+
+ if (best_size)
+ {
+ /*
+ * Standard size...
+ */
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG: size = %s\n", best_size->name);
+#endif /* CUPS_DEBUG */
+
+ gx_device_set_media_size(pdev, best_size->width, best_size->length);
+
+ cups->landscape = 0;
+
+ margins[0] = best_size->left / 72.0;
+ margins[1] = best_size->bottom / 72.0;
+ margins[2] = (best_size->width - best_size->right) / 72.0;
+ margins[3] = (best_size->length - best_size->top) / 72.0;
+ if (xflip == 1)
+ {
+ swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
+ }
+ if (yflip == 1)
+ {
+ swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
+ }
+ }
+ else
+ {
+ /*
+ * No matching portrait size; look for a matching size in
+ * landscape orientation...
+ */
+
+ best_score = -1;
+ best_size = NULL;
+ for (i = cups->PPD->num_sizes, size = cups->PPD->sizes;
+ i > 0;
+ i --, size ++)
+ {
+ if (size->length == 0 || size->width == 0) continue;
+
+ score = 0;
+ name_matched = 0;
+ size_matched = 0;
+ margins_matched = 0;
+
+#ifdef CUPS_RASTER_SYNCv1
+ /* Match the size requested as default (cupsPageSizeName) */
+ if ((strlen(cups->header.cupsPageSizeName) != 0) &&
+ (strcasecmp(cups->header.cupsPageSizeName, size->name) == 0))
+ name_matched = 1;
+#endif
+
+ long_edge_mismatch =
+ fabs(cups->MediaSize[0] - size->length)/size->length;
+ short_edge_mismatch =
+ fabs(cups->MediaSize[1] - size->width)/size->width;
+ if (size->length < size->width)
+ {
+ swap = long_edge_mismatch;
+ long_edge_mismatch = short_edge_mismatch;
+ short_edge_mismatch = swap;
+ }
+
+ if (long_edge_mismatch < LONG_EDGE_LENGTH_MATCH_LIMIT &&
+ short_edge_mismatch < SHORT_EDGE_LENGTH_MATCH_LIMIT)
+ {
+ size_matched = 1;
+ /* If two sizes match within the limits, take the one with less
+ mismatch */
+ score = (long)(9999.0 -
+ long_edge_mismatch * short_edge_mismatch * 9999.0 /
+ LONG_EDGE_LENGTH_MATCH_LIMIT /
+ SHORT_EDGE_LENGTH_MATCH_LIMIT);
+ if (score < 0) score = 0;
+ /* We check whether all 4 margins match with the margin info
+ of the page size in the PPD. Here we check also for swapped
+ left/right and top/bottom margins as the cups->HWMargins
+ info can be from the previous page and there the margins
+ can be swapped due to duplex printing requirements */
+ if (!margins_set ||
+ (((fabs(cups->HWMargins[1] - size->left) < 1.0 &&
+ fabs(cups->HWMargins[3] - size->width + size->right) < 1.0)||
+ (fabs(cups->HWMargins[1] - size->width + size->right) < 1.0 &&
+ fabs(cups->HWMargins[3] - size->left) < 1.0)) &&
+ ((fabs(cups->HWMargins[0] - size->bottom) < 1.0 &&
+ fabs(cups->HWMargins[2] - size->length + size->top) < 1.0) ||
+ (fabs(cups->HWMargins[0] - size->length + size->top) < 1.0 &&
+ fabs(cups->HWMargins[2] - size->bottom) < 1.0))))
+ margins_matched = 1;
+ }
+
+ if (name_matched && size_matched)
+ score += PAGESIZE_SCORE_NAME * 10000;
+ else if (margins_matched)
+ score += PAGESIZE_SCORE_SIZE_MARGINS * 10000;
+ else if (size_matched)
+ score += PAGESIZE_SCORE_SIZE * 10000;
+
+ if (score > best_score)
+ {
+ best_score = score;
+ if (score > 0)
+ best_size = size;
+ }
+ }
+
+ if (best_size)
+ {
+ /*
+ * Standard size in landscape orientation...
+ */
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG: landscape size = %s\n", best_size->name);
+#endif /* CUPS_DEBUG */
+
+ gx_device_set_media_size(pdev, best_size->length, best_size->width);
+
+ cups->landscape = 1;
+
+ margins[0] = (best_size->length - best_size->top) / 72.0;
+ margins[1] = best_size->left / 72.0;
+ margins[2] = best_size->bottom / 72.0;
+ margins[3] = (best_size->width - best_size->right) / 72.0;
+ if (xflip == 1)
+ {
+ swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
+ }
+ if (yflip == 1)
+ {
+ swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
+ }
+ }
+ else
+ {
+ /*
+ * Custom size...
+ */
+
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG: size = Custom\n");
+#endif /* CUPS_DEBUG */
+
+ /* Rotate page if it only fits into the printer's dimensions
+ when rotated */
+ if (((cups->MediaSize[0] > cups->PPD->custom_max[0]) ||
+ (cups->MediaSize[1] > cups->PPD->custom_max[1])) &&
+ ((cups->MediaSize[0] <= cups->PPD->custom_max[1]) &&
+ (cups->MediaSize[1] <= cups->PPD->custom_max[0]))) {
+ /* Rotate */
+ gx_device_set_media_size(pdev, cups->MediaSize[1],
+ cups->MediaSize[0]);
+
+ cups->landscape = 1;
+
+ margins[0] = cups->PPD->custom_margins[3] / 72.0;
+ margins[1] = cups->PPD->custom_margins[0] / 72.0;
+ margins[2] = cups->PPD->custom_margins[1] / 72.0;
+ margins[3] = cups->PPD->custom_margins[2] / 72.0;
+ if (xflip == 1)
+ {
+ swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
+ }
+ if (yflip == 1)
+ {
+ swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
+ }
+ } else {
+ /* Do not rotate */
+ cups->landscape = 0;
+
+ for (i = 0; i < 4; i ++)
+ margins[i] = cups->PPD->custom_margins[i] / 72.0;
+ if (xflip == 1)
+ {
+ swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
+ }
+ if (yflip == 1)
+ {
+ swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
+ }
+ }
+ }
+ }
+
+#ifdef CUPS_DEBUG
+ dmprintf4(pdev->memory, "DEBUG: margins[] = [ %f %f %f %f ]\n",
+ margins[0], margins[1], margins[2], margins[3]);
+#endif /* CUPS_DEBUG */
+ }
+ else
+ {
+ /* If we do not have a PPD file, make sure that margins given via the
+ input file or via something like
+ "-c '<</.HWMargins[12 12 12 12] /Margins[0 0]>>setpagedevice'"
+ on the command line are conserved */
+ margins[0] = pdev->HWMargins[0] / 72.0;
+ margins[1] = pdev->HWMargins[1] / 72.0;
+ margins[2] = pdev->HWMargins[2] / 72.0;
+ margins[3] = pdev->HWMargins[3] / 72.0;
+ }
+
+ /*
+ * Set the margins to update the bitmap size...
+ */
+
+ gx_device_set_margins(pdev, margins, false);
+ }
+
+ /*
+ * Reallocate memory if the size or color depth was changed...
+ */
+
+ if (color_set || size_set)
+ {
+ /*
+ * Make sure the page image is the correct size - current Ghostscript
+ * does not keep track of the margins in the bitmap size...
+ */
+
+ if (cups->landscape)
+ {
+ width = (pdev->MediaSize[1] - pdev->HWMargins[1] - pdev->HWMargins[3]) *
+ pdev->HWResolution[0] / 72.0f + 0.499f;
+ height = (pdev->MediaSize[0] - pdev->HWMargins[0] - pdev->HWMargins[2]) *
+ pdev->HWResolution[1] / 72.0f + 0.499f;
+ }
+ else
+ {
+ width = (pdev->MediaSize[0] - pdev->HWMargins[0] - pdev->HWMargins[2]) *
+ pdev->HWResolution[0] / 72.0f + 0.499f;
+ height = (pdev->MediaSize[1] - pdev->HWMargins[1] - pdev->HWMargins[3]) *
+ pdev->HWResolution[1] / 72.0f + 0.499f;
+ }
+
+#ifdef CUPS_RASTER_SYNCv1
+ if (cups->header.cupsBorderlessScalingFactor > 1.0)
+ {
+ width *= cups->header.cupsBorderlessScalingFactor;
+ height *= cups->header.cupsBorderlessScalingFactor;
+ }
+#endif /* CUPS_RASTER_SYNCv1 */
+
+ pdev->width = width;
+ pdev->height = height;
+
+ /*
+ * Don't reallocate memory unless the device has been opened...
+ * Also reallocate only if the size has actually changed...
+ */
+
+ if (pdev->is_open)
+ {
+
+ /*
+ * Device is open and size has changed, so reallocate...
+ */
+
+#ifdef CUPS_DEBUG
+ dmprintf4(pdev->memory, "DEBUG2: Reallocating memory, [%.0f %.0f] = %dx%d pixels...\n",
+ pdev->MediaSize[0], pdev->MediaSize[1], width, height);
+#endif /* CUPS_DEBUG */
+
+ if ((code = gdev_prn_maybe_realloc_memory((gx_device_printer *)pdev,
+ &sp_old,
+ width_old, height_old,
+ transp_old))
+ < 0)
+ return (code);
+#ifdef CUPS_DEBUG
+ dmprintf4(pdev->memory, "DEBUG2: Reallocated memory, [%.0f %.0f] = %dx%d pixels...\n",
+ pdev->MediaSize[0], pdev->MediaSize[1], width, height);
+#endif /* CUPS_DEBUG */
+ }
+ else
+ {
+ /*
+ * Device isn't yet open, so just save the new width and height...
+ */
+
+#ifdef CUPS_DEBUG
+ dmprintf4(pdev->memory, "DEBUG: Setting initial media size, [%.0f %.0f] = %dx%d pixels...\n",
+ pdev->MediaSize[0], pdev->MediaSize[1], width, height);
+#endif /* CUPS_DEBUG */
+
+ pdev->width = width;
+ pdev->height = height;
+ }
+ }
+
+ /*
+ * Set CUPS raster header values...
+ */
+
+ cups->header.HWResolution[0] = pdev->HWResolution[0];
+ cups->header.HWResolution[1] = pdev->HWResolution[1];
+
+#ifdef CUPS_RASTER_SYNCv1
+
+ if (cups->landscape)
+ {
+ cups->header.cupsPageSize[0] = pdev->MediaSize[1];
+ cups->header.cupsPageSize[1] = pdev->MediaSize[0];
+
+ if ((sf = cups->header.cupsBorderlessScalingFactor) < 1.0)
+ sf = 1.0;
+
+ cups->header.PageSize[0] = (pdev->MediaSize[1] * sf) + 0.5;
+ cups->header.PageSize[1] = (pdev->MediaSize[0] * sf) + 0.5;
+
+ if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
+ {
+ cups->header.Margins[0] = (pdev->HWMargins[1] * sf) + 0.5;
+ cups->header.Margins[1] = (pdev->HWMargins[2] * sf) + 0.5;
+ cups->header.ImagingBoundingBox[0] = (pdev->HWMargins[1] * sf) + 0.5;
+ cups->header.ImagingBoundingBox[1] = (pdev->HWMargins[2] * sf) + 0.5;
+ cups->header.ImagingBoundingBox[2] = ((pdev->MediaSize[1] -
+ pdev->HWMargins[3]) * sf) + 0.5;
+ cups->header.ImagingBoundingBox[3] = ((pdev->MediaSize[0] -
+ pdev->HWMargins[0]) * sf) + 0.5;
+ cups->header.cupsImagingBBox[0] = pdev->HWMargins[1];
+ cups->header.cupsImagingBBox[1] = pdev->HWMargins[2];
+ cups->header.cupsImagingBBox[2] = pdev->MediaSize[1] - pdev->HWMargins[3];
+ cups->header.cupsImagingBBox[3] = pdev->MediaSize[0] - pdev->HWMargins[0];
+ }
+ else
+ {
+ for (i = 0; i < 2; i ++)
+ cups->header.Margins[i] = 0;
+ for (i = 0; i < 4; i ++)
+ {
+ cups->header.ImagingBoundingBox[i] = 0;
+ cups->header.cupsImagingBBox[i] = 0.0;
+ }
+ }
+ }
+ else
+ {
+ cups->header.cupsPageSize[0] = pdev->MediaSize[0];
+ cups->header.cupsPageSize[1] = pdev->MediaSize[1];
+
+ if ((sf = cups->header.cupsBorderlessScalingFactor) < 1.0)
+ sf = 1.0;
+
+ cups->header.PageSize[0] = (pdev->MediaSize[0] * sf) + 0.5;
+ cups->header.PageSize[1] = (pdev->MediaSize[1] * sf) + 0.5;
+
+ if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
+ {
+ cups->header.Margins[0] = (pdev->HWMargins[0] * sf) + 0.5;
+ cups->header.Margins[1] = (pdev->HWMargins[1] * sf) + 0.5;
+ cups->header.ImagingBoundingBox[0] = (pdev->HWMargins[0] * sf) + 0.5;
+ cups->header.ImagingBoundingBox[1] = (pdev->HWMargins[1] * sf) + 0.5;
+ cups->header.ImagingBoundingBox[2] = ((pdev->MediaSize[0] -
+ pdev->HWMargins[2]) * sf) + 0.5;
+ cups->header.ImagingBoundingBox[3] = ((pdev->MediaSize[1] -
+ pdev->HWMargins[3]) * sf) + 0.5;
+ cups->header.cupsImagingBBox[0] = pdev->HWMargins[0];
+ cups->header.cupsImagingBBox[1] = pdev->HWMargins[1];
+ cups->header.cupsImagingBBox[2] = pdev->MediaSize[0] - pdev->HWMargins[2];
+ cups->header.cupsImagingBBox[3] = pdev->MediaSize[1] - pdev->HWMargins[3];
+ }
+ else
+ {
+ for (i = 0; i < 2; i ++)
+ cups->header.Margins[i] = 0;
+ for (i = 0; i < 4; i ++)
+ {
+ cups->header.ImagingBoundingBox[i] = 0;
+ cups->header.cupsImagingBBox[i] = 0.0;
+ }
+ }
+ }
+
+#else
+
+ if (cups->landscape)
+ {
+ cups->header.PageSize[0] = pdev->MediaSize[1] + 0.5;
+ cups->header.PageSize[1] = pdev->MediaSize[0] + 0.5;
+
+ if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
+ {
+ cups->header.Margins[0] = (pdev->HWMargins[1]) + 0.5;
+ cups->header.Margins[1] = (pdev->HWMargins[2]) + 0.5;
+ cups->header.ImagingBoundingBox[0] = (pdev->HWMargins[1]) + 0.5;
+ cups->header.ImagingBoundingBox[1] = (pdev->HWMargins[0]) + 0.5;
+ cups->header.ImagingBoundingBox[2] = (pdev->MediaSize[1] -
+ pdev->HWMargins[3]) + 0.5;
+ cups->header.ImagingBoundingBox[3] = (pdev->MediaSize[0] -
+ pdev->HWMargins[2]) + 0.5;
+ }
+ else
+ {
+ for (i = 0; i < 2; i ++)
+ cups->header.Margins[i] = 0;
+ for (i = 0; i < 4; i ++)
+ cups->header.ImagingBoundingBox[i] = 0;
+ }
+ }
+ else
+ {
+ cups->header.PageSize[0] = pdev->MediaSize[0] + 0.5;
+ cups->header.PageSize[1] = pdev->MediaSize[1] + 0.5;
+
+ if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
+ {
+ cups->header.Margins[0] = (pdev->HWMargins[0]) + 0.5;
+ cups->header.Margins[1] = (pdev->HWMargins[1]) + 0.5;
+ cups->header.ImagingBoundingBox[0] = (pdev->HWMargins[0]) + 0.5;
+ cups->header.ImagingBoundingBox[1] = (pdev->HWMargins[3]) + 0.5;
+ cups->header.ImagingBoundingBox[2] = (pdev->MediaSize[0] -
+ pdev->HWMargins[2]) + 0.5;
+ cups->header.ImagingBoundingBox[3] = (pdev->MediaSize[1] -
+ pdev->HWMargins[1]) + 0.5;
+ }
+ else
+ {
+ for (i = 0; i < 2; i ++)
+ cups->header.Margins[i] = 0;
+ for (i = 0; i < 4; i ++)
+ cups->header.ImagingBoundingBox[i] = 0;
+ }
+ }
+
+#endif /* CUPS_RASTER_SYNCv1 */
+ cups->header.cupsWidth = cups->width;
+ cups->header.cupsHeight = cups->height;
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: ppd = %p\n", cups->PPD);
+ dmprintf2(pdev->memory, "DEBUG2: PageSize = [ %.3f %.3f ]\n",
+ pdev->MediaSize[0], pdev->MediaSize[1]);
+ if (size_set)
+ dmprintf4(pdev->memory, "DEBUG2: margins = [ %.3f %.3f %.3f %.3f ]\n",
+ margins[0], margins[1], margins[2], margins[3]);
+ dmprintf2(pdev->memory, "DEBUG2: HWResolution = [ %.3f %.3f ]\n",
+ pdev->HWResolution[0], pdev->HWResolution[1]);
+ dmprintf2(pdev->memory, "DEBUG2: width = %d, height = %d\n",
+ pdev->width, pdev->height);
+ dmprintf4(pdev->memory, "DEBUG2: HWMargins = [ %.3f %.3f %.3f %.3f ]\n",
+ pdev->HWMargins[0], pdev->HWMargins[1],
+ pdev->HWMargins[2], pdev->HWMargins[3]);
+#endif /* CUPS_DEBUG */
+
+ return (0);
+}
+
+/*
+ * 'cups_set_color_info()' - Set the color information structure based on
+ * the required output.
+ */
+
+private int
+cups_set_color_info(gx_device *pdev) /* I - Device info */
+{
+ int i, j, k; /* Looping vars */
+ int max_lut; /* Maximum LUT value */
+ float d, g; /* Density and gamma correction */
+ float m[3][3]; /* Color correction matrix */
+ char resolution[41]; /* Resolution string */
+ ppd_profile_t *profile; /* Color profile information */
+ int code = 0;
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups_set_color_info(%p)\n", pdev);
+#endif /* CUPS_DEBUG */
+
+#ifndef GX_COLOR_INDEX_TYPE
+ if (cups->header.cupsBitsPerColor > 8)
+ cups->header.cupsBitsPerColor = 8;
+#endif /* !GX_COLOR_INDEX_TYPE */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+#ifdef CUPS_RASTER_SYNCv1
+ cups->header.cupsNumColors = 1;
+#endif /* CUPS_RASTER_SYNCv1 */
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ cups->color_info.depth = cups->header.cupsBitsPerPixel;
+ cups->color_info.num_components = 1;
+ cups->color_info.dither_grays = 1L << cups->header.cupsBitsPerColor;
+ cups->color_info.dither_colors = 1L << cups->header.cupsBitsPerColor;
+ cups->color_info.max_gray = cups->color_info.dither_grays - 1;
+ cups->color_info.max_color = cups->color_info.dither_grays - 1;
+ break;
+
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+#ifdef CUPS_RASTER_SYNCv1
+ cups->header.cupsNumColors = 3;
+#endif /* CUPS_RASTER_SYNCv1 */
+ if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ else if (cups->header.cupsBitsPerColor < 8)
+ cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;
+ else
+ cups->header.cupsBitsPerPixel = 3 * cups->header.cupsBitsPerColor;
+
+ if (cups->header.cupsBitsPerColor < 8)
+ cups->color_info.depth = 4 * cups->header.cupsBitsPerColor;
+ else
+ cups->color_info.depth = 3 * cups->header.cupsBitsPerColor;
+
+ cups->color_info.num_components = 3;
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+#ifdef CUPS_RASTER_SYNCv1
+ cups->header.cupsNumColors = 6;
+#endif /* CUPS_RASTER_SYNCv1 */
+ cups->header.cupsBitsPerPixel = 8;
+ cups->color_info.depth = 8;
+ cups->color_info.num_components = 4;
+ break;
+ }
+
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+#ifdef CUPS_RASTER_SYNCv1
+ cups->header.cupsNumColors = 4;
+#endif /* CUPS_RASTER_SYNCv1 */
+ if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ else
+ cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;
+
+ cups->color_info.depth = 4 * cups->header.cupsBitsPerColor;
+ cups->color_info.num_components = 4;
+ break;
+
+#ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+ /*
+ * Colorimetric color spaces currently are implemented as 24-bit
+ * mapping to XYZ or Lab, which are then converted as needed to
+ * the final representation...
+ *
+ * This code enforces a minimum output depth of 8 bits per
+ * component...
+ */
+
+#ifdef CUPS_RASTER_SYNCv1
+ cups->header.cupsNumColors = 3;
+#endif /* CUPS_RASTER_SYNCv1 */
+
+ if (cups->header.cupsBitsPerColor < 8)
+ cups->header.cupsBitsPerColor = 8;
+
+ if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ else
+ cups->header.cupsBitsPerPixel = 3 * cups->header.cupsBitsPerColor;
+
+ cups->color_info.depth = 24;
+ cups->color_info.num_components = 3;
+ break;
+#endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ }
+
+#ifdef dev_t_proc_encode_color
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ cups->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
+ break;
+
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ case CUPS_CSPACE_KCMYcm :
+ case CUPS_CSPACE_KCMY :
+ cups->color_info.gray_index = 0;
+ break;
+
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ cups->color_info.gray_index = 3;
+ break;
+ }
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ case CUPS_CSPACE_RGBA :
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+# endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ cups->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ break;
+
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ case CUPS_CSPACE_KCMYcm :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ cups->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ break;
+ }
+
+ cups->color_info.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
+#endif /* dev_t_proc_encode_color */
+
+ i = cups->header.cupsBitsPerColor;
+ max_lut = (1 << i) - 1;
+
+ switch (cups->color_info.num_components)
+ {
+ default :
+ case 1 :
+ cups->color_info.max_gray = max_lut;
+ cups->color_info.max_color = 0;
+ cups->color_info.dither_grays = max_lut + 1;
+ cups->color_info.dither_colors = 0;
+ break;
+
+ case 3 :
+ cups->color_info.max_gray = 0;
+ cups->color_info.max_color = max_lut;
+ cups->color_info.dither_grays = 0;
+ cups->color_info.dither_colors = max_lut + 1;
+ break;
+
+ case 4 :
+ cups->color_info.max_gray = max_lut;
+ cups->color_info.max_color = max_lut;
+ cups->color_info.dither_grays = max_lut + 1;
+ cups->color_info.dither_colors = max_lut + 1;
+ break;
+ }
+
+ /*
+ * Enable/disable CMYK color support...
+ */
+
+#ifdef dev_t_proc_encode_color
+ cups->color_info.max_components = cups->color_info.num_components;
+#endif /* dev_t_proc_encode_color */
+
+ /*
+ * Tell Ghostscript to forget any colors it has cached...
+ */
+
+ gx_device_decache_colors(pdev);
+
+ /*
+ * Compute the lookup tables...
+ */
+
+ for (i = 0; i <= gx_max_color_value; i ++)
+ {
+ j = (max_lut * i + gx_max_color_value / 2) / gx_max_color_value;
+
+#if !ARCH_IS_BIG_ENDIAN
+ if (max_lut > 255)
+ j = ((j & 255) << 8) | ((j >> 8) & 255);
+#endif /* !ARCH_IS_BIG_ENDIAN */
+
+ cups->EncodeLUT[i] = j;
+
+#ifdef CUPS_DEBUG
+ if (i == 0 || cups->EncodeLUT[i] != cups->EncodeLUT[i - 1])
+ dmprintf2(pdev->memory, "DEBUG2: cups->EncodeLUT[%d] = %d\n", i,
+ (int)cups->EncodeLUT[i]);
+#endif /* CUPS_DEBUG */
+ }
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups->EncodeLUT[0] = %d\n", (int)cups->EncodeLUT[0]);
+ dmprintf2(pdev->memory, "DEBUG2: cups->EncodeLUT[%d] = %d\n", gx_max_color_value,
+ (int)cups->EncodeLUT[gx_max_color_value]);
+#endif /* CUPS_DEBUG */
+
+ for (i = 0; i < cups->color_info.dither_grays; i ++) {
+ j = i;
+#if !ARCH_IS_BIG_ENDIAN
+ if (max_lut > 255)
+ j = ((j & 255) << 8) | ((j >> 8) & 255);
+#endif /* !ARCH_IS_BIG_ENDIAN */
+ cups->DecodeLUT[i] = gx_max_color_value * j / max_lut;
+ }
+
+#ifdef CUPS_DEBUG
+ dmprintf2(pdev->memory, "DEBUG: num_components = %d, depth = %d\n",
+ cups->color_info.num_components, cups->color_info.depth);
+ dmprintf2(pdev->memory, "DEBUG: cupsColorSpace = %d, cupsColorOrder = %d\n",
+ cups->header.cupsColorSpace, cups->header.cupsColorOrder);
+ dmprintf2(pdev->memory, "DEBUG: cupsBitsPerPixel = %d, cupsBitsPerColor = %d\n",
+ cups->header.cupsBitsPerPixel, cups->header.cupsBitsPerColor);
+ dmprintf2(pdev->memory, "DEBUG: max_gray = %d, dither_grays = %d\n",
+ cups->color_info.max_gray, cups->color_info.dither_grays);
+ dmprintf2(pdev->memory, "DEBUG: max_color = %d, dither_colors = %d\n",
+ cups->color_info.max_color, cups->color_info.dither_colors);
+#endif /* CUPS_DEBUG */
+
+ /*
+ * Set the color profile as needed...
+ */
+
+ cups->HaveProfile = 0;
+
+#ifdef dev_t_proc_encode_color
+ if (cups->Profile)
+#else
+ if (cups->Profile && cups->header.cupsBitsPerColor == 8)
+#endif /* dev_t_proc_encode_color */
+ {
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG: Using user-defined profile \"%s\"...\n", cups->Profile);
+#endif /* CUPS_DEBUG */
+
+ if (sscanf(cups->Profile, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &d, &g,
+ m[0] + 0, m[0] + 1, m[0] + 2,
+ m[1] + 0, m[1] + 1, m[1] + 2,
+ m[2] + 0, m[2] + 1, m[2] + 2) != 11)
+ dmprintf(pdev->memory, "ERROR: User-defined profile does not contain 11 integers!\n");
+ else
+ {
+ cups->HaveProfile = 1;
+
+ d *= 0.001f;
+ g *= 0.001f;
+ m[0][0] *= 0.001f;
+ m[0][1] *= 0.001f;
+ m[0][2] *= 0.001f;
+ m[1][0] *= 0.001f;
+ m[1][1] *= 0.001f;
+ m[1][2] *= 0.001f;
+ m[2][0] *= 0.001f;
+ m[2][1] *= 0.001f;
+ m[2][2] *= 0.001f;
+ }
+ }
+#ifdef dev_t_proc_encode_color
+ else if (cups->PPD)
+#else
+ else if (cups->PPD && cups->header.cupsBitsPerColor == 8)
+#endif /* dev_t_proc_encode_color */
+ {
+ /*
+ * Find the appropriate color profile...
+ */
+
+ if (pdev->HWResolution[0] != pdev->HWResolution[1])
+ sprintf(resolution, "%.0fx%.0fdpi", pdev->HWResolution[0],
+ pdev->HWResolution[1]);
+ else
+ sprintf(resolution, "%.0fdpi", pdev->HWResolution[0]);
+
+ for (i = 0, profile = cups->PPD->profiles;
+ i < cups->PPD->num_profiles;
+ i ++, profile ++)
+ if ((strcmp(profile->resolution, resolution) == 0 ||
+ profile->resolution[0] == '-') &&
+ (strcmp(profile->media_type, cups->header.MediaType) == 0 ||
+ profile->media_type[0] == '-'))
+ break;
+
+ /*
+ * If we found a color profile, use it!
+ */
+
+ if (i < cups->PPD->num_profiles)
+ {
+#ifdef CUPS_DEBUG
+ dmprintf(pdev->memory, "DEBUG: Using color profile in PPD file!\n");
+#endif /* CUPS_DEBUG */
+
+ cups->HaveProfile = 1;
+
+ d = profile->density;
+ g = profile->gamma;
+
+ memcpy(m, profile->matrix, sizeof(m));
+ }
+ }
+
+ if (cups->HaveProfile)
+ {
+ for (i = 0; i < 3; i ++)
+ for (j = 0; j < 3; j ++)
+ for (k = 0; k <= CUPS_MAX_VALUE; k ++)
+ {
+ cups->Matrix[i][j][k] = (int)((float)k * m[i][j] + 0.5);
+
+#ifdef CUPS_DEBUG
+ if ((k & 4095) == 0)
+ dmprintf4(pdev->memory, "DEBUG2: cups->Matrix[%d][%d][%d] = %d\n",
+ i, j, k, cups->Matrix[i][j][k]);
+#endif /* CUPS_DEBUG */
+ }
+
+
+ for (k = 0; k <= CUPS_MAX_VALUE; k ++)
+ {
+ cups->Density[k] = (int)((float)CUPS_MAX_VALUE * d *
+ pow((float)k / (float)CUPS_MAX_VALUE, g) +
+ 0.5);
+
+#ifdef CUPS_DEBUG
+ if ((k & 4095) == 0)
+ dmprintf2(pdev->memory, "DEBUG2: cups->Density[%d] = %d\n", k, cups->Density[k]);
+#endif /* CUPS_DEBUG */
+ }
+ }
+ else
+ {
+ for (k = 0; k <= CUPS_MAX_VALUE; k ++)
+ cups->Density[k] = k;
+ }
+ if (!cups->user_icc) {
+ /* Set up the ICC profile for ghostscript to use based upon the color space.
+ This is different than the PPD profile above which appears to be some sort
+ of matrix based TRC profile */
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+# endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
+ if (!pdev->icc_struct || (pdev->icc_struct &&
+ pdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsRGB)) {
+
+ if (pdev->icc_struct) {
+ rc_decrement(pdev->icc_struct, "cups_set_color_info");
+ }
+ pdev->icc_struct = gsicc_new_device_profile_array(pdev->memory);
+
+ code = gsicc_set_device_profile(pdev, pdev->memory,
+ (char *)DEFAULT_RGB_ICC, gsDEFAULTPROFILE);
+ }
+ break;
+
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_SW :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ if (!pdev->icc_struct || (pdev->icc_struct &&
+ pdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsGRAY)) {
+
+ if (pdev->icc_struct) {
+ rc_decrement(pdev->icc_struct, "cups_set_color_info");
+ }
+ pdev->icc_struct = gsicc_new_device_profile_array(pdev->memory);
+
+ code = gsicc_set_device_profile(pdev, pdev->memory->non_gc_memory,
+ (char *)DEFAULT_GRAY_ICC, gsDEFAULTPROFILE);
+ }
+ break;
+ case CUPS_CSPACE_KCMYcm :
+# ifdef CUPS_RASTER_HAVE_COLORIMETRIC
+ case CUPS_CSPACE_CIEXYZ :
+#endif
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ if (!pdev->icc_struct || (pdev->icc_struct &&
+ pdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsCMYK)) {
+
+ if (pdev->icc_struct) {
+ rc_decrement(pdev->icc_struct, "cups_set_color_info");
+ }
+ pdev->icc_struct = gsicc_new_device_profile_array(pdev->memory);
+
+ code = gsicc_set_device_profile(pdev, pdev->memory,
+ (char *)DEFAULT_CMYK_ICC, gsDEFAULTPROFILE);
+ }
+ break;
+ }
+ }
+ return(code);
+}
+
+/*
+ * 'cups_sync_output()' - Keep the user informed of our status...
+ */
+
+private int /* O - Error status */
+cups_sync_output(gx_device *pdev) /* I - Device info */
+{
+ dmprintf1(pdev->memory, "INFO: Processing page %d...\n", cups->page);
+
+ return (0);
+}
+
+
+/*
+ * 'cups_print_chunked()' - Print a page of chunked pixels.
+ */
+
+static int
+cups_print_chunked(gx_device_printer *pdev,
+ /* I - Printer device */
+ unsigned char *src,
+ /* I - Scanline buffer */
+ unsigned char *dst,
+ /* I - Bitmap buffer */
+ int srcbytes)
+ /* I - Number of bytes in src */
+{
+ int y; /* Looping var */
+ unsigned char *srcptr, /* Pointer to data */
+ *dstptr; /* Pointer to bits */
+ int count; /* Count for loop */
+ int xflip, /* Flip scanline? */
+ yflip, /* Reverse scanline order? */
+ ystart, yend, ystep; /* Loop control for scanline order */
+ ppd_attr_t *backside = NULL;
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.Duplex = %d\n", cups->header.Duplex);
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.Tumble = %d\n", cups->header.Tumble);
+ dmprintf1(pdev->memory, "DEBUG2: cups->page = %d\n", cups->page);
+ dmprintf1(pdev->memory, "DEBUG2: cups->PPD = %p\n", cups->PPD);
+#endif /* CUPS_DEBUG */
+
+ if (cups->PPD) {
+ backside = ppdFindAttr(cups->PPD, "cupsBackSide", NULL);
+ if (backside) {
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cupsBackSide = %s\n", backside->value);
+#endif /* CUPS_DEBUG */
+ cups->PPD->flip_duplex = 0;
+ }
+ }
+ if (cups->header.Duplex && cups->PPD &&
+ ((!cups->header.Tumble &&
+ (cups->PPD->flip_duplex ||
+ (backside && !strcasecmp(backside->value, "Rotated")))) ||
+ (cups->header.Tumble &&
+ (backside && (!strcasecmp(backside->value, "Flipped") ||
+ !strcasecmp(backside->value, "ManualTumble"))))) &&
+ !(cups->page & 1))
+ xflip = 1;
+ else
+ xflip = 0;
+ if (cups->header.Duplex && cups->PPD &&
+ ((!cups->header.Tumble &&
+ (cups->PPD->flip_duplex ||
+ (backside && (!strcasecmp(backside->value, "Flipped") ||
+ !strcasecmp(backside->value, "Rotated"))))) ||
+ (cups->header.Tumble &&
+ (backside && !strcasecmp(backside->value, "ManualTumble")))) &&
+ !(cups->page & 1)) {
+ yflip = 1;
+ ystart = cups->height - 1;
+ yend = -1;
+ ystep = -1;
+ } else {
+ yflip = 0;
+ ystart = 0;
+ yend = cups->height;
+ ystep = 1;
+ }
+
+#ifdef CUPS_DEBUG
+ dmprintf3(pdev->memory, "DEBUG: cups_print_chunked: xflip = %d, yflip = %d, height = %d\n",
+ xflip, yflip, cups->height);
+#endif /* CUPS_DEBUG */
+
+ /*
+ * Loop through the page bitmap and write chunked pixels, reversing as
+ * needed...
+ */
+ for (y = ystart; y != yend; y += ystep)
+ {
+ /*
+ * Grab the scanline data...
+ */
+
+ if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
+ {
+ dmprintf1(pdev->memory, "ERROR: Unable to get scanline %d!\n", y);
+ return_error(gs_error_unknownerror);
+ }
+
+ if (xflip)
+ {
+ /*
+ * Flip the raster data before writing it...
+ */
+
+ if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+ else
+ {
+ dstptr = dst;
+ count = srcbytes;
+
+ switch (cups->color_info.depth)
+ {
+ case 1 : /* B&W bitmap */
+ for (srcptr += srcbytes - 1;
+ count > 0;
+ count --, srcptr --, dstptr ++)
+ {
+ *dstptr = cups->RevUpper1[*srcptr & 15] |
+ cups->RevLower1[*srcptr >> 4];
+ }
+ break;
+
+ case 2 : /* 2-bit W/K image */
+ for (srcptr += srcbytes - 1;
+ count > 0;
+ count --, srcptr --, dstptr ++)
+ {
+ *dstptr = cups->RevUpper2[*srcptr & 15] |
+ cups->RevLower2[*srcptr >> 4];
+ }
+ break;
+
+ case 4 : /* 1-bit RGB/CMY/CMYK bitmap or 4-bit W/K image */
+ for (srcptr += srcbytes - 1;
+ count > 0;
+ count --, srcptr --, dstptr ++)
+ *dstptr = (*srcptr >> 4) | (*srcptr << 4);
+ break;
+
+ case 8 : /* 2-bit RGB/CMY/CMYK or 8-bit W/K image */
+ for (srcptr += srcbytes - 1;
+ count > 0;
+ count --, srcptr --, dstptr ++)
+ *dstptr = *srcptr;
+ break;
+
+ case 16 : /* 4-bit RGB/CMY/CMYK or 16-bit W/K image */
+ for (srcptr += srcbytes - 2;
+ count > 0;
+ count -= 2, srcptr -= 2, dstptr += 2)
+ {
+ dstptr[0] = srcptr[0];
+ dstptr[1] = srcptr[1];
+ }
+ break;
+
+ case 24 : /* 8-bit RGB or CMY image */
+ for (srcptr += srcbytes - 3;
+ count > 0;
+ count -= 3, srcptr -= 3, dstptr += 3)
+ {
+ dstptr[0] = srcptr[0];
+ dstptr[1] = srcptr[1];
+ dstptr[2] = srcptr[2];
+ }
+ break;
+
+ case 32 : /* 8-bit CMYK image */
+ for (srcptr += srcbytes - 4;
+ count > 0;
+ count -= 4, srcptr -= 4, dstptr += 4)
+ {
+ dstptr[0] = srcptr[0];
+ dstptr[1] = srcptr[1];
+ dstptr[2] = srcptr[2];
+ dstptr[3] = srcptr[3];
+ }
+ break;
+
+ case 48 : /* 16-bit RGB or CMY image */
+ for (srcptr += srcbytes - 6;
+ count > 0;
+ count -= 6, srcptr -= 6, dstptr += 6)
+ {
+ dstptr[0] = srcptr[0];
+ dstptr[1] = srcptr[1];
+ dstptr[2] = srcptr[2];
+ dstptr[3] = srcptr[3];
+ dstptr[4] = srcptr[4];
+ dstptr[5] = srcptr[5];
+ }
+ break;
+
+ case 64 : /* 16-bit CMYK image */
+ for (srcptr += srcbytes - 8;
+ count > 0;
+ count -= 8, srcptr -= 8, dstptr += 8)
+ {
+ dstptr[0] = srcptr[0];
+ dstptr[1] = srcptr[1];
+ dstptr[2] = srcptr[2];
+ dstptr[3] = srcptr[3];
+ dstptr[4] = srcptr[4];
+ dstptr[5] = srcptr[5];
+ dstptr[6] = srcptr[6];
+ dstptr[7] = srcptr[7];
+ }
+ break;
+ }
+ }
+
+ /*
+ * Write the bitmap data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
+ }
+ else
+ {
+ /*
+ * Write the scanline data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, srcptr, cups->header.cupsBytesPerLine);
+ }
+ }
+ return (0);
+}
+
+
+/*
+ * 'cups_print_banded()' - Print a page of banded pixels.
+ */
+
+static int
+cups_print_banded(gx_device_printer *pdev,
+ /* I - Printer device */
+ unsigned char *src,
+ /* I - Scanline buffer */
+ unsigned char *dst,
+ /* I - Bitmap buffer */
+ int srcbytes)
+ /* I - Number of bytes in src */
+{
+ int x; /* Looping var */
+ int y; /* Looping var */
+ int bandbytes; /* Bytes per band */
+ unsigned char bit; /* Current bit */
+ unsigned char temp; /* Temporary variable */
+ unsigned char *srcptr; /* Pointer to data */
+ unsigned char *cptr, *mptr, *yptr, /* Pointer to components */
+ *kptr, *lcptr, *lmptr; /* ... */
+ int xflip, /* Flip scanline? */
+ yflip, /* Reverse scanline order? */
+ ystart, yend, ystep; /* Loop control for scanline order */
+ ppd_attr_t *backside = NULL;
+
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.Duplex = %d\n", cups->header.Duplex);
+ dmprintf1(pdev->memory, "DEBUG2: cups->header.Tumble = %d\n", cups->header.Tumble);
+ dmprintf1(pdev->memory, "DEBUG2: cups->page = %d\n", cups->page);
+ dmprintf1(pdev->memory, "DEBUG2: cups->PPD = %p\n", cups->PPD);
+#endif /* CUPS_DEBUG */
+
+ if (cups->PPD) {
+ backside = ppdFindAttr(cups->PPD, "cupsBackSide", NULL);
+ if (backside) {
+#ifdef CUPS_DEBUG
+ dmprintf1(pdev->memory, "DEBUG2: cupsBackSide = %s\n", backside->value);
+#endif /* CUPS_DEBUG */
+ cups->PPD->flip_duplex = 0;
+ }
+ }
+ if (cups->header.Duplex && cups->PPD &&
+ ((!cups->header.Tumble &&
+ (cups->PPD->flip_duplex ||
+ (backside && !strcasecmp(backside->value, "Rotated")))) ||
+ (cups->header.Tumble &&
+ (backside && (!strcasecmp(backside->value, "Flipped") ||
+ !strcasecmp(backside->value, "ManualTumble"))))) &&
+ !(cups->page & 1))
+ xflip = 1;
+ else
+ xflip = 0;
+ if (cups->header.Duplex && cups->PPD &&
+ ((!cups->header.Tumble &&
+ (cups->PPD->flip_duplex ||
+ (backside && (!strcasecmp(backside->value, "Flipped") ||
+ !strcasecmp(backside->value, "Rotated"))))) ||
+ (cups->header.Tumble &&
+ (backside && !strcasecmp(backside->value, "ManualTumble")))) &&
+ !(cups->page & 1)) {
+ yflip = 1;
+ ystart = cups->height - 1;
+ yend = -1;
+ ystep = -1;
+ } else {
+ yflip = 0;
+ ystart = 0;
+ yend = cups->height;
+ ystep = 1;
+ }
+
+#ifdef CUPS_DEBUG
+ dmprintf3(pdev->memory, "DEBUG: cups_print_chunked: xflip = %d, yflip = %d, height = %d\n",
+ xflip, yflip, cups->height);
+#endif /* CUPS_DEBUG */
+
+ /*
+ * Loop through the page bitmap and write banded pixels... We have
+ * to separate each chunked color as needed...
+ */
+
+#ifdef CUPS_RASTER_SYNCv1
+ bandbytes = cups->header.cupsBytesPerLine / cups->header.cupsNumColors;
+#else
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
+ cups->header.cupsBitsPerColor == 1)
+ bandbytes = cups->header.cupsBytesPerLine / 6;
+ else
+ bandbytes = cups->header.cupsBytesPerLine / cups->color_info.num_components;
+#endif /* CUPS_RASTER_SYNCv1 */
+
+ for (y = ystart; y != yend; y += ystep)
+ {
+ /*
+ * Grab the scanline data...
+ */
+
+ if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
+ {
+ dmprintf1(pdev->memory, "ERROR: Unable to get scanline %d!\n", y);
+ return_error(gs_error_unknownerror);
+ }
+
+ /*
+ * Separate the chunked colors into their components...
+ */
+
+ if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+ else
+ {
+ if (xflip)
+ cptr = dst + bandbytes - 1;
+ else
+ cptr = dst;
+
+ mptr = cptr + bandbytes;
+ yptr = mptr + bandbytes;
+ kptr = yptr + bandbytes;
+ lcptr = kptr + bandbytes;
+ lmptr = lcptr + bandbytes;
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ for (x = cups->width, bit = xflip ? 1 << (x & 7) : 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & 0x40)
+ *cptr |= bit;
+ if (*srcptr & 0x20)
+ *mptr |= bit;
+ if (*srcptr & 0x10)
+ *yptr |= bit;
+
+ if (xflip)
+ {
+ if (bit < 128)
+ bit <<= 1;
+ else
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ bit = 1;
+ }
+ }
+ else
+ bit >>= 1;
+
+ x --;
+ if (x == 0)
+ break;
+
+ if (*srcptr & 0x4)
+ *cptr |= bit;
+ if (*srcptr & 0x2)
+ *mptr |= bit;
+ if (*srcptr & 0x1)
+ *yptr |= bit;
+
+ if (xflip)
+ {
+ if (bit < 128)
+ bit <<= 1;
+ else
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ bit = 1;
+ }
+ }
+ else if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ bit = 128;
+ }
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ for (x = cups->width, bit = xflip ? 1 << (x & 7) : 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & 0x80)
+ *cptr |= bit;
+ if (*srcptr & 0x40)
+ *mptr |= bit;
+ if (*srcptr & 0x20)
+ *yptr |= bit;
+ if (*srcptr & 0x10)
+ *kptr |= bit;
+
+ if (xflip)
+ {
+ if (bit < 128)
+ bit <<= 1;
+ else
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ kptr --;
+ bit = 1;
+ }
+ }
+ else
+ bit >>= 1;
+
+ x --;
+ if (x == 0)
+ break;
+
+ if (*srcptr & 0x8)
+ *cptr |= bit;
+ if (*srcptr & 0x4)
+ *mptr |= bit;
+ if (*srcptr & 0x2)
+ *yptr |= bit;
+ if (*srcptr & 0x1)
+ *kptr |= bit;
+
+ if (xflip)
+ {
+ if (bit < 128)
+ bit <<= 1;
+ else
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ kptr --;
+ bit = 1;
+ }
+ }
+ else if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ bit = 128;
+ }
+ }
+ break;
+ case CUPS_CSPACE_KCMYcm :
+ for (x = cups->width, bit = xflip ? 1 << (x & 7) : 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ /*
+ * Note: Because of the way the pointers are setup,
+ * the following code is correct even though
+ * the names don't match...
+ */
+
+ if (*srcptr & 0x20)
+ *cptr |= bit;
+ if (*srcptr & 0x10)
+ *mptr |= bit;
+ if (*srcptr & 0x08)
+ *yptr |= bit;
+ if (*srcptr & 0x04)
+ *kptr |= bit;
+ if (*srcptr & 0x02)
+ *lcptr |= bit;
+ if (*srcptr & 0x01)
+ *lmptr |= bit;
+
+ if (xflip)
+ {
+ if (bit < 128)
+ bit <<= 1;
+ else
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ kptr --;
+ lcptr --;
+ lmptr --;
+ bit = 1;
+ }
+ }
+ else if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ lcptr ++;
+ lmptr ++;
+ bit = 128;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ for (x = cups->width, bit = xflip ? 3 << (2 * (x & 3)) : 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ switch (bit)
+ {
+ case 0xc0 :
+ if ((temp = *srcptr & 0x30) != 0)
+ *cptr |= temp << 2;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *mptr |= temp << 4;
+ if ((temp = *srcptr & 0x03) != 0)
+ *yptr |= temp << 6;
+
+ if (xflip)
+ {
+ bit = 0x03;
+ cptr --;
+ mptr --;
+ yptr --;
+ }
+ else
+ bit = 0x30;
+ break;
+ case 0x30 :
+ if ((temp = *srcptr & 0x30) != 0)
+ *cptr |= temp;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *mptr |= temp << 2;
+ if ((temp = *srcptr & 0x03) != 0)
+ *yptr |= temp << 4;
+
+ if (xflip)
+ bit = 0xc0;
+ else
+ bit = 0x0c;
+ break;
+ case 0x0c :
+ if ((temp = *srcptr & 0x30) != 0)
+ *cptr |= temp >> 2;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *mptr |= temp;
+ if ((temp = *srcptr & 0x03) != 0)
+ *yptr |= temp << 2;
+
+ if (xflip)
+ bit = 0x30;
+ else
+ bit = 0x03;
+ break;
+ case 0x03 :
+ if ((temp = *srcptr & 0x30) != 0)
+ *cptr |= temp >> 4;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *mptr |= temp >> 2;
+ if ((temp = *srcptr & 0x03) != 0)
+ *yptr |= temp;
+
+ if (xflip)
+ bit = 0x0c;
+ else
+ {
+ bit = 0xc0;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ break;
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ for (x = cups->width, bit = xflip ? 3 << (2 * (x & 3)) : 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ switch (bit)
+ {
+ case 0xc0 :
+ if ((temp = *srcptr & 0xc0) != 0)
+ *cptr |= temp;
+ if ((temp = *srcptr & 0x30) != 0)
+ *mptr |= temp << 2;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *yptr |= temp << 4;
+ if ((temp = *srcptr & 0x03) != 0)
+ *kptr |= temp << 6;
+
+ if (xflip)
+ {
+ bit = 0x03;
+ cptr --;
+ mptr --;
+ yptr --;
+ kptr --;
+ }
+ else
+ bit = 0x30;
+ break;
+ case 0x30 :
+ if ((temp = *srcptr & 0xc0) != 0)
+ *cptr |= temp >> 2;
+ if ((temp = *srcptr & 0x30) != 0)
+ *mptr |= temp;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *yptr |= temp << 2;
+ if ((temp = *srcptr & 0x03) != 0)
+ *kptr |= temp << 4;
+
+ if (xflip)
+ bit = 0xc0;
+ else
+ bit = 0x0c;
+ break;
+ case 0x0c :
+ if ((temp = *srcptr & 0xc0) != 0)
+ *cptr |= temp >> 4;
+ if ((temp = *srcptr & 0x30) != 0)
+ *mptr |= temp >> 2;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *yptr |= temp;
+ if ((temp = *srcptr & 0x03) != 0)
+ *kptr |= temp << 2;
+
+ if (xflip)
+ bit = 0x30;
+ else
+ bit = 0x03;
+ break;
+ case 0x03 :
+ if ((temp = *srcptr & 0xc0) != 0)
+ *cptr |= temp >> 6;
+ if ((temp = *srcptr & 0x30) != 0)
+ *mptr |= temp >> 4;
+ if ((temp = *srcptr & 0x0c) != 0)
+ *yptr |= temp >> 2;
+ if ((temp = *srcptr & 0x03) != 0)
+ *kptr |= temp;
+
+ if (xflip)
+ bit = 0x0c;
+ else
+ {
+ bit = 0xc0;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 4 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ for (x = cups->width, bit = xflip && (x & 1) ? 0xf0 : 0x0f;
+ x > 0;
+ x --, srcptr += 2)
+ switch (bit)
+ {
+ case 0xf0 :
+ if ((temp = srcptr[0] & 0x0f) != 0)
+ *cptr |= temp << 4;
+ if ((temp = srcptr[1] & 0xf0) != 0)
+ *mptr |= temp;
+ if ((temp = srcptr[1] & 0x0f) != 0)
+ *yptr |= temp << 4;
+
+ bit = 0x0f;
+
+ if (xflip)
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ }
+ break;
+ case 0x0f :
+ if ((temp = srcptr[0] & 0x0f) != 0)
+ *cptr |= temp;
+ if ((temp = srcptr[1] & 0xf0) != 0)
+ *mptr |= temp >> 4;
+ if ((temp = srcptr[1] & 0x0f) != 0)
+ *yptr |= temp;
+
+ bit = 0xf0;
+
+ if (!xflip)
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ break;
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ for (x = cups->width, bit = xflip && (x & 1) ? 0xf0 : 0x0f;
+ x > 0;
+ x --, srcptr += 2)
+ switch (bit)
+ {
+ case 0xf0 :
+ if ((temp = srcptr[0] & 0xf0) != 0)
+ *cptr |= temp;
+ if ((temp = srcptr[0] & 0x0f) != 0)
+ *mptr |= temp << 4;
+ if ((temp = srcptr[1] & 0xf0) != 0)
+ *yptr |= temp;
+ if ((temp = srcptr[1] & 0x0f) != 0)
+ *kptr |= temp << 4;
+
+ bit = 0x0f;
+
+ if (xflip)
+ {
+ cptr --;
+ mptr --;
+ yptr --;
+ kptr --;
+ }
+ break;
+ case 0x0f :
+ if ((temp = srcptr[0] & 0xf0) != 0)
+ *cptr |= temp >> 4;
+ if ((temp = srcptr[0] & 0x0f) != 0)
+ *mptr |= temp;
+ if ((temp = srcptr[1] & 0xf0) != 0)
+ *yptr |= temp >> 4;
+ if ((temp = srcptr[1] & 0x0f) != 0)
+ *kptr |= temp;
+
+ bit = 0xf0;
+
+ if (!xflip)
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 8 :
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ if (xflip)
+ for (x = cups->width; x > 0; x --)
+ {
+ *cptr-- = *srcptr++;
+ *mptr-- = *srcptr++;
+ *yptr-- = *srcptr++;
+ }
+ else
+ for (x = cups->width; x > 0; x --)
+ {
+ *cptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ if (xflip)
+ for (x = cups->width; x > 0; x --)
+ {
+ *cptr-- = *srcptr++;
+ *mptr-- = *srcptr++;
+ *yptr-- = *srcptr++;
+ *kptr-- = *srcptr++;
+ }
+ else
+ for (x = cups->width; x > 0; x --)
+ {
+ *cptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ *kptr++ = *srcptr++;
+ }
+ break;
+ }
+ break;
+
+ case 16 :
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ if (xflip)
+ for (x = cups->width; x > 0; x --, srcptr += 6)
+ {
+ *cptr-- = srcptr[1];
+ *cptr-- = srcptr[0];
+ *mptr-- = srcptr[3];
+ *mptr-- = srcptr[2];
+ *yptr-- = srcptr[5];
+ *yptr-- = srcptr[4];
+ }
+ else
+ for (x = cups->width; x > 0; x --)
+ {
+ *cptr++ = *srcptr++;
+ *cptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ if (xflip)
+ for (x = cups->width; x > 0; x --, srcptr += 8)
+ {
+ *cptr-- = srcptr[1];
+ *cptr-- = srcptr[0];
+ *mptr-- = srcptr[3];
+ *mptr-- = srcptr[2];
+ *yptr-- = srcptr[5];
+ *yptr-- = srcptr[4];
+ *kptr-- = srcptr[7];
+ *kptr-- = srcptr[6];
+ }
+ else
+ for (x = cups->width; x > 0; x --)
+ {
+ *cptr++ = *srcptr++;
+ *cptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ *kptr++ = *srcptr++;
+ *kptr++ = *srcptr++;
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Write the bitmap data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
+ }
+ return (0);
+}
+
+
+/*
+ * 'cups_print_planar()' - Print a page of planar pixels.
+ */
+
+static int
+cups_print_planar(gx_device_printer *pdev,
+ /* I - Printer device */
+ unsigned char *src,
+ /* I - Scanline buffer */
+ unsigned char *dst,
+ /* I - Bitmap buffer */
+ int srcbytes)
+ /* I - Number of bytes in src */
+{
+ int x; /* Looping var */
+ int y; /* Looping var */
+ int z; /* Looping var */
+ unsigned char srcbit; /* Current source bit */
+ unsigned char dstbit; /* Current destination bit */
+ unsigned char temp; /* Temporary variable */
+ unsigned char *srcptr; /* Pointer to data */
+ unsigned char *dstptr; /* Pointer to bitmap */
+
+
+ /**** NOTE: Currently planar output doesn't support flipped duplex!!! ****/
+
+ /*
+ * Loop through the page bitmap and write planar pixels... We have
+ * to separate each chunked color as needed...
+ */
+
+ for (z = 0; z < pdev->color_info.num_components; z ++)
+ for (y = 0; y < cups->height; y ++)
+ {
+ /*
+ * Grab the scanline data...
+ */
+
+ if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
+ {
+ dmprintf1(pdev->memory, "ERROR: Unable to get scanline %d!\n", y);
+ return_error(gs_error_unknownerror);
+ }
+
+ /*
+ * Pull the individual color planes out of the pixels...
+ */
+
+ if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+ else
+ switch (cups->header.cupsBitsPerColor)
+ {
+ default :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ for (dstptr = dst, x = cups->width, srcbit = 64 >> z,
+ dstbit = 128;
+ x > 0;
+ x --)
+ {
+ if (*srcptr & srcbit)
+ *dstptr |= dstbit;
+
+ if (srcbit >= 16)
+ srcbit >>= 4;
+ else
+ {
+ srcbit = 64 >> z;
+ srcptr ++;
+ }
+
+ if (dstbit > 1)
+ dstbit >>= 1;
+ else
+ {
+ dstbit = 128;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ for (dstptr = dst, x = cups->width, srcbit = 128 >> z,
+ dstbit = 128;
+ x > 0;
+ x --)
+ {
+ if (*srcptr & srcbit)
+ *dstptr |= dstbit;
+
+ if (srcbit >= 16)
+ srcbit >>= 4;
+ else
+ {
+ srcbit = 128 >> z;
+ srcptr ++;
+ }
+
+ if (dstbit > 1)
+ dstbit >>= 1;
+ else
+ {
+ dstbit = 128;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_KCMYcm :
+ for (dstptr = dst, x = cups->width, srcbit = 32 >> z,
+ dstbit = 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & srcbit)
+ *dstptr |= dstbit;
+
+ if (dstbit > 1)
+ dstbit >>= 1;
+ else
+ {
+ dstbit = 128;
+ dstptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ for (dstptr = dst, x = cups->width, srcbit = 48 >> (z * 2),
+ dstbit = 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if ((temp = *srcptr & srcbit) != 0)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ switch (srcbit)
+ {
+ case 0x30 :
+ temp >>= 4;
+ break;
+ case 0x0c :
+ temp >>= 2;
+ break;
+ }
+
+ switch (dstbit)
+ {
+ case 0xc0 :
+ *dstptr |= temp << 6;
+ break;
+ case 0x30 :
+ *dstptr |= temp << 4;
+ break;
+ case 0x0c :
+ *dstptr |= temp << 2;
+ break;
+ case 0x03 :
+ *dstptr |= temp;
+ break;
+ }
+ }
+ }
+
+ if (dstbit > 0x03)
+ dstbit >>= 2;
+ else
+ {
+ dstbit = 0xc0;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ for (dstptr = dst, x = cups->width, srcbit = 192 >> (z * 2),
+ dstbit = 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if ((temp = *srcptr & srcbit) != 0)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ switch (srcbit)
+ {
+ case 0xc0 :
+ temp >>= 6;
+ break;
+ case 0x30 :
+ temp >>= 4;
+ break;
+ case 0x0c :
+ temp >>= 2;
+ break;
+ }
+
+ switch (dstbit)
+ {
+ case 0xc0 :
+ *dstptr |= temp << 6;
+ break;
+ case 0x30 :
+ *dstptr |= temp << 4;
+ break;
+ case 0x0c :
+ *dstptr |= temp << 2;
+ break;
+ case 0x03 :
+ *dstptr |= temp;
+ break;
+ }
+ }
+ }
+
+ if (dstbit > 0x03)
+ dstbit >>= 2;
+ else
+ {
+ dstbit = 0xc0;
+ dstptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 4 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ if (z > 0)
+ srcptr ++;
+
+ if (z == 1)
+ srcbit = 0xf0;
+ else
+ srcbit = 0x0f;
+
+ for (dstptr = dst, x = cups->width, dstbit = 0xf0;
+ x > 0;
+ x --, srcptr += 2)
+ {
+ if ((temp = *srcptr & srcbit) != 0)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ if (srcbit == 0xf0)
+ temp >>= 4;
+
+ if (dstbit == 0xf0)
+ *dstptr |= temp << 4;
+ else
+ *dstptr |= temp;
+ }
+ }
+
+ if (dstbit == 0xf0)
+ dstbit = 0x0f;
+ else
+ {
+ dstbit = 0xf0;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ if (z > 1)
+ srcptr ++;
+
+ if (z & 1)
+ srcbit = 0x0f;
+ else
+ srcbit = 0xf0;
+
+ for (dstptr = dst, x = cups->width, dstbit = 0xf0;
+ x > 0;
+ x --, srcptr += 2)
+ {
+ if ((temp = *srcptr & srcbit) != 0)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ if (srcbit == 0xf0)
+ temp >>= 4;
+
+ if (dstbit == 0xf0)
+ *dstptr |= temp << 4;
+ else
+ *dstptr |= temp;
+ }
+ }
+
+ if (dstbit == 0xf0)
+ dstbit = 0x0f;
+ else
+ {
+ dstbit = 0xf0;
+ dstptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 8 :
+ for (srcptr += z, dstptr = dst, x = cups->header.cupsBytesPerLine;
+ x > 0;
+ srcptr += pdev->color_info.num_components, x --)
+ *dstptr++ = *srcptr;
+ break;
+
+ case 16 :
+ for (srcptr += 2 * z, dstptr = dst, x = cups->header.cupsBytesPerLine;
+ x > 0;
+ srcptr += 2 * pdev->color_info.num_components, x --)
+ {
+ *dstptr++ = srcptr[0];
+ *dstptr++ = srcptr[1];
+ }
+ break;
+ }
+
+ /*
+ * Write the bitmap data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
+ }
+ return (0);
+}
+
+
+/*
+ */
diff --git a/cups/libs/Makedefs b/cups/libs/Makedefs
new file mode 100644
index 000000000..1b25863e5
--- /dev/null
+++ b/cups/libs/Makedefs
@@ -0,0 +1,293 @@
+#
+# "$Id: Makedefs.in 9120 2010-04-23 18:56:34Z mike $"
+#
+# Common makefile definitions for CUPS.
+#
+# Copyright 2007-2010 by Apple Inc.
+# Copyright 1997-2007 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Apple Inc. and are protected by Federal copyright
+# law. Distribution and use rights are outlined in the file "LICENSE.txt"
+# which should have been included with this file. If this file is
+# file is missing or damaged, see the license at "http://www.cups.org/".
+#
+
+#
+# Programs...
+#
+
+AR = /usr/bin/ar
+AWK = gawk
+CC = gcc
+CHMOD = /bin/chmod
+CXX = g++
+DSO = $(CC)
+DSOXX = $(CXX)
+HTMLDOC =
+INSTALL = /home/till/ubuntu/cups/bzr/tmp/cups-1.4.5/install-sh
+LD = /usr/bin/ld
+LIBTOOL =
+LN = /bin/ln -sf
+MV = /bin/mv
+PHPCONFIG =
+RANLIB = ranlib
+RM = /bin/rm -f
+RMDIR = /bin/rmdir
+SED = /bin/sed
+SHELL = /bin/sh
+
+#
+# Installation programs...
+#
+
+INSTALL_BIN = $(LIBTOOL) $(INSTALL) -c -m 555 -s
+INSTALL_CONFIG = $(INSTALL) -c -m 640
+INSTALL_DATA = $(INSTALL) -c -m 444
+INSTALL_DIR = $(INSTALL) -d
+INSTALL_LIB = $(LIBTOOL) $(INSTALL) -c -m 555 -s
+INSTALL_MAN = $(INSTALL) -c -m 444
+INSTALL_SCRIPT = $(INSTALL) -c -m 555
+
+#
+# Default user, group, and system groups for the scheduler...
+#
+
+CUPS_USER = lp
+CUPS_GROUP = lp
+CUPS_SYSTEM_GROUPS = lpadmin sys root
+CUPS_PRIMARY_SYSTEM_GROUP = lpadmin
+
+#
+# Default permissions...
+#
+
+CUPS_CONFIG_FILE_PERM = 640
+CUPS_LOG_FILE_PERM = 644
+
+#
+# Languages to install...
+#
+
+LANGUAGES = da de es eu fi fr id it ja ko nl no pl pt pt_BR ru sv zh zh_TW
+INSTALL_LANGUAGES = install-languages
+UNINSTALL_LANGUAGES = uninstall-languages
+
+#
+# Libraries...
+#
+
+LIBCUPS = libcups.so.2
+LIBCUPSCGI = libcupscgi.so.1
+LIBCUPSDRIVER = libcupsdriver.so.1
+LIBCUPSIMAGE = libcupsimage.so.2
+LIBCUPSMIME = libcupsmime.so.1
+LIBCUPSPPDC = libcupsppdc.so.1
+LIBJPEG = -ljpeg
+LIBLDAP =
+LIBMALLOC =
+LIBPAPER =
+LIBPNG = -lpng
+LIBSLP =
+LIBGSSAPI =
+LIBTIFF = -ltiff
+LIBUSB = -lusb
+LIBWRAP =
+LIBZ = -lz
+
+#
+# Install static libraries?
+#
+
+INSTALLSTATIC = installstatic
+
+#
+# Program options...
+#
+# ARCHFLAGS Defines the default architecture build options.
+# ARCH32FLAGS Defines the 32-bit architecture build options, used
+# when compiling separate 32/64-bit libraries.
+# ARCH64FLAGS Defines the 64-bit architecture build options, used
+# when compiling separate 32/64-bit libraries.
+# OPTIM Defines the common compiler optimization/debugging options
+# for all architectures.
+# OPTIONS Defines other compile-time options (currently only -DDEBUG
+# for extra debug info)
+#
+
+ALL_CFLAGS = -I.. -D_CUPS_SOURCE $(CFLAGS) $(SSLFLAGS) \
+ -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_THREAD_SAFE -D_REENTRANT $(OPTIONS)
+ALL_CXXFLAGS = -I.. -D_CUPS_SOURCE $(CXXFLAGS) $(SSLFLAGS) \
+ -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_THREAD_SAFE -D_REENTRANT $(OPTIONS)
+ARCHFLAGS =
+ARFLAGS = crvs
+BACKLIBS =
+BANNERTOPS = bannertops
+CFLAGS =
+COMMONLIBS = -lpthread -lm -lcrypt
+CUPSDLIBS =
+CXXFLAGS =
+CXXLIBS =
+DBUS_NOTIFIER =
+DBUS_NOTIFIERLIBS =
+DNSSD_BACKEND =
+DSOFLAGS = -L../cups -Wl,-soname,`basename $@` -shared $(OPTIM)
+DSOLIBS = $(LIBTIFF) $(LIBPNG) $(LIBJPEG) $(LIBZ) $(COMMONLIBS)
+DNSSDLIBS =
+FONTS = fonts
+IMGLIBS =
+IMGFILTERS = imagetops imagetoraster
+LAUNCHDLIBS =
+LDFLAGS = -L../cgi-bin -L../cups -L../filter -L../ppdc \
+ -L../scheduler \
+ -pie -fPIE $(OPTIM)
+LEGACY_BACKENDS = parallel scsi
+LIBCUPSORDER = libcups.order
+LIBCUPSIMAGEORDER = libcupsimage.order
+LINKCUPS = -lcups $(SSLLIBS) $(DNSSDLIBS)
+LINKCUPSIMAGE = -lcupsimage
+LIBS = $(LINKCUPS) $(COMMONLIBS)
+OPTIM = -Wall -Wno-format-y2k -fPIC -Os -g -fstack-protector -D_GNU_SOURCE
+OPTIONS =
+PAMLIBS =
+PAP =
+PDFTOPS = pdftops
+PHPDIR =
+PHPOPTIONS = -I../.. `$(PHPCONFIG) --includes`
+SSLFLAGS =
+SSLLIBS =
+TEXTTOPS = texttops
+UNITTESTS =
+
+
+#
+# Separate 32/64-bit library support...
+#
+
+ARCH32FLAGS =
+DSO32FLAGS = -Wl,-soname,`basename $@` -shared $(OPTIM)
+INSTALL32 =
+LIB32CUPS =
+LIB32CUPSIMAGE =
+LIB32DIR = $(BUILDROOT)
+UNINSTALL32 =
+
+ARCH64FLAGS =
+DSO64FLAGS = -Wl,-soname,`basename $@` -shared $(OPTIM)
+INSTALL64 =
+LIB64CUPS =
+LIB64CUPSIMAGE =
+LIB64DIR = $(BUILDROOT)
+UNINSTALL64 =
+
+#
+# Directories...
+#
+# The first section uses the GNU names (which are *extremely*
+# difficult to find in a makefile because they are lowercase...)
+# We have to define these first because autoconf uses ${prefix}
+# and ${exec_prefix} for most of the other directories...
+#
+# The "datarootdir" variable may not get defined if you are using
+# a version of autoconf prior to 2.60.
+#
+# This is immediately followed by definition in ALL CAPS for the
+# needed directories...
+#
+
+bindir = /usr/bin
+datadir = /usr/share
+datarootdir = /usr/share
+exec_prefix = /usr
+includedir = /usr/include
+infodir = ${datarootdir}/info
+libdir = /usr/lib64
+libexecdir = ${exec_prefix}/libexec
+localstatedir = /var
+mandir = /usr/share/man
+oldincludedir = /usr/include
+prefix = /
+sbindir = /usr/sbin
+sharedstatedir = /usr/com
+srcdir = .
+sysconfdir = /etc
+top_srcdir = .
+
+BUILDROOT = $(DSTROOT)
+
+AMANDIR = $(BUILDROOT)/usr/share/man
+BINDIR = $(BUILDROOT)/usr/bin
+CACHEDIR = $(BUILDROOT)/var/cache/cups
+DATADIR = $(BUILDROOT)/usr/share/cups
+DOCDIR = $(BUILDROOT)/usr/share/doc/cups
+ICONDIR = /usr/share/icons
+INCLUDEDIR = $(BUILDROOT)$(includedir)
+INITDIR = /etc
+INITDDIR =
+LIBDIR = $(BUILDROOT)$(libdir)
+LOCALEDIR = $(BUILDROOT)/usr/share/locale
+LOGDIR = $(BUILDROOT)/var/log/cups
+MANDIR = $(BUILDROOT)/usr/share/man
+MENUDIR = /usr/share/applications
+PMANDIR = $(BUILDROOT)/usr/share/man
+RCLEVELS = 2 3 5
+RCSTART = 81
+RCSTOP = 36
+REQUESTS = $(BUILDROOT)/var/spool/cups
+SBINDIR = $(BUILDROOT)/usr/sbin
+SERVERBIN = $(BUILDROOT)/usr/lib/cups
+SERVERROOT = $(BUILDROOT)/etc/cups
+SMFMANIFESTDIR =
+STATEDIR = $(BUILDROOT)/var/run/cups
+XINETD =
+
+MAN1EXT = 1.gz
+MAN5EXT = 5.gz
+MAN7EXT = 7.gz
+MAN8EXT = 8.gz
+MAN8DIR = 8
+
+PAMDIR =
+PAMFILE = pam.std
+
+DEFAULT_LAUNCHD_CONF =
+DBUSDIR = /etc/dbus-1
+
+
+#
+# Rules...
+#
+
+.SILENT:
+.SUFFIXES: .1 .1.gz .1m .1m.gz .3 .3.gz .5 .5.gz .7 .7.gz .8 .8.gz .a .c .cxx .h .man .o .32.o .64.o .gz
+
+.c.o:
+ echo Compiling $<...
+ $(CC) $(ARCHFLAGS) $(OPTIM) $(ALL_CFLAGS) -c -o $@ $<
+
+.c.32.o:
+ echo Compiling 32-bit $<...
+ $(CC) $(ARCH32FLAGS) $(OPTIM) $(ALL_CFLAGS) -c -o $@ $<
+
+.c.64.o:
+ echo Compiling 64-bit $<...
+ $(CC) $(ARCH64FLAGS) $(OPTIM) $(ALL_CFLAGS) -c -o $@ $<
+
+.cxx.o:
+ echo Compiling $<...
+ $(CXX) $(ARCHFLAGS) $(OPTIM) $(ALL_CXXFLAGS) -c -o $@ $<
+
+.man.1 .man.1m .man.3 .man.5 .man.7 .man.8:
+ echo Linking $<...
+ $(RM) $@
+ $(LN) $< $@
+
+.man.1.gz .man.1m.gz .man.3.gz .man.5.gz .man.7.gz .man.8.gz .man.gz:
+ echo -n Compressing $<...
+ $(RM) $@
+ gzip -v9 <$< >$@
+
+
+#
+# End of "$Id: Makedefs.in 9120 2010-04-23 18:56:34Z mike $"
+#
diff --git a/cups/libs/configlinux.h b/cups/libs/configlinux.h
new file mode 100644
index 000000000..b5c4272d2
--- /dev/null
+++ b/cups/libs/configlinux.h
@@ -0,0 +1,710 @@
+/* config.h. Generated from config.h.in by configure. */
+/*
+ * "$Id: config.h.in 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * Configuration file for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ */
+
+#ifndef _CUPS_CONFIG_H_
+#define _CUPS_CONFIG_H_
+
+/*
+ * Version of software...
+ */
+
+#define CUPS_SVERSION "CUPS v1.7.3"
+#define CUPS_MINIMAL "CUPS/1.7.3"
+
+
+/*
+ * Default user and groups...
+ */
+
+#define CUPS_DEFAULT_USER "lp"
+#define CUPS_DEFAULT_GROUP "lp"
+#define CUPS_DEFAULT_SYSTEM_GROUPS "lpadmin sys root"
+#define CUPS_DEFAULT_PRINTOPERATOR_AUTH "@SYSTEM"
+
+
+/*
+ * Default file permissions...
+ */
+
+#define CUPS_DEFAULT_CONFIG_FILE_PERM 0640
+#define CUPS_DEFAULT_LOG_FILE_PERM 0644
+
+
+/*
+ * Default logging settings...
+ */
+
+#define CUPS_DEFAULT_LOG_LEVEL "warn"
+#define CUPS_DEFAULT_ACCESS_LOG_LEVEL "actions"
+
+
+/*
+ * Default fatal error settings...
+ */
+
+#define CUPS_DEFAULT_FATAL_ERRORS "config"
+
+
+/*
+ * Default browsing settings...
+ */
+
+#define CUPS_DEFAULT_BROWSING 1
+#define CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS ""
+#define CUPS_DEFAULT_DEFAULT_SHARED 1
+
+
+/*
+ * Default IPP port...
+ */
+
+#define CUPS_DEFAULT_IPP_PORT 631
+
+
+/*
+ * Default printcap file...
+ */
+
+#define CUPS_DEFAULT_PRINTCAP "/etc/printcap"
+
+
+/*
+ * Default Samba and LPD config files...
+ */
+
+#define CUPS_DEFAULT_SMB_CONFIG_FILE ""
+#define CUPS_DEFAULT_LPD_CONFIG_FILE ""
+
+
+/*
+ * Default MaxCopies value...
+ */
+
+#define CUPS_DEFAULT_MAX_COPIES 9999
+
+
+/*
+ * Do we have domain socket support?
+ */
+
+#define CUPS_DEFAULT_DOMAINSOCKET "/var/run/cups/cups.sock"
+
+
+/*
+ * Where are files stored?
+ *
+ * Note: These are defaults, which can be overridden by environment
+ * variables at run-time...
+ */
+
+#define CUPS_BINDIR "/usr/bin"
+#define CUPS_CACHEDIR "/var/cache/cups"
+#define CUPS_DATADIR "/usr/share/cups"
+#define CUPS_DOCROOT "/usr/share/doc/cups"
+#define CUPS_FONTPATH "/usr/share/cups/fonts"
+#define CUPS_LOCALEDIR "/usr/share/locale"
+#define CUPS_LOGDIR "/var/log/cups"
+#define CUPS_REQUESTS "/var/spool/cups"
+#define CUPS_SBINDIR "/usr/sbin"
+#define CUPS_SERVERBIN "/usr/lib/cups"
+#define CUPS_SERVERROOT "/etc/cups"
+#define CUPS_STATEDIR "/var/run/cups"
+
+
+/*
+ * Do we have various image libraries?
+ */
+
+/* #define HAVE_LIBPNG 1 */
+/* #define HAVE_LIBZ 1 */
+/* #define HAVE_LIBJPEG 1 */
+/* #define HAVE_LIBTIFF 1 */
+
+
+/*
+ * Do we have PAM stuff?
+ */
+
+#ifndef HAVE_LIBPAM
+#define HAVE_LIBPAM 0
+#endif /* !HAVE_LIBPAM */
+
+/* #undef HAVE_PAM_PAM_APPL_H */
+/* #undef HAVE_PAM_SET_ITEM */
+/* #undef HAVE_PAM_SETCRED */
+
+
+/*
+ * Do we have <shadow.h>?
+ */
+
+#define HAVE_SHADOW_H 1
+
+
+/*
+ * Do we have <crypt.h>?
+ */
+
+#define HAVE_CRYPT_H 1
+
+
+/*
+ * Do we have <scsi/sg.h>?
+ */
+
+#define HAVE_SCSI_SG_H 1
+
+
+/*
+ * Use <string.h>, <strings.h>, and/or <bstring.h>?
+ */
+
+#define HAVE_STRING_H 1
+#define HAVE_STRINGS_H 1
+/* #undef HAVE_BSTRING_H */
+
+/*
+ * Do we have the long long type?
+ */
+
+#define HAVE_LONG_LONG 1
+
+#ifdef HAVE_LONG_LONG
+# define CUPS_LLFMT "%lld"
+# define CUPS_LLCAST (long long)
+#else
+# define CUPS_LLFMT "%ld"
+# define CUPS_LLCAST (long)
+#endif /* HAVE_LONG_LONG */
+
+/*
+ * Do we have the strtoll() function?
+ */
+
+#define HAVE_STRTOLL 1
+
+#ifndef HAVE_STRTOLL
+# define strtoll(nptr,endptr,base) strtol((nptr), (endptr), (base))
+#endif /* !HAVE_STRTOLL */
+
+/*
+ * Do we have the strXXX() functions?
+ */
+
+#define HAVE_STRDUP 1
+#define HAVE_STRCASECMP 1
+#define HAVE_STRNCASECMP 1
+/* #undef HAVE_STRLCAT */
+/* #undef HAVE_STRLCPY */
+
+
+/*
+ * Do we have the geteuid() function?
+ */
+
+#define HAVE_GETEUID 1
+
+
+/*
+ * Do we have the vsyslog() function?
+ */
+
+#define HAVE_VSYSLOG 1
+
+
+/*
+ * Do we have the (v)snprintf() functions?
+ */
+
+#define HAVE_SNPRINTF 1
+#define HAVE_VSNPRINTF 1
+
+
+/*
+ * What signal functions to use?
+ */
+
+/* #undef HAVE_SIGSET */
+#define HAVE_SIGACTION 1
+
+
+/*
+ * What wait functions to use?
+ */
+
+#define HAVE_WAITPID 1
+#define HAVE_WAIT3 1
+
+
+/*
+ * Do we have the mallinfo function and malloc.h?
+ */
+
+/* #undef HAVE_MALLINFO */
+#define HAVE_MALLOC_H 1
+
+
+/*
+ * Do we have the POSIX ACL functions?
+ */
+
+/* #undef HAVE_ACL_INIT */
+
+
+/*
+ * Do we have the langinfo.h header file?
+ */
+
+#define HAVE_LANGINFO_H 1
+
+
+/*
+ * Which encryption libraries do we have?
+ */
+
+/* #undef HAVE_CDSASSL */
+/* #undef HAVE_GNUTLS */
+/* #undef HAVE_LIBSSL */
+/* #undef HAVE_SSL */
+
+
+/*
+ * What Security framework headers do we have?
+ */
+
+/* #undef HAVE_AUTHORIZATION_H */
+/* #undef HAVE_SECITEMPRIV_H */
+/* #undef HAVE_SECPOLICY_H */
+/* #undef HAVE_SECPOLICYPRIV_H */
+/* #undef HAVE_SECBASEPRIV_H */
+/* #undef HAVE_SECIDENTITYSEARCHPRIV_H */
+
+
+/*
+ * Do we have the SecIdentitySearchCreateWithPolicy function?
+ */
+
+/* #undef HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+
+
+/*
+ * Do we have the SecPolicyCreateSSL function?
+ */
+
+/* #undef HAVE_SECPOLICYCREATESSL */
+
+
+/*
+ * Do we have the SLP library?
+ */
+
+/* #undef HAVE_LIBSLP */
+
+
+/*
+ * Do we have an LDAP library?
+ */
+
+/* #undef HAVE_LDAP */
+/* #undef HAVE_OPENLDAP */
+/* #undef HAVE_MOZILLA_LDAP */
+/* #undef HAVE_LDAP_SSL_H */
+/* #undef HAVE_LDAP_SSL */
+/* #undef HAVE_LDAP_REBIND_PROC */
+
+
+/*
+ * Do we have libpaper?
+ */
+
+/* #undef HAVE_LIBPAPER */
+
+
+/*
+ * Do we have DNS Service Discovery (aka Bonjour)?
+ */
+
+/* #undef HAVE_DNSSD */
+
+
+/*
+ * Do we have <sys/ioctl.h>?
+ */
+
+#define HAVE_SYS_IOCTL_H 1
+
+
+/*
+ * Does the "stat" structure contain the "st_gen" member?
+ */
+
+/* #undef HAVE_ST_GEN */
+
+
+/*
+ * Does the "tm" structure contain the "tm_gmtoff" member?
+ */
+
+#define HAVE_TM_GMTOFF 1
+
+
+/*
+ * Do we have rresvport_af()?
+ */
+
+#define HAVE_RRESVPORT_AF 1
+
+
+/*
+ * Do we have getaddrinfo()?
+ */
+
+#define HAVE_GETADDRINFO 1
+
+
+/*
+ * Do we have getnameinfo()?
+ */
+
+#define HAVE_GETNAMEINFO 1
+
+
+/*
+ * Do we have getifaddrs()?
+ */
+
+#define HAVE_GETIFADDRS 1
+
+
+/*
+ * Do we have hstrerror()?
+ */
+
+#define HAVE_HSTRERROR 1
+
+
+/*
+ * Do we have res_init()?
+ */
+
+#define HAVE_RES_INIT 1
+
+
+/*
+ * Do we have <resolv.h>
+ */
+
+#define HAVE_RESOLV_H 1
+
+
+/*
+ * Do we have the <sys/sockio.h> header file?
+ */
+
+/* #undef HAVE_SYS_SOCKIO_H */
+
+
+/*
+ * Does the sockaddr structure contain an sa_len parameter?
+ */
+
+/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+
+/*
+ * Do we have the AIX usersec.h header file?
+ */
+
+/* #undef HAVE_USERSEC_H */
+
+
+/*
+ * Do we have pthread support?
+ */
+
+#define HAVE_PTHREAD_H 1
+
+
+/*
+ * Do we have launchd support?
+ */
+
+/* #undef HAVE_LAUNCH_H */
+/* #undef HAVE_LAUNCHD */
+
+
+/*
+ * Various scripting languages...
+ */
+
+#define HAVE_JAVA 1
+#define CUPS_JAVA "/usr/bin/java"
+#define HAVE_PERL 1
+#define CUPS_PERL "/usr/bin/perl"
+#define HAVE_PHP 1
+#define CUPS_PHP "no"
+#define HAVE_PYTHON 1
+#define CUPS_PYTHON "/usr/bin/python"
+
+
+/*
+ * Location of the poppler/Xpdf pdftops program...
+ */
+
+#define HAVE_PDFTOPS 1
+#define CUPS_PDFTOPS "/usr/bin/pdftops"
+
+
+/*
+ * Location of the Ghostscript gs program...
+ */
+
+/* #undef HAVE_GHOSTSCRIPT */
+#define CUPS_GHOSTSCRIPT ""
+
+
+/*
+ * Do we have Darwin's CoreFoundation and SystemConfiguration frameworks?
+ */
+
+/* #undef HAVE_COREFOUNDATION */
+/* #undef HAVE_SYSTEMCONFIGURATION */
+
+
+/*
+ * Do we have CoreFoundation public and private headers?
+ */
+
+/* #undef HAVE_COREFOUNDATION_H */
+/* #undef HAVE_CFPRIV_H */
+/* #undef HAVE_CFBUNDLEPRIV_H */
+
+
+/*
+ * Do we have ApplicationServices public headers?
+ */
+
+/* #undef HAVE_APPLICATIONSERVICES_H */
+
+
+/*
+ * Do we have the SCDynamicStoreCopyComputerName function?
+ */
+
+/* #undef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME */
+
+
+/*
+ * Do we have MacOSX 10.4's mbr_XXX functions?
+ */
+
+/* #undef HAVE_MEMBERSHIP_H */
+/* #undef HAVE_MEMBERSHIPPRIV_H */
+/* #undef HAVE_MBR_UID_TO_UUID */
+
+
+/*
+ * Do we have Darwin's notify_post header and function?
+ */
+
+/* #undef HAVE_NOTIFY_H */
+/* #undef HAVE_NOTIFY_POST */
+
+
+/*
+ * Do we have DBUS?
+ */
+
+/* #undef HAVE_DBUS */
+/* #undef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
+
+
+/*
+ * Do we have the AppleTalk/at_proto.h header?
+ */
+
+/* #undef HAVE_APPLETALK_AT_PROTO_H */
+
+
+/*
+ * Do we have the GSSAPI support library (for Kerberos support)?
+ */
+
+/* #undef HAVE_GSSAPI */
+/* #undef HAVE_GSSAPI_H */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+/* #undef HAVE_GSSAPI_GSSAPI_GENERIC_H */
+/* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */
+/* #undef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY */
+/* #undef HAVE_GSS_C_NT_HOSTBASED_SERVICE */
+/* #undef HAVE_KRB5_CC_NEW_UNIQUE */
+/* #undef HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID */
+/* #undef HAVE_KRB5_H */
+/* #undef HAVE_HEIMDAL */
+
+
+/*
+ * Default GSS service name...
+ */
+
+#define CUPS_DEFAULT_GSSSERVICENAME "host"
+
+
+/*
+ * Select/poll interfaces...
+ */
+
+#define HAVE_POLL 1
+#define HAVE_EPOLL 1
+/* #undef HAVE_KQUEUE */
+
+
+/*
+ * Do we have the <dlfcn.h> header?
+ */
+
+/* #undef HAVE_DLFCN_H */
+
+
+/*
+ * Do we have <sys/param.h>?
+ */
+
+#define HAVE_SYS_PARAM_H 1
+
+
+/*
+ * Do we have <sys/ucred.h>?
+ */
+
+/* #undef HAVE_SYS_UCRED_H */
+
+
+/*
+ * Do we have removefile()?
+ */
+
+/* #undef HAVE_REMOVEFILE */
+
+
+/*
+ * Do we have <sandbox.h>?
+ */
+
+/* #undef HAVE_SANDBOX_H */
+
+
+/*
+ * Which random number generator function to use...
+ */
+
+/* #undef HAVE_ARC4RANDOM */
+#define HAVE_RANDOM 1
+#define HAVE_LRAND48 1
+
+#ifdef HAVE_ARC4RANDOM
+# define CUPS_RAND() arc4random()
+# define CUPS_SRAND(v) arc4random_stir()
+#elif defined(HAVE_RANDOM)
+# define CUPS_RAND() random()
+# define CUPS_SRAND(v) srandom(v)
+#elif defined(HAVE_LRAND48)
+# define CUPS_RAND() lrand48()
+# define CUPS_SRAND(v) srand48(v)
+#else
+# define CUPS_RAND() rand()
+# define CUPS_SRAND(v) srand(v)
+#endif /* HAVE_ARC4RANDOM */
+
+
+/*
+ * Do we have vproc_transaction_begin/end?
+ */
+
+/* #undef HAVE_VPROC_TRANSACTION_BEGIN */
+
+
+/*
+ * Do we have libusb?
+ */
+
+#define HAVE_USB_H 1
+
+
+/*
+ * Do we have libwrap and tcpd.h?
+ */
+
+/* #undef HAVE_TCPD_H */
+
+
+/*
+ * Do we have statfs or statvfs and one of the corresponding headers?
+ */
+
+#define HAVE_STATFS 1
+#define HAVE_STATVFS 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_STATFS_H 1
+#define HAVE_SYS_STATVFS_H 1
+#define HAVE_SYS_VFS_H 1
+
+
+/*
+ * Location of OS X localization bundle, if any.
+ */
+
+/* #undef CUPS_BUNDLEDIR */
+
+
+/*
+ * Do we have XPC?
+ */
+
+/* #undef HAVE_XPC */
+/* #undef HAVE_XPC_PRIVATE_H */
+
+
+/*
+ * Do we have Mini-XML?
+ */
+
+/* #undef HAVE_MXML_H */
+
+
+/*
+ * Do we have the C99 abs() function?
+ */
+
+#define HAVE_ABS 0
+#if !defined(HAVE_ABS) && !defined(abs)
+# if defined(__GNUC__) || __STDC_VERSION__ >= 199901L
+# define abs(x) _cups_abs(x)
+static inline int _cups_abs(int i) { return (i < 0 ? -i : i); }
+# elif defined(_MSC_VER)
+# define abs(x) _cups_abs(x)
+static __inline int _cups_abs(int i) { return (i < 0 ? -i : i); }
+# else
+# define abs(x) ((x) < 0 ? -(x) : (x))
+# endif /* __GNUC__ || __STDC_VERSION__ */
+#endif /* !HAVE_ABS && !abs */
+
+
+#endif /* !_CUPS_CONFIG_H_ */
+
+/*
+ * End of "$Id: config.h.in 9259 2010-08-13 04:11:46Z mike $".
+ */
diff --git a/cups/libs/configwin.h b/cups/libs/configwin.h
new file mode 100644
index 000000000..4bfa3e624
--- /dev/null
+++ b/cups/libs/configwin.h
@@ -0,0 +1,754 @@
+/* config.h. Generated from config.h.in by configure. */
+/*
+ * "$Id: config.h.in 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * Configuration file for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ */
+
+#ifndef _CUPS_CONFIG_H_
+#define _CUPS_CONFIG_H_
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <io.h>
+
+
+/*
+ * Microsoft renames the POSIX functions to _name, and introduces
+ * a broken compatibility layer using the original names. As a result,
+ * random crashes can occur when, for example, strdup() allocates memory
+ * from a different heap than used by malloc() and free().
+ *
+ * To avoid moronic problems like this, we #define the POSIX function
+ * names to the corresponding non-standard Microsoft names.
+ */
+
+#define access _access
+#define close _close
+#define fileno _fileno
+#define lseek _lseek
+#define open _open
+#define read _read
+#ifndef HAVE_SNPRINTF
+#define snprintf _snprintf
+#define HAVE_SNPRINTF 1
+#endif
+#ifndef HAVE_STRDUP
+#define strdup _strdup
+#define HAVE_STRDUP 1
+#endif
+#define unlink _unlink
+#define vsnprintf _vsnprintf
+#define write _write
+
+
+/*
+ * Map the POSIX sleep() and usleep() functions to the Win32 Sleep() function...
+ */
+
+#define sleep(X) Sleep(1000 * (X))
+#define usleep(X) Sleep((X)/1000)
+
+
+/*
+ * Map various parameters to Posix style system calls
+ */
+
+# define F_OK 00
+# define W_OK 02
+# define R_OK 04
+# define O_RDONLY _O_RDONLY
+# define O_WRONLY _O_WRONLY
+# define O_CREATE _O_CREAT
+# define O_TRUNC _O_TRUNC
+
+
+/*
+ * Compiler stuff...
+ */
+
+#undef const
+#undef __CHAR_UNSIGNED__
+
+
+/*
+ * Version of software...
+ */
+
+#define CUPS_SVERSION "CUPS v1.7.3"
+#define CUPS_MINIMAL "CUPS/1.7.3"
+
+
+/*
+ * Default user and groups...
+ */
+
+#define CUPS_DEFAULT_USER ""
+#define CUPS_DEFAULT_GROUP ""
+#define CUPS_DEFAULT_SYSTEM_GROUPS ""
+#define CUPS_DEFAULT_PRINTOPERATOR_AUTH ""
+
+
+/*
+ * Default file permissions...
+ */
+
+#define CUPS_DEFAULT_CONFIG_FILE_PERM 0644
+#define CUPS_DEFAULT_LOG_FILE_PERM 0644
+
+
+/*
+ * Default logging settings...
+ */
+
+#define CUPS_DEFAULT_LOG_LEVEL "warn"
+#define CUPS_DEFAULT_ACCESS_LOG_LEVEL "actions"
+
+
+/*
+ * Default fatal error settings...
+ */
+
+#define CUPS_DEFAULT_FATAL_ERRORS "config"
+
+
+/*
+ * Default browsing settings...
+ */
+
+#define CUPS_DEFAULT_BROWSING 1
+#define CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS ""
+#define CUPS_DEFAULT_DEFAULT_SHARED 1
+
+
+/*
+ * Default IPP port...
+ */
+
+#define CUPS_DEFAULT_IPP_PORT 631
+
+
+/*
+ * Default printcap file...
+ */
+
+#define CUPS_DEFAULT_PRINTCAP ""
+
+
+/*
+ * Default Samba and LPD config files...
+ */
+
+#define CUPS_DEFAULT_SMB_CONFIG_FILE ""
+#define CUPS_DEFAULT_LPD_CONFIG_FILE ""
+
+
+/*
+ * Default MaxCopies value...
+ */
+
+#define CUPS_DEFAULT_MAX_COPIES 9999
+
+
+/*
+ * Do we have domain socket support?
+ */
+
+#undef CUPS_DEFAULT_DOMAINSOCKET
+
+
+/*
+ * Where are files stored?
+ *
+ * Note: These are defaults, which can be overridden by environment
+ * variables at run-time...
+ */
+
+#define CUPS_BINDIR "C:/CUPS/bin"
+#define CUPS_CACHEDIR "C:/CUPS/cache"
+#define CUPS_DATADIR "C:/CUPS/share"
+#define CUPS_DOCROOT "C:/CUPS/share/doc"
+#define CUPS_FONTPATH "C:/CUPS/share/fonts"
+#define CUPS_LOCALEDIR "C:/CUPS/locale"
+#define CUPS_LOGDIR "C:/CUPS/logs"
+#define CUPS_REQUESTS "C:/CUPS/spool"
+#define CUPS_SBINDIR "C:/CUPS/sbin"
+#define CUPS_SERVERBIN "C:/CUPS/lib"
+#define CUPS_SERVERROOT "C:/CUPS/etc"
+#define CUPS_STATEDIR "C:/CUPS/run"
+
+
+/*
+ * Do we have various image libraries?
+ */
+
+/* #undef HAVE_LIBPNG */
+/* #undef HAVE_LIBZ */
+/* #undef HAVE_LIBJPEG */
+/* #undef HAVE_LIBTIFF */
+
+
+/*
+ * Do we have PAM stuff?
+ */
+
+#ifndef HAVE_LIBPAM
+#define HAVE_LIBPAM 0
+#endif /* !HAVE_LIBPAM */
+
+/* #undef HAVE_PAM_PAM_APPL_H */
+/* #undef HAVE_PAM_SET_ITEM */
+/* #undef HAVE_PAM_SETCRED */
+
+
+/*
+ * Do we have <shadow.h>?
+ */
+
+/* #undef HAVE_SHADOW_H */
+
+
+/*
+ * Do we have <crypt.h>?
+ */
+
+/* #undef HAVE_CRYPT_H */
+
+
+/*
+ * Do we have <scsi/sg.h>?
+ */
+
+/* #undef HAVE_SCSI_SG_H */
+
+
+/*
+ * Use <string.h>, <strings.h>, and/or <bstring.h>?
+ */
+
+#define HAVE_STRING_H 1
+/* #undef HAVE_STRINGS_H */
+/* #undef HAVE_BSTRING_H */
+
+
+/*
+ * Do we have the long long type?
+ */
+
+/* #undef HAVE_LONG_LONG */
+
+#ifdef HAVE_LONG_LONG
+# define CUPS_LLFMT "%lld"
+# define CUPS_LLCAST (long long)
+#else
+# define CUPS_LLFMT "%ld"
+# define CUPS_LLCAST (long)
+#endif /* HAVE_LONG_LONG */
+
+
+/*
+ * Do we have the strtoll() function?
+ */
+
+/* #undef HAVE_STRTOLL */
+
+#ifndef HAVE_STRTOLL
+# define strtoll(nptr,endptr,base) strtol((nptr), (endptr), (base))
+#endif /* !HAVE_STRTOLL */
+
+
+/*
+ * Do we have the strXXX() functions?
+ */
+
+#define HAVE_STRDUP 1
+# if defined(WIN32) || defined(__EMX__)
+# ifndef HAVE_STRCASECMP
+# define strcasecmp _stricmp
+# define HAVE_STRCASECMP 1
+# endif /* HAVE_STRCASECMP */
+# ifndef HAVE_STRNCASECMP
+# define strncasecmp _strnicmp
+# define HAVE_STRNCASECMP 1
+# endif /* HAVE_STRNCASECMP */
+# endif /* WIN32 || __EMX__ */
+/* #undef HAVE_STRLCAT */
+/* #undef HAVE_STRLCPY */
+
+/*
+ * Do we have the geteuid() function?
+ */
+
+/* #undef HAVE_GETEUID */
+
+
+/*
+ * Do we have the vsyslog() function?
+ */
+
+/* #undef HAVE_VSYSLOG */
+
+
+/*
+ * Do we have the (v)snprintf() functions?
+ */
+
+#define HAVE_SNPRINTF 1
+#define HAVE_VSNPRINTF 1
+
+
+/*
+ * What signal functions to use?
+ */
+
+/* #undef HAVE_SIGSET */
+/* #undef HAVE_SIGACTION */
+
+
+/*
+ * What wait functions to use?
+ */
+
+/* #undef HAVE_WAITPID */
+/* #undef HAVE_WAIT3 */
+
+
+/*
+ * Do we have the mallinfo function and malloc.h?
+ */
+
+/* #undef HAVE_MALLINFO */
+/* #undef HAVE_MALLOC_H */
+
+
+/*
+ * Do we have the POSIX ACL functions?
+ */
+
+/* #undef HAVE_ACL_INIT */
+
+
+/*
+ * Do we have the langinfo.h header file?
+ */
+
+/* #undef HAVE_LANGINFO_H */
+
+
+/*
+ * Which encryption libraries do we have?
+ */
+
+/* #undef HAVE_CDSASSL */
+/* #undef HAVE_GNUTLS */
+/* #undef HAVE_LIBSSL */
+/* #undef HAVE_SSL */
+
+
+/*
+ * What Security framework headers do we have?
+ */
+
+/* #undef HAVE_AUTHORIZATION_H */
+/* #undef HAVE_SECPOLICY_H */
+/* #undef HAVE_SECPOLICYPRIV_H */
+/* #undef HAVE_SECBASEPRIV_H */
+/* #undef HAVE_SECIDENTITYSEARCHPRIV_H */
+
+
+/*
+ * Do we have the SecIdentitySearchCreateWithPolicy function?
+ */
+
+/* #undef HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+
+
+/*
+ * Do we have the SLP library?
+ */
+
+/* #undef HAVE_LIBSLP */
+
+
+/*
+ * Do we have an LDAP library?
+ */
+
+/* #undef HAVE_LDAP */
+/* #undef HAVE_OPENLDAP */
+/* #undef HAVE_MOZILLA_LDAP */
+/* #undef HAVE_LDAP_SSL_H */
+/* #undef HAVE_LDAP_SSL */
+/* #undef HAVE_LDAP_REBIND_PROC */
+
+
+/*
+ * Do we have libpaper?
+ */
+
+/* #undef HAVE_LIBPAPER */
+
+
+/*
+ * Do we have DNS Service Discovery (aka Bonjour)?
+ */
+
+/* #undef HAVE_DNSSD */
+
+
+/*
+ * Do we have <sys/ioctl.h>?
+ */
+
+/* #undef HAVE_SYS_IOCTL_H */
+
+
+/*
+ * Does the "tm" structure contain the "tm_gmtoff" member?
+ */
+
+/* #undef HAVE_TM_GMTOFF */
+
+
+/*
+ * Do we have rresvport_af()?
+ */
+
+/* #undef HAVE_RRESVPORT_AF */
+
+
+/*
+ * Do we have getaddrinfo()?
+ */
+
+#define HAVE_GETADDRINFO 1
+
+
+/*
+ * Do we have getnameinfo()?
+ */
+
+#define HAVE_GETNAMEINFO 1
+
+
+/*
+ * Do we have getifaddrs()?
+ */
+
+/* #undef HAVE_GETIFADDRS */
+
+
+/*
+ * Do we have hstrerror()?
+ */
+
+/* #undef HAVE_HSTRERROR */
+
+
+/*
+ * Do we have res_init()?
+ */
+
+/* #undef HAVE_RES_INIT */
+
+
+/*
+ * Do we have <resolv.h>
+ */
+
+/* #undef HAVE_RESOLV_H */
+
+
+/*
+ * Do we have the <sys/sockio.h> header file?
+ */
+
+/* #undef HAVE_SYS_SOCKIO_H */
+
+
+/*
+ * Does the sockaddr structure contain an sa_len parameter?
+ */
+
+/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+
+/*
+ * Do we have the AIX usersec.h header file?
+ */
+
+/* #undef HAVE_USERSEC_H */
+
+
+/*
+ * Do we have pthread support?
+ */
+
+/* #undef HAVE_PTHREAD_H */
+
+
+/*
+ * Do we have launchd support?
+ */
+
+/* #undef HAVE_LAUNCH_H */
+/* #undef HAVE_LAUNCHD */
+
+
+/*
+ * Various scripting languages...
+ */
+
+/* #undef HAVE_JAVA */
+#define CUPS_JAVA ""
+/* #undef HAVE_PERL */
+#define CUPS_PERL ""
+/* #undef HAVE_PHP */
+#define CUPS_PHP ""
+/* #undef HAVE_PYTHON */
+#define CUPS_PYTHON ""
+
+
+/*
+ * Location of the poppler/Xpdf pdftops program...
+ */
+
+/* #undef HAVE_PDFTOPS */
+#define CUPS_PDFTOPS ""
+
+
+/*
+ * Location of the Ghostscript gs program...
+ */
+
+/* #undef HAVE_GHOSTSCRIPT */
+#define CUPS_GHOSTSCRIPT ""
+
+
+/*
+ * Do we have Darwin's CoreFoundation and SystemConfiguration frameworks?
+ */
+
+/* #undef HAVE_COREFOUNDATION */
+/* #undef HAVE_SYSTEMCONFIGURATION */
+
+
+/*
+ * Do we have CoreFoundation public and private headers?
+ */
+
+/* #undef HAVE_COREFOUNDATION_H */
+/* #undef HAVE_CFPRIV_H */
+/* #undef HAVE_CFBUNDLEPRIV_H */
+
+
+/*
+ * Do we have the SCDynamicStoreCopyComputerName function?
+ */
+
+/* #undef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME */
+
+
+/*
+ * Do we have MacOSX 10.4's mbr_XXX functions()?
+ */
+
+/* #undef HAVE_MEMBERSHIP_H */
+/* #undef HAVE_MEMBERSHIPPRIV_H */
+/* #undef HAVE_MBR_UID_TO_UUID */
+
+
+/*
+ * Do we have Darwin's notify_post() header and function?
+ */
+
+/* #undef HAVE_NOTIFY_H */
+/* #undef HAVE_NOTIFY_POST */
+
+
+/*
+ * Do we have DBUS?
+ */
+
+/* #undef HAVE_DBUS */
+/* #undef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
+
+
+/*
+ * Do we have the AppleTalk/at_proto.h header?
+ */
+
+/* #undef HAVE_APPLETALK_AT_PROTO_H */
+
+
+/*
+ * Do we have the GSSAPI support library (for Kerberos support)?
+ */
+
+/* #undef HAVE_GSSAPI */
+/* #undef HAVE_GSSAPI_H */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+/* #undef HAVE_GSSAPI_GSSAPI_GENERIC_H */
+/* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */
+/* #undef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY */
+/* #undef HAVE_GSS_C_NT_HOSTBASED_SERVICE */
+/* #undef HAVE_KRB5_CC_NEW_UNIQUE */
+/* #undef HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID */
+/* #undef HAVE_KRB5_H */
+/* #undef HAVE_HEIMDAL */
+
+
+/*
+ * Default GSS service name...
+ */
+
+#define CUPS_DEFAULT_GSSSERVICENAME "host"
+
+
+/*
+ * Select/poll interfaces...
+ */
+
+/* #undef HAVE_POLL */
+/* #undef HAVE_EPOLL */
+/* #undef HAVE_KQUEUE */
+
+
+/*
+ * Do we have the <dlfcn.h> header?
+ */
+
+/* #undef HAVE_DLFCN_H */
+
+
+/*
+ * Do we have <sys/param.h>?
+ */
+
+/* #undef HAVE_SYS_PARAM_H */
+
+
+/*
+ * Do we have <sys/ucred.h>?
+ */
+
+/* #undef HAVE_SYS_UCRED_H */
+
+
+/*
+ * Do we have removefile()?
+ */
+
+/* #undef HAVE_REMOVEFILE */
+
+
+/*
+ * Do we have <sandbox.h>?
+ */
+
+/* #undef HAVE_SANDBOX_H */
+
+
+/*
+ * Which random number generator function to use...
+ */
+
+/* #undef HAVE_ARC4RANDOM */
+/* #undef HAVE_RANDOM */
+/* #undef HAVE_LRAND48 */
+
+#ifdef HAVE_ARC4RANDOM
+# define CUPS_RAND() arc4random()
+# define CUPS_SRAND(v) arc4random_stir()
+#elif defined(HAVE_RANDOM)
+# define CUPS_RAND() random()
+# define CUPS_SRAND(v) srandom(v)
+#elif defined(HAVE_LRAND48)
+# define CUPS_RAND() lrand48()
+# define CUPS_SRAND(v) srand48(v)
+#else
+# define CUPS_RAND() rand()
+# define CUPS_SRAND(v) srand(v)
+#endif /* HAVE_ARC4RANDOM */
+
+
+/*
+ * Do we have vproc_transaction_begin/end?
+ */
+
+/* #undef HAVE_VPROC_TRANSACTION_BEGIN */
+
+
+/*
+ * Do we have libusb?
+ */
+
+/* #undef HAVE_USB_H */
+
+
+/*
+ * Do we have libwrap and tcpd.h?
+ */
+
+/* #undef HAVE_TCPD_H */
+
+/*
+ * Location of OS X localization bundle, if any.
+ */
+
+/* #undef CUPS_BUNDLEDIR */
+
+
+/*
+ * Do we have XPC?
+ */
+
+/* #undef HAVE_XPC */
+/* #undef HAVE_XPC_PRIVATE_H */
+
+
+/*
+ * Do we have Mini-XML?
+ */
+
+/* #undef HAVE_MXML_H */
+
+
+/*
+ * Do we have the C99 abs() function?
+ */
+
+#define HAVE_ABS 0
+#if !defined(HAVE_ABS) && !defined(abs)
+# if defined(__GNUC__) || __STDC_VERSION__ >= 199901L
+# define abs(x) _cups_abs(x)
+static inline int _cups_abs(int i) { return (i < 0 ? -i : i); }
+# elif defined(_MSC_VER)
+# define abs(x) _cups_abs(x)
+static __inline int _cups_abs(int i) { return (i < 0 ? -i : i); }
+# else
+# define abs(x) ((x) < 0 ? -(x) : (x))
+# endif /* __GNUC__ || __STDC_VERSION__ */
+#endif /* !HAVE_ABS && !abs */
+
+
+#endif /* !_CUPS_CONFIG_H_ */
+
+/*
+ * End of "$Id: config.h 9233 2010-08-10 06:15:55Z mike $".
+ */
diff --git a/cups/libs/cups/Dependencies b/cups/libs/cups/Dependencies
new file mode 100644
index 000000000..10beea26b
--- /dev/null
+++ b/cups/libs/cups/Dependencies
@@ -0,0 +1,260 @@
+adminutil.o: adminutil.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h adminutil.h
+array.o: array.c string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h array-private.h ../cups/array.h
+attr.o: attr.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+auth.o: auth.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+backchannel.o: backchannel.c cups.h file.h versioning.h ipp.h http.h \
+ array.h language.h
+backend.o: backend.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h backend.h
+conflicts.o: conflicts.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+custom.o: custom.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+debug.o: debug.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+dest.o: dest.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+dest-job.o: dest-job.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+dest-localization.o: dest-localization.c cups-private.h string-private.h \
+ ../config.h debug-private.h ../cups/versioning.h ipp-private.h \
+ ../cups/ipp.h http.h array.h http-private.h md5-private.h \
+ language-private.h ../cups/transcode.h language.h pwg-private.h \
+ ../cups/cups.h file.h ppd-private.h ../cups/ppd.h thread-private.h
+dest-options.o: dest-options.c cups-private.h string-private.h \
+ ../config.h debug-private.h ../cups/versioning.h ipp-private.h \
+ ../cups/ipp.h http.h array.h http-private.h md5-private.h \
+ language-private.h ../cups/transcode.h language.h pwg-private.h \
+ ../cups/cups.h file.h ppd-private.h ../cups/ppd.h thread-private.h
+dir.o: dir.c string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h dir.h
+emit.o: emit.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+encode.o: encode.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+file.o: file.c file-private.h cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+getdevices.o: getdevices.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+getifaddrs.o: getifaddrs.c http-private.h ../config.h ../cups/http.h \
+ versioning.h array.h md5-private.h ipp-private.h ../cups/ipp.h
+getputfile.o: getputfile.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+globals.o: globals.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+http.o: http.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+http-addr.o: http-addr.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+http-addrlist.o: http-addrlist.c cups-private.h string-private.h \
+ ../config.h debug-private.h ../cups/versioning.h ipp-private.h \
+ ../cups/ipp.h http.h array.h http-private.h md5-private.h \
+ language-private.h ../cups/transcode.h language.h pwg-private.h \
+ ../cups/cups.h file.h ppd-private.h ../cups/ppd.h thread-private.h
+http-support.o: http-support.c cups-private.h string-private.h \
+ ../config.h debug-private.h ../cups/versioning.h ipp-private.h \
+ ../cups/ipp.h http.h array.h http-private.h md5-private.h \
+ language-private.h ../cups/transcode.h language.h pwg-private.h \
+ ../cups/cups.h file.h ppd-private.h ../cups/ppd.h thread-private.h
+ipp.o: ipp.c cups-private.h string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h ipp-private.h ../cups/ipp.h http.h array.h \
+ http-private.h md5-private.h language-private.h ../cups/transcode.h \
+ language.h pwg-private.h ../cups/cups.h file.h ppd-private.h \
+ ../cups/ppd.h thread-private.h
+ipp-support.o: ipp-support.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+langprintf.o: langprintf.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+language.o: language.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+localize.o: localize.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+mark.o: mark.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+md5.o: md5.c md5-private.h string-private.h ../config.h
+md5passwd.o: md5passwd.c http-private.h ../config.h ../cups/http.h \
+ versioning.h array.h md5-private.h ipp-private.h ../cups/ipp.h \
+ string-private.h
+notify.o: notify.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+options.o: options.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+page.o: page.c string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h ppd.h cups.h file.h ipp.h http.h array.h \
+ language.h
+ppd.o: ppd.c cups-private.h string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h ipp-private.h ../cups/ipp.h http.h array.h \
+ http-private.h md5-private.h language-private.h ../cups/transcode.h \
+ language.h pwg-private.h ../cups/cups.h file.h ppd-private.h \
+ ../cups/ppd.h thread-private.h
+ppd-cache.o: ppd-cache.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+pwg-media.o: pwg-media.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+request.o: request.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+sidechannel.o: sidechannel.c sidechannel.h versioning.h cups-private.h \
+ string-private.h ../config.h debug-private.h ipp-private.h \
+ ../cups/ipp.h http.h array.h http-private.h md5-private.h \
+ language-private.h ../cups/transcode.h language.h pwg-private.h \
+ ../cups/cups.h file.h ppd-private.h ../cups/ppd.h thread-private.h
+snmp.o: snmp.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h snmp-private.h
+snprintf.o: snprintf.c string-private.h ../config.h
+string.o: string.c string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h thread-private.h array.h
+tempfile.o: tempfile.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+thread.o: thread.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+transcode.o: transcode.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+usersys.o: usersys.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+util.o: util.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+testadmin.o: testadmin.c adminutil.h cups.h file.h versioning.h ipp.h \
+ http.h array.h language.h string-private.h ../config.h
+testarray.o: testarray.c string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h array.h dir.h
+testconflicts.o: testconflicts.c cups.h file.h versioning.h ipp.h http.h \
+ array.h language.h ppd.h string-private.h ../config.h
+testcups.o: testcups.c string-private.h ../config.h cups.h file.h \
+ versioning.h ipp.h http.h array.h language.h ppd.h
+testfile.o: testfile.c string-private.h ../config.h debug-private.h \
+ ../cups/versioning.h file.h
+testhttp.o: testhttp.c string-private.h ../config.h http-private.h \
+ ../cups/http.h versioning.h array.h md5-private.h ipp-private.h \
+ ../cups/ipp.h
+testi18n.o: testi18n.c string-private.h ../config.h language-private.h \
+ ../cups/transcode.h language.h array.h versioning.h
+testipp.o: testipp.c file.h versioning.h string-private.h ../config.h \
+ ipp-private.h ../cups/ipp.h http.h array.h
+testoptions.o: testoptions.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+testlang.o: testlang.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+testppd.o: testppd.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h
+testpwg.o: testpwg.c ppd-private.h ../cups/cups.h file.h versioning.h \
+ ipp.h http.h array.h language.h ../cups/ppd.h pwg-private.h \
+ file-private.h cups-private.h string-private.h ../config.h \
+ debug-private.h ipp-private.h http-private.h md5-private.h \
+ language-private.h ../cups/transcode.h thread-private.h
+testsnmp.o: testsnmp.c cups-private.h string-private.h ../config.h \
+ debug-private.h ../cups/versioning.h ipp-private.h ../cups/ipp.h \
+ http.h array.h http-private.h md5-private.h language-private.h \
+ ../cups/transcode.h language.h pwg-private.h ../cups/cups.h file.h \
+ ppd-private.h ../cups/ppd.h thread-private.h snmp-private.h
diff --git a/cups/libs/cups/Makefile b/cups/libs/cups/Makefile
new file mode 100644
index 000000000..e3647f3de
--- /dev/null
+++ b/cups/libs/cups/Makefile
@@ -0,0 +1,643 @@
+#
+# "$Id: Makefile 10996 2013-05-29 11:51:34Z msweet $"
+#
+# API library Makefile 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/".
+#
+# This file is subject to the Apple OS-Developed Software exception.
+#
+
+include ../Makedefs
+
+
+#
+# Options to build libcups without the use of deprecated APIs...
+#
+
+OPTIONS = -D_CUPS_NO_DEPRECATED=1 -D_PPD_DEPRECATED=""
+
+
+#
+# Object files...
+#
+
+LIBOBJS = \
+ adminutil.o \
+ array.o \
+ attr.o \
+ auth.o \
+ backchannel.o \
+ backend.o \
+ conflicts.o \
+ custom.o \
+ debug.o \
+ dest.o \
+ dest-job.o \
+ dest-localization.o \
+ dest-options.o \
+ dir.o \
+ emit.o \
+ encode.o \
+ file.o \
+ getdevices.o \
+ getifaddrs.o \
+ getputfile.o \
+ globals.o \
+ http.o \
+ http-addr.o \
+ http-addrlist.o \
+ http-support.o \
+ ipp.o \
+ ipp-support.o \
+ langprintf.o \
+ language.o \
+ localize.o \
+ mark.o \
+ md5.o \
+ md5passwd.o \
+ notify.o \
+ options.o \
+ page.o \
+ ppd.o \
+ ppd-cache.o \
+ pwg-media.o \
+ request.o \
+ sidechannel.o \
+ snmp.o \
+ snprintf.o \
+ string.o \
+ tempfile.o \
+ thread.o \
+ transcode.o \
+ usersys.o \
+ util.o
+TESTOBJS = \
+ testadmin.o \
+ testarray.o \
+ testconflicts.o \
+ testcups.o \
+ testfile.o \
+ testhttp.o \
+ testi18n.o \
+ testipp.o \
+ testoptions.o \
+ testlang.o \
+ testppd.o \
+ testpwg.o \
+ testsnmp.o
+OBJS = \
+ $(LIBOBJS) \
+ $(TESTOBJS)
+
+
+#
+# Header files to install...
+#
+
+HEADERS = \
+ adminutil.h \
+ array.h \
+ backend.h \
+ cups.h \
+ dir.h \
+ file.h \
+ http.h \
+ ipp.h \
+ language.h \
+ ppd.h \
+ pwg.h \
+ raster.h \
+ sidechannel.h \
+ transcode.h \
+ versioning.h
+
+HEADERSPRIV = \
+ array-private.h \
+ cups-private.h \
+ debug-private.h \
+ file-private.h \
+ http-private.h \
+ ipp-private.h \
+ language-private.h \
+ md5-private.h \
+ ppd-private.h \
+ pwg-private.h \
+ raster-private.h \
+ snmp-private.h \
+ string-private.h \
+ thread-private.h
+
+
+#
+# Targets in this directory...
+#
+
+LIBTARGETS = \
+ $(LIBCUPSSTATIC) \
+ $(LIBCUPS)
+
+UNITTARGETS = \
+ testadmin \
+ testarray \
+ testconflicts \
+ testcups \
+ testfile \
+ testhttp \
+ testi18n \
+ testipp \
+ testlang \
+ testoptions \
+ testppd \
+ testpwg \
+ testsnmp
+
+TARGETS = \
+ $(LIBTARGETS)
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Make library targets...
+#
+
+libs: $(LIBTARGETS)
+
+
+#
+# Make unit tests...
+#
+
+unittests: $(UNITTARGETS)
+
+
+#
+# Remove object and target files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS) $(UNITTARGETS)
+ $(RM) libcups.so libcups.sl libcups.dylib
+
+
+#
+# Update dependencies (without system header dependencies...)
+#
+
+depend:
+ $(CC) -MM $(ALL_CFLAGS) $(OBJS:.o=.c) >Dependencies
+
+
+#
+# Run oclint to check code coverage...
+#
+
+oclint:
+ oclint -o=oclint.html -html $(LIBOBJS:.o=.c) -- $(ALL_CFLAGS)
+
+
+#
+# Install all targets...
+#
+
+install: all install-data install-headers install-libs install-exec
+
+
+#
+# Install data files...
+#
+
+install-data:
+
+
+#
+# Install programs...
+#
+
+install-exec:
+
+
+#
+# Install headers...
+#
+
+install-headers:
+ echo Installing header files into $(INCLUDEDIR)/cups...
+ $(INSTALL_DIR) -m 755 $(INCLUDEDIR)/cups
+ for file in $(HEADERS); do \
+ $(INSTALL_DATA) $$file $(INCLUDEDIR)/cups; \
+ done
+ if test "x$(privateinclude)" != x; then \
+ echo Installing private header files into $(PRIVATEINCLUDE)...; \
+ $(INSTALL_DIR) -m 755 $(PRIVATEINCLUDE); \
+ for file in $(HEADERSPRIV); do \
+ $(INSTALL_DATA) $$file $(PRIVATEINCLUDE)/$$file; \
+ done; \
+ fi
+
+
+#
+# Install libraries...
+#
+
+install-libs: $(INSTALLSTATIC)
+ echo Installing libraries in $(LIBDIR)...
+ $(INSTALL_DIR) -m 755 $(LIBDIR)
+ $(INSTALL_LIB) $(LIBCUPS) $(LIBDIR)
+ if test $(LIBCUPS) = "libcups.so.2" -o $(LIBCUPS) = "libcups.sl.2"; then \
+ $(RM) $(LIBDIR)/`basename $(LIBCUPS) .2`; \
+ $(LN) $(LIBCUPS) $(LIBDIR)/`basename $(LIBCUPS) .2`; \
+ fi
+ if test $(LIBCUPS) = "libcups.2.dylib"; then \
+ $(RM) $(LIBDIR)/libcups.dylib; \
+ $(LN) $(LIBCUPS) $(LIBDIR)/libcups.dylib; \
+ fi
+ if test "x$(SYMROOT)" != "x"; then \
+ $(INSTALL_DIR) $(SYMROOT); \
+ cp $(LIBCUPS) $(SYMROOT); \
+ dsymutil $(SYMROOT)/$(LIBCUPS); \
+ fi
+
+installstatic:
+ $(INSTALL_DIR) -m 755 $(LIBDIR)
+ $(INSTALL_LIB) -m 755 $(LIBCUPSSTATIC) $(LIBDIR)
+ $(RANLIB) $(LIBDIR)/$(LIBCUPSSTATIC)
+ $(CHMOD) 555 $(LIBDIR)/$(LIBCUPSSTATIC)
+
+
+#
+# Uninstall object and target files...
+#
+
+uninstall:
+ $(RM) $(LIBDIR)/libcups.2.dylib
+ $(RM) $(LIBDIR)/$(LIBCUPSSTATIC)
+ $(RM) $(LIBDIR)/libcups.dylib
+ $(RM) $(LIBDIR)/libcups_s.a
+ $(RM) $(LIBDIR)/libcups.sl
+ $(RM) $(LIBDIR)/libcups.sl.2
+ $(RM) $(LIBDIR)/libcups.so
+ $(RM) $(LIBDIR)/libcups.so.2
+ -$(RMDIR) $(LIBDIR)
+ for file in $(HEADERS); do \
+ $(RM) $(INCLUDEDIR)/cups/$$file; \
+ done
+ -$(RMDIR) $(INCLUDEDIR)/cups
+
+
+#
+# libcups.so.2, libcups.sl.2
+#
+
+libcups.so.2 libcups.sl.2: $(LIBOBJS)
+ echo Linking $@...
+ $(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS) $(LIBGSSAPI) \
+ $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ $(RM) `basename $@ .2`
+ $(LN) $@ `basename $@ .2`
+
+
+#
+# libcups.2.dylib
+#
+
+libcups.2.dylib: $(LIBOBJS) $(LIBCUPSORDER)
+ echo Creating export list for $@...
+ nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
+ grep -v -e '^(_cupsConnect|_cupsCharset|_cupsEncodingName|_cupsSetDefaults|_cupsSetHTTPError|_cupsUserDefault|_httpWait)$$' | \
+ sort >t.exp
+ echo Linking $@...
+ $(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ \
+ -install_name $(libdir)/$@ \
+ -current_version 2.10.0 \
+ -compatibility_version 2.0.0 \
+ -exported_symbols_list t.exp \
+ $(LIBOBJS) $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) \
+ $(COMMONLIBS) $(LIBZ)
+ $(RM) libcups.dylib t.exp
+ $(LN) $@ libcups.dylib
+
+
+#
+# libcups_s.a
+#
+
+libcups_s.a: $(LIBOBJS) libcups_s.exp
+ echo Creating $@...
+ $(DSO) $(DSOFLAGS) -Wl,-bexport:libcups_s.exp -o libcups_s.o \
+ $(LIBOBJS) $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) \
+ $(COMMONLIBS) $(LIBZ)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ libcups_s.o
+
+
+#
+# libcups.la
+#
+
+libcups.la: $(LIBOBJS)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS:.o=.lo) \
+ -rpath $(LIBDIR) -version-info 2:10 $(LIBGSSAPI) $(SSLLIBS) \
+ $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
+# libcups.a
+#
+
+libcups.a: $(LIBOBJS)
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+
+#
+# testadmin (dependency on static CUPS library is intentional)
+#
+
+testadmin: testadmin.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testadmin.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
+# testarray (dependency on static CUPS library is intentional)
+#
+
+testarray: testarray.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testarray.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running array API tests...
+ ./testarray
+
+
+#
+# testconflicts (dependency on static CUPS library is intentional)
+#
+
+testconflicts: testconflicts.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testconflicts.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
+# testcups (dependency on static CUPS library is intentional)
+#
+
+testcups: testcups.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testcups.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
+# testfile (dependency on static CUPS library is intentional)
+#
+
+testfile: testfile.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testfile.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running file API tests...
+ ./testfile
+
+
+#
+# testhttp (dependency on static CUPS library is intentional)
+#
+
+testhttp: testhttp.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testhttp.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running HTTP API tests...
+ ./testhttp
+
+
+#
+# testipp (dependency on static CUPS library is intentional)
+#
+
+testipp: testipp.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testipp.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running IPP API tests...
+ ./testipp
+
+
+#
+# testi18n (dependency on static CUPS library is intentional)
+#
+
+testi18n: testi18n.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testi18n.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running internationalization API tests...
+ ./testi18n
+
+
+#
+# testlang (dependency on static CUPS library is intentional)
+#
+
+testlang: testlang.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testlang.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running language API tests...
+ ./testlang
+
+
+#
+# testoptions (dependency on static CUPS library is intentional)
+#
+
+testoptions: testoptions.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testoptions.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running option API tests...
+ ./testoptions
+
+
+#
+# testppd (dependency on static CUPS library is intentional)
+#
+
+testppd: testppd.o $(LIBCUPSSTATIC) test.ppd test2.ppd
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testppd.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running PPD API tests...
+ ./testppd
+
+
+#
+# testpwg (dependency on static CUPS library is intentional)
+#
+
+testpwg: testpwg.o $(LIBCUPSSTATIC) test.ppd
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testpwg.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running PWG API tests...
+ ./testpwg test.ppd
+
+
+#
+# testsnmp (dependency on static CUPS library is intentional)
+#
+
+testsnmp: testsnmp.o $(LIBCUPSSTATIC)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testsnmp.o $(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
+# Automatic API help files...
+#
+
+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
+ 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 attr.c conflicts.c custom.c emit.c localize.c mark.c page.c \
+ 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 \
+ --header api-filter.header --intro api-filter.shtml \
+ 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
+
+framedhelp:
+ echo Generating CUPS API help files...
+ mxmldoc --framed api-overview \
+ --section "Programming" \
+ --title "Introduction to CUPS Programming" \
+ --css ../doc/cups-printable.css \
+ --header api-overview.header --intro api-overview.shtml
+ mxmldoc --framed api-array \
+ --section "Programming" --title "Array API" \
+ --css ../doc/cups-printable.css \
+ --header api-array.header --intro api-array.shtml \
+ array.h array.c
+ mxmldoc --framed api-cups \
+ --section "Programming" --title "CUPS API" \
+ --css ../doc/cups-printable.css \
+ --header api-cups.header --intro api-cups.shtml \
+ cups.h adminutil.c dest*.c language.c notify.c \
+ options.c tempfile.c usersys.c \
+ util.c
+ mxmldoc --framed api-filedir \
+ --section "Programming" --title "File and Directory APIs" \
+ --css ../doc/cups-printable.css \
+ --header api-filedir.header --intro api-filedir.shtml \
+ file.h file.c dir.h dir.c
+ mxmldoc --framed api-ppd \
+ --section "Programming" --title "PPD API (DEPRECATED)" \
+ --css ../doc/cups-printable.css \
+ --header api-ppd.header --intro api-ppd.shtml \
+ ppd.h attr.c conflicts.c custom.c emit.c localize.c mark.c \
+ page.c ppd.c
+ mxmldoc --framed api-httpipp \
+ --section "Programming" --title "HTTP and IPP APIs" \
+ --css ../doc/cups-printable.css \
+ --header api-httpipp.header --intro api-httpipp.shtml \
+ 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
+ mxmldoc --framed api-filter \
+ --section "Programming" \
+ --title "Filter and Backend Programming" \
+ --css ../doc/cups-printable.css \
+ --header api-filter.header --intro api-filter.shtml \
+ backchannel.c backend.h backend.c sidechannel.c sidechannel.h
+
+
+#
+# Lines of code computation...
+#
+
+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}'
+
+
+#
+# Dependencies...
+#
+
+include Dependencies
+
+
+#
+# End of "$Id: Makefile 10996 2013-05-29 11:51:34Z msweet $".
+#
diff --git a/cups/libs/cups/adminutil.c b/cups/libs/cups/adminutil.c
new file mode 100644
index 000000000..580511b8f
--- /dev/null
+++ b/cups/libs/cups/adminutil.c
@@ -0,0 +1,2341 @@
+/*
+ * "$Id: adminutil.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Administration utility API definitions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2001-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsAdminCreateWindowsPPD() - Create the Windows PPD file for a printer.
+ * cupsAdminExportSamba() - Export a printer to Samba.
+ * cupsAdminGetServerSettings() - Get settings from the server.
+ * cupsAdminSetServerSettings() - Set settings on the server.
+ * do_samba_command() - Do a SAMBA command.
+ * get_cupsd_conf() - Get the current cupsd.conf file.
+ * invalidate_cupsd_cache() - Invalidate the cached cupsd.conf settings.
+ * write_option() - Write a CUPS option to a PPD file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "adminutil.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#else
+# include <unistd.h>
+# include <sys/wait.h>
+#endif /* WIN32 */
+
+
+/*
+ * Local functions...
+ */
+
+static int do_samba_command(const char *command,
+ const char *address,
+ const char *subcommand,
+ const char *authfile,
+ FILE *logfile);
+static http_status_t get_cupsd_conf(http_t *http, _cups_globals_t *cg,
+ time_t last_update, char *name,
+ int namelen, int *remote);
+static void invalidate_cupsd_cache(_cups_globals_t *cg);
+static void write_option(cups_file_t *dstfp, int order,
+ const char *name, const char *text,
+ const char *attrname,
+ ipp_attribute_t *suppattr,
+ ipp_attribute_t *defattr, int defval,
+ int valcount);
+
+
+/*
+ * 'cupsAdminCreateWindowsPPD()' - Create the Windows PPD file for a printer.
+ *
+ * @deprecated@
+ */
+
+char * /* O - PPD file or NULL */
+cupsAdminCreateWindowsPPD(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *dest, /* I - Printer or class */
+ char *buffer, /* I - Filename buffer */
+ int bufsize) /* I - Size of filename buffer */
+{
+ const char *src; /* Source PPD filename */
+ cups_file_t *srcfp, /* Source PPD file */
+ *dstfp; /* Destination PPD file */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *suppattr, /* IPP -supported attribute */
+ *defattr; /* IPP -default attribute */
+ cups_lang_t *language; /* Current language */
+ char line[256], /* Line from PPD file */
+ junk[256], /* Extra junk to throw away */
+ *ptr, /* Pointer into line */
+ uri[1024], /* Printer URI */
+ option[41], /* Option */
+ choice[41]; /* Choice */
+ int jcloption, /* In a JCL option? */
+ jclorder, /* Next JCL order dependency */
+ linenum; /* Current line number */
+ time_t curtime; /* Current time */
+ struct tm *curdate; /* Current date */
+ static const char * const pattrs[] = /* Printer attributes we want */
+ {
+ "job-hold-until-supported",
+ "job-hold-until-default",
+ "job-sheets-supported",
+ "job-sheets-default",
+ "job-priority-supported",
+ "job-priority-default"
+ };
+
+
+ /*
+ * Range check the input...
+ */
+
+ if (buffer)
+ *buffer = '\0';
+
+ if (!http)
+ http = _cupsConnect();
+
+ if (!http || !dest || !buffer || bufsize < 2)
+ return (NULL);
+
+ /*
+ * Get the PPD file...
+ */
+
+ if ((src = cupsGetPPD2(http, dest)) == NULL)
+ return (NULL);
+
+ /*
+ * Get the supported banner pages, etc. for the printer...
+ */
+
+ request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", 0, "/printers/%s", dest);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
+ NULL, pattrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ response = cupsDoRequest(http, request, "/");
+ if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
+ {
+ unlink(src);
+ return (NULL);
+ }
+
+ /*
+ * Open the original PPD file...
+ */
+
+ if ((srcfp = cupsFileOpen(src, "rb")) == NULL)
+ return (NULL);
+
+ /*
+ * Create a temporary output file using the destination buffer...
+ */
+
+ if ((dstfp = cupsTempFile2(buffer, bufsize)) == NULL)
+ {
+ cupsFileClose(srcfp);
+
+ unlink(src);
+
+ return (NULL);
+ }
+
+ /*
+ * Write a new header explaining that this isn't the original PPD...
+ */
+
+ cupsFilePuts(dstfp, "*PPD-Adobe: \"4.3\"\n");
+
+ curtime = time(NULL);
+ curdate = gmtime(&curtime);
+
+ cupsFilePrintf(dstfp, "*%% Modified on %04d%02d%02d%02d%02d%02d+0000 "
+ "for CUPS Windows Driver\n",
+ curdate->tm_year + 1900, curdate->tm_mon + 1, curdate->tm_mday,
+ curdate->tm_hour, curdate->tm_min, curdate->tm_sec);
+
+ /*
+ * Read the existing PPD file, converting all PJL commands to CUPS
+ * job ticket comments...
+ */
+
+ jcloption = 0;
+ jclorder = 0;
+ linenum = 0;
+ language = cupsLangDefault();
+
+ while (cupsFileGets(srcfp, line, sizeof(line)))
+ {
+ linenum ++;
+
+ if (!strncmp(line, "*PPD-Adobe:", 11))
+ {
+ /*
+ * Already wrote the PPD header...
+ */
+
+ continue;
+ }
+ else if (!strncmp(line, "*JCLBegin:", 10) ||
+ !strncmp(line, "*JCLToPSInterpreter:", 20) ||
+ !strncmp(line, "*JCLEnd:", 8) ||
+ !strncmp(line, "*Protocols:", 11))
+ {
+ /*
+ * Don't use existing JCL keywords; we'll create our own, below...
+ */
+
+ cupsFilePrintf(dstfp, "*%% Commented out for CUPS Windows Driver...\n"
+ "*%%%s\n", line + 1);
+ continue;
+ }
+ else if (!strncmp(line, "*JCLOpenUI", 10))
+ {
+ jcloption = 1;
+ cupsFilePrintf(dstfp, "%s\n", line);
+ }
+ else if (!strncmp(line, "*JCLCloseUI", 11))
+ {
+ jcloption = 0;
+ cupsFilePrintf(dstfp, "%s\n", line);
+ }
+ else if (jcloption && !strncmp(line, "*OrderDependency:", 17))
+ {
+ for (ptr = line + 17; _cups_isspace(*ptr); ptr ++);
+
+ ptr = strchr(ptr, ' ');
+
+ if (ptr)
+ {
+ cupsFilePrintf(dstfp, "*OrderDependency: %d%s\n", jclorder, ptr);
+ jclorder ++;
+ }
+ else
+ cupsFilePrintf(dstfp, "%s\n", line);
+ }
+ else if (jcloption &&
+ strncmp(line, "*End", 4) &&
+ strncmp(line, "*Default", 8))
+ {
+ if ((ptr = strchr(line, ':')) == NULL)
+ {
+ snprintf(line, sizeof(line),
+ _cupsLangString(language, _("Missing value on line %d.")),
+ linenum);
+ _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, line, 0);
+
+ cupsFileClose(srcfp);
+ cupsFileClose(dstfp);
+
+ unlink(src);
+ unlink(buffer);
+
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ if ((ptr = strchr(ptr, '\"')) == NULL)
+ {
+ snprintf(line, sizeof(line),
+ _cupsLangString(language,
+ _("Missing double quote on line %d.")),
+ linenum);
+ _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, line, 0);
+
+ cupsFileClose(srcfp);
+ cupsFileClose(dstfp);
+
+ unlink(src);
+ unlink(buffer);
+
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ if (sscanf(line, "*%40s%*[ \t]%40[^:/]", option, choice) != 2)
+ {
+ snprintf(line, sizeof(line),
+ _cupsLangString(language,
+ _("Bad option + choice on line %d.")),
+ linenum);
+ _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, line, 0);
+
+ cupsFileClose(srcfp);
+ cupsFileClose(dstfp);
+
+ unlink(src);
+ unlink(buffer);
+
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ if (strchr(ptr + 1, '\"') == NULL)
+ {
+ /*
+ * Skip remaining...
+ */
+
+ while (cupsFileGets(srcfp, junk, sizeof(junk)) != NULL)
+ {
+ linenum ++;
+
+ if (!strncmp(junk, "*End", 4))
+ break;
+ }
+ }
+
+ snprintf(ptr + 1, sizeof(line) - (ptr - line + 1),
+ "%%cupsJobTicket: %s=%s\n\"\n*End", option, choice);
+
+ cupsFilePrintf(dstfp, "*%% Changed for CUPS Windows Driver...\n%s\n",
+ line);
+ }
+ else
+ cupsFilePrintf(dstfp, "%s\n", line);
+ }
+
+ cupsFileClose(srcfp);
+ unlink(src);
+
+ if (linenum == 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, _("Empty PPD file."), 1);
+
+ cupsFileClose(dstfp);
+ unlink(buffer);
+
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ /*
+ * Now add the CUPS-specific attributes and options...
+ */
+
+ cupsFilePuts(dstfp, "\n*% CUPS Job Ticket support and options...\n");
+ cupsFilePuts(dstfp, "*Protocols: PJL\n");
+ cupsFilePuts(dstfp, "*JCLBegin: \"%!PS-Adobe-3.0<0A>\"\n");
+ cupsFilePuts(dstfp, "*JCLToPSInterpreter: \"\"\n");
+ cupsFilePuts(dstfp, "*JCLEnd: \"\"\n");
+
+ cupsFilePuts(dstfp, "\n*OpenGroup: CUPS/CUPS Options\n\n");
+
+ if ((defattr = ippFindAttribute(response, "job-hold-until-default",
+ IPP_TAG_ZERO)) != NULL &&
+ (suppattr = ippFindAttribute(response, "job-hold-until-supported",
+ IPP_TAG_ZERO)) != NULL)
+ write_option(dstfp, jclorder ++, "cupsJobHoldUntil", "Hold Until",
+ "job-hold-until", suppattr, defattr, 0, 1);
+
+ if ((defattr = ippFindAttribute(response, "job-priority-default",
+ IPP_TAG_INTEGER)) != NULL &&
+ (suppattr = ippFindAttribute(response, "job-priority-supported",
+ IPP_TAG_RANGE)) != NULL)
+ write_option(dstfp, jclorder ++, "cupsJobPriority", "Priority",
+ "job-priority", suppattr, defattr, 0, 1);
+
+ if ((defattr = ippFindAttribute(response, "job-sheets-default",
+ IPP_TAG_ZERO)) != NULL &&
+ (suppattr = ippFindAttribute(response, "job-sheets-supported",
+ IPP_TAG_ZERO)) != NULL)
+ {
+ write_option(dstfp, jclorder ++, "cupsJobSheetsStart", "Start Banner",
+ "job-sheets", suppattr, defattr, 0, 2);
+ write_option(dstfp, jclorder, "cupsJobSheetsEnd", "End Banner",
+ "job-sheets", suppattr, defattr, 1, 2);
+ }
+
+ cupsFilePuts(dstfp, "*CloseGroup: CUPS\n");
+ cupsFileClose(dstfp);
+
+ ippDelete(response);
+
+ return (buffer);
+}
+
+
+/*
+ * 'cupsAdminExportSamba()' - Export a printer to Samba.
+ *
+ * @deprecated@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsAdminExportSamba(
+ const char *dest, /* I - Destination to export */
+ const char *ppd, /* I - PPD file */
+ const char *samba_server, /* I - Samba server */
+ const char *samba_user, /* I - Samba username */
+ const char *samba_password, /* I - Samba password */
+ FILE *logfile) /* I - Log file, if any */
+{
+ int status; /* Status of Samba commands */
+ int have_drivers; /* Have drivers? */
+ char file[1024], /* File to test for */
+ authfile[1024], /* Temporary authentication file */
+ address[1024], /* Address for command */
+ subcmd[1024], /* Sub-command */
+ message[1024]; /* Error message */
+ cups_file_t *fp; /* Authentication file */
+ cups_lang_t *language; /* Current language */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!dest || !ppd || !samba_server || !samba_user || !samba_password)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Create a temporary authentication file for Samba...
+ */
+
+ if ((fp = cupsTempFile2(authfile, sizeof(authfile))) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+ return (0);
+ }
+
+ cupsFilePrintf(fp, "username = %s\n", samba_user);
+ cupsFilePrintf(fp, "password = %s\n", samba_password);
+ cupsFileClose(fp);
+
+ /*
+ * See which drivers are available; the new CUPS v6 and Adobe drivers
+ * depend on the Windows 2k PS driver, so copy that driver first:
+ *
+ * Files:
+ *
+ * ps5ui.dll
+ * pscript.hlp
+ * pscript.ntf
+ * pscript5.dll
+ */
+
+ have_drivers = 0;
+ language = cupsLangDefault();
+
+ snprintf(file, sizeof(file), "%s/drivers/pscript5.dll", cg->cups_datadir);
+ if (!access(file, 0))
+ {
+ have_drivers |= 1;
+
+ /*
+ * Windows 2k driver is installed; do the smbclient commands needed
+ * to copy the Win2k drivers over...
+ */
+
+ snprintf(address, sizeof(address), "//%s/print$", samba_server);
+
+ snprintf(subcmd, sizeof(subcmd),
+ "mkdir W32X86;"
+ "put %s W32X86/%s.ppd;"
+ "put %s/drivers/ps5ui.dll W32X86/ps5ui.dll;"
+ "put %s/drivers/pscript.hlp W32X86/pscript.hlp;"
+ "put %s/drivers/pscript.ntf W32X86/pscript.ntf;"
+ "put %s/drivers/pscript5.dll W32X86/pscript5.dll",
+ ppd, dest, cg->cups_datadir, cg->cups_datadir,
+ cg->cups_datadir, cg->cups_datadir);
+
+ if ((status = do_samba_command("smbclient", address, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to copy Windows 2000 printer "
+ "driver files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ /*
+ * See if we also have the CUPS driver files; if so, use them!
+ */
+
+ snprintf(file, sizeof(file), "%s/drivers/cupsps6.dll", cg->cups_datadir);
+ if (!access(file, 0))
+ {
+ /*
+ * Copy the CUPS driver files over...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "put %s/drivers/cups6.ini W32X86/cups6.ini;"
+ "put %s/drivers/cupsps6.dll W32X86/cupsps6.dll;"
+ "put %s/drivers/cupsui6.dll W32X86/cupsui6.dll",
+ cg->cups_datadir, cg->cups_datadir, cg->cups_datadir);
+
+ if ((status = do_samba_command("smbclient", address, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to copy CUPS printer driver "
+ "files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ /*
+ * Do the rpcclient command needed for the CUPS drivers...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "adddriver \"Windows NT x86\" \"%s:"
+ "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
+ "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf,"
+ "cups6.ini,cupsps6.dll,cupsui6.dll\"",
+ dest, dest, dest);
+ }
+ else
+ {
+ /*
+ * Don't have the CUPS drivers, so just use the standard Windows
+ * drivers...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "adddriver \"Windows NT x86\" \"%s:"
+ "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
+ "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf\"",
+ dest, dest, dest);
+ }
+
+ if ((status = do_samba_command("rpcclient", samba_server, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to install Windows 2000 printer "
+ "driver files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+ }
+
+ /*
+ * See if we have the Win9x PS driver...
+ */
+
+ snprintf(file, sizeof(file), "%s/drivers/ADOBEPS4.DRV", cg->cups_datadir);
+ if (!access(file, 0))
+ {
+ have_drivers |= 2;
+
+ /*
+ * Do the smbclient commands needed for the Adobe Win9x drivers...
+ */
+
+ snprintf(address, sizeof(address), "//%s/print$", samba_server);
+
+ snprintf(subcmd, sizeof(subcmd),
+ "mkdir WIN40;"
+ "put %s WIN40/%s.PPD;"
+ "put %s/drivers/ADFONTS.MFM WIN40/ADFONTS.MFM;"
+ "put %s/drivers/ADOBEPS4.DRV WIN40/ADOBEPS4.DRV;"
+ "put %s/drivers/ADOBEPS4.HLP WIN40/ADOBEPS4.HLP;"
+ "put %s/drivers/ICONLIB.DLL WIN40/ICONLIB.DLL;"
+ "put %s/drivers/PSMON.DLL WIN40/PSMON.DLL;",
+ ppd, dest, cg->cups_datadir, cg->cups_datadir,
+ cg->cups_datadir, cg->cups_datadir, cg->cups_datadir);
+
+ if ((status = do_samba_command("smbclient", address, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to copy Windows 9x printer "
+ "driver files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ /*
+ * Do the rpcclient commands needed for the Adobe Win9x drivers...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "adddriver \"Windows 4.0\" \"%s:ADOBEPS4.DRV:%s.PPD:NULL:"
+ "ADOBEPS4.HLP:PSMON.DLL:RAW:"
+ "ADOBEPS4.DRV,%s.PPD,ADOBEPS4.HLP,PSMON.DLL,ADFONTS.MFM,"
+ "ICONLIB.DLL\"",
+ dest, dest, dest);
+
+ if ((status = do_samba_command("rpcclient", samba_server, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to install Windows 9x printer "
+ "driver files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+ }
+
+ /*
+ * See if we have the 64-bit Windows PS driver...
+ *
+ * Files:
+ *
+ * x64/ps5ui.dll
+ * x64/pscript.hlp
+ * x64/pscript.ntf
+ * x64/pscript5.dll
+ */
+
+ snprintf(file, sizeof(file), "%s/drivers/x64/pscript5.dll", cg->cups_datadir);
+ if (!access(file, 0))
+ {
+ have_drivers |= 4;
+
+ /*
+ * 64-bit Windows driver is installed; do the smbclient commands needed
+ * to copy the Win64 drivers over...
+ */
+
+ snprintf(address, sizeof(address), "//%s/print$", samba_server);
+
+ snprintf(subcmd, sizeof(subcmd),
+ "mkdir x64;"
+ "put %s x64/%s.ppd;"
+ "put %s/drivers/x64/ps5ui.dll x64/ps5ui.dll;"
+ "put %s/drivers/x64/pscript.hlp x64/pscript.hlp;"
+ "put %s/drivers/x64/pscript.ntf x64/pscript.ntf;"
+ "put %s/drivers/x64/pscript5.dll x64/pscript5.dll",
+ ppd, dest, cg->cups_datadir, cg->cups_datadir,
+ cg->cups_datadir, cg->cups_datadir);
+
+ if ((status = do_samba_command("smbclient", address, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to copy 64-bit Windows printer "
+ "driver files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ /*
+ * See if we also have the CUPS driver files; if so, use them!
+ */
+
+ snprintf(file, sizeof(file), "%s/drivers/x64/cupsps6.dll", cg->cups_datadir);
+ if (!access(file, 0))
+ {
+ /*
+ * Copy the CUPS driver files over...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "put %s/drivers/x64/cups6.ini x64/cups6.ini;"
+ "put %s/drivers/x64/cupsps6.dll x64/cupsps6.dll;"
+ "put %s/drivers/x64/cupsui6.dll x64/cupsui6.dll",
+ cg->cups_datadir, cg->cups_datadir, cg->cups_datadir);
+
+ if ((status = do_samba_command("smbclient", address, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to copy 64-bit CUPS printer driver "
+ "files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ /*
+ * Do the rpcclient command needed for the CUPS drivers...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "adddriver \"Windows x64\" \"%s:"
+ "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
+ "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf,"
+ "cups6.ini,cupsps6.dll,cupsui6.dll\"",
+ dest, dest, dest);
+ }
+ else
+ {
+ /*
+ * Don't have the CUPS drivers, so just use the standard Windows
+ * drivers...
+ */
+
+ snprintf(subcmd, sizeof(subcmd),
+ "adddriver \"Windows x64\" \"%s:"
+ "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
+ "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf\"",
+ dest, dest, dest);
+ }
+
+ if ((status = do_samba_command("rpcclient", samba_server, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to install Windows 2000 printer "
+ "driver files (%d).")), status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+ }
+
+ if (logfile && !(have_drivers & 1))
+ {
+ if (!have_drivers)
+ strlcpy(message,
+ _cupsLangString(language,
+ _("No Windows printer drivers are installed.")),
+ sizeof(message));
+ else
+ strlcpy(message,
+ _cupsLangString(language,
+ _("Warning, no Windows 2000 printer drivers "
+ "are installed.")),
+ sizeof(message));
+
+ _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, message, 0);
+ _cupsLangPuts(logfile, message);
+ }
+
+ if (have_drivers == 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, message, 0);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ /*
+ * Finally, associate the drivers we just added with the queue...
+ */
+
+ snprintf(subcmd, sizeof(subcmd), "setdriver %s %s", dest, dest);
+
+ if ((status = do_samba_command("rpcclient", samba_server, subcmd,
+ authfile, logfile)) != 0)
+ {
+ snprintf(message, sizeof(message),
+ _cupsLangString(language,
+ _("Unable to set Windows printer driver (%d).")),
+ status);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ if (logfile)
+ _cupsLangPuts(logfile, message);
+
+ unlink(authfile);
+
+ return (0);
+ }
+
+ unlink(authfile);
+
+ return (1);
+}
+
+
+/*
+ * 'cupsAdminGetServerSettings()' - Get settings from the server.
+ *
+ * The returned settings should be freed with cupsFreeOptions() when
+ * you are done with them.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsAdminGetServerSettings(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ int *num_settings, /* O - Number of settings */
+ cups_option_t **settings) /* O - Settings */
+{
+ int i; /* Looping var */
+ cups_file_t *cupsd; /* cupsd.conf file */
+ char cupsdconf[1024]; /* cupsd.conf filename */
+ int remote; /* Remote cupsd.conf file? */
+ http_status_t status; /* Status of getting cupsd.conf */
+ char line[1024], /* Line from cupsd.conf file */
+ *value; /* Value on line */
+ cups_option_t *setting; /* Current setting */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http)
+ {
+ /*
+ * See if we are connected to the same server...
+ */
+
+ if (cg->http)
+ {
+ /*
+ * Compare the connection hostname, port, and encryption settings to
+ * the cached defaults; these were initialized the first time we
+ * connected...
+ */
+
+ if (strcmp(cg->http->hostname, cg->server) ||
+ cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
+ (cg->http->encryption != cg->encryption &&
+ cg->http->encryption == HTTP_ENCRYPTION_NEVER))
+ {
+ /*
+ * Need to close the current connection because something has changed...
+ */
+
+ httpClose(cg->http);
+ cg->http = NULL;
+ }
+ }
+
+ /*
+ * (Re)connect as needed...
+ */
+
+ if (!cg->http)
+ {
+ if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
+ cupsEncryption(), 1, 0, NULL)) == NULL)
+ {
+ if (errno)
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0);
+ else
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE,
+ _("Unable to connect to host."), 1);
+
+ if (num_settings)
+ *num_settings = 0;
+
+ if (settings)
+ *settings = NULL;
+
+ return (0);
+ }
+ }
+
+ http = cg->http;
+ }
+
+ if (!http || !num_settings || !settings)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ if (num_settings)
+ *num_settings = 0;
+
+ if (settings)
+ *settings = NULL;
+
+ return (0);
+ }
+
+ *num_settings = 0;
+ *settings = NULL;
+
+ /*
+ * Get the cupsd.conf file...
+ */
+
+ if ((status = get_cupsd_conf(http, cg, cg->cupsd_update, cupsdconf,
+ sizeof(cupsdconf), &remote)) == HTTP_STATUS_OK)
+ {
+ if ((cupsd = cupsFileOpen(cupsdconf, "r")) == NULL)
+ {
+ char message[1024]; /* Message string */
+
+
+ snprintf(message, sizeof(message),
+ _cupsLangString(cupsLangDefault(), _("Open of %s failed: %s")),
+ cupsdconf, strerror(errno));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+ }
+ }
+ else
+ cupsd = NULL;
+
+ if (cupsd)
+ {
+ /*
+ * Read the file, keeping track of what settings are enabled...
+ */
+
+ int remote_access = 0, /* Remote access allowed? */
+ remote_admin = 0, /* Remote administration allowed? */
+ remote_any = 0, /* Remote access from anywhere allowed? */
+ browsing = 1, /* Browsing enabled? */
+ cancel_policy = 1, /* Cancel-job policy set? */
+ debug_logging = 0; /* LogLevel debug set? */
+ int linenum = 0, /* Line number in file */
+ in_location = 0, /* In a location section? */
+ in_policy = 0, /* In a policy section? */
+ in_cancel_job = 0, /* In a cancel-job section? */
+ in_admin_location = 0; /* In the /admin location? */
+
+
+ invalidate_cupsd_cache(cg);
+
+ cg->cupsd_update = time(NULL);
+ httpGetHostname(http, cg->cupsd_hostname, sizeof(cg->cupsd_hostname));
+
+ while (cupsFileGetConf(cupsd, line, sizeof(line), &value, &linenum))
+ {
+ if (!value && strncmp(line, "</", 2))
+ value = line + strlen(line);
+
+ if ((!_cups_strcasecmp(line, "Port") || !_cups_strcasecmp(line, "Listen")) && value)
+ {
+ char *port; /* Pointer to port number, if any */
+
+
+ if ((port = strrchr(value, ':')) != NULL)
+ *port = '\0';
+ else if (isdigit(*value & 255))
+ {
+ /*
+ * Listen on a port number implies remote access...
+ */
+
+ remote_access = 1;
+ continue;
+ }
+
+ if (_cups_strcasecmp(value, "localhost") && strcmp(value, "127.0.0.1")
+#ifdef AF_LOCAL
+ && *value != '/'
+#endif /* AF_LOCAL */
+#ifdef AF_INET6
+ && strcmp(value, "[::1]")
+#endif /* AF_INET6 */
+ )
+ remote_access = 1;
+ }
+ else if (!_cups_strcasecmp(line, "Browsing"))
+ {
+ browsing = !_cups_strcasecmp(value, "yes") ||
+ !_cups_strcasecmp(value, "on") ||
+ !_cups_strcasecmp(value, "true");
+ }
+ else if (!_cups_strcasecmp(line, "LogLevel"))
+ {
+ debug_logging = !_cups_strncasecmp(value, "debug", 5);
+ }
+ else if (!_cups_strcasecmp(line, "<Policy") &&
+ !_cups_strcasecmp(value, "default"))
+ {
+ in_policy = 1;
+ }
+ else if (!_cups_strcasecmp(line, "</Policy>"))
+ {
+ in_policy = 0;
+ }
+ else if (!_cups_strcasecmp(line, "<Limit") && in_policy && value)
+ {
+ /*
+ * See if the policy limit is for the Cancel-Job operation...
+ */
+
+ char *valptr; /* Pointer into value */
+
+
+ while (*value)
+ {
+ for (valptr = value; *valptr && !_cups_isspace(*valptr); valptr ++);
+
+ if (*valptr)
+ *valptr++ = '\0';
+
+ if (!_cups_strcasecmp(value, "cancel-job") ||
+ !_cups_strcasecmp(value, "all"))
+ {
+ in_cancel_job = 1;
+ break;
+ }
+
+ for (value = valptr; _cups_isspace(*value); value ++);
+ }
+ }
+ else if (!_cups_strcasecmp(line, "</Limit>"))
+ {
+ in_cancel_job = 0;
+ }
+ else if (!_cups_strcasecmp(line, "Require") && in_cancel_job)
+ {
+ cancel_policy = 0;
+ }
+ else if (!_cups_strcasecmp(line, "<Location") && value)
+ {
+ in_admin_location = !_cups_strcasecmp(value, "/admin");
+ in_location = 1;
+ }
+ else if (!_cups_strcasecmp(line, "</Location>"))
+ {
+ in_admin_location = 0;
+ in_location = 0;
+ }
+ else if (!_cups_strcasecmp(line, "Allow") && value &&
+ _cups_strcasecmp(value, "localhost") &&
+ _cups_strcasecmp(value, "127.0.0.1")
+#ifdef AF_LOCAL
+ && *value != '/'
+#endif /* AF_LOCAL */
+#ifdef AF_INET6
+ && strcmp(value, "::1")
+#endif /* AF_INET6 */
+ )
+ {
+ if (in_admin_location)
+ remote_admin = 1;
+ else if (!_cups_strcasecmp(value, "all"))
+ remote_any = 1;
+ }
+ else if (line[0] != '<' && !in_location && !in_policy &&
+ _cups_strcasecmp(line, "Allow") &&
+ _cups_strcasecmp(line, "AuthType") &&
+ _cups_strcasecmp(line, "Deny") &&
+ _cups_strcasecmp(line, "Order") &&
+ _cups_strcasecmp(line, "Require") &&
+ _cups_strcasecmp(line, "Satisfy"))
+ cg->cupsd_num_settings = cupsAddOption(line, value,
+ cg->cupsd_num_settings,
+ &(cg->cupsd_settings));
+ }
+
+ cupsFileClose(cupsd);
+
+ cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
+ debug_logging ? "1" : "0",
+ cg->cupsd_num_settings,
+ &(cg->cupsd_settings));
+
+ cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
+ (remote_access && remote_admin) ?
+ "1" : "0",
+ cg->cupsd_num_settings,
+ &(cg->cupsd_settings));
+
+ cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
+ remote_any ? "1" : "0",
+ cg->cupsd_num_settings,
+ &(cg->cupsd_settings));
+
+ cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
+ (remote_access && browsing) ? "1" :
+ "0",
+ cg->cupsd_num_settings,
+ &(cg->cupsd_settings));
+
+ cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
+ cancel_policy ? "1" : "0",
+ cg->cupsd_num_settings,
+ &(cg->cupsd_settings));
+ }
+ else if (status != HTTP_STATUS_NOT_MODIFIED)
+ invalidate_cupsd_cache(cg);
+
+ /*
+ * Remove any temporary files and copy the settings array...
+ */
+
+ if (remote)
+ unlink(cupsdconf);
+
+ for (i = cg->cupsd_num_settings, setting = cg->cupsd_settings;
+ i > 0;
+ i --, setting ++)
+ *num_settings = cupsAddOption(setting->name, setting->value,
+ *num_settings, settings);
+
+ return (cg->cupsd_num_settings > 0);
+}
+
+
+/*
+ * 'cupsAdminSetServerSettings()' - Set settings on the server.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsAdminSetServerSettings(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ int num_settings, /* I - Number of settings */
+ cups_option_t *settings) /* I - Settings */
+{
+ int i; /* Looping var */
+ http_status_t status; /* GET/PUT status */
+ const char *server_port_env; /* SERVER_PORT env var */
+ int server_port; /* IPP port for server */
+ cups_file_t *cupsd; /* cupsd.conf file */
+ char cupsdconf[1024]; /* cupsd.conf filename */
+ int remote; /* Remote cupsd.conf file? */
+ char tempfile[1024]; /* Temporary new cupsd.conf */
+ cups_file_t *temp; /* Temporary file */
+ char line[1024], /* Line from cupsd.conf file */
+ *value; /* Value on line */
+ int linenum, /* Line number in file */
+ in_location, /* In a location section? */
+ in_policy, /* In a policy section? */
+ in_default_policy, /* In the default policy section? */
+ in_cancel_job, /* In a cancel-job section? */
+ in_admin_location, /* In the /admin location? */
+ in_conf_location, /* In the /admin/conf location? */
+ in_root_location; /* In the / location? */
+ const char *val; /* Setting value */
+ int share_printers, /* Share local printers */
+ remote_admin, /* Remote administration allowed? */
+ remote_any, /* Remote access from anywhere? */
+ user_cancel_any, /* Cancel-job policy set? */
+ debug_logging; /* LogLevel debug set? */
+ int wrote_port_listen, /* Wrote the port/listen lines? */
+ wrote_browsing, /* Wrote the browsing lines? */
+ wrote_policy, /* Wrote the policy? */
+ wrote_loglevel, /* Wrote the LogLevel line? */
+ wrote_admin_location, /* Wrote the /admin location? */
+ wrote_conf_location, /* Wrote the /admin/conf location? */
+ wrote_root_location; /* Wrote the / location? */
+ int indent; /* Indentation */
+ int cupsd_num_settings; /* New number of settings */
+ int old_share_printers, /* Share local printers */
+ old_remote_admin, /* Remote administration allowed? */
+ old_user_cancel_any, /* Cancel-job policy set? */
+ old_debug_logging; /* LogLevel debug set? */
+ cups_option_t *cupsd_settings, /* New settings */
+ *setting; /* Current setting */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http)
+ http = _cupsConnect();
+
+ if (!http || !num_settings || !settings)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (0);
+ }
+
+ /*
+ * Get the cupsd.conf file...
+ */
+
+ if (get_cupsd_conf(http, cg, 0, cupsdconf, sizeof(cupsdconf),
+ &remote) == HTTP_STATUS_OK)
+ {
+ if ((cupsd = cupsFileOpen(cupsdconf, "r")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+ return (0);
+ }
+ }
+ else
+ return (0);
+
+ /*
+ * Get current settings...
+ */
+
+ if (!cupsAdminGetServerSettings(http, &cupsd_num_settings,
+ &cupsd_settings))
+ return (0);
+
+ if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, cupsd_num_settings,
+ cupsd_settings)) != NULL)
+ old_debug_logging = atoi(val);
+ else
+ old_debug_logging = 0;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: old debug_logging=%d",
+ old_debug_logging));
+
+ if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, cupsd_num_settings,
+ cupsd_settings)) != NULL)
+ old_remote_admin = atoi(val);
+ else
+ old_remote_admin = 0;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: old remote_admin=%d",
+ old_remote_admin));
+
+ if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, cupsd_num_settings,
+ cupsd_settings)) != NULL)
+ remote_any = atoi(val);
+ else
+ remote_any = 0;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: old remote_any=%d",
+ remote_any));
+
+ if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, cupsd_num_settings,
+ cupsd_settings)) != NULL)
+ old_share_printers = atoi(val);
+ else
+ old_share_printers = 0;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: old share_printers=%d",
+ old_share_printers));
+
+ if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, cupsd_num_settings,
+ cupsd_settings)) != NULL)
+ old_user_cancel_any = atoi(val);
+ else
+ old_user_cancel_any = 0;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: old user_cancel_any=%d",
+ old_user_cancel_any));
+
+ cupsFreeOptions(cupsd_num_settings, cupsd_settings);
+
+ /*
+ * Get basic settings...
+ */
+
+ if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
+ settings)) != NULL)
+ {
+ debug_logging = atoi(val);
+
+ if (debug_logging == old_debug_logging)
+ {
+ /*
+ * No change to this setting...
+ */
+
+ debug_logging = -1;
+ }
+ }
+ else
+ debug_logging = -1;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: debug_logging=%d",
+ debug_logging));
+
+ if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
+ settings)) != NULL)
+ remote_any = atoi(val);
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: remote_any=%d",
+ remote_any));
+
+ if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
+ settings)) != NULL)
+ {
+ remote_admin = atoi(val);
+
+ if (remote_admin == old_remote_admin)
+ {
+ /*
+ * No change to this setting...
+ */
+
+ remote_admin = -1;
+ }
+ }
+ else
+ remote_admin = -1;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: remote_admin=%d",
+ remote_admin));
+
+ if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
+ settings)) != NULL)
+ {
+ share_printers = atoi(val);
+
+ if (share_printers == old_share_printers)
+ {
+ /*
+ * No change to this setting...
+ */
+
+ share_printers = -1;
+ }
+ }
+ else
+ share_printers = -1;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: share_printers=%d",
+ share_printers));
+
+ if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
+ settings)) != NULL)
+ {
+ user_cancel_any = atoi(val);
+
+ if (user_cancel_any == old_user_cancel_any)
+ {
+ /*
+ * No change to this setting...
+ */
+
+ user_cancel_any = -1;
+ }
+ }
+ else
+ user_cancel_any = -1;
+
+ DEBUG_printf(("1cupsAdminSetServerSettings: user_cancel_any=%d",
+ user_cancel_any));
+
+ /*
+ * Create a temporary file for the new cupsd.conf file...
+ */
+
+ if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
+ {
+ cupsFileClose(cupsd);
+
+ if (remote)
+ unlink(cupsdconf);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+ return (0);
+ }
+
+ /*
+ * Copy the old file to the new, making changes along the way...
+ */
+
+ cupsd_num_settings = 0;
+ in_admin_location = 0;
+ in_cancel_job = 0;
+ in_conf_location = 0;
+ in_default_policy = 0;
+ in_location = 0;
+ in_policy = 0;
+ in_root_location = 0;
+ linenum = 0;
+ wrote_admin_location = 0;
+ wrote_browsing = 0;
+ wrote_conf_location = 0;
+ wrote_loglevel = 0;
+ wrote_policy = 0;
+ wrote_port_listen = 0;
+ wrote_root_location = 0;
+ indent = 0;
+
+ if ((server_port_env = getenv("SERVER_PORT")) != NULL)
+ {
+ if ((server_port = atoi(server_port_env)) <= 0)
+ server_port = ippPort();
+ }
+ else
+ server_port = ippPort();
+
+ if (server_port <= 0)
+ server_port = IPP_PORT;
+
+ while (cupsFileGetConf(cupsd, line, sizeof(line), &value, &linenum))
+ {
+ if ((!_cups_strcasecmp(line, "Port") || !_cups_strcasecmp(line, "Listen")) &&
+ (remote_admin >= 0 || remote_any > 0 || share_printers >= 0))
+ {
+ if (!wrote_port_listen)
+ {
+ wrote_port_listen = 1;
+
+ if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
+ {
+ cupsFilePuts(temp, "# Allow remote access\n");
+ cupsFilePrintf(temp, "Port %d\n", server_port);
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Only listen for connections from the local "
+ "machine.\n");
+ cupsFilePrintf(temp, "Listen localhost:%d\n", server_port);
+ }
+
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+ if ((!value || strcmp(CUPS_DEFAULT_DOMAINSOCKET, value)) &&
+ !access(CUPS_DEFAULT_DOMAINSOCKET, 0))
+ cupsFilePuts(temp, "Listen " CUPS_DEFAULT_DOMAINSOCKET "\n");
+#endif /* CUPS_DEFAULT_DOMAINSOCKET */
+ }
+ else if (value && value[0] == '/'
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+ && strcmp(CUPS_DEFAULT_DOMAINSOCKET, value)
+#endif /* CUPS_DEFAULT_DOMAINSOCKET */
+ )
+ cupsFilePrintf(temp, "Listen %s\n", value);
+ }
+ else if ((!_cups_strcasecmp(line, "Browsing") ||
+ !_cups_strcasecmp(line, "BrowseLocalProtocols")) &&
+ share_printers >= 0)
+ {
+ if (!wrote_browsing)
+ {
+ int new_share_printers = (share_printers > 0 ||
+ (share_printers == -1 &&
+ old_share_printers > 0));
+
+ wrote_browsing = 1;
+
+ if (new_share_printers)
+ {
+ const char *localp = cupsGetOption("BrowseLocalProtocols",
+ num_settings, settings);
+
+ if (!localp || !localp[0])
+ localp = cupsGetOption("BrowseLocalProtocols", cupsd_num_settings,
+ cupsd_settings);
+
+ cupsFilePuts(temp, "# Share local printers on the local network.\n");
+ cupsFilePuts(temp, "Browsing On\n");
+
+ if (!localp)
+ localp = CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS;
+
+ cupsFilePrintf(temp, "BrowseLocalProtocols %s\n", localp);
+
+ cupsd_num_settings = cupsAddOption("BrowseLocalProtocols", localp,
+ cupsd_num_settings,
+ &cupsd_settings);
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Disable printer sharing.\n");
+ cupsFilePuts(temp, "Browsing Off\n");
+ }
+ }
+ }
+ else if (!_cups_strcasecmp(line, "LogLevel") && debug_logging >= 0)
+ {
+ wrote_loglevel = 1;
+
+ if (debug_logging)
+ {
+ cupsFilePuts(temp,
+ "# Show troubleshooting information in error_log.\n");
+ cupsFilePuts(temp, "LogLevel debug\n");
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Show general information in error_log.\n");
+ cupsFilePuts(temp, "LogLevel " CUPS_DEFAULT_LOG_LEVEL "\n");
+ }
+ }
+ else if (!_cups_strcasecmp(line, "<Policy"))
+ {
+ in_default_policy = !_cups_strcasecmp(value, "default");
+ in_policy = 1;
+
+ cupsFilePrintf(temp, "%s %s>\n", line, value);
+ indent += 2;
+ }
+ else if (!_cups_strcasecmp(line, "</Policy>"))
+ {
+ indent -= 2;
+ if (!wrote_policy && in_default_policy)
+ {
+ wrote_policy = 1;
+
+ if (!user_cancel_any)
+ cupsFilePuts(temp, " # Only the owner or an administrator can "
+ "cancel a job...\n"
+ " <Limit Cancel-Job>\n"
+ " Order deny,allow\n"
+ " Require user @OWNER "
+ CUPS_DEFAULT_PRINTOPERATOR_AUTH "\n"
+ " </Limit>\n");
+ }
+
+ in_policy = 0;
+ in_default_policy = 0;
+
+ cupsFilePuts(temp, "</Policy>\n");
+ }
+ else if (!_cups_strcasecmp(line, "<Location"))
+ {
+ in_location = 1;
+ indent += 2;
+ if (!strcmp(value, "/admin"))
+ in_admin_location = 1;
+ if (!strcmp(value, "/admin/conf"))
+ in_conf_location = 1;
+ else if (!strcmp(value, "/"))
+ in_root_location = 1;
+
+ cupsFilePrintf(temp, "%s %s>\n", line, value);
+ }
+ else if (!_cups_strcasecmp(line, "</Location>"))
+ {
+ in_location = 0;
+ indent -= 2;
+ if (in_admin_location && remote_admin >= 0)
+ {
+ wrote_admin_location = 1;
+
+ if (remote_admin)
+ cupsFilePuts(temp, " # Allow remote administration...\n");
+ else if (remote_admin == 0)
+ cupsFilePuts(temp, " # Restrict access to the admin pages...\n");
+
+ cupsFilePuts(temp, " Order allow,deny\n");
+
+ if (remote_admin)
+ cupsFilePrintf(temp, " Allow %s\n",
+ remote_any > 0 ? "all" : "@LOCAL");
+ }
+ else if (in_conf_location && remote_admin >= 0)
+ {
+ wrote_conf_location = 1;
+
+ if (remote_admin)
+ cupsFilePuts(temp, " # Allow remote access to the configuration "
+ "files...\n");
+ else
+ cupsFilePuts(temp, " # Restrict access to the configuration "
+ "files...\n");
+
+ cupsFilePuts(temp, " Order allow,deny\n");
+
+ if (remote_admin)
+ cupsFilePrintf(temp, " Allow %s\n",
+ remote_any > 0 ? "all" : "@LOCAL");
+ }
+ else if (in_root_location &&
+ (remote_admin >= 0 || remote_any > 0 || share_printers >= 0))
+ {
+ wrote_root_location = 1;
+
+ if (remote_admin > 0 && share_printers > 0)
+ cupsFilePuts(temp, " # Allow shared printing and remote "
+ "administration...\n");
+ else if (remote_admin > 0)
+ cupsFilePuts(temp, " # Allow remote administration...\n");
+ else if (share_printers > 0)
+ cupsFilePuts(temp, " # Allow shared printing...\n");
+ else if (remote_any > 0)
+ cupsFilePuts(temp, " # Allow remote access...\n");
+ else
+ cupsFilePuts(temp, " # Restrict access to the server...\n");
+
+ cupsFilePuts(temp, " Order allow,deny\n");
+
+ if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
+ cupsFilePrintf(temp, " Allow %s\n",
+ remote_any > 0 ? "all" : "@LOCAL");
+ }
+
+ in_admin_location = 0;
+ in_conf_location = 0;
+ in_root_location = 0;
+
+ cupsFilePuts(temp, "</Location>\n");
+ }
+ else if (!_cups_strcasecmp(line, "<Limit"))
+ {
+ if (in_default_policy)
+ {
+ /*
+ * See if the policy limit is for the Cancel-Job operation...
+ */
+
+ char *valptr; /* Pointer into value */
+
+
+ if (!_cups_strcasecmp(value, "cancel-job") && user_cancel_any >= 0)
+ {
+ /*
+ * Don't write anything for this limit section...
+ */
+
+ in_cancel_job = 2;
+ }
+ else
+ {
+ cupsFilePrintf(temp, "%*s%s", indent, "", line);
+
+ while (*value)
+ {
+ for (valptr = value; *valptr && !_cups_isspace(*valptr); valptr ++);
+
+ if (*valptr)
+ *valptr++ = '\0';
+
+ if (!_cups_strcasecmp(value, "cancel-job") && user_cancel_any >= 0)
+ {
+ /*
+ * Write everything except for this definition...
+ */
+
+ in_cancel_job = 1;
+ }
+ else
+ cupsFilePrintf(temp, " %s", value);
+
+ for (value = valptr; _cups_isspace(*value); value ++);
+ }
+
+ cupsFilePuts(temp, ">\n");
+ }
+ }
+ else
+ cupsFilePrintf(temp, "%*s%s %s>\n", indent, "", line, value);
+
+ indent += 2;
+ }
+ else if (!_cups_strcasecmp(line, "</Limit>") && in_cancel_job)
+ {
+ indent -= 2;
+
+ if (in_cancel_job == 1)
+ cupsFilePuts(temp, " </Limit>\n");
+
+ wrote_policy = 1;
+
+ if (!user_cancel_any)
+ cupsFilePuts(temp, " # Only the owner or an administrator can cancel "
+ "a job...\n"
+ " <Limit Cancel-Job>\n"
+ " Order deny,allow\n"
+ " Require user @OWNER "
+ CUPS_DEFAULT_PRINTOPERATOR_AUTH "\n"
+ " </Limit>\n");
+
+ in_cancel_job = 0;
+ }
+ else if ((((in_admin_location || in_conf_location || in_root_location) &&
+ (remote_admin >= 0 || remote_any > 0)) ||
+ (in_root_location && share_printers >= 0)) &&
+ (!_cups_strcasecmp(line, "Allow") || !_cups_strcasecmp(line, "Deny") ||
+ !_cups_strcasecmp(line, "Order")))
+ continue;
+ else if (in_cancel_job == 2)
+ continue;
+ else if (line[0] == '<')
+ {
+ if (value)
+ {
+ cupsFilePrintf(temp, "%*s%s %s>\n", indent, "", line, value);
+ indent += 2;
+ }
+ else
+ {
+ if (line[1] == '/')
+ indent -= 2;
+
+ cupsFilePrintf(temp, "%*s%s\n", indent, "", line);
+ }
+ }
+ else if (!in_policy && !in_location &&
+ (val = cupsGetOption(line, num_settings, settings)) != NULL)
+ {
+ /*
+ * Replace this directive's value with the new one...
+ */
+
+ cupsd_num_settings = cupsAddOption(line, val, cupsd_num_settings,
+ &cupsd_settings);
+
+ /*
+ * Write the new value in its place, without indentation since we
+ * only support setting root directives, not in sections...
+ */
+
+ cupsFilePrintf(temp, "%s %s\n", line, val);
+ }
+ else if (value)
+ {
+ if (!in_policy && !in_location)
+ {
+ /*
+ * Record the non-policy, non-location directives that we find
+ * in the server settings, since we cache this info and record it
+ * in cupsAdminGetServerSettings()...
+ */
+
+ cupsd_num_settings = cupsAddOption(line, value, cupsd_num_settings,
+ &cupsd_settings);
+ }
+
+ cupsFilePrintf(temp, "%*s%s %s\n", indent, "", line, value);
+ }
+ else
+ cupsFilePrintf(temp, "%*s%s\n", indent, "", line);
+ }
+
+ /*
+ * Write any missing info...
+ */
+
+ if (!wrote_browsing && share_printers >= 0)
+ {
+ if (share_printers > 0)
+ {
+ cupsFilePuts(temp, "# Share local printers on the local network.\n");
+ cupsFilePuts(temp, "Browsing On\n");
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Disable printer sharing and shared printers.\n");
+ cupsFilePuts(temp, "Browsing Off\n");
+ }
+ }
+
+ if (!wrote_loglevel && debug_logging >= 0)
+ {
+ if (debug_logging)
+ {
+ cupsFilePuts(temp, "# Show troubleshooting information in error_log.\n");
+ cupsFilePuts(temp, "LogLevel debug\n");
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Show general information in error_log.\n");
+ cupsFilePuts(temp, "LogLevel " CUPS_DEFAULT_LOG_LEVEL "\n");
+ }
+ }
+
+ if (!wrote_port_listen &&
+ (remote_admin >= 0 || remote_any > 0 || share_printers >= 0))
+ {
+ if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
+ {
+ cupsFilePuts(temp, "# Allow remote access\n");
+ cupsFilePrintf(temp, "Port %d\n", ippPort());
+ }
+ else
+ {
+ cupsFilePuts(temp,
+ "# Only listen for connections from the local machine.\n");
+ cupsFilePrintf(temp, "Listen localhost:%d\n", ippPort());
+ }
+
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+ if (!access(CUPS_DEFAULT_DOMAINSOCKET, 0))
+ cupsFilePuts(temp, "Listen " CUPS_DEFAULT_DOMAINSOCKET "\n");
+#endif /* CUPS_DEFAULT_DOMAINSOCKET */
+ }
+
+ if (!wrote_root_location &&
+ (remote_admin >= 0 || remote_any > 0 || share_printers >= 0))
+ {
+ if (remote_admin > 0 && share_printers > 0)
+ cupsFilePuts(temp,
+ "# Allow shared printing and remote administration...\n");
+ else if (remote_admin > 0)
+ cupsFilePuts(temp, "# Allow remote administration...\n");
+ else if (share_printers > 0)
+ cupsFilePuts(temp, "# Allow shared printing...\n");
+ else if (remote_any > 0)
+ cupsFilePuts(temp, "# Allow remote access...\n");
+ else
+ cupsFilePuts(temp, "# Restrict access to the server...\n");
+
+ cupsFilePuts(temp, "<Location />\n"
+ " Order allow,deny\n");
+
+ if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
+ cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
+
+ cupsFilePuts(temp, "</Location>\n");
+ }
+
+ if (!wrote_admin_location && remote_admin >= 0)
+ {
+ if (remote_admin)
+ cupsFilePuts(temp, "# Allow remote administration...\n");
+ else
+ cupsFilePuts(temp, "# Restrict access to the admin pages...\n");
+
+ cupsFilePuts(temp, "<Location /admin>\n"
+ " Order allow,deny\n");
+
+ if (remote_admin)
+ cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
+
+ cupsFilePuts(temp, "</Location>\n");
+ }
+
+ if (!wrote_conf_location && remote_admin >= 0)
+ {
+ if (remote_admin)
+ cupsFilePuts(temp,
+ "# Allow remote access to the configuration files...\n");
+ else
+ cupsFilePuts(temp, "# Restrict access to the configuration files...\n");
+
+ cupsFilePuts(temp, "<Location /admin/conf>\n"
+ " AuthType Default\n"
+ " Require user @SYSTEM\n"
+ " Order allow,deny\n");
+
+ if (remote_admin)
+ cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
+
+ cupsFilePuts(temp, "</Location>\n");
+ }
+
+ if (!wrote_policy && user_cancel_any >= 0)
+ {
+ cupsFilePuts(temp, "<Policy default>\n"
+ " # Job-related operations must be done by the owner "
+ "or an administrator...\n"
+ " <Limit Send-Document Send-URI Hold-Job Release-Job "
+ "Restart-Job Purge-Jobs Set-Job-Attributes "
+ "Create-Job-Subscription Renew-Subscription "
+ "Cancel-Subscription Get-Notifications Reprocess-Job "
+ "Cancel-Current-Job Suspend-Current-Job Resume-Job "
+ "CUPS-Move-Job>\n"
+ " Require user @OWNER @SYSTEM\n"
+ " Order deny,allow\n"
+ " </Limit>\n"
+ " # All administration operations require an "
+ "administrator to authenticate...\n"
+ " <Limit Pause-Printer Resume-Printer "
+ "Set-Printer-Attributes Enable-Printer "
+ "Disable-Printer Pause-Printer-After-Current-Job "
+ "Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer "
+ "Activate-Printer Restart-Printer Shutdown-Printer "
+ "Startup-Printer Promote-Job Schedule-Job-After "
+ "CUPS-Add-Printer CUPS-Delete-Printer "
+ "CUPS-Add-Class CUPS-Delete-Class "
+ "CUPS-Accept-Jobs CUPS-Reject-Jobs "
+ "CUPS-Set-Default CUPS-Add-Device CUPS-Delete-Device>\n"
+ " AuthType Default\n"
+ " Require user @SYSTEM\n"
+ " Order deny,allow\n"
+ "</Limit>\n");
+
+ if (!user_cancel_any)
+ cupsFilePuts(temp, " # Only the owner or an administrator can cancel "
+ "a job...\n"
+ " <Limit Cancel-Job>\n"
+ " Order deny,allow\n"
+ " Require user @OWNER "
+ CUPS_DEFAULT_PRINTOPERATOR_AUTH "\n"
+ " </Limit>\n");
+
+ cupsFilePuts(temp, " <Limit All>\n"
+ " Order deny,allow\n"
+ " </Limit>\n"
+ "</Policy>\n");
+ }
+
+ for (i = num_settings, setting = settings; i > 0; i --, setting ++)
+ if (setting->name[0] != '_' &&
+ _cups_strcasecmp(setting->name, "Listen") &&
+ _cups_strcasecmp(setting->name, "Port") &&
+ !cupsGetOption(setting->name, cupsd_num_settings, cupsd_settings))
+ {
+ /*
+ * Add this directive to the list of directives we have written...
+ */
+
+ cupsd_num_settings = cupsAddOption(setting->name, setting->value,
+ cupsd_num_settings, &cupsd_settings);
+
+ /*
+ * Write the new value, without indentation since we only support
+ * setting root directives, not in sections...
+ */
+
+ cupsFilePrintf(temp, "%s %s\n", setting->name, setting->value);
+ }
+
+ cupsFileClose(cupsd);
+ cupsFileClose(temp);
+
+ /*
+ * Upload the configuration file to the server...
+ */
+
+ status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
+
+ if (status == HTTP_STATUS_CREATED)
+ {
+ /*
+ * Updated OK, add the basic settings...
+ */
+
+ if (debug_logging >= 0)
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
+ debug_logging ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+ else
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
+ old_debug_logging ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+
+ if (remote_admin >= 0)
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
+ remote_admin ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+ else
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
+ old_remote_admin ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
+ remote_any ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+
+ if (share_printers >= 0)
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
+ share_printers ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+ else
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
+ old_share_printers ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+
+ if (user_cancel_any >= 0)
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
+ user_cancel_any ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+ else
+ cupsd_num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
+ old_user_cancel_any ? "1" : "0",
+ cupsd_num_settings, &cupsd_settings);
+
+ /*
+ * Save the new values...
+ */
+
+ invalidate_cupsd_cache(cg);
+
+ cg->cupsd_num_settings = cupsd_num_settings;
+ cg->cupsd_settings = cupsd_settings;
+ cg->cupsd_update = time(NULL);
+
+ httpGetHostname(http, cg->cupsd_hostname, sizeof(cg->cupsd_hostname));
+ }
+ else
+ cupsFreeOptions(cupsd_num_settings, cupsd_settings);
+
+ /*
+ * Remote our temp files and return...
+ */
+
+ if (remote)
+ unlink(cupsdconf);
+
+ unlink(tempfile);
+
+ return (status == HTTP_STATUS_CREATED);
+}
+
+
+/*
+ * 'do_samba_command()' - Do a SAMBA command.
+ */
+
+static int /* O - Status of command */
+do_samba_command(const char *command, /* I - Command to run */
+ const char *address, /* I - Address for command */
+ const char *subcmd, /* I - Sub-command */
+ const char *authfile, /* I - Samba authentication file */
+ FILE *logfile) /* I - Optional log file */
+{
+#ifdef WIN32
+ return (1); /* Always fail on Windows... */
+
+#else
+ int status; /* Status of command */
+ int pid; /* Process ID of child */
+
+
+ if (logfile)
+ _cupsLangPrintf(logfile,
+ _("Running command: %s %s -N -A %s -c \'%s\'"),
+ command, address, authfile, subcmd);
+
+ if ((pid = fork()) == 0)
+ {
+ /*
+ * Child goes here, redirect stdin/out/err and execute the command...
+ */
+
+ int fd = open("/dev/null", O_RDONLY);
+
+ if (fd > 0)
+ {
+ dup2(fd, 0);
+ close(fd);
+ }
+
+ if (logfile)
+ dup2(fileno(logfile), 1);
+ else if ((fd = open("/dev/null", O_WRONLY)) > 1)
+ {
+ dup2(fd, 1);
+ close(fd);
+ }
+
+ dup2(1, 2);
+
+ execlp(command, command, address, "-N", "-A", authfile, "-c", subcmd,
+ (char *)0);
+ exit(errno);
+ }
+ else if (pid < 0)
+ {
+ status = -1;
+
+ if (logfile)
+ _cupsLangPrintf(logfile, _("Unable to run \"%s\": %s"),
+ command, strerror(errno));
+ }
+ else
+ {
+ /*
+ * Wait for the process to complete...
+ */
+
+ while (wait(&status) != pid);
+ }
+
+ if (logfile)
+ _cupsLangPuts(logfile, "");
+
+ DEBUG_printf(("9do_samba_command: status=%d", status));
+
+ if (WIFEXITED(status))
+ return (WEXITSTATUS(status));
+ else
+ return (-WTERMSIG(status));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'get_cupsd_conf()' - Get the current cupsd.conf file.
+ */
+
+static http_status_t /* O - Status of request */
+get_cupsd_conf(
+ http_t *http, /* I - Connection to server */
+ _cups_globals_t *cg, /* I - Global data */
+ time_t last_update, /* I - Last update time for file */
+ char *name, /* I - Filename buffer */
+ int namesize, /* I - Size of filename buffer */
+ int *remote) /* O - Remote file? */
+{
+ int fd; /* Temporary file descriptor */
+#ifndef WIN32
+ struct stat info; /* cupsd.conf file information */
+#endif /* WIN32 */
+ http_status_t status; /* Status of getting cupsd.conf */
+ char host[HTTP_MAX_HOST]; /* Hostname for connection */
+
+
+ /*
+ * See if we already have the data we need...
+ */
+
+ httpGetHostname(http, host, sizeof(host));
+
+ if (_cups_strcasecmp(cg->cupsd_hostname, host))
+ invalidate_cupsd_cache(cg);
+
+ snprintf(name, namesize, "%s/cupsd.conf", cg->cups_serverroot);
+ *remote = 0;
+
+#ifndef WIN32
+ if (!_cups_strcasecmp(host, "localhost") && !access(name, R_OK))
+ {
+ /*
+ * Read the local file rather than using HTTP...
+ */
+
+ if (stat(name, &info))
+ {
+ char message[1024]; /* Message string */
+
+
+ snprintf(message, sizeof(message),
+ _cupsLangString(cupsLangDefault(), _("stat of %s failed: %s")),
+ name, strerror(errno));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
+
+ *name = '\0';
+
+ return (HTTP_STATUS_SERVER_ERROR);
+ }
+ else if (last_update && info.st_mtime <= last_update)
+ status = HTTP_STATUS_NOT_MODIFIED;
+ else
+ status = HTTP_STATUS_OK;
+ }
+ else
+#endif /* !WIN32 */
+ {
+ /*
+ * Read cupsd.conf via a HTTP GET request...
+ */
+
+ if ((fd = cupsTempFd(name, namesize)) < 0)
+ {
+ *name = '\0';
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+
+ invalidate_cupsd_cache(cg);
+
+ return (HTTP_STATUS_SERVER_ERROR);
+ }
+
+ *remote = 1;
+
+ httpClearFields(http);
+
+ if (last_update)
+ httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE,
+ httpGetDateString(last_update));
+
+ status = cupsGetFd(http, "/admin/conf/cupsd.conf", fd);
+
+ close(fd);
+
+ if (status != HTTP_STATUS_OK)
+ {
+ unlink(name);
+ *name = '\0';
+ }
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'invalidate_cupsd_cache()' - Invalidate the cached cupsd.conf settings.
+ */
+
+static void
+invalidate_cupsd_cache(
+ _cups_globals_t *cg) /* I - Global data */
+{
+ cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings);
+
+ cg->cupsd_hostname[0] = '\0';
+ cg->cupsd_update = 0;
+ cg->cupsd_num_settings = 0;
+ cg->cupsd_settings = NULL;
+}
+
+
+/*
+ * 'write_option()' - Write a CUPS option to a PPD file.
+ */
+
+static void
+write_option(cups_file_t *dstfp, /* I - PPD file */
+ int order, /* I - Order dependency */
+ const char *name, /* I - Option name */
+ const char *text, /* I - Option text */
+ const char *attrname, /* I - Attribute name */
+ ipp_attribute_t *suppattr, /* I - IPP -supported attribute */
+ ipp_attribute_t *defattr, /* I - IPP -default attribute */
+ int defval, /* I - Default value number */
+ int valcount) /* I - Number of values */
+{
+ int i; /* Looping var */
+
+
+ cupsFilePrintf(dstfp, "*JCLOpenUI *%s/%s: PickOne\n"
+ "*OrderDependency: %d JCLSetup *%s\n",
+ name, text, order, name);
+
+ if (defattr->value_tag == IPP_TAG_INTEGER)
+ {
+ /*
+ * Do numeric options with a range or list...
+ */
+
+ cupsFilePrintf(dstfp, "*Default%s: %d\n", name,
+ defattr->values[defval].integer);
+
+ if (suppattr->value_tag == IPP_TAG_RANGE)
+ {
+ /*
+ * List each number in the range...
+ */
+
+ for (i = suppattr->values[0].range.lower;
+ i <= suppattr->values[0].range.upper;
+ i ++)
+ {
+ cupsFilePrintf(dstfp, "*%s %d: \"", name, i);
+
+ if (valcount == 1)
+ cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\n\"\n*End\n",
+ attrname, i);
+ else if (defval == 0)
+ cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\"\n", attrname, i);
+ else if (defval < (valcount - 1))
+ cupsFilePrintf(dstfp, ",%d\"\n", i);
+ else
+ cupsFilePrintf(dstfp, ",%d\n\"\n*End\n", i);
+ }
+ }
+ else
+ {
+ /*
+ * List explicit numbers...
+ */
+
+ for (i = 0; i < suppattr->num_values; i ++)
+ {
+ cupsFilePrintf(dstfp, "*%s %d: \"", name, suppattr->values[i].integer);
+
+ if (valcount == 1)
+ cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname,
+ suppattr->values[i].integer);
+ else if (defval == 0)
+ cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\"\n", attrname,
+ suppattr->values[i].integer);
+ else if (defval < (valcount - 1))
+ cupsFilePrintf(dstfp, ",%d\"\n", suppattr->values[i].integer);
+ else
+ cupsFilePrintf(dstfp, ",%d\n\"\n*End\n", suppattr->values[i].integer);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Do text options with a list...
+ */
+
+ cupsFilePrintf(dstfp, "*Default%s: %s\n", name,
+ defattr->values[defval].string.text);
+
+ for (i = 0; i < suppattr->num_values; i ++)
+ {
+ cupsFilePrintf(dstfp, "*%s %s: \"", name,
+ suppattr->values[i].string.text);
+
+ if (valcount == 1)
+ cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%s\n\"\n*End\n", attrname,
+ suppattr->values[i].string.text);
+ else if (defval == 0)
+ cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%s\"\n", attrname,
+ suppattr->values[i].string.text);
+ else if (defval < (valcount - 1))
+ cupsFilePrintf(dstfp, ",%s\"\n", suppattr->values[i].string.text);
+ else
+ cupsFilePrintf(dstfp, ",%s\n\"\n*End\n",
+ suppattr->values[i].string.text);
+ }
+ }
+
+ cupsFilePrintf(dstfp, "*JCLCloseUI: *%s\n\n", name);
+}
+
+
+/*
+ * End of "$Id: adminutil.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/adminutil.h b/cups/libs/cups/adminutil.h
new file mode 100644
index 000000000..f03d2facb
--- /dev/null
+++ b/cups/libs/cups/adminutil.h
@@ -0,0 +1,81 @@
+/*
+ * "$Id: adminutil.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Administration utility API definitions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2001-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_ADMINUTIL_H_
+# define _CUPS_ADMINUTIL_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include "cups.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define CUPS_SERVER_DEBUG_LOGGING "_debug_logging"
+# define CUPS_SERVER_REMOTE_ADMIN "_remote_admin"
+# define CUPS_SERVER_REMOTE_ANY "_remote_any"
+/*# define CUPS_SERVER_REMOTE_PRINTERS "_remote_printers"*/
+# define CUPS_SERVER_SHARE_PRINTERS "_share_printers"
+# define CUPS_SERVER_USER_CANCEL_ANY "_user_cancel_any"
+
+
+/*
+ * Functions...
+ */
+
+extern int cupsAdminExportSamba(const char *dest, const char *ppd,
+ const char *samba_server,
+ const char *samba_user,
+ const char *samba_password,
+ FILE *logfile) _CUPS_DEPRECATED;
+extern char *cupsAdminCreateWindowsPPD(http_t *http, const char *dest,
+ char *buffer, int bufsize)
+ _CUPS_DEPRECATED;
+
+extern int cupsAdminGetServerSettings(http_t *http,
+ int *num_settings,
+ cups_option_t **settings)
+ _CUPS_API_1_3;
+extern int cupsAdminSetServerSettings(http_t *http,
+ int num_settings,
+ cups_option_t *settings)
+ _CUPS_API_1_3;
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_ADMINUTIL_H_ */
+
+/*
+ * End of "$Id: adminutil.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/api-array.header b/cups/libs/cups/api-array.header
new file mode 100644
index 000000000..4d5acf0d1
--- /dev/null
+++ b/cups/libs/cups/api-array.header
@@ -0,0 +1,34 @@
+<!--
+ "$Id: api-array.header 7266 2008-01-29 02:15:29Z mike $"
+
+ 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/libs/cups/api-array.shtml b/cups/libs/cups/api-array.shtml
new file mode 100644
index 000000000..7246a7bd6
--- /dev/null
+++ b/cups/libs/cups/api-array.shtml
@@ -0,0 +1,196 @@
+<!--
+ "$Id: api-array.shtml 7616 2008-05-28 00:34:13Z mike $"
+
+ 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/libs/cups/api-cups.header b/cups/libs/cups/api-cups.header
new file mode 100644
index 000000000..ac781af42
--- /dev/null
+++ b/cups/libs/cups/api-cups.header
@@ -0,0 +1,40 @@
+<!--
+ "$Id: api-cups.header 7279 2008-01-31 01:50:44Z mike $"
+
+ 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/libs/cups/api-cups.shtml b/cups/libs/cups/api-cups.shtml
new file mode 100644
index 000000000..caa96b953
--- /dev/null
+++ b/cups/libs/cups/api-cups.shtml
@@ -0,0 +1,443 @@
+<!--
+ "$Id: api-cups.shtml 7337 2008-02-22 04:44:04Z mike $"
+
+ 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/libs/cups/api-filedir.header b/cups/libs/cups/api-filedir.header
new file mode 100644
index 000000000..f9f529805
--- /dev/null
+++ b/cups/libs/cups/api-filedir.header
@@ -0,0 +1,36 @@
+<!--
+ "$Id: api-filedir.header 7279 2008-01-31 01:50:44Z mike $"
+
+ 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/libs/cups/api-filedir.shtml b/cups/libs/cups/api-filedir.shtml
new file mode 100644
index 000000000..96bf0facb
--- /dev/null
+++ b/cups/libs/cups/api-filedir.shtml
@@ -0,0 +1,31 @@
+<!--
+ "$Id: api-filedir.shtml 7279 2008-01-31 01:50:44Z mike $"
+
+ 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/libs/cups/api-filter.header b/cups/libs/cups/api-filter.header
new file mode 100644
index 000000000..303145212
--- /dev/null
+++ b/cups/libs/cups/api-filter.header
@@ -0,0 +1,41 @@
+<!--
+ "$Id: api-filter.header 7616 2008-05-28 00:34:13Z mike $"
+
+ Filter and backend programming header for CUPS.
+
+ Copyright 2008-2014 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'>Filter and Backend Programming</h1>
+
+<div class='summary'><table summary='General Information'>
+<thead>
+<tr>
+ <th>Headers</th>
+ <th>cups/backend.h<br>
+ cups/sidechannel.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><br>
+ Programming: <a href='api-ppd.html' target='_top'>PPD API</a><br>
+ Programming: <a href='api-raster.html' target='_top'>Raster API</a><br>
+ Programming: <a href='postscript-driver.html' target='_top'>Developing PostScript Printer Drivers</a><br>
+ Programming: <a href='raster-driver.html' target='_top'>Developing Raster Printer Drivers</a><br>
+ Specifications: <a href='spec-design.html' target='_top'>CUPS Design Description</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/cups/api-filter.shtml b/cups/libs/cups/api-filter.shtml
new file mode 100644
index 000000000..4b8372edd
--- /dev/null
+++ b/cups/libs/cups/api-filter.shtml
@@ -0,0 +1,854 @@
+<!--
+ "$Id: api-filter.shtml 7677 2008-06-19 23:22:19Z mike $"
+
+ Filter and backend programming 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>Filters (which include printer drivers and port monitors) and backends
+are used to convert job files to a printable format and send that data to the
+printer itself. All of these programs use a common interface for processing
+print jobs and communicating status information to the scheduler. Each is run
+with a standard set of command-line arguments:<p>
+
+<dl class="code">
+
+ <dt>argv[1]</dt>
+ <dd>The job ID</dd>
+
+ <dt>argv[2]</dt>
+ <dd>The user printing the job</dd>
+
+ <dt>argv[3]</dt>
+ <dd>The job name/title</dd>
+
+ <dt>argv[4]</dt>
+ <dd>The number of copies to print</dd>
+
+ <dt>argv[5]</dt>
+ <dd>The options that were provided when the job was submitted</dd>
+
+ <dt>argv[6]</dt>
+ <dd>The file to print (first program only)</dd>
+</dl>
+
+<p>The scheduler runs one or more of these programs to print any given job. The
+first filter reads from the print file and writes to the standard output, while
+the remaining filters read from the standard input and write to the standard
+output. The backend is the last filter in the chain and writes to the
+device.</p>
+
+<p>Filters are always run as a non-privileged user, typically "lp", with no
+connection to the user's desktop. Backends are run either as a non-privileged
+user or as root if the file permissions do not allow user or group execution.
+The <a href="#PERMISSIONS">file permissions</a> section talks about this in
+more detail.</p>
+
+<h3><a name="SECURITY">Security Considerations</a></h3>
+
+<p>It is always important to use security programming practices. Filters and
+most backends are run as a non-privileged user, so the major security
+consideration is resource utilization - filters should not depend on unlimited
+amounts of CPU, memory, or disk space, and should protect against conditions
+that could lead to excess usage of any resource like infinite loops and
+unbounded recursion. In addition, filters must <em>never</em> allow the user to
+specify an arbitrary file path to a separator page, template, or other file
+used by the filter since that can lead to an unauthorized disclosure of
+information. <em>Always</em> treat input as suspect and validate it!</p>
+
+<p>If you are developing a backend that runs as root, make sure to check for
+potential buffer overflows, integer under/overflow conditions, and file
+accesses since these can lead to privilege escalations. When writing files,
+always validate the file path and <em>never</em> allow a user to determine
+where to store a file.</p>
+
+<blockquote><b>Note:</b>
+
+<p><em>Never</em> write files to a user's home directory. Aside from the
+security implications, CUPS is a network print service and as such the network
+user may not be the same as the local user and/or there may not be a local home
+directory to write to.</p>
+
+<p>In addition, some operating systems provide additional security mechanisms
+that further limit file system access, even for backends running as root. On
+OS X, for example, no backend may write to a user's home directory.</p>
+</blockquote>
+
+<h3><a name="SIGNALS">Canceled Jobs and Signal Handling</a></h3>
+
+<p>The scheduler sends <code>SIGTERM</code> when a printing job is canceled or
+held. Filters, backends, and port monitors <em>must</em> catch
+<code>SIGTERM</code> and perform any cleanup necessary to produce a valid output
+file or return the printer to a known good state. The recommended behavior is to
+end the output on the current page, preferably on the current line or object
+being printed.</p>
+
+<p>Filters and backends may also receive <code>SIGPIPE</code> when an upstream or downstream filter/backend exits with a non-zero status. Developers should generally ignore <code>SIGPIPE</code> at the beginning of <code>main()</code> with the following function call:</p>
+
+<pre class="example">
+#include &lt;signal.h&gt;>
+
+...
+
+int
+main(int argc, char *argv[])
+{
+ signal(SIGPIPE, SIG_IGN);
+
+ ...
+}
+</pre>
+
+<h3><a name="PERMISSIONS">File Permissions</a></h3>
+
+<p>For security reasons, CUPS will only run filters and backends that are owned
+by root and do not have world or group write permissions. The recommended
+permissions for filters and backends are 0555 - read and execute but no write.
+Backends that must run as root should use permissions of 0500 - read and execute
+by root, no access for other users. Write permissions can be enabled for the
+root user only.</p>
+
+<p>To avoid a warning message, the directory containing your filter(s) must also
+be owned by root and have world and group write disabled - permissions of 0755
+or 0555 are strongly encouraged.</p>
+
+<h3><a name="TEMPFILES">Temporary Files</a></h3>
+
+<p>Temporary files should be created in the directory specified by the
+"TMPDIR" environment variable. The
+<a href="#cupsTempFile2"><code>cupsTempFile2</code></a> function can be
+used to safely create temporary files in this directory.</p>
+
+<h3><a name="COPIES">Copy Generation</a></h3>
+
+<p>The <code>argv[4]</code> argument specifies the number of copies to produce
+of the input file. In general, you should only generate copies if the
+<em>filename</em> argument is supplied. The only exception to this are
+filters that produce device-independent PostScript output, since the PostScript
+filter <var>pstops</var> is responsible for generating copies of PostScript
+files.</p>
+
+<h3><a name="EXITCODES">Exit Codes</a></h3>
+
+<p>Filters must exit with status 0 when they successfully generate print data
+or 1 when they encounter an error. Backends can return any of the
+<a href="#cups_backend_t"><code>cups_backend_t</code></a> constants.</p>
+
+<h3><a name="ENVIRONMENT">Environment Variables</a></h3>
+
+<p>The following environment variables are defined by the printing system
+when running print filters and backends:</p>
+
+<dl class="code">
+
+ <dt>APPLE_LANGUAGE</dt>
+ <dd>The Apple language identifier associated with the job
+ (OS X only).</dd>
+
+ <dt>CHARSET</dt>
+ <dd>The job character set, typically "utf-8".</dd>
+
+ <dt>CLASS</dt>
+ <dd>When a job is submitted to a printer class, contains the name of
+ the destination printer class. Otherwise this environment
+ variable will not be set.</dd>
+
+ <dt>CONTENT_TYPE</dt>
+ <dd>The MIME type associated with the file (e.g.
+ application/postscript).</dd>
+
+ <dt>CUPS_CACHEDIR</dt>
+ <dd>The directory where cache files can be stored. Cache files can be
+ used to retain information between jobs or files in a job.</dd>
+
+ <dt>CUPS_DATADIR</dt>
+ <dd>The directory where (read-only) CUPS data files can be found.</dd>
+
+ <dt>CUPS_FILETYPE</dt>
+ <dd>The type of file being printed: "job-sheet" for a banner page and
+ "document" for a regular print file.</dd>
+
+ <dt>CUPS_SERVERROOT</dt>
+ <dd>The root directory of the server.</dd>
+
+ <dt>DEVICE_URI</dt>
+ <dd>The device-uri associated with the printer.</dd>
+
+ <dt>FINAL_CONTENT_TYPE</dt>
+ <dd>The MIME type associated with the printer (e.g.
+ application/vnd.cups-postscript).</dd>
+
+ <dt>LANG</dt>
+ <dd>The language locale associated with the job.</dd>
+
+ <dt>PPD</dt>
+ <dd>The full pathname of the PostScript Printer Description (PPD)
+ file for this printer.</dd>
+
+ <dt>PRINTER</dt>
+ <dd>The queue name of the class or printer.</dd>
+
+ <dt>RIP_CACHE</dt>
+ <dd>The recommended amount of memory to use for Raster Image
+ Processors (RIPs).</dd>
+
+ <dt>TMPDIR</dt>
+ <dd>The directory where temporary files should be created.</dd>
+
+</dl>
+
+<h3><a name="MESSAGES">Communicating with the Scheduler</a></h3>
+
+<p>Filters and backends communicate with the scheduler by writing messages
+to the standard error file. The scheduler reads messages from all filters in
+a job and processes the message based on its prefix. For example, the following
+code sets the current printer state message to "Printing page 5":</p>
+
+<pre class="example">
+int page = 5;
+
+fprintf(stderr, "INFO: Printing page %d\n", page);
+</pre>
+
+<p>Each message is a single line of text starting with one of the following
+prefix strings:</p>
+
+<dl class="code">
+
+ <dt>ALERT: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "alert" log level.</dd>
+
+ <dt>ATTR: attribute=value [attribute=value]</dt>
+ <dd>Sets the named printer or job attribute(s). Typically this is used
+ to set the <code>marker-colors</code>, <code>marker-high-levels</code>,
+ <code>marker-levels</code>, <code>marker-low-levels</code>,
+ <code>marker-message</code>, <code>marker-names</code>,
+ <code>marker-types</code>, <code>printer-alert</code>, and
+ <code>printer-alert-description</code> printer attributes. Standard
+ <code>marker-types</code> values are listed in <a href='#TABLE1'>Table
+ 1</a>. String values need special handling - see <a href="#ATTR_STRINGS">Reporting Attribute String Values</a> below.</dd>
+
+ <dt>CRIT: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "critical" log
+ level.</dd>
+
+ <dt>DEBUG: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "debug" log level.</dd>
+
+ <dt>DEBUG2: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "debug2" log level.</dd>
+
+ <dt>EMERG: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "emergency" log
+ level.</dd>
+
+ <dt>ERROR: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "error" log level.
+ Use "ERROR:" messages for non-persistent processing errors.</dd>
+
+ <dt>INFO: message</dt>
+ <dd>Sets the printer-state-message attribute. If the current log level
+ is set to "debug2", also adds the specified message to the current error
+ log file using the "info" log level.</dd>
+
+ <dt>NOTICE: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "notice" log level.</dd>
+
+ <dt>PAGE: page-number #-copies</dt>
+ <dt>PAGE: total #-pages</dt>
+ <dd>Adds an entry to the current page log file. The first form adds
+ #-copies to the job-media-sheets-completed attribute. The second
+ form sets the job-media-sheets-completed attribute to #-pages.</dd>
+
+ <dt>PPD: keyword=value [keyword=value ...]</dt>
+ <dd>Changes or adds keywords to the printer's PPD file. Typically
+ this is used to update installable options or default media settings
+ based on the printer configuration.</dd>
+
+ <dt>STATE: + printer-state-reason [printer-state-reason ...]</dt>
+ <dt>STATE: - printer-state-reason [printer-state-reason ...]</dt>
+ <dd>Sets or clears printer-state-reason keywords for the current queue.
+ Typically this is used to indicate persistent media, ink, toner, and
+ configuration conditions or errors on a printer.
+ <a href='#TABLE2'>Table 2</a> lists the standard state keywords -
+ use vendor-prefixed ("com.example.foo") keywords for custom states. See
+ <a href="#MANAGING_STATE">Managing Printer State in a Filter</a> for more
+ information.
+
+ <dt>WARNING: message</dt>
+ <dd>Sets the printer-state-message attribute and adds the specified
+ message to the current error log file using the "warning" log
+ level.</dd>
+
+</dl>
+
+<p>Messages without one of these prefixes are treated as if they began with
+the "DEBUG:" prefix string.</p>
+
+<div class='table'><table width='80%' summary='Table 1: Standard marker-types Values'>
+<caption>Table 1: <a name='TABLE1'>Standard marker-types Values</a></caption>
+<thead>
+<tr>
+ <th>marker-type</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>developer</td>
+ <td>Developer unit</td>
+</tr>
+<tr>
+ <td>fuser</td>
+ <td>Fuser unit</td>
+</tr>
+<tr>
+ <td>fuser-cleaning-pad</td>
+ <td>Fuser cleaning pad</td>
+</tr>
+<tr>
+ <td>fuser-oil</td>
+ <td>Fuser oil</td>
+</tr>
+<tr>
+ <td>ink</td>
+ <td>Ink supply</td>
+</tr>
+<tr>
+ <td>opc</td>
+ <td>Photo conductor</td>
+</tr>
+<tr>
+ <td>solid-wax</td>
+ <td>Wax supply</td>
+</tr>
+<tr>
+ <td>staples</td>
+ <td>Staple supply</td>
+</tr>
+<tr>
+ <td>toner</td>
+ <td>Toner supply</td>
+</tr>
+<tr>
+ <td>transfer-unit</td>
+ <td>Transfer unit</td>
+</tr>
+<tr>
+ <td>waste-ink</td>
+ <td>Waste ink tank</td>
+</tr>
+<tr>
+ <td>waste-toner</td>
+ <td>Waste toner tank</td>
+</tr>
+<tr>
+ <td>waste-wax</td>
+ <td>Waste wax tank</td>
+</tr>
+</tbody>
+</table></div>
+
+<br>
+
+<div class='table'><table width='80%' summary='Table 2: Standard State Keywords'>
+<caption>Table 2: <a name='TABLE2'>Standard State Keywords</a></caption>
+<thead>
+<tr>
+ <th>Keyword</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>connecting-to-device</td>
+ <td>Connecting to printer but not printing yet.</td>
+</tr>
+<tr>
+ <td>cover-open</td>
+ <td>The printer's cover is open.</td>
+</tr>
+<tr>
+ <td>input-tray-missing</td>
+ <td>The paper tray is missing.</td>
+</tr>
+<tr>
+ <td>marker-supply-empty</td>
+ <td>The printer is out of ink.</td>
+</tr>
+<tr>
+ <td>marker-supply-low</td>
+ <td>The printer is almost out of ink.</td>
+</tr>
+<tr>
+ <td>marker-waste-almost-full</td>
+ <td>The printer's waste bin is almost full.</td>
+</tr>
+<tr>
+ <td>marker-waste-full</td>
+ <td>The printer's waste bin is full.</td>
+</tr>
+<tr>
+ <td>media-empty</td>
+ <td>The paper tray (any paper tray) is empty.</td>
+</tr>
+<tr>
+ <td>media-jam</td>
+ <td>There is a paper jam.</td>
+</tr>
+<tr>
+ <td>media-low</td>
+ <td>The paper tray (any paper tray) is almost empty.</td>
+</tr>
+<tr>
+ <td>media-needed</td>
+ <td>The paper tray needs to be filled (for a job that is printing).</td>
+</tr>
+<tr>
+ <td>paused</td>
+ <td>Stop the printer.</td>
+</tr>
+<tr>
+ <td>timed-out</td>
+ <td>Unable to connect to printer.</td>
+</tr>
+<tr>
+ <td>toner-empty</td>
+ <td>The printer is out of toner.</td>
+</tr>
+<tr>
+ <td>toner-low</td>
+ <td>The printer is low on toner.</td>
+</tr>
+</tbody>
+</table></div>
+
+
+<h4><a name="ATTR_STRINGS">Reporting Attribute String Values</a></h4>
+
+<p>When reporting string values using "ATTR:" messages, a filter or backend must take special care to appropriately quote those values. The scheduler uses the CUPS option parsing code for attributes, so the general syntax is:</p>
+
+<pre class="example">
+name=simple
+name=simple,simple,...
+name='complex value'
+name="complex value"
+name='"complex value"','"complex value"',...
+</pre>
+
+<p>Simple values are strings that do not contain spaces, quotes, backslashes, or the comma and can be placed verbatim in the "ATTR:" message, for example:</p>
+
+<pre class="example">
+int levels[4] = { 40, 50, 60, 70 }; /* CMYK */
+
+fputs("ATTR: marker-colors=#00FFFF,#FF00FF,#FFFF00,#000000\n", stderr);
+fputs("ATTR: marker-high-levels=100,100,100,100\n", stderr);
+fprintf(stderr, "ATTR: marker-levels=%d,%d,%d,%d\n", levels[0], levels[1],
+ levels[2], levels[3], levels[4]);
+fputs("ATTR: marker-low-levels=5,5,5,5\n", stderr);
+fputs("ATTR: marker-types=toner,toner,toner,toner\n", stderr);
+</pre>
+
+<p>Complex values that contains spaces, quotes, backslashes, or the comma must be quoted. For a single value a single set of quotes is sufficient:</p>
+
+<pre class="example">
+fputs("ATTR: marker-message='Levels shown are approximate.'\n", stderr);
+</pre>
+
+<p>When multiple values are reported, each value must be enclosed by a set of single and double quotes:</p>
+
+<pre class="example">
+fputs("ATTR: marker-names='\"Cyan Toner\"','\"Magenta Toner\"',"
+ "'\"Yellow Toner\"','\"Black Toner\"'\n", stderr);
+</pre>
+
+<p>The IPP backend includes a <var>quote_string</var> function that may be used to properly quote a complex value in an "ATTR:" message:</p>
+
+<pre class="example">
+static const char * /* O - Quoted string */
+quote_string(const char *s, /* I - String */
+ char *q, /* I - Quoted string buffer */
+ size_t qsize) /* I - Size of quoted string buffer */
+{
+ char *qptr, /* Pointer into string buffer */
+ *qend; /* End of string buffer */
+
+
+ qptr = q;
+ qend = q + qsize - 5;
+
+ if (qend &lt; q)
+ {
+ *q = '\0';
+ return (q);
+ }
+
+ *qptr++ = '\'';
+ *qptr++ = '\"';
+
+ while (*s && qptr &lt; qend)
+ {
+ if (*s == '\\' || *s == '\"' || *s == '\'')
+ {
+ if (qptr &lt; (qend - 4))
+ {
+ *qptr++ = '\\';
+ *qptr++ = '\\';
+ *qptr++ = '\\';
+ }
+ else
+ break;
+ }
+
+ *qptr++ = *s++;
+ }
+
+ *qptr++ = '\"';
+ *qptr++ = '\'';
+ *qptr = '\0';
+
+ return (q);
+}
+</pre>
+
+
+<h4><a name="MANAGING_STATE">Managing Printer State in a Filter</a></h4>
+
+<p>Filters are responsible for managing the state keywords they set using
+"STATE:" messages. Typically you will update <em>all</em> of the keywords that
+are used by the filter at startup, for example:</p>
+
+<pre class="example">
+if (foo_condition != 0)
+ fputs("STATE: +com.example.foo\n", stderr);
+else
+ fputs("STATE: -com.example.foo\n", stderr);
+
+if (bar_condition != 0)
+ fputs("STATE: +com.example.bar\n", stderr);
+else
+ fputs("STATE: -com.example.bar\n", stderr);
+</pre>
+
+<p>Then as conditions change, your filter sends "STATE: +keyword" or "STATE:
+-keyword" messages as necessary to set or clear the corresponding keyword,
+respectively.</p>
+
+<p>State keywords are often used to notify the user of issues that span across
+jobs, for example "media-empty-warning" that indicates one or more paper trays
+are empty. These keywords should not be cleared unless the corresponding issue
+no longer exists.</p>
+
+<p>Filters should clear job-related keywords on startup and exit so that they
+do not remain set between jobs. For example, "connecting-to-device" is a job
+sub-state and not an issue that applies when a job is not printing.</p>
+
+<blockquote><b>Note:</b>
+
+<p>"STATE:" messages often provide visible alerts to the user. For example,
+on OS X setting a printer-state-reason value with an "-error" or
+"-warning" suffix will cause the printer's dock item to bounce if the
+corresponding reason is localized with a cupsIPPReason keyword in the
+printer's PPD file.</p>
+
+<p>When providing a vendor-prefixed keyword, <em>always</em> provide the
+corresponding standard keyword (if any) to allow clients to respond to the
+condition correctly. For example, if you provide a vendor-prefixed keyword
+for a low cyan ink condition ("com.example.cyan-ink-low") you must also set the
+"marker-supply-low-warning" keyword. In such cases you should also refrain
+from localizing the vendor-prefixed keyword in the PPD file - otherwise both
+the generic and vendor-specific keyword will be shown in the user
+interface.</p>
+
+</blockquote>
+
+<h4><a name="REPORTING_SUPPLIES">Reporting Supply Levels</a></h4>
+
+<p>CUPS tracks several "marker-*" attributes for ink/toner supply level
+reporting. These attributes allow applications to display the current supply
+levels for a printer without printer-specific software. <a href="#TABLE3">Table 3</a> lists the marker attributes and what they represent.</p>
+
+<p>Filters set marker attributes by sending "ATTR:" messages to stderr. For
+example, a filter supporting an inkjet printer with black and tri-color ink
+cartridges would use the following to initialize the supply attributes:</p>
+
+<pre class="example">
+fputs("ATTR: marker-colors=#000000,#00FFFF#FF00FF#FFFF00\n", stderr);
+fputs("ATTR: marker-low-levels=5,10\n", stderr);
+fputs("ATTR: marker-names=Black,Tri-Color\n", stderr);
+fputs("ATTR: marker-types=ink,ink\n", stderr);
+</pre>
+
+<p>Then periodically the filter queries the printer for its current supply
+levels and updates them with a separate "ATTR:" message:</p>
+
+<pre class="example">
+int black_level, tri_level;
+...
+fprintf(stderr, "ATTR: marker-levels=%d,%d\n", black_level, tri_level);
+</pre>
+
+<div class='table'><table width='80%' summary='Table 3: Supply Level Attributes'>
+<caption>Table 3: <a name='TABLE3'>Supply Level Attributes</a></caption>
+<thead>
+<tr>
+ <th>Attribute</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>marker-colors</td>
+ <td>A list of comma-separated colors; each color is either "none" or one or
+ more hex-encoded sRGB colors of the form "#RRGGBB".</td>
+</tr>
+<tr>
+ <td>marker-high-levels</td>
+ <td>A list of comma-separated "almost full" level values from 0 to 100; a
+ value of 100 should be used for supplies that are consumed/emptied like ink
+ cartridges.</td>
+</tr>
+<tr>
+ <td>marker-levels</td>
+ <td>A list of comma-separated level values for each supply. A value of -1
+ indicates the level is unavailable, -2 indicates unknown, and -3 indicates
+ the level is unknown but has not yet reached capacity. Values from 0 to 100
+ indicate the corresponding percentage.</td>
+</tr>
+<tr>
+ <td>marker-low-levels</td>
+ <td>A list of comma-separated "almost empty" level values from 0 to 100; a
+ value of 0 should be used for supplies that are filled like waste ink
+ tanks.</td>
+</tr>
+<tr>
+ <td>marker-message</td>
+ <td>A human-readable supply status message for the user like "12 pages of
+ ink remaining."</td>
+</tr>
+<tr>
+ <td>marker-names</td>
+ <td>A list of comma-separated supply names like "Cyan Ink", "Fuser",
+ etc.</td>
+</tr>
+<tr>
+ <td>marker-types</td>
+ <td>A list of comma-separated supply types; the types are listed in
+ <a href="#TABLE1">Table 1</a>.</td>
+</tr>
+</tbody>
+</table></div>
+
+<h3><a name="COMMUNICATING_BACKEND">Communicating with the Backend</a></h3>
+
+<p>Filters can communicate with the backend via the
+<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> and
+<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a>
+functions. The
+<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> function
+reads data that has been sent back from the device and is typically used to
+obtain status and configuration information. For example, the following code
+polls the backend for back-channel data:</p>
+
+<pre class="example">
+#include &lt;cups/cups.h&gt;
+
+char buffer[8192];
+ssize_t bytes;
+
+/* Use a timeout of 0.0 seconds to poll for back-channel data */
+bytes = cupsBackChannelRead(buffer, sizeof(buffer), 0.0);
+</pre>
+
+<p>Filters can also use <code>select()</code> or <code>poll()</code> on the
+back-channel file descriptor (3 or <code>CUPS_BC_FD</code>) to read data only
+when it is available.</p>
+
+<p>The
+<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a>
+function allows you to get out-of-band status information and do synchronization
+with the device. For example, the following code gets the current IEEE-1284
+device ID string from the backend:</p>
+
+<pre class="example">
+#include &lt;cups/sidechannel.h&gt;
+
+char data[2049];
+int datalen;
+<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+
+/* Tell cupsSideChannelDoRequest() how big our buffer is, less 1 byte for
+ nul-termination... */
+datalen = sizeof(data) - 1;
+
+/* Get the IEEE-1284 device ID, waiting for up to 1 second */
+status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_GET_DEVICE_ID, data, &amp;datalen, 1.0);
+
+/* Use the returned value if OK was returned and the length is non-zero */
+if (status == CUPS_SC_STATUS_OK &amp;&amp; datalen > 0)
+ data[datalen] = '\0';
+else
+ data[0] = '\0';
+</pre>
+
+<h4><a name="DRAIN_OUTPUT">Forcing All Output to a Printer</a></h4>
+
+<p>The
+<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a>
+function allows you to tell the backend to send all pending data to the printer.
+This is most often needed when sending query commands to the printer. For example:</p>
+
+<pre class="example">
+#include &lt;cups/cups.h&gt;
+#include &lt;cups/sidechannel.h&gt;
+
+char data[1024];
+int datalen = sizeof(data);
+<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+
+/* Flush pending output to stdout */
+fflush(stdout);
+
+/* Drain output to backend, waiting for up to 30 seconds */
+status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_DRAIN_OUTPUT, data, &amp;datalen, 30.0);
+
+/* Read the response if the output was sent */
+if (status == CUPS_SC_STATUS_OK)
+{
+ ssize_t bytes;
+
+ /* Wait up to 10.0 seconds for back-channel data */
+ bytes = cupsBackChannelRead(data, sizeof(data), 10.0);
+ /* do something with the data from the printer */
+}
+</pre>
+
+<h3><a name="COMMUNICATING_FILTER">Communicating with Filters</a></h3>
+
+<p>Backends communicate with filters using the reciprocal functions
+<a href="#cupsBackChannelWrite"><code>cupsBackChannelWrite</code></a>,
+<a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>, and
+<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a>. We
+recommend writing back-channel data using a timeout of 1.0 seconds:</p>
+
+<pre class="example">
+#include &lt;cups/cups.h&gt;
+
+char buffer[8192];
+ssize_t bytes;
+
+/* Obtain data from printer/device */
+...
+
+/* Use a timeout of 1.0 seconds to give filters a chance to read */
+cupsBackChannelWrite(buffer, bytes, 1.0);
+</pre>
+
+<p>The <a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>
+function reads a side-channel command from a filter, driver, or port monitor.
+Backends can either poll for commands using a <code>timeout</code> of 0.0, wait
+indefinitely for commands using a <code>timeout</code> of -1.0 (probably in a
+separate thread for that purpose), or use <code>select</code> or
+<code>poll</code> on the <code>CUPS_SC_FD</code> file descriptor (4) to handle
+input and output on several file descriptors at the same time.</p>
+
+<p>Once a command is processed, the backend uses the
+<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function
+to send its response. For example, the following code shows how to poll for a
+side-channel command and respond to it:</p>
+
+<pre class="example">
+#include &lt;cups/sidechannel.h&gt;
+
+<a href="#cups_sc_command_t">cups_sc_command_t</a> command;
+<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+char data[2048];
+int datalen = sizeof(data);
+
+/* Poll for a command... */
+if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&amp;command, &amp;status, data, &amp;datalen, 0.0))
+{
+ switch (command)
+ {
+ /* handle supported commands, fill data/datalen/status with values as needed */
+
+ default :
+ status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
+ datalen = 0;
+ break;
+ }
+
+ /* Send a response... */
+ <a href="#cupsSideChannelWrite">cupsSideChannelWrite</a>(command, status, data, datalen, 1.0);
+}
+</pre>
+
+<h3><a name="SNMP">Doing SNMP Queries with Network Printers</a></h3>
+
+<p>The Simple Network Management Protocol (SNMP) allows you to get the current
+status, page counter, and supply levels from most network printers. Every
+piece of information is associated with an Object Identifier (OID), and
+every printer has a <em>community</em> name associated with it. OIDs can be
+queried directly or by "walking" over a range of OIDs with a common prefix.</p>
+
+<p>The two CUPS SNMP functions provide a simple API for querying network
+printers through the side-channel interface. Each accepts a string containing
+an OID like ".1.3.6.1.2.1.43.10.2.1.4.1.1" (the standard page counter OID)
+along with a timeout for the query.</p>
+
+<p>The <a href="#cupsSideChannelSNMPGet"><code>cupsSideChannelSNMPGet</code></a>
+function queries a single OID and returns the value as a string in a buffer
+you supply:</p>
+
+<pre class="example">
+#include &lt;cups/sidechannel.h&gt;
+
+char data[512];
+int datalen = sizeof(data);
+
+if (<a href="#cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a>(".1.3.6.1.2.1.43.10.2.1.4.1.1", data, &amp;datalen, 5.0)
+ == CUPS_SC_STATUS_OK)
+{
+ /* Do something with the value */
+ printf("Page counter is: %s\n", data);
+}
+</pre>
+
+<p>The
+<a href="#cupsSideChannelSNMPWalk"><code>cupsSideChannelSNMPWalk</code></a>
+function allows you to query a whole group of OIDs, calling a function of your
+choice for each OID that is found:</p>
+
+<pre class="example">
+#include &lt;cups/sidechannel.h&gt;
+
+void
+my_callback(const char *oid, const char *data, int datalen, void *context)
+{
+ /* Do something with the value */
+ printf("%s=%s\n", oid, data);
+}
+
+...
+
+void *my_data;
+
+<a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data);
+</pre>
diff --git a/cups/libs/cups/api-httpipp.header b/cups/libs/cups/api-httpipp.header
new file mode 100644
index 000000000..5f245d750
--- /dev/null
+++ b/cups/libs/cups/api-httpipp.header
@@ -0,0 +1,37 @@
+<!--
+ "$Id: api-httpipp.header 7258 2008-01-28 00:15:05Z mike $"
+
+ HTTP and IPP API header 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/".
+-->
+
+<h1 class='title'>HTTP and IPP APIs</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'>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/libs/cups/api-httpipp.shtml b/cups/libs/cups/api-httpipp.shtml
new file mode 100644
index 000000000..cd0fd53b5
--- /dev/null
+++ b/cups/libs/cups/api-httpipp.shtml
@@ -0,0 +1,317 @@
+<!--
+ "$Id: api-httpipp.shtml 7684 2008-06-23 16:47:38Z mike $"
+
+ 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/libs/cups/api-overview.header b/cups/libs/cups/api-overview.header
new file mode 100644
index 000000000..ecb14b601
--- /dev/null
+++ b/cups/libs/cups/api-overview.header
@@ -0,0 +1,53 @@
+<!--
+ "$Id: api-cups.header 7279 2008-01-31 01:50:44Z mike $"
+
+ Introduction to CUPS programming 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'>Introduction to CUPS Programming</h1>
+
+<div class='summary'><table summary='General Information'>
+<thead>
+<tr>
+ <th>Headers</th>
+ <th>cups/cups.h<br>
+ cups/array.h<br>
+ cups/backend.h<br>
+ cups/dir.h<br>
+ cups/file.h<br>
+ cups/ppd.h<br>
+ cups/raster.h<br>
+ cups/sidechannel.h</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <th>Libraries</th>
+ <td>-lcups<br>
+ -lcupsimage</td>
+</tr>
+<tr>
+ <th>See Also</th>
+ <td>Programming: <a href='raster-driver.html' target='_top'>Developing Raster Printer Drivers</a><br>
+ Programming: <a href='postscript-driver.html' target='_top'>Developing PostScript Printer Drivers</a><br>
+ Programming: <a href='api-filter.html' target='_top'>Filter and Backend Programming</a><br>
+ Programming: <a href='ppd-compiler.html' target='_top'>Introduction to the PPD Compiler</a><br>
+ Programming: <a href='api-array.html' target='_top'>Array API</a><br>
+ Programming: <a href='api-cups.html' target='_top'>CUPS API</a><br>
+ Programming: <a href='api-filedir.html' target='_top'>File and Directory APIs</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><br>
+ References: <a href='ref-ppdcfile.html' target='_top'>PPD Compiler Driver Information File Reference</a><br>
+ Specifications: <a href='spec-ppd.html' target='_top'>CUPS PPD Extensions</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/cups/api-overview.shtml b/cups/libs/cups/api-overview.shtml
new file mode 100644
index 000000000..3ece1033c
--- /dev/null
+++ b/cups/libs/cups/api-overview.shtml
@@ -0,0 +1,94 @@
+<!--
+ "$Id: api-cups.header 7279 2008-01-31 01:50:44Z mike $"
+
+ Introduction to CUPS programming content 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/".
+-->
+
+<h2 class="title"><a name="OVERVIEW">Overview</a></h2>
+
+<p>CUPS provides two libraries that interface with the different parts of the
+printing system. The "cups" library provides all of the common application and
+filter functions while the "cupsimage" library provides all of the imaging
+functions used in raster printer drivers. The "cups" library functions are
+accessed by including the <var>&lt;cups/cups.h&gt;</var> header, while
+"cupsimage" functions are found in the <var>&lt;cups/raster.h&gt;</var>
+header.</p>
+
+<h2 class="title"><a name="COMPILING">Compiling Programs</a></h2>
+
+<p>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.</p>
+
+<p>The following simple program lists the available printers on the system:</p>
+
+<pre class="example">
+#include &lt;stdio.h&gt;
+#include &lt;cups/cups.h&gt;
+
+int main(void)
+{
+ int i;
+ cups_dest_t *dests, *dest;
+ int num_dests = cupsGetDests(&amp;dests);
+
+ for (i = num_dests, dest = dests; i &gt; 0; i --, dest ++)
+ {
+ if (dest->instance)
+ printf("%s/%s\n", dest->name, dest->instance);
+ else
+ puts(dest->name);
+ }
+
+ return (0);
+}
+</pre>
+
+<h3><a name="XCODE">Compiling with Xcode</a></h3>
+
+<p>In Xcode, choose <var>New Project...</var> from the <var>File</var> menu,
+then select the <var>Standard Tool</var> project type under <var>Command Line
+Utility</var>. Click <var>Next</var> and choose a project directory. Click
+<var>Next</var> to create the project.</p>
+
+<p>In the project window, double-click on the <var>Targets</var> group and
+control-click on the simple target to show the context menu. Choose
+<var>Existing Framework...</var> from the <var>Add</var> submenu. When the file
+chooser sheet appears, press the <kbd>/</kbd> key and enter "/usr/lib". Scroll
+down the file list and select the <var>libcups.dylib</var> file. Click the
+<var>Add</var> button in the file chooser and attributes sheets.</p>
+
+<p>In the project window, double-click on the <var>main.c</var> source file.
+Replace the template source code with the listing above and save it. Click the
+<var>Build and Go</var> button to build the sample program and run it.</p>
+
+<h3><a name="COMMANDLINE">Compiling with GCC</a></h3>
+
+<p>From the command-line, create a file called <var>sample.c</var> using your
+favorite editor and then run the following command to compile it with GCC and
+run it:</p>
+
+<pre class="command">
+gcc -o simple `cups-config --cflags` simple.c `cups-config --libs`
+./simple
+</pre>
+
+<p>The <code>cups-config</code> command provides the compiler flags
+("cups-config --cflags") and libraries ("cups-config --libs") needed for the
+local system.</p>
+
+<h2 class="title"><a name="WHERETOGO">Where to Go Next</a></h2>
+
+<p>If you are developing a print filter, driver, or backend, see the
+<a href="api-filter.html" target="_top">Filter and Backend Programming</a>
+guide. Raster printer driver developers should also read the
+<a href="api-raster.html" target="_top">Raster API</a> reference.</p>
diff --git a/cups/libs/cups/api-ppd.header b/cups/libs/cups/api-ppd.header
new file mode 100644
index 000000000..ef0d05122
--- /dev/null
+++ b/cups/libs/cups/api-ppd.header
@@ -0,0 +1,38 @@
+<!--
+ "$Id: api-ppd.header 7616 2008-05-28 00:34:13Z mike $"
+
+ PPD API header for CUPS.
+
+ Copyright 2008-2012 by Apple Inc.
+
+ These coded instructions, statements, and computer programs are the
+ property of Apple Inc. and are protected by Federal copyright
+ law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ which should have been included with this file. If this file is
+ file is missing or damaged, see the license at "http://www.cups.org/".
+-->
+
+<h1 class='title'>PPD API (DEPRECATED)</h1>
+
+<blockquote>The PPD API is deprecated starting in CUPS 1.6/OS X 10.8. Please use the new Job Ticket APIs in the <a href="api-cups.html">CUPS API</a> documentation. These functions will be removed in a future release of CUPS.</blockquote>
+
+<div class='summary'><table summary='General Information'>
+<thead>
+<tr>
+ <th>Header</th>
+ <th>cups/ppd.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><br>
+ Specifications: <a href='spec-ppd.html' target='_top'>CUPS PPD Extensions</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/cups/api-ppd.shtml b/cups/libs/cups/api-ppd.shtml
new file mode 100644
index 000000000..6319f23f1
--- /dev/null
+++ b/cups/libs/cups/api-ppd.shtml
@@ -0,0 +1,219 @@
+<!--
+ "$Id: api-ppd.shtml 7616 2008-05-28 00:34:13Z mike $"
+
+ PPD 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>
+
+<blockquote>The PPD API is deprecated starting in CUPS 1.6/OS X 10.8. Please use the new Job Ticket APIs in the <a href="api-cups.html">CUPS API</a> documentation. These functions will be removed in a future release of CUPS.</blockquote>
+
+<p>The CUPS PPD API provides read-only access the data in PostScript Printer
+Description ("PPD") files which are used for all printers with a driver. With
+it you can obtain the data necessary to display printer options to users, mark
+option choices and check for conflicting choices, and output marked choices in
+PostScript output. The <a href="#ppd_file_t"><code>ppd_file_t</code></a>
+structure contains all of the information in a PPD file.</p>
+
+<blockquote><b>Note:</b>
+
+<p>The CUPS PPD API uses the terms "option" and "choice" instead of the Adobe
+terms "MainKeyword" and "OptionKeyword" to refer to specific printer options and
+features. CUPS also treats option ("MainKeyword") and choice ("OptionKeyword")
+values as case-insensitive strings, so option "InputSlot" and choice "Upper"
+are equivalent to "inputslot" and "upper", respectively.</p>
+</blockquote>
+
+<h3><a name="LOADING">Loading a PPD File</a></h3>
+
+<p>The <a href="#ppdOpenFile"><code>ppdOpenFile</code></a> function "opens" a
+PPD file and loads it into memory. For example, the following code opens the
+current printer's PPD file in a CUPS filter:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd = <a href="#ppdOpenFile">ppdOpenFile</a>(getenv("PPD"));
+</pre>
+
+<p>The return value is a pointer to a new
+<a href="#ppd_file_t"><code>ppd_file_t</code></a> structure or <code>NULL</code>
+if the PPD file does not exist or cannot be loaded. The
+<a href="#ppdClose"><code>ppdClose</code></a> function frees the memory used
+by the structure:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd;
+
+<a href="#ppdClose">ppdClose</a>(ppd);
+</pre>
+
+<p>Once closed, pointers to the <a href="#ppd_file_t"><code>ppd_file_t</code></a>
+structure and any data in it will no longer be valid.</p>
+
+<h3><a name="OPTIONS_AND_GROUPS">Options and Groups</a></h3>
+
+<p>PPD files support multiple options, which are stored in arrays of
+<a href="#ppd_option_t"><code>ppd_option_t</code></a> and
+<a href="#ppd_choice_t"><code>ppd_choice_t</code></a> structures.</p>
+
+<p>Each option in turn is associated with a group stored in a
+<a href="#ppd_group_t"><code>ppd_group_t</code></a> structure. Groups can be
+specified in the PPD file; if an option is not associated with a group
+then it is put in an automatically-generated "General" group. Groups can also
+have sub-groups, however CUPS currently ignores sub-groups because of past
+abuses of this functionality.</p>
+
+<p>Option choices are selected by marking them using one of three functions. The
+first is <a href="#ppdMarkDefaults"><code>ppdMarkDefaults</code></a> which
+selects all of the default options in the PPD file:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd;
+
+<a href="#ppdMarkDefaults">ppdMarkDefaults</a>(ppd);
+</pre>
+
+<p>The second is <a href="#ppdMarkOption"><code>ppdMarkOption</code></a>
+which selects a single option choice in the PPD file. For example, the following
+code selects the upper paper tray:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd;
+
+<a href="#ppdMarkOption">ppdMarkOption</a>(ppd, "InputSlot", "Upper");
+</pre>
+
+<p>The last function is
+<a href="#cupsMarkOptions"><code>cupsMarkOptions</code></a> which selects
+multiple option choices in the PPD file from an array of CUPS options, mapping
+IPP attributes like "media" and "sides" to their corresponding PPD options. You
+typically use this function in a print filter with
+<code>cupsParseOptions</code> and
+<a href="#ppdMarkDefaults"><code>ppdMarkDefaults</code></a> to select all of
+the option choices needed for the job, for example:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd = <a href="#ppdOpenFile">ppdOpenFile</a>(getenv("PPD"));
+cups_option_t *options = NULL;
+int num_options = cupsParseOptions(argv[5], 0, &amp;options);
+
+<a href="#ppdMarkDefaults">ppdMarkDefaults</a>(ppd);
+<a href="#cupsMarkOptions">cupsMarkOptions</a>(ppd, num_options, options);
+cupsFreeOptions(num_options, options);
+</pre>
+
+<h3><a name="CONSTRAINTS">Constraints</a></h3>
+
+<p>PPD files support specification of conflict conditions, called
+constraints, between different options. Constraints are stored in an array of
+<a href="#ppd_const_t"><code>ppd_const_t</code></a> structures which specify
+the options and choices that conflict with each other. The
+<a href="#ppdConflicts"><code>ppdConflicts</code></a> function tells you
+how many of the selected options are incompatible. Since constraints are
+normally specified in pairs, the returned value is typically an even number.</p>
+
+<h3><a name="PAGE_SIZES">Page Sizes</a></h3>
+
+<p>Page sizes are special options which have physical dimensions and margins
+associated with them. The size information is stored in
+<a href="#ppd_size_t"><code>ppd_size_t</code></a> structures and is available
+by looking up the named size with the
+<a href="#ppdPageSize"><code>ppdPageSize</code></a> function. The page size and
+margins are returned in units called points; there are 72 points per inch. If
+you pass <code>NULL</code> for the size, the currently selected size is
+returned:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd;
+<a href="#ppd_size_t">ppd_size_t</a> *size = <a href="#ppdPageSize">ppdPageSize</a>(ppd, NULL);
+</pre>
+
+<p>Besides the standard page sizes listed in a PPD file, some printers
+support variable or custom page sizes. Custom page sizes are supported if the
+<code>variables_sizes</code> member of the
+<a href="#ppd_file_t"><code>ppd_file_t</code></a> structure is non-zero.
+The <code>custom_min</code>, <code>custom_max</code>, and
+<code>custom_margins</code> members of the
+<a href="#ppd_file_t"><code>ppd_file_t</code></a> structure define the limits
+of the printable area. To get the resulting media size, use a page size string
+of the form "Custom.<I>width</I>x<I>length</I>", where "width" and "length" are
+in points. Custom page size names can also be specified in inches
+("Custom.<i>width</i>x<i>height</i>in"), centimeters
+("Custom.<i>width</i>x<i>height</i>cm"), or millimeters
+("Custom.<i>width</i>x<i>height</i>mm"):</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd;
+
+/* Get an 576x720 point custom page size */
+<a href="#ppd_size_t">ppd_size_t</a> *size = <a href="#ppdPageSize">ppdPageSize</a>(ppd, "Custom.576x720");
+
+/* Get an 8x10 inch custom page size */
+<a href="#ppd_size_t">ppd_size_t</a> *size = <a href="#ppdPageSize">ppdPageSize</a>(ppd, "Custom.8x10in");
+
+/* Get a 100x200 millimeter custom page size */
+<a href="#ppd_size_t">ppd_size_t</a> *size = <a href="#ppdPageSize">ppdPageSize</a>(ppd, "Custom.100x200mm");
+
+/* Get a 12.7x34.5 centimeter custom page size */
+<a href="#ppd_size_t">ppd_size_t</a> *size = <a href="#ppdPageSize">ppdPageSize</a>(ppd, "Custom.12.7x34.5cm");
+</pre>
+
+<p>If the PPD does not support variable page sizes, the
+<a href="#ppdPageSize"><code>ppdPageSize</code></a> function will return
+<code>NULL</code>.</p>
+
+<h3><a name="ATTRIBUTES">Attributes</a></h3>
+
+<p>Every PPD file is composed of one or more attributes. Most of these
+attributes are used to define groups, options, choices, and page sizes,
+however several informational attributes may be present which you can access
+in your program or filter. Attributes normally look like one of the following
+examples in a PPD file:</p>
+
+<pre class="example">
+*name: "value"
+*name spec: "value"
+*name spec/text: "value"
+</pre>
+
+<p>The <a href="#ppdFindAttr"><code>ppdFindAttr</code></a> and
+<a href="#ppdFindNextAttr"><code>ppdFindNextAttr</code></a> functions find the
+first and next instances, respectively, of the named attribute with the given
+"spec" string and return a <a href="#ppd_attr_t"><code>ppd_attr_t</code></a>
+structure. If you provide a NULL specifier string, all attributes with the
+given name will be returned. For example, the following code lists all of the
+<code>Product</code> attributes in a PPD file:</p>
+
+<pre class="example">
+#include &lt;cups/ppd.h&gt;
+
+<a href="#ppd_file_t">ppd_file_t</a> *ppd;
+<a href="#ppd_attr_t">ppd_attr_t</a> *attr;
+
+for (attr = <a href="#ppdFindAttr">ppdFindAttr</a>(ppd, "Product", NULL);
+ attr != NULL;
+ attr = <a href="#ppdFindNextAttr">ppdFindNextAttr</a>(ppd, "Product", NULL))
+ puts(attr->value);
+</pre>
diff --git a/cups/libs/cups/array-private.h b/cups/libs/cups/array-private.h
new file mode 100644
index 000000000..74b0c9bf0
--- /dev/null
+++ b/cups/libs/cups/array-private.h
@@ -0,0 +1,52 @@
+/*
+ * "$Id: array-private.h 3933 2012-10-01 03:01:10Z msweet $"
+ *
+ * Private array definitions for CUPS.
+ *
+ * Copyright 2011-2012 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_ARRAY_PRIVATE_H_
+# define _CUPS_ARRAY_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/array.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Functions...
+ */
+
+extern int _cupsArrayAddStrings(cups_array_t *a, const char *s,
+ char delim) _CUPS_API_1_5;
+extern cups_array_t *_cupsArrayNewStrings(const char *s, char delim)
+ _CUPS_API_1_5;
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_ARRAY_PRIVATE_H_ */
+
+/*
+ * End of "$Id: array-private.h 3933 2012-10-01 03:01:10Z msweet $".
+ */
diff --git a/cups/libs/cups/array.c b/cups/libs/cups/array.c
new file mode 100644
index 000000000..29ae6d95e
--- /dev/null
+++ b/cups/libs/cups/array.c
@@ -0,0 +1,1366 @@
+/*
+ * "$Id: array.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Sorted array routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsArrayAdd() - Add an element to the array.
+ * _cupsArrayAddStrings() - Add zero or more comma-delimited strings to an
+ * array.
+ * cupsArrayClear() - Clear the array.
+ * cupsArrayCount() - Get the number of elements in the array.
+ * cupsArrayCurrent() - Return the current element in the array.
+ * cupsArrayDelete() - Free all memory used by the array.
+ * cupsArrayDup() - Duplicate the array.
+ * cupsArrayFind() - Find an element in the array.
+ * cupsArrayFirst() - Get the first element in the array.
+ * cupsArrayGetIndex() - Get the index of the current element.
+ * cupsArrayGetInsert() - Get the index of the last inserted element.
+ * cupsArrayIndex() - Get the N-th element in the array.
+ * cupsArrayInsert() - Insert an element in the array.
+ * cupsArrayLast() - Get the last element in the array.
+ * cupsArrayNew() - Create a new array.
+ * cupsArrayNew2() - Create a new array with hash.
+ * cupsArrayNew3() - Create a new array with hash and/or free function.
+ * _cupsArrayNewStrings() - Create a new array of comma-delimited strings.
+ * cupsArrayNext() - Get the next element in the array.
+ * cupsArrayPrev() - Get the previous element in the array.
+ * cupsArrayRemove() - Remove an element from the array.
+ * cupsArrayRestore() - Reset the current element to the last @link
+ * cupsArraySave@.
+ * cupsArraySave() - Mark the current element for a later @link
+ * cupsArrayRestore@.
+ * cupsArrayUserData() - Return the user data for an array.
+ * cups_array_add() - Insert or append an element to the array.
+ * cups_array_find() - Find an element in the array.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+#include "debug-private.h"
+#include "array-private.h"
+
+
+/*
+ * Limits...
+ */
+
+#define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/
+
+
+/*
+ * Types and structures...
+ */
+
+struct _cups_array_s /**** CUPS array structure ****/
+{
+ /*
+ * The current implementation uses an insertion sort into an array of
+ * sorted pointers. We leave the array type private/opaque so that we
+ * can change the underlying implementation without affecting the users
+ * of this API.
+ */
+
+ int num_elements, /* Number of array elements */
+ alloc_elements, /* Allocated array elements */
+ current, /* Current element */
+ insert, /* Last inserted element */
+ unique, /* Are all elements unique? */
+ num_saved, /* Number of saved elements */
+ saved[_CUPS_MAXSAVE];
+ /* Saved elements */
+ void **elements; /* Array elements */
+ cups_array_func_t compare; /* Element comparison function */
+ void *data; /* User data passed to compare */
+ cups_ahash_func_t hashfunc; /* Hash function */
+ int hashsize, /* Size of hash */
+ *hash; /* Hash array */
+ cups_acopy_func_t copyfunc; /* Copy function */
+ cups_afree_func_t freefunc; /* Free function */
+};
+
+
+/*
+ * Local functions...
+ */
+
+static int cups_array_add(cups_array_t *a, void *e, int insert);
+static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff);
+
+
+/*
+ * 'cupsArrayAdd()' - Add an element to the array.
+ *
+ * When adding an element to a sorted array, non-unique elements are
+ * appended at the end of the run of identical elements. For unsorted arrays,
+ * the element is appended to the end of the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsArrayAdd(cups_array_t *a, /* I - Array */
+ void *e) /* I - Element */
+{
+ DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", a, e));
+
+ /*
+ * Range check input...
+ */
+
+ if (!a || !e)
+ {
+ DEBUG_puts("3cupsArrayAdd: returning 0");
+ return (0);
+ }
+
+ /*
+ * Append the element...
+ */
+
+ return (cups_array_add(a, e, 0));
+}
+
+
+/*
+ * '_cupsArrayAddStrings()' - Add zero or more delimited strings to an array.
+ *
+ * Note: The array MUST be created using the @link _cupsArrayNewStrings@
+ * function. Duplicate strings are NOT added. If the string pointer "s" is NULL
+ * or the empty string, no strings are added to the array.
+ */
+
+int /* O - 1 on success, 0 on failure */
+_cupsArrayAddStrings(cups_array_t *a, /* I - Array */
+ const char *s, /* I - Delimited strings or NULL */
+ char delim)/* I - Delimiter character */
+{
+ char *buffer, /* Copy of string */
+ *start, /* Start of string */
+ *end; /* End of string */
+ int status = 1; /* Status of add */
+
+
+ DEBUG_printf(("_cupsArrayAddStrings(a=%p, s=\"%s\", delim='%c')", a, s,
+ delim));
+
+ if (!a || !s || !*s)
+ {
+ DEBUG_puts("1_cupsArrayAddStrings: Returning 0");
+ return (0);
+ }
+
+ if (delim == ' ')
+ {
+ /*
+ * Skip leading whitespace...
+ */
+
+ DEBUG_puts("1_cupsArrayAddStrings: Skipping leading whitespace.");
+
+ while (*s && isspace(*s & 255))
+ s ++;
+
+ DEBUG_printf(("1_cupsArrayAddStrings: Remaining string \"%s\".", s));
+ }
+
+ if (!strchr(s, delim) &&
+ (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n'))))
+ {
+ /*
+ * String doesn't contain a delimiter, so add it as a single value...
+ */
+
+ DEBUG_puts("1_cupsArrayAddStrings: No delimiter seen, adding a single "
+ "value.");
+
+ if (!cupsArrayFind(a, (void *)s))
+ status = cupsArrayAdd(a, (void *)s);
+ }
+ else if ((buffer = strdup(s)) == NULL)
+ {
+ DEBUG_puts("1_cupsArrayAddStrings: Unable to duplicate string.");
+ status = 0;
+ }
+ else
+ {
+ for (start = end = buffer; *end; start = end)
+ {
+ /*
+ * Find the end of the current delimited string and see if we need to add
+ * it...
+ */
+
+ if (delim == ' ')
+ {
+ while (*end && !isspace(*end & 255))
+ end ++;
+ while (*end && isspace(*end & 255))
+ *end++ = '\0';
+ }
+ else if ((end = strchr(start, delim)) != NULL)
+ *end++ = '\0';
+ else
+ end = start + strlen(start);
+
+ DEBUG_printf(("1_cupsArrayAddStrings: Adding \"%s\", end=\"%s\"", start,
+ end));
+
+ if (!cupsArrayFind(a, start))
+ status &= cupsArrayAdd(a, start);
+ }
+
+ free(buffer);
+ }
+
+ DEBUG_printf(("1_cupsArrayAddStrings: Returning %d.", status));
+
+ return (status);
+}
+
+
+/*
+ * 'cupsArrayClear()' - Clear the array.
+ *
+ * This function is equivalent to removing all elements in the array.
+ * The caller is responsible for freeing the memory used by the
+ * elements themselves.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsArrayClear(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return;
+
+ /*
+ * Free the existing elements as needed..
+ */
+
+ if (a->freefunc)
+ {
+ int i; /* Looping var */
+ void **e; /* Current element */
+
+ for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
+ (a->freefunc)(*e, a->data);
+ }
+
+ /*
+ * Set the number of elements to 0; we don't actually free the memory
+ * here - that is done in cupsArrayDelete()...
+ */
+
+ a->num_elements = 0;
+ a->current = -1;
+ a->insert = -1;
+ a->unique = 1;
+ a->num_saved = 0;
+}
+
+
+/*
+ * 'cupsArrayCount()' - Get the number of elements in the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Number of elements */
+cupsArrayCount(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (0);
+
+ /*
+ * Return the number of elements...
+ */
+
+ return (a->num_elements);
+}
+
+
+/*
+ * 'cupsArrayCurrent()' - Return the current element in the array.
+ *
+ * The current element is undefined until you call @link cupsArrayFind@,
+ * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - Element */
+cupsArrayCurrent(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (NULL);
+
+ /*
+ * Return the current element...
+ */
+
+ if (a->current >= 0 && a->current < a->num_elements)
+ return (a->elements[a->current]);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'cupsArrayDelete()' - Free all memory used by the array.
+ *
+ * The caller is responsible for freeing the memory used by the
+ * elements themselves.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsArrayDelete(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return;
+
+ /*
+ * Free the elements if we have a free function (otherwise the caller is
+ * responsible for doing the dirty work...)
+ */
+
+ if (a->freefunc)
+ {
+ int i; /* Looping var */
+ void **e; /* Current element */
+
+ for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
+ (a->freefunc)(*e, a->data);
+ }
+
+ /*
+ * Free the array of element pointers...
+ */
+
+ if (a->alloc_elements)
+ free(a->elements);
+
+ if (a->hashsize)
+ free(a->hash);
+
+ free(a);
+}
+
+
+/*
+ * 'cupsArrayDup()' - Duplicate the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_array_t * /* O - Duplicate array */
+cupsArrayDup(cups_array_t *a) /* I - Array */
+{
+ cups_array_t *da; /* Duplicate array */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (NULL);
+
+ /*
+ * Allocate memory for the array...
+ */
+
+ da = calloc(1, sizeof(cups_array_t));
+ if (!da)
+ return (NULL);
+
+ da->compare = a->compare;
+ da->data = a->data;
+ da->current = a->current;
+ da->insert = a->insert;
+ da->unique = a->unique;
+ da->num_saved = a->num_saved;
+
+ memcpy(da->saved, a->saved, sizeof(a->saved));
+
+ if (a->num_elements)
+ {
+ /*
+ * Allocate memory for the elements...
+ */
+
+ da->elements = malloc(a->num_elements * sizeof(void *));
+ if (!da->elements)
+ {
+ free(da);
+ return (NULL);
+ }
+
+ /*
+ * Copy the element pointers...
+ */
+
+ if (a->copyfunc)
+ {
+ /*
+ * Use the copy function to make a copy of each element...
+ */
+
+ int i; /* Looping var */
+
+ for (i = 0; i < a->num_elements; i ++)
+ da->elements[i] = (a->copyfunc)(a->elements[i], a->data);
+ }
+ else
+ {
+ /*
+ * Just copy raw pointers...
+ */
+
+ memcpy(da->elements, a->elements, a->num_elements * sizeof(void *));
+ }
+
+ da->num_elements = a->num_elements;
+ da->alloc_elements = a->num_elements;
+ }
+
+ /*
+ * Return the new array...
+ */
+
+ return (da);
+}
+
+
+/*
+ * 'cupsArrayFind()' - Find an element in the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - Element found or @code NULL@ */
+cupsArrayFind(cups_array_t *a, /* I - Array */
+ void *e) /* I - Element */
+{
+ int current, /* Current element */
+ diff, /* Difference */
+ hash; /* Hash index */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!a || !e)
+ return (NULL);
+
+ /*
+ * See if we have any elements...
+ */
+
+ if (!a->num_elements)
+ return (NULL);
+
+ /*
+ * Yes, look for a match...
+ */
+
+ if (a->hash)
+ {
+ hash = (*(a->hashfunc))(e, a->data);
+
+ if (hash < 0 || hash >= a->hashsize)
+ {
+ current = a->current;
+ hash = -1;
+ }
+ else
+ {
+ current = a->hash[hash];
+
+ if (current < 0 || current >= a->num_elements)
+ current = a->current;
+ }
+ }
+ else
+ {
+ current = a->current;
+ hash = -1;
+ }
+
+ current = cups_array_find(a, e, current, &diff);
+ if (!diff)
+ {
+ /*
+ * Found a match! If the array does not contain unique values, find
+ * the first element that is the same...
+ */
+
+ if (!a->unique && a->compare)
+ {
+ /*
+ * The array is not unique, find the first match...
+ */
+
+ while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
+ a->data))
+ current --;
+ }
+
+ a->current = current;
+
+ if (hash >= 0)
+ a->hash[hash] = current;
+
+ return (a->elements[current]);
+ }
+ else
+ {
+ /*
+ * No match...
+ */
+
+ a->current = -1;
+
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'cupsArrayFirst()' - Get the first element in the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - First element or @code NULL@ if the array is empty */
+cupsArrayFirst(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (NULL);
+
+ /*
+ * Return the first element...
+ */
+
+ a->current = 0;
+
+ return (cupsArrayCurrent(a));
+}
+
+
+/*
+ * 'cupsArrayGetIndex()' - Get the index of the current element.
+ *
+ * The current element is undefined until you call @link cupsArrayFind@,
+ * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - Index of the current element, starting at 0 */
+cupsArrayGetIndex(cups_array_t *a) /* I - Array */
+{
+ if (!a)
+ return (-1);
+ else
+ return (a->current);
+}
+
+
+/*
+ * 'cupsArrayGetInsert()' - Get the index of the last inserted element.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - Index of the last inserted element, starting at 0 */
+cupsArrayGetInsert(cups_array_t *a) /* I - Array */
+{
+ if (!a)
+ return (-1);
+ else
+ return (a->insert);
+}
+
+
+/*
+ * 'cupsArrayIndex()' - Get the N-th element in the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - N-th element or @code NULL@ */
+cupsArrayIndex(cups_array_t *a, /* I - Array */
+ int n) /* I - Index into array, starting at 0 */
+{
+ if (!a)
+ return (NULL);
+
+ a->current = n;
+
+ return (cupsArrayCurrent(a));
+}
+
+
+/*
+ * 'cupsArrayInsert()' - Insert an element in the array.
+ *
+ * When inserting an element in a sorted array, non-unique elements are
+ * inserted at the beginning of the run of identical elements. For unsorted
+ * arrays, the element is inserted at the beginning of the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on failure, 1 on success */
+cupsArrayInsert(cups_array_t *a, /* I - Array */
+ void *e) /* I - Element */
+{
+ DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", a, e));
+
+ /*
+ * Range check input...
+ */
+
+ if (!a || !e)
+ {
+ DEBUG_puts("3cupsArrayInsert: returning 0");
+ return (0);
+ }
+
+ /*
+ * Insert the element...
+ */
+
+ return (cups_array_add(a, e, 1));
+}
+
+
+/*
+ * 'cupsArrayLast()' - Get the last element in the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - Last element or @code NULL@ if the array is empty */
+cupsArrayLast(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (NULL);
+
+ /*
+ * Return the last element...
+ */
+
+ a->current = a->num_elements - 1;
+
+ return (cupsArrayCurrent(a));
+}
+
+
+/*
+ * 'cupsArrayNew()' - Create a new array.
+ *
+ * The comparison function ("f") is used to create a sorted array. The function
+ * receives pointers to two elements and the user data pointer ("d") - the user
+ * data pointer argument can safely be omitted when not required so functions
+ * like @code strcmp@ can be used for sorted string arrays.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_array_t * /* O - Array */
+cupsArrayNew(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
+ void *d) /* I - User data pointer or @code NULL@ */
+{
+ return (cupsArrayNew3(f, d, 0, 0, 0, 0));
+}
+
+
+/*
+ * 'cupsArrayNew2()' - Create a new array with hash.
+ *
+ * The comparison function ("f") is used to create a sorted array. The function
+ * receives pointers to two elements and the user data pointer ("d") - the user
+ * data pointer argument can safely be omitted when not required so functions
+ * like @code strcmp@ can be used for sorted string arrays.
+ *
+ * The hash function ("h") is used to implement cached lookups with the
+ * specified hash size ("hsize").
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+cups_array_t * /* O - Array */
+cupsArrayNew2(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
+ void *d, /* I - User data or @code NULL@ */
+ cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
+ int hsize) /* I - Hash size (>= 0) */
+{
+ return (cupsArrayNew3(f, d, h, hsize, 0, 0));
+}
+
+
+/*
+ * 'cupsArrayNew3()' - Create a new array with hash and/or free function.
+ *
+ * The comparison function ("f") is used to create a sorted array. The function
+ * receives pointers to two elements and the user data pointer ("d") - the user
+ * data pointer argument can safely be omitted when not required so functions
+ * like @code strcmp@ can be used for sorted string arrays.
+ *
+ * The hash function ("h") is used to implement cached lookups with the
+ * specified hash size ("hsize").
+ *
+ * The copy function ("cf") is used to automatically copy/retain elements when
+ * added or the array is copied.
+ *
+ * The free function ("cf") is used to automatically free/release elements when
+ * removed or the array is deleted.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+cups_array_t * /* O - Array */
+cupsArrayNew3(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
+ void *d, /* I - User data or @code NULL@ */
+ cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
+ int hsize, /* I - Hash size (>= 0) */
+ cups_acopy_func_t cf, /* I - Copy function */
+ cups_afree_func_t ff) /* I - Free function */
+{
+ cups_array_t *a; /* Array */
+
+
+ /*
+ * Allocate memory for the array...
+ */
+
+ a = calloc(1, sizeof(cups_array_t));
+ if (!a)
+ return (NULL);
+
+ a->compare = f;
+ a->data = d;
+ a->current = -1;
+ a->insert = -1;
+ a->num_saved = 0;
+ a->unique = 1;
+
+ if (hsize > 0 && h)
+ {
+ a->hashfunc = h;
+ a->hashsize = hsize;
+ a->hash = malloc(hsize * sizeof(int));
+
+ if (!a->hash)
+ {
+ free(a);
+ return (NULL);
+ }
+
+ memset(a->hash, -1, hsize * sizeof(int));
+ }
+
+ a->copyfunc = cf;
+ a->freefunc = ff;
+
+ return (a);
+}
+
+
+/*
+ * '_cupsArrayNewStrings()' - Create a new array of comma-delimited strings.
+ *
+ * Note: The array automatically manages copies of the strings passed. If the
+ * string pointer "s" is NULL or the empty string, no strings are added to the
+ * newly created array.
+ */
+
+cups_array_t * /* O - Array */
+_cupsArrayNewStrings(const char *s, /* I - Delimited strings or NULL */
+ char delim) /* I - Delimiter character */
+{
+ cups_array_t *a; /* Array */
+
+
+ if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree)) != NULL)
+ _cupsArrayAddStrings(a, s, delim);
+
+ return (a);
+}
+
+
+/*
+ * 'cupsArrayNext()' - Get the next element in the array.
+ *
+ * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)".
+ *
+ * The next element is undefined until you call @link cupsArrayFind@,
+ * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
+ * to set the current element.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - Next element or @code NULL@ */
+cupsArrayNext(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (NULL);
+
+ /*
+ * Return the next element...
+ */
+
+ if (a->current < a->num_elements)
+ a->current ++;
+
+ return (cupsArrayCurrent(a));
+}
+
+
+/*
+ * 'cupsArrayPrev()' - Get the previous element in the array.
+ *
+ * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)".
+ *
+ * The previous element is undefined until you call @link cupsArrayFind@,
+ * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
+ * to set the current element.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - Previous element or @code NULL@ */
+cupsArrayPrev(cups_array_t *a) /* I - Array */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!a)
+ return (NULL);
+
+ /*
+ * Return the previous element...
+ */
+
+ if (a->current >= 0)
+ a->current --;
+
+ return (cupsArrayCurrent(a));
+}
+
+
+/*
+ * 'cupsArrayRemove()' - Remove an element from the array.
+ *
+ * If more than one element matches "e", only the first matching element is
+ * removed.
+ *
+ * The caller is responsible for freeing the memory used by the
+ * removed element.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsArrayRemove(cups_array_t *a, /* I - Array */
+ void *e) /* I - Element */
+{
+ int i, /* Looping var */
+ current, /* Current element */
+ diff; /* Difference */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!a || !e)
+ return (0);
+
+ /*
+ * See if the element is in the array...
+ */
+
+ if (!a->num_elements)
+ return (0);
+
+ current = cups_array_find(a, e, a->current, &diff);
+ if (diff)
+ return (0);
+
+ /*
+ * Yes, now remove it...
+ */
+
+ a->num_elements --;
+
+ if (a->freefunc)
+ (a->freefunc)(a->elements[current], a->data);
+
+ if (current < a->num_elements)
+ memmove(a->elements + current, a->elements + current + 1,
+ (a->num_elements - current) * sizeof(void *));
+
+ if (current <= a->current)
+ a->current --;
+
+ if (current < a->insert)
+ a->insert --;
+ else if (current == a->insert)
+ a->insert = -1;
+
+ for (i = 0; i < a->num_saved; i ++)
+ if (current <= a->saved[i])
+ a->saved[i] --;
+
+ if (a->num_elements <= 1)
+ a->unique = 1;
+
+ return (1);
+}
+
+
+/*
+ * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - New current element */
+cupsArrayRestore(cups_array_t *a) /* I - Array */
+{
+ if (!a)
+ return (NULL);
+
+ if (a->num_saved <= 0)
+ return (NULL);
+
+ a->num_saved --;
+ a->current = a->saved[a->num_saved];
+
+ if (a->current >= 0 && a->current < a->num_elements)
+ return (a->elements[a->current]);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@.
+ *
+ * The current element is undefined until you call @link cupsArrayFind@,
+ * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
+ * to set the current element.
+ *
+ * The save/restore stack is guaranteed to be at least 32 elements deep.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsArraySave(cups_array_t *a) /* I - Array */
+{
+ if (!a)
+ return (0);
+
+ if (a->num_saved >= _CUPS_MAXSAVE)
+ return (0);
+
+ a->saved[a->num_saved] = a->current;
+ a->num_saved ++;
+
+ return (1);
+}
+
+
+/*
+ * 'cupsArrayUserData()' - Return the user data for an array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void * /* O - User data */
+cupsArrayUserData(cups_array_t *a) /* I - Array */
+{
+ if (a)
+ return (a->data);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'cups_array_add()' - Insert or append an element to the array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+static int /* O - 1 on success, 0 on failure */
+cups_array_add(cups_array_t *a, /* I - Array */
+ void *e, /* I - Element to add */
+ int insert) /* I - 1 = insert, 0 = append */
+{
+ int i, /* Looping var */
+ current, /* Current element */
+ diff; /* Comparison with current element */
+
+
+ DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", a, e, insert));
+
+ /*
+ * Verify we have room for the new element...
+ */
+
+ if (a->num_elements >= a->alloc_elements)
+ {
+ /*
+ * Allocate additional elements; start with 16 elements, then
+ * double the size until 1024 elements, then add 1024 elements
+ * thereafter...
+ */
+
+ void **temp; /* New array elements */
+ int count; /* New allocation count */
+
+
+ if (a->alloc_elements == 0)
+ {
+ count = 16;
+ temp = malloc(count * sizeof(void *));
+ }
+ else
+ {
+ if (a->alloc_elements < 1024)
+ count = a->alloc_elements * 2;
+ else
+ count = a->alloc_elements + 1024;
+
+ temp = realloc(a->elements, count * sizeof(void *));
+ }
+
+ DEBUG_printf(("9cups_array_add: count=%d", count));
+
+ if (!temp)
+ {
+ DEBUG_puts("9cups_array_add: allocation failed, returning 0");
+ return (0);
+ }
+
+ a->alloc_elements = count;
+ a->elements = temp;
+ }
+
+ /*
+ * Find the insertion point for the new element; if there is no
+ * compare function or elements, just add it to the beginning or end...
+ */
+
+ if (!a->num_elements || !a->compare)
+ {
+ /*
+ * No elements or comparison function, insert/append as needed...
+ */
+
+ if (insert)
+ current = 0; /* Insert at beginning */
+ else
+ current = a->num_elements; /* Append to the end */
+ }
+ else
+ {
+ /*
+ * Do a binary search for the insertion point...
+ */
+
+ current = cups_array_find(a, e, a->insert, &diff);
+
+ if (diff > 0)
+ {
+ /*
+ * Insert after the current element...
+ */
+
+ current ++;
+ }
+ else if (!diff)
+ {
+ /*
+ * Compared equal, make sure we add to the begining or end of
+ * the current run of equal elements...
+ */
+
+ a->unique = 0;
+
+ if (insert)
+ {
+ /*
+ * Insert at beginning of run...
+ */
+
+ while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
+ a->data))
+ current --;
+ }
+ else
+ {
+ /*
+ * Append at end of run...
+ */
+
+ do
+ {
+ current ++;
+ }
+ while (current < a->num_elements &&
+ !(*(a->compare))(e, a->elements[current], a->data));
+ }
+ }
+ }
+
+ /*
+ * Insert or append the element...
+ */
+
+ if (current < a->num_elements)
+ {
+ /*
+ * Shift other elements to the right...
+ */
+
+ memmove(a->elements + current + 1, a->elements + current,
+ (a->num_elements - current) * sizeof(void *));
+
+ if (a->current >= current)
+ a->current ++;
+
+ for (i = 0; i < a->num_saved; i ++)
+ if (a->saved[i] >= current)
+ a->saved[i] ++;
+
+ DEBUG_printf(("9cups_array_add: insert element at index %d...", current));
+ }
+#ifdef DEBUG
+ else
+ DEBUG_printf(("9cups_array_add: append element at %d...", current));
+#endif /* DEBUG */
+
+ if (a->copyfunc)
+ {
+ if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL)
+ {
+ DEBUG_puts("8cups_array_add: Copy function returned NULL, returning 0");
+ return (0);
+ }
+ }
+ else
+ a->elements[current] = e;
+
+ a->num_elements ++;
+ a->insert = current;
+
+#ifdef DEBUG
+ for (current = 0; current < a->num_elements; current ++)
+ DEBUG_printf(("9cups_array_add: a->elements[%d]=%p", current,
+ a->elements[current]));
+#endif /* DEBUG */
+
+ DEBUG_puts("9cups_array_add: returning 1");
+
+ return (1);
+}
+
+
+/*
+ * 'cups_array_find()' - Find an element in the array.
+ */
+
+static int /* O - Index of match */
+cups_array_find(cups_array_t *a, /* I - Array */
+ void *e, /* I - Element */
+ int prev, /* I - Previous index */
+ int *rdiff) /* O - Difference of match */
+{
+ int left, /* Left side of search */
+ right, /* Right side of search */
+ current, /* Current element */
+ diff; /* Comparison with current element */
+
+
+ DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", a, e, prev,
+ rdiff));
+
+ if (a->compare)
+ {
+ /*
+ * Do a binary search for the element...
+ */
+
+ DEBUG_puts("9cups_array_find: binary search");
+
+ if (prev >= 0 && prev < a->num_elements)
+ {
+ /*
+ * Start search on either side of previous...
+ */
+
+ if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 ||
+ (diff < 0 && prev == 0) ||
+ (diff > 0 && prev == (a->num_elements - 1)))
+ {
+ /*
+ * Exact or edge match, return it!
+ */
+
+ DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff));
+
+ *rdiff = diff;
+
+ return (prev);
+ }
+ else if (diff < 0)
+ {
+ /*
+ * Start with previous on right side...
+ */
+
+ left = 0;
+ right = prev;
+ }
+ else
+ {
+ /*
+ * Start wih previous on left side...
+ */
+
+ left = prev;
+ right = a->num_elements - 1;
+ }
+ }
+ else
+ {
+ /*
+ * Start search in the middle...
+ */
+
+ left = 0;
+ right = a->num_elements - 1;
+ }
+
+ do
+ {
+ current = (left + right) / 2;
+ diff = (*(a->compare))(e, a->elements[current], a->data);
+
+ DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d",
+ left, right, current, diff));
+
+ if (diff == 0)
+ break;
+ else if (diff < 0)
+ right = current;
+ else
+ left = current;
+ }
+ while ((right - left) > 1);
+
+ if (diff != 0)
+ {
+ /*
+ * Check the last 1 or 2 elements...
+ */
+
+ if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0)
+ current = left;
+ else
+ {
+ diff = (*(a->compare))(e, a->elements[right], a->data);
+ current = right;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Do a linear pointer search...
+ */
+
+ DEBUG_puts("9cups_array_find: linear search");
+
+ diff = 1;
+
+ for (current = 0; current < a->num_elements; current ++)
+ if (a->elements[current] == e)
+ {
+ diff = 0;
+ break;
+ }
+ }
+
+ /*
+ * Return the closest element and the difference...
+ */
+
+ DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff));
+
+ *rdiff = diff;
+
+ return (current);
+}
+
+
+/*
+ * End of "$Id: array.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/array.h b/cups/libs/cups/array.h
new file mode 100644
index 000000000..7a5fc584d
--- /dev/null
+++ b/cups/libs/cups/array.h
@@ -0,0 +1,92 @@
+/*
+ * "$Id: array.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Sorted array definitions for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_ARRAY_H_
+# define _CUPS_ARRAY_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "versioning.h"
+# include <stdlib.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef struct _cups_array_s cups_array_t;
+ /**** CUPS array type ****/
+typedef int (*cups_array_func_t)(void *first, void *second, void *data);
+ /**** Array comparison function ****/
+typedef int (*cups_ahash_func_t)(void *element, void *data);
+ /**** Array hash function ****/
+typedef void *(*cups_acopy_func_t)(void *element, void *data);
+ /**** Array element copy function ****/
+typedef void (*cups_afree_func_t)(void *element, void *data);
+ /**** Array element free function ****/
+
+
+/*
+ * Functions...
+ */
+
+extern int cupsArrayAdd(cups_array_t *a, void *e) _CUPS_API_1_2;
+extern void cupsArrayClear(cups_array_t *a) _CUPS_API_1_2;
+extern int cupsArrayCount(cups_array_t *a) _CUPS_API_1_2;
+extern void *cupsArrayCurrent(cups_array_t *a) _CUPS_API_1_2;
+extern void cupsArrayDelete(cups_array_t *a) _CUPS_API_1_2;
+extern cups_array_t *cupsArrayDup(cups_array_t *a) _CUPS_API_1_2;
+extern void *cupsArrayFind(cups_array_t *a, void *e) _CUPS_API_1_2;
+extern void *cupsArrayFirst(cups_array_t *a) _CUPS_API_1_2;
+extern int cupsArrayGetIndex(cups_array_t *a) _CUPS_API_1_3;
+extern int cupsArrayGetInsert(cups_array_t *a) _CUPS_API_1_3;
+extern void *cupsArrayIndex(cups_array_t *a, int n) _CUPS_API_1_2;
+extern int cupsArrayInsert(cups_array_t *a, void *e) _CUPS_API_1_2;
+extern void *cupsArrayLast(cups_array_t *a) _CUPS_API_1_2;
+extern cups_array_t *cupsArrayNew(cups_array_func_t f, void *d) _CUPS_API_1_2;
+extern cups_array_t *cupsArrayNew2(cups_array_func_t f, void *d,
+ cups_ahash_func_t h, int hsize) _CUPS_API_1_3;
+extern cups_array_t *cupsArrayNew3(cups_array_func_t f, void *d,
+ cups_ahash_func_t h, int hsize,
+ cups_acopy_func_t cf,
+ cups_afree_func_t ff) _CUPS_API_1_5;
+extern void *cupsArrayNext(cups_array_t *a) _CUPS_API_1_2;
+extern void *cupsArrayPrev(cups_array_t *a) _CUPS_API_1_2;
+extern int cupsArrayRemove(cups_array_t *a, void *e) _CUPS_API_1_2;
+extern void *cupsArrayRestore(cups_array_t *a) _CUPS_API_1_2;
+extern int cupsArraySave(cups_array_t *a) _CUPS_API_1_2;
+extern void *cupsArrayUserData(cups_array_t *a) _CUPS_API_1_2;
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_ARRAY_H_ */
+
+/*
+ * End of "$Id: array.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/attr.c b/cups/libs/cups/attr.c
new file mode 100644
index 000000000..c3ab07b1f
--- /dev/null
+++ b/cups/libs/cups/attr.c
@@ -0,0 +1,335 @@
+/*
+ * "$Id: attr.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * PPD model-specific attribute routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * Contents:
+ *
+ * ppdFindAttr() - Find the first matching attribute.
+ * ppdFindNextAttr() - Find the next matching attribute.
+ * _ppdNormalizeMakeAndModel() - Normalize a product/make-and-model string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "ppd-private.h"
+
+
+/*
+ * 'ppdFindAttr()' - Find the first matching attribute.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ppd_attr_t * /* O - Attribute or @code NULL@ if not found */
+ppdFindAttr(ppd_file_t *ppd, /* I - PPD file data */
+ const char *name, /* I - Attribute name */
+ const char *spec) /* I - Specifier string or @code NULL@ */
+{
+ ppd_attr_t key, /* Search key */
+ *attr; /* Current attribute */
+
+
+ DEBUG_printf(("2ppdFindAttr(ppd=%p, name=\"%s\", spec=\"%s\")", ppd, name,
+ spec));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !name || ppd->num_attrs == 0)
+ return (NULL);
+
+ /*
+ * Search for a matching attribute...
+ */
+
+ memset(&key, 0, sizeof(key));
+ strlcpy(key.name, name, sizeof(key.name));
+
+ /*
+ * Return the first matching attribute, if any...
+ */
+
+ if ((attr = (ppd_attr_t *)cupsArrayFind(ppd->sorted_attrs, &key)) != NULL)
+ {
+ if (spec)
+ {
+ /*
+ * Loop until we find the first matching attribute for "spec"...
+ */
+
+ while (attr && _cups_strcasecmp(spec, attr->spec))
+ {
+ if ((attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) != NULL &&
+ _cups_strcasecmp(attr->name, name))
+ attr = NULL;
+ }
+ }
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ppdFindNextAttr()' - Find the next matching attribute.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ppd_attr_t * /* O - Attribute or @code NULL@ if not found */
+ppdFindNextAttr(ppd_file_t *ppd, /* I - PPD file data */
+ const char *name, /* I - Attribute name */
+ const char *spec) /* I - Specifier string or @code NULL@ */
+{
+ ppd_attr_t *attr; /* Current attribute */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !name || ppd->num_attrs == 0)
+ return (NULL);
+
+ /*
+ * See if there are more attributes to return...
+ */
+
+ while ((attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) != NULL)
+ {
+ /*
+ * Check the next attribute to see if it is a match...
+ */
+
+ if (_cups_strcasecmp(attr->name, name))
+ {
+ /*
+ * Nope, reset the current pointer to the end of the array...
+ */
+
+ cupsArrayIndex(ppd->sorted_attrs, cupsArrayCount(ppd->sorted_attrs));
+
+ return (NULL);
+ }
+
+ if (!spec || !_cups_strcasecmp(attr->spec, spec))
+ break;
+ }
+
+ /*
+ * Return the next attribute's value...
+ */
+
+ return (attr);
+}
+
+
+/*
+ * '_ppdNormalizeMakeAndModel()' - Normalize a product/make-and-model string.
+ *
+ * This function tries to undo the mistakes made by many printer manufacturers
+ * to produce a clean make-and-model string we can use.
+ */
+
+char * /* O - Normalized make-and-model string or NULL on error */
+_ppdNormalizeMakeAndModel(
+ const char *make_and_model, /* I - Original make-and-model string */
+ char *buffer, /* I - String buffer */
+ size_t bufsize) /* I - Size of string buffer */
+{
+ char *bufptr; /* Pointer into buffer */
+
+
+ if (!make_and_model || !buffer || bufsize < 1)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (_cups_isspace(*make_and_model))
+ make_and_model ++;
+
+ /*
+ * Remove parenthesis and add manufacturers as needed...
+ */
+
+ if (make_and_model[0] == '(')
+ {
+ strlcpy(buffer, make_and_model + 1, bufsize);
+
+ if ((bufptr = strrchr(buffer, ')')) != NULL)
+ *bufptr = '\0';
+ }
+ else if (!_cups_strncasecmp(make_and_model, "XPrint", 6))
+ {
+ /*
+ * Xerox XPrint...
+ */
+
+ snprintf(buffer, bufsize, "Xerox %s", make_and_model);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "Eastman", 7))
+ {
+ /*
+ * Kodak...
+ */
+
+ snprintf(buffer, bufsize, "Kodak %s", make_and_model + 7);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "laserwriter", 11))
+ {
+ /*
+ * Apple LaserWriter...
+ */
+
+ snprintf(buffer, bufsize, "Apple LaserWriter%s", make_and_model + 11);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "colorpoint", 10))
+ {
+ /*
+ * Seiko...
+ */
+
+ snprintf(buffer, bufsize, "Seiko %s", make_and_model);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "fiery", 5))
+ {
+ /*
+ * EFI...
+ */
+
+ snprintf(buffer, bufsize, "EFI %s", make_and_model);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "ps ", 3) ||
+ !_cups_strncasecmp(make_and_model, "colorpass", 9))
+ {
+ /*
+ * Canon...
+ */
+
+ snprintf(buffer, bufsize, "Canon %s", make_and_model);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "primera", 7))
+ {
+ /*
+ * Fargo...
+ */
+
+ snprintf(buffer, bufsize, "Fargo %s", make_and_model);
+ }
+ else if (!_cups_strncasecmp(make_and_model, "designjet", 9) ||
+ !_cups_strncasecmp(make_and_model, "deskjet", 7))
+ {
+ /*
+ * HP...
+ */
+
+ snprintf(buffer, bufsize, "HP %s", make_and_model);
+ }
+ else
+ strlcpy(buffer, make_and_model, bufsize);
+
+ /*
+ * Clean up the make...
+ */
+
+ if (!_cups_strncasecmp(buffer, "agfa", 4))
+ {
+ /*
+ * Replace with AGFA (all uppercase)...
+ */
+
+ buffer[0] = 'A';
+ buffer[1] = 'G';
+ buffer[2] = 'F';
+ buffer[3] = 'A';
+ }
+ else if (!_cups_strncasecmp(buffer, "Hewlett-Packard hp ", 19))
+ {
+ /*
+ * Just put "HP" on the front...
+ */
+
+ buffer[0] = 'H';
+ buffer[1] = 'P';
+ _cups_strcpy(buffer + 2, buffer + 18);
+ }
+ else if (!_cups_strncasecmp(buffer, "Hewlett-Packard ", 16))
+ {
+ /*
+ * Just put "HP" on the front...
+ */
+
+ buffer[0] = 'H';
+ buffer[1] = 'P';
+ _cups_strcpy(buffer + 2, buffer + 15);
+ }
+ else if (!_cups_strncasecmp(buffer, "Lexmark International", 21))
+ {
+ /*
+ * Strip "International"...
+ */
+
+ _cups_strcpy(buffer + 8, buffer + 21);
+ }
+ else if (!_cups_strncasecmp(buffer, "herk", 4))
+ {
+ /*
+ * Replace with LHAG...
+ */
+
+ buffer[0] = 'L';
+ buffer[1] = 'H';
+ buffer[2] = 'A';
+ buffer[3] = 'G';
+ }
+ else if (!_cups_strncasecmp(buffer, "linotype", 8))
+ {
+ /*
+ * Replace with LHAG...
+ */
+
+ buffer[0] = 'L';
+ buffer[1] = 'H';
+ buffer[2] = 'A';
+ buffer[3] = 'G';
+ _cups_strcpy(buffer + 4, buffer + 8);
+ }
+
+ /*
+ * Remove trailing whitespace and return...
+ */
+
+ for (bufptr = buffer + strlen(buffer) - 1;
+ bufptr >= buffer && _cups_isspace(*bufptr);
+ bufptr --);
+
+ bufptr[1] = '\0';
+
+ return (buffer[0] ? buffer : NULL);
+}
+
+
+/*
+ * End of "$Id: attr.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/auth.c b/cups/libs/cups/auth.c
new file mode 100644
index 000000000..9d38b3911
--- /dev/null
+++ b/cups/libs/cups/auth.c
@@ -0,0 +1,892 @@
+/*
+ * "$Id: auth.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Authentication functions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * This file contains Kerberos support code, copyright 2006 by
+ * Jelmer Vernooij.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsDoAuthentication() - Authenticate a request.
+ * _cupsSetNegotiateAuthString() - Set the Kerberos authentication string.
+ * cups_gss_acquire() - Kerberos credentials callback.
+ * cups_gss_getname() - Get CUPS service credentials for
+ * authentication.
+ * cups_gss_printf() - Show debug error messages from GSSAPI.
+ * cups_local_auth() - Get the local authorization certificate if
+ * available/applicable.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+#if HAVE_AUTHORIZATION_H
+# include <Security/Authorization.h>
+# ifdef HAVE_SECBASEPRIV_H
+# include <Security/SecBasePriv.h>
+# else
+extern const char *cssmErrorString(int error);
+# endif /* HAVE_SECBASEPRIV_H */
+#endif /* HAVE_AUTHORIZATION_H */
+
+#if defined(SO_PEERCRED) && defined(AF_LOCAL)
+# include <pwd.h>
+#endif /* SO_PEERCRED && AF_LOCAL */
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef HAVE_GSSAPI
+# ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
+# ifdef HAVE_GSS_GSSAPI_SPI_H
+# include <GSS/gssapi_spi.h>
+# 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
+{
+ uint32_t type;
+ uint32_t flags;
+ char *username;
+ char *realm;
+ char *password;
+ gss_buffer_t *credentialsRef;
+} gss_auth_identity_desc;
+extern OM_uint32 gss_acquire_cred_ex_f(gss_status_id_t, const gss_name_t,
+ OM_uint32, OM_uint32, const gss_OID,
+ gss_cred_usage_t, gss_auth_identity_t,
+ void *, void (*)(void *, OM_uint32,
+ gss_status_id_t,
+ gss_cred_id_t,
+ gss_OID_set,
+ OM_uint32));
+# endif /* HAVE_GSS_GSSAPI_SPI_H */
+# include <dispatch/dispatch.h>
+typedef struct _cups_gss_acquire_s /* Acquire callback data */
+{
+ dispatch_semaphore_t sem; /* Synchronization semaphore */
+ OM_uint32 major; /* Returned status code */
+ gss_cred_id_t creds; /* Returned credentials */
+} _cups_gss_acquire_t;
+
+static void cups_gss_acquire(void *ctx, OM_uint32 major,
+ gss_status_id_t status,
+ gss_cred_id_t creds, gss_OID_set oids,
+ OM_uint32 time_rec);
+# endif /* HAVE_GSS_ACQUIRE_CRED_EX_F */
+static gss_name_t cups_gss_getname(http_t *http, const char *service_name);
+# ifdef DEBUG
+static void cups_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
+ const char *message);
+# else
+# define cups_gss_printf(major, minor, message)
+# endif /* DEBUG */
+#endif /* HAVE_GSSAPI */
+static int cups_local_auth(http_t *http);
+
+
+/*
+ * 'cupsDoAuthentication()' - Authenticate a request.
+ *
+ * This function should be called in response to a @code HTTP_STATUS_UNAUTHORIZED@
+ * status, prior to resubmitting your request.
+ *
+ * @since CUPS 1.1.20/OS X 10.4@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsDoAuthentication(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *method, /* I - Request method ("GET", "POST", "PUT") */
+ const char *resource) /* I - Resource path */
+{
+ const char *password, /* Password string */
+ *www_auth; /* WWW-Authenticate header */
+ char prompt[1024], /* Prompt for user */
+ realm[HTTP_MAX_VALUE], /* realm="xyz" string */
+ nonce[HTTP_MAX_VALUE]; /* nonce="xyz" string */
+ int localauth; /* Local authentication result */
+ _cups_globals_t *cg; /* Global data */
+
+
+ DEBUG_printf(("cupsDoAuthentication(http=%p, method=\"%s\", resource=\"%s\")",
+ http, method, resource));
+
+ if (!http)
+ http = _cupsConnect();
+
+ if (!http || !method || !resource)
+ return (-1);
+
+ DEBUG_printf(("2cupsDoAuthentication: digest_tries=%d, userpass=\"%s\"",
+ http->digest_tries, http->userpass));
+ DEBUG_printf(("2cupsDoAuthentication: WWW-Authenticate=\"%s\"",
+ httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)));
+
+ /*
+ * Clear the current authentication string...
+ */
+
+ httpSetAuthString(http, NULL, NULL);
+
+ /*
+ * See if we can do local authentication...
+ */
+
+ if (http->digest_tries < 3)
+ {
+ if ((localauth = cups_local_auth(http)) == 0)
+ {
+ DEBUG_printf(("2cupsDoAuthentication: authstring=\"%s\"",
+ http->authstring));
+
+ if (http->status == HTTP_STATUS_UNAUTHORIZED)
+ http->digest_tries ++;
+
+ return (0);
+ }
+ else if (localauth == -1)
+ {
+ http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ return (-1); /* Error or canceled */
+ }
+ }
+
+ /*
+ * Nope, see if we should retry the current username:password...
+ */
+
+ www_auth = http->fields[HTTP_FIELD_WWW_AUTHENTICATE];
+
+ if ((http->digest_tries > 1 || !http->userpass[0]) &&
+ (!_cups_strncasecmp(www_auth, "Basic", 5) ||
+ !_cups_strncasecmp(www_auth, "Digest", 6)))
+ {
+ /*
+ * Nope - get a new password from the user...
+ */
+
+ char default_username[HTTP_MAX_VALUE];
+ /* Default username */
+
+ cg = _cupsGlobals();
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
+ default_username))
+ cupsSetUser(default_username);
+
+ snprintf(prompt, sizeof(prompt),
+ _cupsLangString(cg->lang_default, _("Password for %s on %s? ")),
+ cupsUser(),
+ http->hostname[0] == '/' ? "localhost" : http->hostname);
+
+ http->digest_tries = _cups_strncasecmp(www_auth, "Digest", 6) != 0;
+ http->userpass[0] = '\0';
+
+ if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
+ {
+ http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ return (-1);
+ }
+
+ snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(),
+ password);
+ }
+ else if (http->status == HTTP_STATUS_UNAUTHORIZED)
+ http->digest_tries ++;
+
+ if (http->status == HTTP_STATUS_UNAUTHORIZED && http->digest_tries >= 3)
+ {
+ DEBUG_printf(("1cupsDoAuthentication: Too many authentication tries (%d)",
+ http->digest_tries));
+
+ http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ return (-1);
+ }
+
+ /*
+ * Got a password; encode it for the server...
+ */
+
+#ifdef HAVE_GSSAPI
+ if (!_cups_strncasecmp(www_auth, "Negotiate", 9))
+ {
+ /*
+ * Kerberos authentication...
+ */
+
+ if (_cupsSetNegotiateAuthString(http, method, resource))
+ {
+ http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ return (-1);
+ }
+ }
+ else
+#endif /* HAVE_GSSAPI */
+ if (!_cups_strncasecmp(www_auth, "Basic", 5))
+ {
+ /*
+ * Basic authentication...
+ */
+
+ char encode[256]; /* Base64 buffer */
+
+
+ httpEncode64_2(encode, sizeof(encode), http->userpass,
+ (int)strlen(http->userpass));
+ httpSetAuthString(http, "Basic", encode);
+ }
+ else if (!_cups_strncasecmp(www_auth, "Digest", 6))
+ {
+ /*
+ * Digest authentication...
+ */
+
+ char encode[33], /* MD5 buffer */
+ digest[1024]; /* Digest auth data */
+
+
+ httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm);
+ httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce);
+
+ httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode);
+ httpMD5Final(nonce, method, resource, encode);
+ snprintf(digest, sizeof(digest),
+ "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
+ "response=\"%s\"", cupsUser(), realm, nonce, resource, encode);
+ httpSetAuthString(http, "Digest", digest);
+ }
+ else
+ {
+ DEBUG_printf(("1cupsDoAuthentication: Unknown auth type: \"%s\"",
+ www_auth));
+ http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ return (-1);
+ }
+
+ DEBUG_printf(("1cupsDoAuthentication: authstring=\"%s\"", http->authstring));
+
+ return (0);
+}
+
+
+#ifdef HAVE_GSSAPI
+/*
+ * '_cupsSetNegotiateAuthString()' - Set the Kerberos authentication string.
+ */
+
+int /* O - 0 on success, -1 on error */
+_cupsSetNegotiateAuthString(
+ http_t *http, /* I - Connection to server */
+ const char *method, /* I - Request method ("GET", "POST", "PUT") */
+ const char *resource) /* I - Resource path */
+{
+ OM_uint32 minor_status, /* Minor status code */
+ major_status; /* Major status code */
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ /* Output token */
+
+
+ (void)method;
+ (void)resource;
+
+# ifdef __APPLE__
+ /*
+ * If the weak-linked GSSAPI/Kerberos library is not present, don't try
+ * to use it...
+ */
+
+ if (gss_init_sec_context == NULL)
+ {
+ DEBUG_puts("1_cupsSetNegotiateAuthString: Weak-linked GSSAPI/Kerberos "
+ "framework is not present");
+ return (-1);
+ }
+# endif /* __APPLE__ */
+
+ if (http->gssname == GSS_C_NO_NAME)
+ {
+ http->gssname = cups_gss_getname(http, _cupsGSSServiceName());
+ }
+
+ if (http->gssctx != GSS_C_NO_CONTEXT)
+ {
+ gss_delete_sec_context(&minor_status, &http->gssctx, GSS_C_NO_BUFFER);
+ http->gssctx = GSS_C_NO_CONTEXT;
+ }
+
+ major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
+ &http->gssctx,
+ http->gssname, http->gssmech,
+ GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER, &http->gssmech,
+ &output_token, NULL, NULL);
+
+#ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
+ if (major_status == GSS_S_NO_CRED)
+ {
+ /*
+ * Ask the user for credentials...
+ */
+
+ char prompt[1024], /* Prompt for user */
+ userbuf[256]; /* Kerberos username */
+ const char *username, /* Username string */
+ *password; /* Password string */
+ _cups_gss_acquire_t data; /* Callback data */
+ gss_auth_identity_desc identity; /* Kerberos user identity */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Per-thread global data */
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ snprintf(prompt, sizeof(prompt),
+ _cupsLangString(cg->lang_default, _("Password for %s on %s? ")),
+ cupsUser(), http->gsshost);
+
+ if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
+ return (-1);
+
+ /*
+ * Try to acquire credentials...
+ */
+
+ username = cupsUser();
+ if (!strchr(username, '@'))
+ {
+ snprintf(userbuf, sizeof(userbuf), "%s@%s", username, http->gsshost);
+ username = userbuf;
+ }
+
+ identity.type = GSS_AUTH_IDENTITY_TYPE_1;
+ identity.flags = 0;
+ identity.username = (char *)username;
+ identity.realm = (char *)"";
+ identity.password = (char *)password;
+ identity.credentialsRef = NULL;
+
+ data.sem = dispatch_semaphore_create(0);
+ data.major = 0;
+ data.creds = NULL;
+
+ if (data.sem)
+ {
+ major_status = gss_acquire_cred_ex_f(NULL, GSS_C_NO_NAME, 0,
+ GSS_C_INDEFINITE, GSS_KRB5_MECHANISM,
+ GSS_C_INITIATE, &identity, &data,
+ cups_gss_acquire);
+
+ if (major_status == GSS_S_COMPLETE)
+ {
+ dispatch_semaphore_wait(data.sem, DISPATCH_TIME_FOREVER);
+ major_status = data.major;
+ }
+
+ dispatch_release(data.sem);
+
+ if (major_status == GSS_S_COMPLETE)
+ {
+ OM_uint32 release_minor; /* Minor status from releasing creds */
+
+ major_status = gss_init_sec_context(&minor_status, data.creds,
+ &http->gssctx,
+ http->gssname, http->gssmech,
+ GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER, &http->gssmech,
+ &output_token, NULL, NULL);
+ gss_release_cred(&release_minor, &data.creds);
+ }
+ }
+ }
+#endif /* HAVE_GSS_ACQUIRED_CRED_EX_F */
+
+ if (GSS_ERROR(major_status))
+ {
+ cups_gss_printf(major_status, minor_status,
+ "_cupsSetNegotiateAuthString: Unable to initialize "
+ "security context");
+ return (-1);
+ }
+
+#ifdef DEBUG
+ else if (major_status == GSS_S_CONTINUE_NEEDED)
+ cups_gss_printf(major_status, minor_status,
+ "_cupsSetNegotiateAuthString: Continuation needed!");
+#endif /* DEBUG */
+
+ if (output_token.length > 0 && output_token.length <= 65536)
+ {
+ /*
+ * Allocate the authorization string since Windows KDCs can have
+ * arbitrarily large credentials...
+ */
+
+ int authsize = 10 + /* "Negotiate " */
+ output_token.length * 4 / 3 + 1 + /* Base64 */
+ 1; /* nul */
+
+ httpSetAuthString(http, NULL, NULL);
+
+ if ((http->authstring = malloc(authsize)) == NULL)
+ {
+ http->authstring = http->_authstring;
+ authsize = sizeof(http->_authstring);
+ }
+
+ strlcpy(http->authstring, "Negotiate ", authsize);
+ httpEncode64_2(http->authstring + 10, authsize - 10, output_token.value,
+ output_token.length);
+
+ gss_release_buffer(&minor_status, &output_token);
+ }
+ else
+ {
+ DEBUG_printf(("1_cupsSetNegotiateAuthString: Kerberos credentials too "
+ "large - %d bytes!", (int)output_token.length));
+ gss_release_buffer(&minor_status, &output_token);
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+# ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
+/*
+ * 'cups_gss_acquire()' - Kerberos credentials callback.
+ */
+static void
+cups_gss_acquire(
+ void *ctx, /* I - Caller context */
+ OM_uint32 major, /* I - Major error code */
+ gss_status_id_t status, /* I - Status (unused) */
+ gss_cred_id_t creds, /* I - Credentials (if any) */
+ gss_OID_set oids, /* I - Mechanism OIDs (unused) */
+ OM_uint32 time_rec) /* I - Timestamp (unused) */
+{
+ uint32_t min; /* Minor error code */
+ _cups_gss_acquire_t *data; /* Callback data */
+
+
+ (void)status;
+ (void)time_rec;
+
+ data = (_cups_gss_acquire_t *)ctx;
+ data->major = major;
+ data->creds = creds;
+
+ gss_release_oid_set(&min, &oids);
+ dispatch_semaphore_signal(data->sem);
+}
+# endif /* HAVE_GSS_ACQUIRE_CRED_EX_F */
+
+
+/*
+ * 'cups_gss_getname()' - Get CUPS service credentials for authentication.
+ */
+
+static gss_name_t /* O - Server name */
+cups_gss_getname(
+ http_t *http, /* I - Connection to server */
+ const char *service_name) /* I - Service name */
+{
+ gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+ /* Service token */
+ OM_uint32 major_status, /* Major status code */
+ minor_status; /* Minor status code */
+ gss_name_t server_name; /* Server name */
+ char buf[1024]; /* Name buffer */
+
+
+ DEBUG_printf(("7cups_gss_getname(http=%p, service_name=\"%s\")", http,
+ service_name));
+
+
+ /*
+ * Get the hostname...
+ */
+
+ if (!http->gsshost[0])
+ {
+ httpGetHostname(http, http->gsshost, sizeof(http->gsshost));
+
+ if (!strcmp(http->gsshost, "localhost"))
+ {
+ if (gethostname(http->gsshost, sizeof(http->gsshost)) < 0)
+ {
+ DEBUG_printf(("1cups_gss_getname: gethostname() failed: %s",
+ strerror(errno)));
+ http->gsshost[0] = '\0';
+ return (NULL);
+ }
+
+ if (!strchr(http->gsshost, '.'))
+ {
+ /*
+ * The hostname is not a FQDN, so look it up...
+ */
+
+ struct hostent *host; /* Host entry to get FQDN */
+
+ if ((host = gethostbyname(http->gsshost)) != NULL && host->h_name)
+ {
+ /*
+ * Use the resolved hostname...
+ */
+
+ strlcpy(http->gsshost, host->h_name, sizeof(http->gsshost));
+ }
+ else
+ {
+ DEBUG_printf(("1cups_gss_getname: gethostbyname(\"%s\") failed.",
+ http->gsshost));
+ http->gsshost[0] = '\0';
+ return (NULL);
+ }
+ }
+ }
+ }
+
+ /*
+ * Get a service name we can use for authentication purposes...
+ */
+
+ snprintf(buf, sizeof(buf), "%s@%s", service_name, http->gsshost);
+
+ DEBUG_printf(("8cups_gss_getname: Looking up \"%s\".", buf));
+
+ token.value = buf;
+ token.length = strlen(buf);
+ server_name = GSS_C_NO_NAME;
+ major_status = gss_import_name(&minor_status, &token,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ &server_name);
+
+ if (GSS_ERROR(major_status))
+ {
+ cups_gss_printf(major_status, minor_status,
+ "cups_gss_getname: gss_import_name() failed");
+ return (NULL);
+ }
+
+ return (server_name);
+}
+
+
+# ifdef DEBUG
+/*
+ * 'cups_gss_printf()' - Show debug error messages from GSSAPI.
+ */
+
+static void
+cups_gss_printf(OM_uint32 major_status,/* I - Major status code */
+ OM_uint32 minor_status,/* I - Minor status code */
+ const char *message) /* I - Prefix for error message */
+{
+ OM_uint32 err_major_status, /* Major status code for display */
+ err_minor_status; /* Minor status code for display */
+ OM_uint32 msg_ctx; /* Message context */
+ gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
+ /* Major status message */
+ minor_status_string = GSS_C_EMPTY_BUFFER;
+ /* Minor status message */
+
+
+ msg_ctx = 0;
+ err_major_status = gss_display_status(&err_minor_status,
+ major_status,
+ GSS_C_GSS_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &major_status_string);
+
+ if (!GSS_ERROR(err_major_status))
+ gss_display_status(&err_minor_status, minor_status, GSS_C_MECH_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &minor_status_string);
+
+ DEBUG_printf(("1%s: %s, %s", message, (char *)major_status_string.value,
+ (char *)minor_status_string.value));
+
+ gss_release_buffer(&err_minor_status, &major_status_string);
+ gss_release_buffer(&err_minor_status, &minor_status_string);
+}
+# endif /* DEBUG */
+#endif /* HAVE_GSSAPI */
+
+
+/*
+ * 'cups_local_auth()' - Get the local authorization certificate if
+ * available/applicable.
+ */
+
+static int /* O - 0 if available */
+ /* 1 if not available */
+ /* -1 error */
+cups_local_auth(http_t *http) /* I - HTTP connection to server */
+{
+#if defined(WIN32) || defined(__EMX__)
+ /*
+ * Currently WIN32 and OS-2 do not support the CUPS server...
+ */
+
+ return (1);
+#else
+ int pid; /* Current process ID */
+ FILE *fp; /* Certificate file */
+ char trc[16], /* Try Root Certificate parameter */
+ filename[1024]; /* Certificate filename */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+# if defined(HAVE_AUTHORIZATION_H)
+ OSStatus status; /* Status */
+ AuthorizationItem auth_right; /* Authorization right */
+ AuthorizationRights auth_rights; /* Authorization rights */
+ AuthorizationFlags auth_flags; /* Authorization flags */
+ AuthorizationExternalForm auth_extrn; /* Authorization ref external */
+ char auth_key[1024]; /* Buffer */
+ char buffer[1024]; /* Buffer */
+# endif /* HAVE_AUTHORIZATION_H */
+
+
+ DEBUG_printf(("7cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"",
+ http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname));
+
+ /*
+ * See if we are accessing localhost...
+ */
+
+ if (!httpAddrLocalhost(http->hostaddr) &&
+ _cups_strcasecmp(http->hostname, "localhost") != 0)
+ {
+ DEBUG_puts("8cups_local_auth: Not a local connection!");
+ return (1);
+ }
+
+# if defined(HAVE_AUTHORIZATION_H)
+ /*
+ * Delete any previous authorization reference...
+ */
+
+ if (http->auth_ref)
+ {
+ AuthorizationFree(http->auth_ref, kAuthorizationFlagDefaults);
+ http->auth_ref = NULL;
+ }
+
+ if (!getenv("GATEWAY_INTERFACE") &&
+ httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
+ auth_key, sizeof(auth_key)))
+ {
+ status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
+ kAuthorizationFlagDefaults, &http->auth_ref);
+ if (status != errAuthorizationSuccess)
+ {
+ DEBUG_printf(("8cups_local_auth: AuthorizationCreate() returned %d (%s)",
+ (int)status, cssmErrorString(status)));
+ return (-1);
+ }
+
+ auth_right.name = auth_key;
+ auth_right.valueLength = 0;
+ auth_right.value = NULL;
+ auth_right.flags = 0;
+
+ auth_rights.count = 1;
+ auth_rights.items = &auth_right;
+
+ auth_flags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagPreAuthorize |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagExtendRights;
+
+ status = AuthorizationCopyRights(http->auth_ref, &auth_rights,
+ kAuthorizationEmptyEnvironment,
+ auth_flags, NULL);
+ if (status == errAuthorizationSuccess)
+ status = AuthorizationMakeExternalForm(http->auth_ref, &auth_extrn);
+
+ if (status == errAuthorizationSuccess)
+ {
+ /*
+ * Set the authorization string and return...
+ */
+
+ httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn,
+ sizeof(auth_extrn));
+
+ httpSetAuthString(http, "AuthRef", buffer);
+
+ DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
+ http->authstring));
+ return (0);
+ }
+ else if (status == errAuthorizationCanceled)
+ return (-1);
+
+ DEBUG_printf(("9cups_local_auth: AuthorizationCopyRights() returned %d (%s)",
+ (int)status, cssmErrorString(status)));
+
+ /*
+ * Fall through to try certificates...
+ */
+ }
+# endif /* HAVE_AUTHORIZATION_H */
+
+# if defined(SO_PEERCRED) && defined(AF_LOCAL)
+ /*
+ * See if we can authenticate using the peer credentials provided over a
+ * domain socket; if so, specify "PeerCred username" as the authentication
+ * information...
+ */
+
+ if (
+# ifdef HAVE_GSSAPI
+ strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9) &&
+# endif /* HAVE_GSSAPI */
+# ifdef HAVE_AUTHORIZATION_H
+ !httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
+ auth_key, sizeof(auth_key)) &&
+# endif /* HAVE_AUTHORIZATION_H */
+ http->hostaddr->addr.sa_family == AF_LOCAL &&
+ !getenv("GATEWAY_INTERFACE")) /* Not via CGI programs... */
+ {
+ /*
+ * Verify that the current cupsUser() matches the current UID...
+ */
+
+ struct passwd *pwd; /* Password information */
+ const char *username; /* Current username */
+
+ username = cupsUser();
+
+ if ((pwd = getpwnam(username)) != NULL && pwd->pw_uid == getuid())
+ {
+ httpSetAuthString(http, "PeerCred", username);
+
+ DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
+ http->authstring));
+
+ return (0);
+ }
+ }
+# endif /* SO_PEERCRED && AF_LOCAL */
+
+ /*
+ * Try opening a certificate file for this PID. If that fails,
+ * try the root certificate...
+ */
+
+ pid = getpid();
+ snprintf(filename, sizeof(filename), "%s/certs/%d", cg->cups_statedir, pid);
+ if ((fp = fopen(filename, "r")) == NULL && pid > 0)
+ {
+ /*
+ * No certificate for this PID; see if we can get the root certificate...
+ */
+
+ DEBUG_printf(("9cups_local_auth: Unable to open file %s: %s",
+ filename, strerror(errno)));
+
+# ifdef HAVE_GSSAPI
+ if (!strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
+ {
+ /*
+ * Kerberos required, don't try the root certificate...
+ */
+
+ return (1);
+ }
+# endif /* HAVE_GSSAPI */
+
+# ifdef HAVE_AUTHORIZATION_H
+ if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
+ auth_key, sizeof(auth_key)))
+ {
+ /*
+ * Don't use the root certificate as a replacement for an authkey...
+ */
+
+ return (1);
+ }
+# endif /* HAVE_AUTHORIZATION_H */
+ if (!httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "trc", trc,
+ sizeof(trc)))
+ {
+ /*
+ * Scheduler doesn't want us to use the root certificate...
+ */
+
+ return (1);
+ }
+
+ snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
+ fp = fopen(filename, "r");
+ }
+
+ if (fp)
+ {
+ /*
+ * Read the certificate from the file...
+ */
+
+ char certificate[33], /* Certificate string */
+ *certptr; /* Pointer to certificate string */
+
+ certptr = fgets(certificate, sizeof(certificate), fp);
+ fclose(fp);
+
+ if (certptr)
+ {
+ /*
+ * Set the authorization string and return...
+ */
+
+ httpSetAuthString(http, "Local", certificate);
+
+ DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
+ http->authstring));
+
+ return (0);
+ }
+ }
+
+ return (1);
+#endif /* WIN32 || __EMX__ */
+}
+
+
+/*
+ * End of "$Id: auth.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/backchannel.c b/cups/libs/cups/backchannel.c
new file mode 100644
index 000000000..66fa1ce4d
--- /dev/null
+++ b/cups/libs/cups/backchannel.c
@@ -0,0 +1,199 @@
+/*
+ * "$Id: backchannel.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Backchannel functions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsBackChannelRead() - Read data from the backchannel.
+ * cupsBackChannelWrite() - Write data to the backchannel.
+ * cups_setup() - Setup select()
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include <errno.h>
+#ifdef WIN32
+# include <io.h>
+# include <fcntl.h>
+#else
+# include <sys/time.h>
+#endif /* WIN32 */
+
+
+/*
+ * Local functions...
+ */
+
+static void cups_setup(fd_set *set, struct timeval *tval,
+ double timeout);
+
+
+/*
+ * 'cupsBackChannelRead()' - Read data from the backchannel.
+ *
+ * Reads up to "bytes" bytes from the backchannel/backend. The "timeout"
+ * parameter controls how many seconds to wait for the data - use 0.0 to
+ * return immediately if there is no data, -1.0 to wait for data indefinitely.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ssize_t /* O - Bytes read or -1 on error */
+cupsBackChannelRead(char *buffer, /* I - Buffer to read into */
+ size_t bytes, /* I - Bytes to read */
+ double timeout) /* I - Timeout in seconds, typically 0.0 to poll */
+{
+ fd_set input; /* Input set */
+ struct timeval tval; /* Timeout value */
+ int status; /* Select status */
+
+
+ /*
+ * Wait for input ready.
+ */
+
+ do
+ {
+ cups_setup(&input, &tval, timeout);
+
+ if (timeout < 0.0)
+ status = select(4, &input, NULL, NULL, NULL);
+ else
+ status = select(4, &input, NULL, NULL, &tval);
+ }
+ while (status < 0 && errno != EINTR && errno != EAGAIN);
+
+ if (status < 0)
+ return (-1); /* Timeout! */
+
+ /*
+ * Read bytes from the pipe...
+ */
+
+#ifdef WIN32
+ return ((ssize_t)_read(3, buffer, (unsigned)bytes));
+#else
+ return (read(3, buffer, bytes));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'cupsBackChannelWrite()' - Write data to the backchannel.
+ *
+ * Writes "bytes" bytes to the backchannel/filter. The "timeout" parameter
+ * controls how many seconds to wait for the data to be written - use
+ * 0.0 to return immediately if the data cannot be written, -1.0 to wait
+ * indefinitely.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ssize_t /* O - Bytes written or -1 on error */
+cupsBackChannelWrite(
+ const char *buffer, /* I - Buffer to write */
+ size_t bytes, /* I - Bytes to write */
+ double timeout) /* I - Timeout in seconds, typically 1.0 */
+{
+ fd_set output; /* Output set */
+ struct timeval tval; /* Timeout value */
+ int status; /* Select status */
+ ssize_t count; /* Current bytes */
+ size_t total; /* Total bytes */
+
+
+ /*
+ * Write all bytes...
+ */
+
+ total = 0;
+
+ while (total < bytes)
+ {
+ /*
+ * Wait for write-ready...
+ */
+
+ do
+ {
+ cups_setup(&output, &tval, timeout);
+
+ if (timeout < 0.0)
+ status = select(4, NULL, &output, NULL, NULL);
+ else
+ status = select(4, NULL, &output, NULL, &tval);
+ }
+ while (status < 0 && errno != EINTR && errno != EAGAIN);
+
+ if (status <= 0)
+ return (-1); /* Timeout! */
+
+ /*
+ * Write bytes to the pipe...
+ */
+
+#ifdef WIN32
+ count = (ssize_t)_write(3, buffer, (unsigned)(bytes - total));
+#else
+ count = write(3, buffer, bytes - total);
+#endif /* WIN32 */
+
+ if (count < 0)
+ {
+ /*
+ * Write error - abort on fatal errors...
+ */
+
+ if (errno != EINTR && errno != EAGAIN)
+ return (-1);
+ }
+ else
+ {
+ /*
+ * Write succeeded, update buffer pointer and total count...
+ */
+
+ buffer += count;
+ total += count;
+ }
+ }
+
+ return ((ssize_t)bytes);
+}
+
+
+/*
+ * 'cups_setup()' - Setup select()
+ */
+
+static void
+cups_setup(fd_set *set, /* I - Set for select() */
+ struct timeval *tval, /* I - Timer value */
+ double timeout) /* I - Timeout in seconds */
+{
+ tval->tv_sec = (int)timeout;
+ tval->tv_usec = (int)(1000000.0 * (timeout - tval->tv_sec));
+
+ FD_ZERO(set);
+ FD_SET(3, set);
+}
+
+
+/*
+ * End of "$Id: backchannel.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/backend.c b/cups/libs/cups/backend.c
new file mode 100644
index 000000000..0789471e9
--- /dev/null
+++ b/cups/libs/cups/backend.c
@@ -0,0 +1,154 @@
+/*
+ * "$Id: backend.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Backend functions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsBackendDeviceURI() - Get the device URI for a backend.
+ * cupsBackendReport() - Write a device line from a backend.
+ * quote_string() - Write a quoted string to stdout, escaping \ and ".
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "backend.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void quote_string(const char *s);
+
+
+/*
+ * 'cupsBackendDeviceURI()' - Get the device URI for a backend.
+ *
+ * The "argv" argument is the argv argument passed to main(). This
+ * function returns the device URI passed in the DEVICE_URI environment
+ * variable or the device URI passed in argv[0], whichever is found
+ * first.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+const char * /* O - Device URI or @code NULL@ */
+cupsBackendDeviceURI(char **argv) /* I - Command-line arguments */
+{
+ const char *device_uri, /* Device URI */
+ *auth_info_required; /* AUTH_INFO_REQUIRED env var */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global info */
+ int options; /* Resolve options */
+ ppd_file_t *ppd; /* PPD file */
+ ppd_attr_t *ppdattr; /* PPD attribute */
+
+
+ if ((device_uri = getenv("DEVICE_URI")) == NULL)
+ {
+ if (!argv || !argv[0] || !strchr(argv[0], ':'))
+ return (NULL);
+
+ device_uri = argv[0];
+ }
+
+ options = _HTTP_RESOLVE_STDERR;
+ if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) != NULL &&
+ !strcmp(auth_info_required, "negotiate"))
+ options |= _HTTP_RESOLVE_FQDN;
+
+ if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL)
+ {
+ if ((ppdattr = ppdFindAttr(ppd, "cupsIPPFaxOut", NULL)) != NULL &&
+ !_cups_strcasecmp(ppdattr->value, "true"))
+ options |= _HTTP_RESOLVE_FAXOUT;
+
+ ppdClose(ppd);
+ }
+
+ return (_httpResolveURI(device_uri, cg->resolved_uri,
+ sizeof(cg->resolved_uri), options, NULL, NULL));
+}
+
+
+/*
+ * 'cupsBackendReport()' - Write a device line from a backend.
+ *
+ * This function writes a single device line to stdout for a backend.
+ * It handles quoting of special characters in the device-make-and-model,
+ * device-info, device-id, and device-location strings.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+void
+cupsBackendReport(
+ const char *device_scheme, /* I - device-scheme string */
+ const char *device_uri, /* I - device-uri string */
+ const char *device_make_and_model, /* I - device-make-and-model string or @code NULL@ */
+ const char *device_info, /* I - device-info string or @code NULL@ */
+ const char *device_id, /* I - device-id string or @code NULL@ */
+ const char *device_location) /* I - device-location string or @code NULL@ */
+{
+ if (!device_scheme || !device_uri)
+ return;
+
+ printf("%s %s", device_scheme, device_uri);
+ if (device_make_and_model && *device_make_and_model)
+ quote_string(device_make_and_model);
+ else
+ quote_string("unknown");
+ quote_string(device_info);
+ quote_string(device_id);
+ quote_string(device_location);
+ putchar('\n');
+ fflush(stdout);
+}
+
+
+/*
+ * 'quote_string()' - Write a quoted string to stdout, escaping \ and ".
+ */
+
+static void
+quote_string(const char *s) /* I - String to write */
+{
+ fputs(" \"", stdout);
+
+ if (s)
+ {
+ while (*s)
+ {
+ if (*s == '\\' || *s == '\"')
+ putchar('\\');
+
+ if (((*s & 255) < ' ' && *s != '\t') || *s == 0x7f)
+ putchar(' ');
+ else
+ putchar(*s);
+
+ s ++;
+ }
+ }
+
+ putchar('\"');
+}
+
+
+/*
+ * End of "$Id: backend.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/backend.h b/cups/libs/cups/backend.h
new file mode 100644
index 000000000..127c02790
--- /dev/null
+++ b/cups/libs/cups/backend.h
@@ -0,0 +1,78 @@
+/*
+ * "$Id: backend.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Backend definitions for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_BACKEND_H_
+# define _CUPS_BACKEND_H_
+
+
+/*
+ * Include necessary headers...
+ */
+
+# include "versioning.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/*
+ * Constants...
+ */
+
+enum cups_backend_e /**** Backend exit codes ****/
+{
+ CUPS_BACKEND_OK = 0, /* Job completed successfully */
+ CUPS_BACKEND_FAILED = 1, /* Job failed, use error-policy */
+ CUPS_BACKEND_AUTH_REQUIRED = 2, /* Job failed, authentication required */
+ CUPS_BACKEND_HOLD = 3, /* Job failed, hold job */
+ CUPS_BACKEND_STOP = 4, /* Job failed, stop queue */
+ CUPS_BACKEND_CANCEL = 5, /* Job failed, cancel job */
+ CUPS_BACKEND_RETRY = 6, /* Job failed, retry this job later */
+ CUPS_BACKEND_RETRY_CURRENT = 7 /* Job failed, retry this job immediately */
+};
+typedef enum cups_backend_e cups_backend_t;
+ /**** Backend exit codes ****/
+
+
+/*
+ * Prototypes...
+ */
+
+extern const char *cupsBackendDeviceURI(char **argv) _CUPS_API_1_2;
+extern void cupsBackendReport(const char *device_scheme,
+ const char *device_uri,
+ const char *device_make_and_model,
+ const char *device_info,
+ const char *device_id,
+ const char *device_location)
+ _CUPS_API_1_4;
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_BACKEND_H_ */
+
+/*
+ * End of "$Id: backend.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/conflicts.c b/cups/libs/cups/conflicts.c
new file mode 100644
index 000000000..2ec8b7eb6
--- /dev/null
+++ b/cups/libs/cups/conflicts.c
@@ -0,0 +1,1214 @@
+/*
+ * "$Id: conflicts.c 3933 2012-10-01 03:01:10Z msweet $"
+ *
+ * Option marking routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsGetConflicts() - Get a list of conflicting options in a marked
+ * PPD.
+ * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
+ * ppdConflicts() - Check to see if there are any conflicts among
+ * the marked option choices.
+ * ppdInstallableConflict() - Test whether an option choice conflicts with an
+ * installable option.
+ * ppd_is_installable() - Determine whether an option is in the
+ * InstallableOptions group.
+ * ppd_load_constraints() - Load constraints from a PPD file.
+ * ppd_test_constraints() - See if any constraints are active.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "ppd-private.h"
+
+
+/*
+ * Local constants...
+ */
+
+enum
+{
+ _PPD_NORMAL_CONSTRAINTS,
+ _PPD_OPTION_CONSTRAINTS,
+ _PPD_INSTALLABLE_CONSTRAINTS,
+ _PPD_ALL_CONSTRAINTS
+};
+
+
+/*
+ * Local functions...
+ */
+
+static int ppd_is_installable(ppd_group_t *installable,
+ const char *option);
+static void ppd_load_constraints(ppd_file_t *ppd);
+static cups_array_t *ppd_test_constraints(ppd_file_t *ppd,
+ const char *option,
+ const char *choice,
+ int num_options,
+ cups_option_t *options,
+ int which);
+
+
+/*
+ * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD.
+ *
+ * This function gets a list of options that would conflict if "option" and
+ * "choice" were marked in the PPD. You would typically call this function
+ * after marking the currently selected options in the PPD in order to
+ * determine whether a new option selection would cause a conflict.
+ *
+ * The number of conflicting options are returned with "options" pointing to
+ * the conflicting options. The returned option array must be freed using
+ * @link cupsFreeOptions@.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+int /* O - Number of conflicting options */
+cupsGetConflicts(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Option to test */
+ const char *choice, /* I - Choice to test */
+ cups_option_t **options) /* O - Conflicting options */
+{
+ int i, /* Looping var */
+ num_options; /* Number of conflicting options */
+ cups_array_t *active; /* Active conflicts */
+ _ppd_cups_uiconsts_t *c; /* Current constraints */
+ _ppd_cups_uiconst_t *cptr; /* Current constraint */
+ ppd_choice_t *marked; /* Marked choice */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (options)
+ *options = NULL;
+
+ if (!ppd || !option || !choice || !options)
+ return (0);
+
+ /*
+ * Test for conflicts...
+ */
+
+ active = ppd_test_constraints(ppd, option, choice, 0, NULL,
+ _PPD_ALL_CONSTRAINTS);
+
+ /*
+ * Loop through all of the UI constraints and add any options that conflict...
+ */
+
+ for (num_options = 0, c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
+ c;
+ c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
+ {
+ for (i = c->num_constraints, cptr = c->constraints;
+ i > 0;
+ i --, cptr ++)
+ if (_cups_strcasecmp(cptr->option->keyword, option))
+ {
+ if (cptr->choice)
+ num_options = cupsAddOption(cptr->option->keyword,
+ cptr->choice->choice, num_options,
+ options);
+ else if ((marked = ppdFindMarkedChoice(ppd,
+ cptr->option->keyword)) != NULL)
+ num_options = cupsAddOption(cptr->option->keyword, marked->choice,
+ num_options, options);
+ }
+ }
+
+ cupsArrayDelete(active);
+
+ return (num_options);
+}
+
+
+/*
+ * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
+ *
+ * This function attempts to resolve any conflicts in a marked PPD, returning
+ * a list of option changes that are required to resolve them. On input,
+ * "num_options" and "options" contain any pending option changes that have
+ * not yet been marked, while "option" and "choice" contain the most recent
+ * selection which may or may not be in "num_options" or "options".
+ *
+ * On successful return, "num_options" and "options" are updated to contain
+ * "option" and "choice" along with any changes required to resolve conflicts
+ * specified in the PPD file and 1 is returned.
+ *
+ * If option conflicts cannot be resolved, "num_options" and "options" are not
+ * changed and 0 is returned.
+ *
+ * When resolving conflicts, @code cupsResolveConflicts@ does not consider
+ * changes to the current page size (@code media@, @code PageSize@, and
+ * @code PageRegion@) or to the most recent option specified in "option".
+ * Thus, if the only way to resolve a conflict is to change the page size
+ * or the option the user most recently changed, @code cupsResolveConflicts@
+ * will return 0 to indicate it was unable to resolve the conflicts.
+ *
+ * The @code cupsResolveConflicts@ function uses one of two sources of option
+ * constraint information. The preferred constraint information is defined by
+ * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
+ * case, the PPD file provides constraint resolution actions.
+ *
+ * The backup constraint information is defined by the
+ * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
+ * constraints are resolved algorithmically by first selecting the default
+ * choice for the conflicting option, then iterating over all possible choices
+ * until a non-conflicting option choice is found.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsResolveConflicts(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Newly selected option or @code NULL@ for none */
+ const char *choice, /* I - Newly selected choice or @code NULL@ for none */
+ int *num_options, /* IO - Number of additional selected options */
+ cups_option_t **options) /* IO - Additional selected options */
+{
+ int i, /* Looping var */
+ tries, /* Number of tries */
+ num_newopts; /* Number of new options */
+ cups_option_t *newopts; /* New options */
+ cups_array_t *active, /* Active constraints */
+ *pass, /* Resolvers for this pass */
+ *resolvers, /* Resolvers we have used */
+ *test; /* Test array for conflicts */
+ _ppd_cups_uiconsts_t *consts; /* Current constraints */
+ _ppd_cups_uiconst_t *constptr; /* Current constraint */
+ ppd_attr_t *resolver; /* Current resolver */
+ const char *resval; /* Pointer into resolver value */
+ char resoption[PPD_MAX_NAME],
+ /* Current resolver option */
+ reschoice[PPD_MAX_NAME],
+ /* Current resolver choice */
+ *resptr, /* Pointer into option/choice */
+ firstpage[255]; /* AP_FIRSTPAGE_Keyword string */
+ const char *value; /* Selected option value */
+ int changed; /* Did we change anything? */
+ ppd_choice_t *marked; /* Marked choice */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
+ return (0);
+
+ /*
+ * Build a shadow option array...
+ */
+
+ num_newopts = 0;
+ newopts = NULL;
+
+ for (i = 0; i < *num_options; i ++)
+ num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
+ num_newopts, &newopts);
+ if (option && _cups_strcasecmp(option, "Collate"))
+ num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
+
+ /*
+ * Loop until we have no conflicts...
+ */
+
+ cupsArraySave(ppd->sorted_attrs);
+
+ resolvers = NULL;
+ pass = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
+ tries = 0;
+
+ while (tries < 100 &&
+ (active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts,
+ _PPD_ALL_CONSTRAINTS)) != NULL)
+ {
+ tries ++;
+
+ if (!resolvers)
+ resolvers = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
+
+ for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
+ consts;
+ consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
+ {
+ if (consts->resolver[0])
+ {
+ /*
+ * Look up the resolver...
+ */
+
+ if (cupsArrayFind(pass, consts->resolver))
+ continue; /* Already applied this resolver... */
+
+ if (cupsArrayFind(resolvers, consts->resolver))
+ {
+ /*
+ * Resolver loop!
+ */
+
+ DEBUG_printf(("1cupsResolveConflicts: Resolver loop with %s!",
+ consts->resolver));
+ goto error;
+ }
+
+ if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
+ consts->resolver)) == NULL)
+ {
+ DEBUG_printf(("1cupsResolveConflicts: Resolver %s not found!",
+ consts->resolver));
+ goto error;
+ }
+
+ if (!resolver->value)
+ {
+ DEBUG_printf(("1cupsResolveConflicts: Resolver %s has no value!",
+ consts->resolver));
+ goto error;
+ }
+
+ /*
+ * Add the options from the resolver...
+ */
+
+ cupsArrayAdd(pass, consts->resolver);
+ cupsArrayAdd(resolvers, consts->resolver);
+
+ for (resval = resolver->value; *resval && !changed;)
+ {
+ while (_cups_isspace(*resval))
+ resval ++;
+
+ if (*resval != '*')
+ break;
+
+ for (resval ++, resptr = resoption;
+ *resval && !_cups_isspace(*resval);
+ resval ++)
+ if (resptr < (resoption + sizeof(resoption) - 1))
+ *resptr++ = *resval;
+
+ *resptr = '\0';
+
+ while (_cups_isspace(*resval))
+ resval ++;
+
+ for (resptr = reschoice;
+ *resval && !_cups_isspace(*resval);
+ resval ++)
+ if (resptr < (reschoice + sizeof(reschoice) - 1))
+ *resptr++ = *resval;
+
+ *resptr = '\0';
+
+ if (!resoption[0] || !reschoice[0])
+ break;
+
+ /*
+ * Is this the option we are changing?
+ */
+
+ snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", resoption);
+
+ if (option &&
+ (!_cups_strcasecmp(resoption, option) ||
+ !_cups_strcasecmp(firstpage, option) ||
+ (!_cups_strcasecmp(option, "PageSize") &&
+ !_cups_strcasecmp(resoption, "PageRegion")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") &&
+ !_cups_strcasecmp(resoption, "PageSize")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") &&
+ !_cups_strcasecmp(resoption, "PageRegion")) ||
+ (!_cups_strcasecmp(option, "PageRegion") &&
+ !_cups_strcasecmp(resoption, "PageSize")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") &&
+ !_cups_strcasecmp(resoption, "PageSize")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") &&
+ !_cups_strcasecmp(resoption, "PageRegion"))))
+ continue;
+
+ /*
+ * Try this choice...
+ */
+
+ if ((test = ppd_test_constraints(ppd, resoption, reschoice,
+ num_newopts, newopts,
+ _PPD_ALL_CONSTRAINTS)) == NULL)
+ {
+ /*
+ * That worked...
+ */
+
+ changed = 1;
+ }
+ else
+ cupsArrayDelete(test);
+
+ /*
+ * Add the option/choice from the resolver regardless of whether it
+ * worked; this makes sure that we can cascade several changes to
+ * make things resolve...
+ */
+
+ num_newopts = cupsAddOption(resoption, reschoice, num_newopts,
+ &newopts);
+ }
+ }
+ else
+ {
+ /*
+ * Try resolving by choosing the default values for non-installable
+ * options, then by iterating through the possible choices...
+ */
+
+ int j; /* Looping var */
+ ppd_choice_t *cptr; /* Current choice */
+ ppd_size_t *size; /* Current page size */
+
+
+ for (i = consts->num_constraints, constptr = consts->constraints;
+ i > 0 && !changed;
+ i --, constptr ++)
+ {
+ /*
+ * Can't resolve by changing an installable option...
+ */
+
+ if (constptr->installable)
+ continue;
+
+ /*
+ * Is this the option we are changing?
+ */
+
+ if (option &&
+ (!_cups_strcasecmp(constptr->option->keyword, option) ||
+ (!_cups_strcasecmp(option, "PageSize") &&
+ !_cups_strcasecmp(constptr->option->keyword, "PageRegion")) ||
+ (!_cups_strcasecmp(option, "PageRegion") &&
+ !_cups_strcasecmp(constptr->option->keyword, "PageSize"))))
+ continue;
+
+ /*
+ * Get the current option choice...
+ */
+
+ if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
+ newopts)) == NULL)
+ {
+ if (!_cups_strcasecmp(constptr->option->keyword, "PageSize") ||
+ !_cups_strcasecmp(constptr->option->keyword, "PageRegion"))
+ {
+ if ((value = cupsGetOption("PageSize", num_newopts,
+ newopts)) == NULL)
+ value = cupsGetOption("PageRegion", num_newopts, newopts);
+
+ if (!value)
+ {
+ if ((size = ppdPageSize(ppd, NULL)) != NULL)
+ value = size->name;
+ else
+ value = "";
+ }
+ }
+ else
+ {
+ marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
+ value = marked ? marked->choice : "";
+ }
+ }
+
+ if (!_cups_strncasecmp(value, "Custom.", 7))
+ value = "Custom";
+
+ /*
+ * Try the default choice...
+ */
+
+ test = NULL;
+
+ if (_cups_strcasecmp(value, constptr->option->defchoice) &&
+ (test = ppd_test_constraints(ppd, constptr->option->keyword,
+ constptr->option->defchoice,
+ num_newopts, newopts,
+ _PPD_OPTION_CONSTRAINTS)) == NULL)
+ {
+ /*
+ * That worked...
+ */
+
+ num_newopts = cupsAddOption(constptr->option->keyword,
+ constptr->option->defchoice,
+ num_newopts, &newopts);
+ changed = 1;
+ }
+ else
+ {
+ /*
+ * Try each choice instead...
+ */
+
+ for (j = constptr->option->num_choices,
+ cptr = constptr->option->choices;
+ j > 0;
+ j --, cptr ++)
+ {
+ cupsArrayDelete(test);
+ test = NULL;
+
+ if (_cups_strcasecmp(value, cptr->choice) &&
+ _cups_strcasecmp(constptr->option->defchoice, cptr->choice) &&
+ _cups_strcasecmp("Custom", cptr->choice) &&
+ (test = ppd_test_constraints(ppd, constptr->option->keyword,
+ cptr->choice, num_newopts,
+ newopts,
+ _PPD_OPTION_CONSTRAINTS)) == NULL)
+ {
+ /*
+ * This choice works...
+ */
+
+ num_newopts = cupsAddOption(constptr->option->keyword,
+ cptr->choice, num_newopts,
+ &newopts);
+ changed = 1;
+ break;
+ }
+ }
+
+ cupsArrayDelete(test);
+ }
+ }
+ }
+ }
+
+ if (!changed)
+ {
+ DEBUG_puts("1cupsResolveConflicts: Unable to automatically resolve "
+ "constraint!");
+ goto error;
+ }
+
+ cupsArrayClear(pass);
+ cupsArrayDelete(active);
+ active = NULL;
+ }
+
+ if (tries >= 100)
+ goto error;
+
+ /*
+ * Free the caller's option array...
+ */
+
+ cupsFreeOptions(*num_options, *options);
+
+ /*
+ * If Collate is the option we are testing, add it here. Otherwise, remove
+ * any Collate option from the resolve list since the filters automatically
+ * handle manual collation...
+ */
+
+ if (option && !_cups_strcasecmp(option, "Collate"))
+ num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
+ else
+ num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
+
+ /*
+ * Return the new list of options to the caller...
+ */
+
+ *num_options = num_newopts;
+ *options = newopts;
+
+ cupsArrayDelete(pass);
+ cupsArrayDelete(resolvers);
+
+ cupsArrayRestore(ppd->sorted_attrs);
+
+ DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts));
+#ifdef DEBUG
+ for (i = 0; i < num_newopts; i ++)
+ DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i,
+ newopts[i].name, newopts[i].value));
+#endif /* DEBUG */
+
+ return (1);
+
+ /*
+ * If we get here, we failed to resolve...
+ */
+
+ error:
+
+ cupsFreeOptions(num_newopts, newopts);
+
+ cupsArrayDelete(active);
+ cupsArrayDelete(pass);
+ cupsArrayDelete(resolvers);
+
+ cupsArrayRestore(ppd->sorted_attrs);
+
+ DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
+
+ return (0);
+}
+
+
+/*
+ * 'ppdConflicts()' - Check to see if there are any conflicts among the
+ * marked option choices.
+ *
+ * The returned value is the same as returned by @link ppdMarkOption@.
+ */
+
+int /* O - Number of conflicts found */
+ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
+{
+ int i, /* Looping variable */
+ conflicts; /* Number of conflicts */
+ cups_array_t *active; /* Active conflicts */
+ _ppd_cups_uiconsts_t *c; /* Current constraints */
+ _ppd_cups_uiconst_t *cptr; /* Current constraint */
+ ppd_option_t *o; /* Current option */
+
+
+ if (!ppd)
+ return (0);
+
+ /*
+ * Clear all conflicts...
+ */
+
+ cupsArraySave(ppd->options);
+
+ for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
+ o->conflicted = 0;
+
+ cupsArrayRestore(ppd->options);
+
+ /*
+ * Test for conflicts...
+ */
+
+ active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL,
+ _PPD_ALL_CONSTRAINTS);
+ conflicts = cupsArrayCount(active);
+
+ /*
+ * Loop through all of the UI constraints and flag any options
+ * that conflict...
+ */
+
+ for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
+ c;
+ c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
+ {
+ for (i = c->num_constraints, cptr = c->constraints;
+ i > 0;
+ i --, cptr ++)
+ cptr->option->conflicted = 1;
+ }
+
+ cupsArrayDelete(active);
+
+ /*
+ * Return the number of conflicts found...
+ */
+
+ return (conflicts);
+}
+
+
+/*
+ * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
+ * an installable option.
+ *
+ * This function tests whether a particular option choice is available based
+ * on constraints against options in the "InstallableOptions" group.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+int /* O - 1 if conflicting, 0 if not conflicting */
+ppdInstallableConflict(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Option */
+ const char *choice) /* I - Choice */
+{
+ cups_array_t *active; /* Active conflicts */
+
+
+ DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
+ ppd, option, choice));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !option || !choice)
+ return (0);
+
+ /*
+ * Test constraints using the new option...
+ */
+
+ active = ppd_test_constraints(ppd, option, choice, 0, NULL,
+ _PPD_INSTALLABLE_CONSTRAINTS);
+
+ cupsArrayDelete(active);
+
+ return (active != NULL);
+}
+
+
+/*
+ * 'ppd_is_installable()' - Determine whether an option is in the
+ * InstallableOptions group.
+ */
+
+static int /* O - 1 if installable, 0 if normal */
+ppd_is_installable(
+ ppd_group_t *installable, /* I - InstallableOptions group */
+ const char *name) /* I - Option name */
+{
+ if (installable)
+ {
+ int i; /* Looping var */
+ ppd_option_t *option; /* Current option */
+
+
+ for (i = installable->num_options, option = installable->options;
+ i > 0;
+ i --, option ++)
+ if (!_cups_strcasecmp(option->keyword, name))
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ppd_load_constraints()' - Load constraints from a PPD file.
+ */
+
+static void
+ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */
+{
+ int i; /* Looping var */
+ ppd_const_t *oldconst; /* Current UIConstraints data */
+ ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
+ _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */
+ _ppd_cups_uiconst_t *constptr; /* Current constraint */
+ ppd_group_t *installable; /* Installable options group */
+ const char *vptr; /* Pointer into constraint value */
+ char option[PPD_MAX_NAME], /* Option name/MainKeyword */
+ choice[PPD_MAX_NAME], /* Choice/OptionKeyword */
+ *ptr; /* Pointer into option or choice */
+
+
+ DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd));
+
+ /*
+ * Create an array to hold the constraint data...
+ */
+
+ ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
+
+ /*
+ * Find the installable options group if it exists...
+ */
+
+ for (i = ppd->num_groups, installable = ppd->groups;
+ i > 0;
+ i --, installable ++)
+ if (!_cups_strcasecmp(installable->name, "InstallableOptions"))
+ break;
+
+ if (i <= 0)
+ installable = NULL;
+
+ /*
+ * Load old-style [Non]UIConstraints data...
+ */
+
+ for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
+ {
+ /*
+ * Weed out nearby duplicates, since the PPD spec requires that you
+ * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
+ */
+
+ if (i > 1 &&
+ !_cups_strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
+ !_cups_strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
+ !_cups_strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
+ !_cups_strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
+ continue;
+
+ /*
+ * Allocate memory...
+ */
+
+ if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
+ {
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "UIConstraints!");
+ return;
+ }
+
+ if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
+ {
+ free(consts);
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "UIConstraints!");
+ return;
+ }
+
+ /*
+ * Fill in the information...
+ */
+
+ consts->num_constraints = 2;
+ consts->constraints = constptr;
+
+ if (!_cups_strncasecmp(oldconst->option1, "Custom", 6) &&
+ !_cups_strcasecmp(oldconst->choice1, "True"))
+ {
+ constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
+ constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
+ constptr[0].installable = 0;
+ }
+ else
+ {
+ constptr[0].option = ppdFindOption(ppd, oldconst->option1);
+ constptr[0].choice = ppdFindChoice(constptr[0].option,
+ oldconst->choice1);
+ constptr[0].installable = ppd_is_installable(installable,
+ oldconst->option1);
+ }
+
+ if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0]))
+ {
+ DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
+ oldconst->option1, oldconst->choice1));
+ free(consts->constraints);
+ free(consts);
+ continue;
+ }
+
+ if (!_cups_strncasecmp(oldconst->option2, "Custom", 6) &&
+ !_cups_strcasecmp(oldconst->choice2, "True"))
+ {
+ constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
+ constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
+ constptr[1].installable = 0;
+ }
+ else
+ {
+ constptr[1].option = ppdFindOption(ppd, oldconst->option2);
+ constptr[1].choice = ppdFindChoice(constptr[1].option,
+ oldconst->choice2);
+ constptr[1].installable = ppd_is_installable(installable,
+ oldconst->option2);
+ }
+
+ if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0]))
+ {
+ DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
+ oldconst->option2, oldconst->choice2));
+ free(consts->constraints);
+ free(consts);
+ continue;
+ }
+
+ consts->installable = constptr[0].installable || constptr[1].installable;
+
+ /*
+ * Add it to the constraints array...
+ */
+
+ cupsArrayAdd(ppd->cups_uiconstraints, consts);
+ }
+
+ /*
+ * Then load new-style constraints...
+ */
+
+ for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL);
+ constattr;
+ constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
+ {
+ if (!constattr->value)
+ {
+ DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
+ continue;
+ }
+
+ for (i = 0, vptr = strchr(constattr->value, '*');
+ vptr;
+ i ++, vptr = strchr(vptr + 1, '*'));
+
+ if (i == 0)
+ {
+ DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
+ continue;
+ }
+
+ if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
+ {
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "cupsUIConstraints!");
+ return;
+ }
+
+ if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
+ {
+ free(consts);
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "cupsUIConstraints!");
+ return;
+ }
+
+ consts->num_constraints = i;
+ consts->constraints = constptr;
+
+ strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
+
+ for (i = 0, vptr = strchr(constattr->value, '*');
+ vptr;
+ i ++, vptr = strchr(vptr, '*'), constptr ++)
+ {
+ /*
+ * Extract "*Option Choice" or just "*Option"...
+ */
+
+ for (vptr ++, ptr = option; *vptr && !_cups_isspace(*vptr); vptr ++)
+ if (ptr < (option + sizeof(option) - 1))
+ *ptr++ = *vptr;
+
+ *ptr = '\0';
+
+ while (_cups_isspace(*vptr))
+ vptr ++;
+
+ if (*vptr == '*')
+ choice[0] = '\0';
+ else
+ {
+ for (ptr = choice; *vptr && !_cups_isspace(*vptr); vptr ++)
+ if (ptr < (choice + sizeof(choice) - 1))
+ *ptr++ = *vptr;
+
+ *ptr = '\0';
+ }
+
+ if (!_cups_strncasecmp(option, "Custom", 6) && !_cups_strcasecmp(choice, "True"))
+ {
+ _cups_strcpy(option, option + 6);
+ strlcpy(choice, "Custom", sizeof(choice));
+ }
+
+ constptr->option = ppdFindOption(ppd, option);
+ constptr->choice = ppdFindChoice(constptr->option, choice);
+ constptr->installable = ppd_is_installable(installable, option);
+ consts->installable |= constptr->installable;
+
+ if (!constptr->option || (!constptr->choice && choice[0]))
+ {
+ DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
+ option, choice));
+ break;
+ }
+ }
+
+ if (!vptr)
+ cupsArrayAdd(ppd->cups_uiconstraints, consts);
+ else
+ {
+ free(consts->constraints);
+ free(consts);
+ }
+ }
+}
+
+
+/*
+ * 'ppd_test_constraints()' - See if any constraints are active.
+ */
+
+static cups_array_t * /* O - Array of active constraints */
+ppd_test_constraints(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Current option */
+ const char *choice, /* I - Current choice */
+ int num_options, /* I - Number of additional options */
+ cups_option_t *options, /* I - Additional options */
+ int which) /* I - Which constraints to test */
+{
+ int i; /* Looping var */
+ _ppd_cups_uiconsts_t *consts; /* Current constraints */
+ _ppd_cups_uiconst_t *constptr; /* Current constraint */
+ ppd_choice_t key, /* Search key */
+ *marked; /* Marked choice */
+ cups_array_t *active = NULL; /* Active constraints */
+ const char *value, /* Current value */
+ *firstvalue; /* AP_FIRSTPAGE_Keyword value */
+ char firstpage[255]; /* AP_FIRSTPAGE_Keyword string */
+
+
+ DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", "
+ "num_options=%d, options=%p, which=%d)", ppd, option, choice,
+ num_options, options, which));
+
+ if (!ppd->cups_uiconstraints)
+ ppd_load_constraints(ppd);
+
+ DEBUG_printf(("9ppd_test_constraints: %d constraints!",
+ cupsArrayCount(ppd->cups_uiconstraints)));
+
+ cupsArraySave(ppd->marked);
+
+ for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
+ consts;
+ consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
+ {
+ DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", "
+ "num_constraints=%d option1=\"%s\", choice1=\"%s\", "
+ "option2=\"%s\", choice2=\"%s\", ...",
+ consts->installable, consts->resolver, consts->num_constraints,
+ consts->constraints[0].option->keyword,
+ consts->constraints[0].choice ?
+ consts->constraints[0].choice->choice : "",
+ consts->constraints[1].option->keyword,
+ consts->constraints[1].choice ?
+ consts->constraints[1].choice->choice : ""));
+
+ if (consts->installable && which < _PPD_INSTALLABLE_CONSTRAINTS)
+ continue; /* Skip installable option constraint */
+
+ if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS)
+ continue; /* Skip non-installable option constraint */
+
+ if (which == _PPD_OPTION_CONSTRAINTS && option)
+ {
+ /*
+ * Skip constraints that do not involve the current option...
+ */
+
+ for (i = consts->num_constraints, constptr = consts->constraints;
+ i > 0;
+ i --, constptr ++)
+ {
+ if (!_cups_strcasecmp(constptr->option->keyword, option))
+ break;
+
+ if (!_cups_strncasecmp(option, "AP_FIRSTPAGE_", 13) &&
+ !_cups_strcasecmp(constptr->option->keyword, option + 13))
+ break;
+ }
+
+ if (!i)
+ continue;
+ }
+
+ DEBUG_puts("9ppd_test_constraints: Testing...");
+
+ for (i = consts->num_constraints, constptr = consts->constraints;
+ i > 0;
+ i --, constptr ++)
+ {
+ DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr->option->keyword,
+ constptr->choice ? constptr->choice->choice : ""));
+
+ if (constptr->choice &&
+ (!_cups_strcasecmp(constptr->option->keyword, "PageSize") ||
+ !_cups_strcasecmp(constptr->option->keyword, "PageRegion")))
+ {
+ /*
+ * PageSize and PageRegion are used depending on the selected input slot
+ * and manual feed mode. Validate against the selected page size instead
+ * of an individual option...
+ */
+
+ if (option && choice &&
+ (!_cups_strcasecmp(option, "PageSize") ||
+ !_cups_strcasecmp(option, "PageRegion")))
+ {
+ value = choice;
+ }
+ else if ((value = cupsGetOption("PageSize", num_options,
+ options)) == NULL)
+ if ((value = cupsGetOption("PageRegion", num_options,
+ options)) == NULL)
+ if ((value = cupsGetOption("media", num_options, options)) == NULL)
+ {
+ ppd_size_t *size = ppdPageSize(ppd, NULL);
+
+ if (size)
+ value = size->name;
+ }
+
+ if (value && !_cups_strncasecmp(value, "Custom.", 7))
+ value = "Custom";
+
+ if (option && choice &&
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") ||
+ !_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion")))
+ {
+ firstvalue = choice;
+ }
+ else if ((firstvalue = cupsGetOption("AP_FIRSTPAGE_PageSize",
+ num_options, options)) == NULL)
+ firstvalue = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
+ options);
+
+ if (firstvalue && !_cups_strncasecmp(firstvalue, "Custom.", 7))
+ firstvalue = "Custom";
+
+ if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) &&
+ (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice)))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
+ }
+ else if (constptr->choice)
+ {
+ /*
+ * Compare against the constrained choice...
+ */
+
+ if (option && choice && !_cups_strcasecmp(option, constptr->option->keyword))
+ {
+ if (!_cups_strncasecmp(choice, "Custom.", 7))
+ value = "Custom";
+ else
+ value = choice;
+ }
+ else if ((value = cupsGetOption(constptr->option->keyword, num_options,
+ options)) != NULL)
+ {
+ if (!_cups_strncasecmp(value, "Custom.", 7))
+ value = "Custom";
+ }
+ else if (constptr->choice->marked)
+ value = constptr->choice->choice;
+ else
+ value = NULL;
+
+ /*
+ * Now check AP_FIRSTPAGE_option...
+ */
+
+ snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s",
+ constptr->option->keyword);
+
+ if (option && choice && !_cups_strcasecmp(option, firstpage))
+ {
+ if (!_cups_strncasecmp(choice, "Custom.", 7))
+ firstvalue = "Custom";
+ else
+ firstvalue = choice;
+ }
+ else if ((firstvalue = cupsGetOption(firstpage, num_options,
+ options)) != NULL)
+ {
+ if (!_cups_strncasecmp(firstvalue, "Custom.", 7))
+ firstvalue = "Custom";
+ }
+ else
+ firstvalue = NULL;
+
+ DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value,
+ firstvalue));
+
+ if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) &&
+ (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice)))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
+ }
+ else if (option && choice &&
+ !_cups_strcasecmp(option, constptr->option->keyword))
+ {
+ if (!_cups_strcasecmp(choice, "None") || !_cups_strcasecmp(choice, "Off") ||
+ !_cups_strcasecmp(choice, "False"))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
+ }
+ else if ((value = cupsGetOption(constptr->option->keyword, num_options,
+ options)) != NULL)
+ {
+ if (!_cups_strcasecmp(value, "None") || !_cups_strcasecmp(value, "Off") ||
+ !_cups_strcasecmp(value, "False"))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
+ }
+ else
+ {
+ key.option = constptr->option;
+
+ if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
+ == NULL ||
+ (!_cups_strcasecmp(marked->choice, "None") ||
+ !_cups_strcasecmp(marked->choice, "Off") ||
+ !_cups_strcasecmp(marked->choice, "False")))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
+ }
+ }
+
+ if (i <= 0)
+ {
+ if (!active)
+ active = cupsArrayNew(NULL, NULL);
+
+ cupsArrayAdd(active, consts);
+ DEBUG_puts("9ppd_test_constraints: Added...");
+ }
+ }
+
+ cupsArrayRestore(ppd->marked);
+
+ DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!",
+ cupsArrayCount(active)));
+
+ return (active);
+}
+
+
+/*
+ * End of "$Id: conflicts.c 3933 2012-10-01 03:01:10Z msweet $".
+ */
diff --git a/cups/libs/cups/cups-private.h b/cups/libs/cups/cups-private.h
new file mode 100644
index 000000000..9328cdadf
--- /dev/null
+++ b/cups/libs/cups/cups-private.h
@@ -0,0 +1,285 @@
+/*
+ * "$Id: cups-private.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Private definitions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_CUPS_PRIVATE_H_
+# define _CUPS_CUPS_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "string-private.h"
+# include "debug-private.h"
+# include "array-private.h"
+# include "ipp-private.h"
+# include "http-private.h"
+# include "language-private.h"
+# include "pwg-private.h"
+# include "ppd-private.h"
+# include "thread-private.h"
+# include <cups/cups.h>
+# ifdef __APPLE__
+# include <sys/cdefs.h>
+# include <CoreFoundation/CoreFoundation.h>
+# endif /* __APPLE__ */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Types...
+ */
+
+typedef struct _cups_buffer_s /**** Read/write buffer ****/
+{
+ struct _cups_buffer_s *next; /* Next buffer in list */
+ size_t size; /* Size of buffer */
+ char used, /* Is this buffer used? */
+ d[1]; /* Data buffer */
+} _cups_buffer_t;
+
+typedef struct _cups_globals_s /**** CUPS global state data ****/
+{
+ /* Multiple places... */
+ const char *cups_datadir, /* CUPS_DATADIR environment var */
+ *cups_serverbin,/* CUPS_SERVERBIN environment var */
+ *cups_serverroot,
+ /* CUPS_SERVERROOT environment var */
+ *cups_statedir, /* CUPS_STATEDIR environment var */
+ *localedir; /* LOCALDIR environment var */
+
+ /* adminutil.c */
+ time_t cupsd_update; /* Last time we got or set cupsd.conf */
+ char cupsd_hostname[HTTP_MAX_HOST];
+ /* Hostname for connection */
+ int cupsd_num_settings;
+ /* Number of server settings */
+ cups_option_t *cupsd_settings;/* Server settings */
+
+ /* auth.c */
+# ifdef HAVE_GSSAPI
+ char gss_service_name[32];
+ /* Kerberos service name */
+# endif /* HAVE_GSSAPI */
+
+ /* backend.c */
+ char resolved_uri[1024];
+ /* Buffer for cupsBackendDeviceURI */
+
+ /* debug.c */
+# ifdef DEBUG
+ int thread_id; /* Friendly thread ID */
+# endif /* DEBUG */
+
+ /* file.c */
+ cups_file_t *stdio_files[3];/* stdin, stdout, stderr */
+
+ /* http.c */
+ char http_date[256]; /* Date+time buffer */
+
+ /* http-addr.c */
+ unsigned ip_addr; /* Packed IPv4 address */
+ char *ip_ptrs[2]; /* Pointer to packed address */
+ struct hostent hostent; /* Host entry for IP address */
+# ifdef HAVE_GETADDRINFO
+ char hostname[1024]; /* Hostname */
+# endif /* HAVE_GETADDRINFO */
+ int need_res_init; /* Need to reinitialize resolver? */
+
+ /* ipp.c */
+ ipp_uchar_t ipp_date[11]; /* RFC-1903 date/time data */
+ _cups_buffer_t *cups_buffers; /* Buffer list */
+
+ /* ipp-support.c */
+ int ipp_port; /* IPP port number */
+ char ipp_unknown[255];
+ /* Unknown error statuses */
+
+ /* language.c */
+ cups_lang_t *lang_default; /* Default language */
+# ifdef __APPLE__
+ char language[32]; /* Cached language */
+# endif /* __APPLE__ */
+
+ /* ppd.c */
+ ppd_status_t ppd_status; /* Status of last ppdOpen*() */
+ int ppd_line; /* Current line number */
+ ppd_conform_t ppd_conform; /* Level of conformance required */
+
+ /* pwg-media.c */
+ cups_array_t *leg_size_lut, /* Lookup table for legacy names */
+ *ppd_size_lut, /* Lookup table for PPD names */
+ *pwg_size_lut; /* Lookup table for PWG names */
+ pwg_media_t pwg_media; /* PWG media data for custom size */
+ char pwg_name[65]; /* PWG media name for custom size */
+
+ /* request.c */
+ http_t *http; /* Current server connection */
+ ipp_status_t last_error; /* Last IPP error */
+ char *last_status_message;
+ /* Last IPP status-message */
+
+ /* snmp.c */
+ char snmp_community[255];
+ /* Default SNMP community name */
+ int snmp_debug; /* Log SNMP IO to stderr? */
+
+ /* tempfile.c */
+ char tempfile[1024]; /* cupsTempFd/File buffer */
+
+ /* usersys.c */
+ http_encryption_t encryption; /* Encryption setting */
+ char user[65], /* User name */
+ user_agent[256],/* User-Agent string */
+ server[256], /* Server address */
+ servername[256],/* Server hostname */
+ password[128]; /* Password for default callback */
+ cups_password_cb2_t password_cb; /* Password callback */
+ void *password_data; /* Password user data */
+ http_tls_credentials_t tls_credentials;
+ /* Default client credentials */
+ cups_client_cert_cb_t client_cert_cb; /* Client certificate callback */
+ void *client_cert_data;
+ /* Client certificate user data */
+ cups_server_cert_cb_t server_cert_cb; /* Server certificate callback */
+ void *server_cert_data;
+ /* Server certificate user data */
+ int server_version, /* Server IPP version */
+ any_root, /* Allow any root */
+ expired_certs, /* Allow expired certs */
+ expired_root; /* Allow expired root */
+
+ /* util.c */
+ char def_printer[256];
+ /* Default printer */
+ char ppd_filename[HTTP_MAX_URI];
+ /* PPD filename */
+} _cups_globals_t;
+
+typedef struct _cups_media_db_s /* Media database */
+{
+ char *color, /* Media color, if any */
+ *key, /* Media key, if any */
+ *info, /* Media human-readable name, if any */
+ *size_name, /* Media PWG size name, if provided */
+ *source, /* Media source, if any */
+ *type; /* Media type, if any */
+ int width, /* Width in hundredths of millimeters */
+ length, /* Length in hundredths of
+ * millimeters */
+ bottom, /* Bottom margin in hundredths of
+ * millimeters */
+ left, /* Left margin in hundredths of
+ * millimeters */
+ right, /* Right margin in hundredths of
+ * millimeters */
+ top; /* Top margin in hundredths of
+ * millimeters */
+} _cups_media_db_t;
+
+typedef struct _cups_dconstres_s /* Constraint/resolver */
+{
+ char *name; /* Name of resolver */
+ ipp_t *collection; /* Collection containing attrs */
+} _cups_dconstres_t;
+
+struct _cups_dinfo_s /* Destination capability and status
+ * information */
+{
+ int version; /* IPP version */
+ const char *uri; /* Printer URI */
+ char *resource; /* Resource path */
+ ipp_t *attrs; /* Printer attributes */
+ int num_defaults; /* Number of default options */
+ cups_option_t *defaults; /* Default options */
+ cups_array_t *constraints; /* Job constraints */
+ cups_array_t *resolvers; /* Job resolvers */
+ cups_array_t *localizations; /* Localization information */
+ cups_array_t *media_db; /* Media database */
+ _cups_media_db_t min_size, /* Minimum size */
+ max_size; /* Maximum size */
+ unsigned cached_flags; /* Flags used for cached media */
+ cups_array_t *cached_db; /* Cache of media from last index/default */
+ time_t ready_time; /* When xxx-ready attributes were last queried */
+ ipp_t *ready_attrs; /* xxx-ready attributes */
+ cups_array_t *ready_db; /* media[-col]-ready media database */
+};
+
+
+/*
+ * Prototypes...
+ */
+
+# ifdef __APPLE__
+extern CFStringRef _cupsAppleCopyDefaultPaperID(void);
+extern CFStringRef _cupsAppleCopyDefaultPrinter(void);
+extern int _cupsAppleGetUseLastPrinter(void);
+extern void _cupsAppleSetDefaultPaperID(CFStringRef name);
+extern void _cupsAppleSetDefaultPrinter(CFStringRef name);
+extern void _cupsAppleSetUseLastPrinter(int uselast);
+# endif /* __APPLE__ */
+
+extern char *_cupsBufferGet(size_t size);
+extern void _cupsBufferRelease(char *b);
+
+extern http_t *_cupsConnect(void);
+extern int _cupsGet1284Values(const char *device_id,
+ cups_option_t **values);
+extern const char *_cupsGetDestResource(cups_dest_t *dest, char *resource,
+ size_t resourcesize);
+extern int _cupsGetDests(http_t *http, ipp_op_t op,
+ const char *name, cups_dest_t **dests,
+ cups_ptype_t type, cups_ptype_t mask);
+extern const char *_cupsGetPassword(const char *prompt);
+extern void _cupsGlobalLock(void);
+extern _cups_globals_t *_cupsGlobals(void);
+extern void _cupsGlobalUnlock(void);
+# ifdef HAVE_GSSAPI
+extern const char *_cupsGSSServiceName(void);
+# endif /* HAVE_GSSAPI */
+extern int _cupsNextDelay(int current, int *previous);
+extern void _cupsSetDefaults(void);
+extern void _cupsSetError(ipp_status_t status, const char *message,
+ int localize);
+extern void _cupsSetHTTPError(http_status_t status);
+# ifdef HAVE_GSSAPI
+extern int _cupsSetNegotiateAuthString(http_t *http,
+ const char *method,
+ const char *resource);
+# endif /* HAVE_GSSAPI */
+extern char *_cupsUserDefault(char *name, size_t namesize);
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_CUPS_PRIVATE_H_ */
+
+/*
+ * End of "$Id: cups-private.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/cups.h b/cups/libs/cups/cups.h
new file mode 100644
index 000000000..8295277ab
--- /dev/null
+++ b/cups/libs/cups/cups.h
@@ -0,0 +1,633 @@
+/*
+ * "$Id: cups.h 11812 2014-04-10 15:47:53Z msweet $"
+ *
+ * API definitions for CUPS.
+ *
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_CUPS_H_
+# define _CUPS_CUPS_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <sys/types.h>
+# if defined(WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
+# define __CUPS_SSIZE_T_DEFINED
+# include <stddef.h>
+/* Windows does not support the ssize_t type, so map it to off_t... */
+typedef off_t ssize_t; /* @private@ */
+# endif /* WIN32 && !__CUPS_SSIZE_T_DEFINED */
+
+# ifdef __BLOCKS__
+# ifndef __APPLE__
+# include <dispatch/dispatch.h>
+# endif
+# endif /* __BLOCKS__ */
+
+# include "file.h"
+# include "ipp.h"
+# include "language.h"
+# include "pwg.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define CUPS_VERSION 1.0703
+# define CUPS_VERSION_MAJOR 1
+# define CUPS_VERSION_MINOR 7
+# define CUPS_VERSION_PATCH 3
+
+# define CUPS_BC_FD 3
+ /* Back-channel file descriptor for
+ * select/poll */
+# define CUPS_DATE_ANY (time_t)-1
+# define CUPS_EXCLUDE_NONE (const char *)0
+# define CUPS_FORMAT_AUTO "application/octet-stream"
+# define CUPS_FORMAT_COMMAND "application/vnd.cups-command"
+# define CUPS_FORMAT_JPEG "image/jpeg"
+# define CUPS_FORMAT_PDF "application/pdf"
+# define CUPS_FORMAT_POSTSCRIPT "application/postscript"
+# define CUPS_FORMAT_RAW "application/vnd.cups-raw"
+# define CUPS_FORMAT_TEXT "text/plain"
+# define CUPS_HTTP_DEFAULT (http_t *)0
+# define CUPS_INCLUDE_ALL (const char *)0
+# define CUPS_JOBID_ALL -1
+# define CUPS_JOBID_CURRENT 0
+# define CUPS_LENGTH_VARIABLE (ssize_t)0
+# define CUPS_TIMEOUT_DEFAULT 0
+# define CUPS_WHICHJOBS_ALL -1
+# define CUPS_WHICHJOBS_ACTIVE 0
+# define CUPS_WHICHJOBS_COMPLETED 1
+
+/* Flags for cupsConnectDest and cupsEnumDests */
+# define CUPS_DEST_FLAGS_NONE 0x00
+ /* No flags are set */
+# define CUPS_DEST_FLAGS_UNCONNECTED 0x01
+ /* There is not connection */
+# define CUPS_DEST_FLAGS_MORE 0x02
+ /* There are more destinations */
+# define CUPS_DEST_FLAGS_REMOVED 0x04
+ /* The destination has gone away */
+# define CUPS_DEST_FLAGS_ERROR 0x08
+ /* An error occurred */
+# define CUPS_DEST_FLAGS_RESOLVING 0x10
+ /* The destination address is being
+ * resolved */
+# define CUPS_DEST_FLAGS_CONNECTING 0x20
+ /* A connection is being established */
+# define CUPS_DEST_FLAGS_CANCELED 0x40
+ /* Operation was canceled */
+
+/* Flags for cupsGetDestMediaByName/Size */
+# define CUPS_MEDIA_FLAGS_DEFAULT 0x00
+ /* Find the closest size supported by
+ * the printer */
+# define CUPS_MEDIA_FLAGS_BORDERLESS 0x01
+ /* Find a borderless size */
+# define CUPS_MEDIA_FLAGS_DUPLEX 0x02
+ /* Find a size compatible with 2-sided
+ * printing */
+# define CUPS_MEDIA_FLAGS_EXACT 0x04
+ /* Find an exact match for the size */
+# define CUPS_MEDIA_FLAGS_READY 0x08
+ /* If the printer supports media
+ * sensing, find the size amongst the
+ * "ready" media. */
+
+/* Options and values */
+# define CUPS_COPIES "copies"
+# define CUPS_COPIES_SUPPORTED "copies-supported"
+
+# define CUPS_FINISHINGS "finishings"
+# define CUPS_FINISHINGS_SUPPORTED "finishings-supported"
+
+# define CUPS_FINISHINGS_BIND "7"
+# define CUPS_FINISHINGS_COVER "6"
+# define CUPS_FINISHINGS_FOLD "10"
+# define CUPS_FINISHINGS_NONE "3"
+# define CUPS_FINISHINGS_PUNCH "5"
+# define CUPS_FINISHINGS_STAPLE "4"
+# define CUPS_FINISHINGS_TRIM "11"
+
+# define CUPS_MEDIA "media"
+# define CUPS_MEDIA_READY "media-ready"
+# define CUPS_MEDIA_SUPPORTED "media-supported"
+
+# define CUPS_MEDIA_3X5 "na_index-3x5_3x5in"
+# define CUPS_MEDIA_4X6 "na_index-4x6_4x6in"
+# define CUPS_MEDIA_5X7 "na_5x7_5x7in"
+# define CUPS_MEDIA_8X10 "na_govt-letter_8x10in"
+# define CUPS_MEDIA_A3 "iso_a3_297x420mm"
+# define CUPS_MEDIA_A4 "iso_a4_210x297mm"
+# define CUPS_MEDIA_A5 "iso_a5_148x210mm"
+# define CUPS_MEDIA_A6 "iso_a6_105x148mm"
+# define CUPS_MEDIA_ENV10 "na_number-10_4.125x9.5in"
+# define CUPS_MEDIA_ENVDL "iso_dl_110x220mm"
+# define CUPS_MEDIA_LEGAL "na_legal_8.5x14in"
+# define CUPS_MEDIA_LETTER "na_letter_8.5x11in"
+# define CUPS_MEDIA_PHOTO_L "oe_photo-l_3.5x5in"
+# define CUPS_MEDIA_SUPERBA3 "na_super-b_13x19in"
+# define CUPS_MEDIA_TABLOID "na_ledger_11x17in"
+
+# define CUPS_MEDIA_SOURCE "media-source"
+# define CUPS_MEDIA_SOURCE_SUPPORTED "media-source-supported"
+
+# define CUPS_MEDIA_SOURCE_AUTO "auto"
+# define CUPS_MEDIA_SOURCE_MANUAL "manual"
+
+# define CUPS_MEDIA_TYPE "media-type"
+# define CUPS_MEDIA_TYPE_SUPPORTED "media-type-supported"
+
+# define CUPS_MEDIA_TYPE_AUTO "auto"
+# define CUPS_MEDIA_TYPE_ENVELOPE "envelope"
+# define CUPS_MEDIA_TYPE_LABELS "labels"
+# define CUPS_MEDIA_TYPE_LETTERHEAD "stationery-letterhead"
+# define CUPS_MEDIA_TYPE_PHOTO "photographic"
+# define CUPS_MEDIA_TYPE_PHOTO_GLOSSY "photographic-glossy"
+# define CUPS_MEDIA_TYPE_PHOTO_MATTE "photographic-matte"
+# define CUPS_MEDIA_TYPE_PLAIN "stationery"
+# define CUPS_MEDIA_TYPE_TRANSPARENCY "transparency"
+
+# define CUPS_NUMBER_UP "number-up"
+# define CUPS_NUMBER_UP_SUPPORTED "number-up-supported"
+
+# define CUPS_ORIENTATION "orientation-requested"
+# define CUPS_ORIENTATION_SUPPORTED "orientation-requested-supported"
+
+# define CUPS_ORIENTATION_PORTRAIT "3"
+# define CUPS_ORIENTATION_LANDSCAPE "4"
+
+# define CUPS_PRINT_COLOR_MODE "print-color-mode"
+# define CUPS_PRINT_COLOR_MODE_SUPPORTED "print-color-mode-supported"
+
+# define CUPS_PRINT_COLOR_MODE_AUTO "auto"
+# define CUPS_PRINT_COLOR_MODE_MONOCHROME "monochrome"
+# define CUPS_PRINT_COLOR_MODE_COLOR "color"
+
+# define CUPS_PRINT_QUALITY "print-quality"
+# define CUPS_PRINT_QUALITY_SUPPORTED "print-quality-supported"
+
+# define CUPS_PRINT_QUALITY_DRAFT "3"
+# define CUPS_PRINT_QUALITY_NORMAL "4"
+# define CUPS_PRINT_QUALITY_HIGH "5"
+
+# define CUPS_SIDES "sides"
+# define CUPS_SIDES_SUPPORTED "sides-supported"
+
+# define CUPS_SIDES_ONE_SIDED "one-sided"
+# define CUPS_SIDES_TWO_SIDED_PORTRAIT "two-sided-long-edge"
+# define CUPS_SIDES_TWO_SIDED_LANDSCAPE "two-sided-short-edge"
+
+
+/*
+ * Types and structures...
+ */
+
+typedef unsigned cups_ptype_t; /* Printer type/capability bits */
+enum cups_ptype_e /* Printer type/capability bit
+ * constants */
+{ /* Not a typedef'd enum so we can OR */
+ CUPS_PRINTER_LOCAL = 0x0000, /* Local printer or class */
+ CUPS_PRINTER_CLASS = 0x0001, /* Printer class */
+ 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_STAPLE = 0x0020, /* Can staple output */
+ CUPS_PRINTER_COPIES = 0x0040, /* Can do copies */
+ CUPS_PRINTER_COLLATE = 0x0080, /* Can collage 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_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/OS X 10.5@ */
+ CUPS_PRINTER_NOT_SHARED = 0x200000, /* Printer is not shared
+ * @since CUPS 1.2/OS X 10.5@ */
+ CUPS_PRINTER_AUTHENTICATED = 0x400000,/* Printer requires authentication
+ * @since CUPS 1.2/OS X 10.5@ */
+ CUPS_PRINTER_COMMANDS = 0x800000, /* Printer supports maintenance commands
+ * @since CUPS 1.2/OS X 10.5@ */
+ CUPS_PRINTER_DISCOVERED = 0x1000000, /* Printer was automatically discovered
+ * and added @private@
+ * @since Deprecated@ */
+ CUPS_PRINTER_SCANNER = 0x2000000, /* Scanner-only device
+ * @since CUPS 1.4/OS X 10.6@ */
+ CUPS_PRINTER_MFP = 0x4000000, /* Printer with scanning capabilities
+ * @since CUPS 1.4/OS X 10.6@ */
+ CUPS_PRINTER_OPTIONS = 0x6fffc /* ~(CLASS | REMOTE | IMPLICIT |
+ * DEFAULT | FAX | REJECTING | DELETE |
+ * NOT_SHARED | AUTHENTICATED |
+ * COMMANDS | DISCOVERED) @private@ */
+};
+
+typedef struct cups_option_s /**** Printer Options ****/
+{
+ char *name; /* Name of option */
+ char *value; /* Value of option */
+} cups_option_t;
+
+typedef struct cups_dest_s /**** Destination ****/
+{
+ char *name, /* Printer or class name */
+ *instance; /* Local instance name or NULL */
+ int is_default; /* Is this printer the default? */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+} cups_dest_t;
+
+typedef struct _cups_dinfo_s cups_dinfo_t;
+ /* Destination capability and status
+ * information @since CUPS 1.6/OS X 10.8@ */
+
+typedef struct cups_job_s /**** Job ****/
+{
+ int id; /* The job ID */
+ char *dest; /* Printer or class name */
+ char *title; /* Title/job name */
+ char *user; /* User the submitted the job */
+ char *format; /* Document format */
+ ipp_jstate_t state; /* Job state */
+ int size; /* Size in kilobytes */
+ int priority; /* Priority (1-100) */
+ time_t completed_time; /* Time the job was completed */
+ time_t creation_time; /* Time the job was created */
+ time_t processing_time; /* Time the job was processed */
+} cups_job_t;
+
+typedef struct cups_size_s /**** Media Size @since CUPS 1.6/OS X 10.8@ ****/
+{
+ char media[128]; /* Media name to use */
+ int width, /* Width in hundredths of millimeters */
+ length, /* Length in hundredths of
+ * millimeters */
+ bottom, /* Bottom margin in hundredths of
+ * millimeters */
+ left, /* Left margin in hundredths of
+ * millimeters */
+ right, /* Right margin in hundredths of
+ * millimeters */
+ top; /* Top margin in hundredths of
+ * millimeters */
+} cups_size_t;
+
+typedef int (*cups_client_cert_cb_t)(http_t *http, void *tls,
+ cups_array_t *distinguished_names,
+ void *user_data);
+ /* Client credentials callback
+ * @since CUPS 1.5/OS X 10.7@ */
+
+typedef int (*cups_dest_cb_t)(void *user_data, unsigned flags,
+ cups_dest_t *dest);
+ /* Destination enumeration callback
+ * @since CUPS 1.6/OS X 10.8@ */
+
+# ifdef __BLOCKS__
+typedef int (^cups_dest_block_t)(unsigned flags, cups_dest_t *dest);
+ /* Destination enumeration block
+ * @since CUPS 1.6/OS X 10.8@ */
+# endif /* __BLOCKS__ */
+
+typedef void (*cups_device_cb_t)(const char *device_class,
+ const char *device_id, const char *device_info,
+ const char *device_make_and_model,
+ const char *device_uri,
+ const char *device_location, void *user_data);
+ /* Device callback
+ * @since CUPS 1.4/OS X 10.6@ */
+
+typedef const char *(*cups_password_cb_t)(const char *prompt);
+ /* Password callback */
+
+typedef const char *(*cups_password_cb2_t)(const char *prompt, http_t *http,
+ const char *method,
+ const char *resource,
+ void *user_data);
+ /* New password callback
+ * @since CUPS 1.4/OS X 10.6@ */
+
+typedef int (*cups_server_cert_cb_t)(http_t *http, void *tls,
+ cups_array_t *certs, void *user_data);
+ /* Server credentials callback
+ * @since CUPS 1.5/OS X 10.7@ */
+
+
+/*
+ * Functions...
+ */
+
+extern int cupsCancelJob(const char *name, int job_id);
+extern ipp_t *cupsDoFileRequest(http_t *http, ipp_t *request,
+ const char *resource,
+ const char *filename);
+extern ipp_t *cupsDoRequest(http_t *http, ipp_t *request,
+ 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 const char *cupsGetDefault(void);
+extern int cupsGetJobs(cups_job_t **jobs, const char *name,
+ int myjobs, int whichjobs);
+extern const char *cupsGetPPD(const char *name)
+ _CUPS_DEPRECATED_1_6_MSG("Use cupsCopyDestInfo instead.");
+extern int cupsGetPrinters(char ***printers) _CUPS_DEPRECATED_MSG("Use cupsGetDests instead.");
+extern ipp_status_t cupsLastError(void);
+extern int cupsPrintFile(const char *name, const char *filename,
+ const char *title, int num_options,
+ cups_option_t *options);
+extern int cupsPrintFiles(const char *name, int num_files,
+ const char **files, const char *title,
+ int num_options, cups_option_t *options);
+extern char *cupsTempFile(char *filename, int len) _CUPS_DEPRECATED_MSG("Use cupsTempFd or cupsTempFile2 instead.");
+extern int cupsTempFd(char *filename, int len);
+
+extern int cupsAddDest(const char *name, const char *instance,
+ int num_dests, cups_dest_t **dests);
+extern void cupsFreeDests(int num_dests, cups_dest_t *dests);
+extern cups_dest_t *cupsGetDest(const char *name, const char *instance,
+ int num_dests, cups_dest_t *dests);
+extern int cupsGetDests(cups_dest_t **dests);
+extern void cupsSetDests(int num_dests, cups_dest_t *dests);
+
+extern int cupsAddOption(const char *name, const char *value,
+ int num_options, cups_option_t **options);
+extern void cupsEncodeOptions(ipp_t *ipp, int num_options,
+ cups_option_t *options);
+extern void cupsFreeOptions(int num_options,
+ cups_option_t *options);
+extern const char *cupsGetOption(const char *name, int num_options,
+ cups_option_t *options);
+extern int cupsParseOptions(const char *arg, int num_options,
+ cups_option_t **options);
+
+extern const char *cupsGetPassword(const char *prompt);
+extern const char *cupsServer(void);
+extern void cupsSetEncryption(http_encryption_t e);
+extern void cupsSetPasswordCB(cups_password_cb_t cb);
+extern void cupsSetServer(const char *server);
+extern void cupsSetUser(const char *user);
+extern const char *cupsUser(void);
+
+/**** New in CUPS 1.1.20 ****/
+extern int cupsDoAuthentication(http_t *http, const char *method,
+ const char *resource)
+ _CUPS_API_1_1_20;
+extern http_status_t cupsGetFile(http_t *http, const char *resource,
+ const char *filename) _CUPS_API_1_1_20;
+extern http_status_t cupsGetFd(http_t *http, const char *resource, int fd);
+extern http_status_t cupsPutFile(http_t *http, const char *resource,
+ const char *filename) _CUPS_API_1_1_20;
+extern http_status_t cupsPutFd(http_t *http, const char *resource, int fd)
+ _CUPS_API_1_1_20;
+
+/**** New in CUPS 1.1.21 ****/
+extern const char *cupsGetDefault2(http_t *http) _CUPS_API_1_1_21;
+extern int cupsGetDests2(http_t *http, cups_dest_t **dests)
+ _CUPS_API_1_1_21;
+extern int cupsGetJobs2(http_t *http, cups_job_t **jobs,
+ const char *name, int myjobs,
+ int whichjobs) _CUPS_API_1_1_21;
+extern const char *cupsGetPPD2(http_t *http, const char *name)
+ _CUPS_DEPRECATED_1_6_MSG("Use cupsCopyDestInfo instead.");
+extern int cupsPrintFile2(http_t *http, const char *name,
+ const char *filename,
+ const char *title, int num_options,
+ cups_option_t *options) _CUPS_API_1_1_21;
+extern int cupsPrintFiles2(http_t *http, const char *name,
+ int num_files, const char **files,
+ const char *title, int num_options,
+ cups_option_t *options)
+ _CUPS_API_1_1_21;
+extern int cupsSetDests2(http_t *http, int num_dests,
+ cups_dest_t *dests) _CUPS_API_1_1_21;
+
+/**** New in CUPS 1.2/OS X 10.5 ****/
+extern ssize_t cupsBackChannelRead(char *buffer, size_t bytes,
+ double timeout) _CUPS_API_1_2;
+extern ssize_t cupsBackChannelWrite(const char *buffer, size_t bytes,
+ double timeout) _CUPS_API_1_2;
+extern void cupsEncodeOptions2(ipp_t *ipp, int num_options,
+ cups_option_t *options,
+ ipp_tag_t group_tag) _CUPS_API_1_2;
+extern const char *cupsLastErrorString(void) _CUPS_API_1_2;
+extern char *cupsNotifySubject(cups_lang_t *lang, ipp_t *event)
+ _CUPS_API_1_2;
+extern char *cupsNotifyText(cups_lang_t *lang, ipp_t *event)
+ _CUPS_API_1_2;
+extern int cupsRemoveOption(const char *name, int num_options,
+ cups_option_t **options) _CUPS_API_1_2;
+extern cups_file_t *cupsTempFile2(char *filename, int len) _CUPS_API_1_2;
+
+/**** New in CUPS 1.3/OS X 10.5 ****/
+extern ipp_t *cupsDoIORequest(http_t *http, ipp_t *request,
+ const char *resource, int infile,
+ int outfile) _CUPS_API_1_3;
+extern char *cupsGetServerPPD(http_t *http, const char *name)
+ _CUPS_API_1_3;
+extern int cupsRemoveDest(const char *name,
+ const char *instance,
+ int num_dests, cups_dest_t **dests)
+ _CUPS_API_1_3;
+extern void cupsSetDefaultDest(const char *name,
+ const char *instance,
+ int num_dests,
+ cups_dest_t *dests) _CUPS_API_1_3;
+
+/**** New in CUPS 1.4/OS X 10.6 ****/
+extern ipp_status_t cupsCancelJob2(http_t *http, const char *name,
+ int job_id, int purge) _CUPS_API_1_4;
+extern int cupsCreateJob(http_t *http, const char *name,
+ const char *title, int num_options,
+ cups_option_t *options) _CUPS_API_1_4;
+extern ipp_status_t cupsFinishDocument(http_t *http,
+ const char *name) _CUPS_API_1_4;
+extern ipp_status_t cupsGetDevices(http_t *http, int timeout,
+ const char *include_schemes,
+ const char *exclude_schemes,
+ cups_device_cb_t callback,
+ void *user_data) _CUPS_API_1_4;
+extern cups_dest_t *cupsGetNamedDest(http_t *http, const char *name,
+ const char *instance) _CUPS_API_1_4;
+extern const char *cupsGetPassword2(const char *prompt, http_t *http,
+ const char *method,
+ const char *resource) _CUPS_API_1_4;
+extern http_status_t cupsGetPPD3(http_t *http, const char *name,
+ time_t *modtime, char *buffer,
+ size_t bufsize) _CUPS_API_1_4;
+extern ipp_t *cupsGetResponse(http_t *http,
+ const char *resource) _CUPS_API_1_4;
+extern ssize_t cupsReadResponseData(http_t *http, char *buffer,
+ size_t length) _CUPS_API_1_4;
+extern http_status_t cupsSendRequest(http_t *http, ipp_t *request,
+ const char *resource,
+ size_t length) _CUPS_API_1_4;
+extern void cupsSetPasswordCB2(cups_password_cb2_t cb,
+ void *user_data) _CUPS_API_1_4;
+extern http_status_t cupsStartDocument(http_t *http, const char *name,
+ int job_id, const char *docname,
+ const char *format,
+ int last_document) _CUPS_API_1_4;
+extern http_status_t cupsWriteRequestData(http_t *http, const char *buffer,
+ size_t length) _CUPS_API_1_4;
+
+/**** New in CUPS 1.5/OS X 10.7 ****/
+extern void cupsSetClientCertCB(cups_client_cert_cb_t cb,
+ void *user_data) _CUPS_API_1_5;
+extern int cupsSetCredentials(cups_array_t *certs) _CUPS_API_1_5;
+extern void cupsSetServerCertCB(cups_server_cert_cb_t cb,
+ void *user_data) _CUPS_API_1_5;
+
+/**** New in CUPS 1.6/OS X 10.8 ****/
+extern ipp_status_t cupsCancelDestJob(http_t *http, cups_dest_t *dest,
+ int job_id) _CUPS_API_1_6;
+extern int cupsCheckDestSupported(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *info,
+ const char *option,
+ const char *value) _CUPS_API_1_6;
+extern ipp_status_t cupsCloseDestJob(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *info, int job_id)
+ _CUPS_API_1_6;
+extern 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)
+ _CUPS_API_1_6;
+# ifdef __BLOCKS__
+extern http_t *cupsConnectDestBlock(cups_dest_t *dest,
+ unsigned flags, int msec,
+ int *cancel, char *resource,
+ size_t resourcesize,
+ cups_dest_block_t block)
+ _CUPS_API_1_6;
+# endif /* __BLOCKS__ */
+extern int cupsCopyDest(cups_dest_t *dest, int num_dests,
+ cups_dest_t **dests) _CUPS_API_1_6;
+extern cups_dinfo_t *cupsCopyDestInfo(http_t *http, cups_dest_t *dest)
+ _CUPS_API_1_6;
+extern int cupsCopyDestConflicts(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *info,
+ int num_options,
+ cups_option_t *options,
+ const char *new_option,
+ const char *new_value,
+ int *num_conflicts,
+ cups_option_t **conflicts,
+ int *num_resolved,
+ cups_option_t **resolved)
+ _CUPS_API_1_6;
+extern 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) _CUPS_API_1_6;
+extern int cupsEnumDests(unsigned flags, int msec, int *cancel,
+ cups_ptype_t type, cups_ptype_t mask,
+ cups_dest_cb_t cb, void *user_data)
+ _CUPS_API_1_6;
+# ifdef __BLOCKS__
+extern int cupsEnumDestsBlock(unsigned flags, int msec,
+ int *cancel, cups_ptype_t type,
+ cups_ptype_t mask,
+ cups_dest_block_t block)
+ _CUPS_API_1_6;
+# endif /* __BLOCKS__ */
+extern ipp_status_t cupsFinishDestDocument(http_t *http,
+ cups_dest_t *dest,
+ cups_dinfo_t *info)
+ _CUPS_API_1_6;
+extern void cupsFreeDestInfo(cups_dinfo_t *dinfo) _CUPS_API_1_6;
+extern int cupsGetDestMediaByName(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ const char *media,
+ unsigned flags,
+ cups_size_t *size) _CUPS_API_1_6;
+extern int cupsGetDestMediaBySize(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ int width, int length,
+ unsigned flags,
+ cups_size_t *size) _CUPS_API_1_6;
+extern const char *cupsLocalizeDestOption(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *info,
+ const char *option)
+ _CUPS_API_1_6;
+extern const char *cupsLocalizeDestValue(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *info,
+ const char *option,
+ const char *value)
+ _CUPS_API_1_6;
+extern 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) _CUPS_API_1_6;
+
+/* New in CUPS 1.7 */
+extern ipp_attribute_t *cupsFindDestDefault(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ const char *option) _CUPS_API_1_7;
+extern ipp_attribute_t *cupsFindDestReady(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ const char *option) _CUPS_API_1_7;
+extern ipp_attribute_t *cupsFindDestSupported(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ const char *option)
+ _CUPS_API_1_7;
+extern int cupsGetDestMediaByIndex(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo, int n,
+ unsigned flags,
+ cups_size_t *size)
+ _CUPS_API_1_7;
+extern int cupsGetDestMediaCount(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ unsigned flags) _CUPS_API_1_7;
+extern int cupsGetDestMediaDefault(http_t *http, cups_dest_t *dest,
+ cups_dinfo_t *dinfo,
+ unsigned flags,
+ cups_size_t *size)
+ _CUPS_API_1_7;
+extern void cupsSetUserAgent(const char *user_agent) _CUPS_API_1_7;
+extern const char *cupsUserAgent(void) _CUPS_API_1_7;
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_CUPS_H_ */
+
+/*
+ * End of "$Id: cups.h 11812 2014-04-10 15:47:53Z msweet $".
+ */
diff --git a/cups/libs/cups/custom.c b/cups/libs/cups/custom.c
new file mode 100644
index 000000000..28c1fed40
--- /dev/null
+++ b/cups/libs/cups/custom.c
@@ -0,0 +1,122 @@
+/*
+ * "$Id: custom.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * PPD custom option routines 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/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdFindCustomOption() - Find a custom option.
+ * ppdFindCustomParam() - Find a parameter for a custom option.
+ * ppdFirstCustomParam() - Return the first parameter for a custom option.
+ * ppdNextCustomParam() - Return the next parameter for a custom option.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * 'ppdFindCustomOption()' - Find a custom option.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_coption_t * /* O - Custom option or NULL */
+ppdFindCustomOption(ppd_file_t *ppd, /* I - PPD file */
+ const char *keyword)/* I - Custom option name */
+{
+ ppd_coption_t key; /* Custom option search key */
+
+
+ if (!ppd)
+ return (NULL);
+
+ strlcpy(key.keyword, keyword, sizeof(key.keyword));
+ return ((ppd_coption_t *)cupsArrayFind(ppd->coptions, &key));
+}
+
+
+/*
+ * 'ppdFindCustomParam()' - Find a parameter for a custom option.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_cparam_t * /* O - Custom parameter or NULL */
+ppdFindCustomParam(ppd_coption_t *opt, /* I - Custom option */
+ const char *name) /* I - Parameter name */
+{
+ ppd_cparam_t *param; /* Current custom parameter */
+
+
+ if (!opt)
+ return (NULL);
+
+ for (param = (ppd_cparam_t *)cupsArrayFirst(opt->params);
+ param;
+ param = (ppd_cparam_t *)cupsArrayNext(opt->params))
+ if (!_cups_strcasecmp(param->name, name))
+ break;
+
+ return (param);
+}
+
+
+/*
+ * 'ppdFirstCustomParam()' - Return the first parameter for a custom option.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_cparam_t * /* O - Custom parameter or NULL */
+ppdFirstCustomParam(ppd_coption_t *opt) /* I - Custom option */
+{
+ if (!opt)
+ return (NULL);
+
+ return ((ppd_cparam_t *)cupsArrayFirst(opt->params));
+}
+
+
+/*
+ * 'ppdNextCustomParam()' - Return the next parameter for a custom option.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_cparam_t * /* O - Custom parameter or NULL */
+ppdNextCustomParam(ppd_coption_t *opt) /* I - Custom option */
+{
+ if (!opt)
+ return (NULL);
+
+ return ((ppd_cparam_t *)cupsArrayNext(opt->params));
+}
+
+
+/*
+ * End of "$Id: custom.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/debug-private.h b/cups/libs/cups/debug-private.h
new file mode 100644
index 000000000..8968aebfe
--- /dev/null
+++ b/cups/libs/cups/debug-private.h
@@ -0,0 +1,117 @@
+/*
+ * "$Id: debug-private.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Private debugging macros for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_DEBUG_PRIVATE_H_
+# define _CUPS_DEBUG_PRIVATE_H_
+
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/versioning.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * The debug macros are used if you compile with DEBUG defined.
+ *
+ * Usage:
+ *
+ * DEBUG_puts("string")
+ * DEBUG_printf(("format string", arg, arg, ...));
+ *
+ * Note the extra parenthesis around the DEBUG_printf macro...
+ *
+ * Newlines are not required on the end of messages, as both add one when
+ * writing the output.
+ *
+ * If the first character is a digit, then it represents the "log level" of the
+ * message from 0 to 9. The default level is 1. The following defines the
+ * current levels we use:
+ *
+ * 0 = public APIs, other than value accessor functions
+ * 1 = return values for public APIs
+ * 2 = public value accessor APIs, progress for public APIs
+ * 3 = return values for value accessor APIs
+ * 4 = private APIs, progress for value accessor APIs
+ * 5 = return values for private APIs
+ * 6 = progress for private APIs
+ * 7 = static functions
+ * 8 = return values for static functions
+ * 9 = progress for static functions
+ *
+ * The DEBUG_set macro allows an application to programmatically enable (or
+ * disable) debug logging. The arguments correspond to the CUPS_DEBUG_LOG,
+ * CUPS_DEBUG_LEVEL, and CUPS_DEBUG_FILTER environment variables.
+ */
+
+# ifdef DEBUG
+# ifdef WIN32
+# ifdef LIBCUPS2_EXPORTS
+# define DLLExport __declspec(dllexport)
+# else
+# define DLLExport
+# endif /* LIBCUPS2_EXPORTS */
+# else
+# define DLLExport
+# endif /* WIN32 */
+# define DEBUG_puts(x) _cups_debug_puts(x)
+# define DEBUG_printf(x) _cups_debug_printf x
+# define DEBUG_set(logfile,level,filter) _cups_debug_set(logfile,level,filter,1)
+# else
+# define DLLExport
+# define DEBUG_puts(x)
+# define DEBUG_printf(x)
+# define DEBUG_set(logfile,level,filter)
+# endif /* DEBUG */
+
+
+/*
+ * Prototypes...
+ */
+
+extern int _cups_debug_fd;
+extern int _cups_debug_level;
+extern void DLLExport _cups_debug_printf(const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+extern void DLLExport _cups_debug_puts(const char *s);
+extern void DLLExport _cups_debug_set(const char *logfile,
+ const char *level, const char *filter,
+ int force);
+# ifdef WIN32
+extern int _cups_gettimeofday(struct timeval *tv, void *tz);
+# define gettimeofday(a,b) _cups_gettimeofday(a, b)
+# endif /* WIN32 */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_DEBUG_PRIVATE_H_ */
+
+/*
+ * End of "$Id: debug-private.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/debug.c b/cups/libs/cups/debug.c
new file mode 100644
index 000000000..739ceb3af
--- /dev/null
+++ b/cups/libs/cups/debug.c
@@ -0,0 +1,665 @@
+/*
+ * "$Id: debug.c 4027 2012-11-16 01:00:05Z msweet $"
+ *
+ * Debugging functions for CUPS.
+ *
+ * Copyright 2008-2012 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * debug_vsnprintf() - Format a string into a fixed size buffer.
+ * _cups_debug_printf() - Write a formatted line to the log.
+ * _cups_debug_puts() - Write a single line to the log.
+ * _cups_debug_set() - Enable or disable debug logging.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "thread-private.h"
+#ifdef WIN32
+# include <sys/timeb.h>
+# include <time.h>
+# include <io.h>
+# define getpid (int)GetCurrentProcessId
+int /* O - 0 on success, -1 on failure */
+_cups_gettimeofday(struct timeval *tv, /* I - Timeval struct */
+ void *tz) /* I - Timezone */
+{
+ struct _timeb timebuffer; /* Time buffer struct */
+ _ftime(&timebuffer);
+ tv->tv_sec = (long)timebuffer.time;
+ tv->tv_usec = timebuffer.millitm * 1000;
+ return 0;
+}
+#else
+# include <sys/time.h>
+# include <unistd.h>
+#endif /* WIN32 */
+#ifndef WIN32
+# include <regex.h>
+#endif /* WIN32 */
+#include <fcntl.h>
+
+
+/*
+ * Globals...
+ */
+
+int _cups_debug_fd = -1;
+ /* Debug log file descriptor */
+int _cups_debug_level = 1;
+ /* Log level (0 to 9) */
+
+
+#ifdef DEBUG
+/*
+ * Local globals...
+ */
+
+# ifndef WIN32
+static regex_t *debug_filter = NULL;
+ /* Filter expression for messages */
+# endif /* !WIN32 */
+static int debug_init = 0; /* Did we initialize debugging? */
+static _cups_mutex_t debug_init_mutex = _CUPS_MUTEX_INITIALIZER,
+ /* Mutex to control initialization */
+ debug_log_mutex = _CUPS_MUTEX_INITIALIZER;
+ /* Mutex to serialize log entries */
+
+
+/*
+ * 'debug_thread_id()' - Return an integer representing the current thread.
+ */
+
+static int /* O - Local thread ID */
+debug_thread_id(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ return (cg->thread_id);
+}
+
+
+/*
+ * 'debug_vsnprintf()' - Format a string into a fixed size buffer.
+ */
+
+static int /* O - Number of bytes formatted */
+debug_vsnprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ char *bufptr, /* Pointer to position in buffer */
+ *bufend, /* Pointer to end of buffer */
+ size, /* Size character (h, l, L) */
+ type; /* Format type character */
+ int width, /* Width of field */
+ prec; /* Number of characters of precision */
+ char tformat[100], /* Temporary format string for sprintf() */
+ *tptr, /* Pointer into temporary format */
+ temp[1024]; /* Buffer for formatted numbers */
+ char *s; /* Pointer to string */
+ int bytes; /* Total number of bytes needed */
+
+
+ if (!buffer || bufsize < 2 || !format)
+ return (-1);
+
+ /*
+ * Loop through the format string, formatting as needed...
+ */
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+ bytes = 0;
+
+ while (*format)
+ {
+ if (*format == '%')
+ {
+ tptr = tformat;
+ *tptr++ = *format++;
+
+ if (*format == '%')
+ {
+ if (bufptr < bufend)
+ *bufptr++ = *format;
+ bytes ++;
+ format ++;
+ continue;
+ }
+ else if (strchr(" -+#\'", *format))
+ *tptr++ = *format++;
+
+ if (*format == '*')
+ {
+ /*
+ * Get width from argument...
+ */
+
+ format ++;
+ width = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ width = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ width = width * 10 + *format++ - '0';
+ }
+ }
+
+ if (*format == '.')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ format ++;
+
+ if (*format == '*')
+ {
+ /*
+ * Get precision from argument...
+ */
+
+ format ++;
+ prec = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ prec = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ prec = prec * 10 + *format++ - '0';
+ }
+ }
+ }
+
+ if (*format == 'l' && format[1] == 'l')
+ {
+ size = 'L';
+
+ if (tptr < (tformat + sizeof(tformat) - 2))
+ {
+ *tptr++ = 'l';
+ *tptr++ = 'l';
+ }
+
+ format += 2;
+ }
+ else if (*format == 'h' || *format == 'l' || *format == 'L')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ size = *format++;
+ }
+ else
+ size = 0;
+
+ if (!*format)
+ break;
+
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ type = *format++;
+ *tptr = '\0';
+
+ switch (type)
+ {
+ case 'E' : /* Floating point formats */
+ case 'G' :
+ case 'e' :
+ case 'f' :
+ case 'g' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, double));
+
+ bytes += (int)strlen(temp);
+
+ if (bufptr)
+ {
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr += strlen(bufptr);
+ }
+ break;
+
+ case 'B' : /* Integer formats */
+ case 'X' :
+ case 'b' :
+ case 'd' :
+ case 'i' :
+ case 'o' :
+ case 'u' :
+ case 'x' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+# ifdef HAVE_LONG_LONG
+ if (size == 'L')
+ sprintf(temp, tformat, va_arg(ap, long long));
+ else
+# endif /* HAVE_LONG_LONG */
+ if (size == 'l')
+ sprintf(temp, tformat, va_arg(ap, long));
+ else
+ sprintf(temp, tformat, va_arg(ap, int));
+
+ bytes += (int)strlen(temp);
+
+ if (bufptr)
+ {
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr += strlen(bufptr);
+ }
+ break;
+
+ case 'p' : /* Pointer value */
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, void *));
+
+ bytes += (int)strlen(temp);
+
+ if (bufptr)
+ {
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr += strlen(bufptr);
+ }
+ break;
+
+ case 'c' : /* Character or character array */
+ bytes += width;
+
+ if (bufptr)
+ {
+ if (width <= 1)
+ *bufptr++ = va_arg(ap, int);
+ else
+ {
+ if ((bufptr + width) > bufend)
+ width = (int)(bufend - bufptr);
+
+ memcpy(bufptr, va_arg(ap, char *), (size_t)width);
+ bufptr += width;
+ }
+ }
+ break;
+
+ case 's' : /* String */
+ if ((s = va_arg(ap, char *)) == NULL)
+ s = "(null)";
+
+ /*
+ * Copy the C string, replacing control chars and \ with
+ * C character escapes...
+ */
+
+ for (bufend --; *s && bufptr < bufend; s ++)
+ {
+ if (*s == '\n')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = 'n';
+ bytes += 2;
+ }
+ else if (*s == '\r')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = 'r';
+ bytes += 2;
+ }
+ else if (*s == '\t')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = 't';
+ bytes += 2;
+ }
+ else if (*s == '\\')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = '\\';
+ bytes += 2;
+ }
+ else if (*s == '\'')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = '\'';
+ bytes += 2;
+ }
+ else if (*s == '\"')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = '\"';
+ bytes += 2;
+ }
+ else if ((*s & 255) < ' ')
+ {
+ if ((bufptr + 2) >= bufend)
+ break;
+
+ *bufptr++ = '\\';
+ *bufptr++ = '0';
+ *bufptr++ = '0' + *s / 8;
+ *bufptr++ = '0' + (*s & 7);
+ bytes += 4;
+ }
+ else
+ {
+ *bufptr++ = *s;
+ bytes ++;
+ }
+ }
+
+ bufend ++;
+ break;
+
+ case 'n' : /* Output number of chars so far */
+ *(va_arg(ap, int *)) = bytes;
+ break;
+ }
+ }
+ else
+ {
+ bytes ++;
+
+ if (bufptr < bufend)
+ *bufptr++ = *format;
+
+ format ++;
+ }
+ }
+
+ /*
+ * Nul-terminate the string and return the number of characters needed.
+ */
+
+ *bufptr = '\0';
+
+ return (bytes);
+}
+
+
+/*
+ * '_cups_debug_printf()' - Write a formatted line to the log.
+ */
+
+void DLLExport
+_cups_debug_printf(const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+ struct timeval curtime; /* Current time */
+ char buffer[2048]; /* Output buffer */
+ size_t bytes; /* Number of bytes in buffer */
+ int level; /* Log level in message */
+
+
+ /*
+ * See if we need to do any logging...
+ */
+
+ if (!debug_init)
+ _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"),
+ getenv("CUPS_DEBUG_FILTER"), 0);
+
+ if (_cups_debug_fd < 0)
+ return;
+
+ /*
+ * Filter as needed...
+ */
+
+ if (isdigit(format[0]))
+ level = *format++ - '0';
+ else
+ level = 0;
+
+ if (level > _cups_debug_level)
+ return;
+
+#ifndef WIN32
+ if (debug_filter)
+ {
+ int result; /* Filter result */
+
+ _cupsMutexLock(&debug_init_mutex);
+ result = regexec(debug_filter, format, 0, NULL, 0);
+ _cupsMutexUnlock(&debug_init_mutex);
+
+ if (result)
+ return;
+ }
+#endif
+
+ /*
+ * Format the message...
+ */
+
+ gettimeofday(&curtime, NULL);
+ snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d ",
+ debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24),
+ (int)((curtime.tv_sec / 60) % 60),
+ (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000));
+
+ va_start(ap, format);
+ bytes = debug_vsnprintf(buffer + 19, sizeof(buffer) - 20, format, ap) + 19;
+ va_end(ap);
+
+ if (bytes >= (sizeof(buffer) - 1))
+ {
+ buffer[sizeof(buffer) - 2] = '\n';
+ bytes = sizeof(buffer) - 1;
+ }
+ else if (buffer[bytes - 1] != '\n')
+ {
+ buffer[bytes++] = '\n';
+ buffer[bytes] = '\0';
+ }
+
+ /*
+ * Write it out...
+ */
+
+ _cupsMutexLock(&debug_log_mutex);
+ write(_cups_debug_fd, buffer, bytes);
+ _cupsMutexUnlock(&debug_log_mutex);
+}
+
+
+/*
+ * '_cups_debug_puts()' - Write a single line to the log.
+ */
+
+void DLLExport
+_cups_debug_puts(const char *s) /* I - String to output */
+{
+ struct timeval curtime; /* Current time */
+ char buffer[2048]; /* Output buffer */
+ size_t bytes; /* Number of bytes in buffer */
+ int level; /* Log level in message */
+
+
+ /*
+ * See if we need to do any logging...
+ */
+
+ if (!debug_init)
+ _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"),
+ getenv("CUPS_DEBUG_FILTER"), 0);
+
+ if (_cups_debug_fd < 0)
+ return;
+
+ /*
+ * Filter as needed...
+ */
+
+ if (isdigit(s[0]))
+ level = *s++ - '0';
+ else
+ level = 0;
+
+ if (level > _cups_debug_level)
+ return;
+
+#ifndef WIN32
+ if (debug_filter)
+ {
+ int result; /* Filter result */
+
+ _cupsMutexLock(&debug_init_mutex);
+ result = regexec(debug_filter, s, 0, NULL, 0);
+ _cupsMutexUnlock(&debug_init_mutex);
+
+ if (result)
+ return;
+ }
+#endif /* WIN32 */
+ /*
+ * Format the message...
+ */
+
+ gettimeofday(&curtime, NULL);
+ bytes = snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d %s",
+ debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24),
+ (int)((curtime.tv_sec / 60) % 60),
+ (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000),
+ s);
+
+ if (bytes >= (sizeof(buffer) - 1))
+ {
+ buffer[sizeof(buffer) - 2] = '\n';
+ bytes = sizeof(buffer) - 1;
+ }
+ else if (buffer[bytes - 1] != '\n')
+ {
+ buffer[bytes++] = '\n';
+ buffer[bytes] = '\0';
+ }
+
+ /*
+ * Write it out...
+ */
+
+ _cupsMutexLock(&debug_log_mutex);
+ write(_cups_debug_fd, buffer, bytes);
+ _cupsMutexUnlock(&debug_log_mutex);
+}
+
+
+/*
+ * '_cups_debug_set()' - Enable or disable debug logging.
+ */
+
+void DLLExport
+_cups_debug_set(const char *logfile, /* I - Log file or NULL */
+ const char *level, /* I - Log level or NULL */
+ const char *filter, /* I - Filter string or NULL */
+ int force) /* I - Force initialization */
+{
+ _cupsMutexLock(&debug_init_mutex);
+
+ if (!debug_init || force)
+ {
+ /*
+ * Restore debug settings to defaults...
+ */
+
+ if (_cups_debug_fd != -1)
+ {
+ close(_cups_debug_fd);
+ _cups_debug_fd = -1;
+ }
+
+#ifndef WIN32
+ if (debug_filter)
+ {
+ regfree((regex_t *)debug_filter);
+ debug_filter = NULL;
+ }
+#endif /* WIN32 */
+
+ _cups_debug_level = 1;
+
+ /*
+ * Open logs, set log levels, etc.
+ */
+
+ if (!logfile)
+ _cups_debug_fd = -1;
+ else if (!strcmp(logfile, "-"))
+ _cups_debug_fd = 2;
+ else
+ {
+ char buffer[1024]; /* Filename buffer */
+
+ snprintf(buffer, sizeof(buffer), logfile, getpid());
+
+ if (buffer[0] == '+')
+ _cups_debug_fd = open(buffer + 1, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ else
+ _cups_debug_fd = open(buffer, O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ }
+
+ if (level)
+ _cups_debug_level = atoi(level);
+
+#ifndef WIN32
+ if (filter)
+ {
+ if ((debug_filter = (regex_t *)calloc(1, sizeof(regex_t))) == NULL)
+ fputs("Unable to allocate memory for CUPS_DEBUG_FILTER - results not "
+ "filtered!\n", stderr);
+ else if (regcomp(debug_filter, filter, REG_EXTENDED))
+ {
+ fputs("Bad regular expression in CUPS_DEBUG_FILTER - results not "
+ "filtered!\n", stderr);
+ free(debug_filter);
+ debug_filter = NULL;
+ }
+ }
+#endif
+
+ debug_init = 1;
+ }
+
+ _cupsMutexUnlock(&debug_init_mutex);
+}
+#endif /* DEBUG */
+
+
+/*
+ * End of "$Id: debug.c 4027 2012-11-16 01:00:05Z msweet $".
+ */
diff --git a/cups/libs/cups/dest-job.c b/cups/libs/cups/dest-job.c
new file mode 100644
index 000000000..f346444c6
--- /dev/null
+++ b/cups/libs/cups/dest-job.c
@@ -0,0 +1,366 @@
+/*
+ * "$Id: dest-job.c 4274 2013-04-09 20:10:23Z msweet $"
+ *
+ * Destination job support for CUPS.
+ *
+ * Copyright 2012-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsCancelDestJob() - Cancel a job on a destination.
+ * cupsCloseDestJob() - Close a job and start printing.
+ * cupsCreateDestJob() - Create a job on a destination.
+ * cupsFinishDestDocument() - Finish the current document.
+ * cupsStartDestDocument() - Start a new document.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * 'cupsCancelDestJob()' - Cancel a job on a destination.
+ *
+ * The "job_id" is the number returned by cupsCreateDestJob.
+ *
+ * Returns IPP_STATUS_OK on success and IPP_NOT_AUTHORIZED or IPP_FORBIDDEN on
+ * failure.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_status_t
+cupsCancelDestJob(http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ int job_id) /* I - Job ID */
+{
+ return (IPP_STATUS_ERROR_NOT_FOUND);
+}
+
+
+/*
+ * 'cupsCloseDestJob()' - Close a job and start printing.
+ *
+ * Use when the last call to cupsStartDocument passed 0 for "last_document".
+ * "job_id" is the job ID returned by cupsCreateDestJob. Returns @code IPP_STATUS_OK@
+ * on success.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_status_t /* O - IPP status code */
+cupsCloseDestJob(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *info, /* I - Destination information */
+ int job_id) /* I - Job ID */
+{
+ int i; /* Looping var */
+ ipp_t *request = NULL;/* Close-Job/Send-Document request */
+ ipp_attribute_t *attr; /* operations-supported attribute */
+
+
+ DEBUG_printf(("cupsCloseDestJob(http=%p, dest=%p(%s/%s), info=%p, job_id=%d)",
+ http, dest, dest ? dest->name : NULL,
+ dest ? dest->instance : NULL, info, job_id));
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !info || job_id <= 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ DEBUG_puts("1cupsCloseDestJob: Bad arguments.");
+ return (IPP_STATUS_ERROR_INTERNAL);
+ }
+
+ /*
+ * Build a Close-Job or empty Send-Document request...
+ */
+
+ if ((attr = ippFindAttribute(info->attrs, "operations-supported",
+ IPP_TAG_ENUM)) != NULL)
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ if (attr->values[i].integer == IPP_OP_CLOSE_JOB)
+ {
+ request = ippNewRequest(IPP_OP_CLOSE_JOB);
+ break;
+ }
+ }
+
+ if (!request)
+ request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
+
+ if (!request)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
+ DEBUG_puts("1cupsCloseDestJob: Unable to create Close-Job/Send-Document "
+ "request.");
+ return (IPP_STATUS_ERROR_INTERNAL);
+ }
+
+ ippSetVersion(request, info->version / 10, info->version % 10);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, info->uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
+ job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+ if (ippGetOperation(request) == IPP_OP_SEND_DOCUMENT)
+ ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+
+ /*
+ * Send the request and return the status...
+ */
+
+ ippDelete(cupsDoRequest(http, request, info->resource));
+
+ DEBUG_printf(("1cupsCloseDestJob: %s (%s)", ippErrorString(cupsLastError()),
+ cupsLastErrorString()));
+
+ return (cupsLastError());
+}
+
+
+/*
+ * 'cupsCreateDestJob()' - Create a job on a destination.
+ *
+ * Returns @code IPP_STATUS_OK@ or @code IPP_STATUS_OK_SUBST@ on success, saving the job ID
+ * in the variable pointed to by "job_id".
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_status_t /* O - IPP status code */
+cupsCreateDestJob(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *info, /* I - Destination information */
+ int *job_id, /* O - Job ID or 0 on error */
+ const char *title, /* I - Job name */
+ int num_options, /* I - Number of job options */
+ cups_option_t *options) /* I - Job options */
+{
+ ipp_t *request, /* Create-Job request */
+ *response; /* Create-Job response */
+ ipp_attribute_t *attr; /* job-id attribute */
+
+
+ DEBUG_printf(("cupsCreateDestJob(http=%p, dest=%p(%s/%s), info=%p, "
+ "job_id=%p, title=\"%s\", num_options=%d, options=%p)",
+ http, dest, dest ? dest->name : NULL,
+ dest ? dest->instance : NULL, info, job_id, title, num_options,
+ options));
+
+ /*
+ * Range check input...
+ */
+
+ if (job_id)
+ *job_id = 0;
+
+ if (!http || !dest || !info || !job_id)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ DEBUG_puts("1cupsCreateDestJob: Bad arguments.");
+ return (IPP_STATUS_ERROR_INTERNAL);
+ }
+
+ /*
+ * Build a Create-Job request...
+ */
+
+ if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
+ DEBUG_puts("1cupsCreateDestJob: Unable to create Create-Job request.");
+ return (IPP_STATUS_ERROR_INTERNAL);
+ }
+
+ ippSetVersion(request, info->version / 10, info->version % 10);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, info->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...
+ */
+
+ response = cupsDoRequest(http, request, info->resource);
+
+ if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
+ {
+ *job_id = attr->values[0].integer;
+ DEBUG_printf(("1cupsCreateDestJob: job-id=%d", *job_id));
+ }
+
+ ippDelete(response);
+
+ /*
+ * Return the status code from the Create-Job request...
+ */
+
+ DEBUG_printf(("1cupsCreateDestJob: %s (%s)", ippErrorString(cupsLastError()),
+ cupsLastErrorString()));
+
+ return (cupsLastError());
+}
+
+
+/*
+ * 'cupsFinishDestDocument()' - Finish the current document.
+ *
+ * Returns @code IPP_STATUS_OK@ or @code IPP_STATUS_OK_SUBST@ on success.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_status_t /* O - Status of document submission */
+cupsFinishDestDocument(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *info) /* I - Destination information */
+{
+ DEBUG_printf(("cupsFinishDestDocument(http=%p, dest=%p(%s/%s), info=%p)",
+ http, dest, dest ? dest->name : NULL,
+ dest ? dest->instance : NULL, info));
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !info)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ DEBUG_puts("1cupsFinishDestDocument: Bad arguments.");
+ return (IPP_STATUS_ERROR_INTERNAL);
+ }
+
+ /*
+ * Get the response at the end of the document and return it...
+ */
+
+ ippDelete(cupsGetResponse(http, info->resource));
+
+ DEBUG_printf(("1cupsFinishDestDocument: %s (%s)",
+ ippErrorString(cupsLastError()), cupsLastErrorString()));
+
+ return (cupsLastError());
+}
+
+
+/*
+ * 'cupsStartDestDocument()' - Start a new document.
+ *
+ * "job_id" is the job ID returned by cupsCreateDestJob. "docname" is the name
+ * of the document/file being printed, "format" is the MIME media type for the
+ * document (see CUPS_FORMAT_xxx constants), and "num_options" and "options"
+ * are the options do be applied to the document. "last_document" should be 1
+ * if this is the last document to be submitted in the job. Returns
+ * @code HTTP_CONTINUE@ on success.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+http_status_t /* O - Status of document creation */
+cupsStartDestDocument(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *info, /* I - Destination information */
+ int job_id, /* I - Job ID */
+ const char *docname, /* I - Document name */
+ const char *format, /* I - Document format */
+ int num_options, /* I - Number of document options */
+ cups_option_t *options, /* I - Document options */
+ int last_document) /* I - 1 if this is the last document */
+{
+ ipp_t *request; /* Send-Document request */
+ http_status_t status; /* HTTP status */
+
+
+ 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)",
+ http, dest, dest ? dest->name : NULL,
+ dest ? dest->instance : NULL, info, job_id, docname, format,
+ num_options, options, last_document));
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !info || job_id <= 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ DEBUG_puts("1cupsStartDestDocument: Bad arguments.");
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Create a Send-Document request...
+ */
+
+ if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
+ DEBUG_puts("1cupsStartDestDocument: Unable to create Send-Document "
+ "request.");
+ return (HTTP_STATUS_ERROR);
+ }
+
+ ippSetVersion(request, info->version / 10, info->version % 10);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, info->uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+ if (docname)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
+ NULL, docname);
+ if (format)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format);
+ ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", last_document);
+
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
+ cupsEncodeOptions2(request, num_options, options, IPP_TAG_DOCUMENT);
+
+ /*
+ * Send and delete the request, then return the status...
+ */
+
+ status = cupsSendRequest(http, request, info->resource, CUPS_LENGTH_VARIABLE);
+
+ ippDelete(request);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: dest-job.c 4274 2013-04-09 20:10:23Z msweet $".
+ */
diff --git a/cups/libs/cups/dest-localization.c b/cups/libs/cups/dest-localization.c
new file mode 100644
index 000000000..9c3cfae15
--- /dev/null
+++ b/cups/libs/cups/dest-localization.c
@@ -0,0 +1,387 @@
+/*
+ * "$Id: dest-localization.c 4216 2013-03-11 13:57:36Z msweet $"
+ *
+ * Destination localization support for CUPS.
+ *
+ * Copyright 2012-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsLocalizeDestOption() - Get the localized string for a destination
+ * option.
+ * cupsLocalizeDestValue() - Get the localized string for a destination
+ * option+value pair.
+ * cups_create_localizations() - Create the localizations array for a
+ * destination.
+ * cups_read_strings() - Read a pair of strings from a .strings file.
+ * cups_scan_strings() - Scan a quoted string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void cups_create_localizations(http_t *http, cups_dinfo_t *dinfo);
+static int cups_read_strings(cups_file_t *fp, char *buffer, size_t bufsize,
+ char **id, char **str);
+static char *cups_scan_strings(char *buffer);
+
+
+/*
+ * 'cupsLocalizeDestOption()' - Get the localized string for a destination
+ * option.
+ *
+ * The returned string is stored in the destination information and will become
+ * invalid if the destination information is deleted.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+const char * /* O - Localized string */
+cupsLocalizeDestOption(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *option) /* I - Option to localize */
+{
+ _cups_message_t key, /* Search key */
+ *match; /* Matching entry */
+
+
+ if (!http || !dest || !dinfo)
+ return (option);
+
+ if (!dinfo->localizations)
+ cups_create_localizations(http, dinfo);
+
+ if (cupsArrayCount(dinfo->localizations) == 0)
+ return (option);
+
+ key.id = (char *)option;
+ if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
+ &key)) != NULL)
+ return (match->str);
+ else
+ return (option);
+}
+
+
+/*
+ * 'cupsLocalizeDestValue()' - Get the localized string for a destination
+ * option+value pair.
+ *
+ * The returned string is stored in the destination information and will become
+ * invalid if the destination information is deleted.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+const char * /* O - Localized string */
+cupsLocalizeDestValue(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *option, /* I - Option to localize */
+ const char *value) /* I - Value to localize */
+{
+ _cups_message_t key, /* Search key */
+ *match; /* Matching entry */
+ char pair[256]; /* option.value pair */
+
+
+ if (!http || !dest || !dinfo)
+ return (value);
+
+ if (!dinfo->localizations)
+ cups_create_localizations(http, dinfo);
+
+ if (cupsArrayCount(dinfo->localizations) == 0)
+ return (value);
+
+ snprintf(pair, sizeof(pair), "%s.%s", option, value);
+ key.id = pair;
+ if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
+ &key)) != NULL)
+ return (match->str);
+ else
+ return (value);
+}
+
+
+/*
+ * 'cups_create_localizations()' - Create the localizations array for a
+ * destination.
+ */
+
+static void
+cups_create_localizations(
+ http_t *http, /* I - Connection to destination */
+ cups_dinfo_t *dinfo) /* I - Destination informations */
+{
+ http_t *http2; /* Connection for strings file */
+ http_status_t status; /* Request status */
+ ipp_attribute_t *attr; /* "printer-strings-uri" attribute */
+ char scheme[32], /* URI scheme */
+ userpass[256], /* Username/password info */
+ hostname[256], /* Hostname */
+ resource[1024], /* Resource */
+ http_hostname[256],
+ /* Hostname of connection */
+ tempfile[1024]; /* Temporary filename */
+ int port; /* Port number */
+ http_encryption_t encryption; /* Encryption to use */
+ cups_file_t *temp; /* Temporary file */
+
+
+ /*
+ * Create an empty message catalog...
+ */
+
+ dinfo->localizations = _cupsMessageNew(NULL);
+
+ /*
+ * See if there are any localizations...
+ */
+
+ if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri",
+ IPP_TAG_URI)) == NULL)
+ {
+ /*
+ * Nope...
+ */
+
+ DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) "
+ "value.");
+ return; /* Nope */
+ }
+
+ /*
+ * Pull apart the URI and determine whether we need to try a different
+ * server...
+ */
+
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text,
+ scheme, sizeof(scheme), userpass, sizeof(userpass),
+ hostname, sizeof(hostname), &port, resource,
+ sizeof(resource)) < HTTP_URI_STATUS_OK)
+ {
+ DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value "
+ "\"%s\".", attr->values[0].string.text));
+ return;
+ }
+
+ httpGetHostname(http, http_hostname, sizeof(http_hostname));
+
+ if (!_cups_strcasecmp(http_hostname, hostname) &&
+ port == httpAddrPort(http->hostaddr))
+ {
+ /*
+ * Use the same connection...
+ */
+
+ http2 = http;
+ }
+ else
+ {
+ /*
+ * Connect to the alternate host...
+ */
+
+ if (!strcmp(scheme, "https"))
+ encryption = HTTP_ENCRYPTION_ALWAYS;
+ else
+ encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+
+ if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1,
+ 30000, NULL)) == NULL)
+ {
+ DEBUG_printf(("4cups_create_localizations: Unable to connect to "
+ "%s:%d: %s", hostname, port, cupsLastErrorString()));
+ return;
+ }
+ }
+
+ /*
+ * Get a temporary file...
+ */
+
+ if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
+ {
+ DEBUG_printf(("4cups_create_localizations: Unable to create temporary "
+ "file: %s", cupsLastErrorString()));
+ if (http2 != http)
+ httpClose(http2);
+ return;
+ }
+
+ status = cupsGetFd(http2, resource, cupsFileNumber(temp));
+
+ DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource,
+ httpStatus(status)));
+
+ if (status == HTTP_STATUS_OK)
+ {
+ /*
+ * Got the file, read it...
+ */
+
+ char buffer[8192], /* Message buffer */
+ *id, /* ID string */
+ *str; /* Translated message */
+ _cups_message_t *m; /* Current message */
+
+ lseek(cupsFileNumber(temp), 0, SEEK_SET);
+
+ while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str))
+ {
+ if ((m = malloc(sizeof(_cups_message_t))) == NULL)
+ break;
+
+ m->id = strdup(id);
+ m->str = strdup(str);
+
+ if (m->id && m->str)
+ cupsArrayAdd(dinfo->localizations, m);
+ else
+ {
+ if (m->id)
+ free(m->id);
+
+ if (m->str)
+ free(m->str);
+
+ free(m);
+ break;
+ }
+ }
+ }
+
+ DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
+ cupsArrayCount(dinfo->localizations)));
+
+ /*
+ * Cleanup...
+ */
+
+ unlink(tempfile);
+ cupsFileClose(temp);
+
+ if (http2 != http)
+ httpClose(http2);
+}
+
+
+/*
+ * 'cups_read_strings()' - Read a pair of strings from a .strings file.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+cups_read_strings(cups_file_t *strings, /* I - .strings file */
+ char *buffer, /* I - Line buffer */
+ size_t bufsize, /* I - Size of line buffer */
+ char **id, /* O - Pointer to ID string */
+ char **str) /* O - Pointer to translation string */
+{
+ char *bufptr; /* Pointer into buffer */
+
+
+ while (cupsFileGets(strings, buffer, bufsize))
+ {
+ if (buffer[0] != '\"')
+ continue;
+
+ *id = buffer + 1;
+ bufptr = cups_scan_strings(buffer);
+
+ if (*bufptr != '\"')
+ continue;
+
+ *bufptr++ = '\0';
+
+ while (*bufptr && *bufptr != '\"')
+ bufptr ++;
+
+ if (!*bufptr)
+ continue;
+
+ *str = bufptr + 1;
+ bufptr = cups_scan_strings(bufptr);
+
+ if (*bufptr != '\"')
+ continue;
+
+ *bufptr = '\0';
+
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'cups_scan_strings()' - Scan a quoted string.
+ */
+
+static char * /* O - End of string */
+cups_scan_strings(char *buffer) /* I - Start of string */
+{
+ char *bufptr; /* Pointer into string */
+
+
+ for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++)
+ {
+ if (*bufptr == '\\')
+ {
+ if (bufptr[1] >= '0' && bufptr[1] <= '3' &&
+ bufptr[2] >= '0' && bufptr[2] <= '7' &&
+ bufptr[3] >= '0' && bufptr[3] <= '7')
+ {
+ /*
+ * Decode \nnn octal escape...
+ */
+
+ *bufptr = ((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) |
+ (bufptr[3] - '0');
+ _cups_strcpy(bufptr + 1, bufptr + 4);
+ }
+ else
+ {
+ /*
+ * Decode \C escape...
+ */
+
+ _cups_strcpy(bufptr, bufptr + 1);
+ if (*bufptr == 'n')
+ *bufptr = '\n';
+ else if (*bufptr == 'r')
+ *bufptr = '\r';
+ else if (*bufptr == 't')
+ *bufptr = '\t';
+ }
+ }
+ }
+
+ return (bufptr);
+}
+
+
+
+/*
+ * End of "$Id: dest-localization.c 4216 2013-03-11 13:57:36Z msweet $".
+ */
diff --git a/cups/libs/cups/dest-options.c b/cups/libs/cups/dest-options.c
new file mode 100644
index 000000000..8c585340a
--- /dev/null
+++ b/cups/libs/cups/dest-options.c
@@ -0,0 +1,2311 @@
+/*
+ * "$Id: dest-options.c 11883 2014-05-16 21:04:07Z msweet $"
+ *
+ * Destination option/media support for CUPS.
+ *
+ * Copyright 2012-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsCheckDestSupported() - Check that the option and value are supported
+ * by the destination.
+ * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
+ * option/value pair.
+ * cupsCopyDestInfo() - Get the supported values/capabilities for the
+ * destination.
+ * cupsFindDestDefault() - Find the default value(s) for the given
+ * option.
+ * cupsFindDestReady() - Find the default value(s) for the given
+ * option.
+ * cupsFindDestSupported() - Find the default value(s) for the given
+ * option.
+ * cupsFreeDestInfo() - Free destination information obtained using
+ * @link cupsCopyDestInfo@.
+ * cupsGetDestMediaByIndex() - Get a media name, dimension, and margins for a
+ * specific size.
+ * cupsGetDestMediaByName() - Get media names, dimensions, and margins.
+ * cupsGetDestMediaBySize() - Get media names, dimensions, and margins.
+ * cupsGetDestMediaCount() - Get the number of sizes supported by a
+ * destination.
+ * cupsGetDestMediaDefault() - Get the default size for a destination.
+ * cups_add_dconstres() - Add a constraint or resolver to an array.
+ * cups_compare_dconstres() - Compare to resolver entries.
+ * cups_compare_media_db() - Compare two media entries.
+ * cups_copy_media_db() - Copy a media entry.
+ * cups_create_cached() - Create the media selection cache.
+ * cups_create_constraints() - Create the constraints and resolvers arrays.
+ * cups_create_defaults() - Create the -default option array.
+ * cups_create_media_db() - Create the media database.
+ * cups_free_media_cb() - Free a media entry.
+ * cups_get_media_db() - Lookup the media entry for a given size.
+ * cups_is_close_media_db() - Compare two media entries to see if they are
+ * close to the same size.
+ * cups_test_constraints() - Test constraints.
+ * cups_update_ready() - Update xxx-ready attributes for the printer.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local constants...
+ */
+
+#define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
+
+
+/*
+ * Local functions...
+ */
+
+static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
+static int cups_compare_dconstres(_cups_dconstres_t *a,
+ _cups_dconstres_t *b);
+static int cups_compare_media_db(_cups_media_db_t *a,
+ _cups_media_db_t *b);
+static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
+static void cups_create_cached(http_t *http, cups_dinfo_t *dinfo,
+ unsigned flags);
+static void cups_create_constraints(cups_dinfo_t *dinfo);
+static void cups_create_defaults(cups_dinfo_t *dinfo);
+static void cups_create_media_db(cups_dinfo_t *dinfo,
+ unsigned flags);
+static void cups_free_media_db(_cups_media_db_t *mdb);
+static int cups_get_media_db(http_t *http, cups_dinfo_t *dinfo,
+ pwg_media_t *pwg, unsigned flags,
+ cups_size_t *size);
+static int cups_is_close_media_db(_cups_media_db_t *a,
+ _cups_media_db_t *b);
+static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
+ const char *new_option,
+ const char *new_value,
+ int num_options,
+ cups_option_t *options,
+ int *num_conflicts,
+ cups_option_t **conflicts);
+static void cups_update_ready(http_t *http, cups_dinfo_t *dinfo);
+
+
+/*
+ * 'cupsCheckDestSupported()' - Check that the option and value are supported
+ * by the destination.
+ *
+ * Returns 1 if supported, 0 otherwise.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 if supported, 0 otherwise */
+cupsCheckDestSupported(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *option, /* I - Option */
+ const char *value) /* I - Value */
+{
+ int i; /* Looping var */
+ char temp[1024]; /* Temporary string */
+ int int_value; /* Integer value */
+ int xres_value, /* Horizontal resolution */
+ yres_value; /* Vertical resolution */
+ ipp_res_t units_value; /* Resolution units */
+ ipp_attribute_t *attr; /* Attribute */
+ _ipp_value_t *attrval; /* Current attribute value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !dinfo || !option || !value)
+ return (0);
+
+ /*
+ * Lookup the attribute...
+ */
+
+ if (strstr(option, "-supported"))
+ attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
+ else
+ {
+ snprintf(temp, sizeof(temp), "%s-supported", option);
+ attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
+ }
+
+ if (!attr)
+ return (0);
+
+ /*
+ * Compare values...
+ */
+
+ if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
+ {
+ /*
+ * Check range of custom media sizes...
+ */
+
+ pwg_media_t *pwg; /* Current PWG media size info */
+ int min_width, /* Minimum width */
+ min_length, /* Minimum length */
+ max_width, /* Maximum width */
+ max_length; /* Maximum length */
+
+ /*
+ * Get the minimum and maximum size...
+ */
+
+ min_width = min_length = INT_MAX;
+ max_width = max_length = 0;
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (!strncmp(attrval->string.text, "custom_min_", 11) &&
+ (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
+ {
+ min_width = pwg->width;
+ min_length = pwg->length;
+ }
+ else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
+ (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
+ {
+ max_width = pwg->width;
+ max_length = pwg->length;
+ }
+ }
+
+ /*
+ * Check the range...
+ */
+
+ if (min_width < INT_MAX && max_width > 0 &&
+ (pwg = pwgMediaForPWG(value)) != NULL &&
+ pwg->width >= min_width && pwg->width <= max_width &&
+ pwg->length >= min_length && pwg->length <= max_length)
+ return (1);
+ }
+ else
+ {
+ /*
+ * Check literal values...
+ */
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ int_value = atoi(value);
+
+ for (i = 0; i < attr->num_values; i ++)
+ if (attr->values[i].integer == int_value)
+ return (1);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ return (attr->values[0].boolean);
+
+ case IPP_TAG_RANGE :
+ int_value = atoi(value);
+
+ for (i = 0; i < attr->num_values; i ++)
+ if (int_value >= attr->values[i].range.lower &&
+ int_value <= attr->values[i].range.upper)
+ return (1);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
+ {
+ if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
+ return (0);
+
+ yres_value = xres_value;
+ }
+
+ if (!strcmp(temp, "dpi"))
+ units_value = IPP_RES_PER_INCH;
+ else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
+ units_value = IPP_RES_PER_CM;
+ else
+ return (0);
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->resolution.xres == xres_value &&
+ attrval->resolution.yres == yres_value &&
+ attrval->resolution.units == units_value)
+ return (1);
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_MIMETYPE :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ for (i = 0; i < attr->num_values; i ++)
+ if (!strcmp(attr->values[i].string.text, value))
+ return (1);
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ /*
+ * If we get there the option+value is not supported...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
+ * option/value pair.
+ *
+ * "num_options" and "options" represent the currently selected options by the
+ * user. "new_option" and "new_value" are the setting the user has just
+ * changed.
+ *
+ * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
+ * there was an unrecoverable error such as a resolver loop.
+ *
+ * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
+ * contain the list of conflicting option/value pairs. Similarly, if
+ * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
+ * list of changes needed to resolve the conflict.
+ *
+ * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
+ * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 if there is a conflict, 0 if none, -1 on error */
+cupsCopyDestConflicts(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ int num_options, /* I - Number of current options */
+ cups_option_t *options, /* I - Current options */
+ const char *new_option, /* I - New option */
+ const char *new_value, /* I - New value */
+ int *num_conflicts, /* O - Number of conflicting options */
+ cups_option_t **conflicts, /* O - Conflicting options */
+ int *num_resolved, /* O - Number of options to resolve */
+ cups_option_t **resolved) /* O - Resolved options */
+{
+ int i, /* Looping var */
+ have_conflicts = 0, /* Do we have conflicts? */
+ changed, /* Did we change something? */
+ tries, /* Number of tries for resolution */
+ num_myconf = 0, /* My number of conflicting options */
+ num_myres = 0; /* My number of resolved options */
+ cups_option_t *myconf = NULL, /* My conflicting options */
+ *myres = NULL, /* My resolved options */
+ *myoption, /* My current option */
+ *option; /* Current option */
+ cups_array_t *active, /* Active conflicts */
+ *pass = NULL, /* Resolvers for this pass */
+ *resolvers = NULL, /* Resolvers we have used */
+ *test; /* Test array for conflicts */
+ _cups_dconstres_t *c, /* Current constraint */
+ *r; /* Current resolver */
+ ipp_attribute_t *attr; /* Current attribute */
+ char value[2048]; /* Current attribute value as string */
+ const char *myvalue; /* Current value of an option */
+
+
+ /*
+ * Clear returned values...
+ */
+
+ if (num_conflicts)
+ *num_conflicts = 0;
+
+ if (conflicts)
+ *conflicts = NULL;
+
+ if (num_resolved)
+ *num_resolved = 0;
+
+ if (resolved)
+ *resolved = NULL;
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !dinfo ||
+ (num_conflicts != NULL) != (conflicts != NULL) ||
+ (num_resolved != NULL) != (resolved != NULL))
+ return (0);
+
+ /*
+ * Load constraints as needed...
+ */
+
+ if (!dinfo->constraints)
+ cups_create_constraints(dinfo);
+
+ if (cupsArrayCount(dinfo->constraints) == 0)
+ return (0);
+
+ if (!dinfo->num_defaults)
+ cups_create_defaults(dinfo);
+
+ /*
+ * If we are resolving, create a shadow array...
+ */
+
+ if (num_resolved)
+ {
+ for (i = num_options, option = options; i > 0; i --, option ++)
+ num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
+
+ if (new_option && new_value)
+ num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
+ }
+ else
+ {
+ num_myres = num_options;
+ myres = options;
+ }
+
+ /*
+ * Check for any conflicts...
+ */
+
+ if (num_resolved)
+ pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
+
+ for (tries = 0; tries < 100; tries ++)
+ {
+ /*
+ * Check for any conflicts...
+ */
+
+ if (num_conflicts || num_resolved)
+ {
+ cupsFreeOptions(num_myconf, myconf);
+
+ num_myconf = 0;
+ myconf = NULL;
+ active = cups_test_constraints(dinfo, new_option, new_value,
+ num_myres, myres, &num_myconf,
+ &myconf);
+ }
+ else
+ active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
+ myres, NULL, NULL);
+
+ have_conflicts = (active != NULL);
+
+ if (!active || !num_resolved)
+ break; /* All done */
+
+ /*
+ * Scan the constraints that were triggered to apply resolvers...
+ */
+
+ if (!resolvers)
+ resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
+
+ for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
+ c;
+ c = (_cups_dconstres_t *)cupsArrayNext(active))
+ {
+ if (cupsArrayFind(pass, c))
+ continue; /* Already applied this resolver... */
+
+ if (cupsArrayFind(resolvers, c))
+ {
+ DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
+ c->name));
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
+ {
+ DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
+ c->name));
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Add the options from the resolver...
+ */
+
+ cupsArrayAdd(pass, r);
+ cupsArrayAdd(resolvers, r);
+
+ for (attr = ippFirstAttribute(r->collection);
+ attr;
+ attr = ippNextAttribute(r->collection))
+ {
+ if (new_option && !strcmp(attr->name, new_option))
+ continue; /* Ignore this if we just changed it */
+
+ if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
+ continue; /* Ignore if the value is too long */
+
+ if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
+ myres, NULL, NULL)) == NULL)
+ {
+ /*
+ * That worked, flag it...
+ */
+
+ changed = 1;
+ }
+ else
+ cupsArrayDelete(test);
+
+ /*
+ * Add the option/value from the resolver regardless of whether it
+ * worked; this makes sure that we can cascade several changes to
+ * make things resolve...
+ */
+
+ num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
+ }
+ }
+
+ if (!changed)
+ {
+ DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ cupsArrayClear(pass);
+
+ cupsArrayDelete(active);
+ active = NULL;
+ }
+
+ if (tries >= 100)
+ {
+ DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
+ have_conflicts = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Copy resolved options as needed...
+ */
+
+ if (num_resolved)
+ {
+ for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
+ {
+ if ((myvalue = cupsGetOption(myoption->name, num_options,
+ options)) == NULL ||
+ strcmp(myvalue, myoption->value))
+ {
+ if (new_option && !strcmp(new_option, myoption->name) &&
+ new_value && !strcmp(new_value, myoption->value))
+ continue;
+
+ *num_resolved = cupsAddOption(myoption->name, myoption->value,
+ *num_resolved, resolved);
+ }
+ }
+ }
+
+ /*
+ * Clean up...
+ */
+
+ cleanup:
+
+ cupsArrayDelete(active);
+ cupsArrayDelete(pass);
+ cupsArrayDelete(resolvers);
+
+ if (num_resolved)
+ {
+ /*
+ * Free shadow copy of options...
+ */
+
+ cupsFreeOptions(num_myres, myres);
+ }
+
+ if (num_conflicts)
+ {
+ /*
+ * Return conflicting options to caller...
+ */
+
+ *num_conflicts = num_myconf;
+ *conflicts = myconf;
+ }
+ else
+ {
+ /*
+ * Free conflicting options...
+ */
+
+ cupsFreeOptions(num_myconf, myconf);
+ }
+
+ return (have_conflicts);
+}
+
+
+/*
+ * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
+ * destination.
+ *
+ * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
+ * value. @code NULL@ is returned on error.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+cups_dinfo_t * /* O - Destination information */
+cupsCopyDestInfo(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest) /* I - Destination */
+{
+ cups_dinfo_t *dinfo; /* Destination information */
+ ipp_t *request, /* Get-Printer-Attributes request */
+ *response; /* Supported attributes */
+ int tries, /* Number of tries so far */
+ delay, /* Current retry delay */
+ prev_delay; /* Next retry delay */
+ const char *uri; /* Printer URI */
+ char resource[1024]; /* Resource path */
+ int version; /* IPP version */
+ ipp_status_t status; /* Status of request */
+ static const char * const requested_attrs[] =
+ { /* Requested attributes */
+ "job-template",
+ "media-col-database",
+ "printer-description"
+ };
+
+
+ DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http, dest,
+ dest ? dest->name : ""));
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest)
+ return (NULL);
+
+ /*
+ * Get the printer URI and resource path...
+ */
+
+ if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
+ return (NULL);
+
+ /*
+ * Get the supported attributes...
+ */
+
+ delay = 1;
+ prev_delay = 1;
+ tries = 0;
+ version = 20;
+
+ do
+ {
+ /*
+ * Send a Get-Printer-Attributes request...
+ */
+
+ 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(requested_attrs) / sizeof(requested_attrs[0])),
+ NULL, requested_attrs);
+ response = cupsDoRequest(http, request, resource);
+ status = cupsLastError();
+
+ if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
+ {
+ DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
+ "returned %s (%s)", dest->name, ippErrorString(status),
+ cupsLastErrorString()));
+
+ ippDelete(response);
+ response = NULL;
+
+ if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
+ version = 11;
+ else if (status == IPP_STATUS_ERROR_BUSY)
+ {
+ sleep(delay);
+
+ delay = _cupsNextDelay(delay, &prev_delay);
+ }
+ else
+ return (NULL);
+ }
+
+ tries ++;
+ }
+ while (!response && tries < 10);
+
+ if (!response)
+ return (NULL);
+
+ /*
+ * Allocate a cups_dinfo_t structure and return it...
+ */
+
+ if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ ippDelete(response);
+ return (NULL);
+ }
+
+ dinfo->version = version;
+ dinfo->uri = uri;
+ dinfo->resource = _cupsStrAlloc(resource);
+ dinfo->attrs = response;
+
+ return (dinfo);
+}
+
+
+/*
+ * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
+ *
+ * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
+ * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
+ * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
+ * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
+ * functions to inspect the default value(s) as needed.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
+cupsFindDestDefault(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *option) /* I - Option/attribute name */
+{
+ char name[IPP_MAX_NAME]; /* Attribute name */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !dinfo || !option)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (NULL);
+ }
+
+ /*
+ * Find and return the attribute...
+ */
+
+ snprintf(name, sizeof(name), "%s-default", option);
+ return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
+}
+
+/*
+ * 'cupsFindDestReady()' - Find the default value(s) for the given option.
+ *
+ * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
+ * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
+ * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
+ * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
+ * functions to inspect the default value(s) as needed.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
+cupsFindDestReady(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *option) /* I - Option/attribute name */
+{
+ char name[IPP_MAX_NAME]; /* Attribute name */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !dinfo || !option)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (NULL);
+ }
+
+ /*
+ * Find and return the attribute...
+ */
+
+ cups_update_ready(http, dinfo);
+
+ snprintf(name, sizeof(name), "%s-ready", option);
+ return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO));
+}
+
+/*
+ * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
+ *
+ * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
+ * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
+ * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
+ * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
+ * functions to inspect the default value(s) as needed.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
+cupsFindDestSupported(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *option) /* I - Option/attribute name */
+{
+ char name[IPP_MAX_NAME]; /* Attribute name */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !dinfo || !option)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (NULL);
+ }
+
+ /*
+ * Find and return the attribute...
+ */
+
+ snprintf(name, sizeof(name), "%s-supported", option);
+ return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
+}
+
+
+/*
+ * 'cupsFreeDestInfo()' - Free destination information obtained using
+ * @link cupsCopyDestInfo@.
+ */
+
+void
+cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!dinfo)
+ return;
+
+ /*
+ * Free memory and return...
+ */
+
+ _cupsStrFree(dinfo->resource);
+
+ cupsArrayDelete(dinfo->constraints);
+ cupsArrayDelete(dinfo->resolvers);
+
+ cupsArrayDelete(dinfo->localizations);
+
+ cupsArrayDelete(dinfo->media_db);
+
+ cupsArrayDelete(dinfo->cached_db);
+
+ ippDelete(dinfo->ready_attrs);
+ cupsArrayDelete(dinfo->ready_db);
+
+ ippDelete(dinfo->attrs);
+
+ free(dinfo);
+}
+
+
+/*
+ * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
+ * specific size.
+ *
+ * The @code flags@ parameter determines which set of media are indexed. For
+ * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
+ * borderless size supported by the printer.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsGetDestMediaByIndex(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ int n, /* I - Media size number (0-based) */
+ unsigned flags, /* I - Media flags */
+ cups_size_t *size) /* O - Media size information */
+{
+ _cups_media_db_t *nsize; /* Size for N */
+ pwg_media_t *pwg; /* PWG media name for size */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (size)
+ memset(size, 0, sizeof(cups_size_t));
+
+ if (!http || !dest || !dinfo || n < 0 || !size)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Load media list as needed...
+ */
+
+ if (flags & CUPS_MEDIA_FLAGS_READY)
+ cups_update_ready(http, dinfo);
+
+ if (!dinfo->cached_db || dinfo->cached_flags != flags)
+ cups_create_cached(http, dinfo, flags);
+
+ /*
+ * Copy the size over and return...
+ */
+
+ if ((nsize = (_cups_media_db_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ if (nsize->size_name)
+ strlcpy(size->media, nsize->size_name, sizeof(size->media));
+ else if (nsize->key)
+ strlcpy(size->media, nsize->key, sizeof(size->media));
+ else if ((pwg = pwgMediaForSize(nsize->width, nsize->length)) != NULL)
+ strlcpy(size->media, pwg->pwg, sizeof(size->media));
+ else
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ size->width = nsize->width;
+ size->length = nsize->length;
+ size->bottom = nsize->bottom;
+ size->left = nsize->left;
+ size->right = nsize->right;
+ size->top = nsize->top;
+
+ return (1);
+}
+
+
+/*
+ * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
+ *
+ * The "media" string is a PWG media name. "Flags" provides some matching
+ * guidance (multiple flags can be combined):
+ *
+ * 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 2-sided printing,
+ * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
+ * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
+ * size amongst the "ready" media.
+ *
+ * The matching result (if any) is returned in the "cups_size_t" structure.
+ *
+ * Returns 1 when there is a match and 0 if there is not a match.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on match, 0 on failure */
+cupsGetDestMediaByName(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *media, /* I - Media name */
+ unsigned flags, /* I - Media matching flags */
+ cups_size_t *size) /* O - Media size information */
+{
+ pwg_media_t *pwg; /* PWG media info */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (size)
+ memset(size, 0, sizeof(cups_size_t));
+
+ if (!http || !dest || !dinfo || !media || !size)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Lookup the media size name...
+ */
+
+ if ((pwg = pwgMediaForPWG(media)) == NULL)
+ if ((pwg = pwgMediaForLegacy(media)) == NULL)
+ {
+ DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
+ return (0);
+ }
+
+ /*
+ * Lookup the size...
+ */
+
+ return (cups_get_media_db(http, dinfo, pwg, flags, size));
+}
+
+
+/*
+ * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
+ *
+ * "Width" and "length" are the dimensions in hundredths of millimeters.
+ * "Flags" provides some matching guidance (multiple flags can be combined):
+ *
+ * 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 2-sided printing,
+ * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
+ * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
+ * size amongst the "ready" media.
+ *
+ * The matching result (if any) is returned in the "cups_size_t" structure.
+ *
+ * Returns 1 when there is a match and 0 if there is not a match.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on match, 0 on failure */
+cupsGetDestMediaBySize(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ int width, /* I - Media width in hundredths of
+ * of millimeters */
+ int length, /* I - Media length in hundredths of
+ * of millimeters */
+ unsigned flags, /* I - Media matching flags */
+ cups_size_t *size) /* O - Media size information */
+{
+ pwg_media_t *pwg; /* PWG media info */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (size)
+ memset(size, 0, sizeof(cups_size_t));
+
+ if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Lookup the media size name...
+ */
+
+ if ((pwg = pwgMediaForSize(width, length)) == NULL)
+ {
+ DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
+ length));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
+ return (0);
+ }
+
+ /*
+ * Lookup the size...
+ */
+
+ return (cups_get_media_db(http, dinfo, pwg, flags, size));
+}
+
+
+/*
+ * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
+ * destination.
+ *
+ * The @code flags@ parameter determines the set of media sizes that are
+ * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
+ * the number of borderless sizes.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - Number of sizes */
+cupsGetDestMediaCount(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ unsigned flags) /* I - Media flags */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!http || !dest || !dinfo)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Load media list as needed...
+ */
+
+ if (flags & CUPS_MEDIA_FLAGS_READY)
+ cups_update_ready(http, dinfo);
+
+ if (!dinfo->cached_db || dinfo->cached_flags != flags)
+ cups_create_cached(http, dinfo, flags);
+
+ return (cupsArrayCount(dinfo->cached_db));
+}
+
+
+/*
+ * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
+ *
+ * The @code flags@ parameter determines which default size is returned. For
+ * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
+ * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsGetDestMediaDefault(
+ http_t *http, /* I - Connection to destination */
+ cups_dest_t *dest, /* I - Destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ unsigned flags, /* I - Media flags */
+ cups_size_t *size) /* O - Media size information */
+{
+ const char *media; /* Default media size */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (size)
+ memset(size, 0, sizeof(cups_size_t));
+
+ if (!http || !dest || !dinfo || !size)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Get the default media size, if any...
+ */
+
+ if ((media = cupsGetOption("media", dest->num_options,
+ dest->options)) == NULL)
+ media = "na_letter_8.5x11in";
+
+ if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size))
+ return (1);
+
+ if (strcmp(media, "na_letter_8.5x11in") &&
+ cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags,
+ size))
+ return (1);
+
+ if (strcmp(media, "iso_a4_210x297mm") &&
+ cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags,
+ size))
+ return (1);
+
+ if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
+ cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size))
+ return (1);
+
+ /*
+ * Fall back to the first matching media size...
+ */
+
+ return (cupsGetDestMediaByIndex(http, dest, dinfo, flags, 0, size));
+}
+
+
+/*
+ * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
+ */
+
+static void
+cups_add_dconstres(
+ cups_array_t *a, /* I - Array */
+ ipp_t *collection) /* I - Collection value */
+{
+ ipp_attribute_t *attr; /* Attribute */
+ _cups_dconstres_t *temp; /* Current constraint/resolver */
+
+
+ if ((attr = ippFindAttribute(collection, "resolver-name",
+ IPP_TAG_NAME)) == NULL)
+ return;
+
+ if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
+ return;
+
+ temp->name = attr->values[0].string.text;
+ temp->collection = collection;
+
+ cupsArrayAdd(a, temp);
+}
+
+
+/*
+ * 'cups_compare_dconstres()' - Compare to resolver entries.
+ */
+
+static int /* O - Result of comparison */
+cups_compare_dconstres(
+ _cups_dconstres_t *a, /* I - First resolver */
+ _cups_dconstres_t *b) /* I - Second resolver */
+{
+ return (strcmp(a->name, b->name));
+}
+
+
+/*
+ * 'cups_compare_media_db()' - Compare two media entries.
+ */
+
+static int /* O - Result of comparison */
+cups_compare_media_db(
+ _cups_media_db_t *a, /* I - First media entries */
+ _cups_media_db_t *b) /* I - Second media entries */
+{
+ int result; /* Result of comparison */
+
+
+ if ((result = a->width - b->width) == 0)
+ result = a->length - b->length;
+
+ return (result);
+}
+
+
+/*
+ * 'cups_copy_media_db()' - Copy a media entry.
+ */
+
+static _cups_media_db_t * /* O - New media entry */
+cups_copy_media_db(
+ _cups_media_db_t *mdb) /* I - Media entry to copy */
+{
+ _cups_media_db_t *temp; /* New media entry */
+
+
+ if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
+ return (NULL);
+
+ if (mdb->color)
+ temp->color = _cupsStrAlloc(mdb->color);
+ if (mdb->key)
+ temp->key = _cupsStrAlloc(mdb->key);
+ if (mdb->info)
+ temp->info = _cupsStrAlloc(mdb->info);
+ if (mdb->size_name)
+ temp->size_name = _cupsStrAlloc(mdb->size_name);
+ if (mdb->source)
+ temp->source = _cupsStrAlloc(mdb->source);
+ if (mdb->type)
+ temp->type = _cupsStrAlloc(mdb->type);
+
+ temp->width = mdb->width;
+ temp->length = mdb->length;
+ temp->bottom = mdb->bottom;
+ temp->left = mdb->left;
+ temp->right = mdb->right;
+ temp->top = mdb->top;
+
+ return (temp);
+}
+
+
+/*
+ * 'cups_create_cached()' - Create the media selection cache.
+ */
+
+static void
+cups_create_cached(http_t *http, /* I - Connection to destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ unsigned flags) /* I - Media selection flags */
+{
+ cups_array_t *db; /* Media database array to use */
+ _cups_media_db_t *mdb, /* Media database entry */
+ *first; /* First entry this size */
+
+
+ DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", http, dinfo, flags));
+
+ if (dinfo->cached_db)
+ cupsArrayDelete(dinfo->cached_db);
+
+ dinfo->cached_db = cupsArrayNew(NULL, NULL);
+ dinfo->cached_flags = flags;
+
+ if (flags & CUPS_MEDIA_FLAGS_READY)
+ {
+ DEBUG_puts("4cups_create_cached: ready media");
+
+ cups_update_ready(http, dinfo);
+ db = dinfo->ready_db;
+ }
+ else
+ {
+ DEBUG_puts("4cups_create_cached: supported media");
+
+ if (!dinfo->media_db)
+ cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
+
+ db = dinfo->media_db;
+ }
+
+ for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb;
+ mdb;
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ DEBUG_printf(("4cups_create_cached: %p key=\"%s\", type=\"%s\", %dx%d, B%d L%d R%d T%d", mdb, mdb->key, mdb->type, mdb->width, mdb->length, mdb->bottom, mdb->left, mdb->right, mdb->top));
+
+ if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
+ {
+ if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom)
+ {
+ DEBUG_printf(("4cups_create_cached: add %p", mdb));
+ cupsArrayAdd(dinfo->cached_db, mdb);
+ }
+ }
+ else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
+ {
+ if (first->width != mdb->width || first->length != mdb->length)
+ {
+ DEBUG_printf(("4cups_create_cached: add %p", first));
+ cupsArrayAdd(dinfo->cached_db, first);
+ first = mdb;
+ }
+ else if (mdb->left >= first->left && mdb->right >= first->right && mdb->top >= first->top && mdb->bottom >= first->bottom &&
+ (mdb->left != first->left || mdb->right != first->right || mdb->top != first->top || mdb->bottom != first->bottom))
+ first = mdb;
+ }
+ else
+ {
+ DEBUG_printf(("4cups_create_cached: add %p", mdb));
+ cupsArrayAdd(dinfo->cached_db, mdb);
+ }
+ }
+
+ if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
+ {
+ DEBUG_printf(("4cups_create_cached: add %p", first));
+ cupsArrayAdd(dinfo->cached_db, first);
+ }
+}
+
+
+/*
+ * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
+ */
+
+static void
+cups_create_constraints(
+ cups_dinfo_t *dinfo) /* I - Destination information */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* Attribute */
+ _ipp_value_t *val; /* Current value */
+
+
+ dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
+ (cups_afree_func_t)free);
+ dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
+ NULL, NULL, 0, NULL,
+ (cups_afree_func_t)free);
+
+ if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
+ cups_add_dconstres(dinfo->constraints, val->collection);
+ }
+
+ if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
+ cups_add_dconstres(dinfo->resolvers, val->collection);
+ }
+}
+
+
+/*
+ * 'cups_create_defaults()' - Create the -default option array.
+ *
+ * TODO: Need to support collection defaults...
+ */
+
+static void
+cups_create_defaults(
+ cups_dinfo_t *dinfo) /* I - Destination information */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ char name[IPP_MAX_NAME + 1],
+ /* Current name */
+ *nameptr, /* Pointer into current name */
+ value[2048]; /* Current value */
+
+
+ /*
+ * Iterate through the printer attributes looking for xxx-default and adding
+ * xxx=value to the defaults option array.
+ */
+
+ for (attr = ippFirstAttribute(dinfo->attrs);
+ attr;
+ attr = ippNextAttribute(dinfo->attrs))
+ {
+ if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
+ continue;
+
+ if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
+ continue; /* TODO: STR #4096 */
+
+ if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
+ strcmp(nameptr, "-default"))
+ continue;
+
+ strlcpy(name, attr->name, sizeof(name));
+ if ((nameptr = name + strlen(name) - 8) <= name ||
+ strcmp(nameptr, "-default"))
+ continue;
+
+ *nameptr = '\0';
+
+ if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
+ continue;
+
+ dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
+ &dinfo->defaults);
+ }
+}
+
+
+/*
+ * 'cups_create_media_db()' - Create the media database.
+ */
+
+static void
+cups_create_media_db(
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ unsigned flags) /* I - Media flags */
+{
+ int i; /* Looping var */
+ _ipp_value_t *val; /* Current value */
+ ipp_attribute_t *media_col_db, /* media-col-database */
+ *media_attr, /* media-xxx */
+ *x_dimension, /* x-dimension */
+ *y_dimension; /* y-dimension */
+ pwg_media_t *pwg; /* PWG media info */
+ cups_array_t *db; /* New media database array */
+ _cups_media_db_t mdb; /* Media entry */
+
+
+ db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
+ NULL, NULL, 0,
+ (cups_acopy_func_t)cups_copy_media_db,
+ (cups_afree_func_t)cups_free_media_db);
+
+ if (flags == CUPS_MEDIA_FLAGS_READY)
+ {
+ dinfo->ready_db = db;
+
+ media_col_db = ippFindAttribute(dinfo->ready_attrs, "media-col-ready",
+ IPP_TAG_BEGIN_COLLECTION);
+ media_attr = ippFindAttribute(dinfo->ready_attrs, "media-ready",
+ IPP_TAG_ZERO);
+ }
+ else
+ {
+ dinfo->media_db = db;
+ dinfo->min_size.width = INT_MAX;
+ dinfo->min_size.length = INT_MAX;
+ dinfo->max_size.width = 0;
+ dinfo->max_size.length = 0;
+
+ media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
+ IPP_TAG_BEGIN_COLLECTION);
+ media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
+ IPP_TAG_ZERO);
+ }
+
+ if (media_col_db)
+ {
+ _ipp_value_t *custom = NULL; /* Custom size range value */
+
+ for (i = media_col_db->num_values, val = media_col_db->values;
+ i > 0;
+ i --, val ++)
+ {
+ memset(&mdb, 0, sizeof(mdb));
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-size",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ ipp_t *media_size = media_attr->values[0].collection;
+ /* media-size collection value */
+
+ if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
+ IPP_TAG_INTEGER)) != NULL &&
+ (y_dimension = ippFindAttribute(media_size, "y-dimension",
+ IPP_TAG_INTEGER)) != NULL)
+ {
+ /*
+ * Fixed size...
+ */
+
+ mdb.width = x_dimension->values[0].integer;
+ mdb.length = y_dimension->values[0].integer;
+ }
+ else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
+ IPP_TAG_INTEGER)) != NULL &&
+ (y_dimension = ippFindAttribute(media_size, "y-dimension",
+ IPP_TAG_RANGE)) != NULL)
+ {
+ /*
+ * Roll limits...
+ */
+
+ mdb.width = x_dimension->values[0].integer;
+ mdb.length = y_dimension->values[0].range.upper;
+ }
+ else if (flags != CUPS_MEDIA_FLAGS_READY &&
+ (x_dimension = ippFindAttribute(media_size, "x-dimension",
+ IPP_TAG_RANGE)) != NULL &&
+ (y_dimension = ippFindAttribute(media_size, "y-dimension",
+ IPP_TAG_RANGE)) != NULL)
+ {
+ /*
+ * Custom size range; save this as the custom size value with default
+ * margins, then continue; we'll capture the real margins below...
+ */
+
+ custom = val;
+
+ dinfo->min_size.width = x_dimension->values[0].range.lower;
+ dinfo->min_size.length = y_dimension->values[0].range.lower;
+ dinfo->min_size.left =
+ dinfo->min_size.right = 635; /* Default 1/4" side margins */
+ dinfo->min_size.top =
+ dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */
+
+ dinfo->max_size.width = x_dimension->values[0].range.upper;
+ dinfo->max_size.length = y_dimension->values[0].range.upper;
+ dinfo->max_size.left =
+ dinfo->max_size.right = 635; /* Default 1/4" side margins */
+ dinfo->max_size.top =
+ dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */
+ continue;
+ }
+ }
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-color",
+ IPP_TAG_ZERO)) != NULL &&
+ (media_attr->value_tag == IPP_TAG_NAME ||
+ media_attr->value_tag == IPP_TAG_NAMELANG ||
+ media_attr->value_tag == IPP_TAG_KEYWORD))
+ mdb.color = media_attr->values[0].string.text;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-info",
+ IPP_TAG_TEXT)) != NULL)
+ mdb.info = media_attr->values[0].string.text;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-key",
+ IPP_TAG_ZERO)) != NULL &&
+ (media_attr->value_tag == IPP_TAG_NAME ||
+ media_attr->value_tag == IPP_TAG_NAMELANG ||
+ media_attr->value_tag == IPP_TAG_KEYWORD))
+ mdb.key = media_attr->values[0].string.text;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
+ IPP_TAG_ZERO)) != NULL &&
+ (media_attr->value_tag == IPP_TAG_NAME ||
+ media_attr->value_tag == IPP_TAG_NAMELANG ||
+ media_attr->value_tag == IPP_TAG_KEYWORD))
+ mdb.size_name = media_attr->values[0].string.text;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-source",
+ IPP_TAG_ZERO)) != NULL &&
+ (media_attr->value_tag == IPP_TAG_NAME ||
+ media_attr->value_tag == IPP_TAG_NAMELANG ||
+ media_attr->value_tag == IPP_TAG_KEYWORD))
+ mdb.source = media_attr->values[0].string.text;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-type",
+ IPP_TAG_ZERO)) != NULL &&
+ (media_attr->value_tag == IPP_TAG_NAME ||
+ media_attr->value_tag == IPP_TAG_NAMELANG ||
+ media_attr->value_tag == IPP_TAG_KEYWORD))
+ mdb.type = media_attr->values[0].string.text;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ mdb.bottom = media_attr->values[0].integer;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ mdb.left = media_attr->values[0].integer;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ mdb.right = media_attr->values[0].integer;
+
+ if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ mdb.top = media_attr->values[0].integer;
+
+ cupsArrayAdd(db, &mdb);
+ }
+
+ if (custom)
+ {
+ if ((media_attr = ippFindAttribute(custom->collection,
+ "media-bottom-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ {
+ dinfo->min_size.top =
+ dinfo->max_size.top = media_attr->values[0].integer;
+ }
+
+ if ((media_attr = ippFindAttribute(custom->collection,
+ "media-left-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ {
+ dinfo->min_size.left =
+ dinfo->max_size.left = media_attr->values[0].integer;
+ }
+
+ if ((media_attr = ippFindAttribute(custom->collection,
+ "media-right-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ {
+ dinfo->min_size.right =
+ dinfo->max_size.right = media_attr->values[0].integer;
+ }
+
+ if ((media_attr = ippFindAttribute(custom->collection,
+ "media-top-margin",
+ IPP_TAG_INTEGER)) != NULL)
+ {
+ dinfo->min_size.top =
+ dinfo->max_size.top = media_attr->values[0].integer;
+ }
+ }
+ }
+ else if (media_attr &&
+ (media_attr->value_tag == IPP_TAG_NAME ||
+ media_attr->value_tag == IPP_TAG_NAMELANG ||
+ media_attr->value_tag == IPP_TAG_KEYWORD))
+ {
+ memset(&mdb, 0, sizeof(mdb));
+
+ mdb.left =
+ mdb.right = 635; /* Default 1/4" side margins */
+ mdb.top =
+ mdb.bottom = 1270; /* Default 1/2" top/bottom margins */
+
+ for (i = media_attr->num_values, val = media_attr->values;
+ i > 0;
+ i --, val ++)
+ {
+ if ((pwg = pwgMediaForPWG(val->string.text)) == NULL)
+ if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL)
+ {
+ DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
+ val->string.text));
+ continue;
+ }
+
+ mdb.width = pwg->width;
+ mdb.length = pwg->length;
+
+ if (flags != CUPS_MEDIA_FLAGS_READY &&
+ !strncmp(val->string.text, "custom_min_", 11))
+ {
+ mdb.size_name = NULL;
+ dinfo->min_size = mdb;
+ }
+ else if (flags != CUPS_MEDIA_FLAGS_READY &&
+ !strncmp(val->string.text, "custom_max_", 11))
+ {
+ mdb.size_name = NULL;
+ dinfo->max_size = mdb;
+ }
+ else
+ {
+ mdb.size_name = val->string.text;
+
+ cupsArrayAdd(db, &mdb);
+ }
+ }
+ }
+}
+
+
+/*
+ * 'cups_free_media_cb()' - Free a media entry.
+ */
+
+static void
+cups_free_media_db(
+ _cups_media_db_t *mdb) /* I - Media entry to free */
+{
+ if (mdb->color)
+ _cupsStrFree(mdb->color);
+ if (mdb->key)
+ _cupsStrFree(mdb->key);
+ if (mdb->info)
+ _cupsStrFree(mdb->info);
+ if (mdb->size_name)
+ _cupsStrFree(mdb->size_name);
+ if (mdb->source)
+ _cupsStrFree(mdb->source);
+ if (mdb->type)
+ _cupsStrFree(mdb->type);
+
+ free(mdb);
+}
+
+
+/*
+ * 'cups_get_media_db()' - Lookup the media entry for a given size.
+ */
+
+static int /* O - 1 on match, 0 on failure */
+cups_get_media_db(http_t *http, /* I - Connection to destination */
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ pwg_media_t *pwg, /* I - PWG media info */
+ unsigned flags, /* I - Media matching flags */
+ cups_size_t *size) /* O - Media size/margin/name info */
+{
+ cups_array_t *db; /* Which media database to query */
+ _cups_media_db_t *mdb, /* Current media database entry */
+ *best = NULL, /* Best matching entry */
+ key; /* Search key */
+
+
+ /*
+ * Create the media database as needed...
+ */
+
+ if (flags & CUPS_MEDIA_FLAGS_READY)
+ {
+ cups_update_ready(http, dinfo);
+ db = dinfo->ready_db;
+ }
+ else
+ {
+ if (!dinfo->media_db)
+ cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
+
+ db = dinfo->media_db;
+ }
+
+ /*
+ * Find a match...
+ */
+
+ memset(&key, 0, sizeof(key));
+ key.width = pwg->width;
+ key.length = pwg->length;
+
+ if ((mdb = cupsArrayFind(db, &key)) != NULL)
+ {
+ /*
+ * Found an exact match, let's figure out the best margins for the flags
+ * supplied...
+ */
+
+ best = mdb;
+
+ if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
+ {
+ /*
+ * Look for the smallest margins...
+ */
+
+ if (best->left != 0 || best->right != 0 || best->top != 0 || best->bottom != 0)
+ {
+ for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
+ mdb && !cups_compare_media_db(mdb, &key);
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ if (mdb->left <= best->left && mdb->right <= best->right &&
+ mdb->top <= best->top && mdb->bottom <= best->bottom)
+ {
+ best = mdb;
+ if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
+ mdb->top == 0)
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we need an exact match, return no-match if the size is not
+ * borderless.
+ */
+
+ if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
+ (best->left || best->right || best->top || best->bottom))
+ return (0);
+ }
+ else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
+ {
+ /*
+ * Look for the largest margins...
+ */
+
+ for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
+ mdb && !cups_compare_media_db(mdb, &key);
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ if (mdb->left >= best->left && mdb->right >= best->right &&
+ mdb->top >= best->top && mdb->bottom >= best->bottom &&
+ (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
+ best = mdb;
+ }
+ }
+ else
+ {
+ /*
+ * Look for the smallest non-zero margins...
+ */
+
+ for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
+ mdb && !cups_compare_media_db(mdb, &key);
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
+ ((mdb->right > 0 && mdb->right <= best->right) || best->right == 0) &&
+ ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
+ ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || best->bottom == 0) &&
+ (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
+ best = mdb;
+ }
+ }
+ }
+ else if (flags & CUPS_MEDIA_FLAGS_EXACT)
+ {
+ /*
+ * See if we can do this as a custom size...
+ */
+
+ if (pwg->width < dinfo->min_size.width ||
+ pwg->width > dinfo->max_size.width ||
+ pwg->length < dinfo->min_size.length ||
+ pwg->length > dinfo->max_size.length)
+ return (0); /* Out of range */
+
+ if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
+ (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
+ dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
+ return (0); /* Not borderless */
+
+ key.size_name = (char *)pwg->pwg;
+ key.bottom = dinfo->min_size.bottom;
+ key.left = dinfo->min_size.left;
+ key.right = dinfo->min_size.right;
+ key.top = dinfo->min_size.top;
+
+ best = &key;
+ }
+ else if (pwg->width >= dinfo->min_size.width &&
+ pwg->width <= dinfo->max_size.width &&
+ pwg->length >= dinfo->min_size.length &&
+ pwg->length <= dinfo->max_size.length)
+ {
+ /*
+ * Map to custom size...
+ */
+
+ key.size_name = (char *)pwg->pwg;
+ key.bottom = dinfo->min_size.bottom;
+ key.left = dinfo->min_size.left;
+ key.right = dinfo->min_size.right;
+ key.top = dinfo->min_size.top;
+
+ best = &key;
+ }
+ else
+ {
+ /*
+ * Find a close size...
+ */
+
+ for (mdb = (_cups_media_db_t *)cupsArrayFirst(db);
+ mdb;
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ if (cups_is_close_media_db(mdb, &key))
+ break;
+
+ if (!mdb)
+ return (0);
+
+ best = mdb;
+
+ if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
+ {
+ /*
+ * Look for the smallest margins...
+ */
+
+ if (best->left != 0 || best->right != 0 || best->top != 0 ||
+ best->bottom != 0)
+ {
+ for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
+ mdb && cups_is_close_media_db(mdb, &key);
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ if (mdb->left <= best->left && mdb->right <= best->right &&
+ mdb->top <= best->top && mdb->bottom <= best->bottom &&
+ (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
+ {
+ best = mdb;
+ if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
+ mdb->top == 0)
+ break;
+ }
+ }
+ }
+ }
+ else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
+ {
+ /*
+ * Look for the largest margins...
+ */
+
+ for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
+ mdb && cups_is_close_media_db(mdb, &key);
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ if (mdb->left >= best->left && mdb->right >= best->right &&
+ mdb->top >= best->top && mdb->bottom >= best->bottom &&
+ (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
+ best = mdb;
+ }
+ }
+ else
+ {
+ /*
+ * Look for the smallest non-zero margins...
+ */
+
+ for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
+ mdb && cups_is_close_media_db(mdb, &key);
+ mdb = (_cups_media_db_t *)cupsArrayNext(db))
+ {
+ if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
+ ((mdb->right > 0 && mdb->right <= best->right) ||
+ best->right == 0) &&
+ ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
+ ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
+ best->bottom == 0) &&
+ (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
+ best = mdb;
+ }
+ }
+ }
+
+ if (best)
+ {
+ /*
+ * Return the matching size...
+ */
+
+ if (best->size_name)
+ strlcpy(size->media, best->size_name, sizeof(size->media));
+ else if (best->key)
+ strlcpy(size->media, best->key, sizeof(size->media));
+ else
+ strlcpy(size->media, pwg->pwg, sizeof(size->media));
+
+ size->width = best->width;
+ size->length = best->length;
+ size->bottom = best->bottom;
+ size->left = best->left;
+ size->right = best->right;
+ size->top = best->top;
+
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'cups_is_close_media_db()' - Compare two media entries to see if they are
+ * close to the same size.
+ *
+ * Currently we use 5 points (from PostScript) as the matching range...
+ */
+
+static int /* O - 1 if the sizes are close */
+cups_is_close_media_db(
+ _cups_media_db_t *a, /* I - First media entries */
+ _cups_media_db_t *b) /* I - Second media entries */
+{
+ int dwidth, /* Difference in width */
+ dlength; /* Difference in length */
+
+
+ dwidth = a->width - b->width;
+ dlength = a->length - b->length;
+
+ return (dwidth >= -176 && dwidth <= 176 &&
+ dlength >= -176 && dlength <= 176);
+}
+
+
+/*
+ * 'cups_test_constraints()' - Test constraints.
+ *
+ * TODO: STR #4096 - Need to properly support media-col contraints...
+ */
+
+static cups_array_t * /* O - Active constraints */
+cups_test_constraints(
+ cups_dinfo_t *dinfo, /* I - Destination information */
+ const char *new_option, /* I - Newly selected option */
+ const char *new_value, /* I - Newly selected value */
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ int *num_conflicts, /* O - Number of conflicting options */
+ cups_option_t **conflicts) /* O - Conflicting options */
+{
+ int i, /* Looping var */
+ match; /* Value matches? */
+ int num_matching; /* Number of matching options */
+ cups_option_t *matching; /* Matching options */
+ _cups_dconstres_t *c; /* Current constraint */
+ cups_array_t *active = NULL; /* Active constraints */
+ ipp_attribute_t *attr; /* Current attribute */
+ _ipp_value_t *attrval; /* Current attribute value */
+ const char *value; /* Current value */
+ char temp[1024]; /* Temporary string */
+ int int_value; /* Integer value */
+ int xres_value, /* Horizontal resolution */
+ yres_value; /* Vertical resolution */
+ ipp_res_t units_value; /* Resolution units */
+
+
+ for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
+ c;
+ c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
+ {
+ num_matching = 0;
+ matching = NULL;
+
+ for (attr = ippFirstAttribute(c->collection);
+ attr;
+ attr = ippNextAttribute(c->collection))
+ {
+ if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
+ break; /* TODO: STR #4096 */
+
+ /*
+ * Get the value for the current attribute in the constraint...
+ */
+
+ if (new_option && new_value && !strcmp(attr->name, new_option))
+ value = new_value;
+ else if ((value = cupsGetOption(attr->name, num_options,
+ options)) == NULL)
+ value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
+
+ if (!value)
+ {
+ /*
+ * Not set so this constraint does not apply...
+ */
+
+ break;
+ }
+
+ match = 0;
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ int_value = atoi(value);
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->integer == int_value)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ int_value = !strcmp(value, "true");
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->boolean == int_value)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ int_value = atoi(value);
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (int_value >= attrval->range.lower &&
+ int_value <= attrval->range.upper)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
+ {
+ if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
+ break;
+
+ yres_value = xres_value;
+ }
+
+ if (!strcmp(temp, "dpi"))
+ units_value = IPP_RES_PER_INCH;
+ else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
+ units_value = IPP_RES_PER_CM;
+ else
+ break;
+
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (attrval->resolution.xres == xres_value &&
+ attrval->resolution.yres == yres_value &&
+ attrval->resolution.units == units_value)
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_MIMETYPE :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ for (i = attr->num_values, attrval = attr->values;
+ i > 0;
+ i --, attrval ++)
+ {
+ if (!strcmp(attrval->string.text, value))
+ {
+ match = 1;
+ break;
+ }
+ }
+ break;
+
+ default :
+ break;
+ }
+
+ if (!match)
+ break;
+
+ num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
+ }
+
+ if (!attr)
+ {
+ if (!active)
+ active = cupsArrayNew(NULL, NULL);
+
+ cupsArrayAdd(active, c);
+
+ if (num_conflicts && conflicts)
+ {
+ cups_option_t *moption; /* Matching option */
+
+ for (i = num_matching, moption = matching; i > 0; i --, moption ++)
+ *num_conflicts = cupsAddOption(moption->name, moption->value,
+ *num_conflicts, conflicts);
+ }
+ }
+
+ cupsFreeOptions(num_matching, matching);
+ }
+
+ return (active);
+}
+
+
+/*
+ * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
+ */
+
+static void
+cups_update_ready(http_t *http, /* I - Connection to destination */
+ cups_dinfo_t *dinfo) /* I - Destination information */
+{
+ ipp_t *request; /* Get-Printer-Attributes request */
+ static const char * const pattrs[] = /* Printer attributes we want */
+ {
+ "finishings-col-ready",
+ "finishings-ready",
+ "job-finishings-col-ready",
+ "job-finishings-ready",
+ "media-col-ready",
+ "media-ready"
+ };
+
+
+ /*
+ * Don't update more than once every 30 seconds...
+ */
+
+ if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL)
+ return;
+
+ /*
+ * Free any previous results...
+ */
+
+ if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY)
+ {
+ cupsArrayDelete(dinfo->cached_db);
+ dinfo->cached_db = NULL;
+ dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT;
+ }
+
+ ippDelete(dinfo->ready_attrs);
+ dinfo->ready_attrs = NULL;
+
+ cupsArrayDelete(dinfo->ready_db);
+ dinfo->ready_db = NULL;
+
+ /*
+ * Query the xxx-ready values...
+ */
+
+ request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+ ippSetVersion(request, dinfo->version / 10, dinfo->version % 10);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ dinfo->uri);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+ ippAddStrings(request, IPP_TAG_OPERATION,
+ IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "requested-attributes",
+ (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+ dinfo->ready_attrs = cupsDoRequest(http, request, dinfo->resource);
+
+ /*
+ * Update the ready media database...
+ */
+
+ cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_READY);
+
+ /*
+ * Update last lookup time and return...
+ */
+
+ dinfo->ready_time = time(NULL);
+}
+
+
+/*
+ * End of "$Id: dest-options.c 11883 2014-05-16 21:04:07Z msweet $".
+ */
diff --git a/cups/libs/cups/dest.c b/cups/libs/cups/dest.c
new file mode 100644
index 000000000..7b30340a5
--- /dev/null
+++ b/cups/libs/cups/dest.c
@@ -0,0 +1,3924 @@
+/*
+ * "$Id: dest.c 11688 2014-03-05 21:11:32Z msweet $"
+ *
+ * User-defined destination (and option) support for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsAddDest() - Add a destination to the list of
+ * destinations.
+ * _cupsAppleCopyDefaultPaperID() - Get the default paper ID.
+ * _cupsAppleCopyDefaultPrinter() - Get the default printer at this location.
+ * _cupsAppleGetUseLastPrinter() - Get whether to use the last used printer.
+ * _cupsAppleSetDefaultPaperID() - Set the default paper id.
+ * _cupsAppleSetDefaultPrinter() - Set the default printer for this
+ * location.
+ * _cupsAppleSetUseLastPrinter() - Set whether to use the last used printer.
+ * cupsConnectDest() - Connect to the server for a destination.
+ * cupsConnectDestBlock() - Connect to the server for a destination.
+ * cupsCopyDest() - Copy a destination.
+ * cupsEnumDests() - Enumerate available destinations with a
+ * callback function.
+ * cupsEnumDestsBlock() - Enumerate available destinations with a
+ * block.
+ * cupsFreeDests() - Free the memory used by the list of
+ * destinations.
+ * cupsGetDest() - Get the named destination from the list.
+ * _cupsGetDestResource() - Get the resource path and URI for a
+ * destination.
+ * _cupsGetDests() - Get destinations from a server.
+ * cupsGetDests() - Get the list of destinations from the
+ * default server.
+ * cupsGetDests2() - Get the list of destinations from the
+ * specified server.
+ * cupsGetNamedDest() - Get options for the named destination.
+ * cupsRemoveDest() - Remove a destination from the destination
+ * list.
+ * cupsSetDefaultDest() - Set the default destination.
+ * cupsSetDests() - Save the list of destinations for the
+ * default server.
+ * cupsSetDests2() - Save the list of destinations for the
+ * specified server.
+ * _cupsUserDefault() - Get the user default printer from
+ * environment variables and location
+ * information.
+ * appleCopyLocations() - Copy the location history array.
+ * appleCopyNetwork() - Get the network ID for the current
+ * location.
+ * appleGetPaperSize() - Get the default paper size.
+ * appleGetPrinter() - Get a printer from the history array.
+ * cups_add_dest() - Add a destination to the array.
+ * cups_block_cb() - Enumeration callback for block API.
+ * cups_compare_dests() - Compare two destinations.
+ * cups_dnssd_browse_cb() - Browse for printers.
+ * cups_dnssd_browse_cb() - Browse for printers.
+ * cups_dnssd_client_cb() - Avahi client callback function.
+ * cups_dnssd_compare_device() - Compare two devices.
+ * cups_dnssd_free_device() - Free the memory used by a device.
+ * cups_dnssd_get_device() - Lookup a device and create it as needed.
+ * cups_dnssd_local_cb() - Browse for local printers.
+ * cups_dnssd_poll_cb() - Wait for input on the specified file
+ * descriptors.
+ * cups_dnssd_query_cb() - Process query data.
+ * cups_dnssd_resolve() - Resolve a Bonjour printer URI.
+ * cups_dnssd_resolve_cb() - See if we should continue resolving.
+ * cups_dnssd_unquote() - Unquote a name string.
+ * cups_find_dest() - Find a destination using a binary search.
+ * cups_get_default() - Get the default destination from an
+ * lpoptions file.
+ * cups_get_dests() - Get destinations from a file.
+ * cups_make_string() - Make a comma-separated string of values
+ * from an IPP attribute.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <sys/stat.h>
+
+#ifdef HAVE_NOTIFY_H
+# include <notify.h>
+#endif /* HAVE_NOTIFY_H */
+
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif /* HAVE_POLL */
+
+#ifdef HAVE_DNSSD
+# include <dns_sd.h>
+#endif /* HAVE_DNSSD */
+
+#ifdef HAVE_AVAHI
+# include <avahi-client/client.h>
+# include <avahi-client/lookup.h>
+# include <avahi-common/simple-watch.h>
+# include <avahi-common/domain.h>
+# include <avahi-common/error.h>
+# include <avahi-common/malloc.h>
+#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
+#endif /* HAVE_AVAHI */
+
+
+/*
+ * Constants...
+ */
+
+#ifdef __APPLE__
+# include <SystemConfiguration/SystemConfiguration.h>
+# define kCUPSPrintingPrefs CFSTR("org.cups.PrintingPrefs")
+# define kDefaultPaperIDKey CFSTR("DefaultPaperID")
+# define kLastUsedPrintersKey CFSTR("LastUsedPrinters")
+# define kLocationNetworkKey CFSTR("Network")
+# define kLocationPrinterIDKey CFSTR("PrinterID")
+# define kUseLastPrinter CFSTR("UseLastPrinter")
+#endif /* __APPLE__ */
+
+
+/*
+ * Types...
+ */
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+typedef enum _cups_dnssd_state_e /* Enumerated device state */
+{
+ _CUPS_DNSSD_NEW,
+ _CUPS_DNSSD_QUERY,
+ _CUPS_DNSSD_PENDING,
+ _CUPS_DNSSD_ACTIVE,
+ _CUPS_DNSSD_LOCAL,
+ _CUPS_DNSSD_INCOMPATIBLE,
+ _CUPS_DNSSD_ERROR
+} _cups_dnssd_state_t;
+
+typedef struct _cups_dnssd_data_s /* Enumeration data */
+{
+# ifdef HAVE_DNSSD
+ DNSServiceRef main_ref; /* Main service reference */
+# else /* HAVE_AVAHI */
+ AvahiSimplePoll *simple_poll; /* Polling interface */
+ AvahiClient *client; /* Client information */
+ int got_data; /* Did we get data? */
+# endif /* HAVE_DNSSD */
+ cups_dest_cb_t cb; /* Callback */
+ void *user_data; /* User data pointer */
+ cups_ptype_t type, /* Printer type filter */
+ mask; /* Printer type mask */
+ cups_array_t *devices; /* Devices found so far */
+} _cups_dnssd_data_t;
+
+typedef struct _cups_dnssd_device_s /* Enumerated device */
+{
+ _cups_dnssd_state_t state; /* State of device listing */
+# ifdef HAVE_DNSSD
+ DNSServiceRef ref; /* Service reference for query */
+# else /* HAVE_AVAHI */
+ AvahiRecordBrowser *ref; /* Browser for query */
+# endif /* HAVE_DNSSD */
+ char *domain, /* Domain name */
+ *fullName, /* Full name */
+ *regtype; /* Registration type */
+ cups_ptype_t type; /* Device registration type */
+ cups_dest_t dest; /* Destination record */
+} _cups_dnssd_device_t;
+
+typedef struct _cups_dnssd_resolve_s /* Data for resolving URI */
+{
+ int *cancel; /* Pointer to "cancel" variable */
+ struct timeval end_time; /* Ending time */
+} _cups_dnssd_resolve_t;
+#endif /* HAVE_DNSSD */
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef __APPLE__
+static CFArrayRef appleCopyLocations(void);
+static CFStringRef appleCopyNetwork(void);
+static char *appleGetPaperSize(char *name, int namesize);
+static CFStringRef appleGetPrinter(CFArrayRef locations,
+ CFStringRef network, CFIndex *locindex);
+#endif /* __APPLE__ */
+static cups_dest_t *cups_add_dest(const char *name, const char *instance,
+ int *num_dests, cups_dest_t **dests);
+#ifdef __BLOCKS__
+static int cups_block_cb(cups_dest_block_t block, unsigned flags,
+ cups_dest_t *dest);
+#endif /* __BLOCKS__ */
+static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b);
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+# ifdef HAVE_DNSSD
+static void cups_dnssd_browse_cb(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ void *context);
+# else /* HAVE_AVAHI */
+static void cups_dnssd_browse_cb(AvahiServiceBrowser *browser,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ AvahiLookupResultFlags flags,
+ void *context);
+static void cups_dnssd_client_cb(AvahiClient *client,
+ AvahiClientState state,
+ void *context);
+# endif /* HAVE_DNSSD */
+static int cups_dnssd_compare_devices(_cups_dnssd_device_t *a,
+ _cups_dnssd_device_t *b);
+static void cups_dnssd_free_device(_cups_dnssd_device_t *device,
+ _cups_dnssd_data_t *data);
+static _cups_dnssd_device_t *
+ cups_dnssd_get_device(_cups_dnssd_data_t *data,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain);
+# ifdef HAVE_DNSSD
+static void cups_dnssd_local_cb(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ void *context);
+static void cups_dnssd_query_cb(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullName,
+ uint16_t rrtype, uint16_t rrclass,
+ uint16_t rdlen, const void *rdata,
+ uint32_t ttl, void *context);
+# else /* HAVE_AVAHI */
+static int cups_dnssd_poll_cb(struct pollfd *pollfds,
+ unsigned int num_pollfds,
+ int timeout, void *context);
+static void cups_dnssd_query_cb(AvahiRecordBrowser *browser,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name, uint16_t rrclass,
+ uint16_t rrtype, const void *rdata,
+ size_t rdlen,
+ AvahiLookupResultFlags flags,
+ void *context);
+# endif /* HAVE_DNSSD */
+static const char *cups_dnssd_resolve(cups_dest_t *dest, const char *uri,
+ int msec, int *cancel,
+ cups_dest_cb_t cb, void *user_data);
+static int cups_dnssd_resolve_cb(void *context);
+static void cups_dnssd_unquote(char *dst, const char *src,
+ size_t dstsize);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+static int cups_find_dest(const char *name, const char *instance,
+ int num_dests, cups_dest_t *dests, int prev,
+ int *rdiff);
+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,
+ const char *match_inst, int user_default_set,
+ int num_dests, cups_dest_t **dests);
+static char *cups_make_string(ipp_attribute_t *attr, char *buffer,
+ size_t bufsize);
+
+
+/*
+ * 'cupsAddDest()' - Add a destination to the list of destinations.
+ *
+ * This function cannot be used to add a new class or printer queue,
+ * it only adds a new container of saved options for the named
+ * destination or instance.
+ *
+ * If the named destination already exists, the destination list is
+ * returned unchanged. Adding a new instance of a destination creates
+ * a copy of that destination's options.
+ *
+ * Use the @link cupsSaveDests@ function to save the updated list of
+ * destinations to the user's lpoptions file.
+ */
+
+int /* O - New number of destinations */
+cupsAddDest(const char *name, /* I - Destination name */
+ const char *instance, /* I - Instance name or @code NULL@ for none/primary */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t **dests) /* IO - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Destination pointer */
+ cups_dest_t *parent = NULL; /* Parent destination */
+ cups_option_t *doption, /* Current destination option */
+ *poption; /* Current parent option */
+
+
+ if (!name || !dests)
+ return (0);
+
+ if (!cupsGetDest(name, instance, num_dests, *dests))
+ {
+ if (instance && !cupsGetDest(name, NULL, num_dests, *dests))
+ return (num_dests);
+
+ if ((dest = cups_add_dest(name, instance, &num_dests, dests)) == NULL)
+ return (num_dests);
+
+ /*
+ * Find the base dest again now the array has been realloc'd.
+ */
+
+ parent = cupsGetDest(name, NULL, num_dests, *dests);
+
+ if (instance && parent && parent->num_options > 0)
+ {
+ /*
+ * Copy options from parent...
+ */
+
+ dest->options = calloc(sizeof(cups_option_t), parent->num_options);
+
+ if (dest->options)
+ {
+ dest->num_options = parent->num_options;
+
+ for (i = dest->num_options, doption = dest->options,
+ poption = parent->options;
+ i > 0;
+ i --, doption ++, poption ++)
+ {
+ doption->name = _cupsStrRetain(poption->name);
+ doption->value = _cupsStrRetain(poption->value);
+ }
+ }
+ }
+ }
+
+ return (num_dests);
+}
+
+
+#ifdef __APPLE__
+/*
+ * '_cupsAppleCopyDefaultPaperID()' - Get the default paper ID.
+ */
+
+CFStringRef /* O - Default paper ID */
+_cupsAppleCopyDefaultPaperID(void)
+{
+ return (CFPreferencesCopyAppValue(kDefaultPaperIDKey,
+ kCUPSPrintingPrefs));
+}
+
+
+/*
+ * '_cupsAppleCopyDefaultPrinter()' - Get the default printer at this location.
+ */
+
+CFStringRef /* O - Default printer name */
+_cupsAppleCopyDefaultPrinter(void)
+{
+ CFStringRef network; /* Network location */
+ CFArrayRef locations; /* Location array */
+ CFStringRef locprinter; /* Current printer */
+
+
+ /*
+ * Use location-based defaults only if "use last printer" is selected in the
+ * system preferences...
+ */
+
+ if (!_cupsAppleGetUseLastPrinter())
+ {
+ DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Not using last printer as "
+ "default.");
+ return (NULL);
+ }
+
+ /*
+ * Get the current location...
+ */
+
+ if ((network = appleCopyNetwork()) == NULL)
+ {
+ DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Unable to get current "
+ "network.");
+ return (NULL);
+ }
+
+ /*
+ * Lookup the network in the preferences...
+ */
+
+ if ((locations = appleCopyLocations()) == NULL)
+ {
+ /*
+ * Missing or bad location array, so no location-based default...
+ */
+
+ DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Missing or bad last used "
+ "printer array.");
+
+ CFRelease(network);
+
+ return (NULL);
+ }
+
+ DEBUG_printf(("1_cupsAppleCopyDefaultPrinter: Got locations, %d entries.",
+ (int)CFArrayGetCount(locations)));
+
+ if ((locprinter = appleGetPrinter(locations, network, NULL)) != NULL)
+ CFRetain(locprinter);
+
+ CFRelease(network);
+ CFRelease(locations);
+
+ return (locprinter);
+}
+
+
+/*
+ * '_cupsAppleGetUseLastPrinter()' - Get whether to use the last used printer.
+ */
+
+int /* O - 1 to use last printer, 0 otherwise */
+_cupsAppleGetUseLastPrinter(void)
+{
+ Boolean uselast, /* Use last printer preference value */
+ uselast_set; /* Valid is set? */
+
+
+ if (getenv("CUPS_DISABLE_APPLE_DEFAULT"))
+ return (0);
+
+ uselast = CFPreferencesGetAppBooleanValue(kUseLastPrinter,
+ kCUPSPrintingPrefs,
+ &uselast_set);
+ if (!uselast_set)
+ return (1);
+ else
+ return (uselast);
+}
+
+
+/*
+ * '_cupsAppleSetDefaultPaperID()' - Set the default paper id.
+ */
+
+void
+_cupsAppleSetDefaultPaperID(
+ CFStringRef name) /* I - New paper ID */
+{
+ CFPreferencesSetAppValue(kDefaultPaperIDKey, name, kCUPSPrintingPrefs);
+ CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
+ notify_post("com.apple.printerPrefsChange");
+}
+
+
+/*
+ * '_cupsAppleSetDefaultPrinter()' - Set the default printer for this location.
+ */
+
+void
+_cupsAppleSetDefaultPrinter(
+ CFStringRef name) /* I - Default printer/class name */
+{
+ CFStringRef network; /* Current network */
+ CFArrayRef locations; /* Old locations array */
+ CFIndex locindex; /* Index in locations array */
+ CFStringRef locprinter; /* Current printer */
+ CFMutableArrayRef newlocations; /* New locations array */
+ CFMutableDictionaryRef newlocation; /* New location */
+
+
+ /*
+ * Get the current location...
+ */
+
+ if ((network = appleCopyNetwork()) == NULL)
+ {
+ DEBUG_puts("1_cupsAppleSetDefaultPrinter: Unable to get current network...");
+ return;
+ }
+
+ /*
+ * Lookup the network in the preferences...
+ */
+
+ if ((locations = appleCopyLocations()) != NULL)
+ locprinter = appleGetPrinter(locations, network, &locindex);
+ else
+ {
+ locprinter = NULL;
+ locindex = -1;
+ }
+
+ if (!locprinter || CFStringCompare(locprinter, name, 0) != kCFCompareEqualTo)
+ {
+ /*
+ * Need to change the locations array...
+ */
+
+ if (locations)
+ {
+ newlocations = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
+ locations);
+
+ if (locprinter)
+ CFArrayRemoveValueAtIndex(newlocations, locindex);
+ }
+ else
+ newlocations = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeArrayCallBacks);
+
+ newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (newlocation && newlocations)
+ {
+ /*
+ * Put the new location at the front of the array...
+ */
+
+ CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
+ CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, name);
+ CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
+
+ /*
+ * Limit the number of locations to 10...
+ */
+
+ while (CFArrayGetCount(newlocations) > 10)
+ CFArrayRemoveValueAtIndex(newlocations, 10);
+
+ /*
+ * Push the changes out...
+ */
+
+ CFPreferencesSetAppValue(kLastUsedPrintersKey, newlocations,
+ kCUPSPrintingPrefs);
+ CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
+ notify_post("com.apple.printerPrefsChange");
+ }
+
+ if (newlocations)
+ CFRelease(newlocations);
+
+ if (newlocation)
+ CFRelease(newlocation);
+ }
+
+ if (locations)
+ CFRelease(locations);
+
+ CFRelease(network);
+}
+
+
+/*
+ * '_cupsAppleSetUseLastPrinter()' - Set whether to use the last used printer.
+ */
+
+void
+_cupsAppleSetUseLastPrinter(
+ int uselast) /* O - 1 to use last printer, 0 otherwise */
+{
+ CFPreferencesSetAppValue(kUseLastPrinter,
+ uselast ? kCFBooleanTrue : kCFBooleanFalse,
+ kCUPSPrintingPrefs);
+ CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
+ notify_post("com.apple.printerPrefsChange");
+}
+#endif /* __APPLE__ */
+
+
+/*
+ * 'cupsConnectDest()' - Connect to the server for a 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.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+http_t * /* O - Connection to server or @code NULL@ */
+cupsConnectDest(
+ cups_dest_t *dest, /* I - Destination */
+ unsigned flags, /* I - Connection flags */
+ int msec, /* I - Timeout in milliseconds */
+ int *cancel, /* I - Pointer to "cancel" variable */
+ char *resource, /* I - Resource buffer */
+ size_t resourcesize, /* I - Size of resource buffer */
+ cups_dest_cb_t cb, /* I - Callback function */
+ void *user_data) /* I - User data pointer */
+{
+ const char *uri; /* Printer URI */
+ char scheme[32], /* URI scheme */
+ userpass[256], /* Username and password (unused) */
+ hostname[256], /* Hostname */
+ tempresource[1024]; /* Temporary resource buffer */
+ int port; /* Port number */
+ char portstr[16]; /* Port number string */
+ http_encryption_t encryption; /* Encryption to use */
+ http_addrlist_t *addrlist; /* Address list for server */
+ http_t *http; /* Connection to server */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!dest)
+ {
+ if (resource)
+ *resource = '\0';
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (NULL);
+ }
+
+ if (!resource || resourcesize < 1)
+ {
+ resource = tempresource;
+ resourcesize = sizeof(tempresource);
+ }
+
+ /*
+ * Grab the printer URI...
+ */
+
+ if ((uri = cupsGetOption("printer-uri-supported", dest->num_options,
+ dest->options)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR,
+ dest);
+
+ return (NULL);
+ }
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ if (strstr(uri, "._tcp"))
+ {
+ if ((uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb,
+ user_data)) == NULL)
+ return (NULL);
+ }
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
+ userpass, sizeof(userpass), hostname, sizeof(hostname),
+ &port, resource, resourcesize) < HTTP_URI_STATUS_OK)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer URI."), 1);
+
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR,
+ dest);
+
+ return (NULL);
+ }
+
+ /*
+ * Lookup the address for the server...
+ */
+
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_RESOLVING,
+ dest);
+
+ snprintf(portstr, sizeof(portstr), "%d", port);
+
+ if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portstr)) == NULL)
+ {
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR,
+ dest);
+
+ return (NULL);
+ }
+
+ if (cancel && *cancel)
+ {
+ httpAddrFreeList(addrlist);
+
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_CANCELED,
+ dest);
+
+ return (NULL);
+ }
+
+ /*
+ * Create the HTTP object pointing to the server referenced by the URI...
+ */
+
+ if (!strcmp(scheme, "ipps") || port == 443)
+ encryption = HTTP_ENCRYPTION_ALWAYS;
+ else
+ encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+
+ http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, encryption, 1, 0,
+ NULL);
+
+ /*
+ * Connect if requested...
+ */
+
+ if (flags & CUPS_DEST_FLAGS_UNCONNECTED)
+ {
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED, dest);
+ }
+ else
+ {
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_CONNECTING,
+ dest);
+
+ if (!httpReconnect2(http, msec, cancel) && cb)
+ {
+ if (cancel && *cancel)
+ (*cb)(user_data,
+ CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_CONNECTING, dest);
+ else
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR,
+ dest);
+ }
+ else if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_NONE, dest);
+ }
+
+ return (http);
+}
+
+
+#ifdef __BLOCKS__
+/*
+ * 'cupsConnectDestBlock()' - Connect to the server for a 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.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+http_t * /* O - Connection to server or @code NULL@ */
+cupsConnectDestBlock(
+ cups_dest_t *dest, /* I - Destination */
+ unsigned flags, /* I - Connection flags */
+ int msec, /* I - Timeout in milliseconds */
+ int *cancel, /* I - Pointer to "cancel" variable */
+ char *resource, /* I - Resource buffer */
+ size_t resourcesize, /* I - Size of resource buffer */
+ cups_dest_block_t block) /* I - Callback block */
+{
+ return (cupsConnectDest(dest, flags, msec, cancel, resource, resourcesize,
+ (cups_dest_cb_t)cups_block_cb, (void *)block));
+}
+#endif /* __BLOCKS__ */
+
+
+/*
+ * 'cupsCopyDest()' - Copy a destination.
+ *
+ * Make a copy of the destination to an array of destinations (or just a single
+ * copy) - for use with the cupsEnumDests* functions. The caller is responsible
+ * for calling cupsFreeDests() on the returned object(s).
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int
+cupsCopyDest(cups_dest_t *dest,
+ int num_dests,
+ cups_dest_t **dests)
+{
+ int i; /* Looping var */
+ cups_dest_t *new_dest; /* New destination pointer */
+ cups_option_t *new_option, /* Current destination option */
+ *option; /* Current parent option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!dest || num_dests < 0 || !dests)
+ return (num_dests);
+
+ /*
+ * See if the destination already exists...
+ */
+
+ if ((new_dest = cupsGetDest(dest->name, dest->instance, num_dests,
+ *dests)) != NULL)
+ {
+ /*
+ * Protect against copying destination to itself...
+ */
+
+ if (new_dest == dest)
+ return (num_dests);
+
+ /*
+ * Otherwise, free the options...
+ */
+
+ cupsFreeOptions(new_dest->num_options, new_dest->options);
+
+ new_dest->num_options = 0;
+ new_dest->options = NULL;
+ }
+ else
+ new_dest = cups_add_dest(dest->name, dest->instance, &num_dests, dests);
+
+ if (new_dest)
+ {
+ if ((new_dest->options = calloc(sizeof(cups_option_t),
+ dest->num_options)) == NULL)
+ return (cupsRemoveDest(dest->name, dest->instance, num_dests, dests));
+
+ new_dest->num_options = dest->num_options;
+
+ for (i = dest->num_options, option = dest->options,
+ new_option = new_dest->options;
+ i > 0;
+ i --, option ++, new_option ++)
+ {
+ new_option->name = _cupsStrRetain(option->name);
+ new_option->value = _cupsStrRetain(option->value);
+ }
+ }
+
+ return (num_dests);
+}
+
+
+/*
+ * '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.
+ *
+ * Enumeration happens on the current thread and does not return until all
+ * destinations have been enumerated or the callback function returns 0.
+ *
+ * @since CUPS 1.6/OS X 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 */
+{
+ 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);
+
+ 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);
+}
+
+
+# ifdef __BLOCKS__
+/*
+ * '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
+ * continue enumeration or 0 to stop.
+ *
+ * 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/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsEnumDestsBlock(
+ unsigned flags, /* I - Enumeration flags */
+ int timeout, /* I - Timeout in milliseconds, 0 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_block_t block) /* I - Block */
+{
+ return (cupsEnumDests(flags, timeout, cancel, type, mask,
+ (cups_dest_cb_t)cups_block_cb, (void *)block));
+}
+# endif /* __BLOCKS__ */
+
+
+/*
+ * 'cupsFreeDests()' - Free the memory used by the list of destinations.
+ */
+
+void
+cupsFreeDests(int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Current destination */
+
+
+ if (num_dests == 0 || dests == NULL)
+ return;
+
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ {
+ _cupsStrFree(dest->name);
+ _cupsStrFree(dest->instance);
+
+ cupsFreeOptions(dest->num_options, dest->options);
+ }
+
+ free(dests);
+}
+
+
+/*
+ * 'cupsGetDest()' - Get the named destination from the list.
+ *
+ * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
+ * list of supported destinations for the current user.
+ */
+
+cups_dest_t * /* O - Destination pointer or @code NULL@ */
+cupsGetDest(const char *name, /* I - Destination name or @code NULL@ for the default destination */
+ const char *instance, /* I - Instance name or @code NULL@ */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int diff, /* Result of comparison */
+ match; /* Matching index */
+
+
+ if (num_dests <= 0 || !dests)
+ return (NULL);
+
+ if (!name)
+ {
+ /*
+ * NULL name for default printer.
+ */
+
+ while (num_dests > 0)
+ {
+ if (dests->is_default)
+ return (dests);
+
+ num_dests --;
+ dests ++;
+ }
+ }
+ else
+ {
+ /*
+ * Lookup name and optionally the instance...
+ */
+
+ match = cups_find_dest(name, instance, num_dests, dests, -1, &diff);
+
+ if (!diff)
+ return (dests + match);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * '_cupsGetDestResource()' - Get the resource path and URI for a destination.
+ */
+
+const char * /* O - Printer URI */
+_cupsGetDestResource(
+ cups_dest_t *dest, /* I - Destination */
+ char *resource, /* I - Resource buffer */
+ size_t resourcesize) /* I - Size of resource buffer */
+{
+ const char *uri; /* Printer URI */
+ char scheme[32], /* URI scheme */
+ userpass[256], /* Username and password (unused) */
+ hostname[256]; /* Hostname */
+ int port; /* Port number */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!dest || !resource || resourcesize < 1)
+ {
+ if (resource)
+ *resource = '\0';
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (NULL);
+ }
+
+ /*
+ * Grab the printer URI...
+ */
+
+ if ((uri = cupsGetOption("printer-uri-supported", dest->num_options,
+ dest->options)) == NULL)
+ {
+ if (resource)
+ *resource = '\0';
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+
+ return (NULL);
+ }
+
+#ifdef HAVE_DNSSD
+ if (strstr(uri, "._tcp"))
+ {
+ if ((uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL)) == NULL)
+ return (NULL);
+ }
+#endif /* HAVE_DNSSD */
+
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
+ userpass, sizeof(userpass), hostname, sizeof(hostname),
+ &port, resource, resourcesize) < HTTP_URI_STATUS_OK)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer URI."), 1);
+
+ return (NULL);
+ }
+
+ return (uri);
+}
+
+
+/*
+ * '_cupsGetDests()' - Get destinations from a server.
+ *
+ * "op" is IPP_OP_CUPS_GET_PRINTERS to get a full list, IPP_OP_CUPS_GET_DEFAULT
+ * to get the system-wide default printer, or IPP_OP_GET_PRINTER_ATTRIBUTES for
+ * a known printer.
+ *
+ * "name" is the name of an existing printer and is only used when "op" is
+ * IPP_OP_GET_PRINTER_ATTRIBUTES.
+ *
+ * "dest" is initialized to point to the array of destinations.
+ *
+ * 0 is returned if there are no printers, no default printer, or the named
+ * printer does not exist, respectively.
+ *
+ * Free the memory used by the destination array using the @link cupsFreeDests@
+ * function.
+ *
+ * Note: On OS X this function also gets the default paper from the system
+ * preferences (~/L/P/org.cups.PrintingPrefs.plist) and includes it in the
+ * options array for each destination that supports it.
+ */
+
+int /* O - Number of destinations */
+_cupsGetDests(http_t *http, /* I - Connection to server or
+ * @code CUPS_HTTP_DEFAULT@ */
+ ipp_op_t op, /* I - IPP operation */
+ const char *name, /* I - Name of destination */
+ cups_dest_t **dests, /* IO - Destinations */
+ cups_ptype_t type, /* I - Printer type bits */
+ cups_ptype_t mask) /* I - Printer type mask */
+{
+ int num_dests = 0; /* Number of destinations */
+ cups_dest_t *dest; /* Current destination */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ const char *printer_name; /* printer-name attribute */
+ char uri[1024]; /* printer-uri value */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+#ifdef __APPLE__
+ char media_default[41]; /* Default paper size */
+#endif /* __APPLE__ */
+ char optname[1024], /* Option name */
+ value[2048], /* Option value */
+ *ptr; /* Pointer into name/value */
+ static const char * const pattrs[] = /* Attributes we're interested in */
+ {
+ "auth-info-required",
+ "device-uri",
+ "job-sheets-default",
+ "marker-change-time",
+ "marker-colors",
+ "marker-high-levels",
+ "marker-levels",
+ "marker-low-levels",
+ "marker-message",
+ "marker-names",
+ "marker-types",
+#ifdef __APPLE__
+ "media-supported",
+#endif /* __APPLE__ */
+ "printer-commands",
+ "printer-defaults",
+ "printer-info",
+ "printer-is-accepting-jobs",
+ "printer-is-shared",
+ "printer-location",
+ "printer-make-and-model",
+ "printer-mandatory-job-attributes",
+ "printer-name",
+ "printer-state",
+ "printer-state-change-time",
+ "printer-state-reasons",
+ "printer-type",
+ "printer-uri-supported"
+ };
+
+
+#ifdef __APPLE__
+ /*
+ * Get the default paper size...
+ */
+
+ appleGetPaperSize(media_default, sizeof(media_default));
+#endif /* __APPLE__ */
+
+ /*
+ * Build a IPP_OP_CUPS_GET_PRINTERS or IPP_OP_GET_PRINTER_ATTRIBUTES request, which
+ * require the following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requesting-user-name
+ * printer-uri [for IPP_OP_GET_PRINTER_ATTRIBUTES]
+ */
+
+ request = ippNewRequest(op);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
+ NULL, pattrs);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser());
+
+ if (name && op != IPP_OP_CUPS_GET_DEFAULT)
+ {
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", ippPort(), "/printers/%s", name);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ uri);
+ }
+ else if (mask)
+ {
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
+ type);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
+ mask);
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ printer_name = NULL;
+ num_options = 0;
+ options = NULL;
+
+ for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
+ {
+ if (attr->value_tag != IPP_TAG_INTEGER &&
+ attr->value_tag != IPP_TAG_ENUM &&
+ attr->value_tag != IPP_TAG_BOOLEAN &&
+ attr->value_tag != IPP_TAG_TEXT &&
+ attr->value_tag != IPP_TAG_TEXTLANG &&
+ attr->value_tag != IPP_TAG_NAME &&
+ attr->value_tag != IPP_TAG_NAMELANG &&
+ attr->value_tag != IPP_TAG_KEYWORD &&
+ attr->value_tag != IPP_TAG_RANGE &&
+ attr->value_tag != IPP_TAG_URI)
+ continue;
+
+ if (!strcmp(attr->name, "auth-info-required") ||
+ !strcmp(attr->name, "device-uri") ||
+ !strcmp(attr->name, "marker-change-time") ||
+ !strcmp(attr->name, "marker-colors") ||
+ !strcmp(attr->name, "marker-high-levels") ||
+ !strcmp(attr->name, "marker-levels") ||
+ !strcmp(attr->name, "marker-low-levels") ||
+ !strcmp(attr->name, "marker-message") ||
+ !strcmp(attr->name, "marker-names") ||
+ !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-make-and-model") ||
+ !strcmp(attr->name, "printer-mandatory-job-attributes") ||
+ !strcmp(attr->name, "printer-state") ||
+ !strcmp(attr->name, "printer-state-change-time") ||
+ !strcmp(attr->name, "printer-type") ||
+ !strcmp(attr->name, "printer-is-accepting-jobs") ||
+ !strcmp(attr->name, "printer-location") ||
+ !strcmp(attr->name, "printer-state-reasons") ||
+ !strcmp(attr->name, "printer-uri-supported"))
+ {
+ /*
+ * Add a printer description attribute...
+ */
+
+ num_options = cupsAddOption(attr->name,
+ cups_make_string(attr, value,
+ sizeof(value)),
+ num_options, &options);
+ }
+#ifdef __APPLE__
+ else if (!strcmp(attr->name, "media-supported"))
+ {
+ /*
+ * See if we can set a default media size...
+ */
+
+ int i; /* Looping var */
+
+ 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);
+ break;
+ }
+ }
+#endif /* __APPLE__ */
+ else if (!strcmp(attr->name, "printer-name") &&
+ 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 ||
+ attr->value_tag == IPP_TAG_ENUM ||
+ attr->value_tag == IPP_TAG_INTEGER ||
+ attr->value_tag == IPP_TAG_KEYWORD ||
+ attr->value_tag == IPP_TAG_NAME ||
+ attr->value_tag == IPP_TAG_RANGE) &&
+ (ptr = strstr(attr->name, "-default")) != NULL)
+ {
+ /*
+ * Add a default option...
+ */
+
+ 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);
+ }
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (!printer_name)
+ {
+ cupsFreeOptions(num_options, options);
+
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ if ((dest = cups_add_dest(printer_name, NULL, &num_dests, dests)) != NULL)
+ {
+ dest->num_options = num_options;
+ dest->options = options;
+ }
+ else
+ cupsFreeOptions(num_options, options);
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+
+ /*
+ * Return the count...
+ */
+
+ return (num_dests);
+}
+
+
+/*
+ * '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.
+ *
+ * Use the @link cupsFreeDests@ function to free the destination list and
+ * the @link cupsGetDest@ function to find a particular destination.
+ */
+
+int /* O - Number of destinations */
+cupsGetDests(cups_dest_t **dests) /* O - Destinations */
+{
+ return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
+}
+
+
+/*
+ * '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.
+ *
+ * Use the @link cupsFreeDests@ function to free the destination list and
+ * the @link cupsGetDest@ function to find a particular destination.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+int /* O - Number of destinations */
+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 */
+ const char *home; /* HOME environment variable */
+ char filename[1024]; /* Local ~/.cups/lpoptions file */
+ const char *defprinter; /* Default printer */
+ char name[1024], /* Copy of printer name */
+ *instance, /* Pointer to instance name */
+ *user_default; /* User default printer */
+ int num_reals; /* Number of real queues */
+ cups_dest_t *reals; /* Real queues */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * Range check the input...
+ */
+
+ if (!dests)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad NULL dests pointer"), 1);
+ return (0);
+ }
+
+ /*
+ * Grab the printers and classes...
+ */
+
+ *dests = (cups_dest_t *)0;
+ num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, dests, 0, 0);
+
+ if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+ {
+ cupsFreeDests(num_dests, *dests);
+ *dests = (cups_dest_t *)0;
+ return (0);
+ }
+
+ /*
+ * Make a copy of the "real" queues for a later sanity check...
+ */
+
+ if (num_dests > 0)
+ {
+ num_reals = num_dests;
+ reals = calloc(num_reals, sizeof(cups_dest_t));
+
+ if (reals)
+ memcpy(reals, *dests, num_reals * sizeof(cups_dest_t));
+ else
+ num_reals = 0;
+ }
+ else
+ {
+ num_reals = 0;
+ reals = NULL;
+ }
+
+ /*
+ * Grab the default destination...
+ */
+
+ 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;
+ }
+ else
+ instance = NULL;
+
+ /*
+ * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+ num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
+ num_dests, 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);
+ }
+
+ /*
+ * Validate the current default destination - this prevents old
+ * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
+ * pointing to a non-existent printer or class...
+ */
+
+ if (num_reals)
+ {
+ /*
+ * See if we have a default printer...
+ */
+
+ if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
+ {
+ /*
+ * Have a default; see if it is real...
+ */
+
+ if (!cupsGetDest(dest->name, NULL, num_reals, reals))
+ {
+ /*
+ * Remove the non-real printer from the list, since we don't want jobs
+ * going to an unexpected printer... (<rdar://problem/14216472>)
+ */
+
+ num_dests = cupsRemoveDest(dest->name, dest->instance, num_dests,
+ dests);
+ }
+ }
+
+ /*
+ * Free memory...
+ */
+
+ free(reals);
+ }
+
+ /*
+ * Return the number of destinations...
+ */
+
+ if (num_dests > 0)
+ _cupsSetError(IPP_STATUS_OK, NULL, 0);
+
+ return (num_dests);
+}
+
+
+/*
+ * '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.
+ *
+ * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
+ * server will be used.
+ *
+ * If "name" is @code NULL@, the default printer for the current user will be
+ * returned.
+ *
+ * The returned destination must be freed using @link cupsFreeDests@ with a
+ * "num_dests" value of 1.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+cups_dest_t * /* O - Destination or @code NULL@ */
+cupsGetNamedDest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name, /* I - Destination name or @code NULL@ for the default destination */
+ const char *instance) /* I - Instance name or @code NULL@ */
+{
+ cups_dest_t *dest; /* Destination */
+ char filename[1024], /* Path to lpoptions */
+ defname[256]; /* Default printer name */
+ const char *home = getenv("HOME"); /* Home directory */
+ int set_as_default = 0; /* Set returned destination as default */
+ ipp_op_t op = IPP_OP_GET_PRINTER_ATTRIBUTES;
+ /* IPP operation to get server ops */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * If "name" is NULL, find the default destination...
+ */
+
+ if (!name)
+ {
+ set_as_default = 1;
+ name = _cupsUserDefault(defname, sizeof(defname));
+
+ if (name)
+ {
+ char *ptr; /* Temporary pointer... */
+
+ if ((ptr = strchr(defname, '/')) != NULL)
+ {
+ *ptr++ = '\0';
+ instance = ptr;
+ }
+ else
+ instance = NULL;
+ }
+ else if (home)
+ {
+ /*
+ * No default in the environment, try the user's lpoptions files...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
+
+ name = cups_get_default(filename, defname, sizeof(defname), &instance);
+ }
+
+ if (!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);
+ }
+
+ if (!name)
+ {
+ /*
+ * No locally-set default destination, ask the server...
+ */
+
+ op = IPP_OP_CUPS_GET_DEFAULT;
+ }
+ }
+
+ /*
+ * Get the printer's attributes...
+ */
+
+ if (!_cupsGetDests(http, op, name, &dest, 0, 0))
+ return (NULL);
+
+ if (instance)
+ dest->instance = _cupsStrAlloc(instance);
+
+ if (set_as_default)
+ dest->is_default = 1;
+
+ /*
+ * Then add local options...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+ cups_get_dests(filename, name, instance, 1, 1, &dest);
+
+ if (home)
+ {
+ snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
+
+ cups_get_dests(filename, name, instance, 1, 1, &dest);
+ }
+
+ /*
+ * Return the result...
+ */
+
+ return (dest);
+}
+
+
+/*
+ * 'cupsRemoveDest()' - Remove a destination from the destination list.
+ *
+ * Removing a destination/instance does not delete the class or printer
+ * queue, merely the lpoptions for that destination/instance. Use the
+ * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
+ * options for the user.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - New number of destinations */
+cupsRemoveDest(const char *name, /* I - Destination name */
+ const char *instance, /* I - Instance name or @code NULL@ */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t **dests) /* IO - Destinations */
+{
+ int i; /* Index into destinations */
+ cups_dest_t *dest; /* Pointer to destination */
+
+
+ /*
+ * Find the destination...
+ */
+
+ if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
+ return (num_dests);
+
+ /*
+ * Free memory...
+ */
+
+ _cupsStrFree(dest->name);
+ _cupsStrFree(dest->instance);
+ cupsFreeOptions(dest->num_options, dest->options);
+
+ /*
+ * Remove the destination from the array...
+ */
+
+ num_dests --;
+
+ i = dest - *dests;
+
+ if (i < num_dests)
+ memmove(dest, dest + 1, (num_dests - i) * sizeof(cups_dest_t));
+
+ return (num_dests);
+}
+
+
+/*
+ * 'cupsSetDefaultDest()' - Set the default destination.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+void
+cupsSetDefaultDest(
+ const char *name, /* I - Destination name */
+ const char *instance, /* I - Instance name or @code NULL@ */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Current destination */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!name || num_dests <= 0 || !dests)
+ return;
+
+ /*
+ * Loop through the array and set the "is_default" flag for the matching
+ * destination...
+ */
+
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ dest->is_default = !_cups_strcasecmp(name, dest->name) &&
+ ((!instance && !dest->instance) ||
+ (instance && dest->instance &&
+ !_cups_strcasecmp(instance, dest->instance)));
+}
+
+
+/*
+ * 'cupsSetDests()' - Save the list of destinations for the default server.
+ *
+ * This function saves the destinations to /etc/cups/lpoptions when run
+ * as root and ~/.cups/lpoptions when run as a normal user.
+ */
+
+void
+cupsSetDests(int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
+}
+
+
+/*
+ * 'cupsSetDests2()' - Save the list of destinations for the specified server.
+ *
+ * This function saves the destinations to /etc/cups/lpoptions when run
+ * as root and ~/.cups/lpoptions when run as a normal user.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsSetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int i, j; /* Looping vars */
+ int wrote; /* Wrote definition? */
+ cups_dest_t *dest; /* Current destination */
+ cups_option_t *option; /* Current option */
+ _ipp_option_t *match; /* Matching attribute for option */
+ FILE *fp; /* File pointer */
+#ifndef WIN32
+ const char *home; /* HOME environment variable */
+#endif /* WIN32 */
+ char filename[1024]; /* lpoptions file */
+ int num_temps; /* Number of temporary destinations */
+ cups_dest_t *temps = NULL, /* Temporary destinations */
+ *temp; /* Current temporary dest */
+ const char *val; /* Value of temporary option */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * Range check the input...
+ */
+
+ if (!num_dests || !dests)
+ return (-1);
+
+ /*
+ * Get the server destinations...
+ */
+
+ num_temps = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &temps, 0, 0);
+
+ if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+ {
+ cupsFreeDests(num_temps, temps);
+ return (-1);
+ }
+
+ /*
+ * Figure out which file to write to...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+
+#ifndef WIN32
+ if (getuid())
+ {
+ /*
+ * Merge in server defaults...
+ */
+
+ num_temps = cups_get_dests(filename, NULL, NULL, 0, num_temps, &temps);
+
+ /*
+ * Point to user defaults...
+ */
+
+ if ((home = getenv("HOME")) != NULL)
+ {
+ /*
+ * Create ~/.cups subdirectory...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/.cups", home);
+ if (access(filename, 0))
+ mkdir(filename, 0700);
+
+ snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
+ }
+ }
+#endif /* !WIN32 */
+
+ /*
+ * Try to open the file...
+ */
+
+ if ((fp = fopen(filename, "w")) == NULL)
+ {
+ cupsFreeDests(num_temps, temps);
+ return (-1);
+ }
+
+#ifndef WIN32
+ /*
+ * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
+ * file...
+ */
+
+ if (!getuid())
+ fchmod(fileno(fp), 0644);
+#endif /* !WIN32 */
+
+ /*
+ * Write each printer; each line looks like:
+ *
+ * Dest name[/instance] options
+ * Default name[/instance] options
+ */
+
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
+ {
+ if (dest->is_default)
+ {
+ fprintf(fp, "Default %s", dest->name);
+ if (dest->instance)
+ fprintf(fp, "/%s", dest->instance);
+
+ wrote = 1;
+ }
+ else
+ wrote = 0;
+
+ if ((temp = cupsGetDest(dest->name, dest->instance, num_temps, temps)) == NULL)
+ temp = cupsGetDest(dest->name, NULL, num_temps, temps);
+
+ for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
+ {
+ /*
+ * See if this option is a printer attribute; if so, skip it...
+ */
+
+ if ((match = _ippFindOption(option->name)) != NULL &&
+ match->group_tag == IPP_TAG_PRINTER)
+ continue;
+
+ /*
+ * See if the server/global options match these; if so, don't
+ * write 'em.
+ */
+
+ if (temp &&
+ (val = cupsGetOption(option->name, temp->num_options,
+ temp->options)) != NULL &&
+ !_cups_strcasecmp(val, option->value))
+ continue;
+
+ /*
+ * Options don't match, write to the file...
+ */
+
+ if (!wrote)
+ {
+ fprintf(fp, "Dest %s", dest->name);
+ if (dest->instance)
+ fprintf(fp, "/%s", dest->instance);
+ wrote = 1;
+ }
+
+ if (option->value[0])
+ {
+ if (strchr(option->value, ' ') ||
+ strchr(option->value, '\\') ||
+ strchr(option->value, '\"') ||
+ strchr(option->value, '\''))
+ {
+ /*
+ * Quote the value...
+ */
+
+ fprintf(fp, " %s=\"", option->name);
+
+ for (val = option->value; *val; val ++)
+ {
+ if (strchr("\"\'\\", *val))
+ putc('\\', fp);
+
+ putc(*val, fp);
+ }
+
+ putc('\"', fp);
+ }
+ else
+ {
+ /*
+ * Store the literal value...
+ */
+
+ fprintf(fp, " %s=%s", option->name, option->value);
+ }
+ }
+ else
+ fprintf(fp, " %s", option->name);
+ }
+
+ if (wrote)
+ fputs("\n", fp);
+ }
+
+ /*
+ * Free the temporary destinations and close the file...
+ */
+
+ cupsFreeDests(num_temps, temps);
+
+ fclose(fp);
+
+#ifdef __APPLE__
+ /*
+ * Set the default printer for this location - this allows command-line
+ * and GUI applications to share the same default destination...
+ */
+
+ if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
+ {
+ CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault,
+ dest->name,
+ kCFStringEncodingUTF8);
+ /* Default printer name */
+
+ if (name)
+ {
+ _cupsAppleSetDefaultPrinter(name);
+ CFRelease(name);
+ }
+ }
+#endif /* __APPLE__ */
+
+#ifdef HAVE_NOTIFY_POST
+ /*
+ * Send a notification so that MacOS X applications can know about the
+ * change, too.
+ */
+
+ notify_post("com.apple.printerListChange");
+#endif /* HAVE_NOTIFY_POST */
+
+ return (0);
+}
+
+
+/*
+ * '_cupsUserDefault()' - Get the user default printer from environment
+ * variables and location information.
+ */
+
+char * /* O - Default printer or NULL */
+_cupsUserDefault(char *name, /* I - Name buffer */
+ size_t namesize) /* I - Size of name buffer */
+{
+ const char *env; /* LPDEST or PRINTER env variable */
+#ifdef __APPLE__
+ CFStringRef locprinter; /* Last printer as this location */
+#endif /* __APPLE__ */
+
+
+ if ((env = getenv("LPDEST")) == NULL)
+ if ((env = getenv("PRINTER")) != NULL && !strcmp(env, "lp"))
+ env = NULL;
+
+ if (env)
+ {
+ strlcpy(name, env, namesize);
+ return (name);
+ }
+
+#ifdef __APPLE__
+ /*
+ * Use location-based defaults if "use last printer" is selected in the
+ * system preferences...
+ */
+
+ if ((locprinter = _cupsAppleCopyDefaultPrinter()) != NULL)
+ {
+ CFStringGetCString(locprinter, name, namesize, kCFStringEncodingUTF8);
+ CFRelease(locprinter);
+ }
+ else
+ name[0] = '\0';
+
+ DEBUG_printf(("1_cupsUserDefault: Returning \"%s\".", name));
+
+ return (*name ? name : NULL);
+
+#else
+ /*
+ * No location-based defaults on this platform...
+ */
+
+ name[0] = '\0';
+ return (NULL);
+#endif /* __APPLE__ */
+}
+
+
+#ifdef __APPLE__
+/*
+ * 'appleCopyLocations()' - Copy the location history array.
+ */
+
+static CFArrayRef /* O - Location array or NULL */
+appleCopyLocations(void)
+{
+ CFArrayRef locations; /* Location array */
+
+
+ /*
+ * Look up the location array in the preferences...
+ */
+
+ if ((locations = CFPreferencesCopyAppValue(kLastUsedPrintersKey,
+ kCUPSPrintingPrefs)) == NULL)
+ return (NULL);
+
+ if (CFGetTypeID(locations) != CFArrayGetTypeID())
+ {
+ CFRelease(locations);
+ return (NULL);
+ }
+
+ return (locations);
+}
+
+
+/*
+ * 'appleCopyNetwork()' - Get the network ID for the current location.
+ */
+
+static CFStringRef /* O - Network ID */
+appleCopyNetwork(void)
+{
+ SCDynamicStoreRef dynamicStore; /* System configuration data */
+ CFStringRef key; /* Current network configuration key */
+ CFDictionaryRef ip_dict; /* Network configuration data */
+ CFStringRef network = NULL; /* Current network ID */
+
+
+ if ((dynamicStore = SCDynamicStoreCreate(NULL, CFSTR("libcups"), NULL,
+ NULL)) != NULL)
+ {
+ /*
+ * First use the IPv6 router address, if available, since that will generally
+ * be a globally-unique link-local address.
+ */
+
+ if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
+ NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)) != NULL)
+ {
+ if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
+ {
+ if ((network = CFDictionaryGetValue(ip_dict,
+ kSCPropNetIPv6Router)) != NULL)
+ CFRetain(network);
+
+ CFRelease(ip_dict);
+ }
+
+ CFRelease(key);
+ }
+
+ /*
+ * If that doesn't work, try the IPv4 router address. This isn't as unique
+ * and will likely be a 10.x.y.z or 192.168.y.z address...
+ */
+
+ if (!network)
+ {
+ if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
+ NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)) != NULL)
+ {
+ if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
+ {
+ if ((network = CFDictionaryGetValue(ip_dict,
+ kSCPropNetIPv4Router)) != NULL)
+ CFRetain(network);
+
+ CFRelease(ip_dict);
+ }
+
+ CFRelease(key);
+ }
+ }
+
+ CFRelease(dynamicStore);
+ }
+
+ return (network);
+}
+
+
+/*
+ * 'appleGetPaperSize()' - Get the default paper size.
+ */
+
+static char * /* O - Default paper size */
+appleGetPaperSize(char *name, /* I - Paper size name buffer */
+ int namesize) /* I - Size of buffer */
+{
+ CFStringRef defaultPaperID; /* Default paper ID */
+ pwg_media_t *pwgmedia; /* PWG media size */
+
+
+ defaultPaperID = _cupsAppleCopyDefaultPaperID();
+ if (!defaultPaperID ||
+ CFGetTypeID(defaultPaperID) != CFStringGetTypeID() ||
+ !CFStringGetCString(defaultPaperID, name, namesize,
+ kCFStringEncodingUTF8))
+ name[0] = '\0';
+ else if ((pwgmedia = pwgMediaForLegacy(name)) != NULL)
+ strlcpy(name, pwgmedia->pwg, namesize);
+
+ if (defaultPaperID)
+ CFRelease(defaultPaperID);
+
+ return (name);
+}
+
+
+/*
+ * 'appleGetPrinter()' - Get a printer from the history array.
+ */
+
+static CFStringRef /* O - Printer name or NULL */
+appleGetPrinter(CFArrayRef locations, /* I - Location array */
+ CFStringRef network, /* I - Network name */
+ CFIndex *locindex) /* O - Index in array */
+{
+ CFIndex i, /* Looping var */
+ count; /* Number of locations */
+ CFDictionaryRef location; /* Current location */
+ CFStringRef locnetwork, /* Current network */
+ locprinter; /* Current printer */
+
+
+ for (i = 0, count = CFArrayGetCount(locations); i < count; i ++)
+ if ((location = CFArrayGetValueAtIndex(locations, i)) != NULL &&
+ CFGetTypeID(location) == CFDictionaryGetTypeID())
+ {
+ if ((locnetwork = CFDictionaryGetValue(location,
+ kLocationNetworkKey)) != NULL &&
+ CFGetTypeID(locnetwork) == CFStringGetTypeID() &&
+ CFStringCompare(network, locnetwork, 0) == kCFCompareEqualTo &&
+ (locprinter = CFDictionaryGetValue(location,
+ kLocationPrinterIDKey)) != NULL &&
+ CFGetTypeID(locprinter) == CFStringGetTypeID())
+ {
+ if (locindex)
+ *locindex = i;
+
+ return (locprinter);
+ }
+ }
+
+ return (NULL);
+}
+#endif /* __APPLE__ */
+
+
+/*
+ * 'cups_add_dest()' - Add a destination to the array.
+ *
+ * Unlike cupsAddDest(), this function does not check for duplicates.
+ */
+
+static cups_dest_t * /* O - New destination */
+cups_add_dest(const char *name, /* I - Name of destination */
+ const char *instance, /* I - Instance or NULL */
+ int *num_dests, /* IO - Number of destinations */
+ cups_dest_t **dests) /* IO - Destinations */
+{
+ int insert, /* Insertion point */
+ diff; /* Result of comparison */
+ cups_dest_t *dest; /* Destination pointer */
+
+
+ /*
+ * Add new destination...
+ */
+
+ if (*num_dests == 0)
+ dest = malloc(sizeof(cups_dest_t));
+ else
+ dest = realloc(*dests, sizeof(cups_dest_t) * (*num_dests + 1));
+
+ if (!dest)
+ return (NULL);
+
+ *dests = dest;
+
+ /*
+ * Find where to insert the destination...
+ */
+
+ if (*num_dests == 0)
+ insert = 0;
+ else
+ {
+ insert = cups_find_dest(name, instance, *num_dests, *dests, *num_dests - 1,
+ &diff);
+
+ if (diff > 0)
+ insert ++;
+ }
+
+ /*
+ * Move the array elements as needed...
+ */
+
+ if (insert < *num_dests)
+ memmove(*dests + insert + 1, *dests + insert,
+ (*num_dests - insert) * sizeof(cups_dest_t));
+
+ (*num_dests) ++;
+
+ /*
+ * Initialize the destination...
+ */
+
+ dest = *dests + insert;
+ dest->name = _cupsStrAlloc(name);
+ dest->instance = _cupsStrAlloc(instance);
+ dest->is_default = 0;
+ dest->num_options = 0;
+ dest->options = (cups_option_t *)0;
+
+ return (dest);
+}
+
+
+# ifdef __BLOCKS__
+/*
+ * 'cups_block_cb()' - Enumeration callback for block API.
+ */
+
+static int /* O - 1 to continue, 0 to stop */
+cups_block_cb(
+ cups_dest_block_t block, /* I - Block */
+ unsigned flags, /* I - Destination flags */
+ cups_dest_t *dest) /* I - Destination */
+{
+ return ((block)(flags, dest));
+}
+# endif /* __BLOCKS__ */
+
+
+/*
+ * 'cups_compare_dests()' - Compare two destinations.
+ */
+
+static int /* O - Result of comparison */
+cups_compare_dests(cups_dest_t *a, /* I - First destination */
+ cups_dest_t *b) /* I - Second destination */
+{
+ int diff; /* Difference */
+
+
+ if ((diff = _cups_strcasecmp(a->name, b->name)) != 0)
+ return (diff);
+ else if (a->instance && b->instance)
+ return (_cups_strcasecmp(a->instance, b->instance));
+ else
+ return ((a->instance && !b->instance) - (!a->instance && b->instance));
+}
+
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+# ifdef HAVE_DNSSD
+/*
+ * 'cups_dnssd_browse_cb()' - Browse for printers.
+ */
+
+static void
+cups_dnssd_browse_cb(
+ DNSServiceRef sdRef, /* I - Service reference */
+ DNSServiceFlags flags, /* I - Option flags */
+ uint32_t interfaceIndex, /* I - Interface number */
+ DNSServiceErrorType errorCode, /* I - Error, if any */
+ const char *serviceName, /* I - Name of service/device */
+ const char *regtype, /* I - Type of service */
+ const char *replyDomain, /* I - Service domain */
+ void *context) /* I - Enumeration data */
+{
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+
+
+ DEBUG_printf(("5cups_dnssd_browse_cb(sdRef=%p, flags=%x, "
+ "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
+ "regtype=\"%s\", replyDomain=\"%s\", context=%p)",
+ sdRef, flags, interfaceIndex, errorCode, serviceName, regtype,
+ replyDomain, context));
+
+ /*
+ * Don't do anything on error...
+ */
+
+ if (errorCode != kDNSServiceErr_NoError)
+ return;
+
+ /*
+ * Get the device...
+ */
+
+ cups_dnssd_get_device(data, serviceName, regtype, replyDomain);
+}
+
+
+# else /* HAVE_AVAHI */
+/*
+ * 'cups_dnssd_browse_cb()' - Browse for printers.
+ */
+
+static void
+cups_dnssd_browse_cb(
+ AvahiServiceBrowser *browser, /* I - Browser */
+ AvahiIfIndex interface, /* I - Interface index (unused) */
+ AvahiProtocol protocol, /* I - Network protocol (unused) */
+ AvahiBrowserEvent event, /* I - What happened */
+ const char *name, /* I - Service name */
+ const char *type, /* I - Registration type */
+ const char *domain, /* I - Domain */
+ AvahiLookupResultFlags flags, /* I - Flags */
+ void *context) /* I - Devices array */
+{
+#ifdef DEBUG
+ AvahiClient *client = avahi_service_browser_get_client(browser);
+ /* Client information */
+#endif /* DEBUG */
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+
+
+ (void)interface;
+ (void)protocol;
+ (void)context;
+
+ switch (event)
+ {
+ case AVAHI_BROWSER_FAILURE:
+ DEBUG_printf(("cups_dnssd_browse_cb: %s",
+ avahi_strerror(avahi_client_errno(client))));
+ avahi_simple_poll_quit(data->simple_poll);
+ break;
+
+ case AVAHI_BROWSER_NEW:
+ /*
+ * This object is new on the network.
+ */
+
+ if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+ {
+ /*
+ * This comes from the local machine so ignore it.
+ */
+
+ DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".",
+ name));
+ }
+ else
+ {
+ /*
+ * Create a device entry for it if it doesn't yet exist.
+ */
+
+ cups_dnssd_get_device(data, name, type, domain);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ break;
+ }
+}
+
+
+/*
+ * 'cups_dnssd_client_cb()' - Avahi client callback function.
+ */
+
+static void
+cups_dnssd_client_cb(
+ AvahiClient *client, /* I - Client information (unused) */
+ AvahiClientState state, /* I - Current state */
+ void *context) /* I - User data (unused) */
+{
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+
+
+ (void)client;
+
+ /*
+ * If the connection drops, quit.
+ */
+
+ if (state == AVAHI_CLIENT_FAILURE)
+ {
+ DEBUG_puts("cups_dnssd_client_cb: Avahi connection failed.");
+ avahi_simple_poll_quit(data->simple_poll);
+ }
+}
+# endif /* HAVE_DNSSD */
+
+
+/*
+ * 'cups_dnssd_compare_device()' - Compare two devices.
+ */
+
+static int /* O - Result of comparison */
+cups_dnssd_compare_devices(
+ _cups_dnssd_device_t *a, /* I - First device */
+ _cups_dnssd_device_t *b) /* I - Second device */
+{
+ return (strcmp(a->dest.name, b->dest.name));
+}
+
+
+/*
+ * 'cups_dnssd_free_device()' - Free the memory used by a device.
+ */
+
+static void
+cups_dnssd_free_device(
+ _cups_dnssd_device_t *device, /* I - Device */
+ _cups_dnssd_data_t *data) /* I - Enumeration data */
+{
+ DEBUG_printf(("5cups_dnssd_free_device(device=%p(%s), data=%p)", device,
+ device->dest.name, data));
+
+# ifdef HAVE_DNSSD
+ if (device->ref)
+ DNSServiceRefDeallocate(device->ref);
+# else /* HAVE_AVAHI */
+ if (device->ref)
+ avahi_record_browser_free(device->ref);
+# endif /* HAVE_DNSSD */
+
+ _cupsStrFree(device->domain);
+ _cupsStrFree(device->fullName);
+ _cupsStrFree(device->regtype);
+ _cupsStrFree(device->dest.name);
+
+ cupsFreeOptions(device->dest.num_options, device->dest.options);
+
+ free(device);
+}
+
+
+/*
+ * 'cups_dnssd_get_device()' - Lookup a device and create it as needed.
+ */
+
+static _cups_dnssd_device_t * /* O - Device */
+cups_dnssd_get_device(
+ _cups_dnssd_data_t *data, /* I - Enumeration data */
+ const char *serviceName, /* I - Service name */
+ const char *regtype, /* I - Registration type */
+ const char *replyDomain) /* I - Domain name */
+{
+ _cups_dnssd_device_t key, /* Search key */
+ *device; /* Device */
+ char fullName[kDNSServiceMaxDomainName];
+ /* Full name for query */
+
+
+ DEBUG_printf(("5cups_dnssd_get_device(data=%p, serviceName=\"%s\", "
+ "regtype=\"%s\", replyDomain=\"%s\")", data, serviceName,
+ regtype, replyDomain));
+
+ /*
+ * See if this is an existing device...
+ */
+
+ key.dest.name = (char *)serviceName;
+
+ if ((device = cupsArrayFind(data->devices, &key)) != NULL)
+ {
+ /*
+ * Yes, see if we need to do anything with this...
+ */
+
+ int update = 0; /* Non-zero if we need to update */
+
+ if (!_cups_strcasecmp(replyDomain, "local.") &&
+ _cups_strcasecmp(device->domain, replyDomain))
+ {
+ /*
+ * Update the "global" listing to use the .local domain name instead.
+ */
+
+ _cupsStrFree(device->domain);
+ device->domain = _cupsStrAlloc(replyDomain);
+
+ DEBUG_printf(("6cups_dnssd_get_device: Updating '%s' to use local "
+ "domain.", device->dest.name));
+
+ update = 1;
+ }
+
+ if (!_cups_strcasecmp(regtype, "_ipps._tcp") &&
+ _cups_strcasecmp(device->regtype, regtype))
+ {
+ /*
+ * Prefer IPPS over IPP.
+ */
+
+ _cupsStrFree(device->regtype);
+ device->regtype = _cupsStrAlloc(regtype);
+
+ DEBUG_printf(("6cups_dnssd_get_device: Updating '%s' to use IPPS.",
+ device->dest.name));
+
+ update = 1;
+ }
+
+ if (!update)
+ {
+ DEBUG_printf(("6cups_dnssd_get_device: No changes to '%s'.",
+ device->dest.name));
+ return (device);
+ }
+ }
+ else
+ {
+ /*
+ * No, add the device...
+ */
+
+ DEBUG_printf(("6cups_dnssd_get_device: Adding '%s' for %s with domain "
+ "'%s'.", serviceName,
+ !strcmp(regtype, "_ipps._tcp") ? "IPPS" : "IPP",
+ replyDomain));
+
+ device = calloc(sizeof(_cups_dnssd_device_t), 1);
+ device->dest.name = _cupsStrAlloc(serviceName);
+ device->domain = _cupsStrAlloc(replyDomain);
+ device->regtype = _cupsStrAlloc(regtype);
+
+ cupsArrayAdd(data->devices, device);
+ }
+
+ /*
+ * Set the "full name" of this service, which is used for queries...
+ */
+
+# ifdef HAVE_DNSSD
+ DNSServiceConstructFullName(fullName, device->dest.name, device->regtype,
+ device->domain);
+# else /* HAVE_AVAHI */
+ avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName,
+ regtype, replyDomain);
+# endif /* HAVE_DNSSD */
+
+ _cupsStrFree(device->fullName);
+ device->fullName = _cupsStrAlloc(fullName);
+
+ if (device->ref)
+ {
+# ifdef HAVE_DNSSD
+ DNSServiceRefDeallocate(device->ref);
+# else /* HAVE_AVAHI */
+ avahi_record_browser_free(device->ref);
+# endif /* HAVE_DNSSD */
+
+ device->ref = 0;
+ }
+
+ if (device->state == _CUPS_DNSSD_ACTIVE)
+ {
+ (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
+ device->state = _CUPS_DNSSD_NEW;
+ }
+
+ return (device);
+}
+
+
+# ifdef HAVE_DNSSD
+/*
+ * 'cups_dnssd_local_cb()' - Browse for local printers.
+ */
+
+static void
+cups_dnssd_local_cb(
+ DNSServiceRef sdRef, /* I - Service reference */
+ DNSServiceFlags flags, /* I - Option flags */
+ uint32_t interfaceIndex, /* I - Interface number */
+ DNSServiceErrorType errorCode, /* I - Error, if any */
+ const char *serviceName, /* I - Name of service/device */
+ const char *regtype, /* I - Type of service */
+ const char *replyDomain, /* I - Service domain */
+ void *context) /* I - Devices array */
+{
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+ _cups_dnssd_device_t *device; /* Device */
+
+
+ DEBUG_printf(("5cups_dnssd_local_cb(sdRef=%p, flags=%x, "
+ "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
+ "regtype=\"%s\", replyDomain=\"%s\", context=%p)",
+ sdRef, flags, interfaceIndex, errorCode, serviceName,
+ regtype, replyDomain, context));
+
+ /*
+ * Only process "add" data...
+ */
+
+ if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
+ return;
+
+ /*
+ * Get the device...
+ */
+
+ device = cups_dnssd_get_device(data, serviceName, regtype, replyDomain);
+
+ /*
+ * Hide locally-registered devices...
+ */
+
+ DEBUG_printf(("6cups_dnssd_local_cb: Hiding local printer '%s'.",
+ serviceName));
+
+ if (device->ref)
+ {
+ DNSServiceRefDeallocate(device->ref);
+ device->ref = 0;
+ }
+
+ if (device->state == _CUPS_DNSSD_ACTIVE)
+ (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
+
+ device->state = _CUPS_DNSSD_LOCAL;
+}
+# endif /* HAVE_DNSSD */
+
+
+# ifdef HAVE_AVAHI
+/*
+ * 'cups_dnssd_poll_cb()' - Wait for input on the specified file descriptors.
+ *
+ * Note: This function is needed because avahi_simple_poll_iterate is broken
+ * and always uses a timeout of 0 (!) milliseconds.
+ * (Avahi Ticket #364)
+ */
+
+static int /* O - Number of file descriptors matching */
+cups_dnssd_poll_cb(
+ struct pollfd *pollfds, /* I - File descriptors */
+ unsigned int num_pollfds, /* I - Number of file descriptors */
+ int timeout, /* I - Timeout in milliseconds (unused) */
+ void *context) /* I - User data (unused) */
+{
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+ int val; /* Return value */
+
+
+ (void)timeout;
+
+ val = poll(pollfds, num_pollfds, 250);
+
+ if (val < 0)
+ {
+ DEBUG_printf(("cups_dnssd_poll_cb: %s", strerror(errno)));
+ }
+ else if (val > 0)
+ data->got_data = 1;
+
+ return (val);
+}
+# endif /* HAVE_AVAHI */
+
+
+/*
+ * 'cups_dnssd_query_cb()' - Process query data.
+ */
+
+# ifdef HAVE_DNSSD
+static void
+cups_dnssd_query_cb(
+ DNSServiceRef sdRef, /* I - Service reference */
+ DNSServiceFlags flags, /* I - Data flags */
+ uint32_t interfaceIndex, /* I - Interface */
+ DNSServiceErrorType errorCode, /* I - Error, if any */
+ const char *fullName, /* I - Full service name */
+ uint16_t rrtype, /* I - Record type */
+ uint16_t rrclass, /* I - Record class */
+ uint16_t rdlen, /* I - Length of record data */
+ const void *rdata, /* I - Record data */
+ uint32_t ttl, /* I - Time-to-live */
+ void *context) /* I - Enumeration data */
+{
+# else /* HAVE_AVAHI */
+static void
+cups_dnssd_query_cb(
+ AvahiRecordBrowser *browser, /* I - Record browser */
+ AvahiIfIndex interfaceIndex,
+ /* I - Interface index (unused) */
+ AvahiProtocol protocol, /* I - Network protocol (unused) */
+ AvahiBrowserEvent event, /* I - What happened? */
+ const char *fullName, /* I - Service name */
+ uint16_t rrclass, /* I - Record class */
+ uint16_t rrtype, /* I - Record type */
+ const void *rdata, /* I - TXT record */
+ size_t rdlen, /* I - Length of TXT record */
+ AvahiLookupResultFlags flags, /* I - Flags */
+ void *context) /* I - Enumeration data */
+{
+# ifdef DEBUG
+ AvahiClient *client = avahi_record_browser_get_client(browser);
+ /* Client information */
+# endif /* DEBUG */
+# endif /* HAVE_DNSSD */
+ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
+ /* Enumeration data */
+ char name[1024], /* Service name */
+ *ptr; /* Pointer into string */
+ _cups_dnssd_device_t dkey, /* Search key */
+ *device; /* Device */
+
+
+# ifdef HAVE_DNSSD
+ DEBUG_printf(("5cups_dnssd_query_cb(sdRef=%p, flags=%x, "
+ "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
+ "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
+ "context=%p)", sdRef, flags, interfaceIndex, errorCode,
+ fullName, rrtype, rrclass, rdlen, rdata, ttl, context));
+
+ /*
+ * Only process "add" data...
+ */
+
+ if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
+ return;
+
+# else /* HAVE_AVAHI */
+ DEBUG_printf(("5cups_dnssd_query_cb(browser=%p, interfaceIndex=%d, "
+ "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
+ "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)",
+ browser, interfaceIndex, protocol, event, fullName, rrclass,
+ rrtype, rdata, (unsigned)rdlen, flags, context));
+
+ /*
+ * Only process "add" data...
+ */
+
+ if (event != AVAHI_BROWSER_NEW)
+ {
+ if (event == AVAHI_BROWSER_FAILURE)
+ DEBUG_printf(("cups_dnssd_query_cb: %s",
+ avahi_strerror(avahi_client_errno(client))));
+
+ return;
+ }
+# endif /* HAVE_DNSSD */
+
+ /*
+ * Lookup the service in the devices array.
+ */
+
+ dkey.dest.name = name;
+
+ cups_dnssd_unquote(name, fullName, sizeof(name));
+
+ if ((ptr = strstr(name, "._")) != NULL)
+ *ptr = '\0';
+
+ if ((device = cupsArrayFind(data->devices, &dkey)) != NULL)
+ {
+ /*
+ * Found it, pull out the make and model from the TXT record and save it...
+ */
+
+ const uint8_t *txt, /* Pointer into data */
+ *txtnext, /* Next key/value pair */
+ *txtend; /* End of entire TXT record */
+ uint8_t txtlen; /* Length of current key/value pair */
+ char key[256], /* Key string */
+ value[256], /* Value string */
+ make_and_model[512],
+ /* Manufacturer and model */
+ model[256], /* Model */
+ uriname[1024], /* Name for URI */
+ uri[1024]; /* Printer URI */
+ cups_ptype_t type = CUPS_PRINTER_REMOTE | CUPS_PRINTER_BW;
+ /* Printer type */
+ int saw_printer_type = 0;
+ /* Did we see a printer-type key? */
+
+ device->state = _CUPS_DNSSD_PENDING;
+ make_and_model[0] = '\0';
+
+ strlcpy(model, "Unknown", sizeof(model));
+
+ for (txt = rdata, txtend = txt + rdlen;
+ txt < txtend;
+ txt = txtnext)
+ {
+ /*
+ * Read a key/value pair starting with an 8-bit length. Since the
+ * length is 8 bits and the size of the key/value buffers is 256, we
+ * don't need to check for overflow...
+ */
+
+ txtlen = *txt++;
+
+ if (!txtlen || (txt + txtlen) > txtend)
+ break;
+
+ txtnext = txt + txtlen;
+
+ for (ptr = key; txt < txtnext && *txt != '='; txt ++)
+ *ptr++ = *txt;
+ *ptr = '\0';
+
+ if (txt < txtnext && *txt == '=')
+ {
+ txt ++;
+
+ if (txt < txtnext)
+ memcpy(value, txt, txtnext - txt);
+ value[txtnext - txt] = '\0';
+
+ DEBUG_printf(("6cups_dnssd_query_cb: %s=%s", key, value));
+ }
+ else
+ {
+ DEBUG_printf(("6cups_dnssd_query_cb: '%s' with no value.", key));
+ continue;
+ }
+
+ if (!_cups_strcasecmp(key, "usb_MFG") ||
+ !_cups_strcasecmp(key, "usb_MANU") ||
+ !_cups_strcasecmp(key, "usb_MANUFACTURER"))
+ strlcpy(make_and_model, value, sizeof(make_and_model));
+ else if (!_cups_strcasecmp(key, "usb_MDL") ||
+ !_cups_strcasecmp(key, "usb_MODEL"))
+ strlcpy(model, value, sizeof(model));
+ else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
+ {
+ if (value[0] == '(')
+ {
+ /*
+ * Strip parenthesis...
+ */
+
+ if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
+ *ptr = '\0';
+
+ strlcpy(model, value + 1, sizeof(model));
+ }
+ else
+ strlcpy(model, value, sizeof(model));
+ }
+ else if (!_cups_strcasecmp(key, "ty"))
+ {
+ strlcpy(model, value, sizeof(model));
+
+ if ((ptr = strchr(model, ',')) != NULL)
+ *ptr = '\0';
+ }
+ else if (!_cups_strcasecmp(key, "note"))
+ device->dest.num_options = cupsAddOption("printer-location", value,
+ device->dest.num_options,
+ &device->dest.options);
+ else if (!_cups_strcasecmp(key, "pdl"))
+ {
+ /*
+ * Look for PDF-capable printers; only PDF-capable printers are shown.
+ */
+
+ const char *start, *next; /* Pointer into value */
+ int have_pdf = 0; /* Have PDF? */
+
+ for (start = value; start && *start; start = next)
+ {
+ if (!_cups_strncasecmp(start, "application/pdf", 15) &&
+ (!start[15] || start[15] == ','))
+ {
+ have_pdf = 1;
+ break;
+ }
+
+ if ((next = strchr(start, ',')) != NULL)
+ next ++;
+ }
+
+ if (!have_pdf)
+ device->state = _CUPS_DNSSD_INCOMPATIBLE;
+ }
+ else if (!_cups_strcasecmp(key, "printer-type"))
+ {
+ /*
+ * Value is either NNNN or 0xXXXX
+ */
+
+ saw_printer_type = 1;
+ type = strtol(value, NULL, 0);
+ }
+ else if (!saw_printer_type)
+ {
+ if (!_cups_strcasecmp(key, "air") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_AUTHENTICATED;
+ else if (!_cups_strcasecmp(key, "bind") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_BIND;
+ else if (!_cups_strcasecmp(key, "collate") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_COLLATE;
+ else if (!_cups_strcasecmp(key, "color") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_COLOR;
+ else if (!_cups_strcasecmp(key, "copies") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_COPIES;
+ else if (!_cups_strcasecmp(key, "duplex") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_DUPLEX;
+ else if (!_cups_strcasecmp(key, "fax") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_MFP;
+ else if (!_cups_strcasecmp(key, "papercustom") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_VARIABLE;
+ else if (!_cups_strcasecmp(key, "papermax"))
+ {
+ if (!_cups_strcasecmp(value, "legal-a4"))
+ type |= CUPS_PRINTER_SMALL;
+ else if (!_cups_strcasecmp(value, "isoc-a2"))
+ type |= CUPS_PRINTER_MEDIUM;
+ else if (!_cups_strcasecmp(value, ">isoc-a2"))
+ type |= CUPS_PRINTER_LARGE;
+ }
+ else if (!_cups_strcasecmp(key, "punch") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_PUNCH;
+ else if (!_cups_strcasecmp(key, "scan") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_MFP;
+ else if (!_cups_strcasecmp(key, "sort") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_SORT;
+ else if (!_cups_strcasecmp(key, "staple") &&
+ !_cups_strcasecmp(value, "t"))
+ type |= CUPS_PRINTER_STAPLE;
+ }
+ }
+
+ /*
+ * Save the 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));
+ strlcat(make_and_model, model, sizeof(make_and_model));
+
+ device->dest.num_options = cupsAddOption("printer-make-and-model",
+ make_and_model,
+ device->dest.num_options,
+ &device->dest.options);
+ }
+ else
+ device->dest.num_options = cupsAddOption("printer-make-and-model",
+ model,
+ device->dest.num_options,
+ &device->dest.options);
+
+ device->type = type;
+ snprintf(value, sizeof(value), "%u", type);
+ device->dest.num_options = cupsAddOption("printer-type", value,
+ device->dest.num_options,
+ &device->dest.options);
+
+ /*
+ * Save the URI...
+ */
+
+ cups_dnssd_unquote(uriname, device->fullName, sizeof(uriname));
+ httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri),
+ !strcmp(device->regtype, "_ipps._tcp") ? "ipps" : "ipp",
+ NULL, uriname, 0, saw_printer_type ? "/cups" : "/");
+
+ DEBUG_printf(("6cups_dnssd_query: printer-uri-supported=\"%s\"", uri));
+
+ device->dest.num_options = cupsAddOption("printer-uri-supported", uri,
+ device->dest.num_options,
+ &device->dest.options);
+ }
+ else
+ DEBUG_printf(("6cups_dnssd_query: Ignoring TXT record for '%s'.",
+ fullName));
+}
+
+
+/*
+ * 'cups_dnssd_resolve()' - Resolve a Bonjour printer URI.
+ */
+
+static const char * /* O - Resolved URI or NULL */
+cups_dnssd_resolve(
+ cups_dest_t *dest, /* I - Destination */
+ const char *uri, /* I - Current printer URI */
+ int msec, /* I - Time in milliseconds */
+ int *cancel, /* I - Pointer to "cancel" variable */
+ cups_dest_cb_t cb, /* I - Callback */
+ void *user_data) /* I - User data for callback */
+{
+ char tempuri[1024]; /* Temporary URI buffer */
+ _cups_dnssd_resolve_t resolve; /* Resolve data */
+
+
+ /*
+ * Resolve the URI...
+ */
+
+ resolve.cancel = cancel;
+ gettimeofday(&resolve.end_time, NULL);
+ if (msec > 0)
+ {
+ resolve.end_time.tv_sec += msec / 1000;
+ resolve.end_time.tv_usec += (msec % 1000) * 1000;
+
+ while (resolve.end_time.tv_usec >= 1000000)
+ {
+ resolve.end_time.tv_sec ++;
+ resolve.end_time.tv_usec -= 1000000;
+ }
+ }
+ else
+ resolve.end_time.tv_sec += 75;
+
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_RESOLVING,
+ dest);
+
+ if ((uri = _httpResolveURI(uri, tempuri, sizeof(tempuri),
+ _HTTP_RESOLVE_FQDN, cups_dnssd_resolve_cb,
+ &resolve)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to resolve printer URI."), 1);
+
+ if (cb)
+ (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR,
+ dest);
+
+ return (NULL);
+ }
+
+ /*
+ * Save the resolved URI...
+ */
+
+ dest->num_options = cupsAddOption("printer-uri-supported", uri,
+ dest->num_options, &dest->options);
+
+ return (cupsGetOption("printer-uri-supported", dest->num_options,
+ dest->options));
+}
+
+
+/*
+ * 'cups_dnssd_resolve_cb()' - See if we should continue resolving.
+ */
+
+static int /* O - 1 to continue, 0 to stop */
+cups_dnssd_resolve_cb(void *context) /* I - Resolve data */
+{
+ _cups_dnssd_resolve_t *resolve = (_cups_dnssd_resolve_t *)context;
+ /* Resolve data */
+ struct timeval curtime; /* Current time */
+
+
+ /*
+ * If the cancel variable is set, return immediately.
+ */
+
+ if (*resolve->cancel)
+ return (0);
+
+ /*
+ * Otherwise check the end time...
+ */
+
+ gettimeofday(&curtime, NULL);
+
+ return (curtime.tv_sec > resolve->end_time.tv_sec ||
+ (curtime.tv_sec == resolve->end_time.tv_sec &&
+ curtime.tv_usec > resolve->end_time.tv_usec));
+}
+
+
+/*
+ * 'cups_dnssd_unquote()' - Unquote a name string.
+ */
+
+static void
+cups_dnssd_unquote(char *dst, /* I - Destination buffer */
+ const char *src, /* I - Source string */
+ size_t dstsize) /* I - Size of destination buffer */
+{
+ char *dstend = dst + dstsize - 1; /* End of destination buffer */
+
+
+ while (*src && dst < dstend)
+ {
+ if (*src == '\\')
+ {
+ src ++;
+ if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
+ isdigit(src[2] & 255))
+ {
+ *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
+ src += 3;
+ }
+ else
+ *dst++ = *src++;
+ }
+ else
+ *dst++ = *src ++;
+ }
+
+ *dst = '\0';
+}
+#endif /* HAVE_DNSSD */
+
+
+/*
+ * 'cups_find_dest()' - Find a destination using a binary search.
+ */
+
+static int /* O - Index of match */
+cups_find_dest(const char *name, /* I - Destination name */
+ const char *instance, /* I - Instance or NULL */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests, /* I - Destinations */
+ int prev, /* I - Previous index */
+ int *rdiff) /* O - Difference of match */
+{
+ int left, /* Low mark for binary search */
+ right, /* High mark for binary search */
+ current, /* Current index */
+ diff; /* Result of comparison */
+ cups_dest_t key; /* Search key */
+
+
+ key.name = (char *)name;
+ key.instance = (char *)instance;
+
+ if (prev >= 0)
+ {
+ /*
+ * Start search on either side of previous...
+ */
+
+ if ((diff = cups_compare_dests(&key, dests + prev)) == 0 ||
+ (diff < 0 && prev == 0) ||
+ (diff > 0 && prev == (num_dests - 1)))
+ {
+ *rdiff = diff;
+ return (prev);
+ }
+ else if (diff < 0)
+ {
+ /*
+ * Start with previous on right side...
+ */
+
+ left = 0;
+ right = prev;
+ }
+ else
+ {
+ /*
+ * Start wih previous on left side...
+ */
+
+ left = prev;
+ right = num_dests - 1;
+ }
+ }
+ else
+ {
+ /*
+ * Start search in the middle...
+ */
+
+ left = 0;
+ right = num_dests - 1;
+ }
+
+ do
+ {
+ current = (left + right) / 2;
+ diff = cups_compare_dests(&key, dests + current);
+
+ if (diff == 0)
+ break;
+ else if (diff < 0)
+ right = current;
+ else
+ left = current;
+ }
+ while ((right - left) > 1);
+
+ if (diff != 0)
+ {
+ /*
+ * Check the last 1 or 2 elements...
+ */
+
+ if ((diff = cups_compare_dests(&key, dests + left)) <= 0)
+ current = left;
+ else
+ {
+ diff = cups_compare_dests(&key, dests + right);
+ current = right;
+ }
+ }
+
+ /*
+ * Return the closest destination and the difference...
+ */
+
+ *rdiff = diff;
+
+ return (current);
+}
+
+
+/*
+ * 'cups_get_default()' - Get the default destination from an lpoptions file.
+ */
+
+static char * /* O - Default destination or NULL */
+cups_get_default(const char *filename, /* I - File to read */
+ char *namebuf, /* I - Name buffer */
+ size_t namesize, /* I - Size of name buffer */
+ const char **instance) /* I - Instance */
+{
+ cups_file_t *fp; /* lpoptions file */
+ char line[8192], /* Line from file */
+ *value, /* Value for line */
+ *nameptr; /* Pointer into name */
+ int linenum; /* Current line */
+
+
+ *namebuf = '\0';
+
+ if ((fp = cupsFileOpen(filename, "r")) != NULL)
+ {
+ linenum = 0;
+
+ while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ {
+ if (!_cups_strcasecmp(line, "default") && value)
+ {
+ strlcpy(namebuf, value, namesize);
+
+ if ((nameptr = strchr(namebuf, ' ')) != NULL)
+ *nameptr = '\0';
+ if ((nameptr = strchr(namebuf, '\t')) != NULL)
+ *nameptr = '\0';
+
+ if ((nameptr = strchr(namebuf, '/')) != NULL)
+ *nameptr++ = '\0';
+
+ *instance = nameptr;
+ break;
+ }
+ }
+
+ cupsFileClose(fp);
+ }
+
+ return (*namebuf ? namebuf : NULL);
+}
+
+
+/*
+ * 'cups_get_dests()' - Get destinations from a file.
+ */
+
+static int /* O - Number of destinations */
+cups_get_dests(
+ const char *filename, /* I - File to read from */
+ const char *match_name, /* I - Destination name we want */
+ const char *match_inst, /* I - Instance name we want */
+ int user_default_set, /* I - User default printer set? */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t **dests) /* IO - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Current destination */
+ cups_file_t *fp; /* File pointer */
+ char line[8192], /* Line from file */
+ *lineptr, /* Pointer into line */
+ *name, /* Name of destination/option */
+ *instance; /* Instance of destination */
+ int linenum; /* Current line number */
+
+
+ DEBUG_printf(("7cups_get_dests(filename=\"%s\", match_name=\"%s\", "
+ "match_inst=\"%s\", user_default_set=%d, num_dests=%d, "
+ "dests=%p)", filename, match_name, match_inst,
+ user_default_set, num_dests, dests));
+
+ /*
+ * Try to open the file...
+ */
+
+ if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ return (num_dests);
+
+ /*
+ * Read each printer; each line looks like:
+ *
+ * Dest name[/instance] options
+ * Default name[/instance] options
+ */
+
+ linenum = 0;
+
+ while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
+ {
+ /*
+ * See what type of line it is...
+ */
+
+ DEBUG_printf(("9cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"",
+ linenum, line, lineptr));
+
+ if ((_cups_strcasecmp(line, "dest") && _cups_strcasecmp(line, "default")) || !lineptr)
+ {
+ DEBUG_puts("9cups_get_dests: Not a dest or default line...");
+ continue;
+ }
+
+ name = lineptr;
+
+ /*
+ * Search for an instance...
+ */
+
+ while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
+ lineptr ++;
+
+ if (*lineptr == '/')
+ {
+ /*
+ * Found an instance...
+ */
+
+ *lineptr++ = '\0';
+ instance = lineptr;
+
+ /*
+ * Search for an instance...
+ */
+
+ while (!isspace(*lineptr & 255) && *lineptr)
+ lineptr ++;
+ }
+ else
+ instance = NULL;
+
+ if (*lineptr)
+ *lineptr++ = '\0';
+
+ DEBUG_printf(("9cups_get_dests: name=\"%s\", instance=\"%s\"", name,
+ instance));
+
+ /*
+ * See if the primary instance of the destination exists; if not,
+ * ignore this entry and move on...
+ */
+
+ if (match_name)
+ {
+ if (_cups_strcasecmp(name, match_name) ||
+ (!instance && match_inst) ||
+ (instance && !match_inst) ||
+ (instance && _cups_strcasecmp(instance, match_inst)))
+ continue;
+
+ dest = *dests;
+ }
+ else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
+ {
+ DEBUG_puts("9cups_get_dests: Not found!");
+ continue;
+ }
+ else
+ {
+ /*
+ * Add the destination...
+ */
+
+ num_dests = cupsAddDest(name, instance, num_dests, dests);
+
+ if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
+ {
+ /*
+ * Out of memory!
+ */
+
+ DEBUG_puts("9cups_get_dests: Out of memory!");
+ break;
+ }
+ }
+
+ /*
+ * Add options until we hit the end of the line...
+ */
+
+ dest->num_options = cupsParseOptions(lineptr, dest->num_options,
+ &(dest->options));
+
+ /*
+ * If we found what we were looking for, stop now...
+ */
+
+ if (match_name)
+ break;
+
+ /*
+ * Set this as default if needed...
+ */
+
+ if (!user_default_set && !_cups_strcasecmp(line, "default"))
+ {
+ DEBUG_puts("9cups_get_dests: Setting as default...");
+
+ for (i = 0; i < num_dests; i ++)
+ (*dests)[i].is_default = 0;
+
+ dest->is_default = 1;
+ }
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ cupsFileClose(fp);
+
+ return (num_dests);
+}
+
+
+/*
+ * 'cups_make_string()' - Make a comma-separated string of values from an IPP
+ * attribute.
+ */
+
+static char * /* O - New string */
+cups_make_string(
+ ipp_attribute_t *attr, /* I - Attribute to convert */
+ char *buffer, /* I - Buffer */
+ size_t bufsize) /* I - Size of buffer */
+{
+ int i; /* Looping var */
+ char *ptr, /* Pointer into buffer */
+ *end, /* Pointer to end of buffer */
+ *valptr; /* Pointer into string attribute */
+
+
+ /*
+ * Return quickly if we have a single string value...
+ */
+
+ if (attr->num_values == 1 &&
+ attr->value_tag != IPP_TAG_INTEGER &&
+ attr->value_tag != IPP_TAG_ENUM &&
+ attr->value_tag != IPP_TAG_BOOLEAN &&
+ attr->value_tag != IPP_TAG_RANGE)
+ return (attr->values[0].string.text);
+
+ /*
+ * Copy the values to the string, separating with commas and escaping strings
+ * as needed...
+ */
+
+ end = buffer + bufsize - 1;
+
+ for (i = 0, ptr = buffer; i < attr->num_values && ptr < end; i ++)
+ {
+ if (i)
+ *ptr++ = ',';
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ snprintf(ptr, end - ptr + 1, "%d", attr->values[i].integer);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (attr->values[i].boolean)
+ strlcpy(ptr, "true", end - ptr + 1);
+ else
+ strlcpy(ptr, "false", end - ptr + 1);
+ break;
+
+ case IPP_TAG_RANGE :
+ if (attr->values[i].range.lower == attr->values[i].range.upper)
+ snprintf(ptr, end - ptr + 1, "%d", attr->values[i].range.lower);
+ else
+ snprintf(ptr, end - ptr + 1, "%d-%d", attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ break;
+
+ default :
+ for (valptr = attr->values[i].string.text;
+ *valptr && ptr < end;)
+ {
+ if (strchr(" \t\n\\\'\"", *valptr))
+ {
+ if (ptr >= (end - 1))
+ break;
+
+ *ptr++ = '\\';
+ }
+
+ *ptr++ = *valptr++;
+ }
+
+ *ptr = '\0';
+ break;
+ }
+
+ ptr += strlen(ptr);
+ }
+
+ *ptr = '\0';
+
+ return (buffer);
+}
+
+
+/*
+ * End of "$Id: dest.c 11688 2014-03-05 21:11:32Z msweet $".
+ */
diff --git a/cups/libs/cups/dir.c b/cups/libs/cups/dir.c
new file mode 100644
index 000000000..1bae123e2
--- /dev/null
+++ b/cups/libs/cups/dir.c
@@ -0,0 +1,472 @@
+/*
+ * "$Id: dir.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Directory routines for CUPS.
+ *
+ * This set of APIs abstracts enumeration of directory entries.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * 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/".
+ *
+ * Contents:
+ *
+ * _cups_dir_time() - Convert a FILETIME value to a UNIX time value.
+ * cupsDirClose() - Close a directory.
+ * cupsDirOpen() - Open a directory.
+ * cupsDirRead() - Read the next directory entry.
+ * cupsDirRewind() - Rewind to the start of the directory.
+ * cupsDirClose() - Close a directory.
+ * cupsDirOpen() - Open a directory.
+ * cupsDirRead() - Read the next directory entry.
+ * cupsDirRewind() - Rewind to the start of the directory.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+#include "debug-private.h"
+#include "dir.h"
+
+
+/*
+ * Windows implementation...
+ */
+
+#ifdef WIN32
+# include <windows.h>
+
+/*
+ * Types and structures...
+ */
+
+struct _cups_dir_s /**** Directory data structure ****/
+{
+ char directory[1024]; /* Directory filename */
+ HANDLE dir; /* Directory handle */
+ cups_dentry_t entry; /* Directory entry */
+};
+
+
+/*
+ * '_cups_dir_time()' - Convert a FILETIME value to a UNIX time value.
+ */
+
+time_t /* O - UNIX time */
+_cups_dir_time(FILETIME ft) /* I - File time */
+{
+ ULONGLONG val; /* File time in 0.1 usecs */
+
+
+ /*
+ * Convert file time (1/10 microseconds since Jan 1, 1601) to UNIX
+ * time (seconds since Jan 1, 1970). There are 11,644,732,800 seconds
+ * between them...
+ */
+
+ val = ft.dwLowDateTime + ((ULONGLONG)ft.dwHighDateTime << 32);
+ return ((time_t)(val / 10000000 - 11644732800));
+}
+
+
+/*
+ * 'cupsDirClose()' - Close a directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsDirClose(cups_dir_t *dp) /* I - Directory pointer */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!dp)
+ return;
+
+ /*
+ * Close an open directory handle...
+ */
+
+ if (dp->dir != INVALID_HANDLE_VALUE)
+ FindClose(dp->dir);
+
+ /*
+ * Free memory used...
+ */
+
+ free(dp);
+}
+
+
+/*
+ * 'cupsDirOpen()' - Open a directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_dir_t * /* O - Directory pointer or @code NULL@ if the directory could not be opened. */
+cupsDirOpen(const char *directory) /* I - Directory name */
+{
+ cups_dir_t *dp; /* Directory */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!directory)
+ return (NULL);
+
+ /*
+ * Allocate memory for the directory structure...
+ */
+
+ dp = (cups_dir_t *)calloc(1, sizeof(cups_dir_t));
+ if (!dp)
+ return (NULL);
+
+ /*
+ * Copy the directory name for later use...
+ */
+
+ dp->dir = INVALID_HANDLE_VALUE;
+
+ strlcpy(dp->directory, directory, sizeof(dp->directory));
+
+ /*
+ * Return the new directory structure...
+ */
+
+ return (dp);
+}
+
+
+/*
+ * 'cupsDirRead()' - Read the next directory entry.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_dentry_t * /* O - Directory entry or @code NULL@ if there are no more */
+cupsDirRead(cups_dir_t *dp) /* I - Directory pointer */
+{
+ WIN32_FIND_DATA entry; /* Directory entry data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!dp)
+ return (NULL);
+
+ /*
+ * See if we have already started finding files...
+ */
+
+ if (dp->dir == INVALID_HANDLE_VALUE)
+ {
+ /*
+ * No, find the first file...
+ */
+
+ dp->dir = FindFirstFile(dp->directory, &entry);
+ if (dp->dir == INVALID_HANDLE_VALUE)
+ return (NULL);
+ }
+ else if (!FindNextFile(dp->dir, &entry))
+ return (NULL);
+
+ /*
+ * Copy the name over and convert the file information...
+ */
+
+ strlcpy(dp->entry.filename, entry.cFileName, sizeof(dp->entry.filename));
+
+ if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ dp->entry.fileinfo.st_mode = 0755 | S_IFDIR;
+ else
+ dp->entry.fileinfo.st_mode = 0644;
+
+ dp->entry.fileinfo.st_atime = _cups_dir_time(entry.ftLastAccessTime);
+ dp->entry.fileinfo.st_ctime = _cups_dir_time(entry.ftCreationTime);
+ dp->entry.fileinfo.st_mtime = _cups_dir_time(entry.ftLastWriteTime);
+ dp->entry.fileinfo.st_size = entry.nFileSizeLow + ((unsigned long long)entry.nFileSizeHigh << 32);
+
+ /*
+ * Return the entry...
+ */
+
+ return (&(dp->entry));
+}
+
+
+/*
+ * 'cupsDirRewind()' - Rewind to the start of the directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsDirRewind(cups_dir_t *dp) /* I - Directory pointer */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!dp)
+ return;
+
+ /*
+ * Close an open directory handle...
+ */
+
+ if (dp->dir != INVALID_HANDLE_VALUE)
+ {
+ FindClose(dp->dir);
+ dp->dir = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+#else
+
+/*
+ * POSIX implementation...
+ */
+
+# include <sys/types.h>
+# include <dirent.h>
+
+
+/*
+ * Types and structures...
+ */
+
+struct _cups_dir_s /**** Directory data structure ****/
+{
+ char directory[1024]; /* Directory filename */
+ DIR *dir; /* Directory file */
+ cups_dentry_t entry; /* Directory entry */
+};
+
+
+/*
+ * 'cupsDirClose()' - Close a directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsDirClose(cups_dir_t *dp) /* I - Directory pointer */
+{
+ DEBUG_printf(("cupsDirClose(dp=%p)", dp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!dp)
+ return;
+
+ /*
+ * Close the directory and free memory...
+ */
+
+ closedir(dp->dir);
+ free(dp);
+}
+
+
+/*
+ * 'cupsDirOpen()' - Open a directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_dir_t * /* O - Directory pointer or @code NULL@ if the directory could not be opened. */
+cupsDirOpen(const char *directory) /* I - Directory name */
+{
+ cups_dir_t *dp; /* Directory */
+
+
+ DEBUG_printf(("cupsDirOpen(directory=\"%s\")", directory));
+
+ /*
+ * Range check input...
+ */
+
+ if (!directory)
+ return (NULL);
+
+ /*
+ * Allocate memory for the directory structure...
+ */
+
+ dp = (cups_dir_t *)calloc(1, sizeof(cups_dir_t));
+ if (!dp)
+ return (NULL);
+
+ /*
+ * Open the directory...
+ */
+
+ dp->dir = opendir(directory);
+ if (!dp->dir)
+ {
+ free(dp);
+ return (NULL);
+ }
+
+ /*
+ * Copy the directory name for later use...
+ */
+
+ strlcpy(dp->directory, directory, sizeof(dp->directory));
+
+ /*
+ * Return the new directory structure...
+ */
+
+ return (dp);
+}
+
+
+/*
+ * 'cupsDirRead()' - Read the next directory entry.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_dentry_t * /* O - Directory entry or @code NULL@ when there are no more */
+cupsDirRead(cups_dir_t *dp) /* I - Directory pointer */
+{
+ 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)", dp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!dp)
+ return (NULL);
+
+ /*
+ * Try reading an entry that is not "." or ".."...
+ */
+
+ 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...
+ */
+
+ if ((entry = readdir(dp->dir)) == NULL)
+ {
+ DEBUG_puts("3cupsDirRead: readdir() returned a NULL pointer!");
+ return (NULL);
+ }
+
+ DEBUG_printf(("4cupsDirRead: readdir() returned \"%s\"...", entry->d_name));
+
+# endif /* HAVE_PTHREAD_H */
+
+ /*
+ * Skip "." and ".."...
+ */
+
+ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+ continue;
+
+ /*
+ * Copy the name over and get the file information...
+ */
+
+ strlcpy(dp->entry.filename, entry->d_name, sizeof(dp->entry.filename));
+
+ snprintf(filename, sizeof(filename), "%s/%s", dp->directory, entry->d_name);
+
+ if (stat(filename, &(dp->entry.fileinfo)))
+ {
+ DEBUG_printf(("3cupsDirRead: stat() failed for \"%s\" - %s...", filename,
+ strerror(errno)));
+ continue;
+ }
+
+ /*
+ * Return the entry...
+ */
+
+ return (&(dp->entry));
+ }
+}
+
+
+/*
+ * 'cupsDirRewind()' - Rewind to the start of the directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsDirRewind(cups_dir_t *dp) /* I - Directory pointer */
+{
+ DEBUG_printf(("cupsDirRewind(dp=%p)", dp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!dp)
+ return;
+
+ /*
+ * Rewind the directory...
+ */
+
+ rewinddir(dp->dir);
+}
+
+
+#endif /* WIN32 */
+
+/*
+ * End of "$Id: dir.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/dir.h b/cups/libs/cups/dir.h
new file mode 100644
index 000000000..ad8278c45
--- /dev/null
+++ b/cups/libs/cups/dir.h
@@ -0,0 +1,69 @@
+/*
+ * "$Id: dir.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Public directory definitions for CUPS.
+ *
+ * This set of APIs abstracts enumeration of directory entries.
+ *
+ * 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/".
+ */
+
+#ifndef _CUPS_DIR_H_
+# define _CUPS_DIR_H_
+
+
+/*
+ * Include necessary headers...
+ */
+
+# include "versioning.h"
+# include <sys/stat.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Data types...
+ */
+
+typedef struct _cups_dir_s cups_dir_t; /**** Directory type ****/
+
+typedef struct cups_dentry_s /**** Directory entry type ****/
+{
+ char filename[260]; /* File name */
+ struct stat fileinfo; /* File information */
+} cups_dentry_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern void cupsDirClose(cups_dir_t *dp) _CUPS_API_1_2;
+extern cups_dir_t *cupsDirOpen(const char *directory) _CUPS_API_1_2;
+extern cups_dentry_t *cupsDirRead(cups_dir_t *dp) _CUPS_API_1_2;
+extern void cupsDirRewind(cups_dir_t *dp) _CUPS_API_1_2;
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_DIR_H_ */
+
+/*
+ * End of "$Id: dir.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/emit.c b/cups/libs/cups/emit.c
new file mode 100644
index 000000000..a9c35bb94
--- /dev/null
+++ b/cups/libs/cups/emit.c
@@ -0,0 +1,1229 @@
+/*
+ * "$Id: emit.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * PPD code emission routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdCollect() - Collect all marked options that reside in the
+ * specified section.
+ * ppdCollect2() - Collect all marked options that reside in the
+ * specified section and minimum order.
+ * ppdEmit() - Emit code for marked options to a file.
+ * ppdEmitAfterOrder() - Emit a subset of the code for marked options to a
+ * file.
+ * ppdEmitFd() - Emit code for marked options to a file.
+ * ppdEmitJCL() - Emit code for JCL options to a file.
+ * ppdEmitJCLEnd() - Emit JCLEnd code to a file.
+ * ppdEmitString() - Get a string containing the code for marked
+ * options.
+ * ppd_compare_cparams() - Compare the order of two custom parameters.
+ * ppd_handle_media() - Handle media selection...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * Local functions...
+ */
+
+static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
+static void ppd_handle_media(ppd_file_t *ppd);
+
+
+/*
+ * Local globals...
+ */
+
+static const char ppd_custom_code[] =
+ "pop pop pop\n"
+ "<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
+
+
+/*
+ * 'ppdCollect()' - Collect all marked options that reside in the specified
+ * section.
+ *
+ * The choices array should be freed using @code free@ when you are
+ * finished with it.
+ */
+
+int /* O - Number of options marked */
+ppdCollect(ppd_file_t *ppd, /* I - PPD file data */
+ ppd_section_t section, /* I - Section to collect */
+ ppd_choice_t ***choices) /* O - Pointers to choices */
+{
+ return (ppdCollect2(ppd, section, 0.0, choices));
+}
+
+
+/*
+ * 'ppdCollect2()' - Collect all marked options that reside in the
+ * specified section and minimum order.
+ *
+ * The choices array should be freed using @code free@ when you are
+ * finished with it.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Number of options marked */
+ppdCollect2(ppd_file_t *ppd, /* I - PPD file data */
+ ppd_section_t section, /* I - Section to collect */
+ float min_order, /* I - Minimum OrderDependency value */
+ ppd_choice_t ***choices) /* O - Pointers to choices */
+{
+ ppd_choice_t *c; /* Current choice */
+ ppd_section_t csection; /* Current section */
+ float corder; /* Current OrderDependency value */
+ int count; /* Number of choices collected */
+ ppd_choice_t **collect; /* Collected choices */
+ float *orders; /* Collected order values */
+
+
+ DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)",
+ ppd, section, min_order, choices));
+
+ if (!ppd || !choices)
+ {
+ if (choices)
+ *choices = NULL;
+
+ return (0);
+ }
+
+ /*
+ * Allocate memory for up to N selected choices...
+ */
+
+ count = 0;
+ if ((collect = calloc(sizeof(ppd_choice_t *),
+ cupsArrayCount(ppd->marked))) == NULL)
+ {
+ *choices = NULL;
+ return (0);
+ }
+
+ if ((orders = calloc(sizeof(float), cupsArrayCount(ppd->marked))) == NULL)
+ {
+ *choices = NULL;
+ free(collect);
+ return (0);
+ }
+
+ /*
+ * Loop through all options and add choices as needed...
+ */
+
+ for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+ c;
+ c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
+ {
+ csection = c->option->section;
+ corder = c->option->order;
+
+ if (!strcmp(c->choice, "Custom"))
+ {
+ ppd_attr_t *attr; /* NonUIOrderDependency value */
+ float aorder; /* Order value */
+ char asection[17], /* Section name */
+ amain[PPD_MAX_NAME + 1],
+ aoption[PPD_MAX_NAME];
+ /* *CustomFoo and True */
+
+
+ for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
+ if (attr->value &&
+ sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
+ aoption) == 4 &&
+ !strncmp(amain, "*Custom", 7) &&
+ !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
+ {
+ /*
+ * Use this NonUIOrderDependency...
+ */
+
+ corder = aorder;
+
+ if (!strcmp(asection, "DocumentSetup"))
+ csection = PPD_ORDER_DOCUMENT;
+ else if (!strcmp(asection, "ExitServer"))
+ csection = PPD_ORDER_EXIT;
+ else if (!strcmp(asection, "JCLSetup"))
+ csection = PPD_ORDER_JCL;
+ else if (!strcmp(asection, "PageSetup"))
+ csection = PPD_ORDER_PAGE;
+ else if (!strcmp(asection, "Prolog"))
+ csection = PPD_ORDER_PROLOG;
+ else
+ csection = PPD_ORDER_ANY;
+
+ break;
+ }
+ }
+
+ if (csection == section && corder >= min_order)
+ {
+ collect[count] = c;
+ orders[count] = corder;
+ count ++;
+ }
+ }
+
+ /*
+ * If we have more than 1 marked choice, sort them...
+ */
+
+ if (count > 1)
+ {
+ int i, j; /* Looping vars */
+
+ for (i = 0; i < (count - 1); i ++)
+ for (j = i + 1; j < count; j ++)
+ if (orders[i] > orders[j])
+ {
+ c = collect[i];
+ corder = orders[i];
+ collect[i] = collect[j];
+ orders[i] = orders[j];
+ collect[j] = c;
+ orders[j] = corder;
+ }
+ }
+
+ free(orders);
+
+ DEBUG_printf(("2ppdCollect2: %d marked choices...", count));
+
+ /*
+ * Return the array and number of choices; if 0, free the array since
+ * it isn't needed.
+ */
+
+ if (count > 0)
+ {
+ *choices = collect;
+ return (count);
+ }
+ else
+ {
+ *choices = NULL;
+ free(collect);
+ return (0);
+ }
+}
+
+
+/*
+ * 'ppdEmit()' - Emit code for marked options to a file.
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmit(ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp, /* I - File to write to */
+ ppd_section_t section) /* I - Section to write */
+{
+ return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0));
+}
+
+
+/*
+ * 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options to a file.
+ *
+ * When "limit" is non-zero, this function only emits options whose
+ * OrderDependency value is greater than or equal to "min_order".
+ *
+ * When "limit" is zero, this function is identical to ppdEmit().
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmitAfterOrder(
+ ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp, /* I - File to write to */
+ ppd_section_t section, /* I - Section to write */
+ int limit, /* I - Non-zero to use min_order */
+ float min_order) /* I - Lowest OrderDependency */
+{
+ char *buffer; /* Option code */
+ int status; /* Return status */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !fp)
+ return (-1);
+
+ /*
+ * Get the string...
+ */
+
+ buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f);
+
+ /*
+ * Write it as needed and return...
+ */
+
+ if (buffer)
+ {
+ status = fputs(buffer, fp) < 0 ? -1 : 0;
+
+ free(buffer);
+ }
+ else
+ status = 0;
+
+ return (status);
+}
+
+
+/*
+ * 'ppdEmitFd()' - Emit code for marked options to a file.
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
+ int fd, /* I - File to write to */
+ ppd_section_t section) /* I - Section to write */
+{
+ char *buffer, /* Option code */
+ *bufptr; /* Pointer into code */
+ size_t buflength; /* Length of option code */
+ ssize_t bytes; /* Bytes written */
+ int status; /* Return status */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || fd < 0)
+ return (-1);
+
+ /*
+ * Get the string...
+ */
+
+ buffer = ppdEmitString(ppd, section, 0.0);
+
+ /*
+ * Write it as needed and return...
+ */
+
+ if (buffer)
+ {
+ buflength = strlen(buffer);
+ bufptr = buffer;
+ bytes = 0;
+
+ while (buflength > 0)
+ {
+#ifdef WIN32
+ if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
+#else
+ if ((bytes = write(fd, bufptr, buflength)) < 0)
+#endif /* WIN32 */
+ {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+
+ break;
+ }
+
+ buflength -= bytes;
+ bufptr += bytes;
+ }
+
+ status = bytes < 0 ? -1 : 0;
+
+ free(buffer);
+ }
+ else
+ status = 0;
+
+ return (status);
+}
+
+
+/*
+ * 'ppdEmitJCL()' - Emit code for JCL options to a file.
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp, /* I - File to write to */
+ int job_id, /* I - Job ID */
+ const char *user, /* I - Username */
+ const char *title) /* I - Title */
+{
+ char *ptr; /* Pointer into JCL string */
+ char temp[65], /* Local title string */
+ displaymsg[33]; /* Local display string */
+
+
+ /*
+ * Range check the input...
+ */
+
+ if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
+ return (0);
+
+ /*
+ * See if the printer supports HP PJL...
+ */
+
+ if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
+ {
+ /*
+ * This printer uses HP PJL commands for output; filter the output
+ * so that we only have a single "@PJL JOB" command in the header...
+ *
+ * To avoid bugs in the PJL implementation of certain vendors' products
+ * (Xerox in particular), we add a dummy "@PJL" command at the beginning
+ * of the PJL commands to initialize PJL processing.
+ */
+
+ ppd_attr_t *charset; /* PJL charset */
+ ppd_attr_t *display; /* PJL display command */
+
+
+ if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
+ {
+ if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8"))
+ charset = NULL;
+ }
+
+ if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
+ {
+ if (!display->value)
+ display = NULL;
+ }
+
+ fputs("\033%-12345X@PJL\n", fp);
+ for (ptr = ppd->jcl_begin + 9; *ptr;)
+ if (!strncmp(ptr, "@PJL JOB", 8))
+ {
+ /*
+ * Skip job command...
+ */
+
+ for (;*ptr; ptr ++)
+ if (*ptr == '\n')
+ break;
+
+ if (*ptr)
+ ptr ++;
+ }
+ else
+ {
+ /*
+ * Copy line...
+ */
+
+ for (;*ptr; ptr ++)
+ {
+ putc(*ptr, fp);
+ if (*ptr == '\n')
+ break;
+ }
+
+ if (*ptr)
+ ptr ++;
+ }
+
+ /*
+ * Clean up the job title...
+ */
+
+ if ((ptr = strrchr(title, '/')) != NULL)
+ {
+ /*
+ * Only show basename of file path...
+ */
+
+ title = ptr + 1;
+ }
+
+ if (!strncmp(title, "smbprn.", 7))
+ {
+ /*
+ * Skip leading smbprn.######## from Samba jobs...
+ */
+
+ for (title += 7; *title && isdigit(*title & 255); title ++);
+ while (_cups_isspace(*title))
+ title ++;
+
+ if ((ptr = strstr(title, " - ")) != NULL)
+ {
+ /*
+ * Skip application name in "Some Application - Title of job"...
+ */
+
+ title = ptr + 3;
+ }
+ }
+
+ /*
+ * Replace double quotes with single quotes and UTF-8 characters with
+ * question marks so that the title does not cause a PJL syntax error.
+ */
+
+ strlcpy(temp, title, sizeof(temp));
+
+ for (ptr = temp; *ptr; ptr ++)
+ if (*ptr == '\"')
+ *ptr = '\'';
+ else if (!charset && (*ptr & 128))
+ *ptr = '?';
+
+ /*
+ * CUPS STR #3125: Long PJL JOB NAME causes problems with some printers
+ *
+ * Generate the display message, truncating at 32 characters + nul to avoid
+ * issues with some printer's PJL implementations...
+ */
+
+ snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp);
+
+ /*
+ * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
+ */
+
+ if (display && strcmp(display->value, "job"))
+ fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
+ else if (display && !strcmp(display->value, "rdymsg"))
+ fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
+ else
+ fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
+ displaymsg);
+
+ /*
+ * Replace double quotes with single quotes and UTF-8 characters with
+ * question marks so that the user does not cause a PJL syntax error.
+ */
+
+ strlcpy(temp, user, sizeof(temp));
+
+ for (ptr = temp; *ptr; ptr ++)
+ if (*ptr == '\"')
+ *ptr = '\'';
+ else if (!charset && (*ptr & 128))
+ *ptr = '?';
+
+ fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp);
+ }
+ else
+ fputs(ppd->jcl_begin, fp);
+
+ ppdEmit(ppd, fp, PPD_ORDER_JCL);
+ fputs(ppd->jcl_ps, fp);
+
+ return (0);
+}
+
+
+/*
+ * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp) /* I - File to write to */
+{
+ /*
+ * Range check the input...
+ */
+
+ if (!ppd)
+ return (0);
+
+ if (!ppd->jcl_end)
+ {
+ if (ppd->num_filters == 0)
+ putc(0x04, fp);
+
+ return (0);
+ }
+
+ /*
+ * See if the printer supports HP PJL...
+ */
+
+ if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
+ {
+ /*
+ * This printer uses HP PJL commands for output; filter the output
+ * so that we only have a single "@PJL JOB" command in the header...
+ *
+ * To avoid bugs in the PJL implementation of certain vendors' products
+ * (Xerox in particular), we add a dummy "@PJL" command at the beginning
+ * of the PJL commands to initialize PJL processing.
+ */
+
+ fputs("\033%-12345X@PJL\n", fp);
+ fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
+ fputs(ppd->jcl_end + 9, fp);
+ }
+ else
+ fputs(ppd->jcl_end, fp);
+
+ return (0);
+}
+
+
+/*
+ * 'ppdEmitString()' - Get a string containing the code for marked options.
+ *
+ * When "min_order" is greater than zero, this function only includes options
+ * whose OrderDependency value is greater than or equal to "min_order".
+ * Otherwise, all options in the specified section are included in the
+ * returned string.
+ *
+ * The return string is allocated on the heap and should be freed using
+ * @code free@ when you are done with it.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - String containing option code or @code NULL@ if there is no option code */
+ppdEmitString(ppd_file_t *ppd, /* I - PPD file record */
+ ppd_section_t section, /* I - Section to write */
+ float min_order) /* I - Lowest OrderDependency */
+{
+ int i, j, /* Looping vars */
+ count; /* Number of choices */
+ ppd_choice_t **choices; /* Choices */
+ ppd_size_t *size; /* Custom page size */
+ ppd_coption_t *coption; /* Custom option */
+ ppd_cparam_t *cparam; /* Custom parameter */
+ size_t bufsize; /* Size of string buffer needed */
+ char *buffer, /* String buffer */
+ *bufptr, /* Pointer into buffer */
+ *bufend; /* End of buffer */
+ struct lconv *loc; /* Locale data */
+
+
+ DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
+ ppd, section, min_order));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd)
+ return (NULL);
+
+ /*
+ * Use PageSize or PageRegion as required...
+ */
+
+ ppd_handle_media(ppd);
+
+ /*
+ * Collect the options we need to emit...
+ */
+
+ if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
+ return (NULL);
+
+ /*
+ * Count the number of bytes that are required to hold all of the
+ * option code...
+ */
+
+ for (i = 0, bufsize = 1; i < count; i ++)
+ {
+ if (section == PPD_ORDER_JCL)
+ {
+ if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
+ (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
+ != NULL)
+ {
+ /*
+ * Add space to account for custom parameter substitution...
+ */
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_POINTS :
+ case PPD_CUSTOM_REAL :
+ case PPD_CUSTOM_INT :
+ bufsize += 10;
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ if (cparam->current.custom_string)
+ bufsize += strlen(cparam->current.custom_string);
+ break;
+ }
+ }
+ }
+ }
+ else if (section != PPD_ORDER_EXIT)
+ {
+ bufsize += 3; /* [{\n */
+
+ if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
+ !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
+ !_cups_strcasecmp(choices[i]->choice, "Custom"))
+ {
+ DEBUG_puts("2ppdEmitString: Custom size set!");
+
+ bufsize += 37; /* %%BeginFeature: *CustomPageSize True\n */
+ bufsize += 50; /* Five 9-digit numbers + newline */
+ }
+ else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
+ (coption = ppdFindCustomOption(ppd,
+ choices[i]->option->keyword))
+ != NULL)
+ {
+ bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
+ /* %%BeginFeature: *Customkeyword True\n */
+
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_POINTS :
+ case PPD_CUSTOM_REAL :
+ case PPD_CUSTOM_INT :
+ bufsize += 10;
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ bufsize += 3;
+ if (cparam->current.custom_string)
+ bufsize += 4 * strlen(cparam->current.custom_string);
+ break;
+ }
+ }
+ }
+ else
+ bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
+ strlen(choices[i]->choice) + 1;
+ /* %%BeginFeature: *keyword choice\n */
+
+ bufsize += 13; /* %%EndFeature\n */
+ bufsize += 22; /* } stopped cleartomark\n */
+ }
+
+ if (choices[i]->code)
+ bufsize += strlen(choices[i]->code) + 1;
+ else
+ bufsize += strlen(ppd_custom_code);
+ }
+
+ /*
+ * Allocate memory...
+ */
+
+ DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
+ (int)bufsize));
+
+ if ((buffer = calloc(1, bufsize)) == NULL)
+ {
+ free(choices);
+ return (NULL);
+ }
+
+ bufend = buffer + bufsize - 1;
+ loc = localeconv();
+
+ /*
+ * Copy the option code to the buffer...
+ */
+
+ for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
+ if (section == PPD_ORDER_JCL)
+ {
+ if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
+ choices[i]->code &&
+ (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
+ != NULL)
+ {
+ /*
+ * Handle substitutions in custom JCL options...
+ */
+
+ char *cptr; /* Pointer into code */
+ int pnum; /* Parameter number */
+
+
+ for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
+ {
+ if (*cptr == '\\')
+ {
+ cptr ++;
+
+ if (isdigit(*cptr & 255))
+ {
+ /*
+ * Substitute parameter...
+ */
+
+ pnum = *cptr++ - '0';
+ while (isdigit(*cptr & 255))
+ pnum = pnum * 10 + *cptr++ - '0';
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ if (cparam->order == pnum)
+ break;
+
+ if (cparam)
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_POINTS :
+ case PPD_CUSTOM_REAL :
+ bufptr = _cupsStrFormatd(bufptr, bufend,
+ cparam->current.custom_real,
+ loc);
+ break;
+
+ case PPD_CUSTOM_INT :
+ snprintf(bufptr, bufend - bufptr, "%d",
+ cparam->current.custom_int);
+ bufptr += strlen(bufptr);
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ if (cparam->current.custom_string)
+ {
+ strlcpy(bufptr, cparam->current.custom_string,
+ bufend - bufptr);
+ bufptr += strlen(bufptr);
+ }
+ break;
+ }
+ }
+ }
+ else if (*cptr)
+ *bufptr++ = *cptr++;
+ }
+ else
+ *bufptr++ = *cptr++;
+ }
+ }
+ else
+ {
+ /*
+ * Otherwise just copy the option code directly...
+ */
+
+ strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
+ }
+ }
+ else if (section != PPD_ORDER_EXIT)
+ {
+ /*
+ * Add wrapper commands to prevent printer errors for unsupported
+ * options...
+ */
+
+ strlcpy(bufptr, "[{\n", bufend - bufptr + 1);
+ bufptr += 3;
+
+ /*
+ * Send DSC comments with option...
+ */
+
+ DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
+ choices[i]->option->keyword, choices[i]->choice));
+
+ if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
+ !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
+ !_cups_strcasecmp(choices[i]->choice, "Custom"))
+ {
+ /*
+ * Variable size; write out standard size options, using the
+ * parameter positions defined in the PPD file...
+ */
+
+ ppd_attr_t *attr; /* PPD attribute */
+ int pos, /* Position of custom value */
+ orientation; /* Orientation to use */
+ float values[5]; /* Values for custom command */
+
+
+ strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n",
+ bufend - bufptr + 1);
+ bufptr += 37;
+
+ size = ppdPageSize(ppd, "Custom");
+
+ memset(values, 0, sizeof(values));
+
+ if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
+ {
+ pos = atoi(attr->value) - 1;
+
+ if (pos < 0 || pos > 4)
+ pos = 0;
+ }
+ else
+ pos = 0;
+
+ values[pos] = size->width;
+
+ if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
+ {
+ pos = atoi(attr->value) - 1;
+
+ if (pos < 0 || pos > 4)
+ pos = 1;
+ }
+ else
+ pos = 1;
+
+ values[pos] = size->length;
+
+ /*
+ * According to the Adobe PPD specification, an orientation of 1
+ * will produce a print that comes out upside-down with the X
+ * axis perpendicular to the direction of feed, which is exactly
+ * what we want to be consistent with non-PS printers.
+ *
+ * We could also use an orientation of 3 to produce output that
+ * comes out rightside-up (this is the default for many large format
+ * printer PPDs), however for consistency we will stick with the
+ * value 1.
+ *
+ * If we wanted to get fancy, we could use orientations of 0 or
+ * 2 and swap the width and length, however we don't want to get
+ * fancy, we just want it to work consistently.
+ *
+ * The orientation value is range limited by the Orientation
+ * parameter definition, so certain non-PS printer drivers that
+ * only support an Orientation of 0 will get the value 0 as
+ * expected.
+ */
+
+ orientation = 1;
+
+ if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
+ "Orientation")) != NULL)
+ {
+ int min_orient, max_orient; /* Minimum and maximum orientations */
+
+
+ if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
+ &max_orient) != 3)
+ pos = 4;
+ else
+ {
+ pos --;
+
+ if (pos < 0 || pos > 4)
+ pos = 4;
+
+ if (orientation > max_orient)
+ orientation = max_orient;
+ else if (orientation < min_orient)
+ orientation = min_orient;
+ }
+ }
+ else
+ pos = 4;
+
+ values[pos] = (float)orientation;
+
+ for (pos = 0; pos < 5; pos ++)
+ {
+ bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
+ *bufptr++ = '\n';
+ }
+
+ if (!choices[i]->code)
+ {
+ /*
+ * This can happen with certain buggy PPD files that don't include
+ * a CustomPageSize command sequence... We just use a generic
+ * Level 2 command sequence...
+ */
+
+ strlcpy(bufptr, ppd_custom_code, bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
+ }
+ }
+ else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
+ (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
+ != NULL)
+ {
+ /*
+ * Custom option...
+ */
+
+ const char *s; /* Pointer into string value */
+ cups_array_t *params; /* Parameters in the correct output order */
+
+
+ params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ cupsArrayAdd(params, cparam);
+
+ snprintf(bufptr, bufend - bufptr + 1,
+ "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
+ bufptr += strlen(bufptr);
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_POINTS :
+ case PPD_CUSTOM_REAL :
+ bufptr = _cupsStrFormatd(bufptr, bufend,
+ cparam->current.custom_real, loc);
+ *bufptr++ = '\n';
+ break;
+
+ case PPD_CUSTOM_INT :
+ snprintf(bufptr, bufend - bufptr + 1, "%d\n",
+ cparam->current.custom_int);
+ bufptr += strlen(bufptr);
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ *bufptr++ = '(';
+
+ if (cparam->current.custom_string)
+ {
+ for (s = cparam->current.custom_string; *s; s ++)
+ {
+ if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
+ {
+ snprintf(bufptr, bufend - bufptr + 1, "\\%03o", *s & 255);
+ bufptr += strlen(bufptr);
+ }
+ else
+ *bufptr++ = *s;
+ }
+ }
+
+ *bufptr++ = ')';
+ *bufptr++ = '\n';
+ break;
+ }
+ }
+
+ cupsArrayDelete(params);
+ }
+ else
+ {
+ snprintf(bufptr, bufend - bufptr + 1, "%%%%BeginFeature: *%s %s\n",
+ choices[i]->option->keyword, choices[i]->choice);
+ bufptr += strlen(bufptr);
+ }
+
+ if (choices[i]->code && choices[i]->code[0])
+ {
+ j = (int)strlen(choices[i]->code);
+ memcpy(bufptr, choices[i]->code, j);
+ bufptr += j;
+
+ if (choices[i]->code[j - 1] != '\n')
+ *bufptr++ = '\n';
+ }
+
+ strlcpy(bufptr, "%%EndFeature\n"
+ "} stopped cleartomark\n", bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
+
+ DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
+ (int)(bufptr - buffer)));
+ }
+ else
+ {
+ strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
+ }
+
+ /*
+ * Nul-terminate, free, and return...
+ */
+
+ *bufptr = '\0';
+
+ free(choices);
+
+ return (buffer);
+}
+
+
+/*
+ * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
+ */
+
+static int /* O - Result of comparison */
+ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
+ ppd_cparam_t *b) /* I - Second parameter */
+{
+ return (a->order - b->order);
+}
+
+
+/*
+ * 'ppd_handle_media()' - Handle media selection...
+ */
+
+static void
+ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
+{
+ ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
+ *input_slot; /* InputSlot choice, if any */
+ ppd_size_t *size; /* Current media size */
+ ppd_attr_t *rpr; /* RequiresPageRegion value */
+
+
+ /*
+ * This function determines what page size code to use, if any, for the
+ * current media size, InputSlot, and ManualFeed selections.
+ *
+ * We use the PageSize code if:
+ *
+ * 1. A custom media size is selected.
+ * 2. ManualFeed and InputSlot are not selected (or do not exist).
+ * 3. ManualFeed is selected but is False and InputSlot is not selected or
+ * the selection has no code - the latter check done to support "auto" or
+ * "printer default" InputSlot options.
+ *
+ * We use the PageRegion code if:
+ *
+ * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
+ * keywords, indicating this is a CUPS-based driver.
+ * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
+ * InputSlot or ManualFeed selection) and is True.
+ *
+ * If none of the 5 conditions are true, no page size code is used and we
+ * unmark any existing PageSize or PageRegion choices.
+ */
+
+ if ((size = ppdPageSize(ppd, NULL)) == NULL)
+ return;
+
+ manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
+ input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
+
+ if (input_slot != NULL)
+ rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
+ else
+ rpr = NULL;
+
+ if (!rpr)
+ rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
+
+ if (!_cups_strcasecmp(size->name, "Custom") ||
+ (!manual_feed && !input_slot) ||
+ (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
+ (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
+ (!rpr && ppd->num_filters > 0))
+ {
+ /*
+ * Use PageSize code...
+ */
+
+ ppdMarkOption(ppd, "PageSize", size->name);
+ }
+ else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
+ {
+ /*
+ * Use PageRegion code...
+ */
+
+ ppdMarkOption(ppd, "PageRegion", size->name);
+ }
+ else
+ {
+ /*
+ * Do not use PageSize or PageRegion code...
+ */
+
+ ppd_choice_t *page; /* PageSize/Region choice, if any */
+
+ if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
+ {
+ /*
+ * Unmark PageSize...
+ */
+
+ page->marked = 0;
+ cupsArrayRemove(ppd->marked, page);
+ }
+
+ if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
+ {
+ /*
+ * Unmark PageRegion...
+ */
+
+ page->marked = 0;
+ cupsArrayRemove(ppd->marked, page);
+ }
+ }
+}
+
+
+/*
+ * End of "$Id: emit.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/encode.c b/cups/libs/cups/encode.c
new file mode 100644
index 000000000..dcee8f65e
--- /dev/null
+++ b/cups/libs/cups/encode.c
@@ -0,0 +1,851 @@
+/*
+ * "$Id: encode.c 11867 2014-05-09 20:33:08Z msweet $"
+ *
+ * Option encoding routines for CUPS.
+ *
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local list of option names, the value tags they should use, and the list of
+ * supported operations...
+ *
+ * **** THIS LIST MUST BE SORTED BY ATTRIBUTE NAME ****
+ */
+
+static const ipp_op_t ipp_job_creation[] =
+{
+ IPP_OP_PRINT_JOB,
+ IPP_OP_PRINT_URI,
+ IPP_OP_VALIDATE_JOB,
+ IPP_OP_CREATE_JOB,
+ IPP_OP_HOLD_JOB,
+ IPP_OP_SET_JOB_ATTRIBUTES,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t ipp_doc_creation[] =
+{
+ IPP_OP_PRINT_JOB,
+ IPP_OP_PRINT_URI,
+ IPP_OP_SEND_DOCUMENT,
+ IPP_OP_SEND_URI,
+ IPP_OP_SET_JOB_ATTRIBUTES,
+ IPP_OP_SET_DOCUMENT_ATTRIBUTES,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t ipp_sub_creation[] =
+{
+ IPP_OP_PRINT_JOB,
+ IPP_OP_PRINT_URI,
+ IPP_OP_CREATE_JOB,
+ IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,
+ IPP_OP_CREATE_JOB_SUBSCRIPTIONS,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t ipp_all_print[] =
+{
+ IPP_OP_PRINT_JOB,
+ IPP_OP_PRINT_URI,
+ IPP_OP_VALIDATE_JOB,
+ IPP_OP_CREATE_JOB,
+ IPP_OP_SEND_DOCUMENT,
+ IPP_OP_SEND_URI,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t ipp_set_printer[] =
+{
+ IPP_OP_SET_PRINTER_ATTRIBUTES,
+ IPP_OP_CUPS_ADD_MODIFY_PRINTER,
+ IPP_OP_CUPS_ADD_MODIFY_CLASS,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t cups_schemes[] =
+{
+ IPP_OP_CUPS_GET_DEVICES,
+ IPP_OP_CUPS_GET_PPDS,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t cups_get_ppds[] =
+{
+ IPP_OP_CUPS_GET_PPDS,
+ IPP_OP_CUPS_NONE
+};
+
+static const ipp_op_t cups_ppd_name[] =
+{
+ IPP_OP_CUPS_ADD_MODIFY_PRINTER,
+ IPP_OP_CUPS_GET_PPD,
+ IPP_OP_CUPS_NONE
+};
+
+static const _ipp_option_t ipp_options[] =
+{
+ { 1, "auth-info", IPP_TAG_TEXT, IPP_TAG_JOB },
+ { 1, "auth-info-default", IPP_TAG_TEXT, IPP_TAG_PRINTER },
+ { 1, "auth-info-required", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 0, "blackplot", IPP_TAG_BOOLEAN, IPP_TAG_JOB },
+ { 0, "blackplot-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "brightness", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "brightness-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "columns", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "columns-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "compression", IPP_TAG_KEYWORD, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ ipp_doc_creation },
+ { 0, "copies", IPP_TAG_INTEGER, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "copies-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "device-uri", IPP_TAG_URI, IPP_TAG_PRINTER },
+ { 1, "document-copies", IPP_TAG_RANGE, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT,
+ ipp_doc_creation },
+ { 0, "document-format", IPP_TAG_MIMETYPE, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ ipp_doc_creation },
+ { 0, "document-format-default", IPP_TAG_MIMETYPE, IPP_TAG_PRINTER },
+ { 1, "document-numbers", IPP_TAG_RANGE, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT,
+ ipp_all_print },
+ { 1, "exclude-schemes", IPP_TAG_NAME, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_schemes },
+ { 1, "finishings", IPP_TAG_ENUM, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 1, "finishings-default", IPP_TAG_ENUM, IPP_TAG_PRINTER },
+ { 0, "fit-to-page", IPP_TAG_BOOLEAN, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "fit-to-page-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "fitplot", IPP_TAG_BOOLEAN, IPP_TAG_JOB },
+ { 0, "fitplot-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "gamma", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "gamma-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "hue", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "hue-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "include-schemes", IPP_TAG_NAME, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_schemes },
+ { 0, "job-account-id", IPP_TAG_NAME, IPP_TAG_JOB },
+ { 0, "job-account-id-default",IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "job-accounting-user-id", IPP_TAG_NAME, IPP_TAG_JOB },
+ { 0, "job-accounting-user-id-default", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "job-authorization-uri", IPP_TAG_URI, IPP_TAG_OPERATION },
+ { 0, "job-hold-until", IPP_TAG_KEYWORD, IPP_TAG_JOB },
+ { 0, "job-id", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-impressions", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-impressions-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-k-limit", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "job-k-octets", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-k-octets-completed",IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-media-sheets", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-media-sheets-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-page-limit", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "job-password", IPP_TAG_STRING, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ ipp_job_creation },
+ { 0, "job-password-encryption", IPP_TAG_KEYWORD, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ ipp_job_creation },
+ { 0, "job-priority", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "job-quota-period", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "job-sheets", IPP_TAG_NAME, IPP_TAG_JOB },
+ { 1, "job-sheets-default", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "job-state", IPP_TAG_ENUM, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-state-message", IPP_TAG_TEXT, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-state-reasons", IPP_TAG_KEYWORD, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "job-uuid", IPP_TAG_URI, IPP_TAG_JOB },
+ { 0, "landscape", IPP_TAG_BOOLEAN, IPP_TAG_JOB },
+ { 1, "marker-change-time", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "marker-colors", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 1, "marker-high-levels", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "marker-levels", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "marker-low-levels", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "marker-message", IPP_TAG_TEXT, IPP_TAG_PRINTER },
+ { 1, "marker-names", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 1, "marker-types", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 1, "media", IPP_TAG_KEYWORD, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "media-col", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "media-col-default", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_PRINTER },
+ { 0, "media-color", IPP_TAG_KEYWORD, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 1, "media-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 0, "media-key", IPP_TAG_KEYWORD, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "media-size", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "media-type", IPP_TAG_KEYWORD, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "mirror", IPP_TAG_BOOLEAN, IPP_TAG_JOB },
+ { 0, "mirror-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "natural-scaling", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "natural-scaling-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "notify-charset", IPP_TAG_CHARSET, IPP_TAG_SUBSCRIPTION },
+ { 1, "notify-events", IPP_TAG_KEYWORD, IPP_TAG_SUBSCRIPTION },
+ { 1, "notify-events-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 0, "notify-lease-duration", IPP_TAG_INTEGER, IPP_TAG_SUBSCRIPTION },
+ { 0, "notify-lease-duration-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "notify-natural-language", IPP_TAG_LANGUAGE, IPP_TAG_SUBSCRIPTION },
+ { 0, "notify-pull-method", IPP_TAG_KEYWORD, IPP_TAG_SUBSCRIPTION },
+ { 0, "notify-recipient-uri", IPP_TAG_URI, IPP_TAG_SUBSCRIPTION },
+ { 0, "notify-time-interval", IPP_TAG_INTEGER, IPP_TAG_SUBSCRIPTION },
+ { 0, "notify-user-data", IPP_TAG_STRING, IPP_TAG_SUBSCRIPTION },
+ { 0, "number-up", IPP_TAG_INTEGER, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "number-up-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "orientation-requested", IPP_TAG_ENUM, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "orientation-requested-default", IPP_TAG_ENUM, IPP_TAG_PRINTER },
+ { 1, "overrides", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "page-bottom", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "page-bottom-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "page-left", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "page-left-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "page-ranges", IPP_TAG_RANGE, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 1, "page-ranges-default", IPP_TAG_RANGE, IPP_TAG_PRINTER },
+ { 0, "page-right", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "page-right-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "page-top", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "page-top-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "pages", IPP_TAG_RANGE, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "penwidth", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "penwidth-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "port-monitor", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "ppd-device-id", IPP_TAG_TEXT, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-make", IPP_TAG_TEXT, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-make-and-model", IPP_TAG_TEXT, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-model-number", IPP_TAG_INTEGER, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-name", IPP_TAG_NAME, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_ppd_name },
+ { 0, "ppd-natural-language", IPP_TAG_LANGUAGE, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-product", IPP_TAG_TEXT, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-psversion", IPP_TAG_TEXT, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppd-type", IPP_TAG_KEYWORD, IPP_TAG_OPERATION,
+ IPP_TAG_ZERO,
+ cups_get_ppds },
+ { 0, "ppi", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "ppi-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "prettyprint", IPP_TAG_BOOLEAN, IPP_TAG_JOB },
+ { 0, "prettyprint-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "print-quality", IPP_TAG_ENUM, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "print-quality-default", IPP_TAG_ENUM, IPP_TAG_PRINTER },
+ { 1, "printer-commands", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 0, "printer-error-policy", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "printer-info", IPP_TAG_TEXT, IPP_TAG_PRINTER },
+ { 0, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "printer-is-shared", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "printer-location", IPP_TAG_TEXT, IPP_TAG_PRINTER },
+ { 0, "printer-make-and-model", IPP_TAG_TEXT, IPP_TAG_PRINTER },
+ { 0, "printer-more-info", IPP_TAG_URI, IPP_TAG_PRINTER },
+ { 0, "printer-op-policy", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "printer-resolution", IPP_TAG_RESOLUTION, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "printer-state", IPP_TAG_ENUM, IPP_TAG_PRINTER },
+ { 0, "printer-state-change-time", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 1, "printer-state-reasons", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 0, "printer-type", IPP_TAG_ENUM, IPP_TAG_PRINTER },
+ { 0, "printer-uri", IPP_TAG_URI, IPP_TAG_OPERATION },
+ { 1, "printer-uri-supported", IPP_TAG_URI, IPP_TAG_PRINTER },
+ { 0, "queued-job-count", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "raw", IPP_TAG_MIMETYPE, IPP_TAG_OPERATION },
+ { 1, "requested-attributes", IPP_TAG_NAME, IPP_TAG_OPERATION },
+ { 1, "requesting-user-name-allowed", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 1, "requesting-user-name-denied", IPP_TAG_NAME, IPP_TAG_PRINTER },
+ { 0, "resolution", IPP_TAG_RESOLUTION, IPP_TAG_JOB },
+ { 0, "resolution-default", IPP_TAG_RESOLUTION, IPP_TAG_PRINTER },
+ { 0, "saturation", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "saturation-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "scaling", IPP_TAG_INTEGER, IPP_TAG_JOB },
+ { 0, "scaling-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
+ { 0, "sides", IPP_TAG_KEYWORD, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "sides-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
+ { 0, "time-at-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "time-at-creation", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "time-at-processing", IPP_TAG_INTEGER, IPP_TAG_ZERO }, /* never send as option */
+ { 0, "wrap", IPP_TAG_BOOLEAN, IPP_TAG_JOB },
+ { 0, "wrap-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER },
+ { 0, "x-dimension", IPP_TAG_INTEGER, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT },
+ { 0, "y-dimension", IPP_TAG_INTEGER, IPP_TAG_JOB,
+ IPP_TAG_DOCUMENT }
+};
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_ipp_options(_ipp_option_t *a, _ipp_option_t *b);
+
+
+/*
+ * '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
+ * for a single group.
+ */
+
+void
+cupsEncodeOptions(ipp_t *ipp, /* I - Request to add to */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ DEBUG_printf(("cupsEncodeOptions(%p, %d, %p)", ipp, num_options, options));
+
+ /*
+ * Add the options in the proper groups & order...
+ */
+
+ cupsEncodeOptions2(ipp, num_options, options, IPP_TAG_OPERATION);
+ cupsEncodeOptions2(ipp, num_options, options, IPP_TAG_JOB);
+ cupsEncodeOptions2(ipp, num_options, options, IPP_TAG_SUBSCRIPTION);
+}
+
+
+/*
+ * '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()
+ * to add the standard groups.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+cupsEncodeOptions2(
+ ipp_t *ipp, /* I - Request to add to */
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ ipp_tag_t group_tag) /* I - Group to encode */
+{
+ int i, j; /* Looping vars */
+ int count; /* Number of values */
+ char *s, /* Pointer into option value */
+ *val, /* Pointer to option value */
+ *copy, /* Copy of option value */
+ *sep, /* Option separator */
+ quote; /* Quote character */
+ ipp_attribute_t *attr; /* IPP attribute */
+ ipp_tag_t value_tag; /* IPP value tag */
+ cups_option_t *option; /* Current option */
+ ipp_t *collection; /* Collection value */
+ int num_cols; /* Number of collection values */
+ cups_option_t *cols; /* Collection values */
+ ipp_op_t op; /* Operation for this request */
+ const ipp_op_t *ops; /* List of allowed operations */
+
+
+ DEBUG_printf(("cupsEncodeOptions2(ipp=%p(%s), num_options=%d, options=%p, "
+ "group_tag=%x)", ipp,
+ ipp ? ippOpString(ippGetOperation(ipp)) : "", num_options,
+ options, group_tag));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || num_options < 1 || !options)
+ return;
+
+ /*
+ * Do special handling for the document-format/raw options...
+ */
+
+ op = ippGetOperation(ipp);
+
+ if (group_tag == IPP_TAG_OPERATION &&
+ (op == IPP_OP_PRINT_JOB || op == IPP_OP_PRINT_URI ||
+ op == IPP_OP_SEND_DOCUMENT || op == IPP_OP_SEND_URI))
+ {
+ /*
+ * Handle the document format stuff first...
+ */
+
+ if ((val = (char *)cupsGetOption("document-format", num_options,
+ options)) != NULL)
+ ippAddString(ipp, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, val);
+ else if (cupsGetOption("raw", num_options, options))
+ ippAddString(ipp, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/vnd.cups-raw");
+ else
+ ippAddString(ipp, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/octet-stream");
+ }
+
+ /*
+ * Then loop through the options...
+ */
+
+ for (i = num_options, option = options; i > 0; i --, option ++)
+ {
+ _ipp_option_t *match; /* Matching attribute */
+
+
+ /*
+ * Skip document format options that are handled above...
+ */
+
+ if (!_cups_strcasecmp(option->name, "raw") ||
+ !_cups_strcasecmp(option->name, "document-format") ||
+ !option->name[0])
+ continue;
+
+ /*
+ * Figure out the proper value and group tags for this option...
+ */
+
+ if ((match = _ippFindOption(option->name)) != NULL)
+ {
+ if (match->group_tag != group_tag && match->alt_group_tag != group_tag)
+ continue;
+
+ value_tag = match->value_tag;
+
+ if (match->operations)
+ ops = match->operations;
+ else if (group_tag == IPP_TAG_JOB)
+ ops = ipp_job_creation;
+ else if (group_tag == IPP_TAG_DOCUMENT)
+ ops = ipp_doc_creation;
+ else if (group_tag == IPP_TAG_SUBSCRIPTION)
+ ops = ipp_sub_creation;
+ else if (group_tag == IPP_TAG_PRINTER)
+ ops = ipp_set_printer;
+ else
+ {
+ DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
+ continue;
+ }
+ }
+ else
+ {
+ int namelen; /* Length of name */
+
+
+ namelen = (int)strlen(option->name);
+
+ if (namelen < 10 ||
+ (strcmp(option->name + namelen - 8, "-default") &&
+ strcmp(option->name + namelen - 10, "-supported")))
+ {
+ if (group_tag != IPP_TAG_JOB && group_tag != IPP_TAG_DOCUMENT)
+ {
+ DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
+ continue;
+ }
+ }
+ else if (group_tag != IPP_TAG_PRINTER)
+ {
+ DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
+ continue;
+ }
+
+ if (group_tag == IPP_TAG_JOB)
+ ops = ipp_job_creation;
+ else if (group_tag == IPP_TAG_DOCUMENT)
+ ops = ipp_doc_creation;
+ else
+ ops = ipp_set_printer;
+
+ if (!_cups_strcasecmp(option->value, "true") ||
+ !_cups_strcasecmp(option->value, "false"))
+ value_tag = IPP_TAG_BOOLEAN;
+ else
+ value_tag = IPP_TAG_NAME;
+ }
+
+ /*
+ * Verify that we send this attribute for this operation...
+ */
+
+ while (*ops != IPP_OP_CUPS_NONE)
+ if (op == *ops)
+ break;
+ else
+ ops ++;
+
+ if (*ops == IPP_OP_CUPS_NONE && op != IPP_OP_CUPS_NONE)
+ {
+ DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
+ continue;
+ }
+
+ /*
+ * Count the number of values...
+ */
+
+ if (match && match->multivalue)
+ {
+ for (count = 1, sep = option->value, quote = 0; *sep; sep ++)
+ {
+ if (*sep == quote)
+ quote = 0;
+ else if (!quote && (*sep == '\'' || *sep == '\"'))
+ {
+ /*
+ * Skip quoted option value...
+ */
+
+ quote = *sep++;
+ }
+ else if (*sep == ',' && !quote)
+ count ++;
+ else if (*sep == '\\' && sep[1])
+ sep ++;
+ }
+ }
+ else
+ count = 1;
+
+ DEBUG_printf(("2cupsEncodeOptions2: option=\"%s\", count=%d",
+ option->name, count));
+
+ /*
+ * Allocate memory for the attribute values...
+ */
+
+ if ((attr = ippAddStrings(ipp, group_tag, value_tag, option->name, count,
+ NULL, NULL)) == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ DEBUG_puts("1cupsEncodeOptions2: Ran out of memory for attributes!");
+ return;
+ }
+
+ if (count > 1)
+ {
+ /*
+ * Make a copy of the value we can fiddle with...
+ */
+
+ if ((copy = strdup(option->value)) == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ DEBUG_puts("1cupsEncodeOptions2: Ran out of memory for value copy!");
+ ippDeleteAttribute(ipp, attr);
+ return;
+ }
+
+ val = copy;
+ }
+ else
+ {
+ /*
+ * Since we have a single value, use the value directly...
+ */
+
+ val = option->value;
+ copy = NULL;
+ }
+
+ /*
+ * Scan the value string for values...
+ */
+
+ for (j = 0, sep = val; j < count; val = sep, j ++)
+ {
+ /*
+ * Find the end of this value and mark it if needed...
+ */
+
+ if (count > 1)
+ {
+ for (quote = 0; *sep; sep ++)
+ {
+ if (*sep == quote)
+ {
+ /*
+ * Finish quoted value...
+ */
+
+ quote = 0;
+ }
+ else if (!quote && (*sep == '\'' || *sep == '\"'))
+ {
+ /*
+ * Handle quoted option value...
+ */
+
+ quote = *sep;
+ }
+ else if (*sep == ',' && count > 1)
+ break;
+ else if (*sep == '\\' && sep[1])
+ {
+ /*
+ * Skip quoted character...
+ */
+
+ sep ++;
+ }
+ }
+
+ if (*sep == ',')
+ *sep++ = '\0';
+ }
+
+ /*
+ * Copy the option value(s) over as needed by the type...
+ */
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ /*
+ * Integer/enumeration value...
+ */
+
+ attr->values[j].integer = strtol(val, &s, 10);
+
+ DEBUG_printf(("2cupsEncodeOptions2: Added integer option value "
+ "%d...", attr->values[j].integer));
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (!_cups_strcasecmp(val, "true") ||
+ !_cups_strcasecmp(val, "on") ||
+ !_cups_strcasecmp(val, "yes"))
+ {
+ /*
+ * Boolean value - true...
+ */
+
+ attr->values[j].boolean = 1;
+
+ DEBUG_puts("2cupsEncodeOptions2: Added boolean true value...");
+ }
+ else
+ {
+ /*
+ * Boolean value - false...
+ */
+
+ attr->values[j].boolean = 0;
+
+ DEBUG_puts("2cupsEncodeOptions2: Added boolean false value...");
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ /*
+ * Range...
+ */
+
+ if (*val == '-')
+ {
+ attr->values[j].range.lower = 1;
+ s = val;
+ }
+ else
+ attr->values[j].range.lower = strtol(val, &s, 10);
+
+ if (*s == '-')
+ {
+ if (s[1])
+ attr->values[j].range.upper = strtol(s + 1, NULL, 10);
+ else
+ attr->values[j].range.upper = 2147483647;
+ }
+ else
+ attr->values[j].range.upper = attr->values[j].range.lower;
+
+ DEBUG_printf(("2cupsEncodeOptions2: Added range option value "
+ "%d-%d...", attr->values[j].range.lower,
+ attr->values[j].range.upper));
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ /*
+ * Resolution...
+ */
+
+ attr->values[j].resolution.xres = strtol(val, &s, 10);
+
+ if (*s == 'x')
+ attr->values[j].resolution.yres = strtol(s + 1, &s, 10);
+ else
+ attr->values[j].resolution.yres = attr->values[j].resolution.xres;
+
+ if (!_cups_strcasecmp(s, "dpc") ||
+ !_cups_strcasecmp(s, "dpcm"))
+ attr->values[j].resolution.units = IPP_RES_PER_CM;
+ else
+ attr->values[j].resolution.units = IPP_RES_PER_INCH;
+
+ DEBUG_printf(("2cupsEncodeOptions2: Added resolution option value "
+ "%s...", val));
+ break;
+
+ case IPP_TAG_STRING :
+ /*
+ * octet-string
+ */
+
+ attr->values[j].unknown.length = (int)strlen(val);
+ attr->values[j].unknown.data = strdup(val);
+
+ DEBUG_printf(("2cupsEncodeOptions2: Added octet-string value "
+ "\"%s\"...", (char *)attr->values[j].unknown.data));
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ /*
+ * Collection value
+ */
+
+ num_cols = cupsParseOptions(val, 0, &cols);
+ if ((collection = ippNew()) == NULL)
+ {
+ cupsFreeOptions(num_cols, cols);
+
+ if (copy)
+ free(copy);
+
+ ippDeleteAttribute(ipp, attr);
+ return;
+ }
+
+ attr->values[j].collection = collection;
+ cupsEncodeOptions2(collection, num_cols, cols, IPP_TAG_JOB);
+ cupsFreeOptions(num_cols, cols);
+ break;
+
+ default :
+ if ((attr->values[j].string.text = _cupsStrAlloc(val)) == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ DEBUG_puts("1cupsEncodeOptions2: Ran out of memory for string!");
+
+ if (copy)
+ free(copy);
+
+ ippDeleteAttribute(ipp, attr);
+ return;
+ }
+
+ DEBUG_printf(("2cupsEncodeOptions2: Added string value \"%s\"...",
+ val));
+ break;
+ }
+ }
+
+ if (copy)
+ free(copy);
+ }
+}
+
+
+#ifdef DEBUG
+/*
+ * '_ippCheckOptions()' - Validate that the option array is sorted properly.
+ */
+
+const char * /* O - First out-of-order option or NULL */
+_ippCheckOptions(void)
+{
+ int i; /* Looping var */
+
+
+ for (i = 0; i < (int)(sizeof(ipp_options) / sizeof(ipp_options[0]) - 1); i ++)
+ if (strcmp(ipp_options[i].name, ipp_options[i + 1].name) >= 0)
+ return (ipp_options[i + 1].name);
+
+ return (NULL);
+}
+#endif /* DEBUG */
+
+
+/*
+ * '_ippFindOption()' - Find the attribute information for an option.
+ */
+
+_ipp_option_t * /* O - Attribute information */
+_ippFindOption(const char *name) /* I - Option/attribute name */
+{
+ _ipp_option_t key; /* Search key */
+
+
+ /*
+ * Lookup the proper value and group tags for this option...
+ */
+
+ key.name = name;
+
+ return ((_ipp_option_t *)bsearch(&key, ipp_options,
+ sizeof(ipp_options) / sizeof(ipp_options[0]),
+ sizeof(ipp_options[0]),
+ (int (*)(const void *, const void *))
+ compare_ipp_options));
+}
+
+
+/*
+ * 'compare_ipp_options()' - Compare two IPP options.
+ */
+
+static int /* O - Result of comparison */
+compare_ipp_options(_ipp_option_t *a, /* I - First option */
+ _ipp_option_t *b) /* I - Second option */
+{
+ return (strcmp(a->name, b->name));
+}
+
+
+/*
+ * End of "$Id: encode.c 11867 2014-05-09 20:33:08Z msweet $".
+ */
diff --git a/cups/libs/cups/file-private.h b/cups/libs/cups/file-private.h
new file mode 100644
index 000000000..bc147ff8f
--- /dev/null
+++ b/cups/libs/cups/file-private.h
@@ -0,0 +1,139 @@
+/*
+ * "$Id: file-private.h 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * Private file definitions for CUPS.
+ *
+ * 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.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_FILE_PRIVATE_H_
+# define _CUPS_FILE_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "cups-private.h"
+# include <stdio.h>
+# include <stdlib.h>
+# include <stdarg.h>
+# include <fcntl.h>
+
+# ifdef HAVE_LIBZ
+# include <zlib.h>
+# endif /* HAVE_LIBZ */
+# ifdef WIN32
+# include <io.h>
+# include <sys/locking.h>
+# endif /* WIN32 */
+
+
+/*
+ * Some operating systems support large files via open flag O_LARGEFILE...
+ */
+
+# ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+# endif /* !O_LARGEFILE */
+
+
+/*
+ * Some operating systems don't define O_BINARY, which is used by Microsoft
+ * and IBM to flag binary files...
+ */
+
+# ifndef O_BINARY
+# define O_BINARY 0
+# endif /* !O_BINARY */
+
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum /**** _cupsFileCheck return values ****/
+{
+ _CUPS_FILE_CHECK_OK = 0, /* Everything OK */
+ _CUPS_FILE_CHECK_MISSING = 1, /* File is missing */
+ _CUPS_FILE_CHECK_PERMISSIONS = 2, /* File (or parent dir) has bad perms */
+ _CUPS_FILE_CHECK_WRONG_TYPE = 3, /* File has wrong type */
+ _CUPS_FILE_CHECK_RELATIVE_PATH = 4 /* File contains a relative path */
+} _cups_fc_result_t;
+
+typedef enum /**** _cupsFileCheck file type values ****/
+{
+ _CUPS_FILE_CHECK_FILE = 0, /* Check the file and parent directory */
+ _CUPS_FILE_CHECK_PROGRAM = 1, /* Check the program and parent directory */
+ _CUPS_FILE_CHECK_FILE_ONLY = 2, /* Check the file only */
+ _CUPS_FILE_CHECK_DIRECTORY = 3 /* Check the directory */
+} _cups_fc_filetype_t;
+
+typedef void (*_cups_fc_func_t)(void *context, _cups_fc_result_t result,
+ const char *message);
+
+struct _cups_file_s /**** CUPS file structure... ****/
+
+{
+ int fd; /* File descriptor */
+ char mode, /* Mode ('r' or 'w') */
+ compressed, /* Compression used? */
+ is_stdio, /* stdin/out/err? */
+ eof, /* End of file? */
+ buf[4096], /* Buffer */
+ *ptr, /* Pointer into buffer */
+ *end; /* End of buffer data */
+ off_t pos, /* Position in file */
+ bufpos; /* File position for start of buffer */
+
+#ifdef HAVE_LIBZ
+ z_stream stream; /* (De)compression stream */
+ Bytef cbuf[4096]; /* (De)compression buffer */
+ uLong crc; /* (De)compression CRC */
+#endif /* HAVE_LIBZ */
+
+ char *printf_buffer; /* cupsFilePrintf buffer */
+ size_t printf_size; /* Size of cupsFilePrintf buffer */
+};
+
+
+/*
+ * Prototypes...
+ */
+
+extern _cups_fc_result_t _cupsFileCheck(const char *filename,
+ _cups_fc_filetype_t filetype,
+ int dorootchecks,
+ _cups_fc_func_t cb,
+ void *context);
+extern void _cupsFileCheckFilter(void *context,
+ _cups_fc_result_t result,
+ const char *message);
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_FILE_PRIVATE_H_ */
+
+/*
+ * End of "$Id: file-private.h 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/file.c b/cups/libs/cups/file.c
new file mode 100644
index 000000000..e0ea5cef7
--- /dev/null
+++ b/cups/libs/cups/file.c
@@ -0,0 +1,2676 @@
+/*
+ * "$Id: file.c 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * File functions for CUPS.
+ *
+ * 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.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "file-private.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef HAVE_LIBZ
+static ssize_t cups_compress(cups_file_t *fp, const char *buf, size_t bytes);
+#endif /* HAVE_LIBZ */
+static ssize_t cups_fill(cups_file_t *fp);
+static int cups_open(const char *filename, int mode);
+static ssize_t cups_read(cups_file_t *fp, char *buf, size_t bytes);
+static ssize_t cups_write(cups_file_t *fp, const char *buf, size_t bytes);
+
+
+#ifndef WIN32
+/*
+ * '_cupsFileCheck()' - Check the permissions of the given filename.
+ */
+
+_cups_fc_result_t /* O - Check result */
+_cupsFileCheck(
+ const char *filename, /* I - Filename to check */
+ _cups_fc_filetype_t filetype, /* I - Type of file checks? */
+ int dorootchecks, /* I - Check for root permissions? */
+ _cups_fc_func_t cb, /* I - Callback function */
+ void *context) /* I - Context pointer for callback */
+
+{
+ struct stat fileinfo; /* File information */
+ char message[1024], /* Message string */
+ temp[1024], /* Parent directory filename */
+ *ptr; /* Pointer into parent directory */
+ _cups_fc_result_t result; /* Check result */
+
+
+ /*
+ * Does the filename contain a relative path ("../")?
+ */
+
+ if (strstr(filename, "../"))
+ {
+ /*
+ * Yes, fail it!
+ */
+
+ result = _CUPS_FILE_CHECK_RELATIVE_PATH;
+ goto finishup;
+ }
+
+ /*
+ * Does the program even exist and is it accessible?
+ */
+
+ if (stat(filename, &fileinfo))
+ {
+ /*
+ * Nope...
+ */
+
+ result = _CUPS_FILE_CHECK_MISSING;
+ goto finishup;
+ }
+
+ /*
+ * Check the execute bit...
+ */
+
+ result = _CUPS_FILE_CHECK_OK;
+
+ switch (filetype)
+ {
+ case _CUPS_FILE_CHECK_DIRECTORY :
+ if (!S_ISDIR(fileinfo.st_mode))
+ result = _CUPS_FILE_CHECK_WRONG_TYPE;
+ break;
+
+ default :
+ if (!S_ISREG(fileinfo.st_mode))
+ result = _CUPS_FILE_CHECK_WRONG_TYPE;
+ break;
+ }
+
+ if (result)
+ goto finishup;
+
+ /*
+ * Are we doing root checks?
+ */
+
+ if (!dorootchecks)
+ {
+ /*
+ * Nope, so anything (else) goes...
+ */
+
+ goto finishup;
+ }
+
+ /*
+ * Verify permission of the file itself:
+ *
+ * 1. Must be owned by root
+ * 2. Must not be writable by group
+ * 3. Must not be setuid
+ * 4. Must not be writable by others
+ */
+
+ if (fileinfo.st_uid || /* 1. Must be owned by root */
+ (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */
+ (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */
+ (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */
+ {
+ result = _CUPS_FILE_CHECK_PERMISSIONS;
+ goto finishup;
+ }
+
+ if (filetype == _CUPS_FILE_CHECK_DIRECTORY ||
+ filetype == _CUPS_FILE_CHECK_FILE_ONLY)
+ goto finishup;
+
+ /*
+ * Now check the containing directory...
+ */
+
+ strlcpy(temp, filename, sizeof(temp));
+ if ((ptr = strrchr(temp, '/')) != NULL)
+ {
+ if (ptr == temp)
+ ptr[1] = '\0';
+ else
+ *ptr = '\0';
+ }
+
+ if (stat(temp, &fileinfo))
+ {
+ /*
+ * Doesn't exist?!?
+ */
+
+ result = _CUPS_FILE_CHECK_MISSING;
+ filetype = _CUPS_FILE_CHECK_DIRECTORY;
+ filename = temp;
+
+ goto finishup;
+ }
+
+ if (fileinfo.st_uid || /* 1. Must be owned by root */
+ (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */
+ (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */
+ (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */
+ {
+ result = _CUPS_FILE_CHECK_PERMISSIONS;
+ filetype = _CUPS_FILE_CHECK_DIRECTORY;
+ filename = temp;
+ }
+
+ /*
+ * Common return point...
+ */
+
+ finishup:
+
+ if (cb)
+ {
+ cups_lang_t *lang = cupsLangDefault();
+ /* Localization information */
+
+ switch (result)
+ {
+ case _CUPS_FILE_CHECK_OK :
+ if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("Directory \"%s\" permissions OK "
+ "(0%o/uid=%d/gid=%d).")),
+ filename, fileinfo.st_mode, (int)fileinfo.st_uid,
+ (int)fileinfo.st_gid);
+ else
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("File \"%s\" permissions OK "
+ "(0%o/uid=%d/gid=%d).")),
+ filename, fileinfo.st_mode, (int)fileinfo.st_uid,
+ (int)fileinfo.st_gid);
+ break;
+
+ case _CUPS_FILE_CHECK_MISSING :
+ if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("Directory \"%s\" not available: "
+ "%s")),
+ filename, strerror(errno));
+ else
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("File \"%s\" not available: %s")),
+ filename, strerror(errno));
+ break;
+
+ case _CUPS_FILE_CHECK_PERMISSIONS :
+ if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("Directory \"%s\" has insecure "
+ "permissions "
+ "(0%o/uid=%d/gid=%d).")),
+ filename, fileinfo.st_mode, (int)fileinfo.st_uid,
+ (int)fileinfo.st_gid);
+ else
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("File \"%s\" has insecure "
+ "permissions "
+ "(0%o/uid=%d/gid=%d).")),
+ filename, fileinfo.st_mode, (int)fileinfo.st_uid,
+ (int)fileinfo.st_gid);
+ break;
+
+ case _CUPS_FILE_CHECK_WRONG_TYPE :
+ if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("Directory \"%s\" is a file.")),
+ filename);
+ else
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("File \"%s\" is a directory.")),
+ filename);
+ break;
+
+ case _CUPS_FILE_CHECK_RELATIVE_PATH :
+ if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("Directory \"%s\" contains a "
+ "relative path.")), filename);
+ else
+ snprintf(message, sizeof(message),
+ _cupsLangString(lang, _("File \"%s\" contains a relative "
+ "path.")), filename);
+ break;
+ }
+
+ (*cb)(context, result, message);
+ }
+
+ return (result);
+}
+
+
+/*
+ * '_cupsFileCheckFilter()' - Report file check results as CUPS filter messages.
+ */
+
+void
+_cupsFileCheckFilter(
+ void *context, /* I - Context pointer (unused) */
+ _cups_fc_result_t result, /* I - Result code */
+ const char *message) /* I - Message text */
+{
+ const char *prefix; /* Messaging prefix */
+
+
+ (void)context;
+
+ switch (result)
+ {
+ default :
+ case _CUPS_FILE_CHECK_OK :
+ prefix = "DEBUG2";
+ break;
+
+ case _CUPS_FILE_CHECK_MISSING :
+ case _CUPS_FILE_CHECK_WRONG_TYPE :
+ prefix = "ERROR";
+ fputs("STATE: +cups-missing-filter-warning\n", stderr);
+ break;
+
+ case _CUPS_FILE_CHECK_PERMISSIONS :
+ case _CUPS_FILE_CHECK_RELATIVE_PATH :
+ prefix = "ERROR";
+ fputs("STATE: +cups-insecure-filter-warning\n", stderr);
+ break;
+ }
+
+ fprintf(stderr, "%s: %s\n", prefix, message);
+}
+#endif /* !WIN32 */
+
+
+/*
+ * 'cupsFileClose()' - Close a CUPS file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsFileClose(cups_file_t *fp) /* I - CUPS file */
+{
+ int fd; /* File descriptor */
+ char mode; /* Open mode */
+ int status; /* Return status */
+ int is_stdio; /* Is a stdio file? */
+
+
+ DEBUG_printf(("cupsFileClose(fp=%p)", fp));
+
+ /*
+ * Range check...
+ */
+
+ if (!fp)
+ return (-1);
+
+ /*
+ * Flush pending write data...
+ */
+
+ if (fp->mode == 'w')
+ status = cupsFileFlush(fp);
+ else
+ status = 0;
+
+#ifdef HAVE_LIBZ
+ if (fp->compressed && status >= 0)
+ {
+ if (fp->mode == 'r')
+ {
+ /*
+ * Free decompression data...
+ */
+
+ inflateEnd(&fp->stream);
+ }
+ else
+ {
+ /*
+ * Flush any remaining compressed data...
+ */
+
+ unsigned char trailer[8]; /* Trailer CRC and length */
+ int done; /* Done writing... */
+
+
+ fp->stream.avail_in = 0;
+
+ for (done = 0;;)
+ {
+ if (fp->stream.next_out > fp->cbuf)
+ {
+ if (cups_write(fp, (char *)fp->cbuf,
+ fp->stream.next_out - fp->cbuf) < 0)
+ status = -1;
+
+ fp->stream.next_out = fp->cbuf;
+ fp->stream.avail_out = sizeof(fp->cbuf);
+ }
+
+ if (done || status < 0)
+ break;
+
+ done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END &&
+ fp->stream.next_out == fp->cbuf;
+ }
+
+ /*
+ * Write the CRC and length...
+ */
+
+ trailer[0] = fp->crc;
+ trailer[1] = fp->crc >> 8;
+ trailer[2] = fp->crc >> 16;
+ trailer[3] = fp->crc >> 24;
+ trailer[4] = fp->pos;
+ trailer[5] = fp->pos >> 8;
+ trailer[6] = fp->pos >> 16;
+ trailer[7] = fp->pos >> 24;
+
+ if (cups_write(fp, (char *)trailer, 8) < 0)
+ status = -1;
+
+ /*
+ * Free all memory used by the compression stream...
+ */
+
+ deflateEnd(&(fp->stream));
+ }
+ }
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Save the file descriptor we used and free memory...
+ */
+
+ fd = fp->fd;
+ mode = fp->mode;
+ is_stdio = fp->is_stdio;
+
+ if (fp->printf_buffer)
+ free(fp->printf_buffer);
+
+ free(fp);
+
+ /*
+ * Close the file, returning the close status...
+ */
+
+ if (mode == 's')
+ {
+ if (closesocket(fd) < 0)
+ status = -1;
+ }
+ else if (!is_stdio)
+ {
+ if (close(fd) < 0)
+ status = -1;
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'cupsFileCompression()' - Return whether a file is compressed.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - @code CUPS_FILE_NONE@ or @code CUPS_FILE_GZIP@ */
+cupsFileCompression(cups_file_t *fp) /* I - CUPS file */
+{
+ return (fp ? fp->compressed : CUPS_FILE_NONE);
+}
+
+
+/*
+ * 'cupsFileEOF()' - Return the end-of-file status.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 on end of file, 0 otherwise */
+cupsFileEOF(cups_file_t *fp) /* I - CUPS file */
+{
+ return (fp ? fp->eof : 1);
+}
+
+
+/*
+ * 'cupsFileFind()' - Find a file using the specified path.
+ *
+ * This function allows the paths in the path string to be separated by
+ * colons (UNIX standard) or semicolons (Windows standard) and stores the
+ * result in the buffer supplied. If the file cannot be found in any of
+ * the supplied paths, @code NULL@ is returned. A @code NULL@ path only
+ * matches the current directory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+const char * /* O - Full path to file or @code NULL@ if not found */
+cupsFileFind(const char *filename, /* I - File to find */
+ const char *path, /* I - Colon/semicolon-separated path */
+ int executable, /* I - 1 = executable files, 0 = any file/dir */
+ char *buffer, /* I - Filename buffer */
+ int bufsize) /* I - Size of filename buffer */
+{
+ char *bufptr, /* Current position in buffer */
+ *bufend; /* End of buffer */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("cupsFileFind(filename=\"%s\", path=\"%s\", executable=%d, "
+ "buffer=%p, bufsize=%d)", filename, path, executable, buffer,
+ bufsize));
+
+ if (!filename || !buffer || bufsize < 2)
+ return (NULL);
+
+ if (!path)
+ {
+ /*
+ * No path, so check current directory...
+ */
+
+ if (!access(filename, 0))
+ {
+ strlcpy(buffer, filename, bufsize);
+ return (buffer);
+ }
+ else
+ return (NULL);
+ }
+
+ /*
+ * Now check each path and return the first match...
+ */
+
+ bufend = buffer + bufsize - 1;
+ bufptr = buffer;
+
+ while (*path)
+ {
+#ifdef WIN32
+ if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
+#else
+ if (*path == ';' || *path == ':')
+#endif /* WIN32 */
+ {
+ if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
+ *bufptr++ = '/';
+
+ strlcpy(bufptr, filename, bufend - bufptr);
+
+#ifdef WIN32
+ if (!access(buffer, 0))
+#else
+ if (!access(buffer, executable ? X_OK : 0))
+#endif /* WIN32 */
+ {
+ DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
+ return (buffer);
+ }
+
+ bufptr = buffer;
+ }
+ else if (bufptr < bufend)
+ *bufptr++ = *path;
+
+ path ++;
+ }
+
+ /*
+ * Check the last path...
+ */
+
+ if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
+ *bufptr++ = '/';
+
+ strlcpy(bufptr, filename, bufend - bufptr);
+
+ if (!access(buffer, 0))
+ {
+ DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
+ return (buffer);
+ }
+ else
+ {
+ DEBUG_puts("1cupsFileFind: Returning NULL");
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'cupsFileFlush()' - Flush pending output.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsFileFlush(cups_file_t *fp) /* I - CUPS file */
+{
+ ssize_t bytes; /* Bytes to write */
+
+
+ DEBUG_printf(("cupsFileFlush(fp=%p)", fp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!fp || fp->mode != 'w')
+ {
+ DEBUG_puts("1cupsFileFlush: Attempt to flush a read-only file...");
+ return (-1);
+ }
+
+ bytes = (ssize_t)(fp->ptr - fp->buf);
+
+ DEBUG_printf(("2cupsFileFlush: Flushing " CUPS_LLFMT " bytes...",
+ CUPS_LLCAST bytes));
+
+ if (bytes > 0)
+ {
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ bytes = cups_compress(fp, fp->buf, bytes);
+ else
+#endif /* HAVE_LIBZ */
+ bytes = cups_write(fp, fp->buf, bytes);
+
+ if (bytes < 0)
+ return (-1);
+
+ fp->ptr = fp->buf;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'cupsFileGetChar()' - Get a single character from a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Character or -1 on end of file */
+cupsFileGetChar(cups_file_t *fp) /* I - CUPS file */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!fp || (fp->mode != 'r' && fp->mode != 's'))
+ {
+ DEBUG_puts("5cupsFileGetChar: Bad arguments!");
+ return (-1);
+ }
+
+ /*
+ * If the input buffer is empty, try to read more data...
+ */
+
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) < 0)
+ {
+ DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!");
+ return (-1);
+ }
+
+ /*
+ * Return the next character in the buffer...
+ */
+
+ DEBUG_printf(("5cupsFileGetChar: Returning %d...", *(fp->ptr) & 255));
+
+ fp->pos ++;
+
+ DEBUG_printf(("6cupsFileGetChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (*(fp->ptr)++ & 255);
+}
+
+
+/*
+ * 'cupsFileGetConf()' - Get a line from a configuration file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Line read or @code NULL@ on end of file or error */
+cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
+ char *buf, /* O - String buffer */
+ size_t buflen, /* I - Size of string buffer */
+ char **value, /* O - Pointer to value */
+ int *linenum) /* IO - Current line number */
+{
+ char *ptr; /* Pointer into line */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT
+ ", value=%p, linenum=%p)", fp, buf, CUPS_LLCAST buflen,
+ value, linenum));
+
+ if (!fp || (fp->mode != 'r' && fp->mode != 's') ||
+ !buf || buflen < 2 || !value)
+ {
+ if (value)
+ *value = NULL;
+
+ return (NULL);
+ }
+
+ /*
+ * Read the next non-comment line...
+ */
+
+ *value = NULL;
+
+ while (cupsFileGets(fp, buf, buflen))
+ {
+ (*linenum) ++;
+
+ /*
+ * Strip any comments...
+ */
+
+ if ((ptr = strchr(buf, '#')) != NULL)
+ {
+ if (ptr > buf && ptr[-1] == '\\')
+ {
+ // Unquote the #...
+ _cups_strcpy(ptr - 1, ptr);
+ }
+ else
+ {
+ // Strip the comment and any trailing whitespace...
+ while (ptr > buf)
+ {
+ if (!_cups_isspace(ptr[-1]))
+ break;
+
+ ptr --;
+ }
+
+ *ptr = '\0';
+ }
+ }
+
+ /*
+ * Strip leading whitespace...
+ */
+
+ for (ptr = buf; _cups_isspace(*ptr); ptr ++);
+
+ if (ptr > buf)
+ _cups_strcpy(buf, ptr);
+
+ /*
+ * See if there is anything left...
+ */
+
+ if (buf[0])
+ {
+ /*
+ * Yes, grab any value and return...
+ */
+
+ for (ptr = buf; *ptr; ptr ++)
+ if (_cups_isspace(*ptr))
+ break;
+
+ if (*ptr)
+ {
+ /*
+ * Have a value, skip any other spaces...
+ */
+
+ while (_cups_isspace(*ptr))
+ *ptr++ = '\0';
+
+ if (*ptr)
+ *value = ptr;
+
+ /*
+ * Strip trailing whitespace and > for lines that begin with <...
+ */
+
+ ptr += strlen(ptr) - 1;
+
+ if (buf[0] == '<' && *ptr == '>')
+ *ptr-- = '\0';
+ else if (buf[0] == '<' && *ptr != '>')
+ {
+ /*
+ * Syntax error...
+ */
+
+ *value = NULL;
+ return (buf);
+ }
+
+ while (ptr > *value && _cups_isspace(*ptr))
+ *ptr-- = '\0';
+ }
+
+ /*
+ * Return the line...
+ */
+
+ return (buf);
+ }
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may
+ * contain binary data.
+ *
+ * This function differs from @link cupsFileGets@ in that the trailing CR
+ * and LF are preserved, as is any binary data on the line. The buffer is
+ * nul-terminated, however you should use the returned length to determine
+ * the number of bytes on the line.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+size_t /* O - Number of bytes on line or 0 on end of file */
+cupsFileGetLine(cups_file_t *fp, /* I - File to read from */
+ char *buf, /* I - Buffer */
+ size_t buflen) /* I - Size of buffer */
+{
+ int ch; /* Character from file */
+ char *ptr, /* Current position in line buffer */
+ *end; /* End of line buffer */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("2cupsFileGetLine(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")",
+ fp, buf, CUPS_LLCAST buflen));
+
+ if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3)
+ return (0);
+
+ /*
+ * Now loop until we have a valid line...
+ */
+
+ for (ptr = buf, end = buf + buflen - 2; ptr < end ;)
+ {
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) <= 0)
+ break;
+
+ *ptr++ = ch = *(fp->ptr)++;
+ fp->pos ++;
+
+ if (ch == '\r')
+ {
+ /*
+ * Check for CR LF...
+ */
+
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) <= 0)
+ break;
+
+ if (*(fp->ptr) == '\n')
+ {
+ *ptr++ = *(fp->ptr)++;
+ fp->pos ++;
+ }
+
+ break;
+ }
+ else if (ch == '\n')
+ {
+ /*
+ * Line feed ends a line...
+ */
+
+ break;
+ }
+ }
+
+ *ptr = '\0';
+
+ DEBUG_printf(("4cupsFileGetLine: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (ptr - buf);
+}
+
+
+/*
+ * 'cupsFileGets()' - Get a CR and/or LF-terminated line.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Line read or @code NULL@ on end of file or error */
+cupsFileGets(cups_file_t *fp, /* I - CUPS file */
+ char *buf, /* O - String buffer */
+ size_t buflen) /* I - Size of string buffer */
+{
+ int ch; /* Character from file */
+ char *ptr, /* Current position in line buffer */
+ *end; /* End of line buffer */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("2cupsFileGets(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", fp, buf,
+ CUPS_LLCAST buflen));
+
+ if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2)
+ return (NULL);
+
+ /*
+ * Now loop until we have a valid line...
+ */
+
+ for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
+ {
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) <= 0)
+ {
+ if (ptr == buf)
+ return (NULL);
+ else
+ break;
+ }
+
+ ch = *(fp->ptr)++;
+ fp->pos ++;
+
+ if (ch == '\r')
+ {
+ /*
+ * Check for CR LF...
+ */
+
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) <= 0)
+ break;
+
+ if (*(fp->ptr) == '\n')
+ {
+ fp->ptr ++;
+ fp->pos ++;
+ }
+
+ break;
+ }
+ else if (ch == '\n')
+ {
+ /*
+ * Line feed ends a line...
+ */
+
+ break;
+ }
+ else
+ *ptr++ = ch;
+ }
+
+ *ptr = '\0';
+
+ DEBUG_printf(("4cupsFileGets: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (buf);
+}
+
+
+/*
+ * 'cupsFileLock()' - Temporarily lock access to a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsFileLock(cups_file_t *fp, /* I - CUPS file */
+ int block) /* I - 1 to wait for the lock, 0 to fail right away */
+{
+ /*
+ * Range check...
+ */
+
+ if (!fp || fp->mode == 's')
+ return (-1);
+
+ /*
+ * Try the lock...
+ */
+
+#ifdef WIN32
+ return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0));
+#else
+ return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - File descriptor */
+cupsFileNumber(cups_file_t *fp) /* I - CUPS file */
+{
+ if (fp)
+ return (fp->fd);
+ else
+ return (-1);
+}
+
+
+/*
+ * 'cupsFileOpen()' - Open a CUPS file.
+ *
+ * The "mode" parameter can be "r" to read, "w" to write, overwriting any
+ * existing file, "a" to append to an existing file or create a new file,
+ * or "s" to open a socket connection.
+ *
+ * When opening for writing ("w"), an optional number from 1 to 9 can be
+ * supplied which enables Flate compression of the file. Compression is
+ * not supported for the "a" (append) mode.
+ *
+ * When opening a socket connection, the filename is a string of the form
+ * "address:port" or "hostname:port". The socket will make an IPv4 or IPv6
+ * connection as needed, generally preferring IPv6 connections when there is
+ * a choice.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_file_t * /* O - CUPS file or @code NULL@ if the file or socket cannot be opened */
+cupsFileOpen(const char *filename, /* I - Name of file */
+ const char *mode) /* I - Open mode */
+{
+ cups_file_t *fp; /* New CUPS file */
+ int fd; /* File descriptor */
+ char hostname[1024], /* Hostname */
+ *portname; /* Port "name" (number or service) */
+ http_addrlist_t *addrlist; /* Host address list */
+
+
+ DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")", filename,
+ mode));
+
+ /*
+ * Range check input...
+ */
+
+ if (!filename || !mode ||
+ (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
+ (*mode == 'a' && isdigit(mode[1] & 255)))
+ return (NULL);
+
+ /*
+ * Open the file...
+ */
+
+ switch (*mode)
+ {
+ case 'a' : /* Append file */
+ fd = cups_open(filename,
+ O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY);
+ break;
+
+ case 'r' : /* Read file */
+ fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
+ break;
+
+ case 'w' : /* Write file */
+ fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
+ if (fd < 0 && errno == ENOENT)
+ {
+ fd = cups_open(filename,
+ O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY);
+ if (fd < 0 && errno == EEXIST)
+ fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
+ }
+
+ if (fd >= 0)
+#ifdef WIN32
+ _chsize(fd, 0);
+#else
+ ftruncate(fd, 0);
+#endif /* WIN32 */
+ break;
+
+ case 's' : /* Read/write socket */
+ strlcpy(hostname, filename, sizeof(hostname));
+ if ((portname = strrchr(hostname, ':')) != NULL)
+ *portname++ = '\0';
+ else
+ return (NULL);
+
+ /*
+ * Lookup the hostname and service...
+ */
+
+ if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
+ return (NULL);
+
+ /*
+ * Connect to the server...
+ */
+
+ if (!httpAddrConnect(addrlist, &fd))
+ {
+ httpAddrFreeList(addrlist);
+ return (NULL);
+ }
+
+ httpAddrFreeList(addrlist);
+ break;
+
+ default : /* Remove bogus compiler warning... */
+ return (NULL);
+ }
+
+ if (fd < 0)
+ return (NULL);
+
+ /*
+ * Create the CUPS file structure...
+ */
+
+ if ((fp = cupsFileOpenFd(fd, mode)) == NULL)
+ {
+ if (*mode == 's')
+ closesocket(fd);
+ else
+ close(fd);
+ }
+
+ /*
+ * Return it...
+ */
+
+ return (fp);
+}
+
+/*
+ * 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor.
+ *
+ * The "mode" parameter can be "r" to read, "w" to write, "a" to append,
+ * or "s" to treat the file descriptor as a bidirectional socket connection.
+ *
+ * When opening for writing ("w"), an optional number from 1 to 9 can be
+ * supplied which enables Flate compression of the file. Compression is
+ * not supported for the "a" (append) mode.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_file_t * /* O - CUPS file or @code NULL@ if the file could not be opened */
+cupsFileOpenFd(int fd, /* I - File descriptor */
+ const char *mode) /* I - Open mode */
+{
+ cups_file_t *fp; /* New CUPS file */
+
+
+ DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")", fd, mode));
+
+ /*
+ * Range check input...
+ */
+
+ if (fd < 0 || !mode ||
+ (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
+ (*mode == 'a' && isdigit(mode[1] & 255)))
+ return (NULL);
+
+ /*
+ * Allocate memory...
+ */
+
+ if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
+ return (NULL);
+
+ /*
+ * Open the file...
+ */
+
+ fp->fd = fd;
+
+ switch (*mode)
+ {
+ case 'a' :
+ fp->pos = lseek(fd, 0, SEEK_END);
+
+ case 'w' :
+ fp->mode = 'w';
+ fp->ptr = fp->buf;
+ fp->end = fp->buf + sizeof(fp->buf);
+
+#ifdef HAVE_LIBZ
+ if (mode[1] >= '1' && mode[1] <= '9')
+ {
+ /*
+ * Open a compressed stream, so write the standard gzip file
+ * header...
+ */
+
+ unsigned char header[10]; /* gzip file header */
+ time_t curtime; /* Current time */
+
+
+ curtime = time(NULL);
+ header[0] = 0x1f;
+ header[1] = 0x8b;
+ header[2] = Z_DEFLATED;
+ header[3] = 0;
+ header[4] = curtime;
+ header[5] = curtime >> 8;
+ header[6] = curtime >> 16;
+ header[7] = curtime >> 24;
+ header[8] = 0;
+ header[9] = 0x03;
+
+ cups_write(fp, (char *)header, 10);
+
+ /*
+ * Initialize the compressor...
+ */
+
+ deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8,
+ Z_DEFAULT_STRATEGY);
+
+ fp->stream.next_out = fp->cbuf;
+ fp->stream.avail_out = sizeof(fp->cbuf);
+ fp->compressed = 1;
+ fp->crc = crc32(0L, Z_NULL, 0);
+ }
+#endif /* HAVE_LIBZ */
+ break;
+
+ case 'r' :
+ fp->mode = 'r';
+ break;
+
+ case 's' :
+ fp->mode = 's';
+ break;
+
+ default : /* Remove bogus compiler warning... */
+ return (NULL);
+ }
+
+ /*
+ * Don't pass this file to child processes...
+ */
+
+#ifndef WIN32
+ fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC);
+#endif /* !WIN32 */
+
+ return (fp);
+}
+
+
+/*
+ * 'cupsFilePeekChar()' - Peek at the next character from a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Character or -1 on end of file */
+cupsFilePeekChar(cups_file_t *fp) /* I - CUPS file */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!fp || (fp->mode != 'r' && fp->mode != 's'))
+ return (-1);
+
+ /*
+ * If the input buffer is empty, try to read more data...
+ */
+
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) < 0)
+ return (-1);
+
+ /*
+ * Return the next character in the buffer...
+ */
+
+ return (*(fp->ptr) & 255);
+}
+
+
+/*
+ * 'cupsFilePrintf()' - Write a formatted string.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Number of bytes written or -1 on error */
+cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional args as necessary */
+{
+ va_list ap; /* Argument list */
+ ssize_t bytes; /* Formatted size */
+
+
+ DEBUG_printf(("2cupsFilePrintf(fp=%p, format=\"%s\", ...)", fp, format));
+
+ if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
+ return (-1);
+
+ if (!fp->printf_buffer)
+ {
+ /*
+ * Start with an 1k printf buffer...
+ */
+
+ if ((fp->printf_buffer = malloc(1024)) == NULL)
+ return (-1);
+
+ fp->printf_size = 1024;
+ }
+
+ va_start(ap, format);
+ bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
+ va_end(ap);
+
+ if (bytes >= (ssize_t)fp->printf_size)
+ {
+ /*
+ * Expand the printf buffer...
+ */
+
+ char *temp; /* Temporary buffer pointer */
+
+
+ if (bytes > 65535)
+ return (-1);
+
+ if ((temp = realloc(fp->printf_buffer, bytes + 1)) == NULL)
+ return (-1);
+
+ fp->printf_buffer = temp;
+ fp->printf_size = bytes + 1;
+
+ va_start(ap, format);
+ bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
+ va_end(ap);
+ }
+
+ if (fp->mode == 's')
+ {
+ if (cups_write(fp, fp->printf_buffer, bytes) < 0)
+ return (-1);
+
+ fp->pos += bytes;
+
+ DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (bytes);
+ }
+
+ if ((fp->ptr + bytes) > fp->end)
+ if (cupsFileFlush(fp))
+ return (-1);
+
+ fp->pos += bytes;
+
+ DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ if (bytes > sizeof(fp->buf))
+ {
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ return (cups_compress(fp, fp->printf_buffer, bytes));
+ else
+#endif /* HAVE_LIBZ */
+ return (cups_write(fp, fp->printf_buffer, bytes));
+ }
+ else
+ {
+ memcpy(fp->ptr, fp->printf_buffer, bytes);
+ fp->ptr += bytes;
+ return (bytes);
+ }
+}
+
+
+/*
+ * 'cupsFilePutChar()' - Write a character.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */
+ int c) /* I - Character to write */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!fp || (fp->mode != 'w' && fp->mode != 's'))
+ return (-1);
+
+ if (fp->mode == 's')
+ {
+ /*
+ * Send character immediately over socket...
+ */
+
+ char ch; /* Output character */
+
+
+ ch = c;
+
+ if (send(fp->fd, &ch, 1, 0) < 1)
+ return (-1);
+ }
+ else
+ {
+ /*
+ * Buffer it up...
+ */
+
+ if (fp->ptr >= fp->end)
+ if (cupsFileFlush(fp))
+ return (-1);
+
+ *(fp->ptr) ++ = c;
+ }
+
+ fp->pos ++;
+
+ DEBUG_printf(("4cupsFilePutChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (0);
+}
+
+
+/*
+ * 'cupsFilePutConf()' - Write a configuration line.
+ *
+ * This function handles any comment escaping of the value.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ssize_t /* O - Number of bytes written or -1 on error */
+cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */
+ const char *directive, /* I - Directive */
+ const char *value) /* I - Value */
+{
+ ssize_t bytes, /* Number of bytes written */
+ temp; /* Temporary byte count */
+ const char *ptr; /* Pointer into value */
+
+
+ if (!fp || !directive || !*directive)
+ return (-1);
+
+ if ((bytes = cupsFilePuts(fp, directive)) < 0)
+ return (-1);
+
+ if (cupsFilePutChar(fp, ' ') < 0)
+ return (-1);
+ bytes ++;
+
+ if (value && *value)
+ {
+ if ((ptr = strchr(value, '#')) != NULL)
+ {
+ /*
+ * Need to quote the first # in the info string...
+ */
+
+ if ((temp = cupsFileWrite(fp, value, ptr - value)) < 0)
+ return (-1);
+ bytes += temp;
+
+ if (cupsFilePutChar(fp, '\\') < 0)
+ return (-1);
+ bytes ++;
+
+ if ((temp = cupsFilePuts(fp, ptr)) < 0)
+ return (-1);
+ bytes += temp;
+ }
+ else if ((temp = cupsFilePuts(fp, value)) < 0)
+ return (-1);
+ else
+ bytes += temp;
+ }
+
+ if (cupsFilePutChar(fp, '\n') < 0)
+ return (-1);
+ else
+ return (bytes + 1);
+}
+
+
+/*
+ * 'cupsFilePuts()' - Write a string.
+ *
+ * Like the @code fputs@ function, no newline is appended to the string.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Number of bytes written or -1 on error */
+cupsFilePuts(cups_file_t *fp, /* I - CUPS file */
+ const char *s) /* I - String to write */
+{
+ ssize_t bytes; /* Bytes to write */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
+ return (-1);
+
+ /*
+ * Write the string...
+ */
+
+ bytes = (int)strlen(s);
+
+ if (fp->mode == 's')
+ {
+ if (cups_write(fp, s, bytes) < 0)
+ return (-1);
+
+ fp->pos += bytes;
+
+ DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (bytes);
+ }
+
+ if ((fp->ptr + bytes) > fp->end)
+ if (cupsFileFlush(fp))
+ return (-1);
+
+ fp->pos += bytes;
+
+ DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ if (bytes > sizeof(fp->buf))
+ {
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ return (cups_compress(fp, s, bytes));
+ else
+#endif /* HAVE_LIBZ */
+ return (cups_write(fp, s, bytes));
+ }
+ else
+ {
+ memcpy(fp->ptr, s, bytes);
+ fp->ptr += bytes;
+ return (bytes);
+ }
+}
+
+
+/*
+ * 'cupsFileRead()' - Read from a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ssize_t /* O - Number of bytes read or -1 on error */
+cupsFileRead(cups_file_t *fp, /* I - CUPS file */
+ char *buf, /* O - Buffer */
+ size_t bytes) /* I - Number of bytes to read */
+{
+ size_t total; /* Total bytes read */
+ ssize_t count; /* Bytes read */
+
+
+ DEBUG_printf(("2cupsFileRead(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf,
+ CUPS_LLCAST bytes));
+
+ /*
+ * Range check input...
+ */
+
+ if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
+ return (-1);
+
+ if (bytes == 0)
+ return (0);
+
+ /*
+ * Loop until all bytes are read...
+ */
+
+ total = 0;
+ while (bytes > 0)
+ {
+ if (fp->ptr >= fp->end)
+ if (cups_fill(fp) <= 0)
+ {
+ DEBUG_printf(("4cupsFileRead: cups_fill() returned -1, total="
+ CUPS_LLFMT, CUPS_LLCAST total));
+
+ if (total > 0)
+ return ((ssize_t)total);
+ else
+ return (-1);
+ }
+
+ count = (ssize_t)(fp->end - fp->ptr);
+ if (count > (ssize_t)bytes)
+ count = (ssize_t)bytes;
+
+ memcpy(buf, fp->ptr, count);
+ fp->ptr += count;
+ fp->pos += count;
+
+ DEBUG_printf(("4cupsFileRead: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ /*
+ * Update the counts for the last read...
+ */
+
+ bytes -= count;
+ total += count;
+ buf += count;
+ }
+
+ /*
+ * Return the total number of bytes read...
+ */
+
+ DEBUG_printf(("3cupsFileRead: total=" CUPS_LLFMT, CUPS_LLCAST total));
+
+ return ((ssize_t)total);
+}
+
+
+/*
+ * 'cupsFileRewind()' - Set the current file position to the beginning of the
+ * file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+off_t /* O - New file position or -1 on error */
+cupsFileRewind(cups_file_t *fp) /* I - CUPS file */
+{
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("cupsFileRewind(fp=%p)", fp));
+ DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ if (!fp || fp->mode != 'r')
+ return (-1);
+
+ /*
+ * Handle special cases...
+ */
+
+ if (fp->bufpos == 0)
+ {
+ /*
+ * No seeking necessary...
+ */
+
+ fp->pos = 0;
+
+ if (fp->ptr)
+ {
+ fp->ptr = fp->buf;
+ fp->eof = 0;
+ }
+
+ DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (0);
+ }
+
+ /*
+ * Otherwise, seek in the file and cleanup any compression buffers...
+ */
+
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ {
+ inflateEnd(&fp->stream);
+ fp->compressed = 0;
+ }
+#endif /* HAVE_LIBZ */
+
+ if (lseek(fp->fd, 0, SEEK_SET))
+ {
+ DEBUG_printf(("1cupsFileRewind: lseek failed: %s", strerror(errno)));
+ return (-1);
+ }
+
+ fp->bufpos = 0;
+ fp->pos = 0;
+ fp->ptr = NULL;
+ fp->end = NULL;
+ fp->eof = 0;
+
+ DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (0);
+}
+
+
+/*
+ * 'cupsFileSeek()' - Seek in a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+off_t /* O - New file position or -1 on error */
+cupsFileSeek(cups_file_t *fp, /* I - CUPS file */
+ off_t pos) /* I - Position in file */
+{
+ ssize_t bytes; /* Number bytes in buffer */
+
+
+ DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")", fp,
+ CUPS_LLCAST pos));
+ DEBUG_printf(("2cupsFileSeek: fp->pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+ DEBUG_printf(("2cupsFileSeek: fp->ptr=%p, fp->end=%p", fp->ptr, fp->end));
+
+ /*
+ * Range check input...
+ */
+
+ if (!fp || pos < 0 || fp->mode != 'r')
+ return (-1);
+
+ /*
+ * Handle special cases...
+ */
+
+ if (pos == 0)
+ return (cupsFileRewind(fp));
+
+ if (fp->ptr)
+ {
+ bytes = (ssize_t)(fp->end - fp->buf);
+
+ DEBUG_printf(("2cupsFileSeek: bytes=" CUPS_LLFMT, CUPS_LLCAST bytes));
+
+ if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
+ {
+ /*
+ * No seeking necessary...
+ */
+
+ fp->pos = pos;
+ fp->ptr = fp->buf + pos - fp->bufpos;
+ fp->eof = 0;
+
+ return (pos);
+ }
+ }
+
+#ifdef HAVE_LIBZ
+ if (!fp->compressed && !fp->ptr)
+ {
+ /*
+ * Preload a buffer to determine whether the file is compressed...
+ */
+
+ if (cups_fill(fp) < 0)
+ return (-1);
+ }
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Seek forwards or backwards...
+ */
+
+ fp->eof = 0;
+
+ if (pos < fp->bufpos)
+ {
+ /*
+ * Need to seek backwards...
+ */
+
+ DEBUG_puts("2cupsFileSeek: SEEK BACKWARDS");
+
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ {
+ inflateEnd(&fp->stream);
+
+ lseek(fp->fd, 0, SEEK_SET);
+ fp->bufpos = 0;
+ fp->pos = 0;
+ fp->ptr = NULL;
+ fp->end = NULL;
+
+ while ((bytes = cups_fill(fp)) > 0)
+ if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
+ break;
+
+ if (bytes <= 0)
+ return (-1);
+
+ fp->ptr = fp->buf + pos - fp->bufpos;
+ fp->pos = pos;
+ }
+ else
+#endif /* HAVE_LIBZ */
+ {
+ fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
+ fp->pos = fp->bufpos;
+ fp->ptr = NULL;
+ fp->end = NULL;
+
+ DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
+ CUPS_LLCAST fp->pos));
+ }
+ }
+ else
+ {
+ /*
+ * Need to seek forwards...
+ */
+
+ DEBUG_puts("2cupsFileSeek: SEEK FORWARDS");
+
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ {
+ while ((bytes = cups_fill(fp)) > 0)
+ {
+ if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
+ break;
+ }
+
+ if (bytes <= 0)
+ return (-1);
+
+ fp->ptr = fp->buf + pos - fp->bufpos;
+ fp->pos = pos;
+ }
+ else
+#endif /* HAVE_LIBZ */
+ {
+ fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
+ fp->pos = fp->bufpos;
+ fp->ptr = NULL;
+ fp->end = NULL;
+
+ DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
+ CUPS_LLCAST fp->pos));
+ }
+ }
+
+ DEBUG_printf(("2cupsFileSeek: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return (fp->pos);
+}
+
+
+/*
+ * 'cupsFileStderr()' - Return a CUPS file associated with stderr.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_file_t * /* O - CUPS file */
+cupsFileStderr(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
+
+
+ /*
+ * Open file descriptor 2 as needed...
+ */
+
+ if (!cg->stdio_files[2])
+ {
+ /*
+ * Flush any pending output on the stdio file...
+ */
+
+ fflush(stderr);
+
+ /*
+ * Open file descriptor 2...
+ */
+
+ if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
+ cg->stdio_files[2]->is_stdio = 1;
+ }
+
+ return (cg->stdio_files[2]);
+}
+
+
+/*
+ * 'cupsFileStdin()' - Return a CUPS file associated with stdin.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_file_t * /* O - CUPS file */
+cupsFileStdin(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
+
+
+ /*
+ * Open file descriptor 0 as needed...
+ */
+
+ if (!cg->stdio_files[0])
+ {
+ /*
+ * Open file descriptor 0...
+ */
+
+ if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
+ cg->stdio_files[0]->is_stdio = 1;
+ }
+
+ return (cg->stdio_files[0]);
+}
+
+
+/*
+ * 'cupsFileStdout()' - Return a CUPS file associated with stdout.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_file_t * /* O - CUPS file */
+cupsFileStdout(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
+
+
+ /*
+ * Open file descriptor 1 as needed...
+ */
+
+ if (!cg->stdio_files[1])
+ {
+ /*
+ * Flush any pending output on the stdio file...
+ */
+
+ fflush(stdout);
+
+ /*
+ * Open file descriptor 1...
+ */
+
+ if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
+ cg->stdio_files[1]->is_stdio = 1;
+ }
+
+ return (cg->stdio_files[1]);
+}
+
+
+/*
+ * 'cupsFileTell()' - Return the current file position.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+off_t /* O - File position */
+cupsFileTell(cups_file_t *fp) /* I - CUPS file */
+{
+ DEBUG_printf(("2cupsFileTell(fp=%p)", fp));
+ DEBUG_printf(("3cupsFileTell: pos=" CUPS_LLFMT,
+ CUPS_LLCAST (fp ? fp->pos : -1)));
+
+ return (fp ? fp->pos : 0);
+}
+
+
+/*
+ * 'cupsFileUnlock()' - Unlock access to a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsFileUnlock(cups_file_t *fp) /* I - CUPS file */
+{
+ /*
+ * Range check...
+ */
+
+ DEBUG_printf(("cupsFileUnlock(fp=%p)", fp));
+
+ if (!fp || fp->mode == 's')
+ return (-1);
+
+ /*
+ * Unlock...
+ */
+
+#ifdef WIN32
+ return (_locking(fp->fd, _LK_UNLCK, 0));
+#else
+ return (lockf(fp->fd, F_ULOCK, 0));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'cupsFileWrite()' - Write to a file.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ssize_t /* O - Number of bytes written or -1 on error */
+cupsFileWrite(cups_file_t *fp, /* I - CUPS file */
+ const char *buf, /* I - Buffer */
+ size_t bytes) /* I - Number of bytes to write */
+{
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("2cupsFileWrite(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")",
+ fp, buf, CUPS_LLCAST bytes));
+
+ if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
+ return (-1);
+
+ if (bytes == 0)
+ return (0);
+
+ /*
+ * Write the buffer...
+ */
+
+ if (fp->mode == 's')
+ {
+ if (cups_write(fp, buf, bytes) < 0)
+ return (-1);
+
+ fp->pos += (off_t)bytes;
+
+ DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ return ((ssize_t)bytes);
+ }
+
+ if ((fp->ptr + bytes) > fp->end)
+ if (cupsFileFlush(fp))
+ return (-1);
+
+ fp->pos += (off_t)bytes;
+
+ DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
+
+ if (bytes > sizeof(fp->buf))
+ {
+#ifdef HAVE_LIBZ
+ if (fp->compressed)
+ return (cups_compress(fp, buf, bytes));
+ else
+#endif /* HAVE_LIBZ */
+ return (cups_write(fp, buf, bytes));
+ }
+ else
+ {
+ memcpy(fp->ptr, buf, bytes);
+ fp->ptr += bytes;
+ return ((ssize_t)bytes);
+ }
+}
+
+
+#ifdef HAVE_LIBZ
+/*
+ * 'cups_compress()' - Compress a buffer of data.
+ */
+
+static ssize_t /* O - Number of bytes written or -1 */
+cups_compress(cups_file_t *fp, /* I - CUPS file */
+ const char *buf, /* I - Buffer */
+ size_t bytes) /* I - Number bytes */
+{
+ DEBUG_printf(("7cups_compress(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf,
+ CUPS_LLCAST bytes));
+
+ /*
+ * Update the CRC...
+ */
+
+ fp->crc = crc32(fp->crc, (const Bytef *)buf, bytes);
+
+ /*
+ * Deflate the bytes...
+ */
+
+ fp->stream.next_in = (Bytef *)buf;
+ fp->stream.avail_in = bytes;
+
+ while (fp->stream.avail_in > 0)
+ {
+ /*
+ * Flush the current buffer...
+ */
+
+ DEBUG_printf(("9cups_compress: avail_in=%d, avail_out=%d",
+ fp->stream.avail_in, fp->stream.avail_out));
+
+ if (fp->stream.avail_out < (int)(sizeof(fp->cbuf) / 8))
+ {
+ if (cups_write(fp, (char *)fp->cbuf, fp->stream.next_out - fp->cbuf) < 0)
+ return (-1);
+
+ fp->stream.next_out = fp->cbuf;
+ fp->stream.avail_out = sizeof(fp->cbuf);
+ }
+
+ deflate(&(fp->stream), Z_NO_FLUSH);
+ }
+
+ return (bytes);
+}
+#endif /* HAVE_LIBZ */
+
+
+/*
+ * 'cups_fill()' - Fill the input buffer.
+ */
+
+static ssize_t /* O - Number of bytes or -1 */
+cups_fill(cups_file_t *fp) /* I - CUPS file */
+{
+ ssize_t bytes; /* Number of bytes read */
+#ifdef HAVE_LIBZ
+ int status; /* Decompression status */
+ const unsigned char *ptr, /* Pointer into buffer */
+ *end; /* End of buffer */
+#endif /* HAVE_LIBZ */
+
+
+ DEBUG_printf(("7cups_fill(fp=%p)", fp));
+ DEBUG_printf(("9cups_fill: fp->ptr=%p, fp->end=%p, fp->buf=%p, "
+ "fp->bufpos=" CUPS_LLFMT ", fp->eof=%d",
+ fp->ptr, fp->end, fp->buf, CUPS_LLCAST fp->bufpos, fp->eof));
+
+ if (fp->ptr && fp->end)
+ fp->bufpos += fp->end - fp->buf;
+
+#ifdef HAVE_LIBZ
+ DEBUG_printf(("9cups_fill: fp->compressed=%d", fp->compressed));
+
+ while (!fp->ptr || fp->compressed)
+ {
+ /*
+ * Check to see if we have read any data yet; if not, see if we have a
+ * compressed file...
+ */
+
+ if (!fp->ptr)
+ {
+ /*
+ * Reset the file position in case we are seeking...
+ */
+
+ fp->compressed = 0;
+
+ /*
+ * Read the first bytes in the file to determine if we have a gzip'd
+ * file...
+ */
+
+ if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
+ {
+ /*
+ * Can't read from file!
+ */
+
+ DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT,
+ CUPS_LLCAST bytes));
+
+ return (-1);
+ }
+
+ if (bytes < 10 || fp->buf[0] != 0x1f ||
+ (fp->buf[1] & 255) != 0x8b ||
+ fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
+ {
+ /*
+ * Not a gzip'd file!
+ */
+
+ fp->ptr = fp->buf;
+ fp->end = fp->buf + bytes;
+
+ DEBUG_printf(("9cups_fill: Returning " CUPS_LLFMT,
+ CUPS_LLCAST bytes));
+
+ return (bytes);
+ }
+
+ /*
+ * Parse header junk: extra data, original name, and comment...
+ */
+
+ ptr = (unsigned char *)fp->buf + 10;
+ end = (unsigned char *)fp->buf + bytes;
+
+ if (fp->buf[3] & 0x04)
+ {
+ /*
+ * Skip extra data...
+ */
+
+ if ((ptr + 2) > end)
+ {
+ /*
+ * Can't read from file!
+ */
+
+ return (-1);
+ }
+
+ bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
+ ptr += 2 + bytes;
+
+ if (ptr > end)
+ {
+ /*
+ * Can't read from file!
+ */
+
+ return (-1);
+ }
+ }
+
+ if (fp->buf[3] & 0x08)
+ {
+ /*
+ * Skip original name data...
+ */
+
+ while (ptr < end && *ptr)
+ ptr ++;
+
+ if (ptr < end)
+ ptr ++;
+ else
+ {
+ /*
+ * Can't read from file!
+ */
+
+ return (-1);
+ }
+ }
+
+ if (fp->buf[3] & 0x10)
+ {
+ /*
+ * Skip comment data...
+ */
+
+ while (ptr < end && *ptr)
+ ptr ++;
+
+ if (ptr < end)
+ ptr ++;
+ else
+ {
+ /*
+ * Can't read from file!
+ */
+
+ return (-1);
+ }
+ }
+
+ if (fp->buf[3] & 0x02)
+ {
+ /*
+ * Skip header CRC data...
+ */
+
+ ptr += 2;
+
+ if (ptr > end)
+ {
+ /*
+ * Can't read from file!
+ */
+
+ return (-1);
+ }
+ }
+
+ /*
+ * Copy the flate-compressed data to the compression buffer...
+ */
+
+ if ((bytes = end - ptr) > 0)
+ memcpy(fp->cbuf, ptr, bytes);
+
+ /*
+ * Setup the decompressor data...
+ */
+
+ fp->stream.zalloc = (alloc_func)0;
+ fp->stream.zfree = (free_func)0;
+ fp->stream.opaque = (voidpf)0;
+ fp->stream.next_in = (Bytef *)fp->cbuf;
+ fp->stream.next_out = NULL;
+ fp->stream.avail_in = bytes;
+ fp->stream.avail_out = 0;
+ fp->crc = crc32(0L, Z_NULL, 0);
+
+ if (inflateInit2(&(fp->stream), -15) != Z_OK)
+ return (-1);
+
+ fp->compressed = 1;
+ }
+
+ if (fp->compressed)
+ {
+ /*
+ * If we have reached end-of-file, return immediately...
+ */
+
+ if (fp->eof)
+ return (-1);
+
+ /*
+ * Fill the decompression buffer as needed...
+ */
+
+ if (fp->stream.avail_in == 0)
+ {
+ if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
+ return (-1);
+
+ fp->stream.next_in = fp->cbuf;
+ fp->stream.avail_in = bytes;
+ }
+
+ /*
+ * Decompress data from the buffer...
+ */
+
+ fp->stream.next_out = (Bytef *)fp->buf;
+ fp->stream.avail_out = sizeof(fp->buf);
+
+ status = inflate(&(fp->stream), Z_NO_FLUSH);
+
+ if (fp->stream.next_out > (Bytef *)fp->buf)
+ fp->crc = crc32(fp->crc, (Bytef *)fp->buf,
+ fp->stream.next_out - (Bytef *)fp->buf);
+
+ if (status == Z_STREAM_END)
+ {
+ /*
+ * Read the CRC and length...
+ */
+
+ unsigned char trailer[8]; /* Trailer bytes */
+ uLong tcrc; /* Trailer CRC */
+
+
+ if (read(fp->fd, trailer, sizeof(trailer)) < sizeof(trailer))
+ {
+ /*
+ * Can't get it, so mark end-of-file...
+ */
+
+ fp->eof = 1;
+ }
+ else
+ {
+ tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) |
+ (uLong)trailer[1]) << 8) | (uLong)trailer[0];
+
+ if (tcrc != fp->crc)
+ {
+ /*
+ * Bad CRC, mark end-of-file...
+ */
+
+ DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x",
+ (unsigned int)tcrc, (unsigned int)fp->crc));
+
+ fp->eof = 1;
+
+ return (-1);
+ }
+
+ /*
+ * Otherwise, reset the compressed flag so that we re-read the
+ * file header...
+ */
+
+ fp->compressed = 0;
+ }
+ }
+
+ bytes = sizeof(fp->buf) - fp->stream.avail_out;
+
+ /*
+ * Return the decompressed data...
+ */
+
+ fp->ptr = fp->buf;
+ fp->end = fp->buf + bytes;
+
+ if (bytes)
+ return (bytes);
+ }
+ }
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Read a buffer's full of data...
+ */
+
+ if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
+ {
+ /*
+ * Can't read from file!
+ */
+
+ fp->eof = 1;
+ fp->ptr = fp->buf;
+ fp->end = fp->buf;
+
+ return (-1);
+ }
+
+ /*
+ * Return the bytes we read...
+ */
+
+ fp->eof = 0;
+ fp->ptr = fp->buf;
+ fp->end = fp->buf + bytes;
+
+ return (bytes);
+}
+
+
+/*
+ * 'cups_open()' - Safely open a file for writing.
+ *
+ * We don't allow appending to directories or files that are hard-linked or
+ * symlinked.
+ */
+
+static int /* O - File descriptor or -1 otherwise */
+cups_open(const char *filename, /* I - Filename */
+ int mode) /* I - Open mode */
+{
+ int fd; /* File descriptor */
+ struct stat fileinfo; /* File information */
+#ifndef WIN32
+ struct stat linkinfo; /* Link information */
+#endif /* !WIN32 */
+
+
+ /*
+ * Open the file...
+ */
+
+ if ((fd = open(filename, mode, 0666)) < 0)
+ return (-1);
+
+ /*
+ * Then verify that the file descriptor doesn't point to a directory or hard-
+ * linked file.
+ */
+
+ if (fstat(fd, &fileinfo))
+ {
+ close(fd);
+ return (-1);
+ }
+
+ if (fileinfo.st_nlink != 1)
+ {
+ close(fd);
+ errno = EPERM;
+ return (-1);
+ }
+
+#ifdef WIN32
+ if (fileinfo.st_mode & _S_IFDIR)
+#else
+ if (S_ISDIR(fileinfo.st_mode))
+#endif /* WIN32 */
+ {
+ close(fd);
+ errno = EISDIR;
+ return (-1);
+ }
+
+#ifndef WIN32
+ /*
+ * Then use lstat to determine whether the filename is a symlink...
+ */
+
+ if (lstat(filename, &linkinfo))
+ {
+ close(fd);
+ return (-1);
+ }
+
+ if (S_ISLNK(linkinfo.st_mode) ||
+ fileinfo.st_dev != linkinfo.st_dev ||
+ fileinfo.st_ino != linkinfo.st_ino ||
+#ifdef HAVE_ST_GEN
+ fileinfo.st_gen != linkinfo.st_gen ||
+#endif /* HAVE_ST_GEN */
+ fileinfo.st_nlink != linkinfo.st_nlink ||
+ fileinfo.st_mode != linkinfo.st_mode)
+ {
+ /*
+ * Yes, don't allow!
+ */
+
+ close(fd);
+ errno = EPERM;
+ return (-1);
+ }
+#endif /* !WIN32 */
+
+ return (fd);
+}
+
+
+/*
+ * 'cups_read()' - Read from a file descriptor.
+ */
+
+static ssize_t /* O - Number of bytes read or -1 */
+cups_read(cups_file_t *fp, /* I - CUPS file */
+ char *buf, /* I - Buffer */
+ size_t bytes) /* I - Number bytes */
+{
+ ssize_t total; /* Total bytes read */
+
+
+ DEBUG_printf(("7cups_read(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf,
+ CUPS_LLCAST bytes));
+
+ /*
+ * Loop until we read at least 0 bytes...
+ */
+
+ for (;;)
+ {
+#ifdef WIN32
+ if (fp->mode == 's')
+ total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
+ else
+ total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
+#else
+ if (fp->mode == 's')
+ total = recv(fp->fd, buf, bytes, 0);
+ else
+ total = read(fp->fd, buf, bytes);
+#endif /* WIN32 */
+
+ DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total));
+
+ if (total >= 0)
+ break;
+
+ /*
+ * Reads can be interrupted by signals and unavailable resources...
+ */
+
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ else
+ return (-1);
+ }
+
+ /*
+ * Return the total number of bytes read...
+ */
+
+ return (total);
+}
+
+
+/*
+ * 'cups_write()' - Write to a file descriptor.
+ */
+
+static ssize_t /* O - Number of bytes written or -1 */
+cups_write(cups_file_t *fp, /* I - CUPS file */
+ const char *buf, /* I - Buffer */
+ size_t bytes) /* I - Number bytes */
+{
+ size_t total; /* Total bytes written */
+ ssize_t count; /* Count this time */
+
+
+ DEBUG_printf(("7cups_write(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf,
+ CUPS_LLCAST bytes));
+
+ /*
+ * Loop until all bytes are written...
+ */
+
+ total = 0;
+ while (bytes > 0)
+ {
+#ifdef WIN32
+ if (fp->mode == 's')
+ count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
+ else
+ count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
+#else
+ if (fp->mode == 's')
+ count = send(fp->fd, buf, bytes, 0);
+ else
+ count = write(fp->fd, buf, bytes);
+#endif /* WIN32 */
+
+ DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count));
+
+ if (count < 0)
+ {
+ /*
+ * Writes can be interrupted by signals and unavailable resources...
+ */
+
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ else
+ return (-1);
+ }
+
+ /*
+ * Update the counts for the last write call...
+ */
+
+ bytes -= count;
+ total += count;
+ buf += count;
+ }
+
+ /*
+ * Return the total number of bytes written...
+ */
+
+ return ((ssize_t)total);
+}
+
+
+/*
+ * End of "$Id: file.c 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/file.h b/cups/libs/cups/file.h
new file mode 100644
index 000000000..887c33a1e
--- /dev/null
+++ b/cups/libs/cups/file.h
@@ -0,0 +1,118 @@
+/*
+ * "$Id: file.h 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * Public file definitions for CUPS.
+ *
+ * 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.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_FILE_H_
+# define _CUPS_FILE_H_
+
+
+/*
+ * Include necessary headers...
+ */
+
+# include "versioning.h"
+# include <stddef.h>
+# include <sys/types.h>
+# if defined(WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
+# define __CUPS_SSIZE_T_DEFINED
+/* Windows does not support the ssize_t type, so map it to off_t... */
+typedef off_t ssize_t; /* @private@ */
+# endif /* WIN32 && !__CUPS_SSIZE_T_DEFINED */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * CUPS file definitions...
+ */
+
+# define CUPS_FILE_NONE 0 /* No compression */
+# define CUPS_FILE_GZIP 1 /* GZIP compression */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef struct _cups_file_s cups_file_t;/**** CUPS file type ****/
+
+
+/*
+ * Prototypes...
+ */
+
+extern int cupsFileClose(cups_file_t *fp) _CUPS_API_1_2;
+extern int cupsFileCompression(cups_file_t *fp) _CUPS_API_1_2;
+extern int cupsFileEOF(cups_file_t *fp) _CUPS_API_1_2;
+extern const char *cupsFileFind(const char *filename, const char *path,
+ int executable, char *buffer,
+ int bufsize) _CUPS_API_1_2;
+extern int cupsFileFlush(cups_file_t *fp) _CUPS_API_1_2;
+extern int cupsFileGetChar(cups_file_t *fp) _CUPS_API_1_2;
+extern char *cupsFileGetConf(cups_file_t *fp, char *buf,
+ size_t buflen, char **value,
+ int *linenum) _CUPS_API_1_2;
+extern size_t cupsFileGetLine(cups_file_t *fp, char *buf,
+ size_t buflen) _CUPS_API_1_2;
+extern char *cupsFileGets(cups_file_t *fp, char *buf, size_t buflen)
+ _CUPS_API_1_2;
+extern int cupsFileLock(cups_file_t *fp, int block) _CUPS_API_1_2;
+extern int cupsFileNumber(cups_file_t *fp) _CUPS_API_1_2;
+extern cups_file_t *cupsFileOpen(const char *filename, const char *mode)
+ _CUPS_API_1_2;
+extern cups_file_t *cupsFileOpenFd(int fd, const char *mode) _CUPS_API_1_2;
+extern int cupsFilePeekChar(cups_file_t *fp) _CUPS_API_1_2;
+extern int cupsFilePrintf(cups_file_t *fp, const char *format, ...)
+ __attribute__((__format__ (__printf__, 2, 3)))
+ _CUPS_API_1_2;
+extern int cupsFilePutChar(cups_file_t *fp, int c) _CUPS_API_1_2;
+extern ssize_t cupsFilePutConf(cups_file_t *fp, const char *directive,
+ const char *value) _CUPS_API_1_4;
+extern int cupsFilePuts(cups_file_t *fp, const char *s)
+ _CUPS_API_1_2;
+extern ssize_t cupsFileRead(cups_file_t *fp, char *buf, size_t bytes)
+ _CUPS_API_1_2;
+extern off_t cupsFileRewind(cups_file_t *fp) _CUPS_API_1_2;
+extern off_t cupsFileSeek(cups_file_t *fp, off_t pos) _CUPS_API_1_2;
+extern cups_file_t *cupsFileStderr(void) _CUPS_API_1_2;
+extern cups_file_t *cupsFileStdin(void) _CUPS_API_1_2;
+extern cups_file_t *cupsFileStdout(void) _CUPS_API_1_2;
+extern off_t cupsFileTell(cups_file_t *fp) _CUPS_API_1_2;
+extern int cupsFileUnlock(cups_file_t *fp) _CUPS_API_1_2;
+extern ssize_t cupsFileWrite(cups_file_t *fp, const char *buf,
+ size_t bytes) _CUPS_API_1_2;
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_FILE_H_ */
+
+/*
+ * End of "$Id: file.h 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/getdevices.c b/cups/libs/cups/getdevices.c
new file mode 100644
index 000000000..ea862a7ea
--- /dev/null
+++ b/cups/libs/cups/getdevices.c
@@ -0,0 +1,284 @@
+/*
+ * "$Id: getdevices.c 4216 2013-03-11 13:57:36Z msweet $"
+ *
+ * cupsGetDevices implementation for CUPS.
+ *
+ * Copyright 2008-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsGetDevices() - Get available printer devices.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * 'cupsGetDevices()' - Get available printer devices.
+ *
+ * This function sends a CUPS-Get-Devices request and streams the discovered
+ * devices to the specified callback function. The "timeout" parameter controls
+ * how long the request lasts, while the "include_schemes" and "exclude_schemes"
+ * parameters provide comma-delimited lists of backends to include or omit from
+ * the request respectively.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ipp_status_t /* O - Request status - @code IPP_OK@ on success. */
+cupsGetDevices(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ int timeout, /* I - Timeout in seconds or @code CUPS_TIMEOUT_DEFAULT@ */
+ const char *include_schemes, /* I - Comma-separated URI schemes to include or @code CUPS_INCLUDE_ALL@ */
+ const char *exclude_schemes, /* I - Comma-separated URI schemes to exclude or @code CUPS_EXCLUDE_NONE@ */
+ cups_device_cb_t callback, /* I - Callback function */
+ void *user_data) /* I - User data pointer */
+{
+ ipp_t *request, /* CUPS-Get-Devices request */
+ *response; /* CUPS-Get-Devices response */
+ ipp_attribute_t *attr; /* Current attribute */
+ const char *device_class, /* device-class value */
+ *device_id, /* device-id value */
+ *device_info, /* device-info value */
+ *device_location, /* device-location value */
+ *device_make_and_model, /* device-make-and-model value */
+ *device_uri; /* device-uri value */
+ int blocking; /* Current blocking-IO mode */
+ cups_option_t option; /* in/exclude-schemes option */
+ http_status_t status; /* HTTP status of request */
+ ipp_state_t state; /* IPP response state */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("cupsGetDevices(http=%p, timeout=%d, include_schemes=\"%s\", "
+ "exclude_schemes=\"%s\", callback=%p, user_data=%p)", http,
+ timeout, include_schemes, exclude_schemes, callback,
+ user_data));
+
+ if (!callback)
+ return (IPP_STATUS_ERROR_INTERNAL);
+
+ if (!http)
+ http = _cupsConnect();
+
+ if (!http)
+ return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
+
+ /*
+ * Create a CUPS-Get-Devices request...
+ */
+
+ request = ippNewRequest(IPP_OP_CUPS_GET_DEVICES);
+
+ if (timeout > 0)
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "timeout",
+ timeout);
+
+ if (include_schemes)
+ {
+ option.name = "include-schemes";
+ option.value = (char *)include_schemes;
+
+ cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
+ }
+
+ if (exclude_schemes)
+ {
+ option.name = "exclude-schemes";
+ option.value = (char *)exclude_schemes;
+
+ cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
+ }
+
+ /*
+ * Send the request and do any necessary authentication...
+ */
+
+ do
+ {
+ DEBUG_puts("2cupsGetDevices: Sending request...");
+ status = cupsSendRequest(http, request, "/", ippLength(request));
+
+ DEBUG_puts("2cupsGetDevices: Waiting for response status...");
+ while (status == HTTP_STATUS_CONTINUE)
+ status = httpUpdate(http);
+
+ if (status != HTTP_STATUS_OK)
+ {
+ httpFlush(http);
+
+ if (status == HTTP_STATUS_UNAUTHORIZED)
+ {
+ /*
+ * See if we can do authentication...
+ */
+
+ DEBUG_puts("2cupsGetDevices: Need authorization...");
+
+ if (!cupsDoAuthentication(http, "POST", "/"))
+ httpReconnect2(http, 30000, NULL);
+ else
+ {
+ status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ break;
+ }
+ }
+
+#ifdef HAVE_SSL
+ else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ /*
+ * Force a reconnect with encryption...
+ */
+
+ DEBUG_puts("2cupsGetDevices: Need encryption...");
+
+ if (!httpReconnect2(http, 30000, NULL))
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
+ }
+#endif /* HAVE_SSL */
+ }
+ }
+ while (status == HTTP_STATUS_UNAUTHORIZED ||
+ status == HTTP_STATUS_UPGRADE_REQUIRED);
+
+ DEBUG_printf(("2cupsGetDevices: status=%d", status));
+
+ ippDelete(request);
+
+ if (status != HTTP_STATUS_OK)
+ {
+ _cupsSetHTTPError(status);
+ return (cupsLastError());
+ }
+
+ /*
+ * Read the response in non-blocking mode...
+ */
+
+ blocking = httpGetBlocking(http);
+ httpBlocking(http, 0);
+
+ response = ippNew();
+ device_class = NULL;
+ device_id = NULL;
+ device_info = NULL;
+ device_location = "";
+ device_make_and_model = NULL;
+ device_uri = NULL;
+ attr = NULL;
+
+ DEBUG_puts("2cupsGetDevices: Reading response...");
+
+ do
+ {
+ if ((state = ippRead(http, response)) == IPP_STATE_ERROR)
+ break;
+
+ DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state,
+ response->last));
+
+ if (!response->attrs)
+ continue;
+
+ while (attr != response->last)
+ {
+ if (!attr)
+ attr = response->attrs;
+ else
+ attr = attr->next;
+
+ DEBUG_printf(("2cupsGetDevices: attr->name=\"%s\", attr->value_tag=%d",
+ attr->name, attr->value_tag));
+
+ if (!attr->name)
+ {
+ if (device_class && device_id && device_info && device_make_and_model &&
+ device_uri)
+ (*callback)(device_class, device_id, device_info,
+ device_make_and_model, device_uri, device_location,
+ user_data);
+
+ device_class = NULL;
+ device_id = NULL;
+ device_info = NULL;
+ device_location = "";
+ device_make_and_model = NULL;
+ device_uri = NULL;
+ }
+ else if (!strcmp(attr->name, "device-class") &&
+ attr->value_tag == IPP_TAG_KEYWORD)
+ device_class = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "device-id") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ device_id = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "device-info") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ device_info = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "device-location") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ device_location = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "device-make-and-model") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ device_make_and_model = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "device-uri") &&
+ attr->value_tag == IPP_TAG_URI)
+ device_uri = attr->values[0].string.text;
+ }
+ }
+ while (state != IPP_STATE_DATA);
+
+ DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state,
+ response->last));
+
+ if (device_class && device_id && device_info && device_make_and_model &&
+ device_uri)
+ (*callback)(device_class, device_id, device_info,
+ device_make_and_model, device_uri, device_location, user_data);
+
+ /*
+ * Set the IPP status and return...
+ */
+
+ httpBlocking(http, blocking);
+ httpFlush(http);
+
+ if (status == HTTP_STATUS_ERROR)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
+ else
+ {
+ attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
+
+ DEBUG_printf(("cupsGetDevices: status-code=%s, status-message=\"%s\"",
+ ippErrorString(response->request.status.status_code),
+ attr ? attr->values[0].string.text : ""));
+
+ _cupsSetError(response->request.status.status_code,
+ attr ? attr->values[0].string.text :
+ ippErrorString(response->request.status.status_code), 0);
+ }
+
+ ippDelete(response);
+
+ return (cupsLastError());
+}
+
+
+/*
+ * End of "$Id: getdevices.c 4216 2013-03-11 13:57:36Z msweet $".
+ */
diff --git a/cups/libs/cups/getifaddrs.c b/cups/libs/cups/getifaddrs.c
new file mode 100644
index 000000000..02903a799
--- /dev/null
+++ b/cups/libs/cups/getifaddrs.c
@@ -0,0 +1,266 @@
+/*
+ * "$Id: getifaddrs.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Network interface functions for CUPS.
+ *
+ * Copyright 2007-2010 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"
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * Contents:
+ *
+ * _cups_getifaddrs() - Get a list of network interfaces on the system.
+ * _cups_freeifaddrs() - Free an interface list...
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "http-private.h"
+
+
+#ifndef HAVE_GETIFADDRS
+/*
+ * '_cups_getifaddrs()' - Get a list of network interfaces on the system.
+ */
+
+int /* O - 0 on success, -1 on error */
+_cups_getifaddrs(struct ifaddrs **addrs)/* O - List of interfaces */
+{
+ int sock; /* Socket */
+ char buffer[65536], /* Buffer for address info */
+ *bufptr, /* Pointer into buffer */
+ *bufend; /* End of buffer */
+ struct ifconf conf; /* Interface configurations */
+ struct sockaddr addr; /* Address data */
+ struct ifreq *ifp; /* Interface data */
+ int ifpsize; /* Size of interface data */
+ struct ifaddrs *temp; /* Pointer to current interface */
+ struct ifreq request; /* Interface request */
+
+
+ /*
+ * Start with an empty list...
+ */
+
+ if (addrs == NULL)
+ return (-1);
+
+ *addrs = NULL;
+
+ /*
+ * Create a UDP socket to get the interface data...
+ */
+
+ memset (&addr, 0, sizeof(addr));
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return (-1);
+
+ /*
+ * Try to get the list of interfaces...
+ */
+
+ conf.ifc_len = sizeof(buffer);
+ conf.ifc_buf = buffer;
+
+ if (ioctl(sock, SIOCGIFCONF, &conf) < 0)
+ {
+ /*
+ * Couldn't get the list of interfaces...
+ */
+
+ close(sock);
+ return (-1);
+ }
+
+ /*
+ * OK, got the list of interfaces, now lets step through the
+ * buffer to pull them out...
+ */
+
+# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+# define sockaddr_len(a) ((a)->sa_len)
+# else
+# define sockaddr_len(a) (sizeof(struct sockaddr))
+# endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+ for (bufptr = buffer, bufend = buffer + conf.ifc_len;
+ bufptr < bufend;
+ bufptr += ifpsize)
+ {
+ /*
+ * Get the current interface information...
+ */
+
+ ifp = (struct ifreq *)bufptr;
+ ifpsize = sizeof(ifp->ifr_name) + sockaddr_len(&(ifp->ifr_addr));
+
+ if (ifpsize < sizeof(struct ifreq))
+ ifpsize = sizeof(struct ifreq);
+
+ memset(&request, 0, sizeof(request));
+ memcpy(request.ifr_name, ifp->ifr_name, sizeof(ifp->ifr_name));
+
+ /*
+ * Check the status of the interface...
+ */
+
+ if (ioctl(sock, SIOCGIFFLAGS, &request) < 0)
+ continue;
+
+ /*
+ * Allocate memory for a single interface record...
+ */
+
+ if ((temp = calloc(1, sizeof(struct ifaddrs))) == NULL)
+ {
+ /*
+ * Unable to allocate memory...
+ */
+
+ close(sock);
+ return (-1);
+ }
+
+ /*
+ * Add this record to the front of the list and copy the name, flags,
+ * and network address...
+ */
+
+ temp->ifa_next = *addrs;
+ *addrs = temp;
+ temp->ifa_name = strdup(ifp->ifr_name);
+ temp->ifa_flags = request.ifr_flags;
+ if ((temp->ifa_addr = calloc(1, sockaddr_len(&(ifp->ifr_addr)))) != NULL)
+ memcpy(temp->ifa_addr, &(ifp->ifr_addr), sockaddr_len(&(ifp->ifr_addr)));
+
+ /*
+ * Try to get the netmask for the interface...
+ */
+
+ if (!ioctl(sock, SIOCGIFNETMASK, &request))
+ {
+ /*
+ * Got it, make a copy...
+ */
+
+ if ((temp->ifa_netmask = calloc(1, sizeof(request.ifr_netmask))) != NULL)
+ memcpy(temp->ifa_netmask, &(request.ifr_netmask),
+ sizeof(request.ifr_netmask));
+ }
+
+ /*
+ * Then get the broadcast or point-to-point (destination) address,
+ * if applicable...
+ */
+
+ if (temp->ifa_flags & IFF_BROADCAST)
+ {
+ /*
+ * Have a broadcast address, so get it!
+ */
+
+ if (!ioctl(sock, SIOCGIFBRDADDR, &request))
+ {
+ /*
+ * Got it, make a copy...
+ */
+
+ if ((temp->ifa_broadaddr =
+ calloc(1, sizeof(request.ifr_broadaddr))) != NULL)
+ memcpy(temp->ifa_broadaddr, &(request.ifr_broadaddr),
+ sizeof(request.ifr_broadaddr));
+ }
+ }
+ else if (temp->ifa_flags & IFF_POINTOPOINT)
+ {
+ /*
+ * Point-to-point interface; grab the remote address...
+ */
+
+ if (!ioctl(sock, SIOCGIFDSTADDR, &request))
+ {
+ temp->ifa_dstaddr = malloc(sizeof(request.ifr_dstaddr));
+ memcpy(temp->ifa_dstaddr, &(request.ifr_dstaddr),
+ sizeof(request.ifr_dstaddr));
+ }
+ }
+ }
+
+ /*
+ * OK, we're done with the socket, close it and return 0...
+ */
+
+ close(sock);
+
+ return (0);
+}
+
+
+/*
+ * '_cups_freeifaddrs()' - Free an interface list...
+ */
+
+void
+_cups_freeifaddrs(struct ifaddrs *addrs)/* I - Interface list to free */
+{
+ struct ifaddrs *next; /* Next interface in list */
+
+
+ while (addrs != NULL)
+ {
+ /*
+ * Make a copy of the next interface pointer...
+ */
+
+ next = addrs->ifa_next;
+
+ /*
+ * Free data values as needed...
+ */
+
+ if (addrs->ifa_name)
+ {
+ free(addrs->ifa_name);
+ addrs->ifa_name = NULL;
+ }
+
+ if (addrs->ifa_addr)
+ {
+ free(addrs->ifa_addr);
+ addrs->ifa_addr = NULL;
+ }
+
+ if (addrs->ifa_netmask)
+ {
+ free(addrs->ifa_netmask);
+ addrs->ifa_netmask = NULL;
+ }
+
+ if (addrs->ifa_dstaddr)
+ {
+ free(addrs->ifa_dstaddr);
+ addrs->ifa_dstaddr = NULL;
+ }
+
+ /*
+ * Free this node and continue to the next...
+ */
+
+ free(addrs);
+
+ addrs = next;
+ }
+}
+#endif /* !HAVE_GETIFADDRS */
+
+
+/*
+ * End of "$Id: getifaddrs.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/getputfile.c b/cups/libs/cups/getputfile.c
new file mode 100644
index 000000000..33c5e5b8f
--- /dev/null
+++ b/cups/libs/cups/getputfile.c
@@ -0,0 +1,522 @@
+/*
+ * "$Id: getputfile.c 11153 2013-07-17 14:10:21Z msweet $"
+ *
+ * Get/put file functions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsGetFd() - Get a file from the server.
+ * cupsGetFile() - Get a file from the server.
+ * cupsPutFd() - Put a file on the server.
+ * cupsPutFile() - Put a file on the server.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'cupsGetFd()' - Get a file from the server.
+ *
+ * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
+ *
+ * @since CUPS 1.1.20/OS X 10.4@
+ */
+
+http_status_t /* O - HTTP status */
+cupsGetFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *resource, /* I - Resource name */
+ int fd) /* I - File descriptor */
+{
+ int bytes; /* Number of bytes read */
+ char buffer[8192]; /* Buffer for file */
+ http_status_t status; /* HTTP status from server */
+ char if_modified_since[HTTP_MAX_VALUE];
+ /* If-Modified-Since header */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", http,
+ resource, fd));
+
+ if (!resource || fd < 0)
+ {
+ if (http)
+ http->error = EINVAL;
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+
+ /*
+ * Then send GET requests to the HTTP server...
+ */
+
+ strlcpy(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE),
+ sizeof(if_modified_since));
+
+ do
+ {
+ if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
+ {
+ httpClearFields(http);
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ }
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
+ httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since);
+
+ if (httpGet(http, resource))
+ {
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ else
+ {
+ status = HTTP_STATUS_UNAUTHORIZED;
+ continue;
+ }
+ }
+
+ while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
+
+ if (status == HTTP_STATUS_UNAUTHORIZED)
+ {
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+
+ /*
+ * See if we can do authentication...
+ */
+
+ if (cupsDoAuthentication(http, "GET", resource))
+ {
+ status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ break;
+ }
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ continue;
+ }
+#ifdef HAVE_SSL
+ else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ /* Flush any error message... */
+ httpFlush(http);
+
+ /* Reconnect... */
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ /* Upgrade with encryption... */
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
+
+ /* Try again, this time with encryption enabled... */
+ continue;
+ }
+#endif /* HAVE_SSL */
+ }
+ while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED);
+
+ /*
+ * See if we actually got the file or an error...
+ */
+
+ if (status == HTTP_STATUS_OK)
+ {
+ /*
+ * Yes, copy the file...
+ */
+
+ while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
+ write(fd, buffer, bytes);
+ }
+ else
+ {
+ _cupsSetHTTPError(status);
+ httpFlush(http);
+ }
+
+ /*
+ * Return the request status...
+ */
+
+ DEBUG_printf(("1cupsGetFd: Returning %d...", status));
+
+ return (status);
+}
+
+
+/*
+ * 'cupsGetFile()' - Get a file from the server.
+ *
+ * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
+ *
+ * @since CUPS 1.1.20/OS X 10.4@
+ */
+
+http_status_t /* O - HTTP status */
+cupsGetFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *resource, /* I - Resource name */
+ const char *filename) /* I - Filename */
+{
+ int fd; /* File descriptor */
+ http_status_t status; /* Status */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !resource || !filename)
+ {
+ if (http)
+ http->error = EINVAL;
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Create the file...
+ */
+
+ if ((fd = open(filename, O_WRONLY | O_EXCL | O_TRUNC)) < 0)
+ {
+ /*
+ * Couldn't open the file!
+ */
+
+ http->error = errno;
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Get the file...
+ */
+
+ status = cupsGetFd(http, resource, fd);
+
+ /*
+ * If the file couldn't be gotten, then remove the file...
+ */
+
+ close(fd);
+
+ if (status != HTTP_STATUS_OK)
+ unlink(filename);
+
+ /*
+ * Return the HTTP status code...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'cupsPutFd()' - Put a file on the server.
+ *
+ * This function returns @code HTTP_STATUS_CREATED@ when the file is stored
+ * successfully.
+ *
+ * @since CUPS 1.1.20/OS X 10.4@
+ */
+
+http_status_t /* O - HTTP status */
+cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *resource, /* I - Resource name */
+ int fd) /* I - File descriptor */
+{
+ int bytes, /* Number of bytes read */
+ retries; /* Number of retries */
+ char buffer[8192]; /* Buffer for file */
+ http_status_t status; /* HTTP status from server */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", http,
+ resource, fd));
+
+ if (!resource || fd < 0)
+ {
+ if (http)
+ http->error = EINVAL;
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+
+ /*
+ * Then send PUT requests to the HTTP server...
+ */
+
+ retries = 0;
+
+ do
+ {
+ if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
+ {
+ httpClearFields(http);
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ }
+
+ DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...",
+ http->authstring));
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
+ httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked");
+ httpSetExpect(http, HTTP_STATUS_CONTINUE);
+
+ if (httpPut(http, resource))
+ {
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ else
+ {
+ status = HTTP_STATUS_UNAUTHORIZED;
+ continue;
+ }
+ }
+
+ /*
+ * Wait up to 1 second for a 100-continue response...
+ */
+
+ if (httpWait(http, 1000))
+ status = httpUpdate(http);
+ else
+ status = HTTP_STATUS_CONTINUE;
+
+ if (status == HTTP_STATUS_CONTINUE)
+ {
+ /*
+ * Copy the file...
+ */
+
+ lseek(fd, 0, SEEK_SET);
+
+ while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
+ if (httpCheck(http))
+ {
+ if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE)
+ break;
+ }
+ else
+ httpWrite2(http, buffer, bytes);
+ }
+
+ if (status == HTTP_STATUS_CONTINUE)
+ {
+ httpWrite2(http, buffer, 0);
+
+ while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
+ }
+
+ if (status == HTTP_STATUS_ERROR && !retries)
+ {
+ DEBUG_printf(("2cupsPutFd: retry on status %d", status));
+
+ retries ++;
+
+ /* Flush any error message... */
+ httpFlush(http);
+
+ /* Reconnect... */
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ /* Try again... */
+ continue;
+ }
+
+ DEBUG_printf(("2cupsPutFd: status=%d", status));
+
+ if (status == HTTP_STATUS_UNAUTHORIZED)
+ {
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+
+ /*
+ * See if we can do authentication...
+ */
+
+ if (cupsDoAuthentication(http, "PUT", resource))
+ {
+ status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ break;
+ }
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ continue;
+ }
+#ifdef HAVE_SSL
+ else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ /* Flush any error message... */
+ httpFlush(http);
+
+ /* Reconnect... */
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ /* Upgrade with encryption... */
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
+
+ /* Try again, this time with encryption enabled... */
+ continue;
+ }
+#endif /* HAVE_SSL */
+ }
+ while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED ||
+ (status == HTTP_STATUS_ERROR && retries < 2));
+
+ /*
+ * See if we actually put the file or an error...
+ */
+
+ if (status != HTTP_STATUS_CREATED)
+ {
+ _cupsSetHTTPError(status);
+ httpFlush(http);
+ }
+
+ DEBUG_printf(("1cupsPutFd: Returning %d...", status));
+
+ return (status);
+}
+
+
+/*
+ * 'cupsPutFile()' - Put a file on the server.
+ *
+ * This function returns @code HTTP_CREATED@ when the file is stored
+ * successfully.
+ *
+ * @since CUPS 1.1.20/OS X 10.4@
+ */
+
+http_status_t /* O - HTTP status */
+cupsPutFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *resource, /* I - Resource name */
+ const char *filename) /* I - Filename */
+{
+ int fd; /* File descriptor */
+ http_status_t status; /* Status */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !resource || !filename)
+ {
+ if (http)
+ http->error = EINVAL;
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Open the local file...
+ */
+
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ {
+ /*
+ * Couldn't open the file!
+ */
+
+ http->error = errno;
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Put the file...
+ */
+
+ status = cupsPutFd(http, resource, fd);
+
+ close(fd);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: getputfile.c 11153 2013-07-17 14:10:21Z msweet $".
+ */
diff --git a/cups/libs/cups/globals.c b/cups/libs/cups/globals.c
new file mode 100644
index 000000000..c7440b612
--- /dev/null
+++ b/cups/libs/cups/globals.c
@@ -0,0 +1,398 @@
+/*
+ * "$Id: globals.c 11113 2013-07-10 14:08:39Z msweet $"
+ *
+ * Global variable access routines for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsGlobalLock() - Lock the global mutex.
+ * _cupsGlobals() - Return a pointer to thread local storage
+ * _cupsGlobalUnlock() - Unlock the global mutex.
+ * DllMain() - Main entry for library.
+ * cups_fix_path() - Fix a file path to use forward slashes consistently.
+ * cups_globals_alloc() - Allocate and initialize global data.
+ * cups_globals_free() - Free global data.
+ * cups_globals_init() - Initialize environment variables.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local globals...
+ */
+
+
+#ifdef DEBUG
+static int cups_global_index = 0;
+ /* Next thread number */
+#endif /* DEBUG */
+static _cups_threadkey_t cups_globals_key = _CUPS_THREADKEY_INITIALIZER;
+ /* Thread local storage key */
+#ifdef HAVE_PTHREAD_H
+static pthread_once_t cups_globals_key_once = PTHREAD_ONCE_INIT;
+ /* One-time initialization object */
+#endif /* HAVE_PTHREAD_H */
+#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+static _cups_mutex_t cups_global_mutex = _CUPS_MUTEX_INITIALIZER;
+ /* Global critical section */
+#endif /* HAVE_PTHREAD_H || WIN32 */
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef WIN32
+static void cups_fix_path(char *path);
+#endif /* WIN32 */
+static _cups_globals_t *cups_globals_alloc(void);
+#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+static void cups_globals_free(_cups_globals_t *g);
+#endif /* HAVE_PTHREAD_H || WIN32 */
+#ifdef HAVE_PTHREAD_H
+static void cups_globals_init(void);
+#endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * '_cupsGlobalLock()' - Lock the global mutex.
+ */
+
+void
+_cupsGlobalLock(void)
+{
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_lock(&cups_global_mutex);
+#elif defined(WIN32)
+ EnterCriticalSection(&cups_global_mutex.m_criticalSection);
+#endif /* HAVE_PTHREAD_H */
+}
+
+
+/*
+ * '_cupsGlobals()' - Return a pointer to thread local storage
+ */
+
+_cups_globals_t * /* O - Pointer to global data */
+_cupsGlobals(void)
+{
+ _cups_globals_t *cg; /* Pointer to global data */
+
+
+#ifdef HAVE_PTHREAD_H
+ /*
+ * Initialize the global data exactly once...
+ */
+
+ pthread_once(&cups_globals_key_once, cups_globals_init);
+#endif /* HAVE_PTHREAD_H */
+
+ /*
+ * See if we have allocated the data yet...
+ */
+
+ if ((cg = (_cups_globals_t *)_cupsThreadGetData(cups_globals_key)) == NULL)
+ {
+ /*
+ * No, allocate memory as set the pointer for the key...
+ */
+
+ if ((cg = cups_globals_alloc()) != NULL)
+ _cupsThreadSetData(cups_globals_key, cg);
+ }
+
+ /*
+ * Return the pointer to the data...
+ */
+
+ return (cg);
+}
+
+
+/*
+ * '_cupsGlobalUnlock()' - Unlock the global mutex.
+ */
+
+void
+_cupsGlobalUnlock(void)
+{
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_unlock(&cups_global_mutex);
+#elif defined(WIN32)
+ LeaveCriticalSection(&cups_global_mutex.m_criticalSection);
+#endif /* HAVE_PTHREAD_H */
+}
+
+
+#ifdef WIN32
+#if 0
+/*
+ * 'DllMain()' - Main entry for library.
+ */
+
+BOOL WINAPI /* O - Success/failure */
+DllMain(HINSTANCE hinst, /* I - DLL module handle */
+ DWORD reason, /* I - Reason */
+ LPVOID reserved) /* I - Unused */
+{
+ _cups_globals_t *cg; /* Global data */
+
+
+ (void)hinst;
+ (void)reserved;
+
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH : /* Called on library initialization */
+ InitializeCriticalSection(&cups_global_mutex.m_criticalSection);
+
+ if ((cups_globals_key = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ return (FALSE);
+ break;
+
+ case DLL_THREAD_DETACH : /* Called when a thread terminates */
+ if ((cg = (_cups_globals_t *)TlsGetValue(cups_globals_key)) != NULL)
+ cups_globals_free(cg);
+ break;
+
+ case DLL_PROCESS_DETACH : /* Called when library is unloaded */
+ if ((cg = (_cups_globals_t *)TlsGetValue(cups_globals_key)) != NULL)
+ cups_globals_free(cg);
+
+ TlsFree(cups_globals_key);
+ DeleteCriticalSection(&cups_global_mutex.m_criticalSection);
+ break;
+
+ default:
+ break;
+ }
+
+ return (TRUE);
+}
+#endif /* 0 */
+#endif /* WIN32 */
+
+
+/*
+ * 'cups_globals_alloc()' - Allocate and initialize global data.
+ */
+
+static _cups_globals_t * /* O - Pointer to global data */
+cups_globals_alloc(void)
+{
+ _cups_globals_t *cg = malloc(sizeof(_cups_globals_t));
+ /* Pointer to global data */
+#ifdef WIN32
+ HKEY key; /* Registry key */
+ DWORD size; /* Size of string */
+ static char installdir[1024] = "", /* Install directory */
+ confdir[1024] = "", /* Server root directory */
+ localedir[1024] = ""; /* Locale directory */
+#endif /* WIN32 */
+
+
+ if (!cg)
+ return (NULL);
+
+ /*
+ * Clear the global storage and set the default encryption and password
+ * callback values...
+ */
+
+ memset(cg, 0, sizeof(_cups_globals_t));
+ cg->encryption = (http_encryption_t)-1;
+ cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
+ cg->any_root = 1;
+ cg->expired_certs = 1;
+ cg->expired_root = 1;
+
+#ifdef DEBUG
+ /*
+ * Friendly thread ID for debugging...
+ */
+
+ cg->thread_id = ++ cups_global_index;
+#endif /* DEBUG */
+
+ /*
+ * Then set directories as appropriate...
+ */
+
+#ifdef WIN32
+ if (!installdir[0])
+ {
+ /*
+ * Open the registry...
+ */
+
+ strlcpy(installdir, "C:/Program Files/cups.org", sizeof(installdir));
+
+ if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\cups.org", 0, KEY_READ,
+ &key))
+ {
+ /*
+ * Grab the installation directory...
+ */
+
+ char *ptr; /* Pointer into installdir */
+
+ size = sizeof(installdir);
+ RegQueryValueEx(key, "installdir", NULL, NULL, installdir, &size);
+ RegCloseKey(key);
+
+ for (ptr = installdir; *ptr;)
+ {
+ if (*ptr == '\\')
+ {
+ if (ptr[1])
+ *ptr++ = '/';
+ else
+ *ptr = '\0'; /* Strip trailing \ */
+ }
+ else if (*ptr == '/' && !ptr[1])
+ *ptr = '\0'; /* Strip trailing / */
+ else
+ ptr ++;
+ }
+ }
+
+ snprintf(confdir, sizeof(confdir), "%s/conf", installdir);
+ snprintf(localedir, sizeof(localedir), "%s/locale", installdir);
+ }
+
+ if ((cg->cups_datadir = getenv("CUPS_DATADIR")) == NULL)
+ cg->cups_datadir = installdir;
+
+ if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+ cg->cups_serverbin = installdir;
+
+ if ((cg->cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
+ cg->cups_serverroot = confdir;
+
+ if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
+ cg->cups_statedir = confdir;
+
+ if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
+ cg->localedir = localedir;
+
+#else
+# ifdef HAVE_GETEUID
+ if ((geteuid() != getuid() && getuid()) || getegid() != getgid())
+# else
+ if (!getuid())
+# endif /* HAVE_GETEUID */
+ {
+ /*
+ * When running setuid/setgid, don't allow environment variables to override
+ * the directories...
+ */
+
+ cg->cups_datadir = CUPS_DATADIR;
+ cg->cups_serverbin = CUPS_SERVERBIN;
+ cg->cups_serverroot = CUPS_SERVERROOT;
+ cg->cups_statedir = CUPS_STATEDIR;
+ cg->localedir = CUPS_LOCALEDIR;
+ }
+ else
+ {
+ /*
+ * Allow directories to be overridden by environment variables.
+ */
+
+ if ((cg->cups_datadir = getenv("CUPS_DATADIR")) == NULL)
+ cg->cups_datadir = CUPS_DATADIR;
+
+ if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+ cg->cups_serverbin = CUPS_SERVERBIN;
+
+ if ((cg->cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
+ cg->cups_serverroot = CUPS_SERVERROOT;
+
+ if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
+ cg->cups_statedir = CUPS_STATEDIR;
+
+ if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
+ cg->localedir = CUPS_LOCALEDIR;
+ }
+#endif /* WIN32 */
+
+ return (cg);
+}
+
+
+/*
+ * 'cups_globals_free()' - Free global data.
+ */
+
+#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+static void
+cups_globals_free(_cups_globals_t *cg) /* I - Pointer to global data */
+{
+ _cups_buffer_t *buffer, /* Current read/write buffer */
+ *next; /* Next buffer */
+
+
+ if (cg->last_status_message)
+ _cupsStrFree(cg->last_status_message);
+
+ for (buffer = cg->cups_buffers; buffer; buffer = next)
+ {
+ next = buffer->next;
+ free(buffer);
+ }
+
+ cupsArrayDelete(cg->leg_size_lut);
+ cupsArrayDelete(cg->ppd_size_lut);
+ cupsArrayDelete(cg->pwg_size_lut);
+
+ httpClose(cg->http);
+
+ _httpFreeCredentials(cg->tls_credentials);
+
+ cupsFileClose(cg->stdio_files[0]);
+ cupsFileClose(cg->stdio_files[1]);
+ cupsFileClose(cg->stdio_files[2]);
+
+ cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings);
+
+ free(cg);
+}
+#endif /* HAVE_PTHREAD_H || WIN32 */
+
+
+#ifdef HAVE_PTHREAD_H
+/*
+ * 'cups_globals_init()' - Initialize environment variables.
+ */
+
+static void
+cups_globals_init(void)
+{
+ /*
+ * Register the global data for this thread...
+ */
+
+ pthread_key_create(&cups_globals_key, (void (*)(void *))cups_globals_free);
+}
+#endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * End of "$Id: globals.c 11113 2013-07-10 14:08:39Z msweet $".
+ */
diff --git a/cups/libs/cups/http-addr.c b/cups/libs/cups/http-addr.c
new file mode 100644
index 000000000..e8daaf3e7
--- /dev/null
+++ b/cups/libs/cups/http-addr.c
@@ -0,0 +1,760 @@
+/*
+ * "$Id: http-addr.c 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * HTTP address routines 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/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif /* HAVE_RESOLV_H */
+#ifdef __APPLE__
+# include <CoreFoundation/CoreFoundation.h>
+# include <SystemConfiguration/SystemConfiguration.h>
+#endif /* __APPLE__ */
+
+
+/*
+ * 'httpAddrAny()' - Check for the "any" address.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 if "any", 0 otherwise */
+httpAddrAny(const http_addr_t *addr) /* I - Address to check */
+{
+ if (!addr)
+ return (0);
+
+#ifdef AF_INET6
+ if (addr->addr.sa_family == AF_INET6 &&
+ IN6_IS_ADDR_UNSPECIFIED(&(addr->ipv6.sin6_addr)))
+ return (1);
+#endif /* AF_INET6 */
+
+ if (addr->addr.sa_family == AF_INET &&
+ ntohl(addr->ipv4.sin_addr.s_addr) == 0x00000000)
+ return (1);
+
+ return (0);
+}
+
+
+/*
+ * 'httpAddrEqual()' - Compare two addresses.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 if equal, 0 if not */
+httpAddrEqual(const http_addr_t *addr1, /* I - First address */
+ const http_addr_t *addr2) /* I - Second address */
+{
+ if (!addr1 && !addr2)
+ return (1);
+
+ if (!addr1 || !addr2)
+ return (0);
+
+ if (addr1->addr.sa_family != addr2->addr.sa_family)
+ return (0);
+
+#ifdef AF_LOCAL
+ if (addr1->addr.sa_family == AF_LOCAL)
+ return (!strcmp(addr1->un.sun_path, addr2->un.sun_path));
+#endif /* AF_LOCAL */
+
+#ifdef AF_INET6
+ if (addr1->addr.sa_family == AF_INET6)
+ return (!memcmp(&(addr1->ipv6.sin6_addr), &(addr2->ipv6.sin6_addr), 16));
+#endif /* AF_INET6 */
+
+ return (addr1->ipv4.sin_addr.s_addr == addr2->ipv4.sin_addr.s_addr);
+}
+
+
+/*
+ * 'httpAddrLength()' - Return the length of the address in bytes.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Length in bytes */
+httpAddrLength(const http_addr_t *addr) /* I - Address */
+{
+ if (!addr)
+ return (0);
+
+#ifdef AF_INET6
+ if (addr->addr.sa_family == AF_INET6)
+ return (sizeof(addr->ipv6));
+ else
+#endif /* AF_INET6 */
+#ifdef AF_LOCAL
+ if (addr->addr.sa_family == AF_LOCAL)
+ return (offsetof(struct sockaddr_un, sun_path) +
+ strlen(addr->un.sun_path) + 1);
+ else
+#endif /* AF_LOCAL */
+ if (addr->addr.sa_family == AF_INET)
+ return (sizeof(addr->ipv4));
+ else
+ return (0);
+
+}
+
+
+/*
+ * 'httpAddrListen()' - Create a listening socket bound to the specified
+ * address and port.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - Socket or -1 on error */
+httpAddrListen(http_addr_t *addr, /* I - Address to bind to */
+ int port) /* I - Port number to bind to */
+{
+ int fd = -1, /* Socket */
+ val; /* Socket value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!addr || port <= 0)
+ return (-1);
+
+ if ((fd = socket(addr->addr.sa_family, SOCK_STREAM, 0)) < 0)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ return (-1);
+ }
+
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
+
+#ifdef IPV6_V6ONLY
+ if (addr->addr.sa_family == AF_INET6)
+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, CUPS_SOCAST &val, sizeof(val));
+#endif /* IPV6_V6ONLY */
+
+ _httpAddrSetPort(addr, port);
+
+ if (bind(fd, (struct sockaddr *)addr, httpAddrLength(addr)))
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+ close(fd);
+
+ return (-1);
+ }
+
+ if (listen(fd, 5))
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+ close(fd);
+
+ return (-1);
+ }
+
+#ifdef SO_NOSIGPIPE
+ /*
+ * Disable SIGPIPE for this socket.
+ */
+
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
+#endif /* SO_NOSIGPIPE */
+
+ return (fd);
+}
+
+
+/*
+ * 'httpAddrLocalhost()' - Check for the local loopback address.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 if local host, 0 otherwise */
+httpAddrLocalhost(
+ const http_addr_t *addr) /* I - Address to check */
+{
+ if (!addr)
+ return (1);
+
+#ifdef AF_INET6
+ if (addr->addr.sa_family == AF_INET6 &&
+ IN6_IS_ADDR_LOOPBACK(&(addr->ipv6.sin6_addr)))
+ return (1);
+#endif /* AF_INET6 */
+
+#ifdef AF_LOCAL
+ if (addr->addr.sa_family == AF_LOCAL)
+ return (1);
+#endif /* AF_LOCAL */
+
+ if (addr->addr.sa_family == AF_INET &&
+ (ntohl(addr->ipv4.sin_addr.s_addr) & 0xff000000) == 0x7f000000)
+ return (1);
+
+ return (0);
+}
+
+
+/*
+ * 'httpAddrLookup()' - Lookup the hostname associated with the address.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Host name */
+httpAddrLookup(
+ const http_addr_t *addr, /* I - Address to lookup */
+ char *name, /* I - Host name buffer */
+ int namelen) /* I - Size of name buffer */
+{
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ DEBUG_printf(("httpAddrLookup(addr=%p, name=%p, namelen=%d)", addr, name,
+ namelen));
+
+ /*
+ * Range check input...
+ */
+
+ if (!addr || !name || namelen <= 2)
+ {
+ if (name && namelen >= 1)
+ *name = '\0';
+
+ return (NULL);
+ }
+
+#ifdef AF_LOCAL
+ if (addr->addr.sa_family == AF_LOCAL)
+ {
+ strlcpy(name, addr->un.sun_path, namelen);
+ return (name);
+ }
+#endif /* AF_LOCAL */
+
+ /*
+ * Optimize lookups for localhost/loopback addresses...
+ */
+
+ if (httpAddrLocalhost(addr))
+ {
+ strlcpy(name, "localhost", namelen);
+ return (name);
+ }
+
+#ifdef HAVE_RES_INIT
+ /*
+ * STR #2920: Initialize resolver after failure in cups-polld
+ *
+ * If the previous lookup failed, re-initialize the resolver to prevent
+ * temporary network errors from persisting. This *should* be handled by
+ * the resolver libraries, but apparently the glibc folks do not agree.
+ *
+ * We set a flag at the end of this function if we encounter an error that
+ * requires reinitialization of the resolver functions. We then call
+ * res_init() if the flag is set on the next call here or in httpAddrLookup().
+ */
+
+ if (cg->need_res_init)
+ {
+ res_init();
+
+ cg->need_res_init = 0;
+ }
+#endif /* HAVE_RES_INIT */
+
+#ifdef HAVE_GETNAMEINFO
+ {
+ /*
+ * STR #2486: httpAddrLookup() fails when getnameinfo() returns EAI_AGAIN
+ *
+ * FWIW, I think this is really a bug in the implementation of
+ * getnameinfo(), but falling back on httpAddrString() is easy to
+ * do...
+ */
+
+ int error = getnameinfo(&addr->addr, httpAddrLength(addr), name, namelen,
+ NULL, 0, 0);
+
+ if (error)
+ {
+ if (error == EAI_FAIL)
+ cg->need_res_init = 1;
+
+ return (httpAddrString(addr, name, namelen));
+ }
+ }
+#else
+ {
+ struct hostent *host; /* Host from name service */
+
+
+# ifdef AF_INET6
+ if (addr->addr.sa_family == AF_INET6)
+ host = gethostbyaddr((char *)&(addr->ipv6.sin6_addr),
+ sizeof(struct in_addr), AF_INET6);
+ else
+# endif /* AF_INET6 */
+ host = gethostbyaddr((char *)&(addr->ipv4.sin_addr),
+ sizeof(struct in_addr), AF_INET);
+
+ if (host == NULL)
+ {
+ /*
+ * No hostname, so return the raw address...
+ */
+
+ if (h_errno == NO_RECOVERY)
+ cg->need_res_init = 1;
+
+ return (httpAddrString(addr, name, namelen));
+ }
+
+ strlcpy(name, host->h_name, namelen);
+ }
+#endif /* HAVE_GETNAMEINFO */
+
+ DEBUG_printf(("1httpAddrLookup: returning \"%s\"...", name));
+
+ return (name);
+}
+
+
+/*
+ * 'httpAddrPort()' - Get the port number associated with an address.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - Port number */
+httpAddrPort(http_addr_t *addr) /* I - Address */
+{
+ if (!addr)
+ return (ippPort());
+#ifdef AF_INET6
+ else if (addr->addr.sa_family == AF_INET6)
+ return (ntohs(addr->ipv6.sin6_port));
+#endif /* AF_INET6 */
+ else if (addr->addr.sa_family == AF_INET)
+ return (ntohs(addr->ipv4.sin_port));
+ else
+ return (ippPort());
+}
+
+/* For OS X 10.8 and earlier */
+int _httpAddrPort(http_addr_t *addr) { return (httpAddrPort(addr)); }
+
+
+/*
+ * '_httpAddrSetPort()' - Set the port number associated with an address.
+ */
+
+void
+_httpAddrSetPort(http_addr_t *addr, /* I - Address */
+ int port) /* I - Port */
+{
+ if (!addr || port <= 0)
+ return;
+
+#ifdef AF_INET6
+ if (addr->addr.sa_family == AF_INET6)
+ addr->ipv6.sin6_port = htons(port);
+ else
+#endif /* AF_INET6 */
+ if (addr->addr.sa_family == AF_INET)
+ addr->ipv4.sin_port = htons(port);
+}
+
+
+/*
+ * 'httpAddrString()' - Convert an address to a numeric string.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Numeric address string */
+httpAddrString(const http_addr_t *addr, /* I - Address to convert */
+ char *s, /* I - String buffer */
+ int slen) /* I - Length of string */
+{
+ DEBUG_printf(("httpAddrString(addr=%p, s=%p, slen=%d)", addr, s, slen));
+
+ /*
+ * Range check input...
+ */
+
+ if (!addr || !s || slen <= 2)
+ {
+ if (s && slen >= 1)
+ *s = '\0';
+
+ return (NULL);
+ }
+
+#ifdef AF_LOCAL
+ if (addr->addr.sa_family == AF_LOCAL)
+ {
+ if (addr->un.sun_path[0] == '/')
+ strlcpy(s, addr->un.sun_path, slen);
+ else
+ strlcpy(s, "localhost", slen);
+ }
+ else
+#endif /* AF_LOCAL */
+ if (addr->addr.sa_family == AF_INET)
+ {
+ unsigned temp; /* Temporary address */
+
+
+ temp = ntohl(addr->ipv4.sin_addr.s_addr);
+
+ snprintf(s, slen, "%d.%d.%d.%d", (temp >> 24) & 255,
+ (temp >> 16) & 255, (temp >> 8) & 255, temp & 255);
+ }
+#ifdef AF_INET6
+ else if (addr->addr.sa_family == AF_INET6)
+ {
+ char *sptr, /* Pointer into string */
+ temps[64]; /* Temporary string for address */
+
+# ifdef HAVE_GETNAMEINFO
+ if (getnameinfo(&addr->addr, httpAddrLength(addr), temps, sizeof(temps),
+ NULL, 0, NI_NUMERICHOST))
+ {
+ /*
+ * If we get an error back, then the address type is not supported
+ * and we should zero out the buffer...
+ */
+
+ s[0] = '\0';
+
+ return (NULL);
+ }
+ else if ((sptr = strchr(temps, '%')) != NULL)
+ {
+ /*
+ * Convert "%zone" to "+zone" to match URI form...
+ */
+
+ *sptr = '+';
+ }
+
+# else
+ int i; /* Looping var */
+ unsigned temp; /* Current value */
+ const char *prefix; /* Prefix for address */
+
+
+ prefix = "";
+ for (sptr = temps, i = 0; i < 4 && addr->ipv6.sin6_addr.s6_addr32[i]; i ++)
+ {
+ temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
+
+ snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix,
+ (temp >> 16) & 0xffff);
+ prefix = ":";
+ sptr += strlen(sptr);
+
+ temp &= 0xffff;
+
+ if (temp || i == 3 || addr->ipv6.sin6_addr.s6_addr32[i + 1])
+ {
+ snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix, temp);
+ sptr += strlen(sptr);
+ }
+ }
+
+ if (i < 4)
+ {
+ while (i < 4 && !addr->ipv6.sin6_addr.s6_addr32[i])
+ i ++;
+
+ if (i < 4)
+ {
+ snprintf(sptr, sizeof(temps) - (sptr - temps), "%s:", prefix);
+ prefix = ":";
+ sptr += strlen(sptr);
+
+ for (; i < 4; i ++)
+ {
+ temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
+
+ if ((temp & 0xffff0000) ||
+ (i > 0 && addr->ipv6.sin6_addr.s6_addr32[i - 1]))
+ {
+ snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix,
+ (temp >> 16) & 0xffff);
+ sptr += strlen(sptr);
+ }
+
+ snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix,
+ temp & 0xffff);
+ sptr += strlen(sptr);
+ }
+ }
+ else if (sptr == s)
+ {
+ /*
+ * Empty address...
+ */
+
+ strlcpy(temps, "::", sizeof(temps));
+ }
+ else
+ {
+ /*
+ * Empty at end...
+ */
+
+ strlcpy(sptr, "::", sizeof(temps) - (sptr - temps));
+ }
+ }
+# endif /* HAVE_GETNAMEINFO */
+
+ /*
+ * Add "[v1." and "]" around IPv6 address to convert to URI form.
+ */
+
+ snprintf(s, slen, "[v1.%s]", temps);
+ }
+#endif /* AF_INET6 */
+ else
+ strlcpy(s, "UNKNOWN", slen);
+
+ DEBUG_printf(("1httpAddrString: returning \"%s\"...", s));
+
+ return (s);
+}
+
+
+/*
+ * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return
+ * address records for the specified name.
+ *
+ * @deprecated@
+ */
+
+struct hostent * /* O - Host entry */
+httpGetHostByName(const char *name) /* I - Hostname or IP address */
+{
+ const char *nameptr; /* Pointer into name */
+ unsigned ip[4]; /* IP address components */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+
+ DEBUG_printf(("httpGetHostByName(name=\"%s\")", name));
+
+ /*
+ * Avoid lookup delays and configuration problems when connecting
+ * to the localhost address...
+ */
+
+ if (!strcmp(name, "localhost"))
+ name = "127.0.0.1";
+
+ /*
+ * This function is needed because some operating systems have a
+ * buggy implementation of gethostbyname() that does not support
+ * IP addresses. If the first character of the name string is a
+ * number, then sscanf() is used to extract the IP components.
+ * We then pack the components into an IPv4 address manually,
+ * since the inet_aton() function is deprecated. We use the
+ * htonl() macro to get the right byte order for the address.
+ *
+ * We also support domain sockets when supported by the underlying
+ * OS...
+ */
+
+#ifdef AF_LOCAL
+ if (name[0] == '/')
+ {
+ /*
+ * A domain socket address, so make an AF_LOCAL entry and return it...
+ */
+
+ cg->hostent.h_name = (char *)name;
+ cg->hostent.h_aliases = NULL;
+ cg->hostent.h_addrtype = AF_LOCAL;
+ cg->hostent.h_length = strlen(name) + 1;
+ cg->hostent.h_addr_list = cg->ip_ptrs;
+ cg->ip_ptrs[0] = (char *)name;
+ cg->ip_ptrs[1] = NULL;
+
+ DEBUG_puts("1httpGetHostByName: returning domain socket address...");
+
+ return (&cg->hostent);
+ }
+#endif /* AF_LOCAL */
+
+ for (nameptr = name; isdigit(*nameptr & 255) || *nameptr == '.'; nameptr ++);
+
+ if (!*nameptr)
+ {
+ /*
+ * We have an IPv4 address; break it up and provide the host entry
+ * to the caller.
+ */
+
+ if (sscanf(name, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4)
+ return (NULL); /* Must have 4 numbers */
+
+ if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
+ return (NULL); /* Invalid byte ranges! */
+
+ cg->ip_addr = htonl((((((((unsigned)ip[0] << 8) | (unsigned)ip[1]) << 8) |
+ (unsigned)ip[2]) << 8) |
+ (unsigned)ip[3]));
+
+ /*
+ * Fill in the host entry and return it...
+ */
+
+ cg->hostent.h_name = (char *)name;
+ cg->hostent.h_aliases = NULL;
+ cg->hostent.h_addrtype = AF_INET;
+ cg->hostent.h_length = 4;
+ cg->hostent.h_addr_list = cg->ip_ptrs;
+ cg->ip_ptrs[0] = (char *)&(cg->ip_addr);
+ cg->ip_ptrs[1] = NULL;
+
+ DEBUG_puts("1httpGetHostByName: returning IPv4 address...");
+
+ return (&cg->hostent);
+ }
+ else
+ {
+ /*
+ * Use the gethostbyname() function to get the IPv4 address for
+ * the name...
+ */
+
+ DEBUG_puts("1httpGetHostByName: returning domain lookup address(es)...");
+
+ return (gethostbyname(name));
+ }
+}
+
+
+/*
+ * 'httpGetHostname()' - Get the FQDN for the connection or local system.
+ *
+ * When "http" points to a connected socket, return the hostname or
+ * address that was used in the call to httpConnect() or httpConnectEncrypt().
+ * Otherwise, return the FQDN for the local system using both gethostname()
+ * and gethostbyname() to get the local hostname with domain.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+const char * /* O - FQDN for connection or system */
+httpGetHostname(http_t *http, /* I - HTTP connection or NULL */
+ char *s, /* I - String buffer for name */
+ int slen) /* I - Size of buffer */
+{
+ if (!s || slen <= 1)
+ return (NULL);
+
+ if (http)
+ {
+ if (http->hostname[0] == '/')
+ strlcpy(s, "localhost", slen);
+ else
+ strlcpy(s, http->hostname, slen);
+ }
+ else
+ {
+ /*
+ * Get the hostname...
+ */
+
+ if (gethostname(s, slen) < 0)
+ strlcpy(s, "localhost", slen);
+
+ if (!strchr(s, '.'))
+ {
+#ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
+ /*
+ * The hostname is not a FQDN, so use the local hostname from the
+ * SystemConfiguration framework...
+ */
+
+ SCDynamicStoreRef sc = SCDynamicStoreCreate(kCFAllocatorDefault,
+ CFSTR("libcups"), NULL, NULL);
+ /* System configuration data */
+ CFStringRef local = sc ? SCDynamicStoreCopyLocalHostName(sc) : NULL;
+ /* Local host name */
+ char localStr[1024]; /* Local host name C string */
+
+ if (local && CFStringGetCString(local, localStr, sizeof(localStr),
+ kCFStringEncodingUTF8))
+ {
+ /*
+ * Append ".local." to the hostname we get...
+ */
+
+ snprintf(s, slen, "%s.local.", localStr);
+ }
+
+ if (local)
+ CFRelease(local);
+ if (sc)
+ CFRelease(sc);
+
+#else
+ /*
+ * The hostname is not a FQDN, so look it up...
+ */
+
+ struct hostent *host; /* Host entry to get FQDN */
+
+ if ((host = gethostbyname(s)) != NULL && host->h_name)
+ {
+ /*
+ * Use the resolved hostname...
+ */
+
+ strlcpy(s, host->h_name, slen);
+ }
+#endif /* HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME */
+ }
+ }
+
+ /*
+ * Return the hostname with as much domain info as we have...
+ */
+
+ return (s);
+}
+
+
+/*
+ * End of "$Id: http-addr.c 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/http-addrlist.c b/cups/libs/cups/http-addrlist.c
new file mode 100644
index 000000000..bb1ff949f
--- /dev/null
+++ b/cups/libs/cups/http-addrlist.c
@@ -0,0 +1,881 @@
+/*
+ * "$Id: http-addrlist.c 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * HTTP address list routines for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif /* HAVE_RESOLV_H */
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif /* HAVE_POLL */
+#ifndef WIN32
+# include <fcntl.h>
+#endif /* WIN32 */
+
+
+/*
+ * 'httpAddrConnect()' - Connect to any of the addresses in the list.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+http_addrlist_t * /* O - Connected address or NULL on failure */
+httpAddrConnect(
+ http_addrlist_t *addrlist, /* I - List of potential addresses */
+ int *sock) /* O - Socket */
+{
+ DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", addrlist, sock));
+
+ return (httpAddrConnect2(addrlist, sock, 30000, NULL));
+}
+
+
+/*
+ * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a
+ * timeout and optional cancel.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+http_addrlist_t * /* O - Connected address or NULL on failure */
+httpAddrConnect2(
+ http_addrlist_t *addrlist, /* I - List of potential addresses */
+ int *sock, /* O - Socket */
+ int msec, /* I - Timeout in milliseconds */
+ int *cancel) /* I - Pointer to "cancel" variable */
+{
+ int val; /* Socket option value */
+#ifdef O_NONBLOCK
+ socklen_t len; /* Length of value */
+ http_addr_t peer; /* Peer address */
+ int flags, /* Socket flags */
+ remaining; /* Remaining timeout */
+# ifdef HAVE_POLL
+ struct pollfd pfd; /* Polled file descriptor */
+# else
+ fd_set input_set, /* select() input set */
+ output_set; /* select() output set */
+ struct timeval timeout; /* Timeout */
+# endif /* HAVE_POLL */
+ int nfds; /* Result from select()/poll() */
+#endif /* O_NONBLOCK */
+#ifdef DEBUG
+ char temp[256]; /* Temporary address string */
+#endif /* DEBUG */
+
+
+ DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)",
+ addrlist, sock, msec, cancel));
+
+ if (!sock)
+ {
+ errno = EINVAL;
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ return (NULL);
+ }
+
+ if (cancel && *cancel)
+ return (NULL);
+
+ if (msec <= 0 || getenv("CUPS_DISABLE_ASYNC_CONNECT"))
+ msec = INT_MAX;
+
+ /*
+ * Loop through each address until we connect or run out of addresses...
+ */
+
+ while (addrlist)
+ {
+ if (cancel && *cancel)
+ return (NULL);
+
+ /*
+ * Create the socket...
+ */
+
+ DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...",
+ httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
+ httpAddrPort(&(addrlist->addr))));
+
+ if ((*sock = (int)socket(_httpAddrFamily(&(addrlist->addr)), SOCK_STREAM,
+ 0)) < 0)
+ {
+ /*
+ * Don't abort yet, as this could just be an issue with the local
+ * system not being configured with IPv4/IPv6/domain socket enabled...
+ */
+
+ addrlist = addrlist->next;
+ continue;
+ }
+
+ /*
+ * Set options...
+ */
+
+ val = 1;
+ setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
+
+#ifdef SO_REUSEPORT
+ val = 1;
+ setsockopt(*sock, SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val));
+#endif /* SO_REUSEPORT */
+
+#ifdef SO_NOSIGPIPE
+ val = 1;
+ setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
+#endif /* SO_NOSIGPIPE */
+
+ /*
+ * Using TCP_NODELAY improves responsiveness, especially on systems
+ * with a slow loopback interface...
+ */
+
+ val = 1;
+ setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
+
+#ifdef FD_CLOEXEC
+ /*
+ * Close this socket when starting another process...
+ */
+
+ fcntl(*sock, F_SETFD, FD_CLOEXEC);
+#endif /* FD_CLOEXEC */
+
+#ifdef O_NONBLOCK
+ /*
+ * Do an asynchronous connect by setting the socket non-blocking...
+ */
+
+ DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()"));
+
+ flags = fcntl(*sock, F_GETFL, 0);
+ if (msec != INT_MAX)
+ {
+ DEBUG_puts("httpAddrConnect2: Setting non-blocking connect()");
+
+ fcntl(*sock, F_SETFL, flags | O_NONBLOCK);
+ }
+#endif /* O_NONBLOCK */
+
+ /*
+ * Then connect...
+ */
+
+ if (!connect(*sock, &(addrlist->addr.addr),
+ httpAddrLength(&(addrlist->addr))))
+ {
+ DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...",
+ httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
+ httpAddrPort(&(addrlist->addr))));
+
+#ifdef O_NONBLOCK
+ fcntl(*sock, F_SETFL, flags);
+#endif /* O_NONBLOCK */
+
+ return (addrlist);
+ }
+
+#ifdef O_NONBLOCK
+# ifdef WIN32
+ if (WSAGetLastError() == WSAEINPROGRESS ||
+ WSAGetLastError() == WSAEWOULDBLOCK)
+# else
+ if (errno == EINPROGRESS || errno == EWOULDBLOCK)
+# endif /* WIN32 */
+ {
+ DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
+
+ fcntl(*sock, F_SETFL, flags);
+
+ for (remaining = msec; remaining > 0; remaining -= 250)
+ {
+ do
+ {
+ if (cancel && *cancel)
+ {
+ /*
+ * Close this socket and return...
+ */
+
+ DEBUG_puts("1httpAddrConnect2: Canceled connect()");
+
+# ifdef WIN32
+ closesocket(*sock);
+# else
+ close(*sock);
+# endif /* WIN32 */
+
+ *sock = -1;
+
+ return (NULL);
+ }
+
+# ifdef HAVE_POLL
+ pfd.fd = *sock;
+ pfd.events = POLLIN | POLLOUT;
+
+ nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining);
+
+ DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", nfds,
+ errno));
+
+# else
+ FD_ZERO(&input_set);
+ FD_SET(*sock, &input_set);
+ output_set = input_set;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = (remaining > 250 ? 250 : remaining) * 1000;
+
+ nfds = select(*sock + 1, &input_set, &output_set, NULL, &timeout);
+
+ DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", nfds,
+ errno));
+# endif /* HAVE_POLL */
+ }
+# ifdef WIN32
+ while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
+ WSAGetLastError() == WSAEWOULDBLOCK));
+# else
+ while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+# endif /* WIN32 */
+
+ if (nfds > 0)
+ {
+ len = sizeof(peer);
+ if (!getpeername(*sock, (struct sockaddr *)&peer, &len))
+ {
+ DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...",
+ httpAddrString(&peer, temp, sizeof(temp)),
+ httpAddrPort(&peer)));
+
+ return (addrlist);
+ }
+
+ break;
+ }
+ }
+ }
+#endif /* O_NONBLOCK */
+
+ DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s",
+ httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
+ httpAddrPort(&(addrlist->addr)), strerror(errno)));
+
+#ifndef WIN32
+ if (errno == EINPROGRESS)
+ errno = ETIMEDOUT;
+#endif /* !WIN32 */
+
+ /*
+ * Close this socket and move to the next address...
+ */
+
+#ifdef WIN32
+ closesocket(*sock);
+#else
+ close(*sock);
+#endif /* WIN32 */
+
+ *sock = -1;
+ addrlist = addrlist->next;
+ }
+
+ if (!addrlist)
+#ifdef WIN32
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
+#else
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
+#endif /* WIN32 */
+
+ return (addrlist);
+}
+
+
+
+/*
+ * 'httpAddrCopyList()' - Copy an address list.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+http_addrlist_t * /* O - New address list or @code NULL@ on error */
+httpAddrCopyList(
+ http_addrlist_t *src) /* I - Source address list */
+{
+ http_addrlist_t *dst = NULL, /* First list entry */
+ *prev = NULL, /* Previous list entry */
+ *current = NULL;/* Current list entry */
+
+
+ while (src)
+ {
+ if ((current = malloc(sizeof(http_addrlist_t))) == NULL)
+ {
+ current = dst;
+
+ while (current)
+ {
+ prev = current;
+ current = current->next;
+
+ free(prev);
+ }
+
+ return (NULL);
+ }
+
+ memcpy(current, src, sizeof(http_addrlist_t));
+
+ current->next = NULL;
+
+ if (prev)
+ prev->next = current;
+ else
+ dst = current;
+
+ prev = current;
+ src = src->next;
+ }
+
+ return (dst);
+}
+
+
+/*
+ * 'httpAddrFreeList()' - Free an address list.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+httpAddrFreeList(
+ http_addrlist_t *addrlist) /* I - Address list to free */
+{
+ http_addrlist_t *next; /* Next address in list */
+
+
+ /*
+ * Free each address in the list...
+ */
+
+ while (addrlist)
+ {
+ next = addrlist->next;
+
+ free(addrlist);
+
+ addrlist = next;
+ }
+}
+
+
+/*
+ * 'httpAddrGetList()' - Get a list of addresses for a hostname.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+http_addrlist_t * /* O - List of addresses or NULL */
+httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for passive listen address */
+ int family, /* I - Address family or AF_UNSPEC */
+ const char *service) /* I - Service name or port number */
+{
+ http_addrlist_t *first, /* First address in list */
+ *addr, /* Current address in list */
+ *temp; /* New address */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+#ifdef DEBUG
+ _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
+ "service=\"%s\")\n",
+ hostname ? hostname : "(nil)",
+ family == AF_UNSPEC ? "UNSPEC" :
+# ifdef AF_LOCAL
+ family == AF_LOCAL ? "LOCAL" :
+# endif /* AF_LOCAL */
+# ifdef AF_INET6
+ family == AF_INET6 ? "INET6" :
+# endif /* AF_INET6 */
+ family == AF_INET ? "INET" : "???", service);
+#endif /* DEBUG */
+
+#ifdef HAVE_RES_INIT
+ /*
+ * STR #2920: Initialize resolver after failure in cups-polld
+ *
+ * If the previous lookup failed, re-initialize the resolver to prevent
+ * temporary network errors from persisting. This *should* be handled by
+ * the resolver libraries, but apparently the glibc folks do not agree.
+ *
+ * We set a flag at the end of this function if we encounter an error that
+ * requires reinitialization of the resolver functions. We then call
+ * res_init() if the flag is set on the next call here or in httpAddrLookup().
+ */
+
+ if (cg->need_res_init)
+ {
+ res_init();
+
+ cg->need_res_init = 0;
+ }
+#endif /* HAVE_RES_INIT */
+
+ /*
+ * Lookup the address the best way we can...
+ */
+
+ first = addr = NULL;
+
+#ifdef AF_LOCAL
+ if (hostname && hostname[0] == '/')
+ {
+ /*
+ * Domain socket address...
+ */
+
+ if ((first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t))) != NULL)
+ {
+ addr = first;
+ first->addr.un.sun_family = AF_LOCAL;
+ strlcpy(first->addr.un.sun_path, hostname, sizeof(first->addr.un.sun_path));
+ }
+ }
+ else
+#endif /* AF_LOCAL */
+ if (!hostname || _cups_strcasecmp(hostname, "localhost"))
+ {
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, /* Address lookup hints */
+ *results, /* Address lookup results */
+ *current; /* Current result */
+ char ipv6[64], /* IPv6 address */
+ *ipv6zone; /* Pointer to zone separator */
+ int ipv6len; /* Length of IPv6 address */
+ int error; /* getaddrinfo() error */
+
+
+ /*
+ * Lookup the address as needed...
+ */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_flags = hostname ? 0 : AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if (hostname && *hostname == '[')
+ {
+ /*
+ * Remove brackets from numeric IPv6 address...
+ */
+
+ if (!strncmp(hostname, "[v1.", 4))
+ {
+ /*
+ * Copy the newer address format which supports link-local addresses...
+ */
+
+ strlcpy(ipv6, hostname + 4, sizeof(ipv6));
+ if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
+ {
+ ipv6[ipv6len] = '\0';
+ hostname = ipv6;
+
+ /*
+ * Convert "+zone" in address to "%zone"...
+ */
+
+ if ((ipv6zone = strrchr(ipv6, '+')) != NULL)
+ *ipv6zone = '%';
+ }
+ }
+ else
+ {
+ /*
+ * Copy the regular non-link-local IPv6 address...
+ */
+
+ strlcpy(ipv6, hostname + 1, sizeof(ipv6));
+ if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
+ {
+ ipv6[ipv6len] = '\0';
+ hostname = ipv6;
+ }
+ }
+ }
+
+ if ((error = getaddrinfo(hostname, service, &hints, &results)) == 0)
+ {
+ /*
+ * Copy the results to our own address list structure...
+ */
+
+ for (current = results; current; current = current->ai_next)
+ if (current->ai_family == AF_INET || current->ai_family == AF_INET6)
+ {
+ /*
+ * Copy the address over...
+ */
+
+ temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!temp)
+ {
+ httpAddrFreeList(first);
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ return (NULL);
+ }
+
+ if (current->ai_family == AF_INET6)
+ memcpy(&(temp->addr.ipv6), current->ai_addr,
+ sizeof(temp->addr.ipv6));
+ else
+ memcpy(&(temp->addr.ipv4), current->ai_addr,
+ sizeof(temp->addr.ipv4));
+
+ /*
+ * Append the address to the list...
+ */
+
+ if (!first)
+ first = temp;
+
+ if (addr)
+ addr->next = temp;
+
+ addr = temp;
+ }
+
+ /*
+ * Free the results from getaddrinfo()...
+ */
+
+ freeaddrinfo(results);
+ }
+ else
+ {
+ if (error == EAI_FAIL)
+ cg->need_res_init = 1;
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gai_strerror(error), 0);
+ }
+
+#else
+ if (hostname)
+ {
+ int i; /* Looping vars */
+ unsigned ip[4]; /* IPv4 address components */
+ const char *ptr; /* Pointer into hostname */
+ struct hostent *host; /* Result of lookup */
+ struct servent *port; /* Port number for service */
+ int portnum; /* Port number */
+
+
+ /*
+ * Lookup the service...
+ */
+
+ if (!service)
+ portnum = 0;
+ else if (isdigit(*service & 255))
+ portnum = atoi(service);
+ else if ((port = getservbyname(service, NULL)) != NULL)
+ portnum = ntohs(port->s_port);
+ else if (!strcmp(service, "http"))
+ portnum = 80;
+ else if (!strcmp(service, "https"))
+ portnum = 443;
+ else if (!strcmp(service, "ipp") || !strcmp(service, "ipps"))
+ portnum = 631;
+ else if (!strcmp(service, "lpd"))
+ portnum = 515;
+ else if (!strcmp(service, "socket"))
+ portnum = 9100;
+ else
+ return (NULL);
+
+ /*
+ * This code is needed because some operating systems have a
+ * buggy implementation of gethostbyname() that does not support
+ * IPv4 addresses. If the hostname string is an IPv4 address, then
+ * sscanf() is used to extract the IPv4 components. We then pack
+ * the components into an IPv4 address manually, since the
+ * inet_aton() function is deprecated. We use the htonl() macro
+ * to get the right byte order for the address.
+ */
+
+ for (ptr = hostname; isdigit(*ptr & 255) || *ptr == '.'; ptr ++);
+
+ if (!*ptr)
+ {
+ /*
+ * We have an IPv4 address; break it up and create an IPv4 address...
+ */
+
+ if (sscanf(hostname, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) == 4 &&
+ ip[0] <= 255 && ip[1] <= 255 && ip[2] <= 255 && ip[3] <= 255)
+ {
+ first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!first)
+ return (NULL);
+
+ first->addr.ipv4.sin_family = AF_INET;
+ first->addr.ipv4.sin_addr.s_addr = htonl((((((((unsigned)ip[0] << 8) |
+ (unsigned)ip[1]) << 8) |
+ (unsigned)ip[2]) << 8) |
+ (unsigned)ip[3]));
+ first->addr.ipv4.sin_port = htons(portnum);
+ }
+ }
+ else if ((host = gethostbyname(hostname)) != NULL &&
+# ifdef AF_INET6
+ (host->h_addrtype == AF_INET || host->h_addrtype == AF_INET6))
+# else
+ host->h_addrtype == AF_INET)
+# endif /* AF_INET6 */
+ {
+ for (i = 0; host->h_addr_list[i]; i ++)
+ {
+ /*
+ * Copy the address over...
+ */
+
+ temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!temp)
+ {
+ httpAddrFreeList(first);
+ return (NULL);
+ }
+
+# ifdef AF_INET6
+ if (host->h_addrtype == AF_INET6)
+ {
+ temp->addr.ipv6.sin6_family = AF_INET6;
+ memcpy(&(temp->addr.ipv6.sin6_addr), host->h_addr_list[i],
+ sizeof(temp->addr.ipv6));
+ temp->addr.ipv6.sin6_port = htons(portnum);
+ }
+ else
+# endif /* AF_INET6 */
+ {
+ temp->addr.ipv4.sin_family = AF_INET;
+ memcpy(&(temp->addr.ipv4.sin_addr), host->h_addr_list[i],
+ sizeof(temp->addr.ipv4));
+ temp->addr.ipv4.sin_port = htons(portnum);
+ }
+
+ /*
+ * Append the address to the list...
+ */
+
+ if (!first)
+ first = temp;
+
+ if (addr)
+ addr->next = temp;
+
+ addr = temp;
+ }
+ }
+ else
+ {
+ if (h_errno == NO_RECOVERY)
+ cg->need_res_init = 1;
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, hstrerror(h_errno), 0);
+ }
+ }
+#endif /* HAVE_GETADDRINFO */
+ }
+
+ /*
+ * Detect some common errors and handle them sanely...
+ */
+
+ if (!addr && (!hostname || !_cups_strcasecmp(hostname, "localhost")))
+ {
+ struct servent *port; /* Port number for service */
+ int portnum; /* Port number */
+
+
+ /*
+ * Lookup the service...
+ */
+
+ if (!service)
+ portnum = 0;
+ else if (isdigit(*service & 255))
+ portnum = atoi(service);
+ else if ((port = getservbyname(service, NULL)) != NULL)
+ portnum = ntohs(port->s_port);
+ else if (!strcmp(service, "http"))
+ portnum = 80;
+ else if (!strcmp(service, "https"))
+ portnum = 443;
+ else if (!strcmp(service, "ipp") || !strcmp(service, "ipps"))
+ portnum = 631;
+ else if (!strcmp(service, "lpd"))
+ portnum = 515;
+ else if (!strcmp(service, "socket"))
+ portnum = 9100;
+ else
+ {
+ httpAddrFreeList(first);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown service name."), 1);
+ return (NULL);
+ }
+
+ if (hostname && !_cups_strcasecmp(hostname, "localhost"))
+ {
+ /*
+ * Unfortunately, some users ignore all of the warnings in the
+ * /etc/hosts file and delete "localhost" from it. If we get here
+ * then we were unable to resolve the name, so use the IPv6 and/or
+ * IPv4 loopback interface addresses...
+ */
+
+#ifdef AF_INET6
+ if (family != AF_INET)
+ {
+ /*
+ * Add [::1] to the address list...
+ */
+
+ temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!temp)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ httpAddrFreeList(first);
+ return (NULL);
+ }
+
+ temp->addr.ipv6.sin6_family = AF_INET6;
+ temp->addr.ipv6.sin6_port = htons(portnum);
+# ifdef WIN32
+ temp->addr.ipv6.sin6_addr.u.Byte[15] = 1;
+# else
+ temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1);
+# endif /* WIN32 */
+
+ if (!first)
+ first = temp;
+
+ addr = temp;
+ }
+
+ if (family != AF_INET6)
+#endif /* AF_INET6 */
+ {
+ /*
+ * Add 127.0.0.1 to the address list...
+ */
+
+ temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!temp)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ httpAddrFreeList(first);
+ return (NULL);
+ }
+
+ temp->addr.ipv4.sin_family = AF_INET;
+ temp->addr.ipv4.sin_port = htons(portnum);
+ temp->addr.ipv4.sin_addr.s_addr = htonl(0x7f000001);
+
+ if (!first)
+ first = temp;
+
+ if (addr)
+ addr->next = temp;
+ }
+ }
+ else if (!hostname)
+ {
+ /*
+ * Provide one or more passive listening addresses...
+ */
+
+#ifdef AF_INET6
+ if (family != AF_INET)
+ {
+ /*
+ * Add [::] to the address list...
+ */
+
+ temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!temp)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ httpAddrFreeList(first);
+ return (NULL);
+ }
+
+ temp->addr.ipv6.sin6_family = AF_INET6;
+ temp->addr.ipv6.sin6_port = htons(portnum);
+
+ if (!first)
+ first = temp;
+
+ addr = temp;
+ }
+
+ if (family != AF_INET6)
+#endif /* AF_INET6 */
+ {
+ /*
+ * Add 0.0.0.0 to the address list...
+ */
+
+ temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
+ if (!temp)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ httpAddrFreeList(first);
+ return (NULL);
+ }
+
+ temp->addr.ipv4.sin_family = AF_INET;
+ temp->addr.ipv4.sin_port = htons(portnum);
+
+ if (!first)
+ first = temp;
+
+ if (addr)
+ addr->next = temp;
+ }
+ }
+ }
+
+ /*
+ * Return the address list...
+ */
+
+ return (first);
+}
+
+
+/*
+ * End of "$Id: http-addrlist.c 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/http-private.h b/cups/libs/cups/http-private.h
new file mode 100644
index 000000000..99a85c39e
--- /dev/null
+++ b/cups/libs/cups/http-private.h
@@ -0,0 +1,441 @@
+/*
+ * "$Id: http-private.h 11392 2013-11-06 01:29:56Z msweet $"
+ *
+ * Private HTTP definitions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_HTTP_PRIVATE_H_
+# define _CUPS_HTTP_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "config.h"
+# include <stddef.h>
+# include <stdlib.h>
+
+# ifdef __sun
+# include <sys/select.h>
+# endif /* __sun */
+
+# include <limits.h>
+# ifdef WIN32
+# include <io.h>
+# include <winsock2.h>
+# define CUPS_SOCAST (const char *)
+# else
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/socket.h>
+# define closesocket(f) close(f)
+# define CUPS_SOCAST
+# endif /* WIN32 */
+
+# ifdef HAVE_GSSAPI
+# ifdef HAVE_GSS_GSSAPI_H
+# include <GSS/gssapi.h>
+# elif defined(HAVE_GSSAPI_GSSAPI_H)
+# include <gssapi/gssapi.h>
+# elif defined(HAVE_GSSAPI_H)
+# include <gssapi.h>
+# endif /* HAVE_GSS_GSSAPI_H */
+# ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
+# define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+# endif /* !HAVE_GSS_C_NT_HOSTBASED_SERVICE */
+# endif /* HAVE_GSSAPI */
+
+# ifdef HAVE_AUTHORIZATION_H
+# include <Security/Authorization.h>
+# endif /* HAVE_AUTHORIZATION_H */
+
+# if defined(__APPLE__) && !defined(_SOCKLEN_T)
+/*
+ * MacOS X 10.2.x does not define socklen_t, and in fact uses an int instead of
+ * unsigned type for length values...
+ */
+
+typedef int socklen_t;
+# endif /* __APPLE__ && !_SOCKLEN_T */
+
+# include <cups/http.h>
+# include "md5-private.h"
+# include "ipp-private.h"
+
+# if defined HAVE_LIBSSL
+# include <openssl/err.h>
+# include <openssl/rand.h>
+# include <openssl/ssl.h>
+# elif defined HAVE_GNUTLS
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+# include <gcrypt.h>
+# elif defined(HAVE_CDSASSL)
+# include <CoreFoundation/CoreFoundation.h>
+# include <Security/Security.h>
+# include <Security/SecureTransport.h>
+# ifdef HAVE_SECURETRANSPORTPRIV_H
+# include <Security/SecureTransportPriv.h>
+# endif /* HAVE_SECURETRANSPORTPRIV_H */
+# ifdef HAVE_SECITEM_H
+# include <Security/SecItem.h>
+# endif /* HAVE_SECITEM_H */
+# ifdef HAVE_SECBASEPRIV_H
+# include <Security/SecBasePriv.h>
+# endif /* HAVE_SECBASEPRIV_H */
+# ifdef HAVE_SECCERTIFICATE_H
+# include <Security/SecCertificate.h>
+# include <Security/SecIdentity.h>
+# endif /* HAVE_SECCERTIFICATE_H */
+# ifdef HAVE_SECITEMPRIV_H
+# include <Security/SecItemPriv.h>
+# endif /* HAVE_SECITEMPRIV_H */
+# ifdef HAVE_SECIDENTITYSEARCHPRIV_H
+# include <Security/SecIdentitySearchPriv.h>
+# endif /* HAVE_SECIDENTITYSEARCHPRIV_H */
+# ifdef HAVE_SECPOLICYPRIV_H
+# include <Security/SecPolicyPriv.h>
+# endif /* HAVE_SECPOLICYPRIV_H */
+# elif defined(HAVE_SSPISSL)
+# include "sspi-private.h"
+# endif /* HAVE_LIBSSL */
+
+# ifndef WIN32
+# include <net/if.h>
+# ifdef HAVE_GETIFADDRS
+# include <ifaddrs.h>
+# else
+# include <sys/ioctl.h>
+# ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+# endif /* HAVE_SYS_SOCKIO_H */
+# endif /* HAVE_GETIFADDRS */
+# endif /* !WIN32 */
+
+# ifdef HAVE_LIBZ
+# include <zlib.h>
+# endif /* HAVE_LIBZ */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+
+#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? */
+
+
+/*
+ * Types and functions for SSL support...
+ */
+
+# if defined HAVE_LIBSSL
+/*
+ * The OpenSSL library provides its own SSL/TLS context structure for its
+ * IO and protocol management. However, we need to provide our own BIO
+ * (basic IO) implementation to do timeouts...
+ */
+
+typedef SSL *http_tls_t;
+typedef void *http_tls_credentials_t;
+
+extern BIO_METHOD *_httpBIOMethods(void);
+
+# elif defined HAVE_GNUTLS
+/*
+ * The GNU TLS library is more of a "bare metal" SSL/TLS library...
+ */
+
+typedef gnutls_session_t http_tls_t;
+typedef void *http_tls_credentials_t;
+
+extern ssize_t _httpReadGNUTLS(gnutls_transport_ptr_t ptr, void *data,
+ size_t length);
+extern ssize_t _httpWriteGNUTLS(gnutls_transport_ptr_t ptr, const void *data,
+ size_t length);
+
+# elif defined(HAVE_CDSASSL)
+/*
+ * Darwin's Security framework provides its own SSL/TLS context structure
+ * for its IO and protocol management...
+ */
+
+# if !defined(HAVE_SECBASEPRIV_H) && defined(HAVE_CSSMERRORSTRING) /* Declare prototype for function in that header... */
+extern const char *cssmErrorString(int error);
+# endif /* !HAVE_SECBASEPRIV_H && HAVE_CSSMERRORSTRING */
+# ifndef HAVE_SECITEMPRIV_H /* Declare constants from that header... */
+extern const CFTypeRef kSecClassCertificate;
+extern const CFTypeRef kSecClassIdentity;
+# endif /* !HAVE_SECITEMPRIV_H */
+# if !defined(HAVE_SECIDENTITYSEARCHPRIV_H) && defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY) /* Declare prototype for function in that header... */
+extern OSStatus SecIdentitySearchCreateWithPolicy(SecPolicyRef policy,
+ CFStringRef idString, CSSM_KEYUSE keyUsage,
+ CFTypeRef keychainOrArray,
+ Boolean returnOnlyValidIdentities,
+ SecIdentitySearchRef* searchRef);
+# endif /* !HAVE_SECIDENTITYSEARCHPRIV_H && HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+# if !defined(HAVE_SECPOLICYPRIV_H) && defined(HAVE_SECPOLICYSETVALUE) /* Declare prototype for function in that header... */
+extern OSStatus SecPolicySetValue(SecPolicyRef policyRef,
+ const CSSM_DATA *value);
+# endif /* !HAVE_SECPOLICYPRIV_H && HAVE_SECPOLICYSETVALUE */
+
+typedef SSLContextRef http_tls_t;
+typedef CFArrayRef http_tls_credentials_t;
+
+extern OSStatus _httpReadCDSA(SSLConnectionRef connection, void *data,
+ size_t *dataLength);
+extern OSStatus _httpWriteCDSA(SSLConnectionRef connection, const void *data,
+ size_t *dataLength);
+
+# elif defined(HAVE_SSPISSL)
+/*
+ * Windows' SSPI library gets a CUPS wrapper...
+ */
+
+typedef _sspi_struct_t * http_tls_t;
+typedef void *http_tls_credentials_t;
+
+# else
+/*
+ * Otherwise define stub types since we have no SSL support...
+ */
+
+typedef void *http_tls_t;
+typedef void *http_tls_credentials_t;
+# endif /* HAVE_LIBSSL */
+
+typedef enum _http_coding_e /**** HTTP content coding enumeration ****/
+{
+ _HTTP_CODING_IDENTITY, /* No content coding */
+ _HTTP_CODING_GZIP, /* LZ77+gzip decompression */
+ _HTTP_CODING_DEFLATE, /* LZ77+zlib compression */
+ _HTTP_CODING_GUNZIP, /* LZ77+gzip decompression */
+ _HTTP_CODING_INFLATE /* LZ77+zlib decompression */
+} _http_coding_t;
+
+typedef enum _http_mode_e /**** HTTP mode enumeration ****/
+{
+ _HTTP_MODE_CLIENT, /* Client connected to server */
+ _HTTP_MODE_SERVER /* Server connected (accepted) from client */
+} _http_mode_t;
+
+struct _http_s /**** HTTP connection structure ****/
+{
+ int fd; /* File descriptor for this socket */
+ int blocking; /* To block or not to block */
+ int error; /* Last error on read */
+ time_t activity; /* Time since last read/write */
+ http_state_t state; /* State of client */
+ http_status_t status; /* Status of last request */
+ http_version_t version; /* Protocol version */
+ http_keepalive_t keep_alive; /* Keep-alive supported? */
+ struct sockaddr_in _hostaddr; /* Address of connected host (deprecated) */
+ char hostname[HTTP_MAX_HOST],
+ /* Name of connected host */
+ fields[HTTP_FIELD_ACCEPT_ENCODING][HTTP_MAX_VALUE];
+ /* Field values up to Accept-Encoding */
+ char *data; /* Pointer to data buffer */
+ http_encoding_t data_encoding; /* Chunked or not */
+ int _data_remaining;/* Number of bytes left (deprecated) */
+ int used; /* Number of bytes used in buffer */
+ char buffer[HTTP_MAX_BUFFER];
+ /* Buffer for incoming data */
+ int auth_type; /* Authentication in use */
+ _cups_md5_state_t md5_state; /* MD5 state */
+ char nonce[HTTP_MAX_VALUE];
+ /* Nonce value */
+ int nonce_count; /* Nonce count */
+ http_tls_t tls; /* TLS state information */
+ http_encryption_t encryption; /* Encryption requirements */
+
+ /**** New in CUPS 1.1.19 ****/
+ fd_set *input_set; /* select() set for httpWait() (deprecated) */
+ http_status_t expect; /* Expect: header */
+ char *cookie; /* Cookie value(s) */
+
+ /**** New in CUPS 1.1.20 ****/
+ char _authstring[HTTP_MAX_VALUE],
+ /* Current Authorization value (deprecated) */
+ userpass[HTTP_MAX_VALUE];
+ /* Username:password string */
+ int digest_tries; /* Number of tries for digest auth */
+
+ /**** New in CUPS 1.2 ****/
+ off_t data_remaining; /* Number of bytes left */
+ http_addr_t *hostaddr; /* Current host address and port */
+ http_addrlist_t *addrlist; /* List of valid addresses */
+ char wbuffer[HTTP_MAX_BUFFER];
+ /* Buffer for outgoing data */
+ int wused; /* Write buffer bytes used */
+
+ /**** New in CUPS 1.3 ****/
+ char *field_authorization;
+ /* Authorization field */
+ char *authstring; /* Current Authorization field */
+# ifdef HAVE_GSSAPI
+ gss_OID gssmech; /* Authentication mechanism */
+ gss_ctx_id_t gssctx; /* Authentication context */
+ gss_name_t gssname; /* Authentication server name */
+# endif /* HAVE_GSSAPI */
+# ifdef HAVE_AUTHORIZATION_H
+ AuthorizationRef auth_ref; /* Authorization ref */
+# endif /* HAVE_AUTHORIZATION_H */
+
+ /**** New in CUPS 1.5 ****/
+ http_tls_credentials_t tls_credentials;
+ /* TLS credentials */
+ http_timeout_cb_t timeout_cb; /* Timeout callback */
+ void *timeout_data; /* User data pointer */
+ double timeout_value; /* Timeout in seconds */
+ int wait_value; /* httpWait value for timeout */
+# ifdef HAVE_GSSAPI
+ char gsshost[256]; /* Hostname for Kerberos */
+# endif /* HAVE_GSSAPI */
+
+ /**** New in CUPS 1.7 ****/
+ int tls_upgrade; /* Non-zero if we are doing an upgrade */
+ _http_mode_t mode; /* _HTTP_MODE_CLIENT or _HTTP_MODE_SERVER */
+ char *accept_encoding,
+ /* Accept-Encoding field */
+ *allow, /* Allow field */
+ *server, /* Server field */
+ *default_accept_encoding,
+ *default_server,
+ *default_user_agent;
+ /* Default field values */
+# ifdef HAVE_LIBZ
+ _http_coding_t coding; /* _HTTP_CODING_xxx */
+ z_stream stream; /* (De)compression stream */
+ Bytef *dbuffer; /* Decompression buffer */
+# endif /* HAVE_LIBZ */
+};
+
+
+/*
+ * Some OS's don't have hstrerror(), most notably Solaris...
+ */
+
+# ifndef HAVE_HSTRERROR
+extern const char *_cups_hstrerror(int error);
+# define hstrerror _cups_hstrerror
+# elif defined(_AIX) || defined(__osf__)
+/*
+ * AIX and Tru64 UNIX don't provide a prototype but do provide the function...
+ */
+extern const char *hstrerror(int error);
+# endif /* !HAVE_HSTRERROR */
+
+
+/*
+ * Some OS's don't have getifaddrs() and freeifaddrs()...
+ */
+
+# if !defined(WIN32) && !defined(HAVE_GETIFADDRS)
+# ifdef ifa_dstaddr
+# undef ifa_dstaddr
+# endif /* ifa_dstaddr */
+# ifndef ifr_netmask
+# define ifr_netmask ifr_addr
+# endif /* !ifr_netmask */
+
+struct ifaddrs /**** Interface Structure ****/
+{
+ struct ifaddrs *ifa_next; /* Next interface in list */
+ char *ifa_name; /* Name of interface */
+ unsigned int ifa_flags; /* Flags (up, point-to-point, etc.) */
+ struct sockaddr *ifa_addr, /* Network address */
+ *ifa_netmask; /* Address mask */
+ union
+ {
+ struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */
+ struct sockaddr *ifu_dstaddr; /* Point-to-point destination address. */
+ } ifa_ifu;
+
+ void *ifa_data; /* Interface statistics */
+};
+
+# ifndef ifa_broadaddr
+# define ifa_broadaddr ifa_ifu.ifu_broadaddr
+# endif /* !ifa_broadaddr */
+# ifndef ifa_dstaddr
+# define ifa_dstaddr ifa_ifu.ifu_dstaddr
+# endif /* !ifa_dstaddr */
+
+extern int _cups_getifaddrs(struct ifaddrs **addrs);
+# define getifaddrs _cups_getifaddrs
+extern void _cups_freeifaddrs(struct ifaddrs *addrs);
+# define freeifaddrs _cups_freeifaddrs
+# endif /* !WIN32 && !HAVE_GETIFADDRS */
+
+
+/*
+ * Prototypes...
+ */
+
+#define _httpAddrFamily(addrp) (addrp)->addr.sa_family
+extern int _httpAddrPort(http_addr_t *addr)
+ _CUPS_INTERNAL_MSG("Use httpAddrPort instead.");
+extern void _httpAddrSetPort(http_addr_t *addr, int port);
+extern char *_httpAssembleUUID(const char *server, int port,
+ const char *name, int number,
+ char *buffer, size_t bufsize)
+ _CUPS_INTERNAL_MSG("Use httpAssembleUUID instead.");
+extern http_t *_httpCreate(const char *host, int port,
+ http_addrlist_t *addrlist,
+ http_encryption_t encryption,
+ int family)
+ _CUPS_INTERNAL_MSG("Use httpConnect2 or httpAccept instead.");
+extern http_tls_credentials_t
+ _httpCreateCredentials(cups_array_t *credentials);
+extern char *_httpDecodeURI(char *dst, const char *src,
+ size_t dstsize);
+extern void _httpDisconnect(http_t *http);
+extern char *_httpEncodeURI(char *dst, const char *src,
+ size_t dstsize);
+extern void _httpFreeCredentials(http_tls_credentials_t credentials);
+extern ssize_t _httpPeek(http_t *http, char *buffer, size_t length)
+ _CUPS_INTERNAL_MSG("Use httpPeek instead.");
+extern const char *_httpResolveURI(const char *uri, char *resolved_uri,
+ size_t resolved_size, int options,
+ int (*cb)(void *context),
+ void *context);
+extern int _httpUpdate(http_t *http, http_status_t *status);
+extern int _httpWait(http_t *http, int msec, int usessl);
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_HTTP_PRIVATE_H_ */
+
+/*
+ * End of "$Id: http-private.h 11392 2013-11-06 01:29:56Z msweet $".
+ */
diff --git a/cups/libs/cups/http-support.c b/cups/libs/cups/http-support.c
new file mode 100644
index 000000000..a4250beec
--- /dev/null
+++ b/cups/libs/cups/http-support.c
@@ -0,0 +1,2386 @@
+/*
+ * "$Id: http-support.c 11445 2013-12-05 19:57:43Z msweet $"
+ *
+ * HTTP support routines for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * httpAssembleURI() - Assemble a uniform resource identifier from its
+ * components.
+ * httpAssembleURIf() - Assemble a uniform resource identifier from its
+ * components with a formatted resource.
+ * httpAssembleUUID() - Assemble a name-based UUID URN conforming to RFC
+ * 4122.
+ * httpDecode64() - Base64-decode a string.
+ * httpDecode64_2() - Base64-decode a string.
+ * httpEncode64() - Base64-encode a string.
+ * httpEncode64_2() - Base64-encode a string.
+ * httpGetDateString() - Get a formatted date/time string from a time value.
+ * httpGetDateString2() - Get a formatted date/time string from a time value.
+ * httpGetDateTime() - Get a time value from a formatted date/time string.
+ * httpSeparate() - Separate a Universal Resource Identifier into its
+ * components.
+ * httpSeparate2() - Separate a Universal Resource Identifier into its
+ * components.
+ * httpSeparateURI() - Separate a Universal Resource Identifier into its
+ * components.
+ * httpStatus() - Return a short string describing a HTTP status
+ * code.
+ * _cups_hstrerror() - hstrerror() emulation function for Solaris and
+ * others.
+ * _httpDecodeURI() - Percent-decode a HTTP request URI.
+ * _httpEncodeURI() - Percent-encode a HTTP request URI.
+ * _httpResolveURI() - Resolve a DNS-SD URI.
+ * http_client_cb() - Client callback for resolving URI.
+ * http_copy_decode() - Copy and decode a URI.
+ * http_copy_encode() - Copy and encode a URI.
+ * http_poll_cb() - Wait for input on the specified file descriptors.
+ * http_resolve_cb() - Build a device URI for the given service name.
+ * http_resolve_cb() - Build a device URI for the given service name.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#ifdef HAVE_DNSSD
+# include <dns_sd.h>
+# ifdef WIN32
+# include <io.h>
+# elif defined(HAVE_POLL)
+# include <poll.h>
+# else
+# include <sys/select.h>
+# endif /* WIN32 */
+#elif defined(HAVE_AVAHI)
+# include <avahi-client/client.h>
+# include <avahi-client/lookup.h>
+# include <avahi-common/simple-watch.h>
+#endif /* HAVE_DNSSD */
+
+
+/*
+ * Local types...
+ */
+
+typedef struct _http_uribuf_s /* URI buffer */
+{
+#ifdef HAVE_AVAHI
+ AvahiSimplePoll *poll; /* Poll state */
+#endif /* HAVE_AVAHI */
+ char *buffer; /* Pointer to buffer */
+ size_t bufsize; /* Size of buffer */
+ int options; /* Options passed to _httpResolveURI */
+ const char *resource; /* Resource from URI */
+} _http_uribuf_t;
+
+
+/*
+ * Local globals...
+ */
+
+static const char * const http_days[7] =
+ {
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat"
+ };
+static const char * const http_months[12] =
+ {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+ };
+
+
+/*
+ * Local functions...
+ */
+
+static const char *http_copy_decode(char *dst, const char *src,
+ int dstsize, const char *term,
+ int decode);
+static char *http_copy_encode(char *dst, const char *src,
+ char *dstend, const char *reserved,
+ const char *term, int encode);
+#ifdef HAVE_DNSSD
+static void DNSSD_API http_resolve_cb(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullName,
+ const char *hostTarget,
+ uint16_t port, uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context);
+#endif /* HAVE_DNSSD */
+
+#ifdef HAVE_AVAHI
+static void http_client_cb(AvahiClient *client,
+ AvahiClientState state, void *simple_poll);
+static int http_poll_cb(struct pollfd *pollfds, unsigned int num_pollfds,
+ int timeout, void *context);
+static void http_resolve_cb(AvahiServiceResolver *resolver,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name, const char *type,
+ const char *domain, const char *host_name,
+ const AvahiAddress *address, uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags, void *context);
+#endif /* HAVE_AVAHI */
+
+
+/*
+ * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
+ * components.
+ *
+ * This function escapes reserved characters in the URI depending on the
+ * value of the "encoding" argument. You should use this function in
+ * place of traditional string functions whenever you need to create a
+ * URI string.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+http_uri_status_t /* O - URI status */
+httpAssembleURI(
+ http_uri_coding_t encoding, /* I - Encoding flags */
+ char *uri, /* I - URI buffer */
+ int urilen, /* I - Size of URI buffer */
+ const char *scheme, /* I - Scheme name */
+ const char *username, /* I - Username */
+ const char *host, /* I - Hostname or address */
+ int port, /* I - Port number */
+ const char *resource) /* I - Resource */
+{
+ char *ptr, /* Pointer into URI buffer */
+ *end; /* End of URI buffer */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!uri || urilen < 1 || !scheme || port < 0)
+ {
+ if (uri)
+ *uri = '\0';
+
+ return (HTTP_URI_STATUS_BAD_ARGUMENTS);
+ }
+
+ /*
+ * Assemble the URI starting with the scheme...
+ */
+
+ end = uri + urilen - 1;
+ ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
+
+ if (!ptr)
+ goto assemble_overflow;
+
+ if (!strcmp(scheme, "mailto") || !strcmp(scheme, "tel"))
+ {
+ /*
+ * mailto: and tel: only have :, no //...
+ */
+
+ if (ptr < end)
+ *ptr++ = ':';
+ else
+ goto assemble_overflow;
+ }
+ else
+ {
+ /*
+ * Schemes other than mailto: and tel: all have //...
+ */
+
+ if ((ptr + 2) < end)
+ {
+ *ptr++ = ':';
+ *ptr++ = '/';
+ *ptr++ = '/';
+ }
+ else
+ goto assemble_overflow;
+ }
+
+ /*
+ * Next the username and hostname, if any...
+ */
+
+ if (host)
+ {
+ const char *hostptr; /* Pointer into hostname */
+ int have_ipv6; /* Do we have an IPv6 address? */
+
+ if (username && *username)
+ {
+ /*
+ * Add username@ first...
+ */
+
+ ptr = http_copy_encode(ptr, username, end, "/?#[]@", NULL,
+ encoding & HTTP_URI_CODING_USERNAME);
+
+ if (!ptr)
+ goto assemble_overflow;
+
+ if (ptr < end)
+ *ptr++ = '@';
+ else
+ goto assemble_overflow;
+ }
+
+ /*
+ * Then add the hostname. Since IPv6 is a particular pain to deal
+ * with, we have several special cases to deal with. If we get
+ * an IPv6 address with brackets around it, assume it is already in
+ * URI format. Since DNS-SD service names can sometimes look like
+ * raw IPv6 addresses, we specifically look for "._tcp" in the name,
+ * too...
+ */
+
+ for (hostptr = host,
+ have_ipv6 = strchr(host, ':') && !strstr(host, "._tcp");
+ *hostptr && have_ipv6;
+ hostptr ++)
+ if (*hostptr != ':' && !isxdigit(*hostptr & 255))
+ {
+ have_ipv6 = *hostptr == '%';
+ break;
+ }
+
+ if (have_ipv6)
+ {
+ /*
+ * We have a raw IPv6 address...
+ */
+
+ if (strchr(host, '%') && !(encoding & HTTP_URI_CODING_RFC6874))
+ {
+ /*
+ * We have a link-local address, add "[v1." prefix...
+ */
+
+ if ((ptr + 4) < end)
+ {
+ *ptr++ = '[';
+ *ptr++ = 'v';
+ *ptr++ = '1';
+ *ptr++ = '.';
+ }
+ else
+ goto assemble_overflow;
+ }
+ else
+ {
+ /*
+ * We have a normal (or RFC 6874 link-local) address, add "[" prefix...
+ */
+
+ if (ptr < end)
+ *ptr++ = '[';
+ else
+ goto assemble_overflow;
+ }
+
+ /*
+ * Copy the rest of the IPv6 address, and terminate with "]".
+ */
+
+ while (ptr < end && *host)
+ {
+ if (*host == '%')
+ {
+ /*
+ * Convert/encode zone separator
+ */
+
+ if (encoding & HTTP_URI_CODING_RFC6874)
+ {
+ if (ptr >= (end - 2))
+ goto assemble_overflow;
+
+ *ptr++ = '%';
+ *ptr++ = '2';
+ *ptr++ = '5';
+ }
+ else
+ *ptr++ = '+';
+
+ host ++;
+ }
+ else
+ *ptr++ = *host++;
+ }
+
+ if (*host)
+ goto assemble_overflow;
+
+ if (ptr < end)
+ *ptr++ = ']';
+ else
+ goto assemble_overflow;
+ }
+ else
+ {
+ /*
+ * Otherwise, just copy the host string (the extra chars are not in the
+ * "reg-name" ABNF rule; anything <= SP or >= DEL plus % gets automatically
+ * percent-encoded.
+ */
+
+ ptr = http_copy_encode(ptr, host, end, "\"#/:<>?@[\\]^`{|}", NULL,
+ encoding & HTTP_URI_CODING_HOSTNAME);
+
+ if (!ptr)
+ goto assemble_overflow;
+ }
+
+ /*
+ * Finish things off with the port number...
+ */
+
+ if (port > 0)
+ {
+ snprintf(ptr, end - ptr + 1, ":%d", port);
+ ptr += strlen(ptr);
+
+ if (ptr >= end)
+ goto assemble_overflow;
+ }
+ }
+
+ /*
+ * Last but not least, add the resource string...
+ */
+
+ if (resource)
+ {
+ char *query; /* Pointer to query string */
+
+
+ /*
+ * Copy the resource string up to the query string if present...
+ */
+
+ query = strchr(resource, '?');
+ ptr = http_copy_encode(ptr, resource, end, NULL, "?",
+ encoding & HTTP_URI_CODING_RESOURCE);
+ if (!ptr)
+ goto assemble_overflow;
+
+ if (query)
+ {
+ /*
+ * Copy query string without encoding...
+ */
+
+ ptr = http_copy_encode(ptr, query, end, NULL, NULL,
+ encoding & HTTP_URI_CODING_QUERY);
+ if (!ptr)
+ goto assemble_overflow;
+ }
+ }
+ else if (ptr < end)
+ *ptr++ = '/';
+ else
+ goto assemble_overflow;
+
+ /*
+ * Nul-terminate the URI buffer and return with no errors...
+ */
+
+ *ptr = '\0';
+
+ return (HTTP_URI_STATUS_OK);
+
+ /*
+ * Clear the URI string and return an overflow error; I don't usually
+ * like goto's, but in this case it makes sense...
+ */
+
+ assemble_overflow:
+
+ *uri = '\0';
+ return (HTTP_URI_STATUS_OVERFLOW);
+}
+
+
+/*
+ * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
+ * components with a formatted resource.
+ *
+ * This function creates a formatted version of the resource string
+ * argument "resourcef" and escapes reserved characters in the URI
+ * depending on the value of the "encoding" argument. You should use
+ * this function in place of traditional string functions whenever
+ * you need to create a URI string.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+http_uri_status_t /* O - URI status */
+httpAssembleURIf(
+ http_uri_coding_t encoding, /* I - Encoding flags */
+ char *uri, /* I - URI buffer */
+ int urilen, /* I - Size of URI buffer */
+ const char *scheme, /* I - Scheme name */
+ const char *username, /* I - Username */
+ const char *host, /* I - Hostname or address */
+ int port, /* I - Port number */
+ const char *resourcef, /* I - Printf-style resource */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to additional arguments */
+ char resource[1024]; /* Formatted resource string */
+ int bytes; /* Bytes in formatted string */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
+ {
+ if (uri)
+ *uri = '\0';
+
+ return (HTTP_URI_STATUS_BAD_ARGUMENTS);
+ }
+
+ /*
+ * Format the resource string and assemble the URI...
+ */
+
+ va_start(ap, resourcef);
+ bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
+ va_end(ap);
+
+ if (bytes >= sizeof(resource))
+ {
+ *uri = '\0';
+ return (HTTP_URI_STATUS_OVERFLOW);
+ }
+ else
+ return (httpAssembleURI(encoding, uri, urilen, scheme, username, host,
+ port, resource));
+}
+
+
+/*
+ * 'httpAssembleUUID()' - Assemble a name-based UUID URN conforming to RFC 4122.
+ *
+ * This function creates a unique 128-bit identifying number using the server
+ * name, port number, random data, and optionally an object name and/or object
+ * number. The result is formatted as a UUID URN as defined in RFC 4122.
+ *
+ * The buffer needs to be at least 46 bytes in size.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+char * /* I - UUID string */
+httpAssembleUUID(const char *server, /* I - Server name */
+ int port, /* I - Port number */
+ const char *name, /* I - Object name or NULL */
+ int number, /* I - Object number or 0 */
+ char *buffer, /* I - String buffer */
+ size_t bufsize) /* I - Size of buffer */
+{
+ char data[1024]; /* Source string for MD5 */
+ _cups_md5_state_t md5state; /* MD5 state */
+ unsigned char md5sum[16]; /* MD5 digest/sum */
+
+
+ /*
+ * Build a version 3 UUID conforming to RFC 4122.
+ *
+ * Start with the MD5 sum of the server, port, object name and
+ * number, and some random data on the end.
+ */
+
+ snprintf(data, sizeof(data), "%s:%d:%s:%d:%04x:%04x", server,
+ port, name ? name : server, number,
+ (unsigned)CUPS_RAND() & 0xffff, (unsigned)CUPS_RAND() & 0xffff);
+
+ _cupsMD5Init(&md5state);
+ _cupsMD5Append(&md5state, (unsigned char *)data, strlen(data));
+ _cupsMD5Finish(&md5state, md5sum);
+
+ /*
+ * Generate the UUID from the MD5...
+ */
+
+ snprintf(buffer, bufsize,
+ "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x",
+ md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
+ (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40,
+ md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13],
+ md5sum[14], md5sum[15]);
+
+ return (buffer);
+}
+
+/* For OS X 10.8 and earlier */
+char *_httpAssembleUUID(const char *server, int port, const char *name,
+ int number, char *buffer, size_t bufsize)
+{
+ return (httpAssembleUUID(server, port, name, number, buffer, bufsize));
+}
+
+
+/*
+ * 'httpDecode64()' - Base64-decode a string.
+ *
+ * This function is deprecated. Use the httpDecode64_2() function instead
+ * which provides buffer length arguments.
+ *
+ * @deprecated@
+ */
+
+char * /* O - Decoded string */
+httpDecode64(char *out, /* I - String to write to */
+ const char *in) /* I - String to read from */
+{
+ int outlen; /* Output buffer length */
+
+
+ /*
+ * Use the old maximum buffer size for binary compatibility...
+ */
+
+ outlen = 512;
+
+ return (httpDecode64_2(out, &outlen, in));
+}
+
+
+/*
+ * 'httpDecode64_2()' - Base64-decode a string.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+char * /* O - Decoded string */
+httpDecode64_2(char *out, /* I - String to write to */
+ int *outlen, /* IO - Size of output string */
+ const char *in) /* I - String to read from */
+{
+ int pos, /* Bit position */
+ base64; /* Value of this character */
+ char *outptr, /* Output pointer */
+ *outend; /* End of output buffer */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!out || !outlen || *outlen < 1 || !in)
+ return (NULL);
+
+ if (!*in)
+ {
+ *out = '\0';
+ *outlen = 0;
+
+ return (out);
+ }
+
+ /*
+ * Convert from base-64 to bytes...
+ */
+
+ for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
+ {
+ /*
+ * Decode this character into a number from 0 to 63...
+ */
+
+ if (*in >= 'A' && *in <= 'Z')
+ base64 = *in - 'A';
+ else if (*in >= 'a' && *in <= 'z')
+ base64 = *in - 'a' + 26;
+ else if (*in >= '0' && *in <= '9')
+ base64 = *in - '0' + 52;
+ else if (*in == '+')
+ base64 = 62;
+ else if (*in == '/')
+ base64 = 63;
+ else if (*in == '=')
+ break;
+ else
+ continue;
+
+ /*
+ * Store the result in the appropriate chars...
+ */
+
+ switch (pos)
+ {
+ case 0 :
+ if (outptr < outend)
+ *outptr = base64 << 2;
+ pos ++;
+ break;
+ case 1 :
+ if (outptr < outend)
+ *outptr++ |= (base64 >> 4) & 3;
+ if (outptr < outend)
+ *outptr = (base64 << 4) & 255;
+ pos ++;
+ break;
+ case 2 :
+ if (outptr < outend)
+ *outptr++ |= (base64 >> 2) & 15;
+ if (outptr < outend)
+ *outptr = (base64 << 6) & 255;
+ pos ++;
+ break;
+ case 3 :
+ if (outptr < outend)
+ *outptr++ |= base64;
+ pos = 0;
+ break;
+ }
+ }
+
+ *outptr = '\0';
+
+ /*
+ * Return the decoded string and size...
+ */
+
+ *outlen = (int)(outptr - out);
+
+ return (out);
+}
+
+
+/*
+ * 'httpEncode64()' - Base64-encode a string.
+ *
+ * This function is deprecated. Use the httpEncode64_2() function instead
+ * which provides buffer length arguments.
+ *
+ * @deprecated@
+ */
+
+char * /* O - Encoded string */
+httpEncode64(char *out, /* I - String to write to */
+ const char *in) /* I - String to read from */
+{
+ return (httpEncode64_2(out, 512, in, (int)strlen(in)));
+}
+
+
+/*
+ * 'httpEncode64_2()' - Base64-encode a string.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+char * /* O - Encoded string */
+httpEncode64_2(char *out, /* I - String to write to */
+ int outlen, /* I - Size of output string */
+ const char *in, /* I - String to read from */
+ int inlen) /* I - Size of input string */
+{
+ char *outptr, /* Output pointer */
+ *outend; /* End of output buffer */
+ static const char base64[] = /* Base64 characters... */
+ {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "+/"
+ };
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!out || outlen < 1 || !in)
+ return (NULL);
+
+ /*
+ * Convert bytes to base-64...
+ */
+
+ for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
+ {
+ /*
+ * Encode the up to 3 characters as 4 Base64 numbers...
+ */
+
+ if (outptr < outend)
+ *outptr ++ = base64[(in[0] & 255) >> 2];
+
+ if (outptr < outend)
+ {
+ if (inlen > 1)
+ *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
+ else
+ *outptr ++ = base64[((in[0] & 255) << 4) & 63];
+ }
+
+ in ++;
+ inlen --;
+ if (inlen <= 0)
+ {
+ if (outptr < outend)
+ *outptr ++ = '=';
+ if (outptr < outend)
+ *outptr ++ = '=';
+ break;
+ }
+
+ if (outptr < outend)
+ {
+ if (inlen > 1)
+ *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
+ else
+ *outptr ++ = base64[((in[0] & 255) << 2) & 63];
+ }
+
+ in ++;
+ inlen --;
+ if (inlen <= 0)
+ {
+ if (outptr < outend)
+ *outptr ++ = '=';
+ break;
+ }
+
+ if (outptr < outend)
+ *outptr ++ = base64[in[0] & 63];
+ }
+
+ *outptr = '\0';
+
+ /*
+ * Return the encoded string...
+ */
+
+ return (out);
+}
+
+
+/*
+ * 'httpGetDateString()' - Get a formatted date/time string from a time value.
+ *
+ * @deprecated@
+ */
+
+const char * /* O - Date/time string */
+httpGetDateString(time_t t) /* I - UNIX time */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
+}
+
+
+/*
+ * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+const char * /* O - Date/time string */
+httpGetDateString2(time_t t, /* I - UNIX time */
+ char *s, /* I - String buffer */
+ int slen) /* I - Size of string buffer */
+{
+ struct tm *tdate; /* UNIX date/time data */
+
+
+ tdate = gmtime(&t);
+ if (tdate)
+ snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
+ http_days[tdate->tm_wday], tdate->tm_mday,
+ http_months[tdate->tm_mon], tdate->tm_year + 1900,
+ tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
+ else
+ s[0] = '\0';
+
+ return (s);
+}
+
+
+/*
+ * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
+ */
+
+time_t /* O - UNIX time */
+httpGetDateTime(const char *s) /* I - Date/time string */
+{
+ int i; /* Looping var */
+ char mon[16]; /* Abbreviated month name */
+ int day, year; /* Day of month and year */
+ int hour, min, sec; /* Time */
+ int days; /* Number of days since 1970 */
+ static const int normal_days[] = /* Days to a month, normal years */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
+ static const int leap_days[] = /* Days to a month, leap years */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
+
+
+ DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
+
+ /*
+ * Extract the date and time from the formatted string...
+ */
+
+ if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
+ return (0);
+
+ DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
+ "min=%d, sec=%d", day, mon, year, hour, min, sec));
+
+ /*
+ * Convert the month name to a number from 0 to 11.
+ */
+
+ for (i = 0; i < 12; i ++)
+ if (!_cups_strcasecmp(mon, http_months[i]))
+ break;
+
+ if (i >= 12)
+ return (0);
+
+ DEBUG_printf(("4httpGetDateTime: i=%d", i));
+
+ /*
+ * Now convert the date and time to a UNIX time value in seconds since
+ * 1970. We can't use mktime() since the timezone may not be UTC but
+ * the date/time string *is* UTC.
+ */
+
+ if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
+ days = leap_days[i] + day - 1;
+ else
+ days = normal_days[i] + day - 1;
+
+ DEBUG_printf(("4httpGetDateTime: days=%d", days));
+
+ days += (year - 1970) * 365 + /* 365 days per year (normally) */
+ ((year - 1) / 4 - 492) - /* + leap days */
+ ((year - 1) / 100 - 19) + /* - 100 year days */
+ ((year - 1) / 400 - 4); /* + 400 year days */
+
+ DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
+
+ return (days * 86400 + hour * 3600 + min * 60 + sec);
+}
+
+
+/*
+ * 'httpSeparate()' - Separate a Universal Resource Identifier into its
+ * components.
+ *
+ * This function is deprecated; use the httpSeparateURI() function instead.
+ *
+ * @deprecated@
+ */
+
+void
+httpSeparate(const char *uri, /* I - Universal Resource Identifier */
+ char *scheme, /* O - Scheme [32] (http, https, etc.) */
+ char *username, /* O - Username [1024] */
+ char *host, /* O - Hostname [1024] */
+ int *port, /* O - Port number to use */
+ char *resource) /* O - Resource/filename [1024] */
+{
+ httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
+ HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
+ HTTP_MAX_URI);
+}
+
+
+/*
+ * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
+ * components.
+ *
+ * This function is deprecated; use the httpSeparateURI() function instead.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ * @deprecated@
+ */
+
+void
+httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
+ char *scheme, /* O - Scheme (http, https, etc.) */
+ int schemelen, /* I - Size of scheme buffer */
+ char *username, /* O - Username */
+ int usernamelen, /* I - Size of username buffer */
+ char *host, /* O - Hostname */
+ int hostlen, /* I - Size of hostname buffer */
+ int *port, /* O - Port number to use */
+ char *resource, /* O - Resource/filename */
+ int resourcelen) /* I - Size of resource buffer */
+{
+ httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
+ usernamelen, host, hostlen, port, resource, resourcelen);
+}
+
+
+/*
+ * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
+ * components.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+http_uri_status_t /* O - Result of separation */
+httpSeparateURI(
+ http_uri_coding_t decoding, /* I - Decoding flags */
+ const char *uri, /* I - Universal Resource Identifier */
+ char *scheme, /* O - Scheme (http, https, etc.) */
+ int schemelen, /* I - Size of scheme buffer */
+ char *username, /* O - Username */
+ int usernamelen, /* I - Size of username buffer */
+ char *host, /* O - Hostname */
+ int hostlen, /* I - Size of hostname buffer */
+ int *port, /* O - Port number to use */
+ char *resource, /* O - Resource/filename */
+ int resourcelen) /* I - Size of resource buffer */
+{
+ char *ptr, /* Pointer into string... */
+ *end; /* End of string */
+ const char *sep; /* Separator character */
+ http_uri_status_t status; /* Result of separation */
+
+
+ /*
+ * Initialize everything to blank...
+ */
+
+ if (scheme && schemelen > 0)
+ *scheme = '\0';
+
+ if (username && usernamelen > 0)
+ *username = '\0';
+
+ if (host && hostlen > 0)
+ *host = '\0';
+
+ if (port)
+ *port = 0;
+
+ if (resource && resourcelen > 0)
+ *resource = '\0';
+
+ /*
+ * Range check input...
+ */
+
+ if (!uri || !port || !scheme || schemelen <= 0 || !username ||
+ usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
+ resourcelen <= 0)
+ return (HTTP_URI_STATUS_BAD_ARGUMENTS);
+
+ if (!*uri)
+ return (HTTP_URI_STATUS_BAD_URI);
+
+ /*
+ * Grab the scheme portion of the URI...
+ */
+
+ status = HTTP_URI_STATUS_OK;
+
+ if (!strncmp(uri, "//", 2))
+ {
+ /*
+ * Workaround for HP IPP client bug...
+ */
+
+ strlcpy(scheme, "ipp", schemelen);
+ status = HTTP_URI_STATUS_MISSING_SCHEME;
+ }
+ else if (*uri == '/')
+ {
+ /*
+ * Filename...
+ */
+
+ strlcpy(scheme, "file", schemelen);
+ status = HTTP_URI_STATUS_MISSING_SCHEME;
+ }
+ else
+ {
+ /*
+ * Standard URI with scheme...
+ */
+
+ for (ptr = scheme, end = scheme + schemelen - 1;
+ *uri && *uri != ':' && ptr < end;)
+ if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-+.", *uri) != NULL)
+ *ptr++ = *uri++;
+ else
+ break;
+
+ *ptr = '\0';
+
+ if (*uri != ':')
+ {
+ *scheme = '\0';
+ return (HTTP_URI_STATUS_BAD_SCHEME);
+ }
+
+ uri ++;
+ }
+
+ /*
+ * Set the default port number...
+ */
+
+ if (!strcmp(scheme, "http"))
+ *port = 80;
+ else if (!strcmp(scheme, "https"))
+ *port = 443;
+ else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps"))
+ *port = 631;
+ else if (!_cups_strcasecmp(scheme, "lpd"))
+ *port = 515;
+ else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
+ *port = 9100;
+ else if (strcmp(scheme, "file") && strcmp(scheme, "mailto") && strcmp(scheme, "tel"))
+ status = HTTP_URI_STATUS_UNKNOWN_SCHEME;
+
+ /*
+ * Now see if we have a hostname...
+ */
+
+ if (!strncmp(uri, "//", 2))
+ {
+ /*
+ * Yes, extract it...
+ */
+
+ uri += 2;
+
+ /*
+ * Grab the username, if any...
+ */
+
+ if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
+ {
+ /*
+ * Get a username:password combo...
+ */
+
+ uri = http_copy_decode(username, uri, usernamelen, "@",
+ decoding & HTTP_URI_CODING_USERNAME);
+
+ if (!uri)
+ {
+ *username = '\0';
+ return (HTTP_URI_STATUS_BAD_USERNAME);
+ }
+
+ uri ++;
+ }
+
+ /*
+ * Then the hostname/IP address...
+ */
+
+ if (*uri == '[')
+ {
+ /*
+ * Grab IPv6 address...
+ */
+
+ uri ++;
+ if (*uri == 'v')
+ {
+ /*
+ * Skip IPvFuture ("vXXXX.") prefix...
+ */
+
+ uri ++;
+
+ while (isxdigit(*uri & 255))
+ uri ++;
+
+ if (*uri != '.')
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+
+ uri ++;
+ }
+
+ uri = http_copy_decode(host, uri, hostlen, "]",
+ decoding & HTTP_URI_CODING_HOSTNAME);
+
+ if (!uri)
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+
+ /*
+ * Validate value...
+ */
+
+ if (*uri != ']')
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+
+ uri ++;
+
+ for (ptr = host; *ptr; ptr ++)
+ if (*ptr == '+')
+ {
+ /*
+ * Convert zone separator to % and stop here...
+ */
+
+ *ptr = '%';
+ break;
+ }
+ else if (*ptr == '%')
+ {
+ /*
+ * Stop at zone separator (RFC 6874)
+ */
+
+ break;
+ }
+ else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+ }
+ else
+ {
+ /*
+ * Validate the hostname or IPv4 address first...
+ */
+
+ for (ptr = (char *)uri; *ptr; ptr ++)
+ if (strchr(":?/", *ptr))
+ break;
+ else if (!strchr("abcdefghijklmnopqrstuvwxyz" /* unreserved */
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* unreserved */
+ "0123456789" /* unreserved */
+ "-._~" /* unreserved */
+ "%" /* pct-encoded */
+ "!$&'()*+,;=" /* sub-delims */
+ "\\", *ptr)) /* SMB domain */
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+
+ /*
+ * Then copy the hostname or IPv4 address to the buffer...
+ */
+
+ uri = http_copy_decode(host, uri, hostlen, ":?/",
+ decoding & HTTP_URI_CODING_HOSTNAME);
+
+ if (!uri)
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+ }
+
+ /*
+ * Validate hostname for file scheme - only empty and localhost are
+ * acceptable.
+ */
+
+ if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
+ {
+ *host = '\0';
+ return (HTTP_URI_STATUS_BAD_HOSTNAME);
+ }
+
+ /*
+ * See if we have a port number...
+ */
+
+ if (*uri == ':')
+ {
+ /*
+ * Yes, collect the port number...
+ */
+
+ if (!isdigit(uri[1] & 255))
+ {
+ *port = 0;
+ return (HTTP_URI_STATUS_BAD_PORT);
+ }
+
+ *port = strtol(uri + 1, (char **)&uri, 10);
+
+ if (*uri != '/' && *uri)
+ {
+ *port = 0;
+ return (HTTP_URI_STATUS_BAD_PORT);
+ }
+ }
+ }
+
+ /*
+ * The remaining portion is the resource string...
+ */
+
+ if (*uri == '?' || !*uri)
+ {
+ /*
+ * Hostname but no path...
+ */
+
+ status = HTTP_URI_STATUS_MISSING_RESOURCE;
+ *resource = '/';
+
+ /*
+ * Copy any query string...
+ */
+
+ if (*uri == '?')
+ uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
+ decoding & HTTP_URI_CODING_QUERY);
+ else
+ resource[1] = '\0';
+ }
+ else
+ {
+ uri = http_copy_decode(resource, uri, resourcelen, "?",
+ decoding & HTTP_URI_CODING_RESOURCE);
+
+ if (uri && *uri == '?')
+ {
+ /*
+ * Concatenate any query string...
+ */
+
+ char *resptr = resource + strlen(resource);
+
+ uri = http_copy_decode(resptr, uri,
+ resourcelen - (int)(resptr - resource), NULL,
+ decoding & HTTP_URI_CODING_QUERY);
+ }
+ }
+
+ if (!uri)
+ {
+ *resource = '\0';
+ return (HTTP_URI_STATUS_BAD_RESOURCE);
+ }
+
+ /*
+ * Return the URI separation status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * '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.
+ */
+
+const char * /* O - Localized status string */
+httpStatus(http_status_t status) /* I - HTTP status code */
+{
+ const char *s; /* Status string */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ switch (status)
+ {
+ case HTTP_STATUS_ERROR :
+ s = strerror(errno);
+ break;
+ case HTTP_STATUS_CONTINUE :
+ s = _("Continue");
+ break;
+ case HTTP_STATUS_SWITCHING_PROTOCOLS :
+ s = _("Switching Protocols");
+ break;
+ case HTTP_STATUS_OK :
+ s = _("OK");
+ break;
+ case HTTP_STATUS_CREATED :
+ s = _("Created");
+ break;
+ case HTTP_STATUS_ACCEPTED :
+ s = _("Accepted");
+ break;
+ case HTTP_STATUS_NO_CONTENT :
+ s = _("No Content");
+ break;
+ case HTTP_STATUS_MOVED_PERMANENTLY :
+ s = _("Moved Permanently");
+ break;
+ case HTTP_STATUS_SEE_OTHER :
+ s = _("See Other");
+ break;
+ case HTTP_STATUS_NOT_MODIFIED :
+ s = _("Not Modified");
+ break;
+ case HTTP_STATUS_BAD_REQUEST :
+ s = _("Bad Request");
+ break;
+ case HTTP_STATUS_UNAUTHORIZED :
+ case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
+ s = _("Unauthorized");
+ break;
+ case HTTP_STATUS_FORBIDDEN :
+ s = _("Forbidden");
+ break;
+ case HTTP_STATUS_NOT_FOUND :
+ s = _("Not Found");
+ break;
+ case HTTP_STATUS_REQUEST_TOO_LARGE :
+ s = _("Request Entity Too Large");
+ break;
+ case HTTP_STATUS_URI_TOO_LONG :
+ s = _("URI Too Long");
+ break;
+ case HTTP_STATUS_UPGRADE_REQUIRED :
+ s = _("Upgrade Required");
+ break;
+ case HTTP_STATUS_NOT_IMPLEMENTED :
+ s = _("Not Implemented");
+ break;
+ case HTTP_STATUS_NOT_SUPPORTED :
+ s = _("Not Supported");
+ break;
+ case HTTP_STATUS_EXPECTATION_FAILED :
+ s = _("Expectation Failed");
+ break;
+ case HTTP_STATUS_SERVICE_UNAVAILABLE :
+ s = _("Service Unavailable");
+ break;
+ case HTTP_STATUS_SERVER_ERROR :
+ s = _("Internal Server Error");
+ break;
+ case HTTP_STATUS_CUPS_PKI_ERROR :
+ s = _("SSL/TLS Negotiation Error");
+ break;
+ case HTTP_STATUS_CUPS_WEBIF_DISABLED :
+ s = _("Web Interface is Disabled");
+ break;
+
+ default :
+ s = _("Unknown");
+ break;
+ }
+
+ return (_cupsLangString(cg->lang_default, s));
+}
+
+
+#ifndef HAVE_HSTRERROR
+/*
+ * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others.
+ */
+
+const char * /* O - Error string */
+_cups_hstrerror(int error) /* I - Error number */
+{
+ static const char * const errors[] = /* Error strings */
+ {
+ "OK",
+ "Host not found.",
+ "Try again.",
+ "Unrecoverable lookup error.",
+ "No data associated with name."
+ };
+
+
+ if (error < 0 || error > 4)
+ return ("Unknown hostname lookup error.");
+ else
+ return (errors[error]);
+}
+#endif /* !HAVE_HSTRERROR */
+
+
+/*
+ * '_httpDecodeURI()' - Percent-decode a HTTP request URI.
+ */
+
+char * /* O - Decoded URI or NULL on error */
+_httpDecodeURI(char *dst, /* I - Destination buffer */
+ const char *src, /* I - Source URI */
+ size_t dstsize) /* I - Size of destination buffer */
+{
+ if (http_copy_decode(dst, src, (int)dstsize, NULL, 1))
+ return (dst);
+ else
+ return (NULL);
+}
+
+
+/*
+ * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
+ */
+
+char * /* O - Encoded URI */
+_httpEncodeURI(char *dst, /* I - Destination buffer */
+ const char *src, /* I - Source URI */
+ size_t dstsize) /* I - Size of destination buffer */
+{
+ http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
+ return (dst);
+}
+
+
+/*
+ * '_httpResolveURI()' - Resolve a DNS-SD URI.
+ */
+
+const char * /* O - Resolved URI */
+_httpResolveURI(
+ const char *uri, /* I - DNS-SD URI */
+ char *resolved_uri, /* I - Buffer for resolved URI */
+ size_t resolved_size, /* I - Size of URI buffer */
+ int options, /* I - Resolve options */
+ int (*cb)(void *context), /* I - Continue callback function */
+ void *context) /* I - Context pointer for callback */
+{
+ char scheme[32], /* URI components... */
+ userpass[256],
+ hostname[1024],
+ resource[1024];
+ int port;
+#ifdef DEBUG
+ http_uri_status_t status; /* URI decode status */
+#endif /* DEBUG */
+
+
+ DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
+ "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
+ CUPS_LLCAST resolved_size));
+
+ /*
+ * Get the device URI...
+ */
+
+#ifdef DEBUG
+ if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
+ sizeof(scheme), userpass, sizeof(userpass),
+ hostname, sizeof(hostname), &port, resource,
+ sizeof(resource))) < HTTP_URI_STATUS_OK)
+#else
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
+ sizeof(scheme), userpass, sizeof(userpass),
+ hostname, sizeof(hostname), &port, resource,
+ sizeof(resource)) < HTTP_URI_STATUS_OK)
+#endif /* DEBUG */
+ {
+ if (options & _HTTP_RESOLVE_STDERR)
+ _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri);
+
+ DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
+ DEBUG_puts("5_httpResolveURI: Returning NULL");
+ return (NULL);
+ }
+
+ /*
+ * Resolve it as needed...
+ */
+
+ if (strstr(hostname, "._tcp"))
+ {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ char *regtype, /* Pointer to type in hostname */
+ *domain; /* Pointer to domain in hostname */
+ _http_uribuf_t uribuf; /* URI buffer */
+ int offline = 0; /* offline-report state set? */
+# ifdef HAVE_DNSSD
+# ifdef WIN32
+# pragma comment(lib, "dnssd.lib")
+# endif /* WIN32 */
+ DNSServiceRef ref, /* DNS-SD master service reference */
+ domainref, /* DNS-SD service reference for domain */
+ localref; /* DNS-SD service reference for .local */
+ int domainsent = 0; /* Send the domain resolve? */
+# ifdef HAVE_POLL
+ struct pollfd polldata; /* Polling data */
+# else /* select() */
+ fd_set input_set; /* Input set for select() */
+ struct timeval stimeout; /* Timeout value for select() */
+# endif /* HAVE_POLL */
+# elif defined(HAVE_AVAHI)
+ AvahiClient *client; /* Client information */
+ int error; /* Status */
+# endif /* HAVE_DNSSD */
+
+ if (options & _HTTP_RESOLVE_STDERR)
+ fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
+
+ /*
+ * Separate the hostname into service name, registration type, and domain...
+ */
+
+ for (regtype = strstr(hostname, "._tcp") - 2;
+ regtype > hostname;
+ regtype --)
+ if (regtype[0] == '.' && regtype[1] == '_')
+ {
+ /*
+ * Found ._servicetype in front of ._tcp...
+ */
+
+ *regtype++ = '\0';
+ break;
+ }
+
+ if (regtype <= hostname)
+ {
+ DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
+ return (NULL);
+ }
+
+ for (domain = strchr(regtype, '.');
+ domain;
+ domain = strchr(domain + 1, '.'))
+ if (domain[1] != '_')
+ break;
+
+ if (domain)
+ *domain++ = '\0';
+
+ uribuf.buffer = resolved_uri;
+ uribuf.bufsize = resolved_size;
+ uribuf.options = options;
+ uribuf.resource = resource;
+
+ resolved_uri[0] = '\0';
+
+ DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
+ "domain=\"%s\"\n", hostname, regtype, domain));
+ if (options & _HTTP_RESOLVE_STDERR)
+ {
+ fputs("STATE: +connecting-to-device\n", stderr);
+ fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
+ "domain=\"local.\"...\n", hostname, regtype);
+ }
+
+ uri = NULL;
+
+# ifdef HAVE_DNSSD
+ if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
+ {
+ int myinterface = kDNSServiceInterfaceIndexAny;
+ /* Lookup on any interface */
+
+ if (!strcmp(scheme, "ippusb"))
+ myinterface = kDNSServiceInterfaceIndexLocalOnly;
+
+ localref = ref;
+ if (DNSServiceResolve(&localref,
+ kDNSServiceFlagsShareConnection, myinterface,
+ hostname, regtype, "local.", http_resolve_cb,
+ &uribuf) == kDNSServiceErr_NoError)
+ {
+ int fds; /* Number of ready descriptors */
+ time_t timeout, /* Poll timeout */
+ start_time = time(NULL),/* Start time */
+ end_time = start_time + 90;
+ /* End time */
+
+ while (time(NULL) < end_time)
+ {
+ if (options & _HTTP_RESOLVE_STDERR)
+ _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer."));
+
+ if (cb && !(*cb)(context))
+ {
+ DEBUG_puts("5_httpResolveURI: callback returned 0 (stop)");
+ break;
+ }
+
+ /*
+ * Wakeup every 2 seconds to emit a "looking for printer" message...
+ */
+
+ if ((timeout = end_time - time(NULL)) > 2)
+ timeout = 2;
+
+# ifdef HAVE_POLL
+ polldata.fd = DNSServiceRefSockFD(ref);
+ polldata.events = POLLIN;
+
+ fds = poll(&polldata, 1, 1000 * timeout);
+
+# else /* select() */
+ FD_ZERO(&input_set);
+ FD_SET(DNSServiceRefSockFD(ref), &input_set);
+
+# ifdef WIN32
+ stimeout.tv_sec = (long)timeout;
+# else
+ stimeout.tv_sec = timeout;
+# endif /* WIN32 */
+ stimeout.tv_usec = 0;
+
+ fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
+ &stimeout);
+# endif /* HAVE_POLL */
+
+ if (fds < 0)
+ {
+ if (errno != EINTR && errno != EAGAIN)
+ {
+ DEBUG_printf(("5_httpResolveURI: poll error: %s", strerror(errno)));
+ break;
+ }
+ }
+ else if (fds == 0)
+ {
+ /*
+ * Wait 2 seconds for a response to the local resolve; if nothing
+ * comes in, do an additional domain resolution...
+ */
+
+ if (domainsent == 0 && domain && _cups_strcasecmp(domain, "local."))
+ {
+ if (options & _HTTP_RESOLVE_STDERR)
+ fprintf(stderr,
+ "DEBUG: Resolving \"%s\", regtype=\"%s\", "
+ "domain=\"%s\"...\n", hostname, regtype,
+ domain ? domain : "");
+
+ domainref = ref;
+ if (DNSServiceResolve(&domainref,
+ kDNSServiceFlagsShareConnection,
+ myinterface, hostname, regtype, domain,
+ http_resolve_cb,
+ &uribuf) == kDNSServiceErr_NoError)
+ domainsent = 1;
+ }
+
+ /*
+ * If it hasn't resolved within 5 seconds set the offline-report
+ * printer-state-reason...
+ */
+
+ if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
+ time(NULL) > (start_time + 5))
+ {
+ fputs("STATE: +offline-report\n", stderr);
+ offline = 1;
+ }
+ }
+ else
+ {
+ if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
+ {
+ uri = resolved_uri;
+ break;
+ }
+ }
+ }
+
+ if (domainsent)
+ DNSServiceRefDeallocate(domainref);
+
+ DNSServiceRefDeallocate(localref);
+ }
+
+ DNSServiceRefDeallocate(ref);
+ }
+# else /* HAVE_AVAHI */
+ if ((uribuf.poll = avahi_simple_poll_new()) != NULL)
+ {
+ avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL);
+
+ if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll),
+ 0, http_client_cb,
+ &uribuf, &error)) != NULL)
+ {
+ if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, hostname,
+ regtype, "local.", AVAHI_PROTO_UNSPEC, 0,
+ http_resolve_cb, &uribuf) != NULL)
+ {
+ time_t start_time = time(NULL),
+ /* Start time */
+ end_time = start_time + 90;
+ /* End time */
+ int pstatus; /* Poll status */
+
+ pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000);
+
+ if (pstatus == 0 && !resolved_uri[0] && domain &&
+ _cups_strcasecmp(domain, "local."))
+ {
+ /*
+ * Resolve for .local hasn't returned anything, try the listed
+ * domain...
+ */
+
+ avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, hostname,
+ regtype, domain, AVAHI_PROTO_UNSPEC, 0,
+ http_resolve_cb, &uribuf);
+ }
+
+ while (!pstatus && !resolved_uri[0] && time(NULL) < end_time)
+ {
+ if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0)
+ break;
+
+ /*
+ * If it hasn't resolved within 5 seconds set the offline-report
+ * printer-state-reason...
+ */
+
+ if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
+ time(NULL) > (start_time + 5))
+ {
+ fputs("STATE: +offline-report\n", stderr);
+ offline = 1;
+ }
+ }
+
+ /*
+ * Collect the result (if we got one).
+ */
+
+ if (resolved_uri[0])
+ uri = resolved_uri;
+ }
+
+ avahi_client_free(client);
+ }
+
+ avahi_simple_poll_free(uribuf.poll);
+ }
+# endif /* HAVE_DNSSD */
+
+ if (options & _HTTP_RESOLVE_STDERR)
+ {
+ if (uri)
+ {
+ fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
+ fputs("STATE: -connecting-to-device,offline-report\n", stderr);
+ }
+ else
+ {
+ fputs("DEBUG: Unable to resolve URI\n", stderr);
+ fputs("STATE: -connecting-to-device\n", stderr);
+ }
+ }
+
+#else /* HAVE_DNSSD || HAVE_AVAHI */
+ /*
+ * No DNS-SD support...
+ */
+
+ uri = NULL;
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ if ((options & _HTTP_RESOLVE_STDERR) && !uri)
+ _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer."));
+ }
+ else
+ {
+ /*
+ * Nothing more to do...
+ */
+
+ strlcpy(resolved_uri, uri, resolved_size);
+ uri = resolved_uri;
+ }
+
+ DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
+
+ return (uri);
+}
+
+
+#ifdef HAVE_AVAHI
+/*
+ * 'http_client_cb()' - Client callback for resolving URI.
+ */
+
+static void
+http_client_cb(
+ AvahiClient *client, /* I - Client information */
+ AvahiClientState state, /* I - Current state */
+ void *context) /* I - Pointer to URI buffer */
+{
+ DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client,
+ state, context));
+
+ /*
+ * If the connection drops, quit.
+ */
+
+ if (state == AVAHI_CLIENT_FAILURE)
+ {
+ _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
+ /* URI buffer */
+
+ avahi_simple_poll_quit(uribuf->poll);
+ }
+}
+#endif /* HAVE_AVAHI */
+
+
+/*
+ * 'http_copy_decode()' - Copy and decode a URI.
+ */
+
+static const char * /* O - New source pointer or NULL on error */
+http_copy_decode(char *dst, /* O - Destination buffer */
+ const char *src, /* I - Source pointer */
+ int dstsize, /* I - Destination size */
+ const char *term, /* I - Terminating characters */
+ int decode) /* I - Decode %-encoded values */
+{
+ char *ptr, /* Pointer into buffer */
+ *end; /* End of buffer */
+ int quoted; /* Quoted character */
+
+
+ /*
+ * Copy the src to the destination until we hit a terminating character
+ * or the end of the string.
+ */
+
+ for (ptr = dst, end = dst + dstsize - 1;
+ *src && (!term || !strchr(term, *src));
+ src ++)
+ if (ptr < end)
+ {
+ if (*src == '%' && decode)
+ {
+ if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
+ {
+ /*
+ * Grab a hex-encoded character...
+ */
+
+ src ++;
+ if (isalpha(*src))
+ quoted = (tolower(*src) - 'a' + 10) << 4;
+ else
+ quoted = (*src - '0') << 4;
+
+ src ++;
+ if (isalpha(*src))
+ quoted |= tolower(*src) - 'a' + 10;
+ else
+ quoted |= *src - '0';
+
+ *ptr++ = quoted;
+ }
+ else
+ {
+ /*
+ * Bad hex-encoded character...
+ */
+
+ *ptr = '\0';
+ return (NULL);
+ }
+ }
+ else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f)
+ {
+ *ptr = '\0';
+ return (NULL);
+ }
+ else
+ *ptr++ = *src;
+ }
+
+ *ptr = '\0';
+
+ return (src);
+}
+
+
+/*
+ * 'http_copy_encode()' - Copy and encode a URI.
+ */
+
+static char * /* O - End of current URI */
+http_copy_encode(char *dst, /* O - Destination buffer */
+ const char *src, /* I - Source pointer */
+ char *dstend, /* I - End of destination buffer */
+ const char *reserved, /* I - Extra reserved characters */
+ const char *term, /* I - Terminating characters */
+ int encode) /* I - %-encode reserved chars? */
+{
+ static const char hex[] = "0123456789ABCDEF";
+
+
+ while (*src && dst < dstend)
+ {
+ if (term && *src == *term)
+ return (dst);
+
+ if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
+ (reserved && strchr(reserved, *src))))
+ {
+ /*
+ * Hex encode reserved characters...
+ */
+
+ if ((dst + 2) >= dstend)
+ break;
+
+ *dst++ = '%';
+ *dst++ = hex[(*src >> 4) & 15];
+ *dst++ = hex[*src & 15];
+
+ src ++;
+ }
+ else
+ *dst++ = *src++;
+ }
+
+ *dst = '\0';
+
+ if (*src)
+ return (NULL);
+ else
+ return (dst);
+}
+
+
+#ifdef HAVE_DNSSD
+/*
+ * 'http_resolve_cb()' - Build a device URI for the given service name.
+ */
+
+static void DNSSD_API
+http_resolve_cb(
+ DNSServiceRef sdRef, /* I - Service reference */
+ DNSServiceFlags flags, /* I - Results flags */
+ uint32_t interfaceIndex, /* I - Interface number */
+ DNSServiceErrorType errorCode, /* I - Error, if any */
+ const char *fullName, /* I - Full service name */
+ const char *hostTarget, /* I - Hostname */
+ uint16_t port, /* I - Port number */
+ uint16_t txtLen, /* I - Length of TXT record */
+ const unsigned char *txtRecord, /* I - TXT record data */
+ void *context) /* I - Pointer to URI buffer */
+{
+ _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
+ /* URI buffer */
+ const char *scheme, /* URI scheme */
+ *hostptr, /* Pointer into hostTarget */
+ *reskey, /* "rp" or "rfo" */
+ *resdefault; /* Default path */
+ char resource[257], /* Remote path */
+ fqdn[256]; /* FQDN of the .local name */
+ const void *value; /* Value from TXT record */
+ uint8_t valueLen; /* Length of value */
+
+
+ DEBUG_printf(("7http_resolve_cb(sdRef=%p, flags=%x, interfaceIndex=%u, "
+ "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
+ "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
+ interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
+ txtRecord, context));
+
+ /*
+ * Figure out the scheme from the full name...
+ */
+
+ if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls"))
+ scheme = "ipps";
+ else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
+ scheme = "ipp";
+ else if (strstr(fullName, "._http."))
+ scheme = "http";
+ else if (strstr(fullName, "._https."))
+ scheme = "https";
+ else if (strstr(fullName, "._printer."))
+ scheme = "lpd";
+ else if (strstr(fullName, "._pdl-datastream."))
+ scheme = "socket";
+ else
+ scheme = "riousbprint";
+
+ /*
+ * Extract the "remote printer" key from the TXT record...
+ */
+
+ if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
+ (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
+ !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen))
+ {
+ reskey = "rfo";
+ resdefault = "/ipp/faxout";
+ }
+ else
+ {
+ reskey = "rp";
+ resdefault = "/";
+ }
+
+ if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey,
+ &valueLen)) != NULL)
+ {
+ if (((char *)value)[0] == '/')
+ {
+ /*
+ * Value (incorrectly) has a leading slash already...
+ */
+
+ memcpy(resource, value, valueLen);
+ resource[valueLen] = '\0';
+ }
+ else
+ {
+ /*
+ * Convert to resource by concatenating with a leading "/"...
+ */
+
+ resource[0] = '/';
+ memcpy(resource + 1, value, valueLen);
+ resource[valueLen + 1] = '\0';
+ }
+ }
+ else
+ {
+ /*
+ * Use the default value...
+ */
+
+ strlcpy(resource, resdefault, sizeof(resource));
+ }
+
+ /*
+ * Lookup the FQDN if needed...
+ */
+
+ if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
+ (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget &&
+ !_cups_strcasecmp(hostptr, ".local."))
+ {
+ /*
+ * OK, we got a .local name but the caller needs a real domain. Start by
+ * getting the IP address of the .local name and then do reverse-lookups...
+ */
+
+ http_addrlist_t *addrlist, /* List of addresses */
+ *addr; /* Current address */
+
+ DEBUG_printf(("8http_resolve_cb: Looking up \"%s\".", hostTarget));
+
+ snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
+ if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
+ {
+ for (addr = addrlist; addr; addr = addr->next)
+ {
+ int error = getnameinfo(&(addr->addr.addr),
+ httpAddrLength(&(addr->addr)),
+ fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
+
+ if (!error)
+ {
+ DEBUG_printf(("8http_resolve_cb: Found \"%s\".", fqdn));
+
+ if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
+ _cups_strcasecmp(hostptr, ".local"))
+ {
+ hostTarget = fqdn;
+ break;
+ }
+ }
+#ifdef DEBUG
+ else
+ DEBUG_printf(("8http_resolve_cb: \"%s\" did not resolve: %d",
+ httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
+ error));
+#endif /* DEBUG */
+ }
+
+ httpAddrFreeList(addrlist);
+ }
+ }
+
+ /*
+ * Assemble the final device URI...
+ */
+
+ if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
+ !strcmp(uribuf->resource, "/cups"))
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize,
+ scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false",
+ resource);
+ else
+ httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize,
+ scheme, NULL, hostTarget, ntohs(port), resource);
+
+ DEBUG_printf(("8http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer));
+}
+
+#elif defined(HAVE_AVAHI)
+/*
+ * 'http_poll_cb()' - Wait for input on the specified file descriptors.
+ *
+ * Note: This function is needed because avahi_simple_poll_iterate is broken
+ * and always uses a timeout of 0 (!) milliseconds.
+ * (Avahi Ticket #364)
+ */
+
+static int /* O - Number of file descriptors matching */
+http_poll_cb(
+ struct pollfd *pollfds, /* I - File descriptors */
+ unsigned int num_pollfds, /* I - Number of file descriptors */
+ int timeout, /* I - Timeout in milliseconds (used) */
+ void *context) /* I - User data (unused) */
+{
+ (void)timeout;
+ (void)context;
+
+ return (poll(pollfds, num_pollfds, 2000));
+}
+
+
+/*
+ * 'http_resolve_cb()' - Build a device URI for the given service name.
+ */
+
+static void
+http_resolve_cb(
+ AvahiServiceResolver *resolver, /* I - Resolver (unused) */
+ AvahiIfIndex interface, /* I - Interface index (unused) */
+ AvahiProtocol protocol, /* I - Network protocol (unused) */
+ AvahiResolverEvent event, /* I - Event (found, etc.) */
+ const char *name, /* I - Service name */
+ const char *type, /* I - Registration type */
+ const char *domain, /* I - Domain (unused) */
+ const char *hostTarget, /* I - Hostname */
+ const AvahiAddress *address, /* I - Address (unused) */
+ uint16_t port, /* I - Port number */
+ AvahiStringList *txt, /* I - TXT record */
+ AvahiLookupResultFlags flags, /* I - Lookup flags (unused) */
+ void *context) /* I - Pointer to URI buffer */
+{
+ _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
+ /* URI buffer */
+ const char *scheme, /* URI scheme */
+ *hostptr, /* Pointer into hostTarget */
+ *reskey, /* "rp" or "rfo" */
+ *resdefault; /* Default path */
+ char resource[257], /* Remote path */
+ fqdn[256]; /* FQDN of the .local name */
+ AvahiStringList *pair; /* Current TXT record key/value pair */
+ char *value; /* Value for "rp" key */
+ size_t valueLen = 0; /* Length of "rp" key */
+
+
+ DEBUG_printf(("7http_resolve_cb(resolver=%p, "
+ "interface=%d, protocol=%d, event=%d, name=\"%s\", "
+ "type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, "
+ "port=%d, txt=%p, flags=%d, context=%p)",
+ resolver, interface, protocol, event, name, type, domain,
+ hostTarget, address, port, txt, flags, context));
+
+ if (event != AVAHI_RESOLVER_FOUND)
+ {
+ avahi_service_resolver_free(resolver);
+ avahi_simple_poll_quit(uribuf->poll);
+ return;
+ }
+
+ /*
+ * Figure out the scheme from the full name...
+ */
+
+ if (strstr(type, "_ipp."))
+ scheme = "ipp";
+ else if (strstr(type, "_printer."))
+ scheme = "lpd";
+ else if (strstr(type, "_pdl-datastream."))
+ scheme = "socket";
+ else
+ scheme = "riousbprint";
+
+ if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9))
+ scheme = "ipps";
+ else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9))
+ scheme = "ipp";
+ else if (!strncmp(type, "_http.", 6))
+ scheme = "http";
+ else if (!strncmp(type, "_https.", 7))
+ scheme = "https";
+ else if (!strncmp(type, "_printer.", 9))
+ scheme = "lpd";
+ else if (!strncmp(type, "_pdl-datastream.", 16))
+ scheme = "socket";
+ else
+ {
+ avahi_service_resolver_free(resolver);
+ avahi_simple_poll_quit(uribuf->poll);
+ return;
+ }
+
+ /*
+ * Extract the remote resource key from the TXT record...
+ */
+
+ if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
+ (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
+ !avahi_string_list_find(txt, "printer-type"))
+ {
+ reskey = "rfo";
+ resdefault = "/ipp/faxout";
+ }
+ else
+ {
+ reskey = "rp";
+ resdefault = "/";
+ }
+
+ if ((pair = avahi_string_list_find(txt, reskey)) != NULL)
+ {
+ avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
+
+ if (value[0] == '/')
+ {
+ /*
+ * Value (incorrectly) has a leading slash already...
+ */
+
+ memcpy(resource, value, valueLen);
+ resource[valueLen] = '\0';
+ }
+ else
+ {
+ /*
+ * Convert to resource by concatenating with a leading "/"...
+ */
+
+ resource[0] = '/';
+ memcpy(resource + 1, value, valueLen);
+ resource[valueLen + 1] = '\0';
+ }
+ }
+ else
+ {
+ /*
+ * Use the default value...
+ */
+
+ strlcpy(resource, resdefault, sizeof(resource));
+ }
+
+ /*
+ * Lookup the FQDN if needed...
+ */
+
+ if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
+ (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget &&
+ !_cups_strcasecmp(hostptr, ".local"))
+ {
+ /*
+ * OK, we got a .local name but the caller needs a real domain. Start by
+ * getting the IP address of the .local name and then do reverse-lookups...
+ */
+
+ http_addrlist_t *addrlist, /* List of addresses */
+ *addr; /* Current address */
+
+ DEBUG_printf(("8http_resolve_cb: Looking up \"%s\".", hostTarget));
+
+ snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
+ if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
+ {
+ for (addr = addrlist; addr; addr = addr->next)
+ {
+ int error = getnameinfo(&(addr->addr.addr),
+ httpAddrLength(&(addr->addr)),
+ fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
+
+ if (!error)
+ {
+ DEBUG_printf(("8http_resolve_cb: Found \"%s\".", fqdn));
+
+ if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
+ _cups_strcasecmp(hostptr, ".local"))
+ {
+ hostTarget = fqdn;
+ break;
+ }
+ }
+#ifdef DEBUG
+ else
+ DEBUG_printf(("8http_resolve_cb: \"%s\" did not resolve: %d",
+ httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
+ error));
+#endif /* DEBUG */
+ }
+
+ httpAddrFreeList(addrlist);
+ }
+ }
+
+ /*
+ * Assemble the final device URI using the resolved hostname...
+ */
+
+ httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
+ NULL, hostTarget, port, resource);
+ DEBUG_printf(("8http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer));
+
+ avahi_simple_poll_quit(uribuf->poll);
+}
+#endif /* HAVE_DNSSD */
+
+
+/*
+ * End of "$Id: http-support.c 11445 2013-12-05 19:57:43Z msweet $".
+ */
diff --git a/cups/libs/cups/http.c b/cups/libs/cups/http.c
new file mode 100644
index 000000000..d840769f9
--- /dev/null
+++ b/cups/libs/cups/http.c
@@ -0,0 +1,5910 @@
+/*
+ * "$Id: http.c 11761 2014-03-28 13:04:33Z msweet $"
+ *
+ * HTTP routines for CUPS.
+ *
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * This file contains Kerberos support code, copyright 2006 by Jelmer Vernooij.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <fcntl.h>
+#include <math.h>
+#ifdef WIN32
+# include <tchar.h>
+#else
+# include <signal.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif /* WIN32 */
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif /* HAVE_POLL */
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef HAVE_LIBZ
+static void http_content_coding_finish(http_t *http);
+static void http_content_coding_start(http_t *http,
+ const char *value);
+#endif /* HAVE_LIBZ */
+static http_t *http_create(const char *host, int port,
+ http_addrlist_t *addrlist, int family,
+ http_encryption_t encryption,
+ int blocking, _http_mode_t mode);
+#ifdef DEBUG
+static void http_debug_hex(const char *prefix, const char *buffer,
+ int bytes);
+#endif /* DEBUG */
+static http_field_t http_field(const char *name);
+static ssize_t http_read(http_t *http, char *buffer, size_t length);
+static ssize_t http_read_buffered(http_t *http, char *buffer, size_t length);
+static ssize_t http_read_chunk(http_t *http, char *buffer, size_t length);
+static int http_send(http_t *http, http_state_t request,
+ const char *uri);
+static ssize_t http_write(http_t *http, const char *buffer,
+ size_t length);
+static ssize_t http_write_chunk(http_t *http, const char *buffer,
+ size_t length);
+#ifdef HAVE_SSL
+static int http_read_ssl(http_t *http, char *buf, int len);
+# ifdef HAVE_CDSASSL
+static int http_set_credentials(http_t *http);
+# endif /* HAVE_CDSASSL */
+#endif /* HAVE_SSL */
+static off_t http_set_length(http_t *http);
+static void http_set_timeout(int fd, double timeout);
+static void http_set_wait(http_t *http);
+#ifdef DEBUG
+static const char *http_state_string(http_state_t state);
+#endif /* DEBUG */
+#ifdef HAVE_SSL
+static int http_setup_ssl(http_t *http);
+static void http_shutdown_ssl(http_t *http);
+static int http_upgrade(http_t *http);
+static int http_write_ssl(http_t *http, const char *buf, int len);
+#endif /* HAVE_SSL */
+
+
+/*
+ * Local globals...
+ */
+
+static const char * const http_fields[] =
+ {
+ "Accept-Language",
+ "Accept-Ranges",
+ "Authorization",
+ "Connection",
+ "Content-Encoding",
+ "Content-Language",
+ "Content-Length",
+ "Content-Location",
+ "Content-MD5",
+ "Content-Range",
+ "Content-Type",
+ "Content-Version",
+ "Date",
+ "Host",
+ "If-Modified-Since",
+ "If-Unmodified-since",
+ "Keep-Alive",
+ "Last-Modified",
+ "Link",
+ "Location",
+ "Range",
+ "Referer",
+ "Retry-After",
+ "Transfer-Encoding",
+ "Upgrade",
+ "User-Agent",
+ "WWW-Authenticate",
+ "Accept-Encoding",
+ "Allow",
+ "Server"
+ };
+
+
+#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/*
+ * BIO methods for OpenSSL...
+ */
+
+static int http_bio_write(BIO *h, const char *buf, int num);
+static int http_bio_read(BIO *h, char *buf, int size);
+static int http_bio_puts(BIO *h, const char *str);
+static long http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int http_bio_new(BIO *h);
+static int http_bio_free(BIO *data);
+
+static BIO_METHOD http_bio_methods =
+ {
+ BIO_TYPE_SOCKET,
+ "http",
+ http_bio_write,
+ http_bio_read,
+ http_bio_puts,
+ NULL, /* http_bio_gets, */
+ http_bio_ctrl,
+ http_bio_new,
+ http_bio_free,
+ NULL,
+ };
+#endif /* HAVE_SSL && HAVE_LIBSSL */
+
+
+/*
+ * 'httpAcceptConnection()' - Accept a new HTTP client connection from the
+ * specified listening socket.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+http_t * /* O - HTTP connection or @code NULL@ */
+httpAcceptConnection(int fd, /* I - Listen socket file descriptor */
+ int blocking) /* I - 1 if the connection should be
+ blocking, 0 otherwise */
+{
+ http_t *http; /* HTTP connection */
+ http_addrlist_t addrlist; /* Dummy address list */
+ socklen_t addrlen; /* Length of address */
+ int val; /* Socket option value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (fd < 0)
+ return (NULL);
+
+ /*
+ * Create the client connection...
+ */
+
+ memset(&addrlist, 0, sizeof(addrlist));
+
+ if ((http = http_create(NULL, 0, &addrlist, AF_UNSPEC,
+ HTTP_ENCRYPTION_IF_REQUESTED, blocking,
+ _HTTP_MODE_SERVER)) == NULL)
+ return (NULL);
+
+ /*
+ * Accept the client and get the remote address...
+ */
+
+ addrlen = sizeof(http_addr_t);
+
+ if ((http->fd = accept(fd, (struct sockaddr *)&(http->addrlist->addr),
+ &addrlen)) < 0)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ httpClose(http);
+
+ return (NULL);
+ }
+
+ httpAddrString(&(http->addrlist->addr), http->hostname,
+ sizeof(http->hostname));
+
+#ifdef SO_NOSIGPIPE
+ /*
+ * Disable SIGPIPE for this socket.
+ */
+
+ val = 1;
+ setsockopt(http->fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
+#endif /* SO_NOSIGPIPE */
+
+ /*
+ * Using TCP_NODELAY improves responsiveness, especially on systems
+ * with a slow loopback interface. Since we write large buffers
+ * when sending print files and requests, there shouldn't be any
+ * performance penalty for this...
+ */
+
+ val = 1;
+ setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
+
+#ifdef FD_CLOEXEC
+ /*
+ * Close this socket when starting another process...
+ */
+
+ fcntl(http->fd, F_SETFD, FD_CLOEXEC);
+#endif /* FD_CLOEXEC */
+
+ return (http);
+}
+
+
+/*
+ * 'httpAddCredential()' - Allocates and adds a single credential to an array.
+ *
+ * Use @code cupsArrayNew(NULL, NULL)@ to create a credentials array.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+int /* O - 0 on success, -1 on error */
+httpAddCredential(
+ cups_array_t *credentials, /* I - Credentials array */
+ const void *data, /* I - PEM-encoded X.509 data */
+ size_t datalen) /* I - Length of data */
+{
+ http_credential_t *credential; /* Credential data */
+
+
+ if ((credential = malloc(sizeof(http_credential_t))) != NULL)
+ {
+ credential->datalen = datalen;
+
+ if ((credential->data = malloc(datalen)) != NULL)
+ {
+ memcpy(credential->data, data, datalen);
+ cupsArrayAdd(credentials, credential);
+ return (0);
+ }
+
+ free(credential);
+ }
+
+ return (-1);
+}
+
+
+#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/*
+ * '_httpBIOMethods()' - Get the OpenSSL BIO methods for HTTP connections.
+ */
+
+BIO_METHOD * /* O - BIO methods for OpenSSL */
+_httpBIOMethods(void)
+{
+ return (&http_bio_methods);
+}
+#endif /* HAVE_SSL && HAVE_LIBSSL */
+
+
+/*
+ * 'httpBlocking()' - Set blocking/non-blocking behavior on a connection.
+ */
+
+void
+httpBlocking(http_t *http, /* I - Connection to server */
+ int b) /* I - 1 = blocking, 0 = non-blocking */
+{
+ if (http)
+ {
+ http->blocking = b;
+ http_set_wait(http);
+ }
+}
+
+
+/*
+ * 'httpCheck()' - Check to see if there is a pending response from the server.
+ */
+
+int /* O - 0 = no data, 1 = data available */
+httpCheck(http_t *http) /* I - Connection to server */
+{
+ return (httpWait(http, 0));
+}
+
+
+/*
+ * 'httpClearCookie()' - Clear the cookie value(s).
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+void
+httpClearCookie(http_t *http) /* I - Connection to server */
+{
+ if (!http)
+ return;
+
+ if (http->cookie)
+ {
+ free(http->cookie);
+ http->cookie = NULL;
+ }
+}
+
+
+/*
+ * 'httpClearFields()' - Clear HTTP request fields.
+ */
+
+void
+httpClearFields(http_t *http) /* I - Connection to server */
+{
+ DEBUG_printf(("httpClearFields(http=%p)", http));
+
+ if (http)
+ {
+ memset(http->fields, 0, sizeof(http->fields));
+
+ if (http->mode == _HTTP_MODE_CLIENT)
+ {
+ if (http->hostname[0] == '/')
+ httpSetField(http, HTTP_FIELD_HOST, "localhost");
+ else
+ httpSetField(http, HTTP_FIELD_HOST, http->hostname);
+ }
+
+ if (http->field_authorization)
+ {
+ free(http->field_authorization);
+ http->field_authorization = NULL;
+ }
+
+ if (http->accept_encoding)
+ {
+ _cupsStrFree(http->accept_encoding);
+ http->accept_encoding = NULL;
+ }
+
+ if (http->allow)
+ {
+ _cupsStrFree(http->allow);
+ http->allow = NULL;
+ }
+
+ if (http->server)
+ {
+ _cupsStrFree(http->server);
+ http->server = NULL;
+ }
+
+ http->expect = (http_status_t)0;
+ }
+}
+
+
+/*
+ * 'httpClose()' - Close an HTTP connection.
+ */
+
+void
+httpClose(http_t *http) /* I - Connection to server */
+{
+#ifdef HAVE_GSSAPI
+ OM_uint32 minor_status; /* Minor status code */
+#endif /* HAVE_GSSAPI */
+
+
+ DEBUG_printf(("httpClose(http=%p)", http));
+
+ /*
+ * Range check input...
+ */
+
+ if (!http)
+ return;
+
+ /*
+ * Close any open connection...
+ */
+
+ _httpDisconnect(http);
+
+ /*
+ * Free memory used...
+ */
+
+ httpAddrFreeList(http->addrlist);
+
+ if (http->cookie)
+ free(http->cookie);
+
+#ifdef HAVE_GSSAPI
+ if (http->gssctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&minor_status, &http->gssctx, GSS_C_NO_BUFFER);
+
+ if (http->gssname != GSS_C_NO_NAME)
+ gss_release_name(&minor_status, &http->gssname);
+#endif /* HAVE_GSSAPI */
+
+#ifdef HAVE_AUTHORIZATION_H
+ if (http->auth_ref)
+ AuthorizationFree(http->auth_ref, kAuthorizationFlagDefaults);
+#endif /* HAVE_AUTHORIZATION_H */
+
+ httpClearFields(http);
+
+ if (http->authstring && http->authstring != http->_authstring)
+ free(http->authstring);
+
+ free(http);
+}
+
+
+/*
+ * 'httpConnect()' - Connect to a HTTP server.
+ *
+ * This function is deprecated - use @link httpConnect2@ instead.
+ *
+ * @deprecated@
+ */
+
+http_t * /* O - New HTTP connection */
+httpConnect(const char *host, /* I - Host to connect to */
+ int port) /* I - Port number */
+{
+ return (httpConnect2(host, port, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED,
+ 1, 30000, NULL));
+}
+
+
+/*
+ * 'httpConnect2()' - Connect to a HTTP server.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+http_t * /* O - New HTTP connection */
+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 */
+ 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 */
+ int msec, /* I - Connection timeout in milliseconds, 0 means don't connect */
+ int *cancel) /* I - Pointer to "cancel" variable */
+{
+ http_t *http; /* New HTTP connection */
+
+
+ DEBUG_printf(("httpConnect2(host=\"%s\", port=%d, addrlist=%p, family=%d, "
+ "encryption=%d, blocking=%d, msec=%d, cancel=%p)", host, port,
+ addrlist, family, encryption, blocking, msec, cancel));
+
+ /*
+ * Create the HTTP structure...
+ */
+
+ if ((http = http_create(host, port, addrlist, family, encryption, blocking,
+ _HTTP_MODE_CLIENT)) == NULL)
+ return (NULL);
+
+ /*
+ * Optionally connect to the remote system...
+ */
+
+ if (msec == 0 || !httpReconnect2(http, msec, cancel))
+ return (http);
+
+ /*
+ * Could not connect to any known address - bail out!
+ */
+
+ httpClose(http);
+
+ return (NULL);
+}
+
+
+/*
+ * 'httpConnectEncrypt()' - Connect to a HTTP server using encryption.
+ *
+ * This function is now deprecated. Please use the @link httpConnect2@ function
+ * instead.
+ *
+ * @deprecated@
+ */
+
+http_t * /* O - New HTTP connection */
+httpConnectEncrypt(
+ const char *host, /* I - Host to connect to */
+ int port, /* I - Port number */
+ http_encryption_t encryption) /* I - Type of encryption to use */
+{
+ DEBUG_printf(("httpConnectEncrypt(host=\"%s\", port=%d, encryption=%d)",
+ host, port, encryption));
+
+ return (httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000,
+ NULL));
+}
+
+
+/*
+ * 'httpCopyCredentials()' - Copy the credentials associated with an encrypted
+ * connection.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+int /* O - Status of call (0 = success) */
+httpCopyCredentials(
+ http_t *http, /* I - Connection to server */
+ cups_array_t **credentials) /* O - Array of credentials */
+{
+# ifdef HAVE_LIBSSL
+# elif defined(HAVE_GNUTLS)
+# elif defined(HAVE_CDSASSL)
+ OSStatus error; /* Error code */
+ SecTrustRef peerTrust; /* Peer trust reference */
+ CFIndex count; /* Number of credentials */
+ SecCertificateRef secCert; /* Certificate reference */
+ CFDataRef data; /* Certificate data */
+ int i; /* Looping var */
+# elif defined(HAVE_SSPISSL)
+# endif /* HAVE_LIBSSL */
+
+
+ if (credentials)
+ *credentials = NULL;
+
+ if (!http || !http->tls || !credentials)
+ return (-1);
+
+# ifdef HAVE_LIBSSL
+ return (-1);
+
+# elif defined(HAVE_GNUTLS)
+ return (-1);
+
+# elif defined(HAVE_CDSASSL)
+ if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
+ {
+ if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
+ {
+ count = SecTrustGetCertificateCount(peerTrust);
+
+ for (i = 0; i < count; i ++)
+ {
+ secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
+ if ((data = SecCertificateCopyData(secCert)))
+ {
+ httpAddCredential(*credentials, CFDataGetBytePtr(data),
+ CFDataGetLength(data));
+ CFRelease(data);
+ }
+ }
+ }
+
+ CFRelease(peerTrust);
+ }
+
+ return (error);
+
+# elif defined(HAVE_SSPISSL)
+ return (-1);
+
+# else
+ return (-1);
+# endif /* HAVE_LIBSSL */
+}
+
+
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
+
+http_tls_credentials_t /* O - Internal credentials */
+_httpCreateCredentials(
+ cups_array_t *credentials) /* I - Array of credentials */
+{
+ if (!credentials)
+ return (NULL);
+
+# ifdef HAVE_LIBSSL
+ return (NULL);
+
+# elif defined(HAVE_GNUTLS)
+ return (NULL);
+
+# elif defined(HAVE_CDSASSL)
+ CFMutableArrayRef peerCerts; /* Peer credentials reference */
+ SecCertificateRef secCert; /* Certificate reference */
+ CFDataRef data; /* Credential data reference */
+ http_credential_t *credential; /* Credential data */
+
+
+ if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
+ cupsArrayCount(credentials),
+ &kCFTypeArrayCallBacks)) == NULL)
+ return (NULL);
+
+ for (credential = (http_credential_t *)cupsArrayFirst(credentials);
+ credential;
+ credential = (http_credential_t *)cupsArrayNext(credentials))
+ {
+ if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
+ credential->datalen)))
+ {
+ if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
+ != NULL)
+ {
+ CFArrayAppendValue(peerCerts, secCert);
+ CFRelease(secCert);
+ }
+
+ CFRelease(data);
+ }
+ }
+
+ return (peerCerts);
+
+# elif defined(HAVE_SSPISSL)
+ return (NULL);
+
+# else
+ return (NULL);
+# endif /* HAVE_LIBSSL */
+}
+
+
+/*
+ * 'httpDelete()' - Send a DELETE request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpDelete(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI to delete */
+{
+ return (http_send(http, HTTP_STATE_DELETE, uri));
+}
+
+
+/*
+ * '_httpDisconnect()' - Disconnect a HTTP connection.
+ */
+
+void
+_httpDisconnect(http_t *http) /* I - Connection to server */
+{
+#ifdef HAVE_SSL
+ if (http->tls)
+ http_shutdown_ssl(http);
+#endif /* HAVE_SSL */
+
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif /* WIN32 */
+
+ http->fd = -1;
+}
+
+
+/*
+ * 'httpEncryption()' - Set the required encryption on the link.
+ */
+
+int /* O - -1 on error, 0 on success */
+httpEncryption(http_t *http, /* I - Connection to server */
+ http_encryption_t e) /* I - New encryption preference */
+{
+ DEBUG_printf(("httpEncryption(http=%p, e=%d)", http, e));
+
+#ifdef HAVE_SSL
+ if (!http)
+ return (0);
+
+ http->encryption = e;
+
+ if ((http->encryption == HTTP_ENCRYPTION_ALWAYS && !http->tls) ||
+ (http->encryption == HTTP_ENCRYPTION_NEVER && http->tls))
+ return (httpReconnect2(http, 30000, NULL));
+ else if (http->encryption == HTTP_ENCRYPTION_REQUIRED && !http->tls)
+ return (http_upgrade(http));
+ else
+ return (0);
+#else
+ if (e == HTTP_ENCRYPTION_ALWAYS || e == HTTP_ENCRYPTION_REQUIRED)
+ return (-1);
+ else
+ return (0);
+#endif /* HAVE_SSL */
+}
+
+
+/*
+ * 'httpError()' - Get the last error on a connection.
+ */
+
+int /* O - Error code (errno) value */
+httpError(http_t *http) /* I - Connection to server */
+{
+ if (http)
+ return (http->error);
+ else
+ return (EINVAL);
+}
+
+
+/*
+ * 'httpFlush()' - Flush data from a HTTP connection.
+ */
+
+void
+httpFlush(http_t *http) /* I - Connection to server */
+{
+ char buffer[8192]; /* Junk buffer */
+ int blocking; /* To block or not to block */
+ http_state_t oldstate; /* Old state */
+
+
+ DEBUG_printf(("httpFlush(http=%p), state=%s", http,
+ http_state_string(http->state)));
+
+ /*
+ * Nothing to do if we are in the "waiting" state...
+ */
+
+ if (http->state == HTTP_STATE_WAITING)
+ return;
+
+ /*
+ * Temporarily set non-blocking mode so we don't get stuck in httpRead()...
+ */
+
+ blocking = http->blocking;
+ http->blocking = 0;
+
+ /*
+ * Read any data we can...
+ */
+
+ oldstate = http->state;
+ while (httpRead2(http, buffer, sizeof(buffer)) > 0);
+
+ /*
+ * Restore blocking and reset the connection if we didn't get all of
+ * the remaining data...
+ */
+
+ http->blocking = blocking;
+
+ if (http->state == oldstate && http->state != HTTP_STATE_WAITING &&
+ http->fd >= 0)
+ {
+ /*
+ * Didn't get the data back, so close the current connection.
+ */
+
+#ifdef HAVE_LIBZ
+ if (http->coding)
+ http_content_coding_finish(http);
+#endif /* HAVE_LIBZ */
+
+ DEBUG_puts("1httpFlush: Setting state to HTTP_STATE_WAITING and closing.");
+
+ http->state = HTTP_STATE_WAITING;
+
+#ifdef HAVE_SSL
+ if (http->tls)
+ http_shutdown_ssl(http);
+#endif /* HAVE_SSL */
+
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif /* WIN32 */
+
+ http->fd = -1;
+ }
+}
+
+
+/*
+ * 'httpFlushWrite()' - Flush data in write buffer.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - Bytes written or -1 on error */
+httpFlushWrite(http_t *http) /* I - Connection to server */
+{
+ int bytes; /* Bytes written */
+
+
+ DEBUG_printf(("httpFlushWrite(http=%p) data_encoding=%d", http,
+ http ? http->data_encoding : -1));
+
+ if (!http || !http->wused)
+ {
+ DEBUG_puts(http ? "1httpFlushWrite: Write buffer is empty." :
+ "1httpFlushWrite: No connection.");
+ return (0);
+ }
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ bytes = http_write_chunk(http, http->wbuffer, http->wused);
+ else
+ bytes = http_write(http, http->wbuffer, http->wused);
+
+ http->wused = 0;
+
+ DEBUG_printf(("1httpFlushWrite: Returning %d, errno=%d.", bytes, errno));
+
+ return (bytes);
+}
+
+
+/*
+ * '_httpFreeCredentials()' - Free internal credentials.
+ */
+
+void
+_httpFreeCredentials(
+ http_tls_credentials_t credentials) /* I - Internal credentials */
+{
+ if (!credentials)
+ return;
+
+#ifdef HAVE_LIBSSL
+ (void)credentials;
+
+#elif defined(HAVE_GNUTLS)
+ (void)credentials;
+
+#elif defined(HAVE_CDSASSL)
+ CFRelease(credentials);
+
+#elif defined(HAVE_SSPISSL)
+ (void)credentials;
+
+#endif /* HAVE_LIBSSL */
+}
+
+
+/*
+ * 'httpFreeCredentials()' - Free an array of credentials.
+ */
+
+void
+httpFreeCredentials(
+ cups_array_t *credentials) /* I - Array of credentials */
+{
+ http_credential_t *credential; /* Credential */
+
+
+ for (credential = (http_credential_t *)cupsArrayFirst(credentials);
+ credential;
+ credential = (http_credential_t *)cupsArrayNext(credentials))
+ {
+ cupsArrayRemove(credentials, credential);
+ free((void *)credential->data);
+ free(credential);
+ }
+
+ cupsArrayDelete(credentials);
+}
+
+
+/*
+ * 'httpGet()' - Send a GET request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpGet(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI to get */
+{
+ return (http_send(http, HTTP_STATE_GET, uri));
+}
+
+
+/*
+ * '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.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+char * /* O - Authorization string */
+httpGetAuthString(http_t *http) /* I - Connection to server */
+{
+ if (http)
+ return (http->authstring);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'httpGetBlocking()' - Get the blocking/non-block state of a connection.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 1 if blocking, 0 if non-blocking */
+httpGetBlocking(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->blocking : 0);
+}
+
+
+/*
+ * 'httpGetContentEncoding()' - Get a common content encoding, if any, between
+ * the client and server.
+ *
+ * This function uses the value of the Accepts-Encoding HTTP header and must be
+ * called after receiving a response from the server or a request from the
+ * client. The value returned can be use in subsequent requests (for clients)
+ * or in the response (for servers) in order to compress the content stream.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+const char * /* O - Content-Coding value or
+ @code NULL@ for the identity
+ coding. */
+httpGetContentEncoding(http_t *http) /* I - Connection to client/server */
+{
+#ifdef HAVE_LIBZ
+ if (http && http->accept_encoding)
+ {
+ int i; /* Looping var */
+ char temp[HTTP_MAX_VALUE], /* Copy of Accepts-Encoding value */
+ *start, /* Start of coding value */
+ *end; /* End of coding value */
+ double qvalue; /* "qvalue" for coding */
+ struct lconv *loc = localeconv(); /* Locale data */
+ static const char * const codings[] =
+ { /* Supported content codings */
+ "deflate",
+ "gzip",
+ "x-deflate",
+ "x-gzip"
+ };
+
+ strlcpy(temp, http->accept_encoding, sizeof(temp));
+
+ for (start = temp; *start; start = end)
+ {
+ /*
+ * Find the end of the coding name...
+ */
+
+ qvalue = 1.0;
+ end = start;
+ while (*end && *end != ';' && *end != ',' && !isspace(*end & 255))
+ end ++;
+
+ if (*end == ';')
+ {
+ /*
+ * Grab the qvalue as needed...
+ */
+
+ if (!strncmp(end, ";q=", 3))
+ qvalue = _cupsStrScand(end + 3, NULL, loc);
+
+ /*
+ * Skip past all attributes...
+ */
+
+ *end++ = '\0';
+ while (*end && *end != ',' && !isspace(*end & 255))
+ end ++;
+ }
+ else if (*end)
+ *end++ = '\0';
+
+ while (*end && isspace(*end & 255))
+ end ++;
+
+ /*
+ * Check value if it matches something we support...
+ */
+
+ if (qvalue <= 0.0)
+ continue;
+
+ for (i = 0; i < (int)(sizeof(codings) / sizeof(codings[0])); i ++)
+ if (!strcmp(start, codings[i]))
+ return (codings[i]);
+ }
+ }
+#endif /* HAVE_LIBZ */
+
+ return (NULL);
+}
+
+
+/*
+ * 'httpGetCookie()' - Get any cookie data from the response.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+const char * /* O - Cookie data or NULL */
+httpGetCookie(http_t *http) /* I - HTTP connecion */
+{
+ return (http ? http->cookie : NULL);
+}
+
+
+/*
+ * 'httpGetExpect()' - Get the value of the Expect header, if any.
+ *
+ * Returns @code HTTP_STATUS_NONE@ if there is no Expect header, otherwise
+ * returns the expected HTTP status code, typically @code HTTP_STATUS_CONTINUE@.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+http_status_t /* O - Expect: status, if any */
+httpGetExpect(http_t *http) /* I - Connection to client */
+{
+ if (!http)
+ return (HTTP_STATUS_ERROR);
+ else
+ return (http->expect);
+}
+
+
+/*
+ * 'httpGetFd()' - Get the file descriptor associated with a connection.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - File descriptor or -1 if none */
+httpGetFd(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->fd : -1);
+}
+
+
+/*
+ * 'httpGetField()' - Get a field value from a request/response.
+ */
+
+const char * /* O - Field value */
+httpGetField(http_t *http, /* I - Connection to server */
+ http_field_t field) /* I - Field to get */
+{
+ if (!http || field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX)
+ return (NULL);
+
+ switch (field)
+ {
+ case HTTP_FIELD_ACCEPT_ENCODING :
+ return (http->accept_encoding);
+
+ case HTTP_FIELD_ALLOW :
+ return (http->allow);
+
+ case HTTP_FIELD_SERVER :
+ return (http->server);
+
+ case HTTP_FIELD_AUTHORIZATION :
+ if (http->field_authorization)
+ {
+ /*
+ * Special case for WWW-Authenticate: as its contents can be
+ * longer than HTTP_MAX_VALUE...
+ */
+
+ return (http->field_authorization);
+ }
+
+ default :
+ return (http->fields[field]);
+ }
+}
+
+
+/*
+ * 'httpGetLength()' - Get the amount of data remaining from the
+ * content-length or transfer-encoding fields.
+ *
+ * This function is deprecated and will not return lengths larger than
+ * 2^31 - 1; use httpGetLength2() instead.
+ *
+ * @deprecated@
+ */
+
+int /* O - Content length */
+httpGetLength(http_t *http) /* I - Connection to server */
+{
+ /*
+ * Get the read content length and return the 32-bit value.
+ */
+
+ if (http)
+ {
+ httpGetLength2(http);
+
+ return (http->_data_remaining);
+ }
+ else
+ return (-1);
+}
+
+
+/*
+ * 'httpGetLength2()' - Get the amount of data remaining from the
+ * content-length or transfer-encoding fields.
+ *
+ * This function returns the complete content length, even for
+ * content larger than 2^31 - 1.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+off_t /* O - Content length */
+httpGetLength2(http_t *http) /* I - Connection to server */
+{
+ off_t remaining; /* Remaining length */
+
+
+ DEBUG_printf(("2httpGetLength2(http=%p), state=%s", http,
+ http_state_string(http->state)));
+
+ if (!http)
+ return (-1);
+
+ if (!_cups_strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked"))
+ {
+ DEBUG_puts("4httpGetLength2: chunked request!");
+ remaining = 0;
+ }
+ else
+ {
+ /*
+ * The following is a hack for HTTP servers that don't send a
+ * Content-Length or Transfer-Encoding field...
+ *
+ * If there is no Content-Length then the connection must close
+ * after the transfer is complete...
+ */
+
+ if (!http->fields[HTTP_FIELD_CONTENT_LENGTH][0])
+ {
+ /*
+ * Default content length is 0 for errors and certain types of operations,
+ * and 2^31-1 for other successful requests...
+ */
+
+ if (http->status >= HTTP_STATUS_MULTIPLE_CHOICES ||
+ http->state == HTTP_STATE_OPTIONS ||
+ (http->state == HTTP_STATE_GET && http->mode == _HTTP_MODE_SERVER) ||
+ http->state == HTTP_STATE_HEAD ||
+ (http->state == HTTP_STATE_PUT && http->mode == _HTTP_MODE_CLIENT) ||
+ http->state == HTTP_STATE_DELETE ||
+ http->state == HTTP_STATE_TRACE ||
+ http->state == HTTP_STATE_CONNECT)
+ remaining = 0;
+ else
+ remaining = 2147483647;
+ }
+ else if ((remaining = strtoll(http->fields[HTTP_FIELD_CONTENT_LENGTH],
+ NULL, 10)) < 0)
+ remaining = -1;
+
+ DEBUG_printf(("4httpGetLength2: content_length=" CUPS_LLFMT,
+ CUPS_LLCAST remaining));
+ }
+
+ return (remaining);
+}
+
+
+/*
+ * 'httpGets()' - Get a line of text from a HTTP connection.
+ */
+
+char * /* O - Line or NULL */
+httpGets(char *line, /* I - Line to read into */
+ int length, /* I - Max length of buffer */
+ http_t *http) /* I - Connection to server */
+{
+ char *lineptr, /* Pointer into line */
+ *lineend, /* End of line */
+ *bufptr, /* Pointer into input buffer */
+ *bufend; /* Pointer to end of buffer */
+ int bytes, /* Number of bytes read */
+ eol; /* End-of-line? */
+
+
+ DEBUG_printf(("2httpGets(line=%p, length=%d, http=%p)", line, length, http));
+
+ if (!http || !line || length <= 1)
+ return (NULL);
+
+ /*
+ * Read a line from the buffer...
+ */
+
+ http->error = 0;
+ lineptr = line;
+ lineend = line + length - 1;
+ eol = 0;
+
+ while (lineptr < lineend)
+ {
+ /*
+ * Pre-load the buffer as needed...
+ */
+
+#ifdef WIN32
+ WSASetLastError(0);
+#else
+ errno = 0;
+#endif /* WIN32 */
+
+ while (http->used == 0)
+ {
+ /*
+ * No newline; see if there is more data to be read...
+ */
+
+ while (!_httpWait(http, http->wait_value, 1))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ DEBUG_puts("3httpGets: Timed out!");
+#ifdef WIN32
+ http->error = WSAETIMEDOUT;
+#else
+ http->error = ETIMEDOUT;
+#endif /* WIN32 */
+ return (NULL);
+ }
+
+ bytes = http_read(http, http->buffer + http->used,
+ HTTP_MAX_BUFFER - http->used);
+
+ DEBUG_printf(("4httpGets: read %d bytes.", bytes));
+
+ if (bytes < 0)
+ {
+ /*
+ * Nope, can't get a line this time...
+ */
+
+#ifdef WIN32
+ DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError()));
+
+ if (WSAGetLastError() == WSAEINTR)
+ continue;
+ else if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ http->error = WSAGetLastError();
+ }
+ else if (WSAGetLastError() != http->error)
+ {
+ http->error = WSAGetLastError();
+ continue;
+ }
+
+#else
+ DEBUG_printf(("3httpGets: recv() error %d!", errno));
+
+ if (errno == EINTR)
+ continue;
+ else if (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+ else if (!http->timeout_cb && errno == EAGAIN)
+ continue;
+
+ http->error = errno;
+ }
+ else if (errno != http->error)
+ {
+ http->error = errno;
+ continue;
+ }
+#endif /* WIN32 */
+
+ return (NULL);
+ }
+ else if (bytes == 0)
+ {
+ http->error = EPIPE;
+
+ return (NULL);
+ }
+
+ /*
+ * Yup, update the amount used...
+ */
+
+ http->used += bytes;
+ }
+
+ /*
+ * Now copy as much of the current line as possible...
+ */
+
+ for (bufptr = http->buffer, bufend = http->buffer + http->used;
+ lineptr < lineend && bufptr < bufend;)
+ {
+ if (*bufptr == 0x0a)
+ {
+ eol = 1;
+ bufptr ++;
+ break;
+ }
+ else if (*bufptr == 0x0d)
+ bufptr ++;
+ else
+ *lineptr++ = *bufptr++;
+ }
+
+ http->used -= (int)(bufptr - http->buffer);
+ if (http->used > 0)
+ memmove(http->buffer, bufptr, http->used);
+
+ if (eol)
+ {
+ /*
+ * End of line...
+ */
+
+ http->activity = time(NULL);
+
+ *lineptr = '\0';
+
+ DEBUG_printf(("3httpGets: Returning \"%s\"", line));
+
+ return (line);
+ }
+ }
+
+ DEBUG_puts("3httpGets: No new line available!");
+
+ return (NULL);
+}
+
+
+/*
+ * 'httpGetState()' - Get the current state of the HTTP request.
+ */
+
+http_state_t /* O - HTTP state */
+httpGetState(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->state : HTTP_STATE_ERROR);
+}
+
+
+/*
+ * 'httpGetStatus()' - Get the status of the last HTTP request.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+http_status_t /* O - HTTP status */
+httpGetStatus(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->status : HTTP_STATUS_ERROR);
+}
+
+
+/*
+ * 'httpGetSubField()' - Get a sub-field value.
+ *
+ * @deprecated@
+ */
+
+char * /* O - Value or NULL */
+httpGetSubField(http_t *http, /* I - Connection to server */
+ http_field_t field, /* I - Field index */
+ const char *name, /* I - Name of sub-field */
+ char *value) /* O - Value string */
+{
+ return (httpGetSubField2(http, field, name, value, HTTP_MAX_VALUE));
+}
+
+
+/*
+ * 'httpGetSubField2()' - Get a sub-field value.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Value or NULL */
+httpGetSubField2(http_t *http, /* I - Connection to server */
+ http_field_t field, /* I - Field index */
+ const char *name, /* I - Name of sub-field */
+ char *value, /* O - Value string */
+ int valuelen) /* I - Size of value buffer */
+{
+ const char *fptr; /* Pointer into field */
+ char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */
+ *ptr, /* Pointer into string buffer */
+ *end; /* End of value buffer */
+
+ DEBUG_printf(("2httpGetSubField2(http=%p, field=%d, name=\"%s\", value=%p, "
+ "valuelen=%d)", http, field, name, value, valuelen));
+
+ if (!http || !name || !value || valuelen < 2 ||
+ field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX)
+ return (NULL);
+
+ end = value + valuelen - 1;
+
+ for (fptr = http->fields[field]; *fptr;)
+ {
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (_cups_isspace(*fptr))
+ fptr ++;
+
+ if (*fptr == ',')
+ {
+ fptr ++;
+ continue;
+ }
+
+ /*
+ * Get the sub-field name...
+ */
+
+ for (ptr = temp;
+ *fptr && *fptr != '=' && !_cups_isspace(*fptr) &&
+ ptr < (temp + sizeof(temp) - 1);
+ *ptr++ = *fptr++);
+
+ *ptr = '\0';
+
+ DEBUG_printf(("4httpGetSubField2: name=\"%s\"", temp));
+
+ /*
+ * Skip trailing chars up to the '='...
+ */
+
+ while (_cups_isspace(*fptr))
+ fptr ++;
+
+ if (!*fptr)
+ break;
+
+ if (*fptr != '=')
+ continue;
+
+ /*
+ * Skip = and leading whitespace...
+ */
+
+ fptr ++;
+
+ while (_cups_isspace(*fptr))
+ fptr ++;
+
+ if (*fptr == '\"')
+ {
+ /*
+ * Read quoted string...
+ */
+
+ for (ptr = value, fptr ++;
+ *fptr && *fptr != '\"' && ptr < end;
+ *ptr++ = *fptr++);
+
+ *ptr = '\0';
+
+ while (*fptr && *fptr != '\"')
+ fptr ++;
+
+ if (*fptr)
+ fptr ++;
+ }
+ else
+ {
+ /*
+ * Read unquoted string...
+ */
+
+ for (ptr = value;
+ *fptr && !_cups_isspace(*fptr) && *fptr != ',' && ptr < end;
+ *ptr++ = *fptr++);
+
+ *ptr = '\0';
+
+ while (*fptr && !_cups_isspace(*fptr) && *fptr != ',')
+ fptr ++;
+ }
+
+ DEBUG_printf(("4httpGetSubField2: value=\"%s\"", value));
+
+ /*
+ * See if this is the one...
+ */
+
+ if (!strcmp(name, temp))
+ {
+ DEBUG_printf(("3httpGetSubField2: Returning \"%s\"", value));
+ return (value);
+ }
+ }
+
+ value[0] = '\0';
+
+ DEBUG_puts("3httpGetSubField2: Returning NULL");
+
+ return (NULL);
+}
+
+
+/*
+ * 'httpGetVersion()' - Get the HTTP version at the other end.
+ */
+
+http_version_t /* O - Version number */
+httpGetVersion(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->version : HTTP_VERSION_1_0);
+}
+
+
+/*
+ * 'httpHead()' - Send a HEAD request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpHead(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI for head */
+{
+ DEBUG_printf(("httpHead(http=%p, uri=\"%s\")", http, uri));
+ return (http_send(http, HTTP_STATE_HEAD, uri));
+}
+
+
+/*
+ * 'httpInitialize()' - Initialize the HTTP interface library and set the
+ * default HTTP proxy (if any).
+ */
+
+void
+httpInitialize(void)
+{
+ static int initialized = 0; /* Have we been called before? */
+#ifdef WIN32
+ WSADATA winsockdata; /* WinSock data */
+#endif /* WIN32 */
+#ifdef HAVE_LIBSSL
+ int i; /* Looping var */
+ unsigned char data[1024]; /* Seed data */
+#endif /* HAVE_LIBSSL */
+
+
+ _cupsGlobalLock();
+ if (initialized)
+ {
+ _cupsGlobalUnlock();
+ return;
+ }
+
+#ifdef WIN32
+ WSAStartup(MAKEWORD(2,2), &winsockdata);
+
+#elif !defined(SO_NOSIGPIPE)
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+# ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+
+# elif defined(HAVE_SIGACTION)
+ struct sigaction action; /* POSIX sigaction data */
+
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+
+# else
+ signal(SIGPIPE, SIG_IGN);
+# endif /* !SO_NOSIGPIPE */
+#endif /* WIN32 */
+
+#ifdef HAVE_GNUTLS
+ /*
+ * Initialize GNU TLS...
+ */
+
+ gnutls_global_init();
+
+#elif defined(HAVE_LIBSSL)
+ /*
+ * Initialize OpenSSL...
+ */
+
+ SSL_load_error_strings();
+ SSL_library_init();
+
+ /*
+ * Using the current time is a dubious random seed, but on some systems
+ * it is the best we can do (on others, this seed isn't even used...)
+ */
+
+ CUPS_SRAND(time(NULL));
+
+ for (i = 0; i < sizeof(data); i ++)
+ data[i] = CUPS_RAND();
+
+ RAND_seed(data, sizeof(data));
+#endif /* HAVE_GNUTLS */
+
+ initialized = 1;
+ _cupsGlobalUnlock();
+}
+
+
+/*
+ * 'httpOptions()' - Send an OPTIONS request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpOptions(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI for options */
+{
+ return (http_send(http, HTTP_STATE_OPTIONS, uri));
+}
+
+
+/*
+ * 'httpPeek()' - Peek at data from a HTTP connection.
+ *
+ * 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@.
+ *
+ * For non-blocking connections the usual timeouts apply.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ssize_t /* O - Number of bytes copied */
+httpPeek(http_t *http, /* I - Connection to server */
+ char *buffer, /* I - Buffer for data */
+ size_t length) /* I - Maximum number of bytes */
+{
+ ssize_t bytes; /* Bytes read */
+ char len[32]; /* Length string */
+
+
+ DEBUG_printf(("httpPeek(http=%p, buffer=%p, length=" CUPS_LLFMT ")",
+ http, buffer, CUPS_LLCAST length));
+
+ if (http == NULL || buffer == NULL)
+ return (-1);
+
+ http->activity = time(NULL);
+ http->error = 0;
+
+ if (length <= 0)
+ return (0);
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED &&
+ http->data_remaining <= 0)
+ {
+ DEBUG_puts("2httpPeek: Getting chunk length...");
+
+ if (httpGets(len, sizeof(len), http) == NULL)
+ {
+ DEBUG_puts("1httpPeek: Could not get length!");
+ return (0);
+ }
+
+ if (!len[0])
+ {
+ DEBUG_puts("1httpPeek: Blank chunk length, trying again...");
+ if (!httpGets(len, sizeof(len), http))
+ {
+ DEBUG_puts("1httpPeek: Could not get chunk length.");
+ return (0);
+ }
+ }
+
+ http->data_remaining = strtoll(len, NULL, 16);
+
+ if (http->data_remaining < 0)
+ {
+ DEBUG_puts("1httpPeek: Negative chunk length!");
+ return (0);
+ }
+ }
+
+ DEBUG_printf(("2httpPeek: data_remaining=" CUPS_LLFMT,
+ CUPS_LLCAST http->data_remaining));
+
+ if (http->data_remaining <= 0 && http->data_encoding != HTTP_ENCODING_FIELDS)
+ {
+ /*
+ * A zero-length chunk ends a transfer; unless we are reading POST
+ * data, go idle...
+ */
+
+#ifdef HAVE_LIBZ
+ if (http->coding >= _HTTP_CODING_GUNZIP)
+ http_content_coding_finish(http);
+#endif /* HAVE_LIBZ */
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ httpGets(len, sizeof(len), http);
+
+ if (http->state == HTTP_STATE_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_STATE_STATUS;
+
+ DEBUG_printf(("1httpPeek: 0-length chunk, set state to %s.",
+ http_state_string(http->state)));
+
+ /*
+ * Prevent future reads for this request...
+ */
+
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+
+ return (0);
+ }
+ else if (length > (size_t)http->data_remaining)
+ length = (size_t)http->data_remaining;
+
+#ifdef HAVE_LIBZ
+ if (http->used == 0 &&
+ (http->coding == _HTTP_CODING_IDENTITY ||
+ (http->coding >= _HTTP_CODING_GUNZIP && http->stream.avail_in == 0)))
+#else
+ if (http->used == 0)
+#endif /* HAVE_LIBZ */
+ {
+ /*
+ * Buffer small reads for better performance...
+ */
+
+ ssize_t buflen; /* Length of read for buffer */
+
+ if (!http->blocking)
+ {
+ while (!httpWait(http, http->wait_value))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ return (0);
+ }
+ }
+
+ if (http->data_remaining > sizeof(http->buffer))
+ buflen = sizeof(http->buffer);
+ else
+ buflen = http->data_remaining;
+
+ DEBUG_printf(("2httpPeek: Reading %d bytes into buffer.", (int)buflen));
+ bytes = http_read(http, http->buffer, buflen);
+
+ DEBUG_printf(("2httpPeek: Read " CUPS_LLFMT " bytes into buffer.",
+ CUPS_LLCAST bytes));
+ if (bytes > 0)
+ {
+#ifdef DEBUG
+ http_debug_hex("httpPeek", http->buffer, (int)bytes);
+#endif /* DEBUG */
+
+ http->used = bytes;
+ }
+ }
+
+#ifdef HAVE_LIBZ
+ if (http->coding >= _HTTP_CODING_GUNZIP)
+ {
+# ifdef HAVE_INFLATECOPY
+ int zerr; /* Decompressor error */
+ z_stream stream; /* Copy of decompressor stream */
+
+ if (http->used > 0 && http->stream.avail_in < HTTP_MAX_BUFFER)
+ {
+ size_t buflen = buflen = HTTP_MAX_BUFFER - http->stream.avail_in;
+ /* Number of bytes to copy */
+
+ if (http->stream.avail_in > 0 &&
+ http->stream.next_in > http->dbuffer)
+ memmove(http->dbuffer, http->stream.next_in, http->stream.avail_in);
+
+ http->stream.next_in = http->dbuffer;
+
+ if (buflen > http->data_remaining)
+ buflen = http->data_remaining;
+
+ if (buflen > http->used)
+ buflen = http->used;
+
+ DEBUG_printf(("1httpPeek: Copying %d more bytes of data into "
+ "decompression buffer.", (int)buflen));
+
+ memcpy(http->dbuffer + http->stream.avail_in, http->buffer, buflen);
+ http->stream.avail_in += buflen;
+ http->used -= buflen;
+ http->data_remaining -= buflen;
+
+ if (http->used > 0)
+ memmove(http->buffer, http->buffer + buflen, http->used);
+ }
+
+ DEBUG_printf(("2httpPeek: length=%d, avail_in=%d", (int)length,
+ (int)http->stream.avail_in));
+
+ if (inflateCopy(&stream, &(http->stream)) != Z_OK)
+ {
+ DEBUG_puts("2httpPeek: Unable to copy decompressor stream.");
+ http->error = ENOMEM;
+ return (-1);
+ }
+
+ stream.next_out = (Bytef *)buffer;
+ stream.avail_out = length;
+
+ zerr = inflate(&stream, Z_SYNC_FLUSH);
+ inflateEnd(&stream);
+
+ if (zerr < Z_OK)
+ {
+ DEBUG_printf(("2httpPeek: zerr=%d", zerr));
+#ifdef DEBUG
+ http_debug_hex("2httpPeek", (char *)http->dbuffer,
+ http->stream.avail_in);
+#endif /* DEBUG */
+
+ http->error = EIO;
+ return (-1);
+ }
+
+ bytes = length - http->stream.avail_out;
+
+# else
+ DEBUG_puts("2httpPeek: No inflateCopy on this platform, httpPeek does not "
+ "work with compressed streams.");
+ return (-1);
+# endif /* HAVE_INFLATECOPY */
+ }
+ else
+#endif /* HAVE_LIBZ */
+ if (http->used > 0)
+ {
+ if (length > (size_t)http->used)
+ length = (size_t)http->used;
+
+ bytes = (ssize_t)length;
+
+ DEBUG_printf(("2httpPeek: grabbing %d bytes from input buffer...",
+ (int)bytes));
+
+ memcpy(buffer, http->buffer, length);
+ }
+ else
+ bytes = 0;
+
+ if (bytes < 0)
+ {
+#ifdef WIN32
+ if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK)
+ bytes = 0;
+ else
+ http->error = WSAGetLastError();
+#else
+ if (errno == EINTR || errno == EAGAIN)
+ bytes = 0;
+ else
+ http->error = errno;
+#endif /* WIN32 */
+ }
+ else if (bytes == 0)
+ {
+ http->error = EPIPE;
+ return (0);
+ }
+
+ return (bytes);
+}
+
+/* For OS X 10.8 and earlier */
+ssize_t _httpPeek(http_t *http, char *buffer, size_t length)
+{ return (httpPeek(http, buffer, length)); }
+
+
+/*
+ * 'httpPost()' - Send a POST request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpPost(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI for post */
+{
+ return (http_send(http, HTTP_STATE_POST, uri));
+}
+
+
+/*
+ * 'httpPrintf()' - Print a formatted string to a HTTP connection.
+ *
+ * @private@
+ */
+
+int /* O - Number of bytes written */
+httpPrintf(http_t *http, /* I - Connection to server */
+ const char *format, /* I - printf-style format string */
+ ...) /* I - Additional args as needed */
+{
+ int bytes; /* Number of bytes to write */
+ char buf[16384]; /* Buffer for formatted string */
+ va_list ap; /* Variable argument pointer */
+
+
+ DEBUG_printf(("2httpPrintf(http=%p, format=\"%s\", ...)", http, format));
+
+ va_start(ap, format);
+ bytes = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+
+ DEBUG_printf(("3httpPrintf: (%d bytes) %s", bytes, buf));
+
+ if (http->data_encoding == HTTP_ENCODING_FIELDS)
+ return (httpWrite2(http, buf, bytes));
+ else
+ {
+ if (http->wused)
+ {
+ DEBUG_puts("4httpPrintf: flushing existing data...");
+
+ if (httpFlushWrite(http) < 0)
+ return (-1);
+ }
+
+ return (http_write(http, buf, bytes));
+ }
+}
+
+
+/*
+ * 'httpPut()' - Send a PUT request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpPut(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI to put */
+{
+ DEBUG_printf(("httpPut(http=%p, uri=\"%s\")", http, uri));
+ return (http_send(http, HTTP_STATE_PUT, uri));
+}
+
+
+/*
+ * 'httpRead()' - Read data from a HTTP connection.
+ *
+ * This function is deprecated. Use the httpRead2() function which can
+ * read more than 2GB of data.
+ *
+ * @deprecated@
+ */
+
+int /* O - Number of bytes read */
+httpRead(http_t *http, /* I - Connection to server */
+ char *buffer, /* I - Buffer for data */
+ int length) /* I - Maximum number of bytes */
+{
+ return ((int)httpRead2(http, buffer, length));
+}
+
+
+/*
+ * 'httpRead2()' - Read data from a HTTP connection.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ssize_t /* O - Number of bytes read */
+httpRead2(http_t *http, /* I - Connection to server */
+ char *buffer, /* I - Buffer for data */
+ size_t length) /* I - Maximum number of bytes */
+{
+ ssize_t bytes; /* Bytes read */
+
+
+#ifdef HAVE_LIBZ
+ DEBUG_printf(("httpRead2(http=%p, buffer=%p, length=" CUPS_LLFMT
+ ") coding=%d data_encoding=%d data_remaining=" CUPS_LLFMT,
+ http, buffer, CUPS_LLCAST length,
+ http->coding,
+ http->data_encoding, CUPS_LLCAST http->data_remaining));
+#else
+ DEBUG_printf(("httpRead2(http=%p, buffer=%p, length=" CUPS_LLFMT
+ ") data_encoding=%d data_remaining=" CUPS_LLFMT,
+ http, buffer, CUPS_LLCAST length,
+ http->data_encoding, CUPS_LLCAST http->data_remaining));
+#endif /* HAVE_LIBZ */
+
+ if (http == NULL || buffer == NULL)
+ return (-1);
+
+ http->activity = time(NULL);
+ http->error = 0;
+
+ if (length <= 0)
+ return (0);
+
+#ifdef HAVE_LIBZ
+ if (http->coding >= _HTTP_CODING_GUNZIP)
+ {
+ do
+ {
+ if (http->stream.avail_in > 0)
+ {
+ int zerr; /* Decompressor error */
+
+ DEBUG_printf(("2httpRead2: avail_in=%d, avail_out=%d",
+ (int)http->stream.avail_in, (int)length));
+
+ http->stream.next_out = (Bytef *)buffer;
+ http->stream.avail_out = length;
+
+ if ((zerr = inflate(&(http->stream), Z_SYNC_FLUSH)) < Z_OK)
+ {
+ DEBUG_printf(("2httpRead2: zerr=%d", zerr));
+#ifdef DEBUG
+ http_debug_hex("2httpRead2", (char *)http->dbuffer,
+ http->stream.avail_in);
+#endif /* DEBUG */
+
+ http->error = EIO;
+ return (-1);
+ }
+
+ bytes = length - http->stream.avail_out;
+
+ DEBUG_printf(("2httpRead2: avail_in=%d, avail_out=%d, bytes=%d",
+ http->stream.avail_in, http->stream.avail_out,
+ (int)bytes));
+ }
+ else
+ bytes = 0;
+
+ if (bytes == 0)
+ {
+ ssize_t buflen = HTTP_MAX_BUFFER - http->stream.avail_in;
+ /* Additional bytes for buffer */
+
+ if (buflen > 0)
+ {
+ if (http->stream.avail_in > 0 &&
+ http->stream.next_in > http->dbuffer)
+ memmove(http->dbuffer, http->stream.next_in, http->stream.avail_in);
+
+ http->stream.next_in = http->dbuffer;
+
+ DEBUG_printf(("1httpRead2: Reading up to %d more bytes of data into "
+ "decompression buffer.", (int)buflen));
+
+ if (http->data_remaining > 0)
+ {
+ if (buflen > http->data_remaining)
+ buflen = http->data_remaining;
+
+ bytes = http_read_buffered(http,
+ (char *)http->dbuffer +
+ http->stream.avail_in, buflen);
+ }
+ else if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ bytes = http_read_chunk(http,
+ (char *)http->dbuffer +
+ http->stream.avail_in, buflen);
+ else
+ bytes = 0;
+
+ if (bytes < 0)
+ return (bytes);
+ else if (bytes == 0)
+ break;
+
+ DEBUG_printf(("1httpRead2: Adding " CUPS_LLFMT " bytes to "
+ "decompression buffer.", CUPS_LLCAST bytes));
+
+ http->data_remaining -= bytes;
+ http->stream.avail_in += bytes;
+
+ if (http->data_remaining <= 0 &&
+ http->data_encoding == HTTP_ENCODING_CHUNKED)
+ {
+ /*
+ * Read the trailing blank line now...
+ */
+
+ char len[32]; /* Length string */
+
+ httpGets(len, sizeof(len), http);
+ }
+
+ bytes = 0;
+ }
+ else
+ return (0);
+ }
+ }
+ while (bytes == 0);
+ }
+ else
+#endif /* HAVE_LIBZ */
+ if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODING_CHUNKED)
+ {
+ if ((bytes = http_read_chunk(http, buffer, length)) > 0)
+ {
+ http->data_remaining -= bytes;
+
+ if (http->data_remaining <= 0)
+ {
+ /*
+ * Read the trailing blank line now...
+ */
+
+ char len[32]; /* Length string */
+
+ httpGets(len, sizeof(len), http);
+ }
+ }
+ }
+ else if (http->data_remaining <= 0)
+ {
+ /*
+ * No more data to read...
+ */
+
+ return (0);
+ }
+ else
+ {
+ DEBUG_printf(("1httpRead2: Reading up to %d bytes into buffer.",
+ (int)length));
+
+ if (length > (size_t)http->data_remaining)
+ length = (size_t)http->data_remaining;
+
+ if ((bytes = http_read_buffered(http, buffer, length)) > 0)
+ {
+ http->data_remaining -= bytes;
+
+ if (http->data_remaining <= 0 &&
+ http->data_encoding == HTTP_ENCODING_CHUNKED)
+ {
+ /*
+ * Read the trailing blank line now...
+ */
+
+ char len[32]; /* Length string */
+
+ httpGets(len, sizeof(len), http);
+ }
+ }
+ }
+
+ if (
+#ifdef HAVE_LIBZ
+ (http->coding == _HTTP_CODING_IDENTITY ||
+ (http->coding >= _HTTP_CODING_GUNZIP && http->stream.avail_in == 0)) &&
+#endif /* HAVE_LIBZ */
+ ((http->data_remaining <= 0 &&
+ http->data_encoding == HTTP_ENCODING_LENGTH) ||
+ (http->data_encoding == HTTP_ENCODING_CHUNKED && bytes == 0)))
+ {
+#ifdef HAVE_LIBZ
+ if (http->coding >= _HTTP_CODING_GUNZIP)
+ http_content_coding_finish(http);
+#endif /* HAVE_LIBZ */
+
+ if (http->state == HTTP_STATE_POST_RECV)
+ http->state ++;
+ else if (http->state == HTTP_STATE_GET_SEND ||
+ http->state == HTTP_STATE_POST_SEND)
+ http->state = HTTP_STATE_WAITING;
+ else
+ http->state = HTTP_STATE_STATUS;
+
+ DEBUG_printf(("1httpRead2: End of content, set state to %s.",
+ http_state_string(http->state)));
+ }
+
+ return (bytes);
+}
+
+
+#if defined(HAVE_SSL) && defined(HAVE_CDSASSL)
+/*
+ * '_httpReadCDSA()' - Read function for the CDSA library.
+ */
+
+OSStatus /* O - -1 on error, 0 on success */
+_httpReadCDSA(
+ SSLConnectionRef connection, /* I - SSL/TLS connection */
+ void *data, /* I - Data buffer */
+ size_t *dataLength) /* IO - Number of bytes */
+{
+ OSStatus result; /* Return value */
+ ssize_t bytes; /* Number of bytes read */
+ http_t *http; /* HTTP connection */
+
+
+ http = (http_t *)connection;
+
+ if (!http->blocking)
+ {
+ /*
+ * Make sure we have data before we read...
+ */
+
+ while (!_httpWait(http, http->wait_value, 0))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ http->error = ETIMEDOUT;
+ return (-1);
+ }
+ }
+
+ do
+ {
+ bytes = recv(http->fd, data, *dataLength, 0);
+ }
+ while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (bytes == *dataLength)
+ {
+ result = 0;
+ }
+ else if (bytes > 0)
+ {
+ *dataLength = bytes;
+ result = errSSLWouldBlock;
+ }
+ else
+ {
+ *dataLength = 0;
+
+ if (bytes == 0)
+ result = errSSLClosedGraceful;
+ else if (errno == EAGAIN)
+ result = errSSLWouldBlock;
+ else
+ result = errSSLClosedAbort;
+ }
+
+ return (result);
+}
+#endif /* HAVE_SSL && HAVE_CDSASSL */
+
+
+#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
+/*
+ * '_httpReadGNUTLS()' - Read function for the GNU TLS library.
+ */
+
+ssize_t /* O - Number of bytes read or -1 on error */
+_httpReadGNUTLS(
+ gnutls_transport_ptr_t ptr, /* I - Connection to server */
+ void *data, /* I - Buffer */
+ size_t length) /* I - Number of bytes to read */
+{
+ http_t *http; /* HTTP connection */
+ ssize_t bytes; /* Bytes read */
+
+
+ DEBUG_printf(("6_httpReadGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
+
+ http = (http_t *)ptr;
+
+ if (!http->blocking)
+ {
+ /*
+ * Make sure we have data before we read...
+ */
+
+ while (!_httpWait(http, http->wait_value, 0))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ http->error = ETIMEDOUT;
+ return (-1);
+ }
+ }
+
+ bytes = recv(http->fd, data, length, 0);
+ DEBUG_printf(("6_httpReadGNUTLS: bytes=%d", (int)bytes));
+ return (bytes);
+}
+#endif /* HAVE_SSL && HAVE_GNUTLS */
+
+
+/*
+ * 'httpReadRequest()' - Read a HTTP request from a connection.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+http_state_t /* O - New state of connection */
+httpReadRequest(http_t *http, /* I - HTTP connection */
+ char *uri, /* I - URI buffer */
+ size_t urilen) /* I - Size of URI buffer */
+{
+ char line[4096], /* HTTP request line */
+ *req_method, /* HTTP request method */
+ *req_uri, /* HTTP request URI */
+ *req_version; /* HTTP request version number string */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("httpReadRequest(http=%p, uri=%p, urilen=" CUPS_LLFMT ")",
+ http, uri, CUPS_LLCAST urilen));
+
+ if (uri)
+ *uri = '\0';
+
+ if (!http || !uri || urilen < 1)
+ {
+ DEBUG_puts("1httpReadRequest: Bad arguments, returning HTTP_STATE_ERROR.");
+ return (HTTP_STATE_ERROR);
+ }
+ else if (http->state != HTTP_STATE_WAITING)
+ {
+ DEBUG_printf(("1httpReadRequest: Bad state %s, returning HTTP_STATE_ERROR.",
+ http_state_string(http->state)));
+ return (HTTP_STATE_ERROR);
+ }
+
+ /*
+ * Reset state...
+ */
+
+ httpClearFields(http);
+
+ http->activity = time(NULL);
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+ http->data_remaining = 0;
+ http->keep_alive = HTTP_KEEPALIVE_OFF;
+ http->status = HTTP_STATUS_OK;
+ http->version = HTTP_VERSION_1_1;
+
+ /*
+ * Read a line from the socket...
+ */
+
+ if (!httpGets(line, sizeof(line), http))
+ {
+ DEBUG_puts("1httpReadRequest: Unable to read, returning HTTP_STATE_ERROR");
+ return (HTTP_STATE_ERROR);
+ }
+
+ if (!line[0])
+ {
+ DEBUG_puts("1httpReadRequest: Blank line, returning HTTP_STATE_WAITING");
+ return (HTTP_STATE_WAITING);
+ }
+
+ DEBUG_printf(("1httpReadRequest: %s", line));
+
+ /*
+ * Parse it...
+ */
+
+ req_method = line;
+ req_uri = line;
+
+ while (*req_uri && !isspace(*req_uri & 255))
+ req_uri ++;
+
+ if (!*req_uri)
+ {
+ DEBUG_puts("1httpReadRequest: No request URI.");
+ return (HTTP_STATE_ERROR);
+ }
+
+ *req_uri++ = '\0';
+
+ while (*req_uri && isspace(*req_uri & 255))
+ req_uri ++;
+
+ req_version = req_uri;
+
+ while (*req_version && !isspace(*req_version & 255))
+ req_version ++;
+
+ if (!*req_version)
+ {
+ DEBUG_puts("1httpReadRequest: No request protocol version.");
+ return (HTTP_STATE_ERROR);
+ }
+
+ *req_version++ = '\0';
+
+ while (*req_version && isspace(*req_version & 255))
+ req_version ++;
+
+ /*
+ * Validate...
+ */
+
+ if (!strcmp(req_method, "OPTIONS"))
+ http->state = HTTP_STATE_OPTIONS;
+ else if (!strcmp(req_method, "GET"))
+ http->state = HTTP_STATE_GET;
+ else if (!strcmp(req_method, "HEAD"))
+ http->state = HTTP_STATE_HEAD;
+ else if (!strcmp(req_method, "POST"))
+ http->state = HTTP_STATE_POST;
+ else if (!strcmp(req_method, "PUT"))
+ http->state = HTTP_STATE_PUT;
+ else if (!strcmp(req_method, "DELETE"))
+ http->state = HTTP_STATE_DELETE;
+ else if (!strcmp(req_method, "TRACE"))
+ http->state = HTTP_STATE_TRACE;
+ else if (!strcmp(req_method, "CONNECT"))
+ http->state = HTTP_STATE_CONNECT;
+ else
+ {
+ DEBUG_printf(("1httpReadRequest: Unknown method \"%s\".", req_method));
+ return (HTTP_STATE_UNKNOWN_METHOD);
+ }
+
+ DEBUG_printf(("1httpReadRequest: Set state to %s.",
+ http_state_string(http->state)));
+
+ if (!strcmp(req_version, "HTTP/1.0"))
+ {
+ http->version = HTTP_VERSION_1_0;
+ http->keep_alive = HTTP_KEEPALIVE_OFF;
+ }
+ else if (!strcmp(req_version, "HTTP/1.1"))
+ {
+ http->version = HTTP_VERSION_1_1;
+ http->keep_alive = HTTP_KEEPALIVE_ON;
+ }
+ else
+ {
+ DEBUG_printf(("1httpReadRequest: Unknown version \"%s\".", req_version));
+ return (HTTP_STATE_UNKNOWN_VERSION);
+ }
+
+ DEBUG_printf(("1httpReadRequest: URI is \"%s\".", req_uri));
+ strlcpy(uri, req_uri, urilen);
+
+ return (http->state);
+}
+
+
+/*
+ * 'httpReconnect()' - Reconnect to a HTTP server.
+ *
+ * This function is deprecated. Please use the @link httpReconnect2@ function
+ * instead.
+ *
+ * @deprecated@
+ */
+
+int /* O - 0 on success, non-zero on failure */
+httpReconnect(http_t *http) /* I - Connection to server */
+{
+ DEBUG_printf(("httpReconnect(http=%p)", http));
+
+ return (httpReconnect2(http, 30000, NULL));
+}
+
+
+/*
+ * 'httpReconnect2()' - Reconnect to a HTTP server with timeout and optional
+ * cancel.
+ */
+
+int /* O - 0 on success, non-zero on failure */
+httpReconnect2(http_t *http, /* I - Connection to server */
+ int msec, /* I - Timeout in milliseconds */
+ int *cancel) /* I - Pointer to "cancel" variable */
+{
+ http_addrlist_t *addr; /* Connected address */
+#ifdef DEBUG
+ http_addrlist_t *current; /* Current address */
+ char temp[256]; /* Temporary address string */
+#endif /* DEBUG */
+
+
+ DEBUG_printf(("httpReconnect2(http=%p, msec=%d, cancel=%p)", http, msec,
+ cancel));
+
+ if (!http)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (-1);
+ }
+
+#ifdef HAVE_SSL
+ if (http->tls)
+ {
+ DEBUG_puts("2httpReconnect2: Shutting down SSL/TLS...");
+ http_shutdown_ssl(http);
+ }
+#endif /* HAVE_SSL */
+
+ /*
+ * Close any previously open socket...
+ */
+
+ if (http->fd >= 0)
+ {
+ DEBUG_printf(("2httpReconnect2: Closing socket %d...", http->fd));
+
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif /* WIN32 */
+
+ http->fd = -1;
+ }
+
+ /*
+ * Reset all state (except fields, which may be reused)...
+ */
+
+ http->state = HTTP_STATE_WAITING;
+ http->version = HTTP_VERSION_1_1;
+ http->keep_alive = HTTP_KEEPALIVE_OFF;
+ memset(&http->_hostaddr, 0, sizeof(http->_hostaddr));
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+ http->_data_remaining = 0;
+ http->used = 0;
+ http->data_remaining = 0;
+ http->hostaddr = NULL;
+ http->wused = 0;
+
+ /*
+ * Connect to the server...
+ */
+
+#ifdef DEBUG
+ for (current = http->addrlist; current; current = current->next)
+ DEBUG_printf(("2httpReconnect2: Address %s:%d",
+ httpAddrString(&(current->addr), temp, sizeof(temp)),
+ httpAddrPort(&(current->addr))));
+#endif /* DEBUG */
+
+ if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec,
+ cancel)) == NULL)
+ {
+ /*
+ * Unable to connect...
+ */
+
+#ifdef WIN32
+ http->error = WSAGetLastError();
+#else
+ http->error = errno;
+#endif /* WIN32 */
+ http->status = HTTP_STATUS_ERROR;
+
+ DEBUG_printf(("1httpReconnect2: httpAddrConnect failed: %s",
+ strerror(http->error)));
+
+ return (-1);
+ }
+
+ DEBUG_printf(("2httpReconnect2: New socket=%d", http->fd));
+
+ if (http->timeout_value > 0)
+ http_set_timeout(http->fd, http->timeout_value);
+
+ http->hostaddr = &(addr->addr);
+ http->error = 0;
+
+#ifdef HAVE_SSL
+ if (http->encryption == HTTP_ENCRYPTION_ALWAYS)
+ {
+ /*
+ * Always do encryption via SSL.
+ */
+
+ if (http_setup_ssl(http) != 0)
+ {
+# ifdef WIN32
+ closesocket(http->fd);
+# else
+ close(http->fd);
+# endif /* WIN32 */
+
+ return (-1);
+ }
+ }
+ else if (http->encryption == HTTP_ENCRYPTION_REQUIRED && !http->tls_upgrade)
+ return (http_upgrade(http));
+#endif /* HAVE_SSL */
+
+ DEBUG_printf(("1httpReconnect2: Connected to %s:%d...",
+ httpAddrString(http->hostaddr, temp, sizeof(temp)),
+ httpAddrPort(http->hostaddr)));
+
+ return (0);
+}
+
+
+/*
+ * '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().
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+void
+httpSetAuthString(http_t *http, /* I - Connection to server */
+ const char *scheme, /* I - Auth scheme (NULL to clear it) */
+ const char *data) /* I - Auth data (NULL for none) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!http)
+ return;
+
+ if (http->authstring && http->authstring != http->_authstring)
+ free(http->authstring);
+
+ http->authstring = http->_authstring;
+
+ if (scheme)
+ {
+ /*
+ * Set the current authorization string...
+ */
+
+ int len = (int)strlen(scheme) + (data ? (int)strlen(data) + 1 : 0) + 1;
+ char *temp;
+
+ if (len > (int)sizeof(http->_authstring))
+ {
+ if ((temp = malloc(len)) == NULL)
+ len = sizeof(http->_authstring);
+ else
+ http->authstring = temp;
+ }
+
+ if (data)
+ snprintf(http->authstring, len, "%s %s", scheme, data);
+ else
+ strlcpy(http->authstring, scheme, len);
+ }
+ else
+ {
+ /*
+ * Clear the current authorization string...
+ */
+
+ http->_authstring[0] = '\0';
+ }
+}
+
+
+/*
+ * 'httpSetCredentials()' - Set the credentials associated with an encrypted
+ * connection.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+int /* O - Status of call (0 = success) */
+httpSetCredentials(http_t *http, /* I - Connection to server */
+ cups_array_t *credentials) /* I - Array of credentials */
+{
+ if (!http || cupsArrayCount(credentials) < 1)
+ return (-1);
+
+ _httpFreeCredentials(http->tls_credentials);
+
+ http->tls_credentials = _httpCreateCredentials(credentials);
+
+ return (http->tls_credentials ? 0 : -1);
+}
+
+
+/*
+ * 'httpSetCookie()' - Set the cookie value(s).
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+void
+httpSetCookie(http_t *http, /* I - Connection */
+ const char *cookie) /* I - Cookie string */
+{
+ if (!http)
+ return;
+
+ if (http->cookie)
+ free(http->cookie);
+
+ if (cookie)
+ http->cookie = strdup(cookie);
+ else
+ http->cookie = NULL;
+}
+
+
+/*
+ * 'httpSetDefaultField()' - Set the default value of an HTTP header.
+ *
+ * Currently only @code HTTP_FIELD_ACCEPT_ENCODING@, @code HTTP_FIELD_SERVER@,
+ * and @code HTTP_FIELD_USER_AGENT@ can be set.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+void
+httpSetDefaultField(http_t *http, /* I - Connection to server */
+ http_field_t field, /* I - Field index */
+ const char *value)/* I - Value */
+{
+ DEBUG_printf(("httpSetDefaultField(http=%p, field=%d(%s), value=\"%s\")",
+ http, field, http_fields[field], value));
+
+ if (!http)
+ return;
+
+ switch (field)
+ {
+ case HTTP_FIELD_ACCEPT_ENCODING :
+ if (http->default_accept_encoding)
+ _cupsStrFree(http->default_accept_encoding);
+
+ http->default_accept_encoding = value ? _cupsStrAlloc(value) : NULL;
+ break;
+
+ case HTTP_FIELD_SERVER :
+ if (http->default_server)
+ _cupsStrFree(http->default_server);
+
+ http->default_server = value ? _cupsStrAlloc(value) : NULL;
+ break;
+
+ case HTTP_FIELD_USER_AGENT :
+ if (http->default_user_agent)
+ _cupsStrFree(http->default_user_agent);
+
+ http->default_user_agent = value ? _cupsStrAlloc(value) : NULL;
+ break;
+
+ default :
+ DEBUG_puts("1httpSetDefaultField: Ignored.");
+ break;
+ }
+}
+
+
+/*
+ * 'httpSetExpect()' - Set the Expect: header in a request.
+ *
+ * Currently only @code HTTP_STATUS_CONTINUE@ is supported for the "expect"
+ * argument.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+httpSetExpect(http_t *http, /* I - Connection to server */
+ http_status_t expect) /* I - HTTP status to expect
+ (@code HTTP_STATUS_CONTINUE@) */
+{
+ DEBUG_printf(("httpSetExpect(http=%p, expect=%d)", http, expect));
+
+ if (http)
+ http->expect = expect;
+}
+
+
+/*
+ * 'httpSetField()' - Set the value of an HTTP header.
+ */
+
+void
+httpSetField(http_t *http, /* I - Connection to server */
+ http_field_t field, /* I - Field index */
+ const char *value) /* I - Value */
+{
+ DEBUG_printf(("httpSetField(http=%p, field=%d(%s), value=\"%s\")", http,
+ field, http_fields[field], value));
+
+ if (http == NULL ||
+ field < HTTP_FIELD_ACCEPT_LANGUAGE ||
+ field >= HTTP_FIELD_MAX ||
+ value == NULL)
+ return;
+
+ switch (field)
+ {
+ case HTTP_FIELD_ACCEPT_ENCODING :
+ if (http->accept_encoding)
+ _cupsStrFree(http->accept_encoding);
+
+ http->accept_encoding = _cupsStrAlloc(value);
+ break;
+
+ case HTTP_FIELD_ALLOW :
+ if (http->allow)
+ _cupsStrFree(http->allow);
+
+ http->allow = _cupsStrAlloc(value);
+ break;
+
+ case HTTP_FIELD_SERVER :
+ if (http->server)
+ _cupsStrFree(http->server);
+
+ http->server = _cupsStrAlloc(value);
+ break;
+
+ default :
+ strlcpy(http->fields[field], value, HTTP_MAX_VALUE);
+ break;
+ }
+
+ if (field == HTTP_FIELD_AUTHORIZATION)
+ {
+ /*
+ * Special case for Authorization: as its contents can be
+ * longer than HTTP_MAX_VALUE
+ */
+
+ if (http->field_authorization)
+ free(http->field_authorization);
+
+ http->field_authorization = strdup(value);
+ }
+ else if (field == HTTP_FIELD_HOST)
+ {
+ /*
+ * Special-case for Host: as we don't want a trailing "." on the hostname and
+ * need to bracket IPv6 numeric addresses.
+ */
+
+ char *ptr = strchr(value, ':');
+
+ if (value[0] != '[' && ptr && strchr(ptr + 1, ':'))
+ {
+ /*
+ * Bracket IPv6 numeric addresses...
+ *
+ * This is slightly inefficient (basically copying twice), but is an edge
+ * case and not worth optimizing...
+ */
+
+ snprintf(http->fields[HTTP_FIELD_HOST],
+ sizeof(http->fields[HTTP_FIELD_HOST]), "[%s]", value);
+ }
+ else
+ {
+ /*
+ * Check for a trailing dot on the hostname...
+ */
+
+ ptr = http->fields[HTTP_FIELD_HOST];
+
+ if (*ptr)
+ {
+ ptr += strlen(ptr) - 1;
+
+ if (*ptr == '.')
+ *ptr = '\0';
+ }
+ }
+ }
+#ifdef HAVE_LIBZ
+ else if (field == HTTP_FIELD_CONTENT_ENCODING &&
+ http->data_encoding != HTTP_ENCODING_FIELDS)
+ {
+ DEBUG_puts("1httpSetField: Calling http_content_coding_start.");
+ http_content_coding_start(http, value);
+ }
+#endif /* HAVE_LIBZ */
+}
+
+
+/*
+ * 'httpSetLength()' - Set the content-length and content-encoding.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+void
+httpSetLength(http_t *http, /* I - Connection to server */
+ size_t length) /* I - Length (0 for chunked) */
+{
+ DEBUG_printf(("httpSetLength(http=%p, length=" CUPS_LLFMT ")", http,
+ CUPS_LLCAST length));
+
+ if (!http)
+ return;
+
+ if (!length)
+ {
+ strlcpy(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked",
+ HTTP_MAX_VALUE);
+ http->fields[HTTP_FIELD_CONTENT_LENGTH][0] = '\0';
+ }
+ else
+ {
+ http->fields[HTTP_FIELD_TRANSFER_ENCODING][0] = '\0';
+ snprintf(http->fields[HTTP_FIELD_CONTENT_LENGTH], HTTP_MAX_VALUE,
+ CUPS_LLFMT, CUPS_LLCAST length);
+ }
+}
+
+
+/*
+ * 'httpSetTimeout()' - Set read/write timeouts and an optional callback.
+ *
+ * The optional timeout callback receives both the HTTP connection and a user
+ * data pointer and must return 1 to continue or 0 to error (time) out.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+void
+httpSetTimeout(
+ http_t *http, /* I - Connection to server */
+ double timeout, /* I - Number of seconds for timeout,
+ must be greater than 0 */
+ http_timeout_cb_t cb, /* I - Callback function or NULL */
+ void *user_data) /* I - User data pointer */
+{
+ if (!http || timeout <= 0.0)
+ return;
+
+ http->timeout_cb = cb;
+ http->timeout_data = user_data;
+ http->timeout_value = timeout;
+
+ if (http->fd >= 0)
+ http_set_timeout(http->fd, timeout);
+
+ http_set_wait(http);
+}
+
+
+/*
+ * 'httpTrace()' - Send an TRACE request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpTrace(http_t *http, /* I - Connection to server */
+ const char *uri) /* I - URI for trace */
+{
+ return (http_send(http, HTTP_STATE_TRACE, uri));
+}
+
+
+/*
+ * '_httpUpdate()' - Update the current HTTP status for incoming data.
+ *
+ * Note: Unlike httpUpdate(), this function does not flush pending write data
+ * and only retrieves a single status line from the HTTP connection.
+ */
+
+int /* O - 1 to continue, 0 to stop */
+_httpUpdate(http_t *http, /* I - Connection to server */
+ http_status_t *status) /* O - Current HTTP status */
+{
+ char line[32768], /* Line from connection... */
+ *value; /* Pointer to value on line */
+ http_field_t field; /* Field index */
+ int major, minor; /* HTTP version numbers */
+
+
+ DEBUG_printf(("_httpUpdate(http=%p, status=%p), state=%s", http, status,
+ http_state_string(http->state)));
+
+ /*
+ * Grab a single line from the connection...
+ */
+
+ if (!httpGets(line, sizeof(line), http))
+ {
+ *status = HTTP_STATUS_ERROR;
+ return (0);
+ }
+
+ DEBUG_printf(("2_httpUpdate: Got \"%s\"", line));
+
+ if (line[0] == '\0')
+ {
+ /*
+ * Blank line means the start of the data section (if any). Return
+ * the result code, too...
+ *
+ * If we get status 100 (HTTP_STATUS_CONTINUE), then we *don't* change
+ * states. Instead, we just return HTTP_STATUS_CONTINUE to the caller and
+ * keep on tryin'...
+ */
+
+ if (http->status == HTTP_STATUS_CONTINUE)
+ {
+ *status = http->status;
+ return (0);
+ }
+
+ if (http->status < HTTP_STATUS_BAD_REQUEST)
+ http->digest_tries = 0;
+
+#ifdef HAVE_SSL
+ if (http->status == HTTP_STATUS_SWITCHING_PROTOCOLS && !http->tls)
+ {
+ if (http_setup_ssl(http) != 0)
+ {
+# ifdef WIN32
+ closesocket(http->fd);
+# else
+ close(http->fd);
+# endif /* WIN32 */
+
+ *status = http->status = HTTP_STATUS_ERROR;
+ return (0);
+ }
+
+ *status = HTTP_STATUS_CONTINUE;
+ return (0);
+ }
+#endif /* HAVE_SSL */
+
+ if (http_set_length(http) < 0)
+ {
+ DEBUG_puts("1_httpUpdate: Bad Content-Length.");
+ http->error = EINVAL;
+ http->status = *status = HTTP_STATUS_ERROR;
+ return (0);
+ }
+
+ switch (http->state)
+ {
+ case HTTP_STATE_GET :
+ case HTTP_STATE_POST :
+ case HTTP_STATE_POST_RECV :
+ case HTTP_STATE_PUT :
+ http->state ++;
+
+ DEBUG_printf(("1_httpUpdate: Set state to %s.",
+ http_state_string(http->state)));
+
+ case HTTP_STATE_POST_SEND :
+ case HTTP_STATE_HEAD :
+ break;
+
+ default :
+ http->state = HTTP_STATE_WAITING;
+
+ DEBUG_puts("1_httpUpdate: Reset state to HTTP_STATE_WAITING.");
+ break;
+ }
+
+#ifdef HAVE_LIBZ
+ DEBUG_puts("1_httpUpdate: Calling http_content_coding_start.");
+ http_content_coding_start(http,
+ httpGetField(http, HTTP_FIELD_CONTENT_ENCODING));
+#endif /* HAVE_LIBZ */
+
+ *status = http->status;
+ return (0);
+ }
+ else if (!strncmp(line, "HTTP/", 5))
+ {
+ /*
+ * Got the beginning of a response...
+ */
+
+ int intstatus; /* Status value as an integer */
+
+ if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &intstatus) != 3)
+ {
+ *status = http->status = HTTP_STATUS_ERROR;
+ return (0);
+ }
+
+ httpClearFields(http);
+
+ http->version = (http_version_t)(major * 100 + minor);
+ *status = http->status = (http_status_t)intstatus;
+ }
+ else if ((value = strchr(line, ':')) != NULL)
+ {
+ /*
+ * Got a value...
+ */
+
+ *value++ = '\0';
+ while (_cups_isspace(*value))
+ value ++;
+
+ DEBUG_printf(("1_httpUpdate: Header %s: %s", line, value));
+
+ /*
+ * Be tolerants of servers that send unknown attribute fields...
+ */
+
+ if (!_cups_strcasecmp(line, "expect"))
+ {
+ /*
+ * "Expect: 100-continue" or similar...
+ */
+
+ http->expect = (http_status_t)atoi(value);
+ }
+ else if (!_cups_strcasecmp(line, "cookie"))
+ {
+ /*
+ * "Cookie: name=value[; name=value ...]" - replaces previous cookies...
+ */
+
+ httpSetCookie(http, value);
+ }
+ else if ((field = http_field(line)) != HTTP_FIELD_UNKNOWN)
+ httpSetField(http, field, value);
+#ifdef DEBUG
+ else
+ DEBUG_printf(("1_httpUpdate: unknown field %s seen!", line));
+#endif /* DEBUG */
+ }
+ else
+ {
+ DEBUG_printf(("1_httpUpdate: Bad response line \"%s\"!", line));
+ http->error = EINVAL;
+ http->status = *status = HTTP_STATUS_ERROR;
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'httpUpdate()' - Update the current HTTP state for incoming data.
+ */
+
+http_status_t /* O - HTTP status */
+httpUpdate(http_t *http) /* I - Connection to server */
+{
+ http_status_t status; /* Request status */
+
+
+ DEBUG_printf(("httpUpdate(http=%p), state=%s", http,
+ http_state_string(http->state)));
+
+ /*
+ * Flush pending data, if any...
+ */
+
+ if (http->wused)
+ {
+ DEBUG_puts("2httpUpdate: flushing buffer...");
+
+ if (httpFlushWrite(http) < 0)
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * If we haven't issued any commands, then there is nothing to "update"...
+ */
+
+ if (http->state == HTTP_STATE_WAITING)
+ return (HTTP_STATUS_CONTINUE);
+
+ /*
+ * Grab all of the lines we can from the connection...
+ */
+
+ while (_httpUpdate(http, &status));
+
+ /*
+ * See if there was an error...
+ */
+
+ if (http->error == EPIPE && http->status > HTTP_STATUS_CONTINUE)
+ {
+ DEBUG_printf(("1httpUpdate: Returning status %d...", http->status));
+ return (http->status);
+ }
+
+ if (http->error)
+ {
+ DEBUG_printf(("1httpUpdate: socket error %d - %s", http->error,
+ strerror(http->error)));
+ http->status = HTTP_STATUS_ERROR;
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Return the current status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * '_httpWait()' - Wait for data available on a connection (no flush).
+ */
+
+int /* O - 1 if data is available, 0 otherwise */
+_httpWait(http_t *http, /* I - Connection to server */
+ int msec, /* I - Milliseconds to wait */
+ int usessl) /* I - Use SSL context? */
+{
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Polled file descriptor */
+#else
+ fd_set input_set; /* select() input set */
+ struct timeval timeout; /* Timeout */
+#endif /* HAVE_POLL */
+ int nfds; /* Result from select()/poll() */
+
+
+ DEBUG_printf(("4_httpWait(http=%p, msec=%d, usessl=%d)", http, msec, usessl));
+
+ if (http->fd < 0)
+ {
+ DEBUG_printf(("5_httpWait: Returning 0 since fd=%d", http->fd));
+ return (0);
+ }
+
+ /*
+ * Check the SSL/TLS buffers for data first...
+ */
+
+#ifdef HAVE_SSL
+ if (http->tls && usessl)
+ {
+# ifdef HAVE_LIBSSL
+ if (SSL_pending(http->tls))
+ {
+ DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+ return (1);
+ }
+
+# elif defined(HAVE_GNUTLS)
+ if (gnutls_record_check_pending(http->tls))
+ {
+ DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+ return (1);
+ }
+
+# elif defined(HAVE_CDSASSL)
+ size_t bytes; /* Bytes that are available */
+
+ if (!SSLGetBufferedReadSize(http->tls, &bytes) &&
+ bytes > 0)
+ {
+ DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+ return (1);
+ }
+# endif /* HAVE_LIBSSL */
+ }
+#endif /* HAVE_SSL */
+
+ /*
+ * Then try doing a select() or poll() to poll the socket...
+ */
+
+#ifdef HAVE_POLL
+ pfd.fd = http->fd;
+ pfd.events = POLLIN;
+
+ do
+ {
+ nfds = poll(&pfd, 1, msec);
+ }
+ while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+
+#else
+ do
+ {
+ FD_ZERO(&input_set);
+ FD_SET(http->fd, &input_set);
+
+ DEBUG_printf(("6_httpWait: msec=%d, http->fd=%d", msec, http->fd));
+
+ if (msec >= 0)
+ {
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+
+ nfds = select(http->fd + 1, &input_set, NULL, NULL, &timeout);
+ }
+ else
+ nfds = select(http->fd + 1, &input_set, NULL, NULL, NULL);
+
+ DEBUG_printf(("6_httpWait: select() returned %d...", nfds));
+ }
+# ifdef WIN32
+ while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
+ WSAGetLastError() == WSAEWOULDBLOCK));
+# else
+ while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+# endif /* WIN32 */
+#endif /* HAVE_POLL */
+
+ DEBUG_printf(("5_httpWait: returning with nfds=%d, errno=%d...", nfds,
+ errno));
+
+ return (nfds > 0);
+}
+
+
+/*
+ * 'httpWait()' - Wait for data available on a connection.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+int /* O - 1 if data is available, 0 otherwise */
+httpWait(http_t *http, /* I - Connection to server */
+ int msec) /* I - Milliseconds to wait */
+{
+ /*
+ * First see if there is data in the buffer...
+ */
+
+ DEBUG_printf(("2httpWait(http=%p, msec=%d)", http, msec));
+
+ if (http == NULL)
+ return (0);
+
+ if (http->used)
+ {
+ DEBUG_puts("3httpWait: Returning 1 since there is buffered data ready.");
+ return (1);
+ }
+
+#ifdef HAVE_LIBZ
+ if (http->coding >= _HTTP_CODING_GUNZIP && http->stream.avail_in > 0)
+ {
+ DEBUG_puts("3httpWait: Returning 1 since there is buffered data ready.");
+ return (1);
+ }
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Flush pending data, if any...
+ */
+
+ if (http->wused)
+ {
+ DEBUG_puts("3httpWait: Flushing write buffer.");
+
+ if (httpFlushWrite(http) < 0)
+ return (0);
+ }
+
+ /*
+ * If not, check the SSL/TLS buffers and do a select() on the connection...
+ */
+
+ return (_httpWait(http, msec, 1));
+}
+
+
+/*
+ * 'httpWrite()' - Write data to a HTTP connection.
+ *
+ * This function is deprecated. Use the httpWrite2() function which can
+ * write more than 2GB of data.
+ *
+ * @deprecated@
+ */
+
+int /* O - Number of bytes written */
+httpWrite(http_t *http, /* I - Connection to server */
+ const char *buffer, /* I - Buffer for data */
+ int length) /* I - Number of bytes to write */
+{
+ return ((int)httpWrite2(http, buffer, length));
+}
+
+
+/*
+ * 'httpWrite2()' - Write data to a HTTP connection.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ssize_t /* O - Number of bytes written */
+httpWrite2(http_t *http, /* I - Connection to server */
+ const char *buffer, /* I - Buffer for data */
+ size_t length) /* I - Number of bytes to write */
+{
+ ssize_t bytes; /* Bytes written */
+
+
+ DEBUG_printf(("httpWrite2(http=%p, buffer=%p, length=" CUPS_LLFMT ")", http,
+ buffer, CUPS_LLCAST length));
+
+ /*
+ * Range check input...
+ */
+
+ if (!http || !buffer)
+ {
+ DEBUG_puts("1httpWrite2: Returning -1 due to bad input.");
+ return (-1);
+ }
+
+ /*
+ * Mark activity on the connection...
+ */
+
+ http->activity = time(NULL);
+
+ /*
+ * Buffer small writes for better performance...
+ */
+
+#ifdef HAVE_LIBZ
+ if (http->coding == _HTTP_CODING_GZIP || http->coding == _HTTP_CODING_DEFLATE)
+ {
+ DEBUG_printf(("1httpWrite2: http->coding=%d", http->coding));
+
+ if (length == 0)
+ {
+ http_content_coding_finish(http);
+ bytes = 0;
+ }
+ else
+ {
+ http->stream.next_in = (Bytef *)buffer;
+ http->stream.avail_in = length;
+ http->stream.next_out = (Bytef *)http->wbuffer + http->wused;
+ http->stream.avail_out = sizeof(http->wbuffer) - http->wused;
+
+ while (deflate(&(http->stream), Z_NO_FLUSH) == Z_OK)
+ {
+ http->wused = sizeof(http->wbuffer) - http->stream.avail_out;
+
+ if (http->stream.avail_out == 0)
+ {
+ if (httpFlushWrite(http) < 0)
+ {
+ DEBUG_puts("1httpWrite2: Unable to flush, returning -1.");
+ return (-1);
+ }
+
+ http->stream.next_out = (Bytef *)http->wbuffer;
+ http->stream.avail_out = sizeof(http->wbuffer);
+ }
+ }
+
+ http->wused = sizeof(http->wbuffer) - http->stream.avail_out;
+ bytes = length;
+ }
+ }
+ else
+#endif /* HAVE_LIBZ */
+ if (length > 0)
+ {
+ if (http->wused && (length + http->wused) > sizeof(http->wbuffer))
+ {
+ DEBUG_printf(("2httpWrite2: Flushing buffer (wused=%d, length="
+ CUPS_LLFMT ")", http->wused, CUPS_LLCAST length));
+
+ httpFlushWrite(http);
+ }
+
+ if ((length + http->wused) <= sizeof(http->wbuffer) &&
+ length < sizeof(http->wbuffer))
+ {
+ /*
+ * Write to buffer...
+ */
+
+ DEBUG_printf(("2httpWrite2: Copying " CUPS_LLFMT " bytes to wbuffer...",
+ CUPS_LLCAST length));
+
+ memcpy(http->wbuffer + http->wused, buffer, length);
+ http->wused += (int)length;
+ bytes = (ssize_t)length;
+ }
+ else
+ {
+ /*
+ * Otherwise write the data directly...
+ */
+
+ DEBUG_printf(("2httpWrite2: Writing " CUPS_LLFMT " bytes to socket...",
+ CUPS_LLCAST length));
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ bytes = (ssize_t)http_write_chunk(http, buffer, (int)length);
+ else
+ bytes = (ssize_t)http_write(http, buffer, (int)length);
+
+ DEBUG_printf(("2httpWrite2: Wrote " CUPS_LLFMT " bytes...",
+ CUPS_LLCAST bytes));
+ }
+
+ if (http->data_encoding == HTTP_ENCODING_LENGTH)
+ http->data_remaining -= bytes;
+ }
+ else
+ bytes = 0;
+
+ /*
+ * Handle end-of-request processing...
+ */
+
+ if ((http->data_encoding == HTTP_ENCODING_CHUNKED && length == 0) ||
+ (http->data_encoding == HTTP_ENCODING_LENGTH && http->data_remaining == 0))
+ {
+ /*
+ * Finished with the transfer; unless we are sending POST or PUT
+ * data, go idle...
+ */
+
+#ifdef HAVE_LIBZ
+ if (http->coding == _HTTP_CODING_GZIP || http->coding == _HTTP_CODING_DEFLATE)
+ http_content_coding_finish(http);
+#endif /* HAVE_LIBZ */
+
+ if (http->wused)
+ {
+ if (httpFlushWrite(http) < 0)
+ return (-1);
+ }
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ {
+ /*
+ * Send a 0-length chunk at the end of the request...
+ */
+
+ http_write(http, "0\r\n\r\n", 5);
+
+ /*
+ * Reset the data state...
+ */
+
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+ http->data_remaining = 0;
+ }
+
+ if (http->state == HTTP_STATE_POST_RECV)
+ http->state ++;
+ else if (http->state == HTTP_STATE_POST_SEND)
+ http->state = HTTP_STATE_WAITING;
+ else
+ http->state = HTTP_STATE_STATUS;
+
+ DEBUG_printf(("2httpWrite2: Changed state to %s.",
+ http_state_string(http->state)));
+ }
+
+ DEBUG_printf(("1httpWrite2: Returning " CUPS_LLFMT ".", CUPS_LLCAST bytes));
+
+ return (bytes);
+}
+
+
+#if defined(HAVE_SSL) && defined(HAVE_CDSASSL)
+/*
+ * '_httpWriteCDSA()' - Write function for the CDSA library.
+ */
+
+OSStatus /* O - -1 on error, 0 on success */
+_httpWriteCDSA(
+ SSLConnectionRef connection, /* I - SSL/TLS connection */
+ const void *data, /* I - Data buffer */
+ size_t *dataLength) /* IO - Number of bytes */
+{
+ OSStatus result; /* Return value */
+ ssize_t bytes; /* Number of bytes read */
+ http_t *http; /* HTTP connection */
+
+
+ http = (http_t *)connection;
+
+ do
+ {
+ bytes = write(http->fd, data, *dataLength);
+ }
+ while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (bytes == *dataLength)
+ {
+ result = 0;
+ }
+ else if (bytes >= 0)
+ {
+ *dataLength = bytes;
+ result = errSSLWouldBlock;
+ }
+ else
+ {
+ *dataLength = 0;
+
+ if (errno == EAGAIN)
+ result = errSSLWouldBlock;
+ else
+ result = errSSLClosedAbort;
+ }
+
+ return (result);
+}
+#endif /* HAVE_SSL && HAVE_CDSASSL */
+
+
+#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
+/*
+ * '_httpWriteGNUTLS()' - Write function for the GNU TLS library.
+ */
+
+ssize_t /* O - Number of bytes written or -1 on error */
+_httpWriteGNUTLS(
+ gnutls_transport_ptr_t ptr, /* I - Connection to server */
+ const void *data, /* I - Data buffer */
+ size_t length) /* I - Number of bytes to write */
+{
+ ssize_t bytes; /* Bytes written */
+
+
+ DEBUG_printf(("6_httpWriteGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data,
+ (int)length));
+#ifdef DEBUG
+ http_debug_hex("_httpWriteGNUTLS", data, (int)length);
+#endif /* DEBUG */
+
+ bytes = send(((http_t *)ptr)->fd, data, length, 0);
+ DEBUG_printf(("_httpWriteGNUTLS: bytes=%d", (int)bytes));
+
+ return (bytes);
+}
+#endif /* HAVE_SSL && HAVE_GNUTLS */
+
+
+/*
+ * 'httpWriteResponse()' - Write a HTTP response to a client connection.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 0 on success, -1 on error */
+httpWriteResponse(http_t *http, /* I - HTTP connection */
+ http_status_t status) /* I - Status code */
+{
+ http_encoding_t old_encoding; /* Old data_encoding value */
+ off_t old_remaining; /* Old data_remaining value */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("httpWriteResponse(http=%p, status=%d)", http, status));
+
+ if (!http || status < HTTP_STATUS_CONTINUE)
+ {
+ DEBUG_puts("1httpWriteResponse: Bad input.");
+ return (-1);
+ }
+
+ /*
+ * Set the various standard fields if they aren't already...
+ */
+
+ if (!http->fields[HTTP_FIELD_DATE][0])
+ httpSetField(http, HTTP_FIELD_DATE, httpGetDateString(time(NULL)));
+
+ if (status >= HTTP_STATUS_BAD_REQUEST && http->keep_alive)
+ {
+ http->keep_alive = 0;
+ httpSetField(http, HTTP_FIELD_KEEP_ALIVE, "");
+ }
+
+ if (http->version == HTTP_VERSION_1_1)
+ {
+ if (!http->fields[HTTP_FIELD_CONNECTION][0])
+ {
+ if (http->keep_alive)
+ httpSetField(http, HTTP_FIELD_CONNECTION, "Keep-Alive");
+ else
+ httpSetField(http, HTTP_FIELD_CONNECTION, "close");
+ }
+
+ if (http->keep_alive && !http->fields[HTTP_FIELD_KEEP_ALIVE][0])
+ httpSetField(http, HTTP_FIELD_KEEP_ALIVE, "timeout=10");
+ }
+
+#ifdef HAVE_SSL
+ if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ if (!http->fields[HTTP_FIELD_CONNECTION][0])
+ httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade");
+
+ if (!http->fields[HTTP_FIELD_UPGRADE][0])
+ httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.2,TLS/1.1,TLS/1.0");
+ }
+#endif /* HAVE_SSL */
+
+ if (!http->server)
+ httpSetField(http, HTTP_FIELD_SERVER,
+ http->default_server ? http->default_server : CUPS_MINIMAL);
+
+ /*
+ * Set the Accept-Encoding field if it isn't already...
+ */
+
+ if (!http->accept_encoding)
+ httpSetField(http, HTTP_FIELD_ACCEPT_ENCODING,
+ http->default_accept_encoding ? http->default_accept_encoding :
+#ifdef HAVE_LIBZ
+ "gzip, deflate, identity");
+#else
+ "identity");
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Send the response header...
+ */
+
+ old_encoding = http->data_encoding;
+ old_remaining = http->data_remaining;
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+
+ if (httpPrintf(http, "HTTP/%d.%d %d %s\r\n", http->version / 100,
+ http->version % 100, (int)status, httpStatus(status)) < 0)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (status != HTTP_STATUS_CONTINUE)
+ {
+ /*
+ * 100 Continue doesn't have the rest of the response headers...
+ */
+
+ int i; /* Looping var */
+ const char *value; /* Field value */
+
+ for (i = 0; i < HTTP_FIELD_MAX; i ++)
+ {
+ if ((value = httpGetField(http, i)) != NULL && *value)
+ {
+ if (httpPrintf(http, "%s: %s\r\n", http_fields[i], value) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+ }
+
+ if (http->cookie)
+ {
+ if (httpPrintf(http, "Set-Cookie: %s path=/%s\r\n", http->cookie,
+ http->tls ? " secure" : "") < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+ }
+
+ if (httpWrite2(http, "\r\n", 2) < 2)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (httpFlushWrite(http) < 0)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (status == HTTP_STATUS_CONTINUE)
+ {
+ /*
+ * Restore the old data_encoding and data_length values...
+ */
+
+ http->data_encoding = old_encoding;
+ http->data_remaining = old_remaining;
+
+ if (old_remaining <= INT_MAX)
+ http->_data_remaining = (int)old_remaining;
+ else
+ http->_data_remaining = INT_MAX;
+ }
+ else if (http->state == HTTP_STATE_OPTIONS ||
+ http->state == HTTP_STATE_HEAD ||
+ http->state == HTTP_STATE_PUT ||
+ http->state == HTTP_STATE_TRACE ||
+ http->state == HTTP_STATE_CONNECT ||
+ http->state == HTTP_STATE_STATUS)
+ {
+ DEBUG_printf(("1httpWriteResponse: Resetting state to HTTP_STATE_WAITING, "
+ "was %s.", http_state_string(http->state)));
+ http->state = HTTP_STATE_WAITING;
+ }
+ else
+ {
+ /*
+ * Force data_encoding and data_length to be set according to the response
+ * headers...
+ */
+
+ http_set_length(http);
+
+#ifdef HAVE_LIBZ
+ /*
+ * Then start any content encoding...
+ */
+
+ DEBUG_puts("1httpWriteResponse: Calling http_content_coding_start.");
+ http_content_coding_start(http,
+ httpGetField(http, HTTP_FIELD_CONTENT_ENCODING));
+#endif /* HAVE_LIBZ */
+ }
+
+ return (0);
+}
+
+
+#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/*
+ * 'http_bio_ctrl()' - Control the HTTP connection.
+ */
+
+static long /* O - Result/data */
+http_bio_ctrl(BIO *h, /* I - BIO data */
+ int cmd, /* I - Control command */
+ long arg1, /* I - First argument */
+ void *arg2) /* I - Second argument */
+{
+ switch (cmd)
+ {
+ default :
+ return (0);
+
+ case BIO_CTRL_RESET :
+ h->ptr = NULL;
+ return (0);
+
+ case BIO_C_SET_FILE_PTR :
+ h->ptr = arg2;
+ h->init = 1;
+ return (1);
+
+ case BIO_C_GET_FILE_PTR :
+ if (arg2)
+ {
+ *((void **)arg2) = h->ptr;
+ return (1);
+ }
+ else
+ return (0);
+
+ case BIO_CTRL_DUP :
+ case BIO_CTRL_FLUSH :
+ return (1);
+ }
+}
+
+
+/*
+ * 'http_bio_free()' - Free OpenSSL data.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+http_bio_free(BIO *h) /* I - BIO data */
+{
+ if (!h)
+ return (0);
+
+ if (h->shutdown)
+ {
+ h->init = 0;
+ h->flags = 0;
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'http_bio_new()' - Initialize an OpenSSL BIO structure.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+http_bio_new(BIO *h) /* I - BIO data */
+{
+ if (!h)
+ return (0);
+
+ h->init = 0;
+ h->num = 0;
+ h->ptr = NULL;
+ h->flags = 0;
+
+ return (1);
+}
+
+
+/*
+ * 'http_bio_puts()' - Send a string for OpenSSL.
+ */
+
+static int /* O - Bytes written */
+http_bio_puts(BIO *h, /* I - BIO data */
+ const char *str) /* I - String to write */
+{
+#ifdef WIN32
+ return (send(((http_t *)h->ptr)->fd, str, (int)strlen(str), 0));
+#else
+ return (send(((http_t *)h->ptr)->fd, str, strlen(str), 0));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'http_bio_read()' - Read data for OpenSSL.
+ */
+
+static int /* O - Bytes read */
+http_bio_read(BIO *h, /* I - BIO data */
+ char *buf, /* I - Buffer */
+ int size) /* I - Number of bytes to read */
+{
+ http_t *http; /* HTTP connection */
+
+
+ http = (http_t *)h->ptr;
+
+ if (!http->blocking)
+ {
+ /*
+ * Make sure we have data before we read...
+ */
+
+ while (!_httpWait(http, http->wait_value, 0))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+#ifdef WIN32
+ http->error = WSAETIMEDOUT;
+#else
+ http->error = ETIMEDOUT;
+#endif /* WIN32 */
+
+ return (-1);
+ }
+ }
+
+ return (recv(http->fd, buf, size, 0));
+}
+
+
+/*
+ * 'http_bio_write()' - Write data for OpenSSL.
+ */
+
+static int /* O - Bytes written */
+http_bio_write(BIO *h, /* I - BIO data */
+ const char *buf, /* I - Buffer to write */
+ int num) /* I - Number of bytes to write */
+{
+ return (send(((http_t *)h->ptr)->fd, buf, num, 0));
+}
+#endif /* HAVE_SSL && HAVE_LIBSSL */
+
+
+#ifdef HAVE_LIBZ
+/*
+ * 'http_content_coding_finish()' - Finish doing any content encoding.
+ */
+
+static void
+http_content_coding_finish(
+ http_t *http) /* I - HTTP connection */
+{
+ int zerr; /* Compression status */
+ Byte dummy[1]; /* Dummy read buffer */
+
+
+ switch (http->coding)
+ {
+ case _HTTP_CODING_DEFLATE :
+ case _HTTP_CODING_GZIP :
+ http->stream.next_in = dummy;
+ http->stream.avail_in = 0;
+
+ do
+ {
+ http->stream.next_out = (Bytef *)http->wbuffer + http->wused;
+ http->stream.avail_out = sizeof(http->wbuffer) - http->wused;
+
+ zerr = deflate(&(http->stream), Z_FINISH);
+
+ http->wused = sizeof(http->wbuffer) - http->stream.avail_out;
+ if (http->wused == sizeof(http->wbuffer))
+ httpFlushWrite(http);
+ }
+ while (zerr == Z_OK);
+
+ deflateEnd(&(http->stream));
+
+ if (http->wused)
+ httpFlushWrite(http);
+ break;
+
+ case _HTTP_CODING_INFLATE :
+ case _HTTP_CODING_GUNZIP :
+ inflateEnd(&(http->stream));
+ free(http->dbuffer);
+ http->dbuffer = NULL;
+ break;
+
+ default :
+ break;
+ }
+
+ http->coding = _HTTP_CODING_IDENTITY;
+}
+
+
+/*
+ * 'http_content_coding_start()' - Start doing content encoding.
+ */
+
+static void
+http_content_coding_start(
+ http_t *http, /* I - HTTP connection */
+ const char *value) /* I - Value of Content-Encoding */
+{
+ int zerr; /* Error/status */
+ _http_coding_t coding; /* Content coding value */
+
+
+ DEBUG_printf(("http_content_coding_start(http=%p, value=\"%s\")", http,
+ value));
+
+ if (http->coding != _HTTP_CODING_IDENTITY)
+ {
+ DEBUG_printf(("1http_content_coding_start: http->coding already %d.",
+ http->coding));
+ return;
+ }
+ else if (!strcmp(value, "x-gzip") || !strcmp(value, "gzip"))
+ {
+ if (http->state == HTTP_STATE_GET_SEND ||
+ http->state == HTTP_STATE_POST_SEND)
+ coding = http->mode == _HTTP_MODE_SERVER ? _HTTP_CODING_GZIP :
+ _HTTP_CODING_GUNZIP;
+ else if (http->state == HTTP_STATE_POST_RECV ||
+ http->state == HTTP_STATE_PUT_RECV)
+ coding = http->mode == _HTTP_MODE_CLIENT ? _HTTP_CODING_GZIP :
+ _HTTP_CODING_GUNZIP;
+ else
+ {
+ DEBUG_puts("1http_content_coding_start: Not doing content coding.");
+ return;
+ }
+ }
+ else if (!strcmp(value, "x-deflate") || !strcmp(value, "deflate"))
+ {
+ if (http->state == HTTP_STATE_GET_SEND ||
+ http->state == HTTP_STATE_POST_SEND)
+ coding = http->mode == _HTTP_MODE_SERVER ? _HTTP_CODING_DEFLATE :
+ _HTTP_CODING_INFLATE;
+ else if (http->state == HTTP_STATE_POST_RECV ||
+ http->state == HTTP_STATE_PUT_RECV)
+ coding = http->mode == _HTTP_MODE_CLIENT ? _HTTP_CODING_DEFLATE :
+ _HTTP_CODING_INFLATE;
+ else
+ {
+ DEBUG_puts("1http_content_coding_start: Not doing content coding.");
+ return;
+ }
+ }
+ else
+ {
+ DEBUG_puts("1http_content_coding_start: Not doing content coding.");
+ return;
+ }
+
+ memset(&(http->stream), 0, sizeof(http->stream));
+
+ switch (coding)
+ {
+ case _HTTP_CODING_DEFLATE :
+ case _HTTP_CODING_GZIP :
+ if (http->wused)
+ httpFlushWrite(http);
+
+ /*
+ * Window size for compression is 11 bits - optimal based on PWG Raster
+ * sample files on pwg.org. -11 is raw deflate, 27 is gzip, per ZLIB
+ * documentation.
+ */
+
+ if ((zerr = deflateInit2(&(http->stream), Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ coding == _HTTP_CODING_DEFLATE ? -11 : 27, 7,
+ Z_DEFAULT_STRATEGY)) < Z_OK)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ http->error = zerr == Z_MEM_ERROR ? ENOMEM : EINVAL;
+ return;
+ }
+ break;
+
+ case _HTTP_CODING_INFLATE :
+ case _HTTP_CODING_GUNZIP :
+ if ((http->dbuffer = malloc(HTTP_MAX_BUFFER)) == NULL)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ http->error = errno;
+ return;
+ }
+
+ /*
+ * Window size for decompression is up to 15 bits (maximum supported).
+ * -15 is raw inflate, 31 is gunzip, per ZLIB documentation.
+ */
+
+ if ((zerr = inflateInit2(&(http->stream),
+ coding == _HTTP_CODING_INFLATE ? -15 : 31))
+ < Z_OK)
+ {
+ free(http->dbuffer);
+ http->dbuffer = NULL;
+ http->status = HTTP_STATUS_ERROR;
+ http->error = zerr == Z_MEM_ERROR ? ENOMEM : EINVAL;
+ return;
+ }
+
+ http->stream.avail_in = 0;
+ http->stream.next_in = http->dbuffer;
+ break;
+
+ default :
+ break;
+ }
+
+ http->coding = coding;
+
+ DEBUG_printf(("1http_content_coding_start: http->coding now %d.",
+ http->coding));
+}
+#endif /* HAVE_LIBZ */
+
+
+/*
+ * 'http_create()' - Create an unconnected HTTP connection.
+ */
+
+static http_t * /* O - HTTP connection */
+http_create(
+ const char *host, /* I - Hostname */
+ int port, /* I - Port number */
+ http_addrlist_t *addrlist, /* I - Address list or NULL */
+ int family, /* I - Address family or AF_UNSPEC */
+ http_encryption_t encryption, /* I - Encryption to use */
+ int blocking, /* I - 1 for blocking mode */
+ _http_mode_t mode) /* I - _HTTP_MODE_CLIENT or _SERVER */
+{
+ http_t *http; /* New HTTP connection */
+ char service[255]; /* Service name */
+ http_addrlist_t *myaddrlist = NULL; /* My address list */
+
+
+ DEBUG_printf(("4http_create(host=\"%s\", port=%d, addrlist=%p, family=%d, "
+ "encryption=%d, blocking=%d, mode=%d)", host, port, addrlist,
+ family, encryption, blocking, mode));
+
+ if (!host && mode == _HTTP_MODE_CLIENT)
+ return (NULL);
+
+ httpInitialize();
+
+ /*
+ * Lookup the host...
+ */
+
+ if (addrlist)
+ {
+ myaddrlist = httpAddrCopyList(addrlist);
+ }
+ else
+ {
+ snprintf(service, sizeof(service), "%d", port);
+
+ myaddrlist = httpAddrGetList(host, family, service);
+ }
+
+ if (!myaddrlist)
+ return (NULL);
+
+ /*
+ * Allocate memory for the structure...
+ */
+
+ if ((http = calloc(sizeof(http_t), 1)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ httpAddrFreeList(addrlist);
+ return (NULL);
+ }
+
+ /*
+ * Initialize the HTTP data...
+ */
+
+ http->mode = mode;
+ http->activity = time(NULL);
+ http->addrlist = myaddrlist;
+ http->blocking = blocking;
+ http->fd = -1;
+#ifdef HAVE_GSSAPI
+ http->gssctx = GSS_C_NO_CONTEXT;
+ http->gssname = GSS_C_NO_NAME;
+#endif /* HAVE_GSSAPI */
+ http->status = HTTP_STATUS_CONTINUE;
+ http->version = HTTP_VERSION_1_1;
+
+ if (host)
+ strlcpy(http->hostname, host, sizeof(http->hostname));
+
+ if (port == 443) /* Always use encryption for https */
+ http->encryption = HTTP_ENCRYPTION_ALWAYS;
+ else
+ http->encryption = encryption;
+
+ http_set_wait(http);
+
+ /*
+ * Return the new structure...
+ */
+
+ return (http);
+}
+
+/* For OS X 10.8 and earlier */
+http_t *_httpCreate(const char *host, int port, http_addrlist_t *addrlist,
+ http_encryption_t encryption, int family)
+{ return (http_create(host, port, addrlist, family, encryption, 1,
+ _HTTP_MODE_CLIENT)); }
+
+
+#ifdef DEBUG
+/*
+ * 'http_debug_hex()' - Do a hex dump of a buffer.
+ */
+
+static void
+http_debug_hex(const char *prefix, /* I - Prefix for line */
+ const char *buffer, /* I - Buffer to dump */
+ int bytes) /* I - Bytes to dump */
+{
+ int i, j, /* Looping vars */
+ ch; /* Current character */
+ char line[255], /* Line buffer */
+ *start, /* Start of line after prefix */
+ *ptr; /* Pointer into line */
+
+
+ if (_cups_debug_fd < 0 || _cups_debug_level < 6)
+ return;
+
+ DEBUG_printf(("6%s: %d bytes:", prefix, bytes));
+
+ snprintf(line, sizeof(line), "6%s: ", prefix);
+ start = line + strlen(line);
+
+ for (i = 0; i < bytes; i += 16)
+ {
+ for (j = 0, ptr = start; j < 16 && (i + j) < bytes; j ++, ptr += 2)
+ sprintf(ptr, "%02X", buffer[i + j] & 255);
+
+ while (j < 16)
+ {
+ memcpy(ptr, " ", 3);
+ ptr += 2;
+ j ++;
+ }
+
+ memcpy(ptr, " ", 3);
+ ptr += 2;
+
+ for (j = 0; j < 16 && (i + j) < bytes; j ++)
+ {
+ ch = buffer[i + j] & 255;
+
+ if (ch < ' ' || ch >= 127)
+ ch = '.';
+
+ *ptr++ = ch;
+ }
+
+ *ptr = '\0';
+ DEBUG_puts(line);
+ }
+}
+#endif /* DEBUG */
+
+
+/*
+ * 'http_field()' - Return the field index for a field name.
+ */
+
+static http_field_t /* O - Field index */
+http_field(const char *name) /* I - String name */
+{
+ int i; /* Looping var */
+
+
+ for (i = 0; i < HTTP_FIELD_MAX; i ++)
+ if (_cups_strcasecmp(name, http_fields[i]) == 0)
+ return ((http_field_t)i);
+
+ return (HTTP_FIELD_UNKNOWN);
+}
+
+
+/*
+ * 'http_read()' - Read a buffer from a HTTP connection.
+ *
+ * This function does the low-level read from the socket, retrying and timing
+ * out as needed.
+ */
+
+static ssize_t /* O - Number of bytes read or -1 on error */
+http_read(http_t *http, /* I - Connection to server */
+ char *buffer, /* I - Buffer */
+ size_t length) /* I - Maximum bytes to read */
+{
+ ssize_t bytes; /* Bytes read */
+
+
+ DEBUG_printf(("http_read(http=%p, buffer=%p, length=" CUPS_LLFMT ")", http,
+ buffer, CUPS_LLCAST length));
+
+ if (!http->blocking)
+ {
+ while (!httpWait(http, http->wait_value))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ DEBUG_puts("2http_read: Timeout.");
+ return (0);
+ }
+ }
+
+ DEBUG_printf(("2http_read: Reading %d bytes into buffer.", (int)length));
+
+ do
+ {
+#ifdef HAVE_SSL
+ if (http->tls)
+ bytes = http_read_ssl(http, buffer, length);
+ else
+#endif /* HAVE_SSL */
+ bytes = recv(http->fd, buffer, length, 0);
+
+ if (bytes < 0)
+ {
+#ifdef WIN32
+ if (WSAGetLastError() != WSAEINTR)
+ {
+ http->error = WSAGetLastError();
+ return (-1);
+ }
+ else if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ if (!http->timeout_cb ||
+ !(*http->timeout_cb)(http, http->timeout_data))
+ {
+ http->error = WSAEWOULDBLOCK;
+ return (-1);
+ }
+ }
+#else
+ DEBUG_printf(("2http_read: %s", strerror(errno)));
+
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+ {
+ http->error = errno;
+ return (-1);
+ }
+ else if (!http->timeout_cb && errno != EAGAIN)
+ {
+ http->error = errno;
+ return (-1);
+ }
+ }
+ else if (errno != EINTR)
+ {
+ http->error = errno;
+ return (-1);
+ }
+#endif /* WIN32 */
+ }
+ }
+ while (bytes < 0);
+
+ DEBUG_printf(("2http_read: Read " CUPS_LLFMT " bytes into buffer.",
+ CUPS_LLCAST bytes));
+#ifdef DEBUG
+ if (bytes > 0)
+ http_debug_hex("http_read", buffer, (int)bytes);
+#endif /* DEBUG */
+
+ if (bytes < 0)
+ {
+#ifdef WIN32
+ if (WSAGetLastError() == WSAEINTR)
+ bytes = 0;
+ else
+ http->error = WSAGetLastError();
+#else
+ if (errno == EINTR || (errno == EAGAIN && !http->timeout_cb))
+ bytes = 0;
+ else
+ http->error = errno;
+#endif /* WIN32 */
+ }
+ else if (bytes == 0)
+ {
+ http->error = EPIPE;
+ return (0);
+ }
+
+ return (bytes);
+}
+
+
+/*
+ * 'http_read_buffered()' - Do a buffered read from a HTTP connection.
+ *
+ * This function reads data from the HTTP buffer or from the socket, as needed.
+ */
+
+static ssize_t /* O - Number of bytes read or -1 on error */
+http_read_buffered(http_t *http, /* I - Connection to server */
+ char *buffer, /* I - Buffer */
+ size_t length) /* I - Maximum bytes to read */
+{
+ ssize_t bytes; /* Bytes read */
+
+
+ DEBUG_printf(("http_read_buffered(http=%p, buffer=%p, length=" CUPS_LLFMT
+ ") used=%d",
+ http, buffer, CUPS_LLCAST length, http->used));
+
+ if (http->used > 0)
+ {
+ if (length > (size_t)http->used)
+ bytes = (size_t)http->used;
+ else
+ bytes = length;
+
+ DEBUG_printf(("2http_read: Grabbing %d bytes from input buffer.",
+ (int)bytes));
+
+ memcpy(buffer, http->buffer, bytes);
+ http->used -= (int)bytes;
+
+ if (http->used > 0)
+ memmove(http->buffer, http->buffer + bytes, http->used);
+ }
+ else
+ bytes = http_read(http, buffer, length);
+
+ return (bytes);
+}
+
+
+/*
+ * 'http_read_chunk()' - Read a chunk from a HTTP connection.
+ *
+ * This function reads and validates the chunk length, then does a buffered read
+ * returning the number of bytes placed in the buffer.
+ */
+
+static ssize_t /* O - Number of bytes read or -1 on error */
+http_read_chunk(http_t *http, /* I - Connection to server */
+ char *buffer, /* I - Buffer */
+ size_t length) /* I - Maximum bytes to read */
+{
+ DEBUG_printf(("http_read_chunk(http=%p, buffer=%p, length=" CUPS_LLFMT ")",
+ http, buffer, CUPS_LLCAST length));
+
+ if (http->data_remaining <= 0)
+ {
+ char len[32]; /* Length string */
+
+ if (!httpGets(len, sizeof(len), http))
+ {
+ DEBUG_puts("1http_read_chunk: Could not get chunk length.");
+ return (0);
+ }
+
+ if (!len[0])
+ {
+ DEBUG_puts("1http_read_chunk: Blank chunk length, trying again...");
+ if (!httpGets(len, sizeof(len), http))
+ {
+ DEBUG_puts("1http_read_chunk: Could not get chunk length.");
+ return (0);
+ }
+ }
+
+ http->data_remaining = strtoll(len, NULL, 16);
+
+ if (http->data_remaining < 0)
+ {
+ DEBUG_printf(("1http_read_chunk: Negative chunk length \"%s\" ("
+ CUPS_LLFMT ")", len, CUPS_LLCAST http->data_remaining));
+ return (0);
+ }
+
+ DEBUG_printf(("2http_read_chunk: Got chunk length \"%s\" (" CUPS_LLFMT ")",
+ len, CUPS_LLCAST http->data_remaining));
+
+ if (http->data_remaining == 0)
+ {
+ /*
+ * 0-length chunk, grab trailing blank line...
+ */
+
+ httpGets(len, sizeof(len), http);
+ }
+ }
+
+ DEBUG_printf(("2http_read_chunk: data_remaining=" CUPS_LLFMT,
+ CUPS_LLCAST http->data_remaining));
+
+ if (http->data_remaining <= 0)
+ return (0);
+ else if (length > (size_t)http->data_remaining)
+ length = (size_t)http->data_remaining;
+
+ return (http_read_buffered(http, buffer, length));
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_read_ssl()' - Read from a SSL/TLS connection.
+ */
+
+static int /* O - Bytes read */
+http_read_ssl(http_t *http, /* I - Connection to server */
+ char *buf, /* I - Buffer to store data */
+ int len) /* I - Length of buffer */
+{
+# if defined(HAVE_LIBSSL)
+ return (SSL_read((SSL *)(http->tls), buf, len));
+
+# elif defined(HAVE_GNUTLS)
+ ssize_t result; /* Return value */
+
+
+ result = gnutls_record_recv(http->tls, buf, len);
+
+ if (result < 0 && !errno)
+ {
+ /*
+ * Convert GNU TLS error to errno value...
+ */
+
+ switch (result)
+ {
+ case GNUTLS_E_INTERRUPTED :
+ errno = EINTR;
+ break;
+
+ case GNUTLS_E_AGAIN :
+ errno = EAGAIN;
+ break;
+
+ default :
+ errno = EPIPE;
+ break;
+ }
+
+ result = -1;
+ }
+
+ return ((int)result);
+
+# elif defined(HAVE_CDSASSL)
+ int result; /* Return value */
+ OSStatus error; /* Error info */
+ size_t processed; /* Number of bytes processed */
+
+
+ error = SSLRead(http->tls, buf, len, &processed);
+ DEBUG_printf(("6http_read_ssl: error=%d, processed=%d", (int)error,
+ (int)processed));
+ switch (error)
+ {
+ case 0 :
+ result = (int)processed;
+ break;
+
+ case errSSLWouldBlock :
+ if (processed)
+ result = (int)processed;
+ else
+ {
+ result = -1;
+ errno = EINTR;
+ }
+ break;
+
+ case errSSLClosedGraceful :
+ default :
+ if (processed)
+ result = (int)processed;
+ else
+ {
+ result = -1;
+ errno = EPIPE;
+ }
+ break;
+ }
+
+ return (result);
+
+# elif defined(HAVE_SSPISSL)
+ return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
+# endif /* HAVE_LIBSSL */
+}
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_send()' - Send a request with all fields and the trailing blank line.
+ */
+
+static int /* O - 0 on success, non-zero on error */
+http_send(http_t *http, /* I - Connection to server */
+ http_state_t request, /* I - Request code */
+ const char *uri) /* I - URI */
+{
+ int i; /* Looping var */
+ char buf[1024]; /* Encoded URI buffer */
+ const char *value; /* Field value */
+ static const char * const codes[] = /* Request code strings */
+ {
+ NULL,
+ "OPTIONS",
+ "GET",
+ NULL,
+ "HEAD",
+ "POST",
+ NULL,
+ NULL,
+ "PUT",
+ NULL,
+ "DELETE",
+ "TRACE",
+ "CLOSE",
+ NULL,
+ NULL
+ };
+
+
+ DEBUG_printf(("4http_send(http=%p, request=HTTP_%s, uri=\"%s\")",
+ http, codes[request], uri));
+
+ if (http == NULL || uri == NULL)
+ return (-1);
+
+ /*
+ * Set the User-Agent field if it isn't already...
+ */
+
+ if (!http->fields[HTTP_FIELD_USER_AGENT][0])
+ {
+ if (http->default_user_agent)
+ httpSetField(http, HTTP_FIELD_USER_AGENT, http->default_user_agent);
+ else
+ httpSetField(http, HTTP_FIELD_USER_AGENT, cupsUserAgent());
+ }
+
+ /*
+ * Set the Accept-Encoding field if it isn't already...
+ */
+
+ if (!http->accept_encoding && http->default_accept_encoding)
+ httpSetField(http, HTTP_FIELD_ACCEPT_ENCODING,
+ http->default_accept_encoding);
+
+ /*
+ * Encode the URI as needed...
+ */
+
+ _httpEncodeURI(buf, uri, sizeof(buf));
+
+ /*
+ * See if we had an error the last time around; if so, reconnect...
+ */
+
+ if (http->fd < 0 || http->status == HTTP_STATUS_ERROR ||
+ http->status >= HTTP_STATUS_BAD_REQUEST)
+ {
+ DEBUG_printf(("5http_send: Reconnecting, fd=%d, status=%d, tls_upgrade=%d",
+ http->fd, http->status, http->tls_upgrade));
+
+ if (httpReconnect2(http, 30000, NULL))
+ return (-1);
+ }
+
+ /*
+ * Flush any written data that is pending...
+ */
+
+ if (http->wused)
+ {
+ if (httpFlushWrite(http) < 0)
+ if (httpReconnect2(http, 30000, NULL))
+ return (-1);
+ }
+
+ /*
+ * Send the request header...
+ */
+
+ http->state = request;
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+
+ if (request == HTTP_STATE_POST || request == HTTP_STATE_PUT)
+ http->state ++;
+
+ http->status = HTTP_STATUS_CONTINUE;
+
+#ifdef HAVE_SSL
+ if (http->encryption == HTTP_ENCRYPTION_REQUIRED && !http->tls)
+ {
+ httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade");
+ httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.2,TLS/1.1,TLS/1.0");
+ }
+#endif /* HAVE_SSL */
+
+ if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ for (i = 0; i < HTTP_FIELD_MAX; i ++)
+ if ((value = httpGetField(http, i)) != NULL && *value)
+ {
+ DEBUG_printf(("5http_send: %s: %s", http_fields[i], value));
+
+ if (i == HTTP_FIELD_HOST)
+ {
+ if (httpPrintf(http, "Host: %s:%d\r\n", value,
+ httpAddrPort(http->hostaddr)) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+ else if (httpPrintf(http, "%s: %s\r\n", http_fields[i], value) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+
+ if (http->cookie)
+ if (httpPrintf(http, "Cookie: $Version=0; %s\r\n", http->cookie) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ DEBUG_printf(("5http_send: expect=%d, mode=%d, state=%d", http->expect,
+ http->mode, http->state));
+
+ if (http->expect == HTTP_STATUS_CONTINUE && http->mode == _HTTP_MODE_CLIENT &&
+ (http->state == HTTP_STATE_POST_RECV ||
+ http->state == HTTP_STATE_PUT_RECV))
+ if (httpPrintf(http, "Expect: 100-continue\r\n") < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (httpPrintf(http, "\r\n") < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (httpFlushWrite(http) < 0)
+ return (-1);
+
+ http_set_length(http);
+ httpClearFields(http);
+
+ /*
+ * The Kerberos and AuthRef authentication strings can only be used once...
+ */
+
+ if (http->field_authorization && http->authstring &&
+ (!strncmp(http->authstring, "Negotiate", 9) ||
+ !strncmp(http->authstring, "AuthRef", 7)))
+ {
+ http->_authstring[0] = '\0';
+
+ if (http->authstring != http->_authstring)
+ free(http->authstring);
+
+ http->authstring = http->_authstring;
+ }
+
+ return (0);
+}
+
+
+#ifdef HAVE_SSL
+# if defined(HAVE_CDSASSL)
+/*
+ * 'http_set_credentials()' - Set the SSL/TLS credentials.
+ */
+
+static int /* O - Status of connection */
+http_set_credentials(http_t *http) /* I - Connection to server */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+ OSStatus error = 0; /* Error code */
+ http_tls_credentials_t credentials = NULL;
+ /* TLS credentials */
+
+
+ DEBUG_printf(("7http_set_credentials(%p)", http));
+
+ /*
+ * Prefer connection specific credentials...
+ */
+
+ if ((credentials = http->tls_credentials) == NULL)
+ credentials = cg->tls_credentials;
+
+ if (credentials)
+ {
+ error = SSLSetCertificate(http->tls, credentials);
+ DEBUG_printf(("4http_set_credentials: SSLSetCertificate, error=%d",
+ (int)error));
+ }
+ else
+ DEBUG_puts("4http_set_credentials: No credentials to set.");
+
+ return (error);
+}
+# endif /* HAVE_CDSASSL */
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_set_length()' - Set the data_encoding and data_remaining values.
+ */
+
+static off_t /* O - Remainder or -1 on error */
+http_set_length(http_t *http) /* I - Connection */
+{
+ off_t remaining; /* Remainder */
+
+
+ DEBUG_printf(("http_set_length(http=%p) mode=%d state=%s", http, http->mode,
+ http_state_string(http->state)));
+
+ if ((remaining = httpGetLength2(http)) >= 0)
+ {
+ if (http->mode == _HTTP_MODE_SERVER &&
+ http->state != HTTP_STATE_GET_SEND &&
+ http->state != HTTP_STATE_PUT &&
+ http->state != HTTP_STATE_POST &&
+ http->state != HTTP_STATE_POST_SEND)
+ {
+ DEBUG_puts("1http_set_length: Not setting data_encoding/remaining.");
+ return (remaining);
+ }
+
+ if (!_cups_strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING],
+ "chunked"))
+ {
+ DEBUG_puts("1http_set_length: Setting data_encoding to "
+ "HTTP_ENCODING_CHUNKED.");
+ http->data_encoding = HTTP_ENCODING_CHUNKED;
+ }
+ else
+ {
+ DEBUG_puts("1http_set_length: Setting data_encoding to "
+ "HTTP_ENCODING_LENGTH.");
+ http->data_encoding = HTTP_ENCODING_LENGTH;
+ }
+
+ DEBUG_printf(("1http_set_length: Setting data_remaining to " CUPS_LLFMT ".",
+ CUPS_LLCAST remaining));
+ http->data_remaining = remaining;
+
+ if (remaining <= INT_MAX)
+ http->_data_remaining = remaining;
+ else
+ http->_data_remaining = INT_MAX;
+ }
+
+ return (remaining);
+}
+
+/*
+ * 'http_set_timeout()' - Set the socket timeout values.
+ */
+
+static void
+http_set_timeout(int fd, /* I - File descriptor */
+ double timeout) /* I - Timeout in seconds */
+{
+#ifdef WIN32
+ DWORD tv = (DWORD)(timeout * 1000);
+ /* Timeout in milliseconds */
+
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CUPS_SOCAST &tv, sizeof(tv));
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, CUPS_SOCAST &tv, sizeof(tv));
+
+#else
+ struct timeval tv; /* Timeout in secs and usecs */
+
+ tv.tv_sec = (int)timeout;
+ tv.tv_usec = (int)(1000000 * fmod(timeout, 1.0));
+
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CUPS_SOCAST &tv, sizeof(tv));
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, CUPS_SOCAST &tv, sizeof(tv));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'http_set_wait()' - Set the default wait value for reads.
+ */
+
+static void
+http_set_wait(http_t *http) /* I - Connection to server */
+{
+ if (http->blocking)
+ {
+ http->wait_value = (int)(http->timeout_value * 1000);
+
+ if (http->wait_value <= 0)
+ http->wait_value = 60000;
+ }
+ else
+ http->wait_value = 10000;
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+http_setup_ssl(http_t *http) /* I - Connection to server */
+{
+ char hostname[256], /* Hostname */
+ *hostptr; /* Pointer into hostname */
+
+# ifdef HAVE_LIBSSL
+ SSL_CTX *context; /* Context for encryption */
+ BIO *bio; /* BIO data */
+ const char *message = NULL;/* Error message */
+# elif defined(HAVE_GNUTLS)
+ int status; /* Status of handshake */
+ gnutls_certificate_client_credentials *credentials;
+ /* TLS credentials */
+# elif defined(HAVE_CDSASSL)
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+ OSStatus error; /* Error code */
+ const char *message = NULL;/* Error message */
+ cups_array_t *credentials; /* Credentials array */
+ cups_array_t *names; /* CUPS distinguished names */
+ CFArrayRef dn_array; /* CF distinguished names array */
+ CFIndex count; /* Number of credentials */
+ CFDataRef data; /* Certificate data */
+ int i; /* Looping var */
+ http_credential_t *credential; /* Credential data */
+# elif defined(HAVE_SSPISSL)
+ TCHAR username[256]; /* Username returned from GetUserName() */
+ TCHAR commonName[256];/* Common name for certificate */
+ DWORD dwSize; /* 32 bit size */
+# endif /* HAVE_LIBSSL */
+
+
+ DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+
+ /*
+ * Get the hostname to use for SSL...
+ */
+
+ if (httpAddrLocalhost(http->hostaddr))
+ {
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ }
+ else
+ {
+ /*
+ * Otherwise make sure the hostname we have does not end in a trailing dot.
+ */
+
+ strlcpy(hostname, http->hostname, sizeof(hostname));
+ if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+ *hostptr == '.')
+ *hostptr = '\0';
+ }
+
+# ifdef HAVE_LIBSSL
+ context = SSL_CTX_new(SSLv23_client_method());
+
+ SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+
+ bio = BIO_new(_httpBIOMethods());
+ BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+
+ http->tls = SSL_new(context);
+ SSL_set_bio(http->tls, bio, bio);
+
+# ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+ SSL_set_tlsext_host_name(http->tls, hostname);
+# endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
+
+ if (SSL_connect(http->tls) != 1)
+ {
+ unsigned long error; /* Error code */
+
+ while ((error = ERR_get_error()) != 0)
+ {
+ message = ERR_error_string(error, NULL);
+ DEBUG_printf(("8http_setup_ssl: %s", message));
+ }
+
+ SSL_CTX_free(context);
+ SSL_free(http->tls);
+ http->tls = NULL;
+
+# ifdef WIN32
+ http->error = WSAGetLastError();
+# else
+ http->error = errno;
+# endif /* WIN32 */
+ http->status = HTTP_STATUS_ERROR;
+
+ if (!message)
+ message = _("Unable to establish a secure connection to host.");
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+ return (-1);
+ }
+
+# elif defined(HAVE_GNUTLS)
+ credentials = (gnutls_certificate_client_credentials *)
+ malloc(sizeof(gnutls_certificate_client_credentials));
+ if (credentials == NULL)
+ {
+ DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
+ strerror(errno)));
+ http->error = errno;
+ http->status = HTTP_STATUS_ERROR;
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+ return (-1);
+ }
+
+ gnutls_certificate_allocate_credentials(credentials);
+
+ gnutls_init(&http->tls, GNUTLS_CLIENT);
+ gnutls_set_default_priority(http->tls);
+ gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
+ strlen(hostname));
+ gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
+ gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http);
+ gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
+ gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
+
+ while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+ {
+ DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
+ status, gnutls_strerror(status)));
+
+ if (gnutls_error_is_fatal(status))
+ {
+ http->error = EIO;
+ http->status = HTTP_STATUS_ERROR;
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
+
+ gnutls_deinit(http->tls);
+ gnutls_certificate_free_credentials(*credentials);
+ free(credentials);
+ http->tls = NULL;
+
+ return (-1);
+ }
+ }
+
+ http->tls_credentials = credentials;
+
+# elif defined(HAVE_CDSASSL)
+ if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
+ kSSLStreamType)) == NULL)
+ {
+ DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
+ http->error = errno = ENOMEM;
+ http->status = HTTP_STATUS_ERROR;
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+ return (-1);
+ }
+
+ error = SSLSetConnection(http->tls, http);
+ DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
+
+ if (!error)
+ {
+ error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
+ DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
+ }
+
+ if (!error)
+ {
+ error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
+ true);
+ DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
+ (int)error));
+ }
+
+ if (!error)
+ {
+ if (cg->client_cert_cb)
+ {
+ error = SSLSetSessionOption(http->tls,
+ kSSLSessionOptionBreakOnCertRequested, true);
+ DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
+ "error=%d", (int)error));
+ }
+ else
+ {
+ error = http_set_credentials(http);
+ DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
+ (int)error));
+ }
+ }
+
+ /*
+ * Let the server know which hostname/domain we are trying to connect to
+ * in case it wants to serve up a certificate with a matching common name.
+ */
+
+ if (!error)
+ {
+ error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+
+ DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
+ (int)error));
+ }
+
+ if (!error)
+ {
+ int done = 0; /* Are we done yet? */
+
+ while (!error && !done)
+ {
+ error = SSLHandshake(http->tls);
+
+ DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
+
+ switch (error)
+ {
+ case noErr :
+ done = 1;
+ break;
+
+ case errSSLWouldBlock :
+ error = noErr; /* Force a retry */
+ usleep(1000); /* in 1 millisecond */
+ break;
+
+ case errSSLServerAuthCompleted :
+ error = 0;
+ if (cg->server_cert_cb)
+ {
+ error = httpCopyCredentials(http, &credentials);
+ if (!error)
+ {
+ error = (cg->server_cert_cb)(http, http->tls, credentials,
+ cg->server_cert_data);
+ httpFreeCredentials(credentials);
+ }
+
+ DEBUG_printf(("4http_setup_ssl: Server certificate callback "
+ "returned %d.", (int)error));
+ }
+ break;
+
+ case errSSLClientCertRequested :
+ error = 0;
+
+ if (cg->client_cert_cb)
+ {
+ names = NULL;
+ if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
+ dn_array)
+ {
+ if ((names = cupsArrayNew(NULL, NULL)) != NULL)
+ {
+ for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
+ {
+ data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
+
+ if ((credential = malloc(sizeof(*credential))) != NULL)
+ {
+ credential->datalen = CFDataGetLength(data);
+ if ((credential->data = malloc(credential->datalen)))
+ {
+ memcpy((void *)credential->data, CFDataGetBytePtr(data),
+ credential->datalen);
+ cupsArrayAdd(names, credential);
+ }
+ else
+ free(credential);
+ }
+ }
+ }
+
+ CFRelease(dn_array);
+ }
+
+ if (!error)
+ {
+ error = (cg->client_cert_cb)(http, http->tls, names,
+ cg->client_cert_data);
+
+ DEBUG_printf(("4http_setup_ssl: Client certificate callback "
+ "returned %d.", (int)error));
+ }
+
+ httpFreeCredentials(names);
+ }
+ break;
+
+ case errSSLUnknownRootCert :
+ message = _("Unable to establish a secure connection to host "
+ "(untrusted certificate).");
+ break;
+
+ case errSSLNoRootCert :
+ message = _("Unable to establish a secure connection to host "
+ "(self-signed certificate).");
+ break;
+
+ case errSSLCertExpired :
+ message = _("Unable to establish a secure connection to host "
+ "(expired certificate).");
+ break;
+
+ case errSSLCertNotYetValid :
+ message = _("Unable to establish a secure connection to host "
+ "(certificate not yet valid).");
+ break;
+
+ case errSSLHostNameMismatch :
+ message = _("Unable to establish a secure connection to host "
+ "(host name mismatch).");
+ break;
+
+ case errSSLXCertChainInvalid :
+ message = _("Unable to establish a secure connection to host "
+ "(certificate chain invalid).");
+ break;
+
+ case errSSLConnectionRefused :
+ message = _("Unable to establish a secure connection to host "
+ "(peer dropped connection before responding).");
+ break;
+
+ default :
+ break;
+ }
+ }
+ }
+
+ if (error)
+ {
+ http->error = error;
+ http->status = HTTP_STATUS_ERROR;
+ errno = ECONNREFUSED;
+
+ CFRelease(http->tls);
+ http->tls = NULL;
+
+ /*
+ * If an error string wasn't set by the callbacks use a generic one...
+ */
+
+ if (!message)
+#ifdef HAVE_CSSMERRORSTRING
+ message = cssmErrorString(error);
+#else
+ message = _("Unable to establish a secure connection to host.");
+#endif /* HAVE_CSSMERRORSTRING */
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+ return (-1);
+ }
+
+# elif defined(HAVE_SSPISSL)
+ http->tls = _sspiAlloc();
+
+ if (!http->tls)
+ {
+ DEBUG_puts("8http_setup_ssl: Unable to allocate SSPI data.");
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ return (-1);
+ }
+
+ http->tls->sock = http->fd;
+ dwSize = sizeof(username) / sizeof(TCHAR);
+ GetUserName(username, &dwSize);
+ _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
+ sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
+
+ DEBUG_printf(("8http_setup_ssl: commonName=\"%s\"", commonName));
+
+ if (!_sspiGetCredentials(http->tls, L"ClientContainer", commonName, FALSE))
+ {
+ DEBUG_puts("8http_setup_ssl: _sspiGetCredentials failed.");
+
+ _sspiFree(http->tls);
+ http->tls = NULL;
+
+ http->error = EIO;
+ http->status = HTTP_STATUS_ERROR;
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+ _("Unable to establish a secure connection to host."), 1);
+
+ return (-1);
+ }
+
+ _sspiSetAllowsAnyRoot(http->tls, TRUE);
+ _sspiSetAllowsExpiredCerts(http->tls, TRUE);
+
+ if (!_sspiConnect(http->tls, hostname))
+ {
+ DEBUG_printf(("8http_setup_ssl: _sspiConnect failed for \"%s\".", hostname));
+
+ _sspiFree(http->tls);
+ http->tls = NULL;
+
+ http->error = EIO;
+ http->status = HTTP_STATUS_ERROR;
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+ _("Unable to establish a secure connection to host."), 1);
+
+ return (-1);
+ }
+# endif /* HAVE_CDSASSL */
+
+ return (0);
+}
+
+
+/*
+ * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
+ */
+
+static void
+http_shutdown_ssl(http_t *http) /* I - Connection to server */
+{
+# ifdef HAVE_LIBSSL
+ SSL_CTX *context; /* Context for encryption */
+
+ context = SSL_get_SSL_CTX(http->tls);
+
+ SSL_shutdown(http->tls);
+ SSL_CTX_free(context);
+ SSL_free(http->tls);
+
+# elif defined(HAVE_GNUTLS)
+ gnutls_certificate_client_credentials *credentials;
+ /* TLS credentials */
+
+ credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
+
+ gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(http->tls);
+ gnutls_certificate_free_credentials(*credentials);
+ free(credentials);
+
+# elif defined(HAVE_CDSASSL)
+ while (SSLClose(http->tls) == errSSLWouldBlock)
+ usleep(1000);
+
+ CFRelease(http->tls);
+
+ if (http->tls_credentials)
+ CFRelease(http->tls_credentials);
+
+# elif defined(HAVE_SSPISSL)
+ _sspiFree(http->tls);
+# endif /* HAVE_LIBSSL */
+
+ http->tls = NULL;
+ http->tls_credentials = NULL;
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef DEBUG
+/*
+ * 'http_state_string()' - Return the string associated with a given HTTP state.
+ */
+
+static const char * /* O - State string */
+http_state_string(http_state_t state) /* I - HTTP state */
+{
+ static char buffer[255]; /* Unknown value buffer */
+ static const char * const states[] = /* State strings */
+ {
+ "HTTP_STATE_ERROR",
+ "HTTP_STATE_WAITING",
+ "HTTP_STATE_OPTIONS",
+ "HTTP_STATE_GET",
+ "HTTP_STATE_GET_SEND",
+ "HTTP_STATE_HEAD",
+ "HTTP_STATE_POST",
+ "HTTP_STATE_POST_RECV",
+ "HTTP_STATE_POST_SEND",
+ "HTTP_STATE_PUT",
+ "HTTP_STATE_PUT_RECV",
+ "HTTP_STATE_DELETE",
+ "HTTP_STATE_TRACE",
+ "HTTP_STATE_CONNECT",
+ "HTTP_STATE_STATUS",
+ "HTTP_STATE_UNKNOWN_METHOD",
+ "HTTP_STATE_UNKNOWN_VERSION"
+ };
+
+ if (state >= HTTP_STATE_ERROR && state <= HTTP_STATE_UNKNOWN_VERSION)
+ return (states[state - HTTP_STATE_ERROR]);
+
+ snprintf(buffer, sizeof(buffer), "??? %d ???", (int)state);
+ return (buffer);
+}
+#endif /* DEBUG */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_upgrade()' - Force upgrade to TLS encryption.
+ */
+
+static int /* O - Status of connection */
+http_upgrade(http_t *http) /* I - Connection to server */
+{
+ int ret; /* Return value */
+ http_t myhttp; /* Local copy of HTTP data */
+
+
+ DEBUG_printf(("7http_upgrade(%p)", http));
+
+ /*
+ * Flush the connection to make sure any previous "Upgrade" message
+ * has been read.
+ */
+
+ httpFlush(http);
+
+ /*
+ * Copy the HTTP data to a local variable so we can do the OPTIONS
+ * request without interfering with the existing request data...
+ */
+
+ memcpy(&myhttp, http, sizeof(myhttp));
+
+ /*
+ * Send an OPTIONS request to the server, requiring SSL or TLS
+ * encryption on the link...
+ */
+
+ http->tls_upgrade = 1;
+ http->field_authorization = NULL; /* Don't free the auth string */
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONNECTION, "upgrade");
+ httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.2,TLS/1.1,TLS/1.0");
+
+ if ((ret = httpOptions(http, "*")) == 0)
+ {
+ /*
+ * Wait for the secure connection...
+ */
+
+ while (httpUpdate(http) == HTTP_STATUS_CONTINUE);
+ }
+
+ /*
+ * Restore the HTTP request data...
+ */
+
+ memcpy(http->fields, myhttp.fields, sizeof(http->fields));
+ http->data_encoding = myhttp.data_encoding;
+ http->data_remaining = myhttp.data_remaining;
+ http->_data_remaining = myhttp._data_remaining;
+ http->expect = myhttp.expect;
+ http->field_authorization = myhttp.field_authorization;
+ http->digest_tries = myhttp.digest_tries;
+ http->tls_upgrade = 0;
+
+ /*
+ * See if we actually went secure...
+ */
+
+ if (!http->tls)
+ {
+ /*
+ * Server does not support HTTP upgrade...
+ */
+
+ DEBUG_puts("8http_upgrade: Server does not support HTTP upgrade!");
+
+# ifdef WIN32
+ closesocket(http->fd);
+# else
+ close(http->fd);
+# endif
+
+ http->fd = -1;
+
+ return (-1);
+ }
+ else
+ return (ret);
+}
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_write()' - Write a buffer to a HTTP connection.
+ */
+
+static ssize_t /* O - Number of bytes written */
+http_write(http_t *http, /* I - Connection to server */
+ const char *buffer, /* I - Buffer for data */
+ size_t length) /* I - Number of bytes to write */
+{
+ ssize_t tbytes, /* Total bytes sent */
+ bytes; /* Bytes sent */
+
+
+ DEBUG_printf(("2http_write(http=%p, buffer=%p, length=" CUPS_LLFMT ")", http,
+ buffer, CUPS_LLCAST length));
+ http->error = 0;
+ tbytes = 0;
+
+ while (length > 0)
+ {
+ DEBUG_printf(("3http_write: About to write %d bytes.", (int)length));
+
+ if (http->timeout_cb)
+ {
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Polled file descriptor */
+#else
+ fd_set output_set; /* Output ready for write? */
+ struct timeval timeout; /* Timeout value */
+#endif /* HAVE_POLL */
+ int nfds; /* Result from select()/poll() */
+
+ do
+ {
+#ifdef HAVE_POLL
+ pfd.fd = http->fd;
+ pfd.events = POLLOUT;
+
+ while ((nfds = poll(&pfd, 1, http->wait_value)) < 0 &&
+ (errno == EINTR || errno == EAGAIN))
+ /* do nothing */;
+
+#else
+ do
+ {
+ FD_ZERO(&output_set);
+ FD_SET(http->fd, &output_set);
+
+ timeout.tv_sec = http->wait_value / 1000;
+ timeout.tv_usec = 1000 * (http->wait_value % 1000);
+
+ nfds = select(http->fd + 1, NULL, &output_set, NULL, &timeout);
+ }
+# ifdef WIN32
+ while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
+ WSAGetLastError() == WSAEWOULDBLOCK));
+# else
+ while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+# endif /* WIN32 */
+#endif /* HAVE_POLL */
+
+ if (nfds < 0)
+ {
+ http->error = errno;
+ return (-1);
+ }
+ else if (nfds == 0 && !(*http->timeout_cb)(http, http->timeout_data))
+ {
+#ifdef WIN32
+ http->error = WSAEWOULDBLOCK;
+#else
+ http->error = EWOULDBLOCK;
+#endif /* WIN32 */
+ return (-1);
+ }
+ }
+ while (nfds <= 0);
+ }
+
+#ifdef HAVE_SSL
+ if (http->tls)
+ bytes = http_write_ssl(http, buffer, length);
+ else
+#endif /* HAVE_SSL */
+ bytes = send(http->fd, buffer, length, 0);
+
+ DEBUG_printf(("3http_write: Write of " CUPS_LLFMT " bytes returned "
+ CUPS_LLFMT ".", CUPS_LLCAST length, CUPS_LLCAST bytes));
+
+ if (bytes < 0)
+ {
+#ifdef WIN32
+ if (WSAGetLastError() == WSAEINTR)
+ continue;
+ else if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ http->error = WSAGetLastError();
+ }
+ else if (WSAGetLastError() != http->error &&
+ WSAGetLastError() != WSAECONNRESET)
+ {
+ http->error = WSAGetLastError();
+ continue;
+ }
+
+#else
+ if (errno == EINTR)
+ continue;
+ else if (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+ else if (!http->timeout_cb && errno == EAGAIN)
+ continue;
+
+ http->error = errno;
+ }
+ else if (errno != http->error && errno != ECONNRESET)
+ {
+ http->error = errno;
+ continue;
+ }
+#endif /* WIN32 */
+
+ DEBUG_printf(("3http_write: error writing data (%s).",
+ strerror(http->error)));
+
+ return (-1);
+ }
+
+ buffer += bytes;
+ tbytes += bytes;
+ length -= bytes;
+ }
+
+#ifdef DEBUG
+ http_debug_hex("http_write", buffer - tbytes, tbytes);
+#endif /* DEBUG */
+
+ DEBUG_printf(("3http_write: Returning " CUPS_LLFMT ".", CUPS_LLCAST tbytes));
+
+ return (tbytes);
+}
+
+
+/*
+ * 'http_write_chunk()' - Write a chunked buffer.
+ */
+
+static ssize_t /* O - Number bytes written */
+http_write_chunk(http_t *http, /* I - Connection to server */
+ const char *buffer, /* I - Buffer to write */
+ size_t length) /* I - Length of buffer */
+{
+ char header[16]; /* Chunk header */
+ ssize_t bytes; /* Bytes written */
+
+
+ DEBUG_printf(("7http_write_chunk(http=%p, buffer=%p, length=" CUPS_LLFMT ")",
+ http, buffer, CUPS_LLCAST length));
+
+ /*
+ * Write the chunk header, data, and trailer.
+ */
+
+ snprintf(header, sizeof(header), "%x\r\n", (unsigned)length);
+ if (http_write(http, header, strlen(header)) < 0)
+ {
+ DEBUG_puts("8http_write_chunk: http_write of length failed.");
+ return (-1);
+ }
+
+ if ((bytes = http_write(http, buffer, length)) < 0)
+ {
+ DEBUG_puts("8http_write_chunk: http_write of buffer failed.");
+ return (-1);
+ }
+
+ if (http_write(http, "\r\n", 2) < 0)
+ {
+ DEBUG_puts("8http_write_chunk: http_write of CR LF failed.");
+ return (-1);
+ }
+
+ return (bytes);
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_write_ssl()' - Write to a SSL/TLS connection.
+ */
+
+static int /* O - Bytes written */
+http_write_ssl(http_t *http, /* I - Connection to server */
+ const char *buf, /* I - Buffer holding data */
+ int len) /* I - Length of buffer */
+{
+ ssize_t result; /* Return value */
+
+
+ DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+
+# if defined(HAVE_LIBSSL)
+ result = SSL_write((SSL *)(http->tls), buf, len);
+
+# elif defined(HAVE_GNUTLS)
+ result = gnutls_record_send(http->tls, buf, len);
+
+ if (result < 0 && !errno)
+ {
+ /*
+ * Convert GNU TLS error to errno value...
+ */
+
+ switch (result)
+ {
+ case GNUTLS_E_INTERRUPTED :
+ errno = EINTR;
+ break;
+
+ case GNUTLS_E_AGAIN :
+ errno = EAGAIN;
+ break;
+
+ default :
+ errno = EPIPE;
+ break;
+ }
+
+ result = -1;
+ }
+
+# elif defined(HAVE_CDSASSL)
+ OSStatus error; /* Error info */
+ size_t processed; /* Number of bytes processed */
+
+
+ error = SSLWrite(http->tls, buf, len, &processed);
+
+ switch (error)
+ {
+ case 0 :
+ result = (int)processed;
+ break;
+
+ case errSSLWouldBlock :
+ if (processed)
+ result = (int)processed;
+ else
+ {
+ result = -1;
+ errno = EINTR;
+ }
+ break;
+
+ case errSSLClosedGraceful :
+ default :
+ if (processed)
+ result = (int)processed;
+ else
+ {
+ result = -1;
+ errno = EPIPE;
+ }
+ break;
+ }
+# elif defined(HAVE_SSPISSL)
+ return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
+# endif /* HAVE_LIBSSL */
+
+ DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
+
+ return ((int)result);
+}
+#endif /* HAVE_SSL */
+
+
+/*
+ * End of "$Id: http.c 11761 2014-03-28 13:04:33Z msweet $".
+ */
diff --git a/cups/libs/cups/http.h b/cups/libs/cups/http.h
new file mode 100644
index 000000000..f9c83a88e
--- /dev/null
+++ b/cups/libs/cups/http.h
@@ -0,0 +1,627 @@
+/*
+ * "$Id: http.h 11085 2013-07-03 13:53:05Z msweet $"
+ *
+ * Hyper-Text Transport Protocol definitions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_HTTP_H_
+# define _CUPS_HTTP_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "versioning.h"
+# include "array.h"
+# include <string.h>
+# include <time.h>
+# include <sys/types.h>
+# ifdef WIN32
+# ifndef __CUPS_SSIZE_T_DEFINED
+# define __CUPS_SSIZE_T_DEFINED
+/* Windows does not support the ssize_t type, so map it to off_t... */
+typedef off_t ssize_t; /* @private@ */
+# endif /* !__CUPS_SSIZE_T_DEFINED */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# else
+# include <unistd.h>
+# include <sys/time.h>
+# include <sys/socket.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# if !defined(__APPLE__) || !defined(TCP_NODELAY)
+# include <netinet/tcp.h>
+# endif /* !__APPLE__ || !TCP_NODELAY */
+# if defined(AF_UNIX) && !defined(AF_LOCAL)
+# define AF_LOCAL AF_UNIX /* Older UNIX's have old names... */
+# endif /* AF_UNIX && !AF_LOCAL */
+# ifdef AF_LOCAL
+# include <sys/un.h>
+# endif /* AF_LOCAL */
+# if defined(LOCAL_PEERCRED) && !defined(SO_PEERCRED)
+# define SO_PEERCRED LOCAL_PEERCRED
+# endif /* LOCAL_PEERCRED && !SO_PEERCRED */
+# endif /* WIN32 */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Oh, the wonderful world of IPv6 compatibility. Apparently some
+ * implementations expose the (more logical) 32-bit address parts
+ * to everyone, while others only expose it to kernel code... To
+ * make supporting IPv6 even easier, each vendor chose different
+ * core structure and union names, so the same defines or code
+ * can't be used on all platforms.
+ *
+ * The following will likely need tweaking on new platforms that
+ * support IPv6 - the "s6_addr32" define maps to the 32-bit integer
+ * array in the in6_addr union, which is named differently on various
+ * platforms.
+ */
+
+#if defined(AF_INET6) && !defined(s6_addr32)
+# if defined(__sun)
+# define s6_addr32 _S6_un._S6_u32
+# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)|| defined(__DragonFly__)
+# define s6_addr32 __u6_addr.__u6_addr32
+# elif defined(__osf__)
+# define s6_addr32 s6_un.sa6_laddr
+# elif defined(WIN32)
+/*
+ * Windows only defines byte and 16-bit word members of the union and
+ * requires special casing of all raw address code...
+ */
+# define s6_addr32 error_need_win32_specific_code
+# endif /* __sun */
+#endif /* AF_INET6 && !s6_addr32 */
+
+
+/*
+ * Limits...
+ */
+
+# define HTTP_MAX_URI 1024 /* Max length of URI string */
+# define HTTP_MAX_HOST 256 /* Max length of hostname string */
+# define HTTP_MAX_BUFFER 2048 /* Max length of data buffer */
+# define HTTP_MAX_VALUE 256 /* Max header field value length */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum http_auth_e /**** HTTP authentication types ****/
+{
+ HTTP_AUTH_NONE, /* No authentication in use */
+ HTTP_AUTH_BASIC, /* Basic authentication in use */
+ HTTP_AUTH_MD5, /* Digest authentication in use */
+ HTTP_AUTH_MD5_SESS, /* MD5-session authentication in use */
+ HTTP_AUTH_MD5_INT, /* Digest authentication in use for body */
+ HTTP_AUTH_MD5_SESS_INT, /* MD5-session authentication in use for body */
+ HTTP_AUTH_NEGOTIATE /* GSSAPI authentication in use @since CUPS 1.3/OS X 10.5@ */
+} http_auth_t;
+
+typedef enum http_encoding_e /**** HTTP transfer encoding values ****/
+{
+ HTTP_ENCODING_LENGTH, /* Data is sent with Content-Length */
+ HTTP_ENCODING_CHUNKED, /* Data is chunked */
+ HTTP_ENCODING_FIELDS /* Sending HTTP fields */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define HTTP_ENCODE_LENGTH HTTP_ENCODING_LENGTH
+# define HTTP_ENCODE_CHUNKED HTTP_ENCODING_CHUNKED
+# define HTTP_ENCODE_FIELDS HTTP_ENCODING_FIELDS
+# endif /* !_CUPS_NO_DEPRECATED */
+} http_encoding_t;
+
+typedef enum http_encryption_e /**** HTTP encryption values ****/
+{
+ HTTP_ENCRYPTION_IF_REQUESTED, /* Encrypt if requested (TLS upgrade) */
+ HTTP_ENCRYPTION_NEVER, /* Never encrypt */
+ HTTP_ENCRYPTION_REQUIRED, /* Encryption is required (TLS upgrade) */
+ HTTP_ENCRYPTION_ALWAYS /* Always encrypt (SSL) */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define HTTP_ENCRYPT_IF_REQUESTED HTTP_ENCRYPTION_IF_REQUESTED
+# define HTTP_ENCRYPT_NEVER HTTP_ENCRYPTION_NEVER
+# define HTTP_ENCRYPT_REQUIRED HTTP_ENCRYPTION_REQUIRED
+# define HTTP_ENCRYPT_ALWAYS HTTP_ENCRYPTION_ALWAYS
+# endif /* !_CUPS_NO_DEPRECATED */
+} http_encryption_t;
+
+typedef enum http_field_e /**** HTTP field names ****/
+{
+ HTTP_FIELD_UNKNOWN = -1, /* Unknown field */
+ HTTP_FIELD_ACCEPT_LANGUAGE, /* Accept-Language field */
+ HTTP_FIELD_ACCEPT_RANGES, /* Accept-Ranges field */
+ HTTP_FIELD_AUTHORIZATION, /* Authorization field */
+ HTTP_FIELD_CONNECTION, /* Connection field */
+ HTTP_FIELD_CONTENT_ENCODING, /* Content-Encoding field */
+ HTTP_FIELD_CONTENT_LANGUAGE, /* Content-Language field */
+ HTTP_FIELD_CONTENT_LENGTH, /* Content-Length field */
+ HTTP_FIELD_CONTENT_LOCATION, /* Content-Location field */
+ HTTP_FIELD_CONTENT_MD5, /* Content-MD5 field */
+ HTTP_FIELD_CONTENT_RANGE, /* Content-Range field */
+ HTTP_FIELD_CONTENT_TYPE, /* Content-Type field */
+ HTTP_FIELD_CONTENT_VERSION, /* Content-Version field */
+ HTTP_FIELD_DATE, /* Date field */
+ HTTP_FIELD_HOST, /* Host field */
+ HTTP_FIELD_IF_MODIFIED_SINCE, /* If-Modified-Since field */
+ HTTP_FIELD_IF_UNMODIFIED_SINCE, /* If-Unmodified-Since field */
+ HTTP_FIELD_KEEP_ALIVE, /* Keep-Alive field */
+ HTTP_FIELD_LAST_MODIFIED, /* Last-Modified field */
+ HTTP_FIELD_LINK, /* Link field */
+ HTTP_FIELD_LOCATION, /* Location field */
+ HTTP_FIELD_RANGE, /* Range field */
+ HTTP_FIELD_REFERER, /* Referer field */
+ HTTP_FIELD_RETRY_AFTER, /* Retry-After field */
+ HTTP_FIELD_TRANSFER_ENCODING, /* Transfer-Encoding field */
+ HTTP_FIELD_UPGRADE, /* Upgrade field */
+ HTTP_FIELD_USER_AGENT, /* User-Agent field */
+ HTTP_FIELD_WWW_AUTHENTICATE, /* WWW-Authenticate field */
+ HTTP_FIELD_ACCEPT_ENCODING, /* Accepting-Encoding field @since CUPS 1.7/OS X 10.9@ */
+ HTTP_FIELD_ALLOW, /* Allow field @since CUPS 1.7/OS X 10.9@ */
+ HTTP_FIELD_SERVER, /* Server field @since CUPS 1.7/OS X 10.9@ */
+ HTTP_FIELD_MAX /* Maximum field index */
+} http_field_t;
+
+typedef enum http_keepalive_e /**** HTTP keep-alive values ****/
+{
+ HTTP_KEEPALIVE_OFF = 0, /* No keep alive support */
+ HTTP_KEEPALIVE_ON /* Use keep alive */
+} http_keepalive_t;
+
+typedef enum http_state_e /**** HTTP state values; states
+ **** are server-oriented...
+ ****/
+{
+ HTTP_STATE_ERROR = -1, /* Error on socket */
+ HTTP_STATE_WAITING, /* Waiting for command */
+ HTTP_STATE_OPTIONS, /* OPTIONS command, waiting for blank line */
+ HTTP_STATE_GET, /* GET command, waiting for blank line */
+ HTTP_STATE_GET_SEND, /* GET command, sending data */
+ HTTP_STATE_HEAD, /* HEAD command, waiting for blank line */
+ HTTP_STATE_POST, /* POST command, waiting for blank line */
+ HTTP_STATE_POST_RECV, /* POST command, receiving data */
+ HTTP_STATE_POST_SEND, /* POST command, sending data */
+ HTTP_STATE_PUT, /* PUT command, waiting for blank line */
+ HTTP_STATE_PUT_RECV, /* PUT command, receiving data */
+ HTTP_STATE_DELETE, /* DELETE command, waiting for blank line */
+ HTTP_STATE_TRACE, /* TRACE command, waiting for blank line */
+ HTTP_STATE_CONNECT, /* CONNECT command, waiting for blank line */
+ HTTP_STATE_STATUS, /* Command complete, sending status */
+ HTTP_STATE_UNKNOWN_METHOD, /* Unknown request method, waiting for blank line @since CUPS 1.7/OS X 10.9@ */
+ HTTP_STATE_UNKNOWN_VERSION /* Unknown request method, waiting for blank line @since CUPS 1.7/OS X 10.9@ */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define HTTP_WAITING HTTP_STATE_WAITING
+# define HTTP_OPTIONS HTTP_STATE_OPTIONS
+# define HTTP_GET HTTP_STATE_GET
+# define HTTP_GET_SEND HTTP_STATE_GET_SEND
+# define HTTP_HEAD HTTP_STATE_HEAD
+# define HTTP_POST HTTP_STATE_POST
+# define HTTP_POST_RECV HTTP_STATE_POST_RECV
+# define HTTP_POST_SEND HTTP_STATE_POST_SEND
+# define HTTP_PUT HTTP_STATE_PUT
+# define HTTP_PUT_RECV HTTP_STATE_PUT_RECV
+# define HTTP_DELETE HTTP_STATE_DELETE
+# define HTTP_TRACE HTTP_STATE_TRACE
+# define HTTP_CLOSE HTTP_STATE_CONNECT
+# define HTTP_STATUS HTTP_STATE_STATUS
+# endif /* !_CUPS_NO_DEPRECATED */
+} http_state_t;
+
+typedef enum http_status_e /**** HTTP status codes ****/
+{
+ HTTP_STATUS_ERROR = -1, /* An error response from httpXxxx() */
+ HTTP_STATUS_NONE = 0, /* No Expect value @since CUPS 1.7/OS X 10.9@ */
+
+ HTTP_STATUS_CONTINUE = 100, /* Everything OK, keep going... */
+ HTTP_STATUS_SWITCHING_PROTOCOLS, /* HTTP upgrade to TLS/SSL */
+
+ HTTP_STATUS_OK = 200, /* OPTIONS/GET/HEAD/POST/TRACE command was successful */
+ HTTP_STATUS_CREATED, /* PUT command was successful */
+ HTTP_STATUS_ACCEPTED, /* DELETE command was successful */
+ HTTP_STATUS_NOT_AUTHORITATIVE, /* Information isn't authoritative */
+ HTTP_STATUS_NO_CONTENT, /* Successful command, no new data */
+ HTTP_STATUS_RESET_CONTENT, /* Content was reset/recreated */
+ HTTP_STATUS_PARTIAL_CONTENT, /* Only a partial file was recieved/sent */
+
+ HTTP_STATUS_MULTIPLE_CHOICES = 300, /* Multiple files match request */
+ HTTP_STATUS_MOVED_PERMANENTLY, /* Document has moved permanently */
+ HTTP_STATUS_MOVED_TEMPORARILY, /* Document has moved temporarily */
+ HTTP_STATUS_SEE_OTHER, /* See this other link... */
+ HTTP_STATUS_NOT_MODIFIED, /* File not modified */
+ HTTP_STATUS_USE_PROXY, /* Must use a proxy to access this URI */
+
+ HTTP_STATUS_BAD_REQUEST = 400, /* Bad request */
+ HTTP_STATUS_UNAUTHORIZED, /* Unauthorized to access host */
+ HTTP_STATUS_PAYMENT_REQUIRED, /* Payment required */
+ HTTP_STATUS_FORBIDDEN, /* Forbidden to access this URI */
+ HTTP_STATUS_NOT_FOUND, /* URI was not found */
+ HTTP_STATUS_METHOD_NOT_ALLOWED, /* Method is not allowed */
+ HTTP_STATUS_NOT_ACCEPTABLE, /* Not Acceptable */
+ HTTP_STATUS_PROXY_AUTHENTICATION, /* Proxy Authentication is Required */
+ HTTP_STATUS_REQUEST_TIMEOUT, /* Request timed out */
+ HTTP_STATUS_CONFLICT, /* Request is self-conflicting */
+ HTTP_STATUS_GONE, /* Server has gone away */
+ HTTP_STATUS_LENGTH_REQUIRED, /* A content length or encoding is required */
+ HTTP_STATUS_PRECONDITION, /* Precondition failed */
+ HTTP_STATUS_REQUEST_TOO_LARGE, /* Request entity too large */
+ HTTP_STATUS_URI_TOO_LONG, /* URI too long */
+ HTTP_STATUS_UNSUPPORTED_MEDIATYPE, /* The requested media type is unsupported */
+ HTTP_STATUS_REQUESTED_RANGE, /* The requested range is not satisfiable */
+ HTTP_STATUS_EXPECTATION_FAILED, /* The expectation given in an Expect header field was not met */
+ HTTP_STATUS_UPGRADE_REQUIRED = 426, /* Upgrade to SSL/TLS required */
+
+ HTTP_STATUS_SERVER_ERROR = 500, /* Internal server error */
+ HTTP_STATUS_NOT_IMPLEMENTED, /* Feature not implemented */
+ HTTP_STATUS_BAD_GATEWAY, /* Bad gateway */
+ HTTP_STATUS_SERVICE_UNAVAILABLE, /* Service is unavailable */
+ HTTP_STATUS_GATEWAY_TIMEOUT, /* Gateway connection timed out */
+ HTTP_STATUS_NOT_SUPPORTED, /* HTTP version not supported */
+
+ HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED = 1000,
+ /* User canceled authorization @since CUPS 1.4@ */
+ HTTP_STATUS_CUPS_PKI_ERROR, /* Error negotiating a secure connection @since CUPS 1.5/OS X 10.7@ */
+ HTTP_STATUS_CUPS_WEBIF_DISABLED /* Web interface is disabled @private@ */
+
+# ifndef _CUPS_NO_DEPRECATED
+/* Old names for this enumeration */
+# define HTTP_ERROR HTTP_STATUS_ERROR
+
+# define HTTP_CONTINUE HTTP_STATUS_CONTINUE
+# define HTTP_SWITCHING_PROTOCOLS HTTP_STATUS_SWITCHING_PROTOCOLS
+
+# define HTTP_OK HTTP_STATUS_OK
+# define HTTP_CREATED HTTP_STATUS_CREATED
+# define HTTP_ACCEPTED HTTP_STATUS_ACCEPTED
+# define HTTP_NOT_AUTHORITATIVE HTTP_STATUS_NOT_AUTHORITATIVE
+# define HTTP_NO_CONTENT HTTP_STATUS_NO_CONTENT
+# define HTTP_RESET_CONTENT HTTP_STATUS_RESET_CONTENT
+# define HTTP_PARTIAL_CONTENT HTTP_STATUS_PARTIAL_CONTENT
+
+# define HTTP_MULTIPLE_CHOICES HTTP_STATUS_MULTIPLE_CHOICES
+# define HTTP_MOVED_PERMANENTLY HTTP_STATUS_MOVED_PERMANENTLY
+# define HTTP_MOVED_TEMPORARILY HTTP_STATUS_MOVED_TEMPORARILY
+# define HTTP_SEE_OTHER HTTP_STATUS_SEE_OTHER
+# define HTTP_NOT_MODIFIED HTTP_STATUS_NOT_MODIFIED
+# define HTTP_USE_PROXY HTTP_STATUS_USE_PROXY
+
+# define HTTP_BAD_REQUEST HTTP_STATUS_BAD_REQUEST
+# define HTTP_UNAUTHORIZED HTTP_STATUS_UNAUTHORIZED
+# define HTTP_PAYMENT_REQUIRED HTTP_STATUS_PAYMENT_REQUIRED
+# define HTTP_FORBIDDEN HTTP_STATUS_FORBIDDEN
+# define HTTP_NOT_FOUND HTTP_STATUS_NOT_FOUND
+# define HTTP_METHOD_NOT_ALLOWED HTTP_STATUS_METHOD_NOT_ALLOWED
+# define HTTP_NOT_ACCEPTABLE HTTP_STATUS_NOT_ACCEPTABLE
+# define HTTP_PROXY_AUTHENTICATION HTTP_STATUS_PROXY_AUTHENTICATION
+# define HTTP_REQUEST_TIMEOUT HTTP_STATUS_REQUEST_TIMEOUT
+# define HTTP_CONFLICT HTTP_STATUS_CONFLICT
+# define HTTP_GONE HTTP_STATUS_GONE
+# define HTTP_LENGTH_REQUIRED HTTP_STATUS_LENGTH_REQUIRED
+# define HTTP_PRECONDITION HTTP_STATUS_PRECONDITION
+# define HTTP_REQUEST_TOO_LARGE HTTP_STATUS_REQUEST_TOO_LARGE
+# define HTTP_URI_TOO_LONG HTTP_STATUS_URI_TOO_LONG
+# define HTTP_UNSUPPORTED_MEDIATYPE HTTP_STATUS_UNSUPPORTED_MEDIATYPE
+# define HTTP_REQUESTED_RANGE HTTP_STATUS_REQUESTED_RANGE
+# define HTTP_EXPECTATION_FAILED HTTP_STATUS_EXPECTATION_FAILED
+# define HTTP_UPGRADE_REQUIRED HTTP_STATUS_UPGRADE_REQUIRED
+
+# define HTTP_SERVER_ERROR HTTP_STATUS_SERVER_ERROR
+# define HTTP_NOT_IMPLEMENTED HTTP_STATUS_NOT_IMPLEMENTED
+# define HTTP_BAD_GATEWAY HTTP_STATUS_BAD_GATEWAY
+# define HTTP_SERVICE_UNAVAILABLE HTTP_STATUS_SERVICE_UNAVAILABLE
+# define HTTP_GATEWAY_TIMEOUT HTTP_STATUS_GATEWAY_TIMEOUT
+# define HTTP_NOT_SUPPORTED HTTP_STATUS_NOT_SUPPORTED
+
+# define HTTP_AUTHORIZATION_CANCELED HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
+# define HTTP_PKI_ERROR HTTP_STATUS_CUPS_PKI_ERROR
+# define HTTP_WEBIF_DISABLED HTTP_STATUS_CUPS_WEBIF_DISABLED
+# endif /* !_CUPS_NO_DEPRECATED */
+} http_status_t;
+
+typedef enum http_uri_status_e /**** URI separation status @since CUPS 1.2@ ****/
+{
+ HTTP_URI_STATUS_OVERFLOW = -8, /* URI buffer for httpAssembleURI is too small */
+ HTTP_URI_STATUS_BAD_ARGUMENTS = -7, /* Bad arguments to function (error) */
+ HTTP_URI_STATUS_BAD_RESOURCE = -6, /* Bad resource in URI (error) */
+ HTTP_URI_STATUS_BAD_PORT = -5, /* Bad port number in URI (error) */
+ HTTP_URI_STATUS_BAD_HOSTNAME = -4, /* Bad hostname in URI (error) */
+ HTTP_URI_STATUS_BAD_USERNAME = -3, /* Bad username in URI (error) */
+ HTTP_URI_STATUS_BAD_SCHEME = -2, /* Bad scheme in URI (error) */
+ HTTP_URI_STATUS_BAD_URI = -1, /* Bad/empty URI (error) */
+ HTTP_URI_STATUS_OK = 0, /* URI decoded OK */
+ HTTP_URI_STATUS_MISSING_SCHEME, /* Missing scheme in URI (warning) */
+ HTTP_URI_STATUS_UNKNOWN_SCHEME, /* Unknown scheme in URI (warning) */
+ HTTP_URI_STATUS_MISSING_RESOURCE /* Missing resource in URI (warning) */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define HTTP_URI_OVERFLOW HTTP_URI_STATUS_OVERFLOW
+# define HTTP_URI_BAD_ARGUMENTS HTTP_URI_STATUS_BAD_ARGUMENTS
+# define HTTP_URI_BAD_RESOURCE HTTP_URI_STATUS_BAD_RESOURCE
+# define HTTP_URI_BAD_PORT HTTP_URI_STATUS_BAD_PORT
+# define HTTP_URI_BAD_HOSTNAME HTTP_URI_STATUS_BAD_HOSTNAME
+# define HTTP_URI_BAD_USERNAME HTTP_URI_STATUS_BAD_USERNAME
+# define HTTP_URI_BAD_SCHEME HTTP_URI_STATUS_BAD_SCHEME
+# define HTTP_URI_BAD_URI HTTP_URI_STATUS_BAD_URI
+# define HTTP_URI_OK HTTP_URI_STATUS_OK
+# define HTTP_URI_MISSING_SCHEME HTTP_URI_STATUS_MISSING_SCHEME
+# define HTTP_URI_UNKNOWN_SCHEME HTTP_URI_STATUS_UNKNOWN_SCHEME
+# define HTTP_URI_MISSING_RESOURCE HTTP_URI_STATUS_MISSING_RESOURCE
+# endif /* !_CUPS_NO_DEPRECATED */
+} http_uri_status_t;
+
+typedef enum http_uri_coding_e /**** URI en/decode flags ****/
+{
+ HTTP_URI_CODING_NONE = 0, /* Don't en/decode anything */
+ HTTP_URI_CODING_USERNAME = 1, /* En/decode the username portion */
+ HTTP_URI_CODING_HOSTNAME = 2, /* En/decode the hostname portion */
+ HTTP_URI_CODING_RESOURCE = 4, /* En/decode the resource portion */
+ HTTP_URI_CODING_MOST = 7, /* En/decode all but the query */
+ HTTP_URI_CODING_QUERY = 8, /* En/decode the query portion */
+ HTTP_URI_CODING_ALL = 15, /* En/decode everything */
+ HTTP_URI_CODING_RFC6874 = 16 /* Use RFC 6874 address format */
+} http_uri_coding_t;
+
+typedef enum http_version_e /**** HTTP version numbers ****/
+{
+ HTTP_VERSION_0_9 = 9, /* HTTP/0.9 */
+ HTTP_VERSION_1_0 = 100, /* HTTP/1.0 */
+ HTTP_VERSION_1_1 = 101 /* HTTP/1.1 */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define HTTP_0_9 HTTP_VERSION_0_9
+# define HTTP_1_0 HTTP_VERSION_1_0
+# define HTTP_1_1 HTTP_VERSION_1_1
+# endif /* !_CUPS_NO_DEPRECATED */
+} http_version_t;
+
+typedef union _http_addr_u /**** Socket address union, which
+ **** makes using IPv6 and other
+ **** address types easier and
+ **** more portable. @since CUPS 1.2/OS X 10.5@
+ ****/
+{
+ struct sockaddr addr; /* Base structure for family value */
+ struct sockaddr_in ipv4; /* IPv4 address */
+#ifdef AF_INET6
+ struct sockaddr_in6 ipv6; /* IPv6 address */
+#endif /* AF_INET6 */
+#ifdef AF_LOCAL
+ struct sockaddr_un un; /* Domain socket file */
+#endif /* AF_LOCAL */
+ char pad[256]; /* Padding to ensure binary compatibility */
+} http_addr_t;
+
+typedef struct http_addrlist_s /**** Socket address list, which is
+ **** used to enumerate all of the
+ **** addresses that are associated
+ **** with a hostname. @since CUPS 1.2/OS X 10.5@
+ ****/
+{
+ struct http_addrlist_s *next; /* Pointer to next address in list */
+ http_addr_t addr; /* Address */
+} http_addrlist_t;
+
+typedef struct _http_s http_t; /**** HTTP connection type ****/
+
+typedef struct http_credential_s /**** HTTP credential data @since CUPS 1.5/OS X 10.7@ ****/
+{
+ void *data; /* Pointer to credential data */
+ size_t datalen; /* Credential length */
+} http_credential_t;
+
+typedef int (*http_timeout_cb_t)(http_t *http, void *user_data);
+ /**** HTTP timeout callback @since CUPS 1.5/OS X 10.7@ ****/
+
+
+
+/*
+ * Prototypes...
+ */
+
+extern void httpBlocking(http_t *http, int b);
+extern int httpCheck(http_t *http);
+extern void httpClearFields(http_t *http);
+extern void httpClose(http_t *http);
+extern http_t *httpConnect(const char *host, int port)
+ _CUPS_DEPRECATED_1_7_MSG("Use httpConnect2 instead.");
+extern http_t *httpConnectEncrypt(const char *host, int port,
+ http_encryption_t encryption)
+ _CUPS_DEPRECATED_1_7_MSG("Use httpConnect2 instead.");
+extern int httpDelete(http_t *http, const char *uri);
+extern int httpEncryption(http_t *http, http_encryption_t e);
+extern int httpError(http_t *http);
+extern void httpFlush(http_t *http);
+extern int httpGet(http_t *http, const char *uri);
+extern char *httpGets(char *line, int length, http_t *http);
+extern const char *httpGetDateString(time_t t);
+extern time_t httpGetDateTime(const char *s);
+extern const char *httpGetField(http_t *http, http_field_t field);
+extern struct hostent *httpGetHostByName(const char *name);
+extern char *httpGetSubField(http_t *http, http_field_t field,
+ const char *name, char *value);
+extern int httpHead(http_t *http, const char *uri);
+extern void httpInitialize(void);
+extern int httpOptions(http_t *http, const char *uri);
+extern int httpPost(http_t *http, const char *uri);
+extern int httpPrintf(http_t *http, const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+extern int httpPut(http_t *http, const char *uri);
+extern int httpRead(http_t *http, char *buffer, int length) _CUPS_DEPRECATED_MSG("Use httpRead2 instead.");
+extern int httpReconnect(http_t *http) _CUPS_DEPRECATED_1_6_MSG("Use httpReconnect2 instead.");
+extern void httpSeparate(const char *uri, char *method,
+ char *username, char *host, int *port,
+ char *resource) _CUPS_DEPRECATED_MSG("Use httpSeparateURI instead.");
+extern void httpSetField(http_t *http, http_field_t field,
+ const char *value);
+extern const char *httpStatus(http_status_t status);
+extern int httpTrace(http_t *http, const char *uri);
+extern http_status_t httpUpdate(http_t *http);
+extern int httpWrite(http_t *http, const char *buffer, int length) _CUPS_DEPRECATED_MSG("Use httpWrite2 instead.");
+extern char *httpEncode64(char *out, const char *in) _CUPS_DEPRECATED_MSG("Use httpEncode64_2 instead.");
+extern char *httpDecode64(char *out, const char *in) _CUPS_DEPRECATED_MSG("Use httpDecode64_2 instead.");
+extern int httpGetLength(http_t *http) _CUPS_DEPRECATED_MSG("Use httpGetLength2 instead.");
+extern char *httpMD5(const char *, const char *, const char *,
+ char [33]);
+extern char *httpMD5Final(const char *, const char *, const char *,
+ char [33]);
+extern char *httpMD5String(const unsigned char *, char [33]);
+
+/**** New in CUPS 1.1.19 ****/
+extern void httpClearCookie(http_t *http) _CUPS_API_1_1_19;
+extern const char *httpGetCookie(http_t *http) _CUPS_API_1_1_19;
+extern void httpSetCookie(http_t *http, const char *cookie) _CUPS_API_1_1_19;
+extern int httpWait(http_t *http, int msec) _CUPS_API_1_1_19;
+
+/**** New in CUPS 1.1.21 ****/
+extern char *httpDecode64_2(char *out, int *outlen, const char *in) _CUPS_API_1_1_21;
+extern char *httpEncode64_2(char *out, int outlen, const char *in,
+ int inlen) _CUPS_API_1_1_21;
+extern void httpSeparate2(const char *uri,
+ char *method, int methodlen,
+ char *username, int usernamelen,
+ char *host, int hostlen, int *port,
+ char *resource, int resourcelen) _CUPS_DEPRECATED_MSG("Use httpSeparateURI instead.");
+
+/**** New in CUPS 1.2/OS X 10.5 ****/
+extern int httpAddrAny(const http_addr_t *addr) _CUPS_API_1_2;
+extern http_addrlist_t *httpAddrConnect(http_addrlist_t *addrlist, int *sock) _CUPS_API_1_2;
+extern int httpAddrEqual(const http_addr_t *addr1,
+ const http_addr_t *addr2) _CUPS_API_1_2;
+extern void httpAddrFreeList(http_addrlist_t *addrlist) _CUPS_API_1_2;
+extern http_addrlist_t *httpAddrGetList(const char *hostname, int family,
+ const char *service) _CUPS_API_1_2;
+extern int httpAddrLength(const http_addr_t *addr) _CUPS_API_1_2;
+extern int httpAddrLocalhost(const http_addr_t *addr) _CUPS_API_1_2;
+extern char *httpAddrLookup(const http_addr_t *addr,
+ char *name, int namelen) _CUPS_API_1_2;
+extern char *httpAddrString(const http_addr_t *addr,
+ char *s, int slen) _CUPS_API_1_2;
+extern http_uri_status_t httpAssembleURI(http_uri_coding_t encoding,
+ char *uri, int urilen,
+ const char *scheme,
+ const char *username,
+ const char *host, int port,
+ const char *resource) _CUPS_API_1_2;
+extern http_uri_status_t httpAssembleURIf(http_uri_coding_t encoding,
+ char *uri, int urilen,
+ const char *scheme,
+ const char *username,
+ const char *host, int port,
+ const char *resourcef, ...) _CUPS_API_1_2;
+extern int httpFlushWrite(http_t *http) _CUPS_API_1_2;
+extern int httpGetBlocking(http_t *http) _CUPS_API_1_2;
+extern const char *httpGetDateString2(time_t t, char *s, int slen) _CUPS_API_1_2;
+extern int httpGetFd(http_t *http) _CUPS_API_1_2;
+extern const char *httpGetHostname(http_t *http, char *s, int slen) _CUPS_API_1_2;
+extern off_t httpGetLength2(http_t *http) _CUPS_API_1_2;
+extern http_status_t httpGetStatus(http_t *http) _CUPS_API_1_2;
+extern char *httpGetSubField2(http_t *http, http_field_t field,
+ const char *name, char *value,
+ int valuelen) _CUPS_API_1_2;
+extern ssize_t httpRead2(http_t *http, char *buffer, size_t length) _CUPS_API_1_2;
+extern http_uri_status_t httpSeparateURI(http_uri_coding_t decoding,
+ const char *uri,
+ char *scheme, int schemelen,
+ char *username, int usernamelen,
+ char *host, int hostlen, int *port,
+ char *resource, int resourcelen) _CUPS_API_1_2;
+extern void httpSetExpect(http_t *http, http_status_t expect) _CUPS_API_1_2;
+extern void httpSetLength(http_t *http, size_t length) _CUPS_API_1_2;
+extern ssize_t httpWrite2(http_t *http, const char *buffer,
+ size_t length) _CUPS_API_1_2;
+
+/**** New in CUPS 1.3/OS X 10.5 ****/
+extern char *httpGetAuthString(http_t *http) _CUPS_API_1_3;
+extern void httpSetAuthString(http_t *http, const char *scheme,
+ const char *data) _CUPS_API_1_3;
+
+/**** New in CUPS 1.5/OS X 10.7 ****/
+extern int httpAddCredential(cups_array_t *credentials,
+ const void *data, size_t datalen)
+ _CUPS_API_1_5;
+extern int httpCopyCredentials(http_t *http,
+ cups_array_t **credentials)
+ _CUPS_API_1_5;
+extern void httpFreeCredentials(cups_array_t *certs) _CUPS_API_1_5;
+extern int httpSetCredentials(http_t *http, cups_array_t *certs)
+ _CUPS_API_1_5;
+extern void httpSetTimeout(http_t *http, double timeout,
+ http_timeout_cb_t cb, void *user_data)
+ _CUPS_API_1_5;
+
+/**** New in CUPS 1.6/OS X 10.8 ****/
+extern http_addrlist_t *httpAddrConnect2(http_addrlist_t *addrlist, int *sock,
+ int msec, int *cancel)
+ _CUPS_API_1_6;
+extern http_state_t httpGetState(http_t *http) _CUPS_API_1_6;
+extern http_version_t httpGetVersion(http_t *http) _CUPS_API_1_6;
+extern int httpReconnect2(http_t *http, int msec, int *cancel)
+ _CUPS_API_1_6;
+
+
+/**** New in CUPS 1.7 ****/
+extern http_t *httpAcceptConnection(int fd, int blocking)
+ _CUPS_API_1_7;
+extern http_addrlist_t *httpAddrCopyList(http_addrlist_t *src) _CUPS_API_1_7;
+extern int httpAddrListen(http_addr_t *addr, int port)
+ _CUPS_API_1_7;
+extern int httpAddrPort(http_addr_t *addr) _CUPS_API_1_7;
+extern char *httpAssembleUUID(const char *server, int port,
+ const char *name, int number,
+ char *buffer, size_t bufsize)
+ _CUPS_API_1_7;
+extern http_t *httpConnect2(const char *host, int port,
+ http_addrlist_t *addrlist,
+ int family, http_encryption_t encryption,
+ int blocking, int msec, int *cancel)
+ _CUPS_API_1_7;
+extern const char *httpGetContentEncoding(http_t *http) _CUPS_API_1_7;
+extern http_status_t httpGetExpect(http_t *http) _CUPS_API_1_7;
+extern ssize_t httpPeek(http_t *http, char *buffer, size_t length)
+ _CUPS_API_1_7;
+extern http_state_t httpReadRequest(http_t *http, char *resource,
+ size_t resourcelen) _CUPS_API_1_7;
+extern void httpSetDefaultField(http_t *http, http_field_t field,
+ const char *value) _CUPS_API_1_7;
+extern http_state_t httpWriteResponse(http_t *http,
+ http_status_t status) _CUPS_API_1_7;
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_HTTP_H_ */
+
+/*
+ * End of "$Id: http.h 11085 2013-07-03 13:53:05Z msweet $".
+ */
diff --git a/cups/libs/cups/ipp-private.h b/cups/libs/cups/ipp-private.h
new file mode 100644
index 000000000..210c2e796
--- /dev/null
+++ b/cups/libs/cups/ipp-private.h
@@ -0,0 +1,81 @@
+/*
+ * "$Id: ipp-private.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Private IPP definitions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_IPP_PRIVATE_H_
+# define _CUPS_IPP_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/ipp.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define IPP_BUF_SIZE (IPP_MAX_LENGTH + 2)
+ /* Size of buffer */
+
+
+/*
+ * Structures...
+ */
+
+typedef struct /**** Attribute mapping data ****/
+{
+ int multivalue; /* Option has multiple values? */
+ const char *name; /* Option/attribute name */
+ ipp_tag_t value_tag; /* Value tag for this attribute */
+ ipp_tag_t group_tag; /* Group tag for this attribute */
+ ipp_tag_t alt_group_tag; /* Alternate group tag for this
+ * attribute */
+ const ipp_op_t *operations; /* Allowed operations for this attr */
+} _ipp_option_t;
+
+
+/*
+ * Prototypes for private functions...
+ */
+
+#ifdef DEBUG
+extern const char *_ippCheckOptions(void);
+#endif /* DEBUG */
+extern _ipp_option_t *_ippFindOption(const char *name);
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_IPP_H_ */
+
+/*
+ * End of "$Id: ipp-private.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/ipp-support.c b/cups/libs/cups/ipp-support.c
new file mode 100644
index 000000000..2eaa8e530
--- /dev/null
+++ b/cups/libs/cups/ipp-support.c
@@ -0,0 +1,2258 @@
+/*
+ * "$Id: ipp-support.c 11734 2014-03-25 18:01:47Z msweet $"
+ *
+ * Internet Printing Protocol support functions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ippAttributeString() - Convert the attribute's value to a string.
+ * ippCreateRequestedArray() - Create a CUPS array of attribute names from
+ * the given requested-attributes attribute.
+ * ippEnumString() - Return a string corresponding to the enum
+ * value.
+ * ippEnumValue() - Return the value associated with a given enum
+ * string.
+ * ippErrorString() - Return a name for the given status code.
+ * ippErrorValue() - Return a status code for the given name.
+ * ippOpString() - Return a name for the given operation id.
+ * ippOpValue() - Return an operation id for the given name.
+ * ippPort() - Return the default IPP port number.
+ * ippSetPort() - Set the default port number.
+ * ippTagString() - Return the tag name corresponding to a tag
+ * value.
+ * ippTagValue() - Return the tag value corresponding to a tag
+ * name.
+ * ipp_col_string() - Convert a collection to a string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local globals...
+ */
+
+static const char * const ipp_status_oks[] = /* "OK" status codes */
+ { /* (name) = abandoned standard value */
+ "successful-ok",
+ "successful-ok-ignored-or-substituted-attributes",
+ "successful-ok-conflicting-attributes",
+ "successful-ok-ignored-subscriptions",
+ "(successful-ok-ignored-notifications)",
+ "successful-ok-too-many-events",
+ "(successful-ok-but-cancel-subscription)",
+ "successful-ok-events-complete"
+ },
+ * const ipp_status_400s[] = /* Client errors */
+ { /* (name) = abandoned standard value */
+ "client-error-bad-request",
+ "client-error-forbidden",
+ "client-error-not-authenticated",
+ "client-error-not-authorized",
+ "client-error-not-possible",
+ "client-error-timeout",
+ "client-error-not-found",
+ "client-error-gone",
+ "client-error-request-entity-too-large",
+ "client-error-request-value-too-long",
+ "client-error-document-format-not-supported",
+ "client-error-attributes-or-values-not-supported",
+ "client-error-uri-scheme-not-supported",
+ "client-error-charset-not-supported",
+ "client-error-conflicting-attributes",
+ "client-error-compression-not-supported",
+ "client-error-compression-error",
+ "client-error-document-format-error",
+ "client-error-document-access-error",
+ "client-error-attributes-not-settable",
+ "client-error-ignored-all-subscriptions",
+ "client-error-too-many-subscriptions",
+ "(client-error-ignored-all-notifications)",
+ "(client-error-client-print-support-file-not-found)",
+ "client-error-document-password-error",
+ "client-error-document-permission-error",
+ "client-error-document-security-error",
+ "client-error-document-unprintable-error"
+ },
+ * const ipp_status_480s[] = /* Vendor client errors */
+ {
+ /* 0x0480 - 0x048F */
+ "0x0480",
+ "0x0481",
+ "0x0482",
+ "0x0483",
+ "0x0484",
+ "0x0485",
+ "0x0486",
+ "0x0487",
+ "0x0488",
+ "0x0489",
+ "0x048A",
+ "0x048B",
+ "0x048C",
+ "0x048D",
+ "0x048E",
+ "0x048F",
+ /* 0x0490 - 0x049F */
+ "0x0490",
+ "0x0491",
+ "0x0492",
+ "0x0493",
+ "0x0494",
+ "0x0495",
+ "0x0496",
+ "0x0497",
+ "0x0498",
+ "0x0499",
+ "0x049A",
+ "0x049B",
+ "cups-error-account-info-needed",
+ "cups-error-account-closed",
+ "cups-error-account-limit-reached",
+ "cups-error-account-authorization-failed"
+ },
+ * const ipp_status_500s[] = /* Server errors */
+ {
+ "server-error-internal-error",
+ "server-error-operation-not-supported",
+ "server-error-service-unavailable",
+ "server-error-version-not-supported",
+ "server-error-device-error",
+ "server-error-temporary-error",
+ "server-error-not-accepting-jobs",
+ "server-error-busy",
+ "server-error-job-canceled",
+ "server-error-multiple-document-jobs-not-supported",
+ "server-error-printer-is-deactivated",
+ "server-error-too-many-jobs",
+ "server-error-too-many-documents"
+ },
+ * const ipp_status_1000s[] = /* CUPS internal */
+ {
+ "cups-authentication-canceled",
+ "cups-pki-error",
+ "cups-upgrade-required"
+ };
+static const char * const ipp_std_ops[] =
+ {
+ /* 0x0000 - 0x000f */
+ "0x0000",
+ "0x0001",
+ "Print-Job",
+ "Print-URI",
+ "Validate-Job",
+ "Create-Job",
+ "Send-Document",
+ "Send-URI",
+ "Cancel-Job",
+ "Get-Job-Attributes",
+ "Get-Jobs",
+ "Get-Printer-Attributes",
+ "Hold-Job",
+ "Release-Job",
+ "Restart-Job",
+ "0x000f",
+
+ /* 0x0010 - 0x001f */
+ "Pause-Printer",
+ "Resume-Printer",
+ "Purge-Jobs",
+ "Set-Printer-Attributes",
+ "Set-Job-Attributes",
+ "Get-Printer-Supported-Values",
+ "Create-Printer-Subscriptions",
+ "Create-Job-Subscriptions",
+ "Get-Subscription-Attributes",
+ "Get-Subscriptions",
+ "Renew-Subscription",
+ "Cancel-Subscription",
+ "Get-Notifications",
+ "(Send-Notifications)",
+ "(Get-Resource-Attributes)",
+ "(Get-Resource-Data)",
+
+ /* 0x0020 - 0x002f */
+ "(Get-Resources)",
+ "(Get-Printer-Support-Files)",
+ "Enable-Printer",
+ "Disable-Printer",
+ "Pause-Printer-After-Current-Job",
+ "Hold-New-Jobs",
+ "Release-Held-New-Jobs",
+ "Deactivate-Printer",
+ "Activate-Printer",
+ "Restart-Printer",
+ "Shutdown-Printer",
+ "Startup-Printer",
+ "Reprocess-Job",
+ "Cancel-Current-Job",
+ "Suspend-Current-Job",
+ "Resume-Job",
+
+ /* 0x0030 - 0x003e */
+ "Promote-Job",
+ "Schedule-Job-After",
+ "0x0032",
+ "Cancel-Document",
+ "Get-Document-Attributes",
+ "Get-Documents",
+ "Delete-Document",
+ "Set-Document-Attributes",
+ "Cancel-Jobs",
+ "Cancel-My-Jobs",
+ "Resubmit-Job",
+ "Close-Job",
+ "Identify-Printer",
+ "Validate-Document",
+ "Send-Hardcopy-Document"
+ },
+ * const ipp_cups_ops[] =
+ {
+ "CUPS-Get-Default",
+ "CUPS-Get-Printers",
+ "CUPS-Add-Modify-Printer",
+ "CUPS-Delete-Printer",
+ "CUPS-Get-Classes",
+ "CUPS-Add-Modify-Class",
+ "CUPS-Delete-Class",
+ "CUPS-Accept-Jobs",
+ "CUPS-Reject-Jobs",
+ "CUPS-Set-Default",
+ "CUPS-Get-Devices",
+ "CUPS-Get-PPDs",
+ "CUPS-Move-Job",
+ "CUPS-Authenticate-Job",
+ "CUPS-Get-PPD"
+ },
+ * const ipp_cups_ops2[] =
+ {
+ "CUPS-Get-Document"
+ },
+ * const ipp_tag_names[] =
+ { /* Value/group tag names */
+ "zero", /* 0x00 */
+ "operation-attributes-tag",
+ /* 0x01 */
+ "job-attributes-tag", /* 0x02 */
+ "end-of-attributes-tag",
+ /* 0x03 */
+ "printer-attributes-tag",
+ /* 0x04 */
+ "unsupported-attributes-tag",
+ /* 0x05 */
+ "subscription-attributes-tag",
+ /* 0x06 */
+ "event-notification-attributes-tag",
+ /* 0x07 */
+ "(resource-attributes-tag)",
+ /* 0x08 */
+ "document-attributes-tag",
+ /* 0x09 */
+ "0x0a", /* 0x0a */
+ "0x0b", /* 0x0b */
+ "0x0c", /* 0x0c */
+ "0x0d", /* 0x0d */
+ "0x0e", /* 0x0e */
+ "0x0f", /* 0x0f */
+ "unsupported", /* 0x10 */
+ "default", /* 0x11 */
+ "unknown", /* 0x12 */
+ "no-value", /* 0x13 */
+ "0x14", /* 0x14 */
+ "not-settable", /* 0x15 */
+ "delete-attribute", /* 0x16 */
+ "admin-define", /* 0x17 */
+ "0x18", /* 0x18 */
+ "0x19", /* 0x19 */
+ "0x1a", /* 0x1a */
+ "0x1b", /* 0x1b */
+ "0x1c", /* 0x1c */
+ "0x1d", /* 0x1d */
+ "0x1e", /* 0x1e */
+ "0x1f", /* 0x1f */
+ "0x20", /* 0x20 */
+ "integer", /* 0x21 */
+ "boolean", /* 0x22 */
+ "enum", /* 0x23 */
+ "0x24", /* 0x24 */
+ "0x25", /* 0x25 */
+ "0x26", /* 0x26 */
+ "0x27", /* 0x27 */
+ "0x28", /* 0x28 */
+ "0x29", /* 0x29 */
+ "0x2a", /* 0x2a */
+ "0x2b", /* 0x2b */
+ "0x2c", /* 0x2c */
+ "0x2d", /* 0x2d */
+ "0x2e", /* 0x2e */
+ "0x2f", /* 0x2f */
+ "octetString", /* 0x30 */
+ "dateTime", /* 0x31 */
+ "resolution", /* 0x32 */
+ "rangeOfInteger", /* 0x33 */
+ "collection", /* 0x34 */
+ "textWithLanguage", /* 0x35 */
+ "nameWithLanguage", /* 0x36 */
+ "endCollection", /* 0x37 */
+ "0x38", /* 0x38 */
+ "0x39", /* 0x39 */
+ "0x3a", /* 0x3a */
+ "0x3b", /* 0x3b */
+ "0x3c", /* 0x3c */
+ "0x3d", /* 0x3d */
+ "0x3e", /* 0x3e */
+ "0x3f", /* 0x3f */
+ "0x40", /* 0x40 */
+ "textWithoutLanguage",/* 0x41 */
+ "nameWithoutLanguage",/* 0x42 */
+ "0x43", /* 0x43 */
+ "keyword", /* 0x44 */
+ "uri", /* 0x45 */
+ "uriScheme", /* 0x46 */
+ "charset", /* 0x47 */
+ "naturalLanguage", /* 0x48 */
+ "mimeMediaType", /* 0x49 */
+ "memberAttrName" /* 0x4a */
+ };
+static const char * const ipp_document_states[] =
+ { /* document-state-enums */
+ "pending",
+ "4",
+ "processing",
+ "6",
+ "canceled",
+ "aborted",
+ "completed"
+ },
+ * const ipp_finishings[] =
+ { /* finishings enums */
+ "none",
+ "staple",
+ "punch",
+ "cover",
+ "bind",
+ "saddle-stitch",
+ "edge-stitch",
+ "fold",
+ "trim",
+ "bale",
+ "booklet-maker",
+ "jog-offset",
+ "15",
+ "16",
+ "17",
+ "18",
+ "19",
+ "staple-top-left",
+ "staple-bottom-left",
+ "staple-top-right",
+ "staple-bottom-right",
+ "edge-stitch-left",
+ "edge-stitch-top",
+ "edge-stitch-right",
+ "edge-stitch-bottom",
+ "staple-dual-left",
+ "staple-dual-top",
+ "staple-dual-right",
+ "staple-dual-bottom",
+ "32",
+ "33",
+ "34",
+ "35",
+ "36",
+ "37",
+ "38",
+ "39",
+ "40",
+ "41",
+ "42",
+ "43",
+ "44",
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "bind-left",
+ "bind-top",
+ "bind-right",
+ "bind-bottom",
+ "54",
+ "55",
+ "56",
+ "57",
+ "58",
+ "59",
+ "trim-after-pages",
+ "trim-after-documents",
+ "trim-after-copies",
+ "trim-after-job",
+ "64",
+ "65",
+ "66",
+ "67",
+ "68",
+ "69",
+ "punch-top-left",
+ "punch-bottom-left",
+ "punch-top-right",
+ "punch-bottom-right",
+ "punch-dual-left",
+ "punch-dual-top",
+ "punch-dual-right",
+ "punch-dual-bottom",
+ "punch-triple-left",
+ "punch-triple-top",
+ "punch-triple-right",
+ "punch-triple-bottom",
+ "punch-quad-left",
+ "punch-quad-top",
+ "punch-quad-right",
+ "punch-quad-bottom",
+ "86",
+ "87",
+ "88",
+ "89",
+ "fold-accordian",
+ "fold-double-gate",
+ "fold-gate",
+ "fold-half",
+ "fold-half-z",
+ "fold-left-gate",
+ "fold-letter",
+ "fold-parallel",
+ "fold-poster",
+ "fold-right-gate",
+ "fold-z"
+ },
+ * const ipp_finishings_vendor[] =
+ {
+ /* 0x40000000 to 0x4000000F */
+ "0x40000000",
+ "0x40000001",
+ "0x40000002",
+ "0x40000003",
+ "0x40000004",
+ "0x40000005",
+ "0x40000006",
+ "0x40000007",
+ "0x40000008",
+ "0x40000009",
+ "0x4000000A",
+ "0x4000000B",
+ "0x4000000C",
+ "0x4000000D",
+ "0x4000000E",
+ "0x4000000F",
+ /* 0x40000010 to 0x4000001F */
+ "0x40000010",
+ "0x40000011",
+ "0x40000012",
+ "0x40000013",
+ "0x40000014",
+ "0x40000015",
+ "0x40000016",
+ "0x40000017",
+ "0x40000018",
+ "0x40000019",
+ "0x4000001A",
+ "0x4000001B",
+ "0x4000001C",
+ "0x4000001D",
+ "0x4000001E",
+ "0x4000001F",
+ /* 0x40000020 to 0x4000002F */
+ "0x40000020",
+ "0x40000021",
+ "0x40000022",
+ "0x40000023",
+ "0x40000024",
+ "0x40000025",
+ "0x40000026",
+ "0x40000027",
+ "0x40000028",
+ "0x40000029",
+ "0x4000002A",
+ "0x4000002B",
+ "0x4000002C",
+ "0x4000002D",
+ "0x4000002E",
+ "0x4000002F",
+ /* 0x40000030 to 0x4000003F */
+ "0x40000030",
+ "0x40000031",
+ "0x40000032",
+ "0x40000033",
+ "0x40000034",
+ "0x40000035",
+ "0x40000036",
+ "0x40000037",
+ "0x40000038",
+ "0x40000039",
+ "0x4000003A",
+ "0x4000003B",
+ "0x4000003C",
+ "0x4000003D",
+ "0x4000003E",
+ "0x4000003F",
+ /* 0x40000040 - 0x4000004F */
+ "0x40000040",
+ "0x40000041",
+ "0x40000042",
+ "0x40000043",
+ "0x40000044",
+ "0x40000045",
+ "cups-punch-top-left",
+ "cups-punch-bottom-left",
+ "cups-punch-top-right",
+ "cups-punch-bottom-right",
+ "cups-punch-dual-left",
+ "cups-punch-dual-top",
+ "cups-punch-dual-right",
+ "cups-punch-dual-bottom",
+ "cups-punch-triple-left",
+ "cups-punch-triple-top",
+ /* 0x40000050 - 0x4000005F */
+ "cups-punch-triple-right",
+ "cups-punch-triple-bottom",
+ "cups-punch-quad-left",
+ "cups-punch-quad-top",
+ "cups-punch-quad-right",
+ "cups-punch-quad-bottom",
+ "0x40000056",
+ "0x40000057",
+ "0x40000058",
+ "0x40000059",
+ "cups-fold-accordian",
+ "cups-fold-double-gate",
+ "cups-fold-gate",
+ "cups-fold-half",
+ "cups-fold-half-z",
+ "cups-fold-left-gate",
+ /* 0x40000060 - 0x40000064 */
+ "cups-fold-letter",
+ "cups-fold-parallel",
+ "cups-fold-poster",
+ "cups-fold-right-gate",
+ "cups-fold-z"
+ },
+ * const ipp_job_collation_types[] =
+ { /* job-collation-type enums */
+ "uncollated-sheets",
+ "collated-documents",
+ "uncollated-documents"
+ },
+ * const ipp_job_states[] =
+ { /* job-state enums */
+ "pending",
+ "pending-held",
+ "processing",
+ "processing-stopped",
+ "canceled",
+ "aborted",
+ "completed"
+ },
+ * const ipp_orientation_requesteds[] =
+ { /* orientation-requested enums */
+ "portrait",
+ "landscape",
+ "reverse-landscape",
+ "reverse-portrait"
+ },
+ * const ipp_print_qualities[] =
+ { /* print-quality enums */
+ "draft",
+ "normal",
+ "high"
+ },
+ * const ipp_printer_states[] =
+ { /* printer-state enums */
+ "idle",
+ "processing",
+ "stopped",
+ };
+
+
+/*
+ * Local functions...
+ */
+
+static size_t ipp_col_string(ipp_t *col, char *buffer, size_t bufsize);
+
+
+/*
+ * 'ippAttributeString()' - Convert the attribute's value to a string.
+ *
+ * Returns the number of bytes that would be written, not including the
+ * trailing nul. The buffer pointer can be NULL to get the required length,
+ * just like (v)snprintf.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+size_t /* O - Number of bytes less nul */
+ippAttributeString(
+ ipp_attribute_t *attr, /* I - Attribute */
+ char *buffer, /* I - String buffer or NULL */
+ size_t bufsize) /* I - Size of string buffer */
+{
+ int i; /* Looping var */
+ char *bufptr, /* Pointer into buffer */
+ *bufend, /* End of buffer */
+ temp[256]; /* Temporary string */
+ const char *ptr, /* Pointer into string */
+ *end; /* Pointer to end of string */
+ _ipp_value_t *val; /* Current value */
+
+
+ if (!attr || !attr->name)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (0);
+ }
+
+ bufptr = buffer;
+ if (buffer)
+ bufend = buffer + bufsize - 1;
+ else
+ bufend = NULL;
+
+ for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
+ {
+ if (val > attr->values)
+ {
+ if (buffer && bufptr < bufend)
+ *bufptr++ = ',';
+ else
+ bufptr ++;
+ }
+
+ switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
+ {
+ case IPP_TAG_ENUM :
+ ptr = ippEnumString(attr->name, val->integer);
+
+ if (buffer && bufptr < bufend)
+ strlcpy(bufptr, ptr, bufend - bufptr + 1);
+
+ bufptr += strlen(ptr);
+ break;
+
+ case IPP_TAG_INTEGER :
+ if (buffer && bufptr < bufend)
+ bufptr += snprintf(bufptr, bufend - bufptr + 1, "%d", val->integer);
+ else
+ bufptr += snprintf(temp, sizeof(temp), "%d", val->integer);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (buffer && bufptr < bufend)
+ strlcpy(bufptr, val->boolean ? "true" : "false",
+ bufend - bufptr + 1);
+
+ bufptr += val->boolean ? 4 : 5;
+ break;
+
+ case IPP_TAG_RANGE :
+ if (buffer && bufptr < bufend)
+ bufptr += snprintf(bufptr, bufend - bufptr + 1, "%d-%d",
+ val->range.lower, val->range.upper);
+ else
+ bufptr += snprintf(temp, sizeof(temp), "%d-%d", val->range.lower,
+ val->range.upper);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ if (buffer && bufptr < bufend)
+ bufptr += snprintf(bufptr, bufend - bufptr + 1, "%dx%d%s",
+ val->resolution.xres, val->resolution.yres,
+ val->resolution.units == IPP_RES_PER_INCH ?
+ "dpi" : "dpcm");
+ else
+ bufptr += snprintf(temp, sizeof(temp), "%dx%d%s",
+ val->resolution.xres, val->resolution.yres,
+ val->resolution.units == IPP_RES_PER_INCH ?
+ "dpi" : "dpcm");
+ break;
+
+ case IPP_TAG_DATE :
+ {
+ unsigned year; /* Year */
+
+ year = (val->date[0] << 8) + val->date[1];
+
+ if (val->date[9] == 0 && val->date[10] == 0)
+ snprintf(temp, sizeof(temp), "%04u-%02u-%02uT%02u:%02u:%02uZ",
+ year, val->date[2], val->date[3], val->date[4],
+ val->date[5], val->date[6]);
+ else
+ snprintf(temp, sizeof(temp),
+ "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
+ year, val->date[2], val->date[3], val->date[4],
+ val->date[5], val->date[6], val->date[8], val->date[9],
+ val->date[10]);
+
+ if (buffer && bufptr < bufend)
+ strlcpy(bufptr, temp, bufend - bufptr + 1);
+
+ bufptr += strlen(temp);
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_MIMETYPE :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ if (!val->string.text)
+ break;
+
+ for (ptr = val->string.text; *ptr; ptr ++)
+ {
+ if (*ptr == '\\' || *ptr == '\"' || *ptr == '[')
+ {
+ if (buffer && bufptr < bufend)
+ *bufptr = '\\';
+ bufptr ++;
+ }
+
+ if (buffer && bufptr < bufend)
+ *bufptr = *ptr;
+ bufptr ++;
+ }
+
+ if (val->string.language)
+ {
+ /*
+ * Add "[language]" to end of string...
+ */
+
+ if (buffer && bufptr < bufend)
+ *bufptr = '[';
+ bufptr ++;
+
+ if (buffer && bufptr < bufend)
+ strlcpy(bufptr, val->string.language, bufend - bufptr);
+ bufptr += strlen(val->string.language);
+
+ if (buffer && bufptr < bufend)
+ *bufptr = ']';
+ bufptr ++;
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ if (buffer && bufptr < bufend)
+ bufptr += ipp_col_string(val->collection, bufptr,
+ bufend - bufptr + 1);
+ else
+ bufptr += ipp_col_string(val->collection, NULL, 0);
+ break;
+
+ case IPP_TAG_STRING :
+ for (ptr = val->unknown.data, end = ptr + val->unknown.length;
+ ptr < end; ptr ++)
+ {
+ if (*ptr == '\\' || _cups_isspace(*ptr))
+ {
+ if (buffer && bufptr < bufend)
+ *bufptr = '\\';
+ bufptr ++;
+
+ if (buffer && bufptr < bufend)
+ *bufptr = *ptr;
+ bufptr ++;
+ }
+ else if (!isprint(*ptr & 255))
+ {
+ if (buffer && bufptr < bufend)
+ bufptr += snprintf(bufptr, bufend - bufptr + 1, "\\%03o",
+ *ptr & 255);
+ else
+ bufptr += snprintf(temp, sizeof(temp), "\\%03o",
+ *ptr & 255);
+ }
+ else
+ {
+ if (buffer && bufptr < bufend)
+ *bufptr = *ptr;
+ bufptr ++;
+ }
+ }
+ break;
+
+ default :
+ ptr = ippTagString(attr->value_tag);
+ if (buffer && bufptr < bufend)
+ strlcpy(bufptr, ptr, bufend - bufptr + 1);
+ bufptr += strlen(ptr);
+ break;
+ }
+ }
+
+ if (buffer && bufptr < bufend)
+ *bufptr = '\0';
+ else if (bufend)
+ *bufend = '\0';
+
+ return (bufptr - buffer);
+}
+
+
+/*
+ * 'ippCreateRequestedArray()' - Create a CUPS array of attribute names from the
+ * given requested-attributes attribute.
+ *
+ * This function creates a (sorted) CUPS array of attribute names matching the
+ * list of "requested-attribute" values supplied in an IPP request. All IANA-
+ * registered values are supported in addition to the CUPS IPP extension
+ * attributes.
+ *
+ * The @code request@ parameter specifies the request message that was read from
+ * the client.
+ *
+ * @code NULL@ is returned if all attributes should be returned. Otherwise, the
+ * result is a sorted array of attribute names, where @code cupsArrayFind(array,
+ * "attribute-name")@ will return a non-NULL pointer. The array must be freed
+ * using the @code cupsArrayDelete@ function.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+cups_array_t * /* O - CUPS array or @code NULL@ if all */
+ippCreateRequestedArray(ipp_t *request) /* I - IPP request */
+{
+ int i, j, /* Looping vars */
+ count, /* Number of values */
+ added; /* Was name added? */
+ ipp_attribute_t *requested; /* requested-attributes attribute */
+ cups_array_t *ra; /* Requested attributes array */
+ const char *value; /* Current value */
+ /* The following lists come from the current IANA IPP registry of attributes */
+ static const char * const document_description[] =
+ { /* document-description group */
+ "compression",
+ "copies-actual",
+ "cover-back-actual",
+ "cover-front-actual",
+ "current-page-order",
+ "date-time-at-completed",
+ "date-time-at-creation",
+ "date-time-at-processing",
+ "detailed-status-messages",
+ "document-access-errors",
+ "document-charset",
+ "document-digital-signature",
+ "document-format",
+ "document-format-details",
+ "document-format-detected",
+ "document-format-version",
+ "document-format-version-detected",
+ "document-job-id",
+ "document-job-uri",
+ "document-message",
+ "document-metadata",
+ "document-name",
+ "document-natural-language",
+ "document-number",
+ "document-printer-uri",
+ "document-state",
+ "document-state-message",
+ "document-state-reasons",
+ "document-uri",
+ "document-uuid",
+ "errors-count",
+ "finishings-actual",
+ "finishings-col-actual",
+ "force-front-side-actual",
+ "imposition-template-actual",
+ "impressions",
+ "impressions-completed",
+ "impressions-completed-current-copy",
+ "insert-sheet-actual",
+ "k-octets",
+ "k-octets-processed",
+ "last-document",
+ "media-actual",
+ "media-col-actual",
+ "media-input-tray-check-actual",
+ "media-sheets",
+ "media-sheets-completed",
+ "more-info",
+ "number-up-actual",
+ "orientation-requested-actual",
+ "output-bin-actual",
+ "output-device-assigned",
+ "overrides-actual",
+ "page-delivery-actual",
+ "page-order-received-actual",
+ "page-ranges-actual",
+ "pages",
+ "pages-completed",
+ "pages-completed-current-copy",
+ "presentation-direction-number-up-actual",
+ "print-color-mode-actual",
+ "print-content-optimize-actual",
+ "print-quality-actual",
+ "print-rendering-intent-actual",
+ "print-scaling-actual", /* IPP Paid Printing */
+ "printer-resolution-actual",
+ "printer-up-time",
+ "separator-sheets-actual",
+ "sheet-completed-copy-number",
+ "sides-actual",
+ "time-at-completed",
+ "time-at-creation",
+ "time-at-processing",
+ "x-image-position-actual",
+ "x-image-shift-actual",
+ "x-side1-image-shift-actual",
+ "x-side2-image-shift-actual",
+ "y-image-position-actual",
+ "y-image-shift-actual",
+ "y-side1-image-shift-actual",
+ "y-side2-image-shift-actual"
+ };
+ static const char * const document_template[] =
+ { /* document-template group */
+ "copies",
+ "copies-default",
+ "copies-supported",
+ "cover-back",
+ "cover-back-default",
+ "cover-back-supported",
+ "cover-front",
+ "cover-front-default",
+ "cover-front-supported",
+ "feed-orientation",
+ "feed-orientation-default",
+ "feed-orientation-supported",
+ "finishings",
+ "finishings-col",
+ "finishings-col-default",
+ "finishings-col-supported",
+ "finishings-default",
+ "finishings-supported",
+ "font-name-requested",
+ "font-name-requested-default",
+ "font-name-requested-supported",
+ "font-size-requested",
+ "font-size-requested-default",
+ "font-size-requested-supported",
+ "force-front-side",
+ "force-front-side-default",
+ "force-front-side-supported",
+ "imposition-template",
+ "imposition-template-default",
+ "imposition-template-supported",
+ "insert-after-page-number-supported",
+ "insert-count-supported",
+ "insert-sheet",
+ "insert-sheet-default",
+ "insert-sheet-supported",
+ "max-stitching-locations-supported",
+ "media",
+ "media-back-coating-supported",
+ "media-bottom-margin-supported",
+ "media-col",
+ "media-col-default",
+ "media-col-supported",
+ "media-color-supported",
+ "media-default",
+ "media-front-coating-supported",
+ "media-grain-supported",
+ "media-hole-count-supported",
+ "media-info-supported",
+ "media-input-tray-check",
+ "media-input-tray-check-default",
+ "media-input-tray-check-supported",
+ "media-key-supported",
+ "media-left-margin-supported",
+ "media-order-count-supported",
+ "media-pre-printed-supported",
+ "media-recycled-supported",
+ "media-right-margin-supported",
+ "media-size-supported",
+ "media-source-supported",
+ "media-supported",
+ "media-thickness-supported",
+ "media-top-margin-supported",
+ "media-type-supported",
+ "media-weight-metric-supported",
+ "multiple-document-handling",
+ "multiple-document-handling-default",
+ "multiple-document-handling-supported",
+ "number-up",
+ "number-up-default",
+ "number-up-supported",
+ "orientation-requested",
+ "orientation-requested-default",
+ "orientation-requested-supported",
+ "output-mode", /* CUPS extension */
+ "output-mode-default", /* CUPS extension */
+ "output-mode-supported", /* CUPS extension */
+ "overrides",
+ "overrides-supported",
+ "page-delivery",
+ "page-delivery-default",
+ "page-delivery-supported",
+ "page-order-received",
+ "page-order-received-default",
+ "page-order-received-supported",
+ "page-ranges",
+ "page-ranges-supported",
+ "pages-per-subset",
+ "pages-per-subset-supported",
+ "pdl-init-file",
+ "pdl-init-file-default",
+ "pdl-init-file-entry-supported",
+ "pdl-init-file-location-supported",
+ "pdl-init-file-name-subdirectory-supported",
+ "pdl-init-file-name-supported",
+ "pdl-init-file-supported",
+ "presentation-direction-number-up",
+ "presentation-direction-number-up-default",
+ "presentation-direction-number-up-supported",
+ "print-color-mode",
+ "print-color-mode-default",
+ "print-color-mode-supported",
+ "print-content-optimize",
+ "print-content-optimize-default",
+ "print-content-optimize-supported",
+ "print-quality",
+ "print-quality-default",
+ "print-quality-supported",
+ "print-rendering-intent",
+ "print-rendering-intent-default",
+ "print-rendering-intent-supported",
+ "print-scaling", /* IPP Paid Printing */
+ "print-scaling-default", /* IPP Paid Printing */
+ "print-scaling-supported", /* IPP Paid Printing */
+ "printer-resolution",
+ "printer-resolution-default",
+ "printer-resolution-supported",
+ "separator-sheets",
+ "separator-sheets-default",
+ "separator-sheets-supported",
+ "sheet-collate",
+ "sheet-collate-default",
+ "sheet-collate-supported",
+ "sides",
+ "sides-default",
+ "sides-supported",
+ "stitching-locations-supported",
+ "stitching-offset-supported",
+ "x-image-position",
+ "x-image-position-default",
+ "x-image-position-supported",
+ "x-image-shift",
+ "x-image-shift-default",
+ "x-image-shift-supported",
+ "x-side1-image-shift",
+ "x-side1-image-shift-default",
+ "x-side1-image-shift-supported",
+ "x-side2-image-shift",
+ "x-side2-image-shift-default",
+ "x-side2-image-shift-supported",
+ "y-image-position",
+ "y-image-position-default",
+ "y-image-position-supported",
+ "y-image-shift",
+ "y-image-shift-default",
+ "y-image-shift-supported",
+ "y-side1-image-shift",
+ "y-side1-image-shift-default",
+ "y-side1-image-shift-supported",
+ "y-side2-image-shift",
+ "y-side2-image-shift-default",
+ "y-side2-image-shift-supported"
+ };
+ static const char * const job_description[] =
+ { /* job-description group */
+ "compression-supplied",
+ "copies-actual",
+ "cover-back-actual",
+ "cover-front-actual",
+ "current-page-order",
+ "date-time-at-completed",
+ "date-time-at-creation",
+ "date-time-at-processing",
+ "destination-statuses",
+ "document-charset-supplied",
+ "document-digital-signature-supplied",
+ "document-format-details-supplied",
+ "document-format-supplied",
+ "document-message-supplied",
+ "document-metadata",
+ "document-name-supplied",
+ "document-natural-language-supplied",
+ "document-overrides-actual",
+ "errors-count",
+ "finishings-actual",
+ "finishings-col-actual",
+ "force-front-side-actual",
+ "imposition-template-actual",
+ "impressions-completed-current-copy",
+ "insert-sheet-actual",
+ "job-account-id-actual",
+ "job-accounting-sheets-actual",
+ "job-accounting-user-id-actual",
+ "job-attribute-fidelity",
+ "job-charge-info", /* CUPS extension */
+ "job-collation-type",
+ "job-collation-type-actual",
+ "job-copies-actual",
+ "job-cover-back-actual",
+ "job-cover-front-actual",
+ "job-detailed-status-message",
+ "job-document-access-errors",
+ "job-error-sheet-actual",
+ "job-finishings-actual",
+ "job-finishings-col-actual",
+ "job-hold-until-actual",
+ "job-id",
+ "job-impressions",
+ "job-impressions-completed",
+ "job-k-octets",
+ "job-k-octets-processed",
+ "job-mandatory-attributes",
+ "job-media-progress", /* CUPS extension */
+ "job-media-sheets",
+ "job-media-sheets-completed",
+ "job-message-from-operator",
+ "job-more-info",
+ "job-name",
+ "job-originating-host-name", /* CUPS extension */
+ "job-originating-user-name",
+ "job-originating-user-uri",
+ "job-pages",
+ "job-pages-completed",
+ "job-pages-completed-current-copy",
+ "job-printer-state-message", /* CUPS extension */
+ "job-printer-state-reasons", /* CUPS extension */
+ "job-printer-up-time",
+ "job-printer-uri",
+ "job-priority-actual",
+ "job-save-printer-make-and-model",
+ "job-sheet-message-actual",
+ "job-sheets-actual",
+ "job-sheets-col-actual",
+ "job-state",
+ "job-state-message",
+ "job-state-reasons",
+ "job-uri",
+ "job-uuid",
+ "media-actual",
+ "media-col-actual",
+ "media-check-input-tray-actual",
+ "multiple-document-handling-actual",
+ "number-of-documents",
+ "number-of-intervening-jobs",
+ "number-up-actual",
+ "orientation-requested-actual",
+ "original-requesting-user-name",
+ "output-bin-actual",
+ "output-device-assigned",
+ "overrides-actual",
+ "page-delivery-actual",
+ "page-order-received-actual",
+ "page-ranges-actual",
+ "presentation-direction-number-up-actual",
+ "print-color-mode-actual",
+ "print-content-optimize-actual",
+ "print-quality-actual",
+ "print-rendering-intent-actual",
+ "print-scaling-actual", /* IPP Paid Printing */
+ "printer-resolution-actual",
+ "separator-sheets-actual",
+ "sheet-collate-actual",
+ "sheet-completed-copy-number",
+ "sheet-completed-document-number",
+ "sides-actual",
+ "time-at-completed",
+ "time-at-creation",
+ "time-at-processing",
+ "warnings-count",
+ "x-image-position-actual",
+ "x-image-shift-actual",
+ "x-side1-image-shift-actual",
+ "x-side2-image-shift-actual",
+ "y-image-position-actual",
+ "y-image-shift-actual",
+ "y-side1-image-shift-actual",
+ "y-side2-image-shift-actual"
+ };
+ static const char * const job_template[] =
+ { /* job-template group */
+ "confirmation-sheet-print", /* IPP FaxOut */
+ "confirmation-sheet-print-default",
+ "copies",
+ "copies-default",
+ "copies-supported",
+ "cover-back",
+ "cover-back-default",
+ "cover-back-supported",
+ "cover-front",
+ "cover-front-default",
+ "cover-front-supported",
+ "cover-sheet-info", /* IPP FaxOut */
+ "cover-sheet-info-default",
+ "cover-sheet-info-supported",
+ "destination-uri-schemes-supported",/* IPP FaxOut */
+ "destination-uris", /* IPP FaxOut */
+ "destination-uris-supported",
+ "feed-orientation",
+ "feed-orientation-default",
+ "feed-orientation-supported",
+ "finishings",
+ "finishings-col",
+ "finishings-col-default",
+ "finishings-col-supported",
+ "finishings-default",
+ "finishings-supported",
+ "font-name-requested",
+ "font-name-requested-default",
+ "font-name-requested-supported",
+ "font-size-requested",
+ "font-size-requested-default",
+ "font-size-requested-supported",
+ "force-front-side",
+ "force-front-side-default",
+ "force-front-side-supported",
+ "imposition-template",
+ "imposition-template-default",
+ "imposition-template-supported",
+ "insert-after-page-number-supported",
+ "insert-count-supported",
+ "insert-sheet",
+ "insert-sheet-default",
+ "insert-sheet-supported",
+ "job-account-id",
+ "job-account-id-default",
+ "job-account-id-supported",
+ "job-accounting-sheets"
+ "job-accounting-sheets-default"
+ "job-accounting-sheets-supported"
+ "job-accounting-user-id",
+ "job-accounting-user-id-default",
+ "job-accounting-user-id-supported",
+ "job-copies",
+ "job-copies-default",
+ "job-copies-supported",
+ "job-cover-back",
+ "job-cover-back-default",
+ "job-cover-back-supported",
+ "job-cover-front",
+ "job-cover-front-default",
+ "job-cover-front-supported",
+ "job-delay-output-until",
+ "job-delay-output-until-default",
+ "job-delay-output-until-supported",
+ "job-delay-output-until-time",
+ "job-delay-output-until-time-default",
+ "job-delay-output-until-time-supported",
+ "job-error-action",
+ "job-error-action-default",
+ "job-error-action-supported",
+ "job-error-sheet",
+ "job-error-sheet-default",
+ "job-error-sheet-supported",
+ "job-finishings",
+ "job-finishings-col",
+ "job-finishings-col-default",
+ "job-finishings-col-supported",
+ "job-finishings-default",
+ "job-finishings-supported",
+ "job-hold-until",
+ "job-hold-until-default",
+ "job-hold-until-supported",
+ "job-hold-until-time",
+ "job-hold-until-time-default",
+ "job-hold-until-time-supported",
+ "job-message-to-operator",
+ "job-message-to-operator-default",
+ "job-message-to-operator-supported",
+ "job-phone-number",
+ "job-phone-number-default",
+ "job-phone-number-supported",
+ "job-priority",
+ "job-priority-default",
+ "job-priority-supported",
+ "job-recipient-name",
+ "job-recipient-name-default",
+ "job-recipient-name-supported",
+ "job-save-disposition",
+ "job-save-disposition-default",
+ "job-save-disposition-supported",
+ "job-sheets",
+ "job-sheets-col",
+ "job-sheets-col-default",
+ "job-sheets-col-supported",
+ "job-sheets-default",
+ "job-sheets-supported",
+ "logo-uri-schemes-supported",
+ "max-save-info-supported",
+ "max-stitching-locations-supported",
+ "media",
+ "media-back-coating-supported",
+ "media-bottom-margin-supported",
+ "media-col",
+ "media-col-default",
+ "media-col-supported",
+ "media-color-supported",
+ "media-default",
+ "media-front-coating-supported",
+ "media-grain-supported",
+ "media-hole-count-supported",
+ "media-info-supported",
+ "media-input-tray-check",
+ "media-input-tray-check-default",
+ "media-input-tray-check-supported",
+ "media-key-supported",
+ "media-left-margin-supported",
+ "media-order-count-supported",
+ "media-pre-printed-supported",
+ "media-recycled-supported",
+ "media-right-margin-supported",
+ "media-size-supported",
+ "media-source-supported",
+ "media-supported",
+ "media-thickness-supported",
+ "media-top-margin-supported",
+ "media-type-supported",
+ "media-weight-metric-supported",
+ "multiple-document-handling",
+ "multiple-document-handling-default",
+ "multiple-document-handling-supported",
+ "number-of-retries", /* IPP FaxOut */
+ "number-of-retries-default",
+ "number-of-retries-supported",
+ "number-up",
+ "number-up-default",
+ "number-up-supported",
+ "orientation-requested",
+ "orientation-requested-default",
+ "orientation-requested-supported",
+ "output-bin",
+ "output-bin-default",
+ "output-bin-supported",
+ "output-device",
+ "output-device-default",
+ "output-device-supported",
+ "output-mode", /* CUPS extension */
+ "output-mode-default", /* CUPS extension */
+ "output-mode-supported", /* CUPS extension */
+ "overrides",
+ "overrides-supported",
+ "page-delivery",
+ "page-delivery-default",
+ "page-delivery-supported",
+ "page-order-received",
+ "page-order-received-default",
+ "page-order-received-supported",
+ "page-ranges",
+ "page-ranges-supported",
+ "pages-per-subset",
+ "pages-per-subset-supported",
+ "pdl-init-file",
+ "pdl-init-file-default",
+ "pdl-init-file-entry-supported",
+ "pdl-init-file-location-supported",
+ "pdl-init-file-name-subdirectory-supported",
+ "pdl-init-file-name-supported",
+ "pdl-init-file-supported",
+ "presentation-direction-number-up",
+ "presentation-direction-number-up-default",
+ "presentation-direction-number-up-supported",
+ "print-color-mode",
+ "print-color-mode-default",
+ "print-color-mode-supported",
+ "print-content-optimize",
+ "print-content-optimize-default",
+ "print-content-optimize-supported",
+ "print-quality",
+ "print-quality-default",
+ "print-quality-supported",
+ "print-rendering-intent",
+ "print-rendering-intent-default",
+ "print-rendering-intent-supported",
+ "print-scaling", /* IPP Paid Printing */
+ "print-scaling-default", /* IPP Paid Printing */
+ "print-scaling-supported", /* IPP Paid Printing */
+ "printer-resolution",
+ "printer-resolution-default",
+ "printer-resolution-supported",
+ "proof-print",
+ "proof-print-default",
+ "proof-print-supported",
+ "retry-interval", /* IPP FaxOut */
+ "retry-interval-default",
+ "retry-interval-supported",
+ "retry-timeout", /* IPP FaxOut */
+ "retry-timeout-default",
+ "retry-timeout-supported",
+ "save-disposition-supported",
+ "save-document-format-default",
+ "save-document-format-supported",
+ "save-location-default",
+ "save-location-supported",
+ "save-name-subdirectory-supported",
+ "save-name-supported",
+ "separator-sheets",
+ "separator-sheets-default",
+ "separator-sheets-supported",
+ "sheet-collate",
+ "sheet-collate-default",
+ "sheet-collate-supported",
+ "sides",
+ "sides-default",
+ "sides-supported",
+ "stitching-locations-supported",
+ "stitching-offset-supported",
+ "x-image-position",
+ "x-image-position-default",
+ "x-image-position-supported",
+ "x-image-shift",
+ "x-image-shift-default",
+ "x-image-shift-supported",
+ "x-side1-image-shift",
+ "x-side1-image-shift-default",
+ "x-side1-image-shift-supported",
+ "x-side2-image-shift",
+ "x-side2-image-shift-default",
+ "x-side2-image-shift-supported",
+ "y-image-position",
+ "y-image-position-default",
+ "y-image-position-supported",
+ "y-image-shift",
+ "y-image-shift-default",
+ "y-image-shift-supported",
+ "y-side1-image-shift",
+ "y-side1-image-shift-default",
+ "y-side1-image-shift-supported",
+ "y-side2-image-shift",
+ "y-side2-image-shift-default",
+ "y-side2-image-shift-supported"
+ };
+ static const char * const printer_description[] =
+ { /* printer-description group */
+ "auth-info-required", /* CUPS extension */
+ "charset-configured",
+ "charset-supported",
+ "color-supported",
+ "compression-supported",
+ "device-service-count",
+ "device-uri", /* CUPS extension */
+ "device-uuid",
+ "document-charset-default",
+ "document-charset-supported",
+ "document-creation-attributes-supported",
+ "document-digital-signature-default",
+ "document-digital-signature-supported",
+ "document-format-default",
+ "document-format-details-default",
+ "document-format-details-supported",
+ "document-format-supported",
+ "document-format-varying-attributes",
+ "document-format-version-default",
+ "document-format-version-supported",
+ "document-natural-language-default",
+ "document-natural-language-supported",
+ "document-password-supported",
+ "generated-natural-language-supported",
+ "identify-actions-default",
+ "identify-actions-supported",
+ "input-source-supported",
+ "ipp-features-supported",
+ "ipp-versions-supported",
+ "ippget-event-life",
+ "job-authorization-uri-supported", /* CUPS extension */
+ "job-constraints-supported",
+ "job-creation-attributes-supported",
+ "job-finishings-col-ready",
+ "job-finishings-ready",
+ "job-ids-supported",
+ "job-impressions-supported",
+ "job-k-limit", /* CUPS extension */
+ "job-k-octets-supported",
+ "job-media-sheets-supported",
+ "job-page-limit", /* CUPS extension */
+ "job-password-encryption-supported",
+ "job-password-supported",
+ "job-quota-period", /* CUPS extension */
+ "job-resolvers-supported",
+ "job-settable-attributes-supported",
+ "job-spooling-supported",
+ "jpeg-k-octets-supported", /* CUPS extension */
+ "jpeg-x-dimension-supported", /* CUPS extension */
+ "jpeg-y-dimension-supported", /* CUPS extension */
+ "landscape-orientation-requested-preferred",
+ /* CUPS extension */
+ "marker-change-time", /* CUPS extension */
+ "marker-colors", /* CUPS extension */
+ "marker-high-levels", /* CUPS extension */
+ "marker-levels", /* CUPS extension */
+ "marker-low-levels", /* CUPS extension */
+ "marker-message", /* CUPS extension */
+ "marker-names", /* CUPS extension */
+ "marker-types", /* CUPS extension */
+ "media-col-ready",
+ "media-ready",
+ "member-names", /* CUPS extension */
+ "member-uris", /* CUPS extension */
+ "multiple-destination-uris-supported",/* IPP FaxOut */
+ "multiple-document-jobs-supported",
+ "multiple-operation-time-out",
+ "multiple-operation-time-out-action",
+ "natural-language-configured",
+ "operations-supported",
+ "pages-per-minute",
+ "pages-per-minute-color",
+ "pdf-k-octets-supported", /* CUPS extension */
+ "pdf-versions-supported", /* CUPS extension */
+ "pdl-override-supported",
+ "port-monitor", /* CUPS extension */
+ "port-monitor-supported", /* CUPS extension */
+ "preferred-attributes-supported",
+ "printer-alert",
+ "printer-alert-description",
+ "printer-charge-info",
+ "printer-charge-info-uri",
+ "printer-commands", /* CUPS extension */
+ "printer-current-time",
+ "printer-detailed-status-messages",
+ "printer-device-id",
+ "printer-dns-sd-name", /* CUPS extension */
+ "printer-driver-installer",
+ "printer-fax-log-uri", /* IPP FaxOut */
+ "printer-fax-modem-info", /* IPP FaxOut */
+ "printer-fax-modem-name", /* IPP FaxOut */
+ "printer-fax-modem-number", /* IPP FaxOut */
+ "printer-geo-location",
+ "printer-get-attributes-supported",
+ "printer-icc-profiles",
+ "printer-icons",
+ "printer-info",
+ "printer-is-accepting-jobs",
+ "printer-is-shared", /* CUPS extension */
+ "printer-kind", /* IPP Paid Printing */
+ "printer-location",
+ "printer-make-and-model",
+ "printer-mandatory-job-attributes",
+ "printer-message-date-time",
+ "printer-message-from-operator",
+ "printer-message-time",
+ "printer-more-info",
+ "printer-more-info-manufacturer",
+ "printer-name",
+ "printer-native-formats",
+ "printer-organization",
+ "printer-organizational-unit",
+ "printer-settable-attributes-supported",
+ "printer-state",
+ "printer-state-change-date-time",
+ "printer-state-change-time",
+ "printer-state-message",
+ "printer-state-reasons",
+ "printer-supply",
+ "printer-supply-description",
+ "printer-supply-info-uri",
+ "printer-type", /* CUPS extension */
+ "printer-up-time",
+ "printer-uri-supported",
+ "printer-uuid",
+ "printer-xri-supported",
+ "pwg-raster-document-resolution-supported",
+ "pwg-raster-document-sheet-back",
+ "pwg-raster-document-type-supported",
+ "queued-job-count",
+ "reference-uri-schemes-supported",
+ "repertoire-supported",
+ "requesting-user-name-allowed", /* CUPS extension */
+ "requesting-user-name-denied", /* CUPS extension */
+ "requesting-user-uri-supported",
+ "subordinate-printers-supported",
+ "urf-supported", /* CUPS extension */
+ "uri-authentication-supported",
+ "uri-security-supported",
+ "user-defined-value-supported",
+ "which-jobs-supported",
+ "xri-authentication-supported",
+ "xri-security-supported",
+ "xri-uri-scheme-supported"
+ };
+ static const char * const subscription_description[] =
+ { /* subscription-description group */
+ "notify-job-id",
+ "notify-lease-expiration-time",
+ "notify-printer-up-time",
+ "notify-printer-uri",
+ "notify-sequence-number",
+ "notify-subscriber-user-name",
+ "notify-subscriber-user-uri",
+ "notify-subscription-id",
+ "subscriptions-uuid"
+ };
+ static const char * const subscription_template[] =
+ { /* subscription-template group */
+ "notify-attributes",
+ "notify-attributes-supported",
+ "notify-charset",
+ "notify-events",
+ "notify-events-default",
+ "notify-events-supported",
+ "notify-lease-duration",
+ "notify-lease-duration-default",
+ "notify-lease-duration-supported",
+ "notify-max-events-supported",
+ "notify-natural-language",
+ "notify-pull-method",
+ "notify-pull-method-supported",
+ "notify-recipient-uri",
+ "notify-schemes-supported",
+ "notify-time-interval",
+ "notify-user-data"
+ };
+
+
+ /*
+ * Get the requested-attributes attribute...
+ */
+
+ if ((requested = ippFindAttribute(request, "requested-attributes",
+ IPP_TAG_KEYWORD)) == NULL)
+ {
+ /*
+ * The Get-Jobs operation defaults to "job-id" and "job-uri", all others
+ * default to "all"...
+ */
+
+ if (ippGetOperation(request) == IPP_OP_GET_JOBS)
+ {
+ ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+ cupsArrayAdd(ra, "job-id");
+ cupsArrayAdd(ra, "job-uri");
+
+ return (ra);
+ }
+ else
+ return (NULL);
+ }
+
+ /*
+ * If the attribute contains a single "all" keyword, return NULL...
+ */
+
+ count = ippGetCount(requested);
+ if (count == 1 && !strcmp(ippGetString(requested, 0, NULL), "all"))
+ return (NULL);
+
+ /*
+ * Create an array using "strcmp" as the comparison function...
+ */
+
+ ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+ for (i = 0; i < count; i ++)
+ {
+ added = 0;
+ value = ippGetString(requested, i, NULL);
+
+ if (!strcmp(value, "document-description") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(document_description) /
+ sizeof(document_description[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)document_description[j]);
+
+ added = 1;
+ }
+
+ if (!strcmp(value, "document-template") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(document_template) / sizeof(document_template[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)document_template[j]);
+
+ added = 1;
+ }
+
+ if (!strcmp(value, "job-description") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(job_description) / sizeof(job_description[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)job_description[j]);
+
+ added = 1;
+ }
+
+ if (!strcmp(value, "job-template") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(job_template) / sizeof(job_template[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)job_template[j]);
+
+ added = 1;
+ }
+
+ if (!strcmp(value, "printer-description") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(printer_description) /
+ sizeof(printer_description[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)printer_description[j]);
+
+ added = 1;
+ }
+
+ if (!strcmp(value, "subscription-description") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(subscription_description) /
+ sizeof(subscription_description[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)subscription_description[j]);
+
+ added = 1;
+ }
+
+ if (!strcmp(value, "subscription-template") || !strcmp(value, "all"))
+ {
+ for (j = 0;
+ j < (int)(sizeof(subscription_template) /
+ sizeof(subscription_template[0]));
+ j ++)
+ cupsArrayAdd(ra, (void *)subscription_template[j]);
+
+ added = 1;
+ }
+
+ if (!added)
+ cupsArrayAdd(ra, (void *)value);
+ }
+
+ return (ra);
+}
+
+
+/*
+ * 'ippEnumString()' - Return a string corresponding to the enum value.
+ */
+
+const char * /* O - Enum string */
+ippEnumString(const char *attrname, /* I - Attribute name */
+ int enumvalue) /* I - Enum value */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * Check for standard enum values...
+ */
+
+ if (!strcmp(attrname, "document-state") &&
+ enumvalue >= 3 &&
+ enumvalue < (3 + (int)(sizeof(ipp_document_states) /
+ sizeof(ipp_document_states[0]))))
+ return (ipp_document_states[enumvalue - 3]);
+ else if (!strcmp(attrname, "finishings") ||
+ !strcmp(attrname, "finishings-actual") ||
+ !strcmp(attrname, "finishings-default") ||
+ !strcmp(attrname, "finishings-ready") ||
+ !strcmp(attrname, "finishings-supported") ||
+ !strcmp(attrname, "job-finishings") ||
+ !strcmp(attrname, "job-finishings-default") ||
+ !strcmp(attrname, "job-finishings-supported"))
+ {
+ if (enumvalue >= 3 &&
+ enumvalue < (3 + (int)(sizeof(ipp_finishings) /
+ sizeof(ipp_finishings[0]))))
+ return (ipp_finishings[enumvalue - 3]);
+ else if (enumvalue >= 0x40000000 &&
+ enumvalue <= (0x40000000 + (int)(sizeof(ipp_finishings_vendor) /
+ sizeof(ipp_finishings_vendor[0]))))
+ return (ipp_finishings_vendor[enumvalue - 0x40000000]);
+ }
+ else if ((!strcmp(attrname, "job-collation-type") ||
+ !strcmp(attrname, "job-collation-type-actual")) &&
+ enumvalue >= 3 &&
+ enumvalue < (3 + (int)(sizeof(ipp_job_collation_types) /
+ sizeof(ipp_job_collation_types[0]))))
+ return (ipp_job_collation_types[enumvalue - 3]);
+ else if (!strcmp(attrname, "job-state") &&
+ enumvalue >= IPP_JSTATE_PENDING && enumvalue <= IPP_JSTATE_COMPLETED)
+ return (ipp_job_states[enumvalue - IPP_JSTATE_PENDING]);
+ else if (!strcmp(attrname, "operations-supported"))
+ return (ippOpString((ipp_op_t)enumvalue));
+ else if ((!strcmp(attrname, "orientation-requested") ||
+ !strcmp(attrname, "orientation-requested-actual") ||
+ !strcmp(attrname, "orientation-requested-default") ||
+ !strcmp(attrname, "orientation-requested-supported")) &&
+ enumvalue >= 3 &&
+ enumvalue < (3 + (int)(sizeof(ipp_orientation_requesteds) /
+ sizeof(ipp_orientation_requesteds[0]))))
+ return (ipp_orientation_requesteds[enumvalue - 3]);
+ else if ((!strcmp(attrname, "print-quality") ||
+ !strcmp(attrname, "print-quality-actual") ||
+ !strcmp(attrname, "print-quality-default") ||
+ !strcmp(attrname, "print-quality-supported")) &&
+ enumvalue >= 3 &&
+ enumvalue < (3 + (int)(sizeof(ipp_print_qualities) /
+ sizeof(ipp_print_qualities[0]))))
+ return (ipp_print_qualities[enumvalue - 3]);
+ else if (!strcmp(attrname, "printer-state") &&
+ enumvalue >= IPP_PSTATE_IDLE && enumvalue <= IPP_PSTATE_STOPPED)
+ return (ipp_printer_states[enumvalue - IPP_PSTATE_IDLE]);
+
+ /*
+ * Not a standard enum value, just return the decimal equivalent...
+ */
+
+ snprintf(cg->ipp_unknown, sizeof(cg->ipp_unknown), "%d", enumvalue);
+ return (cg->ipp_unknown);
+}
+
+
+/*
+ * 'ippEnumValue()' - Return the value associated with a given enum string.
+ */
+
+int /* O - Enum value or -1 if unknown */
+ippEnumValue(const char *attrname, /* I - Attribute name */
+ const char *enumstring) /* I - Enum string */
+{
+ int i, /* Looping var */
+ num_strings; /* Number of strings to compare */
+ const char * const *strings; /* Strings to compare */
+
+
+ /*
+ * If the string is just a number, return it...
+ */
+
+ if (isdigit(*enumstring & 255))
+ return (strtol(enumstring, NULL, 0));
+
+ /*
+ * Otherwise look up the string...
+ */
+
+ if (!strcmp(attrname, "document-state"))
+ {
+ num_strings = (int)(sizeof(ipp_document_states) / sizeof(ipp_document_states[0]));
+ strings = ipp_document_states;
+ }
+ else if (!strcmp(attrname, "finishings") ||
+ !strcmp(attrname, "finishings-actual") ||
+ !strcmp(attrname, "finishings-default") ||
+ !strcmp(attrname, "finishings-ready") ||
+ !strcmp(attrname, "finishings-supported"))
+ {
+ for (i = 0;
+ i < (int)(sizeof(ipp_finishings_vendor) /
+ sizeof(ipp_finishings_vendor[0]));
+ i ++)
+ if (!strcmp(enumstring, ipp_finishings_vendor[i]))
+ return (i + 0x40000000);
+
+ num_strings = (int)(sizeof(ipp_finishings) / sizeof(ipp_finishings[0]));
+ strings = ipp_finishings;
+ }
+ else if (!strcmp(attrname, "job-collation-type") ||
+ !strcmp(attrname, "job-collation-type-actual"))
+ {
+ num_strings = (int)(sizeof(ipp_job_collation_types) /
+ sizeof(ipp_job_collation_types[0]));
+ strings = ipp_job_collation_types;
+ }
+ else if (!strcmp(attrname, "job-state"))
+ {
+ num_strings = (int)(sizeof(ipp_job_states) / sizeof(ipp_job_states[0]));
+ strings = ipp_job_states;
+ }
+ else if (!strcmp(attrname, "operations-supported"))
+ return (ippOpValue(enumstring));
+ else if (!strcmp(attrname, "orientation-requested") ||
+ !strcmp(attrname, "orientation-requested-actual") ||
+ !strcmp(attrname, "orientation-requested-default") ||
+ !strcmp(attrname, "orientation-requested-supported"))
+ {
+ num_strings = (int)(sizeof(ipp_orientation_requesteds) /
+ sizeof(ipp_orientation_requesteds[0]));
+ strings = ipp_orientation_requesteds;
+ }
+ else if (!strcmp(attrname, "print-quality") ||
+ !strcmp(attrname, "print-quality-actual") ||
+ !strcmp(attrname, "print-quality-default") ||
+ !strcmp(attrname, "print-quality-supported"))
+ {
+ num_strings = (int)(sizeof(ipp_print_qualities) / sizeof(ipp_print_qualities[0]));
+ strings = ipp_print_qualities;
+ }
+ else if (!strcmp(attrname, "printer-state"))
+ {
+ num_strings = (int)(sizeof(ipp_printer_states) / sizeof(ipp_printer_states[0]));
+ strings = ipp_printer_states;
+ }
+ else
+ return (-1);
+
+ for (i = 0; i < num_strings; i ++)
+ if (!strcmp(enumstring, strings[i]))
+ return (i + 3);
+
+ return (-1);
+}
+
+
+/*
+ * 'ippErrorString()' - Return a name for the given status code.
+ */
+
+const char * /* O - Text string */
+ippErrorString(ipp_status_t error) /* I - Error status */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * See if the error code is a known value...
+ */
+
+ if (error >= IPP_STATUS_OK && error <= IPP_STATUS_OK_EVENTS_COMPLETE)
+ return (ipp_status_oks[error]);
+ else if (error == IPP_STATUS_REDIRECTION_OTHER_SITE)
+ return ("redirection-other-site");
+ else if (error == IPP_STATUS_CUPS_SEE_OTHER)
+ return ("cups-see-other");
+ else if (error >= IPP_STATUS_ERROR_BAD_REQUEST &&
+ error <= IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE)
+ return (ipp_status_400s[error - IPP_STATUS_ERROR_BAD_REQUEST]);
+ else if (error >= 0x480 &&
+ error <= IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
+ return (ipp_status_480s[error - 0x0480]);
+ else if (error >= IPP_STATUS_ERROR_INTERNAL &&
+ error <= IPP_STATUS_ERROR_TOO_MANY_DOCUMENTS)
+ return (ipp_status_500s[error - IPP_STATUS_ERROR_INTERNAL]);
+ else if (error >= IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED &&
+ error <= IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED)
+ return (ipp_status_1000s[error -
+ IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED]);
+
+ /*
+ * No, build an "0xxxxx" error string...
+ */
+
+ sprintf(cg->ipp_unknown, "0x%04x", error);
+
+ return (cg->ipp_unknown);
+}
+
+
+/*
+ * 'ippErrorValue()' - Return a status code for the given name.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ipp_status_t /* O - IPP status code */
+ippErrorValue(const char *name) /* I - Name */
+{
+ int i;
+
+
+ for (i = 0; i < (sizeof(ipp_status_oks) / sizeof(ipp_status_oks[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_status_oks[i]))
+ return ((ipp_status_t)i);
+
+ if (!_cups_strcasecmp(name, "redirection-other-site"))
+ return (IPP_STATUS_REDIRECTION_OTHER_SITE);
+
+ if (!_cups_strcasecmp(name, "cups-see-other"))
+ return (IPP_STATUS_CUPS_SEE_OTHER);
+
+ for (i = 0; i < (sizeof(ipp_status_400s) / sizeof(ipp_status_400s[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_status_400s[i]))
+ return ((ipp_status_t)(i + 0x400));
+
+ for (i = 0; i < (sizeof(ipp_status_480s) / sizeof(ipp_status_480s[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_status_480s[i]))
+ return ((ipp_status_t)(i + 0x480));
+
+ for (i = 0; i < (sizeof(ipp_status_500s) / sizeof(ipp_status_500s[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_status_500s[i]))
+ return ((ipp_status_t)(i + 0x500));
+
+ for (i = 0; i < (sizeof(ipp_status_1000s) / sizeof(ipp_status_1000s[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_status_1000s[i]))
+ return ((ipp_status_t)(i + 0x1000));
+
+ return ((ipp_status_t)-1);
+}
+
+
+/*
+ * 'ippOpString()' - Return a name for the given operation id.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+const char * /* O - Name */
+ippOpString(ipp_op_t op) /* I - Operation ID */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * See if the operation ID is a known value...
+ */
+
+ if (op >= IPP_OP_PRINT_JOB && op <= IPP_OP_VALIDATE_DOCUMENT)
+ return (ipp_std_ops[op]);
+ else if (op == IPP_OP_PRIVATE)
+ return ("windows-ext");
+ else if (op >= IPP_OP_CUPS_GET_DEFAULT && op <= IPP_OP_CUPS_GET_PPD)
+ return (ipp_cups_ops[op - IPP_OP_CUPS_GET_DEFAULT]);
+ else if (op == IPP_OP_CUPS_GET_DOCUMENT)
+ return (ipp_cups_ops2[0]);
+
+ /*
+ * No, build an "0xxxxx" operation string...
+ */
+
+ sprintf(cg->ipp_unknown, "0x%04x", op);
+
+ return (cg->ipp_unknown);
+}
+
+
+/*
+ * 'ippOpValue()' - Return an operation id for the given name.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ipp_op_t /* O - Operation ID */
+ippOpValue(const char *name) /* I - Textual name */
+{
+ int i;
+
+
+ if (!strncmp(name, "0x", 2))
+ return ((ipp_op_t)strtol(name + 2, NULL, 16));
+
+ for (i = 0; i < (sizeof(ipp_std_ops) / sizeof(ipp_std_ops[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_std_ops[i]))
+ return ((ipp_op_t)i);
+
+ if (!_cups_strcasecmp(name, "windows-ext"))
+ return (IPP_OP_PRIVATE);
+
+ for (i = 0; i < (sizeof(ipp_cups_ops) / sizeof(ipp_cups_ops[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_cups_ops[i]))
+ return ((ipp_op_t)(i + 0x4001));
+
+ for (i = 0; i < (sizeof(ipp_cups_ops2) / sizeof(ipp_cups_ops2[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_cups_ops2[i]))
+ return ((ipp_op_t)(i + 0x4027));
+
+ if (!_cups_strcasecmp(name, "Create-Job-Subscription"))
+ return (IPP_OP_CREATE_JOB_SUBSCRIPTIONS);
+
+ if (!_cups_strcasecmp(name, "Create-Printer-Subscription"))
+ return (IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS);
+
+ if (!_cups_strcasecmp(name, "CUPS-Add-Class"))
+ return (IPP_OP_CUPS_ADD_MODIFY_CLASS);
+
+ if (!_cups_strcasecmp(name, "CUPS-Add-Printer"))
+ return (IPP_OP_CUPS_ADD_MODIFY_PRINTER);
+
+ return (IPP_OP_CUPS_INVALID);
+}
+
+
+/*
+ * 'ippPort()' - Return the default IPP port number.
+ */
+
+int /* O - Port number */
+ippPort(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ DEBUG_puts("ippPort()");
+
+ if (!cg->ipp_port)
+ _cupsSetDefaults();
+
+ DEBUG_printf(("1ippPort: Returning %d...", cg->ipp_port));
+
+ return (cg->ipp_port);
+}
+
+
+/*
+ * 'ippSetPort()' - Set the default port number.
+ */
+
+void
+ippSetPort(int p) /* I - Port number to use */
+{
+ DEBUG_printf(("ippSetPort(p=%d)", p));
+
+ _cupsGlobals()->ipp_port = p;
+}
+
+
+/*
+ * 'ippTagString()' - Return the tag name corresponding to a tag value.
+ *
+ * The returned names are defined in RFC 2911 and 3382.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+const char * /* O - Tag name */
+ippTagString(ipp_tag_t tag) /* I - Tag value */
+{
+ tag &= IPP_TAG_CUPS_MASK;
+
+ if (tag < (ipp_tag_t)(sizeof(ipp_tag_names) / sizeof(ipp_tag_names[0])))
+ return (ipp_tag_names[tag]);
+ else
+ return ("UNKNOWN");
+}
+
+
+/*
+ * 'ippTagValue()' - Return the tag value corresponding to a tag name.
+ *
+ * The tag names are defined in RFC 2911 and 3382.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ipp_tag_t /* O - Tag value */
+ippTagValue(const char *name) /* I - Tag name */
+{
+ int i; /* Looping var */
+
+
+ for (i = 0; i < (sizeof(ipp_tag_names) / sizeof(ipp_tag_names[0])); i ++)
+ if (!_cups_strcasecmp(name, ipp_tag_names[i]))
+ return ((ipp_tag_t)i);
+
+ if (!_cups_strcasecmp(name, "operation"))
+ return (IPP_TAG_OPERATION);
+ else if (!_cups_strcasecmp(name, "job"))
+ return (IPP_TAG_JOB);
+ else if (!_cups_strcasecmp(name, "printer"))
+ return (IPP_TAG_PRINTER);
+ else if (!_cups_strcasecmp(name, "unsupported"))
+ return (IPP_TAG_UNSUPPORTED_GROUP);
+ else if (!_cups_strcasecmp(name, "subscription"))
+ return (IPP_TAG_SUBSCRIPTION);
+ else if (!_cups_strcasecmp(name, "event"))
+ return (IPP_TAG_EVENT_NOTIFICATION);
+ else if (!_cups_strcasecmp(name, "language"))
+ return (IPP_TAG_LANGUAGE);
+ else if (!_cups_strcasecmp(name, "mimetype"))
+ return (IPP_TAG_MIMETYPE);
+ else if (!_cups_strcasecmp(name, "name"))
+ return (IPP_TAG_NAME);
+ else if (!_cups_strcasecmp(name, "text"))
+ return (IPP_TAG_TEXT);
+ else if (!_cups_strcasecmp(name, "begCollection"))
+ return (IPP_TAG_BEGIN_COLLECTION);
+ else
+ return (IPP_TAG_ZERO);
+}
+
+
+/*
+ * 'ipp_col_string()' - Convert a collection to a string.
+ */
+
+static size_t /* O - Number of bytes */
+ipp_col_string(ipp_t *col, /* I - Collection attribute */
+ char *buffer, /* I - Buffer or NULL */
+ size_t bufsize) /* I - Size of buffer */
+{
+ char *bufptr, /* Position in buffer */
+ *bufend, /* End of buffer */
+ prefix = '{', /* Prefix character */
+ temp[256]; /* Temporary string */
+ ipp_attribute_t *attr; /* Current member attribute */
+
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+
+ for (attr = col->attrs; attr; attr = attr->next)
+ {
+ if (!attr->name)
+ continue;
+
+ if (buffer && bufptr < bufend)
+ *bufptr = prefix;
+ bufptr ++;
+ prefix = ' ';
+
+ if (buffer && bufptr < bufend)
+ bufptr += snprintf(bufptr, bufend - bufptr + 1, "%s=", attr->name);
+ else
+ bufptr += strlen(attr->name) + 1;
+
+ if (buffer && bufptr < bufend)
+ bufptr += ippAttributeString(attr, bufptr, bufend - bufptr + 1);
+ else
+ bufptr += ippAttributeString(attr, temp, sizeof(temp));
+ }
+
+ if (prefix == '{')
+ {
+ if (buffer && bufptr < bufend)
+ *bufptr = prefix;
+ bufptr ++;
+ }
+
+ if (buffer && bufptr < bufend)
+ *bufptr = '}';
+ bufptr ++;
+
+ return (bufptr - buffer);
+}
+
+
+/*
+ * End of "$Id: ipp-support.c 11734 2014-03-25 18:01:47Z msweet $".
+ */
diff --git a/cups/libs/cups/ipp.c b/cups/libs/cups/ipp.c
new file mode 100644
index 000000000..7b4f9bff7
--- /dev/null
+++ b/cups/libs/cups/ipp.c
@@ -0,0 +1,7097 @@
+/*
+ * "$Id: ipp.c 11864 2014-05-08 23:10:47Z msweet $"
+ *
+ * Internet Printing Protocol functions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsBufferGet() - Get a read/write buffer.
+ * _cupsBufferRelease() - Release a read/write buffer.
+ * ippAddBoolean() - Add a boolean attribute to an IPP message.
+ * ippAddBooleans() - Add an array of boolean values.
+ * ippAddCollection() - Add a collection value.
+ * ippAddCollections() - Add an array of collection values.
+ * ippAddDate() - Add a date attribute to an IPP message.
+ * ippAddInteger() - Add a integer attribute to an IPP message.
+ * ippAddIntegers() - Add an array of integer values.
+ * ippAddOctetString() - Add an octetString value to an IPP message.
+ * ippAddOutOfBand() - Add an out-of-band value to an IPP message.
+ * ippAddRange() - Add a range of values to an IPP message.
+ * ippAddRanges() - Add ranges of values to an IPP message.
+ * ippAddResolution() - Add a resolution value to an IPP message.
+ * ippAddResolutions() - Add resolution values to an IPP message.
+ * ippAddSeparator() - Add a group separator to an IPP message.
+ * ippAddString() - Add a language-encoded string to an IPP message.
+ * ippAddStringf() - Add a formatted string to an IPP message.
+ * ippAddStringfv() - Add a formatted string to an IPP message.
+ * ippAddStrings() - Add language-encoded strings to an IPP message.
+ * ippContainsInteger() - Determine whether an attribute contains the
+ * specified value or is within the list of ranges.
+ * ippContainsString() - Determine whether an attribute contains the
+ * specified string value.
+ * ippCopyAttribute() - Copy an attribute.
+ * ippCopyAttributes() - Copy attributes from one IPP message to another.
+ * ippDateToTime() - Convert from RFC 1903 Date/Time format to UNIX
+ * time in seconds.
+ * ippDelete() - Delete an IPP message.
+ * ippDeleteAttribute() - Delete a single attribute in an IPP message.
+ * ippDeleteValues() - Delete values in an attribute.
+ * ippFindAttribute() - Find a named attribute in a request.
+ * ippFindNextAttribute() - Find the next named attribute in a request.
+ * ippFirstAttribute() - Return the first attribute in the message.
+ * ippGetBoolean() - Get a boolean value for an attribute.
+ * ippGetCollection() - Get a collection value for an attribute.
+ * ippGetCount() - Get the number of values in an attribute.
+ * ippGetDate() - Get a date value for an attribute.
+ * ippGetGroupTag() - Get the group associated with an attribute.
+ * ippGetInteger() - Get the integer/enum value for an attribute.
+ * ippGetName() - Get the attribute name.
+ * ippGetOctetString() - Get an octetString value from an IPP attribute.
+ * ippGetOperation() - Get the operation ID in an IPP message.
+ * ippGetRange() - Get a rangeOfInteger value from an attribute.
+ * ippGetRequestId() - Get the request ID from an IPP message.
+ * ippGetResolution() - Get a resolution value for an attribute.
+ * ippGetState() - Get the IPP message state.
+ * ippGetStatusCode() - Get the status code from an IPP response or
+ * event message.
+ * ippGetString() - Get the string and optionally the language code
+ * for an attribute.
+ * ippGetValueTag() - Get the value tag for an attribute.
+ * ippGetVersion() - Get the major and minor version number from an
+ * IPP message.
+ * ippLength() - Compute the length of an IPP message.
+ * ippNextAttribute() - Return the next attribute in the message.
+ * ippNew() - Allocate a new IPP message.
+ * ippNewRequest() - Allocate a new IPP request message.
+ * ippNewResponse() - Allocate a new IPP response message.
+ * ippRead() - Read data for an IPP message from a HTTP
+ * connection.
+ * ippReadFile() - Read data for an IPP message from a file.
+ * ippReadIO() - Read data for an IPP message.
+ * ippSetBoolean() - Set a boolean value in an attribute.
+ * ippSetCollection() - Set a collection value in an attribute.
+ * ippSetDate() - Set a date value in an attribute.
+ * ippSetGroupTag() - Set the group tag of an attribute.
+ * ippSetInteger() - Set an integer or enum value in an attribute.
+ * ippSetName() - Set the name of an attribute.
+ * ippSetOctetString() - Set an octetString value in an IPP attribute.
+ * ippSetOperation() - Set the operation ID in an IPP request message.
+ * ippSetRange() - Set a rangeOfInteger value in an attribute.
+ * ippSetRequestId() - Set the request ID in an IPP message.
+ * ippSetResolution() - Set a resolution value in an attribute.
+ * ippSetState() - Set the current state of the IPP message.
+ * ippSetStatusCode() - Set the status code in an IPP response or event
+ * message.
+ * ippSetString() - Set a string value in an attribute.
+ * ippSetStringf() - Set a formatted string value of an attribute.
+ * ippSetStringf() - Set a formatted string value of an attribute.
+ * ippSetValueTag() - Set the value tag of an attribute.
+ * ippSetVersion() - Set the version number in an IPP message.
+ * ippTimeToDate() - Convert from UNIX time to RFC 1903 format.
+ * ippValidateAttribute() - Validate the contents of an attribute.
+ * ippValidateAttributes() - Validate all attributes in an IPP message.
+ * ippWrite() - Write data for an IPP message to a HTTP
+ * connection.
+ * ippWriteFile() - Write data for an IPP message to a file.
+ * ippWriteIO() - Write data for an IPP message.
+ * ipp_add_attr() - Add a new attribute to the message.
+ * ipp_free_values() - Free attribute values.
+ * ipp_get_code() - Convert a C locale/charset name into an IPP
+ * language/charset code.
+ * ipp_lang_code() - Convert a C locale name into an IPP language
+ * code.
+ * ipp_length() - Compute the length of an IPP message or
+ * collection value.
+ * ipp_read_http() - Semi-blocking read on a HTTP connection...
+ * ipp_read_file() - Read IPP data from a file.
+ * ipp_set_error() - Set a formatted, localized error string.
+ * ipp_set_value() - Get the value element from an attribute,
+ * expanding it as needed.
+ * ipp_write_file() - Write IPP data to a file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+#ifndef WIN32
+#include <regex.h>
+#endif /* WIN32 */
+
+#ifdef WIN32
+# include <io.h>
+#endif /* WIN32 */
+
+
+/*
+ * Local functions...
+ */
+
+static ipp_attribute_t *ipp_add_attr(ipp_t *ipp, const char *name,
+ ipp_tag_t group_tag, ipp_tag_t value_tag,
+ int num_values);
+static void ipp_free_values(ipp_attribute_t *attr, int element,
+ int count);
+static char *ipp_get_code(const char *locale, char *buffer,
+ size_t bufsize)
+ __attribute__((nonnull(1,2)));
+static char *ipp_lang_code(const char *locale, char *buffer,
+ size_t bufsize)
+ __attribute__((nonnull(1,2)));
+static size_t ipp_length(ipp_t *ipp, int collection);
+static ssize_t ipp_read_http(http_t *http, ipp_uchar_t *buffer,
+ size_t length);
+static ssize_t ipp_read_file(int *fd, ipp_uchar_t *buffer,
+ size_t length);
+static void ipp_set_error(ipp_status_t status, const char *format,
+ ...);
+static _ipp_value_t *ipp_set_value(ipp_t *ipp, ipp_attribute_t **attr,
+ int element);
+static ssize_t ipp_write_file(int *fd, ipp_uchar_t *buffer,
+ size_t length);
+
+
+/*
+ * '_cupsBufferGet()' - Get a read/write buffer.
+ */
+
+char * /* O - Buffer */
+_cupsBufferGet(size_t size) /* I - Size required */
+{
+ _cups_buffer_t *buffer; /* Current buffer */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ for (buffer = cg->cups_buffers; buffer; buffer = buffer->next)
+ if (!buffer->used && buffer->size >= size)
+ break;
+
+ if (!buffer)
+ {
+ if ((buffer = malloc(sizeof(_cups_buffer_t) + size - 1)) == NULL)
+ return (NULL);
+
+ buffer->next = cg->cups_buffers;
+ buffer->size = size;
+ cg->cups_buffers = buffer;
+ }
+
+ buffer->used = 1;
+
+ return (buffer->d);
+}
+
+
+/*
+ * '_cupsBufferRelease()' - Release a read/write buffer.
+ */
+
+void
+_cupsBufferRelease(char *b) /* I - Buffer to release */
+{
+ _cups_buffer_t *buffer; /* Buffer */
+
+
+ /*
+ * Mark this buffer as unused...
+ */
+
+ buffer = (_cups_buffer_t *)(b - offsetof(_cups_buffer_t, d));
+ buffer->used = 0;
+}
+
+
+/*
+ * 'ippAddBoolean()' - Add a boolean 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddBoolean(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ char value) /* I - Value of attribute */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddBoolean(ipp=%p, group=%02x(%s), name=\"%s\", value=%d)",
+ ipp, group, ippTagString(group), name, value));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, 1)) == NULL)
+ return (NULL);
+
+ attr->values[0].boolean = value;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddBooleans()' - Add an array of boolean values.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddBooleans(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const char *values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippAddBooleans(ipp=%p, group=%02x(%s), name=\"%s\", "
+ "num_values=%d, values=%p)", ipp, group, ippTagString(group),
+ name, num_values, values));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ num_values < 1)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, num_values)) == NULL)
+ return (NULL);
+
+ if (values)
+ {
+ for (i = num_values, value = attr->values;
+ i > 0;
+ i --, value ++)
+ value->boolean = *values++;
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddCollection()' - Add a collection value.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddCollection(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ ipp_t *value) /* I - Value */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddCollection(ipp=%p, group=%02x(%s), name=\"%s\", "
+ "value=%p)", ipp, group, ippTagString(group), name, value));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION, 1)) == NULL)
+ return (NULL);
+
+ attr->values[0].collection = value;
+
+ if (value)
+ value->use ++;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddCollections()' - Add an array of collection values.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddCollections(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const ipp_t **values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippAddCollections(ipp=%p, group=%02x(%s), name=\"%s\", "
+ "num_values=%d, values=%p)", ipp, group, ippTagString(group),
+ name, num_values, values));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ num_values < 1)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION,
+ num_values)) == NULL)
+ return (NULL);
+
+ if (values)
+ {
+ for (i = num_values, value = attr->values;
+ i > 0;
+ i --, value ++)
+ {
+ value->collection = (ipp_t *)*values++;
+ value->collection->use ++;
+ }
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddDate()' - Add a date 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddDate(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ const ipp_uchar_t *value) /* I - Value */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddDate(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)",
+ ipp, group, ippTagString(group), name, value));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || !value || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_DATE, 1)) == NULL)
+ return (NULL);
+
+ memcpy(attr->values[0].date, value, 11);
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddInteger()' - Add a integer 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported values include enum (@code IPP_TAG_ENUM@) and integer
+ * (@code IPP_TAG_INTEGER@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddInteger(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ int value) /* I - Value of attribute */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddInteger(ipp=%p, group=%02x(%s), type=%02x(%s), "
+ "name=\"%s\", value=%d)", ipp, group, ippTagString(group),
+ value_tag, ippTagString(value_tag), name, value));
+
+ value_tag &= IPP_TAG_CUPS_MASK;
+
+ /*
+ * Special-case for legacy usage: map out-of-band attributes to new ippAddOutOfBand
+ * function...
+ */
+
+ if (value_tag >= IPP_TAG_UNSUPPORTED_VALUE && value_tag <= IPP_TAG_ADMINDEFINE)
+ return (ippAddOutOfBand(ipp, group, value_tag, name));
+
+ /*
+ * Range check input...
+ */
+
+#if 0
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ (value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM))
+ return (NULL);
+#else
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (NULL);
+#endif /* 0 */
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
+ return (NULL);
+
+ attr->values[0].integer = value;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddIntegers()' - Add an array of integer values.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported values include enum (@code IPP_TAG_ENUM@) and integer
+ * (@code IPP_TAG_INTEGER@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddIntegers(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const int *values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippAddIntegers(ipp=%p, group=%02x(%s), type=%02x(%s), "
+ "name=\"%s\", num_values=%d, values=%p)", ipp,
+ group, ippTagString(group), value_tag, ippTagString(value_tag), name,
+ num_values, values));
+
+ value_tag &= IPP_TAG_CUPS_MASK;
+
+ /*
+ * Range check input...
+ */
+
+#if 0
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ (value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM) ||
+ num_values < 1)
+ return (NULL);
+#else
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ num_values < 1)
+ return (NULL);
+#endif /* 0 */
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
+ return (NULL);
+
+ if (values)
+ {
+ for (i = num_values, value = attr->values;
+ i > 0;
+ i --, value ++)
+ value->integer = *values++;
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddOctetString()' - Add an octetString value 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddOctetString(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ const void *data, /* I - octetString data */
+ int datalen) /* I - Length of data in bytes */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ datalen < 0 || datalen > IPP_MAX_LENGTH)
+ return (NULL);
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_STRING, 1)) == NULL)
+ return (NULL);
+
+ /*
+ * Initialize the attribute data...
+ */
+
+ attr->values[0].unknown.length = datalen;
+
+ if (data)
+ {
+ if ((attr->values[0].unknown.data = malloc(datalen)) == NULL)
+ {
+ ippDeleteAttribute(ipp, attr);
+ return (NULL);
+ }
+
+ memcpy(attr->values[0].unknown.data, data, datalen);
+ }
+
+ /*
+ * Return the new attribute...
+ */
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddOutOfBand()' - Add an out-of-band value 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported out-of-band values include unsupported-value
+ * (@code IPP_TAG_UNSUPPORTED_VALUE@), default (@code IPP_TAG_DEFAULT@), unknown
+ * (@code IPP_TAG_UNKNOWN@), no-value (@code IPP_TAG_NOVALUE@), not-settable
+ * (@code IPP_TAG_NOTSETTABLE@), delete-attribute (@code IPP_TAG_DELETEATTR@), and
+ * admin-define (@code IPP_TAG_ADMINDEFINE@).
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddOutOfBand(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name) /* I - Name of attribute */
+{
+ DEBUG_printf(("ippAddOutOfBand(ipp=%p, group=%02x(%s), value_tag=%02x(%s), "
+ "name=\"%s\")", ipp, group, ippTagString(group), value_tag,
+ ippTagString(value_tag), name));
+
+ value_tag &= IPP_TAG_CUPS_MASK;
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ (value_tag != IPP_TAG_UNSUPPORTED_VALUE &&
+ value_tag != IPP_TAG_DEFAULT &&
+ value_tag != IPP_TAG_UNKNOWN &&
+ value_tag != IPP_TAG_NOVALUE &&
+ value_tag != IPP_TAG_NOTSETTABLE &&
+ value_tag != IPP_TAG_DELETEATTR &&
+ value_tag != IPP_TAG_ADMINDEFINE))
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ return (ipp_add_attr(ipp, name, group, value_tag, 1));
+}
+
+
+/*
+ * 'ippAddRange()' - Add a range of values 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * The @code lower@ parameter must be less than or equal to the @code upper@ parameter.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddRange(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int lower, /* I - Lower value */
+ int upper) /* I - Upper value */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddRange(ipp=%p, group=%02x(%s), name=\"%s\", lower=%d, "
+ "upper=%d)", ipp, group, ippTagString(group), name, lower,
+ upper));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, 1)) == NULL)
+ return (NULL);
+
+ attr->values[0].range.lower = lower;
+ attr->values[0].range.upper = upper;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddRanges()' - Add ranges of values 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddRanges(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const int *lower, /* I - Lower values */
+ const int *upper) /* I - Upper values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippAddRanges(ipp=%p, group=%02x(%s), name=\"%s\", "
+ "num_values=%d, lower=%p, upper=%p)", ipp, group,
+ ippTagString(group), name, num_values, lower, upper));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ num_values < 1)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, num_values)) == NULL)
+ return (NULL);
+
+ if (lower && upper)
+ {
+ for (i = num_values, value = attr->values;
+ i > 0;
+ i --, value ++)
+ {
+ value->range.lower = *lower++;
+ value->range.upper = *upper++;
+ }
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddResolution()' - Add a resolution value 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddResolution(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ ipp_res_t units, /* I - Units for resolution */
+ int xres, /* I - X resolution */
+ int yres) /* I - Y resolution */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddResolution(ipp=%p, group=%02x(%s), name=\"%s\", "
+ "units=%d, xres=%d, yres=%d)", ipp, group,
+ ippTagString(group), name, units, xres, yres));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM ||
+ xres < 0 || yres < 0)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, 1)) == NULL)
+ return (NULL);
+
+ attr->values[0].resolution.xres = xres;
+ attr->values[0].resolution.yres = yres;
+ attr->values[0].resolution.units = units;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddResolutions()' - Add resolution values 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddResolutions(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values,/* I - Number of values */
+ ipp_res_t units, /* I - Units for resolution */
+ const int *xres, /* I - X resolutions */
+ const int *yres) /* I - Y resolutions */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippAddResolutions(ipp=%p, group=%02x(%s), name=\"%s\", "
+ "num_value=%d, units=%d, xres=%p, yres=%p)", ipp, group,
+ ippTagString(group), name, num_values, units, xres, yres));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ num_values < 1 ||
+ units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, num_values)) == NULL)
+ return (NULL);
+
+ if (xres && yres)
+ {
+ for (i = num_values, value = attr->values;
+ i > 0;
+ i --, value ++)
+ {
+ value->resolution.xres = *xres++;
+ value->resolution.yres = *yres++;
+ value->resolution.units = units;
+ }
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddSeparator()' - Add a group separator 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.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddSeparator(ipp_t *ipp) /* I - IPP message */
+{
+ DEBUG_printf(("ippAddSeparator(ipp=%p)", ipp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (NULL);
+
+ /*
+ * Create the attribute...
+ */
+
+ return (ipp_add_attr(ipp, NULL, IPP_TAG_ZERO, IPP_TAG_ZERO, 0));
+}
+
+
+/*
+ * 'ippAddString()' - Add a language-encoded string 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
+ * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
+ * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
+ * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
+ * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
+ * (@code IPP_TAG_URISCHEME@).
+ *
+ * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
+ * textWithLanguage string values and must be @code NULL@ for all other string values.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddString(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ const char *language, /* I - Language code */
+ const char *value) /* I - Value */
+{
+ ipp_tag_t temp_tag; /* Temporary value tag (masked) */
+ ipp_attribute_t *attr; /* New attribute */
+ char code[IPP_MAX_LANGUAGE];
+ /* Charset/language code buffer */
+
+
+ DEBUG_printf(("ippAddString(ipp=%p, group=%02x(%s), value_tag=%02x(%s), "
+ "name=\"%s\", language=\"%s\", value=\"%s\")", ipp,
+ group, ippTagString(group), value_tag, ippTagString(value_tag), name,
+ language, value));
+
+ /*
+ * Range check input...
+ */
+
+ temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
+
+#if 0
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ (temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
+ temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE)
+ return (NULL);
+
+ if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
+ != (language != NULL))
+ return (NULL);
+#else
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (NULL);
+#endif /* 0 */
+
+ /*
+ * See if we need to map charset, language, or locale values...
+ */
+
+ if (language && ((int)value_tag & IPP_TAG_CUPS_CONST) &&
+ strcmp(language, ipp_lang_code(language, code, sizeof(code))))
+ value_tag = temp_tag; /* Don't do a fast copy */
+ else if (value && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST) &&
+ strcmp(value, ipp_get_code(value, code, sizeof(code))))
+ value_tag = temp_tag; /* Don't do a fast copy */
+ else if (value && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST) &&
+ strcmp(value, ipp_lang_code(value, code, sizeof(code))))
+ value_tag = temp_tag; /* Don't do a fast copy */
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
+ return (NULL);
+
+ /*
+ * Initialize the attribute data...
+ */
+
+ if ((int)value_tag & IPP_TAG_CUPS_CONST)
+ {
+ attr->values[0].string.language = (char *)language;
+ attr->values[0].string.text = (char *)value;
+ }
+ else
+ {
+ if (language)
+ attr->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language, code,
+ sizeof(code)));
+
+ if (value)
+ {
+ if (value_tag == IPP_TAG_CHARSET)
+ attr->values[0].string.text = _cupsStrAlloc(ipp_get_code(value, code,
+ sizeof(code)));
+ else if (value_tag == IPP_TAG_LANGUAGE)
+ attr->values[0].string.text = _cupsStrAlloc(ipp_lang_code(value, code,
+ sizeof(code)));
+ else
+ attr->values[0].string.text = _cupsStrAlloc(value);
+ }
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddStringf()' - Add a formatted string 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document
+ * (@code IPP_TAG_DOCUMENT@), event notification
+ * (@code IPP_TAG_EVENT_NOTIFICATION@), operation (@code IPP_TAG_OPERATION@),
+ * printer (@code IPP_TAG_PRINTER@), subscription (@code IPP_TAG_SUBSCRIPTION@),
+ * or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
+ * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
+ * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
+ * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
+ * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
+ * (@code IPP_TAG_URISCHEME@).
+ *
+ * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage
+ * and textWithLanguage string values and must be @code NULL@ for all other
+ * string values.
+ *
+ * The @code format@ parameter uses formatting characters compatible with the
+ * printf family of standard functions. Additional arguments follow it as
+ * needed. The formatted string is truncated as needed to the maximum length of
+ * the corresponding value type.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddStringf(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ const char *language, /* I - Language code (@code NULL@ for default) */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ ipp_attribute_t *attr; /* New attribute */
+ va_list ap; /* Argument pointer */
+
+
+ va_start(ap, format);
+ attr = ippAddStringfv(ipp, group, value_tag, name, language, format, ap);
+ va_end(ap);
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddStringfv()' - Add a formatted string 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document
+ * (@code IPP_TAG_DOCUMENT@), event notification
+ * (@code IPP_TAG_EVENT_NOTIFICATION@), operation (@code IPP_TAG_OPERATION@),
+ * printer (@code IPP_TAG_PRINTER@), subscription (@code IPP_TAG_SUBSCRIPTION@),
+ * or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
+ * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
+ * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
+ * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
+ * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
+ * (@code IPP_TAG_URISCHEME@).
+ *
+ * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage
+ * and textWithLanguage string values and must be @code NULL@ for all other
+ * string values.
+ *
+ * The @code format@ parameter uses formatting characters compatible with the
+ * printf family of standard functions. Additional arguments are passed in the
+ * stdarg pointer @code ap@. The formatted string is truncated as needed to the
+ * maximum length of the corresponding value type.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddStringfv(ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ const char *language, /* I - Language code (@code NULL@ for default) */
+ const char *format, /* I - Printf-style format string */
+ va_list ap) /* I - Additional arguments */
+{
+ char buffer[IPP_MAX_TEXT + 4];
+ /* Formatted text string */
+ ssize_t bytes, /* Length of formatted value */
+ max_bytes; /* Maximum number of bytes for value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
+ value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
+ !format)
+ return (NULL);
+
+ if ((value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_NAMELANG)
+ != (language != NULL))
+ return (NULL);
+
+ /*
+ * Format the string...
+ */
+
+ if (!strcmp(format, "%s"))
+ {
+ /*
+ * Optimize the simple case...
+ */
+
+ const char *s = va_arg(ap, char *);
+
+ if (!s)
+ s = "(null)";
+
+ bytes = strlen(s);
+ strlcpy(buffer, s, sizeof(buffer));
+ }
+ else
+ {
+ /*
+ * Do a full formatting of the message...
+ */
+
+ if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
+ return (NULL);
+ }
+
+ /*
+ * Limit the length of the string...
+ */
+
+ switch (value_tag)
+ {
+ default :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_TEXTLANG :
+ max_bytes = IPP_MAX_TEXT;
+ break;
+
+ case IPP_TAG_NAME :
+ case IPP_TAG_NAMELANG :
+ max_bytes = IPP_MAX_NAME;
+ break;
+
+ case IPP_TAG_CHARSET :
+ max_bytes = IPP_MAX_CHARSET;
+ break;
+
+ case IPP_TAG_KEYWORD :
+ max_bytes = IPP_MAX_KEYWORD;
+ break;
+
+ case IPP_TAG_LANGUAGE :
+ max_bytes = IPP_MAX_LANGUAGE;
+ break;
+
+ case IPP_TAG_MIMETYPE :
+ max_bytes = IPP_MAX_MIMETYPE;
+ break;
+
+ case IPP_TAG_URI :
+ max_bytes = IPP_MAX_URI;
+ break;
+
+ case IPP_TAG_URISCHEME :
+ max_bytes = IPP_MAX_URISCHEME;
+ break;
+ }
+
+ if (bytes >= max_bytes)
+ {
+ char *bufmax, /* Buffer at max_bytes */
+ *bufptr; /* Pointer into buffer */
+
+ bufptr = buffer + strlen(buffer) - 1;
+ bufmax = buffer + max_bytes - 1;
+
+ while (bufptr > bufmax)
+ {
+ if (*bufptr & 0x80)
+ {
+ while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
+ bufptr --;
+ }
+
+ bufptr --;
+ }
+
+ *bufptr = '\0';
+ }
+
+ /*
+ * Add the formatted string and return...
+ */
+
+ return (ippAddString(ipp, group, value_tag, name, language, buffer));
+}
+
+
+/*
+ * 'ippAddStrings()' - Add language-encoded strings 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.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
+ * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
+ * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
+ * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
+ * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
+ * (@code IPP_TAG_URISCHEME@).
+ *
+ * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
+ * textWithLanguage string values and must be @code NULL@ for all other string values.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddStrings(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t value_tag, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const char *language, /* I - Language code (@code NULL@ for default) */
+ const char * const *values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_tag_t temp_tag; /* Temporary value tag (masked) */
+ ipp_attribute_t *attr; /* New attribute */
+ _ipp_value_t *value; /* Current value */
+ char code[32]; /* Language/charset value buffer */
+
+
+ DEBUG_printf(("ippAddStrings(ipp=%p, group=%02x(%s), value_tag=%02x(%s), "
+ "name=\"%s\", num_values=%d, language=\"%s\", values=%p)", ipp,
+ group, ippTagString(group), value_tag, ippTagString(value_tag), name,
+ num_values, language, values));
+
+ /*
+ * Range check input...
+ */
+
+ temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
+
+#if 0
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ (temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
+ temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE ||
+ num_values < 1)
+ return (NULL);
+
+ if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
+ != (language != NULL))
+ return (NULL);
+#else
+ if (!ipp || !name || group < IPP_TAG_ZERO ||
+ group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
+ num_values < 1)
+ return (NULL);
+#endif /* 0 */
+
+ /*
+ * See if we need to map charset, language, or locale values...
+ */
+
+ if (language && ((int)value_tag & IPP_TAG_CUPS_CONST) &&
+ strcmp(language, ipp_lang_code(language, code, sizeof(code))))
+ value_tag = temp_tag; /* Don't do a fast copy */
+ else if (values && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST))
+ {
+ for (i = 0; i < num_values; i ++)
+ if (strcmp(values[i], ipp_get_code(values[i], code, sizeof(code))))
+ {
+ value_tag = temp_tag; /* Don't do a fast copy */
+ break;
+ }
+ }
+ else if (values && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST))
+ {
+ for (i = 0; i < num_values; i ++)
+ if (strcmp(values[i], ipp_lang_code(values[i], code, sizeof(code))))
+ {
+ value_tag = temp_tag; /* Don't do a fast copy */
+ break;
+ }
+ }
+
+ /*
+ * Create the attribute...
+ */
+
+ if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
+ return (NULL);
+
+ /*
+ * Initialize the attribute data...
+ */
+
+ for (i = num_values, value = attr->values;
+ i > 0;
+ i --, value ++)
+ {
+ if (language)
+ {
+ if (value == attr->values)
+ {
+ if ((int)value_tag & IPP_TAG_CUPS_CONST)
+ value->string.language = (char *)language;
+ else
+ value->string.language = _cupsStrAlloc(ipp_lang_code(language, code,
+ sizeof(code)));
+ }
+ else
+ value->string.language = attr->values[0].string.language;
+ }
+
+ if (values)
+ {
+ if ((int)value_tag & IPP_TAG_CUPS_CONST)
+ value->string.text = (char *)*values++;
+ else if (value_tag == IPP_TAG_CHARSET)
+ value->string.text = _cupsStrAlloc(ipp_get_code(*values++, code, sizeof(code)));
+ else if (value_tag == IPP_TAG_LANGUAGE)
+ value->string.text = _cupsStrAlloc(ipp_lang_code(*values++, code, sizeof(code)));
+ else
+ value->string.text = _cupsStrAlloc(*values++);
+ }
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippContainsInteger()' - Determine whether an attribute contains the
+ * specified value or is within the list of ranges.
+ *
+ * Returns non-zero when the attribute contains either a matching integer or
+ * enum value, or the value falls within one of the rangeOfInteger values for
+ * the attribute.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on a match, 0 on no match */
+ippContainsInteger(
+ ipp_attribute_t *attr, /* I - Attribute */
+ int value) /* I - Integer/enum value */
+{
+ int i; /* Looping var */
+ _ipp_value_t *avalue; /* Current attribute value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!attr)
+ return (0);
+
+ if (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM &&
+ attr->value_tag != IPP_TAG_RANGE)
+ return (0);
+
+ /*
+ * Compare...
+ */
+
+ if (attr->value_tag == IPP_TAG_RANGE)
+ {
+ for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
+ if (value >= avalue->range.lower && value <= avalue->range.upper)
+ return (1);
+ }
+ else
+ {
+ for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
+ if (value == avalue->integer)
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ippContainsString()' - Determine whether an attribute contains the
+ * specified string value.
+ *
+ * Returns non-zero when the attribute contains a matching charset, keyword,
+ * language, mimeMediaType, name, text, URI, or URI scheme value.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on a match, 0 on no match */
+ippContainsString(
+ ipp_attribute_t *attr, /* I - Attribute */
+ const char *value) /* I - String value */
+{
+ int i; /* Looping var */
+ _ipp_value_t *avalue; /* Current attribute value */
+
+
+ DEBUG_printf(("ippContainsString(attr=%p, value=\"%s\")", attr, value));
+
+ /*
+ * Range check input...
+ */
+
+ if (!attr || !value)
+ {
+ DEBUG_puts("1ippContainsString: Returning 0 (bad input)");
+ return (0);
+ }
+
+ /*
+ * Compare...
+ */
+
+ DEBUG_printf(("1ippContainsString: attr %s, %s with %d values.",
+ attr->name, ippTagString(attr->value_tag),
+ attr->num_values));
+
+ switch (attr->value_tag & IPP_TAG_CUPS_MASK)
+ {
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ case IPP_TAG_NAME :
+ case IPP_TAG_NAMELANG :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ for (i = attr->num_values, avalue = attr->values;
+ i > 0;
+ i --, avalue ++)
+ {
+ DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
+ attr->num_values - i, avalue->string.text));
+
+ if (!strcmp(value, avalue->string.text))
+ {
+ DEBUG_puts("1ippContainsString: Returning 1 (match)");
+ return (1);
+ }
+ }
+
+ default :
+ break;
+ }
+
+ DEBUG_puts("1ippContainsString: Returning 0 (no match)");
+
+ return (0);
+}
+
+
+/*
+ * 'ippCopyAttribute()' - Copy an attribute.
+ *
+ * The specified attribute, @code attr@, is copied to the destination IPP message.
+ * When @code quickcopy@ is non-zero, a "shallow" reference copy of the attribute is
+ * created - this should only be done as long as the original source IPP message will
+ * not be freed for the life of the destination.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+
+ipp_attribute_t * /* O - New attribute */
+ippCopyAttribute(
+ ipp_t *dst, /* I - Destination IPP message */
+ ipp_attribute_t *srcattr, /* I - Attribute to copy */
+ int quickcopy) /* I - 1 for a referenced copy, 0 for normal */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *dstattr; /* Destination attribute */
+ _ipp_value_t *srcval, /* Source value */
+ *dstval; /* Destination value */
+
+
+ DEBUG_printf(("ippCopyAttribute(dst=%p, srcattr=%p, quickcopy=%d)", dst, srcattr,
+ quickcopy));
+
+ /*
+ * Range check input...
+ */
+
+ if (!dst || !srcattr)
+ return (NULL);
+
+ /*
+ * Copy it...
+ */
+
+ quickcopy = quickcopy ? IPP_TAG_CUPS_CONST : 0;
+
+ switch (srcattr->value_tag & ~IPP_TAG_CUPS_CONST)
+ {
+ case IPP_TAG_ZERO :
+ dstattr = ippAddSeparator(dst);
+ break;
+
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
+ srcattr->name, srcattr->num_values, NULL);
+ if (!dstattr)
+ break;
+
+ for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ dstval->integer = srcval->integer;
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ dstattr = ippAddBooleans(dst, srcattr->group_tag, srcattr->name,
+ srcattr->num_values, NULL);
+ if (!dstattr)
+ break;
+
+ for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ dstval->boolean = srcval->boolean;
+ break;
+
+ 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 :
+ dstattr = ippAddStrings(dst, srcattr->group_tag,
+ (ipp_tag_t)(srcattr->value_tag | quickcopy),
+ srcattr->name, srcattr->num_values, NULL, NULL);
+ if (!dstattr)
+ break;
+
+ if (quickcopy)
+ {
+ for (i = srcattr->num_values, srcval = srcattr->values,
+ dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ dstval->string.text = srcval->string.text;
+ }
+ else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
+ {
+ for (i = srcattr->num_values, srcval = srcattr->values,
+ dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ dstval->string.text = _cupsStrAlloc(srcval->string.text);
+ }
+ else
+ {
+ for (i = srcattr->num_values, srcval = srcattr->values,
+ dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ dstval->string.text = _cupsStrRetain(srcval->string.text);
+ }
+ break;
+
+ case IPP_TAG_DATE :
+ if (srcattr->num_values != 1)
+ return (NULL);
+
+ dstattr = ippAddDate(dst, srcattr->group_tag, srcattr->name,
+ srcattr->values[0].date);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ dstattr = ippAddResolutions(dst, srcattr->group_tag, srcattr->name,
+ srcattr->num_values, IPP_RES_PER_INCH,
+ NULL, NULL);
+ if (!dstattr)
+ break;
+
+ for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ dstval->resolution.xres = srcval->resolution.xres;
+ dstval->resolution.yres = srcval->resolution.yres;
+ dstval->resolution.units = srcval->resolution.units;
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ dstattr = ippAddRanges(dst, srcattr->group_tag, srcattr->name,
+ srcattr->num_values, NULL, NULL);
+ if (!dstattr)
+ break;
+
+ for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ dstval->range.lower = srcval->range.lower;
+ dstval->range.upper = srcval->range.upper;
+ }
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ dstattr = ippAddStrings(dst, srcattr->group_tag,
+ (ipp_tag_t)(srcattr->value_tag | quickcopy),
+ srcattr->name, srcattr->num_values, NULL, NULL);
+ if (!dstattr)
+ break;
+
+ if (quickcopy)
+ {
+ for (i = srcattr->num_values, srcval = srcattr->values,
+ dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ dstval->string.language = srcval->string.language;
+ dstval->string.text = srcval->string.text;
+ }
+ }
+ else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
+ {
+ for (i = srcattr->num_values, srcval = srcattr->values,
+ dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ if (srcval == srcattr->values)
+ dstval->string.language = _cupsStrAlloc(srcval->string.language);
+ else
+ dstval->string.language = dstattr->values[0].string.language;
+
+ dstval->string.text = _cupsStrAlloc(srcval->string.text);
+ }
+ }
+ else
+ {
+ for (i = srcattr->num_values, srcval = srcattr->values,
+ dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ if (srcval == srcattr->values)
+ dstval->string.language = _cupsStrRetain(srcval->string.language);
+ else
+ dstval->string.language = dstattr->values[0].string.language;
+
+ dstval->string.text = _cupsStrRetain(srcval->string.text);
+ }
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name,
+ srcattr->num_values, NULL);
+ if (!dstattr)
+ break;
+
+ for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ dstval->collection = srcval->collection;
+ srcval->collection->use ++;
+ }
+ break;
+
+ case IPP_TAG_STRING :
+ default :
+ /* TODO: Implement quick copy for unknown/octetString values */
+ dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
+ srcattr->name, srcattr->num_values, NULL);
+ if (!dstattr)
+ break;
+
+ for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
+ i > 0;
+ i --, srcval ++, dstval ++)
+ {
+ dstval->unknown.length = srcval->unknown.length;
+
+ if (dstval->unknown.length > 0)
+ {
+ if ((dstval->unknown.data = malloc(dstval->unknown.length)) == NULL)
+ dstval->unknown.length = 0;
+ else
+ memcpy(dstval->unknown.data, srcval->unknown.data, dstval->unknown.length);
+ }
+ }
+ break; /* anti-compiler-warning-code */
+ }
+
+ return (dstattr);
+}
+
+
+/*
+ * 'ippCopyAttributes()' - Copy attributes from one IPP message to another.
+ *
+ * Zero or more attributes are copied from the source IPP message, @code@ src, to the
+ * destination IPP message, @code dst@. When @code quickcopy@ is non-zero, a "shallow"
+ * reference copy of the attribute is created - this should only be done as long as the
+ * original source IPP message will not be freed for the life of the destination.
+ *
+ * The @code cb@ and @code context@ parameters provide a generic way to "filter" the
+ * attributes that are copied - the function must return 1 to copy the attribute or
+ * 0 to skip it. The function may also choose to do a partial copy of the source attribute
+ * itself.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on error */
+ippCopyAttributes(
+ ipp_t *dst, /* I - Destination IPP message */
+ ipp_t *src, /* I - Source IPP message */
+ int quickcopy, /* I - 1 for a referenced copy, 0 for normal */
+ ipp_copycb_t cb, /* I - Copy callback or @code NULL@ for none */
+ void *context) /* I - Context pointer */
+{
+ ipp_attribute_t *srcattr; /* Source attribute */
+
+
+ DEBUG_printf(("ippCopyAttributes(dst=%p, src=%p, quickcopy=%d, cb=%p, context=%p)",
+ dst, src, quickcopy, cb, context));
+
+ /*
+ * Range check input...
+ */
+
+ if (!dst || !src)
+ return (0);
+
+ /*
+ * Loop through source attributes and copy as needed...
+ */
+
+ for (srcattr = src->attrs; srcattr; srcattr = srcattr->next)
+ if (!cb || (*cb)(context, dst, srcattr))
+ if (!ippCopyAttribute(dst, srcattr, quickcopy))
+ return (0);
+
+ return (1);
+}
+
+
+/*
+ * 'ippDateToTime()' - Convert from RFC 1903 Date/Time format to UNIX time
+ * in seconds.
+ */
+
+time_t /* O - UNIX time value */
+ippDateToTime(const ipp_uchar_t *date) /* I - RFC 1903 date info */
+{
+ struct tm unixdate; /* UNIX date/time info */
+ time_t t; /* Computed time */
+
+
+ if (!date)
+ return (0);
+
+ memset(&unixdate, 0, sizeof(unixdate));
+
+ /*
+ * RFC-1903 date/time format is:
+ *
+ * Byte(s) Description
+ * ------- -----------
+ * 0-1 Year (0 to 65535)
+ * 2 Month (1 to 12)
+ * 3 Day (1 to 31)
+ * 4 Hours (0 to 23)
+ * 5 Minutes (0 to 59)
+ * 6 Seconds (0 to 60, 60 = "leap second")
+ * 7 Deciseconds (0 to 9)
+ * 8 +/- UTC
+ * 9 UTC hours (0 to 11)
+ * 10 UTC minutes (0 to 59)
+ */
+
+ unixdate.tm_year = ((date[0] << 8) | date[1]) - 1900;
+ unixdate.tm_mon = date[2] - 1;
+ unixdate.tm_mday = date[3];
+ unixdate.tm_hour = date[4];
+ unixdate.tm_min = date[5];
+ unixdate.tm_sec = date[6];
+
+ t = mktime(&unixdate);
+
+ if (date[8] == '-')
+ t += date[9] * 3600 + date[10] * 60;
+ else
+ t -= date[9] * 3600 + date[10] * 60;
+
+ return (t);
+}
+
+
+/*
+ * 'ippDelete()' - Delete an IPP message.
+ */
+
+void
+ippDelete(ipp_t *ipp) /* I - IPP message */
+{
+ ipp_attribute_t *attr, /* Current attribute */
+ *next; /* Next attribute */
+
+
+ DEBUG_printf(("ippDelete(ipp=%p)", ipp));
+
+ if (!ipp)
+ return;
+
+ ipp->use --;
+ if (ipp->use > 0)
+ return;
+
+ for (attr = ipp->attrs; attr != NULL; attr = next)
+ {
+ next = attr->next;
+
+ ipp_free_values(attr, 0, attr->num_values);
+
+ if (attr->name)
+ _cupsStrFree(attr->name);
+
+ free(attr);
+ }
+
+ free(ipp);
+}
+
+
+/*
+ * 'ippDeleteAttribute()' - Delete a single attribute in an IPP message.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+void
+ippDeleteAttribute(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t *attr) /* I - Attribute to delete */
+{
+ ipp_attribute_t *current, /* Current attribute */
+ *prev; /* Previous attribute */
+
+
+ DEBUG_printf(("ippDeleteAttribute(ipp=%p, attr=%p(%s))", ipp, attr,
+ attr ? attr->name : "(null)"));
+
+ /*
+ * Range check input...
+ */
+
+ if (!attr)
+ return;
+
+ /*
+ * Find the attribute in the list...
+ */
+
+ if (ipp)
+ {
+ for (current = ipp->attrs, prev = NULL;
+ current;
+ prev = current, current = current->next)
+ if (current == attr)
+ {
+ /*
+ * Found it, remove the attribute from the list...
+ */
+
+ if (prev)
+ prev->next = current->next;
+ else
+ ipp->attrs = current->next;
+
+ if (current == ipp->last)
+ ipp->last = prev;
+
+ break;
+ }
+
+ if (!current)
+ return;
+ }
+
+ /*
+ * Free memory used by the attribute...
+ */
+
+ ipp_free_values(attr, 0, attr->num_values);
+
+ if (attr->name)
+ _cupsStrFree(attr->name);
+
+ free(attr);
+}
+
+
+/*
+ * 'ippDeleteValues()' - Delete values in an attribute.
+ *
+ * The @code element@ parameter specifies the first value to delete, starting at
+ * 0. It must be less than the number of values returned by @link ippGetCount@.
+ *
+ * The @code attr@ parameter may be modified as a result of setting the value.
+ *
+ * Deleting all values in an attribute deletes the attribute.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippDeleteValues(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - Attribute */
+ int element, /* I - Index of first value to delete (0-based) */
+ int count) /* I - Number of values to delete */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr ||
+ element < 0 || element >= (*attr)->num_values || count <= 0 ||
+ (element + count) >= (*attr)->num_values)
+ return (0);
+
+ /*
+ * If we are deleting all values, just delete the attribute entirely.
+ */
+
+ if (count == (*attr)->num_values)
+ {
+ ippDeleteAttribute(ipp, *attr);
+ *attr = NULL;
+ return (1);
+ }
+
+ /*
+ * Otherwise free the values in question and return.
+ */
+
+ ipp_free_values(*attr, element, count);
+
+ return (1);
+}
+
+
+/*
+ * 'ippFindAttribute()' - Find a named attribute in a request.
+ */
+
+ipp_attribute_t * /* O - Matching attribute */
+ippFindAttribute(ipp_t *ipp, /* I - IPP message */
+ const char *name, /* I - Name of attribute */
+ ipp_tag_t type) /* I - Type of attribute */
+{
+ DEBUG_printf(("2ippFindAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", ipp,
+ name, type, ippTagString(type)));
+
+ if (!ipp || !name)
+ return (NULL);
+
+ /*
+ * Reset the current pointer...
+ */
+
+ ipp->current = NULL;
+
+ /*
+ * Search for the attribute...
+ */
+
+ return (ippFindNextAttribute(ipp, name, type));
+}
+
+
+/*
+ * 'ippFindNextAttribute()' - Find the next named attribute in a request.
+ */
+
+ipp_attribute_t * /* O - Matching attribute */
+ippFindNextAttribute(ipp_t *ipp, /* I - IPP message */
+ const char *name, /* I - Name of attribute */
+ ipp_tag_t type) /* I - Type of attribute */
+{
+ ipp_attribute_t *attr; /* Current atttribute */
+ ipp_tag_t value_tag; /* Value tag */
+
+
+ DEBUG_printf(("2ippFindNextAttribute(ipp=%p, name=\"%s\", type=%02x(%s))",
+ ipp, name, type, ippTagString(type)));
+
+ if (!ipp || !name)
+ return (NULL);
+
+ if (ipp->current)
+ {
+ ipp->prev = ipp->current;
+ attr = ipp->current->next;
+ }
+ else
+ {
+ ipp->prev = NULL;
+ attr = ipp->attrs;
+ }
+
+ for (; attr != NULL; ipp->prev = attr, attr = attr->next)
+ {
+ DEBUG_printf(("4ippFindAttribute: attr=%p, name=\"%s\"", attr,
+ attr->name));
+
+ value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
+
+ if (attr->name != NULL && _cups_strcasecmp(attr->name, name) == 0 &&
+ (value_tag == type || type == IPP_TAG_ZERO ||
+ (value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
+ (value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
+ {
+ ipp->current = attr;
+
+ return (attr);
+ }
+ }
+
+ ipp->current = NULL;
+ ipp->prev = NULL;
+
+ return (NULL);
+}
+
+
+/*
+ * 'ippFirstAttribute()' - Return the first attribute in the message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_attribute_t * /* O - First attribute or @code NULL@ if none */
+ippFirstAttribute(ipp_t *ipp) /* I - IPP message */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (NULL);
+
+ /*
+ * Return the first attribute...
+ */
+
+ return (ipp->current = ipp->attrs);
+}
+
+
+/*
+ * 'ippGetBoolean()' - Get a boolean value for an attribute.
+ *
+ * The @code element@ parameter specifies which value to get from 0 to
+ * @link ippGetCount(attr)@ - 1.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - Boolean value or 0 on error */
+ippGetBoolean(ipp_attribute_t *attr, /* I - IPP attribute */
+ int element) /* I - Value number (0-based) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || attr->value_tag != IPP_TAG_BOOLEAN ||
+ element < 0 || element >= attr->num_values)
+ return (0);
+
+ /*
+ * Return the value...
+ */
+
+ return (attr->values[element].boolean);
+}
+
+
+/*
+ * 'ippGetCollection()' - Get a collection value for an attribute.
+ *
+ * The @code element@ parameter specifies which value to get from 0 to
+ * @link ippGetCount(attr)@ - 1.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_t * /* O - Collection value or @code NULL@ on error */
+ippGetCollection(
+ ipp_attribute_t *attr, /* I - IPP attribute */
+ int element) /* I - Value number (0-based) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || attr->value_tag != IPP_TAG_BEGIN_COLLECTION ||
+ element < 0 || element >= attr->num_values)
+ return (NULL);
+
+ /*
+ * Return the value...
+ */
+
+ return (attr->values[element].collection);
+}
+
+
+/*
+ * 'ippGetCount()' - Get the number of values in an attribute.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - Number of values or 0 on error */
+ippGetCount(ipp_attribute_t *attr) /* I - IPP attribute */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr)
+ return (0);
+
+ /*
+ * Return the number of values...
+ */
+
+ return (attr->num_values);
+}
+
+
+/*
+ * 'ippGetDate()' - Get a date value for an attribute.
+ *
+ * The @code element@ parameter specifies which value to get from 0 to
+ * @link ippGetCount(attr)@ - 1.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+const ipp_uchar_t * /* O - Date value or @code NULL@ */
+ippGetDate(ipp_attribute_t *attr, /* I - IPP attribute */
+ int element) /* I - Value number (0-based) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || attr->value_tag != IPP_TAG_DATE ||
+ element < 0 || element >= attr->num_values)
+ return (NULL);
+
+ /*
+ * Return the value...
+ */
+
+ return (attr->values[element].date);
+}
+
+
+/*
+ * 'ippGetGroupTag()' - Get the group associated with an attribute.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_tag_t /* O - Group tag or @code IPP_TAG_ZERO@ on error */
+ippGetGroupTag(ipp_attribute_t *attr) /* I - IPP attribute */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr)
+ return (IPP_TAG_ZERO);
+
+ /*
+ * Return the group...
+ */
+
+ return (attr->group_tag);
+}
+
+
+/*
+ * '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.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - Value or 0 on error */
+ippGetInteger(ipp_attribute_t *attr, /* I - IPP attribute */
+ int element) /* I - Value number (0-based) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM) ||
+ element < 0 || element >= attr->num_values)
+ return (0);
+
+ /*
+ * Return the value...
+ */
+
+ return (attr->values[element].integer);
+}
+
+
+/*
+ * 'ippGetName()' - Get the attribute name.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+const char * /* O - Attribute name or @code NULL@ for separators */
+ippGetName(ipp_attribute_t *attr) /* I - IPP attribute */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr)
+ return (NULL);
+
+ /*
+ * Return the name...
+ */
+
+ return (attr->name);
+}
+
+
+/*
+ * '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.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+void * /* O - Pointer to octetString data */
+ippGetOctetString(
+ ipp_attribute_t *attr, /* I - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ int *datalen) /* O - Length of octetString data */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || attr->value_tag != IPP_TAG_STRING ||
+ element < 0 || element >= attr->num_values)
+ {
+ if (datalen)
+ *datalen = 0;
+
+ return (NULL);
+ }
+
+ /*
+ * Return the values...
+ */
+
+ if (datalen)
+ *datalen = attr->values[element].unknown.length;
+
+ return (attr->values[element].unknown.data);
+}
+
+
+/*
+ * 'ippGetOperation()' - Get the operation ID in an IPP message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_op_t /* O - Operation ID or 0 on error */
+ippGetOperation(ipp_t *ipp) /* I - IPP request message */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return ((ipp_op_t)0);
+
+ /*
+ * Return the value...
+ */
+
+ return (ipp->request.op.operation_id);
+}
+
+
+/*
+ * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
+ *
+ * The @code element@ parameter specifies which value to get from 0 to
+ * @link ippGetCount(attr)@ - 1.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - Lower value of range or 0 */
+ippGetRange(ipp_attribute_t *attr, /* I - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ int *uppervalue)/* O - Upper value of range */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || attr->value_tag != IPP_TAG_RANGE ||
+ element < 0 || element >= attr->num_values)
+ {
+ if (uppervalue)
+ *uppervalue = 0;
+
+ return (0);
+ }
+
+ /*
+ * Return the values...
+ */
+
+ if (uppervalue)
+ *uppervalue = attr->values[element].range.upper;
+
+ return (attr->values[element].range.lower);
+}
+
+
+/*
+ * 'ippGetRequestId()' - Get the request ID from an IPP message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - Request ID or 0 on error */
+ippGetRequestId(ipp_t *ipp) /* I - IPP message */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (0);
+
+ /*
+ * Return the request ID...
+ */
+
+ return (ipp->request.any.request_id);
+}
+
+
+/*
+ * 'ippGetResolution()' - Get a resolution value for an attribute.
+ *
+ * The @code element@ parameter specifies which value to get from 0 to
+ * @link ippGetCount(attr)@ - 1.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - Horizontal/cross feed resolution or 0 */
+ippGetResolution(
+ ipp_attribute_t *attr, /* I - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ int *yres, /* O - Vertical/feed resolution */
+ ipp_res_t *units) /* O - Units for resolution */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || attr->value_tag != IPP_TAG_RESOLUTION ||
+ element < 0 || element >= attr->num_values)
+ {
+ if (yres)
+ *yres = 0;
+
+ if (units)
+ *units = (ipp_res_t)0;
+
+ return (0);
+ }
+
+ /*
+ * Return the value...
+ */
+
+ if (yres)
+ *yres = attr->values[element].resolution.yres;
+
+ if (units)
+ *units = attr->values[element].resolution.units;
+
+ return (attr->values[element].resolution.xres);
+}
+
+
+/*
+ * 'ippGetState()' - Get the IPP message state.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_state_t /* O - IPP message state value */
+ippGetState(ipp_t *ipp) /* I - IPP message */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (IPP_STATE_IDLE);
+
+ /*
+ * Return the value...
+ */
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'ippGetStatusCode()' - Get the status code from an IPP response or event message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_status_t /* O - Status code in IPP message */
+ippGetStatusCode(ipp_t *ipp) /* I - IPP response or event message */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (IPP_STATUS_ERROR_INTERNAL);
+
+ /*
+ * Return the value...
+ */
+
+ return (ipp->request.status.status_code);
+}
+
+
+/*
+ * '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.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+const char *
+ippGetString(ipp_attribute_t *attr, /* I - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ const char **language)/* O - Language code (@code NULL@ for don't care) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr || element < 0 || element >= attr->num_values ||
+ (attr->value_tag != IPP_TAG_TEXTLANG && attr->value_tag != IPP_TAG_NAMELANG &&
+ (attr->value_tag < IPP_TAG_TEXT || attr->value_tag > IPP_TAG_MIMETYPE)))
+ return (NULL);
+
+ /*
+ * Return the value...
+ */
+
+ if (language)
+ *language = attr->values[element].string.language;
+
+ return (attr->values[element].string.text);
+}
+
+
+/*
+ * 'ippGetValueTag()' - Get the value tag for an attribute.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_tag_t /* O - Value tag or @code IPP_TAG_ZERO@ on error */
+ippGetValueTag(ipp_attribute_t *attr) /* I - IPP attribute */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!attr)
+ return (IPP_TAG_ZERO);
+
+ /*
+ * Return the value...
+ */
+
+ return (attr->value_tag & IPP_TAG_CUPS_MASK);
+}
+
+
+/*
+ * 'ippGetVersion()' - Get the major and minor version number from an IPP message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+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@ */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ {
+ if (minor)
+ *minor = 0;
+
+ return (0);
+ }
+
+ /*
+ * Return the value...
+ */
+
+ if (minor)
+ *minor = ipp->request.any.version[1];
+
+ return (ipp->request.any.version[0]);
+}
+
+
+/*
+ * 'ippLength()' - Compute the length of an IPP message.
+ */
+
+size_t /* O - Size of IPP message */
+ippLength(ipp_t *ipp) /* I - IPP message */
+{
+ return (ipp_length(ipp, 0));
+}
+
+
+/*
+ * 'ippNextAttribute()' - Return the next attribute in the message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+ipp_attribute_t * /* O - Next attribute or @code NULL@ if none */
+ippNextAttribute(ipp_t *ipp) /* I - IPP message */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !ipp->current)
+ return (NULL);
+
+ /*
+ * Return the next attribute...
+ */
+
+ return (ipp->current = ipp->current->next);
+}
+
+
+/*
+ * 'ippNew()' - Allocate a new IPP message.
+ */
+
+ipp_t * /* O - New IPP message */
+ippNew(void)
+{
+ ipp_t *temp; /* New IPP message */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ DEBUG_puts("ippNew()");
+
+ if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
+ {
+ /*
+ * Set default version - usually 2.0...
+ */
+
+ if (cg->server_version == 0)
+ _cupsSetDefaults();
+
+ temp->request.any.version[0] = cg->server_version / 10;
+ temp->request.any.version[1] = cg->server_version % 10;
+ temp->use = 1;
+ }
+
+ DEBUG_printf(("1ippNew: Returning %p", temp));
+
+ return (temp);
+}
+
+
+/*
+ * '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.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ipp_t * /* O - IPP request message */
+ippNewRequest(ipp_op_t op) /* I - Operation code */
+{
+ ipp_t *request; /* IPP request message */
+ cups_lang_t *language; /* Current language localization */
+ static int request_id = 0; /* Current request ID */
+ static _cups_mutex_t request_mutex = _CUPS_MUTEX_INITIALIZER;
+ /* Mutex for request ID */
+
+
+ DEBUG_printf(("ippNewRequest(op=%02x(%s))", op, ippOpString(op)));
+
+ /*
+ * Create a new IPP message...
+ */
+
+ if ((request = ippNew()) == NULL)
+ return (NULL);
+
+ /*
+ * Set the operation and request ID...
+ */
+
+ _cupsMutexLock(&request_mutex);
+
+ request->request.op.operation_id = op;
+ request->request.op.request_id = ++request_id;
+
+ _cupsMutexUnlock(&request_mutex);
+
+ /*
+ * Use UTF-8 as the character set...
+ */
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ /*
+ * Get the language from the current locale...
+ */
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Return the new request...
+ */
+
+ return (request);
+}
+
+
+/*
+ * '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,
+ * respectively.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+ipp_t * /* O - IPP response message */
+ippNewResponse(ipp_t *request) /* I - IPP request message */
+{
+ ipp_t *response; /* IPP response message */
+ ipp_attribute_t *attr; /* Current attribute */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!request)
+ return (NULL);
+
+ /*
+ * Create a new IPP message...
+ */
+
+ if ((response = ippNew()) == NULL)
+ return (NULL);
+
+ /*
+ * Copy the request values over to the response...
+ */
+
+ response->request.status.version[0] = request->request.op.version[0];
+ response->request.status.version[1] = request->request.op.version[1];
+ response->request.status.request_id = request->request.op.request_id;
+
+ /*
+ * The first attribute MUST be attributes-charset...
+ */
+
+ attr = request->attrs;
+
+ if (attr && attr->name && !strcmp(attr->name, "attributes-charset") &&
+ attr->group_tag == IPP_TAG_OPERATION &&
+ attr->value_tag == IPP_TAG_CHARSET &&
+ attr->num_values == 1)
+ {
+ /*
+ * Copy charset from request...
+ */
+
+ ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, attr->values[0].string.text);
+ }
+ else
+ {
+ /*
+ * Use "utf-8" as the default...
+ */
+
+ ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+ }
+
+ /*
+ * Then attributes-natural-language...
+ */
+
+ if (attr)
+ attr = attr->next;
+
+ if (attr && attr->name &&
+ !strcmp(attr->name, "attributes-natural-language") &&
+ attr->group_tag == IPP_TAG_OPERATION &&
+ attr->value_tag == IPP_TAG_LANGUAGE &&
+ attr->num_values == 1)
+ {
+ /*
+ * Copy language from request...
+ */
+
+ ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ attr->values[0].string.text);
+ }
+ else
+ {
+ /*
+ * Use the language from the current locale...
+ */
+
+ cups_lang_t *language = cupsLangDefault();
+ /* Current locale */
+
+ ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+ }
+
+ return (response);
+}
+
+
+/*
+ * 'ippRead()' - Read data for an IPP message from a HTTP connection.
+ */
+
+ipp_state_t /* O - Current state */
+ippRead(http_t *http, /* I - HTTP connection */
+ ipp_t *ipp) /* I - IPP data */
+{
+ DEBUG_printf(("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT,
+ http, ipp, CUPS_LLCAST (http ? http->data_remaining : -1)));
+
+ if (!http)
+ return (IPP_STATE_ERROR);
+
+ DEBUG_printf(("2ippRead: http->state=%d, http->used=%d", http->state,
+ http->used));
+
+ return (ippReadIO(http, (ipp_iocb_t)ipp_read_http, http->blocking, NULL,
+ ipp));
+}
+
+
+/*
+ * 'ippReadFile()' - Read data for an IPP message from a file.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ipp_state_t /* O - Current state */
+ippReadFile(int fd, /* I - HTTP data */
+ ipp_t *ipp) /* I - IPP data */
+{
+ DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, ipp));
+
+ return (ippReadIO(&fd, (ipp_iocb_t)ipp_read_file, 1, NULL, ipp));
+}
+
+
+/*
+ * 'ippReadIO()' - Read data for an IPP message.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ipp_state_t /* O - Current state */
+ippReadIO(void *src, /* I - Data source */
+ ipp_iocb_t cb, /* I - Read callback function */
+ int blocking, /* I - Use blocking IO? */
+ ipp_t *parent, /* I - Parent request, if any */
+ ipp_t *ipp) /* I - IPP data */
+{
+ int n; /* Length of data */
+ unsigned char *buffer, /* Data buffer */
+ string[IPP_MAX_TEXT],
+ /* Small string buffer */
+ *bufptr; /* Pointer into buffer */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_tag_t tag; /* Current tag */
+ ipp_tag_t value_tag; /* Current value tag */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippReadIO(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)",
+ src, cb, blocking, parent, ipp));
+ DEBUG_printf(("2ippReadIO: ipp->state=%d", ipp ? ipp->state : IPP_STATE_ERROR));
+
+ if (!src || !ipp)
+ return (IPP_STATE_ERROR);
+
+ if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
+ {
+ DEBUG_puts("1ippReadIO: Unable to get read buffer.");
+ return (IPP_STATE_ERROR);
+ }
+
+ switch (ipp->state)
+ {
+ case IPP_STATE_IDLE :
+ ipp->state ++; /* Avoid common problem... */
+
+ case IPP_STATE_HEADER :
+ if (parent == NULL)
+ {
+ /*
+ * Get the request header...
+ */
+
+ if ((*cb)(src, buffer, 8) < 8)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read header.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ /*
+ * Then copy the request header over...
+ */
+
+ ipp->request.any.version[0] = buffer[0];
+ ipp->request.any.version[1] = buffer[1];
+ ipp->request.any.op_status = (buffer[2] << 8) | buffer[3];
+ ipp->request.any.request_id = (((((buffer[4] << 8) | buffer[5]) << 8) |
+ buffer[6]) << 8) | buffer[7];
+
+ DEBUG_printf(("2ippReadIO: version=%d.%d", buffer[0], buffer[1]));
+ DEBUG_printf(("2ippReadIO: op_status=%04x",
+ ipp->request.any.op_status));
+ DEBUG_printf(("2ippReadIO: request_id=%d",
+ ipp->request.any.request_id));
+ }
+
+ ipp->state = IPP_STATE_ATTRIBUTE;
+ ipp->current = NULL;
+ ipp->curtag = IPP_TAG_ZERO;
+ ipp->prev = ipp->last;
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!blocking)
+ break;
+
+ case IPP_STATE_ATTRIBUTE :
+ for (;;)
+ {
+ if ((*cb)(src, buffer, 1) < 1)
+ {
+ DEBUG_puts("1ippReadIO: Callback returned EOF/error");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_printf(("2ippReadIO: ipp->current=%p, ipp->prev=%p",
+ ipp->current, ipp->prev));
+
+ /*
+ * Read this attribute...
+ */
+
+ tag = (ipp_tag_t)buffer[0];
+ if (tag == IPP_TAG_EXTENSION)
+ {
+ /*
+ * Read 32-bit "extension" tag...
+ */
+
+ if ((*cb)(src, buffer, 4) < 1)
+ {
+ DEBUG_puts("1ippReadIO: Callback returned EOF/error");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ tag = (ipp_tag_t)((((((buffer[0] << 8) | buffer[1]) << 8) |
+ buffer[2]) << 8) | buffer[3]);
+
+ if (tag & IPP_TAG_CUPS_CONST)
+ {
+ /*
+ * Fail if the high bit is set in the tag...
+ */
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1);
+ DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ }
+
+ if (tag == IPP_TAG_END)
+ {
+ /*
+ * No more attributes left...
+ */
+
+ DEBUG_puts("2ippReadIO: IPP_TAG_END.");
+
+ ipp->state = IPP_STATE_DATA;
+ break;
+ }
+ else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
+ {
+ /*
+ * Group tag... Set the current group and continue...
+ */
+
+ if (ipp->curtag == tag)
+ ipp->prev = ippAddSeparator(ipp);
+ else if (ipp->current)
+ ipp->prev = ipp->current;
+
+ ipp->curtag = tag;
+ ipp->current = NULL;
+ DEBUG_printf(("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag,
+ ippTagString(tag), ipp->prev));
+ continue;
+ }
+
+ DEBUG_printf(("2ippReadIO: value tag=%x(%s)", tag,
+ ippTagString(tag)));
+
+ /*
+ * Get the name...
+ */
+
+ if ((*cb)(src, buffer, 2) < 2)
+ {
+ DEBUG_puts("1ippReadIO: unable to read name length.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ n = (buffer[0] << 8) | buffer[1];
+
+ if (n >= IPP_BUF_SIZE)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad name length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_printf(("2ippReadIO: name length=%d", n));
+
+ if (n == 0 && tag != IPP_TAG_MEMBERNAME &&
+ tag != IPP_TAG_END_COLLECTION)
+ {
+ /*
+ * More values for current attribute...
+ */
+
+ if (ipp->current == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1);
+ DEBUG_puts("1ippReadIO: Attribute without name and no current.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ attr = ipp->current;
+ value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
+
+ /*
+ * Make sure we aren't adding a new value of a different
+ * type...
+ */
+
+ if (value_tag == IPP_TAG_ZERO)
+ {
+ /*
+ * Setting the value of a collection member...
+ */
+
+ attr->value_tag = tag;
+ }
+ else if (value_tag == IPP_TAG_TEXTLANG ||
+ value_tag == IPP_TAG_NAMELANG ||
+ (value_tag >= IPP_TAG_TEXT &&
+ value_tag <= IPP_TAG_MIMETYPE))
+ {
+ /*
+ * String values can sometimes come across in different
+ * forms; accept sets of differing values...
+ */
+
+ if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG &&
+ (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) &&
+ tag != IPP_TAG_NOVALUE)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP 1setOf attribute with incompatible value "
+ "tags."), 1);
+ DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)",
+ value_tag, ippTagString(value_tag), tag,
+ ippTagString(tag)));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if (value_tag != tag)
+ {
+ DEBUG_printf(("1ippReadIO: Converting %s attribute from %s to %s.",
+ attr->name, ippTagString(value_tag), ippTagString(tag)));
+ ippSetValueTag(ipp, &attr, tag);
+ }
+ }
+ else if (value_tag == IPP_TAG_INTEGER ||
+ value_tag == IPP_TAG_RANGE)
+ {
+ /*
+ * Integer and rangeOfInteger values can sometimes be mixed; accept
+ * sets of differing values...
+ */
+
+ if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP 1setOf attribute with incompatible value "
+ "tags."), 1);
+ DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)",
+ value_tag, ippTagString(value_tag), tag,
+ ippTagString(tag)));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE)
+ {
+ /*
+ * Convert integer values to rangeOfInteger values...
+ */
+
+ DEBUG_printf(("1ippReadIO: Converting %s attribute to "
+ "rangeOfInteger.", attr->name));
+ ippSetValueTag(ipp, &attr, IPP_TAG_RANGE);
+ }
+ }
+ else if (value_tag != tag)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP 1setOf attribute with incompatible value "
+ "tags."), 1);
+ DEBUG_printf(("1ippReadIO: value tag %x(%s) != %x(%s)",
+ value_tag, ippTagString(value_tag), tag,
+ ippTagString(tag)));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ /*
+ * Finally, reallocate the attribute array as needed...
+ */
+
+ if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL)
+ {
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ }
+ else if (tag == IPP_TAG_MEMBERNAME)
+ {
+ /*
+ * Name must be length 0!
+ */
+
+ if (n)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1);
+ DEBUG_puts("1ippReadIO: member name not empty.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if (ipp->current)
+ ipp->prev = ipp->current;
+
+ attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1);
+ if (!attr)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ DEBUG_puts("1ippReadIO: unable to allocate attribute.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p",
+ ipp->current, ipp->prev));
+
+ value = attr->values;
+ }
+ else if (tag != IPP_TAG_END_COLLECTION)
+ {
+ /*
+ * New attribute; read the name and add it...
+ */
+
+ if ((*cb)(src, buffer, n) < n)
+ {
+ DEBUG_puts("1ippReadIO: unable to read name.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ buffer[n] = '\0';
+
+ if (ipp->current)
+ ipp->prev = ipp->current;
+
+ if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag,
+ 1)) == NULL)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ DEBUG_puts("1ippReadIO: unable to allocate attribute.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, "
+ "ipp->prev=%p", buffer, ipp->current, ipp->prev));
+
+ value = attr->values;
+ }
+ else
+ {
+ attr = NULL;
+ value = NULL;
+ }
+
+ if ((*cb)(src, buffer, 2) < 2)
+ {
+ DEBUG_puts("1ippReadIO: unable to read value length.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ n = (buffer[0] << 8) | buffer[1];
+ DEBUG_printf(("2ippReadIO: value length=%d", n));
+
+ if (n >= IPP_BUF_SIZE)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP value larger than 32767 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad value length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ switch (tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ if (n != 4)
+ {
+ if (tag == IPP_TAG_INTEGER)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP integer value not 4 bytes."), 1);
+ else
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP enum value not 4 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad integer value length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, buffer, 4) < 4)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read integer value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ n = (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
+ buffer[3];
+
+ if (attr->value_tag == IPP_TAG_RANGE)
+ value->range.lower = value->range.upper = n;
+ else
+ value->integer = n;
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (n != 1)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."),
+ 1);
+ DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, buffer, 1) < 1)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read boolean value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ value->boolean = buffer[0];
+ break;
+
+ case IPP_TAG_NOVALUE :
+ case IPP_TAG_NOTSETTABLE :
+ case IPP_TAG_DELETEATTR :
+ case IPP_TAG_ADMINDEFINE :
+ /*
+ * These value types are not supposed to have values, however
+ * some vendors (Brother) do not implement IPP correctly and so
+ * we need to map non-empty values to text...
+ */
+
+ if (attr->value_tag == tag)
+ {
+ if (n == 0)
+ break;
+
+ attr->value_tag = IPP_TAG_TEXT;
+ }
+
+ 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 :
+ if (n > 0)
+ {
+ if ((*cb)(src, buffer, n) < n)
+ {
+ DEBUG_puts("1ippReadIO: unable to read string value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ }
+
+ buffer[n] = '\0';
+ value->string.text = _cupsStrAlloc((char *)buffer);
+ DEBUG_printf(("2ippReadIO: value=\"%s\"", value->string.text));
+ break;
+
+ case IPP_TAG_DATE :
+ if (n != 11)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad date value length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, value->date, 11) < 11)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read date value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ if (n != 9)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP resolution value not 9 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, buffer, 9) < 9)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read resolution value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ value->resolution.xres =
+ (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
+ buffer[3];
+ value->resolution.yres =
+ (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
+ buffer[7];
+ value->resolution.units =
+ (ipp_res_t)buffer[8];
+ break;
+
+ case IPP_TAG_RANGE :
+ if (n != 8)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP rangeOfInteger value not 8 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length "
+ "%d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, buffer, 8) < 8)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read range value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ value->range.lower =
+ (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
+ buffer[3];
+ value->range.upper =
+ (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
+ buffer[7];
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ if (n < 4)
+ {
+ if (tag == IPP_TAG_TEXTLANG)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP textWithLanguage value less than "
+ "minimum 4 bytes."), 1);
+ else
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP nameWithLanguage value less than "
+ "minimum 4 bytes."), 1);
+ DEBUG_printf(("1ippReadIO: bad stringWithLanguage value "
+ "length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, buffer, n) < n)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read string w/language "
+ "value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+
+ /*
+ * text-with-language and name-with-language are composite
+ * values:
+ *
+ * language-length
+ * language
+ * text-length
+ * text
+ */
+
+ n = (bufptr[0] << 8) | bufptr[1];
+
+ if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE) ||
+ n >= sizeof(string))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP language length overflows value."), 1);
+ DEBUG_printf(("1ippReadIO: bad language value length %d.",
+ n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ else if (n >= IPP_MAX_LANGUAGE)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP language length too large."), 1);
+ DEBUG_printf(("1ippReadIO: bad language value length %d.",
+ n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ memcpy(string, bufptr + 2, n);
+ string[n] = '\0';
+
+ value->string.language = _cupsStrAlloc((char *)string);
+
+ bufptr += 2 + n;
+ n = (bufptr[0] << 8) | bufptr[1];
+
+ if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP string length overflows value."), 1);
+ DEBUG_printf(("1ippReadIO: bad string value length %d.", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr[2 + n] = '\0';
+ value->string.text = _cupsStrAlloc((char *)bufptr + 2);
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ /*
+ * Oh, boy, here comes a collection value, so read it...
+ */
+
+ value->collection = ippNew();
+
+ if (n > 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP begCollection value not 0 bytes."), 1);
+ DEBUG_puts("1ippReadIO: begCollection tag with value length "
+ "> 0.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read collection value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ break;
+
+ case IPP_TAG_END_COLLECTION :
+ _cupsBufferRelease((char *)buffer);
+
+ if (n > 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP endCollection value not 0 bytes."), 1);
+ DEBUG_puts("1ippReadIO: endCollection tag with value length "
+ "> 0.");
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_puts("1ippReadIO: endCollection tag...");
+ return (ipp->state = IPP_STATE_DATA);
+
+ case IPP_TAG_MEMBERNAME :
+ /*
+ * The value the name of the member in the collection, which
+ * we need to carry over...
+ */
+
+ if (!attr)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP memberName with no attribute."), 1);
+ DEBUG_puts("1ippReadIO: Member name without attribute.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ else if (n == 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP memberName value is empty."), 1);
+ DEBUG_puts("1ippReadIO: Empty member name value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ else if ((*cb)(src, buffer, n) < n)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read member name value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ buffer[n] = '\0';
+ attr->name = _cupsStrAlloc((char *)buffer);
+
+ /*
+ * Since collection members are encoded differently than
+ * regular attributes, make sure we don't start with an
+ * empty value...
+ */
+
+ attr->num_values --;
+
+ DEBUG_printf(("2ippReadIO: member name=\"%s\"", attr->name));
+ break;
+
+ default : /* Other unsupported values */
+ if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP octetString length too large."), 1);
+ DEBUG_printf(("1ippReadIO: bad octetString value length %d.",
+ n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ value->unknown.length = n;
+
+ if (n > 0)
+ {
+ if ((value->unknown.data = malloc(n)) == NULL)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ DEBUG_puts("1ippReadIO: Unable to allocate value");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((*cb)(src, value->unknown.data, n) < n)
+ {
+ DEBUG_puts("1ippReadIO: Unable to read unsupported value.");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ }
+ else
+ value->unknown.data = NULL;
+ break;
+ }
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!blocking)
+ break;
+ }
+ break;
+
+ case IPP_STATE_DATA :
+ break;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+
+ DEBUG_printf(("1ippReadIO: returning ipp->state=%d.", ipp->state));
+ _cupsBufferRelease((char *)buffer);
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'ippSetBoolean()' - Set a boolean 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetBoolean(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ int boolvalue)/* I - Boolean value */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN ||
+ element < 0 || element > (*attr)->num_values)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ value->boolean = boolvalue;
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetCollection()' - Set a collection 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetCollection(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ ipp_t *colvalue) /* I - Collection value */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION ||
+ element < 0 || element > (*attr)->num_values || !colvalue)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ {
+ if (value->collection)
+ ippDelete(value->collection);
+
+ value->collection = colvalue;
+ colvalue->use ++;
+ }
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetDate()' - Set a date 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+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 */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_DATE ||
+ element < 0 || element > (*attr)->num_values || !datevalue)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ memcpy(value->date, datevalue, sizeof(value->date));
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetGroupTag()' - Set the group tag of an attribute.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code attr@ parameter may be modified as a result of setting the value.
+ *
+ * The @code group@ parameter specifies the IPP attribute group tag: none
+ * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
+ * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
+ * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
+ * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetGroupTag(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - Attribute */
+ ipp_tag_t group_tag) /* I - Group tag */
+{
+ /*
+ * Range check input - group tag must be 0x01 to 0x0F, per RFC 2911...
+ */
+
+ if (!ipp || !attr || !*attr ||
+ group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END ||
+ group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
+ return (0);
+
+ /*
+ * Set the group tag and return...
+ */
+
+ (*attr)->group_tag = group_tag;
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetInteger()' - Set an integer or enum 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetInteger(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ int intvalue) /* I - Integer/enum value */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr ||
+ ((*attr)->value_tag != IPP_TAG_INTEGER && (*attr)->value_tag != IPP_TAG_ENUM) ||
+ element < 0 || element > (*attr)->num_values)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ value->integer = intvalue;
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetName()' - Set the name of an attribute.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code attr@ parameter may be modified as a result of setting the value.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetName(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ const char *name) /* I - Attribute name */
+{
+ char *temp; /* Temporary name value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((temp = _cupsStrAlloc(name)) != NULL)
+ {
+ if ((*attr)->name)
+ _cupsStrFree((*attr)->name);
+
+ (*attr)->name = temp;
+ }
+
+ return (temp != NULL);
+}
+
+
+/*
+ * 'ippSetOctetString()' - Set an octetString value in an IPP attribute.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetOctetString(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ const void *data, /* I - Pointer to octetString data */
+ int datalen) /* I - Length of octetString data */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_STRING ||
+ element < 0 || element > (*attr)->num_values ||
+ datalen < 0 || datalen > IPP_MAX_LENGTH)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ {
+ if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
+ {
+ /*
+ * Just copy the pointer...
+ */
+
+ value->unknown.data = (void *)data;
+ value->unknown.length = datalen;
+ }
+ else
+ {
+ /*
+ * Copy the data...
+ */
+
+ if (value->unknown.data)
+ {
+ /*
+ * Free previous data...
+ */
+
+ free(value->unknown.data);
+
+ value->unknown.data = NULL;
+ value->unknown.length = 0;
+ }
+
+ if (datalen > 0)
+ {
+ void *temp; /* Temporary data pointer */
+
+ if ((temp = malloc(datalen)) != NULL)
+ {
+ memcpy(temp, data, datalen);
+
+ value->unknown.data = temp;
+ value->unknown.length = datalen;
+ }
+ else
+ return (0);
+ }
+ }
+ }
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetOperation()' - Set the operation ID in an IPP request message.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetOperation(ipp_t *ipp, /* I - IPP request message */
+ ipp_op_t op) /* I - Operation ID */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (0);
+
+ /*
+ * Set the operation and return...
+ */
+
+ ipp->request.op.operation_id = op;
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetRange()' - Set a rangeOfInteger 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetRange(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ int lowervalue, /* I - Lower bound for range */
+ int uppervalue) /* I - Upper bound for range */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RANGE ||
+ element < 0 || element > (*attr)->num_values || lowervalue > uppervalue)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ {
+ value->range.lower = lowervalue;
+ value->range.upper = uppervalue;
+ }
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetRequestId()' - Set the request ID in an IPP message.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code request_id@ parameter must be greater than 0.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetRequestId(ipp_t *ipp, /* I - IPP message */
+ int request_id) /* I - Request ID */
+{
+ /*
+ * Range check input; not checking request_id values since ipptool wants to send
+ * invalid values for conformance testing and a bad request_id does not affect the
+ * encoding of a message...
+ */
+
+ if (!ipp)
+ return (0);
+
+ /*
+ * Set the request ID and return...
+ */
+
+ ipp->request.any.request_id = request_id;
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetResolution()' - Set a resolution 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetResolution(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ ipp_res_t unitsvalue, /* I - Resolution units */
+ int xresvalue, /* I - Horizontal/cross feed resolution */
+ int yresvalue) /* I - Vertical/feed resolution */
+{
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RESOLUTION ||
+ element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 ||
+ unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ {
+ value->resolution.units = unitsvalue;
+ value->resolution.xres = xresvalue;
+ value->resolution.yres = yresvalue;
+ }
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetState()' - Set the current state of the IPP message.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetState(ipp_t *ipp, /* I - IPP message */
+ ipp_state_t state) /* I - IPP state value */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (0);
+
+ /*
+ * Set the state and return...
+ */
+
+ ipp->state = state;
+ ipp->current = NULL;
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetStatusCode()' - Set the status code in an IPP response or event message.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetStatusCode(ipp_t *ipp, /* I - IPP response or event message */
+ ipp_status_t status) /* I - Status code */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp)
+ return (0);
+
+ /*
+ * Set the status code and return...
+ */
+
+ ipp->request.status.status_code = status;
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetString()' - Set a string 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.
+ *
+ * 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)@.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetString(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ const char *strvalue) /* I - String value */
+{
+ char *temp; /* Temporary string */
+ _ipp_value_t *value; /* Current value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr ||
+ ((*attr)->value_tag != IPP_TAG_TEXTLANG &&
+ (*attr)->value_tag != IPP_TAG_NAMELANG &&
+ ((*attr)->value_tag < IPP_TAG_TEXT ||
+ (*attr)->value_tag > IPP_TAG_MIMETYPE)) ||
+ element < 0 || element > (*attr)->num_values || !strvalue)
+ return (0);
+
+ /*
+ * Set the value and return...
+ */
+
+ if ((value = ipp_set_value(ipp, attr, element)) != NULL)
+ {
+ if (element > 0)
+ value->string.language = (*attr)->values[0].string.language;
+
+ if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
+ value->string.text = (char *)strvalue;
+ else if ((temp = _cupsStrAlloc(strvalue)) != NULL)
+ {
+ if (value->string.text)
+ _cupsStrFree(value->string.text);
+
+ value->string.text = temp;
+ }
+ else
+ return (0);
+ }
+
+ return (value != NULL);
+}
+
+
+/*
+ * 'ippSetStringf()' - Set a formatted string value of an attribute.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * 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)@.
+ *
+ * The @code format@ parameter uses formatting characters compatible with the
+ * printf family of standard functions. Additional arguments follow it as
+ * needed. The formatted string is truncated as needed to the maximum length of
+ * the corresponding value type.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetStringf(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ int ret; /* Return value */
+ va_list ap; /* Pointer to additional arguments */
+
+
+ va_start(ap, format);
+ ret = ippSetStringfv(ipp, attr, element, format, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+
+/*
+ * 'ippSetStringf()' - Set a formatted string value of an attribute.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * 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)@.
+ *
+ * The @code format@ parameter uses formatting characters compatible with the
+ * printf family of standard functions. Additional arguments follow it as
+ * needed. The formatted string is truncated as needed to the maximum length of
+ * the corresponding value type.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetStringfv(ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element, /* I - Value number (0-based) */
+ const char *format, /* I - Printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ ipp_tag_t value_tag; /* Value tag */
+ char buffer[IPP_MAX_TEXT + 4];
+ /* Formatted text string */
+ ssize_t bytes, /* Length of formatted value */
+ max_bytes; /* Maximum number of bytes for value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (attr && *attr)
+ value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
+ else
+ value_tag = IPP_TAG_ZERO;
+
+ if (!ipp || !attr || !*attr ||
+ (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
+ value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
+ !format)
+ return (0);
+
+ /*
+ * Format the string...
+ */
+
+ if (!strcmp(format, "%s"))
+ {
+ /*
+ * Optimize the simple case...
+ */
+
+ const char *s = va_arg(ap, char *);
+
+ if (!s)
+ s = "(null)";
+
+ bytes = strlen(s);
+ strlcpy(buffer, s, sizeof(buffer));
+ }
+ else
+ {
+ /*
+ * Do a full formatting of the message...
+ */
+
+ if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
+ return (0);
+ }
+
+ /*
+ * Limit the length of the string...
+ */
+
+ switch (value_tag)
+ {
+ default :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_TEXTLANG :
+ max_bytes = IPP_MAX_TEXT;
+ break;
+
+ case IPP_TAG_NAME :
+ case IPP_TAG_NAMELANG :
+ max_bytes = IPP_MAX_NAME;
+ break;
+
+ case IPP_TAG_CHARSET :
+ max_bytes = IPP_MAX_CHARSET;
+ break;
+
+ case IPP_TAG_KEYWORD :
+ max_bytes = IPP_MAX_KEYWORD;
+ break;
+
+ case IPP_TAG_LANGUAGE :
+ max_bytes = IPP_MAX_LANGUAGE;
+ break;
+
+ case IPP_TAG_MIMETYPE :
+ max_bytes = IPP_MAX_MIMETYPE;
+ break;
+
+ case IPP_TAG_URI :
+ max_bytes = IPP_MAX_URI;
+ break;
+
+ case IPP_TAG_URISCHEME :
+ max_bytes = IPP_MAX_URISCHEME;
+ break;
+ }
+
+ if (bytes >= max_bytes)
+ {
+ char *bufmax, /* Buffer at max_bytes */
+ *bufptr; /* Pointer into buffer */
+
+ bufptr = buffer + strlen(buffer) - 1;
+ bufmax = buffer + max_bytes - 1;
+
+ while (bufptr > bufmax)
+ {
+ if (*bufptr & 0x80)
+ {
+ while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
+ bufptr --;
+ }
+
+ bufptr --;
+ }
+
+ *bufptr = '\0';
+ }
+
+ /*
+ * Set the formatted string and return...
+ */
+
+ return (ippSetString(ipp, attr, element, buffer));
+}
+
+
+/*
+ * 'ippSetValueTag()' - Set the value tag of an attribute.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The @code attr@ parameter may be modified as a result of setting the value.
+ *
+ * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger
+ * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name
+ * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text
+ * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage
+ * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various
+ * out-of-band value tags such as no-value (@code IPP_TAG_NOVALUE@). All other changes
+ * will be rejected.
+ *
+ * Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language
+ * code in the "attributes-natural-language" attribute or, if not present, the language
+ * code for the current locale.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetValueTag(
+ ipp_t *ipp, /* I - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ ipp_tag_t value_tag) /* I - Value tag */
+{
+ int i; /* Looping var */
+ _ipp_value_t *value; /* Current value */
+ int integer; /* Current integer value */
+ cups_lang_t *language; /* Current language */
+ char code[32]; /* Language code */
+ ipp_tag_t temp_tag; /* Temporary value tag */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || !attr || !*attr)
+ return (0);
+
+ /*
+ * If there is no change, return immediately...
+ */
+
+ if (value_tag == (*attr)->value_tag)
+ return (1);
+
+ /*
+ * Otherwise implement changes as needed...
+ */
+
+ temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK);
+
+ switch (value_tag)
+ {
+ 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 :
+ /*
+ * Free any existing values...
+ */
+
+ if ((*attr)->num_values > 0)
+ ipp_free_values(*attr, 0, (*attr)->num_values);
+
+ /*
+ * Set out-of-band value...
+ */
+
+ (*attr)->value_tag = value_tag;
+ break;
+
+ case IPP_TAG_RANGE :
+ if (temp_tag != IPP_TAG_INTEGER)
+ return (0);
+
+ for (i = (*attr)->num_values, value = (*attr)->values;
+ i > 0;
+ i --, value ++)
+ {
+ integer = value->integer;
+ value->range.lower = value->range.upper = integer;
+ }
+
+ (*attr)->value_tag = IPP_TAG_RANGE;
+ break;
+
+ case IPP_TAG_NAME :
+ if (temp_tag != IPP_TAG_KEYWORD && temp_tag != IPP_TAG_URI &&
+ temp_tag != IPP_TAG_URISCHEME && temp_tag != IPP_TAG_LANGUAGE &&
+ temp_tag != IPP_TAG_MIMETYPE)
+ return (0);
+
+ (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST));
+ break;
+
+ case IPP_TAG_NAMELANG :
+ case IPP_TAG_TEXTLANG :
+ if (value_tag == IPP_TAG_NAMELANG &&
+ (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD &&
+ temp_tag != IPP_TAG_URI && temp_tag != IPP_TAG_URISCHEME &&
+ temp_tag != IPP_TAG_LANGUAGE && temp_tag != IPP_TAG_MIMETYPE))
+ return (0);
+
+ if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
+ return (0);
+
+ if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
+ !strcmp(ipp->attrs->next->name, "attributes-natural-language"))
+ {
+ /*
+ * Use the language code from the IPP message...
+ */
+
+ (*attr)->values[0].string.language =
+ _cupsStrAlloc(ipp->attrs->next->values[0].string.text);
+ }
+ else
+ {
+ /*
+ * Otherwise, use the language code corresponding to the locale...
+ */
+
+ language = cupsLangDefault();
+ (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
+ code,
+ sizeof(code)));
+ }
+
+ for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
+ i > 0;
+ i --, value ++)
+ value->string.language = (*attr)->values[0].string.language;
+
+ if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST)
+ {
+ /*
+ * Make copies of all values...
+ */
+
+ for (i = (*attr)->num_values, value = (*attr)->values;
+ i > 0;
+ i --, value ++)
+ value->string.text = _cupsStrAlloc(value->string.text);
+ }
+
+ (*attr)->value_tag = IPP_TAG_NAMELANG;
+ break;
+
+ case IPP_TAG_KEYWORD :
+ if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
+ break; /* Silently "allow" name -> keyword */
+
+ default :
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetVersion()' - Set the version number in an IPP message.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
+ *
+ * @since CUPS 1.6/OS X 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetVersion(ipp_t *ipp, /* I - IPP message */
+ int major, /* I - Major version number (major.minor) */
+ int minor) /* I - Minor version number (major.minor) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || major < 0 || minor < 0)
+ return (0);
+
+ /*
+ * Set the version number...
+ */
+
+ ipp->request.any.version[0] = major;
+ ipp->request.any.version[1] = minor;
+
+ return (1);
+}
+
+
+/*
+ * 'ippTimeToDate()' - Convert from UNIX time to RFC 1903 format.
+ */
+
+const ipp_uchar_t * /* O - RFC-1903 date/time data */
+ippTimeToDate(time_t t) /* I - UNIX time value */
+{
+ struct tm *unixdate; /* UNIX unixdate/time info */
+ ipp_uchar_t *date = _cupsGlobals()->ipp_date;
+ /* RFC-1903 date/time data */
+
+
+ /*
+ * RFC-1903 date/time format is:
+ *
+ * Byte(s) Description
+ * ------- -----------
+ * 0-1 Year (0 to 65535)
+ * 2 Month (1 to 12)
+ * 3 Day (1 to 31)
+ * 4 Hours (0 to 23)
+ * 5 Minutes (0 to 59)
+ * 6 Seconds (0 to 60, 60 = "leap second")
+ * 7 Deciseconds (0 to 9)
+ * 8 +/- UTC
+ * 9 UTC hours (0 to 11)
+ * 10 UTC minutes (0 to 59)
+ */
+
+ unixdate = gmtime(&t);
+ unixdate->tm_year += 1900;
+
+ date[0] = unixdate->tm_year >> 8;
+ date[1] = unixdate->tm_year;
+ date[2] = unixdate->tm_mon + 1;
+ date[3] = unixdate->tm_mday;
+ date[4] = unixdate->tm_hour;
+ date[5] = unixdate->tm_min;
+ date[6] = unixdate->tm_sec;
+ date[7] = 0;
+ date[8] = '+';
+ date[9] = 0;
+ date[10] = 0;
+
+ return (date);
+}
+
+
+/*
+ * 'ippValidateAttribute()' - Validate the contents of an attribute.
+ *
+ * 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.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 if valid, 0 otherwise */
+ippValidateAttribute(
+ ipp_attribute_t *attr) /* I - Attribute */
+{
+ int i; /* Looping var */
+ char scheme[64], /* Scheme from URI */
+ userpass[256], /* Username/password from URI */
+ hostname[256], /* Hostname from URI */
+ resource[1024]; /* Resource from URI */
+ int port, /* Port number from URI */
+ uri_status; /* URI separation status */
+ const char *ptr; /* Pointer into string */
+ ipp_attribute_t *colattr; /* Collection attribute */
+#ifndef WIN32
+ regex_t re; /* Regular expression */
+#endif /* WIN32 */
+ ipp_uchar_t *date; /* Current date value */
+ static const char * const uri_status_strings[] =
+ { /* URI status strings */
+ "URI too large",
+ "Bad arguments to function",
+ "Bad resource in URI",
+ "Bad port number in URI",
+ "Bad hostname/address in URI",
+ "Bad username in URI",
+ "Bad scheme in URI",
+ "Bad/empty URI",
+ "OK",
+ "Missing scheme in URI",
+ "Unknown scheme in URI",
+ "Missing resource in URI"
+ };
+
+
+ /*
+ * Skip separators.
+ */
+
+ if (!attr->name)
+ return (1);
+
+ /*
+ * Validate the attribute name.
+ */
+
+ for (ptr = attr->name; *ptr; ptr ++)
+ if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
+ break;
+
+ if (*ptr || ptr == attr->name)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad attribute name - invalid character "
+ "(RFC 2911 section 4.1.3)."), attr->name);
+ return (0);
+ }
+
+ if ((ptr - attr->name) > 255)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad attribute name - bad length %d "
+ "(RFC 2911 section 4.1.3)."), attr->name,
+ (int)(ptr - attr->name));
+ return (0);
+ }
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].boolean != 0 &&
+ attr->values[i].boolean != 1)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad boolen value %d "
+ "(RFC 2911 section 4.1.11)."), attr->name,
+ attr->values[i].boolean);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_ENUM :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].integer < 1)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad enum value %d - out of range "
+ "(RFC 2911 section 4.1.4)."), attr->name,
+ attr->values[i].integer);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_STRING :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad octetString value - bad length %d "
+ "(RFC 2911 section 4.1.10)."), attr->name,
+ attr->values[i].unknown.length);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_DATE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ date = attr->values[i].date;
+
+ if (date[2] < 1 || date[2] > 12)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime month %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[2]);
+ return (0);
+ }
+
+ if (date[3] < 1 || date[3] > 31)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime day %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[3]);
+ return (0);
+ }
+
+ if (date[4] > 23)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime hours %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[4]);
+ return (0);
+ }
+
+ if (date[5] > 59)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime minutes %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[5]);
+ return (0);
+ }
+
+ if (date[6] > 60)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime seconds %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[6]);
+ return (0);
+ }
+
+ if (date[7] > 9)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime deciseconds %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[7]);
+ return (0);
+ }
+
+ if (date[8] != '-' && date[8] != '+')
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime UTC sign '%c' "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[8]);
+ return (0);
+ }
+
+ if (date[9] > 11)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime UTC hours %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[9]);
+ return (0);
+ }
+
+ if (date[10] > 59)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad dateTime UTC minutes %u "
+ "(RFC 2911 section 4.1.14)."), attr->name, date[10]);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].resolution.xres <= 0)
+ {
+ 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,
+ attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units ==
+ IPP_RES_PER_INCH ? "dpi" :
+ attr->values[i].resolution.units ==
+ IPP_RES_PER_CM ? "dpcm" : "unknown");
+ return (0);
+ }
+
+ if (attr->values[i].resolution.yres <= 0)
+ {
+ 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,
+ attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units ==
+ IPP_RES_PER_INCH ? "dpi" :
+ attr->values[i].resolution.units ==
+ IPP_RES_PER_CM ? "dpcm" : "unknown");
+ return (0);
+ }
+
+ if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
+ attr->values[i].resolution.units != IPP_RES_PER_CM)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad resolution value %dx%d%s - bad "
+ "units value (RFC 2911 section 4.1.15)."),
+ attr->name, attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units ==
+ IPP_RES_PER_INCH ? "dpi" :
+ attr->values[i].resolution.units ==
+ IPP_RES_PER_CM ? "dpcm" : "unknown");
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].range.lower > attr->values[i].range.upper)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad rangeOfInteger value %d-%d - lower "
+ "greater than upper (RFC 2911 section 4.1.13)."),
+ attr->name, attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (colattr = attr->values[i].collection->attrs;
+ colattr;
+ colattr = colattr->next)
+ {
+ if (!ippValidateAttribute(colattr))
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_TEXTLANG :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ {
+ if ((*ptr & 0xe0) == 0xc0)
+ {
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ }
+ else if ((*ptr & 0xf0) == 0xe0)
+ {
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ }
+ else if ((*ptr & 0xf8) == 0xf0)
+ {
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ }
+ else if (*ptr & 0x80)
+ break;
+ }
+
+ if (*ptr)
+ {
+ 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,
+ attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad text value \"%s\" - bad length %d "
+ "(RFC 2911 section 4.1.1)."), attr->name,
+ attr->values[i].string.text,
+ (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_NAME :
+ case IPP_TAG_NAMELANG :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ {
+ if ((*ptr & 0xe0) == 0xc0)
+ {
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ }
+ else if ((*ptr & 0xf0) == 0xe0)
+ {
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ }
+ else if ((*ptr & 0xf8) == 0xf0)
+ {
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ ptr ++;
+ if ((*ptr & 0xc0) != 0x80)
+ break;
+ }
+ else if (*ptr & 0x80)
+ break;
+ }
+
+ if (*ptr)
+ {
+ 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,
+ attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad name value \"%s\" - bad length %d "
+ "(RFC 2911 section 4.1.2)."), attr->name,
+ attr->values[i].string.text,
+ (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_KEYWORD :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
+ *ptr != '_')
+ break;
+
+ if (*ptr || ptr == attr->values[i].string.text)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad keyword value \"%s\" - invalid "
+ "character (RFC 2911 section 4.1.3)."),
+ attr->name, attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad keyword value \"%s\" - bad "
+ "length %d (RFC 2911 section 4.1.3)."),
+ attr->name, attr->values[i].string.text,
+ (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_URI :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
+ attr->values[i].string.text,
+ scheme, sizeof(scheme),
+ userpass, sizeof(userpass),
+ hostname, sizeof(hostname),
+ &port, resource, sizeof(resource));
+
+ if (uri_status < HTTP_URI_STATUS_OK)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad URI value \"%s\" - %s "
+ "(RFC 2911 section 4.1.5)."), attr->name,
+ attr->values[i].string.text,
+ uri_status_strings[uri_status -
+ HTTP_URI_STATUS_OVERFLOW]);
+ return (0);
+ }
+
+ if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad URI value \"%s\" - bad length %d "
+ "(RFC 2911 section 4.1.5)."), attr->name,
+ attr->values[i].string.text,
+ (int)strlen(attr->values[i].string.text));
+ }
+ }
+ break;
+
+ case IPP_TAG_URISCHEME :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ ptr = attr->values[i].string.text;
+ if (islower(*ptr & 255))
+ {
+ for (ptr ++; *ptr; ptr ++)
+ if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
+ *ptr != '+' && *ptr != '-' && *ptr != '.')
+ break;
+ }
+
+ if (*ptr || ptr == attr->values[i].string.text)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad uriScheme value \"%s\" - bad "
+ "characters (RFC 2911 section 4.1.6)."),
+ attr->name, attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad uriScheme value \"%s\" - bad "
+ "length %d (RFC 2911 section 4.1.6)."),
+ attr->name, attr->values[i].string.text,
+ (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_CHARSET :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
+ isspace(*ptr & 255))
+ break;
+
+ if (*ptr || ptr == attr->values[i].string.text)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad charset value \"%s\" - bad "
+ "characters (RFC 2911 section 4.1.7)."),
+ attr->name, attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad charset value \"%s\" - bad "
+ "length %d (RFC 2911 section 4.1.7)."),
+ attr->name, attr->values[i].string.text,
+ (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_LANGUAGE :
+ /*
+ * The following regular expression is derived from the ABNF for
+ * language tags in RFC 4646. All I can say is that this is the
+ * easiest way to check the values...
+ */
+#ifndef WIN32
+ if ((i = regcomp(&re,
+ "^("
+ "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
+ /* language */
+ "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
+ "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
+ "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
+ "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
+ "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
+ "|"
+ "x(-[a-z0-9]{1,8})+" /* privateuse */
+ "|"
+ "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
+ ")$",
+ REG_NOSUB | REG_EXTENDED)) != 0)
+ {
+ char temp[256]; /* Temporary error string */
+
+ regerror(i, &re, temp, sizeof(temp));
+ ipp_set_error(IPP_STATUS_ERROR_INTERNAL,
+ _("Unable to compile naturalLanguage regular "
+ "expression: %s."), temp);
+ return (0);
+ }
+#endif /* WIN32 */
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+#ifndef WIN32
+ if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
+ "characters (RFC 2911 section 4.1.8)."),
+ attr->name, attr->values[i].string.text);
+ regfree(&re);
+ return (0);
+ }
+#endif /* WIN32 */
+
+ if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
+ "length %d (RFC 2911 section 4.1.8)."),
+ attr->name, attr->values[i].string.text,
+ (int)strlen(attr->values[i].string.text));
+#ifndef WIN32
+ regfree(&re);
+#endif /* WIN32 */
+ return (0);
+ }
+ }
+
+#ifndef WIN32
+ regfree(&re);
+#endif /* WIN32 */
+ break;
+
+ case IPP_TAG_MIMETYPE :
+ /*
+ * The following regular expression is derived from the ABNF for
+ * MIME media types in RFC 2045 and 4288. All I can say is that this is
+ * the easiest way to check the values...
+ */
+
+#ifndef WIN32
+ if ((i = regcomp(&re,
+ "^"
+ "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
+ "/"
+ "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
+ "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
+ "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
+ /* value */
+ "$",
+ REG_NOSUB | REG_EXTENDED)) != 0)
+ {
+ char temp[256]; /* Temporary error string */
+
+ regerror(i, &re, temp, sizeof(temp));
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("Unable to compile mimeMediaType regular "
+ "expression: %s."), temp);
+ return (0);
+ }
+#endif /* WIN32 */
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+#ifndef WIN32
+ if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
+ "characters (RFC 2911 section 4.1.9)."),
+ attr->name, attr->values[i].string.text);
+ regfree(&re);
+ return (0);
+ }
+#endif /* WIN32 */
+
+ if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
+ _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
+ "length %d (RFC 2911 section 4.1.9)."),
+ attr->name, attr->values[i].string.text,
+ (int)strlen(attr->values[i].string.text));
+#ifndef WIN32
+ regfree(&re);
+#endif /* WIN32 */
+ return (0);
+ }
+ }
+
+#ifndef WIN32
+ regfree(&re);
+#endif /* WIN32 */
+ break;
+
+ default :
+ break;
+ }
+
+ return (1);
+}
+
+
+/*
+ * '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.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 if valid, 0 otherwise */
+ippValidateAttributes(ipp_t *ipp) /* I - IPP message */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+
+
+ if (!ipp)
+ return (1);
+
+ for (attr = ipp->attrs; attr; attr = attr->next)
+ if (!ippValidateAttribute(attr))
+ return (0);
+
+ return (1);
+}
+
+
+/*
+ * 'ippWrite()' - Write data for an IPP message to a HTTP connection.
+ */
+
+ipp_state_t /* O - Current state */
+ippWrite(http_t *http, /* I - HTTP connection */
+ ipp_t *ipp) /* I - IPP data */
+{
+ DEBUG_printf(("ippWrite(http=%p, ipp=%p)", http, ipp));
+
+ if (!http)
+ return (IPP_STATE_ERROR);
+
+ return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp));
+}
+
+
+/*
+ * 'ippWriteFile()' - Write data for an IPP message to a file.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ipp_state_t /* O - Current state */
+ippWriteFile(int fd, /* I - HTTP data */
+ ipp_t *ipp) /* I - IPP data */
+{
+ DEBUG_printf(("ippWriteFile(fd=%d, ipp=%p)", fd, ipp));
+
+ ipp->state = IPP_STATE_IDLE;
+
+ return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp));
+}
+
+
+/*
+ * 'ippWriteIO()' - Write data for an IPP message.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ipp_state_t /* O - Current state */
+ippWriteIO(void *dst, /* I - Destination */
+ ipp_iocb_t cb, /* I - Write callback function */
+ int blocking, /* I - Use blocking IO? */
+ ipp_t *parent, /* I - Parent IPP message */
+ ipp_t *ipp) /* I - IPP data */
+{
+ int i; /* Looping var */
+ int n; /* Length of data */
+ unsigned char *buffer, /* Data buffer */
+ *bufptr; /* Pointer into buffer */
+ ipp_attribute_t *attr; /* Current attribute */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)",
+ dst, cb, blocking, parent, ipp));
+
+ if (!dst || !ipp)
+ return (IPP_STATE_ERROR);
+
+ if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
+ {
+ DEBUG_puts("1ippWriteIO: Unable to get write buffer");
+ return (IPP_STATE_ERROR);
+ }
+
+ switch (ipp->state)
+ {
+ case IPP_STATE_IDLE :
+ ipp->state ++; /* Avoid common problem... */
+
+ case IPP_STATE_HEADER :
+ if (parent == NULL)
+ {
+ /*
+ * Send the request header:
+ *
+ * Version = 2 bytes
+ * Operation/Status Code = 2 bytes
+ * Request ID = 4 bytes
+ * Total = 8 bytes
+ */
+
+ bufptr = buffer;
+
+ *bufptr++ = ipp->request.any.version[0];
+ *bufptr++ = ipp->request.any.version[1];
+ *bufptr++ = ipp->request.any.op_status >> 8;
+ *bufptr++ = ipp->request.any.op_status;
+ *bufptr++ = ipp->request.any.request_id >> 24;
+ *bufptr++ = ipp->request.any.request_id >> 16;
+ *bufptr++ = ipp->request.any.request_id >> 8;
+ *bufptr++ = ipp->request.any.request_id;
+
+ DEBUG_printf(("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]));
+ DEBUG_printf(("2ippWriteIO: op_status=%04x",
+ ipp->request.any.op_status));
+ DEBUG_printf(("2ippWriteIO: request_id=%d",
+ ipp->request.any.request_id));
+
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP header...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ }
+
+ /*
+ * Reset the state engine to point to the first attribute
+ * in the request/response, with no current group.
+ */
+
+ ipp->state = IPP_STATE_ATTRIBUTE;
+ ipp->current = ipp->attrs;
+ ipp->curtag = IPP_TAG_ZERO;
+
+ DEBUG_printf(("1ippWriteIO: ipp->current=%p", ipp->current));
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!blocking)
+ break;
+
+ case IPP_STATE_ATTRIBUTE :
+ while (ipp->current != NULL)
+ {
+ /*
+ * Write this attribute...
+ */
+
+ bufptr = buffer;
+ attr = ipp->current;
+
+ ipp->current = ipp->current->next;
+
+ if (!parent)
+ {
+ if (ipp->curtag != attr->group_tag)
+ {
+ /*
+ * Send a group tag byte...
+ */
+
+ ipp->curtag = attr->group_tag;
+
+ if (attr->group_tag == IPP_TAG_ZERO)
+ continue;
+
+ DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)",
+ attr->group_tag, ippTagString(attr->group_tag)));
+ *bufptr++ = attr->group_tag;
+ }
+ else if (attr->group_tag == IPP_TAG_ZERO)
+ continue;
+ }
+
+ DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name,
+ attr->num_values > 1 ? "1setOf " : "",
+ ippTagString(attr->value_tag)));
+
+ /*
+ * Write the attribute tag and name.
+ *
+ * The attribute name length does not include the trailing nul
+ * character in the source string.
+ *
+ * Collection values (parent != NULL) are written differently...
+ */
+
+ if (parent == NULL)
+ {
+ /*
+ * Get the length of the attribute name, and make sure it won't
+ * overflow the buffer...
+ */
+
+ if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8))
+ {
+ DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ /*
+ * Write the value tag, name length, and name string...
+ */
+
+ DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
+ attr->value_tag, ippTagString(attr->value_tag)));
+ DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
+ attr->name));
+
+ if (attr->value_tag > 0xff)
+ {
+ *bufptr++ = IPP_TAG_EXTENSION;
+ *bufptr++ = attr->value_tag >> 24;
+ *bufptr++ = attr->value_tag >> 16;
+ *bufptr++ = attr->value_tag >> 8;
+ *bufptr++ = attr->value_tag;
+ }
+ else
+ *bufptr++ = attr->value_tag;
+
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+ memcpy(bufptr, attr->name, n);
+ bufptr += n;
+ }
+ else
+ {
+ /*
+ * Get the length of the attribute name, and make sure it won't
+ * overflow the buffer...
+ */
+
+ if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12))
+ {
+ DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ /*
+ * Write the member name tag, name length, name string, value tag,
+ * and empty name for the collection member attribute...
+ */
+
+ DEBUG_printf(("2ippWriteIO: writing value tag=%x(memberName)",
+ IPP_TAG_MEMBERNAME));
+ DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
+ attr->name));
+ DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
+ attr->value_tag, ippTagString(attr->value_tag)));
+ DEBUG_puts("2ippWriteIO: writing name=0,\"\"");
+
+ *bufptr++ = IPP_TAG_MEMBERNAME;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+ memcpy(bufptr, attr->name, n);
+ bufptr += n;
+
+ if (attr->value_tag > 0xff)
+ {
+ *bufptr++ = IPP_TAG_EXTENSION;
+ *bufptr++ = attr->value_tag >> 24;
+ *bufptr++ = attr->value_tag >> 16;
+ *bufptr++ = attr->value_tag >> 8;
+ *bufptr++ = attr->value_tag;
+ }
+ else
+ *bufptr++ = attr->value_tag;
+
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Now write the attribute value(s)...
+ */
+
+ switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
+ {
+ 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 :
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ break;
+
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Integers and enumerations are both 4-byte signed
+ * (twos-complement) values.
+ *
+ * Put the 2-byte length and 4-byte value into the buffer...
+ */
+
+ *bufptr++ = 0;
+ *bufptr++ = 4;
+ *bufptr++ = value->integer >> 24;
+ *bufptr++ = value->integer >> 16;
+ *bufptr++ = value->integer >> 8;
+ *bufptr++ = value->integer;
+ }
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Boolean values are 1-byte; 0 = false, 1 = true.
+ *
+ * Put the 2-byte length and 1-byte value into the buffer...
+ */
+
+ *bufptr++ = 0;
+ *bufptr++ = 1;
+ *bufptr++ = value->boolean;
+ }
+ break;
+
+ 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, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
+ attr->value_tag,
+ ippTagString(attr->value_tag)));
+ DEBUG_printf(("2ippWriteIO: writing name=0,\"\""));
+
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ if (value->string.text != NULL)
+ n = (int)strlen(value->string.text);
+ else
+ n = 0;
+
+ if (n > (IPP_BUF_SIZE - 2))
+ {
+ DEBUG_printf(("1ippWriteIO: String too long (%d)", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_printf(("2ippWriteIO: writing string=%d,\"%s\"", n,
+ value->string.text));
+
+ if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /*
+ * All simple strings consist of the 2-byte length and
+ * character data without the trailing nul normally found
+ * in C strings. Also, strings cannot be longer than IPP_MAX_LENGTH
+ * bytes since the 2-byte length is a signed (twos-complement)
+ * value.
+ *
+ * Put the 2-byte length and string characters in the buffer.
+ */
+
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ if (n > 0)
+ {
+ memcpy(bufptr, value->string.text, n);
+ bufptr += n;
+ }
+ }
+ break;
+
+ case IPP_TAG_DATE :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Date values consist of a 2-byte length and an
+ * 11-byte date/time structure defined by RFC 1903.
+ *
+ * Put the 2-byte length and 11-byte date/time
+ * structure in the buffer.
+ */
+
+ *bufptr++ = 0;
+ *bufptr++ = 11;
+ memcpy(bufptr, value->date, 11);
+ bufptr += 11;
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Resolution values consist of a 2-byte length,
+ * 4-byte horizontal resolution value, 4-byte vertical
+ * resolution value, and a 1-byte units value.
+ *
+ * Put the 2-byte length and resolution value data
+ * into the buffer.
+ */
+
+ *bufptr++ = 0;
+ *bufptr++ = 9;
+ *bufptr++ = value->resolution.xres >> 24;
+ *bufptr++ = value->resolution.xres >> 16;
+ *bufptr++ = value->resolution.xres >> 8;
+ *bufptr++ = value->resolution.xres;
+ *bufptr++ = value->resolution.yres >> 24;
+ *bufptr++ = value->resolution.yres >> 16;
+ *bufptr++ = value->resolution.yres >> 8;
+ *bufptr++ = value->resolution.yres;
+ *bufptr++ = value->resolution.units;
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Range values consist of a 2-byte length,
+ * 4-byte lower value, and 4-byte upper value.
+ *
+ * Put the 2-byte length and range value data
+ * into the buffer.
+ */
+
+ *bufptr++ = 0;
+ *bufptr++ = 8;
+ *bufptr++ = value->range.lower >> 24;
+ *bufptr++ = value->range.lower >> 16;
+ *bufptr++ = value->range.lower >> 8;
+ *bufptr++ = value->range.lower;
+ *bufptr++ = value->range.upper >> 24;
+ *bufptr++ = value->range.upper >> 16;
+ *bufptr++ = value->range.upper >> 8;
+ *bufptr++ = value->range.upper;
+ }
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * textWithLanguage and nameWithLanguage values consist
+ * of a 2-byte length for both strings and their
+ * individual lengths, a 2-byte length for the
+ * character string, the character string without the
+ * trailing nul, a 2-byte length for the character
+ * set string, and the character set string without
+ * the trailing nul.
+ */
+
+ n = 4;
+
+ if (value->string.language != NULL)
+ n += (int)strlen(value->string.language);
+
+ if (value->string.text != NULL)
+ n += (int)strlen(value->string.text);
+
+ if (n > (IPP_BUF_SIZE - 2))
+ {
+ DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value "
+ "too long (%d)", n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /* Length of entire value */
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Length of language */
+ if (value->string.language != NULL)
+ n = (int)strlen(value->string.language);
+ else
+ n = 0;
+
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Language */
+ if (n > 0)
+ {
+ memcpy(bufptr, value->string.language, n);
+ bufptr += n;
+ }
+
+ /* Length of text */
+ if (value->string.text != NULL)
+ n = (int)strlen(value->string.text);
+ else
+ n = 0;
+
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Text */
+ if (n > 0)
+ {
+ memcpy(bufptr, value->string.text, n);
+ bufptr += n;
+ }
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ /*
+ * Collections are written with the begin-collection
+ * tag first with a value of 0 length, followed by the
+ * attributes in the collection, then the end-collection
+ * value...
+ */
+
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * Write a data length of 0 and flush the buffer...
+ */
+
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+
+ /*
+ * Then write the collection attribute...
+ */
+
+ value->collection->state = IPP_STATE_IDLE;
+
+ if (ippWriteIO(dst, cb, 1, ipp,
+ value->collection) == IPP_STATE_ERROR)
+ {
+ DEBUG_puts("1ippWriteIO: Unable to write collection value");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+ }
+ break;
+
+ default :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ /*
+ * An unknown value might some new value that a
+ * vendor has come up with. It consists of a
+ * 2-byte length and the bytes in the unknown
+ * value buffer.
+ */
+
+ n = value->unknown.length;
+
+ if (n > (IPP_BUF_SIZE - 2))
+ {
+ DEBUG_printf(("1ippWriteIO: Data length too long (%d)",
+ n));
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP "
+ "attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /* Length of unknown value */
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Value */
+ if (n > 0)
+ {
+ memcpy(bufptr, value->unknown.data, n);
+ bufptr += n;
+ }
+ }
+ break;
+ }
+
+ /*
+ * Write the data out...
+ */
+
+ if (bufptr > buffer)
+ {
+ if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP attribute...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ DEBUG_printf(("2ippWriteIO: wrote %d bytes",
+ (int)(bufptr - buffer)));
+ }
+
+ /*
+ * If blocking is disabled and we aren't at the end of the attribute
+ * list, stop here...
+ */
+
+ if (!blocking && ipp->current)
+ break;
+ }
+
+ if (ipp->current == NULL)
+ {
+ /*
+ * Done with all of the attributes; add the end-of-attributes
+ * tag or end-collection attribute...
+ */
+
+ if (parent == NULL)
+ {
+ buffer[0] = IPP_TAG_END;
+ n = 1;
+ }
+ else
+ {
+ buffer[0] = IPP_TAG_END_COLLECTION;
+ buffer[1] = 0; /* empty name */
+ buffer[2] = 0;
+ buffer[3] = 0; /* empty value */
+ buffer[4] = 0;
+ n = 5;
+ }
+
+ if ((*cb)(dst, buffer, n) < 0)
+ {
+ DEBUG_puts("1ippWriteIO: Could not write IPP end-tag...");
+ _cupsBufferRelease((char *)buffer);
+ return (IPP_STATE_ERROR);
+ }
+
+ ipp->state = IPP_STATE_DATA;
+ }
+ break;
+
+ case IPP_STATE_DATA :
+ break;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+
+ _cupsBufferRelease((char *)buffer);
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'ipp_add_attr()' - Add a new attribute to the message.
+ */
+
+static ipp_attribute_t * /* O - New attribute */
+ipp_add_attr(ipp_t *ipp, /* I - IPP message */
+ const char *name, /* I - Attribute name or NULL */
+ ipp_tag_t group_tag, /* I - Group tag or IPP_TAG_ZERO */
+ ipp_tag_t value_tag, /* I - Value tag or IPP_TAG_ZERO */
+ int num_values) /* I - Number of values */
+{
+ int alloc_values; /* Number of values to allocate */
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("4ipp_add_attr(ipp=%p, name=\"%s\", group_tag=0x%x, value_tag=0x%x, "
+ "num_values=%d)", ipp, name, group_tag, value_tag, num_values));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || num_values < 0)
+ return (NULL);
+
+ /*
+ * Allocate memory, rounding the allocation up as needed...
+ */
+
+ if (num_values <= 1)
+ alloc_values = 1;
+ else
+ alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1);
+
+ attr = calloc(sizeof(ipp_attribute_t) +
+ (alloc_values - 1) * sizeof(_ipp_value_t), 1);
+
+ if (attr)
+ {
+ /*
+ * Initialize attribute...
+ */
+
+ if (name)
+ attr->name = _cupsStrAlloc(name);
+
+ attr->group_tag = group_tag;
+ attr->value_tag = value_tag;
+ attr->num_values = num_values;
+
+ /*
+ * Add it to the end of the linked list...
+ */
+
+ if (ipp->last)
+ ipp->last->next = attr;
+ else
+ ipp->attrs = attr;
+
+ ipp->prev = ipp->last;
+ ipp->last = ipp->current = attr;
+ }
+
+ DEBUG_printf(("5ipp_add_attr: Returning %p", attr));
+
+ return (attr);
+}
+
+
+/*
+ * 'ipp_free_values()' - Free attribute values.
+ */
+
+static void
+ipp_free_values(ipp_attribute_t *attr, /* I - Attribute to free values from */
+ int element,/* I - First value to free */
+ int count) /* I - Number of values to free */
+{
+ int i; /* Looping var */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", attr,
+ element, count));
+
+ if (!(attr->value_tag & IPP_TAG_CUPS_CONST))
+ {
+ /*
+ * Free values as needed...
+ */
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ if (element == 0 && count == attr->num_values &&
+ attr->values[0].string.language)
+ {
+ _cupsStrFree(attr->values[0].string.language);
+ attr->values[0].string.language = NULL;
+ }
+ /* Fall through to other string values */
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_RESERVED_STRING :
+ 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 = count, value = attr->values + element;
+ i > 0;
+ i --, value ++)
+ {
+ _cupsStrFree(value->string.text);
+ value->string.text = NULL;
+ }
+ break;
+
+ case IPP_TAG_DEFAULT :
+ case IPP_TAG_UNKNOWN :
+ case IPP_TAG_NOVALUE :
+ case IPP_TAG_NOTSETTABLE :
+ case IPP_TAG_DELETEATTR :
+ case IPP_TAG_ADMINDEFINE :
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ case IPP_TAG_BOOLEAN :
+ case IPP_TAG_DATE :
+ case IPP_TAG_RESOLUTION :
+ case IPP_TAG_RANGE :
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ for (i = count, value = attr->values + element;
+ i > 0;
+ i --, value ++)
+ {
+ ippDelete(value->collection);
+ value->collection = NULL;
+ }
+ break;
+
+ case IPP_TAG_STRING :
+ default :
+ for (i = count, value = attr->values + element;
+ i > 0;
+ i --, value ++)
+ {
+ if (value->unknown.data)
+ {
+ free(value->unknown.data);
+ value->unknown.data = NULL;
+ }
+ }
+ break;
+ }
+ }
+
+ /*
+ * If we are not freeing values from the end, move the remaining values up...
+ */
+
+ if ((element + count) < attr->num_values)
+ memmove(attr->values + element, attr->values + element + count,
+ (attr->num_values - count - element) * sizeof(_ipp_value_t));
+
+ attr->num_values -= count;
+}
+
+
+/*
+ * 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code.
+ *
+ * This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER"
+ * to "ll-cc", "ll-region", and "charset-number", respectively.
+ */
+
+static char * /* O - Language code string */
+ipp_get_code(const char *value, /* I - Locale/charset string */
+ char *buffer, /* I - String buffer */
+ size_t bufsize) /* I - Size of string buffer */
+{
+ char *bufptr, /* Pointer into buffer */
+ *bufend; /* End of buffer */
+
+
+ /*
+ * Convert values to lowercase and change _ to - as needed...
+ */
+
+ for (bufptr = buffer, bufend = buffer + bufsize - 1;
+ *value && bufptr < bufend;
+ value ++)
+ if (*value == '_')
+ *bufptr++ = '-';
+ else
+ *bufptr++ = _cups_tolower(*value);
+
+ *bufptr = '\0';
+
+ /*
+ * Return the converted string...
+ */
+
+ return (buffer);
+}
+
+
+/*
+ * 'ipp_lang_code()' - Convert a C locale name into an IPP language code.
+ *
+ * This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and
+ * "ll-region", respectively. It also converts the "C" (POSIX) locale to "en".
+ */
+
+static char * /* O - Language code string */
+ipp_lang_code(const char *locale, /* I - Locale string */
+ char *buffer, /* I - String buffer */
+ size_t bufsize) /* I - Size of string buffer */
+{
+ /*
+ * Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is.
+ */
+
+ if (!_cups_strcasecmp(locale, "c"))
+ {
+ strlcpy(buffer, "en", bufsize);
+ return (buffer);
+ }
+ else
+ return (ipp_get_code(locale, buffer, bufsize));
+}
+
+
+/*
+ * 'ipp_length()' - Compute the length of an IPP message or collection value.
+ */
+
+static size_t /* O - Size of IPP message */
+ipp_length(ipp_t *ipp, /* I - IPP message or collection */
+ int collection) /* I - 1 if a collection, 0 otherwise */
+{
+ int i; /* Looping var */
+ size_t bytes; /* Number of bytes */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_tag_t group; /* Current group */
+ _ipp_value_t *value; /* Current value */
+
+
+ DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", ipp, collection));
+
+ if (!ipp)
+ {
+ DEBUG_puts("4ipp_length: Returning 0 bytes");
+ return (0);
+ }
+
+ /*
+ * Start with 8 bytes for the IPP message header...
+ */
+
+ bytes = collection ? 0 : 8;
+
+ /*
+ * Then add the lengths of each attribute...
+ */
+
+ group = IPP_TAG_ZERO;
+
+ for (attr = ipp->attrs; attr != NULL; attr = attr->next)
+ {
+ if (attr->group_tag != group && !collection)
+ {
+ group = attr->group_tag;
+ if (group == IPP_TAG_ZERO)
+ continue;
+
+ bytes ++; /* Group tag */
+ }
+
+ if (!attr->name)
+ continue;
+
+ DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
+ "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));
+
+ if (attr->value_tag < IPP_TAG_EXTENSION)
+ bytes += attr->num_values; /* Value tag for each value */
+ else
+ bytes += 5 * attr->num_values; /* Value tag for each value */
+ bytes += 2 * attr->num_values; /* Name lengths */
+ bytes += (int)strlen(attr->name); /* Name */
+ bytes += 2 * attr->num_values; /* Value lengths */
+
+ if (collection)
+ bytes += 5; /* Add membername overhead */
+
+ switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
+ {
+ 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 :
+ break;
+
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ bytes += 4 * attr->num_values;
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ bytes += attr->num_values;
+ break;
+
+ 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, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ if (value->string.text)
+ bytes += strlen(value->string.text);
+ break;
+
+ case IPP_TAG_DATE :
+ bytes += 11 * attr->num_values;
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ bytes += 9 * attr->num_values;
+ break;
+
+ case IPP_TAG_RANGE :
+ bytes += 8 * attr->num_values;
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ bytes += 4 * attr->num_values;/* Charset + text length */
+
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ {
+ if (value->string.language)
+ bytes += strlen(value->string.language);
+
+ if (value->string.text)
+ bytes += strlen(value->string.text);
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ bytes += ipp_length(value->collection, 1);
+ break;
+
+ default :
+ for (i = 0, value = attr->values;
+ i < attr->num_values;
+ i ++, value ++)
+ bytes += value->unknown.length;
+ break;
+ }
+ }
+
+ /*
+ * Finally, add 1 byte for the "end of attributes" tag or 5 bytes
+ * for the "end of collection" tag and return...
+ */
+
+ if (collection)
+ bytes += 5;
+ else
+ bytes ++;
+
+ DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
+
+ return (bytes);
+}
+
+
+/*
+ * 'ipp_read_http()' - Semi-blocking read on a HTTP connection...
+ */
+
+static ssize_t /* O - Number of bytes read */
+ipp_read_http(http_t *http, /* I - Client connection */
+ ipp_uchar_t *buffer, /* O - Buffer for data */
+ size_t length) /* I - Total length */
+{
+ int tbytes, /* Total bytes read */
+ bytes; /* Bytes read this pass */
+
+
+ DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)",
+ http, buffer, (int)length));
+
+ /*
+ * Loop until all bytes are read...
+ */
+
+ for (tbytes = 0, bytes = 0;
+ tbytes < (int)length;
+ tbytes += bytes, buffer += bytes)
+ {
+ DEBUG_printf(("9ipp_read_http: tbytes=%d, http->state=%d", tbytes,
+ http->state));
+
+ if (http->state == HTTP_STATE_WAITING)
+ break;
+
+ if (http->used == 0 && !http->blocking)
+ {
+ /*
+ * Wait up to 10 seconds for more data on non-blocking sockets...
+ */
+
+ if (!httpWait(http, 10000))
+ {
+ /*
+ * Signal no data...
+ */
+
+ bytes = -1;
+ break;
+ }
+ }
+
+ if ((bytes = httpRead2(http, (char *)buffer, length - tbytes)) < 0)
+ {
+#ifdef WIN32
+ break;
+#else
+ if (errno != EAGAIN && errno != EINTR)
+ break;
+
+ bytes = 0;
+#endif /* WIN32 */
+ }
+ else if (bytes == 0)
+ break;
+ }
+
+ /*
+ * Return the number of bytes read...
+ */
+
+ if (tbytes == 0 && bytes < 0)
+ tbytes = -1;
+
+ DEBUG_printf(("8ipp_read_http: Returning %d bytes", tbytes));
+
+ return (tbytes);
+}
+
+
+/*
+ * 'ipp_read_file()' - Read IPP data from a file.
+ */
+
+static ssize_t /* O - Number of bytes read */
+ipp_read_file(int *fd, /* I - File descriptor */
+ ipp_uchar_t *buffer, /* O - Read buffer */
+ size_t length) /* I - Number of bytes to read */
+{
+#ifdef WIN32
+ return ((ssize_t)read(*fd, buffer, (unsigned)length));
+#else
+ return (read(*fd, buffer, length));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'ipp_set_error()' - Set a formatted, localized error string.
+ */
+
+static void
+ipp_set_error(ipp_status_t status, /* I - Status code */
+ const char *format, /* I - Printf-style error string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to additional args */
+ char buffer[2048]; /* Message buffer */
+ cups_lang_t *lang = cupsLangDefault();
+ /* Current language */
+
+
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof(buffer), _cupsLangString(lang, format), ap);
+ va_end(ap);
+
+ _cupsSetError(status, buffer, 0);
+}
+
+
+/*
+ * 'ipp_set_value()' - Get the value element from an attribute, expanding it as
+ * needed.
+ */
+
+static _ipp_value_t * /* O - IPP value element or NULL on error */
+ipp_set_value(ipp_t *ipp, /* IO - IPP message */
+ ipp_attribute_t **attr, /* IO - IPP attribute */
+ int element) /* I - Value number (0-based) */
+{
+ ipp_attribute_t *temp, /* New attribute pointer */
+ *current, /* Current attribute in list */
+ *prev; /* Previous attribute in list */
+ int alloc_values; /* Allocated values */
+
+
+ /*
+ * If we are setting an existing value element, return it...
+ */
+
+ temp = *attr;
+
+ if (temp->num_values <= 1)
+ alloc_values = 1;
+ else
+ alloc_values = (temp->num_values + IPP_MAX_VALUES - 1) &
+ ~(IPP_MAX_VALUES - 1);
+
+ if (element < alloc_values)
+ {
+ if (element >= temp->num_values)
+ temp->num_values = element + 1;
+
+ return (temp->values + element);
+ }
+
+ /*
+ * Otherwise re-allocate the attribute - we allocate in groups of IPP_MAX_VALUE
+ * values when num_values > 1.
+ */
+
+ if (alloc_values < IPP_MAX_VALUES)
+ alloc_values = IPP_MAX_VALUES;
+ else
+ alloc_values += IPP_MAX_VALUES;
+
+ DEBUG_printf(("4ipp_set_value: Reallocating for up to %d values.",
+ alloc_values));
+
+ /*
+ * Reallocate memory...
+ */
+
+ if ((temp = realloc(temp, sizeof(ipp_attribute_t) +
+ (alloc_values - 1) * sizeof(_ipp_value_t))) == NULL)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ DEBUG_puts("4ipp_set_value: Unable to resize attribute.");
+ return (NULL);
+ }
+
+ /*
+ * Zero the new memory...
+ */
+
+ memset(temp->values + temp->num_values, 0,
+ (alloc_values - temp->num_values) * sizeof(_ipp_value_t));
+
+ if (temp != *attr)
+ {
+ /*
+ * Reset pointers in the list...
+ */
+
+ if (ipp->current == *attr && ipp->prev)
+ {
+ /*
+ * Use current "previous" pointer...
+ */
+
+ prev = ipp->prev;
+ }
+ else
+ {
+ /*
+ * Find this attribute in the linked list...
+ */
+
+ for (prev = NULL, current = ipp->attrs;
+ current && current != *attr;
+ prev = current, current = current->next);
+
+ if (!current)
+ {
+ /*
+ * This is a serious error!
+ */
+
+ *attr = temp;
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("IPP attribute is not a member of the message."), 1);
+ DEBUG_puts("4ipp_set_value: Unable to find attribute in message.");
+ return (NULL);
+ }
+ }
+
+ if (prev)
+ prev->next = temp;
+ else
+ ipp->attrs = temp;
+
+ ipp->current = temp;
+ ipp->prev = prev;
+
+ if (ipp->last == *attr)
+ ipp->last = temp;
+
+ *attr = temp;
+ }
+
+ /*
+ * Return the value element...
+ */
+
+ if (element >= temp->num_values)
+ temp->num_values = element + 1;
+
+ return (temp->values + element);
+}
+
+
+/*
+ * 'ipp_write_file()' - Write IPP data to a file.
+ */
+
+static ssize_t /* O - Number of bytes written */
+ipp_write_file(int *fd, /* I - File descriptor */
+ ipp_uchar_t *buffer, /* I - Data to write */
+ size_t length) /* I - Number of bytes to write */
+{
+#ifdef WIN32
+ return ((ssize_t)write(*fd, buffer, (unsigned)length));
+#else
+ return (write(*fd, buffer, length));
+#endif /* WIN32 */
+}
+
+
+/*
+ * End of "$Id: ipp.c 11864 2014-05-08 23:10:47Z msweet $".
+ */
diff --git a/cups/libs/cups/ipp.h b/cups/libs/cups/ipp.h
new file mode 100644
index 000000000..beada0af6
--- /dev/null
+++ b/cups/libs/cups/ipp.h
@@ -0,0 +1,998 @@
+/*
+ * "$Id: ipp.h 11734 2014-03-25 18:01:47Z msweet $"
+ *
+ * Internet Printing Protocol definitions for CUPS.
+ *
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_IPP_H_
+# define _CUPS_IPP_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "http.h"
+# include <stdarg.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * IPP version string...
+ */
+
+# define IPP_VERSION "\002\001"
+
+/*
+ * IPP registered port number...
+ *
+ * Note: Applications should never use IPP_PORT, but instead use the
+ * ippPort() function to allow overrides via the IPP_PORT environment
+ * variable and services file if needed!
+ */
+
+# define IPP_PORT 631
+
+/*
+ * Common limits...
+ */
+
+# define IPP_MAX_CHARSET 64 /* Maximum length of charset values w/nul */
+# define IPP_MAX_KEYWORD 256 /* Maximum length of keyword values w/nul */
+# define IPP_MAX_LANGUAGE 64 /* Maximum length of naturalLanguage values w/nul */
+# define IPP_MAX_LENGTH 32767 /* Maximum size of any single value */
+# define IPP_MAX_MIMETYPE 256 /* Maximum length of mimeMediaType values w/nul */
+# define IPP_MAX_NAME 256 /* Maximum length of common name values w/nul */
+# define IPP_MAX_OCTETSTRING 1023 /* Maximum length of octetString values w/o nul */
+# define IPP_MAX_TEXT 1024 /* Maximum length of text values w/nul */
+# define IPP_MAX_URI 1024 /* Maximum length of uri values w/nul */
+# define IPP_MAX_URISCHEME 64 /* Maximum length of uriScheme values w/nul */
+# define IPP_MAX_VALUES 8 /* Power-of-2 allocation increment */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum ipp_dstate_e /**** Document states ****/
+{
+ IPP_DOCUMENT_PENDING = 3, /* Document is pending */
+ IPP_DOCUMENT_PROCESSING = 5, /* Document is processing */
+ IPP_DOCUMENT_CANCELED = 7, /* Document is canceled */
+ IPP_DOCUMENT_ABORTED, /* Document is aborted */
+ IPP_DOCUMENT_COMPLETED /* Document is completed */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_DOCUMENT_PENDING IPP_DSTATE_PENDING
+# define IPP_DOCUMENT_PROCESSING IPP_DSTATE_PROCESSING
+# define IPP_DOCUMENT_CANCELED IPP_DSTATE_CANCELED
+# define IPP_DOCUMENT_ABORTED IPP_DSTATE_ABORTED
+# define IPP_DOCUMENT_COMPLETED IPP_DSTATE_COMPLETED
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_dstate_t;
+
+typedef enum ipp_finishings_e /**** Finishings ****/
+{
+ IPP_FINISHINGS_NONE = 3, /* No finishing */
+ IPP_FINISHINGS_STAPLE, /* Staple (any location) */
+ IPP_FINISHINGS_PUNCH, /* Punch (any location/count) */
+ IPP_FINISHINGS_COVER, /* Add cover */
+ IPP_FINISHINGS_BIND, /* Bind */
+ IPP_FINISHINGS_SADDLE_STITCH, /* Staple interior */
+ IPP_FINISHINGS_EDGE_STITCH, /* Stitch along any side */
+ IPP_FINISHINGS_FOLD, /* Fold (any type) */
+ IPP_FINISHINGS_TRIM, /* Trim (any type) */
+ IPP_FINISHINGS_BALE, /* Bale (any type) */
+ IPP_FINISHINGS_BOOKLET_MAKER, /* Fold to make booklet */
+ IPP_FINISHINGS_JOG_OFFSET, /* Offset for binding (any type) */
+ IPP_FINISHINGS_STAPLE_TOP_LEFT = 20, /* Staple top left corner */
+ IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, /* Staple bottom left corner */
+ IPP_FINISHINGS_STAPLE_TOP_RIGHT, /* Staple top right corner */
+ IPP_FINISHINGS_STAPLE_BOTTOM_RIGHT, /* Staple bottom right corner */
+ IPP_FINISHINGS_EDGE_STITCH_LEFT, /* Stitch along left side */
+ IPP_FINISHINGS_EDGE_STITCH_TOP, /* Stitch along top edge */
+ IPP_FINISHINGS_EDGE_STITCH_RIGHT, /* Stitch along right side */
+ IPP_FINISHINGS_EDGE_STITCH_BOTTOM, /* Stitch along bottom edge */
+ IPP_FINISHINGS_STAPLE_DUAL_LEFT, /* Two staples on left */
+ IPP_FINISHINGS_STAPLE_DUAL_TOP, /* Two staples on top */
+ IPP_FINISHINGS_STAPLE_DUAL_RIGHT, /* Two staples on right */
+ IPP_FINISHINGS_STAPLE_DUAL_BOTTOM, /* Two staples on bottom */
+ IPP_FINISHINGS_BIND_LEFT = 50, /* Bind on left */
+ IPP_FINISHINGS_BIND_TOP, /* Bind on top */
+ IPP_FINISHINGS_BIND_RIGHT, /* Bind on right */
+ IPP_FINISHINGS_BIND_BOTTOM, /* Bind on bottom */
+ IPP_FINISHINGS_TRIM_AFTER_PAGES = 60, /* Trim output after each page */
+ IPP_FINISHINGS_TRIM_AFTER_DOCUMENTS, /* Trim output after each document */
+ IPP_FINISHINGS_TRIM_AFTER_COPIES, /* Trim output after each copy */
+ IPP_FINISHINGS_TRIM_AFTER_JOB, /* Trim output after job */
+ IPP_FINISHINGS_PUNCH_TOP_LEFT = 70, /* Punch 1 hole top left */
+ IPP_FINISHINGS_PUNCH_BOTTOM_LEFT, /* Punch 1 hole bottom left */
+ IPP_FINISHINGS_PUNCH_TOP_RIGHT, /* Punch 1 hole top right */
+ IPP_FINISHINGS_PUNCH_BOTTOM_RIGHT, /* Punch 1 hole bottom right */
+ IPP_FINISHINGS_PUNCH_DUAL_LEFT, /* Punch 2 holes left side */
+ IPP_FINISHINGS_PUNCH_DUAL_TOP, /* Punch 2 holes top edge */
+ IPP_FINISHINGS_PUNCH_DUAL_RIGHT, /* Punch 2 holes right side */
+ IPP_FINISHINGS_PUNCH_DUAL_BOTTOM, /* Punch 2 holes bottom edge */
+ IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, /* Punch 3 holes left side */
+ IPP_FINISHINGS_PUNCH_TRIPLE_TOP, /* Punch 3 holes top edge */
+ IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, /* Punch 3 holes right side */
+ IPP_FINISHINGS_PUNCH_TRIPLE_BOTTOM, /* Punch 3 holes bottom edge */
+ IPP_FINISHINGS_PUNCH_QUAD_LEFT, /* Punch 4 holes left side */
+ IPP_FINISHINGS_PUNCH_QUAD_TOP, /* Punch 4 holes top edge */
+ IPP_FINISHINGS_PUNCH_QUAD_RIGHT, /* Punch 4 holes right side */
+ IPP_FINISHINGS_PUNCH_QUAD_BOTTOM, /* Punch 4 holes bottom edge */
+ IPP_FINISHINGS_FOLD_ACCORDIAN = 90, /* Accordian-fold the paper vertically into four sections */
+ IPP_FINISHINGS_FOLD_DOUBLE_GATE, /* Fold the top and bottom quarters of the paper towards the midline, then fold in half vertically */
+ IPP_FINISHINGS_FOLD_GATE, /* Fold the top and bottom quarters of the paper towards the midline */
+ IPP_FINISHINGS_FOLD_HALF, /* Fold the paper in half vertically */
+ IPP_FINISHINGS_FOLD_HALF_Z, /* Fold the paper in half horizontally, then Z-fold the paper vertically */
+ IPP_FINISHINGS_FOLD_LEFT_GATE, /* Fold the top quarter of the paper towards the midline */
+ IPP_FINISHINGS_FOLD_LETTER, /* Fold the paper into three sections vertically; sometimes also known as a C fold*/
+ IPP_FINISHINGS_FOLD_PARALLEL, /* Fold the paper in half vertically two times, yielding four sections */
+ IPP_FINISHINGS_FOLD_POSTER, /* Fold the paper in half horizontally and vertically; sometimes also called a cross fold */
+ IPP_FINISHINGS_FOLD_RIGHT_GATE, /* Fold the bottom quarter of the paper towards the midline */
+ IPP_FINISHINGS_FOLD_Z, /* Fold the paper vertically into three sections, forming a Z */
+
+ /* 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 */
+ 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 */
+ IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_RIGHT,
+ /* Punch 3 holes right side */
+ 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 */
+
+ 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 */
+} ipp_finishings_t;
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_FINISHINGS_JOB_OFFSET IPP_FINISHINGS_JOG_OFFSET
+ /* Long-time misspelling... */
+typedef enum ipp_finishings_e ipp_finish_t;
+# endif /* !_CUPS_NO_DEPRECATED */
+
+typedef enum ipp_jcollate_e /**** Job collation types ****/
+{
+ IPP_JCOLLATE_UNCOLLATED_SHEETS = 3,
+ IPP_JCOLLATE_COLLATED_DOCUMENTS,
+ IPP_JCOLLATE_UNCOLLATED_DOCUMENTS
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_JOB_UNCOLLATED_SHEETS IPP_JCOLLATE_UNCOLLATED_SHEETS
+# define IPP_JOB_COLLATED_DOCUMENTS IPP_JCOLLATE_COLLATED_DOCUMENTS
+# define IPP_JOB_UNCOLLATED_DOCUMENTS IPP_JCOLLATE_UNCOLLATED_DOCUMENTS
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_jcollate_t;
+
+typedef enum ipp_jstate_e /**** Job states ****/
+{
+ IPP_JSTATE_PENDING = 3, /* Job is waiting to be printed */
+ IPP_JSTATE_HELD, /* Job is held for printing */
+ IPP_JSTATE_PROCESSING, /* Job is currently printing */
+ IPP_JSTATE_STOPPED, /* Job has been stopped */
+ IPP_JSTATE_CANCELED, /* Job has been canceled */
+ IPP_JSTATE_ABORTED, /* Job has aborted due to error */
+ IPP_JSTATE_COMPLETED /* Job has completed successfully */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_JOB_PENDING IPP_JSTATE_PENDING
+# define IPP_JOB_HELD IPP_JSTATE_HELD
+# define IPP_JOB_PROCESSING IPP_JSTATE_PROCESSING
+# define IPP_JOB_STOPPED IPP_JSTATE_STOPPED
+# define IPP_JOB_CANCELED IPP_JSTATE_CANCELED
+# define IPP_JOB_ABORTED IPP_JSTATE_ABORTED
+# define IPP_JOB_COMPLETED IPP_JSTATE_COMPLETED
+ /* Legacy name for canceled state */
+# define IPP_JOB_CANCELLED IPP_JSTATE_CANCELED
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_jstate_t;
+
+typedef enum ipp_op_e /**** IPP operations ****/
+{
+ 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 @private@ */
+ 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 @private@ */
+ 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 @private@ */
+ 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/OS X 10.5@ */
+ IPP_OP_CREATE_JOB_SUBSCRIPTIONS, /* Create one of more job subscriptions @since CUPS 1.2/OS X 10.5@ */
+ IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES, /* Get subscription attributes @since CUPS 1.2/OS X 10.5@ */
+ IPP_OP_GET_SUBSCRIPTIONS, /* Get list of subscriptions @since CUPS 1.2/OS X 10.5@ */
+ IPP_OP_RENEW_SUBSCRIPTION, /* Renew a printer subscription @since CUPS 1.2/OS X 10.5@ */
+ IPP_OP_CANCEL_SUBSCRIPTION, /* Cancel a subscription @since CUPS 1.2/OS X 10.5@ */
+ IPP_OP_GET_NOTIFICATIONS, /* Get notification events @since CUPS 1.2/OS X 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_PAUSE_PRINTER_AFTER_CURRENT_JOB,
+ /* Stop printer after the current job @private@ */
+ IPP_OP_HOLD_NEW_JOBS, /* Hold new jobs @private@ */
+ IPP_OP_RELEASE_HELD_NEW_JOBS, /* Release new jobs @private@ */
+ IPP_OP_DEACTIVATE_PRINTER, /* Stop a printer @private@ */
+ IPP_OP_ACTIVATE_PRINTER, /* Start a printer @private@ */
+ IPP_OP_RESTART_PRINTER, /* Restart a printer @private@ */
+ IPP_OP_SHUTDOWN_PRINTER, /* Turn a printer off @private@ */
+ IPP_OP_STARTUP_PRINTER, /* Turn a printer on @private@ */
+ IPP_OP_REPROCESS_JOB, /* Reprint a job @private@ */
+ IPP_OP_CANCEL_CURRENT_JOB, /* Cancel the current job @private@ */
+ IPP_OP_SUSPEND_CURRENT_JOB, /* Suspend the current job @private@ */
+ IPP_OP_RESUME_JOB, /* Resume the current job @private@ */
+ IPP_OP_PROMOTE_JOB, /* Promote a job to print sooner @private@ */
+ IPP_OP_SCHEDULE_JOB_AFTER, /* Schedule a job to print after another @private@ */
+ IPP_OP_CANCEL_DOCUMENT = 0x0033, /* Cancel-Document @private@ */
+ IPP_OP_GET_DOCUMENT_ATTRIBUTES, /* Get-Document-Attributes @private@ */
+ IPP_OP_GET_DOCUMENTS, /* Get-Documents @private@ */
+ IPP_OP_DELETE_DOCUMENT, /* Delete-Document @private@ */
+ IPP_OP_SET_DOCUMENT_ATTRIBUTES, /* Set-Document-Attributes @private@ */
+ 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 @private@ */
+ IPP_OP_VALIDATE_DOCUMENT, /* Validate-Document @private@ */
+ IPP_OP_SEND_HARDCOPY_DOCUMENT, /* Send-Hardcopy-Document @private@ */
+
+ 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 */
+ IPP_OP_CUPS_GET_PPDS, /* Get a list of supported drivers */
+ IPP_OP_CUPS_MOVE_JOB, /* Move a job to a different printer */
+ IPP_OP_CUPS_AUTHENTICATE_JOB, /* Authenticate a job @since CUPS 1.2/OS X 10.5@ */
+ IPP_OP_CUPS_GET_PPD, /* Get a PPD file @since CUPS 1.3/OS X 10.5@ */
+ IPP_OP_CUPS_GET_DOCUMENT = 0x4027 /* Get a document file @since CUPS 1.4/OS X 10.6@ */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_PRINT_JOB IPP_OP_PRINT_JOB
+# define IPP_PRINT_URI IPP_OP_PRINT_URI
+# define IPP_VALIDATE_JOB IPP_OP_VALIDATE_JOB
+# define IPP_CREATE_JOB IPP_OP_CREATE_JOB
+# define IPP_SEND_DOCUMENT IPP_OP_SEND_DOCUMENT
+# define IPP_SEND_URI IPP_OP_SEND_URI
+# define IPP_CANCEL_JOB IPP_OP_CANCEL_JOB
+# define IPP_GET_JOB_ATTRIBUTES IPP_OP_GET_JOB_ATTRIBUTES
+# define IPP_GET_JOBS IPP_OP_GET_JOBS
+# define IPP_GET_PRINTER_ATTRIBUTES IPP_OP_GET_PRINTER_ATTRIBUTES
+# define IPP_HOLD_JOB IPP_OP_HOLD_JOB
+# define IPP_RELEASE_JOB IPP_OP_RELEASE_JOB
+# define IPP_RESTART_JOB IPP_OP_RESTART_JOB
+# define IPP_PAUSE_PRINTER IPP_OP_PAUSE_PRINTER
+# define IPP_RESUME_PRINTER IPP_OP_RESUME_PRINTER
+# define IPP_PURGE_JOBS IPP_OP_PURGE_JOBS
+# define IPP_SET_PRINTER_ATTRIBUTES IPP_OP_SET_PRINTER_ATTRIBUTES
+# define IPP_SET_JOB_ATTRIBUTES IPP_OP_SET_JOB_ATTRIBUTES
+# define IPP_GET_PRINTER_SUPPORTED_VALUES IPP_OP_GET_PRINTER_SUPPORTED_VALUES
+# 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_GET_SUBSCRIPTION_ATTRIBUTES IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
+# define IPP_GET_SUBSCRIPTIONS IPP_OP_GET_SUBSCRIPTIONS
+# define IPP_RENEW_SUBSCRIPTION IPP_OP_RENEW_SUBSCRIPTION
+# define IPP_CANCEL_SUBSCRIPTION IPP_OP_CANCEL_SUBSCRIPTION
+# define IPP_GET_NOTIFICATIONS IPP_OP_GET_NOTIFICATIONS
+# define IPP_SEND_NOTIFICATIONS IPP_OP_SEND_NOTIFICATIONS
+# define IPP_GET_RESOURCE_ATTRIBUTES IPP_OP_GET_RESOURCE_ATTRIBUTES
+# define IPP_GET_RESOURCE_DATA IPP_OP_GET_RESOURCE_DATA
+# define IPP_GET_RESOURCES IPP_OP_GET_RESOURCES
+# define IPP_GET_PRINT_SUPPORT_FILES IPP_OP_GET_PRINT_SUPPORT_FILES
+# define IPP_ENABLE_PRINTER IPP_OP_ENABLE_PRINTER
+# define IPP_DISABLE_PRINTER IPP_OP_DISABLE_PRINTER
+# define IPP_PAUSE_PRINTER_AFTER_CURRENT_JOB IPP_OP_PAUSE_PRINTER_AFTER_CURRENT_JOB
+# define IPP_HOLD_NEW_JOBS IPP_OP_HOLD_NEW_JOBS
+# define IPP_RELEASE_HELD_NEW_JOBS IPP_OP_RELEASE_HELD_NEW_JOBS
+# define IPP_DEACTIVATE_PRINTER IPP_OP_DEACTIVATE_PRINTER
+# define IPP_ACTIVATE_PRINTER IPP_OP_ACTIVATE_PRINTER
+# define IPP_RESTART_PRINTER IPP_OP_RESTART_PRINTER
+# define IPP_SHUTDOWN_PRINTER IPP_OP_SHUTDOWN_PRINTER
+# define IPP_STARTUP_PRINTER IPP_OP_STARTUP_PRINTER
+# define IPP_REPROCESS_JOB IPP_OP_REPROCESS_JOB
+# define IPP_CANCEL_CURRENT_JOB IPP_OP_CANCEL_CURRENT_JOB
+# define IPP_SUSPEND_CURRENT_JOB IPP_OP_SUSPEND_CURRENT_JOB
+# define IPP_RESUME_JOB IPP_OP_RESUME_JOB
+# define IPP_PROMOTE_JOB IPP_OP_PROMOTE_JOB
+# define IPP_SCHEDULE_JOB_AFTER IPP_OP_SCHEDULE_JOB_AFTER
+# define IPP_CANCEL_DOCUMENT IPP_OP_CANCEL_DOCUMENT
+# define IPP_GET_DOCUMENT_ATTRIBUTES IPP_OP_GET_DOCUMENT_ATTRIBUTES
+# define IPP_GET_DOCUMENTS IPP_OP_GET_DOCUMENTS
+# define IPP_DELETE_DOCUMENT IPP_OP_DELETE_DOCUMENT
+# define IPP_SET_DOCUMENT_ATTRIBUTES IPP_OP_SET_DOCUMENT_ATTRIBUTES
+# define IPP_CANCEL_JOBS IPP_OP_CANCEL_JOBS
+# define IPP_CANCEL_MY_JOBS IPP_OP_CANCEL_MY_JOBS
+# define IPP_RESUBMIT_JOB IPP_OP_RESUBMIT_JOB
+# define IPP_CLOSE_JOB IPP_OP_CLOSE_JOB
+# define IPP_IDENTIFY_PRINTER IPP_OP_IDENTIFY_PRINTER
+# define IPP_VALIDATE_DOCUMENT IPP_OP_VALIDATE_DOCUMENT
+# define IPP_PRIVATE IPP_OP_PRIVATE
+# define CUPS_GET_DEFAULT IPP_OP_CUPS_GET_DEFAULT
+# define CUPS_GET_PRINTERS IPP_OP_CUPS_GET_PRINTERS
+# define CUPS_ADD_MODIFY_PRINTER IPP_OP_CUPS_ADD_MODIFY_PRINTER
+# define CUPS_DELETE_PRINTER IPP_OP_CUPS_DELETE_PRINTER
+# define CUPS_GET_CLASSES IPP_OP_CUPS_GET_CLASSES
+# define CUPS_ADD_MODIFY_CLASS IPP_OP_CUPS_ADD_MODIFY_CLASS
+# define CUPS_DELETE_CLASS IPP_OP_CUPS_DELETE_CLASS
+# define CUPS_ACCEPT_JOBS IPP_OP_CUPS_ACCEPT_JOBS
+# define CUPS_REJECT_JOBS IPP_OP_CUPS_REJECT_JOBS
+# define CUPS_SET_DEFAULT IPP_OP_CUPS_SET_DEFAULT
+# define CUPS_GET_DEVICES IPP_OP_CUPS_GET_DEVICES
+# define CUPS_GET_PPDS IPP_OP_CUPS_GET_PPDS
+# define CUPS_MOVE_JOB IPP_OP_CUPS_MOVE_JOB
+# define CUPS_AUTHENTICATE_JOB IPP_OP_CUPS_AUTHENTICATE_JOB
+# define CUPS_GET_PPD IPP_OP_CUPS_GET_PPD
+# define CUPS_GET_DOCUMENT IPP_OP_CUPS_GET_DOCUMENT
+ /* Legacy names */
+# define CUPS_ADD_PRINTER IPP_OP_CUPS_ADD_MODIFY_PRINTER
+# define CUPS_ADD_CLASS IPP_OP_CUPS_ADD_MODIFY_CLASS
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_op_t;
+
+typedef enum ipp_orient_e /**** Orientation values ****/
+{
+ IPP_ORIENT_PORTRAIT = 3, /* No rotation */
+ IPP_ORIENT_LANDSCAPE, /* 90 degrees counter-clockwise */
+ IPP_ORIENT_REVERSE_LANDSCAPE, /* 90 degrees clockwise */
+ IPP_ORIENT_REVERSE_PORTRAIT /* 180 degrees */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_PORTRAIT IPP_ORIENT_PORTRAIT
+# define IPP_LANDSCAPE IPP_ORIENT_LANDSCAPE
+# define IPP_REVERSE_LANDSCAPE IPP_ORIENT_REVERSE_LANDSCAPE
+# define IPP_REVERSE_PORTRAIT IPP_ORIENT_REVERSE_PORTRAIT
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_orient_t;
+
+typedef enum ipp_pstate_e /**** Printer states ****/
+{
+ IPP_PSTATE_IDLE = 3, /* Printer is idle */
+ IPP_PSTATE_PROCESSING, /* Printer is working */
+ IPP_PSTATE_STOPPED /* Printer is stopped */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_PRINTER_IDLE IPP_PSTATE_IDLE
+# define IPP_PRINTER_PROCESSING IPP_PSTATE_PROCESSING
+# define IPP_PRINTER_STOPPED IPP_PSTATE_STOPPED
+# endif /* _CUPS_NO_DEPRECATED */
+} ipp_pstate_t;
+
+typedef enum ipp_quality_e /**** Qualities ****/
+{
+ IPP_QUALITY_DRAFT = 3, /* Draft quality */
+ IPP_QUALITY_NORMAL, /* Normal quality */
+ IPP_QUALITY_HIGH /* High quality */
+} ipp_quality_t;
+
+typedef enum ipp_res_e /**** Resolution units ****/
+{
+ IPP_RES_PER_INCH = 3, /* Pixels per inch */
+ IPP_RES_PER_CM /* Pixels per centimeter */
+} ipp_res_t;
+
+typedef enum ipp_state_e /**** IPP states ****/
+{
+ IPP_STATE_ERROR = -1, /* An error occurred */
+ IPP_STATE_IDLE, /* Nothing is happening/request completed */
+ IPP_STATE_HEADER, /* The request header needs to be sent/received */
+ IPP_STATE_ATTRIBUTE, /* One or more attributes need to be sent/received */
+ IPP_STATE_DATA /* IPP request data needs to be sent/received */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_ERROR IPP_STATE_ERROR
+# define IPP_IDLE IPP_STATE_IDLE
+# define IPP_HEADER IPP_STATE_HEADER
+# define IPP_ATTRIBUTE IPP_STATE_ATTRIBUTE
+# define IPP_DATA IPP_STATE_DATA
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_state_t;
+
+typedef enum ipp_status_e /**** IPP status codes ****/
+{
+ IPP_STATUS_CUPS_INVALID = -1, /* Invalid status name for @link ippErrorValue@ */
+ IPP_STATUS_OK = 0x0000, /* successful-ok */
+ IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED, /* successful-ok-ignored-or-substituted-attributes */
+ IPP_STATUS_OK_CONFLICTING, /* successful-ok-conflicting-attributes */
+ IPP_STATUS_OK_IGNORED_SUBSCRIPTIONS, /* successful-ok-ignored-subscriptions */
+ IPP_STATUS_OK_IGNORED_NOTIFICATIONS, /* successful-ok-ignored-notifications @private@ */
+ IPP_STATUS_OK_TOO_MANY_EVENTS, /* successful-ok-too-many-events */
+ IPP_STATUS_OK_BUT_CANCEL_SUBSCRIPTION,/* successful-ok-but-cancel-subscription @private@ */
+ 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_ERROR_BAD_REQUEST = 0x0400,/* client-error-bad-request */
+ IPP_STATUS_ERROR_FORBIDDEN, /* client-error-forbidden */
+ IPP_STATUS_ERROR_NOT_AUTHENTICATED, /* client-error-not-authenticated */
+ IPP_STATUS_ERROR_NOT_AUTHORIZED, /* client-error-not-authorized */
+ IPP_STATUS_ERROR_NOT_POSSIBLE, /* client-error-not-possible */
+ IPP_STATUS_ERROR_TIMEOUT, /* client-error-timeout */
+ IPP_STATUS_ERROR_NOT_FOUND, /* client-error-not-found */
+ IPP_STATUS_ERROR_GONE, /* client-error-gone */
+ IPP_STATUS_ERROR_REQUEST_ENTITY, /* client-error-request-entity-too-large */
+ IPP_STATUS_ERROR_REQUEST_VALUE, /* client-error-request-value-too-long */
+ IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED,
+ /* client-error-document-format-not-supported */
+ IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,/* client-error-attributes-or-values-not-supported */
+ IPP_STATUS_ERROR_URI_SCHEME, /* client-error-uri-scheme-not-supported */
+ IPP_STATUS_ERROR_CHARSET, /* client-error-charset-not-supported */
+ IPP_STATUS_ERROR_CONFLICTING, /* client-error-conflicting-attributes */
+ IPP_STATUS_ERROR_COMPRESSION_NOT_SUPPORTED,
+ /* client-error-compression-not-supported */
+ IPP_STATUS_ERROR_COMPRESSION_ERROR, /* client-error-compression-error */
+ IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR,
+ /* client-error-document-format-error */
+ IPP_STATUS_ERROR_DOCUMENT_ACCESS, /* client-error-document-access-error */
+ IPP_STATUS_ERROR_ATTRIBUTES_NOT_SETTABLE,
+ /* client-error-attributes-not-settable */
+ IPP_STATUS_ERROR_IGNORED_ALL_SUBSCRIPTIONS,
+ /* client-error-ignored-all-subscriptions */
+ IPP_STATUS_ERROR_TOO_MANY_SUBSCRIPTIONS,
+ /* client-error-too-many-subscriptions */
+ IPP_STATUS_ERROR_IGNORED_ALL_NOTIFICATIONS,
+ /* client-error-ignored-all-notifications @private@ */
+ IPP_STATUS_ERROR_PRINT_SUPPORT_FILE_NOT_FOUND,
+ /* client-error-print-support-file-not-found @private@ */
+ IPP_STATUS_ERROR_DOCUMENT_PASSWORD, /* client-error-document-password-error */
+ IPP_STATUS_ERROR_DOCUMENT_PERMISSION, /* client-error-document-permission-error */
+ IPP_STATUS_ERROR_DOCUMENT_SECURITY, /* client-error-document-security-error */
+ IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE,/* client-error-document-unprintable-error */
+
+ /* Proposed extensions for paid printing */
+ IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED = 0x049C,
+ /* cups-error-account-info-needed @since CUPS 1.7/OS X 10.9@ */
+ IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED, /* cups-error-account-closed @since CUPS 1.7/OS X 10.9@ */
+ IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED,
+ /* cups-error-account-limit-reached @since CUPS 1.7/OS X 10.9@ */
+ IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED,
+ /* cups-error-account-authorization-failed @since CUPS 1.7/OS X 10.9@ */
+
+ IPP_STATUS_ERROR_INTERNAL = 0x0500, /* server-error-internal-error */
+ IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
+ /* server-error-operation-not-supported */
+ IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, /* server-error-service-unavailable */
+ IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
+ /* server-error-version-not-supported */
+ IPP_STATUS_ERROR_DEVICE, /* server-error-device-error */
+ IPP_STATUS_ERROR_TEMPORARY, /* server-error-temporary-error */
+ IPP_STATUS_ERROR_NOT_ACCEPTING_JOBS, /* server-error-not-accepting-jobs */
+ IPP_STATUS_ERROR_BUSY, /* server-error-busy */
+ IPP_STATUS_ERROR_JOB_CANCELED, /* server-error-job-canceled */
+ IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
+ /* server-error-multiple-document-jobs-not-supported */
+ IPP_STATUS_ERROR_PRINTER_IS_DEACTIVATED,
+ /* server-error-printer-is-deactivated */
+ IPP_STATUS_ERROR_TOO_MANY_JOBS, /* server-error-too-many-jobs */
+ IPP_STATUS_ERROR_TOO_MANY_DOCUMENTS, /* server-error-too-many-documents */
+
+ /* These are internal and never sent over the wire... */
+ IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED = 0x1000,
+ /* cups-authentication-canceled - Authentication canceled by user @since CUPS 1.5/OS X 10.7@ */
+ IPP_STATUS_ERROR_CUPS_PKI, /* cups-pki-error - Error negotiating a secure connection @since CUPS 1.5/OS X 10.7@ */
+ IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED/* cups-upgrade-required - TLS upgrade required */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_OK IPP_STATUS_OK
+# define IPP_OK_SUBST IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
+# define IPP_OK_CONFLICT IPP_STATUS_OK_CONFLICTING
+# define IPP_OK_IGNORED_SUBSCRIPTIONS IPP_STATUS_OK_IGNORED_SUBSCRIPTIONS
+# define IPP_OK_IGNORED_NOTIFICATIONS IPP_STATUS_OK_IGNORED_NOTIFICATIONS
+# define IPP_OK_TOO_MANY_EVENTS IPP_STATUS_OK_TOO_MANY_EVENTS
+# define IPP_OK_BUT_CANCEL_SUBSCRIPTION IPP_STATUS_OK_BUT_CANCEL_SUBSCRIPTION
+# define IPP_OK_EVENTS_COMPLETE IPP_STATUS_OK_EVENTS_COMPLETE
+# define IPP_REDIRECTION_OTHER_SITE IPP_STATUS_REDIRECTION_OTHER_SITE
+# define CUPS_SEE_OTHER IPP_STATUS_CUPS_SEE_OTHER
+# define IPP_BAD_REQUEST IPP_STATUS_ERROR_BAD_REQUEST
+# define IPP_FORBIDDEN IPP_STATUS_ERROR_FORBIDDEN
+# define IPP_NOT_AUTHENTICATED IPP_STATUS_ERROR_NOT_AUTHENTICATED
+# define IPP_NOT_AUTHORIZED IPP_STATUS_ERROR_NOT_AUTHORIZED
+# define IPP_NOT_POSSIBLE IPP_STATUS_ERROR_NOT_POSSIBLE
+# define IPP_TIMEOUT IPP_STATUS_ERROR_TIMEOUT
+# define IPP_NOT_FOUND IPP_STATUS_ERROR_NOT_FOUND
+# define IPP_GONE IPP_STATUS_ERROR_GONE
+# define IPP_REQUEST_ENTITY IPP_STATUS_ERROR_REQUEST_ENTITY
+# define IPP_REQUEST_VALUE IPP_STATUS_ERROR_REQUEST_VALUE
+# define IPP_DOCUMENT_FORMAT IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED
+# define IPP_ATTRIBUTES IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
+# define IPP_URI_SCHEME IPP_STATUS_ERROR_URI_SCHEME
+# define IPP_CHARSET IPP_STATUS_ERROR_CHARSET
+# define IPP_CONFLICT IPP_STATUS_ERROR_CONFLICTING
+# define IPP_COMPRESSION_NOT_SUPPORTED IPP_STATUS_ERROR_COMPRESSION_NOT_SUPPORTED
+# define IPP_COMPRESSION_ERROR IPP_STATUS_ERROR_COMPRESSION_ERROR
+# define IPP_DOCUMENT_FORMAT_ERROR IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR
+# define IPP_DOCUMENT_ACCESS_ERROR IPP_STATUS_ERROR_DOCUMENT_ACCESS
+# define IPP_ATTRIBUTES_NOT_SETTABLE IPP_STATUS_ERROR_ATTRIBUTES_NOT_SETTABLE
+# define IPP_IGNORED_ALL_SUBSCRIPTIONS IPP_STATUS_ERROR_IGNORED_ALL_SUBSCRIPTIONS
+# define IPP_TOO_MANY_SUBSCRIPTIONS IPP_STATUS_ERROR_TOO_MANY_SUBSCRIPTIONS
+# define IPP_IGNORED_ALL_NOTIFICATIONS IPP_STATUS_ERROR_IGNORED_ALL_NOTIFICATIONS
+# define IPP_PRINT_SUPPORT_FILE_NOT_FOUND IPP_STATUS_ERROR_PRINT_SUPPORT_FILE_NOT_FOUND
+# define IPP_DOCUMENT_PASSWORD_ERROR IPP_STATUS_ERROR_DOCUMENT_PASSWORD
+# define IPP_DOCUMENT_PERMISSION_ERROR IPP_STATUS_ERROR_DOCUMENT_PERMISSION
+# define IPP_DOCUMENT_SECURITY_ERROR IPP_STATUS_ERROR_DOCUMENT_SECURITY
+# define IPP_DOCUMENT_UNPRINTABLE_ERROR IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE
+# define IPP_INTERNAL_ERROR IPP_STATUS_ERROR_INTERNAL
+# define IPP_OPERATION_NOT_SUPPORTED IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
+# define IPP_SERVICE_UNAVAILABLE IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
+# define IPP_VERSION_NOT_SUPPORTED IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
+# define IPP_DEVICE_ERROR IPP_STATUS_ERROR_DEVICE
+# define IPP_TEMPORARY_ERROR IPP_STATUS_ERROR_TEMPORARY
+# define IPP_NOT_ACCEPTING IPP_STATUS_ERROR_NOT_ACCEPTING_JOBS
+# define IPP_PRINTER_BUSY IPP_STATUS_ERROR_BUSY
+# define IPP_ERROR_JOB_CANCELED IPP_STATUS_ERROR_JOB_CANCELED
+# define IPP_MULTIPLE_JOBS_NOT_SUPPORTED IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
+# define IPP_PRINTER_IS_DEACTIVATED IPP_STATUS_ERROR_PRINTER_IS_DEACTIVATED
+# define IPP_TOO_MANY_JOBS IPP_STATUS_ERROR_TOO_MANY_JOBS
+# define IPP_TOO_MANY_DOCUMENTS IPP_STATUS_ERROR_TOO_MANY_DOCUMENTS
+# define IPP_AUTHENTICATION_CANCELED IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED
+# define IPP_PKI_ERROR IPP_STATUS_ERROR_CUPS_PKI
+# define IPP_UPGRADE_REQUIRED IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED
+ /* Legacy name for canceled status */
+# define IPP_ERROR_JOB_CANCELLED IPP_STATUS_ERROR_JOB_CANCELED
+# endif /* _CUPS_NO_DEPRECATED */
+} ipp_status_t;
+
+typedef enum ipp_tag_e /**** Format tags for attributes ****/
+{
+ IPP_TAG_CUPS_INVALID = -1, /* Invalid tag name for @link ippTagValue@ */
+ IPP_TAG_ZERO = 0x00, /* Zero tag - used for separators */
+ IPP_TAG_OPERATION, /* Operation group */
+ IPP_TAG_JOB, /* Job group */
+ IPP_TAG_END, /* End-of-attributes */
+ IPP_TAG_PRINTER, /* Printer group */
+ IPP_TAG_UNSUPPORTED_GROUP, /* Unsupported attributes group */
+ IPP_TAG_SUBSCRIPTION, /* Subscription group */
+ IPP_TAG_EVENT_NOTIFICATION, /* Event group */
+ IPP_TAG_RESOURCE, /* Resource group @private@ */
+ IPP_TAG_DOCUMENT, /* Document group */
+ IPP_TAG_UNSUPPORTED_VALUE = 0x10, /* Unsupported value */
+ IPP_TAG_DEFAULT, /* Default value */
+ IPP_TAG_UNKNOWN, /* Unknown value */
+ IPP_TAG_NOVALUE, /* No-value value */
+ IPP_TAG_NOTSETTABLE = 0x15, /* Not-settable value */
+ IPP_TAG_DELETEATTR, /* Delete-attribute value */
+ IPP_TAG_ADMINDEFINE, /* Admin-defined value */
+ IPP_TAG_INTEGER = 0x21, /* Integer value */
+ IPP_TAG_BOOLEAN, /* Boolean value */
+ IPP_TAG_ENUM, /* Enumeration value */
+ IPP_TAG_STRING = 0x30, /* Octet string value */
+ 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_TEXTLANG, /* Text-with-language value */
+ IPP_TAG_NAMELANG, /* Name-with-language value */
+ IPP_TAG_END_COLLECTION, /* End of collection value */
+ IPP_TAG_TEXT = 0x41, /* Text value */
+ IPP_TAG_NAME, /* Name value */
+ IPP_TAG_RESERVED_STRING, /* Reserved for future string value @private@ */
+ IPP_TAG_KEYWORD, /* Keyword value */
+ IPP_TAG_URI, /* URI value */
+ IPP_TAG_URISCHEME, /* URI scheme value */
+ 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_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@ */
+
+# ifndef _CUPS_NO_DEPRECATED
+# define IPP_TAG_MASK IPP_TAG_CUPS_MASK
+# define IPP_TAG_COPY IPP_TAG_CUPS_CONST
+# endif /* !_CUPS_NO_DEPRECATED */
+} ipp_tag_t;
+
+typedef unsigned char ipp_uchar_t; /**** Unsigned 8-bit integer/character ****/
+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/OS X 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/OS X 10.5@ ****/
+
+/**** New in CUPS 1.6/OS X 10.8 ****/
+typedef int (*ipp_copycb_t)(void *context, ipp_t *dst, ipp_attribute_t *attr);
+
+
+/*
+ * The following structures are PRIVATE starting with CUPS 1.6/OS X 10.8.
+ * Please use the new accessor functions available in CUPS 1.6 and later, as
+ * these definitions will be moved to a private header file in a future release.
+ *
+ * Define _IPP_PRIVATE_STRUCTURES to 1 to cause the private IPP structures to be
+ * exposed in CUPS 1.6. This happens automatically on OS X when compiling for
+ * a deployment target of 10.7 or earlier.
+ *
+ * Define _IPP_PRIVATE_STRUCTURES to 0 to prevent the private IPP structures
+ * from being exposed. This is useful when migrating existing code to the new
+ * accessors.
+ */
+
+# ifdef _IPP_PRIVATE_STRUCTURES
+ /* Somebody has overridden the value */
+# elif defined(_CUPS_SOURCE) || defined(_CUPS_IPP_PRIVATE_H_)
+ /* Building CUPS */
+# define _IPP_PRIVATE_STRUCTURES 1
+# elif defined(__APPLE__)
+# if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
+ /* Building for 10.7 and earlier */
+# define _IPP_PRIVATE_STRUCTURES 1
+# elif !defined(MAC_OS_X_VERSION_10_8)
+ /* Building for 10.7 and earlier */
+# define _IPP_PRIVATE_STRUCTURES 1
+# endif /* MAC_OS_X_VERSION_10_8 && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 */
+# else
+# define _IPP_PRIVATE_STRUCTURES 0
+# endif /* _CUPS_SOURCE || _CUPS_IPP_PRIVATE_H_ */
+
+# if _IPP_PRIVATE_STRUCTURES
+typedef union _ipp_request_u /**** Request Header ****/
+{
+ struct /* Any Header */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ int op_status; /* Operation ID or status code*/
+ int request_id; /* Request ID */
+ } any;
+
+ struct /* Operation Header */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ ipp_op_t operation_id; /* Operation ID */
+ int request_id; /* Request ID */
+ } op;
+
+ struct /* Status Header */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ ipp_status_t status_code; /* Status code */
+ int request_id; /* Request ID */
+ } status;
+
+ /**** New in CUPS 1.1.19 ****/
+ struct /* Event Header @since CUPS 1.1.19/OS X 10.3@ */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ ipp_status_t status_code; /* Status code */
+ int request_id; /* Request ID */
+ } event;
+} _ipp_request_t;
+
+/**** New in CUPS 1.1.19 ****/
+
+typedef union _ipp_value_u /**** Attribute Value ****/
+{
+ int integer; /* Integer/enumerated value */
+
+ char boolean; /* Boolean value */
+
+ ipp_uchar_t date[11]; /* Date/time value */
+
+ struct
+ {
+ int xres, /* Horizontal resolution */
+ yres; /* Vertical resolution */
+ ipp_res_t units; /* Resolution units */
+ } resolution; /* Resolution value */
+
+ struct
+ {
+ int lower, /* Lower value */
+ upper; /* Upper value */
+ } range; /* Range of integers value */
+
+ struct
+ {
+ char *language; /* Language code */
+ char *text; /* String */
+ } string; /* String with language value */
+
+ struct
+ {
+ int length; /* Length of attribute */
+ void *data; /* Data in attribute */
+ } unknown; /* Unknown attribute type */
+
+/**** New in CUPS 1.1.19 ****/
+ ipp_t *collection; /* Collection value @since CUPS 1.1.19/OS X 10.3@ */
+} _ipp_value_t;
+typedef _ipp_value_t ipp_value_t; /**** Convenience typedef that will be removed @private@ ****/
+
+struct _ipp_attribute_s /**** Attribute ****/
+{
+ ipp_attribute_t *next; /* Next attribute in list */
+ ipp_tag_t group_tag, /* Job/Printer/Operation group tag */
+ value_tag; /* What type of value is it? */
+ char *name; /* Name of attribute */
+ int num_values; /* Number of values */
+ _ipp_value_t values[1]; /* Values */
+};
+
+struct _ipp_s /**** IPP Request/Response/Notification ****/
+{
+ ipp_state_t state; /* State of request */
+ _ipp_request_t request; /* Request header */
+ ipp_attribute_t *attrs; /* Attributes */
+ ipp_attribute_t *last; /* Last attribute in list */
+ ipp_attribute_t *current; /* Current attribute (for read/write) */
+ ipp_tag_t curtag; /* Current attribute group tag */
+
+/**** New in CUPS 1.2 ****/
+ ipp_attribute_t *prev; /* Previous attribute (for read) @since CUPS 1.2/OS X 10.5@ */
+
+/**** New in CUPS 1.4.4 ****/
+ int use; /* Use count @since CUPS 1.4.4/OS X 10.6.?@ */
+};
+# endif /* _IPP_PRIVATE_STRUCTURES */
+
+
+/*
+ * Prototypes...
+ */
+
+extern ipp_attribute_t *ippAddBoolean(ipp_t *ipp, ipp_tag_t group,
+ const char *name, char value);
+extern ipp_attribute_t *ippAddBooleans(ipp_t *ipp, ipp_tag_t group,
+ const char *name, int num_values,
+ const char *values);
+extern ipp_attribute_t *ippAddDate(ipp_t *ipp, ipp_tag_t group,
+ const char *name, const ipp_uchar_t *value);
+extern ipp_attribute_t *ippAddInteger(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name,
+ int value);
+extern ipp_attribute_t *ippAddIntegers(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name,
+ int num_values, const int *values);
+extern ipp_attribute_t *ippAddRange(ipp_t *ipp, ipp_tag_t group,
+ const char *name, int lower, int upper);
+extern ipp_attribute_t *ippAddRanges(ipp_t *ipp, ipp_tag_t group,
+ const char *name, int num_values,
+ const int *lower, const int *upper);
+extern ipp_attribute_t *ippAddResolution(ipp_t *ipp, ipp_tag_t group,
+ const char *name, ipp_res_t units,
+ int xres, int yres);
+extern ipp_attribute_t *ippAddResolutions(ipp_t *ipp, ipp_tag_t group,
+ const char *name, int num_values,
+ ipp_res_t units, const int *xres,
+ const int *yres);
+extern ipp_attribute_t *ippAddSeparator(ipp_t *ipp);
+extern ipp_attribute_t *ippAddString(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name,
+ const char *language, const char *value);
+extern ipp_attribute_t *ippAddStrings(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name,
+ int num_values, const char *language,
+ const char * const *values);
+extern time_t ippDateToTime(const ipp_uchar_t *date);
+extern void ippDelete(ipp_t *ipp);
+extern const char *ippErrorString(ipp_status_t error);
+extern ipp_attribute_t *ippFindAttribute(ipp_t *ipp, const char *name,
+ ipp_tag_t value_tag);
+extern ipp_attribute_t *ippFindNextAttribute(ipp_t *ipp, const char *name,
+ ipp_tag_t value_tag);
+extern size_t ippLength(ipp_t *ipp);
+extern ipp_t *ippNew(void);
+extern ipp_state_t ippRead(http_t *http, ipp_t *ipp);
+extern const ipp_uchar_t *ippTimeToDate(time_t t);
+extern ipp_state_t ippWrite(http_t *http, ipp_t *ipp);
+extern int ippPort(void);
+extern void ippSetPort(int p);
+
+/**** New in CUPS 1.1.19 ****/
+extern ipp_attribute_t *ippAddCollection(ipp_t *ipp, ipp_tag_t group,
+ const char *name, ipp_t *value) _CUPS_API_1_1_19;
+extern ipp_attribute_t *ippAddCollections(ipp_t *ipp, ipp_tag_t group,
+ const char *name, int num_values,
+ const ipp_t **values) _CUPS_API_1_1_19;
+extern void ippDeleteAttribute(ipp_t *ipp, ipp_attribute_t *attr) _CUPS_API_1_1_19;
+extern ipp_state_t ippReadFile(int fd, ipp_t *ipp) _CUPS_API_1_1_19;
+extern ipp_state_t ippWriteFile(int fd, ipp_t *ipp) _CUPS_API_1_1_19;
+
+/**** New in CUPS 1.2/OS X 10.5 ****/
+extern ipp_attribute_t *ippAddOctetString(ipp_t *ipp, ipp_tag_t group,
+ const char *name,
+ const void *data, int datalen) _CUPS_API_1_2;
+extern ipp_status_t ippErrorValue(const char *name) _CUPS_API_1_2;
+extern ipp_t *ippNewRequest(ipp_op_t op) _CUPS_API_1_2;
+extern const char *ippOpString(ipp_op_t op) _CUPS_API_1_2;
+extern ipp_op_t ippOpValue(const char *name) _CUPS_API_1_2;
+extern ipp_state_t ippReadIO(void *src, ipp_iocb_t cb, int blocking,
+ ipp_t *parent, ipp_t *ipp) _CUPS_API_1_2;
+extern ipp_state_t ippWriteIO(void *dst, ipp_iocb_t cb, int blocking,
+ ipp_t *parent, ipp_t *ipp) _CUPS_API_1_2;
+
+/**** New in CUPS 1.4/OS X 10.6 ****/
+extern const char *ippTagString(ipp_tag_t tag) _CUPS_API_1_4;
+extern ipp_tag_t ippTagValue(const char *name) _CUPS_API_1_4;
+
+/**** New in CUPS 1.6/OS X 10.8 ****/
+extern ipp_attribute_t *ippAddOutOfBand(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name)
+ _CUPS_API_1_6;
+extern size_t ippAttributeString(ipp_attribute_t *attr, char *buffer,
+ size_t bufsize) _CUPS_API_1_6;
+extern ipp_attribute_t *ippCopyAttribute(ipp_t *dst, ipp_attribute_t *attr,
+ int quickcopy) _CUPS_API_1_6;
+extern int ippCopyAttributes(ipp_t *dst, ipp_t *src,
+ int quickcopy, ipp_copycb_t cb,
+ void *context) _CUPS_API_1_6;
+extern int ippDeleteValues(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, int count) _CUPS_API_1_6;
+extern const char *ippEnumString(const char *attrname, int enumvalue)
+ _CUPS_API_1_6;
+extern int ippEnumValue(const char *attrname,
+ const char *enumstring) _CUPS_API_1_6;
+extern ipp_attribute_t *ippFirstAttribute(ipp_t *ipp) _CUPS_API_1_6;
+extern int ippGetBoolean(ipp_attribute_t *attr, int element)
+ _CUPS_API_1_6;
+extern ipp_t *ippGetCollection(ipp_attribute_t *attr,
+ int element) _CUPS_API_1_6;
+extern int ippGetCount(ipp_attribute_t *attr) _CUPS_API_1_6;
+extern const ipp_uchar_t *ippGetDate(ipp_attribute_t *attr, int element)
+ _CUPS_API_1_6;
+extern ipp_tag_t ippGetGroupTag(ipp_attribute_t *attr) _CUPS_API_1_6;
+extern int ippGetInteger(ipp_attribute_t *attr, int element)
+ _CUPS_API_1_6;
+extern const char *ippGetName(ipp_attribute_t *attr) _CUPS_API_1_6;
+extern ipp_op_t ippGetOperation(ipp_t *ipp) _CUPS_API_1_6;
+extern int ippGetRange(ipp_attribute_t *attr, int element,
+ int *upper) _CUPS_API_1_6;
+extern int ippGetRequestId(ipp_t *ipp) _CUPS_API_1_6;
+extern int ippGetResolution(ipp_attribute_t *attr, int element,
+ int *yres, ipp_res_t *units)
+ _CUPS_API_1_6;
+extern ipp_state_t ippGetState(ipp_t *ipp) _CUPS_API_1_6;
+extern ipp_status_t ippGetStatusCode(ipp_t *ipp) _CUPS_API_1_6;
+extern const char *ippGetString(ipp_attribute_t *attr, int element,
+ const char **language) _CUPS_API_1_6;
+extern ipp_tag_t ippGetValueTag(ipp_attribute_t *attr) _CUPS_API_1_6;
+extern int ippGetVersion(ipp_t *ipp, int *minor) _CUPS_API_1_6;
+extern ipp_attribute_t *ippNextAttribute(ipp_t *ipp) _CUPS_API_1_6;
+extern int ippSetBoolean(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, int boolvalue) _CUPS_API_1_6;
+extern int ippSetCollection(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, ipp_t *colvalue)
+ _CUPS_API_1_6;
+extern int ippSetDate(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, const ipp_uchar_t *datevalue)
+ _CUPS_API_1_6;
+extern int ippSetGroupTag(ipp_t *ipp, ipp_attribute_t **attr,
+ ipp_tag_t group_tag) _CUPS_API_1_6;
+extern int ippSetInteger(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, int intvalue) _CUPS_API_1_6;
+extern int ippSetName(ipp_t *ipp, ipp_attribute_t **attr,
+ const char *name) _CUPS_API_1_6;
+extern int ippSetOperation(ipp_t *ipp, ipp_op_t op) _CUPS_API_1_6;
+extern int ippSetRange(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, int lowervalue, int uppervalue)
+ _CUPS_API_1_6;
+extern int ippSetRequestId(ipp_t *ipp, int request_id)
+ _CUPS_API_1_6;
+extern int ippSetResolution(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, ipp_res_t unitsvalue,
+ int xresvalue, int yresvalue)
+ _CUPS_API_1_6;
+extern int ippSetState(ipp_t *ipp, ipp_state_t state)
+ _CUPS_API_1_6;
+extern int ippSetStatusCode(ipp_t *ipp, ipp_status_t status)
+ _CUPS_API_1_6;
+extern int ippSetString(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, const char *strvalue)
+ _CUPS_API_1_6;
+extern int ippSetValueTag(ipp_t *ipp, ipp_attribute_t **attr,
+ ipp_tag_t value_tag) _CUPS_API_1_6;
+extern int ippSetVersion(ipp_t *ipp, int major, int minor)
+ _CUPS_API_1_6;
+
+/**** New in CUPS 1.7 ****/
+extern ipp_attribute_t *ippAddStringf(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name,
+ const char *language, const char *format,
+ ...) _CUPS_API_1_7;
+extern ipp_attribute_t *ippAddStringfv(ipp_t *ipp, ipp_tag_t group,
+ ipp_tag_t value_tag, const char *name,
+ const char *language,
+ const char *format, va_list ap)
+ _CUPS_API_1_7;
+extern int ippContainsInteger(ipp_attribute_t *attr, int value)
+ _CUPS_API_1_7;
+extern int ippContainsString(ipp_attribute_t *attr,
+ const char *value) _CUPS_API_1_7;
+extern cups_array_t *ippCreateRequestedArray(ipp_t *request) _CUPS_API_1_7;
+extern void *ippGetOctetString(ipp_attribute_t *attr, int element,
+ int *datalen) _CUPS_API_1_7;
+extern ipp_t *ippNewResponse(ipp_t *request) _CUPS_API_1_7;
+extern int ippSetOctetString(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, const void *data,
+ int datalen) _CUPS_API_1_7;
+extern int ippSetStringf(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, const char *format,
+ ...) _CUPS_API_1_7;
+extern int ippSetStringfv(ipp_t *ipp, ipp_attribute_t **attr,
+ int element, const char *format,
+ va_list ap) _CUPS_API_1_7;
+extern int ippValidateAttribute(ipp_attribute_t *attr)
+ _CUPS_API_1_7;
+extern int ippValidateAttributes(ipp_t *ipp) _CUPS_API_1_7;
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_IPP_H_ */
+
+/*
+ * End of "$Id: ipp.h 11734 2014-03-25 18:01:47Z msweet $".
+ */
diff --git a/cups/libs/cups/langprintf.c b/cups/libs/cups/langprintf.c
new file mode 100644
index 000000000..af641c91d
--- /dev/null
+++ b/cups/libs/cups/langprintf.c
@@ -0,0 +1,352 @@
+/*
+ * "$Id: langprintf.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Localized printf/puts functions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2002-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsLangPrintError() - Print a message followed by a standard error.
+ * _cupsLangPrintFilter() - Print a formatted filter message string to a file.
+ * _cupsLangPrintf() - Print a formatted message string to a file.
+ * _cupsLangPuts() - Print a static message string to a file.
+ * _cupsSetLocale() - Set the current locale and transcode the
+ * command-line.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * '_cupsLangPrintError()' - Print a message followed by a standard error.
+ */
+
+void
+_cupsLangPrintError(const char *prefix, /* I - Non-localized message prefix */
+ const char *message)/* I - Message */
+{
+ int bytes; /* Number of bytes formatted */
+ int last_errno; /* Last error */
+ char buffer[2048], /* Message buffer */
+ *bufptr, /* Pointer into buffer */
+ output[8192]; /* Output buffer */
+ _cups_globals_t *cg; /* Global data */
+
+
+ /*
+ * Range check...
+ */
+
+ if (!message)
+ return;
+
+ /*
+ * Save the errno value...
+ */
+
+ last_errno = errno;
+
+ /*
+ * Get the message catalog...
+ */
+
+ cg = _cupsGlobals();
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ /*
+ * Format the message...
+ */
+
+ if (prefix)
+ {
+ snprintf(buffer, sizeof(buffer), "%s:", prefix);
+ bufptr = buffer + strlen(buffer);
+ }
+ else
+ bufptr = buffer;
+
+ snprintf(bufptr, sizeof(buffer) - (bufptr - buffer),
+ /* TRANSLATORS: Message is "subject: error" */
+ _cupsLangString(cg->lang_default, _("%s: %s")),
+ _cupsLangString(cg->lang_default, message), strerror(last_errno));
+ strlcat(buffer, "\n", sizeof(buffer));
+
+ /*
+ * Convert and write to stderr...
+ */
+
+ bytes = cupsUTF8ToCharset(output, (cups_utf8_t *)buffer, sizeof(output),
+ cg->lang_default->encoding);
+
+ if (bytes > 0)
+ fwrite(output, 1, bytes, stderr);
+}
+
+
+/*
+ * '_cupsLangPrintFilter()' - Print a formatted filter message string to a file.
+ */
+
+int /* O - Number of bytes written */
+_cupsLangPrintFilter(
+ FILE *fp, /* I - File to write to */
+ const char *prefix, /* I - Non-localized message prefix */
+ const char *message, /* I - Message string to use */
+ ...) /* I - Additional arguments as needed */
+{
+ int bytes; /* Number of bytes formatted */
+ char temp[2048], /* Temporary format buffer */
+ buffer[2048], /* Message buffer */
+ output[8192]; /* Output buffer */
+ va_list ap; /* Pointer to additional arguments */
+ _cups_globals_t *cg; /* Global data */
+
+
+ /*
+ * Range check...
+ */
+
+ if (!fp || !message)
+ return (-1);
+
+ cg = _cupsGlobals();
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ /*
+ * Format the string...
+ */
+
+ va_start(ap, message);
+ snprintf(temp, sizeof(temp), "%s: %s\n", prefix,
+ _cupsLangString(cg->lang_default, message));
+ vsnprintf(buffer, sizeof(buffer), temp, ap);
+ va_end(ap);
+
+ /*
+ * Transcode to the destination charset...
+ */
+
+ bytes = cupsUTF8ToCharset(output, (cups_utf8_t *)buffer, sizeof(output),
+ cg->lang_default->encoding);
+
+ /*
+ * Write the string and return the number of bytes written...
+ */
+
+ if (bytes > 0)
+ return ((int)fwrite(output, 1, bytes, fp));
+ else
+ return (bytes);
+}
+
+
+/*
+ * '_cupsLangPrintf()' - Print a formatted message string to a file.
+ */
+
+int /* O - Number of bytes written */
+_cupsLangPrintf(FILE *fp, /* I - File to write to */
+ const char *message, /* I - Message string to use */
+ ...) /* I - Additional arguments as needed */
+{
+ int bytes; /* Number of bytes formatted */
+ char buffer[2048], /* Message buffer */
+ output[8192]; /* Output buffer */
+ va_list ap; /* Pointer to additional arguments */
+ _cups_globals_t *cg; /* Global data */
+
+
+ /*
+ * Range check...
+ */
+
+ if (!fp || !message)
+ return (-1);
+
+ cg = _cupsGlobals();
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ /*
+ * Format the string...
+ */
+
+ va_start(ap, message);
+ vsnprintf(buffer, sizeof(buffer) - 1,
+ _cupsLangString(cg->lang_default, message), ap);
+ va_end(ap);
+
+ strlcat(buffer, "\n", sizeof(buffer));
+
+ /*
+ * Transcode to the destination charset...
+ */
+
+ bytes = cupsUTF8ToCharset(output, (cups_utf8_t *)buffer, sizeof(output),
+ cg->lang_default->encoding);
+
+ /*
+ * Write the string and return the number of bytes written...
+ */
+
+ if (bytes > 0)
+ return ((int)fwrite(output, 1, bytes, fp));
+ else
+ return (bytes);
+}
+
+
+/*
+ * '_cupsLangPuts()' - Print a static message string to a file.
+ */
+
+int /* O - Number of bytes written */
+_cupsLangPuts(FILE *fp, /* I - File to write to */
+ const char *message) /* I - Message string to use */
+{
+ int bytes; /* Number of bytes formatted */
+ char output[8192]; /* Message buffer */
+ _cups_globals_t *cg; /* Global data */
+
+
+ /*
+ * Range check...
+ */
+
+ if (!fp || !message)
+ return (-1);
+
+ cg = _cupsGlobals();
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ /*
+ * Transcode to the destination charset...
+ */
+
+ bytes = cupsUTF8ToCharset(output,
+ (cups_utf8_t *)_cupsLangString(cg->lang_default,
+ message),
+ sizeof(output) - 4, cg->lang_default->encoding);
+ bytes += cupsUTF8ToCharset(output + bytes, (cups_utf8_t *)"\n",
+ sizeof(output) - bytes,
+ cg->lang_default->encoding);
+
+ /*
+ * Write the string and return the number of bytes written...
+ */
+
+ if (bytes > 0)
+ return ((int)fwrite(output, 1, bytes, fp));
+ else
+ return (bytes);
+}
+
+
+/*
+ * '_cupsSetLocale()' - Set the current locale and transcode the command-line.
+ */
+
+void
+_cupsSetLocale(char *argv[]) /* IO - Command-line arguments */
+{
+ int i; /* Looping var */
+ char buffer[8192]; /* Command-line argument buffer */
+ _cups_globals_t *cg; /* Global data */
+#ifdef LC_TIME
+ const char *lc_time; /* Current LC_TIME value */
+ char new_lc_time[255], /* New LC_TIME value */
+ *charset; /* Pointer to character set */
+#endif /* LC_TIME */
+
+
+ /*
+ * Set the locale so that times, etc. are displayed properly.
+ *
+ * Unfortunately, while we need the localized time value, we *don't*
+ * want to use the localized charset for the time value, so we need
+ * to set LC_TIME to the locale name with .UTF-8 on the end (if
+ * the locale includes a character set specifier...)
+ */
+
+ setlocale(LC_ALL, "");
+
+#ifdef LC_TIME
+ if ((lc_time = setlocale(LC_TIME, NULL)) == NULL)
+ lc_time = setlocale(LC_ALL, NULL);
+
+ if (lc_time)
+ {
+ strlcpy(new_lc_time, lc_time, sizeof(new_lc_time));
+ if ((charset = strchr(new_lc_time, '.')) == NULL)
+ charset = new_lc_time + strlen(new_lc_time);
+
+ strlcpy(charset, ".UTF-8", sizeof(new_lc_time) - (charset - new_lc_time));
+ }
+ else
+ strlcpy(new_lc_time, "C", sizeof(new_lc_time));
+
+ setlocale(LC_TIME, new_lc_time);
+#endif /* LC_TIME */
+
+ /*
+ * Initialize the default language info...
+ */
+
+ cg = _cupsGlobals();
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ /*
+ * Transcode the command-line arguments from the locale charset to
+ * UTF-8...
+ */
+
+ if (cg->lang_default->encoding != CUPS_US_ASCII &&
+ cg->lang_default->encoding != CUPS_UTF8)
+ {
+ for (i = 1; argv[i]; i ++)
+ {
+ /*
+ * Try converting from the locale charset to UTF-8...
+ */
+
+ if (cupsCharsetToUTF8((cups_utf8_t *)buffer, argv[i], sizeof(buffer),
+ cg->lang_default->encoding) < 0)
+ continue;
+
+ /*
+ * Save the new string if it differs from the original...
+ */
+
+ if (strcmp(buffer, argv[i]))
+ argv[i] = strdup(buffer);
+ }
+ }
+}
+
+
+/*
+ * End of "$Id: langprintf.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/language-private.h b/cups/libs/cups/language-private.h
new file mode 100644
index 000000000..93cdf0831
--- /dev/null
+++ b/cups/libs/cups/language-private.h
@@ -0,0 +1,86 @@
+/*
+ * "$Id: language-private.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Private localization support for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_LANGUAGE_PRIVATE_H_
+# define _CUPS_LANGUAGE_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include <cups/transcode.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Macro for localized text...
+ */
+
+# define _(x) x
+
+
+/*
+ * Types...
+ */
+
+typedef struct _cups_message_s /**** Message catalog entry ****/
+{
+ char *id, /* Original string */
+ *str; /* Localized string */
+} _cups_message_t;
+
+
+/*
+ * Prototypes...
+ */
+
+# ifdef __APPLE__
+extern const char *_cupsAppleLanguage(const char *locale, char *language,
+ size_t langsize);
+# endif /* __APPLE__ */
+extern void _cupsCharmapFlush(void);
+extern const char *_cupsEncodingName(cups_encoding_t encoding);
+extern void _cupsLangPrintError(const char *prefix,
+ const char *message);
+extern int _cupsLangPrintFilter(FILE *fp, const char *prefix,
+ const char *message, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+extern int _cupsLangPrintf(FILE *fp, const char *message, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+extern int _cupsLangPuts(FILE *fp, const char *message);
+extern const char *_cupsLangString(cups_lang_t *lang,
+ const char *message);
+extern void _cupsMessageFree(cups_array_t *a);
+extern cups_array_t *_cupsMessageLoad(const char *filename, int unquote);
+extern const char *_cupsMessageLookup(cups_array_t *a, const char *m);
+extern cups_array_t *_cupsMessageNew(void *context);
+extern void _cupsSetLocale(char *argv[]);
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_LANGUAGE_PRIVATE_H_ */
+
+/*
+ * End of "$Id: language-private.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/language.c b/cups/libs/cups/language.c
new file mode 100644
index 000000000..f02355086
--- /dev/null
+++ b/cups/libs/cups/language.c
@@ -0,0 +1,1596 @@
+/*
+ * "$Id: language.c 11424 2013-11-08 19:51:01Z msweet $"
+ *
+ * I18N/language support for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsAppleLanguage() - Get the Apple language identifier associated with
+ * a locale ID.
+ * _cupsEncodingName() - Return the character encoding name string for the
+ * given encoding enumeration.
+ * cupsLangDefault() - Return the default language.
+ * cupsLangEncoding() - Return the character encoding (us-ascii, etc.)
+ * for the given language.
+ * cupsLangFlush() - Flush all language data out of the cache.
+ * cupsLangFree() - Free language data.
+ * cupsLangGet() - Get a language.
+ * _cupsLangString() - Get a message string.
+ * _cupsMessageFree() - Free a messages array.
+ * _cupsMessageLoad() - Load a .po file into a messages array.
+ * _cupsMessageLookup() - Lookup a message string.
+ * _cupsMessageNew() - Make a new message catalog array.
+ * appleLangDefault() - Get the default locale string.
+ * appleMessageLoad() - Load a message catalog from a localizable bundle.
+ * cups_cache_lookup() - Lookup a language in the cache...
+ * cups_message_compare() - Compare two messages.
+ * cups_message_free() - Free a message.
+ * cups_message_load() - Load the message catalog for a language.
+ * cups_unquote() - Unquote characters in strings...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif /* HAVE_LANGINFO_H */
+#ifdef WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 */
+#ifdef HAVE_COREFOUNDATION_H
+# include <CoreFoundation/CoreFoundation.h>
+#endif /* HAVE_COREFOUNDATION_H */
+
+#ifdef __APPLE__
+/* This file does not #include the neccesary Carbon Framework .h files for
+ * the C code to work, so we'll just undefine it and use the fallback
+ */
+#undef __APPLE__
+#endif
+
+/*
+ * Local globals...
+ */
+
+static _cups_mutex_t lang_mutex = _CUPS_MUTEX_INITIALIZER;
+ /* Mutex to control access to cache */
+static cups_lang_t *lang_cache = NULL;
+ /* Language string cache */
+static const char * const lang_encodings[] =
+ { /* Encoding strings */
+ "us-ascii", "iso-8859-1",
+ "iso-8859-2", "iso-8859-3",
+ "iso-8859-4", "iso-8859-5",
+ "iso-8859-6", "iso-8859-7",
+ "iso-8859-8", "iso-8859-9",
+ "iso-8859-10", "utf-8",
+ "iso-8859-13", "iso-8859-14",
+ "iso-8859-15", "cp874",
+ "cp1250", "cp1251",
+ "cp1252", "cp1253",
+ "cp1254", "cp1255",
+ "cp1256", "cp1257",
+ "cp1258", "koi8-r",
+ "koi8-u", "iso-8859-11",
+ "iso-8859-16", "mac",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "cp932", "cp936",
+ "cp949", "cp950",
+ "cp1361", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "euc-cn", "euc-jp",
+ "euc-kr", "euc-tw",
+ "shift_jisx0213"
+ };
+
+#ifdef __APPLE__
+typedef struct
+{
+ const char * const language; /* Language ID */
+ const char * const locale; /* Locale ID */
+} _apple_language_locale_t;
+
+static const _apple_language_locale_t apple_language_locale[] =
+{ /* Locale to language ID LUT */
+ { "en", "en_US" },
+ { "nb", "no" },
+ { "zh-Hans", "zh_CN" },
+ { "zh-Hant", "zh_TW" }
+};
+#endif /* __APPLE__ */
+
+
+/*
+ * Local functions...
+ */
+
+
+#ifdef __APPLE__
+static const char *appleLangDefault(void);
+# ifdef CUPS_BUNDLEDIR
+# ifndef CF_RETURNS_RETAINED
+# if __has_feature(attribute_cf_returns_retained)
+# define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
+# else
+# define CF_RETURNS_RETAINED
+# endif /* __has_feature(attribute_cf_returns_retained) */
+# endif /* !CF_RETURNED_RETAINED */
+static cups_array_t *appleMessageLoad(const char *locale)
+ CF_RETURNS_RETAINED;
+# endif /* CUPS_BUNDLEDIR */
+#endif /* __APPLE__ */
+static cups_lang_t *cups_cache_lookup(const char *name,
+ cups_encoding_t encoding);
+static int cups_message_compare(_cups_message_t *m1,
+ _cups_message_t *m2);
+static void cups_message_free(_cups_message_t *m);
+static void cups_message_load(cups_lang_t *lang);
+static void cups_unquote(char *d, const char *s);
+
+
+#ifdef __APPLE__
+/*
+ * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
+ * locale ID.
+ */
+
+const char * /* O - Language ID */
+_cupsAppleLanguage(const char *locale, /* I - Locale ID */
+ char *language,/* I - Language ID buffer */
+ size_t langsize) /* I - Size of language ID buffer */
+{
+ int i; /* Looping var */
+ CFStringRef localeid, /* CF locale identifier */
+ langid; /* CF language identifier */
+
+
+ /*
+ * Copy the locale name and convert, as needed, to the Apple-specific
+ * locale identifier...
+ */
+
+ switch (strlen(locale))
+ {
+ default :
+ /*
+ * Invalid locale...
+ */
+
+ strlcpy(language, "en", langsize);
+ break;
+
+ case 2 :
+ strlcpy(language, locale, langsize);
+ break;
+
+ case 5 :
+ strlcpy(language, locale, langsize);
+
+ if (language[2] == '-')
+ {
+ /*
+ * Convert ll-cc to ll_CC...
+ */
+
+ language[2] = '_';
+ language[3] = toupper(language[3] & 255);
+ language[4] = toupper(language[4] & 255);
+ }
+ break;
+ }
+
+ for (i = 0;
+ i < (int)(sizeof(apple_language_locale) /
+ sizeof(apple_language_locale[0]));
+ i ++)
+ if (!strcmp(locale, apple_language_locale[i].locale))
+ {
+ strlcpy(language, apple_language_locale[i].language, sizeof(language));
+ break;
+ }
+
+ /*
+ * Attempt to map the locale ID to a language ID...
+ */
+
+ if ((localeid = CFStringCreateWithCString(kCFAllocatorDefault, language,
+ kCFStringEncodingASCII)) != NULL)
+ {
+ if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
+ kCFAllocatorDefault, localeid)) != NULL)
+ {
+ CFStringGetCString(langid, language, langsize, kCFStringEncodingASCII);
+ CFRelease(langid);
+ }
+
+ CFRelease(localeid);
+ }
+
+ /*
+ * Return what we got...
+ */
+
+ return (language);
+}
+#endif /* __APPLE__ */
+
+
+/*
+ * '_cupsEncodingName()' - Return the character encoding name string
+ * for the given encoding enumeration.
+ */
+
+const char * /* O - Character encoding */
+_cupsEncodingName(
+ cups_encoding_t encoding) /* I - Encoding value */
+{
+ if (encoding < 0 ||
+ encoding >= (sizeof(lang_encodings) / sizeof(const char *)))
+ {
+ DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
+ encoding, lang_encodings[0]));
+ return (lang_encodings[0]);
+ }
+ else
+ {
+ DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
+ encoding, lang_encodings[encoding]));
+ return (lang_encodings[encoding]);
+ }
+}
+
+
+/*
+ * 'cupsLangDefault()' - Return the default language.
+ */
+
+cups_lang_t * /* O - Language data */
+cupsLangDefault(void)
+{
+ return (cupsLangGet(NULL));
+}
+
+
+/*
+ * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
+ * for the given language.
+ */
+
+const char * /* O - Character encoding */
+cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
+{
+ if (lang == NULL)
+ return ((char*)lang_encodings[0]);
+ else
+ return ((char*)lang_encodings[lang->encoding]);
+}
+
+
+/*
+ * 'cupsLangFlush()' - Flush all language data out of the cache.
+ */
+
+void
+cupsLangFlush(void)
+{
+ cups_lang_t *lang, /* Current language */
+ *next; /* Next language */
+
+
+ /*
+ * Free all languages in the cache...
+ */
+
+ _cupsMutexLock(&lang_mutex);
+
+ for (lang = lang_cache; lang != NULL; lang = next)
+ {
+ /*
+ * Free all messages...
+ */
+
+ _cupsMessageFree(lang->strings);
+
+ /*
+ * Then free the language structure itself...
+ */
+
+ next = lang->next;
+ free(lang);
+ }
+
+ lang_cache = NULL;
+
+ _cupsMutexUnlock(&lang_mutex);
+}
+
+
+/*
+ * 'cupsLangFree()' - Free language data.
+ *
+ * This does not actually free anything; use @link cupsLangFlush@ for that.
+ */
+
+void
+cupsLangFree(cups_lang_t *lang) /* I - Language to free */
+{
+ _cupsMutexLock(&lang_mutex);
+
+ if (lang != NULL && lang->used > 0)
+ lang->used --;
+
+ _cupsMutexUnlock(&lang_mutex);
+}
+
+
+/*
+ * 'cupsLangGet()' - Get a language.
+ */
+
+cups_lang_t * /* O - Language data */
+cupsLangGet(const char *language) /* I - Language or locale */
+{
+ int i; /* Looping var */
+#ifndef __APPLE__
+ char locale[255]; /* Copy of locale name */
+#endif /* !__APPLE__ */
+ char langname[16], /* Requested language name */
+ country[16], /* Country code */
+ charset[16], /* Character set */
+ *csptr, /* Pointer to CODESET string */
+ *ptr, /* Pointer into language/charset */
+ real[48]; /* Real language name */
+ cups_encoding_t encoding; /* Encoding to use */
+ cups_lang_t *lang; /* Current language... */
+ static const char * const locale_encodings[] =
+ { /* Locale charset names */
+ "ASCII", "ISO88591", "ISO88592", "ISO88593",
+ "ISO88594", "ISO88595", "ISO88596", "ISO88597",
+ "ISO88598", "ISO88599", "ISO885910", "UTF8",
+ "ISO885913", "ISO885914", "ISO885915", "CP874",
+ "CP1250", "CP1251", "CP1252", "CP1253",
+ "CP1254", "CP1255", "CP1256", "CP1257",
+ "CP1258", "KOI8R", "KOI8U", "ISO885911",
+ "ISO885916", "MACROMAN", "", "",
+
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+
+ "CP932", "CP936", "CP949", "CP950",
+ "CP1361", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+
+ "EUCCN", "EUCJP", "EUCKR", "EUCTW",
+ "SHIFT_JISX0213"
+ };
+
+
+ DEBUG_printf(("2cupsLangGet(language=\"%s\")", language));
+
+#ifdef __APPLE__
+ /*
+ * Set the character set to UTF-8...
+ */
+
+ strlcpy(charset, "UTF8", sizeof(charset));
+
+ /*
+ * Apple's setlocale doesn't give us the user's localization
+ * preference so we have to look it up this way...
+ */
+
+ if (!language)
+ {
+ if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL)
+ language = appleLangDefault();
+
+ DEBUG_printf(("4cupsLangGet: language=\"%s\"", language));
+ }
+
+#else
+ /*
+ * Set the charset to "unknown"...
+ */
+
+ charset[0] = '\0';
+
+ /*
+ * Use setlocale() to determine the currently set locale, and then
+ * fallback to environment variables to avoid setting the locale,
+ * since setlocale() is not thread-safe!
+ */
+
+ if (!language)
+ {
+ /*
+ * First see if the locale has been set; if it is still "C" or
+ * "POSIX", use the environment to get the default...
+ */
+
+# ifdef LC_MESSAGES
+ ptr = setlocale(LC_MESSAGES, NULL);
+# else
+ ptr = setlocale(LC_ALL, NULL);
+# endif /* LC_MESSAGES */
+
+ DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
+
+ if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
+ {
+ /*
+ * Get the character set from the LC_CTYPE locale setting...
+ */
+
+ if ((ptr = getenv("LC_CTYPE")) == NULL)
+ if ((ptr = getenv("LC_ALL")) == NULL)
+ if ((ptr = getenv("LANG")) == NULL)
+ ptr = "en_US";
+
+ if ((csptr = strchr(ptr, '.')) != NULL)
+ {
+ /*
+ * Extract the character set from the environment...
+ */
+
+ for (ptr = charset, csptr ++; *csptr; csptr ++)
+ if (ptr < (charset + sizeof(charset) - 1) && _cups_isalnum(*csptr))
+ *ptr++ = *csptr;
+
+ *ptr = '\0';
+ }
+
+ /*
+ * Get the locale for messages from the LC_MESSAGES locale setting...
+ */
+
+ if ((ptr = getenv("LC_MESSAGES")) == NULL)
+ if ((ptr = getenv("LC_ALL")) == NULL)
+ if ((ptr = getenv("LANG")) == NULL)
+ ptr = "en_US";
+ }
+
+ if (ptr)
+ {
+ strlcpy(locale, ptr, sizeof(locale));
+ language = locale;
+
+ /*
+ * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
+ */
+
+ if (!strncmp(locale, "nb", 2))
+ locale[1] = 'o';
+
+ DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
+ }
+ }
+#endif /* __APPLE__ */
+
+ /*
+ * If "language" is NULL at this point, then chances are we are using
+ * a language that is not installed for the base OS.
+ */
+
+ if (!language)
+ {
+ /*
+ * Switch to the POSIX ("C") locale...
+ */
+
+ language = "C";
+ }
+
+#ifdef CODESET
+ /*
+ * On systems that support the nl_langinfo(CODESET) call, use
+ * this value as the character set...
+ */
+
+ if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
+ {
+ /*
+ * Copy all of the letters and numbers in the CODESET string...
+ */
+
+ for (ptr = charset; *csptr; csptr ++)
+ if (_cups_isalnum(*csptr) && ptr < (charset + sizeof(charset) - 1))
+ *ptr++ = *csptr;
+
+ *ptr = '\0';
+
+ DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
+ "nl_langinfo(CODESET)...", charset));
+ }
+#endif /* CODESET */
+
+ /*
+ * If we don't have a character set by now, default to UTF-8...
+ */
+
+ if (!charset[0])
+ strlcpy(charset, "UTF8", sizeof(charset));
+
+ /*
+ * Parse the language string passed in to a locale string. "C" is the
+ * standard POSIX locale and is copied unchanged. Otherwise the
+ * language string is converted from ll-cc[.charset] (language-country)
+ * to ll_CC[.CHARSET] to match the file naming convention used by all
+ * POSIX-compliant operating systems. Invalid language names are mapped
+ * to the POSIX locale.
+ */
+
+ country[0] = '\0';
+
+ if (language == NULL || !language[0] ||
+ !strcmp(language, "POSIX"))
+ strlcpy(langname, "C", sizeof(langname));
+ else
+ {
+ /*
+ * Copy the parts of the locale string over safely...
+ */
+
+ for (ptr = langname; *language; language ++)
+ if (*language == '_' || *language == '-' || *language == '.')
+ break;
+ else if (ptr < (langname + sizeof(langname) - 1))
+ *ptr++ = tolower(*language & 255);
+
+ *ptr = '\0';
+
+ if (*language == '_' || *language == '-')
+ {
+ /*
+ * Copy the country code...
+ */
+
+ for (language ++, ptr = country; *language; language ++)
+ if (*language == '.')
+ break;
+ else if (ptr < (country + sizeof(country) - 1))
+ *ptr++ = toupper(*language & 255);
+
+ *ptr = '\0';
+ }
+
+ if (*language == '.' && !charset[0])
+ {
+ /*
+ * Copy the encoding...
+ */
+
+ for (language ++, ptr = charset; *language; language ++)
+ if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1))
+ *ptr++ = toupper(*language & 255);
+
+ *ptr = '\0';
+ }
+
+ /*
+ * Force a POSIX locale for an invalid language name...
+ */
+
+ if (strlen(langname) != 2)
+ {
+ strlcpy(langname, "C", sizeof(langname));
+ country[0] = '\0';
+ charset[0] = '\0';
+ }
+ }
+
+ DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
+ langname, country, charset));
+
+ /*
+ * Figure out the desired encoding...
+ */
+
+ encoding = CUPS_AUTO_ENCODING;
+
+ if (charset[0])
+ {
+ for (i = 0;
+ i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
+ i ++)
+ if (!_cups_strcasecmp(charset, locale_encodings[i]))
+ {
+ encoding = (cups_encoding_t)i;
+ break;
+ }
+
+ if (encoding == CUPS_AUTO_ENCODING)
+ {
+ /*
+ * Map alternate names for various character sets...
+ */
+
+ if (!_cups_strcasecmp(charset, "iso-2022-jp") ||
+ !_cups_strcasecmp(charset, "sjis"))
+ encoding = CUPS_WINDOWS_932;
+ else if (!_cups_strcasecmp(charset, "iso-2022-cn"))
+ encoding = CUPS_WINDOWS_936;
+ else if (!_cups_strcasecmp(charset, "iso-2022-kr"))
+ encoding = CUPS_WINDOWS_949;
+ else if (!_cups_strcasecmp(charset, "big5"))
+ encoding = CUPS_WINDOWS_950;
+ }
+ }
+
+ DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
+ encoding == CUPS_AUTO_ENCODING ? "auto" :
+ lang_encodings[encoding]));
+
+ /*
+ * See if we already have this language/country loaded...
+ */
+
+ if (country[0])
+ snprintf(real, sizeof(real), "%s_%s", langname, country);
+ else
+ strlcpy(real, langname, sizeof(real));
+
+ _cupsMutexLock(&lang_mutex);
+
+ if ((lang = cups_cache_lookup(real, encoding)) != NULL)
+ {
+ _cupsMutexUnlock(&lang_mutex);
+
+ DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
+
+ return (lang);
+ }
+
+ /*
+ * See if there is a free language available; if so, use that
+ * record...
+ */
+
+ for (lang = lang_cache; lang != NULL; lang = lang->next)
+ if (lang->used == 0)
+ break;
+
+ if (lang == NULL)
+ {
+ /*
+ * Allocate memory for the language and add it to the cache.
+ */
+
+ if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
+ {
+ _cupsMutexUnlock(&lang_mutex);
+
+ return (NULL);
+ }
+
+ lang->next = lang_cache;
+ lang_cache = lang;
+ }
+ else
+ {
+ /*
+ * Free all old strings as needed...
+ */
+
+ _cupsMessageFree(lang->strings);
+ lang->strings = NULL;
+ }
+
+ /*
+ * Then assign the language and encoding fields...
+ */
+
+ lang->used ++;
+ strlcpy(lang->language, real, sizeof(lang->language));
+
+ if (encoding != CUPS_AUTO_ENCODING)
+ lang->encoding = encoding;
+ else
+ lang->encoding = CUPS_UTF8;
+
+ /*
+ * Return...
+ */
+
+ _cupsMutexUnlock(&lang_mutex);
+
+ return (lang);
+}
+
+
+/*
+ * '_cupsLangString()' - Get a message string.
+ *
+ * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
+ * convert the string to the language encoding.
+ */
+
+const char * /* O - Localized message */
+_cupsLangString(cups_lang_t *lang, /* I - Language */
+ const char *message) /* I - Message */
+{
+ const char *s; /* Localized message */
+
+ /*
+ * Range check input...
+ */
+
+ if (!lang || !message || !*message)
+ return (message);
+
+ _cupsMutexLock(&lang_mutex);
+
+ /*
+ * Load the message catalog if needed...
+ */
+
+ if (!lang->strings)
+ cups_message_load(lang);
+
+ s = _cupsMessageLookup(lang->strings, message);
+
+ _cupsMutexUnlock(&lang_mutex);
+
+ return (s);
+}
+
+
+/*
+ * '_cupsMessageFree()' - Free a messages array.
+ */
+
+void
+_cupsMessageFree(cups_array_t *a) /* I - Message array */
+{
+#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
+ /*
+ * Release the cups.strings dictionary as needed...
+ */
+
+ if (cupsArrayUserData(a))
+ CFRelease((CFDictionaryRef)cupsArrayUserData(a));
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
+
+ /*
+ * Free the array...
+ */
+
+ cupsArrayDelete(a);
+}
+
+
+/*
+ * '_cupsMessageLoad()' - Load a .po file into a messages array.
+ */
+
+cups_array_t * /* O - New message array */
+_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
+ int unquote) /* I - Unescape \foo in strings? */
+{
+ cups_file_t *fp; /* Message file */
+ cups_array_t *a; /* Message array */
+ _cups_message_t *m; /* Current message */
+ char s[4096], /* String buffer */
+ *ptr, /* Pointer into buffer */
+ *temp; /* New string */
+ int length; /* Length of combined strings */
+ size_t ptrlen; /* Length of string */
+
+
+ DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
+
+ /*
+ * Create an array to hold the messages...
+ */
+
+ if ((a = _cupsMessageNew(NULL)) == NULL)
+ {
+ DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
+ return (NULL);
+ }
+
+ /*
+ * Open the message catalog file...
+ */
+
+ if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ {
+ DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
+ strerror(errno)));
+ return (a);
+ }
+
+ /*
+ * Read messages from the catalog file until EOF...
+ *
+ * The format is the GNU gettext .po format, which is fairly simple:
+ *
+ * msgid "some text"
+ * msgstr "localized text"
+ *
+ * The ID and localized text can span multiple lines using the form:
+ *
+ * msgid ""
+ * "some long text"
+ * msgstr ""
+ * "localized text spanning "
+ * "multiple lines"
+ */
+
+ m = NULL;
+
+ while (cupsFileGets(fp, s, sizeof(s)) != NULL)
+ {
+ /*
+ * Skip blank and comment lines...
+ */
+
+ if (s[0] == '#' || !s[0])
+ continue;
+
+ /*
+ * Strip the trailing quote...
+ */
+
+ if ((ptr = strrchr(s, '\"')) == NULL)
+ continue;
+
+ *ptr = '\0';
+
+ /*
+ * Find start of value...
+ */
+
+ if ((ptr = strchr(s, '\"')) == NULL)
+ continue;
+
+ ptr ++;
+
+ /*
+ * Unquote the text...
+ */
+
+ if (unquote)
+ cups_unquote(ptr, ptr);
+
+ /*
+ * Create or add to a message...
+ */
+
+ if (!strncmp(s, "msgid", 5))
+ {
+ /*
+ * Add previous message as needed...
+ */
+
+ if (m)
+ {
+ if (m->str && m->str[0])
+ {
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ /*
+ * Translation is empty, don't add it... (STR #4033)
+ */
+
+ free(m->id);
+ if (m->str)
+ free(m->str);
+ free(m);
+ }
+ }
+
+ /*
+ * Create a new message with the given msgid string...
+ */
+
+ if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
+ {
+ cupsFileClose(fp);
+ return (a);
+ }
+
+ if ((m->id = strdup(ptr)) == NULL)
+ {
+ free(m);
+ cupsFileClose(fp);
+ return (a);
+ }
+ }
+ else if (s[0] == '\"' && m)
+ {
+ /*
+ * Append to current string...
+ */
+
+ length = (int)strlen(m->str ? m->str : m->id);
+ ptrlen = strlen(ptr);
+
+ if ((temp = realloc(m->str ? m->str : m->id,
+ length + ptrlen + 1)) == NULL)
+ {
+ if (m->str)
+ free(m->str);
+ free(m->id);
+ free(m);
+
+ cupsFileClose(fp);
+ return (a);
+ }
+
+ if (m->str)
+ {
+ /*
+ * Copy the new portion to the end of the msgstr string - safe
+ * to use memcpy because the buffer is allocated to the correct
+ * size...
+ */
+
+ m->str = temp;
+
+ memcpy(m->str + length, ptr, ptrlen + 1);
+ }
+ else
+ {
+ /*
+ * Copy the new portion to the end of the msgid string - safe
+ * to use memcpy because the buffer is allocated to the correct
+ * size...
+ */
+
+ m->id = temp;
+
+ memcpy(m->id + length, ptr, ptrlen + 1);
+ }
+ }
+ else if (!strncmp(s, "msgstr", 6) && m)
+ {
+ /*
+ * Set the string...
+ */
+
+ if ((m->str = strdup(ptr)) == NULL)
+ {
+ free(m->id);
+ free(m);
+
+ cupsFileClose(fp);
+ return (a);
+ }
+ }
+ }
+
+ /*
+ * Add the last message string to the array as needed...
+ */
+
+ if (m)
+ {
+ if (m->str && m->str[0])
+ {
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ /*
+ * Translation is empty, don't add it... (STR #4033)
+ */
+
+ free(m->id);
+ if (m->str)
+ free(m->str);
+ free(m);
+ }
+ }
+
+ /*
+ * Close the message catalog file and return the new array...
+ */
+
+ cupsFileClose(fp);
+
+ DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
+ cupsArrayCount(a)));
+
+ return (a);
+}
+
+
+/*
+ * '_cupsMessageLookup()' - Lookup a message string.
+ */
+
+const char * /* O - Localized message */
+_cupsMessageLookup(cups_array_t *a, /* I - Message array */
+ const char *m) /* I - Message */
+{
+ _cups_message_t key, /* Search key */
+ *match; /* Matching message */
+
+
+ /*
+ * Lookup the message string; if it doesn't exist in the catalog,
+ * then return the message that was passed to us...
+ */
+
+ key.id = (char *)m;
+ match = (_cups_message_t *)cupsArrayFind(a, &key);
+
+#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
+ if (!match && cupsArrayUserData(a))
+ {
+ /*
+ * Try looking the string up in the cups.strings dictionary...
+ */
+
+ CFDictionaryRef dict; /* cups.strings dictionary */
+ CFStringRef cfm, /* Message as a CF string */
+ cfstr; /* Localized text as a CF string */
+
+ dict = (CFDictionaryRef)cupsArrayUserData(a);
+ cfm = CFStringCreateWithCString(kCFAllocatorDefault, m,
+ kCFStringEncodingUTF8);
+ match = calloc(1, sizeof(_cups_message_t));
+ match->id = strdup(m);
+ cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL;
+
+ if (cfstr)
+ {
+ char buffer[1024]; /* Message buffer */
+
+ CFStringGetCString(cfstr, buffer, sizeof(buffer), kCFStringEncodingUTF8);
+ match->str = strdup(buffer);
+
+ DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
+ m, buffer));
+ }
+ else
+ {
+ match->str = strdup(m);
+
+ DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m));
+ }
+
+ cupsArrayAdd(a, match);
+
+ if (cfm)
+ CFRelease(cfm);
+ }
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
+
+ if (match && match->str)
+ return (match->str);
+ else
+ return (m);
+}
+
+
+/*
+ * '_cupsMessageNew()' - Make a new message catalog array.
+ */
+
+cups_array_t * /* O - Array */
+_cupsMessageNew(void *context) /* I - User data */
+{
+ return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
+ (cups_ahash_func_t)NULL, 0,
+ (cups_acopy_func_t)NULL,
+ (cups_afree_func_t)cups_message_free));
+}
+
+
+#ifdef __APPLE__
+/*
+ * 'appleLangDefault()' - Get the default locale string.
+ */
+
+static const char * /* O - Locale string */
+appleLangDefault(void)
+{
+ int i; /* Looping var */
+ CFBundleRef bundle; /* Main bundle (if any) */
+ CFArrayRef bundleList; /* List of localizations in bundle */
+ CFPropertyListRef localizationList;
+ /* List of localization data */
+ CFStringRef languageName; /* Current name */
+ CFStringRef localeName; /* Canonical from of name */
+ char *lang; /* LANG environment variable */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+
+ DEBUG_puts("2appleLangDefault()");
+
+ /*
+ * Only do the lookup and translation the first time.
+ */
+
+ if (!cg->language[0])
+ {
+ if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
+ {
+ DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang));
+ strlcpy(cg->language, lang, sizeof(cg->language));
+ return (cg->language);
+ }
+ else if ((bundle = CFBundleGetMainBundle()) != NULL &&
+ (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
+ {
+ DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
+
+ localizationList =
+ CFBundleCopyPreferredLocalizationsFromArray(bundleList);
+
+ CFRelease(bundleList);
+ }
+ else
+ {
+ DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
+
+ localizationList =
+ CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
+ kCFPreferencesCurrentApplication);
+ }
+
+ if (localizationList)
+ {
+
+#ifdef DEBUG
+ if (CFGetTypeID(localizationList) == CFArrayGetTypeID())
+ DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
+ (int)CFArrayGetCount(localizationList)));
+ else
+ DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
+#endif /* DEBUG */
+
+ if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
+ CFArrayGetCount(localizationList) > 0)
+ {
+ languageName = CFArrayGetValueAtIndex(localizationList, 0);
+
+ if (languageName &&
+ CFGetTypeID(languageName) == CFStringGetTypeID())
+ {
+ localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
+ kCFAllocatorDefault, languageName);
+
+ if (localeName)
+ {
+ CFStringGetCString(localeName, cg->language, sizeof(cg->language),
+ kCFStringEncodingASCII);
+ CFRelease(localeName);
+
+ DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
+ cg->language));
+
+ /*
+ * Map new language identifiers to locales...
+ */
+
+ for (i = 0;
+ i < (int)(sizeof(apple_language_locale) /
+ sizeof(apple_language_locale[0]));
+ i ++)
+ {
+ if (!strcmp(cg->language, apple_language_locale[i].language))
+ {
+ DEBUG_printf(("3appleLangDefault: mapping \"%s\" to \"%s\"...",
+ cg->language, apple_language_locale[i].locale));
+ strlcpy(cg->language, apple_language_locale[i].locale,
+ sizeof(cg->language));
+ break;
+ }
+ }
+
+ /*
+ * Convert language subtag into region subtag...
+ */
+
+ if (cg->language[2] == '-')
+ cg->language[2] = '_';
+
+ if (!strchr(cg->language, '.'))
+ strlcat(cg->language, ".UTF-8", sizeof(cg->language));
+ }
+ else
+ DEBUG_puts("3appleLangDefault: Unable to get localeName.");
+ }
+ }
+
+ CFRelease(localizationList);
+ }
+
+ /*
+ * If we didn't find the language, default to en_US...
+ */
+
+ if (!cg->language[0])
+ {
+ DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
+ strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
+ }
+ }
+
+ /*
+ * Return the cached locale...
+ */
+
+ return (cg->language);
+}
+
+
+# ifdef CUPS_BUNDLEDIR
+/*
+ * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
+ */
+
+static cups_array_t * /* O - Message catalog */
+appleMessageLoad(const char *locale) /* I - Locale ID */
+{
+ char filename[1024], /* Path to cups.strings file */
+ applelang[256]; /* Apple language ID */
+ CFURLRef url; /* URL to cups.strings file */
+ CFReadStreamRef stream = NULL; /* File stream */
+ CFPropertyListRef plist = NULL; /* Localization file */
+#ifdef DEBUG
+ CFErrorRef error = NULL; /* Error when opening file */
+#endif /* DEBUG */
+
+
+ DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale));
+
+ /*
+ * Load the cups.strings file...
+ */
+
+ snprintf(filename, sizeof(filename),
+ CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
+ _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
+ DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
+
+ if (access(filename, 0))
+ {
+ /*
+ * Try alternate lproj directory names...
+ */
+
+ if (!strncmp(locale, "en", 2))
+ locale = "English";
+ else if (!strncmp(locale, "nb", 2) || !strncmp(locale, "nl", 2))
+ locale = "Dutch";
+ else if (!strncmp(locale, "fr", 2))
+ locale = "French";
+ else if (!strncmp(locale, "de", 2))
+ locale = "German";
+ else if (!strncmp(locale, "it", 2))
+ locale = "Italian";
+ else if (!strncmp(locale, "ja", 2))
+ locale = "Japanese";
+ else if (!strncmp(locale, "es", 2))
+ locale = "Spanish";
+
+ snprintf(filename, sizeof(filename),
+ CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
+ DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename));
+ }
+
+ url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8 *)filename,
+ strlen(filename), false);
+ if (url)
+ {
+ stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ if (stream)
+ {
+ /*
+ * Read the property list containing the localization data.
+ *
+ * NOTE: This code currently generates a clang "potential leak"
+ * warning, but the object is released in _cupsMessageFree().
+ */
+
+ CFReadStreamOpen(stream);
+
+#ifdef DEBUG
+ plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
+ kCFPropertyListImmutable, NULL,
+ &error);
+ if (error)
+ {
+ CFStringRef msg = CFErrorCopyDescription(error);
+ /* Error message */
+
+ CFStringGetCString(msg, filename, sizeof(filename),
+ kCFStringEncodingUTF8);
+ DEBUG_printf(("1appleMessageLoad: %s", filename));
+
+ CFRelease(msg);
+ CFRelease(error);
+ }
+
+#else
+ plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
+ kCFPropertyListImmutable, NULL,
+ NULL);
+#endif /* DEBUG */
+
+ if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID())
+ {
+ CFRelease(plist);
+ plist = NULL;
+ }
+
+ CFRelease(stream);
+ }
+
+ CFRelease(url);
+ }
+
+ DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url, stream,
+ plist));
+
+ /*
+ * Create and return an empty array to act as a cache for messages, passing the
+ * plist as the user data.
+ */
+
+ return (_cupsMessageNew((void *)plist));
+}
+# endif /* CUPS_BUNDLEDIR */
+#endif /* __APPLE__ */
+
+
+/*
+ * 'cups_cache_lookup()' - Lookup a language in the cache...
+ */
+
+static cups_lang_t * /* O - Language data or NULL */
+cups_cache_lookup(
+ const char *name, /* I - Name of locale */
+ cups_encoding_t encoding) /* I - Encoding of locale */
+{
+ cups_lang_t *lang; /* Current language */
+
+
+ DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
+ encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
+ lang_encodings[encoding]));
+
+ /*
+ * Loop through the cache and return a match if found...
+ */
+
+ for (lang = lang_cache; lang != NULL; lang = lang->next)
+ {
+ DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
+ "encoding=%d(%s)", lang, lang->language, lang->encoding,
+ lang_encodings[lang->encoding]));
+
+ if (!strcmp(lang->language, name) &&
+ (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
+ {
+ lang->used ++;
+
+ DEBUG_puts("8cups_cache_lookup: returning match!");
+
+ return (lang);
+ }
+ }
+
+ DEBUG_puts("8cups_cache_lookup: returning NULL!");
+
+ return (NULL);
+}
+
+
+/*
+ * 'cups_message_compare()' - Compare two messages.
+ */
+
+static int /* O - Result of comparison */
+cups_message_compare(
+ _cups_message_t *m1, /* I - First message */
+ _cups_message_t *m2) /* I - Second message */
+{
+ return (strcmp(m1->id, m2->id));
+}
+
+
+/*
+ * 'cups_message_free()' - Free a message.
+ */
+
+static void
+cups_message_free(_cups_message_t *m) /* I - Message */
+{
+ if (m->id)
+ free(m->id);
+
+ if (m->str)
+ free(m->str);
+
+ free(m);
+}
+
+
+/*
+ * 'cups_message_load()' - Load the message catalog for a language.
+ */
+
+static void
+cups_message_load(cups_lang_t *lang) /* I - Language */
+{
+#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
+ lang->strings = appleMessageLoad(lang->language);
+
+#else
+ char filename[1024]; /* Filename for language locale file */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+
+ snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
+ lang->language, lang->language);
+
+ if (strchr(lang->language, '_') && access(filename, 0))
+ {
+ /*
+ * Country localization not available, look for generic localization...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", cg->localedir,
+ lang->language, lang->language);
+
+ if (access(filename, 0))
+ {
+ /*
+ * No generic localization, so use POSIX...
+ */
+
+ DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename,
+ strerror(errno)));
+
+ snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
+ }
+ }
+
+ /*
+ * Read the strings from the file...
+ */
+
+ lang->strings = _cupsMessageLoad(filename, 1);
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
+}
+
+
+/*
+ * 'cups_unquote()' - Unquote characters in strings...
+ */
+
+static void
+cups_unquote(char *d, /* O - Unquoted string */
+ const char *s) /* I - Original string */
+{
+ while (*s)
+ {
+ if (*s == '\\')
+ {
+ s ++;
+ if (isdigit(*s))
+ {
+ *d = 0;
+
+ while (isdigit(*s))
+ {
+ *d = *d * 8 + *s - '0';
+ s ++;
+ }
+
+ d ++;
+ }
+ else
+ {
+ if (*s == 'n')
+ *d ++ = '\n';
+ else if (*s == 'r')
+ *d ++ = '\r';
+ else if (*s == 't')
+ *d ++ = '\t';
+ else
+ *d++ = *s;
+
+ s ++;
+ }
+ }
+ else
+ *d++ = *s++;
+ }
+
+ *d = '\0';
+}
+
+
+/*
+ * End of "$Id: language.c 11424 2013-11-08 19:51:01Z msweet $".
+ */
diff --git a/cups/libs/cups/language.h b/cups/libs/cups/language.h
new file mode 100644
index 000000000..4fcf9e84f
--- /dev/null
+++ b/cups/libs/cups/language.h
@@ -0,0 +1,115 @@
+/*
+ * "$Id: language.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Multi-language support for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_LANGUAGE_H_
+# define _CUPS_LANGUAGE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <locale.h>
+# include "array.h"
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Types...
+ */
+
+typedef enum cups_encoding_e /**** Language Encodings ****/
+{
+ CUPS_AUTO_ENCODING = -1, /* Auto-detect the encoding @private@ */
+ CUPS_US_ASCII, /* US ASCII */
+ CUPS_ISO8859_1, /* ISO-8859-1 */
+ CUPS_ISO8859_2, /* ISO-8859-2 */
+ CUPS_ISO8859_3, /* ISO-8859-3 */
+ CUPS_ISO8859_4, /* ISO-8859-4 */
+ CUPS_ISO8859_5, /* ISO-8859-5 */
+ CUPS_ISO8859_6, /* ISO-8859-6 */
+ CUPS_ISO8859_7, /* ISO-8859-7 */
+ CUPS_ISO8859_8, /* ISO-8859-8 */
+ CUPS_ISO8859_9, /* ISO-8859-9 */
+ CUPS_ISO8859_10, /* ISO-8859-10 */
+ CUPS_UTF8, /* UTF-8 */
+ CUPS_ISO8859_13, /* ISO-8859-13 */
+ CUPS_ISO8859_14, /* ISO-8859-14 */
+ CUPS_ISO8859_15, /* ISO-8859-15 */
+ CUPS_WINDOWS_874, /* CP-874 */
+ CUPS_WINDOWS_1250, /* CP-1250 */
+ CUPS_WINDOWS_1251, /* CP-1251 */
+ CUPS_WINDOWS_1252, /* CP-1252 */
+ CUPS_WINDOWS_1253, /* CP-1253 */
+ CUPS_WINDOWS_1254, /* CP-1254 */
+ CUPS_WINDOWS_1255, /* CP-1255 */
+ CUPS_WINDOWS_1256, /* CP-1256 */
+ CUPS_WINDOWS_1257, /* CP-1257 */
+ CUPS_WINDOWS_1258, /* CP-1258 */
+ CUPS_KOI8_R, /* KOI-8-R */
+ CUPS_KOI8_U, /* KOI-8-U */
+ CUPS_ISO8859_11, /* ISO-8859-11 */
+ CUPS_ISO8859_16, /* ISO-8859-16 */
+ CUPS_MAC_ROMAN, /* MacRoman */
+ CUPS_ENCODING_SBCS_END = 63, /* End of single-byte encodings @private@ */
+
+ CUPS_WINDOWS_932, /* Japanese JIS X0208-1990 */
+ CUPS_WINDOWS_936, /* Simplified Chinese GB 2312-80 */
+ CUPS_WINDOWS_949, /* Korean KS C5601-1992 */
+ CUPS_WINDOWS_950, /* Traditional Chinese Big Five */
+ CUPS_WINDOWS_1361, /* Korean Johab */
+ CUPS_ENCODING_DBCS_END = 127, /* End of double-byte encodings @private@ */
+
+ CUPS_EUC_CN, /* EUC Simplified Chinese */
+ CUPS_EUC_JP, /* EUC Japanese */
+ CUPS_EUC_KR, /* EUC Korean */
+ CUPS_EUC_TW, /* EUC Traditional Chinese */
+ CUPS_JIS_X0213, /* JIS X0213 aka Shift JIS */
+ CUPS_ENCODING_VBCS_END = 191 /* End of variable-length encodings @private@ */
+} cups_encoding_t;
+
+typedef struct cups_lang_s /**** Language Cache Structure ****/
+{
+ struct cups_lang_s *next; /* Next language in cache */
+ int used; /* Number of times this entry has been used. */
+ cups_encoding_t encoding; /* Text encoding */
+ char language[16]; /* Language/locale name */
+ cups_array_t *strings; /* Message strings @private@ */
+} cups_lang_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern cups_lang_t *cupsLangDefault(void);
+extern const char *cupsLangEncoding(cups_lang_t *lang);
+extern void cupsLangFlush(void);
+extern void cupsLangFree(cups_lang_t *lang);
+extern cups_lang_t *cupsLangGet(const char *language);
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_LANGUAGE_H_ */
+
+/*
+ * End of "$Id: language.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/libcups2.def b/cups/libs/cups/libcups2.def
new file mode 100644
index 000000000..a5124d421
--- /dev/null
+++ b/cups/libs/cups/libcups2.def
@@ -0,0 +1,411 @@
+LIBRARY libcups2
+VERSION 2.10
+EXPORTS
+_cupsBufferGet
+_cupsBufferRelease
+_cupsGet1284Values
+_cupsGetDests
+_cupsGetPassword
+_cupsGlobals
+_cupsLangPrintError
+_cupsLangPrintf
+_cupsLangPuts
+_cupsLangString
+_cupsMD5Append
+_cupsMD5Finish
+_cupsMD5Init
+_cupsMessageFree
+_cupsMessageLoad
+_cupsMessageLookup
+_cupsMutexLock
+_cupsMutexUnlock
+_cupsNextDelay
+_cupsSetError
+_cupsSetLocale
+_cupsStrAlloc
+_cupsStrDate
+_cupsStrFlush
+_cupsStrFormatd
+_cupsStrFree
+_cupsStrRetain
+_cupsStrScand
+_cupsStrStatistics
+_cups_strcasecmp
+_cups_strncasecmp
+_cups_strcpy
+_cups_strlcat
+_cups_strlcpy
+_httpAddrSetPort
+_httpAssembleUUID
+_httpEncodeURI
+_httpResolveURI
+_httpWait
+_ippFindOption
+_ppdCacheCreateWithFile
+_ppdCacheCreateWithPPD
+_ppdCacheDestroy
+_ppdCacheGetBin
+_ppdCacheGetInputSlot
+_ppdCacheGetMediaType
+_ppdCacheGetOutputBin
+_ppdCacheGetPageSize
+_ppdCacheGetSize
+_ppdCacheGetSource
+_ppdCacheGetType
+_ppdCacheWriteFile
+_ppdFreeLanguages
+_ppdGetEncoding
+_ppdGetLanguages
+_ppdHashName
+_ppdLocalizedAttr
+_ppdNormalizeMakeAndModel
+_ppdOpen
+_ppdOpenFile
+_ppdParseOptions
+_pwgMediaTable
+cupsAddDest
+cupsAddOption
+cupsAdminCreateWindowsPPD
+cupsAdminExportSamba
+cupsArrayAdd
+cupsArrayClear
+cupsArrayCount
+cupsArrayCurrent
+cupsArrayDelete
+cupsArrayDup
+cupsArrayFind
+cupsArrayFirst
+cupsArrayGetIndex
+cupsArrayGetInsert
+cupsArrayIndex
+cupsArrayInsert
+cupsArrayLast
+cupsArrayNew
+cupsArrayNew2
+cupsArrayNew3
+cupsArrayNext
+cupsArrayPrev
+cupsArrayRemove
+cupsArrayRestore
+cupsArraySave
+cupsArrayUserData
+cupsCancelJob
+cupsCharsetToUTF8
+cupsDirClose
+cupsDirOpen
+cupsDirRead
+cupsDirRewind
+cupsDoAuthentication
+cupsDoFileRequest
+cupsDoIORequest
+cupsDoRequest
+cupsEncodeOptions
+cupsEncodeOptions2
+cupsEncryption
+cupsFileClose
+cupsFileCompression
+cupsFileEOF
+cupsFileFind
+cupsFileFlush
+cupsFileGetChar
+cupsFileGetConf
+cupsFileGetLine
+cupsFileGets
+cupsFileLock
+cupsFileNumber
+cupsFileOpen
+cupsFileOpenFd
+cupsFilePeekChar
+cupsFilePrintf
+cupsFilePutChar
+cupsFilePuts
+cupsFileRead
+cupsFileRewind
+cupsFileSeek
+cupsFileStderr
+cupsFileStdin
+cupsFileStdout
+cupsFileTell
+cupsFileUnlock
+cupsFileWrite
+cupsFindDestDefault
+cupsFindDestReady
+cupsFindDestSupported
+cupsFreeDests
+cupsFreeJobs
+cupsFreeOptions
+cupsGetClasses
+cupsGetDefault
+cupsGetDefault2
+cupsGetDest
+cupsGetDestMediaByIndex
+cupsGetDestMediaCount
+cupsGetDestMediaDefault
+cupsGetDests
+cupsGetDests2
+cupsGetFd
+cupsGetFile
+cupsGetJobs
+cupsGetJobs2
+cupsGetOption
+cupsGetPPD
+cupsGetPPD2
+cupsGetPassword
+cupsGetPrinters
+cupsGetResponse
+cupsLangDefault
+cupsLangEncoding
+cupsLangFlush
+cupsLangFree
+cupsLangGet
+cupsLastError
+cupsLastErrorString
+cupsMarkOptions
+cupsNotifySubject
+cupsNotifyText
+cupsParseOptions
+cupsPrintFile
+cupsPrintFile2
+cupsPrintFiles
+cupsPrintFiles2
+cupsPutFd
+cupsPutFile
+cupsRemoveOption
+cupsResolveConflicts
+cupsSendRequest
+cupsServer
+cupsSetClientCertCB
+cupsSetCredentials
+cupsSetDests
+cupsSetDests2
+cupsSetEncryption
+cupsSetPasswordCB
+cupsSetServer
+cupsSetServerCertCB
+cupsSetUser
+cupsSetUserAgent
+cupsTempFd
+cupsTempFile
+cupsTempFile2
+cupsUserAgent
+cupsUTF32ToUTF8
+cupsUTF8ToCharset
+cupsUTF8ToUTF32
+cupsUser
+cupsWriteRequestData
+httpAcceptConnection
+httpAddCredential
+httpAddrAny
+httpAddrConnect
+httpAddrCopyList
+httpAddrEqual
+httpAddrFreeList
+httpAddrGetList
+httpAddrLength
+httpAddrListen
+httpAddrLocalhost
+httpAddrLookup
+httpAddrPort
+httpAddrString
+httpAssembleURI
+httpAssembleURIf
+httpAssembleUUID
+httpBlocking
+httpCheck
+httpClearCookie
+httpClearFields
+httpClose
+httpConnect
+httpConnect2
+httpConnectEncrypt
+httpCopyCredentials
+httpDecode64
+httpDecode64_2
+httpDelete
+httpEncode64
+httpEncode64_2
+httpEncryption
+httpError
+httpFlush
+httpFlushWrite
+httpFreeCredentials
+httpGet
+httpGetBlocking
+httpGetContentEncoding
+httpGetCookie
+httpGetDateString
+httpGetDateString2
+httpGetDateTime
+httpGetExpect
+httpGetFd
+httpGetField
+httpGetHostByName
+httpGetHostname
+httpGetLength
+httpGetLength2
+httpGetStatus
+httpGetSubField
+httpGetSubField2
+httpGets
+httpHead
+httpInitialize
+httpMD5
+httpMD5Final
+httpMD5String
+httpOptions
+httpPeek
+httpPost
+httpPrintf
+httpPut
+httpRead
+httpRead2
+httpReadRequest
+httpReconnect
+httpSeparate
+httpSeparate2
+httpSeparateURI
+httpSetCookie
+httpSetCredentials
+httpSetDefaultField
+httpSetExpect
+httpSetField
+httpSetLength
+httpSetTimeout
+httpStatus
+httpTrace
+httpUpdate
+httpWait
+httpWrite
+httpWrite2
+httpWriteResponse
+ippAddBoolean
+ippAddBooleans
+ippAddCollection
+ippAddCollections
+ippAddDate
+ippAddInteger
+ippAddIntegers
+ippAddOctetString
+ippAddOutOfBand
+ippAddRange
+ippAddRanges
+ippAddResolution
+ippAddResolutions
+ippAddSeparator
+ippAddString
+ippAddStringf
+ippAddStringfv
+ippAddStrings
+ippAttributeString
+ippContainsInteger
+ippContainsString
+ippCopyAttribute
+ippCopyAttributes
+ippCreateRequestedArray
+ippDateToTime
+ippDelete
+ippDeleteAttribute
+ippDeleteValues
+ippEnumString
+ippEnumValue
+ippErrorString
+ippErrorValue
+ippFindAttribute
+ippFindNextAttribute
+ippFirstAttribute
+ippGetBoolean
+ippGetCollection
+ippGetCount
+ippGetDate
+ippGetGroupTag
+ippGetInteger
+ippGetName
+ippGetOctetString
+ippGetOperation
+ippGetRange
+ippGetRequestId
+ippGetResolution
+ippGetState
+ippGetStatusCode
+ippGetString
+ippGetValueTag
+ippGetVersion
+ippLength
+ippNew
+ippNewRequest
+ippNewResponse
+ippNextAttribute
+ippOpString
+ippOpValue
+ippPort
+ippRead
+ippReadFile
+ippReadIO
+ippSetPort
+ippSetBoolean
+ippSetCollection
+ippSetDate
+ippSetGroupTag
+ippSetInteger
+ippSetName
+ippSetOctetString
+ippSetOperation
+ippSetRange
+ippSetRequestId
+ippSetResolution
+ippSetState
+ippSetStatusCode
+ippSetString
+ippSetStringf
+ippSetStringfv
+ippSetValueTag
+ippSetVersion
+ippTagString
+ippTagValue
+ippTimeToDate
+ippValidateAttribute
+ippValidateAttributes
+ippWrite
+ippWriteFile
+ippWriteIO
+ppdClose
+ppdCollect
+ppdCollect2
+ppdConflicts
+ppdEmit
+ppdEmitAfterOrder
+ppdEmitFd
+ppdEmitJCL
+ppdEmitJCLEnd
+ppdEmitString
+ppdErrorString
+ppdFindAttr
+ppdFindChoice
+ppdFindCustomOption
+ppdFindCustomParam
+ppdFindMarkedChoice
+ppdFindNextAttr
+ppdFindOption
+ppdFirstCustomParam
+ppdFirstOption
+ppdIsMarked
+ppdLastError
+ppdLocalize
+ppdMarkDefaults
+ppdMarkOption
+ppdNextCustomParam
+ppdNextOption
+ppdOpen
+ppdOpen2
+ppdOpenFd
+ppdOpenFile
+ppdPageLength
+ppdPageSize
+ppdPageWidth
+ppdSetConformance
+pwgFormatSizeName
+pwgInitSize
+pwgMediaForLegacy
+pwgMediaForPPD
+pwgMediaForPWG
+pwgMediaForSize
diff --git a/cups/libs/cups/libcups2.rc b/cups/libs/cups/libcups2.rc
new file mode 100644
index 000000000..bac3b171d
--- /dev/null
+++ b/cups/libs/cups/libcups2.rc
@@ -0,0 +1,75 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "WinVersRes.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MASTER_PROD_VERS
+ PRODUCTVERSION MASTER_PROD_VERS
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", MASTER_COMPANY_NAME
+ VALUE "FileDescription", "CUPS Library"
+ VALUE "FileVersion", MASTER_PROD_VERS_STR
+ VALUE "InternalName", "libcups2.dll"
+ VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT
+ VALUE "OriginalFilename", "libcups2.dll"
+ VALUE "ProductName", MASTER_PROD_NAME
+ VALUE "ProductVersion", MASTER_PROD_VERS_STR
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/cups/libs/cups/libcups_s.exp b/cups/libs/cups/libcups_s.exp
new file mode 100644
index 000000000..b8b22072d
--- /dev/null
+++ b/cups/libs/cups/libcups_s.exp
@@ -0,0 +1,85 @@
+_cups_debug_fd
+_cupsBufferGet
+_cupsBufferRelease
+_cupsGet1284Values
+_cupsGetDests
+_cupsGetPassword
+_cupsGlobals
+_cupsLangPrintError
+_cupsLangPrintf
+_cupsLangPuts
+_cupsLangString
+_cupsMD5Append
+_cupsMD5Finish
+_cupsMD5Init
+_cupsMessageFree
+_cupsMessageLoad
+_cupsMessageLookup
+_cupsNextDelay
+_cupsSetError
+_cupsSetLocale
+_cupsSNMPClose
+_cupsSNMPCopyOID
+_cupsSNMPDefaultCommunity
+_cupsSNMPIsOID
+_cupsSNMPIsOIDPrefixed
+_cupsSNMPOIDToString
+_cupsSNMPOpen
+_cupsSNMPRead
+_cupsSNMPSetDebug
+_cupsSNMPStringToOID
+_cupsSNMPWalk
+_cupsSNMPWrite
+_cupsStrAlloc
+_cupsStrFlush
+_cupsStrFormatd
+_cupsStrFree
+_cupsStrRetain
+_cupsStrScand
+_cupsStrStatistics
+_cups_getifaddrs
+_cups_freeifaddrs
+_cups_strcpy
+_cups_strlcat
+_cups_strlcpy
+_httpAddrPort
+_httpAddrSetPort
+_httpAssembleUUID
+_httpBIOMethods
+_httpCreate
+_httpEncodeURI
+_httpPeek
+_httpResolveURI
+_httpSetTimeout
+_httpWait
+_ippFindOption
+_ppdFreeLanguages
+_ppdGetEncoding
+_ppdGetLanguages
+_ppdHashName
+_ppdLocalizedAttr
+_ppdNormalizeMakeAndModel
+_ppdOpen
+_ppdOpenFile
+_ppdParseOptions
+_pwgCreateWithFile
+_pwgDestroy
+_pwgWriteFile
+_pwgGenerateSize
+_pwgInitSize
+_pwgMediaForLegacy
+_pwgMediaForPPD
+_pwgMediaForPWG
+_pwgMediaForSize
+_pwgCreateWithPPD
+_pwgGetBin
+_pwgGetInputSlot
+_pwgGetMediaType
+_pwgGetOutputBin
+_pwgGetPageSize
+_pwgGetSize
+_pwgGetSource
+_pwgGetType
+_pwgInputSlotForSource
+_pwgMediaTypeForType
+_pwgPageSizeForMedia
diff --git a/cups/libs/cups/localize.c b/cups/libs/cups/localize.c
new file mode 100644
index 000000000..5a9b55f95
--- /dev/null
+++ b/cups/libs/cups/localize.c
@@ -0,0 +1,779 @@
+/*
+ * "$Id: localize.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * PPD localization routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdLocalize() - Localize the PPD file to the current locale.
+ * ppdLocalizeAttr() - Localize an attribute.
+ * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
+ * attribute.
+ * ppdLocalizeMarkerName() - Get the localized version of a marker-names
+ * attribute value.
+ * _ppdFreeLanguages() - Free an array of languages from _ppdGetLanguages.
+ * _ppdGetLanguages() - Get an array of languages from a PPD file.
+ * _ppdHashName() - Generate a hash value for a device or profile
+ * name.
+ * _ppdLocalizedAttr() - Find a localized attribute.
+ * ppd_ll_CC() - Get the current locale names.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "cups-private.h"
+#include "ppd-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+static cups_lang_t *ppd_ll_CC(char *ll_CC, int ll_CC_size);
+
+
+/*
+ * 'ppdLocalize()' - Localize the PPD file to the current locale.
+ *
+ * All groups, options, and choices are localized, as are ICC profile
+ * descriptions, printer presets, and custom option parameters. Each
+ * localized string uses the UTF-8 character encoding.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+ppdLocalize(ppd_file_t *ppd) /* I - PPD file */
+{
+ int i, j, k; /* Looping vars */
+ ppd_group_t *group; /* Current group */
+ ppd_option_t *option; /* Current option */
+ ppd_choice_t *choice; /* Current choice */
+ ppd_coption_t *coption; /* Current custom option */
+ ppd_cparam_t *cparam; /* Current custom parameter */
+ ppd_attr_t *attr, /* Current attribute */
+ *locattr; /* Localized attribute */
+ char ckeyword[PPD_MAX_NAME], /* Custom keyword */
+ ll_CC[6]; /* Language + country locale */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("ppdLocalize(ppd=%p)", ppd));
+
+ if (!ppd)
+ return (-1);
+
+ /*
+ * Get the default language...
+ */
+
+ ppd_ll_CC(ll_CC, sizeof(ll_CC));
+
+ /*
+ * Now lookup all of the groups, options, choices, etc.
+ */
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ {
+ if ((locattr = _ppdLocalizedAttr(ppd, "Translation", group->name,
+ ll_CC)) != NULL)
+ strlcpy(group->text, locattr->text, sizeof(group->text));
+
+ for (j = group->num_options, option = group->options; j > 0; j --, option ++)
+ {
+ if ((locattr = _ppdLocalizedAttr(ppd, "Translation", option->keyword,
+ ll_CC)) != NULL)
+ strlcpy(option->text, locattr->text, sizeof(option->text));
+
+ for (k = option->num_choices, choice = option->choices;
+ k > 0;
+ k --, choice ++)
+ {
+ if (strcmp(choice->choice, "Custom") ||
+ !ppdFindCustomOption(ppd, option->keyword))
+ locattr = _ppdLocalizedAttr(ppd, option->keyword, choice->choice,
+ ll_CC);
+ else
+ {
+ snprintf(ckeyword, sizeof(ckeyword), "Custom%s", option->keyword);
+
+ locattr = _ppdLocalizedAttr(ppd, ckeyword, "True", ll_CC);
+ }
+
+ if (locattr)
+ strlcpy(choice->text, locattr->text, sizeof(choice->text));
+ }
+ }
+ }
+
+ /*
+ * Translate any custom parameters...
+ */
+
+ for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
+ coption;
+ coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
+ {
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ snprintf(ckeyword, sizeof(ckeyword), "ParamCustom%s", coption->keyword);
+
+ if ((locattr = _ppdLocalizedAttr(ppd, ckeyword, cparam->name,
+ ll_CC)) != NULL)
+ strlcpy(cparam->text, locattr->text, sizeof(cparam->text));
+ }
+ }
+
+ /*
+ * Translate ICC profile names...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "APCustomColorMatchingName", NULL)) != NULL)
+ {
+ if ((locattr = _ppdLocalizedAttr(ppd, "APCustomColorMatchingName",
+ attr->spec, ll_CC)) != NULL)
+ strlcpy(attr->text, locattr->text, sizeof(attr->text));
+ }
+
+ for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+ {
+ cupsArraySave(ppd->sorted_attrs);
+
+ if ((locattr = _ppdLocalizedAttr(ppd, "cupsICCProfile", attr->spec,
+ ll_CC)) != NULL)
+ strlcpy(attr->text, locattr->text, sizeof(attr->text));
+
+ cupsArrayRestore(ppd->sorted_attrs);
+ }
+
+ /*
+ * Translate printer presets...
+ */
+
+ for (attr = ppdFindAttr(ppd, "APPrinterPreset", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL))
+ {
+ cupsArraySave(ppd->sorted_attrs);
+
+ if ((locattr = _ppdLocalizedAttr(ppd, "APPrinterPreset", attr->spec,
+ ll_CC)) != NULL)
+ strlcpy(attr->text, locattr->text, sizeof(attr->text));
+
+ cupsArrayRestore(ppd->sorted_attrs);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ppdLocalizeAttr()' - Localize an attribute.
+ *
+ * This function uses the current locale to find the localized attribute for
+ * the given main and option keywords. If no localized version of the
+ * attribute exists for the current locale, the unlocalized version is returned.
+ */
+
+ppd_attr_t * /* O - Localized attribute or @code NULL@ if none exists */
+ppdLocalizeAttr(ppd_file_t *ppd, /* I - PPD file */
+ const char *keyword, /* I - Main keyword */
+ const char *spec) /* I - Option keyword or @code NULL@ for none */
+{
+ ppd_attr_t *locattr; /* Localized attribute */
+ char ll_CC[6]; /* Language + country locale */
+
+
+ /*
+ * Get the default language...
+ */
+
+ ppd_ll_CC(ll_CC, sizeof(ll_CC));
+
+ /*
+ * Find the localized attribute...
+ */
+
+ if (spec)
+ locattr = _ppdLocalizedAttr(ppd, keyword, spec, ll_CC);
+ else
+ locattr = _ppdLocalizedAttr(ppd, "Translation", keyword, ll_CC);
+
+ if (!locattr)
+ locattr = ppdFindAttr(ppd, keyword, spec);
+
+ return (locattr);
+}
+
+
+/*
+ * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
+ * attribute.
+ *
+ * This function uses the current locale to find the corresponding reason
+ * text or URI from the attribute value. If "scheme" is NULL or "text",
+ * the returned value contains human-readable (UTF-8) text from the translation
+ * string or attribute value. Otherwise the corresponding URI is returned.
+ *
+ * If no value of the requested scheme can be found, NULL is returned.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+const char * /* O - Value or NULL if not found */
+ppdLocalizeIPPReason(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *reason, /* I - IPP reason keyword to look up */
+ const char *scheme, /* I - URI scheme or NULL for text */
+ char *buffer, /* I - Value buffer */
+ size_t bufsize) /* I - Size of value buffer */
+{
+ cups_lang_t *lang; /* Current language */
+ ppd_attr_t *locattr; /* Localized attribute */
+ char ll_CC[6], /* Language + country locale */
+ *bufptr, /* Pointer into buffer */
+ *bufend, /* Pointer to end of buffer */
+ *valptr; /* Pointer into value */
+ int ch, /* Hex-encoded character */
+ schemelen; /* Length of scheme name */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (buffer)
+ *buffer = '\0';
+
+ if (!ppd || !reason || (scheme && !*scheme) ||
+ !buffer || bufsize < PPD_MAX_TEXT)
+ return (NULL);
+
+ /*
+ * Get the default language...
+ */
+
+ lang = ppd_ll_CC(ll_CC, sizeof(ll_CC));
+
+ /*
+ * Find the localized attribute...
+ */
+
+ if ((locattr = _ppdLocalizedAttr(ppd, "cupsIPPReason", reason,
+ ll_CC)) == NULL)
+ locattr = ppdFindAttr(ppd, "cupsIPPReason", reason);
+
+ if (!locattr)
+ {
+ if (lang && (!scheme || !strcmp(scheme, "text")))
+ {
+ /*
+ * Try to localize a standard printer-state-reason keyword...
+ */
+
+ const char *message = NULL; /* Localized message */
+
+ if (!strncmp(reason, "media-needed", 12))
+ message = _("The paper tray needs to be filled.");
+ else if (!strncmp(reason, "media-jam", 9))
+ message = _("There is a paper jam.");
+ else if (!strncmp(reason, "offline", 7) ||
+ !strncmp(reason, "shutdown", 8))
+ message = _("The printer is not connected.");
+ else if (!strncmp(reason, "toner-low", 9))
+ message = _("The printer is running low on toner.");
+ else if (!strncmp(reason, "toner-empty", 11))
+ message = _("The printer may be out of toner.");
+ else if (!strncmp(reason, "cover-open", 10))
+ message = _("The printer's cover is open.");
+ else if (!strncmp(reason, "interlock-open", 14))
+ message = _("The printer's interlock is open.");
+ else if (!strncmp(reason, "door-open", 9))
+ message = _("The printer's door is open.");
+ else if (!strncmp(reason, "input-tray-missing", 18))
+ message = _("The paper tray is missing.");
+ else if (!strncmp(reason, "media-low", 9))
+ message = _("The paper tray is almost empty.");
+ else if (!strncmp(reason, "media-empty", 11))
+ message = _("The paper tray is empty.");
+ else if (!strncmp(reason, "output-tray-missing", 19))
+ message = _("The output bin is missing.");
+ else if (!strncmp(reason, "output-area-almost-full", 23))
+ message = _("The output bin is almost full.");
+ else if (!strncmp(reason, "output-area-full", 16))
+ message = _("The output bin is full.");
+ else if (!strncmp(reason, "marker-supply-low", 17))
+ message = _("The printer is running low on ink.");
+ else if (!strncmp(reason, "marker-supply-empty", 19))
+ message = _("The printer may be out of ink.");
+ else if (!strncmp(reason, "marker-waste-almost-full", 24))
+ message = _("The printer's waste bin is almost full.");
+ else if (!strncmp(reason, "marker-waste-full", 17))
+ message = _("The printer's waste bin is full.");
+ else if (!strncmp(reason, "fuser-over-temp", 15))
+ message = _("The fuser's temperature is high.");
+ else if (!strncmp(reason, "fuser-under-temp", 16))
+ message = _("The fuser's temperature is low.");
+ else if (!strncmp(reason, "opc-near-eol", 12))
+ message = _("The optical photoconductor will need to be replaced soon.");
+ else if (!strncmp(reason, "opc-life-over", 13))
+ message = _("The optical photoconductor needs to be replaced.");
+ else if (!strncmp(reason, "developer-low", 13))
+ message = _("The developer unit will need to be replaced soon.");
+ else if (!strncmp(reason, "developer-empty", 15))
+ message = _("The developer unit needs to be replaced.");
+
+ if (message)
+ {
+ strlcpy(buffer, _cupsLangString(lang, message), bufsize);
+ return (buffer);
+ }
+ }
+
+ return (NULL);
+ }
+
+ /*
+ * Now find the value we need...
+ */
+
+ bufend = buffer + bufsize - 1;
+
+ if (!scheme || !strcmp(scheme, "text"))
+ {
+ /*
+ * Copy a text value (either the translation text or text:... URIs from
+ * the value...
+ */
+
+ strlcpy(buffer, locattr->text, bufsize);
+
+ for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
+ {
+ if (!strncmp(valptr, "text:", 5))
+ {
+ /*
+ * Decode text: URI and add to the buffer...
+ */
+
+ valptr += 5;
+
+ while (*valptr && !_cups_isspace(*valptr) && bufptr < bufend)
+ {
+ if (*valptr == '%' && isxdigit(valptr[1] & 255) &&
+ isxdigit(valptr[2] & 255))
+ {
+ /*
+ * Pull a hex-encoded character from the URI...
+ */
+
+ valptr ++;
+
+ if (isdigit(*valptr & 255))
+ ch = (*valptr - '0') << 4;
+ else
+ ch = (tolower(*valptr) - 'a' + 10) << 4;
+ valptr ++;
+
+ if (isdigit(*valptr & 255))
+ *bufptr++ = ch | (*valptr - '0');
+ else
+ *bufptr++ = ch | (tolower(*valptr) - 'a' + 10);
+ valptr ++;
+ }
+ else if (*valptr == '+')
+ {
+ *bufptr++ = ' ';
+ valptr ++;
+ }
+ else
+ *bufptr++ = *valptr++;
+ }
+ }
+ else
+ {
+ /*
+ * Skip this URI...
+ */
+
+ while (*valptr && !_cups_isspace(*valptr))
+ valptr++;
+ }
+
+ /*
+ * Skip whitespace...
+ */
+
+ while (_cups_isspace(*valptr))
+ valptr ++;
+ }
+
+ if (bufptr > buffer)
+ *bufptr = '\0';
+
+ return (buffer);
+ }
+ else
+ {
+ /*
+ * Copy a URI...
+ */
+
+ schemelen = strlen(scheme);
+ if (scheme[schemelen - 1] == ':') /* Force scheme to be just the name */
+ schemelen --;
+
+ for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
+ {
+ if ((!strncmp(valptr, scheme, schemelen) && valptr[schemelen] == ':') ||
+ (*valptr == '/' && !strcmp(scheme, "file")))
+ {
+ /*
+ * Copy URI...
+ */
+
+ while (*valptr && !_cups_isspace(*valptr) && bufptr < bufend)
+ *bufptr++ = *valptr++;
+
+ *bufptr = '\0';
+
+ return (buffer);
+ }
+ else
+ {
+ /*
+ * Skip this URI...
+ */
+
+ while (*valptr && !_cups_isspace(*valptr))
+ valptr++;
+ }
+
+ /*
+ * Skip whitespace...
+ */
+
+ while (_cups_isspace(*valptr))
+ valptr ++;
+ }
+
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
+ * attribute value.
+ *
+ * This function uses the current locale to find the corresponding name
+ * text from the attribute value. If no localized text for the requested
+ * name can be found, @code NULL@ is returned.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+const char * /* O - Value or @code NULL@ if not found */
+ppdLocalizeMarkerName(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *name) /* I - Marker name to look up */
+{
+ ppd_attr_t *locattr; /* Localized attribute */
+ char ll_CC[6]; /* Language + country locale */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !name)
+ return (NULL);
+
+ /*
+ * Get the default language...
+ */
+
+ ppd_ll_CC(ll_CC, sizeof(ll_CC));
+
+ /*
+ * Find the localized attribute...
+ */
+
+ if ((locattr = _ppdLocalizedAttr(ppd, "cupsMarkerName", name,
+ ll_CC)) == NULL)
+ locattr = ppdFindAttr(ppd, "cupsMarkerName", name);
+
+ return (locattr ? locattr->text : NULL);
+}
+
+
+/*
+ * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
+ */
+
+void
+_ppdFreeLanguages(
+ cups_array_t *languages) /* I - Languages array */
+{
+ char *language; /* Current language */
+
+
+ for (language = (char *)cupsArrayFirst(languages);
+ language;
+ language = (char *)cupsArrayNext(languages))
+ free(language);
+
+ cupsArrayDelete(languages);
+}
+
+
+/*
+ * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
+ */
+
+cups_array_t * /* O - Languages array */
+_ppdGetLanguages(ppd_file_t *ppd) /* I - PPD file */
+{
+ cups_array_t *languages; /* Languages array */
+ ppd_attr_t *attr; /* cupsLanguages attribute */
+ char *value, /* Copy of attribute value */
+ *start, /* Start of current language */
+ *ptr; /* Pointer into languages */
+
+
+ /*
+ * See if we have a cupsLanguages attribute...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) == NULL || !attr->value)
+ return (NULL);
+
+ /*
+ * Yes, load the list...
+ */
+
+ if ((languages = cupsArrayNew((cups_array_func_t)strcmp, NULL)) == NULL)
+ return (NULL);
+
+ if ((value = strdup(attr->value)) == NULL)
+ {
+ cupsArrayDelete(languages);
+ return (NULL);
+ }
+
+ for (ptr = value; *ptr;)
+ {
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (_cups_isspace(*ptr))
+ ptr ++;
+
+ if (!*ptr)
+ break;
+
+ /*
+ * Find the end of this language name...
+ */
+
+ for (start = ptr; *ptr && !_cups_isspace(*ptr); ptr ++);
+
+ if (*ptr)
+ *ptr++ = '\0';
+
+ if (!strcmp(start, "en"))
+ continue;
+
+ cupsArrayAdd(languages, strdup(start));
+ }
+
+ /*
+ * Free the temporary string and return either an array with one or more
+ * values or a NULL pointer...
+ */
+
+ free(value);
+
+ if (cupsArrayCount(languages) == 0)
+ {
+ cupsArrayDelete(languages);
+ return (NULL);
+ }
+ else
+ return (languages);
+}
+
+
+/*
+ * '_ppdHashName()' - Generate a hash value for a device or profile name.
+ *
+ * This function is primarily used on OS X, but is generally accessible
+ * since cupstestppd needs to check for profile name collisions in PPD files...
+ */
+
+unsigned /* O - Hash value */
+_ppdHashName(const char *name) /* I - Name to hash */
+{
+ int mult; /* Multiplier */
+ unsigned hash = 0; /* Hash value */
+
+
+ for (mult = 1; *name && mult <= 128; mult ++, name ++)
+ hash += (*name & 255) * mult;
+
+ return (hash);
+}
+
+
+/*
+ * '_ppdLocalizedAttr()' - Find a localized attribute.
+ */
+
+ppd_attr_t * /* O - Localized attribute or NULL */
+_ppdLocalizedAttr(ppd_file_t *ppd, /* I - PPD file */
+ const char *keyword, /* I - Main keyword */
+ const char *spec, /* I - Option keyword */
+ const char *ll_CC) /* I - Language + country locale */
+{
+ char lkeyword[PPD_MAX_NAME]; /* Localization keyword */
+ ppd_attr_t *attr; /* Current attribute */
+
+
+ DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
+ "ll_CC=\"%s\")", ppd, keyword, spec, ll_CC));
+
+ /*
+ * Look for Keyword.ll_CC, then Keyword.ll...
+ */
+
+ snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword);
+ if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL)
+ {
+ snprintf(lkeyword, sizeof(lkeyword), "%2.2s.%s", ll_CC, keyword);
+ attr = ppdFindAttr(ppd, lkeyword, spec);
+
+ if (!attr)
+ {
+ if (!strncmp(ll_CC, "ja", 2))
+ {
+ /*
+ * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
+ * PPD files were incorrectly assigned "jp" as the locale name
+ * instead of "ja". Support both the old (incorrect) and new
+ * locale names for Japanese...
+ */
+
+ snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
+ attr = ppdFindAttr(ppd, lkeyword, spec);
+ }
+ else if (!strncmp(ll_CC, "no", 2))
+ {
+ /*
+ * Norway has two languages, "Bokmal" (the primary one)
+ * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
+ * recommended by the locale folks...
+ */
+
+ snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword);
+ attr = ppdFindAttr(ppd, lkeyword, spec);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (attr)
+ DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr->name,
+ attr->spec, attr->text, attr->value ? attr->value : ""));
+ else
+ DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
+#endif /* DEBUG */
+
+ return (attr);
+}
+
+
+/*
+ * 'ppd_ll_CC()' - Get the current locale names.
+ */
+
+static cups_lang_t * /* O - Current language */
+ppd_ll_CC(char *ll_CC, /* O - Country-specific locale name */
+ int ll_CC_size) /* I - Size of country-specific name */
+{
+ cups_lang_t *lang; /* Current language */
+
+
+ /*
+ * Get the current locale...
+ */
+
+ if ((lang = cupsLangDefault()) == NULL)
+ {
+ strlcpy(ll_CC, "en_US", ll_CC_size);
+ return (NULL);
+ }
+
+ /*
+ * Copy the locale name...
+ */
+
+ strlcpy(ll_CC, lang->language, ll_CC_size);
+
+ if (strlen(ll_CC) == 2)
+ {
+ /*
+ * Map "ll" to primary/origin country locales to have the best
+ * chance of finding a match...
+ */
+
+ if (!strcmp(ll_CC, "cs"))
+ strlcpy(ll_CC, "cs_CZ", ll_CC_size);
+ else if (!strcmp(ll_CC, "en"))
+ strlcpy(ll_CC, "en_US", ll_CC_size);
+ else if (!strcmp(ll_CC, "ja"))
+ strlcpy(ll_CC, "ja_JP", ll_CC_size);
+ else if (!strcmp(ll_CC, "sv"))
+ strlcpy(ll_CC, "sv_SE", ll_CC_size);
+ else if (!strcmp(ll_CC, "zh")) /* Simplified Chinese */
+ strlcpy(ll_CC, "zh_CN", ll_CC_size);
+ }
+
+ DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
+ lang->language, ll_CC));
+ return (lang);
+}
+
+
+/*
+ * End of "$Id: localize.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/mark.c b/cups/libs/cups/mark.c
new file mode 100644
index 000000000..23a399701
--- /dev/null
+++ b/cups/libs/cups/mark.c
@@ -0,0 +1,1101 @@
+/*
+ * "$Id: mark.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Option marking routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsMarkOptions() - Mark command-line options in a PPD file.
+ * ppdFindChoice() - Return a pointer to an option choice.
+ * ppdFindMarkedChoice() - Return the marked choice for the specified option.
+ * ppdFindOption() - Return a pointer to the specified option.
+ * ppdIsMarked() - Check to see if an option is marked.
+ * ppdMarkDefaults() - Mark all default options in the PPD file.
+ * ppdMarkOption() - Mark an option in a PPD file and return the number
+ * of conflicts.
+ * ppdFirstOption() - Return the first option in the PPD file.
+ * ppdNextOption() - Return the next option in the PPD file.
+ * _ppdParseOptions() - Parse options from a PPD file.
+ * ppd_debug_marked() - Output the marked array to stdout...
+ * ppd_defaults() - Set the defaults for this group and all sub-groups.
+ * ppd_mark_choices() - Mark one or more option choices from a string.
+ * ppd_mark_option() - Quickly mark an option without checking for
+ * conflicts.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+#ifdef DEBUG
+static void ppd_debug_marked(ppd_file_t *ppd, const char *title);
+#else
+# define ppd_debug_marked(ppd,title)
+#endif /* DEBUG */
+static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
+static void ppd_mark_choices(ppd_file_t *ppd, const char *s);
+static void ppd_mark_option(ppd_file_t *ppd, const char *option,
+ const char *choice);
+
+
+/*
+ * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
+ *
+ * This function maps the IPP "finishings", "media", "mirror",
+ * "multiple-document-handling", "output-bin", "print-color-mode",
+ * "print-quality", "printer-resolution", and "sides" attributes to their
+ * corresponding PPD options and choices.
+ */
+
+int /* O - 1 if conflicts exist, 0 otherwise */
+cupsMarkOptions(
+ ppd_file_t *ppd, /* I - PPD file */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int i, j; /* Looping vars */
+ char *ptr, /* Pointer into string */
+ s[255]; /* Temporary string */
+ const char *val, /* Pointer into value */
+ *media, /* media option */
+ *output_bin, /* output-bin option */
+ *page_size, /* PageSize option */
+ *ppd_keyword, /* PPD keyword */
+ *print_color_mode, /* print-color-mode option */
+ *print_quality, /* print-quality option */
+ *sides; /* sides option */
+ cups_option_t *optptr; /* Current option */
+ ppd_attr_t *attr; /* PPD attribute */
+ _ppd_cache_t *cache; /* PPD cache and mapping data */
+
+
+ /*
+ * Check arguments...
+ */
+
+ if (!ppd || num_options <= 0 || !options)
+ return (0);
+
+ ppd_debug_marked(ppd, "Before...");
+
+ /*
+ * Do special handling for finishings, media, output-bin, output-mode,
+ * print-color-mode, print-quality, and PageSize...
+ */
+
+ media = cupsGetOption("media", num_options, options);
+ output_bin = cupsGetOption("output-bin", num_options, options);
+ page_size = cupsGetOption("PageSize", num_options, options);
+ print_quality = cupsGetOption("print-quality", num_options, options);
+ sides = cupsGetOption("sides", num_options, options);
+
+ if ((print_color_mode = cupsGetOption("print-color-mode", num_options,
+ options)) == NULL)
+ print_color_mode = cupsGetOption("output-mode", num_options, options);
+
+ if ((media || output_bin || print_color_mode || print_quality || sides) &&
+ !ppd->cache)
+ {
+ /*
+ * Load PPD cache and mapping data as needed...
+ */
+
+ ppd->cache = _ppdCacheCreateWithPPD(ppd);
+ }
+
+ cache = ppd->cache;
+
+ if (media)
+ {
+ /*
+ * Loop through the option string, separating it at commas and marking each
+ * individual option as long as the corresponding PPD option (PageSize,
+ * InputSlot, etc.) is not also set.
+ *
+ * For PageSize, we also check for an empty option value since some versions
+ * of MacOS X use it to specify auto-selection of the media based solely on
+ * the size.
+ */
+
+ for (val = media; *val;)
+ {
+ /*
+ * Extract the sub-option from the string...
+ */
+
+ for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
+ *ptr++ = *val++;
+ *ptr++ = '\0';
+
+ if (*val == ',')
+ val ++;
+
+ /*
+ * Mark it...
+ */
+
+ if (!page_size || !page_size[0])
+ {
+ if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s))
+ ppd_mark_option(ppd, "PageSize", s);
+ else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL)
+ ppd_mark_option(ppd, "PageSize", ppd_keyword);
+ }
+
+ if (cache && cache->source_option &&
+ !cupsGetOption(cache->source_option, num_options, options) &&
+ (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL)
+ ppd_mark_option(ppd, cache->source_option, ppd_keyword);
+
+ if (!cupsGetOption("MediaType", num_options, options) &&
+ (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL)
+ ppd_mark_option(ppd, "MediaType", ppd_keyword);
+ }
+ }
+
+ if (cache)
+ {
+ if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat",
+ num_options, options) &&
+ !cupsGetOption("APPrinterPreset", num_options, options) &&
+ (print_color_mode || print_quality))
+ {
+ /*
+ * Map output-mode and print-quality to a preset...
+ */
+
+ _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */
+ _pwg_print_quality_t pwg_pq; /* print-quality index */
+ cups_option_t *preset;/* Current preset option */
+
+ if (print_color_mode && !strcmp(print_color_mode, "monochrome"))
+ pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME;
+ else
+ pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
+
+ if (print_quality)
+ {
+ pwg_pq = atoi(print_quality) - IPP_QUALITY_DRAFT;
+ if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT)
+ pwg_pq = _PWG_PRINT_QUALITY_DRAFT;
+ else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH)
+ pwg_pq = _PWG_PRINT_QUALITY_HIGH;
+ }
+ else
+ pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
+
+ if (cache->num_presets[pwg_pcm][pwg_pq] == 0)
+ {
+ /*
+ * Try to find a preset that works so that we maximize the chances of us
+ * getting a good print using IPP attributes.
+ */
+
+ if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0)
+ pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
+ else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0)
+ pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
+ else
+ {
+ pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
+ pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
+ }
+ }
+
+ if (cache->num_presets[pwg_pcm][pwg_pq] > 0)
+ {
+ /*
+ * Copy the preset options as long as the corresponding names are not
+ * already defined in the IPP request...
+ */
+
+ for (i = cache->num_presets[pwg_pcm][pwg_pq],
+ preset = cache->presets[pwg_pcm][pwg_pq];
+ i > 0;
+ i --, preset ++)
+ {
+ if (!cupsGetOption(preset->name, num_options, options))
+ ppd_mark_option(ppd, preset->name, preset->value);
+ }
+ }
+ }
+
+ if (output_bin && !cupsGetOption("OutputBin", num_options, options) &&
+ (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL)
+ {
+ /*
+ * Map output-bin to OutputBin...
+ */
+
+ ppd_mark_option(ppd, "OutputBin", ppd_keyword);
+ }
+
+ if (sides && cache->sides_option &&
+ !cupsGetOption(cache->sides_option, num_options, options))
+ {
+ /*
+ * Map sides to duplex option...
+ */
+
+ if (!strcmp(sides, "one-sided") && cache->sides_1sided)
+ ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided);
+ else if (!strcmp(sides, "two-sided-long-edge") &&
+ cache->sides_2sided_long)
+ ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long);
+ else if (!strcmp(sides, "two-sided-short-edge") &&
+ cache->sides_2sided_short)
+ ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short);
+ }
+ }
+
+ /*
+ * Mark other options...
+ */
+
+ 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") ||
+ !_cups_strcasecmp(optptr->name, "print-quality") ||
+ !_cups_strcasecmp(optptr->name, "sides"))
+ continue;
+ else if (!_cups_strcasecmp(optptr->name, "resolution") ||
+ !_cups_strcasecmp(optptr->name, "printer-resolution"))
+ {
+ ppd_mark_option(ppd, "Resolution", optptr->value);
+ ppd_mark_option(ppd, "SetResolution", optptr->value);
+ /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
+ ppd_mark_option(ppd, "JCLResolution", optptr->value);
+ /* HP */
+ ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
+ /* Canon */
+ }
+ else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
+ {
+ if (!cupsGetOption("Collate", num_options, options) &&
+ ppdFindOption(ppd, "Collate"))
+ {
+ if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
+ ppd_mark_option(ppd, "Collate", "True");
+ else
+ ppd_mark_option(ppd, "Collate", "False");
+ }
+ }
+ else if (!_cups_strcasecmp(optptr->name, "finishings"))
+ {
+ /*
+ * Lookup cupsIPPFinishings attributes for each value...
+ */
+
+ for (ptr = optptr->value; *ptr;)
+ {
+ /*
+ * Get the next finishings number...
+ */
+
+ if (!isdigit(*ptr & 255))
+ break;
+
+ if ((j = strtol(ptr, &ptr, 10)) < 3)
+ break;
+
+ /*
+ * Skip separator as needed...
+ */
+
+ if (*ptr == ',')
+ ptr ++;
+
+ /*
+ * Look it up in the PPD file...
+ */
+
+ sprintf(s, "%d", j);
+
+ if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
+ continue;
+
+ /*
+ * Apply "*Option Choice" settings from the attribute value...
+ */
+
+ ppd_mark_choices(ppd, attr->value);
+ }
+ }
+ else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
+ {
+ /*
+ * Lookup APPrinterPreset value...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
+ {
+ /*
+ * Apply "*Option Choice" settings from the attribute value...
+ */
+
+ ppd_mark_choices(ppd, attr->value);
+ }
+ }
+ else if (!_cups_strcasecmp(optptr->name, "mirror"))
+ ppd_mark_option(ppd, "MirrorPrint", optptr->value);
+ else
+ ppd_mark_option(ppd, optptr->name, optptr->value);
+
+ ppd_debug_marked(ppd, "After...");
+
+ return (ppdConflicts(ppd) > 0);
+}
+
+
+/*
+ * 'ppdFindChoice()' - Return a pointer to an option choice.
+ */
+
+ppd_choice_t * /* O - Choice pointer or @code NULL@ */
+ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */
+ const char *choice) /* I - Name of choice */
+{
+ int i; /* Looping var */
+ ppd_choice_t *c; /* Current choice */
+
+
+ if (!o || !choice)
+ return (NULL);
+
+ if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
+ choice = "Custom";
+
+ for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+ if (!_cups_strcasecmp(c->choice, choice))
+ return (c);
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
+ */
+
+ppd_choice_t * /* O - Pointer to choice or @code NULL@ */
+ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */
+ const char *option) /* I - Keyword/option name */
+{
+ ppd_choice_t key, /* Search key for choice */
+ *marked; /* Marked choice */
+
+
+ DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
+
+ if ((key.option = ppdFindOption(ppd, option)) == NULL)
+ {
+ DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
+ return (NULL);
+ }
+
+ marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
+
+ DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
+ marked ? marked->choice : "NULL"));
+
+ return (marked);
+}
+
+
+/*
+ * 'ppdFindOption()' - Return a pointer to the specified option.
+ */
+
+ppd_option_t * /* O - Pointer to option or @code NULL@ */
+ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */
+ const char *option) /* I - Option/Keyword name */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !option)
+ return (NULL);
+
+ if (ppd->options)
+ {
+ /*
+ * Search in the array...
+ */
+
+ ppd_option_t key; /* Option search key */
+
+
+ strlcpy(key.keyword, option, sizeof(key.keyword));
+
+ return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
+ }
+ else
+ {
+ /*
+ * Search in each group...
+ */
+
+ int i, j; /* Looping vars */
+ ppd_group_t *group; /* Current group */
+ ppd_option_t *optptr; /* Current option */
+
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ for (j = group->num_options, optptr = group->options;
+ j > 0;
+ j --, optptr ++)
+ if (!_cups_strcasecmp(optptr->keyword, option))
+ return (optptr);
+
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'ppdIsMarked()' - Check to see if an option is marked.
+ */
+
+int /* O - Non-zero if option is marked */
+ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */
+ const char *option, /* I - Option/Keyword name */
+ const char *choice) /* I - Choice name */
+{
+ ppd_choice_t key, /* Search key */
+ *c; /* Choice pointer */
+
+
+ if (!ppd)
+ return (0);
+
+ if ((key.option = ppdFindOption(ppd, option)) == NULL)
+ return (0);
+
+ if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
+ return (0);
+
+ return (!strcmp(c->choice, choice));
+}
+
+
+/*
+ * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
+ */
+
+void
+ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */
+{
+ int i; /* Looping variables */
+ ppd_group_t *g; /* Current group */
+ ppd_choice_t *c; /* Current choice */
+
+
+ if (!ppd)
+ return;
+
+ /*
+ * Clean out the marked array...
+ */
+
+ for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+ c;
+ c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
+ {
+ cupsArrayRemove(ppd->marked, c);
+ c->marked = 0;
+ }
+
+ /*
+ * Then repopulate it with the defaults...
+ */
+
+ for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+ ppd_defaults(ppd, g);
+}
+
+
+/*
+ * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
+ * conflicts.
+ */
+
+int /* O - Number of conflicts */
+ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */
+ const char *option, /* I - Keyword */
+ const char *choice) /* I - Option name */
+{
+ DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
+ ppd, option, choice));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !option || !choice)
+ return (0);
+
+ /*
+ * Mark the option...
+ */
+
+ ppd_mark_option(ppd, option, choice);
+
+ /*
+ * Return the number of conflicts...
+ */
+
+ return (ppdConflicts(ppd));
+}
+
+
+/*
+ * 'ppdFirstOption()' - Return the first option in the PPD file.
+ *
+ * Options are returned from all groups in ascending alphanumeric order.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_option_t * /* O - First option or @code NULL@ */
+ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */
+{
+ if (!ppd)
+ return (NULL);
+ else
+ return ((ppd_option_t *)cupsArrayFirst(ppd->options));
+}
+
+
+/*
+ * 'ppdNextOption()' - Return the next option in the PPD file.
+ *
+ * Options are returned from all groups in ascending alphanumeric order.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_option_t * /* O - Next option or @code NULL@ */
+ppdNextOption(ppd_file_t *ppd) /* I - PPD file */
+{
+ if (!ppd)
+ return (NULL);
+ else
+ return ((ppd_option_t *)cupsArrayNext(ppd->options));
+}
+
+
+/*
+ * '_ppdParseOptions()' - Parse options from a PPD file.
+ *
+ * This function looks for strings of the form:
+ *
+ * *option choice ... *optionN choiceN
+ * property value ... propertyN valueN
+ *
+ * It stops when it finds a string that doesn't match this format.
+ */
+
+int /* O - Number of options */
+_ppdParseOptions(
+ const char *s, /* I - String to parse */
+ int num_options, /* I - Number of options */
+ cups_option_t **options, /* IO - Options */
+ _ppd_parse_t which) /* I - What to parse */
+{
+ char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */
+ choice[PPD_MAX_NAME], /* Current choice/value */
+ *ptr; /* Pointer into option or choice */
+
+
+ if (!s)
+ return (num_options);
+
+ /*
+ * Read all of the "*Option Choice" and "property value" pairs from the
+ * string, add them to an options array as we go...
+ */
+
+ while (*s)
+ {
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (_cups_isspace(*s))
+ s ++;
+
+ /*
+ * Get the option/property name...
+ */
+
+ ptr = option;
+ while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1))
+ *ptr++ = *s++;
+
+ if (ptr == s || !_cups_isspace(*s))
+ break;
+
+ *ptr = '\0';
+
+ /*
+ * Get the choice...
+ */
+
+ while (_cups_isspace(*s))
+ s ++;
+
+ if (!*s)
+ break;
+
+ ptr = choice;
+ while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1))
+ *ptr++ = *s++;
+
+ if (*s && !_cups_isspace(*s))
+ break;
+
+ *ptr = '\0';
+
+ /*
+ * Add it to the options array...
+ */
+
+ if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES)
+ num_options = cupsAddOption(option + 1, choice, num_options, options);
+ else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS)
+ num_options = cupsAddOption(option, choice, num_options, options);
+ }
+
+ return (num_options);
+}
+
+
+#ifdef DEBUG
+/*
+ * 'ppd_debug_marked()' - Output the marked array to stdout...
+ */
+
+static void
+ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */
+ const char *title) /* I - Title for list */
+{
+ ppd_choice_t *c; /* Current choice */
+
+
+ DEBUG_printf(("2cupsMarkOptions: %s", title));
+
+ for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+ c;
+ c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
+ DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
+}
+#endif /* DEBUG */
+
+
+/*
+ * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
+ */
+
+static void
+ppd_defaults(ppd_file_t *ppd, /* I - PPD file */
+ ppd_group_t *g) /* I - Group to default */
+{
+ int i; /* Looping var */
+ ppd_option_t *o; /* Current option */
+ ppd_group_t *sg; /* Current sub-group */
+
+
+ for (i = g->num_options, o = g->options; i > 0; i --, o ++)
+ if (_cups_strcasecmp(o->keyword, "PageRegion") != 0)
+ ppdMarkOption(ppd, o->keyword, o->defchoice);
+
+ for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
+ ppd_defaults(ppd, sg);
+}
+
+
+/*
+ * 'ppd_mark_choices()' - Mark one or more option choices from a string.
+ */
+
+static void
+ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */
+ const char *s) /* I - "*Option Choice ..." string */
+{
+ int i, /* Looping var */
+ num_options; /* Number of options */
+ cups_option_t *options, /* Options */
+ *option; /* Current option */
+
+
+ if (!s)
+ return;
+
+ options = NULL;
+ num_options = _ppdParseOptions(s, 0, &options, 0);
+
+ for (i = num_options, option = options; i > 0; i --, option ++)
+ ppd_mark_option(ppd, option->name, option->value);
+
+ cupsFreeOptions(num_options, options);
+}
+
+
+/*
+ * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
+ */
+
+static void
+ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Option name */
+ const char *choice) /* I - Choice name */
+{
+ int i, j; /* Looping vars */
+ ppd_option_t *o; /* Option pointer */
+ ppd_choice_t *c, /* Choice pointer */
+ *oldc, /* Old choice pointer */
+ key; /* Search key for choice */
+ struct lconv *loc; /* Locale data */
+
+
+ DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
+ ppd, option, choice));
+
+ /*
+ * AP_D_InputSlot is the "default input slot" on MacOS X, and setting
+ * it clears the regular InputSlot choices...
+ */
+
+ if (!_cups_strcasecmp(option, "AP_D_InputSlot"))
+ {
+ cupsArraySave(ppd->options);
+
+ if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
+ {
+ key.option = o;
+ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+ {
+ oldc->marked = 0;
+ cupsArrayRemove(ppd->marked, oldc);
+ }
+ }
+
+ cupsArrayRestore(ppd->options);
+ }
+
+ /*
+ * Check for custom options...
+ */
+
+ cupsArraySave(ppd->options);
+
+ o = ppdFindOption(ppd, option);
+
+ cupsArrayRestore(ppd->options);
+
+ if (!o)
+ return;
+
+ loc = localeconv();
+
+ if (!_cups_strncasecmp(choice, "Custom.", 7))
+ {
+ /*
+ * Handle a custom option...
+ */
+
+ if ((c = ppdFindChoice(o, "Custom")) == NULL)
+ return;
+
+ if (!_cups_strcasecmp(option, "PageSize"))
+ {
+ /*
+ * Handle custom page sizes...
+ */
+
+ ppdPageSize(ppd, choice);
+ }
+ else
+ {
+ /*
+ * Handle other custom options...
+ */
+
+ ppd_coption_t *coption; /* Custom option */
+ ppd_cparam_t *cparam; /* Custom parameter */
+ char *units; /* Custom points units */
+
+
+ if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
+ {
+ if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
+ return;
+
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_REAL :
+ cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
+ NULL, loc);
+ break;
+
+ case PPD_CUSTOM_POINTS :
+ cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
+ &units,
+ loc);
+
+ if (units)
+ {
+ if (!_cups_strcasecmp(units, "cm"))
+ cparam->current.custom_points *= 72.0f / 2.54f;
+ else if (!_cups_strcasecmp(units, "mm"))
+ cparam->current.custom_points *= 72.0f / 25.4f;
+ else if (!_cups_strcasecmp(units, "m"))
+ cparam->current.custom_points *= 72.0f / 0.0254f;
+ else if (!_cups_strcasecmp(units, "in"))
+ cparam->current.custom_points *= 72.0f;
+ else if (!_cups_strcasecmp(units, "ft"))
+ cparam->current.custom_points *= 12.0f * 72.0f;
+ }
+ break;
+
+ case PPD_CUSTOM_INT :
+ cparam->current.custom_int = atoi(choice + 7);
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ if (cparam->current.custom_string)
+ _cupsStrFree(cparam->current.custom_string);
+
+ cparam->current.custom_string = _cupsStrAlloc(choice + 7);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Make sure that we keep the option marked below...
+ */
+
+ choice = "Custom";
+ }
+ else if (choice[0] == '{')
+ {
+ /*
+ * Handle multi-value custom options...
+ */
+
+ ppd_coption_t *coption; /* Custom option */
+ ppd_cparam_t *cparam; /* Custom parameter */
+ char *units; /* Custom points units */
+ int num_vals; /* Number of values */
+ cups_option_t *vals, /* Values */
+ *val; /* Value */
+
+
+ if ((c = ppdFindChoice(o, "Custom")) == NULL)
+ return;
+
+ if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
+ {
+ num_vals = cupsParseOptions(choice, 0, &vals);
+
+ for (i = 0, val = vals; i < num_vals; i ++, val ++)
+ {
+ if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
+ continue;
+
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_REAL :
+ cparam->current.custom_real = (float)_cupsStrScand(val->value,
+ NULL, loc);
+ break;
+
+ case PPD_CUSTOM_POINTS :
+ cparam->current.custom_points = (float)_cupsStrScand(val->value,
+ &units,
+ loc);
+
+ if (units)
+ {
+ if (!_cups_strcasecmp(units, "cm"))
+ cparam->current.custom_points *= 72.0f / 2.54f;
+ else if (!_cups_strcasecmp(units, "mm"))
+ cparam->current.custom_points *= 72.0f / 25.4f;
+ else if (!_cups_strcasecmp(units, "m"))
+ cparam->current.custom_points *= 72.0f / 0.0254f;
+ else if (!_cups_strcasecmp(units, "in"))
+ cparam->current.custom_points *= 72.0f;
+ else if (!_cups_strcasecmp(units, "ft"))
+ cparam->current.custom_points *= 12.0f * 72.0f;
+ }
+ break;
+
+ case PPD_CUSTOM_INT :
+ cparam->current.custom_int = atoi(val->value);
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ if (cparam->current.custom_string)
+ _cupsStrFree(cparam->current.custom_string);
+
+ cparam->current.custom_string = _cupsStrRetain(val->value);
+ break;
+ }
+ }
+
+ cupsFreeOptions(num_vals, vals);
+ }
+ }
+ else
+ {
+ for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+ if (!_cups_strcasecmp(c->choice, choice))
+ break;
+
+ if (!i)
+ return;
+ }
+
+ /*
+ * Option found; mark it and then handle unmarking any other options.
+ */
+
+ if (o->ui != PPD_UI_PICKMANY)
+ {
+ /*
+ * Unmark all other choices...
+ */
+
+ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
+ {
+ oldc->marked = 0;
+ cupsArrayRemove(ppd->marked, oldc);
+ }
+
+ if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion"))
+ {
+ /*
+ * Mark current page size...
+ */
+
+ for (j = 0; j < ppd->num_sizes; j ++)
+ ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name,
+ choice);
+
+ /*
+ * Unmark the current PageSize or PageRegion setting, as
+ * appropriate...
+ */
+
+ cupsArraySave(ppd->options);
+
+ if (!_cups_strcasecmp(option, "PageSize"))
+ {
+ if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
+ {
+ key.option = o;
+ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+ {
+ oldc->marked = 0;
+ cupsArrayRemove(ppd->marked, oldc);
+ }
+ }
+ }
+ else
+ {
+ if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
+ {
+ key.option = o;
+ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+ {
+ oldc->marked = 0;
+ cupsArrayRemove(ppd->marked, oldc);
+ }
+ }
+ }
+
+ cupsArrayRestore(ppd->options);
+ }
+ else if (!_cups_strcasecmp(option, "InputSlot"))
+ {
+ /*
+ * Unmark ManualFeed option...
+ */
+
+ cupsArraySave(ppd->options);
+
+ if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
+ {
+ key.option = o;
+ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+ {
+ oldc->marked = 0;
+ cupsArrayRemove(ppd->marked, oldc);
+ }
+ }
+
+ cupsArrayRestore(ppd->options);
+ }
+ else if (!_cups_strcasecmp(option, "ManualFeed") &&
+ !_cups_strcasecmp(choice, "True"))
+ {
+ /*
+ * Unmark InputSlot option...
+ */
+
+ cupsArraySave(ppd->options);
+
+ if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
+ {
+ key.option = o;
+ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+ {
+ oldc->marked = 0;
+ cupsArrayRemove(ppd->marked, oldc);
+ }
+ }
+
+ cupsArrayRestore(ppd->options);
+ }
+ }
+
+ c->marked = 1;
+
+ cupsArrayAdd(ppd->marked, c);
+}
+
+
+/*
+ * End of "$Id: mark.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/md5-private.h b/cups/libs/cups/md5-private.h
new file mode 100644
index 000000000..7b9464262
--- /dev/null
+++ b/cups/libs/cups/md5-private.h
@@ -0,0 +1,79 @@
+/*
+ * "$Id: md5-private.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Private MD5 definitions for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 2005 by Easy Software Products
+ *
+ * Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * L. Peter Deutsch
+ * ghost@aladdin.com
+ */
+
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef _CUPS_MD5_PRIVATE_H_
+# define _CUPS_MD5_PRIVATE_H_
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct _cups_md5_state_s {
+ unsigned int count[2]; /* message length in bits, lsw first */
+ unsigned int abcd[4]; /* digest buffer */
+ unsigned char buf[64]; /* accumulate block */
+} _cups_md5_state_t;
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/* Initialize the algorithm. */
+void _cupsMD5Init(_cups_md5_state_t *pms);
+
+/* Append a string to the message. */
+void _cupsMD5Append(_cups_md5_state_t *pms, const unsigned char *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void _cupsMD5Finish(_cups_md5_state_t *pms, unsigned char digest[16]);
+
+# ifdef __cplusplus
+} /* end extern "C" */
+# endif /* __cplusplus */
+#endif /* !_CUPS_MD5_PRIVATE_H_ */
+
+/*
+ * End of "$Id: md5-private.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/md5.c b/cups/libs/cups/md5.c
new file mode 100644
index 000000000..c3e73133a
--- /dev/null
+++ b/cups/libs/cups/md5.c
@@ -0,0 +1,346 @@
+/*
+ * "$Id: md5.c 11374 2013-11-04 23:49:10Z msweet $"
+ *
+ * Private MD5 implementation for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2005 by Easy Software Products
+ * Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * L. Peter Deutsch
+ * ghost@aladdin.com
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5-private.h"
+#include "string-private.h"
+
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+_cups_md5_process(_cups_md5_state_t *pms, const unsigned char *data /*[64]*/)
+{
+ unsigned int
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ unsigned int t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+ /*
+ * On big-endian machines, we must arrange the bytes in the right
+ * order. (This also works on machines of unknown byte order.)
+ */
+ unsigned int X[16];
+ const unsigned char *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = (unsigned)xp[0] + ((unsigned)xp[1] << 8) +
+ ((unsigned)xp[2] << 16) + ((unsigned)xp[3] << 24);
+
+#else /* !ARCH_IS_BIG_ENDIAN */
+
+ /*
+ * On little-endian machines, we can process properly aligned data
+ * without copying it.
+ */
+ unsigned int xbuf[16];
+ const unsigned int *X;
+
+ if (!((data - (const unsigned char *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const unsigned int *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+_cupsMD5Init(_cups_md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = 0xefcdab89;
+ pms->abcd[2] = 0x98badcfe;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+_cupsMD5Append(_cups_md5_state_t *pms, const unsigned char *data, int nbytes)
+{
+ const unsigned char *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ unsigned int nbits = (unsigned int)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ _cups_md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ _cups_md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+_cupsMD5Finish(_cups_md5_state_t *pms, unsigned char digest[16])
+{
+ static const unsigned char pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ unsigned char data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (unsigned char)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ _cupsMD5Append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ _cupsMD5Append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (unsigned char)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+/*
+ * End of "$Id: md5.c 11374 2013-11-04 23:49:10Z msweet $".
+ */
diff --git a/cups/libs/cups/md5passwd.c b/cups/libs/cups/md5passwd.c
new file mode 100644
index 000000000..135282c50
--- /dev/null
+++ b/cups/libs/cups/md5passwd.c
@@ -0,0 +1,142 @@
+/*
+ * "$Id: md5passwd.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * MD5 password support for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * httpMD5() - Compute the MD5 sum of the username:group:password.
+ * httpMD5Nonce() - Combine the MD5 sum of the username, group, and password
+ * with the server-supplied nonce value.
+ * httpMD5String() - Convert an MD5 sum to a character string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "http-private.h"
+#include "string-private.h"
+
+
+/*
+ * 'httpMD5()' - Compute the MD5 sum of the username:group:password.
+ */
+
+char * /* O - MD5 sum */
+httpMD5(const char *username, /* I - User name */
+ const char *realm, /* I - Realm name */
+ const char *passwd, /* I - Password string */
+ char md5[33]) /* O - MD5 string */
+{
+ _cups_md5_state_t state; /* MD5 state info */
+ unsigned char sum[16]; /* Sum data */
+ char line[256]; /* Line to sum */
+
+
+ /*
+ * Compute the MD5 sum of the user name, group name, and password.
+ */
+
+ snprintf(line, sizeof(line), "%s:%s:%s", username, realm, passwd);
+ _cupsMD5Init(&state);
+ _cupsMD5Append(&state, (unsigned char *)line, (int)strlen(line));
+ _cupsMD5Finish(&state, sum);
+
+ /*
+ * Return the sum...
+ */
+
+ return (httpMD5String(sum, md5));
+}
+
+
+/*
+ * 'httpMD5Final()' - Combine the MD5 sum of the username, group, and password
+ * with the server-supplied nonce value, method, and
+ * request-uri.
+ */
+
+char * /* O - New sum */
+httpMD5Final(const char *nonce, /* I - Server nonce value */
+ const char *method, /* I - METHOD (GET, POST, etc.) */
+ const char *resource, /* I - Resource path */
+ char md5[33]) /* IO - MD5 sum */
+{
+ _cups_md5_state_t state; /* MD5 state info */
+ unsigned char sum[16]; /* Sum data */
+ char line[1024]; /* Line of data */
+ char a2[33]; /* Hash of method and resource */
+
+
+ /*
+ * First compute the MD5 sum of the method and resource...
+ */
+
+ snprintf(line, sizeof(line), "%s:%s", method, resource);
+ _cupsMD5Init(&state);
+ _cupsMD5Append(&state, (unsigned char *)line, (int)strlen(line));
+ _cupsMD5Finish(&state, sum);
+ httpMD5String(sum, a2);
+
+ /*
+ * Then combine A1 (MD5 of username, realm, and password) with the nonce
+ * and A2 (method + resource) values to get the final MD5 sum for the
+ * request...
+ */
+
+ snprintf(line, sizeof(line), "%s:%s:%s", md5, nonce, a2);
+
+ _cupsMD5Init(&state);
+ _cupsMD5Append(&state, (unsigned char *)line, (int)strlen(line));
+ _cupsMD5Finish(&state, sum);
+
+ return (httpMD5String(sum, md5));
+}
+
+
+/*
+ * 'httpMD5String()' - Convert an MD5 sum to a character string.
+ */
+
+char * /* O - MD5 sum in hex */
+httpMD5String(const unsigned char *sum, /* I - MD5 sum data */
+ char md5[33])
+ /* O - MD5 sum in hex */
+{
+ int i; /* Looping var */
+ char *md5ptr; /* Pointer into MD5 string */
+ static const char hex[] = "0123456789abcdef";
+ /* Hex digits */
+
+
+ /*
+ * Convert the MD5 sum to hexadecimal...
+ */
+
+ for (i = 16, md5ptr = md5; i > 0; i --, sum ++)
+ {
+ *md5ptr++ = hex[*sum >> 4];
+ *md5ptr++ = hex[*sum & 15];
+ }
+
+ *md5ptr = '\0';
+
+ return (md5);
+}
+
+
+/*
+ * End of "$Id: md5passwd.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/notify.c b/cups/libs/cups/notify.c
new file mode 100644
index 000000000..7e5cebb28
--- /dev/null
+++ b/cups/libs/cups/notify.c
@@ -0,0 +1,202 @@
+/*
+ * "$Id: notify.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Notification routines for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2005-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsNotifySubject() - Return the subject for the given notification
+ * message.
+ * cupsNotifyText() - Return the text for the given notification message.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * 'cupsNotifySubject()' - Return the subject for the given notification message.
+ *
+ * The returned string must be freed by the caller using @code free@.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Subject string or @code NULL@ */
+cupsNotifySubject(cups_lang_t *lang, /* I - Language data */
+ ipp_t *event) /* I - Event data */
+{
+ char buffer[1024]; /* Subject buffer */
+ const char *prefix, /* Prefix on subject */
+ *state; /* Printer/job state string */
+ ipp_attribute_t *job_id, /* notify-job-id */
+ *job_name, /* job-name */
+ *job_state, /* job-state */
+ *printer_name, /* printer-name */
+ *printer_state, /* printer-state */
+ *printer_uri, /* notify-printer-uri */
+ *subscribed; /* notify-subscribed-event */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!event || !lang)
+ return (NULL);
+
+ /*
+ * Get the required attributes...
+ */
+
+ job_id = ippFindAttribute(event, "notify-job-id", IPP_TAG_INTEGER);
+ job_name = ippFindAttribute(event, "job-name", IPP_TAG_NAME);
+ job_state = ippFindAttribute(event, "job-state", IPP_TAG_ENUM);
+ printer_name = ippFindAttribute(event, "printer-name", IPP_TAG_NAME);
+ printer_state = ippFindAttribute(event, "printer-state", IPP_TAG_ENUM);
+ printer_uri = ippFindAttribute(event, "notify-printer-uri", IPP_TAG_URI);
+ subscribed = ippFindAttribute(event, "notify-subscribed-event",
+ IPP_TAG_KEYWORD);
+
+
+ if (job_id && printer_name && printer_uri && job_state)
+ {
+ /*
+ * Job event...
+ */
+
+ prefix = _cupsLangString(lang, _("Print Job:"));
+
+ switch (job_state->values[0].integer)
+ {
+ case IPP_JSTATE_PENDING :
+ state = _cupsLangString(lang, _("pending"));
+ break;
+ case IPP_JSTATE_HELD :
+ state = _cupsLangString(lang, _("held"));
+ break;
+ case IPP_JSTATE_PROCESSING :
+ state = _cupsLangString(lang, _("processing"));
+ break;
+ case IPP_JSTATE_STOPPED :
+ state = _cupsLangString(lang, _("stopped"));
+ break;
+ case IPP_JSTATE_CANCELED :
+ state = _cupsLangString(lang, _("canceled"));
+ break;
+ case IPP_JSTATE_ABORTED :
+ state = _cupsLangString(lang, _("aborted"));
+ break;
+ case IPP_JSTATE_COMPLETED :
+ state = _cupsLangString(lang, _("completed"));
+ break;
+ default :
+ state = _cupsLangString(lang, _("unknown"));
+ break;
+ }
+
+ snprintf(buffer, sizeof(buffer), "%s %s-%d (%s) %s",
+ prefix,
+ printer_name->values[0].string.text,
+ job_id->values[0].integer,
+ job_name ? job_name->values[0].string.text :
+ _cupsLangString(lang, _("untitled")),
+ state);
+ }
+ else if (printer_uri && printer_name && printer_state)
+ {
+ /*
+ * Printer event...
+ */
+
+ prefix = _cupsLangString(lang, _("Printer:"));
+
+ switch (printer_state->values[0].integer)
+ {
+ case IPP_PSTATE_IDLE :
+ state = _cupsLangString(lang, _("idle"));
+ break;
+ case IPP_PSTATE_PROCESSING :
+ state = _cupsLangString(lang, _("processing"));
+ break;
+ case IPP_PSTATE_STOPPED :
+ state = _cupsLangString(lang, _("stopped"));
+ break;
+ default :
+ state = _cupsLangString(lang, _("unknown"));
+ break;
+ }
+
+ snprintf(buffer, sizeof(buffer), "%s %s %s",
+ prefix,
+ printer_name->values[0].string.text,
+ state);
+ }
+ else if (subscribed)
+ strlcpy(buffer, subscribed->values[0].string.text, sizeof(buffer));
+ else
+ return (NULL);
+
+ /*
+ * Duplicate and return the subject string...
+ */
+
+ return (strdup(buffer));
+}
+
+
+/*
+ * 'cupsNotifyText()' - Return the text for the given notification message.
+ *
+ * The returned string must be freed by the caller using @code free@.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+char * /* O - Message text or @code NULL@ */
+cupsNotifyText(cups_lang_t *lang, /* I - Language data */
+ ipp_t *event) /* I - Event data */
+{
+ ipp_attribute_t *notify_text; /* notify-text */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!event || !lang)
+ return (NULL);
+
+ /*
+ * Get the notify-text attribute from the server...
+ */
+
+ if ((notify_text = ippFindAttribute(event, "notify-text",
+ IPP_TAG_TEXT)) == NULL)
+ return (NULL);
+
+ /*
+ * Return a copy...
+ */
+
+ return (strdup(notify_text->values[0].string.text));
+}
+
+
+/*
+ * End of "$Id: notify.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/options.c b/cups/libs/cups/options.c
new file mode 100644
index 000000000..d02500995
--- /dev/null
+++ b/cups/libs/cups/options.c
@@ -0,0 +1,711 @@
+/*
+ * "$Id: options.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Option routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsAddOption() - Add an option to an option array.
+ * cupsFreeOptions() - Free all memory used by options.
+ * cupsGetOption() - Get an option value.
+ * cupsParseOptions() - Parse options from a command-line argument.
+ * cupsRemoveOption() - Remove an option from an option array.
+ * _cupsGet1284Values() - Get 1284 device ID keys and values.
+ * cups_compare_options() - Compare two options.
+ * cups_find_option() - Find an option using a binary search.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int cups_compare_options(cups_option_t *a, cups_option_t *b);
+static int cups_find_option(const char *name, int num_options,
+ cups_option_t *option, int prev, int *rdiff);
+
+
+/*
+ * 'cupsAddOption()' - Add an option to an option array.
+ *
+ * New option arrays can be initialized simply by passing 0 for the
+ * "num_options" parameter.
+ */
+
+int /* O - Number of options */
+cupsAddOption(const char *name, /* I - Name of option */
+ const char *value, /* I - Value of option */
+ int num_options,/* I - Number of options */
+ cups_option_t **options) /* IO - Pointer to options */
+{
+ cups_option_t *temp; /* Pointer to new option */
+ int insert, /* Insertion point */
+ diff; /* Result of search */
+
+
+ DEBUG_printf(("2cupsAddOption(name=\"%s\", value=\"%s\", num_options=%d, "
+ "options=%p)", name, value, num_options, options));
+
+ if (!name || !name[0] || !value || !options || num_options < 0)
+ {
+ DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
+ return (num_options);
+ }
+
+ /*
+ * Look for an existing option with the same name...
+ */
+
+ if (num_options == 0)
+ {
+ insert = 0;
+ diff = 1;
+ }
+ else
+ {
+ insert = cups_find_option(name, num_options, *options, num_options - 1,
+ &diff);
+
+ if (diff > 0)
+ insert ++;
+ }
+
+ if (diff)
+ {
+ /*
+ * No matching option name...
+ */
+
+ DEBUG_printf(("4cupsAddOption: New option inserted at index %d...",
+ insert));
+
+ if (num_options == 0)
+ temp = (cups_option_t *)malloc(sizeof(cups_option_t));
+ else
+ temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) *
+ (num_options + 1));
+
+ if (temp == NULL)
+ {
+ DEBUG_puts("3cupsAddOption: Unable to expand option array, returning 0");
+ return (0);
+ }
+
+ *options = temp;
+
+ if (insert < num_options)
+ {
+ DEBUG_printf(("4cupsAddOption: Shifting %d options...",
+ (int)(num_options - insert)));
+ memmove(temp + insert + 1, temp + insert,
+ (num_options - insert) * sizeof(cups_option_t));
+ }
+
+ temp += insert;
+ temp->name = _cupsStrAlloc(name);
+ num_options ++;
+ }
+ else
+ {
+ /*
+ * Match found; free the old value...
+ */
+
+ DEBUG_printf(("4cupsAddOption: Option already exists at index %d...",
+ insert));
+
+ temp = *options + insert;
+ _cupsStrFree(temp->value);
+ }
+
+ temp->value = _cupsStrAlloc(value);
+
+ DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
+
+ return (num_options);
+}
+
+
+/*
+ * 'cupsFreeOptions()' - Free all memory used by options.
+ */
+
+void
+cupsFreeOptions(
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Pointer to options */
+{
+ int i; /* Looping var */
+
+
+ DEBUG_printf(("cupsFreeOptions(num_options=%d, options=%p)", num_options,
+ options));
+
+ if (num_options <= 0 || !options)
+ return;
+
+ for (i = 0; i < num_options; i ++)
+ {
+ _cupsStrFree(options[i].name);
+ _cupsStrFree(options[i].value);
+ }
+
+ free(options);
+}
+
+
+/*
+ * 'cupsGetOption()' - Get an option value.
+ */
+
+const char * /* O - Option value or @code NULL@ */
+cupsGetOption(const char *name, /* I - Name of option */
+ int num_options,/* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int diff, /* Result of comparison */
+ match; /* Matching index */
+
+
+ DEBUG_printf(("2cupsGetOption(name=\"%s\", num_options=%d, options=%p)",
+ name, num_options, options));
+
+ if (!name || num_options <= 0 || !options)
+ {
+ DEBUG_puts("3cupsGetOption: Returning NULL");
+ return (NULL);
+ }
+
+ match = cups_find_option(name, num_options, options, -1, &diff);
+
+ if (!diff)
+ {
+ DEBUG_printf(("3cupsGetOption: Returning \"%s\"", options[match].value));
+ return (options[match].value);
+ }
+
+ DEBUG_puts("3cupsGetOption: Returning NULL");
+ return (NULL);
+}
+
+
+/*
+ * 'cupsParseOptions()' - Parse options from a command-line argument.
+ *
+ * This function converts space-delimited name/value pairs according
+ * to the PAPI text option ABNF specification. Collection values
+ * ("name={a=... b=... c=...}") are stored with the curley brackets
+ * intact - use @code cupsParseOptions@ on the value to extract the
+ * collection attributes.
+ */
+
+int /* O - Number of options found */
+cupsParseOptions(
+ const char *arg, /* I - Argument to parse */
+ int num_options, /* I - Number of options */
+ cups_option_t **options) /* O - Options found */
+{
+ char *copyarg, /* Copy of input string */
+ *ptr, /* Pointer into string */
+ *name, /* Pointer to name */
+ *value, /* Pointer to value */
+ sep, /* Separator character */
+ quote; /* Quote character */
+
+
+ DEBUG_printf(("cupsParseOptions(arg=\"%s\", num_options=%d, options=%p)",
+ arg, num_options, options));
+
+ /*
+ * Range check input...
+ */
+
+ if (!arg)
+ {
+ DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
+ return (num_options);
+ }
+
+ if (!options || num_options < 0)
+ {
+ DEBUG_puts("1cupsParseOptions: Returning 0");
+ return (0);
+ }
+
+ /*
+ * Make a copy of the argument string and then divide it up...
+ */
+
+ if ((copyarg = strdup(arg)) == NULL)
+ {
+ DEBUG_puts("1cupsParseOptions: Unable to copy arg string");
+ DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
+ return (num_options);
+ }
+
+ if (*copyarg == '{')
+ {
+ /*
+ * Remove surrounding {} so we can parse "{name=value ... name=value}"...
+ */
+
+ if ((ptr = copyarg + strlen(copyarg) - 1) > copyarg && *ptr == '}')
+ {
+ *ptr = '\0';
+ ptr = copyarg + 1;
+ }
+ else
+ ptr = copyarg;
+ }
+ else
+ ptr = copyarg;
+
+ /*
+ * Skip leading spaces...
+ */
+
+ while (_cups_isspace(*ptr))
+ ptr ++;
+
+ /*
+ * Loop through the string...
+ */
+
+ while (*ptr != '\0')
+ {
+ /*
+ * Get the name up to a SPACE, =, or end-of-string...
+ */
+
+ name = ptr;
+ while (!strchr("\f\n\r\t\v =", *ptr) && *ptr)
+ ptr ++;
+
+ /*
+ * Avoid an empty name...
+ */
+
+ if (ptr == name)
+ break;
+
+ /*
+ * Skip trailing spaces...
+ */
+
+ while (_cups_isspace(*ptr))
+ *ptr++ = '\0';
+
+ if ((sep = *ptr) == '=')
+ *ptr++ = '\0';
+
+ DEBUG_printf(("2cupsParseOptions: name=\"%s\"", name));
+
+ if (sep != '=')
+ {
+ /*
+ * Boolean option...
+ */
+
+ if (!_cups_strncasecmp(name, "no", 2))
+ num_options = cupsAddOption(name + 2, "false", num_options,
+ options);
+ else
+ num_options = cupsAddOption(name, "true", num_options, options);
+
+ continue;
+ }
+
+ /*
+ * Remove = and parse the value...
+ */
+
+ value = ptr;
+
+ while (*ptr && !_cups_isspace(*ptr))
+ {
+ if (*ptr == ',')
+ ptr ++;
+ else if (*ptr == '\'' || *ptr == '\"')
+ {
+ /*
+ * Quoted string constant...
+ */
+
+ quote = *ptr;
+ _cups_strcpy(ptr, ptr + 1);
+
+ while (*ptr != quote && *ptr)
+ {
+ if (*ptr == '\\' && ptr[1])
+ _cups_strcpy(ptr, ptr + 1);
+
+ ptr ++;
+ }
+
+ if (*ptr)
+ _cups_strcpy(ptr, ptr + 1);
+ }
+ else if (*ptr == '{')
+ {
+ /*
+ * Collection value...
+ */
+
+ int depth;
+
+ for (depth = 0; *ptr; ptr ++)
+ {
+ if (*ptr == '{')
+ depth ++;
+ else if (*ptr == '}')
+ {
+ depth --;
+ if (!depth)
+ {
+ ptr ++;
+ break;
+ }
+ }
+ else if (*ptr == '\\' && ptr[1])
+ _cups_strcpy(ptr, ptr + 1);
+ }
+ }
+ else
+ {
+ /*
+ * Normal space-delimited string...
+ */
+
+ while (*ptr && !_cups_isspace(*ptr))
+ {
+ if (*ptr == '\\' && ptr[1])
+ _cups_strcpy(ptr, ptr + 1);
+
+ ptr ++;
+ }
+ }
+ }
+
+ if (*ptr != '\0')
+ *ptr++ = '\0';
+
+ DEBUG_printf(("2cupsParseOptions: value=\"%s\"", value));
+
+ /*
+ * Skip trailing whitespace...
+ */
+
+ while (_cups_isspace(*ptr))
+ ptr ++;
+
+ /*
+ * Add the string value...
+ */
+
+ num_options = cupsAddOption(name, value, num_options, options);
+ }
+
+ /*
+ * Free the copy of the argument we made and return the number of options
+ * found.
+ */
+
+ free(copyarg);
+
+ DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
+
+ return (num_options);
+}
+
+
+/*
+ * 'cupsRemoveOption()' - Remove an option from an option array.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - New number of options */
+cupsRemoveOption(
+ const char *name, /* I - Option name */
+ int num_options, /* I - Current number of options */
+ cups_option_t **options) /* IO - Options */
+{
+ int i; /* Looping var */
+ cups_option_t *option; /* Current option */
+
+
+ DEBUG_printf(("2cupsRemoveOption(name=\"%s\", num_options=%d, options=%p)",
+ name, num_options, options));
+
+ /*
+ * Range check input...
+ */
+
+ if (!name || num_options < 1 || !options)
+ {
+ DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
+ return (num_options);
+ }
+
+ /*
+ * Loop for the option...
+ */
+
+ for (i = num_options, option = *options; i > 0; i --, option ++)
+ if (!_cups_strcasecmp(name, option->name))
+ break;
+
+ if (i)
+ {
+ /*
+ * Remove this option from the array...
+ */
+
+ DEBUG_puts("4cupsRemoveOption: Found option, removing it...");
+
+ num_options --;
+ i --;
+
+ _cupsStrFree(option->name);
+ _cupsStrFree(option->value);
+
+ if (i > 0)
+ memmove(option, option + 1, i * sizeof(cups_option_t));
+ }
+
+ /*
+ * Return the new number of options...
+ */
+
+ DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
+ return (num_options);
+}
+
+
+/*
+ * '_cupsGet1284Values()' - Get 1284 device ID keys and values.
+ *
+ * The returned dictionary is a CUPS option array that can be queried with
+ * cupsGetOption and freed with cupsFreeOptions.
+ */
+
+int /* O - Number of key/value pairs */
+_cupsGet1284Values(
+ const char *device_id, /* I - IEEE-1284 device ID string */
+ cups_option_t **values) /* O - Array of key/value pairs */
+{
+ int num_values; /* Number of values */
+ char key[256], /* Key string */
+ value[256], /* Value string */
+ *ptr; /* Pointer into key/value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (values)
+ *values = NULL;
+
+ if (!device_id || !values)
+ return (0);
+
+ /*
+ * Parse the 1284 device ID value into keys and values. The format is
+ * repeating sequences of:
+ *
+ * [whitespace]key:value[whitespace];
+ */
+
+ num_values = 0;
+ while (*device_id)
+ {
+ while (_cups_isspace(*device_id))
+ device_id ++;
+
+ if (!*device_id)
+ break;
+
+ for (ptr = key; *device_id && *device_id != ':'; device_id ++)
+ if (ptr < (key + sizeof(key) - 1))
+ *ptr++ = *device_id;
+
+ if (!*device_id)
+ break;
+
+ while (ptr > key && _cups_isspace(ptr[-1]))
+ ptr --;
+
+ *ptr = '\0';
+ device_id ++;
+
+ while (_cups_isspace(*device_id))
+ device_id ++;
+
+ if (!*device_id)
+ break;
+
+ for (ptr = value; *device_id && *device_id != ';'; device_id ++)
+ if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = *device_id;
+
+ if (!*device_id)
+ break;
+
+ while (ptr > value && _cups_isspace(ptr[-1]))
+ ptr --;
+
+ *ptr = '\0';
+ device_id ++;
+
+ num_values = cupsAddOption(key, value, num_values, values);
+ }
+
+ return (num_values);
+}
+
+
+/*
+ * 'cups_compare_options()' - Compare two options.
+ */
+
+static int /* O - Result of comparison */
+cups_compare_options(cups_option_t *a, /* I - First option */
+ cups_option_t *b) /* I - Second option */
+{
+ return (_cups_strcasecmp(a->name, b->name));
+}
+
+
+/*
+ * 'cups_find_option()' - Find an option using a binary search.
+ */
+
+static int /* O - Index of match */
+cups_find_option(
+ const char *name, /* I - Option name */
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ int prev, /* I - Previous index */
+ int *rdiff) /* O - Difference of match */
+{
+ int left, /* Low mark for binary search */
+ right, /* High mark for binary search */
+ current, /* Current index */
+ diff; /* Result of comparison */
+ cups_option_t key; /* Search key */
+
+
+ DEBUG_printf(("7cups_find_option(name=\"%s\", num_options=%d, options=%p, "
+ "prev=%d, rdiff=%p)", name, num_options, options, prev,
+ rdiff));
+
+#ifdef DEBUG
+ for (left = 0; left < num_options; left ++)
+ DEBUG_printf(("9cups_find_option: options[%d].name=\"%s\", .value=\"%s\"",
+ left, options[left].name, options[left].value));
+#endif /* DEBUG */
+
+ key.name = (char *)name;
+
+ if (prev >= 0)
+ {
+ /*
+ * Start search on either side of previous...
+ */
+
+ if ((diff = cups_compare_options(&key, options + prev)) == 0 ||
+ (diff < 0 && prev == 0) ||
+ (diff > 0 && prev == (num_options - 1)))
+ {
+ *rdiff = diff;
+ return (prev);
+ }
+ else if (diff < 0)
+ {
+ /*
+ * Start with previous on right side...
+ */
+
+ left = 0;
+ right = prev;
+ }
+ else
+ {
+ /*
+ * Start wih previous on left side...
+ */
+
+ left = prev;
+ right = num_options - 1;
+ }
+ }
+ else
+ {
+ /*
+ * Start search in the middle...
+ */
+
+ left = 0;
+ right = num_options - 1;
+ }
+
+ do
+ {
+ current = (left + right) / 2;
+ diff = cups_compare_options(&key, options + current);
+
+ if (diff == 0)
+ break;
+ else if (diff < 0)
+ right = current;
+ else
+ left = current;
+ }
+ while ((right - left) > 1);
+
+ if (diff != 0)
+ {
+ /*
+ * Check the last 1 or 2 elements...
+ */
+
+ if ((diff = cups_compare_options(&key, options + left)) <= 0)
+ current = left;
+ else
+ {
+ diff = cups_compare_options(&key, options + right);
+ current = right;
+ }
+ }
+
+ /*
+ * Return the closest destination and the difference...
+ */
+
+ *rdiff = diff;
+
+ return (current);
+}
+
+
+/*
+ * End of "$Id: options.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/page.c b/cups/libs/cups/page.c
new file mode 100644
index 000000000..7c496c68c
--- /dev/null
+++ b/cups/libs/cups/page.c
@@ -0,0 +1,396 @@
+/*
+ * "$Id: page.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Page size functions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdPageSize() - Get the page size record for the given size.
+ * ppdPageSizeLimits() - Return the custom page size limits.
+ * ppdPageWidth() - Get the page width for the given size.
+ * ppdPageLength() - Get the page length for the given size.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+#include "debug-private.h"
+#include "ppd.h"
+
+
+/*
+ * 'ppdPageSize()' - Get the page size record for the named size.
+ */
+
+ppd_size_t * /* O - Size record for page or NULL */
+ppdPageSize(ppd_file_t *ppd, /* I - PPD file record */
+ const char *name) /* I - Size name */
+{
+ int i; /* Looping var */
+ ppd_size_t *size; /* Current page size */
+ double w, l; /* Width and length of page */
+ char *nameptr; /* Pointer into name */
+ struct lconv *loc; /* Locale data */
+ ppd_coption_t *coption; /* Custom option for page size */
+ ppd_cparam_t *cparam; /* Custom option parameter */
+
+
+ DEBUG_printf(("2ppdPageSize(ppd=%p, name=\"%s\")", ppd, name));
+
+ if (!ppd)
+ {
+ DEBUG_puts("3ppdPageSize: Bad PPD pointer, returning NULL...");
+ return (NULL);
+ }
+
+ if (name)
+ {
+ if (!strncmp(name, "Custom.", 7) && ppd->variable_sizes)
+ {
+ /*
+ * Find the custom page size...
+ */
+
+ for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+ if (!strcmp("Custom", size->name))
+ break;
+
+ if (!i)
+ {
+ DEBUG_puts("3ppdPageSize: No custom sizes, returning NULL...");
+ return (NULL);
+ }
+
+ /*
+ * Variable size; size name can be one of the following:
+ *
+ * Custom.WIDTHxLENGTHin - Size in inches
+ * Custom.WIDTHxLENGTHft - Size in feet
+ * Custom.WIDTHxLENGTHcm - Size in centimeters
+ * Custom.WIDTHxLENGTHmm - Size in millimeters
+ * Custom.WIDTHxLENGTHm - Size in meters
+ * Custom.WIDTHxLENGTH[pt] - Size in points
+ */
+
+ loc = localeconv();
+ w = _cupsStrScand(name + 7, &nameptr, loc);
+ if (!nameptr || *nameptr != 'x')
+ return (NULL);
+
+ l = _cupsStrScand(nameptr + 1, &nameptr, loc);
+ if (!nameptr)
+ return (NULL);
+
+ if (!_cups_strcasecmp(nameptr, "in"))
+ {
+ w *= 72.0;
+ l *= 72.0;
+ }
+ else if (!_cups_strcasecmp(nameptr, "ft"))
+ {
+ w *= 12.0 * 72.0;
+ l *= 12.0 * 72.0;
+ }
+ else if (!_cups_strcasecmp(nameptr, "mm"))
+ {
+ w *= 72.0 / 25.4;
+ l *= 72.0 / 25.4;
+ }
+ else if (!_cups_strcasecmp(nameptr, "cm"))
+ {
+ w *= 72.0 / 2.54;
+ l *= 72.0 / 2.54;
+ }
+ else if (!_cups_strcasecmp(nameptr, "m"))
+ {
+ w *= 72.0 / 0.0254;
+ l *= 72.0 / 0.0254;
+ }
+
+ size->width = (float)w;
+ size->length = (float)l;
+ size->left = ppd->custom_margins[0];
+ size->bottom = ppd->custom_margins[1];
+ size->right = (float)(w - ppd->custom_margins[2]);
+ size->top = (float)(l - ppd->custom_margins[3]);
+
+ /*
+ * Update the custom option records for the page size, too...
+ */
+
+ if ((coption = ppdFindCustomOption(ppd, "PageSize")) != NULL)
+ {
+ if ((cparam = ppdFindCustomParam(coption, "Width")) != NULL)
+ cparam->current.custom_points = (float)w;
+
+ if ((cparam = ppdFindCustomParam(coption, "Height")) != NULL)
+ cparam->current.custom_points = (float)l;
+ }
+
+ /*
+ * Return the page size...
+ */
+
+ DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
+ size->name, size->width, size->length));
+
+ return (size);
+ }
+ else
+ {
+ /*
+ * Lookup by name...
+ */
+
+ for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+ if (!_cups_strcasecmp(name, size->name))
+ {
+ DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
+ size->name, size->width, size->length));
+
+ return (size);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Find default...
+ */
+
+ for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+ if (size->marked)
+ {
+ DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
+ size->name, size->width, size->length));
+
+ return (size);
+ }
+ }
+
+ DEBUG_puts("3ppdPageSize: Size not found, returning NULL");
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdPageSizeLimits()' - Return the custom page size limits.
+ *
+ * This function returns the minimum and maximum custom page sizes and printable
+ * areas based on the currently-marked (selected) options.
+ *
+ * If the specified PPD file does not support custom page sizes, both
+ * "minimum" and "maximum" are filled with zeroes.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+int /* O - 1 if custom sizes are supported, 0 otherwise */
+ppdPageSizeLimits(ppd_file_t *ppd, /* I - PPD file record */
+ ppd_size_t *minimum, /* O - Minimum custom size */
+ ppd_size_t *maximum) /* O - Maximum custom size */
+{
+ ppd_choice_t *qualifier2, /* Second media qualifier */
+ *qualifier3; /* Third media qualifier */
+ ppd_attr_t *attr; /* Attribute */
+ float width, /* Min/max width */
+ length; /* Min/max length */
+ char spec[PPD_MAX_NAME]; /* Selector for min/max */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !ppd->variable_sizes || !minimum || !maximum)
+ {
+ if (minimum)
+ memset(minimum, 0, sizeof(ppd_size_t));
+
+ if (maximum)
+ memset(maximum, 0, sizeof(ppd_size_t));
+
+ return (0);
+ }
+
+ /*
+ * See if we have the cupsMediaQualifier2 and cupsMediaQualifier3 attributes...
+ */
+
+ cupsArraySave(ppd->sorted_attrs);
+
+ if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier2", NULL)) != NULL &&
+ attr->value)
+ qualifier2 = ppdFindMarkedChoice(ppd, attr->value);
+ else
+ qualifier2 = NULL;
+
+ if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier3", NULL)) != NULL &&
+ attr->value)
+ qualifier3 = ppdFindMarkedChoice(ppd, attr->value);
+ else
+ qualifier3 = NULL;
+
+ /*
+ * Figure out the current minimum width and length...
+ */
+
+ width = ppd->custom_min[0];
+ length = ppd->custom_min[1];
+
+ if (qualifier2)
+ {
+ /*
+ * Try getting cupsMinSize...
+ */
+
+ if (qualifier3)
+ {
+ snprintf(spec, sizeof(spec), ".%s.%s", qualifier2->choice,
+ qualifier3->choice);
+ attr = ppdFindAttr(ppd, "cupsMinSize", spec);
+ }
+ else
+ attr = NULL;
+
+ if (!attr)
+ {
+ snprintf(spec, sizeof(spec), ".%s.", qualifier2->choice);
+ attr = ppdFindAttr(ppd, "cupsMinSize", spec);
+ }
+
+ if (!attr && qualifier3)
+ {
+ snprintf(spec, sizeof(spec), "..%s", qualifier3->choice);
+ attr = ppdFindAttr(ppd, "cupsMinSize", spec);
+ }
+
+ if ((attr && attr->value &&
+ sscanf(attr->value, "%f%f", &width, &length) != 2) || !attr)
+ {
+ width = ppd->custom_min[0];
+ length = ppd->custom_min[1];
+ }
+ }
+
+ minimum->width = width;
+ minimum->length = length;
+ minimum->left = ppd->custom_margins[0];
+ minimum->bottom = ppd->custom_margins[1];
+ minimum->right = width - ppd->custom_margins[2];
+ minimum->top = length - ppd->custom_margins[3];
+
+ /*
+ * Figure out the current maximum width and length...
+ */
+
+ width = ppd->custom_max[0];
+ length = ppd->custom_max[1];
+
+ if (qualifier2)
+ {
+ /*
+ * Try getting cupsMaxSize...
+ */
+
+ if (qualifier3)
+ {
+ snprintf(spec, sizeof(spec), ".%s.%s", qualifier2->choice,
+ qualifier3->choice);
+ attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
+ }
+ else
+ attr = NULL;
+
+ if (!attr)
+ {
+ snprintf(spec, sizeof(spec), ".%s.", qualifier2->choice);
+ attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
+ }
+
+ if (!attr && qualifier3)
+ {
+ snprintf(spec, sizeof(spec), "..%s", qualifier3->choice);
+ attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
+ }
+
+ if (!attr ||
+ (attr->value && sscanf(attr->value, "%f%f", &width, &length) != 2))
+ {
+ width = ppd->custom_max[0];
+ length = ppd->custom_max[1];
+ }
+ }
+
+ maximum->width = width;
+ maximum->length = length;
+ maximum->left = ppd->custom_margins[0];
+ maximum->bottom = ppd->custom_margins[1];
+ maximum->right = width - ppd->custom_margins[2];
+ maximum->top = length - ppd->custom_margins[3];
+
+ /*
+ * Return the min and max...
+ */
+
+ cupsArrayRestore(ppd->sorted_attrs);
+
+ return (1);
+}
+
+
+/*
+ * 'ppdPageWidth()' - Get the page width for the given size.
+ */
+
+float /* O - Width of page in points or 0.0 */
+ppdPageWidth(ppd_file_t *ppd, /* I - PPD file record */
+ const char *name) /* I - Size name */
+{
+ ppd_size_t *size; /* Page size */
+
+
+ if ((size = ppdPageSize(ppd, name)) == NULL)
+ return (0.0);
+ else
+ return (size->width);
+}
+
+
+/*
+ * 'ppdPageLength()' - Get the page length for the given size.
+ */
+
+float /* O - Length of page in points or 0.0 */
+ppdPageLength(ppd_file_t *ppd, /* I - PPD file */
+ const char *name) /* I - Size name */
+{
+ ppd_size_t *size; /* Page size */
+
+
+ if ((size = ppdPageSize(ppd, name)) == NULL)
+ return (0.0);
+ else
+ return (size->length);
+}
+
+
+/*
+ * End of "$Id: page.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/ppd-cache.c b/cups/libs/cups/ppd-cache.c
new file mode 100644
index 000000000..75eef2d9c
--- /dev/null
+++ b/cups/libs/cups/ppd-cache.c
@@ -0,0 +1,2732 @@
+/*
+ * "$Id: ppd-cache.c 11833 2014-04-24 15:04:15Z msweet $"
+ *
+ * PPD cache implementation for CUPS.
+ *
+ * Copyright 2010-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _ppdCacheCreateWithFile() - Create PPD cache and mapping data from a
+ * written file.
+ * _ppdCacheCreateWithPPD() - Create PWG mapping data from a PPD file.
+ * _ppdCacheDestroy() - Free all memory used for PWG mapping data.
+ * _ppdCacheGetBin() - Get the PWG output-bin keyword associated with
+ * a PPD OutputBin.
+ * _ppdCacheGetInputSlot() - Get the PPD InputSlot associated with the job
+ * attributes or a keyword string.
+ * _ppdCacheGetMediaType() - Get the PPD MediaType associated with the job
+ * attributes or a keyword string.
+ * _ppdCacheGetOutputBin() - Get the PPD OutputBin associated with the
+ * keyword string.
+ * _ppdCacheGetPageSize() - Get the PPD PageSize associated with the job
+ * attributes or a keyword string.
+ * _ppdCacheGetSize() - Get the PWG size associated with a PPD
+ * PageSize.
+ * _ppdCacheGetSource() - Get the PWG media-source associated with a PPD
+ * InputSlot.
+ * _ppdCacheGetType() - Get the PWG media-type associated with a PPD
+ * MediaType.
+ * _ppdCacheWriteFile() - Write PWG mapping data to a file.
+ * _pwgInputSlotForSource() - Get the InputSlot name for the given PWG
+ * media-source.
+ * _pwgMediaTypeForType() - Get the MediaType name for the given PWG
+ * media-type.
+ * _pwgPageSizeForMedia() - Get the PageSize name for the given media.
+ * pwg_ppdize_name() - Convert an IPP keyword to a PPD keyword.
+ * pwg_unppdize_name() - Convert a PPD keyword to a lowercase IPP
+ * keyword.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <math.h>
+
+
+/*
+ * Macro to test for two almost-equal PWG measurements.
+ */
+
+#define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
+
+
+/*
+ * Local functions...
+ */
+
+static int pwg_compare_finishings(_pwg_finishings_t *a,
+ _pwg_finishings_t *b);
+static void pwg_free_finishings(_pwg_finishings_t *f);
+static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
+static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
+ const char *dashchars);
+
+
+/*
+ * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
+ * written file.
+ *
+ * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
+ * file.
+ */
+
+_ppd_cache_t * /* O - PPD cache and mapping data */
+_ppdCacheCreateWithFile(
+ const char *filename, /* I - File to read */
+ ipp_t **attrs) /* IO - IPP attributes, if any */
+{
+ cups_file_t *fp; /* File */
+ _ppd_cache_t *pc; /* PWG mapping data */
+ pwg_size_t *size; /* Current size */
+ pwg_map_t *map; /* Current map */
+ _pwg_finishings_t *finishings; /* Current finishings option */
+ int linenum, /* Current line number */
+ num_bins, /* Number of bins in file */
+ num_sizes, /* Number of sizes in file */
+ num_sources, /* Number of sources in file */
+ num_types; /* Number of types in file */
+ char line[2048], /* Current line */
+ *value, /* Pointer to value in line */
+ *valueptr, /* Pointer into value */
+ pwg_keyword[128], /* PWG keyword */
+ ppd_keyword[PPD_MAX_NAME];
+ /* PPD keyword */
+ _pwg_print_color_mode_t print_color_mode;
+ /* Print color mode for preset */
+ _pwg_print_quality_t print_quality; /* Print quality for preset */
+
+
+ DEBUG_printf(("_ppdCacheCreateWithFile(filename=\"%s\")", filename));
+
+ /*
+ * Range check input...
+ */
+
+ if (attrs)
+ *attrs = NULL;
+
+ if (!filename)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (NULL);
+ }
+
+ /*
+ * Open the file...
+ */
+
+ if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ return (NULL);
+ }
+
+ /*
+ * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
+ */
+
+ if (!cupsFileGets(fp, line, sizeof(line)))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
+ cupsFileClose(fp);
+ return (NULL);
+ }
+
+ if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ DEBUG_printf(("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line));
+ cupsFileClose(fp);
+ return (NULL);
+ }
+
+ if (atoi(line + 16) != _PPD_CACHE_VERSION)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
+ DEBUG_printf(("_ppdCacheCreateWithFile: Cache file has version %s, "
+ "expected %d.", line + 16, _PPD_CACHE_VERSION));
+ cupsFileClose(fp);
+ return (NULL);
+ }
+
+ /*
+ * Allocate the mapping data structure...
+ */
+
+ if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
+ goto create_error;
+ }
+
+ pc->max_copies = 9999;
+
+ /*
+ * Read the file...
+ */
+
+ linenum = 0;
+ num_bins = 0;
+ num_sizes = 0;
+ num_sources = 0;
+ num_types = 0;
+
+ while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", "
+ "linenum=%d", line, value, linenum));
+
+ if (!value)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Missing value on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+ else if (!_cups_strcasecmp(line, "Filter"))
+ {
+ if (!pc->filters)
+ pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree);
+
+ cupsArrayAdd(pc->filters, value);
+ }
+ else if (!_cups_strcasecmp(line, "PreFilter"))
+ {
+ if (!pc->prefilters)
+ pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree);
+
+ cupsArrayAdd(pc->prefilters, value);
+ }
+ else if (!_cups_strcasecmp(line, "Product"))
+ {
+ pc->product = _cupsStrAlloc(value);
+ }
+ else if (!_cups_strcasecmp(line, "SingleFile"))
+ {
+ pc->single_file = !_cups_strcasecmp(value, "true");
+ }
+ else if (!_cups_strcasecmp(line, "IPP"))
+ {
+ off_t pos = cupsFileTell(fp), /* Position in file */
+ length = strtol(value, NULL, 10);
+ /* Length of IPP attributes */
+
+ if (attrs && *attrs)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+ else if (length <= 0)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (attrs)
+ {
+ /*
+ * Read IPP attributes into the provided variable...
+ */
+
+ *attrs = ippNew();
+
+ if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
+ *attrs) != IPP_STATE_DATA)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+ }
+ else
+ {
+ /*
+ * Skip the IPP data entirely...
+ */
+
+ cupsFileSeek(fp, pos + length);
+ }
+
+ if (cupsFileTell(fp) != (pos + length))
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+ }
+ else if (!_cups_strcasecmp(line, "NumBins"))
+ {
+ if (num_bins > 0)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumBins value %d on line "
+ "%d.", num_sizes, linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((pc->bins = calloc(num_bins, sizeof(pwg_map_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d bins.",
+ num_sizes));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto create_error;
+ }
+ }
+ else if (!_cups_strcasecmp(line, "Bin"))
+ {
+ if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (pc->num_bins >= num_bins)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Too many Bin's on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ map = pc->bins + pc->num_bins;
+ map->pwg = _cupsStrAlloc(pwg_keyword);
+ map->ppd = _cupsStrAlloc(ppd_keyword);
+
+ pc->num_bins ++;
+ }
+ else if (!_cups_strcasecmp(line, "NumSizes"))
+ {
+ if (num_sizes > 0)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSizes value %d on line "
+ "%d.", num_sizes, linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (num_sizes > 0)
+ {
+ if ((pc->sizes = calloc(num_sizes, sizeof(pwg_size_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
+ num_sizes));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto create_error;
+ }
+ }
+ }
+ else if (!_cups_strcasecmp(line, "Size"))
+ {
+ if (pc->num_sizes >= num_sizes)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Too many Size's on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ size = pc->sizes + pc->num_sizes;
+
+ if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
+ &(size->width), &(size->length), &(size->left),
+ &(size->bottom), &(size->right), &(size->top)) != 8)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad Size on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ size->map.pwg = _cupsStrAlloc(pwg_keyword);
+ size->map.ppd = _cupsStrAlloc(ppd_keyword);
+
+ pc->num_sizes ++;
+ }
+ else if (!_cups_strcasecmp(line, "CustomSize"))
+ {
+ if (pc->custom_max_width > 0)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Too many CustomSize's on line "
+ "%d.", linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
+ &(pc->custom_max_length), &(pc->custom_min_width),
+ &(pc->custom_min_length), &(pc->custom_size.left),
+ &(pc->custom_size.bottom), &(pc->custom_size.right),
+ &(pc->custom_size.top)) != 8)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad CustomSize on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
+ pc->custom_max_width, pc->custom_max_length, NULL);
+ pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
+
+ pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
+ pc->custom_min_width, pc->custom_min_length, NULL);
+ pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
+ }
+ else if (!_cups_strcasecmp(line, "SourceOption"))
+ {
+ pc->source_option = _cupsStrAlloc(value);
+ }
+ else if (!_cups_strcasecmp(line, "NumSources"))
+ {
+ if (num_sources > 0)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
+ "times.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSources value %d on "
+ "line %d.", num_sources, linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((pc->sources = calloc(num_sources, sizeof(pwg_map_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sources.",
+ num_sources));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto create_error;
+ }
+ }
+ else if (!_cups_strcasecmp(line, "Source"))
+ {
+ if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad Source on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (pc->num_sources >= num_sources)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Too many Source's on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ map = pc->sources + pc->num_sources;
+ map->pwg = _cupsStrAlloc(pwg_keyword);
+ map->ppd = _cupsStrAlloc(ppd_keyword);
+
+ pc->num_sources ++;
+ }
+ else if (!_cups_strcasecmp(line, "NumTypes"))
+ {
+ if (num_types > 0)
+ {
+ DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((num_types = atoi(value)) <= 0 || num_types > 65536)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumTypes value %d on "
+ "line %d.", num_types, linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if ((pc->types = calloc(num_types, sizeof(pwg_map_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d types.",
+ num_types));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto create_error;
+ }
+ }
+ else if (!_cups_strcasecmp(line, "Type"))
+ {
+ if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad Type on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (pc->num_types >= num_types)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Too many Type's on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ map = pc->types + pc->num_types;
+ map->pwg = _cupsStrAlloc(pwg_keyword);
+ map->ppd = _cupsStrAlloc(ppd_keyword);
+
+ pc->num_types ++;
+ }
+ else if (!_cups_strcasecmp(line, "Preset"))
+ {
+ /*
+ * Preset output-mode print-quality name=value ...
+ */
+
+ print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
+ print_quality = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
+
+ if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
+ print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
+ print_quality < _PWG_PRINT_QUALITY_DRAFT ||
+ print_quality >= _PWG_PRINT_QUALITY_MAX ||
+ valueptr == value || !*valueptr)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Bad Preset on line %d.",
+ linenum));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ pc->num_presets[print_color_mode][print_quality] =
+ cupsParseOptions(valueptr, 0,
+ pc->presets[print_color_mode] + print_quality);
+ }
+ else if (!_cups_strcasecmp(line, "SidesOption"))
+ pc->sides_option = _cupsStrAlloc(value);
+ else if (!_cups_strcasecmp(line, "Sides1Sided"))
+ pc->sides_1sided = _cupsStrAlloc(value);
+ else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
+ pc->sides_2sided_long = _cupsStrAlloc(value);
+ else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
+ pc->sides_2sided_short = _cupsStrAlloc(value);
+ else if (!_cups_strcasecmp(line, "Finishings"))
+ {
+ if (!pc->finishings)
+ pc->finishings =
+ cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
+ NULL, NULL, 0, NULL,
+ (cups_afree_func_t)pwg_free_finishings);
+
+ if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
+ goto create_error;
+
+ finishings->value = strtol(value, &valueptr, 10);
+ finishings->num_options = cupsParseOptions(valueptr, 0,
+ &(finishings->options));
+
+ cupsArrayAdd(pc->finishings, finishings);
+ }
+ else if (!_cups_strcasecmp(line, "MaxCopies"))
+ pc->max_copies = atoi(value);
+ else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
+ pc->charge_info_uri = _cupsStrAlloc(value);
+ else if (!_cups_strcasecmp(line, "JobAccountId"))
+ pc->account_id = !_cups_strcasecmp(value, "true");
+ else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
+ pc->accounting_user_id = !_cups_strcasecmp(value, "true");
+ else if (!_cups_strcasecmp(line, "JobPassword"))
+ pc->password = _cupsStrAlloc(value);
+ else if (!_cups_strcasecmp(line, "Mandatory"))
+ {
+ if (pc->mandatory)
+ _cupsArrayAddStrings(pc->mandatory, value, ' ');
+ else
+ pc->mandatory = _cupsArrayNewStrings(value, ' ');
+ }
+ else if (!_cups_strcasecmp(line, "SupportFile"))
+ {
+ if (!pc->support_files)
+ pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree);
+
+ cupsArrayAdd(pc->support_files, value);
+ }
+ else
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Unknown %s on line %d.", line,
+ linenum));
+ }
+ }
+
+ if (pc->num_sizes < num_sizes)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).",
+ pc->num_sizes, num_sizes));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (pc->num_sources < num_sources)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sources (%d < %d).",
+ pc->num_sources, num_sources));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ if (pc->num_types < num_types)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithFile: Not enough types (%d < %d).",
+ pc->num_types, num_types));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+ goto create_error;
+ }
+
+ cupsFileClose(fp);
+
+ return (pc);
+
+ /*
+ * If we get here the file was bad - free any data and return...
+ */
+
+ create_error:
+
+ cupsFileClose(fp);
+ _ppdCacheDestroy(pc);
+
+ if (attrs)
+ {
+ ippDelete(*attrs);
+ *attrs = NULL;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
+ */
+
+_ppd_cache_t * /* O - PPD cache and mapping data */
+_ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
+{
+ int i, j, k; /* Looping vars */
+ _ppd_cache_t *pc; /* PWG mapping data */
+ ppd_option_t *input_slot, /* InputSlot option */
+ *media_type, /* MediaType option */
+ *output_bin, /* OutputBin option */
+ *color_model, /* ColorModel option */
+ *duplex; /* Duplex option */
+ ppd_choice_t *choice; /* Current InputSlot/MediaType */
+ pwg_map_t *map; /* Current source/type map */
+ ppd_attr_t *ppd_attr; /* Current PPD preset attribute */
+ int num_options; /* Number of preset options and props */
+ cups_option_t *options; /* Preset options and properties */
+ ppd_size_t *ppd_size; /* Current PPD size */
+ pwg_size_t *pwg_size; /* Current PWG size */
+ char pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
+ /* PWG keyword string */
+ ppd_name[PPD_MAX_NAME];
+ /* Normalized PPD name */
+ const char *pwg_name; /* Standard PWG media name */
+ pwg_media_t *pwg_media; /* PWG media data */
+ _pwg_print_color_mode_t pwg_print_color_mode;
+ /* print-color-mode index */
+ _pwg_print_quality_t pwg_print_quality;
+ /* print-quality index */
+ int similar; /* Are the old and new size similar? */
+ pwg_size_t *old_size; /* Current old size */
+ int old_imageable, /* Old imageable length in 2540ths */
+ old_borderless, /* Old borderless state */
+ old_known_pwg; /* Old PWG name is well-known */
+ int new_width, /* New width in 2540ths */
+ new_length, /* New length in 2540ths */
+ new_left, /* New left margin in 2540ths */
+ new_bottom, /* New bottom margin in 2540ths */
+ new_right, /* New right margin in 2540ths */
+ new_top, /* New top margin in 2540ths */
+ new_imageable, /* New imageable length in 2540ths */
+ new_borderless, /* New borderless state */
+ new_known_pwg; /* New PWG name is well-known */
+ pwg_size_t *new_size; /* New size to add, if any */
+ const char *filter; /* Current filter */
+ _pwg_finishings_t *finishings; /* Current finishings value */
+
+
+ DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd)
+ return (NULL);
+
+ /*
+ * Allocate memory...
+ */
+
+ if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
+ {
+ DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
+ goto create_error;
+ }
+
+ /*
+ * Copy and convert size data...
+ */
+
+ if (ppd->num_sizes > 0)
+ {
+ if ((pc->sizes = calloc(ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
+ "pwg_size_t's.", ppd->num_sizes));
+ goto create_error;
+ }
+
+ for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
+ i > 0;
+ i --, ppd_size ++)
+ {
+ /*
+ * Don't copy over custom size...
+ */
+
+ if (!_cups_strcasecmp(ppd_size->name, "Custom"))
+ continue;
+
+ /*
+ * Convert the PPD size name to the corresponding PWG keyword name.
+ */
+
+ if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL)
+ {
+ /*
+ * Standard name, do we have conflicts?
+ */
+
+ for (j = 0; j < pc->num_sizes; j ++)
+ if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
+ {
+ pwg_media = NULL;
+ break;
+ }
+ }
+
+ if (pwg_media)
+ {
+ /*
+ * Standard name and no conflicts, use it!
+ */
+
+ pwg_name = pwg_media->pwg;
+ new_known_pwg = 1;
+ }
+ else
+ {
+ /*
+ * Not a standard name; convert it to a PWG vendor name of the form:
+ *
+ * pp_lowerppd_WIDTHxHEIGHTuu
+ */
+
+ pwg_name = pwg_keyword;
+ new_known_pwg = 0;
+
+ pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
+ pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
+ PWG_FROM_POINTS(ppd_size->width),
+ PWG_FROM_POINTS(ppd_size->length), NULL);
+ }
+
+ /*
+ * If we have a similar paper with non-zero margins then we only want to
+ * keep it if it has a larger imageable area length. The NULL check is for
+ * dimensions that are <= 0...
+ */
+
+ if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width),
+ PWG_FROM_POINTS(ppd_size->length))) == NULL)
+ continue;
+
+ new_width = pwg_media->width;
+ new_length = pwg_media->length;
+ new_left = PWG_FROM_POINTS(ppd_size->left);
+ new_bottom = PWG_FROM_POINTS(ppd_size->bottom);
+ new_right = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
+ new_top = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
+ new_imageable = new_length - new_top - new_bottom;
+ new_borderless = new_bottom == 0 && new_top == 0 &&
+ new_left == 0 && new_right == 0;
+
+ for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
+ k > 0 && !similar;
+ k --, old_size ++)
+ {
+ old_imageable = old_size->length - old_size->top - old_size->bottom;
+ old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
+ old_size->right == 0 && old_size->top == 0;
+ old_known_pwg = strncmp(old_size->map.pwg, "oe_", 3) &&
+ strncmp(old_size->map.pwg, "om_", 3);
+
+ similar = old_borderless == new_borderless &&
+ _PWG_EQUIVALENT(old_size->width, new_width) &&
+ _PWG_EQUIVALENT(old_size->length, new_length);
+
+ if (similar &&
+ (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
+ {
+ /*
+ * The new paper has a larger imageable area so it could replace
+ * the older paper. Regardless of the imageable area, we always
+ * prefer the size with a well-known PWG name.
+ */
+
+ new_size = old_size;
+ _cupsStrFree(old_size->map.ppd);
+ _cupsStrFree(old_size->map.pwg);
+ }
+ }
+
+ if (!similar)
+ {
+ /*
+ * The paper was unique enough to deserve its own entry so add it to the
+ * end.
+ */
+
+ new_size = pwg_size ++;
+ pc->num_sizes ++;
+ }
+
+ if (new_size)
+ {
+ /*
+ * Save this size...
+ */
+
+ new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
+ new_size->map.pwg = _cupsStrAlloc(pwg_name);
+ new_size->width = new_width;
+ new_size->length = new_length;
+ new_size->left = new_left;
+ new_size->bottom = new_bottom;
+ new_size->right = new_right;
+ new_size->top = new_top;
+ }
+ }
+ }
+
+ if (ppd->variable_sizes)
+ {
+ /*
+ * Generate custom size data...
+ */
+
+ pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
+ PWG_FROM_POINTS(ppd->custom_max[0]),
+ PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
+ pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
+ pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]);
+ pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]);
+
+ pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
+ PWG_FROM_POINTS(ppd->custom_min[0]),
+ PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
+ pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
+ pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]);
+ pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]);
+
+ pc->custom_size.left = PWG_FROM_POINTS(ppd->custom_margins[0]);
+ pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
+ pc->custom_size.right = PWG_FROM_POINTS(ppd->custom_margins[2]);
+ pc->custom_size.top = PWG_FROM_POINTS(ppd->custom_margins[3]);
+ }
+
+ /*
+ * Copy and convert InputSlot data...
+ */
+
+ if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
+ input_slot = ppdFindOption(ppd, "HPPaperSource");
+
+ if (input_slot)
+ {
+ pc->source_option = _cupsStrAlloc(input_slot->keyword);
+
+ if ((pc->sources = calloc(input_slot->num_choices,
+ sizeof(pwg_map_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
+ "pwg_map_t's for InputSlot.", input_slot->num_choices));
+ goto create_error;
+ }
+
+ pc->num_sources = input_slot->num_choices;
+
+ for (i = input_slot->num_choices, choice = input_slot->choices,
+ map = pc->sources;
+ i > 0;
+ i --, choice ++, map ++)
+ {
+ if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
+ !_cups_strcasecmp(choice->choice, "Default"))
+ pwg_name = "auto";
+ else if (!_cups_strcasecmp(choice->choice, "Cassette"))
+ pwg_name = "main";
+ else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
+ pwg_name = "photo";
+ else if (!_cups_strcasecmp(choice->choice, "CDTray"))
+ pwg_name = "disc";
+ else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
+ !_cups_strcasecmp(choice->choice, "MP") ||
+ !_cups_strcasecmp(choice->choice, "MPTray"))
+ pwg_name = "by-pass-tray";
+ else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
+ pwg_name = "large-capacity";
+ else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
+ pwg_name = "bottom";
+ else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
+ pwg_name = "middle";
+ else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
+ pwg_name = "top";
+ else if (!_cups_strncasecmp(choice->choice, "Side", 4))
+ pwg_name = "side";
+ else if (!_cups_strcasecmp(choice->choice, "Roll"))
+ pwg_name = "main-roll";
+ else
+ {
+ /*
+ * Convert PPD name to lowercase...
+ */
+
+ pwg_name = pwg_keyword;
+ pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
+ "_");
+ }
+
+ map->pwg = _cupsStrAlloc(pwg_name);
+ map->ppd = _cupsStrAlloc(choice->choice);
+ }
+ }
+
+ /*
+ * Copy and convert MediaType data...
+ */
+
+ if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
+ {
+ if ((pc->types = calloc(media_type->num_choices,
+ sizeof(pwg_map_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
+ "pwg_map_t's for MediaType.", media_type->num_choices));
+ goto create_error;
+ }
+
+ pc->num_types = media_type->num_choices;
+
+ for (i = media_type->num_choices, choice = media_type->choices,
+ map = pc->types;
+ i > 0;
+ i --, choice ++, map ++)
+ {
+ if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
+ !_cups_strcasecmp(choice->choice, "Any") ||
+ !_cups_strcasecmp(choice->choice, "Default"))
+ pwg_name = "auto";
+ else if (!_cups_strncasecmp(choice->choice, "Card", 4))
+ pwg_name = "cardstock";
+ else if (!_cups_strncasecmp(choice->choice, "Env", 3))
+ pwg_name = "envelope";
+ else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
+ pwg_name = "photographic-glossy";
+ else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
+ pwg_name = "photographic-high-gloss";
+ else if (!_cups_strcasecmp(choice->choice, "Matte"))
+ pwg_name = "photographic-matte";
+ else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
+ pwg_name = "stationery";
+ else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
+ pwg_name = "stationery-coated";
+ else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
+ pwg_name = "stationery-inkjet";
+ else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
+ pwg_name = "stationery-letterhead";
+ else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
+ pwg_name = "stationery-preprinted";
+ else if (!_cups_strcasecmp(choice->choice, "Recycled"))
+ pwg_name = "stationery-recycled";
+ else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
+ pwg_name = "transparency";
+ else
+ {
+ /*
+ * Convert PPD name to lowercase...
+ */
+
+ pwg_name = pwg_keyword;
+ pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
+ "_");
+ }
+
+ map->pwg = _cupsStrAlloc(pwg_name);
+ map->ppd = _cupsStrAlloc(choice->choice);
+ }
+ }
+
+ /*
+ * Copy and convert OutputBin data...
+ */
+
+ if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
+ {
+ if ((pc->bins = calloc(output_bin->num_choices,
+ sizeof(pwg_map_t))) == NULL)
+ {
+ DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
+ "pwg_map_t's for OutputBin.", output_bin->num_choices));
+ goto create_error;
+ }
+
+ pc->num_bins = output_bin->num_choices;
+
+ for (i = output_bin->num_choices, choice = output_bin->choices,
+ map = pc->bins;
+ i > 0;
+ i --, choice ++, map ++)
+ {
+ pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
+
+ map->pwg = _cupsStrAlloc(pwg_keyword);
+ map->ppd = _cupsStrAlloc(choice->choice);
+ }
+ }
+
+ if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
+ {
+ /*
+ * Copy and convert APPrinterPreset (output-mode + print-quality) data...
+ */
+
+ const char *quality, /* com.apple.print.preset.quality value */
+ *output_mode, /* com.apple.print.preset.output-mode value */
+ *color_model_val, /* ColorModel choice */
+ *graphicsType, /* com.apple.print.preset.graphicsType value */
+ *media_front_coating; /* com.apple.print.preset.media-front-coating value */
+
+ do
+ {
+ num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
+ _PPD_PARSE_ALL);
+
+ if ((quality = cupsGetOption("com.apple.print.preset.quality",
+ num_options, options)) != NULL)
+ {
+ /*
+ * Get the print-quality for this preset...
+ */
+
+ if (!strcmp(quality, "low"))
+ pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
+ else if (!strcmp(quality, "high"))
+ pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
+ else
+ pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
+
+ /*
+ * Ignore graphicsType "Photo" presets that are not high quality.
+ */
+
+ graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
+ num_options, options);
+
+ if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
+ !strcmp(graphicsType, "Photo"))
+ continue;
+
+ /*
+ * Ignore presets for normal and draft quality where the coating
+ * isn't "none" or "autodetect".
+ */
+
+ media_front_coating = cupsGetOption(
+ "com.apple.print.preset.media-front-coating",
+ num_options, options);
+
+ if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
+ media_front_coating &&
+ strcmp(media_front_coating, "none") &&
+ strcmp(media_front_coating, "autodetect"))
+ continue;
+
+ /*
+ * Get the output mode for this preset...
+ */
+
+ output_mode = cupsGetOption("com.apple.print.preset.output-mode",
+ num_options, options);
+ color_model_val = cupsGetOption("ColorModel", num_options, options);
+
+ if (output_mode)
+ {
+ if (!strcmp(output_mode, "monochrome"))
+ pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
+ else
+ pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
+ }
+ else if (color_model_val)
+ {
+ if (!_cups_strcasecmp(color_model_val, "Gray"))
+ pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
+ else
+ pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
+ }
+ else
+ pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
+
+ /*
+ * Save the options for this combination as needed...
+ */
+
+ if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
+ pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
+ _ppdParseOptions(ppd_attr->value, 0,
+ pc->presets[pwg_print_color_mode] +
+ pwg_print_quality, _PPD_PARSE_OPTIONS);
+ }
+
+ cupsFreeOptions(num_options, options);
+ }
+ while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
+ }
+
+ if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
+ !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
+ !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
+ {
+ /*
+ * Try adding some common color options to create grayscale presets. These
+ * are listed in order of popularity...
+ */
+
+ const char *color_option = NULL, /* Color control option */
+ *gray_choice = NULL; /* Choice to select grayscale */
+
+ if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
+ ppdFindChoice(color_model, "Gray"))
+ {
+ color_option = "ColorModel";
+ gray_choice = "Gray";
+ }
+ else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
+ ppdFindChoice(color_model, "grayscale"))
+ {
+ color_option = "HPColorMode";
+ gray_choice = "grayscale";
+ }
+ else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
+ ppdFindChoice(color_model, "Mono"))
+ {
+ color_option = "BRMonoColor";
+ gray_choice = "Mono";
+ }
+ else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
+ ppdFindChoice(color_model, "1"))
+ {
+ color_option = "CNIJSGrayScale";
+ gray_choice = "1";
+ }
+ else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
+ ppdFindChoice(color_model, "True"))
+ {
+ color_option = "HPColorAsGray";
+ gray_choice = "True";
+ }
+
+ if (color_option && gray_choice)
+ {
+ /*
+ * Copy and convert ColorModel (output-mode) data...
+ */
+
+ cups_option_t *coption, /* Color option */
+ *moption; /* Monochrome option */
+
+ for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
+ pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
+ pwg_print_quality ++)
+ {
+ if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
+ {
+ /*
+ * Copy the color options...
+ */
+
+ num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+ [pwg_print_quality];
+ options = calloc(sizeof(cups_option_t), num_options);
+
+ if (options)
+ {
+ for (i = num_options, moption = options,
+ coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
+ [pwg_print_quality];
+ i > 0;
+ i --, moption ++, coption ++)
+ {
+ moption->name = _cupsStrRetain(coption->name);
+ moption->value = _cupsStrRetain(coption->value);
+ }
+
+ pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
+ num_options;
+ pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
+ options;
+ }
+ }
+ else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
+ continue;
+
+ /*
+ * Add the grayscale option to the preset...
+ */
+
+ pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
+ cupsAddOption(color_option, gray_choice,
+ pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+ [pwg_print_quality],
+ pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
+ pwg_print_quality);
+ }
+ }
+ }
+
+ /*
+ * Copy and convert Duplex (sides) data...
+ */
+
+ if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
+ if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
+ if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
+ if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
+ duplex = ppdFindOption(ppd, "KD03Duplex");
+
+ if (duplex)
+ {
+ pc->sides_option = _cupsStrAlloc(duplex->keyword);
+
+ for (i = duplex->num_choices, choice = duplex->choices;
+ i > 0;
+ i --, choice ++)
+ {
+ if ((!_cups_strcasecmp(choice->choice, "None") ||
+ !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
+ pc->sides_1sided = _cupsStrAlloc(choice->choice);
+ else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
+ !_cups_strcasecmp(choice->choice, "LongEdge") ||
+ !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
+ pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
+ else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
+ !_cups_strcasecmp(choice->choice, "ShortEdge") ||
+ !_cups_strcasecmp(choice->choice, "Bottom")) &&
+ !pc->sides_2sided_short)
+ pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
+ }
+ }
+
+ /*
+ * Copy filters and pre-filters...
+ */
+
+ pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree);
+
+ cupsArrayAdd(pc->filters,
+ "application/vnd.cups-raw application/octet-stream 0 -");
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
+ {
+ do
+ {
+ cupsArrayAdd(pc->filters, ppd_attr->value);
+ }
+ while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
+ }
+ else if (ppd->num_filters > 0)
+ {
+ for (i = 0; i < ppd->num_filters; i ++)
+ cupsArrayAdd(pc->filters, ppd->filters[i]);
+ }
+ else
+ cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
+
+ /*
+ * See if we have a command filter...
+ */
+
+ for (filter = (const char *)cupsArrayFirst(pc->filters);
+ filter;
+ filter = (const char *)cupsArrayNext(pc->filters))
+ if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
+ _cups_isspace(filter[28]))
+ break;
+
+ if (!filter &&
+ ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
+ _cups_strcasecmp(ppd_attr->value, "none")))
+ {
+ /*
+ * No command filter and no cupsCommands keyword telling us not to use one.
+ * See if this is a PostScript printer, and if so add a PostScript command
+ * filter...
+ */
+
+ for (filter = (const char *)cupsArrayFirst(pc->filters);
+ filter;
+ filter = (const char *)cupsArrayNext(pc->filters))
+ if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
+ _cups_isspace(filter[31]))
+ break;
+
+ if (filter)
+ cupsArrayAdd(pc->filters,
+ "application/vnd.cups-command application/postscript 100 "
+ "commandtops");
+ }
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
+ {
+ pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree);
+
+ do
+ {
+ cupsArrayAdd(pc->prefilters, ppd_attr->value);
+ }
+ while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
+ }
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
+ pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
+
+ /*
+ * Copy the product string, if any...
+ */
+
+ if (ppd->product)
+ pc->product = _cupsStrAlloc(ppd->product);
+
+ /*
+ * Copy finishings mapping data...
+ */
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
+ {
+ pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
+ NULL, NULL, 0, NULL,
+ (cups_afree_func_t)pwg_free_finishings);
+
+ do
+ {
+ if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
+ goto create_error;
+
+ finishings->value = atoi(ppd_attr->spec);
+ finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
+ &(finishings->options),
+ _PPD_PARSE_OPTIONS);
+
+ cupsArrayAdd(pc->finishings, finishings);
+ }
+ while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
+ NULL)) != NULL);
+ }
+
+ /*
+ * Max copies...
+ */
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
+ pc->max_copies = atoi(ppd_attr->value);
+ else if (ppd->manual_copies)
+ pc->max_copies = 1;
+ else
+ pc->max_copies = 9999;
+
+ /*
+ * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
+ * cupsJobPassword, and cupsMandatory.
+ */
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
+ pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value);
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
+ pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
+ pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
+ pc->password = _cupsStrAlloc(ppd_attr->value);
+
+ if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
+ pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' ');
+
+ /*
+ * Support files...
+ */
+
+ pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
+ (cups_acopy_func_t)_cupsStrAlloc,
+ (cups_afree_func_t)_cupsStrFree);
+
+ for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
+ ppd_attr;
+ ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+ cupsArrayAdd(pc->support_files, ppd_attr->value);
+
+ if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
+ cupsArrayAdd(pc->support_files, ppd_attr->value);
+
+ /*
+ * Return the cache data...
+ */
+
+ return (pc);
+
+ /*
+ * If we get here we need to destroy the PWG mapping data and return NULL...
+ */
+
+ create_error:
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
+ _ppdCacheDestroy(pc);
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
+ */
+
+void
+_ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */
+{
+ int i; /* Looping var */
+ pwg_map_t *map; /* Current map */
+ pwg_size_t *size; /* Current size */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc)
+ return;
+
+ /*
+ * Free memory as needed...
+ */
+
+ if (pc->bins)
+ {
+ for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
+ {
+ _cupsStrFree(map->pwg);
+ _cupsStrFree(map->ppd);
+ }
+
+ free(pc->bins);
+ }
+
+ if (pc->sizes)
+ {
+ for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
+ {
+ _cupsStrFree(size->map.pwg);
+ _cupsStrFree(size->map.ppd);
+ }
+
+ free(pc->sizes);
+ }
+
+ if (pc->source_option)
+ _cupsStrFree(pc->source_option);
+
+ if (pc->sources)
+ {
+ for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
+ {
+ _cupsStrFree(map->pwg);
+ _cupsStrFree(map->ppd);
+ }
+
+ free(pc->sources);
+ }
+
+ if (pc->types)
+ {
+ for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
+ {
+ _cupsStrFree(map->pwg);
+ _cupsStrFree(map->ppd);
+ }
+
+ free(pc->types);
+ }
+
+ if (pc->custom_max_keyword)
+ _cupsStrFree(pc->custom_max_keyword);
+
+ if (pc->custom_min_keyword)
+ _cupsStrFree(pc->custom_min_keyword);
+
+ _cupsStrFree(pc->product);
+ cupsArrayDelete(pc->filters);
+ cupsArrayDelete(pc->prefilters);
+ cupsArrayDelete(pc->finishings);
+
+ _cupsStrFree(pc->charge_info_uri);
+ _cupsStrFree(pc->password);
+
+ cupsArrayDelete(pc->mandatory);
+
+ cupsArrayDelete(pc->support_files);
+
+ free(pc);
+}
+
+
+/*
+ * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
+ * OutputBin.
+ */
+
+const char * /* O - output-bin or NULL */
+_ppdCacheGetBin(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ const char *output_bin) /* I - PPD OutputBin string */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !output_bin)
+ return (NULL);
+
+ /*
+ * Look up the OutputBin string...
+ */
+
+
+ for (i = 0; i < pc->num_bins; i ++)
+ if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
+ return (pc->bins[i].pwg);
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
+ * IPP finishings value(s).
+ */
+
+int /* O - New number of options */
+_ppdCacheGetFinishingOptions(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ ipp_t *job, /* I - Job attributes or NULL */
+ ipp_finishings_t value, /* I - IPP finishings value of IPP_FINISHINGS_NONE */
+ int num_options, /* I - Number of options */
+ cups_option_t **options) /* IO - Options */
+{
+ int i; /* Looping var */
+ _pwg_finishings_t *f, /* PWG finishings options */
+ key; /* Search key */
+ ipp_attribute_t *attr; /* Finishings attribute */
+ cups_option_t *option; /* Current finishings option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
+ (!job && value == IPP_FINISHINGS_NONE))
+ return (num_options);
+
+ /*
+ * Apply finishing options...
+ */
+
+ if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
+ {
+ int num_values = ippGetCount(attr); /* Number of values */
+
+ for (i = 0; i < num_values; i ++)
+ {
+ key.value = ippGetInteger(attr, i);
+
+ if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
+ {
+ int j; /* Another looping var */
+
+ for (j = f->num_options, option = f->options; j > 0; j --, option ++)
+ num_options = cupsAddOption(option->name, option->value,
+ num_options, options);
+ }
+ }
+ }
+ else if (value != IPP_FINISHINGS_NONE)
+ {
+ key.value = value;
+
+ if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
+ {
+ int j; /* Another looping var */
+
+ for (j = f->num_options, option = f->options; j > 0; j --, option ++)
+ num_options = cupsAddOption(option->name, option->value,
+ num_options, options);
+ }
+ }
+
+ return (num_options);
+}
+
+
+/*
+ * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
+ * PPD options.
+ */
+
+int /* O - Number of finishings values */
+_ppdCacheGetFinishingValues(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ int max_values, /* I - Maximum number of finishings values */
+ int *values) /* O - Finishings values */
+{
+ int i, /* Looping var */
+ num_values = 0; /* Number of values */
+ _pwg_finishings_t *f; /* Current finishings option */
+ cups_option_t *option; /* Current option */
+ const char *val; /* Value for option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
+ return (0);
+
+ /*
+ * Go through the finishings options and see what is set...
+ */
+
+ for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
+ f;
+ f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
+ {
+ for (i = f->num_options, option = f->options; i > 0; i --, option ++)
+ if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
+ _cups_strcasecmp(option->value, val))
+ break;
+
+ if (i == 0)
+ {
+ values[num_values ++] = f->value;
+
+ if (num_values >= max_values)
+ break;
+ }
+ }
+
+ return (num_values);
+}
+
+
+/*
+ * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
+ * attributes or a keyword string.
+ */
+
+const char * /* O - PPD InputSlot or NULL */
+_ppdCacheGetInputSlot(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ ipp_t *job, /* I - Job attributes or NULL */
+ const char *keyword) /* I - Keyword string or NULL */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!pc || pc->num_sources == 0 || (!job && !keyword))
+ return (NULL);
+
+ if (job && !keyword)
+ {
+ /*
+ * Lookup the media-col attribute and any media-source found there...
+ */
+
+ ipp_attribute_t *media_col, /* media-col attribute */
+ *media_source; /* media-source attribute */
+ pwg_size_t size; /* Dimensional size */
+ int margins_set; /* Were the margins set? */
+
+ media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
+ if (media_col &&
+ (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
+ "media-source",
+ IPP_TAG_KEYWORD)) != NULL)
+ {
+ /*
+ * Use the media-source value from media-col...
+ */
+
+ keyword = ippGetString(media_source, 0, NULL);
+ }
+ else if (pwgInitSize(&size, job, &margins_set))
+ {
+ /*
+ * For media <= 5x7, look for a photo tray...
+ */
+
+ if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
+ keyword = "photo";
+ }
+ }
+
+ if (keyword)
+ {
+ int i; /* Looping var */
+
+ for (i = 0; i < pc->num_sources; i ++)
+ if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
+ return (pc->sources[i].ppd);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
+ * attributes or a keyword string.
+ */
+
+const char * /* O - PPD MediaType or NULL */
+_ppdCacheGetMediaType(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ ipp_t *job, /* I - Job attributes or NULL */
+ const char *keyword) /* I - Keyword string or NULL */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!pc || pc->num_types == 0 || (!job && !keyword))
+ return (NULL);
+
+ if (job && !keyword)
+ {
+ /*
+ * Lookup the media-col attribute and any media-source found there...
+ */
+
+ ipp_attribute_t *media_col, /* media-col attribute */
+ *media_type; /* media-type attribute */
+
+ media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
+ if (media_col)
+ {
+ if ((media_type = ippFindAttribute(media_col->values[0].collection,
+ "media-type",
+ IPP_TAG_KEYWORD)) == NULL)
+ media_type = ippFindAttribute(media_col->values[0].collection,
+ "media-type", IPP_TAG_NAME);
+
+ if (media_type)
+ keyword = media_type->values[0].string.text;
+ }
+ }
+
+ if (keyword)
+ {
+ int i; /* Looping var */
+
+ for (i = 0; i < pc->num_types; i ++)
+ if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
+ return (pc->types[i].ppd);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
+ * string.
+ */
+
+const char * /* O - PPD OutputBin or NULL */
+_ppdCacheGetOutputBin(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ const char *output_bin) /* I - Keyword string */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !output_bin)
+ return (NULL);
+
+ /*
+ * Look up the OutputBin string...
+ */
+
+
+ for (i = 0; i < pc->num_bins; i ++)
+ if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
+ return (pc->bins[i].ppd);
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
+ * attributes or a keyword string.
+ */
+
+const char * /* O - PPD PageSize or NULL */
+_ppdCacheGetPageSize(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ ipp_t *job, /* I - Job attributes or NULL */
+ const char *keyword, /* I - Keyword string or NULL */
+ int *exact) /* O - 1 if exact match, 0 otherwise */
+{
+ int i; /* Looping var */
+ pwg_size_t *size, /* Current size */
+ *closest, /* Closest size */
+ jobsize; /* Size data from job */
+ int margins_set, /* Were the margins set? */
+ dwidth, /* Difference in width */
+ dlength, /* Difference in length */
+ dleft, /* Difference in left margins */
+ dright, /* Difference in right margins */
+ dbottom, /* Difference in bottom margins */
+ dtop, /* Difference in top margins */
+ dmin, /* Minimum difference */
+ dclosest; /* Closest difference */
+ const char *ppd_name; /* PPD media name */
+
+
+ DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
+ pc, job, keyword, exact));
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || (!job && !keyword))
+ return (NULL);
+
+ if (exact)
+ *exact = 0;
+
+ ppd_name = keyword;
+
+ if (job)
+ {
+ /*
+ * Try getting the PPD media name from the job attributes...
+ */
+
+ ipp_attribute_t *attr; /* Job attribute */
+
+ if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
+ if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
+ attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
+
+#ifdef DEBUG
+ if (attr)
+ DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
+ attr->name, ippTagString(attr->value_tag)));
+ else
+ DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
+#endif /* DEBUG */
+
+ if (attr && (attr->value_tag == IPP_TAG_NAME ||
+ attr->value_tag == IPP_TAG_KEYWORD))
+ ppd_name = attr->values[0].string.text;
+ }
+
+ DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
+
+ if (ppd_name)
+ {
+ /*
+ * Try looking up the named PPD size first...
+ */
+
+ for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
+ {
+ DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
+ (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
+
+ if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
+ !_cups_strcasecmp(ppd_name, size->map.pwg))
+ {
+ if (exact)
+ *exact = 1;
+
+ DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
+
+ return (size->map.ppd);
+ }
+ }
+ }
+
+ if (job && !keyword)
+ {
+ /*
+ * Get the size using media-col or media, with the preference being
+ * media-col.
+ */
+
+ if (!pwgInitSize(&jobsize, job, &margins_set))
+ return (NULL);
+ }
+ else
+ {
+ /*
+ * Get the size using a media keyword...
+ */
+
+ pwg_media_t *media; /* Media definition */
+
+
+ if ((media = pwgMediaForPWG(keyword)) == NULL)
+ if ((media = pwgMediaForLegacy(keyword)) == NULL)
+ if ((media = pwgMediaForPPD(keyword)) == NULL)
+ return (NULL);
+
+ jobsize.width = media->width;
+ jobsize.length = media->length;
+ margins_set = 0;
+ }
+
+ /*
+ * Now that we have the dimensions and possibly the margins, look at the
+ * available sizes and find the match...
+ */
+
+ closest = NULL;
+ dclosest = 999999999;
+
+ if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
+ _cups_strncasecmp(ppd_name, "custom_", 7))
+ {
+ for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
+ {
+ /*
+ * Adobe uses a size matching algorithm with an epsilon of 5 points, which
+ * is just about 176/2540ths...
+ */
+
+ dwidth = size->width - jobsize.width;
+ dlength = size->length - jobsize.length;
+
+ if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
+ continue;
+
+ if (margins_set)
+ {
+ /*
+ * Use a tighter epsilon of 1 point (35/2540ths) for margins...
+ */
+
+ dleft = size->left - jobsize.left;
+ dright = size->right - jobsize.right;
+ dtop = size->top - jobsize.top;
+ dbottom = size->bottom - jobsize.bottom;
+
+ if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
+ dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
+ {
+ dleft = dleft < 0 ? -dleft : dleft;
+ dright = dright < 0 ? -dright : dright;
+ dbottom = dbottom < 0 ? -dbottom : dbottom;
+ dtop = dtop < 0 ? -dtop : dtop;
+ dmin = dleft + dright + dbottom + dtop;
+
+ if (dmin < dclosest)
+ {
+ dclosest = dmin;
+ closest = size;
+ }
+
+ continue;
+ }
+ }
+
+ if (exact)
+ *exact = 1;
+
+ DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
+
+ return (size->map.ppd);
+ }
+ }
+
+ if (closest)
+ {
+ DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
+ closest->map.ppd));
+
+ return (closest->map.ppd);
+ }
+
+ /*
+ * If we get here we need to check for custom page size support...
+ */
+
+ if (jobsize.width >= pc->custom_min_width &&
+ jobsize.width <= pc->custom_max_width &&
+ jobsize.length >= pc->custom_min_length &&
+ jobsize.length <= pc->custom_max_length)
+ {
+ /*
+ * In range, format as Custom.WWWWxLLLL (points).
+ */
+
+ snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
+ (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
+
+ if (margins_set && exact)
+ {
+ dleft = pc->custom_size.left - jobsize.left;
+ dright = pc->custom_size.right - jobsize.right;
+ dtop = pc->custom_size.top - jobsize.top;
+ dbottom = pc->custom_size.bottom - jobsize.bottom;
+
+ if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
+ dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
+ *exact = 1;
+ }
+ else if (exact)
+ *exact = 1;
+
+ DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
+ pc->custom_ppd_size));
+
+ return (pc->custom_ppd_size);
+ }
+
+ /*
+ * No custom page size support or the size is out of range - return NULL.
+ */
+
+ DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
+ */
+
+pwg_size_t * /* O - PWG size or NULL */
+_ppdCacheGetSize(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ const char *page_size) /* I - PPD PageSize */
+{
+ int i; /* Looping var */
+ pwg_media_t *media; /* Media */
+ pwg_size_t *size; /* Current size */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !page_size)
+ return (NULL);
+
+ if (!_cups_strncasecmp(page_size, "Custom.", 7))
+ {
+ /*
+ * Custom size; size name can be one of the following:
+ *
+ * Custom.WIDTHxLENGTHin - Size in inches
+ * Custom.WIDTHxLENGTHft - Size in feet
+ * Custom.WIDTHxLENGTHcm - Size in centimeters
+ * Custom.WIDTHxLENGTHmm - Size in millimeters
+ * Custom.WIDTHxLENGTHm - Size in meters
+ * Custom.WIDTHxLENGTH[pt] - Size in points
+ */
+
+ double w, l; /* Width and length of page */
+ char *ptr; /* Pointer into PageSize */
+ struct lconv *loc; /* Locale data */
+
+ loc = localeconv();
+ w = (float)_cupsStrScand(page_size + 7, &ptr, loc);
+ if (!ptr || *ptr != 'x')
+ return (NULL);
+
+ l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
+ if (!ptr)
+ return (NULL);
+
+ if (!_cups_strcasecmp(ptr, "in"))
+ {
+ w *= 2540.0;
+ l *= 2540.0;
+ }
+ else if (!_cups_strcasecmp(ptr, "ft"))
+ {
+ w *= 12.0 * 2540.0;
+ l *= 12.0 * 2540.0;
+ }
+ else if (!_cups_strcasecmp(ptr, "mm"))
+ {
+ w *= 100.0;
+ l *= 100.0;
+ }
+ else if (!_cups_strcasecmp(ptr, "cm"))
+ {
+ w *= 1000.0;
+ l *= 1000.0;
+ }
+ else if (!_cups_strcasecmp(ptr, "m"))
+ {
+ w *= 100000.0;
+ l *= 100000.0;
+ }
+ else
+ {
+ w *= 2540.0 / 72.0;
+ l *= 2540.0 / 72.0;
+ }
+
+ pc->custom_size.width = (int)w;
+ pc->custom_size.length = (int)l;
+
+ return (&(pc->custom_size));
+ }
+
+ /*
+ * Not a custom size - look it up...
+ */
+
+ for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
+ if (!_cups_strcasecmp(page_size, size->map.ppd) ||
+ !_cups_strcasecmp(page_size, size->map.pwg))
+ return (size);
+
+ /*
+ * Look up standard sizes...
+ */
+
+ if ((media = pwgMediaForPPD(page_size)) == NULL)
+ if ((media = pwgMediaForLegacy(page_size)) == NULL)
+ media = pwgMediaForPWG(page_size);
+
+ if (media)
+ {
+ pc->custom_size.width = media->width;
+ pc->custom_size.length = media->length;
+
+ return (&(pc->custom_size));
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
+ * InputSlot.
+ */
+
+const char * /* O - PWG media-source keyword */
+_ppdCacheGetSource(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ const char *input_slot) /* I - PPD InputSlot */
+{
+ int i; /* Looping var */
+ pwg_map_t *source; /* Current source */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !input_slot)
+ return (NULL);
+
+ for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
+ if (!_cups_strcasecmp(input_slot, source->ppd))
+ return (source->pwg);
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
+ * MediaType.
+ */
+
+const char * /* O - PWG media-type keyword */
+_ppdCacheGetType(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ const char *media_type) /* I - PPD MediaType */
+{
+ int i; /* Looping var */
+ pwg_map_t *type; /* Current type */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !media_type)
+ return (NULL);
+
+ for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
+ if (!_cups_strcasecmp(media_type, type->ppd))
+ return (type->pwg);
+
+ return (NULL);
+}
+
+
+/*
+ * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
+ */
+
+int /* O - 1 on success, 0 on failure */
+_ppdCacheWriteFile(
+ _ppd_cache_t *pc, /* I - PPD cache and mapping data */
+ const char *filename, /* I - File to write */
+ ipp_t *attrs) /* I - Attributes to write, if any */
+{
+ int i, j, k; /* Looping vars */
+ cups_file_t *fp; /* Output file */
+ pwg_size_t *size; /* Current size */
+ pwg_map_t *map; /* Current map */
+ _pwg_finishings_t *f; /* Current finishing option */
+ cups_option_t *option; /* Current option */
+ const char *value; /* Filter/pre-filter value */
+ char newfile[1024]; /* New filename */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pc || !filename)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Open the file and write with compression...
+ */
+
+ snprintf(newfile, sizeof(newfile), "%s.N", filename);
+ if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ return (0);
+ }
+
+ /*
+ * Standard header...
+ */
+
+ cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
+
+ /*
+ * Output bins...
+ */
+
+ if (pc->num_bins > 0)
+ {
+ cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
+ for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
+ cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
+ }
+
+ /*
+ * Media sizes...
+ */
+
+ cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
+ for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
+ cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
+ size->map.ppd, size->width, size->length, size->left,
+ size->bottom, size->right, size->top);
+ if (pc->custom_max_width > 0)
+ cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
+ pc->custom_max_width, pc->custom_max_length,
+ pc->custom_min_width, pc->custom_min_length,
+ pc->custom_size.left, pc->custom_size.bottom,
+ pc->custom_size.right, pc->custom_size.top);
+
+ /*
+ * Media sources...
+ */
+
+ if (pc->source_option)
+ cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
+
+ if (pc->num_sources > 0)
+ {
+ cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
+ for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
+ cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
+ }
+
+ /*
+ * Media types...
+ */
+
+ if (pc->num_types > 0)
+ {
+ cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
+ for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
+ cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
+ }
+
+ /*
+ * Presets...
+ */
+
+ for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
+ for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
+ if (pc->num_presets[i][j])
+ {
+ cupsFilePrintf(fp, "Preset %d %d", i, j);
+ for (k = pc->num_presets[i][j], option = pc->presets[i][j];
+ k > 0;
+ k --, option ++)
+ cupsFilePrintf(fp, " %s=%s", option->name, option->value);
+ cupsFilePutChar(fp, '\n');
+ }
+
+ /*
+ * Duplex/sides...
+ */
+
+ if (pc->sides_option)
+ cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
+
+ if (pc->sides_1sided)
+ cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
+
+ if (pc->sides_2sided_long)
+ cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
+
+ if (pc->sides_2sided_short)
+ cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
+
+ /*
+ * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
+ */
+
+ if (pc->product)
+ cupsFilePutConf(fp, "Product", pc->product);
+
+ for (value = (const char *)cupsArrayFirst(pc->filters);
+ value;
+ value = (const char *)cupsArrayNext(pc->filters))
+ cupsFilePutConf(fp, "Filter", value);
+
+ for (value = (const char *)cupsArrayFirst(pc->prefilters);
+ value;
+ value = (const char *)cupsArrayNext(pc->prefilters))
+ cupsFilePutConf(fp, "PreFilter", value);
+
+ cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
+
+ /*
+ * Finishing options...
+ */
+
+ for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
+ f;
+ f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
+ {
+ cupsFilePrintf(fp, "Finishings %d", f->value);
+ for (i = f->num_options, option = f->options; i > 0; i --, option ++)
+ cupsFilePrintf(fp, " %s=%s", option->name, option->value);
+ cupsFilePutChar(fp, '\n');
+ }
+
+ /*
+ * Max copies...
+ */
+
+ cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
+
+ /*
+ * Accounting/quota/PIN/managed printing values...
+ */
+
+ if (pc->charge_info_uri)
+ cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
+
+ cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
+ cupsFilePrintf(fp, "AccountingUserId %s\n",
+ pc->accounting_user_id ? "true" : "false");
+
+ if (pc->password)
+ cupsFilePutConf(fp, "Password", pc->password);
+
+ for (value = (char *)cupsArrayFirst(pc->mandatory);
+ value;
+ value = (char *)cupsArrayNext(pc->mandatory))
+ cupsFilePutConf(fp, "Mandatory", value);
+
+ /*
+ * Support files...
+ */
+
+ for (value = (char *)cupsArrayFirst(pc->support_files);
+ value;
+ value = (char *)cupsArrayNext(pc->support_files))
+ cupsFilePutConf(fp, "SupportFile", value);
+
+ /*
+ * IPP attributes, if any...
+ */
+
+ if (attrs)
+ {
+ cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
+
+ attrs->state = IPP_STATE_IDLE;
+ ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
+ }
+
+ /*
+ * Close and return...
+ */
+
+ if (cupsFileClose(fp))
+ {
+ unlink(newfile);
+ return (0);
+ }
+
+ unlink(filename);
+ return (!rename(newfile, filename));
+}
+
+
+/*
+ * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
+ * media-source.
+ */
+
+const char * /* O - InputSlot name */
+_pwgInputSlotForSource(
+ const char *media_source, /* I - PWG media-source */
+ char *name, /* I - Name buffer */
+ size_t namesize) /* I - Size of name buffer */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!media_source || !name || namesize < PPD_MAX_NAME)
+ return (NULL);
+
+ if (_cups_strcasecmp(media_source, "main"))
+ strlcpy(name, "Cassette", namesize);
+ else if (_cups_strcasecmp(media_source, "alternate"))
+ strlcpy(name, "Multipurpose", namesize);
+ else if (_cups_strcasecmp(media_source, "large-capacity"))
+ strlcpy(name, "LargeCapacity", namesize);
+ else if (_cups_strcasecmp(media_source, "bottom"))
+ strlcpy(name, "Lower", namesize);
+ else if (_cups_strcasecmp(media_source, "middle"))
+ strlcpy(name, "Middle", namesize);
+ else if (_cups_strcasecmp(media_source, "top"))
+ strlcpy(name, "Upper", namesize);
+ else if (_cups_strcasecmp(media_source, "rear"))
+ strlcpy(name, "Rear", namesize);
+ else if (_cups_strcasecmp(media_source, "side"))
+ strlcpy(name, "Side", namesize);
+ else if (_cups_strcasecmp(media_source, "envelope"))
+ strlcpy(name, "Envelope", namesize);
+ else if (_cups_strcasecmp(media_source, "main-roll"))
+ strlcpy(name, "Roll", namesize);
+ else if (_cups_strcasecmp(media_source, "alternate-roll"))
+ strlcpy(name, "Roll2", namesize);
+ else
+ pwg_ppdize_name(media_source, name, namesize);
+
+ return (name);
+}
+
+
+/*
+ * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
+ * media-type.
+ */
+
+const char * /* O - MediaType name */
+_pwgMediaTypeForType(
+ const char *media_type, /* I - PWG media-type */
+ char *name, /* I - Name buffer */
+ size_t namesize) /* I - Size of name buffer */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!media_type || !name || namesize < PPD_MAX_NAME)
+ return (NULL);
+
+ if (_cups_strcasecmp(media_type, "auto"))
+ strlcpy(name, "Auto", namesize);
+ else if (_cups_strcasecmp(media_type, "cardstock"))
+ strlcpy(name, "Cardstock", namesize);
+ else if (_cups_strcasecmp(media_type, "envelope"))
+ strlcpy(name, "Envelope", namesize);
+ else if (_cups_strcasecmp(media_type, "photographic-glossy"))
+ strlcpy(name, "Glossy", namesize);
+ else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
+ strlcpy(name, "HighGloss", namesize);
+ else if (_cups_strcasecmp(media_type, "photographic-matte"))
+ strlcpy(name, "Matte", namesize);
+ else if (_cups_strcasecmp(media_type, "stationery"))
+ strlcpy(name, "Plain", namesize);
+ else if (_cups_strcasecmp(media_type, "stationery-coated"))
+ strlcpy(name, "Coated", namesize);
+ else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
+ strlcpy(name, "Inkjet", namesize);
+ else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
+ strlcpy(name, "Letterhead", namesize);
+ else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
+ strlcpy(name, "Preprinted", namesize);
+ else if (_cups_strcasecmp(media_type, "transparency"))
+ strlcpy(name, "Transparency", namesize);
+ else
+ pwg_ppdize_name(media_type, name, namesize);
+
+ return (name);
+}
+
+
+/*
+ * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
+ */
+
+const char * /* O - PageSize name */
+_pwgPageSizeForMedia(
+ pwg_media_t *media, /* I - Media */
+ char *name, /* I - PageSize name buffer */
+ size_t namesize) /* I - Size of name buffer */
+{
+ const char *sizeptr, /* Pointer to size in PWG name */
+ *dimptr; /* Pointer to dimensions in PWG name */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!media || !name || namesize < PPD_MAX_NAME)
+ return (NULL);
+
+ /*
+ * Copy or generate a PageSize name...
+ */
+
+ if (media->ppd)
+ {
+ /*
+ * Use a standard Adobe name...
+ */
+
+ strlcpy(name, media->ppd, namesize);
+ }
+ else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
+ (sizeptr = strchr(media->pwg, '_')) == NULL ||
+ (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
+ (size_t)(dimptr - sizeptr) > namesize)
+ {
+ /*
+ * Use a name of the form "wNNNhNNN"...
+ */
+
+ snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
+ (int)PWG_TO_POINTS(media->length));
+ }
+ else
+ {
+ /*
+ * Copy the size name from class_sizename_dimensions...
+ */
+
+ memcpy(name, sizeptr + 1, dimptr - sizeptr - 1);
+ name[dimptr - sizeptr - 1] = '\0';
+ }
+
+ return (name);
+}
+
+
+/*
+ * 'pwg_compare_finishings()' - Compare two finishings values.
+ */
+
+static int /* O- Result of comparison */
+pwg_compare_finishings(
+ _pwg_finishings_t *a, /* I - First finishings value */
+ _pwg_finishings_t *b) /* I - Second finishings value */
+{
+ return (b->value - a->value);
+}
+
+
+/*
+ * 'pwg_free_finishings()' - Free a finishings value.
+ */
+
+static void
+pwg_free_finishings(
+ _pwg_finishings_t *f) /* I - Finishings value */
+{
+ cupsFreeOptions(f->num_options, f->options);
+ free(f);
+}
+
+
+/*
+ * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
+ */
+
+static void
+pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
+ char *name, /* I - Name buffer */
+ size_t namesize) /* I - Size of name buffer */
+{
+ char *ptr, /* Pointer into name buffer */
+ *end; /* End of name buffer */
+
+
+ *name = toupper(*ipp++);
+
+ for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
+ {
+ if (*ipp == '-' && _cups_isalpha(ipp[1]))
+ {
+ ipp ++;
+ *ptr++ = toupper(*ipp++ & 255);
+ }
+ else
+ *ptr++ = *ipp++;
+ }
+
+ *ptr = '\0';
+}
+
+
+/*
+ * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
+ */
+
+static void
+pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
+ char *name, /* I - Name buffer */
+ size_t namesize, /* I - Size of name buffer */
+ const char *dashchars)/* I - Characters to be replaced by dashes */
+{
+ char *ptr, /* Pointer into name buffer */
+ *end; /* End of name buffer */
+
+
+ for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
+ {
+ if (_cups_isalnum(*ppd) || *ppd == '-')
+ *ptr++ = tolower(*ppd & 255);
+ else if (strchr(dashchars, *ppd))
+ *ptr++ = '-';
+ else
+ *ptr++ = *ppd;
+
+ if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
+ _cups_isupper(ppd[1]) && ptr < end)
+ *ptr++ = '-';
+ else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
+ *ptr++ = '-';
+ }
+
+ *ptr = '\0';
+}
+
+
+/*
+ * End of "$Id: ppd-cache.c 11833 2014-04-24 15:04:15Z msweet $".
+ */
diff --git a/cups/libs/cups/ppd-private.h b/cups/libs/cups/ppd-private.h
new file mode 100644
index 000000000..4b8c4ba09
--- /dev/null
+++ b/cups/libs/cups/ppd-private.h
@@ -0,0 +1,225 @@
+/*
+ * "$Id: ppd-private.h 4274 2013-04-09 20:10:23Z msweet $"
+ *
+ * Private PPD definitions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_PPD_PRIVATE_H_
+# define _CUPS_PPD_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/cups.h>
+# include <cups/ppd.h>
+# include "pwg-private.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define _PPD_CACHE_VERSION 6 /* Version number in cache file */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum _ppd_localization_e /**** Selector for _ppdOpen ****/
+{
+ _PPD_LOCALIZATION_DEFAULT, /* Load only the default localization */
+ _PPD_LOCALIZATION_ICC_PROFILES, /* Load only the color profile localization */
+ _PPD_LOCALIZATION_NONE, /* Load no localizations */
+ _PPD_LOCALIZATION_ALL /* Load all localizations */
+} _ppd_localization_t;
+
+typedef enum _ppd_parse_e /**** Selector for _ppdParseOptions ****/
+{
+ _PPD_PARSE_OPTIONS, /* Parse only the options */
+ _PPD_PARSE_PROPERTIES, /* Parse only the properties */
+ _PPD_PARSE_ALL /* Parse everything */
+} _ppd_parse_t;
+
+typedef struct _ppd_cups_uiconst_s /**** Constraint from cupsUIConstraints ****/
+{
+ ppd_option_t *option; /* Constrained option */
+ ppd_choice_t *choice; /* Constrained choice or @code NULL@ */
+ int installable; /* Installable option? */
+} _ppd_cups_uiconst_t;
+
+typedef struct _ppd_cups_uiconsts_s /**** cupsUIConstraints ****/
+{
+ char resolver[PPD_MAX_NAME]; /* Resolver name */
+ int installable, /* Constrained against any installable options? */
+ num_constraints; /* Number of constraints */
+ _ppd_cups_uiconst_t *constraints; /* Constraints */
+} _ppd_cups_uiconsts_t;
+
+typedef enum _pwg_print_color_mode_e /**** PWG print-color-mode indices ****/
+{
+ _PWG_PRINT_COLOR_MODE_MONOCHROME = 0, /* print-color-mode=monochrome */
+ _PWG_PRINT_COLOR_MODE_COLOR, /* print-color-mode=color */
+ /* Other proposed values are not supported by CUPS yet. */
+ _PWG_PRINT_COLOR_MODE_MAX
+} _pwg_print_color_mode_t;
+
+typedef enum _pwg_print_quality_e /**** PWG print-quality values ****/
+{
+ _PWG_PRINT_QUALITY_DRAFT = 0, /* print-quality=3 */
+ _PWG_PRINT_QUALITY_NORMAL, /* print-quality=4 */
+ _PWG_PRINT_QUALITY_HIGH, /* print-quality=5 */
+ _PWG_PRINT_QUALITY_MAX
+} _pwg_print_quality_t;
+
+typedef struct _pwg_finishings_s /**** PWG finishings mapping data ****/
+{
+ ipp_finishings_t value; /* finishings value */
+ int num_options; /* Number of options to apply */
+ cups_option_t *options; /* Options to apply */
+} _pwg_finishings_t;
+
+struct _ppd_cache_s /**** PPD cache and PWG conversion data ****/
+{
+ int num_bins; /* Number of output bins */
+ pwg_map_t *bins; /* Output bins */
+ int num_sizes; /* Number of media sizes */
+ pwg_size_t *sizes; /* Media sizes */
+ int custom_max_width, /* Maximum custom width in 2540ths */
+ custom_max_length, /* Maximum custom length in 2540ths */
+ custom_min_width, /* Minimum custom width in 2540ths */
+ custom_min_length; /* Minimum custom length in 2540ths */
+ char *custom_max_keyword, /* Maximum custom size PWG keyword */
+ *custom_min_keyword, /* Minimum custom size PWG keyword */
+ custom_ppd_size[41]; /* Custom PPD size name */
+ pwg_size_t custom_size; /* Custom size record */
+ char *source_option; /* PPD option for media source */
+ int num_sources; /* Number of media sources */
+ pwg_map_t *sources; /* Media sources */
+ int num_types; /* Number of media types */
+ pwg_map_t *types; /* Media types */
+ int num_presets[_PWG_PRINT_COLOR_MODE_MAX][_PWG_PRINT_QUALITY_MAX];
+ /* Number of print-color-mode/print-quality options */
+ cups_option_t *presets[_PWG_PRINT_COLOR_MODE_MAX][_PWG_PRINT_QUALITY_MAX];
+ /* print-color-mode/print-quality options */
+ char *sides_option, /* PPD option for sides */
+ *sides_1sided, /* Choice for one-sided */
+ *sides_2sided_long, /* Choice for two-sided-long-edge */
+ *sides_2sided_short; /* Choice for two-sided-short-edge */
+ char *product; /* Product value */
+ cups_array_t *filters, /* cupsFilter/cupsFilter2 values */
+ *prefilters; /* cupsPreFilter values */
+ int single_file; /* cupsSingleFile value */
+ cups_array_t *finishings; /* cupsIPPFinishings values */
+ int max_copies, /* cupsMaxCopies value */
+ account_id, /* cupsJobAccountId value */
+ accounting_user_id; /* cupsJobAccountingUserId value */
+ char *password; /* cupsJobPassword value */
+ cups_array_t *mandatory; /* cupsMandatory value */
+ char *charge_info_uri; /* cupsChargeInfoURI value */
+ cups_array_t *support_files; /* Support files - ICC profiles, etc. */
+};
+
+
+/*
+ * Prototypes...
+ */
+
+extern _ppd_cache_t *_ppdCacheCreateWithFile(const char *filename,
+ ipp_t **attrs);
+extern _ppd_cache_t *_ppdCacheCreateWithPPD(ppd_file_t *ppd);
+extern void _ppdCacheDestroy(_ppd_cache_t *pc);
+extern const char *_ppdCacheGetBin(_ppd_cache_t *pc,
+ const char *output_bin);
+extern int _ppdCacheGetFinishingOptions(_ppd_cache_t *pc,
+ ipp_t *job,
+ ipp_finishings_t value,
+ int num_options,
+ cups_option_t **options);
+extern int _ppdCacheGetFinishingValues(_ppd_cache_t *pc,
+ int num_options,
+ cups_option_t *options,
+ int max_values,
+ int *values);
+extern const char *_ppdCacheGetInputSlot(_ppd_cache_t *pc, ipp_t *job,
+ const char *keyword);
+extern const char *_ppdCacheGetMediaType(_ppd_cache_t *pc, ipp_t *job,
+ const char *keyword);
+extern const char *_ppdCacheGetOutputBin(_ppd_cache_t *pc,
+ const char *keyword);
+extern const char *_ppdCacheGetPageSize(_ppd_cache_t *pc, ipp_t *job,
+ const char *keyword, int *exact);
+extern pwg_size_t *_ppdCacheGetSize(_ppd_cache_t *pc,
+ const char *page_size);
+extern const char *_ppdCacheGetSource(_ppd_cache_t *pc,
+ const char *input_slot);
+extern const char *_ppdCacheGetType(_ppd_cache_t *pc,
+ const char *media_type);
+extern int _ppdCacheWriteFile(_ppd_cache_t *pc,
+ const char *filename, ipp_t *attrs);
+extern void _ppdFreeLanguages(cups_array_t *languages);
+extern cups_encoding_t _ppdGetEncoding(const char *name);
+extern cups_array_t *_ppdGetLanguages(ppd_file_t *ppd);
+extern unsigned _ppdHashName(const char *name);
+extern ppd_attr_t *_ppdLocalizedAttr(ppd_file_t *ppd, const char *keyword,
+ const char *spec, const char *ll_CC);
+extern char *_ppdNormalizeMakeAndModel(const char *make_and_model,
+ char *buffer,
+ size_t bufsize);
+extern ppd_file_t *_ppdOpen(cups_file_t *fp,
+ _ppd_localization_t localization);
+extern ppd_file_t *_ppdOpenFile(const char *filename,
+ _ppd_localization_t localization);
+extern int _ppdParseOptions(const char *s, int num_options,
+ cups_option_t **options,
+ _ppd_parse_t which);
+extern const char *_pwgInputSlotForSource(const char *media_source,
+ char *name, size_t namesize);
+extern const char *_pwgMediaTypeForType(const char *media_type,
+ char *name, size_t namesize);
+extern const char *_pwgPageSizeForMedia(pwg_media_t *media,
+ char *name, size_t namesize);
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_PPD_PRIVATE_H_ */
+
+/*
+ * End of "$Id: ppd-private.h 4274 2013-04-09 20:10:23Z msweet $".
+ */
diff --git a/cups/libs/cups/ppd.c b/cups/libs/cups/ppd.c
new file mode 100644
index 000000000..e5d4c99b1
--- /dev/null
+++ b/cups/libs/cups/ppd.c
@@ -0,0 +1,3401 @@
+/*
+ * "$Id: ppd.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * PPD file routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdClose() - Free all memory used by the PPD file.
+ * ppdErrorString() - Returns the text assocated with a status.
+ * _ppdGetEncoding() - Get the CUPS encoding value for the given
+ * LanguageEncoding.
+ * ppdLastError() - Return the status from the last ppdOpen*().
+ * ppdOpen() - Read a PPD file into memory.
+ * _ppdOpen() - Read a PPD file into memory.
+ * ppdOpen2() - Read a PPD file into memory.
+ * ppdOpenFd() - Read a PPD file into memory.
+ * _ppdOpenFile() - Read a PPD file into memory.
+ * ppdOpenFile() - Read a PPD file into memory.
+ * ppdSetConformance() - Set the conformance level for PPD files.
+ * ppd_add_attr() - Add an attribute to the PPD data.
+ * ppd_add_choice() - Add a choice to an option.
+ * ppd_add_size() - Add a page size.
+ * ppd_compare_attrs() - Compare two attributes.
+ * ppd_compare_choices() - Compare two choices...
+ * ppd_compare_coptions() - Compare two custom options.
+ * ppd_compare_options() - Compare two options.
+ * ppd_decode() - Decode a string value...
+ * ppd_free_filters() - Free the filters array.
+ * ppd_free_group() - Free a single UI group.
+ * ppd_free_option() - Free a single option.
+ * ppd_get_coption() - Get a custom option record.
+ * ppd_get_cparam() - Get a custom parameter record.
+ * ppd_get_group() - Find or create the named group as needed.
+ * ppd_get_option() - Find or create the named option as needed.
+ * ppd_hash_option() - Generate a hash of the option name...
+ * ppd_read() - Read a line from a PPD file, skipping comment
+ * lines as necessary.
+ * ppd_update_filters() - Update the filters array as needed.
+ */
+
+/*
+ * Include necessary headers.
+ */
+#if WIN32
+char *strchr(const char *, int);
+#endif
+
+#include "cups-private.h"
+#include "ppd-private.h"
+
+
+/*
+ * Definitions...
+ */
+
+#if defined(WIN32) || defined(__EMX__)
+# define READ_BINARY "rb" /* Open a binary file for reading */
+# define WRITE_BINARY "wb" /* Open a binary file for writing */
+#else
+# define READ_BINARY "r" /* Open a binary file for reading */
+# define WRITE_BINARY "w" /* Open a binary file for writing */
+#endif /* WIN32 || __EMX__ */
+
+#define ppd_free(p) if (p) free(p) /* Safe free macro */
+
+#define PPD_KEYWORD 1 /* Line contained a keyword */
+#define PPD_OPTION 2 /* Line contained an option name */
+#define PPD_TEXT 4 /* Line contained human-readable text */
+#define PPD_STRING 8 /* Line contained a string or code */
+
+#define PPD_HASHSIZE 512 /* Size of hash */
+
+
+/*
+ * Line buffer structure...
+ */
+
+typedef struct _ppd_line_s
+{
+ char *buffer; /* Pointer to buffer */
+ size_t bufsize; /* Size of the buffer */
+} _ppd_line_t;
+
+
+/*
+ * Local functions...
+ */
+
+static ppd_attr_t *ppd_add_attr(ppd_file_t *ppd, const char *name,
+ const char *spec, const char *text,
+ const char *value);
+static ppd_choice_t *ppd_add_choice(ppd_option_t *option, const char *name);
+static ppd_size_t *ppd_add_size(ppd_file_t *ppd, const char *name);
+static int ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b);
+static int ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b);
+static int ppd_compare_coptions(ppd_coption_t *a,
+ ppd_coption_t *b);
+static int ppd_compare_options(ppd_option_t *a, ppd_option_t *b);
+static int ppd_decode(char *string);
+static void ppd_free_filters(ppd_file_t *ppd);
+static void ppd_free_group(ppd_group_t *group);
+static void ppd_free_option(ppd_option_t *option);
+static ppd_coption_t *ppd_get_coption(ppd_file_t *ppd, const char *name);
+static ppd_cparam_t *ppd_get_cparam(ppd_coption_t *opt,
+ const char *param,
+ const char *text);
+static ppd_group_t *ppd_get_group(ppd_file_t *ppd, const char *name,
+ const char *text, _cups_globals_t *cg,
+ cups_encoding_t encoding);
+static ppd_option_t *ppd_get_option(ppd_group_t *group, const char *name);
+static int ppd_hash_option(ppd_option_t *option);
+static int ppd_read(cups_file_t *fp, _ppd_line_t *line,
+ char *keyword, char *option, char *text,
+ char **string, int ignoreblank,
+ _cups_globals_t *cg);
+static int ppd_update_filters(ppd_file_t *ppd,
+ _cups_globals_t *cg);
+
+
+/*
+ * 'ppdClose()' - Free all memory used by the PPD file.
+ */
+
+void
+ppdClose(ppd_file_t *ppd) /* I - PPD file record */
+{
+ int i; /* Looping var */
+ ppd_emul_t *emul; /* Current emulation */
+ ppd_group_t *group; /* Current group */
+ char **font; /* Current font */
+ ppd_attr_t **attr; /* Current attribute */
+ ppd_coption_t *coption; /* Current custom option */
+ ppd_cparam_t *cparam; /* Current custom parameter */
+
+
+ /*
+ * Range check arguments...
+ */
+
+ if (!ppd)
+ return;
+
+ /*
+ * Free all strings at the top level...
+ */
+
+ _cupsStrFree(ppd->lang_encoding);
+ _cupsStrFree(ppd->nickname);
+ if (ppd->patches)
+ free(ppd->patches);
+ _cupsStrFree(ppd->jcl_begin);
+ _cupsStrFree(ppd->jcl_end);
+ _cupsStrFree(ppd->jcl_ps);
+
+ /*
+ * Free any emulations...
+ */
+
+ if (ppd->num_emulations > 0)
+ {
+ for (i = ppd->num_emulations, emul = ppd->emulations; i > 0; i --, emul ++)
+ {
+ _cupsStrFree(emul->start);
+ _cupsStrFree(emul->stop);
+ }
+
+ ppd_free(ppd->emulations);
+ }
+
+ /*
+ * Free any UI groups, subgroups, and options...
+ */
+
+ if (ppd->num_groups > 0)
+ {
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ ppd_free_group(group);
+
+ ppd_free(ppd->groups);
+ }
+
+ cupsArrayDelete(ppd->options);
+ cupsArrayDelete(ppd->marked);
+
+ /*
+ * Free any page sizes...
+ */
+
+ if (ppd->num_sizes > 0)
+ ppd_free(ppd->sizes);
+
+ /*
+ * Free any constraints...
+ */
+
+ if (ppd->num_consts > 0)
+ ppd_free(ppd->consts);
+
+ /*
+ * Free any filters...
+ */
+
+ ppd_free_filters(ppd);
+
+ /*
+ * Free any fonts...
+ */
+
+ if (ppd->num_fonts > 0)
+ {
+ for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
+ _cupsStrFree(*font);
+
+ ppd_free(ppd->fonts);
+ }
+
+ /*
+ * Free any profiles...
+ */
+
+ if (ppd->num_profiles > 0)
+ ppd_free(ppd->profiles);
+
+ /*
+ * Free any attributes...
+ */
+
+ if (ppd->num_attrs > 0)
+ {
+ for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++)
+ {
+ _cupsStrFree((*attr)->value);
+ ppd_free(*attr);
+ }
+
+ ppd_free(ppd->attrs);
+ }
+
+ cupsArrayDelete(ppd->sorted_attrs);
+
+ /*
+ * Free custom options...
+ */
+
+ for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
+ coption;
+ coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
+ {
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ _cupsStrFree(cparam->current.custom_string);
+ break;
+
+ default :
+ break;
+ }
+
+ free(cparam);
+ }
+
+ cupsArrayDelete(coption->params);
+
+ free(coption);
+ }
+
+ cupsArrayDelete(ppd->coptions);
+
+ /*
+ * Free constraints...
+ */
+
+ if (ppd->cups_uiconstraints)
+ {
+ _ppd_cups_uiconsts_t *consts; /* Current constraints */
+
+
+ for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
+ consts;
+ consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
+ {
+ free(consts->constraints);
+ free(consts);
+ }
+
+ cupsArrayDelete(ppd->cups_uiconstraints);
+ }
+
+ /*
+ * Free any PPD cache/mapping data...
+ */
+
+ if (ppd->cache)
+ _ppdCacheDestroy(ppd->cache);
+
+ /*
+ * Free the whole record...
+ */
+
+ ppd_free(ppd);
+}
+
+
+/*
+ * 'ppdErrorString()' - Returns the text assocated with a status.
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+const char * /* O - Status string */
+ppdErrorString(ppd_status_t status) /* I - PPD status */
+{
+ static const char * const messages[] =/* Status messages */
+ {
+ _("OK"),
+ _("Unable to open PPD file"),
+ _("NULL PPD file pointer"),
+ _("Memory allocation error"),
+ _("Missing PPD-Adobe-4.x header"),
+ _("Missing value string"),
+ _("Internal error"),
+ _("Bad OpenGroup"),
+ _("OpenGroup without a CloseGroup first"),
+ _("Bad OpenUI/JCLOpenUI"),
+ _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
+ _("Bad OrderDependency"),
+ _("Bad UIConstraints"),
+ _("Missing asterisk in column 1"),
+ _("Line longer than the maximum allowed (255 characters)"),
+ _("Illegal control character"),
+ _("Illegal main keyword string"),
+ _("Illegal option keyword string"),
+ _("Illegal translation string"),
+ _("Illegal whitespace character"),
+ _("Bad custom parameter"),
+ _("Missing option keyword"),
+ _("Bad value string"),
+ _("Missing CloseGroup")
+ };
+
+
+ if (status < PPD_OK || status >= PPD_MAX_STATUS)
+ return (_cupsLangString(cupsLangDefault(), _("Unknown")));
+ else
+ return (_cupsLangString(cupsLangDefault(), messages[status]));
+}
+
+
+/*
+ * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
+ * LanguageEncoding.
+ */
+
+cups_encoding_t /* O - CUPS encoding value */
+_ppdGetEncoding(const char *name) /* I - LanguageEncoding string */
+{
+ if (!_cups_strcasecmp(name, "ISOLatin1"))
+ return (CUPS_ISO8859_1);
+ else if (!_cups_strcasecmp(name, "ISOLatin2"))
+ return (CUPS_ISO8859_2);
+ else if (!_cups_strcasecmp(name, "ISOLatin5"))
+ return (CUPS_ISO8859_5);
+ else if (!_cups_strcasecmp(name, "JIS83-RKSJ"))
+ return (CUPS_JIS_X0213);
+ else if (!_cups_strcasecmp(name, "MacStandard"))
+ return (CUPS_MAC_ROMAN);
+ else if (!_cups_strcasecmp(name, "WindowsANSI"))
+ return (CUPS_WINDOWS_1252);
+ else
+ return (CUPS_UTF8);
+}
+
+
+/*
+ * 'ppdLastError()' - Return the status from the last ppdOpen*().
+ *
+ * @since CUPS 1.1.19/OS X 10.3@
+ */
+
+ppd_status_t /* O - Status code */
+ppdLastError(int *line) /* O - Line number */
+{
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ if (line)
+ *line = cg->ppd_line;
+
+ return (cg->ppd_status);
+}
+
+
+/*
+ * '_ppdOpen()' - Read a PPD file into memory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+_ppdOpen(
+ cups_file_t *fp, /* I - File to read from */
+ _ppd_localization_t localization) /* I - Localization to load */
+{
+ int i, j, k; /* Looping vars */
+ int count; /* Temporary count */
+ _ppd_line_t line; /* Line buffer */
+ ppd_file_t *ppd; /* PPD file record */
+ ppd_group_t *group, /* Current group */
+ *subgroup; /* Current sub-group */
+ ppd_option_t *option; /* Current option */
+ ppd_choice_t *choice; /* Current choice */
+ ppd_const_t *constraint; /* Current constraint */
+ ppd_size_t *size; /* Current page size */
+ int mask; /* Line data mask */
+ char keyword[PPD_MAX_NAME],
+ /* Keyword from file */
+ name[PPD_MAX_NAME],
+ /* Option from file */
+ text[PPD_MAX_LINE],
+ /* Human-readable text from file */
+ *string, /* Code/text from file */
+ *sptr, /* Pointer into string */
+ *nameptr, /* Pointer into name */
+ *temp, /* Temporary string pointer */
+ **tempfonts; /* Temporary fonts pointer */
+ float order; /* Order dependency number */
+ ppd_section_t section; /* Order dependency section */
+ ppd_profile_t *profile; /* Pointer to color profile */
+ char **filter; /* Pointer to filter */
+ struct lconv *loc; /* Locale data */
+ int ui_keyword; /* Is this line a UI keyword? */
+ cups_lang_t *lang; /* Language data */
+ cups_encoding_t encoding; /* Encoding of PPD file */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+ char custom_name[PPD_MAX_NAME];
+ /* CustomFoo attribute name */
+ ppd_attr_t *custom_attr; /* CustomFoo attribute */
+ char ll[4], /* Language + '.' */
+ ll_CC[7]; /* Language + country + '.' */
+ size_t ll_len = 0, /* Language length */
+ ll_CC_len = 0; /* Language + country length */
+ static const char * const ui_keywords[] =
+ {
+#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
+ /*
+ * Adobe defines some 41 keywords as "UI", meaning that they are
+ * user interface elements and that they should be treated as such
+ * even if the PPD creator doesn't use Open/CloseUI around them.
+ *
+ * Since this can cause previously invisible options to appear and
+ * confuse users, the default is to only treat the PageSize and
+ * PageRegion keywords this way.
+ */
+ /* Boolean keywords */
+ "BlackSubstitution",
+ "Booklet",
+ "Collate",
+ "ManualFeed",
+ "MirrorPrint",
+ "NegativePrint",
+ "Sorter",
+ "TraySwitch",
+
+ /* PickOne keywords */
+ "AdvanceMedia",
+ "BindColor",
+ "BindEdge",
+ "BindType",
+ "BindWhen",
+ "BitsPerPixel",
+ "ColorModel",
+ "CutMedia",
+ "Duplex",
+ "FoldType",
+ "FoldWhen",
+ "InputSlot",
+ "JCLFrameBufferSize",
+ "JCLResolution",
+ "Jog",
+ "MediaColor",
+ "MediaType",
+ "MediaWeight",
+ "OutputBin",
+ "OutputMode",
+ "OutputOrder",
+ "PageRegion",
+ "PageSize",
+ "Resolution",
+ "Separations",
+ "Signature",
+ "Slipsheet",
+ "Smoothing",
+ "StapleLocation",
+ "StapleOrientation",
+ "StapleWhen",
+ "StapleX",
+ "StapleY"
+#else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
+ "PageRegion",
+ "PageSize"
+#endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
+ };
+ static const char * const color_keywords[] = /* Keywords associated with color profiles */
+ {
+ ".cupsICCProfile",
+ ".ColorModel",
+ };
+
+
+ DEBUG_printf(("_ppdOpen(fp=%p)", fp));
+
+ /*
+ * Default to "OK" status...
+ */
+
+ cg->ppd_status = PPD_OK;
+ cg->ppd_line = 0;
+
+ /*
+ * Range check input...
+ */
+
+ if (fp == NULL)
+ {
+ cg->ppd_status = PPD_NULL_FILE;
+ return (NULL);
+ }
+
+ /*
+ * If only loading a single localization set up the strings to match...
+ */
+
+ if (localization == _PPD_LOCALIZATION_DEFAULT)
+ {
+ if ((lang = cupsLangDefault()) == NULL)
+ return (NULL);
+
+ snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language);
+ snprintf(ll, sizeof(ll), "%2.2s.", lang->language);
+
+ ll_CC_len = strlen(ll_CC);
+ ll_len = strlen(ll);
+
+ DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
+ ll_CC, ll));
+ }
+
+ /*
+ * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
+ */
+
+ line.buffer = NULL;
+ line.bufsize = 0;
+
+ mask = ppd_read(fp, &line, keyword, name, text, &string, 0, cg);
+
+ DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask, keyword));
+
+ if (mask == 0 ||
+ strcmp(keyword, "PPD-Adobe") ||
+ string == NULL || string[0] != '4')
+ {
+ /*
+ * Either this is not a PPD file, or it is not a 4.x PPD file.
+ */
+
+ if (cg->ppd_status == PPD_OK)
+ cg->ppd_status = PPD_MISSING_PPDADOBE4;
+
+ _cupsStrFree(string);
+ ppd_free(line.buffer);
+
+ return (NULL);
+ }
+
+ DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string));
+
+ _cupsStrFree(string);
+
+ /*
+ * Allocate memory for the PPD file record...
+ */
+
+ if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ _cupsStrFree(string);
+ ppd_free(line.buffer);
+
+ return (NULL);
+ }
+
+ ppd->language_level = 2;
+ ppd->color_device = 0;
+ ppd->colorspace = PPD_CS_N;
+ ppd->landscape = -90;
+ ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions,
+ NULL);
+
+ /*
+ * Read lines from the PPD file and add them to the file record...
+ */
+
+ group = NULL;
+ subgroup = NULL;
+ option = NULL;
+ choice = NULL;
+ ui_keyword = 0;
+ encoding = CUPS_ISO8859_1;
+ loc = localeconv();
+
+ while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, cg)) != 0)
+ {
+ DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
+ "text=\"%s\", string=%d chars...", mask, keyword, name, text,
+ string ? (int)strlen(string) : 0));
+
+ if (strncmp(keyword, "Default", 7) && !string &&
+ cg->ppd_conform != PPD_CONFORM_RELAXED)
+ {
+ /*
+ * Need a string value!
+ */
+
+ cg->ppd_status = PPD_MISSING_VALUE;
+
+ goto error;
+ }
+ else if (!string)
+ continue;
+
+ /*
+ * Certain main keywords (as defined by the PPD spec) may be used
+ * without the usual OpenUI/CloseUI stuff. Presumably this is just
+ * so that Adobe wouldn't completely break compatibility with PPD
+ * files prior to v4.0 of the spec, but it is hopelessly
+ * inconsistent... Catch these main keywords and automatically
+ * create the corresponding option, as needed...
+ */
+
+ if (ui_keyword)
+ {
+ /*
+ * Previous line was a UI keyword...
+ */
+
+ option = NULL;
+ ui_keyword = 0;
+ }
+
+ /*
+ * If we are filtering out keyword localizations, see if this line needs to
+ * be used...
+ */
+
+ if (localization != _PPD_LOCALIZATION_ALL &&
+ (temp = strchr(keyword, '.')) != NULL &&
+ ((temp - keyword) == 2 || (temp - keyword) == 5) &&
+ _cups_isalpha(keyword[0]) &&
+ _cups_isalpha(keyword[1]) &&
+ (keyword[2] == '.' ||
+ (keyword[2] == '_' && _cups_isalpha(keyword[3]) &&
+ _cups_isalpha(keyword[4]) && keyword[5] == '.')))
+ {
+ if (localization == _PPD_LOCALIZATION_NONE ||
+ (localization == _PPD_LOCALIZATION_DEFAULT &&
+ strncmp(ll_CC, keyword, ll_CC_len) &&
+ strncmp(ll, keyword, ll_len)))
+ {
+ DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
+ continue;
+ }
+ else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
+ {
+ /*
+ * Only load localizations for the color profile related keywords...
+ */
+
+ for (i = 0;
+ i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0]));
+ i ++)
+ {
+ if (!_cups_strcasecmp(temp, color_keywords[i]))
+ break;
+ }
+
+ if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0])))
+ {
+ DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
+ continue;
+ }
+ }
+ }
+
+ if (option == NULL &&
+ (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
+ (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
+ {
+ for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
+ if (!strcmp(keyword, ui_keywords[i]))
+ break;
+
+ if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
+ {
+ /*
+ * Create the option in the appropriate group...
+ */
+
+ ui_keyword = 1;
+
+ DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
+ keyword));
+
+ if (!group)
+ {
+ if ((group = ppd_get_group(ppd, "General", _("General"), cg,
+ encoding)) == NULL)
+ goto error;
+
+ DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
+ option = ppd_get_option(group, keyword);
+ group = NULL;
+ }
+ else
+ option = ppd_get_option(group, keyword);
+
+ if (option == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ /*
+ * Now fill in the initial information for the option...
+ */
+
+ if (!strncmp(keyword, "JCL", 3))
+ option->section = PPD_ORDER_JCL;
+ else
+ option->section = PPD_ORDER_ANY;
+
+ option->order = 10.0f;
+
+ if (i < 8)
+ option->ui = PPD_UI_BOOLEAN;
+ else
+ option->ui = PPD_UI_PICKONE;
+
+ for (j = 0; j < ppd->num_attrs; j ++)
+ if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
+ !strcmp(ppd->attrs[j]->name + 7, keyword) &&
+ ppd->attrs[j]->value)
+ {
+ DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
+ option->keyword, ppd->attrs[j]->value));
+ strlcpy(option->defchoice, ppd->attrs[j]->value,
+ sizeof(option->defchoice));
+ break;
+ }
+
+ if (!strcmp(keyword, "PageSize"))
+ strlcpy(option->text, _("Media Size"), sizeof(option->text));
+ else if (!strcmp(keyword, "MediaType"))
+ strlcpy(option->text, _("Media Type"), sizeof(option->text));
+ else if (!strcmp(keyword, "InputSlot"))
+ strlcpy(option->text, _("Media Source"), sizeof(option->text));
+ else if (!strcmp(keyword, "ColorModel"))
+ strlcpy(option->text, _("Output Mode"), sizeof(option->text));
+ else if (!strcmp(keyword, "Resolution"))
+ strlcpy(option->text, _("Resolution"), sizeof(option->text));
+ else
+ strlcpy(option->text, keyword, sizeof(option->text));
+ }
+ }
+
+ if (!strcmp(keyword, "LanguageLevel"))
+ ppd->language_level = atoi(string);
+ else if (!strcmp(keyword, "LanguageEncoding"))
+ {
+ /*
+ * Say all PPD files are UTF-8, since we convert to UTF-8...
+ */
+
+ ppd->lang_encoding = _cupsStrAlloc("UTF-8");
+ encoding = _ppdGetEncoding(string);
+ }
+ else if (!strcmp(keyword, "LanguageVersion"))
+ ppd->lang_version = string;
+ else if (!strcmp(keyword, "Manufacturer"))
+ ppd->manufacturer = string;
+ else if (!strcmp(keyword, "ModelName"))
+ ppd->modelname = string;
+ else if (!strcmp(keyword, "Protocols"))
+ ppd->protocols = string;
+ else if (!strcmp(keyword, "PCFileName"))
+ ppd->pcfilename = string;
+ else if (!strcmp(keyword, "NickName"))
+ {
+ if (encoding != CUPS_UTF8)
+ {
+ cups_utf8_t utf8[256]; /* UTF-8 version of NickName */
+
+
+ cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
+ ppd->nickname = _cupsStrAlloc((char *)utf8);
+ }
+ else
+ ppd->nickname = _cupsStrAlloc(string);
+ }
+ else if (!strcmp(keyword, "Product"))
+ ppd->product = string;
+ else if (!strcmp(keyword, "ShortNickName"))
+ ppd->shortnickname = string;
+ else if (!strcmp(keyword, "TTRasterizer"))
+ ppd->ttrasterizer = string;
+ else if (!strcmp(keyword, "JCLBegin"))
+ {
+ ppd->jcl_begin = _cupsStrAlloc(string);
+ ppd_decode(ppd->jcl_begin); /* Decode quoted string */
+ }
+ else if (!strcmp(keyword, "JCLEnd"))
+ {
+ ppd->jcl_end = _cupsStrAlloc(string);
+ ppd_decode(ppd->jcl_end); /* Decode quoted string */
+ }
+ else if (!strcmp(keyword, "JCLToPSInterpreter"))
+ {
+ ppd->jcl_ps = _cupsStrAlloc(string);
+ ppd_decode(ppd->jcl_ps); /* Decode quoted string */
+ }
+ else if (!strcmp(keyword, "AccurateScreensSupport"))
+ ppd->accurate_screens = !strcmp(string, "True");
+ else if (!strcmp(keyword, "ColorDevice"))
+ ppd->color_device = !strcmp(string, "True");
+ else if (!strcmp(keyword, "ContoneOnly"))
+ ppd->contone_only = !strcmp(string, "True");
+ else if (!strcmp(keyword, "cupsFlipDuplex"))
+ ppd->flip_duplex = !strcmp(string, "True");
+ else if (!strcmp(keyword, "cupsManualCopies"))
+ ppd->manual_copies = !strcmp(string, "True");
+ else if (!strcmp(keyword, "cupsModelNumber"))
+ ppd->model_number = atoi(string);
+ else if (!strcmp(keyword, "cupsColorProfile"))
+ {
+ if (ppd->num_profiles == 0)
+ profile = malloc(sizeof(ppd_profile_t));
+ else
+ profile = realloc(ppd->profiles, sizeof(ppd_profile_t) *
+ (ppd->num_profiles + 1));
+
+ if (!profile)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ ppd->profiles = profile;
+ profile += ppd->num_profiles;
+ ppd->num_profiles ++;
+
+ memset(profile, 0, sizeof(ppd_profile_t));
+ strlcpy(profile->resolution, name, sizeof(profile->resolution));
+ strlcpy(profile->media_type, text, sizeof(profile->media_type));
+
+ profile->density = (float)_cupsStrScand(string, &sptr, loc);
+ profile->gamma = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
+ profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
+ }
+ else if (!strcmp(keyword, "cupsFilter"))
+ {
+ if (ppd->num_filters == 0)
+ filter = malloc(sizeof(char *));
+ else
+ filter = realloc(ppd->filters, sizeof(char *) * (ppd->num_filters + 1));
+
+ if (filter == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ ppd->filters = filter;
+ filter += ppd->num_filters;
+ ppd->num_filters ++;
+
+ /*
+ * Retain a copy of the filter string...
+ */
+
+ *filter = _cupsStrRetain(string);
+ }
+ else if (!strcmp(keyword, "Throughput"))
+ ppd->throughput = atoi(string);
+ else if (!strcmp(keyword, "Font"))
+ {
+ /*
+ * Add this font to the list of available fonts...
+ */
+
+ if (ppd->num_fonts == 0)
+ tempfonts = (char **)malloc(sizeof(char *));
+ else
+ tempfonts = (char **)realloc(ppd->fonts,
+ sizeof(char *) * (ppd->num_fonts + 1));
+
+ if (tempfonts == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ ppd->fonts = tempfonts;
+ ppd->fonts[ppd->num_fonts] = _cupsStrAlloc(name);
+ ppd->num_fonts ++;
+ }
+ else if (!strncmp(keyword, "ParamCustom", 11))
+ {
+ ppd_coption_t *coption; /* Custom option */
+ ppd_cparam_t *cparam; /* Custom parameter */
+ int corder; /* Order number */
+ char ctype[33], /* Data type */
+ cminimum[65], /* Minimum value */
+ cmaximum[65]; /* Maximum value */
+
+
+ /*
+ * Get the custom option and parameter...
+ */
+
+ if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ /*
+ * Get the parameter data...
+ */
+
+ if (!string ||
+ sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
+ cmaximum) != 4)
+ {
+ cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
+
+ goto error;
+ }
+
+ cparam->order = corder;
+
+ if (!strcmp(ctype, "curve"))
+ {
+ cparam->type = PPD_CUSTOM_CURVE;
+ cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
+ cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
+ }
+ else if (!strcmp(ctype, "int"))
+ {
+ cparam->type = PPD_CUSTOM_INT;
+ cparam->minimum.custom_int = atoi(cminimum);
+ cparam->maximum.custom_int = atoi(cmaximum);
+ }
+ else if (!strcmp(ctype, "invcurve"))
+ {
+ cparam->type = PPD_CUSTOM_INVCURVE;
+ cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
+ cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
+ }
+ else if (!strcmp(ctype, "passcode"))
+ {
+ cparam->type = PPD_CUSTOM_PASSCODE;
+ cparam->minimum.custom_passcode = atoi(cminimum);
+ cparam->maximum.custom_passcode = atoi(cmaximum);
+ }
+ else if (!strcmp(ctype, "password"))
+ {
+ cparam->type = PPD_CUSTOM_PASSWORD;
+ cparam->minimum.custom_password = atoi(cminimum);
+ cparam->maximum.custom_password = atoi(cmaximum);
+ }
+ else if (!strcmp(ctype, "points"))
+ {
+ cparam->type = PPD_CUSTOM_POINTS;
+ cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
+ cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
+ }
+ else if (!strcmp(ctype, "real"))
+ {
+ cparam->type = PPD_CUSTOM_REAL;
+ cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
+ cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
+ }
+ else if (!strcmp(ctype, "string"))
+ {
+ cparam->type = PPD_CUSTOM_STRING;
+ cparam->minimum.custom_string = atoi(cminimum);
+ cparam->maximum.custom_string = atoi(cmaximum);
+ }
+ else
+ {
+ cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
+
+ goto error;
+ }
+
+ /*
+ * Now special-case for CustomPageSize...
+ */
+
+ if (!strcmp(coption->keyword, "PageSize"))
+ {
+ if (!strcmp(name, "Width"))
+ {
+ ppd->custom_min[0] = cparam->minimum.custom_points;
+ ppd->custom_max[0] = cparam->maximum.custom_points;
+ }
+ else if (!strcmp(name, "Height"))
+ {
+ ppd->custom_min[1] = cparam->minimum.custom_points;
+ ppd->custom_max[1] = cparam->maximum.custom_points;
+ }
+ }
+ }
+ else if (!strcmp(keyword, "HWMargins"))
+ {
+ for (i = 0, sptr = string; i < 4; i ++)
+ ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
+ }
+ else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
+ {
+ ppd_option_t *custom_option; /* Custom option */
+
+ DEBUG_puts("2_ppdOpen: Processing Custom option...");
+
+ /*
+ * Get the option and custom option...
+ */
+
+ if (!ppd_get_coption(ppd, keyword + 6))
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
+ custom_option = option;
+ else
+ custom_option = ppdFindOption(ppd, keyword + 6);
+
+ if (custom_option)
+ {
+ /*
+ * Add the "custom" option...
+ */
+
+ if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
+ if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
+ {
+ DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
+
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ strlcpy(choice->text, text[0] ? text : _("Custom"),
+ sizeof(choice->text));
+
+ choice->code = _cupsStrAlloc(string);
+
+ if (custom_option->section == PPD_ORDER_JCL)
+ ppd_decode(choice->code);
+ }
+
+ /*
+ * Now process custom page sizes specially...
+ */
+
+ if (!strcmp(keyword, "CustomPageSize"))
+ {
+ /*
+ * Add a "Custom" page size entry...
+ */
+
+ ppd->variable_sizes = 1;
+
+ ppd_add_size(ppd, "Custom");
+
+ if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
+ custom_option = option;
+ else
+ custom_option = ppdFindOption(ppd, "PageRegion");
+
+ if (custom_option)
+ {
+ if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
+ if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
+ {
+ DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
+
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ strlcpy(choice->text, text[0] ? text : _("Custom"),
+ sizeof(choice->text));
+ }
+ }
+ }
+ else if (!strcmp(keyword, "LandscapeOrientation"))
+ {
+ if (!strcmp(string, "Minus90"))
+ ppd->landscape = -90;
+ else if (!strcmp(string, "Plus90"))
+ ppd->landscape = 90;
+ }
+ else if (!strcmp(keyword, "Emulators") && string)
+ {
+ for (count = 1, sptr = string; sptr != NULL;)
+ if ((sptr = strchr(sptr, ' ')) != NULL)
+ {
+ count ++;
+ while (*sptr == ' ')
+ sptr ++;
+ }
+
+ ppd->num_emulations = count;
+ if ((ppd->emulations = calloc(count, sizeof(ppd_emul_t))) == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ for (i = 0, sptr = string; i < count; i ++)
+ {
+ for (nameptr = ppd->emulations[i].name;
+ *sptr != '\0' && *sptr != ' ';
+ sptr ++)
+ if (nameptr < (ppd->emulations[i].name + sizeof(ppd->emulations[i].name) - 1))
+ *nameptr++ = *sptr;
+
+ *nameptr = '\0';
+
+ while (*sptr == ' ')
+ sptr ++;
+ }
+ }
+ else if (!strncmp(keyword, "StartEmulator_", 14))
+ {
+ ppd_decode(string);
+
+ for (i = 0; i < ppd->num_emulations; i ++)
+ if (!strcmp(keyword + 14, ppd->emulations[i].name))
+ {
+ ppd->emulations[i].start = string;
+ string = NULL;
+ }
+ }
+ else if (!strncmp(keyword, "StopEmulator_", 13))
+ {
+ ppd_decode(string);
+
+ for (i = 0; i < ppd->num_emulations; i ++)
+ if (!strcmp(keyword + 13, ppd->emulations[i].name))
+ {
+ ppd->emulations[i].stop = string;
+ string = NULL;
+ }
+ }
+ else if (!strcmp(keyword, "JobPatchFile"))
+ {
+ /*
+ * CUPS STR #3421: Check for "*JobPatchFile: int: string"
+ */
+
+ if (isdigit(*string & 255))
+ {
+ for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
+
+ if (*sptr == ':')
+ {
+ /*
+ * Found "*JobPatchFile: int: string"...
+ */
+
+ cg->ppd_status = PPD_BAD_VALUE;
+
+ goto error;
+ }
+ }
+
+ if (!name[0] && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ /*
+ * Found "*JobPatchFile: string"...
+ */
+
+ cg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
+
+ goto error;
+ }
+
+ if (ppd->patches == NULL)
+ ppd->patches = strdup(string);
+ else
+ {
+ temp = realloc(ppd->patches, strlen(ppd->patches) +
+ strlen(string) + 1);
+ if (temp == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ ppd->patches = temp;
+
+ memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
+ }
+ }
+ else if (!strcmp(keyword, "OpenUI"))
+ {
+ /*
+ * Don't allow nesting of options...
+ */
+
+ if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_NESTED_OPEN_UI;
+
+ goto error;
+ }
+
+ /*
+ * Add an option record to the current sub-group, group, or file...
+ */
+
+ DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
+
+ if (name[0] == '*')
+ _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
+
+ for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
+ name[i] = '\0'; /* Eliminate trailing spaces */
+
+ DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
+ group ? group->text : "(null)"));
+
+ if (subgroup != NULL)
+ option = ppd_get_option(subgroup, name);
+ else if (group == NULL)
+ {
+ if ((group = ppd_get_group(ppd, "General", _("General"), cg,
+ encoding)) == NULL)
+ goto error;
+
+ DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
+ option = ppd_get_option(group, name);
+ group = NULL;
+ }
+ else
+ option = ppd_get_option(group, name);
+
+ if (option == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ /*
+ * Now fill in the initial information for the option...
+ */
+
+ if (string && !strcmp(string, "PickMany"))
+ option->ui = PPD_UI_PICKMANY;
+ else if (string && !strcmp(string, "Boolean"))
+ option->ui = PPD_UI_BOOLEAN;
+ else if (string && !strcmp(string, "PickOne"))
+ option->ui = PPD_UI_PICKONE;
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_OPEN_UI;
+
+ goto error;
+ }
+ else
+ option->ui = PPD_UI_PICKONE;
+
+ for (j = 0; j < ppd->num_attrs; j ++)
+ if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
+ !strcmp(ppd->attrs[j]->name + 7, name) &&
+ ppd->attrs[j]->value)
+ {
+ DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
+ option->keyword, ppd->attrs[j]->value));
+ strlcpy(option->defchoice, ppd->attrs[j]->value,
+ sizeof(option->defchoice));
+ break;
+ }
+
+ if (text[0])
+ cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
+ sizeof(option->text), encoding);
+ else
+ {
+ if (!strcmp(name, "PageSize"))
+ strlcpy(option->text, _("Media Size"), sizeof(option->text));
+ else if (!strcmp(name, "MediaType"))
+ strlcpy(option->text, _("Media Type"), sizeof(option->text));
+ else if (!strcmp(name, "InputSlot"))
+ strlcpy(option->text, _("Media Source"), sizeof(option->text));
+ else if (!strcmp(name, "ColorModel"))
+ strlcpy(option->text, _("Output Mode"), sizeof(option->text));
+ else if (!strcmp(name, "Resolution"))
+ strlcpy(option->text, _("Resolution"), sizeof(option->text));
+ else
+ strlcpy(option->text, name, sizeof(option->text));
+ }
+
+ option->section = PPD_ORDER_ANY;
+
+ _cupsStrFree(string);
+ string = NULL;
+
+ /*
+ * Add a custom option choice if we have already seen a CustomFoo
+ * attribute...
+ */
+
+ if (!_cups_strcasecmp(name, "PageRegion"))
+ strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
+ else
+ snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
+
+ if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
+ {
+ if ((choice = ppdFindChoice(option, "Custom")) == NULL)
+ if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+ {
+ DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
+
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ strlcpy(choice->text,
+ custom_attr->text[0] ? custom_attr->text : _("Custom"),
+ sizeof(choice->text));
+ choice->code = _cupsStrRetain(custom_attr->value);
+ }
+ }
+ else if (!strcmp(keyword, "JCLOpenUI"))
+ {
+ /*
+ * Don't allow nesting of options...
+ */
+
+ if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_NESTED_OPEN_UI;
+
+ goto error;
+ }
+
+ /*
+ * Find the JCL group, and add if needed...
+ */
+
+ group = ppd_get_group(ppd, "JCL", _("JCL"), cg, encoding);
+
+ if (group == NULL)
+ goto error;
+
+ /*
+ * Add an option record to the current JCLs...
+ */
+
+ if (name[0] == '*')
+ _cups_strcpy(name, name + 1);
+
+ option = ppd_get_option(group, name);
+
+ if (option == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ /*
+ * Now fill in the initial information for the option...
+ */
+
+ if (string && !strcmp(string, "PickMany"))
+ option->ui = PPD_UI_PICKMANY;
+ else if (string && !strcmp(string, "Boolean"))
+ option->ui = PPD_UI_BOOLEAN;
+ else if (string && !strcmp(string, "PickOne"))
+ option->ui = PPD_UI_PICKONE;
+ else
+ {
+ cg->ppd_status = PPD_BAD_OPEN_UI;
+
+ goto error;
+ }
+
+ for (j = 0; j < ppd->num_attrs; j ++)
+ if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
+ !strcmp(ppd->attrs[j]->name + 7, name) &&
+ ppd->attrs[j]->value)
+ {
+ DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
+ option->keyword, ppd->attrs[j]->value));
+ strlcpy(option->defchoice, ppd->attrs[j]->value,
+ sizeof(option->defchoice));
+ break;
+ }
+
+ if (text[0])
+ cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
+ sizeof(option->text), encoding);
+ else
+ strlcpy(option->text, name, sizeof(option->text));
+
+ option->section = PPD_ORDER_JCL;
+ group = NULL;
+
+ _cupsStrFree(string);
+ string = NULL;
+
+ /*
+ * Add a custom option choice if we have already seen a CustomFoo
+ * attribute...
+ */
+
+ snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
+
+ if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
+ {
+ if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+ {
+ DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
+
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ strlcpy(choice->text,
+ custom_attr->text[0] ? custom_attr->text : _("Custom"),
+ sizeof(choice->text));
+ choice->code = _cupsStrRetain(custom_attr->value);
+ }
+ }
+ else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
+ {
+ option = NULL;
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (!strcmp(keyword, "OpenGroup"))
+ {
+ /*
+ * Open a new group...
+ */
+
+ if (group != NULL)
+ {
+ cg->ppd_status = PPD_NESTED_OPEN_GROUP;
+
+ goto error;
+ }
+
+ if (!string)
+ {
+ cg->ppd_status = PPD_BAD_OPEN_GROUP;
+
+ goto error;
+ }
+
+ /*
+ * Separate the group name from the text (name/text)...
+ */
+
+ if ((sptr = strchr(string, '/')) != NULL)
+ *sptr++ = '\0';
+ else
+ sptr = string;
+
+ /*
+ * Fix up the text...
+ */
+
+ ppd_decode(sptr);
+
+ /*
+ * Find/add the group...
+ */
+
+ group = ppd_get_group(ppd, string, sptr, cg, encoding);
+
+ if (group == NULL)
+ goto error;
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (!strcmp(keyword, "CloseGroup"))
+ {
+ group = NULL;
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (!strcmp(keyword, "OrderDependency"))
+ {
+ order = (float)_cupsStrScand(string, &sptr, loc);
+
+ if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
+ {
+ cg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
+
+ goto error;
+ }
+
+ if (keyword[0] == '*')
+ _cups_strcpy(keyword, keyword + 1);
+
+ if (!strcmp(name, "ExitServer"))
+ section = PPD_ORDER_EXIT;
+ else if (!strcmp(name, "Prolog"))
+ section = PPD_ORDER_PROLOG;
+ else if (!strcmp(name, "DocumentSetup"))
+ section = PPD_ORDER_DOCUMENT;
+ else if (!strcmp(name, "PageSetup"))
+ section = PPD_ORDER_PAGE;
+ else if (!strcmp(name, "JCLSetup"))
+ section = PPD_ORDER_JCL;
+ else
+ section = PPD_ORDER_ANY;
+
+ if (option == NULL)
+ {
+ ppd_group_t *gtemp;
+
+
+ /*
+ * Only valid for Non-UI options...
+ */
+
+ for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
+ if (gtemp->text[0] == '\0')
+ break;
+
+ if (i > 0)
+ for (i = 0; i < gtemp->num_options; i ++)
+ if (!strcmp(keyword, gtemp->options[i].keyword))
+ {
+ gtemp->options[i].section = section;
+ gtemp->options[i].order = order;
+ break;
+ }
+ }
+ else
+ {
+ option->section = section;
+ option->order = order;
+ }
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (!strncmp(keyword, "Default", 7))
+ {
+ if (string == NULL)
+ continue;
+
+ /*
+ * Drop UI text, if any, from value...
+ */
+
+ if (strchr(string, '/') != NULL)
+ *strchr(string, '/') = '\0';
+
+ /*
+ * Assign the default value as appropriate...
+ */
+
+ if (!strcmp(keyword, "DefaultColorSpace"))
+ {
+ /*
+ * Set default colorspace...
+ */
+
+ if (!strcmp(string, "CMY"))
+ ppd->colorspace = PPD_CS_CMY;
+ else if (!strcmp(string, "CMYK"))
+ ppd->colorspace = PPD_CS_CMYK;
+ else if (!strcmp(string, "RGB"))
+ ppd->colorspace = PPD_CS_RGB;
+ else if (!strcmp(string, "RGBK"))
+ ppd->colorspace = PPD_CS_RGBK;
+ else if (!strcmp(string, "N"))
+ ppd->colorspace = PPD_CS_N;
+ else
+ ppd->colorspace = PPD_CS_GRAY;
+ }
+ else if (option && !strcmp(keyword + 7, option->keyword))
+ {
+ /*
+ * Set the default as part of the current option...
+ */
+
+ DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
+
+ strlcpy(option->defchoice, string, sizeof(option->defchoice));
+
+ DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
+ }
+ else
+ {
+ /*
+ * Lookup option and set if it has been defined...
+ */
+
+ ppd_option_t *toption; /* Temporary option */
+
+
+ if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
+ {
+ DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
+ strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
+ }
+ }
+ }
+ else if (!strcmp(keyword, "UIConstraints") ||
+ !strcmp(keyword, "NonUIConstraints"))
+ {
+ if (!string)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (ppd->num_consts == 0)
+ constraint = calloc(2, sizeof(ppd_const_t));
+ else
+ constraint = realloc(ppd->consts,
+ (ppd->num_consts + 2) * sizeof(ppd_const_t));
+
+ if (constraint == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ ppd->consts = constraint;
+ constraint += ppd->num_consts;
+ ppd->num_consts ++;
+
+ switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
+ constraint->choice1, constraint->option2,
+ constraint->choice2))
+ {
+ case 0 : /* Error */
+ case 1 : /* Error */
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+
+ case 2 : /* Two options... */
+ /*
+ * Check for broken constraints like "* Option"...
+ */
+
+ if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+ (!strcmp(constraint->option1, "*") ||
+ !strcmp(constraint->choice1, "*")))
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ /*
+ * The following strcpy's are safe, as optionN and
+ * choiceN are all the same size (size defined by PPD spec...)
+ */
+
+ if (constraint->option1[0] == '*')
+ _cups_strcpy(constraint->option1, constraint->option1 + 1);
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (constraint->choice1[0] == '*')
+ _cups_strcpy(constraint->option2, constraint->choice1 + 1);
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ constraint->choice1[0] = '\0';
+ constraint->choice2[0] = '\0';
+ break;
+
+ case 3 : /* Two options, one choice... */
+ /*
+ * Check for broken constraints like "* Option"...
+ */
+
+ if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+ (!strcmp(constraint->option1, "*") ||
+ !strcmp(constraint->choice1, "*") ||
+ !strcmp(constraint->option2, "*")))
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ /*
+ * The following _cups_strcpy's are safe, as optionN and
+ * choiceN are all the same size (size defined by PPD spec...)
+ */
+
+ if (constraint->option1[0] == '*')
+ _cups_strcpy(constraint->option1, constraint->option1 + 1);
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (constraint->choice1[0] == '*')
+ {
+ if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+ constraint->option2[0] == '*')
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ _cups_strcpy(constraint->choice2, constraint->option2);
+ _cups_strcpy(constraint->option2, constraint->choice1 + 1);
+ constraint->choice1[0] = '\0';
+ }
+ else
+ {
+ if (constraint->option2[0] == '*')
+ _cups_strcpy(constraint->option2, constraint->option2 + 1);
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ constraint->choice2[0] = '\0';
+ }
+ break;
+
+ case 4 : /* Two options, two choices... */
+ /*
+ * Check for broken constraints like "* Option"...
+ */
+
+ if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+ (!strcmp(constraint->option1, "*") ||
+ !strcmp(constraint->choice1, "*") ||
+ !strcmp(constraint->option2, "*") ||
+ !strcmp(constraint->choice2, "*")))
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (constraint->option1[0] == '*')
+ _cups_strcpy(constraint->option1, constraint->option1 + 1);
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+ constraint->choice1[0] == '*')
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (constraint->option2[0] == '*')
+ _cups_strcpy(constraint->option2, constraint->option2 + 1);
+ else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+
+ if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+ constraint->choice2[0] == '*')
+ {
+ cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+ goto error;
+ }
+ break;
+ }
+
+ /*
+ * Don't add this one as an attribute...
+ */
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (!strcmp(keyword, "PaperDimension"))
+ {
+ if ((size = ppdPageSize(ppd, name)) == NULL)
+ size = ppd_add_size(ppd, name);
+
+ if (size == NULL)
+ {
+ /*
+ * Unable to add or find size!
+ */
+
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ size->width = (float)_cupsStrScand(string, &sptr, loc);
+ size->length = (float)_cupsStrScand(sptr, NULL, loc);
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (!strcmp(keyword, "ImageableArea"))
+ {
+ if ((size = ppdPageSize(ppd, name)) == NULL)
+ size = ppd_add_size(ppd, name);
+
+ if (size == NULL)
+ {
+ /*
+ * Unable to add or find size!
+ */
+
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ size->left = (float)_cupsStrScand(string, &sptr, loc);
+ size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
+ size->right = (float)_cupsStrScand(sptr, &sptr, loc);
+ size->top = (float)_cupsStrScand(sptr, NULL, loc);
+
+ _cupsStrFree(string);
+ string = NULL;
+ }
+ else if (option != NULL &&
+ (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
+ (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
+ !strcmp(keyword, option->keyword))
+ {
+ DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
+
+ if (!strcmp(keyword, "PageSize"))
+ {
+ /*
+ * Add a page size...
+ */
+
+ if (ppdPageSize(ppd, name) == NULL)
+ ppd_add_size(ppd, name);
+ }
+
+ /*
+ * Add the option choice...
+ */
+
+ if ((choice = ppd_add_choice(option, name)) == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ goto error;
+ }
+
+ if (text[0])
+ cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
+ sizeof(choice->text), encoding);
+ else if (!strcmp(name, "True"))
+ strlcpy(choice->text, _("Yes"), sizeof(choice->text));
+ else if (!strcmp(name, "False"))
+ strlcpy(choice->text, _("No"), sizeof(choice->text));
+ else
+ strlcpy(choice->text, name, sizeof(choice->text));
+
+ if (option->section == PPD_ORDER_JCL)
+ ppd_decode(string); /* Decode quoted string */
+
+ choice->code = string;
+ string = NULL; /* Don't add as an attribute below */
+ }
+
+ /*
+ * Add remaining lines with keywords and string values as attributes...
+ */
+
+ if (string &&
+ (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
+ ppd_add_attr(ppd, keyword, name, text, string);
+ else
+ _cupsStrFree(string);
+ }
+
+ /*
+ * Check for a missing CloseGroup...
+ */
+
+ if (group && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_MISSING_CLOSE_GROUP;
+ goto error;
+ }
+
+ ppd_free(line.buffer);
+
+ /*
+ * Reset language preferences...
+ */
+
+#ifdef DEBUG
+ if (!cupsFileEOF(fp))
+ DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
+ (unsigned long)cupsFileTell(fp)));
+#endif /* DEBUG */
+
+ if (cg->ppd_status != PPD_OK)
+ {
+ /*
+ * Had an error reading the PPD file, cannot continue!
+ */
+
+ ppdClose(ppd);
+
+ return (NULL);
+ }
+
+ /*
+ * Update the filters array as needed...
+ */
+
+ if (!ppd_update_filters(ppd, cg))
+ {
+ ppdClose(ppd);
+
+ return (NULL);
+ }
+
+ /*
+ * Create the sorted options array and set the option back-pointer for
+ * each choice and custom option...
+ */
+
+ ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
+ (cups_ahash_func_t)ppd_hash_option,
+ PPD_HASHSIZE);
+
+ for (i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ {
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ ppd_coption_t *coption; /* Custom option */
+
+
+ cupsArrayAdd(ppd->options, option);
+
+ for (k = 0; k < option->num_choices; k ++)
+ option->choices[k].option = option;
+
+ if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
+ coption->option = option;
+ }
+ }
+
+ /*
+ * Create an array to track the marked choices...
+ */
+
+ ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
+
+ /*
+ * Return the PPD file structure...
+ */
+
+ return (ppd);
+
+ /*
+ * Common exit point for errors to save code size...
+ */
+
+ error:
+
+ _cupsStrFree(string);
+ ppd_free(line.buffer);
+
+ ppdClose(ppd);
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdOpen()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record */
+ppdOpen(FILE *fp) /* I - File to read from */
+{
+ ppd_file_t *ppd; /* PPD file record */
+ cups_file_t *cf; /* CUPS file */
+
+
+ /*
+ * Reopen the stdio file as a CUPS file...
+ */
+
+ if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
+ return (NULL);
+
+ /*
+ * Load the PPD file using the newer API...
+ */
+
+ ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
+
+ /*
+ * Close the CUPS file and return the PPD...
+ */
+
+ cupsFileClose(cf);
+
+ return (ppd);
+}
+
+
+/*
+ * 'ppdOpen2()' - Read a PPD file into memory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+ppdOpen2(cups_file_t *fp) /* I - File to read from */
+{
+ return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
+}
+
+
+/*
+ * 'ppdOpenFd()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+ppdOpenFd(int fd) /* I - File to read from */
+{
+ cups_file_t *fp; /* CUPS file pointer */
+ ppd_file_t *ppd; /* PPD file record */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ /*
+ * Set the line number to 0...
+ */
+
+ cg->ppd_line = 0;
+
+ /*
+ * Range check input...
+ */
+
+ if (fd < 0)
+ {
+ cg->ppd_status = PPD_NULL_FILE;
+
+ return (NULL);
+ }
+
+ /*
+ * Try to open the file and parse it...
+ */
+
+ if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
+ {
+ ppd = ppdOpen2(fp);
+
+ cupsFileClose(fp);
+ }
+ else
+ {
+ cg->ppd_status = PPD_FILE_OPEN_ERROR;
+ ppd = NULL;
+ }
+
+ return (ppd);
+}
+
+
+/*
+ * '_ppdOpenFile()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+_ppdOpenFile(const char *filename, /* I - File to read from */
+ _ppd_localization_t localization) /* I - Localization to load */
+{
+ cups_file_t *fp; /* File pointer */
+ ppd_file_t *ppd; /* PPD file record */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ /*
+ * Set the line number to 0...
+ */
+
+ cg->ppd_line = 0;
+
+ /*
+ * Range check input...
+ */
+
+ if (filename == NULL)
+ {
+ cg->ppd_status = PPD_NULL_FILE;
+
+ return (NULL);
+ }
+
+ /*
+ * Try to open the file and parse it...
+ */
+
+ if ((fp = cupsFileOpen(filename, "r")) != NULL)
+ {
+ ppd = _ppdOpen(fp, localization);
+
+ cupsFileClose(fp);
+ }
+ else
+ {
+ cg->ppd_status = PPD_FILE_OPEN_ERROR;
+ ppd = NULL;
+ }
+
+ return (ppd);
+}
+
+
+/*
+ * 'ppdOpenFile()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+ppdOpenFile(const char *filename) /* I - File to read from */
+{
+ return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
+}
+
+
+/*
+ * 'ppdSetConformance()' - Set the conformance level for PPD files.
+ *
+ * @since CUPS 1.1.20/OS X 10.4@
+ */
+
+void
+ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
+{
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Global data */
+
+
+ cg->ppd_conform = c;
+}
+
+
+/*
+ * 'ppd_add_attr()' - Add an attribute to the PPD data.
+ */
+
+static ppd_attr_t * /* O - New attribute */
+ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
+ const char *name, /* I - Attribute name */
+ const char *spec, /* I - Specifier string, if any */
+ const char *text, /* I - Text string, if any */
+ const char *value) /* I - Value of attribute */
+{
+ ppd_attr_t **ptr, /* New array */
+ *temp; /* New attribute */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (ppd == NULL || name == NULL || spec == NULL)
+ return (NULL);
+
+ /*
+ * Create the array as needed...
+ */
+
+ if (!ppd->sorted_attrs)
+ ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
+ NULL);
+
+ /*
+ * Allocate memory for the new attribute...
+ */
+
+ if (ppd->num_attrs == 0)
+ ptr = malloc(sizeof(ppd_attr_t *));
+ else
+ ptr = realloc(ppd->attrs, (ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
+
+ if (ptr == NULL)
+ return (NULL);
+
+ ppd->attrs = ptr;
+ ptr += ppd->num_attrs;
+
+ if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
+ return (NULL);
+
+ *ptr = temp;
+
+ ppd->num_attrs ++;
+
+ /*
+ * Copy data over...
+ */
+
+ strlcpy(temp->name, name, sizeof(temp->name));
+ strlcpy(temp->spec, spec, sizeof(temp->spec));
+ strlcpy(temp->text, text, sizeof(temp->text));
+ temp->value = (char *)value;
+
+ /*
+ * Add the attribute to the sorted array...
+ */
+
+ cupsArrayAdd(ppd->sorted_attrs, temp);
+
+ /*
+ * Return the attribute...
+ */
+
+ return (temp);
+}
+
+
+/*
+ * 'ppd_add_choice()' - Add a choice to an option.
+ */
+
+static ppd_choice_t * /* O - Named choice */
+ppd_add_choice(ppd_option_t *option, /* I - Option */
+ const char *name) /* I - Name of choice */
+{
+ ppd_choice_t *choice; /* Choice */
+
+
+ if (option->num_choices == 0)
+ choice = malloc(sizeof(ppd_choice_t));
+ else
+ choice = realloc(option->choices,
+ sizeof(ppd_choice_t) * (option->num_choices + 1));
+
+ if (choice == NULL)
+ return (NULL);
+
+ option->choices = choice;
+ choice += option->num_choices;
+ option->num_choices ++;
+
+ memset(choice, 0, sizeof(ppd_choice_t));
+ strlcpy(choice->choice, name, sizeof(choice->choice));
+
+ return (choice);
+}
+
+
+/*
+ * 'ppd_add_size()' - Add a page size.
+ */
+
+static ppd_size_t * /* O - Named size */
+ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
+ const char *name) /* I - Name of size */
+{
+ ppd_size_t *size; /* Size */
+
+
+ if (ppd->num_sizes == 0)
+ size = malloc(sizeof(ppd_size_t));
+ else
+ size = realloc(ppd->sizes, sizeof(ppd_size_t) * (ppd->num_sizes + 1));
+
+ if (size == NULL)
+ return (NULL);
+
+ ppd->sizes = size;
+ size += ppd->num_sizes;
+ ppd->num_sizes ++;
+
+ memset(size, 0, sizeof(ppd_size_t));
+ strlcpy(size->name, name, sizeof(size->name));
+
+ return (size);
+}
+
+
+/*
+ * 'ppd_compare_attrs()' - Compare two attributes.
+ */
+
+static int /* O - Result of comparison */
+ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
+ ppd_attr_t *b) /* I - Second attribute */
+{
+ return (_cups_strcasecmp(a->name, b->name));
+}
+
+
+/*
+ * 'ppd_compare_choices()' - Compare two choices...
+ */
+
+static int /* O - Result of comparison */
+ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
+ ppd_choice_t *b) /* I - Second choice */
+{
+ return (strcmp(a->option->keyword, b->option->keyword));
+}
+
+
+/*
+ * 'ppd_compare_coptions()' - Compare two custom options.
+ */
+
+static int /* O - Result of comparison */
+ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
+ ppd_coption_t *b) /* I - Second option */
+{
+ return (_cups_strcasecmp(a->keyword, b->keyword));
+}
+
+
+/*
+ * 'ppd_compare_options()' - Compare two options.
+ */
+
+static int /* O - Result of comparison */
+ppd_compare_options(ppd_option_t *a, /* I - First option */
+ ppd_option_t *b) /* I - Second option */
+{
+ return (_cups_strcasecmp(a->keyword, b->keyword));
+}
+
+
+/*
+ * 'ppd_decode()' - Decode a string value...
+ */
+
+static int /* O - Length of decoded string */
+ppd_decode(char *string) /* I - String to decode */
+{
+ char *inptr, /* Input pointer */
+ *outptr; /* Output pointer */
+
+
+ inptr = string;
+ outptr = string;
+
+ while (*inptr != '\0')
+ if (*inptr == '<' && isxdigit(inptr[1] & 255))
+ {
+ /*
+ * Convert hex to 8-bit values...
+ */
+
+ inptr ++;
+ while (isxdigit(*inptr & 255))
+ {
+ if (_cups_isalpha(*inptr))
+ *outptr = (tolower(*inptr) - 'a' + 10) << 4;
+ else
+ *outptr = (*inptr - '0') << 4;
+
+ inptr ++;
+
+ if (!isxdigit(*inptr & 255))
+ break;
+
+ if (_cups_isalpha(*inptr))
+ *outptr |= tolower(*inptr) - 'a' + 10;
+ else
+ *outptr |= *inptr - '0';
+
+ inptr ++;
+ outptr ++;
+ }
+
+ while (*inptr != '>' && *inptr != '\0')
+ inptr ++;
+ while (*inptr == '>')
+ inptr ++;
+ }
+ else
+ *outptr++ = *inptr++;
+
+ *outptr = '\0';
+
+ return ((int)(outptr - string));
+}
+
+
+/*
+ * 'ppd_free_filters()' - Free the filters array.
+ */
+
+static void
+ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */
+{
+ int i; /* Looping var */
+ char **filter; /* Current filter */
+
+
+ if (ppd->num_filters > 0)
+ {
+ for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
+ _cupsStrFree(*filter);
+
+ ppd_free(ppd->filters);
+
+ ppd->num_filters = 0;
+ ppd->filters = NULL;
+ }
+}
+
+
+/*
+ * 'ppd_free_group()' - Free a single UI group.
+ */
+
+static void
+ppd_free_group(ppd_group_t *group) /* I - Group to free */
+{
+ int i; /* Looping var */
+ ppd_option_t *option; /* Current option */
+ ppd_group_t *subgroup; /* Current sub-group */
+
+
+ if (group->num_options > 0)
+ {
+ for (i = group->num_options, option = group->options;
+ i > 0;
+ i --, option ++)
+ ppd_free_option(option);
+
+ ppd_free(group->options);
+ }
+
+ if (group->num_subgroups > 0)
+ {
+ for (i = group->num_subgroups, subgroup = group->subgroups;
+ i > 0;
+ i --, subgroup ++)
+ ppd_free_group(subgroup);
+
+ ppd_free(group->subgroups);
+ }
+}
+
+
+/*
+ * 'ppd_free_option()' - Free a single option.
+ */
+
+static void
+ppd_free_option(ppd_option_t *option) /* I - Option to free */
+{
+ int i; /* Looping var */
+ ppd_choice_t *choice; /* Current choice */
+
+
+ if (option->num_choices > 0)
+ {
+ for (i = option->num_choices, choice = option->choices;
+ i > 0;
+ i --, choice ++)
+ {
+ _cupsStrFree(choice->code);
+ }
+
+ ppd_free(option->choices);
+ }
+}
+
+
+/*
+ * 'ppd_get_coption()' - Get a custom option record.
+ */
+
+static ppd_coption_t * /* O - Custom option... */
+ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
+ const char *name) /* I - Name of option */
+{
+ ppd_coption_t *copt; /* New custom option */
+
+
+ /*
+ * See if the option already exists...
+ */
+
+ if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
+ return (copt);
+
+ /*
+ * Not found, so create the custom option record...
+ */
+
+ if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
+ return (NULL);
+
+ strlcpy(copt->keyword, name, sizeof(copt->keyword));
+
+ copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
+
+ cupsArrayAdd(ppd->coptions, copt);
+
+ /*
+ * Return the new record...
+ */
+
+ return (copt);
+}
+
+
+/*
+ * 'ppd_get_cparam()' - Get a custom parameter record.
+ */
+
+static ppd_cparam_t * /* O - Extended option... */
+ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
+ const char *param, /* I - Name of parameter */
+ const char *text) /* I - Human-readable text */
+{
+ ppd_cparam_t *cparam; /* New custom parameter */
+
+
+ /*
+ * See if the parameter already exists...
+ */
+
+ if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
+ return (cparam);
+
+ /*
+ * Not found, so create the custom parameter record...
+ */
+
+ if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
+ return (NULL);
+
+ strlcpy(cparam->name, param, sizeof(cparam->name));
+ strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
+
+ /*
+ * Add this record to the array...
+ */
+
+ cupsArrayAdd(opt->params, cparam);
+
+ /*
+ * Return the new record...
+ */
+
+ return (cparam);
+}
+
+
+/*
+ * 'ppd_get_group()' - Find or create the named group as needed.
+ */
+
+static ppd_group_t * /* O - Named group */
+ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
+ const char *name, /* I - Name of group */
+ const char *text, /* I - Text for group */
+ _cups_globals_t *cg, /* I - Global data */
+ cups_encoding_t encoding) /* I - Encoding of text */
+{
+ int i; /* Looping var */
+ ppd_group_t *group; /* Group */
+
+
+ DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
+ ppd, name, text, cg));
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ if (!strcmp(group->name, name))
+ break;
+
+ if (i == 0)
+ {
+ DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
+
+ if (cg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
+ {
+ cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
+
+ return (NULL);
+ }
+
+ if (ppd->num_groups == 0)
+ group = malloc(sizeof(ppd_group_t));
+ else
+ group = realloc(ppd->groups,
+ (ppd->num_groups + 1) * sizeof(ppd_group_t));
+
+ if (group == NULL)
+ {
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ return (NULL);
+ }
+
+ ppd->groups = group;
+ group += ppd->num_groups;
+ ppd->num_groups ++;
+
+ memset(group, 0, sizeof(ppd_group_t));
+ strlcpy(group->name, name, sizeof(group->name));
+
+ cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
+ sizeof(group->text), encoding);
+ }
+
+ return (group);
+}
+
+
+/*
+ * 'ppd_get_option()' - Find or create the named option as needed.
+ */
+
+static ppd_option_t * /* O - Named option */
+ppd_get_option(ppd_group_t *group, /* I - Group */
+ const char *name) /* I - Name of option */
+{
+ int i; /* Looping var */
+ ppd_option_t *option; /* Option */
+
+
+ DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
+ group, group->name, name));
+
+ for (i = group->num_options, option = group->options; i > 0; i --, option ++)
+ if (!strcmp(option->keyword, name))
+ break;
+
+ if (i == 0)
+ {
+ if (group->num_options == 0)
+ option = malloc(sizeof(ppd_option_t));
+ else
+ option = realloc(group->options,
+ (group->num_options + 1) * sizeof(ppd_option_t));
+
+ if (option == NULL)
+ return (NULL);
+
+ group->options = option;
+ option += group->num_options;
+ group->num_options ++;
+
+ memset(option, 0, sizeof(ppd_option_t));
+ strlcpy(option->keyword, name, sizeof(option->keyword));
+ }
+
+ return (option);
+}
+
+
+/*
+ * 'ppd_hash_option()' - Generate a hash of the option name...
+ */
+
+static int /* O - Hash index */
+ppd_hash_option(ppd_option_t *option) /* I - Option */
+{
+ int hash = 0; /* Hash index */
+ const char *k; /* Pointer into keyword */
+
+
+ for (hash = option->keyword[0], k = option->keyword + 1; *k;)
+ hash = 33 * hash + *k++;
+
+ return (hash & 511);
+}
+
+
+/*
+ * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
+ * necessary.
+ */
+
+static int /* O - Bitmask of fields read */
+ppd_read(cups_file_t *fp, /* I - File to read from */
+ _ppd_line_t *line, /* I - Line buffer */
+ char *keyword, /* O - Keyword from line */
+ char *option, /* O - Option from line */
+ char *text, /* O - Human-readable text from line */
+ char **string, /* O - Code/string data */
+ int ignoreblank, /* I - Ignore blank lines? */
+ _cups_globals_t *cg) /* I - Global data */
+{
+ int ch, /* Character from file */
+ col, /* Column in line */
+ colon, /* Colon seen? */
+ endquote, /* Waiting for an end quote */
+ mask, /* Mask to be returned */
+ startline, /* Start line */
+ textlen; /* Length of text */
+ char *keyptr, /* Keyword pointer */
+ *optptr, /* Option pointer */
+ *textptr, /* Text pointer */
+ *strptr, /* Pointer into string */
+ *lineptr; /* Current position in line buffer */
+
+
+ /*
+ * Now loop until we have a valid line...
+ */
+
+ *string = NULL;
+ col = 0;
+ startline = cg->ppd_line + 1;
+
+ if (!line->buffer)
+ {
+ line->bufsize = 1024;
+ line->buffer = malloc(1024);
+
+ if (!line->buffer)
+ return (0);
+ }
+
+ do
+ {
+ /*
+ * Read the line...
+ */
+
+ lineptr = line->buffer;
+ endquote = 0;
+ colon = 0;
+
+ while ((ch = cupsFileGetChar(fp)) != EOF)
+ {
+ if (lineptr >= (line->buffer + line->bufsize - 1))
+ {
+ /*
+ * Expand the line buffer...
+ */
+
+ char *temp; /* Temporary line pointer */
+
+
+ line->bufsize += 1024;
+ if (line->bufsize > 262144)
+ {
+ /*
+ * Don't allow lines longer than 256k!
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_LINE_TOO_LONG;
+
+ return (0);
+ }
+
+ temp = realloc(line->buffer, line->bufsize);
+ if (!temp)
+ {
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_LINE_TOO_LONG;
+
+ return (0);
+ }
+
+ lineptr = temp + (lineptr - line->buffer);
+ line->buffer = temp;
+ }
+
+ if (ch == '\r' || ch == '\n')
+ {
+ /*
+ * Line feed or carriage return...
+ */
+
+ cg->ppd_line ++;
+ col = 0;
+
+ if (ch == '\r')
+ {
+ /*
+ * Check for a trailing line feed...
+ */
+
+ if ((ch = cupsFilePeekChar(fp)) == EOF)
+ {
+ ch = '\n';
+ break;
+ }
+
+ if (ch == 0x0a)
+ cupsFileGetChar(fp);
+ }
+
+ if (lineptr == line->buffer && ignoreblank)
+ continue; /* Skip blank lines */
+
+ ch = '\n';
+
+ if (!endquote) /* Continue for multi-line text */
+ break;
+
+ *lineptr++ = '\n';
+ }
+ else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ /*
+ * Other control characters...
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_ILLEGAL_CHARACTER;
+
+ return (0);
+ }
+ else if (ch != 0x1a)
+ {
+ /*
+ * Any other character...
+ */
+
+ *lineptr++ = ch;
+ col ++;
+
+ if (col > (PPD_MAX_LINE - 1))
+ {
+ /*
+ * Line is too long...
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_LINE_TOO_LONG;
+
+ return (0);
+ }
+
+ if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
+ colon = 1;
+
+ if (ch == '\"' && colon)
+ endquote = !endquote;
+ }
+ }
+
+ if (endquote)
+ {
+ /*
+ * Didn't finish this quoted string...
+ */
+
+ while ((ch = cupsFileGetChar(fp)) != EOF)
+ if (ch == '\"')
+ break;
+ else if (ch == '\r' || ch == '\n')
+ {
+ cg->ppd_line ++;
+ col = 0;
+
+ if (ch == '\r')
+ {
+ /*
+ * Check for a trailing line feed...
+ */
+
+ if ((ch = cupsFilePeekChar(fp)) == EOF)
+ break;
+ if (ch == 0x0a)
+ cupsFileGetChar(fp);
+ }
+ }
+ else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ /*
+ * Other control characters...
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_ILLEGAL_CHARACTER;
+
+ return (0);
+ }
+ else if (ch != 0x1a)
+ {
+ col ++;
+
+ if (col > (PPD_MAX_LINE - 1))
+ {
+ /*
+ * Line is too long...
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_LINE_TOO_LONG;
+
+ return (0);
+ }
+ }
+ }
+
+ if (ch != '\n')
+ {
+ /*
+ * Didn't finish this line...
+ */
+
+ while ((ch = cupsFileGetChar(fp)) != EOF)
+ if (ch == '\r' || ch == '\n')
+ {
+ /*
+ * Line feed or carriage return...
+ */
+
+ cg->ppd_line ++;
+ col = 0;
+
+ if (ch == '\r')
+ {
+ /*
+ * Check for a trailing line feed...
+ */
+
+ if ((ch = cupsFilePeekChar(fp)) == EOF)
+ break;
+ if (ch == 0x0a)
+ cupsFileGetChar(fp);
+ }
+
+ break;
+ }
+ else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ /*
+ * Other control characters...
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_ILLEGAL_CHARACTER;
+
+ return (0);
+ }
+ else if (ch != 0x1a)
+ {
+ col ++;
+
+ if (col > (PPD_MAX_LINE - 1))
+ {
+ /*
+ * Line is too long...
+ */
+
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_LINE_TOO_LONG;
+
+ return (0);
+ }
+ }
+ }
+
+ if (lineptr > line->buffer && lineptr[-1] == '\n')
+ lineptr --;
+
+ *lineptr = '\0';
+
+ DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
+
+ /*
+ * The dynamically created PPDs for older style OS X
+ * drivers include a large blob of data inserted as comments
+ * at the end of the file. As an optimization we can stop
+ * reading the PPD when we get to the start of this data.
+ */
+
+ if (!strcmp(line->buffer, "*%APLWORKSET START"))
+ return (0);
+
+ if (ch == EOF && lineptr == line->buffer)
+ return (0);
+
+ /*
+ * Now parse it...
+ */
+
+ mask = 0;
+ lineptr = line->buffer + 1;
+
+ keyword[0] = '\0';
+ option[0] = '\0';
+ text[0] = '\0';
+ *string = NULL;
+
+ if ((!line->buffer[0] || /* Blank line */
+ !strncmp(line->buffer, "*%", 2) || /* Comment line */
+ !strcmp(line->buffer, "*End")) && /* End of multi-line string */
+ ignoreblank) /* Ignore these? */
+ {
+ startline = cg->ppd_line + 1;
+ continue;
+ }
+
+ if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
+ {
+ if (cg->ppd_conform == PPD_CONFORM_RELAXED)
+ {
+ startline = cg->ppd_line + 1;
+ continue;
+ }
+ else
+ {
+ cg->ppd_line = startline;
+ cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
+
+ return (0);
+ }
+ }
+
+ if (line->buffer[0] != '*') /* All lines start with an asterisk */
+ {
+ /*
+ * Allow lines consisting of just whitespace...
+ */
+
+ for (lineptr = line->buffer; *lineptr; lineptr ++)
+ if (*lineptr && !_cups_isspace(*lineptr))
+ break;
+
+ if (*lineptr)
+ {
+ cg->ppd_status = PPD_MISSING_ASTERISK;
+ return (0);
+ }
+ else if (ignoreblank)
+ continue;
+ else
+ return (0);
+ }
+
+ /*
+ * Get a keyword...
+ */
+
+ keyptr = keyword;
+
+ while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
+ {
+ if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
+ (keyptr - keyword) >= (PPD_MAX_NAME - 1))
+ {
+ cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
+ return (0);
+ }
+
+ *keyptr++ = *lineptr++;
+ }
+
+ *keyptr = '\0';
+
+ if (!strcmp(keyword, "End"))
+ continue;
+
+ mask |= PPD_KEYWORD;
+
+ if (_cups_isspace(*lineptr))
+ {
+ /*
+ * Get an option name...
+ */
+
+ while (_cups_isspace(*lineptr))
+ lineptr ++;
+
+ optptr = option;
+
+ while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
+ *lineptr != '/')
+ {
+ if (*lineptr <= ' ' || *lineptr > 126 ||
+ (optptr - option) >= (PPD_MAX_NAME - 1))
+ {
+ cg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
+ return (0);
+ }
+
+ *optptr++ = *lineptr++;
+ }
+
+ *optptr = '\0';
+
+ if (_cups_isspace(*lineptr) && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
+ return (0);
+ }
+
+ while (_cups_isspace(*lineptr))
+ lineptr ++;
+
+ mask |= PPD_OPTION;
+
+ if (*lineptr == '/')
+ {
+ /*
+ * Get human-readable text...
+ */
+
+ lineptr ++;
+
+ textptr = text;
+
+ while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
+ {
+ if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
+ (textptr - text) >= (PPD_MAX_LINE - 1))
+ {
+ cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
+ return (0);
+ }
+
+ *textptr++ = *lineptr++;
+ }
+
+ *textptr = '\0';
+ textlen = ppd_decode(text);
+
+ if (textlen > PPD_MAX_TEXT && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
+ return (0);
+ }
+
+ mask |= PPD_TEXT;
+ }
+ }
+
+ if (_cups_isspace(*lineptr) && cg->ppd_conform == PPD_CONFORM_STRICT)
+ {
+ cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
+ return (0);
+ }
+
+ while (_cups_isspace(*lineptr))
+ lineptr ++;
+
+ if (*lineptr == ':')
+ {
+ /*
+ * Get string after triming leading and trailing whitespace...
+ */
+
+ lineptr ++;
+ while (_cups_isspace(*lineptr))
+ lineptr ++;
+
+ strptr = lineptr + strlen(lineptr) - 1;
+ while (strptr >= lineptr && _cups_isspace(*strptr))
+ *strptr-- = '\0';
+
+ if (*strptr == '\"')
+ {
+ /*
+ * Quoted string by itself, remove quotes...
+ */
+
+ *strptr = '\0';
+ lineptr ++;
+ }
+
+ *string = _cupsStrAlloc(lineptr);
+
+ mask |= PPD_STRING;
+ }
+ }
+ while (mask == 0);
+
+ return (mask);
+}
+
+
+/*
+ * 'ppd_update_filters()' - Update the filters array as needed.
+ *
+ * This function re-populates the filters array with cupsFilter2 entries that
+ * have been stripped of the destination MIME media types and any maxsize hints.
+ *
+ * (All for backwards-compatibility)
+ */
+
+static int /* O - 1 on success, 0 on failure */
+ppd_update_filters(ppd_file_t *ppd,/* I - PPD file */
+ _cups_globals_t *cg) /* I - Global data */
+{
+ ppd_attr_t *attr; /* Current cupsFilter2 value */
+ char srcsuper[16], /* Source MIME media type */
+ srctype[256],
+ dstsuper[16], /* Destination MIME media type */
+ dsttype[256],
+ program[1024], /* Command to run */
+ *ptr, /* Pointer into command to run */
+ buffer[1024], /* Re-written cupsFilter value */
+ **filter; /* Current filter */
+ int cost; /* Cost of filter */
+
+
+ DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, cg));
+
+ /*
+ * See if we have any cupsFilter2 lines...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
+ {
+ DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
+ return (1);
+ }
+
+ /*
+ * Yes, free the cupsFilter-defined filters and re-build...
+ */
+
+ ppd_free_filters(ppd);
+
+ do
+ {
+ /*
+ * Parse the cupsFilter2 string:
+ *
+ * src/type dst/type cost program
+ * src/type dst/type cost maxsize(n) program
+ */
+
+ DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
+
+ if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
+ srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
+ {
+ DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
+ cg->ppd_status = PPD_BAD_VALUE;
+
+ return (0);
+ }
+
+ DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
+ "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
+ srcsuper, srctype, dstsuper, dsttype, cost, program));
+
+ if (!strncmp(program, "maxsize(", 8) &&
+ (ptr = strchr(program + 8, ')')) != NULL)
+ {
+ DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
+
+ ptr ++;
+ while (_cups_isspace(*ptr))
+ ptr ++;
+
+ _cups_strcpy(program, ptr);
+ DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
+ }
+
+ /*
+ * Convert to cupsFilter format:
+ *
+ * src/type cost program
+ */
+
+ snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
+ program);
+ DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
+
+ /*
+ * Add a cupsFilter-compatible string to the filters array.
+ */
+
+ if (ppd->num_filters == 0)
+ filter = malloc(sizeof(char *));
+ else
+ filter = realloc(ppd->filters, sizeof(char *) * (ppd->num_filters + 1));
+
+ if (filter == NULL)
+ {
+ DEBUG_puts("5ppd_update_filters: Out of memory.");
+ cg->ppd_status = PPD_ALLOC_ERROR;
+
+ return (0);
+ }
+
+ ppd->filters = filter;
+ filter += ppd->num_filters;
+ ppd->num_filters ++;
+
+ *filter = _cupsStrAlloc(buffer);
+ }
+ while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
+
+ DEBUG_puts("5ppd_update_filters: Completed OK.");
+ return (1);
+}
+
+
+/*
+ * End of "$Id: ppd.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/ppd.h b/cups/libs/cups/ppd.h
new file mode 100644
index 000000000..d234a59cd
--- /dev/null
+++ b/cups/libs/cups/ppd.h
@@ -0,0 +1,478 @@
+/*
+ * "$Id: ppd.h 11056 2013-06-25 14:27:30Z msweet $"
+ *
+ * PostScript Printer Description definitions for CUPS.
+ *
+ * THESE APIS ARE DEPRECATED. TO COMPILE WITHOUT WARNINGS ADD
+ * -D_PPD_DEPRECATED="" TO YOUR COMPILE OPTIONS. THIS HEADER AND THESE
+ * FUNCTIONS WILL BE REMOVED IN A FUTURE RELEASE OF CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_PPD_H_
+# define _CUPS_PPD_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include "cups.h"
+# include "array.h"
+# include "file.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Define _PPD_DEPRECATED to silence the warnings about PPD functions being
+ * deprecated...
+ */
+
+# ifndef _PPD_DEPRECATED
+# define _PPD_DEPRECATED _CUPS_DEPRECATED_1_6_MSG("Use cupsCopyDestInfo and friends instead.")
+# endif /* !_PPD_DEPRECATED */
+
+
+/*
+ * PPD version...
+ */
+
+# define PPD_VERSION 4.3 /* Kept in sync with Adobe version number */
+
+
+/*
+ * PPD size limits (defined in Adobe spec)
+ */
+
+# define PPD_MAX_NAME 41 /* Maximum size of name + 1 for nul */
+# define PPD_MAX_TEXT 81 /* Maximum size of text + 1 for nul */
+# define PPD_MAX_LINE 256 /* Maximum size of line + 1 for nul */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum ppd_ui_e /**** UI Types ****/
+{
+ PPD_UI_BOOLEAN, /* True or False option */
+ PPD_UI_PICKONE, /* Pick one from a list */
+ PPD_UI_PICKMANY /* Pick zero or more from a list */
+} ppd_ui_t;
+
+typedef enum ppd_section_e /**** Order dependency sections ****/
+{
+ PPD_ORDER_ANY, /* Option code can be anywhere in the file */
+ PPD_ORDER_DOCUMENT, /* ... must be in the DocumentSetup section */
+ PPD_ORDER_EXIT, /* ... must be sent prior to the document */
+ PPD_ORDER_JCL, /* ... must be sent as a JCL command */
+ PPD_ORDER_PAGE, /* ... must be in the PageSetup section */
+ PPD_ORDER_PROLOG /* ... must be in the Prolog section */
+} ppd_section_t;
+
+typedef enum ppd_cs_e /**** Colorspaces ****/
+{
+ PPD_CS_CMYK = -4, /* CMYK colorspace */
+ PPD_CS_CMY, /* CMY colorspace */
+ PPD_CS_GRAY = 1, /* Grayscale colorspace */
+ PPD_CS_RGB = 3, /* RGB colorspace */
+ PPD_CS_RGBK, /* RGBK (K = gray) colorspace */
+ PPD_CS_N /* DeviceN colorspace */
+} ppd_cs_t;
+
+typedef enum ppd_status_e /**** Status Codes @since CUPS 1.1.19/OS X 10.3@ ****/
+{
+ PPD_OK = 0, /* OK */
+ PPD_FILE_OPEN_ERROR, /* Unable to open PPD file */
+ PPD_NULL_FILE, /* NULL PPD file pointer */
+ PPD_ALLOC_ERROR, /* Memory allocation error */
+ PPD_MISSING_PPDADOBE4, /* Missing PPD-Adobe-4.x header */
+ PPD_MISSING_VALUE, /* Missing value string */
+ PPD_INTERNAL_ERROR, /* Internal error */
+ PPD_BAD_OPEN_GROUP, /* Bad OpenGroup */
+ PPD_NESTED_OPEN_GROUP, /* OpenGroup without a CloseGroup first */
+ PPD_BAD_OPEN_UI, /* Bad OpenUI/JCLOpenUI */
+ PPD_NESTED_OPEN_UI, /* OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first */
+ PPD_BAD_ORDER_DEPENDENCY, /* Bad OrderDependency */
+ PPD_BAD_UI_CONSTRAINTS, /* Bad UIConstraints */
+ PPD_MISSING_ASTERISK, /* Missing asterisk in column 0 */
+ PPD_LINE_TOO_LONG, /* Line longer than 255 chars */
+ PPD_ILLEGAL_CHARACTER, /* Illegal control character */
+ PPD_ILLEGAL_MAIN_KEYWORD, /* Illegal main keyword string */
+ PPD_ILLEGAL_OPTION_KEYWORD, /* Illegal option keyword string */
+ PPD_ILLEGAL_TRANSLATION, /* Illegal translation string */
+ PPD_ILLEGAL_WHITESPACE, /* Illegal whitespace character */
+ PPD_BAD_CUSTOM_PARAM, /* Bad custom parameter */
+ PPD_MISSING_OPTION_KEYWORD, /* Missing option keyword */
+ PPD_BAD_VALUE, /* Bad value string */
+ PPD_MISSING_CLOSE_GROUP, /* Missing CloseGroup */
+ PPD_MAX_STATUS /* @private@ */
+} ppd_status_t;
+
+enum ppd_conform_e /**** Conformance Levels @since CUPS 1.1.19/OS X 10.3@ ****/
+{
+ PPD_CONFORM_RELAXED, /* Relax whitespace and control char */
+ PPD_CONFORM_STRICT /* Require strict conformance */
+};
+
+typedef enum ppd_conform_e ppd_conform_t;
+ /**** Conformance Levels @since CUPS 1.1.19/OS X 10.3@ ****/
+
+typedef struct ppd_attr_s /**** PPD Attribute Structure @since CUPS 1.1.19/OS X 10.3@ ****/
+{
+ char name[PPD_MAX_NAME]; /* Name of attribute (cupsXYZ) */
+ char spec[PPD_MAX_NAME]; /* Specifier string, if any */
+ char text[PPD_MAX_TEXT]; /* Human-readable text, if any */
+ char *value; /* Value string */
+} ppd_attr_t;
+
+typedef struct ppd_option_s ppd_option_t;
+ /**** Options ****/
+
+typedef struct ppd_choice_s /**** Option choices ****/
+{
+ char marked; /* 0 if not selected, 1 otherwise */
+ char choice[PPD_MAX_NAME]; /* Computer-readable option name */
+ char text[PPD_MAX_TEXT]; /* Human-readable option name */
+ char *code; /* Code to send for this option */
+ ppd_option_t *option; /* Pointer to parent option structure */
+} ppd_choice_t;
+
+struct ppd_option_s /**** Options ****/
+{
+ char conflicted; /* 0 if no conflicts exist, 1 otherwise */
+ char keyword[PPD_MAX_NAME]; /* Option keyword name ("PageSize", etc.) */
+ char defchoice[PPD_MAX_NAME];/* Default option choice */
+ char text[PPD_MAX_TEXT]; /* Human-readable text */
+ ppd_ui_t ui; /* Type of UI option */
+ ppd_section_t section; /* Section for command */
+ float order; /* Order number */
+ int num_choices; /* Number of option choices */
+ ppd_choice_t *choices; /* Option choices */
+};
+
+typedef struct ppd_group_s /**** Groups ****/
+{
+ /**** Group text strings are limited to 39 chars + nul in order to
+ **** preserve binary compatibility and allow applications to get
+ **** the group's keyword name.
+ ****/
+ char text[PPD_MAX_TEXT - PPD_MAX_NAME];
+ /* Human-readable group name */
+ char name[PPD_MAX_NAME]; /* Group name @since CUPS 1.1.18/OS X 10.3@ */
+ int num_options; /* Number of options */
+ ppd_option_t *options; /* Options */
+ int num_subgroups; /* Number of sub-groups */
+ struct ppd_group_s *subgroups; /* Sub-groups (max depth = 1) */
+} ppd_group_t;
+
+typedef struct ppd_const_s /**** Constraints ****/
+{
+ char option1[PPD_MAX_NAME]; /* First keyword */
+ char choice1[PPD_MAX_NAME]; /* First option/choice (blank for all) */
+ char option2[PPD_MAX_NAME]; /* Second keyword */
+ char choice2[PPD_MAX_NAME]; /* Second option/choice (blank for all) */
+} ppd_const_t;
+
+typedef struct ppd_size_s /**** Page Sizes ****/
+{
+ int marked; /* Page size selected? */
+ char name[PPD_MAX_NAME]; /* Media size option */
+ float width; /* Width of media in points */
+ float length; /* Length of media in points */
+ float left; /* Left printable margin in points */
+ float bottom; /* Bottom printable margin in points */
+ float right; /* Right printable margin in points */
+ float top; /* Top printable margin in points */
+} ppd_size_t;
+
+typedef struct ppd_emul_s /**** Emulators ****/
+{
+ char name[PPD_MAX_NAME]; /* Emulator name */
+ char *start; /* Code to switch to this emulation */
+ char *stop; /* Code to stop this emulation */
+} ppd_emul_t;
+
+typedef struct ppd_profile_s /**** sRGB Color Profiles ****/
+{
+ char resolution[PPD_MAX_NAME];
+ /* Resolution or "-" */
+ char media_type[PPD_MAX_NAME];
+ /* Media type or "-" */
+ float density; /* Ink density to use */
+ float gamma; /* Gamma correction to use */
+ float matrix[3][3]; /* Transform matrix */
+} ppd_profile_t;
+
+/**** New in CUPS 1.2/OS X 10.5 ****/
+typedef enum ppd_cptype_e /**** Custom Parameter Type @since CUPS 1.2/OS X 10.5@ ****/
+{
+ PPD_CUSTOM_CURVE, /* Curve value for f(x) = x^value */
+ PPD_CUSTOM_INT, /* Integer number value */
+ PPD_CUSTOM_INVCURVE, /* Curve value for f(x) = x^(1/value) */
+ PPD_CUSTOM_PASSCODE, /* String of (hidden) numbers */
+ PPD_CUSTOM_PASSWORD, /* String of (hidden) characters */
+ PPD_CUSTOM_POINTS, /* Measurement value in points */
+ PPD_CUSTOM_REAL, /* Real number value */
+ PPD_CUSTOM_STRING /* String of characters */
+} ppd_cptype_t;
+
+typedef union ppd_cplimit_u /**** Custom Parameter Limit @since CUPS 1.2/OS X 10.5@ ****/
+{
+ float custom_curve; /* Gamma value */
+ int custom_int; /* Integer value */
+ float custom_invcurve; /* Gamma value */
+ int custom_passcode; /* Passcode length */
+ int custom_password; /* Password length */
+ float custom_points; /* Measurement value */
+ float custom_real; /* Real value */
+ int custom_string; /* String length */
+} ppd_cplimit_t;
+
+typedef union ppd_cpvalue_u /**** Custom Parameter Value @since CUPS 1.2/OS X 10.5@ ****/
+{
+ float custom_curve; /* Gamma value */
+ int custom_int; /* Integer value */
+ float custom_invcurve; /* Gamma value */
+ char *custom_passcode; /* Passcode value */
+ char *custom_password; /* Password value */
+ float custom_points; /* Measurement value */
+ float custom_real; /* Real value */
+ char *custom_string; /* String value */
+} ppd_cpvalue_t;
+
+typedef struct ppd_cparam_s /**** Custom Parameter @since CUPS 1.2/OS X 10.5@ ****/
+{
+ char name[PPD_MAX_NAME]; /* Parameter name */
+ char text[PPD_MAX_TEXT]; /* Human-readable text */
+ int order; /* Order (0 to N) */
+ ppd_cptype_t type; /* Parameter type */
+ ppd_cplimit_t minimum, /* Minimum value */
+ maximum; /* Maximum value */
+ ppd_cpvalue_t current; /* Current value */
+} ppd_cparam_t;
+
+typedef struct ppd_coption_s /**** Custom Option @since CUPS 1.2/OS X 10.5@ ****/
+{
+ char keyword[PPD_MAX_NAME]; /* Name of option that is being extended... */
+ ppd_option_t *option; /* Option that is being extended... */
+ int marked; /* Extended option is marked */
+ cups_array_t *params; /* Parameters */
+} ppd_coption_t;
+
+typedef struct _ppd_cache_s _ppd_cache_t;
+ /**** PPD cache and mapping data @since CUPS 1.5/OS X 10.7@ @private@ ****/
+
+typedef struct ppd_file_s /**** PPD File ****/
+{
+ int language_level; /* Language level of device */
+ int color_device; /* 1 = color device, 0 = grayscale */
+ int variable_sizes; /* 1 = supports variable sizes, 0 = doesn't */
+ int accurate_screens; /* 1 = supports accurate screens, 0 = not */
+ int contone_only; /* 1 = continuous tone only, 0 = not */
+ int landscape; /* -90 or 90 */
+ int model_number; /* Device-specific model number */
+ int manual_copies; /* 1 = Copies done manually, 0 = hardware */
+ int throughput; /* Pages per minute */
+ ppd_cs_t colorspace; /* Default colorspace */
+ char *patches; /* Patch commands to be sent to printer */
+ int num_emulations; /* Number of emulations supported */
+ ppd_emul_t *emulations; /* Emulations and the code to invoke them */
+ char *jcl_begin; /* Start JCL commands */
+ char *jcl_ps; /* Enter PostScript interpreter */
+ char *jcl_end; /* End JCL commands */
+ char *lang_encoding; /* Language encoding */
+ char *lang_version; /* Language version (English, Spanish, etc.) */
+ char *modelname; /* Model name (general) */
+ char *ttrasterizer; /* Truetype rasterizer */
+ char *manufacturer; /* Manufacturer name */
+ char *product; /* Product name (from PS RIP/interpreter) */
+ char *nickname; /* Nickname (specific) */
+ char *shortnickname; /* Short version of nickname */
+ int num_groups; /* Number of UI groups */
+ ppd_group_t *groups; /* UI groups */
+ int num_sizes; /* Number of page sizes */
+ ppd_size_t *sizes; /* Page sizes */
+ float custom_min[2]; /* Minimum variable page size */
+ float custom_max[2]; /* Maximum variable page size */
+ float custom_margins[4]; /* Margins around page */
+ int num_consts; /* Number of UI/Non-UI constraints */
+ ppd_const_t *consts; /* UI/Non-UI constraints */
+ int num_fonts; /* Number of pre-loaded fonts */
+ char **fonts; /* Pre-loaded fonts */
+ int num_profiles; /* Number of sRGB color profiles @deprecated@ */
+ ppd_profile_t *profiles; /* sRGB color profiles @deprecated@ */
+ int num_filters; /* Number of filters */
+ char **filters; /* Filter strings... */
+
+ /**** New in CUPS 1.1 ****/
+ int flip_duplex; /* 1 = Flip page for back sides @deprecated@ */
+
+ /**** New in CUPS 1.1.19 ****/
+ char *protocols; /* Protocols (BCP, TBCP) string @since CUPS 1.1.19/OS X 10.3@ */
+ char *pcfilename; /* PCFileName string @since CUPS 1.1.19/OS X 10.3@ */
+ int num_attrs; /* Number of attributes @since CUPS 1.1.19/OS X 10.3@ @private@ */
+ int cur_attr; /* Current attribute @since CUPS 1.1.19/OS X 10.3@ @private@ */
+ ppd_attr_t **attrs; /* Attributes @since CUPS 1.1.19/OS X 10.3@ @private@ */
+
+ /**** New in CUPS 1.2/OS X 10.5 ****/
+ cups_array_t *sorted_attrs; /* Attribute lookup array @since CUPS 1.2/OS X 10.5@ @private@ */
+ cups_array_t *options; /* Option lookup array @since CUPS 1.2/OS X 10.5@ @private@ */
+ cups_array_t *coptions; /* Custom options array @since CUPS 1.2/OS X 10.5@ @private@ */
+
+ /**** New in CUPS 1.3/OS X 10.5 ****/
+ cups_array_t *marked; /* Marked choices @since CUPS 1.3/OS X 10.5@ @private@ */
+
+ /**** New in CUPS 1.4/OS X 10.6 ****/
+ cups_array_t *cups_uiconstraints; /* cupsUIConstraints @since CUPS 1.4/OS X 10.6@ @private@ */
+
+ /**** New in CUPS 1.5 ****/
+ _ppd_cache_t *cache; /* PPD cache and mapping data @since CUPS 1.5/OS X 10.7@ @private@ */
+} ppd_file_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern int cupsMarkOptions(ppd_file_t *ppd, int num_options,
+ cups_option_t *options) _PPD_DEPRECATED;
+extern void ppdClose(ppd_file_t *ppd) _PPD_DEPRECATED;
+extern int ppdCollect(ppd_file_t *ppd, ppd_section_t section,
+ ppd_choice_t ***choices) _PPD_DEPRECATED;
+extern int ppdConflicts(ppd_file_t *ppd) _PPD_DEPRECATED;
+extern int ppdEmit(ppd_file_t *ppd, FILE *fp,
+ ppd_section_t section) _PPD_DEPRECATED;
+extern int ppdEmitFd(ppd_file_t *ppd, int fd,
+ ppd_section_t section) _PPD_DEPRECATED;
+extern int ppdEmitJCL(ppd_file_t *ppd, FILE *fp, int job_id,
+ const char *user, const char *title)
+ _PPD_DEPRECATED;
+extern ppd_choice_t *ppdFindChoice(ppd_option_t *o, const char *option)
+ _PPD_DEPRECATED;
+extern ppd_choice_t *ppdFindMarkedChoice(ppd_file_t *ppd,
+ const char *keyword)
+ _PPD_DEPRECATED;
+extern ppd_option_t *ppdFindOption(ppd_file_t *ppd, const char *keyword)
+ _PPD_DEPRECATED;
+extern int ppdIsMarked(ppd_file_t *ppd, const char *keyword,
+ const char *option) _PPD_DEPRECATED;
+extern void ppdMarkDefaults(ppd_file_t *ppd) _PPD_DEPRECATED;
+extern int ppdMarkOption(ppd_file_t *ppd, const char *keyword,
+ const char *option) _PPD_DEPRECATED;
+extern ppd_file_t *ppdOpen(FILE *fp) _PPD_DEPRECATED;
+extern ppd_file_t *ppdOpenFd(int fd) _PPD_DEPRECATED;
+extern ppd_file_t *ppdOpenFile(const char *filename) _PPD_DEPRECATED;
+extern float ppdPageLength(ppd_file_t *ppd, const char *name)
+ _PPD_DEPRECATED;
+extern ppd_size_t *ppdPageSize(ppd_file_t *ppd, const char *name)
+ _PPD_DEPRECATED;
+extern float ppdPageWidth(ppd_file_t *ppd, const char *name)
+ _PPD_DEPRECATED;
+
+/**** New in CUPS 1.1.19 ****/
+extern const char *ppdErrorString(ppd_status_t status) _PPD_DEPRECATED;
+extern ppd_attr_t *ppdFindAttr(ppd_file_t *ppd, const char *name,
+ const char *spec) _PPD_DEPRECATED;
+extern ppd_attr_t *ppdFindNextAttr(ppd_file_t *ppd, const char *name,
+ const char *spec) _PPD_DEPRECATED;
+extern ppd_status_t ppdLastError(int *line) _PPD_DEPRECATED;
+
+/**** New in CUPS 1.1.20 ****/
+extern void ppdSetConformance(ppd_conform_t c) _PPD_DEPRECATED;
+
+/**** New in CUPS 1.2 ****/
+extern int ppdCollect2(ppd_file_t *ppd, ppd_section_t section,
+ float min_order, ppd_choice_t ***choices)
+ _PPD_DEPRECATED;
+extern int ppdEmitAfterOrder(ppd_file_t *ppd, FILE *fp,
+ ppd_section_t section, int limit,
+ float min_order) _PPD_DEPRECATED;
+extern int ppdEmitJCLEnd(ppd_file_t *ppd, FILE *fp)
+ _PPD_DEPRECATED;
+extern char *ppdEmitString(ppd_file_t *ppd, ppd_section_t section,
+ float min_order) _PPD_DEPRECATED;
+extern ppd_coption_t *ppdFindCustomOption(ppd_file_t *ppd,
+ const char *keyword)
+ _PPD_DEPRECATED;
+extern ppd_cparam_t *ppdFindCustomParam(ppd_coption_t *opt,
+ const char *name) _PPD_DEPRECATED;
+extern ppd_cparam_t *ppdFirstCustomParam(ppd_coption_t *opt)
+ _PPD_DEPRECATED;
+extern ppd_option_t *ppdFirstOption(ppd_file_t *ppd) _PPD_DEPRECATED;
+extern ppd_cparam_t *ppdNextCustomParam(ppd_coption_t *opt) _PPD_DEPRECATED;
+extern ppd_option_t *ppdNextOption(ppd_file_t *ppd) _PPD_DEPRECATED;
+extern int ppdLocalize(ppd_file_t *ppd) _PPD_DEPRECATED;
+extern ppd_file_t *ppdOpen2(cups_file_t *fp) _PPD_DEPRECATED;
+
+/**** New in CUPS 1.3/OS X 10.5 ****/
+extern const char *ppdLocalizeIPPReason(ppd_file_t *ppd,
+ const char *reason,
+ const char *scheme,
+ char *buffer,
+ size_t bufsize) _PPD_DEPRECATED;
+
+/**** New in CUPS 1.4/OS X 10.6 ****/
+extern int cupsGetConflicts(ppd_file_t *ppd, const char *option,
+ const char *choice,
+ cups_option_t **options)
+ _PPD_DEPRECATED;
+extern int cupsResolveConflicts(ppd_file_t *ppd,
+ const char *option,
+ const char *choice,
+ int *num_options,
+ cups_option_t **options)
+ _PPD_DEPRECATED;
+extern int ppdInstallableConflict(ppd_file_t *ppd,
+ const char *option,
+ const char *choice)
+ _PPD_DEPRECATED;
+extern ppd_attr_t *ppdLocalizeAttr(ppd_file_t *ppd, const char *keyword,
+ const char *spec) _PPD_DEPRECATED;
+extern const char *ppdLocalizeMarkerName(ppd_file_t *ppd,
+ const char *name)
+ _PPD_DEPRECATED;
+extern int ppdPageSizeLimits(ppd_file_t *ppd,
+ ppd_size_t *minimum,
+ ppd_size_t *maximum) _PPD_DEPRECATED;
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_PPD_H_ */
+
+/*
+ * End of "$Id: ppd.h 11056 2013-06-25 14:27:30Z msweet $".
+ */
diff --git a/cups/libs/cups/pwg-media.c b/cups/libs/cups/pwg-media.c
new file mode 100644
index 000000000..657e1fef7
--- /dev/null
+++ b/cups/libs/cups/pwg-media.c
@@ -0,0 +1,1189 @@
+/*
+ * "$Id: pwg-media.c 11240 2013-08-14 20:33:55Z msweet $"
+ *
+ * PWG media name API implementation for CUPS.
+ *
+ * Copyright 2009-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * pwgFormatSizeName() - Generate a PWG self-describing media size name.
+ * pwgInitSize() - Initialize a pwg_size_t structure using IPP Job
+ * Template attributes.
+ * pwgMediaForLegacy() - Find a PWG media size by ISO/IPP legacy name.
+ * pwgMediaForPPD() - Find a PWG media size by Adobe PPD name.
+ * pwgMediaForPWG() - Find a PWG media size by 5101.1 self-describing
+ * name.
+ * pwgMediaForSize() - Get the PWG media size for the given
+ * dimensions.
+ * _pwgMediaTable() - Return the internal media size table.
+ * pwg_compare_legacy() - Compare two sizes using the legacy names.
+ * pwg_compare_ppd() - Compare two sizes using the PPD names.
+ * pwg_compare_pwg() - Compare two sizes using the PWG names.
+ * pwg_format_inches() - Convert and format PWG units as inches.
+ * pwg_format_millimeters() - Convert and format PWG units as millimeters.
+ * pwg_scan_measurement() - Scan a measurement in inches or millimeters.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <math.h>
+
+
+/*
+ * Local macros...
+ */
+
+#define _PWG_MEDIA_IN(p,l,a,x,y) {p, l, a, (int)(x * 2540), (int)(y * 2540)}
+#define _PWG_MEDIA_MM(p,l,a,x,y) {p, l, a, (int)(x * 100), (int)(y * 100)}
+
+
+/*
+ * Local functions...
+ */
+
+static int pwg_compare_legacy(pwg_media_t *a, pwg_media_t *b);
+static int pwg_compare_pwg(pwg_media_t *a, pwg_media_t *b);
+static int pwg_compare_ppd(pwg_media_t *a, pwg_media_t *b);
+static char *pwg_format_inches(char *buf, size_t bufsize, int val);
+static char *pwg_format_millimeters(char *buf, size_t bufsize, int val);
+static int pwg_scan_measurement(const char *buf, char **bufptr, int numer,
+ int denom);
+
+
+/*
+ * Local globals...
+ */
+
+static pwg_media_t const cups_pwg_media[] =
+{ /* Media size lookup table */
+ /* North American Standard Sheet Media Sizes */
+ _PWG_MEDIA_IN("na_index-3x5_3x5in", NULL, "3x5", 3, 5),
+ _PWG_MEDIA_IN("na_personal_3.625x6.5in", NULL, "EnvPersonal", 3.625, 6.5),
+ _PWG_MEDIA_IN("na_monarch_3.875x7.5in", "monarch-envelope", "EnvMonarch", 3.875, 7.5),
+ _PWG_MEDIA_IN("na_number-9_3.875x8.875in", "na-number-9-envelope", "Env9", 3.875, 8.875),
+ _PWG_MEDIA_IN("na_index-4x6_4x6in", NULL, "4x6", 4, 6),
+ _PWG_MEDIA_IN("na_number-10_4.125x9.5in", "na-number-10-envelope", "Env10", 4.125, 9.5),
+ _PWG_MEDIA_IN("na_a2_4.375x5.75in", NULL, "EnvA2", 4.375, 5.75),
+ _PWG_MEDIA_IN("na_number-11_4.5x10.375in", NULL, "Env11", 4.5, 10.375),
+ _PWG_MEDIA_IN("na_number-12_4.75x11in", NULL, "Env12", 4.75, 11),
+ _PWG_MEDIA_IN("na_5x7_5x7in", NULL, "5x7", 5, 7),
+ _PWG_MEDIA_IN("na_index-5x8_5x8in", NULL, "5x8", 5, 8),
+ _PWG_MEDIA_IN("na_number-14_5x11.5in", NULL, "Env14", 5, 11.5),
+ _PWG_MEDIA_IN("na_invoice_5.5x8.5in", "invoice", "Statement", 5.5, 8.5),
+ _PWG_MEDIA_IN("na_index-4x6-ext_6x8in", NULL, NULL, 6, 8),
+ _PWG_MEDIA_IN("na_6x9_6x9in", "na-6x9-envelope", "6x9", 6, 9),
+ _PWG_MEDIA_IN("na_c5_6.5x9.5in", NULL, "6.5x9.5", 6.5, 9.5),
+ _PWG_MEDIA_IN("na_7x9_7x9in", "na-7x9-envelope", "7x9", 7, 9),
+ _PWG_MEDIA_IN("na_executive_7.25x10.5in", "executive", "Executive", 7.25, 10.5),
+ _PWG_MEDIA_IN("na_govt-letter_8x10in", "na-8x10", "8x10", 8, 10),
+ _PWG_MEDIA_IN("na_govt-legal_8x13in", NULL, "8x13", 8, 13),
+ _PWG_MEDIA_IN("na_quarto_8.5x10.83in", "quarto", "Quarto", 8.5, 10.83),
+ _PWG_MEDIA_IN("na_letter_8.5x11in", "na-letter", "Letter", 8.5, 11),
+ _PWG_MEDIA_IN("na_fanfold-eur_8.5x12in", NULL, "FanFoldGerman", 8.5, 12),
+ _PWG_MEDIA_IN("na_letter-plus_8.5x12.69in", NULL, "LetterPlus", 8.5, 12.69),
+ _PWG_MEDIA_IN("na_foolscap_8.5x13in", NULL, "FanFoldGermanLegal", 8.5, 13),
+ _PWG_MEDIA_IN("na_oficio_8.5x13.4in", NULL, "Oficio", 8.5, 13.4),
+ _PWG_MEDIA_IN("na_legal_8.5x14in", "na-legal", "Legal", 8.5, 14),
+ _PWG_MEDIA_IN("na_super-a_8.94x14in", NULL, "SuperA", 8.94, 14),
+ _PWG_MEDIA_IN("na_9x11_9x11in", "na-9x11-envelope", "9x11", 9, 11),
+ _PWG_MEDIA_IN("na_arch-a_9x12in", "arch-a", "ARCHA", 9, 12),
+ _PWG_MEDIA_IN("na_letter-extra_9.5x12in", NULL, "LetterExtra", 9.5, 12),
+ _PWG_MEDIA_IN("na_legal-extra_9.5x15in", NULL, "LegalExtra", 9.5, 15),
+ _PWG_MEDIA_IN("na_10x11_10x11in", NULL, "10x11", 10, 11),
+ _PWG_MEDIA_IN("na_10x13_10x13in", "na-10x13-envelope", "10x13", 10, 13),
+ _PWG_MEDIA_IN("na_10x14_10x14in", "na-10x14-envelope", "10x14", 10, 14),
+ _PWG_MEDIA_IN("na_10x15_10x15in", "na-10x15-envelope", "10x15", 10, 15),
+ _PWG_MEDIA_IN("na_11x12_11x12in", NULL, "11x12", 11, 12),
+ _PWG_MEDIA_IN("na_edp_11x14in", NULL, "11x14", 11, 14),
+ _PWG_MEDIA_IN("na_fanfold-us_11x14.875in", NULL, NULL, 11, 14.875),
+ _PWG_MEDIA_IN("na_11x15_11x15in", NULL, "11x15", 11, 15),
+ _PWG_MEDIA_IN("na_ledger_11x17in", "tabloid", "Tabloid", 11, 17),
+ _PWG_MEDIA_IN("na_eur-edp_12x14in", NULL, NULL, 12, 14),
+ _PWG_MEDIA_IN("na_arch-b_12x18in", "arch-b", "ARCHB", 12, 18),
+ _PWG_MEDIA_IN("na_12x19_12x19in", NULL, "12x19", 12, 19),
+ _PWG_MEDIA_IN("na_b-plus_12x19.17in", NULL, "SuperB", 12, 19.17),
+ _PWG_MEDIA_IN("na_super-b_13x19in", "super-b", "13x19", 13, 19),
+ _PWG_MEDIA_IN("na_c_17x22in", "c", "AnsiC", 17, 22),
+ _PWG_MEDIA_IN("na_arch-c_18x24in", "arch-c", "ARCHC", 18, 24),
+ _PWG_MEDIA_IN("na_d_22x34in", "d", "AnsiD", 22, 34),
+ _PWG_MEDIA_IN("na_arch-d_24x36in", "arch-d", "ARCHD", 24, 36),
+ _PWG_MEDIA_IN("asme_f_28x40in", "f", NULL, 28, 40),
+ _PWG_MEDIA_IN("na_wide-format_30x42in", NULL, NULL, 30, 42),
+ _PWG_MEDIA_IN("na_e_34x44in", "e", "AnsiE", 34, 44),
+ _PWG_MEDIA_IN("na_arch-e_36x48in", "arch-e", "ARCHE", 36, 48),
+ _PWG_MEDIA_IN("na_f_44x68in", NULL, "AnsiF", 44, 68),
+
+ /* ISO Standard Sheet Media Sizes */
+ _PWG_MEDIA_MM("iso_a10_26x37mm", "iso-a10", "A10", 26, 37),
+ _PWG_MEDIA_MM("iso_a9_37x52mm", "iso-a9", "A9", 37, 52),
+ _PWG_MEDIA_MM("iso_a8_52x74mm", "iso-a8", "A8", 52, 74),
+ _PWG_MEDIA_MM("iso_a7_74x105mm", "iso-a7", "A7", 74, 105),
+ _PWG_MEDIA_MM("iso_a6_105x148mm", "iso-a6", "A6", 105, 148),
+ _PWG_MEDIA_MM("iso_a5_148x210mm", "iso-a5", "A5", 148, 210),
+ _PWG_MEDIA_MM("iso_a5-extra_174x235mm", NULL, "A5Extra", 174, 235),
+ _PWG_MEDIA_MM("iso_a4_210x297mm", "iso-a4", "A4", 210, 297),
+ _PWG_MEDIA_MM("iso_a4-tab_225x297mm", NULL, "A4Tab", 225, 297),
+ _PWG_MEDIA_MM("iso_a4-extra_235.5x322.3mm", NULL, "A4Extra", 235.5, 322.3),
+ _PWG_MEDIA_MM("iso_a3_297x420mm", "iso-a3", "A3", 297, 420),
+ _PWG_MEDIA_MM("iso_a4x3_297x630mm", "iso-a4x3", NULL, 297, 630),
+ _PWG_MEDIA_MM("iso_a4x4_297x841mm", "iso-a4x4", NULL, 297, 841),
+ _PWG_MEDIA_MM("iso_a4x5_297x1051mm", "iso-a4x5", NULL, 297, 1051),
+ _PWG_MEDIA_MM("iso_a4x6_297x1261mm", "iso-a4x6", NULL, 297, 1261),
+ _PWG_MEDIA_MM("iso_a4x7_297x1471mm", "iso-a4x7", NULL, 297, 1471),
+ _PWG_MEDIA_MM("iso_a4x8_297x1682mm", "iso-a4x8", NULL, 297, 1682),
+ _PWG_MEDIA_MM("iso_a4x9_297x1892mm", "iso-a4x9", NULL, 297, 1892),
+ _PWG_MEDIA_MM("iso_a3-extra_322x445mm", "iso-a3-extra", "A3Extra", 322, 445),
+ _PWG_MEDIA_MM("iso_a2_420x594mm", "iso-a2", "A2", 420, 594),
+ _PWG_MEDIA_MM("iso_a3x3_420x891mm", "iso-a3x3", NULL, 420, 891),
+ _PWG_MEDIA_MM("iso_a3x4_420x1189mm", "iso-a3x4", NULL, 420, 1189),
+ _PWG_MEDIA_MM("iso_a3x5_420x1486mm", "iso-a3x5", NULL, 420, 1486),
+ _PWG_MEDIA_MM("iso_a3x6_420x1783mm", "iso-a3x6", NULL, 420, 1783),
+ _PWG_MEDIA_MM("iso_a3x7_420x2080mm", "iso-a3x7", NULL, 420, 2080),
+ _PWG_MEDIA_MM("iso_a1_594x841mm", "iso-a1", "A1", 594, 841),
+ _PWG_MEDIA_MM("iso_a2x3_594x1261mm", "iso-a2x3", NULL, 594, 1261),
+ _PWG_MEDIA_MM("iso_a2x4_594x1682mm", "iso-a2x4", NULL, 594, 1682),
+ _PWG_MEDIA_MM("iso_a2x5_594x2102mm", "iso-a2x5", NULL, 594, 2102),
+ _PWG_MEDIA_MM("iso_a0_841x1189mm", "iso-a0", "A0", 841, 1189),
+ _PWG_MEDIA_MM("iso_a1x3_841x1783mm", "iso-a1x3", NULL, 841, 1783),
+ _PWG_MEDIA_MM("iso_a1x4_841x2378mm", "iso-a1x4", NULL, 841, 2378),
+ _PWG_MEDIA_MM("iso_2a0_1189x1682mm", NULL, NULL, 1189, 1682),
+ _PWG_MEDIA_MM("iso_a0x3_1189x2523mm", NULL, NULL, 1189, 2523),
+ _PWG_MEDIA_MM("iso_b10_31x44mm", "iso-b10", "ISOB10", 31, 44),
+ _PWG_MEDIA_MM("iso_b9_44x62mm", "iso-b9", "ISOB9", 44, 62),
+ _PWG_MEDIA_MM("iso_b8_62x88mm", "iso-b8", "ISOB8", 62, 88),
+ _PWG_MEDIA_MM("iso_b7_88x125mm", "iso-b7", "ISOB7", 88, 125),
+ _PWG_MEDIA_MM("iso_b6_125x176mm", "iso-b6", "ISOB6", 125, 176),
+ _PWG_MEDIA_MM("iso_b6c4_125x324mm", NULL, NULL, 125, 324),
+ _PWG_MEDIA_MM("iso_b5_176x250mm", "iso-b5", "ISOB5", 176, 250),
+ _PWG_MEDIA_MM("iso_b5-extra_201x276mm", NULL, "ISOB5Extra", 201, 276),
+ _PWG_MEDIA_MM("iso_b4_250x353mm", "iso-b4", "ISOB4", 250, 353),
+ _PWG_MEDIA_MM("iso_b3_353x500mm", "iso-b3", "ISOB3", 353, 500),
+ _PWG_MEDIA_MM("iso_b2_500x707mm", "iso-b2", "ISOB2", 500, 707),
+ _PWG_MEDIA_MM("iso_b1_707x1000mm", "iso-b1", "ISOB1", 707, 1000),
+ _PWG_MEDIA_MM("iso_b0_1000x1414mm", "iso-b0", "ISOB0", 1000, 1414),
+ _PWG_MEDIA_MM("iso_c10_28x40mm", "iso-c10", NULL, 28, 40),
+ _PWG_MEDIA_MM("iso_c9_40x57mm", "iso-c9", NULL, 40, 57),
+ _PWG_MEDIA_MM("iso_c8_57x81mm", "iso-c8", NULL, 57, 81),
+ _PWG_MEDIA_MM("iso_c7_81x114mm", "iso-c7", "EnvC7", 81, 114),
+ _PWG_MEDIA_MM("iso_c7c6_81x162mm", NULL, NULL, 81, 162),
+ _PWG_MEDIA_MM("iso_c6_114x162mm", "iso-c6", "EnvC6", 114, 162),
+ _PWG_MEDIA_MM("iso_c6c5_114x229mm", NULL, "EnvC65", 114, 229),
+ _PWG_MEDIA_MM("iso_c5_162x229mm", "iso-c5", "EnvC5", 162, 229),
+ _PWG_MEDIA_MM("iso_c4_229x324mm", "iso-c4", "EnvC4", 229, 324),
+ _PWG_MEDIA_MM("iso_c3_324x458mm", "iso-c3", "EnvC3", 324, 458),
+ _PWG_MEDIA_MM("iso_c2_458x648mm", "iso-c2", "EnvC2", 458, 648),
+ _PWG_MEDIA_MM("iso_c1_648x917mm", "iso-c1", "EnvC1", 648, 917),
+ _PWG_MEDIA_MM("iso_c0_917x1297mm", "iso-c0", "EnvC0", 917, 1297),
+ _PWG_MEDIA_MM("iso_dl_110x220mm", "iso-designated", "EnvDL", 110, 220),
+ _PWG_MEDIA_MM("iso_ra4_215x305mm", "iso-ra4", NULL, 215, 305),
+ _PWG_MEDIA_MM("iso_sra4_225x320mm", "iso-sra4", NULL, 225, 320),
+ _PWG_MEDIA_MM("iso_ra3_305x430mm", "iso-ra3", NULL, 305, 430),
+ _PWG_MEDIA_MM("iso_sra3_320x450mm", "iso-sra3", NULL, 320, 450),
+ _PWG_MEDIA_MM("iso_ra2_430x610mm", "iso-ra2", NULL, 430, 610),
+ _PWG_MEDIA_MM("iso_sra2_450x640mm", "iso-sra2", NULL, 450, 640),
+ _PWG_MEDIA_MM("iso_ra1_610x860mm", "iso-ra1", NULL, 610, 860),
+ _PWG_MEDIA_MM("iso_sra1_640x900mm", "iso-sra1", NULL, 640, 900),
+ _PWG_MEDIA_MM("iso_ra0_860x1220mm", "iso-ra0", NULL, 860, 1220),
+ _PWG_MEDIA_MM("iso_sra0_900x1280mm", "iso-sra0", NULL, 900, 1280),
+
+ /* Japanese Standard Sheet Media Sizes */
+ _PWG_MEDIA_MM("jis_b10_32x45mm", "jis-b10", "B10", 32, 45),
+ _PWG_MEDIA_MM("jis_b9_45x64mm", "jis-b9", "B9", 45, 64),
+ _PWG_MEDIA_MM("jis_b8_64x91mm", "jis-b8", "B8", 64, 91),
+ _PWG_MEDIA_MM("jis_b7_91x128mm", "jis-b7", "B7", 91, 128),
+ _PWG_MEDIA_MM("jis_b6_128x182mm", "jis-b6", "B6", 128, 182),
+ _PWG_MEDIA_MM("jis_b5_182x257mm", "jis-b5", "B5", 182, 257),
+ _PWG_MEDIA_MM("jis_b4_257x364mm", "jis-b4", "B4", 257, 364),
+ _PWG_MEDIA_MM("jis_b3_364x515mm", "jis-b3", "B3", 364, 515),
+ _PWG_MEDIA_MM("jis_b2_515x728mm", "jis-b2", "B2", 515, 728),
+ _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, NULL, 216, 330),
+ _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),
+ _PWG_MEDIA_MM("jpn_kaku5_190x240mm", NULL, "EnvKaku5", 190, 240),
+ _PWG_MEDIA_MM("jpn_kaku7_142x205mm", NULL, "EnvKaku7", 142, 205),
+ _PWG_MEDIA_MM("jpn_kaku8_119x197mm", NULL, "EnvKaku8", 119, 197),
+ _PWG_MEDIA_MM("jpn_chou4_90x205mm", NULL, "EnvChou4", 90, 205),
+ _PWG_MEDIA_MM("jpn_hagaki_100x148mm", NULL, "Postcard", 100, 148),
+ _PWG_MEDIA_MM("jpn_you4_105x235mm", NULL, "EnvYou4", 105, 235),
+ _PWG_MEDIA_MM("jpn_you6_98x190mm", NULL, "EnvYou6", 98, 190),
+ _PWG_MEDIA_MM("jpn_chou2_111.1x146mm", NULL, NULL, 111.1, 146),
+ _PWG_MEDIA_MM("jpn_chou3_120x235mm", NULL, "EnvChou3", 120, 235),
+ _PWG_MEDIA_MM("jpn_chou40_90x225mm", NULL, "EnvChou40", 90, 225),
+ _PWG_MEDIA_MM("jpn_oufuku_148x200mm", NULL, "DoublePostcardRotated", 148, 200),
+ _PWG_MEDIA_MM("jpn_kahu_240x322.1mm", NULL, NULL, 240, 322.1),
+
+ /* Chinese Standard Sheet Media Sizes */
+ _PWG_MEDIA_MM("prc_32k_97x151mm", NULL, "PRC32K", 97, 151),
+ _PWG_MEDIA_MM("prc_1_102x165mm", NULL, "EnvPRC1", 102, 165),
+ _PWG_MEDIA_MM("prc_2_102x176mm", NULL, "EnvPRC2", 102, 176),
+ _PWG_MEDIA_MM("prc_4_110x208mm", NULL, "EnvPRC4", 110, 208),
+ _PWG_MEDIA_MM("prc_8_120x309mm", NULL, "EnvPRC8", 120, 309),
+ _PWG_MEDIA_MM("prc_6_120x320mm", NULL, NULL, 120, 320),
+ _PWG_MEDIA_MM("prc_16k_146x215mm", NULL, "PRC16K", 146, 215),
+ _PWG_MEDIA_MM("prc_7_160x230mm", NULL, "EnvPRC7", 160, 230),
+ _PWG_MEDIA_MM("om_juuro-ku-kai_198x275mm", NULL, NULL, 198, 275),
+ _PWG_MEDIA_MM("om_pa-kai_267x389mm", NULL, NULL, 267, 389),
+ _PWG_MEDIA_MM("om_dai-pa-kai_275x395mm", NULL, NULL, 275, 395),
+
+ /* Chinese Standard Sheet Media Inch Sizes */
+ _PWG_MEDIA_IN("roc_16k_7.75x10.75in", NULL, "roc16k", 7.75, 10.75),
+ _PWG_MEDIA_IN("roc_8k_10.75x15.5in", NULL, "roc8k", 10.75, 15.5),
+
+ /* Other English Standard Sheet Media Sizes */
+ _PWG_MEDIA_IN("oe_photo-l_3.5x5in", NULL, "3.5x5", 3.5, 5),
+
+ /* Other Metric Standard Sheet Media Sizes */
+ _PWG_MEDIA_MM("om_small-photo_100x150mm", NULL, "om_small-photo", 100, 150),
+ _PWG_MEDIA_MM("om_italian_110x230mm", NULL, "EnvItalian", 110, 230),
+ _PWG_MEDIA_MM("om_large-photo_200x300", NULL, "om_large-photo", 200, 300),
+ _PWG_MEDIA_MM("om_folio_210x330mm", "folio", "Folio", 210, 330),
+ _PWG_MEDIA_MM("om_folio-sp_215x315mm", NULL, "FolioSP", 215, 315),
+ _PWG_MEDIA_MM("om_invite_220x220mm", NULL, "EnvInvite", 220, 220),
+ _PWG_MEDIA_MM("om_small-photo_100x200mm", NULL, "om_wide-photo", 100, 200)
+};
+
+
+/*
+ * 'pwgFormatSizeName()' - Generate a PWG self-describing media size name.
+ *
+ * This function generates a PWG self-describing media size name of the form
+ * "prefix_name_WIDTHxLENGTHunits". The prefix is typically "custom" or "roll"
+ * for user-supplied sizes but can also be "disc", "iso", "jis", "jpn", "na",
+ * "oe", "om", "prc", or "roc". A value of @code NULL@ automatically chooses
+ * "oe" or "om" depending on the units.
+ *
+ * The size name may only contain lowercase letters, numbers, "-", and ".". If
+ * @code NULL@ is passed, the size name will contain the formatted dimensions.
+ *
+ * The width and length are specified in hundredths of millimeters, equivalent
+ * to 1/100000th of a meter or 1/2540th of an inch. The width, length, and
+ * units used for the generated size name are calculated automatically if the
+ * units string is @code NULL@, otherwise inches ("in") or millimeters ("mm")
+ * are used.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 on success, 0 on failure */
+pwgFormatSizeName(char *keyword, /* I - Keyword buffer */
+ size_t keysize, /* I - Size of keyword buffer */
+ const char *prefix, /* I - Prefix for PWG size or @code NULL@ for automatic */
+ const char *name, /* I - Size name or @code NULL@ */
+ int width, /* I - Width of page in 2540ths */
+ int length, /* I - Length of page in 2540ths */
+ const char *units) /* I - Units - "in", "mm", or @code NULL@ for automatic */
+{
+ char usize[12 + 1 + 12 + 3], /* Unit size: NNNNNNNNNNNNxNNNNNNNNNNNNuu */
+ *uptr; /* Pointer into unit size */
+ char *(*format)(char *, size_t, int);
+ /* Formatting function */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("pwgFormatSize(keyword=%p, keysize=" CUPS_LLFMT
+ ", prefix=\"%s\", name=\"%s\", width=%d, length=%d, "
+ "units=\"%s\")", keyword, CUPS_LLCAST keysize, prefix, name,
+ width, length, units));
+
+ if (keyword)
+ *keyword = '\0';
+
+ if (!keyword || keysize < 32 || width < 0 || length < 0 ||
+ (units && strcmp(units, "in") && strcmp(units, "mm")))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media name arguments."),
+ 1);
+ return (0);
+ }
+
+ if (name)
+ {
+ /*
+ * Validate name...
+ */
+
+ const char *nameptr; /* Pointer into name */
+
+ for (nameptr = name; *nameptr; nameptr ++)
+ if (!(*nameptr >= 'a' && *nameptr <= 'z') &&
+ !(*nameptr >= '0' && *nameptr <= '9') &&
+ *nameptr != '.' && *nameptr != '-')
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("Invalid media name arguments."), 1);
+ return (0);
+ }
+ }
+ else
+ name = usize;
+
+
+ if (!units)
+ {
+ if ((width % 635) == 0 && (length % 635) == 0)
+ {
+ /*
+ * Use inches since the size is a multiple of 1/4 inch.
+ */
+
+ units = "in";
+ }
+ else
+ {
+ /*
+ * Use millimeters since the size is not a multiple of 1/4 inch.
+ */
+
+ units = "mm";
+ }
+ }
+
+ if (!strcmp(units, "in"))
+ {
+ format = pwg_format_inches;
+
+ if (!prefix)
+ prefix = "oe";
+ }
+ else
+ {
+ format = pwg_format_millimeters;
+
+ if (!prefix)
+ prefix = "om";
+ }
+
+ /*
+ * Format the size string...
+ */
+
+ uptr = usize;
+ (*format)(uptr, sizeof(usize) - (uptr - usize), width);
+ uptr += strlen(uptr);
+ *uptr++ = 'x';
+ (*format)(uptr, sizeof(usize) - (uptr - usize), length);
+ uptr += strlen(uptr);
+
+ /*
+ * Safe because usize can hold up to 12 + 1 + 12 + 4 bytes.
+ */
+
+ memcpy(uptr, units, 3);
+
+ /*
+ * Format the name...
+ */
+
+ snprintf(keyword, keysize, "%s_%s_%s", prefix, name, usize);
+
+ return (1);
+}
+
+/* For OS X 10.8 and earlier... */
+void _pwgGenerateSize(char *keyword, size_t keysize, const char *prefix,
+ const char *name, int width, int length)
+{ pwgFormatSizeName(keyword, keysize, prefix, name, width, length, NULL); }
+
+
+/*
+ * 'pwgInitSize()' - Initialize a pwg_size_t structure using IPP Job Template
+ * attributes.
+ *
+ * This function initializes a pwg_size_t structure from an IPP "media" or
+ * "media-col" attribute in the specified IPP message. 0 is returned if neither
+ * attribute is found in the message or the values are not valid.
+ *
+ * The "margins_set" variable is initialized to 1 if any "media-xxx-margin"
+ * member attribute was specified in the "media-col" Job Template attribute,
+ * otherwise it is initialized to 0.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+int /* O - 1 if size was initialized, 0 otherwise */
+pwgInitSize(pwg_size_t *size, /* I - Size to initialize */
+ ipp_t *job, /* I - Job template attributes */
+ int *margins_set) /* O - 1 if margins were set, 0 otherwise */
+{
+ ipp_attribute_t *media, /* media attribute */
+ *media_bottom_margin, /* media-bottom-margin member attribute */
+ *media_col, /* media-col attribute */
+ *media_left_margin, /* media-left-margin member attribute */
+ *media_right_margin, /* media-right-margin member attribute */
+ *media_size, /* media-size member attribute */
+ *media_top_margin, /* media-top-margin member attribute */
+ *x_dimension, /* x-dimension member attribute */
+ *y_dimension; /* y-dimension member attribute */
+ pwg_media_t *pwg; /* PWG media value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!size || !job || !margins_set)
+ return (0);
+
+ /*
+ * Look for media-col and then media...
+ */
+
+ memset(size, 0, sizeof(pwg_size_t));
+ *margins_set = 0;
+
+ if ((media_col = ippFindAttribute(job, "media-col",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ /*
+ * Got media-col, look for media-size member attribute...
+ */
+
+ if ((media_size = ippFindAttribute(media_col->values[0].collection,
+ "media-size",
+ IPP_TAG_BEGIN_COLLECTION)) != NULL)
+ {
+ /*
+ * Got media-size, look for x-dimension and y-dimension member
+ * attributes...
+ */
+
+ x_dimension = ippFindAttribute(media_size->values[0].collection,
+ "x-dimension", IPP_TAG_INTEGER);
+ y_dimension = ippFindAttribute(media_size->values[0].collection,
+ "y-dimension", IPP_TAG_INTEGER);
+
+ if (x_dimension && y_dimension)
+ {
+ size->width = x_dimension->values[0].integer;
+ size->length = y_dimension->values[0].integer;
+ }
+ else if (!x_dimension)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("Missing x-dimension in media-size."), 1);
+ return (0);
+ }
+ else if (!y_dimension)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("Missing y-dimension in media-size."), 1);
+ return (0);
+ }
+ }
+ else
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Missing media-size in media-col."),
+ 1);
+ return (0);
+ }
+
+ /* media-*-margin */
+ media_bottom_margin = ippFindAttribute(media_col->values[0].collection,
+ "media-bottom-margin",
+ IPP_TAG_INTEGER);
+ media_left_margin = ippFindAttribute(media_col->values[0].collection,
+ "media-left-margin",
+ IPP_TAG_INTEGER);
+ media_right_margin = ippFindAttribute(media_col->values[0].collection,
+ "media-right-margin",
+ IPP_TAG_INTEGER);
+ media_top_margin = ippFindAttribute(media_col->values[0].collection,
+ "media-top-margin",
+ IPP_TAG_INTEGER);
+ if (media_bottom_margin && media_left_margin && media_right_margin &&
+ media_top_margin)
+ {
+ *margins_set = 1;
+ size->bottom = media_bottom_margin->values[0].integer;
+ size->left = media_left_margin->values[0].integer;
+ size->right = media_right_margin->values[0].integer;
+ size->top = media_top_margin->values[0].integer;
+ }
+ }
+ else
+ {
+ if ((media = ippFindAttribute(job, "media", IPP_TAG_NAME)) == NULL)
+ if ((media = ippFindAttribute(job, "media", IPP_TAG_KEYWORD)) == NULL)
+ if ((media = ippFindAttribute(job, "PageSize", IPP_TAG_NAME)) == NULL)
+ media = ippFindAttribute(job, "PageRegion", IPP_TAG_NAME);
+
+ if (media && media->values[0].string.text)
+ {
+ const char *name = media->values[0].string.text;
+ /* Name string */
+
+ if ((pwg = pwgMediaForPWG(name)) == NULL)
+ {
+ /*
+ * Not a PWG name, try a legacy name...
+ */
+
+ if ((pwg = pwgMediaForLegacy(name)) == NULL)
+ {
+ /*
+ * Not a legacy name, try a PPD name...
+ */
+
+ const char *suffix; /* Suffix on media string */
+
+ pwg = pwgMediaForPPD(name);
+ if (pwg &&
+ (suffix = name + strlen(name) - 10 /* .FullBleed */) > name &&
+ !_cups_strcasecmp(suffix, ".FullBleed"))
+ {
+ /*
+ * Indicate that margins are set with the default values of 0.
+ */
+
+ *margins_set = 1;
+ }
+ }
+ }
+
+ if (pwg)
+ {
+ size->width = pwg->width;
+ size->length = pwg->length;
+ }
+ else
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unsupported media value."), 1);
+ return (0);
+ }
+ }
+ else
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Missing media or media-col."), 1);
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/* For OS X 10.8 and earlier */
+int _pwgInitSize(pwg_size_t *size, ipp_t *job, int *margins_set)
+{ return (pwgInitSize(size, job, margins_set)); }
+
+
+/*
+ * 'pwgMediaForLegacy()' - Find a PWG media size by ISO/IPP legacy name.
+ *
+ * The "name" argument specifies the legacy ISO media size name, for example
+ * "iso-a4" or "na-letter".
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+pwg_media_t * /* O - Matching size or NULL */
+pwgMediaForLegacy(const char *legacy) /* I - Legacy size name */
+{
+ pwg_media_t key; /* Search key */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!legacy)
+ return (NULL);
+
+ /*
+ * Build the lookup table for PWG names as needed...
+ */
+
+ if (!cg->leg_size_lut)
+ {
+ int i; /* Looping var */
+ pwg_media_t *size; /* Current size */
+
+ cg->leg_size_lut = cupsArrayNew((cups_array_func_t)pwg_compare_legacy,
+ NULL);
+
+ for (i = (int)(sizeof(cups_pwg_media) / sizeof(cups_pwg_media[0])),
+ size = (pwg_media_t *)cups_pwg_media;
+ i > 0;
+ i --, size ++)
+ if (size->legacy)
+ cupsArrayAdd(cg->leg_size_lut, size);
+ }
+
+ /*
+ * Lookup the name...
+ */
+
+ key.legacy = legacy;
+ return ((pwg_media_t *)cupsArrayFind(cg->leg_size_lut, &key));
+}
+
+/* For OS X 10.8 and earlier */
+pwg_media_t *_pwgMediaForLegacy(const char *legacy)
+{ return (pwgMediaForLegacy(legacy)); }
+
+
+/*
+ * 'pwgMediaForPPD()' - Find a PWG media size by Adobe PPD name.
+ *
+ * The "ppd" argument specifies an Adobe page size name as defined in Table B.1
+ * of the Adobe PostScript Printer Description File Format Specification Version
+ * 4.3.
+ *
+ * If the name is non-standard, the returned PWG media size is stored in
+ * thread-local storage and is overwritten by each call to the function in the
+ * thread. Custom names can be of the form "Custom.WIDTHxLENGTH[units]" or
+ * "WIDTHxLENGTH[units]".
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+pwg_media_t * /* O - Matching size or NULL */
+pwgMediaForPPD(const char *ppd) /* I - PPD size name */
+{
+ pwg_media_t key, /* Search key */
+ *size; /* Matching size */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd)
+ return (NULL);
+
+ /*
+ * Build the lookup table for PWG names as needed...
+ */
+
+ if (!cg->ppd_size_lut)
+ {
+ int i; /* Looping var */
+
+ cg->ppd_size_lut = cupsArrayNew((cups_array_func_t)pwg_compare_ppd, NULL);
+
+ for (i = (int)(sizeof(cups_pwg_media) / sizeof(cups_pwg_media[0])),
+ size = (pwg_media_t *)cups_pwg_media;
+ i > 0;
+ i --, size ++)
+ if (size->ppd)
+ cupsArrayAdd(cg->ppd_size_lut, size);
+ }
+
+ /*
+ * Lookup the name...
+ */
+
+ key.ppd = ppd;
+ if ((size = (pwg_media_t *)cupsArrayFind(cg->ppd_size_lut, &key)) == NULL)
+ {
+ /*
+ * See if the name is of the form:
+ *
+ * [Custom.]WIDTHxLENGTH[.FullBleed] - Size in points/inches [borderless]
+ * [Custom.]WIDTHxLENGTHcm[.FullBleed] - Size in centimeters [borderless]
+ * [Custom.]WIDTHxLENGTHft[.FullBleed] - Size in feet [borderless]
+ * [Custom.]WIDTHxLENGTHin[.FullBleed] - Size in inches [borderless]
+ * [Custom.]WIDTHxLENGTHm[.FullBleed] - Size in meters [borderless]
+ * [Custom.]WIDTHxLENGTHmm[.FullBleed] - Size in millimeters [borderless]
+ * [Custom.]WIDTHxLENGTHpt[.FullBleed] - Size in points [borderless]
+ */
+
+ int w, l, /* Width and length of page */
+ numer, /* Unit scaling factor */
+ denom; /* ... */
+ char *ptr; /* Pointer into name */
+ const char *units; /* Pointer to units */
+ int custom; /* Custom page size? */
+
+
+ if (!_cups_strncasecmp(ppd, "Custom.", 7))
+ {
+ custom = 1;
+ numer = 2540;
+ denom = 72;
+ ptr = (char *)ppd + 7;
+ }
+ else
+ {
+ custom = 0;
+ numer = 2540;
+ denom = 1;
+ ptr = (char *)ppd;
+ }
+
+ /*
+ * Find any units in the size...
+ */
+
+ units = strchr(ptr, '.');
+ while (units && isdigit(units[1] & 255))
+ units = strchr(units + 1, '.');
+
+ if (units)
+ units -= 2;
+ else
+ units = ptr + strlen(ptr) - 2;
+
+ if (units > ptr)
+ {
+ if (isdigit(*units & 255) || *units == '.')
+ units ++;
+
+ if (!_cups_strncasecmp(units, "cm", 2))
+ {
+ numer = 1000;
+ denom = 1;
+ }
+ else if (!_cups_strncasecmp(units, "ft", 2))
+ {
+ numer = 2540 * 12;
+ denom = 1;
+ }
+ else if (!_cups_strncasecmp(units, "in", 2))
+ {
+ numer = 2540;
+ denom = 1;
+ }
+ else if (!_cups_strncasecmp(units, "mm", 2))
+ {
+ numer = 100;
+ denom = 1;
+ }
+ else if (*units == 'm' || *units == 'M')
+ {
+ numer = 100000;
+ denom = 1;
+ }
+ else if (!_cups_strncasecmp(units, "pt", 2))
+ {
+ numer = 2540;
+ denom = 72;
+ }
+ }
+
+ w = pwg_scan_measurement(ptr, &ptr, numer, denom);
+
+ if (ptr && ptr > ppd && *ptr == 'x')
+ {
+ l = pwg_scan_measurement(ptr + 1, &ptr, numer, denom);
+
+ if (ptr)
+ {
+ /*
+ * Not a standard size; convert it to a PWG custom name of the form:
+ *
+ * [oe|om]_WIDTHxHEIGHTuu_WIDTHxHEIGHTuu
+ */
+
+ size = &(cg->pwg_media);
+ size->width = w;
+ size->length = l;
+ size->pwg = cg->pwg_name;
+
+ pwgFormatSizeName(cg->pwg_name, sizeof(cg->pwg_name),
+ custom ? "custom" : NULL, custom ? ppd + 7 : NULL,
+ size->width, size->length, NULL);
+ }
+ }
+ }
+
+ return (size);
+}
+
+/* For OS X 10.8 and earlier */
+pwg_media_t *_pwgMediaForPPD(const char *ppd)
+{ return (pwgMediaForPPD(ppd)); }
+
+
+/*
+ * 'pwgMediaForPWG()' - Find a PWG media size by 5101.1 self-describing name.
+ *
+ * The "pwg" argument specifies a self-describing media size name of the form
+ * "prefix_name_WIDTHxLENGTHunits" as defined in PWG 5101.1.
+ *
+ * If the name is non-standard, the returned PWG media size is stored in
+ * thread-local storage and is overwritten by each call to the function in the
+ * thread.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+pwg_media_t * /* O - Matching size or NULL */
+pwgMediaForPWG(const char *pwg) /* I - PWG size name */
+{
+ char *ptr; /* Pointer into name */
+ pwg_media_t key, /* Search key */
+ *size; /* Matching size */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!pwg)
+ return (NULL);
+
+ /*
+ * Build the lookup table for PWG names as needed...
+ */
+
+ if (!cg->pwg_size_lut)
+ {
+ int i; /* Looping var */
+
+ cg->pwg_size_lut = cupsArrayNew((cups_array_func_t)pwg_compare_pwg, NULL);
+
+ for (i = (int)(sizeof(cups_pwg_media) / sizeof(cups_pwg_media[0])),
+ size = (pwg_media_t *)cups_pwg_media;
+ i > 0;
+ i --, size ++)
+ cupsArrayAdd(cg->pwg_size_lut, size);
+ }
+
+ /*
+ * Lookup the name...
+ */
+
+ key.pwg = pwg;
+ if ((size = (pwg_media_t *)cupsArrayFind(cg->pwg_size_lut, &key)) == NULL &&
+ (ptr = (char *)strchr(pwg, '_')) != NULL &&
+ (ptr = (char *)strchr(ptr + 1, '_')) != NULL)
+ {
+ /*
+ * Try decoding the self-describing name of the form:
+ *
+ * class_name_WWWxHHHin
+ * class_name_WWWxHHHmm
+ */
+
+ int w, l; /* Width and length of page */
+ int numer; /* Scale factor for units */
+ const char *units = ptr + strlen(ptr) - 2;
+ /* Units from size */
+
+ ptr ++;
+
+ if (units >= ptr && !strcmp(units, "in"))
+ numer = 2540;
+ else
+ numer = 100;
+
+ w = pwg_scan_measurement(ptr, &ptr, numer, 1);
+
+ if (ptr && *ptr == 'x')
+ {
+ l = pwg_scan_measurement(ptr + 1, &ptr, numer, 1);
+
+ if (ptr)
+ {
+ size = &(cg->pwg_media);
+ size->width = w;
+ size->length = l;
+
+ strlcpy(cg->pwg_name, pwg, sizeof(cg->pwg_name));
+ size->pwg = cg->pwg_name;
+ }
+ }
+ }
+
+ return (size);
+}
+
+/* For OS X 10.8 and earlier */
+pwg_media_t *_pwgMediaForPWG(const char *pwg)
+{ return (pwgMediaForPWG(pwg)); }
+
+
+/*
+ * 'pwgMediaForSize()' - Get the PWG media size for the given dimensions.
+ *
+ * The "width" and "length" are in hundredths of millimeters, equivalent to
+ * 1/100000th of a meter or 1/2540th of an inch.
+ *
+ * If the dimensions are non-standard, the returned PWG media size is stored in
+ * thread-local storage and is overwritten by each call to the function in the
+ * thread.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+pwg_media_t * /* O - PWG media name */
+pwgMediaForSize(int width, /* I - Width in hundredths of millimeters */
+ int length) /* I - Length in hundredths of millimeters */
+{
+ int i; /* Looping var */
+ pwg_media_t *media, /* Current media */
+ *best_media = NULL; /* Best match */
+ int dw, dl, /* Difference in width and length */
+ best_dw = 999, /* Best difference in width and length */
+ best_dl = 999;
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (width <= 0 || length <= 0)
+ return (NULL);
+
+ /*
+ * Look for a standard size...
+ */
+
+ for (i = (int)(sizeof(cups_pwg_media) / sizeof(cups_pwg_media[0])),
+ media = (pwg_media_t *)cups_pwg_media;
+ i > 0;
+ i --, media ++)
+ {
+ /*
+ * Adobe uses a size matching algorithm with an epsilon of 5 points, which
+ * is just about 176/2540ths...
+ */
+
+ dw = abs(media->width - width);
+ dl = abs(media->length - length);
+
+ if (!dw && !dl)
+ return (media);
+ else if (dw < 176 && dl < 176)
+ {
+ if (dw <= best_dw && dl <= best_dl)
+ {
+ best_media = media;
+ best_dw = dw;
+ best_dl = dl;
+ }
+ }
+ }
+
+ if (best_media)
+ return (best_media);
+
+ /*
+ * Not a standard size; convert it to a PWG custom name of the form:
+ *
+ * custom_WIDTHxHEIGHTuu_WIDTHxHEIGHTuu
+ */
+
+ pwgFormatSizeName(cg->pwg_name, sizeof(cg->pwg_name), "custom", NULL, width,
+ length, NULL);
+
+ cg->pwg_media.pwg = cg->pwg_name;
+ cg->pwg_media.width = width;
+ cg->pwg_media.length = length;
+
+ return (&(cg->pwg_media));
+}
+
+/* For OS X 10.8 and earlier */
+pwg_media_t *_pwgMediaForSize(int width, int length)
+{ return (pwgMediaForSize(width, length)); }
+
+
+/*
+ * '_pwgMediaTable()' - Return the internal media size table.
+ */
+
+const pwg_media_t * /* O - Pointer to first entry */
+_pwgMediaTable(size_t *num_media) /* O - Number of entries */
+{
+ *num_media = sizeof(cups_pwg_media) / sizeof(cups_pwg_media[0]);
+
+ return (cups_pwg_media);
+}
+
+
+/*
+ * 'pwg_compare_legacy()' - Compare two sizes using the legacy names.
+ */
+
+static int /* O - Result of comparison */
+pwg_compare_legacy(pwg_media_t *a, /* I - First size */
+ pwg_media_t *b) /* I - Second size */
+{
+ return (strcmp(a->legacy, b->legacy));
+}
+
+
+/*
+ * 'pwg_compare_ppd()' - Compare two sizes using the PPD names.
+ */
+
+static int /* O - Result of comparison */
+pwg_compare_ppd(pwg_media_t *a, /* I - First size */
+ pwg_media_t *b) /* I - Second size */
+{
+ return (strcmp(a->ppd, b->ppd));
+}
+
+
+/*
+ * 'pwg_compare_pwg()' - Compare two sizes using the PWG names.
+ */
+
+static int /* O - Result of comparison */
+pwg_compare_pwg(pwg_media_t *a, /* I - First size */
+ pwg_media_t *b) /* I - Second size */
+{
+ return (strcmp(a->pwg, b->pwg));
+}
+
+
+/*
+ * 'pwg_format_inches()' - Convert and format PWG units as inches.
+ */
+
+static char * /* O - String */
+pwg_format_inches(char *buf, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ int val) /* I - Value in hundredths of millimeters */
+{
+ int thousandths, /* Thousandths of inches */
+ integer, /* Integer portion */
+ fraction; /* Fractional portion */
+
+
+ /*
+ * Convert hundredths of millimeters to thousandths of inches and round to
+ * the nearest thousandth.
+ */
+
+ thousandths = (val * 1000 + 1270) / 2540;
+ integer = thousandths / 1000;
+ fraction = thousandths % 1000;
+
+ /*
+ * Format as a pair of integers (avoids locale stuff), avoiding trailing
+ * zeros...
+ */
+
+ if (fraction == 0)
+ snprintf(buf, bufsize, "%d", integer);
+ else if (fraction % 10)
+ snprintf(buf, bufsize, "%d.%03d", integer, fraction);
+ else if (fraction % 100)
+ snprintf(buf, bufsize, "%d.%02d", integer, fraction / 10);
+ else
+ snprintf(buf, bufsize, "%d.%01d", integer, fraction / 100);
+
+ return (buf);
+}
+
+
+/*
+ * 'pwg_format_millimeters()' - Convert and format PWG units as millimeters.
+ */
+
+static char * /* O - String */
+pwg_format_millimeters(char *buf, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ int val) /* I - Value in hundredths of millimeters */
+{
+ int integer, /* Integer portion */
+ fraction; /* Fractional portion */
+
+
+ /*
+ * Convert hundredths of millimeters to integer and fractional portions.
+ */
+
+ integer = val / 100;
+ fraction = val % 100;
+
+ /*
+ * Format as a pair of integers (avoids locale stuff), avoiding trailing
+ * zeros...
+ */
+
+ if (fraction == 0)
+ snprintf(buf, bufsize, "%d", integer);
+ else if (fraction % 10)
+ snprintf(buf, bufsize, "%d.%02d", integer, fraction);
+ else
+ snprintf(buf, bufsize, "%d.%01d", integer, fraction / 10);
+
+ return (buf);
+}
+
+
+/*
+ * 'pwg_scan_measurement()' - Scan a measurement in inches or millimeters.
+ *
+ * The "factor" argument specifies the scale factor for the units to convert to
+ * hundredths of millimeters. The returned value is NOT rounded but is an
+ * exact conversion of the fraction value (no floating point is used).
+ */
+
+static int /* O - Hundredths of millimeters */
+pwg_scan_measurement(
+ const char *buf, /* I - Number string */
+ char **bufptr, /* O - First byte after the number */
+ int numer, /* I - Numerator from units */
+ int denom) /* I - Denominator from units */
+{
+ int value = 0, /* Measurement value */
+ fractional = 0, /* Fractional value */
+ divisor = 1, /* Fractional divisor */
+ digits = 10 * numer * denom; /* Maximum fractional value to read */
+
+
+ /*
+ * Scan integer portion...
+ */
+
+ while (*buf >= '0' && *buf <= '9')
+ value = value * 10 + (*buf++) - '0';
+
+ if (*buf == '.')
+ {
+ /*
+ * Scan fractional portion...
+ */
+
+ buf ++;
+
+ while (divisor < digits && *buf >= '0' && *buf <= '9')
+ {
+ fractional = fractional * 10 + (*buf++) - '0';
+ divisor *= 10;
+ }
+
+ /*
+ * Skip trailing digits that won't contribute...
+ */
+
+ while (*buf >= '0' && *buf <= '9')
+ buf ++;
+ }
+
+ if (bufptr)
+ *bufptr = (char *)buf;
+
+ return (value * numer / denom + fractional * numer / denom / divisor);
+}
+
+
+/*
+ * End of "$Id: pwg-media.c 11240 2013-08-14 20:33:55Z msweet $".
+ */
diff --git a/cups/libs/cups/pwg-private.h b/cups/libs/cups/pwg-private.h
new file mode 100644
index 000000000..1f1bc7cff
--- /dev/null
+++ b/cups/libs/cups/pwg-private.h
@@ -0,0 +1,78 @@
+/*
+ * "$Id: pwg-private.h 11240 2013-08-14 20:33:55Z msweet $"
+ *
+ * Private PWG media API definitions for CUPS.
+ *
+ * Copyright 2009-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_PWG_PRIVATE_H_
+# define _CUPS_PWG_PRIVATE_H_
+
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/cups.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Deprecated stuff for prior users of the private PWG media API...
+ */
+
+# ifndef _CUPS_NO_DEPRECATED
+typedef struct pwg_map_s _pwg_map_t;
+typedef struct pwg_media_s _pwg_media_t;
+typedef struct pwg_size_s _pwg_size_t;
+# endif /* _CUPS_NO_DEPRECATED */
+
+
+/*
+ * Functions...
+ */
+
+extern void _pwgGenerateSize(char *keyword, size_t keysize,
+ const char *prefix,
+ const char *name,
+ int width, int length)
+ _CUPS_INTERNAL_MSG("Use pwgFormatSizeName instead.");
+extern int _pwgInitSize(pwg_size_t *size, ipp_t *job,
+ int *margins_set)
+ _CUPS_INTERNAL_MSG("Use pwgInitSize instead.");
+extern pwg_media_t *_pwgMediaForLegacy(const char *legacy)
+ _CUPS_INTERNAL_MSG("Use pwgMediaForLegacy instead.");
+extern pwg_media_t *_pwgMediaForPPD(const char *ppd)
+ _CUPS_INTERNAL_MSG("Use pwgMediaForPPD instead.");
+extern pwg_media_t *_pwgMediaForPWG(const char *pwg)
+ _CUPS_INTERNAL_MSG("Use pwgMediaForPWG instead.");
+extern pwg_media_t *_pwgMediaForSize(int width, int length)
+ _CUPS_INTERNAL_MSG("Use pwgMediaForSize instead.");
+extern const pwg_media_t *_pwgMediaTable(size_t *num_media);
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_PWG_PRIVATE_H_ */
+
+/*
+ * End of "$Id: pwg-private.h 11240 2013-08-14 20:33:55Z msweet $".
+ */
diff --git a/cups/libs/cups/pwg.h b/cups/libs/cups/pwg.h
new file mode 100644
index 000000000..43e0e1c84
--- /dev/null
+++ b/cups/libs/cups/pwg.h
@@ -0,0 +1,94 @@
+/*
+ * "$Id: pwg.h 4274 2013-04-09 20:10:23Z msweet $"
+ *
+ * PWG media API definitions for CUPS.
+ *
+ * Copyright 2009-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_PWG_H_
+# define _CUPS_PWG_H_
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Macros...
+ */
+
+/* Convert from points to hundredths of millimeters */
+# define PWG_FROM_POINTS(n) (int)(((n) * 2540 + 36) / 72)
+/* Convert from hundredths of millimeters to points */
+# define PWG_TO_POINTS(n) ((n) * 72.0 / 2540.0)
+
+
+/*
+ * Types and structures...
+ */
+
+typedef struct pwg_map_s /**** Map element - PPD to/from PWG */
+{
+ char *pwg, /* PWG media keyword */
+ *ppd; /* PPD option keyword */
+} pwg_map_t;
+
+typedef struct pwg_media_s /**** Common media size data ****/
+{
+ const char *pwg, /* PWG 5101.1 "self describing" name */
+ *legacy, /* IPP/ISO legacy name */
+ *ppd; /* Standard Adobe PPD name */
+ int width, /* Width in 2540ths */
+ length; /* Length in 2540ths */
+} pwg_media_t;
+
+typedef struct pwg_size_s /**** Size element - PPD to/from PWG */
+{
+ pwg_map_t map; /* Map element */
+ int width, /* Width in 2540ths */
+ length, /* Length in 2540ths */
+ left, /* Left margin in 2540ths */
+ bottom, /* Bottom margin in 2540ths */
+ right, /* Right margin in 2540ths */
+ top; /* Top margin in 2540ths */
+} pwg_size_t;
+
+
+/*
+ * Functions...
+ */
+
+extern int pwgFormatSizeName(char *keyword, size_t keysize,
+ const char *prefix, const char *name,
+ int width, int length,
+ const char *units) _CUPS_API_1_7;
+extern int pwgInitSize(pwg_size_t *size, ipp_t *job,
+ int *margins_set) _CUPS_API_1_7;
+extern pwg_media_t *pwgMediaForLegacy(const char *legacy) _CUPS_API_1_7;
+extern pwg_media_t *pwgMediaForPPD(const char *ppd) _CUPS_API_1_7;
+extern pwg_media_t *pwgMediaForPWG(const char *pwg) _CUPS_API_1_7;
+extern pwg_media_t *pwgMediaForSize(int width, int length) _CUPS_API_1_7;
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_PWG_H_ */
+
+/*
+ * End of "$Id: pwg.h 4274 2013-04-09 20:10:23Z msweet $".
+ */
diff --git a/cups/libs/cups/raster-private.h b/cups/libs/cups/raster-private.h
new file mode 100644
index 000000000..ebd5d7210
--- /dev/null
+++ b/cups/libs/cups/raster-private.h
@@ -0,0 +1,66 @@
+/*
+ * "$Id: raster-private.h 3794 2012-04-23 22:44:16Z msweet $"
+ *
+ * Private image library definitions for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1993-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_RASTER_PRIVATE_H_
+# define _CUPS_RASTER_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "raster.h"
+# include <cups/cups.h>
+# include <cups/debug-private.h>
+# include <cups/string-private.h>
+# ifdef WIN32
+# include <io.h>
+# include <winsock2.h> /* for htonl() definition */
+# else
+# include <unistd.h>
+# include <fcntl.h>
+# endif /* WIN32 */
+
+
+/*
+ * min/max macros...
+ */
+
+# ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+# endif /* !max */
+# ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+# endif /* !min */
+
+
+/*
+ * Prototypes...
+ */
+
+extern int _cupsRasterExecPS(cups_page_header2_t *h,
+ int *preferred_bits,
+ const char *code)
+ __attribute__((nonnull(3)));
+extern void _cupsRasterAddError(const char *f, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+extern void _cupsRasterClearError(void);
+
+#endif /* !_CUPS_RASTER_PRIVATE_H_ */
+
+/*
+ * End of "$Id: raster-private.h 3794 2012-04-23 22:44:16Z msweet $".
+ */
diff --git a/cups/libs/cups/raster.h b/cups/libs/cups/raster.h
new file mode 100644
index 000000000..dde34f249
--- /dev/null
+++ b/cups/libs/cups/raster.h
@@ -0,0 +1,405 @@
+/*
+ * "$Id: raster.h 4027 2012-11-16 01:00:05Z msweet $"
+ *
+ * Raster file definitions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * This file is part of the CUPS Imaging library.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_RASTER_H_
+# define _CUPS_RASTER_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "cups.h"
+# include "ppd.h"
+
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/*
+ * Every non-PostScript printer driver that supports raster images
+ * should use the application/vnd.cups-raster image file format.
+ * Since both the PostScript RIP (pstoraster, based on GNU/GPL
+ * Ghostscript) and Image RIP (imagetoraster, located in the filter
+ * directory) use it, using this format saves you a lot of work.
+ * Also, the PostScript RIP passes any printer options that are in
+ * a PS file to your driver this way as well...
+ */
+
+/*
+ * Constants...
+ */
+
+# define CUPS_RASTER_SYNC 0x52615333 /* RaS3 */
+# define CUPS_RASTER_REVSYNC 0x33536152 /* 3SaR */
+
+# define CUPS_RASTER_SYNCv1 0x52615374 /* RaSt */
+# define CUPS_RASTER_REVSYNCv1 0x74536152 /* tSaR */
+
+# define CUPS_RASTER_SYNCv2 0x52615332 /* RaS2 */
+# define CUPS_RASTER_REVSYNCv2 0x32536152 /* 2SaR */
+
+# define CUPS_RASTER_SYNC_PWG CUPS_RASTER_SYNCv2
+
+
+/*
+ * The following definition can be used to determine if the
+ * colorimetric colorspaces (CIEXYZ, CIELAB, and ICCn) are
+ * defined...
+ */
+
+# define CUPS_RASTER_HAVE_COLORIMETRIC 1
+
+/*
+ * The following definition can be used to determine if the
+ * device colorspaces (DEVICEn) are defined...
+ */
+
+# define CUPS_RASTER_HAVE_DEVICE 1
+
+/*
+ * The following definition can be used to determine if PWG Raster is supported.
+ */
+
+# define CUPS_RASTER_HAVE_PWGRASTER 1
+
+
+/*
+ * Types...
+ */
+
+typedef enum cups_adv_e /**** AdvanceMedia attribute values ****/
+{
+ CUPS_ADVANCE_NONE = 0, /* Never advance the roll */
+ CUPS_ADVANCE_FILE = 1, /* Advance the roll after this file */
+ CUPS_ADVANCE_JOB = 2, /* Advance the roll after this job */
+ CUPS_ADVANCE_SET = 3, /* Advance the roll after this set */
+ CUPS_ADVANCE_PAGE = 4 /* Advance the roll after this page */
+} cups_adv_t;
+
+typedef enum cups_bool_e /**** Boolean type ****/
+{
+ CUPS_FALSE = 0, /* Logical false */
+ CUPS_TRUE = 1 /* Logical true */
+} cups_bool_t;
+
+typedef enum cups_cspace_e /**** cupsColorSpace attribute values ****/
+{
+ CUPS_CSPACE_W = 0, /* Luminance (DeviceGray, gamma 2.2 by default) */
+ CUPS_CSPACE_RGB = 1, /* Red, green, blue (DeviceRGB, sRGB by default) */
+ CUPS_CSPACE_RGBA = 2, /* Red, green, blue, alpha (DeviceRGB, sRGB by default) */
+ CUPS_CSPACE_K = 3, /* Black (DeviceK) */
+ CUPS_CSPACE_CMY = 4, /* Cyan, magenta, yellow (DeviceCMY) */
+ CUPS_CSPACE_YMC = 5, /* Yellow, magenta, cyan @deprecated@ */
+ CUPS_CSPACE_CMYK = 6, /* Cyan, magenta, yellow, black (DeviceCMYK) */
+ CUPS_CSPACE_YMCK = 7, /* Yellow, magenta, cyan, black @deprecated@ */
+ CUPS_CSPACE_KCMY = 8, /* Black, cyan, magenta, yellow @deprecated@ */
+ CUPS_CSPACE_KCMYcm = 9, /* Black, cyan, magenta, yellow, light-cyan, light-magenta @deprecated@ */
+ CUPS_CSPACE_GMCK = 10, /* Gold, magenta, yellow, black @deprecated@ */
+ CUPS_CSPACE_GMCS = 11, /* Gold, magenta, yellow, silver @deprecated@ */
+ CUPS_CSPACE_WHITE = 12, /* White ink (as black) @deprecated@ */
+ CUPS_CSPACE_GOLD = 13, /* Gold foil @deprecated@ */
+ CUPS_CSPACE_SILVER = 14, /* Silver foil @deprecated@ */
+
+ CUPS_CSPACE_CIEXYZ = 15, /* CIE XYZ @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_CIELab = 16, /* CIE Lab @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_RGBW = 17, /* Red, green, blue, white (DeviceRGB, sRGB by default) @since CUPS 1.2/OS X 10.5@ */
+ CUPS_CSPACE_SW = 18, /* Luminance (gamma 2.2) @since CUPS 1.4.5@ */
+ CUPS_CSPACE_SRGB = 19, /* Red, green, blue (sRGB) @since CUPS 1.4.5@ */
+ CUPS_CSPACE_ADOBERGB = 20, /* Red, green, blue (Adobe RGB) @since CUPS 1.4.5@ */
+
+ CUPS_CSPACE_ICC1 = 32, /* ICC-based, 1 color @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC2 = 33, /* ICC-based, 2 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC3 = 34, /* ICC-based, 3 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC4 = 35, /* ICC-based, 4 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC5 = 36, /* ICC-based, 5 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC6 = 37, /* ICC-based, 6 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC7 = 38, /* ICC-based, 7 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC8 = 39, /* ICC-based, 8 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICC9 = 40, /* ICC-based, 9 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICCA = 41, /* ICC-based, 10 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICCB = 42, /* ICC-based, 11 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICCC = 43, /* ICC-based, 12 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICCD = 44, /* ICC-based, 13 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICCE = 45, /* ICC-based, 14 colors @since CUPS 1.1.19/OS X 10.3@ */
+ CUPS_CSPACE_ICCF = 46, /* ICC-based, 15 colors @since CUPS 1.1.19/OS X 10.3@ */
+
+ CUPS_CSPACE_DEVICE1 = 48, /* DeviceN, 1 color @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE2 = 49, /* DeviceN, 2 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE3 = 50, /* DeviceN, 3 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE4 = 51, /* DeviceN, 4 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE5 = 52, /* DeviceN, 5 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE6 = 53, /* DeviceN, 6 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE7 = 54, /* DeviceN, 7 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE8 = 55, /* DeviceN, 8 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICE9 = 56, /* DeviceN, 9 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICEA = 57, /* DeviceN, 10 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICEB = 58, /* DeviceN, 11 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICEC = 59, /* DeviceN, 12 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICED = 60, /* DeviceN, 13 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICEE = 61, /* DeviceN, 14 colors @since CUPS 1.4.5@ */
+ CUPS_CSPACE_DEVICEF = 62 /* DeviceN, 15 colors @since CUPS 1.4.5@ */
+} cups_cspace_t;
+
+typedef enum cups_cut_e /**** CutMedia attribute values ****/
+{
+ CUPS_CUT_NONE = 0, /* Never cut the roll */
+ CUPS_CUT_FILE = 1, /* Cut the roll after this file */
+ CUPS_CUT_JOB = 2, /* Cut the roll after this job */
+ CUPS_CUT_SET = 3, /* Cut the roll after this set */
+ CUPS_CUT_PAGE = 4 /* Cut the roll after this page */
+} cups_cut_t;
+
+typedef enum cups_edge_e /**** LeadingEdge attribute values ****/
+{
+ CUPS_EDGE_TOP = 0, /* Leading edge is the top of the page */
+ CUPS_EDGE_RIGHT = 1, /* Leading edge is the right of the page */
+ CUPS_EDGE_BOTTOM = 2, /* Leading edge is the bottom of the page */
+ CUPS_EDGE_LEFT = 3 /* Leading edge is the left of the page */
+} cups_edge_t;
+
+typedef enum cups_jog_e /**** Jog attribute values ****/
+{
+ CUPS_JOG_NONE = 0, /* Never move pages */
+ CUPS_JOG_FILE = 1, /* Move pages after this file */
+ CUPS_JOG_JOB = 2, /* Move pages after this job */
+ CUPS_JOG_SET = 3 /* Move pages after this set */
+} cups_jog_t;
+
+enum cups_mode_e /**** cupsRasterOpen modes ****/
+{
+ CUPS_RASTER_READ = 0, /* Open stream for reading */
+ CUPS_RASTER_WRITE = 1, /* Open stream for writing */
+ CUPS_RASTER_WRITE_COMPRESSED = 2, /* Open stream for compressed writing @since CUPS 1.3/OS X 10.5@ */
+ CUPS_RASTER_WRITE_PWG = 3 /* Open stream for compressed writing in PWG mode @since CUPS 1.5/OS X 10.7@ */
+};
+
+typedef enum cups_mode_e cups_mode_t; /**** cupsRasterOpen modes ****/
+
+typedef enum cups_order_e /**** cupsColorOrder attribute values ****/
+{
+ CUPS_ORDER_CHUNKED = 0, /* CMYK CMYK CMYK ... */
+ CUPS_ORDER_BANDED = 1, /* CCC MMM YYY KKK ... */
+ CUPS_ORDER_PLANAR = 2 /* CCC ... MMM ... YYY ... KKK ... */
+} cups_order_t;
+
+typedef enum cups_orient_e /**** Orientation attribute values ****/
+{
+ CUPS_ORIENT_0 = 0, /* Don't rotate the page */
+ CUPS_ORIENT_90 = 1, /* Rotate the page counter-clockwise */
+ CUPS_ORIENT_180 = 2, /* Turn the page upside down */
+ CUPS_ORIENT_270 = 3 /* Rotate the page clockwise */
+} cups_orient_t;
+
+
+/*
+ * The page header structure contains the standard PostScript page device
+ * dictionary, along with some CUPS-specific parameters that are provided
+ * by the RIPs...
+ *
+ * The API supports a "version 1" (from CUPS 1.0 and 1.1) and a "version 2"
+ * (from CUPS 1.2 and higher) page header, for binary compatibility.
+ */
+
+typedef struct cups_page_header_s /**** Version 1 page header @deprecated@ ****/
+{
+ /**** Standard Page Device Dictionary String Values ****/
+ char MediaClass[64]; /* MediaClass string */
+ char MediaColor[64]; /* MediaColor string */
+ char MediaType[64]; /* MediaType string */
+ char OutputType[64]; /* OutputType string */
+
+ /**** Standard Page Device Dictionary Integer Values ****/
+ unsigned AdvanceDistance; /* AdvanceDistance value in points */
+ cups_adv_t AdvanceMedia; /* AdvanceMedia value (@link cups_adv_t@) */
+ cups_bool_t Collate; /* Collated copies value */
+ cups_cut_t CutMedia; /* CutMedia value (@link cups_cut_t@) */
+ cups_bool_t Duplex; /* Duplexed (double-sided) value */
+ unsigned HWResolution[2]; /* Resolution in dots-per-inch */
+ unsigned ImagingBoundingBox[4]; /* Pixel region that is painted (points, left, bottom, right, top) */
+ cups_bool_t InsertSheet; /* InsertSheet value */
+ cups_jog_t Jog; /* Jog value (@link cups_jog_t@) */
+ cups_edge_t LeadingEdge; /* LeadingEdge value (@link cups_edge_t@) */
+ unsigned Margins[2]; /* Lower-lefthand margins in points */
+ cups_bool_t ManualFeed; /* ManualFeed value */
+ unsigned MediaPosition; /* MediaPosition value */
+ unsigned MediaWeight; /* MediaWeight value in grams/m^2 */
+ cups_bool_t MirrorPrint; /* MirrorPrint value */
+ cups_bool_t NegativePrint; /* NegativePrint value */
+ unsigned NumCopies; /* Number of copies to produce */
+ cups_orient_t Orientation; /* Orientation value (@link cups_orient_t@) */
+ cups_bool_t OutputFaceUp; /* OutputFaceUp value */
+ unsigned PageSize[2]; /* Width and length of page in points */
+ cups_bool_t Separations; /* Separations value */
+ cups_bool_t TraySwitch; /* TraySwitch value */
+ cups_bool_t Tumble; /* Tumble value */
+
+ /**** CUPS Page Device Dictionary Values ****/
+ unsigned cupsWidth; /* Width of page image in pixels */
+ unsigned cupsHeight; /* Height of page image in pixels */
+ unsigned cupsMediaType; /* Media type code */
+ unsigned cupsBitsPerColor; /* Number of bits for each color */
+ unsigned cupsBitsPerPixel; /* Number of bits for each pixel */
+ unsigned cupsBytesPerLine; /* Number of bytes per line */
+ cups_order_t cupsColorOrder; /* Order of colors */
+ cups_cspace_t cupsColorSpace; /* True colorspace */
+ unsigned cupsCompression; /* Device compression to use */
+ unsigned cupsRowCount; /* Rows per band */
+ unsigned cupsRowFeed; /* Feed between bands */
+ unsigned cupsRowStep; /* Spacing between lines */
+} cups_page_header_t;
+
+/**** New in CUPS 1.2 ****/
+typedef struct cups_page_header2_s /**** Version 2 page header @since CUPS 1.2/OS X 10.5@ ****/
+{
+ /**** Standard Page Device Dictionary String Values ****/
+ char MediaClass[64]; /* MediaClass string */
+ char MediaColor[64]; /* MediaColor string */
+ char MediaType[64]; /* MediaType string */
+ char OutputType[64]; /* OutputType string */
+
+ /**** Standard Page Device Dictionary Integer Values ****/
+ unsigned AdvanceDistance; /* AdvanceDistance value in points */
+ cups_adv_t AdvanceMedia; /* AdvanceMedia value (@link cups_adv_t@) */
+ cups_bool_t Collate; /* Collated copies value */
+ cups_cut_t CutMedia; /* CutMedia value (@link cups_cut_t@) */
+ cups_bool_t Duplex; /* Duplexed (double-sided) value */
+ unsigned HWResolution[2]; /* Resolution in dots-per-inch */
+ unsigned ImagingBoundingBox[4]; /* Pixel region that is painted (points, left, bottom, right, top) */
+ cups_bool_t InsertSheet; /* InsertSheet value */
+ cups_jog_t Jog; /* Jog value (@link cups_jog_t@) */
+ cups_edge_t LeadingEdge; /* LeadingEdge value (@link cups_edge_t@) */
+ unsigned Margins[2]; /* Lower-lefthand margins in points */
+ cups_bool_t ManualFeed; /* ManualFeed value */
+ unsigned MediaPosition; /* MediaPosition value */
+ unsigned MediaWeight; /* MediaWeight value in grams/m^2 */
+ cups_bool_t MirrorPrint; /* MirrorPrint value */
+ cups_bool_t NegativePrint; /* NegativePrint value */
+ unsigned NumCopies; /* Number of copies to produce */
+ cups_orient_t Orientation; /* Orientation value (@link cups_orient_t@) */
+ cups_bool_t OutputFaceUp; /* OutputFaceUp value */
+ unsigned PageSize[2]; /* Width and length of page in points */
+ cups_bool_t Separations; /* Separations value */
+ cups_bool_t TraySwitch; /* TraySwitch value */
+ cups_bool_t Tumble; /* Tumble value */
+
+ /**** CUPS Page Device Dictionary Values ****/
+ unsigned cupsWidth; /* Width of page image in pixels */
+ unsigned cupsHeight; /* Height of page image in pixels */
+ unsigned cupsMediaType; /* Media type code */
+ unsigned cupsBitsPerColor; /* Number of bits for each color */
+ unsigned cupsBitsPerPixel; /* Number of bits for each pixel */
+ unsigned cupsBytesPerLine; /* Number of bytes per line */
+ cups_order_t cupsColorOrder; /* Order of colors */
+ cups_cspace_t cupsColorSpace; /* True colorspace */
+ unsigned cupsCompression; /* Device compression to use */
+ unsigned cupsRowCount; /* Rows per band */
+ unsigned cupsRowFeed; /* Feed between bands */
+ unsigned cupsRowStep; /* Spacing between lines */
+
+ /**** Version 2 Dictionary Values ****/
+ unsigned cupsNumColors; /* Number of color compoents @since CUPS 1.2/OS X 10.5@ */
+ float cupsBorderlessScalingFactor;
+ /* Scaling that was applied to page data @since CUPS 1.2/OS X 10.5@ */
+ float cupsPageSize[2]; /* Floating point PageSize (scaling *
+ * factor not applied) @since CUPS 1.2/OS X 10.5@ */
+ float cupsImagingBBox[4]; /* Floating point ImagingBoundingBox
+ * (scaling factor not applied, left,
+ * bottom, right, top) @since CUPS 1.2/OS X 10.5@ */
+ unsigned cupsInteger[16]; /* User-defined integer values @since CUPS 1.2/OS X 10.5@ */
+ float cupsReal[16]; /* User-defined floating-point values @since CUPS 1.2/OS X 10.5@ */
+ char cupsString[16][64]; /* User-defined string values @since CUPS 1.2/OS X 10.5@ */
+ char cupsMarkerType[64]; /* Ink/toner type @since CUPS 1.2/OS X 10.5@ */
+ char cupsRenderingIntent[64];/* Color rendering intent @since CUPS 1.2/OS X 10.5@ */
+ char cupsPageSizeName[64]; /* PageSize name @since CUPS 1.2/OS X 10.5@ */
+} cups_page_header2_t;
+
+typedef struct _cups_raster_s cups_raster_t;
+ /**** Raster stream data ****/
+
+typedef int (*cups_interpret_cb_t)(cups_page_header2_t *header, int preferred_bits);
+ /**** cupsRasterInterpretPPD callback function
+ *
+ * This function is called by
+ * @link cupsRasterInterpretPPD@ to
+ * validate (and update, as needed)
+ * the page header attributes. The
+ * "preferred_bits" argument provides
+ * the value of the
+ * @code cupsPreferredBitsPerColor@
+ * key from the PostScript page device
+ * dictionary and is 0 if undefined.
+ ****/
+
+/**** New in CUPS 1.5 ****/
+typedef ssize_t (*cups_raster_iocb_t)(void *ctx, unsigned char *buffer, size_t length);
+ /**** cupsRasterOpenIO callback function
+ *
+ * This function is specified when
+ * creating a raster stream with
+ * @link cupsRasterOpenIO@ and handles
+ * generic reading and writing of raster
+ * data. It must return -1 on error or
+ * the number of bytes specified by
+ * "length" on success.
+ ****/
+
+
+/*
+ * Prototypes...
+ */
+
+extern void cupsRasterClose(cups_raster_t *r);
+extern cups_raster_t *cupsRasterOpen(int fd, cups_mode_t mode);
+extern unsigned cupsRasterReadHeader(cups_raster_t *r,
+ cups_page_header_t *h) _CUPS_DEPRECATED_MSG("Use cupsRasterReadHeader2 instead.");
+extern unsigned cupsRasterReadPixels(cups_raster_t *r,
+ unsigned char *p, unsigned len);
+extern unsigned cupsRasterWriteHeader(cups_raster_t *r,
+ cups_page_header_t *h) _CUPS_DEPRECATED_MSG("Use cupsRasterWriteHeader2 instead.");
+extern unsigned cupsRasterWritePixels(cups_raster_t *r,
+ unsigned char *p, unsigned len);
+
+/**** New in CUPS 1.2 ****/
+extern int cupsRasterInterpretPPD(cups_page_header2_t *h,
+ ppd_file_t *ppd,
+ int num_options,
+ cups_option_t *options,
+ cups_interpret_cb_t func) _CUPS_API_1_2;
+extern unsigned cupsRasterReadHeader2(cups_raster_t *r,
+ cups_page_header2_t *h) _CUPS_API_1_2;
+extern unsigned cupsRasterWriteHeader2(cups_raster_t *r,
+ cups_page_header2_t *h) _CUPS_API_1_2;
+
+/**** New in CUPS 1.3 ****/
+extern const char *cupsRasterErrorString(void) _CUPS_API_1_3;
+
+/**** New in CUPS 1.5 ****/
+extern cups_raster_t *cupsRasterOpenIO(cups_raster_iocb_t iocb, void *ctx,
+ cups_mode_t mode);
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_RASTER_H_ */
+
+/*
+ * End of "$Id: raster.h 4027 2012-11-16 01:00:05Z msweet $".
+ */
diff --git a/cups/libs/cups/request.c b/cups/libs/cups/request.c
new file mode 100644
index 000000000..cf261f4ac
--- /dev/null
+++ b/cups/libs/cups/request.c
@@ -0,0 +1,1209 @@
+/*
+ * "$Id: request.c 11867 2014-05-09 20:33:08Z msweet $"
+ *
+ * IPP utilities for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsDoFileRequest() - Do an IPP request with a file.
+ * cupsDoIORequest() - Do an IPP request with file descriptors.
+ * cupsDoRequest() - Do an IPP request.
+ * cupsGetResponse() - Get a response to an IPP request.
+ * cupsLastError() - Return the last IPP status code.
+ * cupsLastErrorString() - Return the last IPP status-message.
+ * _cupsNextDelay() - Return the next retry delay value.
+ * cupsReadResponseData() - Read additional data after the IPP response.
+ * cupsSendRequest() - Send an IPP request.
+ * cupsWriteRequestData() - Write additional data after an IPP request.
+ * _cupsConnect() - Get the default server connection...
+ * _cupsSetError() - Set the last IPP status code and status-message.
+ * _cupsSetHTTPError() - Set the last error using the HTTP status.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif /* O_BINARY */
+#ifndef MSG_DONTWAIT
+# define MSG_DONTWAIT 0
+#endif /* !MSG_DONTWAIT */
+
+
+/*
+ * 'cupsDoFileRequest()' - Do an IPP request with a file.
+ *
+ * This function sends the IPP request and attached file to the specified
+ * server, retrying and authenticating as necessary. The request is freed with
+ * @link ippDelete@.
+ */
+
+ipp_t * /* O - Response data */
+cupsDoFileRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ ipp_t *request, /* I - IPP request */
+ const char *resource, /* I - HTTP resource for POST */
+ const char *filename) /* I - File to send or @code NULL@ for none */
+{
+ ipp_t *response; /* IPP response data */
+ int infile; /* Input file */
+
+
+ DEBUG_printf(("cupsDoFileRequest(http=%p, request=%p(%s), resource=\"%s\", "
+ "filename=\"%s\")", http, request,
+ request ? ippOpString(request->request.op.operation_id) : "?",
+ resource, filename));
+
+ if (filename)
+ {
+ if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
+ {
+ /*
+ * Can't get file information!
+ */
+
+ _cupsSetError(errno == ENOENT ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED,
+ NULL, 0);
+
+ ippDelete(request);
+
+ return (NULL);
+ }
+ }
+ else
+ infile = -1;
+
+ response = cupsDoIORequest(http, request, resource, infile, -1);
+
+ if (infile >= 0)
+ close(infile);
+
+ return (response);
+}
+
+
+/*
+ * 'cupsDoIORequest()' - Do an IPP request with file descriptors.
+ *
+ * This function sends the IPP request with the optional input file "infile" to
+ * the specified server, retrying and authenticating as necessary. The request
+ * is freed with @link ippDelete@.
+ *
+ * If "infile" is a valid file descriptor, @code cupsDoIORequest@ copies
+ * all of the data from the file after the IPP request message.
+ *
+ * If "outfile" is a valid file descriptor, @code cupsDoIORequest@ copies
+ * all of the data after the IPP response message to the file.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+ipp_t * /* O - Response data */
+cupsDoIORequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ ipp_t *request, /* I - IPP request */
+ const char *resource, /* I - HTTP resource for POST */
+ int infile, /* I - File to read from or -1 for none */
+ int outfile) /* I - File to write to or -1 for none */
+{
+ ipp_t *response = NULL; /* IPP response data */
+ size_t length = 0; /* Content-Length value */
+ http_status_t status; /* Status of HTTP request */
+ struct stat fileinfo; /* File information */
+ int bytes; /* Number of bytes read/written */
+ char buffer[32768]; /* Output buffer */
+
+
+ DEBUG_printf(("cupsDoIORequest(http=%p, request=%p(%s), resource=\"%s\", "
+ "infile=%d, outfile=%d)", http, request,
+ request ? ippOpString(request->request.op.operation_id) : "?",
+ resource, infile, outfile));
+
+ /*
+ * Range check input...
+ */
+
+ if (!request || !resource)
+ {
+ ippDelete(request);
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (NULL);
+ }
+
+ /*
+ * Get the default connection as needed...
+ */
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ {
+ ippDelete(request);
+
+ return (NULL);
+ }
+
+ /*
+ * See if we have a file to send...
+ */
+
+ if (infile >= 0)
+ {
+ if (fstat(infile, &fileinfo))
+ {
+ /*
+ * Can't get file information!
+ */
+
+ _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED,
+ NULL, 0);
+
+ ippDelete(request);
+
+ return (NULL);
+ }
+
+#ifdef WIN32
+ if (fileinfo.st_mode & _S_IFDIR)
+#else
+ if (S_ISDIR(fileinfo.st_mode))
+#endif /* WIN32 */
+ {
+ /*
+ * Can't send a directory...
+ */
+
+ ippDelete(request);
+
+ _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0);
+
+ return (NULL);
+ }
+
+#ifndef WIN32
+ if (!S_ISREG(fileinfo.st_mode))
+ length = 0; /* Chunk when piping */
+ else
+#endif /* !WIN32 */
+ length = ippLength(request) + fileinfo.st_size;
+ }
+ else
+ length = ippLength(request);
+
+ DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld",
+ (long)ippLength(request), (long)length));
+
+ /*
+ * Clear any "Local" authentication data since it is probably stale...
+ */
+
+ if (http->authstring && !strncmp(http->authstring, "Local ", 6))
+ httpSetAuthString(http, NULL, NULL);
+
+ /*
+ * Loop until we can send the request without authorization problems.
+ */
+
+ while (response == NULL)
+ {
+ DEBUG_puts("2cupsDoIORequest: setup...");
+
+ /*
+ * Send the request...
+ */
+
+ status = cupsSendRequest(http, request, resource, length);
+
+ DEBUG_printf(("2cupsDoIORequest: status=%d", status));
+
+ if (status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA && infile >= 0)
+ {
+ DEBUG_puts("2cupsDoIORequest: file write...");
+
+ /*
+ * Send the file with the request...
+ */
+
+#ifndef WIN32
+ if (S_ISREG(fileinfo.st_mode))
+#endif /* WIN32 */
+ lseek(infile, 0, SEEK_SET);
+
+ while ((bytes = (int)read(infile, buffer, sizeof(buffer))) > 0)
+ {
+ if ((status = cupsWriteRequestData(http, buffer, bytes))
+ != HTTP_STATUS_CONTINUE)
+ break;
+ }
+ }
+
+ /*
+ * Get the server's response...
+ */
+
+ if (status <= HTTP_STATUS_CONTINUE || status == HTTP_STATUS_OK)
+ {
+ response = cupsGetResponse(http, resource);
+ status = httpGetStatus(http);
+ }
+
+ DEBUG_printf(("2cupsDoIORequest: status=%d", status));
+
+ if (status == HTTP_STATUS_ERROR ||
+ (status >= HTTP_STATUS_BAD_REQUEST && status != HTTP_STATUS_UNAUTHORIZED &&
+ status != HTTP_STATUS_UPGRADE_REQUIRED))
+ {
+ _cupsSetHTTPError(status);
+ break;
+ }
+
+ if (response && outfile >= 0)
+ {
+ /*
+ * Write trailing data to file...
+ */
+
+ while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
+ if (write(outfile, buffer, bytes) < bytes)
+ break;
+ }
+
+ if (http->state != HTTP_STATE_WAITING)
+ {
+ /*
+ * Flush any remaining data...
+ */
+
+ httpFlush(http);
+ }
+ }
+
+ /*
+ * Delete the original request and return the response...
+ */
+
+ ippDelete(request);
+
+ return (response);
+}
+
+
+/*
+ * 'cupsDoRequest()' - Do an IPP request.
+ *
+ * This function sends the IPP request to the specified server, retrying
+ * and authenticating as necessary. The request is freed with @link ippDelete@.
+ */
+
+ipp_t * /* O - Response data */
+cupsDoRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ ipp_t *request, /* I - IPP request */
+ const char *resource) /* I - HTTP resource for POST */
+{
+ DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")",
+ http, request,
+ request ? ippOpString(request->request.op.operation_id) : "?",
+ resource));
+
+ return (cupsDoIORequest(http, request, resource, -1, -1));
+}
+
+
+/*
+ * 'cupsGetResponse()' - Get a response to an IPP request.
+ *
+ * Use this function to get the response for an IPP request sent using
+ * @link cupsSendRequest@. For requests that return additional data, use
+ * @link cupsReadResponseData@ after getting a successful response,
+ * otherwise call @link httpFlush@ to complete the response processing.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ipp_t * /* O - Response or @code NULL@ on HTTP error */
+cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *resource) /* I - HTTP resource for POST */
+{
+ http_status_t status; /* HTTP status */
+ ipp_state_t state; /* IPP read state */
+ ipp_t *response = NULL; /* IPP response */
+
+
+ DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", http, resource));
+ DEBUG_printf(("1cupsGetResponse: http->state=%d", http ? http->state : HTTP_STATE_ERROR));
+
+ /*
+ * Connect to the default server as needed...
+ */
+
+ if (!http)
+ {
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+ if ((http = cg->http) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection."), 1);
+ DEBUG_puts("1cupsGetResponse: No active connection - returning NULL.");
+ return (NULL);
+ }
+ }
+
+ if (http->state != HTTP_STATE_POST_RECV && http->state != HTTP_STATE_POST_SEND)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No request sent."), 1);
+ DEBUG_puts("1cupsGetResponse: Not in POST state - returning NULL.");
+ return (NULL);
+ }
+
+ /*
+ * Check for an unfinished chunked request...
+ */
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ {
+ /*
+ * Send a 0-length chunk to finish off the request...
+ */
+
+ DEBUG_puts("2cupsGetResponse: Finishing chunked POST...");
+
+ if (httpWrite2(http, "", 0) < 0)
+ return (NULL);
+ }
+
+ /*
+ * Wait for a response from the server...
+ */
+
+ DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...",
+ http->status));
+
+ do
+ {
+ status = httpUpdate(http);
+ }
+ while (status == HTTP_STATUS_CONTINUE);
+
+ DEBUG_printf(("2cupsGetResponse: status=%d", status));
+
+ if (status == HTTP_STATUS_OK)
+ {
+ /*
+ * Get the IPP response...
+ */
+
+ response = ippNew();
+
+ while ((state = ippRead(http, response)) != IPP_STATE_DATA)
+ if (state == IPP_STATE_ERROR)
+ break;
+
+ if (state == IPP_STATE_ERROR)
+ {
+ /*
+ * Flush remaining data and delete the response...
+ */
+
+ DEBUG_puts("1cupsGetResponse: IPP read error!");
+
+ httpFlush(http);
+
+ ippDelete(response);
+ response = NULL;
+
+ http->status = status = HTTP_STATUS_ERROR;
+ http->error = EINVAL;
+ }
+ }
+ else if (status != HTTP_STATUS_ERROR)
+ {
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+
+ /*
+ * Then handle encryption and authentication...
+ */
+
+ if (status == HTTP_STATUS_UNAUTHORIZED)
+ {
+ /*
+ * See if we can do authentication...
+ */
+
+ DEBUG_puts("2cupsGetResponse: Need authorization...");
+
+ if (!cupsDoAuthentication(http, "POST", resource))
+ httpReconnect2(http, 30000, NULL);
+ else
+ http->status = status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ }
+
+#ifdef HAVE_SSL
+ else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ /*
+ * Force a reconnect with encryption...
+ */
+
+ DEBUG_puts("2cupsGetResponse: Need encryption...");
+
+ if (!httpReconnect2(http, 30000, NULL))
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
+ }
+#endif /* HAVE_SSL */
+ }
+
+ if (response)
+ {
+ ipp_attribute_t *attr; /* status-message attribute */
+
+
+ attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
+
+ DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"",
+ ippErrorString(response->request.status.status_code),
+ attr ? attr->values[0].string.text : ""));
+
+ _cupsSetError(response->request.status.status_code,
+ attr ? attr->values[0].string.text :
+ ippErrorString(response->request.status.status_code), 0);
+ }
+
+ return (response);
+}
+
+
+/*
+ * 'cupsLastError()' - Return the last IPP status code received on the current
+ * thread.
+ */
+
+ipp_status_t /* O - IPP status code from last request */
+cupsLastError(void)
+{
+ return (_cupsGlobals()->last_error);
+}
+
+
+/*
+ * 'cupsLastErrorString()' - Return the last IPP status-message received on the
+ * current thread.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+const char * /* O - status-message text from last request */
+cupsLastErrorString(void)
+{
+ return (_cupsGlobals()->last_status_message);
+}
+
+
+/*
+ * '_cupsNextDelay()' - Return the next retry delay value.
+ *
+ * This function currently returns the Fibonacci sequence 1 1 2 3 5 8.
+ *
+ * Pass 0 for the current delay value to initialize the sequence.
+ */
+
+int /* O - Next delay value */
+_cupsNextDelay(int current, /* I - Current delay value or 0 */
+ int *previous) /* IO - Previous delay value */
+{
+ int next; /* Next delay value */
+
+
+ if (current > 0)
+ {
+ next = (current + *previous) % 12;
+ *previous = next < current ? 0 : current;
+ }
+ else
+ {
+ next = 1;
+ *previous = 0;
+ }
+
+ return (next);
+}
+
+
+/*
+ * 'cupsReadResponseData()' - Read additional data after the IPP response.
+ *
+ * This function is used after @link cupsGetResponse@ to read the PPD or document
+ * files from @code CUPS_GET_PPD@ and @code CUPS_GET_DOCUMENT@ requests,
+ * respectively.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ssize_t /* O - Bytes read, 0 on EOF, -1 on error */
+cupsReadResponseData(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ char *buffer, /* I - Buffer to use */
+ size_t length) /* I - Number of bytes to read */
+{
+ /*
+ * Get the default connection as needed...
+ */
+
+ DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, "
+ "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
+
+ if (!http)
+ {
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+ if ((http = cg->http) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
+ return (-1);
+ }
+ }
+
+ /*
+ * Then read from the HTTP connection...
+ */
+
+ return (httpRead2(http, buffer, length));
+}
+
+
+/*
+ * 'cupsSendRequest()' - Send an IPP request.
+ *
+ * Use @link cupsWriteRequestData@ to write any additional data (document, PPD
+ * file, etc.) for the request, @link cupsGetResponse@ to get the IPP response,
+ * and @link cupsReadResponseData@ to read any additional data following the
+ * response. Only one request can be sent/queued at a time per @code http_t@
+ * connection.
+ *
+ * Returns the initial HTTP status code, which will be @code HTTP_STATUS_CONTINUE@
+ * on a successful send of the request.
+ *
+ * Note: Unlike @link cupsDoFileRequest@, @link cupsDoIORequest@, and
+ * @link cupsDoRequest@, the request is NOT freed with @link ippDelete@.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+http_status_t /* O - Initial HTTP status */
+cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ ipp_t *request, /* I - IPP request */
+ const char *resource, /* I - Resource path */
+ size_t length) /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */
+{
+ http_status_t status; /* Status of HTTP request */
+ int got_status; /* Did we get the status? */
+ ipp_state_t state; /* State of IPP processing */
+ http_status_t expect; /* Expect: header to use */
+
+
+ DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", "
+ "length=" CUPS_LLFMT ")", http, request,
+ request ? ippOpString(request->request.op.operation_id) : "?",
+ resource, CUPS_LLCAST length));
+
+ /*
+ * Range check input...
+ */
+
+ if (!request || !resource)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Get the default connection as needed...
+ */
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+
+ /*
+ * If the prior request was not flushed out, do so now...
+ */
+
+ if (http->state == HTTP_STATE_GET_SEND ||
+ http->state == HTTP_STATE_POST_SEND)
+ {
+ DEBUG_puts("2cupsSendRequest: Flush prior response.");
+ httpFlush(http);
+ }
+ else if (http->state != HTTP_STATE_WAITING)
+ {
+ DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), "
+ "reconnecting.", http->state));
+ if (httpReconnect2(http, 30000, NULL))
+ return (HTTP_STATUS_ERROR);
+ }
+
+#ifdef HAVE_SSL
+ /*
+ * See if we have an auth-info attribute and are communicating over
+ * a non-local link. If so, encrypt the link so that we can pass
+ * the authentication information securely...
+ */
+
+ if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
+ !httpAddrLocalhost(http->hostaddr) && !http->tls &&
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+#endif /* HAVE_SSL */
+
+ /*
+ * Reconnect if the last response had a "Connection: close"...
+ */
+
+ if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close"))
+ {
+ DEBUG_puts("2cupsSendRequest: Connection: close");
+ httpClearFields(http);
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /*
+ * Loop until we can send the request without authorization problems.
+ */
+
+ expect = HTTP_STATUS_CONTINUE;
+
+ for (;;)
+ {
+ DEBUG_puts("2cupsSendRequest: Setup...");
+
+ /*
+ * Setup the HTTP variables needed...
+ */
+
+ httpClearFields(http);
+ httpSetExpect(http, expect);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+ httpSetLength(http, length);
+
+#ifdef HAVE_GSSAPI
+ if (http->authstring && !strncmp(http->authstring, "Negotiate", 9))
+ {
+ /*
+ * Do not use cached Kerberos credentials since they will look like a
+ * "replay" attack...
+ */
+
+ _cupsSetNegotiateAuthString(http, "POST", resource);
+ }
+#endif /* HAVE_GSSAPI */
+
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
+
+ DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring));
+
+ /*
+ * Try the request...
+ */
+
+ DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
+
+ if (httpPost(http, resource))
+ {
+ DEBUG_puts("2cupsSendRequest: POST failed, reconnecting.");
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+ else
+ continue;
+ }
+
+ /*
+ * Send the IPP data...
+ */
+
+ DEBUG_puts("2cupsSendRequest: Writing IPP request...");
+
+ request->state = IPP_STATE_IDLE;
+ status = HTTP_STATUS_CONTINUE;
+ got_status = 0;
+
+ while ((state = ippWrite(http, request)) != IPP_STATE_DATA)
+ {
+ if (httpCheck(http))
+ {
+ got_status = 1;
+
+ _httpUpdate(http, &status);
+ if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
+ break;
+ }
+ else if (state == IPP_STATE_ERROR)
+ break;
+ }
+
+ if (state == IPP_STATE_ERROR)
+ {
+ /*
+ * We weren't able to send the IPP request. But did we already get a HTTP
+ * error status?
+ */
+
+ if (!got_status || status < HTTP_STATUS_MULTIPLE_CHOICES)
+ {
+ /*
+ * No, something else went wrong.
+ */
+
+ DEBUG_puts("1cupsSendRequest: Unable to send IPP request.");
+
+ http->status = HTTP_STATUS_ERROR;
+ http->state = HTTP_STATE_WAITING;
+
+ return (HTTP_STATUS_ERROR);
+ }
+ }
+
+ /*
+ * Wait up to 1 second to get the 100-continue response as needed...
+ */
+
+ if (!got_status)
+ {
+ if (expect == HTTP_STATUS_CONTINUE)
+ {
+ DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
+
+ if (httpWait(http, 1000))
+ _httpUpdate(http, &status);
+ }
+ else if (httpCheck(http))
+ _httpUpdate(http, &status);
+ }
+
+ DEBUG_printf(("2cupsSendRequest: status=%d", status));
+
+ /*
+ * Process the current HTTP status...
+ */
+
+ if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
+ {
+ int temp_status; /* Temporary status */
+
+ _cupsSetHTTPError(status);
+
+ do
+ {
+ temp_status = httpUpdate(http);
+ }
+ while (temp_status != HTTP_STATUS_ERROR &&
+ http->state == HTTP_STATE_POST_RECV);
+
+ httpFlush(http);
+ }
+
+ switch (status)
+ {
+ case HTTP_STATUS_CONTINUE :
+ case HTTP_STATUS_OK :
+ case HTTP_STATUS_ERROR :
+ DEBUG_printf(("1cupsSendRequest: Returning %d.", status));
+ return (status);
+
+ case HTTP_STATUS_UNAUTHORIZED :
+ if (cupsDoAuthentication(http, "POST", resource))
+ {
+ DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED.");
+ return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED);
+ }
+
+ DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED.");
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+ break;
+
+#ifdef HAVE_SSL
+ case HTTP_STATUS_UPGRADE_REQUIRED :
+ /*
+ * Flush any error message, reconnect, and then upgrade with
+ * encryption...
+ */
+
+ DEBUG_puts("2cupsSendRequest: Reconnecting after "
+ "HTTP_STATUS_UPGRADE_REQUIRED.");
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+
+ DEBUG_puts("2cupsSendRequest: Upgrading to TLS.");
+ if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+ break;
+#endif /* HAVE_SSL */
+
+ case HTTP_STATUS_EXPECTATION_FAILED :
+ /*
+ * Don't try using the Expect: header the next time around...
+ */
+
+ expect = (http_status_t)0;
+
+ DEBUG_puts("2cupsSendRequest: Reconnecting after "
+ "HTTP_EXPECTATION_FAILED.");
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+ break;
+
+ default :
+ /*
+ * Some other error...
+ */
+
+ return (status);
+ }
+ }
+}
+
+
+/*
+ * 'cupsWriteRequestData()' - Write additional data after an IPP request.
+ *
+ * This function is used after @link cupsSendRequest@ to provide a PPD and
+ * after @link cupsStartDocument@ to provide a document file.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+http_status_t /* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */
+cupsWriteRequestData(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *buffer, /* I - Bytes to write */
+ size_t length) /* I - Number of bytes to write */
+{
+ int wused; /* Previous bytes in buffer */
+
+
+ /*
+ * Get the default connection as needed...
+ */
+
+ DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
+ "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
+
+ if (!http)
+ {
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+ if ((http = cg->http) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
+ DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
+ return (HTTP_STATUS_ERROR);
+ }
+ }
+
+ /*
+ * Then write to the HTTP connection...
+ */
+
+ wused = http->wused;
+
+ if (httpWrite2(http, buffer, length) < 0)
+ {
+ DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
+ return (HTTP_STATUS_ERROR);
+ }
+
+ /*
+ * Finally, check if we have any pending data from the server...
+ */
+
+ if (length >= HTTP_MAX_BUFFER ||
+ http->wused < wused ||
+ (wused > 0 && http->wused == length))
+ {
+ /*
+ * We've written something to the server, so check for response data...
+ */
+
+ if (_httpWait(http, 0, 1))
+ {
+ http_status_t status; /* Status from _httpUpdate */
+
+ _httpUpdate(http, &status);
+ if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
+ {
+ _cupsSetHTTPError(status);
+
+ do
+ {
+ status = httpUpdate(http);
+ }
+ while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV);
+
+ httpFlush(http);
+ }
+
+ DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status));
+ return (status);
+ }
+ }
+
+ DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE.");
+ return (HTTP_STATUS_CONTINUE);
+}
+
+
+/*
+ * '_cupsConnect()' - Get the default server connection...
+ */
+
+http_t * /* O - HTTP connection */
+_cupsConnect(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * See if we are connected to the same server...
+ */
+
+ if (cg->http)
+ {
+ /*
+ * Compare the connection hostname, port, and encryption settings to
+ * the cached defaults; these were initialized the first time we
+ * connected...
+ */
+
+ if (strcmp(cg->http->hostname, cg->server) ||
+ cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
+ (cg->http->encryption != cg->encryption &&
+ cg->http->encryption == HTTP_ENCRYPTION_NEVER))
+ {
+ /*
+ * Need to close the current connection because something has changed...
+ */
+
+ httpClose(cg->http);
+ cg->http = NULL;
+ }
+ else
+ {
+ /*
+ * Same server, see if the connection is still established...
+ */
+
+ char ch; /* Connection check byte */
+ ssize_t n; /* Number of bytes */
+
+#ifdef WIN32
+ if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK)) == 0 ||
+ (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK))
+#else
+ if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT)) == 0 ||
+ (n < 0 && errno != EWOULDBLOCK))
+#endif /* WIN32 */
+ {
+ /*
+ * Nope, close the connection...
+ */
+
+ httpClose(cg->http);
+ cg->http = NULL;
+ }
+ }
+ }
+
+ /*
+ * (Re)connect as needed...
+ */
+
+ if (!cg->http)
+ {
+ if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
+ cupsEncryption(), 1, 30000, NULL)) == NULL)
+ {
+ if (errno)
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0);
+ else
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE,
+ _("Unable to connect to host."), 1);
+ }
+ }
+
+ /*
+ * Return the cached connection...
+ */
+
+ return (cg->http);
+}
+
+
+/*
+ * '_cupsSetError()' - Set the last IPP status code and status-message.
+ */
+
+void
+_cupsSetError(ipp_status_t status, /* I - IPP status code */
+ const char *message, /* I - status-message value */
+ int localize) /* I - Localize the message? */
+{
+ _cups_globals_t *cg; /* Global data */
+
+
+ if (!message && errno)
+ {
+ message = strerror(errno);
+ localize = 0;
+ }
+
+ cg = _cupsGlobals();
+ cg->last_error = status;
+
+ if (cg->last_status_message)
+ {
+ _cupsStrFree(cg->last_status_message);
+
+ cg->last_status_message = NULL;
+ }
+
+ if (message)
+ {
+ if (localize)
+ {
+ /*
+ * Get the message catalog...
+ */
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
+ message));
+ }
+ else
+ cg->last_status_message = _cupsStrAlloc(message);
+ }
+
+ DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
+ ippErrorString(cg->last_error), cg->last_status_message));
+}
+
+
+/*
+ * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
+ */
+
+void
+_cupsSetHTTPError(http_status_t status) /* I - HTTP status code */
+{
+ switch (status)
+ {
+ case HTTP_STATUS_NOT_FOUND :
+ _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_UNAUTHORIZED :
+ _cupsSetError(IPP_STATUS_ERROR_NOT_AUTHENTICATED, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_FORBIDDEN :
+ _cupsSetError(IPP_STATUS_ERROR_FORBIDDEN, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_BAD_REQUEST :
+ _cupsSetError(IPP_STATUS_ERROR_BAD_REQUEST, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_REQUEST_TOO_LARGE :
+ _cupsSetError(IPP_STATUS_ERROR_REQUEST_VALUE, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_NOT_IMPLEMENTED :
+ _cupsSetError(IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_NOT_SUPPORTED :
+ _cupsSetError(IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_UPGRADE_REQUIRED :
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_CUPS_PKI_ERROR :
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, httpStatus(status), 0);
+ break;
+
+ case HTTP_STATUS_ERROR :
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ break;
+
+ default :
+ DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
+ "IPP_STATUS_ERROR_SERVICE_UNAVAILABLE!", status));
+ _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, httpStatus(status), 0);
+ break;
+ }
+}
+
+
+/*
+ * End of "$Id: request.c 11867 2014-05-09 20:33:08Z msweet $".
+ */
diff --git a/cups/libs/cups/sidechannel.c b/cups/libs/cups/sidechannel.c
new file mode 100644
index 000000000..cad2a2b40
--- /dev/null
+++ b/cups/libs/cups/sidechannel.c
@@ -0,0 +1,642 @@
+/*
+ * "$Id: sidechannel.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Side-channel API code for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsSideChannelDoRequest() - Send a side-channel command to a backend and
+ * wait for a response.
+ * cupsSideChannelRead() - Read a side-channel message.
+ * cupsSideChannelSNMPGet() - Query a SNMP OID's value.
+ * cupsSideChannelSNMPWalk() - Query multiple SNMP OID values.
+ * cupsSideChannelWrite() - Write a side-channel message.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "sidechannel.h"
+#include "cups-private.h"
+#ifdef WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 */
+#ifdef __hpux
+# include <sys/time.h>
+#elif !defined(WIN32)
+# include <sys/select.h>
+#endif /* __hpux */
+#ifndef WIN32
+# include <sys/time.h>
+#endif /* !WIN32 */
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif /* HAVE_POLL */
+
+
+/*
+ * Buffer size for side-channel requests...
+ */
+
+#define _CUPS_SC_MAX_DATA 65535
+#define _CUPS_SC_MAX_BUFFER 65540
+
+
+/*
+ * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
+ *
+ * This function is normally only called by filters, drivers, or port
+ * monitors in order to communicate with the backend used by the current
+ * printer. Programs must be prepared to handle timeout or "not
+ * implemented" status codes, which indicate that the backend or device
+ * do not support the specified side-channel command.
+ *
+ * The "datalen" parameter must be initialized to the size of the buffer
+ * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
+ * update the value to contain the number of data bytes in the buffer.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+cups_sc_status_t /* O - Status of command */
+cupsSideChannelDoRequest(
+ cups_sc_command_t command, /* I - Command to send */
+ char *data, /* O - Response data buffer pointer */
+ int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
+ double timeout) /* I - Timeout in seconds */
+{
+ cups_sc_status_t status; /* Status of command */
+ cups_sc_command_t rcommand; /* Response command */
+
+
+ if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ if (rcommand != command)
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+ return (status);
+}
+
+
+/*
+ * 'cupsSideChannelRead()' - Read a side-channel message.
+ *
+ * This function is normally only called by backend programs to read
+ * commands from a filter, driver, or port monitor program. The
+ * caller must be prepared to handle incomplete or invalid messages
+ * and return the corresponding status codes.
+ *
+ * The "datalen" parameter must be initialized to the size of the buffer
+ * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
+ * update the value to contain the number of data bytes in the buffer.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsSideChannelRead(
+ cups_sc_command_t *command, /* O - Command code */
+ cups_sc_status_t *status, /* O - Status code */
+ char *data, /* O - Data buffer pointer */
+ int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
+ double timeout) /* I - Timeout in seconds */
+{
+ char *buffer; /* Message buffer */
+ int bytes; /* Bytes read */
+ int templen; /* Data length from message */
+ int nfds; /* Number of file descriptors */
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Poll structure for poll() */
+#else /* select() */
+ fd_set input_set; /* Input set for select() */
+ struct timeval stimeout; /* Timeout value for select() */
+#endif /* HAVE_POLL */
+
+
+ DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
+ "datalen=%p(%d), timeout=%.3f)", command, status, data,
+ datalen, datalen ? *datalen : -1, timeout));
+
+ /*
+ * Range check input...
+ */
+
+ if (!command || !status)
+ return (-1);
+
+ /*
+ * See if we have pending data on the side-channel socket...
+ */
+
+#ifdef HAVE_POLL
+ pfd.fd = CUPS_SC_FD;
+ pfd.events = POLLIN;
+
+ while ((nfds = poll(&pfd, 1,
+ timeout < 0.0 ? -1 : (long)(timeout * 1000))) < 0 &&
+ (errno == EINTR || errno == EAGAIN))
+ ;
+
+#else /* select() */
+ FD_ZERO(&input_set);
+ FD_SET(CUPS_SC_FD, &input_set);
+
+ stimeout.tv_sec = (int)timeout;
+ stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
+
+ while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
+ timeout < 0.0 ? NULL : &stimeout)) < 0 &&
+ (errno == EINTR || errno == EAGAIN))
+ ;
+
+#endif /* HAVE_POLL */
+
+ if (nfds < 1)
+ {
+ *command = CUPS_SC_CMD_NONE;
+ *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
+ return (-1);
+ }
+
+ /*
+ * Read a side-channel message for the format:
+ *
+ * Byte(s) Description
+ * ------- -------------------------------------------
+ * 0 Command code
+ * 1 Status code
+ * 2-3 Data length (network byte order)
+ * 4-N Data
+ */
+
+ if ((buffer = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
+ {
+ *command = CUPS_SC_CMD_NONE;
+ *status = CUPS_SC_STATUS_TOO_BIG;
+
+ return (-1);
+ }
+
+ while ((bytes = read(CUPS_SC_FD, buffer, _CUPS_SC_MAX_BUFFER)) < 0)
+ if (errno != EINTR && errno != EAGAIN)
+ {
+ DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
+
+ _cupsBufferRelease(buffer);
+
+ *command = CUPS_SC_CMD_NONE;
+ *status = CUPS_SC_STATUS_IO_ERROR;
+
+ return (-1);
+ }
+
+ /*
+ * Watch for EOF or too few bytes...
+ */
+
+ if (bytes < 4)
+ {
+ DEBUG_printf(("1cupsSideChannelRead: Short read of %d bytes", bytes));
+
+ _cupsBufferRelease(buffer);
+
+ *command = CUPS_SC_CMD_NONE;
+ *status = CUPS_SC_STATUS_BAD_MESSAGE;
+
+ return (-1);
+ }
+
+ /*
+ * Validate the command code in the message...
+ */
+
+ if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
+ buffer[0] >= CUPS_SC_CMD_MAX)
+ {
+ DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
+
+ _cupsBufferRelease(buffer);
+
+ *command = CUPS_SC_CMD_NONE;
+ *status = CUPS_SC_STATUS_BAD_MESSAGE;
+
+ return (-1);
+ }
+
+ *command = (cups_sc_command_t)buffer[0];
+
+ /*
+ * Validate the data length in the message...
+ */
+
+ templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255);
+
+ if (templen > 0 && (!data || !datalen))
+ {
+ /*
+ * Either the response is bigger than the provided buffer or the
+ * response is bigger than we've read...
+ */
+
+ *status = CUPS_SC_STATUS_TOO_BIG;
+ }
+ else if (!datalen || templen > *datalen || templen > (bytes - 4))
+ {
+ /*
+ * Either the response is bigger than the provided buffer or the
+ * response is bigger than we've read...
+ */
+
+ *status = CUPS_SC_STATUS_TOO_BIG;
+ }
+ else
+ {
+ /*
+ * The response data will fit, copy it over and provide the actual
+ * length...
+ */
+
+ *status = (cups_sc_status_t)buffer[1];
+ *datalen = templen;
+
+ memcpy(data, buffer + 4, templen);
+ }
+
+ _cupsBufferRelease(buffer);
+
+ DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
+
+ return (0);
+}
+
+
+/*
+ * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
+ *
+ * This function asks the backend to do a SNMP OID query on behalf of the
+ * filter, port monitor, or backend using the default community name.
+ *
+ * "oid" contains a numeric OID consisting of integers separated by periods,
+ * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
+ * supported and must be converted to their numeric forms.
+ *
+ * On input, "data" and "datalen" provide the location and size of the
+ * buffer to hold the OID value as a string. HEX-String (binary) values are
+ * converted to hexadecimal strings representing the binary data, while
+ * NULL-Value and unknown OID types are returned as the empty string.
+ * The returned "datalen" does not include the trailing nul.
+ *
+ * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
+ * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
+ * the printer does not respond to the SNMP query.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+cups_sc_status_t /* O - Query status */
+cupsSideChannelSNMPGet(
+ const char *oid, /* I - OID to query */
+ char *data, /* I - Buffer for OID value */
+ int *datalen, /* IO - Size of OID buffer on entry, size of value on return */
+ double timeout) /* I - Timeout in seconds */
+{
+ cups_sc_status_t status; /* Status of command */
+ cups_sc_command_t rcommand; /* Response command */
+ char *real_data; /* Real data buffer for response */
+ int real_datalen, /* Real length of data buffer */
+ real_oidlen; /* Length of returned OID string */
+
+
+ DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
+ "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
+ timeout));
+
+ /*
+ * Range check input...
+ */
+
+ if (!oid || !*oid || !data || !datalen || *datalen < 2)
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+ *data = '\0';
+
+ /*
+ * Send the request to the backend and wait for a response...
+ */
+
+ if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid,
+ (int)strlen(oid) + 1, timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
+ return (CUPS_SC_STATUS_TOO_BIG);
+
+ real_datalen = _CUPS_SC_MAX_BUFFER;
+ if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
+ {
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_TIMEOUT);
+ }
+
+ if (rcommand != CUPS_SC_CMD_SNMP_GET)
+ {
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+ }
+
+ if (status == CUPS_SC_STATUS_OK)
+ {
+ /*
+ * Parse the response of the form "oid\0value"...
+ */
+
+ real_oidlen = strlen(real_data) + 1;
+ real_datalen -= real_oidlen;
+
+ if ((real_datalen + 1) > *datalen)
+ {
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_TOO_BIG);
+ }
+
+ memcpy(data, real_data + real_oidlen, real_datalen);
+ data[real_datalen] = '\0';
+
+ *datalen = real_datalen;
+ }
+
+ _cupsBufferRelease(real_data);
+
+ return (status);
+}
+
+
+/*
+ * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
+ *
+ * This function asks the backend to do multiple SNMP OID queries on behalf
+ * of the filter, port monitor, or backend using the default community name.
+ * All OIDs under the "parent" OID are queried and the results are sent to
+ * the callback function you provide.
+ *
+ * "oid" contains a numeric OID consisting of integers separated by periods,
+ * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
+ * supported and must be converted to their numeric forms.
+ *
+ * "timeout" specifies the timeout for each OID query. The total amount of
+ * time will depend on the number of OID values found and the time required
+ * for each query.
+ *
+ * "cb" provides a function to call for every value that is found. "context"
+ * is an application-defined pointer that is sent to the callback function
+ * along with the OID and current data. The data passed to the callback is the
+ * same as returned by @link cupsSideChannelSNMPGet@.
+ *
+ * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
+ * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
+ * the printer does not respond to the first SNMP query.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+cups_sc_status_t /* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
+cupsSideChannelSNMPWalk(
+ const char *oid, /* I - First numeric OID to query */
+ double timeout, /* I - Timeout for each query in seconds */
+ cups_sc_walk_func_t cb, /* I - Function to call with each value */
+ void *context) /* I - Application-defined pointer to send to callback */
+{
+ cups_sc_status_t status; /* Status of command */
+ cups_sc_command_t rcommand; /* Response command */
+ char *real_data; /* Real data buffer for response */
+ int real_datalen, /* Real length of data buffer */
+ real_oidlen, /* Length of returned OID string */
+ oidlen; /* Length of first OID */
+ const char *current_oid; /* Current OID */
+ char last_oid[2048]; /* Last OID */
+
+
+ DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
+ "context=%p)", oid, timeout, cb, context));
+
+ /*
+ * Range check input...
+ */
+
+ if (!oid || !*oid || !cb)
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+ if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
+ return (CUPS_SC_STATUS_TOO_BIG);
+
+ /*
+ * Loop until the OIDs don't match...
+ */
+
+ current_oid = oid;
+ oidlen = (int)strlen(oid);
+ last_oid[0] = '\0';
+
+ do
+ {
+ /*
+ * Send the request to the backend and wait for a response...
+ */
+
+ if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE,
+ current_oid, (int)strlen(current_oid) + 1, timeout))
+ {
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_TIMEOUT);
+ }
+
+ real_datalen = _CUPS_SC_MAX_BUFFER;
+ if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
+ timeout))
+ {
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_TIMEOUT);
+ }
+
+ if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
+ {
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+ }
+
+ if (status == CUPS_SC_STATUS_OK)
+ {
+ /*
+ * Parse the response of the form "oid\0value"...
+ */
+
+ if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
+ !strcmp(real_data, last_oid))
+ {
+ /*
+ * Done with this set of OIDs...
+ */
+
+ _cupsBufferRelease(real_data);
+ return (CUPS_SC_STATUS_OK);
+ }
+
+ if (real_datalen < sizeof(real_data))
+ real_data[real_datalen] = '\0';
+
+ real_oidlen = strlen(real_data) + 1;
+ real_datalen -= real_oidlen;
+
+ /*
+ * Call the callback with the OID and data...
+ */
+
+ (*cb)(real_data, real_data + real_oidlen, real_datalen, context);
+
+ /*
+ * Update the current OID...
+ */
+
+ current_oid = real_data;
+ strlcpy(last_oid, current_oid, sizeof(last_oid));
+ }
+ }
+ while (status == CUPS_SC_STATUS_OK);
+
+ _cupsBufferRelease(real_data);
+
+ return (status);
+}
+
+
+/*
+ * 'cupsSideChannelWrite()' - Write a side-channel message.
+ *
+ * This function is normally only called by backend programs to send
+ * responses to a filter, driver, or port monitor program.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on error */
+cupsSideChannelWrite(
+ cups_sc_command_t command, /* I - Command code */
+ cups_sc_status_t status, /* I - Status code */
+ const char *data, /* I - Data buffer pointer */
+ int datalen, /* I - Number of bytes of data */
+ double timeout) /* I - Timeout in seconds */
+{
+ char *buffer; /* Message buffer */
+ int bytes; /* Bytes written */
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Poll structure for poll() */
+#else /* select() */
+ fd_set output_set; /* Output set for select() */
+ struct timeval stimeout; /* Timeout value for select() */
+#endif /* HAVE_POLL */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
+ datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
+ return (-1);
+
+ /*
+ * See if we can safely write to the side-channel socket...
+ */
+
+#ifdef HAVE_POLL
+ pfd.fd = CUPS_SC_FD;
+ pfd.events = POLLOUT;
+
+ if (timeout < 0.0)
+ {
+ if (poll(&pfd, 1, -1) < 1)
+ return (-1);
+ }
+ else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1)
+ return (-1);
+
+#else /* select() */
+ FD_ZERO(&output_set);
+ FD_SET(CUPS_SC_FD, &output_set);
+
+ if (timeout < 0.0)
+ {
+ if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, NULL) < 1)
+ return (-1);
+ }
+ else
+ {
+ stimeout.tv_sec = (int)timeout;
+ stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
+
+ if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, &stimeout) < 1)
+ return (-1);
+ }
+#endif /* HAVE_POLL */
+
+ /*
+ * Write a side-channel message in the format:
+ *
+ * Byte(s) Description
+ * ------- -------------------------------------------
+ * 0 Command code
+ * 1 Status code
+ * 2-3 Data length (network byte order) <= 16384
+ * 4-N Data
+ */
+
+ if ((buffer = _cupsBufferGet(datalen + 4)) == NULL)
+ return (-1);
+
+ buffer[0] = command;
+ buffer[1] = status;
+ buffer[2] = datalen >> 8;
+ buffer[3] = datalen & 255;
+
+ bytes = 4;
+
+ if (datalen > 0)
+ {
+ memcpy(buffer + 4, data, datalen);
+ bytes += datalen;
+ }
+
+ while (write(CUPS_SC_FD, buffer, bytes) < 0)
+ if (errno != EINTR && errno != EAGAIN)
+ {
+ _cupsBufferRelease(buffer);
+ return (-1);
+ }
+
+ _cupsBufferRelease(buffer);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: sidechannel.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/sidechannel.h b/cups/libs/cups/sidechannel.h
new file mode 100644
index 000000000..4694bb974
--- /dev/null
+++ b/cups/libs/cups/sidechannel.h
@@ -0,0 +1,147 @@
+/*
+ * "$Id: sidechannel.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Side-channel API definitions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_SIDECHANNEL_H_
+# define _CUPS_SIDECHANNEL_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "versioning.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+#define CUPS_SC_FD 4 /* File descriptor for select/poll */
+
+
+/*
+ * Enumerations...
+ */
+
+enum cups_sc_bidi_e /**** Bidirectional capability values ****/
+{
+ CUPS_SC_BIDI_NOT_SUPPORTED = 0, /* Bidirectional I/O is not supported */
+ CUPS_SC_BIDI_SUPPORTED = 1 /* Bidirectional I/O is supported */
+};
+typedef enum cups_sc_bidi_e cups_sc_bidi_t;
+ /**** Bidirectional capabilities ****/
+
+enum cups_sc_command_e /**** Request command codes ****/
+{
+ CUPS_SC_CMD_NONE = 0, /* No command @private@ */
+ CUPS_SC_CMD_SOFT_RESET = 1, /* Do a soft reset */
+ CUPS_SC_CMD_DRAIN_OUTPUT = 2, /* Drain all pending output */
+ CUPS_SC_CMD_GET_BIDI = 3, /* Return bidirectional capabilities */
+ CUPS_SC_CMD_GET_DEVICE_ID = 4, /* Return the IEEE-1284 device ID */
+ CUPS_SC_CMD_GET_STATE = 5, /* Return the device state */
+ CUPS_SC_CMD_SNMP_GET = 6, /* Query an SNMP OID @since CUPS 1.4/OS X 10.6@ */
+ CUPS_SC_CMD_SNMP_GET_NEXT = 7, /* Query the next SNMP OID @since CUPS 1.4/OS X 10.6@ */
+ CUPS_SC_CMD_GET_CONNECTED = 8, /* Return whether the backend is "connected" to the printer @since CUPS 1.5/OS X 10.7@ */
+ CUPS_SC_CMD_MAX /* End of valid values @private@ */
+};
+typedef enum cups_sc_command_e cups_sc_command_t;
+ /**** Request command codes ****/
+
+enum cups_sc_connected_e /**** Connectivity values ****/
+{
+ CUPS_SC_NOT_CONNECTED = 0, /* Backend is not "connected" to printer */
+ CUPS_SC_CONNECTED = 1 /* Backend is "connected" to printer */
+};
+typedef enum cups_sc_connected_e cups_sc_connected_t;
+ /**** Connectivity values ****/
+
+
+enum cups_sc_state_e /**** Printer state bits ****/
+{
+ CUPS_SC_STATE_OFFLINE = 0, /* Device is offline */
+ CUPS_SC_STATE_ONLINE = 1, /* Device is online */
+ CUPS_SC_STATE_BUSY = 2, /* Device is busy */
+ CUPS_SC_STATE_ERROR = 4, /* Other error condition */
+ CUPS_SC_STATE_MEDIA_LOW = 16, /* Paper low condition */
+ CUPS_SC_STATE_MEDIA_EMPTY = 32, /* Paper out condition */
+ CUPS_SC_STATE_MARKER_LOW = 64, /* Toner/ink low condition */
+ CUPS_SC_STATE_MARKER_EMPTY = 128 /* Toner/ink out condition */
+};
+typedef enum cups_sc_state_e cups_sc_state_t;
+ /**** Printer state bits ****/
+
+enum cups_sc_status_e /**** Response status codes ****/
+{
+ CUPS_SC_STATUS_NONE, /* No status */
+ CUPS_SC_STATUS_OK, /* Operation succeeded */
+ CUPS_SC_STATUS_IO_ERROR, /* An I/O error occurred */
+ CUPS_SC_STATUS_TIMEOUT, /* The backend did not respond */
+ CUPS_SC_STATUS_NO_RESPONSE, /* The device did not respond */
+ CUPS_SC_STATUS_BAD_MESSAGE, /* The command/response message was invalid */
+ CUPS_SC_STATUS_TOO_BIG, /* Response too big */
+ CUPS_SC_STATUS_NOT_IMPLEMENTED /* Command not implemented */
+};
+typedef enum cups_sc_status_e cups_sc_status_t;
+ /**** Response status codes ****/
+
+typedef void (*cups_sc_walk_func_t)(const char *oid, const char *data,
+ int datalen, void *context);
+ /**** SNMP walk callback ****/
+
+
+/*
+ * Prototypes...
+ */
+
+extern cups_sc_status_t cupsSideChannelDoRequest(cups_sc_command_t command,
+ char *data, int *datalen,
+ double timeout) _CUPS_API_1_3;
+extern int cupsSideChannelRead(cups_sc_command_t *command,
+ cups_sc_status_t *status,
+ char *data, int *datalen,
+ double timeout) _CUPS_API_1_3;
+extern int cupsSideChannelWrite(cups_sc_command_t command,
+ cups_sc_status_t status,
+ const char *data, int datalen,
+ double timeout) _CUPS_API_1_3;
+
+/**** New in CUPS 1.4 ****/
+extern cups_sc_status_t cupsSideChannelSNMPGet(const char *oid, char *data,
+ int *datalen, double timeout)
+ _CUPS_API_1_4;
+extern cups_sc_status_t cupsSideChannelSNMPWalk(const char *oid, double timeout,
+ cups_sc_walk_func_t cb,
+ void *context) _CUPS_API_1_4;
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_SIDECHANNEL_H_ */
+
+/*
+ * End of "$Id: sidechannel.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/snmp-private.h b/cups/libs/cups/snmp-private.h
new file mode 100644
index 000000000..9661243b7
--- /dev/null
+++ b/cups/libs/cups/snmp-private.h
@@ -0,0 +1,146 @@
+/*
+ * "$Id: snmp-private.h 3794 2012-04-23 22:44:16Z msweet $"
+ *
+ * Private SNMP definitions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2006-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * "LICENSE" 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.
+ */
+
+#ifndef _CUPS_SNMP_PRIVATE_H_
+# define _CUPS_SNMP_PRIVATE_H_
+
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/http.h>
+
+
+/*
+ * Constants...
+ */
+
+#define CUPS_SNMP_PORT 161 /* SNMP well-known port */
+#define CUPS_SNMP_MAX_COMMUNITY 512 /* Maximum size of community name */
+#define CUPS_SNMP_MAX_OID 128 /* Maximum number of OID numbers */
+#define CUPS_SNMP_MAX_PACKET 1472 /* Maximum size of SNMP packet */
+#define CUPS_SNMP_MAX_STRING 1024 /* Maximum size of string */
+#define CUPS_SNMP_VERSION_1 0 /* SNMPv1 */
+
+
+/*
+ * Types...
+ */
+
+enum cups_asn1_e /**** ASN1 request/object types ****/
+{
+ CUPS_ASN1_END_OF_CONTENTS = 0x00, /* End-of-contents */
+ CUPS_ASN1_BOOLEAN = 0x01, /* BOOLEAN */
+ CUPS_ASN1_INTEGER = 0x02, /* INTEGER or ENUMERATION */
+ CUPS_ASN1_BIT_STRING = 0x03, /* BIT STRING */
+ CUPS_ASN1_OCTET_STRING = 0x04, /* OCTET STRING */
+ CUPS_ASN1_NULL_VALUE = 0x05, /* NULL VALUE */
+ CUPS_ASN1_OID = 0x06, /* OBJECT IDENTIFIER */
+ CUPS_ASN1_SEQUENCE = 0x30, /* SEQUENCE */
+ CUPS_ASN1_HEX_STRING = 0x40, /* Binary string aka Hex-STRING */
+ CUPS_ASN1_COUNTER = 0x41, /* 32-bit unsigned aka Counter32 */
+ CUPS_ASN1_GAUGE = 0x42, /* 32-bit unsigned aka Gauge32 */
+ CUPS_ASN1_TIMETICKS = 0x43, /* 32-bit unsigned aka Timeticks32 */
+ CUPS_ASN1_GET_REQUEST = 0xa0, /* GetRequest-PDU */
+ CUPS_ASN1_GET_NEXT_REQUEST = 0xa1, /* GetNextRequest-PDU */
+ CUPS_ASN1_GET_RESPONSE = 0xa2 /* GetResponse-PDU */
+};
+typedef enum cups_asn1_e cups_asn1_t; /**** ASN1 request/object types ****/
+
+typedef struct cups_snmp_string_s /**** String value ****/
+{
+ unsigned char bytes[CUPS_SNMP_MAX_STRING];
+ /* Bytes in string */
+ int num_bytes; /* Number of bytes */
+} cups_snmp_string_t;
+
+union cups_snmp_value_u /**** Object value ****/
+{
+ int boolean; /* Boolean value */
+ int integer; /* Integer value */
+ unsigned counter; /* Counter value */
+ unsigned gauge; /* Gauge value */
+ unsigned timeticks; /* Timeticks value */
+ int oid[CUPS_SNMP_MAX_OID]; /* OID value */
+ cups_snmp_string_t string; /* String value */
+};
+
+typedef struct cups_snmp_s /**** SNMP data packet ****/
+{
+ const char *error; /* Encode/decode error */
+ http_addr_t address; /* Source address */
+ int version; /* Version number */
+ char community[CUPS_SNMP_MAX_COMMUNITY];
+ /* Community name */
+ cups_asn1_t request_type; /* Request type */
+ int request_id; /* request-id value */
+ int error_status; /* error-status value */
+ int error_index; /* error-index value */
+ int object_name[CUPS_SNMP_MAX_OID];
+ /* object-name value */
+ cups_asn1_t object_type; /* object-value type */
+ union cups_snmp_value_u
+ object_value; /* object-value value */
+} cups_snmp_t;
+
+typedef void (*cups_snmp_cb_t)(cups_snmp_t *packet, void *data);
+
+/*
+ * Prototypes...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+extern void _cupsSNMPClose(int fd) _CUPS_API_1_4;
+extern int *_cupsSNMPCopyOID(int *dst, const int *src, int dstsize)
+ _CUPS_API_1_4;
+extern const char *_cupsSNMPDefaultCommunity(void) _CUPS_API_1_4;
+extern int _cupsSNMPIsOID(cups_snmp_t *packet, const int *oid)
+ _CUPS_API_1_4;
+extern int _cupsSNMPIsOIDPrefixed(cups_snmp_t *packet,
+ const int *prefix) _CUPS_API_1_4;
+extern char *_cupsSNMPOIDToString(const int *src, char *dst,
+ size_t dstsize) _CUPS_API_1_4;
+extern int _cupsSNMPOpen(int family) _CUPS_API_1_4;
+extern cups_snmp_t *_cupsSNMPRead(int fd, cups_snmp_t *packet,
+ double timeout) _CUPS_API_1_4;
+extern void _cupsSNMPSetDebug(int level) _CUPS_API_1_4;
+extern int *_cupsSNMPStringToOID(const char *src,
+ int *dst, int dstsize)
+ _CUPS_API_1_4;
+extern int _cupsSNMPWalk(int fd, http_addr_t *address, int version,
+ const char *community, const int *prefix,
+ double timeout, cups_snmp_cb_t cb,
+ void *data) _CUPS_API_1_4;
+extern int _cupsSNMPWrite(int fd, http_addr_t *address, int version,
+ const char *community,
+ cups_asn1_t request_type,
+ const unsigned request_id,
+ const int *oid) _CUPS_API_1_4;
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_SNMP_PRIVATE_H_ */
+
+
+/*
+ * End of "$Id: snmp-private.h 3794 2012-04-23 22:44:16Z msweet $".
+ */
diff --git a/cups/libs/cups/snmp.c b/cups/libs/cups/snmp.c
new file mode 100644
index 000000000..b50d3d57a
--- /dev/null
+++ b/cups/libs/cups/snmp.c
@@ -0,0 +1,1733 @@
+/*
+ * "$Id: snmp.c 4167 2013-02-04 19:27:13Z msweet $"
+ *
+ * SNMP functions for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 2006-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * "LICENSE" 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.
+ *
+ * Contents:
+ *
+ * _cupsSNMPClose() - Close a SNMP socket.
+ * _cupsSNMPCopyOID() - Copy an OID.
+ * _cupsSNMPDefaultCommunity() - Get the default SNMP community name.
+ * _cupsSNMPIsOID() - Test whether a SNMP response contains the
+ * specified OID.
+ * _cupsSNMPIsOIDPrefixed() - Test whether a SNMP response uses the
+ * specified OID prefix.
+ * _cupsSNMPOIDToString() - Convert an OID to a string.
+ * _cupsSNMPOpen() - Open a SNMP socket.
+ * _cupsSNMPRead() - Read and parse a SNMP response.
+ * _cupsSNMPSetDebug() - Enable/disable debug logging to stderr.
+ * _cupsSNMPStringToOID() - Convert a numeric OID string to an OID array.
+ * _cupsSNMPWalk() - Enumerate a group of OIDs.
+ * _cupsSNMPWrite() - Send an SNMP query packet.
+ * asn1_debug() - Decode an ASN1-encoded message.
+ * asn1_decode_snmp() - Decode a SNMP packet.
+ * asn1_encode_snmp() - Encode a SNMP packet.
+ * asn1_get_integer() - Get an integer value.
+ * asn1_get_length() - Get a value length.
+ * asn1_get_oid() - Get an OID value.
+ * asn1_get_packed() - Get a packed integer value.
+ * asn1_get_string() - Get a string value.
+ * asn1_get_type() - Get a value type.
+ * asn1_set_integer() - Set an integer value.
+ * asn1_set_length() - Set a value length.
+ * asn1_set_oid() - Set an OID value.
+ * asn1_set_packed() - Set a packed integer value.
+ * asn1_size_integer() - Figure out the number of bytes needed for an
+ * integer value.
+ * asn1_size_length() - Figure out the number of bytes needed for a
+ * length value.
+ * asn1_size_oid() - Figure out the numebr of bytes needed for an
+ * OID value.
+ * asn1_size_packed() - Figure out the number of bytes needed for a
+ * packed integer value.
+ * snmp_set_error() - Set the localized error for a packet.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "cups-private.h"
+#include "snmp-private.h"
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif /* HAVE_POLL */
+
+
+/*
+ * Local functions...
+ */
+
+static void asn1_debug(const char *prefix, unsigned char *buffer,
+ size_t len, int indent);
+static int asn1_decode_snmp(unsigned char *buffer, size_t len,
+ cups_snmp_t *packet);
+static int asn1_encode_snmp(unsigned char *buffer, size_t len,
+ cups_snmp_t *packet);
+static int asn1_get_integer(unsigned char **buffer,
+ unsigned char *bufend,
+ int length);
+static int asn1_get_oid(unsigned char **buffer,
+ unsigned char *bufend,
+ int length, int *oid, int oidsize);
+static int asn1_get_packed(unsigned char **buffer,
+ unsigned char *bufend);
+static char *asn1_get_string(unsigned char **buffer,
+ unsigned char *bufend,
+ int length, char *string,
+ int strsize);
+static unsigned asn1_get_length(unsigned char **buffer,
+ unsigned char *bufend);
+static int asn1_get_type(unsigned char **buffer,
+ unsigned char *bufend);
+static void asn1_set_integer(unsigned char **buffer,
+ int integer);
+static void asn1_set_length(unsigned char **buffer,
+ unsigned length);
+static void asn1_set_oid(unsigned char **buffer,
+ const int *oid);
+static void asn1_set_packed(unsigned char **buffer,
+ int integer);
+static int asn1_size_integer(int integer);
+static int asn1_size_length(int length);
+static int asn1_size_oid(const int *oid);
+static int asn1_size_packed(int integer);
+static void snmp_set_error(cups_snmp_t *packet,
+ const char *message);
+
+
+/*
+ * '_cupsSNMPClose()' - Close a SNMP socket.
+ */
+
+void
+_cupsSNMPClose(int fd) /* I - SNMP socket file descriptor */
+{
+ DEBUG_printf(("4_cupsSNMPClose(fd=%d)", fd));
+
+#ifdef WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif /* WIN32 */
+}
+
+
+/*
+ * '_cupsSNMPCopyOID()' - Copy an OID.
+ *
+ * The array pointed to by "src" is terminated by the value -1.
+ */
+
+int * /* O - New OID */
+_cupsSNMPCopyOID(int *dst, /* I - Destination OID */
+ const int *src, /* I - Source OID */
+ int dstsize) /* I - Number of integers in dst */
+{
+ int i; /* Looping var */
+
+
+ DEBUG_printf(("4_cupsSNMPCopyOID(dst=%p, src=%p, dstsize=%d)", dst, src,
+ dstsize));
+
+ for (i = 0, dstsize --; src[i] >= 0 && i < dstsize; i ++)
+ dst[i] = src[i];
+
+ dst[i] = -1;
+
+ return (dst);
+}
+
+
+/*
+ * '_cupsSNMPDefaultCommunity()' - Get the default SNMP community name.
+ *
+ * The default community name is the first community name found in the
+ * snmp.conf file. If no community name is defined there, "public" is used.
+ */
+
+const char * /* O - Default community name */
+_cupsSNMPDefaultCommunity(void)
+{
+ cups_file_t *fp; /* snmp.conf file */
+ char line[1024], /* Line from file */
+ *value; /* Value from file */
+ int linenum; /* Line number in file */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ DEBUG_puts("4_cupsSNMPDefaultCommunity()");
+
+ if (!cg->snmp_community[0])
+ {
+ strlcpy(cg->snmp_community, "public", sizeof(cg->snmp_community));
+
+ snprintf(line, sizeof(line), "%s/snmp.conf", cg->cups_serverroot);
+ if ((fp = cupsFileOpen(line, "r")) != NULL)
+ {
+ linenum = 0;
+ while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ if (!_cups_strcasecmp(line, "Community") && value)
+ {
+ strlcpy(cg->snmp_community, value, sizeof(cg->snmp_community));
+ break;
+ }
+
+ cupsFileClose(fp);
+ }
+ }
+
+ DEBUG_printf(("5_cupsSNMPDefaultCommunity: Returning \"%s\"",
+ cg->snmp_community));
+
+ return (cg->snmp_community);
+}
+
+
+/*
+ * '_cupsSNMPIsOID()' - Test whether a SNMP response contains the specified OID.
+ *
+ * The array pointed to by "oid" is terminated by the value -1.
+ */
+
+int /* O - 1 if equal, 0 if not equal */
+_cupsSNMPIsOID(cups_snmp_t *packet, /* I - Response packet */
+ const int *oid) /* I - OID */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("4_cupsSNMPIsOID(packet=%p, oid=%p)", packet, oid));
+
+ if (!packet || !oid)
+ {
+ DEBUG_puts("5_cupsSNMPIsOID: Returning 0");
+
+ return (0);
+ }
+
+ /*
+ * Compare OIDs...
+ */
+
+ for (i = 0;
+ i < CUPS_SNMP_MAX_OID && oid[i] >= 0 && packet->object_name[i] >= 0;
+ i ++)
+ if (oid[i] != packet->object_name[i])
+ {
+ DEBUG_puts("5_cupsSNMPIsOID: Returning 0");
+
+ return (0);
+ }
+
+ DEBUG_printf(("5_cupsSNMPIsOID: Returning %d",
+ i < CUPS_SNMP_MAX_OID && oid[i] == packet->object_name[i]));
+
+ return (i < CUPS_SNMP_MAX_OID && oid[i] == packet->object_name[i]);
+}
+
+
+/*
+ * '_cupsSNMPIsOIDPrefixed()' - Test whether a SNMP response uses the specified
+ * OID prefix.
+ *
+ * The array pointed to by "prefix" is terminated by the value -1.
+ */
+
+int /* O - 1 if prefixed, 0 if not prefixed */
+_cupsSNMPIsOIDPrefixed(
+ cups_snmp_t *packet, /* I - Response packet */
+ const int *prefix) /* I - OID prefix */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("4_cupsSNMPIsOIDPrefixed(packet=%p, prefix=%p)", packet,
+ prefix));
+
+ if (!packet || !prefix)
+ {
+ DEBUG_puts("5_cupsSNMPIsOIDPrefixed: Returning 0");
+
+ return (0);
+ }
+
+ /*
+ * Compare OIDs...
+ */
+
+ for (i = 0;
+ i < CUPS_SNMP_MAX_OID && prefix[i] >= 0 && packet->object_name[i] >= 0;
+ i ++)
+ if (prefix[i] != packet->object_name[i])
+ {
+ DEBUG_puts("5_cupsSNMPIsOIDPrefixed: Returning 0");
+
+ return (0);
+ }
+
+ DEBUG_printf(("5_cupsSNMPIsOIDPrefixed: Returning %d",
+ i < CUPS_SNMP_MAX_OID));
+
+ return (i < CUPS_SNMP_MAX_OID);
+}
+
+
+/*
+ * '_cupsSNMPOIDToString()' - Convert an OID to a string.
+ */
+
+
+char * /* O - New string or @code NULL@ on error */
+_cupsSNMPOIDToString(const int *src, /* I - OID */
+ char *dst, /* I - String buffer */
+ size_t dstsize) /* I - Size of string buffer */
+{
+ char *dstptr, /* Pointer into string buffer */
+ *dstend; /* End of string buffer */
+
+
+ DEBUG_printf(("4_cupsSNMPOIDToString(src=%p, dst=%p, dstsize=" CUPS_LLFMT ")",
+ src, dst, CUPS_LLCAST dstsize));
+
+ /*
+ * Range check input...
+ */
+
+ if (!src || !dst || dstsize < 4)
+ return (NULL);
+
+ /*
+ * Loop through the OID array and build a string...
+ */
+
+ for (dstptr = dst, dstend = dstptr + dstsize - 1;
+ *src >= 0 && dstptr < dstend;
+ src ++, dstptr += strlen(dstptr))
+ snprintf(dstptr, dstend - dstptr + 1, ".%d", *src);
+
+ if (*src >= 0)
+ return (NULL);
+ else
+ return (dst);
+}
+
+
+/*
+ * '_cupsSNMPOpen()' - Open a SNMP socket.
+ */
+
+int /* O - SNMP socket file descriptor */
+_cupsSNMPOpen(int family) /* I - Address family - @code AF_INET@ or @code AF_INET6@ */
+{
+ int fd; /* SNMP socket file descriptor */
+ int val; /* Socket option value */
+
+
+ /*
+ * Create the SNMP socket...
+ */
+
+ DEBUG_printf(("4_cupsSNMPOpen(family=%d)", family));
+
+ if ((fd = socket(family, SOCK_DGRAM, 0)) < 0)
+ {
+ DEBUG_printf(("5_cupsSNMPOpen: Returning -1 (%s)", strerror(errno)));
+
+ return (-1);
+ }
+
+ /*
+ * Set the "broadcast" flag...
+ */
+
+ val = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, CUPS_SOCAST &val, sizeof(val)))
+ {
+ DEBUG_printf(("5_cupsSNMPOpen: Returning -1 (%s)", strerror(errno)));
+
+ close(fd);
+
+ return (-1);
+ }
+
+ DEBUG_printf(("5_cupsSNMPOpen: Returning %d", fd));
+
+ return (fd);
+}
+
+
+/*
+ * '_cupsSNMPRead()' - Read and parse a SNMP response.
+ *
+ * If "timeout" is negative, @code _cupsSNMPRead@ will wait for a response
+ * indefinitely.
+ */
+
+cups_snmp_t * /* O - SNMP packet or @code NULL@ if none */
+_cupsSNMPRead(int fd, /* I - SNMP socket file descriptor */
+ cups_snmp_t *packet, /* I - SNMP packet buffer */
+ double timeout) /* I - Timeout in seconds */
+{
+ unsigned char buffer[CUPS_SNMP_MAX_PACKET];
+ /* Data packet */
+ int bytes; /* Number of bytes received */
+ socklen_t addrlen; /* Source address length */
+ http_addr_t address; /* Source address */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("4_cupsSNMPRead(fd=%d, packet=%p, timeout=%.1f)", fd, packet,
+ timeout));
+
+ if (fd < 0 || !packet)
+ {
+ DEBUG_puts("5_cupsSNMPRead: Returning NULL");
+
+ return (NULL);
+ }
+
+ /*
+ * Optionally wait for a response...
+ */
+
+ if (timeout >= 0.0)
+ {
+ int ready; /* Data ready on socket? */
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Polled file descriptor */
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+
+ while ((ready = poll(&pfd, 1, (int)(timeout * 1000.0))) < 0 &&
+ (errno == EINTR || errno == EAGAIN));
+
+#else
+ fd_set input_set; /* select() input set */
+ struct timeval stimeout; /* select() timeout */
+
+ do
+ {
+ FD_ZERO(&input_set);
+ FD_SET(fd, &input_set);
+
+ stimeout.tv_sec = (int)timeout;
+ stimeout.tv_usec = (int)((timeout - stimeout.tv_sec) * 1000000);
+
+ ready = select(fd + 1, &input_set, NULL, NULL, &stimeout);
+ }
+# ifdef WIN32
+ while (ready < 0 && WSAGetLastError() == WSAEINTR);
+# else
+ while (ready < 0 && (errno == EINTR || errno == EAGAIN));
+# endif /* WIN32 */
+#endif /* HAVE_POLL */
+
+ /*
+ * If we don't have any data ready, return right away...
+ */
+
+ if (ready <= 0)
+ {
+ DEBUG_puts("5_cupsSNMPRead: Returning NULL (timeout)");
+
+ return (NULL);
+ }
+ }
+
+ /*
+ * Read the response data...
+ */
+
+ addrlen = sizeof(address);
+
+ if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (void *)&address,
+ &addrlen)) < 0)
+ {
+ DEBUG_printf(("5_cupsSNMPRead: Returning NULL (%s)", strerror(errno)));
+
+ return (NULL);
+ }
+
+ /*
+ * Look for the response status code in the SNMP message header...
+ */
+
+ asn1_debug("DEBUG: IN ", buffer, bytes, 0);
+
+ asn1_decode_snmp(buffer, bytes, packet);
+
+ memcpy(&(packet->address), &address, sizeof(packet->address));
+
+ /*
+ * Return decoded data packet...
+ */
+
+ DEBUG_puts("5_cupsSNMPRead: Returning packet");
+
+ return (packet);
+}
+
+
+/*
+ * '_cupsSNMPSetDebug()' - Enable/disable debug logging to stderr.
+ */
+
+void
+_cupsSNMPSetDebug(int level) /* I - 1 to enable debug output, 0 otherwise */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ DEBUG_printf(("4_cupsSNMPSetDebug(level=%d)", level));
+
+ cg->snmp_debug = level;
+}
+
+
+/*
+ * '_cupsSNMPStringToOID()' - Convert a numeric OID string to an OID array.
+ *
+ * This function converts a string of the form ".N.N.N.N.N" to the
+ * corresponding OID array terminated by -1.
+ *
+ * @code NULL@ is returned if the array is not large enough or the string is
+ * not a valid OID number.
+ */
+
+int * /* O - Pointer to OID array or @code NULL@ on error */
+_cupsSNMPStringToOID(const char *src, /* I - OID string */
+ int *dst, /* I - OID array */
+ int dstsize)/* I - Number of integers in OID array */
+{
+ int *dstptr, /* Pointer into OID array */
+ *dstend; /* End of OID array */
+
+
+ DEBUG_printf(("4_cupsSNMPStringToOID(src=\"%s\", dst=%p, dstsize=%d)",
+ src, dst, dstsize));
+
+ /*
+ * Range check input...
+ */
+
+ if (!src || !dst || dstsize < 2)
+ return (NULL);
+
+ /*
+ * Skip leading "."...
+ */
+
+ if (*src == '.')
+ src ++;
+
+ /*
+ * Loop to the end of the string...
+ */
+
+ for (dstend = dst + dstsize - 1, dstptr = dst, *dstptr = 0;
+ *src && dstptr < dstend;
+ src ++)
+ {
+ if (*src == '.')
+ {
+ dstptr ++;
+ *dstptr = 0;
+ }
+ else if (isdigit(*src & 255))
+ *dstptr = *dstptr * 10 + *src - '0';
+ else
+ break;
+ }
+
+ if (*src)
+ return (NULL);
+
+ /*
+ * Terminate the end of the OID array and return...
+ */
+
+ dstptr[1] = -1;
+
+ return (dst);
+}
+
+
+/*
+ * '_cupsSNMPWalk()' - Enumerate a group of OIDs.
+ *
+ * This function queries all of the OIDs with the specified OID prefix,
+ * calling the "cb" function for every response that is received.
+ *
+ * The array pointed to by "prefix" is terminated by the value -1.
+ *
+ * If "timeout" is negative, @code _cupsSNMPWalk@ will wait for a response
+ * indefinitely.
+ */
+
+int /* O - Number of OIDs found or -1 on error */
+_cupsSNMPWalk(int fd, /* I - SNMP socket */
+ http_addr_t *address, /* I - Address to query */
+ int version, /* I - SNMP version */
+ const char *community,/* I - Community name */
+ const int *prefix, /* I - OID prefix */
+ double timeout, /* I - Timeout for each response in seconds */
+ cups_snmp_cb_t cb, /* I - Function to call for each response */
+ void *data) /* I - User data pointer that is passed to the callback function */
+{
+ int count = 0; /* Number of OIDs found */
+ int request_id = 0; /* Current request ID */
+ cups_snmp_t packet; /* Current response packet */
+ int lastoid[CUPS_SNMP_MAX_OID];
+ /* Last OID we got */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("4_cupsSNMPWalk(fd=%d, address=%p, version=%d, "
+ "community=\"%s\", prefix=%p, timeout=%.1f, cb=%p, data=%p)",
+ fd, address, version, community, prefix, timeout, cb, data));
+
+ if (fd < 0 || !address || version != CUPS_SNMP_VERSION_1 || !community ||
+ !prefix || !cb)
+ {
+ DEBUG_puts("5_cupsSNMPWalk: Returning -1");
+
+ return (-1);
+ }
+
+ /*
+ * Copy the OID prefix and then loop until we have no more OIDs...
+ */
+
+ _cupsSNMPCopyOID(packet.object_name, prefix, CUPS_SNMP_MAX_OID);
+ lastoid[0] = -1;
+
+ for (;;)
+ {
+ request_id ++;
+
+ if (!_cupsSNMPWrite(fd, address, version, community,
+ CUPS_ASN1_GET_NEXT_REQUEST, request_id,
+ packet.object_name))
+ {
+ DEBUG_puts("5_cupsSNMPWalk: Returning -1");
+
+ return (-1);
+ }
+
+ if (!_cupsSNMPRead(fd, &packet, timeout))
+ {
+ DEBUG_puts("5_cupsSNMPWalk: Returning -1");
+
+ return (-1);
+ }
+
+ if (!_cupsSNMPIsOIDPrefixed(&packet, prefix) ||
+ _cupsSNMPIsOID(&packet, lastoid))
+ {
+ DEBUG_printf(("5_cupsSNMPWalk: Returning %d", count));
+
+ return (count);
+ }
+
+ if (packet.error || packet.error_status)
+ {
+ DEBUG_printf(("5_cupsSNMPWalk: Returning %d", count > 0 ? count : -1));
+
+ return (count > 0 ? count : -1);
+ }
+
+ _cupsSNMPCopyOID(lastoid, packet.object_name, CUPS_SNMP_MAX_OID);
+
+ count ++;
+
+ (*cb)(&packet, data);
+ }
+}
+
+
+/*
+ * '_cupsSNMPWrite()' - Send an SNMP query packet.
+ *
+ * The array pointed to by "oid" is terminated by the value -1.
+ */
+
+int /* O - 1 on success, 0 on error */
+_cupsSNMPWrite(
+ int fd, /* I - SNMP socket */
+ http_addr_t *address, /* I - Address to send to */
+ int version, /* I - SNMP version */
+ const char *community, /* I - Community name */
+ cups_asn1_t request_type, /* I - Request type */
+ const unsigned request_id, /* I - Request ID */
+ const int *oid) /* I - OID */
+{
+ int i; /* Looping var */
+ cups_snmp_t packet; /* SNMP message packet */
+ unsigned char buffer[CUPS_SNMP_MAX_PACKET];
+ /* SNMP message buffer */
+ int bytes; /* Size of message */
+ http_addr_t temp; /* Copy of address */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("4_cupsSNMPWrite(fd=%d, address=%p, version=%d, "
+ "community=\"%s\", request_type=%d, request_id=%u, oid=%p)",
+ fd, address, version, community, request_type, request_id, oid));
+
+ if (fd < 0 || !address || version != CUPS_SNMP_VERSION_1 || !community ||
+ (request_type != CUPS_ASN1_GET_REQUEST &&
+ request_type != CUPS_ASN1_GET_NEXT_REQUEST) || request_id < 1 || !oid)
+ {
+ DEBUG_puts("5_cupsSNMPWrite: Returning 0 (bad arguments)");
+
+ return (0);
+ }
+
+ /*
+ * Create the SNMP message...
+ */
+
+ memset(&packet, 0, sizeof(packet));
+
+ packet.version = version;
+ packet.request_type = request_type;
+ packet.request_id = request_id;
+ packet.object_type = CUPS_ASN1_NULL_VALUE;
+
+ strlcpy(packet.community, community, sizeof(packet.community));
+
+ for (i = 0; oid[i] >= 0 && i < (CUPS_SNMP_MAX_OID - 1); i ++)
+ packet.object_name[i] = oid[i];
+ packet.object_name[i] = -1;
+
+ if (oid[i] >= 0)
+ {
+ DEBUG_puts("5_cupsSNMPWrite: Returning 0 (OID too big)");
+
+ errno = E2BIG;
+ return (0);
+ }
+
+ bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet);
+
+ if (bytes < 0)
+ {
+ DEBUG_puts("5_cupsSNMPWrite: Returning 0 (request too big)");
+
+ errno = E2BIG;
+ return (0);
+ }
+
+ asn1_debug("DEBUG: OUT ", buffer, bytes, 0);
+
+ /*
+ * Send the message...
+ */
+
+ temp = *address;
+
+ _httpAddrSetPort(&temp, CUPS_SNMP_PORT);
+
+ return (sendto(fd, buffer, bytes, 0, (void *)&temp,
+ httpAddrLength(&temp)) == bytes);
+}
+
+
+/*
+ * 'asn1_debug()' - Decode an ASN1-encoded message.
+ */
+
+static void
+asn1_debug(const char *prefix, /* I - Prefix string */
+ unsigned char *buffer, /* I - Buffer */
+ size_t len, /* I - Length of buffer */
+ int indent) /* I - Indentation */
+{
+ int i; /* Looping var */
+ unsigned char *bufend; /* End of buffer */
+ int integer; /* Number value */
+ int oid[CUPS_SNMP_MAX_OID]; /* OID value */
+ char string[CUPS_SNMP_MAX_STRING];
+ /* String value */
+ unsigned char value_type; /* Type of value */
+ int value_length; /* Length of value */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ if (cg->snmp_debug <= 0)
+ return;
+
+ if (cg->snmp_debug > 1 && indent == 0)
+ {
+ /*
+ * Do a hex dump of the packet...
+ */
+
+ int j;
+
+ fprintf(stderr, "%sHex Dump (%d bytes):\n", prefix, (int)len);
+
+ for (i = 0; i < (int)len; i += 16)
+ {
+ fprintf(stderr, "%s%04x:", prefix, i);
+
+ for (j = 0; j < 16 && (i + j) < (int)len; j ++)
+ {
+ if (j && !(j & 3))
+ fprintf(stderr, " %02x", buffer[i + j]);
+ else
+ fprintf(stderr, " %02x", buffer[i + j]);
+ }
+
+ while (j < 16)
+ {
+ if (j && !(j & 3))
+ fputs(" ", stderr);
+ else
+ fputs(" ", stderr);
+
+ j ++;
+ }
+
+ fputs(" ", stderr);
+
+ for (j = 0; j < 16 && (i + j) < (int)len; j ++)
+ if (buffer[i + j] < ' ' || buffer[i + j] >= 0x7f)
+ putc('.', stderr);
+ else
+ putc(buffer[i + j], stderr);
+
+ putc('\n', stderr);
+ }
+ }
+
+ if (indent == 0)
+ fprintf(stderr, "%sMessage:\n", prefix);
+
+ bufend = buffer + len;
+
+ while (buffer < bufend)
+ {
+ /*
+ * Get value type...
+ */
+
+ value_type = asn1_get_type(&buffer, bufend);
+ value_length = asn1_get_length(&buffer, bufend);
+
+ switch (value_type)
+ {
+ case CUPS_ASN1_BOOLEAN :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "%s%*sBOOLEAN %d bytes %d\n", prefix, indent, "",
+ value_length, integer);
+ break;
+
+ case CUPS_ASN1_INTEGER :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "%s%*sINTEGER %d bytes %d\n", prefix, indent, "",
+ value_length, integer);
+ break;
+
+ case CUPS_ASN1_COUNTER :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "%s%*sCOUNTER %d bytes %u\n", prefix, indent, "",
+ value_length, (unsigned)integer);
+ break;
+
+ case CUPS_ASN1_GAUGE :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "%s%*sGAUGE %d bytes %u\n", prefix, indent, "",
+ value_length, (unsigned)integer);
+ break;
+
+ case CUPS_ASN1_TIMETICKS :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "%s%*sTIMETICKS %d bytes %u\n", prefix, indent, "",
+ value_length, (unsigned)integer);
+ break;
+
+ case CUPS_ASN1_OCTET_STRING :
+ fprintf(stderr, "%s%*sOCTET STRING %d bytes \"%s\"\n", prefix,
+ indent, "", value_length,
+ asn1_get_string(&buffer, bufend, value_length, string,
+ sizeof(string)));
+ break;
+
+ case CUPS_ASN1_HEX_STRING :
+ asn1_get_string(&buffer, bufend, value_length, string,
+ sizeof(string));
+ fprintf(stderr, "%s%*sHex-STRING %d bytes", prefix,
+ indent, "", value_length);
+ for (i = 0; i < value_length; i ++)
+ fprintf(stderr, " %02X", string[i] & 255);
+ putc('\n', stderr);
+ break;
+
+ case CUPS_ASN1_NULL_VALUE :
+ fprintf(stderr, "%s%*sNULL VALUE %d bytes\n", prefix, indent, "",
+ value_length);
+
+ buffer += value_length;
+ break;
+
+ case CUPS_ASN1_OID :
+ integer = asn1_get_oid(&buffer, bufend, value_length, oid,
+ CUPS_SNMP_MAX_OID);
+
+ fprintf(stderr, "%s%*sOID %d bytes ", prefix, indent, "",
+ value_length);
+ for (i = 0; i < integer; i ++)
+ fprintf(stderr, ".%d", oid[i]);
+ putc('\n', stderr);
+ break;
+
+ case CUPS_ASN1_SEQUENCE :
+ fprintf(stderr, "%s%*sSEQUENCE %d bytes\n", prefix, indent, "",
+ value_length);
+ asn1_debug(prefix, buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ case CUPS_ASN1_GET_NEXT_REQUEST :
+ fprintf(stderr, "%s%*sGet-Next-Request-PDU %d bytes\n", prefix,
+ indent, "", value_length);
+ asn1_debug(prefix, buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ case CUPS_ASN1_GET_REQUEST :
+ fprintf(stderr, "%s%*sGet-Request-PDU %d bytes\n", prefix, indent, "",
+ value_length);
+ asn1_debug(prefix, buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ case CUPS_ASN1_GET_RESPONSE :
+ fprintf(stderr, "%s%*sGet-Response-PDU %d bytes\n", prefix, indent,
+ "", value_length);
+ asn1_debug(prefix, buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ default :
+ fprintf(stderr, "%s%*sUNKNOWN(%x) %d bytes\n", prefix, indent, "",
+ value_type, value_length);
+
+ buffer += value_length;
+ break;
+ }
+ }
+}
+
+
+/*
+ * 'asn1_decode_snmp()' - Decode a SNMP packet.
+ */
+
+static int /* O - 0 on success, -1 on error */
+asn1_decode_snmp(unsigned char *buffer, /* I - Buffer */
+ size_t len, /* I - Size of buffer */
+ cups_snmp_t *packet) /* I - SNMP packet */
+{
+ unsigned char *bufptr, /* Pointer into the data */
+ *bufend; /* End of data */
+ int length; /* Length of value */
+
+
+ /*
+ * Initialize the decoding...
+ */
+
+ memset(packet, 0, sizeof(cups_snmp_t));
+ packet->object_name[0] = -1;
+
+ bufptr = buffer;
+ bufend = buffer + len;
+
+ if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
+ snmp_set_error(packet, _("Packet does not start with SEQUENCE"));
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ snmp_set_error(packet, _("SEQUENCE uses indefinite length"));
+ else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
+ snmp_set_error(packet, _("No version number"));
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ snmp_set_error(packet, _("Version uses indefinite length"));
+ else if ((packet->version = asn1_get_integer(&bufptr, bufend, length))
+ != CUPS_SNMP_VERSION_1)
+ snmp_set_error(packet, _("Bad SNMP version number"));
+ else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_OCTET_STRING)
+ snmp_set_error(packet, _("No community name"));
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ snmp_set_error(packet, _("Community name uses indefinite length"));
+ else
+ {
+ asn1_get_string(&bufptr, bufend, length, packet->community,
+ sizeof(packet->community));
+
+ if ((packet->request_type = asn1_get_type(&bufptr, bufend))
+ != CUPS_ASN1_GET_RESPONSE)
+ snmp_set_error(packet, _("Packet does not contain a Get-Response-PDU"));
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ snmp_set_error(packet, _("Get-Response-PDU uses indefinite length"));
+ else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
+ snmp_set_error(packet, _("No request-id"));
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ snmp_set_error(packet, _("request-id uses indefinite length"));
+ else
+ {
+ packet->request_id = asn1_get_integer(&bufptr, bufend, length);
+
+ if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
+ snmp_set_error(packet, _("No error-status"));
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ snmp_set_error(packet, _("error-status uses indefinite length"));
+ else
+ {
+ packet->error_status = asn1_get_integer(&bufptr, bufend, length);
+
+ if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
+ snmp_set_error(packet, _("No error-index"));
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ snmp_set_error(packet, _("error-index uses indefinite length"));
+ else
+ {
+ packet->error_index = asn1_get_integer(&bufptr, bufend, length);
+
+ if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
+ snmp_set_error(packet, _("No variable-bindings SEQUENCE"));
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ snmp_set_error(packet,
+ _("variable-bindings uses indefinite length"));
+ else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
+ snmp_set_error(packet, _("No VarBind SEQUENCE"));
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ snmp_set_error(packet, _("VarBind uses indefinite length"));
+ else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_OID)
+ snmp_set_error(packet, _("No name OID"));
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ snmp_set_error(packet, _("Name OID uses indefinite length"));
+ else
+ {
+ asn1_get_oid(&bufptr, bufend, length, packet->object_name,
+ CUPS_SNMP_MAX_OID);
+
+ packet->object_type = asn1_get_type(&bufptr, bufend);
+
+ if ((length = asn1_get_length(&bufptr, bufend)) == 0 &&
+ packet->object_type != CUPS_ASN1_NULL_VALUE &&
+ packet->object_type != CUPS_ASN1_OCTET_STRING)
+ snmp_set_error(packet, _("Value uses indefinite length"));
+ else
+ {
+ switch (packet->object_type)
+ {
+ case CUPS_ASN1_BOOLEAN :
+ packet->object_value.boolean =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ case CUPS_ASN1_INTEGER :
+ packet->object_value.integer =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ case CUPS_ASN1_NULL_VALUE :
+ break;
+
+ case CUPS_ASN1_OCTET_STRING :
+ case CUPS_ASN1_BIT_STRING :
+ case CUPS_ASN1_HEX_STRING :
+ packet->object_value.string.num_bytes = length;
+ asn1_get_string(&bufptr, bufend, length,
+ (char *)packet->object_value.string.bytes,
+ sizeof(packet->object_value.string.bytes));
+ break;
+
+ case CUPS_ASN1_OID :
+ asn1_get_oid(&bufptr, bufend, length,
+ packet->object_value.oid, CUPS_SNMP_MAX_OID);
+ break;
+
+ case CUPS_ASN1_COUNTER :
+ packet->object_value.counter =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ case CUPS_ASN1_GAUGE :
+ packet->object_value.gauge =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ case CUPS_ASN1_TIMETICKS :
+ packet->object_value.timeticks =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ default :
+ snmp_set_error(packet, _("Unsupported value type"));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (packet->error ? -1 : 0);
+}
+
+
+/*
+ * 'asn1_encode_snmp()' - Encode a SNMP packet.
+ */
+
+static int /* O - Length on success, -1 on error */
+asn1_encode_snmp(unsigned char *buffer, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ cups_snmp_t *packet) /* I - SNMP packet */
+{
+ unsigned char *bufptr; /* Pointer into buffer */
+ int total, /* Total length */
+ msglen, /* Length of entire message */
+ commlen, /* Length of community string */
+ reqlen, /* Length of request */
+ listlen, /* Length of variable list */
+ varlen, /* Length of variable */
+ namelen, /* Length of object name OID */
+ valuelen; /* Length of object value */
+
+
+ /*
+ * Get the lengths of the community string, OID, and message...
+ */
+
+
+ namelen = asn1_size_oid(packet->object_name);
+
+ switch (packet->object_type)
+ {
+ case CUPS_ASN1_NULL_VALUE :
+ valuelen = 0;
+ break;
+
+ case CUPS_ASN1_BOOLEAN :
+ valuelen = asn1_size_integer(packet->object_value.boolean);
+ break;
+
+ case CUPS_ASN1_INTEGER :
+ valuelen = asn1_size_integer(packet->object_value.integer);
+ break;
+
+ case CUPS_ASN1_OCTET_STRING :
+ valuelen = packet->object_value.string.num_bytes;
+ break;
+
+ case CUPS_ASN1_OID :
+ valuelen = asn1_size_oid(packet->object_value.oid);
+ break;
+
+ default :
+ packet->error = "Unknown object type";
+ return (-1);
+ }
+
+ varlen = 1 + asn1_size_length(namelen) + namelen +
+ 1 + asn1_size_length(valuelen) + valuelen;
+ listlen = 1 + asn1_size_length(varlen) + varlen;
+ reqlen = 2 + asn1_size_integer(packet->request_id) +
+ 2 + asn1_size_integer(packet->error_status) +
+ 2 + asn1_size_integer(packet->error_index) +
+ 1 + asn1_size_length(listlen) + listlen;
+ commlen = strlen(packet->community);
+ msglen = 2 + asn1_size_integer(packet->version) +
+ 1 + asn1_size_length(commlen) + commlen +
+ 1 + asn1_size_length(reqlen) + reqlen;
+ total = 1 + asn1_size_length(msglen) + msglen;
+
+ if (total > (int)bufsize)
+ {
+ packet->error = "Message too large for buffer";
+ return (-1);
+ }
+
+ /*
+ * Then format the message...
+ */
+
+ bufptr = buffer;
+
+ *bufptr++ = CUPS_ASN1_SEQUENCE; /* SNMPv1 message header */
+ asn1_set_length(&bufptr, msglen);
+
+ asn1_set_integer(&bufptr, packet->version);
+ /* version */
+
+ *bufptr++ = CUPS_ASN1_OCTET_STRING; /* community */
+ asn1_set_length(&bufptr, commlen);
+ memcpy(bufptr, packet->community, commlen);
+ bufptr += commlen;
+
+ *bufptr++ = packet->request_type; /* Get-Request-PDU/Get-Next-Request-PDU */
+ asn1_set_length(&bufptr, reqlen);
+
+ asn1_set_integer(&bufptr, packet->request_id);
+
+ asn1_set_integer(&bufptr, packet->error_status);
+
+ asn1_set_integer(&bufptr, packet->error_index);
+
+ *bufptr++ = CUPS_ASN1_SEQUENCE; /* variable-bindings */
+ asn1_set_length(&bufptr, listlen);
+
+ *bufptr++ = CUPS_ASN1_SEQUENCE; /* variable */
+ asn1_set_length(&bufptr, varlen);
+
+ asn1_set_oid(&bufptr, packet->object_name);
+ /* ObjectName */
+
+ switch (packet->object_type)
+ {
+ case CUPS_ASN1_NULL_VALUE :
+ *bufptr++ = CUPS_ASN1_NULL_VALUE;
+ /* ObjectValue */
+ *bufptr++ = 0; /* Length */
+ break;
+
+ case CUPS_ASN1_BOOLEAN :
+ asn1_set_integer(&bufptr, packet->object_value.boolean);
+ break;
+
+ case CUPS_ASN1_INTEGER :
+ asn1_set_integer(&bufptr, packet->object_value.integer);
+ break;
+
+ case CUPS_ASN1_OCTET_STRING :
+ *bufptr++ = CUPS_ASN1_OCTET_STRING;
+ asn1_set_length(&bufptr, valuelen);
+ memcpy(bufptr, packet->object_value.string.bytes, valuelen);
+ bufptr += valuelen;
+ break;
+
+ case CUPS_ASN1_OID :
+ asn1_set_oid(&bufptr, packet->object_value.oid);
+ break;
+
+ default :
+ break;
+ }
+
+ return (bufptr - buffer);
+}
+
+
+/*
+ * 'asn1_get_integer()' - Get an integer value.
+ */
+
+static int /* O - Integer value */
+asn1_get_integer(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend, /* I - End of buffer */
+ int length) /* I - Length of value */
+{
+ int value; /* Integer value */
+
+
+ if (length > sizeof(int))
+ {
+ (*buffer) += length;
+ return (0);
+ }
+
+ for (value = (**buffer & 0x80) ? -1 : 0;
+ length > 0 && *buffer < bufend;
+ length --, (*buffer) ++)
+ value = (value << 8) | **buffer;
+
+ return (value);
+}
+
+
+/*
+ * 'asn1_get_length()' - Get a value length.
+ */
+
+static unsigned /* O - Length */
+asn1_get_length(unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend) /* I - End of buffer */
+{
+ unsigned length; /* Length */
+
+
+ length = **buffer;
+ (*buffer) ++;
+
+ if (length & 128)
+ {
+ int count; /* Number of bytes for length */
+
+
+ if ((count = length & 127) > sizeof(unsigned))
+ {
+ (*buffer) += count;
+ return (0);
+ }
+
+ for (length = 0;
+ count > 0 && *buffer < bufend;
+ count --, (*buffer) ++)
+ length = (length << 8) | **buffer;
+ }
+
+ return (length);
+}
+
+
+/*
+ * 'asn1_get_oid()' - Get an OID value.
+ */
+
+static int /* O - Number of OIDs */
+asn1_get_oid(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend, /* I - End of buffer */
+ int length, /* I - Length of value */
+ int *oid, /* I - OID buffer */
+ int oidsize) /* I - Size of OID buffer */
+{
+ unsigned char *valend; /* End of value */
+ int *oidptr, /* Current OID */
+ *oidend; /* End of OID buffer */
+ int number; /* OID number */
+
+
+ valend = *buffer + length;
+ oidptr = oid;
+ oidend = oid + oidsize - 1;
+
+ if (valend > bufend)
+ valend = bufend;
+
+ number = asn1_get_packed(buffer, bufend);
+
+ if (number < 80)
+ {
+ *oidptr++ = number / 40;
+ number = number % 40;
+ *oidptr++ = number;
+ }
+ else
+ {
+ *oidptr++ = 2;
+ number -= 80;
+ *oidptr++ = number;
+ }
+
+ while (*buffer < valend)
+ {
+ number = asn1_get_packed(buffer, bufend);
+
+ if (oidptr < oidend)
+ *oidptr++ = number;
+ }
+
+ *oidptr = -1;
+
+ return (oidptr - oid);
+}
+
+
+/*
+ * 'asn1_get_packed()' - Get a packed integer value.
+ */
+
+static int /* O - Value */
+asn1_get_packed(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend) /* I - End of buffer */
+{
+ int value; /* Value */
+
+
+ value = 0;
+
+ while ((**buffer & 128) && *buffer < bufend)
+ {
+ value = (value << 7) | (**buffer & 127);
+ (*buffer) ++;
+ }
+
+ if (*buffer < bufend)
+ {
+ value = (value << 7) | **buffer;
+ (*buffer) ++;
+ }
+
+ return (value);
+}
+
+
+/*
+ * 'asn1_get_string()' - Get a string value.
+ */
+
+static char * /* O - String */
+asn1_get_string(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend, /* I - End of buffer */
+ int length, /* I - Value length */
+ char *string, /* I - String buffer */
+ int strsize) /* I - String buffer size */
+{
+ if (length > (bufend - *buffer))
+ length = bufend - *buffer;
+
+ if (length < 0)
+ {
+ /*
+ * Disallow negative lengths!
+ */
+
+ *string = '\0';
+ }
+ else if (length < strsize)
+ {
+ /*
+ * String is smaller than the buffer...
+ */
+
+ if (length > 0)
+ memcpy(string, *buffer, length);
+
+ string[length] = '\0';
+ }
+ else
+ {
+ /*
+ * String is larger than the buffer...
+ */
+
+ memcpy(string, *buffer, strsize - 1);
+ string[strsize - 1] = '\0';
+ }
+
+ if (length > 0)
+ (*buffer) += length;
+
+ return (length < 0 ? NULL : string);
+}
+
+
+/*
+ * 'asn1_get_type()' - Get a value type.
+ */
+
+static int /* O - Type */
+asn1_get_type(unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend) /* I - End of buffer */
+{
+ int type; /* Type */
+
+
+ type = **buffer;
+ (*buffer) ++;
+
+ if ((type & 31) == 31)
+ type = asn1_get_packed(buffer, bufend);
+
+ return (type);
+}
+
+
+/*
+ * 'asn1_set_integer()' - Set an integer value.
+ */
+
+static void
+asn1_set_integer(unsigned char **buffer,/* IO - Pointer in buffer */
+ int integer) /* I - Integer value */
+{
+ **buffer = CUPS_ASN1_INTEGER;
+ (*buffer) ++;
+
+ if (integer > 0x7fffff || integer < -0x800000)
+ {
+ **buffer = 4;
+ (*buffer) ++;
+ **buffer = integer >> 24;
+ (*buffer) ++;
+ **buffer = integer >> 16;
+ (*buffer) ++;
+ **buffer = integer >> 8;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+ else if (integer > 0x7fff || integer < -0x8000)
+ {
+ **buffer = 3;
+ (*buffer) ++;
+ **buffer = integer >> 16;
+ (*buffer) ++;
+ **buffer = integer >> 8;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+ else if (integer > 0x7f || integer < -0x80)
+ {
+ **buffer = 2;
+ (*buffer) ++;
+ **buffer = integer >> 8;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+ else
+ {
+ **buffer = 1;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+}
+
+
+/*
+ * 'asn1_set_length()' - Set a value length.
+ */
+
+static void
+asn1_set_length(unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned length) /* I - Length value */
+{
+ if (length > 255)
+ {
+ **buffer = 0x82; /* 2-byte length */
+ (*buffer) ++;
+ **buffer = length >> 8;
+ (*buffer) ++;
+ **buffer = length;
+ (*buffer) ++;
+ }
+ else if (length > 127)
+ {
+ **buffer = 0x81; /* 1-byte length */
+ (*buffer) ++;
+ **buffer = length;
+ (*buffer) ++;
+ }
+ else
+ {
+ **buffer = length; /* Length */
+ (*buffer) ++;
+ }
+}
+
+
+/*
+ * 'asn1_set_oid()' - Set an OID value.
+ */
+
+static void
+asn1_set_oid(unsigned char **buffer, /* IO - Pointer in buffer */
+ const int *oid) /* I - OID value */
+{
+ **buffer = CUPS_ASN1_OID;
+ (*buffer) ++;
+
+ asn1_set_length(buffer, asn1_size_oid(oid));
+
+ if (oid[1] < 0)
+ {
+ asn1_set_packed(buffer, oid[0] * 40);
+ return;
+ }
+
+ asn1_set_packed(buffer, oid[0] * 40 + oid[1]);
+
+ for (oid += 2; *oid >= 0; oid ++)
+ asn1_set_packed(buffer, *oid);
+}
+
+
+/*
+ * 'asn1_set_packed()' - Set a packed integer value.
+ */
+
+static void
+asn1_set_packed(unsigned char **buffer, /* IO - Pointer in buffer */
+ int integer) /* I - Integer value */
+{
+ if (integer > 0xfffffff)
+ {
+ **buffer = ((integer >> 28) & 0x7f) | 0x80;
+ (*buffer) ++;
+ }
+
+ if (integer > 0x1fffff)
+ {
+ **buffer = ((integer >> 21) & 0x7f) | 0x80;
+ (*buffer) ++;
+ }
+
+ if (integer > 0x3fff)
+ {
+ **buffer = ((integer >> 14) & 0x7f) | 0x80;
+ (*buffer) ++;
+ }
+
+ if (integer > 0x7f)
+ {
+ **buffer = ((integer >> 7) & 0x7f) | 0x80;
+ (*buffer) ++;
+ }
+
+ **buffer = integer & 0x7f;
+ (*buffer) ++;
+}
+
+
+/*
+ * 'asn1_size_integer()' - Figure out the number of bytes needed for an
+ * integer value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_integer(int integer) /* I - Integer value */
+{
+ if (integer > 0x7fffff || integer < -0x800000)
+ return (4);
+ else if (integer > 0x7fff || integer < -0x8000)
+ return (3);
+ else if (integer > 0x7f || integer < -0x80)
+ return (2);
+ else
+ return (1);
+}
+
+
+/*
+ * 'asn1_size_length()' - Figure out the number of bytes needed for a
+ * length value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_length(int length) /* I - Length value */
+{
+ if (length > 0xff)
+ return (3);
+ else if (length > 0x7f)
+ return (2);
+ else
+ return (1);
+}
+
+
+/*
+ * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an
+ * OID value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_oid(const int *oid) /* I - OID value */
+{
+ int length; /* Length of value */
+
+
+ if (oid[1] < 0)
+ return (asn1_size_packed(oid[0] * 40));
+
+ for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2;
+ *oid >= 0;
+ oid ++)
+ length += asn1_size_packed(*oid);
+
+ return (length);
+}
+
+
+/*
+ * 'asn1_size_packed()' - Figure out the number of bytes needed for a
+ * packed integer value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_packed(int integer) /* I - Integer value */
+{
+ if (integer > 0xfffffff)
+ return (5);
+ else if (integer > 0x1fffff)
+ return (4);
+ else if (integer > 0x3fff)
+ return (3);
+ else if (integer > 0x7f)
+ return (2);
+ else
+ return (1);
+}
+
+
+/*
+ * 'snmp_set_error()' - Set the localized error for a packet.
+ */
+
+static void
+snmp_set_error(cups_snmp_t *packet, /* I - Packet */
+ const char *message) /* I - Error message */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ packet->error = _cupsLangString(cg->lang_default, message);
+}
+
+
+/*
+ * End of "$Id: snmp.c 4167 2013-02-04 19:27:13Z msweet $".
+ */
diff --git a/cups/libs/cups/snprintf.c b/cups/libs/cups/snprintf.c
new file mode 100644
index 000000000..4029d2c1d
--- /dev/null
+++ b/cups/libs/cups/snprintf.c
@@ -0,0 +1,366 @@
+/*
+ * "$Id: snprintf.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * snprintf functions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cups_vsnprintf() - Format a string into a fixed size buffer.
+ * _cups_snprintf() - Format a string into a fixed size buffer.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+
+
+#ifndef HAVE_VSNPRINTF
+/*
+ * '_cups_vsnprintf()' - Format a string into a fixed size buffer.
+ */
+
+int /* O - Number of bytes formatted */
+_cups_vsnprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ char *bufptr, /* Pointer to position in buffer */
+ *bufend, /* Pointer to end of buffer */
+ sign, /* Sign of format width */
+ size, /* Size character (h, l, L) */
+ type; /* Format type character */
+ int width, /* Width of field */
+ prec; /* Number of characters of precision */
+ char tformat[100], /* Temporary format string for sprintf() */
+ *tptr, /* Pointer into temporary format */
+ temp[1024]; /* Buffer for formatted numbers */
+ size_t templen; /* Length of "temp" */
+ char *s; /* Pointer to string */
+ int slen; /* Length of string */
+ int bytes; /* Total number of bytes needed */
+
+
+ /*
+ * Loop through the format string, formatting as needed...
+ */
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+ bytes = 0;
+
+ while (*format)
+ {
+ if (*format == '%')
+ {
+ tptr = tformat;
+ *tptr++ = *format++;
+
+ if (*format == '%')
+ {
+ if (bufptr && bufptr < bufend) *bufptr++ = *format;
+ bytes ++;
+ format ++;
+ continue;
+ }
+ else if (strchr(" -+#\'", *format))
+ {
+ *tptr++ = *format;
+ sign = *format++;
+ }
+ else
+ sign = 0;
+
+ if (*format == '*')
+ {
+ /*
+ * Get width from argument...
+ */
+
+ format ++;
+ width = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ width = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ width = width * 10 + *format++ - '0';
+ }
+ }
+
+ if (*format == '.')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ format ++;
+
+ if (*format == '*')
+ {
+ /*
+ * Get precision from argument...
+ */
+
+ format ++;
+ prec = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ prec = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ prec = prec * 10 + *format++ - '0';
+ }
+ }
+ }
+ else
+ prec = -1;
+
+ if (*format == 'l' && format[1] == 'l')
+ {
+ size = 'L';
+
+ if (tptr < (tformat + sizeof(tformat) - 2))
+ {
+ *tptr++ = 'l';
+ *tptr++ = 'l';
+ }
+
+ format += 2;
+ }
+ else if (*format == 'h' || *format == 'l' || *format == 'L')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ size = *format++;
+ }
+
+ if (!*format)
+ break;
+
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ type = *format++;
+ *tptr = '\0';
+
+ switch (type)
+ {
+ case 'E' : /* Floating point formats */
+ case 'G' :
+ case 'e' :
+ case 'f' :
+ case 'g' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, double));
+ templen = strlen(temp):
+
+ bytes += (int)templen;
+
+ if (bufptr)
+ {
+ if ((bufptr + templen) > bufend)
+ {
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ memcpy(bufptr, temp, templen + 1);
+ bufptr += templen;
+ }
+ }
+ break;
+
+ case 'B' : /* Integer formats */
+ case 'X' :
+ case 'b' :
+ case 'd' :
+ case 'i' :
+ case 'o' :
+ case 'u' :
+ case 'x' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, int));
+ templen = strlen(temp):
+
+ bytes += (int)templen;
+
+ if (bufptr)
+ {
+ if ((bufptr + templen) > bufend)
+ {
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ memcpy(bufptr, temp, templen + 1);
+ bufptr += templen;
+ }
+ }
+ break;
+
+ case 'p' : /* Pointer value */
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, void *));
+ templen = strlen(temp):
+
+ bytes += (int)templen;
+
+ if (bufptr)
+ {
+ if ((bufptr + templen) > bufend)
+ {
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ memcpy(bufptr, temp, templen + 1);
+ bufptr += templen;
+ }
+ }
+ break;
+
+ case 'c' : /* Character or character array */
+ bytes += width;
+
+ if (bufptr)
+ {
+ if (width <= 1)
+ *bufptr++ = va_arg(ap, int);
+ else
+ {
+ if ((bufptr + width) > bufend)
+ width = (int)(bufend - bufptr);
+
+ memcpy(bufptr, va_arg(ap, char *), (size_t)width);
+ bufptr += width;
+ }
+ }
+ break;
+
+ case 's' : /* String */
+ if ((s = va_arg(ap, char *)) == NULL)
+ s = "(null)";
+
+ slen = (int)strlen(s);
+ if (slen > width && prec != width)
+ width = slen;
+
+ bytes += width;
+
+ if (bufptr)
+ {
+ if ((bufptr + width) > bufend)
+ width = (int)(bufend - bufptr);
+
+ if (slen > width)
+ slen = width;
+
+ if (sign == '-')
+ {
+ memcpy(bufptr, s, (size_t)slen);
+ memset(bufptr + slen, ' ', (size_t)(width - slen));
+ }
+ else
+ {
+ memset(bufptr, ' ', (size_t)(width - slen));
+ memcpy(bufptr + width - slen, s, (size_t)slen);
+ }
+
+ bufptr += width;
+ }
+ break;
+
+ case 'n' : /* Output number of chars so far */
+ *(va_arg(ap, int *)) = bytes;
+ break;
+ }
+ }
+ else
+ {
+ bytes ++;
+
+ if (bufptr && bufptr < bufend)
+ *bufptr++ = *format;
+
+ format ++;
+ }
+ }
+
+ /*
+ * Nul-terminate the string and return the number of characters needed.
+ */
+
+ *bufptr = '\0';
+
+ return (bytes);
+}
+#endif /* !HAVE_VSNPRINT */
+
+
+#ifndef HAVE_SNPRINTF
+/*
+ * '_cups_snprintf()' - Format a string into a fixed size buffer.
+ */
+
+int /* O - Number of bytes formatted */
+_cups_snprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ int bytes; /* Number of bytes formatted */
+ va_list ap; /* Pointer to additional arguments */
+
+
+ va_start(ap, format);
+ bytes = vsnprintf(buffer, bufsize, format, ap);
+ va_end(ap);
+
+ return (bytes);
+}
+#endif /* !HAVE_SNPRINTF */
+
+
+/*
+ * End of "$Id: snprintf.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
+
diff --git a/cups/libs/cups/sspi-private.h b/cups/libs/cups/sspi-private.h
new file mode 100644
index 000000000..e8f36c2d1
--- /dev/null
+++ b/cups/libs/cups/sspi-private.h
@@ -0,0 +1,82 @@
+/*
+ * Private SSPI definitions for CUPS.
+ *
+ * Copyright 2010 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/".
+ */
+
+#ifndef _CUPS_SSPI_PRIVATE_H_
+# define _CUPS_SSPI_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <config.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <wincrypt.h>
+# include <wintrust.h>
+# include <schannel.h>
+# define SECURITY_WIN32
+# include <security.h>
+# include <sspi.h>
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+typedef struct /**** SSPI/SSL data structure ****/
+{
+ SOCKET sock; /* TCP/IP socket */
+ CredHandle creds; /* Credentials */
+ CtxtHandle context; /* SSL context */
+ BOOL contextInitialized; /* Is context init'd? */
+ SecPkgContext_StreamSizes streamSizes; /* SSL data stream sizes */
+ BYTE *decryptBuffer; /* Data pre-decryption*/
+ size_t decryptBufferLength; /* Length of decrypt buffer */
+ size_t decryptBufferUsed; /* Bytes used in buffer */
+ BYTE *readBuffer; /* Data post-decryption */
+ size_t readBufferLength; /* Length of read buffer */
+ size_t readBufferUsed; /* Bytes used in buffer */
+ DWORD certFlags; /* Cert verification flags */
+} _sspi_struct_t;
+
+
+/*
+ * Prototypes...
+ */
+_sspi_struct_t *_sspiAlloc(void);
+BOOL _sspiAccept(_sspi_struct_t *conn);
+BOOL _sspiConnect(_sspi_struct_t *conn,
+ const CHAR *hostname);
+void _sspiFree(_sspi_struct_t *conn);
+BOOL _sspiGetCredentials(_sspi_struct_t *conn,
+ const LPWSTR containerName,
+ const TCHAR *commonName,
+ BOOL server);
+int _sspiPending(_sspi_struct_t *conn);
+int _sspiRead(_sspi_struct_t *conn,
+ void *buf, size_t len);
+void _sspiSetAllowsAnyRoot(_sspi_struct_t *conn,
+ BOOL allow);
+void _sspiSetAllowsExpiredCerts(_sspi_struct_t *conn,
+ BOOL allow);
+int _sspiWrite(_sspi_struct_t *conn,
+ void *buf, size_t len);
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_SSPI_PRIVATE_H_ */
diff --git a/cups/libs/cups/sspi.c b/cups/libs/cups/sspi.c
new file mode 100644
index 000000000..ba66f729b
--- /dev/null
+++ b/cups/libs/cups/sspi.c
@@ -0,0 +1,1468 @@
+/*
+ * "$Id: sspi.c 11760 2014-03-28 12:58:24Z msweet $"
+ *
+ * Windows SSPI SSL implementation for CUPS.
+ *
+ * Copyright 2010-2014 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/".
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "sspi-private.h"
+#include "debug-private.h"
+
+
+/* required to link this library for certificate functions */
+#pragma comment(lib, "Crypt32.lib")
+#pragma comment(lib, "Secur32.lib")
+#pragma comment(lib, "Ws2_32.lib")
+
+
+#if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
+# define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
+#endif
+
+#if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
+# define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
+#endif
+
+static DWORD sspi_verify_certificate(PCCERT_CONTEXT serverCert,
+ const CHAR *serverName,
+ DWORD dwCertFlags);
+
+
+/*
+ * 'sspi_alloc()' - Allocate SSPI ssl object
+ */
+_sspi_struct_t* /* O - New SSPI/SSL object */
+_sspiAlloc(void)
+{
+ _sspi_struct_t *conn = calloc(sizeof(_sspi_struct_t), 1);
+
+ if (conn)
+ conn->sock = INVALID_SOCKET;
+
+ return (conn);
+}
+
+
+/*
+ * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
+ * If one cannot be found, one is created.
+ */
+BOOL /* O - 1 on success, 0 on failure */
+_sspiGetCredentials(_sspi_struct_t *conn,
+ /* I - Client connection */
+ const LPWSTR container,
+ /* I - Cert container name */
+ const TCHAR *cn, /* I - Common name of certificate */
+ BOOL isServer)
+ /* I - Is caller a server? */
+{
+ HCERTSTORE store = NULL; /* Certificate store */
+ PCCERT_CONTEXT storedContext = NULL;
+ /* Context created from the store */
+ PCCERT_CONTEXT createdContext = NULL;
+ /* Context created by us */
+ DWORD dwSize = 0; /* 32 bit size */
+ PBYTE p = NULL; /* Temporary storage */
+ HCRYPTPROV hProv = (HCRYPTPROV) NULL;
+ /* Handle to a CSP */
+ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
+ SCHANNEL_CRED SchannelCred; /* Schannel credential data */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS Status; /* Status */
+ HCRYPTKEY hKey = (HCRYPTKEY) NULL;
+ /* Handle to crypto key */
+ CRYPT_KEY_PROV_INFO kpi; /* Key container info */
+ SYSTEMTIME et; /* System time */
+ CERT_EXTENSIONS exts; /* Array of cert extensions */
+ CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
+ BOOL ok = TRUE; /* Return value */
+
+
+ DEBUG_printf(("_sspiGetCredentials(conn=%p, container=%p, cn=\"%s\", isServer=%d)", conn, container, cn, isServer));
+
+ if (!conn)
+ return (FALSE);
+ if (!cn)
+ return (FALSE);
+
+ if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
+ PROV_RSA_FULL,
+ CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+ {
+ if (GetLastError() == NTE_EXISTS)
+ {
+ if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
+ PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+ {
+ DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+ }
+ }
+
+ store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
+ X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
+ hProv,
+ CERT_SYSTEM_STORE_LOCAL_MACHINE |
+ CERT_STORE_NO_CRYPT_RELEASE_FLAG |
+ CERT_STORE_OPEN_EXISTING_FLAG,
+ L"MY");
+
+ if (!store)
+ {
+ DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ dwSize = 0;
+
+ if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR,
+ NULL, NULL, &dwSize, NULL))
+ {
+ DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ p = (PBYTE) malloc(dwSize);
+
+ if (!p)
+ {
+ DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL,
+ p, &dwSize, NULL))
+ {
+ DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ sib.cbData = dwSize;
+ sib.pbData = p;
+
+ storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
+
+ if (!storedContext)
+ {
+ /*
+ * If we couldn't find the context, then we'll
+ * create a new one
+ */
+ if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
+ {
+ DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ ZeroMemory(&kpi, sizeof(kpi));
+ kpi.pwszContainerName = (LPWSTR) container;
+ kpi.pwszProvName = MS_DEF_PROV_W;
+ kpi.dwProvType = PROV_RSA_FULL;
+ kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
+ kpi.dwKeySpec = AT_KEYEXCHANGE;
+
+ GetSystemTime(&et);
+ et.wYear += 10;
+
+ ZeroMemory(&exts, sizeof(exts));
+
+ createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL,
+ &et, &exts);
+
+ if (!createdContext)
+ {
+ DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ if (!CertAddCertificateContextToStore(store, createdContext,
+ CERT_STORE_ADD_REPLACE_EXISTING,
+ &storedContext))
+ {
+ DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ ZeroMemory(&ckp, sizeof(ckp));
+ ckp.pwszContainerName = (LPWSTR) container;
+ ckp.pwszProvName = MS_DEF_PROV_W;
+ ckp.dwProvType = PROV_RSA_FULL;
+ ckp.dwFlags = CRYPT_MACHINE_KEYSET;
+ ckp.dwKeySpec = AT_KEYEXCHANGE;
+
+ if (!CertSetCertificateContextProperty(storedContext,
+ CERT_KEY_PROV_INFO_PROP_ID,
+ 0, &ckp))
+ {
+ DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
+ GetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+ }
+
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ SchannelCred.cCreds = 1;
+ SchannelCred.paCred = &storedContext;
+
+ /*
+ * SSPI doesn't seem to like it if grbitEnabledProtocols
+ * is set for a client
+ */
+ if (isServer)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
+
+ /*
+ * Create an SSPI credential.
+ */
+ Status = AcquireCredentialsHandle(NULL, UNISP_NAME,
+ isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND,
+ NULL, &SchannelCred, NULL, NULL, &conn->creds,
+ &tsExpiry);
+ if (Status != SEC_E_OK)
+ {
+ DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+cleanup:
+
+ /*
+ * Cleanup
+ */
+ if (hKey)
+ CryptDestroyKey(hKey);
+
+ if (createdContext)
+ CertFreeCertificateContext(createdContext);
+
+ if (storedContext)
+ CertFreeCertificateContext(storedContext);
+
+ if (p)
+ free(p);
+
+ if (store)
+ CertCloseStore(store, 0);
+
+ if (hProv)
+ CryptReleaseContext(hProv, 0);
+
+ return (ok);
+}
+
+
+/*
+ * '_sspiConnect()' - Make an SSL connection. This function
+ * assumes a TCP/IP connection has already
+ * been successfully made
+ */
+BOOL /* O - 1 on success, 0 on failure */
+_sspiConnect(_sspi_struct_t *conn, /* I - Client connection */
+ const CHAR *hostname) /* I - Server hostname */
+{
+ PCCERT_CONTEXT serverCert; /* Server certificate */
+ DWORD dwSSPIFlags; /* SSL connection attributes we want */
+ DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS scRet; /* Status */
+ DWORD cbData; /* Data count */
+ SecBufferDesc inBuffer; /* Array of SecBuffer structs */
+ SecBuffer inBuffers[2]; /* Security package buffer */
+ SecBufferDesc outBuffer; /* Array of SecBuffer structs */
+ SecBuffer outBuffers[1]; /* Security package buffer */
+ BOOL ok = TRUE; /* Return value */
+
+ serverCert = NULL;
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ /*
+ * Initiate a ClientHello message and generate a token.
+ */
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+ outBuffers[0].cbBuffer = 0;
+
+ outBuffer.cBuffers = 1;
+ outBuffer.pBuffers = outBuffers;
+ outBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = InitializeSecurityContext(&conn->creds, NULL, TEXT(""), dwSSPIFlags,
+ 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
+ &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+
+ if (scRet != SEC_I_CONTINUE_NEEDED)
+ {
+ DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ /*
+ * Send response to server if there is one.
+ */
+ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ {
+ cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+
+ if ((cbData == SOCKET_ERROR) || !cbData)
+ {
+ DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ DeleteSecurityContext(&conn->context);
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData));
+
+ /*
+ * Free output buffer.
+ */
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ outBuffers[0].pvBuffer = NULL;
+ }
+
+ dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ conn->decryptBufferUsed = 0;
+
+ /*
+ * Loop until the handshake is finished or an error occurs.
+ */
+ scRet = SEC_I_CONTINUE_NEEDED;
+
+ while(scRet == SEC_I_CONTINUE_NEEDED ||
+ scRet == SEC_E_INCOMPLETE_MESSAGE ||
+ scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE))
+ {
+ if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+ {
+ conn->decryptBufferLength += 4096;
+ conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, conn->decryptBufferLength);
+
+ if (!conn->decryptBuffer)
+ {
+ DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
+ conn->decryptBufferLength));
+ SetLastError(E_OUTOFMEMORY);
+ ok = FALSE;
+ goto cleanup;
+ }
+ }
+
+ cbData = recv(conn->sock, conn->decryptBuffer + conn->decryptBufferUsed,
+ (int) (conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+
+ if (cbData == SOCKET_ERROR)
+ {
+ DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+ else if (cbData == 0)
+ {
+ DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
+ cbData));
+
+ conn->decryptBufferUsed += cbData;
+ }
+
+ /*
+ * Set up the input buffers. Buffer 0 is used to pass in data
+ * received from the server. Schannel will consume some or all
+ * of this. Leftover data (if any) will be placed in buffer 1 and
+ * given a buffer type of SECBUFFER_EXTRA.
+ */
+ inBuffers[0].pvBuffer = conn->decryptBuffer;
+ inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed;
+ inBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ inBuffers[1].pvBuffer = NULL;
+ inBuffers[1].cbBuffer = 0;
+ inBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ inBuffer.cBuffers = 2;
+ inBuffer.pBuffers = inBuffers;
+ inBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Set up the output buffers. These are initialized to NULL
+ * so as to make it less likely we'll attempt to free random
+ * garbage later.
+ */
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].BufferType= SECBUFFER_TOKEN;
+ outBuffers[0].cbBuffer = 0;
+
+ outBuffer.cBuffers = 1;
+ outBuffer.pBuffers = outBuffers;
+ outBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Call InitializeSecurityContext.
+ */
+ scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags,
+ 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL,
+ &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+
+ /*
+ * If InitializeSecurityContext was successful (or if the error was
+ * one of the special extended ones), send the contends of the output
+ * buffer to the server.
+ */
+ if (scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
+ {
+ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ {
+ cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+
+ if ((cbData == SOCKET_ERROR) || !cbData)
+ {
+ DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ DeleteSecurityContext(&conn->context);
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData));
+
+ /*
+ * Free output buffer.
+ */
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ outBuffers[0].pvBuffer = NULL;
+ }
+ }
+
+ /*
+ * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
+ * then we need to read more data from the server and try again.
+ */
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ continue;
+
+ /*
+ * If InitializeSecurityContext returned SEC_E_OK, then the
+ * handshake completed successfully.
+ */
+ if (scRet == SEC_E_OK)
+ {
+ /*
+ * If the "extra" buffer contains data, this is encrypted application
+ * protocol layer stuff. It needs to be saved. The application layer
+ * will later decrypt it with DecryptMessage.
+ */
+ DEBUG_printf(("_sspiConnect: Handshake was successful"));
+
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ if (conn->decryptBufferLength < inBuffers[1].cbBuffer)
+ {
+ conn->decryptBuffer = realloc(conn->decryptBuffer, inBuffers[1].cbBuffer);
+
+ if (!conn->decryptBuffer)
+ {
+ DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
+ inBuffers[1].cbBuffer));
+ SetLastError(E_OUTOFMEMORY);
+ ok = FALSE;
+ goto cleanup;
+ }
+ }
+
+ memmove(conn->decryptBuffer,
+ conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer),
+ inBuffers[1].cbBuffer);
+
+ conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+
+ DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
+ conn->decryptBufferUsed));
+ }
+ else
+ conn->decryptBufferUsed = 0;
+
+ /*
+ * Bail out to quit
+ */
+ break;
+ }
+
+ /*
+ * Check for fatal error.
+ */
+ if (FAILED(scRet))
+ {
+ DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet));
+ ok = FALSE;
+ break;
+ }
+
+ /*
+ * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
+ * then the server just requested client authentication.
+ */
+ if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ /*
+ * Unimplemented
+ */
+ DEBUG_printf(("_sspiConnect: server requested client credentials"));
+ ok = FALSE;
+ break;
+ }
+
+ /*
+ * Copy any leftover data from the "extra" buffer, and go around
+ * again.
+ */
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memmove(conn->decryptBuffer,
+ conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer),
+ inBuffers[1].cbBuffer);
+
+ conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+ }
+ else
+ {
+ conn->decryptBufferUsed = 0;
+ }
+ }
+
+ if (ok)
+ {
+ conn->contextInitialized = TRUE;
+
+ /*
+ * Get the server cert
+ */
+ scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID*) &serverCert );
+
+ if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ scRet = sspi_verify_certificate(serverCert, hostname, conn->certFlags);
+
+ if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ /*
+ * Find out how big the header/trailer will be:
+ */
+ scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+
+ if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet));
+ ok = FALSE;
+ }
+ }
+
+cleanup:
+
+ if (serverCert)
+ CertFreeCertificateContext(serverCert);
+
+ return (ok);
+}
+
+
+/*
+ * '_sspiAccept()' - Accept an SSL/TLS connection
+ */
+BOOL /* O - 1 on success, 0 on failure */
+_sspiAccept(_sspi_struct_t *conn) /* I - Client connection */
+{
+ DWORD dwSSPIFlags; /* SSL connection attributes we want */
+ DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS scRet; /* SSPI Status */
+ SecBufferDesc inBuffer; /* Array of SecBuffer structs */
+ SecBuffer inBuffers[2]; /* Security package buffer */
+ SecBufferDesc outBuffer; /* Array of SecBuffer structs */
+ SecBuffer outBuffers[1]; /* Security package buffer */
+ DWORD num = 0; /* 32 bit status value */
+ BOOL fInitContext = TRUE;
+ /* Has the context been init'd? */
+ BOOL ok = TRUE; /* Return value */
+
+ if (!conn)
+ return (FALSE);
+
+ dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
+ ASC_REQ_REPLAY_DETECT |
+ ASC_REQ_CONFIDENTIALITY |
+ ASC_REQ_EXTENDED_ERROR |
+ ASC_REQ_ALLOCATE_MEMORY |
+ ASC_REQ_STREAM;
+
+ conn->decryptBufferUsed = 0;
+
+ /*
+ * Set OutBuffer for AcceptSecurityContext call
+ */
+ outBuffer.cBuffers = 1;
+ outBuffer.pBuffers = outBuffers;
+ outBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = SEC_I_CONTINUE_NEEDED;
+
+ while (scRet == SEC_I_CONTINUE_NEEDED ||
+ scRet == SEC_E_INCOMPLETE_MESSAGE ||
+ scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE))
+ {
+ if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+ {
+ conn->decryptBufferLength += 4096;
+ conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer,
+ conn->decryptBufferLength);
+
+ if (!conn->decryptBuffer)
+ {
+ DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
+ conn->decryptBufferLength));
+ ok = FALSE;
+ goto cleanup;
+ }
+ }
+
+ for (;;)
+ {
+ num = recv(conn->sock,
+ conn->decryptBuffer + conn->decryptBufferUsed,
+ (int)(conn->decryptBufferLength - conn->decryptBufferUsed),
+ 0);
+
+ if ((num == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
+ Sleep(1);
+ else
+ break;
+ }
+
+ if (num == SOCKET_ERROR)
+ {
+ DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+ else if (num == 0)
+ {
+ DEBUG_printf(("_sspiAccept: client disconnected"));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
+ num));
+ conn->decryptBufferUsed += num;
+ }
+
+ /*
+ * InBuffers[1] is for getting extra data that
+ * SSPI/SCHANNEL doesn't proccess on this
+ * run around the loop.
+ */
+ inBuffers[0].pvBuffer = conn->decryptBuffer;
+ inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed;
+ inBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ inBuffers[1].pvBuffer = NULL;
+ inBuffers[1].cbBuffer = 0;
+ inBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ inBuffer.cBuffers = 2;
+ inBuffer.pBuffers = inBuffers;
+ inBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Initialize these so if we fail, pvBuffer contains NULL,
+ * so we don't try to free random garbage at the quit
+ */
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+ outBuffers[0].cbBuffer = 0;
+
+ scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context),
+ &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP,
+ (fInitContext?&conn->context:NULL), &outBuffer,
+ &dwSSPIOutFlags, &tsExpiry);
+
+ fInitContext = FALSE;
+
+ if (scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
+ {
+ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ {
+ /*
+ * Send response to server if there is one
+ */
+ num = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+
+ if ((num == SOCKET_ERROR) || (num == 0))
+ {
+ DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
+ outBuffers[0].cbBuffer));
+
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ outBuffers[0].pvBuffer = NULL;
+ }
+ }
+
+ if (scRet == SEC_E_OK)
+ {
+ /*
+ * If there's extra data then save it for
+ * next time we go to decrypt
+ */
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memcpy(conn->decryptBuffer,
+ (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)),
+ inBuffers[1].cbBuffer);
+ conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+ }
+ else
+ {
+ conn->decryptBufferUsed = 0;
+ }
+
+ ok = TRUE;
+ break;
+ }
+ else if (FAILED(scRet) && (scRet != SEC_E_INCOMPLETE_MESSAGE))
+ {
+ DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet));
+ ok = FALSE;
+ break;
+ }
+
+ if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
+ scRet != SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memcpy(conn->decryptBuffer,
+ (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)),
+ inBuffers[1].cbBuffer);
+ conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+ }
+ else
+ {
+ conn->decryptBufferUsed = 0;
+ }
+ }
+ }
+
+ if (ok)
+ {
+ conn->contextInitialized = TRUE;
+
+ /*
+ * Find out how big the header will be:
+ */
+ scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+
+ if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet));
+ ok = FALSE;
+ }
+ }
+
+cleanup:
+
+ return (ok);
+}
+
+
+/*
+ * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
+ */
+void
+_sspiSetAllowsAnyRoot(_sspi_struct_t *conn,
+ /* I - Client connection */
+ BOOL allow)
+ /* I - Allow any root */
+{
+ conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA :
+ conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA;
+}
+
+
+/*
+ * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
+ */
+void
+_sspiSetAllowsExpiredCerts(_sspi_struct_t *conn,
+ /* I - Client connection */
+ BOOL allow)
+ /* I - Allow expired certs */
+{
+ conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID :
+ conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
+}
+
+
+/*
+ * '_sspiWrite()' - Write a buffer to an ssl socket
+ */
+int /* O - Bytes written or SOCKET_ERROR */
+_sspiWrite(_sspi_struct_t *conn, /* I - Client connection */
+ void *buf, /* I - Buffer */
+ size_t len) /* I - Buffer length */
+{
+ SecBufferDesc message; /* Array of SecBuffer struct */
+ SecBuffer buffers[4] = { 0 }; /* Security package buffer */
+ BYTE *buffer = NULL; /* Scratch buffer */
+ int bufferLen; /* Buffer length */
+ size_t bytesLeft; /* Bytes left to write */
+ int index = 0; /* Index into buffer */
+ int num = 0; /* Return value */
+
+ if (!conn || !buf || !len)
+ {
+ WSASetLastError(WSAEINVAL);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+
+ bufferLen = conn->streamSizes.cbMaximumMessage +
+ conn->streamSizes.cbHeader +
+ conn->streamSizes.cbTrailer;
+
+ buffer = (BYTE*) malloc(bufferLen);
+
+ if (!buffer)
+ {
+ DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen));
+ WSASetLastError(E_OUTOFMEMORY);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+
+ bytesLeft = len;
+
+ while (bytesLeft)
+ {
+ size_t chunk = min(conn->streamSizes.cbMaximumMessage, /* Size of data to write */
+ bytesLeft);
+ SECURITY_STATUS scRet; /* SSPI status */
+
+ /*
+ * Copy user data into the buffer, starting
+ * just past the header
+ */
+ memcpy(buffer + conn->streamSizes.cbHeader,
+ ((BYTE*) buf) + index,
+ chunk);
+
+ /*
+ * Setup the SSPI buffers
+ */
+ message.ulVersion = SECBUFFER_VERSION;
+ message.cBuffers = 4;
+ message.pBuffers = buffers;
+ buffers[0].pvBuffer = buffer;
+ buffers[0].cbBuffer = conn->streamSizes.cbHeader;
+ buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+ buffers[1].pvBuffer = buffer + conn->streamSizes.cbHeader;
+ buffers[1].cbBuffer = (unsigned long) chunk;
+ buffers[1].BufferType = SECBUFFER_DATA;
+ buffers[2].pvBuffer = buffer + conn->streamSizes.cbHeader + chunk;
+ buffers[2].cbBuffer = conn->streamSizes.cbTrailer;
+ buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ /*
+ * Encrypt the data
+ */
+ scRet = EncryptMessage(&conn->context, 0, &message, 0);
+
+ if (FAILED(scRet))
+ {
+ DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet));
+ WSASetLastError(WSASYSCALLFAILURE);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+
+ /*
+ * Send the data. Remember the size of
+ * the total data to send is the size
+ * of the header, the size of the data
+ * the caller passed in and the size
+ * of the trailer
+ */
+ num = send(conn->sock,
+ buffer,
+ buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer,
+ 0);
+
+ if ((num == SOCKET_ERROR) || (num == 0))
+ {
+ DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
+ goto cleanup;
+ }
+
+ bytesLeft -= (int) chunk;
+ index += (int) chunk;
+ }
+
+ num = (int) len;
+
+cleanup:
+
+ if (buffer)
+ free(buffer);
+
+ return (num);
+}
+
+
+/*
+ * '_sspiRead()' - Read a buffer from an ssl socket
+ */
+int /* O - Bytes read or SOCKET_ERROR */
+_sspiRead(_sspi_struct_t *conn, /* I - Client connection */
+ void *buf, /* I - Buffer */
+ size_t len) /* I - Buffer length */
+{
+ SecBufferDesc message; /* Array of SecBuffer struct */
+ SecBuffer buffers[4] = { 0 }; /* Security package buffer */
+ int num = 0; /* Return value */
+
+ if (!conn)
+ {
+ WSASetLastError(WSAEINVAL);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+
+ /*
+ * If there are bytes that have already been
+ * decrypted and have not yet been read, return
+ * those
+ */
+ if (buf && (conn->readBufferUsed > 0))
+ {
+ int bytesToCopy = (int) min(conn->readBufferUsed, len); /* Amount of bytes to copy */
+ /* from read buffer */
+
+ memcpy(buf, conn->readBuffer, bytesToCopy);
+ conn->readBufferUsed -= bytesToCopy;
+
+ if (conn->readBufferUsed > 0)
+ /*
+ * If the caller didn't request all the bytes
+ * we have in the buffer, then move the unread
+ * bytes down
+ */
+ memmove(conn->readBuffer,
+ conn->readBuffer + bytesToCopy,
+ conn->readBufferUsed);
+
+ num = bytesToCopy;
+ }
+ else
+ {
+ PSecBuffer pDataBuffer; /* Data buffer */
+ PSecBuffer pExtraBuffer; /* Excess data buffer */
+ SECURITY_STATUS scRet; /* SSPI status */
+ int i; /* Loop control variable */
+
+ /*
+ * Initialize security buffer structs
+ */
+ message.ulVersion = SECBUFFER_VERSION;
+ message.cBuffers = 4;
+ message.pBuffers = buffers;
+
+ do
+ {
+ /*
+ * If there is not enough space in the
+ * buffer, then increase it's size
+ */
+ if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+ {
+ conn->decryptBufferLength += 4096;
+ conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer,
+ conn->decryptBufferLength);
+
+ if (!conn->decryptBuffer)
+ {
+ DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
+ conn->decryptBufferLength));
+ WSASetLastError(E_OUTOFMEMORY);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+ }
+
+ buffers[0].pvBuffer = conn->decryptBuffer;
+ buffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed;
+ buffers[0].BufferType = SECBUFFER_DATA;
+ buffers[1].BufferType = SECBUFFER_EMPTY;
+ buffers[2].BufferType = SECBUFFER_EMPTY;
+ buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ scRet = DecryptMessage(&conn->context, &message, 0, NULL);
+
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (buf)
+ {
+ num = recv(conn->sock,
+ conn->decryptBuffer + conn->decryptBufferUsed,
+ (int)(conn->decryptBufferLength - conn->decryptBufferUsed),
+ 0);
+ if (num == SOCKET_ERROR)
+ {
+ DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
+ goto cleanup;
+ }
+ else if (num == 0)
+ {
+ DEBUG_printf(("_sspiRead: server disconnected"));
+ goto cleanup;
+ }
+
+ conn->decryptBufferUsed += num;
+ }
+ else
+ {
+ num = (int) conn->readBufferUsed;
+ goto cleanup;
+ }
+ }
+ }
+ while (scRet == SEC_E_INCOMPLETE_MESSAGE);
+
+ if (scRet == SEC_I_CONTEXT_EXPIRED)
+ {
+ DEBUG_printf(("_sspiRead: context expired"));
+ WSASetLastError(WSAECONNRESET);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+ else if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet));
+ WSASetLastError(WSASYSCALLFAILURE);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+
+ /*
+ * The decryption worked. Now, locate data buffer.
+ */
+ pDataBuffer = NULL;
+ pExtraBuffer = NULL;
+ for (i = 1; i < 4; i++)
+ {
+ if (buffers[i].BufferType == SECBUFFER_DATA)
+ pDataBuffer = &buffers[i];
+ else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
+ pExtraBuffer = &buffers[i];
+ }
+
+ /*
+ * If a data buffer is found, then copy
+ * the decrypted bytes to the passed-in
+ * buffer
+ */
+ if (pDataBuffer)
+ {
+ int bytesToCopy = min(pDataBuffer->cbBuffer, (int) len);
+ /* Number of bytes to copy into buf */
+ int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
+ /* Number of bytes to save in our read buffer */
+
+ if (bytesToCopy)
+ memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
+
+ /*
+ * If there are more decrypted bytes than can be
+ * copied to the passed in buffer, then save them
+ */
+ if (bytesToSave)
+ {
+ if ((int)(conn->readBufferLength - conn->readBufferUsed) < bytesToSave)
+ {
+ conn->readBufferLength = conn->readBufferUsed + bytesToSave;
+ conn->readBuffer = realloc(conn->readBuffer,
+ conn->readBufferLength);
+
+ if (!conn->readBuffer)
+ {
+ DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn->readBufferLength));
+ WSASetLastError(E_OUTOFMEMORY);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+ }
+
+ memcpy(((BYTE*) conn->readBuffer) + conn->readBufferUsed,
+ ((BYTE*) pDataBuffer->pvBuffer) + bytesToCopy,
+ bytesToSave);
+
+ conn->readBufferUsed += bytesToSave;
+ }
+
+ num = (buf) ? bytesToCopy : (int) conn->readBufferUsed;
+ }
+ else
+ {
+ DEBUG_printf(("_sspiRead: unable to find data buffer"));
+ WSASetLastError(WSASYSCALLFAILURE);
+ num = SOCKET_ERROR;
+ goto cleanup;
+ }
+
+ /*
+ * If the decryption process left extra bytes,
+ * then save those back in decryptBuffer. They will
+ * be processed the next time through the loop.
+ */
+ if (pExtraBuffer)
+ {
+ memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
+ conn->decryptBufferUsed = pExtraBuffer->cbBuffer;
+ }
+ else
+ {
+ conn->decryptBufferUsed = 0;
+ }
+ }
+
+cleanup:
+
+ return (num);
+}
+
+
+/*
+ * '_sspiPending()' - Returns the number of available bytes
+ */
+int /* O - Number of available bytes */
+_sspiPending(_sspi_struct_t *conn) /* I - Client connection */
+{
+ return (_sspiRead(conn, NULL, 0));
+}
+
+
+/*
+ * '_sspiFree()' - Close a connection and free resources
+ */
+void
+_sspiFree(_sspi_struct_t *conn) /* I - Client connection */
+{
+ if (!conn)
+ return;
+
+ if (conn->contextInitialized)
+ {
+ SecBufferDesc message; /* Array of SecBuffer struct */
+ SecBuffer buffers[1] = { 0 };
+ /* Security package buffer */
+ DWORD dwType; /* Type */
+ DWORD status; /* Status */
+
+ /*
+ * Notify schannel that we are about to close the connection.
+ */
+ dwType = SCHANNEL_SHUTDOWN;
+
+ buffers[0].pvBuffer = &dwType;
+ buffers[0].BufferType = SECBUFFER_TOKEN;
+ buffers[0].cbBuffer = sizeof(dwType);
+
+ message.cBuffers = 1;
+ message.pBuffers = buffers;
+ message.ulVersion = SECBUFFER_VERSION;
+
+ status = ApplyControlToken(&conn->context, &message);
+
+ if (SUCCEEDED(status))
+ {
+ PBYTE pbMessage; /* Message buffer */
+ DWORD cbMessage; /* Message buffer count */
+ DWORD cbData; /* Data count */
+ DWORD dwSSPIFlags; /* SSL attributes we requested */
+ DWORD dwSSPIOutFlags; /* SSL attributes we received */
+ TimeStamp tsExpiry; /* Time stamp */
+
+ dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
+ ASC_REQ_REPLAY_DETECT |
+ ASC_REQ_CONFIDENTIALITY |
+ ASC_REQ_EXTENDED_ERROR |
+ ASC_REQ_ALLOCATE_MEMORY |
+ ASC_REQ_STREAM;
+
+ buffers[0].pvBuffer = NULL;
+ buffers[0].BufferType = SECBUFFER_TOKEN;
+ buffers[0].cbBuffer = 0;
+
+ message.cBuffers = 1;
+ message.pBuffers = buffers;
+ message.ulVersion = SECBUFFER_VERSION;
+
+ status = AcceptSecurityContext(&conn->creds, &conn->context, NULL,
+ dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
+ &message, &dwSSPIOutFlags, &tsExpiry);
+
+ if (SUCCEEDED(status))
+ {
+ pbMessage = buffers[0].pvBuffer;
+ cbMessage = buffers[0].cbBuffer;
+
+ /*
+ * Send the close notify message to the client.
+ */
+ if (pbMessage && cbMessage)
+ {
+ cbData = send(conn->sock, pbMessage, cbMessage, 0);
+ if ((cbData == SOCKET_ERROR) || (cbData == 0))
+ {
+ status = WSAGetLastError();
+ DEBUG_printf(("_sspiFree: sending close notify failed: %d", status));
+ }
+ else
+ {
+ FreeContextBuffer(pbMessage);
+ }
+ }
+ }
+ else
+ {
+ DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status));
+ }
+ }
+ else
+ {
+ DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status));
+ }
+
+ DeleteSecurityContext(&conn->context);
+ conn->contextInitialized = FALSE;
+ }
+
+ if (conn->decryptBuffer)
+ {
+ free(conn->decryptBuffer);
+ conn->decryptBuffer = NULL;
+ }
+
+ if (conn->readBuffer)
+ {
+ free(conn->readBuffer);
+ conn->readBuffer = NULL;
+ }
+
+ if (conn->sock != INVALID_SOCKET)
+ {
+ closesocket(conn->sock);
+ conn->sock = INVALID_SOCKET;
+ }
+
+ free(conn);
+}
+
+
+/*
+ * 'sspi_verify_certificate()' - Verify a server certificate
+ */
+static DWORD /* 0 - Error code (0 == No error) */
+sspi_verify_certificate(PCCERT_CONTEXT serverCert,
+ /* I - Server certificate */
+ const CHAR *serverName,
+ /* I - Server name */
+ DWORD dwCertFlags)
+ /* I - Verification flags */
+{
+ HTTPSPolicyCallbackData httpsPolicy;
+ /* HTTPS Policy Struct */
+ CERT_CHAIN_POLICY_PARA policyPara;
+ /* Cert chain policy parameters */
+ CERT_CHAIN_POLICY_STATUS policyStatus;
+ /* Cert chain policy status */
+ CERT_CHAIN_PARA chainPara;
+ /* Used for searching and matching criteria */
+ PCCERT_CHAIN_CONTEXT chainContext = NULL;
+ /* Certificate chain */
+ PWSTR serverNameUnicode = NULL;
+ /* Unicode server name */
+ LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE };
+ /* How are we using this certificate? */
+ DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
+ /* Number of ites in rgszUsages */
+ DWORD count; /* 32 bit count variable */
+ DWORD status; /* Return value */
+
+ if (!serverCert)
+ {
+ status = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ /*
+ * Convert server name to unicode.
+ */
+ if (!serverName || (strlen(serverName) == 0))
+ {
+ status = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, NULL, 0);
+ serverNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
+ if (!serverNameUnicode)
+ {
+ status = SEC_E_INSUFFICIENT_MEMORY;
+ goto cleanup;
+ }
+ count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, serverNameUnicode, count);
+ if (count == 0)
+ {
+ status = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ /*
+ * Build certificate chain.
+ */
+ ZeroMemory(&chainPara, sizeof(chainPara));
+ chainPara.cbSize = sizeof(chainPara);
+ chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
+ chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
+
+ if (!CertGetCertificateChain(NULL, serverCert, NULL, serverCert->hCertStore,
+ &chainPara, 0, NULL, &chainContext))
+ {
+ status = GetLastError();
+ DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status));
+ goto cleanup;
+ }
+
+ /*
+ * Validate certificate chain.
+ */
+ ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
+ httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData);
+ httpsPolicy.dwAuthType = AUTHTYPE_SERVER;
+ httpsPolicy.fdwChecks = dwCertFlags;
+ httpsPolicy.pwszServerName = serverNameUnicode;
+
+ memset(&policyPara, 0, sizeof(policyPara));
+ policyPara.cbSize = sizeof(policyPara);
+ policyPara.pvExtraPolicyPara = &httpsPolicy;
+
+ memset(&policyStatus, 0, sizeof(policyStatus));
+ policyStatus.cbSize = sizeof(policyStatus);
+
+ if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext,
+ &policyPara, &policyStatus))
+ {
+ status = GetLastError();
+ DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status));
+ goto cleanup;
+ }
+
+ if (policyStatus.dwError)
+ {
+ status = policyStatus.dwError;
+ goto cleanup;
+ }
+
+ status = SEC_E_OK;
+
+cleanup:
+
+ if (chainContext)
+ CertFreeCertificateChain(chainContext);
+
+ if (serverNameUnicode)
+ LocalFree(serverNameUnicode);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: sspi.c 11760 2014-03-28 12:58:24Z msweet $".
+ */
diff --git a/cups/libs/cups/string-private.h b/cups/libs/cups/string-private.h
new file mode 100644
index 000000000..1b3a86388
--- /dev/null
+++ b/cups/libs/cups/string-private.h
@@ -0,0 +1,222 @@
+/*
+ * "$Id: string-private.h 11890 2014-05-22 13:59:21Z msweet $"
+ *
+ * Private string definitions for CUPS.
+ *
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_STRING_PRIVATE_H_
+# define _CUPS_STRING_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <stdarg.h>
+# include <ctype.h>
+# include <errno.h>
+# include <locale.h>
+# include <time.h>
+
+# include "config.h"
+
+# ifdef HAVE_STRING_H
+# include <string.h>
+# endif /* HAVE_STRING_H */
+
+# ifdef HAVE_STRINGS_H
+# include <strings.h>
+# endif /* HAVE_STRINGS_H */
+
+# ifdef HAVE_BSTRING_H
+# include <bstring.h>
+# endif /* HAVE_BSTRING_H */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * String pool structures...
+ */
+
+# define _CUPS_STR_GUARD 0x12344321
+
+typedef struct _cups_sp_item_s /**** String Pool Item ****/
+{
+# ifdef DEBUG_GUARDS
+ unsigned int guard; /* Guard word */
+# endif /* DEBUG_GUARDS */
+ unsigned int ref_count; /* Reference count */
+ char str[1]; /* String */
+} _cups_sp_item_t;
+
+
+/*
+ * Replacements for the ctype macros that are not affected by locale, since we
+ * really only care about testing for ASCII characters when parsing files, etc.
+ *
+ * The _CUPS_INLINE definition controls whether we get an inline function body,
+ * and external function body, or an external definition.
+ */
+
+# if defined(__GNUC__) || __STDC_VERSION__ >= 199901L
+# define _CUPS_INLINE static inline
+# elif defined(_MSC_VER)
+# define _CUPS_INLINE static __inline
+# elif defined(_CUPS_STRING_C_)
+# define _CUPS_INLINE
+# endif /* __GNUC__ || __STDC_VERSION__ */
+
+# ifdef _CUPS_INLINE
+_CUPS_INLINE int /* O - 1 on match, 0 otherwise */
+_cups_isalnum(int ch) /* I - Character to test */
+{
+ return ((ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= 'a' && ch <= 'z'));
+}
+
+_CUPS_INLINE int /* O - 1 on match, 0 otherwise */
+_cups_isalpha(int ch) /* I - Character to test */
+{
+ return ((ch >= 'A' && ch <= 'Z') ||
+ (ch >= 'a' && ch <= 'z'));
+}
+
+_CUPS_INLINE int /* O - 1 on match, 0 otherwise */
+_cups_islower(int ch) /* I - Character to test */
+{
+ return (ch >= 'a' && ch <= 'z');
+}
+
+_CUPS_INLINE int /* O - 1 on match, 0 otherwise */
+_cups_isspace(int ch) /* I - Character to test */
+{
+ return (ch == ' ' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' ||
+ ch == '\v');
+}
+
+_CUPS_INLINE int /* O - 1 on match, 0 otherwise */
+_cups_isupper(int ch) /* I - Character to test */
+{
+ return (ch >= 'A' && ch <= 'Z');
+}
+
+_CUPS_INLINE int /* O - Converted character */
+_cups_tolower(int ch) /* I - Character to convert */
+{
+ return (_cups_isupper(ch) ? ch - 'A' + 'a' : ch);
+}
+
+_CUPS_INLINE int /* O - Converted character */
+_cups_toupper(int ch) /* I - Character to convert */
+{
+ return (_cups_islower(ch) ? ch - 'a' + 'A' : ch);
+}
+# else
+extern int _cups_isalnum(int ch);
+extern int _cups_isalpha(int ch);
+extern int _cups_islower(int ch);
+extern int _cups_isspace(int ch);
+extern int _cups_isupper(int ch);
+extern int _cups_tolower(int ch);
+extern int _cups_toupper(int ch);
+# endif /* _CUPS_INLINE */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void _cups_strcpy(char *dst, const char *src);
+
+# ifndef HAVE_STRDUP
+extern char *_cups_strdup(const char *);
+# define strdup _cups_strdup
+# endif /* !HAVE_STRDUP */
+
+extern int _cups_strcasecmp(const char *, const char *);
+
+extern int _cups_strncasecmp(const char *, const char *, size_t n);
+
+# ifndef HAVE_STRLCAT
+extern size_t _cups_strlcat(char *, const char *, size_t);
+# define strlcat _cups_strlcat
+# endif /* !HAVE_STRLCAT */
+
+# ifndef HAVE_STRLCPY
+extern size_t _cups_strlcpy(char *, const char *, size_t);
+# define strlcpy _cups_strlcpy
+# endif /* !HAVE_STRLCPY */
+
+# ifndef HAVE_SNPRINTF
+extern int _cups_snprintf(char *, size_t, const char *, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+# define snprintf _cups_snprintf
+# endif /* !HAVE_SNPRINTF */
+
+# ifndef HAVE_VSNPRINTF
+extern int _cups_vsnprintf(char *, size_t, const char *, va_list);
+# define vsnprintf _cups_vsnprintf
+# endif /* !HAVE_VSNPRINTF */
+
+/*
+ * String pool functions...
+ */
+
+extern char *_cupsStrAlloc(const char *s);
+extern void _cupsStrFlush(void);
+extern void _cupsStrFree(const char *s);
+extern char *_cupsStrRetain(const char *s);
+extern size_t _cupsStrStatistics(size_t *alloc_bytes, size_t *total_bytes);
+
+
+/*
+ * Floating point number functions...
+ */
+
+extern char *_cupsStrFormatd(char *buf, char *bufend, double number,
+ struct lconv *loc);
+extern double _cupsStrScand(const char *buf, char **bufptr,
+ struct lconv *loc);
+
+
+/*
+ * Date function...
+ */
+
+extern char *_cupsStrDate(char *buf, size_t bufsize, time_t timeval);
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_STRING_H_ */
+
+/*
+ * End of "$Id: string-private.h 11890 2014-05-22 13:59:21Z msweet $".
+ */
diff --git a/cups/libs/cups/string.c b/cups/libs/cups/string.c
new file mode 100644
index 000000000..f66d59810
--- /dev/null
+++ b/cups/libs/cups/string.c
@@ -0,0 +1,793 @@
+/*
+ * "$Id: string.c 11890 2014-05-22 13:59:21Z msweet $"
+ *
+ * String functions for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsStrAlloc() - Allocate/reference a string.
+ * _cupsStrFlush() - Flush the string pool.
+ * _cupsStrFormatd() - Format a floating-point number.
+ * _cupsStrFree() - Free/dereference a string.
+ * _cupsStrRetain() - Increment the reference count of a string.
+ * _cupsStrScand() - Scan a string for a floating-point number.
+ * _cupsStrStatistics() - Return allocation statistics for string pool.
+ * _cups_strcpy() - Copy a string allowing for overlapping strings.
+ * _cups_strdup() - Duplicate a string.
+ * _cups_strcasecmp() - Do a case-insensitive comparison.
+ * _cups_strncasecmp() - Do a case-insensitive comparison on up to N chars.
+ * _cups_strlcat() - Safely concatenate two strings.
+ * _cups_strlcpy() - Safely copy two strings.
+ * compare_sp_items() - Compare two string pool items...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#define _CUPS_STRING_C_
+#include "cups-private.h"
+#include <stddef.h>
+#include <limits.h>
+
+
+/*
+ * Local globals...
+ */
+
+static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER;
+ /* Mutex to control access to pool */
+static cups_array_t *stringpool = NULL;
+ /* Global string pool */
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b);
+
+
+/*
+ * '_cupsStrAlloc()' - Allocate/reference a string.
+ */
+
+char * /* O - String pointer */
+_cupsStrAlloc(const char *s) /* I - String */
+{
+ size_t slen; /* Length of string */
+ _cups_sp_item_t *item, /* String pool item */
+ *key; /* Search key */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!s)
+ return (NULL);
+
+ /*
+ * Get the string pool...
+ */
+
+ _cupsMutexLock(&sp_mutex);
+
+ if (!stringpool)
+ stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
+
+ if (!stringpool)
+ {
+ _cupsMutexUnlock(&sp_mutex);
+
+ return (NULL);
+ }
+
+ /*
+ * See if the string is already in the pool...
+ */
+
+ key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
+
+ if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
+ {
+ /*
+ * Found it, return the cached string...
+ */
+
+ item->ref_count ++;
+
+#ifdef DEBUG_GUARDS
+ DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, "
+ "ref_count=%d", item, item->str, s, item->guard,
+ item->ref_count));
+
+ if (item->guard != _CUPS_STR_GUARD)
+ abort();
+#endif /* DEBUG_GUARDS */
+
+ _cupsMutexUnlock(&sp_mutex);
+
+ return (item->str);
+ }
+
+ /*
+ * Not found, so allocate a new one...
+ */
+
+ slen = strlen(s);
+ item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
+ if (!item)
+ {
+ _cupsMutexUnlock(&sp_mutex);
+
+ return (NULL);
+ }
+
+ item->ref_count = 1;
+ memcpy(item->str, s, slen + 1);
+
+#ifdef DEBUG_GUARDS
+ item->guard = _CUPS_STR_GUARD;
+
+ DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, "
+ "ref_count=%d", item, item->str, s, item->guard,
+ item->ref_count));
+#endif /* DEBUG_GUARDS */
+
+ /*
+ * Add the string to the pool and return it...
+ */
+
+ cupsArrayAdd(stringpool, item);
+
+ _cupsMutexUnlock(&sp_mutex);
+
+ return (item->str);
+}
+
+
+/*
+ * '_cupsStrDate()' - Return a localized date for a given time value.
+ *
+ * This function works around the locale encoding issues of strftime...
+ */
+
+char * /* O - Buffer */
+_cupsStrDate(char *buf, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ time_t timeval) /* I - Time value */
+{
+ struct tm *dateval; /* Local date/time */
+ char temp[1024]; /* Temporary buffer */
+ _cups_globals_t *cg = _cupsGlobals(); /* Per-thread globals */
+
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
+ dateval = localtime(&timeval);
+
+ if (cg->lang_default->encoding != CUPS_UTF8)
+ {
+ strftime(temp, sizeof(temp), "%c", dateval);
+ cupsCharsetToUTF8((cups_utf8_t *)buf, temp, (int)bufsize, cg->lang_default->encoding);
+ }
+ else
+ strftime(buf, bufsize, "%c", dateval);
+
+ return (buf);
+}
+
+
+/*
+ * '_cupsStrFlush()' - Flush the string pool.
+ */
+
+void
+_cupsStrFlush(void)
+{
+ _cups_sp_item_t *item; /* Current item */
+
+
+ DEBUG_printf(("4_cupsStrFlush: %d strings in array",
+ cupsArrayCount(stringpool)));
+
+ _cupsMutexLock(&sp_mutex);
+
+ for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
+ item;
+ item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
+ free(item);
+
+ cupsArrayDelete(stringpool);
+ stringpool = NULL;
+
+ _cupsMutexUnlock(&sp_mutex);
+}
+
+
+/*
+ * '_cupsStrFormatd()' - Format a floating-point number.
+ */
+
+char * /* O - Pointer to end of string */
+_cupsStrFormatd(char *buf, /* I - String */
+ char *bufend, /* I - End of string buffer */
+ double number, /* I - Number to format */
+ struct lconv *loc) /* I - Locale data */
+{
+ char *bufptr, /* Pointer into buffer */
+ temp[1024], /* Temporary string */
+ *tempdec, /* Pointer to decimal point */
+ *tempptr; /* Pointer into temporary string */
+ const char *dec; /* Decimal point */
+ int declen; /* Length of decimal point */
+
+
+ /*
+ * Format the number using the "%.12f" format and then eliminate
+ * unnecessary trailing 0's.
+ */
+
+ snprintf(temp, sizeof(temp), "%.12f", number);
+ for (tempptr = temp + strlen(temp) - 1;
+ tempptr > temp && *tempptr == '0';
+ *tempptr-- = '\0');
+
+ /*
+ * Next, find the decimal point...
+ */
+
+ if (loc && loc->decimal_point)
+ {
+ dec = loc->decimal_point;
+ declen = (int)strlen(dec);
+ }
+ else
+ {
+ dec = ".";
+ declen = 1;
+ }
+
+ if (declen == 1)
+ tempdec = strchr(temp, *dec);
+ else
+ tempdec = strstr(temp, dec);
+
+ /*
+ * Copy everything up to the decimal point...
+ */
+
+ if (tempdec)
+ {
+ for (tempptr = temp, bufptr = buf;
+ tempptr < tempdec && bufptr < bufend;
+ *bufptr++ = *tempptr++);
+
+ tempptr += declen;
+
+ if (*tempptr && bufptr < bufend)
+ {
+ *bufptr++ = '.';
+
+ while (*tempptr && bufptr < bufend)
+ *bufptr++ = *tempptr++;
+ }
+
+ *bufptr = '\0';
+ }
+ else
+ {
+ strlcpy(buf, temp, bufend - buf + 1);
+ bufptr = buf + strlen(buf);
+ }
+
+ return (bufptr);
+}
+
+
+/*
+ * '_cupsStrFree()' - Free/dereference a string.
+ */
+
+void
+_cupsStrFree(const char *s) /* I - String to free */
+{
+ _cups_sp_item_t *item, /* String pool item */
+ *key; /* Search key */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!s)
+ return;
+
+ /*
+ * Check the string pool...
+ *
+ * We don't need to lock the mutex yet, as we only want to know if
+ * the stringpool is initialized. The rest of the code will still
+ * work if it is initialized before we lock...
+ */
+
+ if (!stringpool)
+ return;
+
+ /*
+ * See if the string is already in the pool...
+ */
+
+ _cupsMutexLock(&sp_mutex);
+
+ key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
+
+#ifdef DEBUG_GUARDS
+ if (key->guard != _CUPS_STR_GUARD)
+ {
+ DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, "
+ "ref_count=%d", key, key->str, key->guard, key->ref_count));
+ abort();
+ }
+#endif /* DEBUG_GUARDS */
+
+ if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
+ item == key)
+ {
+ /*
+ * Found it, dereference...
+ */
+
+ item->ref_count --;
+
+ if (!item->ref_count)
+ {
+ /*
+ * Remove and free...
+ */
+
+ cupsArrayRemove(stringpool, item);
+
+ free(item);
+ }
+ }
+
+ _cupsMutexUnlock(&sp_mutex);
+}
+
+
+/*
+ * '_cupsStrRetain()' - Increment the reference count of a string.
+ *
+ * Note: This function does not verify that the passed pointer is in the
+ * string pool, so any calls to it MUST know they are passing in a
+ * good pointer.
+ */
+
+char * /* O - Pointer to string */
+_cupsStrRetain(const char *s) /* I - String to retain */
+{
+ _cups_sp_item_t *item; /* Pointer to string pool item */
+
+
+ if (s)
+ {
+ item = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
+
+#ifdef DEBUG_GUARDS
+ if (item->guard != _CUPS_STR_GUARD)
+ {
+ DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, "
+ "ref_count=%d", item, s, item->guard, item->ref_count));
+ abort();
+ }
+#endif /* DEBUG_GUARDS */
+
+ _cupsMutexLock(&sp_mutex);
+
+ item->ref_count ++;
+
+ _cupsMutexUnlock(&sp_mutex);
+ }
+
+ return ((char *)s);
+}
+
+
+/*
+ * '_cupsStrScand()' - Scan a string for a floating-point number.
+ *
+ * This function handles the locale-specific BS so that a decimal
+ * point is always the period (".")...
+ */
+
+double /* O - Number */
+_cupsStrScand(const char *buf, /* I - Pointer to number */
+ char **bufptr, /* O - New pointer or NULL on error */
+ struct lconv *loc) /* I - Locale data */
+{
+ char temp[1024], /* Temporary buffer */
+ *tempptr; /* Pointer into temporary buffer */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!buf)
+ return (0.0);
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (_cups_isspace(*buf))
+ buf ++;
+
+ /*
+ * Copy leading sign, numbers, period, and then numbers...
+ */
+
+ tempptr = temp;
+ if (*buf == '-' || *buf == '+')
+ *tempptr++ = *buf++;
+
+ while (isdigit(*buf & 255))
+ if (tempptr < (temp + sizeof(temp) - 1))
+ *tempptr++ = *buf++;
+ else
+ {
+ if (bufptr)
+ *bufptr = NULL;
+
+ return (0.0);
+ }
+
+ if (*buf == '.')
+ {
+ /*
+ * Read fractional portion of number...
+ */
+
+ buf ++;
+
+ if (loc && loc->decimal_point)
+ {
+ strlcpy(tempptr, loc->decimal_point, sizeof(temp) - (tempptr - temp));
+ tempptr += strlen(tempptr);
+ }
+ else if (tempptr < (temp + sizeof(temp) - 1))
+ *tempptr++ = '.';
+ else
+ {
+ if (bufptr)
+ *bufptr = NULL;
+
+ return (0.0);
+ }
+
+ while (isdigit(*buf & 255))
+ if (tempptr < (temp + sizeof(temp) - 1))
+ *tempptr++ = *buf++;
+ else
+ {
+ if (bufptr)
+ *bufptr = NULL;
+
+ return (0.0);
+ }
+ }
+
+ if (*buf == 'e' || *buf == 'E')
+ {
+ /*
+ * Read exponent...
+ */
+
+ if (tempptr < (temp + sizeof(temp) - 1))
+ *tempptr++ = *buf++;
+ else
+ {
+ if (bufptr)
+ *bufptr = NULL;
+
+ return (0.0);
+ }
+
+ if (*buf == '+' || *buf == '-')
+ {
+ if (tempptr < (temp + sizeof(temp) - 1))
+ *tempptr++ = *buf++;
+ else
+ {
+ if (bufptr)
+ *bufptr = NULL;
+
+ return (0.0);
+ }
+ }
+
+ while (isdigit(*buf & 255))
+ if (tempptr < (temp + sizeof(temp) - 1))
+ *tempptr++ = *buf++;
+ else
+ {
+ if (bufptr)
+ *bufptr = NULL;
+
+ return (0.0);
+ }
+ }
+
+ /*
+ * Nul-terminate the temporary string and return the value...
+ */
+
+ if (bufptr)
+ *bufptr = (char *)buf;
+
+ *tempptr = '\0';
+
+ return (strtod(temp, NULL));
+}
+
+
+/*
+ * '_cupsStrStatistics()' - Return allocation statistics for string pool.
+ */
+
+size_t /* O - Number of strings */
+_cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
+ size_t *total_bytes) /* O - Total string bytes */
+{
+ size_t count, /* Number of strings */
+ abytes, /* Allocated string bytes */
+ tbytes, /* Total string bytes */
+ len; /* Length of string */
+ _cups_sp_item_t *item; /* Current item */
+
+
+ /*
+ * Loop through strings in pool, counting everything up...
+ */
+
+ _cupsMutexLock(&sp_mutex);
+
+ for (count = 0, abytes = 0, tbytes = 0,
+ item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
+ item;
+ item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
+ {
+ /*
+ * Count allocated memory, using a 64-bit aligned buffer as a basis.
+ */
+
+ count += item->ref_count;
+ len = (strlen(item->str) + 8) & ~7;
+ abytes += sizeof(_cups_sp_item_t) + len;
+ tbytes += item->ref_count * len;
+ }
+
+ _cupsMutexUnlock(&sp_mutex);
+
+ /*
+ * Return values...
+ */
+
+ if (alloc_bytes)
+ *alloc_bytes = abytes;
+
+ if (total_bytes)
+ *total_bytes = tbytes;
+
+ return (count);
+}
+
+
+/*
+ * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
+ */
+
+void
+_cups_strcpy(char *dst, /* I - Destination string */
+ const char *src) /* I - Source string */
+{
+ while (*src)
+ *dst++ = *src++;
+
+ *dst = '\0';
+}
+
+
+/*
+ * '_cups_strdup()' - Duplicate a string.
+ */
+
+#ifndef HAVE_STRDUP
+char * /* O - New string pointer */
+_cups_strdup(const char *s) /* I - String to duplicate */
+{
+ char *t; /* New string pointer */
+ size_t slen; /* Length of string */
+
+
+ if (!s)
+ return (NULL);
+
+ slen = strlen(s);
+ if ((t = malloc(slen + 1)) == NULL)
+ return (NULL);
+
+ return (memcpy(t, s, slen + 1));
+}
+#endif /* !HAVE_STRDUP */
+
+
+/*
+ * '_cups_strcasecmp()' - Do a case-insensitive comparison.
+ */
+
+int /* O - Result of comparison (-1, 0, or 1) */
+_cups_strcasecmp(const char *s, /* I - First string */
+ const char *t) /* I - Second string */
+{
+ while (*s != '\0' && *t != '\0')
+ {
+ if (_cups_tolower(*s) < _cups_tolower(*t))
+ return (-1);
+ else if (_cups_tolower(*s) > _cups_tolower(*t))
+ return (1);
+
+ s ++;
+ t ++;
+ }
+
+ if (*s == '\0' && *t == '\0')
+ return (0);
+ else if (*s != '\0')
+ return (1);
+ else
+ return (-1);
+}
+
+/*
+ * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
+ */
+
+int /* O - Result of comparison (-1, 0, or 1) */
+_cups_strncasecmp(const char *s, /* I - First string */
+ const char *t, /* I - Second string */
+ size_t n) /* I - Maximum number of characters to compare */
+{
+ while (*s != '\0' && *t != '\0' && n > 0)
+ {
+ if (_cups_tolower(*s) < _cups_tolower(*t))
+ return (-1);
+ else if (_cups_tolower(*s) > _cups_tolower(*t))
+ return (1);
+
+ s ++;
+ t ++;
+ n --;
+ }
+
+ if (n == 0)
+ return (0);
+ else if (*s == '\0' && *t == '\0')
+ return (0);
+ else if (*s != '\0')
+ return (1);
+ else
+ return (-1);
+}
+
+
+#ifndef HAVE_STRLCAT
+/*
+ * '_cups_strlcat()' - Safely concatenate two strings.
+ */
+
+size_t /* O - Length of string */
+_cups_strlcat(char *dst, /* O - Destination string */
+ const char *src, /* I - Source string */
+ size_t size) /* I - Size of destination string buffer */
+{
+ size_t srclen; /* Length of source string */
+ size_t dstlen; /* Length of destination string */
+
+
+ /*
+ * Figure out how much room is left...
+ */
+
+ dstlen = strlen(dst);
+ size -= dstlen + 1;
+
+ if (!size)
+ return (dstlen); /* No room, return immediately... */
+
+ /*
+ * Figure out how much room is needed...
+ */
+
+ srclen = strlen(src);
+
+ /*
+ * Copy the appropriate amount...
+ */
+
+ if (srclen > size)
+ srclen = size;
+
+ memcpy(dst + dstlen, src, srclen);
+ dst[dstlen + srclen] = '\0';
+
+ return (dstlen + srclen);
+}
+#endif /* !HAVE_STRLCAT */
+
+
+#ifndef HAVE_STRLCPY
+/*
+ * '_cups_strlcpy()' - Safely copy two strings.
+ */
+
+size_t /* O - Length of string */
+_cups_strlcpy(char *dst, /* O - Destination string */
+ const char *src, /* I - Source string */
+ size_t size) /* I - Size of destination string buffer */
+{
+ size_t srclen; /* Length of source string */
+
+
+ /*
+ * Figure out how much room is needed...
+ */
+
+ size --;
+
+ srclen = strlen(src);
+
+ /*
+ * Copy the appropriate amount...
+ */
+
+ if (srclen > size)
+ srclen = size;
+
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+
+ return (srclen);
+}
+#endif /* !HAVE_STRLCPY */
+
+
+/*
+ * 'compare_sp_items()' - Compare two string pool items...
+ */
+
+static int /* O - Result of comparison */
+compare_sp_items(_cups_sp_item_t *a, /* I - First item */
+ _cups_sp_item_t *b) /* I - Second item */
+{
+ return (strcmp(a->str, b->str));
+}
+
+
+/*
+ * End of "$Id: string.c 11890 2014-05-22 13:59:21Z msweet $".
+ */
diff --git a/cups/libs/cups/tempfile.c b/cups/libs/cups/tempfile.c
new file mode 100644
index 000000000..6973702c1
--- /dev/null
+++ b/cups/libs/cups/tempfile.c
@@ -0,0 +1,233 @@
+/*
+ * "$Id: tempfile.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Temp file utilities for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsTempFd() - Creates a temporary file.
+ * cupsTempFile() - Generates a temporary filename.
+ * cupsTempFile2() - Creates a temporary CUPS file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'cupsTempFd()' - Creates a temporary file.
+ *
+ * The temporary filename is returned in the filename buffer.
+ * The temporary file is opened for reading and writing.
+ */
+
+int /* O - New file descriptor or -1 on error */
+cupsTempFd(char *filename, /* I - Pointer to buffer */
+ int len) /* I - Size of buffer */
+{
+ int fd; /* File descriptor for temp file */
+ int tries; /* Number of tries */
+ const char *tmpdir; /* TMPDIR environment var */
+#ifdef WIN32
+ char tmppath[1024]; /* Windows temporary directory */
+ DWORD curtime; /* Current time */
+#else
+ struct timeval curtime; /* Current time */
+#endif /* WIN32 */
+
+
+ /*
+ * See if TMPDIR is defined...
+ */
+
+#ifdef WIN32
+ if ((tmpdir = getenv("TEMP")) == NULL)
+ {
+ GetTempPath(sizeof(tmppath), tmppath);
+ tmpdir = tmppath;
+ }
+#else
+ /*
+ * Previously we put root temporary files in the default CUPS temporary
+ * directory under /var/spool/cups. However, since the scheduler cleans
+ * out temporary files there and runs independently of the user apps, we
+ * don't want to use it unless specifically told to by cupsd.
+ */
+
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+# ifdef __APPLE__
+ tmpdir = "/private/tmp"; /* /tmp is a symlink to /private/tmp */
+# else
+ tmpdir = "/tmp";
+# endif /* __APPLE__ */
+#endif /* WIN32 */
+
+ /*
+ * Make the temporary name using the specified directory...
+ */
+
+ tries = 0;
+
+ do
+ {
+#ifdef WIN32
+ /*
+ * Get the current time of day...
+ */
+
+ curtime = GetTickCount() + tries;
+
+ /*
+ * Format a string using the hex time values...
+ */
+
+ snprintf(filename, len - 1, "%s/%05lx%08lx", tmpdir,
+ GetCurrentProcessId(), curtime);
+#else
+ /*
+ * Get the current time of day...
+ */
+
+ gettimeofday(&curtime, NULL);
+
+ /*
+ * Format a string using the hex time values...
+ */
+
+ snprintf(filename, len - 1, "%s/%05x%08x", tmpdir, (unsigned)getpid(),
+ (unsigned)(curtime.tv_sec + curtime.tv_usec + tries));
+#endif /* WIN32 */
+
+ /*
+ * Open the file in "exclusive" mode, making sure that we don't
+ * stomp on an existing file or someone's symlink crack...
+ */
+
+#ifdef WIN32
+ fd = open(filename, _O_CREAT | _O_RDWR | _O_TRUNC | _O_BINARY,
+ _S_IREAD | _S_IWRITE);
+#elif defined(O_NOFOLLOW)
+ fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
+#else
+ fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
+#endif /* WIN32 */
+
+ if (fd < 0 && errno != EEXIST)
+ break;
+
+ tries ++;
+ }
+ while (fd < 0 && tries < 1000);
+
+ /*
+ * Return the file descriptor...
+ */
+
+ return (fd);
+}
+
+
+/*
+ * 'cupsTempFile()' - Generates a temporary filename.
+ *
+ * The temporary filename is returned in the filename buffer.
+ * This function is deprecated - use @link cupsTempFd@ or
+ * @link cupsTempFile2@ instead.
+ *
+ * @deprecated@
+ */
+
+char * /* O - Filename or @code NULL@ on error */
+cupsTempFile(char *filename, /* I - Pointer to buffer */
+ int len) /* I - Size of buffer */
+{
+ int fd; /* File descriptor for temp file */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * See if a filename was specified...
+ */
+
+ if (filename == NULL)
+ {
+ filename = cg->tempfile;
+ len = sizeof(cg->tempfile);
+ }
+
+ /*
+ * Create the temporary file...
+ */
+
+ if ((fd = cupsTempFd(filename, len)) < 0)
+ return (NULL);
+
+ /*
+ * Close the temp file - it'll be reopened later as needed...
+ */
+
+ close(fd);
+
+ /*
+ * Return the temp filename...
+ */
+
+ return (filename);
+}
+
+
+/*
+ * 'cupsTempFile2()' - Creates a temporary CUPS file.
+ *
+ * The temporary filename is returned in the filename buffer.
+ * The temporary file is opened for writing.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+cups_file_t * /* O - CUPS file or @code NULL@ on error */
+cupsTempFile2(char *filename, /* I - Pointer to buffer */
+ int len) /* I - Size of buffer */
+{
+ cups_file_t *file; /* CUPS file */
+ int fd; /* File descriptor */
+
+
+ if ((fd = cupsTempFd(filename, len)) < 0)
+ return (NULL);
+ else if ((file = cupsFileOpenFd(fd, "w")) == NULL)
+ {
+ close(fd);
+ unlink(filename);
+ return (NULL);
+ }
+ else
+ return (file);
+}
+
+
+/*
+ * End of "$Id: tempfile.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/test.ppd b/cups/libs/cups/test.ppd
new file mode 100644
index 000000000..fc453a649
--- /dev/null
+++ b/cups/libs/cups/test.ppd
@@ -0,0 +1,262 @@
+*PPD-Adobe: "4.3"
+*%
+*% "$Id: test.ppd 7819 2008-08-01 00:27:24Z mike $"
+*%
+*% Test PPD file for CUPS.
+*%
+*% This file is used to test the CUPS PPD API functions and cannot be
+*% used with any known printers. Look on the CUPS web site for working PPD
+*% files.
+*%
+*% If you are a PPD file developer, consider using the PPD compiler (ppdc)
+*% to create your PPD files - not only will it save you time, it produces
+*% consistently high-quality files.
+*%
+*% Copyright 2007-2010 by Apple Inc.
+*% Copyright 2002-2006 by Easy Software Products.
+*%
+*% These coded instructions, statements, and computer programs are the
+*% property of Apple Inc. and are protected by Federal copyright
+*% law. Distribution and use rights are outlined in the file "LICENSE.txt"
+*% which should have been included with this file. If this file is
+*% file is missing or damaged, see the license at "http://www.cups.org/".
+*FormatVersion: "4.3"
+*FileVersion: "1.3"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "TEST.PPD"
+*Manufacturer: "ESP"
+*Product: "(Test)"
+*cupsVersion: 1.4
+*ModelName: "Test"
+*ShortNickName: "Test"
+*NickName: "Test for CUPS"
+*PSVersion: "(3010.000) 0"
+*LanguageLevel: "3"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*TTRasterizer: Type42
+*cupsFilter: "application/vnd.cups-raster 0 -"
+*RequiresPageRegion All: True
+
+*% These constraints are used to test ppdConflicts() and cupsResolveConflicts()
+*UIConstraints: *PageSize Letter *InputSlot Envelope
+*UIConstraints: *InputSlot Envelope *PageSize Letter
+*UIConstraints: *PageRegion Letter *InputSlot Envelope
+*UIConstraints: *InputSlot Envelope *PageRegion Letter
+
+*% These constraints are used to test ppdInstallableConflict()
+*UIConstraints: "*Duplex *InstalledDuplexer False"
+*UIConstraints: "*InstalledDuplexer False *Duplex"
+
+*% These attributes test ppdFindAttr/ppdFindNext...
+*cupsTest Foo/I Love Foo: ""
+*cupsTest Bar/I Love Bar: ""
+
+*% For PageSize, we have put all of the translations in-line...
+*OpenUI *PageSize/Page Size: PickOne
+*fr.Translation PageSize/French Page Size: ""
+*fr_CA.Translation PageSize/French Canadian Page Size: ""
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter/US Letter: "PageSize=Letter"
+*fr.PageSize Letter/French US Letter: ""
+*fr_CA.PageSize Letter/French Canadian US Letter: ""
+*PageSize Letter.Banner/US Letter Banner: "PageSize=Letter.Banner"
+*fr.PageSize Letter.Banner/French US Letter Banner: ""
+*fr_CA.PageSize Letter.Banner/French Canadian US Letter Banner: ""
+*PageSize Letter.Fullbleed/US Letter Borderless: "PageSize=Letter.Fullbleed"
+*fr.PageSize Letter.Fullbleed/French US Letter Borderless: ""
+*fr_CA.PageSize Letter.Fullbleed/French Canadian US Letter Borderless: ""
+*PageSize A4/A4: "PageSize=A4"
+*fr.PageSize A4/French A4: ""
+*fr_CA.PageSize A4/French Canadian A4: ""
+*PageSize Env10/#10 Envelope: "PageSize=Env10"
+*fr.PageSize Env10/French #10 Envelope: ""
+*fr_CA.PageSize Env10/French Canadian #10 Envelope: ""
+*CloseUI: *PageSize
+
+*% For PageRegion, we have separated the translations...
+*OpenUI *PageRegion/Page Region: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter/US Letter: "PageRegion=Letter"
+*PageRegion Letter.Banner/US Letter Banner: "PageRegion=Letter.Fullbleed"
+*PageRegion Letter.Fullbleed/US Letter Borderless: "PageRegion=Letter.Fullbleed"
+*PageRegion A4/A4: "PageRegion=A4"
+*PageRegion Env10/#10 Envelope: "PageRegion=Env10"
+*CloseUI: *PageRegion
+
+*fr.Translation PageRegion/French Page Region: ""
+*fr.PageRegion Letter/French US Letter: ""
+*fr.PageRegion Letter.Banner/French US Letter Banner: ""
+*fr.PageRegion Letter.Fullbleed/French US Letter Borderless: ""
+*fr.PageRegion A4/French A4: ""
+*fr.PageRegion Env10/French #10 Envelope: ""
+
+*fr_CA.Translation PageRegion/French Canadian Page Region: ""
+*fr_CA.PageRegion Letter/French Canadian US Letter: ""
+*fr_CA.PageRegion Letter.Banner/French Canadian US Letter Banner: ""
+*fr_CA.PageRegion Letter.Fullbleed/French Canadian US Letter Borderless: ""
+*fr_CA.PageRegion A4/French Canadian A4: ""
+*fr_CA.PageRegion Env10/French Canadian #10 Envelope: ""
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter: "18 36 594 756"
+*ImageableArea Letter.Banner: "18 0 594 792"
+*ImageableArea Letter.Fullbleed: "0 0 612 792"
+*ImageableArea A4: "18 36 577 806"
+*ImageableArea Env10: "18 36 279 648"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter: "612 792"
+*PaperDimension Letter.Banner: "612 792"
+*PaperDimension Letter.Fullbleed: "612 792"
+*PaperDimension A4: "595 842"
+*PaperDimension Env10: "297 684"
+
+*% Custom page size support
+*HWMargins: 0 0 0 0
+*NonUIOrderDependency: 100 AnySetup *CustomPageSize True
+*CustomPageSize True/Custom Page Size: "PageSize=Custom"
+*ParamCustomPageSize Width: 1 points 36 1080
+*ParamCustomPageSize Height: 2 points 36 86400
+*ParamCustomPageSize WidthOffset/Width Offset: 3 points 0 0
+*ParamCustomPageSize HeightOffset/Height Offset: 4 points 0 0
+*ParamCustomPageSize Orientation: 5 int 0 0
+
+*OpenUI *InputSlot/Input Slot: PickOne
+*OrderDependency: 20 AnySetup *InputSlot
+*DefaultInputSlot: Tray
+*InputSlot Tray/Tray: "InputSlot=Tray"
+*InputSlot Manual/Manual Feed: "InputSlot=Manual"
+*InputSlot Envelope/Envelope Feed: "InputSlot=Envelope"
+*CloseUI: *InputSlot
+
+*OpenUI *MediaType/Media Type: PickOne
+*OrderDependency: 25 AnySetup *MediaType
+*DefaultMediaType: Plain
+*MediaType Plain/Plain Paper: "MediaType=Plain"
+*MediaType Matte/Matte Photo: "MediaType=Matte"
+*MediaType Glossy/Glossy Photo: "MediaType=Glossy"
+*MediaType Transparency/Transparency Film: "MediaType=Transparency"
+*CloseUI: *MediaType
+
+*OpenUI *Duplex/2-Sided Printing: PickOne
+*OrderDependency: 10 DocumentSetup *Duplex
+*DefaultDuplex: None
+*Duplex None/Off: "Duplex=None"
+*Duplex DuplexNoTumble/Long Edge: "Duplex=DuplexNoTumble"
+*Duplex DuplexTumble/Short Edge: "Duplex=DuplexTumble"
+*CloseUI: *Duplex
+
+*% Installable option...
+*OpenGroup: InstallableOptions/Installable Options
+*OpenUI InstalledDuplexer/Duplexer Installed: Boolean
+*DefaultInstalledDuplexer: False
+*InstalledDuplexer False: ""
+*InstalledDuplexer True: ""
+*CloseUI: *InstalledDuplexer
+*CloseGroup: InstallableOptions
+
+*% Custom options...
+*OpenGroup: Extended/Extended Options
+
+*OpenUI IntOption/Integer: PickOne
+*OrderDependency: 30 AnySetup *IntOption
+*DefaultIntOption: None
+*IntOption None: ""
+*IntOption 1: "IntOption=1"
+*IntOption 2: "IntOption=2"
+*IntOption 3: "IntOption=3"
+*CloseUI: *IntOption
+
+*CustomIntOption True/Custom Integer: "IntOption=Custom"
+*ParamCustomIntOption Integer: 1 int -100 100
+
+*OpenUI StringOption/String: PickOne
+*OrderDependency: 40 AnySetup *StringOption
+*DefaultStringOption: None
+*StringOption None: ""
+*StringOption foo: "StringOption=foo"
+*StringOption bar: "StringOption=bar"
+*CloseUI: *StringOption
+
+*CustomStringOption True/Custom String: "StringOption=Custom"
+*ParamCustomStringOption String1: 2 string 1 10
+*ParamCustomStringOption String2: 1 string 1 10
+
+*CloseGroup: Extended
+
+*% IPP reasons for ppdLocalizeIPPReason tests
+*cupsIPPReason foo/Foo Reason: "http://foo/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/foo/bar.html"
+*End
+*fr.cupsIPPReason foo/La Foo Reason: "text:La%20Long%20
+text:Foo%20Reason
+http://foo/fr/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/fr/foo/bar.html"
+*End
+*zh_TW.cupsIPPReason foo/Number 1 Foo Reason: "text:Number%201%20
+text:Foo%20Reason
+http://foo/zh_TW/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/zh_TW/foo/bar.html"
+*End
+*zh.cupsIPPReason foo/Number 2 Foo Reason: "text:Number%202%20
+text:Foo%20Reason
+http://foo/zh/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/zh/foo/bar.html"
+*End
+
+*% Marker names for ppdLocalizeMarkerName tests
+*cupsMarkerName cyan/Cyan Toner: ""
+*fr.cupsMarkerName cyan/La Toner Cyan: ""
+*zh_TW.cupsMarkerName cyan/Number 1 Cyan Toner: ""
+*zh.cupsMarkerName cyan/Number 2 Cyan Toner: ""
+
+*DefaultFont: Courier
+*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM
+*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM
+*Font Bookman-Demi: Standard "(001.004S)" Standard ROM
+*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM
+*Font Bookman-Light: Standard "(001.004S)" Standard ROM
+*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM
+*Font Courier: Standard "(002.004S)" Standard ROM
+*Font Courier-Bold: Standard "(002.004S)" Standard ROM
+*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM
+*Font Courier-Oblique: Standard "(002.004S)" Standard ROM
+*Font Helvetica: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM
+*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM
+*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM
+*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
+*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
+*Font Symbol: Special "(001.007S)" Special ROM
+*Font Times-Bold: Standard "(001.007S)" Standard ROM
+*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
+*Font Times-Italic: Standard "(001.007S)" Standard ROM
+*Font Times-Roman: Standard "(001.007S)" Standard ROM
+*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM
+*Font ZapfDingbats: Special "(001.004S)" Standard ROM
+*%
+*% End of "$Id: test.ppd 7819 2008-08-01 00:27:24Z mike $".
+*%
diff --git a/cups/libs/cups/test2.ppd b/cups/libs/cups/test2.ppd
new file mode 100644
index 000000000..353afb6ef
--- /dev/null
+++ b/cups/libs/cups/test2.ppd
@@ -0,0 +1,252 @@
+*PPD-Adobe: "4.3"
+*%
+*% "$Id: test2.ppd 7791 2008-07-24 00:55:30Z mike $"
+*%
+*% Test PPD file #2 for CUPS.
+*%
+*% This file is used to test the CUPS PPD API functions and cannot be
+*% used with any known printers. Look on the CUPS web site for working PPD
+*% files.
+*%
+*% If you are a PPD file developer, consider using the PPD compiler (ppdc)
+*% to create your PPD files - not only will it save you time, it produces
+*% consistently high-quality files.
+*%
+*% Copyright 2007-2011 by Apple Inc.
+*% Copyright 2002-2006 by Easy Software Products.
+*%
+*% These coded instructions, statements, and computer programs are the
+*% property of Apple Inc. and are protected by Federal copyright
+*% law. Distribution and use rights are outlined in the file "LICENSE.txt"
+*% which should have been included with this file. If this file is
+*% file is missing or damaged, see the license at "http://www.cups.org/".
+*FormatVersion: "4.3"
+*FileVersion: "1.3"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "TEST.PPD"
+*Manufacturer: "ESP"
+*Product: "(Test2)"
+*cupsVersion: 1.4
+*ModelName: "Test2"
+*ShortNickName: "Test2"
+*NickName: "Test2 for CUPS"
+*PSVersion: "(3010.000) 0"
+*LanguageLevel: "3"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*TTRasterizer: Type42
+
+*% These constraints are used to test ppdConflicts() and cupsResolveConflicts()
+*cupsUIConstraints envelope: "*PageSize Letter *InputSlot Envelope"
+*cupsUIConstraints envelope: "*PageSize A4 *InputSlot Envelope"
+*cupsUIResolver envelope: "*InputSlot Manual *PageSize Env10"
+
+*cupsUIConstraints envphoto: "*PageSize Env10 *InputSlot Envelope *Quality Photo"
+*cupsUIResolver envphoto: "*Quality Normal"
+
+*% This constraint is used to test ppdInstallableConflict()
+*cupsUIConstraints: "*Duplex *InstalledDuplexer False"
+
+*% These constraints are used to test the loop detection code in cupsResolveConflicts()
+*cupsUIConstraints loop1: "*PageSize A4 *Quality Photo"
+*cupsUIResolver loop1: "*Quality Normal"
+*cupsUIConstraints loop2: "*PageSize A4 *Quality Normal"
+*cupsUIResolver loop2: "*Quality Photo"
+
+*% For PageSize, we have put all of the translations in-line...
+*OpenUI *PageSize/Page Size: PickOne
+*fr.Translation PageSize/French Page Size: ""
+*fr_CA.Translation PageSize/French Canadian Page Size: ""
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter/US Letter: "PageSize=Letter"
+*fr.PageSize Letter/French US Letter: ""
+*fr_CA.PageSize Letter/French Canadian US Letter: ""
+*PageSize A4/A4: "PageSize=A4"
+*fr.PageSize A4/French A4: ""
+*fr_CA.PageSize A4/French Canadian A4: ""
+*PageSize Env10/#10 Envelope: "PageSize=Env10"
+*fr.PageSize Env10/French #10 Envelope: ""
+*fr_CA.PageSize Env10/French Canadian #10 Envelope: ""
+*CloseUI: *PageSize
+
+*% For PageRegion, we have separated the translations...
+*OpenUI *PageRegion/Page Region: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter/US Letter: "PageRegion=Letter"
+*PageRegion A4/A4: "PageRegion=A4"
+*PageRegion Env10/#10 Envelope: "PageRegion=Env10"
+*CloseUI: *PageRegion
+
+*fr.Translation PageRegion/French Page Region: ""
+*fr.PageRegion Letter/French US Letter: ""
+*fr.PageRegion A4/French A4: ""
+*fr.PageRegion Env10/French #10 Envelope: ""
+
+*fr_CA.Translation PageRegion/French Canadian Page Region: ""
+*fr_CA.PageRegion Letter/French Canadian US Letter: ""
+*fr_CA.PageRegion A4/French Canadian A4: ""
+*fr_CA.PageRegion Env10/French Canadian #10 Envelope: ""
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter: "18 36 594 756"
+*ImageableArea A4: "18 36 577 806"
+*ImageableArea Env10: "18 36 279 648"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter: "612 792"
+*PaperDimension A4: "595 842"
+*PaperDimension Env10: "297 684"
+
+*% Custom page size support
+*HWMargins: 0 0 0 0
+*NonUIOrderDependency: 100 AnySetup *CustomPageSize True
+*CustomPageSize True/Custom Page Size: "PageSize=Custom"
+*ParamCustomPageSize Width: 1 points 36 1080
+*ParamCustomPageSize Height: 2 points 36 86400
+*ParamCustomPageSize WidthOffset/Width Offset: 3 points 0 0
+*ParamCustomPageSize HeightOffset/Height Offset: 4 points 0 0
+*ParamCustomPageSize Orientation: 5 int 0 0
+
+*cupsMediaQualifier2: InputSlot
+*cupsMediaQualifier3: Quality
+*cupsMaxSize .Manual.: "1000 1000"
+*cupsMinSize .Manual.: "100 100"
+*cupsMinSize .Manual.Photo: "200 200"
+*cupsMinSize ..Photo: "300 300"
+
+*OpenUI *InputSlot/Input Slot: PickOne
+*OrderDependency: 20 AnySetup *InputSlot
+*DefaultInputSlot: Tray
+*InputSlot Tray/Tray: "InputSlot=Tray"
+*InputSlot Manual/Manual Feed: "InputSlot=Manual"
+*InputSlot Envelope/Envelope Feed: "InputSlot=Envelope"
+*CloseUI: *InputSlot
+
+*OpenUI *Quality/Output Mode: PickOne
+*OrderDependency: 20 AnySetup *Quality
+*DefaultQuality: Normal
+*Quality Draft: "Quality=Draft"
+*Quality Normal: "Quality=Normal"
+*Quality Photo: "Quality=Photo"
+*CloseUI: *Quality
+
+*OpenUI *Duplex/2-Sided Printing: PickOne
+*OrderDependency: 10 DocumentSetup *Duplex
+*DefaultDuplex: None
+*Duplex None/Off: "Duplex=None"
+*Duplex DuplexNoTumble/Long Edge: "Duplex=DuplexNoTumble"
+*Duplex DuplexTumble/Short Edge: "Duplex=DuplexTumble"
+*CloseUI: *Duplex
+
+*% Installable option...
+*OpenGroup: InstallableOptions/Installable Options
+*OpenUI InstalledDuplexer/Duplexer Installed: Boolean
+*DefaultInstalledDuplexer: False
+*InstalledDuplexer False: ""
+*InstalledDuplexer True: ""
+*CloseUI: *InstalledDuplexer
+*CloseGroup: InstallableOptions
+
+*% Custom options...
+*OpenGroup: Extended/Extended Options
+
+*OpenUI IntOption/Integer: PickOne
+*OrderDependency: 30 AnySetup *IntOption
+*DefaultIntOption: None
+*IntOption None: ""
+*IntOption 1: "IntOption=1"
+*IntOption 2: "IntOption=2"
+*IntOption 3: "IntOption=3"
+*CloseUI: *IntOption
+
+*CustomIntOption True/Custom Integer: "IntOption=Custom"
+*ParamCustomIntOption Integer: 1 int -100 100
+
+*OpenUI StringOption/String: PickOne
+*OrderDependency: 40 AnySetup *StringOption
+*DefaultStringOption: None
+*StringOption None: ""
+*StringOption foo: "StringOption=foo"
+*StringOption bar: "StringOption=bar"
+*CloseUI: *StringOption
+
+*CustomStringOption True/Custom String: "StringOption=Custom"
+*ParamCustomStringOption String: 1 string 1 10
+
+*CloseGroup: Extended
+
+*% IPP reasons for ppdLocalizeIPPReason tests
+*cupsIPPReason foo/Foo Reason: "http://foo/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/foo/bar.html"
+*End
+*fr.cupsIPPReason foo/La Foo Reason: "text:La%20Long%20
+text:Foo%20Reason
+http://foo/fr/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/fr/foo/bar.html"
+*End
+*zh_TW.cupsIPPReason foo/Number 1 Foo Reason: "text:Number%201%20
+text:Foo%20Reason
+http://foo/zh_TW/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/zh_TW/foo/bar.html"
+*End
+*zh.cupsIPPReason foo/Number 2 Foo Reason: "text:Number%202%20
+text:Foo%20Reason
+http://foo/zh/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/zh/foo/bar.html"
+*End
+
+*% Marker names for ppdLocalizeMarkerName tests
+*cupsMarkerName cyan/Cyan Toner: ""
+*fr.cupsMarkerName cyan/La Toner Cyan: ""
+*zh_TW.cupsMarkerName cyan/Number 1 Cyan Toner: ""
+*zh.cupsMarkerName cyan/Number 2 Cyan Toner: ""
+
+*DefaultFont: Courier
+*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM
+*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM
+*Font Bookman-Demi: Standard "(001.004S)" Standard ROM
+*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM
+*Font Bookman-Light: Standard "(001.004S)" Standard ROM
+*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM
+*Font Courier: Standard "(002.004S)" Standard ROM
+*Font Courier-Bold: Standard "(002.004S)" Standard ROM
+*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM
+*Font Courier-Oblique: Standard "(002.004S)" Standard ROM
+*Font Helvetica: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM
+*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM
+*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM
+*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
+*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
+*Font Symbol: Special "(001.007S)" Special ROM
+*Font Times-Bold: Standard "(001.007S)" Standard ROM
+*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
+*Font Times-Italic: Standard "(001.007S)" Standard ROM
+*Font Times-Roman: Standard "(001.007S)" Standard ROM
+*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM
+*Font ZapfDingbats: Special "(001.004S)" Standard ROM
+*%
+*% End of "$Id: test2.ppd 7791 2008-07-24 00:55:30Z mike $".
+*%
diff --git a/cups/libs/cups/testadmin.c b/cups/libs/cups/testadmin.c
new file mode 100644
index 000000000..04b02f1c9
--- /dev/null
+++ b/cups/libs/cups/testadmin.c
@@ -0,0 +1,121 @@
+/*
+ * "$Id: testadmin.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Admin function test program for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * show_settings() - Show settings in the array...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "adminutil.h"
+#include "string-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void show_settings(int num_settings, cups_option_t *settings);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i, /* Looping var */
+ num_settings; /* Number of settings */
+ cups_option_t *settings; /* Settings */
+ http_t *http; /* Connection to server */
+
+
+ /*
+ * Connect to the server using the defaults...
+ */
+
+ http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
+ cupsEncryption(), 1, 30000, NULL);
+
+ /*
+ * Set the current configuration if we have anything on the command-line...
+ */
+
+ if (argc > 1)
+ {
+ for (i = 1, num_settings = 0, settings = NULL; i < argc; i ++)
+ num_settings = cupsParseOptions(argv[i], num_settings, &settings);
+
+ if (cupsAdminSetServerSettings(http, num_settings, settings))
+ {
+ puts("New server settings:");
+ cupsFreeOptions(num_settings, settings);
+ }
+ else
+ {
+ printf("Server settings not changed: %s\n", cupsLastErrorString());
+ return (1);
+ }
+ }
+ else
+ puts("Current server settings:");
+
+ /*
+ * Get the current configuration...
+ */
+
+ if (cupsAdminGetServerSettings(http, &num_settings, &settings))
+ {
+ show_settings(num_settings, settings);
+ cupsFreeOptions(num_settings, settings);
+ return (0);
+ }
+ else
+ {
+ printf(" %s\n", cupsLastErrorString());
+ return (1);
+ }
+}
+
+
+/*
+ * 'show_settings()' - Show settings in the array...
+ */
+
+static void
+show_settings(
+ int num_settings, /* I - Number of settings */
+ cups_option_t *settings) /* I - Settings */
+{
+ while (num_settings > 0)
+ {
+ printf(" %s=%s\n", settings->name, settings->value);
+
+ settings ++;
+ num_settings --;
+ }
+}
+
+
+/*
+ * End of "$Id: testadmin.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/testarray.c b/cups/libs/cups/testarray.c
new file mode 100644
index 000000000..8003dfc31
--- /dev/null
+++ b/cups/libs/cups/testarray.c
@@ -0,0 +1,562 @@
+/*
+ * "$Id: testarray.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Array test program for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * get_seconds() - Get the current time in seconds...
+ * load_words() - Load words from a file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+#include "debug-private.h"
+#include "array-private.h"
+#include "dir.h"
+
+
+/*
+ * Local functions...
+ */
+
+static double get_seconds(void);
+static int load_words(const char *filename, cups_array_t *array);
+
+
+/*
+ * '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 */
+ cups_array_t *array, /* Test array */
+ *dup_array; /* Duplicate array */
+ int status; /* Exit status */
+ char *text; /* Text from array */
+ char word[256]; /* Word from file */
+ double start, /* Start time */
+ end; /* End time */
+ cups_dir_t *dir; /* Current directory */
+ cups_dentry_t *dent; /* Directory entry */
+ char *saved[32]; /* Saved entries */
+ void *data; /* User data for arrays */
+
+
+ /*
+ * No errors so far...
+ */
+
+ status = 0;
+
+ /*
+ * cupsArrayNew()
+ */
+
+ fputs("cupsArrayNew: ", stdout);
+
+ data = (void *)"testarray";
+ array = cupsArrayNew((cups_array_func_t)strcmp, data);
+
+ if (array)
+ puts("PASS");
+ else
+ {
+ puts("FAIL (returned NULL, expected pointer)");
+ status ++;
+ }
+
+ /*
+ * cupsArrayUserData()
+ */
+
+ fputs("cupsArrayUserData: ", stdout);
+ if (cupsArrayUserData(array) == data)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned %p instead of %p!)\n", cupsArrayUserData(array),
+ data);
+ status ++;
+ }
+
+ /*
+ * cupsArrayAdd()
+ */
+
+ fputs("cupsArrayAdd: ", stdout);
+
+ if (!cupsArrayAdd(array, strdup("One Fish")))
+ {
+ puts("FAIL (\"One Fish\")");
+ status ++;
+ }
+ else
+ {
+ if (!cupsArrayAdd(array, strdup("Two Fish")))
+ {
+ puts("FAIL (\"Two Fish\")");
+ status ++;
+ }
+ else
+ {
+ if (!cupsArrayAdd(array, strdup("Red Fish")))
+ {
+ puts("FAIL (\"Red Fish\")");
+ status ++;
+ }
+ else
+ {
+ if (!cupsArrayAdd(array, strdup("Blue Fish")))
+ {
+ puts("FAIL (\"Blue Fish\")");
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ }
+ }
+
+ /*
+ * cupsArrayCount()
+ */
+
+ fputs("cupsArrayCount: ", stdout);
+ if (cupsArrayCount(array) == 4)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned %d, expected 4)\n", cupsArrayCount(array));
+ status ++;
+ }
+
+ /*
+ * cupsArrayFirst()
+ */
+
+ fputs("cupsArrayFirst: ", stdout);
+ if ((text = (char *)cupsArrayFirst(array)) != NULL &&
+ !strcmp(text, "Blue Fish"))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned \"%s\", expected \"Blue Fish\")\n", text);
+ status ++;
+ }
+
+ /*
+ * cupsArrayNext()
+ */
+
+ fputs("cupsArrayNext: ", stdout);
+ if ((text = (char *)cupsArrayNext(array)) != NULL &&
+ !strcmp(text, "One Fish"))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned \"%s\", expected \"One Fish\")\n", text);
+ status ++;
+ }
+
+ /*
+ * cupsArrayLast()
+ */
+
+ fputs("cupsArrayLast: ", stdout);
+ if ((text = (char *)cupsArrayLast(array)) != NULL &&
+ !strcmp(text, "Two Fish"))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned \"%s\", expected \"Two Fish\")\n", text);
+ status ++;
+ }
+
+ /*
+ * cupsArrayPrev()
+ */
+
+ fputs("cupsArrayPrev: ", stdout);
+ if ((text = (char *)cupsArrayPrev(array)) != NULL &&
+ !strcmp(text, "Red Fish"))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned \"%s\", expected \"Red Fish\")\n", text);
+ status ++;
+ }
+
+ /*
+ * cupsArrayFind()
+ */
+
+ fputs("cupsArrayFind: ", stdout);
+ if ((text = (char *)cupsArrayFind(array, (void *)"One Fish")) != NULL &&
+ !strcmp(text, "One Fish"))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned \"%s\", expected \"One Fish\")\n", text);
+ status ++;
+ }
+
+ /*
+ * cupsArrayCurrent()
+ */
+
+ fputs("cupsArrayCurrent: ", stdout);
+ if ((text = (char *)cupsArrayCurrent(array)) != NULL &&
+ !strcmp(text, "One Fish"))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned \"%s\", expected \"One Fish\")\n", text);
+ status ++;
+ }
+
+ /*
+ * cupsArrayDup()
+ */
+
+ fputs("cupsArrayDup: ", stdout);
+ if ((dup_array = cupsArrayDup(array)) != NULL &&
+ cupsArrayCount(dup_array) == 4)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned %p with %d elements, expected pointer with 4 elements)\n",
+ dup_array, cupsArrayCount(dup_array));
+ status ++;
+ }
+
+ /*
+ * cupsArrayRemove()
+ */
+
+ fputs("cupsArrayRemove: ", stdout);
+ if (cupsArrayRemove(array, (void *)"One Fish") &&
+ cupsArrayCount(array) == 3)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (returned 0 with %d elements, expected 1 with 4 elements)\n",
+ cupsArrayCount(array));
+ status ++;
+ }
+
+ /*
+ * cupsArrayClear()
+ */
+
+ fputs("cupsArrayClear: ", stdout);
+ cupsArrayClear(array);
+ if (cupsArrayCount(array) == 0)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%d elements, expected 0 elements)\n",
+ cupsArrayCount(array));
+ status ++;
+ }
+
+ /*
+ * Now load this source file and grab all of the unique words...
+ */
+
+ fputs("Load unique words: ", stdout);
+ fflush(stdout);
+
+ start = get_seconds();
+
+ if ((dir = cupsDirOpen(".")) == NULL)
+ {
+ puts("FAIL (cupsDirOpen failed)");
+ status ++;
+ }
+ else
+ {
+ while ((dent = cupsDirRead(dir)) != NULL)
+ {
+ i = strlen(dent->filename) - 2;
+
+ if (i > 0 && dent->filename[i] == '.' &&
+ (dent->filename[i + 1] == 'c' ||
+ dent->filename[i + 1] == 'h'))
+ load_words(dent->filename, array);
+ }
+
+ cupsDirClose(dir);
+
+ end = get_seconds();
+
+ printf("%d words in %.3f seconds (%.0f words/sec), ", cupsArrayCount(array),
+ end - start, cupsArrayCount(array) / (end - start));
+ fflush(stdout);
+
+ for (text = (char *)cupsArrayFirst(array); text;)
+ {
+ /*
+ * Copy this word to the word buffer (safe because we strdup'd from
+ * the same buffer in the first place... :)
+ */
+
+ strlcpy(word, text, sizeof(word));
+
+ /*
+ * Grab the next word and compare...
+ */
+
+ if ((text = (char *)cupsArrayNext(array)) == NULL)
+ break;
+
+ if (strcmp(word, text) >= 0)
+ break;
+ }
+
+ if (text)
+ {
+ printf("FAIL (\"%s\" >= \"%s\"!)\n", word, text);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+
+ /*
+ * Test deleting with iteration...
+ */
+
+ fputs("Delete While Iterating: ", stdout);
+
+ text = (char *)cupsArrayFirst(array);
+ cupsArrayRemove(array, text);
+ free(text);
+
+ text = (char *)cupsArrayNext(array);
+ if (!text)
+ {
+ puts("FAIL (cupsArrayNext returned NULL!)");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Test save/restore...
+ */
+
+ fputs("cupsArraySave: ", stdout);
+
+ for (i = 0, text = (char *)cupsArrayFirst(array);
+ i < 32;
+ i ++, text = (char *)cupsArrayNext(array))
+ {
+ saved[i] = text;
+
+ if (!cupsArraySave(array))
+ break;
+ }
+
+ if (i < 32)
+ printf("FAIL (depth = %d)\n", i);
+ else
+ puts("PASS");
+
+ fputs("cupsArrayRestore: ", stdout);
+
+ while (i > 0)
+ {
+ i --;
+
+ text = cupsArrayRestore(array);
+ if (text != saved[i])
+ break;
+ }
+
+ if (i)
+ printf("FAIL (depth = %d)\n", i);
+ else
+ puts("PASS");
+
+ /*
+ * Delete the arrays...
+ */
+
+ cupsArrayDelete(array);
+ cupsArrayDelete(dup_array);
+
+ /*
+ * Test the array with string functions...
+ */
+
+ fputs("_cupsArrayNewStrings(\" \\t\\nfoo bar\\tboo\\nfar\", ' '): ", stdout);
+ array = _cupsArrayNewStrings(" \t\nfoo bar\tboo\nfar", ' ');
+ if (!array)
+ {
+ status = 1;
+ puts("FAIL (unable to create array)");
+ }
+ else if (cupsArrayCount(array) != 4)
+ {
+ status = 1;
+ printf("FAIL (got %d elements, expected 4)\n", cupsArrayCount(array));
+ }
+ else if (strcmp(text = (char *)cupsArrayFirst(array), "bar"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"bar\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "boo"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"boo\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "far"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"far\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "foo"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"foo\")\n", text);
+ }
+ else
+ puts("PASS");
+
+ fputs("_cupsArrayAddStrings(array, \"foo2,bar2\", ','): ", stdout);
+ _cupsArrayAddStrings(array, "foo2,bar2", ',');
+
+ if (cupsArrayCount(array) != 6)
+ {
+ status = 1;
+ printf("FAIL (got %d elements, expected 6)\n", cupsArrayCount(array));
+ }
+ else if (strcmp(text = (char *)cupsArrayFirst(array), "bar"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"bar\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "bar2"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"bar2\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "boo"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"boo\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "far"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"far\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "foo"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"foo\")\n", text);
+ }
+ else if (strcmp(text = (char *)cupsArrayNext(array), "foo2"))
+ {
+ status = 1;
+ printf("FAIL (first element \"%s\", expected \"foo2\")\n", text);
+ }
+ else
+ puts("PASS");
+
+ cupsArrayDelete(array);
+
+ /*
+ * Summarize the results and return...
+ */
+
+ if (!status)
+ puts("\nALL TESTS PASSED!");
+ else
+ printf("\n%d TEST(S) FAILED!\n", status);
+
+ return (status);
+}
+
+
+/*
+ * 'get_seconds()' - Get the current time in seconds...
+ */
+
+#ifdef WIN32
+# include <windows.h>
+
+
+static double
+get_seconds(void)
+{
+}
+#else
+# include <sys/time.h>
+
+
+static double
+get_seconds(void)
+{
+ struct timeval curtime; /* Current time */
+
+
+ gettimeofday(&curtime, NULL);
+ return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
+}
+#endif /* WIN32 */
+
+
+/*
+ * 'load_words()' - Load words from a file.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+load_words(const char *filename, /* I - File to load */
+ cups_array_t *array) /* I - Array to add to */
+{
+ FILE *fp; /* Test file */
+ char word[256]; /* Word from file */
+
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ {
+ perror(filename);
+ return (0);
+ }
+
+ while (fscanf(fp, "%255s", word) == 1)
+ {
+ if (!cupsArrayFind(array, word))
+ cupsArrayAdd(array, strdup(word));
+ }
+
+ fclose(fp);
+
+ return (1);
+}
+
+
+/*
+ * End of "$Id: testarray.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/testconflicts.c b/cups/libs/cups/testconflicts.c
new file mode 100644
index 000000000..0ccca8252
--- /dev/null
+++ b/cups/libs/cups/testconflicts.c
@@ -0,0 +1,138 @@
+/*
+ * "$Id: testconflicts.c 3755 2012-03-30 05:59:14Z msweet $"
+ *
+ * PPD constraint test program for CUPS.
+ *
+ * Copyright 2008-2012 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include "ppd.h"
+#include "string-private.h"
+
+
+/*
+ * '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 */
+ ppd_file_t *ppd; /* PPD file loaded from disk */
+ char line[256], /* Input buffer */
+ *ptr, /* Pointer into buffer */
+ *optr, /* Pointer to first option name */
+ *cptr; /* Pointer to first choice */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ char *option, /* Current option */
+ *choice; /* Current choice */
+
+
+ if (argc != 2)
+ {
+ puts("Usage: testconflicts filename.ppd");
+ return (1);
+ }
+
+ if ((ppd = ppdOpenFile(argv[1])) == NULL)
+ {
+ ppd_status_t err; /* Last error in file */
+ int linenum; /* Line number in file */
+
+ err = ppdLastError(&linenum);
+
+ printf("Unable to open PPD file \"%s\": %s on line %d\n", argv[1],
+ ppdErrorString(err), linenum);
+ return (1);
+ }
+
+ ppdMarkDefaults(ppd);
+
+ option = NULL;
+ choice = NULL;
+
+ for (;;)
+ {
+ num_options = 0;
+ options = NULL;
+
+ if (!cupsResolveConflicts(ppd, option, choice, &num_options, &options))
+ puts("Unable to resolve conflicts!");
+ else if ((!option && num_options > 0) || (option && num_options > 1))
+ {
+ fputs("Resolved conflicts with the following options:\n ", stdout);
+ for (i = 0; i < num_options; i ++)
+ if (!option || _cups_strcasecmp(option, options[i].name))
+ printf(" %s=%s", options[i].name, options[i].value);
+ putchar('\n');
+
+ cupsFreeOptions(num_options, options);
+ }
+
+ if (option)
+ {
+ free(option);
+ option = NULL;
+ }
+
+ if (choice)
+ {
+ free(choice);
+ choice = NULL;
+ }
+
+ printf("\nNew Option(s): ");
+ fflush(stdout);
+ if (!fgets(line, sizeof(line), stdin) || line[0] == '\n')
+ break;
+
+ for (ptr = line; isspace(*ptr & 255); ptr ++);
+ for (optr = ptr; *ptr && *ptr != '='; ptr ++);
+ if (!*ptr)
+ break;
+ for (*ptr++ = '\0', cptr = ptr; *ptr && !isspace(*ptr & 255); ptr ++);
+ if (!*ptr)
+ break;
+ *ptr++ = '\0';
+
+ option = strdup(optr);
+ choice = strdup(cptr);
+ num_options = cupsParseOptions(ptr, 0, &options);
+
+ ppdMarkOption(ppd, option, choice);
+ if (cupsMarkOptions(ppd, num_options, options))
+ puts("Options Conflict!");
+ cupsFreeOptions(num_options, options);
+ }
+
+ if (option)
+ free(option);
+ if (choice)
+ free(choice);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: testconflicts.c 3755 2012-03-30 05:59:14Z msweet $".
+ */
diff --git a/cups/libs/cups/testcups.c b/cups/libs/cups/testcups.c
new file mode 100644
index 000000000..0f6c24e75
--- /dev/null
+++ b/cups/libs/cups/testcups.c
@@ -0,0 +1,593 @@
+/*
+ * "$Id: testcups.c 11205 2013-07-31 18:06:15Z msweet $"
+ *
+ * CUPS API test program for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * dests_equal() - Determine whether two destinations are equal.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#undef _CUPS_NO_DEPRECATED
+#include "string-private.h"
+#include "cups.h"
+#include "ppd.h"
+#include <stdlib.h>
+
+
+/*
+ * Local functions...
+ */
+
+static int dests_equal(cups_dest_t *a, cups_dest_t *b);
+static int enum_cb(void *user_data, unsigned flags, cups_dest_t *dest);
+static void show_diffs(cups_dest_t *a, cups_dest_t *b);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int status = 0, /* Exit status */
+ i, /* Looping var */
+ num_dests; /* Number of destinations */
+ cups_dest_t *dests, /* Destinations */
+ *dest, /* Current destination */
+ *named_dest; /* Current named destination */
+ const char *ppdfile; /* PPD file */
+ ppd_file_t *ppd; /* PPD file data */
+ int num_jobs; /* Number of jobs for queue */
+ cups_job_t *jobs; /* Jobs for queue */
+
+
+ if (argc > 1)
+ {
+ if (!strcmp(argv[1], "enum"))
+ {
+ cups_ptype_t mask = CUPS_PRINTER_LOCAL,
+ /* Printer type mask */
+ type = CUPS_PRINTER_LOCAL;
+ /* Printer type */
+ int msec = 0; /* Timeout in milliseconds */
+
+
+ for (i = 2; i < argc; i ++)
+ if (isdigit(argv[i][0] & 255) || argv[i][0] == '.')
+ msec = (int)(atof(argv[i]) * 1000);
+ else if (!_cups_strcasecmp(argv[i], "bw"))
+ {
+ mask |= CUPS_PRINTER_BW;
+ type |= CUPS_PRINTER_BW;
+ }
+ else if (!_cups_strcasecmp(argv[i], "color"))
+ {
+ mask |= CUPS_PRINTER_COLOR;
+ type |= CUPS_PRINTER_COLOR;
+ }
+ else if (!_cups_strcasecmp(argv[i], "mono"))
+ {
+ mask |= CUPS_PRINTER_COLOR;
+ }
+ else if (!_cups_strcasecmp(argv[i], "duplex"))
+ {
+ mask |= CUPS_PRINTER_DUPLEX;
+ type |= CUPS_PRINTER_DUPLEX;
+ }
+ else if (!_cups_strcasecmp(argv[i], "simplex"))
+ {
+ mask |= CUPS_PRINTER_DUPLEX;
+ }
+ else if (!_cups_strcasecmp(argv[i], "staple"))
+ {
+ mask |= CUPS_PRINTER_STAPLE;
+ type |= CUPS_PRINTER_STAPLE;
+ }
+ else if (!_cups_strcasecmp(argv[i], "copies"))
+ {
+ mask |= CUPS_PRINTER_COPIES;
+ type |= CUPS_PRINTER_COPIES;
+ }
+ else if (!_cups_strcasecmp(argv[i], "collate"))
+ {
+ mask |= CUPS_PRINTER_COLLATE;
+ type |= CUPS_PRINTER_COLLATE;
+ }
+ else if (!_cups_strcasecmp(argv[i], "punch"))
+ {
+ mask |= CUPS_PRINTER_PUNCH;
+ type |= CUPS_PRINTER_PUNCH;
+ }
+ else if (!_cups_strcasecmp(argv[i], "cover"))
+ {
+ mask |= CUPS_PRINTER_COVER;
+ type |= CUPS_PRINTER_COVER;
+ }
+ else if (!_cups_strcasecmp(argv[i], "bind"))
+ {
+ mask |= CUPS_PRINTER_BIND;
+ type |= CUPS_PRINTER_BIND;
+ }
+ else if (!_cups_strcasecmp(argv[i], "sort"))
+ {
+ mask |= CUPS_PRINTER_SORT;
+ type |= CUPS_PRINTER_SORT;
+ }
+ else if (!_cups_strcasecmp(argv[i], "mfp"))
+ {
+ mask |= CUPS_PRINTER_MFP;
+ type |= CUPS_PRINTER_MFP;
+ }
+ else if (!_cups_strcasecmp(argv[i], "printer"))
+ {
+ mask |= CUPS_PRINTER_MFP;
+ }
+ else if (!_cups_strcasecmp(argv[i], "large"))
+ {
+ mask |= CUPS_PRINTER_LARGE;
+ type |= CUPS_PRINTER_LARGE;
+ }
+ else if (!_cups_strcasecmp(argv[i], "medium"))
+ {
+ mask |= CUPS_PRINTER_MEDIUM;
+ type |= CUPS_PRINTER_MEDIUM;
+ }
+ else if (!_cups_strcasecmp(argv[i], "small"))
+ {
+ mask |= CUPS_PRINTER_SMALL;
+ type |= CUPS_PRINTER_SMALL;
+ }
+ else
+ fprintf(stderr, "Unknown argument \"%s\" ignored...\n", argv[i]);
+
+ cupsEnumDests(CUPS_DEST_FLAGS_NONE, msec, NULL, type, mask, enum_cb, NULL);
+ }
+ else if (!strcmp(argv[1], "password"))
+ {
+ const char *pass = cupsGetPassword("Password:");
+ /* Password string */
+
+ if (pass)
+ printf("Password entered: %s\n", pass);
+ else
+ puts("No password entered.");
+ }
+ else if (!strcmp(argv[1], "ppd") && argc == 3)
+ {
+ /*
+ * ./testcups ppd printer
+ */
+
+ http_status_t http_status; /* Status */
+ char buffer[1024]; /* PPD filename */
+ time_t modtime = 0; /* Last modified */
+
+ if ((http_status = cupsGetPPD3(CUPS_HTTP_DEFAULT, argv[2], &modtime,
+ buffer, sizeof(buffer))) != HTTP_STATUS_OK)
+ printf("Unable to get PPD: %d (%s)\n", (int)http_status,
+ cupsLastErrorString());
+ else
+ puts(buffer);
+ }
+ else if (!strcmp(argv[1], "print") && argc == 5)
+ {
+ /*
+ * ./testcups print printer file interval
+ */
+
+ int interval, /* Interval between writes */
+ job_id; /* Job ID */
+ cups_file_t *fp; /* Print file */
+ char buffer[16384]; /* Read/write buffer */
+ ssize_t bytes; /* Bytes read/written */
+
+ if ((fp = cupsFileOpen(argv[3], "r")) == NULL)
+ {
+ printf("Unable to open \"%s\": %s\n", argv[2], strerror(errno));
+ return (1);
+ }
+
+ if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, argv[2], "testcups", 0,
+ NULL)) <= 0)
+ {
+ printf("Unable to create print job on %s: %s\n", argv[1],
+ cupsLastErrorString());
+ return (1);
+ }
+
+ interval = atoi(argv[4]);
+
+ if (cupsStartDocument(CUPS_HTTP_DEFAULT, argv[1], job_id, argv[2],
+ CUPS_FORMAT_AUTO, 1) != HTTP_STATUS_CONTINUE)
+ {
+ puts("Unable to start document!");
+ return (1);
+ }
+
+ while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
+ {
+ printf("Writing %d bytes...\n", (int)bytes);
+
+ if (cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer,
+ bytes) != HTTP_STATUS_CONTINUE)
+ {
+ puts("Unable to write bytes!");
+ return (1);
+ }
+
+ if (interval > 0)
+ sleep(interval);
+ }
+
+ cupsFileClose(fp);
+
+ if (cupsFinishDocument(CUPS_HTTP_DEFAULT,
+ argv[1]) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
+ {
+ puts("Unable to finish document!");
+ return (1);
+ }
+ }
+ else
+ {
+ puts("Usage:");
+ puts("");
+ puts("Run basic unit tests:");
+ puts("");
+ puts(" ./testcups");
+ puts("");
+ puts("Enumerate printers (for N seconds, -1 for indefinitely):");
+ puts("");
+ puts(" ./testcups enum [seconds]");
+ puts("");
+ puts("Ask for a password:");
+ puts("");
+ puts(" ./testcups password");
+ puts("");
+ puts("Get the PPD file:");
+ puts("");
+ puts(" ./testcups ppd printer");
+ puts("");
+ puts("Print a file (interval controls delay between buffers in seconds):");
+ puts("");
+ puts(" ./testcups print printer file interval");
+ return (1);
+ }
+
+ return (0);
+ }
+
+ /*
+ * cupsGetDests()
+ */
+
+ fputs("cupsGetDests: ", stdout);
+ fflush(stdout);
+
+ num_dests = cupsGetDests(&dests);
+
+ if (num_dests == 0)
+ {
+ puts("FAIL");
+ return (1);
+ }
+ else
+ {
+ printf("PASS (%d dests)\n", num_dests);
+
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ {
+ printf(" %s", dest->name);
+
+ if (dest->instance)
+ printf(" /%s", dest->instance);
+
+ if (dest->is_default)
+ puts(" ***DEFAULT***");
+ else
+ putchar('\n');
+ }
+ }
+
+ /*
+ * cupsGetDest(NULL)
+ */
+
+ fputs("cupsGetDest(NULL): ", stdout);
+ fflush(stdout);
+
+ if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL)
+ {
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ if (dest->is_default)
+ break;
+
+ if (i)
+ {
+ status = 1;
+ puts("FAIL");
+ }
+ else
+ puts("PASS (no default)");
+
+ dest = NULL;
+ }
+ else
+ printf("PASS (%s)\n", dest->name);
+
+ /*
+ * cupsGetNamedDest(NULL, NULL, NULL)
+ */
+
+ fputs("cupsGetNamedDest(NULL, NULL, NULL): ", stdout);
+ fflush(stdout);
+
+ if ((named_dest = cupsGetNamedDest(NULL, NULL, NULL)) == NULL ||
+ !dests_equal(dest, named_dest))
+ {
+ if (!dest)
+ puts("PASS (no default)");
+ else if (named_dest)
+ {
+ puts("FAIL (different values)");
+ show_diffs(dest, named_dest);
+ status = 1;
+ }
+ else
+ {
+ puts("FAIL (no default)");
+ status = 1;
+ }
+ }
+ else
+ printf("PASS (%s)\n", named_dest->name);
+
+ if (named_dest)
+ cupsFreeDests(1, named_dest);
+
+ /*
+ * cupsGetDest(printer)
+ */
+
+ printf("cupsGetDest(\"%s\"): ", dests[num_dests / 2].name);
+ fflush(stdout);
+
+ if ((dest = cupsGetDest(dests[num_dests / 2].name, NULL, num_dests,
+ dests)) == NULL)
+ {
+ puts("FAIL");
+ return (1);
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsGetNamedDest(NULL, printer, instance)
+ */
+
+ printf("cupsGetNamedDest(NULL, \"%s\", \"%s\"): ", dest->name,
+ dest->instance ? dest->instance : "(null)");
+ fflush(stdout);
+
+ if ((named_dest = cupsGetNamedDest(NULL, dest->name,
+ dest->instance)) == NULL ||
+ !dests_equal(dest, named_dest))
+ {
+ if (named_dest)
+ {
+ puts("FAIL (different values)");
+ show_diffs(dest, named_dest);
+ }
+ else
+ puts("FAIL (no destination)");
+
+
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ if (named_dest)
+ cupsFreeDests(1, named_dest);
+
+ /*
+ * cupsPrintFile()
+ */
+
+ fputs("cupsPrintFile: ", stdout);
+ fflush(stdout);
+
+ if (cupsPrintFile(dest->name, "../data/testprint", "Test Page",
+ dest->num_options, dest->options) <= 0)
+ {
+ printf("FAIL (%s)\n", cupsLastErrorString());
+ return (1);
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsGetPPD(printer)
+ */
+
+ fputs("cupsGetPPD(): ", stdout);
+ fflush(stdout);
+
+ if ((ppdfile = cupsGetPPD(dest->name)) == NULL)
+ {
+ puts("FAIL");
+ }
+ else
+ {
+ puts("PASS");
+
+ /*
+ * ppdOpenFile()
+ */
+
+ fputs("ppdOpenFile(): ", stdout);
+ fflush(stdout);
+
+ if ((ppd = ppdOpenFile(ppdfile)) == NULL)
+ {
+ puts("FAIL");
+ return (1);
+ }
+ else
+ puts("PASS");
+
+ ppdClose(ppd);
+ unlink(ppdfile);
+ }
+
+ /*
+ * cupsGetJobs()
+ */
+
+ fputs("cupsGetJobs: ", stdout);
+ fflush(stdout);
+
+ num_jobs = cupsGetJobs(&jobs, NULL, 0, -1);
+
+ if (num_jobs == 0)
+ {
+ puts("FAIL");
+ return (1);
+ }
+ else
+ puts("PASS");
+
+ cupsFreeJobs(num_jobs, jobs);
+ cupsFreeDests(num_dests, dests);
+
+ return (status);
+}
+
+
+/*
+ * 'dests_equal()' - Determine whether two destinations are equal.
+ */
+
+static int /* O - 1 if equal, 0 if not equal */
+dests_equal(cups_dest_t *a, /* I - First destination */
+ cups_dest_t *b) /* I - Second destination */
+{
+ int i; /* Looping var */
+ cups_option_t *aoption; /* Current option */
+ const char *bval; /* Option value */
+
+
+ if (a == b)
+ return (1);
+
+ if (!a || !b)
+ return (0);
+
+ if (_cups_strcasecmp(a->name, b->name) ||
+ (a->instance && !b->instance) ||
+ (!a->instance && b->instance) ||
+ (a->instance && _cups_strcasecmp(a->instance, b->instance)) ||
+ a->num_options != b->num_options)
+ return (0);
+
+ for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++)
+ if ((bval = cupsGetOption(aoption->name, b->num_options,
+ b->options)) == NULL ||
+ strcmp(aoption->value, bval))
+ return (0);
+
+ return (1);
+}
+
+
+/*
+ * 'enum_cb()' - Report additions and removals.
+ */
+
+static int /* O - 1 to continue, 0 to stop */
+enum_cb(void *user_data, /* I - User data (unused) */
+ unsigned flags, /* I - Destination flags */
+ cups_dest_t *dest) /* I - Destination */
+{
+ int i; /* Looping var */
+ cups_option_t *option; /* Current option */
+
+
+ if (flags & CUPS_DEST_FLAGS_REMOVED)
+ printf("Removed '%s':\n", dest->name);
+ else
+ printf("Added '%s':\n", dest->name);
+
+ for (i = dest->num_options, option = dest->options; i > 0; i --, option ++)
+ printf(" %s=\"%s\"\n", option->name, option->value);
+
+ putchar('\n');
+
+ return (1);
+}
+
+
+/*
+ * 'show_diffs()' - Show differences between two destinations.
+ */
+
+static void
+show_diffs(cups_dest_t *a, /* I - First destination */
+ cups_dest_t *b) /* I - Second destination */
+{
+ int i; /* Looping var */
+ cups_option_t *aoption; /* Current option */
+ const char *bval; /* Option value */
+
+
+ if (!a || !b)
+ return;
+
+ puts(" Item cupsGetDest cupsGetNamedDest");
+ puts(" -------------------- -------------------- --------------------");
+
+ if (_cups_strcasecmp(a->name, b->name))
+ printf(" name %-20.20s %-20.20s\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",
+ 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,
+ 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,
+ aoption->value, bval ? bval : "(null)");
+}
+
+
+/*
+ * End of "$Id: testcups.c 11205 2013-07-31 18:06:15Z msweet $".
+ */
diff --git a/cups/libs/cups/testfile.c b/cups/libs/cups/testfile.c
new file mode 100644
index 000000000..2c86d821d
--- /dev/null
+++ b/cups/libs/cups/testfile.c
@@ -0,0 +1,821 @@
+/*
+ * "$Id: testfile.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * File test program for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * count_lines() - Count the number of lines in a file.
+ * random_tests() - Do random access tests.
+ * read_write_tests() - Perform read/write tests.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+#include "debug-private.h"
+#include "file.h"
+#include <stdlib.h>
+#include <time.h>
+#ifdef HAVE_LIBZ
+# include <zlib.h>
+#endif /* HAVE_LIBZ */
+#ifdef WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 */
+#include <fcntl.h>
+
+
+/*
+ * Local functions...
+ */
+
+static int count_lines(cups_file_t *fp);
+static int random_tests(void);
+static int read_write_tests(int compression);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int status; /* Exit status */
+ char filename[1024]; /* Filename buffer */
+ cups_file_t *fp; /* File pointer */
+#ifndef WIN32
+ int fds[2]; /* Open file descriptors */
+ cups_file_t *fdfile; /* File opened with cupsFileOpenFd() */
+#endif /* !WIN32 */
+ int count; /* Number of lines in file */
+
+
+ if (argc == 1)
+ {
+ /*
+ * Do uncompressed file tests...
+ */
+
+ status = read_write_tests(0);
+
+#ifdef HAVE_LIBZ
+ /*
+ * Do compressed file tests...
+ */
+
+ putchar('\n');
+
+ status += read_write_tests(1);
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Do uncompressed random I/O tests...
+ */
+
+ status += random_tests();
+
+#ifndef WIN32
+ /*
+ * Test fdopen and close without reading...
+ */
+
+ pipe(fds);
+ close(fds[1]);
+
+ fputs("\ncupsFileOpenFd(fd, \"r\"): ", stdout);
+ fflush(stdout);
+
+ if ((fdfile = cupsFileOpenFd(fds[0], "r")) == NULL)
+ {
+ puts("FAIL");
+ status ++;
+ }
+ else
+ {
+ /*
+ * Able to open file, now close without reading. If we don't return
+ * before the alarm fires, that is a failure and we will crash on the
+ * alarm signal...
+ */
+
+ puts("PASS");
+ fputs("cupsFileClose(no read): ", stdout);
+ fflush(stdout);
+
+ alarm(5);
+ cupsFileClose(fdfile);
+ alarm(0);
+
+ puts("PASS");
+ }
+#endif /* !WIN32 */
+
+ /*
+ * Count lines in psglyphs, rewind, then count again.
+ */
+
+ fputs("\ncupsFileOpen(\"../data/media.defs\", \"r\"): ", stdout);
+
+ if ((fp = cupsFileOpen("../data/media.defs", "r")) == NULL)
+ {
+ puts("FAIL");
+ status ++;
+ }
+ else
+ {
+ puts("PASS");
+ fputs("cupsFileGets: ", stdout);
+
+ if ((count = count_lines(fp)) != 208)
+ {
+ printf("FAIL (got %d lines, expected 208)\n", count);
+ status ++;
+ }
+ else
+ {
+ puts("PASS");
+ fputs("cupsFileRewind: ", stdout);
+
+ if (cupsFileRewind(fp) != 0)
+ {
+ puts("FAIL");
+ status ++;
+ }
+ else
+ {
+ puts("PASS");
+ fputs("cupsFileGets: ", stdout);
+
+ if ((count = count_lines(fp)) != 208)
+ {
+ printf("FAIL (got %d lines, expected 208)\n", count);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ }
+
+ cupsFileClose(fp);
+ }
+
+ /*
+ * Test path functions...
+ */
+
+ fputs("\ncupsFileFind: ", stdout);
+#ifdef WIN32
+ if (cupsFileFind("notepad.exe", "C:/WINDOWS", 1, filename, sizeof(filename)) &&
+ cupsFileFind("notepad.exe", "C:/WINDOWS;C:/WINDOWS/SYSTEM32", 1, filename, sizeof(filename)))
+#else
+ if (cupsFileFind("cat", "/bin", 1, filename, sizeof(filename)) &&
+ cupsFileFind("cat", "/bin:/usr/bin", 1, filename, sizeof(filename)))
+#endif /* WIN32 */
+ printf("PASS (%s)\n", filename);
+ else
+ {
+ puts("FAIL");
+ status ++;
+ }
+
+ /*
+ * Summarize the results and return...
+ */
+
+ if (!status)
+ puts("\nALL TESTS PASSED!");
+ else
+ printf("\n%d TEST(S) FAILED!\n", status);
+ }
+ else
+ {
+ /*
+ * Cat the filename on the command-line...
+ */
+
+ char line[1024]; /* Line from file */
+
+ if ((fp = cupsFileOpen(argv[1], "r")) == NULL)
+ {
+ perror(argv[1]);
+ status = 1;
+ }
+ else
+ {
+ status = 0;
+
+ while (cupsFileGets(fp, line, sizeof(line)))
+ puts(line);
+
+ if (!cupsFileEOF(fp))
+ perror(argv[1]);
+
+ cupsFileClose(fp);
+ }
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'count_lines()' - Count the number of lines in a file.
+ */
+
+static int /* O - Number of lines */
+count_lines(cups_file_t *fp) /* I - File to read from */
+{
+ int count; /* Number of lines */
+ char line[1024]; /* Line buffer */
+
+
+ for (count = 0; cupsFileGets(fp, line, sizeof(line)); count ++);
+
+ return (count);
+}
+
+
+/*
+ * 'random_tests()' - Do random access tests.
+ */
+
+static int /* O - Status */
+random_tests(void)
+{
+ int status, /* Status of tests */
+ pass, /* Current pass */
+ count, /* Number of records read */
+ record, /* Current record */
+ num_records; /* Number of records */
+ ssize_t pos, /* Position in file */
+ expected; /* Expected position in file */
+ cups_file_t *fp; /* File */
+ char buffer[512]; /* Data buffer */
+
+
+ /*
+ * Run 4 passes, each time appending to a data file and then reopening the
+ * file for reading to validate random records in the file.
+ */
+
+ for (status = 0, pass = 0; pass < 4; pass ++)
+ {
+ /*
+ * cupsFileOpen(append)
+ */
+
+ printf("\ncupsFileOpen(append %d): ", pass);
+
+ if ((fp = cupsFileOpen("testfile.dat", "a")) == NULL)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ break;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsFileTell()
+ */
+
+ expected = 256 * sizeof(buffer) * pass;
+
+ fputs("cupsFileTell(): ", stdout);
+ if ((pos = cupsFileTell(fp)) != expected)
+ {
+ printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
+ CUPS_LLCAST pos, CUPS_LLCAST expected);
+ status ++;
+ break;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsFileWrite()
+ */
+
+ fputs("cupsFileWrite(256 512-byte records): ", stdout);
+ for (record = 0; record < 256; record ++)
+ {
+ memset(buffer, record, sizeof(buffer));
+ if (cupsFileWrite(fp, buffer, sizeof(buffer)) < sizeof(buffer))
+ break;
+ }
+
+ if (record < 256)
+ {
+ printf("FAIL (%d: %s)\n", record, strerror(errno));
+ status ++;
+ break;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsFileTell()
+ */
+
+ expected += 256 * sizeof(buffer);
+
+ fputs("cupsFileTell(): ", stdout);
+ if ((pos = cupsFileTell(fp)) != expected)
+ {
+ printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
+ CUPS_LLCAST pos, CUPS_LLCAST expected);
+ status ++;
+ break;
+ }
+ else
+ puts("PASS");
+
+ cupsFileClose(fp);
+
+ /*
+ * cupsFileOpen(read)
+ */
+
+ printf("\ncupsFileOpen(read %d): ", pass);
+
+ if ((fp = cupsFileOpen("testfile.dat", "r")) == NULL)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ break;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsFileSeek, cupsFileRead
+ */
+
+ fputs("cupsFileSeek(), cupsFileRead(): ", stdout);
+
+ for (num_records = (pass + 1) * 256, count = (pass + 1) * 256,
+ record = CUPS_RAND() % num_records;
+ count > 0;
+ count --, record = (record + (CUPS_RAND() & 31) - 16 + num_records) %
+ num_records)
+ {
+ /*
+ * The last record is always the first...
+ */
+
+ if (count == 1)
+ record = 0;
+
+ /*
+ * Try reading the data for the specified record, and validate the
+ * contents...
+ */
+
+ expected = sizeof(buffer) * record;
+
+ if ((pos = cupsFileSeek(fp, expected)) != expected)
+ {
+ printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
+ CUPS_LLCAST pos, CUPS_LLCAST expected);
+ status ++;
+ break;
+ }
+ else
+ {
+ if (cupsFileRead(fp, buffer, sizeof(buffer)) != sizeof(buffer))
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ break;
+ }
+ else if ((buffer[0] & 255) != (record & 255) ||
+ memcmp(buffer, buffer + 1, sizeof(buffer) - 1))
+ {
+ printf("FAIL (Bad Data - %d instead of %d)\n", buffer[0] & 255,
+ record & 255);
+ status ++;
+ break;
+ }
+ }
+ }
+
+ if (count == 0)
+ puts("PASS");
+
+ cupsFileClose(fp);
+ }
+
+ /*
+ * Remove the test file...
+ */
+
+ unlink("testfile.dat");
+
+ /*
+ * Return the test status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'read_write_tests()' - Perform read/write tests.
+ */
+
+static int /* O - Status */
+read_write_tests(int compression) /* I - Use compression? */
+{
+ int i; /* Looping var */
+ cups_file_t *fp; /* File */
+ int status; /* Exit status */
+ char line[1024], /* Line from file */
+ *value; /* Directive value from line */
+ int linenum; /* Line number */
+ unsigned char readbuf[8192], /* Read buffer */
+ writebuf[8192]; /* Write buffer */
+ int byte; /* Byte from file */
+ off_t length; /* Length of file */
+ static const char *partial_line = "partial line";
+ /* Partial line */
+
+
+ /*
+ * No errors so far...
+ */
+
+ status = 0;
+
+ /*
+ * Initialize the write buffer with random data...
+ */
+
+ CUPS_SRAND((unsigned)time(NULL));
+
+ for (i = 0; i < (int)sizeof(writebuf); i ++)
+ writebuf[i] = CUPS_RAND();
+
+ /*
+ * cupsFileOpen(write)
+ */
+
+ printf("cupsFileOpen(write%s): ", compression ? " compressed" : "");
+
+ fp = cupsFileOpen(compression ? "testfile.dat.gz" : "testfile.dat",
+ compression ? "w9" : "w");
+ if (fp)
+ {
+ puts("PASS");
+
+ /*
+ * cupsFileCompression()
+ */
+
+ fputs("cupsFileCompression(): ", stdout);
+
+ if (cupsFileCompression(fp) == compression)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (Got %d, expected %d)\n", cupsFileCompression(fp),
+ compression);
+ status ++;
+ }
+
+ /*
+ * cupsFilePuts()
+ */
+
+ fputs("cupsFilePuts(): ", stdout);
+
+ if (cupsFilePuts(fp, "# Hello, World\n") > 0)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFilePrintf()
+ */
+
+ fputs("cupsFilePrintf(): ", stdout);
+
+ for (i = 0; i < 1000; i ++)
+ if (cupsFilePrintf(fp, "TestLine %03d\n", i) < 0)
+ break;
+
+ if (i >= 1000)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFilePutChar()
+ */
+
+ fputs("cupsFilePutChar(): ", stdout);
+
+ for (i = 0; i < 256; i ++)
+ if (cupsFilePutChar(fp, i) < 0)
+ break;
+
+ if (i >= 256)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileWrite()
+ */
+
+ fputs("cupsFileWrite(): ", stdout);
+
+ for (i = 0; i < 10000; i ++)
+ if (cupsFileWrite(fp, (char *)writebuf, sizeof(writebuf)) < 0)
+ break;
+
+ if (i >= 10000)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFilePuts() with partial line...
+ */
+
+ fputs("cupsFilePuts(\"partial line\"): ", stdout);
+
+ if (cupsFilePuts(fp, partial_line) > 0)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileTell()
+ */
+
+ fputs("cupsFileTell(): ", stdout);
+
+ if ((length = cupsFileTell(fp)) == 81933283)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
+ status ++;
+ }
+
+ /*
+ * cupsFileClose()
+ */
+
+ fputs("cupsFileClose(): ", stdout);
+
+ if (!cupsFileClose(fp))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileOpen(read)
+ */
+
+ fputs("\ncupsFileOpen(read): ", stdout);
+
+ fp = cupsFileOpen(compression ? "testfile.dat.gz" : "testfile.dat", "r");
+ if (fp)
+ {
+ puts("PASS");
+
+ /*
+ * cupsFileGets()
+ */
+
+ fputs("cupsFileGets(): ", stdout);
+
+ if (cupsFileGets(fp, line, sizeof(line)))
+ {
+ if (line[0] == '#')
+ puts("PASS");
+ else
+ {
+ printf("FAIL (Got line \"%s\", expected comment line)\n", line);
+ status ++;
+ }
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileCompression()
+ */
+
+ fputs("cupsFileCompression(): ", stdout);
+
+ if (cupsFileCompression(fp) == compression)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (Got %d, expected %d)\n", cupsFileCompression(fp),
+ compression);
+ status ++;
+ }
+
+ /*
+ * cupsFileGetConf()
+ */
+
+ linenum = 1;
+
+ fputs("cupsFileGetConf(): ", stdout);
+
+ for (i = 0; i < 1000; i ++)
+ if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ break;
+ else if (_cups_strcasecmp(line, "TestLine") || !value || atoi(value) != i ||
+ linenum != (i + 2))
+ break;
+
+ if (i >= 1000)
+ puts("PASS");
+ else if (line[0])
+ {
+ printf("FAIL (Line %d, directive \"%s\", value \"%s\")\n", linenum,
+ line, value ? value : "(null)");
+ status ++;
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileGetChar()
+ */
+
+ fputs("cupsFileGetChar(): ", stdout);
+
+ for (i = 0; i < 256; i ++)
+ if ((byte = cupsFileGetChar(fp)) != i)
+ break;
+
+ if (i >= 256)
+ puts("PASS");
+ else if (byte >= 0)
+ {
+ printf("FAIL (Got %d, expected %d)\n", byte, i);
+ status ++;
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileRead()
+ */
+
+ fputs("cupsFileRead(): ", stdout);
+
+ for (i = 0; i < 10000; i ++)
+ if ((byte = cupsFileRead(fp, (char *)readbuf, sizeof(readbuf))) < 0)
+ break;
+ else if (memcmp(readbuf, writebuf, sizeof(readbuf)))
+ break;
+
+ if (i >= 10000)
+ puts("PASS");
+ else if (byte > 0)
+ {
+ printf("FAIL (Pass %d, ", i);
+
+ for (i = 0; i < (int)sizeof(readbuf); i ++)
+ if (readbuf[i] != writebuf[i])
+ break;
+
+ printf("match failed at offset %d - got %02X, expected %02X)\n",
+ i, readbuf[i], writebuf[i]);
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * cupsFileGetChar() with partial line...
+ */
+
+ fputs("cupsFileGetChar(partial line): ", stdout);
+
+ for (i = 0; i < (int)strlen(partial_line); i ++)
+ if ((byte = cupsFileGetChar(fp)) < 0)
+ break;
+ else if (byte != partial_line[i])
+ break;
+
+ if (!partial_line[i])
+ puts("PASS");
+ else
+ {
+ printf("FAIL (got '%c', expected '%c')\n", byte, partial_line[i]);
+ status ++;
+ }
+
+ /*
+ * cupsFileTell()
+ */
+
+ fputs("cupsFileTell(): ", stdout);
+
+ if ((length = cupsFileTell(fp)) == 81933283)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
+ status ++;
+ }
+
+ /*
+ * cupsFileClose()
+ */
+
+ fputs("cupsFileClose(): ", stdout);
+
+ if (!cupsFileClose(fp))
+ puts("PASS");
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
+ /*
+ * Remove the test file...
+ */
+
+ unlink(compression ? "testfile.dat.gz" : "testfile.dat");
+
+ /*
+ * Return the test status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: testfile.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/testhttp.c b/cups/libs/cups/testhttp.c
new file mode 100644
index 000000000..71fe4df84
--- /dev/null
+++ b/cups/libs/cups/testhttp.c
@@ -0,0 +1,835 @@
+/*
+ * "$Id: testhttp.c 11445 2013-12-05 19:57:43Z msweet $"
+ *
+ * HTTP test program for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * Types and structures...
+ */
+
+typedef struct uri_test_s /**** URI test cases ****/
+{
+ http_uri_status_t result; /* Expected return value */
+ const char *uri, /* URI */
+ *scheme, /* Scheme string */
+ *username, /* Username:password string */
+ *hostname, /* Hostname string */
+ *resource; /* Resource string */
+ int port, /* Port number */
+ assemble_port, /* Port number for httpAssembleURI() */
+ assemble_coding;/* Coding for httpAssembleURI() */
+} uri_test_t;
+
+
+/*
+ * Local globals...
+ */
+
+static uri_test_t uri_tests[] = /* URI test data */
+ {
+ /* Start with valid URIs */
+ { HTTP_URI_STATUS_OK, "file:/filename",
+ "file", "", "", "/filename", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "file:/filename%20with%20spaces",
+ "file", "", "", "/filename with spaces", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "file:///filename",
+ "file", "", "", "/filename", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "file:///filename%20with%20spaces",
+ "file", "", "", "/filename with spaces", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "file://localhost/filename",
+ "file", "", "localhost", "/filename", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "file://localhost/filename%20with%20spaces",
+ "file", "", "localhost", "/filename with spaces", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "http://server/",
+ "http", "", "server", "/", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "http://username@server/",
+ "http", "username", "server", "/", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "http://username:passwor%64@server/",
+ "http", "username:password", "server", "/", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "http://username:passwor%64@server:8080/",
+ "http", "username:password", "server", "/", 8080, 8080,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "http://username:passwor%64@server:8080/directory/filename",
+ "http", "username:password", "server", "/directory/filename", 8080, 8080,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "http://[2000::10:100]:631/ipp",
+ "http", "", "2000::10:100", "/ipp", 631, 631,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "https://username:passwor%64@server/directory/filename",
+ "https", "username:password", "server", "/directory/filename", 443, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "ipp://username:passwor%64@[::1]/ipp",
+ "ipp", "username:password", "::1", "/ipp", 631, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "lpd://server/queue?reserve=yes",
+ "lpd", "", "server", "/queue?reserve=yes", 515, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "mailto:user@domain.com",
+ "mailto", "", "", "user@domain.com", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "socket://server/",
+ "socket", "", "server", "/", 9100, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "socket://192.168.1.1:9101/",
+ "socket", "", "192.168.1.1", "/", 9101, 9101,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "tel:8005551212",
+ "tel", "", "", "8005551212", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "ipp://username:password@[v1.fe80::200:1234:5678:9abc+eth0]:999/ipp",
+ "ipp", "username:password", "fe80::200:1234:5678:9abc%eth0", "/ipp", 999, 999,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "ipp://username:password@[fe80::200:1234:5678:9abc%25eth0]:999/ipp",
+ "ipp", "username:password", "fe80::200:1234:5678:9abc%eth0", "/ipp", 999, 999,
+ HTTP_URI_CODING_MOST | HTTP_URI_CODING_RFC6874 },
+ { HTTP_URI_STATUS_OK, "http://server/admin?DEVICE_URI=usb://HP/Photosmart%25202600%2520series?serial=MY53OK70V10400",
+ "http", "", "server", "/admin?DEVICE_URI=usb://HP/Photosmart%25202600%2520series?serial=MY53OK70V10400", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "lpd://Acme%20Laser%20(01%3A23%3A45).local._tcp._printer/",
+ "lpd", "", "Acme Laser (01:23:45).local._tcp._printer", "/", 515, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "ipp://HP%20Officejet%204500%20G510n-z%20%40%20Will's%20MacBook%20Pro%2015%22._ipp._tcp.local./",
+ "ipp", "", "HP Officejet 4500 G510n-z @ Will's MacBook Pro 15\"._ipp._tcp.local.", "/", 631, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_OK, "ipp://%22%23%2F%3A%3C%3E%3F%40%5B%5C%5D%5E%60%7B%7C%7D/",
+ "ipp", "", "\"#/:<>?@[\\]^`{|}", "/", 631, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Missing scheme */
+ { HTTP_URI_STATUS_MISSING_SCHEME, "/path/to/file/index.html",
+ "file", "", "", "/path/to/file/index.html", 0, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_MISSING_SCHEME, "//server/ipp",
+ "ipp", "", "server", "/ipp", 631, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Unknown scheme */
+ { HTTP_URI_STATUS_UNKNOWN_SCHEME, "vendor://server/resource",
+ "vendor", "", "server", "/resource", 0, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Missing resource */
+ { HTTP_URI_STATUS_MISSING_RESOURCE, "socket://[::192.168.2.1]",
+ "socket", "", "::192.168.2.1", "/", 9100, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_MISSING_RESOURCE, "socket://192.168.1.1:9101",
+ "socket", "", "192.168.1.1", "/", 9101, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Bad URI */
+ { HTTP_URI_STATUS_BAD_URI, "",
+ "", "", "", "", 0, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Bad scheme */
+ { HTTP_URI_STATUS_BAD_SCHEME, "bad_scheme://server/resource",
+ "", "", "", "", 0, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Bad username */
+ { HTTP_URI_STATUS_BAD_USERNAME, "http://username:passwor%6@server/resource",
+ "http", "", "", "", 80, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Bad hostname */
+ { HTTP_URI_STATUS_BAD_HOSTNAME, "http://[/::1]/index.html",
+ "http", "", "", "", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_BAD_HOSTNAME, "http://[",
+ "http", "", "", "", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_BAD_HOSTNAME, "http://serve%7/index.html",
+ "http", "", "", "", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_BAD_HOSTNAME, "http://server with spaces/index.html",
+ "http", "", "", "", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_BAD_HOSTNAME, "ipp://\"#/:<>?@[\\]^`{|}/",
+ "ipp", "", "", "", 631, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Bad port number */
+ { HTTP_URI_STATUS_BAD_PORT, "http://127.0.0.1:9999a/index.html",
+ "http", "", "127.0.0.1", "", 0, 0,
+ HTTP_URI_CODING_MOST },
+
+ /* Bad resource */
+ { HTTP_URI_STATUS_BAD_RESOURCE, "http://server/index.html%",
+ "http", "", "server", "", 80, 0,
+ HTTP_URI_CODING_MOST },
+ { HTTP_URI_STATUS_BAD_RESOURCE, "http://server/index with spaces.html",
+ "http", "", "server", "", 80, 0,
+ HTTP_URI_CODING_MOST }
+ };
+static const char * const base64_tests[][2] =
+ {
+ { "A", "QQ==" },
+ /* 010000 01 */
+ { "AB", "QUI=" },
+ /* 010000 010100 0010 */
+ { "ABC", "QUJD" },
+ /* 010000 010100 001001 000011 */
+ { "ABCD", "QUJDRA==" },
+ /* 010000 010100 001001 000011 010001 00 */
+ { "ABCDE", "QUJDREU=" },
+ /* 010000 010100 001001 000011 010001 000100 0101 */
+ { "ABCDEF", "QUJDREVG" },
+ /* 010000 010100 001001 000011 010001 000100 010101 000110 */
+ };
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i, j, k; /* Looping vars */
+ http_t *http; /* HTTP connection */
+ http_encryption_t encryption; /* Encryption type */
+ http_status_t status; /* Status of GET command */
+ int failures; /* Number of test failures */
+ char buffer[8192]; /* Input buffer */
+ long bytes; /* Number of bytes read */
+ FILE *out; /* Output file */
+ char encode[256], /* Base64-encoded string */
+ decode[256]; /* Base64-decoded string */
+ int decodelen; /* Length of decoded string */
+ char scheme[HTTP_MAX_URI], /* Scheme from URI */
+ hostname[HTTP_MAX_URI], /* Hostname from URI */
+ username[HTTP_MAX_URI], /* Username:password from URI */
+ resource[HTTP_MAX_URI]; /* Resource from URI */
+ int port; /* Port number from URI */
+ http_uri_status_t uri_status; /* Status of URI separation */
+ http_addrlist_t *addrlist, /* Address list */
+ *addr; /* Current address */
+ off_t length, total; /* Length and total bytes */
+ time_t start, current; /* Start and end time */
+ const char *encoding; /* Negotiated Content-Encoding */
+ static const char * const uri_status_strings[] =
+ {
+ "HTTP_URI_STATUS_OVERFLOW",
+ "HTTP_URI_STATUS_BAD_ARGUMENTS",
+ "HTTP_URI_STATUS_BAD_RESOURCE",
+ "HTTP_URI_STATUS_BAD_PORT",
+ "HTTP_URI_STATUS_BAD_HOSTNAME",
+ "HTTP_URI_STATUS_BAD_USERNAME",
+ "HTTP_URI_STATUS_BAD_SCHEME",
+ "HTTP_URI_STATUS_BAD_URI",
+ "HTTP_URI_STATUS_OK",
+ "HTTP_URI_STATUS_MISSING_SCHEME",
+ "HTTP_URI_STATUS_UNKNOWN_SCHEME",
+ "HTTP_URI_STATUS_MISSING_RESOURCE"
+ };
+
+
+ /*
+ * Do API tests if we don't have a URL on the command-line...
+ */
+
+ if (argc == 1)
+ {
+ failures = 0;
+
+ /*
+ * httpGetDateString()/httpGetDateTime()
+ */
+
+ fputs("httpGetDateString()/httpGetDateTime(): ", stdout);
+
+ start = time(NULL);
+ strlcpy(buffer, httpGetDateString(start), sizeof(buffer));
+ current = httpGetDateTime(buffer);
+
+ i = (int)(current - start);
+ if (i < 0)
+ i = -i;
+
+ if (!i)
+ puts("PASS");
+ else
+ {
+ failures ++;
+ puts("FAIL");
+ printf(" Difference is %d seconds, %02d:%02d:%02d...\n", i, i / 3600,
+ (i / 60) % 60, i % 60);
+ printf(" httpGetDateString(%d) returned \"%s\"\n", (int)start, buffer);
+ printf(" httpGetDateTime(\"%s\") returned %d\n", buffer, (int)current);
+ printf(" httpGetDateString(%d) returned \"%s\"\n", (int)current,
+ httpGetDateString(current));
+ }
+
+ /*
+ * httpDecode64_2()/httpEncode64_2()
+ */
+
+ fputs("httpDecode64_2()/httpEncode64_2(): ", stdout);
+
+ for (i = 0, j = 0; i < (int)(sizeof(base64_tests) / sizeof(base64_tests[0])); i ++)
+ {
+ httpEncode64_2(encode, sizeof(encode), base64_tests[i][0],
+ (int)strlen(base64_tests[i][0]));
+ decodelen = (int)sizeof(decode);
+ httpDecode64_2(decode, &decodelen, base64_tests[i][1]);
+
+ if (strcmp(decode, base64_tests[i][0]))
+ {
+ failures ++;
+
+ if (j)
+ {
+ puts("FAIL");
+ j = 1;
+ }
+
+ printf(" httpDecode64_2() returned \"%s\", expected \"%s\"...\n",
+ decode, base64_tests[i][0]);
+ }
+
+ if (strcmp(encode, base64_tests[i][1]))
+ {
+ failures ++;
+
+ if (j)
+ {
+ puts("FAIL");
+ j = 1;
+ }
+
+ printf(" httpEncode64_2() returned \"%s\", expected \"%s\"...\n",
+ encode, base64_tests[i][1]);
+ }
+ }
+
+ if (!j)
+ puts("PASS");
+
+ /*
+ * httpGetHostname()
+ */
+
+ fputs("httpGetHostname(): ", stdout);
+
+ if (httpGetHostname(NULL, hostname, sizeof(hostname)))
+ printf("PASS (%s)\n", hostname);
+ else
+ {
+ failures ++;
+ puts("FAIL");
+ }
+
+ /*
+ * httpAddrGetList()
+ */
+
+ printf("httpAddrGetList(%s): ", hostname);
+
+ addrlist = httpAddrGetList(hostname, AF_UNSPEC, NULL);
+ if (addrlist)
+ {
+ for (i = 0, addr = addrlist; addr; i ++, addr = addr->next)
+ {
+ char numeric[1024]; /* Numeric IP address */
+
+
+ httpAddrString(&(addr->addr), numeric, sizeof(numeric));
+ if (!strcmp(numeric, "UNKNOWN"))
+ break;
+ }
+
+ if (addr)
+ printf("FAIL (bad address for %s)\n", hostname);
+ else
+ printf("PASS (%d address(es) for %s)\n", i, hostname);
+
+ httpAddrFreeList(addrlist);
+ }
+ else if (isdigit(hostname[0] & 255))
+ {
+ puts("FAIL (ignored because hostname is numeric)");
+ }
+ else
+ {
+ failures ++;
+ puts("FAIL");
+ }
+
+ /*
+ * Test httpSeparateURI()...
+ */
+
+ fputs("httpSeparateURI(): ", stdout);
+ for (i = 0, j = 0; i < (int)(sizeof(uri_tests) / sizeof(uri_tests[0])); i ++)
+ {
+ uri_status = httpSeparateURI(HTTP_URI_CODING_MOST,
+ uri_tests[i].uri, scheme, sizeof(scheme),
+ username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+ if (uri_status != uri_tests[i].result ||
+ strcmp(scheme, uri_tests[i].scheme) ||
+ strcmp(username, uri_tests[i].username) ||
+ strcmp(hostname, uri_tests[i].hostname) ||
+ port != uri_tests[i].port ||
+ strcmp(resource, uri_tests[i].resource))
+ {
+ failures ++;
+
+ if (!j)
+ {
+ puts("FAIL");
+ j = 1;
+ }
+
+ printf(" \"%s\":\n", uri_tests[i].uri);
+
+ if (uri_status != uri_tests[i].result)
+ printf(" Returned %s instead of %s\n",
+ uri_status_strings[uri_status + 8],
+ uri_status_strings[uri_tests[i].result + 8]);
+
+ if (strcmp(scheme, uri_tests[i].scheme))
+ printf(" Scheme \"%s\" instead of \"%s\"\n",
+ scheme, uri_tests[i].scheme);
+
+ if (strcmp(username, uri_tests[i].username))
+ printf(" Username \"%s\" instead of \"%s\"\n",
+ username, uri_tests[i].username);
+
+ if (strcmp(hostname, uri_tests[i].hostname))
+ printf(" Hostname \"%s\" instead of \"%s\"\n",
+ hostname, uri_tests[i].hostname);
+
+ if (port != uri_tests[i].port)
+ printf(" Port %d instead of %d\n",
+ port, uri_tests[i].port);
+
+ if (strcmp(resource, uri_tests[i].resource))
+ printf(" Resource \"%s\" instead of \"%s\"\n",
+ resource, uri_tests[i].resource);
+ }
+ }
+
+ if (!j)
+ printf("PASS (%d URIs tested)\n",
+ (int)(sizeof(uri_tests) / sizeof(uri_tests[0])));
+
+ /*
+ * Test httpAssembleURI()...
+ */
+
+ fputs("httpAssembleURI(): ", stdout);
+ for (i = 0, j = 0, k = 0;
+ i < (int)(sizeof(uri_tests) / sizeof(uri_tests[0]));
+ i ++)
+ if (uri_tests[i].result == HTTP_URI_STATUS_OK &&
+ !strstr(uri_tests[i].uri, "%64") &&
+ strstr(uri_tests[i].uri, "//"))
+ {
+ k ++;
+ uri_status = httpAssembleURI(uri_tests[i].assemble_coding,
+ buffer, sizeof(buffer),
+ uri_tests[i].scheme,
+ uri_tests[i].username,
+ uri_tests[i].hostname,
+ uri_tests[i].assemble_port,
+ uri_tests[i].resource);
+
+ if (uri_status != HTTP_URI_STATUS_OK)
+ {
+ failures ++;
+
+ if (!j)
+ {
+ puts("FAIL");
+ j = 1;
+ }
+
+ printf(" \"%s\": %s\n", uri_tests[i].uri,
+ uri_status_strings[uri_status + 8]);
+ }
+ else if (strcmp(buffer, uri_tests[i].uri))
+ {
+ failures ++;
+
+ if (!j)
+ {
+ puts("FAIL");
+ j = 1;
+ }
+
+ printf(" \"%s\": assembled = \"%s\"\n", uri_tests[i].uri,
+ buffer);
+ }
+ }
+
+ if (!j)
+ printf("PASS (%d URIs tested)\n", k);
+
+ /*
+ * httpAssembleUUID
+ */
+
+ fputs("httpAssembleUUID: ", stdout);
+ httpAssembleUUID("hostname.example.com", 631, "printer", 12345, buffer,
+ sizeof(buffer));
+ if (strncmp(buffer, "urn:uuid:", 9))
+ {
+ printf("FAIL (%s)\n", buffer);
+ failures ++;
+ }
+ else
+ printf("PASS (%s)\n", buffer);
+
+ /*
+ * Show a summary and return...
+ */
+
+ if (failures)
+ printf("\n%d TESTS FAILED!\n", failures);
+ else
+ puts("\nALL TESTS PASSED!");
+
+ return (failures);
+ }
+ else if (strstr(argv[1], "._tcp"))
+ {
+ /*
+ * Test resolving an mDNS name.
+ */
+
+ char resolved[1024]; /* Resolved URI */
+
+
+ printf("_httpResolveURI(%s, _HTTP_RESOLVE_DEFAULT): ", argv[1]);
+ fflush(stdout);
+
+ if (!_httpResolveURI(argv[1], resolved, sizeof(resolved),
+ _HTTP_RESOLVE_DEFAULT, NULL, NULL))
+ {
+ puts("FAIL");
+ return (1);
+ }
+ else
+ printf("PASS (%s)\n", resolved);
+
+ printf("_httpResolveURI(%s, _HTTP_RESOLVE_FQDN): ", argv[1]);
+ fflush(stdout);
+
+ if (!_httpResolveURI(argv[1], resolved, sizeof(resolved),
+ _HTTP_RESOLVE_FQDN, NULL, NULL))
+ {
+ puts("FAIL");
+ return (1);
+ }
+ else if (strstr(resolved, ".local:"))
+ {
+ printf("FAIL (%s)\n", resolved);
+ return (1);
+ }
+ else
+ {
+ printf("PASS (%s)\n", resolved);
+ return (0);
+ }
+ }
+ else if (!strcmp(argv[1], "-u") && argc == 3)
+ {
+ /*
+ * Test URI separation...
+ */
+
+ uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, argv[2], scheme,
+ sizeof(scheme), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+ printf("uri_status = %s\n", uri_status_strings[uri_status + 8]);
+ printf("scheme = \"%s\"\n", scheme);
+ printf("username = \"%s\"\n", username);
+ printf("hostname = \"%s\"\n", hostname);
+ printf("port = %d\n", port);
+ printf("resource = \"%s\"\n", resource);
+
+ return (0);
+ }
+
+ /*
+ * Test HTTP GET requests...
+ */
+
+ http = NULL;
+ out = stdout;
+
+ for (i = 1; i < argc; i ++)
+ {
+ if (!strcmp(argv[i], "-o"))
+ {
+ i ++;
+ if (i >= argc)
+ break;
+
+ out = fopen(argv[i], "wb");
+ continue;
+ }
+
+ httpSeparateURI(HTTP_URI_CODING_MOST, argv[i], scheme, sizeof(scheme),
+ username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ if (!_cups_strcasecmp(scheme, "https") || !_cups_strcasecmp(scheme, "ipps") ||
+ port == 443)
+ encryption = HTTP_ENCRYPTION_ALWAYS;
+ else
+ encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+
+ http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000,
+ NULL);
+ if (http == NULL)
+ {
+ perror(hostname);
+ continue;
+ }
+ printf("Checking file \"%s\"...\n", resource);
+
+ do
+ {
+ if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
+ {
+ httpClearFields(http);
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ }
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString(http));
+ httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
+ if (httpHead(http, resource))
+ {
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ else
+ {
+ status = HTTP_STATUS_UNAUTHORIZED;
+ continue;
+ }
+ }
+
+ while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
+
+ if (status == HTTP_STATUS_UNAUTHORIZED)
+ {
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+
+ /*
+ * See if we can do authentication...
+ */
+
+ if (cupsDoAuthentication(http, "GET", resource))
+ {
+ status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ break;
+ }
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ continue;
+ }
+#ifdef HAVE_SSL
+ else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ /* Flush any error message... */
+ httpFlush(http);
+
+ /* Reconnect... */
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ /* Upgrade with encryption... */
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
+
+ /* Try again, this time with encryption enabled... */
+ continue;
+ }
+#endif /* HAVE_SSL */
+ }
+ while (status == HTTP_STATUS_UNAUTHORIZED ||
+ status == HTTP_STATUS_UPGRADE_REQUIRED);
+
+ if (status == HTTP_STATUS_OK)
+ puts("HEAD OK:");
+ else
+ printf("HEAD failed with status %d...\n", status);
+
+ encoding = httpGetContentEncoding(http);
+
+ printf("Requesting file \"%s\" (Accept-Encoding: %s)...\n", resource,
+ encoding ? encoding : "identity");
+
+ do
+ {
+ if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
+ {
+ httpClearFields(http);
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ }
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString(http));
+ httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
+ httpSetField(http, HTTP_FIELD_ACCEPT_ENCODING, encoding);
+
+ if (httpGet(http, resource))
+ {
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+ else
+ {
+ status = HTTP_STATUS_UNAUTHORIZED;
+ continue;
+ }
+ }
+
+ while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
+
+ if (status == HTTP_STATUS_UNAUTHORIZED)
+ {
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+
+ /*
+ * See if we can do authentication...
+ */
+
+ if (cupsDoAuthentication(http, "GET", resource))
+ {
+ status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+ break;
+ }
+
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ continue;
+ }
+#ifdef HAVE_SSL
+ else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
+ {
+ /* Flush any error message... */
+ httpFlush(http);
+
+ /* Reconnect... */
+ if (httpReconnect2(http, 30000, NULL))
+ {
+ status = HTTP_STATUS_ERROR;
+ break;
+ }
+
+ /* Upgrade with encryption... */
+ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
+
+ /* Try again, this time with encryption enabled... */
+ continue;
+ }
+#endif /* HAVE_SSL */
+ }
+ while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED);
+
+ if (status == HTTP_STATUS_OK)
+ puts("GET OK:");
+ else
+ printf("GET failed with status %d...\n", status);
+
+ start = time(NULL);
+ length = httpGetLength2(http);
+ total = 0;
+
+ while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
+ {
+ total += bytes;
+ fwrite(buffer, bytes, 1, out);
+ if (out != stdout)
+ {
+ current = time(NULL);
+ if (current == start) current ++;
+ printf("\r" CUPS_LLFMT "/" CUPS_LLFMT " bytes ("
+ CUPS_LLFMT " bytes/sec) ", CUPS_LLCAST total,
+ CUPS_LLCAST length, CUPS_LLCAST (total / (current - start)));
+ fflush(stdout);
+ }
+ }
+ }
+
+ puts("Closing connection to server...");
+ httpClose(http);
+
+ if (out != stdout)
+ fclose(out);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: testhttp.c 11445 2013-12-05 19:57:43Z msweet $".
+ */
diff --git a/cups/libs/cups/testi18n.c b/cups/libs/cups/testi18n.c
new file mode 100644
index 000000000..e5d3b8c10
--- /dev/null
+++ b/cups/libs/cups/testi18n.c
@@ -0,0 +1,619 @@
+/*
+ * "$Id: testi18n.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Internationalization test for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry for internationalization test module.
+ * print_utf8() - Print UTF-8 string with (optional) message.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string-private.h"
+#include "language-private.h"
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+
+/*
+ * Local globals...
+ */
+
+static const char * const lang_encodings[] =
+ { /* Encoding strings */
+ "us-ascii", "iso-8859-1",
+ "iso-8859-2", "iso-8859-3",
+ "iso-8859-4", "iso-8859-5",
+ "iso-8859-6", "iso-8859-7",
+ "iso-8859-8", "iso-8859-9",
+ "iso-8859-10", "utf-8",
+ "iso-8859-13", "iso-8859-14",
+ "iso-8859-15", "windows-874",
+ "windows-1250", "windows-1251",
+ "windows-1252", "windows-1253",
+ "windows-1254", "windows-1255",
+ "windows-1256", "windows-1257",
+ "windows-1258", "koi8-r",
+ "koi8-u", "iso-8859-11",
+ "iso-8859-16", "mac-roman",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "windows-932", "windows-936",
+ "windows-949", "windows-950",
+ "windows-1361", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "unknown", "unknown",
+ "euc-cn", "euc-jp",
+ "euc-kr", "euc-tw",
+ "jis-x0213"
+ };
+
+
+/*
+ * Local functions...
+ */
+
+static void print_utf8(const char *msg, const cups_utf8_t *src);
+
+
+/*
+ * 'main()' - Main entry for internationalization test module.
+ */
+
+int /* O - Exit code */
+main(int argc, /* I - Argument Count */
+ char *argv[]) /* I - Arguments */
+{
+ FILE *fp; /* File pointer */
+ int count; /* File line counter */
+ int status, /* Status of current test */
+ errors; /* Error count */
+ char line[1024]; /* File line source string */
+ int len; /* Length (count) of string */
+ char legsrc[1024], /* Legacy source string */
+ legdest[1024], /* Legacy destination string */
+ *legptr; /* Pointer into legacy string */
+ cups_utf8_t utf8latin[] = /* UTF-8 Latin-1 source */
+ { 0x41, 0x20, 0x21, 0x3D, 0x20, 0xC3, 0x84, 0x2E, 0x00 };
+ /* "A != <A WITH DIAERESIS>." - use ISO 8859-1 */
+ cups_utf8_t utf8repla[] = /* UTF-8 Latin-1 replacement */
+ { 0x41, 0x20, 0xE2, 0x89, 0xA2, 0x20, 0xC3, 0x84, 0x2E, 0x00 };
+ /* "A <NOT IDENTICAL TO> <A WITH DIAERESIS>." */
+ cups_utf8_t utf8greek[] = /* UTF-8 Greek source string */
+ { 0x41, 0x20, 0x21, 0x3D, 0x20, 0xCE, 0x91, 0x2E, 0x00 };
+ /* "A != <ALPHA>." - use ISO 8859-7 */
+ cups_utf8_t utf8japan[] = /* UTF-8 Japanese source */
+ { 0x41, 0x20, 0x21, 0x3D, 0x20, 0xEE, 0x9C, 0x80, 0x2E, 0x00 };
+ /* "A != <PRIVATE U+E700>." - use Windows 932 or EUC-JP */
+ cups_utf8_t utf8taiwan[] = /* UTF-8 Chinese source */
+ { 0x41, 0x20, 0x21, 0x3D, 0x20, 0xE4, 0xB9, 0x82, 0x2E, 0x00 };
+ /* "A != <CJK U+4E42>." - use Windows 950 (Big5) or EUC-TW */
+ cups_utf8_t utf8dest[1024]; /* UTF-8 destination string */
+ cups_utf32_t utf32dest[1024]; /* UTF-32 destination string */
+
+
+ if (argc > 1)
+ {
+ int i; /* Looping var */
+ cups_encoding_t encoding; /* Source encoding */
+
+
+ if (argc != 3)
+ {
+ puts("Usage: ./testi18n [filename charset]");
+ return (1);
+ }
+
+ if ((fp = fopen(argv[1], "rb")) == NULL)
+ {
+ perror(argv[1]);
+ return (1);
+ }
+
+ for (i = 0, encoding = CUPS_AUTO_ENCODING;
+ i < (int)(sizeof(lang_encodings) / sizeof(lang_encodings[0]));
+ i ++)
+ if (!_cups_strcasecmp(lang_encodings[i], argv[2]))
+ {
+ encoding = (cups_encoding_t)i;
+ break;
+ }
+
+ if (encoding == CUPS_AUTO_ENCODING)
+ {
+ fprintf(stderr, "%s: Unknown character set!\n", argv[2]);
+ return (1);
+ }
+
+ while (fgets(line, sizeof(line), fp))
+ {
+ if (cupsCharsetToUTF8(utf8dest, line, sizeof(utf8dest), encoding) < 0)
+ {
+ fprintf(stderr, "%s: Unable to convert line: %s", argv[1], line);
+ return (1);
+ }
+
+ fputs((char *)utf8dest, stdout);
+ }
+
+ fclose(fp);
+ return (0);
+ }
+
+ /*
+ * Start with some conversion tests from a UTF-8 test file.
+ */
+
+ errors = 0;
+
+ if ((fp = fopen("utf8demo.txt", "rb")) == NULL)
+ {
+ perror("utf8demo.txt");
+ return (1);
+ }
+
+ /*
+ * cupsUTF8ToUTF32
+ */
+
+ fputs("cupsUTF8ToUTF32 of utfdemo.txt: ", stdout);
+
+ for (count = 0, status = 0; fgets(line, sizeof(line), fp);)
+ {
+ count ++;
+
+ if (cupsUTF8ToUTF32(utf32dest, (cups_utf8_t *)line, 1024) < 0)
+ {
+ printf("FAIL (UTF-8 to UTF-32 on line %d)\n", count);
+ errors ++;
+ status = 1;
+ break;
+ }
+ }
+
+ if (!status)
+ puts("PASS");
+
+ /*
+ * cupsUTF8ToCharset(CUPS_EUC_JP)
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_EUC_JP) of utfdemo.txt: ", stdout);
+
+ rewind(fp);
+
+ for (count = 0, status = 0; fgets(line, sizeof(line), fp);)
+ {
+ count ++;
+
+ len = cupsUTF8ToCharset(legdest, (cups_utf8_t *)line, 1024, CUPS_EUC_JP);
+ if (len < 0)
+ {
+ printf("FAIL (UTF-8 to EUC-JP on line %d)\n", count);
+ errors ++;
+ status = 1;
+ break;
+ }
+ }
+
+ if (!status)
+ puts("PASS");
+
+ fclose(fp);
+
+ /*
+ * Test UTF-8 to legacy charset (ISO 8859-1)...
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_ISO8859_1): ", stdout);
+
+ legdest[0] = 0;
+
+ len = cupsUTF8ToCharset(legdest, utf8latin, 1024, CUPS_ISO8859_1);
+ if (len < 0)
+ {
+ printf("FAIL (len=%d)\n", len);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * cupsCharsetToUTF8
+ */
+
+ fputs("cupsCharsetToUTF8(CUPS_ISO8859_1): ", stdout);
+
+ strlcpy(legsrc, legdest, sizeof(legsrc));
+
+ len = cupsCharsetToUTF8(utf8dest, legsrc, 1024, CUPS_ISO8859_1);
+ if (len != strlen((char *)utf8latin))
+ {
+ printf("FAIL (len=%d, expected %d)\n", len, (int)strlen((char *)utf8latin));
+ print_utf8(" utf8latin", utf8latin);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (memcmp(utf8latin, utf8dest, len))
+ {
+ puts("FAIL (results do not match)");
+ print_utf8(" utf8latin", utf8latin);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (cupsUTF8ToCharset(legdest, utf8repla, 1024, CUPS_ISO8859_1) < 0)
+ {
+ puts("FAIL (replacement characters do not work!)");
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Test UTF-8 to/from legacy charset (ISO 8859-7)...
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_ISO8859_7): ", stdout);
+
+ if (cupsUTF8ToCharset(legdest, utf8greek, 1024, CUPS_ISO8859_7) < 0)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (legptr = legdest; *legptr && *legptr != '?'; legptr ++);
+
+ if (*legptr)
+ {
+ puts("FAIL (unknown character)");
+ errors ++;
+ }
+ else
+ puts("PASS");
+ }
+
+ fputs("cupsCharsetToUTF8(CUPS_ISO8859_7): ", stdout);
+
+ strlcpy(legsrc, legdest, sizeof(legsrc));
+
+ len = cupsCharsetToUTF8(utf8dest, legsrc, 1024, CUPS_ISO8859_7);
+ if (len != strlen((char *)utf8greek))
+ {
+ printf("FAIL (len=%d, expected %d)\n", len, (int)strlen((char *)utf8greek));
+ print_utf8(" utf8greek", utf8greek);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (memcmp(utf8greek, utf8dest, len))
+ {
+ puts("FAIL (results do not match)");
+ print_utf8(" utf8greek", utf8greek);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Test UTF-8 to/from legacy charset (Windows 932)...
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_WINDOWS_932): ", stdout);
+
+ if (cupsUTF8ToCharset(legdest, utf8japan, 1024, CUPS_WINDOWS_932) < 0)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (legptr = legdest; *legptr && *legptr != '?'; legptr ++);
+
+ if (*legptr)
+ {
+ puts("FAIL (unknown character)");
+ errors ++;
+ }
+ else
+ puts("PASS");
+ }
+
+ fputs("cupsCharsetToUTF8(CUPS_WINDOWS_932): ", stdout);
+
+ strlcpy(legsrc, legdest, sizeof(legsrc));
+
+ len = cupsCharsetToUTF8(utf8dest, legsrc, 1024, CUPS_WINDOWS_932);
+ if (len != strlen((char *)utf8japan))
+ {
+ printf("FAIL (len=%d, expected %d)\n", len, (int)strlen((char *)utf8japan));
+ print_utf8(" utf8japan", utf8japan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (memcmp(utf8japan, utf8dest, len))
+ {
+ puts("FAIL (results do not match)");
+ print_utf8(" utf8japan", utf8japan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Test UTF-8 to/from legacy charset (EUC-JP)...
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_EUC_JP): ", stdout);
+
+ if (cupsUTF8ToCharset(legdest, utf8japan, 1024, CUPS_EUC_JP) < 0)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (legptr = legdest; *legptr && *legptr != '?'; legptr ++);
+
+ if (*legptr)
+ {
+ puts("FAIL (unknown character)");
+ errors ++;
+ }
+ else
+ puts("PASS");
+ }
+
+#ifndef __linux
+ fputs("cupsCharsetToUTF8(CUPS_EUC_JP): ", stdout);
+
+ strlcpy(legsrc, legdest, sizeof(legsrc));
+
+ len = cupsCharsetToUTF8(utf8dest, legsrc, 1024, CUPS_EUC_JP);
+ if (len != strlen((char *)utf8japan))
+ {
+ printf("FAIL (len=%d, expected %d)\n", len, (int)strlen((char *)utf8japan));
+ print_utf8(" utf8japan", utf8japan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (memcmp(utf8japan, utf8dest, len))
+ {
+ puts("FAIL (results do not match)");
+ print_utf8(" utf8japan", utf8japan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else
+ puts("PASS");
+#endif /* !__linux */
+
+ /*
+ * Test UTF-8 to/from legacy charset (Windows 950)...
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_WINDOWS_950): ", stdout);
+
+ if (cupsUTF8ToCharset(legdest, utf8taiwan, 1024, CUPS_WINDOWS_950) < 0)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (legptr = legdest; *legptr && *legptr != '?'; legptr ++);
+
+ if (*legptr)
+ {
+ puts("FAIL (unknown character)");
+ errors ++;
+ }
+ else
+ puts("PASS");
+ }
+
+ fputs("cupsCharsetToUTF8(CUPS_WINDOWS_950): ", stdout);
+
+ strlcpy(legsrc, legdest, sizeof(legsrc));
+
+ len = cupsCharsetToUTF8(utf8dest, legsrc, 1024, CUPS_WINDOWS_950);
+ if (len != strlen((char *)utf8taiwan))
+ {
+ printf("FAIL (len=%d, expected %d)\n", len, (int)strlen((char *)utf8taiwan));
+ print_utf8(" utf8taiwan", utf8taiwan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (memcmp(utf8taiwan, utf8dest, len))
+ {
+ puts("FAIL (results do not match)");
+ print_utf8(" utf8taiwan", utf8taiwan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Test UTF-8 to/from legacy charset (EUC-TW)...
+ */
+
+ fputs("cupsUTF8ToCharset(CUPS_EUC_TW): ", stdout);
+
+ if (cupsUTF8ToCharset(legdest, utf8taiwan, 1024, CUPS_EUC_TW) < 0)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (legptr = legdest; *legptr && *legptr != '?'; legptr ++);
+
+ if (*legptr)
+ {
+ puts("FAIL (unknown character)");
+ errors ++;
+ }
+ else
+ puts("PASS");
+ }
+
+ fputs("cupsCharsetToUTF8(CUPS_EUC_TW): ", stdout);
+
+ strlcpy(legsrc, legdest, sizeof(legsrc));
+
+ len = cupsCharsetToUTF8(utf8dest, legsrc, 1024, CUPS_EUC_TW);
+ if (len != strlen((char *)utf8taiwan))
+ {
+ printf("FAIL (len=%d, expected %d)\n", len, (int)strlen((char *)utf8taiwan));
+ print_utf8(" utf8taiwan", utf8taiwan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else if (memcmp(utf8taiwan, utf8dest, len))
+ {
+ puts("FAIL (results do not match)");
+ print_utf8(" utf8taiwan", utf8taiwan);
+ print_utf8(" utf8dest", utf8dest);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+#if 0
+ /*
+ * Test UTF-8 (16-bit) to UTF-32 (w/ BOM)...
+ */
+ if (verbose)
+ printf("\ntesti18n: Testing UTF-8 to UTF-32 (w/ BOM)...\n");
+ len = cupsUTF8ToUTF32(utf32dest, utf8good, 1024);
+ if (len < 0)
+ return (1);
+ if (verbose)
+ {
+ print_utf8(" utf8good ", utf8good);
+ print_utf32(" utf32dest", utf32dest);
+ }
+ memcpy (utf32src, utf32dest, (len + 1) * sizeof(cups_utf32_t));
+ len = cupsUTF32ToUTF8(utf8dest, utf32src, 1024);
+ if (len < 0)
+ return (1);
+ if (len != strlen ((char *) utf8good))
+ return (1);
+ if (memcmp(utf8good, utf8dest, len) != 0)
+ return (1);
+
+ /*
+ * Test invalid UTF-8 (16-bit) to UTF-32 (w/ BOM)...
+ */
+ if (verbose)
+ printf("\ntesti18n: Testing UTF-8 bad 16-bit source string...\n");
+ len = cupsUTF8ToUTF32(utf32dest, utf8bad, 1024);
+ if (len >= 0)
+ return (1);
+ if (verbose)
+ print_utf8(" utf8bad ", utf8bad);
+
+ /*
+ * Test _cupsCharmapFlush()...
+ */
+ if (verbose)
+ printf("\ntesti18n: Testing _cupsCharmapFlush()...\n");
+ _cupsCharmapFlush();
+ return (0);
+#endif /* 0 */
+
+ return (errors > 0);
+}
+
+
+/*
+ * 'print_utf8()' - Print UTF-8 string with (optional) message.
+ */
+
+static void
+print_utf8(const char *msg, /* I - Message String */
+ const cups_utf8_t *src) /* I - UTF-8 Source String */
+{
+ const char *prefix; /* Prefix string */
+
+
+ if (msg)
+ printf("%s:", msg);
+
+ for (prefix = " "; *src; src ++)
+ {
+ printf("%s%02x", prefix, *src);
+
+ if ((src[0] & 0x80) && (src[1] & 0x80))
+ prefix = "";
+ else
+ prefix = " ";
+ }
+
+ putchar('\n');
+}
+
+
+/*
+ * End of "$Id: testi18n.c 10996 2013-05-29 11:51:34Z msweet $"
+ */
diff --git a/cups/libs/cups/testipp.c b/cups/libs/cups/testipp.c
new file mode 100644
index 000000000..d4cf5b425
--- /dev/null
+++ b/cups/libs/cups/testipp.c
@@ -0,0 +1,1016 @@
+/*
+ * "$Id: testipp.c 11890 2014-05-22 13:59:21Z msweet $"
+ *
+ * IPP test program for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * hex_dump() - Produce a hex dump of a buffer.
+ * print_attributes() - Print the attributes in a request...
+ * read_cb() - Read data from a buffer.
+ * write_cb() - Write data into a buffer.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "file.h"
+#include "string-private.h"
+#include "ipp-private.h"
+#ifdef WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+# include <fcntl.h>
+#endif /* WIN32 */
+
+
+/*
+ * Local types...
+ */
+
+typedef struct _ippdata_t
+{
+ size_t rpos, /* Read position */
+ wused, /* Bytes used */
+ wsize; /* Max size of buffer */
+ ipp_uchar_t *wbuffer; /* Buffer */
+} _ippdata_t;
+
+
+/*
+ * Local globals...
+ */
+
+ipp_uchar_t collection[] = /* Collection buffer */
+ {
+ 0x01, 0x01, /* IPP version */
+ 0x00, 0x02, /* Print-Job operation */
+ 0x00, 0x00, 0x00, 0x01,
+ /* Request ID */
+
+ IPP_TAG_OPERATION,
+
+ IPP_TAG_CHARSET,
+ 0x00, 0x12, /* Name length + name */
+ 'a','t','t','r','i','b','u','t','e','s','-',
+ 'c','h','a','r','s','e','t',
+ 0x00, 0x05, /* Value length + value */
+ 'u','t','f','-','8',
+
+ IPP_TAG_LANGUAGE,
+ 0x00, 0x1b, /* Name length + name */
+ 'a','t','t','r','i','b','u','t','e','s','-',
+ 'n','a','t','u','r','a','l','-','l','a','n',
+ 'g','u','a','g','e',
+ 0x00, 0x02, /* Value length + value */
+ 'e','n',
+
+ IPP_TAG_URI,
+ 0x00, 0x0b, /* Name length + name */
+ 'p','r','i','n','t','e','r','-','u','r','i',
+ 0x00, 0x1c, /* Value length + value */
+ 'i','p','p',':','/','/','l','o','c','a','l',
+ 'h','o','s','t','/','p','r','i','n','t','e',
+ 'r','s','/','f','o','o',
+
+ IPP_TAG_JOB, /* job group tag */
+
+ IPP_TAG_BEGIN_COLLECTION,
+ /* begCollection tag */
+ 0x00, 0x09, /* Name length + name */
+ 'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
+ 0x00, 0x00, /* No value */
+ IPP_TAG_MEMBERNAME, /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0a, /* Value length + value */
+ 'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
+ IPP_TAG_BEGIN_COLLECTION,
+ /* begCollection tag */
+ 0x00, 0x00, /* Name length + name */
+ 0x00, 0x00, /* No value */
+ IPP_TAG_MEMBERNAME,
+ /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0b, /* Value length + value */
+ 'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
+ IPP_TAG_INTEGER, /* integer tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x04, /* Value length + value */
+ 0x00, 0x00, 0x54, 0x56,
+ IPP_TAG_MEMBERNAME,
+ /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0b, /* Value length + value */
+ 'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
+ IPP_TAG_INTEGER, /* integer tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x04, /* Value length + value */
+ 0x00, 0x00, 0x6d, 0x24,
+ IPP_TAG_END_COLLECTION,
+ /* endCollection tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x00, /* No value */
+ IPP_TAG_MEMBERNAME, /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0b, /* Value length + value */
+ 'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',
+ IPP_TAG_KEYWORD, /* keyword tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x04, /* Value length + value */
+ 'b', 'l', 'u', 'e',
+
+ IPP_TAG_MEMBERNAME, /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0a, /* Value length + value */
+ 'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',
+ IPP_TAG_KEYWORD, /* keyword tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x05, /* Value length + value */
+ 'p', 'l', 'a', 'i', 'n',
+ IPP_TAG_END_COLLECTION,
+ /* endCollection tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x00, /* No value */
+
+ IPP_TAG_BEGIN_COLLECTION,
+ /* begCollection tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x00, /* No value */
+ IPP_TAG_MEMBERNAME, /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0a, /* Value length + value */
+ 'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
+ IPP_TAG_BEGIN_COLLECTION,
+ /* begCollection tag */
+ 0x00, 0x00, /* Name length + name */
+ 0x00, 0x00, /* No value */
+ IPP_TAG_MEMBERNAME,
+ /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0b, /* Value length + value */
+ 'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
+ IPP_TAG_INTEGER, /* integer tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x04, /* Value length + value */
+ 0x00, 0x00, 0x52, 0x08,
+ IPP_TAG_MEMBERNAME,
+ /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0b, /* Value length + value */
+ 'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
+ IPP_TAG_INTEGER, /* integer tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x04, /* Value length + value */
+ 0x00, 0x00, 0x74, 0x04,
+ IPP_TAG_END_COLLECTION,
+ /* endCollection tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x00, /* No value */
+ IPP_TAG_MEMBERNAME, /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0b, /* Value length + value */
+ 'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',
+ IPP_TAG_KEYWORD, /* keyword tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x05, /* Value length + value */
+ 'p', 'l', 'a', 'i', 'd',
+
+ IPP_TAG_MEMBERNAME, /* memberAttrName tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x0a, /* Value length + value */
+ 'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',
+ IPP_TAG_KEYWORD, /* keyword tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x06, /* Value length + value */
+ 'g', 'l', 'o', 's', 's', 'y',
+ IPP_TAG_END_COLLECTION,
+ /* endCollection tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x00, /* No value */
+
+ IPP_TAG_END /* end tag */
+ };
+
+ipp_uchar_t mixed[] = /* Mixed value buffer */
+ {
+ 0x01, 0x01, /* IPP version */
+ 0x00, 0x02, /* Print-Job operation */
+ 0x00, 0x00, 0x00, 0x01,
+ /* Request ID */
+
+ IPP_TAG_OPERATION,
+
+ IPP_TAG_INTEGER, /* integer tag */
+ 0x00, 0x1f, /* Name length + name */
+ 'n', 'o', 't', 'i', 'f', 'y', '-', 'l', 'e', 'a', 's', 'e',
+ '-', 'd', 'u', 'r', 'a', 't', 'i', 'o', 'n', '-', 's', 'u',
+ 'p', 'p', 'o', 'r', 't', 'e', 'd',
+ 0x00, 0x04, /* Value length + value */
+ 0x00, 0x00, 0x00, 0x01,
+
+ IPP_TAG_RANGE, /* rangeOfInteger tag */
+ 0x00, 0x00, /* No name */
+ 0x00, 0x08, /* Value length + value */
+ 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x20,
+
+ IPP_TAG_END /* end tag */
+ };
+
+
+/*
+ * Local functions...
+ */
+
+void hex_dump(const char *title, ipp_uchar_t *buffer, int bytes);
+void print_attributes(ipp_t *ipp, int indent);
+ssize_t read_cb(_ippdata_t *data, ipp_uchar_t *buffer, size_t bytes);
+ssize_t write_cb(_ippdata_t *data, ipp_uchar_t *buffer, size_t bytes);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ _ippdata_t data; /* IPP buffer */
+ ipp_uchar_t buffer[8192]; /* Write buffer data */
+ ipp_t *cols[2], /* Collections */
+ *size; /* media-size collection */
+ ipp_t *request; /* Request */
+ ipp_attribute_t *media_col, /* media-col attribute */
+ *media_size, /* media-size attribute */
+ *attr; /* Other attribute */
+ ipp_state_t state; /* State */
+ int length; /* Length of data */
+ cups_file_t *fp; /* File pointer */
+ int i; /* Looping var */
+ int status; /* Status of tests (0 = success, 1 = fail) */
+#ifdef DEBUG
+ const char *name; /* Option name */
+#endif /* DEBUG */
+
+
+ status = 0;
+
+ if (argc == 1)
+ {
+ /*
+ * Test request generation code...
+ */
+
+ printf("Create Sample Request: ");
+
+ request = ippNew();
+ request->request.op.version[0] = 0x01;
+ request->request.op.version[1] = 0x01;
+ request->request.op.operation_id = IPP_OP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, "en");
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, "ipp://localhost/printers/foo");
+
+ cols[0] = ippNew();
+ size = ippNew();
+ ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "x-dimension", 21590);
+ ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "y-dimension", 27940);
+ ippAddCollection(cols[0], IPP_TAG_JOB, "media-size", size);
+ ippDelete(size);
+ ippAddString(cols[0], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-color", NULL,
+ "blue");
+ ippAddString(cols[0], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
+ "plain");
+
+ cols[1] = ippNew();
+ size = ippNew();
+ ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "x-dimension", 21000);
+ ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "y-dimension", 29700);
+ ippAddCollection(cols[1], IPP_TAG_JOB, "media-size", size);
+ ippDelete(size);
+ ippAddString(cols[1], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-color", NULL,
+ "plaid");
+ ippAddString(cols[1], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
+ "glossy");
+
+ ippAddCollections(request, IPP_TAG_JOB, "media-col", 2,
+ (const ipp_t **)cols);
+ ippDelete(cols[0]);
+ ippDelete(cols[1]);
+
+ length = ippLength(request);
+ if (length != sizeof(collection))
+ {
+ printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
+ length, (int)sizeof(collection));
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Write test #1...
+ */
+
+ printf("Write Sample to Memory: ");
+
+ data.wused = 0;
+ data.wsize = sizeof(buffer);
+ data.wbuffer = buffer;
+
+ while ((state = ippWriteIO(&data, (ipp_iocb_t)write_cb, 1, NULL,
+ request)) != IPP_STATE_DATA)
+ if (state == IPP_STATE_ERROR)
+ break;
+
+ if (state != IPP_STATE_DATA)
+ {
+ printf("FAIL - %d bytes written.\n", (int)data.wused);
+ status = 1;
+ }
+ else if (data.wused != sizeof(collection))
+ {
+ printf("FAIL - wrote %d bytes, expected %d bytes!\n", (int)data.wused,
+ (int)sizeof(collection));
+ hex_dump("Bytes Written", data.wbuffer, data.wused);
+ hex_dump("Baseline", collection, sizeof(collection));
+ status = 1;
+ }
+ else if (memcmp(data.wbuffer, collection, data.wused))
+ {
+ for (i = 0; i < data.wused; i ++)
+ if (data.wbuffer[i] != collection[i])
+ break;
+
+ printf("FAIL - output does not match baseline at 0x%04x!\n", i);
+ hex_dump("Bytes Written", data.wbuffer, data.wused);
+ hex_dump("Baseline", collection, sizeof(collection));
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ ippDelete(request);
+
+ /*
+ * Read the data back in and confirm...
+ */
+
+ printf("Read Sample from Memory: ");
+
+ request = ippNew();
+ data.rpos = 0;
+
+ while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL,
+ request)) != IPP_STATE_DATA)
+ if (state == IPP_STATE_ERROR)
+ break;
+
+ length = ippLength(request);
+
+ if (state != IPP_STATE_DATA)
+ {
+ printf("FAIL - %d bytes read.\n", (int)data.rpos);
+ status = 1;
+ }
+ else if (data.rpos != data.wused)
+ {
+ printf("FAIL - read %d bytes, expected %d bytes!\n", (int)data.rpos,
+ (int)data.wused);
+ print_attributes(request, 8);
+ status = 1;
+ }
+ else if (length != sizeof(collection))
+ {
+ printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
+ length, (int)sizeof(collection));
+ print_attributes(request, 8);
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ fputs("ippFindAttribute(media-col): ", stdout);
+ if ((media_col = ippFindAttribute(request, "media-col",
+ IPP_TAG_BEGIN_COLLECTION)) == NULL)
+ {
+ if ((media_col = ippFindAttribute(request, "media-col",
+ IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (not found)");
+ else
+ printf("FAIL (wrong type - %s)\n", ippTagString(media_col->value_tag));
+
+ status = 1;
+ }
+ else if (media_col->num_values != 2)
+ {
+ printf("FAIL (wrong count - %d)\n", media_col->num_values);
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ if (media_col)
+ {
+ fputs("ippFindAttribute(media-size 1): ", stdout);
+ if ((media_size = ippFindAttribute(media_col->values[0].collection,
+ "media-size",
+ IPP_TAG_BEGIN_COLLECTION)) == NULL)
+ {
+ if ((media_size = ippFindAttribute(media_col->values[0].collection,
+ "media-col",
+ IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (not found)");
+ else
+ printf("FAIL (wrong type - %s)\n",
+ ippTagString(media_size->value_tag));
+
+ status = 1;
+ }
+ else
+ {
+ if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "x-dimension", IPP_TAG_INTEGER)) == NULL)
+ {
+ if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "x-dimension", IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (missing x-dimension)");
+ else
+ printf("FAIL (wrong type for x-dimension - %s)\n",
+ ippTagString(attr->value_tag));
+
+ status = 1;
+ }
+ else if (attr->values[0].integer != 21590)
+ {
+ printf("FAIL (wrong value for x-dimension - %d)\n",
+ attr->values[0].integer);
+ status = 1;
+ }
+ else if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "y-dimension",
+ IPP_TAG_INTEGER)) == NULL)
+ {
+ if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "y-dimension", IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (missing y-dimension)");
+ else
+ printf("FAIL (wrong type for y-dimension - %s)\n",
+ ippTagString(attr->value_tag));
+
+ status = 1;
+ }
+ else if (attr->values[0].integer != 27940)
+ {
+ printf("FAIL (wrong value for y-dimension - %d)\n",
+ attr->values[0].integer);
+ status = 1;
+ }
+ else
+ puts("PASS");
+ }
+
+ fputs("ippFindAttribute(media-size 2): ", stdout);
+ if ((media_size = ippFindAttribute(media_col->values[1].collection,
+ "media-size",
+ IPP_TAG_BEGIN_COLLECTION)) == NULL)
+ {
+ if ((media_size = ippFindAttribute(media_col->values[1].collection,
+ "media-col",
+ IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (not found)");
+ else
+ printf("FAIL (wrong type - %s)\n",
+ ippTagString(media_size->value_tag));
+
+ status = 1;
+ }
+ else
+ {
+ if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "x-dimension",
+ IPP_TAG_INTEGER)) == NULL)
+ {
+ if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "x-dimension", IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (missing x-dimension)");
+ else
+ printf("FAIL (wrong type for x-dimension - %s)\n",
+ ippTagString(attr->value_tag));
+
+ status = 1;
+ }
+ else if (attr->values[0].integer != 21000)
+ {
+ printf("FAIL (wrong value for x-dimension - %d)\n",
+ attr->values[0].integer);
+ status = 1;
+ }
+ else if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "y-dimension",
+ IPP_TAG_INTEGER)) == NULL)
+ {
+ if ((attr = ippFindAttribute(media_size->values[0].collection,
+ "y-dimension", IPP_TAG_ZERO)) == NULL)
+ puts("FAIL (missing y-dimension)");
+ else
+ printf("FAIL (wrong type for y-dimension - %s)\n",
+ ippTagString(attr->value_tag));
+
+ status = 1;
+ }
+ else if (attr->values[0].integer != 29700)
+ {
+ printf("FAIL (wrong value for y-dimension - %d)\n",
+ attr->values[0].integer);
+ status = 1;
+ }
+ else
+ puts("PASS");
+ }
+ }
+
+ ippDelete(request);
+
+ /*
+ * Read the mixed data and confirm we converted everything to rangeOfInteger
+ * values...
+ */
+
+ printf("Read Mixed integer/rangeOfInteger from Memory: ");
+
+ request = ippNew();
+ data.rpos = 0;
+ data.wused = sizeof(mixed);
+ data.wsize = sizeof(mixed);
+ data.wbuffer = mixed;
+
+ while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL,
+ request)) != IPP_STATE_DATA)
+ if (state == IPP_STATE_ERROR)
+ break;
+
+ length = ippLength(request);
+
+ if (state != IPP_STATE_DATA)
+ {
+ printf("FAIL - %d bytes read.\n", (int)data.rpos);
+ status = 1;
+ }
+ else if (data.rpos != sizeof(mixed))
+ {
+ printf("FAIL - read %d bytes, expected %d bytes!\n", (int)data.rpos,
+ (int)sizeof(mixed));
+ print_attributes(request, 8);
+ status = 1;
+ }
+ else if (length != (sizeof(mixed) + 4))
+ {
+ printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
+ length, (int)sizeof(mixed) + 4);
+ print_attributes(request, 8);
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ fputs("ippFindAttribute(notify-lease-duration-supported): ", stdout);
+ if ((attr = ippFindAttribute(request, "notify-lease-duration-supported",
+ IPP_TAG_ZERO)) == NULL)
+ {
+ puts("FAIL (not found)");
+ status = 1;
+ }
+ else if (attr->value_tag != IPP_TAG_RANGE)
+ {
+ printf("FAIL (wrong type - %s)\n", ippTagString(attr->value_tag));
+ status = 1;
+ }
+ else if (attr->num_values != 2)
+ {
+ printf("FAIL (wrong count - %d)\n", attr->num_values);
+ status = 1;
+ }
+ else if (attr->values[0].range.lower != 1 ||
+ attr->values[0].range.upper != 1 ||
+ attr->values[1].range.lower != 16 ||
+ attr->values[1].range.upper != 32)
+ {
+ printf("FAIL (wrong values - %d,%d and %d,%d)\n",
+ attr->values[0].range.lower,
+ attr->values[0].range.upper,
+ attr->values[1].range.lower,
+ attr->values[1].range.upper);
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ ippDelete(request);
+
+#ifdef DEBUG
+ /*
+ * Test that private option array is sorted...
+ */
+
+ fputs("_ippCheckOptions: ", stdout);
+ if ((name = _ippCheckOptions()) == NULL)
+ puts("PASS");
+ else
+ {
+ printf("FAIL (\"%s\" out of order)\n", name);
+ status = 1;
+ }
+#endif /* DEBUG */
+
+ /*
+ * Test _ippFindOption() private API...
+ */
+
+ fputs("_ippFindOption(\"printer-type\"): ", stdout);
+ if (_ippFindOption("printer-type"))
+ puts("PASS");
+ else
+ {
+ puts("FAIL");
+ status = 1;
+ }
+
+ /*
+ * Summarize...
+ */
+
+ putchar('\n');
+
+ if (status)
+ puts("Core IPP tests failed.");
+ else
+ puts("Core IPP tests passed.");
+ }
+ else
+ {
+ /*
+ * Read IPP files...
+ */
+
+ for (i = 1; i < argc; i ++)
+ {
+ if ((fp = cupsFileOpen(argv[i], "r")) == NULL)
+ {
+ printf("Unable to open \"%s\" - %s\n", argv[i], strerror(errno));
+ status = 1;
+ continue;
+ }
+
+ request = ippNew();
+ while ((state = ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
+ request)) == IPP_STATE_ATTRIBUTE);
+
+ if (state != IPP_STATE_DATA)
+ {
+ printf("Error reading IPP message from \"%s\"!\n", argv[i]);
+ status = 1;
+ }
+ else
+ {
+ printf("\n%s:\n", argv[i]);
+ print_attributes(request, 4);
+ }
+
+ ippDelete(request);
+ cupsFileClose(fp);
+ }
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'hex_dump()' - Produce a hex dump of a buffer.
+ */
+
+void
+hex_dump(const char *title, /* I - Title */
+ ipp_uchar_t *buffer, /* I - Buffer to dump */
+ int bytes) /* I - Number of bytes */
+{
+ int i, j; /* Looping vars */
+ int ch; /* Current ASCII char */
+
+
+ /*
+ * Show lines of 16 bytes at a time...
+ */
+
+ printf(" %s:\n", title);
+
+ for (i = 0; i < bytes; i += 16)
+ {
+ /*
+ * Show the offset...
+ */
+
+ printf(" %04x ", i);
+
+ /*
+ * Then up to 16 bytes in hex...
+ */
+
+ for (j = 0; j < 16; j ++)
+ if ((i + j) < bytes)
+ printf(" %02x", buffer[i + j]);
+ else
+ printf(" ");
+
+ /*
+ * Then the ASCII representation of the bytes...
+ */
+
+ putchar(' ');
+ putchar(' ');
+
+ for (j = 0; j < 16 && (i + j) < bytes; j ++)
+ {
+ ch = buffer[i + j] & 127;
+
+ if (ch < ' ' || ch == 127)
+ putchar('.');
+ else
+ putchar(ch);
+ }
+
+ putchar('\n');
+ }
+}
+
+
+/*
+ * 'print_attributes()' - Print the attributes in a request...
+ */
+
+void
+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"
+ };
+
+
+ for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
+ {
+ if (!attr->name && indent == 4)
+ {
+ group = IPP_TAG_ZERO;
+ putchar('\n');
+ continue;
+ }
+
+ if (group != attr->group_tag)
+ {
+ group = attr->group_tag;
+
+ printf("\n%*s%s:\n\n", indent - 4, "", tags[group]);
+ }
+
+ printf("%*s%s (", indent, "", attr->name ? attr->name : "(null)");
+ if (attr->num_values > 1)
+ printf("1setOf ");
+ printf("%s):", tags[attr->value_tag]);
+
+ 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;
+ }
+ }
+}
+
+
+/*
+ * 'read_cb()' - Read data from a buffer.
+ */
+
+ssize_t /* O - Number of bytes read */
+read_cb(_ippdata_t *data, /* I - Data */
+ ipp_uchar_t *buffer, /* O - Buffer to read */
+ size_t bytes) /* I - Number of bytes to read */
+{
+ size_t count; /* Number of bytes */
+
+
+ /*
+ * Copy bytes from the data buffer to the read buffer...
+ */
+
+ if ((count = data->wsize - data->rpos) > bytes)
+ count = bytes;
+
+ memcpy(buffer, data->wbuffer + data->rpos, count);
+ data->rpos += count;
+
+ /*
+ * Return the number of bytes read...
+ */
+
+ return (count);
+}
+
+
+/*
+ * 'write_cb()' - Write data into a buffer.
+ */
+
+ssize_t /* O - Number of bytes written */
+write_cb(_ippdata_t *data, /* I - Data */
+ ipp_uchar_t *buffer, /* I - Buffer to write */
+ size_t bytes) /* I - Number of bytes to write */
+{
+ size_t count; /* Number of bytes */
+
+
+ /*
+ * Loop until all bytes are written...
+ */
+
+ if ((count = data->wsize - data->wused) > bytes)
+ count = bytes;
+
+ memcpy(data->wbuffer + data->wused, buffer, count);
+ data->wused += count;
+
+ /*
+ * Return the number of bytes written...
+ */
+
+ return (count);
+}
+
+
+/*
+ * End of "$Id: testipp.c 11890 2014-05-22 13:59:21Z msweet $".
+ */
diff --git a/cups/libs/cups/testlang.c b/cups/libs/cups/testlang.c
new file mode 100644
index 000000000..7c16f821b
--- /dev/null
+++ b/cups/libs/cups/testlang.c
@@ -0,0 +1,114 @@
+/*
+ * "$Id: testlang.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Localization test program for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Load the specified language and show the strings for yes and no.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * 'main()' - Load the specified language and show the strings for yes and no.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int errors = 0; /* Number of errors */
+ cups_lang_t *language; /* Message catalog */
+ cups_lang_t *language2; /* Message catalog */
+ struct lconv *loc; /* Locale data */
+ char buffer[1024]; /* String buffer */
+ double number; /* Number */
+ static const char * const tests[] = /* Test strings */
+ {
+ "1",
+ "-1",
+ "3",
+ "5.125"
+ };
+
+
+ _cupsSetLocale(argv);
+
+ if (argc == 1)
+ {
+ language = cupsLangDefault();
+ language2 = cupsLangDefault();
+ }
+ else
+ {
+ language = cupsLangGet(argv[1]);
+ language2 = cupsLangGet(argv[1]);
+ }
+
+ if (language != language2)
+ {
+ errors ++;
+
+ puts("**** ERROR: Language cache did not work! ****");
+ puts("First result from cupsLangGet:");
+ }
+
+ 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"));
+
+ if (language != language2)
+ {
+ puts("Second result from cupsLangGet:");
+
+ printf("Language = \"%s\"\n", language2->language);
+ printf("Encoding = \"%s\"\n", _cupsEncodingName(language2->encoding));
+ printf("No = \"%s\"\n", _cupsLangString(language2, "No"));
+ printf("Yes = \"%s\"\n", _cupsLangString(language2, "Yes"));
+ }
+
+ loc = localeconv();
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i ++)
+ {
+ number = _cupsStrScand(tests[i], NULL, loc);
+
+ printf("_cupsStrScand(\"%s\") number=%f\n", tests[i], number);
+
+ _cupsStrFormatd(buffer, buffer + sizeof(buffer), number, loc);
+
+ printf("_cupsStrFormatd(%f) buffer=\"%s\"\n", number, buffer);
+
+ if (strcmp(buffer, tests[i]))
+ {
+ errors ++;
+ puts("**** ERROR: Bad formatted number! ****");
+ }
+ }
+
+ return (errors > 0);
+}
+
+
+/*
+ * End of "$Id: testlang.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/testoptions.c b/cups/libs/cups/testoptions.c
new file mode 100644
index 000000000..d14b64f8d
--- /dev/null
+++ b/cups/libs/cups/testoptions.c
@@ -0,0 +1,116 @@
+/*
+ * "$Id: testoptions.c 1992 2010-03-24 14:32:08Z msweet $"
+ *
+ * Option test program for CUPS.
+ *
+ * Copyright 2008-2010 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Test option processing functions.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+
+
+/*
+ * 'main()' - Test option processing functions.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int status = 0, /* Exit status */
+ num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ const char *value; /* Value of an option */
+
+
+ if (argc == 1)
+ {
+ /*
+ * cupsParseOptions()
+ */
+
+ fputs("cupsParseOptions: ", stdout);
+
+ num_options = cupsParseOptions("foo=1234 "
+ "bar=\"One Fish\",\"Two Fish\",\"Red Fish\","
+ "\"Blue Fish\" "
+ "baz={param1=1 param2=2} "
+ "foobar=FOO\\ BAR "
+ "barfoo=barfoo "
+ "barfoo=\"\'BAR FOO\'\"", 0, &options);
+
+ if (num_options != 5)
+ {
+ printf("FAIL (num_options=%d, expected 5)\n", num_options);
+ status ++;
+ }
+ else if ((value = cupsGetOption("foo", num_options, options)) == NULL ||
+ strcmp(value, "1234"))
+ {
+ printf("FAIL (foo=\"%s\", expected \"1234\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("bar", num_options, options)) == NULL ||
+ strcmp(value, "One Fish,Two Fish,Red Fish,Blue Fish"))
+ {
+ printf("FAIL (bar=\"%s\", expected \"One Fish,Two Fish,Red Fish,Blue "
+ "Fish\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("baz", num_options, options)) == NULL ||
+ strcmp(value, "{param1=1 param2=2}"))
+ {
+ printf("FAIL (baz=\"%s\", expected \"{param1=1 param2=2}\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("foobar", num_options, options)) == NULL ||
+ strcmp(value, "FOO BAR"))
+ {
+ printf("FAIL (foobar=\"%s\", expected \"FOO BAR\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("barfoo", num_options, options)) == NULL ||
+ strcmp(value, "\'BAR FOO\'"))
+ {
+ printf("FAIL (barfoo=\"%s\", expected \"\'BAR FOO\'\")\n", value);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ int i; /* Looping var */
+ cups_option_t *option; /* Current option */
+
+
+ num_options = cupsParseOptions(argv[1], 0, &options);
+
+ for (i = 0, option = options; i < num_options; i ++, option ++)
+ printf("options[%d].name=\"%s\", value=\"%s\"\n", i, option->name,
+ option->value);
+ }
+
+ exit (status);
+}
+
+
+/*
+ * End of "$Id: testoptions.c 1992 2010-03-24 14:32:08Z msweet $".
+ */
diff --git a/cups/libs/cups/testppd.c b/cups/libs/cups/testppd.c
new file mode 100644
index 000000000..640c4801f
--- /dev/null
+++ b/cups/libs/cups/testppd.c
@@ -0,0 +1,1113 @@
+/*
+ * "$Id: testppd.c 11060 2013-06-25 15:02:18Z msweet $"
+ *
+ * PPD test program for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#undef _CUPS_NO_DEPRECATED
+#include "cups-private.h"
+#include <sys/stat.h>
+#ifdef WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+# include <fcntl.h>
+#endif /* WIN32 */
+
+
+/*
+ * Test data...
+ */
+
+static const char *default_code =
+ "[{\n"
+ "%%BeginFeature: *InstalledDuplexer False\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *PageRegion Letter\n"
+ "PageRegion=Letter\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *InputSlot Tray\n"
+ "InputSlot=Tray\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *MediaType Plain\n"
+ "MediaType=Plain\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *IntOption None\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *StringOption None\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n";
+
+static const char *custom_code =
+ "[{\n"
+ "%%BeginFeature: *InstalledDuplexer False\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *InputSlot Tray\n"
+ "InputSlot=Tray\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *MediaType Plain\n"
+ "MediaType=Plain\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *IntOption None\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *CustomStringOption True\n"
+ "(value\\0502\\051)\n"
+ "(value 1)\n"
+ "StringOption=Custom\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *CustomPageSize True\n"
+ "400\n"
+ "500\n"
+ "0\n"
+ "0\n"
+ "0\n"
+ "PageSize=Custom\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n";
+
+static const char *default2_code =
+ "[{\n"
+ "%%BeginFeature: *InstalledDuplexer False\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *InputSlot Tray\n"
+ "InputSlot=Tray\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *Quality Normal\n"
+ "Quality=Normal\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *IntOption None\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n"
+ "[{\n"
+ "%%BeginFeature: *StringOption None\n"
+ "%%EndFeature\n"
+ "} stopped cleartomark\n";
+
+
+/*
+ * '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 */
+ ppd_file_t *ppd; /* PPD file loaded from disk */
+ int status; /* Status of tests (0 = success, 1 = fail) */
+ int conflicts; /* Number of conflicts */
+ char *s; /* String */
+ char buffer[8192]; /* String buffer */
+ const char *text, /* Localized text */
+ *val; /* Option value */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ ppd_size_t minsize, /* Minimum size */
+ maxsize, /* Maximum size */
+ *size; /* Current size */
+ ppd_attr_t *attr; /* Current attribute */
+ _ppd_cache_t *pc; /* PPD cache */
+
+
+ status = 0;
+
+ if (argc == 1)
+ {
+ /*
+ * Setup directories for locale stuff...
+ */
+
+ if (access("locale", 0))
+ {
+ mkdir("locale", 0777);
+ mkdir("locale/fr", 0777);
+ symlink("../../../locale/cups_fr.po", "locale/fr/cups_fr.po");
+ mkdir("locale/zh_TW", 0777);
+ symlink("../../../locale/cups_zh_TW.po", "locale/zh_TW/cups_zh_TW.po");
+ }
+
+ putenv("LOCALEDIR=locale");
+ putenv("SOFTWARE=CUPS");
+
+ /*
+ * Do tests with test.ppd...
+ */
+
+ fputs("ppdOpenFile(test.ppd): ", stdout);
+
+ if ((ppd = _ppdOpenFile("test.ppd", _PPD_LOCALIZATION_ALL)) != NULL)
+ puts("PASS");
+ else
+ {
+ ppd_status_t err; /* Last error in file */
+ int line; /* Line number in file */
+
+
+ status ++;
+ err = ppdLastError(&line);
+
+ printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
+ }
+
+ fputs("ppdFindAttr(wildcard): ", stdout);
+ if ((attr = ppdFindAttr(ppd, "cupsTest", NULL)) == NULL)
+ {
+ status ++;
+ puts("FAIL (not found)");
+ }
+ else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo"))
+ {
+ status ++;
+ printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+ }
+ else
+ puts("PASS");
+
+ fputs("ppdFindNextAttr(wildcard): ", stdout);
+ if ((attr = ppdFindNextAttr(ppd, "cupsTest", NULL)) == NULL)
+ {
+ status ++;
+ puts("FAIL (not found)");
+ }
+ else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Bar"))
+ {
+ status ++;
+ printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+ }
+ else
+ puts("PASS");
+
+ fputs("ppdFindAttr(Foo): ", stdout);
+ if ((attr = ppdFindAttr(ppd, "cupsTest", "Foo")) == NULL)
+ {
+ status ++;
+ puts("FAIL (not found)");
+ }
+ else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo"))
+ {
+ status ++;
+ printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+ }
+ else
+ puts("PASS");
+
+ fputs("ppdFindNextAttr(Foo): ", stdout);
+ if ((attr = ppdFindNextAttr(ppd, "cupsTest", "Foo")) != NULL)
+ {
+ status ++;
+ printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+ }
+ else
+ puts("PASS");
+
+ fputs("ppdMarkDefaults: ", stdout);
+ ppdMarkDefaults(ppd);
+
+ if ((conflicts = ppdConflicts(ppd)) == 0)
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (%d conflicts)\n", conflicts);
+ }
+
+ fputs("ppdEmitString (defaults): ", stdout);
+ if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
+ !strcmp(s, default_code))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
+ (int)strlen(default_code));
+
+ if (s)
+ puts(s);
+ }
+
+ if (s)
+ free(s);
+
+ fputs("ppdEmitString (custom size and string): ", stdout);
+ ppdMarkOption(ppd, "PageSize", "Custom.400x500");
+ ppdMarkOption(ppd, "StringOption", "{String1=\"value 1\" String2=value(2)}");
+
+ if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
+ !strcmp(s, custom_code))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
+ (int)strlen(custom_code));
+
+ if (s)
+ puts(s);
+ }
+
+ if (s)
+ free(s);
+
+ /*
+ * Test constraints...
+ */
+
+ fputs("cupsGetConflicts(InputSlot=Envelope): ", stdout);
+ ppdMarkOption(ppd, "PageSize", "Letter");
+
+ num_options = cupsGetConflicts(ppd, "InputSlot", "Envelope", &options);
+ if (num_options != 2 ||
+ (val = cupsGetOption("PageRegion", num_options, options)) == NULL ||
+ _cups_strcasecmp(val, "Letter") ||
+ (val = cupsGetOption("PageSize", num_options, options)) == NULL ||
+ _cups_strcasecmp(val, "Letter"))
+ {
+ printf("FAIL (%d options:", num_options);
+ for (i = 0; i < num_options; i ++)
+ printf(" %s=%s", options[i].name, options[i].value);
+ puts(")");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("ppdConflicts(): ", stdout);
+ ppdMarkOption(ppd, "InputSlot", "Envelope");
+
+ if ((conflicts = ppdConflicts(ppd)) == 2)
+ puts("PASS (2)");
+ else
+ {
+ printf("FAIL (%d)\n", conflicts);
+ status ++;
+ }
+
+ fputs("cupsResolveConflicts(InputSlot=Envelope): ", stdout);
+ num_options = 0;
+ options = NULL;
+ if (!cupsResolveConflicts(ppd, "InputSlot", "Envelope", &num_options,
+ &options))
+ {
+ puts("FAIL (Unable to resolve)");
+ status ++;
+ }
+ else if (num_options != 2 ||
+ !cupsGetOption("PageSize", num_options, options))
+ {
+ printf("FAIL (%d options:", num_options);
+ for (i = 0; i < num_options; i ++)
+ printf(" %s=%s", options[i].name, options[i].value);
+ puts(")");
+ status ++;
+ }
+ else
+ puts("PASS (Resolved by changing PageSize)");
+
+ cupsFreeOptions(num_options, options);
+
+ fputs("cupsResolveConflicts(No option/choice): ", stdout);
+ num_options = 0;
+ options = NULL;
+ if (cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options) &&
+ num_options == 1 && !_cups_strcasecmp(options[0].name, "InputSlot") &&
+ !_cups_strcasecmp(options[0].value, "Tray"))
+ puts("PASS (Resolved by changing InputSlot)");
+ else if (num_options > 0)
+ {
+ printf("FAIL (%d options:", num_options);
+ for (i = 0; i < num_options; i ++)
+ printf(" %s=%s", options[i].name, options[i].value);
+ puts(")");
+ status ++;
+ }
+ else
+ {
+ puts("FAIL (Unable to resolve)");
+ status ++;
+ }
+ cupsFreeOptions(num_options, options);
+
+ fputs("ppdInstallableConflict(): ", stdout);
+ if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") &&
+ !ppdInstallableConflict(ppd, "Duplex", "None"))
+ puts("PASS");
+ else if (!ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble"))
+ {
+ puts("FAIL (Duplex=DuplexNoTumble did not conflict)");
+ status ++;
+ }
+ else
+ {
+ puts("FAIL (Duplex=None conflicted)");
+ status ++;
+ }
+
+ /*
+ * ppdPageSizeLimits
+ */
+
+ fputs("ppdPageSizeLimits: ", stdout);
+ if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+ {
+ if (minsize.width != 36 || minsize.length != 36 ||
+ maxsize.width != 1080 || maxsize.length != 86400)
+ {
+ printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+ "expected min=36x36, max=1080x86400)\n", minsize.width,
+ minsize.length, maxsize.width, maxsize.length);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ puts("FAIL (returned 0)");
+ status ++;
+ }
+
+ /*
+ * cupsMarkOptions with PWG and IPP size names.
+ */
+
+ fputs("cupsMarkOptions(media=iso-a4): ", stdout);
+ num_options = cupsAddOption("media", "iso-a4", 0, &options);
+ cupsMarkOptions(ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+
+ size = ppdPageSize(ppd, NULL);
+ if (!size || strcmp(size->name, "A4"))
+ {
+ printf("FAIL (%s)\n", size ? size->name : "unknown");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("cupsMarkOptions(media=na_letter_8.5x11in): ", stdout);
+ num_options = cupsAddOption("media", "na_letter_8.5x11in", 0, &options);
+ cupsMarkOptions(ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+
+ size = ppdPageSize(ppd, NULL);
+ if (!size || strcmp(size->name, "Letter"))
+ {
+ printf("FAIL (%s)\n", size ? size->name : "unknown");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("cupsMarkOptions(media=oe_letter-fullbleed_8.5x11in): ", stdout);
+ num_options = cupsAddOption("media", "oe_letter-fullbleed_8.5x11in", 0,
+ &options);
+ cupsMarkOptions(ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+
+ size = ppdPageSize(ppd, NULL);
+ if (!size || strcmp(size->name, "Letter.Fullbleed"))
+ {
+ printf("FAIL (%s)\n", size ? size->name : "unknown");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("cupsMarkOptions(media=A4): ", stdout);
+ num_options = cupsAddOption("media", "A4", 0, &options);
+ cupsMarkOptions(ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+
+ size = ppdPageSize(ppd, NULL);
+ if (!size || strcmp(size->name, "A4"))
+ {
+ printf("FAIL (%s)\n", size ? size->name : "unknown");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Custom sizes...
+ */
+
+ fputs("cupsMarkOptions(media=Custom.8x10in): ", stdout);
+ num_options = cupsAddOption("media", "Custom.8x10in", 0, &options);
+ cupsMarkOptions(ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+
+ size = ppdPageSize(ppd, NULL);
+ if (!size || strcmp(size->name, "Custom") ||
+ size->width != 576 || size->length != 720)
+ {
+ printf("FAIL (%s - %gx%g)\n", size ? size->name : "unknown",
+ size ? size->width : 0.0, size ? size->length : 0.0);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ /*
+ * Test localization...
+ */
+
+ fputs("ppdLocalizeIPPReason(text): ", stdout);
+ if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
+ !strcmp(buffer, "Foo Reason"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"Foo Reason\")\n", buffer);
+ }
+
+ fputs("ppdLocalizeIPPReason(http): ", stdout);
+ if (ppdLocalizeIPPReason(ppd, "foo", "http", buffer, sizeof(buffer)) &&
+ !strcmp(buffer, "http://foo/bar.html"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"http://foo/bar.html\")\n", buffer);
+ }
+
+ fputs("ppdLocalizeIPPReason(help): ", stdout);
+ if (ppdLocalizeIPPReason(ppd, "foo", "help", buffer, sizeof(buffer)) &&
+ !strcmp(buffer, "help:anchor='foo'%20bookID=Vendor%20Help"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"help:anchor='foo'%%20bookID=Vendor%%20Help\")\n", buffer);
+ }
+
+ fputs("ppdLocalizeIPPReason(file): ", stdout);
+ if (ppdLocalizeIPPReason(ppd, "foo", "file", buffer, sizeof(buffer)) &&
+ !strcmp(buffer, "/help/foo/bar.html"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"/help/foo/bar.html\")\n", buffer);
+ }
+
+ putenv("LANG=fr");
+ putenv("LC_ALL=fr");
+ putenv("LC_CTYPE=fr");
+ putenv("LC_MESSAGES=fr");
+
+ fputs("ppdLocalizeIPPReason(fr text): ", stdout);
+ if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
+ !strcmp(buffer, "La Long Foo Reason"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"La Long Foo Reason\")\n", buffer);
+ }
+
+ putenv("LANG=zh_TW");
+ putenv("LC_ALL=zh_TW");
+ putenv("LC_CTYPE=zh_TW");
+ putenv("LC_MESSAGES=zh_TW");
+
+ fputs("ppdLocalizeIPPReason(zh_TW text): ", stdout);
+ if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
+ !strcmp(buffer, "Number 1 Foo Reason"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"Number 1 Foo Reason\")\n", buffer);
+ }
+
+ /*
+ * cupsMarkerName localization...
+ */
+
+ putenv("LANG=en");
+ putenv("LC_ALL=en");
+ putenv("LC_CTYPE=en");
+ putenv("LC_MESSAGES=en");
+
+ fputs("ppdLocalizeMarkerName(bogus): ", stdout);
+
+ if ((text = ppdLocalizeMarkerName(ppd, "bogus")) != NULL)
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of NULL)\n", text);
+ }
+ else
+ puts("PASS");
+
+ fputs("ppdLocalizeMarkerName(cyan): ", stdout);
+
+ if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
+ !strcmp(text, "Cyan Toner"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"Cyan Toner\")\n",
+ text ? text : "(null)");
+ }
+
+ putenv("LANG=fr");
+ putenv("LC_ALL=fr");
+ putenv("LC_CTYPE=fr");
+ putenv("LC_MESSAGES=fr");
+
+ fputs("ppdLocalizeMarkerName(fr cyan): ", stdout);
+ if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
+ !strcmp(text, "La Toner Cyan"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"La Toner Cyan\")\n",
+ text ? text : "(null)");
+ }
+
+ putenv("LANG=zh_TW");
+ putenv("LC_ALL=zh_TW");
+ putenv("LC_CTYPE=zh_TW");
+ putenv("LC_MESSAGES=zh_TW");
+
+ fputs("ppdLocalizeMarkerName(zh_TW cyan): ", stdout);
+ if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
+ !strcmp(text, "Number 1 Cyan Toner"))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (\"%s\" instead of \"Number 1 Cyan Toner\")\n",
+ text ? text : "(null)");
+ }
+
+ ppdClose(ppd);
+
+ /*
+ * Test new constraints...
+ */
+
+ fputs("ppdOpenFile(test2.ppd): ", stdout);
+
+ if ((ppd = ppdOpenFile("test2.ppd")) != NULL)
+ puts("PASS");
+ else
+ {
+ ppd_status_t err; /* Last error in file */
+ int line; /* Line number in file */
+
+
+ status ++;
+ err = ppdLastError(&line);
+
+ printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
+ }
+
+ fputs("ppdMarkDefaults: ", stdout);
+ ppdMarkDefaults(ppd);
+
+ if ((conflicts = ppdConflicts(ppd)) == 0)
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (%d conflicts)\n", conflicts);
+ }
+
+ fputs("ppdEmitString (defaults): ", stdout);
+ if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
+ !strcmp(s, default2_code))
+ puts("PASS");
+ else
+ {
+ status ++;
+ printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
+ (int)strlen(default2_code));
+
+ if (s)
+ puts(s);
+ }
+
+ if (s)
+ free(s);
+
+ fputs("ppdConflicts(): ", stdout);
+ ppdMarkOption(ppd, "PageSize", "Env10");
+ ppdMarkOption(ppd, "InputSlot", "Envelope");
+ ppdMarkOption(ppd, "Quality", "Photo");
+
+ if ((conflicts = ppdConflicts(ppd)) == 1)
+ puts("PASS (1)");
+ else
+ {
+ printf("FAIL (%d)\n", conflicts);
+ status ++;
+ }
+
+ fputs("cupsResolveConflicts(Quality=Photo): ", stdout);
+ num_options = 0;
+ options = NULL;
+ if (cupsResolveConflicts(ppd, "Quality", "Photo", &num_options,
+ &options))
+ {
+ printf("FAIL (%d options:", num_options);
+ for (i = 0; i < num_options; i ++)
+ printf(" %s=%s", options[i].name, options[i].value);
+ puts(")");
+ status ++;
+ }
+ else
+ puts("PASS (Unable to resolve)");
+ cupsFreeOptions(num_options, options);
+
+ fputs("cupsResolveConflicts(No option/choice): ", stdout);
+ num_options = 0;
+ options = NULL;
+ if (cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options) &&
+ num_options == 1 && !_cups_strcasecmp(options->name, "Quality") &&
+ !_cups_strcasecmp(options->value, "Normal"))
+ puts("PASS");
+ else if (num_options > 0)
+ {
+ printf("FAIL (%d options:", num_options);
+ for (i = 0; i < num_options; i ++)
+ printf(" %s=%s", options[i].name, options[i].value);
+ puts(")");
+ status ++;
+ }
+ else
+ {
+ puts("FAIL (Unable to resolve!)");
+ status ++;
+ }
+ cupsFreeOptions(num_options, options);
+
+ fputs("cupsResolveConflicts(loop test): ", stdout);
+ ppdMarkOption(ppd, "PageSize", "A4");
+ ppdMarkOption(ppd, "InputSlot", "Tray");
+ ppdMarkOption(ppd, "Quality", "Photo");
+ num_options = 0;
+ options = NULL;
+ if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
+ puts("PASS");
+ else if (num_options > 0)
+ {
+ printf("FAIL (%d options:", num_options);
+ for (i = 0; i < num_options; i ++)
+ printf(" %s=%s", options[i].name, options[i].value);
+ puts(")");
+ }
+ else
+ puts("FAIL (No conflicts!)");
+
+ fputs("ppdInstallableConflict(): ", stdout);
+ if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") &&
+ !ppdInstallableConflict(ppd, "Duplex", "None"))
+ puts("PASS");
+ else if (!ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble"))
+ {
+ puts("FAIL (Duplex=DuplexNoTumble did not conflict)");
+ status ++;
+ }
+ else
+ {
+ puts("FAIL (Duplex=None conflicted)");
+ status ++;
+ }
+
+ /*
+ * ppdPageSizeLimits
+ */
+
+ ppdMarkDefaults(ppd);
+
+ fputs("ppdPageSizeLimits(default): ", stdout);
+ if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+ {
+ if (minsize.width != 36 || minsize.length != 36 ||
+ maxsize.width != 1080 || maxsize.length != 86400)
+ {
+ printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+ "expected min=36x36, max=1080x86400)\n", minsize.width,
+ minsize.length, maxsize.width, maxsize.length);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ puts("FAIL (returned 0)");
+ status ++;
+ }
+
+ ppdMarkOption(ppd, "InputSlot", "Manual");
+
+ fputs("ppdPageSizeLimits(InputSlot=Manual): ", stdout);
+ if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+ {
+ if (minsize.width != 100 || minsize.length != 100 ||
+ maxsize.width != 1000 || maxsize.length != 1000)
+ {
+ printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+ "expected min=100x100, max=1000x1000)\n", minsize.width,
+ minsize.length, maxsize.width, maxsize.length);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ puts("FAIL (returned 0)");
+ status ++;
+ }
+
+ ppdMarkOption(ppd, "Quality", "Photo");
+
+ fputs("ppdPageSizeLimits(Quality=Photo): ", stdout);
+ if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+ {
+ if (minsize.width != 200 || minsize.length != 200 ||
+ maxsize.width != 1000 || maxsize.length != 1000)
+ {
+ printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+ "expected min=200x200, max=1000x1000)\n", minsize.width,
+ minsize.length, maxsize.width, maxsize.length);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ puts("FAIL (returned 0)");
+ status ++;
+ }
+
+ ppdMarkOption(ppd, "InputSlot", "Tray");
+
+ fputs("ppdPageSizeLimits(Quality=Photo): ", stdout);
+ if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+ {
+ if (minsize.width != 300 || minsize.length != 300 ||
+ maxsize.width != 1080 || maxsize.length != 86400)
+ {
+ printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+ "expected min=300x300, max=1080x86400)\n", minsize.width,
+ minsize.length, maxsize.width, maxsize.length);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ puts("FAIL (returned 0)");
+ status ++;
+ }
+ }
+ else
+ {
+ const char *filename; /* PPD filename */
+ struct stat fileinfo; /* File information */
+
+
+ if (!strncmp(argv[1], "-d", 2))
+ {
+ const char *printer; /* Printer name */
+
+ if (argv[1][2])
+ printer = argv[1] + 2;
+ else if (argv[2])
+ printer = argv[2];
+ else
+ {
+ puts("Usage: ./testppd -d printer");
+ return (1);
+ }
+
+ filename = cupsGetPPD(printer);
+
+ if (!filename)
+ {
+ printf("%s: %s\n", printer, cupsLastErrorString());
+ return (1);
+ }
+ }
+ else
+ filename = argv[1];
+
+ if (lstat(filename, &fileinfo))
+ {
+ printf("%s: %s\n", filename, strerror(errno));
+ return (1);
+ }
+
+ if (S_ISLNK(fileinfo.st_mode))
+ {
+ char realfile[1024]; /* Real file path */
+ ssize_t realsize; /* Size of real file path */
+
+
+ if ((realsize = readlink(filename, realfile, sizeof(realfile) - 1)) < 0)
+ strlcpy(realfile, "Unknown", sizeof(realfile));
+ else
+ realfile[realsize] = '\0';
+
+ if (stat(realfile, &fileinfo))
+ printf("%s: symlink to \"%s\", %s\n", filename, realfile,
+ strerror(errno));
+ else
+ printf("%s: symlink to \"%s\", %ld bytes\n", filename, realfile,
+ (long)fileinfo.st_size);
+ }
+ else
+ printf("%s: regular file, %ld bytes\n", filename, (long)fileinfo.st_size);
+
+ if ((ppd = ppdOpenFile(filename)) == NULL)
+ {
+ ppd_status_t err; /* Last error in file */
+ int line; /* Line number in file */
+
+
+ status ++;
+ err = ppdLastError(&line);
+
+ printf("%s: %s on line %d\n", argv[1], ppdErrorString(err), line);
+ }
+ else
+ {
+ int j, k; /* Looping vars */
+ ppd_group_t *group; /* Option group */
+ ppd_option_t *option; /* Option */
+ ppd_coption_t *coption; /* Custom option */
+ ppd_cparam_t *cparam; /* Custom parameter */
+ ppd_const_t *c; /* UIConstraints */
+ char lang[255], /* LANG environment variable */
+ lc_all[255], /* LC_ALL environment variable */
+ lc_ctype[255], /* LC_CTYPE environment variable */
+ lc_messages[255];/* LC_MESSAGES environment variable */
+
+
+ if (argc > 2)
+ {
+ snprintf(lang, sizeof(lang), "LANG=%s", argv[2]);
+ putenv(lang);
+ snprintf(lc_all, sizeof(lc_all), "LC_ALL=%s", argv[2]);
+ putenv(lc_all);
+ snprintf(lc_ctype, sizeof(lc_ctype), "LC_CTYPE=%s", argv[2]);
+ putenv(lc_ctype);
+ snprintf(lc_messages, sizeof(lc_messages), "LC_MESSAGES=%s", argv[2]);
+ putenv(lc_messages);
+ }
+
+ ppdLocalize(ppd);
+ ppdMarkDefaults(ppd);
+
+ if (argc > 3)
+ {
+ text = ppdLocalizeIPPReason(ppd, argv[3], NULL, buffer, sizeof(buffer));
+ printf("ppdLocalizeIPPReason(%s)=%s\n", argv[3],
+ text ? text : "(null)");
+ return (text == NULL);
+ }
+
+ for (i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ {
+ printf("%s (%s):\n", group->name, group->text);
+
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ printf(" %s (%s):\n", option->keyword, option->text);
+
+ for (k = 0; k < option->num_choices; k ++)
+ printf(" - %s%s (%s)\n",
+ option->choices[k].marked ? "*" : "",
+ option->choices[k].choice, option->choices[k].text);
+
+ if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
+ {
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ printf(" %s(%s): PPD_CUSTOM_CURVE (%g to %g)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_curve,
+ cparam->maximum.custom_curve);
+ break;
+
+ case PPD_CUSTOM_INT :
+ printf(" %s(%s): PPD_CUSTOM_INT (%d to %d)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_int,
+ cparam->maximum.custom_int);
+ break;
+
+ case PPD_CUSTOM_INVCURVE :
+ printf(" %s(%s): PPD_CUSTOM_INVCURVE (%g to %g)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_invcurve,
+ cparam->maximum.custom_invcurve);
+ break;
+
+ case PPD_CUSTOM_PASSCODE :
+ printf(" %s(%s): PPD_CUSTOM_PASSCODE (%d to %d)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_passcode,
+ cparam->maximum.custom_passcode);
+ break;
+
+ case PPD_CUSTOM_PASSWORD :
+ printf(" %s(%s): PPD_CUSTOM_PASSWORD (%d to %d)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_password,
+ cparam->maximum.custom_password);
+ break;
+
+ case PPD_CUSTOM_POINTS :
+ printf(" %s(%s): PPD_CUSTOM_POINTS (%g to %g)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_points,
+ cparam->maximum.custom_points);
+ break;
+
+ case PPD_CUSTOM_REAL :
+ printf(" %s(%s): PPD_CUSTOM_REAL (%g to %g)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_real,
+ cparam->maximum.custom_real);
+ break;
+
+ case PPD_CUSTOM_STRING :
+ printf(" %s(%s): PPD_CUSTOM_STRING (%d to %d)\n",
+ cparam->name, cparam->text,
+ cparam->minimum.custom_string,
+ cparam->maximum.custom_string);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ puts("\nSizes:");
+ for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+ printf(" %s = %gx%g, [%g %g %g %g]\n", size->name, size->width,
+ size->length, size->left, size->bottom, size->right, size->top);
+
+ puts("\nConstraints:");
+
+ for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
+ printf(" *UIConstraints: *%s %s *%s %s\n", c->option1, c->choice1,
+ c->option2, c->choice2);
+ if (ppd->num_consts == 0)
+ puts(" NO CONSTRAINTS");
+
+ puts("\nFilters:");
+
+ for (i = 0; i < ppd->num_filters; i ++)
+ printf(" %s\n", ppd->filters[i]);
+
+ if (ppd->num_filters == 0)
+ puts(" NO FILTERS");
+
+ puts("\nAttributes:");
+
+ for (attr = (ppd_attr_t *)cupsArrayFirst(ppd->sorted_attrs);
+ attr;
+ attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs))
+ printf(" *%s %s/%s: \"%s\"\n", attr->name, attr->spec,
+ attr->text, attr->value ? attr->value : "");
+
+ puts("\nPPD Cache:");
+ if ((pc = _ppdCacheCreateWithPPD(ppd)) == NULL)
+ printf(" Unable to create: %s\n", cupsLastErrorString());
+ else
+ {
+ _ppdCacheWriteFile(pc, "t.cache", NULL);
+ puts(" Wrote t.cache.");
+ }
+ }
+
+ if (!strncmp(argv[1], "-d", 2))
+ unlink(filename);
+ }
+
+#ifdef __APPLE__
+ if (getenv("MallocStackLogging") && getenv("MallocStackLoggingNoCompact"))
+ {
+ char command[1024]; /* malloc_history command */
+
+ snprintf(command, sizeof(command), "malloc_history %d -all_by_size",
+ getpid());
+ fflush(stdout);
+ system(command);
+ }
+#endif /* __APPLE__ */
+
+ ppdClose(ppd);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: testppd.c 11060 2013-06-25 15:02:18Z msweet $".
+ */
diff --git a/cups/libs/cups/testpwg.c b/cups/libs/cups/testpwg.c
new file mode 100644
index 000000000..c054accb6
--- /dev/null
+++ b/cups/libs/cups/testpwg.c
@@ -0,0 +1,570 @@
+/*
+ * "$Id: testpwg.c 11240 2013-08-14 20:33:55Z msweet $"
+ *
+ * PWG test program for CUPS.
+ *
+ * Copyright 2009-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * test_pagesize() - Test the PWG mapping functions.
+ * test_ppd_cache() - Test the PPD cache functions.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ppd-private.h"
+#include "file-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int test_pagesize(_ppd_cache_t *pc, ppd_file_t *ppd,
+ const char *ppdsize);
+static int test_ppd_cache(_ppd_cache_t *pc, ppd_file_t *ppd);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int status; /* Status of tests (0 = success, 1 = fail) */
+ const char *ppdfile; /* PPD filename */
+ ppd_file_t *ppd; /* PPD file */
+ _ppd_cache_t *pc; /* PPD cache and PWG mapping data */
+ const pwg_media_t *pwgmedia; /* PWG media size */
+ size_t i, /* Looping var */
+ num_media; /* Number of media sizes */
+ const pwg_media_t *mediatable; /* Media size table */
+ int dupmedia = 0; /* Duplicate media sizes? */
+
+
+ status = 0;
+
+ if (argc < 2 || argc > 3)
+ {
+ puts("Usage: ./testpwg filename.ppd [jobfile]");
+ return (1);
+ }
+
+ ppdfile = argv[1];
+
+ printf("ppdOpenFile(%s): ", ppdfile);
+ if ((ppd = ppdOpenFile(ppdfile)) == NULL)
+ {
+ ppd_status_t err; /* Last error in file */
+ int line; /* Line number in file */
+
+
+ err = ppdLastError(&line);
+
+ printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
+
+ return (1);
+ }
+ else
+ puts("PASS");
+
+ fputs("_ppdCacheCreateWithPPD(ppd): ", stdout);
+ if ((pc = _ppdCacheCreateWithPPD(ppd)) == NULL)
+ {
+ puts("FAIL");
+ status ++;
+ }
+ else
+ {
+ puts("PASS");
+ status += test_ppd_cache(pc, ppd);
+
+ if (argc == 3)
+ {
+ /*
+ * Test PageSize mapping code.
+ */
+
+ int fd; /* Job file descriptor */
+ const char *pagesize; /* PageSize value */
+ ipp_t *job; /* Job attributes */
+ ipp_attribute_t *media; /* Media attribute */
+
+ if ((fd = open(argv[2], O_RDONLY)) >= 0)
+ {
+ job = ippNew();
+ ippReadFile(fd, job);
+ close(fd);
+
+ if ((media = ippFindAttribute(job, "media", IPP_TAG_ZERO)) != NULL &&
+ media->value_tag != IPP_TAG_NAME &&
+ media->value_tag != IPP_TAG_KEYWORD)
+ media = NULL;
+
+ if (media)
+ printf("_ppdCacheGetPageSize(media=%s): ",
+ media->values[0].string.text);
+ else
+ fputs("_ppdCacheGetPageSize(media-col): ", stdout);
+
+ fflush(stdout);
+
+ if ((pagesize = _ppdCacheGetPageSize(pc, job, NULL, NULL)) == NULL)
+ {
+ puts("FAIL (Not Found)");
+ status = 1;
+ }
+ else if (media && _cups_strcasecmp(pagesize, media->values[0].string.text))
+ {
+ printf("FAIL (Got \"%s\", Expected \"%s\")\n", pagesize,
+ media->values[0].string.text);
+ status = 1;
+ }
+ else
+ printf("PASS (%s)\n", pagesize);
+
+ ippDelete(job);
+ }
+ else
+ {
+ perror(argv[2]);
+ status = 1;
+ }
+ }
+
+ /*
+ * _ppdCacheDestroy should never fail...
+ */
+
+ fputs("_ppdCacheDestroy(pc): ", stdout);
+ _ppdCacheDestroy(pc);
+ puts("PASS");
+ }
+
+ fputs("pwgMediaForPWG(\"iso_a4_210x297mm\"): ", stdout);
+ if ((pwgmedia = pwgMediaForPWG("iso_a4_210x297mm")) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "iso_a4_210x297mm"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else if (pwgmedia->width != 21000 || pwgmedia->length != 29700)
+ {
+ printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("pwgMediaForPWG(\"roll_max_36.1025x3622.0472in\"): ", stdout);
+ if ((pwgmedia = pwgMediaForPWG("roll_max_36.1025x3622.0472in")) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (pwgmedia->width != 91700 || pwgmedia->length != 9199999)
+ {
+ printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ status ++;
+ }
+ else
+ printf("PASS (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+
+ fputs("pwgMediaForLegacy(\"na-letter\"): ", stdout);
+ if ((pwgmedia = pwgMediaForLegacy("na-letter")) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "na_letter_8.5x11in"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else if (pwgmedia->width != 21590 || pwgmedia->length != 27940)
+ {
+ printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("pwgMediaForPPD(\"4x6\"): ", stdout);
+ if ((pwgmedia = pwgMediaForPPD("4x6")) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "na_index-4x6_4x6in"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else if (pwgmedia->width != 10160 || pwgmedia->length != 15240)
+ {
+ printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("pwgMediaForPPD(\"10x15cm\"): ", stdout);
+ if ((pwgmedia = pwgMediaForPPD("10x15cm")) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "om_100x150mm_100x150mm"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else if (pwgmedia->width != 10000 || pwgmedia->length != 15000)
+ {
+ printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("pwgMediaForPPD(\"Custom.10x15cm\"): ", stdout);
+ if ((pwgmedia = pwgMediaForPPD("Custom.10x15cm")) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "custom_10x15cm_100x150mm"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else if (pwgmedia->width != 10000 || pwgmedia->length != 15000)
+ {
+ printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("pwgMediaForSize(29700, 42000): ", stdout);
+ if ((pwgmedia = pwgMediaForSize(29700, 42000)) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "iso_a3_297x420mm"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("pwgMediaForSize(9842, 19050): ", stdout);
+ if ((pwgmedia = pwgMediaForSize(9842, 19050)) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "na_monarch_3.875x7.5in"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else
+ printf("PASS (%s)\n", pwgmedia->pwg);
+
+ fputs("pwgMediaForSize(9800, 19000): ", stdout);
+ if ((pwgmedia = pwgMediaForSize(9800, 19000)) == NULL)
+ {
+ puts("FAIL (not found)");
+ status ++;
+ }
+ else if (strcmp(pwgmedia->pwg, "jpn_you6_98x190mm"))
+ {
+ printf("FAIL (%s)\n", pwgmedia->pwg);
+ status ++;
+ }
+ else
+ printf("PASS (%s)\n", pwgmedia->pwg);
+
+ fputs("Duplicate size test: ", stdout);
+ for (mediatable = _pwgMediaTable(&num_media);
+ num_media > 1;
+ num_media --, mediatable ++)
+ {
+ for (i = num_media - 1, pwgmedia = mediatable + 1; i > 0; i --, pwgmedia ++)
+ {
+ if (pwgmedia->width == mediatable->width &&
+ pwgmedia->length == mediatable->length)
+ {
+ if (!dupmedia)
+ {
+ dupmedia = 1;
+ status ++;
+ puts("FAIL");
+ }
+
+ printf(" %s and %s have the same dimensions (%dx%d)\n",
+ pwgmedia->pwg, mediatable->pwg, pwgmedia->width,
+ pwgmedia->length);
+ }
+ }
+ }
+ if (!dupmedia)
+ puts("PASS");
+
+
+ return (status);
+}
+
+
+/*
+ * 'test_pagesize()' - Test the PWG mapping functions.
+ */
+
+static int /* O - 1 on failure, 0 on success */
+test_pagesize(_ppd_cache_t *pc, /* I - PWG mapping data */
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *ppdsize) /* I - PPD page size */
+{
+ int status = 0; /* Return status */
+ ipp_t *job; /* Job attributes */
+ const char *pagesize; /* PageSize value */
+
+
+ if (ppdPageSize(ppd, ppdsize))
+ {
+ printf("_ppdCacheGetPageSize(keyword=%s): ", ppdsize);
+ fflush(stdout);
+
+ if ((pagesize = _ppdCacheGetPageSize(pc, NULL, ppdsize, NULL)) == NULL)
+ {
+ puts("FAIL (Not Found)");
+ status = 1;
+ }
+ else if (_cups_strcasecmp(pagesize, ppdsize))
+ {
+ printf("FAIL (Got \"%s\", Expected \"%s\")\n", pagesize, ppdsize);
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ job = ippNew();
+ ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, ppdsize);
+
+ printf("_ppdCacheGetPageSize(media=%s): ", ppdsize);
+ fflush(stdout);
+
+ if ((pagesize = _ppdCacheGetPageSize(pc, job, NULL, NULL)) == NULL)
+ {
+ puts("FAIL (Not Found)");
+ status = 1;
+ }
+ else if (_cups_strcasecmp(pagesize, ppdsize))
+ {
+ printf("FAIL (Got \"%s\", Expected \"%s\")\n", pagesize, ppdsize);
+ status = 1;
+ }
+ else
+ puts("PASS");
+
+ ippDelete(job);
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'test_ppd_cache()' - Test the PPD cache functions.
+ */
+
+static int /* O - 1 on failure, 0 on success */
+test_ppd_cache(_ppd_cache_t *pc, /* I - PWG mapping data */
+ ppd_file_t *ppd) /* I - PPD file */
+{
+ int i, /* Looping var */
+ status = 0; /* Return status */
+ _ppd_cache_t *pc2; /* Loaded data */
+ pwg_size_t *size, /* Size from original */
+ *size2; /* Size from saved */
+ pwg_map_t *map, /* Map from original */
+ *map2; /* Map from saved */
+
+
+ /*
+ * Verify that we can write and read back the same data...
+ */
+
+ fputs("_ppdCacheWriteFile(test.pwg): ", stdout);
+ if (!_ppdCacheWriteFile(pc, "test.pwg", NULL))
+ {
+ puts("FAIL");
+ status ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("_ppdCacheCreateWithFile(test.pwg): ", stdout);
+ if ((pc2 = _ppdCacheCreateWithFile("test.pwg", NULL)) == NULL)
+ {
+ puts("FAIL");
+ status ++;
+ }
+ else
+ {
+ // TODO: FINISH ADDING ALL VALUES IN STRUCTURE
+ if (pc2->num_sizes != pc->num_sizes)
+ {
+ if (!status)
+ puts("FAIL");
+
+ printf(" SAVED num_sizes=%d, ORIG num_sizes=%d\n", pc2->num_sizes,
+ pc->num_sizes);
+
+ status ++;
+ }
+ else
+ {
+ for (i = pc->num_sizes, size = pc->sizes, size2 = pc2->sizes;
+ i > 0;
+ i --, size ++, size2 ++)
+ {
+ if (strcmp(size2->map.pwg, size->map.pwg) ||
+ strcmp(size2->map.ppd, size->map.ppd) ||
+ size2->width != size->width ||
+ size2->length != size->length ||
+ size2->left != size->left ||
+ size2->bottom != size->bottom ||
+ size2->right != size->right ||
+ size2->top != size->top)
+ {
+ if (!status)
+ puts("FAIL");
+
+ if (strcmp(size->map.pwg, size2->map.pwg))
+ printf(" SAVED size->map.pwg=\"%s\", ORIG "
+ "size->map.pwg=\"%s\"\n", size2->map.pwg, size->map.pwg);
+
+ if (strcmp(size2->map.ppd, size->map.ppd))
+ printf(" SAVED size->map.ppd=\"%s\", ORIG "
+ "size->map.ppd=\"%s\"\n", size2->map.ppd, size->map.ppd);
+
+ if (size2->width != size->width)
+ printf(" SAVED size->width=%d, ORIG size->width=%d\n",
+ size2->width, size->width);
+
+ if (size2->length != size->length)
+ printf(" SAVED size->length=%d, ORIG size->length=%d\n",
+ size2->length, size->length);
+
+ if (size2->left != size->left)
+ printf(" SAVED size->left=%d, ORIG size->left=%d\n",
+ size2->left, size->left);
+
+ if (size2->bottom != size->bottom)
+ printf(" SAVED size->bottom=%d, ORIG size->bottom=%d\n",
+ size2->bottom, size->bottom);
+
+ if (size2->right != size->right)
+ printf(" SAVED size->right=%d, ORIG size->right=%d\n",
+ size2->right, size->right);
+
+ if (size2->top != size->top)
+ printf(" SAVED size->top=%d, ORIG size->top=%d\n",
+ size2->top, size->top);
+
+ status ++;
+ break;
+ }
+ }
+
+ for (i = pc->num_sources, map = pc->sources, map2 = pc2->sources;
+ i > 0;
+ i --, map ++, map2 ++)
+ {
+ if (strcmp(map2->pwg, map->pwg) ||
+ strcmp(map2->ppd, map->ppd))
+ {
+ if (!status)
+ puts("FAIL");
+
+ if (strcmp(map->pwg, map2->pwg))
+ printf(" SAVED source->pwg=\"%s\", ORIG source->pwg=\"%s\"\n",
+ map2->pwg, map->pwg);
+
+ if (strcmp(map2->ppd, map->ppd))
+ printf(" SAVED source->ppd=\"%s\", ORIG source->ppd=\"%s\"\n",
+ map2->ppd, map->ppd);
+
+ status ++;
+ break;
+ }
+ }
+
+ for (i = pc->num_types, map = pc->types, map2 = pc2->types;
+ i > 0;
+ i --, map ++, map2 ++)
+ {
+ if (strcmp(map2->pwg, map->pwg) ||
+ strcmp(map2->ppd, map->ppd))
+ {
+ if (!status)
+ puts("FAIL");
+
+ if (strcmp(map->pwg, map2->pwg))
+ printf(" SAVED type->pwg=\"%s\", ORIG type->pwg=\"%s\"\n",
+ map2->pwg, map->pwg);
+
+ if (strcmp(map2->ppd, map->ppd))
+ printf(" SAVED type->ppd=\"%s\", ORIG type->ppd=\"%s\"\n",
+ map2->ppd, map->ppd);
+
+ status ++;
+ break;
+ }
+ }
+ }
+
+ if (!status)
+ puts("PASS");
+
+ _ppdCacheDestroy(pc2);
+ }
+
+ /*
+ * Test PageSize mapping code...
+ */
+
+ status += test_pagesize(pc, ppd, "Letter");
+ status += test_pagesize(pc, ppd, "na-letter");
+ status += test_pagesize(pc, ppd, "A4");
+ status += test_pagesize(pc, ppd, "iso-a4");
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: testpwg.c 11240 2013-08-14 20:33:55Z msweet $".
+ */
diff --git a/cups/libs/cups/testsnmp.c b/cups/libs/cups/testsnmp.c
new file mode 100644
index 000000000..b60c2b795
--- /dev/null
+++ b/cups/libs/cups/testsnmp.c
@@ -0,0 +1,304 @@
+/*
+ * "$Id: testsnmp.c 3411 2011-09-07 22:31:27Z msweet $"
+ *
+ * SNMP test program for CUPS.
+ *
+ * Copyright 2008-2010 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * scan_oid() - Scan an OID value.
+ * show_oid() - Show the specified OID.
+ * usage() - Show program usage and exit.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "snmp-private.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void print_packet(cups_snmp_t *packet, void *data);
+static int show_oid(int fd, const char *community,
+ http_addr_t *addr, const char *s, int walk);
+static void usage(void) __attribute__((noreturn));
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int fd = -1; /* SNMP socket */
+ http_addrlist_t *host = NULL; /* Address of host */
+ int walk = 0; /* Walk OIDs? */
+ char *oid = NULL; /* Last OID shown */
+ const char *community; /* Community name */
+
+
+ fputs("_cupsSNMPDefaultCommunity: ", stdout);
+
+ if ((community = _cupsSNMPDefaultCommunity()) == NULL)
+ {
+ puts("FAIL (NULL community name)");
+ return (1);
+ }
+
+ printf("PASS (%s)\n", community);
+
+ /*
+ * Query OIDs from the command-line...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (!strcmp(argv[i], "-c"))
+ {
+ i ++;
+
+ if (i >= argc)
+ usage();
+ else
+ community = argv[i];
+ }
+ else if (!strcmp(argv[i], "-d"))
+ _cupsSNMPSetDebug(10);
+ else if (!strcmp(argv[i], "-w"))
+ walk = 1;
+ else if (!host)
+ {
+ if ((host = httpAddrGetList(argv[i], AF_UNSPEC, "161")) == NULL)
+ {
+ printf("testsnmp: Unable to find \"%s\"!\n", argv[1]);
+ return (1);
+ }
+
+ if (fd < 0)
+ {
+ fputs("_cupsSNMPOpen: ", stdout);
+
+ if ((fd = _cupsSNMPOpen(host->addr.addr.sa_family)) < 0)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ return (1);
+ }
+
+ puts("PASS");
+ }
+ }
+ else if (!show_oid(fd, community, &(host->addr), argv[i], walk))
+ return (1);
+ else
+ oid = argv[i];
+
+ if (!host)
+ usage();
+
+ if (!oid)
+ {
+ if (!show_oid(fd, community, &(host->addr),
+ walk ? ".1.3.6.1.2.1.43" :
+ ".1.3.6.1.2.1.43.10.2.1.4.1.1", walk))
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'print_packet()' - Print the contents of the response packet.
+ */
+
+static void
+print_packet(cups_snmp_t *packet, /* I - SNMP response packet */
+ void *data) /* I - User data pointer (not used) */
+{
+ int i; /* Looping var */
+ char temp[1024]; /* Temporary OID string */
+
+
+ (void)data;
+
+ printf("%s = ", _cupsSNMPOIDToString(packet->object_name, temp, sizeof(temp)));
+
+ switch (packet->object_type)
+ {
+ case CUPS_ASN1_BOOLEAN :
+ printf("BOOLEAN %s\n",
+ packet->object_value.boolean ? "TRUE" : "FALSE");
+ break;
+
+ case CUPS_ASN1_INTEGER :
+ printf("INTEGER %d\n", packet->object_value.integer);
+ break;
+
+ case CUPS_ASN1_BIT_STRING :
+ printf("BIT-STRING \"%s\"\n",
+ (char *)packet->object_value.string.bytes);
+ break;
+
+ case CUPS_ASN1_OCTET_STRING :
+ printf("OCTET-STRING \"%s\"\n",
+ (char *)packet->object_value.string.bytes);
+ break;
+
+ case CUPS_ASN1_NULL_VALUE :
+ puts("NULL-VALUE");
+ break;
+
+ case CUPS_ASN1_OID :
+ printf("OID %s\n", _cupsSNMPOIDToString(packet->object_value.oid,
+ temp, sizeof(temp)));
+ break;
+
+ case CUPS_ASN1_HEX_STRING :
+ fputs("Hex-STRING", stdout);
+ for (i = 0; i < packet->object_value.string.num_bytes; i ++)
+ printf(" %02X", packet->object_value.string.bytes[i]);
+ putchar('\n');
+ break;
+
+ case CUPS_ASN1_COUNTER :
+ printf("Counter %d\n", packet->object_value.counter);
+ break;
+
+ case CUPS_ASN1_GAUGE :
+ printf("Gauge %u\n", packet->object_value.gauge);
+ break;
+
+ case CUPS_ASN1_TIMETICKS :
+ printf("Timeticks %u days, %u:%02u:%02u.%02u\n",
+ packet->object_value.timeticks / 8640000,
+ (packet->object_value.timeticks / 360000) % 24,
+ (packet->object_value.timeticks / 6000) % 60,
+ (packet->object_value.timeticks / 100) % 60,
+ packet->object_value.timeticks % 100);
+ break;
+
+ default :
+ printf("Unknown-%X\n", packet->object_type);
+ break;
+ }
+}
+
+
+/*
+ * 'show_oid()' - Show the specified OID.
+ */
+
+static int /* O - 1 on success, 0 on error */
+show_oid(int fd, /* I - SNMP socket */
+ const char *community, /* I - Community name */
+ http_addr_t *addr, /* I - Address to query */
+ const char *s, /* I - OID to query */
+ int walk) /* I - Walk OIDs? */
+{
+ int i; /* Looping var */
+ int oid[CUPS_SNMP_MAX_OID]; /* OID */
+ cups_snmp_t packet; /* SNMP packet */
+ char temp[1024]; /* Temporary OID string */
+
+
+ if (!_cupsSNMPStringToOID(s, oid, sizeof(oid) / sizeof(oid[0])))
+ {
+ puts("testsnmp: Bad OID");
+ return (0);
+ }
+
+ if (walk)
+ {
+ printf("_cupsSNMPWalk(%s): ", _cupsSNMPOIDToString(oid, temp, sizeof(temp)));
+
+ if (_cupsSNMPWalk(fd, addr, CUPS_SNMP_VERSION_1, community, oid, 5.0,
+ print_packet, NULL) < 0)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ return (0);
+ }
+ }
+ else
+ {
+ printf("_cupsSNMPWrite(%s): ", _cupsSNMPOIDToString(oid, temp, sizeof(temp)));
+
+ if (!_cupsSNMPWrite(fd, addr, CUPS_SNMP_VERSION_1, community,
+ CUPS_ASN1_GET_REQUEST, 1, oid))
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ return (0);
+ }
+
+ puts("PASS");
+
+ fputs("_cupsSNMPRead(5.0): ", stdout);
+
+ if (!_cupsSNMPRead(fd, &packet, 5.0))
+ {
+ puts("FAIL (timeout)");
+ return (0);
+ }
+
+ if (!_cupsSNMPIsOID(&packet, oid))
+ {
+ printf("FAIL (bad OID %d", packet.object_name[0]);
+ for (i = 1; packet.object_name[i] >= 0; i ++)
+ printf(".%d", packet.object_name[i]);
+ puts(")");
+ return (0);
+ }
+
+ if (packet.error)
+ {
+ printf("FAIL (%s)\n", packet.error);
+ return (0);
+ }
+
+ puts("PASS");
+
+ print_packet(&packet, NULL);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'usage()' - Show program usage and exit.
+ */
+
+static void
+usage(void)
+{
+ puts("Usage: testsnmp [options] host-or-ip [oid ...]");
+ puts("");
+ puts("Options:");
+ puts("");
+ puts(" -c community Set community name");
+ puts(" -d Enable debugging");
+ puts(" -w Walk all OIDs under the specified one");
+
+ exit (1);
+}
+
+
+/*
+ * End of "$Id: testsnmp.c 3411 2011-09-07 22:31:27Z msweet $".
+ */
diff --git a/cups/libs/cups/thread-private.h b/cups/libs/cups/thread-private.h
new file mode 100644
index 000000000..ae1b86a6b
--- /dev/null
+++ b/cups/libs/cups/thread-private.h
@@ -0,0 +1,100 @@
+/*
+ * "$Id: thread-private.h 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * Private threading definitions for CUPS.
+ *
+ * Copyright 2009-2012 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_THREAD_PRIVATE_H_
+# define _CUPS_THREAD_PRIVATE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "config.h"
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+# ifdef HAVE_PTHREAD_H
+# include <pthread.h>
+typedef void *(*_cups_thread_func_t)(void *arg);
+typedef pthread_mutex_t _cups_mutex_t;
+typedef pthread_rwlock_t _cups_rwlock_t;
+typedef pthread_key_t _cups_threadkey_t;
+# define _CUPS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+# define _CUPS_RWLOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER
+# define _CUPS_THREADKEY_INITIALIZER -1
+# define _cupsThreadGetData(k) pthread_getspecific(k)
+# define _cupsThreadSetData(k,p) pthread_setspecific(k,p)
+
+# elif defined(WIN32)
+# include <winsock2.h>
+# include <windows.h>
+typedef void *(__stdcall *_cups_thread_func_t)(void *arg);
+typedef struct _cups_mutex_s
+{
+ int m_init; /* Flag for on-demand initialization */
+ CRITICAL_SECTION m_criticalSection;
+ /* Win32 Critical Section */
+} _cups_mutex_t;
+typedef _cups_mutex_t _cups_rwlock_t; /* TODO: Implement Win32 reader/writer lock */
+typedef DWORD _cups_threadkey_t;
+# define _CUPS_MUTEX_INITIALIZER { 0, 0 }
+# define _CUPS_RWLOCK_INITIALIZER { 0, 0 }
+# define _CUPS_THREADKEY_INITIALIZER 0
+# define _cupsThreadGetData(k) TlsGetValue(k)
+# define _cupsThreadSetData(k,p) TlsSetValue(k,p)
+
+# else
+typedef void *(*_cups_thread_func_t)(void *arg);
+typedef char _cups_mutex_t;
+typedef char _cups_rwlock_t;
+typedef void *_cups_threadkey_t;
+# define _CUPS_MUTEX_INITIALIZER 0
+# define _CUPS_RWLOCK_INITIALIZER 0
+# define _CUPS_THREADKEY_INITIALIZER (void *)0
+# define _cupsThreadGetData(k) k
+# define _cupsThreadSetData(k,p) k=p
+# endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * Functions...
+ */
+
+extern void _cupsMutexInit(_cups_mutex_t *mutex);
+extern void _cupsMutexLock(_cups_mutex_t *mutex);
+extern void _cupsMutexUnlock(_cups_mutex_t *mutex);
+extern void _cupsRWInit(_cups_rwlock_t *rwlock);
+extern void _cupsRWLockRead(_cups_rwlock_t *rwlock);
+extern void _cupsRWLockWrite(_cups_rwlock_t *rwlock);
+extern void _cupsRWUnlock(_cups_rwlock_t *rwlock);
+extern int _cupsThreadCreate(_cups_thread_func_t func, void *arg);
+
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_THREAD_PRIVATE_H_ */
+
+/*
+ * End of "$Id: thread-private.h 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/thread.c b/cups/libs/cups/thread.c
new file mode 100644
index 000000000..8c4525221
--- /dev/null
+++ b/cups/libs/cups/thread.c
@@ -0,0 +1,338 @@
+/*
+ * "$Id: thread.c 11642 2014-02-27 15:57:59Z msweet $"
+ *
+ * Threading primitives for CUPS.
+ *
+ * Copyright 2009-2012 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsMutexInit() - Initialize a mutex.
+ * _cupsMutexLock() - Lock a mutex.
+ * _cupsMutexUnlock() - Unlock a mutex.
+ * _cupsRWInit() - Initialize a reader/writer lock.
+ * _cupsRWLockRead() - Acquire a reader/writer lock for reading.
+ * _cupsRWLockWrite() - Acquire a reader/writer lock for writing.
+ * _cupsRWUnlock() - Release a reader/writer lock.
+ * _cupsThreadCreate() - Create a thread.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include "thread-private.h"
+
+
+#if defined(HAVE_PTHREAD_H)
+/*
+ * '_cupsMutexInit()' - Initialize a mutex.
+ */
+
+void
+_cupsMutexInit(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ pthread_mutex_init(mutex, NULL);
+}
+
+
+/*
+ * '_cupsMutexLock()' - Lock a mutex.
+ */
+
+void
+_cupsMutexLock(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ pthread_mutex_lock(mutex);
+}
+
+
+/*
+ * '_cupsMutexUnlock()' - Unlock a mutex.
+ */
+
+void
+_cupsMutexUnlock(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ pthread_mutex_unlock(mutex);
+}
+
+
+/*
+ * '_cupsRWInit()' - Initialize a reader/writer lock.
+ */
+
+void
+_cupsRWInit(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ pthread_rwlock_init(rwlock, NULL);
+}
+
+
+/*
+ * '_cupsRWLockRead()' - Acquire a reader/writer lock for reading.
+ */
+
+void
+_cupsRWLockRead(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ pthread_rwlock_rdlock(rwlock);
+}
+
+
+/*
+ * '_cupsRWLockWrite()' - Acquire a reader/writer lock for writing.
+ */
+
+void
+_cupsRWLockWrite(_cups_rwlock_t *rwlock)/* I - Reader/writer lock */
+{
+ pthread_rwlock_wrlock(rwlock);
+}
+
+
+/*
+ * '_cupsRWUnlock()' - Release a reader/writer lock.
+ */
+
+void
+_cupsRWUnlock(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ pthread_rwlock_unlock(rwlock);
+}
+
+
+/*
+ * '_cupsThreadCreate()' - Create a thread.
+ */
+
+int /* O - 0 on failure, 1 on success */
+_cupsThreadCreate(
+ _cups_thread_func_t func, /* I - Entry point */
+ void *arg) /* I - Entry point context */
+{
+ pthread_t thread;
+
+ return (pthread_create(&thread, NULL, (void *(*)(void *))func, arg) == 0);
+}
+
+
+#elif defined(WIN32)
+# include <process.h>
+
+
+/*
+ * '_cupsMutexInit()' - Initialize a mutex.
+ */
+
+void
+_cupsMutexInit(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ InitializeCriticalSection(&mutex->m_criticalSection);
+ mutex->m_init = 1;
+}
+
+
+/*
+ * '_cupsMutexLock()' - Lock a mutex.
+ */
+
+void
+_cupsMutexLock(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ if (!mutex->m_init)
+ {
+ _cupsGlobalLock();
+
+ if (!mutex->m_init)
+ {
+ InitializeCriticalSection(&mutex->m_criticalSection);
+ mutex->m_init = 1;
+ }
+
+ _cupsGlobalUnlock();
+ }
+
+ EnterCriticalSection(&mutex->m_criticalSection);
+}
+
+
+/*
+ * '_cupsMutexUnlock()' - Unlock a mutex.
+ */
+
+void
+_cupsMutexUnlock(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ LeaveCriticalSection(&mutex->m_criticalSection);
+}
+
+
+/*
+ * '_cupsRWInit()' - Initialize a reader/writer lock.
+ */
+
+void
+_cupsRWInit(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ _cupsMutexInit((_cups_mutex_t *)rwlock);
+}
+
+
+/*
+ * '_cupsRWLockRead()' - Acquire a reader/writer lock for reading.
+ */
+
+void
+_cupsRWLockRead(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ _cupsMutexLock((_cups_mutex_t *)rwlock);
+}
+
+
+/*
+ * '_cupsRWLockWrite()' - Acquire a reader/writer lock for writing.
+ */
+
+void
+_cupsRWLockWrite(_cups_rwlock_t *rwlock)/* I - Reader/writer lock */
+{
+ _cupsMutexLock((_cups_mutex_t *)rwlock);
+}
+
+
+/*
+ * '_cupsRWUnlock()' - Release a reader/writer lock.
+ */
+
+void
+_cupsRWUnlock(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ _cupsMutexUnlock((_cups_mutex_t *)rwlock);
+}
+
+
+/*
+ * '_cupsThreadCreate()' - Create a thread.
+ */
+
+int /* O - 0 on failure, 1 on success */
+_cupsThreadCreate(
+ _cups_thread_func_t func, /* I - Entry point */
+ void *arg) /* I - Entry point context */
+{
+ return (_beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) func, arg, 0, NULL)
+ != 0);
+}
+
+
+#else
+/*
+ * '_cupsMutexInit()' - Initialize a mutex.
+ */
+
+void
+_cupsMutexInit(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ (void)mutex;
+}
+
+
+/*
+ * '_cupsMutexLock()' - Lock a mutex.
+ */
+
+void
+_cupsMutexLock(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ (void)mutex;
+}
+
+
+/*
+ * '_cupsMutexUnlock()' - Unlock a mutex.
+ */
+
+void
+_cupsMutexUnlock(_cups_mutex_t *mutex) /* I - Mutex */
+{
+ (void)mutex;
+}
+
+
+/*
+ * '_cupsRWInit()' - Initialize a reader/writer lock.
+ */
+
+void
+_cupsRWInit(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ (void)rwlock;
+}
+
+
+/*
+ * '_cupsRWLockRead()' - Acquire a reader/writer lock for reading.
+ */
+
+void
+_cupsRWLockRead(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ (void)rwlock;
+}
+
+
+/*
+ * '_cupsRWLockWrite()' - Acquire a reader/writer lock for writing.
+ */
+
+void
+_cupsRWLockWrite(_cups_rwlock_t *rwlock)/* I - Reader/writer lock */
+{
+ (void)rwlock;
+}
+
+
+/*
+ * '_cupsRWUnlock()' - Release a reader/writer lock.
+ */
+
+void
+_cupsRWUnlock(_cups_rwlock_t *rwlock) /* I - Reader/writer lock */
+{
+ (void)rwlock;
+}
+
+
+/*
+ * '_cupsThreadCreate()' - Create a thread.
+ */
+
+int /* O - 0 on failure, 1 on success */
+_cupsThreadCreate(
+ _cups_thread_func_t func, /* I - Entry point */
+ void *arg) /* I - Entry point context */
+{
+ fputs("DEBUG: CUPS was compiled without threading support, no thread "
+ "created.\n", stderr);
+
+ (void)func;
+ (void)arg;
+
+ return (0);
+}
+#endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * End of "$Id: thread.c 11642 2014-02-27 15:57:59Z msweet $".
+ */
diff --git a/cups/libs/cups/transcode.c b/cups/libs/cups/transcode.c
new file mode 100644
index 000000000..131b0741c
--- /dev/null
+++ b/cups/libs/cups/transcode.c
@@ -0,0 +1,720 @@
+/*
+ * "$Id: transcode.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Transcoding support for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsCharmapFlush() - Flush all character set maps out of cache.
+ * cupsCharsetToUTF8() - Convert legacy character set to UTF-8.
+ * cupsUTF8ToCharset() - Convert UTF-8 to legacy character set.
+ * cupsUTF8ToUTF32() - Convert UTF-8 to UTF-32.
+ * cupsUTF32ToUTF8() - Convert UTF-32 to UTF-8.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <limits.h>
+#include <time.h>
+#ifdef HAVE_ICONV_H
+# include <iconv.h>
+#endif /* HAVE_ICONV_H */
+
+
+/*
+ * Local globals...
+ */
+
+#ifdef HAVE_ICONV_H
+static _cups_mutex_t map_mutex = _CUPS_MUTEX_INITIALIZER;
+ /* Mutex to control access to maps */
+static iconv_t map_from_utf8 = (iconv_t)-1;
+ /* Convert from UTF-8 to charset */
+static iconv_t map_to_utf8 = (iconv_t)-1;
+ /* Convert from charset to UTF-8 */
+static cups_encoding_t map_encoding = CUPS_AUTO_ENCODING;
+ /* Which charset is cached */
+#endif /* HAVE_ICONV_H */
+
+
+/*
+ * '_cupsCharmapFlush()' - Flush all character set maps out of cache.
+ */
+
+void
+_cupsCharmapFlush(void)
+{
+#ifdef HAVE_ICONV_H
+ if (map_from_utf8 != (iconv_t)-1)
+ {
+ iconv_close(map_from_utf8);
+ map_from_utf8 = (iconv_t)-1;
+ }
+
+ if (map_to_utf8 != (iconv_t)-1)
+ {
+ iconv_close(map_to_utf8);
+ map_to_utf8 = (iconv_t)-1;
+ }
+
+ map_encoding = CUPS_AUTO_ENCODING;
+#endif /* HAVE_ICONV_H */
+}
+
+
+/*
+ * 'cupsCharsetToUTF8()' - Convert legacy character set to UTF-8.
+ */
+
+int /* O - Count or -1 on error */
+cupsCharsetToUTF8(
+ cups_utf8_t *dest, /* O - Target string */
+ const char *src, /* I - Source string */
+ const int maxout, /* I - Max output */
+ const cups_encoding_t encoding) /* I - Encoding */
+{
+ cups_utf8_t *destptr; /* Pointer into UTF-8 buffer */
+#ifdef HAVE_ICONV_H
+ size_t srclen, /* Length of source string */
+ outBytesLeft; /* Bytes remaining in output buffer */
+#endif /* HAVE_ICONV_H */
+
+
+ /*
+ * Check for valid arguments...
+ */
+
+ DEBUG_printf(("2cupsCharsetToUTF8(dest=%p, src=\"%s\", maxout=%d, encoding=%d)",
+ dest, src, maxout, encoding));
+
+ if (!dest || !src || maxout < 1)
+ {
+ if (dest)
+ *dest = '\0';
+
+ DEBUG_puts("3cupsCharsetToUTF8: Bad arguments, returning -1");
+ return (-1);
+ }
+
+ /*
+ * Handle identity conversions...
+ */
+
+ if (encoding == CUPS_UTF8 || encoding <= CUPS_US_ASCII ||
+ encoding >= CUPS_ENCODING_VBCS_END)
+ {
+ strlcpy((char *)dest, src, maxout);
+ return ((int)strlen((char *)dest));
+ }
+
+ /*
+ * Handle ISO-8859-1 to UTF-8 directly...
+ */
+
+ destptr = dest;
+
+ if (encoding == CUPS_ISO8859_1)
+ {
+ int ch; /* Character from string */
+ cups_utf8_t *destend; /* End of UTF-8 buffer */
+
+
+ destend = dest + maxout - 2;
+
+ while (*src && destptr < destend)
+ {
+ ch = *src++ & 255;
+
+ if (ch & 128)
+ {
+ *destptr++ = 0xc0 | (ch >> 6);
+ *destptr++ = 0x80 | (ch & 0x3f);
+ }
+ else
+ *destptr++ = ch;
+ }
+
+ *destptr = '\0';
+
+ return ((int)(destptr - dest));
+ }
+
+ /*
+ * Convert input legacy charset to UTF-8...
+ */
+
+#ifdef HAVE_ICONV_H
+ _cupsMutexLock(&map_mutex);
+
+ if (map_encoding != encoding)
+ {
+ _cupsCharmapFlush();
+
+ map_from_utf8 = iconv_open(_cupsEncodingName(encoding), "UTF-8");
+ map_to_utf8 = iconv_open("UTF-8", _cupsEncodingName(encoding));
+ map_encoding = encoding;
+ }
+
+ if (map_to_utf8 != (iconv_t)-1)
+ {
+ char *altdestptr = (char *)dest; /* Silence bogus GCC type-punned */
+
+ srclen = strlen(src);
+ outBytesLeft = maxout - 1;
+
+ iconv(map_to_utf8, (char **)&src, &srclen, &altdestptr, &outBytesLeft);
+ *altdestptr = '\0';
+
+ _cupsMutexUnlock(&map_mutex);
+
+ return ((int)(altdestptr - (char *)dest));
+ }
+
+ _cupsMutexUnlock(&map_mutex);
+#endif /* HAVE_ICONV_H */
+
+ /*
+ * No iconv() support, so error out...
+ */
+
+ *destptr = '\0';
+
+ return (-1);
+}
+
+
+/*
+ * 'cupsUTF8ToCharset()' - Convert UTF-8 to legacy character set.
+ */
+
+int /* O - Count or -1 on error */
+cupsUTF8ToCharset(
+ char *dest, /* O - Target string */
+ const cups_utf8_t *src, /* I - Source string */
+ const int maxout, /* I - Max output */
+ const cups_encoding_t encoding) /* I - Encoding */
+{
+ char *destptr; /* Pointer into destination */
+#ifdef HAVE_ICONV_H
+ size_t srclen, /* Length of source string */
+ outBytesLeft; /* Bytes remaining in output buffer */
+#endif /* HAVE_ICONV_H */
+
+
+ /*
+ * Check for valid arguments...
+ */
+
+ if (!dest || !src || maxout < 1)
+ {
+ if (dest)
+ *dest = '\0';
+
+ return (-1);
+ }
+
+ /*
+ * Handle identity conversions...
+ */
+
+ if (encoding == CUPS_UTF8 ||
+ encoding >= CUPS_ENCODING_VBCS_END)
+ {
+ strlcpy(dest, (char *)src, maxout);
+ return ((int)strlen(dest));
+ }
+
+ /*
+ * Handle UTF-8 to ISO-8859-1 directly...
+ */
+
+ destptr = dest;
+
+ if (encoding == CUPS_ISO8859_1 || encoding <= CUPS_US_ASCII)
+ {
+ int ch, /* Character from string */
+ maxch; /* Maximum character for charset */
+ char *destend; /* End of ISO-8859-1 buffer */
+
+ maxch = encoding == CUPS_ISO8859_1 ? 256 : 128;
+ destend = dest + maxout - 1;
+
+ while (*src && destptr < destend)
+ {
+ ch = *src++;
+
+ if ((ch & 0xe0) == 0xc0)
+ {
+ ch = ((ch & 0x1f) << 6) | (*src++ & 0x3f);
+
+ if (ch < maxch)
+ *destptr++ = ch;
+ else
+ *destptr++ = '?';
+ }
+ else if ((ch & 0xf0) == 0xe0 ||
+ (ch & 0xf8) == 0xf0)
+ *destptr++ = '?';
+ else if (!(ch & 0x80))
+ *destptr++ = ch;
+ }
+
+ *destptr = '\0';
+
+ return ((int)(destptr - dest));
+ }
+
+#ifdef HAVE_ICONV_H
+ /*
+ * Convert input UTF-8 to legacy charset...
+ */
+
+ _cupsMutexLock(&map_mutex);
+
+ if (map_encoding != encoding)
+ {
+ _cupsCharmapFlush();
+
+ map_from_utf8 = iconv_open(_cupsEncodingName(encoding), "UTF-8");
+ map_to_utf8 = iconv_open("UTF-8", _cupsEncodingName(encoding));
+ map_encoding = encoding;
+ }
+
+ if (map_from_utf8 != (iconv_t)-1)
+ {
+ char *altsrc = (char *)src; /* Silence bogus GCC type-punned */
+
+ srclen = strlen((char *)src);
+ outBytesLeft = maxout - 1;
+
+ iconv(map_from_utf8, &altsrc, &srclen, &destptr, &outBytesLeft);
+ *destptr = '\0';
+
+ _cupsMutexUnlock(&map_mutex);
+
+ return ((int)(destptr - dest));
+ }
+
+ _cupsMutexUnlock(&map_mutex);
+#endif /* HAVE_ICONV_H */
+
+ /*
+ * No iconv() support, so error out...
+ */
+
+ *destptr = '\0';
+
+ return (-1);
+}
+
+
+/*
+ * 'cupsUTF8ToUTF32()' - Convert UTF-8 to UTF-32.
+ *
+ * 32-bit UTF-32 (actually 21-bit) maps to UTF-8 as follows...
+ *
+ * UTF-32 char UTF-8 char(s)
+ * --------------------------------------------------
+ * 0 to 127 = 0xxxxxxx (US-ASCII)
+ * 128 to 2047 = 110xxxxx 10yyyyyy
+ * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz
+ * > 65535 = 11110xxx 10yyyyyy 10zzzzzz 10xxxxxx
+ *
+ * UTF-32 prohibits chars beyond Plane 16 (> 0x10ffff) in UCS-4,
+ * which would convert to five- or six-octet UTF-8 sequences...
+ */
+
+int /* O - Count or -1 on error */
+cupsUTF8ToUTF32(
+ cups_utf32_t *dest, /* O - Target string */
+ const cups_utf8_t *src, /* I - Source string */
+ const int maxout) /* I - Max output */
+{
+ int i; /* Looping variable */
+ cups_utf8_t ch; /* Character value */
+ cups_utf8_t next; /* Next character value */
+ cups_utf32_t ch32; /* UTF-32 character value */
+
+
+ /*
+ * Check for valid arguments and clear output...
+ */
+
+ DEBUG_printf(("2cupsUTF8ToUTF32(dest=%p, src=\"%s\", maxout=%d)", dest,
+ src, maxout));
+
+ if (dest)
+ *dest = 0;
+
+ if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad arguments)");
+
+ return (-1);
+ }
+
+ /*
+ * Convert input UTF-8 to output UTF-32...
+ */
+
+ for (i = maxout - 1; *src && i > 0; i --)
+ {
+ ch = *src++;
+
+ /*
+ * Convert UTF-8 character(s) to UTF-32 character...
+ */
+
+ if (!(ch & 0x80))
+ {
+ /*
+ * One-octet UTF-8 <= 127 (US-ASCII)...
+ */
+
+ *dest++ = ch;
+
+ DEBUG_printf(("4cupsUTF8ToUTF32: %02x => %08X", src[-1], ch));
+ continue;
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-octet UTF-8 <= 2047 (Latin-x)...
+ */
+
+ next = *src++;
+ if ((next & 0xc0) != 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ ch32 = ((ch & 0x1f) << 6) | (next & 0x3f);
+
+ /*
+ * Check for non-shortest form (invalid UTF-8)...
+ */
+
+ if (ch32 < 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ *dest++ = ch32;
+
+ DEBUG_printf(("4cupsUTF8ToUTF32: %02x %02x => %08X",
+ src[-2], src[-1], (unsigned)ch32));
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-octet UTF-8 <= 65535 (Plane 0 - BMP)...
+ */
+
+ next = *src++;
+ if ((next & 0xc0) != 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ ch32 = ((ch & 0x0f) << 6) | (next & 0x3f);
+
+ next = *src++;
+ if ((next & 0xc0) != 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ ch32 = (ch32 << 6) | (next & 0x3f);
+
+ /*
+ * Check for non-shortest form (invalid UTF-8)...
+ */
+
+ if (ch32 < 0x800)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ *dest++ = ch32;
+
+ DEBUG_printf(("4cupsUTF8ToUTF32: %02x %02x %02x => %08X",
+ src[-3], src[-2], src[-1], (unsigned)ch32));
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-octet UTF-8...
+ */
+
+ next = *src++;
+ if ((next & 0xc0) != 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ ch32 = ((ch & 0x07) << 6) | (next & 0x3f);
+
+ next = *src++;
+ if ((next & 0xc0) != 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ ch32 = (ch32 << 6) | (next & 0x3f);
+
+ next = *src++;
+ if ((next & 0xc0) != 0x80)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ ch32 = (ch32 << 6) | (next & 0x3f);
+
+ /*
+ * Check for non-shortest form (invalid UTF-8)...
+ */
+
+ if (ch32 < 0x10000)
+ {
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ *dest++ = ch32;
+
+ DEBUG_printf(("4cupsUTF8ToUTF32: %02x %02x %02x %02x => %08X",
+ src[-4], src[-3], src[-2], src[-1], (unsigned)ch32));
+ }
+ else
+ {
+ /*
+ * More than 4-octet (invalid UTF-8 sequence)...
+ */
+
+ DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
+
+ return (-1);
+ }
+
+ /*
+ * Check for UTF-16 surrogate (illegal UTF-8)...
+ */
+
+ if (ch32 >= 0xd800 && ch32 <= 0xdfff)
+ return (-1);
+ }
+
+ *dest = 0;
+
+ DEBUG_printf(("3cupsUTF8ToUTF32: Returning %d characters", maxout - 1 - i));
+
+ return (maxout - 1 - i);
+}
+
+
+/*
+ * 'cupsUTF32ToUTF8()' - Convert UTF-32 to UTF-8.
+ *
+ * 32-bit UTF-32 (actually 21-bit) maps to UTF-8 as follows...
+ *
+ * UTF-32 char UTF-8 char(s)
+ * --------------------------------------------------
+ * 0 to 127 = 0xxxxxxx (US-ASCII)
+ * 128 to 2047 = 110xxxxx 10yyyyyy
+ * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz
+ * > 65535 = 11110xxx 10yyyyyy 10zzzzzz 10xxxxxx
+ *
+ * UTF-32 prohibits chars beyond Plane 16 (> 0x10ffff) in UCS-4,
+ * which would convert to five- or six-octet UTF-8 sequences...
+ */
+
+int /* O - Count or -1 on error */
+cupsUTF32ToUTF8(
+ cups_utf8_t *dest, /* O - Target string */
+ const cups_utf32_t *src, /* I - Source string */
+ const int maxout) /* I - Max output */
+{
+ cups_utf8_t *start; /* Start of destination string */
+ int i; /* Looping variable */
+ int swap; /* Byte-swap input to output */
+ cups_utf32_t ch; /* Character value */
+
+
+ /*
+ * Check for valid arguments and clear output...
+ */
+
+ DEBUG_printf(("2cupsUTF32ToUTF8(dest=%p, src=%p, maxout=%d)", dest, src,
+ maxout));
+
+ if (dest)
+ *dest = '\0';
+
+ if (!dest || !src || maxout < 1)
+ {
+ DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (bad args)");
+
+ return (-1);
+ }
+
+ /*
+ * Check for leading BOM in UTF-32 and inverted BOM...
+ */
+
+ start = dest;
+ swap = *src == 0xfffe0000;
+
+ DEBUG_printf(("4cupsUTF32ToUTF8: swap=%d", swap));
+
+ if (*src == 0xfffe0000 || *src == 0xfeff)
+ src ++;
+
+ /*
+ * Convert input UTF-32 to output UTF-8...
+ */
+
+ for (i = maxout - 1; *src && i > 0;)
+ {
+ ch = *src++;
+
+ /*
+ * Byte swap input UTF-32, if necessary...
+ * (only byte-swapping 24 of 32 bits)
+ */
+
+ if (swap)
+ ch = ((ch >> 24) | ((ch >> 8) & 0xff00) | ((ch << 8) & 0xff0000));
+
+ /*
+ * Check for beyond Plane 16 (invalid UTF-32)...
+ */
+
+ if (ch > 0x10ffff)
+ {
+ DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (character out of range)");
+
+ return (-1);
+ }
+
+ /*
+ * Convert UTF-32 character to UTF-8 character(s)...
+ */
+
+ if (ch < 0x80)
+ {
+ /*
+ * One-octet UTF-8 <= 127 (US-ASCII)...
+ */
+
+ *dest++ = (cups_utf8_t)ch;
+ i --;
+
+ DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x", (unsigned)ch, dest[-1]));
+ }
+ else if (ch < 0x800)
+ {
+ /*
+ * Two-octet UTF-8 <= 2047 (Latin-x)...
+ */
+
+ if (i < 2)
+ {
+ DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (too long 2)");
+
+ return (-1);
+ }
+
+ *dest++ = (cups_utf8_t)(0xc0 | ((ch >> 6) & 0x1f));
+ *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
+ i -= 2;
+
+ DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x %02x", (unsigned)ch,
+ dest[-2], dest[-1]));
+ }
+ else if (ch < 0x10000)
+ {
+ /*
+ * Three-octet UTF-8 <= 65535 (Plane 0 - BMP)...
+ */
+
+ if (i < 3)
+ {
+ DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (too long 3)");
+
+ return (-1);
+ }
+
+ *dest++ = (cups_utf8_t)(0xe0 | ((ch >> 12) & 0x0f));
+ *dest++ = (cups_utf8_t)(0x80 | ((ch >> 6) & 0x3f));
+ *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
+ i -= 3;
+
+ DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x %02x %02x", (unsigned)ch,
+ dest[-3], dest[-2], dest[-1]));
+ }
+ else
+ {
+ /*
+ * Four-octet UTF-8...
+ */
+
+ if (i < 4)
+ {
+ DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (too long 4)");
+
+ return (-1);
+ }
+
+ *dest++ = (cups_utf8_t)(0xf0 | ((ch >> 18) & 0x07));
+ *dest++ = (cups_utf8_t)(0x80 | ((ch >> 12) & 0x3f));
+ *dest++ = (cups_utf8_t)(0x80 | ((ch >> 6) & 0x3f));
+ *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
+ i -= 4;
+
+ DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x %02x %02x %02x",
+ (unsigned)ch, dest[-4], dest[-3], dest[-2], dest[-1]));
+ }
+ }
+
+ *dest = '\0';
+
+ DEBUG_printf(("3cupsUTF32ToUTF8: Returning %d", (int)(dest - start)));
+
+ return ((int)(dest - start));
+}
+
+
+/*
+ * End of "$Id: transcode.c 10996 2013-05-29 11:51:34Z msweet $"
+ */
diff --git a/cups/libs/cups/transcode.h b/cups/libs/cups/transcode.h
new file mode 100644
index 000000000..da95d0cd6
--- /dev/null
+++ b/cups/libs/cups/transcode.h
@@ -0,0 +1,81 @@
+/*
+ * "$Id: transcode.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Transcoding definitions for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_TRANSCODE_H_
+# define _CUPS_TRANSCODE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include "language.h"
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define CUPS_MAX_USTRING 8192 /* Max size of Unicode string */
+
+
+/*
+ * Types...
+ */
+
+typedef unsigned char cups_utf8_t; /* UTF-8 Unicode/ISO-10646 unit */
+typedef unsigned long cups_utf32_t; /* UTF-32 Unicode/ISO-10646 unit */
+typedef unsigned short cups_ucs2_t; /* UCS-2 Unicode/ISO-10646 unit */
+typedef unsigned long cups_ucs4_t; /* UCS-4 Unicode/ISO-10646 unit */
+typedef unsigned char cups_sbcs_t; /* SBCS Legacy 8-bit unit */
+typedef unsigned short cups_dbcs_t; /* DBCS Legacy 16-bit unit */
+typedef unsigned long cups_vbcs_t; /* VBCS Legacy 32-bit unit */
+ /* EUC uses 8, 16, 24, 32-bit */
+
+
+/*
+ * Prototypes...
+ */
+
+extern int cupsCharsetToUTF8(cups_utf8_t *dest,
+ const char *src,
+ const int maxout,
+ const cups_encoding_t encoding) _CUPS_API_1_2;
+extern int cupsUTF8ToCharset(char *dest,
+ const cups_utf8_t *src,
+ const int maxout,
+ const cups_encoding_t encoding) _CUPS_API_1_2;
+extern int cupsUTF8ToUTF32(cups_utf32_t *dest,
+ const cups_utf8_t *src,
+ const int maxout) _CUPS_API_1_2;
+extern int cupsUTF32ToUTF8(cups_utf8_t *dest,
+ const cups_utf32_t *src,
+ const int maxout) _CUPS_API_1_2;
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_TRANSCODE_H_ */
+
+
+/*
+ * End of "$Id: transcode.h 10996 2013-05-29 11:51:34Z msweet $"
+ */
diff --git a/cups/libs/cups/usersys.c b/cups/libs/cups/usersys.c
new file mode 100644
index 000000000..20786b739
--- /dev/null
+++ b/cups/libs/cups/usersys.c
@@ -0,0 +1,1149 @@
+/*
+ * "$Id: usersys.c 11689 2014-03-05 21:22:12Z msweet $"
+ *
+ * User, system, and password routines for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <stdlib.h>
+#include <sys/stat.h>
+#ifdef WIN32
+# include <windows.h>
+#else
+# include <pwd.h>
+# include <termios.h>
+# include <sys/utsname.h>
+#endif /* WIN32 */
+
+
+/*
+ * Local constants...
+ */
+
+#define _CUPS_PASSCHAR '*' /* Character that is echoed for password */
+
+
+/*
+ * Local functions...
+ */
+
+static void cups_read_client_conf(cups_file_t *fp,
+ _cups_globals_t *cg,
+ const char *cups_encryption,
+ const char *cups_server,
+ const char *cups_user,
+#ifdef HAVE_GSSAPI
+ const char *cups_gssservicename,
+#endif /* HAVE_GSSAPI */
+ const char *cups_anyroot,
+ const char *cups_expiredroot,
+ const char *cups_expiredcerts);
+
+
+/*
+ * 'cupsEncryption()' - Get the current encryption settings.
+ *
+ * The default encryption setting comes from the CUPS_ENCRYPTION
+ * environment variable, then the ~/.cups/client.conf file, and finally the
+ * /etc/cups/client.conf file. If not set, the default is
+ * @code HTTP_ENCRYPTION_IF_REQUESTED@.
+ *
+ * Note: The current encryption setting is tracked separately for each thread
+ * in a program. Multi-threaded programs that override the setting via the
+ * @link cupsSetEncryption@ function need to do so in each thread for the same
+ * setting to be used.
+ */
+
+http_encryption_t /* O - Encryption settings */
+cupsEncryption(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (cg->encryption == (http_encryption_t)-1)
+ _cupsSetDefaults();
+
+ return (cg->encryption);
+}
+
+
+/*
+ * 'cupsGetPassword()' - Get a password from the user.
+ *
+ * Uses the current password callback function. Returns @code NULL@ if the
+ * user does not provide a password.
+ *
+ * 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.
+ */
+
+const char * /* O - Password */
+cupsGetPassword(const char *prompt) /* I - Prompt string */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ return ((cg->password_cb)(prompt, NULL, NULL, NULL, cg->password_data));
+}
+
+
+/*
+ * 'cupsGetPassword2()' - Get a password from the user using the advanced
+ * password callback.
+ *
+ * Uses the current password callback function. Returns @code NULL@ if the
+ * user does not provide a password.
+ *
+ * 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.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+const char * /* O - Password */
+cupsGetPassword2(const char *prompt, /* I - Prompt string */
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *method, /* I - Request method ("GET", "POST", "PUT") */
+ const char *resource) /* I - Resource path */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (!http)
+ http = _cupsConnect();
+
+ return ((cg->password_cb)(prompt, http, method, resource, cg->password_data));
+}
+
+
+/*
+ * 'cupsServer()' - Return the hostname/address of the current server.
+ *
+ * The default server comes from the CUPS_SERVER environment variable, then the
+ * ~/.cups/client.conf file, and finally the /etc/cups/client.conf file. If not
+ * set, the default is the local system - either "localhost" or a domain socket
+ * path.
+ *
+ * The returned value can be a fully-qualified hostname, a numeric IPv4 or IPv6
+ * address, or a domain socket pathname.
+ *
+ * Note: The current server is tracked separately for each thread in a program.
+ * Multi-threaded programs that override the server via the
+ * @link cupsSetServer@ function need to do so in each thread for the same
+ * server to be used.
+ */
+
+const char * /* O - Server name */
+cupsServer(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (!cg->server[0])
+ _cupsSetDefaults();
+
+ return (cg->server);
+}
+
+
+/*
+ * 'cupsSetClientCertCB()' - Set the client certificate callback.
+ *
+ * Pass @code NULL@ to restore the default callback.
+ *
+ * Note: The current certificate 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.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+void
+cupsSetClientCertCB(
+ cups_client_cert_cb_t cb, /* I - Callback function */
+ void *user_data) /* I - User data pointer */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ cg->client_cert_cb = cb;
+ cg->client_cert_data = user_data;
+}
+
+
+/*
+ * 'cupsSetCredentials()' - Set the default credentials to be used for SSL/TLS
+ * connections.
+ *
+ * Note: The default credentials are tracked separately for each thread in a
+ * program. Multi-threaded programs that override the setting need to do so in
+ * each thread for the same setting to be used.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+int /* O - Status of call (0 = success) */
+cupsSetCredentials(
+ cups_array_t *credentials) /* I - Array of credentials */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (cupsArrayCount(credentials) < 1)
+ return (-1);
+
+ _httpFreeCredentials(cg->tls_credentials);
+ cg->tls_credentials = _httpCreateCredentials(credentials);
+
+ return (cg->tls_credentials ? 0 : -1);
+}
+
+
+/*
+ * 'cupsSetEncryption()' - Set the encryption preference.
+ *
+ * The default encryption setting comes from the CUPS_ENCRYPTION
+ * environment variable, then the ~/.cups/client.conf file, and finally the
+ * /etc/cups/client.conf file. If not set, the default is
+ * @code HTTP_ENCRYPTION_IF_REQUESTED@.
+ *
+ * Note: The current encryption setting is tracked separately for each thread
+ * in a program. Multi-threaded programs that override the setting need to do
+ * so in each thread for the same setting to be used.
+ */
+
+void
+cupsSetEncryption(http_encryption_t e) /* I - New encryption preference */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ cg->encryption = e;
+
+ if (cg->http)
+ httpEncryption(cg->http, e);
+}
+
+
+/*
+ * 'cupsSetPasswordCB()' - Set the password callback for CUPS.
+ *
+ * Pass @code NULL@ to restore the default (console) password callback, which
+ * reads the password from the console. Programs should call either this
+ * function or @link cupsSetPasswordCB2@, as only one callback can be registered
+ * by a program per thread.
+ *
+ * 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.
+ */
+
+void
+cupsSetPasswordCB(cups_password_cb_t cb)/* I - Callback function */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (cb == (cups_password_cb_t)0)
+ cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
+ else
+ cg->password_cb = (cups_password_cb2_t)cb;
+
+ cg->password_data = NULL;
+}
+
+
+/*
+ * 'cupsSetPasswordCB2()' - Set the advanced password callback for CUPS.
+ *
+ * Pass @code NULL@ to restore the default (console) password callback, which
+ * reads the password from the console. Programs should call either this
+ * function or @link cupsSetPasswordCB2@, as only one callback can be registered
+ * by a program per thread.
+ *
+ * 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.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+void
+cupsSetPasswordCB2(
+ cups_password_cb2_t cb, /* I - Callback function */
+ void *user_data) /* I - User data pointer */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (cb == (cups_password_cb2_t)0)
+ cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
+ else
+ cg->password_cb = cb;
+
+ cg->password_data = user_data;
+}
+
+
+/*
+ * 'cupsSetServer()' - Set the default server name and port.
+ *
+ * The "server" string can be a fully-qualified hostname, a numeric
+ * IPv4 or IPv6 address, or a domain socket pathname. Hostnames and numeric IP
+ * addresses can be optionally followed by a colon and port number to override
+ * the default port 631, e.g. "hostname:8631". Pass @code NULL@ to restore the
+ * default server name and port.
+ *
+ * Note: The current server is tracked separately for each thread in a program.
+ * Multi-threaded programs that override the server need to do so in each
+ * thread for the same server to be used.
+ */
+
+void
+cupsSetServer(const char *server) /* I - Server name */
+{
+ char *options, /* Options */
+ *port; /* Pointer to port */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (server)
+ {
+ strlcpy(cg->server, server, sizeof(cg->server));
+
+ if (cg->server[0] != '/' && (options = strrchr(cg->server, '/')) != NULL)
+ {
+ *options++ = '\0';
+
+ if (!strcmp(options, "version=1.0"))
+ cg->server_version = 10;
+ else if (!strcmp(options, "version=1.1"))
+ cg->server_version = 11;
+ else if (!strcmp(options, "version=2.0"))
+ cg->server_version = 20;
+ else if (!strcmp(options, "version=2.1"))
+ cg->server_version = 21;
+ else if (!strcmp(options, "version=2.2"))
+ cg->server_version = 22;
+ }
+ else
+ cg->server_version = 20;
+
+ if (cg->server[0] != '/' && (port = strrchr(cg->server, ':')) != NULL &&
+ !strchr(port, ']') && isdigit(port[1] & 255))
+ {
+ *port++ = '\0';
+
+ cg->ipp_port = atoi(port);
+ }
+
+ if (cg->server[0] == '/')
+ strlcpy(cg->servername, "localhost", sizeof(cg->servername));
+ else
+ strlcpy(cg->servername, cg->server, sizeof(cg->servername));
+ }
+ else
+ {
+ cg->server[0] = '\0';
+ cg->servername[0] = '\0';
+ cg->server_version = 20;
+ }
+
+ if (cg->http)
+ {
+ httpClose(cg->http);
+ cg->http = NULL;
+ }
+}
+
+
+/*
+ * 'cupsSetServerCertCB()' - Set the server certificate callback.
+ *
+ * Pass @code NULL@ to restore the default callback.
+ *
+ * Note: The current credentials 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.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+void
+cupsSetServerCertCB(
+ cups_server_cert_cb_t cb, /* I - Callback function */
+ void *user_data) /* I - User data pointer */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ cg->server_cert_cb = cb;
+ cg->server_cert_data = user_data;
+}
+
+
+/*
+ * 'cupsSetUser()' - Set the default user name.
+ *
+ * Pass @code NULL@ to restore the default user name.
+ *
+ * Note: The current user name is tracked separately for each thread in a
+ * program. Multi-threaded programs that override the user name need to do so
+ * in each thread for the same user name to be used.
+ */
+
+void
+cupsSetUser(const char *user) /* I - User name */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (user)
+ strlcpy(cg->user, user, sizeof(cg->user));
+ else
+ cg->user[0] = '\0';
+}
+
+
+/*
+ * 'cupsSetUserAgent()' - Set the default HTTP User-Agent string.
+ *
+ * Setting the string to NULL forces the default value containing the CUPS
+ * version, IPP version, and operating system version and architecture.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+void
+cupsSetUserAgent(const char *user_agent)/* I - User-Agent string or @code NULL@ */
+{
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Thread globals */
+#ifdef WIN32
+ SYSTEM_INFO sysinfo; /* System information */
+ OSVERSIONINFO version; /* OS version info */
+#else
+ struct utsname name; /* uname info */
+#endif /* WIN32 */
+
+
+ if (user_agent)
+ {
+ strlcpy(cg->user_agent, user_agent, sizeof(cg->user_agent));
+ return;
+ }
+
+#ifdef WIN32
+ version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&version);
+#if 0
+ GetNativeSystemInfo(&sysinfo);
+#else
+ GetSystemInfo(&sysinfo);
+#endif
+
+ snprintf(cg->user_agent, sizeof(cg->user_agent),
+ CUPS_MINIMAL " (Windows %d.%d; %s) IPP/2.0",
+ version.dwMajorVersion, version.dwMinorVersion,
+ sysinfo.wProcessorArchitecture
+ == PROCESSOR_ARCHITECTURE_AMD64 ? "amd64" :
+ sysinfo.wProcessorArchitecture
+ == PROCESSOR_ARCHITECTURE_ARM ? "arm" :
+ sysinfo.wProcessorArchitecture
+ == PROCESSOR_ARCHITECTURE_IA64 ? "ia64" :
+ sysinfo.wProcessorArchitecture
+ == PROCESSOR_ARCHITECTURE_INTEL ? "intel" :
+ "unknown");
+
+#else
+ uname(&name);
+
+ snprintf(cg->user_agent, sizeof(cg->user_agent),
+ CUPS_MINIMAL " (%s %s; %s) IPP/2.0",
+ name.sysname, name.release, name.machine);
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'cupsUser()' - Return the current user's name.
+ *
+ * Note: The current user name is tracked separately for each thread in a
+ * program. Multi-threaded programs that override the user name with the
+ * @link cupsSetUser@ function need to do so in each thread for the same user
+ * name to be used.
+ */
+
+const char * /* O - User name */
+cupsUser(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ if (!cg->user[0])
+ _cupsSetDefaults();
+
+ return (cg->user);
+}
+
+
+/*
+ * 'cupsUserAgent()' - Return the default HTTP User-Agent string.
+ *
+ * @since CUPS 1.7/OS X 10.9@
+ */
+
+const char * /* O - User-Agent string */
+cupsUserAgent(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Thread globals */
+
+
+ if (!cg->user_agent[0])
+ cupsSetUserAgent(NULL);
+
+ return (cg->user_agent);
+}
+
+
+/*
+ * '_cupsGetPassword()' - Get a password from the user.
+ */
+
+const char * /* O - Password or @code NULL@ if none */
+_cupsGetPassword(const char *prompt) /* I - Prompt string */
+{
+#ifdef WIN32
+ HANDLE tty; /* Console handle */
+ DWORD mode; /* Console mode */
+ char passch, /* Current key press */
+ *passptr, /* Pointer into password string */
+ *passend; /* End of password string */
+ DWORD passbytes; /* Bytes read */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Thread globals */
+
+
+ /*
+ * Disable input echo and set raw input...
+ */
+
+ if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
+ return (NULL);
+
+ if (!GetConsoleMode(tty, &mode))
+ return (NULL);
+
+ if (!SetConsoleMode(tty, 0))
+ return (NULL);
+
+ /*
+ * Display the prompt...
+ */
+
+ printf("%s ", prompt);
+ fflush(stdout);
+
+ /*
+ * Read the password string from /dev/tty until we get interrupted or get a
+ * carriage return or newline...
+ */
+
+ passptr = cg->password;
+ passend = cg->password + sizeof(cg->password) - 1;
+
+ while (ReadFile(tty, &passch, 1, &passbytes, NULL))
+ {
+ if (passch == 0x0A || passch == 0x0D)
+ {
+ /*
+ * Enter/return...
+ */
+
+ break;
+ }
+ else if (passch == 0x08 || passch == 0x7F)
+ {
+ /*
+ * Backspace/delete (erase character)...
+ */
+
+ if (passptr > cg->password)
+ {
+ passptr --;
+ fputs("\010 \010", stdout);
+ }
+ else
+ putchar(0x07);
+ }
+ else if (passch == 0x15)
+ {
+ /*
+ * CTRL+U (erase line)
+ */
+
+ if (passptr > cg->password)
+ {
+ while (passptr > cg->password)
+ {
+ passptr --;
+ fputs("\010 \010", stdout);
+ }
+ }
+ else
+ putchar(0x07);
+ }
+ else if (passch == 0x03)
+ {
+ /*
+ * CTRL+C...
+ */
+
+ passptr = cg->password;
+ break;
+ }
+ else if ((passch & 255) < 0x20 || passptr >= passend)
+ putchar(0x07);
+ else
+ {
+ *passptr++ = passch;
+ putchar(_CUPS_PASSCHAR);
+ }
+
+ fflush(stdout);
+ }
+
+ putchar('\n');
+ fflush(stdout);
+
+ /*
+ * Cleanup...
+ */
+
+ SetConsoleMode(tty, mode);
+
+ /*
+ * Return the proper value...
+ */
+
+ if (passbytes == 1 && passptr > cg->password)
+ {
+ *passptr = '\0';
+ return (cg->password);
+ }
+ else
+ {
+ memset(cg->password, 0, sizeof(cg->password));
+ return (NULL);
+ }
+
+#else
+ int tty; /* /dev/tty - never read from stdin */
+ struct termios original, /* Original input mode */
+ noecho; /* No echo input mode */
+ char passch, /* Current key press */
+ *passptr, /* Pointer into password string */
+ *passend; /* End of password string */
+ ssize_t passbytes; /* Bytes read */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Thread globals */
+
+
+ /*
+ * Disable input echo and set raw input...
+ */
+
+ if ((tty = open("/dev/tty", O_RDONLY)) < 0)
+ return (NULL);
+
+ if (tcgetattr(tty, &original))
+ {
+ close(tty);
+ return (NULL);
+ }
+
+ noecho = original;
+ noecho.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+ if (tcsetattr(tty, TCSAFLUSH, &noecho))
+ {
+ close(tty);
+ return (NULL);
+ }
+
+ /*
+ * Display the prompt...
+ */
+
+ printf("%s ", prompt);
+ fflush(stdout);
+
+ /*
+ * Read the password string from /dev/tty until we get interrupted or get a
+ * carriage return or newline...
+ */
+
+ passptr = cg->password;
+ passend = cg->password + sizeof(cg->password) - 1;
+
+ while ((passbytes = read(tty, &passch, 1)) == 1)
+ {
+ if (passch == noecho.c_cc[VEOL] ||
+# ifdef VEOL2
+ passch == noecho.c_cc[VEOL2] ||
+# endif /* VEOL2 */
+ passch == 0x0A || passch == 0x0D)
+ {
+ /*
+ * Enter/return...
+ */
+
+ break;
+ }
+ else if (passch == noecho.c_cc[VERASE] ||
+ passch == 0x08 || passch == 0x7F)
+ {
+ /*
+ * Backspace/delete (erase character)...
+ */
+
+ if (passptr > cg->password)
+ {
+ passptr --;
+ fputs("\010 \010", stdout);
+ }
+ else
+ putchar(0x07);
+ }
+ else if (passch == noecho.c_cc[VKILL])
+ {
+ /*
+ * CTRL+U (erase line)
+ */
+
+ if (passptr > cg->password)
+ {
+ while (passptr > cg->password)
+ {
+ passptr --;
+ fputs("\010 \010", stdout);
+ }
+ }
+ else
+ putchar(0x07);
+ }
+ else if (passch == noecho.c_cc[VINTR] || passch == noecho.c_cc[VQUIT] ||
+ passch == noecho.c_cc[VEOF])
+ {
+ /*
+ * CTRL+C, CTRL+D, or CTRL+Z...
+ */
+
+ passptr = cg->password;
+ break;
+ }
+ else if ((passch & 255) < 0x20 || passptr >= passend)
+ putchar(0x07);
+ else
+ {
+ *passptr++ = passch;
+ putchar(_CUPS_PASSCHAR);
+ }
+
+ fflush(stdout);
+ }
+
+ putchar('\n');
+ fflush(stdout);
+
+ /*
+ * Cleanup...
+ */
+
+ tcsetattr(tty, TCSAFLUSH, &original);
+ close(tty);
+
+ /*
+ * Return the proper value...
+ */
+
+ if (passbytes == 1 && passptr > cg->password)
+ {
+ *passptr = '\0';
+ return (cg->password);
+ }
+ else
+ {
+ memset(cg->password, 0, sizeof(cg->password));
+ return (NULL);
+ }
+#endif /* WIN32 */
+}
+
+
+#ifdef HAVE_GSSAPI
+/*
+ * '_cupsGSSServiceName()' - Get the GSS (Kerberos) service name.
+ */
+
+const char *
+_cupsGSSServiceName(void)
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Thread globals */
+
+
+ if (!cg->gss_service_name[0])
+ _cupsSetDefaults();
+
+ return (cg->gss_service_name);
+}
+#endif /* HAVE_GSSAPI */
+
+
+/*
+ * '_cupsSetDefaults()' - Set the default server, port, and encryption.
+ */
+
+void
+_cupsSetDefaults(void)
+{
+ cups_file_t *fp; /* File */
+ const char *home, /* Home directory of user */
+ *cups_encryption, /* CUPS_ENCRYPTION env var */
+ *cups_server, /* CUPS_SERVER env var */
+ *cups_user, /* CUPS_USER/USER env var */
+#ifdef HAVE_GSSAPI
+ *cups_gssservicename, /* CUPS_GSSSERVICENAME env var */
+#endif /* HAVE_GSSAPI */
+ *cups_anyroot, /* CUPS_ANYROOT env var */
+ *cups_expiredroot, /* CUPS_EXPIREDROOT env var */
+ *cups_expiredcerts; /* CUPS_EXPIREDCERTS env var */
+ char filename[1024]; /* Filename */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ DEBUG_puts("_cupsSetDefaults()");
+
+ /*
+ * First collect environment variables...
+ */
+
+ cups_encryption = getenv("CUPS_ENCRYPTION");
+ cups_server = getenv("CUPS_SERVER");
+#ifdef HAVE_GSSAPI
+ cups_gssservicename = getenv("CUPS_GSSSERVICENAME");
+#endif /* HAVE_GSSAPI */
+ cups_anyroot = getenv("CUPS_ANYROOT");
+ cups_expiredroot = getenv("CUPS_EXPIREDROOT");
+ cups_expiredcerts = getenv("CUPS_EXPIREDCERTS");
+
+ if ((cups_user = getenv("CUPS_USER")) == NULL)
+ {
+#ifndef WIN32
+ /*
+ * Try the USER environment variable...
+ */
+
+ if ((cups_user = getenv("USER")) != NULL)
+ {
+ /*
+ * Validate USER matches the current UID, otherwise don't allow it to
+ * override things... This makes sure that printing after doing su or
+ * sudo records the correct username.
+ */
+
+ struct passwd *pw; /* Account information */
+
+ if ((pw = getpwnam(cups_user)) == NULL || pw->pw_uid != getuid())
+ cups_user = NULL;
+ }
+#endif /* !WIN32 */
+ }
+
+ /*
+ * Then, if needed, read the ~/.cups/client.conf or /etc/cups/client.conf
+ * files to get the default values...
+ */
+
+ if (cg->encryption == (http_encryption_t)-1 || !cg->server[0] ||
+ !cg->user[0] || !cg->ipp_port)
+ {
+# ifdef HAVE_GETEUID
+ if ((geteuid() == getuid() || !getuid()) && getegid() == getgid() && (home = getenv("HOME")) != NULL)
+# elif !defined(WIN32)
+ if (getuid() && (home = getenv("HOME")) != NULL)
+# else
+ if ((home = getenv("HOME")) != NULL)
+# endif /* HAVE_GETEUID */
+ {
+ /*
+ * Look for ~/.cups/client.conf...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/.cups/client.conf", home);
+ fp = cupsFileOpen(filename, "r");
+ }
+ else
+ fp = NULL;
+
+ if (!fp)
+ {
+ /*
+ * Look for CUPS_SERVERROOT/client.conf...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/client.conf",
+ cg->cups_serverroot);
+ fp = cupsFileOpen(filename, "r");
+ }
+
+ /*
+ * Read the configuration file and apply any environment variables; both
+ * functions handle NULL cups_file_t pointers...
+ */
+
+ cups_read_client_conf(fp, cg, cups_encryption, cups_server, cups_user,
+#ifdef HAVE_GSSAPI
+ cups_gssservicename,
+#endif /* HAVE_GSSAPI */
+ cups_anyroot, cups_expiredroot,
+ cups_expiredcerts);
+ cupsFileClose(fp);
+ }
+}
+
+
+/*
+ * 'cups_read_client_conf()' - Read a client.conf file.
+ */
+
+static void
+cups_read_client_conf(
+ cups_file_t *fp, /* I - File to read */
+ _cups_globals_t *cg, /* I - Global data */
+ const char *cups_encryption, /* I - CUPS_ENCRYPTION env var */
+ const char *cups_server, /* I - CUPS_SERVER env var */
+ const char *cups_user, /* I - CUPS_USER env var */
+#ifdef HAVE_GSSAPI
+ const char *cups_gssservicename,
+ /* I - CUPS_GSSSERVICENAME env var */
+#endif /* HAVE_GSSAPI */
+ const char *cups_anyroot, /* I - CUPS_ANYROOT env var */
+ const char *cups_expiredroot, /* I - CUPS_EXPIREDROOT env var */
+ const char *cups_expiredcerts) /* I - CUPS_EXPIREDCERTS env var */
+{
+ int linenum; /* Current line number */
+ char line[1024], /* Line from file */
+ *value, /* Pointer into line */
+ encryption[1024], /* Encryption value */
+#ifndef __APPLE__
+ server_name[1024], /* ServerName value */
+#endif /* !__APPLE__ */
+ user[256], /* User value */
+ any_root[1024], /* AllowAnyRoot value */
+ expired_root[1024], /* AllowExpiredRoot value */
+ expired_certs[1024]; /* AllowExpiredCerts value */
+#ifdef HAVE_GSSAPI
+ char gss_service_name[32]; /* GSSServiceName value */
+#endif /* HAVE_GSSAPI */
+
+
+ /*
+ * Read from the file...
+ */
+
+ linenum = 0;
+ while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ {
+ if (!cups_encryption && cg->encryption == (http_encryption_t)-1 &&
+ !_cups_strcasecmp(line, "Encryption") && value)
+ {
+ strlcpy(encryption, value, sizeof(encryption));
+ cups_encryption = encryption;
+ }
+#ifndef __APPLE__
+ /*
+ * The Server directive is not supported on OS X due to app sandboxing
+ * restrictions, i.e. not all apps request network access.
+ */
+ else if (!cups_server && (!cg->server[0] || !cg->ipp_port) &&
+ !_cups_strcasecmp(line, "ServerName") && value)
+ {
+ strlcpy(server_name, value, sizeof(server_name));
+ cups_server = server_name;
+ }
+#endif /* !__APPLE__ */
+ else if (!cups_user && !_cups_strcasecmp(line, "User") && value)
+ {
+ strlcpy(user, value, sizeof(user));
+ cups_user = user;
+ }
+ else if (!cups_anyroot && !_cups_strcasecmp(line, "AllowAnyRoot") && value)
+ {
+ strlcpy(any_root, value, sizeof(any_root));
+ cups_anyroot = any_root;
+ }
+ else if (!cups_expiredroot && !_cups_strcasecmp(line, "AllowExpiredRoot") &&
+ value)
+ {
+ strlcpy(expired_root, value, sizeof(expired_root));
+ cups_expiredroot = expired_root;
+ }
+ else if (!cups_expiredcerts && !_cups_strcasecmp(line, "AllowExpiredCerts") &&
+ value)
+ {
+ strlcpy(expired_certs, value, sizeof(expired_certs));
+ cups_expiredcerts = expired_certs;
+ }
+#ifdef HAVE_GSSAPI
+ else if (!cups_gssservicename && !_cups_strcasecmp(line, "GSSServiceName") &&
+ value)
+ {
+ strlcpy(gss_service_name, value, sizeof(gss_service_name));
+ cups_gssservicename = gss_service_name;
+ }
+#endif /* HAVE_GSSAPI */
+ }
+
+ /*
+ * Set values...
+ */
+
+ if (cg->encryption == (http_encryption_t)-1 && cups_encryption)
+ {
+ if (!_cups_strcasecmp(cups_encryption, "never"))
+ cg->encryption = HTTP_ENCRYPTION_NEVER;
+ else if (!_cups_strcasecmp(cups_encryption, "always"))
+ cg->encryption = HTTP_ENCRYPTION_ALWAYS;
+ else if (!_cups_strcasecmp(cups_encryption, "required"))
+ cg->encryption = HTTP_ENCRYPTION_REQUIRED;
+ else
+ cg->encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+ }
+
+ if ((!cg->server[0] || !cg->ipp_port) && cups_server)
+ cupsSetServer(cups_server);
+
+ if (!cg->server[0])
+ {
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+ /*
+ * If we are compiled with domain socket support, only use the
+ * domain socket if it exists and has the right permissions...
+ */
+
+ struct stat sockinfo; /* Domain socket information */
+
+ if (!stat(CUPS_DEFAULT_DOMAINSOCKET, &sockinfo) &&
+ (sockinfo.st_mode & S_IRWXO) == S_IRWXO)
+ cups_server = CUPS_DEFAULT_DOMAINSOCKET;
+ else
+#endif /* CUPS_DEFAULT_DOMAINSOCKET */
+ cups_server = "localhost";
+
+ cupsSetServer(cups_server);
+ }
+
+ if (!cg->ipp_port)
+ {
+ const char *ipp_port; /* IPP_PORT environment variable */
+
+ if ((ipp_port = getenv("IPP_PORT")) != NULL)
+ {
+ if ((cg->ipp_port = atoi(ipp_port)) <= 0)
+ cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
+ }
+ else
+ cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
+ }
+
+ if (!cg->user[0])
+ {
+ if (cups_user)
+ strlcpy(cg->user, cups_user, sizeof(cg->user));
+ else
+ {
+#ifdef WIN32
+ /*
+ * Get the current user name from the OS...
+ */
+
+ DWORD size; /* Size of string */
+
+ size = sizeof(cg->user);
+ if (!GetUserName(cg->user, &size))
+#else
+ /*
+ * Get the user name corresponding to the current UID...
+ */
+
+ struct passwd *pwd; /* User/password entry */
+
+ setpwent();
+ if ((pwd = getpwuid(getuid())) != NULL)
+ {
+ /*
+ * Found a match!
+ */
+
+ strlcpy(cg->user, pwd->pw_name, sizeof(cg->user));
+ }
+ else
+#endif /* WIN32 */
+ {
+ /*
+ * Use the default "unknown" user name...
+ */
+
+ strlcpy(cg->user, "unknown", sizeof(cg->user));
+ }
+ }
+ }
+
+#ifdef HAVE_GSSAPI
+ if (!cups_gssservicename)
+ cups_gssservicename = CUPS_DEFAULT_GSSSERVICENAME;
+
+ strlcpy(cg->gss_service_name, cups_gssservicename,
+ sizeof(cg->gss_service_name));
+#endif /* HAVE_GSSAPI */
+
+ if (cups_anyroot)
+ cg->any_root = !_cups_strcasecmp(cups_anyroot, "yes") ||
+ !_cups_strcasecmp(cups_anyroot, "on") ||
+ !_cups_strcasecmp(cups_anyroot, "true");
+
+ if (cups_expiredroot)
+ cg->expired_root = !_cups_strcasecmp(cups_expiredroot, "yes") ||
+ !_cups_strcasecmp(cups_expiredroot, "on") ||
+ !_cups_strcasecmp(cups_expiredroot, "true");
+
+ if (cups_expiredcerts)
+ cg->expired_certs = !_cups_strcasecmp(cups_expiredcerts, "yes") ||
+ !_cups_strcasecmp(cups_expiredcerts, "on") ||
+ !_cups_strcasecmp(cups_expiredcerts, "true");
+}
+
+
+/*
+ * End of "$Id: usersys.c 11689 2014-03-05 21:22:12Z msweet $".
+ */
diff --git a/cups/libs/cups/utf8demo.txt b/cups/libs/cups/utf8demo.txt
new file mode 100644
index 000000000..03802e4d7
--- /dev/null
+++ b/cups/libs/cups/utf8demo.txt
@@ -0,0 +1,213 @@
+UTF-8 encoded sample plain-text file
+‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+Markus Kuhn [ˈmaʳkʊs kuːn] <mkuhn@acm.org> — 2002-07-25
+
+
+The ASCII compatible UTF-8 encoding used in this plain-text file
+is defined in Unicode, ISO 10646-1, and RFC 2279.
+
+
+Using Unicode/UTF-8, you can write in emails and source code things such as
+
+Mathematics and sciences:
+
+ ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫
+ ⎪⎢⎜│a²+b³ ⎟⎥⎪
+ ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪
+ ⎪⎢⎜⎷ c₈ ⎟⎥⎪
+ ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬
+ ⎪⎢⎜ ∞ ⎟⎥⎪
+ ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪
+ ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
+ 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭
+
+Linguistics and dictionaries:
+
+ ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
+ Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
+
+APL:
+
+ ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
+
+Nicer typography in plain text files:
+
+ ╔══════════════════════════════════════════╗
+ ║ ║
+ ║ • ‘single’ and “double” quotes ║
+ ║ ║
+ ║ • Curly apostrophes: “We’ve been here” ║
+ ║ ║
+ ║ • Latin-1 apostrophe and accents: '´` ║
+ ║ ║
+ ║ • ‚deutsche‘ „Anführungszeichen“ ║
+ ║ ║
+ ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║
+ ║ ║
+ ║ • ASCII safety test: 1lI|, 0OD, 8B ║
+ ║ ╭─────────╮ ║
+ ║ • the euro symbol: │ 14.95 € │ ║
+ ║ ╰─────────╯ ║
+ ╚══════════════════════════════════════════╝
+
+Combining characters:
+
+ STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑
+
+Greek (in Polytonic):
+
+ The Greek anthem:
+
+ Σὲ γνωρίζω ἀπὸ τὴν κόψη
+ τοῦ σπαθιοῦ τὴν τρομερή,
+ σὲ γνωρίζω ἀπὸ τὴν ὄψη
+ ποὺ μὲ βία μετράει τὴ γῆ.
+
+ ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
+ τῶν ῾Ελλήνων τὰ ἱερά
+ καὶ σὰν πρῶτα ἀνδρειωμένη
+ χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
+
+ From a speech of Demosthenes in the 4th century BC:
+
+ Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
+ ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
+ λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
+ τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
+ εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
+ πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
+ οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
+ οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
+ ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
+ τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
+ γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
+ προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
+ σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
+ τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
+ τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
+ τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
+
+ Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
+
+Georgian:
+
+ From a Unicode conference invitation:
+
+ გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
+ კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
+ ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
+ ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
+ ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
+ ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
+ ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
+
+Russian:
+
+ From a Unicode conference invitation:
+
+ Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
+ Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
+ Конференция соберет широкий круг экспертов по вопросам глобального
+ Интернета и Unicode, локализации и интернационализации, воплощению и
+ применению Unicode в различных операционных системах и программных
+ приложениях, шрифтах, верстке и многоязычных компьютерных системах.
+
+Thai (UCS Level 2):
+
+ Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
+ classic 'San Gua'):
+
+ [----------------------------|------------------------]
+ ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่
+ สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา
+ ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา
+ โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ
+ เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ
+ ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
+ พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้
+ ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
+
+ (The above is a two-column text. If combining characters are handled
+ correctly, the lines of the second column should be aligned with the
+ | character above.)
+
+Ethiopian:
+
+ Proverbs in the Amharic language:
+
+ ሰማይ አይታረስ ንጉሥ አይከሰስ።
+ ብላ ካለኝ እንደአባቴ በቆመጠኝ።
+ ጌጥ ያለቤቱ ቁምጥና ነው።
+ ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
+ የአፍ ወለምታ በቅቤ አይታሽም።
+ አይጥ በበላ ዳዋ ተመታ።
+ ሲተረጉሙ ይደረግሙ።
+ ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
+ ድር ቢያብር አንበሳ ያስር።
+ ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
+ እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
+ የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
+ ሥራ ከመፍታት ልጄን ላፋታት።
+ ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
+ የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
+ ተንጋሎ ቢተፉ ተመልሶ ባፉ።
+ ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
+ እግርህን በፍራሽህ ልክ ዘርጋ።
+
+Runes:
+
+ ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
+
+ (Old English, which transcribed into Latin reads 'He cwaeth that he
+ bude thaem lande northweardum with tha Westsae.' and means 'He said
+ that he lived in the northern land near the Western Sea.')
+
+Braille:
+
+ ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
+
+ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
+ ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
+ ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
+ ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
+ ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
+ ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
+
+ ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+ ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
+ ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
+ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
+ ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
+ ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
+ ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
+ ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
+ ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
+ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+ (The first couple of paragraphs of "A Christmas Carol" by Dickens)
+
+Compact font selection example text:
+
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
+ abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
+ –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
+ ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi?⑀₂ἠḂӥẄɐː⍎אԱა
+
+Greetings in various languages:
+
+ Hello world, Καλημέρα κόσμε, コンニチハ
+
+Box drawing alignment tests: █
+ ▉
+ ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
+ ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
+ ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
+ ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
+ ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
+ ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
+ ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
+ ▝▀▘▙▄▟
+
+
diff --git a/cups/libs/cups/util.c b/cups/libs/cups/util.c
new file mode 100644
index 000000000..602b6b683
--- /dev/null
+++ b/cups/libs/cups/util.c
@@ -0,0 +1,1854 @@
+/*
+ * "$Id: util.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Printing utilities for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsCancelJob() - Cancel a print job on the default server.
+ * cupsCancelJob2() - Cancel or purge a print job.
+ * cupsCreateJob() - Create an empty job for streaming.
+ * cupsFinishDocument() - Finish sending a document.
+ * cupsFreeJobs() - Free memory used by job data.
+ * cupsGetClasses() - Get a list of printer classes from the default
+ * server.
+ * cupsGetDefault() - Get the default printer or class for the default
+ * server.
+ * cupsGetDefault2() - Get the default printer or class for the specified
+ * server.
+ * cupsGetJobs() - Get the jobs from the default server.
+ * cupsGetJobs2() - Get the jobs from the specified server.
+ * cupsGetPPD() - Get the PPD file for a printer on the default
+ * server.
+ * cupsGetPPD2() - Get the PPD file for a printer from the specified
+ * server.
+ * cupsGetPPD3() - Get the PPD file for a printer on the specified
+ * server if it has changed.
+ * cupsGetPrinters() - Get a list of printers from the default server.
+ * cupsGetServerPPD() - Get an available PPD file from the server.
+ * cupsPrintFile() - Print a file to a printer or class on the default
+ * server.
+ * cupsPrintFile2() - Print a file to a printer or class on the
+ * specified server.
+ * cupsPrintFiles() - Print one or more files to a printer or class on
+ * the default server.
+ * cupsPrintFiles2() - Print one or more files to a printer or class on
+ * the specified server.
+ * cupsStartDocument() - Add a document to a job created with
+ * cupsCreateJob().
+ * cups_get_printer_uri() - Get the printer-uri-supported attribute for the
+ * first printer in a class.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups-private.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * Local functions...
+ */
+
+static int cups_get_printer_uri(http_t *http, const char *name,
+ char *host, int hostsize, int *port,
+ char *resource, int resourcesize,
+ int depth);
+
+
+/*
+ * 'cupsCancelJob()' - Cancel a print job on the default server.
+ *
+ * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
+ * to cancel the current job on the named destination.
+ *
+ * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
+ * the cause of any failure.
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsCancelJob(const char *name, /* I - Name of printer or class */
+ int job_id) /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
+{
+ return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0)
+ < IPP_STATUS_REDIRECTION_OTHER_SITE);
+}
+
+
+/*
+ * 'cupsCancelJob2()' - Cancel or purge a print job.
+ *
+ * Canceled jobs remain in the job history while purged jobs are removed
+ * from the job history.
+ *
+ * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
+ * to cancel the current job on the named destination.
+ *
+ * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
+ * the cause of any failure.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ipp_status_t /* O - IPP status */
+cupsCancelJob2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name, /* I - Name of printer or class */
+ int job_id, /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
+ int purge) /* I - 1 to purge, 0 to cancel */
+{
+ char uri[HTTP_MAX_URI]; /* Job/printer URI */
+ ipp_t *request; /* IPP request */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (job_id < -1 || (!name && job_id == 0))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Connect to the default server as needed...
+ */
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
+
+ /*
+ * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri or printer-uri + job-id
+ * requesting-user-name
+ * [purge-job] or [purge-jobs]
+ */
+
+ request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB);
+
+ if (name)
+ {
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", ippPort(), "/printers/%s", name);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
+ job_id);
+ }
+ else if (job_id > 0)
+ {
+ snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+
+ if (purge && job_id >= 0)
+ ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
+ else if (!purge && job_id < 0)
+ ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0);
+
+ /*
+ * Do the request...
+ */
+
+ ippDelete(cupsDoRequest(http, request, "/jobs/"));
+
+ return (cupsLastError());
+}
+
+
+/*
+ * 'cupsCreateJob()' - Create an empty job for streaming.
+ *
+ * Use this function when you want to stream print data using the
+ * @link cupsStartDocument@, @link cupsWriteRequestData@, and
+ * @link cupsFinishDocument@ functions. If you have one or more files to
+ * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
+ * instead.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+int /* O - Job ID or 0 on error */
+cupsCreateJob(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name, /* I - Destination name */
+ const char *title, /* I - Title of job */
+ 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 */
+
+
+ DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", "
+ "num_options=%d, options=%p)",
+ http, name, title, num_options, options));
+
+ /*
+ * Range check input...
+ */
+
+ if (!name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ /*
+ * Build a Create-Job request...
+ */
+
+ if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 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...
+ */
+
+ response = cupsDoRequest(http, request, resource);
+
+ if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
+ job_id = attr->values[0].integer;
+
+ ippDelete(response);
+
+ /*
+ * Return it...
+ */
+
+ return (job_id);
+}
+
+
+/*
+ * 'cupsFinishDocument()' - Finish sending a document.
+ *
+ * The document must have been started using @link cupsStartDocument@.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+ipp_status_t /* O - Status of document submission */
+cupsFinishDocument(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name) /* I - Destination name */
+{
+ char resource[1024]; /* Printer resource */
+
+
+ snprintf(resource, sizeof(resource), "/printers/%s", name);
+
+ ippDelete(cupsGetResponse(http, resource));
+
+ return (cupsLastError());
+}
+
+
+/*
+ * 'cupsFreeJobs()' - Free memory used by job data.
+ */
+
+void
+cupsFreeJobs(int num_jobs, /* I - Number of jobs */
+ cups_job_t *jobs) /* I - Jobs */
+{
+ int i; /* Looping var */
+ cups_job_t *job; /* Current job */
+
+
+ if (num_jobs <= 0 || !jobs)
+ return;
+
+ for (i = num_jobs, job = jobs; i > 0; i --, job ++)
+ {
+ _cupsStrFree(job->dest);
+ _cupsStrFree(job->user);
+ _cupsStrFree(job->format);
+ _cupsStrFree(job->title);
+ }
+
+ free(jobs);
+}
+
+
+/*
+ * 'cupsGetClasses()' - Get a list of printer classes from the default server.
+ *
+ * This function is deprecated - use @link cupsGetDests@ instead.
+ *
+ * @deprecated@
+ */
+
+int /* O - Number of classes */
+cupsGetClasses(char ***classes) /* O - Classes */
+{
+ int n; /* Number of classes */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ char **temp; /* Temporary pointer */
+ http_t *http; /* Connection to server */
+
+
+ if (!classes)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (0);
+ }
+
+ *classes = NULL;
+
+ if ((http = _cupsConnect()) == NULL)
+ return (0);
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNewRequest(IPP_OP_CUPS_GET_CLASSES);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", NULL, "printer-name");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ n = 0;
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ if (attr->name != NULL &&
+ _cups_strcasecmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ {
+ if (n == 0)
+ temp = malloc(sizeof(char *));
+ else
+ temp = realloc(*classes, sizeof(char *) * (n + 1));
+
+ if (temp == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ while (n > 0)
+ {
+ n --;
+ free((*classes)[n]);
+ }
+
+ free(*classes);
+ ippDelete(response);
+ return (0);
+ }
+
+ *classes = temp;
+ temp[n] = strdup(attr->values[0].string.text);
+ n ++;
+ }
+
+ ippDelete(response);
+ }
+
+ return (n);
+}
+
+
+/*
+ * 'cupsGetDefault()' - Get the default printer or class for the default server.
+ *
+ * This function returns the default printer or class as defined by
+ * the LPDEST or PRINTER environment variables. If these environment
+ * variables are not set, the server default destination is returned.
+ * 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.
+ */
+
+const char * /* O - Default printer or @code NULL@ */
+cupsGetDefault(void)
+{
+ /*
+ * Return the default printer...
+ */
+
+ return (cupsGetDefault2(CUPS_HTTP_DEFAULT));
+}
+
+
+/*
+ * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
+ *
+ * This function returns the default printer or class as defined by
+ * the LPDEST or PRINTER environment variables. If these environment
+ * variables are not set, the server default destination is returned.
+ * 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.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+const char * /* O - Default printer or @code NULL@ */
+cupsGetDefault2(http_t *http) /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * See if we have a user default printer set...
+ */
+
+ if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer)))
+ return (cg->def_printer);
+
+ /*
+ * Connect to the server as needed...
+ */
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (NULL);
+
+ /*
+ * Build a CUPS_GET_DEFAULT request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(response, "printer-name",
+ IPP_TAG_NAME)) != NULL)
+ {
+ strlcpy(cg->def_printer, attr->values[0].string.text,
+ sizeof(cg->def_printer));
+ ippDelete(response);
+ return (cg->def_printer);
+ }
+
+ ippDelete(response);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'cupsGetJobs()' - Get the jobs from the default server.
+ *
+ * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
+ * 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.
+ */
+
+int /* O - Number of jobs */
+cupsGetJobs(cups_job_t **jobs, /* O - Job data */
+ const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
+ int myjobs, /* I - 0 = all users, 1 = mine */
+ int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
+{
+ /*
+ * Return the jobs...
+ */
+
+ return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs));
+}
+
+
+
+/*
+ * 'cupsGetJobs2()' - Get the jobs from the specified server.
+ *
+ * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
+ * 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.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+int /* O - Number of jobs */
+cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ cups_job_t **jobs, /* O - Job data */
+ const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
+ int myjobs, /* I - 0 = all users, 1 = mine */
+ int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
+{
+ int n; /* Number of jobs */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_job_t *temp; /* Temporary pointer */
+ int id, /* job-id */
+ priority, /* job-priority */
+ size; /* job-k-octets */
+ ipp_jstate_t state; /* job-state */
+ time_t completed_time, /* time-at-completed */
+ creation_time, /* time-at-creation */
+ processing_time; /* time-at-processing */
+ const char *dest, /* job-printer-uri */
+ *format, /* document-format */
+ *title, /* job-name */
+ *user; /* job-originating-user-name */
+ char uri[HTTP_MAX_URI]; /* URI for jobs */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+ static const char * const attrs[] = /* Requested attributes */
+ {
+ "document-format",
+ "job-id",
+ "job-k-octets",
+ "job-name",
+ "job-originating-user-name",
+ "job-printer-uri",
+ "job-priority",
+ "job-state",
+ "time-at-completed",
+ "time-at-creation",
+ "time-at-processing"
+ };
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!jobs)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (-1);
+ }
+
+ /*
+ * Get the right URI...
+ */
+
+ if (name)
+ {
+ if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", 0, "/printers/%s",
+ name) < HTTP_URI_STATUS_OK)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("Unable to create printer-uri"), 1);
+
+ return (-1);
+ }
+ }
+ else
+ strlcpy(uri, "ipp://localhost/", sizeof(uri));
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (-1);
+
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ * which-jobs
+ * my-jobs
+ * requested-attributes
+ */
+
+ request = ippNewRequest(IPP_OP_GET_JOBS);
+
+ 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 (myjobs)
+ ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
+
+ if (whichjobs == CUPS_WHICHJOBS_COMPLETED)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "which-jobs", NULL, "completed");
+ else if (whichjobs == CUPS_WHICHJOBS_ALL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "which-jobs", NULL, "all");
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
+ NULL, attrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ n = 0;
+ *jobs = NULL;
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ for (attr = response->attrs; attr; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (!attr)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ id = 0;
+ size = 0;
+ priority = 50;
+ state = IPP_JSTATE_PENDING;
+ user = "unknown";
+ dest = NULL;
+ format = "application/octet-stream";
+ title = "untitled";
+ creation_time = 0;
+ completed_time = 0;
+ processing_time = 0;
+
+ while (attr && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (!strcmp(attr->name, "job-id") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ id = attr->values[0].integer;
+ else if (!strcmp(attr->name, "job-state") &&
+ attr->value_tag == IPP_TAG_ENUM)
+ state = (ipp_jstate_t)attr->values[0].integer;
+ else if (!strcmp(attr->name, "job-priority") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ priority = attr->values[0].integer;
+ else if (!strcmp(attr->name, "job-k-octets") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ size = attr->values[0].integer;
+ else if (!strcmp(attr->name, "time-at-completed") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ completed_time = attr->values[0].integer;
+ else if (!strcmp(attr->name, "time-at-creation") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ creation_time = attr->values[0].integer;
+ else if (!strcmp(attr->name, "time-at-processing") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ processing_time = attr->values[0].integer;
+ else if (!strcmp(attr->name, "job-printer-uri") &&
+ attr->value_tag == IPP_TAG_URI)
+ {
+ if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
+ dest ++;
+ }
+ else if (!strcmp(attr->name, "job-originating-user-name") &&
+ attr->value_tag == IPP_TAG_NAME)
+ user = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "document-format") &&
+ attr->value_tag == IPP_TAG_MIMETYPE)
+ format = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "job-name") &&
+ (attr->value_tag == IPP_TAG_TEXT ||
+ attr->value_tag == IPP_TAG_NAME))
+ title = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (!dest || !id)
+ {
+ if (!attr)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * Allocate memory for the job...
+ */
+
+ if (n == 0)
+ temp = malloc(sizeof(cups_job_t));
+ else
+ temp = realloc(*jobs, sizeof(cups_job_t) * (n + 1));
+
+ if (!temp)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+
+ cupsFreeJobs(n, *jobs);
+ *jobs = NULL;
+
+ ippDelete(response);
+
+ return (-1);
+ }
+
+ *jobs = temp;
+ temp += n;
+ n ++;
+
+ /*
+ * Copy the data over...
+ */
+
+ temp->dest = _cupsStrAlloc(dest);
+ temp->user = _cupsStrAlloc(user);
+ temp->format = _cupsStrAlloc(format);
+ temp->title = _cupsStrAlloc(title);
+ temp->id = id;
+ temp->priority = priority;
+ temp->state = state;
+ temp->size = size;
+ temp->completed_time = completed_time;
+ temp->creation_time = creation_time;
+ temp->processing_time = processing_time;
+
+ if (!attr)
+ break;
+ }
+
+ ippDelete(response);
+ }
+
+ if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST)
+ return (-1);
+ else
+ return (n);
+}
+
+
+/*
+ * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
+ *
+ * For classes, @code cupsGetPPD@ returns the PPD file for the first printer
+ * in the class.
+ *
+ * The returned filename is stored in a static buffer and is overwritten with
+ * each call to @code cupsGetPPD@ or @link cupsGetPPD2@. The caller "owns" the
+ * file that is created and must @code unlink@ the returned filename.
+ */
+
+const char * /* O - Filename for PPD file */
+cupsGetPPD(const char *name) /* I - Destination name */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+ time_t modtime = 0; /* Modification time */
+
+
+ /*
+ * Return the PPD file...
+ */
+
+ cg->ppd_filename[0] = '\0';
+
+ if (cupsGetPPD3(CUPS_HTTP_DEFAULT, name, &modtime, cg->ppd_filename,
+ sizeof(cg->ppd_filename)) == HTTP_STATUS_OK)
+ return (cg->ppd_filename);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
+ *
+ * For classes, @code cupsGetPPD2@ returns the PPD file for the first printer
+ * in the class.
+ *
+ * The returned filename is stored in a static buffer and is overwritten with
+ * each call to @link cupsGetPPD@ or @code cupsGetPPD2@. The caller "owns" the
+ * file that is created and must @code unlink@ the returned filename.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+const char * /* O - Filename for PPD file */
+cupsGetPPD2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name) /* I - Destination name */
+{
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+ time_t modtime = 0; /* Modification time */
+
+
+ cg->ppd_filename[0] = '\0';
+
+ if (cupsGetPPD3(http, name, &modtime, cg->ppd_filename,
+ sizeof(cg->ppd_filename)) == HTTP_STATUS_OK)
+ return (cg->ppd_filename);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified
+ * server if it has changed.
+ *
+ * The "modtime" parameter contains the modification time of any
+ * locally-cached content and is updated with the time from the PPD file on
+ * the server.
+ *
+ * The "buffer" parameter contains the local PPD filename. If it contains
+ * the empty string, a new temporary file is created, otherwise the existing
+ * file will be overwritten as needed. The caller "owns" the file that is
+ * created and must @code unlink@ the returned filename.
+ *
+ * On success, @code HTTP_STATUS_OK@ is returned for a new PPD file and
+ * @code HTTP_STATUS_NOT_MODIFIED@ if the existing PPD file is up-to-date. Any other
+ * status is an error.
+ *
+ * For classes, @code cupsGetPPD3@ returns the PPD file for the first printer
+ * in the class.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+http_status_t /* O - HTTP status */
+cupsGetPPD3(http_t *http, /* I - HTTP connection or @code CUPS_HTTP_DEFAULT@ */
+ const char *name, /* I - Destination name */
+ time_t *modtime, /* IO - Modification time */
+ char *buffer, /* I - Filename buffer */
+ size_t bufsize) /* I - Size of filename buffer */
+{
+ int http_port; /* Port number */
+ char http_hostname[HTTP_MAX_HOST];
+ /* Hostname associated with connection */
+ http_t *http2; /* Alternate HTTP connection */
+ int fd; /* PPD file */
+ char localhost[HTTP_MAX_URI],/* Local hostname */
+ hostname[HTTP_MAX_URI], /* Hostname */
+ resource[HTTP_MAX_URI]; /* Resource name */
+ int port; /* Port number */
+ http_status_t status; /* HTTP status from server */
+ char tempfile[1024] = ""; /* Temporary filename */
+ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, "
+ "bufsize=%d)", http, name, modtime,
+ modtime ? (int)*modtime : 0, buffer, (int)bufsize));
+
+ if (!name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer name"), 1);
+ return (HTTP_STATUS_NOT_ACCEPTABLE);
+ }
+
+ if (!modtime)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No modification time"), 1);
+ return (HTTP_STATUS_NOT_ACCEPTABLE);
+ }
+
+ if (!buffer || bufsize <= 1)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad filename buffer"), 1);
+ return (HTTP_STATUS_NOT_ACCEPTABLE);
+ }
+
+#ifndef WIN32
+ /*
+ * See if the PPD file is available locally...
+ */
+
+ if (http)
+ httpGetHostname(http, hostname, sizeof(hostname));
+ else
+ {
+ strlcpy(hostname, cupsServer(), sizeof(hostname));
+ if (hostname[0] == '/')
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ }
+
+ if (!_cups_strcasecmp(hostname, "localhost"))
+ {
+ char ppdname[1024]; /* PPD filename */
+ struct stat ppdinfo; /* PPD file information */
+
+
+ snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot,
+ name);
+ if (!stat(ppdname, &ppdinfo))
+ {
+ /*
+ * OK, the file exists, use it!
+ */
+
+ if (buffer[0])
+ {
+ unlink(buffer);
+
+ if (symlink(ppdname, buffer) && errno != EEXIST)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+
+ return (HTTP_STATUS_SERVER_ERROR);
+ }
+ }
+ else
+ {
+ int tries; /* Number of tries */
+ const char *tmpdir; /* TMPDIR environment variable */
+ struct timeval curtime; /* Current time */
+
+ /*
+ * Previously we put root temporary files in the default CUPS temporary
+ * directory under /var/spool/cups. However, since the scheduler cleans
+ * out temporary files there and runs independently of the user apps, we
+ * don't want to use it unless specifically told to by cupsd.
+ */
+
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+# ifdef __APPLE__
+ tmpdir = "/private/tmp"; /* /tmp is a symlink to /private/tmp */
+# else
+ tmpdir = "/tmp";
+# endif /* __APPLE__ */
+
+ /*
+ * Make the temporary name using the specified directory...
+ */
+
+ tries = 0;
+
+ do
+ {
+ /*
+ * Get the current time of day...
+ */
+
+ gettimeofday(&curtime, NULL);
+
+ /*
+ * Format a string using the hex time values...
+ */
+
+ snprintf(buffer, bufsize, "%s/%08lx%05lx", tmpdir,
+ (unsigned long)curtime.tv_sec,
+ (unsigned long)curtime.tv_usec);
+
+ /*
+ * Try to make a symlink...
+ */
+
+ if (!symlink(ppdname, buffer))
+ break;
+
+ tries ++;
+ }
+ while (tries < 1000);
+
+ if (tries >= 1000)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+
+ return (HTTP_STATUS_SERVER_ERROR);
+ }
+ }
+
+ if (*modtime >= ppdinfo.st_mtime)
+ return (HTTP_STATUS_NOT_MODIFIED);
+ else
+ {
+ *modtime = ppdinfo.st_mtime;
+ return (HTTP_STATUS_OK);
+ }
+ }
+ }
+#endif /* !WIN32 */
+
+ /*
+ * Try finding a printer URI for this printer...
+ */
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+
+ if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port,
+ resource, sizeof(resource), 0))
+ return (HTTP_STATUS_NOT_FOUND);
+
+ DEBUG_printf(("2cupsGetPPD3: Printer hostname=\"%s\", port=%d", hostname,
+ port));
+
+ /*
+ * Remap local hostname to localhost...
+ */
+
+ httpGetHostname(NULL, localhost, sizeof(localhost));
+
+ DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost));
+
+ if (!_cups_strcasecmp(localhost, hostname))
+ strlcpy(hostname, "localhost", sizeof(hostname));
+
+ /*
+ * Get the hostname and port number we are connected to...
+ */
+
+ httpGetHostname(http, http_hostname, sizeof(http_hostname));
+ http_port = httpAddrPort(http->hostaddr);
+
+ DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d",
+ http_hostname, http_port));
+
+ /*
+ * Reconnect to the correct server as needed...
+ */
+
+ if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port)
+ http2 = http;
+ else if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC,
+ cupsEncryption(), 1, 30000, NULL)) == NULL)
+ {
+ DEBUG_puts("1cupsGetPPD3: Unable to connect to server");
+
+ return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+ }
+
+ /*
+ * Get a temp file...
+ */
+
+ if (buffer[0])
+ fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ else
+ fd = cupsTempFd(tempfile, sizeof(tempfile));
+
+ if (fd < 0)
+ {
+ /*
+ * Can't open file; close the server connection and return NULL...
+ */
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+
+ if (http2 != http)
+ httpClose(http2);
+
+ return (HTTP_STATUS_SERVER_ERROR);
+ }
+
+ /*
+ * And send a request to the HTTP server...
+ */
+
+ strlcat(resource, ".ppd", sizeof(resource));
+
+ if (*modtime > 0)
+ httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE,
+ httpGetDateString(*modtime));
+
+ status = cupsGetFd(http2, resource, fd);
+
+ close(fd);
+
+ /*
+ * See if we actually got the file or an error...
+ */
+
+ if (status == HTTP_STATUS_OK)
+ {
+ *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE));
+
+ if (tempfile[0])
+ strlcpy(buffer, tempfile, bufsize);
+ }
+ else if (status != HTTP_STATUS_NOT_MODIFIED)
+ {
+ _cupsSetHTTPError(status);
+
+ if (buffer[0])
+ unlink(buffer);
+ else if (tempfile[0])
+ unlink(tempfile);
+ }
+ else if (tempfile[0])
+ unlink(tempfile);
+
+ if (http2 != http)
+ httpClose(http2);
+
+ /*
+ * Return the PPD file...
+ */
+
+ DEBUG_printf(("1cupsGetPPD3: Returning status %d", status));
+
+ return (status);
+}
+
+
+/*
+ * 'cupsGetPrinters()' - Get a list of printers from the default server.
+ *
+ * This function is deprecated - use @link cupsGetDests@ instead.
+ *
+ * @deprecated@
+ */
+
+int /* O - Number of printers */
+cupsGetPrinters(char ***printers) /* O - Printers */
+{
+ int n; /* Number of printers */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ char **temp; /* Temporary pointer */
+ http_t *http; /* Connection to server */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!printers)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (0);
+ }
+
+ *printers = NULL;
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = _cupsConnect()) == NULL)
+ return (0);
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", NULL, "printer-name");
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
+ "printer-type", 0);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
+ "printer-type-mask", CUPS_PRINTER_CLASS);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ n = 0;
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ if (attr->name != NULL &&
+ _cups_strcasecmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ {
+ if (n == 0)
+ temp = malloc(sizeof(char *));
+ else
+ temp = realloc(*printers, sizeof(char *) * (n + 1));
+
+ if (temp == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ while (n > 0)
+ {
+ n --;
+ free((*printers)[n]);
+ }
+
+ free(*printers);
+ ippDelete(response);
+ return (0);
+ }
+
+ *printers = temp;
+ temp[n] = strdup(attr->values[0].string.text);
+ n ++;
+ }
+
+ ippDelete(response);
+ }
+
+ return (n);
+}
+
+
+/*
+ * 'cupsGetServerPPD()' - Get an available PPD file from the server.
+ *
+ * This function returns the named PPD file from the server. The
+ * list of available PPDs is provided by the IPP @code CUPS_GET_PPDS@
+ * operation.
+ *
+ * You must remove (unlink) the PPD file when you are finished with
+ * it. The PPD filename is stored in a static location that will be
+ * overwritten on the next call to @link cupsGetPPD@, @link cupsGetPPD2@,
+ * or @link cupsGetServerPPD@.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+char * /* O - Name of PPD file or @code NULL@ on error */
+cupsGetServerPPD(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name) /* I - Name of PPD file ("ppd-name") */
+{
+ int fd; /* PPD file descriptor */
+ ipp_t *request; /* IPP request */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No PPD name"), 1);
+
+ return (NULL);
+ }
+
+ if (!http)
+ if ((http = _cupsConnect()) == NULL)
+ return (NULL);
+
+ /*
+ * Get a temp file...
+ */
+
+ if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0)
+ {
+ /*
+ * Can't open file; close the server connection and return NULL...
+ */
+
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
+
+ return (NULL);
+ }
+
+ /*
+ * Get the PPD file...
+ */
+
+ request = ippNewRequest(IPP_OP_CUPS_GET_PPD);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL,
+ name);
+
+ ippDelete(cupsDoIORequest(http, request, "/", -1, fd));
+
+ close(fd);
+
+ if (cupsLastError() != IPP_STATUS_OK)
+ {
+ unlink(cg->ppd_filename);
+ return (NULL);
+ }
+ else
+ return (cg->ppd_filename);
+}
+
+
+/*
+ * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
+ */
+
+int /* O - Job ID or 0 on error */
+cupsPrintFile(const char *name, /* I - Destination name */
+ const char *filename, /* I - File to print */
+ const char *title, /* I - Title of job */
+ int num_options,/* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", "
+ "title=\"%s\", num_options=%d, options=%p)",
+ name, filename, title, num_options, options));
+
+ return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
+ num_options, options));
+}
+
+
+/*
+ * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
+ * server.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+int /* O - Job ID or 0 on error */
+cupsPrintFile2(
+ http_t *http, /* I - Connection to server */
+ const char *name, /* I - Destination name */
+ const char *filename, /* I - File to print */
+ const char *title, /* I - Title of job */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", "
+ "title=\"%s\", num_options=%d, options=%p)",
+ http, name, filename, title, num_options, options));
+
+ return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
+ options));
+}
+
+
+/*
+ * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
+ * default server.
+ */
+
+int /* O - Job ID or 0 on error */
+cupsPrintFiles(
+ const char *name, /* I - Destination name */
+ int num_files, /* I - Number of files */
+ const char **files, /* I - File(s) to print */
+ const char *title, /* I - Title of job */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, "
+ "files=%p, title=\"%s\", num_options=%d, options=%p)",
+ name, num_files, files, title, num_options, options));
+
+ /*
+ * Print the file(s)...
+ */
+
+ return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title,
+ num_options, options));
+}
+
+
+/*
+ * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
+ * specified server.
+ *
+ * @since CUPS 1.1.21/OS X 10.4@
+ */
+
+int /* O - Job ID or 0 on error */
+cupsPrintFiles2(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name, /* I - Destination name */
+ int num_files, /* I - Number of files */
+ const char **files, /* I - File(s) to print */
+ const char *title, /* I - Title of job */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int i; /* Looping var */
+ int job_id; /* New job ID */
+ const char *docname; /* Basename of current filename */
+ const char *format; /* Document format */
+ cups_file_t *fp; /* Current file */
+ char buffer[8192]; /* Copy buffer */
+ ssize_t bytes; /* Bytes in buffer */
+ http_status_t status; /* Status of write */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+ ipp_status_t cancel_status; /* Status code to preserve */
+ char *cancel_message; /* Error message to preserve */
+
+
+ DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, "
+ "files=%p, title=\"%s\", num_options=%d, options=%p)",
+ http, name, num_files, files, title, num_options, options));
+
+ /*
+ * Range check input...
+ */
+
+ if (!name || num_files < 1 || !files)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+
+ return (0);
+ }
+
+ /*
+ * Create the print job...
+ */
+
+ if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0)
+ return (0);
+
+ /*
+ * Send each of the files...
+ */
+
+ if (cupsGetOption("raw", num_options, options))
+ format = CUPS_FORMAT_RAW;
+ else if ((format = cupsGetOption("document-format", num_options,
+ options)) == NULL)
+ format = CUPS_FORMAT_AUTO;
+
+ for (i = 0; i < num_files; i ++)
+ {
+ /*
+ * Start the next file...
+ */
+
+ if ((docname = strrchr(files[i], '/')) != NULL)
+ docname ++;
+ else
+ docname = files[i];
+
+ if ((fp = cupsFileOpen(files[i], "rb")) == NULL)
+ {
+ /*
+ * Unable to open print file, cancel the job and return...
+ */
+
+ _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0);
+ goto cancel_job;
+ }
+
+ status = cupsStartDocument(http, name, job_id, docname, format,
+ i == (num_files - 1));
+
+ while (status == HTTP_STATUS_CONTINUE &&
+ (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
+ status = cupsWriteRequestData(http, buffer, bytes);
+
+ cupsFileClose(fp);
+
+ if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK)
+ {
+ /*
+ * Unable to queue, cancel the job and return...
+ */
+
+ goto cancel_job;
+ }
+ }
+
+ return (job_id);
+
+ /*
+ * If we get here, something happened while sending the print job so we need
+ * to cancel the job without setting the last error (since we need to preserve
+ * the current error...
+ */
+
+ cancel_job:
+
+ cancel_status = cg->last_error;
+ cancel_message = cg->last_status_message ?
+ _cupsStrRetain(cg->last_status_message) : NULL;
+
+ cupsCancelJob2(http, name, job_id, 0);
+
+ cg->last_error = cancel_status;
+ cg->last_status_message = cancel_message;
+
+ return (0);
+}
+
+
+/*
+ * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob().
+ *
+ * Use @link cupsWriteRequestData@ to write data for the document and
+ * @link cupsFinishDocument@ to finish the document and get the submission status.
+ *
+ * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@,
+ * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and
+ * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
+ * any supported MIME type string can be supplied.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+http_status_t /* O - HTTP status of request */
+cupsStartDocument(
+ http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
+ const char *name, /* I - Destination name */
+ int job_id, /* I - Job ID from @link cupsCreateJob@ */
+ const char *docname, /* I - Name of document */
+ const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */
+ int last_document) /* I - 1 for last document in job, 0 otherwise */
+{
+ char resource[1024], /* Resource for destinatio */
+ printer_uri[1024]; /* Printer URI */
+ ipp_t *request; /* Send-Document request */
+ http_status_t status; /* HTTP status */
+
+
+ /*
+ * Create a Send-Document request...
+ */
+
+ if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
+ return (HTTP_STATUS_ERROR);
+ }
+
+ 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);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+ if (docname)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
+ NULL, docname);
+ if (format)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format);
+ ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", last_document);
+
+ /*
+ * Send and delete the request, then return the status...
+ */
+
+ status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE);
+
+ ippDelete(request);
+
+ return (status);
+}
+
+
+/*
+ * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the
+ * first printer in a class.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+cups_get_printer_uri(
+ http_t *http, /* I - Connection to server */
+ const char *name, /* I - Name of printer or class */
+ char *host, /* I - Hostname buffer */
+ int hostsize, /* I - Size of hostname buffer */
+ int *port, /* O - Port number */
+ char *resource, /* I - Resource buffer */
+ int resourcesize, /* I - Size of resource buffer */
+ int depth) /* I - Depth of query */
+{
+ int i; /* Looping var */
+ int http_port; /* Port number */
+ http_t *http2; /* Alternate HTTP connection */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* Current attribute */
+ char uri[HTTP_MAX_URI], /* printer-uri attribute */
+ scheme[HTTP_MAX_URI], /* Scheme name */
+ username[HTTP_MAX_URI], /* Username:password */
+ classname[255], /* Temporary class name */
+ http_hostname[HTTP_MAX_HOST];
+ /* Hostname associated with connection */
+ static const char * const requested_attrs[] =
+ { /* Requested attributes */
+ "device-uri",
+ "member-uris",
+ "printer-uri-supported",
+ "printer-type"
+ };
+
+
+ DEBUG_printf(("7cups_get_printer_uri(http=%p, name=\"%s\", host=%p, "
+ "hostsize=%d, resource=%p, resourcesize=%d, depth=%d)",
+ http, name, host, hostsize, resource, resourcesize, depth));
+
+ /*
+ * Setup the printer URI...
+ */
+
+ if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", 0, "/printers/%s",
+ name) < HTTP_URI_STATUS_OK)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer-uri"),
+ 1);
+
+ *host = '\0';
+ *resource = '\0';
+
+ return (0);
+ }
+
+ DEBUG_printf(("9cups_get_printer_uri: printer-uri=\"%s\"", uri));
+
+ /*
+ * Get the hostname and port number we are connected to...
+ */
+
+ httpGetHostname(http, http_hostname, sizeof(http_hostname));
+ http_port = httpAddrPort(http->hostaddr);
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requested-attributes
+ */
+
+ request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ sizeof(requested_attrs) / sizeof(requested_attrs[0]),
+ NULL, requested_attrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ snprintf(resource, resourcesize, "/printers/%s", name);
+
+ if ((response = cupsDoRequest(http, request, resource)) != NULL)
+ {
+ const char *device_uri = NULL; /* device-uri value */
+
+ if ((attr = ippFindAttribute(response, "device-uri",
+ IPP_TAG_URI)) != NULL)
+ device_uri = attr->values[0].string.text;
+
+ if (device_uri &&
+ (!strncmp(device_uri, "ipp://", 6) ||
+ !strncmp(device_uri, "ipps://", 7) ||
+ ((strstr(device_uri, "._ipp.") != NULL ||
+ strstr(device_uri, "._ipps.") != NULL) &&
+ !strcmp(device_uri + strlen(device_uri) - 5, "/cups"))))
+ {
+ /*
+ * Statically-configured shared printer.
+ */
+
+ httpSeparateURI(HTTP_URI_CODING_ALL,
+ _httpResolveURI(device_uri, uri, sizeof(uri),
+ _HTTP_RESOLVE_DEFAULT, NULL, NULL),
+ scheme, sizeof(scheme), username, sizeof(username),
+ host, hostsize, port, resource, resourcesize);
+ ippDelete(response);
+
+ return (1);
+ }
+ else if ((attr = ippFindAttribute(response, "member-uris",
+ IPP_TAG_URI)) != NULL)
+ {
+ /*
+ * Get the first actual printer name in the class...
+ */
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
+ scheme, sizeof(scheme), username, sizeof(username),
+ host, hostsize, port, resource, resourcesize);
+ if (!strncmp(resource, "/printers/", 10))
+ {
+ /*
+ * Found a printer!
+ */
+
+ ippDelete(response);
+
+ return (1);
+ }
+ }
+
+ /*
+ * No printers in this class - try recursively looking for a printer,
+ * but not more than 3 levels deep...
+ */
+
+ if (depth < 3)
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
+ scheme, sizeof(scheme), username, sizeof(username),
+ host, hostsize, port, resource, resourcesize);
+ if (!strncmp(resource, "/classes/", 9))
+ {
+ /*
+ * Found a class! Connect to the right server...
+ */
+
+ if (!_cups_strcasecmp(http_hostname, host) && *port == http_port)
+ http2 = http;
+ else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC,
+ cupsEncryption(), 1, 30000,
+ NULL)) == NULL)
+ {
+ DEBUG_puts("8cups_get_printer_uri: Unable to connect to server");
+
+ continue;
+ }
+
+ /*
+ * Look up printers on that server...
+ */
+
+ strlcpy(classname, resource + 9, sizeof(classname));
+
+ cups_get_printer_uri(http2, classname, host, hostsize, port,
+ resource, resourcesize, depth + 1);
+
+ /*
+ * Close the connection as needed...
+ */
+
+ if (http2 != http)
+ httpClose(http2);
+
+ if (*host)
+ return (1);
+ }
+ }
+ }
+ }
+ else if ((attr = ippFindAttribute(response, "printer-uri-supported",
+ IPP_TAG_URI)) != NULL)
+ {
+ httpSeparateURI(HTTP_URI_CODING_ALL,
+ _httpResolveURI(attr->values[0].string.text, uri,
+ sizeof(uri), _HTTP_RESOLVE_DEFAULT,
+ NULL, NULL),
+ scheme, sizeof(scheme), username, sizeof(username),
+ host, hostsize, port, resource, resourcesize);
+ ippDelete(response);
+
+ if (!strncmp(resource, "/classes/", 9))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
+ _("No printer-uri found for class"), 1);
+
+ *host = '\0';
+ *resource = '\0';
+
+ return (0);
+ }
+
+ return (1);
+ }
+
+ ippDelete(response);
+ }
+
+ if (cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found"), 1);
+
+ *host = '\0';
+ *resource = '\0';
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: util.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/cups/versioning.h b/cups/libs/cups/versioning.h
new file mode 100644
index 000000000..fe2e30bf7
--- /dev/null
+++ b/cups/libs/cups/versioning.h
@@ -0,0 +1,161 @@
+/*
+ * "$Id: versioning.h 11056 2013-06-25 14:27:30Z msweet $"
+ *
+ * API versioning definitions for CUPS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+#ifndef _CUPS_VERSIONING_H_
+# define _CUPS_VERSIONING_H_
+
+/*
+ * 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, and _CUPS_API_1_7 - which add
+ * compiler-specific attributes that flag functions that are deprecated, added
+ * in particular releases, or internal to CUPS.
+ *
+ * On OS X, 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
+ * provided by the compiler.
+ */
+
+# if defined(__APPLE__) && !defined(_CUPS_SOURCE)
+# include <AvailabilityMacros.h>
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER */
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER */
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER */
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER */
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER */
+# ifndef AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER
+# define AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER __attribute__((unavailable))
+# endif /* !AVAILABLE_MAC_OS_X_VERSION_10_9_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
+# define _CUPS_API_1_2 AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
+# define _CUPS_API_1_3 AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
+# define _CUPS_API_1_4 AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
+# define _CUPS_API_1_5 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
+# define _CUPS_API_1_6 AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER
+# define _CUPS_API_1_7 AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER
+# else
+# define _CUPS_API_1_1_19
+# define _CUPS_API_1_1_20
+# define _CUPS_API_1_1_21
+# define _CUPS_API_1_2
+# define _CUPS_API_1_3
+# define _CUPS_API_1_4
+# define _CUPS_API_1_5
+# define _CUPS_API_1_6
+# define _CUPS_API_1_7
+# endif /* __APPLE__ && !_CUPS_SOURCE */
+
+/*
+ * With GCC and Clang we can mark old APIs as "deprecated" or "unavailable" with
+ * messages so you get warnings/errors are compile-time...
+ */
+
+# ifdef __has_extension /* Clang */
+# define _CUPS_HAS_DEPRECATED
+# if __has_extension(attribute_deprecated_with_message)
+# define _CUPS_HAS_DEPRECATED_WITH_MESSAGE
+# endif
+# if __has_extension(attribute_unavailable_with_message)
+# define _CUPS_HAS_UNAVAILABLE_WITH_MESSAGE
+# endif
+# elif defined(__GNUC__) /* GCC and compatible */
+# if __GNUC__ >= 3 /* GCC 3.0 or higher */
+# define _CUPS_HAS_DEPRECATED
+# endif /* __GNUC__ >= 3 */
+# if __GNUC__ >= 5 /* GCC 5.x */
+# define _CUPS_HAS_DEPRECATED_WITH_MESSAGE
+# elif __GNUC__ == 4 && __GNUC_MINOR__ >= 5
+ /* GCC 4.5 or higher */
+# define _CUPS_HAS_DEPRECATED_WITH_MESSAGE
+# endif /* __GNUC__ >= 5 */
+# endif /* __has_extension */
+
+# if !defined(_CUPS_HAS_DEPRECATED) || (defined(_CUPS_SOURCE) && !defined(_CUPS_NO_DEPRECATED))
+ /*
+ * Don't mark functions deprecated if the compiler doesn't support it
+ * or we are building CUPS source that doesn't care.
+ */
+# define _CUPS_DEPRECATED
+# define _CUPS_DEPRECATED_MSG(m)
+# define _CUPS_DEPRECATED_1_6_MSG(m)
+# define _CUPS_DEPRECATED_1_7_MSG(m)
+# define _CUPS_INTERNAL_MSG(m)
+# elif defined(_CUPS_HAS_UNAVAILABLE_WITH_MESSAGE) && defined(_CUPS_NO_DEPRECATED)
+ /*
+ * Compiler supports the unavailable attribute, so use it when the code
+ * wants to exclude the use of deprecated API.
+ */
+# define _CUPS_DEPRECATED __attribute__ ((unavailable))
+# define _CUPS_DEPRECATED_MSG(m) __attribute__ ((unavailable(m)))
+# define _CUPS_DEPRECATED_1_6_MSG(m) __attribute__ ((unavailable(m)))
+# define _CUPS_DEPRECATED_1_7_MSG(m) __attribute__ ((unavailable(m)))
+# define _CUPS_INTERNAL_MSG(m) __attribute__ ((unavailable(m)))
+# else
+ /*
+ * Compiler supports the deprecated attribute, so use it.
+ */
+# define _CUPS_DEPRECATED __attribute__ ((deprecated))
+# ifdef _CUPS_HAS_DEPRECATED_WITH_MESSAGE
+# define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated(m)))
+# else
+# define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated))
+# endif /* _CUPS_HAS_DEPRECATED_WITH_MESSAGE */
+# if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
+# define _CUPS_DEPRECATED_1_6_MSG(m) _CUPS_DEPRECATED_MSG(m)
+# else
+# define _CUPS_DEPRECATED_1_6_MSG(m)
+# endif /* MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8 */
+# if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+# define _CUPS_DEPRECATED_1_7_MSG(m) _CUPS_DEPRECATED_MSG(m)
+# else
+# define _CUPS_DEPRECATED_1_7_MSG(m)
+# endif /* MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9 */
+# ifdef _CUPS_SOURCE
+# define _CUPS_INTERNAL_MSG(m)
+# elif defined(_CUPS_HAS_UNAVAILABLE_WITH_MESSAGE)
+# define _CUPS_INTERNAL_MSG(m) __attribute__ ((unavailable(m)))
+# elif defined(_CUPS_HAS_DEPRECATED_WITH_MESSAGE)
+# define _CUPS_INTERNAL_MSG(m) __attribute__ ((deprecated(m)))
+# else
+# define _CUPS_INTERNAL_MSG(m) __attribute__ ((deprecated))
+# endif /* _CUPS_SOURCE */
+# endif /* !_CUPS_HAS_DEPRECATED || (_CUPS_SOURCE && !_CUPS_NO_DEPRECATED) */
+
+# ifndef __GNUC__
+# define __attribute__(x)
+# endif /* !__GNUC__ */
+
+#endif /* !_CUPS_VERSIONING_H_ */
+
+/*
+ * End of "$Id: versioning.h 11056 2013-06-25 14:27:30Z msweet $".
+ */
diff --git a/cups/libs/filter/Dependencies b/cups/libs/filter/Dependencies
new file mode 100644
index 000000000..159135b85
--- /dev/null
+++ b/cups/libs/filter/Dependencies
@@ -0,0 +1,61 @@
+error.o: error.c ../cups/raster-private.h ../cups/raster.h ../cups/cups.h \
+ ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h \
+ ../cups/array.h ../cups/language.h ../cups/ppd.h \
+ ../cups/debug-private.h ../cups/string-private.h ../config.h
+interpret.o: interpret.c ../cups/raster-private.h ../cups/raster.h \
+ ../cups/cups.h ../cups/file.h ../cups/versioning.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/language.h ../cups/ppd.h \
+ ../cups/debug-private.h ../cups/string-private.h ../config.h
+raster.o: raster.c ../cups/raster-private.h ../cups/raster.h \
+ ../cups/cups.h ../cups/file.h ../cups/versioning.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/language.h ../cups/ppd.h \
+ ../cups/debug-private.h ../cups/string-private.h ../config.h
+commandtops.o: commandtops.c ../cups/cups-private.h \
+ ../cups/string-private.h ../config.h ../cups/debug-private.h \
+ ../cups/versioning.h ../cups/ipp-private.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/http-private.h \
+ ../cups/md5-private.h ../cups/language-private.h ../cups/transcode.h \
+ ../cups/language.h ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
+ ../cups/ppd-private.h ../cups/ppd.h ../cups/thread-private.h \
+ ../cups/sidechannel.h
+gziptoany.o: gziptoany.c ../cups/cups-private.h ../cups/string-private.h \
+ ../config.h ../cups/debug-private.h ../cups/versioning.h \
+ ../cups/ipp-private.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
+ ../cups/http-private.h ../cups/md5-private.h \
+ ../cups/language-private.h ../cups/transcode.h ../cups/language.h \
+ ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
+ ../cups/ppd-private.h ../cups/ppd.h ../cups/thread-private.h
+common.o: common.c common.h ../cups/string-private.h ../config.h \
+ ../cups/cups.h ../cups/file.h ../cups/versioning.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/language.h ../cups/ppd.h
+pstops.o: pstops.c common.h ../cups/string-private.h ../config.h \
+ ../cups/cups.h ../cups/file.h ../cups/versioning.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/language.h ../cups/ppd.h \
+ ../cups/language-private.h ../cups/transcode.h
+rasterbench.o: rasterbench.c ../config.h ../cups/raster.h ../cups/cups.h \
+ ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h \
+ ../cups/array.h ../cups/language.h ../cups/ppd.h
+rastertoepson.o: rastertoepson.c ../cups/cups.h ../cups/file.h \
+ ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
+ ../cups/language.h ../cups/ppd.h ../cups/string-private.h ../config.h \
+ ../cups/language-private.h ../cups/transcode.h ../cups/raster.h
+rastertohp.o: rastertohp.c ../cups/cups.h ../cups/file.h \
+ ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
+ ../cups/language.h ../cups/ppd.h ../cups/string-private.h ../config.h \
+ ../cups/language-private.h ../cups/transcode.h ../cups/raster.h
+rastertolabel.o: rastertolabel.c ../cups/cups.h ../cups/file.h \
+ ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
+ ../cups/language.h ../cups/ppd.h ../cups/string-private.h ../config.h \
+ ../cups/language-private.h ../cups/transcode.h ../cups/raster.h
+rastertopwg.o: rastertopwg.c ../cups/cups-private.h \
+ ../cups/string-private.h ../config.h ../cups/debug-private.h \
+ ../cups/versioning.h ../cups/ipp-private.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/http-private.h \
+ ../cups/md5-private.h ../cups/language-private.h ../cups/transcode.h \
+ ../cups/language.h ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
+ ../cups/ppd-private.h ../cups/ppd.h ../cups/thread-private.h \
+ ../cups/raster.h
+testraster.o: testraster.c ../cups/raster-private.h ../cups/raster.h \
+ ../cups/cups.h ../cups/file.h ../cups/versioning.h ../cups/ipp.h \
+ ../cups/http.h ../cups/array.h ../cups/language.h ../cups/ppd.h \
+ ../cups/debug-private.h ../cups/string-private.h ../config.h
diff --git a/cups/libs/filter/Makefile b/cups/libs/filter/Makefile
new file mode 100644
index 000000000..99d2ee050
--- /dev/null
+++ b/cups/libs/filter/Makefile
@@ -0,0 +1,402 @@
+#
+# "$Id: Makefile 10996 2013-05-29 11:51:34Z msweet $"
+#
+# Filter makefile for CUPS.
+#
+# Copyright 2007-2012 by Apple Inc.
+# Copyright 1997-2006 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Apple Inc. and are protected by Federal copyright
+# law. Distribution and use rights are outlined in the file "LICENSE.txt"
+# which should have been included with this file. If this file is
+# file is missing or damaged, see the license at "http://www.cups.org/".
+#
+# This file is subject to the Apple OS-Developed Software exception.
+#
+
+include ../Makedefs
+
+
+FILTERS = \
+ commandtops \
+ gziptoany \
+ pstops \
+ rastertoepson \
+ rastertohp \
+ rastertolabel \
+ rastertopwg
+LIBTARGETS = \
+ $(LIBCUPSIMAGE) \
+ libcupsimage.a
+UNITTARGETS = \
+ rasterbench \
+ testraster
+TARGETS = \
+ $(LIBTARGETS) \
+ $(FILTERS)
+
+IMAGEOBJS = error.o interpret.o raster.o
+OBJS = $(IMAGEOBJS) \
+ commandtops.o gziptoany.o common.o pstops.o \
+ rasterbench.o rastertoepson.o rastertohp.o rastertolabel.o \
+ rastertopwg.o testraster.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Make library targets...
+#
+
+libs: $(LIBTARGETS)
+
+
+#
+# Make unit tests...
+#
+
+unittests: $(UNITTARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS) $(UNITTARGETS)
+ $(RM) libcupsimage.so libcupsimage.sl libcupsimage.dylib
+
+
+#
+# Update dependencies (without system header dependencies...)
+#
+
+depend:
+ $(CC) -MM $(ALL_CFLAGS) $(OBJS:.o=.c) >Dependencies
+
+
+#
+# Install all targets...
+#
+
+install: all install-data install-headers install-libs install-exec
+
+
+#
+# Install data files...
+#
+
+install-data:
+
+
+#
+# Install programs...
+#
+
+install-exec:
+ $(INSTALL_DIR) -m 755 $(SERVERBIN)/filter
+ for file in $(FILTERS); do \
+ $(INSTALL_BIN) $$file $(SERVERBIN)/filter; \
+ done
+ $(RM) $(SERVERBIN)/filter/rastertodymo
+ $(LN) rastertolabel $(SERVERBIN)/filter/rastertodymo
+ if test "x$(SYMROOT)" != "x"; then \
+ $(INSTALL_DIR) $(SYMROOT); \
+ for file in $(FILTERS); do \
+ cp $$file $(SYMROOT); \
+ dsymutil $(SYMROOT)/$$file; \
+ done \
+ fi
+
+
+#
+# Install headers...
+#
+
+install-headers:
+
+
+#
+# Install libraries...
+#
+
+install-libs: $(INSTALLSTATIC)
+ $(INSTALL_DIR) -m 755 $(LIBDIR)
+ $(INSTALL_LIB) $(LIBCUPSIMAGE) $(LIBDIR)
+ -if test $(LIBCUPSIMAGE) = "libcupsimage.so.2" -o $(LIBCUPSIMAGE) = "libcupsimage.sl.2"; then \
+ $(RM) $(LIBDIR)/`basename $(LIBCUPSIMAGE) .2`; \
+ $(LN) $(LIBCUPSIMAGE) $(LIBDIR)/`basename $(LIBCUPSIMAGE) .2`; \
+ fi
+ -if test $(LIBCUPSIMAGE) = "libcupsimage.2.dylib"; then \
+ $(RM) $(LIBDIR)/libcupsimage.dylib; \
+ $(LN) $(LIBCUPSIMAGE) $(LIBDIR)/libcupsimage.dylib; \
+ fi
+ if test "x$(SYMROOT)" != "x"; then \
+ $(INSTALL_DIR) $(SYMROOT); \
+ cp $(LIBCUPSIMAGE) $(SYMROOT); \
+ dsymutil $(SYMROOT)/$(LIBCUPSIMAGE); \
+ fi
+
+installstatic:
+ $(INSTALL_DIR) -m 755 $(LIBDIR)
+ $(INSTALL_LIB) -m 755 libcupsimage.a $(LIBDIR)
+ $(RANLIB) $(LIBDIR)/libcupsimage.a
+ $(CHMOD) 555 $(LIBDIR)/libcupsimage.a
+
+
+#
+# Uninstall all targets...
+#
+
+uninstall:
+ for file in $(FILTERS); do \
+ $(RM) $(SERVERBIN)/filter/$$file; \
+ done
+ $(RM) $(SERVERBIN)/filter/rastertodymo
+ -$(RMDIR) $(SERVERBIN)/filter
+ -$(RMDIR) $(SERVERBIN)
+ $(RM) $(LIBDIR)/libcupsimage.2.dylib
+ $(RM) $(LIBDIR)/libcupsimage.a
+ $(RM) $(LIBDIR)/libcupsimage.dylib
+ $(RM) $(LIBDIR)/libcupsimage_s.a
+ $(RM) $(LIBDIR)/libcupsimage.sl
+ $(RM) $(LIBDIR)/libcupsimage.sl.2
+ $(RM) $(LIBDIR)/libcupsimage.so
+ $(RM) $(LIBDIR)/libcupsimage.so.2
+ -$(RMDIR) $(LIBDIR)
+
+
+#
+# Automatic API help files...
+#
+
+apihelp:
+ echo Generating CUPS API help files...
+ mxmldoc --section "Programming" --title "Raster API" \
+ --css ../doc/cups-printable.css \
+ --header api-raster.header --intro api-raster.shtml \
+ api-raster.xml \
+ ../cups/raster.h interpret.c raster.c \
+ >../doc/help/api-raster.html
+ mxmldoc --tokens help/api-raster.html api-raster.xml >../doc/help/api-raster.tokens
+ $(RM) api-raster.xml
+ mxmldoc --section "Programming" \
+ --title "Developing PostScript Printer Drivers" \
+ --css ../doc/cups-printable.css \
+ --header postscript-driver.header \
+ --intro postscript-driver.shtml \
+ >../doc/help/postscript-driver.html
+ mxmldoc --section "Programming" \
+ --title "Introduction to the PPD Compiler" \
+ --css ../doc/cups-printable.css \
+ --header ppd-compiler.header \
+ --intro ppd-compiler.shtml \
+ >../doc/help/ppd-compiler.html
+ mxmldoc --section "Programming" \
+ --title "Developing Raster Printer Drivers" \
+ --css ../doc/cups-printable.css \
+ --header raster-driver.header \
+ --intro raster-driver.shtml \
+ >../doc/help/raster-driver.html
+ mxmldoc --section "Specifications" \
+ --title "CUPS PPD Extensions" \
+ --css ../doc/cups-printable.css \
+ --header spec-ppd.header \
+ --intro spec-ppd.shtml \
+ >../doc/help/spec-ppd.html
+
+framedhelp:
+ echo Generating CUPS API help files...
+ mxmldoc --section "Programming" --title "Raster API" \
+ --framed ../cups/api-raster \
+ --css ../doc/cups-printable.css \
+ --header api-raster.header --intro api-raster.shtml \
+ ../cups/raster.h interpret.c raster.c
+ mxmldoc --section "Programming" \
+ --title "Developing PostScript Printer Drivers" \
+ --framed ../cups/postscript-driver \
+ --css ../doc/cups-printable.css \
+ --header postscript-driver.header \
+ --intro postscript-driver.shtml
+ mxmldoc --section "Programming" \
+ --title "Introduction to the PPD Compiler" \
+ --framed ../cups/ppd-compiler \
+ --css ../doc/cups-printable.css \
+ --header ppd-compiler.header \
+ --intro ppd-compiler.shtml
+ mxmldoc --section "Programming" \
+ --title "Developing Raster Printer Drivers" \
+ --framed ../cups/raster-driver \
+ --css ../doc/cups-printable.css \
+ --header raster-driver.header \
+ --intro raster-driver.shtml
+ mxmldoc --section "Specifications" \
+ --title "CUPS PPD Extensions" \
+ --framed ../cups/spec-ppd \
+ --css ../doc/cups-printable.css \
+ --header spec-ppd.header \
+ --intro spec-ppd.shtml \
+
+
+#
+# commandtops
+#
+
+commandtops: commandtops.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ commandtops.o $(LIBS)
+
+
+#
+# gziptoany
+#
+
+gziptoany: gziptoany.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ gziptoany.o $(LIBZ) $(LIBS)
+
+
+#
+# libcupsimage.so.2, libcupsimage.sl.2
+#
+
+libcupsimage.so.2 libcupsimage.sl.2: $(IMAGEOBJS)
+ echo Linking $@...
+ $(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(IMAGEOBJS) $(DSOLIBS) \
+ -L../cups $(LINKCUPS)
+ $(RM) `basename $@ .2`
+ $(LN) $@ `basename $@ .2`
+
+
+#
+# libcupsimage.2.dylib
+#
+
+libcupsimage.2.dylib: $(IMAGEOBJS) $(LIBCUPSIMAGEORDER)
+ echo Linking $@...
+ $(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ \
+ -install_name $(libdir)/$@ \
+ -current_version 2.3.0 \
+ -compatibility_version 2.0.0 \
+ $(IMAGEOBJS) $(DSOLIBS) -L../cups $(LINKCUPS)
+ $(RM) libcupsimage.dylib
+ $(LN) $@ libcupsimage.dylib
+
+
+#
+# libcupsimage_s.a
+#
+
+libcupsimage_s.a: $(IMAGEOBJS) libcupsimage_s.exp
+ echo Linking $@...
+ $(DSO) $(DSOFLAGS) -Wl,-berok,-bexport:libcupsimage_s.exp \
+ -o libcupsimage_s.o $(IMAGEOBJS) $(DSOLIBS)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ libcupsimage_s.o
+
+
+#
+# libcupsimage.la
+#
+
+libcupsimage.la: $(IMAGEOBJS)
+ echo Linking $@...
+ $(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(IMAGEOBJS:.o=.lo) $(DSOLIBS) \
+ -L../cups $(LINKCUPS) \
+ -rpath $(LIBDIR) -version-info 2:3
+
+
+#
+# libcupsimage.a
+#
+
+libcupsimage.a: $(IMAGEOBJS)
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(IMAGEOBJS)
+ $(RANLIB) $@
+
+
+#
+# pstops
+#
+
+pstops: pstops.o common.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
+
+
+#
+# rastertoepson
+#
+
+rastertoepson: rastertoepson.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertoepson.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+
+
+#
+# rastertohp
+#
+
+rastertohp: rastertohp.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertohp.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+
+
+#
+# rastertolabel
+#
+
+rastertolabel: rastertolabel.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertolabel.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+
+
+#
+# rastertopwg
+#
+
+rastertopwg: rastertopwg.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertopwg.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+
+
+#
+# testraster
+#
+
+testraster: testraster.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testraster.o libcupsimage.a \
+ ../cups/$(LIBCUPSSTATIC) $(IMGLIBS) $(DSOLIBS) $(COMMONLIBS) \
+ $(SSLLIBS) $(DNSSDLIBS) $(LIBGSSAPI)
+ echo Running raster API tests...
+ ./testraster
+
+
+#
+# rasterbench
+#
+
+rasterbench: rasterbench.o libcupsimage.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rasterbench.o libcupsimage.a $(LIBS)
+
+
+#
+# Dependencies...
+#
+
+include Dependencies
+
+
+#
+# End of "$Id: Makefile 10996 2013-05-29 11:51:34Z msweet $".
+#
diff --git a/cups/libs/filter/api-raster.header b/cups/libs/filter/api-raster.header
new file mode 100644
index 000000000..42c11d1ed
--- /dev/null
+++ b/cups/libs/filter/api-raster.header
@@ -0,0 +1,37 @@
+<!--
+ "$Id$"
+
+ Raster API documentation for CUPS.
+
+ Copyright 2008-2010 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'>Raster API</h1>
+
+<div class='summary'><table summary='General Information'>
+<thead>
+<tr>
+ <th>Header</th>
+ <th>cups/raster.h</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <th>Library</th>
+ <td>-lcupsimage</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>
+ Programming: <a href='api-cups.html'>PPD API</a><br>
+ References: <a href='spec-ppd.html'>CUPS PPD Specification</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/filter/api-raster.shtml b/cups/libs/filter/api-raster.shtml
new file mode 100644
index 000000000..cb137d525
--- /dev/null
+++ b/cups/libs/filter/api-raster.shtml
@@ -0,0 +1,160 @@
+<!--
+ "$Id$"
+
+ Raster 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 raster API provides a standard interface for reading and writing
+CUPS raster streams which are used for printing to raster printers. Because the
+raster format is updated from time to time, it is important to use this API to
+avoid incompatibilities with newer versions of CUPS.</p>
+
+<p>Two kinds of CUPS filters use the CUPS raster API - raster image processor
+(RIP) filters such as <code>pstoraster</code> and <code>cgpdftoraster</code>
+(OS X) that produce CUPS raster files and printer driver filters that
+convert CUPS raster files into a format usable by the printer. Printer
+driver filters are by far the most common.</p>
+
+<p>CUPS raster files (<code>application/vnd.cups-raster</code>) consists of
+a stream of raster page descriptions produced by one of the RIP filters such as
+<var>pstoraster</var>, <var>imagetoraster</var>, or
+<var>cgpdftoraster</var>. CUPS raster files are referred to using the
+<a href='#cups_raster_t'><code>cups_raster_t</code></a> type and are
+opened using the <a href='#cupsRasterOpen'><code>cupsRasterOpen</code></a>
+function. For example, to read raster data from the standard input, open
+file descriptor 0:</p>
+
+<pre class="example">
+#include &lt;cups/raster.h&gt;>
+
+<a href="#cups_raster_t">cups_raster_t</a> *ras = <a href="#cupsRasterOpen">cupsRasterOpen</a>(0, CUPS_RASTER_READ);
+</pre>
+
+<p>Each page of data begins with a page dictionary structure called
+<a href="#cups_page_header2_t"><code>cups_page_header2_t</code></a>. This
+structure contains the colorspace, bits per color, media size, media type,
+hardware resolution, and so forth used for the page.</p>
+
+<blockquote><b>Note:</b>
+
+ <p>Do not confuse the colorspace in the page header with the PPD
+ <tt>ColorModel</tt> keyword. <tt>ColorModel</tt> refers to the general type of
+ color used for a device (Gray, RGB, CMYK, DeviceN) and is often used to
+ select a particular colorspace for the page header along with the associate
+ color profile. The page header colorspace (<tt>cupsColorSpace</tt>) describes
+ both the type and organization of the color data, for example KCMY (black
+ first) instead of CMYK and RGBA (RGB + alpha) instead of RGB.</p>
+
+</blockquote>
+
+<p>You read the page header using the
+<a href="#cupsRasterReadHeader2"><code>cupsRasterReadHeader2</code></a>
+function:</p>
+
+<pre class="example">
+#include &lt;cups/raster.h&gt;>
+
+<a href="#cups_raster_t">cups_raster_t</a> *ras = <a href="#cupsRasterOpen">cupsRasterOpen</a>(0, CUPS_RASTER_READ);
+<a href="#cups_page_header2_t">cups_page_header2_t</a> header;
+
+while (<a href="#cupsRasterReadHeader2">cupsRasterReadHeader2</a>(ras, &amp;header))
+{
+ /* setup this page */
+
+ /* read raster data */
+
+ /* finish this page */
+}
+</pre>
+
+<p>After the page dictionary comes the page data which is a full-resolution,
+possibly compressed bitmap representing the page in the printer's output
+colorspace. You read uncompressed raster data using the
+<a href="#cupsRasterReadPixels"><code>cupsRasterReadPixels</code></a>
+function. A <code>for</code> loop is normally used to read the page one line
+at a time:</p>
+
+<pre class="example">
+#include &lt;cups/raster.h&gt;>
+
+<a href="#cups_raster_t">cups_raster_t</a> *ras = <a href="#cupsRasterOpen">cupsRasterOpen</a>(0, CUPS_RASTER_READ);
+<a href="#cups_page_header2_t">cups_page_header2_t</a> header;
+int page = 0;
+int y;
+char *buffer;
+
+while (<a href="#cupsRasterReadHeader2">cupsRasterReadHeader2</a>(ras, &amp;header))
+{
+ /* setup this page */
+ page ++;
+ fprintf(stderr, "PAGE: %d %d\n", page, header.NumCopies);
+
+ /* allocate memory for 1 line */
+ buffer = malloc(header.cupsBytesPerLine);
+
+ /* read raster data */
+ for (y = 0; y &lt; header.cupsHeight; y ++)
+ {
+ if (<a href="#cupsRasterReadPixels">cupsRasterReadPixels</a>(ras, buffer, header.cupsBytesPerLine) == 0)
+ break;
+
+ /* write raster data to printer on stdout */
+ }
+
+ /* finish this page */
+}
+</pre>
+
+<p>When you are done reading the raster data, call the
+<a href="#cupsRasterClose"><code>cupsRasterClose</code></a> function to free
+the memory used to read the raster file:</p>
+
+<pre class="example">
+<a href="#cups_raster_t">cups_raster_t</a> *ras;
+
+<a href="#cupsRasterClose">cupsRasterClose</a>(ras);
+</pre>
+
+
+<h2 class='title'><a name="TASKS">Functions by Task</a></h2>
+
+<h3><a name="OPENCLOSE">Opening and Closing Raster Streams</a></h3>
+
+<ul class="code">
+
+ <li><a href="#cupsRasterClose" title="Close a raster stream.">cupsRasterClose</a></li>
+ <li><a href="#cupsRasterOpen" title="Open a raster stream.">cupsRasterOpen</a></li>
+
+</ul>
+
+<h3><a name="READING">Reading Raster Streams</a></h3>
+
+<ul class="code">
+
+ <li><a href="#cupsRasterReadHeader" title="Read a raster page header and store it in a version 1 page header structure.">cupsRasterReadHeader</a> <span class="info">Deprecated in CUPS 1.2/OS X 10.5</span></li>
+ <li><a href="#cupsRasterReadHeader2" title="Read a raster page header and store it in a version 2 page header structure.">cupsRasterReadHeader2</a></li>
+ <li><a href="#cupsRasterReadPixels" title="Read raster pixels.">cupsRasterReadPixels</a></li>
+
+</ul>
+
+<h3><a name="WRITING">Writing Raster Streams</a></h3>
+
+<ul class="code">
+
+ <li><a href="#cupsRasterInterpretPPD" title="Interpret PPD commands to create a page header.">cupsRasterInterpretPPD</a></li>
+ <li><a href="#cupsRasterWriteHeader" title="Write a raster page header from a version 1 page header structure.">cupsRasterWriteHeader</a> <span class="info">Deprecated in CUPS 1.2/OS X 10.5</span></li>
+ <li><a href="#cupsRasterWriteHeader2" title="Write a raster page header from a version 2 page header structure.">cupsRasterWriteHeader2</a></li>
+ <li><a href="#cupsRasterWritePixels" title="Write raster pixels.">cupsRasterWritePixels</a></li>
+
+</ul>
diff --git a/cups/libs/filter/commandtops.c b/cups/libs/filter/commandtops.c
new file mode 100644
index 000000000..a75809c24
--- /dev/null
+++ b/cups/libs/filter/commandtops.c
@@ -0,0 +1,538 @@
+/*
+ * "$Id: commandtops.c 3794 2012-04-23 22:44:16Z msweet $"
+ *
+ * PostScript command filter for CUPS.
+ *
+ * Copyright 2008-2012 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ *
+ * Contents:
+ *
+ * main() - Process a CUPS command file.
+ * auto_configure() - Automatically configure the printer using
+ * PostScript query commands and/or SNMP lookups.
+ * begin_ps() - Send the standard PostScript prolog.
+ * end_ps() - Send the standard PostScript trailer.
+ * print_self_test_page() - Print a self-test page.
+ * report_levels() - Report supply levels.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups-private.h>
+#include <cups/ppd.h>
+#include <cups/sidechannel.h>
+
+
+/*
+ * Local functions...
+ */
+
+static int auto_configure(ppd_file_t *ppd, const char *user);
+static void begin_ps(ppd_file_t *ppd, const char *user);
+static void end_ps(ppd_file_t *ppd);
+static void print_self_test_page(ppd_file_t *ppd, const char *user);
+static void report_levels(ppd_file_t *ppd, const char *user);
+
+
+/*
+ * 'main()' - Process a CUPS command file.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int status = 0; /* Exit status */
+ cups_file_t *fp; /* Command file */
+ char line[1024], /* Line from file */
+ *value; /* Value on line */
+ int linenum; /* Line number in file */
+ ppd_file_t *ppd; /* PPD file */
+
+
+ /*
+ * Check for valid arguments...
+ */
+
+ if (argc < 6 || argc > 7)
+ {
+ /*
+ * We don't have the correct number of arguments; write an error message
+ * and return.
+ */
+
+ _cupsLangPrintf(stderr,
+ _("Usage: %s job-id user title copies options [file]"),
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * Open the PPD file...
+ */
+
+ if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL)
+ {
+ fputs("ERROR: Unable to open PPD file!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Open the command file as needed...
+ */
+
+ if (argc == 7)
+ {
+ if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
+ {
+ perror("ERROR: Unable to open command file - ");
+ return (1);
+ }
+ }
+ else
+ fp = cupsFileStdin();
+
+ /*
+ * Read the commands from the file and send the appropriate commands...
+ */
+
+ linenum = 0;
+
+ while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ {
+ /*
+ * Parse the command...
+ */
+
+ if (!_cups_strcasecmp(line, "AutoConfigure"))
+ status |= auto_configure(ppd, argv[2]);
+ else if (!_cups_strcasecmp(line, "PrintSelfTestPage"))
+ print_self_test_page(ppd, argv[2]);
+ else if (!_cups_strcasecmp(line, "ReportLevels"))
+ report_levels(ppd, argv[2]);
+ else
+ {
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Invalid printer command \"%s\"."), line);
+ status = 1;
+ }
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'auto_configure()' - Automatically configure the printer using PostScript
+ * query commands and/or SNMP lookups.
+ */
+
+static int /* O - Exit status */
+auto_configure(ppd_file_t *ppd, /* I - PPD file */
+ const char *user) /* I - Printing user */
+{
+ int status = 0; /* Exit status */
+ ppd_option_t *option; /* Current option in PPD */
+ ppd_attr_t *attr; /* Query command attribute */
+ const char *valptr; /* Pointer into attribute value */
+ char buffer[1024], /* String buffer */
+ *bufptr; /* Pointer into buffer */
+ ssize_t bytes; /* Number of bytes read */
+ int datalen; /* Side-channel data length */
+
+
+ /*
+ * See if the backend supports bidirectional I/O...
+ */
+
+ datalen = 1;
+ if (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, buffer, &datalen,
+ 30.0) != CUPS_SC_STATUS_OK ||
+ buffer[0] != CUPS_SC_BIDI_SUPPORTED)
+ {
+ fputs("DEBUG: Unable to auto-configure PostScript Printer - no "
+ "bidirectional I/O available!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Put the printer in PostScript mode...
+ */
+
+ begin_ps(ppd, user);
+
+ /*
+ * (STR #4028)
+ *
+ * As a lot of PPDs contain bad PostScript query code, we need to prevent one
+ * bad query sequence from affecting all auto-configuration. The following
+ * error handler allows us to log PostScript errors to cupsd.
+ */
+
+ puts("/cups_handleerror {\n"
+ " $error /newerror false put\n"
+ " (:PostScript error in \") print cups_query_keyword print (\": ) "
+ "print\n"
+ " $error /errorname get 128 string cvs print\n"
+ " (; offending command:) print $error /command get 128 string cvs "
+ "print (\n) print flush\n"
+ "} bind def\n"
+ "errordict /timeout {} put\n"
+ "/cups_query_keyword (?Unknown) def\n");
+ fflush(stdout);
+
+ /*
+ * Wait for the printer to become connected...
+ */
+
+ do
+ {
+ sleep(1);
+ datalen = 1;
+ }
+ while (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_CONNECTED, buffer, &datalen,
+ 5.0) == CUPS_SC_STATUS_OK && !buffer[0]);
+
+ /*
+ * Then loop through every option in the PPD file and ask for the current
+ * value...
+ */
+
+ fputs("DEBUG: Auto-configuring PostScript printer...\n", stderr);
+
+ for (option = ppdFirstOption(ppd); option; option = ppdNextOption(ppd))
+ {
+ /*
+ * See if we have a query command for this option...
+ */
+
+ snprintf(buffer, sizeof(buffer), "?%s", option->keyword);
+
+ if ((attr = ppdFindAttr(ppd, buffer, NULL)) == NULL || !attr->value)
+ {
+ fprintf(stderr, "DEBUG: Skipping %s option...\n", option->keyword);
+ continue;
+ }
+
+ /*
+ * Send the query code to the printer...
+ */
+
+ fprintf(stderr, "DEBUG: Querying %s...\n", option->keyword);
+
+ for (bufptr = buffer, valptr = attr->value; *valptr; valptr ++)
+ {
+ /*
+ * Log the query code, breaking at newlines...
+ */
+
+ if (*valptr == '\n')
+ {
+ *bufptr = '\0';
+ fprintf(stderr, "DEBUG: %s\\n\n", buffer);
+ bufptr = buffer;
+ }
+ else if (*valptr < ' ')
+ {
+ if (bufptr >= (buffer + sizeof(buffer) - 4))
+ {
+ *bufptr = '\0';
+ fprintf(stderr, "DEBUG: %s\n", buffer);
+ bufptr = buffer;
+ }
+
+ if (*valptr == '\r')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = 'r';
+ }
+ else if (*valptr == '\t')
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = 't';
+ }
+ else
+ {
+ *bufptr++ = '\\';
+ *bufptr++ = '0' + ((*valptr / 64) & 7);
+ *bufptr++ = '0' + ((*valptr / 8) & 7);
+ *bufptr++ = '0' + (*valptr & 7);
+ }
+ }
+ else
+ {
+ if (bufptr >= (buffer + sizeof(buffer) - 1))
+ {
+ *bufptr = '\0';
+ fprintf(stderr, "DEBUG: %s\n", buffer);
+ bufptr = buffer;
+ }
+
+ *bufptr++ = *valptr;
+ }
+ }
+
+ if (bufptr > buffer)
+ {
+ *bufptr = '\0';
+ fprintf(stderr, "DEBUG: %s\n", buffer);
+ }
+
+ printf("/cups_query_keyword (?%s) def\n", option->keyword);
+ /* Set keyword for error reporting */
+ fputs("{ (", stdout);
+ for (valptr = attr->value; *valptr; valptr ++)
+ {
+ if (*valptr == '(' || *valptr == ')' || *valptr == '\\')
+ putchar('\\');
+ putchar(*valptr);
+ }
+ fputs(") cvx exec } stopped { cups_handleerror } if clear\n", stdout);
+ /* Send query code */
+ fflush(stdout);
+
+ datalen = 0;
+ cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer, &datalen, 5.0);
+
+ /*
+ * Read the response data...
+ */
+
+ bufptr = buffer;
+ buffer[0] = '\0';
+ while ((bytes = cupsBackChannelRead(bufptr,
+ sizeof(buffer) - (bufptr - buffer) - 1,
+ 10.0)) > 0)
+ {
+ /*
+ * No newline at the end? Go on reading ...
+ */
+
+ bufptr += bytes;
+ *bufptr = '\0';
+
+ if (bytes == 0 ||
+ (bufptr > buffer && bufptr[-1] != '\r' && bufptr[-1] != '\n'))
+ continue;
+
+ /*
+ * Trim whitespace and control characters from both ends...
+ */
+
+ bytes = bufptr - buffer;
+
+ for (bufptr --; bufptr >= buffer; bufptr --)
+ if (isspace(*bufptr & 255) || iscntrl(*bufptr & 255))
+ *bufptr = '\0';
+ else
+ break;
+
+ for (bufptr = buffer; isspace(*bufptr & 255) || iscntrl(*bufptr & 255);
+ bufptr ++);
+
+ if (bufptr > buffer)
+ {
+ _cups_strcpy(buffer, bufptr);
+ bufptr = buffer;
+ }
+
+ fprintf(stderr, "DEBUG: Got %d bytes.\n", (int)bytes);
+
+ /*
+ * Skip blank lines...
+ */
+
+ if (!buffer[0])
+ continue;
+
+ /*
+ * Check the response...
+ */
+
+ if ((bufptr = strchr(buffer, ':')) != NULL)
+ {
+ /*
+ * PostScript code for this option in the PPD is broken; show the
+ * interpreter's error message that came back...
+ */
+
+ fprintf(stderr, "DEBUG%s\n", bufptr);
+ break;
+ }
+
+ /*
+ * Verify the result is a valid option choice...
+ */
+
+ if (!ppdFindChoice(option, buffer))
+ {
+ if (!strcasecmp(buffer, "Unknown"))
+ break;
+
+ bufptr = buffer;
+ buffer[0] = '\0';
+ continue;
+ }
+
+ /*
+ * Write out the result and move on to the next option...
+ */
+
+ fprintf(stderr, "PPD: Default%s=%s\n", option->keyword, buffer);
+ break;
+ }
+
+ /*
+ * Printer did not answer this option's query
+ */
+
+ if (bytes <= 0)
+ {
+ fprintf(stderr,
+ "DEBUG: No answer to query for option %s within 10 seconds.\n",
+ option->keyword);
+ status = 1;
+ }
+ }
+
+ /*
+ * Finish the job...
+ */
+
+ fflush(stdout);
+ end_ps(ppd);
+
+ /*
+ * Return...
+ */
+
+ if (status)
+ _cupsLangPrintFilter(stderr, "WARNING",
+ _("Unable to configure printer options."));
+
+ return (0);
+}
+
+
+/*
+ * 'begin_ps()' - Send the standard PostScript prolog.
+ */
+
+static void
+begin_ps(ppd_file_t *ppd, /* I - PPD file */
+ const char *user) /* I - Username */
+{
+ (void)user;
+
+ if (ppd->jcl_begin)
+ {
+ fputs(ppd->jcl_begin, stdout);
+ fputs(ppd->jcl_ps, stdout);
+ }
+
+ puts("%!");
+ puts("userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
+
+ fflush(stdout);
+}
+
+
+/*
+ * 'end_ps()' - Send the standard PostScript trailer.
+ */
+
+static void
+end_ps(ppd_file_t *ppd) /* I - PPD file */
+{
+ if (ppd->jcl_end)
+ fputs(ppd->jcl_end, stdout);
+ else
+ putchar(0x04);
+
+ fflush(stdout);
+}
+
+
+/*
+ * 'print_self_test_page()' - Print a self-test page.
+ */
+
+static void
+print_self_test_page(ppd_file_t *ppd, /* I - PPD file */
+ const char *user) /* I - Printing user */
+{
+ /*
+ * Put the printer in PostScript mode...
+ */
+
+ begin_ps(ppd, user);
+
+ /*
+ * Send a simple file the draws a box around the imageable area and shows
+ * the product/interpreter information...
+ */
+
+ puts("\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
+ "%%%%%%%%%%%%%\n"
+ "\r%%%% If you can read this, you are using the wrong driver for your "
+ "printer. %%%%\n"
+ "\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
+ "%%%%%%%%%%%%%\n"
+ "0 setgray\n"
+ "2 setlinewidth\n"
+ "initclip newpath clippath gsave stroke grestore pathbbox\n"
+ "exch pop exch pop exch 9 add exch 9 sub moveto\n"
+ "/Courier findfont 12 scalefont setfont\n"
+ "0 -12 rmoveto gsave product show grestore\n"
+ "0 -12 rmoveto gsave version show ( ) show revision 20 string cvs show "
+ "grestore\n"
+ "0 -12 rmoveto gsave serialnumber 20 string cvs show grestore\n"
+ "showpage");
+
+ /*
+ * Finish the job...
+ */
+
+ end_ps(ppd);
+}
+
+
+/*
+ * 'report_levels()' - Report supply levels.
+ */
+
+static void
+report_levels(ppd_file_t *ppd, /* I - PPD file */
+ const char *user) /* I - Printing user */
+{
+ /*
+ * Put the printer in PostScript mode...
+ */
+
+ begin_ps(ppd, user);
+
+ /*
+ * Don't bother sending any additional PostScript commands, since we just
+ * want the backend to have enough time to collect the supply info.
+ */
+
+ /*
+ * Finish the job...
+ */
+
+ end_ps(ppd);
+}
+
+
+/*
+ * End of "$Id: commandtops.c 3794 2012-04-23 22:44:16Z msweet $".
+ */
diff --git a/cups/libs/filter/common.c b/cups/libs/filter/common.c
new file mode 100644
index 000000000..2d52e781c
--- /dev/null
+++ b/cups/libs/filter/common.c
@@ -0,0 +1,535 @@
+/*
+ * "$Id: common.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Common filter routines for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * SetCommonOptions() - Set common filter options for media size,
+ * etc.
+ * UpdatePageVars() - Update the page variables for the orientation.
+ * WriteComment() - Write a DSC comment.
+ * WriteCommon() - Write common procedures...
+ * WriteLabelProlog() - Write the prolog with the classification
+ * and page label.
+ * WriteLabels() - Write the actual page labels.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+#include <locale.h>
+
+
+/*
+ * Globals...
+ */
+
+int Orientation = 0, /* 0 = portrait, 1 = landscape, etc. */
+ Duplex = 0, /* Duplexed? */
+ LanguageLevel = 1, /* Language level of printer */
+ ColorDevice = 1; /* Do color text? */
+float PageLeft = 18.0f, /* Left margin */
+ PageRight = 594.0f, /* Right margin */
+ PageBottom = 36.0f, /* Bottom margin */
+ PageTop = 756.0f, /* Top margin */
+ PageWidth = 612.0f, /* Total page width */
+ PageLength = 792.0f; /* Total page length */
+
+
+/*
+ * 'SetCommonOptions()' - Set common filter options for media size, etc.
+ */
+
+ppd_file_t * /* O - PPD file */
+SetCommonOptions(
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ int change_size) /* I - Change page size? */
+{
+ ppd_file_t *ppd; /* PPD file */
+ ppd_size_t *pagesize; /* Current page size */
+ const char *val; /* Option value */
+
+
+#ifdef LC_TIME
+ setlocale(LC_TIME, "");
+#endif /* LC_TIME */
+
+ ppd = ppdOpenFile(getenv("PPD"));
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ if ((pagesize = ppdPageSize(ppd, NULL)) != NULL)
+ {
+ PageWidth = pagesize->width;
+ PageLength = pagesize->length;
+ PageTop = pagesize->top;
+ PageBottom = pagesize->bottom;
+ PageLeft = pagesize->left;
+ PageRight = pagesize->right;
+
+ fprintf(stderr, "DEBUG: Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f\n",
+ PageWidth, PageLength, PageLeft, PageBottom, PageRight, PageTop);
+ }
+
+ if (ppd != NULL)
+ {
+ ColorDevice = ppd->color_device;
+ LanguageLevel = ppd->language_level;
+ }
+
+ if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
+ {
+ if (_cups_strcasecmp(val, "no") != 0 && _cups_strcasecmp(val, "off") != 0 &&
+ _cups_strcasecmp(val, "false") != 0)
+ {
+ if (ppd && ppd->landscape > 0)
+ Orientation = 1;
+ else
+ Orientation = 3;
+ }
+ }
+ else if ((val = cupsGetOption("orientation-requested", num_options, options)) != NULL)
+ {
+ /*
+ * Map IPP orientation values to 0 to 3:
+ *
+ * 3 = 0 degrees = 0
+ * 4 = 90 degrees = 1
+ * 5 = -90 degrees = 3
+ * 6 = 180 degrees = 2
+ */
+
+ Orientation = atoi(val) - 3;
+ if (Orientation >= 2)
+ Orientation ^= 1;
+ }
+
+ if ((val = cupsGetOption("page-left", num_options, options)) != NULL)
+ {
+ switch (Orientation & 3)
+ {
+ case 0 :
+ PageLeft = (float)atof(val);
+ break;
+ case 1 :
+ PageBottom = (float)atof(val);
+ break;
+ case 2 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ case 3 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ }
+ }
+
+ if ((val = cupsGetOption("page-right", num_options, options)) != NULL)
+ {
+ switch (Orientation & 3)
+ {
+ case 0 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ case 1 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ case 2 :
+ PageLeft = (float)atof(val);
+ break;
+ case 3 :
+ PageBottom = (float)atof(val);
+ break;
+ }
+ }
+
+ if ((val = cupsGetOption("page-bottom", num_options, options)) != NULL)
+ {
+ switch (Orientation & 3)
+ {
+ case 0 :
+ PageBottom = (float)atof(val);
+ break;
+ case 1 :
+ PageLeft = (float)atof(val);
+ break;
+ case 2 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ case 3 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ }
+ }
+
+ if ((val = cupsGetOption("page-top", num_options, options)) != NULL)
+ {
+ switch (Orientation & 3)
+ {
+ case 0 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ case 1 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ case 2 :
+ PageBottom = (float)atof(val);
+ break;
+ case 3 :
+ PageLeft = (float)atof(val);
+ break;
+ }
+ }
+
+ if (change_size)
+ UpdatePageVars();
+
+ if (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble") ||
+ ppdIsMarked(ppd, "Duplex", "DuplexTumble") ||
+ ppdIsMarked(ppd, "JCLDuplex", "DuplexNoTumble") ||
+ ppdIsMarked(ppd, "JCLDuplex", "DuplexTumble") ||
+ ppdIsMarked(ppd, "EFDuplex", "DuplexNoTumble") ||
+ ppdIsMarked(ppd, "EFDuplex", "DuplexTumble") ||
+ ppdIsMarked(ppd, "KD03Duplex", "DuplexNoTumble") ||
+ ppdIsMarked(ppd, "KD03Duplex", "DuplexTumble"))
+ Duplex = 1;
+
+ return (ppd);
+}
+
+
+/*
+ * 'UpdatePageVars()' - Update the page variables for the orientation.
+ */
+
+void
+UpdatePageVars(void)
+{
+ float temp; /* Swapping variable */
+
+
+ switch (Orientation & 3)
+ {
+ case 0 : /* Portait */
+ break;
+
+ case 1 : /* Landscape */
+ temp = PageLeft;
+ PageLeft = PageBottom;
+ PageBottom = temp;
+
+ temp = PageRight;
+ PageRight = PageTop;
+ PageTop = temp;
+
+ temp = PageWidth;
+ PageWidth = PageLength;
+ PageLength = temp;
+ break;
+
+ case 2 : /* Reverse Portrait */
+ temp = PageWidth - PageLeft;
+ PageLeft = PageWidth - PageRight;
+ PageRight = temp;
+
+ temp = PageLength - PageBottom;
+ PageBottom = PageLength - PageTop;
+ PageTop = temp;
+ break;
+
+ case 3 : /* Reverse Landscape */
+ temp = PageWidth - PageLeft;
+ PageLeft = PageWidth - PageRight;
+ PageRight = temp;
+
+ temp = PageLength - PageBottom;
+ PageBottom = PageLength - PageTop;
+ PageTop = temp;
+
+ temp = PageLeft;
+ PageLeft = PageBottom;
+ PageBottom = temp;
+
+ temp = PageRight;
+ PageRight = PageTop;
+ PageTop = temp;
+
+ temp = PageWidth;
+ PageWidth = PageLength;
+ PageLength = temp;
+ break;
+ }
+}
+
+
+/*
+ * 'WriteCommon()' - Write common procedures...
+ */
+
+void
+WriteCommon(void)
+{
+ puts("% x y w h ESPrc - Clip to a rectangle.\n"
+ "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
+ "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ "neg 0 rlineto closepath clip newpath}bind}ifelse put");
+ puts("% x y w h ESPrf - Fill a rectangle.\n"
+ "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
+ "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ "neg 0 rlineto closepath fill grestore}bind}ifelse put");
+ puts("% x y w h ESPrs - Stroke a rectangle.\n"
+ "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
+ "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ "neg 0 rlineto closepath stroke grestore}bind}ifelse put");
+}
+
+
+/*
+ * 'WriteLabelProlog()' - Write the prolog with the classification
+ * and page label.
+ */
+
+void
+WriteLabelProlog(const char *label, /* I - Page label */
+ float bottom, /* I - Bottom position in points */
+ float top, /* I - Top position in points */
+ float width) /* I - Width in points */
+{
+ const char *classification; /* CLASSIFICATION environment variable */
+ const char *ptr; /* Temporary string pointer */
+
+
+ /*
+ * First get the current classification...
+ */
+
+ if ((classification = getenv("CLASSIFICATION")) == NULL)
+ classification = "";
+ if (strcmp(classification, "none") == 0)
+ classification = "";
+
+ /*
+ * If there is nothing to show, bind an empty 'write labels' procedure
+ * and return...
+ */
+
+ if (!classification[0] && (label == NULL || !label[0]))
+ {
+ puts("userdict/ESPwl{}bind put");
+ return;
+ }
+
+ /*
+ * Set the classification + page label string...
+ */
+
+ printf("userdict");
+ if (strcmp(classification, "confidential") == 0)
+ printf("/ESPpl(CONFIDENTIAL");
+ else if (strcmp(classification, "classified") == 0)
+ printf("/ESPpl(CLASSIFIED");
+ else if (strcmp(classification, "secret") == 0)
+ printf("/ESPpl(SECRET");
+ else if (strcmp(classification, "topsecret") == 0)
+ printf("/ESPpl(TOP SECRET");
+ else if (strcmp(classification, "unclassified") == 0)
+ printf("/ESPpl(UNCLASSIFIED");
+ else
+ {
+ printf("/ESPpl(");
+
+ for (ptr = classification; *ptr; ptr ++)
+ if (*ptr < 32 || *ptr > 126)
+ printf("\\%03o", *ptr);
+ else if (*ptr == '_')
+ putchar(' ');
+ else
+ {
+ if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
+ putchar('\\');
+
+ putchar(*ptr);
+ }
+ }
+
+ if (label)
+ {
+ if (classification[0])
+ printf(" - ");
+
+ /*
+ * Quote the label string as needed...
+ */
+
+ for (ptr = label; *ptr; ptr ++)
+ if (*ptr < 32 || *ptr > 126)
+ printf("\\%03o", *ptr);
+ else
+ {
+ if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
+ putchar('\\');
+
+ putchar(*ptr);
+ }
+ }
+
+ puts(")put");
+
+ /*
+ * Then get a 14 point Helvetica-Bold font...
+ */
+
+ puts("userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put");
+
+ /*
+ * Finally, the procedure to write the labels on the page...
+ */
+
+ puts("userdict/ESPwl{");
+ puts(" ESPpf setfont");
+ printf(" ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
+ width * 0.5f);
+ puts(" 1 setgray");
+ printf(" dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
+ printf(" dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
+ puts(" 0 setgray");
+ printf(" dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
+ printf(" dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
+ printf(" dup %.0f moveto ESPpl show\n", bottom + 2.0);
+ printf(" %.0f moveto ESPpl show\n", top - 14.0);
+ puts("pop");
+ puts("}bind put");
+}
+
+
+/*
+ * 'WriteLabels()' - Write the actual page labels.
+ */
+
+void
+WriteLabels(int orient) /* I - Orientation of the page */
+{
+ float width, /* Width of page */
+ length; /* Length of page */
+
+
+ puts("gsave");
+
+ if ((orient ^ Orientation) & 1)
+ {
+ width = PageLength;
+ length = PageWidth;
+ }
+ else
+ {
+ width = PageWidth;
+ length = PageLength;
+ }
+
+ switch (orient & 3)
+ {
+ case 1 : /* Landscape */
+ printf("%.1f 0.0 translate 90 rotate\n", length);
+ break;
+ case 2 : /* Reverse Portrait */
+ printf("%.1f %.1f translate 180 rotate\n", width, length);
+ break;
+ case 3 : /* Reverse Landscape */
+ printf("0.0 %.1f translate -90 rotate\n", width);
+ break;
+ }
+
+ puts("ESPwl");
+ puts("grestore");
+}
+
+
+/*
+ * 'WriteTextComment()' - Write a DSC text comment.
+ */
+
+void
+WriteTextComment(const char *name, /* I - Comment name ("Title", etc.) */
+ const char *value) /* I - Comment value */
+{
+ int len; /* Current line length */
+
+
+ /*
+ * DSC comments are of the form:
+ *
+ * %%name: value
+ *
+ * The name and value must be limited to 7-bit ASCII for most printers,
+ * so we escape all non-ASCII and ASCII control characters as described
+ * in the Adobe Document Structuring Conventions specification.
+ */
+
+ printf("%%%%%s: (", name);
+ len = 5 + strlen(name);
+
+ while (*value)
+ {
+ if (*value < ' ' || *value >= 127)
+ {
+ /*
+ * Escape this character value...
+ */
+
+ if (len >= 251) /* Keep line < 254 chars */
+ break;
+
+ printf("\\%03o", *value & 255);
+ len += 4;
+ }
+ else if (*value == '\\')
+ {
+ /*
+ * Escape the backslash...
+ */
+
+ if (len >= 253) /* Keep line < 254 chars */
+ break;
+
+ putchar('\\');
+ putchar('\\');
+ len += 2;
+ }
+ else
+ {
+ /*
+ * Put this character literally...
+ */
+
+ if (len >= 254) /* Keep line < 254 chars */
+ break;
+
+ putchar(*value);
+ len ++;
+ }
+
+ value ++;
+ }
+
+ puts(")");
+}
+
+
+/*
+ * End of "$Id: common.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/common.h b/cups/libs/filter/common.h
new file mode 100644
index 000000000..9693ce8d3
--- /dev/null
+++ b/cups/libs/filter/common.h
@@ -0,0 +1,78 @@
+/*
+ * "$Id: common.h 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Common filter definitions for CUPS.
+ *
+ * Copyright 2007-2010 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/string-private.h>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include <time.h>
+
+
+/*
+ * C++ magic...
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*
+ * Globals...
+ */
+
+extern int Orientation, /* 0 = portrait, 1 = landscape, etc. */
+ Duplex, /* Duplexed? */
+ LanguageLevel, /* Language level of printer */
+ ColorDevice; /* Do color text? */
+extern float PageLeft, /* Left margin */
+ PageRight, /* Right margin */
+ PageBottom, /* Bottom margin */
+ PageTop, /* Top margin */
+ PageWidth, /* Total page width */
+ PageLength; /* Total page length */
+
+
+/*
+ * Prototypes...
+ */
+
+extern ppd_file_t *SetCommonOptions(int num_options, cups_option_t *options,
+ int change_size);
+extern void UpdatePageVars(void);
+extern void WriteCommon(void);
+extern void WriteLabelProlog(const char *label, float bottom,
+ float top, float width);
+extern void WriteLabels(int orient);
+extern void WriteTextComment(const char *name, const char *value);
+
+
+/*
+ * C++ magic...
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+/*
+ * End of "$Id: common.h 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/error.c b/cups/libs/filter/error.c
new file mode 100644
index 000000000..994cfb86d
--- /dev/null
+++ b/cups/libs/filter/error.c
@@ -0,0 +1,286 @@
+/*
+ * "$Id: error.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Raster error handling for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * _cupsRasterAddError() - Add an error message to the error buffer.
+ * _cupsRasterClearError() - Clear the error buffer.
+ * cupsRasterErrorString() - Return the last error from a raster function.
+ * get_error_buffer() - Return a pointer to thread local storage.
+ * raster_init() - Initialize error buffer once.
+ * raster_destructor() - Free memory allocated by get_error_buffer().
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/raster-private.h>
+
+
+/*
+ * Local structures...
+ */
+
+typedef struct _cups_raster_error_s /**** Error buffer structure ****/
+{
+ char *start, /* Start of buffer */
+ *current, /* Current position in buffer */
+ *end; /* End of buffer */
+} _cups_raster_error_t;
+
+
+/*
+ * Local functions...
+ */
+
+static _cups_raster_error_t *get_error_buffer(void);
+
+
+/*
+ * '_cupsRasterAddError()' - Add an error message to the error buffer.
+ */
+
+void
+_cupsRasterAddError(const char *f, /* I - Printf-style error message */
+ ...) /* I - Additional arguments as needed */
+{
+ _cups_raster_error_t *buf = get_error_buffer();
+ /* Error buffer */
+ va_list ap; /* Pointer to additional arguments */
+ char s[2048]; /* Message string */
+ size_t bytes; /* Bytes in message string */
+
+
+ va_start(ap, f);
+ bytes = vsnprintf(s, sizeof(s), f, ap);
+ va_end(ap);
+
+ if (bytes <= 0)
+ return;
+
+ bytes ++;
+
+ if (bytes >= sizeof(s))
+ return;
+
+ if (bytes > (size_t)(buf->end - buf->current))
+ {
+ /*
+ * Allocate more memory...
+ */
+
+ char *temp; /* New buffer */
+ size_t size; /* Size of buffer */
+
+
+ size = buf->end - buf->start + 2 * bytes + 1024;
+
+ if (buf->start)
+ temp = realloc(buf->start, size);
+ else
+ temp = malloc(size);
+
+ if (!temp)
+ return;
+
+ /*
+ * Update pointers...
+ */
+
+ buf->end = temp + size;
+ buf->current = temp + (buf->current - buf->start);
+ buf->start = temp;
+ }
+
+ /*
+ * Append the message to the end of the current string...
+ */
+
+ memcpy(buf->current, s, bytes);
+ buf->current += bytes - 1;
+}
+
+
+/*
+ * '_cupsRasterClearError()' - Clear the error buffer.
+ */
+
+void
+_cupsRasterClearError(void)
+{
+ _cups_raster_error_t *buf = get_error_buffer();
+ /* Error buffer */
+
+
+ buf->current = buf->start;
+
+ if (buf->start)
+ *(buf->start) = '\0';
+}
+
+
+/*
+ * 'cupsRasterErrorString()' - Return the last error from a raster function.
+ *
+ * If there are no recent errors, NULL is returned.
+ *
+ * @since CUPS 1.3/OS X 10.5@
+ */
+
+const char * /* O - Last error */
+cupsRasterErrorString(void)
+{
+ _cups_raster_error_t *buf = get_error_buffer();
+ /* Error buffer */
+
+
+ if (buf->current == buf->start)
+ return (NULL);
+ else
+ return (buf->start);
+}
+
+
+#ifdef HAVE_PTHREAD_H
+/*
+ * Implement per-thread globals...
+ */
+
+# include <pthread.h>
+
+
+/*
+ * Local globals...
+ */
+
+static pthread_key_t raster_key = -1;
+ /* Thread local storage key */
+static pthread_once_t raster_key_once = PTHREAD_ONCE_INIT;
+ /* One-time initialization object */
+
+
+/*
+ * Local functions...
+ */
+
+static void raster_init(void);
+static void raster_destructor(void *value);
+
+
+/*
+ * 'get_error_buffer()' - Return a pointer to thread local storage.
+ */
+
+_cups_raster_error_t * /* O - Pointer to error buffer */
+get_error_buffer(void)
+{
+ _cups_raster_error_t *buf; /* Pointer to error buffer */
+
+
+ /*
+ * Initialize the global data exactly once...
+ */
+
+ DEBUG_puts("get_error_buffer()");
+
+ pthread_once(&raster_key_once, raster_init);
+
+ /*
+ * See if we have allocated the data yet...
+ */
+
+ if ((buf = (_cups_raster_error_t *)pthread_getspecific(raster_key))
+ == NULL)
+ {
+ DEBUG_puts("get_error_buffer: allocating memory for thread...");
+
+ /*
+ * No, allocate memory as set the pointer for the key...
+ */
+
+ buf = calloc(1, sizeof(_cups_raster_error_t));
+ pthread_setspecific(raster_key, buf);
+
+ DEBUG_printf((" buf=%p\n", buf));
+ }
+
+ /*
+ * Return the pointer to the data...
+ */
+
+ return (buf);
+}
+
+
+/*
+ * 'raster_init()' - Initialize error buffer once.
+ */
+
+static void
+raster_init(void)
+{
+ pthread_key_create(&raster_key, raster_destructor);
+
+ DEBUG_printf(("raster_init(): raster_key=%x(%u)\n", (unsigned)raster_key,
+ (unsigned)raster_key));
+}
+
+
+/*
+ * 'raster_destructor()' - Free memory allocated by get_error_buffer().
+ */
+
+static void
+raster_destructor(void *value) /* I - Data to free */
+{
+ _cups_raster_error_t *buf = (_cups_raster_error_t *)value;
+ /* Error buffer */
+
+
+ DEBUG_printf(("raster_destructor(value=%p)\n", value));
+
+ if (buf->start)
+ free(buf->start);
+
+ free(value);
+}
+
+
+#else
+/*
+ * Implement static globals...
+ */
+
+/*
+ * 'get_error_buffer()' - Return a pointer to thread local storage.
+ */
+
+_cups_raster_error_t * /* O - Pointer to error buffer */
+get_error_buffer(void)
+{
+ static _cups_raster_error_t buf = { 0, 0, 0 };
+ /* Error buffer */
+
+
+ return (&buf);
+}
+#endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * End of "$Id: error.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/gziptoany.c b/cups/libs/filter/gziptoany.c
new file mode 100644
index 000000000..81a3f17d8
--- /dev/null
+++ b/cups/libs/filter/gziptoany.c
@@ -0,0 +1,112 @@
+/*
+ * "$Id: gziptoany.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * GZIP/raw pre-filter for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1993-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Copy (and uncompress) files to stdout.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups-private.h>
+
+
+/*
+ * 'main()' - Copy (and uncompress) files to stdout.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ cups_file_t *fp; /* File */
+ char buffer[8192]; /* Data buffer */
+ int bytes; /* Number of bytes read/written */
+ int copies; /* Number of copies */
+
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc != 7)
+ {
+ _cupsLangPrintf(stderr,
+ _("Usage: %s job-id user title copies options [file]"),
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * Get the copy count; if we have no final content type, this is a
+ * raw queue or raw print file, so we need to make copies...
+ */
+
+ if (!getenv("FINAL_CONTENT_TYPE"))
+ copies = atoi(argv[4]);
+ else
+ copies = 1;
+
+ /*
+ * Open the file...
+ */
+
+ if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
+ {
+ _cupsLangPrintError("ERROR", _("Unable to open print file"));
+ return (1);
+ }
+
+ /*
+ * Copy the file to stdout...
+ */
+
+ while (copies > 0)
+ {
+ if (!getenv("FINAL_CONTENT_TYPE"))
+ fputs("PAGE: 1 1\n", stderr);
+
+ cupsFileRewind(fp);
+
+ while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
+ if (write(1, buffer, bytes) < bytes)
+ {
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to write uncompressed print data: %s"),
+ strerror(errno));
+ cupsFileClose(fp);
+
+ return (1);
+ }
+
+ copies --;
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ cupsFileClose(fp);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: gziptoany.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/interpret.c b/cups/libs/filter/interpret.c
new file mode 100644
index 000000000..03ed6658a
--- /dev/null
+++ b/cups/libs/filter/interpret.c
@@ -0,0 +1,1690 @@
+/*
+ * "$Id: interpret.c 11551 2014-01-29 16:31:35Z msweet $"
+ *
+ * PPD command interpreter for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1993-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsRasterInterpretPPD() - Interpret PPD commands to create a page header.
+ * _cupsRasterExecPS() - Execute PostScript code to initialize a page
+ * header.
+ * cleartomark_stack() - Clear to the last mark ([) on the stack.
+ * copy_stack() - Copy the top N stack objects.
+ * delete_stack() - Free memory used by a stack.
+ * error_object() - Add an object's value to the current error
+ * message.
+ * error_stack() - Add a stack to the current error message.
+ * index_stack() - Copy the Nth value on the stack.
+ * new_stack() - Create a new stack.
+ * pop_stock() - Pop the top object off the stack.
+ * push_stack() - Push an object on the stack.
+ * roll_stack() - Rotate stack objects.
+ * scan_ps() - Scan a string for the next PS object.
+ * setpagedevice() - Simulate the PostScript setpagedevice operator.
+ * DEBUG_object() - Print an object value.
+ * DEBUG_stack() - Print a stack.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/raster-private.h>
+
+
+/*
+ * Stack values for the PostScript mini-interpreter...
+ */
+
+typedef enum
+{
+ CUPS_PS_NAME,
+ CUPS_PS_NUMBER,
+ CUPS_PS_STRING,
+ CUPS_PS_BOOLEAN,
+ CUPS_PS_NULL,
+ CUPS_PS_START_ARRAY,
+ CUPS_PS_END_ARRAY,
+ CUPS_PS_START_DICT,
+ CUPS_PS_END_DICT,
+ CUPS_PS_START_PROC,
+ CUPS_PS_END_PROC,
+ CUPS_PS_CLEARTOMARK,
+ CUPS_PS_COPY,
+ CUPS_PS_DUP,
+ CUPS_PS_INDEX,
+ CUPS_PS_POP,
+ CUPS_PS_ROLL,
+ CUPS_PS_SETPAGEDEVICE,
+ CUPS_PS_STOPPED,
+ CUPS_PS_OTHER
+} _cups_ps_type_t;
+
+typedef struct
+{
+ _cups_ps_type_t type; /* Object type */
+ union
+ {
+ int boolean; /* Boolean value */
+ char name[64]; /* Name value */
+ double number; /* Number value */
+ char other[64]; /* Other operator */
+ char string[64]; /* Sring value */
+ } value; /* Value */
+} _cups_ps_obj_t;
+
+typedef struct
+{
+ int num_objs, /* Number of objects on stack */
+ alloc_objs; /* Number of allocated objects */
+ _cups_ps_obj_t *objs; /* Objects in stack */
+} _cups_ps_stack_t;
+
+
+/*
+ * Local functions...
+ */
+
+static int cleartomark_stack(_cups_ps_stack_t *st);
+static int copy_stack(_cups_ps_stack_t *st, int count);
+static void delete_stack(_cups_ps_stack_t *st);
+static void error_object(_cups_ps_obj_t *obj);
+static void error_stack(_cups_ps_stack_t *st, const char *title);
+static _cups_ps_obj_t *index_stack(_cups_ps_stack_t *st, int n);
+static _cups_ps_stack_t *new_stack(void);
+static _cups_ps_obj_t *pop_stack(_cups_ps_stack_t *st);
+static _cups_ps_obj_t *push_stack(_cups_ps_stack_t *st,
+ _cups_ps_obj_t *obj);
+static int roll_stack(_cups_ps_stack_t *st, int c, int s);
+static _cups_ps_obj_t *scan_ps(_cups_ps_stack_t *st, char **ptr);
+static int setpagedevice(_cups_ps_stack_t *st,
+ cups_page_header2_t *h,
+ int *preferred_bits);
+#ifdef DEBUG
+static void DEBUG_object(_cups_ps_obj_t *obj);
+static void DEBUG_stack(_cups_ps_stack_t *st);
+#endif /* DEBUG */
+
+
+/*
+ * 'cupsRasterInterpretPPD()' - Interpret PPD commands to create a page header.
+ *
+ * This function is used by raster image processing (RIP) filters like
+ * cgpdftoraster and imagetoraster when writing CUPS raster data for a page.
+ * It is not used by raster printer driver filters which only read CUPS
+ * raster data.
+ *
+ *
+ * @code cupsRasterInterpretPPD@ does not mark the options in the PPD using
+ * the "num_options" and "options" arguments. Instead, mark the options with
+ * @code cupsMarkOptions@ and @code ppdMarkOption@ prior to calling it -
+ * this allows for per-page options without manipulating the options array.
+ *
+ * The "func" argument specifies an optional callback function that is
+ * called prior to the computation of the final raster data. The function
+ * can make changes to the @link cups_page_header2_t@ data as needed to use a
+ * supported raster format and then returns 0 on success and -1 if the
+ * requested attributes cannot be supported.
+ *
+ *
+ * @code cupsRasterInterpretPPD@ supports a subset of the PostScript language.
+ * Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@,
+ * @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@,
+ * @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators
+ * are supported.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+int /* O - 0 on success, -1 on failure */
+cupsRasterInterpretPPD(
+ cups_page_header2_t *h, /* O - Page header to create */
+ ppd_file_t *ppd, /* I - PPD file */
+ int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ cups_interpret_cb_t func) /* I - Optional page header callback (@code NULL@ for none) */
+{
+ int status; /* Cummulative status */
+ char *code; /* Code to run */
+ const char *val; /* Option value */
+ ppd_size_t *size; /* Current size */
+ float left, /* Left position */
+ bottom, /* Bottom position */
+ right, /* Right position */
+ top; /* Top position */
+ int preferred_bits; /* Preferred bits per color */
+
+
+ /*
+ * Range check input...
+ */
+
+ _cupsRasterClearError();
+
+ if (!h)
+ {
+ _cupsRasterAddError("Page header cannot be NULL!\n");
+ return (-1);
+ }
+
+ /*
+ * Reset the page header to the defaults...
+ */
+
+ memset(h, 0, sizeof(cups_page_header2_t));
+
+ h->NumCopies = 1;
+ h->PageSize[0] = 612;
+ h->PageSize[1] = 792;
+ h->HWResolution[0] = 100;
+ h->HWResolution[1] = 100;
+ h->cupsBitsPerColor = 1;
+ h->cupsColorOrder = CUPS_ORDER_CHUNKED;
+ h->cupsColorSpace = CUPS_CSPACE_K;
+ h->cupsBorderlessScalingFactor = 1.0f;
+ h->cupsPageSize[0] = 612.0f;
+ h->cupsPageSize[1] = 792.0f;
+ h->cupsImagingBBox[0] = 0.0f;
+ h->cupsImagingBBox[1] = 0.0f;
+ h->cupsImagingBBox[2] = 612.0f;
+ h->cupsImagingBBox[3] = 792.0f;
+
+ strlcpy(h->cupsPageSizeName, "Letter", sizeof(h->cupsPageSizeName));
+
+#ifdef __APPLE__
+ /*
+ * cupsInteger0 is also used for the total page count on OS X; set an
+ * uncommon default value so we can tell if the driver is using cupsInteger0.
+ */
+
+ h->cupsInteger[0] = 0x80000000;
+#endif /* __APPLE__ */
+
+ /*
+ * Apply patches and options to the page header...
+ */
+
+ status = 0;
+ preferred_bits = 0;
+
+ if (ppd)
+ {
+ /*
+ * Apply any patch code (used to override the defaults...)
+ */
+
+ if (ppd->patches)
+ status |= _cupsRasterExecPS(h, &preferred_bits, ppd->patches);
+
+ /*
+ * Then apply printer options in the proper order...
+ */
+
+ if ((code = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
+ {
+ status |= _cupsRasterExecPS(h, &preferred_bits, code);
+ free(code);
+ }
+
+ if ((code = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
+ {
+ status |= _cupsRasterExecPS(h, &preferred_bits, code);
+ free(code);
+ }
+
+ if ((code = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
+ {
+ status |= _cupsRasterExecPS(h, &preferred_bits, code);
+ free(code);
+ }
+
+ if ((code = ppdEmitString(ppd, PPD_ORDER_PAGE, 0.0)) != NULL)
+ {
+ status |= _cupsRasterExecPS(h, &preferred_bits, code);
+ free(code);
+ }
+ }
+
+ /*
+ * Allow option override for page scaling...
+ */
+
+ if ((val = cupsGetOption("cupsBorderlessScalingFactor", num_options,
+ options)) != NULL)
+ {
+ double sc = atof(val); /* Scale factor */
+
+ if (sc >= 0.1 && sc <= 2.0)
+ h->cupsBorderlessScalingFactor = (float)sc;
+ }
+
+ /*
+ * Get the margins for the current size...
+ */
+
+ if ((size = ppdPageSize(ppd, NULL)) != NULL)
+ {
+ /*
+ * Use the margins from the PPD file...
+ */
+
+ left = size->left;
+ bottom = size->bottom;
+ right = size->right;
+ top = size->top;
+
+ strlcpy(h->cupsPageSizeName, size->name, sizeof(h->cupsPageSizeName));
+
+ h->cupsPageSize[0] = size->width;
+ h->cupsPageSize[1] = size->length;
+ }
+ else
+ {
+ /*
+ * Use the default margins...
+ */
+
+ left = 0.0f;
+ bottom = 0.0f;
+ right = 612.0f;
+ top = 792.0f;
+ }
+
+ h->PageSize[0] = (unsigned)(h->cupsPageSize[0] *
+ h->cupsBorderlessScalingFactor);
+ h->PageSize[1] = (unsigned)(h->cupsPageSize[1] *
+ h->cupsBorderlessScalingFactor);
+ h->Margins[0] = (unsigned)(left *
+ h->cupsBorderlessScalingFactor);
+ h->Margins[1] = (unsigned)(bottom *
+ h->cupsBorderlessScalingFactor);
+ h->ImagingBoundingBox[0] = (unsigned)(left *
+ h->cupsBorderlessScalingFactor);
+ h->ImagingBoundingBox[1] = (unsigned)(bottom *
+ h->cupsBorderlessScalingFactor);
+ h->ImagingBoundingBox[2] = (unsigned)(right *
+ h->cupsBorderlessScalingFactor);
+ h->ImagingBoundingBox[3] = (unsigned)(top *
+ h->cupsBorderlessScalingFactor);
+ h->cupsImagingBBox[0] = (float)left;
+ h->cupsImagingBBox[1] = (float)bottom;
+ h->cupsImagingBBox[2] = (float)right;
+ h->cupsImagingBBox[3] = (float)top;
+
+ /*
+ * Use the callback to validate the page header...
+ */
+
+ if (func && (*func)(h, preferred_bits))
+ {
+ _cupsRasterAddError("Page header callback returned error.\n");
+ return (-1);
+ }
+
+ /*
+ * Check parameters...
+ */
+
+ if (!h->HWResolution[0] || !h->HWResolution[1] ||
+ !h->PageSize[0] || !h->PageSize[1] ||
+ (h->cupsBitsPerColor != 1 && h->cupsBitsPerColor != 2 &&
+ h->cupsBitsPerColor != 4 && h->cupsBitsPerColor != 8 &&
+ h->cupsBitsPerColor != 16) ||
+ h->cupsBorderlessScalingFactor < 0.1 ||
+ h->cupsBorderlessScalingFactor > 2.0)
+ {
+ _cupsRasterAddError("Page header uses unsupported values.\n");
+ return (-1);
+ }
+
+ /*
+ * Compute the bitmap parameters...
+ */
+
+ h->cupsWidth = (int)((right - left) * h->cupsBorderlessScalingFactor *
+ h->HWResolution[0] / 72.0f + 0.5f);
+ h->cupsHeight = (int)((top - bottom) * h->cupsBorderlessScalingFactor *
+ h->HWResolution[1] / 72.0f + 0.5f);
+
+ switch (h->cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ case CUPS_CSPACE_SW :
+ h->cupsNumColors = 1;
+ h->cupsBitsPerPixel = h->cupsBitsPerColor;
+ break;
+
+ default :
+ /*
+ * Ensure that colorimetric colorspaces use at least 8 bits per
+ * component...
+ */
+
+ if (h->cupsColorSpace >= CUPS_CSPACE_CIEXYZ &&
+ h->cupsBitsPerColor < 8)
+ h->cupsBitsPerColor = 8;
+
+ /*
+ * Figure out the number of bits per pixel...
+ */
+
+ if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
+ {
+ if (h->cupsBitsPerColor >= 8)
+ h->cupsBitsPerPixel = h->cupsBitsPerColor * 3;
+ else
+ h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
+ }
+ else
+ h->cupsBitsPerPixel = h->cupsBitsPerColor;
+
+ h->cupsNumColors = 3;
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (h->cupsBitsPerColor == 1)
+ {
+ if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
+ h->cupsBitsPerPixel = 8;
+ else
+ h->cupsBitsPerPixel = 1;
+
+ h->cupsNumColors = 6;
+ break;
+ }
+
+ /*
+ * Fall through to CMYK code...
+ */
+
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
+ h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
+ else
+ h->cupsBitsPerPixel = h->cupsBitsPerColor;
+
+ h->cupsNumColors = 4;
+ break;
+
+ case CUPS_CSPACE_DEVICE1 :
+ case CUPS_CSPACE_DEVICE2 :
+ case CUPS_CSPACE_DEVICE3 :
+ case CUPS_CSPACE_DEVICE4 :
+ case CUPS_CSPACE_DEVICE5 :
+ case CUPS_CSPACE_DEVICE6 :
+ case CUPS_CSPACE_DEVICE7 :
+ case CUPS_CSPACE_DEVICE8 :
+ case CUPS_CSPACE_DEVICE9 :
+ case CUPS_CSPACE_DEVICEA :
+ case CUPS_CSPACE_DEVICEB :
+ case CUPS_CSPACE_DEVICEC :
+ case CUPS_CSPACE_DEVICED :
+ case CUPS_CSPACE_DEVICEE :
+ case CUPS_CSPACE_DEVICEF :
+ h->cupsNumColors = h->cupsColorSpace - CUPS_CSPACE_DEVICE1 + 1;
+
+ if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
+ h->cupsBitsPerPixel = h->cupsBitsPerColor * h->cupsNumColors;
+ else
+ h->cupsBitsPerPixel = h->cupsBitsPerColor;
+ break;
+ }
+
+ h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8;
+
+ if (h->cupsColorOrder == CUPS_ORDER_BANDED)
+ h->cupsBytesPerLine *= h->cupsNumColors;
+
+ return (status);
+}
+
+
+/*
+ * '_cupsRasterExecPS()' - Execute PostScript code to initialize a page header.
+ */
+
+int /* O - 0 on success, -1 on error */
+_cupsRasterExecPS(
+ cups_page_header2_t *h, /* O - Page header */
+ int *preferred_bits,/* O - Preferred bits per color */
+ const char *code) /* I - PS code to execute */
+{
+ int error = 0; /* Error condition? */
+ _cups_ps_stack_t *st; /* PostScript value stack */
+ _cups_ps_obj_t *obj; /* Object from top of stack */
+ char *codecopy, /* Copy of code */
+ *codeptr; /* Pointer into copy of code */
+
+
+ DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
+ h, preferred_bits, code));
+
+ /*
+ * Copy the PostScript code and create a stack...
+ */
+
+ if ((codecopy = strdup(code)) == NULL)
+ {
+ _cupsRasterAddError("Unable to duplicate code string.\n");
+ return (-1);
+ }
+
+ if ((st = new_stack()) == NULL)
+ {
+ _cupsRasterAddError("Unable to create stack.\n");
+ free(codecopy);
+ return (-1);
+ }
+
+ /*
+ * Parse the PS string until we run out of data...
+ */
+
+ codeptr = codecopy;
+
+ while ((obj = scan_ps(st, &codeptr)) != NULL)
+ {
+#ifdef DEBUG
+ DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)\n", st->num_objs));
+ DEBUG_object(obj);
+#endif /* DEBUG */
+
+ switch (obj->type)
+ {
+ default :
+ /* Do nothing for regular values */
+ break;
+
+ case CUPS_PS_CLEARTOMARK :
+ pop_stack(st);
+
+ if (cleartomark_stack(st))
+ _cupsRasterAddError("cleartomark: Stack underflow!\n");
+
+#ifdef DEBUG
+ DEBUG_puts(" dup: ");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ break;
+
+ case CUPS_PS_COPY :
+ pop_stack(st);
+ if ((obj = pop_stack(st)) != NULL)
+ {
+ copy_stack(st, (int)obj->value.number);
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: copy");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ }
+ break;
+
+ case CUPS_PS_DUP :
+ pop_stack(st);
+ copy_stack(st, 1);
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: dup");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ break;
+
+ case CUPS_PS_INDEX :
+ pop_stack(st);
+ if ((obj = pop_stack(st)) != NULL)
+ {
+ index_stack(st, (int)obj->value.number);
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: index");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ }
+ break;
+
+ case CUPS_PS_POP :
+ pop_stack(st);
+ pop_stack(st);
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: pop");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ break;
+
+ case CUPS_PS_ROLL :
+ pop_stack(st);
+ if ((obj = pop_stack(st)) != NULL)
+ {
+ int c; /* Count */
+
+
+ c = (int)obj->value.number;
+
+ if ((obj = pop_stack(st)) != NULL)
+ {
+ roll_stack(st, (int)obj->value.number, c);
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: roll");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ }
+ }
+ break;
+
+ case CUPS_PS_SETPAGEDEVICE :
+ pop_stack(st);
+ setpagedevice(st, h, preferred_bits);
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: setpagedevice");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+ break;
+
+ case CUPS_PS_START_PROC :
+ case CUPS_PS_END_PROC :
+ case CUPS_PS_STOPPED :
+ pop_stack(st);
+ break;
+
+ case CUPS_PS_OTHER :
+ _cupsRasterAddError("Unknown operator \"%s\"!\n", obj->value.other);
+ error = 1;
+ DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\"!\n",
+ obj->value.other));
+ break;
+ }
+
+ if (error)
+ break;
+ }
+
+ /*
+ * Cleanup...
+ */
+
+ free(codecopy);
+
+ if (st->num_objs > 0)
+ {
+ error_stack(st, "Stack not empty:");
+
+#ifdef DEBUG
+ DEBUG_puts("_cupsRasterExecPS: Stack not empty:");
+ DEBUG_stack(st);
+#endif /* DEBUG */
+
+ delete_stack(st);
+
+ return (-1);
+ }
+
+ delete_stack(st);
+
+ /*
+ * Return success...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
+ */
+
+static int /* O - 0 on success, -1 on error */
+cleartomark_stack(_cups_ps_stack_t *st) /* I - Stack */
+{
+ _cups_ps_obj_t *obj; /* Current object on stack */
+
+
+ while ((obj = pop_stack(st)) != NULL)
+ if (obj->type == CUPS_PS_START_ARRAY)
+ break;
+
+ return (obj ? 0 : -1);
+}
+
+
+/*
+ * 'copy_stack()' - Copy the top N stack objects.
+ */
+
+static int /* O - 0 on success, -1 on error */
+copy_stack(_cups_ps_stack_t *st, /* I - Stack */
+ int c) /* I - Number of objects to copy */
+{
+ int n; /* Index */
+
+
+ if (c < 0)
+ return (-1);
+ else if (c == 0)
+ return (0);
+
+ if ((n = st->num_objs - c) < 0)
+ return (-1);
+
+ while (c > 0)
+ {
+ if (!push_stack(st, st->objs + n))
+ return (-1);
+
+ n ++;
+ c --;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'delete_stack()' - Free memory used by a stack.
+ */
+
+static void
+delete_stack(_cups_ps_stack_t *st) /* I - Stack */
+{
+ free(st->objs);
+ free(st);
+}
+
+
+/*
+ * 'error_object()' - Add an object's value to the current error message.
+ */
+
+static void
+error_object(_cups_ps_obj_t *obj) /* I - Object to add */
+{
+ switch (obj->type)
+ {
+ case CUPS_PS_NAME :
+ _cupsRasterAddError(" /%s", obj->value.name);
+ break;
+
+ case CUPS_PS_NUMBER :
+ _cupsRasterAddError(" %g", obj->value.number);
+ break;
+
+ case CUPS_PS_STRING :
+ _cupsRasterAddError(" (%s)", obj->value.string);
+ break;
+
+ case CUPS_PS_BOOLEAN :
+ if (obj->value.boolean)
+ _cupsRasterAddError(" true");
+ else
+ _cupsRasterAddError(" false");
+ break;
+
+ case CUPS_PS_NULL :
+ _cupsRasterAddError(" null");
+ break;
+
+ case CUPS_PS_START_ARRAY :
+ _cupsRasterAddError(" [");
+ break;
+
+ case CUPS_PS_END_ARRAY :
+ _cupsRasterAddError(" ]");
+ break;
+
+ case CUPS_PS_START_DICT :
+ _cupsRasterAddError(" <<");
+ break;
+
+ case CUPS_PS_END_DICT :
+ _cupsRasterAddError(" >>");
+ break;
+
+ case CUPS_PS_START_PROC :
+ _cupsRasterAddError(" {");
+ break;
+
+ case CUPS_PS_END_PROC :
+ _cupsRasterAddError(" }");
+ break;
+
+ case CUPS_PS_COPY :
+ _cupsRasterAddError(" --copy--");
+ break;
+
+ case CUPS_PS_CLEARTOMARK :
+ _cupsRasterAddError(" --cleartomark--");
+ break;
+
+ case CUPS_PS_DUP :
+ _cupsRasterAddError(" --dup--");
+ break;
+
+ case CUPS_PS_INDEX :
+ _cupsRasterAddError(" --index--");
+ break;
+
+ case CUPS_PS_POP :
+ _cupsRasterAddError(" --pop--");
+ break;
+
+ case CUPS_PS_ROLL :
+ _cupsRasterAddError(" --roll--");
+ break;
+
+ case CUPS_PS_SETPAGEDEVICE :
+ _cupsRasterAddError(" --setpagedevice--");
+ break;
+
+ case CUPS_PS_STOPPED :
+ _cupsRasterAddError(" --stopped--");
+ break;
+
+ case CUPS_PS_OTHER :
+ _cupsRasterAddError(" --%s--", obj->value.other);
+ break;
+ }
+}
+
+
+/*
+ * 'error_stack()' - Add a stack to the current error message...
+ */
+
+static void
+error_stack(_cups_ps_stack_t *st, /* I - Stack */
+ const char *title) /* I - Title string */
+{
+ int c; /* Looping var */
+ _cups_ps_obj_t *obj; /* Current object on stack */
+
+
+ _cupsRasterAddError("%s", title);
+
+ for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
+ error_object(obj);
+
+ _cupsRasterAddError("\n");
+}
+
+
+/*
+ * 'index_stack()' - Copy the Nth value on the stack.
+ */
+
+static _cups_ps_obj_t * /* O - New object */
+index_stack(_cups_ps_stack_t *st, /* I - Stack */
+ int n) /* I - Object index */
+{
+ if (n < 0 || (n = st->num_objs - n - 1) < 0)
+ return (NULL);
+
+ return (push_stack(st, st->objs + n));
+}
+
+
+/*
+ * 'new_stack()' - Create a new stack.
+ */
+
+static _cups_ps_stack_t * /* O - New stack */
+new_stack(void)
+{
+ _cups_ps_stack_t *st; /* New stack */
+
+
+ if ((st = calloc(1, sizeof(_cups_ps_stack_t))) == NULL)
+ return (NULL);
+
+ st->alloc_objs = 32;
+
+ if ((st->objs = calloc(32, sizeof(_cups_ps_obj_t))) == NULL)
+ {
+ free(st);
+ return (NULL);
+ }
+ else
+ return (st);
+}
+
+
+/*
+ * 'pop_stock()' - Pop the top object off the stack.
+ */
+
+static _cups_ps_obj_t * /* O - Object */
+pop_stack(_cups_ps_stack_t *st) /* I - Stack */
+{
+ if (st->num_objs > 0)
+ {
+ st->num_objs --;
+
+ return (st->objs + st->num_objs);
+ }
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'push_stack()' - Push an object on the stack.
+ */
+
+static _cups_ps_obj_t * /* O - New object */
+push_stack(_cups_ps_stack_t *st, /* I - Stack */
+ _cups_ps_obj_t *obj) /* I - Object */
+{
+ _cups_ps_obj_t *temp; /* New object */
+
+
+ if (st->num_objs >= st->alloc_objs)
+ {
+
+
+ st->alloc_objs += 32;
+
+ if ((temp = realloc(st->objs, st->alloc_objs *
+ sizeof(_cups_ps_obj_t))) == NULL)
+ return (NULL);
+
+ st->objs = temp;
+ memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
+ }
+
+ temp = st->objs + st->num_objs;
+ st->num_objs ++;
+
+ memcpy(temp, obj, sizeof(_cups_ps_obj_t));
+
+ return (temp);
+}
+
+
+/*
+ * 'roll_stack()' - Rotate stack objects.
+ */
+
+static int /* O - 0 on success, -1 on error */
+roll_stack(_cups_ps_stack_t *st, /* I - Stack */
+ int c, /* I - Number of objects */
+ int s) /* I - Amount to shift */
+{
+ _cups_ps_obj_t *temp; /* Temporary array of objects */
+ int n; /* Index into array */
+
+
+ DEBUG_printf((" roll_stack(st=%p, s=%d, c=%d)\n", st, s, c));
+
+ /*
+ * Range check input...
+ */
+
+ if (c < 0)
+ return (-1);
+ else if (c == 0)
+ return (0);
+
+ if ((n = st->num_objs - c) < 0)
+ return (-1);
+
+ s %= c;
+
+ if (s == 0)
+ return (0);
+
+ /*
+ * Copy N objects and move things around...
+ */
+
+ if (s < 0)
+ {
+ /*
+ * Shift down...
+ */
+
+ s = -s;
+
+ if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
+ return (-1);
+
+ memcpy(temp, st->objs + n, s * sizeof(_cups_ps_obj_t));
+ memmove(st->objs + n, st->objs + n + s, (c - s) * sizeof(_cups_ps_obj_t));
+ memcpy(st->objs + n + c - s, temp, s * sizeof(_cups_ps_obj_t));
+ }
+ else
+ {
+ /*
+ * Shift up...
+ */
+
+ if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
+ return (-1);
+
+ memcpy(temp, st->objs + n + c - s, s * sizeof(_cups_ps_obj_t));
+ memmove(st->objs + n + s, st->objs + n,
+ (c - s) * sizeof(_cups_ps_obj_t));
+ memcpy(st->objs + n, temp, s * sizeof(_cups_ps_obj_t));
+ }
+
+ free(temp);
+
+ return (0);
+}
+
+
+/*
+ * 'scan_ps()' - Scan a string for the next PS object.
+ */
+
+static _cups_ps_obj_t * /* O - New object or NULL on EOF */
+scan_ps(_cups_ps_stack_t *st, /* I - Stack */
+ char **ptr) /* IO - String pointer */
+{
+ _cups_ps_obj_t obj; /* Current object */
+ char *start, /* Start of object */
+ *cur, /* Current position */
+ *valptr, /* Pointer into value string */
+ *valend; /* End of value string */
+ int parens; /* Parenthesis nesting level */
+
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ for (cur = *ptr; *cur; cur ++)
+ {
+ if (*cur == '%')
+ {
+ /*
+ * Comment, skip to end of line...
+ */
+
+ for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++);
+
+ if (!*cur)
+ cur --;
+ }
+ else if (!isspace(*cur & 255))
+ break;
+ }
+
+ if (!*cur)
+ {
+ *ptr = NULL;
+
+ return (NULL);
+ }
+
+ /*
+ * See what we have...
+ */
+
+ memset(&obj, 0, sizeof(obj));
+
+ switch (*cur)
+ {
+ case '(' : /* (string) */
+ obj.type = CUPS_PS_STRING;
+ start = cur;
+
+ for (cur ++, parens = 1, valptr = obj.value.string,
+ valend = obj.value.string + sizeof(obj.value.string) - 1;
+ *cur;
+ cur ++)
+ {
+ if (*cur == ')' && parens == 1)
+ break;
+
+ if (*cur == '(')
+ parens ++;
+ else if (*cur == ')')
+ parens --;
+
+ if (valptr >= valend)
+ {
+ *ptr = start;
+
+ return (NULL);
+ }
+
+ if (*cur == '\\')
+ {
+ /*
+ * Decode escaped character...
+ */
+
+ cur ++;
+
+ if (*cur == 'b')
+ *valptr++ = '\b';
+ else if (*cur == 'f')
+ *valptr++ = '\f';
+ else if (*cur == 'n')
+ *valptr++ = '\n';
+ else if (*cur == 'r')
+ *valptr++ = '\r';
+ else if (*cur == 't')
+ *valptr++ = '\t';
+ else if (*cur >= '0' && *cur <= '7')
+ {
+ int ch = *cur - '0';
+
+ if (cur[1] >= '0' && cur[1] <= '7')
+ {
+ cur ++;
+ ch = (ch << 3) + *cur - '0';
+ }
+
+ if (cur[1] >= '0' && cur[1] <= '7')
+ {
+ cur ++;
+ ch = (ch << 3) + *cur - '0';
+ }
+
+ *valptr++ = ch;
+ }
+ else if (*cur == '\r')
+ {
+ if (cur[1] == '\n')
+ cur ++;
+ }
+ else if (*cur != '\n')
+ *valptr++ = *cur;
+ }
+ else
+ *valptr++ = *cur;
+ }
+
+ if (*cur != ')')
+ {
+ *ptr = start;
+
+ return (NULL);
+ }
+
+ cur ++;
+ break;
+
+ case '[' : /* Start array */
+ obj.type = CUPS_PS_START_ARRAY;
+ cur ++;
+ break;
+
+ case ']' : /* End array */
+ obj.type = CUPS_PS_END_ARRAY;
+ cur ++;
+ break;
+
+ case '<' : /* Start dictionary or hex string */
+ if (cur[1] == '<')
+ {
+ obj.type = CUPS_PS_START_DICT;
+ cur += 2;
+ }
+ else
+ {
+ obj.type = CUPS_PS_STRING;
+ start = cur;
+
+ for (cur ++, valptr = obj.value.string,
+ valend = obj.value.string + sizeof(obj.value.string) - 1;
+ *cur;
+ cur ++)
+ {
+ int ch; /* Current character */
+
+
+
+ if (*cur == '>')
+ break;
+ else if (valptr >= valend || !isxdigit(*cur & 255))
+ {
+ *ptr = start;
+ return (NULL);
+ }
+
+ if (*cur >= '0' && *cur <= '9')
+ ch = (*cur - '0') << 4;
+ else
+ ch = (tolower(*cur) - 'a' + 10) << 4;
+
+ if (isxdigit(cur[1] & 255))
+ {
+ cur ++;
+
+ if (*cur >= '0' && *cur <= '9')
+ ch |= *cur - '0';
+ else
+ ch |= tolower(*cur) - 'a' + 10;
+ }
+
+ *valptr++ = ch;
+ }
+
+ if (*cur != '>')
+ {
+ *ptr = start;
+ return (NULL);
+ }
+
+ cur ++;
+ }
+ break;
+
+ case '>' : /* End dictionary? */
+ if (cur[1] == '>')
+ {
+ obj.type = CUPS_PS_END_DICT;
+ cur += 2;
+ }
+ else
+ {
+ obj.type = CUPS_PS_OTHER;
+ obj.value.other[0] = *cur;
+
+ cur ++;
+ }
+ break;
+
+ case '{' : /* Start procedure */
+ obj.type = CUPS_PS_START_PROC;
+ cur ++;
+ break;
+
+ case '}' : /* End procedure */
+ obj.type = CUPS_PS_END_PROC;
+ cur ++;
+ break;
+
+ case '-' : /* Possible number */
+ case '+' :
+ if (!isdigit(cur[1] & 255) && cur[1] != '.')
+ {
+ obj.type = CUPS_PS_OTHER;
+ obj.value.other[0] = *cur;
+
+ cur ++;
+ break;
+ }
+
+ case '0' : /* Number */
+ case '1' :
+ case '2' :
+ case '3' :
+ case '4' :
+ case '5' :
+ case '6' :
+ case '7' :
+ case '8' :
+ case '9' :
+ case '.' :
+ obj.type = CUPS_PS_NUMBER;
+
+ start = cur;
+ for (cur ++; *cur; cur ++)
+ if (!isdigit(*cur & 255))
+ break;
+
+ if (*cur == '#')
+ {
+ /*
+ * Integer with radix...
+ */
+
+ obj.value.number = strtol(cur + 1, &cur, atoi(start));
+ break;
+ }
+ else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255))
+ {
+ /*
+ * Integer or real number...
+ */
+
+ obj.value.number = _cupsStrScand(start, &cur, localeconv());
+ break;
+ }
+ else
+ cur = start;
+
+ default : /* Operator/variable name */
+ start = cur;
+
+ if (*cur == '/')
+ {
+ obj.type = CUPS_PS_NAME;
+ valptr = obj.value.name;
+ valend = obj.value.name + sizeof(obj.value.name) - 1;
+ cur ++;
+ }
+ else
+ {
+ obj.type = CUPS_PS_OTHER;
+ valptr = obj.value.other;
+ valend = obj.value.other + sizeof(obj.value.other) - 1;
+ }
+
+ while (*cur)
+ {
+ if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255))
+ break;
+ else if (valptr < valend)
+ *valptr++ = *cur++;
+ else
+ {
+ *ptr = start;
+ return (NULL);
+ }
+ }
+
+ if (obj.type == CUPS_PS_OTHER)
+ {
+ if (!strcmp(obj.value.other, "true"))
+ {
+ obj.type = CUPS_PS_BOOLEAN;
+ obj.value.boolean = 1;
+ }
+ else if (!strcmp(obj.value.other, "false"))
+ {
+ obj.type = CUPS_PS_BOOLEAN;
+ obj.value.boolean = 0;
+ }
+ else if (!strcmp(obj.value.other, "null"))
+ obj.type = CUPS_PS_NULL;
+ else if (!strcmp(obj.value.other, "cleartomark"))
+ obj.type = CUPS_PS_CLEARTOMARK;
+ else if (!strcmp(obj.value.other, "copy"))
+ obj.type = CUPS_PS_COPY;
+ else if (!strcmp(obj.value.other, "dup"))
+ obj.type = CUPS_PS_DUP;
+ else if (!strcmp(obj.value.other, "index"))
+ obj.type = CUPS_PS_INDEX;
+ else if (!strcmp(obj.value.other, "pop"))
+ obj.type = CUPS_PS_POP;
+ else if (!strcmp(obj.value.other, "roll"))
+ obj.type = CUPS_PS_ROLL;
+ else if (!strcmp(obj.value.other, "setpagedevice"))
+ obj.type = CUPS_PS_SETPAGEDEVICE;
+ else if (!strcmp(obj.value.other, "stopped"))
+ obj.type = CUPS_PS_STOPPED;
+ }
+ break;
+ }
+
+ /*
+ * Save the current position in the string and return the new object...
+ */
+
+ *ptr = cur;
+
+ return (push_stack(st, &obj));
+}
+
+
+/*
+ * 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
+ */
+
+static int /* O - 0 on success, -1 on error */
+setpagedevice(
+ _cups_ps_stack_t *st, /* I - Stack */
+ cups_page_header2_t *h, /* O - Page header */
+ int *preferred_bits)/* O - Preferred bits per color */
+{
+ int i; /* Index into array */
+ _cups_ps_obj_t *obj, /* Current object */
+ *end; /* End of dictionary */
+ const char *name; /* Attribute name */
+
+
+ /*
+ * Make sure we have a dictionary on the stack...
+ */
+
+ if (st->num_objs == 0)
+ return (-1);
+
+ obj = end = st->objs + st->num_objs - 1;
+
+ if (obj->type != CUPS_PS_END_DICT)
+ return (-1);
+
+ obj --;
+
+ while (obj > st->objs)
+ {
+ if (obj->type == CUPS_PS_START_DICT)
+ break;
+
+ obj --;
+ }
+
+ if (obj < st->objs)
+ return (-1);
+
+ /*
+ * Found the start of the dictionary, empty the stack to this point...
+ */
+
+ st->num_objs = (int)(obj - st->objs);
+
+ /*
+ * Now pull /name and value pairs from the dictionary...
+ */
+
+ DEBUG_puts("setpagedevice: Dictionary:");
+
+ for (obj ++; obj < end; obj ++)
+ {
+ /*
+ * Grab the name...
+ */
+
+ if (obj->type != CUPS_PS_NAME)
+ return (-1);
+
+ name = obj->value.name;
+ obj ++;
+
+#ifdef DEBUG
+ DEBUG_printf(("setpagedevice: /%s ", name));
+ DEBUG_object(obj);
+#endif /* DEBUG */
+
+ /*
+ * Then grab the value...
+ */
+
+ if (!strcmp(name, "MediaClass") && obj->type == CUPS_PS_STRING)
+ strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass));
+ else if (!strcmp(name, "MediaColor") && obj->type == CUPS_PS_STRING)
+ strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor));
+ else if (!strcmp(name, "MediaType") && obj->type == CUPS_PS_STRING)
+ strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType));
+ else if (!strcmp(name, "OutputType") && obj->type == CUPS_PS_STRING)
+ strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType));
+ else if (!strcmp(name, "AdvanceDistance") && obj->type == CUPS_PS_NUMBER)
+ h->AdvanceDistance = (unsigned)obj->value.number;
+ else if (!strcmp(name, "AdvanceMedia") && obj->type == CUPS_PS_NUMBER)
+ h->AdvanceMedia = (unsigned)obj->value.number;
+ else if (!strcmp(name, "Collate") && obj->type == CUPS_PS_BOOLEAN)
+ h->Collate = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "CutMedia") && obj->type == CUPS_PS_NUMBER)
+ h->CutMedia = (cups_cut_t)(unsigned)obj->value.number;
+ else if (!strcmp(name, "Duplex") && obj->type == CUPS_PS_BOOLEAN)
+ h->Duplex = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "HWResolution") && obj->type == CUPS_PS_START_ARRAY)
+ {
+ if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
+ obj[3].type == CUPS_PS_END_ARRAY)
+ {
+ h->HWResolution[0] = (unsigned)obj[1].value.number;
+ h->HWResolution[1] = (unsigned)obj[2].value.number;
+ obj += 3;
+ }
+ else
+ return (-1);
+ }
+ else if (!strcmp(name, "InsertSheet") && obj->type == CUPS_PS_BOOLEAN)
+ h->InsertSheet = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "Jog") && obj->type == CUPS_PS_NUMBER)
+ h->Jog = (unsigned)obj->value.number;
+ else if (!strcmp(name, "LeadingEdge") && obj->type == CUPS_PS_NUMBER)
+ h->LeadingEdge = (unsigned)obj->value.number;
+ else if (!strcmp(name, "ManualFeed") && obj->type == CUPS_PS_BOOLEAN)
+ h->ManualFeed = (unsigned)obj->value.boolean;
+ else if ((!strcmp(name, "cupsMediaPosition") ||
+ !strcmp(name, "MediaPosition")) && obj->type == CUPS_PS_NUMBER)
+ {
+ /*
+ * cupsMediaPosition is supported for backwards compatibility only.
+ * We added it back in the Ghostscript 5.50 days to work around a
+ * bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
+ *
+ * All new development should set MediaPosition...
+ */
+
+ h->MediaPosition = (unsigned)obj->value.number;
+ }
+ else if (!strcmp(name, "MediaWeight") && obj->type == CUPS_PS_NUMBER)
+ h->MediaWeight = (unsigned)obj->value.number;
+ else if (!strcmp(name, "MirrorPrint") && obj->type == CUPS_PS_BOOLEAN)
+ h->MirrorPrint = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "NegativePrint") && obj->type == CUPS_PS_BOOLEAN)
+ h->NegativePrint = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "NumCopies") && obj->type == CUPS_PS_NUMBER)
+ h->NumCopies = (unsigned)obj->value.number;
+ else if (!strcmp(name, "Orientation") && obj->type == CUPS_PS_NUMBER)
+ h->Orientation = (unsigned)obj->value.number;
+ else if (!strcmp(name, "OutputFaceUp") && obj->type == CUPS_PS_BOOLEAN)
+ h->OutputFaceUp = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "PageSize") && obj->type == CUPS_PS_START_ARRAY)
+ {
+ if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
+ obj[3].type == CUPS_PS_END_ARRAY)
+ {
+ h->cupsPageSize[0] = (float)obj[1].value.number;
+ h->cupsPageSize[1] = (float)obj[2].value.number;
+
+ h->PageSize[0] = (unsigned)obj[1].value.number;
+ h->PageSize[1] = (unsigned)obj[2].value.number;
+
+ obj += 3;
+ }
+ else
+ return (-1);
+ }
+ else if (!strcmp(name, "Separations") && obj->type == CUPS_PS_BOOLEAN)
+ h->Separations = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "TraySwitch") && obj->type == CUPS_PS_BOOLEAN)
+ h->TraySwitch = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "Tumble") && obj->type == CUPS_PS_BOOLEAN)
+ h->Tumble = (unsigned)obj->value.boolean;
+ else if (!strcmp(name, "cupsMediaType") && obj->type == CUPS_PS_NUMBER)
+ h->cupsMediaType = (unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsBitsPerColor") && obj->type == CUPS_PS_NUMBER)
+ h->cupsBitsPerColor = (unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsPreferredBitsPerColor") &&
+ obj->type == CUPS_PS_NUMBER)
+ *preferred_bits = (int)obj->value.number;
+ else if (!strcmp(name, "cupsColorOrder") && obj->type == CUPS_PS_NUMBER)
+ h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsColorSpace") && obj->type == CUPS_PS_NUMBER)
+ h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsCompression") && obj->type == CUPS_PS_NUMBER)
+ h->cupsCompression = (unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsRowCount") && obj->type == CUPS_PS_NUMBER)
+ h->cupsRowCount = (unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsRowFeed") && obj->type == CUPS_PS_NUMBER)
+ h->cupsRowFeed = (unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsRowStep") && obj->type == CUPS_PS_NUMBER)
+ h->cupsRowStep = (unsigned)obj->value.number;
+ else if (!strcmp(name, "cupsBorderlessScalingFactor") &&
+ obj->type == CUPS_PS_NUMBER)
+ h->cupsBorderlessScalingFactor = (float)obj->value.number;
+ else if (!strncmp(name, "cupsInteger", 11) && obj->type == CUPS_PS_NUMBER)
+ {
+ if ((i = atoi(name + 11)) < 0 || i > 15)
+ return (-1);
+
+ h->cupsInteger[i] = (unsigned)obj->value.number;
+ }
+ else if (!strncmp(name, "cupsReal", 8) && obj->type == CUPS_PS_NUMBER)
+ {
+ if ((i = atoi(name + 8)) < 0 || i > 15)
+ return (-1);
+
+ h->cupsReal[i] = (float)obj->value.number;
+ }
+ else if (!strncmp(name, "cupsString", 10) && obj->type == CUPS_PS_STRING)
+ {
+ if ((i = atoi(name + 10)) < 0 || i > 15)
+ return (-1);
+
+ strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i]));
+ }
+ else if (!strcmp(name, "cupsMarkerType") && obj->type == CUPS_PS_STRING)
+ strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType));
+ else if (!strcmp(name, "cupsPageSizeName") && obj->type == CUPS_PS_STRING)
+ strlcpy(h->cupsPageSizeName, obj->value.string,
+ sizeof(h->cupsPageSizeName));
+ else if (!strcmp(name, "cupsRenderingIntent") &&
+ obj->type == CUPS_PS_STRING)
+ strlcpy(h->cupsRenderingIntent, obj->value.string,
+ sizeof(h->cupsRenderingIntent));
+ else
+ {
+ /*
+ * Ignore unknown name+value...
+ */
+
+ DEBUG_printf((" Unknown name (\"%s\") or value...\n", name));
+
+ while (obj[1].type != CUPS_PS_NAME && obj < end)
+ obj ++;
+ }
+ }
+
+ return (0);
+}
+
+
+#ifdef DEBUG
+/*
+ * 'DEBUG_object()' - Print an object's value...
+ */
+
+static void
+DEBUG_object(_cups_ps_obj_t *obj) /* I - Object to print */
+{
+ switch (obj->type)
+ {
+ case CUPS_PS_NAME :
+ DEBUG_printf(("/%s\n", obj->value.name));
+ break;
+
+ case CUPS_PS_NUMBER :
+ DEBUG_printf(("%g\n", obj->value.number));
+ break;
+
+ case CUPS_PS_STRING :
+ DEBUG_printf(("(%s)\n", obj->value.string));
+ break;
+
+ case CUPS_PS_BOOLEAN :
+ if (obj->value.boolean)
+ DEBUG_puts("true");
+ else
+ DEBUG_puts("false");
+ break;
+
+ case CUPS_PS_NULL :
+ DEBUG_puts("null");
+ break;
+
+ case CUPS_PS_START_ARRAY :
+ DEBUG_puts("[");
+ break;
+
+ case CUPS_PS_END_ARRAY :
+ DEBUG_puts("]");
+ break;
+
+ case CUPS_PS_START_DICT :
+ DEBUG_puts("<<");
+ break;
+
+ case CUPS_PS_END_DICT :
+ DEBUG_puts(">>");
+ break;
+
+ case CUPS_PS_START_PROC :
+ DEBUG_puts("{");
+ break;
+
+ case CUPS_PS_END_PROC :
+ DEBUG_puts("}");
+ break;
+
+ case CUPS_PS_CLEARTOMARK :
+ DEBUG_puts("--cleartomark--");
+ break;
+
+ case CUPS_PS_COPY :
+ DEBUG_puts("--copy--");
+ break;
+
+ case CUPS_PS_DUP :
+ DEBUG_puts("--dup--");
+ break;
+
+ case CUPS_PS_INDEX :
+ DEBUG_puts("--index--");
+ break;
+
+ case CUPS_PS_POP :
+ DEBUG_puts("--pop--");
+ break;
+
+ case CUPS_PS_ROLL :
+ DEBUG_puts("--roll--");
+ break;
+
+ case CUPS_PS_SETPAGEDEVICE :
+ DEBUG_puts("--setpagedevice--");
+ break;
+
+ case CUPS_PS_STOPPED :
+ DEBUG_puts("--stopped--");
+ break;
+
+ case CUPS_PS_OTHER :
+ DEBUG_printf(("--%s--\n", obj->value.other));
+ break;
+ }
+}
+
+
+/*
+ * 'DEBUG_stack()' - Print a stack...
+ */
+
+static void
+DEBUG_stack(_cups_ps_stack_t *st) /* I - Stack */
+{
+ int c; /* Looping var */
+ _cups_ps_obj_t *obj; /* Current object on stack */
+
+
+ for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
+ DEBUG_object(obj);
+}
+#endif /* DEBUG */
+
+
+/*
+ * End of "$Id: interpret.c 11551 2014-01-29 16:31:35Z msweet $".
+ */
diff --git a/cups/libs/filter/libcupsimage2.def b/cups/libs/filter/libcupsimage2.def
new file mode 100644
index 000000000..8af5c5132
--- /dev/null
+++ b/cups/libs/filter/libcupsimage2.def
@@ -0,0 +1,14 @@
+LIBRARY libcupsimage2
+VERSION 2.3
+EXPORTS
+cupsRasterClose
+cupsRasterErrorString
+cupsRasterInterpretPPD
+cupsRasterOpen
+cupsRasterOpenIO
+cupsRasterReadHeader
+cupsRasterReadHeader2
+cupsRasterReadPixels
+cupsRasterWriteHeader
+cupsRasterWriteHeader2
+cupsRasterWritePixels
diff --git a/cups/libs/filter/libcupsimage_s.exp b/cups/libs/filter/libcupsimage_s.exp
new file mode 100644
index 000000000..57f4259f9
--- /dev/null
+++ b/cups/libs/filter/libcupsimage_s.exp
@@ -0,0 +1,16 @@
+_cupsImagePutCol
+_cupsImagePutRow
+_cupsImageReadBMP
+_cupsImageReadGIF
+_cupsImageReadJPEG
+_cupsImageReadPIX
+_cupsImageReadPNG
+_cupsImageReadPNM
+_cupsImageReadPhotoCD
+_cupsImageReadSGI
+_cupsImageReadSunRaster
+_cupsImageReadTIFF
+_cupsImageZoomDelete
+_cupsImageZoomFill
+_cupsImageZoomNew
+_cupsRasterExecPS
diff --git a/cups/libs/filter/postscript-driver.header b/cups/libs/filter/postscript-driver.header
new file mode 100644
index 000000000..08e4c175b
--- /dev/null
+++ b/cups/libs/filter/postscript-driver.header
@@ -0,0 +1,32 @@
+<!--
+ "$Id$"
+
+ PostScript printer driver documentation for CUPS.
+
+ Copyright 2007-2012 by Apple Inc.
+ Copyright 1997-2007 by Easy Software Products.
+
+ These coded instructions, statements, and computer programs are the
+ property of Apple Inc. and are protected by Federal copyright
+ law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ which should have been included with this file. If this file is
+ file is missing or damaged, see the license at "http://www.cups.org/".
+-->
+
+<h1 class='title'>Developing PostScript Printer Drivers</h1>
+
+<p>This document describes how to develop printer drivers for PostScript printers. Topics include: <a href='#BASICS'>printer driver basics</a>, <a href='#CREATE'>creating new PPD files</a>, <a href='#IMPORT'>importing existing PPD files</a>, <a href='#FILTERS'>using custom filters</a>, <a href='#COLOR'>implementing color management</a>, and <a href='#MACOSX'>adding OS X features</a>.</p>
+
+<div class='summary'><table summary='General Information'>
+<tbody>
+<tr>
+ <th>See Also</th>
+ <td>Programming: <a href='raster-driver.html'>Developing Raster Printer Drivers</a><br>
+ Programming: <a href='api-filter.html'>Filter and Backend Programming</a><br>
+ Programming: <a href='ppd-compiler.html'>Introduction to the PPD Compiler</a><br>
+ Programming: <a href='api-raster.html'>Raster API</a><br>
+ References: <a href='ref-ppdcfile.html'>PPD Compiler Driver Information File Reference</a><br>
+ Specifications: <a href='spec-ppd.html'>CUPS PPD Extensions</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/filter/postscript-driver.shtml b/cups/libs/filter/postscript-driver.shtml
new file mode 100644
index 000000000..5e93f8580
--- /dev/null
+++ b/cups/libs/filter/postscript-driver.shtml
@@ -0,0 +1,276 @@
+<h2 class='title'><a name='BASICS'>Printer Driver Basics</a></h2>
+
+<p>A CUPS PostScript printer driver consists of a PostScript Printer Description (PPD) file that describes the features and capabilities of the device, zero or more <em>filter</em> programs that prepare print data for the device, and zero or more support files for color management, online help, and so forth. The PPD file includes references to all of the filters and support files used by the driver.</p>
+
+<p>Every time a user prints something the scheduler program, <a href='man-cupsd.html'>cupsd(8)</a>, determines the format of the print job and the programs required to convert that job into something the printer understands. CUPS includes filter programs for many common formats, for example to convert Portable Document Format (PDF) files into device-independent PostScript, and then from device-independent PostScript to device-dependent PostScript. <a href='#FIGURE_1'>Figure 1</a> shows the data flow of a typical print job.</p>
+
+<div class='figure'><table summary='PostScript Filter Chain'>
+<caption>Figure 1: <a name='FIGURE_1'>PostScript Filter Chain</a></caption>
+<tr><td><img src='../images/cups-postscript-chain.png' width='700' height='150' alt='PostScript Filter Chain'></td></tr>
+</table></div>
+
+<p>The optional PostScript filter can be provided to add printer-specific commands to the PostScript output that cannot be represented in the PPD file or to reorganize the output for special printer features. Typically this is used to support advanced job management or finishing functions on the printer. CUPS includes a generic PostScript filter that handles all PPD-defined commands.</p>
+
+<p>The optional port monitor handles interface-specific protocol or encoding issues. For example, many PostScript printers support the Binary Communications Protocol (BCP) and Tagged Binary Communications Protocol (TBCP) to allow applications to print 8-bit ("binary") PostScript jobs. CUPS includes port monitors for BCP and TBCP, and you can supply your own port monitors as needed.</p>
+
+<p>The backend handles communications with the printer, sending print data from the last filter to the printer and relaying back-channel data from the printer to the upstream filters. CUPS includes backend programs for common direct-connect interfaces and network protocols, and you can provide your own backend to support custom interfaces and protocols.</p>
+
+<p>The scheduler also supports a special "command" file format for sending maintenance commands and status queries to a printer or printer driver. Command print jobs typically use a single command filter program defined in the PPD file to generate the appropriate printer commands and handle any responses from the printer. <a href='#FIGURE_2'>Figure 2</a> shows the data flow of a typical command job.</p>
+
+<div class='figure'><table summary='Command Filter Chain'>
+<caption>Figure 2: <a name='FIGURE_2'>Command Filter Chain</a></caption>
+<tr><td><img src='../images/cups-command-chain.png' width='575' height='150' alt='Command Filter Chain'></td></tr>
+</table></div>
+
+<p>PostScript printer drivers typically do not require their own command filter since CUPS includes a generic PostScript command filter that supports all of the standard functions using PPD-defined commands.</p>
+
+
+<h2 class='title'><a name='CREATING'>Creating New PPD Files</a></h2>
+
+<p>We recommend using the CUPS PPD compiler, <a href='man-ppdc.html'>ppdc(1)</a>, to create new PPD files since it manages many of the tedious (and error-prone!) details of paper sizes and localization for you. It also allows you to easily support multiple devices from a single source file. For more information see the "<a href='ppd-compiler.html'>Introduction to the PPD Compiler</a>" document. <a href='#LISTING_1'>Listing 1</a> shows a driver information file for a black-and-white PostScript printer.</p>
+
+<p class='example'>Listing 1: <a name='LISTING_1'>"examples/postscript.drv"</a></p>
+
+<pre class='example'>
+// Include standard font and media definitions
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+// Specify this is a PostScript printer driver
+<a href='ref-ppdcfile.html#DriverType'>DriverType</a> ps
+
+// List the fonts that are supported, in this case all standard fonts
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+// Manufacturer, model name, and version
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#ModelName'>ModelName</a> "Foo LaserProofer 2000"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+// PostScript printer attributes
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> DefaultColorSpace "" Gray
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> LandscapeOrientation "" Minus90
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> LanguageLevel "" "3"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> Product "" "(Foo LaserProofer 2000)"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> PSVersion "" "(3010) 0"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> TTRasterizer "" Type42
+
+// Supported page sizes
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Legal
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+// Query command for page size
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> "?PageSize" "" "
+ save
+ currentpagedevice /PageSize get aload pop
+ 2 copy gt {exch} if (Unknown)
+ 23 dict
+ dup [612 792] (Letter) put
+ dup [612 1008] (Legal) put
+ dup [595 842] (A4) put
+ {exch aload pop 4 index sub abs 5 le exch
+ 5 index sub abs 5 le and
+ {exch pop exit} {pop} ifelse
+ } bind forall = flush pop pop
+ restore"
+
+// Specify the name of the PPD file we want to generate
+<a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "fooproof.ppd"
+</pre>
+
+<h3>Required Attributes</h3>
+
+<p>PostScript drivers require the attributes listed in <a href='#TABLE_1'>Table 1</a>. If not specified, the defaults for CUPS drivers are used. A typical PostScript driver information file would include the following attributes:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> DefaultColorSpace "" Gray
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> LandscapeOrientation "" Minus90
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> LanguageLevel "" "3"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> Product "" "(Foo LaserProofer 2000)"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> PSVersion "" "(3010) 0"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> TTRasterizer "" Type42
+</pre>
+
+<div class='table'><table summary='Required PostScript Printer Driver Attributes'>
+<caption>Table 1: <a name='TABLE_1'>Required PostScript Printer Driver Attributes</a></caption>
+<thead>
+<tr>
+ <th>Attribute</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td><tt>DefaultColorSpace</tt></td>
+ <td>The default colorspace:
+ <tt>Gray</tt>, <tt>RGB</tt>, <tt>CMY</tt>, or
+ <tt>CMYK</tt>. If not specified, then <tt>RGB</tt> is
+ assumed.</td>
+</tr>
+<tr>
+ <td><tt>LandscapeOrientation</tt></td>
+ <td>The preferred landscape
+ orientation: <tt>Plus90</tt>, <tt>Minus90</tt>, or
+ <tt>Any</tt>. If not specified, <tt>Plus90</tt> is
+ assumed.</td>
+</tr>
+<tr>
+ <td><tt>LanguageLevel</tt></td>
+ <td>The PostScript language
+ level supported by the device: 1, 2, or 3. If not
+ specified, 2 is assumed.</td>
+</tr>
+<tr>
+ <td><tt>Product</tt></td>
+ <td>The string returned by
+ the PostScript <tt>product</tt> operator, which
+ <i>must</i> include parenthesis to conform with
+ PostScript syntax rules for strings. Multiple
+ <tt>Product</tt> attributes may be specified to support
+ multiple products with the same PPD file. If not
+ specified, "(ESP Ghostscript)" and "(GNU Ghostscript)"
+ are assumed.</td>
+</tr>
+<tr>
+ <td><tt>PSVersion</tt></td>
+ <td>The PostScript
+ interpreter version numbers as returned by the
+ <tt>version</tt> and <tt>revision</tt> operators. The
+ required format is "(version) revision". Multiple
+ <tt>PSVersion</tt> attributes may be specified to
+ support multiple interpreter version numbers. If not
+ specified, "(3010) 705" and "(3010) 707" are
+ assumed.</td>
+</tr>
+<tr>
+ <td><tt>TTRasterizer</tt></td>
+ <td>The type of TrueType
+ font rasterizer supported by the device, if any. The
+ supported values are <tt>None</tt>, <tt>Accept68k</tt>,
+ <tt>Type42</tt>, and <tt>TrueImage</tt>. If not
+ specified, <tt>None</tt> is assumed.</td>
+</tr>
+</table></div>
+
+<h3>Query Commands</h3>
+
+<p>Most PostScript printer PPD files include query commands (<tt>?PageSize</tt>, etc.) that allow applications to query the printer for its current settings and configuration. Query commands are included in driver information files as attributes. For example, the example in <a href='#LISTING_1'>Listing 1</a> uses the following definition for the <tt>PageSize</tt> query command:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> "?PageSize" "" "
+ save
+ currentpagedevice /PageSize get aload pop
+ 2 copy gt {exch} if (Unknown)
+ 23 dict
+ dup [612 792] (Letter) put
+ dup [612 1008] (Legal) put
+ dup [595 842] (A4) put
+ {exch aload pop 4 index sub abs 5 le exch
+ 5 index sub abs 5 le and
+ {exch pop exit} {pop} ifelse
+ } bind forall = flush pop pop
+ restore"
+</pre>
+
+<p>Query commands can span multiple lines, however no single line may contain more than 255 characters.</p>
+
+<h3><a name='IMPORT'>Importing Existing PPD Files</a></h3>
+
+<P>CUPS includes a utility called <a href='man-ppdi.html'>ppdi(1)</a>
+which allows you to import existing PPD files into the driver information file
+format used by the PPD compiler <a href='man-ppdc.html'>ppdc(1)</a>. Once
+imported, you can modify, localize, and regenerate the PPD files easily. Type
+the following command to import the PPD file <VAR>mydevice.ppd</VAR> into the
+driver information file <VAR>mydevice.drv</VAR>:</P>
+
+<pre class='command'>
+ppdi -o mydevice.drv mydevice.ppd
+</pre>
+
+<P>If you have a whole directory of PPD files that you would like to import,
+you can list multiple filenames or use shell wildcards to import more than one
+PPD file on the command-line:</P>
+
+<pre class='command'>
+ppdi -o mydevice.drv mydevice1.ppd mydevice2.ppd
+ppdi -o mydevice.drv *.ppd
+</pre>
+
+<P>If the driver information file already exists, the new PPD
+file entries are appended to the end of the file. Each PPD file
+is placed in its own group of curly braces within the driver
+information file.</P>
+
+
+<h2 class='title'><a name='FILTERS'>Using Custom Filters</a></h2>
+
+<p>Normally a PostScript printer driver will not utilize any additional print filters. For drivers that provide additional filters such as a CUPS command file filter for doing printer maintenance, you must also list the following <tt>Filter</tt> directive to handle printing PostScript files:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-postscript 0 -
+</pre>
+
+<h3>Custom Command Filters</h3>
+
+<p>The <tt>application/vnd.cups-command</tt> file type is used for CUPS command files. Use the following <tt>Filter</tt> directive to handle CUPS command files:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-command 100 /path/to/command/filter
+</pre>
+
+<p>To use the standard PostScript command filter, specify <var>commandtops</var> as the path to the command filter.</p>
+
+<h3>Custom PDF Filters</h3>
+
+<p>The <tt>application/pdf</tt> file type is used for unfiltered PDF files while the <tt>application/vnd.cups-pdf</tt> file type is used for filtered PDF files. Use the following <tt>Filter</tt> directive to handle filtered PDF files:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-pdf 100 /path/to/pdf/filter
+</pre>
+
+<p>For unfiltered PDF files, use:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/pdf 100 /path/to/pdf/filter
+</pre>
+
+<p>Custom PDF filters that accept filtered data do not need to perform number-up processing and other types of page imposition, while those that accept unfiltered data MUST do the number-up processing themselves.</p>
+
+<h3>Custom PostScript Filters</h3>
+
+<p>The <tt>application/vnd.cups-postscript</tt> file type is used for filtered PostScript files. Use the following <tt>Filter</tt> directive to handle PostScript files:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-postscript 100 /path/to/postscript/filter
+</pre>
+
+
+<h2 class='title'><a name='COLOR'>Implementing Color Management</a></h2>
+
+<p>CUPS uses ICC color profiles to provide more accurate color reproduction. The <a href='spec-ppd.html#cupsICCProfile'><tt>cupsICCProfile</tt></a> attribute defines the color profiles that are available for a given printer, for example:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsICCProfile "ColorModel.MediaType.Resolution/Description" /path/to/ICC/profile
+</pre>
+
+<p>where "ColorModel.MediaType.Resolution" defines a selector based on the corresponding option selections. A simple driver might only define profiles for the color models that are supported, for example a printer supporting Gray and RGB might use:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsICCProfile "Gray../Grayscale Profile" /path/to/ICC/gray-profile
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsICCProfile "RGB../Full Color Profile" /path/to/ICC/rgb-profile
+</pre>
+
+<p>The options used for profile selection can be customized using the <tt>cupsICCQualifier2</tt> and <tt>cupsICCQualifier3</tt> attributes.</p>
+
+
+<h2 class='title'><a name='MACOSX'>Adding OS X Features</a></h2>
+
+<p>OS X printer drivers can provide <a href='spec-ppd.html#MACOSX'>additional attributes</a> to specify additional option panes in the print dialog, an image of the printer, a help book, and option presets for the driver software:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APDialogExtension "" /Library/Printers/Vendor/filename.plugin
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APHelpBook "" /Library/Printers/Vendor/filename.bundle
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APPrinterIconPath "" /Library/Printers/Vendor/filename.icns
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APPrinterPreset "name/text" "*option choice ..."
+</pre>
diff --git a/cups/libs/filter/ppd-compiler.header b/cups/libs/filter/ppd-compiler.header
new file mode 100644
index 000000000..2caf3aecd
--- /dev/null
+++ b/cups/libs/filter/ppd-compiler.header
@@ -0,0 +1,40 @@
+<!--
+ "$Id$"
+
+ PPD compiler documentation for CUPS.
+
+ Copyright 2007-2012 by Apple Inc.
+ Copyright 1997-2007 by Easy Software Products.
+
+ These coded instructions, statements, and computer programs are the
+ property of Apple Inc. and are protected by Federal copyright
+ law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ which should have been included with this file. If this file is
+ file is missing or damaged, see the license at "http://www.cups.org/".
+-->
+
+<h1 class='title'>Introduction to the PPD Compiler</h1>
+
+<P>This document describes how to use the CUPS PostScript Printer Description
+(PPD) file compiler. The PPD compiler generates PPD files from simple text files
+that describe the features and capabilities of one or more printers.</P>
+
+<BLOCKQUOTE><B>Note:</B>
+
+<P>The PPD compiler and related tools are deprecated and will be removed in a future release of CUPS.</P>
+
+</BLOCKQUOTE>
+
+<div class='summary'><table summary='General Information'>
+<tbody>
+<tr>
+ <th>See Also</th>
+ <td>Programming: <a href='raster-driver.html'>Developing Raster Printer Drivers</a><br>
+ Programming: <a href='postscript-driver.html'>Developing PostScript Printer Drivers</a><br>
+ Programming: <a href='api-filter.html'>Filter and Backend Programming</a><br>
+ Programming: <a href='api-raster.html'>Raster API</a><br>
+ References: <a href='ref-ppdcfile.html'>PPD Compiler Driver Information File Reference</a><br>
+ Specifications: <a href='spec-ppd.html'>CUPS PPD Extensions</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/filter/ppd-compiler.shtml b/cups/libs/filter/ppd-compiler.shtml
new file mode 100644
index 000000000..dca2870f2
--- /dev/null
+++ b/cups/libs/filter/ppd-compiler.shtml
@@ -0,0 +1,883 @@
+<h2 class='title'><a name='BASICS'>The Basics</a></h2>
+
+<P>The PPD compiler, <a href='man-ppdc.html'><code>ppdc(1)</code></a>, is a
+simple command-line tool that takes a single <I>driver information file</I>,
+which by convention uses the extension <VAR>.drv</VAR>, and produces one or more
+PPD files that may be distributed with your printer drivers for use with CUPS.
+For example, you would run the following command to create the English language
+PPD files defined by the driver information file <VAR>mydrivers.drv</VAR>:</P>
+
+<pre class='command'>
+ppdc mydrivers.drv
+</pre>
+
+<P>The PPD files are placed in a subdirectory called
+<VAR>ppd</VAR>. The <TT>-d</TT> option is used to put the PPD
+files in a different location, for example:</p>
+
+<pre class='command'>
+ppdc -d myppds mydrivers.drv
+</pre>
+
+<P>places the PPD files in a subdirectory named
+<VAR>myppds</VAR>. Finally, use the <TT>-l</TT> option to
+specify the language localization for the PPD files that are
+created, for example:</P>
+
+<pre class='command'>
+ppdc -d myppds/de -l de mydrivers.drv
+ppdc -d myppds/en -l en mydrivers.drv
+ppdc -d myppds/es -l es mydrivers.drv
+ppdc -d myppds/fr -l fr mydrivers.drv
+ppdc -d myppds/it -l it mydrivers.drv
+</pre>
+
+<P>creates PPD files in German (de), English (en), Spanish (es),
+French (fr), and Italian (it) in the corresponding
+subdirectories. Specify multiple languages (separated by commas) to produce
+"globalized" PPD files:</p>
+
+<pre class='command'>
+ppdc -d myppds -l de,en,es,fr,it mydrivers.drv
+</pre>
+
+
+<h2 class='title'><a name='DRV'>Driver Information Files</a></h2>
+
+<P>The driver information files accepted by the PPD compiler are
+plain text files that define the various attributes and options
+that are included in the PPD files that are generated. A driver
+information file can define the information for one or more printers and
+their corresponding PPD files.</P>
+
+<p class='example'><a name="LISTING1">Listing 1: "examples/minimum.drv"</a></p>
+
+<pre class='example'>
+<I>// Include standard font and media definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+<I>// List the fonts that are supported, in this case all standard fonts...</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+<I>// Manufacturer, model name, and version</I>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+<I>// Each filter provided by the driver...</I>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 rastertofoo
+
+<I>// Supported page sizes</I>
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+<I>// Supported resolutions</I>
+*<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+
+<I>// Specify the name of the PPD file we want to generate...</I>
+<a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+</pre>
+
+
+<h3><a name='SIMPLE'>A Simple Example</a></h3>
+
+<P>The example in <A HREF="#LISTING1">Listing 1</A> shows a driver information
+file which defines the minimum required attributes to provide a valid PPD file.
+The first part of the file includes standard definition files for fonts and
+media sizes:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+</pre>
+
+<P>The <TT>#include</TT> directive works just like the C/C++ include directive;
+files included using the angle brackets (<TT>&lt;filename&gt;</TT>) are found
+in any of the standard include directories and files included using quotes
+(<TT>"filename"</TT>) are found in the same directory as the source or include
+file. The <TT>&lt;font.defs&gt;</TT> include file defines the standard fonts
+which are included with GPL Ghostscript and the Apple PDF RIP, while the
+<TT>&lt;media.defs&gt;</TT> include file defines the standard media sizes
+listed in Appendix B of the Adobe PostScript Printer Description File Format
+Specification.</P>
+
+<P>CUPS provides several other standard include files:</P>
+
+<UL>
+
+ <LI><TT>&lt;epson.h&gt;</TT> - Defines all of the rastertoepson driver
+ constants.</LI>
+
+ <LI><TT>&lt;escp.h&gt;</TT> - Defines all of the rastertoescpx driver
+ constants.</LI>
+
+ <LI><TT>&lt;hp.h&gt;</TT> - Defines all of the rastertohp driver
+ constants.</LI>
+
+ <LI><TT>&lt;label.h&gt;</TT> - Defines all of the rastertolabel driver
+ constants.</LI>
+
+ <LI><TT>&lt;pcl.h&gt;</TT> - Defines all of the rastertopclx driver
+ constants.</LI>
+
+ <LI><TT>&lt;raster.defs&gt;</TT> - Defines all of the CUPS raster format
+ constants.</LI>
+
+</UL>
+
+<P>Next we list all of the fonts that are available in the driver; for CUPS
+raster drivers, the following line is all that is usually supplied:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+</pre>
+
+<P>The <TT>Font</TT> directive specifies the name of a single font or the
+asterisk to specify all fonts. For example, you would use the following line to
+define an additional bar code font that you are supplying with your printer
+driver:</P>
+
+<pre class='example'>
+<I>// name encoding version charset status</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> Barcode-Foo Special "(1.0)" Special ROM
+</pre>
+
+<P>The name of the font is <TT>Barcode-Foo</TT>. Since it is not a standard
+text font, the encoding and charset name <TT>Special</TT> is used. The version
+number is <TT>1.0</TT> and the status (where the font is located) is
+<TT>ROM</TT> to indicate that the font does not need to be embedded in
+documents that use the font for this printer.</P>
+
+<P>Third comes the manufacturer, model name, and version number information
+strings:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+</pre>
+
+<P>These strings are used when the user (or auto-configuration program) selects
+the printer driver for a newly connected device.</p>
+
+<P>The list of filters comes after the information strings; for the example in
+<A HREF="#LISTING1">Listing 1</A>, we have a single filter that takes CUPS
+raster data:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 rastertofoo
+</pre>
+
+<P>Each filter specified in the driver information file is the equivalent of a
+printer driver for that format; if a user submits a print job in a different
+format, CUPS figures out the sequence of commands that will produce a supported
+format for the least relative cost.</P>
+
+<P>Once we have defined the driver information we specify the supported options.
+For the example driver we support a single resolution of 600 dots per inch and
+two media sizes, A4 and Letter:</P>
+
+<pre class='example'>
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+*<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+</pre>
+
+<P>The asterisk in front of the <TT>MediaSize</TT> and <TT>Resolution</TT>
+directives specify that those option choices are the default. The
+<TT>MediaSize</TT> directive is followed by a media size name which is normally
+defined in the <TT>&lt;media.defs&gt;</TT> file and corresponds to a standard
+Adobe media size name. If the default media size is <TT>Letter</TT>, the PPD
+compiler will override it to be <TT>A4</TT> for non-English localizations for
+you automatically.</P>
+
+<P>The <TT>Resolution</TT> directive accepts several values after it as
+follows:</P>
+
+<OL>
+
+ <LI>Colorspace for this resolution, if any. In the example file, the
+ colorspace <TT>k</TT> is used which corresponds to black. For printer
+ drivers that support color printing, this field is usually specified as
+ "-" for "no change".</LI>
+
+ <LI>Bits per color. In the example file, we define 8 bits per color, for
+ a continuous-tone grayscale output. All versions of CUPS support 1 and
+ 8 bits per color. CUPS 1.2 and higher (OS X 10.5 and higher) also
+ supports 16 bits per color.</LI>
+
+ <LI>Rows per band. In the example file, we define 0 rows per band to
+ indicate that our printer driver does not process the page in
+ bands.</LI>
+
+ <LI>Row feed. In the example, we define the feed value to be 0 to
+ indicate that our printer driver does not interleave the output.</LI>
+
+ <LI>Row step. In the example, we define the step value to be 0 to
+ indicate that our printer driver does not interleave the output. This
+ value normally indicates the spacing between the nozzles of an inkjet
+ printer - when combined with the previous two values, it informs the
+ driver how to stagger the output on the page to produce interleaved
+ lines on the page for higher-resolution output.</LI>
+
+ <LI>Choice name and text. In the example, we define the choice name and
+ text to be <TT>"600dpi/600 DPI"</TT>. The name and text are separated by
+ slash (<TT>/</TT>) character; if no text is specified, then the name is
+ used as the text. The PPD compiler parses the name to determine the
+ actual resolution; the name can be of the form
+ <TT><I>RESOLUTION</I>dpi</TT> for resolutions that are equal
+ horizontally and vertically or <TT><I>HRES</I>x<I>VRES</I>dpi</TT> for
+ isometric resolutions. Only integer resolution values are supported, so
+ a resolution name of <TT>300dpi</TT> is valid while <TT>300.1dpi</TT> is
+ not.</LI>
+
+</OL>
+
+<P>Finally, the <TT>PCFileName</TT> directive specifies that the named PPD file
+should be written for the current driver definitions:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+</pre>
+
+<P>The filename follows the directive and <I>must</I> conform to the Adobe
+filename requirements in the Adobe Postscript Printer Description File Format
+Specification. Specifically, the filename may not exceed 8 characters followed
+by the extension <VAR>.ppd</VAR>. The <TT>FileName</TT> directive can be used to
+specify longer filenames:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#FileName'>FileName</a> "FooJet 2000"
+</pre>
+
+
+<h3><a name='GROUPING'>Grouping and Inheritance</a></h3>
+
+<P>The previous example created a single PPD file. Driver information files can
+also define multiple printers by using the PPD compiler grouping functionality.
+Directives are grouped using the curly braces (<TT>{</TT> and <TT>}</TT>) and
+every group that uses the <TT>PCFileName</TT> or <TT>FileName</TT> directives
+produces a PPD file with that name. <A HREF="#LISTING2">Listing 2</A> shows a
+variation of the original example that uses two groups to define two printers
+that share the same printer driver filter but provide two different resolution
+options.</P>
+
+<p class='example'><a name="LISTING2">Listing 2: "examples/grouping.drv"</a></p>
+
+<pre class='example'>
+
+<I>// Include standard font and media definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+<I>// List the fonts that are supported, in this case all standard fonts...</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+<I>// Manufacturer and version</I>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+<I>// Each filter provided by the driver...</I>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 rastertofoo
+
+<I>// Supported page sizes</I>
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+{
+ <I>// Supported resolutions</I>
+ *<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+
+ <I>// Specify the model name and filename...</I>
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+}
+
+{
+ <I>// Supported resolutions</I>
+ *<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "1200dpi/1200 DPI"
+
+ <I>// Specify the model name and filename...</I>
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2001"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojt2k1.ppd"
+}
+</pre>
+
+<P>The second example is essentially the same as the first, except that each
+printer model is defined inside of a pair of curly braces. For example, the
+first printer is defined using:</P>
+
+<pre class='example'>
+{
+ // Supported resolutions
+ *<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+
+ // Specify the model name and filename...
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+}
+</pre>
+
+<P>The printer <I>inherits</I> all of the definitions from the parent group (the
+top part of the file) and adds the additional definitions inside the curly
+braces for that printer driver. When we define the second group, it also
+inherits the same definitions from the parent group but <I>none</I> of the
+definitions from the first driver. Groups can be nested to any number of levels
+to support variations of similar models without duplication of information.</P>
+
+
+<h3><a name='COLOR'>Color Support</a></h3>
+
+<P>For printer drivers that support color printing, the
+<TT>ColorDevice</TT> and <TT>ColorModel</TT> directives should be
+used to tell the printing system that color output is desired
+and in what formats. <A HREF="#LISTING3">Listing 3</A> shows a
+variation of the previous example which includes a color printer
+that supports printing at 300 and 600 DPI.</P>
+
+<P>The key changes are the addition of the <TT>ColorDevice</TT>
+directive:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#ColorDevice'>ColorDevice</a> true
+</pre>
+
+<P>which tells the printing system that the printer supports
+color printing, and the <TT>ColorModel</TT> directives:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#ColorModel'>ColorModel</a> Gray/Grayscale w chunky 0
+*<a href='ref-ppdcfile.html#ColorModel'>ColorModel</a> RGB/Color rgb chunky 0
+</pre>
+
+<P>which tell the printing system which colorspaces are supported by the printer
+driver for color printing. Each of the <TT>ColorModel</TT> directives is
+followed by the option name and text (<TT>Gray/Grayscale</TT> and
+<TT>RGB/Color</TT>), the colorspace name (<TT>w</TT> and <TT>rgb</TT>), the
+color organization (<TT>chunky</TT>), and the compression mode number
+(<TT>0</TT>) to be passed to the driver. The option name can be any of the
+standard Adobe <TT>ColorModel</TT> names:</P>
+
+<UL>
+
+ <LI><TT>Gray</TT> - Grayscale output.
+
+ <LI><TT>RGB</TT> - Color output, typically using the RGB
+ colorspace, but without a separate black channel.
+
+ <LI><TT>CMYK</TT> - Color output with a separate black
+ channel.
+
+</UL>
+
+<P>Custom names can be used, however it is recommended that you use your vendor
+prefix for any custom names, for example "fooName".</P>
+
+<P>The colorspace name can be any of the following universally supported
+colorspaces:</P>
+
+<UL>
+ <LI><TT>w</TT> - Luminance</LI>
+
+ <LI><TT>rgb</TT> - Red, green, blue</LI>
+
+ <LI><TT>k</TT> - Black</LI>
+
+ <LI><TT>cmy</TT> - Cyan, magenta, yellow</LI>
+
+ <LI><TT>cmyk</TT> - Cyan, magenta, yellow, black</LI>
+
+</UL>
+
+<P>The color organization can be any of the following values:</P>
+
+<UL>
+
+ <LI><TT>chunky</TT> - Color values are passed together on a line
+ as RGB RGB RGB RGB</LI>
+
+ <LI><TT>banded</TT> - Color values are passed separately
+ on a line as RRRR GGGG BBBB; not supported by the Apple
+ RIP filters</LI>
+
+ <LI><TT>planar</TT> - Color values are passed separately
+ on a page as RRRR RRRR RRRR ... GGGG GGGG GGGG ... BBBB
+ BBBB BBBB; not supported by the Apple RIP filters</LI>
+
+</UL>
+
+<P>The compression mode value is passed to the driver in the
+<TT>cupsCompression</TT> attribute. It is traditionally used to select an
+appropriate compression mode for the color model but can be used for any
+purpose, such as specifying a photo mode vs. standard mode.</P>
+
+<p class='example'><a name="LISTING3">Listing 3: "examples/color.drv"</a></p>
+
+<pre class='example'>
+
+<I>// Include standard font and media definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+<I>// List the fonts that are supported, in this case all standard fonts...</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+<I>// Manufacturer and version</I>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+<I>// Each filter provided by the driver...</I>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 rastertofoo
+
+<I>// Supported page sizes</I>
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+{
+ <I>// Supported resolutions</I>
+ *<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+
+ <I>// Specify the model name and filename...</I>
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+}
+
+{
+ <I>// Supports color printing</I>
+ <a href='ref-ppdcfile.html#ColorDevice'>ColorDevice</a> true
+
+ <I>// Supported colorspaces</I>
+ <a href='ref-ppdcfile.html#ColorModel'>ColorModel</a> Gray/Grayscale w chunky 0
+ *<a href='ref-ppdcfile.html#ColorModel'>ColorModel</a> RGB/Color rgb chunky 0
+
+ <I>// Supported resolutions</I>
+ *<a href='ref-ppdcfile.html#Resolution'>Resolution</a> - 8 0 0 0 "300dpi/300 DPI"
+ <a href='ref-ppdcfile.html#Resolution'>Resolution</a> - 8 0 0 0 "600dpi/600 DPI"
+
+ <I>// Specify the model name and filename...</I>
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet Color"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojetco.ppd"
+}
+</pre>
+
+
+<h3><a name='OPTIONS'>Defining Custom Options and Option Groups</a></h3>
+
+<P>The <TT>Group</TT>, <TT>Option</TT>, and <TT>Choice</TT>
+directives are used to define or select a group, option, or
+choice. <A HREF="#LISTING4">Listing 4</A> shows a variation of
+the first example that provides two custom options in a group
+named "Footasm".</P>
+
+<p class='example'><a name="LISTING4">Listing 4: "examples/custom.drv"</a></p>
+
+<pre class='example'>
+
+<I>// Include standard font and media definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+<I>// List the fonts that are supported, in this case all standard fonts...</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+<I>// Manufacturer, model name, and version</I>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+<I>// Each filter provided by the driver...</I>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 rastertofoo
+
+<I>// Supported page sizes</I>
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+<I>// Supported resolutions</I>
+*<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+
+<I>// Option Group</I>
+<a href='ref-ppdcfile.html#Group'>Group</a> "Footasm"
+
+ <I>// Boolean option</I>
+ <a href='ref-ppdcfile.html#Option'>Option</a> "fooEnhance/Resolution Enhancement" Boolean AnySetup 10
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> True/Yes "&lt;&lt;/cupsCompression 1&gt;&gt;setpagedevice"
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> False/No "&lt;&lt;/cupsCompression 0&gt;&gt;setpagedevice"
+
+ <I>// Multiple choice option</I>
+ <a href='ref-ppdcfile.html#Option'>Option</a> "fooOutputType/Output Quality" PickOne AnySetup 10
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "Auto/Automatic Selection"
+ "&lt;&lt;/OutputType(Auto)&gt;&gt;setpagedevice""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "Text/Optimize for Text"
+ "&lt;&lt;/OutputType(Text)&gt;&gt;setpagedevice""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "Graph/Optimize for Graphics"
+ "&lt;&lt;/OutputType(Graph)&gt;&gt;setpagedevice""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "Photo/Optimize for Photos"
+ "&lt;&lt;/OutputType(Photo)&gt;&gt;setpagedevice""
+
+<I>// Specify the name of the PPD file we want to generate...</I>
+<a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+</pre>
+
+<P>The custom group is introduced by the <TT>Group</TT>
+directive which is followed by the name and optionally text for
+the user:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Group'>Group</a> "Footasm/Footastic Options"
+</pre>
+
+<P>The group name must conform to the PPD specification and
+cannot exceed 40 characters in length. If you specify user text,
+it cannot exceed 80 characters in length. The groups
+<TT>General</TT>, <TT>Extra</TT>, and
+<TT>InstallableOptions</TT> are predefined by CUPS; the general
+and extra groups are filled by the UI options defined by the PPD
+specification. The <TT>InstallableOptions</TT> group is reserved
+for options that define whether accessories for the printer
+(duplexer unit, finisher, stapler, etc.) are installed.</P>
+
+<P>Once the group is specified, the <TT>Option</TT> directive is
+used to introduce a new option:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Option'>Option</a> "fooEnhance/Resolution Enhancement" Boolean AnySetup 10
+</pre>
+
+<P>The directive is followed by the name of the option and any
+optional user text, the option type, the PostScript document group, and
+the sort order number. The option name must conform to the PPD specification
+and cannot exceed 40 characters in length. If you specify user text, it
+cannot exceed 80 characters in length.</P>
+
+<P>The option type can be <TT>Boolean</TT> for true/false
+selections, <TT>PickOne</TT> for picking one of many choices, or
+<TT>PickMany</TT> for picking zero or more choices. Boolean
+options can have at most two choices with the names
+<TT>False</TT> and <TT>True</TT>. Pick options can have any
+number of choices, although for Windows compatibility reasons
+the number of choices should not exceed 255.</P>
+
+<P>The PostScript document group is typically <TT>AnySetup</TT>,
+meaning that the option can be introduced at any point in the
+PostScript document. Other values include <TT>PageSetup</TT> to
+include the option before each page and <TT>DocumentSetup</TT>
+to include the option once at the beginning of the document.</P>
+
+<P>The sort order number is used to sort the printer commands
+associated with each option choice within the PostScript
+document. This allows you to setup certain options before others
+as required by the printer. For most CUPS raster printer
+drivers, the value <TT>10</TT> can be used for all options.</P>
+
+<P>Once the option is specified, each option choice can be
+listed using the <TT>Choice</TT> directive:</P>
+
+<pre class='example'>
+*<a href='ref-ppdcfile.html#Choice'>Choice</a> True/Yes "&lt;&lt;/cupsCompression 1&gt;&gt;setpagedevice"
+<a href='ref-ppdcfile.html#Choice'>Choice</a> False/No "&lt;&lt;/cupsCompression 0&gt;&gt;setpagedevice"
+</pre>
+
+<P>The directive is followed by the choice name and optionally
+user text, and the PostScript commands that should be inserted
+when printing a file to this printer. The option name must
+conform to the PPD specification and cannot exceed 40 characters
+in length. If you specify user text, it cannot exceed 80
+characters in length.</P>
+
+<P>The PostScript commands are also interpreted by any RIP
+filters, so these commands typically must be present for all
+option choices. Most commands take the form:</P>
+
+<pre class='example'>
+&lt;&lt;/name value&gt;&gt;setpagedevice
+</pre>
+
+<P>where <TT>name</TT> is the name of the PostScript page device
+attribute and <TT>value</TT> is the numeric or string value for
+that attribute.</P>
+
+
+<h3><a name='DEFINE'>Defining Constants</a></h3>
+
+<P>Sometimes you will want to define constants for your drivers
+so that you can share values in different groups within the same
+driver information file, or to share values between different
+driver information files using the <TT>#include</TT> directive.
+The <TT>#define</TT> directive is used to define constants for
+use in your printer definitions:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_define'>#define</a> NAME value
+</pre>
+
+<P>The <TT>NAME</TT> is any sequence of letters, numbers, and
+the underscore. The <TT>value</TT> is a number or string; if the
+value contains spaces you must put double quotes around it, for
+example:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_define'>#define</a> FOO "My String Value"
+</pre>
+
+<P>Constants can also be defined on the command-line using the <tt>-D</tt>
+option:</P>
+
+<pre class='command'>
+ppdc -DNAME="value" filename.drv
+</pre>
+
+<P>Once defined, you use the notation <TT>$NAME</TT> to substitute the value of
+the constant in the file, for example:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_define'>#define</a> MANUFACTURER "Foo"
+<a href='ref-ppdcfile.html#_define'>#define</a> FOO_600 0
+<a href='ref-ppdcfile.html#_define'>#define</a> FOO_1200 1
+
+{
+ <a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "$MANUFACTURER"
+ <a href='ref-ppdcfile.html#ModelNumber'>ModelNumber</a> $FOO_600
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+ ...
+}
+
+{
+ <a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "$MANUFACTURER"
+ <a href='ref-ppdcfile.html#ModelNumber'>ModelNumber</a> $FOO_1200
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2001"
+ ...
+}
+</pre>
+
+<P>Numeric constants can be bitwise OR'd together by placing the constants
+inside parenthesis, for example:</P>
+
+<pre class='example'>
+<I>// ModelNumber capability bits</I>
+<a href='ref-ppdcfile.html#_define'>#define</a> DUPLEX 1
+<a href='ref-ppdcfile.html#_define'>#define</a> COLOR 2
+
+...
+
+{
+ <I>// Define a model number specifying the capabilities of the printer...</I>
+ <a href='ref-ppdcfile.html#ModelNumber'>ModelNumber</a> ($DUPLEX $COLOR)
+ ...
+}
+</pre>
+
+
+<h3><a name='CONDITIONAL'>Conditional Statements</a></h3>
+
+<p>The PPD compiler supports conditional compilation using the <tt>#if</tt>,
+<tt>#elif</tt>, <tt>#else</tt>, and <tt>#endif</tt> directives. The <tt>#if</tt>
+and <tt>#elif</tt> directives are followed by a constant name or an expression.
+For example, to include a group of options when "ADVANCED" is defined:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_if'>#if</a> ADVANCED
+<a href='ref-ppdcfile.html#Group'>Group</a> "Advanced/Advanced Options"
+ <a href='ref-ppdcfile.html#Option'>Option</a> "fooCyanAdjust/Cyan Adjustment"
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus10/+10%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus5/+5%" ""
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "none/No Adjustment" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus5/-5%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus10/-10%" ""
+ <a href='ref-ppdcfile.html#Option'>Option</a> "fooMagentaAdjust/Magenta Adjustment"
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus10/+10%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus5/+5%" ""
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "none/No Adjustment" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus5/-5%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus10/-10%" ""
+ <a href='ref-ppdcfile.html#Option'>Option</a> "fooYellowAdjust/Yellow Adjustment"
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus10/+10%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus5/+5%" ""
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "none/No Adjustment" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus5/-5%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus10/-10%" ""
+ <a href='ref-ppdcfile.html#Option'>Option</a> "fooBlackAdjust/Black Adjustment"
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus10/+10%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "plus5/+5%" ""
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "none/No Adjustment" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus5/-5%" ""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "minus10/-10%" ""
+<a href='ref-ppdcfile.html#_endif'>#endif</a>
+</pre>
+
+
+<h3><a name='CONSTRAINTS'>Defining Constraints</a></h3>
+
+<P>Constraints are strings that are used to specify that one or more option
+choices are incompatible, for example two-sided printing on transparency media.
+Constraints are also used to prevent the use of uninstalled features such as the
+duplexer unit, additional media trays, and so forth.</P>
+
+<P>The <TT>UIConstraints</TT> directive is used to specify a constraint that is
+placed in the PPD file. The directive is followed by a string using one of the
+following formats:</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*Option1 *Option2"
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*Option1 Choice1 *Option2"
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*Option1 *Option2 Choice2"
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*Option1 Choice1 *Option2 Choice2"
+</pre>
+
+<P>Each option name is preceded by the asterisk (<TT>*</TT>). If no choice is
+given for an option, then all choices <I>except</I> <TT>False</TT> and
+<TT>None</TT> will conflict with the other option and choice(s). Since the PPD
+compiler automatically adds reciprocal constraints (option A conflicts with
+option B, so therefore option B conflicts with option A), you need only specify
+the constraint once.</P>
+
+<p class='example'><a name="LISTING5">Listing 5: "examples/constraint.drv"</a></p>
+
+<pre class='example'>
+
+<I>// Include standard font and media definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+<I>// List the fonts that are supported, in this case all standard fonts...</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+<I>// Manufacturer, model name, and version</I>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "Foo"
+<a href='ref-ppdcfile.html#ModelName'>ModelName</a> "FooJet 2000"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+<I>// Each filter provided by the driver...</I>
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 rastertofoo
+
+<I>// Supported page sizes</I>
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+
+<I>// Supported resolutions</I>
+*<a href='ref-ppdcfile.html#Resolution'>Resolution</a> k 8 0 0 0 "600dpi/600 DPI"
+
+<I>// Installable Option Group</I>
+<a href='ref-ppdcfile.html#Group'>Group</a> "InstallableOptions/Options Installed"
+
+ <I>// Duplexing unit option</I>
+ <a href='ref-ppdcfile.html#Option'>Option</a> "OptionDuplexer/Duplexing Unit" Boolean AnySetup 10
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> True/Installed ""
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "False/Not Installed" ""
+
+<I>// General Option Group</I>
+<a href='ref-ppdcfile.html#Group'>Group</a> General
+
+ <I>// Duplexing option</I>
+ <a href='ref-ppdcfile.html#Option'>Option</a> "Duplex/Two-Sided Printing" PickOne AnySetup 10
+ *<a href='ref-ppdcfile.html#Choice'>Choice</a> "None/No" "&lt;&lt;/Duplex false&gt;&gt;setpagedevice""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "DuplexNoTumble/Long Edge Binding"
+ "&lt;&lt;/Duplex true/Tumble false&gt;&gt;setpagedevice""
+ <a href='ref-ppdcfile.html#Choice'>Choice</a> "DuplexTumble/Short Edge Binding"
+ "&lt;&lt;/Duplex true/Tumble true&gt;&gt;setpagedevice""
+
+<I>// Only allow duplexing if the duplexer is installed</I>
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*Duplex *OptionDuplexer False"
+
+<I>// Specify the name of the PPD file we want to generate...</I>
+<a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "foojet2k.ppd"
+</pre>
+
+<P><A HREF="#LISTING5">Listing 5</A> shows a variation of the first example with
+an added <TT>Duplex</TT> option and installable option for the duplexer,
+<TT>OptionDuplex</TT>. A constraint is added at the end to specify that any
+choice of the <TT>Duplex</TT> option that is not <TT>None</TT> is incompatible
+with the "Duplexer Installed" option set to "Not Installed"
+(<TT>False</TT>):</P>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*Duplex *OptionDuplexer False"
+</pre>
+
+<h4>Enhanced Constraints</h4>
+
+<p>CUPS 1.4 supports constraints between 2 or more options using the
+<TT>Attribute</TT> directive. <TT>cupsUIConstraints</TT> attributes define
+the constraints, while <TT>cupsUIResolver</TT> attributes define option changes
+to resolve constraints. For example, we can specify the previous duplex
+constraint with a resolver that turns off duplexing with the following two
+lines:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsUIConstraints DuplexOff "*Duplex *OptionDuplexer False"
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsUIResolver DuplexOff "*Duplex None"
+</pre>
+
+<h2 class='title'><a name='LOCALIZATION'>Localization</a></h2>
+
+<p>The PPD compiler provides localization of PPD files in different languages
+through <i>message catalog</i> files in the GNU gettext or Apple .strings
+formats. Each user text string and several key PPD attribute values such as
+<tt>LanguageVersion</tt> and <tt>LanguageEncoding</tt> are looked up in the
+corresponding message catalog and the translated text is substituted in the
+generated PPD files. One message catalog file can be used by multiple driver
+information files, and each file contains a single language translation.</p>
+
+<h3><a name='PPDPO'>The ppdpo Utility</a></h3>
+
+<p>While CUPS includes localizations of all standard media sizes and options in
+several languages, your driver information files may provide their own media
+sizes and options that need to be localized. CUPS provides a utility program to
+aid in the localization of drivers called <a
+href='man-ppdpo.html'><tt>ppdpo(1)</tt></a>. The <tt>ppdpo</tt> program creates
+or updates a message catalog file based upon one or more driver information
+files. New messages are added with the word "TRANSLATE" added to the front of
+the translation string to make locating new strings for translation easier. The
+program accepts the message catalog filename and one or more driver information
+files.</p>
+
+<p>For example, run the following command to create a new German message catalog
+called <var>de.po</var> for all of the driver information files in the current
+directory:</p>
+
+<pre class='command'>
+ppdpo -o de.po *.drv
+</pre>
+
+<p>If the file <var>de.po</var> already exists, <tt>ppdpo</tt> will update the
+contents of the file with any new messages that need to be translated. To create
+an Apple .strings file instead, specify the output filename with a .strings
+extension, for example:</p>
+
+<pre class='command'>
+ppdpo -o de.strings *.drv
+</pre>
+
+<h3><a name='PPDC_CATALOG'>Using Message Catalogs with the PPD Compiler</a></h3>
+
+<p>Once you have created a message catalog, use the <a
+href='ref-ppdcfile.html#_po'><tt>#po</tt></a> directive to declare it in each
+driver information file. For example, to declare the German message catalog for
+a driver use:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_po'>#po</a> de "de.po" // German
+</pre>
+
+<p>In fact, you can use the <tt>#po</tt> directive as many times as needed:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#_po'>#po</a> de "de.po" // German
+<a href='ref-ppdcfile.html#_po'>#po</a> es "es.po" // Spanish
+<a href='ref-ppdcfile.html#_po'>#po</a> fr "fr.po" // French
+<a href='ref-ppdcfile.html#_po'>#po</a> it "it.po" // Italian
+<a href='ref-ppdcfile.html#_po'>#po</a> ja "ja.po" // Japanese
+</pre>
+
+<p>The filename ("de.po", etc.) can be relative to the location of the driver
+information file or an absolute path. Once defined, the PPD compiler will
+automatically generate a globalized PPD for every language declared in your
+driver information file. To generate a single-language PPD file, simply use the
+<tt>-l</tt> option to list the corresponding locale, for example:</p>
+
+<pre class='command'>
+ppdc -l de -d ppd/de mydrivers.drv
+</pre>
+
+<p>to generate German PPD files.</p>
diff --git a/cups/libs/filter/pstops.c b/cups/libs/filter/pstops.c
new file mode 100644
index 000000000..49a6fa542
--- /dev/null
+++ b/cups/libs/filter/pstops.c
@@ -0,0 +1,3434 @@
+/*
+ * "$Id: pstops.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * PostScript filter for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1993-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ * add_page() - Add a page to the pages array.
+ * cancel_job() - Flag the job as canceled.
+ * check_range() - Check to see if the current page is selected for
+ * printing.
+ * copy_bytes() - Copy bytes from the input file to stdout.
+ * copy_comments() - Copy all of the comments section.
+ * copy_dsc() - Copy a DSC-conforming document.
+ * copy_non_dsc() - Copy a document that does not conform to the DSC.
+ * copy_page() - Copy a page description.
+ * copy_prolog() - Copy the document prolog section.
+ * copy_setup() - Copy the document setup section.
+ * copy_trailer() - Copy the document trailer.
+ * do_prolog() - Send the necessary document prolog commands.
+ * do_setup() - Send the necessary document setup commands.
+ * doc_printf() - Send a formatted string to stdout and/or the temp
+ * file.
+ * doc_puts() - Send a nul-terminated string to stdout and/or the
+ * temp file.
+ * doc_write() - Send data to stdout and/or the temp file.
+ * end_nup() - End processing for N-up printing.
+ * include_feature() - Include a printer option/feature command.
+ * parse_text() - Parse a text value in a comment.
+ * set_pstops_options() - Set pstops options.
+ * skip_page() - Skip past a page that won't be printed.
+ * start_nup() - Start processing for N-up printing.
+ * write_label_prolog() - Write the prolog with the classification and page
+ * label.
+ * write_labels() - Write the actual page labels.
+ * write_options() - Write options provided via %%IncludeFeature.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+#include <limits.h>
+#include <math.h>
+#include <cups/file.h>
+#include <cups/array.h>
+#include <cups/language-private.h>
+#include <signal.h>
+
+
+/*
+ * Constants...
+ */
+
+#define PSTOPS_BORDERNONE 0 /* No border or hairline border */
+#define PSTOPS_BORDERTHICK 1 /* Think border */
+#define PSTOPS_BORDERSINGLE 2 /* Single-line hairline border */
+#define PSTOPS_BORDERSINGLE2 3 /* Single-line thick border */
+#define PSTOPS_BORDERDOUBLE 4 /* Double-line hairline border */
+#define PSTOPS_BORDERDOUBLE2 5 /* Double-line thick border */
+
+#define PSTOPS_LAYOUT_LRBT 0 /* Left to right, bottom to top */
+#define PSTOPS_LAYOUT_LRTB 1 /* Left to right, top to bottom */
+#define PSTOPS_LAYOUT_RLBT 2 /* Right to left, bottom to top */
+#define PSTOPS_LAYOUT_RLTB 3 /* Right to left, top to bottom */
+#define PSTOPS_LAYOUT_BTLR 4 /* Bottom to top, left to right */
+#define PSTOPS_LAYOUT_TBLR 5 /* Top to bottom, left to right */
+#define PSTOPS_LAYOUT_BTRL 6 /* Bottom to top, right to left */
+#define PSTOPS_LAYOUT_TBRL 7 /* Top to bottom, right to left */
+
+#define PSTOPS_LAYOUT_NEGATEY 1 /* The bits for the layout */
+#define PSTOPS_LAYOUT_NEGATEX 2 /* definitions above... */
+#define PSTOPS_LAYOUT_VERTICAL 4
+
+
+/*
+ * Types...
+ */
+
+typedef struct /**** Page information ****/
+{
+ char *label; /* Page label */
+ int bounding_box[4]; /* PageBoundingBox */
+ off_t offset; /* Offset to start of page */
+ ssize_t length; /* Number of bytes for page */
+ int num_options; /* Number of options for this page */
+ cups_option_t *options; /* Options for this page */
+} pstops_page_t;
+
+typedef struct /**** Document information ****/
+{
+ int page; /* Current page */
+ int bounding_box[4]; /* BoundingBox from header */
+ int new_bounding_box[4]; /* New composite bounding box */
+ int num_options; /* Number of document-wide options */
+ cups_option_t *options; /* Document-wide options */
+ int normal_landscape, /* Normal rotation for landscape? */
+ saw_eof, /* Saw the %%EOF comment? */
+ slow_collate, /* Collate copies by hand? */
+ slow_duplex, /* Duplex pages slowly? */
+ slow_order, /* Reverse pages slowly? */
+ use_ESPshowpage; /* Use ESPshowpage? */
+ cups_array_t *pages; /* Pages in document */
+ cups_file_t *temp; /* Temporary file, if any */
+ char tempfile[1024]; /* Temporary filename */
+ int job_id; /* Job ID */
+ const char *user, /* User name */
+ *title; /* Job name */
+ int copies; /* Number of copies */
+ const char *ap_input_slot, /* AP_FIRSTPAGE_InputSlot value */
+ *ap_manual_feed, /* AP_FIRSTPAGE_ManualFeed value */
+ *ap_media_color, /* AP_FIRSTPAGE_MediaColor value */
+ *ap_media_type, /* AP_FIRSTPAGE_MediaType value */
+ *ap_page_region, /* AP_FIRSTPAGE_PageRegion value */
+ *ap_page_size; /* AP_FIRSTPAGE_PageSize value */
+ int collate, /* Collate copies? */
+ emit_jcl, /* Emit JCL commands? */
+ fit_to_page; /* Fit pages to media */
+ const char *input_slot, /* InputSlot value */
+ *manual_feed, /* ManualFeed value */
+ *media_color, /* MediaColor value */
+ *media_type, /* MediaType value */
+ *page_region, /* PageRegion value */
+ *page_size; /* PageSize value */
+ int mirror, /* doc->mirror/mirror pages */
+ number_up, /* Number of pages on each sheet */
+ number_up_layout, /* doc->number_up_layout of N-up pages */
+ output_order, /* Requested reverse output order? */
+ page_border; /* doc->page_border around pages */
+ const char *page_label, /* page-label option, if any */
+ *page_ranges, /* page-ranges option, if any */
+ *page_set; /* page-set option, if any */
+} pstops_doc_t;
+
+
+/*
+ * Convenience macros...
+ */
+
+#define is_first_page(p) (doc->number_up == 1 || \
+ ((p) % doc->number_up) == 1)
+#define is_last_page(p) (doc->number_up == 1 || \
+ ((p) % doc->number_up) == 0)
+#define is_not_last_page(p) (doc->number_up > 1 && \
+ ((p) % doc->number_up) != 0)
+
+
+/*
+ * Local globals...
+ */
+
+static int JobCanceled = 0;/* Set to 1 on SIGTERM */
+
+
+/*
+ * Local functions...
+ */
+
+static pstops_page_t *add_page(pstops_doc_t *doc, const char *label);
+static void cancel_job(int sig);
+static int check_range(pstops_doc_t *doc, int page);
+static void copy_bytes(cups_file_t *fp, off_t offset,
+ size_t length);
+static ssize_t copy_comments(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, char *line,
+ ssize_t linelen, size_t linesize);
+static void copy_dsc(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, char *line, ssize_t linelen,
+ size_t linesize);
+static void copy_non_dsc(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, char *line,
+ ssize_t linelen, size_t linesize);
+static ssize_t copy_page(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, int number, char *line,
+ ssize_t linelen, size_t linesize);
+static ssize_t copy_prolog(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, char *line,
+ ssize_t linelen, size_t linesize);
+static ssize_t copy_setup(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, char *line,
+ ssize_t linelen, size_t linesize);
+static ssize_t copy_trailer(cups_file_t *fp, pstops_doc_t *doc,
+ ppd_file_t *ppd, int number, char *line,
+ ssize_t linelen, size_t linesize);
+static void do_prolog(pstops_doc_t *doc, ppd_file_t *ppd);
+static void do_setup(pstops_doc_t *doc, ppd_file_t *ppd);
+static void doc_printf(pstops_doc_t *doc, const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+static void doc_puts(pstops_doc_t *doc, const char *s);
+static void doc_write(pstops_doc_t *doc, const char *s, size_t len);
+static void end_nup(pstops_doc_t *doc, int number);
+static int include_feature(ppd_file_t *ppd, const char *line,
+ int num_options,
+ cups_option_t **options);
+static char *parse_text(const char *start, char **end, char *buffer,
+ size_t bufsize);
+static void set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd,
+ char *argv[], int num_options,
+ cups_option_t *options);
+static ssize_t skip_page(cups_file_t *fp, char *line, ssize_t linelen,
+ size_t linesize);
+static void start_nup(pstops_doc_t *doc, int number,
+ int show_border, const int *bounding_box);
+static void write_label_prolog(pstops_doc_t *doc, const char *label,
+ float bottom, float top,
+ float width);
+static void write_labels(pstops_doc_t *doc, int orient);
+static void write_options(pstops_doc_t *doc, ppd_file_t *ppd,
+ int num_options, cups_option_t *options);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ pstops_doc_t doc; /* Document information */
+ cups_file_t *fp; /* Print file */
+ ppd_file_t *ppd; /* PPD file */
+ int num_options; /* Number of print options */
+ cups_option_t *options; /* Print options */
+ char line[8192]; /* Line buffer */
+ size_t len; /* Length of line buffer */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore broken pipe signals...
+ */
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc < 6 || argc > 7)
+ {
+ _cupsLangPrintf(stderr,
+ _("Usage: %s job-id user title copies options [file]"),
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * Register a signal handler to cleanly cancel a job.
+ */
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, cancel_job);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = cancel_job;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, cancel_job);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = cupsFileStdin();
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
+ {
+ _cupsLangPrintError("ERROR", _("Unable to open print file"));
+ return (1);
+ }
+ }
+
+ /*
+ * Read the first line to see if we have DSC comments...
+ */
+
+ if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
+ {
+ fputs("DEBUG: The print file is empty.\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Process command-line options...
+ */
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+ ppd = SetCommonOptions(num_options, options, 1);
+
+ set_pstops_options(&doc, ppd, argv, num_options, options);
+
+ /*
+ * Write any "exit server" options that have been selected...
+ */
+
+ ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
+
+ /*
+ * Write any JCL commands that are needed to print PostScript code...
+ */
+
+ if (doc.emit_jcl)
+ ppdEmitJCL(ppd, stdout, doc.job_id, doc.user, doc.title);
+
+ /*
+ * Start with a DSC header...
+ */
+
+ puts("%!PS-Adobe-3.0");
+
+ /*
+ * Skip leading PJL in the document...
+ */
+
+ while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
+ {
+ /*
+ * Yup, we have leading PJL fun, so skip it until we hit the line
+ * with "ENTER LANGUAGE"...
+ */
+
+ fputs("DEBUG: Skipping PJL header...\n", stderr);
+
+ while (strstr(line, "ENTER LANGUAGE") == NULL && strncmp(line, "%!", 2))
+ if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
+ break;
+
+ if (!strncmp(line, "%!", 2))
+ break;
+
+ if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
+ break;
+ }
+
+ /*
+ * Now see if the document conforms to the Adobe Document Structuring
+ * Conventions...
+ */
+
+ if (!strncmp(line, "%!PS-Adobe-", 11))
+ {
+ /*
+ * Yes, filter the document...
+ */
+
+ copy_dsc(fp, &doc, ppd, line, len, sizeof(line));
+ }
+ else
+ {
+ /*
+ * No, display an error message and treat the file as if it contains
+ * a single page...
+ */
+
+ copy_non_dsc(fp, &doc, ppd, line, len, sizeof(line));
+ }
+
+ /*
+ * Send %%EOF as needed...
+ */
+
+ if (!doc.saw_eof)
+ puts("%%EOF");
+
+ /*
+ * End the job with the appropriate JCL command or CTRL-D...
+ */
+
+ if (doc.emit_jcl)
+ {
+ if (ppd && ppd->jcl_end)
+ ppdEmitJCLEnd(ppd, stdout);
+ else
+ putchar(0x04);
+ }
+
+ /*
+ * Close files and remove the temporary file if needed...
+ */
+
+ if (doc.temp)
+ {
+ cupsFileClose(doc.temp);
+ unlink(doc.tempfile);
+ }
+
+ ppdClose(ppd);
+ cupsFreeOptions(num_options, options);
+
+ cupsFileClose(fp);
+
+ return (0);
+}
+
+
+/*
+ * 'add_page()' - Add a page to the pages array.
+ */
+
+static pstops_page_t * /* O - New page info object */
+add_page(pstops_doc_t *doc, /* I - Document information */
+ const char *label) /* I - Page label */
+{
+ pstops_page_t *pageinfo; /* New page info object */
+
+
+ if (!doc->pages)
+ doc->pages = cupsArrayNew(NULL, NULL);
+
+ if (!doc->pages)
+ {
+ _cupsLangPrintError("EMERG", _("Unable to allocate memory for pages array"));
+ exit(1);
+ }
+
+ if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL)
+ {
+ _cupsLangPrintError("EMERG", _("Unable to allocate memory for page info"));
+ exit(1);
+ }
+
+ pageinfo->label = strdup(label);
+ pageinfo->offset = cupsFileTell(doc->temp);
+
+ cupsArrayAdd(doc->pages, pageinfo);
+
+ doc->page ++;
+
+ return (pageinfo);
+}
+
+
+/*
+ * 'cancel_job()' - Flag the job as canceled.
+ */
+
+static void
+cancel_job(int sig) /* I - Signal number (unused) */
+{
+ (void)sig;
+
+ JobCanceled = 1;
+}
+
+
+/*
+ * 'check_range()' - Check to see if the current page is selected for
+ * printing.
+ */
+
+static int /* O - 1 if selected, 0 otherwise */
+check_range(pstops_doc_t *doc, /* I - Document information */
+ int page) /* I - Page number */
+{
+ const char *range; /* Pointer into range string */
+ int lower, upper; /* Lower and upper page numbers */
+
+
+ if (doc->page_set)
+ {
+ /*
+ * See if we only print even or odd pages...
+ */
+
+ if (!_cups_strcasecmp(doc->page_set, "even") && (page & 1))
+ return (0);
+
+ if (!_cups_strcasecmp(doc->page_set, "odd") && !(page & 1))
+ return (0);
+ }
+
+ if (!doc->page_ranges)
+ return (1); /* No range, print all pages... */
+
+ for (range = doc->page_ranges; *range != '\0';)
+ {
+ if (*range == '-')
+ {
+ lower = 1;
+ range ++;
+ upper = strtol(range, (char **)&range, 10);
+ }
+ else
+ {
+ lower = strtol(range, (char **)&range, 10);
+
+ if (*range == '-')
+ {
+ range ++;
+ if (!isdigit(*range & 255))
+ upper = 65535;
+ else
+ upper = strtol(range, (char **)&range, 10);
+ }
+ else
+ upper = lower;
+ }
+
+ if (page >= lower && page <= upper)
+ return (1);
+
+ if (*range == ',')
+ range ++;
+ else
+ break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'copy_bytes()' - Copy bytes from the input file to stdout.
+ */
+
+static void
+copy_bytes(cups_file_t *fp, /* I - File to read from */
+ off_t offset, /* I - Offset to page data */
+ size_t length) /* I - Length of page data */
+{
+ char buffer[8192]; /* Data buffer */
+ ssize_t nbytes; /* Number of bytes read */
+ size_t nleft; /* Number of bytes left/remaining */
+
+
+ nleft = length;
+
+ if (cupsFileSeek(fp, offset) < 0)
+ {
+ _cupsLangPrintError("ERROR", _("Unable to see in file"));
+ return;
+ }
+
+ while (nleft > 0 || length == 0)
+ {
+ if (nleft > sizeof(buffer) || length == 0)
+ nbytes = sizeof(buffer);
+ else
+ nbytes = nleft;
+
+ if ((nbytes = cupsFileRead(fp, buffer, nbytes)) < 1)
+ return;
+
+ nleft -= nbytes;
+
+ fwrite(buffer, 1, nbytes, stdout);
+ }
+}
+
+
+/*
+ * 'copy_comments()' - Copy all of the comments section.
+ *
+ * This function expects "line" to be filled with a comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t /* O - Length of next line */
+copy_comments(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ int saw_bounding_box, /* Saw %%BoundingBox: comment? */
+ saw_for, /* Saw %%For: comment? */
+ saw_pages, /* Saw %%Pages: comment? */
+ saw_title; /* Saw %%Title: comment? */
+
+
+ /*
+ * Loop until we see %%EndComments or a non-comment line...
+ */
+
+ saw_bounding_box = 0;
+ saw_for = 0;
+ saw_pages = 0;
+ saw_title = 0;
+
+ while (line[0] == '%')
+ {
+ /*
+ * Strip trailing whitespace...
+ */
+
+ while (linelen > 0)
+ {
+ linelen --;
+
+ if (!isspace(line[linelen] & 255))
+ break;
+ else
+ line[linelen] = '\0';
+ }
+
+ /*
+ * Log the header...
+ */
+
+ fprintf(stderr, "DEBUG: %s\n", line);
+
+ /*
+ * Pull the headers out...
+ */
+
+ if (!strncmp(line, "%%Pages:", 8))
+ {
+ int pages; /* Number of pages */
+
+ if (saw_pages)
+ fputs("DEBUG: A duplicate %%Pages: comment was seen.\n", stderr);
+
+ saw_pages = 1;
+
+ if (Duplex && (pages = atoi(line + 8)) > 0 && pages <= doc->number_up)
+ {
+ /*
+ * Since we will only be printing on a single page, disable duplexing.
+ */
+
+ Duplex = 0;
+ doc->slow_duplex = 0;
+
+ if (cupsGetOption("sides", doc->num_options, doc->options))
+ doc->num_options = cupsAddOption("sides", "one-sided",
+ doc->num_options, &(doc->options));
+
+ if (cupsGetOption("Duplex", doc->num_options, doc->options))
+ doc->num_options = cupsAddOption("Duplex", "None",
+ doc->num_options, &(doc->options));
+
+ if (cupsGetOption("EFDuplex", doc->num_options, doc->options))
+ doc->num_options = cupsAddOption("EFDuplex", "None",
+ doc->num_options, &(doc->options));
+
+ if (cupsGetOption("EFDuplexing", doc->num_options, doc->options))
+ doc->num_options = cupsAddOption("EFDuplexing", "False",
+ doc->num_options, &(doc->options));
+
+ if (cupsGetOption("KD03Duplex", doc->num_options, doc->options))
+ doc->num_options = cupsAddOption("KD03Duplex", "None",
+ doc->num_options, &(doc->options));
+
+ if (cupsGetOption("JCLDuplex", doc->num_options, doc->options))
+ doc->num_options = cupsAddOption("JCLDuplex", "None",
+ doc->num_options, &(doc->options));
+
+ ppdMarkOption(ppd, "Duplex", "None");
+ ppdMarkOption(ppd, "EFDuplex", "None");
+ ppdMarkOption(ppd, "EFDuplexing", "False");
+ ppdMarkOption(ppd, "KD03Duplex", "None");
+ ppdMarkOption(ppd, "JCLDuplex", "None");
+ }
+ }
+ else if (!strncmp(line, "%%BoundingBox:", 14))
+ {
+ if (saw_bounding_box)
+ fputs("DEBUG: A duplicate %%BoundingBox: comment was seen.\n", stderr);
+ else if (strstr(line + 14, "(atend)"))
+ {
+ /*
+ * Do nothing for now but use the default imageable area...
+ */
+ }
+ else if (sscanf(line + 14, "%d%d%d%d", doc->bounding_box + 0,
+ doc->bounding_box + 1, doc->bounding_box + 2,
+ doc->bounding_box + 3) != 4)
+ {
+ fputs("DEBUG: A bad %%BoundingBox: comment was seen.\n", stderr);
+
+ doc->bounding_box[0] = (int)PageLeft;
+ doc->bounding_box[1] = (int)PageBottom;
+ doc->bounding_box[2] = (int)PageRight;
+ doc->bounding_box[3] = (int)PageTop;
+ }
+
+ saw_bounding_box = 1;
+ }
+ else if (!strncmp(line, "%%For:", 6))
+ {
+ saw_for = 1;
+ doc_printf(doc, "%s\n", line);
+ }
+ else if (!strncmp(line, "%%Title:", 8))
+ {
+ saw_title = 1;
+ doc_printf(doc, "%s\n", line);
+ }
+ else if (!strncmp(line, "%cupsRotation:", 14))
+ {
+ /*
+ * Reset orientation of document?
+ */
+
+ int orient = (atoi(line + 14) / 90) & 3;
+
+ if (orient != Orientation)
+ {
+ /*
+ * Yes, update things so that the pages come out right...
+ */
+
+ Orientation = (4 - Orientation + orient) & 3;
+ UpdatePageVars();
+ Orientation = orient;
+ }
+ }
+ else if (!strcmp(line, "%%EndComments"))
+ {
+ linelen = cupsFileGetLine(fp, line, linesize);
+ break;
+ }
+ else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5))
+ doc_printf(doc, "%s\n", line);
+
+ if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+ break;
+ }
+
+ if (!saw_bounding_box)
+ fputs("DEBUG: There wasn't a %%BoundingBox: comment in the header.\n",
+ stderr);
+
+ if (!saw_pages)
+ fputs("DEBUG: There wasn't a %%Pages: comment in the header.\n", stderr);
+
+ if (!saw_for)
+ WriteTextComment("For", doc->user);
+
+ if (!saw_title)
+ WriteTextComment("Title", doc->title);
+
+ if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
+ {
+ /*
+ * Tell the document processor the copy and duplex options
+ * that are required...
+ */
+
+ doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
+ doc->collate ? " collate" : "",
+ Duplex ? " duplex" : "");
+
+ /*
+ * Apple uses RBI comments for various non-PPD options...
+ */
+
+ doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies);
+ }
+ else
+ {
+ /*
+ * Tell the document processor the duplex option that is required...
+ */
+
+ if (Duplex)
+ doc_puts(doc, "%%Requirements: duplex\n");
+
+ /*
+ * Apple uses RBI comments for various non-PPD options...
+ */
+
+ doc_puts(doc, "%RBINumCopies: 1\n");
+ }
+
+ doc_puts(doc, "%%Pages: (atend)\n");
+ doc_puts(doc, "%%BoundingBox: (atend)\n");
+ doc_puts(doc, "%%EndComments\n");
+
+ return (linelen);
+}
+
+
+/*
+ * 'copy_dsc()' - Copy a DSC-conforming document.
+ *
+ * This function expects "line" to be filled with the %!PS-Adobe comment line.
+ */
+
+static void
+copy_dsc(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ int number; /* Page number */
+ pstops_page_t *pageinfo; /* Page information */
+
+
+ /*
+ * Make sure we use ESPshowpage for EPS files...
+ */
+
+ if (strstr(line, "EPSF"))
+ {
+ doc->use_ESPshowpage = 1;
+ doc->number_up = 1;
+ }
+
+ /*
+ * Start sending the document with any commands needed...
+ */
+
+ fprintf(stderr, "DEBUG: Before copy_comments - %s", line);
+ linelen = copy_comments(fp, doc, ppd, line, linelen, linesize);
+
+ /*
+ * Now find the prolog section, if any...
+ */
+
+ fprintf(stderr, "DEBUG: Before copy_prolog - %s", line);
+ linelen = copy_prolog(fp, doc, ppd, line, linelen, linesize);
+
+ /*
+ * Then the document setup section...
+ */
+
+ fprintf(stderr, "DEBUG: Before copy_setup - %s", line);
+ linelen = copy_setup(fp, doc, ppd, line, linelen, linesize);
+
+ /*
+ * Copy until we see %%Page:...
+ */
+
+ while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9))
+ {
+ doc_write(doc, line, linelen);
+
+ if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+ break;
+ }
+
+ /*
+ * Then process pages until we have no more...
+ */
+
+ number = 0;
+
+ fprintf(stderr, "DEBUG: Before page loop - %s", line);
+ while (!strncmp(line, "%%Page:", 7))
+ {
+ if (JobCanceled)
+ break;
+
+ number ++;
+
+ if (check_range(doc, (number - 1) / doc->number_up + 1))
+ {
+ fprintf(stderr, "DEBUG: Copying page %d...\n", number);
+ linelen = copy_page(fp, doc, ppd, number, line, linelen, linesize);
+ }
+ else
+ {
+ fprintf(stderr, "DEBUG: Skipping page %d...\n", number);
+ linelen = skip_page(fp, line, linelen, linesize);
+ }
+ }
+
+ /*
+ * Finish up the last page(s)...
+ */
+
+ if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) &&
+ check_range(doc, (number - 1) / doc->number_up + 1))
+ {
+ pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
+
+ start_nup(doc, doc->number_up, 0, doc->bounding_box);
+ doc_puts(doc, "showpage\n");
+ end_nup(doc, doc->number_up);
+
+ pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
+ }
+
+ if (doc->slow_duplex && (doc->page & 1))
+ {
+ /*
+ * Make sure we have an even number of pages...
+ */
+
+ pageinfo = add_page(doc, "(filler)");
+
+ if (!doc->slow_order)
+ {
+ if (!ppd || !ppd->num_filters)
+ fprintf(stderr, "PAGE: %d %d\n", doc->page,
+ doc->slow_collate ? 1 : doc->copies);
+
+ printf("%%%%Page: (filler) %d\n", doc->page);
+ }
+
+ start_nup(doc, doc->number_up, 0, doc->bounding_box);
+ doc_puts(doc, "showpage\n");
+ end_nup(doc, doc->number_up);
+
+ pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
+ }
+
+ /*
+ * Make additional copies as necessary...
+ */
+
+ number = doc->slow_order ? 0 : doc->page;
+
+ if (doc->temp && !JobCanceled && cupsArrayCount(doc->pages) > 0)
+ {
+ int copy; /* Current copy */
+
+
+ /*
+ * Reopen the temporary file for reading...
+ */
+
+ cupsFileClose(doc->temp);
+
+ doc->temp = cupsFileOpen(doc->tempfile, "r");
+
+ /*
+ * Make the copies...
+ */
+
+ if (doc->slow_collate)
+ copy = !doc->slow_order;
+ else
+ copy = doc->copies - 1;
+
+ for (; copy < doc->copies; copy ++)
+ {
+ if (JobCanceled)
+ break;
+
+ /*
+ * Send end-of-job stuff followed by any start-of-job stuff required
+ * for the JCL options...
+ */
+
+ if (number && doc->emit_jcl && ppd && ppd->jcl_end)
+ {
+ /*
+ * Send the trailer...
+ */
+
+ puts("%%Trailer");
+ printf("%%%%Pages: %d\n", cupsArrayCount(doc->pages));
+ if (doc->number_up > 1 || doc->fit_to_page)
+ printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
+ PageLeft, PageBottom, PageRight, PageTop);
+ else
+ printf("%%%%BoundingBox: %d %d %d %d\n",
+ doc->new_bounding_box[0], doc->new_bounding_box[1],
+ doc->new_bounding_box[2], doc->new_bounding_box[3]);
+ puts("%%EOF");
+
+ /*
+ * Start a new document...
+ */
+
+ ppdEmitJCLEnd(ppd, stdout);
+ ppdEmitJCL(ppd, stdout, doc->job_id, doc->user, doc->title);
+
+ puts("%!PS-Adobe-3.0");
+
+ number = 0;
+ }
+
+ /*
+ * Copy the prolog as needed...
+ */
+
+ if (!number)
+ {
+ pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages);
+ copy_bytes(doc->temp, 0, pageinfo->offset);
+ }
+
+ /*
+ * Then copy all of the pages...
+ */
+
+ pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayLast(doc->pages) :
+ (pstops_page_t *)cupsArrayFirst(doc->pages);
+
+ while (pageinfo)
+ {
+ if (JobCanceled)
+ break;
+
+ number ++;
+
+ if (!ppd || !ppd->num_filters)
+ fprintf(stderr, "PAGE: %d %d\n", number,
+ doc->slow_collate ? 1 : doc->copies);
+
+ if (doc->number_up > 1)
+ {
+ printf("%%%%Page: (%d) %d\n", number, number);
+ printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
+ PageLeft, PageBottom, PageRight, PageTop);
+ }
+ else
+ {
+ printf("%%%%Page: %s %d\n", pageinfo->label, number);
+ printf("%%%%PageBoundingBox: %d %d %d %d\n",
+ pageinfo->bounding_box[0], pageinfo->bounding_box[1],
+ pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
+ }
+
+ copy_bytes(doc->temp, pageinfo->offset, pageinfo->length);
+
+ pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayPrev(doc->pages) :
+ (pstops_page_t *)cupsArrayNext(doc->pages);
+ }
+ }
+ }
+
+ /*
+ * Restore the old showpage operator as needed...
+ */
+
+ if (doc->use_ESPshowpage)
+ puts("userdict/showpage/ESPshowpage load put\n");
+
+ /*
+ * Write/copy the trailer...
+ */
+
+ if (!JobCanceled)
+ copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
+}
+
+
+/*
+ * 'copy_non_dsc()' - Copy a document that does not conform to the DSC.
+ *
+ * This function expects "line" to be filled with the %! comment line.
+ */
+
+static void
+copy_non_dsc(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ int copy; /* Current copy */
+ char buffer[8192]; /* Copy buffer */
+ int bytes; /* Number of bytes copied */
+
+
+ /*
+ * First let the user know that they are attempting to print a file
+ * that may not print correctly...
+ */
+
+ fputs("DEBUG: This document does not conform to the Adobe Document "
+ "Structuring Conventions and may not print correctly.\n", stderr);
+
+ /*
+ * Then write a standard DSC comment section...
+ */
+
+ printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
+ PageRight, PageTop);
+
+ if (doc->slow_collate && doc->copies > 1)
+ printf("%%%%Pages: %d\n", doc->copies);
+ else
+ puts("%%Pages: 1");
+
+ WriteTextComment("For", doc->user);
+ WriteTextComment("Title", doc->title);
+
+ if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
+ {
+ /*
+ * Tell the document processor the copy and duplex options
+ * that are required...
+ */
+
+ printf("%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
+ doc->collate ? " collate" : "",
+ Duplex ? " duplex" : "");
+
+ /*
+ * Apple uses RBI comments for various non-PPD options...
+ */
+
+ printf("%%RBINumCopies: %d\n", doc->copies);
+ }
+ else
+ {
+ /*
+ * Tell the document processor the duplex option that is required...
+ */
+
+ if (Duplex)
+ puts("%%Requirements: duplex");
+
+ /*
+ * Apple uses RBI comments for various non-PPD options...
+ */
+
+ puts("%RBINumCopies: 1");
+ }
+
+ puts("%%EndComments");
+
+ /*
+ * Then the prolog...
+ */
+
+ puts("%%BeginProlog");
+
+ do_prolog(doc, ppd);
+
+ puts("%%EndProlog");
+
+ /*
+ * Then the setup section...
+ */
+
+ puts("%%BeginSetup");
+
+ do_setup(doc, ppd);
+
+ puts("%%EndSetup");
+
+ /*
+ * Finally, embed a copy of the file inside a %%Page...
+ */
+
+ if (!ppd || !ppd->num_filters)
+ fprintf(stderr, "PAGE: 1 %d\n", doc->temp ? 1 : doc->copies);
+
+ puts("%%Page: 1 1");
+ puts("%%BeginPageSetup");
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+ puts("%%EndPageSetup");
+ puts("%%BeginDocument: nondsc");
+
+ fwrite(line, linelen, 1, stdout);
+
+ if (doc->temp)
+ cupsFileWrite(doc->temp, line, linelen);
+
+ while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
+ {
+ fwrite(buffer, 1, bytes, stdout);
+
+ if (doc->temp)
+ cupsFileWrite(doc->temp, buffer, bytes);
+ }
+
+ puts("%%EndDocument");
+
+ if (doc->use_ESPshowpage)
+ {
+ WriteLabels(Orientation);
+ puts("ESPshowpage");
+ }
+
+ if (doc->temp && !JobCanceled)
+ {
+ /*
+ * Reopen the temporary file for reading...
+ */
+
+ cupsFileClose(doc->temp);
+
+ doc->temp = cupsFileOpen(doc->tempfile, "r");
+
+ /*
+ * Make the additional copies as needed...
+ */
+
+ for (copy = 1; copy < doc->copies; copy ++)
+ {
+ if (JobCanceled)
+ break;
+
+ if (!ppd || !ppd->num_filters)
+ fputs("PAGE: 1 1\n", stderr);
+
+ printf("%%%%Page: %d %d\n", copy + 1, copy + 1);
+ puts("%%BeginPageSetup");
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+ puts("%%EndPageSetup");
+ puts("%%BeginDocument: nondsc");
+
+ copy_bytes(doc->temp, 0, 0);
+
+ puts("%%EndDocument");
+
+ if (doc->use_ESPshowpage)
+ {
+ WriteLabels(Orientation);
+ puts("ESPshowpage");
+ }
+ }
+ }
+
+ /*
+ * Restore the old showpage operator as needed...
+ */
+
+ if (doc->use_ESPshowpage)
+ puts("userdict/showpage/ESPshowpage load put\n");
+}
+
+
+/*
+ * 'copy_page()' - Copy a page description.
+ *
+ * This function expects "line" to be filled with a %%Page comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t /* O - Length of next line */
+copy_page(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ int number, /* I - Current page number */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ char label[256], /* Page label string */
+ *ptr; /* Pointer into line */
+ int level; /* Embedded document level */
+ pstops_page_t *pageinfo; /* Page information */
+ int first_page; /* First page on N-up output? */
+ int has_page_setup = 0; /* Does the page have %%Begin/EndPageSetup? */
+ int bounding_box[4]; /* PageBoundingBox */
+
+
+ /*
+ * Get the page label for this page...
+ */
+
+ first_page = is_first_page(number);
+
+ if (!parse_text(line + 7, &ptr, label, sizeof(label)))
+ {
+ fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
+ label[0] = '\0';
+ number = doc->page;
+ }
+ else if (strtol(ptr, &ptr, 10) == LONG_MAX || !isspace(*ptr & 255))
+ {
+ fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
+ number = doc->page;
+ }
+
+ /*
+ * Create or update the current output page...
+ */
+
+ if (first_page)
+ pageinfo = add_page(doc, label);
+ else
+ pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
+
+ /*
+ * Handle first page override...
+ */
+
+ if (doc->ap_input_slot || doc->ap_manual_feed)
+ {
+ if ((doc->page == 1 && (!doc->slow_order || !Duplex)) ||
+ (doc->page == 2 && doc->slow_order && Duplex))
+ {
+ /*
+ * First page/sheet gets AP_FIRSTPAGE_* options...
+ */
+
+ pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("ManualFeed",
+ doc->ap_input_slot ? "False" :
+ doc->ap_manual_feed,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("MediaColor", doc->ap_media_color,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("MediaType", doc->ap_media_type,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("PageRegion", doc->ap_page_region,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("PageSize", doc->ap_page_size,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ }
+ else if (doc->page == (Duplex + 2))
+ {
+ /*
+ * Second page/sheet gets default options...
+ */
+
+ pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("ManualFeed",
+ doc->input_slot ? "False" :
+ doc->manual_feed,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("MediaColor", doc->media_color,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("MediaType", doc->media_type,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("PageRegion", doc->page_region,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ pageinfo->num_options = cupsAddOption("PageSize", doc->page_size,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ }
+ }
+
+ /*
+ * Scan comments until we see something other than %%Page*: or
+ * %%Include*...
+ */
+
+ memcpy(bounding_box, doc->bounding_box, sizeof(bounding_box));
+
+ while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+ {
+ if (!strncmp(line, "%%PageBoundingBox:", 18))
+ {
+ /*
+ * %%PageBoundingBox: llx lly urx ury
+ */
+
+ if (sscanf(line + 18, "%d%d%d%d", bounding_box + 0,
+ bounding_box + 1, bounding_box + 2,
+ bounding_box + 3) != 4)
+ {
+ fputs("DEBUG: There was a bad %%PageBoundingBox: comment in the file.\n", stderr);
+ memcpy(bounding_box, doc->bounding_box,
+ sizeof(bounding_box));
+ }
+ else if (doc->number_up == 1 && !doc->fit_to_page && Orientation)
+ {
+ int temp_bbox[4]; /* Temporary bounding box */
+
+
+ memcpy(temp_bbox, bounding_box, sizeof(temp_bbox));
+
+ fprintf(stderr, "DEBUG: Orientation = %d\n", Orientation);
+ fprintf(stderr, "DEBUG: original bounding_box = [ %d %d %d %d ]\n",
+ bounding_box[0], bounding_box[1],
+ bounding_box[2], bounding_box[3]);
+ fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
+ PageWidth, PageLength);
+
+ switch (Orientation)
+ {
+ case 1 : /* Landscape */
+ bounding_box[0] = PageLength - temp_bbox[3];
+ bounding_box[1] = temp_bbox[0];
+ bounding_box[2] = PageLength - temp_bbox[1];
+ bounding_box[3] = temp_bbox[2];
+ break;
+
+ case 2 : /* Reverse Portrait */
+ bounding_box[0] = PageWidth - temp_bbox[2];
+ bounding_box[1] = PageLength - temp_bbox[3];
+ bounding_box[2] = PageWidth - temp_bbox[0];
+ bounding_box[3] = PageLength - temp_bbox[1];
+ break;
+
+ case 3 : /* Reverse Landscape */
+ bounding_box[0] = temp_bbox[1];
+ bounding_box[1] = PageWidth - temp_bbox[2];
+ bounding_box[2] = temp_bbox[3];
+ bounding_box[3] = PageWidth - temp_bbox[0];
+ break;
+ }
+
+ fprintf(stderr, "DEBUG: updated bounding_box = [ %d %d %d %d ]\n",
+ bounding_box[0], bounding_box[1],
+ bounding_box[2], bounding_box[3]);
+ }
+ }
+#if 0
+ else if (!strncmp(line, "%%PageCustomColors:", 19) ||
+ !strncmp(line, "%%PageMedia:", 12) ||
+ !strncmp(line, "%%PageOrientation:", 18) ||
+ !strncmp(line, "%%PageProcessColors:", 20) ||
+ !strncmp(line, "%%PageRequirements:", 18) ||
+ !strncmp(line, "%%PageResources:", 16))
+ {
+ /*
+ * Copy literal...
+ */
+ }
+#endif /* 0 */
+ else if (!strncmp(line, "%%PageCustomColors:", 19))
+ {
+ /*
+ * %%PageCustomColors: ...
+ */
+ }
+ else if (!strncmp(line, "%%PageMedia:", 12))
+ {
+ /*
+ * %%PageMedia: ...
+ */
+ }
+ else if (!strncmp(line, "%%PageOrientation:", 18))
+ {
+ /*
+ * %%PageOrientation: ...
+ */
+ }
+ else if (!strncmp(line, "%%PageProcessColors:", 20))
+ {
+ /*
+ * %%PageProcessColors: ...
+ */
+ }
+ else if (!strncmp(line, "%%PageRequirements:", 18))
+ {
+ /*
+ * %%PageRequirements: ...
+ */
+ }
+ else if (!strncmp(line, "%%PageResources:", 16))
+ {
+ /*
+ * %%PageResources: ...
+ */
+ }
+ else if (!strncmp(line, "%%IncludeFeature:", 17))
+ {
+ /*
+ * %%IncludeFeature: *MainKeyword OptionKeyword
+ */
+
+ if (doc->number_up == 1 &&!doc->fit_to_page)
+ pageinfo->num_options = include_feature(ppd, line,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ }
+ else if (!strncmp(line, "%%BeginPageSetup", 16))
+ {
+ has_page_setup = 1;
+ break;
+ }
+ else
+ break;
+ }
+
+ if (doc->number_up == 1)
+ {
+ /*
+ * Update the document's composite and page bounding box...
+ */
+
+ memcpy(pageinfo->bounding_box, bounding_box,
+ sizeof(pageinfo->bounding_box));
+
+ if (bounding_box[0] < doc->new_bounding_box[0])
+ doc->new_bounding_box[0] = bounding_box[0];
+ if (bounding_box[1] < doc->new_bounding_box[1])
+ doc->new_bounding_box[1] = bounding_box[1];
+ if (bounding_box[2] > doc->new_bounding_box[2])
+ doc->new_bounding_box[2] = bounding_box[2];
+ if (bounding_box[3] > doc->new_bounding_box[3])
+ doc->new_bounding_box[3] = bounding_box[3];
+ }
+
+ /*
+ * Output the page header as needed...
+ */
+
+ if (!doc->slow_order && first_page)
+ {
+ if (!ppd || !ppd->num_filters)
+ fprintf(stderr, "PAGE: %d %d\n", doc->page,
+ doc->slow_collate ? 1 : doc->copies);
+
+ if (doc->number_up > 1)
+ {
+ printf("%%%%Page: (%d) %d\n", doc->page, doc->page);
+ printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
+ PageLeft, PageBottom, PageRight, PageTop);
+ }
+ else
+ {
+ printf("%%%%Page: %s %d\n", pageinfo->label, doc->page);
+ printf("%%%%PageBoundingBox: %d %d %d %d\n",
+ pageinfo->bounding_box[0], pageinfo->bounding_box[1],
+ pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
+ }
+ }
+
+ /*
+ * Copy any page setup commands...
+ */
+
+ if (first_page)
+ doc_puts(doc, "%%BeginPageSetup\n");
+
+ if (has_page_setup)
+ {
+ int feature = 0; /* In a Begin/EndFeature block? */
+
+ while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+ {
+ if (!strncmp(line, "%%EndPageSetup", 14))
+ break;
+ else if (!strncmp(line, "%%BeginFeature:", 15))
+ {
+ feature = 1;
+
+ if (doc->number_up > 1 || doc->fit_to_page)
+ continue;
+ }
+ else if (!strncmp(line, "%%EndFeature", 12))
+ {
+ feature = 0;
+
+ if (doc->number_up > 1 || doc->fit_to_page)
+ continue;
+ }
+ else if (!strncmp(line, "%%IncludeFeature:", 17))
+ {
+ pageinfo->num_options = include_feature(ppd, line,
+ pageinfo->num_options,
+ &(pageinfo->options));
+ continue;
+ }
+ else if (!strncmp(line, "%%Include", 9))
+ continue;
+
+ if (line[0] != '%' && !feature)
+ break;
+
+ if (!feature || (doc->number_up == 1 && !doc->fit_to_page))
+ doc_write(doc, line, linelen);
+ }
+
+ /*
+ * Skip %%EndPageSetup...
+ */
+
+ if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14))
+ linelen = cupsFileGetLine(fp, line, linesize);
+ }
+
+ if (first_page)
+ {
+ char *page_setup; /* PageSetup commands to send */
+
+
+ if (pageinfo->num_options > 0)
+ write_options(doc, ppd, pageinfo->num_options, pageinfo->options);
+
+ /*
+ * Output commands for the current page...
+ */
+
+ page_setup = ppdEmitString(ppd, PPD_ORDER_PAGE, 0);
+
+ if (page_setup)
+ {
+ doc_puts(doc, page_setup);
+ free(page_setup);
+ }
+ }
+
+ /*
+ * Prep for the start of the page description...
+ */
+
+ start_nup(doc, number, 1, bounding_box);
+
+ if (first_page)
+ doc_puts(doc, "%%EndPageSetup\n");
+
+ /*
+ * Read the rest of the page description...
+ */
+
+ level = 0;
+
+ do
+ {
+ if (level == 0 &&
+ (!strncmp(line, "%%Page:", 7) ||
+ !strncmp(line, "%%Trailer", 9) ||
+ !strncmp(line, "%%EOF", 5)))
+ break;
+ else if (!strncmp(line, "%%BeginDocument", 15) ||
+ !strncmp(line, "%ADO_BeginApplication", 21))
+ {
+ doc_write(doc, line, linelen);
+
+ level ++;
+ }
+ else if ((!strncmp(line, "%%EndDocument", 13) ||
+ !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
+ {
+ doc_write(doc, line, linelen);
+
+ level --;
+ }
+ else if (!strncmp(line, "%%BeginBinary:", 14) ||
+ (!strncmp(line, "%%BeginData:", 12) &&
+ !strstr(line, "ASCII") && !strstr(line, "Hex")))
+ {
+ /*
+ * Copy binary data...
+ */
+
+ int bytes; /* Bytes of data */
+
+
+ doc_write(doc, line, linelen);
+
+ bytes = atoi(strchr(line, ':') + 1);
+
+ while (bytes > 0)
+ {
+ if (bytes > linesize)
+ linelen = cupsFileRead(fp, line, linesize);
+ else
+ linelen = cupsFileRead(fp, line, bytes);
+
+ if (linelen < 1)
+ {
+ line[0] = '\0';
+ perror("ERROR: Early end-of-file while reading binary data");
+ return (0);
+ }
+
+ doc_write(doc, line, linelen);
+
+ bytes -= linelen;
+ }
+ }
+ else
+ doc_write(doc, line, linelen);
+ }
+ while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0);
+
+ /*
+ * Finish up this page and return...
+ */
+
+ end_nup(doc, number);
+
+ pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
+
+ return (linelen);
+}
+
+
+/*
+ * 'copy_prolog()' - Copy the document prolog section.
+ *
+ * This function expects "line" to be filled with a %%BeginProlog comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t /* O - Length of next line */
+copy_prolog(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ while (strncmp(line, "%%BeginProlog", 13))
+ {
+ if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7))
+ break;
+
+ doc_write(doc, line, linelen);
+
+ if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+ break;
+ }
+
+ doc_puts(doc, "%%BeginProlog\n");
+
+ do_prolog(doc, ppd);
+
+ if (!strncmp(line, "%%BeginProlog", 13))
+ {
+ while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+ {
+ if (!strncmp(line, "%%EndProlog", 11) ||
+ !strncmp(line, "%%BeginSetup", 12) ||
+ !strncmp(line, "%%Page:", 7))
+ break;
+
+ doc_write(doc, line, linelen);
+ }
+
+ if (!strncmp(line, "%%EndProlog", 11))
+ linelen = cupsFileGetLine(fp, line, linesize);
+ else
+ fputs("DEBUG: The %%EndProlog comment is missing.\n", stderr);
+ }
+
+ doc_puts(doc, "%%EndProlog\n");
+
+ return (linelen);
+}
+
+
+/*
+ * 'copy_setup()' - Copy the document setup section.
+ *
+ * This function expects "line" to be filled with a %%BeginSetup comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t /* O - Length of next line */
+copy_setup(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+
+
+ while (strncmp(line, "%%BeginSetup", 12))
+ {
+ if (!strncmp(line, "%%Page:", 7))
+ break;
+
+ doc_write(doc, line, linelen);
+
+ if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+ break;
+ }
+
+ doc_puts(doc, "%%BeginSetup\n");
+
+ do_setup(doc, ppd);
+
+ num_options = 0;
+ options = NULL;
+
+ if (!strncmp(line, "%%BeginSetup", 12))
+ {
+ while (strncmp(line, "%%EndSetup", 10))
+ {
+ if (!strncmp(line, "%%Page:", 7))
+ break;
+ else if (!strncmp(line, "%%IncludeFeature:", 17))
+ {
+ /*
+ * %%IncludeFeature: *MainKeyword OptionKeyword
+ */
+
+ if (doc->number_up == 1 && !doc->fit_to_page)
+ num_options = include_feature(ppd, line, num_options, &options);
+ }
+ else if (strncmp(line, "%%BeginSetup", 12))
+ doc_write(doc, line, linelen);
+
+ if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+ break;
+ }
+
+ if (!strncmp(line, "%%EndSetup", 10))
+ linelen = cupsFileGetLine(fp, line, linesize);
+ else
+ fputs("DEBUG: The %%EndSetup comment is missing.\n", stderr);
+ }
+
+ if (num_options > 0)
+ {
+ write_options(doc, ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+ }
+
+ doc_puts(doc, "%%EndSetup\n");
+
+ return (linelen);
+}
+
+
+/*
+ * 'copy_trailer()' - Copy the document trailer.
+ *
+ * This function expects "line" to be filled with a %%Trailer comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t /* O - Length of next line */
+copy_trailer(cups_file_t *fp, /* I - File to read from */
+ pstops_doc_t *doc, /* I - Document info */
+ ppd_file_t *ppd, /* I - PPD file */
+ int number, /* I - Number of pages */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ /*
+ * Write the trailer comments...
+ */
+
+ puts("%%Trailer");
+
+ while (linelen > 0)
+ {
+ if (!strncmp(line, "%%EOF", 5))
+ break;
+ else if (strncmp(line, "%%Trailer", 9) &&
+ strncmp(line, "%%Pages:", 8) &&
+ strncmp(line, "%%BoundingBox:", 14))
+ fwrite(line, 1, linelen, stdout);
+
+ linelen = cupsFileGetLine(fp, line, linesize);
+ }
+
+ fprintf(stderr, "DEBUG: Wrote %d pages...\n", number);
+
+ printf("%%%%Pages: %d\n", number);
+ if (doc->number_up > 1 || doc->fit_to_page)
+ printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
+ PageLeft, PageBottom, PageRight, PageTop);
+ else
+ printf("%%%%BoundingBox: %d %d %d %d\n",
+ doc->new_bounding_box[0], doc->new_bounding_box[1],
+ doc->new_bounding_box[2], doc->new_bounding_box[3]);
+
+ return (linelen);
+}
+
+
+/*
+ * 'do_prolog()' - Send the necessary document prolog commands.
+ */
+
+static void
+do_prolog(pstops_doc_t *doc, /* I - Document information */
+ ppd_file_t *ppd) /* I - PPD file */
+{
+ char *ps; /* PS commands */
+
+
+ /*
+ * Send the document prolog commands...
+ */
+
+ if (ppd && ppd->patches)
+ {
+ doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n");
+ doc_puts(doc, ppd->patches);
+ doc_puts(doc, "\n%%EndFeature\n");
+ }
+
+ if ((ps = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
+ {
+ doc_puts(doc, ps);
+ free(ps);
+ }
+
+ /*
+ * Define ESPshowpage here so that applications that define their
+ * own procedure to do a showpage pick it up...
+ */
+
+ if (doc->use_ESPshowpage)
+ doc_puts(doc, "userdict/ESPshowpage/showpage load put\n"
+ "userdict/showpage{}put\n");
+}
+
+
+/*
+ * 'do_setup()' - Send the necessary document setup commands.
+ */
+
+static void
+do_setup(pstops_doc_t *doc, /* I - Document information */
+ ppd_file_t *ppd) /* I - PPD file */
+{
+ char *ps; /* PS commands */
+
+
+ /*
+ * Disable CTRL-D so that embedded files don't cause printing
+ * errors...
+ */
+
+ doc_puts(doc, "% Disable CTRL-D as an end-of-file marker...\n");
+ doc_puts(doc, "userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
+
+ /*
+ * Mark job options...
+ */
+
+ cupsMarkOptions(ppd, doc->num_options, doc->options);
+
+ /*
+ * Send all the printer-specific setup commands...
+ */
+
+ if ((ps = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
+ {
+ doc_puts(doc, ps);
+ free(ps);
+ }
+
+ if ((ps = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
+ {
+ doc_puts(doc, ps);
+ free(ps);
+ }
+
+ /*
+ * Set the number of copies for the job...
+ */
+
+ if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
+ {
+ doc_printf(doc, "%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies);
+ doc_printf(doc,
+ "%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse\n"
+ "{1 dict begin/NumCopies exch def currentdict end "
+ "setpagedevice}\n"
+ "{userdict/#copies 3 -1 roll put}ifelse\n", doc->copies);
+ doc_puts(doc, "%RBIEndNonPPDFeature\n");
+ }
+
+ /*
+ * If we are doing N-up printing, disable setpagedevice...
+ */
+
+ if (doc->number_up > 1)
+ {
+ doc_puts(doc, "userdict/CUPSsetpagedevice/setpagedevice load put\n");
+ doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
+ }
+
+ /*
+ * Make sure we have rectclip and rectstroke procedures of some sort...
+ */
+
+ doc_puts(doc,
+ "% x y w h ESPrc - Clip to a rectangle.\n"
+ "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
+ "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ "neg 0 rlineto closepath clip newpath}bind}ifelse put\n");
+
+ doc_puts(doc,
+ "% x y w h ESPrf - Fill a rectangle.\n"
+ "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
+ "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ "neg 0 rlineto closepath fill grestore}bind}ifelse put\n");
+
+ doc_puts(doc,
+ "% x y w h ESPrs - Stroke a rectangle.\n"
+ "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
+ "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ "neg 0 rlineto closepath stroke grestore}bind}ifelse put\n");
+
+ /*
+ * Write the page and label prologs...
+ */
+
+ if (doc->number_up == 2 || doc->number_up == 6)
+ {
+ /*
+ * For 2- and 6-up output, rotate the labels to match the orientation
+ * of the pages...
+ */
+
+ if (Orientation & 1)
+ write_label_prolog(doc, doc->page_label, PageBottom,
+ PageWidth - PageLength + PageTop, PageLength);
+ else
+ write_label_prolog(doc, doc->page_label, PageLeft, PageRight,
+ PageLength);
+ }
+ else
+ write_label_prolog(doc, doc->page_label, PageBottom, PageTop, PageWidth);
+}
+
+
+/*
+ * 'doc_printf()' - Send a formatted string to stdout and/or the temp file.
+ *
+ * This function should be used for all page-level output that is affected
+ * by ordering, collation, etc.
+ */
+
+static void
+doc_printf(pstops_doc_t *doc, /* I - Document information */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+ char buffer[1024]; /* Output buffer */
+ size_t bytes; /* Number of bytes to write */
+
+
+ va_start(ap, format);
+ bytes = vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+
+ if (bytes > sizeof(buffer))
+ {
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Buffer overflow detected, aborting."));
+ exit(1);
+ }
+
+ doc_write(doc, buffer, bytes);
+}
+
+
+/*
+ * 'doc_puts()' - Send a nul-terminated string to stdout and/or the temp file.
+ *
+ * This function should be used for all page-level output that is affected
+ * by ordering, collation, etc.
+ */
+
+static void
+doc_puts(pstops_doc_t *doc, /* I - Document information */
+ const char *s) /* I - String to send */
+{
+ doc_write(doc, s, strlen(s));
+}
+
+
+/*
+ * 'doc_write()' - Send data to stdout and/or the temp file.
+ */
+
+static void
+doc_write(pstops_doc_t *doc, /* I - Document information */
+ const char *s, /* I - Data to send */
+ size_t len) /* I - Number of bytes to send */
+{
+ if (!doc->slow_order)
+ fwrite(s, 1, len, stdout);
+
+ if (doc->temp)
+ cupsFileWrite(doc->temp, s, len);
+}
+
+
+/*
+ * 'end_nup()' - End processing for N-up printing.
+ */
+
+static void
+end_nup(pstops_doc_t *doc, /* I - Document information */
+ int number) /* I - Page number */
+{
+ if (doc->number_up > 1)
+ doc_puts(doc, "userdict/ESPsave get restore\n");
+
+ switch (doc->number_up)
+ {
+ case 1 :
+ if (doc->use_ESPshowpage)
+ {
+ write_labels(doc, Orientation);
+ doc_puts(doc, "ESPshowpage\n");
+ }
+ break;
+
+ case 2 :
+ case 6 :
+ if (is_last_page(number) && doc->use_ESPshowpage)
+ {
+ if (Orientation & 1)
+ {
+ /*
+ * Rotate the labels back to portrait...
+ */
+
+ write_labels(doc, Orientation - 1);
+ }
+ else if (Orientation == 0)
+ {
+ /*
+ * Rotate the labels to landscape...
+ */
+
+ write_labels(doc, doc->normal_landscape ? 1 : 3);
+ }
+ else
+ {
+ /*
+ * Rotate the labels to landscape...
+ */
+
+ write_labels(doc, doc->normal_landscape ? 3 : 1);
+ }
+
+ doc_puts(doc, "ESPshowpage\n");
+ }
+ break;
+
+ default :
+ if (is_last_page(number) && doc->use_ESPshowpage)
+ {
+ write_labels(doc, Orientation);
+ doc_puts(doc, "ESPshowpage\n");
+ }
+ break;
+ }
+
+ fflush(stdout);
+}
+
+
+/*
+ * 'include_feature()' - Include a printer option/feature command.
+ */
+
+static int /* O - New number of options */
+include_feature(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *line, /* I - DSC line */
+ int num_options, /* I - Number of options */
+ cups_option_t **options) /* IO - Options */
+{
+ char name[255], /* Option name */
+ value[255]; /* Option value */
+ ppd_option_t *option; /* Option in file */
+
+
+ /*
+ * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
+ */
+
+ if (sscanf(line + 17, "%254s%254s", name, value) != 2)
+ {
+ fputs("DEBUG: The %%IncludeFeature: comment is not valid.\n", stderr);
+ return (num_options);
+ }
+
+ /*
+ * Find the option and choice...
+ */
+
+ if ((option = ppdFindOption(ppd, name + 1)) == NULL)
+ {
+ _cupsLangPrintFilter(stderr, "WARNING", _("Unknown option \"%s\"."),
+ name + 1);
+ return (num_options);
+ }
+
+ if (option->section == PPD_ORDER_EXIT ||
+ option->section == PPD_ORDER_JCL)
+ {
+ _cupsLangPrintFilter(stderr, "WARNING",
+ _("Option \"%s\" cannot be included via "
+ "%%%%IncludeFeature."), name + 1);
+ return (num_options);
+ }
+
+ if (!ppdFindChoice(option, value))
+ {
+ _cupsLangPrintFilter(stderr, "WARNING",
+ _("Unknown choice \"%s\" for option \"%s\"."),
+ value, name + 1);
+ return (num_options);
+ }
+
+ /*
+ * Add the option to the option array and return...
+ */
+
+ return (cupsAddOption(name + 1, value, num_options, options));
+}
+
+
+/*
+ * 'parse_text()' - Parse a text value in a comment.
+ *
+ * This function parses a DSC text value as defined on page 36 of the
+ * DSC specification. Text values are either surrounded by parenthesis
+ * or whitespace-delimited.
+ *
+ * The value returned is the literal characters for the entire text
+ * string, including any parenthesis and escape characters.
+ */
+
+static char * /* O - Value or NULL on error */
+parse_text(const char *start, /* I - Start of text value */
+ char **end, /* O - End of text value */
+ char *buffer, /* I - Buffer */
+ size_t bufsize) /* I - Size of buffer */
+{
+ char *bufptr, /* Pointer in buffer */
+ *bufend; /* End of buffer */
+ int level; /* Parenthesis level */
+
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (isspace(*start & 255))
+ start ++;
+
+ /*
+ * Then copy the value...
+ */
+
+ level = 0;
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+
+ while (bufptr < bufend)
+ {
+ if (isspace(*start & 255) && !level)
+ break;
+
+ *bufptr++ = *start;
+
+ if (*start == '(')
+ level ++;
+ else if (*start == ')')
+ {
+ if (!level)
+ {
+ start ++;
+ break;
+ }
+ else
+ level --;
+ }
+ else if (*start == '\\')
+ {
+ /*
+ * Copy escaped character...
+ */
+
+ int i; /* Looping var */
+
+
+ for (i = 1;
+ i <= 3 && isdigit(start[i] & 255) && bufptr < bufend;
+ *bufptr++ = start[i], i ++);
+ }
+
+ start ++;
+ }
+
+ *bufptr = '\0';
+
+ /*
+ * Return the value and new pointer into the line...
+ */
+
+ if (end)
+ *end = (char *)start;
+
+ if (bufptr == bufend)
+ return (NULL);
+ else
+ return (buffer);
+}
+
+
+/*
+ * 'set_pstops_options()' - Set pstops options.
+ */
+
+static void
+set_pstops_options(
+ pstops_doc_t *doc, /* I - Document information */
+ ppd_file_t *ppd, /* I - PPD file */
+ char *argv[], /* I - Command-line arguments */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ const char *val; /* Option value */
+ int intval; /* Integer option value */
+ ppd_attr_t *attr; /* PPD attribute */
+ ppd_option_t *option; /* PPD option */
+ ppd_choice_t *choice; /* PPD choice */
+ const char *content_type; /* Original content type */
+ int max_copies; /* Maximum number of copies supported */
+
+
+ /*
+ * Initialize document information structure...
+ */
+
+ memset(doc, 0, sizeof(pstops_doc_t));
+
+ doc->job_id = atoi(argv[1]);
+ doc->user = argv[2];
+ doc->title = argv[3];
+ doc->copies = atoi(argv[4]);
+
+ if (ppd && ppd->landscape > 0)
+ doc->normal_landscape = 1;
+
+ doc->bounding_box[0] = (int)PageLeft;
+ doc->bounding_box[1] = (int)PageBottom;
+ doc->bounding_box[2] = (int)PageRight;
+ doc->bounding_box[3] = (int)PageTop;
+
+ doc->new_bounding_box[0] = INT_MAX;
+ doc->new_bounding_box[1] = INT_MAX;
+ doc->new_bounding_box[2] = INT_MIN;
+ doc->new_bounding_box[3] = INT_MIN;
+
+ /*
+ * AP_FIRSTPAGE_* and the corresponding non-first-page options.
+ */
+
+ doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
+ options);
+ doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
+ options);
+ doc->ap_media_color = cupsGetOption("AP_FIRSTPAGE_MediaColor", num_options,
+ options);
+ doc->ap_media_type = cupsGetOption("AP_FIRSTPAGE_MediaType", num_options,
+ options);
+ doc->ap_page_region = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
+ options);
+ doc->ap_page_size = cupsGetOption("AP_FIRSTPAGE_PageSize", num_options,
+ options);
+
+ if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
+ doc->input_slot = choice->choice;
+ if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
+ doc->manual_feed = choice->choice;
+ if ((choice = ppdFindMarkedChoice(ppd, "MediaColor")) != NULL)
+ doc->media_color = choice->choice;
+ if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
+ doc->media_type = choice->choice;
+ if ((choice = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
+ doc->page_region = choice->choice;
+ if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
+ doc->page_size = choice->choice;
+
+ /*
+ * collate, multiple-document-handling
+ */
+
+ if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
+ {
+ /*
+ * This IPP attribute is unnecessarily complicated...
+ *
+ * single-document, separate-documents-collated-copies, and
+ * single-document-new-sheet all require collated copies.
+ *
+ * separate-documents-uncollated-copies allows for uncollated copies.
+ */
+
+ doc->collate = _cups_strcasecmp(val, "separate-documents-uncollated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ (!_cups_strcasecmp(val, "true") ||!_cups_strcasecmp(val, "on") ||
+ !_cups_strcasecmp(val, "yes")))
+ doc->collate = 1;
+
+ /*
+ * emit-jcl
+ */
+
+ if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
+ (!_cups_strcasecmp(val, "false") || !_cups_strcasecmp(val, "off") ||
+ !_cups_strcasecmp(val, "no") || !strcmp(val, "0")))
+ doc->emit_jcl = 0;
+ else
+ doc->emit_jcl = 1;
+
+ /*
+ * fit-to-page/ipp-attribute-fidelity
+ *
+ * (Only for original PostScript content)
+ */
+
+ if ((content_type = getenv("CONTENT_TYPE")) == NULL)
+ content_type = "application/postscript";
+
+ if (!_cups_strcasecmp(content_type, "application/postscript"))
+ {
+ if ((val = cupsGetOption("fit-to-page", num_options, options)) != NULL &&
+ !_cups_strcasecmp(val, "true"))
+ doc->fit_to_page = 1;
+ else if ((val = cupsGetOption("ipp-attribute-fidelity", num_options,
+ options)) != NULL &&
+ !_cups_strcasecmp(val, "true"))
+ doc->fit_to_page = 1;
+ }
+
+ /*
+ * mirror/MirrorPrint
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL)
+ {
+ val = choice->choice;
+ choice->marked = 0;
+ }
+ else
+ val = cupsGetOption("mirror", num_options, options);
+
+ if (val && (!_cups_strcasecmp(val, "true") || !_cups_strcasecmp(val, "on") ||
+ !_cups_strcasecmp(val, "yes")))
+ doc->mirror = 1;
+
+ /*
+ * number-up
+ */
+
+ if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
+ {
+ switch (intval = atoi(val))
+ {
+ case 1 :
+ case 2 :
+ case 4 :
+ case 6 :
+ case 9 :
+ case 16 :
+ doc->number_up = intval;
+ break;
+ default :
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unsupported number-up value %d, using "
+ "number-up=1."), intval);
+ doc->number_up = 1;
+ break;
+ }
+ }
+ else
+ doc->number_up = 1;
+
+ /*
+ * number-up-layout
+ */
+
+ if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
+ {
+ if (!_cups_strcasecmp(val, "lrtb"))
+ doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
+ else if (!_cups_strcasecmp(val, "lrbt"))
+ doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
+ else if (!_cups_strcasecmp(val, "rltb"))
+ doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
+ else if (!_cups_strcasecmp(val, "rlbt"))
+ doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
+ else if (!_cups_strcasecmp(val, "tblr"))
+ doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
+ else if (!_cups_strcasecmp(val, "tbrl"))
+ doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
+ else if (!_cups_strcasecmp(val, "btlr"))
+ doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
+ else if (!_cups_strcasecmp(val, "btrl"))
+ doc->number_up_layout = PSTOPS_LAYOUT_BTRL;
+ else
+ {
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unsupported number-up-layout value %s, using "
+ "number-up-layout=lrtb."), val);
+ doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
+ }
+ }
+ else
+ doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
+
+ /*
+ * OutputOrder
+ */
+
+ if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
+ {
+ if (!_cups_strcasecmp(val, "Reverse"))
+ doc->output_order = 1;
+ }
+ else if (ppd)
+ {
+ /*
+ * Figure out the right default output order from the PPD file...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
+ (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
+ attr->value)
+ doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
+ else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
+ attr->value)
+ doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
+ }
+
+ /*
+ * page-border
+ */
+
+ if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
+ {
+ if (!_cups_strcasecmp(val, "none"))
+ doc->page_border = PSTOPS_BORDERNONE;
+ else if (!_cups_strcasecmp(val, "single"))
+ doc->page_border = PSTOPS_BORDERSINGLE;
+ else if (!_cups_strcasecmp(val, "single-thick"))
+ doc->page_border = PSTOPS_BORDERSINGLE2;
+ else if (!_cups_strcasecmp(val, "double"))
+ doc->page_border = PSTOPS_BORDERDOUBLE;
+ else if (!_cups_strcasecmp(val, "double-thick"))
+ doc->page_border = PSTOPS_BORDERDOUBLE2;
+ else
+ {
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unsupported page-border value %s, using "
+ "page-border=none."), val);
+ doc->page_border = PSTOPS_BORDERNONE;
+ }
+ }
+ else
+ doc->page_border = PSTOPS_BORDERNONE;
+
+ /*
+ * page-label
+ */
+
+ doc->page_label = cupsGetOption("page-label", num_options, options);
+
+ /*
+ * page-ranges
+ */
+
+ doc->page_ranges = cupsGetOption("page-ranges", num_options, options);
+
+ /*
+ * page-set
+ */
+
+ doc->page_set = cupsGetOption("page-set", num_options, options);
+
+ /*
+ * Now figure out if we have to force collated copies, etc.
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
+ max_copies = atoi(attr->value);
+ else if (ppd && ppd->manual_copies)
+ max_copies = 1;
+ else
+ max_copies = 9999;
+
+ if (doc->copies > max_copies)
+ doc->collate = 1;
+ else if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
+ {
+ /*
+ * Force collated copies when printing a duplexed document to
+ * a non-PS printer that doesn't do hardware copy generation.
+ * Otherwise the copies will end up on the front/back side of
+ * each page.
+ */
+
+ doc->collate = 1;
+ }
+
+ /*
+ * See if we have to filter the fast or slow way...
+ */
+
+ if (doc->collate && doc->copies > 1)
+ {
+ /*
+ * See if we need to manually collate the pages...
+ */
+
+ doc->slow_collate = 1;
+
+ if (doc->copies <= max_copies &&
+ (choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
+ !_cups_strcasecmp(choice->choice, "True"))
+ {
+ /*
+ * Hardware collate option is selected, see if the option is
+ * conflicting - if not, collate in hardware. Otherwise,
+ * turn the hardware collate option off...
+ */
+
+ if ((option = ppdFindOption(ppd, "Collate")) != NULL &&
+ !option->conflicted)
+ doc->slow_collate = 0;
+ else
+ ppdMarkOption(ppd, "Collate", "False");
+ }
+ }
+ else
+ doc->slow_collate = 0;
+
+ if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order)
+ doc->slow_order = 1;
+ else
+ doc->slow_order = 0;
+
+ if (Duplex &&
+ (doc->slow_collate || doc->slow_order ||
+ ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
+ attr->value && !_cups_strcasecmp(attr->value, "true"))))
+ doc->slow_duplex = 1;
+ else
+ doc->slow_duplex = 0;
+
+ /*
+ * Create a temporary file for page data if we need to filter slowly...
+ */
+
+ if (doc->slow_order || doc->slow_collate)
+ {
+ if ((doc->temp = cupsTempFile2(doc->tempfile,
+ sizeof(doc->tempfile))) == NULL)
+ {
+ perror("DEBUG: Unable to create temporary file");
+ exit(1);
+ }
+ }
+
+ /*
+ * Figure out if we should use ESPshowpage or not...
+ */
+
+ if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 ||
+ doc->page_border)
+ {
+ /*
+ * Yes, use ESPshowpage...
+ */
+
+ doc->use_ESPshowpage = 1;
+ }
+
+ fprintf(stderr, "DEBUG: slow_collate=%d, slow_duplex=%d, slow_order=%d\n",
+ doc->slow_collate, doc->slow_duplex, doc->slow_order);
+}
+
+
+/*
+ * 'skip_page()' - Skip past a page that won't be printed.
+ */
+
+static ssize_t /* O - Length of next line */
+skip_page(cups_file_t *fp, /* I - File to read from */
+ char *line, /* I - Line buffer */
+ ssize_t linelen, /* I - Length of initial line */
+ size_t linesize) /* I - Size of line buffer */
+{
+ int level; /* Embedded document level */
+
+
+ level = 0;
+
+ while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+ {
+ if (level == 0 &&
+ (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9)))
+ break;
+ else if (!strncmp(line, "%%BeginDocument", 15) ||
+ !strncmp(line, "%ADO_BeginApplication", 21))
+ level ++;
+ else if ((!strncmp(line, "%%EndDocument", 13) ||
+ !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
+ level --;
+ else if (!strncmp(line, "%%BeginBinary:", 14) ||
+ (!strncmp(line, "%%BeginData:", 12) &&
+ !strstr(line, "ASCII") && !strstr(line, "Hex")))
+ {
+ /*
+ * Skip binary data...
+ */
+
+ int bytes; /* Bytes of data */
+
+
+ bytes = atoi(strchr(line, ':') + 1);
+
+ while (bytes > 0)
+ {
+ if (bytes > linesize)
+ linelen = cupsFileRead(fp, line, linesize);
+ else
+ linelen = cupsFileRead(fp, line, bytes);
+
+ if (linelen < 1)
+ {
+ line[0] = '\0';
+ perror("ERROR: Early end-of-file while reading binary data");
+ return (0);
+ }
+
+ bytes -= linelen;
+ }
+ }
+ }
+
+ return (linelen);
+}
+
+
+/*
+ * 'start_nup()' - Start processing for N-up printing.
+ */
+
+static void
+start_nup(pstops_doc_t *doc, /* I - Document information */
+ int number, /* I - Page number */
+ int show_border, /* I - Show the border? */
+ const int *bounding_box) /* I - BoundingBox value */
+{
+ int pos; /* Position on page */
+ int x, y; /* Relative position of subpage */
+ float w, l, /* Width and length of subpage */
+ tx, ty; /* Translation values for subpage */
+ float pagew, /* Printable width of page */
+ pagel; /* Printable height of page */
+ int bboxx, /* BoundingBox X origin */
+ bboxy, /* BoundingBox Y origin */
+ bboxw, /* BoundingBox width */
+ bboxl; /* BoundingBox height */
+ float margin = 0; /* Current margin for border */
+
+
+ if (doc->number_up > 1)
+ doc_puts(doc, "userdict/ESPsave save put\n");
+
+ pos = (number - 1) % doc->number_up;
+ pagew = PageRight - PageLeft;
+ pagel = PageTop - PageBottom;
+
+ if (doc->fit_to_page)
+ {
+ bboxx = bounding_box[0];
+ bboxy = bounding_box[1];
+ bboxw = bounding_box[2] - bounding_box[0];
+ bboxl = bounding_box[3] - bounding_box[1];
+ }
+ else
+ {
+ bboxx = 0;
+ bboxy = 0;
+ bboxw = PageWidth;
+ bboxl = PageLength;
+ }
+
+ fprintf(stderr, "DEBUG: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
+ fprintf(stderr, "DEBUG: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d\n",
+ bboxx, bboxy, bboxw, bboxl);
+ fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n",
+ PageLeft, PageRight);
+ fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n",
+ PageTop, PageBottom);
+ fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
+ PageWidth, PageLength);
+
+ switch (Orientation)
+ {
+ case 1 : /* Landscape */
+ doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", PageLength);
+ break;
+ case 2 : /* Reverse Portrait */
+ doc_printf(doc, "%.1f %.1f translate 180 rotate\n", PageWidth,
+ PageLength);
+ break;
+ case 3 : /* Reverse Landscape */
+ doc_printf(doc, "0.0 %.1f translate -90 rotate\n", PageWidth);
+ break;
+ }
+
+ /*
+ * Mirror the page as needed...
+ */
+
+ if (doc->mirror)
+ doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
+
+ /*
+ * Offset and scale as necessary for fit_to_page/fit-to-page/number-up...
+ */
+
+ if (Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1))
+ doc_printf(doc, "%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
+ else if (doc->number_up > 1 || doc->fit_to_page)
+ doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
+
+ switch (doc->number_up)
+ {
+ default :
+ if (doc->fit_to_page)
+ {
+ w = pagew;
+ l = w * bboxl / bboxw;
+
+ if (l > pagel)
+ {
+ l = pagel;
+ w = l * bboxw / bboxl;
+ }
+
+ tx = 0.5 * (pagew - w);
+ ty = 0.5 * (pagel - l);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty,
+ w / bboxw, l / bboxl);
+ }
+ else
+ w = PageWidth;
+ break;
+
+ case 2 :
+ if (Orientation & 1)
+ {
+ x = pos & 1;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ x = 1 - x;
+
+ w = pagel;
+ l = w * bboxl / bboxw;
+
+ if (l > (pagew * 0.5))
+ {
+ l = pagew * 0.5;
+ w = l * bboxw / bboxl;
+ }
+
+ tx = 0.5 * (pagew * 0.5 - l);
+ ty = 0.5 * (pagel - w);
+
+ if (doc->normal_landscape)
+ doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
+ else
+ doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl);
+ }
+ else
+ {
+ x = pos & 1;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 1 - x;
+
+ l = pagew;
+ w = l * bboxw / bboxl;
+
+ if (w > (pagel * 0.5))
+ {
+ w = pagel * 0.5;
+ l = w * bboxl / bboxw;
+ }
+
+ tx = 0.5 * (pagel * 0.5 - w);
+ ty = 0.5 * (pagew - l);
+
+ if (doc->normal_landscape)
+ doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
+ else
+ doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl);
+ }
+ break;
+
+ case 4 :
+ if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
+ {
+ x = (pos / 2) & 1;
+ y = pos & 1;
+ }
+ else
+ {
+ x = pos & 1;
+ y = (pos / 2) & 1;
+ }
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 1 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 1 - y;
+
+ w = pagew * 0.5;
+ l = w * bboxl / bboxw;
+
+ if (l > (pagel * 0.5))
+ {
+ l = pagel * 0.5;
+ w = l * bboxw / bboxl;
+ }
+
+ tx = 0.5 * (pagew * 0.5 - w);
+ ty = 0.5 * (pagel * 0.5 - l);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ tx + x * pagew * 0.5, ty + y * pagel * 0.5,
+ w / bboxw, l / bboxl);
+ break;
+
+ case 6 :
+ if (Orientation & 1)
+ {
+ if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
+ {
+ x = pos / 3;
+ y = pos % 3;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 1 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 2 - y;
+ }
+ else
+ {
+ x = pos & 1;
+ y = pos / 2;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 1 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 2 - y;
+ }
+
+ w = pagel * 0.5;
+ l = w * bboxl / bboxw;
+
+ if (l > (pagew * 0.333))
+ {
+ l = pagew * 0.333;
+ w = l * bboxw / bboxl;
+ }
+
+ tx = 0.5 * (pagel - 2 * w);
+ ty = 0.5 * (pagew - 3 * l);
+
+ if (doc->normal_landscape)
+ doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
+ else
+ doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ tx + x * w, ty + y * l, l / bboxl, w / bboxw);
+ }
+ else
+ {
+ if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
+ {
+ x = pos / 2;
+ y = pos & 1;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 2 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 1 - y;
+ }
+ else
+ {
+ x = pos % 3;
+ y = pos / 3;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 2 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 1 - y;
+ }
+
+ l = pagew * 0.5;
+ w = l * bboxw / bboxl;
+
+ if (w > (pagel * 0.333))
+ {
+ w = pagel * 0.333;
+ l = w * bboxl / bboxw;
+ }
+
+ tx = 0.5 * (pagel - 3 * w);
+ ty = 0.5 * (pagew - 2 * l);
+
+ if (doc->normal_landscape)
+ doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
+ else
+ doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ tx + w * x, ty + l * y, w / bboxw, l / bboxl);
+
+ }
+ break;
+
+ case 9 :
+ if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
+ {
+ x = (pos / 3) % 3;
+ y = pos % 3;
+ }
+ else
+ {
+ x = pos % 3;
+ y = (pos / 3) % 3;
+ }
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 2 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 2 - y;
+
+ w = pagew * 0.333;
+ l = w * bboxl / bboxw;
+
+ if (l > (pagel * 0.333))
+ {
+ l = pagel * 0.333;
+ w = l * bboxw / bboxl;
+ }
+
+ tx = 0.5 * (pagew * 0.333 - w);
+ ty = 0.5 * (pagel * 0.333 - l);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ tx + x * pagew * 0.333, ty + y * pagel * 0.333,
+ w / bboxw, l / bboxl);
+ break;
+
+ case 16 :
+ if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
+ {
+ x = (pos / 4) & 3;
+ y = pos & 3;
+ }
+ else
+ {
+ x = pos & 3;
+ y = (pos / 4) & 3;
+ }
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
+ x = 3 - x;
+
+ if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
+ y = 3 - y;
+
+ w = pagew * 0.25;
+ l = w * bboxl / bboxw;
+
+ if (l > (pagel * 0.25))
+ {
+ l = pagel * 0.25;
+ w = l * bboxw / bboxl;
+ }
+
+ tx = 0.5 * (pagew * 0.25 - w);
+ ty = 0.5 * (pagel * 0.25 - l);
+
+ doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+ tx + x * pagew * 0.25, ty + y * pagel * 0.25,
+ w / bboxw, l / bboxl);
+ break;
+ }
+
+ /*
+ * Draw borders as necessary...
+ */
+
+ if (doc->page_border && show_border)
+ {
+ int rects; /* Number of border rectangles */
+ float fscale; /* Scaling value for points */
+
+
+ rects = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
+ fscale = PageWidth / w;
+ margin = 2.25 * fscale;
+
+ /*
+ * Set the line width and color...
+ */
+
+ doc_puts(doc, "gsave\n");
+ doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n",
+ (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale :
+ 0.24 * fscale);
+
+ /*
+ * Draw border boxes...
+ */
+
+ for (; rects > 0; rects --, margin += 2 * fscale)
+ if (doc->number_up > 1)
+ doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
+ margin,
+ margin,
+ bboxw - 2 * margin,
+ bboxl - 2 * margin);
+ else
+ doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
+ PageLeft + margin,
+ PageBottom + margin,
+ PageRight - PageLeft - 2 * margin,
+ PageTop - PageBottom - 2 * margin);
+
+ /*
+ * Restore pen settings...
+ */
+
+ doc_puts(doc, "grestore\n");
+ }
+
+ if (doc->fit_to_page)
+ {
+ /*
+ * Offset the page by its bounding box...
+ */
+
+ doc_printf(doc, "%d %d translate\n", -bounding_box[0],
+ -bounding_box[1]);
+ }
+
+ if (doc->fit_to_page || doc->number_up > 1)
+ {
+ /*
+ * Clip the page to the page's bounding box...
+ */
+
+ doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n",
+ bboxx + margin, bboxy + margin,
+ bboxw - 2 * margin, bboxl - 2 * margin);
+ }
+}
+
+
+/*
+ * 'write_label_prolog()' - Write the prolog with the classification
+ * and page label.
+ */
+
+static void
+write_label_prolog(pstops_doc_t *doc, /* I - Document info */
+ const char *label, /* I - Page label */
+ float bottom, /* I - Bottom position in points */
+ float top, /* I - Top position in points */
+ float width) /* I - Width in points */
+{
+ const char *classification; /* CLASSIFICATION environment variable */
+ const char *ptr; /* Temporary string pointer */
+
+
+ /*
+ * First get the current classification...
+ */
+
+ if ((classification = getenv("CLASSIFICATION")) == NULL)
+ classification = "";
+ if (strcmp(classification, "none") == 0)
+ classification = "";
+
+ /*
+ * If there is nothing to show, bind an empty 'write labels' procedure
+ * and return...
+ */
+
+ if (!classification[0] && (label == NULL || !label[0]))
+ {
+ doc_puts(doc, "userdict/ESPwl{}bind put\n");
+ return;
+ }
+
+ /*
+ * Set the classification + page label string...
+ */
+
+ doc_puts(doc, "userdict");
+ if (!strcmp(classification, "confidential"))
+ doc_puts(doc, "/ESPpl(CONFIDENTIAL");
+ else if (!strcmp(classification, "classified"))
+ doc_puts(doc, "/ESPpl(CLASSIFIED");
+ else if (!strcmp(classification, "secret"))
+ doc_puts(doc, "/ESPpl(SECRET");
+ else if (!strcmp(classification, "topsecret"))
+ doc_puts(doc, "/ESPpl(TOP SECRET");
+ else if (!strcmp(classification, "unclassified"))
+ doc_puts(doc, "/ESPpl(UNCLASSIFIED");
+ else
+ {
+ doc_puts(doc, "/ESPpl(");
+
+ for (ptr = classification; *ptr; ptr ++)
+ {
+ if (*ptr < 32 || *ptr > 126)
+ doc_printf(doc, "\\%03o", *ptr);
+ else if (*ptr == '_')
+ doc_puts(doc, " ");
+ else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
+ doc_printf(doc, "\\%c", *ptr);
+ else
+ doc_printf(doc, "%c", *ptr);
+ }
+ }
+
+ if (label)
+ {
+ if (classification[0])
+ doc_puts(doc, " - ");
+
+ /*
+ * Quote the label string as needed...
+ */
+
+ for (ptr = label; *ptr; ptr ++)
+ {
+ if (*ptr < 32 || *ptr > 126)
+ doc_printf(doc, "\\%03o", *ptr);
+ else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
+ doc_printf(doc, "\\%c", *ptr);
+ else
+ doc_printf(doc, "%c", *ptr);
+ }
+ }
+
+ doc_puts(doc, ")put\n");
+
+ /*
+ * Then get a 14 point Helvetica-Bold font...
+ */
+
+ doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n");
+
+ /*
+ * Finally, the procedure to write the labels on the page...
+ */
+
+ doc_puts(doc, "userdict/ESPwl{\n");
+ doc_puts(doc, " ESPpf setfont\n");
+ doc_printf(doc, " ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
+ width * 0.5f);
+ doc_puts(doc, " 1 setgray\n");
+ doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
+ doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
+ doc_puts(doc, " 0 setgray\n");
+ doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
+ doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
+ doc_printf(doc, " dup %.0f moveto ESPpl show\n", bottom + 2.0);
+ doc_printf(doc, " %.0f moveto ESPpl show\n", top - 14.0);
+ doc_puts(doc, "pop\n");
+ doc_puts(doc, "}bind put\n");
+}
+
+
+/*
+ * 'write_labels()' - Write the actual page labels.
+ *
+ * This function is a copy of the one in common.c since we need to
+ * use doc_puts/doc_printf instead of puts/printf...
+ */
+
+static void
+write_labels(pstops_doc_t *doc, /* I - Document information */
+ int orient) /* I - Orientation of the page */
+{
+ float width, /* Width of page */
+ length; /* Length of page */
+
+
+ doc_puts(doc, "gsave\n");
+
+ if ((orient ^ Orientation) & 1)
+ {
+ width = PageLength;
+ length = PageWidth;
+ }
+ else
+ {
+ width = PageWidth;
+ length = PageLength;
+ }
+
+ switch (orient & 3)
+ {
+ case 1 : /* Landscape */
+ doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length);
+ break;
+ case 2 : /* Reverse Portrait */
+ doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length);
+ break;
+ case 3 : /* Reverse Landscape */
+ doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width);
+ break;
+ }
+
+ doc_puts(doc, "ESPwl\n");
+ doc_puts(doc, "grestore\n");
+}
+
+
+/*
+ * 'write_options()' - Write options provided via %%IncludeFeature.
+ */
+
+static void
+write_options(
+ pstops_doc_t *doc, /* I - Document */
+ ppd_file_t *ppd, /* I - PPD file */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int i; /* Looping var */
+ ppd_option_t *option; /* PPD option */
+ int min_order; /* Minimum OrderDependency value */
+ char *doc_setup, /* DocumentSetup commands to send */
+ *any_setup; /* AnySetup commands to send */
+
+
+ /*
+ * Figure out the minimum OrderDependency value...
+ */
+
+ if ((option = ppdFindOption(ppd, "PageRegion")) != NULL)
+ min_order = option->order;
+ else
+ min_order = 999.0f;
+
+ for (i = 0; i < num_options; i ++)
+ if ((option = ppdFindOption(ppd, options[i].name)) != NULL &&
+ option->order < min_order)
+ min_order = option->order;
+
+ /*
+ * Mark and extract them...
+ */
+
+ cupsMarkOptions(ppd, num_options, options);
+
+ doc_setup = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, min_order);
+ any_setup = ppdEmitString(ppd, PPD_ORDER_ANY, min_order);
+
+ /*
+ * Then send them out...
+ */
+
+ if (doc->number_up > 1)
+ {
+ /*
+ * Temporarily restore setpagedevice so we can set the options...
+ */
+
+ doc_puts(doc, "userdict/setpagedevice/CUPSsetpagedevice load put\n");
+ }
+
+ if (doc_setup)
+ {
+ doc_puts(doc, doc_setup);
+ free(doc_setup);
+ }
+
+ if (any_setup)
+ {
+ doc_puts(doc, any_setup);
+ free(any_setup);
+ }
+
+ if (doc->number_up > 1)
+ {
+ /*
+ * Disable setpagedevice again...
+ */
+
+ doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
+ }
+}
+
+
+/*
+ * End of "$Id: pstops.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/raster-driver.header b/cups/libs/filter/raster-driver.header
new file mode 100644
index 000000000..b15c50d99
--- /dev/null
+++ b/cups/libs/filter/raster-driver.header
@@ -0,0 +1,32 @@
+<!--
+ "$Id$"
+
+ Raster printer driver documentation for CUPS.
+
+ Copyright 2007-2012 by Apple Inc.
+ Copyright 1997-2007 by Easy Software Products.
+
+ These coded instructions, statements, and computer programs are the
+ property of Apple Inc. and are protected by Federal copyright
+ law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ which should have been included with this file. If this file is
+ file is missing or damaged, see the license at "http://www.cups.org/".
+-->
+
+<h1 class='title'>Developing Raster Printer Drivers</h1>
+
+<p>This document describes how to develop printer drivers for raster printers. Topics include: <a href='#BASICS'>printer driver basics</a>, <a href='#CREATE'>creating new PPD files</a>, <a href='#FILTERS'>using filters</a>, <a href='#COLOR'>implementing color management</a>, and <a href='#MACOSX'>adding OS X features</a>.</p>
+
+<div class='summary'><table summary='General Information'>
+<tbody>
+<tr>
+ <th>See Also</th>
+ <td>Programming: <a href='postscript-driver.html'>Developing PostScript Printer Drivers</a><br>
+ Programming: <a href='api-filter.html'>Filter and Backend Programming</a><br>
+ Programming: <a href='ppd-compiler.html'>Introduction to the PPD Compiler</a><br>
+ Programming: <a href='api-raster.html'>Raster API</a><br>
+ References: <a href='ref-ppdcfile.html'>PPD Compiler Driver Information File Reference</a><br>
+ Specifications: <a href='spec-ppd.html'>CUPS PPD Extensions</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/filter/raster-driver.shtml b/cups/libs/filter/raster-driver.shtml
new file mode 100644
index 000000000..f56982a77
--- /dev/null
+++ b/cups/libs/filter/raster-driver.shtml
@@ -0,0 +1,194 @@
+<h2 class='title'><a name='BASICS'>Printer Driver Basics</a></h2>
+
+<p>A CUPS raster printer driver consists of a PostScript Printer Description (PPD) file that describes the features and capabilities of the device, one or more <em>filter</em> programs that prepare print data for the device, and zero or more support files for color management, online help, and so forth. The PPD file includes references to all of the filters and support files used by the driver.</p>
+
+<p>Every time a user prints something the scheduler program, <a href='man-cupsd.html'>cupsd(8)</a>, determines the format of the print job and the programs required to convert that job into something the printer understands. CUPS includes filter programs for many common formats, for example to convert Portable Document Format (PDF) files into CUPS raster data. <a href='#FIGURE_1'>Figure 1</a> shows the data flow of a typical print job.</p>
+
+<div class='figure'><table summary='Raster Filter Chain'>
+<caption>Figure 1: <a name='FIGURE_1'>Raster Filter Chain</a></caption>
+<tr><td><img src='../images/cups-raster-chain.png' width='700' height='150' alt='Raster Filter Chain'></td></tr>
+</table></div>
+
+<p>The raster filter converts CUPS raster data into a format the printer understands, for example HP-PCL. CUPS includes several sample raster filters supporting standard page description languages (PDLs). <a href='#TABLE_1'>Table 1</a> shows the raster filters that are bundled with CUPS and the languages they support.</p>
+
+<div class='table'><table summary='Standard CUPS Raster Filters'>
+<caption>Table 1: <a name='TABLE_1'>Standard CUPS Raster Filters</a></caption>
+<thead>
+<tr><th>Filter</th><th>PDLs</th><th>ppdc DriverType</th><th>ppdc #include file</th></tr>
+</thead>
+<tbody>
+<tr><td>rastertoepson</td><td>ESC/P, ESC/P2</td><td>epson</td><td>epson.h</td></tr>
+<tr><td>rastertoescpx</td><td>ESC/P, ESC/P2, EPSON Remote Mode</td><td>escp</td><td>escp.h</td></tr>
+<tr><td>rastertohp</td><td>HP-PCL3, HP-PCL5</td><td>hp</td><td>hp.h</td></tr>
+<tr><td>rastertolabel</td><td>CPCL, Dymo, EPL1, EPL2, Intellitech PCL, ZPL</td><td>label</td><td>label.h</td></tr>
+<tr><td>rastertopclx</td><td>HP-RTL, HP-PCL3, HP-PCL3GUI, HP-PCL5, HP-PCL5c, HP-PCL5e</td><td>pcl</td><td>pcl.h</td></tr>
+</tbody>
+</table></div>
+
+<p>The optional port monitor handles interface-specific protocol or encoding issues. For example, some raster printers use the 1284.4 communications protocol.</p>
+
+<p>The backend handles communications with the printer, sending print data from the last filter to the printer and relaying back-channel data from the printer to the upstream filters. CUPS includes backend programs for common direct-connect interfaces and network protocols, and you can provide your own backend to support custom interfaces and protocols.</p>
+
+<p>The scheduler also supports a special "command" file format for sending maintenance commands and status queries to a printer or printer driver. Command print jobs typically use a single command filter program defined in the PPD file to generate the appropriate printer commands and handle any responses from the printer. <a href='#FIGURE_2'>Figure 2</a> shows the data flow of a typical command job.</p>
+
+<div class='figure'><table summary='Command Filter Chain'>
+<caption>Figure 2: <a name='FIGURE_2'>Command Filter Chain</a></caption>
+<tr><td><img src='../images/cups-command-chain.png' width='575' height='150' alt='Command Filter Chain'></td></tr>
+</table></div>
+
+<p>Raster printer drivers must provide their own command filter.</p>
+
+
+<h2 class='title'><a name='CREATING'>Creating New PPD Files</a></h2>
+
+<p>We recommend using the CUPS PPD compiler, <a href='man-ppdc.html'>ppdc(1)</a>, to create new PPD files since it manages many of the tedious (and error-prone!) details of paper sizes and localization for you. It also allows you to easily support multiple devices from a single source file. For more information see the "<a href='ppd-compiler.html'>Introduction to the PPD Compiler</a>" document. <a href='#LISTING_1'>Listing 1</a> shows a driver information file for several similar black-and-white HP-PCL5 laser printers.</p>
+
+<p class='example'>Listing 1: <a name='LISTING_1'>"examples/laserjet-basic.drv"</a></p>
+
+<pre class='example'>
+<I>// Include standard font and media definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;font.defs&gt;
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;media.defs&gt;
+
+<I>// Include HP-PCL driver definitions</I>
+<a href='ref-ppdcfile.html#_include'>#include</a> &lt;pcl.h&gt;
+
+<I>// Specify that this driver uses the HP-PCL driver...</I>
+<a href='ref-ppdcfile.html#DriverType'>DriverType</a> pcl
+
+<I>// Specify the driver options via the model number...</I>
+<a href='ref-ppdcfile.html#ModelNumber'>ModelNumber</a> ($PCL_PAPER_SIZE $PCL_PJL $PCL_PJL_RESOLUTION)
+
+<I>// List the fonts that are supported, in this case all standard fonts...</I>
+<a href='ref-ppdcfile.html#Font'>Font</a> *
+
+<I>// Manufacturer and driver version</I>
+<a href='ref-ppdcfile.html#Manufacturer'>Manufacturer</a> "HP"
+<a href='ref-ppdcfile.html#Version'>Version</a> 1.0
+
+<I>// Supported page sizes and their margins</I>
+<a href='ref-ppdcfile.html#HWMargins'>HWMargins</a> 18 12 18 12
+*<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Letter
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Legal
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Executive
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Monarch
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Statement
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> FanFoldGermanLegal
+
+<a href='ref-ppdcfile.html#HWMargins'>HWMargins</a> 18 12.72 18 12.72
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Env10
+
+<a href='ref-ppdcfile.html#HWMargins'>HWMargins</a> 9.72 12 9.72 12
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A4
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> A5
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> B5
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> EnvC5
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> EnvDL
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> EnvISOB5
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> Postcard
+<a href='ref-ppdcfile.html#MediaSize'>MediaSize</a> DoublePostcard
+
+<I>// Only black-and-white output with mode 3 compression...</I>
+<a href='ref-ppdcfile.html#ColorModel'>ColorModel</a> Gray k chunky 3
+
+<I>// Supported resolutions</I>
+<a href='ref-ppdcfile.html#Resolution'>Resolution</a> - 1 0 0 0 "300dpi/300 DPI"
+*<a href='ref-ppdcfile.html#Resolution'>Resolution</a> - 8 0 0 0 "600dpi/600 DPI"
+
+<I>// Supported input slots</I>
+*<a href='ref-ppdcfile.html#InputSlot'>InputSlot</a> 7 "Auto/Automatic Selection"
+<a href='ref-ppdcfile.html#InputSlot'>InputSlot</a> 2 "Manual/Tray 1 - Manual Feed"
+<a href='ref-ppdcfile.html#InputSlot'>InputSlot</a> 4 "Upper/Tray 1"
+<a href='ref-ppdcfile.html#InputSlot'>InputSlot</a> 1 "Lower/Tray 2"
+<a href='ref-ppdcfile.html#InputSlot'>InputSlot</a> 5 "LargeCapacity/Tray 3"
+
+<I>// Tray 3 is an option...</I>
+<a href='ref-ppdcfile.html#Installable'>Installable</a> "OptionLargeCapacity/Tray 3 Installed"
+<a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*OptionLargeCapacity False *InputSlot LargeCapacity"
+
+{
+ <I>// HP LaserJet 2100 Series</I>
+ <a href='ref-ppdcfile.html#Throughput'>Throughput</a> 10
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "LaserJet 2100 Series"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "hpljt211.ppd"
+}
+
+{
+ <I>// LaserJet 2200 and 2300 series have duplexer option...</I>
+ <a href='ref-ppdcfile.html#Duplex'>Duplex</a> normal
+ <a href='ref-ppdcfile.html#Installable'>Installable</a> "OptionDuplex/Duplexer Installed"
+ <a href='ref-ppdcfile.html#UIConstraints'>UIConstraints</a> "*OptionDuplex False *Duplex"
+
+ {
+ <I>// HP LaserJet 2200 Series</I>
+ <a href='ref-ppdcfile.html#Throughput'>Throughput</a> 19
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "LaserJet 2200 Series"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "hpljt221.ppd"
+ }
+
+ {
+ <I>// HP LaserJet 2300 Series</I>
+ <a href='ref-ppdcfile.html#Throughput'>Throughput</a> 25
+ <a href='ref-ppdcfile.html#ModelName'>ModelName</a> "LaserJet 2300 Series"
+ <a href='ref-ppdcfile.html#PCFileName'>PCFileName</a> "hpljt231.ppd"
+ }
+}
+</pre>
+
+
+<h2 class='title'><a name='FILTERS'>Using Filters</a></h2>
+
+<p>The standard CUPS raster filters can be specified using the
+<a href='ref-ppdcfile.html#DriverType'><tt>DriverType</tt></a> directive, for example:</p>
+
+<pre class='example'>
+<I>// Specify that this driver uses the HP-PCL driver...</I>
+<a href='ref-ppdcfile.html#DriverType'>DriverType</a> pcl
+</pre>
+
+<p><a href='#TABLE_1'>Table 1</a> shows the driver types for each of the standard CUPS raster filters. For drivers that do not use the standard raster filters, the "custom" type is used with <a href='ref-ppdcfile.html#Filter'><tt>Filter</tt></a> directives:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#DriverType'>DriverType</a> custom
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-raster 100 /path/to/raster/filter
+<a href='ref-ppdcfile.html#Filter'>Filter</a> application/vnd.cups-command 100 /path/to/command/filter
+</pre>
+
+
+<h2 class='title'><a name='COLOR'>Implementing Color Management</a></h2>
+
+<p>CUPS uses ICC color profiles to provide more accurate color reproduction. The <a href='spec-ppd.html#cupsICCProfile'><tt>cupsICCProfile</tt></a> attribute defines the color profiles that are available for a given printer, for example:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsICCProfile "ColorModel.MediaType.Resolution/Description" /path/to/ICC/profile
+</pre>
+
+<p>where "ColorModel.MediaType.Resolution" defines a selector based on the corresponding option selections. A simple driver might only define profiles for the color models that are supported, for example a printer supporting Gray and RGB might use:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsICCProfile "Gray../Grayscale Profile" /path/to/ICC/gray-profile
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> cupsICCProfile "RGB../Full Color Profile" /path/to/ICC/rgb-profile
+</pre>
+
+<p>The options used for profile selection can be customized using the <tt>cupsICCQualifier2</tt> and <tt>cupsICCQualifier3</tt> attributes.</p>
+
+<h3><span class='info'>Since OS X 10.5</span>Custom Color Matching Support</h3>
+
+<p>OS X printer drivers that are based on an existing standard RGB colorspace can tell the system to use the corresponding colorspace instead of an arbitrary ICC color profile when doing color management. The <a href='#APCustom'><tt>APSupportsCustomColorMatching</tt></a> and <tt>APDefaultCustomColorMatchingProfile</tt> attributes can be used to enable this mode:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APSupportsCustomColorMatching "" true
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APDefaultCustomColorMatchingProfile "" sRGB
+</pre>
+
+
+<h2 class='title'><a name='MACOSX'>Adding OS X Features</a></h2>
+
+<p>OS X printer drivers can provide <a href='spec-ppd.html#MACOSX'>additional attributes</a> to specify additional option panes in the print dialog, an image of the printer, a help book, and option presets for the driver software:</p>
+
+<pre class='example'>
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APDialogExtension "" /Library/Printers/Vendor/filename.plugin
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APHelpBook "" /Library/Printers/Vendor/filename.bundle
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APPrinterIconPath "" /Library/Printers/Vendor/filename.icns
+<a href='ref-ppdcfile.html#Attribute'>Attribute</a> APPrinterPreset "name/text" "*option choice ..."
+</pre>
diff --git a/cups/libs/filter/raster.c b/cups/libs/filter/raster.c
new file mode 100644
index 000000000..8dd76abe7
--- /dev/null
+++ b/cups/libs/filter/raster.c
@@ -0,0 +1,1479 @@
+/*
+ * "$Id: raster.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Raster file routines for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * This file is part of the CUPS Imaging library.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * cupsRasterClose() - Close a raster stream.
+ * cupsRasterOpen() - Open a raster stream using a file descriptor.
+ * cupsRasterOpenIO() - Open a raster stream using a callback function.
+ * cupsRasterReadHeader() - Read a raster page header and store it in a
+ * version 1 page header structure.
+ * cupsRasterReadHeader2() - Read a raster page header and store it in a
+ * version 2 page header structure.
+ * cupsRasterReadPixels() - Read raster pixels.
+ * cupsRasterWriteHeader() - Write a raster page header from a version 1
+ * page header structure.
+ * cupsRasterWriteHeader2() - Write a raster page header from a version 2
+ * page header structure.
+ * cupsRasterWritePixels() - Write raster pixels.
+ * cups_raster_read_header() - Read a raster page header.
+ * cups_raster_read() - Read through the raster buffer.
+ * cups_raster_update() - Update the raster header and row count for the
+ * current page.
+ * cups_raster_write() - Write a row of compressed raster data...
+ * cups_read_fd() - Read bytes from a file.
+ * cups_swap() - Swap bytes in raster data...
+ * cups_write_fd() - Write bytes to a file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/raster-private.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif /* HAVE_STDINT_H */
+
+
+/*
+ * Private structures...
+ */
+
+struct _cups_raster_s /**** Raster stream data ****/
+{
+ unsigned sync; /* Sync word from start of stream */
+ void *ctx; /* File descriptor */
+ cups_raster_iocb_t iocb; /* IO callback */
+ cups_mode_t mode; /* Read/write mode */
+ cups_page_header2_t header; /* Raster header for current page */
+ int count, /* Current row run-length count */
+ remaining, /* Remaining rows in page image */
+ bpp; /* Bytes per pixel/color */
+ unsigned char *pixels, /* Pixels for current row */
+ *pend, /* End of pixel buffer */
+ *pcurrent; /* Current byte in pixel buffer */
+ int compressed, /* Non-zero if data is compressed */
+ swapped; /* Non-zero if data is byte-swapped */
+ unsigned char *buffer, /* Read/write buffer */
+ *bufptr, /* Current (read) position in buffer */
+ *bufend; /* End of current (read) buffer */
+ size_t bufsize; /* Buffer size */
+};
+
+
+/*
+ * Local functions...
+ */
+
+static int cups_raster_io(cups_raster_t *r, unsigned char *buf, int bytes);
+static unsigned cups_raster_read_header(cups_raster_t *r);
+static int cups_raster_read(cups_raster_t *r, unsigned char *buf,
+ int bytes);
+static void cups_raster_update(cups_raster_t *r);
+static int cups_raster_write(cups_raster_t *r,
+ const unsigned char *pixels);
+static ssize_t cups_read_fd(void *ctx, unsigned char *buf, size_t bytes);
+static void cups_swap(unsigned char *buf, int bytes);
+static ssize_t cups_write_fd(void *ctx, unsigned char *buf, size_t bytes);
+
+
+/*
+ * 'cupsRasterClose()' - Close a raster stream.
+ *
+ * The file descriptor associated with the raster stream must be closed
+ * separately as needed.
+ */
+
+void
+cupsRasterClose(cups_raster_t *r) /* I - Stream to close */
+{
+ if (r != NULL)
+ {
+ if (r->buffer)
+ free(r->buffer);
+
+ if (r->pixels)
+ free(r->pixels);
+
+ free(r);
+ }
+}
+
+
+/*
+ * 'cupsRasterOpen()' - Open a raster stream using a file descriptor.
+ *
+ * This function associates a raster stream with the given file descriptor.
+ * For most printer driver filters, "fd" will be 0 (stdin). For most raster
+ * image processor (RIP) filters that generate raster data, "fd" will be 1
+ * (stdout).
+ *
+ * When writing raster data, the @code CUPS_RASTER_WRITE@,
+ * @code CUPS_RASTER_WRITE_COMPRESS@, or @code CUPS_RASTER_WRITE_PWG@ mode can
+ * be used - compressed and PWG output is generally 25-50% smaller but adds a
+ * 100-300% execution time overhead.
+ */
+
+cups_raster_t * /* O - New stream */
+cupsRasterOpen(int fd, /* I - File descriptor */
+ cups_mode_t mode) /* I - Mode - @code CUPS_RASTER_READ@,
+ @code CUPS_RASTER_WRITE@,
+ @code CUPS_RASTER_WRITE_COMPRESSED@,
+ or @code CUPS_RASTER_WRITE_PWG@ */
+{
+ if (mode == CUPS_RASTER_READ)
+ return (cupsRasterOpenIO(cups_read_fd, (void *)((intptr_t)fd), mode));
+ else
+ return (cupsRasterOpenIO(cups_write_fd, (void *)((intptr_t)fd), mode));
+}
+
+
+/*
+ * 'cupsRasterOpenIO()' - Open a raster stream using a callback function.
+ *
+ * This function associates a raster stream with the given callback function and
+ * context pointer.
+ *
+ * When writing raster data, the @code CUPS_RASTER_WRITE@,
+ * @code CUPS_RASTER_WRITE_COMPRESS@, or @code CUPS_RASTER_WRITE_PWG@ mode can
+ * be used - compressed and PWG output is generally 25-50% smaller but adds a
+ * 100-300% execution time overhead.
+ */
+
+cups_raster_t * /* O - New stream */
+cupsRasterOpenIO(
+ cups_raster_iocb_t iocb, /* I - Read/write callback */
+ void *ctx, /* I - Context pointer for callback */
+ cups_mode_t mode) /* I - Mode - @code CUPS_RASTER_READ@,
+ @code CUPS_RASTER_WRITE@,
+ @code CUPS_RASTER_WRITE_COMPRESSED@,
+ or @code CUPS_RASTER_WRITE_PWG@ */
+{
+ cups_raster_t *r; /* New stream */
+
+
+ _cupsRasterClearError();
+
+ if ((r = calloc(sizeof(cups_raster_t), 1)) == NULL)
+ {
+ _cupsRasterAddError("Unable to allocate memory for raster stream: %s\n",
+ strerror(errno));
+ return (NULL);
+ }
+
+ r->ctx = ctx;
+ r->iocb = iocb;
+ r->mode = mode;
+
+ if (mode == CUPS_RASTER_READ)
+ {
+ /*
+ * Open for read - get sync word...
+ */
+
+ if (cups_raster_io(r, (unsigned char *)&(r->sync), sizeof(r->sync)) !=
+ sizeof(r->sync))
+ {
+ _cupsRasterAddError("Unable to read header from raster stream: %s\n",
+ strerror(errno));
+ free(r);
+ return (NULL);
+ }
+
+ if (r->sync != CUPS_RASTER_SYNC &&
+ r->sync != CUPS_RASTER_REVSYNC &&
+ r->sync != CUPS_RASTER_SYNCv1 &&
+ r->sync != CUPS_RASTER_REVSYNCv1 &&
+ r->sync != CUPS_RASTER_SYNCv2 &&
+ r->sync != CUPS_RASTER_REVSYNCv2)
+ {
+ _cupsRasterAddError("Unknown raster format %08x!\n", r->sync);
+ free(r);
+ return (NULL);
+ }
+
+ if (r->sync == CUPS_RASTER_SYNCv2 ||
+ r->sync == CUPS_RASTER_REVSYNCv2)
+ r->compressed = 1;
+
+ if (r->sync == CUPS_RASTER_REVSYNC ||
+ r->sync == CUPS_RASTER_REVSYNCv1 ||
+ r->sync == CUPS_RASTER_REVSYNCv2)
+ r->swapped = 1;
+
+ DEBUG_printf(("r->swapped=%d, r->sync=%08x\n", r->swapped, r->sync));
+ }
+ else
+ {
+ /*
+ * Open for write - put sync word...
+ */
+
+ switch (mode)
+ {
+ default :
+ case CUPS_RASTER_WRITE :
+ r->sync = CUPS_RASTER_SYNC;
+ break;
+
+ case CUPS_RASTER_WRITE_COMPRESSED :
+ r->compressed = 1;
+ r->sync = CUPS_RASTER_SYNCv2;
+ break;
+
+ case CUPS_RASTER_WRITE_PWG :
+ r->compressed = 1;
+ r->sync = htonl(CUPS_RASTER_SYNC_PWG);
+ r->swapped = r->sync != CUPS_RASTER_SYNC_PWG;
+ break;
+ }
+
+ if (cups_raster_io(r, (unsigned char *)&(r->sync), sizeof(r->sync))
+ < sizeof(r->sync))
+ {
+ _cupsRasterAddError("Unable to write raster stream header: %s\n",
+ strerror(errno));
+ free(r);
+ return (NULL);
+ }
+ }
+
+ return (r);
+}
+
+
+/*
+ * 'cupsRasterReadHeader()' - Read a raster page header and store it in a
+ * version 1 page header structure.
+ *
+ * This function is deprecated. Use @link cupsRasterReadHeader2@ instead.
+ *
+ * Version 1 page headers were used in CUPS 1.0 and 1.1 and contain a subset
+ * of the version 2 page header data. This function handles reading version 2
+ * page headers and copying only the version 1 data into the provided buffer.
+ *
+ * @deprecated@
+ */
+
+unsigned /* O - 1 on success, 0 on failure/end-of-file */
+cupsRasterReadHeader(
+ cups_raster_t *r, /* I - Raster stream */
+ cups_page_header_t *h) /* I - Pointer to header data */
+{
+ /*
+ * Get the raster header...
+ */
+
+ if (!cups_raster_read_header(r))
+ return (0);
+
+ /*
+ * Copy the header to the user-supplied buffer...
+ */
+
+ memcpy(h, &(r->header), sizeof(cups_page_header_t));
+
+ return (1);
+}
+
+
+/*
+ * 'cupsRasterReadHeader2()' - Read a raster page header and store it in a
+ * version 2 page header structure.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+unsigned /* O - 1 on success, 0 on failure/end-of-file */
+cupsRasterReadHeader2(
+ cups_raster_t *r, /* I - Raster stream */
+ cups_page_header2_t *h) /* I - Pointer to header data */
+{
+ /*
+ * Get the raster header...
+ */
+
+ if (!cups_raster_read_header(r))
+ return (0);
+
+ /*
+ * Copy the header to the user-supplied buffer...
+ */
+
+ memcpy(h, &(r->header), sizeof(cups_page_header2_t));
+
+ return (1);
+}
+
+
+/*
+ * 'cupsRasterReadPixels()' - Read raster pixels.
+ *
+ * For best performance, filters should read one or more whole lines.
+ * The "cupsBytesPerLine" value from the page header can be used to allocate
+ * the line buffer and as the number of bytes to read.
+ */
+
+unsigned /* O - Number of bytes read */
+cupsRasterReadPixels(cups_raster_t *r, /* I - Raster stream */
+ unsigned char *p, /* I - Pointer to pixel buffer */
+ unsigned len) /* I - Number of bytes to read */
+{
+ int bytes; /* Bytes read */
+ unsigned cupsBytesPerLine; /* cupsBytesPerLine value */
+ unsigned remaining; /* Bytes remaining */
+ unsigned char *ptr, /* Pointer to read buffer */
+ byte, /* Byte from file */
+ *temp; /* Pointer into buffer */
+ int count; /* Repetition count */
+
+
+ if (r == NULL || r->mode != CUPS_RASTER_READ || r->remaining == 0 ||
+ r->header.cupsBytesPerLine == 0)
+ return (0);
+
+ if (!r->compressed)
+ {
+ /*
+ * Read without compression...
+ */
+
+ r->remaining -= len / r->header.cupsBytesPerLine;
+
+ if (cups_raster_io(r, p, len) < (ssize_t)len)
+ return (0);
+
+ /*
+ * Swap bytes as needed...
+ */
+
+ if (r->swapped &&
+ (r->header.cupsBitsPerColor == 16 ||
+ r->header.cupsBitsPerPixel == 12 ||
+ r->header.cupsBitsPerPixel == 16))
+ cups_swap(p, len);
+
+ /*
+ * Return...
+ */
+
+ return (len);
+ }
+
+ /*
+ * Read compressed data...
+ */
+
+ remaining = len;
+ cupsBytesPerLine = r->header.cupsBytesPerLine;
+
+ while (remaining > 0 && r->remaining > 0)
+ {
+ if (r->count == 0)
+ {
+ /*
+ * Need to read a new row...
+ */
+
+ if (remaining == cupsBytesPerLine)
+ ptr = p;
+ else
+ ptr = r->pixels;
+
+ /*
+ * Read using a modified PackBits compression...
+ */
+
+ if (!cups_raster_read(r, &byte, 1))
+ return (0);
+
+ r->count = byte + 1;
+
+ if (r->count > 1)
+ ptr = r->pixels;
+
+ temp = ptr;
+ bytes = cupsBytesPerLine;
+
+ while (bytes > 0)
+ {
+ /*
+ * Get a new repeat count...
+ */
+
+ if (!cups_raster_read(r, &byte, 1))
+ return (0);
+
+ if (byte & 128)
+ {
+ /*
+ * Copy N literal pixels...
+ */
+
+ count = (257 - byte) * r->bpp;
+
+ if (count > bytes)
+ count = bytes;
+
+ if (!cups_raster_read(r, temp, count))
+ return (0);
+
+ temp += count;
+ bytes -= count;
+ }
+ else
+ {
+ /*
+ * Repeat the next N bytes...
+ */
+
+ count = (byte + 1) * r->bpp;
+ if (count > bytes)
+ count = bytes;
+
+ if (count < r->bpp)
+ break;
+
+ bytes -= count;
+
+ if (!cups_raster_read(r, temp, r->bpp))
+ return (0);
+
+ temp += r->bpp;
+ count -= r->bpp;
+
+ while (count > 0)
+ {
+ memcpy(temp, temp - r->bpp, r->bpp);
+ temp += r->bpp;
+ count -= r->bpp;
+ }
+ }
+ }
+
+ /*
+ * Swap bytes as needed...
+ */
+
+ if ((r->header.cupsBitsPerColor == 16 ||
+ r->header.cupsBitsPerPixel == 12 ||
+ r->header.cupsBitsPerPixel == 16) &&
+ r->swapped)
+ cups_swap(ptr, bytes);
+
+ /*
+ * Update pointers...
+ */
+
+ if (remaining >= cupsBytesPerLine)
+ {
+ bytes = cupsBytesPerLine;
+ r->pcurrent = r->pixels;
+ r->count --;
+ r->remaining --;
+ }
+ else
+ {
+ bytes = remaining;
+ r->pcurrent = r->pixels + bytes;
+ }
+
+ /*
+ * Copy data as needed...
+ */
+
+ if (ptr != p)
+ memcpy(p, ptr, bytes);
+ }
+ else
+ {
+ /*
+ * Copy fragment from buffer...
+ */
+
+ if ((unsigned)(bytes = (int)(r->pend - r->pcurrent)) > remaining)
+ bytes = remaining;
+
+ memcpy(p, r->pcurrent, bytes);
+ r->pcurrent += bytes;
+
+ if (r->pcurrent >= r->pend)
+ {
+ r->pcurrent = r->pixels;
+ r->count --;
+ r->remaining --;
+ }
+ }
+
+ remaining -= bytes;
+ p += bytes;
+ }
+
+ return (len);
+}
+
+
+/*
+ * 'cupsRasterWriteHeader()' - Write a raster page header from a version 1 page
+ * header structure.
+ *
+ * This function is deprecated. Use @link cupsRasterWriteHeader2@ instead.
+ *
+ * @deprecated@
+ */
+
+unsigned /* O - 1 on success, 0 on failure */
+cupsRasterWriteHeader(
+ cups_raster_t *r, /* I - Raster stream */
+ cups_page_header_t *h) /* I - Raster page header */
+{
+ if (r == NULL || r->mode == CUPS_RASTER_READ)
+ return (0);
+
+ /*
+ * Make a copy of the header, and compute the number of raster
+ * lines in the page image...
+ */
+
+ memset(&(r->header), 0, sizeof(r->header));
+ memcpy(&(r->header), h, sizeof(cups_page_header_t));
+
+ cups_raster_update(r);
+
+ /*
+ * Write the raster header...
+ */
+
+ if (r->mode == CUPS_RASTER_WRITE_PWG)
+ {
+ /*
+ * PWG raster data is always network byte order with much of the page header
+ * zeroed.
+ */
+
+ cups_page_header2_t fh; /* File page header */
+
+ memset(&fh, 0, sizeof(fh));
+
+ strlcpy(fh.MediaClass, "PwgRaster", sizeof(fh.MediaClass));
+ /* PwgRaster */
+ strlcpy(fh.MediaColor, r->header.MediaColor, sizeof(fh.MediaColor));
+ strlcpy(fh.MediaType, r->header.MediaType, sizeof(fh.MediaType));
+ strlcpy(fh.OutputType, r->header.OutputType, sizeof(fh.OutputType));
+ /* PrintContentType */
+
+ fh.CutMedia = htonl(r->header.CutMedia);
+ fh.Duplex = htonl(r->header.Duplex);
+ fh.HWResolution[0] = htonl(r->header.HWResolution[0]);
+ fh.HWResolution[1] = htonl(r->header.HWResolution[1]);
+ fh.ImagingBoundingBox[0] = htonl(r->header.ImagingBoundingBox[0]);
+ fh.ImagingBoundingBox[1] = htonl(r->header.ImagingBoundingBox[1]);
+ fh.ImagingBoundingBox[2] = htonl(r->header.ImagingBoundingBox[2]);
+ fh.ImagingBoundingBox[3] = htonl(r->header.ImagingBoundingBox[3]);
+ fh.InsertSheet = htonl(r->header.InsertSheet);
+ fh.Jog = htonl(r->header.Jog);
+ fh.LeadingEdge = htonl(r->header.LeadingEdge);
+ fh.ManualFeed = htonl(r->header.ManualFeed);
+ fh.MediaPosition = htonl(r->header.MediaPosition);
+ fh.MediaWeight = htonl(r->header.MediaWeight);
+ fh.NumCopies = htonl(r->header.NumCopies);
+ fh.Orientation = htonl(r->header.Orientation);
+ fh.PageSize[0] = htonl(r->header.PageSize[0]);
+ fh.PageSize[1] = htonl(r->header.PageSize[1]);
+ fh.Tumble = htonl(r->header.Tumble);
+ fh.cupsWidth = htonl(r->header.cupsWidth);
+ fh.cupsHeight = htonl(r->header.cupsHeight);
+ fh.cupsBitsPerColor = htonl(r->header.cupsBitsPerColor);
+ fh.cupsBitsPerPixel = htonl(r->header.cupsBitsPerPixel);
+ fh.cupsBytesPerLine = htonl(r->header.cupsBytesPerLine);
+ fh.cupsColorOrder = htonl(r->header.cupsColorOrder);
+ fh.cupsColorSpace = htonl(r->header.cupsColorSpace);
+ fh.cupsNumColors = htonl(r->header.cupsNumColors);
+ fh.cupsInteger[0] = htonl(r->header.cupsInteger[0]);
+ /* TotalPageCount */
+ fh.cupsInteger[1] = htonl(r->header.cupsInteger[1]);
+ /* CrossFeedTransform */
+ fh.cupsInteger[2] = htonl(r->header.cupsInteger[2]);
+ /* FeedTransform */
+ fh.cupsInteger[3] = htonl(r->header.cupsInteger[3]);
+ /* ImageBoxLeft */
+ fh.cupsInteger[4] = htonl(r->header.cupsInteger[4]);
+ /* ImageBoxTop */
+ fh.cupsInteger[5] = htonl(r->header.cupsInteger[5]);
+ /* ImageBoxRight */
+ fh.cupsInteger[6] = htonl(r->header.cupsInteger[6]);
+ /* ImageBoxBottom */
+ fh.cupsInteger[7] = htonl(r->header.cupsInteger[7]);
+ /* BlackPrimary */
+ fh.cupsInteger[8] = htonl(r->header.cupsInteger[8]);
+ /* PrintQuality */
+ fh.cupsInteger[14] = htonl(r->header.cupsInteger[14]);
+ /* VendorIdentifier */
+ fh.cupsInteger[15] = htonl(r->header.cupsInteger[15]);
+ /* VendorLength */
+
+ memcpy(fh.cupsReal, r->header.cupsReal,
+ sizeof(fh.cupsReal) + sizeof(fh.cupsString));
+ /* VendorData */
+
+ strlcpy(fh.cupsRenderingIntent, r->header.cupsRenderingIntent,
+ sizeof(fh.cupsRenderingIntent));
+ strlcpy(fh.cupsPageSizeName, r->header.cupsPageSizeName,
+ sizeof(fh.cupsPageSizeName));
+
+ return (cups_raster_io(r, (unsigned char *)&fh, sizeof(fh)) == sizeof(fh));
+ }
+ else
+ return (cups_raster_io(r, (unsigned char *)&(r->header), sizeof(r->header))
+ == sizeof(r->header));
+}
+
+
+/*
+ * 'cupsRasterWriteHeader2()' - Write a raster page header from a version 2
+ * page header structure.
+ *
+ * The page header can be initialized using @link cupsRasterInterpretPPD@.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+unsigned /* O - 1 on success, 0 on failure */
+cupsRasterWriteHeader2(
+ cups_raster_t *r, /* I - Raster stream */
+ cups_page_header2_t *h) /* I - Raster page header */
+{
+ if (r == NULL || r->mode == CUPS_RASTER_READ)
+ return (0);
+
+ /*
+ * Make a copy of the header, and compute the number of raster
+ * lines in the page image...
+ */
+
+ memcpy(&(r->header), h, sizeof(cups_page_header2_t));
+
+ cups_raster_update(r);
+
+ /*
+ * Write the raster header...
+ */
+
+ if (r->mode == CUPS_RASTER_WRITE_PWG)
+ {
+ /*
+ * PWG raster data is always network byte order with most of the page header
+ * zeroed.
+ */
+
+ cups_page_header2_t fh; /* File page header */
+
+ memset(&fh, 0, sizeof(fh));
+ strlcpy(fh.MediaClass, "PwgRaster", sizeof(fh.MediaClass));
+ strlcpy(fh.MediaColor, r->header.MediaColor, sizeof(fh.MediaColor));
+ strlcpy(fh.MediaType, r->header.MediaType, sizeof(fh.MediaType));
+ strlcpy(fh.OutputType, r->header.OutputType, sizeof(fh.OutputType));
+ strlcpy(fh.cupsRenderingIntent, r->header.cupsRenderingIntent,
+ sizeof(fh.cupsRenderingIntent));
+ strlcpy(fh.cupsPageSizeName, r->header.cupsPageSizeName,
+ sizeof(fh.cupsPageSizeName));
+
+ fh.CutMedia = htonl(r->header.CutMedia);
+ fh.Duplex = htonl(r->header.Duplex);
+ fh.HWResolution[0] = htonl(r->header.HWResolution[0]);
+ fh.HWResolution[1] = htonl(r->header.HWResolution[1]);
+ fh.ImagingBoundingBox[0] = htonl(r->header.ImagingBoundingBox[0]);
+ fh.ImagingBoundingBox[1] = htonl(r->header.ImagingBoundingBox[1]);
+ fh.ImagingBoundingBox[2] = htonl(r->header.ImagingBoundingBox[2]);
+ fh.ImagingBoundingBox[3] = htonl(r->header.ImagingBoundingBox[3]);
+ fh.InsertSheet = htonl(r->header.InsertSheet);
+ fh.Jog = htonl(r->header.Jog);
+ fh.LeadingEdge = htonl(r->header.LeadingEdge);
+ fh.ManualFeed = htonl(r->header.ManualFeed);
+ fh.MediaPosition = htonl(r->header.MediaPosition);
+ fh.MediaWeight = htonl(r->header.MediaWeight);
+ fh.NumCopies = htonl(r->header.NumCopies);
+ fh.Orientation = htonl(r->header.Orientation);
+ fh.PageSize[0] = htonl(r->header.PageSize[0]);
+ fh.PageSize[1] = htonl(r->header.PageSize[1]);
+ fh.Tumble = htonl(r->header.Tumble);
+ fh.cupsWidth = htonl(r->header.cupsWidth);
+ fh.cupsHeight = htonl(r->header.cupsHeight);
+ fh.cupsBitsPerColor = htonl(r->header.cupsBitsPerColor);
+ fh.cupsBitsPerPixel = htonl(r->header.cupsBitsPerPixel);
+ fh.cupsBytesPerLine = htonl(r->header.cupsBytesPerLine);
+ fh.cupsColorOrder = htonl(r->header.cupsColorOrder);
+ fh.cupsColorSpace = htonl(r->header.cupsColorSpace);
+ fh.cupsNumColors = htonl(r->header.cupsNumColors);
+ fh.cupsInteger[0] = htonl(r->header.cupsInteger[0]);
+ fh.cupsInteger[1] = htonl(r->header.cupsInteger[1]);
+ fh.cupsInteger[2] = htonl(r->header.cupsInteger[2]);
+ fh.cupsInteger[3] = htonl((unsigned)(r->header.cupsImagingBBox[0] *
+ r->header.HWResolution[0]));
+ fh.cupsInteger[4] = htonl((unsigned)(r->header.cupsImagingBBox[1] *
+ r->header.HWResolution[1]));
+ fh.cupsInteger[5] = htonl((unsigned)(r->header.cupsImagingBBox[2] *
+ r->header.HWResolution[0]));
+ fh.cupsInteger[6] = htonl((unsigned)(r->header.cupsImagingBBox[3] *
+ r->header.HWResolution[1]));
+ fh.cupsInteger[7] = htonl(0xffffff);
+
+ return (cups_raster_io(r, (unsigned char *)&fh, sizeof(fh)) == sizeof(fh));
+ }
+ else
+ return (cups_raster_io(r, (unsigned char *)&(r->header), sizeof(r->header))
+ == sizeof(r->header));
+}
+
+
+/*
+ * 'cupsRasterWritePixels()' - Write raster pixels.
+ *
+ * For best performance, filters should write one or more whole lines.
+ * The "cupsBytesPerLine" value from the page header can be used to allocate
+ * the line buffer and as the number of bytes to write.
+ */
+
+unsigned /* O - Number of bytes written */
+cupsRasterWritePixels(cups_raster_t *r, /* I - Raster stream */
+ unsigned char *p, /* I - Bytes to write */
+ unsigned len)/* I - Number of bytes to write */
+{
+ int bytes; /* Bytes read */
+ unsigned remaining; /* Bytes remaining */
+
+
+ DEBUG_printf(("cupsRasterWritePixels(r=%p, p=%p, len=%u), remaining=%u\n",
+ r, p, len, r->remaining));
+
+ if (r == NULL || r->mode == CUPS_RASTER_READ || r->remaining == 0)
+ return (0);
+
+ if (!r->compressed)
+ {
+ /*
+ * Without compression, just write the raster data raw unless the data needs
+ * to be swapped...
+ */
+
+ r->remaining -= len / r->header.cupsBytesPerLine;
+
+ if (r->swapped &&
+ (r->header.cupsBitsPerColor == 16 ||
+ r->header.cupsBitsPerPixel == 12 ||
+ r->header.cupsBitsPerPixel == 16))
+ {
+ unsigned char *bufptr; /* Pointer into write buffer */
+ unsigned count; /* Remaining count */
+
+ /*
+ * Allocate a write buffer as needed...
+ */
+
+ if ((size_t)len > r->bufsize)
+ {
+ if (r->buffer)
+ bufptr = realloc(r->buffer, len);
+ else
+ bufptr = malloc(len);
+
+ if (!bufptr)
+ return (0);
+
+ r->buffer = bufptr;
+ r->bufsize = len;
+ }
+
+ /*
+ * Byte swap the pixels...
+ */
+
+ for (bufptr = r->buffer, count = len; count > 1; count -= 2, bufptr += 2)
+ {
+ bufptr[1] = *p++;
+ bufptr[0] = *p++;
+ }
+
+ if (count) /* This should never happen... */
+ *bufptr = *p;
+
+ /*
+ * Write the byte-swapped buffer...
+ */
+
+ return (cups_raster_io(r, r->buffer, len));
+ }
+ else
+ return (cups_raster_io(r, p, len));
+ }
+
+ /*
+ * Otherwise, compress each line...
+ */
+
+ for (remaining = len; remaining > 0; remaining -= bytes, p += bytes)
+ {
+ /*
+ * Figure out the number of remaining bytes on the current line...
+ */
+
+ if ((bytes = remaining) > (int)(r->pend - r->pcurrent))
+ bytes = (int)(r->pend - r->pcurrent);
+
+ if (r->count > 0)
+ {
+ /*
+ * Check to see if this line is the same as the previous line...
+ */
+
+ if (memcmp(p, r->pcurrent, bytes))
+ {
+ if (!cups_raster_write(r, r->pixels))
+ return (0);
+
+ r->count = 0;
+ }
+ else
+ {
+ /*
+ * Mark more bytes as the same...
+ */
+
+ r->pcurrent += bytes;
+
+ if (r->pcurrent >= r->pend)
+ {
+ /*
+ * Increase the repeat count...
+ */
+
+ r->count ++;
+ r->pcurrent = r->pixels;
+
+ /*
+ * Flush out this line if it is the last one...
+ */
+
+ r->remaining --;
+
+ if (r->remaining == 0)
+ return (cups_raster_write(r, r->pixels));
+ else if (r->count == 256)
+ {
+ if (cups_raster_write(r, r->pixels) == 0)
+ return (0);
+
+ r->count = 0;
+ }
+ }
+
+ continue;
+ }
+ }
+
+ if (r->count == 0)
+ {
+ /*
+ * Copy the raster data to the buffer...
+ */
+
+ memcpy(r->pcurrent, p, bytes);
+
+ r->pcurrent += bytes;
+
+ if (r->pcurrent >= r->pend)
+ {
+ /*
+ * Increase the repeat count...
+ */
+
+ r->count ++;
+ r->pcurrent = r->pixels;
+
+ /*
+ * Flush out this line if it is the last one...
+ */
+
+ r->remaining --;
+
+ if (r->remaining == 0)
+ return (cups_raster_write(r, r->pixels));
+ }
+ }
+ }
+
+ return (len);
+}
+
+
+/*
+ * 'cups_raster_read_header()' - Read a raster page header.
+ */
+
+static unsigned /* O - 1 on success, 0 on fail */
+cups_raster_read_header(
+ cups_raster_t *r) /* I - Raster stream */
+{
+ int len; /* Length for read/swap */
+
+
+ if (r == NULL || r->mode != CUPS_RASTER_READ)
+ return (0);
+
+ /*
+ * Get the length of the raster header...
+ */
+
+ if (r->sync == CUPS_RASTER_SYNCv1 || r->sync == CUPS_RASTER_REVSYNCv1)
+ len = sizeof(cups_page_header_t);
+ else
+ len = sizeof(cups_page_header2_t);
+
+ /*
+ * Read the header...
+ */
+
+ memset(&(r->header), 0, sizeof(r->header));
+
+ if (cups_raster_read(r, (unsigned char *)&(r->header), len) < len)
+ return (0);
+
+ /*
+ * Swap bytes as needed...
+ */
+
+ if (r->swapped)
+ {
+ unsigned *s, /* Current word */
+ temp; /* Temporary copy */
+
+
+ DEBUG_puts("Swapping header bytes...");
+
+ for (len = 81, s = &(r->header.AdvanceDistance);
+ len > 0;
+ len --, s ++)
+ {
+ DEBUG_printf(("%08x =>", *s));
+
+ temp = *s;
+ *s = ((temp & 0xff) << 24) |
+ ((temp & 0xff00) << 8) |
+ ((temp & 0xff0000) >> 8) |
+ ((temp & 0xff000000) >> 24);
+
+ DEBUG_printf((" %08x\n", *s));
+ }
+ }
+
+ /*
+ * Update the header and row count...
+ */
+
+ cups_raster_update(r);
+
+ return (r->header.cupsBytesPerLine != 0 && r->header.cupsHeight != 0);
+}
+
+
+/*
+ * 'cups_raster_io()' - Read/write bytes from a context, handling interruptions.
+ */
+
+static int /* O - Bytes read or -1 */
+cups_raster_io(cups_raster_t *r, /* I - Raster stream */
+ unsigned char *buf, /* I - Buffer for read/write */
+ int bytes) /* I - Number of bytes to read/write */
+{
+ ssize_t count; /* Number of bytes read/written */
+ size_t total; /* Total bytes read/written */
+
+
+ DEBUG_printf(("4cups_raster_io(r=%p, buf=%p, bytes=%d)", r, buf, bytes));
+
+ for (total = 0; total < (size_t)bytes; total += count, buf += count)
+ {
+ count = (*r->iocb)(r->ctx, buf, bytes - total);
+
+ DEBUG_printf(("5cups_raster_io: count=%d, total=%d", (int)count,
+ (int)total));
+ if (count == 0)
+ return (0);
+ else if (count < 0)
+ return (-1);
+ }
+
+ return ((int)total);
+}
+
+
+/*
+ * 'cups_raster_read()' - Read through the raster buffer.
+ */
+
+static int /* O - Number of bytes read */
+cups_raster_read(cups_raster_t *r, /* I - Raster stream */
+ unsigned char *buf, /* I - Buffer */
+ int bytes) /* I - Number of bytes to read */
+{
+ int count, /* Number of bytes read */
+ remaining, /* Remaining bytes in buffer */
+ total; /* Total bytes read */
+
+
+ DEBUG_printf(("cups_raster_read(r=%p, buf=%p, bytes=%d)\n", r, buf, bytes));
+
+ if (!r->compressed)
+ return (cups_raster_io(r, buf, bytes));
+
+ /*
+ * Allocate a read buffer as needed...
+ */
+
+ count = 2 * r->header.cupsBytesPerLine;
+
+ if ((size_t)count > r->bufsize)
+ {
+ int offset = (int)(r->bufptr - r->buffer);
+ /* Offset to current start of buffer */
+ int end = (int)(r->bufend - r->buffer);
+ /* Offset to current end of buffer */
+ unsigned char *rptr; /* Pointer in read buffer */
+
+ if (r->buffer)
+ rptr = realloc(r->buffer, count);
+ else
+ rptr = malloc(count);
+
+ if (!rptr)
+ return (0);
+
+ r->buffer = rptr;
+ r->bufptr = rptr + offset;
+ r->bufend = rptr + end;
+ r->bufsize = count;
+ }
+
+ /*
+ * Loop until we have read everything...
+ */
+
+ for (total = 0, remaining = (int)(r->bufend - r->bufptr);
+ total < bytes;
+ total += count, buf += count)
+ {
+ count = bytes - total;
+
+ DEBUG_printf(("count=%d, remaining=%d, buf=%p, bufptr=%p, bufend=%p...\n",
+ count, remaining, buf, r->bufptr, r->bufend));
+
+ if (remaining == 0)
+ {
+ if (count < 16)
+ {
+ /*
+ * Read into the raster buffer and then copy...
+ */
+
+ remaining = (*r->iocb)(r->ctx, r->buffer, r->bufsize);
+ if (remaining <= 0)
+ return (0);
+
+ r->bufptr = r->buffer;
+ r->bufend = r->buffer + remaining;
+ }
+ else
+ {
+ /*
+ * Read directly into "buf"...
+ */
+
+ count = (*r->iocb)(r->ctx, buf, count);
+
+ if (count <= 0)
+ return (0);
+
+ continue;
+ }
+ }
+
+ /*
+ * Copy bytes from raster buffer to "buf"...
+ */
+
+ if (count > remaining)
+ count = remaining;
+
+ if (count == 1)
+ {
+ /*
+ * Copy 1 byte...
+ */
+
+ *buf = *(r->bufptr)++;
+ remaining --;
+ }
+ else if (count < 128)
+ {
+ /*
+ * Copy up to 127 bytes without using memcpy(); this is
+ * faster because it avoids an extra function call and is
+ * often further optimized by the compiler...
+ */
+
+ unsigned char *bufptr; /* Temporary buffer pointer */
+
+ remaining -= count;
+
+ for (bufptr = r->bufptr; count > 0; count --, total ++)
+ *buf++ = *bufptr++;
+
+ r->bufptr = bufptr;
+ }
+ else
+ {
+ /*
+ * Use memcpy() for a large read...
+ */
+
+ memcpy(buf, r->bufptr, count);
+ r->bufptr += count;
+ remaining -= count;
+ }
+ }
+
+ return (total);
+}
+
+
+/*
+ * 'cups_raster_update()' - Update the raster header and row count for the
+ * current page.
+ */
+
+static void
+cups_raster_update(cups_raster_t *r) /* I - Raster stream */
+{
+ if (r->sync == CUPS_RASTER_SYNCv1 || r->sync == CUPS_RASTER_REVSYNCv1 ||
+ r->header.cupsNumColors == 0)
+ {
+ /*
+ * Set the "cupsNumColors" field according to the colorspace...
+ */
+
+ switch (r->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ case CUPS_CSPACE_SW :
+ r->header.cupsNumColors = 1;
+ break;
+
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ case CUPS_CSPACE_CIEXYZ :
+ case CUPS_CSPACE_CIELab :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ case CUPS_CSPACE_ICC1 :
+ case CUPS_CSPACE_ICC2 :
+ case CUPS_CSPACE_ICC3 :
+ case CUPS_CSPACE_ICC4 :
+ case CUPS_CSPACE_ICC5 :
+ case CUPS_CSPACE_ICC6 :
+ case CUPS_CSPACE_ICC7 :
+ case CUPS_CSPACE_ICC8 :
+ case CUPS_CSPACE_ICC9 :
+ case CUPS_CSPACE_ICCA :
+ case CUPS_CSPACE_ICCB :
+ case CUPS_CSPACE_ICCC :
+ case CUPS_CSPACE_ICCD :
+ case CUPS_CSPACE_ICCE :
+ case CUPS_CSPACE_ICCF :
+ r->header.cupsNumColors = 3;
+ break;
+
+ case CUPS_CSPACE_RGBA :
+ case CUPS_CSPACE_RGBW :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ r->header.cupsNumColors = 4;
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (r->header.cupsBitsPerPixel < 8)
+ r->header.cupsNumColors = 6;
+ else
+ r->header.cupsNumColors = 4;
+ break;
+
+ case CUPS_CSPACE_DEVICE1 :
+ case CUPS_CSPACE_DEVICE2 :
+ case CUPS_CSPACE_DEVICE3 :
+ case CUPS_CSPACE_DEVICE4 :
+ case CUPS_CSPACE_DEVICE5 :
+ case CUPS_CSPACE_DEVICE6 :
+ case CUPS_CSPACE_DEVICE7 :
+ case CUPS_CSPACE_DEVICE8 :
+ case CUPS_CSPACE_DEVICE9 :
+ case CUPS_CSPACE_DEVICEA :
+ case CUPS_CSPACE_DEVICEB :
+ case CUPS_CSPACE_DEVICEC :
+ case CUPS_CSPACE_DEVICED :
+ case CUPS_CSPACE_DEVICEE :
+ case CUPS_CSPACE_DEVICEF :
+ r->header.cupsNumColors = r->header.cupsColorSpace -
+ CUPS_CSPACE_DEVICE1 + 1;
+ break;
+ }
+ }
+
+ /*
+ * Set the number of bytes per pixel/color...
+ */
+
+ if (r->header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+ r->bpp = (r->header.cupsBitsPerPixel + 7) / 8;
+ else
+ r->bpp = (r->header.cupsBitsPerColor + 7) / 8;
+
+ /*
+ * Set the number of remaining rows...
+ */
+
+ if (r->header.cupsColorOrder == CUPS_ORDER_PLANAR)
+ r->remaining = r->header.cupsHeight * r->header.cupsNumColors;
+ else
+ r->remaining = r->header.cupsHeight;
+
+ /*
+ * Allocate the compression buffer...
+ */
+
+ if (r->compressed)
+ {
+ if (r->pixels != NULL)
+ free(r->pixels);
+
+ r->pixels = calloc(r->header.cupsBytesPerLine, 1);
+ r->pcurrent = r->pixels;
+ r->pend = r->pixels + r->header.cupsBytesPerLine;
+ r->count = 0;
+ }
+}
+
+
+/*
+ * 'cups_raster_write()' - Write a row of compressed raster data...
+ */
+
+static int /* O - Number of bytes written */
+cups_raster_write(
+ cups_raster_t *r, /* I - Raster stream */
+ const unsigned char *pixels) /* I - Pixel data to write */
+{
+ const unsigned char *start, /* Start of sequence */
+ *ptr, /* Current pointer in sequence */
+ *pend, /* End of raster buffer */
+ *plast; /* Pointer to last pixel */
+ unsigned char *wptr; /* Pointer into write buffer */
+ int bpp, /* Bytes per pixel */
+ count; /* Count */
+
+
+ DEBUG_printf(("cups_raster_write(r=%p, pixels=%p)\n", r, pixels));
+
+ /*
+ * Allocate a write buffer as needed...
+ */
+
+ count = r->header.cupsBytesPerLine * 2;
+ if ((size_t)count > r->bufsize)
+ {
+ if (r->buffer)
+ wptr = realloc(r->buffer, count);
+ else
+ wptr = malloc(count);
+
+ if (!wptr)
+ return (-1);
+
+ r->buffer = wptr;
+ r->bufsize = count;
+ }
+
+ /*
+ * Write the row repeat count...
+ */
+
+ bpp = r->bpp;
+ pend = pixels + r->header.cupsBytesPerLine;
+ plast = pend - bpp;
+ wptr = r->buffer;
+ *wptr++ = r->count - 1;
+
+ /*
+ * Write using a modified PackBits compression...
+ */
+
+ for (ptr = pixels; ptr < pend;)
+ {
+ start = ptr;
+ ptr += bpp;
+
+ if (ptr == pend)
+ {
+ /*
+ * Encode a single pixel at the end...
+ */
+
+ *wptr++ = 0;
+ for (count = bpp; count > 0; count --)
+ *wptr++ = *start++;
+ }
+ else if (!memcmp(start, ptr, bpp))
+ {
+ /*
+ * Encode a sequence of repeating pixels...
+ */
+
+ for (count = 2; count < 128 && ptr < plast; count ++, ptr += bpp)
+ if (memcmp(ptr, ptr + bpp, bpp))
+ break;
+
+ *wptr++ = count - 1;
+ for (count = bpp; count > 0; count --)
+ *wptr++ = *ptr++;
+ }
+ else
+ {
+ /*
+ * Encode a sequence of non-repeating pixels...
+ */
+
+ for (count = 1; count < 128 && ptr < plast; count ++, ptr += bpp)
+ if (!memcmp(ptr, ptr + bpp, bpp))
+ break;
+
+ if (ptr >= plast && count < 128)
+ {
+ count ++;
+ ptr += bpp;
+ }
+
+ *wptr++ = 257 - count;
+
+ count *= bpp;
+ memcpy(wptr, start, count);
+ wptr += count;
+ }
+ }
+
+ return (cups_raster_io(r, r->buffer, (int)(wptr - r->buffer)));
+}
+
+
+/*
+ * 'cups_read_fd()' - Read bytes from a file.
+ */
+
+static ssize_t /* O - Bytes read or -1 */
+cups_read_fd(void *ctx, /* I - File descriptor as pointer */
+ unsigned char *buf, /* I - Buffer for read */
+ size_t bytes) /* I - Maximum number of bytes to read */
+{
+ int fd = (int)((intptr_t)ctx);
+ /* File descriptor */
+ ssize_t count; /* Number of bytes read */
+
+
+#ifdef WIN32 /* Sigh */
+ while ((count = read(fd, buf, (unsigned)bytes)) < 0)
+#else
+ while ((count = read(fd, buf, bytes)) < 0)
+#endif /* WIN32 */
+ if (errno != EINTR && errno != EAGAIN)
+ return (-1);
+
+ return (count);
+}
+
+
+/*
+ * 'cups_swap()' - Swap bytes in raster data...
+ */
+
+static void
+cups_swap(unsigned char *buf, /* I - Buffer to swap */
+ int bytes) /* I - Number of bytes to swap */
+{
+ unsigned char even, odd; /* Temporary variables */
+
+
+ bytes /= 2;
+
+ while (bytes > 0)
+ {
+ even = buf[0];
+ odd = buf[1];
+ buf[0] = odd;
+ buf[1] = even;
+
+ buf += 2;
+ bytes --;
+ }
+}
+
+
+/*
+ * 'cups_write_fd()' - Write bytes to a file.
+ */
+
+static ssize_t /* O - Bytes written or -1 */
+cups_write_fd(void *ctx, /* I - File descriptor pointer */
+ unsigned char *buf, /* I - Bytes to write */
+ size_t bytes) /* I - Number of bytes to write */
+{
+ int fd = (int)((intptr_t)ctx);
+ /* File descriptor */
+ ssize_t count; /* Number of bytes written */
+
+
+#ifdef WIN32 /* Sigh */
+ while ((count = write(fd, buf, (unsigned)bytes)) < 0)
+#else
+ while ((count = write(fd, buf, bytes)) < 0)
+#endif /* WIN32 */
+ if (errno != EINTR && errno != EAGAIN)
+ return (-1);
+
+ return (count);
+}
+
+
+/*
+ * End of "$Id: raster.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/rasterbench.c b/cups/libs/filter/rasterbench.c
new file mode 100644
index 000000000..eacbd0eec
--- /dev/null
+++ b/cups/libs/filter/rasterbench.c
@@ -0,0 +1,355 @@
+/*
+ * "$Id: rasterbench.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Raster benchmark program for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Benchmark the raster read/write functions.
+ * compute_median() - Compute the median time for a test.
+ * read_test() - Benchmark the raster read functions.
+ * write_test() - Benchmark the raster write functions.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <config.h>
+#include <cups/raster.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+
+/*
+ * Constants...
+ */
+
+#define TEST_WIDTH 1024
+#define TEST_HEIGHT 1024
+#define TEST_PAGES 16
+#define TEST_PASSES 20
+
+
+/*
+ * Local functions...
+ */
+
+static double compute_median(double *secs);
+static double get_time(void);
+static void read_test(int fd);
+static int run_read_test(void);
+static void write_test(int fd, cups_mode_t mode);
+
+
+/*
+ * 'main()' - Benchmark the raster read/write functions.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int ras_fd, /* File descriptor for read process */
+ status; /* Exit status of read process */
+ double start_secs, /* Start time */
+ write_secs, /* Write time */
+ read_secs, /* Read time */
+ pass_secs[TEST_PASSES]; /* Total test times */
+ cups_mode_t mode; /* Write mode */
+
+
+ /*
+ * See if we have anything on the command-line...
+ */
+
+ if (argc > 2 || (argc == 2 && strcmp(argv[1], "-z")))
+ {
+ puts("Usage: rasterbench [-z]");
+ return (1);
+ }
+
+ mode = argc > 1 ? CUPS_RASTER_WRITE_COMPRESSED : CUPS_RASTER_WRITE;
+
+ /*
+ * Ignore SIGPIPE...
+ */
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Run the tests several times to get a good average...
+ */
+
+ printf("Test read/write speed of %d pages, %dx%d pixels...\n\n",
+ TEST_PAGES, TEST_WIDTH, TEST_HEIGHT);
+ for (i = 0; i < TEST_PASSES; i ++)
+ {
+ printf("PASS %2d: ", i + 1);
+ fflush(stdout);
+
+ ras_fd = run_read_test();
+ start_secs = get_time();
+
+ write_test(ras_fd, mode);
+
+ write_secs = get_time();
+ printf(" %.3f write,", write_secs - start_secs);
+ fflush(stdout);
+
+ close(ras_fd);
+ wait(&status);
+
+ read_secs = get_time();
+ pass_secs[i] = read_secs - start_secs;
+ printf(" %.3f read, %.3f total\n", read_secs - write_secs, pass_secs[i]);
+ }
+
+ printf("\nMedian Total Time: %.3f seconds per document\n",
+ compute_median(pass_secs));
+
+ return (0);
+}
+
+
+/*
+ * 'compute_median()' - Compute the median time for a test.
+ */
+
+static double /* O - Median time in seconds */
+compute_median(double *secs) /* I - Array of time samples */
+{
+ int i, j; /* Looping vars */
+ double temp; /* Swap variable */
+
+
+ /*
+ * Sort the array into ascending order using a quicky bubble sort...
+ */
+
+ for (i = 0; i < (TEST_PASSES - 1); i ++)
+ for (j = i + 1; j < TEST_PASSES; j ++)
+ if (secs[i] > secs[j])
+ {
+ temp = secs[i];
+ secs[i] = secs[j];
+ secs[j] = temp;
+ }
+
+ /*
+ * Return the average of the middle two samples...
+ */
+
+ return (0.5 * (secs[TEST_PASSES / 2 - 1] + secs[TEST_PASSES / 2]));
+}
+
+
+/*
+ * 'get_time()' - Get the current time in seconds.
+ */
+
+static double /* O - Time in seconds */
+get_time(void)
+{
+ struct timeval curtime; /* Current time */
+
+
+ gettimeofday(&curtime, NULL);
+ return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
+}
+
+
+/*
+ * 'read_test()' - Benchmark the raster read functions.
+ */
+
+static void
+read_test(int fd) /* I - File descriptor to read from */
+{
+ int y; /* Looping var */
+ cups_raster_t *r; /* Raster stream */
+ cups_page_header2_t header; /* Page header */
+ unsigned char buffer[8 * TEST_WIDTH];
+ /* Read buffer */
+
+
+ /*
+ * Test read speed...
+ */
+
+ if ((r = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL)
+ {
+ perror("Unable to create raster input stream");
+ return;
+ }
+
+ while (cupsRasterReadHeader2(r, &header))
+ {
+ for (y = 0; y < header.cupsHeight; y ++)
+ cupsRasterReadPixels(r, buffer, header.cupsBytesPerLine);
+ }
+
+ cupsRasterClose(r);
+}
+
+
+/*
+ * 'run_read_test()' - Run the read test as a child process via pipes.
+ */
+
+static int /* O - Standard input of child */
+run_read_test(void)
+{
+ int ras_pipes[2]; /* Raster data pipes */
+ int pid; /* Child process ID */
+
+
+ if (pipe(ras_pipes))
+ return (-1);
+
+ if ((pid = fork()) < 0)
+ {
+ /*
+ * Fork error - return -1 on error...
+ */
+
+ close(ras_pipes[0]);
+ close(ras_pipes[1]);
+
+ return (-1);
+ }
+ else if (pid == 0)
+ {
+ /*
+ * Child comes here - read data from the input pipe...
+ */
+
+ close(ras_pipes[1]);
+ read_test(ras_pipes[0]);
+ exit(0);
+ }
+ else
+ {
+ /*
+ * Parent comes here - return the output pipe...
+ */
+
+ close(ras_pipes[0]);
+ return (ras_pipes[1]);
+ }
+}
+
+
+/*
+ * 'write_test()' - Benchmark the raster write functions.
+ */
+
+static void
+write_test(int fd, /* I - File descriptor to write to */
+ cups_mode_t mode) /* I - Write mode */
+{
+ int page, x, y; /* Looping vars */
+ int count; /* Number of bytes to set */
+ cups_raster_t *r; /* Raster stream */
+ cups_page_header2_t header; /* Page header */
+ unsigned char data[32][8 * TEST_WIDTH];
+ /* Raster data to write */
+
+
+ /*
+ * Create a combination of random data and repeated data to simulate
+ * text with some whitespace.
+ */
+
+ CUPS_SRAND(time(NULL));
+
+ memset(data, 0, sizeof(data));
+
+ for (y = 0; y < 28; y ++)
+ {
+ for (x = CUPS_RAND() & 127, count = (CUPS_RAND() & 15) + 1;
+ x < sizeof(data[0]);
+ x ++, count --)
+ {
+ if (count <= 0)
+ {
+ x += (CUPS_RAND() & 15) + 1;
+ count = (CUPS_RAND() & 15) + 1;
+
+ if (x >= sizeof(data[0]))
+ break;
+ }
+
+ data[y][x] = CUPS_RAND();
+ }
+ }
+
+ /*
+ * Test write speed...
+ */
+
+ if ((r = cupsRasterOpen(fd, mode)) == NULL)
+ {
+ perror("Unable to create raster output stream");
+ return;
+ }
+
+ for (page = 0; page < TEST_PAGES; page ++)
+ {
+ memset(&header, 0, sizeof(header));
+ header.cupsWidth = TEST_WIDTH;
+ header.cupsHeight = TEST_HEIGHT;
+ header.cupsBytesPerLine = TEST_WIDTH;
+
+ if (page & 1)
+ {
+ header.cupsBytesPerLine *= 4;
+ header.cupsColorSpace = CUPS_CSPACE_CMYK;
+ header.cupsColorOrder = CUPS_ORDER_CHUNKED;
+ }
+ else
+ {
+ header.cupsColorSpace = CUPS_CSPACE_K;
+ header.cupsColorOrder = CUPS_ORDER_BANDED;
+ }
+
+ if (page & 2)
+ {
+ header.cupsBytesPerLine *= 2;
+ header.cupsBitsPerColor = 16;
+ header.cupsBitsPerPixel = (page & 1) ? 64 : 16;
+ }
+ else
+ {
+ header.cupsBitsPerColor = 8;
+ header.cupsBitsPerPixel = (page & 1) ? 32 : 8;
+ }
+
+ cupsRasterWriteHeader2(r, &header);
+
+ for (y = 0; y < TEST_HEIGHT; y ++)
+ cupsRasterWritePixels(r, data[y & 31], header.cupsBytesPerLine);
+ }
+
+ cupsRasterClose(r);
+}
+
+
+/*
+ * End of "$Id: rasterbench.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/rastertoepson.c b/cups/libs/filter/rastertoepson.c
new file mode 100644
index 000000000..e53acabfc
--- /dev/null
+++ b/cups/libs/filter/rastertoepson.c
@@ -0,0 +1,1157 @@
+/*
+ * "$Id: rastertoepson.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * EPSON ESC/P and ESC/P2 filter for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1993-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * Setup() - Prepare the printer for printing.
+ * StartPage() - Start a page of graphics.
+ * EndPage() - Finish a page of graphics.
+ * Shutdown() - Shutdown the printer.
+ * CompressData() - Compress a line of graphics.
+ * OutputLine() - Output a line of graphics.
+ * main() - Main entry and processing of driver.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include <cups/string-private.h>
+#include <cups/language-private.h>
+#include <cups/raster.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+
+/*
+ * Model numbers...
+ */
+
+#define EPSON_9PIN 0
+#define EPSON_24PIN 1
+#define EPSON_COLOR 2
+#define EPSON_PHOTO 3
+#define EPSON_ICOLOR 4
+#define EPSON_IPHOTO 5
+
+
+/*
+ * Macros...
+ */
+
+#define pwrite(s,n) fwrite((s), 1, (n), stdout)
+
+
+/*
+ * Globals...
+ */
+
+unsigned char *Planes[6], /* Output buffers */
+ *CompBuffer, /* Compression buffer */
+ *LineBuffers[2]; /* Line bitmap buffers */
+int Model, /* Model number */
+ NumPlanes, /* Number of color planes */
+ Feed, /* Number of lines to skip */
+ EjectPage; /* Eject the page when done? */
+int DotBit, /* Bit in buffers */
+ DotBytes, /* # bytes in a dot column */
+ DotColumns, /* # columns in 1/60 inch */
+ LineCount, /* # of lines processed */
+ EvenOffset, /* Offset into 'even' buffers */
+ OddOffset, /* Offset into 'odd' buffers */
+ Shingling, /* Shingle output? */
+ Canceled; /* Has the current job been canceled? */
+
+
+/*
+ * Prototypes...
+ */
+
+void Setup(void);
+void StartPage(const ppd_file_t *ppd, const cups_page_header2_t *header);
+void EndPage(const cups_page_header2_t *header);
+void Shutdown(void);
+
+void CancelJob(int sig);
+void CompressData(const unsigned char *line, int length, int plane,
+ int type, int xstep, int ystep);
+void OutputLine(const cups_page_header2_t *header);
+void OutputRows(const cups_page_header2_t *header, int row);
+
+
+/*
+ * 'Setup()' - Prepare the printer for printing.
+ */
+
+void
+Setup(void)
+{
+ const char *device_uri; /* The device for the printer... */
+
+
+ /*
+ * EPSON USB printers need an additional command issued at the
+ * beginning of each job to exit from "packet" mode...
+ */
+
+ if ((device_uri = getenv("DEVICE_URI")) != NULL &&
+ strncmp(device_uri, "usb:", 4) == 0 && Model >= EPSON_ICOLOR)
+ pwrite("\000\000\000\033\001@EJL 1284.4\n@EJL \n\033@", 29);
+}
+
+
+/*
+ * 'StartPage()' - Start a page of graphics.
+ */
+
+void
+StartPage(
+ const ppd_file_t *ppd, /* I - PPD file */
+ const cups_page_header2_t *header) /* I - Page header */
+{
+ int n, t; /* Numbers */
+ int plane; /* Looping var */
+
+
+ /*
+ * Send a reset sequence.
+ */
+
+ if (ppd && ppd->nickname && strstr(ppd->nickname, "OKIDATA") != NULL)
+ printf("\033{A"); /* Set EPSON emulation mode */
+
+ printf("\033@");
+
+ /*
+ * See which type of printer we are using...
+ */
+
+ switch (Model)
+ {
+ case EPSON_9PIN :
+ case EPSON_24PIN :
+ printf("\033P\022"); /* Set 10 CPI */
+
+ if (header->HWResolution[0] == 360 || header->HWResolution[0] == 240)
+ {
+ printf("\033x1"); /* LQ printing */
+ printf("\033U1"); /* Unidirectional */
+ }
+ else
+ {
+ printf("\033x0"); /* Draft printing */
+ printf("\033U0"); /* Bidirectional */
+ }
+
+ printf("\033l%c\033Q%c", 0, /* Side margins */
+ (int)(10.0 * header->PageSize[0] / 72.0 + 0.5));
+ printf("\033\062\033C%c", /* Page length in 1/6th inches */
+ (int)(header->PageSize[1] / 12.0 + 0.5));
+ printf("\033N%c", 0); /* Bottom margin */
+ printf("\033O"); /* No perforation skip */
+
+ /*
+ * Setup various buffer limits...
+ */
+
+ DotBytes = header->cupsRowCount / 8;
+ DotColumns = header->HWResolution[0] / 60;
+ Shingling = 0;
+
+ if (Model == EPSON_9PIN)
+ printf("\033\063\030"); /* Set line feed */
+ else
+ switch (header->HWResolution[0])
+ {
+ case 60:
+ case 120 :
+ case 240 :
+ printf("\033\063\030"); /* Set line feed */
+ break;
+
+ case 180 :
+ case 360 :
+ Shingling = 1;
+
+ if (header->HWResolution[1] == 180)
+ printf("\033\063\010");/* Set line feed */
+ else
+ printf("\033+\010"); /* Set line feed */
+ break;
+ }
+ break;
+
+ default :
+ /*
+ * Set graphics mode...
+ */
+
+ pwrite("\033(G\001\000\001", 6); /* Graphics mode */
+
+ /*
+ * Set the media size...
+ */
+
+ if (Model < EPSON_ICOLOR)
+ {
+ pwrite("\033(U\001\000", 5); /* Resolution/units */
+ putchar(3600 / header->HWResolution[1]);
+ }
+ else
+ {
+ pwrite("\033(U\005\000", 5);
+ putchar(1440 / header->HWResolution[1]);
+ putchar(1440 / header->HWResolution[1]);
+ putchar(1440 / header->HWResolution[0]);
+ putchar(0xa0); /* n/1440ths... */
+ putchar(0x05);
+ }
+
+ n = header->PageSize[1] * header->HWResolution[1] / 72.0;
+
+ pwrite("\033(C\002\000", 5); /* Page length */
+ putchar(n);
+ putchar(n >> 8);
+
+ if (ppd)
+ t = (ppd->sizes[1].length - ppd->sizes[1].top) *
+ header->HWResolution[1] / 72.0;
+ else
+ t = 0;
+
+ pwrite("\033(c\004\000", 5); /* Top & bottom margins */
+ putchar(t);
+ putchar(t >> 8);
+ putchar(n);
+ putchar(n >> 8);
+
+ if (header->HWResolution[1] == 720)
+ {
+ pwrite("\033(i\001\000\001", 6); /* Microweave */
+ pwrite("\033(e\002\000\000\001", 7); /* Small dots */
+ }
+
+ pwrite("\033(V\002\000\000\000", 7); /* Set absolute position 0 */
+
+ DotBytes = 0;
+ DotColumns = 0;
+ Shingling = 0;
+ break;
+ }
+
+ /*
+ * Set other stuff...
+ */
+
+ if (header->cupsColorSpace == CUPS_CSPACE_CMY)
+ NumPlanes = 3;
+ else if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
+ NumPlanes = 4;
+ else if (header->cupsColorSpace == CUPS_CSPACE_KCMYcm)
+ NumPlanes = 6;
+ else
+ NumPlanes = 1;
+
+ Feed = 0; /* No blank lines yet */
+
+ /*
+ * Allocate memory for a line/row of graphics...
+ */
+
+ if ((Planes[0] = malloc(header->cupsBytesPerLine)) == NULL)
+ {
+ fputs("ERROR: Unable to allocate memory\n", stderr);
+ exit(1);
+ }
+
+ for (plane = 1; plane < NumPlanes; plane ++)
+ Planes[plane] = Planes[0] + plane * header->cupsBytesPerLine / NumPlanes;
+
+ if (header->cupsCompression || DotBytes)
+ {
+ if ((CompBuffer = calloc(2, header->cupsWidth)) == NULL)
+ {
+ fputs("ERROR: Unable to allocate memory\n", stderr);
+ exit(1);
+ }
+ }
+ else
+ CompBuffer = NULL;
+
+ if (DotBytes)
+ {
+ if ((LineBuffers[0] = calloc(DotBytes,
+ header->cupsWidth * (Shingling + 1))) == NULL)
+ {
+ fputs("ERROR: Unable to allocate memory\n", stderr);
+ exit(1);
+ }
+
+ LineBuffers[1] = LineBuffers[0] + DotBytes * header->cupsWidth;
+ DotBit = 128;
+ LineCount = 0;
+ EvenOffset = 0;
+ OddOffset = 0;
+ }
+}
+
+
+/*
+ * 'EndPage()' - Finish a page of graphics.
+ */
+
+void
+EndPage(
+ const cups_page_header2_t *header) /* I - Page header */
+{
+ if (DotBytes && header)
+ {
+ /*
+ * Flush remaining graphics as needed...
+ */
+
+ if (!Shingling)
+ {
+ if (DotBit < 128 || EvenOffset)
+ OutputRows(header, 0);
+ }
+ else if (OddOffset > EvenOffset)
+ {
+ OutputRows(header, 1);
+ OutputRows(header, 0);
+ }
+ else
+ {
+ OutputRows(header, 0);
+ OutputRows(header, 1);
+ }
+ }
+
+ /*
+ * Eject the current page...
+ */
+
+ putchar(12); /* Form feed */
+ fflush(stdout);
+
+ /*
+ * Free memory...
+ */
+
+ free(Planes[0]);
+
+ if (CompBuffer)
+ free(CompBuffer);
+
+ if (DotBytes)
+ free(LineBuffers[0]);
+}
+
+
+/*
+ * 'Shutdown()' - Shutdown the printer.
+ */
+
+void
+Shutdown(void)
+{
+ /*
+ * Send a reset sequence.
+ */
+
+ printf("\033@");
+}
+
+
+/*
+ * 'CancelJob()' - Cancel the current job...
+ */
+
+void
+CancelJob(int sig) /* I - Signal */
+{
+ (void)sig;
+
+ Canceled = 1;
+}
+
+
+/*
+ * 'CompressData()' - Compress a line of graphics.
+ */
+
+void
+CompressData(const unsigned char *line, /* I - Data to compress */
+ int length,/* I - Number of bytes */
+ int plane, /* I - Color plane */
+ int type, /* I - Type of compression */
+ int xstep, /* I - X resolution */
+ int ystep) /* I - Y resolution */
+{
+ const unsigned char *line_ptr, /* Current byte pointer */
+ *line_end, /* End-of-line byte pointer */
+ *start; /* Start of compression sequence */
+ unsigned char *comp_ptr, /* Pointer into compression buffer */
+ temp; /* Current byte */
+ int count; /* Count of bytes for output */
+ static int ctable[6] = { 0, 2, 1, 4, 18, 17 };
+ /* KCMYcm color values */
+
+
+ /*
+ * Setup pointers...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+
+ /*
+ * Do depletion for 720 DPI printing...
+ */
+
+ if (ystep == 5)
+ {
+ for (comp_ptr = (unsigned char *)line; comp_ptr < line_end;)
+ {
+ /*
+ * Grab the current byte...
+ */
+
+ temp = *comp_ptr;
+
+ /*
+ * Check adjacent bits...
+ */
+
+ if ((temp & 0xc0) == 0xc0)
+ temp &= 0xbf;
+ if ((temp & 0x60) == 0x60)
+ temp &= 0xdf;
+ if ((temp & 0x30) == 0x30)
+ temp &= 0xef;
+ if ((temp & 0x18) == 0x18)
+ temp &= 0xf7;
+ if ((temp & 0x0c) == 0x0c)
+ temp &= 0xfb;
+ if ((temp & 0x06) == 0x06)
+ temp &= 0xfd;
+ if ((temp & 0x03) == 0x03)
+ temp &= 0xfe;
+
+ *comp_ptr++ = temp;
+
+ /*
+ * Check the last bit in the current byte and the first bit in the
+ * next byte...
+ */
+
+ if ((temp & 0x01) && comp_ptr < line_end && *comp_ptr & 0x80)
+ *comp_ptr &= 0x7f;
+ }
+ }
+
+ switch (type)
+ {
+ case 0 :
+ /*
+ * Do no compression...
+ */
+ break;
+
+ case 1 :
+ /*
+ * Do TIFF pack-bits encoding...
+ */
+
+ comp_ptr = CompBuffer;
+
+ while (line_ptr < line_end)
+ {
+ if ((line_ptr + 1) >= line_end)
+ {
+ /*
+ * Single byte on the end...
+ */
+
+ *comp_ptr++ = 0x00;
+ *comp_ptr++ = *line_ptr++;
+ }
+ else if (line_ptr[0] == line_ptr[1])
+ {
+ /*
+ * Repeated sequence...
+ */
+
+ line_ptr ++;
+ count = 2;
+
+ while (line_ptr < (line_end - 1) &&
+ line_ptr[0] == line_ptr[1] &&
+ count < 127)
+ {
+ line_ptr ++;
+ count ++;
+ }
+
+ *comp_ptr++ = 257 - count;
+ *comp_ptr++ = *line_ptr++;
+ }
+ else
+ {
+ /*
+ * Non-repeated sequence...
+ */
+
+ start = line_ptr;
+ line_ptr ++;
+ count = 1;
+
+ while (line_ptr < (line_end - 1) &&
+ line_ptr[0] != line_ptr[1] &&
+ count < 127)
+ {
+ line_ptr ++;
+ count ++;
+ }
+
+ *comp_ptr++ = count - 1;
+
+ memcpy(comp_ptr, start, count);
+ comp_ptr += count;
+ }
+ }
+
+ line_ptr = CompBuffer;
+ line_end = comp_ptr;
+ break;
+ }
+
+ putchar(0x0d); /* Move print head to left margin */
+
+ if (Model < EPSON_ICOLOR)
+ {
+ /*
+ * Do graphics the "old" way...
+ */
+
+ if (NumPlanes > 1)
+ {
+ /*
+ * Set the color...
+ */
+
+ if (plane > 3)
+ printf("\033(r%c%c%c%c", 2, 0, 1, ctable[plane] & 15);
+ /* Set extended color */
+ else if (NumPlanes == 3)
+ printf("\033r%c", ctable[plane + 1]);
+ /* Set color */
+ else
+ printf("\033r%c", ctable[plane]); /* Set color */
+ }
+
+ /*
+ * Send a raster plane...
+ */
+
+ length *= 8;
+ printf("\033."); /* Raster graphics */
+ putchar(type);
+ putchar(ystep);
+ putchar(xstep);
+ putchar(1);
+ putchar(length);
+ putchar(length >> 8);
+ }
+ else
+ {
+ /*
+ * Do graphics the "new" way...
+ */
+
+ printf("\033i");
+ putchar(ctable[plane]);
+ putchar(type);
+ putchar(1);
+ putchar(length & 255);
+ putchar(length >> 8);
+ putchar(1);
+ putchar(0);
+ }
+
+ pwrite(line_ptr, line_end - line_ptr);
+ fflush(stdout);
+}
+
+
+/*
+ * 'OutputLine()' - Output a line of graphics.
+ */
+
+void
+OutputLine(
+ const cups_page_header2_t *header) /* I - Page header */
+{
+ if (header->cupsRowCount)
+ {
+ int width;
+ unsigned char *tempptr,
+ *evenptr,
+ *oddptr;
+ register int x;
+ unsigned char bit;
+ const unsigned char *pixel;
+ unsigned char *temp;
+
+
+ /*
+ * Collect bitmap data in the line buffers and write after each buffer.
+ */
+
+ for (x = header->cupsWidth, bit = 128, pixel = Planes[0],
+ temp = CompBuffer;
+ x > 0;
+ x --, temp ++)
+ {
+ if (*pixel & bit)
+ *temp |= DotBit;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ bit = 128;
+ pixel ++;
+ }
+ }
+
+ if (DotBit > 1)
+ DotBit >>= 1;
+ else
+ {
+ /*
+ * Copy the holding buffer to the output buffer, shingling as necessary...
+ */
+
+ if (Shingling && LineCount != 0)
+ {
+ /*
+ * Shingle the output...
+ */
+
+ if (LineCount & 1)
+ {
+ evenptr = LineBuffers[1] + OddOffset;
+ oddptr = LineBuffers[0] + EvenOffset + DotBytes;
+ }
+ else
+ {
+ evenptr = LineBuffers[0] + EvenOffset;
+ oddptr = LineBuffers[1] + OddOffset + DotBytes;
+ }
+
+ for (width = header->cupsWidth, tempptr = CompBuffer;
+ width > 0;
+ width -= 2, tempptr += 2, oddptr += DotBytes * 2,
+ evenptr += DotBytes * 2)
+ {
+ evenptr[0] = tempptr[0];
+ oddptr[0] = tempptr[1];
+ }
+ }
+ else
+ {
+ /*
+ * Don't shingle the output...
+ */
+
+ for (width = header->cupsWidth, tempptr = CompBuffer,
+ evenptr = LineBuffers[0] + EvenOffset;
+ width > 0;
+ width --, tempptr ++, evenptr += DotBytes)
+ *evenptr = tempptr[0];
+ }
+
+ if (Shingling && LineCount != 0)
+ {
+ EvenOffset ++;
+ OddOffset ++;
+
+ if (EvenOffset == DotBytes)
+ {
+ EvenOffset = 0;
+ OutputRows(header, 0);
+ }
+
+ if (OddOffset == DotBytes)
+ {
+ OddOffset = 0;
+ OutputRows(header, 1);
+ }
+ }
+ else
+ {
+ EvenOffset ++;
+
+ if (EvenOffset == DotBytes)
+ {
+ EvenOffset = 0;
+ OutputRows(header, 0);
+ }
+ }
+
+ DotBit = 128;
+ LineCount ++;
+
+ memset(CompBuffer, 0, header->cupsWidth);
+ }
+ }
+ else
+ {
+ int plane; /* Current plane */
+ int bytes; /* Bytes per plane */
+ int xstep, ystep; /* X & Y resolutions */
+
+
+ /*
+ * Write a single line of bitmap data as needed...
+ */
+
+ xstep = 3600 / header->HWResolution[0];
+ ystep = 3600 / header->HWResolution[1];
+ bytes = header->cupsBytesPerLine / NumPlanes;
+
+ for (plane = 0; plane < NumPlanes; plane ++)
+ {
+ /*
+ * Skip blank data...
+ */
+
+ if (!Planes[plane][0] &&
+ memcmp(Planes[plane], Planes[plane] + 1, bytes - 1) == 0)
+ continue;
+
+ /*
+ * Output whitespace as needed...
+ */
+
+ if (Feed > 0)
+ {
+ pwrite("\033(v\002\000", 5); /* Relative vertical position */
+ putchar(Feed);
+ putchar(Feed >> 8);
+
+ Feed = 0;
+ }
+
+ CompressData(Planes[plane], bytes, plane, header->cupsCompression, xstep,
+ ystep);
+ }
+
+ Feed ++;
+ }
+}
+
+
+/*
+ * 'OutputRows()' - Output 8, 24, or 48 rows.
+ */
+
+void
+OutputRows(
+ const cups_page_header2_t *header, /* I - Page image header */
+ int row) /* I - Row number (0 or 1) */
+{
+ unsigned i, n; /* Looping vars */
+ int dot_count, /* Number of bytes to print */
+ dot_min; /* Minimum number of bytes */
+ unsigned char *dot_ptr, /* Pointer to print data */
+ *ptr; /* Current data */
+
+
+ dot_min = DotBytes * DotColumns;
+
+ if (LineBuffers[row][0] != 0 ||
+ memcmp(LineBuffers[row], LineBuffers[row] + 1,
+ header->cupsWidth * DotBytes - 1))
+ {
+ /*
+ * Skip leading space...
+ */
+
+ i = 0;
+ dot_count = header->cupsWidth * DotBytes;
+ dot_ptr = LineBuffers[row];
+
+ while (dot_count >= dot_min && dot_ptr[0] == 0 &&
+ memcmp(dot_ptr, dot_ptr + 1, dot_min - 1) == 0)
+ {
+ i ++;
+ dot_ptr += dot_min;
+ dot_count -= dot_min;
+ }
+
+ /*
+ * Skip trailing space...
+ */
+
+ while (dot_count >= dot_min && dot_ptr[dot_count - dot_min] == 0 &&
+ memcmp(dot_ptr + dot_count - dot_min,
+ dot_ptr + dot_count - dot_min + 1, dot_min - 1) == 0)
+ dot_count -= dot_min;
+
+ /*
+ * Position print head for printing...
+ */
+
+ if (i == 0)
+ putchar('\r');
+ else
+ {
+ putchar(0x1b);
+ putchar('$');
+ putchar(i & 255);
+ putchar(i >> 8);
+ }
+
+ /*
+ * Start bitmap graphics for this line...
+ */
+
+ printf("\033*"); /* Select bit image */
+ switch (header->HWResolution[0])
+ {
+ case 60 : /* 60x60/72 DPI gfx */
+ putchar(0);
+ break;
+ case 120 : /* 120x60/72 DPI gfx */
+ putchar(1);
+ break;
+ case 180 : /* 180 DPI gfx */
+ putchar(39);
+ break;
+ case 240 : /* 240x72 DPI gfx */
+ putchar(3);
+ break;
+ case 360 : /* 360x180/360 DPI gfx */
+ if (header->HWResolution[1] == 180)
+ {
+ if (Shingling && LineCount != 0)
+ putchar(40); /* 360x180 fast */
+ else
+ putchar(41); /* 360x180 slow */
+ }
+ else
+ {
+ if (Shingling && LineCount != 0)
+ putchar(72); /* 360x360 fast */
+ else
+ putchar(73); /* 360x360 slow */
+ }
+ break;
+ }
+
+ n = (unsigned)dot_count / DotBytes;
+ putchar(n & 255);
+ putchar(n / 256);
+
+ /*
+ * Write the graphics data...
+ */
+
+ if (header->HWResolution[0] == 120 ||
+ header->HWResolution[0] == 240)
+ {
+ /*
+ * Need to interleave the dots to avoid hosing the print head...
+ */
+
+ for (n = dot_count / 2, ptr = dot_ptr; n > 0; n --, ptr += 2)
+ {
+ putchar(*ptr);
+ putchar(0);
+ }
+
+ /*
+ * Move the head back and print the odd bytes...
+ */
+
+ if (i == 0)
+ putchar('\r');
+ else
+ {
+ putchar(0x1b);
+ putchar('$');
+ putchar(i & 255);
+ putchar(i >> 8);
+ }
+
+ if (header->HWResolution[0] == 120)
+ printf("\033*\001"); /* Select bit image */
+ else
+ printf("\033*\003"); /* Select bit image */
+
+ n = (unsigned)dot_count / DotBytes;
+ putchar(n & 255);
+ putchar(n / 256);
+
+ for (n = dot_count / 2, ptr = dot_ptr + 1; n > 0; n --, ptr += 2)
+ {
+ putchar(0);
+ putchar(*ptr);
+ }
+ }
+ else
+ pwrite(dot_ptr, dot_count);
+ }
+
+ /*
+ * Feed the paper...
+ */
+
+ putchar('\n');
+
+ if (Shingling && row == 1)
+ {
+ if (header->HWResolution[1] == 360)
+ printf("\n\n\n\n");
+ else
+ printf("\n");
+ }
+
+ fflush(stdout);
+
+ /*
+ * Clear the buffer...
+ */
+
+ memset(LineBuffers[row], 0, header->cupsWidth * DotBytes);
+}
+
+
+/*
+ * 'main()' - Main entry and processing of driver.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fd; /* File descriptor */
+ cups_raster_t *ras; /* Raster stream for printing */
+ cups_page_header2_t header; /* Page header from file */
+ ppd_file_t *ppd; /* PPD file */
+ int page; /* Current page */
+ int y; /* Current line */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc < 6 || argc > 7)
+ {
+ /*
+ * We don't have the correct number of arguments; write an error message
+ * and return.
+ */
+
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("%s job-id user title copies options [file]"),
+ "rastertoepson");
+ return (1);
+ }
+
+ /*
+ * Open the page stream...
+ */
+
+ if (argc == 7)
+ {
+ if ((fd = open(argv[6], O_RDONLY)) == -1)
+ {
+ _cupsLangPrintError("ERROR", _("Unable to open raster file"));
+ sleep(1);
+ return (1);
+ }
+ }
+ else
+ fd = 0;
+
+ ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
+
+ /*
+ * Register a signal handler to eject the current page if the
+ * job is cancelled.
+ */
+
+ Canceled = 0;
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, CancelJob);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = CancelJob;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, CancelJob);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Initialize the print device...
+ */
+
+ ppd = ppdOpenFile(getenv("PPD"));
+ if (!ppd)
+ {
+ ppd_status_t status; /* PPD error */
+ int linenum; /* Line number */
+
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("The PPD file could not be opened."));
+
+ status = ppdLastError(&linenum);
+
+ fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
+
+ return (1);
+ }
+
+ Model = ppd->model_number;
+
+ Setup();
+
+ /*
+ * Process pages as needed...
+ */
+
+ page = 0;
+
+ while (cupsRasterReadHeader2(ras, &header))
+ {
+ /*
+ * Write a status message with the page number and number of copies.
+ */
+
+ if (Canceled)
+ break;
+
+ page ++;
+
+ fprintf(stderr, "PAGE: %d %d\n", page, header.NumCopies);
+ _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), page);
+
+ /*
+ * Start the page...
+ */
+
+ StartPage(ppd, &header);
+
+ /*
+ * Loop for each line on the page...
+ */
+
+ for (y = 0; y < header.cupsHeight; y ++)
+ {
+ /*
+ * Let the user know how far we have progressed...
+ */
+
+ if (Canceled)
+ break;
+
+ if ((y & 127) == 0)
+ {
+ _cupsLangPrintFilter(stderr, "INFO",
+ _("Printing page %d, %d%% complete."),
+ page, 100 * y / header.cupsHeight);
+ fprintf(stderr, "ATTR: job-media-progress=%d\n",
+ 100 * y / header.cupsHeight);
+ }
+
+ /*
+ * Read a line of graphics...
+ */
+
+ if (cupsRasterReadPixels(ras, Planes[0], header.cupsBytesPerLine) < 1)
+ break;
+
+ /*
+ * Write it to the printer...
+ */
+
+ OutputLine(&header);
+ }
+
+ /*
+ * Eject the page...
+ */
+
+ _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), page);
+
+ EndPage(&header);
+
+ if (Canceled)
+ break;
+ }
+
+ /*
+ * Shutdown the printer...
+ */
+
+ Shutdown();
+
+ ppdClose(ppd);
+
+ /*
+ * Close the raster stream...
+ */
+
+ cupsRasterClose(ras);
+ if (fd != 0)
+ close(fd);
+
+ /*
+ * If no pages were printed, send an error message...
+ */
+
+ if (page == 0)
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
+ return (1);
+ }
+ else
+ return (0);
+}
+
+
+/*
+ * End of "$Id: rastertoepson.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/rastertohp.c b/cups/libs/filter/rastertohp.c
new file mode 100644
index 000000000..5efad593b
--- /dev/null
+++ b/cups/libs/filter/rastertohp.c
@@ -0,0 +1,886 @@
+/*
+ * "$Id: rastertohp.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Hewlett-Packard Page Control Language filter for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1993-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * Setup() - Prepare the printer for printing.
+ * StartPage() - Start a page of graphics.
+ * EndPage() - Finish a page of graphics.
+ * Shutdown() - Shutdown the printer.
+ * CancelJob() - Cancel the current job...
+ * CompressData() - Compress a line of graphics.
+ * OutputLine() - Output a line of graphics.
+ * main() - Main entry and processing of driver.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include <cups/string-private.h>
+#include <cups/language-private.h>
+#include <cups/raster.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+
+/*
+ * Globals...
+ */
+
+unsigned char *Planes[4], /* Output buffers */
+ *CompBuffer, /* Compression buffer */
+ *BitBuffer; /* Buffer for output bits */
+int NumPlanes, /* Number of color planes */
+ ColorBits, /* Number of bits per color */
+ Feed, /* Number of lines to skip */
+ Duplex, /* Current duplex mode */
+ Page, /* Current page number */
+ Canceled; /* Has the current job been canceled? */
+
+
+/*
+ * Prototypes...
+ */
+
+void Setup(void);
+void StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
+void EndPage(void);
+void Shutdown(void);
+
+void CancelJob(int sig);
+void CompressData(unsigned char *line, int length, int plane, int type);
+void OutputLine(cups_page_header2_t *header);
+
+
+/*
+ * 'Setup()' - Prepare the printer for printing.
+ */
+
+void
+Setup(void)
+{
+ /*
+ * Send a PCL reset sequence.
+ */
+
+ putchar(0x1b);
+ putchar('E');
+}
+
+
+/*
+ * 'StartPage()' - Start a page of graphics.
+ */
+
+void
+StartPage(ppd_file_t *ppd, /* I - PPD file */
+ cups_page_header2_t *header) /* I - Page header */
+{
+ int plane; /* Looping var */
+
+
+ /*
+ * Show page device dictionary...
+ */
+
+ fprintf(stderr, "DEBUG: StartPage...\n");
+ fprintf(stderr, "DEBUG: MediaClass = \"%s\"\n", header->MediaClass);
+ fprintf(stderr, "DEBUG: MediaColor = \"%s\"\n", header->MediaColor);
+ fprintf(stderr, "DEBUG: MediaType = \"%s\"\n", header->MediaType);
+ fprintf(stderr, "DEBUG: OutputType = \"%s\"\n", header->OutputType);
+
+ fprintf(stderr, "DEBUG: AdvanceDistance = %d\n", header->AdvanceDistance);
+ fprintf(stderr, "DEBUG: AdvanceMedia = %d\n", header->AdvanceMedia);
+ fprintf(stderr, "DEBUG: Collate = %d\n", header->Collate);
+ fprintf(stderr, "DEBUG: CutMedia = %d\n", header->CutMedia);
+ fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex);
+ fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0],
+ header->HWResolution[1]);
+ fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n",
+ header->ImagingBoundingBox[0], header->ImagingBoundingBox[1],
+ header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
+ fprintf(stderr, "DEBUG: InsertSheet = %d\n", header->InsertSheet);
+ fprintf(stderr, "DEBUG: Jog = %d\n", header->Jog);
+ fprintf(stderr, "DEBUG: LeadingEdge = %d\n", header->LeadingEdge);
+ fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0],
+ header->Margins[1]);
+ fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
+ fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
+ fprintf(stderr, "DEBUG: MediaWeight = %d\n", header->MediaWeight);
+ fprintf(stderr, "DEBUG: MirrorPrint = %d\n", header->MirrorPrint);
+ fprintf(stderr, "DEBUG: NegativePrint = %d\n", header->NegativePrint);
+ fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies);
+ fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation);
+ fprintf(stderr, "DEBUG: OutputFaceUp = %d\n", header->OutputFaceUp);
+ fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0],
+ header->PageSize[1]);
+ fprintf(stderr, "DEBUG: Separations = %d\n", header->Separations);
+ fprintf(stderr, "DEBUG: TraySwitch = %d\n", header->TraySwitch);
+ fprintf(stderr, "DEBUG: Tumble = %d\n", header->Tumble);
+ fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
+ fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
+ fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
+ fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
+ fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
+ fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
+ fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
+ fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
+ fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
+
+ /*
+ * Setup printer/job attributes...
+ */
+
+ Duplex = header->Duplex;
+ ColorBits = header->cupsBitsPerColor;
+
+ if ((!Duplex || (Page & 1)) && header->MediaPosition)
+ printf("\033&l%dH", /* Set media position */
+ header->MediaPosition);
+
+ if (Duplex && ppd && ppd->model_number == 2)
+ {
+ /*
+ * Handle duplexing on new DeskJet printers...
+ */
+
+ printf("\033&l-2H"); /* Load media */
+
+ if (Page & 1)
+ printf("\033&l2S"); /* Set duplex mode */
+ }
+
+ if (!Duplex || (Page & 1) || (ppd && ppd->model_number == 2))
+ {
+ /*
+ * Set the media size...
+ */
+
+ printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */
+ printf("\033&l0O"); /* Set portrait orientation */
+
+ switch (header->PageSize[1])
+ {
+ case 540 : /* Monarch Envelope */
+ printf("\033&l80A"); /* Set page size */
+ break;
+
+ case 595 : /* A5 */
+ printf("\033&l25A"); /* Set page size */
+ break;
+
+ case 624 : /* DL Envelope */
+ printf("\033&l90A"); /* Set page size */
+ break;
+
+ case 649 : /* C5 Envelope */
+ printf("\033&l91A"); /* Set page size */
+ break;
+
+ case 684 : /* COM-10 Envelope */
+ printf("\033&l81A"); /* Set page size */
+ break;
+
+ case 709 : /* B5 Envelope */
+ printf("\033&l100A"); /* Set page size */
+ break;
+
+ case 756 : /* Executive */
+ printf("\033&l1A"); /* Set page size */
+ break;
+
+ case 792 : /* Letter */
+ printf("\033&l2A"); /* Set page size */
+ break;
+
+ case 842 : /* A4 */
+ printf("\033&l26A"); /* Set page size */
+ break;
+
+ case 1008 : /* Legal */
+ printf("\033&l3A"); /* Set page size */
+ break;
+
+ case 1191 : /* A3 */
+ printf("\033&l27A"); /* Set page size */
+ break;
+
+ case 1224 : /* Tabloid */
+ printf("\033&l6A"); /* Set page size */
+ break;
+ }
+
+ printf("\033&l%dP", /* Set page length */
+ header->PageSize[1] / 12);
+ printf("\033&l0E"); /* Set top margin to 0 */
+ }
+
+ if (!Duplex || (Page & 1))
+ {
+ /*
+ * Set other job options...
+ */
+
+ printf("\033&l%dX", header->NumCopies); /* Set number copies */
+
+ if (header->cupsMediaType &&
+ (!ppd || ppd->model_number != 2 || header->HWResolution[0] == 600))
+ printf("\033&l%dM", /* Set media type */
+ header->cupsMediaType);
+
+ if (!ppd || ppd->model_number != 2)
+ {
+ int mode = Duplex ? 1 + header->Tumble != 0 : 0;
+
+ printf("\033&l%dS", mode); /* Set duplex mode */
+ printf("\033&l0L"); /* Turn off perforation skip */
+ }
+ }
+ else if (!ppd || ppd->model_number != 2)
+ printf("\033&a2G"); /* Set back side */
+
+ /*
+ * Set graphics mode...
+ */
+
+ if (ppd && ppd->model_number == 2)
+ {
+ /*
+ * Figure out the number of color planes...
+ */
+
+ if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
+ NumPlanes = 4;
+ else
+ NumPlanes = 1;
+
+ /*
+ * Set the resolution and top-of-form...
+ */
+
+ printf("\033&u%dD", header->HWResolution[0]);
+ /* Resolution */
+ printf("\033&l0e0L"); /* Reset top and don't skip */
+ printf("\033*p0Y\033*p0X"); /* Set top of form */
+
+ /*
+ * Send 26-byte configure image data command with horizontal and
+ * vertical resolutions as well as a color count...
+ */
+
+ printf("\033*g26W");
+ putchar(2); /* Format 2 */
+ putchar(NumPlanes); /* Output planes */
+
+ putchar(header->HWResolution[0] >> 8); /* Black resolution */
+ putchar(header->HWResolution[0]);
+ putchar(header->HWResolution[1] >> 8);
+ putchar(header->HWResolution[1]);
+ putchar(0);
+ putchar(1 << ColorBits); /* # of black levels */
+
+ putchar(header->HWResolution[0] >> 8); /* Cyan resolution */
+ putchar(header->HWResolution[0]);
+ putchar(header->HWResolution[1] >> 8);
+ putchar(header->HWResolution[1]);
+ putchar(0);
+ putchar(1 << ColorBits); /* # of cyan levels */
+
+ putchar(header->HWResolution[0] >> 8); /* Magenta resolution */
+ putchar(header->HWResolution[0]);
+ putchar(header->HWResolution[1] >> 8);
+ putchar(header->HWResolution[1]);
+ putchar(0);
+ putchar(1 << ColorBits); /* # of magenta levels */
+
+ putchar(header->HWResolution[0] >> 8); /* Yellow resolution */
+ putchar(header->HWResolution[0]);
+ putchar(header->HWResolution[1] >> 8);
+ putchar(header->HWResolution[1]);
+ putchar(0);
+ putchar(1 << ColorBits); /* # of yellow levels */
+
+ printf("\033&l0H"); /* Set media position */
+ }
+ else
+ {
+ printf("\033*t%dR", header->HWResolution[0]);
+ /* Set resolution */
+
+ if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
+ {
+ NumPlanes = 4;
+ printf("\033*r-4U"); /* Set KCMY graphics */
+ }
+ else if (header->cupsColorSpace == CUPS_CSPACE_CMY)
+ {
+ NumPlanes = 3;
+ printf("\033*r-3U"); /* Set CMY graphics */
+ }
+ else
+ NumPlanes = 1; /* Black&white graphics */
+
+ /*
+ * Set size and position of graphics...
+ */
+
+ printf("\033*r%dS", header->cupsWidth); /* Set width */
+ printf("\033*r%dT", header->cupsHeight); /* Set height */
+
+ printf("\033&a0H"); /* Set horizontal position */
+
+ if (ppd)
+ printf("\033&a%.0fV", /* Set vertical position */
+ 10.0 * (ppd->sizes[0].length - ppd->sizes[0].top));
+ else
+ printf("\033&a0V"); /* Set top-of-page */
+ }
+
+ printf("\033*r1A"); /* Start graphics */
+
+ if (header->cupsCompression)
+ printf("\033*b%dM", /* Set compression */
+ header->cupsCompression);
+
+ Feed = 0; /* No blank lines yet */
+
+ /*
+ * Allocate memory for a line of graphics...
+ */
+
+ if ((Planes[0] = malloc(header->cupsBytesPerLine)) == NULL)
+ {
+ fputs("ERROR: Unable to allocate memory\n", stderr);
+ exit(1);
+ }
+
+ for (plane = 1; plane < NumPlanes; plane ++)
+ Planes[plane] = Planes[0] + plane * header->cupsBytesPerLine / NumPlanes;
+
+ if (ColorBits > 1)
+ BitBuffer = malloc(ColorBits * ((header->cupsWidth + 7) / 8));
+ else
+ BitBuffer = NULL;
+
+ if (header->cupsCompression)
+ CompBuffer = malloc(header->cupsBytesPerLine * 2);
+ else
+ CompBuffer = NULL;
+}
+
+
+/*
+ * 'EndPage()' - Finish a page of graphics.
+ */
+
+void
+EndPage(void)
+{
+ /*
+ * Eject the current page...
+ */
+
+ if (NumPlanes > 1)
+ {
+ printf("\033*rC"); /* End color GFX */
+
+ if (!(Duplex && (Page & 1)))
+ printf("\033&l0H"); /* Eject current page */
+ }
+ else
+ {
+ printf("\033*r0B"); /* End GFX */
+
+ if (!(Duplex && (Page & 1)))
+ printf("\014"); /* Eject current page */
+ }
+
+ fflush(stdout);
+
+ /*
+ * Free memory...
+ */
+
+ free(Planes[0]);
+
+ if (BitBuffer)
+ free(BitBuffer);
+
+ if (CompBuffer)
+ free(CompBuffer);
+}
+
+
+/*
+ * 'Shutdown()' - Shutdown the printer.
+ */
+
+void
+Shutdown(void)
+{
+ /*
+ * Send a PCL reset sequence.
+ */
+
+ putchar(0x1b);
+ putchar('E');
+}
+
+
+/*
+ * 'CancelJob()' - Cancel the current job...
+ */
+
+void
+CancelJob(int sig) /* I - Signal */
+{
+ (void)sig;
+
+ Canceled = 1;
+}
+
+
+/*
+ * 'CompressData()' - Compress a line of graphics.
+ */
+
+void
+CompressData(unsigned char *line, /* I - Data to compress */
+ int length, /* I - Number of bytes */
+ int plane, /* I - Color plane */
+ int type) /* I - Type of compression */
+{
+ unsigned char *line_ptr, /* Current byte pointer */
+ *line_end, /* End-of-line byte pointer */
+ *comp_ptr, /* Pointer into compression buffer */
+ *start; /* Start of compression sequence */
+ int count; /* Count of bytes for output */
+
+
+ switch (type)
+ {
+ default :
+ /*
+ * Do no compression...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+ break;
+
+ case 1 :
+ /*
+ * Do run-length encoding...
+ */
+
+ line_end = line + length;
+ for (line_ptr = line, comp_ptr = CompBuffer;
+ line_ptr < line_end;
+ comp_ptr += 2, line_ptr += count)
+ {
+ for (count = 1;
+ (line_ptr + count) < line_end &&
+ line_ptr[0] == line_ptr[count] &&
+ count < 256;
+ count ++);
+
+ comp_ptr[0] = count - 1;
+ comp_ptr[1] = line_ptr[0];
+ }
+
+ line_ptr = CompBuffer;
+ line_end = comp_ptr;
+ break;
+
+ case 2 :
+ /*
+ * Do TIFF pack-bits encoding...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+ comp_ptr = CompBuffer;
+
+ while (line_ptr < line_end)
+ {
+ if ((line_ptr + 1) >= line_end)
+ {
+ /*
+ * Single byte on the end...
+ */
+
+ *comp_ptr++ = 0x00;
+ *comp_ptr++ = *line_ptr++;
+ }
+ else if (line_ptr[0] == line_ptr[1])
+ {
+ /*
+ * Repeated sequence...
+ */
+
+ line_ptr ++;
+ count = 2;
+
+ while (line_ptr < (line_end - 1) &&
+ line_ptr[0] == line_ptr[1] &&
+ count < 127)
+ {
+ line_ptr ++;
+ count ++;
+ }
+
+ *comp_ptr++ = 257 - count;
+ *comp_ptr++ = *line_ptr++;
+ }
+ else
+ {
+ /*
+ * Non-repeated sequence...
+ */
+
+ start = line_ptr;
+ line_ptr ++;
+ count = 1;
+
+ while (line_ptr < (line_end - 1) &&
+ line_ptr[0] != line_ptr[1] &&
+ count < 127)
+ {
+ line_ptr ++;
+ count ++;
+ }
+
+ *comp_ptr++ = count - 1;
+
+ memcpy(comp_ptr, start, count);
+ comp_ptr += count;
+ }
+ }
+
+ line_ptr = CompBuffer;
+ line_end = comp_ptr;
+ break;
+ }
+
+ /*
+ * Set the length of the data and write a raster plane...
+ */
+
+ printf("\033*b%d%c", (int)(line_end - line_ptr), plane);
+ fwrite(line_ptr, line_end - line_ptr, 1, stdout);
+}
+
+
+/*
+ * 'OutputLine()' - Output a line of graphics.
+ */
+
+void
+OutputLine(cups_page_header2_t *header) /* I - Page header */
+{
+ int plane, /* Current plane */
+ bytes, /* Bytes to write */
+ count; /* Bytes to convert */
+ unsigned char bit, /* Current plane data */
+ bit0, /* Current low bit data */
+ bit1, /* Current high bit data */
+ *plane_ptr, /* Pointer into Planes */
+ *bit_ptr; /* Pointer into BitBuffer */
+
+
+ /*
+ * Output whitespace as needed...
+ */
+
+ if (Feed > 0)
+ {
+ printf("\033*b%dY", Feed);
+ Feed = 0;
+ }
+
+ /*
+ * Write bitmap data as needed...
+ */
+
+ bytes = (header->cupsWidth + 7) / 8;
+
+ for (plane = 0; plane < NumPlanes; plane ++)
+ if (ColorBits == 1)
+ {
+ /*
+ * Send bits as-is...
+ */
+
+ CompressData(Planes[plane], bytes, plane < (NumPlanes - 1) ? 'V' : 'W',
+ header->cupsCompression);
+ }
+ else
+ {
+ /*
+ * Separate low and high bit data into separate buffers.
+ */
+
+ for (count = header->cupsBytesPerLine / NumPlanes,
+ plane_ptr = Planes[plane], bit_ptr = BitBuffer;
+ count > 0;
+ count -= 2, plane_ptr += 2, bit_ptr ++)
+ {
+ bit = plane_ptr[0];
+
+ bit0 = ((bit & 64) << 1) | ((bit & 16) << 2) | ((bit & 4) << 3) | ((bit & 1) << 4);
+ bit1 = (bit & 128) | ((bit & 32) << 1) | ((bit & 8) << 2) | ((bit & 2) << 3);
+
+ if (count > 1)
+ {
+ bit = plane_ptr[1];
+
+ bit0 |= (bit & 1) | ((bit & 4) >> 1) | ((bit & 16) >> 2) | ((bit & 64) >> 3);
+ bit1 |= ((bit & 2) >> 1) | ((bit & 8) >> 2) | ((bit & 32) >> 3) | ((bit & 128) >> 4);
+ }
+
+ bit_ptr[0] = bit0;
+ bit_ptr[bytes] = bit1;
+ }
+
+ /*
+ * Send low and high bits...
+ */
+
+ CompressData(BitBuffer, bytes, 'V', header->cupsCompression);
+ CompressData(BitBuffer + bytes, bytes, plane < (NumPlanes - 1) ? 'V' : 'W',
+ header->cupsCompression);
+ }
+
+ fflush(stdout);
+}
+
+
+/*
+ * 'main()' - Main entry and processing of driver.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fd; /* File descriptor */
+ cups_raster_t *ras; /* Raster stream for printing */
+ cups_page_header2_t header; /* Page header from file */
+ int y; /* Current line */
+ ppd_file_t *ppd; /* PPD file */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc < 6 || argc > 7)
+ {
+ /*
+ * We don't have the correct number of arguments; write an error message
+ * and return.
+ */
+
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("%s job-id user title copies options [file]"),
+ "rastertohp");
+ return (1);
+ }
+
+ /*
+ * Open the page stream...
+ */
+
+ if (argc == 7)
+ {
+ if ((fd = open(argv[6], O_RDONLY)) == -1)
+ {
+ _cupsLangPrintError("ERROR", _("Unable to open raster file"));
+ sleep(1);
+ return (1);
+ }
+ }
+ else
+ fd = 0;
+
+ ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
+
+ /*
+ * Register a signal handler to eject the current page if the
+ * job is cancelled.
+ */
+
+ Canceled = 0;
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, CancelJob);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = CancelJob;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, CancelJob);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Initialize the print device...
+ */
+
+ ppd = ppdOpenFile(getenv("PPD"));
+ if (!ppd)
+ {
+ ppd_status_t status; /* PPD error */
+ int linenum; /* Line number */
+
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("The PPD file could not be opened."));
+
+ status = ppdLastError(&linenum);
+
+ fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
+
+ return (1);
+ }
+
+ Setup();
+
+ /*
+ * Process pages as needed...
+ */
+
+ Page = 0;
+
+ while (cupsRasterReadHeader2(ras, &header))
+ {
+ /*
+ * Write a status message with the page number and number of copies.
+ */
+
+ if (Canceled)
+ break;
+
+ Page ++;
+
+ fprintf(stderr, "PAGE: %d %d\n", Page, header.NumCopies);
+ _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page);
+
+ /*
+ * Start the page...
+ */
+
+ StartPage(ppd, &header);
+
+ /*
+ * Loop for each line on the page...
+ */
+
+ for (y = 0; y < header.cupsHeight; y ++)
+ {
+ /*
+ * Let the user know how far we have progressed...
+ */
+
+ if (Canceled)
+ break;
+
+ if ((y & 127) == 0)
+ {
+ _cupsLangPrintFilter(stderr, "INFO",
+ _("Printing page %d, %d%% complete."),
+ Page, 100 * y / header.cupsHeight);
+ fprintf(stderr, "ATTR: job-media-progress=%d\n",
+ 100 * y / header.cupsHeight);
+ }
+
+ /*
+ * Read a line of graphics...
+ */
+
+ if (cupsRasterReadPixels(ras, Planes[0], header.cupsBytesPerLine) < 1)
+ break;
+
+ /*
+ * See if the line is blank; if not, write it to the printer...
+ */
+
+ if (Planes[0][0] ||
+ memcmp(Planes[0], Planes[0] + 1, header.cupsBytesPerLine - 1))
+ OutputLine(&header);
+ else
+ Feed ++;
+ }
+
+ /*
+ * Eject the page...
+ */
+
+ _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page);
+
+ EndPage();
+
+ if (Canceled)
+ break;
+ }
+
+ /*
+ * Shutdown the printer...
+ */
+
+ Shutdown();
+
+ if (ppd)
+ ppdClose(ppd);
+
+ /*
+ * Close the raster stream...
+ */
+
+ cupsRasterClose(ras);
+ if (fd != 0)
+ close(fd);
+
+ /*
+ * If no pages were printed, send an error message...
+ */
+
+ if (Page == 0)
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
+ return (1);
+ }
+ else
+ return (0);
+}
+
+
+/*
+ * End of "$Id: rastertohp.c 10996 2013-05-29 11:51:34Z msweet $".
+ */
diff --git a/cups/libs/filter/rastertolabel.c b/cups/libs/filter/rastertolabel.c
new file mode 100644
index 000000000..ca0e143da
--- /dev/null
+++ b/cups/libs/filter/rastertolabel.c
@@ -0,0 +1,1306 @@
+/*
+ * "$Id: rastertolabel.c 11756 2014-03-27 17:06:25Z msweet $"
+ *
+ * Label printer filter for CUPS.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2001-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * Setup() - Prepare the printer for printing.
+ * StartPage() - Start a page of graphics.
+ * EndPage() - Finish a page of graphics.
+ * CancelJob() - Cancel the current job...
+ * OutputLine() - Output a line of graphics.
+ * PCLCompress() - Output a PCL (mode 3) compressed line.
+ * ZPLCompress() - Output a run-length compression sequence.
+ * main() - Main entry and processing of driver.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include <cups/string-private.h>
+#include <cups/language-private.h>
+#include <cups/raster.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+
+/*
+ * This driver filter currently supports Dymo, Intellitech, and Zebra
+ * label printers.
+ *
+ * The Dymo portion of the driver has been tested with the 300, 330,
+ * and 330 Turbo label printers; it may also work with other models.
+ * The Dymo printers support printing at 136, 203, and 300 DPI.
+ *
+ * The Intellitech portion of the driver has been tested with the
+ * Intellibar 408, 412, and 808 and supports their PCL variant.
+ *
+ * The Zebra portion of the driver has been tested with the LP-2844,
+ * LP-2844Z, QL-320, and QL-420 label printers; it may also work with
+ * other models. The driver supports EPL line mode, EPL page mode,
+ * ZPL, and CPCL as defined in Zebra's online developer documentation.
+ */
+
+/*
+ * Model number constants...
+ */
+
+#define DYMO_3x0 0 /* Dymo Labelwriter 300/330/330 Turbo */
+
+#define ZEBRA_EPL_LINE 0x10 /* Zebra EPL line mode printers */
+#define ZEBRA_EPL_PAGE 0x11 /* Zebra EPL page mode printers */
+#define ZEBRA_ZPL 0x12 /* Zebra ZPL-based printers */
+#define ZEBRA_CPCL 0x13 /* Zebra CPCL-based printers */
+
+#define INTELLITECH_PCL 0x20 /* Intellitech PCL-based printers */
+
+
+/*
+ * Globals...
+ */
+
+unsigned char *Buffer; /* Output buffer */
+unsigned char *CompBuffer; /* Compression buffer */
+unsigned char *LastBuffer; /* Last buffer */
+int LastSet; /* Number of repeat characters */
+int ModelNumber, /* cupsModelNumber attribute */
+ Page, /* Current page */
+ Feed, /* Number of lines to skip */
+ Canceled; /* Non-zero if job is canceled */
+
+
+/*
+ * Prototypes...
+ */
+
+void Setup(ppd_file_t *ppd);
+void StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
+void EndPage(ppd_file_t *ppd, cups_page_header2_t *header);
+void CancelJob(int sig);
+void OutputLine(ppd_file_t *ppd, cups_page_header2_t *header, int y);
+void PCLCompress(unsigned char *line, int length);
+void ZPLCompress(char repeat_char, int repeat_count);
+
+
+/*
+ * 'Setup()' - Prepare the printer for printing.
+ */
+
+void
+Setup(ppd_file_t *ppd) /* I - PPD file */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * Get the model number from the PPD file...
+ */
+
+ if (ppd)
+ ModelNumber = ppd->model_number;
+
+ /*
+ * Initialize based on the model number...
+ */
+
+ switch (ModelNumber)
+ {
+ case DYMO_3x0 :
+ /*
+ * Clear any remaining data...
+ */
+
+ for (i = 0; i < 100; i ++)
+ putchar(0x1b);
+
+ /*
+ * Reset the printer...
+ */
+
+ fputs("\033@", stdout);
+ break;
+
+ case ZEBRA_EPL_LINE :
+ break;
+
+ case ZEBRA_EPL_PAGE :
+ break;
+
+ case ZEBRA_ZPL :
+ break;
+
+ case ZEBRA_CPCL :
+ break;
+
+ case INTELLITECH_PCL :
+ /*
+ * Send a PCL reset sequence.
+ */
+
+ putchar(0x1b);
+ putchar('E');
+ break;
+ }
+}
+
+
+/*
+ * 'StartPage()' - Start a page of graphics.
+ */
+
+void
+StartPage(ppd_file_t *ppd, /* I - PPD file */
+ cups_page_header2_t *header) /* I - Page header */
+{
+ ppd_choice_t *choice; /* Marked choice */
+ int length; /* Actual label length */
+
+
+ /*
+ * Show page device dictionary...
+ */
+
+ fprintf(stderr, "DEBUG: StartPage...\n");
+ fprintf(stderr, "DEBUG: MediaClass = \"%s\"\n", header->MediaClass);
+ fprintf(stderr, "DEBUG: MediaColor = \"%s\"\n", header->MediaColor);
+ fprintf(stderr, "DEBUG: MediaType = \"%s\"\n", header->MediaType);
+ fprintf(stderr, "DEBUG: OutputType = \"%s\"\n", header->OutputType);
+
+ fprintf(stderr, "DEBUG: AdvanceDistance = %d\n", header->AdvanceDistance);
+ fprintf(stderr, "DEBUG: AdvanceMedia = %d\n", header->AdvanceMedia);
+ fprintf(stderr, "DEBUG: Collate = %d\n", header->Collate);
+ fprintf(stderr, "DEBUG: CutMedia = %d\n", header->CutMedia);
+ fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex);
+ fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0],
+ header->HWResolution[1]);
+ fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n",
+ header->ImagingBoundingBox[0], header->ImagingBoundingBox[1],
+ header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
+ fprintf(stderr, "DEBUG: InsertSheet = %d\n", header->InsertSheet);
+ fprintf(stderr, "DEBUG: Jog = %d\n", header->Jog);
+ fprintf(stderr, "DEBUG: LeadingEdge = %d\n", header->LeadingEdge);
+ fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0],
+ header->Margins[1]);
+ fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
+ fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
+ fprintf(stderr, "DEBUG: MediaWeight = %d\n", header->MediaWeight);
+ fprintf(stderr, "DEBUG: MirrorPrint = %d\n", header->MirrorPrint);
+ fprintf(stderr, "DEBUG: NegativePrint = %d\n", header->NegativePrint);
+ fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies);
+ fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation);
+ fprintf(stderr, "DEBUG: OutputFaceUp = %d\n", header->OutputFaceUp);
+ fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0],
+ header->PageSize[1]);
+ fprintf(stderr, "DEBUG: Separations = %d\n", header->Separations);
+ fprintf(stderr, "DEBUG: TraySwitch = %d\n", header->TraySwitch);
+ fprintf(stderr, "DEBUG: Tumble = %d\n", header->Tumble);
+ fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
+ fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
+ fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
+ fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
+ fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
+ fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
+ fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
+ fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
+ fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
+ fprintf(stderr, "DEBUG: cupsRowCount = %d\n", header->cupsRowCount);
+ fprintf(stderr, "DEBUG: cupsRowFeed = %d\n", header->cupsRowFeed);
+ fprintf(stderr, "DEBUG: cupsRowStep = %d\n", header->cupsRowStep);
+
+ switch (ModelNumber)
+ {
+ case DYMO_3x0 :
+ /*
+ * Setup printer/job attributes...
+ */
+
+ length = header->PageSize[1] * header->HWResolution[1] / 72;
+
+ printf("\033L%c%c", length >> 8, length);
+ printf("\033D%c", header->cupsBytesPerLine);
+
+ printf("\033%c", header->cupsCompression + 'c'); /* Darkness */
+ break;
+
+ case ZEBRA_EPL_LINE :
+ /*
+ * Set print rate...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
+ strcmp(choice->choice, "Default"))
+ printf("\033S%.0f", atof(choice->choice) * 2.0 - 2.0);
+
+ /*
+ * Set darkness...
+ */
+
+ if (header->cupsCompression > 0 && header->cupsCompression <= 100)
+ printf("\033D%d", 7 * header->cupsCompression / 100);
+
+ /*
+ * Set left margin to 0...
+ */
+
+ fputs("\033M01", stdout);
+
+ /*
+ * Start buffered output...
+ */
+
+ fputs("\033B", stdout);
+ break;
+
+ case ZEBRA_EPL_PAGE :
+ /*
+ * Start a new label...
+ */
+
+ puts("");
+ puts("N");
+
+ /*
+ * Set hardware options...
+ */
+
+ if (!strcmp(header->MediaType, "Direct"))
+ puts("OD");
+
+ /*
+ * Set print rate...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
+ strcmp(choice->choice, "Default"))
+ {
+ float val = atof(choice->choice);
+
+ if (val >= 3.0)
+ printf("S%.0f\n", val);
+ else
+ printf("S%.0f\n", val * 2.0 - 2.0);
+ }
+
+ /*
+ * Set darkness...
+ */
+
+ if (header->cupsCompression > 0 && header->cupsCompression <= 100)
+ printf("D%d\n", 15 * header->cupsCompression / 100);
+
+ /*
+ * Set label size...
+ */
+
+ printf("q%d\n", (header->cupsWidth + 7) & ~7);
+ break;
+
+ case ZEBRA_ZPL :
+ /*
+ * Set darkness...
+ */
+
+ if (header->cupsCompression > 0 && header->cupsCompression <= 100)
+ printf("~SD%02d\n", 30 * header->cupsCompression / 100);
+
+ /*
+ * Start bitmap graphics...
+ */
+
+ printf("~DGR:CUPS.GRF,%d,%d,\n",
+ header->cupsHeight * header->cupsBytesPerLine,
+ header->cupsBytesPerLine);
+
+ /*
+ * Allocate compression buffers...
+ */
+
+ CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
+ LastBuffer = malloc(header->cupsBytesPerLine);
+ LastSet = 0;
+ break;
+
+ case ZEBRA_CPCL :
+ /*
+ * Start label...
+ */
+
+ printf("! 0 %u %u %u %u\r\n", header->HWResolution[0],
+ header->HWResolution[1], header->cupsHeight,
+ header->NumCopies);
+ printf("PAGE-WIDTH %d\r\n", header->cupsWidth);
+ printf("PAGE-HEIGHT %d\r\n", header->cupsWidth);
+ break;
+
+ case INTELLITECH_PCL :
+ /*
+ * Set the media size...
+ */
+
+ printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */
+ printf("\033&l0O"); /* Set portrait orientation */
+
+ switch (header->PageSize[1])
+ {
+ case 540 : /* Monarch Envelope */
+ printf("\033&l80A"); /* Set page size */
+ break;
+
+ case 624 : /* DL Envelope */
+ printf("\033&l90A"); /* Set page size */
+ break;
+
+ case 649 : /* C5 Envelope */
+ printf("\033&l91A"); /* Set page size */
+ break;
+
+ case 684 : /* COM-10 Envelope */
+ printf("\033&l81A"); /* Set page size */
+ break;
+
+ case 756 : /* Executive */
+ printf("\033&l1A"); /* Set page size */
+ break;
+
+ case 792 : /* Letter */
+ printf("\033&l2A"); /* Set page size */
+ break;
+
+ case 842 : /* A4 */
+ printf("\033&l26A"); /* Set page size */
+ break;
+
+ case 1008 : /* Legal */
+ printf("\033&l3A"); /* Set page size */
+ break;
+
+ default : /* Custom size */
+ printf("\033!f%dZ", header->PageSize[1] * 300 / 72);
+ break;
+ }
+
+ printf("\033&l%dP", /* Set page length */
+ header->PageSize[1] / 12);
+ printf("\033&l0E"); /* Set top margin to 0 */
+ printf("\033&l%dX", header->NumCopies);
+ /* Set number copies */
+ printf("\033&l0L"); /* Turn off perforation skip */
+
+ /*
+ * Print settings...
+ */
+
+ if (Page == 1)
+ {
+ if (header->cupsRowFeed) /* inPrintRate */
+ printf("\033!p%dS", header->cupsRowFeed);
+
+ if (header->cupsCompression != ~0)
+ /* inPrintDensity */
+ printf("\033&d%dA", 30 * header->cupsCompression / 100 - 15);
+
+ if ((choice = ppdFindMarkedChoice(ppd, "inPrintMode")) != NULL)
+ {
+ if (!strcmp(choice->choice, "Standard"))
+ fputs("\033!p0M", stdout);
+ else if (!strcmp(choice->choice, "Tear"))
+ {
+ fputs("\033!p1M", stdout);
+
+ if (header->cupsRowCount) /* inTearInterval */
+ printf("\033!n%dT", header->cupsRowCount);
+ }
+ else
+ {
+ fputs("\033!p2M", stdout);
+
+ if (header->cupsRowStep) /* inCutInterval */
+ printf("\033!n%dC", header->cupsRowStep);
+ }
+ }
+ }
+
+ /*
+ * Setup graphics...
+ */
+
+ printf("\033*t%dR", header->HWResolution[0]);
+ /* Set resolution */
+
+ printf("\033*r%dS", header->cupsWidth);
+ /* Set width */
+ printf("\033*r%dT", header->cupsHeight);
+ /* Set height */
+
+ printf("\033&a0H"); /* Set horizontal position */
+ printf("\033&a0V"); /* Set vertical position */
+ printf("\033*r1A"); /* Start graphics */
+ printf("\033*b3M"); /* Set compression */
+
+ /*
+ * Allocate compression buffers...
+ */
+
+ CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
+ LastBuffer = malloc(header->cupsBytesPerLine);
+ LastSet = 0;
+ break;
+ }
+
+ /*
+ * Allocate memory for a line of graphics...
+ */
+
+ Buffer = malloc(header->cupsBytesPerLine);
+ Feed = 0;
+}
+
+
+/*
+ * 'EndPage()' - Finish a page of graphics.
+ */
+
+void
+EndPage(ppd_file_t *ppd, /* I - PPD file */
+ cups_page_header2_t *header) /* I - Page header */
+{
+ int val; /* Option value */
+ ppd_choice_t *choice; /* Marked choice */
+
+
+ switch (ModelNumber)
+ {
+ case DYMO_3x0 :
+ /*
+ * Eject the current page...
+ */
+
+ fputs("\033E", stdout);
+ break;
+
+ case ZEBRA_EPL_LINE :
+ /*
+ * End buffered output, eject the label...
+ */
+
+ fputs("\033E\014", stdout);
+ break;
+
+ case ZEBRA_EPL_PAGE :
+ /*
+ * Print the label...
+ */
+
+ puts("P1");
+ break;
+
+ case ZEBRA_ZPL :
+ if (Canceled)
+ {
+ /*
+ * Cancel bitmap download...
+ */
+
+ puts("~DN");
+ break;
+ }
+
+ /*
+ * Start label...
+ */
+
+ puts("^XA");
+
+ /*
+ * Set print rate...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
+ strcmp(choice->choice, "Default"))
+ {
+ val = atoi(choice->choice);
+ printf("^PR%d,%d,%d\n", val, val, val);
+ }
+
+ /*
+ * Put label home in default position (0,0)...
+ */
+
+ printf("^LH0,0\n");
+
+ /*
+ * Set media tracking...
+ */
+
+ if (ppdIsMarked(ppd, "zeMediaTracking", "Continuous"))
+ {
+ /*
+ * Add label length command for continuous...
+ */
+
+ printf("^LL%d\n", header->cupsHeight);
+ printf("^MNN\n");
+ }
+ else if (ppdIsMarked(ppd, "zeMediaTracking", "Web"))
+ printf("^MNY\n");
+ else if (ppdIsMarked(ppd, "zeMediaTracking", "Mark"))
+ printf("^MNM\n");
+
+ /*
+ * Set label top
+ */
+
+ if (header->cupsRowStep != 200)
+ printf("^LT%d\n", header->cupsRowStep);
+
+ /*
+ * Set media type...
+ */
+
+ if (!strcmp(header->MediaType, "Thermal"))
+ printf("^MTT\n");
+ else if (!strcmp(header->MediaType, "Direct"))
+ printf("^MTD\n");
+
+ /*
+ * Set print mode...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "zePrintMode")) != NULL &&
+ strcmp(choice->choice, "Saved"))
+ {
+ printf("^MM");
+
+ if (!strcmp(choice->choice, "Tear"))
+ printf("T,Y\n");
+ else if (!strcmp(choice->choice, "Peel"))
+ printf("P,Y\n");
+ else if (!strcmp(choice->choice, "Rewind"))
+ printf("R,Y\n");
+ else if (!strcmp(choice->choice, "Applicator"))
+ printf("A,Y\n");
+ else
+ printf("C,Y\n");
+ }
+
+ /*
+ * Set tear-off adjust position...
+ */
+
+ if (header->AdvanceDistance != 1000)
+ {
+ if ((int)header->AdvanceDistance < 0)
+ printf("~TA%04d\n", (int)header->AdvanceDistance);
+ else
+ printf("~TA%03d\n", (int)header->AdvanceDistance);
+ }
+
+ /*
+ * Allow for reprinting after an error...
+ */
+
+ if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
+ printf("^JZY\n");
+ else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
+ printf("^JZN\n");
+
+ /*
+ * Print multiple copies
+ */
+
+ if (header->NumCopies > 1)
+ printf("^PQ%d, 0, 0, N\n", header->NumCopies);
+
+ /*
+ * Display the label image...
+ */
+
+ puts("^FO0,0^XGR:CUPS.GRF,1,1^FS");
+
+ /*
+ * End the label and eject...
+ */
+
+ puts("^IDR:CUPS.GRF^FS");
+ puts("^XZ");
+ break;
+
+ case ZEBRA_CPCL :
+ /*
+ * Set tear-off adjust position...
+ */
+
+ if (header->AdvanceDistance != 1000)
+ printf("PRESENT-AT %d 1\r\n", (int)header->AdvanceDistance);
+
+ /*
+ * Allow for reprinting after an error...
+ */
+
+ if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
+ puts("ON-OUT-OF-PAPER WAIT\r");
+ else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
+ puts("ON-OUT-OF-PAPER PURGE\r");
+
+ /*
+ * Cut label?
+ */
+
+ if (header->CutMedia)
+ puts("CUT\r");
+
+ /*
+ * Set darkness...
+ */
+
+ if (header->cupsCompression > 0)
+ printf("TONE %u\r\n", 2 * header->cupsCompression);
+
+ /*
+ * Set print rate...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
+ strcmp(choice->choice, "Default"))
+ {
+ val = atoi(choice->choice);
+ printf("SPEED %d\r\n", val);
+ }
+
+ /*
+ * Print the label...
+ */
+
+ if ((choice = ppdFindMarkedChoice(ppd, "zeMediaTracking")) == NULL ||
+ strcmp(choice->choice, "Continuous"))
+ puts("FORM\r");
+
+ puts("PRINT\r");
+ break;
+
+ case INTELLITECH_PCL :
+ printf("\033*rB"); /* End GFX */
+ printf("\014"); /* Eject current page */
+ break;
+ }
+
+ fflush(stdout);
+
+ /*
+ * Free memory...
+ */
+
+ free(Buffer);
+
+ if (CompBuffer)
+ {
+ free(CompBuffer);
+ CompBuffer = NULL;
+ }
+
+ if (LastBuffer)
+ {
+ free(LastBuffer);
+ LastBuffer = NULL;
+ }
+}
+
+
+/*
+ * 'CancelJob()' - Cancel the current job...
+ */
+
+void
+CancelJob(int sig) /* I - Signal */
+{
+ /*
+ * Tell the main loop to stop...
+ */
+
+ (void)sig;
+
+ Canceled = 1;
+}
+
+
+/*
+ * 'OutputLine()' - Output a line of graphics...
+ */
+
+void
+OutputLine(ppd_file_t *ppd, /* I - PPD file */
+ cups_page_header2_t *header, /* I - Page header */
+ int y) /* I - Line number */
+{
+ int i; /* Looping var */
+ unsigned char *ptr; /* Pointer into buffer */
+ unsigned char *compptr; /* Pointer into compression buffer */
+ char repeat_char; /* Repeated character */
+ int repeat_count; /* Number of repeated characters */
+ static const char *hex = "0123456789ABCDEF";
+ /* Hex digits */
+
+
+ switch (ModelNumber)
+ {
+ case DYMO_3x0 :
+ /*
+ * See if the line is blank; if not, write it to the printer...
+ */
+
+ if (Buffer[0] ||
+ memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
+ {
+ if (Feed)
+ {
+ while (Feed > 255)
+ {
+ printf("\033f\001%c", 255);
+ Feed -= 255;
+ }
+
+ printf("\033f\001%c", Feed);
+ Feed = 0;
+ }
+
+ putchar(0x16);
+ fwrite(Buffer, header->cupsBytesPerLine, 1, stdout);
+ fflush(stdout);
+ }
+ else
+ Feed ++;
+ break;
+
+ case ZEBRA_EPL_LINE :
+ printf("\033g%03d", header->cupsBytesPerLine);
+ fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
+ fflush(stdout);
+ break;
+
+ case ZEBRA_EPL_PAGE :
+ if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
+ {
+ printf("GW0,%d,%d,1\n", y, header->cupsBytesPerLine);
+ for (i = header->cupsBytesPerLine, ptr = Buffer; i > 0; i --, ptr ++)
+ putchar(~*ptr);
+ putchar('\n');
+ fflush(stdout);
+ }
+ break;
+
+ case ZEBRA_ZPL :
+ /*
+ * Determine if this row is the same as the previous line.
+ * If so, output a ':' and return...
+ */
+
+ if (LastSet)
+ {
+ if (!memcmp(Buffer, LastBuffer, header->cupsBytesPerLine))
+ {
+ putchar(':');
+ return;
+ }
+ }
+
+ /*
+ * Convert the line to hex digits...
+ */
+
+ for (ptr = Buffer, compptr = CompBuffer, i = header->cupsBytesPerLine;
+ i > 0;
+ i --, ptr ++)
+ {
+ *compptr++ = hex[*ptr >> 4];
+ *compptr++ = hex[*ptr & 15];
+ }
+
+ *compptr = '\0';
+
+ /*
+ * Run-length compress the graphics...
+ */
+
+ for (compptr = CompBuffer + 1, repeat_char = CompBuffer[0], repeat_count = 1;
+ *compptr;
+ compptr ++)
+ if (*compptr == repeat_char)
+ repeat_count ++;
+ else
+ {
+ ZPLCompress(repeat_char, repeat_count);
+ repeat_char = *compptr;
+ repeat_count = 1;
+ }
+
+ if (repeat_char == '0')
+ {
+ /*
+ * Handle 0's on the end of the line...
+ */
+
+ if (repeat_count & 1)
+ {
+ repeat_count --;
+ putchar('0');
+ }
+
+ if (repeat_count > 0)
+ putchar(',');
+ }
+ else
+ ZPLCompress(repeat_char, repeat_count);
+
+ fflush(stdout);
+
+ /*
+ * Save this line for the next round...
+ */
+
+ memcpy(LastBuffer, Buffer, header->cupsBytesPerLine);
+ LastSet = 1;
+ break;
+
+ case ZEBRA_CPCL :
+ if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
+ {
+ printf("CG %u 1 0 %d ", header->cupsBytesPerLine, y);
+ fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
+ puts("\r");
+ fflush(stdout);
+ }
+ break;
+
+ case INTELLITECH_PCL :
+ if (Buffer[0] ||
+ memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
+ {
+ if (Feed)
+ {
+ printf("\033*b%dY", Feed);
+ Feed = 0;
+ LastSet = 0;
+ }
+
+ PCLCompress(Buffer, header->cupsBytesPerLine);
+ }
+ else
+ Feed ++;
+ break;
+ }
+}
+
+
+/*
+ * 'PCLCompress()' - Output a PCL (mode 3) compressed line.
+ */
+
+void
+PCLCompress(unsigned char *line, /* I - Line to compress */
+ int length) /* I - Length of line */
+{
+ unsigned char *line_ptr, /* Current byte pointer */
+ *line_end, /* End-of-line byte pointer */
+ *comp_ptr, /* Pointer into compression buffer */
+ *start, /* Start of compression sequence */
+ *seed; /* Seed buffer pointer */
+ int count, /* Count of bytes for output */
+ offset; /* Offset of bytes for output */
+
+
+ /*
+ * Do delta-row compression...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+
+ comp_ptr = CompBuffer;
+ seed = LastBuffer;
+
+ while (line_ptr < line_end)
+ {
+ /*
+ * Find the next non-matching sequence...
+ */
+
+ start = line_ptr;
+
+ if (!LastSet)
+ {
+ /*
+ * The seed buffer is invalid, so do the next 8 bytes, max...
+ */
+
+ offset = 0;
+
+ if ((count = line_end - line_ptr) > 8)
+ count = 8;
+
+ line_ptr += count;
+ }
+ else
+ {
+ /*
+ * The seed buffer is valid, so compare against it...
+ */
+
+ while (*line_ptr == *seed &&
+ line_ptr < line_end)
+ {
+ line_ptr ++;
+ seed ++;
+ }
+
+ if (line_ptr == line_end)
+ break;
+
+ offset = line_ptr - start;
+
+ /*
+ * Find up to 8 non-matching bytes...
+ */
+
+ start = line_ptr;
+ count = 0;
+ while (*line_ptr != *seed &&
+ line_ptr < line_end &&
+ count < 8)
+ {
+ line_ptr ++;
+ seed ++;
+ count ++;
+ }
+ }
+
+ /*
+ * Place mode 3 compression data in the buffer; see HP manuals
+ * for details...
+ */
+
+ if (offset >= 31)
+ {
+ /*
+ * Output multi-byte offset...
+ */
+
+ *comp_ptr++ = ((count - 1) << 5) | 31;
+
+ offset -= 31;
+ while (offset >= 255)
+ {
+ *comp_ptr++ = 255;
+ offset -= 255;
+ }
+
+ *comp_ptr++ = offset;
+ }
+ else
+ {
+ /*
+ * Output single-byte offset...
+ */
+
+ *comp_ptr++ = ((count - 1) << 5) | offset;
+ }
+
+ memcpy(comp_ptr, start, count);
+ comp_ptr += count;
+ }
+
+ /*
+ * Set the length of the data and write it...
+ */
+
+ printf("\033*b%dW", (int)(comp_ptr - CompBuffer));
+ fwrite(CompBuffer, comp_ptr - CompBuffer, 1, stdout);
+
+ /*
+ * Save this line as a "seed" buffer for the next...
+ */
+
+ memcpy(LastBuffer, line, length);
+ LastSet = 1;
+}
+
+
+/*
+ * 'ZPLCompress()' - Output a run-length compression sequence.
+ */
+
+void
+ZPLCompress(char repeat_char, /* I - Character to repeat */
+ int repeat_count) /* I - Number of repeated characters */
+{
+ if (repeat_count > 1)
+ {
+ /*
+ * Print as many z's as possible - they are the largest denomination
+ * representing 400 characters (zC stands for 400 adjacent C's)
+ */
+
+ while (repeat_count >= 400)
+ {
+ putchar('z');
+ repeat_count -= 400;
+ }
+
+ /*
+ * Then print 'g' through 'y' as multiples of 20 characters...
+ */
+
+ if (repeat_count >= 20)
+ {
+ putchar('f' + repeat_count / 20);
+ repeat_count %= 20;
+ }
+
+ /*
+ * Finally, print 'G' through 'Y' as 1 through 19 characters...
+ */
+
+ if (repeat_count > 0)
+ putchar('F' + repeat_count);
+ }
+
+ /*
+ * Then the character to be repeated...
+ */
+
+ putchar(repeat_char);
+}
+
+
+/*
+ * 'main()' - Main entry and processing of driver.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fd; /* File descriptor */
+ cups_raster_t *ras; /* Raster stream for printing */
+ cups_page_header2_t header; /* Page header from file */
+ int y; /* Current line */
+ ppd_file_t *ppd; /* PPD file */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc < 6 || argc > 7)
+ {
+ /*
+ * We don't have the correct number of arguments; write an error message
+ * and return.
+ */
+
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("%s job-id user title copies options [file]"),
+ "rastertolabel");
+ return (1);
+ }
+
+ /*
+ * Open the page stream...
+ */
+
+ if (argc == 7)
+ {
+ if ((fd = open(argv[6], O_RDONLY)) == -1)
+ {
+ _cupsLangPrintError("ERROR", _("Unable to open raster file"));
+ sleep(1);
+ return (1);
+ }
+ }
+ else
+ fd = 0;
+
+ ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
+
+ /*
+ * Register a signal handler to eject the current page if the
+ * job is cancelled.
+ */
+
+ Canceled = 0;
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, CancelJob);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = CancelJob;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, CancelJob);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Open the PPD file and apply options...
+ */
+
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ ppd = ppdOpenFile(getenv("PPD"));
+ if (!ppd)
+ {
+ ppd_status_t status; /* PPD error */
+ int linenum; /* Line number */
+
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("The PPD file could not be opened."));
+
+ status = ppdLastError(&linenum);
+
+ fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
+
+ return (1);
+ }
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ /*
+ * Initialize the print device...
+ */
+
+ Setup(ppd);
+
+ /*
+ * Process pages as needed...
+ */
+
+ Page = 0;
+
+ while (cupsRasterReadHeader2(ras, &header))
+ {
+ /*
+ * Write a status message with the page number and number of copies.
+ */
+
+ if (Canceled)
+ break;
+
+ Page ++;
+
+ fprintf(stderr, "PAGE: %d 1\n", Page);
+ _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page);
+
+ /*
+ * Start the page...
+ */
+
+ StartPage(ppd, &header);
+
+ /*
+ * Loop for each line on the page...
+ */
+
+ for (y = 0; y < header.cupsHeight && !Canceled; y ++)
+ {
+ /*
+ * Let the user know how far we have progressed...
+ */
+
+ if (Canceled)
+ break;
+
+ if ((y & 15) == 0)
+ {
+ _cupsLangPrintFilter(stderr, "INFO",
+ _("Printing page %d, %d%% complete."),
+ Page, 100 * y / header.cupsHeight);
+ fprintf(stderr, "ATTR: job-media-progress=%d\n",
+ 100 * y / header.cupsHeight);
+ }
+
+ /*
+ * Read a line of graphics...
+ */
+
+ if (cupsRasterReadPixels(ras, Buffer, header.cupsBytesPerLine) < 1)
+ break;
+
+ /*
+ * Write it to the printer...
+ */
+
+ OutputLine(ppd, &header, y);
+ }
+
+ /*
+ * Eject the page...
+ */
+
+ _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page);
+
+ EndPage(ppd, &header);
+
+ if (Canceled)
+ break;
+ }
+
+ /*
+ * Close the raster stream...
+ */
+
+ cupsRasterClose(ras);
+ if (fd != 0)
+ close(fd);
+
+ /*
+ * Close the PPD file and free the options...
+ */
+
+ ppdClose(ppd);
+ cupsFreeOptions(num_options, options);
+
+ /*
+ * If no pages were printed, send an error message...
+ */
+
+ if (Page == 0)
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
+ return (1);
+ }
+ else
+ return (0);
+}
+
+
+/*
+ * End of "$Id: rastertolabel.c 11756 2014-03-27 17:06:25Z msweet $".
+ */
diff --git a/cups/libs/filter/rastertopwg.c b/cups/libs/filter/rastertopwg.c
new file mode 100644
index 000000000..97dab1cbc
--- /dev/null
+++ b/cups/libs/filter/rastertopwg.c
@@ -0,0 +1,461 @@
+/*
+ * "$Id: rastertopwg.c 3427 2011-09-20 18:40:57Z msweet $"
+ *
+ * CUPS raster to PWG raster format filter for CUPS.
+ *
+ * Copyright 2011 Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright law.
+ * Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Main entry for filter.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups-private.h>
+#include <cups/raster.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+/*
+ * 'main()' - Main entry for filter.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fd; /* Raster file */
+ cups_raster_t *inras, /* Input raster stream */
+ *outras; /* Output raster stream */
+ cups_page_header2_t inheader, /* Input raster page header */
+ outheader; /* Output raster page header */
+ int y; /* Current line */
+ unsigned char *line; /* Line buffer */
+ int page = 0, /* Current page */
+ page_width, /* Actual page width */
+ page_height, /* Actual page height */
+ page_top, /* Top margin */
+ page_bottom, /* Bottom margin */
+ page_left, /* Left margin */
+ linesize, /* Bytes per line */
+ lineoffset; /* Offset into line */
+ unsigned char white; /* White pixel */
+ ppd_file_t *ppd; /* PPD file */
+ ppd_attr_t *back; /* cupsBackSize attribute */
+ _ppd_cache_t *cache; /* PPD cache */
+ _pwg_size_t *pwg_size; /* PWG media size */
+ _pwg_media_t *pwg_media; /* PWG media name */
+ int num_options; /* Number of options */
+ cups_option_t *options = NULL;/* Options */
+ const char *val; /* Option value */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ puts("Usage: rastertopwg job user title copies options [filename]");
+ return (1);
+ }
+ else if (argc == 7)
+ {
+ if ((fd = open(argv[6], O_RDONLY)) < 0)
+ {
+ perror("ERROR: Unable to open print file");
+ return (1);
+ }
+ }
+ else
+ fd = 0;
+
+ inras = cupsRasterOpen(fd, CUPS_RASTER_READ);
+ outras = cupsRasterOpen(1, CUPS_RASTER_WRITE_PWG);
+
+ ppd = ppdOpenFile(getenv("PPD"));
+ back = ppdFindAttr(ppd, "cupsBackSide", NULL);
+
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ cache = ppd ? ppd->cache : NULL;
+
+ while (cupsRasterReadHeader2(inras, &inheader))
+ {
+ /*
+ * Compute the real raster size...
+ */
+
+ page ++;
+
+ fprintf(stderr, "PAGE: %d %d\n", page, inheader.NumCopies);
+
+ page_width = (int)(inheader.cupsPageSize[0] * inheader.HWResolution[0] /
+ 72.0);
+ page_height = (int)(inheader.cupsPageSize[1] * inheader.HWResolution[1] /
+ 72.0);
+ page_left = (int)(inheader.cupsImagingBBox[0] *
+ inheader.HWResolution[0] / 72.0);
+ page_bottom = (int)(inheader.cupsImagingBBox[1] *
+ inheader.HWResolution[1] / 72.0);
+ page_top = page_height - page_bottom - inheader.cupsHeight;
+ linesize = (page_width * inheader.cupsBitsPerPixel + 7) / 8;
+ lineoffset = page_left * inheader.cupsBitsPerPixel / 8; /* Round down */
+
+ switch (inheader.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_SW :
+ case CUPS_CSPACE_SRGB :
+ case CUPS_CSPACE_ADOBERGB :
+ white = 255;
+ break;
+
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_DEVICE1 :
+ case CUPS_CSPACE_DEVICE2 :
+ case CUPS_CSPACE_DEVICE3 :
+ case CUPS_CSPACE_DEVICE4 :
+ case CUPS_CSPACE_DEVICE5 :
+ case CUPS_CSPACE_DEVICE6 :
+ case CUPS_CSPACE_DEVICE7 :
+ case CUPS_CSPACE_DEVICE8 :
+ case CUPS_CSPACE_DEVICE9 :
+ case CUPS_CSPACE_DEVICEA :
+ case CUPS_CSPACE_DEVICEB :
+ case CUPS_CSPACE_DEVICEC :
+ case CUPS_CSPACE_DEVICED :
+ case CUPS_CSPACE_DEVICEE :
+ case CUPS_CSPACE_DEVICEF :
+ white = 0;
+ break;
+
+ default :
+ _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
+ fprintf(stderr, "DEBUG: Unsupported cupsColorSpace %d on page %d.\n",
+ inheader.cupsColorSpace, page);
+ return (1);
+ }
+
+ if (inheader.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
+ fprintf(stderr, "DEBUG: Unsupported cupsColorOrder %d on page %d.\n",
+ inheader.cupsColorOrder, page);
+ return (1);
+ }
+
+ if (inheader.cupsBitsPerPixel != 1 &&
+ inheader.cupsBitsPerColor != 8 && inheader.cupsBitsPerColor != 16)
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
+ fprintf(stderr, "DEBUG: Unsupported cupsBitsPerColor %d on page %d.\n",
+ inheader.cupsBitsPerColor, page);
+ return (1);
+ }
+
+ memcpy(&outheader, &inheader, sizeof(outheader));
+ outheader.cupsWidth = page_width;
+ outheader.cupsHeight = page_height;
+ outheader.cupsBytesPerLine = linesize;
+
+ outheader.cupsInteger[14] = 0; /* VendorIdentifier */
+ outheader.cupsInteger[15] = 0; /* VendorLength */
+
+ if ((val = cupsGetOption("print-content-optimize", num_options,
+ options)) != NULL)
+ {
+ if (!strcmp(val, "automatic"))
+ strlcpy(outheader.OutputType, "Automatic",
+ sizeof(outheader.OutputType));
+ else if (!strcmp(val, "graphics"))
+ strlcpy(outheader.OutputType, "Graphics", sizeof(outheader.OutputType));
+ else if (!strcmp(val, "photo"))
+ strlcpy(outheader.OutputType, "Photo", sizeof(outheader.OutputType));
+ else if (!strcmp(val, "text"))
+ strlcpy(outheader.OutputType, "Text", sizeof(outheader.OutputType));
+ else if (!strcmp(val, "text-and-graphics"))
+ strlcpy(outheader.OutputType, "TextAndGraphics",
+ sizeof(outheader.OutputType));
+ else
+ {
+ fprintf(stderr, "DEBUG: Unsupported print-content-type \"%s\".\n", val);
+ outheader.OutputType[0] = '\0';
+ }
+ }
+
+ if ((val = cupsGetOption("print-quality", num_options, options)) != NULL)
+ {
+ int quality = atoi(val); /* print-quality value */
+
+ if (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH)
+ outheader.cupsInteger[8] = quality;
+ else
+ {
+ fprintf(stderr, "DEBUG: Unsupported print-quality %d.\n", quality);
+ outheader.cupsInteger[8] = 0;
+ }
+ }
+
+ if ((val = cupsGetOption("print-rendering-intent", num_options,
+ options)) != NULL)
+ {
+ if (!strcmp(val, "absolute"))
+ strlcpy(outheader.cupsRenderingIntent, "Absolute",
+ sizeof(outheader.cupsRenderingIntent));
+ else if (!strcmp(val, "automatic"))
+ strlcpy(outheader.cupsRenderingIntent, "Automatic",
+ sizeof(outheader.cupsRenderingIntent));
+ else if (!strcmp(val, "perceptual"))
+ strlcpy(outheader.cupsRenderingIntent, "Perceptual",
+ sizeof(outheader.cupsRenderingIntent));
+ else if (!strcmp(val, "relative"))
+ strlcpy(outheader.cupsRenderingIntent, "Relative",
+ sizeof(outheader.cupsRenderingIntent));
+ else if (!strcmp(val, "relative-bpc"))
+ strlcpy(outheader.cupsRenderingIntent, "RelativeBpc",
+ sizeof(outheader.cupsRenderingIntent));
+ else if (!strcmp(val, "saturation"))
+ strlcpy(outheader.cupsRenderingIntent, "Saturation",
+ sizeof(outheader.cupsRenderingIntent));
+ else
+ {
+ fprintf(stderr, "DEBUG: Unsupported print-rendering-intent \"%s\".\n",
+ val);
+ outheader.cupsRenderingIntent[0] = '\0';
+ }
+ }
+
+ if (inheader.cupsPageSizeName[0] &&
+ (pwg_size = _ppdCacheGetSize(cache, inheader.cupsPageSizeName)) != NULL)
+ {
+ strlcpy(outheader.cupsPageSizeName, pwg_size->map.pwg,
+ sizeof(outheader.cupsPageSizeName));
+ }
+ else
+ {
+ pwg_media = _pwgMediaForSize((int)(2540.0 * inheader.cupsPageSize[0] /
+ 72.0),
+ (int)(2540.0 * inheader.cupsPageSize[1] /
+ 72.0));
+
+ if (pwg_media)
+ strlcpy(outheader.cupsPageSizeName, pwg_media->pwg,
+ sizeof(outheader.cupsPageSizeName));
+ else
+ {
+ fprintf(stderr, "DEBUG: Unsupported PageSize %.2fx%.2f.\n",
+ inheader.cupsPageSize[0], inheader.cupsPageSize[1]);
+ outheader.cupsPageSizeName[0] = '\0';
+ }
+ }
+
+ if (inheader.Duplex && !(page & 1) &&
+ back && _cups_strcasecmp(back->value, "Normal"))
+ {
+ if (_cups_strcasecmp(back->value, "Flipped"))
+ {
+ if (inheader.Tumble)
+ {
+ outheader.cupsInteger[1] = -1;/* CrossFeedTransform */
+ outheader.cupsInteger[2] = 1; /* FeedTransform */
+
+ outheader.cupsInteger[3] = page_width - page_left -
+ inheader.cupsWidth;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_top;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_width - page_left;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_bottom;
+ /* ImageBoxBottom */
+ }
+ else
+ {
+ outheader.cupsInteger[1] = 1; /* CrossFeedTransform */
+ outheader.cupsInteger[2] = -1;/* FeedTransform */
+
+ outheader.cupsInteger[3] = page_left;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_bottom;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_top;
+ /* ImageBoxBottom */
+ }
+ }
+ else if (_cups_strcasecmp(back->value, "ManualTumble"))
+ {
+ if (inheader.Tumble)
+ {
+ outheader.cupsInteger[1] = -1;/* CrossFeedTransform */
+ outheader.cupsInteger[2] = -1;/* FeedTransform */
+
+ outheader.cupsInteger[3] = page_width - page_left -
+ inheader.cupsWidth;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_bottom;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_width - page_left;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_top;
+ /* ImageBoxBottom */
+ }
+ else
+ {
+ outheader.cupsInteger[1] = 1; /* CrossFeedTransform */
+ outheader.cupsInteger[2] = 1; /* FeedTransform */
+
+ outheader.cupsInteger[3] = page_left;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_top;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_bottom;
+ /* ImageBoxBottom */
+ }
+ }
+ else if (_cups_strcasecmp(back->value, "Rotated"))
+ {
+ if (inheader.Tumble)
+ {
+ outheader.cupsInteger[1] = -1;/* CrossFeedTransform */
+ outheader.cupsInteger[2] = -1;/* FeedTransform */
+
+ outheader.cupsInteger[3] = page_width - page_left -
+ inheader.cupsWidth;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_bottom;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_width - page_left;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_top;
+ /* ImageBoxBottom */
+ }
+ else
+ {
+ outheader.cupsInteger[1] = 1; /* CrossFeedTransform */
+ outheader.cupsInteger[2] = 1; /* FeedTransform */
+
+ outheader.cupsInteger[3] = page_left;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_top;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_bottom;
+ /* ImageBoxBottom */
+ }
+ }
+ else
+ {
+ /*
+ * Unsupported value...
+ */
+
+ fprintf(stderr, "DEBUG: Unsupported cupsBackSide \"%s\".\n", back->value);
+
+ outheader.cupsInteger[1] = 1; /* CrossFeedTransform */
+ outheader.cupsInteger[2] = 1; /* FeedTransform */
+
+ outheader.cupsInteger[3] = page_left;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_top;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_bottom;
+ /* ImageBoxBottom */
+ }
+ }
+ else
+ {
+ outheader.cupsInteger[1] = 1; /* CrossFeedTransform */
+ outheader.cupsInteger[2] = 1; /* FeedTransform */
+
+ outheader.cupsInteger[3] = page_left;
+ /* ImageBoxLeft */
+ outheader.cupsInteger[4] = page_top;
+ /* ImageBoxTop */
+ outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
+ /* ImageBoxRight */
+ outheader.cupsInteger[6] = page_height - page_bottom;
+ /* ImageBoxBottom */
+ }
+
+ if (!cupsRasterWriteHeader2(outras, &outheader))
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
+ fprintf(stderr, "DEBUG: Unable to write header for page %d.\n", page);
+ return (1);
+ }
+
+ /*
+ * Copy raster data...
+ */
+
+ line = malloc(linesize);
+
+ memset(line, white, linesize);
+ for (y = page_top; y > 0; y --)
+ if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
+ fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
+ page_top - y + 1, page);
+ return (1);
+ }
+
+ for (y = inheader.cupsHeight; y > 0; y --)
+ {
+ cupsRasterReadPixels(inras, line + lineoffset, inheader.cupsBytesPerLine);
+ if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
+ fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
+ inheader.cupsHeight - y + page_top + 1, page);
+ return (1);
+ }
+ }
+
+ memset(line, white, linesize);
+ for (y = page_bottom; y > 0; y --)
+ if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
+ {
+ _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
+ fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
+ page_bottom - y + page_top + inheader.cupsHeight + 1, page);
+ return (1);
+ }
+
+ free(line);
+ }
+
+ cupsRasterClose(inras);
+ if (fd)
+ close(fd);
+
+ cupsRasterClose(outras);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: rastertopwg.c 3427 2011-09-20 18:40:57Z msweet $".
+ */
diff --git a/cups/libs/filter/spec-ppd.header b/cups/libs/filter/spec-ppd.header
new file mode 100644
index 000000000..40433aa55
--- /dev/null
+++ b/cups/libs/filter/spec-ppd.header
@@ -0,0 +1,32 @@
+<!--
+ "$Id$"
+
+ PPD extension documentation for CUPS.
+
+ Copyright 2007-2011 by Apple Inc.
+ Copyright 1997-2007 by Easy Software Products.
+
+ These coded instructions, statements, and computer programs are the
+ property of Apple Inc. and are protected by Federal copyright
+ law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ which should have been included with this file. If this file is
+ file is missing or damaged, see the license at "http://www.cups.org/".
+-->
+
+<H1 CLASS="title">CUPS PPD Extensions</H1>
+
+<p>This specification describes the attributes and extensions that CUPS adds to <a href="http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf" target="_blank">Adobe TechNote #5003: PostScript Printer Description File Format Specification Version 4.3</a>. PostScript Printer Description ("PPD") files describe the capabilities of each printer and are used by CUPS to support printer-specific features and intelligent filtering.</p>
+
+<div class='summary'><table summary='General Information'>
+<tbody>
+<tr>
+ <th>See Also</th>
+ <td>Programming: <a href='postscript-driver.html'>Developing PostScript Printer Drivers</a><br>
+ Programming: <a href='raster-driver.html'>Developing Raster Printer Drivers</a><br>
+ Programming: <a href='api-filter.html'>Filter and Backend Programming</a><br>
+ Programming: <a href='ppd-compiler.html'>Introduction to the PPD Compiler</a><br>
+ Programming: <a href='api-raster.html'>Raster API</a><br>
+ References: <a href='ref-ppdcfile.html'>PPD Compiler Driver Information File Reference</a></td>
+</tr>
+</tbody>
+</table></div>
diff --git a/cups/libs/filter/spec-ppd.shtml b/cups/libs/filter/spec-ppd.shtml
new file mode 100644
index 000000000..33c2d5791
--- /dev/null
+++ b/cups/libs/filter/spec-ppd.shtml
@@ -0,0 +1,2026 @@
+<h2 class='title'><a name='SYNTAX'>PPD File Syntax</a></h2>
+
+<p>The PPD format is text-based and uses lines of up to 255 characters terminated by a carriage return, linefeed, or combination of carriage return and line feed. The following ABNF definition [<a href="http://tools.ietf.org/html/rfc5234" target="_blank">RFC5234</a>] defines the general format of lines in a PPD file:</p>
+
+<pre class='command'>
+PPD-FILE = HEADER +(DATA / COMMENT / LINE-END)
+
+HEADER = "*PPD-Adobe:" *WSP DQUOTE VERSION DQUOTE LINE-END
+
+VERSION = "4.0" / "4.1" / "4.2" / "4.3"
+
+COMMENT = "*%" *TCHAR LINE-END
+
+DATA = "*" 1*KCHAR [ WSP 1*KCHAR [ "/" 1*TCHAR ] ] ":"
+ 1*(*WSP VALUE) LINE-END
+
+VALUE = 1*TCHAR / DQUOTE 1*SCHAR DQUOTE
+
+KCHAR = ALPHA / DIGIT / "_" / "." / "-"
+
+SCHAR = LINE-END / WSP / %x21.23-7E.A0-FF
+
+TCHAR = %x20-7E.A0-FF
+
+LINE-END = CR / LF / CR LF
+</pre>
+
+
+<h2 class='title'><a name='AUTOCONFIG'>Auto-Configuration</a></h2>
+
+<p>CUPS supports several methods of auto-configuration via PPD keywords.</p>
+
+<h3><span class='info'>OS X 10.5</span><a name='APAutoSetupTool'>APAutoSetupTool</a></h3>
+
+<p class='summary'>*APAutoSetupTool: "/LibraryPrinters/vendor/filename"</p>
+
+<p>This OS X keyword defines a program that sets the default option choices. It is run when a printer is added from the <var>Add Printer</var> window or the <var>Nearby Printers</var> list in the <var>Print</var> dialog.</p>
+
+<p>The program is provided with two arguments: the printer's device URI and the PPD file to be used for the printer. The program must write an updated PPD file to stdout.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Use our setup tool when adding a printer
+*APAutoSetupTool: "/Library/Printers/vendor/Tools/autosetuptool"
+</pre>
+
+<h3><span class='info'>OS X 10.2/CUPS 1.4</span><a name='QUERYKEYWORD'>?MainKeyword</a></h3>
+
+<p class='summary'>*?<i>MainKeyword</i>: "<br>
+ PostScript query code that writes a message using the = operator...<br>
+"<br>
+*End</p>
+
+<p>The <tt>?<i>MainKeyword</i></tt> keyword defines PostScript code that determines the currently selected/enabled option keyword (choice) for the main keyword (option). It is typically used when communicating with USB, serial, Appletalk, and AppSocket (port 9100) printers.</p>
+
+<p>The PostScript code typically sends its response back using the <tt>=</tt> operator.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+*OpenUI OptionDuplex/Duplexer Installed: Boolean
+*DuplexOptionDuplex: False
+*OptionDuplex False/Not Installed: ""
+*OptionDuplex True/Installed: ""
+
+<em>*% Query the printer for the presence of the duplexer option...</em>
+*?OptionDuplex: "
+ currentpagedevice /Duplex known
+ {(True)} {(False)} ifelse
+ = flush
+"
+*End
+*CloseUI: OptionDuplex
+</pre>
+
+<h3><span class='info'>OS X 10.4/CUPS 1.5</span><a name='OID'>OIDMainKeyword</a></h3>
+
+<p class='summary'>*?OID<i>MainKeyword</i>: ".n.n.n..."<br>
+*OID<i>MainKeyword</i> <i>OptionKeyword1</i>: "value"<br>
+...<br>
+*OID<i>MainKeyword</i> <i>OptionKeywordN</i>: "value"</p>
+
+<p>The <tt>OID<i>MainKeyword</i></tt> keyword is used to define SNMP OIDs that map to installable options. The first (query) line defines the OID to lookup on the network device. The second and subsequent keywords define a mapping from OID value to option keyword. Since SNMP is an IP-based network protocol, this method is typically only used to configure AppSocket, IPP, and LPD network printers.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Get the installed memory on the printer...
+*?OIDInstalledMemory: ".1.3.6.1.2.1.25.2.2.0"
+*OIDInstalledMemory 16MB: "16384 KBytes"
+*OIDInstalledMemory 32MB: "32768 KBytes"
+*OIDInstalledMemory 48MB: "49152 KBytes"
+*OIDInstalledMemory 72MB: "73728 KBytes"
+</pre>
+
+
+<h2 class='title'><a name='PROFILES'>Color Profiles</a></h2>
+
+<p>CUPS supports three types of color profiles. The first type is based on sRGB and is used by the standard CUPS raster filters and GPL Ghostscript. The second type is based on ICC profiles and is used by the Quartz-based filters on MacOS X. The final type is based on well-known colorspaces such as sRGB and Adobe RGB.</p>
+
+<blockquote><b>Note:</b>
+
+<p>At this time, none of the CUPS raster filters support ICC profiles. This will be addressed as time and resources permit.</p>
+
+</blockquote>
+
+<h3><span class='info'>Deprecated</span><a name='cupsColorProfile'>cupsColorProfile</a></h3>
+
+<p class='summary'>*cupsColorProfile Resolution/MediaType: "density gamma m00 m01 m02 m10 m11 m12 m20 m21 m22"</p>
+
+<p>This string keyword specifies an sRGB-based color profile consisting of gamma and density controls and a 3x3 CMY color transform matrix. <em>This keyword is not supported on OS X.</em></p>
+
+<p>The <i>Resolution</i> and <i>MediaType</i> values may be "-" to act as a wildcard. Otherwise they must match one of the <tt>Resolution</tt> or <tt>MediaType</tt> option keywords defined in the PPD file.</p>
+
+<p>The <i>density</i> and <i>gamma</i> values define gamma and
+density adjustment function such that:</p>
+
+<pre class='command'>
+f(x) = density * x <sup style='font-size: 100%'>gamma</sup>
+</pre>
+
+<p>The <i>m00</i> through <i>m22</i> values define a 3x3 transformation matrix for the CMY color values. The density function is applied <i>after</i> the CMY transformation:</p>
+
+<pre class='command'>
+| m00 m01 m02 |
+| m10 m11 m12 |
+| m20 m21 m22 |
+</pre>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Specify a profile for printing at 360dpi on all media types</em>
+*cupsColorProfile 360dpi/-: "1.0 1.5 1.0 0.0 -0.2 -0.4 1.0 0.0 -0.2 0.0 1.0"
+
+<em>*% Specify a profile for printing at 720dpi on Glossy media</em>
+*cupsColorProfile 720dpi/Glossy: "1.0 2.5 1.0 0.0 -0.2 -0.4 1.0 0.0 -0.2 0.0 1.0"
+
+<em>*% Specify a default profile for printing at all other resolutions and media types</em>
+*cupsColorProfile -/-: "0.9 2.0 1.0 0.0 -0.2 -0.4 1.0 0.0 -0.2 0.0 1.0"
+</pre>
+
+
+<h3><span class='info'>OS X 10.3/CUPS 1.2</span><a name='cupsICCProfile'>cupsICCProfile</a></h3>
+
+<p class='summary'>*cupsICCProfile ColorModel.MediaType.Resolution/Description: "filename"</p>
+
+<p>This keyword specifies an ICC color profile that is used to convert the document colors to the device colorspace. The <tt>ColorModel</tt>, <tt>MediaType</tt>, and <tt>Resolution</tt> option keywords specify a selector for color profiles. If omitted, the color profile will match any option keyword for the corresponding main keyword.</p>
+
+<p>The <tt>Description</tt> specifies human-readable text that is associated with the color profile. The <tt>filename</tt> portion specifies the ICC color profile to use; if the filename is not absolute, it is loaded relative to the <var>/usr/share/cups/profiles</var> directory.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Specify a profile for CMYK printing at 360dpi on all media types</em>
+*cupsICCProfile CMYK..360dpi/360dpi CMYK: "/Library/Printers/vendor/Profiles/foo-360-cmyk.icc"
+
+<em>*% Specify a profile for RGB printing at 720dpi on Glossy media</em>
+*cupsColorProfile RGB.Glossy.720dpi/720dpi Glossy: "/Library/Printers/vendor/Profiles/foo-720-glossy-rgb.icc"
+
+<em>*% Specify a default profile for printing at all other resolutions and media types</em>
+*cupsICCProfile ../Default: "/Library/Printers/vendor/Profiles/foo-default.icc"
+</pre>
+
+<h4>Customizing the Profile Selection Keywords</h4>
+
+<p>The <tt>ColorModel</tt>, <tt>MediaType</tt>, and <tt>Resolution</tt> main keywords can be reassigned to different main keywords, allowing drivers to do color profile selection based on different parameters. The <tt>cupsICCQualifier1</tt>, <tt>cupsICCQualifier2</tt>, and <tt>cupsICCQualifier3</tt> keywords define the mapping from selector to main keyword:</p>
+
+<pre class='command'>
+*cupsICCQualifier1: MainKeyword1
+*cupsICCQualifier2: MainKeyword2
+*cupsICCQualifier3: MainKeyword3
+</pre>
+
+<p>The default mapping is as follows:</p>
+
+<pre class='command'>
+*cupsICCQualifier1: ColorModel
+*cupsICCQualifier2: MediaType
+*cupsICCQualifier3: Resolution
+</pre>
+
+<h3><span class='info'>OS X 10.4</span><a name='APCustom'>Custom Color Matching Support</a></h3>
+
+<p class='summary'>*<a href='#APSupportsCustomColorMatching'>APSupportsCustomColorMatching</a>: true<br>
+*<a href='#APCustomColorMatchingName'>APCustomColorMatchingName</a> name/text: ""<br>
+*<a href='#APCustomColorMatchingProfile'>APCustomColorMatchingProfile</a>: profile<br>
+*<a href='#APDefaultCustomColorMatchingProfile'>APDefaultCustomColorMatchingProfile</a>: profile</p>
+
+<p>These keywords tell the OS X raster filters that the printer driver provides its own custom color matching and that generic color profiles should be used when generating 1-, 3-, and 4-component raster data as requested by the driver. The <tt>APCustomColorMatchingProfile</tt> and <tt>APDefaultColorMatchingProfile</tt> keywords specify alternate color profiles (sRGB or AdobeRGB) to use for 3-color (RGB) raster data.</p>
+
+<blockquote><b>Note:</b>
+
+<p>Prior to OS X 10.6, the default RGB color space was Apple's "GenericRGB". The new default in OS X 10.6 and later is "sRGB". For more information, see <a href="http://support.apple.com/kb/HT3712">"OS X v10.6: About gamma 2.2"</a> on Apple's support site.</p>
+
+</blockquote>
+
+<h4><span class='info'>OS X 10.5</span><a name='APCustomColorMatchingName'>APCustomColorMatchingName</a></h4>
+
+<p class='summary'>*APCustomColorMatchingName name/text: ""</p>
+
+<p>This keyword defines an alternate name for the color matching provided by a driver in the <var>Color Matching</var> print panel. The default is to use the name "Vendor Matching" or its localized equivalent.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Define the names for our color matching...
+*APCustomColorMatchingName name/AcmeColor(tm): ""
+*fr.APCustomColorMatchingName name/La AcmeColor(tm): ""
+</pre>
+
+<h4><span class='info'>OS X 10.5</span><a name='APCustomColorMatchingProfile'>APCustomColorMatchingProfile</a></h4>
+
+<p class='summary'>*APCustomColorMatchingProfile: name</p>
+
+<p>This keyword defines a supported RGB color profile that can be used when doing custom color matching. Currently only <tt>sRGB</tt>, <tt>AdobeRGB</tt>, and <tt>GenericRGB</tt> are supported. If not specified, RGB data will use the GenericRGB colorspace.</p>
+
+<blockquote><b>Note:</b>
+
+<p>If you provide multiple <tt>APCustomColorMatchingProfile</tt> keywords, you are responsible for providing the necessary user interface controls to select the profile in a <a href='#APDialogExtension'>print dialog pane</a>. Add the named profile to the print settings using the key <tt>kPMCustomColorMatchingProfileKey</tt>.</p>
+
+</blockquote>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Use sRGB for RGB color by default, but support both sRGB and AdobeRGB
+*APSupportsCustomColorMatching: true
+*APDefaultCustomColorMatchingProfile: sRGB
+*APCustomColorMatchingProfile: sRGB
+*APCustomColorMatchingProfile: AdobeRGB
+</pre>
+
+<h4><span class='info'>OS X 10.5</span><a name='APDefaultCustomColorMatchingProfile'>APDefaultCustomColorMatchingProfile</a></h4>
+
+<p class='summary'>*APDefaultCustomColorMatchingProfile: name</p>
+
+<p>This keyword defines the default RGB color profile that will be used when doing custom color matching. Currently only <tt>sRGB</tt>, <tt>AdobeRGB</tt>, and <tt>GenericRGB</tt> are supported.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Use sRGB for RGB color by default
+*APSupportsCustomColorMatching: true
+*APDefaultCustomColorMatchingProfile: sRGB
+</pre>
+
+<h4><span class='info'>OS X 10.4</span><a name='APSupportsCustomColorMatching'>APSupportsCustomColorMatching</a></h4>
+
+<p class='summary'>*APSupportsCustomColorMatching: boolean</p>
+
+<p>This keyword specifies that the driver provides its own custom color matching. When <tt>true</tt>, the default hand-off colorspace will be GenericGray, GenericRGB, or GenericCMYK depending on the number of components the driver requests. The <a href='#APDefaultCustomColorMatchingProfile'><tt>APDefaultCustomColorMatchingProfile</tt></a> keyword can be used to override the default 3-component (RGB) colorspace.</p>
+
+<p>The default for <tt>APSupportsCustomColorMatching</tt> is <tt>false</tt>.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*APSupportsCustomColorMatching: true
+*APDefaultCustomColorMatchingProfile: sRGB
+</pre>
+
+
+<h2 class='title'><a name='CONSTRAINTS'>Constraints</a></h2>
+
+<p>Constraints are option choices that are not allowed by the driver or device, for example printing 2-sided transparencies. All versions of CUPS support constraints defined by the legacy Adobe <tt>UIConstraints</tt> and <tt>NonUIConstraints</tt> keywords which support conflicts between any two option choices, for example:</p>
+
+<pre class='command'>
+*% Do not allow 2-sided printing on transparency media
+*UIConstraints: "*Duplex *MediaType Transparency"
+*UIConstraints: "*MediaType Transparency *Duplex"
+</pre>
+
+<p>While nearly all constraints can be expressed using these keywords, there are valid scenarios requiring constraints between more than two option choices. In addition, resolution of constraints is problematic since users and software have to guess how a particular constraint is best resolved.</p>
+
+<p>CUPS 1.4 and higher define two new keywords for constraints, <tt>cupsUIConstraints</tt> and <tt>cupsUIResolver</tt>. Each <tt>cupsUIConstraints</tt> keyword points to a <tt>cupsUIResolver</tt> keyword which specifies alternate options that resolve the conflict condition. The same <tt>cupsUIResolver</tt> can be used by multiple <tt>cupsUIConstraints</tt>.</p>
+
+<blockquote><b>Note:</b>
+
+<p>When developing PPD files that contain constraints, it is very important to use the <a href="man-cupstestppd.html">cupstestppd(1)</a> program to verify that your constraints are accurate and cannot result in unresolvable option selections.</p>
+
+</blockquote>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsUIConstraints'>cupsUIConstraints</a></h3>
+
+<p class='summary'>*cupsUIConstraints resolver: "*Keyword1 *Keyword2 ..."<br>
+*cupsUIConstraints resolver: "*Keyword1 OptionKeyword1 *Keyword2 ..."<br>
+*cupsUIConstraints resolver: "*Keyword1 *Keyword2 OptionKeyword2 ..."<br>
+*cupsUIConstraints resolver: "*Keyword1 OptionKeyword1 *Keyword2 OptionKeyword2 ..."<br>
+*cupsUIConstraints: "*InstallableKeyword1 OptionKeyword1 *Keyword2 OptionKeyword2 ..."</p>
+
+<p>Lists two or more options which conflict. The "resolver" string is a (possibly unique) keyword which specifies which options to change when the constraint exists. When no resolver is provided, CUPS first tries the default choice followed by testing each option choice to resolve the conflict.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Specify that 2-sided printing cannot happen on transparencies</em>
+*cupsUIConstraints transparency: "*Duplex *MediaType Transparency"
+
+<em>*% Specify that envelope printing cannot happen from the paper trays</em>
+*cupsUIConstraints envelope: "*PageSize Env10 *InputSlot Tray1"
+*cupsUIConstraints envelope: "*PageSize Env10 *InputSlot Tray1"
+*cupsUIConstraints envelope: "*PageSize EnvDL *InputSlot Tray2"
+*cupsUIConstraints envelope: "*PageSize EnvDL *InputSlot Tray2"
+
+<em>*% Specify an installable option constraint for the envelope feeder</em>
+*cupsUIConstraints: "*InputSlot EnvFeeder *InstalledEnvFeeder"
+
+<em>*% Specify that photo printing cannot happen on plain paper or transparencies at 1200dpi</em>
+*cupsUIConstraints photo: "*OutputMode Photo *MediaType Plain *Resolution 1200dpi"
+*cupsUIConstraints photo: "*OutputMode Photo *MediaType Transparency *Resolution 1200dpi"
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsUIResolver'>cupsUIResolver</a></h3>
+
+<p class='summary'>*cupsUIResolver resolver: "*Keyword1 OptionKeyword1 *Keyword2 OptionKeyword2 ..."</p>
+
+<p>Specifies two or more options to mark/select to resolve a constraint. The "resolver" string identifies a particular action to take for one or more <a href='#cupsUIConstraints'><tt>cupsUIConstraints</tt></a>. The same action can be used for multiple constraints. The option keyword pairs are treated as an ordered list of option selections to try - only the first N selections will be used, where N is the minimum number of selections required. Because <a href="api-ppd.html#cupsResolveConflicts"><code>cupsResolveConflicts()</code></a> will not change the most recent option selection passed to it, at least two options from the constraints must be listed to avoid situations where conflicts cannot be resolved.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Specify the options to change for the 2-sided transparency constraint</em>
+*cupsUIResolver transparency: "*Duplex None *MediaType Plain"
+
+<em>*% Specify the options to change for the envelope printing constraints. Notice
+*% that we try to change the InputSlot to either the envelope feeder or the
+*% manual feed first, then we change the page size...</em>
+*cupsUIResolver envelope: "*InputSlot EnvFeeder *InputSlot ManualFeed *PageSize Letter"
+
+<em>*% Specify the options to change for the photo printing constraints</em>
+*cupsUIResolver photo: "*OutputMode Best *Resolution 600dpi"
+</pre>
+
+
+<h2 class='title'><a name='I18N'>Globalized PPD Support</a></h2>
+
+<p>CUPS 1.2 and higher adds support for PPD files containing multiple languages by following the following additional rules:</p>
+
+<ol>
+
+ <li>The <tt>LanguageVersion</tt> MUST be <tt>English</tt></li>
+
+ <li>The <tt>LanguageEncoding</tt> MUST be <tt>ISOLatin1</tt></li>
+
+ <li>The <tt>cupsLanguages</tt> keyword MUST be provided and list each of the supported locales in the PPD file</li>
+
+ <li>Main and option keywords MUST NOT exceed 34 (instead of 40) characters to allow room for the locale prefixes in translation keywords</li>
+
+ <li>The main keyword "Translation" MUST NOT be used</li>
+
+ <li>Translation strings included with the main and option keywords MUST NOT contain characters outside the ASCII subset of ISOLatin1 and UTF-8; developers wishing to use characters outside ASCII MUST provide a separate set of English localization keywords for the affected keywords.</li>
+
+ <li>Localizations are specified using a locale prefix of the form "ll" or "ll_CC." where "ll" is the 2-letter ISO language code and "CC" is the 2-letter ISO country code<ul>
+ <li>A generic language translation ("ll") SHOULD be provided with country-specific differences ("ll_CC") provided only as needed</li>
+ <li>For historical reasons, the "zh" and "zh_CN" locales map to Simplified Chinese while the "zh_TW" locale maps to Traditional Chinese</li>
+ </ul></li>
+
+ <li>Locale-specific translation strings MUST be encoded using UTF-8.</li>
+
+ <li>Main keywords MUST be localized using one of the following forms:
+ <p><tt>*ll.Translation MainKeyword/translation text: ""</tt><br />
+ <tt>*ll_CC.Translation MainKeyword/translation text: ""</tt></p></li>
+
+ <li>Option keywords MUST be localized using one of the following forms:
+ <p><tt>*ll.MainKeyword OptionKeyword/translation text: ""</tt><br>
+ <tt>*ll_CC.MainKeyword OptionKeyword/translation text: ""</tt></p></li>
+
+ <li>Localization keywords MAY appear anywhere after the first line of the PPD file</li>
+
+</ol>
+
+<blockquote><b>Note:</b>
+
+<p>We use a <tt>LanguageEncoding</tt> value of <tt>ISOLatin1</tt> and limit the allowed base translation strings to ASCII to avoid character coding issues that would otherwise occur. In addition, requiring the base translation strings to be in English allows for easier fallback translation when no localization is provided in the PPD file for a given locale.</p>
+
+</blockquote>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*cupsLanguages: "de fr_CA"
+*ModelName: "Foobar Laser 9999"
+
+<em>*% Localize ModelName for French and German</em>
+*fr_CA.Translation ModelName/La Foobar Laser 9999: ""
+*de.Translation ModelName/Foobar LaserDrucken 9999: ""
+
+*cupsIPPReason com.vendor-error/A serious error occurred: "/help/com.vendor/error.html"
+<em>*% Localize printer-state-reason for French and German</em>
+*fr_CA.cupsIPPReason com.vendor-error/Une erreur s&egrave;rieuse s'est produite: "/help/com.vendor/error.html"
+*de.cupsIPPReason com.vendor-error/Eine ernste St&ouml;rung trat: "/help/com.vendor/error.html"
+
+...
+
+*OpenUI *InputSlot/Paper Source: PickOne
+*OrderDependency: 10 AnySetup *InputSlot
+*DefaultInputSlot: Auto
+<em>*% Localize InputSlot for French and German</em>
+*fr_CA.Translation InputSlot/Papier source: ""
+*de.Translation InputSlot/Papiereinzug: ""
+*InputSlot Auto/Default: "&lt;&lt;/ManualFeed false&gt;&gt;setpagedevice"
+<em>*% Localize InputSlot=Auto for French and German</em>
+*fr_CA.InputSlot Auto/Par Defaut: ""
+*de.InputSlot Auto/Standard: ""
+*InputSlot Manual/Manual Feed: "&lt;&lt;/ManualFeed true&gt;&gt;setpagedevice"
+<em>*% Localize InputSlot=Manual for French and German</em>
+*fr_CA.InputSlot Manual/Manuel mecanisme de alimentation: ""
+*de.InputSlot Manual/Manueller Einzug: ""
+*CloseUI: *InputSlot
+</pre>
+
+
+<h2 class='title'><a name='OPTIONS'><span class="info">CUPS 1.3/OS X 10.6</span>Custom Options</a></h2>
+
+<p>CUPS supports custom options using an extension of the <tt>CustomPageSize</tt> and <tt>ParamCustomPageSize</tt> syntax:</p>
+
+<pre class='command'>
+*CustomFoo True: "command"
+*ParamCustomFoo Name1/Text 1: order type minimum maximum
+*ParamCustomFoo Name2/Text 2: order type minimum maximum
+...
+*ParamCustomFoo NameN/Text N: order type minimum maximum
+</pre>
+
+<p>When the base option is part of the <tt>JCLSetup</tt> section, the "command" string contains JCL commands with "\order" placeholders for each numbered parameter. The CUPS API handles any necessary value quoting for HP-PJL commands. For example, if the JCL command string is "@PJL SET PASSCODE=\1" and the first
+option value is "1234" then CUPS will output the string "@PJL SET PASSCODE=1234".</p>
+
+<p>For non-<tt>JCLSetup</tt> options, the "order" value is a number from 1 to N and specifies the order of values as they are placed on the stack before the command. For example, if the PostScript command string is "&lt;&lt;/cupsReal1 2 1 roll&gt;&gt;setpagedevice" and the option value is "2.0" then CUPS will output the string "2.0 &lt;&lt;/cupsReal1 2 1 roll&gt;&gt;setpagedevice".</p>
+
+<p>The "type" is one of the following keywords:</p>
+
+<ul>
+
+ <li><tt>curve</tt> - a real value from "minimum" to "maximum" representing a gamma correction curve using the function: f(x) = x <sup>value</sup></li>
+
+ <li><tt>int</tt> - an integer value from "minimum" to "maximum"</li>
+
+ <li><tt>invcurve</tt> - a real value from "minimum" to "maximum" representing a gamma correction curve using the function: f(x) = x <sup>1 / value</sup></li>
+
+ <li><tt>passcode</tt> - a string of numbers value with a minimum of "minimum" numbers and a maximum of "maximum" numbers ("minimum" and "maximum" are numbers and passcode strings are not displayed in the user interface)</li>
+
+ <li><tt>password</tt> - a string value with a minimum of "minimum" characters and a maximum of "maximum" characters ("minimum" and "maximum" are numbers and password strings are not displayed in the user interface)</li>
+
+ <li><tt>points</tt> - a measurement value in points from "minimum" to "maximum"</li>
+
+ <li><tt>real</tt> - a real value from "minimum" to "maximum"</li>
+
+ <li><tt>string</tt> - a string value with a minimum of "minimum" characters and a maximum of "maximum" characters ("minimum" and "maximum" are numbers)</li>
+
+</ul>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Base JCL key code option</em>
+*JCLOpenUI JCLPasscode/Key Code: PickOne
+*OrderDependency: 10 JCLSetup *JCLPasscode
+*DefaultJCLPasscode: None
+*JCLPasscode None/No Code: ""
+*JCLPasscode 1111: "@PJL SET PASSCODE = 1111&lt;0A&gt;"
+*JCLPasscode 2222: "@PJL SET PASSCODE = 2222&lt;0A&gt;"
+*JCLPasscode 3333: "@PJL SET PASSCODE = 3333&lt;0A&gt;"
+*JCLCloseUI: *JCLPasscode
+
+<em>*% Custom JCL key code option</em>
+*CustomJCLPasscode True: "@PJL SET PASSCODE = \1&lt;0A&gt;"
+*ParamCustomJCLPasscode Code/Key Code: 1 passcode 4 4
+
+
+<em>*% Base PostScript watermark option</em>
+*OpenUI WatermarkText/Watermark Text: PickOne
+*OrderDependency: 10 AnySetup *WatermarkText
+*DefaultWatermarkText: None
+*WatermarkText None: ""
+*WatermarkText Draft: "&lt;&lt;/cupsString1(Draft)&gt;&gt;setpagedevice"
+*CloseUI: *WatermarkText
+
+<em>*% Custom PostScript watermark option</em>
+*CustomWatermarkText True: "&lt;&lt;/cupsString1 3 -1 roll&gt;&gt;setpagedevice"
+*ParamCustomWatermarkText Text: 1 string 0 32
+
+
+<em>*% Base PostScript gamma/density option</em>
+*OpenUI GammaDensity/Gamma and Density: PickOne
+*OrderDependency: 10 AnySetup *GammaDensity
+*DefaultGammaDensity: Normal
+*GammaDensity Normal/Normal: "&lt;&lt;/cupsReal1 1.0/cupsReal2 1.0&gt;&gt;setpagedevice"
+*GammaDensity Light/Lighter: "&lt;&lt;/cupsReal1 0.9/cupsReal2 0.67&gt;&gt;setpagedevice"
+*GammaDensity Dark/Darker: "&lt;&lt;/cupsReal1 1.1/cupsReal2 1.5&gt;&gt;setpagedevice"
+*CloseUI: *GammaDensity
+
+<em>*% Custom PostScript gamma/density option</em>
+*CustomGammaDensity True: "&lt;&lt;/cupsReal1 3 -1 roll/cupsReal2 5 -1&gt;&gt;setpagedevice"
+*ParamCustomGammaDensity Gamma: 1 curve 0.1 10
+*ParamCustomGammaDensity Density: 2 real 0 2
+</pre>
+
+
+<h2 class='title'><a name='RASTERPS'>Writing PostScript Option Commands for Raster Drivers</a></h2>
+
+<p>PPD files are used for both PostScript and non-PostScript printers. For CUPS raster drivers, you use a subset of the PostScript language to set page device keywords such as page size, resolution, and so forth. For example, the following code sets the page size to A4 size:</p>
+
+<pre class='command'>
+*PageSize A4: "&lt;&lt;/PageSize[595 842]&gt;&gt;setpagedevice"
+</pre>
+
+<p>Custom options typically use other operators to organize the values into a key/value dictionary for <tt>setpagedevice</tt>. For example, our previous <tt>CustomWatermarkText</tt> option code uses the <tt>roll</tt> operator to move the custom string value into the dictionary for <tt>setpagedevice</tt>:</p>
+
+<pre class='command'>
+*CustomWatermarkText True: "&lt;&lt;/cupsString1 3 -1 roll&gt;&gt;setpagedevice"
+</pre>
+
+<p>For a custom string value of "My Watermark", CUPS will produce the following PostScript code for the option:</p>
+
+<pre class='command'>
+(My Watermark)
+&lt;&lt;/cupsString1 3 -1 roll&gt;&gt;setpagedevice
+</pre>
+
+<p>The code moves the string value ("My Watermark") from the bottom of the stack to the top, creating a dictionary that looks like:</p>
+
+<pre class='command'>
+&lt;&lt;/cupsString1(My Watermark)&gt;&gt;setpagedevice
+</pre>
+
+<p>The resulting dictionary sets the page device attributes that are sent to your raster driver in the page header.</p>
+
+<h3>Custom Page Size Code</h3>
+
+<p>There are many possible implementations of the <tt>CustomPageSize</tt> code. For CUPS raster drivers, the following code is recommended:</p>
+
+<pre class='command'>
+*ParamCustomPageSize Width: 1 points <i>min-width max-width</i>
+*ParamCustomPageSize Height: 2 points <i>min-height max-height</i>
+*ParamCustomPageSize WidthOffset: 3 points 0 0
+*ParamCustomPageSize HeightOffset: 4 points 0 0
+*ParamCustomPageSize Orientation: 5 int 0 0
+*CustomPageSize True: "pop pop pop &lt;&lt;/PageSize[5 -2 roll]/ImagingBBox null&gt;&gt;setpagedevice"
+</pre>
+
+<h3>Supported PostScript Operators</h3>
+
+<p>CUPS supports the following PostScript operators in addition to the usual PostScript number, string (literal and hex-encoded), boolean, null, and name values:</p>
+
+<ul>
+
+ <li><tt>&lt;&lt;</tt> - Start a dictionary.</li>
+
+ <li><tt>&gt;&gt;</tt> - End a dictionary.</li>
+
+ <li><tt>[</tt> - Start an array.</li>
+
+ <li><tt>]</tt> - End an array.</li>
+
+ <li><tt>copy</tt> - Copy the top N objects on the stack.</li>
+
+ <li><tt>dup</tt> - Copy the top object on the stack.</li>
+
+ <li><tt>index</tt> - Copy the Nth from the top object on the stack.</li>
+
+ <li><tt>pop</tt> - Pop the top object on the stack.</li>
+
+ <li><tt>roll</tt> - Shift the top N objects on the stack.</li>
+
+ <li><tt>setpagedevice</tt> - Set the page header values according to the key/value dictionary on the stack.</li>
+
+</ul>
+
+<blockquote><b>Note:</b>
+
+<p><em>Never</em> use the unsupported <tt>dict</tt> or <tt>put</tt>
+operators in your option code. These operators are typically used in
+option code dating back to Level 1 PostScript printers, which did not
+support the simpler <tt>&lt;&lt;</tt> or <tt>&gt;&gt;</tt> operators.
+If you have old option code using <tt>dict</tt> or <tt>put</tt>, you can
+rewrite it very easily to use the newer <tt>&lt;&lt;</tt> and
+<tt>&gt;&gt;</tt> operators instead. For example, the following code
+to set the page size:</p>
+
+<style type='text/css'><!--
+PRE B {
+ background: #000000;
+ color: #ffffff;
+ padding: 2px 5px;
+}
+--></style>
+
+<pre class='command'>
+<b>1 dict dup</b> /PageSize [612 792] <b>put</b> setpagedevice
+</pre>
+
+<p>can be rewritten as:</p>
+
+<pre class='command'>
+<b>&lt;&lt;</b> /PageSize [612 792] <b>&gt;&gt;</b> setpagedevice
+</pre>
+
+</blockquote>
+
+<h3>Supported Page Device Attributes</h3>
+
+<p>Table 2 shows the supported page device attributes along with PostScript code examples.</p>
+
+<div class='table'>
+<table summary='Supported Page Device Attributes'>
+<caption>Table 2: <a name='TABLE_2'>Supported Page Device Attributes</a></caption>
+<thead>
+<tr>
+ <th>Name(s)</th>
+ <th>Type</th>
+ <th>Description</th>
+ <th>Example(s)</th>
+</tr>
+</thead>
+<tbody>
+<tr valign='top'>
+ <td><tt>AdvanceDistance</tt></td>
+ <td>Integer</td>
+ <td>Specifies the number of points to advance roll media after printing.</td>
+ <td><tt>&lt;&lt;/AdvanceDistance 18&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>AdvanceMedia</tt></td>
+ <td>Integer</td>
+ <td>Specifies when to advance the media: 0 = never, 1 = after the file, 2 = after the job, 3 = after the set, and 4 = after the page.</td>
+ <td><tt>&lt;&lt;/AdvanceMedia 4&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>Collate</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether collated copies are required.</td>
+ <td><tt>&lt;&lt;/Collate true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>CutMedia</tt></td>
+ <td>Integer</td>
+ <td>Specifies when to cut the media: 0 = never, 1 = after the file, 2 = after the job, 3 = after the set, and 4 = after the page.</td>
+ <td><tt>&lt;&lt;/CutMedia 1&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>Duplex</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether 2-sided printing is required.</td>
+ <td><tt>&lt;&lt;/Duplex true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>HWResolution</tt></td>
+ <td>Integer Array</td>
+ <td>Specifies the resolution of the page image in pixels per inch.</td>
+ <td><tt>&lt;&lt;/HWResolution[1200 1200]&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>InsertSheet</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether to insert a blank sheet before the job.</td>
+ <td><tt>&lt;&lt;/InsertSheet true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>Jog</tt></td>
+ <td>Integer</td>
+ <td>Specifies when to shift the media in the output bin: 0 = never, 1 = after the file, 2 = after the job, 3 = after the set, and 4 = after the page.</td>
+ <td><tt>&lt;&lt;/Jog 2&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>LeadingEdge</tt></td>
+ <td>Integer</td>
+ <td>Specifies the leading edge of the media: 0 = top, 1 = right, 2 = bottom, 3 = left.</td>
+ <td><tt>&lt;&lt;/LeadingEdge 0&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>ManualFeed</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether media should be drawn from the manual feed tray. Note: The <tt>MediaPosition</tt> attribute is preferred over the <tt>ManualFeed</tt> attribute.</td>
+ <td><tt>&lt;&lt;/ManualFeed true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>MediaClass</tt></td>
+ <td>String</td>
+ <td>Specifies a named media.</td>
+ <td><tt>&lt;&lt;/MediaClass (Invoices)&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>MediaColor</tt></td>
+ <td>String</td>
+ <td>Specifies the color of the media.</td>
+ <td><tt>&lt;&lt;/MediaColor &gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>MediaPosition</tt></td>
+ <td>Integer</td>
+ <td>Specifies the tray or source of the media.</td>
+ <td><tt>&lt;&lt;/MediaPosition 12&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>MediaType</tt></td>
+ <td>String</td>
+ <td>Specifies the general media type.</td>
+ <td><tt>&lt;&lt;/MediaType (Glossy)&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>MediaWeight</tt></td>
+ <td>Integer</td>
+ <td>Specifies the media weight in grams per meter<sup>2</sup>.</td>
+ <td><tt>&lt;&lt;/MediaWeight 100&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>MirrorPrint</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether to flip the output image horizontally.</td>
+ <td><tt>&lt;&lt;/MirrorPrint true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>NegativePrint</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether to invert the output image.</td>
+ <td><tt>&lt;&lt;/NegativePrint true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>NumCopies</tt></td>
+ <td>Integer</td>
+ <td>Specifies the number of copies to produce of each page.</td>
+ <td><tt>&lt;&lt;/NumCopies 100&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>Orientation</tt></td>
+ <td>Integer</td>
+ <td>Specifies the orientation of the output: 0 = portrait, 1 = landscape rotated counter-clockwise, 2 = upside-down, 3 = landscape rotated clockwise.</td>
+ <td><tt>&lt;&lt;/Orientation 3&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>OutputFaceUp</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether to place the media face-up in the output bin/tray.</td>
+ <td><tt>&lt;&lt;/OutputFaceUp true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>OutputType</tt></td>
+ <td>String</td>
+ <td>Specifies the output type name.</td>
+ <td><tt>&lt;&lt;/OutputType (Photo)&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>PageSize</tt></td>
+ <td>Integer/Real Array</td>
+ <td>Specifies the width and length/height of the page in points.</td>
+ <td><tt>&lt;&lt;/PageSize[595 842]&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>Separations</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether to produce color separations.</td>
+ <td><tt>&lt;&lt;/Separations true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>TraySwitch</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether to switch trays automatically.</td>
+ <td><tt>&lt;&lt;/TraySwitch true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>Tumble</tt></td>
+ <td>Boolean</td>
+ <td>Specifies whether the back sides of pages are rotated 180 degrees.</td>
+ <td><tt>&lt;&lt;/Tumble true&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsBorderlessScalingFactor</tt></td>
+ <td>Real</td>
+ <td>Specifies the amount to scale the page image dimensions.</td>
+ <td><tt>&lt;&lt;/cupsBorderlessScalingFactor 1.01&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsColorOrder</tt></td>
+ <td>Integer</td>
+ <td>Specifies the order of colors: 0 = chunked, 1 = banded, 2 = planar.</td>
+ <td><tt>&lt;&lt;/cupsColorOrder 0&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsColorSpace</tt></td>
+ <td>Integer</td>
+ <td>Specifies the page image colorspace: 0 = W, 1 = RGB, 2 = RGBA, 3 = K, 4 = CMY, 5 = YMC, 6 = CMYK, 7 = YMCK, 8 = KCMY, 9 = KCMYcm, 10 = GMCK, 11 = GMCS, 12 = White, 13 = Gold, 14 = Silver, 15 = CIE XYZ, 16 = CIE Lab, 17 = RGBW, 32 to 46 = CIE Lab (1 to 15 inks)</td>
+ <td><tt>&lt;&lt;/cupsColorSpace 1 &gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsCompression</tt></td>
+ <td>Integer</td>
+ <td>Specifies a driver compression type/mode.</td>
+ <td><tt>&lt;&lt;/cupsCompression 2&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsInteger0<br>
+ ...<br>
+ cupsInteger15</tt></td>
+ <td>Integer</td>
+ <td>Specifies driver integer values.</td>
+ <td><tt>&lt;&lt;/cupsInteger11 1234&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsMarkerType</tt></td>
+ <td>String</td>
+ <td>Specifies the type of ink/toner to use.</td>
+ <td><tt>&lt;&lt;/cupsMarkerType (Black+Color)&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsMediaType</tt></td>
+ <td>Integer</td>
+ <td>Specifies a numeric media type.</td>
+ <td><tt>&lt;&lt;/cupsMediaType 999&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsPageSizeName</tt></td>
+ <td>String</td>
+ <td>Specifies the name of the page size.</td>
+ <td><tt>&lt;&lt;/cupsPageSizeName (A4.Full)&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsPreferredBitsPerColor</tt></td>
+ <td>Integer</td>
+ <td>Specifies the preferred number of bits per color, typically 8 or 16.</td>
+ <td><tt>&lt;&lt;/cupsPreferredBitsPerColor 16&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsReal0<br>
+ ...<br>
+ cupsReal15</tt></td>
+ <td>Real</td>
+ <td>Specifies driver real number values.</td>
+ <td><tt>&lt;&lt;/cupsReal15 1.234&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsRenderingIntent</tt></td>
+ <td>String</td>
+ <td>Specifies the color rendering intent.</td>
+ <td><tt>&lt;&lt;/cupsRenderingIntent (AbsoluteColorimetric)&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsRowCount</tt></td>
+ <td>Integer</td>
+ <td>Specifies the number of rows of raster data to print on each line for some drivers.</td>
+ <td><tt>&lt;&lt;/cupsRowCount 24&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsRowFeed</tt></td>
+ <td>Integer</td>
+ <td>Specifies the number of rows to feed between passes for some drivers.</td>
+ <td><tt>&lt;&lt;/cupsRowFeed 17&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsRowStep</tt></td>
+ <td>Integer</td>
+ <td>Specifies the number of lines between columns/rows on the print head for some drivers.</td>
+ <td><tt>&lt;&lt;/cupsRowStep 2&gt;&gt;setpagedevice</tt></td>
+</tr>
+<tr valign='top'>
+ <td><tt>cupsString0<br>
+ ...<br>
+ cupsString15</tt></td>
+ <td>String</td>
+ <td>Specifies driver string values.</td>
+ <td><tt>&lt;&lt;/cupsString0(String Value)&gt;&gt;setpagedevice</tt></td>
+</tr>
+</tbody>
+</table></div>
+
+
+<h2 class='title'><a name='MEDIA'>Media Keywords</a></h2>
+
+<p>The CUPS media keywords allow drivers to specify alternate custom page
+size limits based on up to two options.</p>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsMediaQualifier2'>cupsMediaQualifier2</a></h3>
+
+<p class='summary'>*cupsMediaQualifier2: MainKeyword</p>
+
+<p>This keyword specifies the second option to use for overriding the
+custom page size limits.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify alternate custom page size limits based on InputSlot and Quality</em>
+*cupsMediaQualifier2: InputSlot
+*cupsMediaQualifier3: Quality
+*cupsMaxSize .Manual.: "1000 1000"
+*cupsMinSize .Manual.: "100 100"
+*cupsMinSize .Manual.Photo: "200 200"
+*cupsMinSize ..Photo: "300 300"
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsMediaQualifier3'>cupsMediaQualifier3</a></h3>
+
+<p class='summary'>*cupsMediaQualifier3: MainKeyword</p>
+
+<p>This keyword specifies the third option to use for overriding the
+custom page size limits.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify alternate custom page size limits based on InputSlot and Quality</em>
+*cupsMediaQualifier2: InputSlot
+*cupsMediaQualifier3: Quality
+*cupsMaxSize .Manual.: "1000 1000"
+*cupsMinSize .Manual.: "100 100"
+*cupsMinSize .Manual.Photo: "200 200"
+*cupsMinSize ..Photo: "300 300"
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsMinSize'>cupsMinSize</a></h3>
+
+<p class='summary'>*cupsMinSize .Qualifier2.Qualifier3: "width length"<br>
+*cupsMinSize .Qualifier2.: "width length"<br>
+*cupsMinSize ..Qualifier3: "width length"</p>
+
+<p>This keyword specifies alternate minimum custom page sizes in points.
+The <a href='#cupsMediaQualifier2'><tt>cupsMediaQualifier2</tt></a> and
+<a href='#cupsMediaQualifier3'><tt>cupsMediaQualifier3</tt></a> keywords
+are used to identify options to use for matching.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify alternate custom page size limits based on InputSlot and Quality</em>
+*cupsMediaQualifier2: InputSlot
+*cupsMediaQualifier3: Quality
+*cupsMaxSize .Manual.: "1000 1000"
+*cupsMinSize .Manual.: "100 100"
+*cupsMinSize .Manual.Photo: "200 200"
+*cupsMinSize ..Photo: "300 300"
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsMaxSize'>cupsMaxSize</a></h3>
+
+<p class='summary'>*cupsMaxSize .Qualifier2.Qualifier3: "width length"<br>
+*cupsMaxSize .Qualifier2.: "width length"<br>
+*cupsMaxSize ..Qualifier3: "width length"</p>
+
+<p>This keyword specifies alternate maximum custom page sizes in points.
+The <a href='#cupsMediaQualifier2'><tt>cupsMediaQualifier2</tt></a> and
+<a href='#cupsMediaQualifier3'><tt>cupsMediaQualifier3</tt></a> keywords
+are used to identify options to use for matching.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify alternate custom page size limits based on InputSlot and Quality</em>
+*cupsMediaQualifier2: InputSlot
+*cupsMediaQualifier3: Quality
+*cupsMaxSize .Manual.: "1000 1000"
+*cupsMinSize .Manual.: "100 100"
+*cupsMinSize .Manual.Photo: "200 200"
+*cupsMinSize ..Photo: "300 300"
+</pre>
+
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsPageSizeCategory'>cupsPageSizeCategory</a></h3>
+
+<p class="summary">*cupsPageSizeCategory name/text: "name name2 ... nameN"</p>
+
+<p>This keyword lists related paper size names that should be grouped together in the Print or Page Setup dialogs. The "name" portion of the keyword specifies the root/default size for the grouping. On OS X the grouped paper sizes are shown in a submenu of the main paper size. When omitted, sizes with the same dimensions are automatically grouped together, for example "Letter" and "Letter.Borderless".</p>
+
+<p>Example:</p>
+
+<pre class="command">
+<em>*% Specify grouping of borderless/non-borderless sizes</em>
+*cupsPageSizeCategory Letter/US Letter: "Letter Letter.Borderless"
+*cupsPageSizeCategory A4/A4: "A4 A4.Borderless"
+</pre>
+
+
+<h2 class='title'><a name='ATTRIBUTES'>General Attributes</a></h2>
+
+<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsBackSide'>cupsBackSide</a></h3>
+
+<p class='summary'>*cupsBackSide: keyword</p>
+
+<p>This keyword requests special handling of the back side of pages
+when doing duplexed (2-sided) output. <a href='#TABLE_1'>Table 1</a>
+shows the supported keyword values for this keyword and their effect
+on the raster data sent to your driver. For example, when <tt>cupsBackSide</tt>
+is <code>Rotated</code> and <tt>Tumble</tt> is <tt>false</tt>, your driver
+will receive print data starting at the bottom right corner of the page, with
+each line going right-to-left instead of left-to-right. The default value is
+<code>Normal</code>.</p>
+
+<blockquote><b>Note:</b>
+
+<p><tt>cupsBackSide</tt> replaces the older <tt>cupsFlipDuplex</tt>
+keyword - if <tt>cupsBackSide</tt> is specified, <tt>cupsFlipDuplex</tt>
+will be ignored.</p>
+
+</blockquote>
+
+<div class='table'>
+<table width='80%' summary='Back Side Raster Coordinate System'>
+<caption>Table 1: <a name='TABLE_1'>Back Side Raster Coordinate System</a></caption>
+<thead>
+<tr>
+ <th>cupsBackSide</th>
+ <th>Tumble Value</th>
+ <th>Image Presentation</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td><code>Normal</code></td>
+ <td><code>false</code></td>
+ <td>Left-to-right, top-to-bottom</td>
+</tr>
+<tr>
+ <td><code>Normal</code></td>
+ <td><code>true</code></td>
+ <td>Left-to-right, top-to-bottom</td>
+</tr>
+<tr>
+ <td><code>ManualTumble</code></td>
+ <td><code>false</code></td>
+ <td>Left-to-right, top-to-bottom</td>
+</tr>
+<tr>
+ <td><code>ManualTumble</code></td>
+ <td><code>true</code></td>
+ <td>Right-to-left, bottom-to-top</td>
+</tr>
+<tr>
+ <td><code>Rotated</code></td>
+ <td><code>false</code></td>
+ <td>Right-to-left, bottom-to-top</td>
+</tr>
+<tr>
+ <td><code>Rotated</code></td>
+ <td><code>true</code></td>
+ <td>Right-to-left, top-to-bottom</td>
+</tr>
+<tr>
+ <td><code>Flipped</code> *</td>
+ <td><code>false</code></td>
+ <td>Left-to-right, bottom-to-top</td>
+</tr>
+<tr>
+ <td><code>Flipped</code> *</td>
+ <td><code>true</code></td>
+ <td>Right-to-left, top-to-bottom</td>
+</tr>
+</tbody>
+</table>
+</div>
+
+<p><em>* - Not supported in OS X 10.5.x and earlier</em></p>
+
+<div class='figure'><table summary='Back side images'>
+<caption>Figure 1: Back side images</caption>
+<tr><td><img src='../images/raster.png' width='624' height='448' alt='Back side images'></td></tr>
+</table></div>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Flip the page image for the back side of duplexed output</em>
+*cupsBackSide: Flipped
+
+<em>*% Rotate the page image for the back side of duplexed output</em>
+*cupsBackSide: Rotated
+</pre>
+
+<p>Also see the related <a href='#APDuplexRequiresFlippedMargin'><tt>APDuplexRequiresFlippedMargin</tt></a>
+keyword.</p>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsCommands'>cupsCommands</a></h3>
+
+<p class='summary'>*cupsCommands: "name name2 ... nameN"</p>
+
+<p>This string keyword specifies the commands that are supported by the
+CUPS command file filter for this device. The command names are separated
+by whitespace.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify the list of commands we support</em>
+*cupsCommands: "AutoConfigure Clean PrintSelfTestPage ReportLevels com.vendor.foo"
+</pre>
+
+
+<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsEvenDuplex'>cupsEvenDuplex</a></h3>
+
+<p class='summary'>*cupsEvenDuplex: boolean</p>
+
+<p>This boolean keyword notifies the RIP filters that the
+destination printer requires an even number of pages when 2-sided
+printing is selected. The default value is <code>false</code>.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Always send an even number of pages when duplexing</em>
+*cupsEvenDuplex: true
+</pre>
+
+<h3><a name='cupsFax'>cupsFax</a></h3>
+
+<p class='summary'>*cupsFax: boolean</p>
+
+<p>This boolean keyword specifies whether the PPD defines a facsimile device. The default is <tt>false</tt>.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*cupsFax: true
+</pre>
+
+<h3><a name='cupsFilter'>cupsFilter</a></h3>
+
+<p class='summary'>*cupsFilter: "source/type cost program"</p>
+
+<p>This string keyword provides a conversion rule from the
+given source type to the printer's native format using the
+filter "program". If a printer supports the source type directly,
+the special filter program "-" may be specified.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Standard raster printer driver filter</em>
+*cupsFilter: "application/vnd.cups-raster 100 rastertofoo"
+
+<em>*% Plain text filter</em>
+*cupsFilter: "text/plain 10 texttofoo"
+
+<em>*% Pass-through filter for PostScript printers</em>
+*cupsFilter: "application/vnd.cups-postscript 0 -"
+</pre>
+
+<h3><span class='info'>CUPS 1.5</span><a name='cupsFilter2'>cupsFilter2</a></h3>
+
+<p class='summary'>*cupsFilter2: "source/type destination/type cost program"</p>
+
+<p>This string keyword provides a conversion rule from the given source type to the printer's native format using the filter "program". If a printer supports the source type directly, the special filter program "-" may be specified. The destination type is automatically created as needed and is passed to the filters and backend as the FINAL_CONTENT_TYPE value.</p>
+
+<blockquote><b>Note:</b>
+
+<p>The presence of a single <code>cupsFilter2</code> keyword in the PPD file will hide any <code>cupsFilter</code> keywords from the CUPS scheduler. When using <code>cupsFilter2</code> to provide filters specific for CUPS 1.5 and later, provide a <code>cupsFilter2</code> line for every filter and a <code>cupsFilter</code> line for each filter that is compatible with older versions of CUPS.</p>
+
+</blockquote>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Standard raster printer driver filter</em>
+*cupsFilter2: "application/vnd.cups-raster application/vnd.foo 100 rastertofoo"
+
+<em>*% Plain text filter</em>
+*cupsFilter2: "text/plain application/vnd.foo 10 texttofoo"
+
+<em>*% Pass-through filter for PostScript printers</em>
+*cupsFilter2: "application/vnd.cups-postscript application/postscript 0 -"
+</pre>
+
+<h3><span class='info'>Deprecated</span><a name='cupsFlipDuplex'>cupsFlipDuplex</a></h3>
+
+<p class='summary'>*cupsFlipDuplex: boolean</p>
+
+<p>Due to implementation differences between OS X and Ghostscript,
+the <tt>cupsFlipDuplex</tt> keyword is deprecated. Instead, use
+the <a href='#cupsBackSide'><tt>cupsBackSide</tt></a> keyword to specify
+the coordinate system (pixel layout) of the page data on the back side of
+duplex pages.</p>
+
+<p>The value <code>true</code> maps to a <tt>cupsBackSide</tt> value
+of <code>Rotated</code> on OS X and <code>Flipped</code> with
+Ghostscript.</p>
+
+<p>The default value is <code>false</code>.</p>
+
+<blockquote><b>Note:</b>
+
+<p>OS X drivers that previously used
+<tt>cupsFlipDuplex</tt> may wish to provide both the old and
+new keywords for maximum compatibility, for example:</p>
+
+<pre class='command'>
+*cupsBackSide: Rotated
+*cupsFlipDuplex: true
+</pre>
+
+<p>Similarly, drivers written for other operating systems using
+Ghostscript can use:</p>
+
+<pre class='command'>
+*cupsBackSide: Flipped
+*cupsFlipDuplex: true
+</pre></blockquote>
+
+<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsIPPFinishings'>cupsIPPFinishings</a></h3>
+
+<p class='summary'>*cupsIPPFinishings number/text: "*Option Choice ..."</p>
+
+<p>This keyword defines a mapping from IPP <code>finishings</code>
+values to PPD options and choices.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*cupsIPPFinishings 4/staple: "*StapleLocation SinglePortrait"
+*cupsIPPFinishings 5/punch: "*PunchMedia Yes *PunchLocation LeftSide"
+*cupsIPPFinishings 20/staple-top-left: "*StapleLocation SinglePortrait"
+*cupsIPPFinishings 21/staple-bottom-left: "*StapleLocation SingleLandscape"
+</pre>
+
+<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsIPPReason'>cupsIPPReason</a></h3>
+
+<p class='summary'>*cupsIPPReason reason/Reason Text: "optional URIs"</p>
+
+<p>This optional keyword maps custom
+<code>printer-state-reasons</code> keywords that are generated by
+the driver to human readable text. The optional URIs string
+contains zero or more URIs separated by a newline. Each URI can
+be a CUPS server absolute path to a help file under the
+scheduler's <code>DocumentRoot</code> directory, a full HTTP URL
+("http://www.domain.com/path/to/help/page.html"), or any other
+valid URI which directs the user at additional information
+concerning the condition that is being reported.</p>
+
+<p>Since the reason text is limited to 80 characters by the PPD specification, longer text strings can be included by URI-encoding the text with the "text" scheme, for example "text:some%20text". Multiple <code>text</code> URIs are combined by the <tt>ppdLocalizeIPPReason</tt> into a single string that can be displayed to the user.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Map com.vendor-error to text but no page</em>
+*cupsIPPReason com.vendor-error/A serious error occurred: ""
+
+<em>*% Map com.vendor-error to more than 80 characters of text but no page</em>
+*cupsIPPReason com.vendor-error/A serious error occurred: "text:Now%20is%20the%20time
+text:for%20all%20good%20men%20to%20come%20to%20the%20aid%20of%20their%20country."
+
+<em>*% Map com.vendor-error to text and a local page</em>
+*cupsIPPReason com.vendor-error/A serious error occurred: "/help/com.vendor/error.html"
+
+<em>*% Map com.vendor-error to text and a remote page</em>
+*cupsIPPReason com.vendor-error/A serious error occurred: "http://www.vendor.com/help"
+
+<em>*% Map com.vendor-error to text and a local, Apple help book, and remote page</em>
+*APHelpBook: "file:///Library/Printers/vendor/Help.bundle"
+*cupsIPPReason com.vendor-error/A serious error occurred: "/help/com.vendor/error.html
+help:anchor='com.vendor-error'%20bookID=Vendor%20Help
+http://www.vendor.com/help"
+*End
+</pre>
+
+<h3><span class='info'>CUPS 1.5</span><a name='cupsIPPSupplies'>cupsIPPSupplies</a></h3>
+
+<p class='summary'>*cupsIPPSupplies: boolean</p>
+
+<p>This keyword tells the IPP backend whether it should report the current marker-xxx supply attribute values. The default value is <code>True</code>.
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Do not use IPP marker-xxx attributes to report supply levels</em>
+*cupsIPPSupplies: False
+</pre>
+
+
+<h3><span class='info'>CUPS 1.7/OS X 10.9</span><a name='cupsJobAccountId'>cupsJobAccountId</a></h3>
+
+<p class='summary'>*cupsJobAccountId: boolean</p>
+
+<p>This keyword defines whether the printer accepts the job-account-id IPP attribute.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify the printer accepts the job-account-id IPP attribute.</em>
+*cupsJobAccountId: True
+</pre>
+
+
+<h3><span class='info'>CUPS 1.7/OS X 10.9</span><a name='cupsJobAccountingUserId'>cupsJobAccountingUserId</a></h3>
+
+<p class='summary'>*cupsJobAccountingUserId: boolean</p>
+
+<p>This keyword defines whether the printer accepts the job-accounting-user-id IPP attribute.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify the printer accepts the job-accounting-user-id IPP attribute.</em>
+*cupsJobAccountingUserId: True
+</pre>
+
+
+<h3><span class='info'>CUPS 1.7/OS X 10.9</span><a name='cupsJobPassword'>cupsJobPassword</a></h3>
+
+<p class='summary'>*cupsJobPassword: "format"</p>
+
+<p>This keyword defines the format of the job-password IPP attribute, if supported by the printer. Currently the only supported format is "1111" indicating a 4-digit PIN code.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify the printer supports 4-digit PIN codes.</em>
+*cupsJobPassword: "1111"
+</pre>
+
+
+<h3><span class='info'>CUPS 1.2/OS X 10.5</span><a name='cupsLanguages'>cupsLanguages</a></h3>
+
+<p class='summary'>*cupsLanguages: "locale list"</p>
+
+<p>This keyword describes which language localizations are
+included in the PPD. The "locale list" string is a space-delimited
+list of locale names ("en", "en_US", "fr_CA", etc.)</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify Canadian, UK, and US English, and Canadian and French French</em>
+*cupsLanguages: "en_CA en_UK en_US fr_CA fr_FR"
+</pre>
+
+
+<h3><span class='info'>CUPS 1.7/OS X 10.9</span><a name='cupsMandatory'>cupsMandatory</a></h3>
+
+<p class='summary'>*cupsMandatory: "attribute1 attribute2 ... attributeN"</p>
+
+<p>This keyword defines a list of IPP attributes that must be provided when submitting a print job creation request.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify that the user must supply a job-password</em>
+*cupsMandatory: "job-password job-password-encryption"
+</pre>
+
+
+<h3><a name='cupsManualCopies'>cupsManualCopies</a></h3>
+
+<p class='summary'>*cupsManualCopies: boolean</p>
+
+<p>This boolean keyword notifies the RIP filters that the
+destination printer does not support copy generation in
+hardware. The default value is <code>false</code>.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Tell the RIP filters to generate the copies for us</em>
+*cupsManualCopies: true
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsMarkerName'>cupsMarkerName</a></h3>
+
+<p class='summary'>*cupsMarkerName/Name Text: ""</p>
+
+<p>This optional keyword maps <code>marker-names</code> strings that are
+generated by the driver to human readable text.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Map cyanToner to "Cyan Toner"</em>
+*cupsMarkerName cyanToner/Cyan Toner: ""
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsMarkerNotice'>cupsMarkerNotice</a></h3>
+
+<p class='summary'>*cupsMarkerNotice: "disclaimer text"</p>
+
+<p>This optional keyword provides disclaimer text for the supply level
+information provided by the driver, typically something like "supply levels
+are approximate".</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*cupsMarkerNotice: "Supply levels are approximate."
+</pre>
+
+<h3><span class='info'>CUPS 1.6/OS X 10.8</span><a name='cupsMaxCopies'>cupsMaxCopies</a></h3>
+
+<p class='summary'>*cupsMaxCopies: integer</p>
+
+<p>This integer keyword notifies the filters that the destination printer supports up to N copies in hardware. The default value is <code>9999</code>.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Tell the RIP filters we can do up to 99 copies</em>
+*cupsMaxCopies: 99
+</pre>
+
+<h3><a name='cupsModelNumber'>cupsModelNumber</a></h3>
+
+<p class='summary'>*cupsModelNumber: number</p>
+
+<p>This integer keyword specifies a printer-specific model
+number. This number can be used by a filter program to adjust
+the output for a specific model of printer.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify an integer for a driver-specific model number</em>
+*cupsModelNumber: 1234
+</pre>
+
+
+<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsPJLCharset'>cupsPJLCharset</a></h3>
+
+<p class='summary'>*cupsPJLCharset: "ISO character set name"</p>
+
+<p>This string keyword specifies the character set that is used
+for strings in PJL commands. If not specified, US-ASCII is
+assumed.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify UTF-8 is used in PJL strings</em>
+*cupsPJLCharset: "UTF-8"
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsPJLDisplay'>cupsPJLDisplay</a></h3>
+
+<p class='summary'>*cupsPJLDisplay: "what"</p>
+
+<p>This optional keyword specifies which command is used to display the
+job ID, name, and user on the printer's control panel. "What" is either "none"
+to disable this functionality, "job" to use "@PJL JOB DISPLAY", or "rdymsg"
+to use "@PJL RDYMSG DISPLAY". The default is "job".</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Display job information using @PJL SET RDYMSG DISPLAY="foo"</em>
+*cupsPJLDisplay: "rdymsg"
+
+<em>*% Display job information display</em>
+*cupsPJLDisplay: "none"
+</pre>
+
+<h3><span class='info'>CUPS 1.2/OS X 10.5</span><a name='cupsPortMonitor'>cupsPortMonitor</a></h3>
+
+<p class='summary'>*cupsPortMonitor urischeme/Descriptive Text: "port monitor"</p>
+
+<p>This string keyword specifies printer-specific "port
+monitor" filters that may be used with the printer. The CUPS
+scheduler also looks for the <tt>Protocols</tt> keyword to see
+if the <tt>BCP</tt> or <tt>TBCP</tt> protocols are supported. If
+so, the corresponding port monitor ("bcp" and "tbcp",
+respectively) is listed in the printer's
+<tt>port-monitor-supported</tt> keyword.</p>
+
+<p>The "urischeme" portion of the keyword specifies the URI scheme
+that this port monitor should be used for. Typically this is used to
+pre-select a particular port monitor for each type of connection that
+is supported by the printer. The "port monitor" string can be "none"
+to disable the port monitor for the given URI scheme.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Specify a PostScript printer that supports the TBCP protocol</em>
+*Protocols: TBCP PJL
+
+<em>*% Specify that TBCP should be used for socket connections but not USB</em>
+*cupsPortMonitor socket/AppSocket Printing: "tbcp"
+*cupsPortMonitor usb/USB Printing: "none"
+
+<em>*% Specify a printer-specific port monitor for an Epson USB printer</em>
+*cupsPortMonitor usb/USB Status Monitor: "epson-usb"
+</pre>
+
+<h3><span class='info'>CUPS 1.3/OS X 10.5</span><a name='cupsPreFilter'>cupsPreFilter</a></h3>
+
+<p class='summary'>*cupsPreFilter: "source/type cost program"</p>
+
+<p>This string keyword provides a pre-filter rule. The pre-filter
+program will be inserted in the conversion chain immediately
+before the filter that accepts the given MIME type.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% PDF pre-filter</em>
+*cupsPreFilter: "application/pdf 100 mypdfprefilter"
+
+<em>*% PNG pre-filter</em>
+*cupsPreFilter: "image/png 0 mypngprefilter"
+</pre>
+
+
+<h3><span class='info'>CUPS 1.5</span><a name='cupsPrintQuality'>cupsPrintQuality</a></h3>
+
+<p class='summary'>*cupsPrintQuality keyword/text: "code"</p>
+
+<p>This UI keyword defines standard print qualities that directly map from the IPP "print-quality" job template keyword. Standard keyword values are "Draft", "Normal", and "High" which are mapped from the IPP "print-quality" values 3, 4, and 5 respectively. Each <code>cupsPrintQuality</code> option typically sets output mode and resolution parameters in the page device dictionary, eliminating the need for separate (and sometimes confusing) output mode and resolution options.</p>
+
+<blockquote><b>Note:</b>
+
+<p>Unlike all of the other keywords defined in this document, <code>cupsPrintQuality</code> is a UI keyword that MUST be enclosed inside the PPD <code>OpenUI</code> and <code>CloseUI</code> keywords.</p>
+
+</blockquote>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*OpenUI *cupsPrintQuality/Print Quality: PickOne
+*OrderDependency: 10 AnySetup *cupsPrintQuality
+*DefaultcupsPrintQuality: Normal
+*cupsPrintQuality Draft/Draft: "code"
+*cupsPrintQuality Normal/Normal: "code"
+*cupsPrintQuality High/Photo: "code"
+*CloseUI: *cupsPrintQuality
+</pre>
+
+<h3><span class='info'>CUPS 1.5</span><a name='cupsSingleFile'>cupsSingleFile</a></h3>
+
+<p class='summary'>*cupsSingleFile: Boolean</p>
+
+<p>This boolean keyword tells the scheduler whether to print multiple files in a job together or singly. The default is "False" which uses a single instance of the backend for all files in the print job. Setting this keyword to "True" will result in separate instances of the backend for each file in the print job.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+<em>*% Send all print data to a single backend</em>
+*cupsSingleFile: False
+
+<em>*% Send each file using a separate backend</em>
+*cupsSingleFile: True
+</pre>
+
+<h3><span class='info'>CUPS 1.4/OS X 10.6</span><a name='cupsSNMPSupplies'>cupsSNMPSupplies</a></h3>
+
+<p class='summary'>*cupsSNMPSupplies: boolean</p>
+
+<p>This keyword tells the standard network backends whether they should query
+the standard SNMP Printer MIB OIDs for supply levels. The default value is
+<code>True</code>.
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Do not use SNMP queries to report supply levels</em>
+*cupsSNMPSupplies: False
+</pre>
+
+<h3><a name='cupsVersion'>cupsVersion</a></h3>
+
+<p class='summary'>*cupsVersion: major.minor</p>
+
+<p>This required keyword describes which version of the CUPS
+PPD file extensions was used. Currently it must be the string
+"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", or "1.6".</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Specify a CUPS 1.2 driver</em>
+*cupsVersion: "1.2"
+</pre>
+
+
+<h3><span class="info">CUPS 1.6/OS X 10.8</span><a name="JCLToPDFInterpreter">JCLToPDFInterpreter</a></h3>
+
+<p class="summary">*JCLToPDFInterpreter: "JCL"</p>
+
+<p>This keyword provfides the JCL command to insert a PDF job file into a printer-ready data stream. The JCL command is added after the <tt>JCLBegin</tt> value and any commands for JCL options in the PPD file.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% PJL command to start the PDF interpreter</em>
+*JCLToPDFInterpreter: "@PJL ENTER LANGUAGE = PDF&lt;0A&gt;"
+</pre>
+
+
+<h2 class='title'><a name='MACOSX'>OS X Attributes</a></h2>
+
+<h3><span class='info'>OS X 10.3</span><a name='APDialogExtension'>APDialogExtension</a></h3>
+
+<p class='summary'>*APDialogExtension: "/Library/Printers/vendor/filename.plugin"</p>
+
+<p>This keyword defines additional option panes that are displayed in the
+print dialog. Each keyword adds one or more option panes. See the "OutputBinsPDE"
+example and <a href='http://developer.apple.com/qa/qa2004/qa1352.html'>Apple
+Technical Q&amp;A QA1352</a> for information on writing your own print dialog
+plug-ins.</p>
+
+<blockquote><b>Note:</b>
+
+<p>Starting with OS X 10.5, each plug-in must be compiled "4-way fat"
+(32-bit and 64-bit for both PowerPC and Intel) with garbage collection enabled
+in order to be usable with all applications.</p>
+
+</blockquote>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Add two panes for finishing and driver options
+*APDialogExtension: "/Library/Printers/vendor/finishing.plugin"
+*APDialogExtension: "/Library/Printers/vendor/options.plugin"
+</pre>
+
+<h3><span class='info'>OS X 10.4</span><a name='APDuplexRequiresFlippedMargin'>APDuplexRequiresFlippedMargin</a></h3>
+
+<p class='summary'>*APDuplexRequiresFlippedMargin: boolean</p>
+
+<p>This boolean keyword notifies the RIP filters that the
+destination printer requires the top and bottom margins of the
+<tt>ImageableArea</tt> to be swapped for the back page. The
+default is <tt>true</tt> when <tt>cupsBackSide</tt> is <tt>Flipped</tt>
+and <tt>false</tt> otherwise. <a href='#TABLE_2'>Table 2</a> shows how
+<tt>APDuplexRequiresFlippedMargin</tt> interacts with <tt>cupsBackSide</tt>
+and the <tt>Tumble</tt> page attribute.</p>
+
+<div class='table'>
+<table width='80%' summary='Margin Flipping Modes'>
+<caption>Table 2: <a name='TABLE_2'>Margin Flipping Modes</a></caption>
+<thead>
+<tr>
+ <th>APDuplexRequiresFlippedMargin</th>
+ <th>cupsBackSide</th>
+ <th>Tumble Value</th>
+ <th>Margins</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>false</td>
+ <td>any</td>
+ <td>any</td>
+ <td>Normal</td>
+</tr>
+<tr>
+ <td>any</td>
+ <td>Normal</td>
+ <td>any</td>
+ <td>Normal</td>
+</tr>
+<tr>
+ <td>true</td>
+ <td>ManualDuplex</td>
+ <td>false</td>
+ <td>Normal</td>
+</tr>
+<tr>
+ <td>true</td>
+ <td>ManualDuplex</td>
+ <td>true</td>
+ <td>Flipped</td>
+</tr>
+<tr>
+ <td>true</td>
+ <td>Rotated</td>
+ <td>false</td>
+ <td>Flipped</td>
+</tr>
+<tr>
+ <td>true</td>
+ <td>Rotated</td>
+ <td>true</td>
+ <td>Normal</td>
+</tr>
+<tr>
+ <td>true or unspecified</td>
+ <td>Flipped</td>
+ <td>any</td>
+ <td>Flipped</td>
+</tr>
+</tbody>
+</table></div>
+
+<p>Example:</p>
+
+<pre class='command'>
+<em>*% Rotate the back side images</em>
+*cupsBackSide: Rotated
+
+<em>*% Don't swap the top and bottom margins for the back side</em>
+*APDuplexRequiresFlippedMargin: false
+</pre>
+
+<p>Also see the related <a href='#cupsBackSide'><tt>cupsBackSide</tt></a>
+keyword.</p>
+
+<h3><a name='APHelpBook'>APHelpBook</a></h3>
+
+<p class='summary'>*APHelpBook: "bundle URL"</p>
+
+<p>This string keyword specifies the Apple help book bundle to use when
+looking up IPP reason codes for this printer driver. The
+<a href='#cupsIPPReason'><tt>cupsIPPReason</tt></a> keyword maps
+"help" URIs to this file.</p>
+
+<p>Example:</p>
+
+<pre class='command'>
+*APHelpBook: "file:///Library/Printers/vendor/Help.bundle"
+</pre>
+
+<h3><span class='info'>OS X 10.6</span><a name='APICADriver'>APICADriver</a></h3>
+
+<p class='summary'>*APICADriver: boolean</p>
+
+<p>This keyword specifies whether the device has a matching Image Capture
+Architecture (ICA) driver for scanning. The default is <tt>False</tt>.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*APICADriver: True
+*APScanAppBundleID: "com.apple.ImageCaptureApp"
+</pre>
+
+<h3><span class='info'>OS X 10.3</span><a name='APPrinterIconPath'>APPrinterIconPath</a></h3>
+
+<p class='summary'>*APPrinterIconPath: "/Library/Printers/vendor/filename.icns"</p>
+
+<p>This keyword defines the location of a printer icon file to use when
+displaying the printer. The file must be in the Apple icon format.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Apple icon file
+*APPrinterIconPath: "/Library/Printers/vendor/Icons/filename.icns"
+</pre>
+
+<h3><span class='info'>OS X 10.4</span><a name='APPrinterLowInkTool'>APPrinterLowInkTool</a></h3>
+
+<p class='summary'>*APPrinterLowInkTool: "/Library/Printers/vendor/program"</p>
+
+<p>This keyword defines an program that checks the ink/toner/marker levels
+on a printer, returning an XML document with those levels. See the "InkTool"
+example and
+<a href='http://developer.apple.com/technotes/tn2005/tn2144.html'>Apple
+Technical Note TN2144</a> for more information.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Use a vendor monitoring program
+*APPrinterLowInkTool: "/Library/Printers/vendor/Tools/lowinktool"
+</pre>
+
+<h3><span class='info'>OS X 10.5</span><a name='APPrinterPreset'>APPrinterPreset</a></h3>
+
+<p class='summary'>*APPrinterPreset name/text: "*Option Choice ..."</p>
+
+<p>This keyword defines presets for multiple options that show up
+in the print dialog of applications (such as iPhoto) that set the job
+style hint to <tt>NSPrintPhotoJobStyleHint</tt>. Each preset maps to one or
+more pairs of PPD options and choices as well as providing key/value data for
+the application. The following standard preset names are currently defined:</p>
+
+<ul>
+
+ <li><code>General_with_Paper_Auto-Detect</code>; Normal quality general printing with auto-detected media.</li>
+
+ <li><code>General_with_Paper_Auto-Detect_-_Draft</code>; Draft quality general printing with auto-detected media.</li>
+
+ <li><code>General_on_Plain_Paper</code>; Normal quality general printing on plain paper.</li>
+
+ <li><code>General_on_Plain_Paper_-_Draft</code>; Draft quality general printing on plain paper.</li>
+
+ <li><code>Photo_with_Paper_Auto-Detect</code>; Normal quality photo printing with auto-detected media.</li>
+
+ <li><code>Photo_with_Paper_Auto-Detect_-_Fine</code>; High quality photo printing with auto-detected media.</li>
+
+ <li><code>Photo_on_Plain_Paper</code>; Normal quality photo printing on plain paper.</li>
+
+ <li><code>Photo_on_Plain_Paper_-_Fine</code>; High quality photo printing on plain paper.</li>
+
+ <li><code>Photo_on_Photo_Paper</code>; Normal quality photo printing on glossy photo paper.</li>
+
+ <li><code>Photo_on_Photo_Paper_-_Fine</code>; High quality photo printing on glossy photo paper.</li>
+
+ <li><code>Photo_on_Matte_Paper</code>; Normal quality photo printing on matte paper.</li>
+
+ <li><code>Photo_on_Matte_Paper_-_Fine</code>; High quality photo printing on matte paper.</li>
+
+</ul>
+
+<p>The value string consists of pairs of keywords, either an option name and
+choice (*MainKeyword OptionKeyword) or a preset identifier and value
+(com.apple.print.preset.foo value). The following preset identifiers are currently used:</p>
+
+<ul>
+
+ <li><code>com.apple.print.preset.graphicsType</code>; specifies the type of printing used for this printing - "General" for general purpose printing and "Photo" for photo printing.</li>
+
+ <li><code>com.apple.print.preset.media-front-coating</code>; specifies the media type selected by this preset - "none" (plain paper), "glossy", "high-gloss", "semi-gloss", "satin", "matte", and "autodetect".</li>
+
+ <li><code>com.apple.print.preset.output-mode</code>; specifies the output mode for this preset - "color" (default for color printers) or "monochrome" (grayscale, default for B&amp;W printers).</li>
+
+ <li><code>com.apple.print.preset.quality</code>; specifies the overall print quality selected by this preset - "low" (draft), "mid" (normal), or "high".</li>
+
+</ul>
+
+<p>Presets, like options, can also be localized in multiple languages.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*APPrinterPreset Photo_on_Photo_Paper/Photo on Photo Paper: "
+ *MediaType Glossy
+ *ColorModel RGB
+ *Resolution 300dpi
+ com.apple.print.preset.graphicsType Photo
+ com.apple.print.preset.quality mid
+ com.apple.print.preset.media-front-coating glossy"
+*End
+*fr.APPrinterPreset Photo_on_Photo_Paper/Photo sur papier photographique: ""
+</pre>
+
+<h3><span class='info'>OS X 10.3</span><a name='APPrinterUtilityPath'>APPrinterUtilityPath</a></h3>
+
+<p class='summary'>*APPrinterPrinterUtilityPath: "/Library/Printers/vendor/filename.app"</p>
+
+<p>This keyword defines a GUI application that can be used to do printer
+maintenance functions such as cleaning the print head(s). See ... for more
+information.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*% Define the printer utility application
+*APPrinterPrinterUtilityPath: "/Library/Printers/vendor/Tools/utility.app"
+</pre>
+
+<h3><span class='info'>OS X 10.6</span><a name='APScannerOnly'>APScannerOnly</a></h3>
+
+<p class='summary'>*APScannerOnly: boolean</p>
+
+<p>This keyword specifies whether the device has scanning but no printing
+capabilities. The default is <tt>False</tt>.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*APICADriver: True
+*APScannerOnly: True
+</pre>
+
+<h3><span class='info'>OS X 10.3</span><a name='APScanAppBundleID'>APScanAppBundleID</a></h3>
+
+<p class='summary'>*APScanAppBundleID: "bundle ID"</p>
+
+<p>This keyword defines the application to use when scanning pages from
+the device.</p>
+
+<p>Examples:</p>
+
+<pre class='command'>
+*APICADriver: True
+*APScanAppBundleID: "com.apple.ImageCaptureApp"
+</pre>
+
+
+<h2 class='title'><a name='HISTORY'>Change History</a></h2>
+
+<h3>Changes in CUPS 1.7</h3>
+
+<ul>
+
+ <li>Added <a href="#cupsJobAccountId"><tt>cupsJobAccountId</tt></a>,
+ <a href="#cupsJobAccountingUserId"><tt>cupsJobAccountingUserId</tt></a>,
+ <a href="#cupsJobPassword"><tt>cupsJobPassword</tt></a>,
+ <a href="#cupsMandatory"><tt>cupsMandatory</tt></a> keywords.</li>
+
+</ul>
+
+
+<h3>Changes in CUPS 1.6</h3>
+
+<ul>
+
+ <li>Added <a href="#cupsPageSizeCategory"><tt>cupsPageSizeCategory</tt></a> keyword (originally defined in CUPS 1.4).</li>
+
+ <li>Added <a href="#cupsMaxCopies"><tt>cupsMaxCopies</tt></a> keyword.</li>
+
+ <li>Documented <a href="#JCLToPDFInterpreter"><tt>JCLToPDFInterpreter</tt></a> keyword.</li>
+
+ <li>Updated <a href="#cupsVersion"><tt>cupsVersion</tt></a> keyword documentation to list all current releases of CUPS.</li>
+
+</ul>
+
+
+<h3>Changes in CUPS 1.5</h3>
+
+<ul>
+
+ <li>Changes all instances of PPD attributes to PPD keywords, to be consistent with the parent specification from Adobe.</li>
+
+</ul>
+
+
+<h3>Changes in CUPS 1.4.5</h3>
+
+<ul>
+
+ <li>Added <a href='#cupsPrintQuality'><tt>cupsPrintQuality</tt></a> UI keyword.</li>
+
+ <li>Added new properties and values for the <a href='#APPrinterPreset'><tt>APPrinterPreset</tt></a> keyword.</li>
+
+</ul>
+
+
+<h3>Changes in CUPS 1.4</h3>
+
+<ul>
+
+ <li>Added <a href='#APICADriver'><tt>APICADriver</tt></a>
+ keyword.</li>
+
+ <li>Added <a href='#cupsCommands'><tt>cupsCommands</tt></a>
+ keyword.</li>
+
+ <li>Added <a href='#cupsMarkerName'><tt>cupsMarkerName</tt></a>
+ keyword.</li>
+
+ <li>Added <a href='#cupsMarkerNotice'><tt>cupsMarkerNotice</tt></a>
+ keyword.</li>
+
+ <li>Added <a href='#cupsPJLDisplay'><tt>cupsPJLDisplay</tt></a>
+ keyword.</li>
+
+ <li>Added <a href='#cupsSNMPSupplies'><tt>cupsSNMPSupplies</tt></a>
+ keyword.</li>
+
+ <li>Added <a href='#cupsUIResolver'><tt>cupsUIResolver</tt></a> and
+ <a href='#cupsUIConstraints'><tt>cupsUIConstraints</tt></a>
+ keywords.</li>
+
+ <li>Added
+ <a href='#cupsMediaQualifier2'><tt>cupsMediaQualifier2</tt></a>,
+ <a href='#cupsMediaQualifier3'><tt>cupsMediaQualifier3</tt></a>,
+ <a href='#cupsMinSize'><tt>cupsMinSize</tt></a>, and
+ <a href='#cupsMaxSize'><tt>cupsMaxSize</tt></a> keywords.</li>
+
+</ul>
+
+
+<h3>Changes in CUPS 1.3.1</h3>
+
+<ul>
+
+ <li>Added missing OS X <tt>AP</tt> keywords.</li>
+
+ <li>Added section on auto-configuration including the
+ <tt>OID<i>MainKeyword</i></tt> and <tt>?<i>MainKeyword</i></tt>
+ keywords.</li>
+
+ <li>Minor reorganization.</li>
+
+</ul>
+
+
+<h3>Changes in CUPS 1.3</h3>
+
+<ul>
+
+ <li>Added <a href='#cupsBackSide'><tt>cupsBackSide</tt></a> and
+ deprecated <a href='#cupsFlipDuplex'><tt>cupsFlipDuplex</tt></a>.</li>
+
+ <li>Added text URI information to
+ <a href='#cupsIPPReason'><tt>cupsIPPReason</tt></a> documentation.</li>
+
+ <li>Added <a href='#APPrinterPreset'><tt>APPrinterPreset</tt></a>,
+ <a href='#cupsIPPFinishings'><tt>cupsIPPFinishings</tt></a>, and
+ <a href='#cupsPreFilter'><tt>cupsPreFilter</tt></a> keywords.</li>
+
+ <li>Added discussion of custom option code, sample
+ <tt>CustomPageSize</tt> code, and "do not use dict and put" note.</li>
+
+</ul>
+
+<h3>Changes in CUPS 1.2.8</h3>
+
+<ul>
+
+ <li>Added section on supported PostScript commands for raster
+ drivers</li>
+
+</ul>
+
+<h3>Changes in CUPS 1.2</h3>
+
+<ul>
+
+ <li>Added globalization support keywords</li>
+
+ <li>Added custom option values support</li>
+
+ <li>Added <a href='#APHelpBook'><tt>APHelpBook</tt></a> keyword</li>
+
+ <li>Added <a href='#APDuplexRequiresFlippedMargin'><tt>APDuplexRequiresFlippedMargin</tt></a>
+ keyword</li>
+
+ <li>Added <a href='#cupsICCProfile'><tt>cupsICCProfile</tt></a> keyword</li>
+
+ <li>Added <a href='#cupsIPPReason'><tt>cupsIPPReason</tt></a> keyword</li>
+
+ <li>Added <a href='#cupsLanguages'><tt>cupsLanguages</tt></a> keyword</li>
+
+ <li>Added <a href='#cupsPortMonitor'><tt>cupsPortMonitor</tt></a> keyword</li>
+
+ <li>Removed <tt>cupsProtocol</tt> keyword</li>
+
+</ul>
+
+<h3>Changes in CUPS 1.1</h3>
+
+<ul>
+
+ <li>Added <a href='#cupsFlipDuplex'><tt>cupsFlipDuplex</tt></a> keyword</li>
+
+ <li>Added <tt>cupsProtocol</tt> keyword</li>
+
+</ul>
diff --git a/cups/libs/filter/testraster.c b/cups/libs/filter/testraster.c
new file mode 100644
index 000000000..8b682a5ab
--- /dev/null
+++ b/cups/libs/filter/testraster.c
@@ -0,0 +1,1078 @@
+/*
+ * "$Id: testraster.c 10996 2013-05-29 11:51:34Z msweet $"
+ *
+ * Raster test program routines for CUPS.
+ *
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Test the raster functions.
+ * do_ppd_tests() - Test the default option commands in a PPD file.
+ * do_ps_tests() - Test standard PostScript commands.
+ * do_ras_file() - Test reading of a raster file.
+ * do_raster_tests() - Test reading and writing of raster data.
+ * print_changes() - Print differences in the page header.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/raster-private.h>
+
+
+/*
+ * Test PS commands and header...
+ */
+
+static const char *dsc_code =
+"[{\n"
+"%%BeginFeature: *PageSize Tabloid\n"
+"<</PageSize[792 1224]>>setpagedevice\n"
+"%%EndFeature\n"
+"} stopped cleartomark\n";
+static const char *setpagedevice_code =
+"<<"
+"/MediaClass(Media Class)"
+"/MediaColor((Media Color))"
+"/MediaType(Media\\\\Type)"
+"/OutputType<416263>"
+"/AdvanceDistance 1000"
+"/AdvanceMedia 1"
+"/Collate false"
+"/CutMedia 2"
+"/Duplex true"
+"/HWResolution[100 200]"
+"/InsertSheet true"
+"/Jog 3"
+"/LeadingEdge 1"
+"/ManualFeed true"
+"/MediaPosition 8#777"
+"/MediaWeight 16#fe01"
+"/MirrorPrint true"
+"/NegativePrint true"
+"/NumCopies 1"
+"/Orientation 1"
+"/OutputFaceUp true"
+"/PageSize[612 792.1]"
+"/Separations true"
+"/TraySwitch true"
+"/Tumble true"
+"/cupsMediaType 2"
+"/cupsColorOrder 1"
+"/cupsColorSpace 1"
+"/cupsCompression 1"
+"/cupsRowCount 1"
+"/cupsRowFeed 1"
+"/cupsRowStep 1"
+"/cupsBorderlessScalingFactor 1.001"
+"/cupsInteger0 1"
+"/cupsInteger1 2"
+"/cupsInteger2 3"
+"/cupsInteger3 4"
+"/cupsInteger4 5"
+"/cupsInteger5 6"
+"/cupsInteger6 7"
+"/cupsInteger7 8"
+"/cupsInteger8 9"
+"/cupsInteger9 10"
+"/cupsInteger10 11"
+"/cupsInteger11 12"
+"/cupsInteger12 13"
+"/cupsInteger13 14"
+"/cupsInteger14 15"
+"/cupsInteger15 16"
+"/cupsReal0 1.1"
+"/cupsReal1 2.1"
+"/cupsReal2 3.1"
+"/cupsReal3 4.1"
+"/cupsReal4 5.1"
+"/cupsReal5 6.1"
+"/cupsReal6 7.1"
+"/cupsReal7 8.1"
+"/cupsReal8 9.1"
+"/cupsReal9 10.1"
+"/cupsReal10 11.1"
+"/cupsReal11 12.1"
+"/cupsReal12 13.1"
+"/cupsReal13 14.1"
+"/cupsReal14 15.1"
+"/cupsReal15 16.1"
+"/cupsString0(1)"
+"/cupsString1(2)"
+"/cupsString2(3)"
+"/cupsString3(4)"
+"/cupsString4(5)"
+"/cupsString5(6)"
+"/cupsString6(7)"
+"/cupsString7(8)"
+"/cupsString8(9)"
+"/cupsString9(10)"
+"/cupsString10(11)"
+"/cupsString11(12)"
+"/cupsString12(13)"
+"/cupsString13(14)"
+"/cupsString14(15)"
+"/cupsString15(16)"
+"/cupsMarkerType(Marker Type)"
+"/cupsRenderingIntent(Rendering Intent)"
+"/cupsPageSizeName(Letter)"
+"/cupsPreferredBitsPerColor 17"
+">> setpagedevice";
+
+static cups_page_header2_t setpagedevice_header =
+{
+ "Media Class", /* MediaClass */
+ "(Media Color)", /* MediaColor */
+ "Media\\Type", /* MediaType */
+ "Abc", /* OutputType */
+ 1000, /* AdvanceDistance */
+ CUPS_ADVANCE_FILE, /* AdvanceMedia */
+ CUPS_FALSE, /* Collate */
+ CUPS_CUT_JOB, /* CutMedia */
+ CUPS_TRUE, /* Duplex */
+ { 100, 200 }, /* HWResolution */
+ { 0, 0, 0, 0 }, /* ImagingBoundingBox */
+ CUPS_TRUE, /* InsertSheet */
+ CUPS_JOG_SET, /* Jog */
+ CUPS_EDGE_RIGHT, /* LeadingEdge */
+ { 0, 0 }, /* Margins */
+ CUPS_TRUE, /* ManualFeed */
+ 0777, /* MediaPosition */
+ 0xfe01, /* MediaWeight */
+ CUPS_TRUE, /* MirrorPrint */
+ CUPS_TRUE, /* NegativePrint */
+ 1, /* NumCopies */
+ CUPS_ORIENT_90, /* Orientation */
+ CUPS_TRUE, /* OutputFaceUp */
+ { 612, 792 }, /* PageSize */
+ CUPS_TRUE, /* Separations */
+ CUPS_TRUE, /* TraySwitch */
+ CUPS_TRUE, /* Tumble */
+ 0, /* cupsWidth */
+ 0, /* cupsHeight */
+ 2, /* cupsMediaType */
+ 0, /* cupsBitsPerColor */
+ 0, /* cupsBitsPerPixel */
+ 0, /* cupsBytesPerLine */
+ CUPS_ORDER_BANDED, /* cupsColorOrder */
+ CUPS_CSPACE_RGB, /* cupsColorSpace */
+ 1, /* cupsCompression */
+ 1, /* cupsRowCount */
+ 1, /* cupsRowFeed */
+ 1, /* cupsRowStep */
+ 0, /* cupsNumColors */
+ 1.001, /* cupsBorderlessScalingFactor */
+ { 612.0, 792.1 }, /* cupsPageSize */
+ { 0.0, 0.0, 0.0, 0.0 }, /* cupsImagingBBox */
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
+ /* cupsInteger[16] */
+ { 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1, 10.1, 11.1, 12.1, 13.1,
+ 14.1, 15.1, 16.1 }, /* cupsReal[16] */
+ { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13",
+ "14", "15", "16" }, /* cupsString[16] */
+ "Marker Type", /* cupsMarkerType */
+ "Rendering Intent", /* cupsRenderingIntent */
+ "Letter" /* cupsPageSizeName */
+};
+
+
+/*
+ * Local functions...
+ */
+
+static int do_ppd_tests(const char *filename, int num_options,
+ cups_option_t *options);
+static int do_ps_tests(void);
+static int do_ras_file(const char *filename);
+static int do_raster_tests(cups_mode_t mode);
+static void print_changes(cups_page_header2_t *header,
+ cups_page_header2_t *expected);
+
+
+/*
+ * 'main()' - Test the raster functions.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int errors; /* Number of errors */
+ const char *ext; /* Filename extension */
+
+
+ if (argc == 1)
+ {
+ errors = do_ps_tests();
+ errors += do_raster_tests(CUPS_RASTER_WRITE);
+ errors += do_raster_tests(CUPS_RASTER_WRITE_COMPRESSED);
+ errors += do_raster_tests(CUPS_RASTER_WRITE_PWG);
+ }
+ else
+ {
+ int i; /* Looping var */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+
+
+ for (errors = 0, num_options = 0, options = NULL, i = 1; i < argc; i ++)
+ {
+ if (argv[i][0] == '-')
+ {
+ if (argv[i][1] == 'o')
+ {
+ if (argv[i][2])
+ num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
+ else
+ {
+ i ++;
+ if (i < argc)
+ num_options = cupsParseOptions(argv[i], num_options, &options);
+ else
+ {
+ puts("Usage: testraster [-o name=value ...] [filename.ppd ...]");
+ puts(" testraster [filename.ras ...]");
+ return (1);
+ }
+ }
+ }
+ else
+ {
+ puts("Usage: testraster [-o name=value ...] [filename.ppd ...]");
+ puts(" testraster [filename.ras ...]");
+ return (1);
+ }
+ }
+ else if ((ext = strrchr(argv[i], '.')) != NULL)
+ {
+ if (!strcmp(ext, ".ppd"))
+ errors += do_ppd_tests(argv[i], num_options, options);
+ else
+ errors += do_ras_file(argv[i]);
+ }
+ else
+ {
+ puts("Usage: testraster [-o name=value ...] [filename.ppd ...]");
+ puts(" testraster [filename.ras ...]");
+ return (1);
+ }
+ }
+
+ cupsFreeOptions(num_options, options);
+ }
+
+ return (errors);
+}
+
+
+/*
+ * 'do_ppd_tests()' - Test the default option commands in a PPD file.
+ */
+
+static int /* O - Number of errors */
+do_ppd_tests(const char *filename, /* I - PPD file */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ ppd_file_t *ppd; /* PPD file data */
+ cups_page_header2_t header; /* Page header */
+
+
+ printf("\"%s\": ", filename);
+ fflush(stdout);
+
+ if ((ppd = ppdOpenFile(filename)) == NULL)
+ {
+ ppd_status_t status; /* Status from PPD loader */
+ int line; /* Line number containing error */
+
+
+ status = ppdLastError(&line);
+
+ puts("FAIL (bad PPD file)");
+ printf(" %s on line %d\n", ppdErrorString(status), line);
+
+ return (1);
+ }
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, NULL))
+ {
+ puts("FAIL (error from function)");
+ puts(cupsRasterErrorString());
+
+ return (1);
+ }
+ else
+ {
+ puts("PASS");
+
+ return (0);
+ }
+}
+
+
+/*
+ * 'do_ps_tests()' - Test standard PostScript commands.
+ */
+
+static int
+do_ps_tests(void)
+{
+ cups_page_header2_t header; /* Page header */
+ int preferred_bits; /* Preferred bits */
+ int errors = 0; /* Number of errors */
+
+
+ /*
+ * Test PS exec code...
+ */
+
+ fputs("_cupsRasterExecPS(\"setpagedevice\"): ", stdout);
+ fflush(stdout);
+
+ memset(&header, 0, sizeof(header));
+ header.Collate = CUPS_TRUE;
+ preferred_bits = 0;
+
+ if (_cupsRasterExecPS(&header, &preferred_bits, setpagedevice_code))
+ {
+ puts("FAIL (error from function)");
+ puts(cupsRasterErrorString());
+ errors ++;
+ }
+ else if (preferred_bits != 17 ||
+ memcmp(&header, &setpagedevice_header, sizeof(header)))
+ {
+ puts("FAIL (bad header)");
+
+ if (preferred_bits != 17)
+ printf(" cupsPreferredBitsPerColor %d, expected 17\n",
+ preferred_bits);
+
+ print_changes(&setpagedevice_header, &header);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("_cupsRasterExecPS(\"roll\"): ", stdout);
+ fflush(stdout);
+
+ if (_cupsRasterExecPS(&header, &preferred_bits,
+ "792 612 0 0 0\n"
+ "pop pop pop\n"
+ "<</PageSize[5 -2 roll]/ImagingBBox null>>"
+ "setpagedevice\n"))
+ {
+ puts("FAIL (error from function)");
+ puts(cupsRasterErrorString());
+ errors ++;
+ }
+ else if (header.PageSize[0] != 792 || header.PageSize[1] != 612)
+ {
+ printf("FAIL (PageSize [%d %d], expected [792 612])\n", header.PageSize[0],
+ header.PageSize[1]);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ fputs("_cupsRasterExecPS(\"dup index\"): ", stdout);
+ fflush(stdout);
+
+ if (_cupsRasterExecPS(&header, &preferred_bits,
+ "true false dup\n"
+ "<</Collate 4 index"
+ "/Duplex 5 index"
+ "/Tumble 6 index>>setpagedevice\n"
+ "pop pop pop"))
+ {
+ puts("FAIL (error from function)");
+ puts(cupsRasterErrorString());
+ errors ++;
+ }
+ else
+ {
+ if (!header.Collate)
+ {
+ printf("FAIL (Collate false, expected true)\n");
+ errors ++;
+ }
+
+ if (header.Duplex)
+ {
+ printf("FAIL (Duplex true, expected false)\n");
+ errors ++;
+ }
+
+ if (header.Tumble)
+ {
+ printf("FAIL (Tumble true, expected false)\n");
+ errors ++;
+ }
+
+ if(header.Collate && !header.Duplex && !header.Tumble)
+ puts("PASS");
+ }
+
+ fputs("_cupsRasterExecPS(\"%%Begin/EndFeature code\"): ", stdout);
+ fflush(stdout);
+
+ if (_cupsRasterExecPS(&header, &preferred_bits, dsc_code))
+ {
+ puts("FAIL (error from function)");
+ puts(cupsRasterErrorString());
+ errors ++;
+ }
+ else if (header.PageSize[0] != 792 || header.PageSize[1] != 1224)
+ {
+ printf("FAIL (bad PageSize [%d %d], expected [792 1224])\n",
+ header.PageSize[0], header.PageSize[1]);
+ errors ++;
+ }
+ else
+ puts("PASS");
+
+ return (errors);
+}
+
+
+/*
+ * 'do_ras_file()' - Test reading of a raster file.
+ */
+
+static int /* O - Number of errors */
+do_ras_file(const char *filename) /* I - Filename */
+{
+ unsigned y; /* Looping vars */
+ int fd; /* File descriptor */
+ cups_raster_t *ras; /* Raster stream */
+ cups_page_header2_t header; /* Page header */
+ unsigned char *data; /* Raster data */
+ int errors = 0; /* Number of errors */
+ unsigned pages = 0; /* Number of pages */
+
+
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ {
+ printf("%s: %s\n", filename, strerror(errno));
+ return (1);
+ }
+
+ if ((ras = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL)
+ {
+ printf("%s: cupsRasterOpen failed.\n", filename);
+ close(fd);
+ return (1);
+ }
+
+ printf("%s:\n", filename);
+
+ while (cupsRasterReadHeader2(ras, &header))
+ {
+ pages ++;
+ data = malloc(header.cupsBytesPerLine);
+
+ printf(" Page %u: %ux%ux%u@%ux%udpi", pages,
+ header.cupsWidth, header.cupsHeight, header.cupsBitsPerPixel,
+ header.HWResolution[0], header.HWResolution[1]);
+ fflush(stdout);
+
+ for (y = 0; y < header.cupsHeight; y ++)
+ if (cupsRasterReadPixels(ras, data, header.cupsBytesPerLine) <
+ header.cupsBytesPerLine)
+ break;
+
+ if (y < header.cupsHeight)
+ printf(" ERROR AT LINE %d\n", y);
+ else
+ putchar('\n');
+
+ free(data);
+ }
+
+ cupsRasterClose(ras);
+ close(fd);
+
+ return (errors);
+}
+
+
+/*
+ * 'do_raster_tests()' - Test reading and writing of raster data.
+ */
+
+static int /* O - Number of errors */
+do_raster_tests(cups_mode_t mode) /* O - Write mode */
+{
+ int page, x, y; /* Looping vars */
+ FILE *fp; /* Raster file */
+ cups_raster_t *r; /* Raster stream */
+ cups_page_header2_t header, /* Page header */
+ expected; /* Expected page header */
+ unsigned char data[2048]; /* Raster data */
+ int errors = 0; /* Number of errors */
+
+
+ /*
+ * Test writing...
+ */
+
+ printf("cupsRasterOpen(%s): ",
+ mode == CUPS_RASTER_WRITE ? "CUPS_RASTER_WRITE" :
+ mode == CUPS_RASTER_WRITE ? "CUPS_RASTER_WRITE_COMPRESSED" :
+ "CUPS_RASTER_WRITE_PWG");
+ fflush(stdout);
+
+ if ((fp = fopen("test.raster", "wb")) == NULL)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ return (1);
+ }
+
+ if ((r = cupsRasterOpen(fileno(fp), mode)) == NULL)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ fclose(fp);
+ return (1);
+ }
+
+ puts("PASS");
+
+ for (page = 0; page < 4; page ++)
+ {
+ memset(&header, 0, sizeof(header));
+ header.cupsWidth = 256;
+ header.cupsHeight = 256;
+ header.cupsBytesPerLine = 256;
+
+ if (page & 1)
+ {
+ header.cupsBytesPerLine *= 2;
+ header.cupsColorSpace = CUPS_CSPACE_CMYK;
+ header.cupsColorOrder = CUPS_ORDER_CHUNKED;
+ header.cupsNumColors = 4;
+ }
+ else
+ {
+ header.cupsColorSpace = CUPS_CSPACE_K;
+ header.cupsColorOrder = CUPS_ORDER_BANDED;
+ header.cupsNumColors = 1;
+ }
+
+ if (page & 2)
+ {
+ header.cupsBytesPerLine *= 2;
+ header.cupsBitsPerColor = 16;
+ header.cupsBitsPerPixel = (page & 1) ? 64 : 16;
+ }
+ else
+ {
+ header.cupsBitsPerColor = 8;
+ header.cupsBitsPerPixel = (page & 1) ? 32 : 8;
+ }
+
+ if (cupsRasterWriteHeader2(r, &header))
+ puts("cupsRasterWriteHeader2: PASS");
+ else
+ {
+ puts("cupsRasterWriteHeader2: FAIL");
+ errors ++;
+ }
+
+ fputs("cupsRasterWritePixels: ", stdout);
+ fflush(stdout);
+
+ memset(data, 0, header.cupsBytesPerLine);
+ for (y = 0; y < 64; y ++)
+ if (!cupsRasterWritePixels(r, data, header.cupsBytesPerLine))
+ break;
+
+ if (y < 64)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (x = 0; x < header.cupsBytesPerLine; x ++)
+ data[x] = x;
+
+ for (y = 0; y < 64; y ++)
+ if (!cupsRasterWritePixels(r, data, header.cupsBytesPerLine))
+ break;
+
+ if (y < 64)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ memset(data, 255, header.cupsBytesPerLine);
+ for (y = 0; y < 64; y ++)
+ if (!cupsRasterWritePixels(r, data, header.cupsBytesPerLine))
+ break;
+
+ if (y < 64)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ {
+ for (x = 0; x < header.cupsBytesPerLine; x ++)
+ data[x] = x / 4;
+
+ for (y = 0; y < 64; y ++)
+ if (!cupsRasterWritePixels(r, data, header.cupsBytesPerLine))
+ break;
+
+ if (y < 64)
+ {
+ puts("FAIL");
+ errors ++;
+ }
+ else
+ puts("PASS");
+ }
+ }
+ }
+ }
+
+ cupsRasterClose(r);
+ fclose(fp);
+
+ /*
+ * Test reading...
+ */
+
+ fputs("cupsRasterOpen(CUPS_RASTER_READ): ", stdout);
+ fflush(stdout);
+
+ if ((fp = fopen("test.raster", "rb")) == NULL)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ return (1);
+ }
+
+ if ((r = cupsRasterOpen(fileno(fp), CUPS_RASTER_READ)) == NULL)
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ fclose(fp);
+ return (1);
+ }
+
+ puts("PASS");
+
+ for (page = 0; page < 4; page ++)
+ {
+ memset(&expected, 0, sizeof(expected));
+ expected.cupsWidth = 256;
+ expected.cupsHeight = 256;
+ expected.cupsBytesPerLine = 256;
+
+ if (mode == CUPS_RASTER_WRITE_PWG)
+ {
+ strlcpy(expected.MediaClass, "PwgRaster", sizeof(expected.MediaClass));
+ expected.cupsInteger[7] = 0xffffff;
+ }
+
+ if (page & 1)
+ {
+ expected.cupsBytesPerLine *= 2;
+ expected.cupsColorSpace = CUPS_CSPACE_CMYK;
+ expected.cupsColorOrder = CUPS_ORDER_CHUNKED;
+ expected.cupsNumColors = 4;
+ }
+ else
+ {
+ expected.cupsColorSpace = CUPS_CSPACE_K;
+ expected.cupsColorOrder = CUPS_ORDER_BANDED;
+ expected.cupsNumColors = 1;
+ }
+
+ if (page & 2)
+ {
+ expected.cupsBytesPerLine *= 2;
+ expected.cupsBitsPerColor = 16;
+ expected.cupsBitsPerPixel = (page & 1) ? 64 : 16;
+ }
+ else
+ {
+ expected.cupsBitsPerColor = 8;
+ expected.cupsBitsPerPixel = (page & 1) ? 32 : 8;
+ }
+
+ fputs("cupsRasterReadHeader2: ", stdout);
+ fflush(stdout);
+
+ if (!cupsRasterReadHeader2(r, &header))
+ {
+ puts("FAIL (read error)");
+ errors ++;
+ break;
+ }
+
+ if (memcmp(&header, &expected, sizeof(header)))
+ {
+ puts("FAIL (bad page header)");
+ errors ++;
+ print_changes(&header, &expected);
+ }
+
+ fputs("cupsRasterReadPixels: ", stdout);
+ fflush(stdout);
+
+ for (y = 0; y < 64; y ++)
+ {
+ if (!cupsRasterReadPixels(r, data, header.cupsBytesPerLine))
+ {
+ puts("FAIL (read error)");
+ errors ++;
+ break;
+ }
+
+ if (data[0] != 0 || memcmp(data, data + 1, header.cupsBytesPerLine - 1))
+ {
+ printf("FAIL (raster line %d corrupt)\n", y);
+ errors ++;
+ break;
+ }
+ }
+
+ if (y == 64)
+ {
+ for (y = 0; y < 64; y ++)
+ {
+ if (!cupsRasterReadPixels(r, data, header.cupsBytesPerLine))
+ {
+ puts("FAIL (read error)");
+ errors ++;
+ break;
+ }
+
+ for (x = 0; x < header.cupsBytesPerLine; x ++)
+ if (data[x] != (x & 255))
+ break;
+
+ if (x < header.cupsBytesPerLine)
+ {
+ printf("FAIL (raster line %d corrupt)\n", y + 64);
+ errors ++;
+ break;
+ }
+ }
+
+ if (y == 64)
+ {
+ for (y = 0; y < 64; y ++)
+ {
+ if (!cupsRasterReadPixels(r, data, header.cupsBytesPerLine))
+ {
+ puts("FAIL (read error)");
+ errors ++;
+ break;
+ }
+
+ if (data[0] != 255 || memcmp(data, data + 1, header.cupsBytesPerLine - 1))
+ {
+ printf("fail (raster line %d corrupt)\n", y + 128);
+ errors ++;
+ break;
+ }
+ }
+
+ if (y == 64)
+ {
+ for (y = 0; y < 64; y ++)
+ {
+ if (!cupsRasterReadPixels(r, data, header.cupsBytesPerLine))
+ {
+ puts("FAIL (read error)");
+ errors ++;
+ break;
+ }
+
+ for (x = 0; x < header.cupsBytesPerLine; x ++)
+ if (data[x] != ((x / 4) & 255))
+ break;
+
+ if (x < header.cupsBytesPerLine)
+ {
+ printf("FAIL (raster line %d corrupt)\n", y + 192);
+ errors ++;
+ break;
+ }
+ }
+
+ if (y == 64)
+ puts("PASS");
+ }
+ }
+ }
+ }
+
+ cupsRasterClose(r);
+ fclose(fp);
+
+ return (errors);
+}
+
+
+/*
+ * 'print_changes()' - Print differences in the page header.
+ */
+
+static void
+print_changes(
+ cups_page_header2_t *header, /* I - Actual page header */
+ cups_page_header2_t *expected) /* I - Expected page header */
+{
+ int i; /* Looping var */
+
+
+ if (strcmp(header->MediaClass, expected->MediaClass))
+ printf(" MediaClass (%s), expected (%s)\n", header->MediaClass,
+ expected->MediaClass);
+
+ if (strcmp(header->MediaColor, expected->MediaColor))
+ printf(" MediaColor (%s), expected (%s)\n", header->MediaColor,
+ expected->MediaColor);
+
+ if (strcmp(header->MediaType, expected->MediaType))
+ printf(" MediaType (%s), expected (%s)\n", header->MediaType,
+ expected->MediaType);
+
+ if (strcmp(header->OutputType, expected->OutputType))
+ printf(" OutputType (%s), expected (%s)\n", header->OutputType,
+ expected->OutputType);
+
+ if (header->AdvanceDistance != expected->AdvanceDistance)
+ printf(" AdvanceDistance %d, expected %d\n", header->AdvanceDistance,
+ expected->AdvanceDistance);
+
+ if (header->AdvanceMedia != expected->AdvanceMedia)
+ printf(" AdvanceMedia %d, expected %d\n", header->AdvanceMedia,
+ expected->AdvanceMedia);
+
+ if (header->Collate != expected->Collate)
+ printf(" Collate %d, expected %d\n", header->Collate,
+ expected->Collate);
+
+ if (header->CutMedia != expected->CutMedia)
+ printf(" CutMedia %d, expected %d\n", header->CutMedia,
+ expected->CutMedia);
+
+ if (header->Duplex != expected->Duplex)
+ printf(" Duplex %d, expected %d\n", header->Duplex,
+ expected->Duplex);
+
+ if (header->HWResolution[0] != expected->HWResolution[0] ||
+ header->HWResolution[1] != expected->HWResolution[1])
+ printf(" HWResolution [%d %d], expected [%d %d]\n",
+ header->HWResolution[0], header->HWResolution[1],
+ expected->HWResolution[0], expected->HWResolution[1]);
+
+ if (memcmp(header->ImagingBoundingBox, expected->ImagingBoundingBox,
+ sizeof(header->ImagingBoundingBox)))
+ printf(" ImagingBoundingBox [%d %d %d %d], expected [%d %d %d %d]\n",
+ header->ImagingBoundingBox[0],
+ header->ImagingBoundingBox[1],
+ header->ImagingBoundingBox[2],
+ header->ImagingBoundingBox[3],
+ expected->ImagingBoundingBox[0],
+ expected->ImagingBoundingBox[1],
+ expected->ImagingBoundingBox[2],
+ expected->ImagingBoundingBox[3]);
+
+ if (header->InsertSheet != expected->InsertSheet)
+ printf(" InsertSheet %d, expected %d\n", header->InsertSheet,
+ expected->InsertSheet);
+
+ if (header->Jog != expected->Jog)
+ printf(" Jog %d, expected %d\n", header->Jog,
+ expected->Jog);
+
+ if (header->LeadingEdge != expected->LeadingEdge)
+ printf(" LeadingEdge %d, expected %d\n", header->LeadingEdge,
+ expected->LeadingEdge);
+
+ if (header->Margins[0] != expected->Margins[0] ||
+ header->Margins[1] != expected->Margins[1])
+ printf(" Margins [%d %d], expected [%d %d]\n",
+ header->Margins[0], header->Margins[1],
+ expected->Margins[0], expected->Margins[1]);
+
+ if (header->ManualFeed != expected->ManualFeed)
+ printf(" ManualFeed %d, expected %d\n", header->ManualFeed,
+ expected->ManualFeed);
+
+ if (header->MediaPosition != expected->MediaPosition)
+ printf(" MediaPosition %d, expected %d\n", header->MediaPosition,
+ expected->MediaPosition);
+
+ if (header->MediaWeight != expected->MediaWeight)
+ printf(" MediaWeight %d, expected %d\n", header->MediaWeight,
+ expected->MediaWeight);
+
+ if (header->MirrorPrint != expected->MirrorPrint)
+ printf(" MirrorPrint %d, expected %d\n", header->MirrorPrint,
+ expected->MirrorPrint);
+
+ if (header->NegativePrint != expected->NegativePrint)
+ printf(" NegativePrint %d, expected %d\n", header->NegativePrint,
+ expected->NegativePrint);
+
+ if (header->NumCopies != expected->NumCopies)
+ printf(" NumCopies %d, expected %d\n", header->NumCopies,
+ expected->NumCopies);
+
+ if (header->Orientation != expected->Orientation)
+ printf(" Orientation %d, expected %d\n", header->Orientation,
+ expected->Orientation);
+
+ if (header->OutputFaceUp != expected->OutputFaceUp)
+ printf(" OutputFaceUp %d, expected %d\n", header->OutputFaceUp,
+ expected->OutputFaceUp);
+
+ if (header->PageSize[0] != expected->PageSize[0] ||
+ header->PageSize[1] != expected->PageSize[1])
+ printf(" PageSize [%d %d], expected [%d %d]\n",
+ header->PageSize[0], header->PageSize[1],
+ expected->PageSize[0], expected->PageSize[1]);
+
+ if (header->Separations != expected->Separations)
+ printf(" Separations %d, expected %d\n", header->Separations,
+ expected->Separations);
+
+ if (header->TraySwitch != expected->TraySwitch)
+ printf(" TraySwitch %d, expected %d\n", header->TraySwitch,
+ expected->TraySwitch);
+
+ if (header->Tumble != expected->Tumble)
+ printf(" Tumble %d, expected %d\n", header->Tumble,
+ expected->Tumble);
+
+ if (header->cupsWidth != expected->cupsWidth)
+ printf(" cupsWidth %d, expected %d\n", header->cupsWidth,
+ expected->cupsWidth);
+
+ if (header->cupsHeight != expected->cupsHeight)
+ printf(" cupsHeight %d, expected %d\n", header->cupsHeight,
+ expected->cupsHeight);
+
+ if (header->cupsMediaType != expected->cupsMediaType)
+ printf(" cupsMediaType %d, expected %d\n", header->cupsMediaType,
+ expected->cupsMediaType);
+
+ if (header->cupsBitsPerColor != expected->cupsBitsPerColor)
+ printf(" cupsBitsPerColor %d, expected %d\n", header->cupsBitsPerColor,
+ expected->cupsBitsPerColor);
+
+ if (header->cupsBitsPerPixel != expected->cupsBitsPerPixel)
+ printf(" cupsBitsPerPixel %d, expected %d\n", header->cupsBitsPerPixel,
+ expected->cupsBitsPerPixel);
+
+ if (header->cupsBytesPerLine != expected->cupsBytesPerLine)
+ printf(" cupsBytesPerLine %d, expected %d\n", header->cupsBytesPerLine,
+ expected->cupsBytesPerLine);
+
+ if (header->cupsColorOrder != expected->cupsColorOrder)
+ printf(" cupsColorOrder %d, expected %d\n", header->cupsColorOrder,
+ expected->cupsColorOrder);
+
+ if (header->cupsColorSpace != expected->cupsColorSpace)
+ printf(" cupsColorSpace %d, expected %d\n", header->cupsColorSpace,
+ expected->cupsColorSpace);
+
+ if (header->cupsCompression != expected->cupsCompression)
+ printf(" cupsCompression %d, expected %d\n", header->cupsCompression,
+ expected->cupsCompression);
+
+ if (header->cupsRowCount != expected->cupsRowCount)
+ printf(" cupsRowCount %d, expected %d\n", header->cupsRowCount,
+ expected->cupsRowCount);
+
+ if (header->cupsRowFeed != expected->cupsRowFeed)
+ printf(" cupsRowFeed %d, expected %d\n", header->cupsRowFeed,
+ expected->cupsRowFeed);
+
+ if (header->cupsRowStep != expected->cupsRowStep)
+ printf(" cupsRowStep %d, expected %d\n", header->cupsRowStep,
+ expected->cupsRowStep);
+
+ if (header->cupsNumColors != expected->cupsNumColors)
+ printf(" cupsNumColors %d, expected %d\n", header->cupsNumColors,
+ expected->cupsNumColors);
+
+ if (header->cupsBorderlessScalingFactor !=
+ expected->cupsBorderlessScalingFactor)
+ printf(" cupsBorderlessScalingFactor %g, expected %g\n",
+ header->cupsBorderlessScalingFactor,
+ expected->cupsBorderlessScalingFactor);
+
+ if (header->cupsPageSize[0] != expected->cupsPageSize[0] ||
+ header->cupsPageSize[1] != expected->cupsPageSize[1])
+ printf(" cupsPageSize [%g %g], expected [%g %g]\n",
+ header->cupsPageSize[0], header->cupsPageSize[1],
+ expected->cupsPageSize[0], expected->cupsPageSize[1]);
+
+ if (header->cupsImagingBBox[0] != expected->cupsImagingBBox[0] ||
+ header->cupsImagingBBox[1] != expected->cupsImagingBBox[1] ||
+ header->cupsImagingBBox[2] != expected->cupsImagingBBox[2] ||
+ header->cupsImagingBBox[3] != expected->cupsImagingBBox[3])
+ printf(" cupsImagingBBox [%g %g %g %g], expected [%g %g %g %g]\n",
+ header->cupsImagingBBox[0], header->cupsImagingBBox[1],
+ header->cupsImagingBBox[2], header->cupsImagingBBox[3],
+ expected->cupsImagingBBox[0], expected->cupsImagingBBox[1],
+ expected->cupsImagingBBox[2], expected->cupsImagingBBox[3]);
+
+ for (i = 0; i < 16; i ++)
+ if (header->cupsInteger[i] != expected->cupsInteger[i])
+ printf(" cupsInteger%d %d, expected %d\n", i, header->cupsInteger[i],
+ expected->cupsInteger[i]);
+
+ for (i = 0; i < 16; i ++)
+ if (header->cupsReal[i] != expected->cupsReal[i])
+ printf(" cupsReal%d %g, expected %g\n", i, header->cupsReal[i],
+ expected->cupsReal[i]);
+
+ for (i = 0; i < 16; i ++)
+ if (strcmp(header->cupsString[i], expected->cupsString[i]))
+ printf(" cupsString%d (%s), expected (%s)\n", i,
+ header->cupsString[i], expected->cupsString[i]);
+
+ if (strcmp(header->cupsMarkerType, expected->cupsMarkerType))
+ printf(" cupsMarkerType (%s), expected (%s)\n", header->cupsMarkerType,
+ expected->cupsMarkerType);
+
+ if (strcmp(header->cupsRenderingIntent, expected->cupsRenderingIntent))
+ printf(" cupsRenderingIntent (%s), expected (%s)\n",
+ header->cupsRenderingIntent,
+ expected->cupsRenderingIntent);
+
+ if (strcmp(header->cupsPageSizeName, expected->cupsPageSizeName))
+ printf(" cupsPageSizeName (%s), expected (%s)\n",
+ header->cupsPageSizeName,
+ expected->cupsPageSizeName);
+}
+
+
+/*
+ * End of "$Id: testraster.c 10996 2013-05-29 11:51:34Z msweet $".
+ */