1 /*
2  * PPD test program for CUPS.
3  *
4  * Copyright 2007-2017 by Apple Inc.
5  * Copyright 1997-2006 by Easy Software Products.
6  *
7  * These coded instructions, statements, and computer programs are the
8  * property of Apple Inc. and are protected by Federal copyright
9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10  * which should have been included with this file.  If this file is
11  * missing or damaged, see the license at "http://www.cups.org/".
12  *
13  * This file is subject to the Apple OS-Developed Software exception.
14  */
15 
16 /*
17  * Include necessary headers...
18  */
19 
20 #undef _CUPS_NO_DEPRECATED
21 #include "cups-private.h"
22 #include "ppd-private.h"
23 #include <sys/stat.h>
24 #ifdef WIN32
25 #  include <io.h>
26 #else
27 #  include <unistd.h>
28 #  include <fcntl.h>
29 #endif /* WIN32 */
30 #include <math.h>
31 
32 
33 /*
34  * Test data...
35  */
36 
37 static const char	*default_code =
38 			"[{\n"
39 			"%%BeginFeature: *InstalledDuplexer False\n"
40 			"%%EndFeature\n"
41 			"} stopped cleartomark\n"
42 			"[{\n"
43 			"%%BeginFeature: *PageRegion Letter\n"
44 			"PageRegion=Letter\n"
45 			"%%EndFeature\n"
46 			"} stopped cleartomark\n"
47 			"[{\n"
48 			"%%BeginFeature: *InputSlot Tray\n"
49 			"InputSlot=Tray\n"
50 			"%%EndFeature\n"
51 			"} stopped cleartomark\n"
52 			"[{\n"
53 			"%%BeginFeature: *OutputBin Tray1\n"
54 			"OutputBin=Tray1\n"
55 			"%%EndFeature\n"
56 			"} stopped cleartomark\n"
57 			"[{\n"
58 			"%%BeginFeature: *MediaType Plain\n"
59 			"MediaType=Plain\n"
60 			"%%EndFeature\n"
61 			"} stopped cleartomark\n"
62 			"[{\n"
63 			"%%BeginFeature: *IntOption None\n"
64 			"%%EndFeature\n"
65 			"} stopped cleartomark\n"
66 			"[{\n"
67 			"%%BeginFeature: *StringOption None\n"
68 			"%%EndFeature\n"
69 			"} stopped cleartomark\n";
70 
71 static const char	*custom_code =
72 			"[{\n"
73 			"%%BeginFeature: *InstalledDuplexer False\n"
74 			"%%EndFeature\n"
75 			"} stopped cleartomark\n"
76 			"[{\n"
77 			"%%BeginFeature: *InputSlot Tray\n"
78 			"InputSlot=Tray\n"
79 			"%%EndFeature\n"
80 			"} stopped cleartomark\n"
81 			"[{\n"
82 			"%%BeginFeature: *MediaType Plain\n"
83 			"MediaType=Plain\n"
84 			"%%EndFeature\n"
85 			"} stopped cleartomark\n"
86 			"[{\n"
87 			"%%BeginFeature: *OutputBin Tray1\n"
88 			"OutputBin=Tray1\n"
89 			"%%EndFeature\n"
90 			"} stopped cleartomark\n"
91 			"[{\n"
92 			"%%BeginFeature: *IntOption None\n"
93 			"%%EndFeature\n"
94 			"} stopped cleartomark\n"
95 			"[{\n"
96 			"%%BeginFeature: *CustomStringOption True\n"
97 			"(value\\0502\\051)\n"
98 			"(value 1)\n"
99 			"StringOption=Custom\n"
100 			"%%EndFeature\n"
101 			"} stopped cleartomark\n"
102 			"[{\n"
103 			"%%BeginFeature: *CustomPageSize True\n"
104 			"400\n"
105 			"500\n"
106 			"0\n"
107 			"0\n"
108 			"0\n"
109 			"PageSize=Custom\n"
110 			"%%EndFeature\n"
111 			"} stopped cleartomark\n";
112 
113 static const char	*default2_code =
114 			"[{\n"
115 			"%%BeginFeature: *InstalledDuplexer False\n"
116 			"%%EndFeature\n"
117 			"} stopped cleartomark\n"
118 			"[{\n"
119 			"%%BeginFeature: *InputSlot Tray\n"
120 			"InputSlot=Tray\n"
121 			"%%EndFeature\n"
122 			"} stopped cleartomark\n"
123 			"[{\n"
124 			"%%BeginFeature: *Quality Normal\n"
125 			"Quality=Normal\n"
126 			"%%EndFeature\n"
127 			"} stopped cleartomark\n"
128 			"[{\n"
129 			"%%BeginFeature: *IntOption None\n"
130 			"%%EndFeature\n"
131 			"} stopped cleartomark\n"
132 			"[{\n"
133 			"%%BeginFeature: *StringOption None\n"
134 			"%%EndFeature\n"
135 			"} stopped cleartomark\n";
136 
137 
138 /*
139  * 'main()' - Main entry.
140  */
141 
142 int					/* O - Exit status */
main(int argc,char * argv[])143 main(int  argc,				/* I - Number of command-line arguments */
144      char *argv[])			/* I - Command-line arguments */
145 {
146   int		i;			/* Looping var */
147   ppd_file_t	*ppd;			/* PPD file loaded from disk */
148   int		status;			/* Status of tests (0 = success, 1 = fail) */
149   int		conflicts;		/* Number of conflicts */
150   char		*s;			/* String */
151   char		buffer[8192];		/* String buffer */
152   const char	*text,			/* Localized text */
153 		*val;			/* Option value */
154   int		num_options;		/* Number of options */
155   cups_option_t	*options;		/* Options */
156   ppd_size_t	minsize,		/* Minimum size */
157 		maxsize,		/* Maximum size */
158 		*size;			/* Current size */
159   ppd_attr_t	*attr;			/* Current attribute */
160   _ppd_cache_t	*pc;			/* PPD cache */
161 
162 
163   status = 0;
164 
165   if (argc == 1)
166   {
167    /*
168     * Setup directories for locale stuff...
169     */
170 
171     if (access("locale", 0))
172     {
173       mkdir("locale", 0777);
174       mkdir("locale/fr", 0777);
175       symlink("../../../locale/cups_fr.po", "locale/fr/cups_fr.po");
176       mkdir("locale/zh_TW", 0777);
177       symlink("../../../locale/cups_zh_TW.po", "locale/zh_TW/cups_zh_TW.po");
178     }
179 
180     putenv("LOCALEDIR=locale");
181     putenv("SOFTWARE=CUPS");
182 
183    /*
184     * Do tests with test.ppd...
185     */
186 
187     fputs("ppdOpenFile(test.ppd): ", stdout);
188 
189     if ((ppd = _ppdOpenFile("test.ppd", _PPD_LOCALIZATION_ALL)) != NULL)
190       puts("PASS");
191     else
192     {
193       ppd_status_t	err;		/* Last error in file */
194       int		line;		/* Line number in file */
195 
196 
197       status ++;
198       err = ppdLastError(&line);
199 
200       printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
201     }
202 
203     fputs("ppdFindAttr(wildcard): ", stdout);
204     if ((attr = ppdFindAttr(ppd, "cupsTest", NULL)) == NULL)
205     {
206       status ++;
207       puts("FAIL (not found)");
208     }
209     else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo"))
210     {
211       status ++;
212       printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
213     }
214     else
215       puts("PASS");
216 
217     fputs("ppdFindNextAttr(wildcard): ", stdout);
218     if ((attr = ppdFindNextAttr(ppd, "cupsTest", NULL)) == NULL)
219     {
220       status ++;
221       puts("FAIL (not found)");
222     }
223     else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Bar"))
224     {
225       status ++;
226       printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
227     }
228     else
229       puts("PASS");
230 
231     fputs("ppdFindAttr(Foo): ", stdout);
232     if ((attr = ppdFindAttr(ppd, "cupsTest", "Foo")) == NULL)
233     {
234       status ++;
235       puts("FAIL (not found)");
236     }
237     else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo"))
238     {
239       status ++;
240       printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
241     }
242     else
243       puts("PASS");
244 
245     fputs("ppdFindNextAttr(Foo): ", stdout);
246     if ((attr = ppdFindNextAttr(ppd, "cupsTest", "Foo")) != NULL)
247     {
248       status ++;
249       printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
250     }
251     else
252       puts("PASS");
253 
254     fputs("ppdMarkDefaults: ", stdout);
255     ppdMarkDefaults(ppd);
256 
257     if ((conflicts = ppdConflicts(ppd)) == 0)
258       puts("PASS");
259     else
260     {
261       status ++;
262       printf("FAIL (%d conflicts)\n", conflicts);
263     }
264 
265     fputs("ppdEmitString (defaults): ", stdout);
266     if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
267 	!strcmp(s, default_code))
268       puts("PASS");
269     else
270     {
271       status ++;
272       printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
273 	     (int)strlen(default_code));
274 
275       if (s)
276 	puts(s);
277     }
278 
279     if (s)
280       free(s);
281 
282     fputs("ppdEmitString (custom size and string): ", stdout);
283     ppdMarkOption(ppd, "PageSize", "Custom.400x500");
284     ppdMarkOption(ppd, "StringOption", "{String1=\"value 1\" String2=value(2)}");
285 
286     if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
287 	!strcmp(s, custom_code))
288       puts("PASS");
289     else
290     {
291       status ++;
292       printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
293 	     (int)strlen(custom_code));
294 
295       if (s)
296 	puts(s);
297     }
298 
299     if (s)
300       free(s);
301 
302    /*
303     * Test constraints...
304     */
305 
306     fputs("cupsGetConflicts(InputSlot=Envelope): ", stdout);
307     ppdMarkOption(ppd, "PageSize", "Letter");
308 
309     num_options = cupsGetConflicts(ppd, "InputSlot", "Envelope", &options);
310     if (num_options != 2 ||
311         (val = cupsGetOption("PageRegion", num_options, options)) == NULL ||
312 	_cups_strcasecmp(val, "Letter") ||
313 	(val = cupsGetOption("PageSize", num_options, options)) == NULL ||
314 	_cups_strcasecmp(val, "Letter"))
315     {
316       printf("FAIL (%d options:", num_options);
317       for (i = 0; i < num_options; i ++)
318         printf(" %s=%s", options[i].name, options[i].value);
319       puts(")");
320       status ++;
321     }
322     else
323       puts("PASS");
324 
325     fputs("ppdConflicts(): ", stdout);
326     ppdMarkOption(ppd, "InputSlot", "Envelope");
327 
328     if ((conflicts = ppdConflicts(ppd)) == 2)
329       puts("PASS (2)");
330     else
331     {
332       printf("FAIL (%d)\n", conflicts);
333       status ++;
334     }
335 
336     fputs("cupsResolveConflicts(InputSlot=Envelope): ", stdout);
337     num_options = 0;
338     options     = NULL;
339     if (!cupsResolveConflicts(ppd, "InputSlot", "Envelope", &num_options,
340                              &options))
341     {
342       puts("FAIL (Unable to resolve)");
343       status ++;
344     }
345     else if (num_options != 2 ||
346              !cupsGetOption("PageSize", num_options, options))
347     {
348       printf("FAIL (%d options:", num_options);
349       for (i = 0; i < num_options; i ++)
350         printf(" %s=%s", options[i].name, options[i].value);
351       puts(")");
352       status ++;
353     }
354     else
355       puts("PASS (Resolved by changing PageSize)");
356 
357     cupsFreeOptions(num_options, options);
358 
359     fputs("cupsResolveConflicts(No option/choice): ", stdout);
360     num_options = 0;
361     options     = NULL;
362     if (cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options) &&
363         num_options == 1 && !_cups_strcasecmp(options[0].name, "InputSlot") &&
364 	!_cups_strcasecmp(options[0].value, "Tray"))
365       puts("PASS (Resolved by changing InputSlot)");
366     else if (num_options > 0)
367     {
368       printf("FAIL (%d options:", num_options);
369       for (i = 0; i < num_options; i ++)
370         printf(" %s=%s", options[i].name, options[i].value);
371       puts(")");
372       status ++;
373     }
374     else
375     {
376       puts("FAIL (Unable to resolve)");
377       status ++;
378     }
379     cupsFreeOptions(num_options, options);
380 
381     fputs("ppdInstallableConflict(): ", stdout);
382     if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") &&
383         !ppdInstallableConflict(ppd, "Duplex", "None"))
384       puts("PASS");
385     else if (!ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble"))
386     {
387       puts("FAIL (Duplex=DuplexNoTumble did not conflict)");
388       status ++;
389     }
390     else
391     {
392       puts("FAIL (Duplex=None conflicted)");
393       status ++;
394     }
395 
396    /*
397     * ppdPageSizeLimits
398     */
399 
400     fputs("ppdPageSizeLimits: ", stdout);
401     if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
402     {
403       if (fabs(minsize.width - 36.0) > 0.001 || fabs(minsize.length - 36.0) > 0.001 ||
404           fabs(maxsize.width - 1080.0) > 0.001 || fabs(maxsize.length - 86400.0) > 0.001)
405       {
406         printf("FAIL (got min=%.3fx%.3f, max=%.3fx%.3f, "
407 	       "expected min=36x36, max=1080x86400)\n", minsize.width,
408 	       minsize.length, maxsize.width, maxsize.length);
409         status ++;
410       }
411       else
412         puts("PASS");
413     }
414     else
415     {
416       puts("FAIL (returned 0)");
417       status ++;
418     }
419 
420    /*
421     * cupsMarkOptions with PWG and IPP size names.
422     */
423 
424     fputs("cupsMarkOptions(media=iso-a4): ", stdout);
425     num_options = cupsAddOption("media", "iso-a4", 0, &options);
426     cupsMarkOptions(ppd, num_options, options);
427     cupsFreeOptions(num_options, options);
428 
429     size = ppdPageSize(ppd, NULL);
430     if (!size || strcmp(size->name, "A4"))
431     {
432       printf("FAIL (%s)\n", size ? size->name : "unknown");
433       status ++;
434     }
435     else
436       puts("PASS");
437 
438     fputs("cupsMarkOptions(media=na_letter_8.5x11in): ", stdout);
439     num_options = cupsAddOption("media", "na_letter_8.5x11in", 0, &options);
440     cupsMarkOptions(ppd, num_options, options);
441     cupsFreeOptions(num_options, options);
442 
443     size = ppdPageSize(ppd, NULL);
444     if (!size || strcmp(size->name, "Letter"))
445     {
446       printf("FAIL (%s)\n", size ? size->name : "unknown");
447       status ++;
448     }
449     else
450       puts("PASS");
451 
452     fputs("cupsMarkOptions(media=oe_letter-fullbleed_8.5x11in): ", stdout);
453     num_options = cupsAddOption("media", "oe_letter-fullbleed_8.5x11in", 0,
454                                 &options);
455     cupsMarkOptions(ppd, num_options, options);
456     cupsFreeOptions(num_options, options);
457 
458     size = ppdPageSize(ppd, NULL);
459     if (!size || strcmp(size->name, "Letter.Fullbleed"))
460     {
461       printf("FAIL (%s)\n", size ? size->name : "unknown");
462       status ++;
463     }
464     else
465       puts("PASS");
466 
467     fputs("cupsMarkOptions(media=A4): ", stdout);
468     num_options = cupsAddOption("media", "A4", 0, &options);
469     cupsMarkOptions(ppd, num_options, options);
470     cupsFreeOptions(num_options, options);
471 
472     size = ppdPageSize(ppd, NULL);
473     if (!size || strcmp(size->name, "A4"))
474     {
475       printf("FAIL (%s)\n", size ? size->name : "unknown");
476       status ++;
477     }
478     else
479       puts("PASS");
480 
481    /*
482     * Custom sizes...
483     */
484 
485     fputs("cupsMarkOptions(media=Custom.8x10in): ", stdout);
486     num_options = cupsAddOption("media", "Custom.8x10in", 0, &options);
487     cupsMarkOptions(ppd, num_options, options);
488     cupsFreeOptions(num_options, options);
489 
490     size = ppdPageSize(ppd, NULL);
491     if (!size || strcmp(size->name, "Custom") ||
492         fabs(size->width - 576.0) > 0.001 ||
493         fabs(size->length - 720.0) > 0.001)
494     {
495       printf("FAIL (%s - %gx%g)\n", size ? size->name : "unknown",
496              size ? size->width : 0.0, size ? size->length : 0.0);
497       status ++;
498     }
499     else
500       puts("PASS");
501 
502    /*
503     * Test localization...
504     */
505 
506     fputs("ppdLocalizeIPPReason(text): ", stdout);
507     if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
508         !strcmp(buffer, "Foo Reason"))
509       puts("PASS");
510     else
511     {
512       status ++;
513       printf("FAIL (\"%s\" instead of \"Foo Reason\")\n", buffer);
514     }
515 
516     fputs("ppdLocalizeIPPReason(http): ", stdout);
517     if (ppdLocalizeIPPReason(ppd, "foo", "http", buffer, sizeof(buffer)) &&
518         !strcmp(buffer, "http://foo/bar.html"))
519       puts("PASS");
520     else
521     {
522       status ++;
523       printf("FAIL (\"%s\" instead of \"http://foo/bar.html\")\n", buffer);
524     }
525 
526     fputs("ppdLocalizeIPPReason(help): ", stdout);
527     if (ppdLocalizeIPPReason(ppd, "foo", "help", buffer, sizeof(buffer)) &&
528         !strcmp(buffer, "help:anchor='foo'%20bookID=Vendor%20Help"))
529       puts("PASS");
530     else
531     {
532       status ++;
533       printf("FAIL (\"%s\" instead of \"help:anchor='foo'%%20bookID=Vendor%%20Help\")\n", buffer);
534     }
535 
536     fputs("ppdLocalizeIPPReason(file): ", stdout);
537     if (ppdLocalizeIPPReason(ppd, "foo", "file", buffer, sizeof(buffer)) &&
538         !strcmp(buffer, "/help/foo/bar.html"))
539       puts("PASS");
540     else
541     {
542       status ++;
543       printf("FAIL (\"%s\" instead of \"/help/foo/bar.html\")\n", buffer);
544     }
545 
546     putenv("LANG=fr");
547     putenv("LC_ALL=fr");
548     putenv("LC_CTYPE=fr");
549     putenv("LC_MESSAGES=fr");
550 
551     fputs("ppdLocalizeIPPReason(fr text): ", stdout);
552     if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
553         !strcmp(buffer, "La Long Foo Reason"))
554       puts("PASS");
555     else
556     {
557       status ++;
558       printf("FAIL (\"%s\" instead of \"La Long Foo Reason\")\n", buffer);
559     }
560 
561     putenv("LANG=zh_TW");
562     putenv("LC_ALL=zh_TW");
563     putenv("LC_CTYPE=zh_TW");
564     putenv("LC_MESSAGES=zh_TW");
565 
566     fputs("ppdLocalizeIPPReason(zh_TW text): ", stdout);
567     if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
568         !strcmp(buffer, "Number 1 Foo Reason"))
569       puts("PASS");
570     else
571     {
572       status ++;
573       printf("FAIL (\"%s\" instead of \"Number 1 Foo Reason\")\n", buffer);
574     }
575 
576    /*
577     * cupsMarkerName localization...
578     */
579 
580     putenv("LANG=en");
581     putenv("LC_ALL=en");
582     putenv("LC_CTYPE=en");
583     putenv("LC_MESSAGES=en");
584 
585     fputs("ppdLocalizeMarkerName(bogus): ", stdout);
586 
587     if ((text = ppdLocalizeMarkerName(ppd, "bogus")) != NULL)
588     {
589       status ++;
590       printf("FAIL (\"%s\" instead of NULL)\n", text);
591     }
592     else
593       puts("PASS");
594 
595     fputs("ppdLocalizeMarkerName(cyan): ", stdout);
596 
597     if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
598         !strcmp(text, "Cyan Toner"))
599       puts("PASS");
600     else
601     {
602       status ++;
603       printf("FAIL (\"%s\" instead of \"Cyan Toner\")\n",
604              text ? text : "(null)");
605     }
606 
607     putenv("LANG=fr");
608     putenv("LC_ALL=fr");
609     putenv("LC_CTYPE=fr");
610     putenv("LC_MESSAGES=fr");
611 
612     fputs("ppdLocalizeMarkerName(fr cyan): ", stdout);
613     if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
614         !strcmp(text, "La Toner Cyan"))
615       puts("PASS");
616     else
617     {
618       status ++;
619       printf("FAIL (\"%s\" instead of \"La Toner Cyan\")\n",
620              text ? text : "(null)");
621     }
622 
623     putenv("LANG=zh_TW");
624     putenv("LC_ALL=zh_TW");
625     putenv("LC_CTYPE=zh_TW");
626     putenv("LC_MESSAGES=zh_TW");
627 
628     fputs("ppdLocalizeMarkerName(zh_TW cyan): ", stdout);
629     if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
630         !strcmp(text, "Number 1 Cyan Toner"))
631       puts("PASS");
632     else
633     {
634       status ++;
635       printf("FAIL (\"%s\" instead of \"Number 1 Cyan Toner\")\n",
636              text ? text : "(null)");
637     }
638 
639     ppdClose(ppd);
640 
641    /*
642     * Test new constraints...
643     */
644 
645     fputs("ppdOpenFile(test2.ppd): ", stdout);
646 
647     if ((ppd = ppdOpenFile("test2.ppd")) != NULL)
648       puts("PASS");
649     else
650     {
651       ppd_status_t	err;		/* Last error in file */
652       int		line;		/* Line number in file */
653 
654 
655       status ++;
656       err = ppdLastError(&line);
657 
658       printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
659     }
660 
661     fputs("ppdMarkDefaults: ", stdout);
662     ppdMarkDefaults(ppd);
663 
664     if ((conflicts = ppdConflicts(ppd)) == 0)
665       puts("PASS");
666     else
667     {
668       status ++;
669       printf("FAIL (%d conflicts)\n", conflicts);
670     }
671 
672     fputs("ppdEmitString (defaults): ", stdout);
673     if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
674 	!strcmp(s, default2_code))
675       puts("PASS");
676     else
677     {
678       status ++;
679       printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
680 	     (int)strlen(default2_code));
681 
682       if (s)
683 	puts(s);
684     }
685 
686     if (s)
687       free(s);
688 
689     fputs("ppdConflicts(): ", stdout);
690     ppdMarkOption(ppd, "PageSize", "Env10");
691     ppdMarkOption(ppd, "InputSlot", "Envelope");
692     ppdMarkOption(ppd, "Quality", "Photo");
693 
694     if ((conflicts = ppdConflicts(ppd)) == 1)
695       puts("PASS (1)");
696     else
697     {
698       printf("FAIL (%d)\n", conflicts);
699       status ++;
700     }
701 
702     fputs("cupsResolveConflicts(Quality=Photo): ", stdout);
703     num_options = 0;
704     options     = NULL;
705     if (cupsResolveConflicts(ppd, "Quality", "Photo", &num_options,
706                              &options))
707     {
708       printf("FAIL (%d options:", num_options);
709       for (i = 0; i < num_options; i ++)
710         printf(" %s=%s", options[i].name, options[i].value);
711       puts(")");
712       status ++;
713     }
714     else
715       puts("PASS (Unable to resolve)");
716     cupsFreeOptions(num_options, options);
717 
718     fputs("cupsResolveConflicts(No option/choice): ", stdout);
719     num_options = 0;
720     options     = NULL;
721     if (cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options) &&
722         num_options == 1 && !_cups_strcasecmp(options->name, "Quality") &&
723 	!_cups_strcasecmp(options->value, "Normal"))
724       puts("PASS");
725     else if (num_options > 0)
726     {
727       printf("FAIL (%d options:", num_options);
728       for (i = 0; i < num_options; i ++)
729         printf(" %s=%s", options[i].name, options[i].value);
730       puts(")");
731       status ++;
732     }
733     else
734     {
735       puts("FAIL (Unable to resolve!)");
736       status ++;
737     }
738     cupsFreeOptions(num_options, options);
739 
740     fputs("cupsResolveConflicts(loop test): ", stdout);
741     ppdMarkOption(ppd, "PageSize", "A4");
742     ppdMarkOption(ppd, "InputSlot", "Tray");
743     ppdMarkOption(ppd, "Quality", "Photo");
744     num_options = 0;
745     options     = NULL;
746     if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
747       puts("PASS");
748     else if (num_options > 0)
749     {
750       printf("FAIL (%d options:", num_options);
751       for (i = 0; i < num_options; i ++)
752         printf(" %s=%s", options[i].name, options[i].value);
753       puts(")");
754     }
755     else
756       puts("FAIL (No conflicts!)");
757 
758     fputs("ppdInstallableConflict(): ", stdout);
759     if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") &&
760         !ppdInstallableConflict(ppd, "Duplex", "None"))
761       puts("PASS");
762     else if (!ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble"))
763     {
764       puts("FAIL (Duplex=DuplexNoTumble did not conflict)");
765       status ++;
766     }
767     else
768     {
769       puts("FAIL (Duplex=None conflicted)");
770       status ++;
771     }
772 
773    /*
774     * ppdPageSizeLimits
775     */
776 
777     ppdMarkDefaults(ppd);
778 
779     fputs("ppdPageSizeLimits(default): ", stdout);
780     if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
781     {
782       if (fabs(minsize.width - 36.0) > 0.001 || fabs(minsize.length - 36.0) > 0.001 ||
783           fabs(maxsize.width - 1080.0) > 0.001 || fabs(maxsize.length - 86400.0) > 0.001)
784       {
785         printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
786 	       "expected min=36x36, max=1080x86400)\n", minsize.width,
787 	       minsize.length, maxsize.width, maxsize.length);
788         status ++;
789       }
790       else
791         puts("PASS");
792     }
793     else
794     {
795       puts("FAIL (returned 0)");
796       status ++;
797     }
798 
799     ppdMarkOption(ppd, "InputSlot", "Manual");
800 
801     fputs("ppdPageSizeLimits(InputSlot=Manual): ", stdout);
802     if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
803     {
804       if (fabs(minsize.width - 100.0) > 0.001 || fabs(minsize.length - 100.0) > 0.001 ||
805           fabs(maxsize.width - 1000.0) > 0.001 || fabs(maxsize.length - 1000.0) > 0.001)
806       {
807         printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
808 	       "expected min=100x100, max=1000x1000)\n", minsize.width,
809 	       minsize.length, maxsize.width, maxsize.length);
810         status ++;
811       }
812       else
813         puts("PASS");
814     }
815     else
816     {
817       puts("FAIL (returned 0)");
818       status ++;
819     }
820 
821     ppdMarkOption(ppd, "Quality", "Photo");
822 
823     fputs("ppdPageSizeLimits(Quality=Photo): ", stdout);
824     if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
825     {
826       if (fabs(minsize.width - 200.0) > 0.001 || fabs(minsize.length - 200.0) > 0.001 ||
827           fabs(maxsize.width - 1000.0) > 0.001 || fabs(maxsize.length - 1000.0) > 0.001)
828       {
829         printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
830 	       "expected min=200x200, max=1000x1000)\n", minsize.width,
831 	       minsize.length, maxsize.width, maxsize.length);
832         status ++;
833       }
834       else
835         puts("PASS");
836     }
837     else
838     {
839       puts("FAIL (returned 0)");
840       status ++;
841     }
842 
843     ppdMarkOption(ppd, "InputSlot", "Tray");
844 
845     fputs("ppdPageSizeLimits(Quality=Photo): ", stdout);
846     if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
847     {
848       if (fabs(minsize.width - 300.0) > 0.001 || fabs(minsize.length - 300.0) > 0.001 ||
849           fabs(maxsize.width - 1080.0) > 0.001 || fabs(maxsize.length - 86400.0) > 0.001)
850       {
851         printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
852 	       "expected min=300x300, max=1080x86400)\n", minsize.width,
853 	       minsize.length, maxsize.width, maxsize.length);
854         status ++;
855       }
856       else
857         puts("PASS");
858     }
859     else
860     {
861       puts("FAIL (returned 0)");
862       status ++;
863     }
864   }
865   else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7))
866   {
867    /*
868     * ipp://... or ipps://...
869     */
870 
871     http_t	*http;			/* Connection to printer */
872     ipp_t	*request,		/* Get-Printer-Attributes request */
873 		*response;		/* Get-Printer-Attributes response */
874     char	scheme[32],		/* URI scheme */
875 		userpass[256],		/* Username:password */
876 		host[256],		/* Hostname */
877 		resource[256];		/* Resource path */
878     int		port;			/* Port number */
879 
880     if (httpSeparateURI(HTTP_URI_CODING_ALL, argv[1], scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
881     {
882       printf("Bad URI \"%s\".\n", argv[1]);
883       return (1);
884     }
885 
886     http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
887     if (!http)
888     {
889       printf("Unable to connect to \"%s:%d\": %s\n", host, port, cupsLastErrorString());
890       return (1);
891     }
892 
893     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
894     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, argv[1]);
895     response = cupsDoRequest(http, request, resource);
896 
897     if (_ppdCreateFromIPP(buffer, sizeof(buffer), response))
898       printf("Created PPD: %s\n", buffer);
899     else
900       puts("Unable to create PPD.");
901 
902     ippDelete(response);
903     httpClose(http);
904     return (0);
905   }
906   else
907   {
908     const char	*filename;		/* PPD filename */
909     struct stat	fileinfo;		/* File information */
910 
911 
912     if (strchr(argv[1], ':'))
913     {
914      /*
915       * Server PPD...
916       */
917 
918       if ((filename = cupsGetServerPPD(CUPS_HTTP_DEFAULT, argv[1])) == NULL)
919       {
920         printf("%s: %s\n", argv[1], cupsLastErrorString());
921         return (1);
922       }
923     }
924     else if (!strncmp(argv[1], "-d", 2))
925     {
926       const char *printer;		/* Printer name */
927 
928       if (argv[1][2])
929 	printer = argv[1] + 2;
930       else if (argv[2])
931 	printer = argv[2];
932       else
933       {
934         puts("Usage: ./testppd -d printer");
935 	return (1);
936       }
937 
938       filename = cupsGetPPD(printer);
939 
940       if (!filename)
941       {
942         printf("%s: %s\n", printer, cupsLastErrorString());
943         return (1);
944       }
945     }
946     else
947       filename = argv[1];
948 
949     if (lstat(filename, &fileinfo))
950     {
951       printf("%s: %s\n", filename, strerror(errno));
952       return (1);
953     }
954 
955     if (S_ISLNK(fileinfo.st_mode))
956     {
957       char	realfile[1024];		/* Real file path */
958       ssize_t	realsize;		/* Size of real file path */
959 
960 
961       if ((realsize = readlink(filename, realfile, sizeof(realfile) - 1)) < 0)
962         strlcpy(realfile, "Unknown", sizeof(realfile));
963       else
964         realfile[realsize] = '\0';
965 
966       if (stat(realfile, &fileinfo))
967 	printf("%s: symlink to \"%s\", %s\n", filename, realfile,
968 	       strerror(errno));
969       else
970 	printf("%s: symlink to \"%s\", %ld bytes\n", filename, realfile,
971 	       (long)fileinfo.st_size);
972     }
973     else
974       printf("%s: regular file, %ld bytes\n", filename, (long)fileinfo.st_size);
975 
976     if ((ppd = ppdOpenFile(filename)) == NULL)
977     {
978       ppd_status_t	err;		/* Last error in file */
979       int		line;		/* Line number in file */
980 
981 
982       status ++;
983       err = ppdLastError(&line);
984 
985       printf("%s: %s on line %d\n", argv[1], ppdErrorString(err), line);
986     }
987     else
988     {
989       int		j, k;		/* Looping vars */
990       ppd_group_t	*group;		/* Option group */
991       ppd_option_t	*option;	/* Option */
992       ppd_coption_t	*coption;	/* Custom option */
993       ppd_cparam_t	*cparam;	/* Custom parameter */
994       ppd_const_t	*c;		/* UIConstraints */
995       char		lang[255],	/* LANG environment variable */
996 			lc_all[255],	/* LC_ALL environment variable */
997 			lc_ctype[255],	/* LC_CTYPE environment variable */
998 			lc_messages[255];/* LC_MESSAGES environment variable */
999 
1000 
1001       if (argc > 2)
1002       {
1003         snprintf(lang, sizeof(lang), "LANG=%s", argv[2]);
1004 	putenv(lang);
1005         snprintf(lc_all, sizeof(lc_all), "LC_ALL=%s", argv[2]);
1006 	putenv(lc_all);
1007         snprintf(lc_ctype, sizeof(lc_ctype), "LC_CTYPE=%s", argv[2]);
1008 	putenv(lc_ctype);
1009         snprintf(lc_messages, sizeof(lc_messages), "LC_MESSAGES=%s", argv[2]);
1010 	putenv(lc_messages);
1011       }
1012 
1013       ppdLocalize(ppd);
1014       ppdMarkDefaults(ppd);
1015 
1016       if (argc > 3)
1017       {
1018         text = ppdLocalizeIPPReason(ppd, argv[3], NULL, buffer, sizeof(buffer));
1019 	printf("ppdLocalizeIPPReason(%s)=%s\n", argv[3],
1020 	       text ? text : "(null)");
1021 	return (text == NULL);
1022       }
1023 
1024       for (i = ppd->num_groups, group = ppd->groups;
1025 	   i > 0;
1026 	   i --, group ++)
1027       {
1028 	printf("%s (%s):\n", group->name, group->text);
1029 
1030 	for (j = group->num_options, option = group->options;
1031 	     j > 0;
1032 	     j --, option ++)
1033 	{
1034 	  printf("    %s (%s):\n", option->keyword, option->text);
1035 
1036 	  for (k = 0; k < option->num_choices; k ++)
1037 	    printf("        - %s%s (%s)\n",
1038 	           option->choices[k].marked ? "*" : "",
1039 		   option->choices[k].choice, option->choices[k].text);
1040 
1041           if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
1042 	  {
1043 	    for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
1044 	         cparam;
1045 		 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
1046             {
1047 	      switch (cparam->type)
1048 	      {
1049 	        case PPD_CUSTOM_CURVE :
1050 		    printf("              %s(%s): PPD_CUSTOM_CURVE (%g to %g)\n",
1051 		           cparam->name, cparam->text,
1052 			   cparam->minimum.custom_curve,
1053 			   cparam->maximum.custom_curve);
1054 		    break;
1055 
1056 	        case PPD_CUSTOM_INT :
1057 		    printf("              %s(%s): PPD_CUSTOM_INT (%d to %d)\n",
1058 		           cparam->name, cparam->text,
1059 			   cparam->minimum.custom_int,
1060 			   cparam->maximum.custom_int);
1061 		    break;
1062 
1063 	        case PPD_CUSTOM_INVCURVE :
1064 		    printf("              %s(%s): PPD_CUSTOM_INVCURVE (%g to %g)\n",
1065 		           cparam->name, cparam->text,
1066 			   cparam->minimum.custom_invcurve,
1067 			   cparam->maximum.custom_invcurve);
1068 		    break;
1069 
1070 	        case PPD_CUSTOM_PASSCODE :
1071 		    printf("              %s(%s): PPD_CUSTOM_PASSCODE (%d to %d)\n",
1072 		           cparam->name, cparam->text,
1073 			   cparam->minimum.custom_passcode,
1074 			   cparam->maximum.custom_passcode);
1075 		    break;
1076 
1077 	        case PPD_CUSTOM_PASSWORD :
1078 		    printf("              %s(%s): PPD_CUSTOM_PASSWORD (%d to %d)\n",
1079 		           cparam->name, cparam->text,
1080 			   cparam->minimum.custom_password,
1081 			   cparam->maximum.custom_password);
1082 		    break;
1083 
1084 	        case PPD_CUSTOM_POINTS :
1085 		    printf("              %s(%s): PPD_CUSTOM_POINTS (%g to %g)\n",
1086 		           cparam->name, cparam->text,
1087 			   cparam->minimum.custom_points,
1088 			   cparam->maximum.custom_points);
1089 		    break;
1090 
1091 	        case PPD_CUSTOM_REAL :
1092 		    printf("              %s(%s): PPD_CUSTOM_REAL (%g to %g)\n",
1093 		           cparam->name, cparam->text,
1094 			   cparam->minimum.custom_real,
1095 			   cparam->maximum.custom_real);
1096 		    break;
1097 
1098 	        case PPD_CUSTOM_STRING :
1099 		    printf("              %s(%s): PPD_CUSTOM_STRING (%d to %d)\n",
1100 		           cparam->name, cparam->text,
1101 			   cparam->minimum.custom_string,
1102 			   cparam->maximum.custom_string);
1103 		    break;
1104 	      }
1105 	    }
1106 	  }
1107 	}
1108       }
1109 
1110       puts("\nSizes:");
1111       for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
1112         printf("    %s = %gx%g, [%g %g %g %g]\n", size->name, size->width,
1113 	       size->length, size->left, size->bottom, size->right, size->top);
1114 
1115       puts("\nConstraints:");
1116 
1117       for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
1118         printf("    *UIConstraints: *%s %s *%s %s\n", c->option1, c->choice1,
1119 	       c->option2, c->choice2);
1120       if (ppd->num_consts == 0)
1121         puts("    NO CONSTRAINTS");
1122 
1123       puts("\nFilters:");
1124 
1125       for (i = 0; i < ppd->num_filters; i ++)
1126         printf("    %s\n", ppd->filters[i]);
1127 
1128       if (ppd->num_filters == 0)
1129         puts("    NO FILTERS");
1130 
1131       puts("\nAttributes:");
1132 
1133       for (attr = (ppd_attr_t *)cupsArrayFirst(ppd->sorted_attrs);
1134            attr;
1135 	   attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs))
1136         printf("    *%s %s/%s: \"%s\"\n", attr->name, attr->spec,
1137 	       attr->text, attr->value ? attr->value : "");
1138 
1139       puts("\nPPD Cache:");
1140       if ((pc = _ppdCacheCreateWithPPD(ppd)) == NULL)
1141         printf("    Unable to create: %s\n", cupsLastErrorString());
1142       else
1143       {
1144         _ppdCacheWriteFile(pc, "t.cache", NULL);
1145         puts("    Wrote t.cache.");
1146       }
1147     }
1148 
1149     if (!strncmp(argv[1], "-d", 2))
1150       unlink(filename);
1151   }
1152 
1153 #ifdef __APPLE__
1154   if (getenv("MallocStackLogging") && getenv("MallocStackLoggingNoCompact"))
1155   {
1156     char	command[1024];		/* malloc_history command */
1157 
1158     snprintf(command, sizeof(command), "malloc_history %d -all_by_size",
1159 	     getpid());
1160     fflush(stdout);
1161     system(command);
1162   }
1163 #endif /* __APPLE__ */
1164 
1165   ppdClose(ppd);
1166 
1167   return (status);
1168 }
1169