]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/testipp.c
Merge pull request #5621 from zdohnal/cgigetarray-sigsegv
[thirdparty/cups.git] / cups / testipp.c
index efe2174f122c9e5f0d2097b0bac89cea5e733dec..aad53e46fcbae45fdc5aaedcba17076023cb504a 100644 (file)
@@ -1,26 +1,11 @@
 /*
- * "$Id: testipp.c 6649 2007-07-11 21:46:42Z mike $"
+ * IPP test program for CUPS.
  *
- *   IPP test program for CUPS.
+ * Copyright © 2007-2019 by Apple Inc.
+ * Copyright © 1997-2005 by Easy Software Products.
  *
- *   Copyright 2007-2011 by Apple Inc.
- *   Copyright 1997-2005 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.
- *   hex_dump()         - Produce a hex dump of a buffer.
- *   print_attributes() - Print the attributes in a request...
- *   read_cb()          - Read data from a buffer.
- *   write_cb()         - Write data into a buffer.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
  */
 
 /*
 #include "file.h"
 #include "string-private.h"
 #include "ipp-private.h"
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
 #  include <fcntl.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -55,12 +40,12 @@ typedef struct _ippdata_t
  * Local globals...
  */
 
-ipp_uchar_t    collection[] =          /* Collection buffer */
+static ipp_uchar_t collection[] =      /* Collection buffer */
                {
                  0x01, 0x01,           /* IPP version */
                  0x00, 0x02,           /* Print-Job operation */
                  0x00, 0x00, 0x00, 0x01,
-                                       /* Request ID */
+                                       /* Request ID */
 
                  IPP_TAG_OPERATION,
 
@@ -90,7 +75,7 @@ ipp_uchar_t   collection[] =          /* Collection buffer */
                  IPP_TAG_JOB,          /* job group tag */
 
                  IPP_TAG_BEGIN_COLLECTION,
-                                       /* begCollection tag */
+                                       /* begCollection tag */
                  0x00, 0x09,           /* Name length + name */
                  'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
                  0x00, 0x00,           /* No value */
@@ -99,11 +84,11 @@ ipp_uchar_t collection[] =          /* Collection buffer */
                    0x00, 0x0a,         /* Value length + value */
                    'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
                    IPP_TAG_BEGIN_COLLECTION,
-                                       /* begCollection tag */
+                                       /* begCollection tag */
                    0x00, 0x00,         /* Name length + name */
                    0x00, 0x00,         /* No value */
                      IPP_TAG_MEMBERNAME,
-                                       /* memberAttrName tag */
+                                       /* memberAttrName tag */
                      0x00, 0x00,       /* No name */
                      0x00, 0x0b,       /* Value length + value */
                      'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
@@ -112,7 +97,7 @@ ipp_uchar_t  collection[] =          /* Collection buffer */
                      0x00, 0x04,       /* Value length + value */
                      0x00, 0x00, 0x54, 0x56,
                      IPP_TAG_MEMBERNAME,
-                                       /* memberAttrName tag */
+                                       /* memberAttrName tag */
                      0x00, 0x00,       /* No name */
                      0x00, 0x0b,       /* Value length + value */
                      'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
@@ -121,7 +106,7 @@ ipp_uchar_t collection[] =          /* Collection buffer */
                      0x00, 0x04,       /* Value length + value */
                      0x00, 0x00, 0x6d, 0x24,
                    IPP_TAG_END_COLLECTION,
-                                       /* endCollection tag */
+                                       /* endCollection tag */
                    0x00, 0x00,         /* No name */
                    0x00, 0x00,         /* No value */
                    IPP_TAG_MEMBERNAME, /* memberAttrName tag */
@@ -142,12 +127,12 @@ ipp_uchar_t       collection[] =          /* Collection buffer */
                    0x00, 0x05,         /* Value length + value */
                    'p', 'l', 'a', 'i', 'n',
                  IPP_TAG_END_COLLECTION,
-                                       /* endCollection tag */
+                                       /* endCollection tag */
                  0x00, 0x00,           /* No name */
                  0x00, 0x00,           /* No value */
 
                  IPP_TAG_BEGIN_COLLECTION,
-                                       /* begCollection tag */
+                                       /* begCollection tag */
                  0x00, 0x00,           /* No name */
                  0x00, 0x00,           /* No value */
                    IPP_TAG_MEMBERNAME, /* memberAttrName tag */
@@ -155,11 +140,11 @@ ipp_uchar_t       collection[] =          /* Collection buffer */
                    0x00, 0x0a,         /* Value length + value */
                    'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
                    IPP_TAG_BEGIN_COLLECTION,
-                                       /* begCollection tag */
+                                       /* begCollection tag */
                    0x00, 0x00,         /* Name length + name */
                    0x00, 0x00,         /* No value */
                      IPP_TAG_MEMBERNAME,
-                                       /* memberAttrName tag */
+                                       /* memberAttrName tag */
                      0x00, 0x00,       /* No name */
                      0x00, 0x0b,       /* Value length + value */
                      'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
@@ -168,7 +153,7 @@ ipp_uchar_t collection[] =          /* Collection buffer */
                      0x00, 0x04,       /* Value length + value */
                      0x00, 0x00, 0x52, 0x08,
                      IPP_TAG_MEMBERNAME,
-                                       /* memberAttrName tag */
+                                       /* memberAttrName tag */
                      0x00, 0x00,       /* No name */
                      0x00, 0x0b,       /* Value length + value */
                      'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
@@ -177,7 +162,7 @@ ipp_uchar_t collection[] =          /* Collection buffer */
                      0x00, 0x04,       /* Value length + value */
                      0x00, 0x00, 0x74, 0x04,
                    IPP_TAG_END_COLLECTION,
-                                       /* endCollection tag */
+                                       /* endCollection tag */
                    0x00, 0x00,         /* No name */
                    0x00, 0x00,         /* No value */
                    IPP_TAG_MEMBERNAME, /* memberAttrName tag */
@@ -198,19 +183,84 @@ ipp_uchar_t       collection[] =          /* Collection buffer */
                    0x00, 0x06,         /* Value length + value */
                    'g', 'l', 'o', 's', 's', 'y',
                  IPP_TAG_END_COLLECTION,
-                                       /* endCollection tag */
+                                       /* endCollection tag */
                  0x00, 0x00,           /* No name */
                  0x00, 0x00,           /* No value */
 
                  IPP_TAG_END           /* end tag */
                };
