#ifndef UTIL_LINUX_STRUTILS #define UTIL_LINUX_STRUTILS #include #include #include #include #include #include #include #include #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); extern long strtol_or_err(const char *str, const char *errmesg); extern unsigned long strtoul_or_err(const char *str, const char *errmesg); 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); #endif