1
0

string: make VIR_STRDUP easier to use

While reviewing proposed VIR_STRDUP conversions, I've already noticed
several places that do:

if (str && VIR_STRDUP(dest, str) < 0)

which can be simplified by allowing str to be NULL (something that
strdup() doesn't allow).  Meanwhile, code that wants to ensure a
non-NULL dest regardless of the source can check for <= 0.

Also, make it part of the VIR_STRDUP contract that macro arguments
are evaluated exactly once.

* src/util/virstring.h (VIR_STRDUP, VIR_STRDUP_QUIET, VIR_STRNDUP)
(VIR_STRNDUP_QUIET): Improve contract.
* src/util/virstring.c (virStrdup, virStrndup): Change return
conventions.
* docs/hacking.html.in: Document this.
* HACKING: Regenerate.

Signed-off-by: Eric Blake <eblake@redhat.com>

Conflicts:
	HACKING
	docs/hacking.html.in

(cherry picked from commit 6b74a9f5d98e066f8dfdf5d5ccda68230b516246)
This commit is contained in:
Eric Blake 2013-05-06 20:42:44 -06:00 committed by Guido Günther
parent 9d4a1af78c
commit 6c06d86d9a
4 changed files with 47 additions and 20 deletions

17
HACKING
View File

@ -234,6 +234,11 @@ But if negating a complex condition is too ugly, then at least add braces:
Preprocessor Preprocessor
============ ============
Macros defined with an ALL_CAPS name should generally be assumed to be unsafe
with regards to arguments with side-effects (that is, MAX(a++, b--) might
increment a or decrement b too many or too few times). Exceptions to this rule
are explicitly documented for macros in viralloc.h and virstring.h.
For variadic macros, stick with C99 syntax: For variadic macros, stick with C99 syntax:
#define vshPrint(_ctl, ...) fprintf(stdout, __VA_ARGS__) #define vshPrint(_ctl, ...) fprintf(stdout, __VA_ARGS__)
@ -539,12 +544,12 @@ virStrncpy(dest, src, strlen(src), sizeof(dest)).
VIR_STRNDUP(char *dst, const char *src, size_t n); VIR_STRNDUP(char *dst, const char *src, size_t n);
You should avoid using strdup or strndup directly as they do not report You should avoid using strdup or strndup directly as they do not report
out-of-memory error. Use VIR_STRDUP or VIR_STRNDUP macros instead. Note, that out-of-memory error, and do not allow a NULL source. Use VIR_STRDUP or
these two behave similar to VIR_ALLOC: on success zero is returned, otherwise VIR_STRNDUP macros instead, which return 0 for NULL source, 1 for successful
the result is -1 and dst is guaranteed to be NULL. In very specific cases, copy, and -1 for allocation failure with the error already reported. In very
when you don't want to report the out-of-memory error, you can use specific cases, when you don't want to report the out-of-memory error, you can
VIR_STRDUP_QUIET or VIR_STRNDUP_QUIET, but such usage is very rare and usually use VIR_STRDUP_QUIET or VIR_STRNDUP_QUIET, but such usage is very rare and
considered a flaw. usually considered a flaw.
Variable length string buffer Variable length string buffer

View File

@ -303,7 +303,14 @@
} }
</pre> </pre>
<h2><a href="types">Preprocessor</a></h2> <h2><a name="preprocessor">Preprocessor</a></h2>
<p>Macros defined with an ALL_CAPS name should generally be
assumed to be unsafe with regards to arguments with side-effects
(that is, MAX(a++, b--) might increment a or decrement b too
many or too few times). Exceptions to this rule are explicitly
documented for macros in viralloc.h and virstring.h.
</p>
<p> <p>
For variadic macros, stick with C99 syntax: For variadic macros, stick with C99 syntax:
@ -647,9 +654,10 @@
</pre> </pre>
<p> <p>
You should avoid using strdup or strndup directly as they do not report You should avoid using strdup or strndup directly as they do not report
out-of-memory error. Use VIR_STRDUP or VIR_STRNDUP macros instead. Note, out-of-memory error, and do not allow a NULL source. Use
that these two behave similar to VIR_ALLOC: on success zero is returned, VIR_STRDUP or VIR_STRNDUP macros instead, which return 0 for
otherwise the result is -1 and dst is guaranteed to be NULL. In very NULL source, 1 for successful copy, and -1 for allocation
failure with the error already reported. In very
specific cases, when you don't want to report the out-of-memory error, you specific cases, when you don't want to report the out-of-memory error, you
can use VIR_STRDUP_QUIET or VIR_STRNDUP_QUIET, but such usage is very rare can use VIR_STRDUP_QUIET or VIR_STRNDUP_QUIET, but such usage is very rare
and usually considered a flaw. and usually considered a flaw.

View File

