QB64.org Forum

Active Forums => Programs => Topic started by: bplus on November 04, 2021, 11:05:25 pm

Title: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 04, 2021, 11:05:25 pm
Here is something that might actually be useful, temperamental as hell but I finally got results after several compromises:
 
I had to ChDir to the search Directory to get good results from the GetLists Subroutine. I had never noted that before but it turns out I did do that for Oh Interpreter and other places I successfully used the cross platform files retriever. Dang! It's so frustrating to think you've got something nailed down, go to use it 6 months later and stumble into problems.

Then Windows wont let me ChDir in allot of places where _DirExists so I couldn't just start looking from C:\ for bas files nor even from C:\users\me and my stuff! blah!
But it did work OK from my Desktop and my Downloads Folders where I learn tonight over 10,000 bas files reside.

Takes about 1 sec to do the downloads folder that had 2115 bas files.
Code: QB64: [Select]
  1. _Title "Count BAS files from a start Directory" ' b+ 2021-11-04
  2.  
  3. ' direntry.h needs to be in QB64.exe folder, see below for a commented copy
  4.     Function load_dir& (s As String)
  5.     Function has_next_entry& ()
  6.     Sub close_dir ()
  7.     Sub get_next_entry (s As String, flags As Long, file_size As Long)
  8.  
  9. ReDim Shared BasList$(10000) 'store our Bas pathed Files here
  10. Dim Shared GrandTotal As _Unsigned Long ' ha, ha not that many!
  11.  
  12. FullPathedDir$ = "C:\Users\marka\downloads" ' <<<<<<<<<<<<<  change this line to top directory of your search for
  13. '                                                            best results use places where Windows lets you write bas files.
  14.  
  15. FindAndCountBasFileFrom FullPathedDir$
  16. Print " Grand Total .bas ="; GrandTotal
  17. Open FullPathedDir$ + "\Bas List.txt" For Output As #1
  18. For i = 1 To GrandTotal
  19.     Print #1, BasList$(i)
  20. Print FullPathedDir$ + "\Bas List.txt file is ready."
  21.  
  22.  
  23. Sub FindAndCountBasFileFrom (startDir$)
  24.     If startDir$ <> "." And startDir$ <> ".." And _Trim$(startDir$) <> "" Then
  25.         If Right$(startDir$, 1) <> "\" Then startDir$ = startDir$ + "\"
  26.         If _DirExists(startDir$) Then ChDir startDir$ Else Exit Sub ' >>> There are allot of places where dir exists but cant CD to go!
  27.         'Print "Changing Directory to "; startDir$; ""
  28.         ReDim ds(0) As String, fs(0) As String
  29.         GetLists startDir$, ds(), fs()
  30.         'Print startDir$ + " .bas Files:"
  31.         For i = LBound(fs) To UBound(fs)
  32.             If UCase$(Right$(fs(i), 4)) = ".BAS" Then
  33.                 GrandTotal = GrandTotal + 1
  34.                 Print GrandTotal, startDir$ + fs(i)
  35.                 BasList$(GrandTotal) = startDir$ + fs(i)
  36.                 'If i Mod 20 = 19 Then Print "Press any to cont..": Sleep
  37.             End If
  38.         Next
  39.         'Print
  40.         'Print startDir$ + " Sub-Directories:  zzz...": Sleep
  41.         For j = LBound(ds) To UBound(ds)
  42.             If ds(j) <> "." And ds(j) <> ".." And _Trim$(ds(j)) <> "" Then
  43.                 newD$ = startDir$ + ds(j)
  44.                 'Print "Press any to FindAndCountBasFileFrom " + newD$ + "... zzz": Sleep
  45.                 FindAndCountBasFileFrom newD$
  46.             End If
  47.         Next
  48.         'Print "Press any to cont...": Sleep
  49.     End If
  50.  
  51. Sub GetLists (SearchDirectory As String, DirList() As String, FileList() As String)
  52.  
  53.     '   !!! For this sub to work the _CWD has to be the Search Directory! !!!
  54.  
  55.     ' Thanks SNcNeill ! for a cross platform method to get file and directory lists
  56.     'put this block in main code section of your program close to top
  57.     '' direntry.h needs to be in QB64 folder '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  58.     'DECLARE CUSTOMTYPE LIBRARY ".\direntry"
  59.     '    FUNCTION load_dir& (s AS STRING)
  60.     '    FUNCTION has_next_entry& ()
  61.     '    SUB close_dir ()
  62.     '    SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  63.     'END DECLARE
  64.  
  65.     Const IS_DIR = 1
  66.     Const IS_FILE = 2
  67.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  68.     Dim nam$
  69.     ReDim _Preserve DirList(100), FileList(100)
  70.     DirCount = 0: FileCount = 0
  71.  
  72.     If load_dir(SearchDirectory + Chr$(0)) Then
  73.         Do
  74.             length = has_next_entry
  75.             If length > -1 Then
  76.                 nam$ = Space$(length)
  77.                 get_next_entry nam$, flags, file_size
  78.                 If (flags And IS_DIR) Then
  79.                     DirCount = DirCount + 1
  80.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 1000)
  81.                     DirList(DirCount) = nam$
  82.                 ElseIf (flags And IS_FILE) Then
  83.                     FileCount = FileCount + 1
  84.                     If FileCount > UBound(FileList) Then
  85.                         ReDim _Preserve FileList(UBound(FileList) + 1000)
  86.                         'Print Left$(SearchDirectory, Len(searchDiretory) - 1)
  87.                         'getting allot of wierd misfires
  88.                         If Left$(SearchDirectory, Len(searchDiretory) - 1) = "C:\" Then Exit Sub
  89.                     End If
  90.                     FileList(FileCount) = nam$
  91.                 End If
  92.             End If
  93.         Loop Until length = -1
  94.         'close_dir 'move to after end if  might correct the multi calls problem
  95.     Else
  96.     End If
  97.     close_dir 'this  might correct the multi calls problem
  98.  
  99.     ReDim _Preserve DirList(DirCount)
  100.     ReDim _Preserve FileList(FileCount)
  101.  
  102. '  Remove comments below and save as direntry.h
  103. ' in your QB64.exe folder if you don't have it already
  104. '=============================================================
  105.  
  106. '#include <dirent.h>
  107. '#include <sys/stat.h>
  108. '#include <unistd.h>
  109.  
  110. 'const int IS_DIR_FLAG = 1, IS_FILE_FLAG = 2;
  111.  
  112. 'DIR *pdir;
  113. 'struct dirent *next_entry;
  114. 'struct stat statbuf1;
  115.  
  116. 'char current_dir[FILENAME_MAX];
  117. '#ifdef QB64_WINDOWS
  118. '  #define GetCurrentDir _getcwd
  119. '#else
  120. '  #define GetCurrentDir getcwd
  121. '#endif
  122.  
  123. 'int load_dir (char * path) {
  124. '  struct dirent *pent;
  125. '  struct stat statbuf1;
  126. '//Open current directory
  127. 'pdir = opendir(path);
  128. 'if (!pdir) {
  129. 'return 0; //Didn't open
  130. '}
  131. 'return -1;
  132. '}
  133.  
  134. 'int has_next_entry () {
  135. '  next_entry = readdir(pdir);
  136. '  if (next_entry == NULL) return -1;
  137.  
  138. '  stat(next_entry->d_name, &statbuf1);
  139. '  return strlen(next_entry->d_name);
  140. '}
  141.  
  142. 'void get_next_entry (char * nam, int * flags, int * file_size) {
  143. '  strcpy(nam, next_entry->d_name);
  144. '  if (S_ISDIR(statbuf1.st_mode)) {
  145. '    *flags = IS_DIR_FLAG;
  146. '  } else {
  147. '    *flags = IS_FILE_FLAG;
  148. '  }
  149. '  *file_size = statbuf1.st_size;
  150. '  return ;
  151. '}
  152.  
  153. 'void close_dir () {
  154. '  closedir(pdir);
  155. '  pdir = NULL;
  156. '  return ;
  157. '}
  158.  
  159. 'int current_dir_length () {
  160. '  GetCurrentDir(current_dir, sizeof(current_dir));
  161. '  return strlen(current_dir);
  162. '}
  163.  
  164. 'void get_current_dir(char *dir) {
  165. '  memcpy(dir, current_dir, strlen(current_dir));
  166. '  return ;
  167. '}
  168.  
  169.  

