From 8f1192bb2361fcd7907b926e86c41a171ed04ad0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 10 Nov 2020 09:45:15 +0100 Subject: Add serd_new_real_file_uri() --- include/serd/serd.h | 16 ++++++++++++++++ src/node.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ test/.clang-tidy | 4 ++++ test/test_uri.c | 26 +++++++++++++++++++++++++- 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/include/serd/serd.h b/include/serd/serd.h index a2e767ff..dd165360 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -673,6 +673,22 @@ SERD_API SerdNode* SERD_ALLOCATED serd_new_boolean(bool b); +/** + Create a new file URI node for a file that exists on this system. + + This is like serd_new_file_uri() except it resolves and canonicalizes the + path, so the returned node is always a complete file URI with a scheme and + absolute path that does not contain any dot references or links, or NULL if + this is impossible (for example, because the path does not exist). + + This should be used wherever the URI for an existent file is required, for + example to set the base URI of a document. +*/ +SERD_API +SerdNode* SERD_ALLOCATED +serd_new_real_file_uri(const char* SERD_NULLABLE path, + const char* SERD_NULLABLE hostname); + /** Create a new node by serialising `d` into an xsd:decimal string diff --git a/src/node.c b/src/node.c index 566e8839..76c12235 100644 --- a/src/node.c +++ b/src/node.c @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define _XOPEN_SOURCE 600 /* for realpath */ + #include "node.h" #include "namespaces.h" @@ -24,6 +26,11 @@ #include "exess/exess.h" #include "serd/serd.h" +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +#endif + #include #include #include @@ -539,6 +546,32 @@ is_uri_path_char(const char c) } } +static char* +serd_realpath(const char* const path) +{ + if (!path) { + return NULL; + } + +#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 realpath(path, NULL); +#endif +} + SerdNode* serd_new_file_uri(const SerdStringView path, const SerdStringView hostname) { @@ -586,6 +619,20 @@ serd_new_file_uri(const SerdStringView path, const SerdStringView hostname) return node; } +SerdNode* +serd_new_real_file_uri(const char* const path, const char* const hostname) +{ + char* const real_path = serd_realpath(path); + if (!real_path) { + return NULL; + } + + SerdNode* const node = serd_new_file_uri(SERD_MEASURE_STRING(real_path), + SERD_MEASURE_STRING(hostname)); + free(real_path); + return node; +} + typedef size_t (*SerdWriteLiteralFunc)(const void* const user_data, const size_t buf_size, char* const buf); diff --git a/test/.clang-tidy b/test/.clang-tidy index de654d8d..b7dd752b 100644 --- a/test/.clang-tidy +++ b/test/.clang-tidy @@ -3,6 +3,10 @@ Checks: > -*-magic-numbers, -*-uppercase-literal-suffix, -android-cloexec-fopen, + -bugprone-reserved-identifier, + -bugprone-suspicious-string-compare, + -cert-dcl37-c, + -cert-dcl51-cpp, -clang-analyzer-nullability.NullabilityBase, -clang-analyzer-nullability.NullableDereferenced, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, diff --git a/test/test_uri.c b/test/test_uri.c index 88aa342c..dff4d999 100644 --- a/test/test_uri.c +++ b/test/test_uri.c @@ -20,6 +20,11 @@ #include "serd/serd.h" +#if defined(_WIN32) && !defined(__MINGW32__) +# include +# define mkstemp(pat) _mktemp(pat) +#endif + #include #include #include @@ -79,6 +84,24 @@ test_uri_parsing(void) serd_free(out_path); } +static void +test_real_file_uri(void) +{ + char path[16]; + strncpy(path, "serd_XXXXXX", sizeof(path)); + assert(mkstemp(path) > 0); + + SerdNode* const good = serd_new_real_file_uri(path, NULL); + assert(good); + assert(!strncmp(serd_node_string(good), "file://", 7)); + serd_node_free(good); + + SerdNode* const bad = serd_new_real_file_uri("not_a_file", NULL); + assert(!bad); + + assert(!remove(path)); +} + static void test_parse_uri(void) { @@ -204,7 +227,7 @@ test_uri_resolution(void) const SerdURIView rel_foo_uri = serd_relative_uri(abs_foo_uri, base_uri); const SerdURIView resolved_uri = serd_resolve_uri(rel_foo_uri, base_uri); -SerdNode* const resolved = serd_new_parsed_uri(resolved_uri); + SerdNode* const resolved = serd_new_parsed_uri(resolved_uri); assert(!strcmp(serd_node_string(resolved), "http://example.org/a/b/c/foo")); serd_node_free(resolved); @@ -215,6 +238,7 @@ main(void) { test_uri_parsing(); test_parse_uri(); + test_real_file_uri(); test_is_within(); test_relative_uri(); test_uri_resolution(); -- cgit v1.2.1