]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - test/ippfind.c
libcupsimage is now in the "cups" directory.
[thirdparty/cups.git] / test / ippfind.c
index 3482f41aa8f155beb6301ea132591c3314c1daab..ba814147364e2c86a2d75bca2e95ad8d461f1fdd 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-2017 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...
@@ -142,7 +143,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 +151,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 +192,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 +209,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 +235,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 */
@@ -256,6 +258,29 @@ 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",
+    "HOST_REGEX",
+    "PORT_RANGE",
+    "PATH_REGEX",
+    "TXT_EXISTS",
+    "TXT_REGEX",
+    "URI_REGEX",
+    "EXEC",
+    "LIST",
+    "PRINT_NAME",
+    "PRINT_URI",
+    "QUIET"
+  };
 
 
  /*
@@ -275,6 +300,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] == '-')
@@ -306,7 +335,12 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           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)
@@ -316,7 +350,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)
@@ -329,16 +367,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);
@@ -351,7 +389,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)
@@ -359,10 +402,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);
@@ -371,10 +410,6 @@ main(int  argc,                            /* I - Number of command-line args */
         }
         else if (!strcmp(argv[i], "--local"))
         {
-          i ++;
-          if (i >= argc)
-            show_usage();
-
           if ((temp = new_expr(IPPFIND_OP_IS_LOCAL, invert, NULL, NULL,
                                NULL)) == NULL)
             return (IPPFIND_EXIT_MEMORY);
@@ -383,7 +418,12 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           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)
@@ -484,7 +524,12 @@ main(int  argc,                            /* I - Number of command-line args */
         {
           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)
@@ -494,7 +539,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)
@@ -540,19 +590,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)
@@ -562,7 +621,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)
@@ -621,21 +685,20 @@ main(int  argc,                           /* I - Number of command-line args */
           * 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;
-          logic      = IPPFIND_OP_AND;
-          temp       = NULL;
+         current = temp;
+          invert  = 0;
+          logic   = IPPFIND_OP_AND;
+          temp    = NULL;
         }
       }
       else
@@ -659,7 +722,12 @@ main(int  argc,                            /* I - Number of command-line args */
             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)
@@ -669,7 +737,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;
@@ -677,7 +750,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;
@@ -688,44 +766,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)
@@ -733,10 +804,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);
@@ -747,7 +814,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)
@@ -787,7 +859,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)
@@ -797,18 +874,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)
@@ -853,21 +965,20 @@ main(int  argc,                           /* I - Number of command-line args */
            * 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;
-           logic      = IPPFIND_OP_AND;
-           temp       = NULL;
+           current = temp;
+           invert  = 0;
+           logic   = IPPFIND_OP_AND;
+           temp    = NULL;
          }
         }
       }
@@ -968,30 +1079,7 @@ main(int  argc,                           /* I - Number of command-line args */
 
   if (getenv("IPPFIND_DEBUG"))
   {
-    int                        indent = 4;     /* Indentation */
-    static const char * const ops[] =
-    {
-      "NONE",
-      "AND",
-      "OR",
-      "TRUE",
-      "FALSE",
-      "IS_LOCAL",
-      "IS_REMOTE",
-      "DOMAIN_REGEX",
-      "NAME_REGEX",
-      "HOST_REGEX",
-      "PORT_RANGE",
-      "PATH_REGEX",
-      "TXT_EXISTS",
-      "TXT_REGEX",
-      "URI_REGEX",
-      "EXEC",
-      "LIST",
-      "PRINT_NAME",
-      "PRINT_URI",
-      "QUIET"
-    };
+    int                indent = 4;             /* Indentation */
 
     puts("Expression tree:");
     current = expressions;
@@ -1017,8 +1105,15 @@ main(int  argc,                          /* I - Number of command-line args */
         current = current->next;
       else if (current->parent)
       {
-        current = current->parent->next;
-        indent -= 4;
+        while (current->parent)
+        {
+         indent -= 4;
+          current = current->parent;
+          if (current->next)
+            break;
+        }
+
+        current = current->next;
       }
       else
         current = NULL;
@@ -1046,8 +1141,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);
   }
 
@@ -1055,10 +1150,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 */
@@ -1073,27 +1168,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;
@@ -1125,7 +1239,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
@@ -1155,7 +1269,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 */
     }
 
@@ -1164,14 +1278,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);
     }
   }
@@ -1291,7 +1397,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)
@@ -1316,14 +1422,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;
         }
@@ -1352,7 +1458,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 */
@@ -1367,6 +1473,9 @@ browse_callback(
   * Only process "add" data...
   */
 
+  (void)sdRef;
+  (void)interfaceIndex;
+
   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
     return;
 
@@ -1382,7 +1491,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 */
@@ -1400,6 +1509,9 @@ browse_local_callback(
   * Only process "add" data...
   */
 
+  (void)sdRef;
+  (void)interfaceIndex;
+
   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
     return;
 
@@ -1446,7 +1558,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:
@@ -1612,8 +1724,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)
@@ -1697,13 +1811,16 @@ eval_expr(ippfind_srv_t  *service,      /* I - Service */
            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);
@@ -1744,11 +1861,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);
 }
 
 
@@ -1844,7 +2200,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);
 }
@@ -1917,8 +2480,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);
@@ -1949,10 +2513,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);
 }
@@ -1964,7 +2526,7 @@ poll_callback(
  */
 
 #ifdef HAVE_DNSSD
-static void
+static void DNSSD_API
 resolve_callback(
     DNSServiceRef       sdRef,         /* I - Service reference */
     DNSServiceFlags     flags,         /* I - Data flags */
@@ -1989,7 +2551,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));
@@ -2001,6 +2568,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...
   */
@@ -2039,36 +2610,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...
@@ -2190,8 +2767,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."));
@@ -2201,6 +2776,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."));
@@ -2267,8 +2844,3 @@ show_version(void)
 
   exit(IPPFIND_EXIT_TRUE);
 }
-
-
-/*
- * End of "$Id$".
- */