static void browse_callback()

in tools/ippfind.c [152:1474]


static void		browse_callback(AvahiServiceBrowser *browser,
					AvahiIfIndex interface,
					AvahiProtocol protocol,
					AvahiBrowserEvent event,
					const char *serviceName,
					const char *regtype,
					const char *replyDomain,
					AvahiLookupResultFlags flags,
					void *context);
static void		client_callback(AvahiClient *client,
					AvahiClientState state,
					void *context);
#endif /* HAVE_DNSSD */

static int		compare_services(ippfind_srv_t *a, ippfind_srv_t *b);
static const char	*dnssd_error_string(int error);
static int		eval_expr(ippfind_srv_t *service,
			          ippfind_expr_t *expressions);
static int		exec_program(ippfind_srv_t *service, int num_args,
			             char **args);
static ippfind_srv_t	*get_service(cups_array_t *services, const char *serviceName, const char *regtype, const char *replyDomain) _CUPS_NONNULL(1,2,3,4);
static double		get_time(void);
static int		list_service(ippfind_srv_t *service);
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 DNSSD_API	resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullName, const char *hostTarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) _CUPS_NONNULL(1,5,6,9, 10);
#elif defined(HAVE_AVAHI)
static int		poll_callback(struct pollfd *pollfds,
			              unsigned int num_pollfds, int timeout,
			              void *context);
static void		resolve_callback(AvahiServiceResolver *res,
					 AvahiIfIndex interface,
					 AvahiProtocol protocol,
					 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,
					 void *context);
#endif /* HAVE_DNSSD */
static void		set_service_uri(ippfind_srv_t *service);
static void		show_usage(void) _CUPS_NORETURN;
static void		show_version(void) _CUPS_NORETURN;


