Send inn-committers mailing list submissions to inn-committers@lists.isc.org
To subscribe or unsubscribe via the World Wide Web, visit https://lists.isc.org/mailman/listinfo/inn-committers or, via email, send a message with subject or body 'help' to inn-committers-requ...@lists.isc.org You can reach the person managing the list at inn-committers-ow...@lists.isc.org When replying, please edit your Subject line so it is more specific than "Re: Contents of inn-committers digest..." Today's Topics: 1. INN commit: trunk (4 files) (INN Commit) 2. INN commit: trunk (3 files) (INN Commit) 3. INN commit: trunk (3 files) (INN Commit) ---------------------------------------------------------------------- Message: 1 Date: Wed, 3 Sep 2014 10:27:01 -0700 (PDT) From: INN Commit <r...@isc.org> To: inn-committ...@isc.org Subject: INN commit: trunk (4 files) Message-ID: <20140903172701.9dcda67...@hope.eyrie.org> Date: Wednesday, September 3, 2014 @ 10:27:01 Author: iulius Revision: 9670 add getc-tap-harness support script to keep INN in sync with C TAP Harness Added: trunk/support/getc-tap-harness Modified: trunk/MANIFEST trunk/doc/pod/hacking.pod trunk/doc/pod/news.pod --------------------------+ MANIFEST | 1 doc/pod/hacking.pod | 31 ++--- doc/pod/news.pod | 8 + support/getc-tap-harness | 260 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 286 insertions(+), 14 deletions(-) Modified: MANIFEST =================================================================== --- MANIFEST 2014-09-03 17:18:22 UTC (rev 9669) +++ MANIFEST 2014-09-03 17:27:01 UTC (rev 9670) @@ -761,6 +761,7 @@ support/config.sub Canonicalize system type for libtool support/fixconfig.in Config file path fixup script support/fixscript.in Interpreter path fixup script +support/getc-tap-harness Get the latest files from C TAP Harness support/getrra-c-util Get the latest files from rra-c-util support/indent A mostly working wrapper around indent support/install-sh Installation utility Modified: doc/pod/hacking.pod =================================================================== --- doc/pod/hacking.pod 2014-09-03 17:18:22 UTC (rev 9669) +++ doc/pod/hacking.pod 2014-09-03 17:27:01 UTC (rev 9670) @@ -659,23 +659,26 @@ =item * -Make sure that the latest upstream version of the C TAP Harness package -is used for the test suite driver. It is available from -L<http://www.eyrie.org/~eagle/software/c-tap-harness/>. The file -F<tests/runtests.c> should be updated, as well as the files in the -F<tests/tap> directory. Parts specific to INN should be kept during -an update (especially sections relative to LIBTEST_NEW_FORMAT because -the test suite has not yet been updated to use the new format of C -TAP Harness). +Make sure that the latest upstream version of the C TAP Harness +package is used for the test suite driver. It is available at +L<http://www.eyrie.org/~eagle/software/c-tap-harness/>, and can be +easily synchronized with the script F<support/getc-tap-harness>; just +run it, have a look at the resulting changes in INN source code and, +if everything seems all right, commit these changes. +Parts specific to INN should be kept during an update (especially +sections relative to LIBTEST_NEW_FORMAT because the test suite has not +yet been fully updated to use the new format of C TAP Harness). + =item * -Make sure that the latest upstream version of the files maintained in the -rra-c-util package that INN uses are in sync with upstream. These files -are available at L<http://www.eyrie.org/~eagle/software/rra-c-util/> -and can be easily synchronized with the script F<support/getrra-c-util>; -just run it, have a look at the resulting changes in INN source code and, -if everything seems all right, commit these changes. +Make sure that the latest upstream version of the files +maintained in the rra-c-util package that INN uses are the +ones shipped with the release. These files are available at +L<http://www.eyrie.org/~eagle/software/rra-c-util/> and can be easily +synchronized with the script F<support/getrra-c-util>; just run it, have +a look at the resulting changes in INN source code and, if everything +seems all right, commit these changes. =item * Modified: doc/pod/news.pod =================================================================== --- doc/pod/news.pod 2014-09-03 17:18:22 UTC (rev 9669) +++ doc/pod/news.pod 2014-09-03 17:27:01 UTC (rev 9670) @@ -168,6 +168,14 @@ For security reasons, use of the flawed SSLv2 protocol is now disabled for TLS sessions with B<nnrpd>. +=item * + +The INN test suite driver is now fully synchronized with the upstream +version of the C TAP Harness package maintained by Russ Allbery. +Keeping the INN test suite driver up-to-date will be possible thanks +to a new B<getc-tap-harness> script in the F<support> directory that +automatically fetches the latest upstream changes. + =back =head1 Changes in 2.5.5 Added: support/getc-tap-harness =================================================================== --- support/getc-tap-harness (rev 0) +++ support/getc-tap-harness 2014-09-03 17:27:01 UTC (rev 9670) @@ -0,0 +1,260 @@ +#!/bin/sh + +## $Id$ +## +## Synchronize INN files maintained in c-tap-harness with upstream. +## +## This script downloads the latest version of the files maintained +## in the C TAP Harness package that INN uses for its test driver suite. +## These files are available at +## <http://www.eyrie.org/~eagle/software/c-tap-harness/>. +## +## Running this script permits to keep up-to-date the INN test driver suite +## by automatically fetching the latest version of the upstream files, and +## putting them in the expected location in the INN source code. +## The name of the files that have been modified since the last run of the +## script are written to standard output. Have a look at the changes and, +## if all looks right, commit the changes. + +## URL where the files can be downloaded. +URL_START="http://git.eyrie.org/?p=devel/c-tap-harness.git;a=blob_plain;hb=HEAD;f=" + +## This function downloads the files and copies them to the expected +## location in the INN source code, after having performed a few minimal +## changes to integrate them in the INN build system. +## If a file is not found in upstream or has been modified since the last +## run of the script, write it on standard output. +## +## This function expects the following arguments: +## $1 - full path of the file to download in the upstream package +## $2 - directory where the file should be copied in INN source code +## $3 - name of the file in INN source code +download () +{ + TEMP=$3.tmp + + rm -f ${TEMP} + wget -q "${URL_START}$1" -O ${TEMP} + + if [ ! -s "${TEMP}" ] + then + echo "File $1 does not exist in upstream package" + else + if [ "$2" = "tests" ] || [ "$2" = "tests/tap" ] + then + # Add the SVN Id keyword at the beginning of source code files, + # and update the path of included C header files. + # Changes in shell scripts and C files are not the same. + # Also, do not modify the README file. + if [ "$3" = "libtap.sh" ] + then + sed -i -e "1 i \\ +# \$Id\$\\ +#" \ + ${TEMP} + elif [ "$3" != "README" ] + then + sed -i -e '1 s/$/ $Id$\n */' \ + -e 's/^#include <tests\/tap\//#include <tap\//g' \ + ${TEMP} + fi + fi + + # Specific additions to a few files. + if [ "$3" = "basic.c" ] + then + sed -i -e "55 i \\ +/* Specific to the integration of C TAP Harness in INN. */\\ +#ifndef LIBTEST_NEW_FORMAT\\ +# include \"inn/messages.h\"\\ +# include \"inn/libinn.h\"\\ +\\ +void\\ +test_init(int count)\\ +{\\ + plan(count);\\ +}\\ +\\ +void\\ +ok(int n UNUSED, int success)\\ +{\\ + new_ok(success, NULL);\\ +}\\ +\\ +void\\ +skip(int n UNUSED, const char *reason)\\ +{\\ + new_skip(reason);\\ +}\\ +\\ +void\\ +ok_block(int n UNUSED, int count, int success)\\ +{\\ + new_ok_block(count, success, NULL);\\ +}\\ +\\ +void\\ +skip_block(int n UNUSED, int count, const char *reason)\\ +{\\ + new_skip_block(count, reason);\\ +}\\ +\\ +void\\ +ok_int(int n UNUSED, int wanted, int seen)\\ +{\\ + is_int(wanted, seen, NULL);\\ +}\\ +\\ +void\\ +ok_string(int n UNUSED, const char *wanted, const char *seen)\\ +{\\ + is_string(wanted, seen, NULL);\\ +}\\ +\\ +/* A global buffer into which message_log_buffer stores error messages. */\\ +char *errors = NULL;\\ +\\ +/*\\ + * An error handler that appends all errors to the errors global. Used by\\ + * error_capture.\\ + */\\ +static void\\ +message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED)\\ +{\\ + char *message;\\ +\\ + message = xmalloc(len + 1);\\ + vsnprintf(message, len + 1, fmt, args);\\ + if (errors == NULL) {\\ + errors = concat(message, \"\\\n\", (char *) 0);\\ + } else {\\ + char *new_errors;\\ +\\ + new_errors = concat(errors, message, \"\\\n\", (char *) 0);\\ + free(errors);\\ + errors = new_errors;\\ + }\\ + free(message);\\ +}\\ +\\ +/*\\ + * Turn on the capturing of errors. Errors will be stored in the global\\ + * errors variable where they can be checked by the test suite. Capturing is\\ + * turned off with errors_uncapture.\\ + */\\ +void\\ +errors_capture(void)\\ +{\\ + if (errors != NULL) {\\ + free(errors);\\ + errors = NULL;\\ + }\\ + message_handlers_warn(1, message_log_buffer);\\ + message_handlers_notice(1, message_log_buffer);\\ +}\\ +\\ +/*\\ + * Turn off the capturing of errors again.\\ + */\\ +void\\ +errors_uncapture(void)\\ +{\\ + message_handlers_warn(1, message_log_stderr);\\ + message_handlers_notice(1, message_log_stdout);\\ +}\\ +#endif\\ +" \ + -e "150 i \\ +#ifndef LIBINN_H" \ + -e "184 i \\ +#endif" \ + -e 's/^ok.int /new_ok(int /g' \ + -e 's/^skip.const /new_skip(const /g' \ + -e 's/^ok_block.unsigned /new_ok_block(unsigned /g' \ + -e 's/^skip_block.unsigned /new_skip_block(unsigned /g' \ + ${TEMP} + fi + + if [ "$3" = "basic.h" ] + then + sed -i -e "74 i \\ +#ifndef LIBTEST_NEW_FORMAT\\ +/* Specific to the integration of C TAP Harness in INN. */\\ +void ok(int n, int success);\\ +int new_ok(int success, const char *format, ...)\\ + __attribute__((__format__(printf, 2, 3)));\\ +void ok_int(int n, int wanted, int seen);\\ +void ok_string(int n, const char *wanted, const char *seen);\\ +int okv(int success, const char *format, va_list args);\\ +void skip(int n, const char *reason);\\ +void new_skip(const char *reason, ...)\\ + __attribute__((__format__(printf, 1, 2)));\\ +void ok_block(int n, int count, int success);\\ +int new_ok_block(unsigned long count, int success, const char *format, ...)\\ + __attribute__((__format__(printf, 3, 4)));\\ +void skip_block(int n, int count, const char *reason);\\ +void new_skip_block(unsigned long count, const char *reason, ...)\\ + __attribute__((__format__(printf, 2, 3)));\\ +\\ +void test_init(int count);\\ +\\ +/* A global buffer into which errors_capture stores errors. */\\ +extern char *errors;\\ +\\ +/* Turn on capturing of errors with errors_capture. Errors reported by warn\\ + * will be stored in the global errors variable. Turn this off again with\\ + * errors_uncapture. Caller is responsible for freeing errors when done.\\ + */\\ +void errors_capture(void);\\ +void errors_uncapture(void);\\ +#else\\ +# define ok new_ok\\ +# define skip new_skip\\ +# define ok_block new_ok_block\\ +# define skip_block new_skip_block" \ + -e "88 i \\ +#endif" \ + ${TEMP} + fi + + if [ "$3" = "float.c" ] + then + sed -i -e "49 i \\ +#ifndef LIBTEST_NEW_FORMAT\\ +/* Specific to the integration of C TAP Harness in INN. */\\ +void\\ +ok_double(int n UNUSED, double wanted, double seen)\\ +{\\ + is_double(wanted, seen, 0.01, NULL);\\ +}\\ +#endif\\ +" \ + ${TEMP} + fi + + if [ "$3" = "float.h" ] + then + sed -i -e "37 i \\ +#ifndef LIBTEST_NEW_FORMAT\\ +/* Specific to the integration of C TAP Harness in INN. */\\ +void ok_double(int n, double wanted, double seen);\\ +#endif" \ + ${TEMP} + fi + + mv -f ${TEMP} ../$2/$3 + svn status ../$2/$3 + fi +} + +## Synchronize the test driver suite from upstream. +download tests/runtests.c tests runtests.c +download tests/tap/basic.c tests/tap basic.c +download tests/tap/basic.h tests/tap basic.h +download tests/tap/float.c tests/tap float.c +download tests/tap/float.h tests/tap float.h +download tests/tap/libtap.sh tests/tap libtap.sh +download tests/tap/macros.h tests/tap macros.h + +## Synchronize the README file from upstream. +download docs/writing-tests tests README Property changes on: trunk/support/getc-tap-harness ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Added: svn:executable + * ------------------------------ Message: 2 Date: Wed, 3 Sep 2014 10:40:31 -0700 (PDT) From: INN Commit <r...@isc.org> To: inn-committ...@isc.org Subject: INN commit: trunk (3 files) Message-ID: <20140903174032.0b1ce67...@hope.eyrie.org> Date: Wednesday, September 3, 2014 @ 10:40:31 Author: iulius Revision: 9671 sync with latest rra-c-util Add GCC attributes to util/buffer.h functions, mostly nonnull. Clarify the documentation of some of the functions, particularly around error reporting. Fix visibility for util/buffer.c functions and for inet_aton and inet_ntoa replacements to match the default hidden visiblity of other portability and util functions. Modified: trunk/include/inn/buffer.h trunk/include/portable/socket.h trunk/lib/fdflag.c ---------------------------+ include/inn/buffer.h | 59 ++++++++++++++++++++++++++++++-------------- include/portable/socket.h | 14 +++++++++- lib/fdflag.c | 2 - 3 files changed, 55 insertions(+), 20 deletions(-) Modified: include/inn/buffer.h =================================================================== --- include/inn/buffer.h 2014-09-03 17:27:01 UTC (rev 9670) +++ include/inn/buffer.h 2014-09-03 17:40:31 UTC (rev 9671) @@ -17,6 +17,7 @@ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. * * Written by Russ Allbery <ea...@eyrie.org> + * Copyright 2014 Russ Allbery <ea...@eyrie.org> * Copyright 2011, 2012 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006 @@ -57,7 +58,8 @@ BEGIN_DECLS /* Allocate a new buffer and initialize its contents. */ -struct buffer *buffer_new(void); +struct buffer *buffer_new(void) + __attribute__((__warn_unused_result__, __malloc__)); /* Free an allocated buffer. */ void buffer_free(struct buffer *); @@ -66,35 +68,45 @@ * Resize a buffer to be at least as large as the provided size. Invalidates * pointers into the buffer. */ -void buffer_resize(struct buffer *, size_t); +void buffer_resize(struct buffer *, size_t) + __attribute__((__nonnull__)); /* * Compact a buffer, removing all used data and moving unused data to the * beginning of the buffer. Invalidates pointers into the buffer. */ -void buffer_compact(struct buffer *); +void buffer_compact(struct buffer *) + __attribute__((__nonnull__)); -/* Set the buffer contents, ignoring anything currently there. */ -void buffer_set(struct buffer *, const char *data, size_t length); +/* + * Set the buffer contents, ignoring anything currently there. If length is + * 0, empties the buffer, in which case data may be NULL. + */ +void buffer_set(struct buffer *, const char *data, size_t length) + __attribute__((__nonnull__(1))); /* * Set the buffer contents via a sprintf-style format string. No trailing * nul is added. */ void buffer_sprintf(struct buffer *, const char *, ...) - __attribute__((__format__(printf, 2, 3))); -void buffer_vsprintf(struct buffer *, const char *, va_list); + __attribute__((__format__(printf, 2, 3), __nonnull__)); +void buffer_vsprintf(struct buffer *, const char *, va_list) + __attribute__((__nonnull__)); /* Append data to the buffer. */ -void buffer_append(struct buffer *, const char *data, size_t length); +void buffer_append(struct buffer *, const char *data, size_t length) + __attribute__((__nonnull__(1))); /* Append via an sprintf-style format string. No trailing nul is added. */ void buffer_append_sprintf(struct buffer *, const char *, ...) - __attribute__((__format__(printf, 2, 3))); -void buffer_append_vsprintf(struct buffer *, const char *, va_list); + __attribute__((__format__(printf, 2, 3), __nonnull__)); +void buffer_append_vsprintf(struct buffer *, const char *, va_list) + __attribute__((__nonnull__)); /* Swap the contents of two buffers. */ -void buffer_swap(struct buffer *, struct buffer *); +void buffer_swap(struct buffer *, struct buffer *) + __attribute__((__nonnull__)); /* * Find the given string in the unconsumed data in a buffer. start is an @@ -105,22 +117,33 @@ * the fourth argument. Returns false if the terminator isn't found. */ bool buffer_find_string(struct buffer *, const char *, size_t start, - size_t *offset); + size_t *offset) + __attribute__((__nonnull__)); /* * Read from a file descriptor into a buffer, up to the available space in the - * buffer. Return the number of characters read. + * buffer. Return the number of characters read. Retries the read if + * interrupted by a signal or if it returns EAGAIN, but stops on any other + * error or after any successful read. Returns -1 on an error reading from + * the file descriptor and sets errno. */ -ssize_t buffer_read(struct buffer *, int fd); +ssize_t buffer_read(struct buffer *, int fd) + __attribute__((__nonnull__)); -/* Read from a file descriptor into a buffer until end of file is reached. */ -bool buffer_read_all(struct buffer *, int fd); +/* + * Read from a file descriptor into a buffer until end of file is reached. + * Returns true on success and false (setting errno) on error. + */ +bool buffer_read_all(struct buffer *, int fd) + __attribute__((__nonnull__)); /* * Read the contents of a file into a buffer. This should be used instead of - * buffer_read_all when fstat can be called on the file descriptor. + * buffer_read_all when fstat can be called on the file descriptor. Returns + * true on success and false (setting errno) on error. */ -bool buffer_read_file(struct buffer *, int fd); +bool buffer_read_file(struct buffer *, int fd) + __attribute__((__nonnull__)); END_DECLS Modified: include/portable/socket.h =================================================================== --- include/portable/socket.h 2014-09-03 17:27:01 UTC (rev 9670) +++ include/portable/socket.h 2014-09-03 17:40:31 UTC (rev 9671) @@ -17,6 +17,7 @@ * The canonical version of this file is maintained in the rra-c-util package, * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. * + * Copyright 2014 Russ Allbery <ea...@eyrie.org> * Copyright 2008, 2009, 2011, 2013 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006, 2007 @@ -203,13 +204,24 @@ /* * Provide prototypes for inet_aton and inet_ntoa if not prototyped in the * system header files since they're occasionally available without proper - * prototypes. + * prototypes. If we're providing a replacement, be sure to set visibility + * accordingly. */ #if !HAVE_DECL_INET_ATON +# if !HAVE_INET_ATON +extern int inet_aton(const char *, struct in_addr *) + __attribute__((__visibility__("hidden"))); +# else extern int inet_aton(const char *, struct in_addr *); +# endif #endif #if !HAVE_DECL_INET_NTOA +# if !HAVE_INET_NTOA +extern const char *inet_ntoa(const struct in_addr) + __attribute__((__visibility__("hidden"))); +# else extern const char *inet_ntoa(const struct in_addr); +# endif #endif #if !HAVE_INET_NTOP Modified: lib/fdflag.c =================================================================== --- lib/fdflag.c 2014-09-03 17:27:01 UTC (rev 9670) +++ lib/fdflag.c 2014-09-03 17:40:31 UTC (rev 9671) @@ -33,7 +33,6 @@ #include "config.h" #include "clibrary.h" -#include <errno.h> #ifdef _WIN32 # include <winsock2.h> @@ -50,6 +49,7 @@ #include "inn/fdflag.h" #include "inn/libinn.h" + /* * Set a file to close-on-exec (or clear that setting if the flag is false), * returning true on success and false on failure. ------------------------------ Message: 3 Date: Wed, 3 Sep 2014 10:54:26 -0700 (PDT) From: INN Commit <r...@isc.org> To: inn-committ...@isc.org Subject: INN commit: trunk (3 files) Message-ID: <20140903175426.2accf67...@hope.eyrie.org> Date: Wednesday, September 3, 2014 @ 10:54:25 Author: iulius Revision: 9672 sync the vector library with its latest rra-c-util version Changes are: - Always allocate room for at least one string to be stored in the vector. It simplifies some of the internal logic and, more to the point, unconfuses clang, which otherwise produces tons of warnings about possibly dereferencing NULL pointers since it can't follow the logic. - Update the confparse test to expect another value for the allocated size of a vector, further to the previous change. - Add __warn_unused_result__ to some vector functions that return newly-allocated memory. - Add asserts to the vector library to catch incoming NULL vectors. These will probably be optimized away by gcc, but they also help unconfuse clang. - Allow NULL to be passed to (c)vector_free. It's much more convenient for freeing data structures to be able to call free routines unconditionally without first testing for NULL. Support this in the vector interface. - Define *_split_space in terms of *_split_multi to save some code. - Check for integer overflow when determining the size of the results of vector_join and vector_cjoin. - Handle empty vectors in vector_join and cvector_join. - Don't check whether a pointer is NULL before passing it into free and instead assume free can handle NULL pointers properly. This has been true for many years. - Fix vector_free to support taking NULL pointers and doing nothing with them. - Allocate memory with calloc and assume this sets pointers to NULL instead of explicitly initializing them. We already had to assume this in various places, and architectures where the all-zero bit pattern is not the NULL pointer are exceedingly rare. Modified: trunk/include/inn/vector.h trunk/lib/vector.c trunk/tests/lib/confparse-t.c -------------------------+ include/inn/vector.h | 119 +++++++++----- lib/vector.c | 379 +++++++++++++++++++++++----------------------- tests/lib/confparse-t.c | 4 3 files changed, 271 insertions(+), 231 deletions(-) Modified: include/inn/vector.h =================================================================== --- include/inn/vector.h 2014-09-03 17:40:31 UTC (rev 9671) +++ include/inn/vector.h 2014-09-03 17:54:25 UTC (rev 9672) @@ -1,27 +1,35 @@ -/* $Id$ -** -** Vector handling (counted lists of char *'s). -** -** Written by Russ Allbery <r...@stanford.edu> -** This work is hereby placed in the public domain by its author. -** -** A vector is a simple array of char *'s combined with a count. It's a -** convenient way of managing a list of strings, as well as a reasonable -** output data structure for functions that split up a string. There are -** two basic types of vectors, regular vectors (in which case strings are -** copied when put into a vector and freed when the vector is freed) and -** cvectors or const vectors (where each pointer is a const char * to some -** external string that isn't freed when the vector is freed). -** -** There are two interfaces here, one for vectors and one for cvectors, -** with the basic operations being the same between the two. -*/ +/* $Id$ + * + * Prototypes for vector handling. + * + * A vector is a list of strings, with dynamic resizing of the list as new + * strings are added and support for various operations on strings (such as + * splitting them on delimiters). + * + * Vectors require list of strings, not arbitrary binary data, and cannot + * handle data elements containing nul characters. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + * + * Written by Russ Allbery <ea...@eyrie.org> + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. + */ #ifndef INN_VECTOR_H #define INN_VECTOR_H 1 #include <inn/defines.h> +#include <stddef.h> + struct vector { size_t count; size_t allocated; @@ -37,29 +45,44 @@ BEGIN_DECLS /* Create a new, empty vector. */ -struct vector *vector_new(void); -struct cvector *cvector_new(void); +struct vector *vector_new(void) + __attribute__((__warn_unused_result__, __malloc__)); +struct cvector *cvector_new(void) + __attribute__((__warn_unused_result__, __malloc__)); /* Add a string to a vector. Resizes the vector if necessary. */ -void vector_add(struct vector *, const char *string); -void cvector_add(struct cvector *, const char *string); +void vector_add(struct vector *, const char *string) + __attribute__((__nonnull__)); +void cvector_add(struct cvector *, const char *string) + __attribute__((__nonnull__)); /* Add a counted string to a vector. Only available for vectors. */ void vector_addn(struct vector *, const char *string, size_t length) __attribute__((__nonnull__)); -/* Resize the array of strings to hold size entries. Saves reallocation work - in vector_add if it's known in advance how many entries there will be. */ -void vector_resize(struct vector *, size_t size); -void cvector_resize(struct cvector *, size_t size); +/* + * Resize the array of strings to hold size entries. Saves reallocation work + * in vector_add if it's known in advance how many entries there will be. + */ +void vector_resize(struct vector *, size_t size) + __attribute__((__nonnull__)); +void cvector_resize(struct cvector *, size_t size) + __attribute__((__nonnull__)); -/* Reset the number of elements to zero, freeing all of the strings for a - regular vector, but not freeing the strings array (to cut down on memory - allocations if the vector will be reused). */ -void vector_clear(struct vector *); -void cvector_clear(struct cvector *); +/* + * Reset the number of elements to zero, freeing all of the strings for a + * regular vector, but not freeing the strings array (to cut down on memory + * allocations if the vector will be reused). + */ +void vector_clear(struct vector *) + __attribute__((__nonnull__)); +void cvector_clear(struct cvector *) + __attribute__((__nonnull__)); -/* Free the vector and all resources allocated for it. */ +/* + * Free the vector and all resources allocated for it. NULL may be passed in + * safely and will be ignored. + */ void vector_free(struct vector *); void cvector_free(struct cvector *); @@ -75,7 +98,9 @@ * Empty strings will yield zero-length vectors. Adjacent delimiters are * treated as a single delimiter by *_split_space and *_split_multi, but *not* * by *_split, so callers of *_split should be prepared for zero-length - * strings in the vector. + * strings in the vector. *_split_space and *_split_multi ignore any leading + * or trailing delimiters, so those functions will never create zero-length + * strings (similar to the behavior of strtok). */ struct vector *vector_split(const char *string, char sep, struct vector *) __attribute__((__nonnull__(1))); @@ -92,17 +117,25 @@ struct cvector *cvector_split_space(char *string, struct cvector *) __attribute__((__nonnull__(1))); -/* Build a string from a vector by joining its components together with the - specified string as separator. Returns a newly allocated string; caller is - responsible for freeing. */ -char *vector_join(const struct vector *, const char *seperator); -char *cvector_join(const struct cvector *, const char *separator); +/* + * Build a string from a vector by joining its components together with the + * specified string as separator. Returns a newly allocated string; caller is + * responsible for freeing. + */ +char *vector_join(const struct vector *, const char *separator) + __attribute__((__malloc__, __nonnull__, __warn_unused_result__)); +char *cvector_join(const struct cvector *, const char *separator) + __attribute__((__malloc__, __nonnull__, __warn_unused_result__)); -/* Exec the given program with the vector as its arguments. Return behavior - is the same as execv. Note the argument order is different than the other - vector functions (but the same as execv). */ -int vector_exec(const char *path, struct vector *); -int cvector_exec(const char *path, struct cvector *); +/* + * Exec the given program with the vector as its arguments. Return behavior + * is the same as execv. Note the argument order is different than the other + * vector functions (but the same as execv). + */ +int vector_exec(const char *path, struct vector *) + __attribute__((__nonnull__)); +int cvector_exec(const char *path, struct cvector *) + __attribute__((__nonnull__)); END_DECLS Modified: lib/vector.c =================================================================== --- lib/vector.c 2014-09-03 17:40:31 UTC (rev 9671) +++ lib/vector.c 2014-09-03 17:54:25 UTC (rev 9672) @@ -1,46 +1,59 @@ -/* $Id$ -** -** Vector handling (counted lists of char *'s). -** -** Written by Russ Allbery <r...@stanford.edu> -** This work is hereby placed in the public domain by its author. -** -** A vector is a table for handling a list of strings with less overhead than -** linked list. The intention is for vectors, once allocated, to be reused; -** this saves on memory allocations once the array of char *'s reaches a -** stable size. -** -** There are two types of vectors. Standard vectors copy strings when -** they're inserted into the vector, whereas cvectors just accept pointers -** to external strings to store. There are therefore two entry points for -** every vector function, one for vectors and one for cvectors. -** -** There's a whole bunch of code duplication here. This would be a lot -** cleaner with C++ features (either inheritance or templates would -** probably help). One could probably in some places just cast a cvector -** to a vector and perform the same operations, but I'm leery of doing that -** as I'm not sure if it's a violation of the C type aliasing rules. -*/ +/* $Id$ + * + * Vector handling (counted lists of char *'s). + * + * A vector is a table for handling a list of strings with less overhead than + * linked list. The intention is for vectors, once allocated, to be reused; + * this saves on memory allocations once the array of char *'s reaches a + * stable size. + * + * There are two types of vectors. Standard vectors copy strings when they're + * inserted into the vector, whereas cvectors just accept pointers to external + * strings to store. There are therefore two entry points for every vector + * function, one for vectors and one for cvectors. + * + * Vectors require list of strings, not arbitrary binary data, and cannot + * handle data elements containing nul characters. + * + * There's a whole bunch of code duplication here. This would be a lot + * cleaner with C++ features (either inheritance or templates would probably + * help). One could probably in some places just cast a cvector to a vector + * and perform the same operations, but I'm leery of doing that as I'm not + * sure if it's a violation of the C type aliasing rules. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + * + * Written by Russ Allbery <ea...@eyrie.org> + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. + */ #include "config.h" #include "clibrary.h" +#include <assert.h> #include <ctype.h> #include "inn/vector.h" #include "inn/libinn.h" /* -** Allocate a new, empty vector. -*/ + * Allocate a new, empty vector. + */ struct vector * vector_new(void) { struct vector *vector; - vector = xmalloc(sizeof(struct vector)); - vector->count = 0; - vector->allocated = 0; - vector->strings = NULL; + vector = xcalloc(1, sizeof(struct vector)); + vector->allocated = 1; + vector->strings = xcalloc(1, sizeof(char *)); return vector; } @@ -49,62 +62,60 @@ { struct cvector *vector; - vector = xmalloc(sizeof(struct cvector)); - vector->count = 0; - vector->allocated = 0; - vector->strings = NULL; + vector = xcalloc(1, sizeof(struct cvector)); + vector->allocated = 1; + vector->strings = xcalloc(1, sizeof(const char *)); return vector; } /* -** Resize a vector (using realloc to resize the table). -*/ + * Resize a vector (using realloc to resize the table). Maintain a minimum + * allocated size of 1 so that the strings data element is never NULL. This + * simplifies other code. + */ void vector_resize(struct vector *vector, size_t size) { size_t i; + assert(vector != NULL); if (vector->count > size) { for (i = size; i < vector->count; i++) free(vector->strings[i]); vector->count = size; } - if (size == 0) { - free(vector->strings); - vector->strings = NULL; - } else { - vector->strings = xrealloc(vector->strings, size * sizeof(char *)); - } + if (size == 0) + size = 1; + vector->strings = xrealloc(vector->strings, size * sizeof(char *)); vector->allocated = size; } void cvector_resize(struct cvector *vector, size_t size) { + assert(vector != NULL); if (vector->count > size) vector->count = size; - if (size == 0) { - free(vector->strings); - vector->strings = NULL; - } else { - vector->strings = - xrealloc(vector->strings, size * sizeof(const char *)); - } + if (size == 0) + size = 1; + vector->strings + = xrealloc(vector->strings, size * sizeof(const char *)); vector->allocated = size; } /* -** Add a new string to the vector, resizing the vector as necessary. The -** vector is resized an element at a time; if a lot of resizes are expected, -** vector_resize should be called explicitly with a more suitable size. -*/ + * Add a new string to the vector, resizing the vector as necessary. The + * vector is resized an element at a time; if a lot of resizes are expected, + * vector_resize should be called explicitly with a more suitable size. + */ void vector_add(struct vector *vector, const char *string) { size_t next = vector->count; + assert(vector != NULL); if (vector->count == vector->allocated) vector_resize(vector, vector->allocated + 1); vector->strings[next] = xstrdup(string); @@ -116,6 +127,7 @@ { size_t next = vector->count; + assert(vector != NULL); if (vector->count == vector->allocated) cvector_resize(vector, vector->allocated + 1); vector->strings[next] = string; @@ -124,16 +136,17 @@ /* - * Add a new counted string to the vector, resizing the vector as necessary - * the same as with vector_add. This function is only available for vectors, - * not cvectors, since it requires the duplication of the input string to be - * sure it's nul-terminated. + * Add a new string to the vector, copying at most length characters of the + * string, resizing the vector as necessary the same as with vector_add. This + * function is only available for vectors, not cvectors, since it requires the + * duplication of the input string to be sure it's nul-terminated. */ void vector_addn(struct vector *vector, const char *string, size_t length) { size_t next = vector->count; + assert(vector != NULL); if (vector->count == vector->allocated) vector_resize(vector, vector->allocated + 1); vector->strings[next] = xstrndup(string, length); @@ -142,13 +155,14 @@ /* -** Empty a vector but keep the allocated memory for the pointer table. -*/ + * Empty a vector but keep the allocated memory for the pointer table. + */ void vector_clear(struct vector *vector) { size_t i; + assert(vector != NULL); for (i = 0; i < vector->count; i++) free(vector->strings[i]); vector->count = 0; @@ -157,16 +171,19 @@ void cvector_clear(struct cvector *vector) { + assert(vector != NULL); vector->count = 0; } /* -** Free a vector completely. -*/ + * Free a vector completely. + */ void vector_free(struct vector *vector) { + if (vector == NULL) + return; vector_clear(vector); free(vector->strings); free(vector); @@ -175,6 +192,8 @@ void cvector_free(struct cvector *vector) { + if (vector == NULL) + return; cvector_clear(vector); free(vector->strings); free(vector); @@ -182,9 +201,9 @@ /* -** Given a vector that we may be reusing, clear it out. If the first -** argument is NULL, allocate a new vector. Used by vector_split*. -*/ + * Given a vector that we may be reusing, clear it out. If the argument is + * NULL, allocate a new vector. Helper function for vector_split*. + */ static struct vector * vector_reuse(struct vector *vector) { @@ -209,9 +228,9 @@ /* -** Given a string and a separator character, count the number of strings -** that it will split into. -*/ + * Given a string and a separator character, count the number of strings that + * it will split into. + */ static size_t split_count(const char *string, char separator) { @@ -220,7 +239,7 @@ if (*string == '\0') return 1; - for (count = 1, p = string; *p; p++) + for (count = 1, p = string; *p != '\0'; p++) if (*p == separator) count++; return count; @@ -228,53 +247,61 @@ /* -** Given a string and a separator character, form a vector by splitting the -** string at those separators. Do a first pass to size the vector, and if -** the third argument isn't NULL, reuse it. Otherwise, allocate a new one. -*/ + * Given a string and a separator character, form a vector by splitting the + * string at occurrences of that separator. Consecutive occurrences of the + * character will result in empty strings added to the vector. Reuse the + * provided vector if non-NULL. + */ struct vector * vector_split(const char *string, char separator, struct vector *vector) { const char *p, *start; size_t i, count; + /* If the vector argument isn't NULL, reuse it. */ vector = vector_reuse(vector); + /* Do a first pass to size the vector. */ count = split_count(string, separator); if (vector->allocated < count) vector_resize(vector, count); - for (start = string, p = string, i = 0; *p; p++) + /* Walk the string and create the new strings with xstrndup. */ + for (start = string, p = string, i = 0; *p != '\0'; p++) if (*p == separator) { vector->strings[i++] = xstrndup(start, p - start); start = p + 1; } vector->strings[i++] = xstrndup(start, p - start); vector->count = i; - return vector; } /* -** Given a modifiable string and a separator character, form a cvector by -** modifying the string in-place to add nuls at the separators and then -** building a vector of pointers into the string. Do a first pass to size -** the vector, and if the third argument isn't NULL, reuse it. Otherwise, -** allocate a new one. -*/ + * Given a modifiable string and a separator character, form a cvector by + * modifying the string in-place to add nuls at the separators and then + * building a vector of pointers into the string. Reuse the provided vector + * if non-NULL. + */ struct cvector * cvector_split(char *string, char separator, struct cvector *vector) { char *p, *start; size_t i, count; + /* If the vector argument isn't NULL, reuse it. */ vector = cvector_reuse(vector); + /* Do a first pass to size the vector. */ count = split_count(string, separator); if (vector->allocated < count) cvector_resize(vector, count); + /* + * Walk the string and replace separators with nul, and store the pointers + * to the start of each string segment. + */ for (start = string, p = string, i = 0; *p; p++) if (*p == separator) { *p = '\0'; @@ -283,7 +310,6 @@ } vector->strings[i++] = start; vector->count = i; - return vector; } @@ -291,7 +317,8 @@ /* * Given a string and a set of separators expressed as a string, count the * number of strings that it will split into when splitting on those - * separators. + * separators. Unlike with split_count, multiple consecutive separator + * characters will be treated the same as a single separator. */ static size_t split_multi_count(const char *string, const char *seps) @@ -299,8 +326,14 @@ const char *p; size_t count; + /* The empty string produces no substrings. */ if (*string == '\0') return 0; + + /* + * Walk the string looking for the first separator not preceeded by + * another separator (and ignore a separator at the start of the string). + */ for (count = 1, p = string + 1; *p != '\0'; p++) if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL) count++; @@ -317,9 +350,8 @@ /* * Given a string, split it at any of the provided separators to form a - * vector, copying each string segment. If the third argument isn't NULL, - * reuse that vector; otherwise, allocate a new one. Any number of - * consecutive separators are considered a single separator. + * vector, copying each string segment. Any number of consecutive separators + * are considered a single separator. Reuse the provided vector if non-NULL. */ struct vector * vector_split_multi(const char *string, const char *seps, @@ -328,13 +360,20 @@ const char *p, *start; size_t i, count; + /* If the vector argument isn't NULL, reuse it. */ vector = vector_reuse(vector); + /* Count the number of strings we'll create and size the vector. */ count = split_multi_count(string, seps); if (vector->allocated < count) vector_resize(vector, count); - for (start = string, p = string, i = 0; *p; p++) + /* + * Walk the string and look for separators. start tracks the + * non-separator that starts a new string, so as long as start == p, we're + * tracking a sequence of separators. + */ + for (start = string, p = string, i = 0; *p != '\0'; p++) if (strchr(seps, *p) != NULL) { if (start != p) vector->strings[i++] = xstrndup(start, p - start); @@ -343,7 +382,6 @@ if (start != p) vector->strings[i++] = xstrndup(start, p - start); vector->count = i; - return vector; } @@ -351,9 +389,8 @@ /* * Given a string, split it at any of the provided separators to form a * vector, destructively modifying the string to nul-terminate each segment. - * If the third argument isn't NULL, reuse that vector; otherwise, allocate a - * new one. Any number of consecutive separators are considered a single - * separator. + * Any number of consecutive separators are considered a single separator. + * Reuse the provided vector if non-NULL. */ struct cvector * cvector_split_multi(char *string, const char *seps, struct cvector *vector) @@ -361,13 +398,21 @@ char *p, *start; size_t i, count; + /* If the vector argument isn't NULL, reuse it. */ vector = cvector_reuse(vector); + /* Count the number of strings we'll create and size the vector. */ count = split_multi_count(string, seps); if (vector->allocated < count) cvector_resize(vector, count); - for (start = string, p = string, i = 0; *p; p++) + /* + * Walk the string and look for separators, replacing the ones that + * terminate a substring with a nul. start tracks the non-separator that + * starts a new string, so as long as start == p, we're tracking a + * sequence of separators. + */ + for (start = string, p = string, i = 0; *p != '\0'; p++) if (strchr(seps, *p) != NULL) { if (start != p) { *p = '\0'; @@ -378,158 +423,119 @@ if (start != p) vector->strings[i++] = start; vector->count = i; - return vector; } /* -** Given a string, count the number of strings that it will split into when -** splitting on whitespace. -*/ -static size_t -split_space_count(const char *string) -{ - const char *p; - size_t count; - - if (*string == '\0') - return 0; - for (count = 1, p = string + 1; *p != '\0'; p++) - if ((*p == ' ' || *p == '\t') && !(p[-1] == ' ' || p[-1] == '\t')) - count++; - - /* If the string ends in whitespace, we've overestimated the number of - strings by one. */ - if (p[-1] == ' ' || p[-1] == '\t') - count--; - return count; -} - - -/* -** Given a string, split it at whitespace to form a vector, copying each -** string segment. If the second argument isn't NULL, reuse that vector; -** otherwise, allocate a new one. Any number of consecutive whitespace -** characters is considered a single separator. -*/ + * Given a string, split it at whitespace to form a vector, copying each + * string segment. Any number of consecutive whitespace characters are + * considered a single separator. Reuse the provided vector if non-NULL. + * This is just a special case of vector_split_multi. + */ struct vector * vector_split_space(const char *string, struct vector *vector) { - const char *p, *start; - size_t i, count; - - vector = vector_reuse(vector); - - count = split_space_count(string); - if (vector->allocated < count) - vector_resize(vector, count); - - for (start = string, p = string, i = 0; *p; p++) - if (*p == ' ' || *p == '\t') { - if (start != p) - vector->strings[i++] = xstrndup(start, p - start); - start = p + 1; - } - if (start != p) - vector->strings[i++] = xstrndup(start, p - start); - vector->count = i; - - return vector; + return vector_split_multi(string, " \t", vector); } /* -** Given a string, split it at whitespace to form a vector, destructively -** modifying the string to nul-terminate each segment. If the second -** argument isn't NULL, reuse that vector; otherwise, allocate a new one. -** Any number of consecutive whitespace characters is considered a single -** separator. -*/ + * Given a string, split it at whitespace to form a vector, destructively + * modifying the string to nul-terminate each segment. Any number of + * consecutive whitespace characters are considered a single separator. Reuse + * the provided vector if non-NULL. This is just a special case of + * cvector_split_multi. + */ struct cvector * cvector_split_space(char *string, struct cvector *vector) { - char *p, *start; - size_t i, count; - - vector = cvector_reuse(vector); - - count = split_space_count(string); - if (vector->allocated < count) - cvector_resize(vector, count); - - for (start = string, p = string, i = 0; *p; p++) - if (*p == ' ' || *p == '\t') { - if (start != p) { - *p = '\0'; - vector->strings[i++] = start; - } - start = p + 1; - } - if (start != p) - vector->strings[i++] = start; - vector->count = i; - - return vector; + return cvector_split_multi(string, " \t", vector); } /* -** Given a vector and a separator string, allocate and build a new string -** composed of all the strings in the vector separated from each other by the -** seperator string. Caller is responsible for freeing. -*/ + * Given a vector and a separator string, allocate and build a new string + * composed of all the strings in the vector separated from each other by the + * separator string. Caller is responsible for freeing. + */ char * -vector_join(const struct vector *vector, const char *seperator) +vector_join(const struct vector *vector, const char *separator) { char *string; size_t i, size, seplen; - seplen = strlen(seperator); - for (size = 0, i = 0; i < vector->count; i++) + /* If the vector is empty, this is trivial. */ + assert(vector != NULL); + if (vector->count == 0) + return xstrdup(""); + + /* + * Determine the total size of the resulting string. Be careful of + * integer overflow while doing so. + */ + seplen = strlen(separator); + for (size = 0, i = 0; i < vector->count; i++) { + assert(SIZE_MAX - size >= strlen(vector->strings[i]) + seplen + 1); size += strlen(vector->strings[i]); + } + assert(SIZE_MAX - size >= (vector->count - 1) * seplen + 1); size += (vector->count - 1) * seplen + 1; + /* Allocate the memory and build up the string using strlcat. */ string = xmalloc(size); strlcpy(string, vector->strings[0], size); for (i = 1; i < vector->count; i++) { - strlcat(string, seperator, size); + strlcat(string, separator, size); strlcat(string, vector->strings[i], size); } - return string; } char * -cvector_join(const struct cvector *vector, const char *seperator) +cvector_join(const struct cvector *vector, const char *separator) { char *string; size_t i, size, seplen; - seplen = strlen(seperator); - for (size = 0, i = 0; i < vector->count; i++) + /* If the vector is empty, this is trivial. */ + assert(vector != NULL); + if (vector->count == 0) + return xstrdup(""); + + /* + * Determine the total size of the resulting string. Be careful of + * integer overflow while doing so. + */ + seplen = strlen(separator); + for (size = 0, i = 0; i < vector->count; i++) { + assert(SIZE_MAX - size >= strlen(vector->strings[i])); size += strlen(vector->strings[i]); + } + assert(SIZE_MAX - size >= (vector->count - 1) * seplen + 1); size += (vector->count - 1) * seplen + 1; + /* Allocate the memory and build up the string using strlcat. */ string = xmalloc(size); strlcpy(string, vector->strings[0], size); for (i = 1; i < vector->count; i++) { - strlcat(string, seperator, size); + strlcat(string, separator, size); strlcat(string, vector->strings[i], size); } - return string; } /* -** Given a vector and a path to a program, exec that program with the vector -** as its arguments. This requires adding a NULL terminator to the vector -** and casting it appropriately. -*/ + * Given a vector and a path to a program, exec that program with the vector + * as its arguments. This requires adding a NULL terminator to the vector + * (which we do not add to count, so it will be invisible to other users of + * the vector) and casting it appropriately. + */ int vector_exec(const char *path, struct vector *vector) { + assert(vector != NULL); if (vector->allocated == vector->count) vector_resize(vector, vector->count + 1); vector->strings[vector->count] = NULL; @@ -539,6 +545,7 @@ int cvector_exec(const char *path, struct cvector *vector) { + assert(vector != NULL); if (vector->allocated == vector->count) cvector_resize(vector, vector->count + 1); vector->strings[vector->count] = NULL; Modified: tests/lib/confparse-t.c =================================================================== --- tests/lib/confparse-t.c 2014-09-03 17:40:31 UTC (rev 9671) +++ tests/lib/confparse-t.c 2014-09-03 17:54:25 UTC (rev 9672) @@ -514,7 +514,7 @@ exit(1); vector = config_params(group); ok_int(67, 2, vector->count); - ok_int(68, 2, vector->allocated); + ok_int(68, 3, vector->allocated); if (strcmp(vector->strings[0], "foo") == 0) ok_string(69, "bar", vector->strings[1]); else if (strcmp(vector->strings[0], "bar") == 0) @@ -637,7 +637,7 @@ vector = config_params(subgroup); ok(153, vector != NULL); ok_int(154, 3, vector->count); - ok_int(155, 3, vector->allocated); + ok_int(155, 4, vector->allocated); ok_string(156, "third", vector->strings[0]); ok_string(157, "second", vector->strings[1]); ok_string(158, "first", vector->strings[2]); ------------------------------ _______________________________________________ inn-committers mailing list inn-committers@lists.isc.org https://lists.isc.org/mailman/listinfo/inn-committers End of inn-committers Digest, Vol 67, Issue 1 *********************************************