Discussion:
Converting a file name to its correct case as listed in the file system
(too old to reply)
Arch
2003-10-27 16:27:27 UTC
Permalink
This has probably been posted before, but if so I couldn't find it.
Here's my C++ implementation that seems to work okay with limited
testing. Feedback and corrections welcome.

char *ConvertFilespecToCorrectCase(char *aFullFileSpec)
// aFullFileSpec must be a modifiable string
// since it will be converted to proper case.
// Returns aFullFileSpec, the contents of which
// have been converted to the case used by the
// file system. Note: The trick of changing
// the current directory to be that of
// aFullFileSpec and then calling GetFullPathName()
// doesn't always work. So perhaps the
// only easy way is to call FindFirstFile() on each
// directory that composes aFullFileSpec, which
// is what is done here.
{
// Longer in case of UNCs and such,
// which might be longer than MAX_PATH:
#define WORK_AREA_SIZE (MAX_PATH * 2)
if (!aFullFileSpec || !*aFullFileSpec) return aFullFileSpec;
size_t length = strlen(aFullFileSpec);
if (length < 2 || length >= WORK_AREA_SIZE) return aFullFileSpec;
// Start with something easy, the drive letter:
if (aFullFileSpec[1] == ':')
aFullFileSpec[0] = toupper(aFullFileSpec[0]);
// else it might be a UNC that has no drive letter.
char built_filespec[WORK_AREA_SIZE], *dir_start, *dir_end;
if (dir_start = strchr(aFullFileSpec, ':'))
// Skip over 1st backslash that goes with drive letter.
dir_start += 2;
else // it's probably a UNC (handling of UNCs hasn't been tested)
{
if (strncmp(aFullFileSpec, "\\\\", 2))
// Doesn't appear to be a UNC, so don't try:
return aFullFileSpec;
// I think MS says you can't use FindFirstFile() directly
// on a share name, so we want to omit that from consideration
// (i.e. we don't attempt to find its proper case):
dir_start = aFullFileSpec + 2;
}
// Init the new string (the filespec we're building),
// e.g. copy just the "c:\\" part.
int chars_to_copy = dir_start - aFullFileSpec;
if (chars_to_copy)
memcpy(built_filespec, aFullFileSpec, chars_to_copy);
built_filespec[chars_to_copy] = '\0';
WIN32_FIND_DATA found_file;
HANDLE file_search;
for (dir_end = dir_start; dir_end = strchr(dir_end, '\\'); ++dir_end)
{
*dir_end = '\0'; // Temporarily terminate.
file_search = FindFirstFile(aFullFileSpec, &found_file);
*dir_end = '\\'; // Restore it before we do anything else.
if (file_search == INVALID_HANDLE_VALUE)
return aFullFileSpec;
FindClose(file_search);
// Append the case-corrected version of this directory name:
strcat(built_filespec, found_file.cFileName);
strcat(built_filespec, "\\");
}
// Now do the filename itself:
if ( (file_search = FindFirstFile(aFullFileSpec, &found_file))
== INVALID_HANDLE_VALUE )
return aFullFileSpec;
FindClose(file_search);
strcat(built_filespec, found_file.cFileName);
// It might be possible for the new one to be longer than the old,
// e.g. if some 8.3 short names were converted to long names by the
// process. Thus, the caller should ensure that aFullFileSpec is
// large enough:
strcpy(aFullFileSpec, built_filespec);
return aFullFileSpec;
}


Hopefully this will be of help to someone.
- Chris Mallett

