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