1 rem --------------------- cut  here ---------------------|

1 CLS:DELETE_DIRECTORY 'ram1_','temp_', 1
2 :

rem                  Careless use of this program
rem may have   A   D E V A S T A T I N G    E F F E C T
rem                          on your life
rem for all I know. I could list reams of possible ways things could
rem go wrong, and the consequences subsequent thereunto, as a result
rem of using this potentially HAZARDOUS software; whether used as
rem intended, or in error. Please take this WARNING seriously,
rem because you see, whatever happens, that'll be ENTIRELY
rem YOUR OWN FAULT. I accept NO responsibility WHATSOEVERNONOTI!
rem for YOUR ACTIONS - or your rotten luck, for that matter.
rem However, if you like it, if it improves your understanding or
rem inspires you if, in a funny sort of way, it leads to your
rem promotion, or saves your marriage, teaches your kids to break
rem the bank, or.. Then REMEMEBER ME. That's MY doing. That shows I
rem got it right, and that YOU took good note of this warning!

11 REMark S*BASIC PROCedure to delete an
12 REMark     entire directory tree
13 REMark   Requires tk2 or equivalent
14 REMark          ©PWitte 1998
15 REMark Use at own risk! No warranties!
16 REMark Parameters:
17 REMark  dev$ = device; eg win1_, ram2_
18 REMark  dir$ = directory; eg temp_
19 REMark  tree:  0 => files only
20 REMark       <>0 => dirs and files
21 :

rem For the benefit of qlers (of all ages) who, like myself,
rem enjoy reading (short snippets!) of other people's commented
rem code. I enjoy a small, well-defined problem - like
rem traversing the directory tree - much as others might like a
rem crossword puzzle. Except this is fun!
rem If you just want to get on and try it:
rem cut out the bit between the two --- cut here --- lines.
rem Save it as a file named say, flp1_DelDir.txt.
rem LOAD 'flp1_deldir.txt' into BASIC.
rem SAVE 'win1_temp_DelDir_bas', for example.
rem Re-LOAD it and check if any lines start with MIStake.
rem Genuine bug or incompatibility reports (and comments)
rem welcomed - and where appropriate, may be dealt with.
rem NB         No other support provided!
rem NB These rem lines may miraclulously disappear!

1000 DEFine PROCedure DELETE_DIRECTORY(dev$, dir$, tree)

rem To remind the user what s/he's about to do, an awkward name
rem has been chosen.
rem The selection of parameters has been made to avoid parsing.
rem                  ----------
rem The dir$ parameter seeds the name variable n$. The reason for
rem for not re-using the dir$ parameter and dispensing with n$
rem altogether is that n$ changes, and if dir$ were used as a
rem variable an unintended change would be passed all the way
rem back to the calling code's variables! In S*BASIC all
rem parameters are passed by Reference ie, the parameter passes
rem an actual copy of the calling variable - a very nice feature.
rem But in this instance it would be safer to pass the variable
rem by Value ie, only the value is passed, completely disociated
rem from the variable. (I'll have a bit more to say on that
rem subject in an upcomming QHJ article (if my contribution is
rem deemed acceptable)).
rem tree is simply a switch to enable the whole sub-directory
rem tree to be scanned. In SBASIC it defaults to off, as all
rem variables are pre-initialled to zero/nul. In SuperBASIC
rem a value must always be supplied.
rem I've chosen a none-recursive tree traversal algorithm here
rem As traversing a tree structure is by nature a recursive
rem operation this entails the use of a stack. This stack only
rem explicitly needs to store the S*BASIC channel numbers; the
rem remaining information required - namely our current file
rem position in each open directory - is "remembered" by the
rem the file system itself.

1010 LOCal c%, t%, pos, ch%(17), n$(36)
1020 n$ = dir$: c% = 0

