]>
Commit | Line | Data |
---|---|---|
faeb1b64 KZ |
1 | /* |
2 | * No copyright is claimed. This code is in the public domain; do with | |
3 | * it what you wish. | |
4 | */ | |
8abcf290 DB |
5 | #ifndef UTIL_LINUX_STRUTILS |
6 | #define UTIL_LINUX_STRUTILS | |
7 | ||
23106a29 | 8 | #include <stdlib.h> |
8abcf290 DB |
9 | #include <inttypes.h> |
10 | #include <string.h> | |
ce877f2d | 11 | #include <sys/types.h> |
675de3f5 | 12 | #include <ctype.h> |
3e5a5455 | 13 | #include <stdio.h> |
deb1c903 | 14 | #include <errno.h> |
35c84bf7 | 15 | #include <time.h> |
8abcf290 | 16 | |
ad265938 KZ |
17 | #include "c.h" |
18 | ||
9c8b9fba RM |
19 | /* initialize a custom exit code for all *_or_err functions */ |
20 | extern void strutils_set_exitcode(int exit_code); | |
a99c9130 | 21 | |
23106a29 | 22 | extern int parse_size(const char *str, uintmax_t *res, int *power); |
8abcf290 | 23 | extern int strtosize(const char *str, uintmax_t *res); |
551dae40 KZ |
24 | extern uintmax_t strtosize_or_err(const char *str, const char *errmesg); |
25 | ||
84825b16 KZ |
26 | extern int ul_strtos64(const char *str, int64_t *num, int base); |
27 | extern int ul_strtou64(const char *str, uint64_t *num, int base); | |
9fc0f69c KZ |
28 | extern int ul_strtos32(const char *str, int32_t *num, int base); |
29 | extern int ul_strtou32(const char *str, uint32_t *num, int base); | |
a57de0ef | 30 | extern int ul_strtold(const char *str, long double *num); |
84825b16 | 31 | |
9fc0f69c KZ |
32 | extern int64_t str2num_or_err(const char *str, int base, const char *errmesg, int64_t low, int64_t up); |
33 | extern uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up); | |
551dae40 | 34 | |
9fc0f69c KZ |
35 | #define strtos64_or_err(_s, _e) str2num_or_err(_s, 10, _e, 0, 0) |
36 | #define strtou64_or_err(_s, _e) str2unum_or_err(_s, 10, _e, 0) | |
37 | #define strtox64_or_err(_s, _e) str2unum_or_err(_s, 16, _e, 0) | |
551dae40 | 38 | |
9fc0f69c KZ |
39 | #define strtos32_or_err(_s, _e) (int32_t) str2num_or_err(_s, 10, _e, INT32_MIN, INT32_MAX) |
40 | #define strtou32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 10, _e, UINT32_MAX) | |
41 | #define strtox32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 16, _e, UINT32_MAX) | |
42 | ||
43 | #define strtos16_or_err(_s, _e) (int16_t) str2num_or_err(_s, 10, _e, INT16_MIN, INT16_MAX) | |
44 | #define strtou16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 10, _e, UINT16_MAX) | |
45 | #define strtox16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 16, _e, UINT16_MAX) | |
551dae40 | 46 | |
a9f97001 | 47 | extern double strtod_or_err(const char *str, const char *errmesg); |
35c84bf7 | 48 | extern long double strtold_or_err(const char *str, const char *errmesg); |
551dae40 | 49 | |
3b888573 | 50 | #define strtol_or_err(_s, _e) (long) str2num_or_err(_s, 10, _e, LONG_MIN, LONG_MAX) |
550d32c4 | 51 | #define strtopid_or_err(_s, _e) (pid_t) str2num_or_err(_s, 10, _e, 1, SINT_MAX(pid_t)) |
3b888573 | 52 | #define strtoul_or_err(_s, _e) (unsigned long) str2unum_or_err(_s, 10, _e, ULONG_MAX) |
8abcf290 | 53 | |
477254da KZ |
54 | extern void strtotimeval_or_err(const char *str, struct timeval *tv, |
55 | const char *errmesg); | |
937cd872 TW |
56 | extern void strtotimespec_or_err(const char *str, struct timespec *ts, |
57 | const char *errmesg); | |
35c84bf7 | 58 | extern time_t strtotime_or_err(const char *str, const char *errmesg); |
477254da | 59 | |
61cbc8a3 KZ |
60 | extern int isdigit_strend(const char *str, const char **end); |
61 | #define isdigit_string(_s) isdigit_strend(_s, NULL) | |
62 | ||
63 | extern int isxdigit_strend(const char *str, const char **end); | |
64 | #define isxdigit_string(_s) isxdigit_strend(_s, NULL) | |
65 | ||
416c43a9 | 66 | |
30b294c4 | 67 | extern int parse_switch(const char *arg, const char *errmesg, ...); |
e5cf1476 | 68 | |
02887b73 DT |
69 | #ifndef HAVE_MEMPCPY |
70 | extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n); | |
71 | #endif | |
8abcf290 DB |
72 | #ifndef HAVE_STRNLEN |
73 | extern size_t strnlen(const char *s, size_t maxlen); | |
74 | #endif | |
75 | #ifndef HAVE_STRNDUP | |
76 | extern char *strndup(const char *s, size_t n); | |
77 | #endif | |
78 | #ifndef HAVE_STRNCHR | |
79 | extern char *strnchr(const char *s, size_t maxlen, int c); | |
80 | #endif | |
81 | ||
82 | /* caller guarantees n > 0 */ | |
83 | static inline void xstrncpy(char *dest, const char *src, size_t n) | |
84 | { | |
ad265938 KZ |
85 | size_t len = src ? strlen(src) : 0; |
86 | ||
87 | if (!len) | |
88 | return; | |
89 | len = min(len, n - 1); | |
90 | memcpy(dest, src, len); | |
91 | dest[len] = 0; | |
8abcf290 | 92 | } |
ce877f2d | 93 | |
a338eb4a KZ |
94 | /* This is like strncpy(), but based on memcpy(), so compilers and static |
95 | * analyzers do not complain when sizeof(destination) is the same as 'n' and | |
96 | * result is not terminated by zero. | |
97 | * | |
98 | * Use this function to copy string to logs with fixed sizes (wtmp/utmp. ...) | |
99 | * where string terminator is optional. | |
100 | */ | |
d57672e2 KZ |
101 | static inline void * __attribute__((nonnull (1))) |
102 | str2memcpy(void *dest, const char *src, size_t n) | |
a338eb4a KZ |
103 | { |
104 | size_t bytes = strlen(src) + 1; | |
105 | ||
106 | if (bytes > n) | |
107 | bytes = n; | |
108 | ||
109 | memcpy(dest, src, bytes); | |
110 | return dest; | |
111 | } | |
112 | ||
d57672e2 KZ |
113 | static inline char * __attribute__((nonnull (1))) |
114 | mem2strcpy(char *dest, const void *src, size_t n, size_t nmax) | |
a338eb4a KZ |
115 | { |
116 | if (n + 1 > nmax) | |
117 | n = nmax - 1; | |
118 | ||
9c05f4b6 | 119 | memset(dest, '\0', nmax); |
a338eb4a | 120 | memcpy(dest, src, n); |
a338eb4a KZ |
121 | return dest; |
122 | } | |
123 | ||
01aedbec KZ |
124 | /* Reallocate @str according to @newstr and copy @newstr to @str; returns new @str. |
125 | * The @str is not modified if reallocation failed (like classic realloc()). | |
126 | */ | |
23afadca KZ |
127 | static inline char * __attribute__((warn_unused_result)) |
128 | strrealloc(char *str, const char *newstr) | |
129 | { | |
130 | size_t nsz, osz; | |
131 | ||
132 | if (!str) | |
133 | return newstr ? strdup(newstr) : NULL; | |
01aedbec KZ |
134 | if (!newstr) |
135 | return NULL; | |
23afadca KZ |
136 | |
137 | osz = strlen(str); | |
138 | nsz = strlen(newstr); | |
139 | ||
01aedbec KZ |
140 | if (nsz > osz) |
141 | str = realloc(str, nsz + 1); | |
142 | if (str) | |
143 | memcpy(str, newstr, nsz + 1); | |
23afadca | 144 | return str; |
23afadca KZ |
145 | } |
146 | ||
787226dc | 147 | /* Copy string @str to struct @stru to member addressed by @offset */ |
deb1c903 | 148 | static inline int strdup_to_offset(void *stru, size_t offset, const char *str) |
23106a29 | 149 | { |
deb1c903 | 150 | char **o; |
787226dc | 151 | char *p = NULL; |
23106a29 | 152 | |
deb1c903 KZ |
153 | if (!stru) |
154 | return -EINVAL; | |
155 | ||
156 | o = (char **) ((char *) stru + offset); | |
23106a29 | 157 | if (str) { |
787226dc KZ |
158 | p = strdup(str); |
159 | if (!p) | |
deb1c903 | 160 | return -ENOMEM; |
23106a29 KZ |
161 | } |
162 | ||
163 | free(*o); | |
787226dc | 164 | *o = p; |
deb1c903 | 165 | return 0; |
23106a29 KZ |
166 | } |
167 | ||
787226dc | 168 | /* Copy string __str to struct member _m of the struct _s */ |
23106a29 KZ |
169 | #define strdup_to_struct_member(_s, _m, _str) \ |
170 | strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str) | |
171 | ||
787226dc KZ |
172 | /* Copy string addressed by @offset between two structs */ |
173 | static inline int strdup_between_offsets(void *stru_dst, void *stru_src, size_t offset) | |
174 | { | |
175 | char **src; | |
176 | char **dst; | |
177 | char *p = NULL; | |
178 | ||
179 | if (!stru_src || !stru_dst) | |
180 | return -EINVAL; | |
181 | ||
182 | src = (char **) ((char *) stru_src + offset); | |
183 | dst = (char **) ((char *) stru_dst + offset); | |
184 | ||
185 | if (*src) { | |
186 | p = strdup(*src); | |
187 | if (!p) | |
188 | return -ENOMEM; | |
189 | } | |
190 | ||
191 | free(*dst); | |
192 | *dst = p; | |
193 | return 0; | |
194 | } | |
195 | ||
196 | /* Copy string addressed by struct member between two instances of the same | |
197 | * struct type */ | |
198 | #define strdup_between_structs(_dst, _src, _m) \ | |
199 | strdup_between_offsets((void *)_dst, (void *)_src, offsetof(__typeof__(*(_src)), _m)) | |
200 | ||
201 | ||
5b82289b | 202 | extern char *xstrmode(mode_t mode, char *str); |
5d2a9849 FC |
203 | |
204 | /* Options for size_to_human_string() */ | |
205 | enum | |
206 | { | |
07b94c9f KZ |
207 | SIZE_SUFFIX_1LETTER = 0, |
208 | SIZE_SUFFIX_3LETTER = (1 << 0), | |
209 | SIZE_SUFFIX_SPACE = (1 << 1), | |
210 | SIZE_DECIMAL_2DIGITS = (1 << 2) | |
5d2a9849 FC |
211 | }; |
212 | ||
213 | extern char *size_to_human_string(int options, uint64_t bytes); | |
ce877f2d | 214 | |
c87638ad KZ |
215 | extern int string_to_idarray(const char *list, int ary[], size_t arysz, |
216 | int (name2id)(const char *, size_t)); | |
f5077b51 | 217 | extern int string_add_to_idarray(const char *list, int ary[], |
40b17508 | 218 | size_t arysz, size_t *ary_pos, |
f5077b51 MB |
219 | int (name2id)(const char *, size_t)); |
220 | ||
c87638ad | 221 | extern int string_to_bitarray(const char *list, char *ary, |
73a96af6 TW |
222 | int (*name2bit)(const char *, size_t), |
223 | size_t allow_range); | |
c87638ad | 224 | |
5ef16771 KZ |
225 | extern int string_to_bitmask(const char *list, |
226 | unsigned long *mask, | |
227 | long (*name2flag)(const char *, size_t)); | |
af7df9ee | 228 | extern int parse_range(const char *str, int *lower, int *upper, int def); |
a883c634 | 229 | |
d4e89dea | 230 | extern int streq_paths(const char *a, const char *b); |
b106d052 | 231 | |
646e159a KZ |
232 | /* |
233 | * Match string beginning. | |
234 | */ | |
235 | static inline const char *startswith(const char *s, const char *prefix) | |
236 | { | |
237 | size_t sz = prefix ? strlen(prefix) : 0; | |
238 | ||
239 | if (s && sz && strncmp(s, prefix, sz) == 0) | |
240 | return s + sz; | |
241 | return NULL; | |
242 | } | |
243 | ||
244 | /* | |
245 | * Case insensitive match string beginning. | |
246 | */ | |
247 | static inline const char *startswith_no_case(const char *s, const char *prefix) | |
248 | { | |
249 | size_t sz = prefix ? strlen(prefix) : 0; | |
250 | ||
251 | if (s && sz && strncasecmp(s, prefix, sz) == 0) | |
252 | return s + sz; | |
253 | return NULL; | |
254 | } | |
255 | ||
256 | /* | |
257 | * Match string ending. | |
258 | */ | |
259 | static inline const char *endswith(const char *s, const char *postfix) | |
260 | { | |
261 | size_t sl = s ? strlen(s) : 0; | |
262 | size_t pl = postfix ? strlen(postfix) : 0; | |
263 | ||
264 | if (pl == 0) | |
8a7aeeda | 265 | return s + sl; |
646e159a KZ |
266 | if (sl < pl) |
267 | return NULL; | |
268 | if (memcmp(s + sl - pl, postfix, pl) != 0) | |
269 | return NULL; | |
8a7aeeda | 270 | return s + sl - pl; |
646e159a | 271 | } |
199e939d | 272 | |
675de3f5 OO |
273 | /* |
274 | * Skip leading white space. | |
275 | */ | |
276 | static inline const char *skip_space(const char *p) | |
277 | { | |
278 | while (isspace(*p)) | |
279 | ++p; | |
280 | return p; | |
281 | } | |
282 | ||
283 | static inline const char *skip_blank(const char *p) | |
284 | { | |
285 | while (isblank(*p)) | |
286 | ++p; | |
287 | return p; | |
288 | } | |
289 | ||
22e9e9c8 KZ |
290 | |
291 | /* Removes whitespace from the right-hand side of a string (trailing | |
292 | * whitespace). | |
293 | * | |
294 | * Returns size of the new string (without \0). | |
295 | */ | |
296 | static inline size_t rtrim_whitespace(unsigned char *str) | |
297 | { | |
0b404f08 | 298 | size_t i; |
22e9e9c8 | 299 | |
0b404f08 SK |
300 | if (!str) |
301 | return 0; | |
302 | i = strlen((char *) str); | |
2f8610ee SK |
303 | while (i) { |
304 | i--; | |
305 | if (!isspace(str[i])) { | |
306 | i++; | |
22e9e9c8 | 307 | break; |
2f8610ee | 308 | } |
22e9e9c8 | 309 | } |
2f8610ee | 310 | str[i] = '\0'; |
22e9e9c8 KZ |
311 | return i; |
312 | } | |
313 | ||
314 | /* Removes whitespace from the left-hand side of a string. | |
315 | * | |
316 | * Returns size of the new string (without \0). | |
317 | */ | |
318 | static inline size_t ltrim_whitespace(unsigned char *str) | |
319 | { | |
320 | size_t len; | |
321 | unsigned char *p; | |
322 | ||
0b404f08 SK |
323 | if (!str) |
324 | return 0; | |
325 | for (p = str; *p && isspace(*p); p++); | |
22e9e9c8 KZ |
326 | |
327 | len = strlen((char *) p); | |
328 | ||
6c183c28 | 329 | if (p > str) |
22e9e9c8 KZ |
330 | memmove(str, p, len + 1); |
331 | ||
332 | return len; | |
333 | } | |
334 | ||
5d68f974 KZ |
335 | /* Removes left-hand, right-hand and repeating whitespaces. |
336 | */ | |
c862d0e1 KZ |
337 | static inline size_t __normalize_whitespace( |
338 | const unsigned char *src, | |
339 | size_t sz, | |
340 | unsigned char *dst, | |
341 | size_t len) | |
5d68f974 | 342 | { |
c862d0e1 | 343 | size_t i, x = 0; |
5d68f974 KZ |
344 | int nsp = 0, intext = 0; |
345 | ||
346 | if (!sz) | |
c862d0e1 | 347 | goto done; |
5d68f974 | 348 | |
c862d0e1 KZ |
349 | for (i = 0, x = 0; i < sz && x < len - 1; ) { |
350 | if (isspace(src[i])) | |
5d68f974 KZ |
351 | nsp++; |
352 | else | |
353 | nsp = 0, intext = 1; | |
354 | ||
355 | if (nsp > 1 || (nsp && !intext)) | |
356 | i++; | |
357 | else | |
c862d0e1 | 358 | dst[x++] = src[i++]; |
5d68f974 | 359 | } |
c862d0e1 | 360 | if (nsp && x > 0) /* tailing space */ |
5d68f974 | 361 | x--; |
c862d0e1 KZ |
362 | done: |
363 | dst[x] = '\0'; | |
5d68f974 KZ |
364 | return x; |
365 | } | |
366 | ||
c862d0e1 KZ |
367 | static inline size_t normalize_whitespace(unsigned char *str) |
368 | { | |
369 | size_t sz = strlen((char *) str); | |
370 | return __normalize_whitespace(str, sz, str, sz + 1); | |
371 | } | |
372 | ||
3c4ff2dc KZ |
373 | static inline void strrep(char *s, int find, int replace) |
374 | { | |
375 | while (s && *s && (s = strchr(s, find)) != NULL) | |
376 | *s++ = replace; | |
377 | } | |
378 | ||
379 | static inline void strrem(char *s, int rem) | |
380 | { | |
381 | char *p; | |
382 | ||
d2a1ee4e SK |
383 | if (!s) |
384 | return; | |
385 | for (p = s; *s; s++) { | |
3c4ff2dc KZ |
386 | if (*s != rem) |
387 | *p++ = *s; | |
388 | } | |
b94acada | 389 | *p = '\0'; |
3c4ff2dc KZ |
390 | } |
391 | ||
357e6a0e KZ |
392 | /* returns next string after \0 if before @end */ |
393 | static inline char *ul_next_string(char *p, char *end) | |
394 | { | |
395 | char *last; | |
396 | ||
397 | if (!p || !end || p >= end) | |
398 | return NULL; | |
399 | ||
400 | for (last = p; p < end; p++) { | |
401 | if (*last == '\0' && p != last) | |
402 | return p; | |
403 | last = p; | |
404 | } | |
405 | ||
406 | return NULL; | |
407 | } | |
408 | ||
8420463b KZ |
409 | extern char *strnconcat(const char *s, const char *suffix, size_t b); |
410 | extern char *strconcat(const char *s, const char *suffix); | |
411 | extern char *strfconcat(const char *s, const char *format, ...) | |
40b55f5a | 412 | __attribute__ ((__format__ (__printf__, 2, 3))); |
2e03758d KZ |
413 | |
414 | extern int strappend(char **a, const char *b); | |
25a5cff8 MY |
415 | extern int strfappend(char **a, const char *format, ...) |
416 | __attribute__ ((__format__ (__printf__, 2, 3))); | |
417 | extern int strvfappend(char **a, const char *format, va_list ap) | |
418 | __attribute__ ((__format__ (__printf__, 2, 0))); | |
2e03758d | 419 | |
548b9714 KZ |
420 | extern const char *split(const char **state, size_t *l, const char *separator, int quoted); |
421 | ||
63bb0450 KZ |
422 | extern char *ul_strchr_escaped(const char *s, int c); |
423 | ||
3e5a5455 | 424 | extern int skip_fline(FILE *fp); |
27e6b3a9 | 425 | extern int ul_stralnumcmp(const char *p1, const char *p2); |
3e5a5455 | 426 | |
6c513f3c KZ |
427 | extern int ul_optstr_next(char **optstr, char **name, size_t *namesz, char **value, size_t *valsz); |
428 | ||
8abcf290 | 429 | #endif |