Main
Project
Links

Documentation for Iain's Internet Learning System.

Contents:

  1. Introduction.
  2. User specs.
  3. Instructions for Users (to learn).
  4. Instructions for Administrators (to input/edit learning materials).
  5. Appendix A (File Structure).
  6. Appendix B (Source Code).
  7. Appendix C (Explanation of source code).
  8. Appendix D (Data-flow analysis, and system diagram).
  9. Appendix E (System testing).

Introduction:

This program was written for the course work element of my Computer Science A-Level, so this program is Freeware: You may copy it, modify it, or whatever. But I'm not liable if it formats your favourite disk: it's provided AS-IS. The program is written is Archimedes Basic, and is nearly 600 lines long. The program was written with Zap, a good Freeware text editor. The program was designed because the library stall approached me with the problem that many people are unaware of how the Internet functions. Most people seem to regard the Internet as more of an information service, rather than what it is: a global network. The Internet is amass with differing protocols, services, and equipment. This package is an attempt at allowing individuals to learn how the Internet works.

Proposal/ Other methods.

To use a computer to present the facts about the Internet, in such a way as to allow users to research and learn about it. However, this is not the only route, for instance, books could be a better way, since, while they hold less information that is possible on computer, and blatantly lacks more advanced navigation techniques, such as searching, books have an advantage. They are comfortable to read, more comfortable than most non-paper-white monitors, which flicker enough to cause migraines and headaches. Books are also much cheaper, and more portable than computers, even laptops. Books aren't the only alternative either, people can learn about the Internet using magazines, and perhaps talking to other people, however, I believe these techniques to be less effective. In general computers are generally getting cheaper, so a typical computer necessary to run a program like Iain's Internet Learning System, would probably only cost £100-£200 pounds, since a lowly 286, or A3000 would suffice.

User Specifications.

For this to be a success, the user must be comfortable with the program. The best way of making a user happy with the program, is to involve the user in the development of the program. To do this, I asked potential users of the program, what they want to see in it. Most users said that they needed an easy-to-use interface for accessing the data, which is why a help-line appears on the data viewing screens. Users also wanted similar levels of help for creating the help file, which is why I placed the hint-line on most data-editing screens, and also provided a help-screen, listing the editing and navigation controls. Users also wanted the ability to search for data, perhaps a keyword, within the database. The search page is accessed by pressing the [S] key. Finally, users expressed a wish that the program would run on a wide range of computers and operation systems. Since, there are BASIC interpreters/compilers for just about every computing platform around, I chose to create this application in BASIC, making the transition to another platform much easier to do.

Users also felt that images, and diagrams would be helpful. Due to a limited time-scale, it was not plausible to implement a graphics engine into the program. Users also felt that other multi-media features would be useful, like verbal explanations of concepts, and animations, visibly showing, say, the path of a datagram between two computers, leaping across routers and hubs. Again, due to limited time-scale, the inclusion of such multi-media abilities did not seem sensible. Some users felt that the use of a mouse as a navigation tool was appropriate, however, due to the fact that I wanted the application to run as a wimp task, for debugging and development ease, mouse control was made ever so much harder, requiring a slightly different approach to the structure of the program. This would have made the program platform-specific, that is, tied to the Acorn range of computers. Since another requirement of the users is platform independence, the use of Acorn-specific Soft Ware Interface calls is not acceptable.

Instructions for Users (learning).

First, the system must be installed, and an initial database created. Instruction on how to do this can be found in the next section, Instructions for Administrators. The administrator should have created the initial database, and set it as the default.

Next the application needs to be running, so you must double-click the !!DB icon.

After double-clicking this icon, the main application starts up, presenting a simple help screen.

As the screen suggests, pressing the [ENTER] key causes the program to proceed into the main section of the program, which allows the viewing and searching of the data.
The data viewing screen looks like this:

As the hint line suggests, browsing through the data file can be done by pressing [Q] to browse to the next page, and [A] to browse to the previous page. Pressing [F] takes you to the search page, where specific words can be searched for, in any of the available pages.

When you have picked a field to search in, or pressed [F] for the all field option, which is recommended for the greatest chance of finding the required information, the program will display a new prompt. 

As this prompt directs, not you enter the word you are looking for, however, this is a simple search, as such it does not recognise any Boolean search strings. For instance, searching for "html OR page", will not find anything, unless the database contains the string "html or page", as such the better searches are those that consist of just one word, like "html".
Once a search has been made, as the new hint-line suggests, pressing [C] will re-start the search, from the current page, looking for the same word. Browsing to another page, or pressing [F], to start another search will result in this search-again feature being cleared. The final key you may press is [X] for exit. This will, not surprisingly return you to whatever you were running before you started the program, however, the RISC-OS incarnation requires that you close the Task Window, by clicking the [X] in the top-left corner.

Instructions for Administrators (creating data file).

The instructions here basically fall into two categories. One, Creating the database, and two, Editing a database. Since the two operations are quite different, I will cover them separately.

Creating a datafile.

To create the datafile, simply run the application for the first time, the program then prompts you for the information it needs in order to create your datafile. If you want to create another datafile, in addition to any existing ones, you press [F] at the title screen to change the current file name, and then enter a file name that does not exist yet, and it will be created. Part of the process of creating the datafile, is defining it's dimensions. The program is capable of supporting data files with up to ten lines per screen, with up to fifty characters on each line. The program will support up to two hundred pages in a single file.

When creating the data file, the program will ask for the dimensions, except number of pages.

In this screen, the program is requesting the number of lines per page. This value, once set, cannot be changed by editing the file. This screen also shows the process of creating a new file, by specifying a file name that does not exist yet.

This screen shot shows the process of creating a file, defining the lengths of lines in the file, and the number of lines per page. This screen shot was taken just after the one above, showing the steps involved in creating the file.

After the dimensions of the file are input, the program creates the initial, empty, file. This initial file contains only one record, record 0. This record should be used to describe the future contents of the fields, as this record it that that is both displayed on startup, and as the template for the search-screen, for the users, and also as the sort-screen template.

Editing the data file.

Basic editing.

To edit the data file, you must select Editing mode in one of two ways. Either you press [E] at the title screen, or if you create a file, you are automatically placed in editing mode. The first screen you are shown is a brief help screen.

Initially, this mode looks similar to the normal viewing mode. However, there are extra keys that are available, for example, [E] to start editing a record. When starting a new file, there in only one record, or page. The suggested first step here is to edit page 0, describing the contents of each field. The next step will be to add another page. To add a page to the end of the file, press [U]. Then, navigate to this new, blank, page by pressing [A], and fill the field as needed. You can keep sticking another record to the end of the file, and editing it as needed. However, you must remember, there is a maximum limit of 200 pages. Remembering that page 0 also counts, the highest page number of 199.

Advanced editing.

There are two extra, somewhat more advanced procedures that can be done, these are sort, and insert. Inserting a record is a different way of adding a page to the file. To insert a record, navigate to the record which you want to displace, and press [I]. The current page, and all the pages that follow it will be moved one step (displaced) towards the end of the file. The other advanced editing procedure is sorting. The sorting procedure will sort the records in the file, in ascendingorder of one field. Note that the sort algorithm is a bubble sort type, therefore, sorting data files larger than about 20 pages may take a few moments of a fast machine, to a few minutes on a slower machine.

Appendix A (File structure).

The main data file is stored as follows: Initially, the overall file dimensions are written to the file, in Sequential mode. One integer for the number of fields per record, and another for the number of records in the file.

Then, an array describing, for each field in a record, the size of the field. Thus, we now have a complete description of how the header of the file is laid out.

In sumary: [Number of fields],[Number of records],[Length of field 1],[Length of field 2]...[End of header]

Directly after the header, the data is stored, this time in direct-access mode, in the format of [Record 1, field 1], [Record 1, field 2]...[Record 2, field 1],[Record 2, field 2] etc. The direct-access nature of this section of the file means that any field of any record can be directly accessed, both read from and written to, which can improve the speed at which a total data-write can take, since only those records that need updating get written to, meaning less data which must be written to disk, however, in this program, this feature has not been implemented, since it provides little gain for the amount of work required to achieve it.

Appendix B(Source code).

10 : REM Some initial work, close filehandles, set current dir, handle errors (as if there are any!), and tell if to default to DEBUG mode (extra data is printed out, which is vital for internal operation of program, usually put in to find errors...)!

20 : ON ERROR OSCLI "close":REPORT:PRINT " on line ";ERL:END

30 : Changefilename=FALSE

40 : Searched_Record=FALSE

50 : REM DEBUG = TRUE

60 : DEBUG=FALSE

70 : REM Bad programming practice to do this... OSCLI "close"

80 : OSCLI "dir <idb$dir>.data"

90 : REM An Internet Jargon-Buster. By Iain Thomas. Computing Coursework. Very Important!

100 : CLS

110 :

120 : REM We don't define a main() procedure, not in basic anyway.

130 : REM Start of Program

140 : REM Draw a screen. To say Hello, and wait for a keypress.

150 : PROC_Screen(1)

160 : PROC_Screen(2)

170 :

180 : REM Load the data to data$()

190 : PROC_Initialise

200 :

210 : IF DEBUG THEN CLS

220 : REM Just to tidy up the DEBUGGING sessions!!

230 : REM This is for when the user has selected Edit Mode.

240 : CASE EMODE OF

250 : WHEN TRUE: PROC_Screen(3) : PROC_Edit : PROC_Screen(2)

260 :

270 : WHEN FALSE: PROC_Screen(4) : PROC_View

280 : ENDCASE

290 : PROC_CloseFile

300 : CLS

310 :

320 : PRINT "End of Program"

330 : REM

340 :

350 : REM THE PROGRAM ENDS HERE, EXCEPT FOR PROCs AND FNs

360 :

370 :

380 : REM All below THIS line, PROCs and FNs OnLy!!!

390 : END

400 : REM PROC_Initialise To initialise the arrays, and load the data.

410 : DEFPROC_Initialise

420 : Curpage=0

430 : Array_Set_Up=FALSE

440 : REM open da file.

450 : PROC_File_open

460 : REM Load da data.

470 : PROC_File_read

480 : REM That *ShOuLd* be all.

490 : ENDPROC

500 :

510 : REM PROC_Screen : To draw screens, menus etc. Screens listed below.

520 : REM Screen 1 is a welcome screen.

530 : REM Screen 2 clears the screen, and invites the user to wait.

540 : REM Screen 3 informs the user if they pressed E for Edit Mode. Will also provide instructions of how to use the edit mode.

550 : REM Screen 4 is to Display the first page of data, and provide for scrolling between records.

560 : REM Screen 5 is to Display the page of data for editing.

570 : REM Screen 6 is to intro the edit system.

580 : DEFPROC_Screen(screen)

590 : CASE screen OF

600 :

610 : WHEN 1

620 : CLS

630 : PRINT "Welcome to my Internet Learning System!"

640 : PRINT "By Iain Thomas (A-Level Computer science project)"

650 : PRINTTAB(0,10);"Press [ENTER] key to continue..."

660 : PRINTTAB(0,11);"Press [E] key to enter edit mode!"

670 : PRINTTAB(0,12);"Press [F] key to toggle file name prompting (change active file)."

680 : REM Provide a way of changing the active filename: mostly for testing purposes...

690 : REPEAT

700 : A=GET

710 : CASE A OF

720 : WHEN 70,102

730 : Changefilename=NOT Changefilename

