DEFINT A-Z ' Define the symbolic constants used globally in the program: CONST FALSE = 0, TRUE = NOT FALSE ' Define a record structure for random-file records: TYPE StockItem PartNumber AS STRING * 6 Description AS STRING * 20 UnitPrice AS SINGLE Quantity AS INTEGER END TYPE ' Define a record structure for each element of the index: TYPE IndexType RecordNumber AS INTEGER PartNumber AS STRING * 6 END TYPE ' Declare procedures that will be called: DECLARE FUNCTION Filter$ (Prompt$) DECLARE FUNCTION FindRecord% (PartNumber$, RecordVar AS StockItem) DECLARE SUB AddRecord (RecordVar AS StockItem) DECLARE SUB InputRecord (RecordVar AS StockItem) DECLARE SUB PrintRecord (RecordVar AS StockItem) DECLARE SUB SortIndex () DECLARE SUB ShowPartNumbers () ' Define a buffer (using the StockItem type) and ' define & dimension the index array: DIM StockRecord AS StockItem, Index(1 TO 100) AS IndexType ' Open the random-access file: OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN = LEN(StockRecord) ' Calculate number of records in the file: NumberOfRecords = LOF(1) \ LEN(StockRecord) ' If there are records, read them and build the index: IF NumberOfRecords <> 0 THEN FOR RecordNumber = 1 TO NumberOfRecords ' Read the data from a new record in the file: GET #1, RecordNumber, StockRecord ' Place part number and record number in index: Index(RecordNumber).RecordNumber = RecordNumber Index(RecordNumber).PartNumber = StockRecord.PartNumber NEXT SortIndex ' Sort index in part-number order. END IF DO ' Main-menu loop. CLS PRINT "(A)dd records." PRINT "(L)ook up records." PRINT "(Q)uit program." PRINT LOCATE , , 1 PRINT "Type your choice (A, L, or Q) here: "; ' Loop until user presses, A, L, or Q: DO Choice$ = UCASE$(INPUT$(1)) LOOP WHILE INSTR("ALQ", Choice$) = 0 ' Branch according to choice: SELECT CASE Choice$ CASE "A" AddRecord StockRecord CASE "L" IF NumberOfRecords = 0 THEN PRINT : PRINT "No records in file yet. "; PRINT "Press any key to continue."; Pause$ = INPUT$(1) ELSE InputRecord StockRecord END IF CASE "Q" ' End program END SELECT LOOP UNTIL Choice$ = "Q" CLOSE #1 ' All done, close file and end. END ' ' ======================== ADDRECORD ========================= ' Adds records to the file from input typed at the keyboard. ' ============================================================ ' SUB AddRecord (RecordVar AS StockItem) STATIC SHARED Index() AS IndexType, NumberOfRecords DO CLS INPUT "Part Number: ", RecordVar.PartNumber INPUT "Description: ", RecordVar.Description RecordVar.UnitPrice = VAL(Filter$("Unit Price : ")) RecordVar.Quantity = VAL(Filter$("Quantity : ")) NumberOfRecords = NumberOfRecords + 1 PUT #1, NumberOfRecords, RecordVar Index(NumberOfRecords).RecordNumber = NumberOfRecords Index(NumberOfRecords).PartNumber = RecordVar.PartNumber PRINT : PRINT "Add another? "; OK$ = UCASE$(INPUT$(1)) LOOP WHILE OK$ = "Y" SortIndex ' Re-sort index file. END SUB ' ' ========================= FILTER =========================== ' Filters all non-numeric characters from a string ' and returns the filtered string. ' ============================================================ ' FUNCTION Filter$ (Prompt$) STATIC ValTemp2$ = "" PRINT Prompt$; ' Print the prompt passed. INPUT "", ValTemp1$ ' Input a number as ' a string. StringLength = LEN(ValTemp1$) ' Get the string's length. FOR I% = 1 TO StringLength ' Go through the string, Char$ = MID$(ValTemp1$, I%, 1) ' one character at a time. ' Is the character a valid part of a number (i.e., ' a digit or a decimal point)? If yes, add it to ' the end of a new string: IF INSTR(".0123456789", Char$) > 0 THEN ValTemp2$ = ValTemp2$ + Char$ ' Otherwise, check to see if it's a lowercase "l", ' since users used to typewriters may enter a one ' value that way: ELSEIF Char$ = "l" THEN ValTemp2$ = ValTemp2$ + "1" ' Change the "l" to a "1". END IF NEXT I% Filter$ = ValTemp2$ ' Return filtered string. END FUNCTION ' ' ======================= FINDRECORD ========================= ' Uses a binary search to locate a record in the index. ' ============================================================ ' FUNCTION FindRecord% (Part$, RecordVar AS StockItem) STATIC SHARED Index() AS IndexType, NumberOfRecords ' Set top and bottom bounds of search: TopRecord = NumberOfRecords BottomRecord = 1 ' Search until top of range is less than bottom: DO UNTIL (TopRecord < BottomRecord) ' Choose midpoint: Midpoint = (TopRecord + BottomRecord) \ 2 ' Test to see if it's the one wanted (RTRIM$() trims ' trailing blanks from a fixed string): Test$ = RTRIM$(Index(Midpoint).PartNumber) ' If it is, exit loop: IF Test$ = Part$ THEN EXIT DO ' Otherwise, if what we're looking for is greater, ' move bottom up: ELSEIF Part$ > Test$ THEN BottomRecord = Midpoint + 1 ' Otherwise, move the top down: ELSE TopRecord = Midpoint - 1 END IF LOOP ' If part was found, get record from file using ' pointer in index and set FindRecord% to TRUE: IF Test$ = Part$ THEN GET #1, Index(Midpoint).RecordNumber, RecordVar FindRecord% = TRUE ' Otherwise, if part was not found, set FindRecord% ' to FALSE: ELSE FindRecord% = FALSE END IF END FUNCTION ' ' ======================= INPUTRECORD ======================== ' First, INPUTRECORD calls SHOWPARTNUMBERS, which ' prints a menu of part numbers on the top of the screen. ' Next, INPUTRECORD prompts the user to enter a part ' number. Finally, it calls the FINDRECORD and PRINTRECORD ' procedures to find and print the given record. ' ============================================================ ' SUB InputRecord (RecordVar AS StockItem) STATIC CLS ShowPartNumbers ' Call the ShowPartNumbers SUB. ' Print data from specified records on the bottom ' part of the screen: DO PRINT "Type a part number listed above "; INPUT "(or Q to quit) and press : ", Part$ IF UCASE$(Part$) <> "Q" THEN IF FindRecord(Part$, RecordVar) THEN PrintRecord RecordVar ELSE PRINT "Part not found." END IF END IF PRINT STRING$(40, "_") LOOP WHILE UCASE$(Part$) <> "Q" VIEW PRINT ' Restore the text viewport to entire screen. END SUB ' ' ======================= PRINTRECORD ======================== ' Prints a record on the screen ' ============================================================ ' SUB PrintRecord (RecordVar AS StockItem) STATIC PRINT "Part Number: "; RecordVar.PartNumber PRINT "Description: "; RecordVar.Description PRINT USING "Unit Price :$$###.##"; RecordVar.UnitPrice PRINT "Quantity :"; RecordVar.Quantity END SUB ' ' ===================== SHOWPARTNUMBERS ====================== ' Prints an index of all the part numbers in the upper part ' of the screen. ' ============================================================ ' SUB ShowPartNumbers STATIC SHARED Index() AS IndexType, NumberOfRecords CONST NUMCOLS = 8, COLWIDTH = 80 \ NUMCOLS ' At the top of the screen, print a menu indexing all ' the part numbers for records in the file. This menu is ' printed in columns of equal length (except possibly the ' last column, which may be shorter than the others): ColumnLength = NumberOfRecords DO WHILE ColumnLength MOD NUMCOLS ColumnLength = ColumnLength + 1 LOOP ColumnLength = ColumnLength \ NUMCOLS Column = 1 RecordNumber = 1 DO UNTIL RecordNumber > NumberOfRecords FOR Row = 1 TO ColumnLength LOCATE Row, Column PRINT Index(RecordNumber).PartNumber RecordNumber = RecordNumber + 1 IF RecordNumber > NumberOfRecords THEN EXIT FOR NEXT Row Column = Column + COLWIDTH LOOP LOCATE ColumnLength + 1, 1 PRINT STRING$(80, "_") ' Print separator line. ' Scroll information about records below the part-number ' menu (this way, the part numbers are not erased): VIEW PRINT ColumnLength + 2 TO 24 END SUB ' ' ========================= SORTINDEX ======================== ' Sorts the index by part number ' ============================================================ ' SUB SortIndex STATIC SHARED Index() AS IndexType, NumberOfRecords ' Set comparison offset to half the number of records ' in index: Offset = NumberOfRecords \ 2 ' Loop until offset gets to zero: DO WHILE Offset > 0 Limit = NumberOfRecords - Offset DO ' Assume no switches at this offset: Switch = FALSE ' Compare elements and switch ones out of order: FOR I = 1 TO Limit IF Index(I).PartNumber > Index(I + Offset).PartNumber THEN SWAP Index(I), Index(I + Offset) Switch = I END IF NEXT I ' Sort on next pass only to where last ' switch was made: Limit = Switch LOOP WHILE Switch ' No switches at last offset, try one half as big: Offset = Offset \ 2 LOOP END SUB