Parse DIR listing for folder name and size

General FreeBASIC programming questions.
Post Reply
nfunk
Posts: 6
Joined: Jun 05, 2020 20:25

Parse DIR listing for folder name and size

Post by nfunk »

I am having a hell of a time trying to parse a directory listing to get just the folder name, and folder size.
I just want to retrieve the following including total files and bytes. I know there are many FreeBasic gurus out there, and probably an easy solution to this problem, but I am at a loss.

Folder 1, 540235493 bytes
Folder 2, 256470327 bytes
Folder 3, 1430815946 bytes
Folder 4, 1496155138 bytes
Folder 5, 1382708779 bytes
11 File, 5106385683 bytes


For example, below is a DIR listing (DIR /S /-C):

Volume in drive G is 1.5Terrabyte
Volume Serial Number is 5492-5AFI

Directory of G:\test

06/08/2020 10:42 AM <DIR> .
06/08/2020 10:42 AM <DIR> ..
06/08/2020 12:40 PM <DIR> Folder 1
06/08/2020 10:40 AM <DIR> Folder 2
06/08/2020 10:40 AM <DIR> Folder 3
06/08/2020 12:41 PM <DIR> Folder 4
06/08/2020 12:41 PM <DIR> Folder 5
0 File(s) 0 bytes

Directory of G:\test\Folder 1

06/08/2020 12:40 PM <DIR> .
06/08/2020 12:40 PM <DIR> ..
08/24/2019 08:08 AM 108167 backup_1.tib
08/24/2019 08:52 AM 533477418 backup_2.tib
08/24/2019 08:08 AM 6649856 backup_3.tib
08/24/2019 06:57 AM 52 text.txt
4 File(s) 540235493 bytes

Directory of G:\test\JFolder 2

06/08/2020 10:40 AM <DIR> .
06/08/2020 10:40 AM <DIR> ..
06/06/2020 12:40 PM 256379805 Backup_4.tib
12/16/2018 07:05 PM 90522 Backup_5.tib
2 File(s) 256470327 bytes

Directory of G:\test\Folder 3

06/08/2020 10:40 AM <DIR> .
06/08/2020 10:40 AM <DIR> ..
08/09/2019 09:24 PM 1430815946 Backup_6.tib
1 File(s) 1430815946 bytes

Directory of G:\test\Folder 4

06/08/2020 12:41 PM <DIR> .
06/08/2020 12:41 PM <DIR> ..
08/09/2019 09:02 PM 1496154228 Backup_7.tib
08/10/2019 10:56 AM 910 text.txt
2 File(s) 1496155138 bytes

Directory of G:\test\Folder 5

06/08/2020 12:41 PM <DIR> .
06/08/2020 12:41 PM <DIR> ..
08/11/2019 11:28 AM 1382707834 Backup_8.tib
08/11/2019 11:30 AM 945 text.txt
2 File(s) 1382708779 bytes

Total Files Listed:
11 File(s) 5106385683 bytes
17 Dir(s) 714031509504 bytes free

Information is GREATLY Appreciated!
Nick
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Parse DIR listing for folder name and size

Post by Tourist Trap »

nfunk wrote:I am having a hell of a time trying to parse a directory listing to get just the folder name, and folder size.
Hello,

as far as I remember this is not that trivial to get those kind of metrics (directory tend to have a lot of subdirectories in there). Even powershell which is the job can be slow on that (from my remembrance).
Maybe you could use a simple utility program like microsoft Diskusage?
https://docs.microsoft.com/en-us/sysint ... k-usage-du

If you use Shell in FB, maybe you'll have to take care of well enclosing things within quotes. Otherwise it will be easy to output at the console, or to create a CSV with your statistics.

Code: Select all

var d = """"& curDir() &""""
shell("du64.exe -nobanner -v " & d )
Just a suggestion here. There are probably many other ways to proceed any way.
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Parse DIR listing for folder name and size

Post by badidea »

