!Program to interpret the $:TRACE special file.
!J. Butler Sep 1984

!Parameters are <context>,<infile>/<outfile>
! defaults: context = current one, infile = $:TRACE, outfile = TT:

%include "system:config.inc"
%include "inc:fs.imp"
%include "inc:util.imp"

%ownbyte disflag = 0
%ownstring (15) infile = "$:TRACE", outfile = "", param = ""
%conststring(7) this fs = "BRAVO"
%constinteger trace in length = 23
%constinteger repct tab       = 56
%conststring (19) nocomm = "*** No command ***"
%conststring (19) norply = "*** No reply ***"
%conststring (19) error  = "*** Error ***"
%conststring (3) nulltxt = "..."

%recordformat trace fm(%integer context, inout, %string(55) text)
%recordformat tbuff fm(%integer q,
                       %record(trace fm)%array t(0 : tbuffs),
                       %integer p)
%constinteger trace in  = 0
%constinteger trace out = 1
%constinteger null      = 16_80
%constinteger null in   = 16_80
%constinteger null out  = 16_81
%constinteger eof       = 2

%constinteger wait in   = 0
%constinteger wait out  = 1

%begin
   %record(tbuff fm)%name tbuff
   %record(trace fm)%name t
   %record (trace fm) tlast
   %owninteger s, l, i, blockcount, tbuffptr, first, last, state, type = 0
   %owninteger context = 0, lowcon = 0, highcon = 0, firstline=0, lastprim = 0
   %owninteger lastcont = -1, lastinout = -1, col = 1
   %string(127) command = "", text = "", lastcomm = "", lasttext = ""

   %routine dumpcount(%integername count)
      !Dump the count in square brackets tabbed to a constant value, if > 1
      !and throw a newline
      %if count > 0 %start
         spaces(repct tab - col)
         printstring("[") %and write(count, 0) %and printsymbol(']') %unless count = 1
      %finish
      newline; col = 1
   %end

   %routine maybe dump header or count
      !dumps a heading on-the-fly for the first call, dumps a count otherwise.
      !Program will stop printing after printing it if the user has typed a space.
      !Program will resume if user types another space, newline etc.

      %integer c
      %if firstline = 0 %start
         newline; col = 1
         printstring(this fs); col = col + length(this fs)
         printstring(": Trace for context"); write(context, 2)
         col = col + 22; spaces(repct tab - col - 4)
         printstring("[repeat count]")
         newlines(2); col = 1
         firstline = 1
      %else
         dumpcount(blockcount)
      %finish

      c = testsymbol
      %if c <= ' ' %start
         %if c = ' ' %start
            %cycle
               c = testsymbol 
               %exit %if 0 <= c <= ' '
            %repeat
         %finish
      %finish
   %end

   %routine printname(%string (*) %name command)
      !Print the primitive name contained in the supplied string. Two funnies..
      !Note if it doesn't recognise the primitive it dumps the first char.
    
      %integer primitive
      %string (63) s
      %conststring(7) %array prim('A':']') = %c
      "Openmod","Rename ","C      ","Delete ","Permit ","Finfo  ","General","Uclose ",
      "Readbak","Setdir ","Close  ","Logon  ","Logoff ","Ninfo  ","Copyfil","Pass   ",
      "Quote  ","Readda ","Openr  ","Openw  ","Reset  ","V      ","Writeda","Readsq ",
      "Writesq","Readfil","[      ","\      ","FSCntrl"

      %if command = nulltxt %start
         printstring("       ")
      %elseif command -> ("Connect").s
         printstring("*Conn* ")
      %elseif command -> ("Disconnect").s
         printstring("*Disc* ")
      %else
         primitive = charno(command, 1)
         primitive = primitive & 16_df %if 'a' <= primitive <= 'z'
         %if 'A' <= primitive <= ']' %then printstring(prim(primitive)) %else %c
         printsymbol(primitive) %and spaces(6)
      %finish
      col = col + 7
   %end

   %predicate same as last
      !Returns true if the command/response pair just read in is the same as
      !the last one printed.
      %false %if blockcount = 0 ;!Only true for first time around
      %true %if command = lastcomm %and text = lasttext
      %false
   %end

   %record (trace fm) %map next valid record(%integer state, context, 
   %integername type)
      !Return the next interesting record. Ignore records for other contexts
      !and flag one of a few error conditions via TYPE.
      !TYPE: Tracein, Traceout - normal input records. Return ptr to record and
      !                          advance buffer pointer.
      !TYPE: null in, null out - expected an input & got output or vice versa
      !                          Usually indicates missing reply.
      !TYPE: eof               - End of file.

      %record (trace fm) %name t

      %while tbuffptr <= last %cycle
         t == tbuff_t(tbuffptr & tbuffs); tbuffptr = tbuffptr + 1
         %if context < 0 %start
            !Intermingled format
            type = t_inout ; %result == t
         %elseif t_context = context
            %if (t_inout = trace in %and state = wait in) %or %c
            (t_inout = trace out %and state = wait out) %start
                type = t_inout; %result == t
            %else
                tbuffptr = tbuffptr - 1
                type = 16_80 ! state ; %result == nil
            %finish
         %finish
      %repeat
      type = eof; %result == nil
   %end

   %on 3,9 %start;  %stop;  %finish
