using dir$ to fill an array

New to FreeBASIC? Post your questions here.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Post by coderJeff »

@lillo, yeah, it does look a little un-cool. ( lol, even for basic). But I think it will work, and it would certainly be much easier/faster to implement/document than a adding a new keyword.

filename = Dir([filespec [, filter [, attribs]]] )

I think an extra param should be added to Dir(). This is a good way to do it.
hippy
Posts: 84
Joined: Nov 04, 2005 18:13
Location: UK

Found the problem !

Post by hippy »

Create files and directory with all possible attributes ( CREATE.BAT ) ...

Code: Select all

@ECHO OFF

If "%1" == "" Goto CreateAll

MkDir C:\TESTDIR\%1.DIR
Attrib %2 %3 %4 %5 C:\TESTDIR\%1.DIR

Echo > C:\TESTDIR\%1.TXT
Attrib %2 %3 %4 %5 C:\TESTDIR\%1.TXT

Goto Exit

:CreateAll

MkDir C:\TESTDIR

Call Create   ASHR +A +S +H +R
Call Create   -SHR -A +S +H +R
Call Create   A-HR +A -S +H +R
Call Create   --HR -A -S +H +R
Call Create   AS-R +A +S -H +R
Call Create   -S-R -A +S -H +R
Call Create   A--R +A -S -H +R
Call Create   ---R -A -S -H +R
Call Create   ASH- +A +S +H -R
Call Create   -SH- -A +S +H -R
Call Create   A-H- +A -S +H -R
Call Create   --H- -A -S +H -R
Call Create   AS-- +A +S -H -R
Call Create   -S-- -A +S -H -R
Call Create   A--- +A -S -H -R
Call Create   ---- -A -S -H -R

:Exit
FreeBASIC test program (TEST.BAS) ...

Code: Select all

' Use "Test -1"     for Dir$("C:\TESTDIR")
' Use "Test attrib" for Dir$("C:\TESTDIR",attrib)

If Val(Command$) < 0 Then
  Print
  filename$ = Dir$("C:\TESTDIR\*.*")
  While filename$ <> ""
    Print "Dir$(""C:\TESTDIR\*.*"") = "+filename$
    filename$ = Dir$
  Wend
Else
  If ( Val(Command$) And 1  ) <> 0 Then Print "ReadOnly ";
  If ( Val(Command$) And 2  ) <> 0 Then Print "Hidden ";
  If ( Val(Command$) And 4  ) <> 0 Then Print "System ";
  If ( Val(Command$) And 16 ) <> 0 Then Print "Directory ";
  If ( Val(Command$) And 32 ) <> 0 Then Print "Archive ";
  Print
  filename$ = Dir$("C:\TESTDIR\*.*",Val(Command$))
  While filename$ <> ""
    Print "Dir$(""C:\TESTDIR\*.*"","+Command$+") = "+filename$
    filename$ = Dir$
  Wend
End If
Results ...

Code: Select all

C:\FreeBas>TEST 16
Directory
Dir$("C:\TESTDIR\*.*",16) = .
Dir$("C:\TESTDIR\*.*",16) = ..
Dir$("C:\TESTDIR\*.*",16) = ----.DIR
Dir$("C:\TESTDIR\*.*",16) = ----.TXT

C:\FreeBas>TEST 48
Directory Archive
Dir$("C:\TESTDIR\*.*",48) = .
Dir$("C:\TESTDIR\*.*",48) = ..
Dir$("C:\TESTDIR\*.*",48) = A---.DIR
Dir$("C:\TESTDIR\*.*",48) = A---.TXT
Dir$("C:\TESTDIR\*.*",48) = ----.DIR
Dir$("C:\TESTDIR\*.*",48) = ----.TXT
Dir$("*.*",16) doesn't return all the files nor all the directories, only those which don't have the Archive bit set, don't have the System bit set, and don't have the Hidden bit set, and don't have the Read Only bit set.

The confusion I was seeing was because some files have their Archive bits set ( most of them ), but not all.

The attrib matching code would seem at first glance to be along the lines of ...

If ( GetFileAttrib(filename$) = 0 ) Or ( ( GetFileAttrib(filename$) And attrib ) <> 0 ) Then

but that doesn't hold up to analysis, because it isn't returning those which are Hidden or Read Only. But neither is it ...

If ( GetFileAttrib(filename$) = 0 ) Or ( GetFileAttrib(filename$) = attrib ) Then

I guess it would be possible to analyse the results and determine the algorithm actually used, but it would be easier if someone could tell us :-)

Whatever the algorithm used at present, I'm not convinced it's possible to produce a list of particular types of files/directories excluding other types without a lot of work.
hippy
Posts: 84
Joined: Nov 04, 2005 18:13
Location: UK

