[ccan] [PATCH] stringbuilder: Functions for joining strings.

David Gibson david at gibson.dropbear.id.au
Sat Aug 23 11:40:30 EST 2014


On Sat, Aug 23, 2014 at 09:28:20AM +1000, Stuart Longland wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA512
> 
> On 18/08/14 13:26, David Gibson wrote:
> > On Sun, Aug 17, 2014 at 09:16:05PM +1000, 
> > stuartl at longlandclan.yi.org wrote:
> >> From: Stuart Longland <stuartl at longlandclan.yi.org>
> >> 
> >> This is a small couple of functions for joining lists of strings 
> >> together.  The lists can either be varadic arguments or arrays, 
> >> and delimiters are optional.
> > 
> > So, I'm a bit torn as to whether this makes more sense as its own 
> > module or an extension to the 'str' module.  I'd also suggest 
> > putting "join" somewhere in the title, since that's the name the 
> > operation you're performing often goes by (including in the tal/str
> > module).
> 
> I did think about this too… I thought I'd play it safe by putting it
> by itself, with a view that it could be merged with the 'str' module
> if needed.

Ok.

> As to naming, I was thinking of "building up a string", since delim
> can be NULL indicating no delimiter, and then it's like repeatedly
> calling strcat, except it'll take care of how long the destination
> buffer is.
> 
> > [snip]
> >> diff --git a/ccan/stringbuilder/stringbuilder.c 
> >> b/ccan/stringbuilder/stringbuilder.c new file mode 100644 index 
> >> 0000000..cb2bc2d --- /dev/null +++ 
> >> b/ccan/stringbuilder/stringbuilder.c @@ -0,0 +1,71 @@ +/* CC0 
> >> (Public domain) - see LICENSE file for details */ +#include 
> >> <ccan/stringbuilder/stringbuilder.h> +#include <string.h> 
> >> +#include <errno.h> + +int stringbuilder(const char* delim, char*
> >> str, size_t str_sz, ...)
> > 
> > I'd suggest putting the destination arguments (str and str_sz) 
> > first in all these functions, to match sprintf(), strcat() and 
> > others.
> 
> A fair point, I'll look into this.
> 
> >> +{ +	int res; +	va_list ap; +	va_start(ap, str_sz); +	res = 
> >> stringbuilder_va(delim, str, str_sz, ap); +	va_end(ap); +	return
> >>  res; +} + +static inline int stringbuilder_cpy( +		char** str, 
> >> size_t* str_sz, const char* s, size_t s_len)
> > 
> > Because this isn't in a header, you shouldn't specify "inline" here
> > - the compiler can work out for itself whether it's best to 
> > inline.
> 
> Ahh okay, my intent was to hint to the compiler that the function
> *should* be inlined since it'll be called a lot.

For a static function, a modern compiler can work that out better
than you can.

[snip]
> >> +/** + * stringbuilder - Join strings from a varadic list.  The 
> >> list of arguments + * are all assumed to be of type const char* 
> >> and must end with a NULL.  If the + * first argument is str, then
> >> the contents of str are preserved and appended + * to. + * + *
> >> @delim:	A delimiter to separate the strings with, or NULL. + * 
> >> @str:	A pointer to a string buffer that will receive the result. 
> >> + * @str_sz:	Size of the buffer pointed to by str. + * + * 
> >> Returns:	0 on success + * 		EMSGSIZE if the resulting string 
> >> would overflow the buffer. + * 		If an overflow condition is 
> >> detected, the buffer content is + * 		NOT defined.
> > 
> > Returning an errno code is not a normal error interface.  Usually 
> > you'd return -1 (or something negative), and set errno to 
> > EMSGSIZE.
> 
> My preference for doing the former is that it's more thread safe,
> something errno is definitely not.

So, errno is generally threadsafe these days, albeit via somewhat
tortured means.  Generally modern errno will be either a magic macro,
or (for compilers that support it) a thread local variable, which will
reference a per-thread errno value.

> I recognise that it's not conventional practice but if I'm going to
> return one thing for "all okay" and another for "error", then why not
> return the error code via a route where it's less likely to get
> splattered by another system call?

Hrm.  I'm torn.  You're right that returning the error code is a
better interface, but I'm not sure that's a strong enough reason to
break with convention.

At the very least I'd suggest returning -EWHATEVER, rather than
EWHATEVER.  I think when people see a function returning an integer
status, they're going to expect that (rc < 0) is a suitable check for
an error condition.

> >> + * + * Example: + * 	int res; + * 	char file_name[80]; + * 	res
> >>  = stringbuilder("/", file_name, sizeof(file_name), + * 
> >> "/var/lib/foo", "bar", "baz", + * 		NULL); + * 	if (res) + * 
> >> printf("Failed to determine file name: %s", + * strerror(res));
> >> + * 	else + * 		printf("File is at %s", file_name); + */ +int 
> >> stringbuilder(const char* delim, char* str, size_t str_sz, ...);
> > 
> > It would be nice to use a macro wrapper around this to avoid having
> > to explicitly include the terminating NULL.
> 
> Ahh okay, I hadn't thought of that option.  I'll do so.

Yeah, it's becoming a common ccan idiom.

[snip]
> >> + +/* + * Even though stringbuilder is implemented using + * 
> >> stringbuilder_va, we need to test it explicitly.
> > 
> > Really?  Why?
> 
> I honestly don't know, but it wasn't happy until I did so.
> Or maybe I was misinterpreting ccanlint.  It'll give a score, but then
> leaves me to figure out what I missed.
> 
> Perhaps I haven't fully figured out how to drive it yet. :-)

Hrm, that is very odd.  I wonder if Rusty has any ideas why that would
be.

> >> +int main(int argc, char *argv[]) +{ +	char string[20]; +	const 
> >> char* str_array[] = { +		"xxx", "yyy" +	}; +	int res; + +	res = 
> >> test_stringbuilder_va(NULL, string, sizeof(string), +			"aaa", 
> >> "bbb", NULL); +	printf("res: %s, string: %s\n", + strerror(res), 
> >> string); +	ok1(res == 0); +	ok1(!strcmp(string, "aaabbb"));
> > 
> > I suggest using streq() from ccan/str, rather than the potentially 
> > confusing !strcmp construction.
> 
> I did try this, but had all kinds of issues with getting the tests to
> compile.  I don't recall the exact errors I got.

This is also very strange

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.ozlabs.org/pipermail/ccan/attachments/20140823/12da5db0/attachment-0001.sig>


More information about the ccan mailing list