740 : WHEN 68,100

750 : DEBUG=NOT DEBUG

760 : WHEN ASC("X"),ASC("x")

770 : END

780 : OTHERWISE

790 : ENDCASE

800 : IF Changefilename THEN PRINTTAB(5,6);"File name WILL be asked for." ELSE PRINTTAB(5,6);SPC(28)

810 : IF DEBUG THEN PRINTTAB(5,5);"Running in DEBUG mode (Press [D] to cancel)!" ELSE PRINTTAB(5,5);SPC(44)

820 : UNTIL A<>70 AND A<>102 AND A<>100 AND A<>68

830 : CASE A OF

840 : WHEN69,101: EMODE=TRUE

850 : OTHERWISE:

860 : EMODE=FALSE

870 : ENDCASE

880 : WHEN 2

890 : CLS

900 : PRINTTAB(5,1);"Please Wait: File operations in progress"

910 : WHEN 3

920 : CLS

930 : PRINT "Editing Mode; Press a key!"

940 : PRINT"Press [U] to append a record to the end of the file."

950 : PRINT"Use [Q] and [A] to page through the data."

960 : PRINT"Use [F] to Search for data, and [C] to find again."

970 : PRINT"Use [I] to insert a record, and move current record (and all after) towards end of file."

980 : PRINT"Use [E] to switch to field editor, and use [Q] and [A] to select field."

990 : PRINT"Once selected, press [E] to edit the field."

1000 : PRINT"Then [Enter] to return to record-display."

1010 : PRINT"Use [S] to sort the data."

1020 : PRINT"Then [X] to save the datafile, and exit."

1030 : A=GET

1040 : CLS

1050 : WHEN 4

1060 : PRINTTAB(1,1);"The Internet"

1070 : PRINTTAB(2,2);"Page: ";Curpage;" of ";Numpages;". " :REM Spaces to get rid of unwelcome full-stops.....

1080 : PRINTTAB(2,15);"Use [Q] and [A] to navigate, [F] to find data, or [X] to exit. "

1090 : WHEN 5

1100 : PRINTTAB(40,1);"Selected field is ";eField

1110 : PRINTTAB(0,2);"Data is ";data$(Selected_Record,eField);STRING$(80," ")

1120 : PROC_DataScreen(Selected_Record)

1130 : PROC_EScreenMarkeField(eField)

1140 : WHEN 6

1150 : PRINTTAB(0,0);"Editing record ";Selected_Record;SPC(1)

1160 : PRINTTAB(0,1);"Select Field with [Q] and [A] Edit with [E]!"

1170 : WHEN 7

1180 : PRINTTAB(0,0);"Sort Records by field Press [Enter] to sort..."

1190 : WHEN 8

1200 : PRINTTAB(0,0);"Search Records; Please select the field to search in, and press [Enter]."

1210 : PRINTTAB(0,1);"Press [F] to search all fields."

1220 : WHEN 9

1230 : PRINTTAB(0,0);"Please enter the text you are looking for."

1240 : WHEN 10

1250 : CLS

1260 : PRINTTAB(0,17);"Your text was found in record ";Curpage

1270 : REM PRINTTAB(0,1);"Here is the record. Please press a key to return to Viewing mode."

1280 : PROC_DataScreen(Curpage)

1290 : REM A=GET : CLS

1300 : PROC_Screen(4)

1310 : WHEN 11

1320 : CLS

1330 : PRINTTAB(0,17);"Your text was not found. Sorry about that!"

1340 : PRINTTAB(0,18);"Please press a key!":SOUND 1,-10,200,2

1350 : A=GET : CLS

1360 : PROC_Screen(4)

1370 : WHEN 12

1380 : CLS

1390 : PRINTTAB(0,17);"Sorry, there is no more room for another record!"

1400 : PRINTTAB(0,18);"Please press a key!":SOUND 1,-10,200,2

1410 : A=GET : CLS

1420 : PROC_Screen(4)

1440 : ENDCASE

1450 :

1460 : ENDPROC

1470 :

1480 : DEFPROC_ClearStatusLine

1490 : PRINTTAB(0,17);STRING$(80," ")

1500 : ENDPROC

1510 :

1520 : REM PROC_View: To be used to run the actual viewing of the data.

1530 : DEFPROC_View

1540 : REPEAT

1550 : PRINTTAB(2,2);"Page: ";Curpage;" of ";Numpages;". " :REM Spaces to get rid of unwelcome full-stops.....

1560 : PROC_DataScreen(Curpage)

1570 : A=GET

1580 : CASE A OF

1590 : WHENASC("Q"),ASC("q")

1600 : IF Curpage+1<=Numpages THEN Curpage=Curpage+1

1610 : Searched_Record=FALSE

1620 : WHENASC("A"),ASC("a")

1630 : IF Curpage-1>=0 THEN Curpage=Curpage-1

1640 : Searched_Record=FALSE

1650 : WHENASC("F"),ASC("f")

1660 : PROC_FindInterface(0)

1670 : WHENASC("C"),ASC("c")

1680 : IF Searched_Record=TRUE THEN PROC_FindInterface(1)

1690 : ENDCASE

1700 : PROC_ClearStatusLine

1710 : CASE Searched_Record OF

1720 : WHEN FALSE

1730 : PRINTTAB(0,15);"Use [Q] and [A] to navigate, [F] to find data, or [X] to exit. "

1740 : WHEN TRUE

1750 : PRINTTAB(0,15);"Use [Q] and [A] to navigate, [C] to Find Again, or [X] to exit."

1760 : ENDCASE

1770 :

1780 : UNTIL A=88 OR A=120

1790 :

1800 : ENDPROC

1810 :

1820 : REM PROC_FindInterface(mode): Provides a user interface for the search function, allowing the user to look for data in any field... Mode 0 is for new searches, Mode 1 is to resume a search.

