]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/string_utils.c
cgroups: flatten hierarchy
[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 (!strcmp(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 char **lxc_normalize_path(const char *path)
196 {
197 char **components;
198 char **p;
199 size_t components_len = 0;
200 size_t pos = 0;
201
202 components = lxc_string_split(path, '/');
203 if (!components)
204 return NULL;
205
206 for (p = components; *p; p++)
207 components_len++;
208
209 /* resolve '.' and '..' */
210 for (pos = 0; pos < components_len;) {
211 if (!strcmp(components[pos], ".") ||
212 (!strcmp(components[pos], "..") && pos == 0)) {
213 /* eat this element */
214 free(components[pos]);
215 memmove(&components[pos], &components[pos + 1],
216 sizeof(char *) * (components_len - pos));
217 components_len--;
218 } else if (!strcmp(components[pos], "..")) {
219 /* eat this and the previous element */
220 free(components[pos - 1]);
221 free(components[pos]);
222 memmove(&components[pos - 1], &components[pos + 1],
223 sizeof(char *) * (components_len - pos));
224 components_len -= 2;
225 pos--;
226 } else {
227 pos++;
228 }
229 }
230
231 return components;
232 }
233
234 char *lxc_deslashify(const char *path)
235 {
236 char *dup, *p;
237 char **parts = NULL;
238 size_t n, len;
239
240 dup = strdup(path);
241 if (!dup)
242 return NULL;
243
244 parts = lxc_normalize_path(dup);
245 if (!parts) {
246 free(dup);
247 return NULL;
248 }
249
250 /* We'll end up here if path == "///" or path == "". */
251 if (!*parts) {
252 len = strlen(dup);
253 if (!len) {
254 lxc_free_array((void **)parts, free);
255 return dup;
256 }
257
258 n = strcspn(dup, "/");
259 if (n == len) {
260 free(dup);
261 lxc_free_array((void **)parts, free);
262
263 p = strdup("/");
264 if (!p)
265 return NULL;
266
267 return p;
268 }
269 }
270
271 p = lxc_string_join("/", (const char **)parts, *dup == '/');
272 free(dup);
273 lxc_free_array((void **)parts, free);
274 return p;
275 }
276
277 char *lxc_append_paths(const char *first, const char *second)
278 {
279 int ret;
280 size_t len;
281 char *result = NULL;
282 int pattern_type = 0;
283
284 len = strlen(first) + strlen(second) + 1;
285 if (second[0] != '/') {
286 len += 1;
287 pattern_type = 1;
288 }
289
290 result = calloc(1, len);
291 if (!result)
292 return NULL;
293
294 if (pattern_type == 0)
295 ret = snprintf(result, len, "%s%s", first, second);
296 else
297 ret = snprintf(result, len, "%s/%s", first, second);
298 if (ret < 0 || (size_t)ret >= len) {
299 free(result);
300 return NULL;
301 }
302
303 return result;
304 }
305
306 bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
307 {
308 __do_free char *str = NULL;
309 char *token;
310 char sep[2] = { _sep, '\0' };
311
312 if (!haystack || !needle)
313 return 0;
314
315 str = must_copy_string(haystack);
316 lxc_iterate_parts(token, str, sep)
317 if (strcmp(needle, token) == 0)
318 return 1;
319
320 return 0;
321 }
322
323 char **lxc_string_split(const char *string, char _sep)
324 {
325 __do_free char *str = NULL;
326 char *token;
327 char sep[2] = {_sep, '\0'};
328 char **tmp = NULL, **result = NULL;
329 size_t result_capacity = 0;
330 size_t result_count = 0;
331 int r, saved_errno;
332
333 if (!string)
334 return calloc(1, sizeof(char *));
335
336 str = must_copy_string(string);
337 lxc_iterate_parts(token, str, sep) {
338 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
339 if (r < 0)
340 goto error_out;
341
342 result[result_count] = strdup(token);
343 if (!result[result_count])
344 goto error_out;
345
346 result_count++;
347 }
348
349 /* if we allocated too much, reduce it */
350 tmp = realloc(result, (result_count + 1) * sizeof(char *));
351 if (!tmp)
352 goto error_out;
353
354 result = tmp;
355
356 /* Make sure we don't return uninitialized memory. */
357 if (result_count == 0)
358 *result = NULL;
359
360 return result;
361
362 error_out:
363 saved_errno = errno;
364 lxc_free_array((void **)result, free);
365 errno = saved_errno;
366 return NULL;
367 }
368
369 static bool complete_word(char ***result, char *start, char *end, size_t *cap,
370 size_t *cnt)
371 {
372 int r;
373
374 r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16);
375 if (r < 0)
376 return false;
377
378 (*result)[*cnt] = strndup(start, end - start);
379 if (!(*result)[*cnt])
380 return false;
381
382 (*cnt)++;
383
384 return true;
385 }
386
387 /*
388 * Given a a string 'one two "three four"', split into three words,
389 * one, two, and "three four"
390 */
391 char **lxc_string_split_quoted(char *string)
392 {
393 char *nextword = string, *p, state;
394 char **result = NULL;
395 size_t result_capacity = 0;
396 size_t result_count = 0;
397
398 if (!string || !*string)
399 return calloc(1, sizeof(char *));
400
401 // TODO I'm *not* handling escaped quote
402 state = ' ';
403 for (p = string; *p; p++) {
404 switch(state) {
405 case ' ':
406 if (isspace(*p))
407 continue;
408 else if (*p == '"' || *p == '\'') {
409 nextword = p;
410 state = *p;
411 continue;
412 }
413 nextword = p;
414 state = 'a';
415 continue;
416 case 'a':
417 if (isspace(*p)) {
418 complete_word(&result, nextword, p, &result_capacity, &result_count);
419 state = ' ';
420 continue;
421 }
422 continue;
423 case '"':
424 case '\'':
425 if (*p == state) {
426 complete_word(&result, nextword+1, p, &result_capacity, &result_count);
427 state = ' ';
428 continue;
429 }
430 continue;
431 }
432 }
433
434 if (state == 'a')
435 complete_word(&result, nextword, p, &result_capacity, &result_count);
436
437 return realloc(result, (result_count + 1) * sizeof(char *));
438 }
439
440 char **lxc_string_split_and_trim(const char *string, char _sep)
441 {
442 __do_free char *str = NULL;
443 char *token;
444 char sep[2] = { _sep, '\0' };
445 char **result = NULL;
446 size_t result_capacity = 0;
447 size_t result_count = 0;
448 int r, saved_errno;
449 size_t i = 0;
450
451 if (!string)
452 return calloc(1, sizeof(char *));
453
454 str = must_copy_string(string);
455 lxc_iterate_parts(token, str, sep) {
456 while (token[0] == ' ' || token[0] == '\t')
457 token++;
458
459 i = strlen(token);
460 while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
461 token[i - 1] = '\0';
462 i--;
463 }
464
465 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
466 if (r < 0)
467 goto error_out;
468
469 result[result_count] = strdup(token);
470 if (!result[result_count])
471 goto error_out;
472
473 result_count++;
474 }
475
476 /* if we allocated too much, reduce it */
477 return realloc(result, (result_count + 1) * sizeof(char *));
478
479 error_out:
480 saved_errno = errno;
481 lxc_free_array((void **)result, free);
482 errno = saved_errno;
483 return NULL;
484 }
485
486 void lxc_free_array(void **array, lxc_free_fn element_free_fn)
487 {
488 void **p;
489
490 for (p = array; p && *p; p++)
491 element_free_fn(*p);
492
493 free((void*)array);
494 }
495
496 int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment)
497 {
498 size_t new_capacity;
499 void **new_array;
500
501 /* first time around, catch some trivial mistakes of the user
502 * only initializing one of these */
503 if (!*array || !*capacity) {
504 *array = NULL;
505 *capacity = 0;
506 }
507
508 new_capacity = *capacity;
509 while (new_size + 1 > new_capacity)
510 new_capacity += capacity_increment;
511
512 if (new_capacity != *capacity) {
513 /* we have to reallocate */
514 new_array = realloc(*array, new_capacity * sizeof(void *));
515 if (!new_array)
516 return -1;
517
518 memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
519 *array = new_array;
520 *capacity = new_capacity;
521 }
522
523 /* array has sufficient elements */
524 return 0;
525 }
526
527 size_t lxc_array_len(void **array)
528 {
529 void **p;
530 size_t result = 0;
531
532 for (p = array; p && *p; p++)
533 result++;
534
535 return result;
536 }
537
538 void **lxc_append_null_to_array(void **array, size_t count)
539 {
540 void **temp;
541
542 /* Append NULL to the array */
543 if (count) {
544 temp = realloc(array, (count + 1) * sizeof(*array));
545 if (!temp) {
546 size_t i;
547 for (i = 0; i < count; i++)
548 free(array[i]);
549 free(array);
550 return NULL;
551 }
552
553 array = temp;
554 array[count] = NULL;
555 }
556
557 return array;
558 }
559
560 static int lxc_append_null_to_list(void ***list)
561 {
562 int newentry = 0;
563 void **tmp;
564
565 if (*list)
566 for (; (*list)[newentry]; newentry++) {
567 ;
568 }
569
570 tmp = realloc(*list, (newentry + 2) * sizeof(void **));
571 if (!tmp)
572 return -1;
573
574 *list = tmp;
575 (*list)[newentry + 1] = NULL;
576
577 return newentry;
578 }
579
580 int lxc_append_string(char ***list, char *entry)
581 {
582 char *copy;
583 int newentry;
584
585 newentry = lxc_append_null_to_list((void ***)list);
586 if (newentry < 0)
587 return -1;
588
589 copy = strdup(entry);
590 if (!copy)
591 return -1;
592
593 (*list)[newentry] = copy;
594
595 return 0;
596 }
597
598 int lxc_safe_uint(const char *numstr, unsigned int *converted)
599 {
600 char *err = NULL;
601 unsigned long int uli;
602
603 while (isspace(*numstr))
604 numstr++;
605
606 if (*numstr == '-')
607 return -EINVAL;
608
609 errno = 0;
610 uli = strtoul(numstr, &err, 0);
611 if (errno == ERANGE && uli == ULONG_MAX)
612 return -ERANGE;
613
614 if (err == numstr || *err != '\0')
615 return -EINVAL;
616
617 if (uli > UINT_MAX)
618 return -ERANGE;
619
620 *converted = (unsigned int)uli;
621 return 0;
622 }
623
624 int lxc_safe_ulong(const char *numstr, unsigned long *converted)
625 {
626 char *err = NULL;
627 unsigned long int uli;
628
629 while (isspace(*numstr))
630 numstr++;
631
632 if (*numstr == '-')
633 return -EINVAL;
634
635 errno = 0;
636 uli = strtoul(numstr, &err, 0);
637 if (errno == ERANGE && uli == ULONG_MAX)
638 return -ERANGE;
639
640 if (err == numstr || *err != '\0')
641 return -EINVAL;
642
643 *converted = uli;
644 return 0;
645 }
646
647 int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base)
648 {
649 char *err = NULL;
650 uint64_t u;
651
652 while (isspace(*numstr))
653 numstr++;
654
655 if (*numstr == '-')
656 return -EINVAL;
657
658 errno = 0;
659 u = strtoull(numstr, &err, base);
660 if (errno == ERANGE && u == UINT64_MAX)
661 return -ERANGE;
662
663 if (err == numstr || *err != '\0')
664 return -EINVAL;
665
666 *converted = u;
667 return 0;
668 }
669
670 int lxc_safe_int(const char *numstr, int *converted)
671 {
672 char *err = NULL;
673 signed long int sli;
674
675 errno = 0;
676 sli = strtol(numstr, &err, 0);
677 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
678 return -ERANGE;
679
680 if (errno != 0 && sli == 0)
681 return -EINVAL;
682
683 if (err == numstr || *err != '\0')
684 return -EINVAL;
685
686 if (sli > INT_MAX || sli < INT_MIN)
687 return -ERANGE;
688
689 *converted = (int)sli;
690 return 0;
691 }
692
693 int lxc_safe_long(const char *numstr, long int *converted)
694 {
695 char *err = NULL;
696 signed long int sli;
697
698 errno = 0;
699 sli = strtol(numstr, &err, 0);
700 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
701 return -ERANGE;
702
703 if (errno != 0 && sli == 0)
704 return -EINVAL;
705
706 if (err == numstr || *err != '\0')
707 return -EINVAL;
708
709 *converted = sli;
710 return 0;
711 }
712
713 int lxc_safe_long_long(const char *numstr, long long int *converted)
714 {
715 char *err = NULL;
716 signed long long int sli;
717
718 errno = 0;
719 sli = strtoll(numstr, &err, 0);
720 if (errno == ERANGE && (sli == LLONG_MAX || sli == LLONG_MIN))
721 return -ERANGE;
722
723 if (errno != 0 && sli == 0)
724 return -EINVAL;
725
726 if (err == numstr || *err != '\0')
727 return -EINVAL;
728
729 *converted = sli;
730 return 0;
731 }
732
733 char *must_concat(size_t *len, const char *first, ...)
734 {
735 va_list args;
736 char *cur, *dest;
737 size_t cur_len, it_len;
738
739 dest = must_copy_string(first);
740 cur_len = it_len = strlen(first);
741
742 va_start(args, first);
743 while ((cur = va_arg(args, char *)) != NULL) {
744 it_len = strlen(cur);
745
746 dest = must_realloc(dest, cur_len + it_len + 1);
747
748 (void)memcpy(dest + cur_len, cur, it_len);
749 cur_len += it_len;
750 }
751 va_end(args);
752
753 dest[cur_len] = '\0';
754 if (len)
755 *len = cur_len;
756 return dest;
757 }
758
759 char *must_make_path(const char *first, ...)
760 {
761 va_list args;
762 char *cur, *dest;
763 size_t full_len = strlen(first);
764 size_t buf_len;
765 size_t cur_len;
766
767 dest = must_copy_string(first);
768 cur_len = full_len;
769
770 va_start(args, first);
771 while ((cur = va_arg(args, char *)) != NULL) {
772 buf_len = strlen(cur);
773
774 full_len += buf_len;
775 if (cur[0] != '/')
776 full_len++;
777
778 dest = must_realloc(dest, full_len + 1);
779
780 if (cur[0] != '/') {
781 memcpy(dest + cur_len, "/", 1);
782 cur_len++;
783 }
784
785 memcpy(dest + cur_len, cur, buf_len);
786 cur_len += buf_len;
787 }
788 va_end(args);
789
790 dest[cur_len] = '\0';
791 return dest;
792 }
793
794 char *must_append_path(char *first, ...)
795 {
796 char *cur;
797 size_t full_len;
798 va_list args;
799 char *dest = first;
800 size_t buf_len;
801 size_t cur_len;
802
803 full_len = strlen(first);
804 cur_len = full_len;
805
806 va_start(args, first);
807 while ((cur = va_arg(args, char *)) != NULL) {
808 buf_len = strlen(cur);
809
810 full_len += buf_len;
811 if (cur[0] != '/')
812 full_len++;
813
814 dest = must_realloc(dest, full_len + 1);
815
816 if (cur[0] != '/') {
817 memcpy(dest + cur_len, "/", 1);
818 cur_len++;
819 }
820
821 memcpy(dest + cur_len, cur, buf_len);
822 cur_len += buf_len;
823 }
824 va_end(args);
825
826 dest[cur_len] = '\0';
827 return dest;
828 }
829
830 char *must_copy_string(const char *entry)
831 {
832 char *ret;
833
834 if (!entry)
835 return NULL;
836
837 do {
838 ret = strdup(entry);
839 } while (!ret);
840
841 return ret;
842 }
843
844 void *must_realloc(void *orig, size_t sz)
845 {
846 void *ret;
847
848 do {
849 ret = realloc(orig, sz);
850 } while (!ret);
851
852 return ret;
853 }
854
855 int parse_byte_size_string(const char *s, int64_t *converted)
856 {
857 int ret, suffix_len;
858 long long int conv;
859 int64_t mltpl, overflow;
860 char *end;
861 char dup[INTTYPE_TO_STRLEN(int64_t)];
862 char suffix[3] = {0};
863
864 if (!s || !strcmp(s, ""))
865 return -EINVAL;
866
867 end = stpncpy(dup, s, sizeof(dup) - 1);
868 if (*end != '\0')
869 return -EINVAL;
870
871 if (isdigit(*(end - 1)))
872 suffix_len = 0;
873 else if (isalpha(*(end - 1)))
874 suffix_len = 1;
875 else
876 return -EINVAL;
877
878 if (suffix_len > 0 && (end - 2) == dup && !isdigit(*(end - 2)))
879 return -EINVAL;
880
881 if (suffix_len > 0 && isalpha(*(end - 2)))
882 suffix_len++;
883
884 if (suffix_len > 0) {
885 memcpy(suffix, end - suffix_len, suffix_len);
886 *(suffix + suffix_len) = '\0';
887 *(end - suffix_len) = '\0';
888 }
889 dup[lxc_char_right_gc(dup, strlen(dup))] = '\0';
890
891 ret = lxc_safe_long_long(dup, &conv);
892 if (ret < 0)
893 return -ret;
894
895 if (suffix_len != 2) {
896 *converted = conv;
897 return 0;
898 }
899
900 if (strcasecmp(suffix, "KB") == 0)
901 mltpl = 1024;
902 else if (strcasecmp(suffix, "MB") == 0)
903 mltpl = 1024 * 1024;
904 else if (strcasecmp(suffix, "GB") == 0)
905 mltpl = 1024 * 1024 * 1024;
906 else
907 return -EINVAL;
908
909 overflow = conv * mltpl;
910 if (conv != 0 && (overflow / conv) != mltpl)
911 return -ERANGE;
912
913 *converted = overflow;
914 return 0;
915 }
916
917 void remove_trailing_newlines(char *l)
918 {
919 char *p = l;
920
921 while (*p)
922 p++;
923
924 while (--p >= l && *p == '\n')
925 *p = '\0';
926 }
927
928 int lxc_char_left_gc(const char *buffer, size_t len)
929 {
930 size_t i;
931
932 for (i = 0; i < len; i++) {
933 if (buffer[i] == ' ' ||
934 buffer[i] == '\t')
935 continue;
936
937 return i;
938 }
939
940 return 0;
941 }
942
943 int lxc_char_right_gc(const char *buffer, size_t len)
944 {
945 int i;
946
947 for (i = len - 1; i >= 0; i--) {
948 if (buffer[i] == ' ' ||
949 buffer[i] == '\t' ||
950 buffer[i] == '\n' ||
951 buffer[i] == '\0')
952 continue;
953
954 return i + 1;
955 }
956
957 return 0;
958 }
959
960 char *lxc_trim_whitespace_in_place(char *buffer)
961 {
962 buffer += lxc_char_left_gc(buffer, strlen(buffer));
963 buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0';
964 return buffer;
965 }
966
967 int lxc_is_line_empty(const char *line)
968 {
969 int i;
970 size_t len = strlen(line);
971
972 for (i = 0; i < len; i++)
973 if (line[i] != ' ' && line[i] != '\t' &&
974 line[i] != '\n' && line[i] != '\r' &&
975 line[i] != '\f' && line[i] != '\0')
976 return 0;
977 return 1;
978 }
979
980 void remove_trailing_slashes(char *p)
981 {
982 int l = strlen(p);
983 while (--l >= 0 && (p[l] == '/' || p[l] == '\n'))
984 p[l] = '\0';
985 }