diff -r 4204db81cdbc -r c89e5edb8eae librazor/path.c --- a/librazor/path.c Thu Jul 07 15:17:29 2016 +0100 +++ b/librazor/path.c Thu Jun 07 18:36:20 2018 +0100 @@ -159,41 +159,129 @@ RAZOR_EXPORT char *razor_path_to_uri(const char *path) { - char *uri, *p; + char *uri, *s; + const char *p; + int check_dotdot, len; + struct razor_uri ru; - uri = malloc(6 + 3 * strlen(path) + 1); + uri = malloc(5 + (4 - 3) + 4 + 3 * strlen(path) + 1); strcpy(uri, "file:"); - p = uri + 5; + s = uri + 5; + +#ifdef MSWIN_API + check_dotdot = path[0] != '/' && path[0] != '\\'; +#else + check_dotdot = path[0] != '/'; +#endif + + p = path; #ifdef MSWIN_API /* * Under MS-Windows, c:/xxx maps to a path of /c:/xxx + * Relative paths that include a drive letter (eg., c:xxx) + * can't be handled directly and have to be converted + * to absolute form. */ - if (is_alpha(path[0]) && path[1] == ':' && - (path[2] == '/' || path[2] == '\\')) - *p++ = '/'; + if (is_alpha(p[0]) && p[1] == ':') { + if (p[2] == '/' || p[2] == '\\') { + *s++ = '/'; + *s++ = p[0]; + *s++ = ':'; + *s++ = '/'; + p += 3; + /* + * We need to take care that ".." segments don't remove + * the drive letter (eg., c:/../xxx -> file:/c:/../xxx + * which normalizes to file:/xxx). + */ + check_dotdot = 2; + } else { + s = razor_abspath(p); + uri = razor_path_to_uri(s); + free(s); + return uri; + } + } #endif - while(*path) { - if (*path == '/' || is_unreserved(*path) || - is_sub_delim(*path) || *path == ':' || *path == '@') - *p++ = *path; + /* + * Relative paths are complicated. URIs can't have dot segments + * so these will be removed during normalization. That often does + * the right thing, but where a relative path traverses up the + * tree then the result is a URI that points to somewhere quite + * different to path: eg., file:../dir normalizes to file:dir + * We solve this by inserting a sentinel segment at the beginning. + * If the segment is still present after normalization, then it + * can just be removed. If it is missing, then we need to create + * an absolute path and redo the conversion. + */ + if (check_dotdot) { + *s++ = '%'; + *s++ = '2'; + *s++ = 'F'; + *s++ = '/'; + } + + while(*p) { + if (*p == '/' || is_unreserved(*p) || is_sub_delim(*p) || + *p == ':' || *p == '@') + *s++ = *p; #ifdef MSWIN_API - else if (*path == '\\') - *p++ = '/'; + else if (*p == '\\') + *s++ = '/'; #endif else { - *p++ = '%'; - *p++ = "0123456789ABCDEF"[(*(unsigned char *)path)/16]; - *p++ = "0123456789ABCDEF"[(*(unsigned char *)path)%16]; + *s++ = '%'; + *s++ = "0123456789ABCDEF"[(*(unsigned char *)p)/16]; + *s++ = "0123456789ABCDEF"[(*(unsigned char *)p)%16]; } - path++; + p++; } - *p++ = '\0'; + *s++ = '\0'; - return realloc(uri, p - uri); + if (razor_uri_parse(&ru, uri, NULL) < 0) { + free(uri); + return NULL; + } + free(uri); + + razor_uri_normalize(&ru); + + uri = razor_uri_recompose(&ru); + + razor_uri_destroy(&ru); + + if (check_dotdot == 2) { + s = strdup("file:/x:/%2F/"); + s[6] = path[0]; + if (str_has_prefix(uri, s)) { + free(s); + memmove(uri + 5 + 3, uri + 9 + 3, + strlen(uri + 9 + 3) + 1); + uri = realloc(uri, strlen(uri) + 1); + } else { + free(s); + free(uri); + s = razor_abspath(path); + uri = razor_path_to_uri(s); + free(s); + } + } else if (check_dotdot) { + if (str_has_prefix(uri, "file:%2F/")) { + memmove(uri + 5, uri + 9, strlen(uri + 9) + 1); + uri = realloc(uri, strlen(uri) + 1); + } else { + free(uri); + s = razor_abspath(path); + uri = razor_path_to_uri(s); + free(s); + } + } + + return uri; } RAZOR_EXPORT char *