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.
23 * Include necessary headers.
26 #include <cups/cups-private.h>
30 #elif defined(HAVE_AVAHI)
31 # include <avahi-client/client.h>
32 # include <avahi-client/lookup.h>
33 # include <avahi-common/simple-watch.h>
34 # include <avahi-common/domain.h>
35 # include <avahi-common/error.h>
36 # include <avahi-common/malloc.h>
37 # define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
38 #endif /* HAVE_DNSSD */
45 typedef enum ippfind_exit_e
/* Exit codes */
47 IPPFIND_EXIT_TRUE
= 0, /* OK and result is true */
48 IPPFIND_EXIT_FALSE
, /* OK but result is false*/
49 IPPFIND_EXIT_BONJOUR
, /* Browse/resolve failure */
50 IPPFIND_EXIT_SYNTAX
, /* Bad option or syntax error */
51 IPPFIND_EXIT_MEMORY
/* Out of memory */
54 typedef enum ippfind_op_e
/* Operations for expressions */
56 /* "Evaluation" operations */
57 IPPFIND_OP_NONE
, /* No operation */
58 IPPFIND_OP_AND
, /* Logical AND of all children */
59 IPPFIND_OP_OR
, /* Logical OR of all children */
60 IPPFIND_OP_TRUE
, /* Always true */
61 IPPFIND_OP_FALSE
, /* Always false */
62 IPPFIND_OP_IS_LOCAL
, /* Is a local service */
63 IPPFIND_OP_IS_REMOTE
, /* Is a remote service */
64 IPPFIND_OP_DOMAIN_REGEX
, /* Domain matches regular expression */
65 IPPFIND_OP_NAME_REGEX
, /* Name matches regular expression */
66 IPPFIND_OP_HOST_REGEX
, /* Hostname matches regular expression */
67 IPPFIND_OP_PORT_RANGE
, /* Port matches range */
68 IPPFIND_OP_PATH_REGEX
, /* Path matches regular expression */
69 IPPFIND_OP_TXT_EXISTS
, /* TXT record key exists */
70 IPPFIND_OP_TXT_REGEX
, /* TXT record key matches regular expression */
71 IPPFIND_OP_URI_REGEX
, /* URI matches regular expression */
73 /* "Output" operations */
74 IPPFIND_OP_EXEC
, /* Execute when true */
75 IPPFIND_OP_LIST
, /* List when true */
76 IPPFIND_OP_PRINT_NAME
, /* Print URI when true */
77 IPPFIND_OP_PRINT_URI
, /* Print name when true */
78 IPPFIND_OP_QUIET
, /* No output when true */
81 typedef struct ippfind_expr_s
/* Expression */
84 *prev
, /* Previous expression */
85 *next
, /* Next expression */
86 *parent
, /* Parent expressions */
87 *child
; /* Child expressions */
88 ippfind_op_t op
; /* Operation code (see above) */
89 int invert
; /* Invert the result */
90 char *key
; /* TXT record key */
91 regex_t re
; /* Regular expression for matching */
92 int range
[2]; /* Port number range */
93 int num_args
; /* Number of arguments for exec */
94 char **args
; /* Arguments for exec */
97 typedef struct ippfind_srv_s
/* Service information */
100 DNSServiceRef ref
; /* Service reference for query */
101 #elif defined(HAVE_AVAHI)
102 AvahiServiceResolver
*ref
; /* Resolver */
103 #endif /* HAVE_DNSSD */
104 char *name
, /* Service name */
105 *domain
, /* Domain name */
106 *regtype
, /* Registration type */
107 *fullName
, /* Full name */
108 *host
, /* Hostname */
109 *resource
, /* Resource path */
111 int num_txt
; /* Number of TXT record keys */
112 cups_option_t
*txt
; /* TXT record keys */
113 int port
, /* Port number */
114 is_local
, /* Is a local service? */
115 is_processed
, /* Did we process the service? */
116 is_resolved
; /* Got the resolve data? */
125 static DNSServiceRef dnssd_ref
; /* Master service reference */
126 #elif defined(HAVE_AVAHI)
127 static AvahiClient
*avahi_client
= NULL
;/* Client information */
128 static int avahi_got_data
= 0; /* Got data from poll? */
129 static AvahiSimplePoll
*avahi_poll
= NULL
;
130 /* Poll information */
131 #endif /* HAVE_DNSSD */
133 static int address_family
= AF_UNSPEC
;
134 /* Address family for LIST */
135 static int bonjour_error
= 0; /* Error browsing/resolving? */
136 static double bonjour_timeout
= 1.0; /* Timeout in seconds */
137 static int ipp_version
= 20; /* IPP version for LIST */
145 static void browse_callback(DNSServiceRef sdRef
,
146 DNSServiceFlags flags
,
147 uint32_t interfaceIndex
,
148 DNSServiceErrorType errorCode
,
149 const char *serviceName
,
151 const char *replyDomain
, void *context
)
152 __attribute__((nonnull(1,5,6,7,8)));
153 static void browse_local_callback(DNSServiceRef sdRef
,
154 DNSServiceFlags flags
,
155 uint32_t interfaceIndex
,
156 DNSServiceErrorType errorCode
,
157 const char *serviceName
,
159 const char *replyDomain
,
161 __attribute__((nonnull(1,5,6,7,8)));
162 #elif defined(HAVE_AVAHI)
163 static void browse_callback(AvahiServiceBrowser
*browser
,
164 AvahiIfIndex interface
,
165 AvahiProtocol protocol
,
166 AvahiBrowserEvent event
,
167 const char *serviceName
,
169 const char *replyDomain
,
170 AvahiLookupResultFlags flags
,
172 static void client_callback(AvahiClient
*client
,
173 AvahiClientState state
,
175 #endif /* HAVE_AVAHI */
177 static int compare_services(ippfind_srv_t
*a
, ippfind_srv_t
*b
);
178 static const char *dnssd_error_string(int error
);
179 static int eval_expr(ippfind_srv_t
*service
,
180 ippfind_expr_t
*expressions
);
181 static int exec_program(ippfind_srv_t
*service
, int num_args
,
183 static ippfind_srv_t
*get_service(cups_array_t
*services
,
184 const char *serviceName
,
186 const char *replyDomain
)
187 __attribute__((nonnull(1,2,3,4)));
188 static double get_time(void);
189 static int list_service(ippfind_srv_t
*service
);
190 static ippfind_expr_t
*new_expr(ippfind_op_t op
, int invert
,
191 const char *value
, const char *regex
,
194 static void resolve_callback(DNSServiceRef sdRef
,
195 DNSServiceFlags flags
,
196 uint32_t interfaceIndex
,
197 DNSServiceErrorType errorCode
,
198 const char *fullName
,
199 const char *hostTarget
, uint16_t port
,
201 const unsigned char *txtRecord
,
203 __attribute__((nonnull(1,5,6,9, 10)));
204 #elif defined(HAVE_AVAHI)
205 static int poll_callback(struct pollfd
*pollfds
,
206 unsigned int num_pollfds
, int timeout
,
208 static void resolve_callback(AvahiServiceResolver
*res
,
209 AvahiIfIndex interface
,
210 AvahiProtocol protocol
,
211 AvahiBrowserEvent event
,
212 const char *serviceName
,
214 const char *replyDomain
,
215 const char *host_name
,
217 AvahiStringList
*txt
,
218 AvahiLookupResultFlags flags
,
220 #endif /* HAVE_DNSSD */
221 static void set_service_uri(ippfind_srv_t
*service
);
222 static void show_usage(void) __attribute__((noreturn
));
223 static void show_version(void) __attribute__((noreturn
));
227 * 'main()' - Browse for printers.
230 int /* O - Exit status */
231 main(int argc
, /* I - Number of command-line args */
232 char *argv
[]) /* I - Command-line arguments */
234 int i
, /* Looping var */
235 have_output
= 0,/* Have output expression */
236 status
= IPPFIND_EXIT_TRUE
;
238 const char *opt
, /* Option character */
239 *search
; /* Current browse/resolve string */
240 cups_array_t
*searches
; /* Things to browse/resolve */
241 cups_array_t
*services
; /* Service array */
242 ippfind_srv_t
*service
; /* Current service */
243 ippfind_expr_t
*expressions
= NULL
,
244 /* Expression tree */
245 *temp
= NULL
, /* New expression */
246 *parent
= NULL
, /* Parent expression */
247 *current
= NULL
;/* Current expression */
248 ippfind_op_t logic
= IPPFIND_OP_AND
;
249 /* Logic for next expression */
250 int invert
= 0; /* Invert expression? */
251 int err
; /* DNS-SD error */
253 fd_set sinput
; /* Input set for select() */
254 struct timeval stimeout
; /* Timeout for select() */
255 #endif /* HAVE_DNSSD */
256 double endtime
; /* End time */
260 * Initialize the locale...
263 _cupsSetLocale(argv
);
266 * Create arrays to track services and things we want to browse/resolve...
269 searches
= cupsArrayNew(NULL
, NULL
);
270 services
= cupsArrayNew((cups_array_func_t
)compare_services
, NULL
);
273 * Parse command-line...
276 for (i
= 1; i
< argc
; i
++)
278 if (argv
[i
][0] == '-')
280 if (argv
[i
][1] == '-')
283 * Parse --option options...
286 if (!strcmp(argv
[i
], "--and"))
288 if (logic
!= IPPFIND_OP_AND
&& current
&& current
->prev
)
291 * OK, we have more than 1 rule in the current tree level.
292 * Make a new group tree and move the previous rule to it...
295 if ((temp
= new_expr(IPPFIND_OP_AND
, 0, NULL
, NULL
, NULL
)) == NULL
)
296 return (IPPFIND_EXIT_MEMORY
);
298 temp
->child
= current
;
299 temp
->parent
= current
->parent
;
300 current
->prev
->next
= temp
;
301 temp
->prev
= current
->prev
;
303 current
->prev
= NULL
;
304 current
->parent
= temp
;
308 parent
->op
= IPPFIND_OP_AND
;
310 logic
= IPPFIND_OP_AND
;
313 else if (!strcmp(argv
[i
], "--domain"))
319 if ((temp
= new_expr(IPPFIND_OP_DOMAIN_REGEX
, invert
, NULL
, argv
[i
],
321 return (IPPFIND_EXIT_MEMORY
);
323 else if (!strcmp(argv
[i
], "--exec"))
329 if ((temp
= new_expr(IPPFIND_OP_EXEC
, invert
, NULL
, NULL
,
331 return (IPPFIND_EXIT_MEMORY
);
334 if (!strcmp(argv
[i
], ";"))
344 else if (!strcmp(argv
[i
], "--false"))
350 if ((temp
= new_expr(IPPFIND_OP_FALSE
, invert
, NULL
, NULL
,
352 return (IPPFIND_EXIT_MEMORY
);
354 else if (!strcmp(argv
[i
], "--help"))
358 else if (!strcmp(argv
[i
], "--host"))
364 if ((temp
= new_expr(IPPFIND_OP_HOST_REGEX
, invert
, NULL
, argv
[i
],
366 return (IPPFIND_EXIT_MEMORY
);
368 else if (!strcmp(argv
[i
], "--ls"))
374 if ((temp
= new_expr(IPPFIND_OP_LIST
, invert
, NULL
, NULL
,
376 return (IPPFIND_EXIT_MEMORY
);
380 else if (!strcmp(argv
[i
], "--local"))
386 if ((temp
= new_expr(IPPFIND_OP_IS_LOCAL
, invert
, NULL
, NULL
,
388 return (IPPFIND_EXIT_MEMORY
);
390 else if (!strcmp(argv
[i
], "--name"))
396 if ((temp
= new_expr(IPPFIND_OP_NAME_REGEX
, invert
, NULL
, argv
[i
],
398 return (IPPFIND_EXIT_MEMORY
);
400 else if (!strcmp(argv
[i
], "--not"))
404 else if (!strcmp(argv
[i
], "--or"))
406 if (logic
!= IPPFIND_OP_OR
&& current
)
409 * OK, we have two possibilities; either this is the top-level
410 * rule or we have a bunch of AND rules at this level.
416 * This is the top-level rule; we have to group *all* of the AND
417 * rules down a level, as AND has precedence over OR.
420 if ((temp
= new_expr(IPPFIND_OP_AND
, 0, NULL
, NULL
,
422 return (IPPFIND_EXIT_MEMORY
);
424 while (current
->prev
)
426 current
->parent
= temp
;
427 current
= current
->prev
;
430 current
->parent
= temp
;
431 temp
->child
= current
;
433 expressions
= current
= temp
;
438 * This isn't the top rule, so go up one level...
442 parent
= current
->parent
;
446 logic
= IPPFIND_OP_OR
;
449 else if (!strcmp(argv
[i
], "--path"))
455 if ((temp
= new_expr(IPPFIND_OP_PATH_REGEX
, invert
, NULL
, argv
[i
],
457 return (IPPFIND_EXIT_MEMORY
);
459 else if (!strcmp(argv
[i
], "--port"))
465 if ((temp
= new_expr(IPPFIND_OP_PORT_RANGE
, invert
, argv
[i
], NULL
,
467 return (IPPFIND_EXIT_MEMORY
);
469 else if (!strcmp(argv
[i
], "--print"))
471 if ((temp
= new_expr(IPPFIND_OP_PRINT_URI
, invert
, NULL
, NULL
,
473 return (IPPFIND_EXIT_MEMORY
);
477 else if (!strcmp(argv
[i
], "--print-name"))
479 if ((temp
= new_expr(IPPFIND_OP_PRINT_NAME
, invert
, NULL
, NULL
,
481 return (IPPFIND_EXIT_MEMORY
);
485 else if (!strcmp(argv
[i
], "--quiet"))
487 if ((temp
= new_expr(IPPFIND_OP_QUIET
, invert
, NULL
, NULL
,
489 return (IPPFIND_EXIT_MEMORY
);
493 else if (!strcmp(argv
[i
], "--remote"))
495 if ((temp
= new_expr(IPPFIND_OP_IS_REMOTE
, invert
, NULL
, NULL
,
497 return (IPPFIND_EXIT_MEMORY
);
499 else if (!strcmp(argv
[i
], "--true"))
501 if ((temp
= new_expr(IPPFIND_OP_TRUE
, invert
, NULL
, argv
[i
],
503 return (IPPFIND_EXIT_MEMORY
);
505 else if (!strcmp(argv
[i
], "--txt"))
511 if ((temp
= new_expr(IPPFIND_OP_TXT_EXISTS
, invert
, argv
[i
], NULL
,
513 return (IPPFIND_EXIT_MEMORY
);
515 else if (!strncmp(argv
[i
], "--txt-", 5))
517 const char *key
= argv
[i
] + 5;/* TXT key */
523 if ((temp
= new_expr(IPPFIND_OP_TXT_REGEX
, invert
, key
, argv
[i
],
525 return (IPPFIND_EXIT_MEMORY
);
527 else if (!strcmp(argv
[i
], "--uri"))
533 if ((temp
= new_expr(IPPFIND_OP_URI_REGEX
, invert
, NULL
, argv
[i
],
535 return (IPPFIND_EXIT_MEMORY
);
537 else if (!strcmp(argv
[i
], "--version"))
543 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."),
551 * Add new expression...
556 temp
->parent
= parent
;
557 current
->next
= temp
;
560 parent
->child
= temp
;
564 temp
->prev
= current
;
576 for (opt
= argv
[i
] + 1; *opt
; opt
++)
581 address_family
= AF_INET
;
585 address_family
= AF_INET6
;
593 if ((temp
= new_expr(IPPFIND_OP_PORT_RANGE
, invert
, argv
[i
],
594 NULL
, NULL
)) == NULL
)
595 return (IPPFIND_EXIT_MEMORY
);
603 bonjour_timeout
= atof(argv
[i
]);
611 if (!strcmp(argv
[i
], "1.1"))
613 else if (!strcmp(argv
[i
], "2.0"))
615 else if (!strcmp(argv
[i
], "2.1"))
617 else if (!strcmp(argv
[i
], "2.2"))
628 if ((temp
= new_expr(IPPFIND_OP_DOMAIN_REGEX
, invert
, NULL
,
629 argv
[i
], NULL
)) == NULL
)
630 return (IPPFIND_EXIT_MEMORY
);
638 if ((temp
= new_expr(IPPFIND_OP_EXEC
, invert
, NULL
, NULL
,
640 return (IPPFIND_EXIT_MEMORY
);
643 if (!strcmp(argv
[i
], ";"))
659 if ((temp
= new_expr(IPPFIND_OP_HOST_REGEX
, invert
, NULL
,
660 argv
[i
], NULL
)) == NULL
)
661 return (IPPFIND_EXIT_MEMORY
);
669 if ((temp
= new_expr(IPPFIND_OP_LIST
, invert
, NULL
, NULL
,
671 return (IPPFIND_EXIT_MEMORY
);
681 if ((temp
= new_expr(IPPFIND_OP_NAME_REGEX
, invert
, NULL
,
682 argv
[i
], NULL
)) == NULL
)
683 return (IPPFIND_EXIT_MEMORY
);
687 if ((temp
= new_expr(IPPFIND_OP_PRINT_URI
, invert
, NULL
, NULL
,
689 return (IPPFIND_EXIT_MEMORY
);
695 if ((temp
= new_expr(IPPFIND_OP_QUIET
, invert
, NULL
, NULL
,
697 return (IPPFIND_EXIT_MEMORY
);
703 if ((temp
= new_expr(IPPFIND_OP_IS_REMOTE
, invert
, NULL
, NULL
,
705 return (IPPFIND_EXIT_MEMORY
);
709 if ((temp
= new_expr(IPPFIND_OP_PRINT_NAME
, invert
, NULL
, NULL
,
711 return (IPPFIND_EXIT_MEMORY
);
721 if ((temp
= new_expr(IPPFIND_OP_TXT_EXISTS
, invert
, argv
[i
],
722 NULL
, NULL
)) == NULL
)
723 return (IPPFIND_EXIT_MEMORY
);
731 if ((temp
= new_expr(IPPFIND_OP_URI_REGEX
, invert
, NULL
,
732 argv
[i
], NULL
)) == NULL
)
733 return (IPPFIND_EXIT_MEMORY
);
737 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."),
746 * Add new expression...
751 temp
->parent
= parent
;
752 current
->next
= temp
;
755 parent
->child
= temp
;
759 temp
->prev
= current
;
767 else if (!strcmp(argv
[i
], "("))
769 if ((temp
= new_expr(IPPFIND_OP_AND
, invert
, NULL
, NULL
, NULL
)) == NULL
)
770 return (IPPFIND_EXIT_MEMORY
);
774 temp
->parent
= current
->parent
;
775 current
->next
= temp
;
780 temp
->prev
= current
;
784 logic
= IPPFIND_OP_AND
;
786 else if (!strcmp(argv
[i
], ")"))
790 _cupsLangPuts(stderr
, _("ippfind: Missing open parenthesis."));
795 parent
= current
->parent
;
798 logic
= IPPFIND_OP_AND
;
802 else if (!strcmp(argv
[i
], "!"))
809 * _regtype._tcp[,subtype][.domain]
813 * service-name[._regtype._tcp[.domain]]
816 cupsArrayAdd(searches
, argv
[i
]);
822 _cupsLangPuts(stderr
, _("ippfind: Missing close parenthesis."));
826 if (cupsArrayCount(searches
) == 0)
829 * Add an implicit browse for IPP printers ("_ipp._tcp")...
832 cupsArrayAdd(searches
, "_ipp._tcp");
838 * Add an implicit --print-uri to the end...
841 if ((temp
= new_expr(IPPFIND_OP_PRINT_URI
, 0, NULL
, NULL
, NULL
)) == NULL
)
842 return (IPPFIND_EXIT_MEMORY
);
846 temp
->parent
= parent
;
847 current
->next
= temp
;
852 temp
->prev
= current
;
856 * Start up browsing/resolving...
860 if ((err
= DNSServiceCreateConnection(&dnssd_ref
)) != kDNSServiceErr_NoError
)
862 _cupsLangPrintf(stderr
, _("ippfind: Unable to use Bonjour: %s"),
863 dnssd_error_string(err
));
864 return (IPPFIND_EXIT_BONJOUR
);
867 #elif defined(HAVE_AVAHI)
868 if ((avahi_poll
= avahi_simple_poll_new()) == NULL
)
870 _cupsLangPrintError(stderr
, _("ippfind: Unable to use Bonjour: %s"),
872 return (IPPFIND_EXIT_BONJOUR
);
875 avahi_simple_poll_set_func(avahi_poll
, poll_callback
, NULL
);
877 avahi_client
= avahi_client_new(avahi_simple_poll_get(avahi_poll
),
878 0, client_callback
, avahi_poll
, &err
);
881 _cupsLangPrintError(stderr
, _("ippfind: Unable to use Bonjour: %s"),
882 dnssd_error_string(err
));
883 return (IPPFIND_EXIT_BONJOUR
);
885 #endif /* HAVE_DNSSD */
887 for (search
= (const char *)cupsArrayFirst(searches
);
889 search
= (const char *)cupsArrayNext(searches
))
891 char buf
[1024], /* Full name string */
892 *name
= NULL
, /* Service instance name */
893 *regtype
, /* Registration type */
894 *domain
; /* Domain, if any */
896 strlcpy(buf
, search
, sizeof(buf
));
901 else if ((regtype
= strstr(buf
, "._")) != NULL
)
909 regtype
= "_ipp._tcp";
912 for (domain
= regtype
; *domain
; domain
++)
913 if (*domain
== '.' && domain
[1] != '_')
925 * Resolve the given service instance name, regtype, and domain...
931 service
= get_service(services
, name
, regtype
, domain
);
934 service
->ref
= dnssd_ref
;
935 err
= DNSServiceResolve(&(service
->ref
),
936 kDNSServiceFlagsShareConnection
, 0, name
,
937 regtype
, domain
, resolve_callback
,
940 #elif defined(HAVE_AVAHI)
941 service
->ref
= avahi_service_resolver_new(avahi_client
, AVAHI_IF_UNSPEC
,
942 AVAHI_PROTO_UNSPEC
, name
,
944 AVAHI_PROTO_UNSPEC
, 0,
945 resolve_callback
, service
);
949 err
= avahi_client_get_errno(avahi_client
);
950 #endif /* HAVE_DNSSD */
955 * Browse for services of the given type...
959 DNSServiceRef ref
; /* Browse reference */
962 err
= DNSServiceBrowse(&ref
, kDNSServiceFlagsShareConnection
, 0, regtype
,
963 domain
, browse_callback
, services
);
968 err
= DNSServiceBrowse(&ref
, kDNSServiceFlagsShareConnection
,
969 kDNSServiceInterfaceIndexLocalOnly
, regtype
,
970 domain
, browse_local_callback
, services
);
973 #elif defined(HAVE_AVAHI)
974 if (avahi_service_browser_new(avahi_client
, AVAHI_IF_UNSPEC
,
975 AVAHI_PROTO_UNSPEC
, regtype
, domain
, 0,
976 browse_callback
, services
))
979 err
= avahi_client_get_errno(avahi_client
);
980 #endif /* HAVE_DNSSD */
985 _cupsLangPrintf(stderr
, _("ippfind: Unable to browse or resolve: %s"),
986 dnssd_error_string(err
));
989 printf("name=\"%s\"\n", name
);
991 printf("regtype=\"%s\"\n", regtype
);
994 printf("domain=\"%s\"\n", domain
);
996 return (IPPFIND_EXIT_BONJOUR
);
1001 * Process browse/resolve requests...
1004 if (bonjour_timeout
> 1.0)
1005 endtime
= get_time() + bonjour_timeout
;
1007 endtime
= get_time() + 300.0;
1009 while (get_time() < endtime
)
1011 int process
= 0; /* Process services? */
1014 int fd
= DNSServiceRefSockFD(dnssd_ref
);
1015 /* File descriptor for DNS-SD */
1018 FD_SET(fd
, &sinput
);
1020 stimeout
.tv_sec
= 0;
1021 stimeout
.tv_usec
= 500000;
1023 if (select(fd
+ 1, &sinput
, NULL
, NULL
, &stimeout
) < 0)
1026 if (FD_ISSET(fd
, &sinput
))
1029 * Process responses...
1032 DNSServiceProcessResult(dnssd_ref
);
1037 * Time to process services...
1043 #elif defined(HAVE_AVAHI)
1046 if (avahi_simple_poll_iterate(avahi_poll
, 500) > 0)
1049 * We've been told to exit the loop. Perhaps the connection to
1053 return (IPPFIND_EXIT_BONJOUR
);
1056 if (!avahi_got_data
)
1059 * Time to process services...
1064 #endif /* HAVE_DNSSD */
1069 * Process any services that we have found...
1072 int active
= 0, /* Number of active resolves */
1073 resolved
= 0, /* Number of resolved services */
1074 processed
= 0; /* Number of processed services */
1076 for (service
= (ippfind_srv_t
*)cupsArrayFirst(services
);
1078 service
= (ippfind_srv_t
*)cupsArrayNext(services
))
1080 if (service
->is_processed
)
1083 if (service
->is_resolved
)
1086 if (!service
->ref
&& !service
->is_resolved
)
1089 * Found a service, now resolve it (but limit to 50 active resolves...)
1095 service
->ref
= dnssd_ref
;
1096 err
= DNSServiceResolve(&(service
->ref
),
1097 kDNSServiceFlagsShareConnection
, 0,
1098 service
->name
, service
->regtype
,
1099 service
->domain
, resolve_callback
,
1102 #elif defined(HAVE_AVAHI)
1103 service
->ref
= avahi_service_resolver_new(avahi_client
,
1109 AVAHI_PROTO_UNSPEC
, 0,
1115 err
= avahi_client_get_errno(avahi_client
);
1116 #endif /* HAVE_DNSSD */
1120 _cupsLangPrintf(stderr
,
1121 _("ippfind: Unable to browse or resolve: %s"),
1122 dnssd_error_string(err
));
1123 return (IPPFIND_EXIT_BONJOUR
);
1129 else if (service
->is_resolved
&& !service
->is_processed
)
1132 * Resolved, not process this service against the expressions...
1138 DNSServiceRefDeallocate(service
->ref
);
1140 avahi_record_browser_free(service
->ref
);
1141 #endif /* HAVE_DNSSD */
1143 service
->ref
= NULL
;
1146 if (!eval_expr(service
, expressions
))
1147 status
= IPPFIND_EXIT_FALSE
;
1149 service
->is_processed
= 1;
1151 else if (service
->ref
)
1156 * If we have processed all services we have discovered, then we are done.
1159 if (processed
== cupsArrayCount(services
) && bonjour_timeout
<= 1.0)
1165 return (IPPFIND_EXIT_BONJOUR
);
1173 * 'browse_callback()' - Browse devices.
1178 DNSServiceRef sdRef
, /* I - Service reference */
1179 DNSServiceFlags flags
, /* I - Option flags */
1180 uint32_t interfaceIndex
, /* I - Interface number */
1181 DNSServiceErrorType errorCode
, /* I - Error, if any */
1182 const char *serviceName
, /* I - Name of service/device */
1183 const char *regtype
, /* I - Type of service */
1184 const char *replyDomain
, /* I - Service domain */
1185 void *context
) /* I - Services array */
1188 * Only process "add" data...
1191 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1198 get_service((cups_array_t
*)context
, serviceName
, regtype
, replyDomain
);
1203 * 'browse_local_callback()' - Browse local devices.
1207 browse_local_callback(
1208 DNSServiceRef sdRef
, /* I - Service reference */
1209 DNSServiceFlags flags
, /* I - Option flags */
1210 uint32_t interfaceIndex
, /* I - Interface number */
1211 DNSServiceErrorType errorCode
, /* I - Error, if any */
1212 const char *serviceName
, /* I - Name of service/device */
1213 const char *regtype
, /* I - Type of service */
1214 const char *replyDomain
, /* I - Service domain */
1215 void *context
) /* I - Services array */
1217 ippfind_srv_t
*service
; /* Service */
1221 * Only process "add" data...
1224 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1231 service
= get_service((cups_array_t
*)context
, serviceName
, regtype
,
1233 service
->is_local
= 1;
1235 #endif /* HAVE_DNSSD */
1240 * 'browse_callback()' - Browse devices.
1245 AvahiServiceBrowser
*browser
, /* I - Browser */
1246 AvahiIfIndex interface
, /* I - Interface index (unused) */
1247 AvahiProtocol protocol
, /* I - Network protocol (unused) */
1248 AvahiBrowserEvent event
, /* I - What happened */
1249 const char *name
, /* I - Service name */
1250 const char *type
, /* I - Registration type */
1251 const char *domain
, /* I - Domain */
1252 AvahiLookupResultFlags flags
, /* I - Flags */
1253 void *context
) /* I - Services array */
1255 AvahiClient
*client
= avahi_service_browser_get_client(browser
);
1256 /* Client information */
1257 ippfind_srv_t
*service
; /* Service information */
1266 case AVAHI_BROWSER_FAILURE
:
1267 fprintf(stderr
, "DEBUG: browse_callback: %s\n",
1268 avahi_strerror(avahi_client_errno(client
)));
1270 avahi_simple_poll_quit(simple_poll
);
1273 case AVAHI_BROWSER_NEW
:
1275 * This object is new on the network. Create a device entry for it if
1276 * it doesn't yet exist.
1279 service
= get_service((cups_array_t
*)context
, name
, type
, domain
);
1281 if (flags
& AVAHI_LOOKUP_RESULT_LOCAL
)
1282 service
->is_local
= 1;
1285 case AVAHI_BROWSER_REMOVE
:
1286 case AVAHI_BROWSER_ALL_FOR_NOW
:
1287 case AVAHI_BROWSER_CACHE_EXHAUSTED
:
1294 * 'client_callback()' - Avahi client callback function.
1299 AvahiClient
*client
, /* I - Client information (unused) */
1300 AvahiClientState state
, /* I - Current state */
1301 void *context
) /* I - User data (unused) */
1307 * If the connection drops, quit.
1310 if (state
== AVAHI_CLIENT_FAILURE
)
1312 fputs("DEBUG: Avahi connection failed.\n", stderr
);
1314 avahi_simple_poll_quit(avahi_poll
);
1317 #endif /* HAVE_AVAHI */
1321 * 'compare_services()' - Compare two devices.
1324 static int /* O - Result of comparison */
1325 compare_services(ippfind_srv_t
*a
, /* I - First device */
1326 ippfind_srv_t
*b
) /* I - Second device */
1328 return (strcmp(a
->name
, b
->name
));
1333 * 'dnssd_error_string()' - Return an error string for an error code.
1336 static const char * /* O - Error message */
1337 dnssd_error_string(int error
) /* I - Error number */
1342 case kDNSServiceErr_NoError
:
1346 case kDNSServiceErr_Unknown
:
1347 return ("Unknown error.");
1349 case kDNSServiceErr_NoSuchName
:
1350 return ("Service not found.");
1352 case kDNSServiceErr_NoMemory
:
1353 return ("Out of memory.");
1355 case kDNSServiceErr_BadParam
:
1356 return ("Bad parameter.");
1358 case kDNSServiceErr_BadReference
:
1359 return ("Bad service reference.");
1361 case kDNSServiceErr_BadState
:
1362 return ("Bad state.");
1364 case kDNSServiceErr_BadFlags
:
1365 return ("Bad flags.");
1367 case kDNSServiceErr_Unsupported
:
1368 return ("Unsupported.");
1370 case kDNSServiceErr_NotInitialized
:
1371 return ("Not initialized.");
1373 case kDNSServiceErr_AlreadyRegistered
:
1374 return ("Already registered.");
1376 case kDNSServiceErr_NameConflict
:
1377 return ("Name conflict.");
1379 case kDNSServiceErr_Invalid
:
1380 return ("Invalid name.");
1382 case kDNSServiceErr_Firewall
:
1383 return ("Firewall prevents registration.");
1385 case kDNSServiceErr_Incompatible
:
1386 return ("Client library incompatible.");
1388 case kDNSServiceErr_BadInterfaceIndex
:
1389 return ("Bad interface index.");
1391 case kDNSServiceErr_Refused
:
1392 return ("Server prevents registration.");
1394 case kDNSServiceErr_NoSuchRecord
:
1395 return ("Record not found.");
1397 case kDNSServiceErr_NoAuth
:
1398 return ("Authentication required.");
1400 case kDNSServiceErr_NoSuchKey
:
1401 return ("Encryption key not found.");
1403 case kDNSServiceErr_NATTraversal
:
1404 return ("Unable to traverse NAT boundary.");
1406 case kDNSServiceErr_DoubleNAT
:
1407 return ("Unable to traverse double-NAT boundary.");
1409 case kDNSServiceErr_BadTime
:
1410 return ("Bad system time.");
1412 case kDNSServiceErr_BadSig
:
1413 return ("Bad signature.");
1415 case kDNSServiceErr_BadKey
:
1416 return ("Bad encryption key.");
1418 case kDNSServiceErr_Transient
:
1419 return ("Transient error occurred - please try again.");
1421 case kDNSServiceErr_ServiceNotRunning
:
1422 return ("Server not running.");
1424 case kDNSServiceErr_NATPortMappingUnsupported
:
1425 return ("NAT doesn't support NAT-PMP or UPnP.");
1427 case kDNSServiceErr_NATPortMappingDisabled
:
1428 return ("NAT supports NAT-PNP or UPnP but it is disabled.");
1430 case kDNSServiceErr_NoRouter
:
1431 return ("No Internet/default router configured.");
1433 case kDNSServiceErr_PollingMode
:
1434 return ("Service polling mode error.");
1436 case kDNSServiceErr_Timeout
:
1437 return ("Service timeout.");
1440 # elif defined(HAVE_AVAHI)
1441 return (avahi_strerror(error
));
1442 # endif /* HAVE_DNSSD */
1447 * 'eval_expr()' - Evaluate the expressions against the specified service.
1449 * Returns 1 for true and 0 for false.
1452 static int /* O - Result of evaluation */
1453 eval_expr(ippfind_srv_t
*service
, /* I - Service */
1454 ippfind_expr_t
*expressions
) /* I - Expressions */
1456 int logic
, /* Logical operation */
1457 result
; /* Result of current expression */
1458 ippfind_expr_t
*expression
; /* Current expression */
1459 const char *val
; /* TXT value */
1462 * Loop through the expressions...
1465 if (expressions
&& expressions
->parent
)
1466 logic
= expressions
->parent
->op
;
1468 logic
= IPPFIND_OP_AND
;
1470 for (expression
= expressions
; expression
; expression
= expression
->next
)
1472 switch (expression
->op
)
1475 case IPPFIND_OP_AND
:
1476 case IPPFIND_OP_OR
:
1477 if (expression
->child
)
1478 result
= eval_expr(service
, expression
->child
);
1480 result
= expression
->op
== IPPFIND_OP_AND
;
1482 case IPPFIND_OP_TRUE
:
1485 case IPPFIND_OP_FALSE
:
1488 case IPPFIND_OP_IS_LOCAL
:
1489 result
= service
->is_local
;
1491 case IPPFIND_OP_IS_REMOTE
:
1492 result
= !service
->is_local
;
1494 case IPPFIND_OP_DOMAIN_REGEX
:
1495 result
= !regexec(&(expression
->re
), service
->domain
, 0, NULL
, 0);
1497 case IPPFIND_OP_NAME_REGEX
:
1498 result
= !regexec(&(expression
->re
), service
->name
, 0, NULL
, 0);
1500 case IPPFIND_OP_HOST_REGEX
:
1501 result
= !regexec(&(expression
->re
), service
->host
, 0, NULL
, 0);
1503 case IPPFIND_OP_PORT_RANGE
:
1504 result
= service
->port
>= expression
->range
[0] &&
1505 service
->port
<= expression
->range
[1];
1507 case IPPFIND_OP_PATH_REGEX
:
1508 result
= !regexec(&(expression
->re
), service
->resource
, 0, NULL
, 0);
1510 case IPPFIND_OP_TXT_EXISTS
:
1511 result
= cupsGetOption(expression
->key
, service
->num_txt
,
1512 service
->txt
) != NULL
;
1514 case IPPFIND_OP_TXT_REGEX
:
1515 val
= cupsGetOption(expression
->key
, service
->num_txt
,
1518 result
= !regexec(&(expression
->re
), val
, 0, NULL
, 0);
1522 case IPPFIND_OP_URI_REGEX
:
1523 result
= !regexec(&(expression
->re
), service
->uri
, 0, NULL
, 0);
1525 case IPPFIND_OP_EXEC
:
1526 result
= exec_program(service
, expression
->num_args
,
1529 case IPPFIND_OP_LIST
:
1530 result
= list_service(service
);
1532 case IPPFIND_OP_PRINT_NAME
:
1533 _cupsLangPuts(stdout
, service
->name
);
1536 case IPPFIND_OP_PRINT_URI
:
1537 _cupsLangPuts(stdout
, service
->uri
);
1540 case IPPFIND_OP_QUIET
:
1545 if (expression
->invert
)
1548 if (logic
== IPPFIND_OP_AND
&& !result
)
1550 else if (logic
== IPPFIND_OP_OR
&& result
)
1554 return (logic
== IPPFIND_OP_AND
);
1559 * 'exec_program()' - Execute a program for a service.
1562 static int /* O - 1 if program terminated
1563 successfully, 0 otherwise. */
1564 exec_program(ippfind_srv_t
*service
, /* I - Service */
1565 int num_args
, /* I - Number of command-line args */
1566 char **args
) /* I - Command-line arguments */
1577 * 'get_service()' - Create or update a device.
1580 static ippfind_srv_t
* /* O - Service */
1581 get_service(cups_array_t
*services
, /* I - Service array */
1582 const char *serviceName
, /* I - Name of service/device */
1583 const char *regtype
, /* I - Type of service */
1584 const char *replyDomain
) /* I - Service domain */
1586 ippfind_srv_t key
, /* Search key */
1587 *service
; /* Service */
1588 char fullName
[kDNSServiceMaxDomainName
];
1589 /* Full name for query */
1593 * See if this is a new device...
1596 key
.name
= (char *)serviceName
;
1597 key
.regtype
= (char *)regtype
;
1599 for (service
= cupsArrayFind(services
, &key
);
1601 service
= cupsArrayNext(services
))
1602 if (_cups_strcasecmp(service
->name
, key
.name
))
1604 else if (!strcmp(service
->regtype
, key
.regtype
))
1608 * Yes, add the service...
1611 service
= calloc(sizeof(ippfind_srv_t
), 1);
1612 service
->name
= strdup(serviceName
);
1613 service
->domain
= strdup(replyDomain
);
1614 service
->regtype
= strdup(regtype
);
1616 cupsArrayAdd(services
, service
);
1619 * Set the "full name" of this service, which is used for queries and
1624 DNSServiceConstructFullName(fullName
, serviceName
, regtype
, replyDomain
);
1625 #else /* HAVE_AVAHI */
1626 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
, serviceName
,
1627 regtype
, replyDomain
);
1628 #endif /* HAVE_DNSSD */
1630 service
->fullName
= strdup(fullName
);
1637 * 'get_time()' - Get the current time-of-day in seconds.
1644 struct _timeb curtime
; /* Current Windows time */
1648 return (curtime
.time
+ 0.001 * curtime
.millitm
);
1651 struct timeval curtime
; /* Current UNIX time */
1653 if (gettimeofday(&curtime
, NULL
))
1656 return (curtime
.tv_sec
+ 0.000001 * curtime
.tv_usec
);
1662 * 'list_service()' - List the contents of a service.
1665 static int /* O - 1 if successful, 0 otherwise */
1666 list_service(ippfind_srv_t
*service
) /* I - Service */
1675 * 'new_expr()' - Create a new expression.
1678 static ippfind_expr_t
* /* O - New expression */
1679 new_expr(ippfind_op_t op
, /* I - Operation */
1680 int invert
, /* I - Invert result? */
1681 const char *value
, /* I - TXT key or port range */
1682 const char *regex
, /* I - Regular expression */
1683 char **args
) /* I - Pointer to argument strings */
1685 ippfind_expr_t
*temp
; /* New expression */
1688 if ((temp
= calloc(1, sizeof(ippfind_expr_t
))) == NULL
)
1692 temp
->invert
= invert
;
1694 if (op
== IPPFIND_OP_TXT_EXISTS
|| op
== IPPFIND_OP_TXT_REGEX
)
1695 temp
->key
= (char *)value
;
1696 else if (op
== IPPFIND_OP_PORT_RANGE
)
1699 * Pull port number range of the form "number", "-number" (0-number),
1700 * "number-" (number-65535), and "number-number".
1705 temp
->range
[1] = atoi(value
+ 1);
1707 else if (strchr(value
, '-'))
1709 if (sscanf(value
, "%d-%d", temp
->range
, temp
->range
+ 1) == 1)
1710 temp
->range
[1] = 65535;
1714 temp
->range
[0] = temp
->range
[1] = atoi(value
);
1720 int err
= regcomp(&(temp
->re
), regex
, REG_NOSUB
| REG_ICASE
| REG_EXTENDED
);
1724 char message
[256]; /* Error message */
1726 regerror(err
, &(temp
->re
), message
, sizeof(message
));
1727 _cupsLangPrintf(stderr
, _("ippfind: Bad regular expression: %s"),
1729 exit(IPPFIND_EXIT_SYNTAX
);
1735 int num_args
; /* Number of arguments */
1737 for (num_args
= 1; args
[num_args
]; num_args
++)
1738 if (!strcmp(args
[num_args
], ";"))
1741 temp
->args
= malloc(num_args
* sizeof(char *));
1742 memcpy(temp
->args
, args
, num_args
* sizeof(char *));
1751 * 'poll_callback()' - Wait for input on the specified file descriptors.
1753 * Note: This function is needed because avahi_simple_poll_iterate is broken
1754 * and always uses a timeout of 0 (!) milliseconds.
1755 * (Avahi Ticket #364)
1758 static int /* O - Number of file descriptors matching */
1760 struct pollfd
*pollfds
, /* I - File descriptors */
1761 unsigned int num_pollfds
, /* I - Number of file descriptors */
1762 int timeout
, /* I - Timeout in milliseconds (unused) */
1763 void *context
) /* I - User data (unused) */
1765 int val
; /* Return value */
1771 val
= poll(pollfds
, num_pollfds
, 500);
1774 fprintf(stderr
, "DEBUG: poll_callback: %s\n", strerror(errno
));
1780 #endif /* HAVE_AVAHI */
1784 * 'resolve_callback()' - Process resolve data.
1790 DNSServiceRef sdRef
, /* I - Service reference */
1791 DNSServiceFlags flags
, /* I - Data flags */
1792 uint32_t interfaceIndex
, /* I - Interface */
1793 DNSServiceErrorType errorCode
, /* I - Error, if any */
1794 const char *fullName
, /* I - Full service name */
1795 const char *hostTarget
, /* I - Hostname */
1796 uint16_t port
, /* I - Port number (network byte order) */
1797 uint16_t txtLen
, /* I - Length of TXT record data */
1798 const unsigned char *txtRecord
, /* I - TXT record data */
1799 void *context
) /* I - Service */
1801 char key
[256], /* TXT key value */
1802 *value
; /* Value from TXT record */
1803 const unsigned char *txtEnd
; /* End of TXT record */
1804 uint8_t valueLen
; /* Length of value */
1805 ippfind_srv_t
*service
= (ippfind_srv_t
*)context
;
1810 * Only process "add" data...
1813 if (errorCode
!= kDNSServiceErr_NoError
)
1815 _cupsLangPrintf(stderr
, _("ippfind: Unable to browse or resolve: %s"),
1816 dnssd_error_string(errorCode
));
1821 service
->is_resolved
= 1;
1822 service
->host
= strdup(hostTarget
);
1823 service
->port
= ntohs(port
);
1826 * Loop through the TXT key/value pairs and add them to an array...
1829 for (txtEnd
= txtRecord
+ txtLen
; txtRecord
< txtEnd
; txtRecord
+= valueLen
)
1832 * Ignore bogus strings...
1835 valueLen
= *txtRecord
++;
1837 memcpy(key
, txtRecord
, valueLen
);
1838 key
[valueLen
] = '\0';
1840 if ((value
= strchr(key
, '=')) == NULL
)
1846 * Add to array of TXT values...
1849 service
->num_txt
= cupsAddOption(key
, value
, service
->num_txt
,
1853 set_service_uri(service
);
1857 #elif defined(HAVE_AVAHI)
1860 AvahiServiceResolver
*resolver
, /* I - Resolver */
1861 AvahiIfIndex interface
, /* I - Interface */
1862 AvahiProtocol protocol
, /* I - Address protocol */
1863 AvahiBrowserEvent event
, /* I - Event */
1864 const char *serviceName
,/* I - Service name */
1865 const char *regtype
, /* I - Registration type */
1866 const char *replyDomain
,/* I - Domain name */
1867 const char *hostTarget
, /* I - FQDN */
1868 uint16_t port
, /* I - Port number */
1869 AvahiStringList
*txt
, /* I - TXT records */
1870 AvahiLookupResultFlags flags
, /* I - Lookup flags */
1871 void *context
) /* I - Service */
1873 char uri
[1024]; /* URI */
1874 key
[256], /* TXT key */
1875 *value
; /* TXT value */
1876 ippfind_srv_t
*service
= (ippfind_srv_t
*)context
;
1878 AvahiStringList
*current
; /* Current TXT key/value pair */
1881 if (event
!= AVAHI_RESOLVER_FOUND
)
1885 avahi_service_resolver_free(resolver
);
1886 avahi_simple_poll_quit(uribuf
->poll
);
1890 service
->is_resolved
= 1;
1891 service
->host
= strdup(hostTarget
);
1892 service
->port
= ntohs(port
);
1895 * Loop through the TXT key/value pairs and add them to an array...
1898 for (current
= txt
; current
; current
= current
->next
)
1901 * Ignore bogus strings...
1904 if (current
->size
> (sizeof(key
) - 1))
1907 memcpy(key
, current
->text
, current
->size
);
1908 key
[current
->size
] = '\0';
1910 if ((value
= strchr(key
, '=')) == NULL
)
1916 * Add to array of TXT values...
1919 service
->num_txt
= cupsAddOption(key
, value
, service
->num_txt
,
1923 set_service_uri(service
);
1925 #endif /* HAVE_DNSSD */
1929 * 'set_service_uri()' - Set the URI of the service.
1933 set_service_uri(ippfind_srv_t
*service
) /* I - Service */
1935 char uri
[1024]; /* URI */
1936 const char *path
, /* Resource path */
1937 *scheme
; /* URI scheme */
1940 if (!strncmp(service
->regtype
, "_http.", 6))
1943 path
= cupsGetOption("path", service
->num_txt
, service
->txt
);
1945 else if (!strncmp(service
->regtype
, "_https.", 7))
1948 path
= cupsGetOption("path", service
->num_txt
, service
->txt
);
1950 else if (!strncmp(service
->regtype
, "_ipp.", 5))
1953 path
= cupsGetOption("rp", service
->num_txt
, service
->txt
);
1955 else if (!strncmp(service
->regtype
, "_ipps.", 6))
1958 path
= cupsGetOption("rp", service
->num_txt
, service
->txt
);
1960 else if (!strncmp(service
->regtype
, "_printer.", 9))
1963 path
= cupsGetOption("rp", service
->num_txt
, service
->txt
);
1968 if (!path
|| !*path
)
1973 service
->resource
= strdup(path
);
1977 snprintf(uri
, sizeof(uri
), "/%s", path
);
1978 service
->resource
= strdup(uri
);
1981 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
,
1982 service
->host
, service
->port
, service
->resource
);
1983 service
->uri
= strdup(uri
);
1988 * 'show_usage()' - Show program usage.
1994 _cupsLangPuts(stderr
, _("Usage: ippfind [options] regtype[,subtype]"
1995 "[.domain.] ... [expression]\n"
1996 " ippfind [options] name[.regtype[.domain.]] "
1997 "... [expression]\n"
1999 " ippfind --version"));
2000 _cupsLangPuts(stderr
, "");
2001 _cupsLangPuts(stderr
, _("Options:"));
2002 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
2003 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
2004 _cupsLangPuts(stderr
, _(" -T seconds Set the browse timeout in "
2006 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
2008 _cupsLangPuts(stderr
, _(" --help Show this help."));
2009 _cupsLangPuts(stderr
, _(" --version Show program version."));
2010 _cupsLangPuts(stderr
, "");
2011 _cupsLangPuts(stderr
, _("Expressions:"));
2012 _cupsLangPuts(stderr
, _(" -P number[-number] Match port to number or range."));
2013 _cupsLangPuts(stderr
, _(" -d regex Match domain to regular expression."));
2014 _cupsLangPuts(stderr
, _(" -e utility [argument ...] ;\n"
2015 " Execute program if true."));
2016 _cupsLangPuts(stderr
, _(" -h regex Match hostname to regular expression."));
2017 _cupsLangPuts(stderr
, _(" -l List attributes."));
2018 _cupsLangPuts(stderr
, _(" -n regex Match service name to regular expression."));
2019 _cupsLangPuts(stderr
, _(" -p Print URI if true."));
2020 _cupsLangPuts(stderr
, _(" -q Quietly report match via exit code."));
2021 _cupsLangPuts(stderr
, _(" -r True if service is remote."));
2022 _cupsLangPuts(stderr
, _(" -s Print service name if true."));
2023 _cupsLangPuts(stderr
, _(" -t key True if the TXT record contains the key."));
2024 _cupsLangPuts(stderr
, _(" -u regex Match URI to regular expression."));
2025 _cupsLangPuts(stderr
, _(" --domain regex Match domain to regular expression."));
2026 _cupsLangPuts(stderr
, _(" --exec utility [argument ...] ;\n"
2027 " Execute program if true."));
2028 _cupsLangPuts(stderr
, _(" --host regex Match hostname to regular expression."));
2029 _cupsLangPuts(stderr
, _(" --ls List attributes."));
2030 _cupsLangPuts(stderr
, _(" --local True if service is local."));
2031 _cupsLangPuts(stderr
, _(" --name regex Match service name to regular expression."));
2032 _cupsLangPuts(stderr
, _(" --path regex Match resource path to regular expression."));
2033 _cupsLangPuts(stderr
, _(" --port number[-number] Match port to number or range."));
2034 _cupsLangPuts(stderr
, _(" --print Print URI if true."));
2035 _cupsLangPuts(stderr
, _(" --print-name Print service name if true."));
2036 _cupsLangPuts(stderr
, _(" --quiet Quietly report match via exit code."));
2037 _cupsLangPuts(stderr
, _(" --remote True if service is remote."));
2038 _cupsLangPuts(stderr
, _(" --txt key True if the TXT record contains the key."));
2039 _cupsLangPuts(stderr
, _(" --txt-* regex Match TXT record key to regular expression."));
2040 _cupsLangPuts(stderr
, _(" --uri regex Match URI to regular expression."));
2041 _cupsLangPuts(stderr
, "");
2042 _cupsLangPuts(stderr
, _("Modifiers:"));
2043 _cupsLangPuts(stderr
, _(" ( expressions ) Group expressions."));
2044 _cupsLangPuts(stderr
, _(" ! expression Unary NOT of expression."));
2045 _cupsLangPuts(stderr
, _(" --not expression Unary NOT of expression."));
2046 _cupsLangPuts(stderr
, _(" --false Always false."));
2047 _cupsLangPuts(stderr
, _(" --true Always true."));
2048 _cupsLangPuts(stderr
, _(" expression expression Logical AND."));
2049 _cupsLangPuts(stderr
, _(" expression --and expression\n"
2051 _cupsLangPuts(stderr
, _(" expression --or expression\n"
2053 _cupsLangPuts(stderr
, "");
2054 _cupsLangPuts(stderr
, _("Substitutions:"));
2055 _cupsLangPuts(stderr
, _(" {} URI"));
2056 _cupsLangPuts(stderr
, _(" {service_domain} Domain name"));
2057 _cupsLangPuts(stderr
, _(" {service_hostname} Fully-qualified domain name"));
2058 _cupsLangPuts(stderr
, _(" {service_name} Service instance name"));
2059 _cupsLangPuts(stderr
, _(" {service_port} Port number"));
2060 _cupsLangPuts(stderr
, _(" {service_regtype} DNS-SD registration type"));
2061 _cupsLangPuts(stderr
, _(" {service_scheme} URI scheme"));
2062 _cupsLangPuts(stderr
, _(" {service_uri} URI"));
2063 _cupsLangPuts(stderr
, _(" {txt_*} Value of TXT record key"));
2064 _cupsLangPuts(stderr
, "");
2065 _cupsLangPuts(stderr
, _("Environment Variables:"));
2066 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_DOMAIN Domain name"));
2067 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_HOSTNAME\n"
2068 " Fully-qualified domain name"));
2069 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_NAME Service instance name"));
2070 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_PORT Port number"));
2071 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_REGTYPE DNS-SD registration type"));
2072 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_SCHEME URI scheme"));
2073 _cupsLangPuts(stderr
, _(" IPPFIND_SERVICE_URI URI"));
2074 _cupsLangPuts(stderr
, _(" IPPFIND_TXT_* Value of TXT record key"));
2076 exit(IPPFIND_EXIT_TRUE
);
2081 * 'show_version()' - Show program version.
2087 _cupsLangPuts(stderr
, CUPS_SVERSION
);
2089 exit(IPPFIND_EXIT_TRUE
);