Author Topic: Bas Files Count and List by Recursive Algorithm  (Read 6295 times)

0 Members and 1 Guest are viewing this topic.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • GitHub
Re: Bas Files Count and List by Recursive Algorithm
« Reply #15 on: November 08, 2021, 08:21:47 pm »
Also, this is a simple alternative for recursive files listing :)

Code: QB64: [Select]
  1.  
  2. Dim x#, y#
  3. x# = Timer(0.01)
  4. path = _CWD$
  5. comm = pipecom_lite("PowerShell Get-ChildItem -Path \" + Chr$(34) + path + "\" + Chr$(34) + " -Recurse -Force -ErrorAction SilentlyContinue -Attributes Hidden, !Hidden ^| Select-Object -ExpandProperty FullName ^| Sort-Object ^| Format-Table -AutoSize")
  6.  
  7. y# = Timer(0.01)
  8. Print Len(comm)
  9. Print y# - x#; " seconds"
  10. ReDim As String filelist(0)
  11. String.Split comm, Chr$(10), filelist()
  12. Print UBound(filelist)
  13. For i = 1 To UBound(filelist)
  14.     Print i, filelist(i)
  15.  
  16. '$INCLUDE:'pipecomqb64.bas'
  17. Sub String.Split (Expression As String, delimiter As String, StorageArray() As String)
  18.     Dim copy As String, p As Long, curpos As Long, arrpos As Long, dpos As Long
  19.     copy = Expression
  20.     If delimiter = " " Then
  21.         copy = RTrim$(LTrim$(copy))
  22.         p = InStr(copy, "  ")
  23.         While p > 0
  24.             copy = Mid$(copy, 1, p - 1) + Mid$(copy, p + 1)
  25.             p = InStr(copy, "  ")
  26.         Wend
  27.     End If
  28.     curpos = 1
  29.     arrpos = UBound(StorageArray)
  30.     dpos = InStr(curpos, copy, delimiter)
  31.     Do Until dpos = 0
  32.         StorageArray(UBound(StorageArray)) = Mid$(copy, curpos, dpos - curpos)
  33.         ReDim _Preserve StorageArray(UBound(StorageArray) + 1) As String
  34.         curpos = dpos + Len(delimiter)
  35.         dpos = InStr(curpos, copy, delimiter)
  36.     Loop
  37.     StorageArray(UBound(StorageArray)) = Mid$(copy, curpos)
  38.     ReDim _Preserve StorageArray(UBound(StorageArray)) As String
* pipecomqb64.bas (Filesize: 7.63 KB, Downloads: 114)
Shuwatch!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: Bas Files Count and List by Recursive Algorithm
« Reply #16 on: November 08, 2021, 09:13:19 pm »
If I remember correctly, Steve had a bug in his code that would falsely identify some directories as files or vice-versa. Maybe that's why you have a difference in number of files found between his code and CMD.

That wasn't so much a bug as just cleaning up the code with newer QB64 commands.  When I first worked up the routine, we didn't have a _DIREXISTS and _FILEEXISTS, so we passed flags from the POSIX commands back to sort which was which.  They work, but are redundant and folks kept mixing up which flag was which.  (It's not a 0 or -1 flag, but instead a 1 or 2 value, so checking IF flag THEN... is always going to fail.)  Swapping to _DIREXISTS and _FILEEXISTS cleaned out those constants and keeps things  simple for people who wanted to tweak the routine for personal needs.

If I had to guess at the file count difference, I'd guess they're in a hidden directory (like the Recycle Bin) which dir is going to skip unless you set the /ah flags with it.  The POSIX routines will list those files without regards of the flags.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #17 on: November 08, 2021, 09:32:20 pm »
Thanks @SpriggsySpriggs

That gets me independent confirmation that we agree on at least a smallish Directory and SubDirectories, a couple tiny mods for ease of comparison (I just inserted pipecom.bas where you said include, so one file program):
Code: QB64: [Select]
  1. ' Spriggsy  ref: https://www.qb64.org/forum/index.php?topic=4360.msg137995#msg137995
  2. ' bplus mods a bit to compare to other counts  2021-11-08
  3.  
  4.  
  5. ReDim As String filelist(0)
  6. Dim GrandTotal As _Unsigned Long
  7.  
  8. path = _CWD$
  9. comm = pipecom_lite("PowerShell Get-ChildItem -Path \" + Chr$(34) + path + "\" + Chr$(34) + _
  10. " -Recurse -Force -ErrorAction SilentlyContinue -Attributes Hidden, " + _
  11. "!Hidden ^| Select-Object -ExpandProperty FullName ^| Sort-Object ^| Format-Table -AutoSize")
  12. String.Split comm, Chr$(10), filelist()
  13. Open path + "\Count and List Bas by Spriggsy.txt" For Output As #1
  14. For i = 1 To UBound(filelist)
  15.     If UCase$(Right$(filelist(i), 4)) = ".BAS" Then
  16.         GrandTotal = GrandTotal + 1
  17.         Print GrandTotal, filelist(i)
  18.         Print #1, filelist(i)
  19.     End If
  20. Print " Grandtotal ="; GrandTotal
  21.  
  22. ' Spriggsy's pipecom.bas include
  23. $If PIPECOM = UNDEFINED Then
  24.     $Let PIPECOM = TRUE
  25.     Function pipecom& (cmd As String, stdout As String, stderr As String)
  26.         stdout = "": stderr = ""
  27.         $If WIN Then
  28.             Type SECURITY_ATTRIBUTES
  29.                 As Long nLength
  30.                 $If 64BIT Then
  31.                     As Long padding
  32.                 $End If
  33.                 As _Offset lpSecurityDescriptor
  34.                 As Long bInheritHandle
  35.                 $If 64BIT Then
  36.                     As Long padding2
  37.                 $End If
  38.             End Type
  39.  
  40.             Type STARTUPINFO
  41.                 As Long cb
  42.                 $If 64BIT Then
  43.                     As Long padding
  44.                 $End If
  45.                 As _Offset lpReserved, lpDesktop, lpTitle
  46.                 As Long dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags
  47.                 As Integer wShowWindow, cbReserved2
  48.                 $If 64BIT Then
  49.                     As Long padding2
  50.                 $End If
  51.                 As _Offset lpReserved2, hStdInput, hStdOutput, hStdError
  52.             End Type
  53.  
  54.             Type PROCESS_INFORMATION
  55.                 As _Offset hProcess, hThread
  56.                 As Long dwProcessId
  57.                 $If 64BIT Then
  58.                     As Long padding
  59.                 $End If
  60.             End Type
  61.  
  62.             Const STARTF_USESTDHANDLES = &H00000100
  63.             Const CREATE_NO_WINDOW = &H8000000
  64.  
  65.             Const INFINITE = 4294967295
  66.             Const WAIT_FAILED = &HFFFFFFFF
  67.  
  68.             Declare CustomType Library
  69.                 Function CreatePipe& (ByVal hReadPipe As _Offset, Byval hWritePipe As _Offset, Byval lpPipeAttributes As _Offset, Byval nSize As Long)
  70.                 Function CreateProcess& (ByVal lpApplicationName As _Offset, Byval lpCommandLine As _Offset, Byval lpProcessAttributes As _Offset, Byval lpThreadAttributes As _Offset, Byval bInheritHandles As Integer, Byval dwCreationFlags As Long, Byval lpEnvironment As _Offset, Byval lpCurrentDirectory As _Offset, Byval lpStartupInfor As _Offset, Byval lpProcessInformation As _Offset)
  71.                 Function GetExitCodeProcess& (ByVal hProcess As _Offset, Byval lpExitCode As _Offset)
  72.                 Sub HandleClose Alias "CloseHandle" (ByVal hObject As _Offset)
  73.                 Function ReadFile& (ByVal hFile As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfBytesToRead As Long, Byval lpNumberOfBytesRead As _Offset, Byval lpOverlapped As _Offset)
  74.                 Function WaitForSingleObject& (ByVal hHandle As _Offset, Byval dwMilliseconds As Long)
  75.             End Declare
  76.  
  77.             Dim As Long ok: ok = 1
  78.             Dim As _Offset hStdOutPipeRead, hStdOutPipeWrite, hStdReadPipeError, hStdOutPipeError
  79.             Dim As SECURITY_ATTRIBUTES sa: sa.nLength = Len(sa): sa.lpSecurityDescriptor = 0: sa.bInheritHandle = 1
  80.  
  81.             If CreatePipe(_Offset(hStdOutPipeRead), _Offset(hStdOutPipeWrite), _Offset(sa), 0) = 0 Then
  82.                 pipecom = -1
  83.                 Exit Function
  84.             End If
  85.  
  86.             If CreatePipe(_Offset(hStdReadPipeError), _Offset(hStdOutPipeError), _Offset(sa), 0) = 0 Then
  87.                 pipecom = -1
  88.                 Exit Function
  89.             End If
  90.  
  91.             Dim As STARTUPINFO si
  92.             si.cb = Len(si)
  93.             si.dwFlags = STARTF_USESTDHANDLES
  94.             si.hStdError = hStdOutPipeError
  95.             si.hStdOutput = hStdOutPipeWrite
  96.             si.hStdInput = 0
  97.             Dim As PROCESS_INFORMATION procinfo
  98.             Dim As _Offset lpApplicationName
  99.             Dim As String fullcmd: fullcmd = "cmd /c " + cmd + Chr$(0)
  100.             Dim As String lpCommandLine: lpCommandLine = fullcmd
  101.             Dim As _Offset lpProcessAttributes, lpThreadAttributes
  102.             Dim As Integer bInheritHandles: bInheritHandles = 1
  103.             Dim As Long dwCreationFlags: dwCreationFlags = CREATE_NO_WINDOW
  104.             Dim As _Offset lpEnvironment, lpCurrentDirectory
  105.             ok = CreateProcess(lpApplicationName, _Offset(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, _Offset(si), _Offset(procinfo))
  106.  
  107.             If ok = 0 Then
  108.                 pipecom = -1
  109.                 Exit Function
  110.             End If
  111.  
  112.             HandleClose hStdOutPipeWrite
  113.             HandleClose hStdOutPipeError
  114.  
  115.             Dim As String buf: buf = Space$(4096 + 1)
  116.             Dim As Long dwRead
  117.             While ReadFile(hStdOutPipeRead, _Offset(buf), 4096, _Offset(dwRead), 0) <> 0 And dwRead > 0
  118.                 buf = Mid$(buf, 1, dwRead)
  119.                 GoSub RemoveChr13
  120.                 stdout = stdout + buf
  121.                 buf = Space$(4096 + 1)
  122.             Wend
  123.  
  124.             While ReadFile(hStdReadPipeError, _Offset(buf), 4096, _Offset(dwRead), 0) <> 0 And dwRead > 0
  125.                 buf = Mid$(buf, 1, dwRead)
  126.                 GoSub RemoveChr13
  127.                 stderr = stderr + buf
  128.                 buf = Space$(4096 + 1)
  129.             Wend
  130.  
  131.             Dim As Long exit_code, ex_stat
  132.             If WaitForSingleObject(procinfo.hProcess, INFINITE) <> WAIT_FAILED Then
  133.                 If GetExitCodeProcess(procinfo.hProcess, _Offset(exit_code)) Then
  134.                     ex_stat = 1
  135.                 End If
  136.             End If
  137.  
  138.             HandleClose hStdOutPipeRead
  139.             HandleClose hStdReadPipeError
  140.             If ex_stat = 1 Then
  141.                 pipecom = exit_code
  142.             Else
  143.                 pipecom = -1
  144.             End If
  145.  
  146.             Exit Function
  147.  
  148.             RemoveChr13:
  149.             Dim As Long j
  150.             j = InStr(buf, Chr$(13))
  151.             Do While j
  152.                 buf = Left$(buf, j - 1) + Mid$(buf, j + 1)
  153.                 j = InStr(buf, Chr$(13))
  154.             Loop
  155.             Return
  156.         $Else
  157.             Declare CustomType Library
  158.             Function popen%& (cmd As String, readtype As String)
  159.             Function feof& (ByVal stream As _Offset)
  160.             Function fgets$ (str As String, Byval n As Long, Byval stream As _Offset)
  161.             Function pclose& (ByVal stream As _Offset)
  162.             End Declare
  163.  
  164.             Declare Library
  165.             Function WEXITSTATUS& (ByVal stat_val As Long)
  166.             End Declare
  167.  
  168.             Dim As String pipecom_buffer
  169.             Dim As _Offset stream
  170.  
  171.             Dim buffer As String * 4096
  172.             If _FileExists("pipestderr") Then
  173.             Kill "pipestderr"
  174.             End If
  175.             stream = popen(cmd + " 2>pipestderr", "r")
  176.             If stream Then
  177.             While feof(stream) = 0
  178.             If fgets(buffer, 4096, stream) <> "" And feof(stream) = 0 Then
  179.             stdout = stdout + Mid$(buffer, 1, InStr(buffer, Chr$(0)) - 1)
  180.             End If
  181.             Wend
  182.             Dim As Long status, exit_code
  183.             status = pclose(stream)
  184.             exit_code = WEXITSTATUS(status)
  185.             If _FileExists("pipestderr") Then
  186.             Dim As Integer errfile
  187.             errfile = FreeFile
  188.             Open "pipestderr" For Binary As #errfile
  189.             If LOF(errfile) > 0 Then
  190.             stderr = Space$(LOF(errfile))
  191.             Get #errfile, , stderr
  192.             End If
  193.             Close #errfile
  194.             Kill "pipestderr"
  195.             End If
  196.             pipecom = exit_code
  197.             Else
  198.             pipecom = -1
  199.             End If
  200.         $End If
  201.  
  202.     Function pipecom_lite$ (cmd As String)
  203.         Dim As Long a
  204.         Dim As String stdout, stderr
  205.         a = pipecom(cmd, stdout, stderr)
  206.         If stderr <> "" Then
  207.             pipecom_lite = stderr
  208.         Else
  209.             pipecom_lite = stdout
  210.         End If
  211.  
  212. Sub String.Split (Expression As String, delimiter As String, StorageArray() As String)
  213.     Dim copy As String, p As Long, curpos As Long, arrpos As Long, dpos As Long
  214.     copy = Expression
  215.     If delimiter = " " Then
  216.         copy = RTrim$(LTrim$(copy))
  217.         p = InStr(copy, "  ")
  218.         While p > 0
  219.             copy = Mid$(copy, 1, p - 1) + Mid$(copy, p + 1)
  220.             p = InStr(copy, "  ")
  221.         Wend
  222.     End If
  223.     curpos = 1
  224.     arrpos = UBound(StorageArray)
  225.     dpos = InStr(curpos, copy, delimiter)
  226.     Do Until dpos = 0
  227.         StorageArray(UBound(StorageArray)) = Mid$(copy, curpos, dpos - curpos)
  228.         ReDim _Preserve StorageArray(UBound(StorageArray) + 1) As String
  229.         curpos = dpos + Len(delimiter)
  230.         dpos = InStr(curpos, copy, delimiter)
  231.     Loop
  232.     StorageArray(UBound(StorageArray)) = Mid$(copy, curpos)
  233.     ReDim _Preserve StorageArray(UBound(StorageArray)) As String
  234.  
  235.  

And here is how 4 compare, yours, Steve's, my fixed recursive and my non recursive (both ChDir):
 
2696 Files Agreement.PNG
 

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #18 on: November 08, 2021, 09:59:02 pm »
Quote
Umm...  But it is recursive.  GetAll calls itself until done...  😂😂

OK I have contracted the Dimster's blindness to recursive calls ;-)) so I better for sure make it the Best Answer!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #19 on: November 08, 2021, 11:49:16 pm »
Well I did pickup a discrepancy between file counts between Spriggsy's Method and Steve's. Out of 11,850 files on my Desktop Spriggy's Method picked up two more than Steve's.

Here is test code and the two files are from SmallBasic Directories:
 
Compare two Bas Lists.PNG


Could there be some character one method finds and the other doesn't?

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #20 on: November 09, 2021, 12:02:30 am »
Oh hey, I copy Steve's notes on GetAll and converted them into comments to paste into the source file:
Code: QB64: [Select]
  1. ' Note that Ive also tweaked the GetDir routine here so that it just does a
  2. ' simple file dump for us, without presorting files and directories for us.  (I
  3. ' also added the full path onto the file names, which I think is essential in a
  4. ' case of recursion so you can tell what the heck it found where...)  This has
  5. ' no issues on my PC starting at the root drive and working its way down into
  6. ' the various subdirectories, and as far as I can tell its not missing anything.
  7. '  Note 2:  This doesnt autoclear the old directory listing and builds upon it
  8. ' as you call it.  If youre going to call the routine multiple times, reset your
  9. ' listing with a fresh ReDim As String Dir(0) before making the call to GetAll.
  10.  

I know these notes important because I think I missed something about using getLists that required me to ChDir to get a proper listing of files and directories of the Current Working Directory, some time ago.

@SMcNeill  if your up for a little puzzle here is GetLists that I have, that works pretty good when I ChDir to the directory I was to get the lists.
Code: QB64: [Select]
  1. Sub GetLists (SearchDirectory As String, DirList() As String, FileList() As String)
  2.     ' Thanks SNcNeill ! for a cross platform method to get file and directory lists
  3.     'put this block in main code section of your program close to top
  4.     '' direntry.h needs to be in QB64 folder '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  5.     'DECLARE CUSTOMTYPE LIBRARY ".\direntry"
  6.     '    FUNCTION load_dir& (s AS STRING)
  7.     '    FUNCTION has_next_entry& ()
  8.     '    SUB close_dir ()
  9.     '    SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  10.     'END DECLARE
  11.  
  12.     Const IS_DIR = 1
  13.     Const IS_FILE = 2
  14.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  15.     Dim nam$
  16.     ReDim _Preserve DirList(100), FileList(100)
  17.     DirCount = 0: FileCount = 0
  18.  
  19.     If load_dir(SearchDirectory + Chr$(0)) Then
  20.         Do
  21.             length = has_next_entry
  22.             If length > -1 Then
  23.                 nam$ = Space$(length)
  24.                 get_next_entry nam$, flags, file_size
  25.                 If (flags And IS_DIR) Then
  26.                     DirCount = DirCount + 1
  27.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 100)
  28.                     DirList(DirCount) = nam$
  29.                 ElseIf (flags And IS_FILE) Then
  30.                     FileCount = FileCount + 1
  31.                     If FileCount > UBound(FileList) Then ReDim _Preserve FileList(UBound(FileList) + 100)
  32.                     FileList(FileCount) = nam$
  33.                 End If
  34.             End If
  35.         Loop Until length = -1
  36.         'close_dir 'move to after end if  might correct the multi calls problem
  37.     Else
  38.     End If
  39.     close_dir 'this  might correct the multi calls problem
  40.  
  41.     ReDim _Preserve DirList(DirCount)
  42.     ReDim _Preserve FileList(FileCount)
  43.  

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: Bas Files Count and List by Recursive Algorithm
« Reply #21 on: November 09, 2021, 04:34:09 am »
Those 2 files that were missed, can you see what attributes they have set?  System?  Hidden?  Archived?  Or something  else odd?  Do they flag properly with _DIREXISTS?  I'm curious why it may have skipped them.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: Bas Files Count and List by Recursive Algorithm
« Reply #22 on: November 09, 2021, 06:22:25 am »
See if this works without missing any files for you:

Code: QB64: [Select]
  1. ReDim As String Dir(0), File(0)
  2.  
  3. GetLists "D:\repo\qb64", Dir(), File()
  4. For i = 1 To UBound(Dir)
  5.     Print Dir(i),
  6. For i = 1 To UBound(File)
  7.     Print File(i),
  8.  
  9.  
  10. Sub GetLists (SearchDirectory As String, DirList() As String, FileList() As String)
  11.     ' Thanks SNcNeill ! for a cross platform method to get file and directory lists
  12.     'put this block in main code section of your program close to top
  13.     '' direntry.h needs to be in QB64 folder '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  14.     Declare CustomType Library ".\direntry"
  15.         Function load_dir& (s As String)
  16.         Function has_next_entry& ()
  17.         Sub close_dir ()
  18.         Sub get_next_entry (s As String, flags As Long, file_size As Long)
  19.     End Declare
  20.  
  21.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  22.     Dim nam$, slash$
  23.     ReDim _Preserve DirList(100), FileList(100)
  24.     DirCount = 0: FileCount = 0
  25.     $If WIN Then
  26.         slash$ = "\"
  27.     $Else
  28.         slash$ = "/"
  29.     $End If
  30.     If Right$(SearchDirectory$, 1) <> "/" And Right$(SearchDirectory$, 1) <> "\" Then SearchDirectory$ = SearchDirectory$ + slash$
  31.  
  32.     If load_dir(SearchDirectory + Chr$(0)) Then
  33.         Do
  34.             length = has_next_entry
  35.             If length > -1 Then
  36.                 nam$ = Space$(length)
  37.                 get_next_entry nam$, flags, file_size
  38.                 If _DirExists(SearchDirectory + nam$) Then
  39.                     DirCount = DirCount + 1
  40.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 100)
  41.                     DirList(DirCount) = nam$
  42.                 ElseIf _FileExists(SearchDirectory + nam$) Then
  43.                     FileCount = FileCount + 1
  44.                     If FileCount > UBound(FileList) Then ReDim _Preserve FileList(UBound(FileList) + 100)
  45.                     FileList(FileCount) = nam$
  46.                 Else 'This else should never actually trigger
  47.                     Print "Unknown file found: "; SearchDirectory; slash$; nam$, _DirExists(nam$)
  48.                     Sleep
  49.                 End If
  50.             End If
  51.         Loop Until length = -1
  52.     End If
  53.     close_dir
  54.  
  55.     ReDim _Preserve DirList(DirCount)
  56.     ReDim _Preserve FileList(FileCount)
  57.  
  58.  

This removes the POSIX flag checking and relies on QB64's native _DIREXISTS and _FILEEXISTS to tell us which is a file and which is a directory.  If it finds the files but misses identifying the files completely, you'll get a nice BEEP and message.  OF course, if it misses the files completely, they just won't show up anywhere for us...  :P
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #23 on: November 09, 2021, 10:54:18 am »
Those 2 files that were missed, can you see what attributes they have set?  System?  Hidden?  Archived?  Or something  else odd?  Do they flag properly with _DIREXISTS?  I'm curious why it may have skipped them.

OK turns out the "files" missed were actually folders that ended in ".bas".

So both methods failed to recognize them as folders, open and catch the  .bas file inside each, or did catch what was inside both and Spriggsy's just called the folder a file too. Checking on that next...

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: Bas Files Count and List by Recursive Algorithm
« Reply #24 on: November 09, 2021, 11:28:13 am »
OK turns out the "files" missed were actually folders that ended in ".bas".

So both methods failed to recognize them as folders, open and catch the  .bas file inside each, or did catch what was inside both and Spriggsy's just called the folder a file too. Checking on that next...

Mine wouldn't count a folder as it reports folders as ending with a \.

C:\
C:\temp\
C:\temp\QB64.bas\

The last 4 digits are bas\, not .bas. 

They were found but not counted for that reason.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #25 on: November 09, 2021, 11:39:00 am »
That's probably it Steve, I am sidetracked with -2^-3^-4 nice test for Oh Interpreter, confirming what you just said next...

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #26 on: November 09, 2021, 12:36:59 pm »
Yes, I reconciled the difference in List counts by changing my mod of Spriggsy's code to make sure it's a file we are adding to the list, his code just dumps the whole mess files and folders and I lazily just picked out ones that ended in .BAS

Here is my fix of Spriggsy's to count .Bas Files only:
Code: QB64: [Select]
  1. ' Spriggsy  ref: https://www.qb64.org/forum/index.php?topic=4360.msg137995#msg137995
  2. ' bplus mods a bit to comapare to other counts  2021-11-08
  3.  
  4.  
  5. ReDim As String filelist(0)
  6. Dim GrandTotal As _Unsigned Long
  7.  
  8. path = "C:\Users\marka\Desktop" ' <<<<<< set the directory to do here
  9.  
  10.  
  11. comm = pipecom_lite("PowerShell Get-ChildItem -Path \" + Chr$(34) + path + "\" + Chr$(34) + _
  12. " -Recurse -Force -ErrorAction SilentlyContinue -Attributes Hidden, " + _
  13. "!Hidden ^| Select-Object -ExpandProperty FullName ^| Sort-Object ^| Format-Table -AutoSize")
  14. String.Split comm, Chr$(10), filelist()
  15. Open path + "\Count and List Bas by Spriggsy.txt" For Output As #1
  16. For i = 1 To UBound(filelist)
  17.     If UCase$(Right$(filelist(i), 4)) = ".BAS" Then ' bplus mod to count .Bas files as asked for in thread
  18.  
  19.         ' bplus changed 2021-11-09 This reconciles the difference I was getting in file counts
  20.         ' if filelist contains folders too how do you tell?
  21.         If _FileExists(filelist(i)) Then ' that should do it and increase the time too
  22.             GrandTotal = GrandTotal + 1 ' now count the FILE!
  23.             Print GrandTotal, filelist(i)
  24.             Print #1, filelist(i)
  25.         End If
  26.  
  27.  
  28.     End If
  29. Print " Grand Total ="; GrandTotal
  30.  
  31. ' Spriggsy's pipecom.bas include
  32. $If PIPECOM = UNDEFINED Then
  33.     $Let PIPECOM = TRUE
  34.     Function pipecom& (cmd As String, stdout As String, stderr As String)
  35.         stdout = "": stderr = ""
  36.         $If WIN Then
  37.             Type SECURITY_ATTRIBUTES
  38.                 As Long nLength
  39.                 $If 64BIT Then
  40.                     As Long padding
  41.                 $End If
  42.                 As _Offset lpSecurityDescriptor
  43.                 As Long bInheritHandle
  44.                 $If 64BIT Then
  45.                     As Long padding2
  46.                 $End If
  47.             End Type
  48.  
  49.             Type STARTUPINFO
  50.                 As Long cb
  51.                 $If 64BIT Then
  52.                     As Long padding
  53.                 $End If
  54.                 As _Offset lpReserved, lpDesktop, lpTitle
  55.                 As Long dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags
  56.                 As Integer wShowWindow, cbReserved2
  57.                 $If 64BIT Then
  58.                     As Long padding2
  59.                 $End If
  60.                 As _Offset lpReserved2, hStdInput, hStdOutput, hStdError
  61.             End Type
  62.  
  63.             Type PROCESS_INFORMATION
  64.                 As _Offset hProcess, hThread
  65.                 As Long dwProcessId
  66.                 $If 64BIT Then
  67.                     As Long padding
  68.                 $End If
  69.             End Type
  70.  
  71.             Const STARTF_USESTDHANDLES = &H00000100
  72.             Const CREATE_NO_WINDOW = &H8000000
  73.  
  74.             Const INFINITE = 4294967295
  75.             Const WAIT_FAILED = &HFFFFFFFF
  76.  
  77.             Declare CustomType Library
  78.                 Function CreatePipe& (ByVal hReadPipe As _Offset, Byval hWritePipe As _Offset, Byval lpPipeAttributes As _Offset, Byval nSize As Long)
  79.                 Function CreateProcess& (ByVal lpApplicationName As _Offset, Byval lpCommandLine As _Offset, Byval lpProcessAttributes As _Offset, Byval lpThreadAttributes As _Offset, Byval bInheritHandles As Integer, Byval dwCreationFlags As Long, Byval lpEnvironment As _Offset, Byval lpCurrentDirectory As _Offset, Byval lpStartupInfor As _Offset, Byval lpProcessInformation As _Offset)
  80.                 Function GetExitCodeProcess& (ByVal hProcess As _Offset, Byval lpExitCode As _Offset)
  81.                 Sub HandleClose Alias "CloseHandle" (ByVal hObject As _Offset)
  82.                 Function ReadFile& (ByVal hFile As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfBytesToRead As Long, Byval lpNumberOfBytesRead As _Offset, Byval lpOverlapped As _Offset)
  83.                 Function WaitForSingleObject& (ByVal hHandle As _Offset, Byval dwMilliseconds As Long)
  84.             End Declare
  85.  
  86.             Dim As Long ok: ok = 1
  87.             Dim As _Offset hStdOutPipeRead, hStdOutPipeWrite, hStdReadPipeError, hStdOutPipeError
  88.             Dim As SECURITY_ATTRIBUTES sa: sa.nLength = Len(sa): sa.lpSecurityDescriptor = 0: sa.bInheritHandle = 1
  89.  
  90.             If CreatePipe(_Offset(hStdOutPipeRead), _Offset(hStdOutPipeWrite), _Offset(sa), 0) = 0 Then
  91.                 pipecom = -1
  92.                 Exit Function
  93.             End If
  94.  
  95.             If CreatePipe(_Offset(hStdReadPipeError), _Offset(hStdOutPipeError), _Offset(sa), 0) = 0 Then
  96.                 pipecom = -1
  97.                 Exit Function
  98.             End If
  99.  
  100.             Dim As STARTUPINFO si
  101.             si.cb = Len(si)
  102.             si.dwFlags = STARTF_USESTDHANDLES
  103.             si.hStdError = hStdOutPipeError
  104.             si.hStdOutput = hStdOutPipeWrite
  105.             si.hStdInput = 0
  106.             Dim As PROCESS_INFORMATION procinfo
  107.             Dim As _Offset lpApplicationName
  108.             Dim As String fullcmd: fullcmd = "cmd /c " + cmd + Chr$(0)
  109.             Dim As String lpCommandLine: lpCommandLine = fullcmd
  110.             Dim As _Offset lpProcessAttributes, lpThreadAttributes
  111.             Dim As Integer bInheritHandles: bInheritHandles = 1
  112.             Dim As Long dwCreationFlags: dwCreationFlags = CREATE_NO_WINDOW
  113.             Dim As _Offset lpEnvironment, lpCurrentDirectory
  114.             ok = CreateProcess(lpApplicationName, _Offset(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, _Offset(si), _Offset(procinfo))
  115.  
  116.             If ok = 0 Then
  117.                 pipecom = -1
  118.                 Exit Function
  119.             End If
  120.  
  121.             HandleClose hStdOutPipeWrite
  122.             HandleClose hStdOutPipeError
  123.  
  124.             Dim As String buf: buf = Space$(4096 + 1)
  125.             Dim As Long dwRead
  126.             While ReadFile(hStdOutPipeRead, _Offset(buf), 4096, _Offset(dwRead), 0) <> 0 And dwRead > 0
  127.                 buf = Mid$(buf, 1, dwRead)
  128.                 GoSub RemoveChr13
  129.                 stdout = stdout + buf
  130.                 buf = Space$(4096 + 1)
  131.             Wend
  132.  
  133.             While ReadFile(hStdReadPipeError, _Offset(buf), 4096, _Offset(dwRead), 0) <> 0 And dwRead > 0
  134.                 buf = Mid$(buf, 1, dwRead)
  135.                 GoSub RemoveChr13
  136.                 stderr = stderr + buf
  137.                 buf = Space$(4096 + 1)
  138.             Wend
  139.  
  140.             Dim As Long exit_code, ex_stat
  141.             If WaitForSingleObject(procinfo.hProcess, INFINITE) <> WAIT_FAILED Then
  142.                 If GetExitCodeProcess(procinfo.hProcess, _Offset(exit_code)) Then
  143.                     ex_stat = 1
  144.                 End If
  145.             End If
  146.  
  147.             HandleClose hStdOutPipeRead
  148.             HandleClose hStdReadPipeError
  149.             If ex_stat = 1 Then
  150.                 pipecom = exit_code
  151.             Else
  152.                 pipecom = -1
  153.             End If
  154.  
  155.             Exit Function
  156.  
  157.             RemoveChr13:
  158.             Dim As Long j
  159.             j = InStr(buf, Chr$(13))
  160.             Do While j
  161.                 buf = Left$(buf, j - 1) + Mid$(buf, j + 1)
  162.                 j = InStr(buf, Chr$(13))
  163.             Loop
  164.             Return
  165.         $Else
  166.             Declare CustomType Library
  167.             Function popen%& (cmd As String, readtype As String)
  168.             Function feof& (ByVal stream As _Offset)
  169.             Function fgets$ (str As String, Byval n As Long, Byval stream As _Offset)
  170.             Function pclose& (ByVal stream As _Offset)
  171.             End Declare
  172.  
  173.             Declare Library
  174.             Function WEXITSTATUS& (ByVal stat_val As Long)
  175.             End Declare
  176.  
  177.             Dim As String pipecom_buffer
  178.             Dim As _Offset stream
  179.  
  180.             Dim buffer As String * 4096
  181.             If _FileExists("pipestderr") Then
  182.             Kill "pipestderr"
  183.             End If
  184.             stream = popen(cmd + " 2>pipestderr", "r")
  185.             If stream Then
  186.             While feof(stream) = 0
  187.             If fgets(buffer, 4096, stream) <> "" And feof(stream) = 0 Then
  188.             stdout = stdout + Mid$(buffer, 1, InStr(buffer, Chr$(0)) - 1)
  189.             End If
  190.             Wend
  191.             Dim As Long status, exit_code
  192.             status = pclose(stream)
  193.             exit_code = WEXITSTATUS(status)
  194.             If _FileExists("pipestderr") Then
  195.             Dim As Integer errfile
  196.             errfile = FreeFile
  197.             Open "pipestderr" For Binary As #errfile
  198.             If LOF(errfile) > 0 Then
  199.             stderr = Space$(LOF(errfile))
  200.             Get #errfile, , stderr
  201.             End If
  202.             Close #errfile
  203.             Kill "pipestderr"
  204.             End If
  205.             pipecom = exit_code
  206.             Else
  207.             pipecom = -1
  208.             End If
  209.         $End If
  210.  
  211.     Function pipecom_lite$ (cmd As String)
  212.         Dim As Long a
  213.         Dim As String stdout, stderr
  214.         a = pipecom(cmd, stdout, stderr)
  215.         If stderr <> "" Then
  216.             pipecom_lite = stderr
  217.         Else
  218.             pipecom_lite = stdout
  219.         End If
  220.  
  221. Sub String.Split (Expression As String, delimiter As String, StorageArray() As String)
  222.     Dim copy As String, p As Long, curpos As Long, arrpos As Long, dpos As Long
  223.     copy = Expression
  224.     If delimiter = " " Then
  225.         copy = RTrim$(LTrim$(copy))
  226.         p = InStr(copy, "  ")
  227.         While p > 0
  228.             copy = Mid$(copy, 1, p - 1) + Mid$(copy, p + 1)
  229.             p = InStr(copy, "  ")
  230.         Wend
  231.     End If
  232.     curpos = 1
  233.     arrpos = UBound(StorageArray)
  234.     dpos = InStr(curpos, copy, delimiter)
  235.     Do Until dpos = 0
  236.         StorageArray(UBound(StorageArray)) = Mid$(copy, curpos, dpos - curpos)
  237.         ReDim _Preserve StorageArray(UBound(StorageArray) + 1) As String
  238.         curpos = dpos + Len(delimiter)
  239.         dpos = InStr(curpos, copy, delimiter)
  240.     Loop
  241.     StorageArray(UBound(StorageArray)) = Mid$(copy, curpos)
  242.     ReDim _Preserve StorageArray(UBound(StorageArray)) As String
  243.  
  244.  

BTW my recursive, now fixed, and non recursive get same results as Steve and Spriggsy's method but they start printing out immediately the list and count.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #27 on: November 09, 2021, 12:42:23 pm »
Ah! The slashes, can someone remind me which one Linux uses?

I don't think it matters for Windows.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: Bas Files Count and List by Recursive Algorithm
« Reply #28 on: November 09, 2021, 12:51:10 pm »
Ah! The slashes, can someone remind me which one Linux uses?

I don't think it matters for Windows.

    $If WIN Then
        slash$ = "\"
    $Else
        slash$ = "/"
    $End If
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Bas Files Count and List by Recursive Algorithm
« Reply #29 on: November 09, 2021, 01:08:13 pm »
See if this works without missing any files for you:

Code: QB64: [Select]
  1. ReDim As String Dir(0), File(0)
  2.  
  3. GetLists "D:\repo\qb64", Dir(), File()
  4. For i = 1 To UBound(Dir)
  5.     Print Dir(i),
  6. For i = 1 To UBound(File)
  7.     Print File(i),
  8.  
  9.  
  10. Sub GetLists (SearchDirectory As String, DirList() As String, FileList() As String)
  11.     ' Thanks SNcNeill ! for a cross platform method to get file and directory lists
  12.     'put this block in main code section of your program close to top
  13.     '' direntry.h needs to be in QB64 folder '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  14.     Declare CustomType Library ".\direntry"
  15.         Function load_dir& (s As String)
  16.         Function has_next_entry& ()
  17.         Sub close_dir ()
  18.         Sub get_next_entry (s As String, flags As Long, file_size As Long)
  19.     End Declare
  20.  
  21.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  22.     Dim nam$, slash$
  23.     ReDim _Preserve DirList(100), FileList(100)
  24.     DirCount = 0: FileCount = 0
  25.     $If WIN Then
  26.         slash$ = "\"
  27.     $Else
  28.         slash$ = "/"
  29.     $End If
  30.     If Right$(SearchDirectory$, 1) <> "/" And Right$(SearchDirectory$, 1) <> "\" Then SearchDirectory$ = SearchDirectory$ + slash$
  31.  
  32.     If load_dir(SearchDirectory + Chr$(0)) Then
  33.         Do
  34.             length = has_next_entry
  35.             If length > -1 Then
  36.                 nam$ = Space$(length)
  37.                 get_next_entry nam$, flags, file_size
  38.                 If _DirExists(SearchDirectory + nam$) Then
  39.                     DirCount = DirCount + 1
  40.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 100)
  41.                     DirList(DirCount) = nam$
  42.                 ElseIf _FileExists(SearchDirectory + nam$) Then
  43.                     FileCount = FileCount + 1
  44.                     If FileCount > UBound(FileList) Then ReDim _Preserve FileList(UBound(FileList) + 100)
  45.                     FileList(FileCount) = nam$
  46.                 Else 'This else should never actually trigger
  47.                     Print "Unknown file found: "; SearchDirectory; slash$; nam$, _DirExists(nam$)
  48.                     Sleep
  49.                 End If
  50.             End If
  51.         Loop Until length = -1
  52.     End If
  53.     close_dir
  54.  
  55.     ReDim _Preserve DirList(DirCount)
  56.     ReDim _Preserve FileList(FileCount)
  57.  
  58.  

This removes the POSIX flag checking and relies on QB64's native _DIREXISTS and _FILEEXISTS to tell us which is a file and which is a directory.  If it finds the files but misses identifying the files completely, you'll get a nice BEEP and message.  OF course, if it misses the files completely, they just won't show up anywhere for us...  :P

So this should work as GetLists replacement in the OP?
I will check and looks like Linux still being taken into consideration.