2012-11-30 15:21:02 +00:00
|
|
|
/*
|
2015-04-23 14:13:53 +00:00
|
|
|
* Copyright (C) 2012-2015 Red Hat, Inc.
|
2012-11-30 15:21:02 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2019-09-06 12:10:24 +00:00
|
|
|
#include <glib/gprintf.h>
|
2017-06-22 11:34:38 +00:00
|
|
|
#include <locale.h>
|
2020-09-01 11:27:44 +00:00
|
|
|
#ifdef WITH_XLOCALE_H
|
2020-01-27 10:59:19 +00:00
|
|
|
# include <xlocale.h>
|
|
|
|
#endif
|
2013-04-03 10:36:23 +00:00
|
|
|
|
2012-11-30 15:21:02 +00:00
|
|
|
#include "virstring.h"
|
2017-06-21 17:08:28 +00:00
|
|
|
#include "virthread.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2014-01-23 09:28:29 +00:00
|
|
|
#include "virlog.h"
|
2012-11-30 15:21:02 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("util.string");
|
|
|
|
|
2013-04-03 10:36:23 +00:00
|
|
|
/* Like strtol, but produce an "int" result, and check more carefully.
|
|
|
|
Return 0 upon success; return -1 to indicate failure.
|
|
|
|
When END_PTR is NULL, the byte after the final valid digit must be NUL.
|
|
|
|
Otherwise, it's like strtol and lets the caller check any suffix for
|
|
|
|
validity. This function is careful to return -1 when the string S
|
|
|
|
represents a number that is not representable as an "int". */
|
|
|
|
int
|
|
|
|
virStrToLong_i(char const *s, char **end_ptr, int base, int *result)
|
|
|
|
{
|
|
|
|
long int val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtol(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (errno || (!end_ptr && *p) || p == s || (int) val != val);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-01 02:11:09 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce an "unsigned int"
|
|
|
|
* value. This version allows twos-complement wraparound of negative
|
|
|
|
* numbers. */
|
2013-04-03 10:36:23 +00:00
|
|
|
int
|
|
|
|
virStrToLong_ui(char const *s, char **end_ptr, int base, unsigned int *result)
|
|
|
|
{
|
|
|
|
unsigned long int val;
|
|
|
|
char *p;
|
util: fix uint parsing on 64-bit platforms
Commit f22b7899 called to light a long-standing latent bug: the
behavior of virStrToLong_ui was different on 32-bit platforms
than on 64-bit platforms. Curse you, C type promotion and
narrowing rules, and strtoul specification. POSIX says that for
a 32-bit long, strtol handles only 2^32 values [LONG_MIN to
LONG_MAX] while strtoul handles 2^33 - 1 values [-ULONG_MAX to
ULONG_MAX] with twos-complement wraparound for negatives. Thus,
parsing -1 as unsigned long produces ULONG_MAX, rather than a
range error. We WANT[1] this same shortcut for turning -1 into
UINT_MAX when parsing to int; and get it for free with 32-bit
long. But with 64-bit long, ULONG_MAX is outside the range
of int and we were rejecting it as invalid; meanwhile, we were
silently treating -18446744073709551615 as 1 even though it
textually exceeds INT_MIN. Too bad there's not a strtoui() in
libc that does guaranteed parsing to int, regardless of the size
of long.
The bug has been latent since 2007, introduced by Jim Meyering
in commit 5d25419 in the attempt to eradicate unsafe use of
strto[u]l when parsing ints and longs. How embarrassing that we
are only discovering it now - so I'm adding a testsuite to ensure
that it covers all the corner cases we care about.
[1] Ideally, we really want the caller to be able to choose whether
to allow negative numbers to wrap around to their 2s-complement
counterpart, as in strtoul, or to force a stricter input range
of [0 to UINT_MAX] by rejecting negative signs; this will be added
in a later patch for all three int types.
This patch is tested on both 32- and 64-bit; the enhanced
virstringtest passes on both platforms, while virstoragetest now
reliably fails on both platforms instead of just 32-bit platforms.
That test will be fixed later.
* src/util/virstring.c (virStrToLong_ui): Ensure same behavior
regardless of platform long size.
* tests/virstringtest.c (testStringToLong): New function.
(mymain): Comprehensively test string to long parsing.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-04-30 20:46:18 +00:00
|
|
|
bool err = false;
|
2013-04-03 10:36:23 +00:00
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoul(s, &p, base); /* exempt from syntax-check */
|
util: fix uint parsing on 64-bit platforms
Commit f22b7899 called to light a long-standing latent bug: the
behavior of virStrToLong_ui was different on 32-bit platforms
than on 64-bit platforms. Curse you, C type promotion and
narrowing rules, and strtoul specification. POSIX says that for
a 32-bit long, strtol handles only 2^32 values [LONG_MIN to
LONG_MAX] while strtoul handles 2^33 - 1 values [-ULONG_MAX to
ULONG_MAX] with twos-complement wraparound for negatives. Thus,
parsing -1 as unsigned long produces ULONG_MAX, rather than a
range error. We WANT[1] this same shortcut for turning -1 into
UINT_MAX when parsing to int; and get it for free with 32-bit
long. But with 64-bit long, ULONG_MAX is outside the range
of int and we were rejecting it as invalid; meanwhile, we were
silently treating -18446744073709551615 as 1 even though it
textually exceeds INT_MIN. Too bad there's not a strtoui() in
libc that does guaranteed parsing to int, regardless of the size
of long.
The bug has been latent since 2007, introduced by Jim Meyering
in commit 5d25419 in the attempt to eradicate unsafe use of
strto[u]l when parsing ints and longs. How embarrassing that we
are only discovering it now - so I'm adding a testsuite to ensure
that it covers all the corner cases we care about.
[1] Ideally, we really want the caller to be able to choose whether
to allow negative numbers to wrap around to their 2s-complement
counterpart, as in strtoul, or to force a stricter input range
of [0 to UINT_MAX] by rejecting negative signs; this will be added
in a later patch for all three int types.
This patch is tested on both 32- and 64-bit; the enhanced
virstringtest passes on both platforms, while virstoragetest now
reliably fails on both platforms instead of just 32-bit platforms.
That test will be fixed later.
* src/util/virstring.c (virStrToLong_ui): Ensure same behavior
regardless of platform long size.
* tests/virstringtest.c (testStringToLong): New function.
(mymain): Comprehensively test string to long parsing.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-04-30 20:46:18 +00:00
|
|
|
|
|
|
|
/* This one's tricky. We _want_ to allow "-1" as shorthand for
|
|
|
|
* UINT_MAX regardless of whether long is 32-bit or 64-bit. But
|
|
|
|
* strtoul treats "-1" as ULONG_MAX, and going from ulong back
|
|
|
|
* to uint differs depending on the size of long. */
|
|
|
|
if (sizeof(long) > sizeof(int) && memchr(s, '-', p - s)) {
|
|
|
|
if (-val > UINT_MAX)
|
|
|
|
err = true;
|
|
|
|
else
|
|
|
|
val &= 0xffffffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
err |= (errno || (!end_ptr && *p) || p == s || (unsigned int) val != val);
|
2013-04-03 10:36:23 +00:00
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-01 02:11:09 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce an "unsigned int"
|
|
|
|
* value. This version rejects any negative signs. */
|
|
|
|
int
|
|
|
|
virStrToLong_uip(char const *s, char **end_ptr, int base, unsigned int *result)
|
|
|
|
{
|
|
|
|
unsigned long int val;
|
|
|
|
char *p;
|
|
|
|
bool err = false;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoul(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (memchr(s, '-', p - s) ||
|
|
|
|
errno || (!end_ptr && *p) || p == s || (unsigned int) val != val);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-03 10:36:23 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce a "long" value. */
|
|
|
|
int
|
|
|
|
virStrToLong_l(char const *s, char **end_ptr, int base, long *result)
|
|
|
|
{
|
|
|
|
long int val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtol(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-01 02:11:09 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce an "unsigned long"
|
|
|
|
* value. This version allows twos-complement wraparound of negative
|
|
|
|
* numbers. */
|
2013-04-03 10:36:23 +00:00
|
|
|
int
|
|
|
|
virStrToLong_ul(char const *s, char **end_ptr, int base, unsigned long *result)
|
|
|
|
{
|
|
|
|
unsigned long int val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoul(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-01 02:11:09 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce an "unsigned long"
|
|
|
|
* value. This version rejects any negative signs. */
|
|
|
|
int
|
|
|
|
virStrToLong_ulp(char const *s, char **end_ptr, int base,
|
|
|
|
unsigned long *result)
|
|
|
|
{
|
|
|
|
unsigned long int val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoul(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (memchr(s, '-', p - s) ||
|
|
|
|
errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-03 10:36:23 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce a "long long" value. */
|
|
|
|
int
|
|
|
|
virStrToLong_ll(char const *s, char **end_ptr, int base, long long *result)
|
|
|
|
{
|
|
|
|
long long val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoll(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-01 02:11:09 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce an "unsigned long
|
|
|
|
* long" value. This version allows twos-complement wraparound of
|
|
|
|
* negative numbers. */
|
2013-04-03 10:36:23 +00:00
|
|
|
int
|
2014-05-01 02:11:09 +00:00
|
|
|
virStrToLong_ull(char const *s, char **end_ptr, int base,
|
|
|
|
unsigned long long *result)
|
2013-04-03 10:36:23 +00:00
|
|
|
{
|
|
|
|
unsigned long long val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoull(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-01 02:11:09 +00:00
|
|
|
/* Just like virStrToLong_i, above, but produce an "unsigned long
|
|
|
|
* long" value. This version rejects any negative signs. */
|
|
|
|
int
|
|
|
|
virStrToLong_ullp(char const *s, char **end_ptr, int base,
|
|
|
|
unsigned long long *result)
|
|
|
|
{
|
|
|
|
unsigned long long val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
val = strtoull(s, &p, base); /* exempt from syntax-check */
|
|
|
|
err = (memchr(s, '-', p - s) ||
|
|
|
|
errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-21 17:08:28 +00:00
|
|
|
/* In case thread-safe locales are available */
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_NEWLOCALE
|
2017-06-21 17:08:28 +00:00
|
|
|
|
2017-06-22 12:36:53 +00:00
|
|
|
typedef locale_t virLocale;
|
|
|
|
static virLocale virLocaleRaw;
|
2017-06-21 17:08:28 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
virLocaleOnceInit(void)
|
|
|
|
{
|
2017-06-22 12:36:53 +00:00
|
|
|
virLocaleRaw = newlocale(LC_ALL_MASK, "C", (locale_t)0);
|
|
|
|
if (!virLocaleRaw)
|
2017-06-21 17:08:28 +00:00
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virLocale);
|
2017-06-22 12:36:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virLocaleSetRaw:
|
|
|
|
*
|
|
|
|
* @oldlocale: set to old locale pointer
|
|
|
|
*
|
|
|
|
* Sets the locale to 'C' to allow operating on non-localized objects.
|
|
|
|
* Returns 0 on success -1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virLocaleSetRaw(virLocale *oldlocale)
|
|
|
|
{
|
|
|
|
if (virLocaleInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
*oldlocale = uselocale(virLocaleRaw);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virLocaleRevert(virLocale *oldlocale)
|
|
|
|
{
|
|
|
|
uselocale(*oldlocale);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-14 12:45:33 +00:00
|
|
|
virLocaleFixupRadix(char **strp G_GNUC_UNUSED)
|
2017-06-22 12:36:53 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#else /* !WITH_NEWLOCALE */
|
2017-06-22 12:36:53 +00:00
|
|
|
|
|
|
|
typedef int virLocale;
|
|
|
|
|
|
|
|
static int
|
2019-10-14 12:45:33 +00:00
|
|
|
virLocaleSetRaw(virLocale *oldlocale G_GNUC_UNUSED)
|
2017-06-22 12:36:53 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-14 12:45:33 +00:00
|
|
|
virLocaleRevert(virLocale *oldlocale G_GNUC_UNUSED)
|
2017-06-22 12:36:53 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virLocaleFixupRadix(char **strp)
|
|
|
|
{
|
|
|
|
char *radix, *tmp;
|
|
|
|
struct lconv *lc;
|
|
|
|
|
|
|
|
lc = localeconv();
|
|
|
|
radix = lc->decimal_point;
|
|
|
|
tmp = strstr(*strp, radix);
|
|
|
|
if (tmp) {
|
|
|
|
*tmp = '.';
|
|
|
|
if (strlen(radix) > 1)
|
|
|
|
memmove(tmp + 1, tmp + strlen(radix), strlen(*strp) - (tmp - *strp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#endif /* !WITH_NEWLOCALE */
|
2017-06-22 12:36:53 +00:00
|
|
|
|
2017-06-21 17:08:28 +00:00
|
|
|
|
2017-06-21 17:08:29 +00:00
|
|
|
/**
|
|
|
|
* virStrToDouble
|
|
|
|
*
|
|
|
|
* converts string with C locale (thread-safe) to double.
|
|
|
|
*
|
|
|
|
* Returns -1 on error or returns 0 on success.
|
|
|
|
*/
|
2013-04-03 10:36:23 +00:00
|
|
|
int
|
|
|
|
virStrToDouble(char const *s,
|
|
|
|
char **end_ptr,
|
|
|
|
double *result)
|
|
|
|
{
|
2017-06-22 12:36:53 +00:00
|
|
|
virLocale oldlocale;
|
2013-04-03 10:36:23 +00:00
|
|
|
double val;
|
|
|
|
char *p;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
errno = 0;
|
2017-06-22 12:36:53 +00:00
|
|
|
if (virLocaleSetRaw(&oldlocale) < 0)
|
2017-06-21 17:08:29 +00:00
|
|
|
return -1;
|
2013-04-03 10:36:23 +00:00
|
|
|
val = strtod(s, &p); /* exempt from syntax-check */
|
2017-06-22 12:36:53 +00:00
|
|
|
virLocaleRevert(&oldlocale);
|
|
|
|
|
2013-04-03 10:36:23 +00:00
|
|
|
err = (errno || (!end_ptr && *p) || p == s);
|
|
|
|
if (end_ptr)
|
|
|
|
*end_ptr = p;
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
*result = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-21 17:08:28 +00:00
|
|
|
/**
|
|
|
|
* virDoubleToStr
|
|
|
|
*
|
|
|
|
* converts double to string with C locale (thread-safe).
|
|
|
|
*
|
2019-10-19 10:09:32 +00:00
|
|
|
* Returns: 0 on success, -1 otherwise.
|
2017-06-21 17:08:28 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
virDoubleToStr(char **strp, double number)
|
|
|
|
{
|
2017-06-22 12:36:53 +00:00
|
|
|
virLocale oldlocale;
|
2017-06-21 17:08:28 +00:00
|
|
|
|
2017-06-22 12:36:53 +00:00
|
|
|
if (virLocaleSetRaw(&oldlocale) < 0)
|
|
|
|
return -1;
|
2017-06-21 17:08:28 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
*strp = g_strdup_printf("%lf", number);
|
2017-06-21 17:08:28 +00:00
|
|
|
|
2017-06-22 12:36:53 +00:00
|
|
|
virLocaleRevert(&oldlocale);
|
|
|
|
virLocaleFixupRadix(strp);
|
2017-06-21 17:08:28 +00:00
|
|
|
|
2019-10-19 10:09:32 +00:00
|
|
|
return 0;
|
2017-06-21 17:08:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-03 10:36:23 +00:00
|
|
|
/**
|
2018-07-20 11:00:44 +00:00
|
|
|
* virStrcpy:
|
|
|
|
*
|
|
|
|
* @dest: destination buffer
|
|
|
|
* @src: source buffer
|
2018-12-04 17:08:14 +00:00
|
|
|
* @destbytes: number of bytes the destination can accommodate
|
2018-07-20 11:00:44 +00:00
|
|
|
*
|
2021-03-02 10:00:23 +00:00
|
|
|
* Copies @src to @dest. @dest is guaranteed to be 'nul' terminated if
|
|
|
|
* destbytes is 1 or more.
|
2018-07-20 11:00:44 +00:00
|
|
|
*
|
2021-03-02 10:00:23 +00:00
|
|
|
* Returns: 0 on success, -1 if @src doesn't fit into @dest and was truncated.
|
2013-04-03 10:36:23 +00:00
|
|
|
*/
|
2018-07-20 07:50:37 +00:00
|
|
|
int
|
2013-04-03 10:36:23 +00:00
|
|
|
virStrcpy(char *dest, const char *src, size_t destbytes)
|
|
|
|
{
|
2021-03-02 10:00:23 +00:00
|
|
|
if (g_strlcpy(dest, src, destbytes) >= destbytes)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2013-04-03 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virSkipSpaces:
|
|
|
|
* @str: pointer to the char pointer used
|
|
|
|
*
|
|
|
|
* Skip potential blanks, this includes space tabs, line feed,
|
|
|
|
* carriage returns.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virSkipSpaces(const char **str)
|
|
|
|
{
|
|
|
|
const char *cur = *str;
|
|
|
|
|
2019-11-18 14:10:02 +00:00
|
|
|
while (g_ascii_isspace(*cur))
|
2013-04-03 10:36:23 +00:00
|
|
|
cur++;
|
|
|
|
*str = cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virSkipSpacesAndBackslash:
|
|
|
|
* @str: pointer to the char pointer used
|
|
|
|
*
|
|
|
|
* Like virSkipSpaces, but also skip backslashes erroneously emitted
|
|
|
|
* by xend
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virSkipSpacesAndBackslash(const char **str)
|
|
|
|
{
|
|
|
|
const char *cur = *str;
|
|
|
|
|
2019-11-18 14:10:02 +00:00
|
|
|
while (g_ascii_isspace(*cur) || *cur == '\\')
|
2013-04-03 10:36:23 +00:00
|
|
|
cur++;
|
|
|
|
*str = cur;
|
|
|
|
}
|
|
|
|
|
2021-01-08 00:55:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virSkipToDigit:
|
|
|
|
* @str: pointer to the char pointer used
|
|
|
|
*
|
|
|
|
* Skip over any character that is not 0-9
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virSkipToDigit(const char **str)
|
|
|
|
{
|
|
|
|
const char *cur = *str;
|
|
|
|
|
|
|
|
while (*cur && !g_ascii_isdigit(*cur))
|
|
|
|
cur++;
|
|
|
|
*str = cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-03 10:36:23 +00:00
|
|
|
/**
|
|
|
|
* virTrimSpaces:
|
|
|
|
* @str: string to modify to remove all trailing spaces
|
|
|
|
* @endp: track the end of the string
|
|
|
|
*
|
|
|
|
* If @endp is NULL on entry, then all spaces prior to the trailing
|
|
|
|
* NUL in @str are removed, by writing NUL into the appropriate
|
|
|
|
* location. If @endp is non-NULL but points to a NULL pointer,
|
|
|
|
* then all spaces prior to the trailing NUL in @str are removed,
|
|
|
|
* NUL is written to the new string end, and endp is set to the
|
|
|
|
* location of the (new) string end. If @endp is non-NULL and
|
|
|
|
* points to a non-NULL pointer, then that pointer is used as
|
|
|
|
* the end of the string, endp is set to the (new) location, but
|
|
|
|
* no NUL pointer is written into the string.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virTrimSpaces(char *str, char **endp)
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
if (!endp || !*endp)
|
|
|
|
end = str + strlen(str);
|
|
|
|
else
|
|
|
|
end = *endp;
|
2019-11-18 14:10:02 +00:00
|
|
|
while (end > str && g_ascii_isspace(end[-1]))
|
2013-04-03 10:36:23 +00:00
|
|
|
end--;
|
|
|
|
if (endp) {
|
|
|
|
if (!*endp)
|
|
|
|
*end = '\0';
|
|
|
|
*endp = end;
|
|
|
|
} else {
|
|
|
|
*end = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virSkipSpacesBackwards:
|
|
|
|
* @str: start of string
|
|
|
|
* @endp: on entry, *endp must be NULL or a location within @str, on exit,
|
|
|
|
* will be adjusted to skip trailing spaces, or to NULL if @str had nothing
|
|
|
|
* but spaces.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virSkipSpacesBackwards(const char *str, char **endp)
|
|
|
|
{
|
|
|
|
/* Casting away const is safe, since virTrimSpaces does not
|
|
|
|
* modify string with this particular usage. */
|
|
|
|
char *s = (char*) str;
|
|
|
|
|
|
|
|
if (!*endp)
|
|
|
|
*endp = s + strlen(s);
|
|
|
|
virTrimSpaces(s, endp);
|
|
|
|
if (s == *endp)
|
|
|
|
*endp = NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-22 08:26:42 +00:00
|
|
|
/**
|
|
|
|
* virStringIsEmpty:
|
|
|
|
* @str: string to check
|
|
|
|
*
|
|
|
|
* Returns true if string is empty (may contain only whitespace) or NULL.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virStringIsEmpty(const char *str)
|
|
|
|
{
|
|
|
|
if (!str)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
virSkipSpaces(&str);
|
|
|
|
return str[0] == '\0';
|
|
|
|
}
|
|
|
|
|
2013-04-25 16:05:00 +00:00
|
|
|
|
2013-11-28 11:14:59 +00:00
|
|
|
/**
|
|
|
|
* virStringSortCompare:
|
|
|
|
*
|
|
|
|
* A comparator function for sorting strings in
|
|
|
|
* normal order with qsort().
|
|
|
|
*/
|
|
|
|
int virStringSortCompare(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const char **sa = (const char**)a;
|
|
|
|
const char **sb = (const char**)b;
|
|
|
|
|
|
|
|
return strcmp(*sa, *sb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virStringSortRevCompare:
|
|
|
|
*
|
|
|
|
* A comparator function for sorting strings in
|
|
|
|
* reverse order with qsort().
|
|
|
|
*/
|
|
|
|
int virStringSortRevCompare(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const char **sa = (const char**)a;
|
|
|
|
const char **sb = (const char**)b;
|
|
|
|
|
|
|
|
return strcmp(*sb, *sa);
|
|
|
|
}
|
2014-01-23 09:28:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virStringSearch:
|
|
|
|
* @str: string to search
|
|
|
|
* @regexp: POSIX Extended regular expression pattern used for matching
|
|
|
|
* @max_matches: maximum number of substrings to return
|
2017-12-23 09:49:08 +00:00
|
|
|
* @matches: pointer to an array to be filled with NULL terminated list of matches
|
2014-01-23 09:28:29 +00:00
|
|
|
*
|
|
|
|
* Performs a POSIX extended regex search against a string and return all matching substrings.
|
2020-08-02 22:01:11 +00:00
|
|
|
* The @matches value should be freed with g_strfreev() when no longer
|
2014-01-23 09:28:29 +00:00
|
|
|
* required.
|
|
|
|
*
|
|
|
|
* @code
|
|
|
|
* char *source = "6853a496-1c10-472e-867a-8244937bd6f0
|
|
|
|
* 773ab075-4cd7-4fc2-8b6e-21c84e9cb391
|
|
|
|
* bbb3c75c-d60f-43b0-b802-fd56b84a4222
|
|
|
|
* 60c04aa1-0375-4654-8d9f-e149d9885273
|
|
|
|
* 4548d465-9891-4c34-a184-3b1c34a26aa8";
|
|
|
|
* char **matches = NULL;
|
|
|
|
* virStringSearch(source,
|
|
|
|
* "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})",
|
|
|
|
* 3,
|
|
|
|
* &matches);
|
|
|
|
*
|
|
|
|
* // matches[0] == "6853a496-1c10-472e-867a-8244937bd6f0";
|
|
|
|
* // matches[1] == "773ab075-4cd7-4fc2-8b6e-21c84e9cb391";
|
|
|
|
* // matches[2] == "bbb3c75c-d60f-43b0-b802-fd56b84a4222"
|
|
|
|
* // matches[3] == NULL;
|
|
|
|
*
|
2020-08-02 22:01:11 +00:00
|
|
|
* g_strfreev(matches);
|
2014-01-23 09:28:29 +00:00
|
|
|
* @endcode
|
|
|
|
*
|
|
|
|
* Returns: -1 on error, or number of matches
|
|
|
|
*/
|
|
|
|
ssize_t
|
|
|
|
virStringSearch(const char *str,
|
|
|
|
const char *regexp,
|
|
|
|
size_t max_matches,
|
|
|
|
char ***matches)
|
|
|
|
{
|
2019-11-13 14:54:43 +00:00
|
|
|
g_autoptr(GRegex) regex = NULL;
|
|
|
|
g_autoptr(GError) err = NULL;
|
2014-01-23 09:28:29 +00:00
|
|
|
size_t nmatches = 0;
|
|
|
|
ssize_t ret = -1;
|
|
|
|
|
|
|
|
*matches = NULL;
|
|
|
|
|
|
|
|
VIR_DEBUG("search '%s' for '%s'", str, regexp);
|
|
|
|
|
2019-11-13 14:54:43 +00:00
|
|
|
regex = g_regex_new(regexp, 0, 0, &err);
|
|
|
|
if (!regex) {
|
2014-01-23 09:28:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2019-11-13 14:54:43 +00:00
|
|
|
_("Failed to compile regex %s"), err->message);
|
2014-01-23 09:28:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-11-13 14:54:43 +00:00
|
|
|
if (g_regex_get_capture_count(regex) != 1) {
|
2014-01-23 09:28:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2019-11-13 14:54:43 +00:00
|
|
|
_("Regular expression '%s' must have exactly 1 match group, not %d"),
|
|
|
|
regexp, g_regex_get_capture_count(regex));
|
2014-01-23 09:28:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* '*matches' must always be NULL terminated in every iteration
|
|
|
|
* of the loop, so start by allocating 1 element
|
|
|
|
*/
|
2021-03-19 23:37:03 +00:00
|
|
|
VIR_EXPAND_N(*matches, nmatches, 1);
|
2014-01-23 09:28:29 +00:00
|
|
|
|
|
|
|
while ((nmatches - 1) < max_matches) {
|
2019-11-13 14:54:43 +00:00
|
|
|
g_autoptr(GMatchInfo) info = NULL;
|
2014-01-23 09:28:29 +00:00
|
|
|
char *match;
|
2019-11-13 14:54:43 +00:00
|
|
|
int endpos;
|
2014-01-23 09:28:29 +00:00
|
|
|
|
2019-11-13 14:54:43 +00:00
|
|
|
if (!g_regex_match(regex, str, 0, &info))
|
2014-01-23 09:28:29 +00:00
|
|
|
break;
|
|
|
|
|
2021-03-19 23:37:03 +00:00
|
|
|
VIR_EXPAND_N(*matches, nmatches, 1);
|
2014-01-23 09:28:29 +00:00
|
|
|
|
2019-11-13 14:54:43 +00:00
|
|
|
match = g_match_info_fetch(info, 1);
|
2014-01-23 09:28:29 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("Got '%s'", match);
|
|
|
|
|
|
|
|
(*matches)[nmatches-2] = match;
|
|
|
|
|
2019-11-13 14:54:43 +00:00
|
|
|
g_match_info_fetch_pos(info, 1, NULL, &endpos);
|
|
|
|
str += endpos;
|
2014-01-23 09:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = nmatches - 1; /* don't count the trailing null */
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2014-01-23 09:28:29 +00:00
|
|
|
if (ret < 0) {
|
2022-01-28 17:42:45 +00:00
|
|
|
g_clear_pointer(matches, g_strfreev);
|
2014-01-23 09:28:29 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2014-02-19 20:30:46 +00:00
|
|
|
|
2017-05-11 15:16:13 +00:00
|
|
|
/**
|
|
|
|
* virStringMatch:
|
|
|
|
* @str: string to match
|
|
|
|
* @regexp: POSIX Extended regular expression pattern used for matching
|
|
|
|
*
|
|
|
|
* Performs a POSIX extended regex match against a string.
|
|
|
|
* Returns true on match, false on error or no match.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virStringMatch(const char *str,
|
|
|
|
const char *regexp)
|
|
|
|
{
|
2019-11-13 15:01:19 +00:00
|
|
|
g_autoptr(GRegex) regex = NULL;
|
|
|
|
g_autoptr(GError) err = NULL;
|
2017-05-11 15:16:13 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("match '%s' for '%s'", str, regexp);
|
|
|
|
|
2019-11-13 15:01:19 +00:00
|
|
|
regex = g_regex_new(regexp, 0, 0, &err);
|
|
|
|
if (!regex) {
|
|
|
|
VIR_WARN("Failed to compile regex %s", err->message);
|
2017-05-11 15:16:13 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-11-13 15:01:19 +00:00
|
|
|
return g_regex_match(regex, str, 0, NULL);
|
2017-05-11 15:16:13 +00:00
|
|
|
}
|
|
|
|
|
2014-02-19 20:30:46 +00:00
|
|
|
/**
|
|
|
|
* virStringReplace:
|
|
|
|
* @haystack: the source string to process
|
|
|
|
* @oldneedle: the substring to locate
|
|
|
|
* @newneedle: the substring to insert
|
|
|
|
*
|
2014-04-20 20:07:46 +00:00
|
|
|
* Search @haystack and replace all occurrences of @oldneedle with @newneedle.
|
2014-02-19 20:30:46 +00:00
|
|
|
*
|
|
|
|
* Returns: a new string with all the replacements, or NULL on error
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
virStringReplace(const char *haystack,
|
|
|
|
const char *oldneedle,
|
|
|
|
const char *newneedle)
|
|
|
|
{
|
2020-07-03 02:30:20 +00:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2014-02-19 20:30:46 +00:00
|
|
|
const char *tmp1, *tmp2;
|
|
|
|
size_t oldneedlelen = strlen(oldneedle);
|
|
|
|
size_t newneedlelen = strlen(newneedle);
|
|
|
|
|
|
|
|
tmp1 = haystack;
|
|
|
|
tmp2 = NULL;
|
|
|
|
|
|
|
|
while (tmp1) {
|
|
|
|
tmp2 = strstr(tmp1, oldneedle);
|
|
|
|
|
|
|
|
if (tmp2) {
|
|
|
|
virBufferAdd(&buf, tmp1, (tmp2 - tmp1));
|
|
|
|
virBufferAdd(&buf, newneedle, newneedlelen);
|
|
|
|
tmp2 += oldneedlelen;
|
|
|
|
} else {
|
|
|
|
virBufferAdd(&buf, tmp1, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp1 = tmp2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
}
|
2014-10-07 15:27:40 +00:00
|
|
|
|
2019-03-06 16:05:03 +00:00
|
|
|
bool
|
|
|
|
virStringHasSuffix(const char *str,
|
|
|
|
const char *suffix)
|
|
|
|
{
|
|
|
|
int len = strlen(str);
|
|
|
|
int suffixlen = strlen(suffix);
|
|
|
|
|
|
|
|
if (len < suffixlen)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return STREQ(str + len - suffixlen, suffix);
|
|
|
|
}
|
|
|
|
|
2019-03-07 09:17:19 +00:00
|
|
|
bool
|
2019-03-06 14:07:26 +00:00
|
|
|
virStringHasCaseSuffix(const char *str,
|
|
|
|
const char *suffix)
|
|
|
|
{
|
|
|
|
int len = strlen(str);
|
|
|
|
int suffixlen = strlen(suffix);
|
|
|
|
|
|
|
|
if (len < suffixlen)
|
2019-03-07 09:17:19 +00:00
|
|
|
return false;
|
2019-03-06 14:07:26 +00:00
|
|
|
|
|
|
|
return STRCASEEQ(str + len - suffixlen, suffix);
|
|
|
|
}
|
2014-10-07 15:27:40 +00:00
|
|
|
|
2019-03-07 09:18:38 +00:00
|
|
|
bool
|
2019-03-06 14:30:04 +00:00
|
|
|
virStringStripSuffix(char *str,
|
|
|
|
const char *suffix)
|
|
|
|
{
|
|
|
|
int len = strlen(str);
|
|
|
|
int suffixlen = strlen(suffix);
|
|
|
|
|
|
|
|
if (len < suffixlen)
|
2019-03-07 09:18:38 +00:00
|
|
|
return false;
|
2019-03-06 14:30:04 +00:00
|
|
|
|
|
|
|
if (STRNEQ(str + len - suffixlen, suffix))
|
2019-03-07 09:18:38 +00:00
|
|
|
return false;
|
2019-03-06 14:30:04 +00:00
|
|
|
|
|
|
|
str[len - suffixlen] = '\0';
|
|
|
|
|
2019-03-07 09:18:38 +00:00
|
|
|
return true;
|
2019-03-06 14:30:04 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 09:18:58 +00:00
|
|
|
bool
|
2019-03-06 14:45:15 +00:00
|
|
|
virStringMatchesNameSuffix(const char *file,
|
|
|
|
const char *name,
|
|
|
|
const char *suffix)
|
|
|
|
{
|
|
|
|
int filelen = strlen(file);
|
|
|
|
int namelen = strlen(name);
|
|
|
|
int suffixlen = strlen(suffix);
|
|
|
|
|
|
|
|
if (filelen == (namelen + suffixlen) &&
|
|
|
|
STREQLEN(file, name, namelen) &&
|
2019-03-07 09:47:21 +00:00
|
|
|
STREQ(file + namelen, suffix))
|
2019-03-07 09:18:58 +00:00
|
|
|
return true;
|
2019-03-06 14:45:15 +00:00
|
|
|
else
|
2019-03-07 09:18:58 +00:00
|
|
|
return false;
|
2019-03-06 14:45:15 +00:00
|
|
|
}
|
|
|
|
|
2014-10-07 15:27:40 +00:00
|
|
|
/**
|
|
|
|
* virStringStripIPv6Brackets:
|
|
|
|
* @str: the string to strip
|
|
|
|
*
|
|
|
|
* Modify the string in-place to remove the leading and closing brackets
|
|
|
|
* from an IPv6 address.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virStringStripIPv6Brackets(char *str)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
|
|
|
len = strlen(str);
|
|
|
|
if (str[0] == '[' && str[len - 1] == ']' && strchr(str, ':')) {
|
|
|
|
memmove(&str[0], &str[1], len - 2);
|
|
|
|
str[len - 2] = '\0';
|
|
|
|
}
|
|
|
|
}
|
2015-04-14 10:30:16 +00:00
|
|
|
|
|
|
|
|
2017-04-26 14:26:53 +00:00
|
|
|
/**
|
|
|
|
* virStringHasChars:
|
|
|
|
* @str: string to look for chars in
|
|
|
|
* @chars: chars to find in string @str
|
|
|
|
*
|
|
|
|
* Returns true if @str contains any of the chars in @chars.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virStringHasChars(const char *str,
|
|
|
|
const char *chars)
|
|
|
|
{
|
|
|
|
if (!str)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return str[strcspn(str, chars)] != '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-14 10:30:16 +00:00
|
|
|
static const char control_chars[] =
|
|
|
|
"\x01\x02\x03\x04\x05\x06\x07"
|
|
|
|
"\x08" /* \t \n */ "\x0B\x0C" /* \r */ "\x0E\x0F"
|
|
|
|
"\x10\x11\x12\x13\x14\x15\x16\x17"
|
|
|
|
"\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
|
|
|
|
|
|
|
|
bool
|
|
|
|
virStringHasControlChars(const char *str)
|
|
|
|
{
|
2017-04-26 14:26:53 +00:00
|
|
|
return virStringHasChars(str, control_chars);
|
2015-04-14 10:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virStringStripControlChars:
|
|
|
|
* @str: the string to strip
|
|
|
|
*
|
|
|
|
* Modify the string in-place to remove the control characters
|
|
|
|
* in the interval: [0x01, 0x20)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virStringStripControlChars(char *str)
|
|
|
|
{
|
|
|
|
size_t len, i, j;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
|
|
|
len = strlen(str);
|
|
|
|
for (i = 0, j = 0; i < len; i++) {
|
2015-04-23 14:13:53 +00:00
|
|
|
if (strchr(control_chars, str[i]))
|
2015-04-14 10:30:16 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
str[j++] = str[i];
|
|
|
|
}
|
|
|
|
str[j] = '\0';
|
|
|
|
}
|
2015-06-15 16:53:58 +00:00
|
|
|
|
2017-12-18 14:46:53 +00:00
|
|
|
/**
|
|
|
|
* virStringFilterChars:
|
|
|
|
* @str: the string to strip
|
|
|
|
* @valid: the valid characters for the string
|
|
|
|
*
|
|
|
|
* Modify the string in-place to remove the characters that aren't
|
|
|
|
* in the list of valid ones.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virStringFilterChars(char *str, const char *valid)
|
|
|
|
{
|
|
|
|
size_t len, i, j;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
|
|
|
len = strlen(str);
|
|
|
|
for (i = 0, j = 0; i < len; i++) {
|
|
|
|
if (strchr(valid, str[i]))
|
|
|
|
str[j++] = str[i];
|
|
|
|
}
|
|
|
|
str[j] = '\0';
|
|
|
|
}
|
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
/**
|
|
|
|
* virStringToUpper:
|
2018-01-03 09:56:35 +00:00
|
|
|
* @src string to capitalize
|
2015-06-15 16:53:58 +00:00
|
|
|
* @dst: where to store the new capitalized string
|
|
|
|
*
|
|
|
|
* Capitalize the string with replacement of all '-' characters for '_'
|
|
|
|
* characters. Caller frees the result.
|
|
|
|
*
|
2016-11-15 14:00:08 +00:00
|
|
|
* Returns 0 if src is NULL, 1 if capitalization was successful, -1 on failure.
|
2015-06-15 16:53:58 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
virStringToUpper(char **dst, const char *src)
|
|
|
|
{
|
|
|
|
char *cap = NULL;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!src)
|
|
|
|
return 0;
|
|
|
|
|
2020-10-05 17:13:12 +00:00
|
|
|
cap = g_new0(char, strlen(src) + 1);
|
2015-06-15 16:53:58 +00:00
|
|
|
|
|
|
|
for (i = 0; src[i]; i++) {
|
2019-11-18 14:16:33 +00:00
|
|
|
cap[i] = g_ascii_toupper(src[i]);
|
2015-06-15 16:53:58 +00:00
|
|
|
if (cap[i] == '-')
|
|
|
|
cap[i] = '_';
|
|
|
|
}
|
|
|
|
|
|
|
|
*dst = cap;
|
|
|
|
return 1;
|
|
|
|
}
|
2016-04-11 11:35:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virStringIsPrintable:
|
|
|
|
*
|
|
|
|
* Returns true @str contains only printable characters.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virStringIsPrintable(const char *str)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; str[i]; i++)
|
2019-11-18 14:14:47 +00:00
|
|
|
if (!g_ascii_isprint(str[i]))
|
2016-04-11 11:35:25 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2016-05-13 11:15:15 +00:00
|
|
|
|
|
|
|
|
2016-05-12 15:43:39 +00:00
|
|
|
/**
|
|
|
|
* virBufferIsPrintable:
|
|
|
|
*
|
|
|
|
* Returns true if @buf of @buflen contains only printable characters
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virStringBufferIsPrintable(const uint8_t *buf,
|
|
|
|
size_t buflen)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < buflen; i++)
|
2019-11-18 14:14:47 +00:00
|
|
|
if (!g_ascii_isprint(buf[i]))
|
2016-05-12 15:43:39 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-10 08:16:11 +00:00
|
|
|
/**
|
|
|
|
* virStringTrimOptionalNewline:
|
|
|
|
* @str: the string to modify in-place
|
|
|
|
*
|
|
|
|
* Modify @str to remove a single '\n' character
|
|
|
|
* from its end, if one exists.
|
|
|
|
*/
|
|
|
|
void virStringTrimOptionalNewline(char *str)
|
|
|
|
{
|
2017-11-22 20:52:17 +00:00
|
|
|
size_t len = strlen(str);
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (str[len - 1] == '\n')
|
|
|
|
str[len - 1] = '\0';
|
2017-05-10 08:16:11 +00:00
|
|
|
}
|
2017-07-13 13:31:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virStringParsePort:
|
|
|
|
* @str: port number to parse
|
|
|
|
* @port: pointer to parse port into
|
|
|
|
*
|
|
|
|
* Parses a string representation of a network port and validates it. Returns
|
|
|
|
* 0 on success and -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virStringParsePort(const char *str,
|
2017-07-20 10:42:53 +00:00
|
|
|
unsigned int *port)
|
2017-07-13 13:31:50 +00:00
|
|
|
{
|
|
|
|
unsigned int p = 0;
|
|
|
|
|
|
|
|
*port = 0;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (virStrToLong_uip(str, NULL, 10, &p) < 0) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("failed to parse port number '%s'"), str);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p > UINT16_MAX) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("port '%s' out of range"), str);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*port = p;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-13 06:30:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virStringParseYesNo:
|
|
|
|
* @str: "yes|no" to parse, must not be NULL.
|
|
|
|
* @result: pointer to the boolean result of @str conversion
|
|
|
|
*
|
|
|
|
* Parses a "yes|no" string and converts it into a boolean.
|
|
|
|
*
|
|
|
|
* Returns 0 on success and -1 on error.
|
|
|
|
*/
|
|
|
|
int virStringParseYesNo(const char *str, bool *result)
|
|
|
|
{
|
|
|
|
if (STREQ(str, "yes"))
|
|
|
|
*result = true;
|
|
|
|
else if (STREQ(str, "no"))
|
|
|
|
*result = false;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-02-23 23:44:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2020-02-23 23:47:18 +00:00
|
|
|
* virStringParseVersion:
|
2020-02-23 23:44:01 +00:00
|
|
|
* @version: unsigned long pointer to output the version number
|
2020-02-23 23:47:18 +00:00
|
|
|
* @str: const char pointer to the version string
|
2020-02-23 23:44:01 +00:00
|
|
|
* @allowMissing: true to treat 3 like 3.0.0, false to error out on
|
|
|
|
* missing minor or micro
|
|
|
|
*
|
|
|
|
* Parse an unsigned version number from a version string. Expecting
|
|
|
|
* 'major.minor.micro' format, ignoring an optional suffix.
|
|
|
|
*
|
|
|
|
* The major, minor and micro numbers are encoded into a single version number:
|
|
|
|
*
|
|
|
|
* 1000000 * major + 1000 * minor + micro
|
|
|
|
*
|
|
|
|
* Returns the 0 for success, -1 for error.
|
|
|
|
*/
|
|
|
|
int
|
2020-02-23 23:47:18 +00:00
|
|
|
virStringParseVersion(unsigned long *version,
|
|
|
|
const char *str,
|
2020-02-23 23:44:01 +00:00
|
|
|
bool allowMissing)
|
|
|
|
{
|
|
|
|
unsigned int major, minor = 0, micro = 0;
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
if (virStrToLong_ui(str, &tmp, 10, &major) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!allowMissing && *tmp != '.')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((*tmp == '.') && virStrToLong_ui(tmp + 1, &tmp, 10, &minor) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!allowMissing && *tmp != '.')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((*tmp == '.') && virStrToLong_ui(tmp + 1, &tmp, 10, µ) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (major > UINT_MAX / 1000000 || minor > 999 || micro > 999)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
*version = 1000000 * major + 1000 * minor + micro;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|