; File MSSFIL.ASM
; Edit history:
; [2.29] [jrd]
; Add further check on multiple dots in a filename; use first, convert
;  others to X. [jrd]
;
; [v2.28 jrd]
; Major cleanup and revisions.
; Fix range check error (for control codes) in packet decoder, plus
; two minor bug patches. Improve unique filename algorithm. Etc.
; Straighten out encode and decode, etc buffer counters.
; Remove DOS 1.x code. J R Doupnik 12 Oct 1985
; 27 Dec 1985: Add code to (hopefully) avoid overwriting any file marked
; as read-only, hidden, system, volume label, or subdirectory. Also,
; enforce max filename length of 64 text chars and 3 extension chars on
; incoming files (receive or get commands). Telescope filename verification
; code into a single procedure named verfil. [jrd]
; Improve accuracy of percentage file transferred.
; [v2.28]
; Clear retry count before writing to it
; Redo unique filename generation.
; Jeff Damens 14 May 1985

        public  bufpnt, buff, cptio, chrcnt, init, init1,
        public  gofil, outbuf, ptchr, gtchr, gtnfil, getfil, filbuf,
        public  encode, decode, nulref, nulr, decbuf, errpack, rptq,
        public  origr, rptct, rptval, clrfln, cxmsg, biterr, intmsg,
        public  rtpos, erpos, rppos, stpos, nppos, rprpos, nrtpos, sppos,
        public  kbpos, perpos, frpos, prtasz, prtscr, prtfn
        public  diskio, locfil, strlen, strcat, strcpy, fparse
        include mssdef.h

rptmin  equ     3               ; At least 3 of same char in a row.

; equates for screen positioning
scrfln  equ     0316H           ; Place for file name.
scrkb   equ     0416H           ; Place for percent transferred.
scrper  equ     0516H           ; Place for Kbytes transferred.
scrst   equ     0616H           ; Place for status.
scrnp   equ     0816H           ; Place for number of packets.
scrnrt  equ     0916H           ; Place for number of retries.
screrr  equ     0A16H           ; Place for error msgs.
scrhi   equ     0B16H           ; Err when 8th bit is on.
scrfr   equ     0B16H           ; Rename file.
scrint  equ     0B16H           ; Acknowledge interrupt. [20b]
scrsp   equ     0C00H           ; Place for send packet.
scrrp   equ     0E00H           ; Place for receive packet.
scrrpr  equ     1100H           ; Prompt when Kermit ends.

datas   segment public 'datas'
        extrn   data:byte, flags:byte, trans:byte, pack:byte, hierr:byte
        extrn   filtst:byte

outlin  db      cr,lf,cr,lf
        db      cr,lf,'           File name:'
        db      cr,lf,'  KBytes transferred:'
        db      cr,lf
        db      cr,lf
        db      cr,lf
        db      cr,lf,'   Number of packets:'
        db      cr,lf,'   Number of retries:'
        db      cr,lf,'          Last error: None'
        db      cr,lf,'        Last warning: None'
        db      '$'

ermes3  db      '?unable to open file for sending $'
ermes4  db      '?Unable to make unique name$'
erms10  db      '?Unable to receive data$'
erms11  db      '?Disk full$'
erms12  db      '?Unable to create file $'
erms17  db      'Record length exceeds size of buffer$'
infms5  db      'Renaming file to $'
infms7  db      'File interrupt$'
infms8  db      'File group interrupt$'
hibit   db      'Warning - Non Ascii char$'
asmsg   db      '  as  $'
crlf    db      cr,lf,'$'
printer db      'PRN',0                 ; [jrd]
                                ; spchar2 =  $&#@!%'()-{}_^ ~`
spchar2 db      '$', '&', '#', '@', '!', '%', 25H, '(', ')', '-', '{', '}'
        db      '_', 5eh, '~', '`'
spc2len equ     $-spchar2
next    db      0FFH            ; No next character just yet.
rptval  db      0               ; Repeated character.
rptct   db      1               ; Number of times it's repeated.
rptq    db      drpt            ; Repeat prefix.
origr   db      drpt            ; Original repeat prefix.
chrcnt  dw      ?               ; Number of chars in the file buffer.
outpnt  dw      ?               ; Position in packet.
bufpnt  dw      ?               ; Position in file buffer.
ofilsz  dw      0               ; Double word original file size (in bytes.)
        dw      0
tfilsz  dw      0               ; Bytes transferred.
        dw      0
oldper  dw      ?               ; old percentage
oldkbt  dw      ?               ; old KB transferred.
wrpmsg  db      ?               ; non-zero if we wrote percent message
onek    dw      1024
onehun  dw      100
denom   dw      0
permsg  db      cr,' Percent transferred:$'
cxzhlp  db      '^X cancels file, ^Z cancels batch'
        db      ', ^E aborts protocol'
        db      ', ^C quits'
        db      ', Return retries'
        db      '$'
templp  db      65 dup (?)      ; temp for local path part [jrd]
templf  db      14 dup (?)      ; temp for local filename part [jrd]
temprp  db      65 dup (?)      ; temp for remote path part [jrd]
temprf  db      14 dup (?)      ; temp for remote filename part [jrd]
filbuf  db      65 dup(?)       ; Character buffer. [jrd]
locfil  db      65 dup (?)      ; local filename for receive and get [jrd]
cptio   filest  <>              ; DOS 2.0 file structure for logging. [jrd]
diskio  filest  <>              ; ditto, for ordinary file transfers. [jrd]
buff    db      buffsz DUP(?)   ; Use as our DTA.
decbuf  db      decsiz DUP(?)   ; For decoding incoming data.
unum    dw      ?               ; unique filename generation number
havdot  db      ?               ; dot-found status in verify
datas   ends

code    segment public
        extrn   spack:near, cmblnk:near, locate:near, nout:near
        extrn   putmod:near, poscur:near, clearl:near, isfile:near
        assume  cs:code,ds:datas

; Position cursor for an error message.

ERPOS   PROC    NEAR
        push dx                 ; save any preexisting message pointer [jrd]
        cmp flags.xflg,1        ; Packet header seen? [21c start]
        jne erp0                ; No, do as normal.
        mov dx,offset crlf
        mov ah,prstr
        int dos
        pop dx                  ; restore old pointer [jrd]
        ret
erp0:   mov dx,screrr
        call poscur
        pop dx                  ; restore old pointer [jrd]
        ret
ERPOS   ENDP

; Position cursor for number of retries message.

RTPOS   PROC    NEAR
        cmp flags.xflg,1        ; Packet header seen? [21c]
        jne rtp0                ; No, do as normal.
        ret
rtp0:   mov dx,scrnrt
        call poscur
        jmp clearl
RTPOS   ENDP

; Reassure user that we acknowledge his ^X/^Z.

INTMSG  PROC    NEAR
        cmp flags.xflg,0        ; Writing to screen?
        jne int1                ; Yes. Don't do anything.
        mov dx,scrint
        call poscur
        call clearl
        mov dx,offset infms7    ; File interrupted?
        cmp flags.cxzflg,'X'    ; Yes.
        je int0
        mov dx,offset infms8    ; File group interrupted.
int0:   mov ah,prstr
        int dos
int1:   ret
INTMSG  ENDP

; Print err message that found a non-standard-Ascii char in the file.

BITERR  PROC    NEAR
        cmp flags.remflg,0      ; remote mode?
        jne biter1              ; yes, no printing.
        push bx
        mov dx,scrhi
        call poscur
        call clearl
        mov ah,prstr
        mov dx,offset hibit
        int dos
        pop bx
biter1: ret
BITERR  ENDP

;  Clear out message about interrupted file.

CXMSG   PROC    NEAR
        cmp flags.xflg,0        ; Writing to screen?
        jne cxm0                ; Yes. Don't do anything.
        mov dx,scrint
        call poscur
        call clearl
cxm0:   ret
CXMSG   ENDP

;  Clear out the old filename on the screen.

CLRFLN  PROC    NEAR
        mov dx,scrfln
        call poscur
        call clearl             ; Clear to end of line. [19a]
        ret
CLRFLN  ENDP

; some random screen positioning functions
kbpos:  mov dx,scrkb            ; KBytes transferred.
        call poscur
        jmp clearl
perpos: mov dx,scrper           ; Percent transferred.
        call poscur
        jmp clearl
frpos:  mov dx,scrfr            ; Say renamed file.
        call poscur
        jmp clearl
stpos:  mov dx,scrst            ; Print status of file transfer.
        call poscur
        jmp clearl
nppos:  mov dx,scrnp            ; Number of packets sent.
        jmp poscur
rprpos: mov dx,scrrpr           ; Reprompt position.
        jmp poscur
nrtpos: mov dx,scrnrt           ; Number of retries.
        call poscur
        jmp clearl
sppos:  mov dx,scrsp            ; Send packet location.
        jmp poscur
rppos:  mov dx,scrrp            ; Receive packet location.
        jmp poscur



;       Initialize buffers and clear line.

INIT    PROC    NEAR
        call cmblnk
        call locate
        mov ah,prstr            ; Put statistics headers on the screen.
        mov dx,offset outlin
        int dos
        mov dx,offset cxzhlp
        call putmod             ; write mode line
        mov wrpmsg,0            ; haven't printed the messsage yet.
        call init1
        ret
INIT    ENDP

INIT1   PROC    NEAR
        mov chrcnt,buffsz              ; Number of chars left. [jrd]
        mov bufpnt,offset buff         ; Addr for beginning.
        mov hierr,0
        ret
INIT1   ENDP

;       Output the chars in a packet.

; Called with AX = size of the data, BX = address of source.

FILEIO  PROC    NEAR

ptchr:  mov cx,ax
        mov ax,offset outbuf    ;Where to put data when buffer gets full.
        jmp decode

; CX = Size of data, BX = Address of data, AX = Routine to call to
; dump data.

decode: push si
        push di
        push es
        push dx
        push ax
        mov ax,ds
        mov es,ax
        pop ax
        mov si,bx               ; Source of data.
        mov bx,ax               ; Coroutine to call.
        mov di,bufpnt           ; Destination of data.
        mov dh,0                ; assume no quote char
        cmp trans.ebquot,'N'    ; no quoting?
        je decod1               ; yes, keep going
        cmp trans.ebquot,'Y'    ; or not doing it?
        je decod1               ; yes, keep going
        mov dh,trans.ebquot     ; otherwise use quote char

decod1: mov rptct,0             ; Reset.
        mov rptval,0            ; Ditto.
        cmp cx,0                ; any more chars in source? [jrd]
        jg dcod11               ; g = yes. [jrd]
        jmp decod6              ; Else, we're through.
dcod11: cmp chrcnt,0            ; any space left in output buffer? [jrd]
        jg decod2               ; g = yes, continue filling [jrd]
        push cx
        push dx
        push bx
        call bx                 ; Output it if full.
         jmp decod5             ;  Error return if disk is full.
         nop
        pop bx
        pop dx
        pop cx
        mov di,bufpnt
decod2: cmp rptct,0             ; Doing a repeat?
        je dcod20               ; No, so go get a character.
        mov ah,0
        mov al,rptval           ; Get the character we're repeating.
        jmp decod4              ; And write it out to the file.
dcod20: lodsb                   ; Pick up a char.
        dec cx                  ; count number left [jrd]
        cmp rptq,0              ; Doing repeat quoting?
        je dcod21               ; Nope, skip this part.
        cmp al,rptq             ; Did we pick up the repeat quote char?
        jne dcod21              ; No, continue processing it.
        lodsb                   ; Get the size.
        dec cx                  ; Modify buffer count.
        sub al,20H              ; Was made printable.
        mov rptct,al            ; Remember how many repetitions.
        lodsb                   ; Get the char to repeat.
        dec cx                  ; Modify buffer count.
dcod21: mov ah,00H              ; Assume no 8-bit quote char. [21b start]
        cmp al,dh               ; This the 8-bit quot char?
        jne decod3
        lodsb                   ; Get the real character.
        dec cx                  ; Decrement # chars in packet
        mov ah,80H              ; Turn on 8-bit quot char flag. [21b end]
decod3: cmp al,trans.squote     ; Is it the quote char? [21b] [21c]
        jne decod4              ; If not proceed.
        lodsb                   ; Get the quoted character
        dec cx                  ; Decrement # of chars in packet.
        or ah,al                ; save parity (combine with prefix)
        and ah,80h              ; only parity
        and al,7FH              ; Turn off the parity bit.
        cmp al,trans.squote     ; Is it the quote char? [21c]
        je decod4               ; If so just go write it out.
        cmp al,dh               ; This the 8-bit quot char?
        je  decod4              ; If so, just go write it out
        cmp al,rptq             ; Is is the repeat quote character?
        je decod4               ; If so, just write it out.
                                ; fix bug in unquoting chars [jrd]
        cmp al,'?'              ; char less than '?' ? [jrd]
        jl decod4               ; l = yes; leave it intact [jrd]
        cmp al,'_'              ; char greater than '_' ? [jrd]
        jg decod4               ; g = yes; leave it alone. [jrd]
        add al,40H              ; Make it a control char again.
        and al,7FH              ; Modulo 128.
decod4: or al,ah                ; or in parity
        stosb                   ; store the character
        dec chrcnt              ; Decrement number of chars in dta. [jrd]
        dec rptct               ; Repeat counter.
        cmp rptct,0             ; Write out char again?
        jg dcod41
        jmp decod1              ; No, get next char.
dcod41: mov rptval,al           ; Save the char.
        jmp dcod11              ; and loop to next char.
decod5: pop bx
        pop dx                  ; dx is pushed twice (really)
        pop cx
        pop dx
        pop es
        pop di
        pop si
        ret
