]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Save work on ippfind program. Nearly there.
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Wed, 5 Jun 2013 16:05:45 +0000 (16:05 +0000)
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Wed, 5 Jun 2013 16:05:45 +0000 (16:05 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@11010 a1ca3aef-8c08-0410-bb20-df032aa958be

Makedefs.in
config-scripts/cups-dnssd.m4
doc/help/man-ippfind.html [new file with mode: 0644]
man/Makefile
man/ippfind.man [new file with mode: 0644]
test/Dependencies
test/Makefile
test/ippfind.c
test/run-stp-tests.sh

index c971b0200f33245eef1b77fec2e7fd4dc8203214..676e0dd64df82031c698f575f7611403074fadb9 100644 (file)
@@ -138,6 +138,8 @@ DNSSD_BACKEND       =       @DNSSD_BACKEND@
 DSOFLAGS       =       -L../cups @DSOFLAGS@
 DSOLIBS                =       @DSOLIBS@ $(COMMONLIBS)
 DNSSDLIBS      =       @DNSSDLIBS@
+IPPFIND_BIN    =       @IPPFIND_BIN@
+IPPFIND_MAN    =       @IPPFIND_MAN@
 LAUNCHDLIBS    =       @LAUNCHDLIBS@
 LDFLAGS                =       -L../cgi-bin -L../cups -L../filter -L../ppdc \
                        -L../scheduler @LDARCHFLAGS@ \
index 426637a20e74cf120a8b5ab0dff17ada7bab0f45..02974ef8aea4bd2422a5a0b615bc0337038e2b47 100644 (file)
@@ -23,6 +23,8 @@ AC_ARG_WITH(dnssd-includes, [  --with-dnssd-includes   set directory for DNS Ser
 
 DNSSDLIBS=""
 DNSSD_BACKEND=""
+IPPFIND_BIN=""
+IPPFIND_MAN=""
 
 if test "x$PKGCONFIG" != x -a x$enable_avahi != xno; then
        AC_MSG_CHECKING(for Avahi)
@@ -31,6 +33,8 @@ if test "x$PKGCONFIG" != x -a x$enable_avahi != xno; then
                CFLAGS="$CFLAGS `$PKGCONFIG --cflags avahi-client`"
                DNSSDLIBS="`$PKGCONFIG --libs avahi-client`"
                DNSSD_BACKEND="dnssd"
+               IPPFIND_BIN="ippfind"
+               IPPFIND_MAN="ippfind.\$(MAN1EXT)"
                AC_DEFINE(HAVE_AVAHI)
        else
                AC_MSG_RESULT(no)
@@ -45,6 +49,8 @@ if test "x$DNSSD_BACKEND" = x -a x$enable_dnssd != xno; then
                                AC_DEFINE(HAVE_DNSSD)
                                DNSSDLIBS="-framework CoreFoundation -framework SystemConfiguration"
                                DNSSD_BACKEND="dnssd"
+                               IPPFIND_BIN="ippfind"
+                               IPPFIND_MAN="ippfind.\$(MAN1EXT)"
                                ;;
                        *)
                                # All others...
@@ -61,6 +67,8 @@ if test "x$DNSSD_BACKEND" = x -a x$enable_dnssd != xno; then
                                        AC_DEFINE(HAVE_DNSSD)
                                        DNSSDLIBS="-ldns_sd"
                                        DNSSD_BACKEND="dnssd",
+                                       IPPFIND_BIN="ippfind"
+                                       IPPFIND_MAN="ippfind.\$(MAN1EXT)"
                                        AC_MSG_RESULT(no))
                                LIBS="$SAVELIBS"
                                ;;
@@ -70,6 +78,8 @@ fi
 
 AC_SUBST(DNSSDLIBS)
 AC_SUBST(DNSSD_BACKEND)
+AC_SUBST(IPPFIND_BIN)
+AC_SUBST(IPPFIND_MAN)
 
 dnl
 dnl End of "$Id: cups-dnssd.m4 7890 2008-08-29 22:19:39Z mike $".
diff --git a/doc/help/man-ippfind.html b/doc/help/man-ippfind.html
new file mode 100644 (file)
index 0000000..32c5398
--- /dev/null
@@ -0,0 +1,294 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<!-- SECTION: Man Pages -->
+<head>
+       <link rel="stylesheet" type="text/css" href="../cups-printable.css">
+       <title>ippfind(1)</title>
+</head>
+<body>
+<h1 class="title">ippfind(1)</h1>
+<h2 class="title"><a name="NAME">Name</a></h2>
+ippfind - find internet printing protocol printers
+<h2 class="title"><a name="SYNOPSIS">Synopsis</a></h2>
+<b>ippfind
+</b>[
+<i>options
+</i>] regtype[,subtype][.domain.] ... [
+<i>expression ...
+</i>]
+<b>ippfind
+</b>[
+<i>options
+</i>] name[.regtype[.domain.]] ... [
+<i>expression ...
+</i>]
+<b>ippfind
+</b>--help
+<b>ippfind
+</b>--version
+<h2 class="title"><a name="SUPPORTED_REGISTRATION_TYPES">Supported Registration Types</a></h2>
+<i>ippfind</i> supports the following registration types:
+<dl>
+<dt>_http._tcp
+</dt>
+<dd>HyperText Transport Protocol (HTTP, RFC 2616)
+</dd>
+<dt>_https._tcp
+</dt>
+<dd>Secure HyperText Transport Protocol (HTTPS, RFC 2818)
+</dd>
+<dt>_ipp._tcp
+</dt>
+<dd>Internet Printing Protocol (IPP, RFC 2911)
+</dd>
+<dt>_ipps._tcp
+</dt>
+<dd>Secure Internet Printing Protocol (IPPS, draft)
+</dd>
+<dt>_printer._tcp
+</dt>
+<dd>Line Printer Daemon (LPD, RFC 1179)
+
+</dd>
+</dl>
+<h2 class="title"><a name="OPTIONS">Options</a></h2>
+<dl>
+<dt>--help
+</dt>
+<dd>Show program help
+</dd>
+<dt>--version
+</dt>
+<dd>Show program version
+</dd>
+<dt>-4
+</dt>
+<dd>Use IPv4 when listing
+</dd>
+<dt>-6
+</dt>
+<dd>Use IPv6 when listing
+</dd>
+<dt>-T seconds
+</dt>
+<dd>Specify find timeout in seconds. If 1 or less, <i>ippfind</i> stops as soon as it thinks it has found everything. The default is 1 second.
+</dd>
+<dt>-V version
+</dt>
+<dd>Specifies the IPP version when listing. Supported values are 1.1, 2.0, 2.1, and 2.2.
+
+</dd>
+</dl>
+<h2 class="title"><a name="DESCRIPTION">Description</a></h2>
+<i>ippfind</i> finds printer services registered with the local DNS infrastructure or available through the local links.
+
+<h2 class="title"><a name="EXPRESSIONS">Expressions</a></h2>
+<i>ippfind</i> supports expressions much like the <i>find(1)</i> utility. However, unlike <i>find</i>, <i>ippfind</i> uses POSIX regular expressions instead of shell filename matching patterns. If -e, --exec, -l, --ls, -p, --print, --print-name, -q, --quiet, or -s is not specified, <i>ippfind</i> adds --print to print the service URI of anything it finds. The following expressions are supported:
+<dl>
+<dt>-d regex
+</dt>
+<dd></dd>
+<dt>--domain regex
+</dt>
+<dd>True if the domain matches the given regular expression.
+</dd>
+<dt>-e utility [argument ...] ;
+</dt>
+<dd></dd>
+<dt>--exec utility [argument ...] ;
+</dt>
+<dd>Executes the specified program if the current result is true. "{foo}" arguments are replaced with the corresponding value - see SUBSTITUTIONS below.
+</dd>
+<dt>--false
+</dt>
+<dd>Always false.
+</dd>
+<dt>-l
+</dt>
+<dd></dd>
+<dt>--ls
+</dt>
+<dd>Lists attributes returned by Get-Printer-Attributes for IPP printers and traditional <i>find</i> "-ls" output for HTTP URLs. The result is true if the URI is accessible, false otherwise.
+</dd>
+<dt>--local
+</dt>
+<dd>True if the service is local to this computer.
+</dd>
+<dt>-n regex
+</dt>
+<dd></dd>
+<dt>--name regex
+</dt>
+<dd>True if the service instance name matches the given regular expression.
+</dd>
+<dt>--path regex
+</dt>
+<dd>True if the URI resource path matches the given regular expression.
+</dd>
+<dt>-p
+</dt>
+<dd></dd>
+<dt>--print
+</dt>
+<dd>Prints the URI if the result of previous expressions is true. The result is always true.
+</dd>
+<dt>-q
+</dt>
+<dd></dd>
+<dt>--quiet
+</dt>
+<dd>Quiet mode - just returns the exit codes below.
+</dd>
+<dt>-r
+</dt>
+<dd></dd>
+<dt>--remote
+</dt>
+<dd>True if the service is not local to this computer.
+</dd>
+<dt>-s
+</dt>
+<dd></dd>
+<dt>--print-name
+</dt>
+<dd>Prints the service instance name if the result of previous expressions is true. The result is always true.
+</dd>
+<dt>--true
+</dt>
+<dd>Always true.
+</dd>
+<dt>-t key
+</dt>
+<dd></dd>
+<dt>--txt key
+</dt>
+<dd>True if the TXT record contains the named key.
+</dd>
+<dt>--txt-<i>key</i> regex
+</dt>
+<dd>True if the TXT record contains the named key and matches the given regular
+expression.
+</dd>
+<dt>-u regex
+</dt>
+<dd></dd>
+<dt>--uri regex
+</dt>
+<dd>True if the URI matches the given regular expression.
+</dd>
+</dl>
+<p>Expressions may also contain modifiers:
+<dl>
+<dt>( expression )
+</dt>
+<dd>Group the result of expressions.
+</dd>
+<dt>! expression
+</dt>
+<dd></dd>
+<dt>--not expression
+</dt>
+<dd>Unary NOT of the expression.
+</dd>
+<dt>expression expression
+</dt>
+<dd></dd>
+<dt>expression --and expression
+</dt>
+<dd>Logical AND of expressions.
+</dd>
+<dt>expression --or expression
+</dt>
+<dd>Logical OR of expressions.
+
+</dd>
+</dl>
+<h2 class="title"><a name="SUBSTITUTIONS">Substitutions</a></h2>
+The substitutions for "{foo}" in -e and --exec are:
+<dl>
+<dt>{}
+</dt>
+<dd>URI
+</dd>
+<dt>{service_domain}
+</dt>
+<dd>Domain name, e.g., "example.com.", "local.", etc.
+</dd>
+<dt>{service_hostname}
+</dt>
+<dd>Fully-qualified domain name, e.g., "printer.example.com.", "printer.local.", etc.
+</dd>
+<dt>{service_name}
+</dt>
+<dd>Service instance name, e.g., "My Fine Printer".
+</dd>
+<dt>{service_port}
+</dt>
+<dd>Port number for server, typically 631 for IPP and 80 for HTTP.
+</dd>
+<dt>{service_regtype}
+</dt>
+<dd>DNS-SD registration type, e.g., "_ipp._tcp", "_http._tcp", etc.
+</dd>
+<dt>{service_scheme}
+</dt>
+<dd>URI scheme for DNS-SD registration type, e.g., "ipp", "http", etc.
+</dd>
+<dt>{service_uri}
+</dt>
+<dd>URI for service, e.g., "ipp://printer.local./ipp/print", "<a href='http://printer.local./",'>http://printer.local./",</a> etc.
+</dd>
+<dt>{txt_<i>key</i>}
+</dt>
+<dd>Value of TXT record <i>key</i> (lowercase).
+
+</dd>
+</dl>
+<h2 class="title"><a name="ENVIRONMENT_VARIABLES">Environment Variables</a></h2>
+When executing a program, <i>ippfind</i> sets the following environment variables for the matching service registration:
+<dl>
+<dt>IPPFIND_SERVICE_DOMAIN
+</dt>
+<dd>Domain name, e.g., "example.com.", "local.", etc.
+</dd>
+<dt>IPPFIND_SERVICE_HOSTNAME
+</dt>
+<dd>Fully-qualified domain name, e.g., "printer.example.com.", "printer.local.", etc.
+</dd>
+<dt>IPPFIND_SERVICE_NAME
+</dt>
+<dd>Service instance name, e.g., "My Fine Printer".
+</dd>
+<dt>IPPFIND_SERVICE_PORT
+</dt>
+<dd>Port number for server, typically 631 for IPP and 80 for HTTP.
+</dd>
+<dt>IPPFIND_SERVICE_REGTYPE
+</dt>
+<dd>DNS-SD registration type, e.g., "_ipp._tcp", "_http._tcp", etc.
+</dd>
+<dt>IPPFIND_SERVICE_SCHEME
+</dt>
+<dd>URI scheme for DNS-SD registration type, e.g., "ipp", "http", etc.
+</dd>
+<dt>IPPFIND_SERVICE_URI
+</dt>
+<dd>URI for service, e.g., "ipp://printer.local./ipp/print", "<a href='http://printer.local./",'>http://printer.local./",</a> etc.
+</dd>
+<dt>IPPFIND_TXT_<i>KEY</i>
+</dt>
+<dd>Values of TXT record <i>KEY</i> (uppercase).
+
+</dd>
+</dl>
+<h2 class="title"><a name="EXIT_CODES">Exit Codes</a></h2>
+<i>ippfind</i> returns 0 if the result for all processed expressions is true, 1 if the result of any processed expression is false, 2 if browsing or any query or resolution failed, 3 if an undefined option or invalid expression was specified, and 4 if it ran out of memory.
+
+<h2 class="title"><a name="SEE_ALSO">See Also</a></h2>
+<a href='man-ipptool.html?TOPIC=Man+Pages'>ipptool(1)</a>
+
+<h2 class="title"><a name="COPYRIGHT">Copyright</a></h2>
+Copyright 2013 by Apple Inc.
+
+</body>
+</html>
index 29e0557aa7fa802968730daf64ebf355c6606d3a..37c51a478d89357f97f32b8fe608e0279939d909 100644 (file)
@@ -3,7 +3,7 @@
 #
 #   Man page makefile for CUPS.
 #
-#   Copyright 2007-2012 by Apple Inc.
+#   Copyright 2007-2013 by Apple Inc.
 #   Copyright 1993-2006 by Easy Software Products.
 #
 #   These coded instructions, statements, and computer programs are the
@@ -24,6 +24,7 @@ MAN1  =       cancel.$(MAN1EXT) \
                cups-config.$(MAN1EXT) \
                cupstestdsc.$(MAN1EXT) \
                cupstestppd.$(MAN1EXT) \
+               $(IPPFIND_MAN) \
                ipptool.$(MAN1EXT) \
                lp.$(MAN1EXT) \
                lpoptions.$(MAN1EXT) \
diff --git a/man/ippfind.man b/man/ippfind.man
new file mode 100644 (file)
index 0000000..0741a3e
--- /dev/null
@@ -0,0 +1,229 @@
+.\"
+.\" "$Id$"
+.\"
+.\"   ippfind man page for CUPS.
+.\"
+.\"   Copyright 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/".
+.\"
+.TH ippfind 1 "CUPS" "5 June 2013" "Apple Inc."
+.SH NAME
+ippfind - find internet printing protocol printers
+.SH SYNOPSIS
+.B ippfind
+[
+.I options
+] regtype[,subtype][.domain.] ... [
+.I expression ...
+]
+.B ippfind
+[
+.I options
+] name[.regtype[.domain.]] ... [
+.I expression ...
+]
+.B ippfind
+--help
+.B ippfind
+--version
+.SH SUPPORTED REGISTRATION TYPES
+\fIippfind\fR supports the following registration types:
+.TP 5
+_http._tcp
+HyperText Transport Protocol (HTTP, RFC 2616)
+.TP 5
+_https._tcp
+Secure HyperText Transport Protocol (HTTPS, RFC 2818)
+.TP 5
+_ipp._tcp
+Internet Printing Protocol (IPP, RFC 2911)
+.TP 5
+_ipps._tcp
+Secure Internet Printing Protocol (IPPS, draft)
+.TP 5
+_printer._tcp
+Line Printer Daemon (LPD, RFC 1179)
+
+.SH OPTIONS
+.TP 5
+--help
+Show program help
+.TP 5
+--version
+Show program version
+.TP 5
+-4
+Use IPv4 when listing
+.TP 5
+-6
+Use IPv6 when listing
+.TP 5
+-T seconds
+Specify find timeout in seconds. If 1 or less, \fIippfind\fR stops as soon as it thinks it has found everything. The default is 1 second.
+.TP 5
+-V version
+Specifies the IPP version when listing. Supported values are 1.1, 2.0, 2.1, and 2.2.
+
+.SH DESCRIPTION
+\fIippfind\fR finds printer services registered with the local DNS infrastructure or available through the local links.
+
+.SH EXPRESSIONS
+\fIippfind\fR supports expressions much like the \fIfind(1)\fR utility. However, unlike \fIfind\fR, \fIippfind\fR uses POSIX regular expressions instead of shell filename matching patterns. If -e, --exec, -l, --ls, -p, --print, --print-name, -q, --quiet, or -s is not specified, \fIippfind\fR adds --print to print the service URI of anything it finds. The following expressions are supported:
+.TP 5
+-d regex
+.TP 5
+--domain regex
+True if the domain matches the given regular expression.
+.TP 5
+-e utility [argument ...] ;
+.TP 5
+--exec utility [argument ...] ;
+Executes the specified program if the current result is true. "{foo}" arguments are replaced with the corresponding value - see SUBSTITUTIONS below.
+.TP 5
+--false
+Always false.
+.TP 5
+-l
+.TP 5
+--ls
+Lists attributes returned by Get-Printer-Attributes for IPP printers and traditional \fIfind\fR "-ls" output for HTTP URLs. The result is true if the URI is accessible, false otherwise.
+.TP 5
+--local
+True if the service is local to this computer.
+.TP 5
+-n regex
+.TP 5
+--name regex
+True if the service instance name matches the given regular expression.
+.TP 5
+--path regex
+True if the URI resource path matches the given regular expression.
+.TP 5
+-p
+.TP 5
+--print
+Prints the URI if the result of previous expressions is true. The result is always true.
+.TP 5
+-q
+.TP 5
+--quiet
+Quiet mode - just returns the exit codes below.
+.TP 5
+-r
+.TP 5
+--remote
+True if the service is not local to this computer.
+.TP 5
+-s
+.TP 5
+--print-name
+Prints the service instance name if the result of previous expressions is true. The result is always true.
+.TP 5
+--true
+Always true.
+.TP 5
+-t key
+.TP 5
+--txt key
+True if the TXT record contains the named key.
+.TP 5
+--txt-\fIkey\fR regex
+True if the TXT record contains the named key and matches the given regular
+expression.
+.TP 5
+-u regex
+.TP 5
+--uri regex
+True if the URI matches the given regular expression.
+.PP
+Expressions may also contain modifiers:
+.TP 5
+( expression )
+Group the result of expressions.
+.TP 5
+! expression
+.TP 5
+--not expression
+Unary NOT of the expression.
+.TP 5
+expression expression
+.TP 5
+expression --and expression
+Logical AND of expressions.
+.TP 5
+expression --or expression
+Logical OR of expressions.
+
+.SH SUBSTITUTIONS
+The substitutions for "{foo}" in -e and --exec are:
+.TP 5
+{}
+URI
+.TP 5
+{service_domain}
+Domain name, e.g., "example.com.", "local.", etc.
+.TP 5
+{service_hostname}
+Fully-qualified domain name, e.g., "printer.example.com.", "printer.local.", etc.
+.TP 5
+{service_name}
+Service instance name, e.g., "My Fine Printer".
+.TP 5
+{service_port}
+Port number for server, typically 631 for IPP and 80 for HTTP.
+.TP 5
+{service_regtype}
+DNS-SD registration type, e.g., "_ipp._tcp", "_http._tcp", etc.
+.TP 5
+{service_scheme}
+URI scheme for DNS-SD registration type, e.g., "ipp", "http", etc.
+.TP 5
+{service_uri}
+URI for service, e.g., "ipp://printer.local./ipp/print", "http://printer.local./", etc.
+.TP 5
+{txt_\fIkey\fR}
+Value of TXT record \fIkey\fR (lowercase).
+
+.SH ENVIRONMENT VARIABLES
+When executing a program, \fIippfind\fR sets the following environment variables for the matching service registration:
+.TP 5
+IPPFIND_SERVICE_DOMAIN
+Domain name, e.g., "example.com.", "local.", etc.
+.TP 5
+IPPFIND_SERVICE_HOSTNAME
+Fully-qualified domain name, e.g., "printer.example.com.", "printer.local.", etc.
+.TP 5
+IPPFIND_SERVICE_NAME
+Service instance name, e.g., "My Fine Printer".
+.TP 5
+IPPFIND_SERVICE_PORT
+Port number for server, typically 631 for IPP and 80 for HTTP.
+.TP 5
+IPPFIND_SERVICE_REGTYPE
+DNS-SD registration type, e.g., "_ipp._tcp", "_http._tcp", etc.
+.TP 5
+IPPFIND_SERVICE_SCHEME
+URI scheme for DNS-SD registration type, e.g., "ipp", "http", etc.
+.TP 5
+IPPFIND_SERVICE_URI
+URI for service, e.g., "ipp://printer.local./ipp/print", "http://printer.local./", etc.
+.TP 5
+IPPFIND_TXT_\fIKEY\fR
+Values of TXT record \fIKEY\fR (uppercase).
+
+.SH EXIT CODES
+\fIippfind\fR returns 0 if the result for all processed expressions is true, 1 if the result of any processed expression is false, 2 if browsing or any query or resolution failed, 3 if an undefined option or invalid expression was specified, and 4 if it ran out of memory.
+
+.SH SEE ALSO
+\fIipptool(1)\fR
+
+.SH COPYRIGHT
+Copyright 2013 by Apple Inc.
+.\"
+.\" End of "$Id$".
+.\"
index 69b96a6f4dd68652997280e83d997857bdbb0d3b..9686a1f663ffc0aa948ba000c8203842e0d51e94 100644 (file)
@@ -1,18 +1,23 @@
-ippserver.o: ippserver.c ../cups/cups-private.h ../cups/string-private.h \
+ippfind.o: ippfind.c ../cups/cups-private.h ../cups/string-private.h \
   ../config.h ../cups/debug-private.h ../cups/versioning.h \
-  ../cups/ipp-private.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
-  ../cups/http-private.h ../cups/md5-private.h \
-  ../cups/language-private.h ../cups/transcode.h ../cups/language.h \
-  ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
-  ../cups/ppd-private.h ../cups/ppd.h ../cups/thread-private.h
+  ../cups/array-private.h ../cups/array.h ../cups/ipp-private.h \
+  ../cups/ipp.h ../cups/http.h ../cups/http-private.h \
+  ../cups/md5-private.h ../cups/language-private.h ../cups/transcode.h \
+  ../cups/language.h ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
+  ../cups/pwg.h ../cups/ppd-private.h ../cups/ppd.h \
+  ../cups/thread-private.h
+ippserver.o: ippserver.c ../cups/cups.h ../cups/file.h \
+  ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
+  ../cups/language.h ../cups/pwg.h ../config.h ../cups/string-private.h \
+  ../cups/thread-private.h
 ipptool.o: ipptool.c ../cups/cups-private.h ../cups/string-private.h \
   ../config.h ../cups/debug-private.h ../cups/versioning.h \
-  ../cups/ipp-private.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
-  ../cups/http-private.h ../cups/md5-private.h \
-  ../cups/language-private.h ../cups/transcode.h ../cups/language.h \
-  ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
-  ../cups/ppd-private.h ../cups/ppd.h ../cups/thread-private.h \
-  ../cups/file-private.h
+  ../cups/array-private.h ../cups/array.h ../cups/ipp-private.h \
+  ../cups/ipp.h ../cups/http.h ../cups/http-private.h \
+  ../cups/md5-private.h ../cups/language-private.h ../cups/transcode.h \
+  ../cups/language.h ../cups/pwg-private.h ../cups/cups.h ../cups/file.h \
+  ../cups/pwg.h ../cups/ppd-private.h ../cups/ppd.h \
+  ../cups/thread-private.h ../cups/file-private.h
 xmltotest.o: xmltotest.c ../config.h ../cups/cups.h ../cups/file.h \
   ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h \
-  ../cups/language.h
+  ../cups/language.h ../cups/pwg.h
index c7c5c8e994d6be8f252dd5b5ef45ed90cfa44bf8..c7507d9cb59ca5e38aeb7e03c909611995fb902b 100644 (file)
@@ -53,10 +53,12 @@ TESTFILES   =       \
                        print-job-gzip.test \
                        validate-job.test
 OBJS           =       \
+                       ippfind.o \
                        ippserver.o \
                        ipptool.o \
                        xmltotest.o
 TARGETS                =       \
+                       $(IPPFIND_BIN) \
                        ippserver \
                        ipptool \
                        ipptool-static
@@ -165,6 +167,18 @@ uninstall:
        -$(RMDIR) $(DATADIR)/ipptool
 
 
+#
+# ippfind
+#
+
+ippfind:       ippfind.o ../cups/$(LIBCUPS) ../cups/$(LIBCUPSSTATIC)
+       echo Linking $@...
+       $(CC) $(LDFLAGS) -o $@ ippfind.o $(LIBS)
+       echo Linking $@-static...
+       $(CC) $(LDFLAGS) -o $@-static ippfind.o  ../cups/$(LIBCUPSSTATIC) \
+               $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
 #
 # ippserver
 #
index 73f395c26fc76e93a6a6ce2527b5fa4bc1dbffc7..44223d69ea1729f89c56c2a32608855b519d1f7c 100644 (file)
  *   --true                          - Always true
  *
  *   expression expression
- *   expression --and expression
- *   expression -a expression        - Logical AND.
+ *   expression --and expression     - Logical AND.
  *
- *   expression -o expression
  *   expression --or expression      - Logical OR.
  *
  * The substitutions for {} are:
 
 typedef enum ippfind_exit_e            /* Exit codes */
 {
-  IPPFIND_EXIT_OK = 0,                 /* OK and result is true */
+  IPPFIND_EXIT_TRUE = 0,               /* OK and result is true */
   IPPFIND_EXIT_FALSE,                  /* OK but result is false*/
   IPPFIND_EXIT_BONJOUR,                        /* Browse/resolve failure */
-  IPPFIND_EXIT_SYNTAX                  /* Bad option or syntax error */
+  IPPFIND_EXIT_SYNTAX,                 /* Bad option or syntax error */
+  IPPFIND_EXIT_MEMORY                  /* Out of memory */
 } ippfind_exit_t;
 
 typedef enum ippfind_op_e              /* Operations for expressions */
@@ -184,6 +183,8 @@ typedef enum ippfind_op_e           /* Operations for expressions */
   IPPFIND_OP_OR,                       /* Logical OR of all children */
   IPPFIND_OP_TRUE,                     /* Always true */
   IPPFIND_OP_FALSE,                    /* Always false */
+  IPPFIND_OP_IS_LOCAL,                 /* Is a local service */
+  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_PATH_REGEX,               /* Path matches regular expression */
@@ -210,6 +211,8 @@ typedef struct ippfind_expr_s               /* Expression */
   int          invert;                 /* Invert the result */
   char         *key;                   /* TXT record key */
   regex_t      re;                     /* Regular expression for matching */
+  int          num_args;               /* Number of arguments for exec */
+  char         **args;                 /* Arguments for exec */
 } ippfind_expr_t;
 
 typedef struct ippfind_srv_s           /* Service information */
@@ -231,7 +234,6 @@ typedef struct ippfind_srv_s                /* Service information */
                is_local,               /* Is a local service? */
                is_processed,           /* Did we process the service? */
                is_resolved;            /* Got the resolve data? */
-  time_t       resolve_time;           /* Time we started the resolve */
 } ippfind_srv_t;
 
 
@@ -251,8 +253,8 @@ static AvahiSimplePoll *avahi_poll = NULL;
 static int     address_family = AF_UNSPEC;
                                        /* Address family for LIST */
 static int     bonjour_error = 0;      /* Error browsing/resolving? */
+static double  bonjour_timeout = 1.0;  /* Timeout in seconds */
 static int     ipp_version = 20;       /* IPP version for LIST */
-static double  timeout = 10;           /* Timeout in seconds */
 
 
 /*
@@ -293,11 +295,17 @@ static void               client_callback(AvahiClient *client,
 #endif /* HAVE_AVAHI */
 
 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 ippfind_srv_t   *get_service(cups_array_t *services,
                                     const char *serviceName,
                                     const char *regtype,
                                     const char *replyDomain)
                                     __attribute__((nonnull(1,2,3,4)));
+static double          get_time(void);
+static ippfind_expr_t  *new_expr(ippfind_op_t op, int invert, const char *key,
+                                 const char *regex, char **args);
 #ifdef HAVE_DNSSD
 static void            resolve_callback(DNSServiceRef sdRef,
                                         DNSServiceFlags flags,
@@ -307,7 +315,7 @@ static void         resolve_callback(DNSServiceRef sdRef,
                                         const char *hostTarget, uint16_t port,
                                         uint16_t txtLen,
                                         const unsigned char *txtRecord,
-                                        void *context);
+                                        void *context)
                                         __attribute__((nonnull(1,5,6,9, 10)));
 #elif defined(HAVE_AVAHI)
 static int             poll_callback(struct pollfd *pollfds,
@@ -329,8 +337,6 @@ static void         resolve_callback(AvahiServiceResolver *res,
 static void            set_service_uri(ippfind_srv_t *service);
 static void            show_usage(void) __attribute__((noreturn));
 static void            show_version(void) __attribute__((noreturn));
-static void            unquote(char *dst, const char *src, size_t dstsize)
-                           __attribute__((nonnull(1,2)));
 
 
 /*
@@ -341,9 +347,29 @@ int                                        /* O - Exit status */
 main(int  argc,                                /* I - Number of command-line args */
      char *argv[])                     /* I - Command-line arguments */
 {
-  int          fd;                     /* File descriptor for Bonjour */
-  cups_array_t *services;              /* Service array */
-  ippfind_srv_t        *service;               /* Current service */
+  int                  i,              /* Looping var */
+                       have_output = 0,/* Have output expression */
+                       status = IPPFIND_EXIT_TRUE;
+                                       /* 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 */
+  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 */
 
 
  /*
@@ -353,409 +379,868 @@ main(int  argc,                         /* I - Number of command-line args */
   _cupsSetLocale(argv);
 
  /*
-  * Create an array to track services...
+  * 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);
 
  /*
-  * Start up masters for browsing/resolving...
+  * Parse command-line...
   */
 
-#ifdef HAVE_DNSSD
-  if (DNSServiceCreateConnection(&dnssd_ref) != kDNSServiceErr_NoError)
+  for (i = 1; i < argc; i ++)
   {
-    perror("ERROR: Unable to create service connection");
-    return (IPPFIND_EXIT_BONJOUR);
-  }
+    if (argv[i][0] == '-')
+    {
+      if (argv[i][1] == '-')
+      {
+       /*
+        * Parse --option options...
+        */
+
+        if (!strcmp(argv[i], "--and"))
+        {
+         if (logic != IPPFIND_OP_AND && current && current->prev)
+         {
+          /*
+           * OK, we have more than 1 rule in the current tree level.
+           * Make a new group tree and move the previous rule to it...
+           */
+
+           if ((temp = new_expr(IPPFIND_OP_AND, 0, NULL, NULL, NULL)) == NULL)
+             return (IPPFIND_EXIT_MEMORY);
+
+           temp->child         = current;
+           temp->parent        = current->parent;
+           current->prev->next = temp;
+           temp->prev          = current->prev;
+
+           current->prev   = NULL;
+           current->parent = temp;
+           parent          = temp;
+         }
+         else if (parent)
+           parent->op = IPPFIND_OP_AND;
 
-  fd = DNSServiceRefSockFD(dnssd_ref);
+         logic = IPPFIND_OP_AND;
+         temp  = NULL;
+        }
+        else if (!strcmp(argv[i], "--domain"))
+        {
+          i ++;
+          if (i >= argc)
+            show_usage();
 
-#elif defined(HAVE_AVAHI)
-#endif /* HAVE_DNSSD */
+          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)
+            show_usage();
 
-#if 0
-  int          i;                      /* Looping var */
-  DNSServiceRef        main_ref,               /* Main service reference */
-               ipp_ref;                /* IPP service reference */
-  int          fd;                     /* Main file descriptor */
-  fd_set       input;                  /* Input set for select() */
-  struct timeval timeout;              /* Timeout for select() */
-  cups_array_t *devices;               /* Device array */
-  ippfind_srv_t        *device;                /* Current device */
-  http_t       *http;                  /* Connection to printer */
-  ipp_t                *request,               /* Get-Printer-Attributes request */
-               *response;              /* Get-Printer-Attributes response */
-  ipp_attribute_t *attr;               /* IPP attribute in response */
-  const char   *version,               /* Version supported */
-               *testfile;              /* Test file to use */
-  int          ipponly = 0,            /* Do IPP tests only? */
-               snmponly = 0;           /* Do SNMP walk only? */
+          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 ++;
 
-  for (i = 1; i < argc; i ++)
-    if (!strcmp(argv[i], "snmp"))
-      snmponly = 1;
-    else if (!strcmp(argv[i], "ipp"))
-      ipponly = 1;
-    else
+          if (i >= argc)
+            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);
+        }
+        else if (!strcmp(argv[i], "--help"))
+        {
+          show_usage();
+        }
+        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);
+
+          have_output = 1;
+        }
+        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);
+        }
+        else if (!strcmp(argv[i], "--name"))
+        {
+          i ++;
+          if (i >= argc)
+            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 (logic != IPPFIND_OP_OR && current)
+         {
+          /*
+           * OK, we have two possibilities; either this is the top-level
+           * rule or we have a bunch of AND rules at this level.
+           */
+
+           if (!parent)
+           {
+            /*
+             * This is the top-level rule; we have to group *all* of the AND
+             * rules down a level, as AND has precedence over OR.
+             */
+
+             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;
+
+             expressions = current = temp;
+           }
+           else
+           {
+            /*
+             * This isn't the top rule, so go up one level...
+             */
+
+             current = parent;
+             parent  = current->parent;
+           }
+         }
+
+         logic = IPPFIND_OP_OR;
+         temp  = NULL;
+        }
+        else if (!strcmp(argv[i], "--path"))
+        {
+          i ++;
+          if (i >= argc)
+            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], "--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)
+            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))
+        {
+          const char *key = argv[i] + 5;/* TXT key */
+
+          i ++;
+          if (i >= argc)
+            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)
+            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 (current)
+         {
+           temp->parent  = parent;
+           current->next = temp;
+         }
+         else if (parent)
+           parent->child = temp;
+         else
+           expressions = temp;
+
+         temp->prev = current;
+         current    = temp;
+          invert     = 0;
+          temp       = NULL;
+        }
+      }
+      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 'T' :
+                i ++;
+                if (i >= argc)
+                  show_usage();
+
+                bonjour_timeout = atof(argv[i]);
+                break;
+
+            case 'V' :
+                i ++;
+                if (i >= argc)
+                  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
+                  show_usage();
+                break;
+
+            case 'd' :
+               i ++;
+               if (i >= argc)
+                 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 'l' :
+               i ++;
+               if (i >= argc)
+                 show_usage();
+
+               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)
+                 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)
+                 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)
+                 show_usage();
+
+               if ((temp = new_expr(IPPFIND_OP_URI_REGEX, invert, NULL,
+                                    argv[i], NULL)) == NULL)
+                 return (IPPFIND_EXIT_MEMORY);
+                break;
+
+            default :
+                _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."),
+                                "ippfind", *opt);
+                show_usage();
+                break;
+          }
+
+         if (temp)
+         {
+          /*
+           * Add new expression...
+           */
+
+           if (current)
+           {
+             temp->parent  = parent;
+             current->next = temp;
+           }
+           else if (parent)
+             parent->child = temp;
+           else
+             expressions = temp;
+
+           temp->prev = current;
+           current    = temp;
+           invert     = 0;
+           temp       = NULL;
+         }
+        }
+      }
+    }
+    else if (!strcmp(argv[i], "("))
     {
-      puts("Usage: ./ipp-printers [{ipp | snmp}]");
-      return (1);
+      if ((temp = new_expr(IPPFIND_OP_AND, invert, NULL, NULL, NULL)) == NULL)
+       return (IPPFIND_EXIT_MEMORY);
+
+      if (current)
+      {
+       temp->parent  = current->parent;
+       current->next = temp;
+      }
+      else
+       expressions = temp;
+
+      temp->prev = current;
+      parent     = temp;
+      current    = NULL;
+      invert     = 0;
+      logic      = IPPFIND_OP_AND;
     }
+    else if (!strcmp(argv[i], ")"))
+    {
+      if (!parent)
+      {
+        _cupsLangPuts(stderr, _("ippfind: Missing open parenthesis."));
+        show_usage();
+      }
 
- /*
-  * Create an array to track devices...
-  */
+      current = parent;
+      parent  = current->parent;
 
-  devices = cupsArrayNew((cups_array_func_t)compare_services, NULL);
+      if (!parent)
+        logic = IPPFIND_OP_AND;
+      else
+        logic = parent->op;
+    }
+    else if (!strcmp(argv[i], "!"))
+    {
+      invert = 1;
+    }
+    else
+    {
+     /*
+      * _regtype._tcp[,subtype][.domain]
+      *
+      *   OR
+      *
+      * service-name[._regtype._tcp[.domain]]
+      */
 
- /*
-  * Browse for different kinds of printers...
-  */
+      cupsArrayAdd(searches, argv[i]);
+    }
+  }
+
+  if (parent)
+  {
+    _cupsLangPuts(stderr, _("ippfind: Missing close parenthesis."));
+    show_usage();
+  }
 
-  if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
+  if (cupsArrayCount(searches) == 0)
   {
-    perror("ERROR: Unable to create service connection");
-    return (1);
+   /*
+    * Add an implicit browse for IPP printers ("_ipp._tcp")...
+    */
+
+    cupsArrayAdd(searches, "_ipp._tcp");
   }
 