rem The maximum level of recursion can only be 19 deep (0..18)
rem DIM ch%(18), or rather LOCal ch%(18), sets it up.
rem (Remember we only need to store the S*BASIC channel
rem numbers, and they're integer.)
rem c% is our stack pointer, and for SuperBASIC's sake, we
rem initialise it to 0.
rem t% is just a help variable to store the type of the file
rem we're looking at.
rem pos maintains our current-directory record position,
rem n$(36) contains the current directory or file name.

1030 REPeat dir_loop

rem The outer loop is invoked each time a new directory
rem is to be opened ie, the first time, and then for every
rem recursive step down the sub-tree.

1040  ch%(c%) = FOP_DIR(dev$ & n$)

rem Here the current directory is opened and it's channel
rem number is stacked.
rem (The very first time through the loop c% = 0, and
rem n$ contains the value passed in dir$)

1050  IF ch%(c%) < 0: Action '', ch%(c%)

rem If an error occurs the channel number will be a negative
rem error code, thanks to FOP_DIR.
rem The Action sub-routine is defined below. Taking the action
rem routine out of the main body of the code perhaps makes it
rem easyer to modify for other tasks. The action routine does
rem not return on an error parameter!

1060  pos = -64

rem Initialise pos (somewhere where we can see it!)

1070  REPeat file_loop

rem The inner loop goes through every entry in a directory file
rem once: If the current entry is a new directory file, it
rem temporarily postpones what it's doing and then does the whole
rem sub-tree (and any sub-trees of that again) before returning
rem to continue processing the next entry.

1080   pos = pos + 64

rem Each directory entry, or record, is 64 bytes long, starting
rem at position 0. The structure of the directory header table
rem looks like this (there doesn't appear to be an "official"
rem version, so here's mine (appended)):

1090   IF pos >= FLEN(#ch%(c%)) THEN

rem here we're checking for End Of File

1100    n$ = FNAME$(#ch%(c%)): CLOSE#ch%(c%)

rem All subdirectories and files in Current Directory (CD) have
rem been deleted at this point. We lost the CD's name when we
rem start processing it, but it can be regained from its
rem Channel Definition Block with FNAME$. We're done with
rem this directory, all that's left to do now is to close it and

1110    IF tree: Action 'Deleting', 1

rem delete it (if appropriate).
rem Here the tree switch is checked to see if all subdirectories
rem have been deleted (you can only DELETE an empty directory).

1120    c% = c% - 1: IF c% < 0: EXIT dir_loop

rem Pop the stack ie, proceed to a higher level. If the stack
rem "underflows" (c% < 0) then it's the end - we're done!

1130    pos = INT(FPOS(#ch%(c%)) / 64) * 64

rem At line 1130 we don't know exactly where the file pointer is!
rem Getting it is no problem: pos = FPOS(ch%(c%)). But it has to
rem be aligned to the record boundry, so amputate any bits
rem sticking out. (INT is word sized in SuperBASIC, so this could
rem error on a hard disk! (if youre lucky enough to have one..))

1140   ELSE

rem This is the second main program block. The first block dealt
rem with rear-guard action; closing doors, turning off lights,
rem picking up new trails.. This part does most of the work. It
rem gets information from the file header, sorts directories from
rem files, and makes other decisions based on parameters and
rem data.

1150    GET#ch%(c%)\ pos + 14; n$

rem GET (and BGET, both inluded in tk2 and similar toolkits) has
rem the following syntax:
rem     GET #channel [\ position], items
rem The name of the file (or directory) is located at record
rem base (ie, pos) + 14 (see end of listing). It's a string in
rem the internal format, len.w +

1160    IF LEN(n$) > 0 THEN

rem If the filename length is 0, the file has been deleted,
rem although its slot in the directory still remains. This
rem may be re-used when a new file is added to the directory.

1170     BGET#ch%(c%)\ pos + 5; t%
1180     IF t% = 255 THEN

rem File type 255 indicates a directory. A minority of drivers
rem may use a different type number for this! (eg Thor?)

1190      IF tree THEN
1200       Action 'Checking', 2

rem Only go down this rabbit hole if the tree flag is set.

1210       c% = c% + 1: EXIT file_loop
1220      END IF
1230     ELSE
1240      Action 'Deleting', 0

rem Here we have a file or a (now) empty directory, so do the
rem business!

1250     END IF
1260    END IF
1270   END IF
1280  END REPeat file_loop
1290 END REPeat dir_loop
1300 END DEFine
1310 :

rem The following routine is the sharp end of the enterprise.
rem It could be doing other stuff than deleting files; for
rem example just displaying information about them.

2000 DEFine PROCedure Action(tx$, type)
2010 IF CODE(INKEY$(#0; 0)) = 27: CLOSE: ERT -1
2020 IF type THEN
2030  IF type < 0: CLOSE: ERT type

rem We've made this routine respond to different codes. Here
rem is the response to any negative code: an error. TK2's
rem CLOSE routine, without a channel number, closes ALL open
rem channels > #2. ERT throws the error code given and halts
rem the program.

2040  PRINT \ tx$! dev$; n$! '->'
2050  IF type = 1: DELETE dev$ & n$

rem Code (ie type) 1 does the business for directories..

2060 ELSE
2070  PRINT tx$! n$
2080  DELETE dev$ & n$

rem ..while code 0 does the same for simple files.

2090 END IF
2100 END DEFine
2110 :

rem               File Header Definition

rem mnemonic  pos  size   description
rem fht_len     0  long   file length
rem fht_acces   4  byte   file access key
rem fht_type    5  byte   file type:
rem fht.bod          0    basic program or ordinary data files
rem fht.exe          1    executable program
rem fht.rel          2    relocatable object files
rem          3    Thor directory files (ArgOS)
rem                       Others ?
rem fht.dir        255    directory files (standard)
rem fht_finf    6         8 bytes of file type-dependent information
rem fht_data    6  long   default data space for executable files
rem fht_name   14  qstr   name string
rem fht.nmln        36    max file name length
rem fht_updt   52  long   date of last update
rem fht_vers   56  word   version number
rem fht_filn   58  word   file number
rem fht_bkdt   60  long   date of last backup

rem -------------------------- cut here ---------------------------|

Generated with sb2htm on 2019 Mar 31
©pjwitte March 2oi9