xref: /aoo4110/main/tools/source/misc/pathutils.cxx (revision b1cdbd2c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "sal/config.h"
25 
26 #if defined WNT
27 
28 #include <cstddef>
29 
30 #define WIN32_LEAN_AND_MEAN
31 #include <windows.h>
32 
33 #include "sal/types.h"
34 #include "tools/pathutils.hxx"
35 
36 namespace tools {
37 
filename(WCHAR * path)38 WCHAR * filename(WCHAR * path) {
39     WCHAR * f = path;
40     for (WCHAR * p = path;;) {
41         switch (*p++) {
42         case L'\0':
43             return f;
44         case L'\\':
45             f = p;
46             break;
47         }
48     }
49 }
50 
buildPath(WCHAR * path,WCHAR const * frontBegin,WCHAR const * frontEnd,WCHAR const * backBegin,std::size_t backLength)51 WCHAR * buildPath(
52     WCHAR * path, WCHAR const * frontBegin, WCHAR const * frontEnd,
53     WCHAR const * backBegin, std::size_t backLength)
54 {
55     // Remove leading ".." segments in the second path together with matching
56     // segments in the first path that are neither empty nor "." nor ".." nor
57     // end in ":" (which is not foolprove, as it can erroneously erase the start
58     // of a UNC path, but only if the input is bad data):
59     while (backLength >= 2 && backBegin[0] == L'.' && backBegin[1] == L'.' &&
60            (backLength == 2 || backBegin[2] == L'\\'))
61     {
62         if (frontEnd - frontBegin < 2 || frontEnd[-1] != L'\\' ||
63             frontEnd[-2] == L'\\' || frontEnd[-2] == L':' ||
64             (frontEnd[-2] == L'.' &&
65              (frontEnd - frontBegin < 3 || frontEnd[-3] == L'\\' ||
66               (frontEnd[-3] == L'.' &&
67                (frontEnd - frontBegin < 4 || frontEnd[-4] == L'\\')))))
68         {
69             break;
70         }
71         WCHAR const * p = frontEnd - 1;
72         while (p != frontBegin && p[-1] != L'\\') {
73             --p;
74         }
75         if (p == frontBegin) {
76             break;
77         }
78         frontEnd = p;
79         if (backLength == 2) {
80             backBegin += 2;
81             backLength -= 2;
82         } else {
83             backBegin += 3;
84             backLength -= 3;
85         }
86     }
87     if (backLength <
88         static_cast< std::size_t >(MAX_PATH - (frontEnd - frontBegin)))
89         // hopefully std::size_t is large enough
90     {
91         WCHAR * p;
92         if (frontBegin == path) {
93             p = const_cast< WCHAR * >(frontEnd);
94         } else {
95             p = path;
96             while (frontBegin != frontEnd) {
97                 *p++ = *frontBegin++;
98             }
99         }
100         for (; backLength > 0; --backLength) {
101             *p++ = *backBegin++;
102         }
103         *p = L'\0';
104         return p;
105     } else {
106         SetLastError(ERROR_FILENAME_EXCED_RANGE);
107         return NULL;
108     }
109 }
110 
resolveLink(WCHAR * path)111 WCHAR * resolveLink(WCHAR * path) {
112     HANDLE h = CreateFileW(
113         path, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
114     if (h == INVALID_HANDLE_VALUE) {
115         return NULL;
116     }
117     char p1[MAX_PATH];
118     DWORD n;
119     BOOL ok = ReadFile(h, p1, MAX_PATH, &n, NULL);
120     CloseHandle(h);
121     if (!ok) {
122         return NULL;
123     }
124     WCHAR p2[MAX_PATH];
125     std::size_t n2 = 0;
126     bool colon = false;
127     for (DWORD i = 0; i < n;) {
128         unsigned char c = static_cast< unsigned char >(p1[i++]);
129         switch (c) {
130         case '\0':
131             SetLastError(ERROR_BAD_PATHNAME);
132             return NULL;
133         case '\x0A':
134         case '\x0D':
135             if (n2 == MAX_PATH) {
136                 SetLastError(ERROR_FILENAME_EXCED_RANGE);
137                 return NULL;
138             }
139             p2[n2] = L'\0';
140             break;
141         case ':':
142             colon = true;
143             // fall through
144         default:
145             // Convert from UTF-8 to UTF-16:
146             if (c <= 0x7F) {
147                 p2[n2++] = c;
148             } else if (c >= 0xC2 && c <= 0xDF && i < n &&
149                        static_cast< unsigned char >(p1[i]) >= 0x80 &&
150                        static_cast< unsigned char >(p1[i]) <= 0xBF)
151             {
152                 p2[n2++] = ((c & 0x1F) << 6) |
153                     (static_cast< unsigned char >(p1[i++]) & 0x3F);
154             } else if (n - i > 1 &&
155                        ((c == 0xE0 &&
156                          static_cast< unsigned char >(p1[i]) >= 0xA0 &&
157                          static_cast< unsigned char >(p1[i]) <= 0xBF) ||
158                         ((c >= 0xE1 && c <= 0xEC || c >= 0xEE && c <= 0xEF) &&
159                          static_cast< unsigned char >(p1[i]) >= 0x80 &&
160                          static_cast< unsigned char >(p1[i]) <= 0xBF) ||
161                         (c == 0xED &&
162                          static_cast< unsigned char >(p1[i]) >= 0x80 &&
163                          static_cast< unsigned char >(p1[i]) <= 0x9F)) &&
164                        static_cast< unsigned char >(p1[i + 1]) >= 0x80 &&
165                        static_cast< unsigned char >(p1[i + 1]) <= 0xBF)
166             {
167                 p2[n2++] = ((c & 0x0F) << 12) |
168                     ((static_cast< unsigned char >(p1[i]) & 0x3F) << 6) |
169                     (static_cast< unsigned char >(p1[i + 1]) & 0x3F);
170                 i += 2;
171             } else if (n - 2 > 1 &&
172                        ((c == 0xF0 &&
173                          static_cast< unsigned char >(p1[i]) >= 0x90 &&
174                          static_cast< unsigned char >(p1[i]) <= 0xBF) ||
175                         (c >= 0xF1 && c <= 0xF3 &&
176                          static_cast< unsigned char >(p1[i]) >= 0x80 &&
177                          static_cast< unsigned char >(p1[i]) <= 0xBF) ||
178                         (c == 0xF4 &&
179                          static_cast< unsigned char >(p1[i]) >= 0x80 &&
180                          static_cast< unsigned char >(p1[i]) <= 0x8F)) &&
181                        static_cast< unsigned char >(p1[i + 1]) >= 0x80 &&
182                        static_cast< unsigned char >(p1[i + 1]) <= 0xBF &&
183                        static_cast< unsigned char >(p1[i + 2]) >= 0x80 &&
184                        static_cast< unsigned char >(p1[i + 2]) <= 0xBF)
185             {
186                 sal_Int32 u = ((c & 0x07) << 18) |
187                     ((static_cast< unsigned char >(p1[i]) & 0x3F) << 12) |
188                     ((static_cast< unsigned char >(p1[i + 1]) & 0x3F) << 6) |
189                     (static_cast< unsigned char >(p1[i + 2]) & 0x3F);
190                 i += 3;
191                 p2[n2++] = static_cast< WCHAR >(((u - 0x10000) >> 10) | 0xD800);
192                 p2[n2++] = static_cast< WCHAR >(
193                     ((u - 0x10000) & 0x3FF) | 0xDC00);
194             } else {
195                 SetLastError(ERROR_BAD_PATHNAME);
196                 return NULL;
197             }
198             break;
199         }
200     }
201     WCHAR * end;
202     if (colon || p2[0] == L'\\') {
203         // Interpret p2 as an absolute path:
204         end = path;
205     } else {
206         // Interpret p2 as a relative path:
207         end = filename(path);
208     }
209     return buildPath(path, path, end, p2, n2);
210 }
211 
212 }
213 
214 #endif
215