	.title	scanf	Formatted input conversion

;
;	The  information  in  this  document  is  subject  to  change
;	without  notice  and  should not be construed as a commitment
;	by Digital Equipment Corporation or by DECUS.
;
;	Neither Digital Equipment Corporation, DECUS, nor the authors
;	assume any responsibility for the use or reliability of  this
;	document or the described software.
;
;		Copyright (C) 1980, DECUS
;
;
;	General permission to copy or modify, but not for profit,  is
;	hereby  granted,  provided that the above copyright notice is
;	included and reference made to  the  fact  that  reproduction
;	privileges were granted by DECUS.
;
	.ident	/000002/

;+
;
; Index		Formatted input conversion
;
; Usage
;
;	scanf(fmt, pointer(s));
;	char		*fmt;	/* Format string	*/
;
;	fscanf(fd, fmt, pointer(s));
;	FILE		*fd;	/* Input file pointer	*/
;	char		*fmt;	/* Format string	*/
;
;	sscanf(buf, fmt, pointer(s));
;	char		*buf;	/* Input text buffer	*/
;	char		*fmt;	/* Format string	*/
;
; Description
;
;	Using the format string, these functions parse the input file
;	(or given text file), storing the results in the pointer
;	arguments.  The three routines differ as follows:
;
;		scanf	Reads from the standard input file.
;
;		fscanf	Reads from the indicated file.
;
;		sscanf	Reads from the text buffer.
;
;	The format string may contain control characters to direct
;	the conversion of the input textual data:
;
;	  ' '	Blanks, tabs, or newlines are ignored in  format
;		strings.  To match whitespace, use "%[ \t\n]".
;
;	   x	An  ordinary  character  (not  %) must match the
;		next input character.
;
;	   %	Conversion specifications consist of a  '%',  an
;		optional   '*'  (to   supress   assignment),  an
;		optional  maximum  numeric  field  width,  and a
;		conversion specifier.
;
;	Unless value assignment was supressed by the '*' format modifier,
;	the next field is converted and stored in the variable pointed to
;	by the corresponding argument.  An input field is defined as a
;	string of non-space characters, extending to the first inappropriate
;	character or until the field width (if given) is exhausted.
;
;	The following conversion characters are specified:
;
;	  %	A single  '%'  is  expected  in  the  input,  no
;		assignment is done.
;
;	  d	An integer  of  the  specified  class  (decimal,
;	  o	octal,  or  hexadecimal)   is   expected.    The
;	  x	corresponding  argument  should  be  an  integer
;		pointer.   If   the  format specifer is given in
;		upper-case, or preceeded by 'l', a long  integer
;		will be stored.  For example, "%ld" is identical
;		to "%D".   Leading  whitespace  will be ignored.
;		A null field is not converted.  Scanf()  returns
;		a  count  of the number of fields converted, but
;		does not indicate which fields were ignored.
;
;	  s	A character string is expected.  The input field
;		will be terminated by a space, tab, or  newline.
;		The corresponding argument  should  point  to  a
;		buffer large enough to contain the  text  and  a
;		terminating  NULL.   Leading  whitespace will be
;		ignored.
;
;	  c	A single character is read. Leading white  space
;		is not supressed  --  to read the next non-blank
;		character, use "%1s".  If a field width is given
;		the  corresponding  argument  is  a pointer to a
;		vector of characters and the indicated number of
;		characters are read.
;
;	  e	A floating-point number is converted and  stored
;	  f	appropriately.    If  the  format  indicator  is
;		capitalized, or  preceeded  by  'l',  a  double-
;		precision  floating-point number will be stored.
;		The floating-point format is the same as in  the
;		C language:   an  optionally  signed  string  of
;		digits  possibly  containing  a  decimal  point,
;		followed by an  optional  exponent  field  which
;		consists  of  an  'E'  or  'e'  followed  by  an
;		optionally signed integer.
;
;	 [ ]	A string not  to  be  delimited  by  white-space
;		characters.  Unless the first character is  '^',
;		the format constitutes an acceptance  list:  the
;		input field is all characters  until  the  first
;		character  which  is  not  in  the  set   within
;		brackets.   Note  that leading whitespace is not
;		ignored.
;
;		If the first character after the left bracket is
;		'^', the format defines a stop list:  the  input
;		field   is   all   characters  until  the  first
;		character specified within the bracketed string.
;
;		The corresponding argument points to a character
;		vector.  The result will be null-terminated.
;
;	Scanf() returns the number of successfully matched and
;	assigned input items.  If the end of input was reached,
;	EOF (-1) is returned.  For example:
;
;		main()
;		{
;			register int	c;
;			int		i;
;			char		text[10];
;
;			c = scanf("%d%9s", &i, &text);
;			printf("i = %d, text = %s\n",
;				i, text);
;		}
;
;	If the input line is "150   foobar", the program will print:
;
;		i = 150, text = foobar
;
; Diagnostics
;
;	Scanf() returns -1 if an end of file (end of string) condition
;	exists and no data was stored.  It returns -2 if a palpably
;	incorrect format, such as "%" is encountered.
;
; Bugs
;
;	No floating point -- trying it will crash the program.
;
;-
;
; Edit history
; 000001 19-Aug-80 MM		Initial Edit
; 000002 18-Sep-80 MM		Fixed .psect name


.MACRO	PRINTF	FMT,A1,A2,A3,A4,A5,A6,A7,A8,A9,?FORMAT,?EXIT
	  MOV	R0,-(SP)
	  MOV	R1,-(SP)
$$$$$$	=	4
.IF	NB	A9
	  MOV	A9,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A8
	  MOV	A8,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A7
	  MOV	A7,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A6
	  MOV	A6,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A5
	  MOV	A5,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A4
	  MOV	A4,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A3
	  MOV	A3,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A2
	  MOV	A2,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
.IF	NB	A1
	  MOV	A1,-(SP)
$$$$$$	=	$$$$$$+2
.ENDC
	  MOV	#FORMAT,-(SP)
	  MOV	STDERR,-(SP)
	  CALL	FPRINTF
	  ADD	#$$$$$$,SP
	  MOV	(SP)+,R1
	  MOV	(SP)+,R0
	  BR	EXIT
FORMAT:	.ASCII	FMT
	.BYTE	12,0
	.EVEN
EXIT:
.ENDM	PRINTF

.MACRO	NOTE	TEXT,?TLOC,?EXIT
	  mov	r0,-(sp)
	  mov	#tloc,r0
	  call	$$msg
	  mov	(sp)+,r0
	  br	exit
tloc:	.asciz	text
	.even
EXIT:
.ENDM	NOTE
;
;
	.psect	.data.
;
; These are made static to simplify argument passing to getnum
; myget, and gettext
;
fd:	.word	0		; File or string pointer
func:	.word	0		; Get next char. function
endflg:	.word	0		; "End of input" flag
length:	.word	0		; Current field length
argptr:	.word	0		; Actual argument pointer
match:	.word	0		; Number of items compiled
;
; Character class table
;
ccltab:	.byte	0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0	; <TAB>, <LF>
	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.byte	1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	; <SPACE>
	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
	.psect	.strn.							;02
err01:	.asciz	/?scanf/

;
; Define local (stack) storage:
;
;
; Register usage within main scan loop:
;
;	r2	Current (format) character
;	r3	Current argument pointer
;	r4	Current format pointer
;
	.psect	.prog.
scanf::
	jsr	r5,csv$		; Link environments
	mov	r5,r3		; R3 ->
	add	#C$PMTR+0,r3	; first argument
	mov	stdin,fd	; Input from stdin
	br	fscan1		; Continue with mainstream

sscanf::
	jsr	r5,csv$		; Link environments
	mov	r5,r3		; R3 ->
	add	#C$PMTR+0,r3	; first argument
	mov	(r3)+,fd	; first arg. is buffer pointer
	mov	#myget,func	; Get funny getc function
	br	fscan2		; Continue with mainstream

fscanf::
	jsr	r5,csv$		; Link environments
	mov	r5,r3		; R3 ->
	add	#C$PMTR+0,r3	; first argument
	mov	(r3)+,fd	; first arg. is file pointer

fscan1:
	mov	#getc,func	; Store "get character" function

fscan2:
	tst	-(sp)		; Get space for match
	mov	(r3)+,r4	; R4 -> format string.
	clr	match		; Number matched = 0
	clr	endflg		; End of input flag = 0
;
; Loop through here for all format characters
;
loop:
	movb	(r4)+,r2	; Next format character
	bne	10$		; If none,
	mov	match,r0	; return number matched
	jmp	done		; Off to exit.

10$:
	mov	r2,r0		; What is it?
	call	whtest		; Whitespace?
	bne	loop		; Ignore it if so
	cmpb	r2,#'%		; '%'?
	bne	text		; no, match next character.