Key words: name correct case convert findfirstfile pathname filename
filespec getfullpathname
Bob Masta
2003-10-28 13:26:32 UTC
Permalink
Post by Arch
This has probably been posted before, but if so I couldn't find it.
Here's my C++ implementation that seems to work okay with limited
testing. Feedback and corrections welcome.
char *ConvertFilespecToCorrectCase(char *aFullFileSpec)
// aFullFileSpec must be a modifiable string
// since it will be converted to proper case.
// Returns aFullFileSpec, the contents of which
// have been converted to the case used by the
// file system. Note: The trick of changing
// the current directory to be that of
// aFullFileSpec and then calling GetFullPathName()
// doesn't always work. So perhaps the
// only easy way is to call FindFirstFile() on each
// directory that composes aFullFileSpec, which
// is what is done here.
{
// Longer in case of UNCs and such,
#define WORK_AREA_SIZE (MAX_PATH * 2)
if (!aFullFileSpec || !*aFullFileSpec) return aFullFileSpec;
size_t length = strlen(aFullFileSpec);
if (length < 2 || length >= WORK_AREA_SIZE) return aFullFileSpec;
if (aFullFileSpec[1] == ':')
aFullFileSpec[0] = toupper(aFullFileSpec[0]);
// else it might be a UNC that has no drive letter.
char built_filespec[WORK_AREA_SIZE], *dir_start, *dir_end;
if (dir_start = strchr(aFullFileSpec, ':'))
// Skip over 1st backslash that goes with drive letter.
dir_start += 2;
else // it's probably a UNC (handling of UNCs hasn't been tested)
{
if (strncmp(aFullFileSpec, "\\\\", 2))
return aFullFileSpec;
// I think MS says you can't use FindFirstFile() directly
// on a share name, so we want to omit that from consideration
dir_start = aFullFileSpec + 2;
}
// Init the new string (the filespec we're building),
// e.g. copy just the "c:\\" part.
int chars_to_copy = dir_start - aFullFileSpec;
if (chars_to_copy)
memcpy(built_filespec, aFullFileSpec, chars_to_copy);
built_filespec[chars_to_copy] = '\0';
WIN32_FIND_DATA found_file;
HANDLE file_search;
for (dir_end = dir_start; dir_end = strchr(dir_end, '\\'); ++dir_end)
{
*dir_end = '\0'; // Temporarily terminate.
file_search = FindFirstFile(aFullFileSpec, &found_file);
*dir_end = '\\'; // Restore it before we do anything else.
if (file_search == INVALID_HANDLE_VALUE)
return aFullFileSpec;
FindClose(file_search);
strcat(built_filespec, found_file.cFileName);
strcat(built_filespec, "\\");
}
if ( (file_search = FindFirstFile(aFullFileSpec, &found_file))
== INVALID_HANDLE_VALUE )
return aFullFileSpec;
FindClose(file_search);
strcat(built_filespec, found_file.cFileName);
// It might be possible for the new one to be longer than the old,
// e.g. if some 8.3 short names were converted to long names by the
// process. Thus, the caller should ensure that aFullFileSpec is
strcpy(aFullFileSpec, built_filespec);
return aFullFileSpec;
}
Hopefully this will be of help to someone.
- Chris Mallett
Key words: name correct case convert findfirstfile pathname filename
filespec getfullpathname
Funny, I just had need for this a couple of weeks ago. I also
discovered that FindFirstFile is the only way to get the proper
case. The problem with OPENFILENAME seems to be only
when the user types the name instead of clicking on it; then
you get whatever case he typed. GetFullPathName then
returns the same thing.

One other issue I stumbled onto is that if the file name is
an old 8.3 style, then FindFirstFile returns all caps;
however, the user sees a "pretty" version in GetOpenFileName
that has only the first letter capitalized. Since I wanted this
name for a title, I scanned the FindFirstFile
WIN32_FIND_DATA.cFileName for the end of the name.
If it is a long name, the 8.3 form follows, but if it is 8.3 only
there is nothing after it. So after I find the null at the end
of the first name, I see if the next character is also null.
If so, it was an 8.3 and I leave only the initial char in caps
and convert the rest to lower case. Note that you have to
null the whole name area in the structure before you call
FindFirstFile, since if there is no second name string it
just leaves whatever garbage was present... it does not
add the second null for you.

Hope this helps!


Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Lucian Wischik
2003-10-28 14:11:25 UTC
Permalink
Post by Bob Masta
Funny, I just had need for this a couple of weeks ago. I also
discovered that FindFirstFile is the only way to get the proper
case.
Could you elaborate on this please, Bob? I'd have expected that
converting it into a PIDL and then converting the PIDL back into a
display string would work. Did you try, and did it fail to work?

