]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/testppd.c
Migrate Windows conditional code to _WIN32 define.
[thirdparty/cups.git] / cups / testppd.c
index 10f11650f4095d2f9fd4150e851d0c938389236f..3e4f29b03819b1f3a56b9200fa6a827d3e297b5e 100644 (file)
@@ -1,40 +1,27 @@
 /*
- * "$Id: testppd.c 7633 2008-06-10 23:07:29Z mike $"
+ * PPD test program for CUPS.
  *
- *   PPD test program for the Common UNIX Printing System (CUPS).
+ * Copyright 2007-2017 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products.
  *
- *   Copyright 2007-2008 by Apple Inc.
- *   Copyright 1997-2006 by Easy Software Products.
- *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   which should have been included with this file.  If this file is
- *   file is missing or damaged, see the license at "http://www.cups.org/".
- *
- *   This file is subject to the Apple OS-Developed Software exception.
- *
- * Contents:
- *
- *   main() - Main entry.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
  */
 
 /*
  * Include necessary headers...
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <cups/string.h>
+#undef _CUPS_NO_DEPRECATED
+#include "cups-private.h"
+#include "ppd-private.h"
 #include <sys/stat.h>
-#include <errno.h>
-#include "cups.h"
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
 #  include <fcntl.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
+#include <math.h>
 
 
 /*
@@ -57,6 +44,16 @@ static const char    *default_code =
                        "%%EndFeature\n"
                        "} stopped cleartomark\n"
                        "[{\n"
+                       "%%BeginFeature: *OutputBin Tray1\n"
+                       "OutputBin=Tray1\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
+                       "%%BeginFeature: *MediaType Plain\n"
+                       "MediaType=Plain\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
                        "%%BeginFeature: *IntOption None\n"
                        "%%EndFeature\n"
                        "} stopped cleartomark\n"
@@ -76,11 +73,24 @@ static const char   *custom_code =
                        "%%EndFeature\n"
                        "} stopped cleartomark\n"
                        "[{\n"
+                       "%%BeginFeature: *MediaType Plain\n"
+                       "MediaType=Plain\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
+                       "%%BeginFeature: *OutputBin Tray1\n"
+                       "OutputBin=Tray1\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
                        "%%BeginFeature: *IntOption None\n"
                        "%%EndFeature\n"
                        "} stopped cleartomark\n"
                        "[{\n"
-                       "%%BeginFeature: *StringOption None\n"
+                       "%%BeginFeature: *CustomStringOption True\n"
+                       "(value\\0502\\051)\n"
+                       "(value 1)\n"
+                       "StringOption=Custom\n"
                        "%%EndFeature\n"
                        "} stopped cleartomark\n"
                        "[{\n"
@@ -94,6 +104,30 @@ static const char   *custom_code =
                        "%%EndFeature\n"
                        "} stopped cleartomark\n";
 
+static const char      *default2_code =
+                       "[{\n"
+                       "%%BeginFeature: *InstalledDuplexer False\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
+                       "%%BeginFeature: *InputSlot Tray\n"
+                       "InputSlot=Tray\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
+                       "%%BeginFeature: *Quality Normal\n"
+                       "Quality=Normal\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
+                       "%%BeginFeature: *IntOption None\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n"
+                       "[{\n"
+                       "%%BeginFeature: *StringOption None\n"
+                       "%%EndFeature\n"
+                       "} stopped cleartomark\n";
+
 
 /*
  * 'main()' - Main entry.
@@ -109,9 +143,15 @@ main(int  argc,                            /* I - Number of command-line arguments */
   int          conflicts;              /* Number of conflicts */
   char         *s;                     /* String */
   char         buffer[8192];           /* String buffer */
-  const char   *text;                  /* Localized text */
+  const char   *text,                  /* Localized text */
+               *val;                   /* Option value */
   int          num_options;            /* Number of options */
   cups_option_t        *options;               /* Options */
+  ppd_size_t   minsize,                /* Minimum size */
+               maxsize,                /* Maximum size */
+               *size;                  /* Current size */
+  ppd_attr_t   *attr;                  /* Current attribute */
+  _ppd_cache_t *pc;                    /* PPD cache */
 
 
   status = 0;