-  fd = DNSServiceRefSockFD(main_ref);
+  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)
+    {
+      temp->parent  = parent;
+      current->next = temp;
+    }
+    else
+      expressions = temp;
 
-  ipp_ref = main_ref;
-  DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
-                   "_ipp._tcp", NULL, browse_callback, devices);
+    temp->prev = current;
+  }
 
  /*
-  * Loop until we are killed...
+  * Start up browsing/resolving...
   */
 
-  progress();
+#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);
+  }
 
-  for (;;)
+#elif defined(HAVE_AVAHI)
+  if ((avahi_poll = avahi_simple_poll_new()) == NULL)
   {
-    FD_ZERO(&input);
-    FD_SET(fd, &input);
+    _cupsLangPrintError(stderr, _("ippfind: Unable to use Bonjour: %s"),
+                        strerror(errno));
+    return (IPPFIND_EXIT_BONJOUR);
+  }
 
-    timeout.tv_sec  = 2;
-    timeout.tv_usec = 500000;
+  avahi_simple_poll_set_func(avahi_poll, poll_callback, NULL);
 
-    if (select(fd + 1, &input, NULL, NULL, &timeout) <= 0)
-    {
-      time_t curtime = time(NULL);
+  avahi_client = avahi_client_new(avahi_simple_poll_get(avahi_poll),
+                                 0, client_callback, avahi_poll, &err);
+  if (!client)
+  {
+    _cupsLangPrintError(stderr, _("ippfind: Unable to use Bonjour: %s"),
+                        dnssd_error_string(err));
+    return (IPPFIND_EXIT_BONJOUR);
+  }
+#endif /* HAVE_DNSSD */
 
-      for (device = (ippfind_srv_t *)cupsArrayFirst(devices);
-           device;
-          device = (ippfind_srv_t *)cupsArrayNext(devices))
-        if (!device->got_resolve)
-        {
-          if (!device->ref)
-            break;
+  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 */
 
-          if ((curtime - device->resolve_time) > 10)
-          {
-            device->got_resolve = -1;
-           fprintf(stderr, "\rUnable to resolve \"%s\": timeout\n",
-                   device->name);
-           progress();
-         }
-          else
-            break;
-        }
+    strlcpy(buf, search, sizeof(buf));
+    if (buf[0] == '_')
+    {
+      regtype = buf;
+    }
+    else if ((regtype = strstr(buf, "._")) != NULL)
+    {
+      name = buf;
+      *regtype++ = '\0';
+    }
+    else
+    {
+      name    = buf;
+      regtype = "_ipp._tcp";
+    }
 
-      if (!device)
+    for (domain = regtype; *domain; domain ++)
+      if (*domain == '.' && domain[1] != '_')
+      {
+        *domain++ = '\0';
         break;
-    }
+      }
 
