Skip to content

(feature request) add file size and date to dir plus make the output sortable, name, date, size #452

@thrive4

Description

@thrive4

Ok maybe this has been suggested in the past but I thought
it might be worthwhile to possibly revisit or re-evaluate expanding
the function dir() by adding File Attributes 'size' and 'date' plus
an optional parameter to sort the output (most likely on date, name, size)
in a platform independent manner.

The following code snippets are purely to illustrate various approaches,
intended use, and considering the pitfalls they bring with them, why it
would be preferable to have freebasic's dir() have this functionality natively.

The first two are "multi platform" the third is windows specific.

example:
edir.exe na f:\dev\freebasic
sorts on name ascending
edir.exe nd f:\dev\freebasic
sorts on name descending

version using dir()

#include once "file.bi"
#include once "string.bi"
' needed for getfilesandfolders
#include once "dir.bi"
' needed for date2unixtime
#include once "crt.bi"

' setup list
type LISTBOX
    as integer   file           ' nr of files           
    as integer   dirs           ' nr of dirs
    as single    tsize          ' cummulated size files
    as integer   total          ' nr items 
    as string    item(any)      ' item names
    as string    label(any)     ' screen name item use for translations etc
    as string    misc(any)      ' misc item use for launching apps or attributes
    as string    idate(any)     ' item use for dates
    as single    isize(any)     ' item use for size
end type
dim lst as LISTBOX

' used for automatic spacing columns 
dim shared as integer ioffset
ioffset = 0

function date2unixtime(stamp as string, current as boolean = false) as long
    dim as integer year, month, day, hour, minute, second

    day    = valint(mid(stamp, 1,  2))
    month  = valint(mid(stamp, 4,  2))
    year   = valint(mid(stamp, 7,  4))
    hour   = valint(mid(stamp, 12, 2))
    minute = valint(mid(stamp, 15, 2))
    second = valint(mid(stamp, 18, 2))

    if current then
        return time_(null)
    else
        return (year - 1970) * 31536000 + (month - 1) * 2592000 + (day - 1) * 86400 + hour * 3600 + minute * 60 + second
    end if
end function

sub sortswap(lst as LISTBOX, i as integer, j as integer)
    dim as string temp_item, temp_label, temp_misc, temp_idate
    dim as single temp_isize

    temp_item = lst.item(i)
    lst.item(i) = lst.item(j)
    lst.item(j) = temp_item

    temp_label = lst.label(i)
    lst.label(i) = lst.label(j)
    lst.label(j) = temp_label

    temp_misc = lst.misc(i)
    lst.misc(i) = lst.misc(j)
    lst.misc(j) = temp_misc

    temp_idate = lst.idate(i)
    lst.idate(i) = lst.idate(j)
    lst.idate(j) = temp_idate

    temp_isize = lst.isize(i)
    lst.isize(i) = lst.isize(j)
    lst.isize(j) = temp_isize
end sub


function sortlst(lst as LISTBOX, sortby as string) as boolean
    dim as integer i, j
    dim as string temp_item, temp_label, temp_misc, temp_idate
    dim as single temp_isize
    dim as long   temp_date1, temp_date2

    for i = 0 to lst.total - 1
        for j = i + 1 to lst.total - 1
            select case sortby
            case "n"
                if lst.item(j) < lst.item(i) then
                    sortswap(lst, i, j)
                end if
            case "label"
                if lst.label(j) < lst.label(i) then
                    sortswap(lst, i, j)
                end if
            case "misc"
                if lst.misc(j) < lst.misc(i) then
                    sortswap(lst, i, j)
                end if
            case "d"
                temp_date1 = date2unixtime(lst.idate(i))
                temp_date2 = date2unixtime(lst.idate(j))
                if temp_date2 < temp_date1 then
                    sortswap(lst, i, j)
                end if
            case "s"
                if lst.isize(j) < lst.isize(i) then
                    sortswap(lst, i, j)
                end if
            case "nd"
                if lst.item(j) > lst.item(i) then
                    sortswap(lst, i, j)
                end if
            case "label"
                if lst.label(j) > lst.label(i) then
                    sortswap(lst, i, j)
                end if
            case "misc"
                if lst.misc(j) > lst.misc(i) then
                    sortswap(lst, i, j)
                end if
            case "dd"
                temp_date1 = date2unixtime(lst.idate(i))
                temp_date2 = date2unixtime(lst.idate(j))
                if temp_date2 > temp_date1 then
                    sortswap(lst, i, j)
                end if
            case "sd"
                if lst.isize(j) > lst.isize(i) then
                    sortswap(lst, i, j)
                end if
            end select
        next j
    next i

    return true

end function

