home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / qbnewsl / qbnws302 / qbnws302.nws
Text File  |  1992-06-29  |  44KB  |  1,160 lines

  1.      Volume  3, Number  2                                     June 29, 1992
  2.  
  3.      
  4.      
  5.      
  6.      
  7.      
  8.      
  9.      
  10.      
  11.      
  12.      
  13.      
  14.      
  15.                    **************************************************
  16.                    *                                                *
  17.                    *                    QBNews                      *
  18.                    *                                                *
  19.                    *      International QuickBASIC Electronic       *
  20.                    *                  Newsleter                     *
  21.                    *                                                *
  22.                    *    Dedicated to promoting QuickBASIC around    *
  23.                    *                  the world                     *
  24.                    *                                                *
  25.                    **************************************************
  26.      
  27.      
  28.      
  29.      
  30.      
  31.      
  32.      
  33.      
  34.      
  35.      
  36.      
  37.      
  38.      
  39.      
  40.      
  41.      
  42.         The  QBNews  is  an electronic newsletter  published  by  Clearware
  43.      Computing. It can be freely distributed providing NO CHARGE is charged
  44.      for  distribution.  The  QBNews is copyrighted in  full  by  Clearware
  45.      Computing.  The  authors  hold  the  copyright  to  their   individual
  46.      articles.  All program code appearing in QBNews is released  into  the
  47.      public  domain.   You  may  do what you  wish  with  the  code  except
  48.      copyright  it.  QBNews  must  be  distributed  whole  and  unmodified.
  49.      
  50.      You can write The QBNews at:
  51.      
  52.           The QBNews
  53.           P.O. Box 507
  54.           Sandy Hook, CT 06482
  55.      
  56.      Copyright (c) 1992 by Clearware Computing.
  57.      
  58.      The QBNews                                                   Page    i
  59.      Volume  3, Number  2                                     June 29, 1992
  60.  
  61.      
  62.  
  63.      ----------------------------------------------------------------------
  64.  
  65.                         T A B L E   O F   C O N T E N T S
  66.  
  67.      
  68.      1.  From the Editor's Desk
  69.           Is this Goodbye? .............................................  1
  70.  
  71.      2.  DataBASICs and File I/O
  72.           File Types 100 by Richard Vannoy .............................  2
  73.           Indexing 101 by Richard Vannoy ...............................  5
  74.           Hashing-It isn't all Corned Beef by Dave Cleary and Mike Avery 10
  75.           Create dBASE III Data Files from QuickBASIC by Dennis Gellert  11
  76.           Adding LANtastic Support to your Programs by Chip Morrow ..... 13
  77.           A powerful line editor for QB by Larry Stone ................. 15
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.      The QBNews                                                   Page   ii
  118.      Volume  3, Number  2                                     June 29, 1992
  119.  
  120.  
  121.  
  122.      ----------------------------------------------------------------------
  123.                    F r o m   t h e   E d i t o r ' s   D e s k
  124.      ----------------------------------------------------------------------
  125.  
  126.      Is this Goodbye?
  127.      
  128.      As you may have noticed, this edition of The QBNews is not up to the
  129.      usual high standards set by previous issues. The problem is that I am
  130.      running out of people who I can hit up for free articles. Most of the
  131.      articles that have appeared in The QBNews were because of direct
  132.      solicitations from me. This issue relied on on contributions sent in
  133.      to me without my asking for them.
  134.      
  135.      For some reason, at least from the mail I have (or have not is more
  136.      like it) received, interest seems to be wanning in The QBNews. Because
  137.      of this, I have no choice but to suspend offering disk subscriptions
  138.      and as of right now, Volume 3 will be the end of The QBNews. The
  139.      months that passed since the 301 issue have really been disappointing
  140.      as far as response goes. I don't expect this issue to generate much
  141.      either because of how poor it is. However, I am committed to
  142.      continuing on until 304, and alot can happen in those six months. If
  143.      possible, I would like to continue producing The QBNews. However, that
  144.      means the term "user supported" will have to take on much more meaning
  145.      in the coming months
  146.      
  147.      Dave Cleary - Publisher of The QBNews
  148.      
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.      The QBNews                                                     Page  1
  177.      Volume  3, Number  2                                     June 29, 1992
  178.  
  179.  
  180.  
  181.      ----------------------------------------------------------------------
  182.                   D a t a B A S I C s   a n d   F i l e   I / O
  183.      ----------------------------------------------------------------------
  184.  
  185.      File Types 100 by Richard Vannoy
  186.      
  187.      This article will familiarize beginners to computer programming and
  188.      the QuickBASIC programming language with the basics of file creation
  189.      and a brief on the types of files generally used in computer programs.
  190.      First, we need a few definitions that describe how data is stored.
  191.      
  192.      Definitions:
  193.         Field:  A particular type of information in a file.
  194.                 Common field names would be phone number, name,
  195.                 address, or date.
  196.         Record: The sum of the fields for one person, place or
  197.                 thing.
  198.      
  199.         Field 1:     Name: Richard  <----- Together, these three
  200.         Field 2:    Phone: 777-1212 <-----   fields make one
  201.         Field 3: Birthday: 04\26\60 <-----     record.
  202.      
  203.      There are generally three types of files most commonly used today.
  204.      They are sequential, random access and binary.
  205.      
  206.      Sequential, as the name implies, means that data is written to the
  207.      file in sequence, or one after the other, so if I write the name and
  208.      phone numbers of a few friends in a sequential file, it might look
  209.      like the line below.  (The commas are called delimiters.  They are put
  210.      in by you or the program to separate each piece of data)
  211.      
  212.      Sam,777-5155,George,123-4567,Bill,323-1212
  213.      
  214.      Notice that all of the information (fields and records) are slammed
  215.      together and that there is no way to tell where the name Bill starts
  216.      without reading the file from the beginning.  To retrieve, or read the
  217.      items, we read them from the beginning, sequentially, until we get to
  218.      the information desired.  If Richard is the 100th name in the list, we
  219.      have to READ the first 99 names/phone numbers to get to it.
  220.      
  221.      In a random access file, the fields are defined to a specific length
  222.      and any spaces not used are filled with blanks.  This allows each
  223.      record to be the exact same size.  Like..
  224.      
  225.       Name: 10 bytes |Richard   |
  226.      Phone:  8 bytes |777-1212|
  227.      
  228.      Now we have a record length of 18 bytes, no matter how long the data
  229.      is, so lets write the same info as above to a file..
  230.      
  231.      Sam       777-5155George    123-4567Bill      323-1212
  232.      |                 |                 |
  233.      Note how a new record starts every 18 bytes, and that "unused" bytes
  234.      in the records are filled with spaces, so we can predict where every
  235.      
  236.      The QBNews                                                     Page  2
  237.      Volume  3, Number  2                                     June 29, 1992
  238.  
  239.      record is going to start.  And we don't need separaters since we know
  240.      exactly where each record and each field starts.  Not only that, if we
  241.      know that Richard's info is in the 100th record, we can go directly to
  242.      it since the record length is constant.  Because of this
  243.      predictability, which transforms to SPEED when it is time to find
  244.      information, random access records are well suited to storing and
  245.      retrieving large quantities of data.
  246.      
  247.      These are the two most common storage techniques, but there are many
  248.      more!  One, called Indexed Sequential Access Method (ISAM) is stored
  249.      somewhat like a sequential file, but is accessed through an indexing
  250.      system, which gives it one of the main advantage of sequential files
  251.      (packing info tightly) and also one of the main advantage of random
  252.      access files (FAST recovery).
  253.      
  254.      Binary files...  Well, ALL files are binary files to the extent that
  255.      any DOS file can be opened in the binary mode.  By binary, we
  256.      generally mean we want the ability to see, get or write to any byte in
  257.      the file.  In the examples above, if we wanted to know the three digit
  258.      prefix of Bill's phone number, with both sequential and random access,
  259.      we would have to read in the whole number, and pull out the first
  260.      three digits, but with binary, we could go right to the applicable
  261.      bytes and grab just the 323 prefix.
  262.      
  263.      Another common use of binary files is when we want to a machine
  264.      language (EXE, COM) file and perhaps correct or change just a few
  265.      bytes.
  266.      
  267.      Also, if you have no idea what is in a file, opening it in binary lets
  268.      you look around easier and snoop the file to determine the contents of
  269.      field/record layout.  Opening any of these types of files is handled
  270.      with the OPEN command.  Check your QuickBASIC reference which will
  271.      expand on the use and syntax of the following examples.
  272.      
  273.      OPEN "DATA.FIL" FOR INPUT AS #1
  274.      This opens a file for INPUT only.  You can't write to or change the
  275.      file contents.  You can then read the information one variable at a
  276.      time or one line at a time.
  277.      
  278.      OPEN "DATA.FIL" FOR OUTPUT AS #1
  279.      This opens a file for output.  It creates a new file and allows you to
  280.      write information into it.
  281.      
  282.      OPEN "DATA.FIL" FOR APPEND AS #1
  283.      The APPEND mode does not create a new file.  It allows you to add
  284.      information on to the end of an existing file.
  285.      
  286.      OPEN "DATA.DBF" FOR RANDOM AS #3
  287.      This allows to to define (with the TYPE statement) the type and length
  288.      of fields you want in your data file, such as:
  289.      
  290.      TYPE Information
  291.         firstName AS STRING * 15
  292.         lastName AS STRING * 20
  293.      
  294.      The QBNews                                                     Page  3
  295.      Volume  3, Number  2                                     June 29, 1992
  296.  
  297.         age AS INTEGER
  298.         salary AS SINGLE
  299.      END TYPE
  300.      
  301.      The TYPE defines the fields and sets the proper size so you don't have
  302.      to keep track of the byte counts.
  303.      
  304.      OPEN "FIND.EXE" FOR BINARY AS #4
  305.      Now you are telling the system to open the file and allow you to
  306.      retrieve, see or write any information from one to many bytes.
  307.      
  308.      Each file has its preferred uses.  Data bases, where there are many
  309.      entries such as customers, employees, or items, typically use RANDOM
  310.      files.  Applications where you just need to store and retrieve a small
  311.      number of items generally use SEQUENTIAL files in the INPUT or OUTPUT
  312.      mode.  BINARY files have many special uses such as overlays, graphic
  313.      image files and other specialized applications where binary (as
  314.      opposed to text or ASCII) is stored.
  315.      
  316.      I hope the basic introduction will give you some insight into the
  317.      available file types and their uses.  Have fun programming!
  318.      
  319.  
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.      The QBNews                                                     Page  4
  352.      Volume  3, Number  2                                     June 29, 1992
  353.  
  354.      Indexing 101 by Richard Vannoy
  355.      
  356.      So here's the basics of file indexing.  I'll use the file extensions
  357.      .DBF to indicate a normal RANDOM data file and .NDX to show an index
  358.      file.  We'll start with a data file:
  359.      
  360.      TYPE EmployeeData
  361.         lastname AS STRING * 15
  362.         firstname AS STRING * 10
  363.         employeeNumber AS INTEGER
  364.         SSN AS STRING * 11
  365.         salary AS SINGLE
  366.         hireDate AS STRING * 8
  367.         'Many more fields could (and usually do) go here for a
  368.         'typical company data base.  Imafine that there are
  369.         'perhaps 60 to 80 fields using maybe 400 to 600 bytes
  370.         'each. It's important to remember, as we go along, that
  371.         'ALL of this data for all the employees is much too
  372.         'massive to fit in the memory of most machines.
  373.      END TYPE
  374.      DIM EMPL AS EmployeeData
  375.      
  376.      Now let's suppose the personnel department wants us to design the data
  377.      base so they can quickly retrieve an employee's information, and they
  378.      want to be able to enter EITHER the first and last name OR the
  379.      employee number.  As you could guess, if you were sitting at a
  380.      terminal all day, you would quickly find out that if you can enter
  381.      just the employee number instead of the full name, that data entry
  382.      will be much faster and more efficient.  So if we are dealing with
  383.      documents that have the employee number (as most work type documents
  384.      do), then that would be quicker.  But we also have to allow for the
  385.      case where the employee number is not known.  So, we must also allow
  386.      the employee's name to be entered also.
  387.      
  388.      So we decide to index the data base in two ways; first by employee
  389.      number:
  390.      
  391.      We need a small data structure to contain just two things: the
  392.      employee number (what we're indexing on), and the record number where
  393.      that employee's information can be found.  So..
  394.      
  395.      TYPE EmpNumberIndex
  396.         EmpNumber AS SINGLE
  397.         RecordNumber AS SINGLE
  398.      END TYPE
  399.      DIM NUMB AS EmpNumberIndex
  400.      
  401.      Then we need a small data structure for the first name/Last name
  402.      information:
  403.      
  404.      TYPE EmpNameIndex
  405.         BothNames AS STRING * 26
  406.         RecordNumber AS SINGLE
  407.      END TYPE
  408.      
  409.      The QBNews                                                     Page  5
  410.      Volume  3, Number  2                                     June 29, 1992
  411.  
  412.      DIM ENAM AS EmpNameIndex
  413.      
  414.      Note that the BothNames field is big enough to hold the first name, a
  415.      space, and then the last name.  Also note that both EmpNumberIndex and
  416.      EmpNameIndex have the RecordNumber for where that employee is stored,
  417.      since we don't need ALL the employee info, just a pointer to where we
  418.      can find it.
  419.      
  420.      We're going to open three files...
  421.      
  422.      OPEN "EMPLOYEE.DBF" FOR RANDOM AS #1 LEN = LEN(EMPL)
  423.      OPEN "EMPNUMBR.NDX" FOR RANDOM AS #2 LEN = LEN(NUMB)
  424.      OPEN "EMPLNAME.NDX" FOR RANDOM AS #3 LEN = LEN(ENAM)
  425.      
  426.      Let's start with three employees.
  427.      EMPLOYEE.DBF will have:
  428.      RECLast Name  First Name  Employee Numb.  AND all the------>
  429.      -- ---------  ----------  --------------  rest of the------>
  430.       1 Vannoy     Richard         46          fields for------->
  431.       2 Que        Suzie           32          for several------>
  432.       3 McGee      Bobbie          23          hundred bytes---->
  433.      
  434.      EMPNUMBR.NDX will have:
  435.      Employee Numb.  REC
  436.      --------------  ---
  437.            46         1
  438.            32         2
  439.            23         3
  440.      
  441.      EMPLNAME.NDX will have:
  442.      BothNames      REC
  443.      ---------      ---
  444.      Vannoy Richard  1
  445.      Que Suzie       2
  446.      McGee Bobbie    3
  447.      
  448.      The last thing we have to do before using this data base is to sort
  449.      each .NDX file so we can use a binary search to quickly find what we
  450.      want.
  451.      
  452.      EMPLNUMBR.NDX will now look like:
  453.      Employee Numb.  REC
  454.      --------------  ---
  455.           23          3    (Sorted by Employee number)
  456.           32          2
  457.           46          1
  458.      
  459.      EMPLNAME.NDX will now look like:
  460.      BothNames      REC
  461.      ---------      ---
  462.      McGee Bobbie    3     (Sorted by name)
  463.      Que Suzie       2
  464.      Vannoy Richard  1
  465.      
  466.      
  467.      The QBNews                                                     Page  6
  468.      Volume  3, Number  2                                     June 29, 1992
  469.  
  470.      So far it seems like a lot of work to do all this for just three
  471.      records, but remember that we probably have hundreds, maybe even
  472.      thousands of records, so be aware that the problem of NOT having to
  473.      search thousands of records for a name OR an employee number (and BOTH
  474.      can never be in the same order in the original .DBF file) is what got
  475.      us here in the first place.
  476.      
  477.      In order to understand the speed of an indexing system, it is
  478.      necessary to know how a binary search works, so we'll digress a moment
  479.      to cover it.  The first requirement of a binary search is that the
  480.      records we are searching MUST BE IN ORDER, either numerically or
  481.      alphabetically, for the search to work.  That's why we sorted the .NDX
  482.      files above.
  483.      
  484.      Let's say we are searching the EMPLNAME.NDX file for me (Vannoy
  485.      Richard), all the records are in alphabetical order by name, there are
  486.      1000 records and my name happens to be at record 687.
  487.      
  488.      First set LOW to 1 and HIGH to the number of records (1000).  the
  489.      formula for our first LOOK is going to be (HIGH + LOW)\2 or 500.  LOOK
  490.      at record #500.  Is the name there (alphabetically) greater than or
  491.      less than MY name?  Well, since I'm at 687, the name at 500 has to be
  492.      LESS.  We now readjust and recompute LOOK as folows:
  493.      
  494.      IF NameAt LOOK < MyName THEN
  495.         LOW = 500
  496.      ELSE
  497.         HIGH = 500
  498.      END IF
  499.      NewLOOK = (HIGH + LOW)/2
  500.      
  501.      Notice, we have cut the search area IN HALF (thus the name BINARY) by
  502.      looking at the file JUST ONCE.  And by refining our LOOK record to the
  503.      middle of the new search area which has just been halved, it won't
  504.      take long to find the right record.  The code looks like:
  505.      
  506.         found = 0
  507.         low = 1
  508.         high = numberOfRecords
  509.         DO
  510.            look = (high + low) \ 2  'Integer division
  511.            GET #3, look, ENAM
  512.            IF ENAM.BothNames < SearchingForName$ THEN
  513.               low = look
  514.            ELSEIF ENAM.BothNames > SearchingForName$ THEN
  515.               high = look
  516.            ELSE
  517.               found = 1
  518.               EXIT DO
  519.            END IF
  520.         LOOP UNTIL low = high
  521.         IF found THEN
  522.            'Process the record
  523.         ELSE
  524.      
  525.      The QBNews                                                     Page  7
  526.      Volume  3, Number  2                                     June 29, 1992
  527.  
  528.            PRINT "Record not found."
  529.         END IF
  530.      
  531.      So eventually, one of two things has to happen, either the record is
  532.      found (ENAM.BothNames = SearchName$) or, if the record is NOT in the
  533.      data base, the HIGH and LOW numbers will run together (Thus the LOOP
  534.      UNTIL low = high).
  535.      
  536.      Again, this may SEEM like a lot of trouble, because, say with 100
  537.      records, the best case is that the record you want is at record #50 (1
  538.      look!) but the WORST case is that it will take 7 looks, for an average
  539.      of about four looks at 100 records to find someone.  (Remember that in
  540.      a sequential file, the worst case could be 100 looks for an average of
  541.      about 50 looks!)
  542.      
  543.      And the beauty of binary search is that YOU CAN DOUBLE THE NUMBER OF
  544.      RECORDS AND ONLY INCREASE THE AVERAGE NUMBER OF LOOKS BY ONE!
  545.      
  546.      So, to sum up the binary search routine, lets look for an employee by
  547.      number and name.
  548.      
  549.      IF input was a number THEN
  550.         Binary search the EMPLNUMB.NDX for the number.
  551.         If found, go to the indicated record in EMPLOYEE.DBF and
  552.         display or process the data.
  553.         If not found, print an error message.
  554.      IF input was a string
  555.         Binary search the EMPLNAME.NDX for the name.
  556.         If found, got to the indicated record in EMPLOYEE.DBF and
  557.         display or process the data.
  558.         If not found, print an error message.
  559.      
  560.      Note that the logic for a number or a string is identical, so you
  561.      write the code for a number looked up in EMPLNUMB.NDX, then make a
  562.      copy of the same code and make the minor modifications of looking for
  563.      a string in EMPLNAME.NDX.
  564.      
  565.      That's how it works!  Indexing may seem bulky or unnecessary at first,
  566.      but I can never emphasize enough the increase in speed you get.  You
  567.      will just have to try it or see it for yourself.
  568.      
  569.      The part of an indexing system that takes the most caution and care is
  570.      the maintenance of the .NDX files.  Remember that whenever you add a
  571.      record, delete a record, or change the data (field) that is in an
  572.      index file, then THAT index file must be IMMEDIATELY updated with the
  573.      correct information for the system to work.  It can be easy if you
  574.      take it a step at a time.
  575.      
  576.      In our example, let's say you add an employee.  The new employee's
  577.      data goes in the next available record of EMPLOYEE.DBF, say number
  578.      1234.  His name is Jones Casey, and his employee number is 2345.  Now
  579.      create a new record for EMPNUMBR.NDX
  580.      
  581.      EmpNumb.  Record Number
  582.      
  583.      The QBNews                                                     Page  8
  584.      Volume  3, Number  2                                     June 29, 1992
  585.  
  586.        2345       1234
  587.      
  588.      Put this info at the end of the EMPNUMBR.NDX file and reverse bubble
  589.      sort it up to where it belongs.  Do the same with
  590.      
  591.      EmpName     RecordNumber
  592.      Jones Casey  1234
  593.      
  594.      in the EMPLNAME.NDX file.
  595.      
  596.      All maintenance must be done on the fly (right NOW!), because the very
  597.      next operation may be to find the employee you just added, or
  598.      otherwise process the record that was just manipulated, so do it now.
  599.      
  600.      That should acquaint you with the basic theory of index file creating
  601.      and use.  The best way to fully understand the concepts is to set up
  602.      an indexed file and try things out.  I suggest you set up a data base
  603.      with no index, then add one index, and later one other to get you
  604.      started.
  605.      
  606.      **********************************************************************
  607.      Richard Vannoy started programming as a hobby back in 1975.  In 1980,
  608.      he began his career as a computer programmer and teacher upon his
  609.      retirement as a naval submarine officer.  His projects have included 
  610.      
  611.       . Writing a BASIC navigation program to assist in locating objects 
  612.         of value lost on the bottom of the ocean.  
  613.       . Maintaining an extensive technical manual data base on an Imsai 
  614.         8080 in North Star BASIC.  
  615.       . Maintaining a large government contractor accounting program on 
  616.         a PDP-11 with Fortran.  
  617.       . Various contracting/consulting jobs programming with dBASE III+.  
  618.      
  619.      Richard presently resides in Portland, Oregon where he writes software
  620.      for local businesses in QuickBASIC.
  621.      **********************************************************************
  622.      
  623.  
  624.  
  625.  
  626.  
  627.  
  628.  
  629.  
  630.  
  631.  
  632.  
  633.  
  634.  
  635.  
  636.  
  637.  
  638.  
  639.  
  640.      The QBNews                                                     Page  9
  641.      Volume  3, Number  2                                     June 29, 1992
  642.  
  643.      Hashing - It isn't all Corned Beef by Dave Cleary and Mike Avery
  644.      
  645.      A few months ago, Mike Avery posted some code on the QUIK_BAS echo
  646.      concerning Hashing. I thought this was pretty interesting, so I'm
  647.      including his code in The QBNews and decided to write a little
  648.      introductory to it.
  649.      
  650.      Hashing is an algorithm where you take a record key, and compute a
  651.      file address or record number from it. This approach alows you to keep
  652.      file accesses down to a minimum since there is no index file to worry
  653.      about. You take your key, perform some mathamatical functions on it,
  654.      and bingo, you have your record number of your data.
  655.      
  656.      The first step in hashing is to compute a hash function. An ideal hash
  657.      function would compute a different number for an infinite amount of
  658.      different keys. However, if you were to come up with a perfect hash
  659.      function, you would probably need to use a supercomputer to get decent
  660.      performance. So after coming up with a hash function, you need to come
  661.      up with some sort of collision resolution for the times when two
  662.      different keys hash out to the same record number.
  663.      
  664.      The collision resolution Mike chose to implement was adding a power of
  665.      two to the result of the hash function. Another method would be to use
  666.      linked lists, or store multiple records per computed hash number. An
  667.      example of having multiple records would be this. Say you have 128
  668.      byte records. A disk access of 4096 bytes is an efficient number to
  669.      use. Therefore, you could have up to 32 (4096 \ 128) records per hash
  670.      number.
  671.      
  672.      The code Mike presents is well commented and works. I encourage the
  673.      reader to take some time and play with it. For more information on
  674.      hashing, I suggest you look at some books of basic algorithms. They
  675.      all should contain a section on hashing.
  676.      
  677.      Code for this article appears in HASH.ZIP
  678.      
  679.  
  680.  
  681.  
  682.  
  683.  
  684.  
  685.  
  686.  
  687.  
  688.  
  689.  
  690.  
  691.  
  692.  
  693.  
  694.  
  695.  
  696.  
  697.      The QBNews                                                     Page 10
  698.      Volume  3, Number  2                                     June 29, 1992
  699.  
  700.      Create dBASE III Data Files from QuickBASIC by Dennis Gellert
  701.      
  702.      With the need for a QuickBASIC routine which was able to create a
  703.      dBASE III data file, and encouraged by David Perrys article in
  704.      Volume 1, no 5, I set to work. 
  705.      
  706.      This routine is quite sufficient if there is a need to export data
  707.      from your program in dBASE III format. However, a full database
  708.      application would almost certainly require, in addition,
  709.      Index file management routines.
  710.      
  711.      Armed with the information from the above article, the dBASE III
  712.      manual, and PC Tools, I set to work. The dBASE manual actually
  713.      specifies the header and data structure fairly well, so you're not
  714.      left in the dark when working with the dBASE III file structure.
  715.      
  716.      dBASE refuses to open a data file if it detects there is anything
  717.      amiss with the file structure, so you have to be careful to get
  718.      everything right. I used two checks along the development path:
  719.      firstly, that dBASE would actually open the file, and secondly,
  720.      to compare contents of this file, byte-by-byte, with the contents
  721.      of an identically structured file created with dBASE III itself.
  722.      
  723.      As it stands, the program is an example which creates a dBASE data
  724.      file, and writes one record. As the file structure is embedded in
  725.      the program, it will be necessary to modify the source code to suit
  726.      your needs. The dBASE file structure specification is contained in
  727.      data statements, so this is quite easy to change. The structure of
  728.      the data written to the file will also need to be looked at.
  729.      (Commented in the source code).
  730.      
  731.      Notes on Specifying a dBASE III Structure
  732.      -----------------------------------------
  733.      When creating a new dBASE data file, you must specify its data
  734.      structure. This consists of supplying the Name, Data type,
  735.      Total length (including decimal point, if used), and Decimal
  736.      places for each field.
  737.      
  738.      Field Names:
  739.       - Field names must be in upper-case, or dBASE will not be able to
  740.         access this field. This program uses UCASE$ to ensure this.
  741.       - Field names must not be repeated. This program does not check
  742.         this. If there is a repeated field name, dBASE will not be able
  743.         to access the repeated field.
  744.       - Field names begin with a Letter, and may contain letters, digits,
  745.         or underscores.
  746.       - Field name length, by convention, may be from 1 to 10 characters.
  747.         (but 11 bytes are reserved for this ?).
  748.      
  749.      Field Types and lengths:
  750.         The field type is specified by an upper-case character. The
  751.         maximum lengths for each of the types is shown:
  752.      
  753.            C  - Character    254
  754.      
  755.      The QBNews                                                     Page 11
  756.      Volume  3, Number  2                                     June 29, 1992
  757.  
  758.            N  - Numeric       19
  759.            L  - Logical        1
  760.            D  - Date           8      (set length)
  761.            M  - Memo          10      (set length of pointer)
  762.      
  763.      Code for this article appears in QB-DB3.ZIP
  764.      
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.  
  772.  
  773.  
  774.  
  775.  
  776.  
  777.  
  778.  
  779.  
  780.  
  781.  
  782.  
  783.  
  784.  
  785.  
  786.  
  787.  
  788.  
  789.  
  790.  
  791.  
  792.  
  793.  
  794.  
  795.  
  796.  
  797.  
  798.  
  799.  
  800.  
  801.  
  802.  
  803.  
  804.  
  805.  
  806.  
  807.  
  808.  
  809.  
  810.  
  811.  
  812.      The QBNews                                                     Page 12
  813.      Volume  3, Number  2                                     June 29, 1992
  814.  
  815.      Adding LANtastic Support to your Programs by Chip Morrow
  816.      
  817.      Ever thought it would be nice to throw some rudimentary LANtastic function
  818.      calls into your QB programs?  This set of 8 routines makes it possible to:
  819.       
  820.        - Determine if LANtastic netbios is present
  821.        - Cancel redirection of a device
  822.        - Determine LANtastic machine name currently in use
  823.        - Get a list of redirected device entries
  824.        - Get a list of servers that you're not currently logged into
  825.        - Get a list of servers that you ARE currently logged into
  826.        - Determine LANtastic version currently in use
  827.        - Redirect a device
  828.      
  829.      The DECLARE block used in the sample program LANSTAT:
  830.      
  831.      DECLARE FUNCTION NetBios% ()                           'Is netbios present?
  832.      DECLARE FUNCTION NetCancel% (DevName$)                 'Cancel redirection
  833.      DECLARE FUNCTION NetName% (Machine$)                   'Get machine name
  834.      DECLARE SUB GetDevice (DeviceNum%, DevName$, NetPath$) 'Get device entry(s)
  835.      DECLARE SUB Inactive (EntryNum%, Returned$)            'List available servers
  836.      DECLARE SUB LoggedIn (EntryNum%, LogName$)             'List active servers
  837.      DECLARE SUB NetVersion (Major%, Minor%)                'Get LANtastic version
  838.      DECLARE SUB Redirect (DevType%, DevName$, NetPath$)    'Redirect a device
  839.      DECLARE SUB Strip (A$, B$)                             'Used by these routines
  840.       
  841.      NetBios    - Returns 0 if netbios not installed, or -1 if installed.
  842.      
  843.      NetCancel  - Cancel redirection of an already-active device.  Pass the
  844.                   device name to this routine in the form "c:", "d:", "prn",
  845.                   "lpt1:", etc.  NetCancel% will return zero if successful,
  846.                   or an error code otherwise.
  847.      
  848.      NetName    - Determine machine name currently in use.
  849.      
  850.      GetDevice  - Get a redirected device entry. Pass a value (EntryNum%) as the
  851.                   index number to check, beginning at zero.  DevName$ and NetPath$
  852.                   are returned with this entry's information.  If nothing found
  853.                   for EntryNum%, DevName$ returns "".
  854.      
  855.      Inactive   - Get the name of a server that you're not currently logged into.
  856.                   Pass EntryNum% as the index number to check, beginning at zero.
  857.                   Returned$ will be the name of the inactive server, or "" if
  858.                   nothing found.
  859.       
  860.      LoggedIn   - The reverse of Inactive.  Pass EntryNum% in the same manner,
  861.                   and the returned string will be the active server name, or
  862.                   "" if nothing found.
  863.      
  864.      NetVersion - Determine LANTastic version currently in use.  Major% and
  865.                   Minor% will be returned values.  Version 2.57 of the NOS
  866.                   would be Major = 2, Minor = 57.
  867.       
  868.      Redirect   - Redirect a device to an active server's path.  Works similar
  869.      
  870.      The QBNews                                                     Page 13
  871.      Volume  3, Number  2                                     June 29, 1992
  872.  
  873.                   to LANtastic's NET USE command.  Passed values (from the
  874.                   DECLARE statement above:
  875.       
  876.                   DevType% = 3 if a printer device, 4 for disk.  No other
  877.                              values accepted.
  878.                   DevName$ = Your local device name. "C:", "D:", "LPT1:", etc.
  879.                   NetPath$ = Server's network path to use, in the format
  880.                              \\server_name\pathname
  881.      
  882.                   DevType% returns zero if successful, or an error code otherwise.
  883.       
  884.      Strip      - Rudimentary method of converting fixed-length strings to
  885.                   variable length (there's probably a better way).  Used by
  886.                   most of the above routines in order to pass QB-standard
  887.                   variable-length strings back to you.
  888.       
  889.      Last thing to note here is that I've used these routines only with
  890.      version 2.57 of the NOS.  I don't see why they wouldn't work with
  891.      other versions of LANTastic, but wanted to make you aware.
  892.       
  893.                                                 Happy INTERRUPT'ing!
  894.      
  895.      Code for this article appears in LATASTIC.ZIP.                                           
  896.      
  897.  
  898.  
  899.  
  900.  
  901.  
  902.  
  903.  
  904.  
  905.  
  906.  
  907.  
  908.  
  909.  
  910.  
  911.  
  912.  
  913.  
  914.  
  915.  
  916.  
  917.  
  918.  
  919.  
  920.  
  921.  
  922.  
  923.  
  924.  
  925.  
  926.  
  927.      The QBNews                                                     Page 14
  928.      Volume  3, Number  2                                     June 29, 1992
  929.  
  930.      A powerful line editor for QB by Larry Stone
  931.      
  932.      I use QB's INPUT statement to quickly test code.  However, I find that
  933.      its limitation of 255 characters, the fact that it will  automatically
  934.      wrap at the eightieth column, its inability to pre-size  a  window  or
  935.      work within a virtual window, it's complete lack of character masking,
  936.      and the fact that the floating point library is loaded with it,  when-
  937.      ever it is used, whether needed or not, leaves the QB INPUT  statement
  938.      well short of a desirable routine to use with a "finished" program.
  939.      
  940.      Although I have, and use, an effective substitute routine  written  in
  941.      ASM, the idea of a pure QB routine remained a fancy for quite a while.
  942.      The opportunity to do a pure QB routine came as I was trying  to  come
  943.      up with a project for the class I taught at the local  community  col-
  944.      lege.   I teach (part-time) "Introduction to Modular Programming"  and
  945.      the compiler we use to teach modular programming is, you  guessed  it,
  946.      QB.  The project began in the Fall term - a simple line editor to  use
  947.      in lieu of QB's INPUT statement. My Winter term students were required
  948.      to expand its capabilities to include virtual windowing and  automatic
  949.      exiting when the end of the input line is reached (an optional feature
  950.      used with field entries such as, zip codes and telephone numbers).
  951.      
  952.      The result of our effort (well - mostly my effort) is LINEEDIT.BAS, an
  953.      extremely effective line editor ready to include with your code.  With
  954.      LINEEDIT.BAS are two other highly  useful  modules,  KEYBOARD.BAS  and
  955.      VIDTOOLS.BAS  (These two routines have been abbreviated for brevity).
  956.      
  957.      The KEYBOARD module must be loaded with LINEEDIT.  The VIDTOOLS module
  958.      is not necessary but is included because the test  program,  EDITDEMO,
  959.      makes calls to two of its routines.   The main module, EDITDEMO.BAS is
  960.      a test vehicle for the editor and illustrates how to setup  parameters
  961.      needed to call the editor.
  962.      
  963.      LineEdit has twelve parameters in its parameter list.  Row%, Col%, A$,
  964.      and DisplayLen% are required.  You must tell LineEdit what row to work
  965.      on and what column to display the string from.  You must also supply a
  966.      string to edit (A$), and define the length of the display (better than
  967.      QB's automatic, 255 character length that is guaranteed to wreck havoc
  968.      on your nicely designed screen form).
  969.      
  970.      Let's review some of LineEdit's other parameters - the ones that  turn
  971.      a simple line editor into a power-packed routine.
  972.      
  973.      VwindowSize%, if defined as less than the string's DisplayLen, has  no
  974.      effect on LineEdit.  However, if it is a larger number than DisplayLen
  975.      then it defines a virtual window. Let's say that DisplayLen is defined
  976.      for 60 characters.  If VwindowSize% is smaller then the maximum string
  977.      size allowed is 60 characters.   But, if we define VwindowSize% as 300
  978.      characters then we can now edit a string up to 300 characters within a
  979.      60 character window.   The displayed string will simply slide left  or
  980.      right 10 characters at a time, when we reach the end of the display.
  981.      
  982.      Separaters$, when non-null, tells LineEdit what  characters  are  word
  983.      separaters for jumping the cursor between words (use Ctrl plus arrow).
  984.      
  985.      The QBNews                                                     Page 15
  986.      Volume  3, Number  2                                     June 29, 1992
  987.  
  988.      An effective Separaters$ string might be, " .:-()\/".
  989.      
  990.      Terminators%() MUST BE DIMmed by your controlling program.  It  is  an
  991.      array defining what key presses to exit the editor.  Escape, up & down
  992.      arrows, PgUp and PgDn, are examples of terminator  keys.  The  zeroeth
  993.      element of the array informs LineEdit what the last terminator is.This
  994.      allows extreme flexibility.  Let's say you have defined 7 terminators,
  995.      Escape, Up, Down, PgUp, PgDn, Ctrl + PgUp, and Ctrl + PgDn.  Now let's
  996.      say that your first screen contains a single edit item  - all you need
  997.      is the escape key.  You LET Terminators%(0) = 1. Your first terminator
  998.      is escape and that is now the only terminator that LineEdit will use.
  999.      
  1000.      Your next screen has five edit items.   You now need to use  up, down,
  1001.      PgUp and, PgDn so that your user can navigate between the strings. You
  1002.      LET Terminators%(0) = 5.  If you have multiple screens of edit strings
  1003.      you would LET Terminators%(0) = 7 so that Ctrl + PgUp sends the cursor
  1004.      to the first string of the first screen and Ctrl + PgDn  goes  to  the
  1005.      end string on the last screen.
  1006.      
  1007.      EditMask$ when null has no effect.  However when filled with character
  1008.      A's, tells LineEdit to force every key stroke to upper  case.  If  the
  1009.      EditMask$ looked like, "Aaaaa" then LineEdit would force the 1st  char
  1010.      to upper case and the next four characters to lower case.
  1011.      
  1012.      CurPos% and CurOffset% are input/output parameters.   They can be used
  1013.      to send the cursor to the same position it was in or, set them to zero
  1014.      for fresh starts at the beginning of the edit string.
  1015.      
  1016.      Kee% is a value that defines what keystroke terminated  LineEdit.  You
  1017.      would treat Kee = 13 (enter key) differently than Kee = 27 (escape).
  1018.      
  1019.      While we are on the subject of Kee%, LineEdit uses a routine from KEY-
  1020.      BOARD.BAS to handle all keyboard activity.   The routine, KeyPressed%,
  1021.      returns extended keystrokes as negative numbers.  In other words, down
  1022.      is returned as the value -80 (not CHR$(0) + CHR$(80) as QB does). This
  1023.      means that you must define them this way for you Terminators%() array.
  1024.      
  1025.      Below is a listing of LineEdit's built-in capabilities:
  1026.      
  1027.               Backspace       Deletes character to left of cursor
  1028.               Delete          Deletes character under cursor
  1029.               Ctrl + Home     Deletes from cursor to beginning of line
  1030.               Ctrl + End      Deletes from cursor to end of line
  1031.               Ctrl + Right    Move to word on right (skips separaters)
  1032.               Ctrl + Left     Move to word on left (skips separaters)
  1033.               Home            Move to beginning of string
  1034.               End             Move to space after last char of string
  1035.               Right           Move cursor one character to right
  1036.      
  1037.      The LineEdit code is self-explanatory.    However, one section of code
  1038.      deals with word seperaters and is constructed a little different.
  1039.      
  1040.      StepValue is set to -1 when the user presses Ctrl + Left and StepValue
  1041.      is set to 1 when the user presses Ctrl + Right.  The code reads:
  1042.      
  1043.      The QBNews                                                     Page 16
  1044.      Volume  3, Number  2                                     June 29, 1992
  1045.  
  1046.      
  1047.      IF StepValue < False THEN X = 1 ELSE X = LEN(A$)
  1048.      FOR N = StrPos TO X STEP StepValue
  1049.      
  1050.         '---- Look into A$, one character at a time - is it a seperater?
  1051.         J = INSTR(Separaters$, MID$(A$, N, 1))
  1052.      
  1053.         IF J THEN               'Found a separater character
  1054.             FoundSeparater = J  'Save J's value
  1055.      
  1056.             '---- Move our cursor to this separater position
  1057.             FOR i = StrPos TO N STEP StepValue
  1058.                 IF StepValue < False THEN GOSUB CursorLeft ELSE _
  1059.                 GOSUB CursorRight
  1060.             NEXT
  1061.      
  1062.             EXIT FOR            'Cursor is on a separater so exit the loop
  1063.         END IF
  1064.      NEXT
  1065.      
  1066.      This loop simply steps through the string, one char at  a  time,  then
  1067.      looks into Separaters$ to see if this one character has  been  defined
  1068.      as a word separater.  If it is a separater, its location in the  sepa-
  1069.      rater string is saved and the cursor is moved to that character in the
  1070.      string being edited.
  1071.      
  1072.      '---- If a separater was found, skip any repeating sequences of it.
  1073.      DO WHILE J                     'Loop while Separater has been found
  1074.         N = N + StepValue           'Increment or Decrement N
  1075.         IF N = False THEN EXIT DO   'Prevent error with MID$() function
  1076.      
  1077.         '---- Only looking for repeating sequences of FoundSeparater
  1078.         J = INSTR(MID$(Separaters$, FoundSeparater, 1), MID$(A$, N, 1))
  1079.      
  1080.         IF J THEN                   'If we found another seperater
  1081.             IF StepValue < False THEN GOSUB CursorLeft ELSE _
  1082.             GOSUB CursorRight
  1083.         END IF
  1084.      
  1085.         IF N >= LEN(A$) THEN EXIT DO
  1086.      LOOP
  1087.      
  1088.      This next loop simply looks to see if the found separater character is
  1089.      repeated.  While it is repeated, the cursor is moved  left  or  right.
  1090.      Once past the found separater, the loop is exited.  In other words, if
  1091.      you have defined the space character (ASCII 32) as  a  separater,  and
  1092.      the string has a word followed by ten spaces followed by another word,
  1093.      LineEdit will jump across all ten spaces with a Ctrl + left  or  right
  1094.      arrow keys (a slick little trick).
  1095.      
  1096.      To see how to use LineEdit, load and run its test program, EDITDEMO.
  1097.      
  1098.      Final note.  LineEdit has no code for the Tab key. I leave that to you
  1099.      to encode into the routine.
  1100.      
  1101.      The QBNews                                                     Page 17
  1102.      Volume  3, Number  2                                     June 29, 1992
  1103.  
  1104.      
  1105.      Code for this article appears in LINEDIT.ZIP.
  1106.      
  1107.  
  1108.  
  1109.  
  1110.  
  1111.  
  1112.  
  1113.  
  1114.  
  1115.  
  1116.  
  1117.  
  1118.  
  1119.  
  1120.  
  1121.  
  1122.  
  1123.  
  1124.  
  1125.  
  1126.  
  1127.  
  1128.  
  1129.  
  1130.  
  1131.  
  1132.  
  1133.  
  1134.  
  1135.  
  1136.  
  1137.  
  1138.  
  1139.  
  1140.  
  1141.  
  1142.  
  1143.  
  1144.  
  1145.  
  1146.  
  1147.  
  1148.  
  1149.  
  1150.  
  1151.  
  1152.  
  1153.  
  1154.  
  1155.  
  1156.  
  1157.  
  1158.      The QBNews                                                     Page 18
  1159.  
  1160.