;
; Percent seen
;
percnt:
	movb	(r4)+,r2	; Get format type
	cmpb	r2,#'%		; Explicit percent?
	beq	text		; Yes, go for a match
	clr	argptr		; No argument yet
	cmpb	r2,#'*		; Supress assignment?
	beq	10$		; Br if so,
	mov	(r3)+,argptr	; Want this one.
	br	20$		; Continue mainstream

10$:
	movb	(r4)+,r2	; Was '*', get next format byte

20$:
	clr	r1		; Length accumulator
;
; Loop to get explicit length, if any.
;
30$:
	mov	r2,(sp)		; Get format byte
	sub	#'0,(sp)	; To ascii integer value
	blt	40$		; Exit if < '0'
	cmp	(sp),#9.	; or if > '9'
	bgt	40$		; Too big, exit.
	mov	r1,r0		; Save length
	asl	r1		; length = length * 10
	asl	r1		;
	add	r0,r1		;
	asl	r1		;
	add	(sp),r1		; length = (length * 10) + (c - '0')
	movb	(r4)+,r2	; Get next character
	br	30$		; and go for more length
;
; Got the length, if any.
;
40$:
	mov	r1,length	; Store the length
	bne	50$		; but, if it's zero,
	mov	#32767.,length	; Store a really big length.
;
; Length is gotten, look at format byte
;
50$:
	cmpb	r2,#'[		; Class
	beq	ccl		; Yep
	cmpb	r2,#'L+040	; "Long"?
	bne	60$		; Nope
	movb	(r4)+,r2	; Yep, get format flavor
	cmpb	r2,#'A+040	; Is it lowercase?
	blt	60$		; Not if it's < 'a'
	cmpb	r2,#'Z+040	; Nor if it's > 'z'
	bgt	60$		;
	bic	#040,r2		; Uppercase it
;
; Can't be a character class
;
60$:
	tst	r2		; Still going strong?
	beq	bug		; Nope.  Just exit.
	cmpb	r2,#'C+040	; Yep, Characters?
	beq	istext		; Character stuff
	cmpb	r2,#'S+040	; String?
	beq	istext		; Character stuff
	call	getnum		; Do a number
	br	gotit		; Continue mainstream
;
; Compile character class
;
ccl:
	clr	r1		; Assume "normal" class
	clr	r0		; r0 counts characters
	cmpb	(r4),#'^	; Inverse class?
	bne	10$		; Br if not
	inc	r4		; Yes, eat the '^'
	tst	(r1)+		; and set the flag
;
10$:
	jsr	pc,bisbic(r1)	; Set/clear the flag,
	inc	r0		; counting it.
	cmp	r0,#128.	; Still in the table?
	blt	10$		; Keep on going, then.
	neg	r1		; Flip the set/clear flag
;
; Loop through the format (until ] or end of string), updating the
; stop table.
;
20$:
	movb	(r4)+,r0	; Get the next byte
	bic	#177600,r0	; Erase parity bit
	cmp	r0,#']		; At the end?
	beq	istext		; Yes, continue mainstream
	jsr	pc,bisbic+2(r1)	; Clear/set the flag
	tstb	-1(r4)		; True EOS (parity bit untouched)
	bne	20$		; No, get another byte
	dec	r4		; Yes, format -> NULL
;
istext:
	call	gettxt		; Get textual stuff
;
gotit:
	tst	endflg		; End of file?
back:
	beq	loop		; No, go for next
	mov	match,r0	; Yes, get number matched
	bne	done		; Got something
	dec	r0		; Nothing, return -1
	br	done		;
;
; Bit set or clear routine
;
bisbic:	br	10$		; call bisbic+0 to set   bit 2
	bicb	#2,ccltab(r0)	; call bisbic+2 to clear bit 2
	return
;
10$:
	bisb	#2,ccltab(r0)
	return
;
;
; Here for a text character within the format string
;
text:
	mov	fd,(sp)		; Here's the FILE
	call	@func		; Get next character
	cmp	r0,r2		; Match?
	beq	back		; Yes, continue
	cmp	r0,#-1		; No, EOF?
	beq	done		; Yes, return same
	mov	match,r0	; No, return number gotten.
;
; Exit
;
done:
	jmp	cret$		; All done for now.
;
bug:
	mov	#-2,r0		; Illegal format
	br	done		; Just exit


;
; Myget is called by invocations of sscanf() to return the next character
; from the text buffer.  <NULL> returns end of file.
;
myget:
	movb	@fd,r0		; Get next byte
	beq	10$		; End of file?
	inc	fd		; No, point to next
	return			; and exit.
;
10$:
	dec	r0		; End of file, return -1
	return			; Don't update pointer
;
;
; Whtest is called to test the byte (in r0) for whitespaceness.
; calling sequence:
;
;	mov	byte,r0
;	call	whtest
;	bne	iswhite		; Note -- branch not equal.
;
; All registers preserved.  The value in r0 will be mapped into the
; range 0..127
;
whtest:
	bic	#^C0177,r0	; Clear out the parity bit
	bitb	#1,ccltab(r0)	; If whitespace bit is set
	return			; return with Z-bit cleared.

;
; Getnum is called by scanf to compile a real number
;
; On entrance:
;	r2		Format type ( d o x D O X, e f E F )
;	r2, r3, r4	Preserved
;
; Also static stuff
;
; No interesting returned value
;
; Note: floating point causes errors
; 
;
; Local storage:
;
;
; Note: to disable Floating-point code, set MAXN = 0
;
MAXN	=	0		; No floating point
.IIF	NDF	MAXN	MAXN	=	64.		; Longest number

EXPFLG	=	0001		; Exponent seen
FLOAT	=	0200		; Floating point
LONG	=	0001		; Long result
NEGFLG	=	0200		; Negative result
;
; Note:
;	tstb	fflag			tstb	sflag
;	bhi	(EXPFLG set)		bhi	(LONG   set)
;	blos	(EXPFLG clear)		blos	(LONG   clear)
;	bmi	(FLOAT  set)		bmi	(NEGFLG set)
;	bpl	(FLOAT  clear)		bmi	(NEGFLG clear)
;
flags	=	C$AUTO-2	; Both flag bytes
fflag	=	flags		; Float flags
sflag	=	flags+1		; Scan flags
ndigit	=	C$AUTO-4	; Number of digits found
hval	=	C$AUTO-6	; High integer
lval	=	C$AUTO-10	; Low  integer
nbrbuf	=	C$AUTO-10-MAXN	; Number buffer
				; Note: nbrbuf(r5) == sp
getnum:
	jsr	r5,csv$		; Linkage (to save registers)
	sub	#MAXN+10,sp	; Get space for number buffer		
	clr	flags(r5)	; Clear flag bits
	clr	hval(r5)	; Clear integer
	clr	lval(r5)	; Buffer
	cmpb	r2,#'A		; Uppercase
	blt	10$		;  means
	cmpb	r2,#'Z		;   it's
	bgt	10$		;    long
	incb	sflag(r5)	; Set "long result" flag
	bis	#040,r2		; Convert to lowercase
;
10$:
	cmpb	r2,#'E+040	; 'e'
	beq	20$		; Yes,
	cmpb	r2,#'F+040	; 'f'
	bne	30$		; Nope
;
20$:
.if ne	MAXN
	bisb	#FLOAT,fflag(r5) ; (FPU) set flag
.iff
	CRASH			; No floating point
.endc
;
30$:
	clr	r3		; r3 := jump index
	cmpb	r2,#'X+040	; Hexidecimal
	beq	40$		; Yes, exit
	tst	(r3)+		; No, step flag
	cmpb	r2,#'O+040	; Octal
	beq	40$		; Yes, exit
	sub	#4,r3		; No, gotta be decimal or floating
;
; r3 := -2/0/+2 depending on decimal/hex/octal
; r2 is no longer needed.
;
40$:
.if ne	MAXN
	mov	sp,r4		; r4 -> number buffer
.iff
	clr	r4		; r4 counts characters read
.endc

;
; Skip over leading whitespace
;

50$:
	mov	fd,(sp)		; Get the next character
	call	@func		; From defined function
	call	whtest		; Whitespace?
	bne	50$		; Loop if so.
;
; Negative?
;
	cmpb	r0,#'-		; If first byte's a minus
	bne	60$		; (nope)
	bisb	#NEGFLG,sflag(r5) ; set flag and
.if ne	MAXN
	movb	r0,(r4)+	; store the byte and
.iff
	inc	r4		; count the character and
.endc
	br	170$		; go for the next byte
;
60$:
	cmpb	r0,#'+		; (optional) plus sign
	bne	80$		; nope, continue
;
170$:
	mov	fd,(sp)		; Stuff the FILE pointer
	call	@func		; + or -, get next byte
	dec	length		; and drop the length count
;
80$:
;
; Main number loop -- the current byte is in r0
;
nloop:
	mov	r0,r1		; Save (for eof test)
.if ne	MAXN
	movb	r0,(r4)+	; Store the character away
.iff
	inc	r4		; Count the character.
.endc
	dec	length		; More field to do?
	blt	nloopx		; No, field is complete
;
; Note: Unix stdio allows '8' and '9' in octal strings for some
; strange reason.  We don't.
;
	cmpb	r0,#'0		; Is it too low?
	blt	notdig		; Sorry
	cmpb	r0,digtab(r3)	; Is it in range?
	ble	10$		; Yes, ok
;
; We can only come here if the character is greater than the highest digit.
;
	tst	r3		; Are we in Hex mode?
	bne	notdig		; Sorry
	bic	#040,r0		; Convert 'a'..'z' to 'A'..'Z'
	cmpb	r0,#'A		; Is it an OK alpha?
	blt	notdig		; Sorry
	cmpb	r0,#'F		; Is it really a hex alpha?
	bgt	notdig		; Nope.
	sub	#'A-<'9+1>,r0	; Fix up the range ('A' follows '9')		
;
10$:
	sub	#'0,r0		; R0 has binary value of the digit.
;
; Process the new digit
;
	inc	ndigit(r5)	; Count that it's gotten
	mov	#3,r1		; Prime for octal
	jmp	@20$(r3)	; Off to the races
	.word	30$		; Multiply by 10
20$:	.word	40$		; Multiply by 16
	.word	50$		; Multiply by 8

;
; Decimal multiply (a bit of a hack)
;

30$:
	mov	hval(r5),r1
	mov	lval(r5),r2
	asl	r2		; Multiply by 2
	rol	r1		;
	asl	r2		; Multiply by 4
	rol	r1		;
	add	lval(r5),r2	; Multiply by 5
	adc	r1		;
	add	hval(r5),r1	;
	asl	r2		; Multiply by 10
	rol	r1		;
	mov	r2,lval(r5)	; Save in the
	mov	r1,hval(r5)	; buffer
	br	60$		; Continue
	
40$:
	inc	r1		; Gotta multiply by 16
;
50$:
	asl	lval(r5)	; Multiply by 2
	rol	hval(r5)	;
	dec	r1		; Count it
	bne	50$		; And continue
60$:
	add	r0,lval(r5)	; Add it the value
	adc	hval(r5)	; all of it
;
nnext:
	mov	fd,(sp)		; Stuff the FILE pointer
	call	@func		; Get next byte
	br	nloop		; Go for it
;
; Not a digit
;
notdig:
.if ne	MAXN
	cmpb	r0,#'.		; Decimal point?
	bne	10$		; Nope
	tstb	fflag(r5)	; Float?
	bpl	nloopx		; Nope, it terminates the field.
	inc	ndigit(r5)	; Ok, keep it
	br	nnext		; and continue
;
; Here to test for exponent part
;
10$:
	tstb	fflag(r5)	; Exponent seen?
	bhi	nloopx		; Yep, don't bother to test
	cmpb	r0,#'E		; Exponent?
	beq	20$		; Yep, continue
	cmpb	r0,#'E+040	; Another chance?
	bne	nloopx		; Nothing left to do.
;
20$:
	incb	fflag(r5)	; Set "exponent seen" flag
	mov	fd,(sp)		; Stuff the FILE pointer
	call	@func		; Get the next byte
	mov	r0,r1		; Save in case of exit
	movb	r0,(r4)+	; Store it away
	inc	r4		; Count it
	cmpb	r0,#'+		; Plus?
	beq	nloop		; Wonderful
	cmpb	r0,#'-		; Minus?
	beq	nloop		; Marvelous
	cmpb	r0,#'0		; Decimal
	blt	nloopx		; Sorry
	cmpb	r0,#'9		; Other decimal end?
	ble	nloop		; Perfection
.endc
;
; End of the "get a number loop".  The last byte read is in r1 and -(r4)
; Clean up r4, returning the last byte to the file system.
;
nloopx:
.if ne	MAXN
	clrb	-(r4)		; Terminate the output
.iff
	dec	r4		; Uncount the character
.endc
	mov	r1,r0		; Backup wants the character
	call	backup		; Unget the last character
	tstb	sflag(r5)	; Negative?
	bpl	10$		; Nope
	neg	hval(r5)	; Yep, negate them
	neg	lval(r5)	; Both halves
	sbc	hval(r5)	; All 32 bit's worth
;
10$:
	mov	argptr,r0	; A real argument?
	beq	40$		; Nope, exit
.if ne	MAXN
	cmp	r4,sp		; At buffer start?
.iff
	tst	r4		; Compiled zero characters?
.endc
	beq	40$		; Nothing was read if so
	inc	match		; got one to store
.if ne	MAXN
	tstb	fflag(r5)	; Floating point?
	bmi	30$		; Yes, do it.
.endc
;
; Output an integer
;
	tstb	sflag(r5)	; Long?
	bhi	20$		; Br if so
	mov	lval(r5),(r0)	; Output it
	br	40$		; and exit
;
20$:
	mov	hval(r5),(r0)+	; Output the long
	mov	lval(r5),(r0)	; both halves
.if ne	MAXN
	br	40$		; and exit
;
; Convert and output the floating point value
;
; (Someday)  (This code should setup and call atof()).
;
30$:
	mov	#err02,(sp)
	call	error
.endc
;
40$:
	jmp	cret$		; All done.
;
; Digit test vector:
;
	.word	'9		; Decimal
digtab:	.word	'9		; Hex
	.word	'7		; Octal

;
; Gettxt is called to read text until the break character
;
; On entrance:
;	r2		Format code	c s [
;	r2, r3, r4	Preserved
;
; Nothing returned
;

gettxt:
	jsr	r5,csv$		; Linkage
	mov	argptr,r4	; R4 -> output buffer
	cmpb	r2,#'C+040	; Character type?
	bne	10$		; Nope
	cmp	length,#32767.	; Yep, default (zero) length?
	bne	10$		; Nope, length was specified
	mov	#1,length	; Only want one character
;
10$:
	clr	r3		; Stop code
	cmp	r2,#'S+040	; String type?
	bne	20$		; Nope
	inc	r3		; Yep, stop code := 1
;
20$:
;
; Initial skip over stuff we don't want
;
30$:
	mov	fd,(sp)		; Stuff the FILE pointer
	call	@func		; Get the next byte
	bitb	ccltab(r0),r3	; Match?
	beq	40$		; No, exit this loop
	cmp	r0,#-1		; Yes, EOF?
	bne	30$		; No, continue
;
40$:
	clr	r3		; Clear stop flag
	cmp	r2,#'C+040	; Character
	beq	50$		; Yes, stop flag := 0
	inc	r3		; No, assume a string
	cmp	r2,#'S+040	; Well, ...
	beq	50$		; Ok
	inc	r3		; Nope, gotta be [ class
;
50$:
;
; Main loop for character getting (current char in r0)
;
tloop:
	dec	length		; Get another?
	blt	10$		; No more
	tst	r0		; Real character?
	blt	10$		; No (EOF)
	bitb	ccltab(r0),r3	; Yes, in stop table?
	bne	10$		; Yes, exit.
	tst	r4		; Storing?
	beq	10$		; No, continue
	movb	r0,(r4)+	; Yes, store it
	mov	fd,(sp)		; Stuff the FILE pointer
	call	@func		; and get another
	br	tloop		; Loop some more
;
10$:
	call	backup		; Unget the character
	tst	r4		; Really storing
	beq	20$		; Nope
	cmp	r4,argptr	; Actually stored something?
	beq	20$		; Nope
	inc	match		; Yes, count it
	cmpb	r2,#'C+040	; Character arg?
	beq	20$		; Yes, don't terminate it
	clrb	(r4)		; Terminate the string
;
20$:
	jmp	cret$		; Exit

;
; Backup the scan pointer -- called when the main scan a field loop
; terminates.
;
; On entrance:
;	r0	:= last character read (or EOF flag)
;
; Return:
;
; If r0 was not EOF, the character will be pushed back onto the input stream.
;
backup:
	cmp	r0,#-1		; EOF?
	bne	10$		; Nope
	inc	endflg		; Yep, set flag
	return			; and exit
;
10$:
	cmp	func,#myget	; Internal "get
	bne	20$		; Nope, do it the hard way
	dec	fd		; Backup the scan pointer
	return			; And exit
;
20$:
	mov	fd,-(sp)	; ungetc(c, fd);
	mov	r0,-(sp)	;
	call	ungetc		; Do it
	cmp	(sp)+,(sp)+	; Clean the stack
	return			; Exit cleanup code
	.end
