home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / basic / index.bas < prev    next >
Encoding:
BASIC Source File  |  1989-11-09  |  9.0 KB  |  306 lines

  1. DEFINT A-Z
  2.  
  3. ' Define the symbolic constants used globally in the program:
  4. CONST FALSE = 0, TRUE = NOT FALSE
  5.  
  6. ' Define a record structure for random-file records:
  7. TYPE StockItem
  8.     PartNumber AS STRING * 6
  9.     Description AS STRING * 20
  10.     UnitPrice AS SINGLE
  11.     Quantity AS INTEGER
  12. END TYPE
  13.  
  14. ' Define a record structure for each element of the index:
  15. TYPE IndexType
  16.     RecordNumber AS INTEGER
  17.     PartNumber AS STRING * 6
  18. END TYPE
  19.  
  20. ' Declare procedures that will be called:
  21. DECLARE FUNCTION Filter$ (Prompt$)
  22. DECLARE FUNCTION FindRecord% (PartNumber$, RecordVar AS StockItem)
  23.  
  24. DECLARE SUB AddRecord (RecordVar AS StockItem)
  25. DECLARE SUB InputRecord (RecordVar AS StockItem)
  26. DECLARE SUB PrintRecord (RecordVar AS StockItem)
  27. DECLARE SUB SortIndex ()
  28. DECLARE SUB ShowPartNumbers ()
  29. ' Define a buffer (using the StockItem type)
  30. ' and define and dimension the index array:
  31. DIM StockRecord AS StockItem, index(1 TO 100) AS IndexType
  32.  
  33. ' Open the random-access file:
  34. OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN = LEN(StockRecord)
  35.  
  36. ' Calculate number of records in the file:
  37. NumberOfRecords = LOF(1) \ LEN(StockRecord)
  38.  
  39. ' If there are records, read them and build the index:
  40. IF NumberOfRecords <> 0 THEN
  41.     FOR RecordNumber = 1 TO NumberOfRecords
  42.  
  43.         ' Read the data from a new record in the file:
  44.         GET #1, RecordNumber, StockRecord
  45.  
  46.         ' Place part number and record number in index:
  47.         index(RecordNumber).RecordNumber = RecordNumber
  48.         index(RecordNumber).PartNumber = StockRecord.PartNumber
  49.     NEXT
  50.  
  51.     SortIndex            ' Sort index in part-number order.
  52. END IF
  53.  
  54. DO                      ' Main-menu loop.
  55.     CLS
  56.     PRINT "(A)dd records."
  57.     PRINT "(L)ook up records."
  58.     PRINT "(Q)uit program."
  59.     PRINT
  60.     LOCATE , , 1
  61.     PRINT "Type your choice (A, L, or Q) here: ";
  62.  
  63.     ' Loop until user presses, A, L, or Q:
  64.     DO
  65.         Choice$ = UCASE$(INPUT$(1))
  66.     LOOP WHILE INSTR("ALQ", Choice$) = 0
  67.  
  68.     ' Branch according to choice:
  69.     SELECT CASE Choice$
  70.         CASE "A"
  71.      AddRecord StockRecord
  72.         CASE "L"
  73.      IF NumberOfRecords = 0 THEN
  74.          PRINT : PRINT "No records in file yet. ";
  75.          PRINT "Press any key to continue.";
  76.          Pause$ = INPUT$(1)
  77.      ELSE
  78.          InputRecord StockRecord
  79.      END IF
  80.         CASE "Q"          ' End program.
  81.     END SELECT
  82. LOOP UNTIL Choice$ = "Q"
  83.  
  84. CLOSE #1                ' All done, close file and end.
  85. END
  86. ' ======================== ADDRECORD ======================
  87. ' Adds records to the file from input typed at the keyboard
  88. ' =========================================================
  89. ' ========================= FILTER ========================
  90. '       Filters all non-numeric characters from a string
  91. '       and returns the filtered string
  92. ' =========================================================
  93. ' ======================= FINDRECORD ===================
  94. '  Uses a binary search to locate a record in the index
  95. ' ======================================================
  96. ' ======================= PRINTRECORD =====================
  97. '                Prints a record on the screen
  98. ' =========================================================
  99. ' ===================== SHOWPARTNUMBERS ===================
  100. ' Prints an index of all the part numbers in the upper part
  101. ' of the screen
  102. ' =========================================================
  103. ' ========================= SORTINDEX =====================
  104. '                Sorts the index by part number
  105. ' =========================================================
  106.  
  107.  
  108. SUB AddRecord (RecordVar AS StockItem) STATIC
  109.     SHARED index() AS IndexType, NumberOfRecords
  110.     DO
  111.         CLS
  112.         INPUT "Part Number: ", RecordVar.PartNumber
  113.         INPUT "Description: ", RecordVar.Description
  114.  
  115.         ' Call the Filter$ FUNCTION to input price & quantity:
  116.         RecordVar.UnitPrice = VAL(Filter$("Unit Price : "))
  117.         RecordVar.Quantity = VAL(Filter$("Quantity   : "))
  118.  
  119.         NumberOfRecords = NumberOfRecords + 1
  120.  
  121.         PUT #1, NumberOfRecords, RecordVar
  122.  
  123.         index(NumberOfRecords).RecordNumber = NumberOfRecords
  124.         index(NumberOfRecords).PartNumber = RecordVar.PartNumber
  125.         PRINT : PRINT "Add another? ";
  126.         OK$ = UCASE$(INPUT$(1))
  127.     LOOP WHILE OK$ = "Y"
  128.  
  129.     SortIndex            ' Sort index file again.
  130. END SUB
  131.  
  132. FUNCTION Filter$ (Prompt$) STATIC
  133.     ValTemp2$ = ""
  134.     PRINT Prompt$;                    ' Print the prompt passed.
  135.     INPUT "", ValTemp1$               ' Input a number as
  136.                                                  ' a string.
  137.     StringLength = LEN(ValTemp1$)     ' Get the string's length.
  138.     FOR I% = 1 TO StringLength        ' Go through the string,
  139.         Char$ = MID$(ValTemp1$, I%, 1) ' one character at a time.
  140.  
  141.         ' Is the character a valid part of a number (i.e.,
  142.         ' a digit or a decimal point)?  If yes, add it to
  143.         ' the end of a new string:
  144.         IF INSTR(".0123456789", Char$) > 0 THEN
  145.             ValTemp2$ = ValTemp2$ + Char$
  146.  
  147.         ' Otherwise, check to see if it's a lowercase "l",
  148.         ' since typewriter users may enter a one that way:
  149.         ELSEIF Char$ = "l" THEN
  150.             ValTemp2$ = ValTemp2$ + "1" ' Change the "l" to a "1".
  151.         END IF
  152.     NEXT I%
  153.  
  154.     Filter$ = ValTemp2$               ' Return filtered string.
  155.  
  156. END FUNCTION
  157.  
  158. FUNCTION FindRecord% (Part$, RecordVar AS StockItem) STATIC
  159.     SHARED index() AS IndexType, NumberOfRecords
  160.  
  161.     ' Set top and bottom bounds of search:
  162.     TopRecord = NumberOfRecords
  163.     BottomRecord = 1
  164.  
  165.     ' Search until top of range is less than bottom:
  166.     DO UNTIL (TopRecord < BottomRecord)
  167.  
  168.         ' Choose midpoint:
  169.         Midpoint = (TopRecord + BottomRecord) \ 2
  170.  
  171.         ' Test to see if it's the one wanted (RTRIM$()
  172.         ' trims trailing blanks from a fixed string):
  173.         Test$ = RTRIM$(index(Midpoint).PartNumber)
  174.  
  175.         ' If it is, exit loop:
  176.         IF Test$ = Part$ THEN
  177.      EXIT DO
  178.  
  179.         ' Otherwise, if what we're looking for is greater,
  180.         ' move bottom up:
  181.         ELSEIF Part$ > Test$ THEN
  182.      BottomRecord = Midpoint + 1
  183.  
  184.         ' Otherwise, move the top down:
  185.         ELSE
  186.      TopRecord = Midpoint - 1
  187.         END IF
  188.     LOOP
  189.  
  190.     ' If part was found, input record from file using
  191.     ' pointer in index and set FindRecord% to TRUE:
  192.     IF Test$ = Part$ THEN
  193.         GET #1, index(Midpoint).RecordNumber, RecordVar
  194.         FindRecord% = TRUE
  195.  
  196.     ' Otherwise, if part was not found, set FindRecord%
  197.     ' to FALSE:
  198.     ELSE
  199.         FindRecord% = FALSE
  200.     END IF
  201. END FUNCTION
  202.  
  203. ' ======================= INPUTRECORD =====================
  204. '    First, INPUTRECORD calls SHOWPARTNUMBERS, which prints
  205. '    a menu of part numbers on the top of the screen. Next,
  206. '    INPUTRECORD prompts the user to enter a part number.
  207. '    Finally, it calls the FINDRECORD and PRINTRECORD
  208. '    procedures to find and print the given record.
  209. ' =========================================================
  210. SUB InputRecord (RecordVar AS StockItem) STATIC
  211.     CLS
  212.     ShowPartNumbers      ' Call the ShowPartNumbers SUB.
  213.  
  214.     ' Print data from specified records
  215.     ' on the bottom part of the screen:
  216.     DO
  217.         PRINT "Type a part number listed above ";
  218.         INPUT "(or Q to quit) and press <ENTER>: ", Part$
  219.         IF UCASE$(Part$) <> "Q" THEN
  220.      IF FindRecord(Part$, RecordVar) THEN
  221.          PrintRecord RecordVar
  222.      ELSE
  223.          PRINT "Part not found."
  224.      END IF
  225.         END IF
  226.         PRINT STRING$(40, "_")
  227.     LOOP WHILE UCASE$(Part$) <> "Q"
  228.  
  229.     VIEW PRINT   ' Restore the text viewport to entire screen.
  230. END SUB
  231.  
  232. SUB PrintRecord (RecordVar AS StockItem) STATIC
  233.     PRINT "Part Number: "; RecordVar.PartNumber
  234.     PRINT "Description: "; RecordVar.Description
  235.     PRINT USING "Unit Price :$$###.##"; RecordVar.UnitPrice
  236.     PRINT "Quantity   :"; RecordVar.Quantity
  237. END SUB
  238.  
  239. SUB ShowPartNumbers STATIC
  240.     SHARED index() AS IndexType, NumberOfRecords
  241.  
  242.     CONST NUMCOLS = 8, COLWIDTH = 80 \ NUMCOLS
  243.  
  244.     ' At the top of the screen, print a menu indexing all
  245.     ' the part numbers for records in the file. This menu is
  246.     ' printed in columns of equal length (except possibly the
  247.     ' last column, which may be shorter than the others):
  248.     ColumnLength = NumberOfRecords
  249.     DO WHILE ColumnLength MOD NUMCOLS
  250.         ColumnLength = ColumnLength + 1
  251.     LOOP
  252.     ColumnLength = ColumnLength \ NUMCOLS
  253.     Column = 1
  254.     RecordNumber = 1
  255. DO UNTIL RecordNumber > NumberOfRecords
  256.         FOR Row = 1 TO ColumnLength
  257.      LOCATE Row, Column
  258.      PRINT index(RecordNumber).PartNumber
  259.      RecordNumber = RecordNumber + 1
  260.      IF RecordNumber > NumberOfRecords THEN EXIT FOR
  261.         NEXT Row
  262.         Column = Column + COLWIDTH
  263.     LOOP
  264.  
  265.     LOCATE ColumnLength + 1, 1
  266.     PRINT STRING$(80, "_")       ' Print separator line.
  267.  
  268.     ' Scroll information about records below the part-number
  269.     ' menu (this way, the part numbers are not erased):
  270.     VIEW PRINT ColumnLength + 2 TO 24
  271. END SUB
  272.  
  273. SUB SortIndex STATIC
  274.     SHARED index() AS IndexType, NumberOfRecords
  275.  
  276.     ' Set comparison offset to half the number of records
  277.     ' in index:
  278.     Offset = NumberOfRecords \ 2
  279.  
  280.     ' Loop until offset gets to zero:
  281.     DO WHILE Offset > 0
  282.         Limit = NumberOfRecords - Offset
  283.         DO
  284.  
  285.      ' Assume no switches at this offset:
  286.      Switch = FALSE
  287.  
  288.      ' Compare elements and switch ones out of order:
  289.      FOR I = 1 TO Limit
  290.          IF index(I).PartNumber > index(I + Offset).PartNumber THEN
  291.              SWAP index(I), index(I + Offset)
  292.              Switch = I
  293.          END IF
  294.      NEXT I
  295.  
  296.      ' Sort on next pass only to where
  297.      ' last switch was made:
  298.      Limit = Switch
  299.         LOOP WHILE Switch
  300.  
  301.         ' No switches at last offset, try one half as big:
  302.         Offset = Offset \ 2
  303.     LOOP
  304. END SUB
  305.  
  306.