############################################################################
#
#	File:     fileprnt.icn
#
#	Subject:  Program to display characters in file
#
#	Author:   Ralph E. Griswold
#
#	Date:     November 21, 1989
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#     This program reads the file specified as a command-line argument and
#  writes out a representation of each character in several forms:
#  hexadecimal, octal, decimal, symbolic, and ASCII code.
#
#     Input is from a named file rather than standard input, so that it
#  can be opened in untranslated mode.  Otherwise, on some systems, input
#  is terminated for characters like ^Z.
#
#     Since this program is comparatively slow, it is not suitable
#  for processing very large files.
#
#     There are several useful extensions that could be added to this program,
#  including other character representations, an option to skip an initial
#  portion of the input file, and suppression of long ranges of identical
#  characters.
#
############################################################################
#
#  Requires: co-expressions
#
############################################################################
#
#  Program note:
#
#     This program illustrates a situation in which co-expressions can be
#  used to considerably simplify programming.  Try recasting it without
#  co-expressions.
#
############################################################################

procedure main(arg)
   local width, chars, nonprint, prntc, asc, hex, sym, dec
   local oct, ascgen, hexgen, octgen, chrgen, prtgen, c
   local cnt, line, length, bar, input

   input := open(arg[1],"u") | stop("*** cannot open input file")
   width := 16
   chars := string(&cset)
   nonprint := chars[1:33] || chars[128:0]
   prntc := map(chars,nonprint,repl(" ",*nonprint))

   asc := table("   |")
   hex := table()
   sym := table()
   dec := table()
   oct := table()
   ascgen := create "NUL" | "SOH" | "STX" | "ETX" | "EOT" | "ENQ" | "ACK" |
      "BEL" | " BS" | " HT" | " LF" |  " VT" | " FF" | " CR" | " SO" | " SI" |
      "DLE" | "DC1" | "DC2" | "DC3" | "DC4" | "NAK" | "SYN" |  "ETB" | "CAN" |
      " EM" | "SUB" | "ESC" | " FS" | " GS" | " RS" | " US" | " SP"
   hexgen := create !"0123456789ABCDEF" || !"0123456789ABCDEF"
   octgen := create (0 to 3) || (0 to 7) || (0 to 7)
   chrgen := create !chars
   prtgen := create !prntc
   every c := !&cset do {
      asc[c] := @ascgen || "|"
      oct[c] := @octgen || "|"
      hex[c] := " " || @hexgen || "|"
      sym[c] := " " || @prtgen || " |"
      }
   asc[char(127)] := "DEL|"			# special case

   cnt := -1	# to handle zero-indexing of byte count

   while line := reads(input,width) do {	# read one line's worth
      length := *line	# may not have gotten that many
      bar := "\n" || repl("-",5 + length * 4)
      write()
      writes("BYTE|")
      every writes(right(cnt + (1 to length),3),"|")
      write(bar)
      writes(" HEX|")
      every writes(hex[!line])
      write(bar)
      writes(" OCT|")
      every writes(oct[!line])
      write(bar)
      writes(" DEC|")
      every writes(right(ord(!line),3),"|")
      write(bar)
      writes(" SYM|")
      every writes(sym[!line])
      write(bar)
      writes(" ASC|")
      every writes(asc[!line])
      write(bar)
      cnt +:= length
      }
end
