]> git.ipfire.org Git - thirdparty/cups.git/blob - examples/ppdx.c
ec93d63accab4abfb630151089e5ba7fb1172711
[thirdparty/cups.git] / examples / ppdx.c
1 /*
2 * Example code for encoding and decoding large amounts of data in a PPD file.
3 * This would typically be used in a driver to save configuration/state
4 * information that could be used by an application.
5 *
6 * Copyright 2012 by Apple Inc.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 */
16
17 /*
18 * Include necessary headers...
19 */
20
21 #include "ppdx.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <zlib.h> /* For compression of the data */
26
27
28 /*
29 * Constants...
30 */
31
32 #define PPDX_MAX_VALUE (PPD_MAX_LINE - PPD_MAX_NAME - 4)
33 /* Max value length with delimiters + nul */
34 #define PPDX_MAX_CHUNK (PPDX_MAX_VALUE * 3 / 4)
35 /* Max length of each chunk when Base64-encoded */
36
37
38 /*
39 * 'ppdxReadData()' - Read encoded data from a ppd_file_t *.
40 *
41 * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns
42 * an allocated pointer to the data (which is nul-terminated for convenience)
43 * along with the length of the data in the variable pointed to by "datasize",
44 * which can be NULL to indicate the caller doesn't need the length.
45 *
46 * Returns NULL if no data is present in the PPD with the prefix.
47 */
48
49 void * /* O - Data or NULL */
50 ppdxReadData(ppd_file_t *ppd, /* I - PPD file */
51 const char *name, /* I - Keyword prefix */
52 size_t *datasize) /* O - Size of data or NULL for don't care */
53 {
54 char keyword[PPD_MAX_NAME], /* Keyword name */
55 decoded[PPDX_MAX_CHUNK + 1];
56 /* Decoded string */
57 unsigned chunk = 0; /* Current chunk number */
58 int len; /* Length of current chunk */
59 ppd_attr_t *attr; /* Keyword/value from PPD file */
60 Bytef *data; /* Pointer to data */
61 size_t alloc_size; /* Allocated size of data buffer */
62 z_stream decomp; /* Decompressor stream */
63 int error; /* Error/status from inflate() */
64
65
66 /*
67 * Range check input...
68 */
69
70 if (datasize)
71 *datasize = 0;
72
73 if (!ppd || !name)
74 return (NULL);
75
76 /*
77 * First see if there are any instances of the named keyword in the PPD...
78 */
79
80 snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk);
81 if ((attr = ppdFindAttr(ppd, keyword, NULL)) == NULL)
82 return (NULL);
83
84 /*
85 * Allocate some memory and start decoding...
86 */
87
88 data = malloc(257);
89 alloc_size = 256;
90
91 memset(&decomp, 0, sizeof(decomp));
92 decomp.next_out = data;
93 decomp.avail_out = 256;
94
95 inflateInit(&decomp);
96
97 do
98 {
99 /*
100 * Grab the data from the current attribute and decode it...
101 */
102
103 len = sizeof(decoded);
104 if (!httpDecode64_2(decoded, &len, attr->value) || len == 0)
105 break;
106
107 // printf("chunk %04x has length %d\n", chunk, len);
108
109 /*
110 * Decompress this chunk...
111 */
112
113 decomp.next_in = decoded;
114 decomp.avail_in = len;
115
116 do
117 {
118 Bytef *temp; /* Temporary pointer */
119 size_t temp_size; /* Temporary allocation size */
120
121 // printf("Before inflate: avail_in=%d, avail_out=%d\n", decomp.avail_in,
122 // decomp.avail_out);
123
124 if ((error = inflate(&decomp, Z_NO_FLUSH)) < Z_OK)
125 {
126 fprintf(stderr, "ERROR: inflate returned %d (%s)\n", error, decomp.msg);
127 break;
128 }
129
130 // printf("After inflate: avail_in=%d, avail_out=%d, error=%d\n",
131 // decomp.avail_in, decomp.avail_out, error);
132
133 if (decomp.avail_out == 0)
134 {
135 if (alloc_size < 2048)
136 temp_size = alloc_size * 2;
137 else if (alloc_size < PPDX_MAX_DATA)
138 temp_size = alloc_size + 2048;
139 else
140 break;
141
142 if ((temp = realloc(data, temp_size + 1)) == NULL)
143 {
144 free(data);
145 return (NULL);
146 }
147
148 decomp.next_out = temp + (decomp.next_out - data);
149 decomp.avail_out = temp_size - alloc_size;
150 data = temp;
151 alloc_size = temp_size;
152 }
153 }
154 while (decomp.avail_in > 0);
155
156 chunk ++;
157 snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk);
158 }
159 while ((attr = ppdFindAttr(ppd, keyword, NULL)) != NULL);
160
161 inflateEnd(&decomp);
162
163 /*
164 * Nul-terminate the data (usually a string)...
165 */
166
167 *(decomp.next_out) = '\0';
168
169 if (datasize)
170 *datasize = decomp.next_out - data;
171
172 return (data);
173 }
174
175
176 /*
177 * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages.
178 *
179 * Writes chunked data to the PPD file using PPD: messages sent to stderr for
180 * cupsd. "name" must be a valid PPD keyword string whose length is less than
181 * 37 characters to allow for chunk numbering. "data" provides a pointer to the
182 * data to be written, and "datasize" provides the length.
183 */
184
185 extern void
186 ppdxWriteData(const char *name, /* I - Base name of keyword */
187 const void *data, /* I - Data to write */
188 size_t datasize) /* I - Number of bytes in data */
189 {
190 char buffer[PPDX_MAX_CHUNK], /* Chunk buffer */
191 encoded[PPDX_MAX_VALUE + 1],
192 /* Encoded data */
193 pair[PPD_MAX_LINE], /* name=value pair */
194 line[PPDX_MAX_STATUS], /* Line buffer */
195 *lineptr, /* Current position in line buffer */
196 *lineend; /* End of line buffer */
197 unsigned chunk = 0; /* Current chunk number */
198 int len; /* Length of current chunk */
199 z_stream comp; /* Compressor stream */
200 int error; /* Error/status from deflate() */
201
202
203 /*
204 * Range check input...
205 */
206
207 if (!name || (!data && datasize > 0) || datasize > PPDX_MAX_DATA)
208 return;
209
210 strlcpy(line, "PPD:", sizeof(line));
211 lineptr = line + 4;
212 lineend = line + sizeof(line) - 2;
213
214 if (datasize > 0)
215 {
216 /*
217 * Compress and encode output...
218 */
219
220 memset(&comp, 0, sizeof(comp));
221 comp.next_in = (Bytef *)data;
222 comp.avail_in = datasize;
223
224 deflateInit(&comp, 9);
225
226 do
227 {
228 /*
229 * Compress a chunk...
230 */
231
232 comp.next_out = buffer;
233 comp.avail_out = sizeof(buffer);
234
235 if ((error = deflate(&comp, Z_FINISH)) < Z_OK)
236 {
237 fprintf(stderr, "ERROR: deflate returned %d (%s)\n", error, comp.msg);
238 break;
239 }
240
241 /*
242 * Write a chunk...
243 */
244
245 len = sizeof(buffer) - comp.avail_out;
246 httpEncode64_2(encoded, sizeof(encoded), buffer, len);
247
248 len = (int)snprintf(pair, sizeof(pair), " %s%04x=%s", name, chunk,
249 encoded);
250 #ifdef DEBUG
251 fprintf(stderr, "DEBUG: *%s%04x: \"%s\"\n", name, chunk, encoded);
252 #endif /* DEBUG */
253
254 if ((lineptr + len) >= lineend)
255 {
256 *lineptr++ = '\n';
257 *lineptr = '\0';
258
259 fputs(line, stderr);
260 lineptr = line + 4;
261 }
262
263 strlcpy(lineptr, pair, lineend - lineptr);
264 lineptr += len;
265
266 /*
267 * Setup for the next one...
268 */
269
270 chunk ++;
271 }
272 while (comp.avail_out == 0);
273 }
274
275 deflateEnd(&comp);
276
277 /*
278 * Write a trailing empty chunk to signal EOD...
279 */
280
281 len = (int)snprintf(pair, sizeof(pair), " %s%04x=\"\"", name, chunk);
282 #ifdef DEBUG
283 fprintf(stderr, "DEBUG: *%s%04x: \"\"\n", name, chunk);
284 #endif /* DEBUG */
285
286 if ((lineptr + len) >= lineend)
287 {
288 *lineptr++ = '\n';
289 *lineptr = '\0';
290
291 fputs(line, stderr);
292 lineptr = line + 4;
293 }
294
295 strlcpy(lineptr, pair, lineend - lineptr);
296 lineptr += len;
297
298 *lineptr++ = '\n';
299 *lineptr = '\0';
300
301 fputs(line, stderr);
302 }