--
Lucian
Bob Masta
2003-10-29 14:38:06 UTC
Permalink
Post by Lucian Wischik
Post by Bob Masta
Funny, I just had need for this a couple of weeks ago. I also
discovered that FindFirstFile is the only way to get the proper
case.
Could you elaborate on this please, Bob? I'd have expected that
converting it into a PIDL and then converting the PIDL back into a
display string would work. Did you try, and did it fail to work?
Lucian:
I didn't try any PIDL stuff, and never even heard of it until you
brought it up <g>. A zillion Google hits later, I now know that
it stands for Pointer to Item ID List, and it is apparently used
in shell operations (which I've never used). Can you give an
example of what you have in mind? Thanks!



Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Lucian Wischik
2003-10-29 15:49:55 UTC
Permalink
Post by Bob Masta
I didn't try any PIDL stuff, and never even heard of it until you
brought it up <g>. A zillion Google hits later, I now know that
it stands for Pointer to Item ID List, and it is apparently used
in shell operations (which I've never used). Can you give an
example of what you have in mind? Thanks!
Well, PIDLs are binary identifiers that Windows uses as the canonical
location of any entity in the shell filespace (including virtual
folders as well as real physical disk folders). There are functions to
"parse" a display name from a text string like "c:\documents and
settings\ljw1004\my documents\folder\file.txt" into its canonical
PIDL, I think. "ParseDisplayName". And I presume there are functions
which take a PIDL and turn it into a text string, though I've not
looked. Presumably these latter functions will use the correct case.

--
Lucian
Arch
2003-10-31 02:58:26 UTC
Permalink
I scanned the FindFirstFile WIN32_FIND_DATA.cFileName for the end
of the name. If it is a long name, the 8.3 form follows, but if it is
8.3 only there is nothing after it. So after I find the null at the
end of the first name, I see if the next character is also null. If
so, it was an 8.3 ...

It sounds like you might be using an old version of the
WIN32_FIND_DATA structure. The one I see documented contains a field
"cAlternateFileName", which is "the classic 8.3 (filename.ext)
filename format" immediately after the cFileName field. In light of
this, it doesn't seem necessary to do the steps you describe(?)
Bob Masta
2003-10-31 13:01:29 UTC
Permalink
Post by Arch
I scanned the FindFirstFile WIN32_FIND_DATA.cFileName for the end
of the name. If it is a long name, the 8.3 form follows, but if it is
8.3 only there is nothing after it. So after I find the null at the
end of the first name, I see if the next character is also null. If
so, it was an 8.3 ...
It sounds like you might be using an old version of the
WIN32_FIND_DATA structure. The one I see documented contains a field
"cAlternateFileName", which is "the classic 8.3 (filename.ext)
filename format" immediately after the cFileName field. In light of
this, it doesn't seem necessary to do the steps you describe(?)
If the file has both types of names, then the cAlternateFileName
appears after the long cFileName. But old files that never had long
names only have a single entry, which appears as cFileName.
So how do you know if you have an old 8.3 name or a new
long-style name that simply happens to have 8.3 or less
characters? If only one name appears, then it is an old 8.3
name. If two appear, it is a "long" name, no matter how long
it really is.


Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com

Arch
2003-10-28 14:31:38 UTC
Permalink
A small correction that will hopefully support UNCs better (though
this part is still untested):

// I think MS says you can't use FindFirstFile()
// directly on a share name, so we
// want to omit both that and the server name
// from consideration (i.e. we don't attempt
// to find their proper case):
dir_start = aFullFileSpec + 2;
char *end_of_server_name = strchr(dir_start, '\\');
if (end_of_server_name)
{
dir_start = end_of_server_name + 1;
char *end_of_share_name = strchr(dir_start, '\\');
if (end_of_share_name)
dir_start = end_of_share_name + 1;
}
Continue reading on narkive:
Loading...