You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

399 lines
9.6 KiB

#ifndef UTIL_LINUX_STRUTILS
#define UTIL_LINUX_STRUTILS
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include "c.h"
/* initialize a custom exit code for all *_or_err functions */
extern void strutils_set_exitcode(int exit_code);
extern int parse_size(const char *str, uintmax_t *res, int *power);
extern int strtosize(const char *str, uintmax_t *res);
extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
extern int ul_strtos64(const char *str, int64_t *num, int base);
extern int ul_strtou64(const char *str, uint64_t *num, int base);
extern int ul_strtos32(const char *str, int32_t *num, int base);
extern int ul_strtou32(const char *str, uint32_t *num, int base);
extern int64_t str2num_or_err(const char *str, int base, const char *errmesg, int64_t low, int64_t up);
extern uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up);
#define strtos64_or_err(_s, _e) str2num_or_err(_s, 10, _e, 0, 0)
#define strtou64_or_err(_s, _e) str2unum_or_err(_s, 10, _e, 0)
#define strtox64_or_err(_s, _e) str2unum_or_err(_s, 16, _e, 0)
#define strtos32_or_err(_s, _e) (int32_t) str2num_or_err(_s, 10, _e, INT32_MIN, INT32_MAX)
#define strtou32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 10, _e, UINT32_MAX)
#define strtox32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 16, _e, UINT32_MAX)
#define strtos16_or_err(_s, _e) (int16_t) str2num_or_err(_s, 10, _e, INT16_MIN, INT16_MAX)
#define strtou16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 10, _e, UINT16_MAX)
#define strtox16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 16, _e, UINT16_MAX)
extern double strtod_or_err(const char *str, const char *errmesg);
extern long double strtold_or_err(const char *str, const char *errmesg);
#define strtol_or_err(_s, _e) (long) str2num_or_err(_s, 10, _e, LONG_MIN, LONG_MAX)
#define strtoul_or_err(_s, _e) (unsigned long) str2unum_or_err(_s, 10, _e, ULONG_MAX)
extern void strtotimeval_or_err(const char *str, struct timeval *tv,
const char *errmesg);
extern time_t strtotime_or_err(const char *str, const char *errmesg);
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)
extern int parse_switch(const char *arg, const char *errmesg, ...);
#ifndef HAVE_MEMPCPY
extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n);
#endif
#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)
{
size_t len = src ? strlen(src) : 0;
if (!len)
return;
len = min(len, n - 1);
memcpy(dest, src, len);
dest[len] = 0;
}
/* 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 * __attribute__((nonnull (1)))
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 * __attribute__((nonnull (1)))
mem2strcpy(char *dest, const void *src, size_t n, size_t nmax)
{
if (n + 1 > nmax)
n = nmax - 1;
memset(dest, '\0', nmax);
memcpy(dest, src, n);
return dest;
}
/* Reallocate @str according to @newstr and copy @newstr to @str; returns new @str.
* The @str is not modified if reallocation failed (like classic realloc()).
*/
static inline char * __attribute__((warn_unused_result))
strrealloc(char *str, const char *newstr)
{
size_t nsz, osz;
if (!str)
return newstr ? strdup(newstr) : NULL;
if (!newstr)
return NULL;
osz = strlen(str);
nsz = strlen(newstr);
if (nsz > osz)
str = realloc(str, nsz + 1);
if (str)
memcpy(str, newstr, nsz + 1);
return str;
}
/* Copy string @str to struct @stru to member addressed by @offset */
static inline int strdup_to_offset(void *stru, size_t offset, const char *str)
{
char **o;
char *p = NULL;
if (!stru)
return -EINVAL;
o = (char **) ((char *) stru + offset);
if (str) {
p = strdup(str);
if (!p)
return -ENOMEM;
}
free(*o);
*o = p;
return 0;
}
/* Copy string __str to struct member _m of the struct _s */
#define strdup_to_struct_member(_s, _m, _str) \
strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
/* 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))
extern char *xstrmode(mode_t mode, char *str);
/* Options for size_to_human_string() */
enum
{
SIZE_SUFFIX_1LETTER = 0,
SIZE_SUFFIX_3LETTER = (1 << 0),
SIZE_SUFFIX_SPACE = (1 << 1),
SIZE_DECIMAL_2DIGITS = (1 << 2)
};
extern char *size_to_human_string(int options, uint64_t bytes);
extern int string_to_idarray(const char *list, int ary[], size_t arysz,
int (name2id)(const char *, size_t));
extern int string_add_to_idarray(const char *list, int ary[],
size_t arysz, size_t *ary_pos,
int (name2id)(const char *, size_t));
extern int string_to_bitarray(const char *list, char *ary,
int (*name2bit)(const char *, size_t));
extern int string_to_bitmask(const char *list,
unsigned long *mask,
long (*name2flag)(const char *, size_t));
extern int parse_range(const char *str, int *lower, int *upper, int def);
extern int streq_paths(const char *a, const char *b);
/*
* 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)
return s + sl;
if (sl < pl)
return NULL;
if (memcmp(s + sl - pl, postfix, pl) != 0)
return NULL;
return s + sl - pl;
}
/*
* 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;
}
/* 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)
{
size_t i;
if (!str)
return 0;
i = strlen((char *) str);
while (i) {
i--;
if (!isspace(str[i])) {
i++;
break;
}
}
str[i] = '\0';
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;
if (!str)
return 0;
for (p = str; *p && isspace(*p); p++);
len = strlen((char *) p);
if (p > str)
memmove(str, p, len + 1);
return len;
}
/* Removes left-hand, right-hand and repeating whitespaces.
*/
static inline size_t __normalize_whitespace(
const unsigned char *src,
size_t sz,
unsigned char *dst,
size_t len)
{
size_t i, x = 0;
int nsp = 0, intext = 0;
if (!sz)
goto done;
for (i = 0, x = 0; i < sz && x < len - 1; ) {
if (isspace(src[i]))
nsp++;
else
nsp = 0, intext = 1;
if (nsp > 1 || (nsp && !intext))
i++;
else
dst[x++] = src[i++];
}
if (nsp && x > 0) /* tailing space */
x--;
done:
dst[x] = '\0';
return x;
}
static inline size_t normalize_whitespace(unsigned char *str)
{
size_t sz = strlen((char *) str);
return __normalize_whitespace(str, sz, str, sz + 1);
}
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;
if (!s)
return;
for (p = s; *s; s++) {
if (*s != rem)
*p++ = *s;
}
*p = '\0';
}
extern char *strnconcat(const char *s, const char *suffix, size_t b);
extern char *strconcat(const char *s, const char *suffix);
extern char *strfconcat(const char *s, const char *format, ...)
__attribute__ ((__format__ (__printf__, 2, 3)));
extern int strappend(char **a, const char *b);
extern const char *split(const char **state, size_t *l, const char *separator, int quoted);
extern char *ul_strchr_escaped(const char *s, int c);
extern int skip_fline(FILE *fp);
extern int ul_stralnumcmp(const char *p1, const char *p2);
extern int ul_optstr_next(char **optstr, char **name, size_t *namesz, char **value, size_t *valsz);
#endif