-    if (FD_ISSET(fd, &input))
+    if (!*domain)
+      domain = NULL;
+
+    if (name)
     {
      /*
-      * Process results of our browsing...
+      * Resolve the given service instance name, regtype, and domain...
       */
 
-      progress();
-      DNSServiceProcessResult(main_ref);
+      if (!domain)
+        domain = "local.";
+
+      service = get_service(services, 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_get_errno(avahi_client);
+#endif /* HAVE_DNSSD */
     }
     else
     {
      /*
-      * Query any devices we've found...
+      * Browse for services of the given type...
       */
 
-      DNSServiceErrorType      status; /* DNS query status */
-      int                      count;  /* Number of queries */
+#ifdef HAVE_DNSSD
+      DNSServiceRef    ref;            /* Browse reference */
 
+      ref = dnssd_ref;
+      err = DNSServiceBrowse(&ref, kDNSServiceFlagsShareConnection, 0, regtype,
+                             domain, browse_callback, services);
 
-      for (device = (ippfind_srv_t *)cupsArrayFirst(devices), count = 0;
-           device;
-          device = (ippfind_srv_t *)cupsArrayNext(devices))
+      if (!err)
       {
-        if (!device->ref && !device->sent)
-       {
-        /*
-         * Found the device, now get the TXT record(s) for it...
-         */
+       ref = dnssd_ref;
+       err = DNSServiceBrowse(&ref, kDNSServiceFlagsShareConnection,
+                              kDNSServiceInterfaceIndexLocalOnly, regtype,
+                              domain, browse_local_callback, services);
+      }
 
-          if (count < 50)
-         {
-           device->resolve_time = time(NULL);
-           device->ref          = main_ref;
-
-           status = DNSServiceResolve(&(device->ref),
-                                      kDNSServiceFlagsShareConnection,
-                                      0, device->name, device->regtype,
-                                      device->domain, resolve_callback,
-                                      device);
-            if (status != kDNSServiceErr_NoError)
-            {
-             fprintf(stderr, "\rUnable to resolve \"%s\": %d\n",
-                     device->name, status);
-             progress();
-           }
-           else
-             count ++;
-          }
-       }
-       else if (!device->sent && device->got_resolve)
-       {
-        /*
-         * Got the TXT records, now report the device...
-         */
+#elif defined(HAVE_AVAHI)
+      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_get_errno(avahi_client);
+#endif /* HAVE_DNSSD */
+    }
 
-         DNSServiceRefDeallocate(device->ref);
-         device->ref  = 0;
-         device->sent = 1;
-        }
-      }
+    if (err)
+    {
+      _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);
     }
   }
 
