4 * Utility to find IPP printers via Bonjour/DNS-SD and optionally run
5 * commands such as IPP and Bonjour conformance tests. This tool is
6 * inspired by the UNIX "find" command, thus its name.
8 * Copyright 2008-2013 by Apple Inc.
10 * These coded instructions, statements, and computer programs are the
11 * property of Apple Inc. and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
13 * which should have been included with this file. If this file is
14 * file is missing or damaged, see the license at "http://www.cups.org/".
16 * This file is subject to the Apple OS-Developed Software exception.
20 * ./ippfind [options] regtype[,subtype][.domain.] ... [expression]
21 * ./ippfind [options] name[.regtype[.domain.]] ... [expression]
25 * Supported regtypes are:
27 * _http._tcp - HTTP (RFC 2616)
28 * _https._tcp - HTTPS (RFC 2818)
29 * _ipp._tcp - IPP (RFC 2911)
30 * _ipps._tcp - IPPS (pending)
31 * _printer._tcp - LPD (RFC 1179)
35 * 0 if result for all processed expressions is true
36 * 1 if result of any processed expression is false
37 * 2 if browsing or any query or resolution failed
38 * 3 if an undefined option or invalid expression was specified
42 * --help - Show program help
43 * --version - Show program version
44 * -4 - Use IPv4 when listing
45 * -6 - Use IPv6 when listing
46 * -T seconds - Specify browse timeout (default 10
48 * -V version - Specify IPP version (1.1, 2.0, 2.1, 2.2)
50 * "expression" is any of the following:
53 * --domain regex - True if the domain matches the given
56 * -e utility [argument ...] ;
57 * --exec utility [argument ...] ; - Executes the specified program; "{}"
58 * does a substitution (see below)
61 * --ls - Lists attributes returned by
62 * Get-Printer-Attributes for IPP printers,
63 * ???? of HEAD request for HTTP URLs)
64 * True if resource is accessible, false
67 * --local - True if the service is local to this
71 * --name regex - True if the name matches the given
74 * --path regex - True if the URI resource path matches
75 * the given regular expression.
78 * --print - Prints the URI of found printers (always
79 * true, default if -e, -l, -p, -q, or -s
83 * --quiet - Quiet mode (just return exit code)
86 * --remote - True if the service is not local to this
90 * --print-name - Prints the service name of found
94 * --txt key - True if the TXT record contains the
97 * --txt-* regex - True if the TXT record contains the
98 * named key and matches the given regular
102 * --uri regex - True if the URI matches the given
103 * regular expression.
105 * Expressions may also contain modifiers:
107 * ( expression ) - Group the result of expressions.
110 * --not expression - Unary NOT
112 * --false - Always false
113 * --true - Always true
115 * expression expression
116 * expression --and expression - Logical AND.
118 * expression --or expression - Logical OR.
120 * The substitutions for {} are:
123 * {service_domain} - Domain name
124 * {service_hostname} - FQDN
125 * {service_name} - Service name
126 * {service_port} - Port number
127 * {service_regtype} - DNS-SD registration type
128 * {service_scheme} - URI scheme for DNS-SD registration type
129 * {service_uri} - URI
130 * {txt_*} - Value of TXT record key
132 * These variables are also set in the environment for executed programs:
134 * IPPFIND_SERVICE_DOMAIN - Domain name
135 * IPPFIND_SERVICE_HOSTNAME - FQDN
136 * IPPFIND_SERVICE_NAME - Service name
137 * IPPFIND_SERVICE_PORT - Port number
138 * IPPFIND_SERVICE_REGTYPE - DNS-SD registration type
139 * IPPFIND_SERVICE_SCHEME - URI scheme for DNS-SD registration type
140 * IPPFIND_SERVICE_URI - URI
141 * IPPFIND_TXT_* - Values of TXT record key (uppercase)
148 * Include necessary headers.
151 #include <cups/cups-private.h>
155 #elif defined(HAVE_AVAHI)
156 # include <avahi-client/client.h>
157 # include <avahi-client/lookup.h>
158 # include <avahi-common/simple-watch.h>
159 # include <avahi-common/domain.h>
160 # include <avahi-common/error.h>
161 # include <avahi-common/malloc.h>
162 # define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
163 #endif /* HAVE_DNSSD */
170 typedef enum ippfind_exit_e
/* Exit codes */
172 IPPFIND_EXIT_TRUE
= 0, /* OK and result is true */
173 IPPFIND_EXIT_FALSE
, /* OK but result is false*/
174 IPPFIND_EXIT_BONJOUR
, /* Browse/resolve failure */
175 IPPFIND_EXIT_SYNTAX
, /* Bad option or syntax error */
176 IPPFIND_EXIT_MEMORY
/* Out of memory */
179 typedef enum ippfind_op_e
/* Operations for expressions */
181 IPPFIND_OP_NONE
, /* No operation */
182 IPPFIND_OP_AND
, /* Logical AND of all children */
183 IPPFIND_OP_OR
, /* Logical OR of all children */
184 IPPFIND_OP_TRUE
, /* Always true */
185 IPPFIND_OP_FALSE
, /* Always false */
186 IPPFIND_OP_IS_LOCAL
, /* Is a local service */
187 IPPFIND_OP_IS_REMOTE
, /* Is a remote service */
188 IPPFIND_OP_DOMAIN_REGEX
, /* Domain matches regular expression */
189 IPPFIND_OP_NAME_REGEX
, /* Name matches regular expression */
190 IPPFIND_OP_PATH_REGEX
, /* Path matches regular expression */
191 IPPFIND_OP_TXT_EXISTS
, /* TXT record key exists */
192 IPPFIND_OP_TXT_REGEX
, /* TXT record key matches regular expression */
193 IPPFIND_OP_URI_REGEX
, /* URI matches regular expression */
195 IPPFIND_OP_OUTPUT
= 100, /* Output operations */
196 IPPFIND_OP_EXEC
, /* Execute when true */
197 IPPFIND_OP_LIST
, /* List when true */
198 IPPFIND_OP_PRINT_NAME
, /* Print URI when true */
199 IPPFIND_OP_PRINT_URI
, /* Print name when true */
200 IPPFIND_OP_QUIET
, /* No output when true */
203 typedef struct ippfind_expr_s
/* Expression */
205 struct ippfind_expr_s
206 *prev
, /* Previous expression */
207 *next
, /* Next expression */
208 *parent
, /* Parent expressions */
209 *child
; /* Child expressions */
210 ippfind_op_t op
; /* Operation code (see above) */
211 int invert
; /* Invert the result */
212 char *key
; /* TXT record key */
213 regex_t re
; /* Regular expression for matching */
214 int num_args
; /* Number of arguments for exec */
215 char **args
; /* Arguments for exec */
218 typedef struct ippfind_srv_s
/* Service information */
221 DNSServiceRef ref
; /* Service reference for query */
222 #elif defined(HAVE_AVAHI)
223 AvahiServiceResolver
*ref
; /* Resolver */
224 #endif /* HAVE_DNSSD */
225 char *name
, /* Service name */
226 *domain
, /* Domain name */
227 *regtype
, /* Registration type */
228 *fullName
, /* Full name */
229 *host
, /* Hostname */
231 int num_txt
; /* Number of TXT record keys */
232 cups_option_t
*txt
; /* TXT record keys */
233 int port
, /* Port number */
234 is_local
, /* Is a local service? */
235 is_processed
, /* Did we process the service? */
236 is_resolved
; /* Got the resolve data? */
245 static DNSServiceRef dnssd_ref
; /* Master service reference */
246 #elif defined(HAVE_AVAHI)
247 static AvahiClient
*avahi_client
= NULL
;/* Client information */
248 static int avahi_got_data
= 0; /* Got data from poll? */
249 static AvahiSimplePoll
*avahi_poll
= NULL
;
250 /* Poll information */
251 #endif /* HAVE_DNSSD */
253 static int address_family
= AF_UNSPEC
;
254 /* Address family for LIST */
255 static int bonjour_error
= 0; /* Error browsing/resolving? */
256 static double bonjour_timeout
= 1.0; /* Timeout in seconds */
257 static int ipp_version
= 20; /* IPP version for LIST */
265 static void browse_callback(DNSServiceRef sdRef
,
266 DNSServiceFlags flags
,
267 uint32_t interfaceIndex
,
268 DNSServiceErrorType errorCode
,
269 const char *serviceName
,
271 const char *replyDomain
, void *context
)
272 __attribute__((nonnull(1,5,6,7,8)));
273 static void browse_local_callback(DNSServiceRef sdRef
,
274 DNSServiceFlags flags
,
275 uint32_t interfaceIndex
,
276 DNSServiceErrorType errorCode
,
277 const char *serviceName
,
279 const char *replyDomain
,
281 __attribute__((nonnull(1,5,6,7,8)));
282 #elif defined(HAVE_AVAHI)
283 static void browse_callback(AvahiServiceBrowser
*browser
,
284 AvahiIfIndex interface
,
285 AvahiProtocol protocol
,
286 AvahiBrowserEvent event
,
287 const char *serviceName
,
289 const char *replyDomain
,
290 AvahiLookupResultFlags flags
,
292 static void client_callback(AvahiClient
*client
,
293 AvahiClientState state
,
295 #endif /* HAVE_AVAHI */
297 static int compare_services(ippfind_srv_t
*a
, ippfind_srv_t
*b
);
298 static const char *dnssd_error_string(int error
);
299 static int eval_expr(ippfind_srv_t
*service
,
300 ippfind_expr_t
*expressions
);
301 static ippfind_srv_t
*get_service(cups_array_t
*services
,
302 const char *serviceName
,
304 const char *replyDomain
)
305 __attribute__((nonnull(1,2,3,4)));
306 static double get_time(void);
307 static ippfind_expr_t
*new_expr(ippfind_op_t op
, int invert
, const char *key
,
308 const char *regex
, char **args
);
310 static void resolve_callback(DNSServiceRef sdRef
,
311 DNSServiceFlags flags
,
312 uint32_t interfaceIndex
,
313 DNSServiceErrorType errorCode
,
314 const char *fullName
,
315 const char *hostTarget
, uint16_t port
,
317 const unsigned char *txtRecord
,
319 __attribute__((nonnull(1,5,6,9, 10)));
320 #elif defined(HAVE_AVAHI)
321 static int poll_callback(struct pollfd
*pollfds
,
322 unsigned int num_pollfds
, int timeout
,
324 static void resolve_callback(AvahiServiceResolver
*res
,
325 AvahiIfIndex interface
,
326 AvahiProtocol protocol
,
327 AvahiBrowserEvent event
,
328 const char *serviceName
,
330 const char *replyDomain
,
331 const char *host_name
,
333 AvahiStringList
*txt
,
334 AvahiLookupResultFlags flags
,
336 #endif /* HAVE_DNSSD */
337 static void set_service_uri(ippfind_srv_t
*service
);
338 static void show_usage(void) __attribute__((noreturn
));
339 static void show_version(void) __attribute__((noreturn
));
343 * 'main()' - Browse for printers.
346 int /* O - Exit status */
347 main(int argc
, /* I - Number of command-line args */
348 char *argv
[]) /* I - Command-line arguments */
350 int i
, /* Looping var */
351 have_output
= 0,/* Have output expression */
352 status
= IPPFIND_EXIT_TRUE
;
354 const char *opt
, /* Option character */
355 *search
; /* Current browse/resolve string */
356 cups_array_t
*searches
; /* Things to browse/resolve */
357 cups_array_t
*services
; /* Service array */
358 ippfind_srv_t
*service
; /* Current service */
359 ippfind_expr_t
*expressions
= NULL
,
360 /* Expression tree */
361 *temp
= NULL
, /* New expression */
362 *parent
= NULL
, /* Parent expression */
363 *current
= NULL
;/* Current expression */
364 ippfind_op_t logic
= IPPFIND_OP_AND
;
365 /* Logic for next expression */
366 int invert
= 0; /* Invert expression? */
367 int err
; /* DNS-SD error */
369 fd_set sinput
; /* Input set for select() */
370 struct timeval stimeout
; /* Timeout for select() */
371 #endif /* HAVE_DNSSD */
372 double endtime
; /* End time */
376 * Initialize the locale...
379 _cupsSetLocale(argv
);
382 * Create arrays to track services and things we want to browse/resolve...
385 searches
= cupsArrayNew(NULL
, NULL
);
386 services
= cupsArrayNew((cups_array_func_t
)compare_services
, NULL
);
389 * Parse command-line...
392 for (i
= 1; i
< argc
; i
++)
394 if (argv
[i
][0] == '-')
396 if (argv
[i
][1] == '-')
399 * Parse --option options...
402 if (!strcmp(argv
[i
], "--and"))
404 if (logic
!= IPPFIND_OP_AND
&& current
&& current
->prev
)
407 * OK, we have more than 1 rule in the current tree level.
408 * Make a new group tree and move the previous rule to it...
411 if ((temp
= new_expr(IPPFIND_OP_AND
, 0, NULL
, NULL
, NULL
)) == NULL
)
412 return (IPPFIND_EXIT_MEMORY
);
414 temp
->child
= current
;
415 temp
->parent
= current
->parent
;
416 current
->prev
->next
= temp
;
417 temp
->prev
= current
->prev
;
419 current
->prev
= NULL
;
420 current
->parent
= temp
;
424 parent
->op
= IPPFIND_OP_AND
;
426 logic
= IPPFIND_OP_AND
;
429 else if (!strcmp(argv
[i
], "--domain"))
435 if ((temp
= new_expr(IPPFIND_OP_DOMAIN_REGEX
, invert
, NULL
, argv
[i
],
437 return (IPPFIND_EXIT_MEMORY
);
439 else if (!strcmp(argv
[i
], "--exec"))
445 if ((temp
= new_expr(IPPFIND_OP_EXEC
, invert
, NULL
, NULL
,
447 return (IPPFIND_EXIT_MEMORY
);
450 if (!strcmp(argv
[i
], ";"))
460 else if (!strcmp(argv
[i
], "--false"))
466 if ((temp
= new_expr(IPPFIND_OP_FALSE
, invert
, NULL
, NULL
,
468 return (IPPFIND_EXIT_MEMORY
);
470 else if (!strcmp(argv
[i
], "--help"))
474 else if (!strcmp(argv
[i
], "--ls"))
480 if ((temp
= new_expr(IPPFIND_OP_LIST
, invert
, NULL
, NULL
,
482 return (IPPFIND_EXIT_MEMORY
);
486 else if (!strcmp(argv
[i
], "--local"))
492 if ((temp
= new_expr(IPPFIND_OP_IS_LOCAL
, invert
, NULL
, NULL
,
494 return (IPPFIND_EXIT_MEMORY
);
496 else if (!strcmp(argv
[i
], "--name"))
502 if ((temp
= new_expr(IPPFIND_OP_NAME_REGEX
, invert
, NULL
, argv
[i
],
504 return (IPPFIND_EXIT_MEMORY
);
506 else if (!strcmp(argv
[i
], "--not"))
510 else if (!strcmp(argv
[i
], "--or"))
512 if (logic
!= IPPFIND_OP_OR
&& current
)
515 * OK, we have two possibilities; either this is the top-level
516 * rule or we have a bunch of AND rules at this level.
522 * This is the top-level rule; we have to group *all* of the AND
523 * rules down a level, as AND has precedence over OR.
526 if ((temp
= new_expr(IPPFIND_OP_AND
, 0, NULL
, NULL
,
528 return (IPPFIND_EXIT_MEMORY
);
530 while (current
->prev
)
532 current
->parent
= temp
;
533 current
= current
->prev
;
536 current
->parent
= temp
;
537 temp
->child
= current
;
539 expressions
= current
= temp
;
544 * This isn't the top rule, so go up one level...
548 parent
= current
->parent
;
552 logic
= IPPFIND_OP_OR
;
555 else if (!strcmp(argv
[i
], "--path"))
561 if ((temp
= new_expr(IPPFIND_OP_PATH_REGEX
, invert
, NULL
, argv
[i
],
563 return (IPPFIND_EXIT_MEMORY
);
565 else if (!strcmp(argv
[i
], "--print"))
567 if ((temp
= new_expr(IPPFIND_OP_PRINT_URI
, invert
, NULL
, NULL
,
569 return (IPPFIND_EXIT_MEMORY
);
573 else if (!strcmp(argv
[i
], "--print-name"))
575 if ((temp
= new_expr(IPPFIND_OP_PRINT_NAME
, invert
, NULL
, NULL
,
577 return (IPPFIND_EXIT_MEMORY
);
581 else if (!strcmp(argv
[i
], "--quiet"))
583 if ((temp
= new_expr(IPPFIND_OP_QUIET
, invert
, NULL
, NULL
,
585 return (IPPFIND_EXIT_MEMORY
);
589 else if (!strcmp(argv
[i
], "--remote"))
591 if ((temp
= new_expr(IPPFIND_OP_IS_REMOTE
, invert
, NULL
, NULL
,
593 return (IPPFIND_EXIT_MEMORY
);
595 else if (!strcmp(argv
[i
], "--true"))
597 if ((temp
= new_expr(IPPFIND_OP_TRUE
, invert
, NULL
, argv
[i
],
599 return (IPPFIND_EXIT_MEMORY
);
601 else if (!strcmp(argv
[i
], "--txt"))
607 if ((temp
= new_expr(IPPFIND_OP_TXT_EXISTS
, invert
, argv
[i
], NULL
,
609 return (IPPFIND_EXIT_MEMORY
);
611 else if (!strncmp(argv
[i
], "--txt-", 5))
613 const char *key
= argv
[i
] + 5;/* TXT key */
619 if ((temp
= new_expr(IPPFIND_OP_TXT_REGEX
, invert
, key
, argv
[i
],
621 return (IPPFIND_EXIT_MEMORY
);
623 else if (!strcmp(argv
[i
], "--uri"))
629 if ((temp
= new_expr(IPPFIND_OP_URI_REGEX
, invert
, NULL
, argv
[i
],
631 return (IPPFIND_EXIT_MEMORY
);
633 else if (!strcmp(argv
[i
], "--version"))
639 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."),
647 * Add new expression...
652 temp
->parent
= parent
;
653 current
->next
= temp
;
656 parent
->child
= temp
;
660 temp
->prev
= current
;
672 for (opt
= argv
[i
] + 1; *opt
; opt
++)
677 address_family
= AF_INET
;
681 address_family
= AF_INET6
;
689 bonjour_timeout
= atof(argv
[i
]);
697 if (!strcmp(argv
[i
], "1.1"))
699 else if (!strcmp(argv
[i
], "2.0"))
701 else if (!strcmp(argv
[i
], "2.1"))
703 else if (!strcmp(argv
[i
], "2.2"))
714 if ((temp
= new_expr(IPPFIND_OP_DOMAIN_REGEX
, invert
, NULL
,
715 argv
[i
], NULL
)) == NULL
)
716 return (IPPFIND_EXIT_MEMORY
);
724 if ((temp
= new_expr(IPPFIND_OP_EXEC
, invert
, NULL
, NULL
,
726 return (IPPFIND_EXIT_MEMORY
);
729 if (!strcmp(argv
[i
], ";"))
745 if ((temp
= new_expr(IPPFIND_OP_LIST
, invert
, NULL
, NULL
,
747 return (IPPFIND_EXIT_MEMORY
);
757 if ((temp
= new_expr(IPPFIND_OP_NAME_REGEX
, invert
, NULL
,
758 argv
[i
], NULL
)) == NULL
)
759 return (IPPFIND_EXIT_MEMORY
);
763 if ((temp
= new_expr(IPPFIND_OP_PRINT_URI
, invert
, NULL
, NULL
,
765 return (IPPFIND_EXIT_MEMORY
);
771 if ((temp
= new_expr(IPPFIND_OP_QUIET
, invert
, NULL
, NULL
,
773 return (IPPFIND_EXIT_MEMORY
);
779 if ((temp
= new_expr(IPPFIND_OP_IS_REMOTE
, invert
, NULL
, NULL
,
781 return (IPPFIND_EXIT_MEMORY
);
785 if ((temp
= new_expr(IPPFIND_OP_PRINT_NAME
, invert
, NULL
, NULL
,
787 return (IPPFIND_EXIT_MEMORY
);
797 if ((temp
= new_expr(IPPFIND_OP_TXT_EXISTS
, invert
, argv
[i
],
798 NULL
, NULL
)) == NULL
)
799 return (IPPFIND_EXIT_MEMORY
);
807 if ((temp
= new_expr(IPPFIND_OP_URI_REGEX
, invert
, NULL
,
808 argv
[i
], NULL
)) == NULL
)
809 return (IPPFIND_EXIT_MEMORY
);
813 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."),
822 * Add new expression...
827 temp
->parent
= parent
;
828 current
->next
= temp
;
831 parent
->child
= temp
;
835 temp
->prev
= current
;
843 else if (!strcmp(argv
[i
], "("))
845 if ((temp
= new_expr(IPPFIND_OP_AND
, invert
, NULL
, NULL
, NULL
)) == NULL
)
846 return (IPPFIND_EXIT_MEMORY
);
850 temp
->parent
= current
->parent
;
851 current
->next
= temp
;
856 temp
->prev
= current
;
860 logic
= IPPFIND_OP_AND
;
862 else if (!strcmp(argv
[i
], ")"))
866 _cupsLangPuts(stderr
, _("ippfind: Missing open parenthesis."));
871 parent
= current
->parent
;
874 logic
= IPPFIND_OP_AND
;
878 else if (!strcmp(argv
[i
], "!"))
885 * _regtype._tcp[,subtype][.domain]
889 * service-name[._regtype._tcp[.domain]]
892 cupsArrayAdd(searches
, argv
[i
]);
898 _cupsLangPuts(stderr
, _("ippfind: Missing close parenthesis."));
902 if (cupsArrayCount(searches
) == 0)
905 * Add an implicit browse for IPP printers ("_ipp._tcp")...
908 cupsArrayAdd(searches
, "_ipp._tcp");
914 * Add an implicit --print-uri to the end...
917 if ((temp
= new_expr(IPPFIND_OP_PRINT_URI
, 0, NULL
, NULL
, NULL
)) == NULL
)
918 return (IPPFIND_EXIT_MEMORY
);
922 temp
->parent
= parent
;
923 current
->next
= temp
;
928 temp
->prev
= current
;
932 * Start up browsing/resolving...
936 if ((err
= DNSServiceCreateConnection(&dnssd_ref
)) != kDNSServiceErr_NoError
)
938 _cupsLangPrintf(stderr
, _("ippfind: Unable to use Bonjour: %s"),
939 dnssd_error_string(err
));
940 return (IPPFIND_EXIT_BONJOUR
);
943 #elif defined(HAVE_AVAHI)
944 if ((avahi_poll
= avahi_simple_poll_new()) == NULL
)
946 _cupsLangPrintError(stderr
, _("ippfind: Unable to use Bonjour: %s"),
948 return (IPPFIND_EXIT_BONJOUR
);
951 avahi_simple_poll_set_func(avahi_poll
, poll_callback
, NULL
);
953 avahi_client
= avahi_client_new(avahi_simple_poll_get(avahi_poll
),
954 0, client_callback
, avahi_poll
, &err
);
957 _cupsLangPrintError(stderr
, _("ippfind: Unable to use Bonjour: %s"),
958 dnssd_error_string(err
));
959 return (IPPFIND_EXIT_BONJOUR
);
961 #endif /* HAVE_DNSSD */
963 for (search
= (const char *)cupsArrayFirst(searches
);
965 search
= (const char *)cupsArrayNext(searches
))
967 char buf
[1024], /* Full name string */
968 *name
= NULL
, /* Service instance name */
969 *regtype
, /* Registration type */
970 *domain
; /* Domain, if any */
972 strlcpy(buf
, search
, sizeof(buf
));
977 else if ((regtype
= strstr(buf
, "._")) != NULL
)
985 regtype
= "_ipp._tcp";
988 for (domain
= regtype
; *domain
; domain
++)
989 if (*domain
== '.' && domain
[1] != '_')
1001 * Resolve the given service instance name, regtype, and domain...
1007 service
= get_service(services
, name
, regtype
, domain
);
1010 service
->ref
= dnssd_ref
;
1011 err
= DNSServiceResolve(&(service
->ref
),
1012 kDNSServiceFlagsShareConnection
, 0, name
,
1013 regtype
, domain
, resolve_callback
,
1016 #elif defined(HAVE_AVAHI)
1017 service
->ref
= avahi_service_resolver_new(avahi_client
, AVAHI_IF_UNSPEC
,
1018 AVAHI_PROTO_UNSPEC
, name
,
1020 AVAHI_PROTO_UNSPEC
, 0,
1021 resolve_callback
, service
);
1025 err
= avahi_client_get_errno(avahi_client
);
1026 #endif /* HAVE_DNSSD */
1031 * Browse for services of the given type...
1035 DNSServiceRef ref
; /* Browse reference */
1038 err
= DNSServiceBrowse(&ref
, kDNSServiceFlagsShareConnection
, 0, regtype
,
1039 domain
, browse_callback
, services
);
1044 err
= DNSServiceBrowse(&ref
, kDNSServiceFlagsShareConnection
,
1045 kDNSServiceInterfaceIndexLocalOnly
, regtype
,
1046 domain
, browse_local_callback
, services
);
1049 #elif defined(HAVE_AVAHI)
1050 if (avahi_service_browser_new(avahi_client
, AVAHI_IF_UNSPEC
,
1051 AVAHI_PROTO_UNSPEC
, regtype
, domain
, 0,
1052 browse_callback
, services
))
1055 err
= avahi_client_get_errno(avahi_client
);
1056 #endif /* HAVE_DNSSD */
1061 _cupsLangPrintf(stderr
, _("ippfind: Unable to browse or resolve: %s"),
1062 dnssd_error_string(err
));
1065 printf("name=\"%s\"\n", name
);
1067 printf("regtype=\"%s\"\n", regtype
);
1070 printf("domain=\"%s\"\n", domain
);
1072 return (IPPFIND_EXIT_BONJOUR
);
1077 * Process browse/resolve requests...
1080 if (bonjour_timeout
> 1.0)
1081 endtime
= get_time() + bonjour_timeout
;
1083 endtime
= get_time() + 300.0;
1085 while (get_time() < endtime
)
1087 int process
= 0; /* Process services? */
1090 int fd
= DNSServiceRefSockFD(dnssd_ref
);
1091 /* File descriptor for DNS-SD */
1094 FD_SET(fd
, &sinput
);
1096 stimeout
.tv_sec
= 0;
1097 stimeout
.tv_usec
= 500000;
1099 if (select(fd
+ 1, &sinput
, NULL
, NULL
, &stimeout
) < 0)
1102 if (FD_ISSET(fd
, &sinput
))
1105 * Process responses...
1108 DNSServiceProcessResult(dnssd_ref
);
1113 * Time to process services...
1119 #elif defined(HAVE_AVAHI)
1122 if (avahi_simple_poll_iterate(avahi_poll
, 500) > 0)
1125 * We've been told to exit the loop. Perhaps the connection to
1129 return (IPPFIND_EXIT_BONJOUR
);
1132 if (!avahi_got_data
)
1135 * Time to process services...
1140 #endif /* HAVE_DNSSD */
1145 * Process any services that we have found...
1148 int active
= 0, /* Number of active resolves */
1149 resolved
= 0, /* Number of resolved services */
1150 processed
= 0; /* Number of processed services */
1152 for (service
= (ippfind_srv_t
*)cupsArrayFirst(services
);
1154 service
= (ippfind_srv_t
*)cupsArrayNext(services
))
1156 if (service
->is_processed
)
1159 if (service
->is_resolved
)
1162 if (!service
->ref
&& !service
->is_resolved
)
1165 * Found a service, now resolve it (but limit to 50 active resolves...)
1171 service
->ref
= dnssd_ref
;
1172 err
= DNSServiceResolve(&(service
->ref
),
1173 kDNSServiceFlagsShareConnection
, 0,
1174 service
->name
, service
->regtype
,
1175 service
->domain
, resolve_callback
,
1178 #elif defined(HAVE_AVAHI)
1179 service
->ref
= avahi_service_resolver_new(avahi_client
,
1185 AVAHI_PROTO_UNSPEC
, 0,
1191 err
= avahi_client_get_errno(avahi_client
);
1192 #endif /* HAVE_DNSSD */
1196 _cupsLangPrintf(stderr
,
1197 _("ippfind: Unable to browse or resolve: %s"),
1198 dnssd_error_string(err
));
1199 return (IPPFIND_EXIT_BONJOUR
);
1205 else if (service
->is_resolved
&& !service
->is_processed
)
1208 * Resolved, not process this service against the expressions...
1214 DNSServiceRefDeallocate(service
->ref
);
1216 avahi_record_browser_free(service
->ref
);
1217 #endif /* HAVE_DNSSD */
1219 service
->ref
= NULL
;
1222 if (!eval_expr(service
, expressions
))
1223 status
= IPPFIND_EXIT_FALSE
;
1225 service
->is_processed
= 1;
1227 else if (service
->ref
)
1232 * If we have processed all services we have discovered, then we are done.
1235 if (processed
== cupsArrayCount(services
) && bonjour_timeout
<= 1.0)
1241 return (IPPFIND_EXIT_BONJOUR
);
1249 * 'browse_callback()' - Browse devices.
1254 DNSServiceRef sdRef
, /* I - Service reference */
1255 DNSServiceFlags flags
, /* I - Option flags */
1256 uint32_t interfaceIndex
, /* I - Interface number */
1257 DNSServiceErrorType errorCode
, /* I - Error, if any */
1258 const char *serviceName
, /* I - Name of service/device */
1259 const char *regtype
, /* I - Type of service */
1260 const char *replyDomain
, /* I - Service domain */
1261 void *context
) /* I - Services array */
1264 * Only process "add" data...
1267 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1274 get_service((cups_array_t
*)context
, serviceName
, regtype
, replyDomain
);
1279 * 'browse_local_callback()' - Browse local devices.
1283 browse_local_callback(
1284 DNSServiceRef sdRef
, /* I - Service reference */
1285 DNSServiceFlags flags
, /* I - Option flags */
1286 uint32_t interfaceIndex
, /* I - Interface number */
1287 DNSServiceErrorType errorCode
, /* I - Error, if any */
1288 const char *serviceName
, /* I - Name of service/device */
1289 const char *regtype
, /* I - Type of service */
1290 const char *replyDomain
, /* I - Service domain */
1291 void *context
) /* I - Services array */
1293 ippfind_srv_t
*service
; /* Service */
1297 * Only process "add" data...
1300 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1307 service
= get_service((cups_array_t
*)context
, serviceName
, regtype
,
1309 service
->is_local
= 1;
1311 #endif /* HAVE_DNSSD */
1316 * 'browse_callback()' - Browse devices.
1321 AvahiServiceBrowser
*browser
, /* I - Browser */
1322 AvahiIfIndex interface
, /* I - Interface index (unused) */
1323 AvahiProtocol protocol
, /* I - Network protocol (unused) */
1324 AvahiBrowserEvent event
, /* I - What happened */
1325 const char *name
, /* I - Service name */
1326 const char *type
, /* I - Registration type */
1327 const char *domain
, /* I - Domain */
1328 AvahiLookupResultFlags flags
, /* I - Flags */
1329 void *context
) /* I - Services array */
1331 AvahiClient
*client
= avahi_service_browser_get_client(browser
);
1332 /* Client information */
1333 ippfind_srv_t
*service
; /* Service information */
1342 case AVAHI_BROWSER_FAILURE
:
1343 fprintf(stderr
, "DEBUG: browse_callback: %s\n",
1344 avahi_strerror(avahi_client_errno(client
)));
1346 avahi_simple_poll_quit(simple_poll
);
1349 case AVAHI_BROWSER_NEW
:
1351 * This object is new on the network. Create a device entry for it if
1352 * it doesn't yet exist.
1355 service
= get_service((cups_array_t
*)context
, name
, type
, domain
);
1357 if (flags
& AVAHI_LOOKUP_RESULT_LOCAL
)
1358 service
->is_local
= 1;
1361 case AVAHI_BROWSER_REMOVE
:
1362 case AVAHI_BROWSER_ALL_FOR_NOW
:
1363 case AVAHI_BROWSER_CACHE_EXHAUSTED
:
1370 * 'client_callback()' - Avahi client callback function.
1375 AvahiClient
*client
, /* I - Client information (unused) */
1376 AvahiClientState state
, /* I - Current state */
1377 void *context
) /* I - User data (unused) */
1383 * If the connection drops, quit.
1386 if (state
== AVAHI_CLIENT_FAILURE
)
1388 fputs("DEBUG: Avahi connection failed.\n", stderr
);
1390 avahi_simple_poll_quit(avahi_poll
);
1393 #endif /* HAVE_AVAHI */
1397 * 'compare_services()' - Compare two devices.
1400 static int /* O - Result of comparison */
1401 compare_services(ippfind_srv_t
*a
, /* I - First device */
1402 ippfind_srv_t
*b
) /* I - Second device */
1404 return (strcmp(a
->name
, b
->name
));
1409 * 'dnssd_error_string()' - Return an error string for an error code.
1412 static const char * /* O - Error message */
1413 dnssd_error_string(int error
) /* I - Error number */
1418 case kDNSServiceErr_NoError
:
1422 case kDNSServiceErr_Unknown
:
1423 return ("Unknown error.");
1425 case kDNSServiceErr_NoSuchName
:
1426 return ("Service not found.");
1428 case kDNSServiceErr_NoMemory
:
1429 return ("Out of memory.");
1431 case kDNSServiceErr_BadParam
:
1432 return ("Bad parameter.");
1434 case kDNSServiceErr_BadReference
:
1435 return ("Bad service reference.");
1437 case kDNSServiceErr_BadState
:
1438 return ("Bad state.");
1440 case kDNSServiceErr_BadFlags
:
1441 return ("Bad flags.");
1443 case kDNSServiceErr_Unsupported
:
1444 return ("Unsupported.");
1446 case kDNSServiceErr_NotInitialized
:
1447 return ("Not initialized.");
1449 case kDNSServiceErr_AlreadyRegistered
:
1450 return ("Already registered.");
1452 case kDNSServiceErr_NameConflict
:
1453 return ("Name conflict.");
1455 case kDNSServiceErr_Invalid
:
1456 return ("Invalid name.");
1458 case kDNSServiceErr_Firewall
:
1459 return ("Firewall prevents registration.");
1461 case kDNSServiceErr_Incompatible
:
1462 return ("Client library incompatible.");
1464 case kDNSServiceErr_BadInterfaceIndex
:
1465 return ("Bad interface index.");
1467 case kDNSServiceErr_Refused
:
1468 return ("Server prevents registration.");
1470 case kDNSServiceErr_NoSuchRecord
:
1471 return ("Record not found.");
1473 case kDNSServiceErr_NoAuth
:
1474 return ("Authentication required.");
1476 case kDNSServiceErr_NoSuchKey
:
1477 return ("Encryption key not found.");
1479 case kDNSServiceErr_NATTraversal
:
1480 return ("Unable to traverse NAT boundary.");
1482 case kDNSServiceErr_DoubleNAT
:
1483 return ("Unable to traverse double-NAT boundary.");
1485 case kDNSServiceErr_BadTime
:
1486 return ("Bad system time.");
1488 case kDNSServiceErr_BadSig
:
1489 return ("Bad signature.");
1491 case kDNSServiceErr_BadKey
:
1492 return ("Bad encryption key.");
1494 case kDNSServiceErr_Transient
:
1495 return ("Transient error occurred - please try again.");
1497 case kDNSServiceErr_ServiceNotRunning
:
1498 return ("Server not running.");
1500 case kDNSServiceErr_NATPortMappingUnsupported
:
1501 return ("NAT doesn't support NAT-PMP or UPnP.");
1503 case kDNSServiceErr_NATPortMappingDisabled
:
1504 return ("NAT supports NAT-PNP or UPnP but it is disabled.");
1506 case kDNSServiceErr_NoRouter
:
1507 return ("No Internet/default router configured.");
1509 case kDNSServiceErr_PollingMode
:
1510 return ("Service polling mode error.");
1512 case kDNSServiceErr_Timeout
:
1513 return ("Service timeout.");
1516 # elif defined(HAVE_AVAHI)
1517 return (avahi_strerror(error
));
1518 # endif /* HAVE_DNSSD */
1523 * 'eval_expr()' - Evaluate the expressions against the specified service.
1525 * Returns 1 for true and 0 for false.
1528 static int /* O - Result of evaluation */
1529 eval_expr(ippfind_srv_t
*service
, /* I - Service */
1530 ippfind_expr_t
*expressions
) /* I - Expressions */
1540 * 'get_service()' - Create or update a device.
1543 static ippfind_srv_t
* /* O - Service */
1544 get_service(cups_array_t
*services
, /* I - Service array */
1545 const char *serviceName
, /* I - Name of service/device */
1546 const char *regtype
, /* I - Type of service */
1547 const char *replyDomain
) /* I - Service domain */
1549 ippfind_srv_t key
, /* Search key */
1550 *service
; /* Service */
1551 char fullName
[kDNSServiceMaxDomainName
];
1552 /* Full name for query */
1556 * See if this is a new device...
1559 key
.name
= (char *)serviceName
;
1560 key
.regtype
= (char *)regtype
;
1562 for (service
= cupsArrayFind(services
, &key
);
1564 service
= cupsArrayNext(services
))
1565 if (_cups_strcasecmp(service
->name
, key
.name
))
1567 else if (!strcmp(service
->regtype
, key
.regtype
))
1571 * Yes, add the service...
1574 service
= calloc(sizeof(ippfind_srv_t
), 1);
1575 service
->name
= strdup(serviceName
);
1576 service
->domain
= strdup(replyDomain
);
1577 service
->regtype
= strdup(regtype
);
1579 cupsArrayAdd(services
, service
);
1582 * Set the "full name" of this service, which is used for queries and
1587 DNSServiceConstructFullName(fullName
, serviceName
, regtype
, replyDomain
);
1588 #else /* HAVE_AVAHI */
1589 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
, serviceName
,
1590 regtype
, replyDomain
);
1591 #endif /* HAVE_DNSSD */
1593 service
->fullName
= strdup(fullName
);
1600 * 'get_time()' - Get the current time-of-day in seconds.
1607 struct _timeb curtime
; /* Current Windows time */
1611 return (curtime
.time
+ 0.001 * curtime
.millitm
);
1614 struct timeval curtime
; /* Current UNIX time */
1616 if (gettimeofday(&curtime
, NULL
))
1619 return (curtime
.tv_sec
+ 0.000001 * curtime
.tv_usec
);
1625 * 'new_expr()' - Create a new expression.
1628 static ippfind_expr_t
* /* O - New expression */
1629 new_expr(ippfind_op_t op
, /* I - Operation */
1630 int invert
, /* I - Invert result? */
1631 const char *key
, /* I - TXT key */
1632 const char *regex
, /* I - Regular expression */
1633 char **args
) /* I - Pointer to argument strings */
1635 ippfind_expr_t
*temp
; /* New expression */
1638 if ((temp
= calloc(1, sizeof(ippfind_expr_t
))) == NULL
)
1642 temp
->invert
= invert
;
1643 temp
->key
= (char *)key
;
1647 int err
= regcomp(&(temp
->re
), regex
, REG_NOSUB
| REG_EXTENDED
);
1651 char message
[256]; /* Error message */
1653 regerror(err
, &(temp
->re
), message
, sizeof(message
));
1654 _cupsLangPrintf(stderr
, _("ippfind: Bad regular expression: %s"),
1656 exit(IPPFIND_EXIT_SYNTAX
);
1662 int num_args
; /* Number of arguments */
1664 for (num_args
= 1; args
[num_args
]; num_args
++)
1665 if (!strcmp(args
[num_args
], ";"))
1668 temp
->args
= malloc(num_args
* sizeof(char *));
1669 memcpy(temp
->args
, args
, num_args
* sizeof(char *));
1678 * 'poll_callback()' - Wait for input on the specified file descriptors.
1680 * Note: This function is needed because avahi_simple_poll_iterate is broken
1681 * and always uses a timeout of 0 (!) milliseconds.
1682 * (Avahi Ticket #364)
1685 static int /* O - Number of file descriptors matching */
1687 struct pollfd
*pollfds
, /* I - File descriptors */
1688 unsigned int num_pollfds
, /* I - Number of file descriptors */
1689 int timeout
, /* I - Timeout in milliseconds (unused) */
1690 void *context
) /* I - User data (unused) */
1692 int val
; /* Return value */
1698 val
= poll(pollfds
, num_pollfds
, 500);
1701 fprintf(stderr
, "DEBUG: poll_callback: %s\n", strerror(errno
));
1707 #endif /* HAVE_AVAHI */
1711 * 'resolve_callback()' - Process resolve data.
1717 DNSServiceRef sdRef
, /* I - Service reference */
1718 DNSServiceFlags flags
, /* I - Data flags */
1719 uint32_t interfaceIndex
, /* I - Interface */
1720 DNSServiceErrorType errorCode
, /* I - Error, if any */
1721 const char *fullName
, /* I - Full service name */
1722 const char *hostTarget
, /* I - Hostname */
1723 uint16_t port
, /* I - Port number (network byte order) */
1724 uint16_t txtLen
, /* I - Length of TXT record data */
1725 const unsigned char *txtRecord
, /* I - TXT record data */
1726 void *context
) /* I - Service */
1728 char key
[256], /* TXT key value */
1729 *value
; /* Value from TXT record */
1730 const unsigned char *txtEnd
; /* End of TXT record */
1731 uint8_t valueLen
; /* Length of value */
1732 ippfind_srv_t
*service
= (ippfind_srv_t
*)context
;
1737 * Only process "add" data...
1740 if (errorCode
!= kDNSServiceErr_NoError
)
1742 _cupsLangPrintf(stderr
, _("ippfind: Unable to browse or resolve: %s"),
1743 dnssd_error_string(errorCode
));
1748 service
->is_resolved
= 1;
1749 service
->host
= strdup(hostTarget
);
1750 service
->port
= ntohs(port
);
1753 * Loop through the TXT key/value pairs and add them to an array...
1756 for (txtEnd
= txtRecord
+ txtLen
; txtRecord
< txtEnd
; txtRecord
+= valueLen
)
1759 * Ignore bogus strings...
1762 valueLen
= *txtRecord
++;
1764 memcpy(key
, txtRecord
, valueLen
);
1765 key
[valueLen
] = '\0';
1767 if ((value
= strchr(key
, '=')) == NULL
)
1773 * Add to array of TXT values...
1776 service
->num_txt
= cupsAddOption(key
, value
, service
->num_txt
,
1780 set_service_uri(service
);
1784 #elif defined(HAVE_AVAHI)
1787 AvahiServiceResolver
*resolver
, /* I - Resolver */
1788 AvahiIfIndex interface
, /* I - Interface */
1789 AvahiProtocol protocol
, /* I - Address protocol */
1790 AvahiBrowserEvent event
, /* I - Event */
1791 const char *serviceName
,/* I - Service name */
1792 const char *regtype
, /* I - Registration type */
1793 const char *replyDomain
,/* I - Domain name */
1794 const char *hostTarget
, /* I - FQDN */
1795 uint16_t port
, /* I - Port number */
1796 AvahiStringList
*txt
, /* I - TXT records */
1797 AvahiLookupResultFlags flags
, /* I - Lookup flags */
1798 void *context
) /* I - Service */
1800 char uri
[1024]; /* URI */
1801 key
[256], /* TXT key */
1802 *value
; /* TXT value */
1803 ippfind_srv_t
*service
= (ippfind_srv_t
*)context
;
1805 AvahiStringList
*current
; /* Current TXT key/value pair */
1808 if (event
!= AVAHI_RESOLVER_FOUND
)
1812 avahi_service_resolver_free(resolver
);
1813 avahi_simple_poll_quit(uribuf
->poll
);
1817 service
->is_resolved
= 1;
1818 service
->host
= strdup(hostTarget
);
1819 service
->port
= ntohs(port
);
1822 * Loop through the TXT key/value pairs and add them to an array...
1825 for (current
= txt
; current
; current
= current
->next
)
1828 * Ignore bogus strings...
1831 if (current
->size
> (sizeof(key
) - 1))
1834 memcpy(key
, current
->text
, current
->size
);
1835 key
[current
->size
] = '\0';
1837 if ((value
= strchr(key
, '=')) == NULL
)
1843 * Add to array of TXT values...
1846 service
->num_txt
= cupsAddOption(key
, value
, service
->num_txt
,
1850 set_service_uri(service
);
1852 #endif /* HAVE_DNSSD */
1856 * 'set_service_uri()' - Set the URI of the service.
1860 set_service_uri(ippfind_srv_t
*service
) /* I - Service */
1862 char uri
[1024]; /* URI */
1863 const char *path
, /* Resource path */
1864 *scheme
; /* URI scheme */
1867 if (!strncmp(service
->regtype
, "_http.", 6))
1870 path
= cupsGetOption("path", service
->num_txt
, service
->txt
);
1872 else if (!strncmp(service
->regtype
, "_https.", 7))
1875 path
= cupsGetOption("path", service
->num_txt
, service
->txt
);
1877 else if (!strncmp(service
->regtype
, "_ipp.", 5))
1880 path
= cupsGetOption("rp", service
->num_txt
, service
->txt
);
1882 else if (!strncmp(service
->regtype
, "_ipps.", 6))
1885 path
= cupsGetOption("rp", service
->num_txt
, service
->txt
);
1887 else if (!strncmp(service
->regtype
, "_printer.", 9))
1890 path
= cupsGetOption("rp", service
->num_txt
, service
->txt
);
1895 if (!path
|| !*path
)
1899 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
,
1900 service
->host
, service
->port
, path
);
1902 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
,
1903 service
->host
, service
->port
, "/%s", path
);
1905 service
->uri
= strdup(uri
);
1910 * 'show_usage()' - Show program usage.
1916 _cupsLangPuts(stderr
, _("Usage: ippfind [options] regtype[,subtype]"
1917 "[.domain.] ... [expression]\n"
1918 " ippfind [options] name[.regtype[.domain.]] "
1919 "... [expression]\n"
1921 " ippfind --version"));
1922 _cupsLangPuts(stderr
, "");
1923 _cupsLangPuts(stderr
, _("Options:"));
1924 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
1925 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
1926 _cupsLangPuts(stderr
, _(" -T seconds Set the browse timeout in "
1928 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
1930 _cupsLangPuts(stderr
, _(" --help Show this help."));
1931 _cupsLangPuts(stderr
, _(" --version Show program version."));
1932 _cupsLangPuts(stderr
, "");
1933 _cupsLangPuts(stderr
, _("Expressions:"));
1934 _cupsLangPuts(stderr
, _(" -d regex Match domain to regular expression."));
1935 _cupsLangPuts(stderr
, _(" -e utility [argument ...] ;\n"
1936 " Execute program if true."));
1937 _cupsLangPuts(stderr
, _(" -l List attributes."));
1938 _cupsLangPuts(stderr
, _(" -n regex Match service name to regular expression."));
1939 _cupsLangPuts(stderr
, _(" -p Print URI if true."));
1940 _cupsLangPuts(stderr
, _(" -q Quietly report match via exit code."));
1941 _cupsLangPuts(stderr
, _(" -r True if service is remote."));
1942 _cupsLangPuts(stderr
, _(" -s Print service name if true."));
1943 _cupsLangPuts(stderr
, _(" -t key True if the TXT record contains the key."));
1944 _cupsLangPuts(stderr
, _(" -u regex Match URI to regular expression."));
1945 _cupsLangPuts(stderr
, _(" --domain regex Match domain to regular expression."));
1946 _cupsLangPuts(stderr
, _(" --exec utility [argument ...] ;\n"
1947 " Execute program if true."));
1948 _cupsLangPuts(stderr
, _(" --ls List attributes."));
1949 _cupsLangPuts(stderr
, _(" --local True if service is local."));
1950 _cupsLangPuts(stderr
, _(" --name regex Match service name to regular expression."));
1951 _cupsLangPuts(stderr
, _(" --path regex Match resource path to regular expression."));
1952 _cupsLangPuts(stderr
, _(" --print Print URI if true."));
1953 _cupsLangPuts(stderr
, _(" --print-name Print service name if true."));
1954 _cupsLangPuts(stderr
, _(" --quiet Quietly report match via exit code."));
1955 _cupsLangPuts(stderr
, _(" --remote True if service is remote."));
1956 _cupsLangPuts(stderr
, _(" --txt key True if the TXT record contains the key."));
1957 _cupsLangPuts(stderr
, _(" --txt-* regex Match TXT record key to regular expression."));
1958 _cupsLangPuts(stderr
, _(" --uri regex Match URI to regular expression."));
1959 _cupsLangPuts(stderr
, "");
1960 _cupsLangPuts(stderr
, _("Modifiers:"));
1961 _cupsLangPuts(stderr
, _(" ( expressions ) Group expressions."));
1962 _cupsLangPuts(stderr
, _(" ! expression Unary NOT of expression."));
1963 _cupsLangPuts(stderr
, _(" --not expression Unary NOT of expression."));
1964 _cupsLangPuts(stderr
, _(" --false Always false."));
1965 _cupsLangPuts(stderr
, _(" --true Always true."));
1966 _cupsLangPuts(stderr
, _(" expression expression Logical AND."));
1967 _cupsLangPuts(stderr
, _(" expression --and expression\n"
1969 _cupsLangPuts(stderr
, _(" expression --or expression\n"
1971 _cupsLangPuts(stderr
, "");
1972 _cupsLangPuts(stderr
, _("Substitutions:"));
1973 _cupsLangPuts(stderr
, _(" {} URI"));
1974 _cupsLangPuts(stderr
, _(" {service_domain} Domain name"));
1975 _cupsLangPuts(stderr
, _(" {service_hostname} Fully-qualified domain name"));
1976 _cupsLangPuts(stderr
, _(" {service_name} Service instance name"));
1977 _cupsLangPuts(stderr
, _(" {service_port} Port number"));
1978 _cupsLangPuts(stderr
, _(" {service_regtype} DNS-SD registration type"));
1979 _cupsLangPuts(stderr
, _(" {service_scheme} URI scheme"));
1980 _cupsLangPuts(stderr
, _(" {service_uri} URI"));
1981 _cupsLangPuts(stderr
, _(" {txt_*} Value of TXT record key"));
1982 _cupsLangPuts(stderr
, "");
1983 _cupsLangPuts(stderr
, _("Environment Variables:"));
1984 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_DOMAIN Domain name"));
1985 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_HOSTNAME\n"
1986 " Fully-qualified domain name"));
1987 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_NAME Service instance name"));
1988 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_PORT Port number"));
1989 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_REGTYPE DNS-SD registration type"));
1990 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_SCHEME URI scheme"));
1991 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_URI URI"));
1992 _cupsLangPuts(stderr
, _(" IPPFIND_TXT_* Value of TXT record key"));
1994 exit(IPPFIND_EXIT_TRUE
);
1999 * 'show_version()' - Show program version.
2005 _cupsLangPuts(stderr
, CUPS_SVERSION
);
2007 exit(IPPFIND_EXIT_TRUE
);