]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
bd7854cb | 2 | * "$Id: cups-driverd.c 5130 2006-02-17 20:25:33Z mike $" |
ef416fc2 | 3 | * |
4 | * PPD/driver support for the Common UNIX Printing System (CUPS). | |
5 | * | |
6 | * This program handles listing and installing both static PPD files | |
7 | * in CUPS_DATADIR/model and dynamically generated PPD files using | |
8 | * the driver helper programs in CUPS_SERVERBIN/driver. | |
9 | * | |
bd7854cb | 10 | * Copyright 1997-2006 by Easy Software Products. |
ef416fc2 | 11 | * |
12 | * These coded instructions, statements, and computer programs are the | |
13 | * property of Easy Software Products and are protected by Federal | |
14 | * copyright law. Distribution and use rights are outlined in the file | |
15 | * "LICENSE.txt" which should have been included with this file. If this | |
16 | * file is missing or damaged please contact Easy Software Products | |
17 | * at: | |
18 | * | |
19 | * Attn: CUPS Licensing Information | |
20 | * Easy Software Products | |
21 | * 44141 Airport View Drive, Suite 204 | |
22 | * Hollywood, Maryland 20636 USA | |
23 | * | |
24 | * Voice: (301) 373-9600 | |
25 | * EMail: cups-info@cups.org | |
26 | * WWW: http://www.cups.org | |
27 | * | |
28 | * Contents: | |
29 | * | |
30 | * main() - Scan for drivers and return an IPP response. | |
31 | * add_ppd() - Add a PPD file. | |
32 | * cat_ppd() - Copy a PPD file to stdout. | |
33 | * compare_names() - Compare PPD filenames for sorting. | |
34 | * compare_ppds() - Compare PPD file make and model names for sorting. | |
35 | * list_ppds() - List PPD files. | |
36 | * load_ppds() - Load PPD files recursively. | |
37 | * load_drivers() - Load driver-generated PPD files. | |
38 | */ | |
39 | ||
40 | /* | |
41 | * Include necessary headers... | |
42 | */ | |
43 | ||
44 | #include "util.h" | |
45 | #include <cups/dir.h> | |
46 | ||
47 | ||
48 | /* | |
49 | * PPD information structures... | |
50 | */ | |
51 | ||
52 | typedef struct /**** PPD record ****/ | |
53 | { | |
54 | time_t mtime; /* Modification time */ | |
55 | size_t size; /* Size in bytes */ | |
56 | char name[512 - sizeof(time_t) - sizeof(size_t)], | |
57 | /* PPD name */ | |
58 | natural_language[128], /* Natural language(s) */ | |
59 | make[128], /* Manufacturer */ | |
bd7854cb | 60 | make_and_model[128], /* Make and model */ |
61 | device_id[128]; /* IEEE 1284 Device ID */ | |
ef416fc2 | 62 | } ppd_rec_t; |
63 | ||
64 | typedef struct /**** In-memory record ****/ | |
65 | { | |
66 | int found; /* 1 if PPD is found */ | |
67 | ppd_rec_t record; /* PPDs.dat record */ | |
68 | } ppd_info_t; | |
69 | ||
70 | ||
71 | /* | |
72 | * Globals... | |
73 | */ | |
74 | ||
75 | int NumPPDs, /* Number of PPD files */ | |
76 | SortedPPDs, /* Number of sorted PPD files */ | |
77 | AllocPPDs; /* Number of allocated entries */ | |
78 | ppd_info_t *PPDs; /* PPD file info */ | |
79 | int ChangedPPD; /* Did we change the PPD database? */ | |
80 | ||
81 | ||
82 | /* | |
83 | * Local functions... | |
84 | */ | |
85 | ||
bd7854cb | 86 | static ppd_info_t *add_ppd(const char *name, const char *natural_language, |
ef416fc2 | 87 | const char *make, const char *make_and_model, |
bd7854cb | 88 | const char *device_id, time_t mtime, size_t size); |
89 | static int cat_ppd(const char *name); | |
90 | static int compare_names(const ppd_info_t *p0, | |
91 | const ppd_info_t *p1); | |
92 | static int compare_ppds(const ppd_info_t *p0, | |
93 | const ppd_info_t *p1); | |
94 | static int list_ppds(int request_id, int limit, const char *opt); | |
95 | static int load_drivers(void); | |
96 | static int load_ppds(const char *d, const char *p); | |
ef416fc2 | 97 | |
98 | ||
99 | /* | |
100 | * 'main()' - Scan for drivers and return an IPP response. | |
101 | * | |
102 | * Usage: | |
103 | * | |
104 | * cups-driverd request_id limit options | |
105 | */ | |
106 | ||
107 | int /* O - Exit code */ | |
108 | main(int argc, /* I - Number of command-line args */ | |
109 | char *argv[]) /* I - Command-line arguments */ | |
110 | { | |
111 | /* | |
112 | * Install or list PPDs... | |
113 | */ | |
114 | ||
115 | if (argc == 3 && !strcmp(argv[1], "cat")) | |
116 | return (cat_ppd(argv[2])); | |
117 | else if (argc == 5 && !strcmp(argv[1], "list")) | |
118 | return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4])); | |
119 | else | |
120 | { | |
121 | fputs("Usage: cups-driverd cat ppd-name\n", stderr); | |
122 | fputs("Usage: cups-driverd list request_id limit options\n", stderr); | |
123 | return (1); | |
124 | } | |
125 | } | |
126 | ||
127 | ||
128 | /* | |
129 | * 'add_ppd()' - Add a PPD file. | |
130 | */ | |
131 | ||
bd7854cb | 132 | static ppd_info_t * /* O - PPD */ |
ef416fc2 | 133 | add_ppd(const char *name, /* I - PPD name */ |
134 | const char *natural_language, /* I - Language(s) */ | |
135 | const char *make, /* I - Manufacturer */ | |
136 | const char *make_and_model, /* I - NickName */ | |
bd7854cb | 137 | const char *device_id, /* I - 1284DeviceId */ |
ef416fc2 | 138 | time_t mtime, /* I - Modification time */ |
139 | size_t size) /* I - File size */ | |
140 | { | |
141 | ppd_info_t *ppd; /* PPD */ | |
142 | ||
143 | ||
144 | /* | |
145 | * Add a new PPD file... | |
146 | */ | |
147 | ||
148 | if (NumPPDs >= AllocPPDs) | |
149 | { | |
150 | /* | |
151 | * Allocate (more) memory for the PPD files... | |
152 | */ | |
153 | ||
154 | AllocPPDs += 128; | |
155 | ||
156 | if (!PPDs) | |
157 | ppd = malloc(sizeof(ppd_info_t) * AllocPPDs); | |
158 | else | |
159 | ppd = realloc(PPDs, sizeof(ppd_info_t) * AllocPPDs); | |
160 | ||
161 | if (ppd == NULL) | |
162 | { | |
163 | fprintf(stderr, "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n", | |
164 | AllocPPDs); | |
165 | return (NULL); | |
166 | } | |
167 | ||
168 | PPDs = ppd; | |
169 | } | |
170 | ||
171 | ppd = PPDs + NumPPDs; | |
172 | NumPPDs ++; | |
173 | ||
174 | /* | |
175 | * Zero-out the PPD data and copy the values over... | |
176 | */ | |
177 | ||
178 | memset(ppd, 0, sizeof(ppd_info_t)); | |
179 | ||
180 | ppd->found = 1; | |
181 | ppd->record.mtime = mtime; | |
182 | ppd->record.size = size; | |
183 | ||
184 | strlcpy(ppd->record.name, name, sizeof(ppd->record.name)); | |
185 | strlcpy(ppd->record.natural_language, natural_language, | |
186 | sizeof(ppd->record.natural_language)); | |
187 | strlcpy(ppd->record.make, make, sizeof(ppd->record.make)); | |
188 | strlcpy(ppd->record.make_and_model, make_and_model, | |
189 | sizeof(ppd->record.make_and_model)); | |
bd7854cb | 190 | strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id)); |
ef416fc2 | 191 | |
192 | /* | |
193 | * Return the new PPD pointer... | |
194 | */ | |
195 | ||
196 | return (ppd); | |
197 | } | |
198 | ||
199 | ||
200 | /* | |
201 | * 'cat_ppd()' - Copy a PPD file to stdout. | |
202 | */ | |
203 | ||
bd7854cb | 204 | static int /* O - Exit code */ |
ef416fc2 | 205 | cat_ppd(const char *name) /* I - PPD name */ |
206 | { | |
207 | char scheme[256], /* Scheme from PPD name */ | |
208 | *sptr; /* Pointer into scheme */ | |
209 | char line[1024]; /* Line/filename */ | |
210 | ||
211 | ||
212 | /* | |
213 | * Figure out if this is a static or dynamic PPD file... | |
214 | */ | |
215 | ||
216 | strlcpy(scheme, name, sizeof(scheme)); | |
217 | if ((sptr = strchr(scheme, ':')) != NULL) | |
218 | { | |
219 | *sptr = '\0'; | |
220 | ||
221 | if (!strcmp(scheme, "file")) | |
222 | { | |
223 | /* | |
224 | * "file:name" == "name"... | |
225 | */ | |
226 | ||
227 | name += 5; | |
228 | scheme[0] = '\0'; | |
229 | } | |
230 | } | |
231 | else | |
232 | scheme[0] = '\0'; | |
233 | ||
234 | if (scheme[0]) | |
235 | { | |
236 | /* | |
237 | * Dynamic PPD, see if we have a driver program to support it... | |
238 | */ | |
239 | ||
240 | const char *serverbin; /* CUPS_SERVERBIN env var */ | |
241 | ||
242 | ||
243 | if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL) | |
244 | serverbin = CUPS_SERVERBIN; | |
245 | ||
246 | snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme); | |
247 | if (access(line, X_OK)) | |
248 | { | |
249 | /* | |
250 | * File does not exist or is not executable... | |
251 | */ | |
252 | ||
253 | fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n", | |
254 | line, strerror(errno)); | |
255 | return (1); | |
256 | } | |
257 | ||
258 | /* | |
259 | * Yes, let it cat the PPD file... | |
260 | */ | |
261 | ||
262 | if (execl(line, scheme, "cat", name, (char *)NULL)) | |
263 | { | |
264 | /* | |
265 | * Unable to execute driver... | |
266 | */ | |
267 | ||
268 | fprintf(stderr, "ERROR: [cups-driverd] Unable to execute \"%s\" - %s\n", | |
269 | line, strerror(errno)); | |
270 | return (1); | |
271 | } | |
272 | } | |
273 | else | |
274 | { | |
275 | /* | |
276 | * Static PPD, see if we have a valid path and it exists... | |
277 | */ | |
278 | ||
279 | cups_file_t *fp; /* PPD file */ | |
280 | const char *datadir; /* CUPS_DATADIR env var */ | |
281 | ||
282 | ||
283 | if (name[0] == '/' || strstr(name, "../") || strstr(name, "/..")) | |
284 | { | |
285 | /* | |
286 | * Bad name... | |
287 | */ | |
288 | ||
289 | fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\"!\n", name); | |
290 | return (1); | |
291 | } | |
292 | ||
293 | /* | |
294 | * Try opening the file... | |
295 | */ | |
296 | ||
297 | if ((datadir = getenv("CUPS_DATADIR")) == NULL) | |
298 | datadir = CUPS_DATADIR; | |
299 | ||
300 | snprintf(line, sizeof(line), "%s/model/%s", datadir, name); | |
301 | if ((fp = cupsFileOpen(line, "r")) == NULL) | |
302 | { | |
303 | fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n", | |
304 | line, strerror(errno)); | |
305 | return (1); | |
306 | } | |
307 | ||
308 | /* | |
309 | * Now copy the file to stdout... | |
310 | */ | |
311 | ||
312 | while (cupsFileGets(fp, line, sizeof(line))) | |
313 | puts(line); | |
314 | ||
315 | cupsFileClose(fp); | |
316 | } | |
317 | ||
318 | /* | |
319 | * Return with no errors... | |
320 | */ | |
321 | ||
322 | return (0); | |
323 | } | |
324 | ||
325 | ||
326 | /* | |
327 | * 'compare_names()' - Compare PPD filenames for sorting. | |
328 | */ | |
329 | ||
bd7854cb | 330 | static int /* O - Result of comparison */ |
ef416fc2 | 331 | compare_names(const ppd_info_t *p0, /* I - First PPD file */ |
332 | const ppd_info_t *p1) /* I - Second PPD file */ | |
333 | { | |
334 | return (strcasecmp(p0->record.name, p1->record.name)); | |
335 | } | |
336 | ||
337 | ||
338 | /* | |
339 | * 'compare_ppds()' - Compare PPD file make and model names for sorting. | |
340 | */ | |
341 | ||
bd7854cb | 342 | static int /* O - Result of comparison */ |
ef416fc2 | 343 | compare_ppds(const ppd_info_t *p0, /* I - First PPD file */ |
344 | const ppd_info_t *p1) /* I - Second PPD file */ | |
345 | { | |
346 | int diff; /* Difference between strings */ | |
347 | ||
bd7854cb | 348 | |
ef416fc2 | 349 | /* |
350 | * First compare manufacturers... | |
351 | */ | |
352 | ||
353 | if ((diff = strcasecmp(p0->record.make, p1->record.make)) != 0) | |
354 | return (diff); | |
355 | else if ((diff = cupsdCompareNames(p0->record.make_and_model, | |
356 | p1->record.make_and_model)) != 0) | |
357 | return (diff); | |
358 | else | |
359 | return (strcasecmp(p0->record.natural_language, | |
360 | p1->record.natural_language)); | |
361 | } | |
362 | ||
363 | ||
364 | /* | |
365 | * 'list_ppds()' - List PPD files. | |
366 | */ | |
367 | ||
bd7854cb | 368 | static int /* O - Exit code */ |
ef416fc2 | 369 | list_ppds(int request_id, /* I - Request ID */ |
370 | int limit, /* I - Limit */ | |
371 | const char *opt) /* I - Option argument */ | |
372 | { | |
373 | int i; /* Looping var */ | |
374 | int count; /* Number of PPDs to send */ | |
375 | ppd_info_t *ppd; /* Current PPD file */ | |
376 | cups_file_t *fp; /* ppds.dat file */ | |
377 | struct stat fileinfo; /* ppds.dat information */ | |
378 | char filename[1024], /* ppds.dat filename */ | |
379 | model[1024]; /* Model directory */ | |
380 | const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */ | |
381 | const char *cups_datadir; /* CUPS_DATADIR environment variable */ | |
382 | int num_options; /* Number of options */ | |
383 | cups_option_t *options; /* Options */ | |
384 | const char *requested, /* requested-attributes option */ | |
385 | *make; /* ppd-make option */ | |
386 | int send_natural_language, /* Send ppd-natural-language attribute? */ | |
387 | send_make, /* Send ppd-make attribute? */ | |
388 | send_make_and_model, /* Send ppd-make-and-model attribute? */ | |
bd7854cb | 389 | send_name, /* Send ppd-name attribute? */ |
390 | send_device_id; /* Send ppd-device-id attribute? */ | |
ef416fc2 | 391 | |
392 | ||
393 | fprintf(stderr, "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, opt=\"%s\"\n", | |
394 | request_id, limit, opt); | |
395 | ||
396 | /* | |
397 | * See if we a PPD database file... | |
398 | */ | |
399 | ||
400 | NumPPDs = 0; | |
401 | AllocPPDs = 0; | |
402 | PPDs = (ppd_info_t *)NULL; | |
403 | ChangedPPD = 0; | |
404 | ||
405 | if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL) | |
406 | cups_cachedir = CUPS_CACHEDIR; | |
407 | ||
408 | snprintf(filename, sizeof(filename), "%s/ppds.dat", cups_cachedir); | |
409 | if (!stat(filename, &fileinfo) && | |
410 | (fileinfo.st_size % sizeof(ppd_rec_t)) == 0 && | |
411 | (NumPPDs = fileinfo.st_size / sizeof(ppd_rec_t)) > 0) | |
412 | { | |
413 | /* | |
414 | * We have a ppds.dat file, so read it! | |
415 | */ | |
416 | ||
417 | AllocPPDs = NumPPDs; | |
418 | ||
419 | if ((PPDs = malloc(sizeof(ppd_info_t) * NumPPDs)) == NULL) | |
420 | { | |
bd7854cb | 421 | fprintf(stderr, |
422 | "ERROR: [cups-driverd] Unable to allocate memory for %d " | |
423 | "PPD files!\n", NumPPDs); | |
ef416fc2 | 424 | NumPPDs = 0; |
425 | AllocPPDs = 0; | |
426 | } | |
427 | else if ((fp = cupsFileOpen(filename, "r")) != NULL) | |
428 | { | |
429 | for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++) | |
430 | { | |
431 | cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)); | |
432 | ppd->found = 0; | |
433 | } | |
434 | ||
435 | cupsFileClose(fp); | |
436 | ||
437 | fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n", | |
438 | filename, NumPPDs); | |
439 | } | |
440 | else | |
441 | { | |
442 | fprintf(stderr, "ERROR: [cups-driverd] Unable to read \"%s\" - %s\n", filename, | |
443 | strerror(errno)); | |
444 | NumPPDs = 0; | |
445 | } | |
446 | } | |
447 | ||
448 | /* | |
449 | * Load all PPDs in the specified directory and below... | |
450 | */ | |
451 | ||
452 | SortedPPDs = NumPPDs; | |
453 | ||
454 | if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL) | |
455 | cups_datadir = CUPS_DATADIR; | |
456 | ||
457 | snprintf(model, sizeof(model), "%s/model", cups_datadir); | |
458 | load_ppds(model, ""); | |
459 | ||
460 | /* | |
461 | * Cull PPD files that are no longer present... | |
462 | */ | |
463 | ||
464 | for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++) | |
465 | if (!ppd->found) | |
466 | { | |
467 | /* | |
468 | * Remove this PPD file from the list... | |
469 | */ | |
470 | ||
471 | if (i > 1) | |
472 | memmove(ppd, ppd + 1, (i - 1) * sizeof(ppd_info_t)); | |
473 | ||
474 | NumPPDs --; | |
475 | ppd --; | |
476 | } | |
477 | ||
478 | /* | |
479 | * Sort the PPDs by name... | |
480 | */ | |
481 | ||
482 | if (NumPPDs > 1) | |
483 | qsort(PPDs, NumPPDs, sizeof(ppd_info_t), | |
484 | (int (*)(const void *, const void *))compare_names); | |
485 | ||
486 | /* | |
487 | * Write the new ppds.dat file... | |
488 | */ | |
489 | ||
490 | if (ChangedPPD) | |
491 | { | |
492 | if ((fp = cupsFileOpen(filename, "w")) != NULL) | |
493 | { | |
494 | for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++) | |
495 | cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)); | |
496 | ||
497 | cupsFileClose(fp); | |
498 | ||
499 | fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n", | |
500 | filename, NumPPDs); | |
501 | } | |
502 | else | |
503 | fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s\n", | |
504 | filename, strerror(errno)); | |
505 | } | |
506 | else | |
507 | fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr); | |
508 | ||
509 | /* | |
510 | * Scan for dynamic PPD files... | |
511 | */ | |
512 | ||
513 | load_drivers(); | |
514 | ||
515 | /* | |
516 | * Add the raw driver... | |
517 | */ | |
518 | ||
bd7854cb | 519 | add_ppd("raw", "en", "Raw", "Raw Queue", "", 0, 0); |
ef416fc2 | 520 | |
521 | /* | |
522 | * Sort the PPDs by make and model... | |
523 | */ | |
524 | ||
525 | if (NumPPDs > 1) | |
526 | qsort(PPDs, NumPPDs, sizeof(ppd_info_t), | |
527 | (int (*)(const void *, const void *))compare_ppds); | |
528 | ||
529 | /* | |
530 | * Send IPP attributes... | |
531 | */ | |
532 | ||
533 | num_options = cupsParseOptions(opt, 0, &options); | |
534 | requested = cupsGetOption("requested-attributes", num_options, options); | |
535 | make = cupsGetOption("ppd-make", num_options, options); | |
536 | ||
537 | fprintf(stderr, "DEBUG: [cups-driverd] requested=\"%s\"\n", | |
538 | requested ? requested : "(nil)"); | |
539 | ||
540 | if (!requested || strstr(requested, "all")) | |
541 | { | |
542 | send_name = 1; | |
543 | send_make = 1; | |
544 | send_make_and_model = 1; | |
545 | send_natural_language = 1; | |
bd7854cb | 546 | send_device_id = 1; |
ef416fc2 | 547 | } |
548 | else | |
549 | { | |
550 | send_name = strstr(requested, "ppd-name") != NULL; | |
551 | send_make = strstr(requested, "ppd-make,") != NULL || | |
552 | strstr(requested, ",ppd-make") != NULL || | |
553 | !strcmp(requested, "ppd-make"); | |
554 | send_make_and_model = strstr(requested, "ppd-make-and-model") != NULL; | |
555 | send_natural_language = strstr(requested, "ppd-natural-language") != NULL; | |
bd7854cb | 556 | send_device_id = strstr(requested, "ppd-device-id") != NULL; |
ef416fc2 | 557 | } |
558 | ||
559 | puts("Content-Type: application/ipp\n"); | |
560 | ||
561 | cupsdSendIPPHeader(IPP_OK, request_id); | |
562 | cupsdSendIPPGroup(IPP_TAG_OPERATION); | |
563 | cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8"); | |
564 | cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US"); | |
565 | ||
566 | if (limit <= 0 || limit > NumPPDs) | |
567 | count = NumPPDs; | |
568 | else | |
569 | count = limit; | |
570 | ||
571 | for (i = NumPPDs, ppd = PPDs; count > 0 && i > 0; i --, ppd ++) | |
572 | if (!make || !strcasecmp(ppd->record.make, make)) | |
573 | { | |
574 | /* | |
575 | * Send this PPD... | |
576 | */ | |
577 | ||
578 | fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n", | |
579 | ppd->record.name, ppd->record.make_and_model); | |
580 | ||
581 | count --; | |
582 | ||
583 | cupsdSendIPPGroup(IPP_TAG_PRINTER); | |
584 | ||
585 | if (send_name) | |
586 | cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name); | |
587 | ||
588 | if (send_natural_language) | |
589 | cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language", | |
590 | ppd->record.natural_language); | |
591 | ||
592 | if (send_make) | |
593 | cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make); | |
594 | ||
595 | if (send_make_and_model) | |
596 | cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model", | |
597 | ppd->record.make_and_model); | |
598 | ||
bd7854cb | 599 | if (send_device_id) |
600 | cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id", | |
601 | ppd->record.device_id); | |
602 | ||
ef416fc2 | 603 | /* |
604 | * If we have only requested the ppd-make attribute, then skip | |
605 | * the remaining PPDs with this make... | |
606 | */ | |
607 | ||
608 | if (requested && !strcmp(requested, "ppd-make")) | |
609 | { | |
610 | const char *this_make; /* This ppd-make */ | |
611 | ||
612 | ||
613 | for (this_make = ppd->record.make, i --, ppd ++; i > 0; i --, ppd ++) | |
614 | if (strcasecmp(this_make, ppd->record.make)) | |
615 | break; | |
616 | ||
617 | i ++; | |
618 | ppd --; | |
619 | } | |
620 | } | |
621 | ||
622 | cupsdSendIPPTrailer(); | |
623 | ||
624 | return (0); | |
625 | } | |
626 | ||
627 | ||
628 | /* | |
629 | * 'load_ppds()' - Load PPD files recursively. | |
630 | */ | |
631 | ||
bd7854cb | 632 | static int /* O - 1 on success, 0 on failure */ |
ef416fc2 | 633 | load_ppds(const char *d, /* I - Actual directory */ |
634 | const char *p) /* I - Virtual path in name */ | |
635 | { | |
636 | int i; /* Looping var */ | |
637 | cups_file_t *fp; /* Pointer to file */ | |
638 | cups_dir_t *dir; /* Directory pointer */ | |
639 | cups_dentry_t *dent; /* Directory entry */ | |
640 | char filename[1024], /* Name of PPD or directory */ | |
641 | line[256], /* Line from backend */ | |
642 | *ptr, /* Pointer into name */ | |
643 | name[128], /* Name of PPD file */ | |
644 | language[64], /* PPD language version */ | |
645 | country[64], /* Country code */ | |
646 | manufacturer[256], /* Manufacturer */ | |
647 | make_model[256], /* Make and Model */ | |
648 | model_name[256], /* ModelName */ | |
bd7854cb | 649 | nick_name[256], /* NickName */ |
650 | device_id[256]; /* 1284DeviceId */ | |
ef416fc2 | 651 | ppd_info_t *ppd, /* New PPD file */ |
652 | key; /* Search key */ | |
653 | int new_ppd; /* Is this a new PPD? */ | |
654 | struct /* LanguageVersion translation table */ | |
655 | { | |
656 | const char *version, /* LanguageVersion string */ | |
657 | *language; /* Language code */ | |
658 | } languages[] = | |
659 | { | |
660 | { "chinese", "cn" }, | |
661 | { "danish", "da" }, | |
662 | { "dutch", "nl" }, | |
663 | { "english", "en" }, | |
664 | { "finnish", "fi" }, | |
665 | { "french", "fr" }, | |
666 | { "german", "de" }, | |
667 | { "greek", "el" }, | |
668 | { "italian", "it" }, | |
669 | { "japanese", "jp" }, | |
670 | { "norwegian", "no" }, | |
671 | { "polish", "pl" }, | |
672 | { "portuguese", "pt" }, | |
673 | { "russian", "ru" }, | |
674 | { "slovak", "sk" }, | |
675 | { "spanish", "es" }, | |
676 | { "swedish", "sv" }, | |
677 | { "turkish", "tr" } | |
678 | }; | |
679 | ||
680 | ||
681 | if ((dir = cupsDirOpen(d)) == NULL) | |
682 | { | |
683 | fprintf(stderr, "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n", | |
684 | d, strerror(errno)); | |
685 | return (0); | |
686 | } | |
687 | ||
688 | while ((dent = cupsDirRead(dir)) != NULL) | |
689 | { | |
bd7854cb | 690 | /* |
691 | * Skip files/directories starting with "."... | |
692 | */ | |
693 | ||
694 | if (dent->filename[0] == '.') | |
695 | continue; | |
696 | ||
ef416fc2 | 697 | /* |
698 | * See if this is a file... | |
699 | */ | |
700 | ||
701 | snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename); | |
702 | ||
703 | if (p[0]) | |
704 | snprintf(name, sizeof(name), "%s/%s", p, dent->filename); | |
705 | else | |
706 | strlcpy(name, dent->filename, sizeof(name)); | |
707 | ||
708 | if (S_ISDIR(dent->fileinfo.st_mode)) | |
709 | { | |
710 | /* | |
711 | * Do subdirectory... | |
712 | */ | |
713 | ||
714 | if (!load_ppds(filename, name)) | |
715 | { | |
716 | cupsDirClose(dir); | |
717 | return (1); | |
718 | } | |
719 | ||
720 | continue; | |
721 | } | |
722 | ||
723 | /* | |
724 | * See if this file has been scanned before... | |
725 | */ | |
726 | ||
727 | if (SortedPPDs > 0) | |
728 | { | |
729 | strcpy(key.record.name, name); | |
730 | ||
731 | ppd = bsearch(&key, PPDs, SortedPPDs, sizeof(ppd_info_t), | |
732 | (int (*)(const void *, const void *))compare_names); | |
733 | ||
734 | if (ppd && | |
735 | ppd->record.size == dent->fileinfo.st_size && | |
736 | ppd->record.mtime == dent->fileinfo.st_mtime) | |
737 | { | |
738 | ppd->found = 1; | |
739 | continue; | |
740 | } | |
741 | } | |
742 | else | |
743 | ppd = NULL; | |
744 | ||
745 | /* | |
746 | * No, file is new/changed, so re-scan it... | |
747 | */ | |
748 | ||
749 | if ((fp = cupsFileOpen(filename, "r")) == NULL) | |
750 | continue; | |
751 | ||
752 | /* | |
753 | * Now see if this is a PPD file... | |
754 | */ | |
755 | ||
756 | line[0] = '\0'; | |
757 | cupsFileGets(fp, line, sizeof(line)); | |
758 | ||
759 | if (strncmp(line, "*PPD-Adobe:", 11)) | |
760 | { | |
761 | /* | |
762 | * Nope, close the file and continue... | |
763 | */ | |
764 | ||
765 | cupsFileClose(fp); | |
766 | ||
767 | continue; | |
768 | } | |
769 | ||
770 | /* | |
771 | * Now read until we get the NickName field... | |
772 | */ | |
773 | ||
774 | model_name[0] = '\0'; | |
775 | nick_name[0] = '\0'; | |
776 | manufacturer[0] = '\0'; | |
bd7854cb | 777 | device_id[0] = '\0'; |
ef416fc2 | 778 | strcpy(language, "en"); |
779 | ||
780 | while (cupsFileGets(fp, line, sizeof(line)) != NULL) | |
781 | { | |
782 | if (!strncmp(line, "*Manufacturer:", 14)) | |
783 | sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer); | |
784 | else if (!strncmp(line, "*ModelName:", 11)) | |
785 | sscanf(line, "%*[^\"]\"%127[^\"]", model_name); | |
786 | else if (!strncmp(line, "*LanguageVersion:", 17)) | |
787 | sscanf(line, "%*[^:]:%63s", language); | |
788 | else if (!strncmp(line, "*NickName:", 10)) | |
789 | sscanf(line, "%*[^\"]\"%255[^\"]", nick_name); | |
bd7854cb | 790 | else if (!strncmp(line, "*1284DeviceId:", 14)) |
791 | sscanf(line, "%*[^\"]\"%255[^\"]", device_id); | |
ef416fc2 | 792 | else if (!strncmp(line, "*OpenUI", 7)) |
793 | { | |
794 | /* | |
795 | * Stop early if we have a NickName or ModelName attributes | |
796 | * before the first OpenUI... | |
797 | */ | |
798 | ||
799 | if (model_name[0] || nick_name[0]) | |
800 | break; | |
801 | } | |
ef416fc2 | 802 | } |
803 | ||
804 | /* | |
805 | * Close the file... | |
806 | */ | |
807 | ||
808 | cupsFileClose(fp); | |
809 | ||
810 | /* | |
811 | * See if we got all of the required info... | |
812 | */ | |
813 | ||
814 | if (nick_name[0]) | |
815 | strcpy(make_model, nick_name); | |
816 | else | |
817 | strcpy(make_model, model_name); | |
818 | ||
819 | while (isspace(make_model[0] & 255)) | |
820 | _cups_strcpy(make_model, make_model + 1); | |
821 | ||
822 | if (!make_model[0]) | |
823 | continue; /* Nope... */ | |
824 | ||
825 | /* | |
826 | * See if we got a manufacturer... | |
827 | */ | |
828 | ||
829 | while (isspace(manufacturer[0] & 255)) | |
830 | _cups_strcpy(manufacturer, manufacturer + 1); | |
831 | ||
832 | if (!manufacturer[0] || !strcmp(manufacturer, "ESP")) | |
833 | { | |
834 | /* | |
835 | * Nope, copy the first part of the make and model then... | |
836 | */ | |
837 | ||
838 | strlcpy(manufacturer, make_model, sizeof(manufacturer)); | |
839 | ||
840 | /* | |
841 | * Truncate at the first space, dash, or slash, or make the | |
842 | * manufacturer "Other"... | |
843 | */ | |
844 | ||
845 | for (ptr = manufacturer; *ptr; ptr ++) | |
846 | if (*ptr == ' ' || *ptr == '-' || *ptr == '/') | |
847 | break; | |
848 | ||
849 | if (*ptr && ptr > manufacturer) | |
850 | *ptr = '\0'; | |
851 | else if (!strncasecmp(manufacturer, "agfa", 4)) | |
852 | strcpy(manufacturer, "AGFA"); | |
853 | else if (!strncasecmp(manufacturer, "herk", 4) || | |
854 | !strncasecmp(manufacturer, "linotype", 8)) | |
855 | strcpy(manufacturer, "LHAG"); | |
856 | else | |
857 | strcpy(manufacturer, "Other"); | |
858 | ||
859 | /* | |
860 | * Hack for various vendors... | |
861 | */ | |
862 | ||
863 | if (!strcasecmp(manufacturer, "XPrint")) | |
864 | strcpy(manufacturer, "Xerox"); | |
865 | else if (!strcasecmp(manufacturer, "Eastman")) | |
866 | strcpy(manufacturer, "Kodak"); | |
867 | else if (!strcasecmp(manufacturer, "laserwriter")) | |
868 | strcpy(manufacturer, "Apple"); | |
869 | else if (!strcasecmp(manufacturer, "colorpoint")) | |
870 | strcpy(manufacturer, "Seiko"); | |
871 | else if (!strcasecmp(manufacturer, "fiery")) | |
872 | strcpy(manufacturer, "EFI"); | |
873 | else if (!strcasecmp(manufacturer, "ps") || | |
874 | !strcasecmp(manufacturer, "colorpass")) | |
875 | strcpy(manufacturer, "Canon"); | |
876 | else if (!strncasecmp(manufacturer, "primera", 7)) | |
877 | strcpy(manufacturer, "Fargo"); | |
878 | else if (!strcasecmp(manufacturer, "designjet")) | |
879 | strcpy(manufacturer, "HP"); | |
880 | } | |
881 | else if (!strncasecmp(manufacturer, "LHAG", 4) || | |
882 | !strncasecmp(manufacturer, "linotype", 8)) | |
883 | strcpy(manufacturer, "LHAG"); | |
884 | ||
885 | /* | |
886 | * Fix the language as needed... | |
887 | */ | |
888 | ||
889 | if ((ptr = strchr(language, '-')) != NULL) | |
890 | *ptr++ = '\0'; | |
891 | else if ((ptr = strchr(language, '_')) != NULL) | |
892 | *ptr++ = '\0'; | |
893 | ||
894 | if (ptr) | |
895 | { | |
896 | /* | |
897 | * Setup the country suffix... | |
898 | */ | |
899 | ||
900 | country[0] = '_'; | |
901 | _cups_strcpy(country + 1, ptr); | |
902 | } | |
903 | else | |
904 | { | |
905 | /* | |
906 | * No country suffix... | |
907 | */ | |
908 | ||
909 | country[0] = '\0'; | |
910 | } | |
911 | ||
912 | for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++) | |
913 | if (!strcasecmp(languages[i].version, language)) | |
914 | break; | |
915 | ||
916 | if (i < (int)(sizeof(languages) / sizeof(languages[0]))) | |
917 | { | |
918 | /* | |
919 | * Found a known language... | |
920 | */ | |
921 | ||
922 | snprintf(language, sizeof(language), "%s%s", languages[i].language, | |
923 | country); | |
924 | } | |
925 | else | |
926 | { | |
927 | /* | |
928 | * Unknown language; use "xx"... | |
929 | */ | |
930 | ||
931 | strcpy(language, "xx"); | |
932 | } | |
933 | ||
934 | /* | |
935 | * Add the PPD file... | |
936 | */ | |
937 | ||
938 | new_ppd = !ppd; | |
939 | ||
940 | if (new_ppd) | |
941 | { | |
942 | /* | |
943 | * Add new PPD file... | |
944 | */ | |
945 | ||
946 | fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name); | |
947 | ||
bd7854cb | 948 | if (!add_ppd(name, language, manufacturer, make_model, device_id, |
ef416fc2 | 949 | dent->fileinfo.st_mtime, dent->fileinfo.st_size)) |
950 | { | |
951 | cupsDirClose(dir); | |
952 | return (0); | |
953 | } | |
954 | } | |
955 | else | |
956 | { | |
957 | /* | |
958 | * Update existing record... | |
959 | */ | |
960 | ||
961 | fprintf(stderr, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name); | |
962 | ||
963 | memset(ppd, 0, sizeof(ppd_info_t)); | |
964 | ||
965 | ppd->found = 1; | |
966 | ppd->record.mtime = dent->fileinfo.st_mtime; | |
967 | ppd->record.size = dent->fileinfo.st_size; | |
968 | ||
969 | strlcpy(ppd->record.name, name, sizeof(ppd->record.name)); | |
970 | strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make)); | |
971 | strlcpy(ppd->record.make_and_model, make_model, | |
972 | sizeof(ppd->record.make_and_model)); | |
973 | strlcpy(ppd->record.natural_language, language, | |
974 | sizeof(ppd->record.natural_language)); | |
bd7854cb | 975 | strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id)); |
ef416fc2 | 976 | } |
977 | ||
978 | ChangedPPD = 1; | |
979 | } | |
980 | ||
981 | cupsDirClose(dir); | |
982 | ||
983 | return (1); | |
984 | } | |
985 | ||
986 | ||
987 | /* | |
988 | * 'load_drivers()' - Load driver-generated PPD files. | |
989 | */ | |
990 | ||
bd7854cb | 991 | static int /* O - 1 on success, 0 on failure */ |
ef416fc2 | 992 | load_drivers(void) |
993 | { | |
994 | const char *server_bin; /* CUPS_SERVERBIN environment variable */ | |
995 | char drivers[1024]; /* Location of driver programs */ | |
996 | FILE *fp; /* Pipe to driver program */ | |
997 | cups_dir_t *dir; /* Directory pointer */ | |
998 | cups_dentry_t *dent; /* Directory entry */ | |
999 | char filename[1024], /* Name of driver */ | |
1000 | line[2048], /* Line from driver */ | |
1001 | name[512], /* ppd-name */ | |
1002 | natural_language[128], /* ppd-natural-language */ | |
1003 | make[128], /* ppd-make */ | |
bd7854cb | 1004 | make_and_model[256], /* ppd-make-and-model */ |
1005 | device_id[256]; /* ppd-device-id */ | |
ef416fc2 | 1006 | |
1007 | ||
1008 | /* | |
1009 | * Try opening the driver directory... | |
1010 | */ | |
1011 | ||
1012 | if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL) | |
1013 | server_bin = CUPS_SERVERBIN; | |
1014 | ||
1015 | snprintf(drivers, sizeof(drivers), "%s/driver", server_bin); | |
1016 | ||
1017 | if ((dir = cupsDirOpen(drivers)) == NULL) | |
1018 | { | |
1019 | fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory " | |
1020 | "\"%s\": %s\n", | |
1021 | drivers, strerror(errno)); | |
1022 | return (0); | |
1023 | } | |
1024 | ||
1025 | /* | |
1026 | * Loop through all of the device drivers... | |
1027 | */ | |
1028 | ||
1029 | while ((dent = cupsDirRead(dir)) != NULL) | |
1030 | { | |
1031 | /* | |
1032 | * Only look at executable files... | |
1033 | */ | |
1034 | ||
1035 | if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode)) | |
1036 | continue; | |
1037 | ||
1038 | /* | |
1039 | * Run the driver with no arguments and collect the output... | |
1040 | */ | |
1041 | ||
bd7854cb | 1042 | snprintf(filename, sizeof(filename), "%s/%s list", drivers, dent->filename); |
ef416fc2 | 1043 | if ((fp = popen(filename, "r")) != NULL) |
1044 | { | |
1045 | while (fgets(line, sizeof(line), fp) != NULL) | |
1046 | { | |
1047 | /* | |
1048 | * Each line is of the form: | |
1049 | * | |
1050 | * \"ppd-name\" ppd-natural-language "ppd-make" "ppd-make-and-model" | |
1051 | */ | |
1052 | ||
bd7854cb | 1053 | device_id[0] = '\0'; |
1054 | ||
1055 | if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\"" | |
1056 | "%*[ \t]\"%256[^\"]\"%*[ \t]\"%256[^\"]\"", | |
1057 | name, natural_language, make, make_and_model, | |
1058 | device_id) < 4) | |
ef416fc2 | 1059 | { |
1060 | /* | |
1061 | * Bad format; strip trailing newline and write an error message. | |
1062 | */ | |
1063 | ||
1064 | if (line[strlen(line) - 1] == '\n') | |
1065 | line[strlen(line) - 1] = '\0'; | |
1066 | ||
1067 | fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n", | |
1068 | dent->filename, line); | |
1069 | break; | |
1070 | } | |
1071 | else | |
1072 | { | |
1073 | /* | |
1074 | * Add the device to the array of available devices... | |
1075 | */ | |
1076 | ||
bd7854cb | 1077 | if (!add_ppd(name, natural_language, make, make_and_model, device_id, |
1078 | 0, 0)) | |
ef416fc2 | 1079 | { |
1080 | cupsDirClose(dir); | |
1081 | return (0); | |
1082 | } | |
1083 | ||
1084 | fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n", | |
1085 | name); | |
1086 | } | |
1087 | } | |
1088 | ||
1089 | pclose(fp); | |
1090 | } | |
1091 | else | |
1092 | fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n", | |
1093 | filename, strerror(errno)); | |
1094 | } | |
1095 | ||
1096 | cupsDirClose(dir); | |
1097 | ||
1098 | return (1); | |
1099 | } | |
1100 | ||
1101 | ||
1102 | /* | |
bd7854cb | 1103 | * End of "$Id: cups-driverd.c 5130 2006-02-17 20:25:33Z mike $". |
ef416fc2 | 1104 | */ |