]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/string_utils.c
storage/dir: cleanup mount code
[mirror_lxc.git] / src / lxc / string_utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
7 #include <ctype.h>
8 #include <dirent.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <grp.h>
12 #include <inttypes.h>
13 #include <libgen.h>
14 #include <pthread.h>
15 #include <stdarg.h>
16 #include <stddef.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/mman.h>
21 #include <sys/mount.h>
22 #include <sys/param.h>
23 #include <sys/prctl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28
29 #include "config.h"
30 #include "lxclock.h"
31 #include "macro.h"
32 #include "memory_utils.h"
33 #include "namespace.h"
34 #include "parse.h"
35 #include "string_utils.h"
36
37 #ifndef HAVE_STRLCPY
38 #include "include/strlcpy.h"
39 #endif
40
41 #ifndef HAVE_STRLCAT
42 #include "include/strlcat.h"
43 #endif
44
45 char **lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup)
46 {
47 va_list ap2;
48 size_t count = 1 + skip;
49 char **result;
50
51 /* first determine size of argument list, we don't want to reallocate
52 * constantly...
53 */
54 va_copy(ap2, ap);
55 for (;;) {
56 char *arg = va_arg(ap2, char *);
57 if (!arg)
58 break;
59 count++;
60 }
61 va_end(ap2);
62
63 result = calloc(count, sizeof(char *));
64 if (!result)
65 return NULL;
66
67 count = skip;
68 for (;;) {
69 char *arg = va_arg(ap, char *);
70 if (!arg)
71 break;
72 arg = do_strdup ? strdup(arg) : arg;
73 if (!arg)
74 goto oom;
75 result[count++] = arg;
76 }
77
78 /* calloc has already set last element to NULL*/
79 return result;
80
81 oom:
82 free(result);
83 return NULL;
84 }
85
86 const char **lxc_va_arg_list_to_argv_const(va_list ap, size_t skip)
87 {
88 return (const char **)lxc_va_arg_list_to_argv(ap, skip, 0);
89 }
90
91 char *lxc_string_replace(const char *needle, const char *replacement,
92 const char *haystack)
93 {
94 ssize_t len = -1, saved_len = -1;
95 char *result = NULL;
96 size_t replacement_len = strlen(replacement);
97 size_t needle_len = strlen(needle);
98
99 /* should be executed exactly twice */
100 while (len == -1 || result == NULL) {
101 char *p;
102 char *last_p;
103 ssize_t part_len;
104
105 if (len != -1) {
106 result = calloc(1, len + 1);
107 if (!result)
108 return NULL;
109
110 saved_len = len;
111 }
112
113 len = 0;
114
115 for (last_p = (char *)haystack, p = strstr(last_p, needle); p;
116 last_p = p, p = strstr(last_p, needle)) {
117 part_len = (ssize_t)(p - last_p);
118 if (result && part_len > 0)
119 memcpy(&result[len], last_p, part_len);
120
121 len += part_len;
122
123 if (result && replacement_len > 0)
124 memcpy(&result[len], replacement,
125 replacement_len);
126
127 len += replacement_len;
128 p += needle_len;
129 }
130
131 part_len = strlen(last_p);
132 if (result && part_len > 0)
133 memcpy(&result[len], last_p, part_len);
134
135 len += part_len;
136 }
137
138 /* make sure we did the same thing twice,
139 * once for calculating length, the other
140 * time for copying data */
141 if (saved_len != len) {
142 free(result);
143 return NULL;
144 }
145
146 /* make sure we didn't overwrite any buffer,
147 * due to calloc the string should be 0-terminated */
148 if (result[len] != '\0') {
149 free(result);
150 return NULL;
151 }
152
153 return result;
154 }
155
156 bool lxc_string_in_array(const char *needle, const char **haystack)
157 {
158 for (; haystack && *haystack; haystack++)
159 if (strequal(needle, *haystack))
160 return true;
161
162 return false;
163 }
164
165 char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
166 {
167 char *result;
168 char **p;
169 size_t sep_len = strlen(sep);
170 size_t result_len = use_as_prefix * sep_len;
171 size_t buf_len;
172
173 /* calculate new string length */
174 for (p = (char **)parts; *p; p++)
175 result_len += (p > (char **)parts) * sep_len + strlen(*p);
176
177 buf_len = result_len + 1;
178 result = calloc(buf_len, 1);
179 if (!result)
180 return NULL;
181
182 if (use_as_prefix)
183 (void)strlcpy(result, sep, buf_len);
184
185 for (p = (char **)parts; *p; p++) {
186 if (p > (char **)parts)
187 (void)strlcat(result, sep, buf_len);
188
189 (void)strlcat(result, *p, buf_len);
190 }
191
192 return result;
193 }
194
195 /* taken from systemd */
196 char *path_simplify(const char *path)
197 {
198 __do_free char *path_new = NULL;
199 char *f, *t;
200 bool slash = false, ignore_slash = false, absolute;
201
202 path_new = strdup(path);
203 if (!path_new)
204 return NULL;
205
206 if (is_empty_string(path_new))
207 return move_ptr(path_new);
208
209 absolute = abspath(path_new);
210
211 f = path_new;
212 if (*f == '.' && IN_SET(f[1], 0, '/')) {
213 ignore_slash = true;
214 f++;
215 }
216
217 for (t = path_new; *f; f++) {
218
219 if (*f == '/') {
220 slash = true;
221 continue;
222 }
223
224 if (slash) {
225 if (*f == '.' && IN_SET(f[1], 0, '/'))
226 continue;
227
228 slash = false;
229 if (ignore_slash)
230 ignore_slash = false;
231 else
232 *(t++) = '/';
233 }
234
235 *(t++) = *f;
236 }
237
238 if (t == path_new) {
239 if (absolute)
240 *(t++) = '/';
241 else
242 *(t++) = '.';
243 }
244
245 *t = 0;
246 return move_ptr(path_new);
247 }
248
249 char *lxc_append_paths(const char *first, const char *second)
250 {
251 __do_free char *result = NULL;
252 int ret;
253 size_t len;
254 int pattern_type = 0;
255
256 len = strlen(first) + strlen(second) + 1;
257 if (second[0] != '/') {
258 len += 1;
259 pattern_type = 1;
260 }
261
262 result = zalloc(len);
263 if (!result)
264 return NULL;
265
266 if (pattern_type == 0)
267 ret = strnprintf(result, len, "%s%s", first, second);
268 else
269 ret = strnprintf(result, len, "%s/%s", first, second);
270 if (ret < 0)
271 return NULL;
272
273 return move_ptr(result);
274 }
275
276 bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
277 {
278 __do_free char *str = NULL;
279 char *token;
280 char sep[2] = { _sep, '\0' };
281
282 if (!haystack || !needle)
283 return 0;
284
285 str = must_copy_string(haystack);
286 lxc_iterate_parts(token, str, sep)
287 if (strequal(needle, token))
288 return 1;
289
290 return 0;
291 }
292
293 char **lxc_string_split(const char *string, char _sep)
294 {
295 __do_free char *str = NULL;
296 char *token;
297 char sep[2] = {_sep, '\0'};
298 char **tmp = NULL, **result = NULL;
299 size_t result_capacity = 0;
300 size_t result_count = 0;
301 int r, saved_errno;
302
303 if (!string)
304 return calloc(1, sizeof(char *));
305
306 str = must_copy_string(string);
307 lxc_iterate_parts(token, str, sep) {
308 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
309 if (r < 0)
310 goto error_out;
311
312 result[result_count] = strdup(token);
313 if (!result[result_count])
314 goto error_out;
315
316 result_count++;
317 }
318
319 /* if we allocated too much, reduce it */
320 tmp = realloc(result, (result_count + 1) * sizeof(char *));
321 if (!tmp)
322 goto error_out;
323
324 result = tmp;
325
326 /* Make sure we don't return uninitialized memory. */
327 if (result_count == 0)
328 *result = NULL;
329
330 return result;
331
332 error_out:
333 saved_errno = errno;
334 lxc_free_array((void **)result, free);
335 errno = saved_errno;
336 return NULL;
337 }
338
339 static bool complete_word(char ***result, char *start, char *end, size_t *cap,
340 size_t *cnt)
341 {
342 int r;
343
344 r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16);
345 if (r < 0)
346 return false;
347
348 (*result)[*cnt] = strndup(start, end - start);
349 if (!(*result)[*cnt])
350 return false;
351
352 (*cnt)++;
353
354 return true;
355 }
356
357 /*
358 * Given a a string 'one two "three four"', split into three words,
359 * one, two, and "three four"
360 */
361 char **lxc_string_split_quoted(char *string)
362 {
363 char *nextword = string, *p, state;
364 char **result = NULL;
365 size_t result_capacity = 0;
366 size_t result_count = 0;
367
368 if (!string || !*string)
369 return calloc(1, sizeof(char *));
370
371 // TODO I'm *not* handling escaped quote
372 state = ' ';
373 for (p = string; *p; p++) {
374 switch(state) {
375 case ' ':
376 if (isspace(*p))
377 continue;
378 else if (*p == '"' || *p == '\'') {
379 nextword = p;
380 state = *p;
381 continue;
382 }
383 nextword = p;
384 state = 'a';
385 continue;
386 case 'a':
387 if (isspace(*p)) {
388 complete_word(&result, nextword, p, &result_capacity, &result_count);
389 state = ' ';
390 continue;
391 }
392 continue;
393 case '"':
394 case '\'':
395 if (*p == state) {
396 complete_word(&result, nextword+1, p, &result_capacity, &result_count);
397 state = ' ';
398 continue;
399 }
400 continue;
401 }
402 }
403
404 if (state == 'a')
405 complete_word(&result, nextword, p, &result_capacity, &result_count);
406
407 return realloc(result, (result_count + 1) * sizeof(char *));
408 }
409
410 char **lxc_string_split_and_trim(const char *string, char _sep)
411 {
412 __do_free char *str = NULL;
413 char *token;
414 char sep[2] = { _sep, '\0' };
415 char **result = NULL;
416 size_t result_capacity = 0;
417 size_t result_count = 0;
418 int r, saved_errno;
419 size_t i = 0;
420
421 if (!string)
422 return calloc(1, sizeof(char *));
423
424 str = must_copy_string(string);
425 lxc_iterate_parts(token, str, sep) {
426 while (token[0] == ' ' || token[0] == '\t')
427 token++;
428
429 i = strlen(token);
430 while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
431 token[i - 1] = '\0';
432 i--;
433 }
434
435 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
436 if (r < 0)
437 goto error_out;
438
439 result[result_count] = strdup(token);
440 if (!result[result_count])
441 goto error_out;
442
443 result_count++;
444 }
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
881 if (is_empty_string(s))
882 return ret_errno(EINVAL);
883
884 end = stpncpy(dup, s, sizeof(dup) - 1);
885 if (*end != '\0')
886 return ret_errno(EINVAL);
887
888 if (isdigit(*(end - 1)))
889 suffix_len = 0;
890 else if (isalpha(*(end - 1)))
891 suffix_len = 1;
892 else
893 return ret_errno(EINVAL);
894
895 if (suffix_len > 0) {
896 if ((end - 1) == dup)
897 return ret_errno(EINVAL);
898
899 if ((end - 2) == dup) {
900 if (isalpha(*(end - 2)))
901 return ret_errno(EINVAL);
902 /* 1B */
903 } else {
904 if (isalpha(*(end - 2))) /* 12MB */
905 suffix_len++;
906
907 /* 12B */
908 }
909
910 memcpy(suffix, end - suffix_len, suffix_len);
911 *(suffix + suffix_len) = '\0';
912 *(end - suffix_len) = '\0';
913 }
914
915 dup[lxc_char_right_gc(dup, strlen(dup))] = '\0';
916
917 ret = lxc_safe_long_long(dup, &conv);
918 if (ret)
919 return ret;
920
921 if (suffix_len != 2) {
922 *converted = conv;
923 return 0;
924 }
925
926 if (strcasecmp(suffix, "KB") == 0)
927 mltpl = 1024;
928 else if (strcasecmp(suffix, "MB") == 0)
929 mltpl = 1024 * 1024;
930 else if (strcasecmp(suffix, "GB") == 0)
931 mltpl = 1024 * 1024 * 1024;
932 else
933 return ret_errno(EINVAL);
934
935 if (check_mul_overflow(conv, mltpl, converted))
936 return ret_errno(ERANGE);
937
938 return 0;
939 }
940
941 void remove_trailing_newlines(char *l)
942 {
943 char *p = l;
944
945 while (*p)
946 p++;
947
948 while (--p >= l && *p == '\n')
949 *p = '\0';
950 }
951
952 int lxc_char_left_gc(const char *buffer, size_t len)
953 {
954 size_t i;
955
956 for (i = 0; i < len; i++) {
957 if (buffer[i] == ' ' ||
958 buffer[i] == '\t')
959 continue;
960
961 return i;
962 }
963
964 return 0;
965 }
966
967 int lxc_char_right_gc(const char *buffer, size_t len)
968 {
969 int i;
970
971 for (i = len - 1; i >= 0; i--) {
972 if (buffer[i] == ' ' ||
973 buffer[i] == '\t' ||
974 buffer[i] == '\n' ||
975 buffer[i] == '\0')
976 continue;
977
978 return i + 1;
979 }
980
981 return 0;
982 }
983
984 char *lxc_trim_whitespace_in_place(char *buffer)
985 {
986 buffer += lxc_char_left_gc(buffer, strlen(buffer));
987 buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0';
988 return buffer;
989 }
990
991 int lxc_is_line_empty(const char *line)
992 {
993 int i;
994 size_t len = strlen(line);
995
996 for (i = 0; i < len; i++)
997 if (line[i] != ' ' && line[i] != '\t' &&
998 line[i] != '\n' && line[i] != '\r' &&
999 line[i] != '\f' && line[i] != '\0')
1000 return 0;
1001 return 1;
1002 }
1003
1004 void remove_trailing_slashes(char *p)
1005 {
1006 int l = strlen(p);
1007 while (--l >= 0 && (p[l] == '/' || p[l] == '\n'))
1008 p[l] = '\0';
1009 }