!Utility to analyse a file whatever it is and print out something sensible.
!Various bits hacked from ANAL, NANAL, NLINES, DUMP, I:ANAL20, MOBANAL, MEXANAL
!etc courtesy of RWT, RMM, HMD etc.

!J.Butler Oct 29 84
!Modded JHB 18/2/87: -c option counts less likely to run into each other

%begin
%option "-nocheck-nodiag"
   %include "inc:util.imp"
   %include "inc:wildmat.imp"

   %ownstring(255)s="", outfile="",eventmess=""
   %string (80) %array Files (1 : 110)
   %integer Number of Files

   %constinteger vbrief = 0
   %constinteger brief  = 1
   %constinteger normal = 2
   %constinteger full   = 3
   %constinteger dump   = 4
   %constinteger count  = 5

   %ownbyte options = full
   %string (255) File, File 1, defext
   %integer I, cc, lc, start, filelen, sym, hpos=1
   %integer File Number = 0, Wild Lines = 0, Wild Chars = 0

   ! -- Dump stuff from DUMP

%routine countfile(%integer from, size)
   %integer p,q,r,max
   %constbytearray hex(0:15) = %c
   '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
   %integerarray tot(0:255), wid(0:15)
   %for p=0,1,255 %cycle; tot(p)=0; %repeat
   %for q=from,1,from+size-1 %cycle
      tot(byteinteger(q)) = tot(byteinteger(q))+1
   %repeat
   max=0
   %for q=0,1,15 %cycle; wid(q)=0; %repeat
   %for p=0,1,15 %cycle
   %for q=0,1,15 %cycle; wid(q)=tot(p*16+q) %if tot(p*16+q)>wid(q); %repeat
   %repeat
   %for q=0,1,15 %cycle
      p=0; r=1
      p=p+1 %and r=r*10 %while r-1<wid(q)
      %if p>2 %then wid(q)=p %else wid(q)=2
   %repeat
   newline; spaces(3)
   %for p=0,1,15 %cycle; spaces(wid(p)-1); printsymbol('-'); printsymbol(hex(p)); %repeat
   newlines(2)
   %for p=0,1,15 %cycle
      printsymbol(hex(p)); printsymbol('-'); space
      %for q=0,1,15 %cycle; write(tot(p*16+q), -(wid(q)+1)); %repeat
      newline
   %repeat
%end

%routine dumpfile(%integer from,size)
   %integer p,q
   %constinteger max=16
   p = from
   newline
   %cycle
     phex(p-from); spaces(2)
     %for q=p,1,p+max-1 %cycle
       sym = byteinteger(q); sym = '_' %unless ' '<=sym&16_7f<='~'
       %if q >= from+size %then space %else printsymbol(sym&16_7f)
     %repeat
     space
     %for q = p,1,p+max-1 %cycle
       space
       %if q >= from+size %then newline %and %return %else phex2(byteinteger(q))
     %repeat
     p = p + max
     newline
  %repeat
%end

   %routine Do one File (%string (255) File)
      %record (finfof) r
      %integer limit,integer i,j, p
      %string (255) s, dir, f, ext

      %routine count lines and junk(%integername lines, %realname junkfract, %c
      %integername maxline)
         %integer junk, ok, p, q
         lines = 0; junk = 0; ok = 0; maxline = 0; q=start
         %for p = start, 1, limit-1 %cycle
            %if byteinteger(p) = nl %start
               lines = lines + 1
               %if p-q>maxline %then maxline=p-q; q=p+1
            %elseif byteinteger(p) = 9 %or byteinteger(p) = 12 %or %c
            byteinteger(p) = 13 %or ' ' <= byteinteger(p) <= 126
               ok = ok + 1 
            %else
              junk = junk + 1
            %finish
         %repeat
         %if junk+ok = 0 %then junkfract = 0 %else junkfract = junk/(junk+ok)
         Wild Lines = Wild Lines + lines
      %end

      %routine genanal(%string (63) text)
         %integer lc,maxline
         %real jfract
         printstring("File is a")
         count  lines and junk(lc, jfract, maxline); write(lc, 1)
         print string ("-line "); printstring(text)
         %if jfract*100 < 1 %start
            printstring(" (longest = "); write(maxline, -1)
            printstring(" characters)")
         %else
            printstring(" ("); print(jfract*100, 0, 0)
            printstring("% unprintable)")
         %finish
         newline
      %end

      %routine mexanal
         %constinteger current flavour = 16_02
         %recordformat hf(%byte mark, version,%short checks,export,import,
                    %integer codesize,
                    %short reset,main,%integer ownsize,stacksize,spare1,spare2)
         %record(hf)%name header
         %integer i, offset

         %routine show entry(%integername i)
           %integer k
           phex(integer(i)); phex(integer(i+4))
           space; phex(integer(i+8)+offset)
           space; printstring(string(i+12))
           newline
           i = (i+byteinteger(i+12)+14)&\1
         %end

         %routine SHOW CHECKS(%integer bits)
         !Courtesy HMD
         %integer PROBE=16_8000, I=1
         %const%string TEXT = %c
         "-ARR-LOOP-CAP-OVER-ASS-STRASS-SASS-BASS-LINE-DIAG-TRACE-STACK-"
           printstring("Checks set at end:")
           %if bits = 0 %start
             printstring("none")
           %else
             %cycle
               %cycle
                 printsymbol(charno(text,i)) %if bits&probe # 0
                 i = i+1
               %repeat %until charno(text,i) = '-'
               probe = probe>>1
             %repeat %until probe = 16_0008
           %finish
           newline
         %end

         header == record(start)
         printstring("File is a Version"); write(header_version, 1)
         printstring(" (current)") %if header_version = current flavour
         printstring(" object module")
         newlines(2)
         i = sizeof(header)+header_export+header_import+header_codesize
         %unless i=filelen %start
            printstring("File is ")
            write(filelen-i,0); printstring(" bytes too long"); newline
         %finish
         show checks(header_checks)
         printstring("Code size "); phex(header_codesize)
         printstring("  Data size "); phex(header_ownsize); newline
         printstring("Stack size ") %and phex(header_stacksize) %and %c
         newline %unless header_stacksize=0
         printstring("Entries:"); newline
         offset = sizeof(header)+header_export+header_import
         spaces(17); phex(header_reset<<1+offset); printstring(" {reset}"); newline
         spaces(17); phex(header_main<<1+offset); printstring(" {main}"); newline
         i = start+sizeof(header); j = i+header_export
         show entry(i) %while i<j %and integer(i)<0
         printstring("References:"); newline
         i = j; j = i+header_import
         show entry(i) %while i<j %and integer(i)<0
      %end

      %routine mobanal
         %integer n, p, q, refs
         printstring("File is a Version 0 object module"); newlines(2)
         p = start+2
         printstring("Entries:            "); n = 1; refs = 0
         %while 0<byteinteger(p)<12 %cycle
           space %unless n=0; n = n+1; refs = refs + 1
       {}  phex(integer(p+12)); space
           printstring(string(p))
           %if n=3{6} %then newline %and n = 0 %else spaces(11-byteinteger(p))
           p = p+20
         %repeat
         p = p+2
         printstring(" none") %if refs = 0
         newline %unless n=0
         printstring("Code size: ");
         q = limit-2+shortinteger(limit-2)+40
         q = q-4 %if shortinteger(q-8)=16_6100
         phex(q-40-p)
         printstring("  Data size: ");phex(limit-4-q); newline
         printstring("References:         "); n = 1; refs = 0
         %while 0<byteinteger(q)<12 %cycle
           space %unless n=0; n = n+1; refs = refs + 1
       {}  phex(integer(q+12)); space
           printstring(string(q))
           %if n=3{6} %then newline %and n = 0 %else spaces(11-byteinteger(q))
           q = q+20
         %repeat
         q = q+2
         printstring(" none") %if refs = 0
         newline %unless n=0
      %end

      %routine cmobanal
         %constinteger entptoffset = 28
         %integer p

         %routine showval(%integername p, %string (63) text)
            printstring(text); space; phex(integer(p))
            %if p-start # entptoffset %start ;!Not meaningful for entry point
               %if integer(p) # 0 %start
                  printstring(" ("); write(integer(p), 0); printstring(")")
               %finish
               printstring(" bytes")
            %finish
            newline
            p = p + 4
         %end

         printstring("File is a 'C' object module"); newlines(2)
         p = start + 4
         showval(p, "text size:                     ")
         showval(p, "data size:                     ")
         showval(p, "blank data size:               ")
         showval(p, "symbol table size:             ")
         showval(p, "text relocation directory size:")
         showval(p, "data relocation directory size:")
         showval(p, "entry point:                   ")
      %end

      %integer check

      %routine rhex1(%integername p, val)
         eventmess = "Unterminated" %and %signal 15 %if p >= start+filelen
         val = byteinteger(p); p = p + 1
         val = val - '0'; val = val - 7 %if val > 9
      %end

      %routine rhex2(%integername p, val)
         %integer a
         rhex1(p, val); rhex1(p, a)
         val = val<<4 + a
         check = check + val
      %end

      %routine rhex4(%integername p, val)
         %integer a
         rhex2(p, val); rhex2(p, a)
         val = val<<8 + a
      %end

      %predicate looks like an m68 file(%integername p)
         %integer c, i, lgth, q
         p = start
         p = p + 1 %while byteinteger(p) = 13 %or byteinteger(p) = nl
         %false %unless byteinteger(p) = 'S' %and byteinteger(p+1) = '0'
         q = p+2; check = 0
         rhex2(q, lgth)
         %for i = 1,1,lgth %cycle; rhex2(q, c); %repeat
         check = check + 1
         %false %unless check & 255 = 0
         %true
      %end

      %routine m68mobanal(%integer p)
         %integer i, s, type, offset, add, lgth
         %integerarray buffer(0:255)

         %routine fail(%string (63) text)
            eventmess = text
            %signal 15
         %end

         %on %event 15 %start; -> abort; %finish

         printstring("File is a Motorola M6800")
         %if p = start %then printsymbol('0')
         printstring(" OMF file"); newlines(2)
         add = -1
         %cycle
            s = byteinteger(p); p = p + 1; fail("'S' Missing") %if s # 'S'
            type = byteinteger(p); p = p + 1 ; fail("Funny Type value") %c
            %unless '0' <= type <= '9'
            check = 0
            rhex2(p, lgth); rhex4(p, offset)
            %for i = 0, 1, lgth-3 %cycle
               rhex2(p, buffer(i))
            %repeat
            check = check + 1
            fail("Checksum failure") %if check&255 # 0
            %if type = '0' %start
               printstring("Module Name: ")
               %for i = 0, 1, lgth-4 %cycle; printsymbol(buffer(i)); %repeat
               newline
            %elseif type = '1'
               %if offset # add %start
                  %if add = -1 %start
                     printstring("Areas loaded:")
                  %else
                     phex4(add-1)
                  %finish
                  newline
                  phex4(offset); printstring(" - ")
                  add = offset
               %finish
               add = add + lgth - 3
            %elseif type = '9'
               %exit
            %finish
            p = p + 1 %while byteinteger(p) < ' ' %and p < start+filelen
            fail("Unterminated") %if p >= start + filelen
         %repeat
         phex4(add-1); newline
         %return
