Download text file
' ==========================================================================
' Binary Text File handling
' ==========================================================================
' $LINK "BIN_TXT.PBU
'   DECLARE FUNCTION fBTFopen&   (BYVAL FileName$)
'   DECLARE      SUB  BTFclose   ()
'   DECLARE FUNCTION fBTFread$   (BYVAL LineNo&)
'   DECLARE      SUB  BTFwrite   (BYVAL Text$,BYVAL LineNo&)
'   DECLARE FUNCTION fBTFadd$    (BYVAL Text$,BYVAL LineNo&)
'   DECLARE FUNCTION fBTFdelete& (BYVAL LineNo&)


$if 1
$STRING 32

SHARED sTXTfile$, sTfileNo%                           ' file being read
SHARED sIDXfile$, sXfileNo%                           ' temp index file
SHARED sLastLine&, sCrLf$                             ' #of lines and EOL
SHARED sMaxLen%                                       ' longest line -CrLf
                                                      ' start of test code
  SHELL "COPY D:\INTERRUP\OPCODES.LST"                ' get a fresh copy
  CLS                                                 ' ready the screen
  LastLine& = fBTFopen&( "OPCODES.LST" )            ' create the index
  IF LastLine& = 0 THEN BEEP : END                    ' no text file!
  PRINT USING "Longest Line: ###"; fMaxLineLen%       '
  ThisLine& = 100                                     ' line # to test
  Display7 ThisLine&

  Txt$ = "Test Line" + string$(60, 95 )
  BTFwrite Txt$, ThisLine&

  Display7 ThisLine&
                                                      '
  ByeBye:                                             ' clean-up
    BTFclose                                          '  close/kill index
    END                                               '  end of prog
                                                      '
  SUB Display7( BYVAL ThisLine& ) LOCAL PUBLIC
    LOCAL X&, L$

      PRINT USING "Number of Lines: ##,###"; sLastLine&
      ThisLine& = MAX( ThisLine&, 4 )
      ThisLine& = MIN( ThisLine&, sLastLine& - 3 )
      PRINT STRING$(80,196);
      FOR X& = ThisLine& -3 TO ThisLine& + 3
        L$ = fBTFread$( X& )
        PRINT USING "#### "; X&;
        PRINT LEFT$( L$, 72 ); "<<"
      NEXT
      PRINT STRING$(80,196)
  END SUB
$endif