@@ -132,6 +172,7 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
 
     putenv("LOCALEDIR=locale");
+    putenv("SOFTWARE=CUPS");
 
    /*
     * Do tests with test.ppd...
@@ -139,7 +180,7 @@ main(int  argc,                             /* I - Number of command-line arguments */
 
     fputs("ppdOpenFile(test.ppd): ", stdout);
 
-    if ((ppd = ppdOpenFile("test.ppd")) != NULL)
+    if ((ppd = _ppdOpenFile("test.ppd", _PPD_LOCALIZATION_ALL)) != NULL)
       puts("PASS");
     else
     {
@@ -153,6 +194,57 @@ main(int  argc,                            /* I - Number of command-line arguments */
       printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
     }
 
+    fputs("ppdFindAttr(wildcard): ", stdout);
+    if ((attr = ppdFindAttr(ppd, "cupsTest", NULL)) == NULL)
+    {
+      status ++;
+      puts("FAIL (not found)");
+    }
+    else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo"))
+    {
+      status ++;
+      printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+    }
+    else
+      puts("PASS");
+
+    fputs("ppdFindNextAttr(wildcard): ", stdout);
+    if ((attr = ppdFindNextAttr(ppd, "cupsTest", NULL)) == NULL)
+    {
+      status ++;
+      puts("FAIL (not found)");
+    }
+    else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Bar"))
+    {
+      status ++;
+      printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+    }
+    else
+      puts("PASS");
+
+    fputs("ppdFindAttr(Foo): ", stdout);
+    if ((attr = ppdFindAttr(ppd, "cupsTest", "Foo")) == NULL)
+    {
+      status ++;
+      puts("FAIL (not found)");
+    }
+    else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo"))
+    {
+      status ++;
+      printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+    }
+    else
+      puts("PASS");
+
+    fputs("ppdFindNextAttr(Foo): ", stdout);
+    if ((attr = ppdFindNextAttr(ppd, "cupsTest", "Foo")) != NULL)
+    {
+      status ++;
+      printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec);
+    }
+    else
+      puts("PASS");
+
     fputs("ppdMarkDefaults: ", stdout);
     ppdMarkDefaults(ppd);
 
@@ -181,8 +273,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
     if (s)
       free(s);
 
-    fputs("ppdEmitString (custom size): ", stdout);
+    fputs("ppdEmitString (custom size and string): ", stdout);
     ppdMarkOption(ppd, "PageSize", "Custom.400x500");
+    ppdMarkOption(ppd, "StringOption", "{String1=\"value 1\" String2=value(2)}");
 
     if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
        !strcmp(s, custom_code))