1830 :

1840 : DEFPROC_FindInterface(mode)

1850 : CLS

1860 : IF mode=0 THEN

1870 : PROC_Screen(8)

1880 : PROC_DataScreen(0)

1890 : eField=0

1900 : Selected_Record=0

1910 : REPEAT

1920 : PROC_EScreenMarkeField(eField)

1930 : A=GET

1940 : CASE A OF

1950 : WHENASC("G"),ASC("g")

1960 : searchfield=eField

1970 : e=TRUE

1980 : WHENASC("A"),ASC("a")

1990 : IF eField<fields-1 THEN eField=eField+1

2000 : e=FALSE

2010 : WHENASC("Q"),ASC("q")

2020 : IF eField>0 THEN eField=eField-1

2030 : e=FALSE

2040 : WHEN13

2050 : searchfield=eField

2060 : e=TRUE

2070 : WHENASC("F"),ASC("f")

2080 : all_fields=TRUE

2090 : searchfield=0

2100 : e=TRUE

2110 : OTHERWISE

2120 : e=FALSE

2130 : ENDCASE

2140 : UNTIL e=TRUE

2150 :

2160 : CLS

2170 : PROC_Screen(9)

2180 : INPUT searchtext$

2190 :

2200 : REM Mode <> 0 (=1), so we're doing a continue-search...

2210 : ENDIF

2220 : Searched_Record=TRUE

2230 : foundpage=FN_Find(searchtext$,searchfield,Selected_Record)

2240 : IF foundpage<>-1 THEN Curpage=foundpage:Selected_Record=Curpage+1:PROC_Screen(10) ELSE PROC_Screen(11)

2250 : ENDPROC

2260 :

2270 : REM PROC_Edit shows the user the records: press [ENTER] to edit record(page).

2280 : REM ALSO save the data on ending...

2290 : DEFPROC_Edit

2300 : PROC_Screen(4)

2310 : REPEAT

2320 : PRINTTAB(2,2);"Edit Page: ";Curpage;" of ";Numpages;". " :REM Spaces to get rid of unwelcome full-stops.....

2330 : PROC_DataScreen(Curpage)

2340 : A=GET

2350 : CASE A OF

2360 : WHENASC("Q"),ASC("q")

2370 : IF Curpage+1<=Numpages THEN Curpage=Curpage+1

2380 : WHENASC("A"),ASC("a")

2390 : IF Curpage-1>=0 THEN Curpage=Curpage-1

2400 : WHENASC("E"),ASC("e")

2410 : Selected=Curpage

2420 : PROC_EditRec(Selected)

2430 : PROC_Screen(4)

2440 : WHENASC("I"),ASC("i")

2450 : PROC_Insert_Record(Curpage)

2460 : WHENASC("U"),ASC("u")

2470 : PROCAdd_Record

2480 : WHENASC("S"),ASC("s")

2490 : PROC_SortInterface

2500 : OTHERWISE

2510 :

2520 : ENDCASE

2530 : PROC_ClearStatusLine

2540 :

2550 : UNTIL A=88 OR A=120

2560 : PROC_Data_Save

2570 : ENDPROC

2580 :

2590 : REMPROC_EditRec(Selected_Record): Provide an interface for choosing a field to edit. This will provide the text-input routine. Selected Record must be the record chosen for editing. There is no way to change the edited field from this routine.

2600 : DEFPROC_EditRec(Selected_Record)

2610 : CLS

2620 : PROC_Screen(6)

2630 : eField=0

2640 : REPEAT

2650 : PROC_Screen(5)

2660 : A=GET

2670 : CASE A OF

2680 : WHENASC("A"),ASC("a")

2690 : IF eField<fields-1 THEN eField=eField+1

2700 : WHENASC("Q"),ASC("q")

2710 : IF eField>0 THEN eField=eField-1

2720 : WHENASC("E"),ASC("e")

2730 : CLS

2740 : PRINTTAB(25,2);STRING$(fieldlengths(eField),"-")

2750 : INPUT TAB(1,1);"New field data, please?",in$

2760 : IF LEN(in$)>fieldlengths(eField) THEN

2770 : PRINT "Please stick to ";fieldlengths(eField);" characters! Press a key." : A=GET

2780 : ELSE

2790 : data$(Selected_Record,eField)=in$

2800 : ENDIF

2810 : CLS : PROC_Screen(6):PROC_Screen(5)

2820 : ENDCASE

2830 : UNTIL A=13

2840 : CLS

2850 : ENDPROC

2860 :

2870 : REMPROC_SortInterface: Prompts the user to select field to sort by, then uses the bubblesort alogarithm to sort it.

2880 : DEFPROC_SortInterface

2890 : CLS

2900 : PROC_Screen(7)

2910 : eField=0

2920 : Selected_Record=0

2930 : REPEAT

2940 : PROC_Screen(5)

2950 : A=GET

2960 : CASE A OF

2970 : WHENASC("A"),ASC("a")

2980 : IF eField<fields-1 THEN eField=eField+1

2990 : WHENASC("Q"),ASC("q")

3000 : IF eField>0 THEN eField=eField-1

3010 : WHEN13

3020 : PROCSortData(eField)

3030 : ENDCASE

3040 : UNTIL A=13

3050 : CLS

3060 : ENDPROC

3070 :

3080 :

3090 : REM PROC_DataScreen(pagenum): To be used for filling in the data in screen 4. Parameter is the record (or page number)

3100 : DEFPROC_DataScreen(pagenum)

3110 : FOR loop_field=0 TO fields-1

3120 :

3130 : PRINTTAB(12,5+loop_field);SPC(fieldlengths(loop_field))

3140 : IF pagenum>=0 THEN PRINTTAB(12,5+loop_field);data$(pagenum,loop_field) ELSE PRINTTAB(12,5+loop_field);"Data"

3150 :

3160 : NEXT loop_field

3170 : ENDPROC

3180 :

3190 : REM PROC_EScreenMarkeField(mrk)

3200 : DEFPROC_EScreenMarkeField(mrk)

3210 : FOR clm=0 TO fields-1

3220 : PRINTTAB(9,5+clm);" "

3230 : NEXT clm

3240 : PRINTTAB(9,5+mrk);"->"

3250 : ENDPROC

3260 :

3270 : REM Proc-Table: For database engine.

3280 : REM PROC_Set_pointer : To point the file-pointer at a particular record and field (working now)

3290 : REM PROC_File_open : To open and initialise the database file. Setting variables

3300 : REM PROC_File_read : To read the data in. (From main% disk file TO data$())

3310 : REM PROC_Data_save : To write the data in data$() into the main% disk file (filename$)

3320 :

3330 : REM Procedure to point the pointer at the start of the record and field (data item) passed.

3340 :

3350 : DEFPROC_Set_pointer(record,field)

3360 :

3370 : REM Start_of_data_offset=40

3380 : REM WE'D BETTER HAVE THE FILE-DIMENSIONS SET UP FOR THIS, OR ALL HELL WILL BREAK LOSE!!!...

3390 : length_of_record=0

3400 : FOR loop=0 TO fields-1

3410 : length_of_record=length_of_record+fieldlengths(loop)+2

3420 : NEXT loop

3430 : IF DEBUG THEN PRINT "Set Rec: ";record;" Set fld: ";field;" RecLen: ";length_of_record

3440 : record_offset=0

3450 : IF DEBUG THEN PRINT "Data starts at: ";Start_of_data_offset

3460 : IF field>0 THEN

3470 : FOR loop=0 TO field-1

3480 : record_offset=record_offset+fieldlengths(loop)+2

3490 : IF DEBUG THEN PRINT "Record offset: ";record_offset

3500 : NEXT loop

3510 : ENDIF

3520 : point=Start_of_data_offset+record_offset+(length_of_record*record)

3530 : IF DEBUG THEN PRINT "Pointer is at: ";point

3540 : PTR#main%=point

3550 : ENDPROC

3560 : REM ----- MARKER End of file opening procedure. "-----"

3570 :

3580 : DEFPROC_CloseFile

3590 : CLOSE #main%

3600 : ENDPROC

3610 :

3620 : DEFPROC_File_open

3630 : REM ----- MARKER start of File-opening procedure. "-----"

3640 : REM This has to be here, and MUST ONLY BE CALLED ONCE, since Archamedies BASIC don't approve of arrays being redimensioned.

3650 : IF NOT Array_Set_Up THEN

3660 : REM 10 is chosen as a sensible maximum number of fields, since otherwise, they would over-write the help text. Max number of pages is 200, since we can't use dymanic arrays...

3670 : DIM data$(200,10)

3680 : DIM fieldlengths(10)

3690 : ENDIF

3700 : Array_Set_Up=TRUE : REM Just in case it is called again.

3710 : REM Do we have a last File Name??

3720 : z%=OPENIN("^.lastfile/d")

3730 : REM ON ERROR Sucess=FALSE

3740 : Sucess=TRUE

3750 : IF DEBUG THEN PRINT "Pre-input Sucess: ";Sucess

3760 : IF DEBUG THEN PRINT "z% (^.lastfile/dat access channel) = ";z%

3770 : REM This next line has caused no end of problems: the newline was in the wrong place, so the interpreter always executed the first set of instructions, instead of the conditional execution that I was expecting.

3780 : IF z%<>0 THEN

3790 : INPUT#z%,filename$

3800 : Sucess=TRUE

3810 : IF DEBUG THEN PRINT "Executing as if z% NOT 0"

3820 : ELSE

3830 : Sucess=FALSE

3840 : filename$=""

3850 : IF DEBUG THEN PRINT "Executing as if z% IS 0"

3860 : ENDIF

3870 : IF DEBUG THEN PRINT "^.lastfile returned filename: ";filename$

3880 : IF DEBUG THEN PRINT "Post-input Sucess: ";Sucess

3890 : REM No ^.lastfile/d present... OR read failed. Using the fact that if a file is opened for input, and it does not exist, the file channel returned is nought(0).

3900 : REM Sucess=FALSE

3910 : IF Changefilename=TRUE THEN Sucess=FALSE

3920 : WHILE Sucess=FALSE

3930 : PRINT "A File Name, please (press [ENTER] to quit) (* to see file list)...???"

3940 : INPUT filename$

3950 : Sucess=TRUE

3960 : REM Get out of the GOTO problem.

3970 : IF filename$="" THEN OSCLI "close" : END

3980 : IF filename$="*" THEN

3990 : CLS:PRINT "Directory Listing"

4000 : OSCLI "."

4010 : Sucess=FALSE

4020 : ENDIF

4030 : ENDWHILE

4040 : CLOSE#z%

4050 : REPEAT

4060 : IF DEBUG THEN OSCLI "."

4070 : REM We should have a file name by now, but we don't know if it exists..

4080 : tempfile%=OPENIN(filename$)

4090 : IF tempfile%=0 THEN

4100 : PRINT "File does not exist, Create and edit it?(Y/N)"

4110 : CASE GET OF

4120 : WHEN ASC("Y"), ASC("y")

4130 : PROC_DefineDatabase

4140 : EMODE=TRUE

4150 : fields=Lines

4160 : records=1

4170 : Numpages=0

4180 :

4190 : main%=OPENOUT(filename$)

4200 : PROC_Data_Save

4210 : CLOSE#main%

4220 :

4230 : REM Deal with a non-existant file: to create and define one.

4240 : OTHERWISE

4250 : filename$=""

4260 : ENDCASE

