VirtualBox

source: kBuild/trunk/src/kmk/w32/pathstuff.c@ 1127

Last change on this file since 1127 was 1127, checked in by bird, 17 years ago

Use NtQueryInformationFile + FileNameInformation to figure out the correctly cased names.

  • Property svn:eol-style set to native
File size: 18.6 KB
Line 
1/* Path conversion for Windows pathnames.
2Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
32006 Free Software Foundation, Inc.
4This file is part of GNU Make.
5
6GNU Make is free software; you can redistribute it and/or modify it under the
7terms of the GNU General Public License as published by the Free Software
8Foundation; either version 2, or (at your option) any later version.
9
10GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15GNU Make; see the file COPYING. If not, write to the Free Software
16Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
17
18#include <Windows.h> /* bird */
19#include <stdio.h> /* bird */
20#include <string.h>
21#include <stdlib.h>
22#include "make.h"
23#include "pathstuff.h"
24
25/*
26 * Convert delimiter separated vpath to Canonical format.
27 */
28char *
29convert_vpath_to_windows32(char *Path, char to_delim)
30{
31 char *etok; /* token separator for old Path */
32
33 /*
34 * Convert all spaces to delimiters. Note that pathnames which
35 * contain blanks get trounced here. Use 8.3 format as a workaround.
36 */
37 for (etok = Path; etok && *etok; etok++)
38 if (isblank ((unsigned char) *etok))
39 *etok = to_delim;
40
41 return (convert_Path_to_windows32(Path, to_delim));
42}
43
44/*
45 * Convert delimiter separated path to Canonical format.
46 */
47char *
48convert_Path_to_windows32(char *Path, char to_delim)
49{
50 char *etok; /* token separator for old Path */
51 char *p; /* points to element of old Path */
52
53 /* is this a multi-element Path ? */
54 for (p = Path, etok = strpbrk(p, ":;");
55 etok;
56 etok = strpbrk(p, ":;"))
57 if ((etok - p) == 1) {
58 if (*(etok - 1) == ';' ||
59 *(etok - 1) == ':') {
60 etok[-1] = to_delim;
61 etok[0] = to_delim;
62 p = ++etok;
63 continue; /* ignore empty bucket */
64 } else if (!isalpha ((unsigned char) *p)) {
65 /* found one to count, handle things like '.' */
66 *etok = to_delim;
67 p = ++etok;
68 } else if ((*etok == ':') && (etok = strpbrk(etok+1, ":;"))) {
69 /* found one to count, handle drive letter */
70 *etok = to_delim;
71 p = ++etok;
72 } else
73 /* all finished, force abort */
74 p += strlen(p);
75 } else {
76 /* found another one, no drive letter */
77 *etok = to_delim;
78 p = ++etok;
79 }
80
81 return Path;
82}
83
84#if 1 /* bird */
85/*
86 * Corrects the case of a path.
87 * Expects a fullpath!
88 * Added by bird for the $(abspath ) function and w32ify
89 */
90void
91w32_fixcase(char *pszPath)
92{
93 static char s_szLast[260];
94 unsigned cchLast;
95
96#ifndef NDEBUG
97# define my_assert(expr) \
98 do { \
99 if (!(expr)) { \
100 printf("my_assert: %s, file %s, line %d\npszPath=%s\npsz=%s\n", \
101 #expr, __FILE__, __LINE__, pszPath, psz); \
102 __debugbreak(); \
103 exit(1); \
104 } \
105 } while (0)
106#else
107# define my_assert(expr) do {} while (0)
108#endif
109
110 char *psz = pszPath;
111 if (*psz == '/' || *psz == '\\')
112 {
113 if (psz[1] == '/' || psz[1] == '\\')
114 {
115 /* UNC */
116 my_assert(psz[1] == '/' || psz[1] == '\\');
117 my_assert(psz[2] != '/' && psz[2] != '\\');
118
119 /* skip server name */
120 psz += 2;
121 while (*psz != '\\' && *psz != '/')
122 {
123 if (!*psz)
124 return;
125 *psz++ = toupper(*psz);
126 }
127
128 /* skip the share name */
129 psz++;
130 my_assert(*psz != '/' && *psz != '\\');
131 while (*psz != '\\' && *psz != '/')
132 {
133 if (!*psz)
134 return;
135 *psz++ = toupper(*psz);
136 }
137 my_assert(*psz == '/' || *psz == '\\');
138 psz++;
139 }
140 else
141 {
142 /* Unix spec */
143 psz++;
144 }
145 }
146 else
147 {
148 /* Drive letter */
149 my_assert(psz[1] == ':');
150 *psz = toupper(*psz);
151 my_assert(psz[0] >= 'A' && psz[0] <= 'Z');
152 my_assert(psz[2] == '/' || psz[2] == '\\');
153 psz += 3;
154 }
155
156 /*
157 * Try make use of the result from the previous call.
158 * This is ignorant to slashes and similar, but may help even so.
159 */
160 if ( s_szLast[0] == pszPath[0]
161 && (psz - pszPath == 1 || s_szLast[1] == pszPath[1])
162 && (psz - pszPath <= 2 || s_szLast[2] == pszPath[2])
163 )
164 {
165 char *pszLast = &s_szLast[psz - pszPath];
166 char *pszCur = psz;
167 for (;;)
168 {
169 const char ch1 = *pszCur;
170 const char ch2 = *pszLast;
171 if ( ch1 != ch2
172 && (ch1 != '\\' || ch2 != '/')
173 && (ch1 != '/' || ch2 != '\\'))
174 {
175 if ( tolower(ch1) != tolower(ch2)
176 && toupper(ch1) != toupper(ch2))
177 break;
178 /* optimistic, component mismatch will be corrected in the next loop. */
179 *pszCur = ch2;
180 }
181 if (ch1 == '/' || ch1 == '\\')
182 psz = pszCur + 1;
183 else if (ch1 == '\0')
184 {
185 psz = pszCur;
186 break;
187 }
188 pszCur++;
189 pszLast++;
190 }
191 }
192
193 /*
194 * Pointing to the first char after the unc or drive specifier,
195 * or in case of a cache hit, the first non-matching char (following a slash of course).
196 */
197 while (*psz)
198 {
199 WIN32_FIND_DATA FindFileData;
200 HANDLE hDir;
201 char chSaved0;
202 char chSaved1;
203 char *pszEnd;
204 int iLongNameDiff;
205
206
207 /* find the end of the component. */
208 pszEnd = psz;
209 while (*pszEnd && *pszEnd != '/' && *pszEnd != '\\')
210 pszEnd++;
211
212 /* replace the end with "?\0" */
213 chSaved0 = pszEnd[0];
214 chSaved1 = pszEnd[1];
215 pszEnd[0] = '?';
216 pszEnd[1] = '\0';
217
218 /* find the right filename. */
219 hDir = FindFirstFile(pszPath, &FindFileData);
220 pszEnd[1] = chSaved1;
221 if (!hDir)
222 {
223 cchLast = psz - pszPath;
224 memcpy(s_szLast, pszPath, cchLast + 1);
225 pszEnd[0] = chSaved0;
226 return;
227 }
228 pszEnd[0] = '\0';
229 while ( (iLongNameDiff = stricmp(FindFileData.cFileName, psz))
230 && stricmp(FindFileData.cAlternateFileName, psz))
231 {
232 if (!FindNextFile(hDir, &FindFileData))
233 {
234 cchLast = psz - pszPath;
235 memcpy(s_szLast, pszPath, cchLast + 1);
236 pszEnd[0] = chSaved0;
237 return;
238 }
239 }
240 strcpy(psz, !iLongNameDiff ? FindFileData.cFileName : FindFileData.cAlternateFileName);
241 pszEnd[0] = chSaved0;
242 FindClose(hDir);
243
244 /* advance to the next component */
245 if (!chSaved0)
246 {
247 psz = pszEnd;
248 break;
249 }
250 psz = pszEnd + 1;
251 my_assert(*psz != '/' && *psz != '\\');
252 }
253
254 /* *psz == '\0', the end. */
255 cchLast = psz - pszPath;
256 memcpy(s_szLast, pszPath, cchLast + 1);
257#undef my_assert
258}
259
260#define MY_FileNameInformation 9
261
262typedef struct _MY_FILE_NAME_INFORMATION
263{
264 ULONG FileNameLength;
265 WCHAR FileName[1];
266} MY_FILE_NAME_INFORMATION, *PMY_FILE_NAME_INFORMATION;
267
268typedef struct _IO_STATUS_BLOCK
269{
270 union
271 {
272 LONG Status;
273 PVOID Pointer;
274 };
275 ULONG_PTR Information;
276} MY_IO_STATUS_BLOCK, *PMY_IO_STATUS_BLOCK;
277
278static BOOL g_fInitialized = FALSE;
279static LONG (NTAPI *g_pfnNtQueryInformationFile)(HANDLE FileHandle,
280 PMY_IO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
281 ULONG Length, ULONG FileInformationClass);
282
283
284int
285nt_get_filename_info(const char *pszPath, char *pszFull, size_t cchFull)
286{
287 static char abBuf[8192];
288 PMY_FILE_NAME_INFORMATION pFileNameInfo = (PMY_FILE_NAME_INFORMATION)abBuf;
289 MY_IO_STATUS_BLOCK Ios;
290 LONG rcNt;
291 HANDLE hFile;
292
293 /*
294 * Check for NtQueryInformationFile the first time around.
295 */
296 if (!g_fInitialized)
297 {
298 g_fInitialized = TRUE;
299 if (!getenv("KMK_DONT_USE_NT_QUERY_INFORMATION_FILE"))
300 *(FARPROC *)&g_pfnNtQueryInformationFile =
301 GetProcAddress(LoadLibrary("ntdll.dll"), "NtQueryInformationFile");
302 }
303 if (!g_pfnNtQueryInformationFile)
304 return -1;
305
306 /*
307 * Try open the path and query its file name information.
308 */
309 hFile = CreateFile(pszPath,
310 GENERIC_READ,
311 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
312 NULL,
313 OPEN_EXISTING,
314 FILE_FLAG_BACKUP_SEMANTICS,
315 NULL);
316 if (hFile)
317 {
318 memset(&Ios, 0, sizeof(Ios));
319 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, abBuf, sizeof(abBuf), MY_FileNameInformation);
320 CloseHandle(hFile);
321 if (rcNt >= 0)
322 {
323 /*
324 * The FileNameInformation we get is relative to where the volume is mounted,
325 * so we have to extract the driveletter prefix ourselves.
326 *
327 * FIXME: This will probably not work for volumes mounted in NTFS sub-directories.
328 */
329 int cchOut;
330 int fUnc = 0;
331 char *psz = pszFull;
332 if (pszPath[0] == '\\' || pszPath[0] == '/')
333 {
334 /* unc or root of volume */
335 if ( (pszPath[1] == '\\' || pszPath[1] == '/')
336 && (pszPath[2] != '\\' || pszPath[2] == '/'))
337 {
338 /* unc - we get the server + name back */
339 *psz++ = '\\';
340 fUnc = 1;
341 }
342 else
343 {
344 /* root slash */
345 *psz++ = _getdrive() + 'A' - 1;
346 *psz++ = ':';
347 }
348 }
349 else if (pszPath[1] == ':' && isalpha(pszPath[0]))
350 {
351 /* drive letter */
352 *psz++ = toupper(pszPath[0]);
353 *psz++ = ':';
354 }
355 else
356 {
357 /* relative */
358 *psz++ = _getdrive() + 'A' - 1;
359 *psz++ = ':';
360 }
361
362 cchOut = WideCharToMultiByte(CP_ACP, 0,
363 pFileNameInfo->FileName, pFileNameInfo->FileNameLength / sizeof(WCHAR),
364 psz, cchFull - (psz - pszFull) - 2, NULL, NULL);
365 if (cchOut > 0)
366 {
367 const char *pszEnd;
368
369 /* upper case the server and share */
370 if (fUnc)
371 {
372 for (psz++; *psz != '/' && *psz != '\\'; psz++)
373 *psz = toupper(*psz);
374 for (psz++; *psz != '/' && *psz != '\\'; psz++)
375 *psz = toupper(*psz);
376 }
377
378 /* add trailing slash on directories if input has it. */
379 pszEnd = strchr(pszPath, '\0');
380 if ( (pszEnd[-1] == '/' || pszEnd[-1] == '\\')
381 && psz[cchOut - 1] != '\\'
382 && psz[cchOut - 1] != '//')
383 psz[cchOut++] = '\\';
384
385 /* make sure it's terminated */
386 psz[cchOut] = '\0';
387 return 0;
388 }
389 return -3;
390 }
391 }
392 return -2;
393}
394
395void
396nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull)
397{
398#if 0
399 static int s_cHits = 0;
400 static int s_cFallbacks = 0;
401#endif
402
403 /*
404 * The simple case, the file / dir / whatever exists and can be
405 * queried without problems.
406 */
407 if (nt_get_filename_info(pszPath, pszFull, cchFull) == 0)
408 {
409#if 0
410 fprintf(stderr, "nt #%d - %s\n", ++s_cHits, pszFull);
411#endif
412 return;
413 }
414 if (g_pfnNtQueryInformationFile)
415 {
416 /* do _fullpath and drop off path elements until we get a hit... - later */
417 }
418
419 /*
420 * For now, simply fall back on the old method.
421 */
422 _fullpath(pszFull, pszPath, cchFull);
423 w32_fixcase(pszFull);
424#if 0
425 fprintf(stderr, "fb #%d - %s\n", ++s_cFallbacks, pszFull);
426#endif
427}
428
429#endif /* bird */
430
431
432/*
433 * Convert to forward slashes. Resolve to full pathname optionally
434 */
435char *
436w32ify(const char *filename, int resolve)
437{
438 static char w32_path[FILENAME_MAX];
439 char *p;
440
441 if (resolve) {
442#if 1 /* bird */
443# if 1
444 nt_fullpath(filename, w32_path, sizeof(w32_path));
445# else
446 _fullpath(w32_path, filename, sizeof(w32_path));
447 w32_fixcase(w32_path);
448# endif
449#else /* !bird */
450 _fullpath(w32_path, filename, sizeof (w32_path));
451#endif /* !bird */
452 } else
453 strncpy(w32_path, filename, sizeof (w32_path));
454
455 for (p = w32_path; p && *p; p++)
456 if (*p == '\\')
457 *p = '/';
458
459 return w32_path;
460}
461
462char *
463getcwd_fs(char* buf, int len)
464{
465 char *p = getcwd(buf, len);
466
467 if (p) {
468 char *q = w32ify(buf, 0);
469 strncpy(buf, q, len);
470 }
471
472 return p;
473}
474
475#undef stat
476/*
477 * Workaround for directory names with trailing slashes.
478 * Added by bird reasons stated.
479 */
480int
481my_stat(const char *path, struct stat *st)
482{
483 int rc = stat(path, st);
484 if ( rc != 0
485 && errno == ENOENT
486 && *path != '\0')
487 {
488 char *slash = strchr(path, '\0') - 1;
489 if (*slash == '/' || *slash == '\\')
490 {
491 size_t len_path = slash - path + 1;
492 char *tmp = alloca(len_path + 4);
493 memcpy(tmp, path, len_path);
494 tmp[len_path] = '.';
495 tmp[len_path + 1] = '\0';
496 errno = 0;
497 rc = stat(tmp, st);
498 if ( rc == 0
499 && !S_ISDIR(st->st_mode))
500 {
501 errno = ENOTDIR;
502 rc = -1;
503 }
504 }
505 }
506 return rc;
507}
508
509#ifdef unused
510/*
511 * Convert delimiter separated pathnames (e.g. PATH) or single file pathname
512 * (e.g. c:/foo, c:\bar) to NutC format. If we are handed a string that
513 * _NutPathToNutc() fails to convert, just return the path we were handed
514 * and assume the caller will know what to do with it (It was probably
515 * a mistake to try and convert it anyway due to some of the bizarre things
516 * that might look like pathnames in makefiles).
517 */
518char *
519convert_path_to_nutc(char *path)
520{
521 int count; /* count of path elements */
522 char *nutc_path; /* new NutC path */
523 int nutc_path_len; /* length of buffer to allocate for new path */
524 char *pathp; /* pointer to nutc_path used to build it */
525 char *etok; /* token separator for old path */
526 char *p; /* points to element of old path */
527 char sep; /* what flavor of separator used in old path */
528 char *rval;
529
530 /* is this a multi-element path ? */
531 for (p = path, etok = strpbrk(p, ":;"), count = 0;
532 etok;
533 etok = strpbrk(p, ":;"))
534 if ((etok - p) == 1) {
535 if (*(etok - 1) == ';' ||
536 *(etok - 1) == ':') {
537 p = ++etok;
538 continue; /* ignore empty bucket */
539 } else if (etok = strpbrk(etok+1, ":;"))
540 /* found one to count, handle drive letter */
541 p = ++etok, count++;
542 else
543 /* all finished, force abort */
544 p += strlen(p);
545 } else
546 /* found another one, no drive letter */
547 p = ++etok, count++;
548
549 if (count) {
550 count++; /* x1;x2;x3 <- need to count x3 */
551
552 /*
553 * Hazard a guess on how big the buffer needs to be.
554 * We have to convert things like c:/foo to /c=/foo.
555 */
556 nutc_path_len = strlen(path) + (count*2) + 1;
557 nutc_path = xmalloc(nutc_path_len);
558 pathp = nutc_path;
559 *pathp = '\0';
560
561 /*
562 * Loop through PATH and convert one elemnt of the path at at
563 * a time. Single file pathnames will fail this and fall
564 * to the logic below loop.
565 */
566 for (p = path, etok = strpbrk(p, ":;");
567 etok;
568 etok = strpbrk(p, ":;")) {
569
570 /* don't trip up on device specifiers or empty path slots */
571 if ((etok - p) == 1)
572 if (*(etok - 1) == ';' ||
573 *(etok - 1) == ':') {
574 p = ++etok;
575 continue;
576 } else if ((etok = strpbrk(etok+1, ":;")) == NULL)
577 break; /* thing found was a WINDOWS32 pathname */
578
579 /* save separator */
580 sep = *etok;
581
582 /* terminate the current path element -- temporarily */
583 *etok = '\0';
584
585#ifdef __NUTC__
586 /* convert to NutC format */
587 if (_NutPathToNutc(p, pathp, 0) == FALSE) {
588 free(nutc_path);
589 rval = savestring(path, strlen(path));
590 return rval;
591 }
592#else
593 *pathp++ = '/';
594 *pathp++ = p[0];
595 *pathp++ = '=';
596 *pathp++ = '/';
597 strcpy(pathp, &p[2]);
598#endif
599
600 pathp += strlen(pathp);
601 *pathp++ = ':'; /* use Unix style path separtor for new path */
602 *pathp = '\0'; /* make sure we are null terminaed */
603
604 /* restore path separator */
605 *etok = sep;
606
607 /* point p to first char of next path element */
608 p = ++etok;
609
610 }
611 } else {
612 nutc_path_len = strlen(path) + 3;
613 nutc_path = xmalloc(nutc_path_len);
614 pathp = nutc_path;
615 *pathp = '\0';
616 p = path;
617 }
618
619 /*
620 * OK, here we handle the last element in PATH (e.g. c of a;b;c)
621 * or the path was a single filename and will be converted
622 * here. Note, testing p here assures that we don't trip up
623 * on paths like a;b; which have trailing delimiter followed by
624 * nothing.
625 */
626 if (*p != '\0') {
627#ifdef __NUTC__
628 if (_NutPathToNutc(p, pathp, 0) == FALSE) {
629 free(nutc_path);
630 rval = savestring(path, strlen(path));
631 return rval;
632 }
633#else
634 *pathp++ = '/';
635 *pathp++ = p[0];
636 *pathp++ = '=';
637 *pathp++ = '/';
638 strcpy(pathp, &p[2]);
639#endif
640 } else
641 *(pathp-1) = '\0'; /* we're already done, don't leave trailing : */
642
643 rval = savestring(nutc_path, strlen(nutc_path));
644 free(nutc_path);
645 return rval;
646}
647
648#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette