	title	like, do some lines, man
;++
;
; Count the lines in files.
;
; By John Wilson.
;
; 01/10/88	JMBW	Created.
; 02/17/95	JMBW	Accepts wildcards as cmd line args, default=STDIN.
;			Converted to .COM file for smaller size.
;
;--
	.radix	8
;
lf=	12
cr=	15
;
code	segment
	assume	cs:code
	org	100h		;.COM file
;
beg:	jmp	short beg1
	db	10,10		;(two backspaces)
	db	'LINES.COM, by John Wilson',cr,lf
	db	'Z'-100		;EOF for TYPE
beg1:	cld			;DF=0
	; give back memory we don't need
	cmp	sp,offset pdl	;make sure we have it in the first place
	jb	beg3
	mov	sp,offset pdl	;shrink stack if so
	mov	bx,mem		;size of memory to save in paragraphs
	mov	ah,4Ah		;func=setblock
	int	21h
	jc	beg3		;punt on failure, can't reorganize memory
	; allocate buffer
	mov	bx,0FFFh	;size=64KB -1 par (REPNE can't reach all 64K)
beg2:	mov	ah,48h		;func=getblock
	int	21h
	jnc	beg4		;got it, skip
	mov	bx,ax		;copy amount DOS says it can give
	test	bx,bx		;anything?
	jnz	beg2		;try again if so
beg3:	; failed, punt
	mov	dx,offset nomem	;pt at msg
	mov	cx,21d		;length
	mov	bx,0002h	;STDERR
	mov	ah,40h		;func=write
	int	21h
	mov	ax,4C01h	;func=punt
	int	21h
beg4:	; got it, copy cmd line out of DTA
	mov	ds:buf,ax	;save buf ptr
	mov	cl,4		;shift count
	sal	bx,cl		;get # bytes in buf
	mov	ds:buflen,bx	;save
	xor	ah,ah		;ah=0
	mov	si,80h		;pt at JCL
	lodsb			;get length
	mov	cx,ax		;copy
	mov	di,offset jcl	;pt at cmd line buffer
	push	di		;save ptr
	rep	movsb		;copy it there
	pop	si		;point at it
	mov	cx,ax		;restore length
prs1:	; parse out each word and look it up as a wildcard
	jcxz	prs3		;skip if nothing to do
prs2:	lodsb			;get a byte
	cmp	al,' '		;blank or ctrl char?
	ja	prs6		;no
	loop	prs2		;ignore if so
prs3:	; end of command line
	mov	di,offset lbuf	;pt at LBUF
	cmp	ds:nfiles,1	;gotten anything?
	jb	prs5		;no, read STDIN
	je	prs4		;exactly one thing, done
	; more than one file, print total
	mov	si,offset total	;pt at string
	mov	cx,7		;LEN('Total: ')
	rep	movsb		;copy
	mov	ax,ds:totlin	;get total # lines
	mov	dx,ds:totlin+2
	call	prline		;finish off line
prs4:	int	20h
prs5:	; no files given, read standard input
	mov	si,offset stdin	;pt at string
	mov	cx,9d		;LEN('(stdin): ')
	rep	movsb		;copy
	call	count		;count it up, print result
	int	20h
prs6:	; beginning of a word
	mov	di,offset lbuf	;point at LBUF
	mov	dx,di		;copy ptr
	dec	si		;back up
prs7:	lodsb			;get a char
	cmp	al,' '		;blank or ctrl char?
	jbe	prs11		;yes, end of word
	cmp	al,'/'		;either kind of slash?
	je	prs9
	cmp	al,'\'
	je	prs8
	cmp	al,'a'		;lower case?
	jb	prs10
	cmp	al,'z'
	ja	prs10
	sub	al,40		;convert to upper if so
	jmp	short prs10
prs8:	mov	al,'/'		;normalize to forward slash
prs9:	lea	dx,[di+1]	;point after it
prs10:	stosb			;save
	loop	prs7		;loop
	inc	si		;correct for next
prs11:	dec	si		;unget last char
	; si,cx=cmd line descriptor, di points past end of filespec in LBUF
	; dx points at beginning of filename in filespec in LBUF
	push	si		;save
	push	cx
	push	dx		;save filename ptr
	xor	al,al		;mark end
	stosb
	dec	di		;back up in case of error
	mov	dx,offset lbuf	;pt at begn
	xor	cx,cx		;attrib=generic file
	mov	ah,4Eh		;func=find first
	int	21h
	jc	prs14		;error, no matches
prs12:	pop	di		;get ptr to filename in LBUF
	push	di		;save again
	mov	si,9Eh		;pt at filename in default DTA
prs13:	lodsb			;get a byte
	stosb			;save
	test	al,al		;end?
	jnz	prs13		;loop if not
	dec	di		;back up
	mov	dx,offset lbuf	;pt at filename
	mov	ax,3D00h	;func=open /RONLY
	int	21h		;open it
	jc	prs15
	mov	ds:handle,ax	;save
	mov	ax," :"		;': '
	stosw
	call	count		;count up this file
	mov	bx,ds:handle	;get handle
	mov	ah,3Eh		;func=close
	int	21h
	mov	dx,80h		;point at default PSP (not in V2, MS geeks!!!)
	mov	ah,4Fh		;func=find next
	int	21h
	jnc	prs12		;loop if got another
	pop	di		;flush filename ptr once and for all
	pop	cx		;restore cmd line ptr
	pop	si
	jmp	prs1
prs14:	; file not found
	mov	si,offset fnf	;pt at msg
	mov	cx,18d		;length
	jmp	short prs16
prs15:	; open error
	mov	si,offset opnerr ;pt at msg
	mov	cx,19d		;length
prs16:	rep	movsb		;copy msg
	mov	dx,offset lbuf	;pt at buf
	mov	cx,di		;calc length
	sub	cx,dx
	mov	bx,0002h	;STDERR
	mov	ah,40h		;func=write
	int	21h
	mov	ax,4C01h	;func=punt
	int	21h
;+
;
; Count and display number of line feeds in a file.
;
; File handle is in CS:HANDLE.
;
; ds,es	preserved
; es:di	pointer into LBUF for decimal number
;
;-
count:	push	di		;save
	push	ds
	push	es
	inc	ds:nfiles	;count it
	mov	ds:hiline,0	;nuke high word of line count
	mov	ax,ds:buf	;pt at buf
	mov	ds,ax		;with ds
	mov	es,ax		;and es
	xor	si,si		;init low word of line count
cnt1:	; read a bufferload
	xor	dx,dx		;offset=0
	mov	cx,cs:buflen	;length
	mov	bx,cs:handle	;file handle
	mov	ah,3Fh		;func=read
	int	21h		;do it
	jc	cnt3		;err, treat as eof
	mov	cx,ax		;copy count
	jcxz	cnt3		;eof, skip
	xor	di,di		;start of buf
	mov	al,lf		;line feed
cnt2:	repne	scasb		;look for it
	jne	cnt1		;end of buf, load next
	inc	si		;count it
	jnz	cnt2		;look again (OK if cx=0)
	inc	cs:hiline	;bump high count
	jmp	short cnt2
cnt3:	; display count
	pop	es		;restore
	pop	ds
	pop	di
	mov	ax,si		;copy
	mov	dx,ds:hiline
	add	ds:totlin,ax	;add to total
	adc	ds:totlin+2,dx
	;jmp	short prline	;print line, return
;+
;
; Add a number to LBUF and print the whole line.
;
; dx:ax	number
; es:di	ptr to end of line in LBUF
;
;-
prline:	call	prdec		;print # in decimal
	mov	ax,cr+(lf*400)	;crlf
	stosw
	mov	dx,offset lbuf	;pt at buf
	mov	cx,di		;calc length
	sub	cx,dx
	mov	bx,0001h	;STDOUT
	mov	ah,40h		;func=write
	int	21h
	ret
;+
;
; Print decimal # in dx:ax.
;
; es:di	buffer (updated on return)
;
;-
prdec:	mov	bx,10d		;divisor
prdec1:	xor	cx,cx		;assume high order result=0 (no overflow)
	cmp	dx,bx		;number .lt. divisor*65536?
	jb	prdec2		;yes, a single DIV will do it
	; result won't fit in 16 bits (quo.lt.65536, rem.lt.bx)
	; do the division in two steps so as to get correct low order
	mov	cx,ax		;save low order
	mov	ax,dx		;copy high order
	xor	dx,dx		;0-extend
	div	bx		;do first 16-bit "digit" of long division
	xchg	ax,cx		;save high "digit" of result, get low order
prdec2:	div	bx		;(dx is remainder "carried" from first div)
	; cx:ax=quotient, dx=remainder
	push	dx		;save remainder
	mov	dx,cx		;copy
	or	cx,ax		;should we recurse?
	jz	prdec3
	 call	prdec1		;yes
prdec3:	pop	ax		;restore remainder
	or	al,'0'		;convert to decimal
	stosb			;save
	ret
;
nomem	db	'Insufficient memory',cr,lf
total	db	'Total: '
stdin	db	'(stdin): '
fnf	db	': File not found',cr,lf
opnerr	db	': File open error',cr,lf
;
totlin	dw	0,0		;total # lines
nfiles	dw	0		;# files processed so far
;
handle	dw	0000h		;file handle (STDIN unless changed)
;
lines	dw	2 dup(?)	;# lines in current file
;
buf	dw	1 dup(?)	;seg addr of file buffer
buflen	dw	1 dup(?)	;length in bytes of file buffer
;
hiline	dw	1 dup(?)	;high word of # lines in current file
;
jcl	db	127d dup(?)	;copy of JCL (moved out of DTA for FINDNEXT)
;
lbuf	db	132d+2 dup(?)	;output line buffer
;
	dw	100h dup(?)	;stack
pdl	label	word
;
mem=	((($+0Fh)-beg)/10h)+10h	;# paragraphs to save, including 10h for PSP
;
code	ends
;
	end	beg