@@ -204,8 +297,26 @@ main(int  argc,                            /* I - Number of command-line arguments */
     * Test constraints...
     */
 
-    fputs("ppdConflicts(): ", stdout);
+    fputs("cupsGetConflicts(InputSlot=Envelope): ", stdout);
     ppdMarkOption(ppd, "PageSize", "Letter");
+
+    num_options = cupsGetConflicts(ppd, "InputSlot", "Envelope", &options);
+    if (num_options != 2 ||
+        (val = cupsGetOption("PageRegion", num_options, options)) == NULL ||
+       _cups_strcasecmp(val, "Letter") ||
+       (val = cupsGetOption("PageSize", num_options, options)) == NULL ||
+       _cups_strcasecmp(val, "Letter"))
+    {
+      printf("FAIL (%d options:", num_options);
+      for (i = 0; i < num_options; i ++)
+        printf(" %s=%s", options[i].name, options[i].value);
+      puts(")");
+      status ++;
+    }
+    else
+      puts("PASS");
+
+    fputs("ppdConflicts(): ", stdout);
     ppdMarkOption(ppd, "InputSlot", "Envelope");
 
     if ((conflicts = ppdConflicts(ppd)) == 2)
@@ -216,14 +327,36 @@ main(int  argc,                           /* I - Number of command-line arguments */
       status ++;
     }
 
-    fputs("cupsResolveConflicts(): ", stdout);
+    fputs("cupsResolveConflicts(InputSlot=Envelope): ", stdout);
     num_options = 0;
     options     = NULL;
-    if (cupsResolveConflicts(ppd, "InputSlot", "Envelope", &num_options,
-                             &options) &&
-        num_options == 1 && !strcasecmp(options->name, "InputSlot") &&
-       !strcasecmp(options->value, "Tray"))
-      puts("PASS");
+    if (!cupsResolveConflicts(ppd, "InputSlot", "Envelope", &num_options,
+                             &options))
+    {
+      puts("FAIL (Unable to resolve)");
+      status ++;
+    }
+    else if (num_options != 2 ||
+             !cupsGetOption("PageSize", num_options, options))
+    {
+      printf("FAIL (%d options:", num_options);
+      for (i = 0; i < num_options; i ++)
+        printf(" %s=%s", options[i].name, options[i].value);
+      puts(")");
+      status ++;
+    }
+    else
+      puts("PASS (Resolved by changing PageSize)");
+
+    cupsFreeOptions(num_options, options);
+
+    fputs("cupsResolveConflicts(No option/choice): ", stdout);
+    num_options = 0;
+    options     = NULL;
+    if (cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options) &&
+        num_options == 1 && !_cups_strcasecmp(options[0].name, "InputSlot") &&
+       !_cups_strcasecmp(options[0].value, "Tray"))
+      puts("PASS (Resolved by changing InputSlot)");
     else if (num_options > 0)
     {
       printf("FAIL (%d options:", num_options);
@@ -234,7 +367,7 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
     else
     {
-      puts("FAIL (Unable to resolve!)");
+      puts("FAIL (Unable to resolve)");
       status ++;
     }
     cupsFreeOptions(num_options, options);
@@ -254,6 +387,112 @@ main(int  argc,                           /* I - Number of command-line arguments */
       status ++;
     }
 
+   /*
+    * ppdPageSizeLimits
+    */
+
+    fputs("ppdPageSizeLimits: ", stdout);
+    if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+    {
+      if (fabs(minsize.width - 36.0) > 0.001 || fabs(minsize.length - 36.0) > 0.001 ||
+          fabs(maxsize.width - 1080.0) > 0.001 || fabs(maxsize.length - 86400.0) > 0.001)
+      {
+        printf("FAIL (got min=%.3fx%.3f, max=%.3fx%.3f, "
+              "expected min=36x36, max=1080x86400)\n", minsize.width,
+              minsize.length, maxsize.width, maxsize.length);
+        status ++;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (returned 0)");
+      status ++;
+    }
+
+   /*
+    * cupsMarkOptions with PWG and IPP size names.
+    */
+
+    fputs("cupsMarkOptions(media=iso-a4): ", stdout);
+    num_options = cupsAddOption("media", "iso-a4", 0, &options);
+    cupsMarkOptions(ppd, num_options, options);
+    cupsFreeOptions(num_options, options);
+
+    size = ppdPageSize(ppd, NULL);
+    if (!size || strcmp(size->name, "A4"))
+    {
+      printf("FAIL (%s)\n", size ? size->name : "unknown");
+      status ++;
+    }
+    else
+      puts("PASS");
+
+    fputs("cupsMarkOptions(media=na_letter_8.5x11in): ", stdout);
+    num_options = cupsAddOption("media", "na_letter_8.5x11in", 0, &options);
+    cupsMarkOptions(ppd, num_options, options);
+    cupsFreeOptions(num_options, options);
+
+    size = ppdPageSize(ppd, NULL);
+    if (!size || strcmp(size->name, "Letter"))
+    {
+      printf("FAIL (%s)\n", size ? size->name : "unknown");
+      status ++;
+    }
+    else
+      puts("PASS");
+
+    fputs("cupsMarkOptions(media=oe_letter-fullbleed_8.5x11in): ", stdout);
+    num_options = cupsAddOption("media", "oe_letter-fullbleed_8.5x11in", 0,
+                                &options);
+    cupsMarkOptions(ppd, num_options, options);
+    cupsFreeOptions(num_options, options);
+
+    size = ppdPageSize(ppd, NULL);
+    if (!size || strcmp(size->name, "Letter.Fullbleed"))
+    {
+      printf("FAIL (%s)\n", size ? size->name : "unknown");
+      status ++;
+    }
+    else
+      puts("PASS");
+
+    fputs("cupsMarkOptions(media=A4): ", stdout);
+    num_options = cupsAddOption("media", "A4", 0, &options);
+    cupsMarkOptions(ppd, num_options, options);
+    cupsFreeOptions(num_options, options);
+
+    size = ppdPageSize(ppd, NULL);
+    if (!size || strcmp(size->name, "A4"))
+    {
+      printf("FAIL (%s)\n", size ? size->name : "unknown");
+      status ++;
+    }
+    else
+      puts("PASS");
+
+   /*
+    * Custom sizes...
+    */
+
+    fputs("cupsMarkOptions(media=Custom.8x10in): ", stdout);
+    num_options = cupsAddOption("media", "Custom.8x10in", 0, &options);
+    cupsMarkOptions(ppd, num_options, options);
+    cupsFreeOptions(num_options, options);
+
+    size = ppdPageSize(ppd, NULL);
+    if (!size || strcmp(size->name, "Custom") ||
+        fabs(size->width - 576.0) > 0.001 ||
+        fabs(size->length - 720.0) > 0.001)
+    {
+      printf("FAIL (%s - %gx%g)\n", size ? size->name : "unknown",
+             size ? size->width : 0.0, size ? size->length : 0.0);
+      status ++;
+    }
+    else
+      puts("PASS");
+
    /*
     * Test localization...
     */
@@ -299,6 +538,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
 
     putenv("LANG=fr");
+    putenv("LC_ALL=fr");
+    putenv("LC_CTYPE=fr");
+    putenv("LC_MESSAGES=fr");
 
     fputs("ppdLocalizeIPPReason(fr text): ", stdout);
     if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
@@ -311,6 +553,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
 
     putenv("LANG=zh_TW");
+    putenv("LC_ALL=zh_TW");
+    putenv("LC_CTYPE=zh_TW");
+    putenv("LC_MESSAGES=zh_TW");
 
     fputs("ppdLocalizeIPPReason(zh_TW text): ", stdout);
     if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) &&
@@ -327,6 +572,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
     */
 
     putenv("LANG=en");
+    putenv("LC_ALL=en");
+    putenv("LC_CTYPE=en");
+    putenv("LC_MESSAGES=en");
 
     fputs("ppdLocalizeMarkerName(bogus): ", stdout);
 
@@ -351,6 +599,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
 
     putenv("LANG=fr");
+    putenv("LC_ALL=fr");
+    putenv("LC_CTYPE=fr");
+    putenv("LC_MESSAGES=fr");
 
     fputs("ppdLocalizeMarkerName(fr cyan): ", stdout);
     if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
@@ -364,6 +615,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
 
     putenv("LANG=zh_TW");
+    putenv("LC_ALL=zh_TW");
+    putenv("LC_CTYPE=zh_TW");
+    putenv("LC_MESSAGES=zh_TW");
 
     fputs("ppdLocalizeMarkerName(zh_TW cyan): ", stdout);
     if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL &&
@@ -409,26 +663,58 @@ main(int  argc,                           /* I - Number of command-line arguments */
       printf("FAIL (%d conflicts)\n", conflicts);
     }
 
+    fputs("ppdEmitString (defaults): ", stdout);
+    if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
+       !strcmp(s, default2_code))
+      puts("PASS");
+    else
+    {
+      status ++;
+      printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0,
+            (int)strlen(default2_code));
+
+      if (s)
+       puts(s);
+    }
+
+    if (s)
+      free(s);
+
     fputs("ppdConflicts(): ", stdout);
     ppdMarkOption(ppd, "PageSize", "Env10");
     ppdMarkOption(ppd, "InputSlot", "Envelope");
     ppdMarkOption(ppd, "Quality", "Photo");
 
