On Windows, check for case-mismatched file names on successful kopen4load().

When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.

This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively.  However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names.  However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.

git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2012-05-25 15:23:55 +00:00
parent f69d9f241d
commit 68c701fb4a

View file

@ -6,6 +6,10 @@
// by Jonathon Fowler (jf@jonof.id.au) // by Jonathon Fowler (jf@jonof.id.au)
#include "compat.h" #include "compat.h"
#ifdef _WIN32
// for FILENAME_CASE_CHECK
# include <shellapi.h>
#endif
#include "cache1d.h" #include "cache1d.h"
#include "pragmas.h" #include "pragmas.h"
#include "baselayer.h" #include "baselayer.h"
@ -443,6 +447,16 @@ int32_t findfrompath(const char *fn, char **where)
return -1; return -1;
} }
#ifdef _WIN32
# define FILENAME_CASE_CHECK
#endif
#ifdef FILENAME_CASE_CHECK
// don't free pfn if !=0 AND we Bopen()'ed the file successfully
static int32_t dont_free_pfn;
static char *lastpfn;
#endif
int32_t openfrompath(const char *fn, int32_t flags, int32_t mode) int32_t openfrompath(const char *fn, int32_t flags, int32_t mode)
{ {
char *pfn; char *pfn;
@ -450,7 +464,12 @@ int32_t openfrompath(const char *fn, int32_t flags, int32_t mode)
if (findfrompath(fn, &pfn) < 0) return -1; if (findfrompath(fn, &pfn) < 0) return -1;
h = Bopen(pfn, flags, mode); h = Bopen(pfn, flags, mode);
Bfree(pfn); #ifdef FILENAME_CASE_CHECK
if (h>=0 && dont_free_pfn)
lastpfn = pfn;
else
#endif
Bfree(pfn);
return h; return h;
} }
@ -666,6 +685,24 @@ void uninitgroupfile(void)
} }
} }
#ifdef FILENAME_CASE_CHECK
// See
// http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows
// for relevant discussion.
static SHFILEINFO shinf;
// -1: failure, 0: match, 1: mismatch
static int32_t check_filename_mismatch(const char *filename, int32_t ofs)
{
// we assume that UNICODE is not #defined, winlayer.c errors out else
if (!SHGetFileInfo(filename, -1, &shinf, sizeof(shinf), SHGFI_DISPLAYNAME))
return -1;
return !!Bstrcmp(shinf.szDisplayName, filename+ofs);
}
#endif
int32_t kopen4load(const char *filename, char searchfirst) int32_t kopen4load(const char *filename, char searchfirst)
{ {
int32_t j, k, fil, newhandle = MAXOPENFILES-1; int32_t j, k, fil, newhandle = MAXOPENFILES-1;
@ -685,14 +722,55 @@ int32_t kopen4load(const char *filename, char searchfirst)
} }
} }
#ifdef FILENAME_CASE_CHECK
dont_free_pfn = 1;
#endif
if (searchfirst == 0 && (fil = openfrompath(filename,BO_BINARY|BO_RDONLY,S_IREAD)) >= 0) if (searchfirst == 0 && (fil = openfrompath(filename,BO_BINARY|BO_RDONLY,S_IREAD)) >= 0)
{ {
#ifdef FILENAME_CASE_CHECK
int32_t status;
char *cp, *lastslash;
// convert all slashes to backslashes because SHGetFileInfo()
// complains else!
lastslash = lastpfn;
for (cp=lastpfn; *cp; cp++)
if (*cp=='/')
{
*cp = '\\';
lastslash = cp;
}
if (lastslash != lastpfn)
lastslash++;
status = check_filename_mismatch(lastpfn, lastslash-lastpfn);
dont_free_pfn = 0;
if (status == -1)
{
initprintf("SHGetFileInfo failed with error code %lu\n", GetLastError());
}
else if (status == 1)
{
initprintf("warning: case mismatch: passed \"%s\", real \"%s\"\n",
lastslash, shinf.szDisplayName);
}
Bfree(lastpfn);
lastpfn=NULL;
#endif
filegrp[newhandle] = 255; filegrp[newhandle] = 255;
filehan[newhandle] = fil; filehan[newhandle] = fil;
filepos[newhandle] = 0; filepos[newhandle] = 0;
return(newhandle); return(newhandle);
} }
#ifdef FILENAME_CASE_CHECK
dont_free_pfn = 0;
#endif
for (; toupperlookup[*filename] == '/'; filename++); for (; toupperlookup[*filename] == '/'; filename++);
#ifdef WITHKPLIB #ifdef WITHKPLIB