' =========================================================================
' =================   start of code  ======================================
' =========================================================================
FUNCTION fBTFopen&( BYVAL TxtFile$ ) LOCAL PUBLIC
  LOCAL Chunk%, Offset&, Jump%
  LOCAL D$, L%, P1%, P2%

  IF sXfileNo% > 0 THEN EXIT FUNCTION                ' one at a time, please
  sXfileNo% = 0                                      ' just in case
  sTfileNo% = 0                                      '
  sCrLf$    = CHR$(13,10)                            '
  Jump%     = LEN( sCrLf$ )                          '
  IF LEN( DIR$(TxtFile$) ) = 0 THEN EXIT FUNCTION    ' file doesn't exist
  sTfileNo% = FREEFILE                               ' next file number
  OPEN "B", #sTfileNo%, TxtFile$                     ' open the file
  IF LOF(sTfileNo%) = 0 THEN                         ' empty file
    CLOSE sTfileNo%                                  '  close it
    KILL TxtFile$                                    '  kill it
    EXIT FUNCTION                                    '  RETURN ZERO
  END IF                                             '
  sTXTfile$ = TxtFile$                               ' store to SHARED var
  sIDXfile$ = EXTRACT$( TxtFile$, "." ) + "._$_"     ' create file name
  sXfileNo% = FREEFILE                               ' next file number
  IF LEN(DIR$(sIDXfile$)) > 0 THEN KILL sIDXfile$    ' erase existing file
  OPEN "B", #sXfileNo%, sIDXfile$                    ' create index file
  Offset& = 0                                        ' set offset pointer
  PUT #sXfileNo%, ,Offset&                           ' dummy record
  PUT #sXfileNo%, ,Offset&                           ' 1st line at offset 4
                                                     '
  Chunk% = FRE(-4)                                   ' max string length
  DECR Chunk%, ( Chunk% MOD 512 )                    ' rounded to 512bytes
                                                     '
  DO                                                 '
    SEEK #sTfileNo%, Offset&                         ' set infile's pointer
    GET$ #sTfileNo%, Chunk%, D$                      ' read in the chunk
    Chunk% = LEN(D$)                                 ' check the length
    P1% = 1                                          ' reset buffer pointer
    DO                                               ' process the buffer
      P2% = INSTR( P1%, D$, sCrLf$ )                 '  find next CrLf
      IF P2% = 0 THEN EXIT LOOP                      '  end of buffer
      L% = P2% - P1% + 2                             '  length of line
      sMaxLen% = MAX%( sMaxLen%, L% )                '  get new max length
      P1% = P2% + Jump%                              '  set buffer pointer
      INCR Offset&, L%                               '  the NEXT line
      PUT #sXfileNo%, ,Offset&                       '  store line pointer
    LOOP UNTIL P1% => Chunk%                         '  just in case
  LOOP UNTIL EOF(sTfileNo%)                          ' all done
  sLastLine& = ( LOF(sXfileNo%) \ 4 ) -2             ' compute # of lines
  DECR sMaxLen%, Jump%                               ' adjust max line len
  sMaxLen%   = MIN%( 512, sMaxLen% )                 ' can't be longer
                                                     '
  FUNCTION = sLastLine&                              ' RETURN # of lines
                                                     '
END FUNCTION                                         '

' =========================================================================

FUNCTION fBTFmaxlen% () LOCAL PUBLIC

  FUNCTION = sMaxLen%

END FUNCTION

' =========================================================================

SUB BTFClose LOCAL PUBLIC

  CLOSE #sTfileNo%                                   ' close both file
  CLOSE #sXfileNo%                                   '
  KILL sIDXfile$                                     ' kill index file
  sTfileNo%  = 0                                     ' clear SHARED vars
  sXfileNo%  = 0                                     '
  sLastLine& = 0                                     '
  sMaxLen%   = 0
  sCrLf$     = ""                                    '
  sIDXfile$  = ""                                    '
  sTXTfile$  = ""                                    '

END SUB

' =========================================================================

FUNCTION fBTFread$( BYVAL LineNo& ) LOCAL PUBLIC
  LOCAL Offset&, Txt$

  IF LineNo& < 1          THEN EXIT FUNCTION         ' not far enough!
  IF LineNo& > sLastLine& THEN EXIT FUNCTION         ' too far!
  SEEK #sXfileNo%, ( LineNo& * 4 )                   ' 4 bytes per pointer
  GET  #sXfileNo%, , Offset&                         ' get the offset
  SEEK #sTfileNo%, Offset&                           ' set the file pointer
  GET$ #sTfileNo%, 512, Txt$                         ' read in a buffer full
  FUNCTION = EXTRACT$( Txt$, sCrLf$ )                ' strip off the CrLf

END FUNCTION

' ==========================================================================