+static ipp_uchar_t bad_collection[] =  /* Collection buffer (bad encoding) */
+               {
+                 0x01, 0x01,           /* IPP version */
+                 0x00, 0x02,           /* Print-Job operation */
+                 0x00, 0x00, 0x00, 0x01,
+                                       /* Request ID */
+
+                 IPP_TAG_OPERATION,
+
+                 IPP_TAG_CHARSET,
+                 0x00, 0x12,           /* Name length + name */
+                 'a','t','t','r','i','b','u','t','e','s','-',
+                 'c','h','a','r','s','e','t',
+                 0x00, 0x05,           /* Value length + value */
+                 'u','t','f','-','8',
+
+                 IPP_TAG_LANGUAGE,
+                 0x00, 0x1b,           /* Name length + name */
+                 'a','t','t','r','i','b','u','t','e','s','-',
+                 'n','a','t','u','r','a','l','-','l','a','n',
+                 'g','u','a','g','e',
+                 0x00, 0x02,           /* Value length + value */
+                 'e','n',
+
+                 IPP_TAG_URI,
+                 0x00, 0x0b,           /* Name length + name */
+                 'p','r','i','n','t','e','r','-','u','r','i',
+                 0x00, 0x1c,                   /* Value length + value */
+                 'i','p','p',':','/','/','l','o','c','a','l',
+                 'h','o','s','t','/','p','r','i','n','t','e',
+                 'r','s','/','f','o','o',
+
+                 IPP_TAG_JOB,          /* job group tag */
 
-ipp_uchar_t    mixed[] =               /* Mixed value buffer */
+                 IPP_TAG_BEGIN_COLLECTION,
+                                       /* begCollection tag */
+                 0x00, 0x09,           /* Name length + name */
+                 'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
+                 0x00, 0x00,           /* No value */
+                   IPP_TAG_BEGIN_COLLECTION,
+                                       /* begCollection tag */
+                   0x00, 0x0a,         /* Name length + name */
+                   'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
+                   0x00, 0x00,         /* No value */
+                     IPP_TAG_INTEGER,  /* integer tag */
+                     0x00, 0x0b,       /* Name length + name */
+                     'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
+                     0x00, 0x04,       /* Value length + value */
+                     0x00, 0x00, 0x54, 0x56,
+                     IPP_TAG_INTEGER,  /* integer tag */
+                     0x00, 0x0b,       /* Name length + name */
+                     'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
+                     0x00, 0x04,       /* Value length + value */
+                     0x00, 0x00, 0x6d, 0x24,
+                   IPP_TAG_END_COLLECTION,
+                                       /* endCollection tag */
+                   0x00, 0x00,         /* No name */
+                   0x00, 0x00,         /* No value */
+                 IPP_TAG_END_COLLECTION,
+                                       /* endCollection tag */
+                 0x00, 0x00,           /* No name */
+                 0x00, 0x00,           /* No value */
+
+                 IPP_TAG_END           /* end tag */
+               };
+
+static ipp_uchar_t mixed[] =           /* Mixed value buffer */
                {
                  0x01, 0x01,           /* IPP version */
                  0x00, 0x02,           /* Print-Job operation */
                  0x00, 0x00, 0x00, 0x01,
-                                       /* Request ID */
+                                       /* Request ID */
 
                  IPP_TAG_OPERATION,
 
@@ -236,9 +286,11 @@ ipp_uchar_t        mixed[] =               /* Mixed value buffer */
  * Local functions...
  */
 
-void   hex_dump(const char *title, ipp_uchar_t *buffer, int bytes);
+void   hex_dump(const char *title, ipp_uchar_t *buffer, size_t bytes);
 void   print_attributes(ipp_t *ipp, int indent);
 ssize_t        read_cb(_ippdata_t *data, ipp_uchar_t *buffer, size_t bytes);
+ssize_t        read_hex(cups_file_t *fp, ipp_uchar_t *buffer, size_t bytes);
+int    token_cb(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, const char *token);
 ssize_t        write_cb(_ippdata_t *data, ipp_uchar_t *buffer, size_t bytes);
 
 
@@ -259,10 +311,13 @@ main(int  argc,                   /* I - Number of command-line arguments */
                *media_size,    /* media-size attribute */
                *attr;          /* Other attribute */
   ipp_state_t  state;          /* State */
-  int          length;         /* Length of data */
+  size_t       length;         /* Length of data */
   cups_file_t  *fp;            /* File pointer */
-  int          i;              /* Looping var */
+  size_t       i;              /* Looping var */
   int          status;         /* Status of tests (0 = success, 1 = fail) */
+#ifdef DEBUG
+  const char   *name;          /* Option name */
+#endif /* DEBUG */
 
 
   status = 0;
@@ -278,7 +333,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
     request = ippNew();
     request->request.op.version[0]   = 0x01;
     request->request.op.version[1]   = 0x01;
-    request->request.op.operation_id = IPP_PRINT_JOB;
+    request->request.op.operation_id = IPP_OP_PRINT_JOB;
     request->request.op.request_id   = 1;
 
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
@@ -319,7 +374,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
     if (length != sizeof(collection))
     {
       printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
-             length, (int)sizeof(collection));
+             (int)length, (int)sizeof(collection));
       status = 1;
     }
     else
@@ -336,11 +391,11 @@ main(int  argc,                   /* I - Number of command-line arguments */
     data.wbuffer = buffer;
 
     while ((state = ippWriteIO(&data, (ipp_iocb_t)write_cb, 1, NULL,
-                               request)) != IPP_DATA)
-      if (state == IPP_ERROR)
+                               request)) != IPP_STATE_DATA)
+      if (state == IPP_STATE_ERROR)
        break;
 
-    if (state != IPP_DATA)
+    if (state != IPP_STATE_DATA)
     {
       printf("FAIL - %d bytes written.\n", (int)data.wused);
       status = 1;
@@ -359,7 +414,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
         if (data.wbuffer[i] != collection[i])
          break;
 
-      printf("FAIL - output does not match baseline at 0x%04x!\n", i);
+      printf("FAIL - output does not match baseline at 0x%04x!\n", (unsigned)i);
       hex_dump("Bytes Written", data.wbuffer, data.wused);
       hex_dump("Baseline", collection, sizeof(collection));
       status = 1;
@@ -379,13 +434,13 @@ main(int  argc,                   /* I - Number of command-line arguments */
     data.rpos = 0;
 
     while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL,
-                              request)) != IPP_DATA)
-      if (state == IPP_ERROR)
+                              request)) != IPP_STATE_DATA)
+      if (state == IPP_STATE_ERROR)
        break;
 
     length = ippLength(request);
 
