Parse DIR listing for folder name and size

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

Parse DIR listing for folder name and size

Postby nfunk » Jun 21, 2020 16:15

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: 2880
Joined: Jun 02, 2015 16:24

Re: Parse DIR listing for folder name and size

Postby Tourist Trap » Jun 21, 2020 18:09

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: 2079
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Parse DIR listing for folder name and size

Postby badidea » Jun 21, 2020 20:18

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: 6557
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Parse DIR listing for folder name and size

Postby dodicat » Jun 21, 2020 20:24

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: 2079
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Parse DIR listing for folder name and size

Postby badidea » Jun 21, 2020 22:33

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: 12
Joined: Nov 07, 2019 20:08

Re: Parse DIR listing for folder name and size

Postby adeyblue » Jun 22, 2020 0:38

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: 3596
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Parse DIR listing for folder name and size

Postby TJF » Jun 22, 2020 7:42

@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
Posts: 9720
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Parse DIR listing for folder name and size

Postby fxm » Jun 22, 2020 8:42

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: 586
Joined: May 05, 2017 19:59
Location: Germany

Re: Parse DIR listing for folder name and size

Postby UEZ » Jun 22, 2020 9:38

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

Postby nfunk » Jun 22, 2020 13:22

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: 6557
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Parse DIR listing for folder name and size

Postby dodicat » Jun 22, 2020 17:01

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.

Return to “General”

Who is online

Users browsing this forum: No registered users and 8 guests