4270 : ENDIF

4280 : CLOSE#tempfile%

4290 : IF NOT (filename$="") THEN main%=OPENUP(filename$):CLS:PTR#main%=0

4300 : IF DEBUG THEN PRINT "File name is " , filename$ : REM Just a little debugging...

4310 : IF filename$="" THEN Sucess=FALSE: ERROR 0, "Quit: No File Error!!" : REM Because we don't have a file.

4320 : UNTIL Sucess

4330 : z%=OPENOUT("^.lastfile/d")

4340 : PRINT#z%, filename$

4350 : CLOSE#z%

4360 :

4370 : REM ----- MARKER end of File Opening procedure. "-----"

4380 : ENDPROC

4390 :

4400 : DEFPROC_File_read

4410 : REM ----- MARKER Start of File-initialization procedure "-----"

4420 : INPUT#main%,fields

4430 : INPUT#main%,records

4440 : Numpages=records-1

4450 : IF DEBUG THEN PRINT "Fields: ";fields;" Records: ";records

4460 : IF records>0 THEN

4470 : IF DEBUG THEN PRINT "Reading Data Record from file"

4480 : FOR loop=0 TO fields-1

4490 : INPUT#main%,fieldlengths(loop)

4500 : IF DEBUG THEN PRINT "Fieldlength ";loop;" returned: ";fieldlengths(loop)

4510 : NEXT loop

4520 : Start_of_data_offset=PTR#main% :REM This doesn't seem to work... PTR#main% OKOK, it workz

4530 :

4540 : REM Reminder to oneself, we start at record 0, field 0

4550 : FOR loop_record=0 TO records-1

4560 : FOR loop_field=0 TO fields-1

4570 : PROC_Set_pointer(loop_record,loop_field)

4580 : INPUT#main%,data$(loop_record,loop_field)

4590 : IF DEBUG THEN PRINT "Rec: ";loop_record;" Fld: ";loop_field;" = ";data$(loop_record,loop_field)

4600 : NEXT loop_field

4610 : NEXT loop_record

4620 : ENDIF

4630 :

4640 : REM ----- MARKER end of file-initialization procdure. "-----"

4650 :

4660 : ENDPROC

4670 :

4680 : DEFPROC_Data_Save

4690 : REM ----- Start of Data Saving procedure "-----"

4700 : REM Save da DATA, if possible. If not, then we bomb out...

4710 : PRINT "Writing Data Record..." :REM Just to let the user know what is going on...

4720 : PTR#main%=0

4730 : PRINT#main%,fields

4740 : PRINT#main%,records

4750 : FOR loop=0 TO fields-1

4760 : PRINT#main%,fieldlengths(loop)

4770 : NEXT loop

4780 : Start_of_data_offset=PTR#main% :REM This is to prevent hell from breaking loose...

4790 :

4800 : FOR loop_record=0 TO records-1

4810 : FOR loop_field=0 TO fields-1

4820 : PROC_Set_pointer(loop_record,loop_field)

4830 : PRINT#main%, data$(loop_record,loop_field)

4840 : NEXT loop_field

4850 : NEXT loop_record

4860 : REM ----- End of Data Saving procedure "-----"

4870 : ENDPROC

4880 :

4890 : REM PROC_DefineDatabase. This procedure is to be executed when a new database is created. It will provide facilities for choosing the lines/page, and the chars per line, although it may be more efficient to hard-code number of lines, and, characters..

4900 : REM per line...

4910 : DEFPROC_DefineDatabase

4920 : PRINT "Please enter the number of lines you wish to have per screen?"

4930 : REPEAT

4940 : PRINT "Suggest 7, Minimum is 1, Maximum is 10"

4950 : INPUT Lines

4960 : CASE Lines OF

4970 : WHEN Lines<1 : PRINT "Erm, I said MINIMUM is 1!"

4980 : WHEN Lines>10: PRINT "Erm, I said MAXIMUM is 10!"

4990 : ENDCASE

5000 : UNTIL Lines >= 1 AND Lines <= 10

5010 : FOR i=0 TO Lines-1

5020 : REPEAT

5030 : PRINT "Number of chars for line ";i;" (1 to 50) Suggest 40"

5040 : INPUT f

5050 : CASE f OF

5060 : WHEN f<1: PRINT "Erm, I said MINIMUM is 1!"

5070 : WHEN f>50:PRINT "Erm, I said MAXIMUM is 50!"

5080 : ENDCASE

5090 : UNTIL f>=1 AND f<=50

5100 : fieldlengths(i)=f

5110 : NEXT i

5120 : ENDPROC

5130 :

5140 : REM This procedure is to be called by the edit routine, when [I] is pressed, to insert a record in the file, or at the end. Where the record is to be inserted (ie, the new record is created, and the existing one, and ones after it are moved towards

5150 : REM the end of the file.)

5160 : DEFPROC_Insert_Record(InsRec)

5170 : IF records <200 THEN

5180 : FOR loop_rec=records TO InsRec STEP -1

5190 : FOR loop_flds= 0 TO fields

5200 : IF DEBUG THEN PRINT "loop_rec=";loop_rec;" loop_flds=";loop_flds

5210 : data$(loop_rec+1,loop_flds)=data$(loop_rec,loop_flds)

5220 : NEXT loop_flds

5230 : NEXT loop_rec

5240 :

5250 : FOR loop_flds= 0 TO fields

5260 : IF DEBUG THEN PRINT "InsRec=";InsRec

5270 : data$(InsRec,loop_flds)=""

5280 : NEXT loop_flds

5290 :

5300 : records=records+1

5310 : Numpages=Numpages+1

5320 : ELSE

5330 : PROC_Screen(12)

5340 : ENDIF

5350 : ENDPROC

5360 :