@ -194,7 +194,7 @@ size_t virStringListLength(char **strings)
* caller's body where virStrdup is called from. Consider * caller's body where virStrdup is called from. Consider
* using VIR_STRDUP which sets these automatically. * using VIR_STRDUP which sets these automatically.
* *
* Returns: 0 on success, -1 otherwise. * Returns: 0 for NULL src, 1 on successful copy, -1 otherwise.
*/ */
int int
virStrdup(char **dest, virStrdup(char **dest,
@ -205,13 +205,15 @@ virStrdup(char **dest,
const char *funcname, const char *funcname,
size_t linenr) size_t linenr)
{ {
if (!src)
return 0;
if (!(*dest = strdup(src))) { if (!(*dest = strdup(src))) {
if (report) if (report)
virReportOOMErrorFull(domcode, filename, funcname, linenr); virReportOOMErrorFull(domcode, filename, funcname, linenr);
return -1; return -1;
} }
return 0; return 1;
} }
/** /**
@ -231,7 +233,7 @@ virStrdup(char **dest,
* caller's body where virStrndup is called from. Consider * caller's body where virStrndup is called from. Consider
* using VIR_STRNDUP which sets these automatically. * using VIR_STRNDUP which sets these automatically.
* *
* Returns: 0 on success, -1 otherwise. * Returns: 0 for NULL src, 1 on successful copy, -1 otherwise.
*/ */
int int
virStrndup(char **dest, virStrndup(char **dest,
@ -243,11 +245,13 @@ virStrndup(char **dest,
const char *funcname, const char *funcname,
size_t linenr) size_t linenr)
{ {
if (!src)
return 0;
if (!(*dest = strndup(src, n))) { if (!(*dest = strndup(src, n))) {
if (report) if (report)
virReportOOMErrorFull(domcode, filename, funcname, linenr); virReportOOMErrorFull(domcode, filename, funcname, linenr);
return -1; return -1;
} }
return 0; return 1;
} }

View File

@ -40,11 +40,11 @@ size_t virStringListLength(char **strings);
/* Don't call these directly - use the macros below */ /* Don't call these directly - use the macros below */
int virStrdup(char **dest, const char *src, bool report, int domcode, int virStrdup(char **dest, const char *src, bool report, int domcode,
const char *filename, const char *funcname, size_t linenr) const char *filename, const char *funcname, size_t linenr)
ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1);
int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode, int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode,
const char *filename, const char *funcname, size_t linenr) const char *filename, const char *funcname, size_t linenr)
ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1);
/** /**
* VIR_STRDUP: * VIR_STRDUP:
@ -53,7 +53,10 @@ int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode,
* *
* Duplicate @src string and store it into @dst. * Duplicate @src string and store it into @dst.
* *
* Returns -1 on failure (with OOM error reported), 0 on success * This macro is safe to use on arguments with side effects.
*
* Returns -1 on failure (with OOM error reported), 0 if @src was NULL,
* 1 if @src was copied
*/ */
# define VIR_STRDUP(dst, src) virStrdup(&(dst), src, true, VIR_FROM_THIS, \ # define VIR_STRDUP(dst, src) virStrdup(&(dst), src, true, VIR_FROM_THIS, \
__FILE__, __FUNCTION__, __LINE__) __FILE__, __FUNCTION__, __LINE__)
@ -65,7 +68,9 @@ int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode,
* *
* Duplicate @src string and store it into @dst. * Duplicate @src string and store it into @dst.
* *
* Returns -1 on failure, 0 on success * This macro is safe to use on arguments with side effects.
*
* Returns -1 on failure, 0 if @src was NULL, 1 if @src was copied
*/ */
# define VIR_STRDUP_QUIET(dst, src) virStrdup(&(dst), src, false, 0, NULL, NULL, 0) # define VIR_STRDUP_QUIET(dst, src) virStrdup(&(dst), src, false, 0, NULL, NULL, 0)
@ -78,7 +83,10 @@ int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode,
* Duplicate @src string and store it into @dst. If @src is longer than @n, * Duplicate @src string and store it into @dst. If @src is longer than @n,
* only @n bytes are copied and terminating null byte '\0' is added. * only @n bytes are copied and terminating null byte '\0' is added.
* *
* Returns -1 on failure (with OOM error reported), 0 on success * This macro is safe to use on arguments with side effects.
*
* Returns -1 on failure (with OOM error reported), 0 if @src was NULL,
* 1 if @src was copied
*/ */
# define VIR_STRNDUP(dst, src, n) virStrndup(&(dst), src, n, true, \ # define VIR_STRNDUP(dst, src, n) virStrndup(&(dst), src, n, true, \
VIR_FROM_THIS, __FILE__, \ VIR_FROM_THIS, __FILE__, \
@ -93,7 +101,9 @@ int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode,
* Duplicate @src string and store it into @dst. If @src is longer than @n, * Duplicate @src string and store it into @dst. If @src is longer than @n,
* only @n bytes are copied and terminating null byte '\0' is added. * only @n bytes are copied and terminating null byte '\0' is added.
* *
* Returns -1 on failure, 0 on success * This macro is safe to use on arguments with side effects.
*
* Returns -1 on failure, 0 if @src was NULL, 1 if @src was copied
*/ */
# define VIR_STRNDUP_QUIET(dst, src, n) virStrndup(&(dst), src, n, false, \ # define VIR_STRNDUP_QUIET(dst, src, n) virStrndup(&(dst), src, n, false, \
0, NULL, NULL, 0) 0, NULL, NULL, 0)