-    if ((conflicts = ppdConflicts(ppd)) == 2)
-      puts("PASS (2)");
+    if ((conflicts = ppdConflicts(ppd)) == 1)
+      puts("PASS (1)");
     else
     {
       printf("FAIL (%d)\n", conflicts);
       status ++;
     }
 
-    fputs("cupsResolveConflicts(): ", stdout);
+    fputs("cupsResolveConflicts(Quality=Photo): ", stdout);
     num_options = 0;
     options     = NULL;
     if (cupsResolveConflicts(ppd, "Quality", "Photo", &num_options,
-                            &options) &&
-        num_options == 1 && !strcasecmp(options->name, "Quality") &&
-       !strcasecmp(options->value, "Normal"))
+                             &options))
+    {
+      printf("FAIL (%d options:", num_options);
+      for (i = 0; i < num_options; i ++)
+        printf(" %s=%s", options[i].name, options[i].value);
+      puts(")");
+      status ++;
+    }
+    else
+      puts("PASS (Unable to resolve)");
+    cupsFreeOptions(num_options, options);
+
+    fputs("cupsResolveConflicts(No option/choice): ", stdout);
+    num_options = 0;
+    options     = NULL;
+    if (cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options) &&
+        num_options == 1 && !_cups_strcasecmp(options->name, "Quality") &&
+       !_cups_strcasecmp(options->value, "Normal"))
       puts("PASS");
     else if (num_options > 0)
     {
@@ -447,11 +733,11 @@ main(int  argc,                           /* I - Number of command-line arguments */
 
     fputs("cupsResolveConflicts(loop test): ", stdout);
     ppdMarkOption(ppd, "PageSize", "A4");
+    ppdMarkOption(ppd, "InputSlot", "Tray");
     ppdMarkOption(ppd, "Quality", "Photo");
     num_options = 0;
     options     = NULL;
-    if (!cupsResolveConflicts(ppd, "Quality", "Photo", &num_options,
-                             &options))
+    if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
       puts("PASS");
     else if (num_options > 0)
     {
@@ -462,7 +748,7 @@ main(int  argc,                             /* I - Number of command-line arguments */
     }
     else
       puts("FAIL (No conflicts!)");
-    
+
     fputs("ppdInstallableConflict(): ", stdout);
     if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") &&
         !ppdInstallableConflict(ppd, "Duplex", "None"))
@@ -477,17 +763,218 @@ main(int  argc,                          /* I - Number of command-line arguments */
       puts("FAIL (Duplex=None conflicted)");
       status ++;
     }
+
+   /*
+    * ppdPageSizeLimits
+    */
+
+    ppdMarkDefaults(ppd);
+
+    fputs("ppdPageSizeLimits(default): ", stdout);
+    if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+    {
+      if (fabs(minsize.width - 36.0) > 0.001 || fabs(minsize.length - 36.0) > 0.001 ||
+          fabs(maxsize.width - 1080.0) > 0.001 || fabs(maxsize.length - 86400.0) > 0.001)
+      {
+        printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+              "expected min=36x36, max=1080x86400)\n", minsize.width,
+              minsize.length, maxsize.width, maxsize.length);
+        status ++;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (returned 0)");
+      status ++;
+    }
+
+    ppdMarkOption(ppd, "InputSlot", "Manual");
+
+    fputs("ppdPageSizeLimits(InputSlot=Manual): ", stdout);
+    if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+    {
+      if (fabs(minsize.width - 100.0) > 0.001 || fabs(minsize.length - 100.0) > 0.001 ||
+          fabs(maxsize.width - 1000.0) > 0.001 || fabs(maxsize.length - 1000.0) > 0.001)
+      {
+        printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+              "expected min=100x100, max=1000x1000)\n", minsize.width,
+              minsize.length, maxsize.width, maxsize.length);
+        status ++;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (returned 0)");
+      status ++;
+    }
+
+    ppdMarkOption(ppd, "Quality", "Photo");
+
+    fputs("ppdPageSizeLimits(Quality=Photo): ", stdout);
+    if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+    {
+      if (fabs(minsize.width - 200.0) > 0.001 || fabs(minsize.length - 200.0) > 0.001 ||
+          fabs(maxsize.width - 1000.0) > 0.001 || fabs(maxsize.length - 1000.0) > 0.001)
+      {
+        printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+              "expected min=200x200, max=1000x1000)\n", minsize.width,
+              minsize.length, maxsize.width, maxsize.length);
+        status ++;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (returned 0)");
+      status ++;
+    }
+
+    ppdMarkOption(ppd, "InputSlot", "Tray");
+
+    fputs("ppdPageSizeLimits(Quality=Photo): ", stdout);
+    if (ppdPageSizeLimits(ppd, &minsize, &maxsize))
+    {
+      if (fabs(minsize.width - 300.0) > 0.001 || fabs(minsize.length - 300.0) > 0.001 ||
+          fabs(maxsize.width - 1080.0) > 0.001 || fabs(maxsize.length - 86400.0) > 0.001)
+      {
+        printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, "
+              "expected min=300x300, max=1080x86400)\n", minsize.width,
+              minsize.length, maxsize.width, maxsize.length);
+        status ++;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (returned 0)");
+      status ++;
+    }
+  }
+  else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7))
+  {
+   /*
+    * ipp://... or ipps://...
+    */
+
+    http_t     *http;                  /* Connection to printer */
+    ipp_t      *request,               /* Get-Printer-Attributes request */
+               *response;              /* Get-Printer-Attributes response */
+    char       scheme[32],             /* URI scheme */
+               userpass[256],          /* Username:password */
+               host[256],              /* Hostname */
+               resource[256];          /* Resource path */
+    int                port;                   /* Port number */
+    static const char * const pattrs[] =/* Requested printer attributes */
+    {
+      "job-template",
+      "printer-defaults",
+      "printer-description",
+      "media-col-database"
+    };
+
+    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)
+    {
+      printf("Bad URI \"%s\".\n", argv[1]);
+      return (1);
+    }
+
+    http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
+    if (!http)
+    {
+      printf("Unable to connect to \"%s:%d\": %s\n", host, port, cupsLastErrorString());
+      return (1);
+    }
+
+    request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, argv[1]);
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]), NULL, pattrs);
+    response = cupsDoRequest(http, request, resource);
+
+    if (_ppdCreateFromIPP(buffer, sizeof(buffer), response))
+      printf("Created PPD: %s\n", buffer);
+    else
+      puts("Unable to create PPD.");
+
+    ippDelete(response);
+    httpClose(http);
+    return (0);
   }
   else
   {
     const char *filename;              /* PPD filename */
+    struct stat        fileinfo;               /* File information */
 
 
-    if (!strncmp(argv[1], "-d", 2))
-      filename = cupsGetPPD(argv[1] + 2);
+    if (strchr(argv[1], ':'))
+    {
+     /*
+      * Server PPD...
+      */
+
+      if ((filename = cupsGetServerPPD(CUPS_HTTP_DEFAULT, argv[1])) == NULL)
+      {
+        printf("%s: %s\n", argv[1], cupsLastErrorString());
+        return (1);
+      }
+    }
+    else if (!strncmp(argv[1], "-d", 2))
+    {
+      const char *printer;             /* Printer name */
+
+      if (argv[1][2])
+       printer = argv[1] + 2;
+      else if (argv[2])
+       printer = argv[2];
+      else
+      {
+        puts("Usage: ./testppd -d printer");
+       return (1);
+      }
+
+      filename = cupsGetPPD(printer);
+
+      if (!filename)
+      {
+        printf("%s: %s\n", printer, cupsLastErrorString());
+        return (1);
+      }
+    }
     else
       filename = argv[1];
 
+    if (lstat(filename, &fileinfo))
+    {
+      printf("%s: %s\n", filename, strerror(errno));
+      return (1);
+    }
+
+    if (S_ISLNK(fileinfo.st_mode))
+    {
+      char     realfile[1024];         /* Real file path */
+      ssize_t  realsize;               /* Size of real file path */
+
+
+      if ((realsize = readlink(filename, realfile, sizeof(realfile) - 1)) < 0)
+        strlcpy(realfile, "Unknown", sizeof(realfile));
+      else
+        realfile[realsize] = '\0';
+
+      if (stat(realfile, &fileinfo))
+       printf("%s: symlink to \"%s\", %s\n", filename, realfile,
+              strerror(errno));
+      else
+       printf("%s: symlink to \"%s\", %ld bytes\n", filename, realfile,
+              (long)fileinfo.st_size);
+    }
+    else
+      printf("%s: regular file, %ld bytes\n", filename, (long)fileinfo.st_size);
+
     if ((ppd = ppdOpenFile(filename)) == NULL)
     {
       ppd_status_t     err;            /* Last error in file */
@@ -502,22 +989,39 @@ main(int  argc,                           /* I - Number of command-line arguments */
     else
     {
       int              j, k;           /* Looping vars */
-      ppd_attr_t       *attr;          /* Current attribute */
       ppd_group_t      *group;         /* Option group */
       ppd_option_t     *option;        /* Option */
       ppd_coption_t    *coption;       /* Custom option */
       ppd_cparam_t     *cparam;        /* Custom parameter */
       ppd_const_t      *c;             /* UIConstraints */
-      char             lang[255];      /* LANG environment variable */
+      char             lang[255],      /* LANG environment variable */
+                       lc_all[255],    /* LC_ALL environment variable */
+                       lc_ctype[255],  /* LC_CTYPE environment variable */
+                       lc_messages[255];/* LC_MESSAGES environment variable */
 
 
       if (argc > 2)
       {
         snprintf(lang, sizeof(lang), "LANG=%s", argv[2]);
        putenv(lang);
+        snprintf(lc_all, sizeof(lc_all), "LC_ALL=%s", argv[2]);
+       putenv(lc_all);
+        snprintf(lc_ctype, sizeof(lc_ctype), "LC_CTYPE=%s", argv[2]);
+       putenv(lc_ctype);
+        snprintf(lc_messages, sizeof(lc_messages), "LC_MESSAGES=%s", argv[2]);
+       putenv(lc_messages);
       }
 
       ppdLocalize(ppd);
+      ppdMarkDefaults(ppd);
+
+      if (argc > 3)
+      {
+        text = ppdLocalizeIPPReason(ppd, argv[3], NULL, buffer, sizeof(buffer));
+       printf("ppdLocalizeIPPReason(%s)=%s\n", argv[3],
+              text ? text : "(null)");
+       return (text == NULL);
+      }
 
       for (i = ppd->num_groups, group = ppd->groups;
           i > 0;
@@ -532,8 +1036,9 @@ main(int  argc,                            /* I - Number of command-line arguments */
          printf("    %s (%s):\n", option->keyword, option->text);
 
          for (k = 0; k < option->num_choices; k ++)
-           printf("        - %s (%s)\n", option->choices[k].choice,
-                  option->choices[k].text);
+           printf("        - %s%s (%s)\n",
+                  option->choices[k].marked ? "*" : "",
+                  option->choices[k].choice, option->choices[k].text);
 
           if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
          {
@@ -604,19 +1109,43 @@ main(int  argc,                          /* I - Number of command-line arguments */
        }
       }
 
-      puts("Constraints:");
+      puts("\nSizes:");
+      for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+        printf("    %s = %gx%g, [%g %g %g %g]\n", size->name, size->width,
+              size->length, size->left, size->bottom, size->right, size->top);
+
+      puts("\nConstraints:");
 
       for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
         printf("    *UIConstraints: *%s %s *%s %s\n", c->option1, c->choice1,
               c->option2, c->choice2);
+      if (ppd->num_consts == 0)
+        puts("    NO CONSTRAINTS");
+
+      puts("\nFilters:");
 
-      puts("Attributes:");
+      for (i = 0; i < ppd->num_filters; i ++)
+        printf("    %s\n", ppd->filters[i]);
+
+      if (ppd->num_filters == 0)
+        puts("    NO FILTERS");
+
+      puts("\nAttributes:");
 
       for (attr = (ppd_attr_t *)cupsArrayFirst(ppd->sorted_attrs);
            attr;
           attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs))
         printf("    *%s %s/%s: \"%s\"\n", attr->name, attr->spec,
               attr->text, attr->value ? attr->value : "");
+
+      puts("\nPPD Cache:");
+      if ((pc = _ppdCacheCreateWithPPD(ppd)) == NULL)
+        printf("    Unable to create: %s\n", cupsLastErrorString());
+      else
+      {
+        _ppdCacheWriteFile(pc, "t.cache", NULL);
+        puts("    Wrote t.cache.");
+      }
     }
 
     if (!strncmp(argv[1], "-d", 2))
@@ -639,8 +1168,3 @@ main(int  argc,                            /* I - Number of command-line arguments */
 
   return (status);
 }
-
-
-/*
- * End of "$Id: testppd.c 7633 2008-06-10 23:07:29Z mike $".
- */