Post by hippy »

As to the suggestion of a thrid parameter for Dir$; I think that's horrible.

If it a job's worth doing, it's worth doing well. Either add a new FileAttrib function ( is it really that much more difficult ? ), or leave it as it is. The third parameter is a nasty kludge IMHO.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Post by coderJeff »

I don't think it's a nasty kludge; just different.

For Example:

Code: Select all

'' Classic example
dim d as string
d = Dir("*.*",16)
while d > ""
  print d
  d = Dir()
wend
print
print

'' New form of Dir
dim retattrib as integer
d = DirEx("*.*",16,retattrib)
while d > ""
  print hex(retattrib),d
  d = DirEx(,,retattrib)
wend
So here's an FB version of the win32-Dir() function (slightly simplified) found in the rt-lib with an additional parameter that returns the attrib of the file. This will only run on fbc for win32. (I'd have to make different versions for dos/linux) Source lines marked with ''New show what would change to include a returned attrib parameter.

I made it in FB so users could play around with it's operation/behaviour without having to rebuild the rt-lib / compiler.

Code: Select all

Option Explicit
#include "crt.bi"
dim shared dirhandle as integer = 0
dim shared dirattrib as integer = 0
dim shared dirinuse as integer = 0
dim shared dirdata as _finddata_t

sub close_dir
  _findclose(dirhandle)
  dirinuse = 0
end sub

function find_next() as string
	do
    if( _findnext( dirhandle, @dirdata ) ) then
      close_dir( )
      function = ""
      exit do
    end if
    function = dirdata.name
  loop while( dirdata.attrib and (not dirattrib ))
end function

function DirEx(strFile as string = "", byval attrib = &h33, byref retattrib as integer = 0) as string
  dim ret as string
  retattrib = -1                    '' New
  function = ""
  if len(strfile) > 0 then
    if dirinuse then
      close_dir
    end if
    dirhandle = _findfirst( @strfile[0], @dirdata)
    if dirhandle <> -1 then
      dirattrib = attrib Or &hFFFFFF00
      if ((attrib and &h10) = 0) then
        dirattrib or= &h20
      end if
      if( dirdata.attrib and (not dirattrib )) then
        ret = find_next( )
      else
        ret = dirdata.name
      endif
      if( ret > "" ) then
        retattrib = dirdata.attrib   '' New
        dirinuse = 1
      endif
    endif
  else
    if dirinuse then
      ret = find_next()
      if (ret > "") then            '' New
        retattrib = dirdata.attrib  '' New
      endif                         '' New
    end if
  endif
  function = ret
end function
I like this idea for the dir() function.

It should also be possible to make a GetAttr(filename) function as in visual basic since win32 has GetFileAttributes() and djgpp has _dos_getfileattr(). I guess linux would have to be simulated. I just don't see an huge need for it as it is typically used with Dir(), and returning the attrib with Dir() is very efficient, and the rtl-code could be changed with little work.

@hippy, cool test program by the way. Dir() still needs to be fixed for behaviour and the test program you made certainly shows where the problems are.
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Post by Sisophon2001 »

Please don’t forget about this. Any solution would be great.

Garvan
yetifoot
Posts: 1710
Joined: Sep 11, 2005 7:08
Location: England
Contact:

Post by yetifoot »

Hi there CoderJeff,

i recently was trying out _findfirst etc, and found it found it wouldn't work on Linux, is there a way? i tried removing the underscore in the declaration but no joy.
ShadowDust
Posts: 274
Joined: Oct 13, 2005 2:05
Contact:

Post by ShadowDust »

Option Explicit
#include "crt.bi"
dim shared dirhandle as integer = 0
dim shared dirattrib as integer = 0
dim shared dirinuse as integer = 0
dim shared dirdata as _finddata_t

sub close_dir
_findclose(dirhandle)
dirinuse = 0
end sub

function find_next() as string
do
if( _findnext( dirhandle, @dirdata ) ) then
close_dir( )
function = ""
exit do
end if
function = dirdata.name
loop while( dirdata.attrib and (not dirattrib ))
end function

function DirEx(strFile as string = "", byval attrib = &h33, byref retattrib as integer = 0) as string
dim ret as string
retattrib = -1 '' New
function = ""
if len(strfile) > 0 then
if dirinuse then
close_dir
end if
dirhandle = _findfirst( @strfile[0], @dirdata)
if dirhandle <> -1 then
dirattrib = attrib Or &hFFFFFF00
if ((attrib and &h10) = 0) then
dirattrib or= &h20
end if
if( dirdata.attrib and (not dirattrib )) then
ret = find_next( )
else
ret = dirdata.name
endif
if( ret > "" ) then
retattrib = dirdata.attrib '' New
dirinuse = 1
endif
endif
else
if dirinuse then
ret = find_next()
if (ret > "") then '' New
retattrib = dirdata.attrib '' New
endif '' New
end if
endif
function = ret
end function
i really dont have time to study this :/ how can i use it to fill an array(1 for directory's, 1 for file's).. i've been messing around with the first way.. but it just doesnt work right
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Post by coderJeff »

yetifoot wrote:Hi there CoderJeff,

i recently was trying out _findfirst etc, and found it found it wouldn't work on Linux, is there a way? i tried removing the underscore in the declaration but no joy.
Sorry, I missed this post. Yeah, linux has a different mechanism for getting file/dir info. I just reconstructed the win32 version (because that's what I have) in FB in case anyone wanted to see how Dir works (or doesn't depending on the point of view) If Dir() function in the fb-rtlib were modified to return an attrib for Dir, it would make Dir() much more flexible and would then offer the portable solution between platforms.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Post by coderJeff »

ShadowDust wrote:i really dont have time to study this :/ how can i use it to fill an array(1 for directory's, 1 for file's).. i've been messing around with the first way.. but it just doesnt work right
Using the DirEx() function already posted here's a couple of ideas:

Code: Select all

dim retattrib as integer, d as string, i as integer

'' get all normal dirs
d = DirEx("c:\*.*",,retattrib) '' get all dirs
while d > "" 
  if (retattrib and 16) <> 0 then
    print "dir  "; hex(retattrib),d
  end if
  d = DirEx(,,retattrib)
wend 

'' get all normal files
d = DirEx("c:\*.*",,retattrib) '' get all files
while d > "" 
  if (retattrib and 16) = 0 then
    print "file "; hex(retattrib),d
  end if
  d = DirEx(,,retattrib)
wend 
Store files/dirs in one pass to two arrays:

Code: Select all

redim dirlist(1) as string, ndirlist as integer = 0
redim fillist(1) as string, nfillist as integer = 0

'' get all dirs/files (incl. system/hidden)
d = DirEx("c:\*.*",511,retattrib) 
while d > "" 
  if (retattrib and 16) = 0 then  '' is a file
    nfillist += 1
    redim preserve fillist(nfillist) as string
    fillist(nfillist) = d
  else
    ndirlist += 1
    redim preserve dirlist(ndirlist) as string
    dirlist(ndirlist) = d
  end if
  d = DirEx(,,retattrib)
wend 

for i = 1 to ndirlist
  print dirlist(i)
next i

for i = 1 to nfillist
  print fillist(i)
next i
ShadowDust
Posts: 274
Joined: Oct 13, 2005 2:05
Contact:

Post by ShadowDust »

thx. this worked plus, CoderJeff ur credits has been incorporated. please tell if credit is due elsewhere as well.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Post by coderJeff »

thanks, I appreciate it, but credit is not necessary. The code itself comes from the fb-runtime library so i guess you could credit the freebasic devs.

I just converted the code from C to freebasic and posted it so some people could play around with it. I think it would be a good idea to change the Dir() command and I will post a feature request on sf. But first I was hoping for a little discussion about it. For example, the filter attrib doesn't produce the exact same results across win32/dos/linux. hippy previously posted showing the problems with the attrib filter. What should the behaviour of the attrib param be? For me, as long as there is a way to call Dir() that gets all the files/dirs _and_ returns the attrib, I'm fine with that.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Post by coderJeff »

Sisophon2001 wrote:Please don’t forget about this. Any solution would be great.
I recently noticed a bug report on sf about dir() and added links to this topic as a comment.

Adding a third parameter for dir seems like a doable and useful solution. If you don't know what I'm talking about, read the entire post.

My personal preference on Dir(spec) is that it should return all files and folders (including files with no attributes at all) and that Dir(spec, 16) should return just directories. With an extra parameter to give the actual attributes of the file (simulated for linux) additional filtering based on attrib can be done be in the user's code.
tunginobi
Posts: 655
Joined: Jan 10, 2006 0:44
Contact:

Post by tunginobi »

I agree with coderJeff. dir() should just flat out return files and directories, and leave the flag handling to the developer. It'd be uniform, and more importantly, it would work as people expect it to. What's the point of a function if you can't predict it's output (RND aside for a moment)?

A quick question, though: why would attribute retrieval have to be simulated for Linux?

...

Oh wait, never mind. I figured it out. FAT-type filesystems have Archive, Hidden and Read-Only attributes. Hidden files on a Linux fs are just those whose first character is the period (.), and read-only is easily obtainable by taking the file's relevant permissions (according to which user is running). I don't think there's any equivalent to Archive, but then again, who really uses it?
Post Reply