-#ifndef DEBUG
-  fprintf(stderr, "\rFound %d printers. Now querying for capabilities...\n",
-          cupsArrayCount(devices));
-#endif /* !DEBUG */
-
-  puts("#!/bin/sh -x");
-  puts("test -d results && rm -rf results");
-  puts("mkdir results");
-  puts("CUPS_DEBUG_LEVEL=6; export CUPS_DEBUG_LEVEL");
-  puts("CUPS_DEBUG_FILTER='^(ipp|http|_ipp|_http|cupsGetResponse|cupsSend|"
-       "cupsWrite|cupsDo).*'; export CUPS_DEBUG_FILTER");
-
-  for (device = (ippfind_srv_t *)cupsArrayFirst(devices);
-       device;
-       device = (ippfind_srv_t *)cupsArrayNext(devices))
+ /*
+  * Process browse/resolve requests...
+  */
+
+  if (bonjour_timeout > 1.0)
+    endtime = get_time() + bonjour_timeout;
+  else
+    endtime = get_time() + 300.0;
+
+  while (get_time() < endtime)
   {
-    if (device->got_resolve <= 0 || device->cups_shared)
+    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;
 
-#ifdef DEBUG
-    fprintf(stderr, "Checking \"%s\" (got_resolve=%d, cups_shared=%d, uri=%s)\n",
-            device->name, device->got_resolve, device->cups_shared, device->uri);
-#else
-    fprintf(stderr, "Checking \"%s\"...\n", device->name);
-#endif /* DEBUG */
+    if (FD_ISSET(fd, &sinput))
+    {
+     /*
+      * Process responses...
+      */
 
-    if ((http = httpConnect(device->host, device->port)) == NULL)
+      DNSServiceProcessResult(dnssd_ref);
+    }
+    else
     {
-      fprintf(stderr, "Failed to connect to \"%s\": %s\n", device->name,
-              cupsLastErrorString());
-      continue;
+     /*
+      * Time to process services...
+      */
+
+      process = 1;
     }
 
-    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
-                 device->uri);
+#elif defined(HAVE_AVAHI)
+    avahi_got_data = 0;
 
-    response = cupsDoRequest(http, request, device->rp);
+    if (avahi_simple_poll_iterate(avahi_poll, 500) > 0)
+    {
+     /*
+      * We've been told to exit the loop.  Perhaps the connection to
+      * Avahi failed.
+      */
 
-    if (cupsLastError() > IPP_OK_SUBST)
-      fprintf(stderr, "Failed to query \"%s\": %s\n", device->name,
-              cupsLastErrorString());
-    else
+      return (IPPFIND_EXIT_BONJOUR);
+    }
+
+    if (!avahi_got_data)
     {
-      if ((attr = ippFindAttribute(response, "ipp-versions-supported",
-                                  IPP_TAG_KEYWORD)) != NULL)
-      {
-       version = attr->values[0].string.text;
+     /*
+      * Time to process services...
+      */
 
-       for (i = 1; i < attr->num_values; i ++)
-         if (strcmp(attr->values[i].string.text, version) > 0)
-           version = attr->values[i].string.text;
-      }
-      else
-       version = "1.0";
+      process = 1;
+    }
+#endif /* HAVE_DNSSD */
 
-      testfile = NULL;
+    if (process)
+    {
+     /*
+      * Process any services that we have found...
+      */
 
-      if ((attr = ippFindAttribute(response, "document-format-supported",
-                                   IPP_TAG_MIMETYPE)) != NULL)
+      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))
       {
-       /*
-        * Figure out the test file for printing, preferring PDF and PostScript
-        * over JPEG and plain text...
-        */
+        if (service->is_processed)
+          processed ++;
 
-        for (i = 0; i < attr->num_values; i ++)
-        {
-          if (!strcasecmp(attr->values[i].string.text, "application/pdf"))
-          {
-            testfile = "testfile.pdf";
-            break;
-          }
-          else if (!strcasecmp(attr->values[i].string.text,
-                               "application/postscript"))
-            testfile = "testfile.ps";
-          else if (!strcasecmp(attr->values[i].string.text, "image/jpeg") &&
-                   !testfile)
-            testfile = "testfile.jpg";
-          else if (!strcasecmp(attr->values[i].string.text, "text/plain") &&
-                   !testfile)
-            testfile = "testfile.txt";
-          else if (!strcasecmp(attr->values[i].string.text,
-                               "application/vnd.hp-PCL") && !testfile)
-            testfile = "testfile.pcl";
-        }
+        if (service->is_resolved)
+          resolved ++;
 
-        if (!testfile)
+        if (!service->ref && !service->is_resolved)
         {
-          fprintf(stderr,
-                  "Printer \"%s\" reports the following IPP file formats:\n",
-                  device->name);
-          for (i = 0; i < attr->num_values; i ++)
-            fprintf(stderr, "    \"%s\"\n", attr->values[i].string.text);
-        }
-      }
-
-      if (!testfile && device->pdl)
-      {
-       char    *pdl,                   /* Copy of pdl string */
-               *start, *end;           /* Pointers into pdl string */
+         /*
+          * 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);
 
-        pdl = strdup(device->pdl);
-       for (start = device->pdl; start && *start; start = end)
-       {
-         if ((end = strchr(start, ',')) != NULL)
-           *end++ = '\0';
+#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_get_errno(avahi_client);
+#endif /* HAVE_DNSSD */
 
-         if (!strcasecmp(start, "application/pdf"))
-         {
-           testfile = "testfile.pdf";
-           break;
-         }
-         else if (!strcasecmp(start, "application/postscript"))
-           testfile = "testfile.ps";
-         else if (!strcasecmp(start, "image/jpeg") && !testfile)
-           testfile = "testfile.jpg";
-         else if (!strcasecmp(start, "text/plain") && !testfile)
-           testfile = "testfile.txt";
-         else if (!strcasecmp(start, "application/vnd.hp-PCL") && !testfile)
-           testfile = "testfile.pcl";
-       }
-       free(pdl);
+           if (err)
+           {
+             _cupsLangPrintf(stderr,
+                             _("ippfind: Unable to browse or resolve: %s"),
+                             dnssd_error_string(err));
+             return (IPPFIND_EXIT_BONJOUR);
+           }
 
-        if (testfile)
-        {
-         fprintf(stderr,
-                 "Using \"%s\" for printer \"%s\" based on TXT record pdl "
-                 "info.\n", testfile, device->name);
+           active ++;
+          }
         }
