]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippdiscover.c
Import CUPS v1.7.1
[thirdparty/cups.git] / test / ippdiscover.c
CommitLineData
06399b6e 1/*
61515785 2 * "$Id: ippdiscover.c 10983 2013-05-13 23:57:32Z msweet $"
06399b6e
MS
3 *
4 * ippdiscover command for CUPS.
5 *
6 * Copyright 2007-2013 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 */
20
21
22/*
23 * Include necessary headers.
24 */
25
26#include <cups/cups-private.h>
27#ifdef HAVE_DNSSD
28# include <dns_sd.h>
29# ifdef WIN32
30# pragma comment(lib, "dnssd.lib")
31# endif /* WIN32 */
32#endif /* HAVE_DNSSD */
33#ifdef HAVE_AVAHI
34# include <avahi-client/client.h>
35# include <avahi-client/lookup.h>
36# include <avahi-common/simple-watch.h>
37# include <avahi-common/domain.h>
38# include <avahi-common/error.h>
39# include <avahi-common/malloc.h>
40#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
41#endif /* HAVE_AVAHI */
42
43
44/*
45 * Local globals...
46 */
47
48#ifdef HAVE_AVAHI
49static int got_data = 0; /* Got data from poll? */
50static AvahiSimplePoll *simple_poll = NULL;
51 /* Poll information */
52#endif /* HAVE_AVAHI */
53static const char *program = NULL;/* Program to run */
54
55
56/*
57 * Local functions...
58 */
59
60#ifdef HAVE_DNSSD
61static void DNSSD_API browse_callback(DNSServiceRef sdRef,
62 DNSServiceFlags flags,
63 uint32_t interfaceIndex,
64 DNSServiceErrorType errorCode,
65 const char *serviceName,
66 const char *regtype,
67 const char *replyDomain, void *context)
68 __attribute__((nonnull(1,5,6,7,8)));
69static void DNSSD_API resolve_cb(DNSServiceRef sdRef,
70 DNSServiceFlags flags,
71 uint32_t interfaceIndex,
72 DNSServiceErrorType errorCode,
73 const char *fullName,
74 const char *hostTarget,
75 uint16_t port, uint16_t txtLen,
76 const unsigned char *txtRecord,
77 void *context);
78#endif /* HAVE_DNSSD */
79
80#ifdef HAVE_AVAHI
81static void browse_callback(AvahiServiceBrowser *browser,
82 AvahiIfIndex interface,
83 AvahiProtocol protocol,
84 AvahiBrowserEvent event,
85 const char *serviceName,
86 const char *regtype,
87 const char *replyDomain,
88 AvahiLookupResultFlags flags,
89 void *context);
90static void client_cb(AvahiClient *client, AvahiClientState state,
91 void *simple_poll);
92static int poll_cb(struct pollfd *pollfds, unsigned int num_pollfds,
93 int timeout, void *context);
94static void resolve_cb(AvahiServiceResolver *resolver,
95 AvahiIfIndex interface,
96 AvahiProtocol protocol,
97 AvahiResolverEvent event,
98 const char *name, const char *type,
99 const char *domain, const char *host_name,
100 const AvahiAddress *address, uint16_t port,
101 AvahiStringList *txt,
102 AvahiLookupResultFlags flags, void *context);
103#endif /* HAVE_AVAHI */
104
105static void resolve_and_run(const char *name, const char *type,
106 const char *domain);
107static void unquote(char *dst, const char *src, size_t dstsize);
108static void usage(void) __attribute__((noreturn));
109
110
111/*
112 * 'main()' - Browse for printers and run the specified command.
113 */
114
115int /* O - Exit status */
116main(int argc, /* I - Number of command-line args */
117 char *argv[]) /* I - Command-line arguments */
118{
119 int i; /* Looping var */
120 const char *opt, /* Current option character */
121 *name = NULL, /* Service name */
122 *type = "_ipp._tcp", /* Service type */
123 *domain = "local."; /* Service domain */
124#ifdef HAVE_DNSSD
125 DNSServiceRef ref; /* Browsing service reference */
126#endif /* HAVE_DNSSD */
127#ifdef HAVE_AVAHI
128 AvahiClient *client; /* Client information */
129 int error; /* Error code, if any */
130#endif /* HAVE_AVAHI */
131
132
133 for (i = 1; i < argc; i ++)
134 if (!strcmp(argv[i], "snmp"))
135 snmponly = 1;
136 else if (!strcmp(argv[i], "ipp"))
137 ipponly = 1;
138 else
139 {
140 puts("Usage: ./ipp-printers [{ipp | snmp}]");
141 return (1);
142 }
143
144 /*
145 * Create an array to track devices...
146 */
147
148 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
149
150 /*
151 * Browse for different kinds of printers...
152 */
153
154 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
155 {
156 perror("ERROR: Unable to create service connection");
157 return (1);
158 }
159
160 fd = DNSServiceRefSockFD(main_ref);
161
162 ipp_ref = main_ref;
163 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
164 "_ipp._tcp", NULL, browse_callback, devices);
165
166 /*
167 * Loop until we are killed...
168 */
169
170 progress();
171
172 for (;;)
173 {
174 FD_ZERO(&input);
175 FD_SET(fd, &input);
176
177 timeout.tv_sec = 2;
178 timeout.tv_usec = 500000;
179
180 if (select(fd + 1, &input, NULL, NULL, &timeout) <= 0)
181 {
182 time_t curtime = time(NULL);
183
184 for (device = (cups_device_t *)cupsArrayFirst(devices);
185 device;
186 device = (cups_device_t *)cupsArrayNext(devices))
187 if (!device->got_resolve)
188 {
189 if (!device->ref)
190 break;
191
192 if ((curtime - device->resolve_time) > 10)
193 {
194 device->got_resolve = -1;
195 fprintf(stderr, "\rUnable to resolve \"%s\": timeout\n",
196 device->name);
197 progress();
198 }
199 else
200 break;
201 }
202
203 if (!device)
204 break;
205 }
206
207 if (FD_ISSET(fd, &input))
208 {
209 /*
210 * Process results of our browsing...
211 */
212
213 progress();
214 DNSServiceProcessResult(main_ref);
215 }
216 else
217 {
218 /*
219 * Query any devices we've found...
220 */
221
222 DNSServiceErrorType status; /* DNS query status */
223 int count; /* Number of queries */
224
225
226 for (device = (cups_device_t *)cupsArrayFirst(devices), count = 0;
227 device;
228 device = (cups_device_t *)cupsArrayNext(devices))
229 {
230 if (!device->ref && !device->sent)
231 {
232 /*
233 * Found the device, now get the TXT record(s) for it...
234 */
235
236 if (count < 50)
237 {
238 device->resolve_time = time(NULL);
239 device->ref = main_ref;
240
241 status = DNSServiceResolve(&(device->ref),
242 kDNSServiceFlagsShareConnection,
243 0, device->name, device->regtype,
244 device->domain, resolve_callback,
245 device);
246 if (status != kDNSServiceErr_NoError)
247 {
248 fprintf(stderr, "\rUnable to resolve \"%s\": %d\n",
249 device->name, status);
250 progress();
251 }
252 else
253 count ++;
254 }
255 }
256 else if (!device->sent && device->got_resolve)
257 {
258 /*
259 * Got the TXT records, now report the device...
260 */
261
262 DNSServiceRefDeallocate(device->ref);
263 device->ref = 0;
264 device->sent = 1;
265 }
266 }
267 }
268 }
269
270#ifndef DEBUG
271 fprintf(stderr, "\rFound %d printers. Now querying for capabilities...\n",
272 cupsArrayCount(devices));
273#endif /* !DEBUG */
274
275 puts("#!/bin/sh -x");
276 puts("test -d results && rm -rf results");
277 puts("mkdir results");
278 puts("CUPS_DEBUG_LEVEL=6; export CUPS_DEBUG_LEVEL");
279 puts("CUPS_DEBUG_FILTER='^(ipp|http|_ipp|_http|cupsGetResponse|cupsSend|"
280 "cupsWrite|cupsDo).*'; export CUPS_DEBUG_FILTER");
281
282 for (device = (cups_device_t *)cupsArrayFirst(devices);
283 device;
284 device = (cups_device_t *)cupsArrayNext(devices))
285 {
286 if (device->got_resolve <= 0 || device->cups_shared)
287 continue;
288
289#ifdef DEBUG
290 fprintf(stderr, "Checking \"%s\" (got_resolve=%d, cups_shared=%d, uri=%s)\n",
291 device->name, device->got_resolve, device->cups_shared, device->uri);
292#else
293 fprintf(stderr, "Checking \"%s\"...\n", device->name);
294#endif /* DEBUG */
295
296 if ((http = httpConnect(device->host, device->port)) == NULL)
297 {
298 fprintf(stderr, "Failed to connect to \"%s\": %s\n", device->name,
299 cupsLastErrorString());
300 continue;
301 }
302
303 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
304 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
305 device->uri);
306
307 response = cupsDoRequest(http, request, device->rp);
308
309 if (cupsLastError() > IPP_OK_SUBST)
310 fprintf(stderr, "Failed to query \"%s\": %s\n", device->name,
311 cupsLastErrorString());
312 else
313 {
314 if ((attr = ippFindAttribute(response, "ipp-versions-supported",
315 IPP_TAG_KEYWORD)) != NULL)
316 {
317 version = attr->values[0].string.text;
318
319 for (i = 1; i < attr->num_values; i ++)
320 if (strcmp(attr->values[i].string.text, version) > 0)
321 version = attr->values[i].string.text;
322 }
323 else
324 version = "1.0";
325
326 testfile = NULL;
327
328 if ((attr = ippFindAttribute(response, "document-format-supported",
329 IPP_TAG_MIMETYPE)) != NULL)
330 {
331 /*
332 * Figure out the test file for printing, preferring PDF and PostScript
333 * over JPEG and plain text...
334 */
335
336 for (i = 0; i < attr->num_values; i ++)
337 {
338 if (!strcasecmp(attr->values[i].string.text, "application/pdf"))
339 {
340 testfile = "testfile.pdf";
341 break;
342 }
343 else if (!strcasecmp(attr->values[i].string.text,
344 "application/postscript"))
345 testfile = "testfile.ps";
346 else if (!strcasecmp(attr->values[i].string.text, "image/jpeg") &&
347 !testfile)
348 testfile = "testfile.jpg";
349 else if (!strcasecmp(attr->values[i].string.text, "text/plain") &&
350 !testfile)
351 testfile = "testfile.txt";
352 else if (!strcasecmp(attr->values[i].string.text,
353 "application/vnd.hp-PCL") && !testfile)
354 testfile = "testfile.pcl";
355 }
356
357 if (!testfile)
358 {
359 fprintf(stderr,
360 "Printer \"%s\" reports the following IPP file formats:\n",
361 device->name);
362 for (i = 0; i < attr->num_values; i ++)
363 fprintf(stderr, " \"%s\"\n", attr->values[i].string.text);
364 }
365 }
366
367 if (!testfile && device->pdl)
368 {
369 char *pdl, /* Copy of pdl string */
370 *start, *end; /* Pointers into pdl string */
371
372
373 pdl = strdup(device->pdl);
374 for (start = device->pdl; start && *start; start = end)
375 {
376 if ((end = strchr(start, ',')) != NULL)
377 *end++ = '\0';
378
379 if (!strcasecmp(start, "application/pdf"))
380 {
381 testfile = "testfile.pdf";
382 break;
383 }
384 else if (!strcasecmp(start, "application/postscript"))
385 testfile = "testfile.ps";
386 else if (!strcasecmp(start, "image/jpeg") && !testfile)
387 testfile = "testfile.jpg";
388 else if (!strcasecmp(start, "text/plain") && !testfile)
389 testfile = "testfile.txt";
390 else if (!strcasecmp(start, "application/vnd.hp-PCL") && !testfile)
391 testfile = "testfile.pcl";
392 }
393 free(pdl);
394
395 if (testfile)
396 {
397 fprintf(stderr,
398 "Using \"%s\" for printer \"%s\" based on TXT record pdl "
399 "info.\n", testfile, device->name);
400 }
401 else
402 {
403 fprintf(stderr,
404 "Printer \"%s\" reports the following TXT file formats:\n",
405 device->name);
406 fprintf(stderr, " \"%s\"\n", device->pdl);
407 }
408 }
409
410 if (!device->ty &&
411 (attr = ippFindAttribute(response, "printer-make-and-model",
412 IPP_TAG_TEXT)) != NULL)
413 device->ty = strdup(attr->values[0].string.text);
414
415 if (strcmp(version, "1.0") && testfile && device->ty)
416 {
417 char filename[1024], /* Filename */
418 *fileptr; /* Pointer into filename */
419 const char *typtr; /* Pointer into ty */
420
421 if (!strncasecmp(device->ty, "DeskJet", 7) ||
422 !strncasecmp(device->ty, "DesignJet", 9) ||
423 !strncasecmp(device->ty, "OfficeJet", 9) ||
424 !strncasecmp(device->ty, "Photosmart", 10))
425 strlcpy(filename, "HP_", sizeof(filename));
426 else
427 filename[0] = '\0';
428
429 fileptr = filename + strlen(filename);
430
431 if (!strncasecmp(device->ty, "Lexmark International Lexmark", 29))
432 typtr = device->ty + 22;
433 else
434 typtr = device->ty;
435
436 while (*typtr && fileptr < (filename + sizeof(filename) - 1))
437 {
438 if (isalnum(*typtr & 255) || *typtr == '-')
439 *fileptr++ = *typtr++;
440 else
441 {
442 *fileptr++ = '_';
443 typtr++;
444 }
445 }
446
447 *fileptr = '\0';
448
449 printf("# %s\n", device->name);
450 printf("echo \"Testing %s...\"\n", device->name);
451
452 if (!ipponly)
453 {
454 printf("echo \"snmpwalk -c public -v 1 -Cc %s 1.3.6.1.2.1.25 "
455 "1.3.6.1.2.1.43 1.3.6.1.4.1.2699.1\" > results/%s.snmpwalk\n",
456 device->host, filename);
457 printf("snmpwalk -c public -v 1 -Cc %s 1.3.6.1.2.1.25 "
458 "1.3.6.1.2.1.43 1.3.6.1.4.1.2699.1 | "
459 "tee -a results/%s.snmpwalk\n",
460 device->host, filename);
461 }
462
463 if (!snmponly)
464 {
465 printf("echo \"./ipptool-static -tIf %s -T 30 -d NOPRINT=1 -V %s %s "
466 "ipp-%s.test\" > results/%s.log\n", testfile, version,
467 device->uri, version, filename);
468 printf("CUPS_DEBUG_LOG=results/%s.debug_log "
469 "./ipptool-static -tIf %s -T 30 -d NOPRINT=1 -V %s %s "
470 "ipp-%s.test | tee -a results/%s.log\n", filename,
471 testfile, version, device->uri,
472 version, filename);
473 }
474
475 puts("");
476 }
477 else if (!device->ty)
478 fprintf(stderr,
479 "Ignoring \"%s\" since it doesn't provide a make and model.\n",
480 device->name);
481 else if (!testfile)
482 fprintf(stderr,
483 "Ignoring \"%s\" since it does not support a common format.\n",
484 device->name);
485 else
486 fprintf(stderr, "Ignoring \"%s\" since it only supports IPP/1.0.\n",
487 device->name);
488 }
489
490 ippDelete(response);
491 httpClose(http);
492 }
493
494 return (0);
495}
496
497
498/*
499 * 'browse_callback()' - Browse devices.
500 */
501
502static void
503browse_callback(
504 DNSServiceRef sdRef, /* I - Service reference */
505 DNSServiceFlags flags, /* I - Option flags */
506 uint32_t interfaceIndex, /* I - Interface number */
507 DNSServiceErrorType errorCode, /* I - Error, if any */
508 const char *serviceName, /* I - Name of service/device */
509 const char *regtype, /* I - Type of service */
510 const char *replyDomain, /* I - Service domain */
511 void *context) /* I - Devices array */
512{
513#ifdef DEBUG
514 fprintf(stderr, "browse_callback(sdRef=%p, flags=%x, "
515 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
516 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
517 sdRef, flags, interfaceIndex, errorCode,
518 serviceName ? serviceName : "(null)",
519 regtype ? regtype : "(null)",
520 replyDomain ? replyDomain : "(null)",
521 context);
522#endif /* DEBUG */
523
524 /*
525 * Only process "add" data...
526 */
527
528 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
529 return;
530
531 /*
532 * Get the device...
533 */
534
535 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
536}
537
538
539/*
540 * 'compare_devices()' - Compare two devices.
541 */
542
543static int /* O - Result of comparison */
544compare_devices(cups_device_t *a, /* I - First device */
545 cups_device_t *b) /* I - Second device */
546{
547 int retval = strcmp(a->name, b->name);
548
549 if (retval)
550 return (retval);
551 else
552 return (-strcmp(a->regtype, b->regtype));
553}
554
555
556/*
557 * 'get_device()' - Create or update a device.
558 */
559
560static cups_device_t * /* O - Device */
561get_device(cups_array_t *devices, /* I - Device array */
562 const char *serviceName, /* I - Name of service/device */
563 const char *regtype, /* I - Type of service */
564 const char *replyDomain) /* I - Service domain */
565{
566 cups_device_t key, /* Search key */
567 *device; /* Device */
568 char fullName[kDNSServiceMaxDomainName];
569 /* Full name for query */
570
571
572 /*
573 * See if this is a new device...
574 */
575
576 key.name = (char *)serviceName;
577 key.regtype = (char *)regtype;
578
579 for (device = cupsArrayFind(devices, &key);
580 device;
581 device = cupsArrayNext(devices))
582 if (strcasecmp(device->name, key.name))
583 break;
584 else
585 {
586 if (!strcasecmp(device->domain, "local.") &&
587 strcasecmp(device->domain, replyDomain))
588 {
589 /*
590 * Update the .local listing to use the "global" domain name instead.
591 * The backend will try local lookups first, then the global domain name.
592 */
593
594 free(device->domain);
595 device->domain = strdup(replyDomain);
596
597 DNSServiceConstructFullName(fullName, device->name, regtype,
598 replyDomain);
599 free(device->fullName);
600 device->fullName = strdup(fullName);
601 }
602
603 return (device);
604 }
605
606 /*
607 * Yes, add the device...
608 */
609
610 device = calloc(sizeof(cups_device_t), 1);
611 device->name = strdup(serviceName);
612 device->domain = strdup(replyDomain);
613 device->regtype = strdup(regtype);
614
615 cupsArrayAdd(devices, device);
616
617 /*
618 * Set the "full name" of this service, which is used for queries...
619 */
620
621 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
622 device->fullName = strdup(fullName);
623
624#ifdef DEBUG
625 fprintf(stderr, "get_device: fullName=\"%s\"...\n", fullName);
626#endif /* DEBUG */
627
628 return (device);
629}
630
631
632/*
633 * 'progress()' - Show query progress.
634 */
635
636static void
637progress(void)
638{
639#ifndef DEBUG
640 const char *chars = "|/-\\";
641 static int count = 0;
642
643
644 fprintf(stderr, "\rLooking for printers %c", chars[count]);
645 fflush(stderr);
646 count = (count + 1) & 3;
647#endif /* !DEBUG */
648}
649
650
651/*
652 * 'resolve_callback()' - Process resolve data.
653 */
654
655static void
656resolve_callback(
657 DNSServiceRef sdRef, /* I - Service reference */
658 DNSServiceFlags flags, /* I - Data flags */
659 uint32_t interfaceIndex, /* I - Interface */
660 DNSServiceErrorType errorCode, /* I - Error, if any */
661 const char *fullName, /* I - Full service name */
662 const char *hostTarget, /* I - Hostname */
663 uint16_t port, /* I - Port number (network byte order) */
664 uint16_t txtLen, /* I - Length of TXT record data */
665 const unsigned char *txtRecord, /* I - TXT record data */
666 void *context) /* I - Device */
667{
668 char temp[257], /* TXT key value */
669 uri[1024]; /* Printer URI */
670 const void *value; /* Value from TXT record */
671 uint8_t valueLen; /* Length of value */
672 cups_device_t *device = (cups_device_t *)context;
673 /* Device */
674
675
676#ifdef DEBUG
677 fprintf(stderr, "\rresolve_callback(sdRef=%p, flags=%x, "
678 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
679 "hostTarget=\"%s\", port=%d, txtLen=%u, txtRecord=%p, "
680 "context=%p)\n",
681 sdRef, flags, interfaceIndex, errorCode,
682 fullName ? fullName : "(null)", hostTarget ? hostTarget : "(null)",
683 ntohs(port), txtLen, txtRecord, context);
684#endif /* DEBUG */
685
686 /*
687 * Only process "add" data...
688 */
689
690 if (errorCode != kDNSServiceErr_NoError)
691 return;
692
693 device->got_resolve = 1;
694 device->host = strdup(hostTarget);
695 device->port = ntohs(port);
696
697 /*
698 * Extract the "remote printer" key from the TXT record and save the URI...
699 */
700
701 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "rp",
702 &valueLen)) != NULL)
703 {
704 if (((char *)value)[0] == '/')
705 {
706 /*
707 * "rp" value (incorrectly) has a leading slash already...
708 */
709
710 memcpy(temp, value, valueLen);
711 temp[valueLen] = '\0';
712 }
713 else
714 {
715 /*
716 * Convert to resource by concatenating with a leading "/"...
717 */
718
719 temp[0] = '/';
720 memcpy(temp + 1, value, valueLen);
721 temp[valueLen + 1] = '\0';
722 }
723 }
724 else
725 {
726 /*
727 * Default "rp" value is blank, mapping to a path of "/"...
728 */
729
730 temp[0] = '/';
731 temp[1] = '\0';
732 }
733
734 if (!strncmp(temp, "/printers/", 10))
735 device->cups_shared = -1;
736
737 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp",
738 NULL, hostTarget, ntohs(port), temp);
739 device->uri = strdup(uri);
740 device->rp = strdup(temp);
741
742 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "ty",
743 &valueLen)) != NULL)
744 {
745 memcpy(temp, value, valueLen);
746 temp[valueLen] = '\0';
747
748 device->ty = strdup(temp);
749 }
750
751 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "pdl",
752 &valueLen)) != NULL)
753 {
754 memcpy(temp, value, valueLen);
755 temp[valueLen] = '\0';
756
757 device->pdl = strdup(temp);
758 }
759
760 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type",
761 &valueLen)) != NULL)
762 device->cups_shared = 1;
763
764 if (device->cups_shared)
765 fprintf(stderr, "\rIgnoring CUPS printer %s\n", uri);
766 else
767 fprintf(stderr, "\rFound IPP printer %s\n", uri);
768
769 progress();
770}
771
772
773/*
774 * 'unquote()' - Unquote a name string.
775 */
776
777static void
778unquote(char *dst, /* I - Destination buffer */
779 const char *src, /* I - Source string */
780 size_t dstsize) /* I - Size of destination buffer */
781{
782 char *dstend = dst + dstsize - 1; /* End of destination buffer */
783
784
785 while (*src && dst < dstend)
786 {
787 if (*src == '\\')
788 {
789 src ++;
790 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
791 isdigit(src[2] & 255))
792 {
793 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
794 src += 3;
795 }
796 else
797 *dst++ = *src++;
798 }
799 else
800 *dst++ = *src ++;
801 }
802
803 *dst = '\0';
804}
805
806
807/*
808 * 'usage()' - Show program usage and exit.
809 */
810
811static void
812usage(void)
813{
814 _cupsLangPuts(stdout, _("Usage: ippdiscover [options] -a\n"
815 " ippdiscover [options] \"service name\"\n"
816 "\n"
817 "Options:"));
818 _cupsLangPuts(stdout, _(" -a Browse for all services."));
819 _cupsLangPuts(stdout, _(" -d domain Browse/resolve in specified domain."));
820 _cupsLangPuts(stdout, _(" -p program Run specified program for each service."));
821 _cupsLangPuts(stdout, _(" -t type Browse/resolve with specified type."));
822
823 exit(0);
824}
825
826
827/*
61515785 828 * End of "$Id: ippdiscover.c 10983 2013-05-13 23:57:32Z msweet $".
06399b6e 829 */