-    if (state != IPP_DATA)
+    if (state != IPP_STATE_DATA)
     {
       printf("FAIL - %d bytes read.\n", (int)data.rpos);
       status = 1;
@@ -400,7 +455,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
     else if (length != sizeof(collection))
     {
       printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
-             length, (int)sizeof(collection));
+             (int)length, (int)sizeof(collection));
       print_attributes(request, 8);
       status = 1;
     }
@@ -547,14 +602,82 @@ main(int  argc,                   /* I - Number of command-line arguments */
       }
     }
 
+   /*
+    * Test hierarchical find...
+    */
+
+    fputs("ippFindAttribute(media-col/media-size/x-dimension): ", stdout);
+    if ((attr = ippFindAttribute(request, "media-col/media-size/x-dimension", IPP_TAG_INTEGER)) != NULL)
+    {
+      if (ippGetInteger(attr, 0) != 21590)
+      {
+        printf("FAIL (wrong value for x-dimension - %d)\n", ippGetInteger(attr, 0));
+        status = 1;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (not found)");
+      status = 1;
+    }
+
+    fputs("ippFindNextAttribute(media-col/media-size/x-dimension): ", stdout);
+    if ((attr = ippFindNextAttribute(request, "media-col/media-size/x-dimension", IPP_TAG_INTEGER)) != NULL)
+    {
+      if (ippGetInteger(attr, 0) != 21000)
+      {
+        printf("FAIL (wrong value for x-dimension - %d)\n", ippGetInteger(attr, 0));
+        status = 1;
+      }
+      else
+        puts("PASS");
+    }
+    else
+    {
+      puts("FAIL (not found)");
+      status = 1;
+    }
+
+    fputs("ippFindNextAttribute(media-col/media-size/x-dimension) again: ", stdout);
+    if ((attr = ippFindNextAttribute(request, "media-col/media-size/x-dimension", IPP_TAG_INTEGER)) != NULL)
+    {
+      printf("FAIL (got %d, expected nothing)\n", ippGetInteger(attr, 0));
+      status = 1;
+    }
+    else
+      puts("PASS");
+
     ippDelete(request);
 
+   /*
+    * Read the bad collection data and confirm we get an error...
+    */
+
+    fputs("Read Bad Collection from Memory: ", stdout);
+
+    request = ippNew();
+    data.rpos    = 0;
+    data.wused   = sizeof(bad_collection);
+    data.wsize   = sizeof(bad_collection);
+    data.wbuffer = bad_collection;
+
+    while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL, request)) != IPP_STATE_DATA)
+      if (state == IPP_STATE_ERROR)
+       break;
+
+    if (state != IPP_STATE_ERROR)
+      puts("FAIL (read successful)");
+    else
+      puts("PASS");
+
    /*
     * Read the mixed data and confirm we converted everything to rangeOfInteger
     * values...
     */
 
-    printf("Read Mixed integer/rangeOfInteger from Memory: ");
+    fputs("Read Mixed integer/rangeOfInteger from Memory: ", stdout);
 
     request = ippNew();
     data.rpos    = 0;
@@ -563,13 +686,13 @@ main(int  argc,                   /* I - Number of command-line arguments */
     data.wbuffer = mixed;
 
     while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL,
-                              request)) != IPP_DATA)
-      if (state == IPP_ERROR)
+                              request)) != IPP_STATE_DATA)
+      if (state == IPP_STATE_ERROR)
        break;
 
     length = ippLength(request);
 
-    if (state != IPP_DATA)
+    if (state != IPP_STATE_DATA)
     {
       printf("FAIL - %d bytes read.\n", (int)data.rpos);
       status = 1;
@@ -584,7 +707,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
     else if (length != (sizeof(mixed) + 4))
     {
       printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
-             length, (int)sizeof(mixed) + 4);
+             (int)length, (int)sizeof(mixed) + 4);
       print_attributes(request, 8);
       status = 1;
     }