/*
 * 'main()' - Browse for printers.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line arguments */
{
  int			i,		/* Looping var */
			have_output = 0,/* Have output expression */
			status = IPPFIND_EXIT_FALSE;
					/* Exit status */
  const char		*opt,		/* Option character */
			*search;	/* Current browse/resolve string */
  cups_array_t		*searches;	/* Things to browse/resolve */
  cups_array_t		*services;	/* Service array */
  ippfind_srv_t		*service;	/* Current service */
  ippfind_expr_t	*expressions = NULL,
					/* Expression tree */
			*temp = NULL,	/* New expression */
			*parent = NULL,	/* Parent 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? */
  int			err;		/* DNS-SD error */
#ifdef HAVE_DNSSD
  fd_set		sinput;		/* Input set for select() */
  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"
  };


 /*
  * Initialize the locale...
  */

  _cupsSetLocale(argv);

 /*
  * Create arrays to track services and things we want to browse/resolve...
  */

  searches = cupsArrayNew(NULL, NULL);
  services = cupsArrayNew((cups_array_func_t)compare_services, NULL);

 /*
  * 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] == '-')
    {
      if (argv[i][1] == '-')
      {
       /*
        * Parse --option options...
        */

        if (!strcmp(argv[i], "--and"))
        {
          if (logic == IPPFIND_OP_OR)
          {
            _cupsLangPuts(stderr, _("ippfind: Cannot use --and after --or."));
            show_usage();
          }

          if (!current)
          {
            _cupsLangPuts(stderr,
                          _("ippfind: Missing expression before \"--and\"."));
            show_usage();
          }

	  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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--exec"))
        {
          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)
            return (IPPFIND_EXIT_MEMORY);

          while (i < argc)
            if (!strcmp(argv[i], ";"))
              break;
            else
              i ++;

          if (i >= argc)
          {
            _cupsLangPrintf(stderr, _("ippfind: Expected semi-colon after %s."),
                            "--exec");
            show_usage();
          }

          have_output = 1;
        }
        else if (!strcmp(argv[i], "--false"))
        {
          if ((temp = new_expr(IPPFIND_OP_FALSE, invert, NULL, NULL,
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--help"))
        {
          show_usage();
        }
        else if (!strcmp(argv[i], "--host"))
        {
          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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--ls"))
        {
          if ((temp = new_expr(IPPFIND_OP_LIST, invert, NULL, NULL,
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);

          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_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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--not"))
        {
          invert = 1;
        }
        else if (!strcmp(argv[i], "--or"))
        {
          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
	  {
	   /*
	    * Need to group previous expressions in an AND node, and then
	    * put that in an OR node...
	    */

	    if ((temp = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
				 NULL)) == NULL)
	      return (IPPFIND_EXIT_MEMORY);

	    while (current->prev)
	    {
	      current->parent = temp;
	      current         = current->prev;
	    }

	    current->parent = temp;
	    temp->child     = current;
	    current         = temp;

	    if ((temp = new_expr(IPPFIND_OP_OR, 0, NULL, NULL,
				 NULL)) == NULL)
	      return (IPPFIND_EXIT_MEMORY);

            temp->parent    = parent;
            current->parent = temp;

            if (parent)
              parent->child = temp;
            else
              expressions = temp;

	    parent = temp;
	    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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--port"))
        {
          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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--print"))
        {
          if ((temp = new_expr(IPPFIND_OP_PRINT_URI, invert, NULL, NULL,
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);

          have_output = 1;
        }
        else if (!strcmp(argv[i], "--print-name"))
        {
          if ((temp = new_expr(IPPFIND_OP_PRINT_NAME, invert, NULL, NULL,
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);

          have_output = 1;
        }
        else if (!strcmp(argv[i], "--quiet"))
        {
          if ((temp = new_expr(IPPFIND_OP_QUIET, invert, NULL, NULL,
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);

          have_output = 1;
        }
        else if (!strcmp(argv[i], "--remote"))
        {
          if ((temp = new_expr(IPPFIND_OP_IS_REMOTE, invert, NULL, NULL,
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--true"))
        {
          if ((temp = new_expr(IPPFIND_OP_TRUE, invert, NULL, argv[i],
                               NULL)) == NULL)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--txt"))
        {
          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-", 6))
        {
          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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--uri"))
        {
          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)
            return (IPPFIND_EXIT_MEMORY);
        }
        else if (!strcmp(argv[i], "--version"))
        {
          show_version();
        }
        else
        {
	  _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."),
			  "ippfind", argv[i]);
	  show_usage();
	}

        if (temp)
        {
         /*
          * 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)
	    current->next = temp;
	  else if (parent)
	    parent->child = temp;
	  else
	    expressions = temp;

	  current = temp;
          invert  = 0;
          logic   = IPPFIND_OP_AND;
          temp    = NULL;
        }
      }
      else
      {
       /*
        * Parse -o options
        */

        for (opt = argv[i] + 1; *opt; opt ++)
        {
          switch (*opt)
          {
            case '4' :
                address_family = AF_INET;
                break;

            case '6' :
                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)
		  return (IPPFIND_EXIT_MEMORY);
		break;

            case 'T' :
                i ++;
                if (i >= argc)
		{
		  _cupsLangPrintf(stderr,
				  _("%s: Missing timeout for \"-T\"."),
				  "ippfind");
		  show_usage();
		}

                bonjour_timeout = atof(argv[i]);
                break;

            case 'V' :
                i ++;
                if (i >= argc)
		{
		  _cupsLangPrintf(stderr,
				  _("%s: Missing version for \"-V\"."),
				  "ippfind");
		  show_usage();
		}

                if (!strcmp(argv[i], "1.1"))
                  ipp_version = 11;
                else if (!strcmp(argv[i], "2.0"))
                  ipp_version = 20;
                else if (!strcmp(argv[i], "2.1"))
                  ipp_version = 21;
                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 '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)
		  return (IPPFIND_EXIT_MEMORY);
                break;

            case 'l' :
		if ((temp = new_expr(IPPFIND_OP_LIST, invert, NULL, NULL,
				     NULL)) == NULL)
		  return (IPPFIND_EXIT_MEMORY);

		have_output = 1;
                break;

            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)
		  return (IPPFIND_EXIT_MEMORY);
                break;

            case 'p' :
		if ((temp = new_expr(IPPFIND_OP_PRINT_URI, invert, NULL, NULL,
				     NULL)) == NULL)
		  return (IPPFIND_EXIT_MEMORY);

		have_output = 1;
                break;

            case 'q' :
		if ((temp = new_expr(IPPFIND_OP_QUIET, invert, NULL, NULL,
				     NULL)) == NULL)
		  return (IPPFIND_EXIT_MEMORY);

		have_output = 1;
                break;

            case 'r' :
		if ((temp = new_expr(IPPFIND_OP_IS_REMOTE, invert, NULL, NULL,
				     NULL)) == NULL)
		  return (IPPFIND_EXIT_MEMORY);
                break;

            case 's' :
		if ((temp = new_expr(IPPFIND_OP_PRINT_NAME, invert, NULL, NULL,
				     NULL)) == NULL)
		  return (IPPFIND_EXIT_MEMORY);

		have_output = 1;
                break;

            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)
		  return (IPPFIND_EXIT_MEMORY);
                break;

            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();
          }

	  if (temp)
	  {
	   /*
	    * 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)
	      current->next = temp;
	    else if (parent)
	      parent->child = temp;
	    else
	      expressions = temp;

	    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;

      parent  = temp;
      current = NULL;
      invert  = 0;
      logic   = IPPFIND_OP_AND;
    }
    else if (!strcmp(argv[i], ")"))
    {
      if (num_parens <= 0)
      {
        _cupsLangPuts(stderr, _("ippfind: Missing open parenthesis."));
        show_usage();
      }

      current = parens[--num_parens];
      parent  = current->parent;
      invert  = 0;
      logic   = IPPFIND_OP_AND;
    }
    else if (!strcmp(argv[i], "!"))
    {
      invert = 1;
    }
    else
    {
     /*
      * _regtype._tcp[,subtype][.domain]
      *
      *   OR
      *
      * service-name[._regtype._tcp[.domain]]
      */

      cupsArrayAdd(searches, argv[i]);
    }
  }

  if (num_parens > 0)
  {
    _cupsLangPuts(stderr, _("ippfind: Missing close parenthesis."));
    show_usage();
  }

  if (!have_output)
  {
   /*
    * Add an implicit --print-uri to the end...
    */

    if ((temp = new_expr(IPPFIND_OP_PRINT_URI, 0, NULL, NULL, NULL)) == NULL)
      return (IPPFIND_EXIT_MEMORY);

    if (current)
    {
      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;
    }

    puts("\nSearch items:");
    for (search = (const char *)cupsArrayFirst(searches);
	 search;
	 search = (const char *)cupsArrayNext(searches))
      printf("    %s\n", search);
  }

 /*
  * Start up browsing/resolving...
  */

#ifdef HAVE_DNSSD
  if ((err = DNSServiceCreateConnection(&dnssd_ref)) != kDNSServiceErr_NoError)
  {
    _cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
                    dnssd_error_string(err));
    return (IPPFIND_EXIT_BONJOUR);
  }

#elif defined(HAVE_AVAHI)
  if ((avahi_poll = avahi_simple_poll_new()) == NULL)
  {
    _cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
                    strerror(errno));
    return (IPPFIND_EXIT_BONJOUR);
  }

  avahi_simple_poll_set_func(avahi_poll, poll_callback, NULL);

  avahi_client = avahi_client_new(avahi_simple_poll_get(avahi_poll),
			          0, client_callback, avahi_poll, &err);
  if (!avahi_client)
  {
    _cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
                    dnssd_error_string(err));
    return (IPPFIND_EXIT_BONJOUR);
  }
#endif /* HAVE_DNSSD */

  for (search = (const char *)cupsArrayFirst(searches);
       search;
       search = (const char *)cupsArrayNext(searches))
  {
    char		buf[1024],	/* Full name string */
			*name = NULL,	/* Service instance name */
			*regtype,	/* Registration type */
			*domain;	/* Domain, if any */

    strlcpy(buf, search, sizeof(buf));

    if (!strncmp(buf, "_http._", 7) || !strncmp(buf, "_https._", 8) || !strncmp(buf, "_ipp._", 6) || !strncmp(buf, "_ipps._", 7))
    {
      regtype = buf;
    }
    else if ((regtype = strstr(buf, "._")) != NULL)
    {
      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;
      }
    }

    if (!*domain)
      domain = NULL;

    if (name)
    {
     /*
      * Resolve the given service instance name, regtype, and domain...
      */

      if (!domain)
        domain = "local.";

      service = get_service(services, name, regtype, domain);

      if (getenv("IPPFIND_DEBUG"))
        fprintf(stderr, "Resolving name=\"%s\", regtype=\"%s\", domain=\"%s\"\n", name, regtype, domain);

#ifdef HAVE_DNSSD
      service->ref = dnssd_ref;
      err          = DNSServiceResolve(&(service->ref),
                                       kDNSServiceFlagsShareConnection, 0, name,
				       regtype, domain, resolve_callback,
				       service);

#elif defined(HAVE_AVAHI)
      service->ref = avahi_service_resolver_new(avahi_client, AVAHI_IF_UNSPEC,
                                                AVAHI_PROTO_UNSPEC, name,
                                                regtype, domain,
                                                AVAHI_PROTO_UNSPEC, 0,
                                                resolve_callback, service);
      if (service->ref)
        err = 0;
      else
        err = avahi_client_errno(avahi_client);
#endif /* HAVE_DNSSD */
    }
    else
    {
     /*
      * Browse for services of the given type...
      */

      if (getenv("IPPFIND_DEBUG"))
        fprintf(stderr, "Browsing for regtype=\"%s\", domain=\"%s\"\n", regtype, domain);

#ifdef HAVE_DNSSD
      DNSServiceRef	ref;		/* Browse reference */

      ref = dnssd_ref;
      err = DNSServiceBrowse(&ref, kDNSServiceFlagsShareConnection, 0, regtype,
                             domain, browse_callback, services);

      if (!err)
      {
	ref = dnssd_ref;
	err = DNSServiceBrowse(&ref, kDNSServiceFlagsShareConnection,
			       kDNSServiceInterfaceIndexLocalOnly, regtype,
			       domain, browse_local_callback, services);
      }

#elif defined(HAVE_AVAHI)
      char	*subtype,		/* Sub-type, if any */
		subtype_buf[256];	/* Sub-type buffer */

      if ((subtype = strstr(regtype, ",_")) != NULL)
      {
        *subtype++ = '\0';
        snprintf(subtype_buf, sizeof(subtype_buf), "%s._sub.%s", subtype, regtype);
        regtype = subtype_buf;
      }

      if (avahi_service_browser_new(avahi_client, AVAHI_IF_UNSPEC,
                                    AVAHI_PROTO_UNSPEC, regtype, domain, 0,
                                    browse_callback, services))
        err = 0;
      else
        err = avahi_client_errno(avahi_client);
#endif /* HAVE_DNSSD */
    }

    if (err)
    {
      _cupsLangPrintf(stderr, _("ippfind: Unable to browse or resolve: %s"),
                      dnssd_error_string(err));

      return (IPPFIND_EXIT_BONJOUR);
    }
  }

 /*
  * Process browse/resolve requests...
  */

  if (bonjour_timeout > 1.0)
    endtime = get_time() + bonjour_timeout;
  else
    endtime = get_time() + 300.0;

  while (get_time() < endtime)
  {
    int		process = 0;		/* Process services? */

#ifdef HAVE_DNSSD
    int fd = DNSServiceRefSockFD(dnssd_ref);
					/* File descriptor for DNS-SD */

    FD_ZERO(&sinput);
    FD_SET(fd, &sinput);

    stimeout.tv_sec  = 0;
    stimeout.tv_usec = 500000;

    if (select(fd + 1, &sinput, NULL, NULL, &stimeout) < 0)
      continue;

    if (FD_ISSET(fd, &sinput))
    {
     /*
      * Process responses...
      */

      DNSServiceProcessResult(dnssd_ref);
    }
    else
    {
     /*
      * Time to process services...
      */

      process = 1;
    }

#elif defined(HAVE_AVAHI)
    avahi_got_data = 0;

    if (avahi_simple_poll_iterate(avahi_poll, 500) > 0)
    {
     /*
      * We've been told to exit the loop.  Perhaps the connection to
      * Avahi failed.
      */

      return (IPPFIND_EXIT_BONJOUR);
    }

    if (!avahi_got_data)
    {
     /*
      * Time to process services...
      */

      process = 1;
    }
#endif /* HAVE_DNSSD */

    if (process)
    {
     /*
      * Process any services that we have found...
      */

      int	active = 0,		/* Number of active resolves */
		resolved = 0,		/* Number of resolved services */
		processed = 0;		/* Number of processed services */

      for (service = (ippfind_srv_t *)cupsArrayFirst(services);
           service;
           service = (ippfind_srv_t *)cupsArrayNext(services))
      {
        if (service->is_processed)
          processed ++;

        if (service->is_resolved)
          resolved ++;

        if (!service->ref && !service->is_resolved)
        {
         /*
          * Found a service, now resolve it (but limit to 50 active resolves...)
          */

          if (active < 50)
          {
#ifdef HAVE_DNSSD
	    service->ref = dnssd_ref;
	    err          = DNSServiceResolve(&(service->ref),
					     kDNSServiceFlagsShareConnection, 0,
					     service->name, service->regtype,
					     service->domain, resolve_callback,
					     service);

#elif defined(HAVE_AVAHI)
	    service->ref = avahi_service_resolver_new(avahi_client,
						      AVAHI_IF_UNSPEC,
						      AVAHI_PROTO_UNSPEC,
						      service->name,
						      service->regtype,
						      service->domain,
						      AVAHI_PROTO_UNSPEC, 0,
						      resolve_callback,
						      service);
	    if (service->ref)
	      err = 0;
	    else
	      err = avahi_client_errno(avahi_client);
#endif /* HAVE_DNSSD */

	    if (err)
	    {
	      _cupsLangPrintf(stderr,
	                      _("ippfind: Unable to browse or resolve: %s"),
			      dnssd_error_string(err));
	      return (IPPFIND_EXIT_BONJOUR);
	    }

	    active ++;
          }
        }
        else if (service->is_resolved && !service->is_processed)
        {
	 /*
	  * Resolved, not process this service against the expressions...
	  */

          if (service->ref)
          {
#ifdef HAVE_DNSSD
	    DNSServiceRefDeallocate(service->ref);
#else
            avahi_service_resolver_free(service->ref);
#endif /* HAVE_DNSSD */

	    service->ref = NULL;
	  }

          if (eval_expr(service, expressions))
            status = IPPFIND_EXIT_TRUE;

          service->is_processed = 1;
        }
        else if (service->ref)
          active ++;
      }

     /*
      * If we have processed all services we have discovered, then we are done.
      */

      if (processed == cupsArrayCount(services) && bonjour_timeout <= 1.0)
        break;
    }
  }

  if (bonjour_error)
    return (IPPFIND_EXIT_BONJOUR);
  else
    return (status);
}