Theoretically this is cross platform, would non Windows users verify?

So how many bas files have you got laying around your computer?

Update: This code has nasty error in it and is missing allot of folders.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: Petr on November 05, 2021, 06:43:02 am
Quote
So how many bas files have you got laying around your computer?

Hi, just for curiosity - 7717 (but not all are my own sources)
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 05, 2021, 09:10:27 am
Thanks Petr, I can assume it's working for you too on Windows probably.

Yeah most my bas files are probably not mine, some are yours.

For Linux I am wondering if the slash is pointed the right way for paths, supposedly for Windows it doesn't matter.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 08, 2021, 12:50:26 pm
oops!

I started digging into a smaller directory count and found it missing folders from a certain point onward.

I don't know if GetLists is still buggy or just trying to use it for this recursive code.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: Cobalt on November 08, 2021, 02:02:43 pm
finds 2 files(in drive d:\, 3 files in drive E:\ and 0 files in c:\) then errors out with "PATH NOT FOUND" error.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 08, 2021, 02:31:23 pm
Well I never intended it to jump drives, probably should have. Do you think ChDir (without change drives also like we needed in DOS) could keep up with that?

GetLists routine needs to be in the Directory it's finding files and folders from = the compromise I am willing to accept to be able to do cross platform. No one else, including Spriggsy with PipeCom, has been able to produce simple File and Directory lists that one can use for navigation or as I am trying here: counting Bas Files in a directory and all it's subdirectories. (It's kinda odd because the IDE is doing it and very well, thank very much!, and I am assuming it uses something like the GetLists routine.)

Currently I am attempting a fix with Stack Techniques. I am pretty sure whatever is broken is from trying it with recursive calls. I am having no trouble navigating and finding files with GetLists in both TextFetch and the Oh Interpreter even after the QB64 v2.0 update, I've run both now with QB64 v 2.0. (Did have to fix Oh Interpreter in a couple of places.)
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: TempodiBasic on November 08, 2021, 02:37:05 pm
Hi Bplus
here my feedback from Windows 8.1 pro

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

as you can see your application found more file than CMD : DIR *.BAS /S

What do I to think about it?
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill on November 08, 2021, 03:10:21 pm
Why use CHDIR?  There's a lot of hidden and system only directories which won't let you into them, but you can still get a directory listing from them.

For example, this is a recursive routine which gets ALL the file and directories from a folder and its subfolders -- warning, this might take a while to run if you choose a directory with a zillion files such as the root directory of C:\.  If you get tired of waiting, hit ESC and it'll dump what it's found so far to disk to and screen for you to view.

Code: QB64: [Select]
  1. _Title "Count BAS files from a start Directory" ' b+ 2021-11-04
  2.  
  3. ReDim Shared BasList$(10000) 'store our Bas pathed Files here
  4.  
  5. ReDim As String Dir(0)
  6.  
  7. GetAll "C:\", Dir()
  8. Open "DirList.txt" For Output As #1
  9. For i = 1 To UBound(Dir)
  10.     If _DirExists(Dir(i)) Then Color 7 Else Color 15
  11.     Print #1, Dir(i)
  12.     Print i, Dir(i)
  13.  
  14.  
  15. Sub GetAll (Path As String, DirList() As String)
  16.     If _KeyDown(27) Then Exit Sub
  17.     ReDim tempDir(0) As String
  18.     GetDir Path, tempDir()
  19.     For i = 1 To UBound(tempDir)
  20.         DirCount = UBound(DirList) + 1
  21.         ReDim _Preserve DirList(DirCount)
  22.         DirList(DirCount) = tempDir(i)
  23.         If _DirExists(tempDir(i)) And Right$(tempDir(i), 1) <> "." Then
  24.             DirList(DirCount) = DirList(DirCount) + "\"
  25.             GetAll tempDir(i), DirList()
  26.         End If
  27.  
  28.     Next
  29.  
  30.  
  31. Sub GetDir (SearchDirectory As String, DirList() As String)
  32.     Declare CustomType Library ".\direntry"
  33.         Function load_dir& (s As String)
  34.         Function has_next_entry& ()
  35.         Sub close_dir ()
  36.         Sub get_next_entry (s As String, flags As Long, file_size As Long)
  37.     End Declare
  38.  
  39.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  40.     Dim nam$
  41.     DirCount = UBound(DirList)
  42.     If Right$(SearchDirectory, 1) = "\" Or Right$(SearchDirectory, 1) = "/" Then SearchDirectory = Left$(SearchDirectory, Len(SearchDirectory) - 1)
  43.     If load_dir(SearchDirectory + Chr$(0)) Then
  44.         Do
  45.             length = has_next_entry
  46.             If length > -1 Then
  47.                 nam$ = Space$(length)
  48.                 get_next_entry nam$, flags, file_size
  49.                 If Right$(nam$, 1) <> "." Then
  50.                     DirCount = DirCount + 1
  51.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 1000)
  52.                     DirList(DirCount) = SearchDirectory + "\" + nam$
  53.                 End If
  54.             End If
  55.         Loop Until length = -1
  56.     End If
  57.     close_dir
  58.     ReDim _Preserve DirList(DirCount)
  59.  
  60. '  Remove comments below and save as direntry.h
  61. ' in your QB64.exe folder if you don't have it already
  62. '=============================================================
  63.  
  64. '#include <dirent.h>
  65. '#include <sys/stat.h>
  66. '#include <unistd.h>
  67.  
  68. 'const int IS_DIR_FLAG = 1, IS_FILE_FLAG = 2;
  69.  
  70. 'DIR *pdir;
  71. 'struct dirent *next_entry;
  72. 'struct stat statbuf1;
  73.  
  74. 'char current_dir[FILENAME_MAX];
  75. '#ifdef QB64_WINDOWS
  76. '  #define GetCurrentDir _getcwd
  77. '#else
  78. '  #define GetCurrentDir getcwd
  79. '#endif
  80.  
  81. 'int load_dir (char * path) {
  82. '  struct dirent *pent;
  83. '  struct stat statbuf1;
  84. '//Open current directory
  85. 'pdir = opendir(path);
  86. 'if (!pdir) {
  87. 'return 0; //Didn't open
  88. '}
  89. 'return -1;
  90. '}
  91.  
  92. 'int has_next_entry () {
  93. '  next_entry = readdir(pdir);
  94. '  if (next_entry == NULL) return -1;
  95.  
  96. '  stat(next_entry->d_name, &statbuf1);
  97. '  return strlen(next_entry->d_name);
  98. '}
  99.  
  100. 'void get_next_entry (char * nam, int * flags, int * file_size) {
  101. '  strcpy(nam, next_entry->d_name);
  102. '  if (S_ISDIR(statbuf1.st_mode)) {
  103. '    *flags = IS_DIR_FLAG;
  104. '  } else {
  105. '    *flags = IS_FILE_FLAG;
  106. '  }
  107. '  *file_size = statbuf1.st_size;
  108. '  return ;
  109. '}
  110.  
  111. 'void close_dir () {
  112. '  closedir(pdir);
  113. '  pdir = NULL;
  114. '  return ;
  115. '}
  116.  
  117. 'int current_dir_length () {
  118. '  GetCurrentDir(current_dir, sizeof(current_dir));
  119. '  return strlen(current_dir);
  120. '}
  121.  
  122. 'void get_current_dir(char *dir) {
  123. '  memcpy(dir, current_dir, strlen(current_dir));
  124. '  return ;
  125. '}
  126.  

