/* Copyright 2006,2007,2008,2012 Martin Pärtel This file is part of bindfs. bindfs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. bindfs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with bindfs. If not, see . */ #include "misc.h" #include #include #include #include #include #include #include "arena.h" static char *arena_strdup(struct arena *a, const char *s) { size_t n = strlen(s) + 1; char *p = (char *)arena_malloc(a, n); if (!p) { fprintf(stderr, "bindfs: arena allocation failed\n"); abort(); } memcpy(p, s, n); return p; } /* Konkateniert zwei Strings in der Arena: out = s1 + s2 */ static char *arena_strcat2(struct arena *a, const char *s1, const char *s2) { size_t n1 = strlen(s1); size_t n2 = strlen(s2); char *p = (char *)arena_malloc(a, n1 + n2 + 1); if (!p) { fprintf(stderr, "bindfs: arena allocation failed\n"); abort(); } memcpy(p, s1, n1); memcpy(p + n1, s2, n2 + 1); return p; } int count_chars(const char *s, char ch) { int count = 0; assert(s != NULL); while (*s != '\0') { if (*s == ch) ++count; ++s; } return count; } int count_substrs(const char *s, const char *sub) { int count = 0; int sublen = strlen(sub); int left = strlen(s); assert(s != NULL && sub != NULL); while (left > sublen) { if (strncmp(s, sub, sublen) == 0) ++count; --left; ++s; } return count; } char *strdup_until(const char *s, const char *endchars) { char *endloc = strpbrk(s, endchars); char *ret; if (!endloc) { ret = malloc((strlen(s) + 1) * sizeof(char)); strcpy(ret, s); return ret; } else { ret = malloc((endloc - s + 1) * sizeof(char)); memcpy(ret, s, (endloc - s) * sizeof(char)); ret[(endloc - s)] = '\0'; return ret; } } char *sprintf_new(const char *format, ...) { va_list ap; size_t buffer_size = strlen(format) + 32; char *buffer = malloc(buffer_size); if (buffer == NULL) { return NULL; } while (1) { va_start(ap, format); size_t len = (size_t)vsnprintf(buffer, buffer_size, format, ap); va_end(ap); if (len < buffer_size) { return buffer; } free(buffer); buffer_size *= 2; buffer = malloc(buffer_size); if (buffer == NULL) { return NULL; } } } const char *my_basename(const char *path) { const char *p; if (path == NULL) return NULL; p = strrchr(path, '/'); if (p != NULL) return p + 1; else return path; } const char *my_dirname(char *path) { if (strcmp(path, ".") == 0) { return ".."; } else if (strcmp(path, "/") == 0) { return "/"; } else { size_t len = strlen(path); char *p = path + len - 1; while (p > path) { if (*p == '/') { break; } --p; } if (p > path) { *p = '\0'; return path; } else if (*path == '/') { return "/"; } else { return "."; } } } static const char* find_last_char_between(const char* start, const char* end, char ch) { assert(start != NULL && end != NULL); const char* p = end - 1; while (p >= start) { if (*p == ch) { return p; } --p; } return NULL; } bool path_starts_with(const char *path, const char* prefix, size_t prefix_len) { size_t path_len = strlen(path); while (prefix_len > 0 && prefix[prefix_len - 1] == '/') { --prefix_len; } while (path_len > 0 && path[path_len - 1] == '/') { --path_len; } if (strncmp(path, prefix, prefix_len) == 0) { // We still need to check that the last path component of // 'path' does not simply start with the last path component of 'prefix'. const char* prefix_slash = find_last_char_between(prefix, prefix + prefix_len, '/'); const char* prefix_part = prefix_slash ? prefix_slash + 1 : prefix; size_t prefix_part_len = (prefix + prefix_len - prefix_part); const char* path_part = path + (prefix_part - prefix); const char* path_slash = strchr(path_part, '/'); size_t path_part_len = path_slash ? (size_t)(path_slash - path_part) : path_len - (path_part - path); return prefix_part_len == path_part_len; } return false; } static char **dup_argv(int argc, const char * const *argv, struct arena *arena) { char **pointer_list = arena_malloc(arena, (argc + 1) * sizeof(char*)); char **next_ptr = pointer_list; for (int i = 0; i < argc; ++i) { size_t len = strlen(argv[i]); char *str = arena_malloc(arena, len + 1); memcpy(str, argv[i], len + 1); *next_ptr = str; ++next_ptr; } *next_ptr = NULL; return pointer_list; } /* Converts all ("-o", "...") into ("-o..."). */ static void merge_o_args( int *argc, char **argv, struct arena *arena ) { int i = 0; while (i < *argc) { char *arg = argv[i]; if (strcmp(arg, "-o") == 0) { if (i + 1 < *argc) { char *merged = arena_malloc(arena, 2 + strlen(argv[i + 1]) + 1); merged[0] = '-'; merged[1] = 'o'; strcpy(&merged[2], argv[i + 1]); argv[i] = merged; for (int j = i + 1; j < *argc - 1; ++j) { argv[j] = argv[j + 1]; } } --*argc; } ++i; } } char *strtok_escape_r(char *str, const char *delim, char escape, char **saveptr) { char *r, *w, *start; if (str == NULL) { if (saveptr == NULL || *saveptr == NULL) return NULL; str = *saveptr; } r = str; while (*r && strchr(delim, *r) != NULL) { r++; } if (*r == '\0') { *saveptr = r; return NULL; } start = w = r; for (;;) { if (*r == '\0') { *w = '\0'; *saveptr = r; return start; } if (*r == escape) { if (r[1] != '\0') { char c = r[1]; *w++ = c; r += 2; continue; } else { r++; continue; } } if (strchr(delim, *r) != NULL) { *w = '\0'; r++; while (*r && strchr(delim, *r) != NULL) r++; *saveptr = r; return start; } *w++ = *r++; } } char *strtok_escape(char *str, const char *delim, char escape) { static char *save; return strtok_escape_r(str, delim, escape, &save); } char *strtok_escape_preserve_r(char *str, const char *delim, char escape, char **saveptr) { char *s = str ? str : *saveptr; char *r = s; char *w = s; if (s == NULL) return NULL; /* führende Delimiter überspringen (kollabieren) */ while (*r && strchr(delim, *r)) r++; if (!*r) { *saveptr = r; return NULL; } for (;;) { if (*r == '\0') { *w = '\0'; *saveptr = r; return s; } if (*r == escape) { if (r[1] == '\0') { /* dangling escape: '\' wörtlich übernehmen */ *w++ = *r++; *w = '\0'; *saveptr = r; return s; } /* WICHTIG: Backslash BEHALTEN und nächstes Zeichen wörtlich kopieren */ *w++ = escape; *w++ = r[1]; r += 2; continue; } if (strchr(delim, *r)) { /* Ende des Tokens; nachfolgende Delimiter kollabieren */ *w = '\0'; do { r++; } while (*r && strchr(delim, *r)); *saveptr = r; return s; } *w++ = *r++; } } char *strtok_escape_preserve(char *str, const char *delim, char escape) { static char *saveptr_pres; return strtok_escape_preserve_r(str, delim, escape, &saveptr_pres); } int join_strings(char **res, const char *a, const char *b, const char *sep) { size_t la = a ? strlen(a) : 0; size_t lb = b ? strlen(b) : 0; size_t ls = sep ? strlen(sep) : 0; size_t old = *res ? strlen(*res) : 0; size_t extra = (old ? ls : 0) + la + lb; char *d = realloc(*res, old + extra + 1); if (!d) return -1; char *p = d + old; if (old && sep) { memcpy(p, sep, ls); p += ls; } if (a) { memcpy(p, a, la); p += la; } if (b) { memcpy(p, b, lb); p += lb; } *p = '\0'; *res = d; return 0; } int add_option(char ***argv, int *argc, const char *opt) { char **newv = realloc(*argv, sizeof(char*) * (*argc + 2)); if (!newv) return -1; newv[*argc] = strdup(opt); if (!newv[*argc]) return -1; (*argc)++; newv[*argc] = NULL; *argv = newv; return 0; } int add_options(char ***argv, int *argc, const char *o1, const char *o2) { if (add_option(argv, argc, o1) == -1) return -1; if (o2) return add_option(argv, argc, o2); return 0; } void remove_option(char **argv, int *argc, int idx) { free(argv[idx]); for (int i = idx; i + 1 < *argc; ++i) argv[i] = argv[i+1]; (*argc)--; argv[*argc] = NULL; } void filter_o_opts(bool (*keep_option)(const char *opt), int argc_in, const char * const *argv_in, int *argc_out, char ***argv_out, struct arena *arena) { (void)keep_option; /* aktuell nicht genutzt; API bleibt stabil */ /* Worst-case: gleich viele Ausgabeargumente wie Eingabe. (Bei Zusammenziehen von "-o" "" werden es sogar weniger.) */ char **outv = (char **)arena_malloc(arena, (size_t)(argc_in > 0 ? argc_in : 1) * sizeof(char *)); if (!outv) { fprintf(stderr, "bindfs: arena allocation failed\n"); abort(); } int outc = 0; for (int i = 0; i < argc_in; ++i) { const char *arg = argv_in[i]; if (arg[0] == '-' && arg[1] == 'o') { /* Fall A: "-o" -> unverändert übernehmen */ if (arg[2] != '\0') { outv[outc++] = arena_strdup(arena, arg); continue; } /* Fall B: separates "-o" + "" zusammenziehen */ if (i + 1 < argc_in) { const char *val = argv_in[i + 1]; char *merged = arena_strcat2(arena, "-o", val); outv[outc++] = merged; ++i; /* den Wert haben wir verbraucht */ continue; } /* Edge case: nacktes "-o" am Ende – übernimm es trotzdem, libfuse meldet später einen sinnvollen Fehler. */ outv[outc++] = arena_strdup(arena, arg); continue; } /* alle anderen Argumente 1:1 durchreichen */ outv[outc++] = arena_strdup(arena, arg); } *argc_out = outc; *argv_out = outv; } void grow_array_impl(void **array, int *capacity, int member_size) { int new_cap = *capacity; if (new_cap == 0) { new_cap = 8; } else { new_cap *= 2; } *array = realloc(*array, new_cap * member_size); *capacity = new_cap; } int parse_byte_count(const char *str, double *result) { char* end; double base = strtod(str, &end); long long mul = 1; if (*end == '\0') { mul = 1L; } else if (strcmp(end, "k") == 0) { mul = 1024L; } else if (strcmp(end, "M") == 0) { mul = 1024L * 1024L; } else if (strcmp(end, "G") == 0) { mul = 1024L * 1024L * 1024L; } else if (strcmp(end, "T") == 0) { mul = 1024LL * 1024LL * 1024LL * 1024LL; } else { return 0; } *result = base * mul; return 1; } void init_memory_block(struct memory_block *a, size_t initial_capacity) { a->size = 0; a->capacity = initial_capacity; if (initial_capacity > 0) { a->ptr = (char *)malloc(initial_capacity); } else { a->ptr = NULL; } } void grow_memory_block(struct memory_block *a, size_t amount) { size_t new_cap; a->size += amount; if (a->size >= a->capacity) { new_cap = a->capacity; while (new_cap < a->size) { if (new_cap == 0) { new_cap = 8; } else { if (new_cap > SIZE_MAX / 2) { fprintf(stderr, "Memory block too large."); abort(); } new_cap *= 2; } } a->ptr = (char *)realloc(a->ptr, new_cap); a->capacity = new_cap; } } int append_to_memory_block(struct memory_block *a, const void *src, size_t src_size) { size_t dest = a->size; grow_memory_block(a, src_size); memcpy(&a->ptr[dest], src, src_size); return dest; } void free_memory_block(struct memory_block *a) { free(a->ptr); init_memory_block(a, 0); }