5370 : REM Since PROC_Insert_Record doesn't allow for adding a record to the end of the file, I must also create a proc to add a record onto the end...

5380 : REM PROCAdd_Record. Does exactly what it says...

5390 : DEFPROCAdd_Record

5400 : IF records <200 THEN

5410 : records=records+1

5420 : Numpages=Numpages+1

5430 : ELSE

5440 : PROC_Screen(12)

5450 : ENDIF

5460 : ENDPROC

5470 :

5480 : REM PROCSortData(KeyField): sorts the data in data$(,), in ascending order of KeyField.

5490 : REM PROCBubble(item): Used exclusivly by PROCSortData...

5500 :

5510 : DEFPROCSortData(KeyField)

5520 : REPEAT

5530 : REPEA=FALSE

5540 : FOR I = 0 TO records-2

5550 : IF FN_Upper(data$(I,KeyField)) > FN_Upper(data$(I+1,KeyField)) THEN

5560 : IF DEBUG THEN PRINT data$(I,KeyField) ; " is MORE than " ; data$(I+1,KeyField)

5570 : IF DEBUG THEN PRINT "I=";I

5580 : PROCBubble(I)

5590 : REPEA=TRUE

5600 : ENDIF

5610 : NEXT I

5620 : UNTIL REPEA=FALSE

5630 : IF DEBUG THEN A=GET

5640 : ENDPROC

5650 :

5660 : DEFPROCBubble(item)

5670 : FOR M=0 TO fields

5680 : sTEMP$=data$(item,M)

5690 : data$(item,M)=data$(item+1,M)

5700 : data$(item+1,M)=sTEMP$

5710 : NEXT M

5720 : ENDPROC

5730 :

5740 :

5750 : REM FN_Upper$(outstr$): Will take a string, and return the string, in uppercase characters. Will not affect numbers/punctuation etc. Used for sorting/searching data...

5760 :

5770 : DEFFN_Upper(instr$)

5780 :

5790 : outstr$=STRING$(LEN(instr$)," ")

5800 :

5810 : FOR loop=1 TO LEN(instr$)

5820 : IF ASC(MID$(instr$,loop,1)) >= 97 AND ASC(MID$(instr$,loop,1)) <= 122 THEN

5830 : MID$(outstr$,loop,1)=CHR$(ASC(MID$(instr$,loop,1))-(97-65))

5840 : ELSE

5850 : MID$(outstr$,loop,1)=MID$(instr$,loop,1)

5860 : ENDIF

5870 :

5880 : NEXT loop

5890 : =outstr$

5900 :

5910 : REM DEF_FN_Find(findtext$,findfield%,startfrom%): This function will find the specified data in any record, in the specified field, starting from a specified record.

5920 : REM Result will be -1 if not found, or else the record number the data was found in.

5930 :

5940 : DEFFN_Find(findtext$,findfield%,startfrom%)

5950 : IF findfield%=-1 THEN all_fields=1 ELSE all_fields=0

5960 : foundrecord=-1

5970 :

5980 : I=startfrom%

5990 :

6000 : IF all_fields=TRUE THEN

6010 :

6020 : REPEAT

6030 : findfield%=findfield%+1

6040 : IF INSTR(FN_Upper(data$(I,findfield%)),FN_Upper(findtext$)) <>0 THEN foundrecord=I

6050 :

6060 : IF findfield%=fields THEN I=I+1:findfields%=-1

6070 : UNTIL foundrecord <> -1 OR I > records-1

6080 :

6090 : ELSE

6100 :

6110 : REPEAT

6120 : IF INSTR(FN_Upper(data$(I,findfield%)),FN_Upper(findtext$)) <>0 THEN foundrecord=I

6130 : I=I+1

6140 : UNTIL foundrecord <> -1 OR I > records-1

6150 :

6160 : ENDIF

6170 :

6180 : =foundrecord

Appendix C (Explanation of source code).

Since the source code is over 600 lines long, I feel that it is not practical to explain every line. Therefore, I will explain three procedures, which I feel are particularly relevant.

PROC_Initialise (Line 420).

This fairly short procedure sets up a few variables, which are used later. It also calls another procedure to open the data file and read the file geometry, and the second procedure to, using the open file handle, and the file geometry, read the contents of the data file, and place it in an array, for later use.

PROC_EditRec (Line 2600).

This, somewhat longer procedure, is called by the browsing procedure, to edit a record. The call is made in the form of PROC_EditRec(Number of record to edit).The first three lines call two procedures to paint the screen, and one line to reset the currently selected field to 0. Next, a loop to check keypresses and alter the selected field accordingly, and to receive new data, and place it in the array. This sub-procedure also ensures that the length of data input is allowable, requesting that the user try again if it isn't. Finally, the loop waits for the user to hit [ENTER] to return to the browser.

PROC_SetPointer (Line 3350).

This procedure is used by the data-saving and data-loading procedures to position the main file pointer at the start of a specific field of a specific record. This pointer can then be used to read and write data directly to and from the file.

Appendix D (Data-flow analysis and System Diagrams).

Here are the main system diagrams showing how the program fits in to the three main activities, Creating a file, editing it, and viewing it.

Creating the data file.

This system diagram shows the process of transferring data from the source documents, as the data is typed in on the keyboard, to the left, the file is created, and data added to it, as the process, and the new data file is output to the right. The system diagram does not show, for clarity, the validation processes that validate the incoming data. Specifically, keyed in data is checked for the correct range, when specifying field sizes, for example. Strings are also checked to ensure that they are not too long. Errors and user feedback is where the feedback from the data input is sent, since it would make little sense to store these in the data file. These are therefore reported back to the user, indicating what corrective action needs to be taken,

Editing the data file.