If you do it in freebasic, a recursive function is needed.
Also there is 'useful' file size and the disc size used (also for directories).
Here on linux, I can say du -s --apparent-size Pictures/ and du -s Pictures/
Last edited by badidea on Jun 21, 2020 20:28, edited 2 times in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Parse DIR listing for folder name and size

Post by dodicat »

NOT very trivial, but not too difficult.
This works here.
WINDOWS ONLY (due to crt stats)

Code: Select all


#include "crt.bi"
 #include "file.bi"
Declare Function stats Cdecl Alias "_stat"(As zstring Ptr,As Any Ptr) As Integer

Function String_Split(s_in As String,chars As String,result() As String) As Long
    Dim As Long ctr,ctr2,k,n,LC=Len(chars)
    Dim As boolean tally(Len(s_in))
    #macro check_instring()
    n=0
    While n<Lc
        If chars[n]=s_in[k] Then 
            tally(k)=true
            If (ctr2-1) Then ctr+=1
            ctr2=0
            Exit While
        End If
        n+=1
    Wend
    #endmacro
    
    #macro split()
    If tally(k) Then
        If (ctr2-1) Then ctr+=1:result(ctr)=Mid(s_in,k+2-ctr2,ctr2-1)
        ctr2=0
    End If
    #endmacro
    '==================  LOOP TWICE =======================
    For k  =0 To Len(s_in)-1
        ctr2+=1:check_instring()
    Next k
     if ctr=0 then
         if len(s_in) andalso instr(chars,chr(s_in[0])) then ctr=1':beep
         end if
    If ctr Then Redim result(1 To ctr): ctr=0:ctr2=0 Else  Return 0
    For k  =0 To Len(s_in)-1
        ctr2+=1:split()
    Next k
    '===================== Last one ========================
    If ctr2>0 Then
        Redim Preserve result(1 To ctr+1)
        result(ctr+1)=Mid(s_in,k+1-ctr2,ctr2)
    End If
   
    Return Ubound(result)
End Function

Function _Remove(Byval Text As String,Char As String) As String
    Var index = 0,asci=Asc(char)
    For i As Integer = 0 To Len(Text) - 1
        If Text[i] <> ASCi Then Text[index] = Text[i] : index =index+ 1
    Next 
    Return Left(Text,index)
End Function


Function isfolder(path As zstring Ptr) As Long
    #define S_ISDIR(m)   (((m) And &hF000) = &h4000)
    Dim As stat statbuf
    If (stats(path, @statbuf) <> 0) Then Return 0
    Return S_ISDIR(statbuf.st_mode)
End Function

Function isfile(fname As String) As boolean
    'return fileexists(fname)
    Return Iif(isfolder(fname),0,1)
End Function

Function pipeout(Byval s As String="") Byref As String
    Var f=Freefile
    Dim As String tmp
    Open Pipe s For Input As #f 
    s=""
    Do Until Eof(f)
        Line Input #f,tmp
        s+=tmp+Chr(10)
    Loop
    Close #f
    Return s
End Function 

dim shared as ulongint sz
function Size(inputs As String) as long
    dim as string path
    If isfile(inputs) Then return filelen(inputs)
    Dim As String file
    If Instr(inputs," ") Then inputs=Chr(34)+inputs+Chr(34)
    Dim As String s=pipeout("dir /b " + inputs)
    reDim As String a()
    string_split(s,Chr(13,10),a())
    inputs=_remove(inputs,Chr(34))
    For n As Long=Lbound(a) To Ubound(a)
         path=(inputs+"\"+a(n))
        If isfile(path) Then
            Redim As String tmp()
            string_split(path,"\",tmp())
            file= tmp(Ubound(tmp))
            If Instr(file," ") Then file=Chr(34)+file+Chr(34)
                sz+=filelen(path)
           Else
            size(path) 'for nested folders
        End If
    Next n
    return sz
End function 



sub show(fullpath as string)
if instr(fullpath," ") then fullpath=chr(34)+fullpath+chr(34)
dim as string g=pipeout( "dir "+fullpath)
var i=instr(g,":")-1
var i1=instr(mid(g,i),chr(10))-1
dim as string fpath=mid(g,i,i1)+"\"
print fpath
dim as const string dirlist="dir /b "
dim as string path=dirlist+ fullpath
var s=pipeout(path)
redim as string a()
string_split(s,chr(10),a())
for n as long=lbound(a) to ubound(a)
    sz=0
    print mid(a(n),instrrev(a(n),"\")+1);tab(40);size(fpath+ a(n));tab(60); iif(isfolder(fpath+ a(n)),"--->folder","")
next
end sub

dim as string fullpath="C:\Users\User\Desktop\Return to Castle Wolfenstein"
show(fullpath)
print"DONE . . ."
sleep




  
my results

Code: Select all

 C:\Users\User\Desktop\Return to Castle Wolfenstein\
anet.inf                                345
BACKUP                                  0                  --->folder
cgamex86.dll                            536576
cgame_mp_x86.dll                        573440
Docs                                    1053315            --->folder
Getinfo.dll                             23552
gl                                      630784             --->folder
Main                                    702482252          --->folder
qagamex86.dll                           708608
qagame_mp_x86.dll                       753664
register.exe                            131072
servercache.dat                         350224
sysinfo.exe                             84480
Sysinv.dll                              38912
uix86.dll                               225280
ui_mp_x86.dll                           217088
Uninstall                               196778             --->folder
WolfMP.exe                              1024057
WolfSP.exe                              1282095
 
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Parse DIR listing for folder name and size

Post by badidea »

The dir command cannot be used recursively that simple it seems:

Code: Select all

#include "dir.bi"
#include "file.bi"

const as integer ATTR_MASK_ALL = _
	fbArchive or fbNormal or fbHidden or fbSystem or fbDirectory

const as integer KB = 1024, MB = 1024 * 1024, GB = 1024 * 1024 * 1024

type file_info
	dim as ulongint apparentSize
	dim as uLong fileCount
	dim as uLong dirCount
end type

'get size and count of file or directory
function getFileInfo(fileSpec as string) as file_info
	dim as file_info retInfo, tempInfo
	dim as integer outAttr
	dim as string fileName = dir(filespec, ATTR_MASK_ALL, outAttr)
	while len(filename) > 0
		if (fileName <> ".") and (fileName <> "..") then
			if (outAttr and fbDirectory) <> 0 then
				'is dir
				print "DIR: " & fileName
				retInfo.dirCount += 1
				tempInfo = getFileInfo(fileName & "/*") '<- recursive call
				retInfo.dirCount += tempInfo.dirCount
				retInfo.fileCount += tempInfo.fileCount
				retInfo.apparentSize += tempInfo.apparentSize
			else
				'is file
				print "FILE: " & fileName
				retInfo.fileCount += 1
				retInfo.apparentSize += fileLen(fileName)
			end if
		end if
		fileName = dir(outAttr)
	wend
	return retInfo
end function

dim as file_info fileInfo
fileInfo = getFileInfo("*")
print "dirCount : " & fileInfo.dirCount
print "fileCount: " & fileInfo.fileCount
print "apparent size: " & fileInfo.apparentSize & " B"
print "apparent size: " & fileInfo.apparentSize \ KB & " KB"
print "apparent size: " & fileInfo.apparentSize \ MB & " MB"
So first a list of directories need to be made before visiting that directory and complete looping the current directory first. To be continued...

This seems to work better. But ends up in a loop when encountering directory links.

Code: Select all

type string_list
	dim as string list(any)
	declare function size() as integer
	declare function empty() as integer
	declare function add(text as string) as integer
end type

function string_list.size() as integer
	return ubound(list) + 1
end function

function string_list.empty() as integer
	erase(list)
	return 0
end function

function string_list.add(text as string) as integer
	dim as integer ub = ubound(list)
	redim preserve list(ub + 1)
	list(ub + 1) = text
	return ub + 1
end function

#include "dir.bi"
#include "file.bi"

const as integer ATTR_MASK_ALL = _
	fbArchive or fbNormal or fbHidden or fbSystem or fbDirectory

const as integer KB = 1024, MB = 1024 * 1024, GB = 1024 * 1024 * 1024

type file_info
	dim as ulongint apparentSize
	dim as uLong fileCount
	dim as uLong dirCount
end type

'get size and count of file or directory
function getFileInfo(path as string, files as string) as file_info
	static as integer depth = 0 '<- not essential, for printing
	depth += 1
	print string(depth, "*") & " PATH: " & path
	dim as file_info retInfo, tempInfo
	dim as integer outAttr
	dim as string_list dirList
	dim as string filespec = path & files
	dim as string fileName = dir(filespec, ATTR_MASK_ALL, outAttr)
	while len(filename) > 0
		if (fileName <> ".") and (fileName <> "..") then
			if (outAttr and fbDirectory) <> 0 then
				'is dir
				'print string(depth, "*") & " DIR: " & fileName
				retInfo.dirCount += 1
				dirList.add(fileName)
			else
				'is file
				'print string(depth, "*") & " FILE: " & fileName, fileLen(path & fileName)
				retInfo.fileCount += 1
				retInfo.apparentSize += fileLen(path & fileName)
			end if
		end if
		fileName = dir(outAttr)
	wend
	'loop dirs
	for i as integer = 0 to dirList.size() - 1
		fileName = dirList.list(i)
		'print string(depth, "*") & " DIR: " & fileName
		tempInfo = getFileInfo(path & fileName & "/", "*") '<- recursive call
		retInfo.dirCount += tempInfo.dirCount
		retInfo.fileCount += tempInfo.fileCount
		retInfo.apparentSize += tempInfo.apparentSize
	next
	dirList.empty()
	depth -= 1
	return retInfo
end function

dim as file_info fileInfo
fileInfo = getFileInfo("/home/badidea/Pictures/", "*")
print "dirCount : " & fileInfo.dirCount
print "fileCount: " & fileInfo.fileCount
print "apparent size: " & fileInfo.apparentSize & " B"
print "apparent size: " & fileInfo.apparentSize \ KB & " KB"
print "apparent size: " & fileInfo.apparentSize \ MB & " MB"
print "apparent size: " & fileInfo.apparentSize \ GB & " GB"
So next step: Add this: How do I detect if a file is symbolic link?
Tomorrow...
adeyblue
Posts: 299
Joined: Nov 07, 2019 20:08

Re: Parse DIR listing for folder name and size

Post by adeyblue »

I went with a fancypants thread per top-level directory approach. Don't know whether print is thread safe or not, so the mutex stuff can be removed if it is.

Code: Select all

#include "dir.bi"
#include "crt/sys/stat.bi"

Type DirInfo
	Dim size As ULongInt
	Dim path As String
	Dim outputLock As Any Ptr
End Type

const FILE_MASK As Long = fbArchive or fbNormal or fbHidden or fbSystem

Sub DirForEach( _
	ByVal path As String, _
	ByVal attrib As Long, _
	ByVal context As Any Ptr, _
	ByVal callback As Function (ByVal wholeFileName As String, Byval context As Any Ptr) As Long _
)
	Dim pathSlashed As String = path & "/"
	Dim pathPattern As String = pathSlashed & "*"
	Dim emptyString As String
	Dim foundItem As String = Dir(pathPattern, attrib)
	While foundItem <> emptyString
		If foundItem <> "." And foundItem <> ".." Then
			If callback(pathSlashed + foundItem, context) = False Then
				Exit While
			End If
		End If
		foundItem = Dir(emptyString, attrib)
	Wend
End Sub

Function SumFileSizes(ByVal fileName As String, ByVal context As Any Ptr) As Long

	Dim fData As _stat
	Dim pCumulativeSize As ULongInt Ptr = context
	stat(StrPtr(fileName), @fData)
	*pCumulativeSize += fData.st_size
	Return True

End Function

Sub ThreadSafePrint(ByVal mtx As Any Ptr, ByVal text As String)

	MutexLock(mtx)
	Print text
	MutexUnlock(mtx)

End Sub

Function PrintChildDirs(ByVal directory As String, context As Any Ptr) As Long
	Dim thisDirSize As UlongInt
	Dim dirInfo As DirInfo Ptr = context
	
	DirForEach(directory, FILE_MASK, @thisDirSize, @SumFileSizes)

	dirInfo->size += thisDirSize

	DirForEach(directory, fbDirectory, context, @PrintChildDirs)
	Return True

End Function

Sub ThreadStart(ByVal userData As Any Ptr)
	Dim dirInfo As DirInfo Ptr = userData
	PrintChildDirs(dirInfo->path, dirInfo)
	ThreadSafePrint(dirInfo->outputLock, "Total size of " & dirInfo->path & " is " & dirInfo->size & " bytes")
End Sub

Dim Shared g_threads(Any, Any) As Any Ptr

Type ThreadCreateContext
	Dim outputLock As Any Ptr
	Dim numThreads As Long
End Type

Function CreateDirThreads(ByVal fileDir As String, ByVal context As Any Ptr) As Long

	Dim pTcContext As ThreadCreateContext Ptr = context
	Dim numThreads As Long = pTcContext->numThreads

	Dim thisTopDir As DirInfo Ptr = New DirInfo
	thisTopDir->size = 0
	thisTopDir->path = fileDir
	Redim Preserve g_threads(numThreads + 1, 2)

	g_threads(numThreads, 0) = thisTopDir
	g_threads(numThreads, 1) = ThreadCreate(@ThreadStart, thisTopDir)
	pTcContext->numThreads = numThreads + 1
	Return True

End Function

Sub DoTopLevelDir(path As String)
	Dim outputLock As Any Ptr
	Dim tcContext As ThreadCreateContext
	Dim size As UlongInt

	outputLock = MutexCreate()
	tcContext.outputLock = outputLock
	tcContext.numThreads = 0

	DirForEach(path, fbDirectory, @tcContext, @CreateDirThreads)

	DirForEach(path, FILE_MASK, @size, @SumFileSizes)

	For i As Long = 0 To tcContext.numThreads - 1
		Dim pDirInfo As DirInfo Ptr = g_threads(i, 0)
		ThreadWait g_threads(i, 1)
		size += pDirInfo->size
		Delete pDirInfo
	Next

	MutexDestroy(outputLock)

	Print "Total Size of all files under " & path & " is " & Str(size) & " bytes"

End Sub


Dim startDir as String = Command(1)

If startDir = "" Then
	startDir = CurDir()
End If

Print "Dir-ing " & startDir

DoTopLevelDir startDir
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Parse DIR listing for folder name and size

Post by TJF »

@nfunk:

The file size isn't equal to the memory used on disc. In order to evaluate the HDD consumption, you've to consider the sector size. In this example a sector size of 4096 bytes is used

Code: Select all

#INCLUDE ONCE "dir.bi"

TYPE DirSize
  AS ZSTRING PTR Mask
  AS ULONGINT Fsize, Ssize
  AS ULONG Dcnt, Fcnt, Sect, Scnt
  DECLARE CONSTRUCTOR(BYVAL M AS ZSTRING PTR, BYVAL P AS ZSTRING PTR, BYVAL S AS ULONG = 4096)
  DECLARE SUB Eval()
END TYPE

CONSTRUCTOR DirSize(BYVAL M AS ZSTRING PTR, BYVAL P AS ZSTRING PTR, BYVAL S AS ULONG = 4096)
  Mask = M
  Sect = S
  VAR cd = CURDIR()
  IF CHDIR(*P) THEN EXIT CONSTRUCTOR
  Eval()
  CHDIR(cd)
  Ssize = Sect * Scnt
END CONSTRUCTOR

SUB DirSize.Eval()
  VAR res = 0, n = DIR(*Mask, fbDirectory, @res), t = ""
  Dcnt += 1
  Scnt += 1 ' assuming a directory consumes 1 sector = 4096 bytes on HDD
  WHILE LEN(n)
    IF res = fbDirectory ANDALSO n <> "." ANDALSO n <> ".." THEN t &= n & !"\n"
    n = DIR("", fbDirectory, @res)
  WEND

  VAR a = 1, e = a, l = LEN(t)
  WHILE a < l
    e = INSTR(a, t, !"\n")
    n = MID(t, a, e - a)
    IF 0 = CHDIR(n) THEN
'?n
      Eval()
      CHDIR ("..")
    END IF
    a = e + 1
  WEND

  n = DIR(*Mask)
  WHILE LEN(n)
    VAR fnr = FREEFILE
    IF 0 = OPEN(n FOR INPUT AS fnr) THEN
      VAR l = LOF(fnr)
      CLOSE #fnr
      Fcnt += 1
      Scnt += 1 + (l - 1) \ Sect
      Fsize += l
'?"  ";n,l,1 + (l - 1) \ Sect
    END IF
    n = DIR()
  WEND
END SUB

VAR t = NEW DirSize(@"*.bas", @"..", 4096)
WITH *t
  ?"Size: " & .Fsize & " (" & .Ssize & " = " & .Scnt & " sectors)"
  ?"folders: " & .Dcnt & ", files: " & .Fcnt
END WITH
DELETE t
It generates output like
Size: 581477 (671744 = 164 sectors)
folders: 1, files: 33
The CTOR requires
  • the pattern for the file names,
  • the path where the evaluation should start, and
  • the optional sector size (defaults to 4096).
In order to evaluate the folder and file names, just uncomment the ? ... lines. A directory name starts at the left border, a file name starts after two spaces.

In order to evaluate your sector size use system commands. Ie. on a Debian based LINUX system execute in a terminal

Code: Select all

$ sudo hdparm -I /dev/sda | grep Physical
	Physical Sector size:                  4096 bytes
Regards

[edit]
Added line:
Scnt += 1 ' assuming a directory consumes 1 sector = 4096 bytes on HDD
[/edit]
Last edited by TJF on Jun 22, 2020 9:30, edited 1 time in total.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Parse DIR listing for folder name and size

Post by fxm »

adeyblue wrote:Don't know whether print is thread safe or not, so the mutex stuff can be removed if it is.
In any case in your code, the value of the pointer 'mtx' used in 'MutexLock(mtx) / MutexUnlock(mtx)' is not initialized compared to the one get from 'MutexCreate()'.

The same 'outputLock' member data name is declared in two different Types !
Perhaps define 'outputLock' once as a static member data of the 'ThreadCreateContext' Type ?

Code: Select all

.....
Type ThreadCreateContext
   Static outputLock As Any Ptr
   Dim numThreads As Long
End Type
Dim ThreadCreateContext.outputLock As Any Ptr

Sub ThreadStart(ByVal userData As Any Ptr)
   Dim dirInfo As DirInfo Ptr = userData
   PrintChildDirs(dirInfo->path, dirInfo)
   ThreadSafePrint(ThreadCreateContext.outputLock, "Total size of " & dirInfo->path & " is " & dirInfo->size & " bytes")
End Sub

Dim Shared g_threads(Any, Any) As Any Ptr
.....
Last edited by fxm on Jun 22, 2020 11:55, edited 5 times in total.
UEZ
Posts: 972
Joined: May 05, 2017 19:59
Location: Germany

Re: Parse DIR listing for folder name and size

Post by UEZ »

Here a variant using the Windows API:

Code: Select all

'Coded by UEZ build 2020-06-22 beta
#Include "windows.bi"

Declare Sub GetDirSize(sDir As String)

Type tFileSize
	Union
		As Ulongint quadpart
		Type
			As Uinteger LowPart, HighPart
		End Type
	End Union
End Type

Dim As String sDir = "c:\Temp\*" '<---- change path and don't forget * at the end!
? "Dir size", "Amount of files", "Dir root name"
? " (bytes)", "within folder"
? "----------------------------------------------------------------------"
GetDirSize(sDir)

? "Done."
Sleep

Sub GetDirSize(sDir As String)
	Dim As WIN32_FIND_DATA ffd
	Dim As tFileSize filesize
	Dim As HANDLE hFind = INVALID_HANDLE_VALUE
	Dim As String sDir2
	Static As Integer level = 0, filecount = 0
	Static As Ulongint DirSize = 0, totalfiles = 0, totalsize = 0, totaldirs = 0
	hFind = FindFirstFile(sDir, @ffd)
	If hFind = INVALID_HANDLE_VALUE Then
		'? "ERROR: FindFirstFile"
		Exit Sub
	End If

	While (FindNextFile(hFind, @ffd)) <> 0
		filesize.LowPart = ffd.nFileSizeLow
		filesize.HighPart = ffd.nFileSizeHigh
		If level > 0 Then 
			DirSize += filesize.quadpart
			If (ffd.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) <> FILE_ATTRIBUTE_DIRECTORY Then filecount += 1
		End If
		If ffd.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY And ffd.cFileName <> ".." Then 
			If level = 0 Then totaldirs += 1
			level += 1
			sDir2 = ffd.cFileName
			GetDirSize(Left(sDir, InStrRev(sDir, "\")) & ffd.cFileName & Right(sDir, Len(sDir) - InStrRev(sDir, "\") + 1))
			level -= 1
			If level = 0 Then 
				? DirSize, filecount,, sDir2
				totalfiles += filecount
				totalsize += DirSize
				DirSize = 0 : filecount = 0
			End If
		End If
	Wend
	FindClose(hFind)
	If level = 0 Then 
		? "======================================================================"
		? "Total directories: " & totaldirs
		? "Total files: " & totalfiles
		? "Total file size: " & totalsize & " bytes"
	End If
End Sub
I hope it works properly...^^
nfunk
Posts: 6
Joined: Jun 05, 2020 20:25

Re: Parse DIR listing for folder name and size

Post by nfunk »

Gurus,

Well, it looks like I have a lot to learn! What I though wouldn't be difficult isn't so (at least for me). I spent hours searching the forums including Libraries hoping that I might get an idea on programming this. I will have to sit down and study everyone's code and learn the intricacies of the algorithms.

Again, Thank y'all for the help and insight!!!!!
Nick
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Parse DIR listing for folder name and size

Post by dodicat »

My code gives the sizes (as in shell dir)
The total size is the upper if you were to right click- properties.
Also the number of folders and number of files.

Code: Select all

#include "crt.bi"
#include "file.bi"
Declare Function stats Cdecl Alias "_stat"(As zstring Ptr,As Any Ptr) As Integer

Function String_Split(s_in As String,chars As String,result() As String) As Long
    Dim As Long ctr,ctr2,k,n,LC=Len(chars)
    Dim As boolean tally(Len(s_in))
    #macro check_instring()
    n=0
    While n<Lc
        If chars[n]=s_in[k] Then
            tally(k)=true
            If (ctr2-1) Then ctr+=1
            ctr2=0
            Exit While
        End If
        n+=1
    Wend
    #endmacro
    
    #macro split()
    If tally(k) Then
        If (ctr2-1) Then ctr+=1:result(ctr)=Mid(s_in,k+2-ctr2,ctr2-1)
        ctr2=0
    End If
    #endmacro
    '==================  LOOP TWICE =======================
    For k  =0 To Len(s_in)-1
        ctr2+=1:check_instring()
    Next k
    If ctr=0 Then
        If Len(s_in) Andalso Instr(chars,Chr(s_in[0])) Then ctr=1':beep
    End If
    If ctr Then Redim result(1 To ctr): ctr=0:ctr2=0 Else  Return 0
    For k  =0 To Len(s_in)-1
        ctr2+=1:split()
    Next k
    '===================== Last one ========================
    If ctr2>0 Then
        Redim Preserve result(1 To ctr+1)
        result(ctr+1)=Mid(s_in,k+1-ctr2,ctr2)
    End If
    
    Return Ubound(result)
End Function

Function _Remove(Byval Text As String,Char As String) As String
    Var index = 0,asci=Asc(char)
    For i As Integer = 0 To Len(Text) - 1
        If Text[i] <> ASCi Then Text[index] = Text[i] : index =index+ 1
    Next
    Return Left(Text,index)
End Function


Function isfolder(path As zstring Ptr) As Long
    #define S_ISDIR(m)   (((m) And &hF000) = &h4000)
    Dim As stat statbuf
    If (stats(path, @statbuf) <> 0) Then Return 0
    Return S_ISDIR(statbuf.st_mode)
End Function

Function isfile(fname As String) As boolean
    Return Iif(isfolder(fname),0,1)
End Function

Function pipeout(Byval s As String="") Byref As String
    Var f=Freefile
    Dim As String tmp
    Open Pipe s For Input As #f
    s=""
    Do Until Eof(f)
        Line Input #f,tmp
        s+=tmp+Chr(10)
    Loop
    Close #f
    Return s
End Function

Dim Shared As Ulongint sz,filecount,foldercount
Function Size(inputs As String) As Long
    Dim As String path
    If isfile(inputs) Then filecount+=1: Return Filelen(inputs)
    foldercount+=1
    Dim As String file
    If Instr(inputs," ") Then inputs=Chr(34)+inputs+Chr(34)
    Dim As String s=pipeout("dir /b " + inputs)
    Redim As String a()
    string_split(s,Chr(13,10),a())
    inputs=_remove(inputs,Chr(34))
    For n As Long=Lbound(a) To Ubound(a)
        path=(inputs+"\"+a(n))
        If isfile(path) Then
            filecount+=1
            Redim As String tmp()
            string_split(path,"\",tmp())
            file= tmp(Ubound(tmp))
            If Instr(file," ") Then file=Chr(34)+file+Chr(34)
            sz+=Filelen(path)
        Else
            size(path) 'for nested folders
        End If
    Next n
    Return sz
End Function



Sub show(fullpath As String)
    Print "Please wait while counting . . ."
    If Instr(fullpath," ") Then fullpath=Chr(34)+fullpath+Chr(34)
    
    Dim As String g=pipeout( "dir "+fullpath)
    Var i=Instr(g,":")-1
    Var i1=Instr(Mid(g,i),Chr(10))-1
    Dim As String fpath=Mid(g,i,i1)+"\"
    Print fpath
    Dim As Const String dirlist="dir /b "
    Dim As String path=dirlist+ fullpath
    Var s=pipeout(path)
    Redim As String a()
    string_split(s,Chr(10),a())
    Dim As Ulong tot,d,n
    
    For n As Long=Lbound(a) To Ubound(a)
        sz=0
        d=size(fpath+ a(n))
        Print Mid(a(n),Instrrev(a(n),"\")+1);Tab(40);d;Tab(55); Iif(isfolder(fpath+ a(n)),"--->folder","")
        tot+=d
    Next
    
    Print Tab(35);"__________________"
    Print "Total size  ";Tab(40);tot
    Print "Files   ";filecount
    Print "Folders "; foldercount
End Sub

Dim As String fullpath="C:\fb17_64\FreeBASIC-1.07.1-win64\FreeBASIC-1.07.1-win64"
show(fullpath)
Print"DONE . . ."
Sleep

 
A run on a freebasic distro
result:

Code: Select all

Please wait while counting . . .
C:\fb17_64\FreeBASIC-1.07.1-win64\FreeBASIC-1.07.1-win64\
bin                                    27308032       --->folder
changelog.txt                          261503
doc                                    52574          --->folder
examples                               4339364        --->folder
fbc.exe                                2015232
inc                                    26792897       --->folder
lib                                    85030482       --->folder
readme.txt                             12130
                                  __________________
Total size                             145812214
Files   4051
Folders 250
DONE . . .
 
I can do this without the c runtime by using

#define isfolder(path) fileexists(path)=0
#define isfile(path) fileexists(path)

It is slightly slower.

Tested win 32, win 64 and gas64.
Post Reply