]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - test/ippfind.c
Bump version number on PostScript test file.
[thirdparty/cups.git] / test / ippfind.c
index 118a11f1f03aa4e319233d9726d5e2aa324237b5..c7e8765d0dc3efa2a4fe14c3797aa8008d87e9a3 100644 (file)
@@ -1,29 +1,26 @@
 /*
- * "$Id$"
+ * Utility to find IPP printers via Bonjour/DNS-SD and optionally run
+ * commands such as IPP and Bonjour conformance tests.  This tool is
+ * inspired by the UNIX "find" command, thus its name.
  *
- *   Utility to find IPP printers via Bonjour/DNS-SD and optionally run
- *   commands such as IPP and Bonjour conformance tests.  This tool is
- *   inspired by the UNIX "find" command, thus its name.
- *
- *   Copyright 2008-2013 by Apple Inc.
- *
- *   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:
+ * Copyright © 2008-2018 by Apple Inc.
  *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
  */
 
 /*
  * Include necessary headers.
  */
 
+#define _CUPS_NO_DEPRECATED
 #include <cups/cups-private.h>
+#ifdef WIN32
+#  include <process.h>
+#  include <sys/timeb.h>
+#else
+#  include <sys/wait.h>
+#endif /* WIN32 */
 #include <regex.h>
 #ifdef HAVE_DNSSD
 #  include <dns_sd.h>
 #  define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
 #endif /* HAVE_DNSSD */
 
+#ifndef WIN32
+extern char **environ;                 /* Process environment variables */
+#endif /* !WIN32 */
+
 
 /*
  * Structures...
@@ -63,6 +64,7 @@ typedef enum ippfind_op_e             /* Operations for expressions */
   IPPFIND_OP_IS_REMOTE,                        /* Is a remote service */
   IPPFIND_OP_DOMAIN_REGEX,             /* Domain matches regular expression */
   IPPFIND_OP_NAME_REGEX,               /* Name matches regular expression */
+  IPPFIND_OP_NAME_LITERAL,             /* Name matches literal string */
   IPPFIND_OP_HOST_REGEX,               /* Hostname matches regular expression */
   IPPFIND_OP_PORT_RANGE,               /* Port matches range */
   IPPFIND_OP_PATH_REGEX,               /* Path matches regular expression */
@@ -75,7 +77,7 @@ typedef enum ippfind_op_e             /* Operations for expressions */
   IPPFIND_OP_LIST,                     /* List when true */
   IPPFIND_OP_PRINT_NAME,               /* Print URI when true */
   IPPFIND_OP_PRINT_URI,                        /* Print name when true */
-  IPPFIND_OP_QUIET,                    /* No output when true */
+  IPPFIND_OP_QUIET                     /* No output when true */
 } ippfind_op_t;
 
 typedef struct ippfind_expr_s          /* Expression */
@@ -87,7 +89,7 @@ typedef struct ippfind_expr_s         /* Expression */
                *child;                 /* Child expressions */
   ippfind_op_t op;                     /* Operation code (see above) */
   int          invert;                 /* Invert the result */
-  char         *key;                   /* TXT record key */
+  char         *name;                  /* TXT record key or literal name */
   regex_t      re;                     /* Regular expression for matching */
   int          range[2];               /* Port number range */
   int          num_args;               /* Number of arguments for exec */
@@ -142,7 +144,7 @@ static int  ipp_version = 20;       /* IPP version for LIST */
  */
 
 #ifdef HAVE_DNSSD
-static void            browse_callback(DNSServiceRef sdRef,
+static void DNSSD_API  browse_callback(DNSServiceRef sdRef,
                                        DNSServiceFlags flags,
                                        uint32_t interfaceIndex,
                                        DNSServiceErrorType errorCode,
@@ -150,7 +152,7 @@ static void         browse_callback(DNSServiceRef sdRef,
                                        const char *regtype,
                                        const char *replyDomain, void *context)
                                        __attribute__((nonnull(1,5,6,7,8)));
-static void            browse_local_callback(DNSServiceRef sdRef,
+static void DNSSD_API  browse_local_callback(DNSServiceRef sdRef,
                                              DNSServiceFlags flags,
                                              uint32_t interfaceIndex,
                                              DNSServiceErrorType errorCode,
@@ -191,7 +193,7 @@ static ippfind_expr_t       *new_expr(ippfind_op_t op, int invert,
                                  const char *value, const char *regex,
                                  char **args);
 #ifdef HAVE_DNSSD
-static void            resolve_callback(DNSServiceRef sdRef,
+static void DNSSD_API  resolve_callback(DNSServiceRef sdRef,
                                         DNSServiceFlags flags,
                                         uint32_t interfaceIndex,
                                         DNSServiceErrorType errorCode,
@@ -208,11 +210,12 @@ static int                poll_callback(struct pollfd *pollfds,
 static void            resolve_callback(AvahiServiceResolver *res,
                                         AvahiIfIndex interface,
                                         AvahiProtocol protocol,
-                                        AvahiBrowserEvent event,
+                                        AvahiResolverEvent event,
                                         const char *serviceName,
                                         const char *regtype,
                                         const char *replyDomain,
                                         const char *host_name,
+                                        const AvahiAddress *address,
                                         uint16_t port,
                                         AvahiStringList *txt,
                                         AvahiLookupResultFlags flags,
@@ -233,7 +236,7 @@ main(int  argc,                             /* I - Number of command-line args */
 {
   int                  i,              /* Looping var */
                        have_output = 0,/* Have output expression */
-                       status = IPPFIND_EXIT_TRUE;
+                       status = IPPFIND_EXIT_FALSE;
                                        /* Exit status */
   const char           *opt,           /* Option character */
                        *search;        /* Current browse/resolve string */
@@ -244,7 +247,9 @@ main(int  argc,                             /* I - Number of command-line args */
                                        /* Expression tree */
                        *temp = NULL,   /* New expression */
                        *parent = NULL, /* Parent expression */
-                       *current = NULL;/* Current expression */
+                       *current = NULL,/* Current expression */
+                       *parens[100];   /* Markers for parenthesis */
+  int                  num_parens = 0; /* Number of parenthesis */
   ippfind_op_t         logic = IPPFIND_OP_AND;
                                        /* Logic for next expression */
   int                  invert = 0;     /* Invert expression? */
@@ -254,6 +259,30 @@ main(int  argc,                            /* I - Number of command-line args */
   struct timeval       stimeout;       /* Timeout for select() */
 #endif /* HAVE_DNSSD */
   double               endtime;        /* End time */
+  static const char * const ops[] =    /* Node operation names */
+  {
+    "NONE",
+    "AND",
+    "OR",
+    "TRUE",
+    "FALSE",
+    "IS_LOCAL",
+    "IS_REMOTE",
+    "DOMAIN_REGEX",
+    "NAME_REGEX",
+    "NAME_LITERAL",
+    "HOST_REGEX",
+    "PORT_RANGE",
+    "PATH_REGEX",
+    "TXT_EXISTS",
+    "TXT_REGEX",
+    "URI_REGEX",
+    "EXEC",
+    "LIST",
+    "PRINT_NAME",
+    "PRINT_URI",
+    "QUIET"
+  };
 
 
  /*
@@ -273,6 +302,10 @@ main(int  argc,                            /* I - Number of command-line args */
   * Parse command-line...
   */
 
+  if (getenv("IPPFIND_DEBUG"))
+    for (i = 1; i < argc; i ++)
+      fprintf(stderr, "argv[%d]=\"%s\"\n", i, argv[i]);
+
   for (i = 1; i < argc; i ++)
   {
     if (argv[i][0] == '-')
@@ -285,36 +318,31 @@ main(int  argc,                           /* I - Number of command-line args */
 
         if (!strcmp(argv[i], "--and"))
         {
-         if (logic != IPPFIND_OP_AND && current && current->prev)
-         {
-          /*
-           * OK, we have more than 1 rule in the current tree level.
-           * Make a new group tree and move the previous rule to it...
-           */
-
-           if ((temp = new_expr(IPPFIND_OP_AND, 0, NULL, NULL, NULL)) == NULL)
-             return (IPPFIND_EXIT_MEMORY);
-
-           temp->child         = current;
-           temp->parent        = current->parent;
-           current->prev->next = temp;
-           temp->prev          = current->prev;
+          if (logic == IPPFIND_OP_OR)
+          {
+            _cupsLangPuts(stderr, _("ippfind: Cannot use --and after --or."));
+            show_usage();
+          }
 
-           current->prev   = NULL;
-           current->parent = temp;
-           parent          = temp;
-         }
-         else if (parent)
-           parent->op = IPPFIND_OP_AND;
+          if (!current)
+          {
+            _cupsLangPuts(stderr,
+                          _("ippfind: Missing expression before \"--and\"."));
+            show_usage();
+          }
 
-         logic = IPPFIND_OP_AND;
-         temp  = NULL;
+         temp = NULL;
         }
         else if (!strcmp(argv[i], "--domain"))
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Missing regular expression after %s."),
+                            "--domain");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_DOMAIN_REGEX, invert, NULL, argv[i],
                                NULL)) == NULL)
@@ -324,7 +352,11 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr, _("ippfind: Expected program after %s."),
+                            "--exec");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_EXEC, invert, NULL, NULL,
                                argv + i)) == NULL)
@@ -337,16 +369,16 @@ main(int  argc,                           /* I - Number of command-line args */
               i ++;
 
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr, _("ippfind: Expected semi-colon after %s."),
+                            "--exec");
             show_usage();
+          }
 
           have_output = 1;
         }
         else if (!strcmp(argv[i], "--false"))
         {
-          i ++;
-          if (i >= argc)
-            show_usage();
-
           if ((temp = new_expr(IPPFIND_OP_FALSE, invert, NULL, NULL,
                                NULL)) == NULL)
             return (IPPFIND_EXIT_MEMORY);
@@ -359,7 +391,12 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Missing regular expression after %s."),
+                            "--host");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_HOST_REGEX, invert, NULL, argv[i],
                                NULL)) == NULL)
@@ -367,10 +404,6 @@ main(int  argc,                            /* I - Number of command-line args */
         }
         else if (!strcmp(argv[i], "--ls"))
         {
-          i ++;
-          if (i >= argc)
-            show_usage();
-
           if ((temp = new_expr(IPPFIND_OP_LIST, invert, NULL, NULL,
                                NULL)) == NULL)
             return (IPPFIND_EXIT_MEMORY);
@@ -378,20 +411,33 @@ main(int  argc,                           /* I - Number of command-line args */
           have_output = 1;
         }
         else if (!strcmp(argv[i], "--local"))
+        {
+          if ((temp = new_expr(IPPFIND_OP_IS_LOCAL, invert, NULL, NULL,
+                               NULL)) == NULL)
+            return (IPPFIND_EXIT_MEMORY);
+        }
+        else if (!strcmp(argv[i], "--literal-name"))
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr, _("ippfind: Missing name after %s."), "--literal-name");
             show_usage();
+          }
 
-          if ((temp = new_expr(IPPFIND_OP_IS_LOCAL, invert, NULL, NULL,
-                               NULL)) == NULL)
+          if ((temp = new_expr(IPPFIND_OP_NAME_LITERAL, invert, argv[i], NULL, NULL)) == NULL)
             return (IPPFIND_EXIT_MEMORY);
         }
         else if (!strcmp(argv[i], "--name"))
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Missing regular expression after %s."),
+                            "--name");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_NAME_REGEX, invert, NULL, argv[i],
                                NULL)) == NULL)
@@ -403,54 +449,101 @@ main(int  argc,                          /* I - Number of command-line args */
         }
         else if (!strcmp(argv[i], "--or"))
         {
-         if (logic != IPPFIND_OP_OR && current)
+          if (!current)
+          {
+            _cupsLangPuts(stderr,
+                          _("ippfind: Missing expression before \"--or\"."));
+            show_usage();
+          }
+
+          logic = IPPFIND_OP_OR;
+
+          if (parent && parent->op == IPPFIND_OP_OR)
+          {
+           /*
+            * Already setup to do "foo --or bar --or baz"...
+            */
+
+            temp = NULL;
+          }
+          else if (!current->prev && parent)
+          {
+           /*
+            * Change parent node into an OR node...
+            */
+
+            parent->op = IPPFIND_OP_OR;
+            temp       = NULL;
+          }
+          else if (!current->prev)
+          {
+           /*
+            * Need to group "current" in a new OR node...
+            */
+
+           if ((temp = new_expr(IPPFIND_OP_OR, 0, NULL, NULL,
+                                NULL)) == NULL)
+             return (IPPFIND_EXIT_MEMORY);
+
+            temp->parent    = parent;
+            temp->child     = current;
+            current->parent = temp;
+
+            if (parent)
+              parent->child = temp;
+            else
+              expressions = temp;
+
+           parent = temp;
+           temp   = NULL;
+         }
+         else
          {
           /*
-           * OK, we have two possibilities; either this is the top-level
-           * rule or we have a bunch of AND rules at this level.
+           * Need to group previous expressions in an AND node, and then
+           * put that in an OR node...
            */
 
-           if (!parent)
+           if ((temp = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
+                                NULL)) == NULL)
+             return (IPPFIND_EXIT_MEMORY);
+
+           while (current->prev)
            {
-            /*
-             * This is the top-level rule; we have to group *all* of the AND
-             * rules down a level, as AND has precedence over OR.
-             */
+             current->parent = temp;
+             current         = current->prev;
+           }
 
-             if ((temp = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
-                                  NULL)) == NULL)
-               return (IPPFIND_EXIT_MEMORY);
+           current->parent = temp;
+           temp->child     = current;
+           current         = temp;
 
-             while (current->prev)
-             {
-               current->parent = temp;
-               current         = current->prev;
-             }
+           if ((temp = new_expr(IPPFIND_OP_OR, 0, NULL, NULL,
+                                NULL)) == NULL)
+             return (IPPFIND_EXIT_MEMORY);
 
-             current->parent = temp;
-             temp->child     = current;
+            temp->parent    = parent;
+            current->parent = temp;
 
-             expressions = current = temp;
-           }
-           else
-           {
-            /*
-             * This isn't the top rule, so go up one level...
-             */
+            if (parent)
+              parent->child = temp;
+            else
+              expressions = temp;
 
-             current = parent;
-             parent  = current->parent;
-           }
+           parent = temp;
+           temp   = NULL;
          }
