Browsing the NAMES file
Now that three B-trees have been created by the BUILD program for the NAMES file, it is possible to search and display the file in a variety of ways. This is done with a "browser" program like the following:
BROWSE.NAMES001 * Define the fields in our file items002 EQU AMC$FNAME TO 1003 EQU AMC$LNAME TO 2004 EQU AMC$COMP TO 3005 EQU AMC$ADR TO 4006 EQU AMC$ZIP TO 6007 *008 * Define the three available b-tree roots009 EQU root1 TO "ZIP"010 EQU root2 TO "COMP"011 EQU root3 TO "LNAME"012 *013 OPEN "NAMES" TO NFILE ELSE STOP014 OPEN "B-TREE" TO BFILE ELSE STOP015 *016 clear = @(-1) ;* Chars for clearing the screen017 bell = CHAR(7) ;* ...for sounding the alarm018 eos = @(-3) ;* ...clear to end of screen019 eol = @(-4) ;* ...clear to end of line020 center.line = 11 ;* Middle row on screen021 *022 CRT clear: ;* Clear the screen023 WIDTH.SET = 1 ;* Choose std width settings024 *025 * Choose one of the three b-trees to start026 ROOT = root1 ; FRAG = AMC$ZIP027 * ROOT = root2 ; FRAG = AMC$COMP028 * ROOT = root3 ; FRAG = AMC$LNAME029 *030 100 * Pick the starting item id out of the root031 READ ROOT.ID FROM BFILE,ROOT ELSE STOP "Build!"032 READ ROOT.NODE FROM BFILE,ROOT.ID ELSE STOP "Build!"033 * Pick the id in the middle of the node034 CURR.ID = ROOT.NODE<2, INT(ROOT.NODE<1>+2)/2>035 *036 200 * Get the item with the current id037 READ ITEM FROM NFILE, CURR.ID ELSE GO TO 100038 *039 300 * Find the node holding the current id040 CALL BTPFIND(ROOT, BFILE, NFILE, CURR.ID, ITEM, NODE.ID, NODE.POS)041 *042 400 * Remember the node with the current id043 CURR.NODE = NODE.ID ; CURR.POS = NODE.POS044 *045 500 * Define all data and column formats046 BEGIN CASE047 CASE WIDTH.SET = 1 ;* Standard width settings048 NAME.LEN = 11 ;* Width of name field049 FNAME.LEN = 2 ;* Width of first name only050 COMP.LEN = 15 ;* Width of company name051 ADR.LEN = 26 ;* Width of address field052 ZIP.LEN = 5 ;* Width of ZIP field053 ABOVE.BELOW = 9 ;* How many above/below center054 CASE 1 ;* WIDTH.SET must be 2, widen the name055 NAME.LEN = 26 ;* Width of name field056 FNAME.LEN = 11 ;* Width of first name only057 COMP.LEN = 11 ;* Width of company name058 ADR.LEN = 15 ;* Width of address field059 ZIP.LEN = 5 ;* Width of ZIP field060 ABOVE.BELOW = 9 ;* How many above/below center061 END CASE062 *063 * Define positions for V's at top of columns064 COMP.TAB = NAME.LEN+7065 ZIP.TAB = COMP.TAB+COMP.LEN+ADR.LEN+2066 TOTAL.WIDTH = ZIP.TAB+ZIP.LEN ;* For hyphens067 *068 600 CRT @(0,0): ;* Show V's at top of sorted column069 BEGIN CASE070 CASE ROOT = root1; CRT SPACE(ZIP.TAB):STR("V",ZIP.LEN):eol:071 CASE ROOT = root2; CRT SPACE(COMP.TAB):STR("V",COMP.LEN):eol:072 CASE 1; CRT STR("V",NAME.LEN):eol:073 END CASE074 *075 I = 1 ; CRT @(0,center.line): ;* Move to display ctr076 NEXT.ID=CURR.ID;NODE.ID=CURR.NODE;NODE.POS=CURR.POS077 LOOP ;* Paint lower half of tube078 READ ITEM FROM NFILE, NEXT.ID ELSE GO TO 100079 GOSUB 700 ;* Display one item080 * Remember last line painted for quick jumps081 BOTTOM.ID=NEXT.ID ; BOTTOM.NODE=NODE.ID ; BOTTOM.POS=NODE.POS082 IF I = 1 THEN ;* First item083 CRT ; CRT STR("-",TOTAL.WIDTH):eol:084 TOP.ID = NEXT.ID ; TOP.NODE = NODE.ID ; TOP.POS = NODE.POS085 CALL BTPSEQ(BFILE, NODE.ID, NODE.POS, "PREV", PREV.ID)086 PREV.NID = NODE.ID ; PREV.NPOS = NODE.POS087 NODE.ID = CURR.NODE ; NODE.POS = CURR.POS088 END089 CRT ;* Done with this line, go to next090 CALL BTPSEQ(BFILE, NODE.ID, NODE.POS, "NEXT", NEXT.ID)091 WHILE (NEXT.ID # "") AND (I <= ABOVE.BELOW) DO092 I = I+1093 REPEAT094 CRT eos: ;* Clear to end of screen095 *096 * Reposition for top half of display097 ROW = center.line-1098 CRT @(0,ROW):STR("-",TOTAL.WIDTH):eol:099 NEXT.ID = PREV.ID ;* GOSUB expects NEXT.ID100 NODE.ID = PREV.NID ; NODE.POS = PREV.NPOS101 I = ABOVE.BELOW ;* How many loops to make102 LOOP WHILE (NEXT.ID # "") AND (I>0) DO ;* Top half103 READ ITEM FROM NFILE, NEXT.ID ELSE GO TO 100104 ROW=ROW-1; CRT @(0,ROW): ; GOSUB 700 ;* Show item105 * Remember top line painted for future quick jumps106 TOP.ID=NEXT.ID; TOP.NODE=NODE.ID; TOP.POS=NODE.POS107 CALL BTPSEQ(BFILE,NODE.ID,NODE.POS,"PREV",NEXT.ID)108 I = I-1 ;* Move up to previous item109 REPEAT110 *111 LOOP ;* Clear remaining top of screen if any112 ROW = ROW-1113 WHILE ROW > 0 DO114 CRT @(0,ROW):eol: ;* Clear to end of line115 REPEAT116 *117 CRT @(0,23): ;* Go to corner and show commands118 CRT "N/U/UU/D/DD/W1/W2/Z/C/L/X/return/chars/lname(;fname)/(.)id #":119 INPUT COMMAND: ;* Get a command120 CRT @(0,23):eol: ;* Erase commands while processing121 BEGIN CASE122 CASE COMMAND = "U" ;* Move up to previous item123 NODE.ID = CURR.NODE ; NODE.POS = CURR.POS124 CALL BTPSEQ(BFILE,NODE.ID,NODE.POS,"PREV",PREV.ID)125 IF PREV.ID = "" THEN CRT bell: ELSE126 CURR.ID=PREV.ID ; CURR.NODE=NODE.ID ; CURR.POS=NODE.POS127 END128 CASE COMMAND = "UU" ;* Move up to top item129 CURR.ID=TOP.ID ; CURR.NODE=TOP.NODE ; CURR.POS=TOP.POS130 CASE COMMAND = "D" ;* Move down to next item131 NODE.ID = CURR.NODE ; NODE.POS = CURR.POS132 CALL BTPSEQ(BFILE,NODE.ID,NODE.POS,"NEXT",NEXT.ID)133 IF NEXT.ID = "" THEN CRT bell: ELSE134 CURR.ID=NEXT.ID ; CURR.NODE=NODE.ID ; CURR.POS=NODE.POS135 END136 CASE (COMMAND = "DD") OR (COMMAND = "") ;* Move down to last item137 CURR.ID=BOTTOM.ID ; CURR.NODE=BOTTOM.NODE ; CURR.POS=BOTTOM.POS138 CASE COMMAND = "W1" ;* Use standard widths139 WIDTH.SET = 1 ; GO TO 500140 CASE COMMAND = "W2" ;* Use wide names141 WIDTH.SET = 2 ; GO TO 500142 CASE COMMAND = "Z" ;* Use the ZIP b-tree143 ROOT = root1 ; FRAG = AMC$ZIP ; GO TO 200144 CASE COMMAND = "C";* Use the COMP b-tree145 ROOT = root2 ; FRAG = AMC$COMP ; GO TO 200146 CASE COMMAND = "L" ;* Use the LNAME b-tree147 ROOT = root3 ; FRAG = AMC$LNAME ; GO TO 200148 CASE COMMAND = "N" ;* Find different value149 READV PREV.FRAG FROM NFILE, CURR.ID, FRAG ELSE GO TO 100150 LOOP ;* Step through items until value changes151 READ ITEM FROM NFILE, CURR.ID ELSE GO TO 100152 NODE.ID = CURR.NODE ; NODE.POS = CURR.POS153 CALL BTPSEQ(BFILE, NODE.ID, NODE.POS, "NEXT", NEXT.ID)154 CHANGE = (PREV.FRAG # ITEM<FRAG>)155 UNTIL (NEXT.ID = "") OR CHANGE DO156 CURR.ID=NEXT.ID ; CURR.NODE=NODE.ID ; CURR.POS=NODE.POS157 REPEAT158 CASE COMMAND = "X" ; STOP159 CASE ((ROOT=root1) & NOT(COMMAND MATCHES"5N")) OR (COMMAND MATCHES "'.'0N") OR ((ROOT#root1) AND (COMMAND MATCHES "0N"))160 GOOD.ID = CURR.ID ;* Remember good ID number161 CURR.ID = OCONV(COMMAND,"MCN")162 FOUND = 1 ;* Assume new ID exists163 READ ITEM FROM NFILE, CURR.ID ELSE FOUND = 0164 IF FOUND THEN GO TO 300 ELSE CURR.ID = GOOD.ID ; CRT bell:165 CASE 1 ;* Search for arbitrary string166 DUMMY.ID="";DUMMY.ITEM="";*Start w/ nothing167 BEGIN CASE ;* Put string in approp dummy pos168 CASE ROOT=root1;DUMMY.ITEM<AMC$ZIP>=COMMAND169 CASE ROOT=root2;DUMMY.ITEM<AMC$COMP>=COMMAND170 CASE 1 ;* Must be root3, split into names171 DUMMY.ITEM<AMC$LNAME>=OCONV(COMMAND,"G;1")172 DUMMY.ITEM<AMC$FNAME>=OCONV(COMMAND,"G1;1")173 END CASE174 CALL BTPFIND(ROOT,BFILE,NFILE,DUMMY.ID, DUMMY.ITEM,NODE.ID,NODE.POS)175 CURR.ID = DUMMY.ID176 GO TO 400177 END CASE178 GO TO 600179 *180 700 * Display a name and address item181 NAME.STR = ITEM<AMC$FNAME> ("L#":FNAME.LEN):" ":ITEM<AMC$LNAME>182 CRT NAME.STR ("L#":NAME.LEN):" ":183 CRT NEXT.ID "R#5":" ":184 CRT ITEM<AMC$COMP> ("L#":COMP.LEN):" ":185 CRT ITEM<AMC$ADR> ("L#":ADR.LEN):" ":186 CRT ITEM<AMC$ZIP> ("L#":ZIP.LEN):eol:187 RETURN188 *189 ENDUse the Editor to type in the above code for the BROWSE.NAMES program, then compile and catalog the program.
BROWSE.NAMES calls two more B-TREE-P subroutines that help it traverse B-trees: BTPFIND is a subroutine that finds the position of a given file item in a B-tree. BTPSEQ is a subroutine that, given the position of an item previously found with BTPFIND, locates the previous or next item in the B-tree. (BTPSEQ is short for BTP Sequential.) Like BTPINS, the code in BTPFIND and BTPSEQ will never have to be modified, regardless of the application files being used or the way in which files are sorted and searched.
Use the Editor to type in the following code for BTPFIND and BTPSEQ, then compile and catalog the programs:
BTPFIND001 SUBROUTINE BTPFIND(ROOT, BFILE, DFILE, ID, ITEM, NODE.ID, NODE.POS)002 CALL BTPKEY(ROOT, ID, ITEM, FK)003 READ NODE.ID FROM BFILE, ROOT ELSE STOP "BTP8"004 100 READ N FROM BFILE, NODE.ID ELSE STOP "BTP9"005 L = 0 ; R = N<1>+1006 IF R = 1 THEN NODE.POS = 1 ELSE007 LOOP008 NODE.POS = INT((L+R)/2)009 UID = N<2, NODE.POS>010 IF UID = ID THEN RETURN011 READ IT FROM DFILE, UID ELSE STOP "BTP10"012 CALL BTPKEY(ROOT, UID, IT, KEY)013 IG = (FK > KEY)014 IF IG THEN L = NODE.POS ELSE R = NODE.POS015 UNTIL (R-L) <= 1 DO REPEAT016 IF IG THEN NODE.POS = R017 END018 NNID = N<3, NODE.POS>019 IF NNID # "" THEN020 NODE.ID = NNID021 GO TO 100022 END023 IF NODE.POS > N<1> THEN NODE.POS = N<1>024 ID = N<2, NODE.POS>025 RETURN026 END BTPSEQ001 SUBROUTINE BTPSEQ(BFILE,NODE.ID,NODE.POS,PN,ADJKEY)002 PREV = (PN="PREV")003 IF PREV THEN P = NODE.POS ELSE P = NODE.POS+1004 READ N FROM BFILE, NODE.ID ELSE STOP "BTP11"005 I = N<2, NODE.POS>006 100 NNID = N<3, P>007 IF NNID # "" THEN008 NODE.ID = NNID009 READ N FROM BFILE, NODE.ID ELSE STOP "BTP12"010 IF PREV THEN P = N<1>+1 ELSE P = 1011 GO TO 100012 END013 IF N<2, NODE.POS> # I THEN014 IF PREV THEN NODE.POS = N<1> ELSE NODE.POS=1015 ADJKEY = N<2, NODE.POS>016 RETURN017 END018 IF PREV AND (NODE.POS > 1) THEN019 NODE.POS = NODE.POS-1020 ADJKEY = N<2, NODE.POS>021 RETURN022 END023 IF NOT(PREV) AND (NODE.POS < N<1>) THEN024 NODE.POS = NODE.POS+1025 ADJKEY = N<2, NODE.POS>026 RETURN027 END028 200 PID = N<4>029 IF PID # "" THEN030 READ F FROM BFILE, PID ELSE STOP "BTP13"031 LOCATE(NODE.ID,F,3;NODE.POS) ELSE STOP"BTP14"032 IF (PREV AND (NODE.POS=1)) OR (NOT(PREV) AND NODE.POS > F<1>) THEN033 NODE.ID = PID034 N = F035 GO TO 200036 END037 IF PREV THEN NODE.POS = NODE.POS-1038 ADJKEY = F<2, NODE.POS>039 NODE.ID = PID040 END ELSE ADJKEY = ""041 RETURN042 ENDNote that BTPSEQ uses a LOCATE in line 031 to make an unsorted search for NODE.ID in the third attribute of F, setting NODE.POS accordingly. The syntax of the LOCATE statement may have to be slightly different for your compiler.
Now that you have filled the NAMES file with items created with the GET.NAMES program, and have filled the B-TREE file with three B-trees using the BUILD program, you are ready to search and display the NAMES file using the browser program. Give the command BROWSE.NAMES to execute the cataloged BROWSE.NAMES program.
Your terminal will display the NAMES items, sorted by ZIP code. The file items will appear in five columns: names, id numbers, companies, addresses, and ZIP codes. A line of V's at the top of the display indicates that the ZIP column is the currently sorted column. Two horizontal lines of hyphens through the center of the display surround the center "anchor" item. The bottom of the display shows a prompt for one of the fifteen different possible browser commands. You must hit the Return key after any command you type.
Type a U to scroll the display up by one line. Type UU to scroll up ten lines at once. Similarly, type D to scroll down one line, or DD or just the Return key to scroll down ten lines at once.
Type W2 to widen the name column and use less space in the company and address columns. Type W1 to return the name column back to its original width.
Type an L to show the names sorted by last name instead of ZIP code. The name and address in the center of the display remains in position as an anchor, and the rest of the names are resorted above and below. Note that the V's at the top of the display are now over the name column to indicate it is the currently sorted column.
Type a C to show the names sorted by company. As always, the center name and address stays in position, and the remaining items are sorted around it. Type a Z to return to the ZIP code sort.
Typing any string of characters that is not a reserved command finds the first item with that string in the currently selected column, and shows the item at the center of the display. For example, if the company column is currently selected and sorted (designated by V's at the top of the column), and you type MAT, the display will jump to the companies starting with the characters MAT. If no item matches the string you typed, the item that most closely matches is displayed.
Typing a number at any time changes the center anchor item on the display to the item with the ID number you just typed. But if the ZIP column is currently selected with V's and you type a five-digit number, the browser assumes you are searching for a ZIP code, and displays the first item with that ZIP. Precede a five-digit ID with a period (.) to force the browser to treat the number as an ID instead of as a ZIP code when the ZIP column is selected.
If the name column is selected with V's and you type two strings separated by a semicolon (;), the browser treats the string as last name;first name. For example, typing SMITH jumps to the first SMITH, but typing SMITH;T jumps to the first SMITH that also has a first name starting with T.
Type an N to find the next item with a different value in the currently selected column. For example, if the ZIP column is selected and the N command is given, the browser will search starting at the anchor item down the ZIP column until an item with a different ZIP code is found. That item then becomes the new anchor item. The N command is handy for skipping by long runs of identical names or ZIP codes, without having to know what the next different value is.
Finally, the X command will terminate BROWSE.NAMES and exit the program. Type BROWSE.NAMES to restart browsing.