decod6: mov bufpnt,di           ; store address for next output char [jrd]
        pop dx
        pop es
        pop di
        pop si
        jmp rskp                ; Return successfully if done.

                                ; output the buffer, reset bufpnt and chrcnt

outbuf: cmp flags.xflg,1        ; Writing to screen? [21c]
        je outbf2               ; Yes, handle specially. [21c]
        push bx
        mov bx,diskio.handle    ; file handle [jrd]
        mov cx,buffsz           ; get full size of buffer [jrd]
        sub cx,chrcnt           ; minus space remaining = # to write [jrd]
        mov dx,offset buff      ; address of buffer [jrd]
        mov ah,write2           ; DOS 2.0 write [jrd]
        int dos                 ; Write the record.
        pop bx
        jc outbf0               ; c set means disk is full [jrd]
        cmp ax,cx               ; did we write all the bytes? [jrd]
        je outbf1               ; e = yes. [jrd]
        call abfil              ; Fix things up before aborting. [20d]
        ret
outbf0: call erpos
        mov ah,prstr            ; Tell about it.
        mov dx,offset erms11    ; Disk full error.
        int dos
        ret
outbf1: add tfilsz+2,cx         ; count received chars [jrd]
        adc tfilsz,0
        call kbpr               ; Print the kilobytes received.
        call perpr              ; Print the percent ('?' for now).
outb11: mov bufpnt,offset buff  ; Addr for beginning.
        mov chrcnt,buffsz       ; Buffer size. [jrd]
        jmp rskp
outbf2: mov cx,buffsz           ; Number of chars to write. [21c] [jrd]
        sub cx,chrcnt           ; minus # of unused in buffer
        jle outb11              ; none to print, don't try
        mov di,offset buff      ; Where they are. [21c]
        call prtscr             ; Output buffer to screen. [21c]
        jmp outb11              ; Reset counter & pointer. [21c]

;  Tidy up before aborting.     [20d]. Retidied by [jrd]
ABFIL   PROC    NEAR
        cmp flags.xflg,1        ; Writing to screen? [jrd]
        je abfil1               ; Yes don't delete "file". [jrd]
        mov bx,diskio.handle    ; get file handle [jrd]
        mov ah,close2           ; DOS 2.0 file close [jrd]
        int dos
        cmp flags.abfflg,1      ; Delete what got across or keep it?
        jne abfil1              ; Nope, keep it.
        push dx
        mov dx,offset diskio.string ; the full file name
        mov ah,del2             ; DOS 2.0 file delete. Delete it. [jrd]
        int dos
        pop dx
abfil1: mov bx,offset erms10    ; Text of message to send.
        call errpack            ; Send an error packet.
        ret
ABFIL   ENDP

; General routine for sending an error packet.  Register BX should
; point to the text of the message being sent in the packet. [20f]

ERRPACK PROC    NEAR
        mov di,offset data      ; Where to put the message.
        mov al,0
errp1:  mov ah,[bx]
        cmp ah,'$'              ; At end of message?
        je errp2
        inc al                  ; Remember number of chars in msg.
        mov [di],ah
        inc bx
        inc di
        jmp errp1
errp2:  mov ah,0
        mov pack.argbk1,ax
        mov ah,'E'              ; And send an error packet.
        call spack
         ret                    ; Return if succeed or fail.
        nop
        nop
        ret
ERRPACK ENDP

;       Get the chars from the file.

gtchr:  cmp flags.filflg,0      ; Is there anything in the DMA?
        jz gtchr0               ; Yup, proceed.
        mov ah,rptq
        mov origr,ah            ; Save repeat prefix here.
        mov rptct,1             ; Number of times char is repeated.
        mov rptval,0            ; Value of repeated char.
        call inbuf
         jmp gtchr1             ; No more chars, go return EOF.
         nop                    ; Make three bytes long.
gtchr0: mov bx,offset inbuf
        jmp encode
gtchr1: mov ax,0ffffh
        ret

; encode - writes data portion of kermit packet into filbuf.
; expects BX to contain the address of a routine to refill the buffer,
; chrcnt to be the # of chars in the buffer, trans.maxdat to contain
; the maximum size of the data packet, bufpnt to contain a pointer to
; the source of the characters.
; Returns: AX/ the number of characters actually written to the buffer.

encode: mov cl,trans.maxdat     ; Maximum packet size. [21b]
        mov ch,0
        mov di,offset filbuf    ; Where to put the data.
        mov si,bufpnt           ; pointer into source buffer
        mov dl,trans.rquote     ; send quote char
        mov dh,0                ; assume no 8-bit quoting
        cmp trans.ebquot,'N'    ; not doing 8-bit quoting
        je encod1
        cmp trans.ebquot,'Y'    ; or can but won't?
        je encod1
        mov dh,0ffh             ; remember we have to do it
encod1: cmp cx,0                ; any space left in output buffer? [jrd]
        jg encod2               ; g = yes. [jrd]
        sub di,offset filbuf
        mov ax,di
        mov bufpnt,si           ; update pointer into DMA.
        jmp rskp
encod2: cmp chrcnt,0            ; Any data in buffer?
        jg encod3               ; yes, skip over buffer refill.
        call bx                 ; Get another buffer full.
         jmp encod8
        mov si,bufpnt           ; update position in DMA.
        cmp chrcnt,0            ; no characters returned?
        jne encod3              ; Got some, keep going.
        jmp encod8              ; none, assume eof.
encod3: dec chrcnt              ; Decrement input count.
        lodsb
        cmp flags.eofcz,0       ; Is a control-z an end of file? [27b]
        je encd30               ; No, don't have to look for one. [27b]
        cmp al,'Z'-40H          ; Is this a control-Z? [27b]
        jne encd30              ; No, skip eof-processing. [27b]
        mov flags.eoflag,0FFH   ; Yes, set eof flag. [27b]
        mov flags.filflg,0FFH   ; No more input in buffer. [28]
        mov chrcnt,0            ; Ditto. [28]
        jmp encod8              ; Go set character count and return. [27b]
encd30: cmp rptq,0              ; Are we doing repeat prefixing?
        je encd3x               ; Nope, skip next part.
        cmp chrcnt,0            ; Are we on the last character?
        jle encd31              ; Yes, so there's no next character.
        cmp rptct,94            ; Max number that we can put in a byte.
        je encd31               ; Then that's it.
        mov ah,[si]             ; Get the next character.
        cmp al,ah               ; Is current char == next char?
        jne encd31
        inc rptct               ; Number of times char appears.
        mov rptval,al           ; Remember the character.
        jmp encod1              ; Keep checking for more.
encd31: cmp rptct,1             ; Were previous characters repeats?
        je encd3x               ; No, so just add this char.
        cmp rptct,rptmin        ; Are we within bounds for repeat prefixing?
        jge encd32              ; Yes, use repeat prefixing.
        mov al,rptct
        mov ah,0
        sub si,ax               ; Not enough characters to warrant it.
        mov rptval,0            ; Clear out this value.
        mov al,rptq
        mov origr,al            ; Save original repeat prefix.
        mov rptq,0              ; Pretend we're not doing the prefixing.
        mov al,rptct
        mov ah,0
        add chrcnt,ax           ; Adjust input buffer pointer.
        jmp encod1              ; Reprocess those characters.
encd32: push ax                 ; Do repeat prefixing - save data.
        mov al,rptq             ; Add repeat prefix char.
        stosb
        dec cx                  ; Account for it in buffer size.
        mov al,rptct            ; Get the repeat count.
        add al,20H              ; Make it printable.
        stosb                   ; Add to buffer.
        dec cx
        pop ax                  ; Get back the actual character.
        mov rptct,1             ; Reset repeat count.
        mov rptval,0            ; And this.
encd3x: cmp dh,0                ; are we doing 8-bit quoting?
        je encod4               ; no, forget this.
        test al,80h             ; parity on?
        je encod4               ; no, don't bother with this
        and al,7fh              ; turn off parity
        push ax                 ; save original char for a bit
        dec cx                  ; decrement # of chars left
        mov al,trans.ebquot     ; get quote char
        stosb                   ; save in buffer
        pop ax                  ; restore character
encod4: mov ah,al               ; save character
        and ah,80h              ; only parity
        and al,7fh              ; turn off parity in character
        cmp al,' '              ; Compare to a space.
        jl encod5               ; If less then its a control char.
        cmp al,del              ; Is the char a delete?
        jz encod5               ; Go quote it.
        cmp al,dl               ; Is it the quote char?
        je encod6               ; Yes - go add it. [21b start]
        cmp dh,0                ; are we doing 8-bit quoting?
        je encd41               ; no, don't translate it
        cmp al,trans.ebquot     ; Is it the 8-bit quote char?
        je encod6               ; Yes, just output with quote
encd41: cmp origr,0             ; Doing repeat prefixing?
        je encod7               ; No, don't check for quote char.
        cmp al,origr            ; Is this the repeat quote character.
        je encod6               ; Yes, then quote it.
        jmp short encod7        ; else don't quote it.
encod5: add al,40h              ; control char, uncontrollify
        and al,7fh
encod6: push ax                 ; save the char
        dec cx
        mov al,dl
        stosb
        pop ax
encod7: or al,ah                ; put parity back
        stosb
        dec cx                  ; Decrement output buffer counter. [jrd]
        cmp rptct,1             ; One occurence of this char?
        jne encd7x
        mov al,origr
        mov rptq,al             ; Restore repeat quote char.
        jmp encod1              ; Yes, so loop around for some more.
encd7x: dec rptct               ; Add another entry of this char.
        jmp encod1              ; With quoting and all.

encod8: sub di,offset filbuf
        or di,di
        je encod9               ; Nope.
        mov ax,di
        jmp rskp
encod9: mov ax,0FFFFH           ; Get a minus one.
        ret


inbuf:  cmp flags.eoflag,0      ; Have we reached the end?
        jz inbuf0
        ret                     ; Return if set.
inbuf0: push si
        push di
        push dx
        push bx
        push cx
        mov bx,offset buff      ; Set the r/w buffer pointer.
        mov bufpnt,bx
        mov bx,diskio.handle    ; get file handle [jrd]
        mov cx,buffsz           ; record size [jrd]
        mov dx,bufpnt           ; buffer address [jrd]
        mov ah,readf2           ; DOS 2.0 read a record. [jrd]
        int dos
        jc inbuf1               ; c = error, ie file not open. [jrd]
        or ax,ax                ; any bytes read?
        jne inbuf2              ; ne = yes (the number read)
inbuf1: mov flags.eoflag,0FFH   ; Set End-of-file. [jrd]
        mov flags.filflg,0ffh   ; Buffer empty.[jrd]
        mov chrcnt,0            ; zero bytes left in buffer
        pop cx
        pop bx
        pop dx
        pop di
        pop si
        ret
inbuf2: add tfilsz+2,ax         ; total the # bytes transferred so far. [jrd]
        adc tfilsz,0            ; it's a double word. [jrd]
        mov chrcnt,ax           ; Number of chars read from file.
        mov flags.filflg,0      ; Buffer not empty.
        push ax
        call kbpr               ; Print the kilobytes sent.
        call perpr              ; Print the percent sent.
        pop ax
        pop cx
        pop bx
        pop dx
        pop di
        pop si
        jmp rskp

nulref: mov chrcnt,0            ; No data to return.
        jmp rskp

nulr:   ret

; Print the number of Kilobytes transferred.

kbpr:   cmp flags.remflg,0      ; remote mode?
        jne kbpr1               ; yes, no printing.
        push bx
        push cx
        mov ax,tfilsz+2         ; low order word
        mov bx,tfilsz           ; high order word
        mov cl,10
        shr ax,cl               ; divide by 1024
        mov cl,6                ; high order moves 16-10 = 6 bits
        shl bx,cl
        or ax,bx
        pop cx
        pop bx
        cmp ax,oldkbt           ; is it the same?
        je kbpr1                ; yes, skip printing
        mov oldkbt,ax           ; save new # of kb
        push ax
        call kbpos              ; Postion the cursor.
        pop ax
        call nout               ; Print the number of KBytes transferred.
kbpr1:  ret

; Print the percent transferred.

perpr:  cmp flags.remflg,0      ; remote mode?
        je perpr1               ; e = no. allow printing. [jrd]
        ret                     ; skip printing in remote mode. [jrd]
perpr1: cmp ofilsz,0            ; high word of original file size > 0 ?
        jnz perpr3              ; nz = yes, use big file code.
        cmp ofilsz+2,0          ; anything here at all?
        jnz perpr2              ; nz = yes, use small file code.
        ret                     ; otherwise, quit now.
perpr2: push dx                 ; case for files < 64 Kb.
        push cx
        mov ax,ofilsz+2         ; original size (low word)
        mov denom,ax
        mov dx,tfilsz           ; transferred size times 256 in [dx,ax]
        mov ax,tfilsz+2
        mov dh,dl               ; whole value multilpied by 256
        mov dl,ah
        mov ah,al
        mov al,0
        div denom               ; (256*xfer)/orig. ax = quot, dx = remainder
        mul onehun              ; multiply quotient above by one hundred
        mov al,ah               ; divide result (ax) by 256
        mov ah,0                ; percentage is in ax
        jmp perpr4              ; finish in common code
perpr3: push dx                 ; case for file size > 64 KB.
        push cx
        mov ax,ofilsz+2         ; original file size low order word
        shr ax,1                ; divide by 2
        mov al,ah               ; divide again by 256 for total of 512
        mov ah,0                ; clear ah
        mov dx,ofilsz           ; high order word
        mov cl,7                ; work with lower order byte
        shl dx,cl               ; divide it by 2 also.
        or ax,dx                ; paste together the two parts into ax.
        mov denom,ax            ; denom = original size divided by 512
        mov dx,tfilsz           ; high order word of transferred size
        mov ax,tfilsz+2         ; low order word
        div denom               ; xfer/(512*orig). quot (in ax), rem (in dx)
        mul onehun              ; times 100 for 512 * precentage, in ax
        mov cl,9                ; divide ax by 512
        shr ax,cl               ; final percentage, in ax
perpr4: pop cx
        pop dx
        cmp ax,oldper           ; same as it was before?
        je perpr7               ; yes, don't bother printing.
        mov oldper,ax           ; remember this for next time
        cmp wrpmsg,0            ; did we write the percentage message?
        jne perpr5              ; yes, skip this part
        push ax
        call perpos             ; position cursor
        mov dx,offset permsg
        mov ah,prstr
        int dos                 ; write out message
        pop ax
        mov wrpmsg,1            ; init flag so we don't do it again
perpr5: push ax
        call perpos             ; Position the cursor.
        pop ax
        cmp ax,onehun           ; > 100% ?
        jle perpr6              ; no, accept it
        mov ax,onehun           ; else just use 100
perpr6: call nout
        mov dl,'%'              ; Load a percent sign.
        mov ah,conout           ; Print the character.
        int dos
perpr7: ret
                                ; GETFIL, called only by send code
getfil: mov flags.filflg,0ffh   ; Say nothing is in the buffer.
        mov flags.eoflag,0      ; Not the end of file.
        mov dx,offset diskio.dta ; data transfer address [jrd]
        mov ah,setdma           ; set disk transfer address [jrd]
        int dos                 ; do it [jrd]
        mov cx,0                ; attributes: find only normal files
        mov dx,offset diskio.string ; filename string (may have wild cards)
        mov ah,first2           ; DOS 2.0 search for first [jrd]
        int dos                 ; get file's characteristics [jrd]
        pushf                   ; save c flag
        mov ah,setdma           ; reset dta address [jrd]
        mov dx,offset buff      ; restore dta [jrd]
        int dos                 ; [jrd]
        popf                    ; restore status of search for first
        jnc getfi1              ; nc = ok so far
        ret                     ; else take error exit
getfi1:
        mov dx,offset diskio.string     ; original file spec (may be wild)
        mov di,offset templp    ; place for path part
        mov si,offset templf    ; place for filename part
        call fparse             ; split them
        mov si,offset diskio.name ; current filename from DOS
        call strcat             ; local path + diskio.name
        mov si,di               ; make it a source
        mov di,offset diskio.string ; new destination
        call strcpy             ; new string = old path + DOS's filename
        mov ah,open2            ; DOS 2.0 file open [jrd]
        mov al,0                ; open readonly [jrd]
        mov dx,offset diskio.string     ; filename string [jrd]
        int dos
        jnc getfi2              ; nc = opened the file
        ret                     ; else take error return
getfi2: mov diskio.handle,ax    ; save file handle [jrd]
        mov ax,diskio.sizehi    ; get file size (high order word) [jrd]
        mov ofilsz,ax           ; new form
        mov ax,diskio.sizelo    ; low order word [jrd]
        mov ofilsz+2,ax         ; new form
        mov tfilsz,0            ; Set bytes sent to zero.
        mov tfilsz+2,0
        mov oldkbt,-1
        mov oldper,-1
        cmp ofilsz,0            ; Null file?
        jne getfl0              ; Nope.
        cmp ofilsz+2,0          ; Null file?
        jne getfl0              ; Nope.
        mov flags.eoflag,0FFH   ; yes. Set EOF.
getfl0: jmp rskp

; GTNFIL called by send code to get next file. Rewritten by [jrd]

gtnfil: cmp flags.cxzflg,'Z'    ; Did we have a ^Z? [20b]
        jne gtn1                ; ne = no; else we're done sending files.[jrd]
        ret                     ; take failure exit

gtn1:   mov flags.filflg,0ffh   ; Nothing in the DMA.
        mov flags.eoflag,0      ; Not the end of file.
        mov dx,offset diskio.dta ; point at dta [jrd]
        mov ah,setdma           ; set the dta address [jrd]
        clc                     ; clear c flag
        int dos
        mov ah,next2            ; DOS 2.0 search for next [jrd]
        int dos
        pushf                   ; save carry flag
        mov ah,setdma           ; restore dta [jrd]
        mov dx,offset buff      ; [jrd]
        int dos
        popf                    ; recover carry flag
        jc  gtn4                ; carry set means no more files found [jrd]
        push es
        mov dx,ds
        mov es,dx
        mov di,offset templp    ; place for path part
        mov si,offset templf    ; place for filename part
        mov dx,offset diskio.string     ; current full filename
        call fparse             ; split them
        mov si,offset diskio.name ; new filename part from DOS
        call strcat             ; rejoin path and new filename
        mov si,di               ; new source
        mov di,offset diskio.string     ; place for whole new name
        call strcpy             ; copy new string
        mov dx,offset diskio.string     ; address of new string
        pop es
        clc
        mov dx,offset diskio.string     ; address of new string
        mov ah,open2            ; DOS 2.0 file open [jrd]
        mov al,0                ; open readonly [jrd]
        int dos
        jc gtn4                 ; c = could not open the file

        mov diskio.handle,ax    ; save file handle [jrd]
        push dx
        mov dx,diskio.sizehi    ; get file size (high order word) [jrd]
        mov ofilsz,dx           ; save as original file size
        mov ax,diskio.sizelo    ; low order word [jrd]
        mov ofilsz+2,ax
        pop dx
        mov tfilsz,0            ; Set bytes sent to zero.
        mov tfilsz+2,0
        mov oldkbt,-1
        mov oldper,-1
        cmp ofilsz,0            ; Null file?
        jne gtn2                ; Nope.
        cmp ofilsz+2,0          ; Null file?
        jne gtn2                ; Nope.
        mov flags.eoflag,0FFH   ; Set EOF.
gtn2:   jmp rskp                ; set success condition
gtn4:   ret                     ; set failure condition


; Get the file name from the data portion of the F packet or from locally
; specified override filename (in locfil).
; prints the filename, handles any manipulation of the filename
; necessary, including changing the name to prevent collisions.
; Called by READ (receive a file, at rfil32).

gofil:  cmp flags.xflg,1        ; Remote command?
        jne gofil1              ; ne = No. Command was given locally.
        jmp gofi20              ; Yes. so skip this stuff.
gofil1: mov ax,ds               ; set es reg to datas segment for di & si
        mov es,ax               ;
        cmp flags.destflg,2     ; receiving to the screen?
        je gofi1a               ; e = yes, don't display filename
        cmp flags.remflg,0      ; are we a server?
        jne gofi1a              ; ne = yes, don't display filename
        call prtfn              ; display the packet's filename