abort:
         printstring("(Corrupt: ".eventmess.")"); newline
      %end

      %on %event 3,9 %start
         printstring("Analyse ";file;" fails: ";event_message); newline
         %return
      %finish

      File Number = File Number + 1
      newline %if file number > 1
         %if options = vbrief %then to lower(file) %and printstring(file) %elsestart
            ext = "" %unless file -> s.(".").ext
            i = 0 %while ext -> s.(".").ext
            to upper(ext)
            %if file -> dir.("$:").f %then s = "" %else s = ninfo(file)
            %if options = brief %then printstring(s) %elsestart
               unpack finfo(s, r)
               printstring("File: ".file); spaces(2)
               %if s # "" %start
                  printstring("  Last altered: "); printstring(r_date)
                  spaces(2); printstring(r_time); spaces(4)
                  newline
                  printstring("Permissions: Self: "); printsymbol(charno(r_perms, 1))
                  printstring("  Others: "); printsymbol(charno(r_perms, 2))
                  printstring("   Archive: "); printsymbol(charno(r_perms, 3))
               %finishelse printstring("is a pseudo-file")
               newline
               filelen = filesize(file)
               printstring("Size: "); phex(filelen)
               printstring(" ("); write(filelen, 0); printstring(") bytes")
               %if s # "" %start
                  printstring(" in ")
                  write(r_blocks, 0); printstring(" block")
                  %if r_blocks > 1 %then printstring("s, ") %else printstring(", ")
                  write(r_extents, 0); printstring(" extent")
                  printsymbol('s') %if r_extents > 1
               %finish
               newline
               Wild Chars = Wild Chars + filelen
               %if filelen # 0 %start
                  %if options > normal %start
                     connectfile(file,0,start,filelen); limit = filelen+start
                     %if options = dump %start
                        dumpfile(start, filelen)
                     %elseif options = count
                        countfile(start, filelen)
                     %else
                        %if {ext = "MOB" %or ext = "MEX") %and}%c
                        byteinteger(start) = 16_fe %start ;!Object module
             %if byteinteger(start+1) = 1 %or byteinteger(start+1) = 2 %start
                              !New format object module - various flavours
                              mexanal
                           %elseif byteinteger(start+1) = 0
                              mobanal
                           %elseif byteinteger(start+1) = 16_cc ;!C object module
                              cmobanal
                           %elseif byteinteger(start+1) = 16_bc
                              printstring("File is an M68000 assembler object module"); newline
                           %elseif byteinteger(start+1) = 16_fe
                              printstring("File is an S-Algol object module"); newline
                           %else
                              genanal("character file or object module of unknown type")
                           %finish
                        %elseif (ext = "MOB" %and byteinteger(start) = '{') %or ext = "COM"
                           genanal("command file")
                        %elseif ext = "MOB" %and byteinteger(start) = 'S'
                           m68mobanal(start)
                        %elseif ext = "OBJ" %and looks like an M68 file(p)
                           m68mobanal(p)
                        %else
                           genanal("character file")
                        %finish
                        heapput(start)
                     %finish
                  %finish
               %else
                  printstring("File is empty"); newline
               %finish
            %finish
         %finish
   %end

   %routine Do Wild Files (%string (80) Wild File)
      %string (127) Line, A, B, Directory
      %integer Flag = 0, Index

      %on 3,9 %start
         %if Flag = 0 %start
            Event_Message = "No message" %if Event_Message = ""
            Print String ("Analyse ".wildfile." fails: ".Event_Message)
            New Line
            %return
         %finish
         -> End Load
      %finish

      Directory = ""
      %if Wild File -> Directory .(":"). Wild File %start; %finish
      Directory = Directory . ":" %if Directory # ""
      Open Input (3, Directory."Directory")
      Select Input (3)
      Flag = 1
      Index = 1
      Number of Files = 0
      %cycle
         Read Line (Line)
         %if Matches (Line, Wild File) %start
            %if Wild File = "*" %start
               %continue %if Line -> A .("."). B
            %finish
            Number of Files = Number of Files + 1
            Files (Number of Files) = Directory.Line
         %finish
         Index = Index + 1
      %repeat