@@ -625,11 +748,26 @@ main(int  argc,                   /* I - Number of command-line arguments */
 
     ippDelete(request);
 
+#ifdef DEBUG
+   /*
+    * Test that private option array is sorted...
+    */
+
+    fputs("_ippCheckOptions: ", stdout);
+    if ((name = _ippCheckOptions()) == NULL)
+      puts("PASS");
+    else
+    {
+      printf("FAIL (\"%s\" out of order)\n", name);
+      status = 1;
+    }
+#endif /* DEBUG */
+
    /*
     * Test _ippFindOption() private API...
     */
 
-    fputs("_ippFindOption(printer-type): ", stdout);
+    fputs("_ippFindOption(\"printer-type\"): ", stdout);
     if (_ippFindOption("printer-type"))
       puts("PASS");
     else
@@ -655,32 +793,82 @@ main(int  argc,                   /* I - Number of command-line arguments */
     * Read IPP files...
     */
 
-    for (i = 1; i < argc; i ++)
+    for (i = 1; i < (size_t)argc; i ++)
     {
-      if ((fp = cupsFileOpen(argv[i], "r")) == NULL)
+      if (strlen(argv[i]) > 5 && !strcmp(argv[i] + strlen(argv[i]) - 5, ".test"))
       {
-       printf("Unable to open \"%s\" - %s\n", argv[i], strerror(errno));
-       status = 1;
-       continue;
-      }
+       /*
+        * Read an ASCII IPP message...
+        */
 
-      request = ippNew();
-      while ((state = ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
-                                request)) == IPP_ATTRIBUTE);
+        _ipp_vars_t v;                 /* IPP variables */
 
-      if (state != IPP_DATA)
+        _ippVarsInit(&v, NULL, NULL, token_cb);
+        request = _ippFileParse(&v, argv[i], NULL);
+        _ippVarsDeinit(&v);
+      }
+      else if (strlen(argv[i]) > 4 && !strcmp(argv[i] + strlen(argv[i]) - 4, ".hex"))
       {
-       printf("Error reading IPP message from \"%s\"!\n", argv[i]);
-       status = 1;
+       /*
+        * Read a hex-encoded IPP message...
+        */
+
+       if ((fp = cupsFileOpen(argv[i], "r")) == NULL)
+       {
+         printf("Unable to open \"%s\" - %s\n", argv[i], strerror(errno));
+         status = 1;
+         continue;
+       }
+
+       request = ippNew();
+       while ((state = ippReadIO(fp, (ipp_iocb_t)read_hex, 1, NULL, request)) == IPP_STATE_ATTRIBUTE);
+
+       if (state != IPP_STATE_DATA)
+       {
+         printf("Error reading IPP message from \"%s\": %s\n", argv[i], cupsLastErrorString());
+         status = 1;
+
+         ippDelete(request);
+         request = NULL;
+       }
+
+        cupsFileClose(fp);
       }
       else
+      {
+       /*
+        * Read a raw (binary) IPP message...
+        */
+
+       if ((fp = cupsFileOpen(argv[i], "r")) == NULL)
+       {
+         printf("Unable to open \"%s\" - %s\n", argv[i], strerror(errno));
+         status = 1;
+         continue;
+       }
+
+       request = ippNew();
+       while ((state = ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
+                                 request)) == IPP_STATE_ATTRIBUTE);
+
+       if (state != IPP_STATE_DATA)
+       {
+         printf("Error reading IPP message from \"%s\": %s\n", argv[i], cupsLastErrorString());
+         status = 1;
+
+         ippDelete(request);
+         request = NULL;
+       }
+
+        cupsFileClose(fp);
+      }
+
+      if (request)
       {
        printf("\n%s:\n", argv[i]);
        print_attributes(request, 4);
+       ippDelete(request);
       }
-
-      ippDelete(request);
-      cupsFileClose(fp);
     }
   }
 
