--- /dev/null
+/*
+ * Copyright (C) 2014 J. Ali Harlow <ali@juiblex.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include "razor.h"
+#include "razor-internal.h"
+
+/**
+ * razor_path_add_root:
+ *
+ * Adds a root to a path. path must be an absolute pathname. In POSIX
+ * environments this is equivalent to the concationation of root and path.
+ * In Microsoft Windows an adjustment may need to be made for a drive letter
+ * in path (which will be dropped).
+ *
+ * Returns: The new pathname.
+ **/
+RAZOR_EXPORT char *razor_path_add_root(const char *path, const char *root)
+{
+ if (root && *root)
+ return razor_concat(root, SKIP_DRIVE_LETTER(path), NULL);
+ else
+ return strdup(path);
+}
+
+#if 0
+
+/*
+ * This should work, but for some reason PathCreateFromUrlW()
+ * treats the percent-encoded bytes as being in CP 850 rather
+ * than UTF-8 as expected even if we set the codepage.
+ */
+RAZOR_EXPORT char *razor_path_from_url(const char *url)
+{
+ UINT saved_cp;
+ HRESULT result;
+ DWORD len = MAX_PATH;
+ wchar_t *url16;
+ wchar_t path16[MAX_PATH];
+ char *path;
+
+ url16 = razor_utf8_to_utf16(url, -1);
+
+ saved_cp = GetConsoleCP();
+ SetConsoleCP(CP_UTF8);
+
+ result = PathCreateFromUrlW(url16, path16, &len, NULL);
+
+ SetConsoleCP(saved_cp);
+
+ if (result == S_OK)
+ path = razor_utf16_to_utf8(path16, len);
+ else
+ path = NULL;
+
+ free(url16);
+
+ return path;
+}
+
+#else
+
+#ifdef MSWIN_API
+static int is_ascii_letter(char c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+#endif
+
+static int xdigit_value(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else
+ return (c | 0x20) - 'a' + 10;
+}
+
+static int valid_unicode(unsigned unicode)
+{
+ /*
+ * Within the U+0000..U+10FFFF range defined by RFC3629
+ * but not in the U+D800..U+DFFF range prohibited in UTF-8.
+ */
+ return unicode < 0xD800 || (unicode >= 0xE000 && unicode < 0x110000);
+}
+
+RAZOR_EXPORT char *razor_path_from_url(const char *url)
+{
+ int continuation_bytes = 0;
+ char *path, *p;
+ unsigned char c;
+ unsigned unicode;
+
+ if (strncmp(url, "file://", 7) == 0)
+ url += 7;
+ else
+ return NULL;
+
+ if (strncmp(url, "localhost/", 10) == 0)
+ url += 9;
+ else if (strncmp(url, "/", 1) != 0)
+ return NULL;
+
+#ifdef MSWIN_API
+ /*
+ * Under MS-Windows, file:///c:/xxx maps to c:/xxx
+ * Note that PathCreateFromUrl converts / to \ as well.
+ */
+ if (is_ascii_letter(url[1]) && url[2] == ':' && url[3] == '/')
+ url++;
+#endif
+
+ p = path = malloc(strlen(url) + 1);
+
+ while(*url) {
+ if (*url >= 0x7F || *url < 0x20) {
+ free(path);
+ return NULL;
+ } else if (*url != '%') {
+ if (continuation_bytes) {
+ free(path);
+ return NULL;
+ } else
+ *p++ = *url++;
+ } else if (isxdigit(url[1]) && isxdigit(url[2])) {
+ c = xdigit_value(url[1]) * 16 + xdigit_value(url[2]);
+ if (c == '/') {
+ free(path);
+ return NULL;
+ } else if (!continuation_bytes) {
+ if (c >= 0xF5 || c == 0xC0 || c == 0xC1) {
+ free(path);
+ return NULL;
+ } else if (c >= 0xF0) {
+ unicode = c & 7;
+ continuation_bytes = 3;
+ } else if (c >= 0xE0) {
+ unicode = c & 3;
+ continuation_bytes = 2;
+ } else if (c >= 0xC0) {
+ unicode = c & 1;
+ continuation_bytes = 1;
+ }
+ } else if ((c & 0xC0) != 0x80) {
+ free(path);
+ return NULL;
+ } else {
+ unicode <<= 6;
+ unicode |= (c & 0x3F);
+
+ if (!--continuation_bytes &&
+ !valid_unicode(unicode)) {
+ free(path);
+ return NULL;
+ }
+ }
+
+ *p++ = c;
+ url += 3;
+ } else {
+ free(path);
+ return NULL;
+ }
+ }
+
+ if (continuation_bytes) {
+ free(path);
+ return NULL;
+ }
+
+ *p++ = '\0';
+
+ return realloc(path, p - path);
+}
+
+#endif /* 0 */
--- /dev/null
+/*
+ * Copyright (C) 2014 J. Ali Harlow <ali@juiblex.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef MSWIN_API
+#include <windows.h>
+#endif
+#include "razor.h"
+
+int is_ascii_letter(char c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+int is_slash(char c)
+{
+ return c == '/' || c == '\\';
+}
+
+const char *mswin_path(const char *path)
+{
+ if (path == NULL)
+ return NULL;
+
+ if (path[0] == '/' && is_ascii_letter(path[1]) && path[2] == ':' &&
+ path[3] == '/')
+ path++;
+
+ return path;
+}
+
+int path_cmp(const char *p1, const char *p2)
+{
+#ifdef MSWIN_API
+ while(*p1 && *p2) {
+ if (*p1 == *p2 || is_slash(*p1) && is_slash(*p2)) {
+ p1++;
+ p2++;
+ } else
+ break;
+ }
+
+ return *p1 || *p2;
+#else
+ return strcmp(p1, p2);
+#endif
+}
+
+int test_pfu(const char *url, const char *path)
+{
+ char *s;
+ int r;
+
+ s = razor_path_from_url(url);
+
+#ifdef MSWIN_API
+ path = mswin_path(path);
+#endif
+
+ if (s && path)
+ r = path_cmp(s, path);
+ else
+ r = (s != path);
+
+ if (r) {
+ fprintf(stderr, "Fail: razor_path_from_url(\"%s\")", url);
+ if (s)
+ fprintf(stderr, " returns \"%s\", expected", s);
+ else
+ fprintf(stderr, " returns NULL, expected");
+ if (path)
+ fprintf(stderr, " \"%s\"\n", path);
+ else
+ fprintf(stderr, " NULL\n");
+ }
+
+ free(s);
+
+ return r;
+}
+
+#ifdef MSWIN_API
+UINT saved_cp;
+
+void cleanup_on_exit(void)
+{
+ SetConsoleOutputCP(saved_cp);
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+ int r = 0;
+
+#ifdef MSWIN_API
+ atexit(cleanup_on_exit);
+ saved_cp = GetConsoleOutputCP();
+ SetConsoleOutputCP(CP_UTF8);
+#endif
+
+ r |= test_pfu("file://localhost/etc/fstab", "/etc/fstab");
+ r |= test_pfu("file:///etc/fstab", "/etc/fstab");
+ r |= test_pfu("file://localhost/c:/WINDOWS/clock.avi",
+ "/c:/WINDOWS/clock.avi");
+ r |= test_pfu("file:///c:/WINDOWS/clock.avi",
+ "/c:/WINDOWS/clock.avi");
+ r |= test_pfu("file:///path/to/the%20file.txt",
+ "/path/to/the file.txt");
+ r |= test_pfu("file:///home/s%C3%A9bastien", "/home/sébastien");
+ r |= test_pfu("file:///home/luk%C3%A1%C5%A1", "/home/lukáš");
+ r |= test_pfu("file:///var/log/22%20%e0%b8%aa%e0%b8%b4%e0%b8%87%e0%b8%ab%e0%b8%b2%e0%b8%84%e0%b8%a1%202014",
+ "/var/log/22 สิงหาคม 2014");
+
+ exit(r ? 1 : 0);
+}