Welcome, Guest
Username: Password: Remember me
  • Page:
  • 1

TOPIC:

find files pitfall 23 Sep 2021 18:42 #19685

  • Karl-Heinz
  • Karl-Heinz's Avatar
  • Topic Author


  • Posts: 774
  • Guys,

    In the German Xbase++ forum there´s been a discussion about the Directory() behaviour with the search pattern "862*.jpg".

    www.xbaseforum.de/viewtopic.php?p=139600#p139600

    The problem can be reproduced with these two (long name ! ) files:

    86220200630.JPG
    86720210702.JPG

    Because of the search pattern, only this file should be found.

    86220200630.JPG

    but both files are found !

    Since VO and X# give the same result I took a closer look. The problem occurs when long filename are used and the
    DOS 8.3 names feature is enabled on the search drive. You can check the current state of your drives when you open
    a CMD box with admin rights and run the fsutil.exe.

    fsutil 8dot3name query <Drive:>

    These are the results of my "C:" and "D:" drives

    8dot3 name creation is enabled on C:
    8dot3 name creation is disabled on D:

    When i search in my C:\Test dir both files are found and only the search in my D:\test directory shows one file. The
    problem is that in the C:\Test dir a comparison takes place with the short filenames and the long filenames. If the
    pattern is found in the short filename it is interpreted as a hit.

    That´s the content of my C:\Test dir. As you can see both short file names start with "862"
    C:\Test>dir/X 862*.jpg
     Volume in Laufwerk C: hat keine Bezeichnung.
     Volumeseriennummer: DA00-F3C0
    
     Verzeichnis von C:\Test
    
    23.09.2021  18:00                 5 862202~1.JPG 86220200630.JPG
    23.09.2021  18:00                 5 8624E7~1.JPG 86720210702.JPG
                   2 Datei(en),             10 Bytes
                   0 Verzeichnis(se), 119.755.788.288 Bytes frei
    From what i´ve found so far, the 8dot3name feature is enabled by default for the C drive only. That's the reason why
    the search works in my D:\test dir. It seems the only way to fix this behaviour is to disable the 8dot3name feature, or
    to implement a own pattern search ?

    That's my test code:
    FUNCTION SearchFiles()
    LOCAL aFiles, aDir AS ARRAY
    LOCAL i AS DWORD 
    LOCAL hFile, hFind AS PTR 
    LOCAL cDir, cFile AS STRING
    LOCAL struFindData IS _winWIN32_FIND_DATA 
    
    
    	cDir := "c:\Test\"	
    //	cDir := "d:\Test\"
    
    	aFiles := {} 
    	
    	// long filenames 
    	AAdd(aFiles,"86220200630.JPG")
    	AAdd(aFiles,"86720210702.JPG")
       
    	FOR i := 1 TO ALen(aFiles)
    		IF ! File( cDir + aFiles [i])
    			IF ( hFile := FCreate( cDir + aFiles[i], FC_NORMAL ) ) != F_ERROR
    				FWrite( hFile, "Jimmy" )
    				FClose( hFile )
    			ENDIF 	
    		ENDIF	       
    	NEXT
    
    	aDir := Directory( cDir + "862*.JPG") 
        
    	?
    	? "Directory() results"
    	?
       
    	FOR i := 1 UPTO ALen ( ADir ) 
    		? aDir [ i , F_NAME ] 
    	NEXT
    	
    	?
    	? "FindxxxFile results"
    	?
    	
    	cFile := cDir + "862*.JPG" 
    
    	IF ( hFind := FindFirstFile( String2Psz ( cFile ) , @struFindData) )  != INVALID_HANDLE_VALUE
    
    		// note: Psz2String ( @struFindData.cAlternateFileName ) shows the 8.3 filename if any
    		
    		? Psz2String ( @struFindData.cFileName ) , Psz2String ( @struFindData.cAlternateFileName )
    		
    		WHILE FindNextFile(hFind, @struFindData)
    			? Psz2String ( @struFindData.cFileName ) , Psz2String ( @struFindData.cAlternateFileName )
    		ENDDO 	           
     			
     		FindClose ( hFind ) 	
    
    	ENDIF    		
       
    
    #ifdef __XSHARP__ 
    
    	?
    	? "System.IO.Directory.GetFiles results"
    	?
            
    	VAR files := System.IO.Directory.GetFiles(cDir, "862*.JPG" ) 
    	
    	FOREACH VAR f IN files 
    		? System.IO.Path.GetFileName ( f )
    	NEXT 	
    	
      
       
    #endif   
    
       
    RETURN TRUE

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    find files pitfall 23 Sep 2021 22:42 #19690

    • robert
    • robert's Avatar


  • Posts: 3599
  • Karl-Heinz,
    Maybe the Directory function should (internally) double check to see if the found file matches the mask?
    We have a _Like() function that could be used for that.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    find files pitfall 24 Sep 2021 13:28 #19701

    • ic2
    • ic2's Avatar


  • Posts: 1667
  • Hello Robert, Karl-Heinz,

    Reading this amazed me. What is the use of DOS 8.3 filenames anyway? If not already disabled, I think every sensible system administrator should do that (I think it's fsutil behavior set disable8dot3 3, correct?)

    If 86720210702.JPG becomes 8624E7~1.JPG , a totally random name change, then you could expect to find anything. This of course again comes from Microsoft. Logically this file should be called 867202~1.jpg I'd say. I often like older techniques to remain available but who wants to see a >8+3 filename converted in 2021 to a 8+3 file name which was from before 1994?

    Dick

    Please Log in or Create an account to join the conversation.

    find files pitfall 03 Oct 2021 12:37 #19814

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 774
  • Guys,

    the advice of Robert to use the _Like() function seems the way to go. Here´s a modified test function:

    FUNCTION SearchFiles2()
    LOCAL cDir, sWildCard  AS STRING 	
    LOCAL aFiles AS ARRAY 
    LOCAL files AS STRING[]
    LOCAL i AS DWORD 
    LOCAL hFile AS PTR 
    
    	cDir := "c:\Test\"	// 8dot3name enabled 
    //	cDir := "d:\Test\"
    
    	aFiles := {}  
    	
    	AAdd(aFiles,"86220200630.JPG")
    	AAdd(aFiles,"86720210702.JPG")
    	AAdd(aFiles,"86220200630.Txt")
    	AAdd(aFiles,"86720210702.Txt")
    	AAdd(aFiles,"Test1.htm")
    	AAdd(aFiles,"Test1.html")	
    	AAdd(aFiles,"TEst2.html")		
    	
    //	sWildCard := "*.???" 	
    //	sWildCard := "*.*" 
    	sWildCard := "862*.jpg" 
    //	sWildCard := "86220200630.JPG"	
    //	sWildCard := "862*.txt" 	
    //	sWildCard := "*.h*"
    //	sWildCard := "*.htm"	 
    //	sWildCard := "*.html" 
    	
    	? "Current search mask: " + sWildCard
    
    	FOR i := 1 TO ALen(aFiles)
    		IF ! File( cDir + aFiles [i])
    			IF ( hFile := FCreate( cDir + aFiles[i], FC_NORMAL ) ) != F_ERROR
    				FWrite( hFile, "Jimmy" )
    				FClose( hFile )
    			ENDIF 	
    		ENDIF	       
    	NEXT
    
    	
    
    	
    	?
    	? "System.IO.Directory.GetFiles results"
    	?
    	
     
    	files := System.IO.Directory.GetFiles(cDir, sWildCard ) 
    
    	FOREACH cFile AS STRING IN files 
    		
    		VAR oFile := System.IO.FileInfo{cFile}	
    		
    		? _Like ( sWildCard:ToUpper() , oFile:Name:ToUpper() ) , oFile:Name
    		
    		/* 
    			Only if _Like() returns true, the file should be added inside  		
    		 	the Directory() function.			
    
    		*/
    
    	NEXT 
    	
    	?
    	? PathMatchSpec ( "Test.htm" , "*.htm" ) 	// ok, true 
    	? PathMatchSpec ( "Test.html" , "*.htm" ) 	// ok, false 
    	? PathMatchSpec ( "Test.html" , "*.html" ) 	// ok, true 
    	
    RETURN TRUE 	
    
    [DllImport("shlwapi.dll", CharSet := CharSet.Auto)] ;
    FUNCTION PathMatchSpec( pszFileParam AS STRING , pszSpec AS STRING ) AS LOGIC

    A side effect is that a search with a pattern like "*.htm" no longer includes "*.html" files. Just activate the line

    sWildCard := "*.htm"

    and you´ll see that the - imo correct - results are now:

    .T. Test1.htm
    .F. Test1.html
    .F. TEst2.html

    BTW I. Another option to filter out files according the file extension is to use the API function PathMatchSpec() - see the PathMatchSpec() samples.

    BTW II. Without looking directly at the created short file names you´ll never know how such names look like. Simply use the pattern "862*.txt" instead of "862*.jpg" and you´ll see that now only the file "86220200630.Txt" will be found ...

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Last edit: by Karl-Heinz.

    find files pitfall 05 Oct 2021 08:29 #19829

    • robert
    • robert's Avatar


  • Posts: 3599
  • Karl Heinz,
    I will add the check to the runtime.
    Btw if you use Like() instead of _Like() then you do not have to uppercase the strings in your code. Like() is case insensitive.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    find files pitfall 06 Oct 2021 18:17 #19849

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 774
  • Robert,

    my intention to use _Like() instead of Like() was that Like() behaves differently when the Fox dialect is used.
    FUNCTION Like(sWildCard AS STRING, sSource AS STRING) AS LOGIC
        IF XSharp.RuntimeState.Dialect == XSharpDialect.FoxPro
            RETURN _Like(sWildCard, sSource)
        ENDIF
        RETURN _Like(Upper(sWildCard), Upper(sSource))
    But in the meantime I found out that the VFP Directory() works completely different than the VO Directory() function.

    @all

    When I look at the result of the X#/VO Directory() function, i see that the content of the array returned has been expanded. The new array constants are marked with // new

    CONST EXPORT F_NAME := 1 AS INT
    CONST EXPORT F_SIZE := 2 AS INT
    CONST EXPORT F_DATE := 3 AS INT
    CONST EXPORT F_WRITE_DATE := 3 AS INT  // New
    CONST EXPORT F_TIME := 4 AS INT
    CONST EXPORT F_WRITE_TIME := 4 AS INT // New
    CONST EXPORT F_ATTR := 5 AS INT
    CONST EXPORT F_EA_SIZE := 6 AS INT  // New: 
    CONST EXPORT F_CREATION_DATE := 7 AS INT // New
    CONST EXPORT F_CREATION_TIME := 8 AS INT // New
    CONST EXPORT F_ACCESS_DATE := 9 AS INT  // New
    CONST EXPORT F_ACCESS_TIME := 10 AS INT // New


    Now, showing a X#/VO Directory() result like
    ? ShowArray ( aDirectoryResult )
    results in:
    a[1] = {[0000000010]0x0141B42A} (A)
    a[1][1] = 86220200630.Txt (C)
    a[1][2] = 5 (N)
    a[1][3] = 29.09.2021 (D)
    a[1][4] = 08:30:11 (C)
    a[1][5] = A (C)
    a[1][6] = 32 (N)
    a[1][7] = 27.09.2021 (D)
    a[1][8] = 07:39:26 (C)
    a[1][9] = 29.09.2021 (D)
    a[1][10] = 08:30:11 (C)

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    • Page:
    • 1