summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/path-util.c53
-rw-r--r--src/basic/path-util.h6
2 files changed, 38 insertions, 21 deletions
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index f98859939c..f06858da6c 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -548,18 +548,22 @@ bool path_equal_filename(const char *a, const char *b) {
return path_equal(a_basename, b_basename);
}
-char* path_join_internal(const char *first, ...) {
- char *joined, *q;
+char* path_extend_internal(char **x, ...) {
+ size_t sz, old_sz;
+ char *q, *nx;
const char *p;
va_list ap;
bool slash;
- size_t sz;
/* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
* already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
* removed. The string returned is hence always equal to or longer than the sum of the lengths of each
* individual string.
*
+ * The first argument may be an already allocated string that is extended via realloc() if
+ * non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the
+ * first parameter to distinguish the two operations.
+ *
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
* are optional.
*
@@ -569,28 +573,39 @@ char* path_join_internal(const char *first, ...) {
* path_join("foo/", "bar") → "foo/bar"
* path_join("", "foo", "", "bar", "") → "foo/bar" */
- sz = strlen_ptr(first);
- va_start(ap, first);
- while ((p = va_arg(ap, char*)) != POINTER_MAX)
- if (!isempty(p))
- sz += 1 + strlen(p);
+ sz = old_sz = x ? strlen_ptr(*x) : 0;
+ va_start(ap, x);
+ while ((p = va_arg(ap, char*)) != POINTER_MAX) {
+ size_t add;
+
+ if (isempty(p))
+ continue;
+
+ add = 1 + strlen(p);
+ if (sz > SIZE_MAX - add) /* overflow check */
+ return NULL;
+
+ sz += add;
+ }
+
va_end(ap);
- joined = new(char, sz + 1);
- if (!joined)
+ nx = realloc(x ? *x : NULL, GREEDY_ALLOC_ROUND_UP(sz+1));
+ if (!nx)
return NULL;
+ if (x)
+ *x = nx;
- if (!isempty(first)) {
- q = stpcpy(joined, first);
- slash = endswith(first, "/");
- } else {
- /* Skip empty items */
- joined[0] = 0;
- q = joined;
+ if (old_sz > 0)
+ slash = nx[old_sz] == '/';
+ else {
+ nx[old_sz] = 0;
slash = true; /* no need to generate a slash anymore */
}
- va_start(ap, first);
+ q = nx + old_sz;
+
+ va_start(ap, x);
while ((p = va_arg(ap, char*)) != POINTER_MAX) {
if (isempty(p))
continue;
@@ -603,7 +618,7 @@ char* path_join_internal(const char *first, ...) {
}
va_end(ap);
- return joined;
+ return nx;
}
static int check_x_access(const char *path, int *ret_fd) {
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index f82d935dc5..238a76ea73 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -63,8 +63,10 @@ bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b, int flags);
/* Compares only the last portion of the input paths, ie: the filenames */
bool path_equal_filename(const char *a, const char *b);
-char* path_join_internal(const char *first, ...);
-#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX)
+
+char* path_extend_internal(char **x, ...);
+#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
+#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
char* path_simplify(char *path, bool kill_dots);