gofi1a: cmp flags.nmoflg,0      ; Overriding name from other side?
        je gofil4               ; e = No. get the other end's filename. [jrd]
        mov ax,offset locfil    ; get local override filename
        call isfile             ; does it exist?
        jc gofil3               ; carry set = no such file
        test byte ptr filtst.dta+21,10H ; subdirectory name?
        jnz gofi1b              ; nz = yes
        test byte ptr filtst.dta+21,0fh ; r/o, hidden, system, vol label?
        jz gofil3               ; z = no
        jmp gofi18              ; nz = yes. Complain and don't transfer file.
gofi1b: mov dx,offset locfil    ; locfil is a subdirectory name
        call strlen             ; get its length w/o terminator
        dec cx                  ; examine last char
        push bx                 ; save bx
        cmp cx,0
        jl gofil2
        mov bx,cx
        add bx,dx
        cmp byte ptr [bx],'\'   ; ends in slash?
        je gofil2               ; e = yes
        cmp byte ptr [bx],'/'   ; maybe this kind of slash?
        je gofil2               ; e = yes
        mov byte ptr [bx + 1],'\'       ; no slash yet. use this one
        mov byte ptr [bx + 2],0 ; plant new terminator
gofil2: pop bx

gofil3: mov di,offset templp    ; local path
        mov si,offset templf    ; local filename
        mov dx,offset locfil    ; local string
        call fparse             ; split local string
        mov di,offset temprp    ; remote path
        mov si,offset temprf    ; remote file
        mov dx,offset data      ; remote string
        push bx                 ; guard against excessively long filenames
        mov bx,offset data
        mov byte ptr [bx+64],0  ; force filename to be <= 64 text chars
        pop bx
        call fparse             ; split remote string
        mov si,offset templp    ; copy local path to
        mov di,offset data      ;       final filename
        call strcpy             ; do the copy
gofi3a: mov si,offset templf    ; assume using local file name
        cmp byte ptr templf,0   ; local file name given?
        jne gofi3b              ; ne = yes
        mov si,offset temprf    ; else use remote file name
gofi3b: call strcat             ; do the append
                                ; now offset data holds the new filename
                                ;
gofil4: mov ax,offset data      ; assume we're writing to disk
        push bx                 ; guard against excessively long filenames
        mov bx,offset data
        mov byte ptr [bx+64],0  ; force filename to be <= 64 text chars
        pop bx
        cmp flags.destflg,0     ; writing to printer?  [jrd]
        jne gofil5              ; ne = no, go on
        mov ax,offset printer   ; this is filename now
        jmp gofi16              ; rejoin code later...

                                ; recheck legality of filename in 'data'
gofil5: mov di,offset temprp    ; remote path
        mov si,offset temprf    ; remote file
        mov dx,offset data      ; remote string
        call strlen             ; get original size
        push cx                 ; remember it
        call fparse             ; further massage filename
        push si                 ; put pieces back together
        mov si,di               ; get path part first
        mov di,dx               ; set destination
        call strcpy             ; copy in path part
        pop si                  ; recover (new) filename
        call strcat             ; append it.
        call strlen             ; see if we chopped out something
        pop si                  ; get original length (from push cx above)
        cmp cx,si               ; same size?
        je gofil6               ; e = yes
        mov flags.nmoflg,0ffh   ; say that we have a replacement name
gofil6: call verfil             ; verify each char in 'data' [jrd]
                                ; filename is now in 'data', all converted
gofil9: cmp flags.remflg,0      ; remote mode?
        jne gofi10              ; ne = yes, don't print it.
        cmp flags.nmoflg,0      ; using local override name?
        je gofi10               ; e = no
        mov ah,prstr
        mov dx,offset asmsg     ; print " as "
        int dos
        mov dx,offset data      ; plus the local filename
        call strlen             ; computes length in cx for prtscr
        mov di,dx
        call prtscr

gofi10: mov ax,offset data      ; point to name
        cmp flags.flwflg,0      ; Is file warning on?
        je gofi16               ; e = no, just proceed
        call isfile             ; does it exist?
        mov ax,offset data      ; reload ptr in case...
        jc gofi16               ; carry set = no, just proceed
        call unique             ; generate unique name
        jc gofi14               ; could not generate a unique name
        cmp flags.remflg,0      ; remote mode?
        jne gofi13              ; yes, skip printing
        push ax                 ; save unique name again
        call frpos              ; Position cursor.
        mov ah,prstr            ; Inform the user we are renaming the file.
        mov dx,offset infms5
        int dos
        pop ax                  ; get name back into ax again
        mov dx,ax               ; print current filename
        call strlen             ; get cx = byte count
        mov di,dx               ; pointer for prtscr
        call prtscr             ; display filename
gofi13: jmp gofi16              ; and go handle file

gofi14: cmp flags.remflg,0      ; remote mode?
        jne gofi15              ; yes, no printing
        call erpos              ; Position cursor.
        mov ah,prstr            ; Tell the user that we can't rename it.
        mov dx,offset ermes4
        int dos
gofi15: mov bx,dx               ; Tell host can't rename.  [20f]
        call errpack            ; Send error packet before abort. [20f]
        ret

gofi16: mov si,ax               ; pointer to (maybe new) name
        mov di,offset diskio.string     ; filename, used in open [jrd]
        call strcpy             ; copy name to diskio.string
        mov ofilsz,0            ; original file size is unknown.
        mov ofilsz+2,0          ; double word
        mov tfilsz,0            ; Set bytes received to zero.
        mov tfilsz+2,0
        mov oldkbt,-1
        mov oldper,-1
        mov dx,offset diskio.string ; filename [jrd]
        call isfile             ; check for read-only/hidden/system/vol-label
        test byte ptr filtst.dta+21,1fh ; the no-no file attributes.
        jnz gofi18              ; nz = shouldn't write over one of these.
        mov ah,creat2           ; DOS 2.0 create file [jrd]
        mov cx,0                ; attributes bits [jrd]
        int dos
        mov diskio.handle,ax    ; file handle [jrd]
        jc gofi18               ; carry set means can't open (disk full) [jrd]
        jmp rskp

gofi18: cmp flags.remflg,0      ; remote mode?
        jne gofi19              ; yes, don't try printing
        call erpos              ; Position cursor.
        mov ah,prstr            ; tell the user.
        mov dx,offset erms12
        int dos
        mov dx,offset diskio.string     ; print offending name
        call strlen                     ; get cx = byte count
        mov di,dx                       ; pointer for prtscr
        call prtscr                     ; display filename
gofi19: mov dx,offset erms12            ; reset error message for packet
        mov bx,dx
        call errpack            ; Send an error packet.
        ret
gofi20:  cmp pack.argbk1,0      ; Any data in "X" packet? [21c start]
        je gofi21               ; Nothing to print.
        mov ah,prstr
        mov dx,offset crlf
        int dos
        int dos                 ; Print another crlf.
        mov di,offset data      ; Where data is.
        mov cx,pack.argbk1      ; How much data we have.
        call prtscr             ; Print it on the screen.