-
-         logic = IPPFIND_OP_OR;
-         temp  = NULL;
         }
         else if (!strcmp(argv[i], "--path"))
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Missing regular expression after %s."),
+                            "--path");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_PATH_REGEX, invert, NULL, argv[i],
                                NULL)) == NULL)
@@ -460,7 +553,12 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Expected port range after %s."),
+                            "--port");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_PORT_RANGE, invert, argv[i], NULL,
                                NULL)) == NULL)
@@ -506,19 +604,28 @@ main(int  argc,                           /* I - Number of command-line args */
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr, _("ippfind: Expected key name after %s."),
+                            "--txt");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_TXT_EXISTS, invert, argv[i], NULL,
                                NULL)) == NULL)
             return (IPPFIND_EXIT_MEMORY);
         }
-        else if (!strncmp(argv[i], "--txt-", 5))
+        else if (!strncmp(argv[i], "--txt-", 6))
         {
-          const char *key = argv[i] + 5;/* TXT key */
+          const char *key = argv[i] + 6;/* TXT key */
 
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Missing regular expression after %s."),
+                            argv[i - 1]);
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_TXT_REGEX, invert, key, argv[i],
                                NULL)) == NULL)
@@ -528,7 +635,12 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           i ++;
           if (i >= argc)
+          {
+            _cupsLangPrintf(stderr,
+                            _("ippfind: Missing regular expression after %s."),
+                            "--uri");
             show_usage();
+          }
 
           if ((temp = new_expr(IPPFIND_OP_URI_REGEX, invert, NULL, argv[i],
                                NULL)) == NULL)
@@ -551,20 +663,56 @@ main(int  argc,                           /* I - Number of command-line args */
           * Add new expression...
           */
 
+         if (logic == IPPFIND_OP_AND &&
+             current && current->prev &&
+             parent && parent->op != IPPFIND_OP_AND)
+          {
+           /*
+            * Need to re-group "current" in a new AND node...
+            */
+
+            ippfind_expr_t *tempand;   /* Temporary AND node */
+
+           if ((tempand = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
+                                   NULL)) == NULL)
+             return (IPPFIND_EXIT_MEMORY);
+
+           /*
+            * Replace "current" with new AND node at the end of this list...
+            */
+
+            current->prev->next = tempand;
+            tempand->prev       = current->prev;
+            tempand->parent     = parent;
+
+           /*
+            * Add "current to the new AND node...
+            */
+
+            tempand->child  = current;
+            current->parent = tempand;
+            current->prev   = NULL;
+           parent          = tempand;
+         }
+
+         /*
+          * Add the new node at current level...
+          */
+
+         temp->parent = parent;
+         temp->prev   = current;
+
          if (current)
-         {
-           temp->parent  = parent;
            current->next = temp;
-         }
          else if (parent)
            parent->child = temp;
          else
            expressions = temp;
 
-         temp->prev = current;
-         current    = temp;
-          invert     = 0;
-          temp       = NULL;
+         current = temp;
+          invert  = 0;
+          logic   = IPPFIND_OP_AND;
+          temp    = NULL;
         }
       }
       else
@@ -585,10 +733,27 @@ main(int  argc,                           /* I - Number of command-line args */
                 address_family = AF_INET6;
                 break;
 
+            case 'N' : /* Literal name */
+               i ++;
+               if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr, _("ippfind: Missing name after %s."), "-N");
+                 show_usage();
+               }
+
+               if ((temp = new_expr(IPPFIND_OP_NAME_LITERAL, invert, argv[i], NULL, NULL)) == NULL)
+                 return (IPPFIND_EXIT_MEMORY);
+               break;
+
             case 'P' :
                i ++;
                if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Expected port range after %s."),
+                                 "-P");
                  show_usage();
+               }
 
                if ((temp = new_expr(IPPFIND_OP_PORT_RANGE, invert, argv[i],
                                     NULL, NULL)) == NULL)
@@ -598,7 +763,12 @@ main(int  argc,                            /* I - Number of command-line args */
             case 'T' :
                 i ++;
                 if (i >= argc)
-                  show_usage();
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("%s: Missing timeout for \"-T\"."),
+                                 "ippfind");
+                 show_usage();
+               }
 
                 bonjour_timeout = atof(argv[i]);
                 break;
@@ -606,7 +776,12 @@ main(int  argc,                            /* I - Number of command-line args */
             case 'V' :
                 i ++;
                 if (i >= argc)
-                  show_usage();
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("%s: Missing version for \"-V\"."),
+                                 "ippfind");
+                 show_usage();
+               }
 
                 if (!strcmp(argv[i], "1.1"))
                   ipp_version = 11;
@@ -617,44 +792,37 @@ main(int  argc,                           /* I - Number of command-line args */
                 else if (!strcmp(argv[i], "2.2"))
                   ipp_version = 22;
                 else
+                {
+                  _cupsLangPrintf(stderr, _("%s: Bad version %s for \"-V\"."),
+                                  "ippfind", argv[i]);
                   show_usage();
+                }
                 break;
 
             case 'd' :
                i ++;
                if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing regular expression after "
+                                   "%s."), "-d");
                  show_usage();
+               }
 
                if ((temp = new_expr(IPPFIND_OP_DOMAIN_REGEX, invert, NULL,
                                     argv[i], NULL)) == NULL)
                  return (IPPFIND_EXIT_MEMORY);
                 break;
 
-            case 'e' :
-               i ++;
-               if (i >= argc)
-                 show_usage();
-
-               if ((temp = new_expr(IPPFIND_OP_EXEC, invert, NULL, NULL,
-                                    argv + i)) == NULL)
-                 return (IPPFIND_EXIT_MEMORY);
-
-               while (i < argc)
-                 if (!strcmp(argv[i], ";"))
-                   break;
-                 else
-                   i ++;
-
-               if (i >= argc)
-                 show_usage();
-
-               have_output = 1;
-                break;
-
             case 'h' :
                i ++;
                if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing regular expression after "
+                                   "%s."), "-h");
                  show_usage();
+               }
 
                if ((temp = new_expr(IPPFIND_OP_HOST_REGEX, invert, NULL,
                                     argv[i], NULL)) == NULL)
@@ -662,10 +830,6 @@ main(int  argc,                            /* I - Number of command-line args */
                 break;
 
             case 'l' :
-               i ++;
-               if (i >= argc)
-                 show_usage();
-
                if ((temp = new_expr(IPPFIND_OP_LIST, invert, NULL, NULL,
                                     NULL)) == NULL)
                  return (IPPFIND_EXIT_MEMORY);
@@ -676,7 +840,12 @@ main(int  argc,                            /* I - Number of command-line args */
             case 'n' :
                i ++;
                if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing regular expression after "
+                                   "%s."), "-n");
                  show_usage();
+               }
 
                if ((temp = new_expr(IPPFIND_OP_NAME_REGEX, invert, NULL,
                                     argv[i], NULL)) == NULL)
@@ -716,7 +885,12 @@ main(int  argc,                            /* I - Number of command-line args */
             case 't' :
                i ++;
                if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing key name after %s."),
+                                 "-t");
                  show_usage();
+               }
 
                if ((temp = new_expr(IPPFIND_OP_TXT_EXISTS, invert, argv[i],
                                     NULL, NULL)) == NULL)
@@ -726,18 +900,53 @@ main(int  argc,                           /* I - Number of command-line args */
             case 'u' :
                i ++;
                if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing regular expression after "
+                                   "%s."), "-u");
                  show_usage();
