; Tee, split stdinput to stdoutput and multiple file streams.

       INCLUDE "/include/init.i"

; Local Equs

BufSize                EQU     512
Handles                EQU     BufSize

; Variable storage

       STRUCT  ArgArray,(2+1)*4                ; +1 'cause I'm parnoid
       SIZE

; Regs

AppendBool     EQUR    D4
StdInput       EQUR    D5
AllocStruct    EQUR    D6
ActualLength   EQUR    D7

FileNamePtrPtr EQUR    A2
HandlePtr      EQUR    A3
ErrorStrPtr    EQUR    A4

; Start

       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
       TST.L   D0
       BEQ.S   UnusableArgs
       BPL.S   ArgsMightBeOK
UnusableArgs:
       MOVE.L  (A2),ErrorStrPtr
       BRA     ErrorExit
ArgsMightBeOK:

; Fetch the -a switch and check if at least one file present.

       MOVE.L  4(A2),AppendBool
       BEQ.S   ArgsOK
       LEA     HelpMsg(PC),ErrorStrPtr
       MOVEQ   #1,D1
       CMP.L   D0,D1
       BEQ     ErrorExit
ArgsOK:

; Alloc tracked mem for the read write buf and the handles.

       LEA     OutOfMem(PC),ErrorStrPtr
       ADDQ.L  #1+1,D0                 ; Make space for StdOut + null termination
       LSL.L   #2,D0                   ; in the file handles longword array.
       ADD.L   #BufSize,D0             ; Buffer and Handles use same mem allocation
       MOVEQ   #0,D1
       CALL    ArpAllocMem
       MOVE.L  D0,AllocStruct          ; Data reg, so Z gets set
       BEQ     ErrorExit

; Initialize the handle and filename longword array pointers

       MOVE.L  ArgArray(GP),FileNamePtrPtr
       MOVE.L  AllocStruct,A0
       LEA     Handles(A0),HandlePtr

; Open the standard input and output

       CALL    Input
       MOVE.L  D0,StdInput
       CALL    Output
       MOVE.L  D0,(HandlePtr)+


;----- The open the files, and seek to end loop

       LEA     OpenError(PC),ErrorStrPtr
OpenNextFile:
       TST.L   (FileNamePtrPtr)
       BEQ.S   AllFilesOpened

; If Appending, try to open (tracked) the file as an old file

       TST.L   AppendBool
       BEQ.S   NoAppendingRequested
       MOVE.L  (FileNamePtrPtr),D1
       MOVE.L  #MODE_OLDFILE,D2
       CALL    ArpOpen
       MOVE.L  D0,(HandlePtr)
       BEQ.S   TryToOpenNewFile

; Seek to the end

       MOVE.L  D0,D1
       MOVEQ   #0,D2
       MOVE.L  #OFFSET_END,D3
       CALL    Seek
       ADDQ.L  #1,D0
       BEQ     ErrorExit
       BRA.S   IncrementAndLoop
NoAppendingRequested:

; Open files (tracked) as new

TryToOpenNewFile:
       MOVE.L  (FileNamePtrPtr),D1
       MOVE.L  #MODE_NEWFILE,D2
       CALL    ArpOpen
       MOVE.L  D0,(HandlePtr)
       BEQ     ErrorExit

; Update pointers and loop back

IncrementAndLoop:
       ADDQ.L  #4,FileNamePtrPtr
       ADDQ.L  #4,HandlePtr
       BRA     OpenNextFile
AllFilesOpened:
       CLR.L   (HandlePtr)                             ; Null termination


;----- The single input multi output copy loop, check for ^C & initize

DoNextRdWrChunk:
       SUB.L   A1,A1
       CALL    CheckAbort
       MOVE.L  A1,ErrorStrPtr
       TST.L   D0
       BNE.S   ErrorExit

       LEA     WriteError(PC),ErrorStrPtr
       MOVE.L  AllocStruct,A0
       LEA     Handles(A0),HandlePtr                   ; Reset to start of array

; Read from the standard input until EOF or error

       MOVE.L  StdInput,D1
       MOVE.L  AllocStruct,D2
       MOVE.L  #BufSize,D3
       CALL    Read
       MOVE.L  D0,ActualLength
       BEQ.S   Exit
       BMI.S   ErrorExit

; Do the multiwrite, and loop back

DoNextWrite:
       MOVE.L  (HandlePtr)+,D1
       BEQ     DoNextRdWrChunk
       MOVE.L  AllocStruct,D2
       MOVE.L  ActualLength,D3
       CALL    Write
       CMP.L   D0,D3
       BNE.S   ErrorExit
       BRA     DoNextWrite


; 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

; The string section

Template:
       DC.B    'Files/...,-a=APPEND/s',0
HelpMsg:
       DC.B    'Tee - pipe fitting.',10
       DC.B    'Usage: Tee [-a] <File> [...]',0
OutOfMem:
       DC.B    'Out of memory!',0
OpenError:
       DC.B    'Tee-file could not be opened',10
WriteError:
       DC.B    'Error while writing to tee-file',0
       CNOP    0,2

       END