FUNCTION fBTFadd&( BYVAL Txt$, BYVAL LineNo& ) LOCAL PUBLIC
  LOCAL Offset&, X&, Noff&, Bytes%

  Txt$    = EXTRACT$( Txt$, sCrLf$ ) + sCrLf$        ' massage incoming $
  Bytes%  = LEN( Txt$ )                              ' get the length
  LineNo& = MIN( LineNo&, sLastLine& )               ' just in case
  LineNo& = MAX( LineNo&, 0 )                        '
  INCR sLastLine&                                    ' new last line
  INCR LineNo&                                       ' and new after line#
  X& = ( LineNo& * 4 )                               ' pointer offset
  GET# sXfileNo%, X&, Offset&                        ' txt file offset
  IF sLineNo& < sLastLine& THEN                      ' if not new last line
    MoveFileBytes sTfileNo%, Offset&, Bytes%         '  then move data down
  END IF                                             '
  SEEK #sTfileNo%, Offset&                           ' position file pointer
  PUT$ #sTfileNo%, Txt$                              ' and put the new line
                                                     '
  Offset& = ( sLastLine& * 4 )                       ' start at the bottom
  FOR X& = sLastLine& TO LineNo& STEP -1             ' of the index file
    GET #sXfileNo%, Offset&, Noff&                   ' get the old ptr
    INCR Noff&, Bytes%                               ' incr it by the length
    PUT #sXfileNo%, , Noff&                          ' put it back 4bytes
    DECR Offset&, 4                                  ' farther down
  NEXT                                               '
                                                     '
  FUNCTION = sLastLine&                              '  RETURN new last line

END FUNCTION

' ==========================================================================

SUB BTFwrite( BYVAL Txt$, BYVAL LineNo& ) LOCAL PUBLIC
  LOCAL OldLen%, NewLen%, Bytes%
  LOCAL Noff&, X&, Offset&, Null$

  IF LineNo& < 1          THEN EXIT SUB              ' not far enough!
  IF LineNo& > sLastLine& THEN EXIT SUB              ' too far!
  Txt$ = EXTRACT$( Txt$, sCrLf$ ) + sCrLf$           ' one line only, please
  SEEK #sXfileNo%, ( LineNo& * 4 )                   ' 4 bytes per pointer
  GET #sXfileNo%, ,Offset&                           ' offset to this line
  GET #sXfileNo%, ,Noff&                             ' offset to next line
  OldLen% = Noff& - Offset&                          ' compute orig. length
  NewLen% = LEN(Txt$)                                ' new lenght
  Bytes%  = NewLen% - OldLen%                        ' difference in bytes
  IF Bytes% < 1 THEN                                 ' decrease file size
    SEEK #sTfileNo%, Offset&                         '  position pointer
    PUT$ #sTfileNo%, Txt$                            '  put the new info
    IF Bytes% = 0 THEN EXIT SUB                      '  all done here!
    IF LineNo& = sLastLine& THEN GOTO FixIndex       '  don't need move data
    Offset& = Noff& + Bytes%                         '  new eol positon
  END IF                                             '
  MoveFileBytes sTfileNo%, Offset&, Bytes%           ' move the data
  IF Bytes% > 0 THEN                                 ' increased file size
    SEEK #sTfileNo%, Offset&                         '  position pointer
    PUT$ #sTfileNo%, Txt$                            '  put the new info
  END IF                                             '
                                                     '
  FixIndex:                                          '
    Offset& = ( LineNo& * 4 )                        '
    FOR X& = LineNo& + 1 TO sLastLine& + 1           ' adjust pointer to
      INCR Offset&, 4                                '  new positions
      GET  #sXfileNo%, Offset&, Noff&                '  read old pointer
      INCR Noff&, Bytes%                             '  adjust
      PUT  #sXfileNo%, Offset&, Noff&                '  write new pointer
    NEXT                                             '
    IF Bytes% < 0 THEN BTFtruncate Bytes%            ' all done here


END SUB

' ==========================================================================

