2010-11-16 07:47:35 -06:00
|
|
|
#ifndef UTIL_LINUX_STRUTILS
|
|
|
|
#define UTIL_LINUX_STRUTILS
|
|
|
|
|
2013-01-29 08:25:47 -06:00
|
|
|
#include <stdlib.h>
|
2010-11-16 07:47:35 -06:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <string.h>
|
2010-11-24 09:41:20 -06:00
|
|
|
#include <sys/types.h>
|
2013-09-23 08:39:34 -05:00
|
|
|
#include <ctype.h>
|
2015-01-10 12:03:25 -06:00
|
|
|
#include <stdio.h>
|
2016-04-22 06:59:06 -05:00
|
|
|
#include <errno.h>
|
2021-05-06 04:06:45 -05:00
|
|
|
#include <time.h>
|
2010-11-16 07:47:35 -06:00
|
|
|
|
2020-03-17 11:24:28 -05:00
|
|
|
#include "c.h"
|
|
|
|
|
2017-06-22 15:15:14 -05:00
|
|
|
/* initialize a custom exit code for all *_or_err functions */
|
|
|
|
extern void strutils_set_exitcode(int exit_code);
|
2012-03-30 09:11:31 -05:00
|
|
|
|
2013-01-29 08:25:47 -06:00
|
|
|
extern int parse_size(const char *str, uintmax_t *res, int *power);
|
2010-11-16 07:47:35 -06:00
|
|
|
extern int strtosize(const char *str, uintmax_t *res);
|
2012-05-15 10:40:03 -05:00
|
|
|
extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
|
|
|
|
|
|
|
|
extern int16_t strtos16_or_err(const char *str, const char *errmesg);
|
|
|
|
extern uint16_t strtou16_or_err(const char *str, const char *errmesg);
|
2016-10-12 07:00:44 -05:00
|
|
|
extern uint16_t strtox16_or_err(const char *str, const char *errmesg);
|
2012-05-15 10:40:03 -05:00
|
|
|
|
|
|
|
extern int32_t strtos32_or_err(const char *str, const char *errmesg);
|
|
|
|
extern uint32_t strtou32_or_err(const char *str, const char *errmesg);
|
2016-10-12 07:00:44 -05:00
|
|
|
extern uint32_t strtox32_or_err(const char *str, const char *errmesg);
|
2012-05-15 10:40:03 -05:00
|
|
|
|
|
|
|
extern int64_t strtos64_or_err(const char *str, const char *errmesg);
|
|
|
|
extern uint64_t strtou64_or_err(const char *str, const char *errmesg);
|
2016-10-12 07:00:44 -05:00
|
|
|
extern uint64_t strtox64_or_err(const char *str, const char *errmesg);
|
2012-05-15 10:40:03 -05:00
|
|
|
|
2011-09-25 04:03:07 -05:00
|
|
|
extern double strtod_or_err(const char *str, const char *errmesg);
|
2021-05-06 04:06:45 -05:00
|
|
|
extern long double strtold_or_err(const char *str, const char *errmesg);
|
2012-05-15 10:40:03 -05:00
|
|
|
|
2010-11-16 07:47:35 -06:00
|
|
|
extern long strtol_or_err(const char *str, const char *errmesg);
|
2011-05-24 15:56:38 -05:00
|
|
|
extern unsigned long strtoul_or_err(const char *str, const char *errmesg);
|
2010-11-16 07:47:35 -06:00
|
|
|
|
2013-03-13 05:51:15 -05:00
|
|
|
extern void strtotimeval_or_err(const char *str, struct timeval *tv,
|
|
|
|
const char *errmesg);
|
2021-05-06 04:06:45 -05:00
|
|
|
extern time_t strtotime_or_err(const char *str, const char *errmesg);
|
2013-03-13 05:51:15 -05:00
|
|
|
|
2017-01-28 05:39:30 -06:00
|
|
|
extern int isdigit_strend(const char *str, const char **end);
|
|
|
|
#define isdigit_string(_s) isdigit_strend(_s, NULL)
|
|
|
|
|
|
|
|
extern int isxdigit_strend(const char *str, const char **end);
|
|
|
|
#define isxdigit_string(_s) isxdigit_strend(_s, NULL)
|
|
|
|
|
2013-01-28 09:16:11 -06:00
|
|
|
|
2015-02-24 05:04:22 -06:00
|
|
|
extern int parse_switch(const char *arg, const char *errmesg, ...);
|
2015-02-22 08:41:37 -06:00
|
|
|
|
2013-01-30 17:49:08 -06:00
|
|
|
#ifndef HAVE_MEMPCPY
|
|
|
|
extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n);
|
|
|
|
#endif
|
2010-11-16 07:47:35 -06:00
|
|
|
#ifndef HAVE_STRNLEN
|
|
|
|
extern size_t strnlen(const char *s, size_t maxlen);
|
|
|
|
#endif
|
|
|
|
#ifndef HAVE_STRNDUP
|
|
|
|
extern char *strndup(const char *s, size_t n);
|
|
|
|
#endif
|
|
|
|
#ifndef HAVE_STRNCHR
|
|
|
|
extern char *strnchr(const char *s, size_t maxlen, int c);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* caller guarantees n > 0 */
|
|
|
|
static inline void xstrncpy(char *dest, const char *src, size_t n)
|
|
|
|
{
|
2020-03-17 11:24:28 -05:00
|
|
|
size_t len = src ? strlen(src) : 0;
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
return;
|
|
|
|
len = min(len, n - 1);
|
|
|
|
memcpy(dest, src, len);
|
|
|
|
dest[len] = 0;
|
2010-11-16 07:47:35 -06:00
|
|
|
}
|
2010-11-24 09:41:20 -06:00
|
|
|
|
2018-10-03 10:03:11 -05:00
|
|
|
/* This is like strncpy(), but based on memcpy(), so compilers and static
|
|
|
|
* analyzers do not complain when sizeof(destination) is the same as 'n' and
|
|
|
|
* result is not terminated by zero.
|
|
|
|
*
|
|
|
|
* Use this function to copy string to logs with fixed sizes (wtmp/utmp. ...)
|
|
|
|
* where string terminator is optional.
|
|
|
|
*/
|
|
|
|
static inline void *str2memcpy(void *dest, const char *src, size_t n)
|
|
|
|
{
|
|
|
|
size_t bytes = strlen(src) + 1;
|
|
|
|
|
|
|
|
if (bytes > n)
|
|
|
|
bytes = n;
|
|
|
|
|
|
|
|
memcpy(dest, src, bytes);
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline char *mem2strcpy(char *dest, const void *src, size_t n, size_t nmax)
|
|
|
|
{
|
|
|
|
if (n + 1 > nmax)
|
|
|
|
n = nmax - 1;
|
|
|
|
|
|
|
|
memcpy(dest, src, n);
|
|
|
|
dest[nmax-1] = '\0';
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2020-01-15 05:58:29 -06:00
|
|
|
/* Reallocate @str according to @newstr and copy @newstr to @str; returns new @str.
|
|
|
|
* The @str is not modified if reallocation failed (like classic realloc()).
|
|
|
|
*/
|
2019-06-26 09:47:24 -05:00
|
|
|
static inline char * __attribute__((warn_unused_result))
|
|
|
|
strrealloc(char *str, const char *newstr)
|
|
|
|
{
|
|
|
|
size_t nsz, osz;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return newstr ? strdup(newstr) : NULL;
|
2020-01-15 05:58:29 -06:00
|
|
|
if (!newstr)
|
|
|
|
return NULL;
|
2019-06-26 09:47:24 -05:00
|
|
|
|
|
|
|
osz = strlen(str);
|
|
|
|
nsz = strlen(newstr);
|
|
|
|
|
2020-01-15 05:58:29 -06:00
|
|
|
if (nsz > osz)
|
|
|
|
str = realloc(str, nsz + 1);
|
|
|
|
if (str)
|
|
|
|
memcpy(str, newstr, nsz + 1);
|
2019-06-26 09:47:24 -05:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2019-10-23 05:28:09 -05:00
|
|
|
/* Copy string @str to struct @stru to member addressed by @offset */
|
2016-04-22 06:59:06 -05:00
|
|
|
static inline int strdup_to_offset(void *stru, size_t offset, const char *str)
|
2013-01-29 08:25:47 -06:00
|
|
|
{
|
2016-04-22 06:59:06 -05:00
|
|
|
char **o;
|
2019-10-23 05:28:09 -05:00
|
|
|
char *p = NULL;
|
2013-01-29 08:25:47 -06:00
|
|
|
|
2016-04-22 06:59:06 -05:00
|
|
|
if (!stru)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
o = (char **) ((char *) stru + offset);
|
2013-01-29 08:25:47 -06:00
|
|
|
if (str) {
|
2019-10-23 05:28:09 -05:00
|
|
|
p = strdup(str);
|
|
|
|
if (!p)
|
2016-04-22 06:59:06 -05:00
|
|
|
return -ENOMEM;
|
2013-01-29 08:25:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
free(*o);
|
2019-10-23 05:28:09 -05:00
|
|
|
*o = p;
|
2016-04-22 06:59:06 -05:00
|
|
|
return 0;
|
2013-01-29 08:25:47 -06:00
|
|
|
}
|
|
|
|
|
2019-10-23 05:28:09 -05:00
|
|
|
/* Copy string __str to struct member _m of the struct _s */
|
2013-01-29 08:25:47 -06:00
|
|
|
#define strdup_to_struct_member(_s, _m, _str) \
|
|
|
|
strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
|
|
|
|
|
2019-10-23 05:28:09 -05:00
|
|
|
/* Copy string addressed by @offset between two structs */
|
|
|
|
static inline int strdup_between_offsets(void *stru_dst, void *stru_src, size_t offset)
|
|
|
|
{
|
|
|
|
char **src;
|
|
|
|
char **dst;
|
|
|
|
char *p = NULL;
|
|
|
|
|
|
|
|
if (!stru_src || !stru_dst)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
src = (char **) ((char *) stru_src + offset);
|
|
|
|
dst = (char **) ((char *) stru_dst + offset);
|
|
|
|
|
|
|
|
if (*src) {
|
|
|
|
p = strdup(*src);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(*dst);
|
|
|
|
*dst = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy string addressed by struct member between two instances of the same
|
|
|
|
* struct type */
|
|
|
|
#define strdup_between_structs(_dst, _src, _m) \
|
|
|
|
strdup_between_offsets((void *)_dst, (void *)_src, offsetof(__typeof__(*(_src)), _m))
|
|
|
|
|
|
|
|
|
2018-09-14 09:26:38 -05:00
|
|
|
extern char *xstrmode(mode_t mode, char *str);
|
2011-05-26 08:17:25 -05:00
|
|
|
|
|
|
|
/* Options for size_to_human_string() */
|
|
|
|
enum
|
|
|
|
{
|
2019-02-12 06:27:56 -06:00
|
|
|
SIZE_SUFFIX_1LETTER = 0,
|
|
|
|
SIZE_SUFFIX_3LETTER = (1 << 0),
|
|
|
|
SIZE_SUFFIX_SPACE = (1 << 1),
|
|
|
|
SIZE_DECIMAL_2DIGITS = (1 << 2)
|
2011-05-26 08:17:25 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
extern char *size_to_human_string(int options, uint64_t bytes);
|
2010-11-24 09:41:20 -06:00
|
|
|
|
2011-07-27 09:41:21 -05:00
|
|
|
extern int string_to_idarray(const char *list, int ary[], size_t arysz,
|
|
|
|
int (name2id)(const char *, size_t));
|
2012-07-26 08:52:04 -05:00
|
|
|
extern int string_add_to_idarray(const char *list, int ary[],
|
2015-04-22 04:08:05 -05:00
|
|
|
size_t arysz, size_t *ary_pos,
|
2012-07-26 08:52:04 -05:00
|
|
|
int (name2id)(const char *, size_t));
|
|
|
|
|
2011-07-27 09:41:21 -05:00
|
|
|
extern int string_to_bitarray(const char *list, char *ary,
|
|
|
|
int (*name2bit)(const char *, size_t));
|
|
|
|
|
2012-05-03 10:28:44 -05:00
|
|
|
extern int string_to_bitmask(const char *list,
|
|
|
|
unsigned long *mask,
|
|
|
|
long (*name2flag)(const char *, size_t));
|
2011-10-14 15:32:15 -05:00
|
|
|
extern int parse_range(const char *str, int *lower, int *upper, int def);
|
2011-10-11 22:18:12 -05:00
|
|
|
|
2016-08-08 10:23:54 -05:00
|
|
|
extern int streq_paths(const char *a, const char *b);
|
2011-11-08 09:25:01 -06:00
|
|
|
|
2013-09-10 05:18:20 -05:00
|
|
|
/*
|
|
|
|
* Match string beginning.
|
|
|
|
*/
|
|
|
|
static inline const char *startswith(const char *s, const char *prefix)
|
|
|
|
{
|
|
|
|
size_t sz = prefix ? strlen(prefix) : 0;
|
|
|
|
|
|
|
|
if (s && sz && strncmp(s, prefix, sz) == 0)
|
|
|
|
return s + sz;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Case insensitive match string beginning.
|
|
|
|
*/
|
|
|
|
static inline const char *startswith_no_case(const char *s, const char *prefix)
|
|
|
|
{
|
|
|
|
size_t sz = prefix ? strlen(prefix) : 0;
|
|
|
|
|
|
|
|
if (s && sz && strncasecmp(s, prefix, sz) == 0)
|
|
|
|
return s + sz;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Match string ending.
|
|
|
|
*/
|
|
|
|
static inline const char *endswith(const char *s, const char *postfix)
|
|
|
|
{
|
|
|
|
size_t sl = s ? strlen(s) : 0;
|
|
|
|
size_t pl = postfix ? strlen(postfix) : 0;
|
|
|
|
|
|
|
|
if (pl == 0)
|
2018-07-20 05:52:09 -05:00
|
|
|
return s + sl;
|
2013-09-10 05:18:20 -05:00
|
|
|
if (sl < pl)
|
|
|
|
return NULL;
|
|
|
|
if (memcmp(s + sl - pl, postfix, pl) != 0)
|
|
|
|
return NULL;
|
2018-07-20 05:52:09 -05:00
|
|
|
return s + sl - pl;
|
2013-09-10 05:18:20 -05:00
|
|
|
}
|
2013-08-29 09:50:17 -05:00
|
|
|
|
2013-09-23 08:39:34 -05:00
|
|
|
/*
|
|
|
|
* Skip leading white space.
|
|
|
|
*/
|
|
|
|
static inline const char *skip_space(const char *p)
|
|
|
|
{
|
|
|
|
while (isspace(*p))
|
|
|
|
++p;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char *skip_blank(const char *p)
|
|
|
|
{
|
|
|
|
while (isblank(*p))
|
|
|
|
++p;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2014-08-25 08:31:53 -05:00
|
|
|
|
|
|
|
/* Removes whitespace from the right-hand side of a string (trailing
|
|
|
|
* whitespace).
|
|
|
|
*
|
|
|
|
* Returns size of the new string (without \0).
|
|
|
|
*/
|
|
|
|
static inline size_t rtrim_whitespace(unsigned char *str)
|
|
|
|
{
|
2016-09-04 05:15:34 -05:00
|
|
|
size_t i;
|
2014-08-25 08:31:53 -05:00
|
|
|
|
2016-09-04 05:15:34 -05:00
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
i = strlen((char *) str);
|
2014-11-22 02:18:17 -06:00
|
|
|
while (i) {
|
|
|
|
i--;
|
|
|
|
if (!isspace(str[i])) {
|
|
|
|
i++;
|
2014-08-25 08:31:53 -05:00
|
|
|
break;
|
2014-11-22 02:18:17 -06:00
|
|
|
}
|
2014-08-25 08:31:53 -05:00
|
|
|
}
|
2014-11-22 02:18:17 -06:00
|
|
|
str[i] = '\0';
|
2014-08-25 08:31:53 -05:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Removes whitespace from the left-hand side of a string.
|
|
|
|
*
|
|
|
|
* Returns size of the new string (without \0).
|
|
|
|
*/
|
|
|
|
static inline size_t ltrim_whitespace(unsigned char *str)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
unsigned char *p;
|
|
|
|
|
2016-09-04 05:15:34 -05:00
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
for (p = str; *p && isspace(*p); p++);
|
2014-08-25 08:31:53 -05:00
|
|
|
|
|
|
|
len = strlen((char *) p);
|
|
|
|
|
2017-08-29 07:31:06 -05:00
|
|
|
if (p > str)
|
2014-08-25 08:31:53 -05:00
|
|
|
memmove(str, p, len + 1);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2020-07-21 05:16:40 -05:00
|
|
|
/* Removes left-hand, right-hand and repeating whitespaces.
|
|
|
|
*/
|
2021-06-14 09:24:59 -05:00
|
|
|
static inline size_t __normalize_whitespace(
|
|
|
|
const unsigned char *src,
|
|
|
|
size_t sz,
|
|
|
|
unsigned char *dst,
|
|
|
|
size_t len)
|
2020-07-21 05:16:40 -05:00
|
|
|
{
|
2021-06-14 09:24:59 -05:00
|
|
|
size_t i, x = 0;
|
2020-07-21 05:16:40 -05:00
|
|
|
int nsp = 0, intext = 0;
|
|
|
|
|
|
|
|
if (!sz)
|
2021-06-14 09:24:59 -05:00
|
|
|
goto done;
|
2020-07-21 05:16:40 -05:00
|
|
|
|
2021-06-14 09:24:59 -05:00
|
|
|
for (i = 0, x = 0; i < sz && x < len - 1; ) {
|
|
|
|
if (isspace(src[i]))
|
2020-07-21 05:16:40 -05:00
|
|
|
nsp++;
|
|
|
|
else
|
|
|
|
nsp = 0, intext = 1;
|
|
|
|
|
|
|
|
if (nsp > 1 || (nsp && !intext))
|
|
|
|
i++;
|
|
|
|
else
|
2021-06-14 09:24:59 -05:00
|
|
|
dst[x++] = src[i++];
|
2020-07-21 05:16:40 -05:00
|
|
|
}
|
2021-06-14 09:24:59 -05:00
|
|
|
if (nsp && x > 0) /* tailing space */
|
2020-07-21 05:16:40 -05:00
|
|
|
x--;
|
2021-06-14 09:24:59 -05:00
|
|
|
done:
|
|
|
|
dst[x] = '\0';
|
2020-07-21 05:16:40 -05:00
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2021-06-14 09:24:59 -05:00
|
|
|
static inline size_t normalize_whitespace(unsigned char *str)
|
|
|
|
{
|
|
|
|
size_t sz = strlen((char *) str);
|
|
|
|
return __normalize_whitespace(str, sz, str, sz + 1);
|
|
|
|
}
|
|
|
|
|
2019-04-11 06:13:06 -05:00
|
|
|
static inline void strrep(char *s, int find, int replace)
|
|
|
|
{
|
|
|
|
while (s && *s && (s = strchr(s, find)) != NULL)
|
|
|
|
*s++ = replace;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void strrem(char *s, int rem)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
2019-05-16 15:56:57 -05:00
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
for (p = s; *s; s++) {
|
2019-04-11 06:13:06 -05:00
|
|
|
if (*s != rem)
|
|
|
|
*p++ = *s;
|
|
|
|
}
|
2019-05-16 05:44:38 -05:00
|
|
|
*p = '\0';
|
2019-04-11 06:13:06 -05:00
|
|
|
}
|
|
|
|
|
2015-05-15 07:57:04 -05:00
|
|
|
extern char *strnappend(const char *s, const char *suffix, size_t b);
|
|
|
|
extern char *strappend(const char *s, const char *suffix);
|
2016-05-11 08:31:02 -05:00
|
|
|
extern char *strfappend(const char *s, const char *format, ...)
|
|
|
|
__attribute__ ((__format__ (__printf__, 2, 0)));
|
2015-05-15 07:57:04 -05:00
|
|
|
extern const char *split(const char **state, size_t *l, const char *separator, int quoted);
|
|
|
|
|
2015-01-10 12:03:25 -06:00
|
|
|
extern int skip_fline(FILE *fp);
|
2021-02-23 02:19:48 -06:00
|
|
|
extern int ul_stralnumcmp(const char *p1, const char *p2);
|
2015-01-10 12:03:25 -06:00
|
|
|
|
2010-11-16 07:47:35 -06:00
|
|
|
#endif
|