Note that I've also tweaked the GetDir routine here so that it just does a simple file dump for us, without presorting files and directories for us.  (I also added the full path onto the file names, which I think is essential in a case of recursion so you can tell what the heck it found where...)

This has no issues on my PC starting at the root drive and working its way down into the various subdirectories, and as far as I can tell it's not missing anything.

Note 2:  This doesn't autoclear the old directory listing and builds upon it as you call it.  If you're going to call the routine multiple times, reset your listing with a fresh ReDim As String Dir(0) before making the call to GetAll.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill on November 08, 2021, 03:26:59 pm
And to just see a list of all the BAS files which you have, the following should work:

Code: QB64: [Select]
  1. _Title "Count BAS files from a start Directory" ' b+ 2021-11-04
  2.  
  3. ReDim Shared BasList$(100000) 'store our Bas pathed Files here
  4.  
  5. ReDim As String Dir(0)
  6.  
  7. GetAll "D:\", Dir()
  8. Open "BasList.txt" For Output As #1
  9. For i = 1 To UBound(Dir)
  10.     If _DirExists(Dir(i)) Then Color 7 Else Color 15
  11.     If _StriCmp(Right$(Dir(i), 4), ".bas") = 0 Then
  12.         'it's a BAS file
  13.         BasCount = BasCount + 1
  14.         BasList$(BasCount) = Dir(i)
  15.         Print BasCount, BasList$(BasCount)
  16.         Print #1, BasCount, BasList$(BasCount)
  17.     End If
  18.  
  19.  
  20. Sub GetAll (Path As String, DirList() As String)
  21.     If _KeyDown(27) Then Exit Sub
  22.     ReDim tempDir(0) As String
  23.     GetDir Path, tempDir()
  24.     For i = 1 To UBound(tempDir)
  25.         DirCount = UBound(DirList) + 1
  26.         ReDim _Preserve DirList(DirCount)
  27.         DirList(DirCount) = tempDir(i)
  28.         If _DirExists(tempDir(i)) And Right$(tempDir(i), 1) <> "." Then
  29.             DirList(DirCount) = DirList(DirCount) + "\"
  30.             GetAll tempDir(i), DirList()
  31.         End If
  32.  
  33.     Next
  34.  
  35.  
  36. Sub GetDir (SearchDirectory As String, DirList() As String)
  37.     Declare CustomType Library ".\direntry"
  38.         Function load_dir& (s As String)
  39.         Function has_next_entry& ()
  40.         Sub close_dir ()
  41.         Sub get_next_entry (s As String, flags As Long, file_size As Long)
  42.     End Declare
  43.  
  44.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  45.     Dim nam$
  46.     DirCount = UBound(DirList)
  47.     If Right$(SearchDirectory, 1) = "\" Or Right$(SearchDirectory, 1) = "/" Then SearchDirectory = Left$(SearchDirectory, Len(SearchDirectory) - 1)
  48.     If load_dir(SearchDirectory + Chr$(0)) Then
  49.         Do
  50.             length = has_next_entry
  51.             If length > -1 Then
  52.                 nam$ = Space$(length)
  53.                 get_next_entry nam$, flags, file_size
  54.                 If Right$(nam$, 1) <> "." Then
  55.                     DirCount = DirCount + 1
  56.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 1000)
  57.                     DirList(DirCount) = SearchDirectory + "\" + nam$
  58.                 End If
  59.             End If
  60.         Loop Until length = -1
  61.     End If
  62.     close_dir
  63.     ReDim _Preserve DirList(DirCount)
  64.  
  65. '  Remove comments below and save as direntry.h
  66. ' in your QB64.exe folder if you don't have it already
  67. '=============================================================
  68.  
  69. '#include <dirent.h>
  70. '#include <sys/stat.h>
  71. '#include <unistd.h>
  72.  
  73. 'const int IS_DIR_FLAG = 1, IS_FILE_FLAG = 2;
  74.  
  75. 'DIR *pdir;
  76. 'struct dirent *next_entry;
  77. 'struct stat statbuf1;
  78.  
  79. 'char current_dir[FILENAME_MAX];
  80. '#ifdef QB64_WINDOWS
  81. '  #define GetCurrentDir _getcwd
  82. '#else
  83. '  #define GetCurrentDir getcwd
  84. '#endif
  85.  
  86. 'int load_dir (char * path) {
  87. '  struct dirent *pent;
  88. '  struct stat statbuf1;
  89. '//Open current directory
  90. 'pdir = opendir(path);
  91. 'if (!pdir) {
  92. 'return 0; //Didn't open
  93. '}
  94. 'return -1;
  95. '}
  96.  
  97. 'int has_next_entry () {
  98. '  next_entry = readdir(pdir);
  99. '  if (next_entry == NULL) return -1;
  100.  
  101. '  stat(next_entry->d_name, &statbuf1);
  102. '  return strlen(next_entry->d_name);
  103. '}
  104.  
  105. 'void get_next_entry (char * nam, int * flags, int * file_size) {
  106. '  strcpy(nam, next_entry->d_name);
  107. '  if (S_ISDIR(statbuf1.st_mode)) {
  108. '    *flags = IS_DIR_FLAG;
  109. '  } else {
  110. '    *flags = IS_FILE_FLAG;
  111. '  }
  112. '  *file_size = statbuf1.st_size;
  113. '  return ;
  114. '}
  115.  
  116. 'void close_dir () {
  117. '  closedir(pdir);
  118. '  pdir = NULL;
  119. '  return ;
  120. '}
  121.  
  122. 'int current_dir_length () {
  123. '  GetCurrentDir(current_dir, sizeof(current_dir));
  124. '  return strlen(current_dir);
  125. '}
  126.  
  127. 'void get_current_dir(char *dir) {
  128. '  memcpy(dir, current_dir, strlen(current_dir));
  129. '  return ;
  130. '}
  131.  