FUNCTION fBTFdelete&( LineNo& ) LOCAL PUBLIC
  LOCAL Offset&, Noff&, X&
  LOCAL Bytes%, NULL$, Poff&

  IF LineNo& < 1          THEN EXIT FUNCTION         ' not far enough!
  IF LineNo& > sLastLine& THEN EXIT FUNCTION         ' too far!
  DECR sLastLine&                                    '
  Poff& = ( LineNo& * 4 )                            '
  GET #sXfileNo%, Poff&, Offset&                     '
  GET #sXfileNo%,      , Noff&                       '
  Bytes% = ( Noff& - Offset& )                       '
  MoveFileBytes sTfileNo%, Offset&, -Bytes%          '
                                                     '
  Noff& = Poff& + 4                                  ' next line offset
  FOR X& = LineNo& TO sLastLine&                     ' move line offset data
    GET #sXfileNo%, Noff&, Offset& : INCR Noff&, 4   '  up one line and
    DECR Offset&, Bytes%                             '  decr by lost bytes
    PUT #sXfileNo%, Poff&, Offset& : INCR Poff&, 4   '
  NEXT                                               '
  BTFtruncate Bytes%                                 ' chop the txt file
                                                     '
  FUNCTION = sLastLine&                              ' RETURN new last line#

END FUNCTION

' ==========================================================================

SUB BTFtruncate( BYVAL Bytes% ) LOCAL PRIVATE
  LOCAL Null$, Offset&

  Null$ = ""
  Offset& = LOF( sTfileNo% ) - Bytes%                ' new EOF
  SEEK #sTfileNo%, Offset&                           ' set the pointer
  $if 1                                              ' pb v3.2 & fb
    PUT$ #sTfileNo%, Null$                           '  put a NULL string
    CLOSE #sTfileNo%                                 '  close the file
    OPEN "B", #sTfileNo%, sTXTfile$                  '  reopen it
  $else                                              ' pb v3.5
    SETEOF #sTfileNo%                                '
  $endif

END SUB

'============================================================================
'
' PURPOSE: to move bytes of a BIN file around to create/close-up space
'  PARAMS: FileNo%  PowerBASIC's file number
'          Start&   starting offset of data to be moved
'          Bytes&   < 0 the number of bytes to close-up
'                   > 0 the number of bytes to open up
'    NOTE: When closing-up data the value of Start& is the 1st byte
'          to be over-written NOT the first byte to be moved to the
'          new position. This was done to keep the extra math inside
'          this routine and out of all the calls to it!
'                                                     5
'          THE FILE as it was:                   11111abcdef_22222
'          MoveFileBytes FileNo%, 5, +7 produces 11111abcdef_abcdef_22222
'          MoveFileBytes FileNo%, 5, -7 produces 1111122222
'
'============================================================================
SUB MoveFileBytes( BYVAL FileNo%, BYVAL StartOff&, BYVAL Bytes& ) LOCAL PUBLIC
  LOCAL B%, BytesToMove&, L&, MaxBytes%, D$
                                                   'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  MaxBytes%    = FRE(-4)                           '³ biggest string chunk
  DECR MaxBytes%, ( MaxBytes% MOD 512 )            '³ round off to 512
  L&           = LOF( FileNo% )                    '³ length of file
  BytesToMove& = L& - StartOFF&                    '³
  IF Bytes& > 0 THEN                               '³ opening a space
      StartOff&    = L&                            '³
    ELSE                                           '³ closing up a gap
      StartOFF&    = StartOFF&    - Bytes&         '³ this adds the neg #
      BytesToMove& = BytesToMove& + Bytes&         '³ this subs the neg #
  END IF                                           '³
                                                   '³
  DO                                               '³
    B% = MIN( MaxBytes%, BytesToMove& )            '³ size of chunk
    IF Bytes& > 0 THEN DECR StartOff&, B%          '³ fix seek position <¿
    SEEK #FileNo%, StartOff&                       '³ set get position   ³
    GET$ #FileNo%, B%, D$                          '³ take a bite        ³or
    SEEK #FileNo%, StartOff& + Bytes&              '³ set put position   ³
    PUT$ #FileNo%, D$                              '³ spit it out        ³
    IF Bytes& < 0 THEN INCR StartOff&, B%          '³ fix seek position <Ù
    DECR BytesToMove&, B%                          '³ decr work load by chunk
  LOOP UNTIL BytesToMove& =< 0                     '³
                                                   'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
END SUB