	.title	format - Format a string UNIX style
	.ident	"V02.00"
;+
; Index 	Format a string UNIX style
;
; Usage
;
;	length = format(buffer, format, arg1, ...);
;	char *buffer;
;	char *format;
;	int length;
;
; Description
;
;	format() converts and formats its arguments, under the control
;	of the format string, into the buffer indicated in the first
;	argument.
;
;	The format string is a character string which contains two
;	types of objects: plain characters, which are simply copied
;	to the buffer, and conversion specifications, each of which
;	cause conversion of the next successive argument.
;
;	Each conversion specification is introduced by the % character.
;	Following the %, there may be
;
;	 - an  optional  minus  sign  "-"  which  specifies left
;	   adjustment of the converted argument in the indicated
;	   field.
;
;	 - an optional digit string specifying field  width;  if
;	   the  converted argument has fewer characters than the
;	   field width, it will be blank-padded on the left  (or
;	   right, if  the  left-adjustment  indicator  has  been
;	   given) to make up the  field  width.   If  the  field
;	   width  is  specified as '*' or '?', the next argument
;	   is used.  (Note: '?' is obsolete.)
;
;	 - an  optional  period "." which serves to separate the
;	   field width from the next digit string;
;
;	 - an optional digit string (precision) which  specifies
;	   the number of digits  to  appear  after  the  decimal
;	   point for e- and f-conversion, or the maximum  number
;	   of  characters  to be printed from a string.   If the
;	   precision is  specified  as  '*'  or  '?',  the  next
;	   argument is used.
;
;	 - a character which indicates the  type  of  conversion
;	   to be applied.
;
;	The conversion characters and their meanings are
;
;	 d	Signed-decimal
;	 u	Unsigned-decimal
;	 o	Octal
;	 X	Hexadecimal, 10-15 are represented by A-F
;	 x	Hexadecimal, 10-15 are represented by a-f
;
;		The  integer  argument  is converted to decimal,
;		octal, or hexadecimal   notation   respectively.
;		Any   of   the   conversion  characters  may  be
;		preceeded  by  'l'  to  signal  "long"   integer
;		argument.   Note  that the Unix usage of capital
;		letters  to  represent  long  arguments  is  not
;		supported.
;
;	 c	The argument character is printed.   (Note  that
;		'lc' takes a long integer argument.)
;
;	 r	Remote format.  The next  printf()  argument  is
;		the format.  Note that this is not a subroutine.
;		The current format is not processed further. For
;		example:
;
;			bug(args)
;			{
;				error("Error at %r", &args);
;			}
;
;		This routine might be called as follows:
;
;			bug("Error %d at %s\n", val, name);
;
;	 s	The  argument is taken to be a string (character
;		pointer) and  characters  from  the  string  are
;		printed  until  a  null  character  or until the
;		number of characters indicated by the  precision
;		specification  is   reached;   however   if  the
;		precision   specification  is  0  or missing all
;		characters up to null are printed.
;
;	If no recognizable character appears after the %, that character
;	is printed; thus % may be printed by the use of the string %%.
;	In no case does a non-existant or small field width cause truncation
;	of a field; padding takes place only if the specified field
;	width exceeds the actual width.
;-
; Edits:
; V02.00 19-Oct-82 TTC		Rework of old CS library
;

auto$$	=	-6		; See acsv.mac for the correct value

spare	=	auto$$-2	; Output function is fixed.
ARGP	=	auto$$-4
LJUST	=	auto$$-6
PREC	=	auto$$-10
WIDE	=	auto$$-12
FILL	=	auto$$-14
HORD	=	auto$$-16
LORD	=	auto$$-20
BUF	=	auto$$-44	; 20 bytes.
OLDP	=	auto$$-46
STACK	=	46		; Last argument for stack get

	.psect	c$strn

null:	.asciz	"{Null}"
	.even

	.psect	c$code

format::
	jsr	r5,csv$			;Save registers, etc.
	mov	c$pmtr+0(r5),r4		;r4 --> output buffer
	mov	#c$pmtr+2,r0		;r0 offset to format string address
	sub	#STACK,sp		;Get stack area
	add	r5,r0			;r0 --> format address
	mov	(r0)+,r3		;r3 --> format
	mov	r0,ARGP(r5)		;ARGP(r5) --> arg's.

;
; Get next character from the
; format string.
; If not a '%' just send it to the
; output.
;


prf01:	movb	(r3)+,r0
	bne	25$
	jmp	prf03
25$:

	cmp	r0,#'%
	beq	30$
	movb	r0,(r4)+
	br	prf01

;
; Get widths and flags.
;

30$:
	clr	LJUST(r5)
	clr	WIDE(r5)
	mov	#-1,PREC(r5)
	mov	#' ,FILL(r5)

	movb	(r3)+,r0
	cmp	r0,#'-
	bne	40$
	inc	LJUST(r5)
	movb	(r3)+,r0

