]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - test/ippfind.c
Update ippfind to support -N/--literal-name.
[thirdparty/cups.git] / test / ippfind.c
index 9294a48bab279ad71f2901c8bf186cd7ce1f78dc..c7e8765d0dc3efa2a4fe14c3797aa8008d87e9a3 100644 (file)
@@ -1,42 +1,12 @@
 /*
- * "$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-2018 by Apple Inc.
  *
- *   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:
- *
- *   main()                 - Browse for printers.
- *   browse_callback()      - Browse devices.
- *   browse_local_callback() - Browse local devices.
- *   browse_callback()      - Browse devices.
- *   client_callback()      - Avahi client callback function.
- *   compare_services()      - Compare two devices.
- *   dnssd_error_string()    - Return an error string for an error code.
- *   eval_expr()            - Evaluate the expressions against the specified
- *                            service.
- *   exec_program()         - Execute a program for a service.
- *   get_service()          - Create or update a device.
- *   get_time()             - Get the current time-of-day in seconds.
- *   list_service()         - List the contents of a service.
- *   new_expr()             - Create a new expression.
- *   poll_callback()        - Wait for input on the specified file
- *                            descriptors.
- *   resolve_callback()      - Process resolve data.
- *   set_service_uri()      - Set the URI of the service.
- *   show_usage()           - Show program usage.
- *   show_version()         - Show program version.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
  */
 
 /*
@@ -94,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 */
@@ -118,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 */
@@ -265,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 */
@@ -299,6 +270,7 @@ main(int  argc,                             /* I - Number of command-line args */
     "IS_REMOTE",
     "DOMAIN_REGEX",
     "NAME_REGEX",
+    "NAME_LITERAL",
     "HOST_REGEX",
     "PORT_RANGE",
     "PATH_REGEX",
@@ -330,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] == '-')
@@ -440,6 +416,18 @@ main(int  argc,                            /* I - Number of command-line args */
                                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_NAME_LITERAL, invert, argv[i], NULL, NULL)) == NULL)
+            return (IPPFIND_EXIT_MEMORY);
+        }
         else if (!strcmp(argv[i], "--name"))
         {
           i ++;
@@ -626,9 +614,9 @@ main(int  argc,                             /* I - Number of command-line args */
                                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)
@@ -745,6 +733,18 @@ 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)
@@ -782,7 +782,6 @@ main(int  argc,                             /* I - Number of command-line args */
                                  "ippfind");
                  show_usage();
                }
-                  show_usage();
 
                 if (!strcmp(argv[i], "1.1"))
                   ipp_version = 11;
@@ -948,7 +947,6 @@ main(int  argc,                             /* I - Number of command-line args */
                 _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."),
                                 "ippfind", *opt);
                 show_usage();
-                break;
           }
 
          if (temp)
@@ -1196,27 +1194,46 @@ main(int  argc,                         /* I - Number of command-line args */
                        *domain;        /* Domain, if any */
 
     strlcpy(buf, search, sizeof(buf));
-    if (buf[0] == '_')
+    if ((regtype = strstr(buf, "._")) != NULL)
     {
-      regtype = buf;
-    }
-    else 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;
@@ -1287,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);
     }
   }
@@ -1445,8 +1454,8 @@ main(int  argc,                           /* I - Number of command-line args */
            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;
         }
@@ -1490,6 +1499,9 @@ browse_callback(
   * Only process "add" data...
   */
 
+  (void)sdRef;
+  (void)interfaceIndex;
+
   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
     return;
 
@@ -1523,6 +1535,9 @@ browse_local_callback(
   * Only process "add" data...
   */
 
+  (void)sdRef;
+  (void)interfaceIndex;
+
   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
     return;
 
@@ -1801,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;
@@ -1812,16 +1830,19 @@ 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);
@@ -1912,14 +1933,14 @@ exec_program(ippfind_srv_t *service,    /* I - Service */
     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 = _cups_toupper(*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 *), myenvc + 1)) == NULL)
