############################################################################
#
#	File:     loadmap.icn
#
#	Subject:  Program to show load map of UNIX object file
#
#	Author:   Stephen B. Wampler
#
#	Date:     December 13, 1985
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#  
#     This program produces a formatted listing of selected symbol classes
#  from a compiled file.  The listing is by class, and gives the
#  name, starting address, and length of the region associated with
#  each symbol.
#  
#     The options are:
#  
#  	-a Display the absolute symbols.
#  
#  	-b Display the BSS segment symbols.
#  
#  	-c Display the common segment symbols.
#  
#  	-d Display the data segment symbols.
#  
#  	-t Display the text segment symbols.
#  
#  	-u Display the undefined symbols.
#  
#  If no options are specified, -t is assumed.
#  
#  If the address of a symbol cannot be determined, ???? is given in
#  its place.
#  
############################################################################
#  
#  Notes:
#
#     The size of the last region in a symbol class is suspect and is
#  usually given as rem.
#  
#     Output is not particularly exciting on a stripped file.
#  
############################################################################
#
#  Requires: UNIX
#
############################################################################

record entry(name,address)

procedure main(args)
   local maptype, arg, file, nm, ldmap, tname, line, text, data, bss
   local SPACE, COLON, DIGITS, HEXDIGITS, usize, address, name, nmtype
   initial {
      if *args = 0 then stop("usage: loadmap [-t -d -b -u -a -c -l] file")
      SPACE := '\t '
      COLON := ':'
      DIGITS := '0123456789'
      HEXDIGITS := DIGITS ++ 'abcdef'
      ldmap := table(6)
      ldmap["u"] := []
      ldmap["d"] := []
      ldmap["a"] := []
      ldmap["b"] := []
      ldmap["t"] := []
      ldmap["c"] := []
      tname := table(6)
      tname["u"] := "Undefined symbols"
      tname["a"] := "Absolute locations"
      tname["t"] := "Text segment symbols"
      tname["d"] := "Data segment symbols"
      tname["b"] := "BSS segment symbols"
      tname["c"] := "Common symbols"
      nmtype := "nm -gno "
      }
   maptype := ""
   every arg := !args do
      if arg[1] ~== "-" then file := arg
      else if arg == "-l" then nmtype := "nm -no "
      else if arg[1] == "-" then maptype ||:= (!"ltdbuac" == arg[2:0]) |
         stop("usage:  loadmap [-t -d -b -u -a -c -l] file")
   maptype := if *maptype = 0 then "t" else string(cset(maptype))
   write("\n",file,"\n")
   usize := open("size " || file,"rp") | stop("loadmap: cannot execute size")
   !usize ? {
      writes("Text space: ",right(text := tab(many(DIGITS)),6),"   ")
      move(1)
      writes("Initialized Data: ",right(data := tab(many(DIGITS)),6),"   ")
      move(1)
      write("Uninitialized Data: ",right(bss := tab(many(DIGITS)),6))
      }
   close(usize)
   nm := open(nmtype || file,"rp") | stop("loadmap: cannot execute nm")
   every line := !nm do
      line ? {
         tab(upto(COLON)) & move(1)
         address := integer("16r" || tab(many(HEXDIGITS))) | "????"
         tab(many(SPACE))
         type := map(move(1))
         tab(many(SPACE))
         name := tab(0)
         if find(type,maptype) then put(ldmap[type],entry(name,address))
         }
   every type := !maptype do {
      if *ldmap[type] > 0 then {
         write("\n\n\n")
         write(tname[type],":")
         write()
         show(ldmap[type],(type == "t" & text) |
            (type == "d" & data) | (type == "b" & bss) | &null,
            ldmap[type][1].address)
         }
      }
end

procedure show(l,ssize,base)
   local i1, i2, nrows
   static ncols
   initial ncols := 3
   write(repl(repl(" ",3) || left("name",9) || right("addr",7) ||
      right("size",6),ncols))
   write()
   nrows := (*l + (ncols - 1)) / ncols
   every i1 := 1 to nrows do {
      every i2 := i1 to *l by nrows do
         writes(repl(" ",3),left(l[i2].name,9),right(l[i2].address,7),
            right(area(l[i2 + 1].address,l[i2].address) |
            if /ssize then "rem" else base + ssize - l[i2].address,6))
         write()
         }
   return
end

procedure area(high,low)
   if integer(low) & integer(high) then return high - low
   else return "????"
end
