]> git.ozlabs.org Git - ccan/commitdiff
Merge branch 'io'
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 14 Oct 2013 11:03:51 +0000 (21:33 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 14 Oct 2013 11:03:51 +0000 (21:33 +1030)
ccan/net/_info
ccan/net/net.c
ccan/net/net.h
ccan/net/test/run-bind.c
ccan/opt/helpers.c
ccan/opt/opt.c
ccan/opt/opt.h
ccan/opt/parse.c
ccan/opt/private.h
ccan/opt/test/run-set_alloc.c [new file with mode: 0644]
ccan/opt/usage.c

index 9ce0e150dfe3b53f26bc2c293497b6cf4e10e20d..b4b219fd3599094a7195e9c1a83c24bc79a8c28f 100644 (file)
@@ -66,6 +66,7 @@ int main(int argc, char *argv[])
                return 1;
 
        if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/noerr\n");
                return 0;
        }
 
index e84380fff96f27a885d05618a46b6d1d2d46cc2c..d2eaa9ecb45e58660fb80ea607210226d3385a7f 100644 (file)
@@ -1,5 +1,6 @@
 /* Licensed under BSD-MIT - see LICENSE file for details */
 #include <ccan/net/net.h>
+#include <ccan/noerr/noerr.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <poll.h>
@@ -11,6 +12,7 @@
 #include <errno.h>
 #include <stdbool.h>
 #include <netinet/in.h>
+#include <assert.h>
 
 struct addrinfo *net_client_lookup(const char *hostname,
                                   const char *service,
@@ -48,107 +50,145 @@ static bool set_nonblock(int fd, bool nonblock)
        return (fcntl(fd, F_SETFL, flags) == 0);
 }
 
-/* We only handle IPv4 and IPv6 */
-#define MAX_PROTOS 2
-
-static void remove_fd(struct pollfd pfd[],
-                     const struct addrinfo *addr[],
-                     socklen_t slen[],
-                     unsigned int *num,
-                     unsigned int i)
+static int start_connect(const struct addrinfo *addr, bool *immediate)
 {
-       memmove(pfd + i, pfd + i + 1, (*num - i - 1) * sizeof(pfd[0]));
-       memmove(addr + i, addr + i + 1, (*num - i - 1) * sizeof(addr[0]));
-       memmove(slen + i, slen + i + 1, (*num - i - 1) * sizeof(slen[0]));
-       (*num)--;
+       int fd;
+
+       *immediate = false;
+
+       fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+       if (fd == -1)
+               return fd;
+
+       if (!set_nonblock(fd, true))
+               goto close;
+
+       if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
+               /* Immediate connect. */
+               *immediate = true;
+               return fd;
+       }
+
+       if (errno == EINPROGRESS)
+               return fd;
+
+close:
+       close_noerr(fd);
+       return -1;
 }
 
-int net_connect(const struct addrinfo *addrinfo)
+
+int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
 {
-       int sockfd = -1, saved_errno;
-       unsigned int i, num;
-       const struct addrinfo *ipv4 = NULL, *ipv6 = NULL;
-       const struct addrinfo *addr[MAX_PROTOS];
-       socklen_t slen[MAX_PROTOS];
-       struct pollfd pfd[MAX_PROTOS];
+       const struct addrinfo *addr[2] = { NULL, NULL };
+       unsigned int i;
 
+       pfds[0].fd = pfds[1].fd = -1;
+       pfds[0].events = pfds[1].events = POLLOUT;
+
+       /* Give IPv6 a slight advantage, by trying it first. */
        for (; addrinfo; addrinfo = addrinfo->ai_next) {
                switch (addrinfo->ai_family) {
                case AF_INET:
-                       if (!ipv4)
-                               ipv4 = addrinfo;
+                       addr[1] = addrinfo;
                        break;
                case AF_INET6:
-                       if (!ipv6)
-                               ipv6 = addrinfo;
+                       addr[0] = addrinfo;
                        break;
+               default:
+                       continue;
                }
        }
 
-       num = 0;
-       /* We give IPv6 a slight edge by connecting it first. */
-       if (ipv6) {
-               addr[num] = ipv6;
-               slen[num] = sizeof(struct sockaddr_in6);
-               pfd[num].fd = socket(AF_INET6, ipv6->ai_socktype,
-                                    ipv6->ai_protocol);
-               if (pfd[num].fd != -1)
-                       num++;
+       /* In case we found nothing. */
+       errno = ENOENT;
+       for (i = 0; i < 2; i++) {
+               bool immediate;
+
+               if (!addr[i])
+                       continue;
+
+               pfds[i].fd = start_connect(addr[i], &immediate);
+               if (immediate) {
+                       if (pfds[!i].fd != -1)
+                               close(pfds[!i].fd);
+                       if (!set_nonblock(pfds[i].fd, false)) {
+                               close_noerr(pfds[i].fd);
+                               return -1;
+                       }
+                       return pfds[0].fd;
+               }
        }
-       if (ipv4) {
-               addr[num] = ipv4;
-               slen[num] = sizeof(struct sockaddr_in);
-               pfd[num].fd = socket(AF_INET, ipv4->ai_socktype,
-                                    ipv4->ai_protocol);
-               if (pfd[num].fd != -1)
-                       num++;
+
+       if (pfds[0].fd != -1 || pfds[1].fd != -1)
+               errno = EINPROGRESS;
+       return -1;
+}
+
+void net_connect_abort(struct pollfd pfds[2])
+{
+       unsigned int i;
+
+       for (i = 0; i < 2; i++) {
+               if (pfds[i].fd != -1)
+                       close_noerr(pfds[i].fd);
+               pfds[i].fd = -1;
        }
+}
+
+int net_connect_complete(struct pollfd pfds[2])
+{
+       unsigned int i;
+
+       assert(pfds[0].fd != -1 || pfds[1].fd != -1);
+
+       for (i = 0; i < 2; i++) {
+               int err;
+               socklen_t errlen = sizeof(err);
 
-       for (i = 0; i < num; i++) {
-               if (!set_nonblock(pfd[i].fd, true)) {
-                       remove_fd(pfd, addr, slen, &num, i--);
+               if (pfds[i].fd == -1)
                        continue;
+               if (getsockopt(pfds[i].fd, SOL_SOCKET, SO_ERROR, &err,
+                              &errlen) != 0) {
+                       net_connect_abort(pfds);
+                       return -1;
                }
-               /* Connect *can* be instant. */
-               if (connect(pfd[i].fd, addr[i]->ai_addr, slen[i]) == 0)
-                       goto got_one;
-               if (errno != EINPROGRESS) {
-                       /* Remove dead one. */
-                       remove_fd(pfd, addr, slen, &num, i--);
+               if (err == 0) {
+                       /* Don't hand them non-blocking fd! */
+                       if (!set_nonblock(pfds[i].fd, false)) {
+                               net_connect_abort(pfds);
+                               return -1;
+                       }
+                       /* Close other one. */
+                       if (pfds[!i].fd != -1)
+                               close(pfds[!i].fd);
+                       return pfds[i].fd;
                }
-               pfd[i].events = POLLOUT;
        }
 
-       while (num && poll(pfd, num, -1) != -1) {
-               for (i = 0; i < num; i++) {
-                       int err;
-                       socklen_t errlen = sizeof(err);
-                       if (!pfd[i].revents)
-                               continue;
-                       if (getsockopt(pfd[i].fd, SOL_SOCKET, SO_ERROR, &err,
-                                      &errlen) != 0)
-                               goto out;
-                       if (err == 0)
-                               goto got_one;
-
-                       /* Remove dead one. */
-                       errno = err;
-                       remove_fd(pfd, addr, slen, &num, i--);
-               }
-       }
+       /* Still going... */
+       errno = EINPROGRESS;
+       return -1;
+}
 
-got_one:
-       /* We don't want to hand them a non-blocking socket! */
-       if (set_nonblock(pfd[i].fd, false))
-               sockfd = pfd[i].fd;
+int net_connect(const struct addrinfo *addrinfo)
+{
+       struct pollfd pfds[2];
+       int sockfd;
+
+       sockfd = net_connect_async(addrinfo, pfds);
+       /* Immediate connect or error is easy. */
+       if (sockfd >= 0 || errno != EINPROGRESS)
+               return sockfd;
+
+       while (poll(pfds, 2, -1) != -1) {
+               sockfd = net_connect_complete(pfds);
+               if (sockfd >= 0 || errno != EINPROGRESS)
+                       return sockfd;
+       }
 
-out:
-       saved_errno = errno;
-       for (i = 0; i < num; i++)
-               if (pfd[i].fd != sockfd)
-                       close(pfd[i].fd);
-       errno = saved_errno;
-       return sockfd;
+       net_connect_abort(pfds);
+       return -1;
 }
 
 struct addrinfo *net_server_lookup(const char *service,
index 9ad6de56a498b12aab9a5372c9114b5def47b505..5e02fc36e7d09a329484ac5bfebf582c8c400cf3 100644 (file)
@@ -1,6 +1,10 @@
 /* Licensed under BSD-MIT - see LICENSE file for details */
 #ifndef CCAN_NET_H
 #define CCAN_NET_H
+#include <stdbool.h>
+
+struct pollfd;
+
 /**
  * net_client_lookup - look up a network name to connect to.
  * @hostname: the name to look up
@@ -16,6 +20,7 @@
  *     #include <sys/socket.h>
  *     #include <stdio.h>
  *     #include <netdb.h>
+ *     #include <poll.h>
  *     #include <err.h>
  *     ...
  *     struct addrinfo *addr;
@@ -48,6 +53,67 @@ struct addrinfo *net_client_lookup(const char *hostname,
  */
 int net_connect(const struct addrinfo *addrinfo);
 
+/**
+ * net_connect_async - initiate connect to a server
+ * @addrinfo: linked list struct addrinfo (usually from net_client_lookup).
+ * @pfds: array of two struct pollfd.
+ *
+ * This begins connecting to a server described by @addrinfo,
+ * and places the one or two file descriptors into pfds[0] and pfds[1].
+ * It returns a valid file descriptor if connect() returned immediately.
+ *
+ * Otherwise it returns -1 and sets errno, most likely EINPROGRESS.
+ * In this case, poll() on pfds and call net_connect_complete().
+ *
+ * Example:
+ *     struct pollfd pfds[2];
+ *     ...
+ *     fd = net_connect_async(addr, pfds);
+ *     if (fd < 0 && errno != EINPROGRESS)
+ *             err(1, "Failed to connect to ccan.ozlabs.org");
+ */
+int net_connect_async(const struct addrinfo *addrinfo, struct pollfd *pfds);
+
+/**
+ * net_connect_complete - complete net_connect_async call.
+ * @pfds: array of two struct pollfd handed to net_connect_async.
+ *
+ * When poll() reports some activity, this determines whether a connection
+ * has completed.  If so, it cleans up and returns the connected fd.
+ * Otherwise, it returns -1, and sets errno (usually EINPROGRESS).
+ *
+ * Example:
+ *     // After net_connect_async.
+ *     while (fd < 0 && errno == EINPROGRESS) {
+ *             // Wait for activity...
+ *             poll(pfds, 2, -1);
+ *             fd = net_connect_complete(pfds);
+ *     }
+ *     if (fd < 0)
+ *             err(1, "connecting");
+ *     printf("Connected on fd %i!\n", fd);
+ */
+int net_connect_complete(struct pollfd *pfds);
+
+/**
+ * net_connect_abort - abort a net_connect_async call.
+ * @pfds: array of two struct pollfd handed to net_connect_async.
+ *
+ * Closes the file descriptors.
+ *
+ * Example:
+ *     // After net_connect_async.
+ *     if (poll(pfds, 2, 1000) == 0) { // Timeout.
+ *             net_connect_abort(pfds);
+ *             fd = -1;
+ *     } else {
+ *             fd = net_connect_complete(pfds);
+ *             if (fd < 0)
+ *                     err(1, "connecting");
+ *     }
+ */
+void net_connect_abort(struct pollfd *pfds);
+
 /**
  * net_server_lookup - look up a service name to bind to.
  * @service: the service to look up
index 9fb2081db3396a128027b984625d8c9350bb15a2..14b38853c96053de55d1e6984c79a671a35d8c7d 100644 (file)
@@ -117,6 +117,7 @@ int main(void)
        close(fd);
        for (i = 0; i < num_fds; i++)
                close(fds[i]);
+       freeaddrinfo(addr);
 
        /* Simple UDP test. */
        addr = net_server_lookup(TEST_PORT, AF_UNSPEC, SOCK_DGRAM);
@@ -155,6 +156,7 @@ int main(void)
        if (addr->ai_next)
                ipv6_only = true;
 #endif
+       freeaddrinfo(addr);
 
        if (ipv6_only) {
                int j;
index e7ab273cdcede67a9bec9ae868ec8d8faa84cd6b..25929bd5bf705f6681a1c7303288a58a16d4453c 100644 (file)
@@ -134,7 +134,7 @@ char *opt_usage_and_exit(const char *extra)
        char *usage = opt_usage(opt_argv0, extra);
        printf("%s", usage);
        /* Don't have valgrind complain! */
-       free(usage);
+       opt_alloc.free(usage);
        opt_free_table();
        exit(0);
 }
index 94eb0d5ab7f2808e0f1a4fb155b617cebf9422b6..85de935a7d3b77fc2591b1435b1b38e79ff741cf 100644 (file)
@@ -12,6 +12,9 @@
 struct opt_table *opt_table;
 unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
 const char *opt_argv0;
+struct opt_alloc opt_alloc = {
+       malloc, realloc, free
+};
 
 /* Returns string after first '-'. */
 static const char *first_name(const char *names, unsigned *len)
@@ -150,7 +153,8 @@ static void check_opt(const struct opt_table *entry)
 
 static void add_opt(const struct opt_table *entry)
 {
-       opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
+       opt_table = opt_alloc.realloc(opt_table,
+                                     sizeof(opt_table[0]) * (opt_count+1));
        opt_table[opt_count++] = *entry;
 }
 
@@ -214,7 +218,7 @@ bool opt_early_parse(int argc, char *argv[],
 {
        int ret;
        unsigned off = 0;
-       char **tmpargv = malloc(sizeof(argv[0]) * (argc + 1));
+       char **tmpargv = opt_alloc.alloc(sizeof(argv[0]) * (argc + 1));
 
        /* We could avoid a copy and skip instead, but this is simple. */
        memcpy(tmpargv, argv, sizeof(argv[0]) * (argc + 1));
@@ -224,7 +228,7 @@ bool opt_early_parse(int argc, char *argv[],
 
        while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog)) == 1);
 
-       free(tmpargv);
+       opt_alloc.free(tmpargv);
 
        /* parse_one returns 0 on finish, -1 on error */
        return (ret == 0);
@@ -232,7 +236,7 @@ bool opt_early_parse(int argc, char *argv[],
 
 void opt_free_table(void)
 {
-       free(opt_table);
+       opt_alloc.free(opt_table);
        opt_table = NULL;
        opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
 }
@@ -260,7 +264,16 @@ void opt_log_stderr_exit(const char *fmt, ...)
 
 char *opt_invalid_argument(const char *arg)
 {
-       char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
+       char *str = opt_alloc.alloc(sizeof("Invalid argument '%s'") + strlen(arg));
        sprintf(str, "Invalid argument '%s'", arg);
        return str;
 }
+
+void opt_set_alloc(void *(*allocfn)(size_t size),
+                  void *(*reallocfn)(void *ptr, size_t size),
+                  void (*freefn)(void *ptr))
+{
+       opt_alloc.alloc = allocfn;
+       opt_alloc.realloc = reallocfn;
+       opt_alloc.free = freefn;
+}
index 8bbc8f51d162c9e19c7c9bc1c480002899922719..6c3cbee148f02ef241806b1f293b21774b4134f1 100644 (file)
@@ -21,7 +21,7 @@ struct opt_table;
  *
  * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
  * returned string to form an error message for errlog(), free() the
- * string and return false.
+ * string (or see opt_set_alloc) and return false.
  *
  * Any number of equivalent short or long options can be listed in @names,
  * separated by '|'.  Short options are a single hyphen followed by a single
@@ -60,7 +60,7 @@ struct opt_table;
  *
  * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
  * returned string to form an error message for errlog(), free() the
- * string and return false.
+ * string (or see opt_set_alloc) and return false.
  *
  * See Also:
  *     OPT_WITHOUT_ARG()
@@ -159,7 +159,7 @@ void opt_register_table(const struct opt_table *table, const char *desc);
  *
  * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
  * returned string to form an error message for errlog(), free() the
- * string and return false.
+ * string (or see opt_set_alloc) and return false.
  */
 #define opt_register_noarg(names, cb, arg, desc)                       \
        _opt_register((names), OPT_CB_NOARG((cb), 0, (arg)), (arg), (desc))
@@ -182,7 +182,7 @@ void opt_register_table(const struct opt_table *table, const char *desc);
  *
  * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
  * returned string to form an error message for errlog(), free() the
- * string and return false.
+ * string (or see opt_set_alloc) and return false.
  *
  * Example:
  * static char *explode(const char *optarg, void *unused)
@@ -296,6 +296,19 @@ bool opt_early_parse(int argc, char *argv[],
  */
 void opt_free_table(void);
 
+/**
+ * opt_set_alloc - set alloc/realloc/free function for opt to use.
+ * @allocfn: allocator function
+ * @reallocfn: reallocator function, ptr may be NULL, size never 0.
+ * @freefn: free function
+ *
+ * By default opt uses malloc/realloc/free, and simply crashes if they fail.
+ * You can set your own variants here.
+ */
+void opt_set_alloc(void *(*allocfn)(size_t size),
+                  void *(*reallocfn)(void *ptr, size_t size),
+                  void (*freefn)(void *ptr));
+
 /**
  * opt_log_stderr - print message to stderr.
  * @fmt: printf-style format.
index 5b2cec8b3d284e0b6795c87355d6ae00cf1290b9..14c7afbb083b3a0cc9eb5e5f88d46ba105464ebd 100644 (file)
@@ -116,7 +116,7 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
 
        if (problem) {
                parse_err(errlog, argv[0], o, len, problem);
-               free(problem);
+               opt_alloc.free(problem);
                return -1;
        }
 
index 12c569311b4a62d880981323de1eac82aff8ca77..8d7815c86b8a90da32c87807f0424b17291d42ae 100644 (file)
@@ -14,6 +14,13 @@ const char *next_sopt(const char *names, unsigned *i);
 const char *first_lopt(unsigned *i, unsigned *len);
 const char *next_lopt(const char *p, unsigned *i, unsigned *len);
 
+struct opt_alloc {
+       void *(*alloc)(size_t size);
+       void *(*realloc)(void *ptr, size_t size);
+       void (*free)(void *ptr);
+};
+extern struct opt_alloc opt_alloc;
+
 int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
              void (*errlog)(const char *fmt, ...));
 
diff --git a/ccan/opt/test/run-set_alloc.c b/ccan/opt/test/run-set_alloc.c
new file mode 100644 (file)
index 0000000..b30a77d
--- /dev/null
@@ -0,0 +1,356 @@
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+
+/* Make sure we override these! */
+static void *no_malloc(size_t size)
+{
+       abort();
+}
+static void *no_realloc(void *p, size_t size)
+{
+       abort();
+}
+static void no_free(void *p)
+{
+       abort();
+}
+#define malloc no_malloc
+#define realloc no_realloc
+#define free no_free
+
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+#undef malloc
+#undef realloc
+#undef free
+
+static unsigned int alloc_count, realloc_count, free_count;
+static void *ptrs[100];
+
+static void **find_ptr(void *p)
+{
+       unsigned int i;
+
+       for (i = 0; i < 100; i++)
+               if (ptrs[i] == p)
+                       return ptrs + i;
+       return NULL;
+}
+
+static void *allocfn(size_t size)
+{
+       alloc_count++;
+       return *find_ptr(NULL) = malloc(size);
+}
+
+static void *reallocfn(void *ptr, size_t size)
+{
+       realloc_count++;
+       if (!ptr)
+               alloc_count++;
+
+       return *find_ptr(ptr) = realloc(ptr, size);
+}
+
+static void freefn(void *ptr)
+{
+       free_count++;
+       free(ptr);
+       *find_ptr(ptr) = NULL;
+}
+
+int main(int argc, char *argv[])
+{
+       const char *myname = argv[0];
+
+       plan_tests(220);
+
+       opt_set_alloc(allocfn, reallocfn, freefn);
+
+       /* Simple short arg.*/
+       opt_register_noarg("-a", test_noarg, NULL, "All");
+       ok1(parse_args(&argc, &argv, "-a", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 1);
+
+       /* Simple long arg. */
+       opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
+       ok1(parse_args(&argc, &argv, "--aaa", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 2);
+
+       /* Both long and short args. */
+       opt_register_noarg("--aaa|-a", test_noarg, NULL, "AAAAAAll");
+       ok1(parse_args(&argc, &argv, "--aaa", "-a", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 4);
+
+       /* Extra arguments preserved. */
+       ok1(parse_args(&argc, &argv, "--aaa", "-a", "extra", "args", NULL));
+       ok1(argc == 3);
+       ok1(argv[0] == myname);
+       ok1(strcmp(argv[1], "extra") == 0);
+       ok1(strcmp(argv[2], "args") == 0);
+       ok1(test_cb_called == 6);
+
+       /* Malformed versions. */
+       ok1(!parse_args(&argc, &argv, "--aaa=arg", NULL));
+       ok1(strstr(err_output, ": --aaa: doesn't allow an argument"));
+       ok1(!parse_args(&argc, &argv, "--aa", NULL));
+       ok1(strstr(err_output, ": --aa: unrecognized option"));
+       ok1(!parse_args(&argc, &argv, "--aaargh", NULL));
+       ok1(strstr(err_output, ": --aaargh: unrecognized option"));
+
+       /* Argument variants. */
+       reset_options();
+       test_cb_called = 0;
+       opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "AAAAAAll");
+       ok1(parse_args(&argc, &argv, "--aaa", "aaa", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 1);
+
+       ok1(parse_args(&argc, &argv, "--aaa=aaa", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 2);
+
+       ok1(parse_args(&argc, &argv, "-a", "aaa", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 3);
+
+       /* Malformed versions. */
+       ok1(!parse_args(&argc, &argv, "-a", NULL));
+       ok1(strstr(err_output, ": -a: requires an argument"));
+       ok1(!parse_args(&argc, &argv, "--aaa", NULL));
+       ok1(strstr(err_output, ": --aaa: requires an argument"));
+       ok1(!parse_args(&argc, &argv, "--aa", NULL));
+       ok1(strstr(err_output, ": --aa: unrecognized option"));
+       ok1(!parse_args(&argc, &argv, "--aaargh", NULL));
+       ok1(strstr(err_output, ": --aaargh: unrecognized option"));
+
+       /* Now, tables. */
+       /* Short table: */
+       reset_options();
+       test_cb_called = 0;
+       opt_register_table(short_table, NULL);
+       ok1(parse_args(&argc, &argv, "-a", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 1);
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+       ok1(test_cb_called == 1);
+       ok1(parse_args(&argc, &argv, "-b", "b", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 2);
+
+       /* Long table: */
+       reset_options();
+       test_cb_called = 0;
+       opt_register_table(long_table, NULL);
+       ok1(parse_args(&argc, &argv, "--ddd", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 1);
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "--eee", NULL) == false);
+       ok1(test_cb_called == 1);
+       ok1(parse_args(&argc, &argv, "--eee", "eee", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 2);
+
+       /* Short and long, both. */
+       reset_options();
+       test_cb_called = 0;
+       opt_register_table(long_and_short_table, NULL);
+       ok1(parse_args(&argc, &argv, "-g", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 1);
+       ok1(parse_args(&argc, &argv, "--ggg", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 2);
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "-h", NULL) == false);
+       ok1(test_cb_called == 2);
+       ok1(parse_args(&argc, &argv, "-h", "hhh", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 3);
+       ok1(parse_args(&argc, &argv, "--hhh", NULL) == false);
+       ok1(test_cb_called == 3);
+       ok1(parse_args(&argc, &argv, "--hhh", "hhh", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 4);
+
+       /* Those will all work as tables. */
+       test_cb_called = 0;
+       reset_options();
+       opt_register_table(subtables, NULL);
+       ok1(parse_args(&argc, &argv, "-a", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 1);
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+       ok1(test_cb_called == 1);
+       ok1(parse_args(&argc, &argv, "-b", "b", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 2);
+
+       ok1(parse_args(&argc, &argv, "--ddd", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 3);
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "--eee", NULL) == false);
+       ok1(test_cb_called == 3);
+       ok1(parse_args(&argc, &argv, "--eee", "eee", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 4);
+
+       /* Short and long, both. */
+       ok1(parse_args(&argc, &argv, "-g", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 5);
+       ok1(parse_args(&argc, &argv, "--ggg", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 6);
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "-h", NULL) == false);
+       ok1(test_cb_called == 6);
+       ok1(parse_args(&argc, &argv, "-h", "hhh", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 7);
+       ok1(parse_args(&argc, &argv, "--hhh", NULL) == false);
+       ok1(test_cb_called == 7);
+       ok1(parse_args(&argc, &argv, "--hhh", "hhh", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 8);
+
+       /* Now the tricky one: -? must not be confused with an unknown option */
+       test_cb_called = 0;
+       reset_options();
+
+       /* glibc's getopt does not handle ? with arguments. */
+       opt_register_noarg("-?", test_noarg, NULL, "Help");
+       ok1(parse_args(&argc, &argv, "-?", NULL));
+       ok1(test_cb_called == 1);
+       ok1(parse_args(&argc, &argv, "-a", NULL) == false);
+       ok1(test_cb_called == 1);
+       ok1(strstr(err_output, ": -a: unrecognized option"));
+       ok1(parse_args(&argc, &argv, "--aaaa", NULL) == false);
+       ok1(test_cb_called == 1);
+       ok1(strstr(err_output, ": --aaaa: unrecognized option"));
+
+       test_cb_called = 0;
+       reset_options();
+
+       /* Corner cases involving short arg parsing weirdness. */
+       opt_register_noarg("-a|--aaa", test_noarg, NULL, "a");
+       opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "b");
+       opt_register_arg("-c|--ccc", test_arg, NULL, "aaa", "c");
+       /* -aa == -a -a */
+       ok1(parse_args(&argc, &argv, "-aa", NULL));
+       ok1(test_cb_called == 2);
+       ok1(parse_args(&argc, &argv, "-aab", NULL) == false);
+       ok1(test_cb_called == 4);
+       ok1(strstr(err_output, ": -b: requires an argument"));
+       ok1(parse_args(&argc, &argv, "-bbbb", NULL));
+       ok1(test_cb_called == 5);
+       ok1(parse_args(&argc, &argv, "-aabbbb", NULL));
+       ok1(test_cb_called == 8);
+       ok1(parse_args(&argc, &argv, "-aabbbb", "-b", "bbb", NULL));
+       ok1(test_cb_called == 12);
+       ok1(parse_args(&argc, &argv, "-aabbbb", "--bbb", "bbb", NULL));
+       ok1(test_cb_called == 16);
+       ok1(parse_args(&argc, &argv, "-aabbbb", "--bbb=bbb", NULL));
+       ok1(test_cb_called == 20);
+       ok1(parse_args(&argc, &argv, "-aacaaa", NULL));
+       ok1(test_cb_called == 23);
+       ok1(parse_args(&argc, &argv, "-aacaaa", "-a", NULL));
+       ok1(test_cb_called == 27);
+       ok1(parse_args(&argc, &argv, "-aacaaa", "--bbb", "bbb", "-aacaaa",
+                      NULL));
+       ok1(test_cb_called == 34);
+
+       test_cb_called = 0;
+       reset_options();
+
+       /* -- and POSIXLY_CORRECT */
+       opt_register_noarg("-a|--aaa", test_noarg, NULL, "a");
+       ok1(parse_args(&argc, &argv, "-a", "--", "-a", NULL));
+       ok1(test_cb_called == 1);
+       ok1(argc == 2);
+       ok1(strcmp(argv[1], "-a") == 0);
+       ok1(!argv[2]);
+
+       unsetenv("POSIXLY_CORRECT");
+       ok1(parse_args(&argc, &argv, "-a", "somearg", "-a", "--", "-a", NULL));
+       ok1(test_cb_called == 3);
+       ok1(argc == 3);
+       ok1(strcmp(argv[1], "somearg") == 0);
+       ok1(strcmp(argv[2], "-a") == 0);
+       ok1(!argv[3]);
+
+       setenv("POSIXLY_CORRECT", "1", 1);
+       ok1(parse_args(&argc, &argv, "-a", "somearg", "-a", "--", "-a", NULL));
+       ok1(test_cb_called == 4);
+       ok1(argc == 5);
+       ok1(strcmp(argv[1], "somearg") == 0);
+       ok1(strcmp(argv[2], "-a") == 0);
+       ok1(strcmp(argv[3], "--") == 0);
+       ok1(strcmp(argv[4], "-a") == 0);
+       ok1(!argv[5]);
+
+       /* We should have tested each one at least once! */
+       ok1(realloc_count);
+       ok1(alloc_count);
+       ok1(free_count);
+
+       ok1(free_count < alloc_count);
+       reset_options();
+       ok1(free_count == alloc_count);
+
+       /* parse_args allocates argv */
+       free(argv);
+       return exit_status();
+}
index 7c1971c2e48ed919db13625c027ffcfc328c8f6c..1142fb85c9c91ca091172d8971fe58101d0beca6 100644 (file)
@@ -60,7 +60,7 @@ static char *add_str_len(char *base, size_t *len, size_t *max,
                         const char *str, size_t slen)
 {
        if (slen >= *max - *len)
-               base = realloc(base, *max = (*max * 2 + slen + 1));
+               base = opt_alloc.realloc(base, *max = (*max * 2 + slen + 1));
        memcpy(base + *len, str, slen);
        *len += slen;
        return base;
@@ -74,7 +74,7 @@ static char *add_str(char *base, size_t *len, size_t *max, const char *str)
 static char *add_indent(char *base, size_t *len, size_t *max, size_t indent)
 {
        if (indent >= *max - *len)
-               base = realloc(base, *max = (*max * 2 + indent + 1));
+               base = opt_alloc.realloc(base, *max = (*max * 2 + indent + 1));
        memset(base + *len, ' ', indent);
        *len += indent;
        return base;