No CHDIR involved.  Just calls to our GetDir routine with the path updating as it goes.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 08, 2021, 04:25:10 pm
Hi Bplus
here my feedback from Windows 8.1 pro

 


as you can see your application found more file than CMD : DIR *.BAS /S

What do I to think about it?

@TempodiBasic

Do you have some Basic files as .bas? Maybe your command only does capital BAS?
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 08, 2021, 04:38:25 pm
And to just see a list of all the BAS files which you have, the following should work:

Code: QB64: [Select]
  1. _Title "Count BAS files from a start Directory" ' b+ 2021-11-04
  2.  
  3. ReDim Shared BasList$(100000) 'store our Bas pathed Files here
  4.  
  5. ReDim As String Dir(0)
  6.  
  7. GetAll "D:\", Dir()
  8. Open "BasList.txt" For Output As #1
  9. For i = 1 To UBound(Dir)
  10.     If _DirExists(Dir(i)) Then Color 7 Else Color 15
  11.     If _StriCmp(Right$(Dir(i), 4), ".bas") = 0 Then
  12.         'it's a BAS file
  13.         BasCount = BasCount + 1
  14.         BasList$(BasCount) = Dir(i)
  15.         Print BasCount, BasList$(BasCount)
  16.         Print #1, BasCount, BasList$(BasCount)
  17.     End If
  18.  
  19.  
  20. Sub GetAll (Path As String, DirList() As String)
  21.     If _KeyDown(27) Then Exit Sub
  22.     ReDim tempDir(0) As String
  23.     GetDir Path, tempDir()
  24.     For i = 1 To UBound(tempDir)
  25.         DirCount = UBound(DirList) + 1
  26.         ReDim _Preserve DirList(DirCount)
  27.         DirList(DirCount) = tempDir(i)
  28.         If _DirExists(tempDir(i)) And Right$(tempDir(i), 1) <> "." Then
  29.             DirList(DirCount) = DirList(DirCount) + "\"
  30.             GetAll tempDir(i), DirList()
  31.         End If
  32.  
  33.     Next
  34.  
  35.  
  36. Sub GetDir (SearchDirectory As String, DirList() As String)
  37.     Declare CustomType Library ".\direntry"
  38.         Function load_dir& (s As String)
  39.         Function has_next_entry& ()
  40.         Sub close_dir ()
  41.         Sub get_next_entry (s As String, flags As Long, file_size As Long)
  42.     End Declare
  43.  
  44.     Dim flags As Long, file_size As Long, DirCount As Integer, FileCount As Integer, length As Long
  45.     Dim nam$
  46.     DirCount = UBound(DirList)
  47.     If Right$(SearchDirectory, 1) = "\" Or Right$(SearchDirectory, 1) = "/" Then SearchDirectory = Left$(SearchDirectory, Len(SearchDirectory) - 1)
  48.     If load_dir(SearchDirectory + Chr$(0)) Then
  49.         Do
  50.             length = has_next_entry
  51.             If length > -1 Then
  52.                 nam$ = Space$(length)
  53.                 get_next_entry nam$, flags, file_size
  54.                 If Right$(nam$, 1) <> "." Then
  55.                     DirCount = DirCount + 1
  56.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 1000)
  57.                     DirList(DirCount) = SearchDirectory + "\" + nam$
  58.                 End If
  59.             End If
  60.         Loop Until length = -1
  61.     End If
  62.     close_dir
  63.     ReDim _Preserve DirList(DirCount)
  64.  
  65. '  Remove comments below and save as direntry.h
  66. ' in your QB64.exe folder if you don't have it already
  67. '=============================================================
  68.  
  69. '#include <dirent.h>
  70. '#include <sys/stat.h>
  71. '#include <unistd.h>
  72.  
  73. 'const int IS_DIR_FLAG = 1, IS_FILE_FLAG = 2;
  74.  
  75. 'DIR *pdir;
  76. 'struct dirent *next_entry;
  77. 'struct stat statbuf1;
  78.  
  79. 'char current_dir[FILENAME_MAX];
  80. '#ifdef QB64_WINDOWS
  81. '  #define GetCurrentDir _getcwd
  82. '#else
  83. '  #define GetCurrentDir getcwd
  84. '#endif
  85.  
  86. 'int load_dir (char * path) {
  87. '  struct dirent *pent;
  88. '  struct stat statbuf1;
  89. '//Open current directory
  90. 'pdir = opendir(path);
  91. 'if (!pdir) {
  92. 'return 0; //Didn't open
  93. '}
  94. 'return -1;
  95. '}
  96.  
  97. 'int has_next_entry () {
  98. '  next_entry = readdir(pdir);
  99. '  if (next_entry == NULL) return -1;
  100.  
  101. '  stat(next_entry->d_name, &statbuf1);
  102. '  return strlen(next_entry->d_name);
  103. '}
  104.  
  105. 'void get_next_entry (char * nam, int * flags, int * file_size) {
  106. '  strcpy(nam, next_entry->d_name);
  107. '  if (S_ISDIR(statbuf1.st_mode)) {
  108. '    *flags = IS_DIR_FLAG;
  109. '  } else {
  110. '    *flags = IS_FILE_FLAG;
  111. '  }
  112. '  *file_size = statbuf1.st_size;
  113. '  return ;
  114. '}
  115.  
  116. 'void close_dir () {
  117. '  closedir(pdir);
  118. '  pdir = NULL;
  119. '  return ;
  120. '}
  121.  
  122. 'int current_dir_length () {
  123. '  GetCurrentDir(current_dir, sizeof(current_dir));
  124. '  return strlen(current_dir);
  125. '}
  126.  
  127. 'void get_current_dir(char *dir) {
  128. '  memcpy(dir, current_dir, strlen(current_dir));
  129. '  return ;
  130. '}
  131.  

No CHDIR involved.  Just calls to our GetDir routine with the path updating as it goes.

@SMcNeill

Thanks so much for this! Now I have confirmation that I have fixed my stuff. Though obviously I'd prefer to do the whole thing without the ChDir as you have.

There is some slight difference between GetLists from Oh Interpreter where it was working and here in OP that I've yet to pin down. Turns out I was using a Windows Only Technique for TextFetch.

I have 11,843 Bas files on my Desktop alone not 8,000+- I have 3 backup copies at least of all my BAS work files in various QB64 version numbers. I get this number from your code and the one I fixed with Oh Interpreter GetLists AND used a Non Recursive Stack because something dies in recursive version at 8080.

I will slowly get to using your much better version but first I am curious what the heck was the difference in GetLists.

Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 08, 2021, 07:16:32 pm
Have to make Steve's solution Best Answer it so much better than recursive!

Just a tiny mod to put the file that it writes in the top directory of your search and count and list.
Code: QB64: [Select]
  1. FullPathedDir$ = "C:\Users\marka\Desktop\QB64 work"
  2. 'GetAll "D:\", Dir()
  3. GetAll FullPathedDir$, Dir()
  4. Open FullPathedDir$ + "\Count BasList SMcNeill.txt" For Output As #1 ' <put this in the directory we are doing
  5.  
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill on November 08, 2021, 07:55:50 pm
Have to make Steve's solution Best Answer it so much better than recursive!