End Load:
      Close Input
      Select Input (0)
      Index = 1
      %while Index <= Number of Files %cycle
         Do One File (Files (Index))
         Index = Index + 1
      %repeat
   %end

   %on %event 9, 15 %start
      -> abend
   %finish

   file = ""; defext = ""
   define param("FILE", file, pam major)
   define param("EXTension",defext,0)
   define param("OUT", outfile, pam new group)
   define enum param("VBrief,Brief,Normal,Full,Dump,Count", options, 0)
   process parameters(cli param)
   open output(1, outfile) %if outfile # ""
   prompt("File(s):")
   readline(file) %while file = ""
   %while File -> File 1 .(","). File %cycle
      file 1 = file 1.".".defext %unless defext = "" %or file 1 -> s.(".")
      %if Wildness (File 1) # 0 %then Do Wild Files (File 1) %c
                                %else Do One File (File 1)
   %repeat
   %if File # "" %start
      file = file.".".defext %unless defext = "" %or file -> s.(".")
      %if Wildness (File) # 0 %then Do Wild Files (File) %c
                                %else Do One File (File)
   %finish
   %if File Number > 1 %and options >= normal %start
      newline
      Print String ("Total of ")
      Write (Wild Chars, 0)
      Print String (" character")
      Print Symbol ('s') %if Wild Chars # 1
      %if options = full %start
         Print String (" on ")
         Write (Wild Lines, 0)
         Print String (" line")
         Print Symbol ('s') %if Wild Lines # 1
      %finish
      Print Symbol ('.')
   %finish
abend:
   New Line
%endofprogram
