1. Testers required
- Posted by petelomax Apr 15, 2010
- 1463 views
The age-old problem of Edita opening /both/ C:\Program Files\Edita\edita.exw /and/ C:\PROGRA~1\EDITA\EDITA.EXW still bites me from time to time (last one was when I loaded an old TODO.TXT from my w98 box onto XP, and edited/clicked on various file:xxx links).
By accident, I stumbled upon GetFullPathName and later GetLongPathName hiding in kernel32, both of which turned out to be a bit useless. I also tried getFullPathName() from w32file.e with disappointing results.
So here is my third attempt, this time with SHGetFileInfo from shell32.
Before plugging it in, and in case anyone else has use for it, I thought I would post it here, maybe iron out a few bugs the easy way. You may need to delete a few "builtins\", and try some of your own files after "test code:".
I assume a Linux version of this would be much simpler, just the part that replaces "stuff/../other" with "other" and the like.
-- (to become part of axtra.ew) -- -- Tested on Phix (0.5.9), RDS Eu 2.4 and 4.0, Windows 98 and XP. -- --/* include builtins\machine.e include builtins\dll.e include builtins\sort.e include builtins\misc.e include builtins\file.e --*/ --with trace function peek_string(atom addr) atom last last = addr -- find the string's null terminator while peek(last) do last += 1 end while return peek({addr,last-addr}) end function sequence charMapUp --, charMapDown integer AZ charMapUp = repeat(0,255) for i=1 to 255 do charMapUp[i] = i end for -- charMapDown=charMapUp for i='a' to 'z' do AZ = i-32 charMapUp[i] = AZ -- charMapDown[AZ] = i end for function UpperCase(object text) if integer(text) and text>=1 and text<=255 then return charMapUp[text] elsif sequence(text) then for i=1 to length(text) do text[i]=UpperCase(text[i]) end for end if return text end function function cleanUpPath(sequence filepath, sequence rootdir) integer k -- -- First make sure there is a proper filepath. -- if length(filepath)<2 or filepath[2]!=':' then if length(rootdir)=0 then puts(1,"error: length(rootdir) is 0\n") -- ?9/0 else if rootdir[length(rootdir)]='\\' or length(filepath)=0 or filepath[1]='\\' then filepath = rootdir & filepath else filepath = rootdir & '\\' & filepath end if end if end if -- -- Replace any / in filepath with \\ -- while 1 do k = find('/',filepath) if k=0 then exit end if filepath[k] = '\\' end while -- -- check for and remove any \..\ in filepath -- while 1 do k = match("\\..\\",filepath) if k=0 then exit end if for j=k-1 to 1 by -1 do if filepath[j]='\\' then -- remove "\\xxx\\.." --/**/ filepath[j..k+2] = "" --/* -- Phix filepath = filepath[1..j-1] & filepath[k+3..length(filepath)] --*/ -- RDS k = 0 -- signal found exit end if end for if k!=0 then puts(1,"Warning, cannot cleanup "&filepath&'\n') -- ?9/0 exit end if end while -- -- repeat for any \. -- while 1 do k = match("\\.",filepath) if k=0 then exit end if -- remove "\\." --/**/ filepath[k..k+1] = "" --/* -- Phix filepath = filepath[1..k-1] & filepath[k+2..length(filepath)] --*/ -- RDS end while -- -- repeat for any \\ -- while 1 do k = match("\\\\",filepath) if k=0 then exit end if -- remove 1st "\\" of 2 --/**/ filepath [k..k] = "" --/* -- Phix filepath = filepath[1..k-1] & filepath[k+1..length(filepath)] --*/ -- RDS end while return filepath end function constant shell32 = open_dll("shell32"), xSHGetFileInfo = define_c_func(shell32, "SHGetFileInfo", {C_POINTER, -- LPCTSTR pszPath C_UINT, -- DWORD dwFileAttributes (Ignored/we do not use SHGFI_USEFILEATTRIBUTES) C_POINTER, -- SHFILEINFO *psfi C_UINT, -- UINT cbFileInfo (size of psfi) C_UINT}, -- UINT uFlags C_POINTER), -- DWORD_PTR --constant SHGFI_DISPLAYNAME = #000000200 -- get display name -- SHFILEINFO = new_struct(), -- SHFILEINFO_hIcon = struc(C_POINTER), -- HICON hIcon -- SHFILEINFO_iIcon = struc(C_UINT), -- int iIcon -- SHFILEINFO_dwAttributes = struc(C_LONG), -- DWORD dwAttributes -- SHFILEINFO_szDisplayName = struc(anySize(260)), -- TCHAR szDisplayName[MAX_PATH] , SHFILEINFO_szDisplayName = 12 -- SHFILEINFO_szTypeName = struc(anySize(80)), -- TCHAR szTypeName[80] constant SHFI_len = 352, --sizeof(SHFILEINFO), SHFI = allocate(SHFI_len) function getProperPath(sequence filepath, sequence rootdir) -- -- For an input filepath of eg c:\PROGRA~1\edita\EDITA.EXW, -- returns eg C:\Program Files\Edita\edita.exw. -- This routine was written so that edita does not -- accidentally open the same file twice. -- Should work equally well for files and directories. -- Of course the result is fairly meaningless if the -- the specified file or path does not exist, but if -- you can open the input the same should be true for -- the output. -- -- rootdir may be "" if filepath[2] is ':', otherwise it -- should typically be current_dir(). Specifically in -- in the case of edita.edb/vedb.exw, filepath would be -- from T_files and rootdir from T_directories. -- sequence res --/* atom pFilePath --*/ res = {} filepath = cleanUpPath(filepath,rootdir) if filepath[length(filepath)]='\\' then res = "\\" filepath = filepath[1..length(filepath)-1] end if for i=length(filepath) to 1 by -1 do if filepath[i] = '\\' then if length(res) and res[1]!='\\' then res = '\\' & res end if --/* pFilePath = allocate_string(filepath) if c_func(xSHGetFileInfo,{pFilePath,0,SHFI,SHFI_len,SHGFI_DISPLAYNAME})=0 then --*/ if c_func(xSHGetFileInfo,{filepath,0,SHFI,SHFI_len,SHGFI_DISPLAYNAME})=0 then -- error: preserve what we have res = filepath[i+1..length(filepath)] & res else res = peek_string(SHFI+SHFILEINFO_szDisplayName) & res end if --/* free(pFilePath) --*/ filepath = filepath[1..i-1] end if end for filepath = UpperCase(filepath) -- should just be the drive letter. res = filepath & '\\' & res return res end function -- test code: constant icd = current_dir() -- NB was in C:\Program Files\Phix for these tests, -- and of course I had Edita installed above it. procedure show(sequence filepath) sequence filepath2 puts(1,filepath) puts(1," ==>") filepath = getProperPath(filepath,icd) puts(1,filepath) puts(1,"\n") filepath2 = getProperPath(filepath,"") -- exact match expected... if not equal(filepath2,filepath) then ?9/0 end if -- ...including case, iff it exists if sequence(dir(filepath)) then filepath2 = getProperPath(UpperCase(filepath),"") if not equal(filepath2,filepath) then ?9/0 end if else puts(1," (nb ^ does not exist)\n") end if end procedure show("C:\\Program Files\\Edita\\edita.exw") show("C:\\PROGRAM FILES\\EDITA\\EDITA.EXW") show("C:\\Program Files\\Edita\\") show("C:\\PrograM FiLes\\eDita\\") show("c:\\program files\\edita") show("c:\\progra~1\\edita") show("C:\\PROGRA~1\\EDITA") show("C:\\PROGRA~1\\") show("C:\\PROGRA~1") show(".") show("..\\edita\\SRC\\") show("..\\Xedita\\src\\") -- (test non-existent or (recently) deleted file does not crash) -- (not overly worried whether result is sensible/of any use.) if getc(0) then end if
2. Re: Testers required
- Posted by mattlewis (admin) Apr 15, 2010
- 1373 views
So here is my third attempt, this time with SHGetFileInfo from shell32.
Before plugging it in, and in case anyone else has use for it, I thought I would post it here, maybe iron out a few bugs the easy way. You may need to delete a few "builtins\", and try some of your own files after "test code:".
Seems to work for me on XP with 4.0. I replaced your includes with:
include std/machine.e include std/dll.e include std/sort.e include std/filesys.e
Also, you might take a look at pathinfo(), which seems to do most of this work for you, although it splits the information into pieces.
I changed show() slightly to demonstrate:
procedure show(sequence filepath) sequence filepath2 puts(1,filepath) puts(1," ==>") filepath = getProperPath(filepath,icd) puts(1,filepath) puts(1,"\n") filepath2 = getProperPath(filepath,"") sequence filepath3 = pathinfo( filepath ) -- exact match expected... if not equal(filepath2,filepath) then ?9/0 end if -- ...including case, iff it exists if sequence(dir(filepath)) then filepath2 = getProperPath(UpperCase(filepath),"") if not equal(filepath2,filepath) then ?9/0 end if else puts(1," (nb ^ does not exist)\n") end if printf(1, "\tpath: %s\n" & "\tunqualified file name: %s\n" & "\tfilename: %s\n" & "\tfile extension: %s\n\tdrive id: %s\n", filepath3 ) end procedure
I get, for example:
..\Xedita\src\ ==>C:\euphoria\Development\Xedita\src\ (nb ^ does not exist) path: \euphoria\Development\Xedita\src unqualified file name: filename: file extension: drive id: C
Matt
3. Re: Testers required
- Posted by petelomax Apr 15, 2010
- 1361 views
Seems to work for me on XP with 4.0.
Thanks
pathinfo() seems to do most of this work for you
You had me going for a moment there! I take it you mean that's the Linux side sorted out, but of course (removing my code) I get:
C:\PROGRA~1 ==> path: unqualified file name: PROGRA~1 filename: PROGRA~1 file extension: drive id: C
And maybe this is missing from the documentation (of pathinfo):
std_slash can be -1 to use / on Linux and \ on Windows, or you can specify "/" or "\\" ('/' and '\\' should behave the same), or in fact any other character or string of your choice. The default (0) is to leave path separators unaltered.
4. Re: Testers required
- Posted by mattlewis (admin) Apr 15, 2010
- 1253 views
pathinfo() seems to do most of this work for you
You had me going for a moment there! I take it you mean that's the Linux side sorted out, but of course (removing my code) I get:
No, I ran on Windows, but I guess I didn't read the output carefully enough. Sorry about that.
And maybe this is missing from the documentation (of pathinfo):
std_slash can be -1 to use / on Linux and \ on Windows, or you can specify "/" or "\\" ('/' and '\\' should behave the same), or in fact any other character or string of your choice. The default (0) is to leave path separators unaltered.
Yes, looks like that never got documented.
Matt
5. Re: Testers required
- Posted by petelomax Apr 15, 2010
- 1278 views
I guess I didn't read the output carefully enough. Sorry about that.
No worries. The output was fine but your call was added after my
filepath = getProperPath(filepath,icd)
Regards,
Pete