1 /*
2 * PPD model-specific attribute routines for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright 2007-2015 by Apple Inc.
6 * Copyright 1997-2006 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include "cups-private.h"
17 #include "ppd-private.h"
18 #include "debug-internal.h"
19
20
21 /*
22 * 'ppdFindAttr()' - Find the first matching attribute.
23 *
24 * @since CUPS 1.1.19/macOS 10.3@
25 */
26
27 ppd_attr_t * /* O - Attribute or @code NULL@ if not found */
ppdFindAttr(ppd_file_t * ppd,const char * name,const char * spec)28 ppdFindAttr(ppd_file_t *ppd, /* I - PPD file data */
29 const char *name, /* I - Attribute name */
30 const char *spec) /* I - Specifier string or @code NULL@ */
31 {
32 ppd_attr_t key, /* Search key */
33 *attr; /* Current attribute */
34
35
36 DEBUG_printf(("2ppdFindAttr(ppd=%p, name=\"%s\", spec=\"%s\")", ppd, name,
37 spec));
38
39 /*
40 * Range check input...
41 */
42
43 if (!ppd || !name || ppd->num_attrs == 0)
44 return (NULL);
45
46 /*
47 * Search for a matching attribute...
48 */
49
50 memset(&key, 0, sizeof(key));
51 strlcpy(key.name, name, sizeof(key.name));
52
53 /*
54 * Return the first matching attribute, if any...
55 */
56
57 if ((attr = (ppd_attr_t *)cupsArrayFind(ppd->sorted_attrs, &key)) != NULL)
58 {
59 if (spec)
60 {
61 /*
62 * Loop until we find the first matching attribute for "spec"...
63 */
64
65 while (attr && _cups_strcasecmp(spec, attr->spec))
66 {
67 if ((attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) != NULL &&
68 _cups_strcasecmp(attr->name, name))
69 attr = NULL;
70 }
71 }
72 }
73
74 return (attr);
75 }
76
77
78 /*
79 * 'ppdFindNextAttr()' - Find the next matching attribute.
80 *
81 * @since CUPS 1.1.19/macOS 10.3@
82 */
83
84 ppd_attr_t * /* O - Attribute or @code NULL@ if not found */
ppdFindNextAttr(ppd_file_t * ppd,const char * name,const char * spec)85 ppdFindNextAttr(ppd_file_t *ppd, /* I - PPD file data */
86 const char *name, /* I - Attribute name */
87 const char *spec) /* I - Specifier string or @code NULL@ */
88 {
89 ppd_attr_t *attr; /* Current attribute */
90
91
92 /*
93 * Range check input...
94 */
95
96 if (!ppd || !name || ppd->num_attrs == 0)
97 return (NULL);
98
99 /*
100 * See if there are more attributes to return...
101 */
102
103 while ((attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) != NULL)
104 {
105 /*
106 * Check the next attribute to see if it is a match...
107 */
108
109 if (_cups_strcasecmp(attr->name, name))
110 {
111 /*
112 * Nope, reset the current pointer to the end of the array...
113 */
114
115 cupsArrayIndex(ppd->sorted_attrs, cupsArrayCount(ppd->sorted_attrs));
116
117 return (NULL);
118 }
119
120 if (!spec || !_cups_strcasecmp(attr->spec, spec))
121 break;
122 }
123
124 /*
125 * Return the next attribute's value...
126 */
127
128 return (attr);
129 }
130
131
132 /*
133 * '_ppdNormalizeMakeAndModel()' - Normalize a product/make-and-model string.
134 *
135 * This function tries to undo the mistakes made by many printer manufacturers
136 * to produce a clean make-and-model string we can use.
137 */
138
139 char * /* O - Normalized make-and-model string or NULL on error */
_ppdNormalizeMakeAndModel(const char * make_and_model,char * buffer,size_t bufsize)140 _ppdNormalizeMakeAndModel(
141 const char *make_and_model, /* I - Original make-and-model string */
142 char *buffer, /* I - String buffer */
143 size_t bufsize) /* I - Size of string buffer */
144 {
145 char *bufptr; /* Pointer into buffer */
146
147
148 if (!make_and_model || !buffer || bufsize < 1)
149 {
150 if (buffer)
151 *buffer = '\0';
152
153 return (NULL);
154 }
155
156 /*
157 * Skip leading whitespace...
158 */
159
160 while (_cups_isspace(*make_and_model))
161 make_and_model ++;
162
163 /*
164 * Remove parenthesis and add manufacturers as needed...
165 */
166
167 if (make_and_model[0] == '(')
168 {
169 strlcpy(buffer, make_and_model + 1, bufsize);
170
171 if ((bufptr = strrchr(buffer, ')')) != NULL)
172 *bufptr = '\0';
173 }
174 else if (!_cups_strncasecmp(make_and_model, "XPrint ", 7))
175 {
176 /*
177 * Xerox XPrint...
178 * Note: We check for the space after XPrint to ensure we do not display
179 * Xerox for Xprinter devices, which are NOT by Xerox.
180 */
181
182 snprintf(buffer, bufsize, "Xerox %s", make_and_model);
183 }
184 else if (!_cups_strncasecmp(make_and_model, "Eastman", 7))
185 {
186 /*
187 * Kodak...
188 */
189
190 snprintf(buffer, bufsize, "Kodak %s", make_and_model + 7);
191 }
192 else if (!_cups_strncasecmp(make_and_model, "laserwriter", 11))
193 {
194 /*
195 * Apple LaserWriter...
196 */
197
198 snprintf(buffer, bufsize, "Apple LaserWriter%s", make_and_model + 11);
199 }
200 else if (!_cups_strncasecmp(make_and_model, "colorpoint", 10))
201 {
202 /*
203 * Seiko...
204 */
205
206 snprintf(buffer, bufsize, "Seiko %s", make_and_model);
207 }
208 else if (!_cups_strncasecmp(make_and_model, "fiery", 5))
209 {
210 /*
211 * EFI...
212 */
213
214 snprintf(buffer, bufsize, "EFI %s", make_and_model);
215 }
216 else if (!_cups_strncasecmp(make_and_model, "ps ", 3) ||
217 !_cups_strncasecmp(make_and_model, "colorpass", 9))
218 {
219 /*
220 * Canon...
221 */
222
223 snprintf(buffer, bufsize, "Canon %s", make_and_model);
224 }
225 else if (!_cups_strncasecmp(make_and_model, "designjet", 9) ||
226 !_cups_strncasecmp(make_and_model, "deskjet", 7))
227 {
228 /*
229 * HP...
230 */
231
232 snprintf(buffer, bufsize, "HP %s", make_and_model);
233 }
234 else
235 strlcpy(buffer, make_and_model, bufsize);
236
237 /*
238 * Clean up the make...
239 */
240
241 if (!_cups_strncasecmp(buffer, "agfa", 4))
242 {
243 /*
244 * Replace with AGFA (all uppercase)...
245 */
246
247 buffer[0] = 'A';
248 buffer[1] = 'G';
249 buffer[2] = 'F';
250 buffer[3] = 'A';
251 }
252 else if (!_cups_strncasecmp(buffer, "Hewlett-Packard hp ", 19))
253 {
254 /*
255 * Just put "HP" on the front...
256 */
257
258 buffer[0] = 'H';
259 buffer[1] = 'P';
260 _cups_strcpy(buffer + 2, buffer + 18);
261 }
262 else if (!_cups_strncasecmp(buffer, "Hewlett-Packard ", 16))
263 {
264 /*
265 * Just put "HP" on the front...
266 */
267
268 buffer[0] = 'H';
269 buffer[1] = 'P';
270 _cups_strcpy(buffer + 2, buffer + 15);
271 }
272 else if (!_cups_strncasecmp(buffer, "Lexmark International", 21))
273 {
274 /*
275 * Strip "International"...
276 */
277
278 _cups_strcpy(buffer + 8, buffer + 21);
279 }
280 else if (!_cups_strncasecmp(buffer, "herk", 4))
281 {
282 /*
283 * Replace with LHAG...
284 */
285
286 buffer[0] = 'L';
287 buffer[1] = 'H';
288 buffer[2] = 'A';
289 buffer[3] = 'G';
290 }
291 else if (!_cups_strncasecmp(buffer, "linotype", 8))
292 {
293 /*
294 * Replace with LHAG...
295 */
296
297 buffer[0] = 'L';
298 buffer[1] = 'H';
299 buffer[2] = 'A';
300 buffer[3] = 'G';
301 _cups_strcpy(buffer + 4, buffer + 8);
302 }
303
304 /*
305 * Remove trailing whitespace and return...
306 */
307
308 for (bufptr = buffer + strlen(buffer) - 1;
309 bufptr >= buffer && _cups_isspace(*bufptr);
310 bufptr --);
311
312 bufptr[1] = '\0';
313
314 return (buffer[0] ? buffer : NULL);
315 }
316