function getfilesandfolders(lst as listbox, path as string) as boolean
dim filename as string
    Dim As Integer i, dircount, filecount
    i = 0

    Const attrib_mask = fbNormal Or fbHidden Or fbSystem Or fbArchive or fbReadOnly or fbDirectory 
    Dim As UInteger out_attr

    ' get folders    
    chdir(path)
'    filename = Dir("*", fbDirectory)
    filename = Dir("*", attrib_mask, out_attr)
    Do Until Len(filename) = 0

        redim preserve lst.item(i)
        redim preserve lst.label(i)
        redim preserve lst.idate(i)
        redim preserve lst.misc(i)
        redim preserve lst.isize(i)
        lst.item(i)  = trim(lcase(filename))
        lst.label(i) = trim(filename)
        lst.misc(i)  = trim(filename)
        lst.idate(i) = format(FileDateTime(filename), "dd-mm-yyyy hh:mm")
        lst.isize(i) = filelen(filename)

        If out_attr = fbDirectory Then
            dircount += 1
        else
            filecount += 1
            lst.tsize += lst.isize(i)
        end if

        ' used for commandline output spacing
        if len(lst.item(i)) > ioffset then
            ioffset = len(lst.item(i))
        end if

        filename = Dir(out_attr)
        i += 1
    loop

    lst.total = i
    lst.file = filecount
    lst.dirs = dircount - 2

    return true

end function

' main
dim as string order, path, sort
sort = command(1)
path = command(2)

if command(1) = "/?" then
    print "edir <sort> <path>"
    print "sort:    d,  n,  s  equivelent of date, name, size ascending"
    print "sort:    dd, nd, sd equivelent of date, name, size descending"
    print "example: edir.exe s c:\windows sorts on size ascending"
    end
end if

if command(1) = "" then
    sort = "n"
end if
if command(2) = "" then
    path = exepath
end if

getfilesandfolders(lst, path)
sortlst(lst, sort) 

for yy as integer = 0 to lst.total - 1
    print lst.label(yy);space((ioffset + 5) - Len(lst.item(yy)));lst.idate(yy);space(20 - Len(lst.idate(yy)));lst.isize(yy)
next yy

print
Print "file(s)   " & lst.file & " " & lst.tsize & " bytes"
print "folder(s) " & lst.dirs

End

version using stats, the c approach (kudos to dodicat)

#include once "file.bi"
#include once "string.bi"
#include "crt.bi"

' courtesy https://www.freebasic.net/forum/viewtopic.php?t=32809&hilit=popen dodicat
Declare Function stats Cdecl Alias "_stat"(As zstring Ptr,As Any Ptr) As Integer
dim shared as integer ioffset
dim as string dummy, locale, order, path, sort, s
sort  = command(1)
path  = command(2)
ioffset = 0

' setup list
type LISTBOX
    as integer   file           ' nr of files           
    as integer   dirs           ' nr of dirs
    as single    tsize          ' cummulated size files
    as integer   total          ' nr items 
    as string    item(any)      ' item names
    as string    label(any)     ' screen name item use for translations etc
    as string    misc(any)      ' misc item use for launching apps or attributes
    as string    idate(any)     ' item use for dates
    as single    isize(any)     ' item use for size
end type
dim lst as LISTBOX

if command(1) = "/?" then
    print "fdir <sort> <path>"
    print "sort:    d,  n,  s  equivelent of date, name, size ascending"
    print "sort:    dd, nd, sd equivelent of date, name, size descending"
    print "example: fdir.exe s c:\windows sorts on size ascending"
    end
end if

if command(1) = "" then
    sort = "n"
end if
if command(2) = "" then
    path = exepath
end if

Function pipeout(lst as listbox, Byval s As String = "") Byref As String
    Var f = Freefile
    Dim As String  tmp
    dim as integer cnt = 0

    Open Pipe s For Input As #f 
        s = ""
        Do Until Eof(f)
            Line Input #f, tmp
            if len(tmp) > 2 then
                redim preserve lst.item(cnt)
                redim preserve lst.label(cnt)
                redim preserve lst.misc(cnt)
                redim preserve lst.idate(cnt)
                redim preserve lst.isize(cnt)
                lst.item(cnt) = trim(tmp)
                lst.label(cnt) = tmp
                lst.misc(cnt) = tmp
                lst.idate(cnt) = tmp
                lst.isize(cnt) = 1
                cnt += 1
            end if
            s += tmp + Chr(10)
        Loop
    Close #f
    lst.total = cnt

    Return s

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

' windows
#ifdef __FB_WIN32__
locale = ""
' issue difficult to force a date time notation ....
select case sort
    case "n"
        dummy = "dir /o "
    case "s"
        dummy = "dir /os "
    case "d"
        dummy = "dir /od "
    case "nd"
        dummy = "dir /o- "
    case "sd"
        dummy = "dir /o-s "
    case "dd"
        dummy = "dir /o-d "
    case else
        print "incorrect switch ... "& sort
        end
end select
' unix
#else
locale = "--time-style=long-iso"
select case sort
    case "n"
        dummy = "ls -al "
        ' reverse dir /o-
        'ls -al
        'reverse ls -alr
    case "s"
        dummy = "ls -alhS "
        ' reverse dir /o-s
        'ls -alhS
        'reverse ls -alhSr
    case "d"
        dummy = "ls -alt "
        ' reverse dir /o-d
        'ls -alt
        'reverse ls -altr
        'european ls -al --time-style=long-iso
    case "nd"
        dummy = "ls -alr "
        ' reverse dir /o-
        'ls -al
        'reverse ls -alr
    case "sd"
        dummy = "ls -alhSr "
        ' reverse dir /o-s
        'ls -alhS
        'reverse ls -alhSr
    case "dd"
        dummy = "ls -altr "
        ' reverse dir /o-d
        'ls -alt
        'reverse ls -altr
        'european ls -al --time-style=long-iso
    case else
        print "incorrect switch ... "& sort
        end
end select
#endif

s = pipeout(lst, dummy + locale + " " + chr$(34) + path + chr$(34))

' dir equivelent by approximation
'ls -al;df -h -T
' odd why is lst.file 1
lst.file = -1

#ifdef __FB_WIN32__
    ' used for commandline output spacing
    for i as integer = 2 to lst.total - 3
        dummy = trim(mid(lst.item(i), instrrev(lst.item(i), " ")))
        if instr(lcase(lst.item(i)), "<dir>") > 0 then
            lst.dirs += 1
        else
            lst.file += 1
            lst.tsize = lst.tsize + filelen(path + "\" + dummy) 
        end if

        if len(dummy) > ioffset then
            ioffset = len(dummy)
        end if
    next i
    ' metric
    'print "label ";trim(mid(lst.item(0), instrrev(lst.item(0), " ")))
    'print "serial ";trim(mid(lst.item(1), instrrev(lst.item(1), " ")))
#else
#endif

for i as integer = 2 to lst.total - 3
    #ifdef __FB_WIN32__
        ' metric windows
'        if i = 0 then
'        end if
'        if i = 1 then
'        end if
    if i > 2 then
        dummy = trim(mid(lst.item(i), instrrev(lst.item(i), " ")))
        print dummy;space((ioffset + 5) - Len(dummy));format(FileDateTime(path + "\" + dummy), "dd-mm-yyyy hh:mm");space(5);filelen(path + "\" + dummy)
        ' + chr$(13) + chr$(10)
    end if
    #else
        ' metric unix
    #endif
next i

print
Print "file(s)   " & lst.file & " " & lst.tsize & " bytes"
print "folder(s) " & lst.dirs - 2

' metric
'print "free space ";lst.item(lst.total - 1)
'print "'";rtrim(s,chr(10));"'"
'print cbool(isfolder(rtrim(s,chr(10))))

End

version using windows api FindFirstFile and WIN32_FIND_DATA


' needed for getfilesandfolders
#include once "file.bi"
#include once "string.bi"
#include once "dir.bi"
' needed for date2unixtime
#include once "crt.bi"

#ifdef __FB_WIN32__
    #Include Once "windows.bi"
    #Include Once "win/shellapi.bi"
#else
    #Include Once "crt/stdio.bi"
#endif

' setup list
type LISTBOX
    as integer   file           ' nr of files           
    as integer   dirs           ' nr of dirs
    as single    tsize          ' cummulated size files
    as integer   total          ' nr items 
    as string    item(any)      ' item names
    as string    label(any)     ' screen name item use for translations etc
    as string    misc(any)      ' misc item use for launching apps or attributes
    as string    idate(any)     ' item use for dates
    as single    isize(any)     ' item use for size
end type
dim lst as LISTBOX

' used for automatic spacing columns 
dim shared as integer ioffset
ioffset = 0

function date2unixtime(stamp as string, current as boolean = false) as long
    dim as integer year, month, day, hour, minute, second

    day    = valint(mid(stamp, 1,  2))
    month  = valint(mid(stamp, 4,  2))
    year   = valint(mid(stamp, 7,  4))
    hour   = valint(mid(stamp, 12, 2))
    minute = valint(mid(stamp, 15, 2))
    second = valint(mid(stamp, 18, 2))

    if current then
        return time_(null)
    else
        return (year - 1970) * 31536000 + (month - 1) * 2592000 + (day - 1) * 86400 + hour * 3600 + minute * 60 + second
    end if
end function

sub sortswap(lst as LISTBOX, i as integer, j as integer)
    dim as string temp_item, temp_label, temp_misc, temp_idate
    dim as single temp_isize

    temp_item = lst.item(i)
    lst.item(i) = lst.item(j)
    lst.item(j) = temp_item

    temp_label = lst.label(i)
    lst.label(i) = lst.label(j)
    lst.label(j) = temp_label

    temp_misc = lst.misc(i)
    lst.misc(i) = lst.misc(j)
    lst.misc(j) = temp_misc

    temp_idate = lst.idate(i)
    lst.idate(i) = lst.idate(j)
    lst.idate(j) = temp_idate

    temp_isize = lst.isize(i)
    lst.isize(i) = lst.isize(j)
    lst.isize(j) = temp_isize
end sub

function sortlst(lst as LISTBOX, sortby as string) as boolean
    dim as integer i, j
    dim as string temp_item, temp_label, temp_misc, temp_idate
    dim as single temp_isize
    dim as long   temp_date1, temp_date2

    for i = 0 to lst.total - 1
        for j = i + 1 to lst.total - 1
            select case sortby
            case "n"
                if lst.item(j) < lst.item(i) then
                    sortswap(lst, i, j)
                end if
            case "label"
                if lst.label(j) < lst.label(i) then
                    sortswap(lst, i, j)
                end if
            case "misc"
                if lst.misc(j) < lst.misc(i) then
                    sortswap(lst, i, j)
                end if
            case "d"
                temp_date1 = date2unixtime(lst.idate(i))
                temp_date2 = date2unixtime(lst.idate(j))
                if temp_date2 < temp_date1 then
                    sortswap(lst, i, j)
                end if
            case "s"
                if lst.isize(j) < lst.isize(i) then
                    sortswap(lst, i, j)
                end if
            case "nd"
                if lst.item(j) > lst.item(i) then
                    sortswap(lst, i, j)
                end if
            case "label"
                if lst.label(j) > lst.label(i) then
                    sortswap(lst, i, j)
                end if
            case "misc"
                if lst.misc(j) > lst.misc(i) then
                    sortswap(lst, i, j)
                end if
            case "dd"
                temp_date1 = date2unixtime(lst.idate(i))
                temp_date2 = date2unixtime(lst.idate(j))
                if temp_date2 > temp_date1 then
                    sortswap(lst, i, j)
                end if
            case "sd"
                if lst.isize(j) > lst.isize(i) then
                    sortswap(lst, i, j)
                end if
            end select
        next j
    next i

    return true

end function

function getfilesandfolders(lst as listbox, path As String) as boolean
    Dim As Integer i, dircount, filecount
    i = 0
    #ifdef __FB_WIN32__
        Dim hFind As Long
        Dim wfd As WIN32_FIND_DATA
        Dim pathWithWildcard As String = path & "\*"
        hFind = FindFirstFile(StrPtr(pathWithWildcard), @wfd)
        If hFind <> 0 Then
            Do
                redim preserve lst.item(i)
                redim preserve lst.label(i)
                redim preserve lst.idate(i)
                redim preserve lst.misc(i)
                redim preserve lst.isize(i)
                lst.item(i)  = trim(lcase(wfd.cFileName))
                lst.label(i) = trim(wfd.cFileName)
                lst.idate(i) = format(FileDateTime(trim(path + "\" + wfd.cFileName)), "dd-mm-yyyy hh:mm")
                lst.isize(i) = wfd.nFileSizeLow
                If (wfd.dwFileAttributes And fbDirectory) Then
                    dircount += 1
                Else
                    filecount += 1
                    lst.tsize += lst.isize(i)
                End If
                if len(lst.item(i)) > ioffset then
                    ioffset = len(lst.item(i))
                end if
                i += 1
            Loop While FindNextFile(hFind, @wfd)
            FindClose(hFind)
        End If
    #else
    #endif

    lst.total = i
    lst.file = filecount
    lst.dirs = dircount - 2

    return true

End function

' main
dim as string path, sort, s
path = command(2)
sort = command(1)

if command(1) = "/?" then
    print "dir <sort on> <path>"
    print "options name, size, date"
    print "example: edir.exe size c:\windows"
    end
end if

if command(1) = "" then
    sort = "n"
end if
if command(2) = "" then
    path = exepath
end if

getfilesandfolders(lst, path)
sortlst(lst, sort) 

for yy as integer = 0 to lst.total - 1
    print lst.label(yy);space((ioffset + 5) - Len(lst.item(yy)));lst.idate(yy);space(20 - Len(lst.idate(yy)));lst.isize(yy)
next yy

print
Print "file(s)   " & lst.file & " " & lst.tsize & " bytes"
print "folder(s) " & lst.dirs

'GetFilesAndFolders("/home/user/Documents")

End

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions