Metropoli BBS
VIEWER: nocrlf.asm MODE: TEXT (ASCII)
;**********************************************************************
; NOCRLF.ASM
;
; By Mark Thomas, released to the public domain.
;
;	This little utility will fix binary files which have been
;	corrupted by being accidentally transfered in ASCII mode.
;
; Revision History:
; v0.01 05-09-1993	Written by Mark Thomas and Tali Streit.
;
; v1.00 16-11-1995	Added multiple files support on the command line.
;			(Thanks to David Kirschbaum's args.asm).
;			Added wildcard support on the command line.
;			Added full error checking and reporting.
;			Much tidying.
;
;
;**********************************************************************


BFR_SIZE	EQU 8192		; Size of input and output buffers.


_TEXT	SEGMENT PARA PUBLIC 'CODE'
	ASSUME	CS:_TEXT, DS:_TEXT, ES:_TEXT, SS:_TEXT
	ORG 0100h


Extrn	SetArgv:NEAR
Extrn	New:NEAR
Extrn	argc:WORD, argv:WORD


Main	PROC NEAR

	call	SetArgv 			; Get command line args.

	mov	ax,offset msgTitle
	call	PrintString

	cmp	argc,0				; No arguments, display usage.
	jne	HasArgs
	mov	ax,offset msgUsage
	call	PrintString
	jmp	Exit
HasArgs:

	mov	cx,BFR_SIZE			; Allocate input and output
	call	New				; buffers.
	mov	InBuffer,bx
	call	New
	mov	OutBuffer,bx

	mov	cx,256				; Allocate a buffer for the
	call	New				; output file name.
	mov	OutName,bx

		; Main Loop.
		; Processes each file in the command line argument array.

	mov	ax,argv
	inc	ax
	inc	ax				; Skip argv[0].
	mov	arg,ax


ProcessFiles:

	mov	ax,offset msgCRLF
	call	PrintString

	mov	bx,arg				; Get next command line
	mov	ax,[bx] 			; argument, all done when
	inc	bx				; NULL.
	inc	bx
	or	ax,ax
	jz	Exit

	mov	arg,bx
	mov	InName,ax

	call	PrintString			; Print the input filename.

	mov	dx,InName
	call	BfrReadInit			; Open the input file.
	jnc	@OpenOk

	mov	ax,offset msgFailInput
	call	PrintSkipMessage
	jmp	ProcessFiles

@OpenOk:

		; Create a temporary file in the same directory as the input
		; file.

	mov	si,InName
	mov	di,OutName
	call	GetOutName

	mov	dx,OutName			; Open the output file.
	call	BfrWriteInit
	jnc	@CreateOk

	mov	ax,offset msgFailOutput
	call	PrintSkipMessage

	jmp	@CreateFail

@CreateOk:
		; File Conversion Loop.

	mov	ChangeFlag,0

ReadLoop:
	cmp	BytesRead,0			; Continue until read buffer
	je	FileDone			; is empty.

	call	BfrRead
	jc	@IOFail

	cmp	BytesRead,0			; If the read emptied the
	je	NotCRLF 			; buffer then done.

	cmp	ax,0A0Dh			; Check for CR/LF.  (BfrRead
	jne	NotCRLF 			; returns next char. in AH).

	mov	ChangeFlag,1

	call	BfrRead 			; Read the LF character, it
	jc	@IOFail 			; will be written out.

NotCRLF:
	call	BfrWrite
	jnc	ReadLoop
@IOFail:

	mov	ax,offset msgIOFail
	call	PrintSkipMessage

        mov     bx,OutHandle
	mov	ah,03Eh
	int	21h			; DOS: Close File.
	;; jc	  @UnlinkError			; Don't care.

	mov	dx,OutName
	mov	ah,41h			; DOS: Unlink file.
	int	21h
	;; jc	  @UnlinkError			; Don't care.

@CreateFail:

	mov	bx,InHandle
	mov	ah,03Eh
	int	21h			; DOS: Close File.
	;; jc	  @UnlinkError			; Don't care.

	jmp	ProcessFiles

FileDone:
	call	BfrWriteDone			; Flush out the remainder of
	jc	@IOFail 			; output buffer.

	call	BfrReadDone

	cmp	ChangeFlag,0			; Were any changes made?
	jne	UpdateSource

	mov	dx,OutName			; No, delete the output file.
	mov	ah,41h			; DOS: Unlink file.
	int	21h
	;; jc	  @UnlinkError			; Don't care.

	mov	ax,offset msgNoChange
	call	PrintString

	jmp	ProcessFiles

UpdateSource:

		; Conversion has been successful.  Now delete the input file
		; and replace it by renaming the temporary file.  If the
		; rename fails the we abort the program to allow the user
		; to recover something.

	mov	dx,InName
	mov	ah,41h
	int	21h			; DOS: Unlink file.
	jc	@RenameFail

	mov	dx,OutName
	mov	di,InName
	mov	ah,56h
	int	21h			; DOS: Rename file.
	jc	@RenameFail

	mov	ax,offset msgOK 		; File has been successfully
	call	PrintString			; processed.

	jmp	ProcessFiles
@RenameFail:

	mov	ax,offset msgRenameFail
	call	PrintString
	mov	al,-1

Exit:
        mov     ah,04ch
	int	21h			; DOS: Terminate program.
Main	ENDP


BfrReadInit PROC NEAR
	; DX=Filename, returns CF=1 on error

	mov	ax,03D02h			; Open for RW.
	int	21h			; DOS: Open File.
	jc	briDone
	mov	InHandle,ax

	mov	bx,ax
	mov	ax,05700h
	int	21h			; DOS: Get file date/time
	jc	briError

	mov	InFileDate,dx
	mov	InFileTime,cx

	mov	ax,InBuffer
	mov	InIndex,ax
	mov	BytesRead,1
	call	BfrRead
	jnc	briDone

briError:
	mov	bx,InHandle
	mov	ah,03Eh 		; DOS: Close File.
	int	21h
	stc
briDone:

	ret
BfrReadInit ENDP


BfrRead PROC NEAR
	; Returns AL=character, AH next character.  CF=1 on error
	; AH is undefined if BytesRead==0.

	mov	si,InIndex

        lodsb
	dec	BytesRead		; *CF clear*
	jnz	NoRead

        push    ax
	mov	cx,BFR_SIZE
	mov	dx,InBuffer
	mov	si,dx
	mov	bx,InHandle
	mov	ah,03Fh
	int	21h			; DOS: Read from File.
	jc	brDone
	mov	BytesRead,ax		; *CF clear*
	pop	ax

NoRead:
	mov	ah,[si]
brDone:
	mov	InIndex,si
	ret
BfrRead ENDP


BfrReadDone PROC NEAR

	mov	bx,InHandle
	mov	ah,03Eh 		; DOS: Close File.
        int     21h

	ret
BfrReadDone ENDP


BfrWriteInit	PROC NEAR
	; DX=Filename, returns CF=1 on error

        mov     cx,0
	mov	ah,03Ch
	int	21h			; DOS: Create File.
	jc	bwiDone
	mov	OutHandle,ax		; *CF clear*

	mov	ax,OutBuffer			; Reset the buffer pointer.
	mov	OutIndex,ax
bwiDone:
	ret
BfrWriteInit	ENDP


BfrWrite	PROC NEAR
	; AL=character, returns CF=1 on error

        mov     di,OutIndex
	stosb
	mov	ax,OutBuffer
	add	ax,BFR_SIZE
	cmp	di,ax
        jb      NoWrite

        push    bx
        push    cx
        mov     bx,OutHandle
	mov	dx,OutBuffer
	mov	cx,BFR_SIZE
        mov     ah,040h
	int	21h			; DOS: Write to File
	jc	bwDone
	pop	cx
        pop     bx
	mov	di,OutBuffer
NoWrite:
	mov	OutIndex,di
	clc

bwDone:
	ret
BfrWrite	ENDP


BfrWriteDone	PROC NEAR

        mov     bx,OutHandle
	mov	dx,OutBuffer
        mov     cx,OutIndex
	sub	cx,OutBuffer
        mov     ah,040h
	int	21h			; DOS: Write to File.
	jc	bwdDone

	mov	dx,InFileDate
	mov	cx,InFileTime

	mov	bx,OutHandle
	mov	ax,05701h
	int	21h			; DOS: Set file date/time

bwdDone:				; *CF1*
	rcr	al,1
	push	ax

        mov     bx,OutHandle
	mov	ah,03Eh
	int	21h			; DOS: Close File.
					; *CF2*
	rcr	al,1
	pop	bx
	or	al,bl
	rcl	al,1				; Net result CF=(CF1 | CF2).

	ret
BfrWriteDone	ENDP


PrintString	PROC NEAR
	; AX = ASCIIZ string.

	mov	si,ax
PrintLoop:
	lodsb
	or	al,al
	jz	PrintDone
	mov	dl,al
	mov	ah,02h
	int	21h			; DOS: Print character
	jmp	PrintLoop
PrintDone:
	ret
PrintString	ENDP


PrintSkipMessage	PROC NEAR
	; AX = ASCIIZ string.

	push	ax
	mov	ax,offset msgSkipping
	call	PrintString

	pop	ax
	call	PrintString

	ret
PrintSkipMessage	ENDP


GetOutName	PROC NEAR
	; SI=InName, DI=OutName

	mov	bx,di
CopyName:
	lodsb					; Copy input name, keeping
	stosb					; track of the last backslash
	or	al,al				; which is where the temp
	jz	EndName 			; file name will be appended.
	cmp	al,'\'
	jne	CopyName
	mov	bx,di
	jmp	CopyName
EndName:

	mov	di,bx
	mov	cx,13
	mov	si,offset TempFile
	rep	movsb

	ret
GetOutName	ENDP


	msgFailInput	db 'unable to open input file.', 0
	msgFailOutput	db 'unable to create output file.', 0
	msgIOFail	db 'I/O error during conversion.', 0
	msgNoChange	db 09h, 'No change required.', 0
	msgRenameFail	db 09h, 'Aborting, unable to rename temporary file.', 0
	msgSkipping	db 09h, 'Skipping, ',0

	msgTitle	db '[NOCRLF v1.00.  By Mark Thomas, NPS Software.  1995]'     ; **,CR,LF,0
	msgCRLF 	db 0Dh, 0Ah, 0					; **

	msgOK		db 09h, 'OK', 0


	msgUsage	db 0Dh, 0Ah
			db 'Fixes binary files which have been corrupted by being transfered', 0Dh, 0Ah
			db 'in ASCII mode.', 0Dh, 0Ah
			db 0Dh, 0Ah
			db 'Usage: NOCRLF files', 0Dh, 0Ah
			db 0Dh, 0Ah
			db 0

	TempFile	db '~NOCRLF~.TMP', 0

EVEN

	InFileDate	dw ?
	InFileTime	dw ?

	InHandle	dw ?
	OutHandle	dw ?

	InBuffer	dw ?
	InIndex 	dw ?
        BytesRead       dw ?

	OutBuffer	dw ?
	OutIndex	dw ?

	arg		dw ?
	InName		dw ?
	OutName 	dw ?

	ChangeFlag	db ?

_TEXT	ENDS


END	Main
[ RETURN TO DIRECTORY ]