' USAGE  :   $INCLUDE "filecopy.bas"

' EXPECTS:   source = source file (full path!)
'            target = target file (full path!)

' RETURNS:   -1 = Success
'             1 = Source does not exist
'             2 = Source is target
'             3 = Not enough disk space
'             4 = User interrupted

FUNCTION CopyFile(BYVAL Source AS STRING, BYVAL Target AS STRING) PUBLIC AS INTEGER
 DIM Buffsize AS INTEGER
 DIM Drive AS STRING
 DIM Free AS DWORD
 DIM Copied AS DWORD
 DIM TotalCopy AS DWORD
 DIM SourceHandle AS INTEGER
 DIM TargetHandle AS INTEGER
 DIM Handle1 AS INTEGER
 DIM Handle2 AS INTEGER
 DIM Buff AS STRING
 DIM Source2 AS STRING
 DIM Target2 AS STRING
 DIM SourceSeg AS WORD
 DIM SourceOfs AS WORD
 DIM TargetSeg AS WORD
 DIM TargetOfs AS WORD

 IF LEN(DIR$(Source)) = 0 THEN
    ' Source does not exist
    FUNCTION = 1
    EXIT FUNCTION
 END IF

 IF Source = Target THEN
    ' Don't write on top of itself
    FUNCTION = 2
    EXIT FUNCTION
 END IF

 IF MID$(Target, 2, 1) = ":" THEN
    Drive = LEFT$(UCASE$(Target), 1)
 END IF

 ' 63 sectors
 Buffsize = 32256

 IF Exist(Target) THEN
    MakeBAK Target
 END IF

 ' Get free space on target disk
 Free = DiskFree(Drive)

 SourceHandle = FREEFILE

 OPEN Source FOR BINARY AS SourceHandle

 IF Free < LOF(SourceHandle) THEN
    ' Not enough disk space
    CLOSE SourceHandle
    FUNCTION = 3
    EXIT FUNCTION
 END IF

 TargetHandle = FREEFILE

 OPEN Target FOR BINARY AS TargetHandle

 TotalCopy = LOF(SourceHandle)

'---------- Start loop ---------
 DO WHILE NOT EOF(SourceHandle) AND NOT I$ = CHR$(27)
    GET$ SourceHandle, Buffsize, Buff   ' read buffer from source
    PUT$ TargetHandle, Buff             ' write it to target
    INCR Copied, LEN(Buff)
    I$ = INKEY$
 LOOP
'----------- End loop ----------

 IF NOT EOF(SourceHandle) THEN
    ' User interrupted so remove uncomplete file
    CLOSE SourceHandle, TargetHandle
    KILL Target
    FUNCTION = 4 
    EXIT FUNCTION
 END IF

 Handle1 = FILEATTR(SourceHandle, 2)   ' DOS handle for Source
 Handle2 = FILEATTR(TargetHandle, 2)   ' DOS handle for Target
 Source2 = Source + CHR$(0)            ' Need ASCIIZ string
 Target2 = Target + CHR$(0)
 SourceSeg = STRSEG(Source2)
 SourceOfs = STRPTR(Source2)
 TargetSeg = STRSEG(Target2)
 TargetOfs = STRPTR(Target2)

 ' Copy file date/time
 ! push    DS                         ; save DS for PowerBASIC
 ! mov     AX, &H5700                 ; get file date/time
 ! mov     BX, Handle1                ; for source file
 ! int     &h21                       ; through DOS interrupt
 ! mov     AX, &H5701                 ; write file date/time
 ! mov     BX, Handle2                ; for target file
 ! int     &h21                       ; through DOS interrupt
 ! pop     DS

 CLOSE SourceHandle, TargetHandle

 ' Copy file attributes
 ! push    DS
 ! mov     DX, SourceOfs              ; source offset in DX
 ! mov     AX, SourceSeg              ; source segment in DS
 ! mov     DS, AX                     ; via AX
 ! mov     AX, &H4300                 ; get file attribute
 ! int     &H21                       ; through DOS interrupt
 ! mov     DX, TargetOfs              ; target offset in DX
 ! mov     AX, TargetSeg              ; target segment in DS
 ! mov     DS, AX                     ; via AX
 ! mov     AX, &H4301                 ; write file attribute
 ! int     &H21                       ; through DOS interrupt
 ! pop     DS                         ; restore DS

 FUNCTION = -1     ' success
END FUNCTION


FUNCTION MakeBAK(BYVAL Filename AS STRING) AS STRING
 DIM Bak AS STRING
 DIM Dot AS INTEGER
 Bak = Filename
 Dot = INSTR(Bak, ".")
 IF Dot = 0 THEN
    Bak = Bak + ".BAK"
 ELSE
    Bak = LEFT$(Bak, Dot) + "BAK"
 END IF
 IF LEN(DIR$(Bak)) THEN
    KILL Bak
 END IF
 NAME Filename AS Bak
 FUNCTION = Bak
END FUNCTION


FUNCTION DiskFree(BYVAL Drive AS STRING) AS DWORD
 DIM DriveNum AS INTEGER
 DriveNum = ASCII(UCASE$(Drive)) - 64
 IF DriveNum < 0 THEN DriveNum = 0
 ! push    DS                          ; save DS for PowerBASIC
 ! mov     AX, &h3600                  ; function 36h, get drive info
 ! mov     DX, DriveNum                ; put requested drive in DX
 ! int     &h21                        ; call DOS
 ! cmp     AX, &hFFFF                  ; AX = -1?
 ! je      DiskFreeDone                ; yes, there was an error
 ! mul     CX                          ; AX = AX * CX
 ! mul     BX                          ; AX = AX * BX
 ! mov     FUNCTION [0], AX            ; return low part of dword
 ! mov     FUNCTION [2], DX            ; return high part of dword
DiskFreeDone:
 ! pop     DS                          ; restore DS for PowerBASIC
END FUNCTION