-        else
+        else if (service->is_resolved && !service->is_processed)
         {
-         fprintf(stderr,
-                 "Printer \"%s\" reports the following TXT file formats:\n",
-                 device->name);
-         fprintf(stderr, "    \"%s\"\n", device->pdl);
-       }
-      }
-
-      if (!device->ty &&
-         (attr = ippFindAttribute(response, "printer-make-and-model",
-                                  IPP_TAG_TEXT)) != NULL)
-       device->ty = strdup(attr->values[0].string.text);
-
-      if (strcmp(version, "1.0") && testfile && device->ty)
-      {
-       char            filename[1024], /* Filename */
-                       *fileptr;       /* Pointer into filename */
-       const char      *typtr;         /* Pointer into ty */
-
-        if (!strncasecmp(device->ty, "DeskJet", 7) ||
-            !strncasecmp(device->ty, "DesignJet", 9) ||
-            !strncasecmp(device->ty, "OfficeJet", 9) ||
-            !strncasecmp(device->ty, "Photosmart", 10))
-          strlcpy(filename, "HP_", sizeof(filename));
-        else
-          filename[0] = '\0';
-
-       fileptr = filename + strlen(filename);
+        /*
+         * Resolved, not process this service against the expressions...
+         */
 
-        if (!strncasecmp(device->ty, "Lexmark International Lexmark", 29))
-          typtr = device->ty + 22;
-        else
-          typtr = device->ty;
+          if (service->ref)
+          {
+#ifdef HAVE_DNSSD
+           DNSServiceRefDeallocate(service->ref);
+#else
+            avahi_record_browser_free(service->ref);
+#endif /* HAVE_DNSSD */
 
-       while (*typtr && fileptr < (filename + sizeof(filename) - 1))
-       {
-         if (isalnum(*typtr & 255) || *typtr == '-')
-           *fileptr++ = *typtr++;
-         else
-         {
-           *fileptr++ = '_';
-           typtr++;
+           service->ref = NULL;
          }
-       }
-
-       *fileptr = '\0';
 
-        printf("# %s\n", device->name);
-        printf("echo \"Testing %s...\"\n", device->name);
+          if (!eval_expr(service, expressions))
+            status = IPPFIND_EXIT_FALSE;
 
-        if (!ipponly)
-        {
-         printf("echo \"snmpwalk -c public -v 1 -Cc %s 1.3.6.1.2.1.25 "
-                "1.3.6.1.2.1.43 1.3.6.1.4.1.2699.1\" > results/%s.snmpwalk\n",
-                device->host, filename);
-         printf("snmpwalk -c public -v 1 -Cc %s 1.3.6.1.2.1.25 "
-                "1.3.6.1.2.1.43 1.3.6.1.4.1.2699.1 | "
-                "tee -a results/%s.snmpwalk\n",
-                device->host, filename);
+          service->is_processed = 1;
         }
+        else if (service->ref)
+          active ++;
+      }
 
-        if (!snmponly)
-        {
-         printf("echo \"./ipptool-static -tIf %s -T 30 -d NOPRINT=1 -V %s %s "
-                "ipp-%s.test\" > results/%s.log\n", testfile, version,
-                device->uri, version, filename);
-         printf("CUPS_DEBUG_LOG=results/%s.debug_log "
-                "./ipptool-static -tIf %s -T 30 -d NOPRINT=1 -V %s %s "
-                "ipp-%s.test | tee -a results/%s.log\n", filename,
-                testfile, version, device->uri,
-                version, filename);
-        }
+     /*
+      * If we have processed all services we have discovered, then we are done.
+      */
 
-       puts("");
-      }
-      else if (!device->ty)
-       fprintf(stderr,
-               "Ignoring \"%s\" since it doesn't provide a make and model.\n",
-               device->name);
-      else if (!testfile)
-       fprintf(stderr,
-               "Ignoring \"%s\" since it does not support a common format.\n",
-               device->name);
-      else
-       fprintf(stderr, "Ignoring \"%s\" since it only supports IPP/1.0.\n",
-               device->name);
+      if (processed == cupsArrayCount(services) && bonjour_timeout <= 1.0)
+        break;
     }
-
-    ippDelete(response);
-    httpClose(http);
   }
 
-  return (0);
-#endif /* 0 */
+  if (bonjour_error)
+    return (IPPFIND_EXIT_BONJOUR);
+  else
+    return (status);
 }
 
 
@@ -920,6 +1405,137 @@ compare_services(ippfind_srv_t *a,       /* I - First device */
 }
 
 
+/*
+ * 'dnssd_error_string()' - Return an error string for an error code.
+ */
+
+static const char *                    /* O - Error message */
+dnssd_error_string(int error)          /* I - Error number */
+{
+#  ifdef HAVE_DNSSD
+  switch (error)
+  {
+    case kDNSServiceErr_NoError :
+        return ("OK.");
+
+    default :
+    case kDNSServiceErr_Unknown :
+        return ("Unknown error.");
+
+    case kDNSServiceErr_NoSuchName :
+        return ("Service not found.");
+
+    case kDNSServiceErr_NoMemory :
+        return ("Out of memory.");
+
+    case kDNSServiceErr_BadParam :
+        return ("Bad parameter.");
+
+    case kDNSServiceErr_BadReference :
+        return ("Bad service reference.");
+
+    case kDNSServiceErr_BadState :
+        return ("Bad state.");
+
+    case kDNSServiceErr_BadFlags :
+        return ("Bad flags.");
+
+    case kDNSServiceErr_Unsupported :
+        return ("Unsupported.");
+
+    case kDNSServiceErr_NotInitialized :
+        return ("Not initialized.");
+
+    case kDNSServiceErr_AlreadyRegistered :
+        return ("Already registered.");
+
+    case kDNSServiceErr_NameConflict :
+        return ("Name conflict.");
+
+    case kDNSServiceErr_Invalid :
+        return ("Invalid name.");
+
+    case kDNSServiceErr_Firewall :
+        return ("Firewall prevents registration.");
+
+    case kDNSServiceErr_Incompatible :
+        return ("Client library incompatible.");
+
+    case kDNSServiceErr_BadInterfaceIndex :
+        return ("Bad interface index.");
+
+    case kDNSServiceErr_Refused :
+        return ("Server prevents registration.");
+
+    case kDNSServiceErr_NoSuchRecord :
+        return ("Record not found.");
+
+    case kDNSServiceErr_NoAuth :
+        return ("Authentication required.");
+
+    case kDNSServiceErr_NoSuchKey :
+        return ("Encryption key not found.");
+
+    case kDNSServiceErr_NATTraversal :
+        return ("Unable to traverse NAT boundary.");
+
+    case kDNSServiceErr_DoubleNAT :
+        return ("Unable to traverse double-NAT boundary.");
+
+    case kDNSServiceErr_BadTime :
+        return ("Bad system time.");
+
+    case kDNSServiceErr_BadSig :
+        return ("Bad signature.");
+
+    case kDNSServiceErr_BadKey :
+        return ("Bad encryption key.");
+
+    case kDNSServiceErr_Transient :
+        return ("Transient error occurred - please try again.");
+
+    case kDNSServiceErr_ServiceNotRunning :
+        return ("Server not running.");
+
+    case kDNSServiceErr_NATPortMappingUnsupported :
+        return ("NAT doesn't support NAT-PMP or UPnP.");
+
+    case kDNSServiceErr_NATPortMappingDisabled :
+        return ("NAT supports NAT-PNP or UPnP but it is disabled.");
+
+    case kDNSServiceErr_NoRouter :
+        return ("No Internet/default router configured.");
+
+    case kDNSServiceErr_PollingMode :
+        return ("Service polling mode error.");
+
+    case kDNSServiceErr_Timeout :
+        return ("Service timeout.");
+  }
+
+#  elif defined(HAVE_AVAHI)
+  return (avahi_strerror(error));
+#  endif /* HAVE_DNSSD */
+}
+
+
+/*
+ * 'eval_expr()' - Evaluate the expressions against the specified service.
+ *
+ * Returns 1 for true and 0 for false.
+ */
+
+static int                             /* O - Result of evaluation */
+eval_expr(ippfind_srv_t  *service,     /* I - Service */
+         ippfind_expr_t *expressions)  /* I - Expressions */
+{
+  (void)expressions;
+
+  puts(service->uri);
+  return (1);
+}
+
+
 /*
  * 'get_service()' - Create or update a device.
  */
@@ -980,6 +1596,83 @@ get_service(cups_array_t *services,       /* I - Service array */
 }
 
 
+/*
+ * 'get_time()' - Get the current time-of-day in seconds.
+ */
+
+static double
+get_time(void)
+{
+#ifdef WIN32
+  struct _timeb curtime;               /* Current Windows time */
+
+  _ftime(&curtime);
+
+  return (curtime.time + 0.001 * curtime.millitm);
+
+#else
+  struct timeval       curtime;        /* Current UNIX time */
+
+  if (gettimeofday(&curtime, NULL))
+    return (0.0);
+  else
+    return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'new_expr()' - Create a new expression.
+ */
+
+static ippfind_expr_t *                        /* O - New expression */
+new_expr(ippfind_op_t op,              /* I - Operation */
+         int          invert,          /* I - Invert result? */
+         const char   *key,            /* I - TXT key */
+        const char   *regex,           /* I - Regular expression */
+        char         **args)           /* I - Pointer to argument strings */
+{
+  ippfind_expr_t       *temp;          /* New expression */
+
+
+  if ((temp = calloc(1, sizeof(ippfind_expr_t))) == NULL)
+    return (NULL);
+
+  temp->op = op;
+  temp->invert = invert;
+  temp->key    = (char *)key;
+
+  if (regex)
+  {
+    int err = regcomp(&(temp->re), regex, REG_NOSUB | REG_EXTENDED);
+
+    if (err)
+    {
+      char     message[256];           /* Error message */
+
+      regerror(err, &(temp->re), message, sizeof(message));
+      _cupsLangPrintf(stderr, _("ippfind: Bad regular expression: %s"),
+                      message);
+      exit(IPPFIND_EXIT_SYNTAX);
+    }
+  }
+
+  if (args)
+  {
+    int        num_args;                       /* Number of arguments */
+
+    for (num_args = 1; args[num_args]; num_args ++)
+      if (!strcmp(args[num_args], ";"))
+        break;
+
+     temp->args = malloc(num_args * sizeof(char *));
+     memcpy(temp->args, args, num_args * sizeof(char *));
+  }
+
+  return (temp);
+}
+
+
 #ifdef HAVE_AVAHI
 /*
  * 'poll_callback()' - Wait for input on the specified file descriptors.
@@ -1045,7 +1738,12 @@ resolve_callback(
   */
 
   if (errorCode != kDNSServiceErr_NoError)
+  {
+    _cupsLangPrintf(stderr, _("ippfind: Unable to browse or resolve: %s"),
+                   dnssd_error_string(errorCode));
+    bonjour_error = 1;
     return;
+  }
 
   service->is_resolved = 1;
   service->host        = strdup(hostTarget);
@@ -1198,10 +1896,10 @@ set_service_uri(ippfind_srv_t *service) /* I - Service */
     path = "/";
 
   if (*path == '/')
-    httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(URI), scheme, NULL,
+    httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL,
                     service->host, service->port, path);
   else
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(URI), scheme, NULL,
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL,
                      service->host, service->port, "/%s", path);
 
   service->uri = strdup(uri);