gofi21: mov ah,prstr
        mov dx,offset crlf
        int dos
        jmp rskp                ; And done. [21c end]

FILEIO  ENDP

; Given incoming filename in 'data'.  Verify that each char is legal
; (if not change it to an "X"), force max of three chars after a period (dot).
; Upgraded again on 27 Dec 1985. [jrd]

VERFIL  PROC    NEAR
        push es                 ; verify each char in 'data'
        push cx
        mov ax,ds
        mov es,ax
        mov havdot,0            ; say no dot found in name yet
        cld
        mov si,offset data      ; source & destination of filename text.
verfi1: lodsb                   ; get a byte of name from si
        and al,7fH              ; strip any eight bit. [jrd]
        cmp al,0                ; end of name?
        je verfi5               ; e = yes
        cmp al,'.'              ; a dot? [jrd]
        jne verfi2              ; ne = no.
        cmp havdot,0            ; have one dot already?
        jne verfi3              ; ne = yes, change to X
        mov byte ptr [si+3],0   ; forceably end filename after 3 char ext.
        mov havdot,1            ; say have a dot now.
        jmp verfi4              ; continue
verfi2: cmp al,':'              ; colon?
        je verfi4
        cmp al,'\'              ; path separator?
        je verfi4
        cmp al,'/'              ; or this kind?
        je verfi4
        cmp al,'0'
        jb verfi3               ; See if it's a legal char < '0'.
        cmp al,'9'
        jbe verfi4              ; It's between 0-9 so it's OK.
        cmp al,'A'
        jb verfi3               ; Check for a legal punctuation char
        cmp al,'Z'
        jbe verfi4              ; It's A-Z so it's OK.
        cmp al,'a'
        jb verfi3               ; Check for a legal punctuation char
        cmp al,'z'
        ja verfi3
        and al,5FH              ; It's a-z, capitalize.
        jmp verfi4              ; continue with no change

verfi3: push di                 ; special char. Is it on the list?
        mov di,offset spchar2   ; list of acceptable special chars
        mov cx,spc2len
        repne scasb             ; Search string for input char.
        cmp cx,0                ; at end of table?
        pop di
        jne verfi4              ; ne = in table, return it
        mov al,'X'              ; else illegal, replace with "X".
        mov flags.nmoflg,0FFH   ; and say we have a replacement filename
verfi4: mov [si-1],al           ; update name
        jmp verfi1              ; loop thru rest of name
verfi5: mov al,0                ; null for end
        mov [si-1],al           ; make sure it's terminated
        pop cx
        pop es
        ret
VERFIL  ENDP

; find a unique filename...     Upgraded by [jrd]
; Enter with a pointer to a (null-terminated) filename in ax.
; Return with same pointer but with a new name (or old if failure).
; Success = carry clear; failure = carry set.
; The idea is to pad out the main name part (8 chars) with ascii zeros and
; then change the last chars successively to a 1, 2, etc. until
; a unique name is found. All registers are preserved.

unique  proc    near
        push bx
        push cx
        push dx
        push si
        push di
        push es
        push ax                         ; save address of source string
        mov dx,ds                       ; make es use ds segment
        mov es,dx
        mov dx,ax                       ; point at original filename string
        mov di,offset templp            ; place for path
        mov si,offset templf            ; place for filename
        call fparse                     ; separate path (di) and filename (si)
        mov dx,di                       ; point at path part
        call strlen                     ; put length in cx
        mov si,ax                       ; point to original string
        add si,cx                       ; point to filename part [jrd]
        mov di,offset templf            ; destination is temporary location
        mov cx,0                        ; a counter
        cld                             ; set direction to be forward [jrd]
uniq1:  lodsb                           ; get a byte
        cmp     al,'.'                  ; have a dot?
        je      uniq2                   ; e = yes.
        cmp     al,0                    ; maybe null at end?
        jne     uniq3                   ; ne = no. continue loop

uniq2:  cmp     cl,8                    ; have we copied any chars before dot?
        jge     uniq3                   ; ge = all 8 [jrd]
        mov     byte ptr [di],'0'       ; avoid clobbers; pad with 0's [jrd]
        inc     di                      ; and count the output chars
        inc     cl                      ; and this counter too
        jmp     uniq2                   ; continue until filled 8 slots
uniq3:  inc     cl                      ; cl = # char in destination
        stosb                           ; store the char [jrd]
        cmp     al,0                    ; null at end?
        jne     uniq1                   ; ne = no, continue copying

        mov     di,offset templf
        add     di,7                    ; address of last name char
        mov     byte ptr [di],'1'       ; put '1' in last name char
        mov     unum,1                  ; start with this generation digit

uniq4:  mov     di,offset filbuf        ; build a temporary full filename
        mov     si,offset templp        ; path part
        call    strcpy                  ; copy that much
        mov     si,offset templf        ; get rebuilt filename part
        call    strcat                  ; paste that to the end
        mov     ax,offset filbuf        ; point to full name
        call    isfile                  ; does it exist?
        jc      uniq6                   ; c = no, succeed now

        inc     unum                    ; move to next generation
        mov     di,offset templf        ; position for characters
        add     di,7                    ; point to last name char
        mov     cx,7                    ; max # of digits to play with
        mov     bx,10                   ; divisor (16 bits)
        mov     ax,unum                 ; low order part of generation #
uniq5:  mov     dx,0                    ; high order part of generation #
        div     bx                      ; compute digit (unum / 10)
        add     dl,'0'                  ; make remainder part printable
        mov     byte ptr [di],dl        ; put into right place
        cmp     ax,0                    ; any more to do? (quotient nonzero)
        jz      uniq4                   ; z = no, try this name
        dec     di                      ; else decrement char position
        loop    uniq5                   ;   and keep making a number
        stc                             ; failure: set carry, keep old name
        jmp short uniq7                 ;   and exit

uniq6:  pop di                          ; address of original filename
        push ax                         ; save for exit clean up
        mov si,offset filbuf
        call strcpy                     ; copy new filename over old
        clc                             ; success: clear carry flag
uniq7:  pop ax
        pop es
        pop di
        pop si
        pop dx
        pop cx
        pop bx
        ret
unique  endp


;       [jrd]
; strlen -- computes the length, excluding the terminator, of an asciiz
;       string. Input: dx = offset of the string.
;               Output: cx = the byte count.
;       Normal 'ret' return. All registers except cx are preserved.
;
STRLEN  PROC    NEAR
        push si
        push es
        push ax
        mov ax,ds               ; ensure proper segment address is used
        mov es,ax
        mov si,dx
        mov cx,0                ; byte count
        cld                     ; set direction to be forward
strln1: lodsb                   ; get a byte
        inc cx                  ; count chars looked at
        cmp al,0                ; is it the terminator?
        jne strln1              ; ne = no, so keep looking
        dec cx                  ; don't count the terminator
        pop ax
        pop es
        pop si
        ret