@@ -695,10 +883,10 @@ main(int  argc,                   /* I - Number of command-line arguments */
 void
 hex_dump(const char  *title,           /* I - Title */
          ipp_uchar_t *buffer,          /* I - Buffer to dump */
-         int         bytes)            /* I - Number of bytes */
+         size_t      bytes)            /* I - Number of bytes */
 {
-  int  i, j;                           /* Looping vars */
-  int  ch;                             /* Current ASCII char */
+  size_t       i, j;                   /* Looping vars */
+  int          ch;                     /* Current ASCII char */
 
 
  /*
@@ -713,7 +901,7 @@ hex_dump(const char  *title,                /* I - Title */
     * Show the offset...
     */
 
-    printf("    %04x ", i);
+    printf("    %04x ", (unsigned)i);
 
    /*
     * Then up to 16 bytes in hex...
@@ -755,88 +943,9 @@ void
 print_attributes(ipp_t *ipp,           /* I - IPP request */
                  int   indent)         /* I - Indentation */
 {
-  int                  i;              /* Looping var */
   ipp_tag_t            group;          /* Current group */
   ipp_attribute_t      *attr;          /* Current attribute */
-  ipp_value_t          *val;           /* Current value */
-  static const char * const tags[] =   /* Value/group tag strings */
-                       {
-                         "reserved-00",
-                         "operation-attributes-tag",
-                         "job-attributes-tag",
-                         "end-of-attributes-tag",
-                         "printer-attributes-tag",
-                         "unsupported-attributes-tag",
-                         "subscription-attributes-tag",
-                         "event-attributes-tag",
-                         "reserved-08",
-                         "reserved-09",
-                         "reserved-0A",
-                         "reserved-0B",
-                         "reserved-0C",
-                         "reserved-0D",
-                         "reserved-0E",
-                         "reserved-0F",
-                         "unsupported",
-                         "default",
-                         "unknown",
-                         "no-value",
-                         "reserved-14",
-                         "not-settable",
-                         "delete-attr",
-                         "admin-define",
-                         "reserved-18",
-                         "reserved-19",
-                         "reserved-1A",
-                         "reserved-1B",
-                         "reserved-1C",
-                         "reserved-1D",
-                         "reserved-1E",
-                         "reserved-1F",
-                         "reserved-20",
-                         "integer",
-                         "boolean",
-                         "enum",
-                         "reserved-24",
-                         "reserved-25",
-                         "reserved-26",
-                         "reserved-27",
-                         "reserved-28",
-                         "reserved-29",
-                         "reserved-2a",
-                         "reserved-2b",
-                         "reserved-2c",
-                         "reserved-2d",
-                         "reserved-2e",
-                         "reserved-2f",
-                         "octetString",
-                         "dateTime",
-                         "resolution",
-                         "rangeOfInteger",
-                         "begCollection",
-                         "textWithLanguage",
-                         "nameWithLanguage",
-                         "endCollection",
-                         "reserved-38",
-                         "reserved-39",
-                         "reserved-3a",
-                         "reserved-3b",
-                         "reserved-3c",
-                         "reserved-3d",
-                         "reserved-3e",
-                         "reserved-3f",
-                         "reserved-40",
-                         "textWithoutLanguage",
-                         "nameWithoutLanguage",
-                         "reserved-43",
-                         "keyword",
-                         "uri",
-                         "uriScheme",
-                         "charset",
-                         "naturalLanguage",
-                         "mimeMediaType",
-                         "memberName"
-                       };
+  char                  buffer[2048];   /* Value string */
 
 
   for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
@@ -852,90 +961,12 @@ print_attributes(ipp_t *ipp,              /* I - IPP request */
     {
       group = attr->group_tag;
 
-      printf("\n%*s%s:\n\n", indent - 4, "", tags[group]);
+      printf("\n%*s%s:\n\n", indent - 4, "", ippTagString(group));
     }
 
-    printf("%*s%s (", indent, "", attr->name ? attr->name : "(null)");
-    if (attr->num_values > 1)
-      printf("1setOf ");
-    printf("%s):", tags[attr->value_tag]);
+    ippAttributeString(attr, buffer, sizeof(buffer));
 
-    switch (attr->value_tag)
-    {
-      case IPP_TAG_ENUM :
-      case IPP_TAG_INTEGER :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-           printf(" %d", val->integer);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_BOOLEAN :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-           printf(" %s", val->boolean ? "true" : "false");
-          putchar('\n');
-          break;
-
-      case IPP_TAG_RANGE :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-           printf(" %d-%d", val->range.lower, val->range.upper);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_DATE :
-          {
-           time_t      vtime;          /* Date/Time value */
-           struct tm   *vdate;         /* Date info */
-           char        vstring[256];   /* Formatted time */
-
-           for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-           {
-             vtime = ippDateToTime(val->date);
-             vdate = localtime(&vtime);
-             strftime(vstring, sizeof(vstring), "%c", vdate);
-             printf(" (%s)", vstring);
-           }
-          }
-          putchar('\n');
-          break;
-
-      case IPP_TAG_RESOLUTION :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-           printf(" %dx%d%s", val->resolution.xres, val->resolution.yres,
-                  val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpc");
-          putchar('\n');
-          break;
-
-      case IPP_TAG_STRING :
-      case IPP_TAG_TEXTLANG :
-      case IPP_TAG_NAMELANG :
-      case IPP_TAG_TEXT :
-      case IPP_TAG_NAME :
-      case IPP_TAG_KEYWORD :
-      case IPP_TAG_URI :
-      case IPP_TAG_URISCHEME :
-      case IPP_TAG_CHARSET :
-      case IPP_TAG_LANGUAGE :
-      case IPP_TAG_MIMETYPE :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-           printf(" \"%s\"", val->string.text);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_BEGIN_COLLECTION :
-          putchar('\n');
-
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-         {
-           if (i)
-             putchar('\n');
-           print_attributes(val->collection, indent + 4);
-         }
-          break;
-
-      default :
-          printf("UNKNOWN (%d values)\n", attr->num_values);
-          break;
-    }
+    printf("%*s%s (%s%s): %s\n", indent, "", attr->name ? attr->name : "(null)", attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), buffer);
   }
 }
 
