Files
qdl/pathbuf.c
T
Bjorn Andersson 908e3a78d2 contents: Look for OS-specific path tag
The contents.xml provides two different "root path" tags, one with Linux
and one with Windows path delimiters. Use the Windows tag to avoid
issues related to path traversal.

Extend the tests as necessary.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-05-16 16:36:27 -05:00

188 lines
3.3 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
#include "oscompat.h"
#include "pathbuf.h"
static bool qdl_path_is_sep(char ch)
{
#ifdef _WIN32
return ch == '/' || ch == '\\';
#else
return ch == '/';
#endif
}
static char *qdl_pathbuf_last_sep(char *path)
{
char *last = NULL;
for (; *path; path++) {
if (qdl_path_is_sep(*path))
last = path;
}
return last;
}
#ifdef _WIN32
static size_t qdl_pathbuf_unc_root_len(const char *path)
{
size_t i;
size_t start;
if (!qdl_path_is_sep(path[0]) || !qdl_path_is_sep(path[1]))
return 0;
i = 2;
while (qdl_path_is_sep(path[i]))
i++;
if (!path[i])
return 2;
start = i;
while (path[i] && !qdl_path_is_sep(path[i]))
i++;
if (i == start)
return 2;
while (qdl_path_is_sep(path[i]))
i++;
if (!path[i])
return 2;
start = i;
while (path[i] && !qdl_path_is_sep(path[i]))
i++;
if (i == start)
return 2;
return i;
}
#endif
void qdl_pathbuf_reset(struct pathbuf *path)
{
if (!path)
return;
path->buf[0] = '\0';
path->len = 0;
}
void qdl_pathbuf_dup(struct pathbuf *dst, const struct pathbuf *orig)
{
memcpy(dst, orig, sizeof(struct pathbuf));
}
int qdl_pathbuf_push(struct pathbuf *path, const char *component)
{
size_t component_len;
size_t path_len;
size_t skip = 0;
size_t need_sep = 0;
if (!path || !component)
return -EINVAL;
if (component[0] == '\0')
return 0;
path_len = path->len;
if (path_is_absolute(component))
path_len = 0;
if (component[0] == '.' && qdl_path_is_sep(component[1]))
skip += 2;
if (path_len > 0)
need_sep = !qdl_path_is_sep(path->buf[path_len - 1]);
if (path_len > 0 && !need_sep) {
while (qdl_path_is_sep(component[skip]))
skip++;
}
component_len = strlen(component + skip);
if (component_len == 0)
return 0;
if (path_len + need_sep + component_len + 1 > sizeof(path->buf))
return -ENAMETOOLONG;
if (need_sep)
path->buf[path_len++] = '/';
#ifndef _WIN32
memcpy(path->buf + path_len, component + skip, component_len + 1);
#else
for (size_t i = 0; i <= component_len; i++)
path->buf[path_len + i] = qdl_path_is_sep(component[skip + i]) ? '/' : component[skip + i];
#endif
path->len = path_len + component_len;
return 0;
}
const char *qdl_pathbuf_str(const struct pathbuf *path)
{
return path ? path->buf : NULL;
}
void qdl_pathbuf_dirname(struct pathbuf *path)
{
size_t root_len = 0;
char *sep;
if (!path || !path->buf[0])
return;
if (path->len == 0)
path->len = strlen(path->buf);
#ifdef _WIN32
if (isalpha((unsigned char)path->buf[0]) && path->buf[1] == ':' && qdl_path_is_sep(path->buf[2])) {
root_len = 3;
} else {
root_len = qdl_pathbuf_unc_root_len(path->buf);
if (root_len == 0 && qdl_path_is_sep(path->buf[0]))
root_len = 1;
}
#else
if (path->buf[0] == '/')
root_len = 1;
#endif
while (path->len > root_len && qdl_path_is_sep(path->buf[path->len - 1])) {
path->buf[--path->len] = '\0';
}
sep = qdl_pathbuf_last_sep(path->buf);
if (!sep)
return;
if ((size_t)(sep - path->buf) < root_len) {
path->buf[root_len] = '\0';
path->len = root_len;
return;
}
*sep = '\0';
path->len = (size_t)(sep - path->buf);
}
bool qdl_path_exists(const struct pathbuf *path)
{
struct stat st;
if (!path || !path->buf[0])
return false;
return stat(path->buf, &st) == 0;
}