//edit 2022-02-21: added error handling
Code: Select all
; -- Get sizes of a directory and its sub-directories
; tested with PB 5.73 LTS (32 bit and 64 bit)
; <https://www.purebasic.fr/english/viewtopic.php?t=78722>
EnableExplicit
Structure Node
name$
level.i
bytes.q
EndStructure
Procedure.q _TreeSize (path$, List dir.Node(), level.i=0)
; -- recursive post-order traversal of a directory tree
; in : path$: path *without* trailing separator
; level: recursion level
; out: dir() : list containing 'path$' and its subdirectories
; return value: total size of all files in 'path$'
; (including all sub-directories);
; -1 on error reading 'path$' itself
; -2 on error reading sub-tree of 'path$'
Protected id.i, subSize.q, localSize.q=0, ret.q=0
id = ExamineDirectory(#PB_Any, path$, "")
If id
While NextDirectoryEntry(id)
If DirectoryEntryType(id) & #PB_DirectoryEntry_File
localSize + DirectoryEntrySize(id)
ElseIf (DirectoryEntryType(id) & #PB_DirectoryEntry_Directory) And
(FindString("..", DirectoryEntryName(id)) = 0)
subSize = _TreeSize(path$ + #PS$ + DirectoryEntryName(id), dir(), level+1)
If ret >= 0 And subSize >= 0
ret + subSize
Else
ret = -2
EndIf
EndIf
Wend
FinishDirectory(id)
If ret >= 0
ret + localSize
EndIf
Else
ret = - 1
EndIf
AddElement(dir())
dir()\name$ = path$
dir()\level = level
dir()\bytes = ret
ProcedureReturn ret
EndProcedure
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
#PSChars$ = "\/" ; characters used as path separators
CompilerElse
#PSChars$ = "/"
CompilerEndIf
Macro RTrimPS (_path_)
; _path_ must not be an expression, but a variable.
If FindString(#PSChars$, Right(_path_,1)) <> 0
_path_ = Left(_path_, Len(_path_)-1)
EndIf
EndMacro
Procedure TreeSize (path$, List dir.Node(), sortBySize.i=#False)
; The paths can only be sorted meaningfully *without* a trailing separator.
RTrimPS(path$)
_TreeSize(path$, dir())
If ListSize(dir()) > 1
SortStructuredList(dir(), #PB_Sort_Ascending|#PB_Sort_NoCase, OffsetOf(Node\name$), TypeOf(Node\name$))
If sortBySize
SortStructuredList(dir(), #PB_Sort_Descending, OffsetOf(Node\bytes), TypeOf(Node\bytes))
EndIf
EndIf
EndProcedure
;-- Demo
Procedure.s StrR (n.q, width.i)
; -- convert a quad number into a right-aligned string
Protected s$ = Str(n)
If Len(s$) < width
ProcedureReturn RSet(s$, width)
Else
ProcedureReturn s$
EndIf
EndProcedure
Define path$
NewList dir.Node()
; --------------------------------------------------
path$ = ... ; put your path here
; --------------------------------------------------
If FileSize(path$) <> -2
Debug "Directory '" + path$ + "' not found."
End
EndIf
TreeSize(path$, dir(), #True)
Debug "Size of '" + path$ + "' (including sub-directories):"
Debug ""
Debug "Level Bytes Name"
Debug "----- ----- ----"
ForEach dir()
Debug StrR(dir()\level, 4) + Space(5) + StrR(dir()\bytes, 11) + Space(4) + dir()\name$ + #PS$
Next