+  if ((myenvp = calloc(sizeof(char *), (size_t)(myenvc + 1))) == NULL)
   {
     _cupsLangPuts(stderr, _("ippfind: Out of memory."));
     exit(IPPFIND_EXIT_MEMORY);
@@ -1944,7 +1965,7 @@ exec_program(ippfind_srv_t *service,      /* I - Service */
   * Allocate and copy command-line arguments...
   */
 
-  if ((myargv = calloc(sizeof(char *), num_args + 1)) == NULL)
+  if ((myargv = calloc(sizeof(char *), (size_t)(num_args + 1))) == NULL)
   {
     _cupsLangPuts(stderr, _("ippfind: Out of memory."));
     exit(IPPFIND_EXIT_MEMORY);
@@ -1980,24 +2001,24 @@ exec_program(ippfind_srv_t *service,    /* I - Service */
 
           *kptr = '\0';
           if (!keyword[0] || !strcmp(keyword, "service_uri"))
-           strlcpy(tptr, service->uri, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, service->uri, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strcmp(keyword, "service_domain"))
-           strlcpy(tptr, service->domain, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, service->domain, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strcmp(keyword, "service_hostname"))
-           strlcpy(tptr, service->host, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, service->host, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strcmp(keyword, "service_name"))
-           strlcpy(tptr, service->name, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, service->name, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strcmp(keyword, "service_path"))
-           strlcpy(tptr, service->resource, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, service->resource, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strcmp(keyword, "service_port"))
-           strlcpy(tptr, port + 20, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, port + 21, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strcmp(keyword, "service_scheme"))
-           strlcpy(tptr, scheme + 22, sizeof(temp) - (tptr - temp));
+           strlcpy(tptr, scheme + 22, sizeof(temp) - (size_t)(tptr - temp));
          else if (!strncmp(keyword, "txt_", 4))
          {
-           if ((ptr = (char *)cupsGetOption(keyword + 4, service->num_txt,
-                                            service->txt)) != NULL)
-             strlcpy(tptr, strdup(ptr), sizeof(temp) - (tptr - temp));
+           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';
          }
@@ -2022,6 +2043,17 @@ exec_program(ippfind_srv_t *service,     /* I - Service */
   }
 
 #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
@@ -2082,10 +2114,25 @@ exec_program(ippfind_srv_t *service,    /* I - Service */
   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);
 }
 
@@ -2276,7 +2323,7 @@ list_service(ippfind_srv_t *service)      /* I - Service */
 
     if ((attr = ippFindAttribute(response, "printer-state",
                                  IPP_TAG_ENUM)) != NULL)
-      pstate = ippGetInteger(attr, 0);
+      pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
     else
       pstate = IPP_PSTATE_STOPPED;
 
@@ -2297,7 +2344,7 @@ list_service(ippfind_srv_t *service)      /* I - Service */
            i ++, ptr += strlen(ptr))
       {
         *ptr++ = ',';
-        strlcpy(ptr, ippGetString(attr, i, NULL), end - ptr + 1);
+        strlcpy(ptr, ippGetString(attr, i, NULL), (size_t)(end - ptr + 1));
       }
     }
     else
@@ -2382,11 +2429,7 @@ list_service(ippfind_srv_t *service)     /* I - Service */
     _cupsLangPrintf(stdout, "%s available", service->uri);
     httpAddrFreeList(addrlist);
 
-#ifdef WIN32
-    closesocket(sock);
-#else
-    close(sock);
-#endif /* WIN32 */
+    httpAddrClose(NULL, sock);
   }
   else
   {
@@ -2419,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)
   {
    /*
@@ -2467,8 +2510,8 @@ new_expr(ippfind_op_t op,         /* I - Operation */
         break;
 
      temp->num_args = num_args;
-     temp->args     = malloc(num_args * sizeof(char *));
-     memcpy(temp->args, args, num_args * sizeof(char *));
+     temp->args     = malloc((size_t)num_args * sizeof(char *));
+     memcpy(temp->args, args, (size_t)num_args * sizeof(char *));
   }
 
   return (temp);
@@ -2537,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));
@@ -2549,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...
   */
@@ -2620,6 +2672,10 @@ resolve_callback(
   service->host        = strdup(hostTarget);
   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...
   */
@@ -2817,8 +2873,3 @@ show_version(void)
 
   exit(IPPFIND_EXIT_TRUE);
 }
-
-
-/*
- * End of "$Id$".
- */