urlmon: Implemented case when relative path begins with '/' in CoInternetCombineIUri.
diff --git a/dlls/urlmon/tests/uri.c b/dlls/urlmon/tests/uri.c
index 5a45632..7dd58d8 100644
--- a/dlls/urlmon/tests/uri.c
+++ b/dlls/urlmon/tests/uri.c
@@ -5693,7 +5693,7 @@
     },
     {   "http://google.com/path",0,
         "/test/../test/.././testing",Uri_CREATE_ALLOW_RELATIVE,
-        0,S_OK,TRUE,
+        0,S_OK,FALSE,
         {
             {"http://google.com/testing",S_OK},
             {"google.com",S_OK},
@@ -5720,7 +5720,7 @@
     },
     {   "http://google.com/path",0,
         "/test/../test/.././testing",Uri_CREATE_ALLOW_RELATIVE,
-        URL_DONT_SIMPLIFY,S_OK,TRUE,
+        URL_DONT_SIMPLIFY,S_OK,FALSE,
         {
             {"http://google.com:80/test/../test/.././testing",S_OK},
             {"google.com",S_OK},
@@ -5883,7 +5883,7 @@
     },
     {   "file:///c:/test/test",0,
         "/testing.mp3",Uri_CREATE_ALLOW_RELATIVE,
-        URL_FILE_USE_PATHURL,S_OK,TRUE,
+        URL_FILE_USE_PATHURL,S_OK,FALSE,
         {
             {"file://c:\\testing.mp3",S_OK},
             {"",S_FALSE},
@@ -5910,7 +5910,7 @@
     },
     {   "file:///c:/test/test",0,
         "/testing.mp3",Uri_CREATE_ALLOW_RELATIVE,
-        0,S_OK,TRUE,
+        0,S_OK,FALSE,
         {
             {"file:///c:/testing.mp3",S_OK},
             {"",S_FALSE},
@@ -5937,7 +5937,7 @@
     },
     {   "file://test.com/test/test",0,
         "/testing.mp3",Uri_CREATE_ALLOW_RELATIVE,
-        URL_FILE_USE_PATHURL,S_OK,TRUE,
+        URL_FILE_USE_PATHURL,S_OK,FALSE,
         {
             {"file://\\\\test.com\\testing.mp3",S_OK},
             {"test.com",S_OK},
@@ -6054,7 +6054,7 @@
     /* Windows validates the path component from the relative Uri. */
     {   "http://google.com/test",0,
         "/Te%XXst",Uri_CREATE_ALLOW_RELATIVE,
-        0,E_INVALIDARG,TRUE
+        0,E_INVALIDARG,FALSE
     },
     /* Windows doesn't validate the query from the relative Uri. */
     {   "http://google.com/test",0,
@@ -6115,7 +6115,7 @@
     /* Creates an IUri which contains an invalid dos path char. */
     {   "file:///c:/test",0,
         "/test<ing",Uri_CREATE_ALLOW_RELATIVE,
-        URL_FILE_USE_PATHURL,S_OK,TRUE,
+        URL_FILE_USE_PATHURL,S_OK,FALSE,
         {
             {"file://c:\\test<ing",S_OK},
             {"",S_FALSE},
@@ -6143,7 +6143,7 @@
     /* Appends the path after the drive letter (if any). */
     {   "file:///c:/test",0,
         "/c:/testing",Uri_CREATE_ALLOW_RELATIVE,
-        0,S_OK,TRUE,
+        0,S_OK,FALSE,
         {
             {"file:///c:/c:/testing",S_OK},
             {"",S_FALSE},
diff --git a/dlls/urlmon/uri.c b/dlls/urlmon/uri.c
index 9b25f69..7eaadb4 100644
--- a/dlls/urlmon/uri.c
+++ b/dlls/urlmon/uri.c
@@ -36,7 +36,8 @@
 #define SKIP_IP_FUTURE_CHECK            0x10
 #define IGNORE_PORT_DELIMITER           0x20
 
-#define RAW_URI_FORCE_PORT_DISP 0x1
+#define RAW_URI_FORCE_PORT_DISP     0x1
+#define RAW_URI_CONVERT_TO_DOS_PATH 0x2
 
 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
 
@@ -3935,6 +3936,28 @@
     return S_OK;
 }
 
+static void convert_to_dos_path(const WCHAR *path, DWORD path_len,
+                                WCHAR *output, DWORD *output_len)
+{
+    const WCHAR *ptr = path;
+
+    if(path_len > 3 && *ptr == '/' && is_drive_path(path+1))
+        /* Skip over the leading / before the drive path. */
+        ++ptr;
+
+    for(; ptr < path+path_len; ++ptr) {
+        if(*ptr == '/') {
+            if(output)
+                *output++ = '\\';
+            (*output_len)++;
+        } else {
+            if(output)
+                *output++ = *ptr;
+            (*output_len)++;
+        }
+    }
+}
+
 /* Generates a raw uri string using the parse_data. */
 static DWORD generate_raw_uri(const parse_data *data, BSTR uri, DWORD flags) {
     DWORD length = 0;
@@ -3954,6 +3977,18 @@
             uri[length+1] = '/';
         }
         length += 2;
+
+        /* Check if we need to add the "\\" before the host name
+         * of a UNC server name in a DOS path.
+         */
+        if(flags & RAW_URI_CONVERT_TO_DOS_PATH &&
+           data->scheme_type == URL_SCHEME_FILE && data->host) {
+            if(uri) {
+                uri[length] = '\\';
+                uri[length+1] = '\\';
+            }
+            length += 2;
+        }
     }
 
     if(data->username) {
@@ -4031,9 +4066,21 @@
     }
 
     if(data->path) {
-        if(uri)
-            memcpy(uri+length, data->path, data->path_len*sizeof(WCHAR));
-        length += data->path_len;
+        if(!data->is_opaque && data->scheme_type == URL_SCHEME_FILE &&
+           flags & RAW_URI_CONVERT_TO_DOS_PATH) {
+            DWORD len = 0;
+
+            if(uri)
+                convert_to_dos_path(data->path, data->path_len, uri+length, &len);
+            else
+                convert_to_dos_path(data->path, data->path_len, NULL, &len);
+
+            length += len;
+        } else {
+            if(uri)
+                memcpy(uri+length, data->path, data->path_len*sizeof(WCHAR));
+            length += data->path_len;
+        }
     }
 
     if(data->query) {
@@ -5672,6 +5719,7 @@
 
         *result = URI(ret);
     } else {
+        WCHAR *path = NULL;
         DWORD raw_flags = 0;
 
         if(base->scheme_start > -1) {
@@ -5705,7 +5753,7 @@
                 data.has_port = TRUE;
                 data.port_value = base->port;
             }
-        } else
+        } else if(base->scheme_type != URL_SCHEME_FILE)
             data.is_opaque = TRUE;
 
         if(relative->path_start == -1 || !relative->path_len) {
@@ -5729,9 +5777,78 @@
                 data.query_len = base->query_len;
             }
         } else {
-            FIXME("Path merging not implemented yet!\n");
-            *result = NULL;
-            return E_NOTIMPL;
+            DWORD path_offset = 0, path_len = 0;
+
+            if(relative->path_len && *(relative->canon_uri+relative->path_start) == '/') {
+                const WCHAR *ptr, **pptr;
+                WCHAR *tmp = NULL;
+                BOOL copy_drive_path = FALSE;
+
+                /* If the relative IUri's path starts with a '/', then we
+                 * don't use the base IUri's path. Unless the base IUri
+                 * is a file URI, in which case it uses the drive path of
+                 * the base IUri (if it has any) in the new path.
+                 */
+                if(base->scheme_type == URL_SCHEME_FILE) {
+                    if(base->path_len > 3 && *(base->canon_uri+base->path_start) == '/' &&
+                       is_drive_path(base->canon_uri+base->path_start+1)) {
+                        path_len += 3;
+                        copy_drive_path = TRUE;
+                    }
+                }
+
+                path_len += relative->path_len;
+
+                path = heap_alloc((path_len+1)*sizeof(WCHAR));
+                if(!path) {
+                    *result = NULL;
+                    return E_OUTOFMEMORY;
+                }
+
+                tmp = path;
+
+                /* Copy the base paths, drive path over. */
+                if(copy_drive_path) {
+                    memcpy(tmp, base->canon_uri+base->path_start, 3*sizeof(WCHAR));
+                    tmp += 3;
+                }
+
+                memcpy(tmp, relative->canon_uri+relative->path_start, relative->path_len*sizeof(WCHAR));
+                path[path_len] = '\0';
+
+                ptr = path;
+                pptr = &ptr;
+                if((data.is_opaque && !parse_path_opaque(pptr, &data, 0)) ||
+                   (!data.is_opaque && !parse_path_hierarchical(pptr, &data, 0))) {
+                    heap_free(path);
+                    *result = NULL;
+                    return E_INVALIDARG;
+                }
+            } else {
+                FIXME("Path merging not implemented yet!\n");
+                *result = NULL;
+                return E_NOTIMPL;
+            }
+
+            /* Check if the dot segments need to be removed from the path. */
+            if(!(flags & URL_DONT_SIMPLIFY) && !data.is_opaque) {
+                DWORD offset = (path_offset > 0) ? path_offset+1 : 0;
+                DWORD new_len = remove_dot_segments(path+offset,path_len-offset);
+
+                if(new_len != path_len) {
+                    WCHAR *tmp = heap_realloc(path, (new_len+1)*sizeof(WCHAR));
+                    if(!tmp) {
+                        heap_free(path);
+                        *result = NULL;
+                        return E_OUTOFMEMORY;
+                    }
+
+                    tmp[new_len] = '\0';
+                    data.path = tmp;
+                    data.path_len = new_len;
+                    path = tmp;
+                }
+            }
         }
 
         if(relative->fragment_start > -1) {
@@ -5741,10 +5858,13 @@
 
         if(flags & URL_DONT_SIMPLIFY)
             raw_flags |= RAW_URI_FORCE_PORT_DISP;
+        if(flags & URL_FILE_USE_PATHURL)
+            raw_flags |= RAW_URI_CONVERT_TO_DOS_PATH;
 
         len = generate_raw_uri(&data, data.uri, raw_flags);
         data.uri = SysAllocStringLen(NULL, len);
         if(!data.uri) {
+            heap_free(path);
             *result = NULL;
             return E_OUTOFMEMORY;
         }
@@ -5754,10 +5874,16 @@
         ret = create_uri_obj();
         if(!ret) {
             SysFreeString(data.uri);
+            heap_free(path);
             *result = NULL;
             return E_OUTOFMEMORY;
         }
 
+        if(flags & URL_DONT_SIMPLIFY)
+            create_flags |= Uri_CREATE_NO_CANONICALIZE;
+        if(flags & URL_FILE_USE_PATHURL)
+            create_flags |= Uri_CREATE_FILE_USE_DOS_PATH;
+
         ret->raw_uri = data.uri;
         hr = canonicalize_uri(&data, ret, create_flags);
         if(FAILED(hr)) {
@@ -5766,9 +5892,14 @@
             return hr;
         }
 
+        if(flags & URL_DONT_SIMPLIFY)
+            ret->display_modifiers |= URI_DISPLAY_NO_DEFAULT_PORT_AUTH;
+
         apply_default_flags(&create_flags);
         ret->create_flags = create_flags;
         *result = URI(ret);
+
+        heap_free(path);
     }
 
     return S_OK;