40$:
	cmp	r0,#'0
	bne	50$
	mov	r0,FILL(r5)
	movb	(r3)+,r0

50$:
	call	num
	mov	r1,WIDE(r5)

	cmp	r0,#'.
	bne	60$
	movb	(r3)+,r0
	call	num
	mov	r1,PREC(r5)

;
; %r					Remote format
;

60$:
	cmp	r0,#'r
	bne	90$
	mov	@ARGP(r5),ARGP(r5)
	mov	@ARGP(r5),r3
	add	#2,ARGP(r5)
	br	prf01


;
; Get the data. If there is an 'l' in
; the format copy a long to HORD and
; LORD. Otherwise copy the integer to
; LORD and sign extend if %d.
;

90$:
	mov	r5,r2
	add	#BUF,r2
	mov	ARGP(r5),OLDP(r5)
	mov	@ARGP(r5),r1
	add	#2,ARGP(r5)

	cmp	r0,#'l
	bne	100$
;
; %l					Long integer
;
	mov	r1,HORD(r5)
	mov	@ARGP(r5),LORD(r5)
	add	#2,ARGP(r5)
	movb	(r3)+,r0
	br	120$

100$:
	cmp	r0,#'d
	beq	110$
	mov	r1,LORD(r5)
	clr	HORD(r5)
	br	120$

110$:
	clr	HORD(r5)
	mov	r1,LORD(r5)
	bpl	120$
	com	HORD(r5)

;
; %c					Character
;

120$:
	cmp	r0,#'c
	bne	130$
	mov	r5,r2
	add	#LORD,r2
	clrb	LORD+1(r5)
	br	prf02

;
; %s					String
;
; If pointer is 0, print {Null}
;

130$:
	cmp	r0,#'s
	bne	140$
	mov	LORD(r5),r2
	bne	prf02
	mov	#null,r2
	br	prf02

;
; %d, %o, %u, %x, %X			Integer conversion
;
; Only %d gets integers sign extended.
; All done with common routine.
;

140$:
	cmp	r0,#'d
	beq	150$
	cmp	r0,#'o
	beq	150$
	cmp	r0,#'u
	beq	150$
	cmp	r0,#'x
	beq	150$
	cmp	r0,#'X
	bne	160$

150$:
	mov	LORD(r5),-(sp)
	mov	HORD(r5),-(sp)
	mov	r0,-(sp)
	mov	r2,-(sp)
	call	$$ltoa
	add	#10,sp
	br	prf02

;
; None of the above.
; Restore ARGP so an argument is not
; eaten.
;

160$:
	mov	OLDP(r5),ARGP(r5)
	movb	r0,(r4)+
	jmp	prf01

;
; Send out the null terminated ascii
; string pointed to by r2.
; Inserted any required fills and
; pads.
;

prf02:
	mov	r2,r1
170$:
	tstb	(r1)+
	bne	170$
	dec	r1
	sub	r2,r1
	tst	PREC(r5)
	bmi	180$
	cmp	PREC(r5),r1
	bhis	180$
	mov	PREC(r5),r1

180$:
	mov	r1,PREC(r5)

	tst	LJUST(r5)
	bne	190$
	call	fills

190$:
	mov	PREC(r5),r1
200$:
	dec	r1
	bmi	210$
	movb	(r2)+,r0
	movb	r0,(r4)+
	br	200$

210$:
	tst	LJUST(r5)
	beq	215$
	call	fills

215$:
	jmp	prf01

;
; All done. Append the null byte, compute and return
; the null byte.
;

prf03:	clrb	(r4)
	sub	C$PMTR+0(r5),r4		; r4 = length
	mov	r4,r0			; Return it
	jmp	cret$


;
; Output WIDE - PREC copies of the
; FILL character.
;

fills:	mov	WIDE(r5),r1
	sub	PREC(r5),r1
	ble	240$

	mov	FILL(r5),r0

230$:
	movb	r0,(r4)+
	dec	r1
	bne	230$

240$:
	return

;
; Read in a field width.
; The first character of the width is
; in r0.
; The width is returned in r1.
; If the width specifier is '*' or '?' then the
; width is obtained from the args.
;

num:	cmp	r0,#'*
	beq	10$
	cmp	r0,#'?
	bne	250$
10$:
	mov	@ARGP(r5),r1
	add	#2,ARGP(r5)
	movb	(r3)+,r0
	br	270$

250$:
	clr	r1

260$:
	cmp	r0,#'0
	blo	270$
	cmp	r0,#'9
	bhi	270$

	asl	r1
	mov	r1,-(sp)
	asl	r1
	asl	r1
	add	(sp)+,r1

	add	r0,r1
	sub	#'0,r1
	movb	(r3)+,r0
	br	260$

270$:
	return

	.end