Just a tiny mod to put the file that it writes in the top directory of your search and count and list.
Code: QB64: [Select]
  1. FullPathedDir$ = "C:\Users\marka\Desktop\QB64 work"
  2. 'GetAll "D:\", Dir()
  3. GetAll FullPathedDir$, Dir()
  4. Open FullPathedDir$ + "\Count BasList SMcNeill.txt" For Output As #1 ' <put this in the directory we are doing
  5.  

Umm...  But it is recursive.  GetAll calls itself until done...  😂😂

And remember: ReDim As String Dir(0) before making the call to GetAll if you're going to call it again.  Reset your UBOUND to 0.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SpriggsySpriggs on November 08, 2021, 08:10:13 pm
I'd like to submit a simple alternative, if I may. I know this isn't recursive but I'm too lazy to do that right now.

Code: QB64 $NOPREFIX: [Select]
  1.  
  2. Type FILETIME
  3.     As Unsigned Long dwLowDateTime, dwHighDateTime
  4.  
  5. Type WIN32_FIND_DATA
  6.     As Unsigned Long dwFileAttributes
  7.     As FILETIME ftCreationTime, ftLastAccessTime, ftLastWriteTime
  8.     As Unsigned Long nFileSizeHigh, nFileSizeLow, dwReserved0, dwReserved1
  9.     As String * 260 cFileName
  10.     As String * 14 cAlternateFileName
  11.     As Unsigned Long dwFileType, dwCreatorType
  12.     As Unsigned Integer wFinderFlags
  13.  
  14.     Function FindFirstFile%& (ByVal lpFileName As Offset, Byval lpFindFileData As Offset)
  15.     Function FindNextFile& (ByVal hFindFile As Offset, Byval lpFindFileData As Offset)
  16.     Sub FindClose (ByVal hFindFile As Offset)
  17.  
  18. Dim As String searchTerm
  19. Dim As WIN32_FIND_DATA attr
  20.  
  21. searchTerm = ".\*.bas" + Chr$(0)
  22.  
  23. find = FindFirstFile(Offset(searchTerm), Offset(attr))
  24.  
  25. If find <> -1 And find <> 0 Then
  26.     Do
  27.         Print Mid$(attr.cFileName, 1, InStr(attr.cFileName, Chr$(0)) - 1)
  28.     Loop While FindNextFile(find, Offset(attr))
  29.  
  30. FindClose find
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SpriggsySpriggs on November 08, 2021, 08:19:19 pm
@TempodiBasic

