1 rem --------------------- cut here ---------------------|
1 CLS:DELETE_DIRECTORY 'ram1_','temp_', 1
rem NB! WARNING! ACHTUNG! VARSKU I SKAUEN! VARNING! LET OP! VIVA!;>
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!
10 REMark DELETE_DIRECTORY
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
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 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..))
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
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 + chars.bb
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
1240 Action 'Deleting', 0
rem Here we have a file or a (now) empty directory, so do the
1250 END IF
1260 END IF
1270 END IF
1280 END REPeat file_loop
1290 END REPeat dir_loop
1300 END DEFine
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..
2070 PRINT tx$! n$
2080 DELETE dev$ & n$
rem ..while code 0 does the same for simple files.
2090 END IF
2100 END DEFine
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 fht.thd 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