+               }
 
                if ((temp = new_expr(IPPFIND_OP_URI_REGEX, invert, NULL,
                                     argv[i], NULL)) == NULL)
                  return (IPPFIND_EXIT_MEMORY);
                 break;
 
+            case 'x' :
+               i ++;
+               if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing program after %s."),
+                                 "-x");
+                 show_usage();
+               }
+
+               if ((temp = new_expr(IPPFIND_OP_EXEC, invert, NULL, NULL,
+                                    argv + i)) == NULL)
+                 return (IPPFIND_EXIT_MEMORY);
+
+               while (i < argc)
+                 if (!strcmp(argv[i], ";"))
+                   break;
+                 else
+                   i ++;
+
+               if (i >= argc)
+               {
+                 _cupsLangPrintf(stderr,
+                                 _("ippfind: Missing semi-colon after %s."),
+                                 "-x");
+                 show_usage();
+               }
+
+               have_output = 1;
+                break;
+
             default :
                 _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."),
                                 "ippfind", *opt);
                 show_usage();
-                break;
           }
 
          if (temp)
@@ -746,58 +955,99 @@ main(int  argc,                           /* I - Number of command-line args */
            * Add new expression...
            */
 
-           if (current)
+           if (logic == IPPFIND_OP_AND &&
+               current && current->prev &&
+               parent && parent->op != IPPFIND_OP_AND)
            {
-             temp->parent  = parent;
-             current->next = temp;
+            /*
+             * Need to re-group "current" in a new AND node...
+             */
+
+             ippfind_expr_t *tempand;  /* Temporary AND node */
+
+             if ((tempand = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
+                                     NULL)) == NULL)
+               return (IPPFIND_EXIT_MEMORY);
+
+            /*
+             * Replace "current" with new AND node at the end of this list...
+             */
+
+             current->prev->next = tempand;
+             tempand->prev       = current->prev;
+             tempand->parent     = parent;
+
+            /*
+             * Add "current to the new AND node...
+             */
+
+             tempand->child  = current;
+             current->parent = tempand;
+             current->prev   = NULL;
+             parent          = tempand;
            }
+
+          /*
+           * Add the new node at current level...
+           */
+
+           temp->parent = parent;
+           temp->prev   = current;
+
+           if (current)
+             current->next = temp;
            else if (parent)
              parent->child = temp;
            else
              expressions = temp;
 
-           temp->prev = current;
-           current    = temp;
-           invert     = 0;
-           temp       = NULL;
+           current = temp;
+           invert  = 0;
+           logic   = IPPFIND_OP_AND;
+           temp    = NULL;
          }
         }
       }
     }
     else if (!strcmp(argv[i], "("))
     {
+      if (num_parens >= 100)
+      {
+        _cupsLangPuts(stderr, _("ippfind: Too many parenthesis."));
+        show_usage();
+      }
+
       if ((temp = new_expr(IPPFIND_OP_AND, invert, NULL, NULL, NULL)) == NULL)
        return (IPPFIND_EXIT_MEMORY);
 
+      parens[num_parens++] = temp;
+
       if (current)
       {
        temp->parent  = current->parent;
        current->next = temp;
+       temp->prev    = current;
       }
       else
        expressions = temp;
 
-      temp->prev = current;
-      parent     = temp;
-      current    = NULL;
-      invert     = 0;
-      logic      = IPPFIND_OP_AND;
+      parent  = temp;
+      current = NULL;
+      invert  = 0;
+      logic   = IPPFIND_OP_AND;
     }
     else if (!strcmp(argv[i], ")"))
     {
-      if (!parent)
+      if (num_parens <= 0)
       {
         _cupsLangPuts(stderr, _("ippfind: Missing open parenthesis."));
         show_usage();
       }
 
-      current = parent;
+      current = parens[--num_parens];
       parent  = current->parent;
-
-      if (!parent)
-        logic = IPPFIND_OP_AND;
-      else
-        logic = parent->op;
+      invert  = 0;
+      logic   = IPPFIND_OP_AND;
     }
     else if (!strcmp(argv[i], "!"))
     {
@@ -817,21 +1067,12 @@ main(int  argc,                          /* I - Number of command-line args */
     }
   }
 
-  if (parent)
+  if (num_parens > 0)
   {
     _cupsLangPuts(stderr, _("ippfind: Missing close parenthesis."));
     show_usage();
   }
 
-  if (cupsArrayCount(searches) == 0)
-  {
-   /*
-    * Add an implicit browse for IPP printers ("_ipp._tcp")...
-    */
-
-    cupsArrayAdd(searches, "_ipp._tcp");
-  }
-
   if (!have_output)
   {
    /*
@@ -843,13 +1084,72 @@ main(int  argc,                          /* I - Number of command-line args */
 
     if (current)
     {
-      temp->parent  = parent;
+      while (current->parent)
+       current = current->parent;
+
       current->next = temp;
+      temp->prev    = current;
     }
     else
       expressions = temp;
+  }
+
+  if (cupsArrayCount(searches) == 0)
+  {
+   /*
+    * Add an implicit browse for IPP printers ("_ipp._tcp")...
+    */
+
+    cupsArrayAdd(searches, "_ipp._tcp");
+  }
+
+  if (getenv("IPPFIND_DEBUG"))
+  {
+    int                indent = 4;             /* Indentation */
+
+    puts("Expression tree:");
+    current = expressions;
+    while (current)
+    {
+     /*
+      * Print the current node...
+      */
+
+      printf("%*s%s%s\n", indent, "", current->invert ? "!" : "",
+             ops[current->op]);
+
+     /*
+      * Advance to the next node...
+      */
+
+      if (current->child)
+      {
+        current = current->child;
+        indent += 4;
+      }
+      else if (current->next)
+        current = current->next;
+      else if (current->parent)
+      {
+        while (current->parent)
+        {
+         indent -= 4;
+          current = current->parent;
+          if (current->next)
+            break;
+        }
+
+        current = current->next;
+      }
+      else
+        current = NULL;
+    }
 
-    temp->prev = current;
+    puts("\nSearch items:");
+    for (search = (const char *)cupsArrayFirst(searches);
+        search;
+        search = (const char *)cupsArrayNext(searches))
+      printf("    %s\n", search);
   }
 
  /*
@@ -867,8 +1167,8 @@ main(int  argc,                            /* I - Number of command-line args */
 #elif defined(HAVE_AVAHI)
   if ((avahi_poll = avahi_simple_poll_new()) == NULL)
   {
-    _cupsLangPrintError(stderr, _("ippfind: Unable to use Bonjour: %s"),
-                        strerror(errno));
+    _cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
+                    strerror(errno));
     return (IPPFIND_EXIT_BONJOUR);
   }
 
@@ -876,10 +1176,10 @@ main(int  argc,                          /* I - Number of command-line args */
 
   avahi_client = avahi_client_new(avahi_simple_poll_get(avahi_poll),
                                  0, client_callback, avahi_poll, &err);
-  if (!client)
+  if (!avahi_client)
   {
-    _cupsLangPrintError(stderr, _("ippfind: Unable to use Bonjour: %s"),
-                        dnssd_error_string(err));
+    _cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
+                    dnssd_error_string(err));
     return (IPPFIND_EXIT_BONJOUR);
   }
 #endif /* HAVE_DNSSD */
