; Format input lines to standard output

       INCLUDE "/include/init.i"

;      OPT     D+

; Local Equs

FormatBufSize  EQU     1500
MaxLineSize    EQU     512
MaxFormatArgs  EQU     20

; Variable storage

       STRUCT  ArgArray,(1+1)*4
       STRUCT  ArgStream,MaxFormatArgs*4
       STRUCT  FormatClipInfo,2*4
       LONG    StdIn
       LONG    StdOut
       LONG    FormatBufPtr
       SIZE

; Regs

ByteFetch      EQUR    D4
ReadBytesLeft  EQUR    D5
LineBufPtr     EQUR    D6
LineBufLeft    EQUR    D7
LineEndPtr     EQUR    A2
ErrorStrPtr    EQUR    A4

; Startup

       STACK   4000
       INIT

; Let ARP interpret the commandline

       MOVE.L  ComLineBase(GP),A0
       MOVE.L  ComLineSize(GP),D0
       LEA     HelpMsg(PC),A1
       LEA     ArgArray(GP),A2
       LEA     Template(PC),A3
       MOVE.L  A1,(A2)
       CALL    GADS
       MOVE.L  (A2),ErrorStrPtr
       TST.L   D0
       BEQ     ErrorExit
       BMI     ErrorExit

; Open the standard input & output

       CALL    Input
       MOVE.L  D0,StdIn(GP)
       CALL    Output
       MOVE.L  D0,StdOut(GP)

; Alloc tracked mem for the string format buffer.

       LEA     OutOfMem(PC),ErrorStrPtr
       MOVE.L  #FormatBufSize,D0
       MOVEQ   #0,D1
       CALL    ArpAllocMem
       MOVE.L  D0,FormatBufPtr(GP)
       BEQ     ErrorExit

; Allocate the read-line buffer

       MOVE.L  #MaxLineSize+4+MaxLineSize,D0   ; 2 part buf with 0 term space.
       MOVEQ   #0,D1
       CALL    ArpAllocMem
       MOVE.L  D0,LineBufPtr
       BEQ     ErrorExit

; Initialize the argument stream

       MOVEQ   #MaxFormatArgs-1,D0
       LEA     ArgStream(GP),A0
InitArgStreamLongs:
       MOVE.L  LineBufPtr,(A0)+
       DBRA    D0,InitArgStreamLongs


; ** This is an intelligent ReadLine routine.

       MOVEQ   #0,ReadBytesLeft

; Reinitialize the line buffer. (First part of dual buffer space)

DoForEachLine:
       LEA     ReadError(PC),ErrorStrPtr
       MOVE.L  LineBufPtr,LineEndPtr
       MOVE.L  #MaxLineSize,LineBufLeft

; Extract lines from the readbuffer into the line buffer.

       BRA.S   ExtractEntry
CopyLineBytes:
       MOVE.L  ByteFetch,A0
       ADDQ.L  #1,ByteFetch
       SUBQ.L  #1,ReadBytesLeft
       MOVE.B  (A0),D0
       CMP.B   #10,D0
       BEQ.S   GotLine
       MOVE.B  D0,(LineEndPtr)+
       SUBQ.L  #1,LineBufLeft
       BEQ.S   GotLine
ExtractEntry:
       TST.L   ReadBytesLeft
       BNE     CopyLineBytes

; Refill the read buffer.

       MOVE.L  StdIn(GP),D1
       MOVE.L  LineBufPtr,D2
       MOVE.L  #MaxLineSize,D3
       ADD.L   D3,D2
       ADDQ.L  #4,D2
       CALL    Read
       TST.L   D0
       BMI.S   ErrorExit
       BNE.S   NotYetDone
       CMP.L   LineBufPtr,LineEndPtr
       BEQ.S   Exit
NotYetDone:
       MOVE.L  D2,ByteFetch
       MOVE.L  D0,ReadBytesLeft
       BNE     CopyLineBytes

; Null terminate the line.

GotLine:
       CLR.B   (LineEndPtr)

; Format things, terminate with a LF, and write the format buffer

       BSR.S   VanillaDoFmt
       MOVE.L  FormatBufPtr(GP),A0
       MOVE.B  #10,-1(A0,D0.L)

       LEA     WriteError(PC),ErrorStrPtr
       MOVE.L  StdOut(GP),D1
       MOVE.L  A0,D2
       MOVE.L  D0,D3
       BEQ.S   WriteDone
       CALL    Write
       CMP.L   D0,D3
       BNE.S   ErrorExit
WriteDone:

; Check if user hit ^C 

       SUB.L   A1,A1
       CALL    CheckAbort
       MOVE.L  A1,ErrorStrPtr
       TST.L   D0
       BNE.S   ErrorExit
       BRA     DoForEachLine

; Done, cleanup

Exit:
       CLR.W   ReturnCode(GP)
       SUB.L   ErrorStrPtr,ErrorStrPtr
ErrorExit:

; Display error string, if any, and exit

       MOVE.L  ErrorStrPtr,D0
       BEQ.S   NoErrorMsg
       MOVE.L  D0,A1
       CALL    Puts
NoErrorMsg:
       RTS

; Subroutines

; This sub uses exec's RawDoFmt to do string formatting while clipping
; any surplus data to fit the buffer size. Arps format routines have
; no overflow checks, and since the user is not guaranteed to act
; sanely, clipping is essential.
; I : Assumes a valid format string and buffer
; O : The format buffer contain the formatted string.
;   : D0.L = Size of formatted string.

Vanilla_Regs   REG     A2/A3/A6

VanillaDoFmt:
       MOVEM.L Vanilla_Regs,-(SP)
       MOVE.L  ArgArray(GP),A0
       LEA     ArgStream(GP),A1
       LEA     DoClippedBufferFill(PC),A2
       LEA     FormatClipInfo(GP),A3
       MOVE.L  FormatBufPtr(GP),(A3)
       MOVE.L  (A3),4(A3)
       ADD.L   #FormatBufSize,4(A3)
       MOVE.L  $4.W,A6                         ; exec
       CALL    RawDoFmt
       MOVE.L  (A3),D0
       SUB.L   FormatBufPtr(GP),D0
       MOVEM.L (SP)+,Vanilla_Regs
       RTS

; Assumes A0 is usable, not clear from autodocs whether this is true.
; (Default calling conventions and ROM code suggest it is)

DoClippedBufferFill:
       MOVE.L  (A3)+,A0
       CMP.L   (A3),A0
       BNE.S   NotYetAtBufEnd
       SUBQ.L  #1,A0
NotYetAtBufEnd:
       MOVE.B  D0,(A0)+
       MOVE.L  A0,-(A3)
       RTS

; The string section

Template:
       DC.B    'FormatString/a',0
HelpMsg:
       DC.B    'PForm - Pipe formatted lines.',10
       DC.B    'Usage: PForm "prefix %[width.limit]s postfix"',0
OutOfMem:
       DC.B    'Out of memory!',0
ReadError:
       DC.B    'Error while reading',0
WriteError:
       DC.B    'Error while writing',0
       CNOP    0,2

       END