Do you have some Basic files as .bas? Maybe your command only does capital BAS?

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.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SpriggsySpriggs 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
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill 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.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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):
  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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!
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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:
  [ This attachment cannot be displayed inline in 'Print Page' view ]  

Could there be some character one method finds and the other doesn't?
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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.  
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill 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.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill 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
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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...
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill 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.  ;)
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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...
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill 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
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus 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.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill on November 09, 2021, 01:12:52 pm
It should work just as you're used to, with the addition of an error message if something slides past detection as either file or directory.  (And you shouldn't need to CHDIR for it to work, I wouldn't think.)
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SpriggsySpriggs on November 09, 2021, 10:11:24 pm
All I was showing was how you can do a recursive search with PowerShell. It isn't necessarily only searching files. It's searching based on the string. So it will return anything that matches the string, no matter if it is a file or a folder. It is interesting that one would have a folder with an extension in the name, however. I think the PowerShell call could be edited to do only files.
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 10, 2021, 06:36:16 pm
OK I have 5 programs that count and list Bas files from a start Directory, they are described with a couple times I got testing the programs. Turns out when I fixed the original recursive code, it turned in the fastest times but Steve's code is checking everything which I think accounts for difference in times, more than double.

Anyway here is code and comments tested on my old Windows 10 laptop 64-bit with a Desktop containing over 11,850 files. Of course you will have different results.

PS to test in your system edit a couple of lines near the top of each program specifying the path and the pathed file list file name to use. Oh, also if direntry.h is not in the same folder as QB64.exe, there is a copy that is commented out at the bottom of programs that use it.

Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 16, 2021, 02:36:31 pm
Update: I was working on clearing all these experiments off my Desktop and retesting them in my Downloads Folder.

Big snag with all SMcNeills code because he was using Integer Type for FileCount and DirCount. Turns out I had a folder with over 40,000 files and I got subscript error repeated over and over for that Folder.

Also after fixing that with Long Type, I ran into a problem file caught by Steve's code "that should never happen", here is a snapshot of that file:
 