@@ -966,7 +997,77 @@ read_cb(_ippdata_t   *data,                /* I - Data */
   * Return the number of bytes read...
   */
 
-  return (count);
+  return ((ssize_t)count);
+}
+
+
+/*
+ * 'read_hex()' - Read a hex dump of an IPP request.
+ */
+
+ssize_t                                        /* O - Number of bytes read */
+read_hex(cups_file_t *fp,              /* I - File to read from */
+         ipp_uchar_t *buffer,          /* I - Buffer to read */
+         size_t      bytes)            /* I - Number of bytes to read */
+{
+  size_t       total = 0;              /* Total bytes read */
+  static char  hex[256] = "";          /* Line from file */
+  static char  *hexptr = NULL;         /* Pointer in line */
+
+
+  while (total < bytes)
+  {
+    if (!hexptr || (isspace(hexptr[0] & 255) && isspace(hexptr[1] & 255)))
+    {
+      if (!cupsFileGets(fp, hex, sizeof(hex)))
+        break;
+
+      hexptr = hex;
+      while (isxdigit(*hexptr & 255))
+        hexptr ++;
+      while (isspace(*hexptr & 255))
+        hexptr ++;
+
+      if (!isxdigit(*hexptr & 255))
+      {
+        hexptr = NULL;
+        continue;
+      }
+    }
+
+    *buffer++ = (ipp_uchar_t)strtol(hexptr, &hexptr, 16);
+    total ++;
+  }
+
+  return (total == 0 ? -1 : (ssize_t)total);
+}
+
+
+/*
+ * 'token_cb()' - Token callback for ASCII IPP data file parser.
+ */
+
+int                                    /* O - 1 on success, 0 on failure */
+token_cb(_ipp_file_t *f,               /* I - IPP file data */
+         _ipp_vars_t *v,               /* I - IPP variables */
+         void        *user_data,       /* I - User data pointer */
+         const char  *token)           /* I - Token string */
+{
+  (void)v;
+  (void)user_data;
+
+  if (!token)
+  {
+    f->attrs     = ippNew();
+    f->group_tag = IPP_TAG_PRINTER;
+  }
+  else
+  {
+    fprintf(stderr, "Unknown directive \"%s\" on line %d of \"%s\".\n", token, f->linenum, f->filename);
+    return (0);
+  }
+
+  return (1);
 }
 
 
@@ -996,10 +1097,5 @@ write_cb(_ippdata_t   *data,              /* I - Data */
   * Return the number of bytes written...
   */
 
-  return (count);
+  return ((ssize_t)count);
 }
-
-
-/*
- * End of "$Id: testipp.c 6649 2007-07-11 21:46:42Z mike $".
- */