This system diagram shows the old data taken from the old file, and how it is merged with new data, to produce the new data file. This new data can either be added to the old data, adding records, or it can replace the old data, changing the records. Alternatively, the new data can be an instruction to abandon the record, so deleting a record. However, deleting records is not implemented in my program, since it was not specifically requested, and would have been complex to toce. Again, any errors are fed back to the user via the screen, directing them as to what corrective action may be required.

Viewing the data.

This diagram shows the processes involved in viewing the data. It shows how the data file is taken in as an input to the browsing engine, as are the user commands. These two inputs allow the user to browse through the file. The browsing engine performs all the reading of the data file, and all the searching tasks.

Appendix E (System Testing).

Most parts of the system were tested before adding them to the main project. These can be broken down into basically three areas: Data loading and saving, Data searching, and Data Sorting.

Data loading and saving.

This was the first section of the program to be completed, this was originally restricted to a small, hard-coded data set, using DATA statements. The program would simply read this built-in data, then write it out to a fixed file. On the next invocation, the program would read the data from the file, and display it to the screen. This allowed me to discover my first bug, where the program would get part way through reading the data from the file, and then abort, suffering from a file not found error. This was later traced to the data that was being read, defining the file dimensions, had been previously overwritten by the actual test data, causing the file structure to become corrupted. The cause was at the switch from sequential-access to direct-access, the Start_Of_Data_Offset variable was not getting set correctly to the current pointer value when the sequential data had been written. When this value was then used to calculate new pointer positions, the start-address of the first field of the first record got returned as 0, rather than a value equal to Start_Of_Data_Offset. This caused the sequential data which specified how the data was stored to be over-written with actual data. The remody was to re-code the line that was supposed to read the current file pointer, replacing it with one that actually worked.

The next bug was found when testing the program's ability to use a data file with a different number of records. Reducing this number then caused the program to malfunction, reporting that the FieldLengths() array caused an Out of Range error. This was later found to have been caused by using the wrong variable in it's definition. Rather than use Fields to define its size, I had used Records. This meant that while testing a file with the same number of records as fields, like 3*3, the error would not be found. Also, when testing a file with mode records than fields, again, this array would be over-sizes, but that would not generate the error, neither would it generate unexpected behaviour.

Now those two major bugs are removed, I looked at how the code appeared to be organised, and realized that I needed to do a lot of tidying-up work, so I began the process of splitting the different code sections into distinct procedures and functions.

I needed to write a procedure to keep track of the last file which was opened, called "Lastfile/DAT", it contains a single record, written in sequential mode, which records the file name of the last file opened or edited, allowing that file to be re-opened immediately, without the user having to specify a file name to open. However, my initial implementation of the procedure which checked for the presence of both lastfile/dat and the data file, was dependant on the operating system flagging an error when an attempt was made to open a non- existant file. This procedure, however, is both vulnerable to interference by other factors, such as hitting [ESCAPE] during the open attempt would lead the program to believe the file did not exist, and the operating system did not flag the error if the file was to be opened in read/write mode. Some operating systems may only flag the error when an attempt is made to read from the file. This procedure, therefore must be platform-specific, which runs contrary to the user requirements. The use of ON ERROR to handle this is also quite inefficient, and adds the danger that the program may get caught in a loop, if the line that restores the error handler to it's original state causes an error. As this technique also uses GOTO statements, it is incompatible with the new modular approach to the code, causing errors relating to the use of GOTO within a procedure. This forced me to open the file, as suggested in the ARM Basic reference, for read mode, and check if the channel number returned by OpenIN() is zero. During the development of the reasonably simple test on the channel number, another hole was uncovered, in my coding. The symptoms were that LastFile/DAT was always treated as non- existant. This was caused by a space at the end of IF <condition> THEN(space) when used in a multi-line IF statement. To fix, the space was removed. This approach now leaves the file opening procedures in a stable state.

The final bug that I found related to this, was caused by the capitalisation of "Records". This was causing one of the fields in the file header to be written out as zero. This was one problem introduced when defining a database, using the Define_Database procedure, as it led to part of the saving loop being re-written, to support the variable file geometry, and I accidentally replaced the lower-case "r" with an upper-case one "R".

Data Searching.

This feature was reasonably simple to implement, as it only requires the system to examine the contents of every field of every record, to check if it contains the required string. With a maximum of 200 records, and 10 fields per page, performance is quite 'snappy' even on an old RISC-PC, and really fast on a new RISC-PC. Such high performance would also be seen on other platforms, although the algorithm in inherently inefficient. The algorithm was tested by creating a file, and searching for specific strings. The engine had no bugs, although it is strictly a string-only, case insensitive match. New search-engines may be expected to match multiple words, and include support for wildcards, which I believe is out of the scope of my program.

Data Sorting.

This function was reasonably hard to achieve a satisfactorily working system. The existing resources for sorting strings in ARM basic are somewhat limiting. They will always, for instance, return upper-case strings first, and lower-case strings last, regardless of the actual contents of the string. Therefore to defeat this major problem, I needed to write a function which examines a string and returns a capitals-only version. To do this, I simply loop through each character of the string, and check if it's lower-case, and if it is, make it upper-case, and place it in the string. I check if it's lower-case by testing the ASCII value, regarding values in the range of 97 to 122. I make it uppercase by subtracting 32 from it's ASCII value. The simple sort, below produced two, quite differant results, the only change being that one program uses my algorithm for converting to upper case, the other doesn't.
The code:
 

Once this sorting algorithm had been implemented, I now found that I was also inserting a blank record at the front. This was happening, because, the searching loop found a record past the end, which was blank, and hence always came before the other records. This was fixed by ensuring that the sort-engine was always passed a valid last record number, and not the number of records. Having, for instance, 4 records, 0;1;2;3. giving the sort-engine the number 4, lead it to sort records 0;1;2;3;4. Since 4 was past the end of the known data, it was blank, hence the strange problems.

Iains Page