Here is revised GetLists subroutine:
Code: QB64: [Select]
  1. ' fixed 2021-11-16 for folders or files OVER 32,000+ Integer Range
  2. ' ref 2021-11-09 Steve update GetLists:  https://www.qb64.org/forum/index.php?topic=4360.msg138031#msg138031
  3. Sub GetLists (SearchDirectory As String, DirList() As String, FileList() As String)
  4.     ' Thanks SNcNeill ! for a cross platform method to get file and directory lists
  5.     'put this block in main code section of your program close to top
  6.     '' direntry.h needs to be in QB64 folder '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  7.     'Declare CustomType Library ".\direntry"
  8.     '    Function load_dir& (s As String)
  9.     '    Function has_next_entry& ()
  10.     '    Sub close_dir ()
  11.     '    Sub get_next_entry (s As String, flags As Long, file_size As Long)
  12.     'End Declare
  13.  
  14.     ' fix 2021-11-16 I have a folder in Downloads that is over 40,000 files mostly HTML which caused subscript errors
  15.     ' change DirCount and FileCount to Long from Integer
  16.     Dim flags As Long, file_size As Long, DirCount As Long, FileCount As Long, length As Long
  17.     Dim nam$, slash$
  18.     ReDim _Preserve DirList(100), FileList(100)
  19.     DirCount = 0: FileCount = 0
  20.     $If WIN Then
  21.         slash$ = "\"
  22.     $Else
  23.         slash$ = "/"
  24.     $End If
  25.     If Right$(SearchDirectory$, 1) <> "/" And Right$(SearchDirectory$, 1) <> "\" Then SearchDirectory$ = SearchDirectory$ + slash$
  26.  
  27.     If load_dir(SearchDirectory + Chr$(0)) Then
  28.         Do
  29.             length = has_next_entry
  30.             If length > -1 Then
  31.                 nam$ = Space$(length)
  32.                 get_next_entry nam$, flags, file_size
  33.                 If _DirExists(SearchDirectory + nam$) Then
  34.                     DirCount = DirCount + 1
  35.                     If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 100)
  36.                     DirList(DirCount) = nam$
  37.                 ElseIf _FileExists(SearchDirectory + nam$) Then
  38.                     FileCount = FileCount + 1
  39.                     If FileCount > UBound(FileList) Then ReDim _Preserve FileList(UBound(FileList) + 100)
  40.                     FileList(FileCount) = nam$
  41.                 Else 'This else should never actually trigger
  42.                     Print: Print: Print "zzz...  Unknown file found: "; SearchDirectory; slash$; nam$, _DirExists(nam$)
  43.                     Beep: Sleep ' alert the user to
  44.                 End If
  45.             End If
  46.         Loop Until length = -1
  47.     End If
  48.     close_dir
  49.  
  50.     ReDim _Preserve DirList(DirCount)
  51.     ReDim _Preserve FileList(FileCount)
  52.  

copy of direntry.h to put in same folder as your QB64.exe
Code: [Select]
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

const int IS_DIR_FLAG = 1, IS_FILE_FLAG = 2;

DIR *pdir;
struct dirent *next_entry;
struct stat statbuf1;

char current_dir[FILENAME_MAX];
#ifdef QB64_WINDOWS
  #define GetCurrentDir _getcwd
#else
  #define GetCurrentDir getcwd
#endif

int load_dir (char * path) {
  struct dirent *pent;
  struct stat statbuf1;
//Open current directory
pdir = opendir(path);
if (!pdir) {
return 0; //Didn't open
}
return -1;
}

int has_next_entry () {
  next_entry = readdir(pdir);
  if (next_entry == NULL) return -1;
 
  stat(next_entry->d_name, &statbuf1);
  return strlen(next_entry->d_name);
}

void get_next_entry (char * nam, int * flags, int * file_size) {
  strcpy(nam, next_entry->d_name);
  if (S_ISDIR(statbuf1.st_mode)) {
    *flags = IS_DIR_FLAG;
  } else {
    *flags = IS_FILE_FLAG;
  }
  *file_size = statbuf1.st_size;
  return ;
}

void close_dir () {
  closedir(pdir);
  pdir = NULL;
  return ;
}

int current_dir_length () {
  GetCurrentDir(current_dir, sizeof(current_dir));
  return strlen(current_dir);
}

void get_current_dir(char *dir) {
  memcpy(dir, current_dir, strlen(current_dir));
  return ;
}

Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: SMcNeill on November 16, 2021, 05:59:00 pm
Question: How'd you generate two slashes in the filename?  And if it's not a file, nor a directory, what is it?  Does it even exist, or is that some false positive it generated somehow?
Title: Re: Bas Files Count and List by Recursive Algorithm
Post by: bplus on November 16, 2021, 07:46:06 pm
I think I was trying out a Wiki access from something by you @SMcNeill

A view from InForm Project: