Scanning Directories (Reading Directory Entries)

From BeebWiki
Revision as of 04:33, 14 January 2012 by Jgharston (talk) (Updated formatting, added missing line from DFS code.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

OSGBPB 8 (often known as OS HeebieJeebie) scans the current directory and returns the entries.

OSGBPB 8 has the following entry and exit conditions. To call from BASIC you should point X% to a control block and set Y%=X%DIV256.

 On entry at &FFD1:
   A%   8
   X%!1 Data address
   X%!5 Number of object names to read
   X%!9 Directory index, 0 to start
 
 On exit:
   X%?0 Directory cycle number
   X%!1 Updated data address
   X%!5 Number of object names /not/ read
   X%!9 Directory index for next call.
 
 The data block will hold the following information:
     &00 length of object name 1 (n)
     &01 object name 1 in ASCII characters
   &01+n length of object name 2 (m)
   &02+n object name 2 in ASCII characters
 &02+n+m etc...

Note that the directory index is an opaque value with only the value zero defined. You cannot and must not assume that it follows any particular sequence, for instance incrementing by one after each call. The only thing you can do with a nonzero value is pass it to another OSGBPB call. Some filing systems return the object names padded with trailing spaces, so you will need to strip those spaces if you want to append the names, such as when descending into a directory tree.

Reading directory entries

The following code will scan through a directory on any filing system and return the directory entries.

 DIM ctrl% 31:REM Need at least 13 bytes for OSGBPB
 DIM name% 31:REM Need at least 12 bytes for directory entries
 
 X%=ctrl%:Y%=X%DIV256        :REM Point to control block
 
 ret%=0                      :REM Return value
 idx%=0                      :REM Directory index, starts at zero
 REPEAT                   
   X%!1=name%                :REM Address in memory to read names to
   X%!5=1                    :REM Fetch one directory entry
   X%!9=idx%                 :REM Directory index
   A%=8:CALL &FFD1           :REM OSGBPB call to read directory entries
   ret%=X%!5                 :REM Number of entries not read
   idx%=X%!9                 :REM Get directory index back
   IF ret%<>1 THEN PROCname  :REM Directory entry returned
 UNTIL ret%=1
 ...etc
 
 DEFPROCname                 :REM Extract object name
 name%?(1+?name%)=13         :REM Put <cr> terminator in
 f$=$(name%+1)               :REM Fetch object name
 ...etc

Using the FileIO BASIC Library this can be considerably compressed to the following:

 idx%=0:REPEAT:f$=FNgbpb8(idx%):idx%=X%!9:IF f$<>"" THEN ....
 UNTIL f$=""

The data block passed in X%!1 needs to be long enough to receive the filenames read. Most filenames encountered on BBC systems are a maximum of 10 characters long, so name% needs to point to at least 12 bytes - ten characters for the filename, one byte for the name length and one byte for the <cr> terminator.

However, there is nothing that specifies that filenames are a maximum of ten characters. Filenames can be anything up to 255 bytes long and it is entirely down the the filing system what length names it can use. For instance, DOSFS uses up to twelve character filenames, viz "Filename.txt", and Win95FS can use almost unlimited filename lengths. Strictly speaking, there should be 257 bytes available at the address pointed to by X%!1, but 32 bytes is a realistic compromise.

Reading entries from all DFS directories

OSGBPB 8 reads the directory entries from the current directory. DFS implements single-character 'sideways' directories. The following code will run on all filing systems, and on DFS will scan through all the DFS directories. It uses the FileIO BASIC Library.

 DIM ctrl% 31:REM Need at least 13 bytes for OSGBPB
 DIM name% 31:REM Need at least 12 bytes for directory entries
 
 X%=ctrl%:Y%=X%DIV256          :REM Point to control block
 
 dfs_all%=TRUE                 :REM TRUE to do all DFS directories
 
 dfs%=FNfile("$",5)<>2         :REM Test for DFS-line filing system
 dir%=0
 IF dfs_all% THEN IF dfs% THEN dir%=ASC"!":REM Start at dir "!"
 REPEAT
   IF dir% THEN OSCLI "Dir """+STRING$(1+(dir%=34),CHR$dir%)+"""
   idx%=0                      :REM Directory index, starts at zero
   REPEAT                   
     f$=FNgbpb8(idx%):idx%=X%!9:IF f$<>"" THEN PROCname
   UNTIL f$=""
   IF dir%=95:dir%=0           :REM Loop for all directories
   IF dir% THEN dir%=dir%+1:IF dir%=ASC":" THEN dir%=dir%+1:REM Skip ':'
 UNTIL dir%=0
 ...etc
 
 DEFPROCname
 IFdir%:IFdir%<>36:f$=CHR$dir%+"."+f$  :REM Prepend with non-"$" dir. char
 ...etc

Tree Walking (Recursive Directory Scanning)

OSGBPB 8 can 'walk' a directory tree, descending subdirectories and returning from them with the following code, written using the FileIO BASIC library.

 DIM ctrl% 31,name% 31:X%=ctrl%:Y%=X%DIV256
 
 DEFPROCScan
 LOCAL idx%,f$
 REPEAT:f$=FNgbpb8(idx%):idx%=X%!9:IF f$<>"":PROCObject
 UNTIL f$=""
 ENDPROC
 :
 DEFPROCObject
 LOCAL type%
 type%=FNfile(f$,5)
 REM Process object f$
 IF type%=2:OSCLI "Dir "+f$:PROCScan:OSCLI "Dir ^"
 ENDPROC

Note that as 6502 BASIC only implements a 20-deep REPEAT stack, this will only descend to a maximum depth of 20 subdirectories before getting a "Too many REPEATs" error. To avoid this a program would have to maintain its own recursion stack.

See Also

Jgharston 14:34, 4 January 2008 (UTC)