aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/serd/serd.h13
-rw-r--r--meson.build5
-rw-r--r--src/system.c25
-rw-r--r--test/.clang-tidy1
-rw-r--r--test/test_string.c30
5 files changed, 72 insertions, 2 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 72a4c3fa..362de28d 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -256,6 +256,19 @@ size_t
serd_strlen(const char* SERD_NONNULL str, SerdNodeFlags* SERD_NULLABLE flags);
/**
+ Return `path` as a canonical absolute path.
+
+ This expands all symbolic links, relative references, and removes extra
+ directory separators. Null is returned on error, including if the path does
+ not exist.
+
+ @return A new string that must be freed with serd_free(), or null.
+*/
+SERD_API
+char* SERD_NULLABLE
+serd_canonical_path(const char* SERD_NONNULL path);
+
+/**
@}
@defgroup serd_io_functions I/O Function Types
@{
diff --git a/meson.build b/meson.build
index 90f59e9b..cdc35ffc 100644
--- a/meson.build
+++ b/meson.build
@@ -109,7 +109,10 @@ m_dep = cc.find_library('m', required: false)
# System defines needed to expose platform APIs
platform_args = []
if host_machine.system() != 'windows'
- platform_args += ['-D_POSIX_C_SOURCE=200809L']
+ platform_args += [
+ '-D_POSIX_C_SOURCE=200809L',
+ '-D_XOPEN_SOURCE=600',
+ ]
endif
# Determine library type and the flags needed to build it
diff --git a/src/system.c b/src/system.c
index c02f2d3c..61635591 100644
--- a/src/system.c
+++ b/src/system.c
@@ -16,10 +16,13 @@
#include "system.h"
+#include "serd/serd.h"
#include "serd_config.h"
#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN 1
# include <malloc.h>
+# include <windows.h>
#endif
#include <stdio.h>
@@ -85,3 +88,25 @@ serd_free_aligned(void* const ptr)
free(ptr);
#endif
}
+
+char*
+serd_canonical_path(const char* const path)
+{
+#ifdef _WIN32
+ const DWORD size = GetFullPathName(path, 0, NULL, NULL);
+ if (size == 0) {
+ return NULL;
+ }
+
+ char* const out = (char*)calloc(size, 1);
+ const DWORD ret = GetFullPathName(path, MAX_PATH, out, NULL);
+ if (ret == 0 || ret >= size) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+#else
+ return path ? realpath(path, NULL) : NULL;
+#endif
+}
diff --git a/test/.clang-tidy b/test/.clang-tidy
index 4e2b14eb..dd609218 100644
--- a/test/.clang-tidy
+++ b/test/.clang-tidy
@@ -6,6 +6,7 @@ Checks: >
-android-cloexec-fopen,
-bugprone-easily-swappable-parameters,
-bugprone-reserved-identifier,
+ -bugprone-suspicious-string-compare,
-clang-analyzer-nullability.NullabilityBase,
-clang-analyzer-nullability.NullableDereferenced,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
diff --git a/test/test_string.c b/test/test_string.c
index 472b464a..5bc304d1 100644
--- a/test/test_string.c
+++ b/test/test_string.c
@@ -18,6 +18,11 @@
#include "serd/serd.h"
+#if defined(_WIN32) && !defined(__MINGW32__)
+# include <io.h>
+# define mkstemp(pat) _mktemp(pat)
+#endif
+
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
@@ -48,11 +53,34 @@ test_strerror(void)
assert(!strcmp(msg, "Unknown error"));
}
+static void
+test_canonical_path(const char* const path)
+{
+ char* const canonical = serd_canonical_path(path);
+
+ assert(canonical);
+ assert(!strstr(canonical, "../"));
+
+#if defined(_WIN32)
+ assert(strlen(canonical) > 2);
+ assert(canonical[0] >= 'A' && canonical[0] <= 'Z');
+ assert(canonical[1] == ':');
+ assert(!strstr(canonical, "..\\"));
+#else
+ assert(canonical[0] == '/');
+#endif
+
+ serd_free(canonical);
+}
+
int
-main(void)
+main(int argc, char** argv)
{
+ (void)argc;
+
test_strlen();
test_strerror();
+ test_canonical_path(argv[0]);
printf("Success\n");
return 0;