From: Michael R Sweet Date: Wed, 24 Apr 2024 21:27:17 +0000 (-0400) Subject: Convert dnssd backend to use cupsDNSSD APIs. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3b92a5acb0c04b2c4929a174927f36cccd764d9;p=thirdparty%2Fcups.git Convert dnssd backend to use cupsDNSSD APIs. --- diff --git a/backend/dnssd.c b/backend/dnssd.c index 20f889e32a..2e7e72ecf9 100644 --- a/backend/dnssd.c +++ b/backend/dnssd.c @@ -1,170 +1,91 @@ -/* - * DNS-SD discovery backend for CUPS. - * - * Copyright © 2020-2024 by OpenPrinting. - * Copyright © 2008-2018 by Apple Inc. - * - * Licensed under Apache License v2.0. See the file "LICENSE" for more - * information. - */ - -/* - * Include necessary headers. - */ +// +// DNS-SD discovery backend for CUPS. +// +// Copyright © 2020-2024 by OpenPrinting. +// Copyright © 2008-2018 by Apple Inc. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// #include "backend-private.h" #include -#ifdef HAVE_MDNSRESPONDER -# include -#endif /* HAVE_MDNSRESPONDER */ -#ifdef HAVE_AVAHI -# include -# include -# include -# include -# include -# include -#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX -#endif /* HAVE_AVAHI */ +#include -/* - * Device structure... - */ +// +// Device structure... +// typedef enum { - CUPS_DEVICE_PRINTER = 0, /* lpd://... */ - CUPS_DEVICE_IPPS, /* ipps://... */ - CUPS_DEVICE_IPP, /* ipp://... */ - CUPS_DEVICE_FAX_IPP, /* ipp://... */ - CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */ - CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */ + CUPS_DEVICE_PRINTER = 0, // lpd://... + CUPS_DEVICE_IPPS, // ipps://... + CUPS_DEVICE_IPP, // ipp://... + CUPS_DEVICE_FAX_IPP, // ipp://... + CUPS_DEVICE_PDL_DATASTREAM, // socket://... + CUPS_DEVICE_RIOUSBPRINT // riousbprint://... } cups_devtype_t; typedef struct { -#ifdef HAVE_MDNSRESPONDER - DNSServiceRef ref; /* Service reference for query */ -#endif /* HAVE_MDNSRESPONDER */ -#ifdef HAVE_AVAHI - AvahiRecordBrowser *ref; /* Browser for query */ -#endif /* HAVE_AVAHI */ - char *name, /* Service name */ - *domain, /* Domain name */ - *fullName, /* Full name */ - *make_and_model, /* Make and model from TXT record */ - *device_id, /* 1284 device ID from TXT record */ - *uuid; /* UUID from TXT record */ - cups_devtype_t type; /* Device registration type */ - int priority, /* Priority associated with type */ - cups_shared, /* CUPS shared printer? */ - sent; /* Did we list the device? */ + cups_dnssd_query_t *query; // Query request + char *name, // Service name + *domain, // Domain name + *fullname, // Full name + *make_and_model, // Make and model from TXT record + *device_id, // 1284 device ID from TXT record + *location, // Location from TXT record + *uuid; // UUID from TXT record + cups_devtype_t type; // Device registration type + int priority; // Priority associated with type + bool cups_shared, // CUPS shared printer? + sent; // Did we list the device? } cups_device_t; -/* - * Local globals... - */ +// +// Local globals... +// -static int job_canceled = 0; - /* Set to 1 on SIGTERM */ -#ifdef HAVE_AVAHI -static AvahiSimplePoll *simple_poll = NULL; - /* Poll information */ -static int got_data = 0; /* Got data from poll? */ -static int browsers = 0; /* Number of running browsers */ -#endif /* HAVE_AVAHI */ +static cups_array_t *Devices = NULL;// Found devices +static cups_mutex_t DevicesMutex = CUPS_MUTEX_INITIALIZER; + // Mutex for devices array +static int JobCanceled = 0;// Set to 1 on SIGTERM -/* - * Local functions... - */ - -#ifdef HAVE_MDNSRESPONDER -static void browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8); -static void browse_local_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8); -#endif /* HAVE_MDNSRESPONDER */ -#ifdef HAVE_AVAHI -static void browse_callback(AvahiServiceBrowser *browser, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *serviceName, - const char *regtype, - const char *replyDomain, - AvahiLookupResultFlags flags, - void *context); -static void client_callback(AvahiClient *client, - AvahiClientState state, - void *context); -#endif /* HAVE_AVAHI */ +// +// Local functions... +// +static void browse_callback(cups_dnssd_browse_t *browser, void *data, cups_dnssd_flags_t flags, uint32_t if_index, const char *name, const char *regtype, const char *domain); static int compare_devices(cups_device_t *a, cups_device_t *b, void *data); +static void error_cb(void *data, const char *message); static void exec_backend(char **argv) _CUPS_NORETURN; -static cups_device_t *get_device(cups_array_t *devices, const char *serviceName, const char *regtype, const char *replyDomain) _CUPS_NONNULL(1,2,3,4); -#ifdef HAVE_MDNSRESPONDER -static void query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) _CUPS_NONNULL(1,5,9,11); -#elif defined(HAVE_AVAHI) -static int poll_callback(struct pollfd *pollfds, - unsigned int num_pollfds, int timeout, - void *context); -static void query_callback(AvahiRecordBrowser *browser, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, uint16_t rrclass, - uint16_t rrtype, const void *rdata, - size_t rdlen, - AvahiLookupResultFlags flags, - void *context); -#endif /* HAVE_MDNSRESPONDER */ +static cups_device_t *get_device(const char *serviceName, const char *regtype, const char *replyDomain); +static void query_callback(cups_dnssd_query_t *query, cups_device_t *device, cups_dnssd_flags_t flags, uint32_t if_index, const char *fullname, uint16_t rrtype, const void *qdata, uint16_t qlen); static void sigterm_handler(int sig); static void unquote(char *dst, const char *src, size_t dstsize) _CUPS_NONNULL(1,2); -/* - * 'main()' - Browse for printers. - */ +// +// 'main()' - Browse for printers. +// -int /* O - Exit status */ -main(int argc, /* I - Number of command-line args */ - char *argv[]) /* I - Command-line arguments */ +int // O - Exit status +main(int argc, // I - Number of command-line args + char *argv[]) // I - Command-line arguments { - const char *name; /* Backend name */ - cups_array_t *devices; /* Device array */ - cups_device_t *device; /* Current device */ - char uriName[1024]; /* Unquoted fullName for URI */ -#ifdef HAVE_MDNSRESPONDER - int fd; /* Main file descriptor */ - fd_set input; /* Input set for select() */ - struct timeval timeout; /* Timeout for select() */ - DNSServiceRef main_ref, /* Main service reference */ - fax_ipp_ref, /* IPP fax service reference */ - ipp_ref, /* IPP service reference */ - ipp_tls_ref, /* IPP w/TLS service reference */ - ipps_ref, /* IPP service reference */ - local_fax_ipp_ref, /* Local IPP fax service reference */ - local_ipp_ref, /* Local IPP service reference */ - local_ipp_tls_ref, /* Local IPP w/TLS service reference */ - local_ipps_ref, /* Local IPP service reference */ - local_printer_ref, /* Local LPD service reference */ - pdl_datastream_ref, /* AppSocket service reference */ - printer_ref, /* LPD service reference */ - riousbprint_ref; /* Remote IO service reference */ -#endif /* HAVE_MDNSRESPONDER */ -#ifdef HAVE_AVAHI - AvahiClient *client; /* Client information */ - int error; /* Error code, if any */ -#endif /* HAVE_AVAHI */ - struct sigaction action; /* Actions for POSIX signals */ - + const char *name; // Backend name + cups_device_t *device; // Current device + char uriName[1024]; // Unquoted fullname for URI + cups_dnssd_t *dnssd; // DNS-SD context + struct sigaction action; // Actions for POSIX signals + time_t start; // Start time - /* - * Don't buffer stderr, and catch SIGTERM... - */ + // Don't buffer stderr, and catch SIGTERM... setbuf(stderr, NULL); memset(&action, 0, sizeof(action)); @@ -173,24 +94,18 @@ main(int argc, /* I - Number of command-line args */ action.sa_handler = sigterm_handler; sigaction(SIGTERM, &action, NULL); - /* - * Check command-line... - */ - + // Check command-line... if (argc >= 6) + { exec_backend(argv); + } else if (argc != 1) { - _cupsLangPrintf(stderr, - _("Usage: %s job-id user title copies options [file]"), - argv[0]); + _cupsLangPrintf(stderr, _("Usage: %s job-id user title copies options [file]"), argv[0]); return (1); } - /* - * Only do discovery when run as "dnssd"... - */ - + // Only do discovery when run as "dnssd"... if ((name = strrchr(argv[0], '/')) != NULL) name ++; else @@ -199,294 +114,92 @@ main(int argc, /* I - Number of command-line args */ if (strcmp(name, "dnssd")) return (0); - /* - * Create an array to track devices... - */ - - devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL); - - /* - * Browse for different kinds of printers... - */ + // Create an array to track devices... + Devices = cupsArrayNew((cups_array_func_t)compare_devices, /*cb_data*/NULL); -#ifdef HAVE_MDNSRESPONDER - if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError) - { - perror("ERROR: Unable to create service connection"); + // Browse for different kinds of printers... + if ((dnssd = cupsDNSSDNew(error_cb, /*cb_data*/NULL)) == NULL) return (1); - } - - fd = DNSServiceRefSockFD(main_ref); - - fax_ipp_ref = main_ref; - DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0, - "_fax-ipp._tcp", NULL, browse_callback, devices); - - ipp_ref = main_ref; - DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0, - "_ipp._tcp", NULL, browse_callback, devices); - ipp_tls_ref = main_ref; - DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0, - "_ipp-tls._tcp", NULL, browse_callback, devices); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_fax-ipp._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipp._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipp-tls._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipps._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_pdl-datastream._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_printer._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); + cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_riousbprint._tcp", /*domain*/NULL, (cups_dnssd_browse_cb_t)browse_callback, /*cb_data*/NULL); - ipps_ref = main_ref; - DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0, - "_ipps._tcp", NULL, browse_callback, devices); + // Loop until we are killed... + start = time(NULL); - local_fax_ipp_ref = main_ref; - DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection, - kDNSServiceInterfaceIndexLocalOnly, - "_fax-ipp._tcp", NULL, browse_local_callback, devices); - - local_ipp_ref = main_ref; - DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection, - kDNSServiceInterfaceIndexLocalOnly, - "_ipp._tcp", NULL, browse_local_callback, devices); - - local_ipp_tls_ref = main_ref; - DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection, - kDNSServiceInterfaceIndexLocalOnly, - "_ipp-tls._tcp", NULL, browse_local_callback, devices); - - local_ipps_ref = main_ref; - DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection, - kDNSServiceInterfaceIndexLocalOnly, - "_ipps._tcp", NULL, browse_local_callback, devices); - - local_printer_ref = main_ref; - DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection, - kDNSServiceInterfaceIndexLocalOnly, - "_printer._tcp", NULL, browse_local_callback, devices); - - pdl_datastream_ref = main_ref; - DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0, - "_pdl-datastream._tcp", NULL, browse_callback, devices); - - printer_ref = main_ref; - DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0, - "_printer._tcp", NULL, browse_callback, devices); - - riousbprint_ref = main_ref; - DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0, - "_riousbprint._tcp", NULL, browse_callback, devices); -#endif /* HAVE_MDNSRESPONDER */ - -#ifdef HAVE_AVAHI - if ((simple_poll = avahi_simple_poll_new()) == NULL) - { - fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr); - return (0); - } - - avahi_simple_poll_set_func(simple_poll, poll_callback, NULL); - - client = avahi_client_new(avahi_simple_poll_get(simple_poll), - 0, client_callback, simple_poll, &error); - if (!client) + while (!JobCanceled) { - fputs("DEBUG: Unable to create Avahi client.\n", stderr); - return (0); - } - - browsers = 6; - avahi_service_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_fax-ipp._tcp", NULL, 0, - browse_callback, devices); - avahi_service_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_ipp._tcp", NULL, 0, - browse_callback, devices); - avahi_service_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_ipp-tls._tcp", NULL, 0, - browse_callback, devices); - avahi_service_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_ipps._tcp", NULL, 0, - browse_callback, devices); - avahi_service_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_pdl-datastream._tcp", - NULL, 0, - browse_callback, - devices); - avahi_service_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_printer._tcp", NULL, 0, - browse_callback, devices); -#endif /* HAVE_AVAHI */ - - /* - * Loop until we are killed... - */ - - while (!job_canceled) - { - int announce = 0; /* Announce printers? */ - -#ifdef HAVE_MDNSRESPONDER - FD_ZERO(&input); - FD_SET(fd, &input); + sleep(1); - timeout.tv_sec = 0; - timeout.tv_usec = 500000; - - if (select(fd + 1, &input, NULL, NULL, &timeout) < 0) - continue; + cupsMutexLock(&DevicesMutex); - if (FD_ISSET(fd, &input)) + if (cupsArrayGetCount(Devices) > 0) { - /* - * Process results of our browsing... - */ - - DNSServiceProcessResult(main_ref); - } - else - announce = 1; + // Announce any devices we've found... + cups_device_t *best; // Best matching device + char device_uri[1024]; // Device URI + int count; // Number of queries + int sent; // Number of sent -#elif defined(HAVE_AVAHI) - got_data = 0; - - if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0) - { - /* - * We've been told to exit the loop. Perhaps the connection to - * Avahi failed. - */ - - break; - } - - if (!got_data) - announce = 1; -#endif /* HAVE_MDNSRESPONDER */ - -/* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/ - - if (announce) - { - /* - * Announce any devices we've found... - */ - -#ifdef HAVE_MDNSRESPONDER - DNSServiceErrorType status; /* DNS query status */ -#endif /* HAVE_MDNSRESPONDER */ - cups_device_t *best; /* Best matching device */ - char device_uri[1024]; /* Device URI */ - int count; /* Number of queries */ - int sent; /* Number of sent */ - - for (device = (cups_device_t *)cupsArrayFirst(devices), - best = NULL, count = 0, sent = 0; - device; - device = (cups_device_t *)cupsArrayNext(devices)) + for (device = (cups_device_t *)cupsArrayGetFirst(Devices), best = NULL, count = 0, sent = 0; device; device = (cups_device_t *)cupsArrayGetNext(Devices)) { if (device->sent) sent ++; - if (device->ref) + if (device->query) count ++; - if (!device->ref && !device->sent) + if (!device->query && !device->sent) { - /* - * Found the device, now get the TXT record(s) for it... - */ - + // Found the device, now get the TXT record(s) for it... if (count < 50) { - fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName); - -#ifdef HAVE_MDNSRESPONDER - device->ref = main_ref; - - status = DNSServiceQueryRecord(&(device->ref), - kDNSServiceFlagsShareConnection, - 0, device->fullName, - kDNSServiceType_TXT, - kDNSServiceClass_IN, query_callback, - device); - if (status != kDNSServiceErr_NoError) - fprintf(stderr, - "ERROR: Unable to query \"%s\" for TXT records: %d\n", - device->fullName, status); - /* Users never see this */ - else - count ++; + fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullname); -#else - if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - device->fullName, - AVAHI_DNS_CLASS_IN, - AVAHI_DNS_TYPE_TXT, - 0, - query_callback, - device)) == NULL) - fprintf(stderr, - "ERROR: Unable to query \"%s\" for TXT records: %s\n", - device->fullName, - avahi_strerror(avahi_client_errno(client))); - /* Users never see this */ - else + if ((device->query = cupsDNSSDQueryNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, device->fullname, CUPS_DNSSD_RRTYPE_TXT, (cups_dnssd_query_cb_t)query_callback, device)) != NULL) count ++; -#endif /* HAVE_AVAHI */ } } else if (!device->sent) { -#ifdef HAVE_MDNSRESPONDER - /* - * Got the TXT records, now report the device... - */ - - DNSServiceRefDeallocate(device->ref); -#else - avahi_record_browser_free(device->ref); -#endif /* HAVE_MDNSRESPONDER */ - - device->ref = NULL; + // Got the TXT records, now report the device... + cupsDNSSDQueryDelete(device->query); + device->query = NULL; if (!best) + { best = device; - else if (_cups_strcasecmp(best->name, device->name) || - _cups_strcasecmp(best->domain, device->domain)) + } + else if (_cups_strcasecmp(best->name, device->name) || _cups_strcasecmp(best->domain, device->domain)) { - unquote(uriName, best->fullName, sizeof(uriName)); + unquote(uriName, best->fullname, sizeof(uriName)); if (best->uuid) - httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, - sizeof(device_uri), "dnssd", NULL, uriName, 0, - best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s", - best->uuid); + httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "dnssd", /*userpass*/NULL, uriName, /*port*/0, best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s", best->uuid); else - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, - sizeof(device_uri), "dnssd", NULL, uriName, 0, - best->cups_shared ? "/cups" : "/"); + httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "dnssd", /*userpass*/NULL, uriName, /*port*/0, best->cups_shared ? "/cups" : "/"); - cupsBackendReport("network", device_uri, best->make_and_model, - best->name, best->device_id, NULL); - best->sent = 1; + cupsBackendReport("network", device_uri, best->make_and_model, best->name, best->device_id, best->location); + best->sent = true; best = device; sent ++; } - else if (best->priority > device->priority || - (best->priority == device->priority && - best->type < device->type)) + else if (best->priority > device->priority || (best->priority == device->priority && best->type < device->type)) { - best->sent = 1; + best->sent = true; best = device; sent ++; } else { - device->sent = 1; + device->sent = true; sent ++; } @@ -495,31 +208,25 @@ main(int argc, /* I - Number of command-line args */ if (best) { - unquote(uriName, best->fullName, sizeof(uriName)); + unquote(uriName, best->fullname, sizeof(uriName)); if (best->uuid) - httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, - sizeof(device_uri), "dnssd", NULL, uriName, 0, - best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s", - best->uuid); + httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "dnssd", /*userpass*/NULL, uriName, /*port*/0, best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s", best->uuid); else - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, - sizeof(device_uri), "dnssd", NULL, uriName, 0, - best->cups_shared ? "/cups" : "/"); + httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "dnssd", /*userpass*/NULL, uriName, /*port*/0, best->cups_shared ? "/cups" : "/"); - cupsBackendReport("network", device_uri, best->make_and_model, - best->name, best->device_id, NULL); - best->sent = 1; + cupsBackendReport("network", device_uri, best->make_and_model, best->name, best->device_id, best->location); + best->sent = true; sent ++; } fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count); -#ifdef HAVE_AVAHI - if (sent == cupsArrayCount(devices) && browsers == 0) -#else - if (sent == cupsArrayCount(devices)) -#endif /* HAVE_AVAHI */ + bool done = sent == cupsArrayGetCount(Devices) && (time(NULL) - start) > 5; + + cupsMutexUnlock(&DevicesMutex); + + if (done) break; } } @@ -528,219 +235,77 @@ main(int argc, /* I - Number of command-line args */ } -#ifdef HAVE_MDNSRESPONDER -/* - * 'browse_callback()' - Browse devices. - */ +// +// 'browse_callback()' - Browse devices. +// static void browse_callback( - DNSServiceRef sdRef, /* I - Service reference */ - DNSServiceFlags flags, /* I - Option flags */ - uint32_t interfaceIndex, /* I - Interface number */ - DNSServiceErrorType errorCode, /* I - Error, if any */ - const char *serviceName, /* I - Name of service/device */ - const char *regtype, /* I - Type of service */ - const char *replyDomain, /* I - Service domain */ - void *context) /* I - Devices array */ + cups_dnssd_browse_t *browser, // I - Service browser + void *data, // I - Callback data (unused) + cups_dnssd_flags_t flags, // I - Browse flags + uint32_t if_index, // I - Interface index + const char *name, // I - Service name + const char *regtype, // I - Service type + const char *domain) // I - Domain { - fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, " - "interfaceIndex=%u, errorCode=%d, serviceName=\"%s\", " - "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", - (void *)sdRef, flags, interfaceIndex, errorCode, - serviceName, regtype, replyDomain, context); + fprintf(stderr, "DEBUG2: browse_callback(browser=%p, data=%p, flags=%x, if_index==%u, name=\"%s\", regtype=\"%s\", domain=\"%s\")\n", browser, data, flags, if_index, name, regtype, domain); - /* - * Only process "add" data... - */ - - if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) + // Only process "add" data... + if (!(flags & CUPS_DNSSD_FLAGS_ADD)) return; - /* - * Get the device... - */ - - get_device((cups_array_t *)context, serviceName, regtype, replyDomain); + // Get the device... + get_device(name, regtype, domain); } -/* - * 'browse_local_callback()' - Browse local devices. - */ +// +// 'compare_devices()' - Compare two devices. +// -static void -browse_local_callback( - DNSServiceRef sdRef, /* I - Service reference */ - DNSServiceFlags flags, /* I - Option flags */ - uint32_t interfaceIndex, /* I - Interface number */ - DNSServiceErrorType errorCode, /* I - Error, if any */ - const char *serviceName, /* I - Name of service/device */ - const char *regtype, /* I - Type of service */ - const char *replyDomain, /* I - Service domain */ - void *context) /* I - Devices array */ +static int // O - Result of comparison +compare_devices(cups_device_t *a, // I - First device + cups_device_t *b, // I - Second device + void *data) // I - Callback data (unused) { - cups_device_t *device; /* Device */ - - - fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, " - "interfaceIndex=%u, errorCode=%d, serviceName=\"%s\", " - "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", - (void *)sdRef, flags, interfaceIndex, errorCode, - serviceName, regtype, replyDomain, context); - - /* - * Only process "add" data... - */ - - if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) - return; - - /* - * Get the device... - */ - - device = get_device((cups_array_t *)context, serviceName, regtype, - replyDomain); - - /* - * Hide locally-registered devices... - */ - - fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n", - device->fullName); - device->sent = 1; -} -#endif /* HAVE_MDNSRESPONDER */ - - -#ifdef HAVE_AVAHI -/* - * 'browse_callback()' - Browse devices. - */ - -static void -browse_callback( - AvahiServiceBrowser *browser, /* I - Browser */ - AvahiIfIndex interface, /* I - Interface index (unused) */ - AvahiProtocol protocol, /* I - Network protocol (unused) */ - AvahiBrowserEvent event, /* I - What happened */ - const char *name, /* I - Service name */ - const char *type, /* I - Registration type */ - const char *domain, /* I - Domain */ - AvahiLookupResultFlags flags, /* I - Flags */ - void *context) /* I - Devices array */ -{ - AvahiClient *client = avahi_service_browser_get_client(browser); - /* Client information */ - - - (void)interface; - (void)protocol; - (void)context; - - switch (event) - { - case AVAHI_BROWSER_FAILURE: - fprintf(stderr, "DEBUG: browse_callback: %s\n", - avahi_strerror(avahi_client_errno(client))); - avahi_simple_poll_quit(simple_poll); - break; - - case AVAHI_BROWSER_NEW: - /* - * This object is new on the network. - */ - - if (flags & AVAHI_LOOKUP_RESULT_LOCAL) - { - /* - * This comes from the local machine so ignore it. - */ - - fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name); - } - else - { - /* - * Create a device entry for it if it doesn't yet exist. - */ - - get_device((cups_array_t *)context, name, type, domain); - } - break; - - case AVAHI_BROWSER_REMOVE: - case AVAHI_BROWSER_CACHE_EXHAUSTED: - break; + (void)data; - case AVAHI_BROWSER_ALL_FOR_NOW: - browsers--; - break; - } + return (strcmp(a->name, b->name)); } -/* - * 'client_callback()' - Avahi client callback function. - */ +// +// 'error_cb()' - Log an error message. +// static void -client_callback( - AvahiClient *client, /* I - Client information (unused) */ - AvahiClientState state, /* I - Current state */ - void *context) /* I - User data (unused) */ -{ - (void)client; - (void)context; - - /* - * If the connection drops, quit. - */ - - if (state == AVAHI_CLIENT_FAILURE) - { - fputs("DEBUG: Avahi connection failed.\n", stderr); - avahi_simple_poll_quit(simple_poll); - } -} -#endif /* HAVE_AVAHI */ - - -/* - * 'compare_devices()' - Compare two devices. - */ - -static int /* O - Result of comparison */ -compare_devices(cups_device_t *a, /* I - First device */ - cups_device_t *b, /* I - Second device */ - void *data) /* I - Unused */ +error_cb(void *data, // I - Callback data (unused) + const char *message) // I - Error message { (void)data; - return (strcmp(a->name, b->name)); + + fprintf(stderr, "ERROR: %s\n", message); } -/* - * 'exec_backend()' - Execute the backend that corresponds to the - * resolved service name. - */ +// +// 'exec_backend()' - Execute the backend that corresponds to the +// resolved service name. +// static void -exec_backend(char **argv) /* I - Command-line arguments */ +exec_backend(char **argv) // I - Command-line arguments { - const char *resolved_uri, /* Resolved device URI */ - *cups_serverbin; /* Location of programs */ - char scheme[1024], /* Scheme from URI */ - *ptr, /* Pointer into scheme */ - filename[1024]; /* Backend filename */ + const char *resolved_uri, // Resolved device URI + *cups_serverbin; // Location of programs + char scheme[1024], // Scheme from URI + *ptr, // Pointer into scheme + filename[1024]; // Backend filename - /* - * Resolve the device URI... - */ - - job_canceled = -1; + // Resolve the device URI... + JobCanceled = -1; while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL) { @@ -786,12 +351,12 @@ exec_backend(char **argv) /* I - Command-line arguments */ } -/* - * 'device_type()' - Get DNS-SD type enumeration from string. - */ +// +// 'device_type()' - Get DNS-SD type enumeration from string. +// -static cups_devtype_t /* O - Device type */ -device_type(const char *regtype) /* I - Service registration type */ +static cups_devtype_t // O - Device type +device_type(const char *regtype) // I - Service registration type { #ifdef HAVE_AVAHI if (!strcmp(regtype, "_ipp._tcp")) @@ -815,247 +380,131 @@ device_type(const char *regtype) /* I - Service registration type */ return (CUPS_DEVICE_PRINTER); else if (!strcmp(regtype, "_pdl-datastream._tcp.")) return (CUPS_DEVICE_PDL_DATASTREAM); -#endif /* HAVE_AVAHI */ +#endif // HAVE_AVAHI return (CUPS_DEVICE_RIOUSBPRINT); } -/* - * 'get_device()' - Create or update a device. - */ +// +// 'get_device()' - Create or update a device. +// -static cups_device_t * /* O - Device */ -get_device(cups_array_t *devices, /* I - Device array */ - const char *serviceName, /* I - Name of service/device */ - const char *regtype, /* I - Type of service */ - const char *replyDomain) /* I - Service domain */ +static cups_device_t * // O - Device +get_device(const char *name, // I - Name of service/device + const char *regtype, // I - Type of service + const char *domain) // I - Service domain { - cups_device_t key, /* Search key */ - *device; /* Device */ - char fullName[kDNSServiceMaxDomainName]; - /* Full name for query */ + cups_device_t key, // Search key + *device; // Device + char fullname[1024]; // Full name for query - /* - * See if this is a new device... - */ + // See if this is a new device... + cupsMutexLock(&DevicesMutex); - key.name = (char *)serviceName; + key.name = (char *)name; key.type = device_type(regtype); - for (device = cupsArrayFind(devices, &key); - device; - device = cupsArrayNext(devices)) + for (device = cupsArrayFind(Devices, &key); device; device = cupsArrayGetNext(Devices)) + { if (_cups_strcasecmp(device->name, key.name)) + { + // Out of matches... break; + } else if (device->type == key.type) { - if (!_cups_strcasecmp(device->domain, "local.") && - _cups_strcasecmp(device->domain, replyDomain)) + // Match! + if (!_cups_strcasecmp(device->domain, "local.") && _cups_strcasecmp(device->domain, domain)) { - /* - * Update the .local listing to use the "global" domain name instead. - * The backend will try local lookups first, then the global domain name. - */ - + // Update the .local listing to use the "global" domain name instead. + // The backend will try local lookups first, then the global domain name. free(device->domain); - device->domain = strdup(replyDomain); - -#ifdef HAVE_MDNSRESPONDER - DNSServiceConstructFullName(fullName, device->name, regtype, - replyDomain); -#else /* HAVE_AVAHI */ - avahi_service_name_join(fullName, kDNSServiceMaxDomainName, - serviceName, regtype, replyDomain); -#endif /* HAVE_MDNSRESPONDER */ - - free(device->fullName); - device->fullName = strdup(fullName); + device->domain = strdup(domain); + + cupsDNSSDAssembleFullName(fullname, sizeof(fullname), name, regtype, domain); + free(device->fullname); + device->fullname = strdup(fullname); } + cupsMutexUnlock(&DevicesMutex); + return (device); } + } - /* - * Yes, add the device... - */ - + // New device, add it... if ((device = calloc(1, sizeof(cups_device_t))) == NULL) { perror("DEBUG: Out of memory adding a device"); return (NULL); } - device->name = strdup(serviceName); - device->domain = strdup(replyDomain); + device->name = strdup(name); + device->domain = strdup(domain); device->type = key.type; device->priority = 50; - cupsArrayAdd(devices, device); - - /* - * Set the "full name" of this service, which is used for queries... - */ + cupsArrayAdd(Devices, device); -#ifdef HAVE_MDNSRESPONDER - DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain); -#else /* HAVE_AVAHI */ - avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain); -#endif /* HAVE_MDNSRESPONDER */ + // Set the "full name" of this service, which is used for queries... + cupsDNSSDAssembleFullName(fullname, sizeof(fullname), name, regtype, domain); + device->fullname = strdup(fullname); - device->fullName = strdup(fullName); + cupsMutexUnlock(&DevicesMutex); return (device); } -#ifdef HAVE_AVAHI -/* - * 'poll_callback()' - Wait for input on the specified file descriptors. - * - * Note: This function is needed because avahi_simple_poll_iterate is broken - * and always uses a timeout of 0 (!) milliseconds. - * (https://github.com/lathiat/avahi/issues/127) - */ - -static int /* O - Number of file descriptors matching */ -poll_callback( - struct pollfd *pollfds, /* I - File descriptors */ - unsigned int num_pollfds, /* I - Number of file descriptors */ - int timeout, /* I - Timeout in milliseconds (unused) */ - void *context) /* I - User data (unused) */ -{ - int val; /* Return value */ - - - (void)timeout; - (void)context; - - val = poll(pollfds, num_pollfds, 500); - - if (val < 0) - fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno)); - else if (val > 0) - got_data = 1; - - return (val); -} -#endif /* HAVE_AVAHI */ - - -#ifdef HAVE_MDNSRESPONDER -/* - * 'query_callback()' - Process query data. - */ - -static void -query_callback( - DNSServiceRef sdRef, /* I - Service reference */ - DNSServiceFlags flags, /* I - Data flags */ - uint32_t interfaceIndex, /* I - Interface */ - DNSServiceErrorType errorCode, /* I - Error, if any */ - const char *fullName, /* I - Full service name */ - uint16_t rrtype, /* I - Record type */ - uint16_t rrclass, /* I - Record class */ - uint16_t rdlen, /* I - Length of record data */ - const void *rdata, /* I - Record data */ - uint32_t ttl, /* I - Time-to-live */ - void *context) /* I - Device */ -{ -# else -/* - * 'query_callback()' - Process query data. - */ +// +// 'query_callback()' - Process query data. +// static void query_callback( - AvahiRecordBrowser *browser, /* I - Record browser */ - AvahiIfIndex interfaceIndex, - /* I - Interface index (unused) */ - AvahiProtocol protocol, /* I - Network protocol (unused) */ - AvahiBrowserEvent event, /* I - What happened? */ - const char *fullName, /* I - Service name */ - uint16_t rrclass, /* I - Record class */ - uint16_t rrtype, /* I - Record type */ - const void *rdata, /* I - TXT record */ - size_t rdlen, /* I - Length of TXT record */ - AvahiLookupResultFlags flags, /* I - Flags */ - void *context) /* I - Device */ + cups_dnssd_query_t *query, // I - Query request + cups_device_t *device, // I - Device + cups_dnssd_flags_t flags, // I - Query flags + uint32_t if_index, // I - Response interface index + const char *fullname, // I - Fullname + uint16_t rrtype, // I - RR type (TXT) + const void *qdata, // I - Data + uint16_t qlen) // I - Length of data { - AvahiClient *client = avahi_record_browser_get_client(browser); - /* Client information */ -# endif /* HAVE_MDNSRESPONDER */ - char *ptr; /* Pointer into string */ - cups_device_t *device = (cups_device_t *)context; - /* Device */ - const uint8_t *data, /* Pointer into data */ - *datanext, /* Next key/value pair */ - *dataend; /* End of entire TXT record */ - uint8_t datalen; /* Length of current key/value pair */ - char key[256], /* Key string */ - value[256], /* Value string */ - make_and_model[512], /* Manufacturer and model */ - model[256], /* Model */ - pdl[256], /* PDL */ - device_id[2048]; /* 1284 device ID */ - - -# ifdef HAVE_MDNSRESPONDER - fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, " - "interfaceIndex=%u, errorCode=%d, fullName=\"%s\", " - "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, " - "context=%p)\n", - (void *)sdRef, flags, interfaceIndex, errorCode, fullName, rrtype, rrclass, rdlen, rdata, ttl, context); - - /* - * Only process "add" data... - */ - - if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) - return; - -# else - fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%u, " - "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, " - "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n", - browser, interfaceIndex, protocol, event, fullName, rrclass, rrtype, rdata, (unsigned)rdlen, flags, context); - - /* - * Only process "add" data... - */ - - if (event != AVAHI_BROWSER_NEW) - { - if (event == AVAHI_BROWSER_FAILURE) - fprintf(stderr, "ERROR: %s\n", - avahi_strerror(avahi_client_errno(client))); - + char *ptr; // Pointer into string + const uint8_t *data, // Pointer into data + *datanext, // Next key/value pair + *dataend; // End of entire TXT record + uint8_t datalen; // Length of current key/value pair + char key[256], // Key string + value[256], // Value string + make_and_model[512], // Manufacturer and model + model[256], // Model + pdl[256], // PDL + device_id[2048]; // 1284 device ID + + + fprintf(stderr, "DEBUG2: query_callback(query=%p, device=%p, flags=%x, if_index=%u, fullname=\"%s\", rrtype=%u, qdata=%p, qlen=%u)\n", query, device, flags, if_index, fullname, rrtype, qdata, qlen); + + // Only process "add" data... + if (!(flags & CUPS_DNSSD_FLAGS_ADD)) return; - } -# endif /* HAVE_MDNSRESPONDER */ - - /* - * Pull out the priority and make and model from the TXT - * record and save it... - */ + // Pull out the priority, location, make and model, and pdl list from the TXT + // record and save it... device_id[0] = '\0'; make_and_model[0] = '\0'; pdl[0] = '\0'; cupsCopyString(model, "Unknown", sizeof(model)); - for (data = rdata, dataend = data + rdlen; - data < dataend; - data = datanext) + for (data = qdata, dataend = data + qlen; data < dataend; data = datanext) { - /* - * Read a key/value pair starting with an 8-bit length. Since the - * length is 8 bits and the size of the key/value buffers is 256, we - * don't need to check for overflow... - */ - + // Read a key/value pair starting with an 8-bit length. Since the + // length is 8 bits and the size of the key/value buffers is 256, we + // don't need to check for overflow... datalen = *data++; if (!datalen || (data + datalen) > dataend) @@ -1075,46 +524,43 @@ query_callback( memcpy(value, data, (size_t)(datanext - data)); value[datanext - data] = '\0'; - fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n", - key, value); + fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n", key, value); } else { - fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n", - key); + fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n", key); continue; } if (!_cups_strncasecmp(key, "usb_", 4)) { - /* - * Add USB device ID information... - */ - + // Add USB device ID information... ptr = device_id + strlen(device_id); snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value); } - if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") || - !_cups_strcasecmp(key, "usb_MANUFACTURER")) + if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") || !_cups_strcasecmp(key, "usb_MANUFACTURER")) + { cupsCopyString(make_and_model, value, sizeof(make_and_model)); + } else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL")) + { cupsCopyString(model, value, sizeof(model)); + } else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript")) { if (value[0] == '(') { - /* - * Strip parenthesis... - */ - + // Strip parenthesis... if ((ptr = value + strlen(value) - 1) > value && *ptr == ')') *ptr = '\0'; cupsCopyString(model, value + 1, sizeof(model)); } else + { cupsCopyString(model, value, sizeof(model)); + } } else if (!_cups_strcasecmp(key, "ty")) { @@ -1124,25 +570,29 @@ query_callback( *ptr = '\0'; } else if (!_cups_strcasecmp(key, "pdl")) + { cupsCopyString(pdl, value, sizeof(pdl)); + } else if (!_cups_strcasecmp(key, "priority")) + { device->priority = atoi(value); - else if ((device->type == CUPS_DEVICE_IPP || - device->type == CUPS_DEVICE_IPPS || - device->type == CUPS_DEVICE_PRINTER) && - !_cups_strcasecmp(key, "printer-type")) + } + else if ((device->type == CUPS_DEVICE_IPP || device->type == CUPS_DEVICE_IPPS || device->type == CUPS_DEVICE_PRINTER) && !_cups_strcasecmp(key, "printer-type")) { - /* - * This is a CUPS printer! - */ - - device->cups_shared = 1; + // This is a CUPS printer. + device->cups_shared = true; if (device->type == CUPS_DEVICE_PRINTER) - device->sent = 1; + device->sent = true; + } + else if (!_cups_strcasecmp(key, "note") && value[0]) + { + device->location = strdup(value); } else if (!_cups_strcasecmp(key, "UUID")) + { device->uuid = strdup(value); + } } if (device->device_id) @@ -1151,33 +601,28 @@ query_callback( if (!device_id[0] && strcmp(model, "Unknown")) { if (make_and_model[0]) - snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", - make_and_model, model); + { + snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make_and_model, model); + } else if (!_cups_strncasecmp(model, "designjet ", 10)) + { snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s;", model + 10); + } else if (!_cups_strncasecmp(model, "stylus ", 7)) + { snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s;", model + 7); + } else if ((ptr = strchr(model, ' ')) != NULL) { - /* - * Assume the first word is the make... - */ - + // Assume the first word is the make... memcpy(make_and_model, model, (size_t)(ptr - model)); make_and_model[ptr - model] = '\0'; - snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", - make_and_model, ptr + 1); + snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make_and_model, ptr + 1); } } - if (device_id[0] && - !strstr(device_id, "CMD:") && - !strstr(device_id, "COMMAND SET:") && - (strstr(pdl, "application/pdf") || - strstr(pdl, "application/postscript") || - strstr(pdl, "application/vnd.hp-PCL") || - strstr(pdl, "image/"))) + if (device_id[0] && !strstr(device_id, "CMD:") && !strstr(device_id, "COMMAND SET:") && (strstr(pdl, "application/pdf") || strstr(pdl, "application/postscript") || strstr(pdl, "application/vnd.hp-PCL") || strstr(pdl, "image/"))) { value[0] = '\0'; if (strstr(pdl, "application/pdf")) @@ -1189,7 +634,7 @@ query_callback( for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/")) { char *valptr = value + strlen(value); - /* Pointer into value */ + // Pointer into value if (valptr < (value + sizeof(value) - 1)) *valptr++ = ','; @@ -1233,36 +678,38 @@ query_callback( device->make_and_model = strdup(make_and_model); } else + { device->make_and_model = strdup(model); + } } -/* - * 'sigterm_handler()' - Handle termination signals. - */ +// +// 'sigterm_handler()' - Handle termination signals. +// static void -sigterm_handler(int sig) /* I - Signal number (unused) */ +sigterm_handler(int sig) // I - Signal number (unused) { (void)sig; - if (job_canceled) + if (JobCanceled) _exit(CUPS_BACKEND_OK); else - job_canceled = 1; + JobCanceled = 1; } -/* - * 'unquote()' - Unquote a name string. - */ +// +// 'unquote()' - Unquote a name string. +// static void -unquote(char *dst, /* I - Destination buffer */ - const char *src, /* I - Source string */ - size_t dstsize) /* I - Size of destination buffer */ +unquote(char *dst, // I - Destination buffer + const char *src, // I - Source string + size_t dstsize) // I - Size of destination buffer { - char *dstend = dst + dstsize - 1; /* End of destination buffer */ + char *dstend = dst + dstsize - 1; // End of destination buffer while (*src && dst < dstend) @@ -1270,17 +717,20 @@ unquote(char *dst, /* I - Destination buffer */ if (*src == '\\') { src ++; - if (isdigit(src[0] & 255) && isdigit(src[1] & 255) && - isdigit(src[2] & 255)) + if (isdigit(src[0] & 255) && isdigit(src[1] & 255) && isdigit(src[2] & 255)) { *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0'; src += 3; } else + { *dst++ = *src++; + } } else + { *dst++ = *src ++; + } } *dst = '\0';