@@ -894,27 +1194,46 @@ main(int  argc,                          /* I - Number of command-line args */
                        *domain;        /* Domain, if any */
 
     strlcpy(buf, search, sizeof(buf));
-    if (buf[0] == '_')
-    {
-      regtype = buf;
-    }
-    else if ((regtype = strstr(buf, "._")) != NULL)
+    if ((regtype = strstr(buf, "._")) != NULL)
     {
-      name = buf;
-      *regtype++ = '\0';
+      if (strcmp(regtype, "._tcp"))
+      {
+       /*
+        * "something._protocol._tcp" -> search for something with the given
+        * protocol...
+        */
+
+       name = buf;
+       *regtype++ = '\0';
+      }
+      else
+      {
+       /*
+        * "_protocol._tcp" -> search for everything with the given protocol...
+        */
+
+        /* name = NULL; */
+        regtype = buf;
+      }
     }
     else
     {
+     /*
+      * "something" -> search for something with IPP protocol...
+      */
+
       name    = buf;
       regtype = "_ipp._tcp";
     }
 
     for (domain = regtype; *domain; domain ++)
+    {
       if (*domain == '.' && domain[1] != '_')
       {
-        *domain++ = '\0';
-        break;
+       *domain++ = '\0';
+       break;
       }
+    }
 
     if (!*domain)
       domain = NULL;
@@ -946,7 +1265,7 @@ main(int  argc,                            /* I - Number of command-line args */
       if (service->ref)
         err = 0;
       else
-        err = avahi_client_get_errno(avahi_client);
+        err = avahi_client_errno(avahi_client);
 #endif /* HAVE_DNSSD */
     }
     else
@@ -976,7 +1295,7 @@ main(int  argc,                            /* I - Number of command-line args */
                                     browse_callback, services))
         err = 0;
       else
-        err = avahi_client_get_errno(avahi_client);
+        err = avahi_client_errno(avahi_client);
 #endif /* HAVE_DNSSD */
     }
 
@@ -985,14 +1304,6 @@ main(int  argc,                           /* I - Number of command-line args */
       _cupsLangPrintf(stderr, _("ippfind: Unable to browse or resolve: %s"),
                       dnssd_error_string(err));
 
-      if (name)
-        printf("name=\"%s\"\n", name);
-
-      printf("regtype=\"%s\"\n", regtype);
-
-      if (domain)
-        printf("domain=\"%s\"\n", domain);
-
       return (IPPFIND_EXIT_BONJOUR);
     }
   }
@@ -1112,7 +1423,7 @@ main(int  argc,                           /* I - Number of command-line args */
            if (service->ref)
              err = 0;
            else
-             err = avahi_client_get_errno(avahi_client);
+             err = avahi_client_errno(avahi_client);
 #endif /* HAVE_DNSSD */
 
            if (err)
@@ -1137,14 +1448,14 @@ main(int  argc,                         /* I - Number of command-line args */
 #ifdef HAVE_DNSSD
            DNSServiceRefDeallocate(service->ref);
 #else
-            avahi_record_browser_free(service->ref);
+            avahi_service_resolver_free(service->ref);
 #endif /* HAVE_DNSSD */
 
            service->ref = NULL;
          }
 
-          if (!eval_expr(service, expressions))
-            status = IPPFIND_EXIT_FALSE;
+          if (eval_expr(service, expressions))
+            status = IPPFIND_EXIT_TRUE;
 
           service->is_processed = 1;
         }
@@ -1173,7 +1484,7 @@ main(int  argc,                           /* I - Number of command-line args */
  * 'browse_callback()' - Browse devices.
  */
 
-static void
+static void DNSSD_API
 browse_callback(
     DNSServiceRef       sdRef,         /* I - Service reference */
     DNSServiceFlags     flags,         /* I - Option flags */
@@ -1188,6 +1499,9 @@ browse_callback(
   * Only process "add" data...
   */
 
+  (void)sdRef;
+  (void)interfaceIndex;
+
   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
     return;
 
@@ -1203,7 +1517,7 @@ browse_callback(
  * 'browse_local_callback()' - Browse local devices.
  */
 
-static void
+static void DNSSD_API
 browse_local_callback(
     DNSServiceRef       sdRef,         /* I - Service reference */
     DNSServiceFlags     flags,         /* I - Option flags */
@@ -1221,6 +1535,9 @@ browse_local_callback(
   * Only process "add" data...
   */
 
+  (void)sdRef;
+  (void)interfaceIndex;
+
   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
     return;
 
@@ -1267,7 +1584,7 @@ browse_callback(
        fprintf(stderr, "DEBUG: browse_callback: %s\n",
                avahi_strerror(avahi_client_errno(client)));
        bonjour_error = 1;
-       avahi_simple_poll_quit(simple_poll);
+       avahi_simple_poll_quit(avahi_poll);
        break;
 
     case AVAHI_BROWSER_NEW:
@@ -1433,8 +1750,10 @@ dnssd_error_string(int error)            /* I - Error number */
     case kDNSServiceErr_PollingMode :
         return ("Service polling mode error.");
 
+#ifndef WIN32
     case kDNSServiceErr_Timeout :
         return ("Service timeout.");
+#endif /* !WIN32 */
   }
 
 #  elif defined(HAVE_AVAHI)
@@ -1497,6 +1816,9 @@ eval_expr(ippfind_srv_t  *service,        /* I - Service */
       case IPPFIND_OP_NAME_REGEX :
           result = !regexec(&(expression->re), service->name, 0, NULL, 0);
           break;
+      case IPPFIND_OP_NAME_LITERAL :
+          result = !_cups_strcasecmp(expression->name, service->name);
+          break;
       case IPPFIND_OP_HOST_REGEX :
           result = !regexec(&(expression->re), service->host, 0, NULL, 0);
           break;
@@ -1508,23 +1830,26 @@ eval_expr(ippfind_srv_t  *service,      /* I - Service */
           result = !regexec(&(expression->re), service->resource, 0, NULL, 0);
           break;
       case IPPFIND_OP_TXT_EXISTS :
-          result = cupsGetOption(expression->key, service->num_txt,
+          result = cupsGetOption(expression->name, service->num_txt,
                                 service->txt) != NULL;
           break;
       case IPPFIND_OP_TXT_REGEX :
-          val = cupsGetOption(expression->key, service->num_txt,
+          val = cupsGetOption(expression->name, service->num_txt,
                              service->txt);
          if (val)
            result = !regexec(&(expression->re), val, 0, NULL, 0);
          else
            result = 0;
+
+         if (getenv("IPPFIND_DEBUG"))
+           printf("TXT_REGEX of \"%s\": %d\n", val, result);
           break;
       case IPPFIND_OP_URI_REGEX :
           result = !regexec(&(expression->re), service->uri, 0, NULL, 0);
           break;
       case IPPFIND_OP_EXEC :
           result = exec_program(service, expression->num_args,
-                                expression->args);
+                               expression->args);
           break;
       case IPPFIND_OP_LIST :
           result = list_service(service);
@@ -1565,11 +1890,250 @@ exec_program(ippfind_srv_t *service,   /* I - Service */
              int           num_args,   /* I - Number of command-line args */
              char          **args)     /* I - Command-line arguments */
 {
-  (void)service;
-  (void)num_args;
-  (void)args;
+  char         **myargv,               /* Command-line arguments */
+               **myenvp,               /* Environment variables */
+               *ptr,                   /* Pointer into variable */
+               domain[1024],           /* IPPFIND_SERVICE_DOMAIN */
+               hostname[1024],         /* IPPFIND_SERVICE_HOSTNAME */
+               name[256],              /* IPPFIND_SERVICE_NAME */
+               port[32],               /* IPPFIND_SERVICE_PORT */
+               regtype[256],           /* IPPFIND_SERVICE_REGTYPE */
+               scheme[128],            /* IPPFIND_SERVICE_SCHEME */
+               uri[1024],              /* IPPFIND_SERVICE_URI */
+               txt[100][256];          /* IPPFIND_TXT_foo */
+  int          i,                      /* Looping var */
+               myenvc,                 /* Number of environment variables */
+               status;                 /* Exit status of program */
+#ifndef WIN32
+  char         program[1024];          /* Program to execute */
+  int          pid;                    /* Process ID */
+#endif /* !WIN32 */
 
-  return (1);
+
+ /*
+  * Environment variables...
+  */
+
+  snprintf(domain, sizeof(domain), "IPPFIND_SERVICE_DOMAIN=%s",
+           service->domain);
+  snprintf(hostname, sizeof(hostname), "IPPFIND_SERVICE_HOSTNAME=%s",
+           service->host);
+  snprintf(name, sizeof(name), "IPPFIND_SERVICE_NAME=%s", service->name);
+  snprintf(port, sizeof(port), "IPPFIND_SERVICE_PORT=%d", service->port);
+  snprintf(regtype, sizeof(regtype), "IPPFIND_SERVICE_REGTYPE=%s",
+           service->regtype);
+  snprintf(scheme, sizeof(scheme), "IPPFIND_SERVICE_SCHEME=%s",
+           !strncmp(service->regtype, "_http._tcp", 10) ? "http" :
+               !strncmp(service->regtype, "_https._tcp", 11) ? "https" :
+               !strncmp(service->regtype, "_ipp._tcp", 9) ? "ipp" :
+               !strncmp(service->regtype, "_ipps._tcp", 10) ? "ipps" : "lpd");
+  snprintf(uri, sizeof(uri), "IPPFIND_SERVICE_URI=%s", service->uri);
+  for (i = 0; i < service->num_txt && i < 100; i ++)
+  {
+    snprintf(txt[i], sizeof(txt[i]), "IPPFIND_TXT_%s=%s", service->txt[i].name,
+             service->txt[i].value);
+    for (ptr = txt[i] + 12; *ptr && *ptr != '='; ptr ++)
+      *ptr = (char)_cups_toupper(*ptr);
+  }
+
+  for (i = 0, myenvc = 7 + service->num_txt; environ[i]; i ++)
+    if (strncmp(environ[i], "IPPFIND_", 8))
+      myenvc ++;
+
+  if ((myenvp = calloc(sizeof(char *), (size_t)(myenvc + 1))) == NULL)
+  {
+    _cupsLangPuts(stderr, _("ippfind: Out of memory."));
+    exit(IPPFIND_EXIT_MEMORY);
+  }
+
+  for (i = 0, myenvc = 0; environ[i]; i ++)
+    if (strncmp(environ[i], "IPPFIND_", 8))
+      myenvp[myenvc++] = environ[i];
+
+  myenvp[myenvc++] = domain;
+  myenvp[myenvc++] = hostname;
+  myenvp[myenvc++] = name;
+  myenvp[myenvc++] = port;
+  myenvp[myenvc++] = regtype;
+  myenvp[myenvc++] = scheme;
+  myenvp[myenvc++] = uri;
+
+  for (i = 0; i < service->num_txt && i < 100; i ++)
+    myenvp[myenvc++] = txt[i];
+
+ /*
+  * Allocate and copy command-line arguments...
+  */
+
+  if ((myargv = calloc(sizeof(char *), (size_t)(num_args + 1))) == NULL)
+  {
+    _cupsLangPuts(stderr, _("ippfind: Out of memory."));
+    exit(IPPFIND_EXIT_MEMORY);
+  }
+
+  for (i = 0; i < num_args; i ++)
+  {
+    if (strchr(args[i], '{'))
+    {
+      char     temp[2048],             /* Temporary string */
+               *tptr,                  /* Pointer into temporary string */
+               keyword[256],           /* {keyword} */
+               *kptr;                  /* Pointer into keyword */
+
+      for (ptr = args[i], tptr = temp; *ptr; ptr ++)
+      {
+        if (*ptr == '{')
+        {
+         /*
+          * Do a {var} substitution...
+          */
+
+          for (kptr = keyword, ptr ++; *ptr && *ptr != '}'; ptr ++)
+            if (kptr < (keyword + sizeof(keyword) - 1))
+              *kptr++ = *ptr;
+
+          if (*ptr != '}')
+          {
+            _cupsLangPuts(stderr,
+                          _("ippfind: Missing close brace in substitution."));
+            exit(IPPFIND_EXIT_SYNTAX);
+          }
+
+          *kptr = '\0';
+          if (!keyword[0] || !strcmp(keyword, "service_uri"))
+           strlcpy(tptr, service->uri, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strcmp(keyword, "service_domain"))
+           strlcpy(tptr, service->domain, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strcmp(keyword, "service_hostname"))
+           strlcpy(tptr, service->host, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strcmp(keyword, "service_name"))
+           strlcpy(tptr, service->name, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strcmp(keyword, "service_path"))
+           strlcpy(tptr, service->resource, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strcmp(keyword, "service_port"))
+           strlcpy(tptr, port + 21, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strcmp(keyword, "service_scheme"))
+           strlcpy(tptr, scheme + 22, sizeof(temp) - (size_t)(tptr - temp));
+         else if (!strncmp(keyword, "txt_", 4))
+         {
+           const char *val = cupsGetOption(keyword + 4, service->num_txt, service->txt);
+           if (val)
+             strlcpy(tptr, val, sizeof(temp) - (size_t)(tptr - temp));
+           else
+             *tptr = '\0';
+         }
+         else
+         {
+           _cupsLangPrintf(stderr, _("ippfind: Unknown variable \"{%s}\"."),
+                           keyword);
+           exit(IPPFIND_EXIT_SYNTAX);
+         }
+
+         tptr += strlen(tptr);
+       }
+       else if (tptr < (temp + sizeof(temp) - 1))
+         *tptr++ = *ptr;
+      }
+
+      *tptr = '\0';
+      myargv[i] = strdup(temp);
+    }
+    else
+      myargv[i] = strdup(args[i]);
+  }
+
+#ifdef WIN32
+  if (getenv("IPPFIND_DEBUG"))
+  {
+    printf("\nProgram:\n    %s\n", args[0]);
+    puts("\nArguments:");
+    for (i = 0; i < num_args; i ++)
+      printf("    %s\n", myargv[i]);
+    puts("\nEnvironment:");
+    for (i = 0; i < myenvc; i ++)
+      printf("    %s\n", myenvp[i]);
+  }
+
+  status = _spawnvpe(_P_WAIT, args[0], myargv, myenvp);
+
+#else
+ /*
+  * Execute the program...
+  */
+
+  if (strchr(args[0], '/') && !access(args[0], X_OK))
+    strlcpy(program, args[0], sizeof(program));
+  else if (!cupsFileFind(args[0], getenv("PATH"), 1, program, sizeof(program)))
+  {
+    _cupsLangPrintf(stderr, _("ippfind: Unable to execute \"%s\": %s"),
+                    args[0], strerror(ENOENT));
+    exit(IPPFIND_EXIT_SYNTAX);
+  }
+
+  if (getenv("IPPFIND_DEBUG"))
+  {
+    printf("\nProgram:\n    %s\n", program);
+    puts("\nArguments:");
+    for (i = 0; i < num_args; i ++)
+      printf("    %s\n", myargv[i]);
+    puts("\nEnvironment:");
+    for (i = 0; i < myenvc; i ++)
+      printf("    %s\n", myenvp[i]);
+  }
+
+  if ((pid = fork()) == 0)
+  {
+   /*
+    * Child comes here...
+    */
+
+    execve(program, myargv, myenvp);
+    exit(1);
+  }
+  else if (pid < 0)
+  {
+    _cupsLangPrintf(stderr, _("ippfind: Unable to execute \"%s\": %s"),
+                    args[0], strerror(errno));
+    exit(IPPFIND_EXIT_SYNTAX);
+  }
+  else
+  {
+   /*
+    * Wait for it to complete...
+    */
+
+    while (wait(&status) != pid)
+      ;
+  }
+#endif /* WIN32 */
+
+ /*
+  * Free memory...
+  */
+
+  for (i = 0; i < num_args; i ++)
+    free(myargv[i]);
+
+  free(myargv);
+  free(myenvp);
+
+ /*
+  * Return whether the program succeeded or crashed...
+  */
+
+  if (getenv("IPPFIND_DEBUG"))
+  {
+#ifdef WIN32
+    printf("Exit Status: %d\n", status);
+#else
+    if (WIFEXITED(status))
+      printf("Exit Status: %d\n", WEXITSTATUS(status));
+    else
+      printf("Terminating Signal: %d\n", WTERMSIG(status));
+#endif /* WIN32 */
+  }
+
+  return (status == 0);
 }
 
 
@@ -1665,7 +2229,214 @@ get_time(void)
 static int                             /* O - 1 if successful, 0 otherwise */
 list_service(ippfind_srv_t *service)   /* I - Service */
 {
-  (void)service;
+  http_addrlist_t      *addrlist;      /* Address(es) of service */
+  char                 port[10];       /* Port number of service */
+
+
+  snprintf(port, sizeof(port), "%d", service->port);
+
+  if ((addrlist = httpAddrGetList(service->host, address_family, port)) == NULL)
+  {
+    _cupsLangPrintf(stdout, "%s unreachable", service->uri);
+    return (0);
+  }
+
+  if (!strncmp(service->regtype, "_ipp._tcp", 9) ||
+      !strncmp(service->regtype, "_ipps._tcp", 10))
+  {
+   /*
+    * IPP/IPPS printer
+    */
+
+    http_t             *http;          /* HTTP connection */
+    ipp_t              *request,       /* IPP request */
+                       *response;      /* IPP response */
+    ipp_attribute_t    *attr;          /* IPP attribute */
+    int                        i,              /* Looping var */
+                       count,          /* Number of values */
+                       version,        /* IPP version */
+                       paccepting;     /* printer-is-accepting-jobs value */
+    ipp_pstate_t       pstate;         /* printer-state value */
+    char               preasons[1024], /* Comma-delimited printer-state-reasons */
+                       *ptr,           /* Pointer into reasons */
+                       *end;           /* End of reasons buffer */
+    static const char * const rattrs[] =/* Requested attributes */
+    {
+      "printer-is-accepting-jobs",
+      "printer-state",
+      "printer-state-reasons"
+    };
+
+   /*
+    * Connect to the printer...
+    */
+
+    http = httpConnect2(service->host, service->port, addrlist, address_family,
+                       !strncmp(service->regtype, "_ipps._tcp", 10) ?
+                           HTTP_ENCRYPTION_ALWAYS :
+                           HTTP_ENCRYPTION_IF_REQUESTED,
+                       1, 30000, NULL);
+
+    httpAddrFreeList(addrlist);
+
+    if (!http)
+    {
+      _cupsLangPrintf(stdout, "%s unavailable", service->uri);
+      return (0);
+    }
+
+   /*
+    * Get the current printer state...
+    */
+
+    response = NULL;
+    version  = ipp_version;
+
+    do
+    {
+      request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+      ippSetVersion(request, version / 10, version % 10);
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+                   service->uri);
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                   "requesting-user-name", NULL, cupsUser());
+      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                    "requested-attributes",
+                    (int)(sizeof(rattrs) / sizeof(rattrs[0])), NULL, rattrs);
+
+      response = cupsDoRequest(http, request, service->resource);
+
+      if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST && version > 11)
+        version = 11;
+    }
+    while (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE && version > 11);
+
+   /*
+    * Show results...
+    */
+
+    if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
+    {
+      _cupsLangPrintf(stdout, "%s: unavailable", service->uri);
+      return (0);
+    }
+
+    if ((attr = ippFindAttribute(response, "printer-state",
+                                 IPP_TAG_ENUM)) != NULL)
+      pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
+    else
+      pstate = IPP_PSTATE_STOPPED;
+
+    if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
+                                 IPP_TAG_BOOLEAN)) != NULL)
+      paccepting = ippGetBoolean(attr, 0);
+    else
+      paccepting = 0;
+
+    if ((attr = ippFindAttribute(response, "printer-state-reasons",
+                                 IPP_TAG_KEYWORD)) != NULL)
+    {
+      strlcpy(preasons, ippGetString(attr, 0, NULL), sizeof(preasons));
+
+      for (i = 1, count = ippGetCount(attr), ptr = preasons + strlen(preasons),
+               end = preasons + sizeof(preasons) - 1;
+           i < count && ptr < end;
+           i ++, ptr += strlen(ptr))
+      {
+        *ptr++ = ',';
+        strlcpy(ptr, ippGetString(attr, i, NULL), (size_t)(end - ptr + 1));
+      }
+    }
+    else
+      strlcpy(preasons, "none", sizeof(preasons));
+
+    ippDelete(response);
+    httpClose(http);
+
+    _cupsLangPrintf(stdout, "%s %s %s %s", service->uri,
+                    ippEnumString("printer-state", pstate),
+                    paccepting ? "accepting-jobs" : "not-accepting-jobs",
+                    preasons);
+  }
+  else if (!strncmp(service->regtype, "_http._tcp", 10) ||
+           !strncmp(service->regtype, "_https._tcp", 11))
+  {
+   /*
+    * HTTP/HTTPS web page
+    */
+
+    http_t             *http;          /* HTTP connection */
+    http_status_t      status;         /* HEAD status */
+
+
+   /*
+    * Connect to the web server...
+    */
+
+    http = httpConnect2(service->host, service->port, addrlist, address_family,
+                       !strncmp(service->regtype, "_ipps._tcp", 10) ?
+                           HTTP_ENCRYPTION_ALWAYS :
+                           HTTP_ENCRYPTION_IF_REQUESTED,
+                       1, 30000, NULL);
+
+    httpAddrFreeList(addrlist);
+
+    if (!http)
+    {
+      _cupsLangPrintf(stdout, "%s unavailable", service->uri);
+      return (0);
+    }
+
+    if (httpGet(http, service->resource))
+    {
+      _cupsLangPrintf(stdout, "%s unavailable", service->uri);
+      return (0);
+    }
+
+    do
+    {
+      status = httpUpdate(http);
+    }
+    while (status == HTTP_STATUS_CONTINUE);
+
+    httpFlush(http);
+    httpClose(http);
+
+    if (status >= HTTP_STATUS_BAD_REQUEST)
+    {
+      _cupsLangPrintf(stdout, "%s unavailable", service->uri);
+      return (0);
+    }
+
+    _cupsLangPrintf(stdout, "%s available", service->uri);
+  }
+  else if (!strncmp(service->regtype, "_printer._tcp", 13))
+  {
+   /*
+    * LPD printer
+    */
+
+    int        sock;                           /* Socket */
+
+
+    if (!httpAddrConnect(addrlist, &sock))
+    {
+      _cupsLangPrintf(stdout, "%s unavailable", service->uri);
+      httpAddrFreeList(addrlist);
+      return (0);
+    }
+
+    _cupsLangPrintf(stdout, "%s available", service->uri);
+    httpAddrFreeList(addrlist);
+
+    httpAddrClose(NULL, sock);
+  }
+  else
+  {
+    _cupsLangPrintf(stdout, "%s unsupported", service->uri);
+    httpAddrFreeList(addrlist);
+    return (0);
+  }
 
   return (1);
 }
@@ -1691,8 +2462,8 @@ new_expr(ippfind_op_t op,         /* I - Operation */
   temp->op = op;
   temp->invert = invert;
 
-  if (op == IPPFIND_OP_TXT_EXISTS || op == IPPFIND_OP_TXT_REGEX)
-    temp->key = (char *)value;
+  if (op == IPPFIND_OP_TXT_EXISTS || op == IPPFIND_OP_TXT_REGEX || op == IPPFIND_OP_NAME_LITERAL)
+    temp->name = (char *)value;
   else if (op == IPPFIND_OP_PORT_RANGE)
   {
    /*
@@ -1738,8 +2509,9 @@ new_expr(ippfind_op_t op,         /* I - Operation */
       if (!strcmp(args[num_args], ";"))
         break;
 
-     temp->args = malloc(num_args * sizeof(char *));
-     memcpy(temp->args, args, num_args * sizeof(char *));
+     temp->num_args = num_args;
+     temp->args     = malloc((size_t)num_args * sizeof(char *));
+     memcpy(temp->args, args, (size_t)num_args * sizeof(char *));
   }
 
   return (temp);
@@ -1770,10 +2542,8 @@ poll_callback(
 
   val = poll(pollfds, num_pollfds, 500);
 
-  if (val < 0)
-    fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
-  else if (val > 0)
-    got_data = 1;
+  if (val > 0)
+    avahi_got_data = 1;
 
   return (val);
 }
@@ -1785,7 +2555,7 @@ poll_callback(
  */
 
 #ifdef HAVE_DNSSD
-static void
+static void DNSSD_API
 resolve_callback(
     DNSServiceRef       sdRef,         /* I - Service reference */
     DNSServiceFlags     flags,         /* I - Data flags */
@@ -1810,7 +2580,12 @@ resolve_callback(
   * Only process "add" data...
   */
 
-  if (errorCode != kDNSServiceErr_NoError)
+  (void)sdRef;
+  (void)flags;
+  (void)interfaceIndex;
+  (void)fullName;
+
+   if (errorCode != kDNSServiceErr_NoError)
   {
     _cupsLangPrintf(stderr, _("ippfind: Unable to browse or resolve: %s"),
                    dnssd_error_string(errorCode));
@@ -1822,6 +2597,10 @@ resolve_callback(
   service->host        = strdup(hostTarget);
   service->port        = ntohs(port);
 
+  value = service->host + strlen(service->host) - 1;
+  if (value >= service->host && *value == '.')
+    *value = '\0';
+
  /*
   * Loop through the TXT key/value pairs and add them to an array...
   */
@@ -1860,36 +2639,42 @@ resolve_callback(
     AvahiServiceResolver   *resolver,  /* I - Resolver */
     AvahiIfIndex           interface,  /* I - Interface */
     AvahiProtocol          protocol,   /* I - Address protocol */
-    AvahiBrowserEvent      event,      /* I - Event */
+    AvahiResolverEvent     event,      /* I - Event */
     const char             *serviceName,/* I - Service name */
     const char             *regtype,   /* I - Registration type */
     const char             *replyDomain,/* I - Domain name */
     const char             *hostTarget,        /* I - FQDN */
+    const AvahiAddress     *address,   /* I - Address */
     uint16_t               port,       /* I - Port number */
     AvahiStringList        *txt,       /* I - TXT records */
     AvahiLookupResultFlags flags,      /* I - Lookup flags */
     void                   *context)   /* I - Service */
 {
-  char         uri[1024];              /* URI */
-               key[256],               /* TXT key */
+  char         key[256],               /* TXT key */
                *value;                 /* TXT value */
   ippfind_srv_t        *service = (ippfind_srv_t *)context;
                                        /* Service */
   AvahiStringList *current;            /* Current TXT key/value pair */
 
 
+  (void)address;
+
   if (event != AVAHI_RESOLVER_FOUND)
   {
     bonjour_error = 1;
 
     avahi_service_resolver_free(resolver);
-    avahi_simple_poll_quit(uribuf->poll);
+    avahi_simple_poll_quit(avahi_poll);
     return;
   }
 
   service->is_resolved = 1;
   service->host        = strdup(hostTarget);
-  service->port        = ntohs(port);
+  service->port        = port;
+
+  value = service->host + strlen(service->host) - 1;
+  if (value >= service->host && *value == '.')
+    *value = '\0';
 
  /*
   * Loop through the TXT key/value pairs and add them to an array...
@@ -2011,8 +2796,6 @@ show_usage(void)
   _cupsLangPuts(stderr, _("Expressions:"));
   _cupsLangPuts(stderr, _("  -P number[-number]      Match port to number or range."));
   _cupsLangPuts(stderr, _("  -d regex                Match domain to regular expression."));
-  _cupsLangPuts(stderr, _("  -e utility [argument ...] ;\n"
-                          "                          Execute program if true."));
   _cupsLangPuts(stderr, _("  -h regex                Match hostname to regular expression."));
   _cupsLangPuts(stderr, _("  -l                      List attributes."));
   _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
@@ -2022,6 +2805,8 @@ show_usage(void)
   _cupsLangPuts(stderr, _("  -s                      Print service name if true."));
   _cupsLangPuts(stderr, _("  -t key                  True if the TXT record contains the key."));
   _cupsLangPuts(stderr, _("  -u regex                Match URI to regular expression."));
+  _cupsLangPuts(stderr, _("  -x utility [argument ...] ;\n"
+                          "                          Execute program if true."));
   _cupsLangPuts(stderr, _("  --domain regex          Match domain to regular expression."));
   _cupsLangPuts(stderr, _("  --exec utility [argument ...] ;\n"
                           "                          Execute program if true."));
@@ -2088,8 +2873,3 @@ show_version(void)
 
   exit(IPPFIND_EXIT_TRUE);
 }
-
-
-/*
- * End of "$Id$".
- */