diff options
| author | Thomas Holder <thomas@thomas-holder.de> | 2018-11-05 21:12:05 +0000 |
|---|---|---|
| committer | Thomas Holder <thomas@thomas-holder.de> | 2018-11-05 22:35:47 +0000 |
| commit | 18f8137798ccf99c1f275fd1f6e2247e1e3e45ed (patch) | |
| tree | 31208ec2f2d8f7281a49c7a29139ceed4ea94df9 /src/object | |
| parent | fix URI::from_dirname("/") (diff) | |
| download | inkscape-18f8137798ccf99c1f275fd1f6e2247e1e3e45ed.tar.gz inkscape-18f8137798ccf99c1f275fd1f6e2247e1e3e45ed.zip | |
Replacement for xmlBuildRelativeURI
- xmlBuildRelativeURI has a bug
- we add a special case: Don't cross filesystem root
Diffstat (limited to 'src/object')
| -rw-r--r-- | src/object/uri.cpp | 73 |
1 files changed, 62 insertions, 11 deletions
diff --git a/src/object/uri.cpp b/src/object/uri.cpp index 7c5ab657e..75d7cf954 100644 --- a/src/object/uri.cpp +++ b/src/object/uri.cpp @@ -320,22 +320,73 @@ gchar *URI::Impl::toString() const { } } +/** + * Replacement for buggy xmlBuildRelativeURI + * https://gitlab.gnome.org/GNOME/libxml2/merge_requests/12 + * + * Special case: Don't cross filesystem root, e.g. drive letter on Windows. + * This is an optimization to keep things practical, it's not required for correctness. + * + * @param uri an absolute URI + * @param base an absolute URI without any ".." path segments + * @return relative URI if possible, otherwise @a uri unchanged + */ +static std::string build_relative_uri(char const *uri, char const *base) +{ + size_t n_slash = 0; + size_t i = 0; + + // find longest common prefix + for (; uri[i]; ++i) { + if (uri[i] != base[i]) { + break; + } + + if (uri[i] == '/') { + ++n_slash; + } + } + + // URIs must share protocol://server/ + if (n_slash < 3) { + return uri; + } + + // Don't cross filesystem root + if (n_slash == 3 && g_str_has_prefix(base, "file:///") && base[8]) { + return uri; + } + + std::string relative; + + for (size_t j = i; base[j]; ++j) { + if (base[j] == '/') { + relative += "../"; + } + } + + while (uri[i - 1] != '/') { + --i; + } + + relative += (uri + i); + + if (relative.empty() && base[i]) { + relative = "./"; + } + + return relative; +} + std::string URI::str(char const *baseuri) const { std::string s; gchar *save = _impl->toString(); if (save) { - xmlChar *rel = nullptr; - const char *latest = save; - if (baseuri) { - rel = xmlBuildRelativeURI((xmlChar *)save, (xmlChar *)baseuri); - if (rel) { - latest = (const char *)rel; - } - } - s = latest; - if (rel) { - xmlFree(rel); + if (baseuri && baseuri[0]) { + s = build_relative_uri(save, baseuri); + } else { + s = save; } g_free(save); } |