!   context = rsap
!   define int param("Context", context, 0)
!   define param("INfile", infile, 0)
!   define param("OUTfile", outfile, pam newgroup)
!   define param("DISplay", disflag, 0)
!   process parameters(cli param)
!   open output(1, outfile) %if outfile # ""

   param = cli param
   to upper(param)
   open output(1, outfile) %if param -> param.("/").outfile
   infile = "$:TRACE" %unless param -> param.(",").infile
   length(param) = length(param)-1 %while charno(param, length(param)) = ' '
   param = substring(param, 2, length(param)) %while charno(param, 1) = ' '
   %if param = "" %then context = rsap %else context = stoi(param)
   
   connect file(infile, 0, s, l)

   tbuff == record(s)
   %if context < 0 %then context = 0 %and disflag = 1
   newline

   %if disflag = 0 %start ;!Display each context in turn
      %if context = 0 %then lowcon = 0 %and highcon = 31 %else %c
      lowcon = context %and highcon = context
      %if lowcon # highcon %start
         printstring("Searching contexts 0-31: You are context")
         write(rsap, 3)
         newline
      %finish
      %for context = lowcon, 1, highcon %cycle
         blockcount = 0; lastprim = 0
         firstline = 0; command = nocomm; lastcomm = "Z"; lasttext = norply
         !Note lastcomm. By setting it to "Z" (which there's a good chance it was)
         !if the first reply has no command, it will show as a blank not a <nocomm>
         first = tbuff_p; last = tbuffs + tbuff_q
         state = wait in; tbuffptr = first
!t!      write(first, 3); write(last, 3)
   
         %cycle
!t!         write(state, 3)
            t == next valid record(state, context, type)
!t!         write(type, 3); write(tbuffptr, 3); newline
            %if type & 16_7f = trace out %start ;!Reply coming back
               %if type = null out %start
                  %if command -> ("Disconnect").text %then text = nulltxt %else text = norply
               %finishelse text = t_text
   
               %if same as last %start
                  blockcount = blockcount + 1
               %else
                  maybe dump header or count
                  printname(command)
                  spaces(trace in length - length(command))
                  printstring(command); lastcomm = command
                  printstring(" / ")
                  col = col + trace in length + 3
                  printstring(text); lasttext = text
                  col = col + length(text)
                  blockcount = 1
               %finish
               state = wait in
   
            %elseif type & 16_7f = trace in
               %if type = null in %start
                  %if lastprim & 16_5f = 'X' %or lastprim = 'Z' %then %c
                  command = nulltxt %else command = nocomm
               %else
                  command = t_text
                  lastprim = charno(command, 1)
               %finish
               state = wait out
   
            %elseif type = eof
               %exit
            %else
               command = error
            %finish
         %repeat


         %if state = wait out %start
            maybe dump header or count
            printname(command)
            spaces(trace in length - length(command))
            printstring(command)
            printstring(" / ".norply)
            newlines(2)
         %else
            newline %if firstline # 0
         %finish
      %repeat

   %elseif disflag = 1 ;!Intermingled
      first = tbuff_p; last = tbuffs + tbuff_q
      tbuffptr = first
      tlast = 0; tlast_inout = 999
      %cycle
         t == next valid record(0, -1, type)
!t!      write(t_inout, 3); write(t_context, 3)
         %exit %if type = eof
         %if t_inout = lastinout %and t_context = lastcont %and t_text = lasttext  %start
            blockcount = blockcount + 1
         %else
            dumpcount(blockcount); blockcount = 1
            write(t_context, 3); printstring(": "); col = 7
            %if type = trace in %start
               printname(t_text)
               spaces(trace in length - length(t_text))
               printstring(t_text)
               col = col + trace in length
            %elseif type = trace out
               spaces(34); printstring("/ ")
               printstring(t_text)
               col = col + length(t_text) + 36
            %finish
            lastinout = t_inout; lastcont = t_context; lasttext = t_text
         %finish
      %repeat
      dumpcount(blockcount)
   %finish
   newline

   %if infile="$:TRACE"  %start
      Prompt("Keep raw trace (Y/N):")
      skipsymbol %while nextsymbol&16_5f # 'Y' %and nextsymbol&16_5f # 'N'
      readsymbol(i)
      %if i&16_5f = 'Y' %start
         prompt("Where?:")
         skip symbol %while next symbol <= ' '
         read line(out file)
         open output(1, out file)
         %for i = 0, 1, l-1 %cycle
            printsymbol(byteinteger(s+i))
         %repeat
         close output
         selectoutput(0)
      %finish
    %finish

%end %of %program
