Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson and Brosco
Translation to HTML: Raymond Roumeas

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

Also see - Using Random files - Part 1

Also see - Using Random files - Part 3


Newsletter written by: Brosco. Comments, requests or corrections to: brosco@orac.net.au Translated from Australian to English by an American: Alyce Watson. Thanks Alyce.