---------------------------------------------------------
Brosco's Liberty Basic Newsletter - Issue #10 - June 98

In this issue:
       1) Using Random files - Part 2

Last issue we developed a program to maintain our collection of
movie cassettes - but we still had a couple of problems with the 
solution:

1) The KEY was also used as a record number.  This gave us little
flexibility in what we could use as a Key for the database.

2) We had to know where the 'deleted' records were to be able to
re-use the space.

3) Direct access was only based on Record number.

We will address the first two problems in this issue.  The 3rd
problem will be in Issue #11.

OK - on to the solution!

First, since the KEY is no longer the same value as the record
number, we need to include this as a field in the database. 

    open "video3.txt" for random as #f len=55
    field #f, _
        1 as active$, _
	6 as casNum$, _          ' The KEY to the DB
        24 as name$, _
        24 as mainActor$

We will also reserve record #1 of the database to hold some
'control' information - it will NOT contain info about our
movies!  It will be used to keep track of our 'deleted'
records so that we can reclaim the space.  Now I know that
this sounds impossible - but even if you have thousands of
'deleted' records - we will be able to keep track of all of
them with just one 55 byte record and re-use them when we
add new records!!!!!  How do we do this?

OK - after we OPEN our database - if its the first time -
we create a control record at Position #1:

    if lof(#f)/55 = 0 then       ' Empty DB - create CONTROL Rec
        casNum$ = "0"
        put #f, 1
        end if

In the control record we only use the 'casNum$' field.  This
will be used to "point" to the last record deleted.  If no 
'deleted' records exist - it will have a value of "0".

When we delete a record - we update the Control rec:

[delete.movie]
    if cNum < 2 or cNum > lof(#f) / 55 then     ' Check if valid to delete this
        notice "Delete Error!" + chr$(13) + _
            "No record to delete!"
        goto [loop]
        end if
    get #f, 1           ' Get the Control record
    put #f, cNum        ' Write it to the Deleted space
    casNum$ = str$(cNum)
    put #f, 1           ' Write a new control record
    active$ = ""
    name$ = ""
    mainActor$ = ""
    gosub [display.movie]
    print #w.status, "Movie: " + casNum$ + " has been deleted."
    goto [loop]

First we check that this record is 'allowed' to be deleted.

Now this is where you have to concentrate - and if its the first 
time you have done something like this - you will probably have
to read it a few times - and if the eyes start to glaze over, dont
worry - you will have plenty of company!

We get the ControlRec (remember, casNum$ in this record points to 
the previous deleted record - or "0" if none) and we write it to the 
position of the record we are deleting.  We then write a new 
ControlRec with casNum$ now pointing to the deleted record.


Now for the second part of the equation - this is how we re-use the
space when we add new records:


[add.movie]
    gosub [GetEmptyRec]     ' Find somewhere to write the new record.
    print #w.cn, "!contents?"
    input #w.cn, casNum$

    gosub [write.record]
    print #w.status, "Movie: " + casNum$ + " has been added."
    goto [loop]

[GetEmptyRec]               ' Returns cNum set to position for new rec
    get #f, 1               ' Get the Control rec
    cNum = val(casNum$)
    if cNum = 0 then     ' No deleted space available - get from end
        cNum = lof(#f) / 55 + 1
        return
        end if
    get #f, cNum        ' Get the deleted rec
    put #f, 1           ' write the Deleted rec as the new Control
    return


The 'magic' is all in [GetEmptyRec].  It Gets the ControlRec and
checks if there is any deleted records.  If not - it just sets
the output position to the End of File position - that's the 
place we normally add new records.

But - if there is deleted space available - we set the output
position to the record pointed to by the ControlRec.  Then we
get that record (remember casNum$ in that record points to the
'previous' deleted record) and we write that as our new ControlRec.

If you are having a problem understanding this - get a piece of 
paper and create a list of added records.  Dont forget to put the 
ControlRec in position 1.  Then go through an exercise of deleting 
and adding records - VERY carefully updating the value of casNum$ -
you will soon get the hang of it.

Here's the full sample program:

' Sample program to update a database
' plus automatically re-use deleted space.
' Brosco - June 98 (Newsletter #10)
'
    open "video3.txt" for random as #f len=55
    field #f, _
        1 as active$, _
        6 as casNum$, _          ' The KEY to the DB
        24 as name$, _
        24 as mainActor$

    if lof(#f)/55 = 0 then       ' Empty DB - create CONTROL Rec
        casNum$ = "0"
        mainActor$ = "(Control Record)"
        put #f, 1
        end if

    cNum = 1                    ' initialise for browsing

'    nomainwin

    WindowWidth = 320
    WindowHeight = 230

    statictext #w.1, "Cassette Number:", 10, 40, 130, 20
    statictext #w.2, "Movie Name:", 10, 70, 130, 20
    statictext #w.3, "Main Star:", 10, 100, 130, 20
    statictext #w.4, "Status:", 10, 175, 65, 20
    statictext #w.status, "", 80, 175, 240, 20

    textbox #w.cn, 150, 40, 50, 25
    textbox #w.movie, 150, 70, 150, 25
    textbox #w.star, 150, 100, 150, 25

    button #w.add, "Add", [add.movie], UL, 70, 140, 50, 25
    button #w.upd, "Update", [update.movie], UL, 130, 140, 50, 25
    button #w.del, "Delete", [delete.movie], UL, 190, 140, 50, 25
    button #w.exit, "Quit", [close.w], UL, 250, 140, 50, 25

    button #w.prev, "<--Prev", [previous], UL, 70, 5, 60, 25
    button #w.next, "Next -->", [next], UL, 160, 5, 60, 25

    button #w.default, "Get", [get.movie], UL, 10, 140, 50, 25

    open "Movie Database" for dialog as #w
    print #w, "trapclose [close.w]"
    print #w.cn, "!setfocus"

    print "DB at start of program"
    gosub [print.db]


[loop]
    print #w.cn, "!setfocus"
    input var$
    goto [loop]

[previous]
    cNum = cNum - 1
    if cNum < 2 then    ' Note - Record #1 is Control info
        cNum = 2
        print #w.status, "You are at the start of the database"
        goto [loop]
        end if
    get #f, cNum
    if active$ <> "1" then [previous]
    gosub [display.movie]
    goto [loop]

[next]
    cNum = cNum + 1
    if cNum > lof(#f) / 55 then
        cNum = lof(#f) / 55
        print #w.status, "You are at the end of the database"
        goto [loop]
        end if
    get #f, cNum
    if active$ <> "1" then [next]
    gosub [display.movie]
    goto [loop]

[display.movie]
    print #w.cn, casNum$
    print #w.movie, name$
    print #w.star, mainActor$ 
    print #w.status, ""
    print #w.cn, "!setfocus"
    return

[print.db]
    print " R# Active casNum Name  "
    for i = 1 to lof(#f) / 55
        get #f,i
        rn$ = right$("000" + str$(i),3) + " "
        print rn$;active$;"      ";casNum$;" ";name$;mainActor$
        next i
    print
    return

[get.movie]
    print #w.cn, "!contents?"
    input #w.cn, cNum
    if cNum < 2 then
        notice "Get error!" + chr$(13) + _
            "Cassette number must be greater than 1"
        goto [loop]
        end if
    if cNum > lof(#f) / 55 then    'check if the record exists
[get.error]
        notice "Get Error!" + chr$(13) + _
            "This record doesn't exist"
        goto [loop]
        end if
    get #f, cNum
    if active$ <> "1" then [get.error]
    gosub [display.movie]
    goto [loop]

[add.movie]
    gosub [GetEmptyRec]     ' Find somewhere to write the new record.
    print #w.cn, "!contents?"
    input #w.cn, casNum$

    gosub [write.record]
    print #w.status, "Movie: " + casNum$ + " has been added."
    print "DB After Add new Record:"
    gosub [print.db]
    goto [loop]

[GetEmptyRec]
    get #f, 1               ' Get the Control rec
    cNum = val(casNum$)
    if cNum = 0 then     ' No deleted space available
        cNum = lof(#f) / 55 + 1
        return
        end if
    get #f, cNum        ' Get the deleted rec
    mainActor$ = "(Control Record)"
    put #f, 1           ' write the Deleted rec as the new Control
    return

[update.movie]
    if cNum < 2 or cNum > lof(#f) / 55 then
        notice "Update Error!" + chr$(13) + _
            "No record to Update!"
        goto [loop]
        end if
    print #w.cn, "!contents?"
    input #w.cn, casNum$
    gosub [write.record]
    print #w.status, "Movie: " + casNum$ + " has been updated."
    goto [loop]

[delete.movie]
    if cNum < 2 or cNum > lof(#f) / 55 then
        notice "Delete Error!" + chr$(13) + _
            "No record to delete!"
        goto [loop]
        end if
    get #f, 1           ' Get the Control record
    mainActor$ = "(deleted)"
    put #f, cNum        ' Write it to the Deleted space
    casNum$ = str$(cNum)
    mainActor$ = "(Control Record)"
    put #f, 1           ' Write a new control record
    active$ = ""
    name$ = ""
    mainActor$ = ""
    print #w.cn, "!contents?"
    input #w.cn , casNum$
    gosub [display.movie]
    print #w.status, "Movie: " + casNum$ + " has been deleted."
    print "DB After DELETE Record:"
    gosub [print.db]
    goto [loop]

[write.record]
    print #w.movie, "!contents?"
    input #w.movie, name$
    print #w.star, "!contents?"
    input #w.star, mainActor$
    active$ = "1"               ' show the entry is valid
    put #f, cNum
    return

[close.w]
    close #w
    close #f
    end



 Newsletter written by: Brosco.
 Comments, requests or corrections mailto:brosco@orac.net.au

 Translated from Australian to English by an American:
 Alyce Watson -  Chief Editor.  Thanks Alyce.