@@ -1209,11 +1907,11 @@ set_service_uri(ippfind_srv_t *service) /* I - Service */
 
 
 /*
- * 'usage()' - Show program usage.
+ * 'show_usage()' - Show program usage.
  */
 
 static void
-usage(void)
+show_usage(void)
 {
   _cupsLangPuts(stderr, _("Usage: ippfind [options] regtype[,subtype]"
                           "[.domain.] ... [expression]\n"
@@ -1237,33 +1935,63 @@ usage(void)
   _cupsLangPuts(stderr, _("  -e utility [argument ...] ;\n"
                           "                          Execute program if true."));
   _cupsLangPuts(stderr, _("  -l                      List attributes."));
-  _cupsLangPuts(stderr, _("  --local                 True if service is local."));
   _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  --path regex            Match resource path to regular expression."));
   _cupsLangPuts(stderr, _("  -p                      Print URI if true."));
   _cupsLangPuts(stderr, _("  -q                      Quietly report match via exit code."));
   _cupsLangPuts(stderr, _("  -r                      True if service is remote."));
-
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -n regex                Match service name to regular expression."));
-  _cupsLangPuts(stderr, _("  -d name=value           Set named variable to "
-                          "value."));
-  _cupsLangPuts(stderr, _("  -f filename             Set default request "
-                          "filename."));
-  _cupsLangPuts(stderr, _("  -i seconds              Repeat the last file with "
-                          "the given time interval."));
-  _cupsLangPuts(stderr, _("  -n count                Repeat the last file the "
-                          "given number of times."));
-  _cupsLangPuts(stderr, _("  -q                      Run silently."));
-  _cupsLangPuts(stderr, _("  -t                      Produce a test report."));
-  _cupsLangPuts(stderr, _("  -v                      Be verbose."));
-
-  exit(IPPFIND_EXIT_OK);
+  _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, _("  --domain regex          Match domain to regular expression."));
+  _cupsLangPuts(stderr, _("  --exec utility [argument ...] ;\n"
+                          "                          Execute program if true."));
+  _cupsLangPuts(stderr, _("  --ls                    List attributes."));
+  _cupsLangPuts(stderr, _("  --local                 True if service is local."));
+  _cupsLangPuts(stderr, _("  --name regex            Match service name to regular expression."));
+  _cupsLangPuts(stderr, _("  --path regex            Match resource path to regular expression."));
+  _cupsLangPuts(stderr, _("  --print                 Print URI if true."));
+  _cupsLangPuts(stderr, _("  --print-name            Print service name if true."));
+  _cupsLangPuts(stderr, _("  --quiet                 Quietly report match via exit code."));
+  _cupsLangPuts(stderr, _("  --remote                True if service is remote."));
+  _cupsLangPuts(stderr, _("  --txt key               True if the TXT record contains the key."));
+  _cupsLangPuts(stderr, _("  --txt-* regex           Match TXT record key to regular expression."));
+  _cupsLangPuts(stderr, _("  --uri regex             Match URI to regular expression."));
+  _cupsLangPuts(stderr, "");
+  _cupsLangPuts(stderr, _("Modifiers:"));
+  _cupsLangPuts(stderr, _("  ( expressions )         Group expressions."));
+  _cupsLangPuts(stderr, _("  ! expression            Unary NOT of expression."));
+  _cupsLangPuts(stderr, _("  --not expression        Unary NOT of expression."));
+  _cupsLangPuts(stderr, _("  --false                 Always false."));
+  _cupsLangPuts(stderr, _("  --true                  Always true."));
+  _cupsLangPuts(stderr, _("  expression expression   Logical AND."));
+  _cupsLangPuts(stderr, _("  expression --and expression\n"
+                          "                          Logical AND."));
+  _cupsLangPuts(stderr, _("  expression --or expression\n"
+                          "                          Logical OR."));
+  _cupsLangPuts(stderr, "");
+  _cupsLangPuts(stderr, _("Substitutions:"));
+  _cupsLangPuts(stderr, _("  {}                      URI"));
+  _cupsLangPuts(stderr, _("  {service_domain}        Domain name"));
+  _cupsLangPuts(stderr, _("  {service_hostname}      Fully-qualified domain name"));
+  _cupsLangPuts(stderr, _("  {service_name}          Service instance name"));
+  _cupsLangPuts(stderr, _("  {service_port}          Port number"));
+  _cupsLangPuts(stderr, _("  {service_regtype}       DNS-SD registration type"));
+  _cupsLangPuts(stderr, _("  {service_scheme}        URI scheme"));
+  _cupsLangPuts(stderr, _("  {service_uri}           URI"));
+  _cupsLangPuts(stderr, _("  {txt_*}                 Value of TXT record key"));
+  _cupsLangPuts(stderr, "");
+  _cupsLangPuts(stderr, _("Environment Variables:"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_DOMAIN  Domain name"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_HOSTNAME\n"
+                          "                          Fully-qualified domain name"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_NAME    Service instance name"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_PORT    Port number"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_REGTYPE DNS-SD registration type"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_SCHEME  URI scheme"));
+  _cupsLangPuts(stderr, _("  IPPFIND_SERVICE_URI     URI"));
+  _cupsLangPuts(stderr, _("  IPPFIND_TXT_*           Value of TXT record key"));
+
+  exit(IPPFIND_EXIT_TRUE);
 }
 
 
@@ -1276,41 +2004,7 @@ show_version(void)
 {
   _cupsLangPuts(stderr, CUPS_SVERSION);
 
-  exit(IPPFIND_EXIT_OK);
-}
-
-
-/*
- * 'unquote()' - Unquote a name string.
- */
-
-static void
-unquote(char       *dst,               /* I - Destination buffer */
-        const char *src,               /* I - Source string */
-       size_t     dstsize)             /* I - Size of destination buffer */
-{
-  char *dstend = dst + dstsize - 1;    /* End of destination buffer */
-
-
-  while (*src && dst < dstend)
-  {
-    if (*src == '\\')
-    {
-      src ++;
-      if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
-          isdigit(src[2] & 255))
-      {
-        *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
-       src += 3;
-      }
-      else
-        *dst++ = *src++;
-    }
-    else
-      *dst++ = *src ++;
-  }
-
-  *dst = '\0';
+  exit(IPPFIND_EXIT_TRUE);
 }
 
 
index 18b7b9051abd1b32a72efe5cfa9e64a8d6a24f96..656503a5a324592c003a52bc0e7dd7413c0791d2 100755 (executable)
@@ -100,6 +100,7 @@ case "$testtype" in
                nprinters2=0
                pjobs=0
                pprinters=0
+               loglevel="debug2"
                ;;
        2)
                echo "Running the medium tests (2)"
@@ -107,6 +108,7 @@ case "$testtype" in
                nprinters2=20
                pjobs=20
                pprinters=10
+               loglevel="debug"
                ;;
        3)
                echo "Running the extreme tests (3)"
@@ -114,6 +116,7 @@ case "$testtype" in
                nprinters2=1000
                pjobs=100
                pprinters=50
+               loglevel="debug"
                ;;
        4)
                echo "Running the torture tests (4)"
@@ -121,6 +124,7 @@ case "$testtype" in
                nprinters2=20000
                pjobs=200
                pprinters=100
+               loglevel="debug"
                ;;
        *)
                echo "Running the timid tests (1)"
@@ -128,6 +132,7 @@ case "$testtype" in
                nprinters2=0
                pjobs=10
                pprinters=0
+               loglevel="debug2"
                ;;
 esac
 
@@ -397,7 +402,7 @@ PassEnv DYLD_INSERT_LIBRARIES
 MaxSubscriptions 3
 MaxLogSize 0
 AccessLogLevel actions
-LogLevel debug2
+LogLevel $loglevel
 LogTimeFormat usecs
 PreserveJobHistory Yes
 PreserveJobFiles No