STRLEN  ENDP

;       [jrd]
; strcat -- concatenates asciiz string 2 to the end of asciiz string 1.
;       offset of string 1 is expected to be in ds:di. input & output
;       offset of string 2 is expected to be in ds:si. input only (unchanged)
;       Preserves all registers. No error returns, returns normally via ret.
;
STRCAT  PROC    NEAR
        push di                 ; save work registers
        push si
        push es
        push dx
        push cx
        push ax
        mov ax,ds               ; get data segment value
        mov es,ax               ; set es to ds for implied es:di usage
        mov dx,di
        call strlen             ; get length (w/o terminator) of dest string
        add di,cx               ; address of first terminator
        mov dx,si               ; start offset of source string
        call strlen             ; find its length too (in cx)
        inc cx                  ; include its terminator in the count
        rep movsb               ; copy source string to end of output string
        pop ax
        pop cx
        pop dx
        pop es
        pop si
        pop di
        ret
STRCAT  ENDP

;       [jrd]
; strcpy -- copies asciiz string pointed to by ds:si into area pointed to by
;       ds:di. Returns via ret. All registers are preserved.
;
STRCPY  PROC    NEAR
        mov byte ptr [di],0     ; clear destination string
        call strcat             ; let strcat do the real work
        ret
STRCPY  ENDP

;       [jrd]
; fparse -- separate the drive:path part from the filename.ext part of an
;       asciiz string. Characters separating parts are  \ or / or :
;       Inputs: asciiz input full filename string offset in ds:dx
;               asciiz path offset in ds:di
;               asciiz filename offset in ds:si
;       Outputs: the above strings in the indicated spots.
;       Strategy is simple. Reverse scan input string until one of the
;       three separators is encountered and then cleave at that point.
;       Simple filename construction restrictions added 30 Dec 1985;
;       to wit: mainname limited to 8 chars or less,
;       extension field limited to 3 chars or less and is found by searching
;       for first occurence of a dot in the filename field. Thus the whole
;       filename part is restricted to 12 (8+dot+3) chars plus a null.
;       All registers are preserved. Return is always via ret.
;       (Microsoft should have written this for DOS 2.x et seq.)

FPARSE  PROC    NEAR
        push cx                 ; local counter
        push ax                 ; local work area
        push es                 ; implied segment register for di
        push di                 ; offset of path part of output
        push si                 ; offset of file name part of output
        mov ax,ds               ; get data segment value
        mov es,ax               ; set es to ds for implied es:di usage
        mov byte ptr [si],0     ; clear outputs
        mov byte ptr [di],0

        push si                 ; save original file name address
        mov si,dx               ; get original string address
        call strcpy             ; copy string to original di
        call strlen             ; find its length (w/o terminator), in cx
        mov si,di               ; address of string start
        add si,cx
        dec si                  ; si = address of last non-null char
        cmp cx,0                ; if null skip the path scan
        je fpars5
                                ; now find last path char, if any.
                                ; start at the end of input string
        std                     ; set direction to be backward
fpars4: lodsb                   ; get a byte (dec's si afterward)
        cmp al,'\'              ; is it this kind of slash ('\')?
        je fpars6               ; e = yes
        cmp al,'/'              ; or this kind ('/')?
        je fpars6               ; e = yes
        cmp al,':'              ; or even the drive terminator?
        je fpars6               ; e = yes
        loop fpars4             ; else keep looking until cx == 0
                                ; si is at beginning of file name
fpars5: dec si                  ; dec for inc below
fpars6: inc si
        inc si                  ; si now points at first filename char
                                ; cx holds number of path chars
                                ; get original file name address (si)
        pop di                  ; and make it place to copy filename.
        cld                     ; reset direction to be forward
        mov ax,si               ; ax holds filename address for a while.
        push dx
        mov dx,si               ; place where strlen wants string pointer.
        call strlen             ; get length of filename part into cx
        pop dx
        cmp cx,0                ; any chars to look at?
        jz fpar7a               ; z = no
fpars7: cmp byte ptr [si],'.'   ; look for a dot in filename
        je fpars8               ; e = found one
        inc si                  ; look at next filename char
        loop fpars7             ; keep looking until cx = zero
fpar7a: mov si,ax               ; no dot. recover starting address
        mov byte ptr [si+8],0   ; forcably truncate mainname to 8 chars
        call strcpy             ; copy this part to filename field and exit
        jmp fparsx              ; exit.
fpars8: mov byte ptr [si+4],0   ; plant terminator after dot + 3 ext chars.
        mov cx,si
        sub cx,ax               ; cx now = number of chars in mainname field
        cmp cx,9                ; more than 8?
        jb fpars9               ; b = no, we're safe.
        mov cx,8                ; limit ourselves to 8 chars in mainname
fpars9: push si                 ; remember address of dot and extension
        mov si,ax               ; point to start of input filename
        rep movsb               ; copy cx chars from si to di (output)
        mov byte ptr [di],0     ; plant terminator where dot goes
        pop si                  ; source = dot and extension address
        call strcat             ; append the dot & ext to the filename field.
fparsx: mov si,ax               ; recover start of filename in input string.
        mov byte ptr [si],0     ; terminate path field
        pop si
        pop di
        pop es
        pop ax
        pop cx
        ret
FPARSE  ENDP

; Print filename in offset data. Shortened by [jrd].
PRTFN   PROC    NEAR
        push ax                 ; saves for messy clrfln routine
        push bx
        push cx
        push dx
        push di
        call clrfln             ; Position cursor & blank out the line.
        mov dx,offset data
        call strlen             ; compute length of asciiz string in cx
        mov di,dx               ; where prtscr wants its string
        call prtscr
        pop di
        pop dx
        pop cx
        pop bx
        pop ax
        ret
PRTFN   ENDP


; Print string to screen from offset ds:di for # bytes given in cx,
; regardless of $'s.  All registers are preserved.              [jrd]

PRTSCR  PROC    NEAR
        push ax
        push cx
        push di
        push si
        push es
        mov ax,ds
        mov es,ax                       ; set ds segment for es
        mov si,di                       ; source of text
        cmp cx,0
        jle prtsc2                      ; when to stop
prtsc1: lodsb                           ; get a byte into al
        mov dl,al                       ; where DOS expects it
        mov ah,conout                   ; console i/o
        int dos                         ; print the char
        loop prtsc1                     ; and do another
prtsc2: pop es
        pop si
        pop di
        pop cx
        pop ax
        ret
PRTSCR  ENDP

; Print to screen asciiz string given in ds:dx. Everything preserved. [jrd]
PRTASZ  PROC    NEAR
        push cx
        push di
        call strlen                     ; get length of asciiz string
        mov di,dx                       ; where prtscr looks
        call prtscr                     ; print counted string
        pop di
        pop cx
        ret
PRTASZ  ENDP


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
        pop bp
        add bp,3
        push bp
        ret
RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end


