1. Testers required

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 
new topic     » topic index » view message » categorize

2. Re: Testers required

petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

3. Re: Testers required

mattlewis said...

Seems to work for me on XP with 4.0.

Thanks

mattlewis said...

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. 

new topic     » goto parent     » topic index » view message » categorize

4. Re: Testers required

petelomax said...
mattlewis said...

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.

petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

5. Re: Testers required

mattlewis said...

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

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu