diff options
author | pommicket <pommicket@gmail.com> | 2023-01-11 17:34:27 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2023-01-11 17:34:27 -0500 |
commit | b983ee158ace30f836ec25c4d6a6426281e5700c (patch) | |
tree | d326bf2a5ba77d8ac9a6dd47367ef29fc8a7f3e1 | |
parent | b0fe15647d196081d801db574fa074ff77e46f5f (diff) |
URI %-escape sequences
-rw-r--r-- | lsp-parse.c | 31 | ||||
-rw-r--r-- | lsp-write.c | 25 |
2 files changed, 51 insertions, 5 deletions
diff --git a/lsp-parse.c b/lsp-parse.c index fd07913..6cb7cd4 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -104,13 +104,36 @@ static bool parse_document_uri(LSP *lsp, const JSON *json, JSONValue value, LSPD char *path; #if _WIN32 path = string + strlen("file:///"); - // replace slashes with backslashes - for (char *p = path; *p; ++p) - if (*p == '/') - *p = '\\'; #else path = string + strlen("file://"); #endif + + // replace percent-encoded sequences (e.g. replace %20 with ' ') + char *out = path; + for (const char *in = path; *in; ) { + char c = *in++; + if (c == '%') { + char sequence[3] = {0}; + if (!in[0] || !in[1] || !isxdigit(in[0]) || !isxdigit(in[1])) { + lsp_set_error(lsp, "Bad escape sequence in URI."); + free(string); + return false; + } + sequence[0] = in[0]; + sequence[1] = in[1]; + in += 2; + long byte = strtol(sequence, NULL, 16); + assert(byte >= 0 && byte <= 255); + c = (char)byte; + } + #if _WIN32 + // replace forward slashes with backslashes for consistency + if (c == '/') c = '\\'; + #endif + *out++ = c; + } + *out = '\0'; + *id = lsp_document_id(lsp, path); free(string); return true; diff --git a/lsp-write.c b/lsp-write.c index 8472190..19bd766 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -188,7 +188,30 @@ static void write_file_uri(JSONWriter *o, LSPDocumentID document) { // why the fuck is there another slash it makes no goddamn sense str_builder_append(&o->builder, "/"); #endif - write_escaped(o, path); + for (const char *p = path; *p; ++p) { + char c = *p; + #if _WIN32 + // i think file URIs have to use slashes? + if (c == '\\') c = '/'; + #endif + + // see https://www.rfc-editor.org/rfc/rfc3986#page-12 + // these are the only allowed un-escaped characters in URIs + bool escaped = !( + (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '_' || c == '-' || c == '.' || c == '~' || c == '/' + #if _WIN32 + || c == ':' // i dont think you're supposed to escape the : in C:\... + #endif + ); + if (escaped) { + str_builder_appendf(&o->builder, "%%%02x", (uint8_t)c); + } else { + str_builder_appendf(&o->builder, "%c", c); + } + } str_builder_append(&o->builder, "\""); } |