diff options
-rw-r--r-- | include/serd/serd.h | 13 | ||||
-rw-r--r-- | meson.build | 5 | ||||
-rw-r--r-- | src/system.c | 25 | ||||
-rw-r--r-- | test/.clang-tidy | 1 | ||||
-rw-r--r-- | test/test_string.c | 30 |
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; |