Imported Upstream version 2.2.2
[platform/upstream/cups.git] / cups / testppd.c
1 /*
2  * PPD test program for CUPS.
3  *
4  * Copyright 2007-2015 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 */
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 }