]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/string_utils.c
2bb05139c1520e6544c57a495a0abd22a5f1bf55
[mirror_lxc.git] / src / lxc / string_utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "config.h"
4
5 #include <ctype.h>
6 #include <dirent.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <grp.h>
10 #include <inttypes.h>
11 #include <libgen.h>
12 #include <pthread.h>
13 #include <stdarg.h>
14 #include <stddef.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/mman.h>
19 #include <sys/mount.h>
20 #include <sys/param.h>
21 #include <sys/prctl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26
27 #include "string_utils.h"
28 #include "macro.h"
29 #include "memory_utils.h"
30
31 #if !HAVE_STRLCPY
32 #include "strlcpy.h"
33 #endif
34
35 #if !HAVE_STRLCAT
36 #include "strlcat.h"
37 #endif
38
39 char **lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup)
40 {
41 va_list ap2;
42 size_t count = 1 + skip;
43 char **result;
44
45 /* first determine size of argument list, we don't want to reallocate
46 * constantly...
47 */
48 va_copy(ap2, ap);
49 for (;;) {
50 char *arg = va_arg(ap2, char *);
51 if (!arg)
52 break;
53 count++;
54 }
55 va_end(ap2);
56
57 result = calloc(count, sizeof(char *));
58 if (!result)
59 return NULL;
60
61 count = skip;
62 for (;;) {
63 char *arg = va_arg(ap, char *);
64 if (!arg)
65 break;
66 arg = do_strdup ? strdup(arg) : arg;
67 if (!arg)
68 goto oom;
69 result[count++] = arg;
70 }
71
72 /* calloc has already set last element to NULL*/
73 return result;
74
75 oom:
76 free(result);
77 return NULL;
78 }
79
80 const char **lxc_va_arg_list_to_argv_const(va_list ap, size_t skip)
81 {
82 return (const char **)lxc_va_arg_list_to_argv(ap, skip, 0);
83 }
84
85 char *lxc_string_replace(const char *needle, const char *replacement,
86 const char *haystack)
87 {
88 ssize_t len = -1, saved_len = -1;
89 char *result = NULL;
90 size_t replacement_len = strlen(replacement);
91 size_t needle_len = strlen(needle);
92
93 /* should be executed exactly twice */
94 while (len == -1 || result == NULL) {
95 char *p;
96 char *last_p;
97 ssize_t part_len;
98
99 if (len != -1) {
100 result = calloc(1, len + 1);
101 if (!result)
102 return NULL;
103
104 saved_len = len;
105 }
106
107 len = 0;
108
109 for (last_p = (char *)haystack, p = strstr(last_p, needle); p;
110 last_p = p, p = strstr(last_p, needle)) {
111 part_len = (ssize_t)(p - last_p);
112 if (result && part_len > 0)
113 memcpy(&result[len], last_p, part_len);
114
115 len += part_len;
116
117 if (result && replacement_len > 0)
118 memcpy(&result[len], replacement,
119 replacement_len);
120
121 len += replacement_len;
122 p += needle_len;
123 }
124
125 part_len = strlen(last_p);
126 if (result && part_len > 0)
127 memcpy(&result[len], last_p, part_len);
128
129 len += part_len;
130 }
131
132 /* make sure we did the same thing twice,
133 * once for calculating length, the other
134 * time for copying data */
135 if (saved_len != len) {
136 free(result);
137 return NULL;
138 }
139
140 /* make sure we didn't overwrite any buffer,
141 * due to calloc the string should be 0-terminated */
142 if (result[len] != '\0') {
143 free(result);
144 return NULL;
145 }
146
147 return result;
148 }
149
150 bool lxc_string_in_array(const char *needle, const char **haystack)
151 {
152 for (; haystack && *haystack; haystack++)
153 if (strequal(needle, *haystack))
154 return true;
155
156 return false;
157 }
158
159 char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
160 {
161 char *result;
162 char **p;
163 size_t sep_len = strlen(sep);
164 size_t result_len = use_as_prefix * sep_len;
165 size_t buf_len;
166
167 /* calculate new string length */
168 for (p = (char **)parts; *p; p++)
169 result_len += (p > (char **)parts) * sep_len + strlen(*p);
170
171 buf_len = result_len + 1;
172 result = calloc(buf_len, 1);
173 if (!result)
174 return NULL;
175
176 if (use_as_prefix)
177 (void)strlcpy(result, sep, buf_len);
178
179 for (p = (char **)parts; *p; p++) {
180 if (p > (char **)parts)
181 (void)strlcat(result, sep, buf_len);
182
183 (void)strlcat(result, *p, buf_len);
184 }
185
186 return result;
187 }
188
189 /* taken from systemd */
190 char *lxc_path_simplify(const char *path)
191 {
192 __do_free char *path_new = NULL;
193 char *f, *t;
194 bool slash = false, ignore_slash = false, absolute;
195
196 path_new = strdup(path);
197 if (!path_new)
198 return NULL;
199
200 if (is_empty_string(path_new))
201 return move_ptr(path_new);
202
203 absolute = abspath(path_new);
204
205 f = path_new;
206 if (*f == '.' && (f[1] == 0 || f[1] == '/')) {
207 ignore_slash = true;
208 f++;
209 }
210
211 for (t = path_new; *f; f++) {
212
213 if (*f == '/') {
214 slash = true;
215 continue;
216 }
217
218 if (slash) {
219 if (*f == '.' && (f[1] == 0 || f[1] == '/'))
220 continue;
221
222 slash = false;
223 if (ignore_slash)
224 ignore_slash = false;
225 else
226 *(t++) = '/';
227 }
228
229 *(t++) = *f;
230 }
231
232 if (t == path_new) {
233 if (absolute)
234 *(t++) = '/';
235 else
236 *(t++) = '.';
237 }
238
239 *t = 0;
240 return move_ptr(path_new);
241 }
242
243 char *lxc_append_paths(const char *first, const char *second)
244 {
245 __do_free char *result = NULL;
246 int ret;
247 size_t len;
248 int pattern_type = 0;
249
250 len = strlen(first) + strlen(second) + 1;
251 if (second[0] != '/') {
252 len += 1;
253 pattern_type = 1;
254 }
255
256 result = zalloc(len);
257 if (!result)
258 return NULL;
259
260 if (pattern_type == 0)
261 ret = strnprintf(result, len, "%s%s", first, second);
262 else
263 ret = strnprintf(result, len, "%s/%s", first, second);
264 if (ret < 0)
265 return NULL;
266
267 return move_ptr(result);
268 }
269
270 bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
271 {
272 __do_free char *str = NULL;
273 char *token;
274 char sep[2] = { _sep, '\0' };
275
276 if (!haystack || !needle)
277 return 0;
278
279 str = must_copy_string(haystack);
280 lxc_iterate_parts(token, str, sep)
281 if (strequal(needle, token))
282 return 1;
283
284 return 0;
285 }
286
287 char **lxc_string_split(const char *string, char _sep)
288 {
289 __do_free char *str = NULL;
290 char *token;
291 char sep[2] = {_sep, '\0'};
292 char **tmp = NULL, **result = NULL;
293 size_t result_capacity = 0;
294 size_t result_count = 0;
295 int r, saved_errno;
296
297 if (!string)
298 return calloc(1, sizeof(char *));
299
300 str = must_copy_string(string);
301 lxc_iterate_parts(token, str, sep) {
302 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
303 if (r < 0)
304 goto error_out;
305
306 result[result_count] = strdup(token);
307 if (!result[result_count])
308 goto error_out;
309
310 result_count++;
311 }
312
313 /* if we allocated too much, reduce it */
314 tmp = realloc(result, (result_count + 1) * sizeof(char *));
315 if (!tmp)
316 goto error_out;
317
318 result = tmp;
319
320 /* Make sure we don't return uninitialized memory. */
321 if (result_count == 0)
322 *result = NULL;
323
324 return result;
325
326 error_out:
327 saved_errno = errno;
328 lxc_free_array((void **)result, free);
329 errno = saved_errno;
330 return NULL;
331 }
332
333 static bool complete_word(char ***result, char *start, char *end, size_t *cap,
334 size_t *cnt)
335 {
336 int r;
337
338 r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16);
339 if (r < 0)
340 return false;
341
342 (*result)[*cnt] = strndup(start, end - start);
343 if (!(*result)[*cnt])
344 return false;
345
346 (*cnt)++;
347
348 return true;
349 }
350
351 /*
352 * Given a a string 'one two "three four"', split into three words,
353 * one, two, and "three four"
354 */
355 char **lxc_string_split_quoted(char *string)
356 {
357 char *nextword = string, *p, state;
358 char **result = NULL;
359 size_t result_capacity = 0;
360 size_t result_count = 0;
361
362 if (!string || !*string)
363 return calloc(1, sizeof(char *));
364
365 // TODO I'm *not* handling escaped quote
366 state = ' ';
367 for (p = string; *p; p++) {
368 switch(state) {
369 case ' ':
370 if (isspace(*p))
371 continue;
372 else if (*p == '"' || *p == '\'') {
373 nextword = p;
374 state = *p;
375 continue;
376 }
377 nextword = p;
378 state = 'a';
379 continue;
380 case 'a':
381 if (isspace(*p)) {
382 complete_word(&result, nextword, p, &result_capacity, &result_count);
383 state = ' ';
384 continue;
385 }
386 continue;
387 case '"':
388 case '\'':
389 if (*p == state) {
390 complete_word(&result, nextword+1, p, &result_capacity, &result_count);
391 state = ' ';
392 continue;
393 }
394 continue;
395 }
396 }
397
398 if (state == 'a')
399 complete_word(&result, nextword, p, &result_capacity, &result_count);
400
401 if (result == NULL)
402 return calloc(1, sizeof(char *));
403
404 return realloc(result, (result_count + 1) * sizeof(char *));
405 }
406
407 char **lxc_string_split_and_trim(const char *string, char _sep)
408 {
409 __do_free char *str = NULL;
410 char *token;
411 char sep[2] = { _sep, '\0' };
412 char **result = NULL;
413 size_t result_capacity = 0;
414 size_t result_count = 0;
415 int r, saved_errno;
416 size_t i = 0;
417
418 if (!string)
419 return calloc(1, sizeof(char *));
420
421 str = must_copy_string(string);
422 lxc_iterate_parts(token, str, sep) {
423 while (token[0] == ' ' || token[0] == '\t')
424 token++;
425
426 i = strlen(token);
427 while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
428 token[i - 1] = '\0';
429 i--;
430 }
431
432 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
433 if (r < 0)
434 goto error_out;
435
436 result[result_count] = strdup(token);
437 if (!result[result_count])
438 goto error_out;
439
440 result_count++;
441 }
442
443 if (result == NULL)
444 return calloc(1, sizeof(char *));
445
446 /* if we allocated too much, reduce it */
447 return realloc(result, (result_count + 1) * sizeof(char *));
448
449 error_out:
450 saved_errno = errno;
451 lxc_free_array((void **)result, free);
452 errno = saved_errno;
453 return NULL;
454 }
455
456 void lxc_free_array(void **array, lxc_free_fn element_free_fn)
457 {
458 void **p;
459
460 for (p = array; p && *p; p++)
461 element_free_fn(*p);
462
463 free((void*)array);
464 }
465
466 int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment)
467 {
468 size_t new_capacity;
469 void **new_array;
470
471 /* first time around, catch some trivial mistakes of the user
472 * only initializing one of these */
473 if (!*array || !*capacity) {
474 *array = NULL;
475 *capacity = 0;
476 }
477
478 new_capacity = *capacity;
479 while (new_size + 1 > new_capacity)
480 new_capacity += capacity_increment;
481
482 if (new_capacity != *capacity) {
483 /* we have to reallocate */
484 new_array = realloc(*array, new_capacity * sizeof(void *));
485 if (!new_array)
486 return -1;
487
488 memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
489 *array = new_array;
490 *capacity = new_capacity;
491 }
492
493 /* array has sufficient elements */
494 return 0;
495 }
496
497 size_t lxc_array_len(void **array)
498 {
499 void **p;
500 size_t result = 0;
501
502 for (p = array; p && *p; p++)
503 result++;
504
505 return result;
506 }
507
508 void **lxc_append_null_to_array(void **array, size_t count)
509 {
510 void **temp;
511
512 /* Append NULL to the array */
513 if (count) {
514 temp = realloc(array, (count + 1) * sizeof(*array));
515 if (!temp) {
516 size_t i;
517 for (i = 0; i < count; i++)
518 free(array[i]);
519 free(array);
520 return NULL;
521 }
522
523 array = temp;
524 array[count] = NULL;
525 }
526
527 return array;
528 }
529
530 static int lxc_append_null_to_list(void ***list)
531 {
532 int newentry = 0;
533 void **tmp;
534
535 if (*list)
536 for (; (*list)[newentry]; newentry++) {
537 ;
538 }
539
540 tmp = realloc(*list, (newentry + 2) * sizeof(void **));
541 if (!tmp)
542 return -1;
543
544 *list = tmp;
545 (*list)[newentry + 1] = NULL;
546
547 return newentry;
548 }
549
550 int lxc_append_string(char ***list, char *entry)
551 {
552 char *copy;
553 int newentry;
554
555 newentry = lxc_append_null_to_list((void ***)list);
556 if (newentry < 0)
557 return -1;
558
559 copy = strdup(entry);
560 if (!copy)
561 return -1;
562
563 (*list)[newentry] = copy;
564
565 return 0;
566 }
567
568 int lxc_safe_uint(const char *numstr, unsigned int *converted)
569 {
570 char *err = NULL;
571 unsigned long int uli;
572
573 while (isspace(*numstr))
574 numstr++;
575
576 if (*numstr == '-')
577 return -EINVAL;
578
579 errno = 0;
580 uli = strtoul(numstr, &err, 0);
581 if (errno == ERANGE && uli == ULONG_MAX)
582 return -ERANGE;
583
584 if (err == numstr || *err != '\0')
585 return -EINVAL;
586
587 if (uli > UINT_MAX)
588 return -ERANGE;
589
590 *converted = (unsigned int)uli;
591 return 0;
592 }
593
594 int lxc_safe_ulong(const char *numstr, unsigned long *converted)
595 {
596 char *err = NULL;
597 unsigned long int uli;
598
599 while (isspace(*numstr))
600 numstr++;
601
602 if (*numstr == '-')
603 return -EINVAL;
604
605 errno = 0;
606 uli = strtoul(numstr, &err, 0);
607 if (errno == ERANGE && uli == ULONG_MAX)
608 return -ERANGE;
609
610 if (err == numstr || *err != '\0')
611 return -EINVAL;
612
613 *converted = uli;
614 return 0;
615 }
616
617 int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base)
618 {
619 char *err = NULL;
620 uint64_t u;
621
622 while (isspace(*numstr))
623 numstr++;
624
625 if (*numstr == '-')
626 return -EINVAL;
627
628 errno = 0;
629 u = strtoull(numstr, &err, base);
630 if (errno == ERANGE && u == UINT64_MAX)
631 return -ERANGE;
632
633 if (err == numstr || *err != '\0')
634 return -EINVAL;
635
636 *converted = u;
637 return 0;
638 }
639
640 int lxc_safe_int64_residual(const char *restrict numstr,
641 int64_t *restrict converted, int base,
642 char *restrict residual, size_t residual_len)
643 {
644 char *remaining = NULL;
645 int64_t u;
646
647 if (residual && residual_len == 0)
648 return ret_errno(EINVAL);
649
650 if (!residual && residual_len != 0)
651 return ret_errno(EINVAL);
652
653 memset(residual, 0, residual_len);
654
655 while (isspace(*numstr))
656 numstr++;
657
658 errno = 0;
659 u = strtoll(numstr, &remaining, base);
660 if (errno == ERANGE && u == INT64_MAX)
661 return ret_errno(ERANGE);
662
663 if (remaining == numstr)
664 return -EINVAL;
665
666 if (residual) {
667 size_t len = 0;
668
669 if (*remaining == '\0')
670 goto out;
671
672 len = strlen(remaining);
673 if (len >= residual_len)
674 return ret_errno(EINVAL);
675
676 memcpy(residual, remaining, len);
677 } else if (*remaining != '\0') {
678 return ret_errno(EINVAL);
679 }
680
681 out:
682 *converted = u;
683 return 0;
684 }
685
686 int lxc_safe_int(const char *numstr, int *converted)
687 {
688 char *err = NULL;
689 signed long int sli;
690
691 errno = 0;
692 sli = strtol(numstr, &err, 0);
693 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
694 return -ERANGE;
695
696 if (errno != 0 && sli == 0)
697 return -EINVAL;
698
699 if (err == numstr || *err != '\0')
700 return -EINVAL;
701
702 if (sli > INT_MAX || sli < INT_MIN)
703 return -ERANGE;
704
705 *converted = (int)sli;
706 return 0;
707 }
708
709 int lxc_safe_long(const char *numstr, long int *converted)
710 {
711 char *err = NULL;
712 signed long int sli;
713
714 errno = 0;
715 sli = strtol(numstr, &err, 0);
716 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
717 return -ERANGE;
718
719 if (errno != 0 && sli == 0)
720 return -EINVAL;
721
722 if (err == numstr || *err != '\0')
723 return -EINVAL;
724
725 *converted = sli;
726 return 0;
727 }
728
729 int lxc_safe_long_long(const char *numstr, long long int *converted)
730 {
731 char *err = NULL;
732 signed long long int sli;
733
734 errno = 0;
735 sli = strtoll(numstr, &err, 0);
736 if (errno == ERANGE && (sli == LLONG_MAX || sli == LLONG_MIN))
737 return -ERANGE;
738
739 if (errno != 0 && sli == 0)
740 return -EINVAL;
741
742 if (err == numstr || *err != '\0')
743 return -EINVAL;
744
745 *converted = sli;
746 return 0;
747 }
748
749 char *must_concat(size_t *len, const char *first, ...)
750 {
751 va_list args;
752 char *cur, *dest;
753 size_t cur_len, it_len;
754
755 dest = must_copy_string(first);
756 cur_len = it_len = strlen(first);
757
758 va_start(args, first);
759 while ((cur = va_arg(args, char *)) != NULL) {
760 it_len = strlen(cur);
761
762 dest = must_realloc(dest, cur_len + it_len + 1);
763
764 (void)memcpy(dest + cur_len, cur, it_len);
765 cur_len += it_len;
766 }
767 va_end(args);
768
769 dest[cur_len] = '\0';
770 if (len)
771 *len = cur_len;
772 return dest;
773 }
774
775 char *must_make_path(const char *first, ...)
776 {
777 va_list args;
778 char *cur, *dest;
779 size_t full_len = strlen(first);
780 size_t buf_len;
781 size_t cur_len;
782
783 dest = must_copy_string(first);
784 cur_len = full_len;
785
786 va_start(args, first);
787 while ((cur = va_arg(args, char *)) != NULL) {
788 buf_len = strlen(cur);
789 if (buf_len == 0)
790 continue;
791
792 full_len += buf_len;
793 if (cur[0] != '/')
794 full_len++;
795
796 dest = must_realloc(dest, full_len + 1);
797
798 if (cur[0] != '/') {
799 memcpy(dest + cur_len, "/", 1);
800 cur_len++;
801 }
802
803 memcpy(dest + cur_len, cur, buf_len);
804 cur_len += buf_len;
805 }
806 va_end(args);
807
808 dest[cur_len] = '\0';
809 return dest;
810 }
811
812 char *must_append_path(char *first, ...)
813 {
814 char *cur;
815 size_t full_len;
816 va_list args;
817 char *dest = first;
818 size_t buf_len;
819 size_t cur_len;
820
821 full_len = strlen(first);
822 cur_len = full_len;
823
824 va_start(args, first);
825 while ((cur = va_arg(args, char *)) != NULL) {
826 buf_len = strlen(cur);
827
828 full_len += buf_len;
829 if (cur[0] != '/')
830 full_len++;
831
832 dest = must_realloc(dest, full_len + 1);
833
834 if (cur[0] != '/') {
835 memcpy(dest + cur_len, "/", 1);
836 cur_len++;
837 }
838
839 memcpy(dest + cur_len, cur, buf_len);
840 cur_len += buf_len;
841 }
842 va_end(args);
843
844 dest[cur_len] = '\0';
845 return dest;
846 }
847
848 char *must_copy_string(const char *entry)
849 {
850 char *ret;
851
852 if (!entry)
853 return NULL;
854
855 do {
856 ret = strdup(entry);
857 } while (!ret);
858
859 return ret;
860 }
861
862 void *must_realloc(void *orig, size_t sz)
863 {
864 void *ret;
865
866 do {
867 ret = realloc(orig, sz);
868 } while (!ret);
869
870 return ret;
871 }
872
873 int parse_byte_size_string(const char *s, long long int *converted)
874 {
875 int ret, suffix_len;
876 long long int conv, mltpl;
877 char *end;
878 char dup[INTTYPE_TO_STRLEN(long long int)] = {0};
879 char suffix[3] = {0};
880 size_t len;
881
882 if (!s)
883 return ret_errno(EINVAL);
884
885 len = strlen(s);
886 if (len == 0 || len > sizeof(dup) - 1)
887 return ret_errno(EINVAL);
888
889 memcpy(dup, s, len);
890
891 end = dup + len;
892 if (isdigit(*(end - 1)))
893 suffix_len = 0;
894 else if (isalpha(*(end - 1)))
895 suffix_len = 1;
896 else
897 return ret_errno(EINVAL);
898
899 if (suffix_len > 0) {
900 if ((end - 1) == dup)
901 return ret_errno(EINVAL);
902
903 if ((end - 2) == dup) {
904 if (isalpha(*(end - 2)))
905 return ret_errno(EINVAL);
906 /* 1B */
907 } else {
908 if (isalpha(*(end - 2))) /* 12MB */
909 suffix_len++;
910
911 /* 12B */
912 }
913
914 memcpy(suffix, end - suffix_len, suffix_len);
915 *(suffix + suffix_len) = '\0';
916 *(end - suffix_len) = '\0';
917 }
918
919 dup[lxc_char_right_gc(dup, strlen(dup))] = '\0';
920
921 ret = lxc_safe_long_long(dup, &conv);
922 if (ret)
923 return ret;
924
925 if (suffix_len != 2) {
926 *converted = conv;
927 return 0;
928 }
929
930 if (strcasecmp(suffix, "KB") == 0)
931 mltpl = 1024;
932 else if (strcasecmp(suffix, "MB") == 0)
933 mltpl = 1024 * 1024;
934 else if (strcasecmp(suffix, "GB") == 0)
935 mltpl = 1024 * 1024 * 1024;
936 else
937 return ret_errno(EINVAL);
938
939 if (check_mul_overflow(conv, mltpl, converted))
940 return ret_errno(ERANGE);
941
942 return 0;
943 }
944
945 void remove_trailing_newlines(char *l)
946 {
947 char *p = l;
948
949 while (*p)
950 p++;
951
952 while (--p >= l && *p == '\n')
953 *p = '\0';
954 }
955
956 int lxc_char_left_gc(const char *buffer, size_t len)
957 {
958 size_t i;
959
960 for (i = 0; i < len; i++) {
961 if (buffer[i] == ' ' ||
962 buffer[i] == '\t')
963 continue;
964
965 return i;
966 }
967
968 return 0;
969 }
970
971 int lxc_char_right_gc(const char *buffer, size_t len)
972 {
973 int i;
974
975 for (i = len - 1; i >= 0; i--) {
976 if (buffer[i] == ' ' ||
977 buffer[i] == '\t' ||
978 buffer[i] == '\n' ||
979 buffer[i] == '\0')
980 continue;
981
982 return i + 1;
983 }
984
985 return 0;
986 }
987
988 char *lxc_trim_whitespace_in_place(char *buffer)
989 {
990 buffer += lxc_char_left_gc(buffer, strlen(buffer));
991 buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0';
992 return buffer;
993 }
994
995 int lxc_is_line_empty(const char *line)
996 {
997 size_t len = strlen(line);
998
999 for (size_t i = 0; i < len; i++)
1000 if (line[i] != ' ' && line[i] != '\t' &&
1001 line[i] != '\n' && line[i] != '\r' &&
1002 line[i] != '\f' && line[i] != '\0')
1003 return 0;
1004 return 1;
1005 }
1006
1007 void remove_trailing_slashes(char *p)
1008 {
1009 int l = strlen(p);
1010 while (--l >= 0 && (p[l] == '/' || p[l] == '\n'))
1011 p[l] = '\0';
1012 }