#! /bin/bash
# Last edited on 2012-12-08 21:40:44 by stolfilocal
 
PROG_NAME=${0##*/}
PROG_DESC="create a cross-reference of identifiers matching a given regexp"
PROG_HELP=(
  "${PROG_NAME} '{REGEXP}' {FILENAME}.. "
)
PROG_INFO=(
  "\nNAME"
  "\n  ${PROG_NAME} - ${PROG_DESC}."
  "\n"
  "\nSYNOPSIS"
  "\n  ${PROG_HELP[@]}"
  "\n"
  "\nDESCRIPTION"
  "\n  Reads the C source files from the {FILENAME} list. Prints all" \
  "\n occurrences of the given {REGEXP}, marking each as 'D' for" \
  "\n definition, 'U' for use, sorted."
  "\nSEE ALSO"
  "\n  "
  "\nAUTHOR"
  "\n  Created 2006-04-29 by Jorge Stolfi, Unicamp"
)

# Internal options

# ----------------------------------------------------------------------
# COMMAND LINE PARSING

# Parse command line switches: 
fasulla=
while [[ ( $# -ge 1 ) && ( "/$1" =~ /-.* ) ]]; do
  if [[ ( $# -ge 1 ) && ( "/$1" == "/-fasulla" ) ]]; then 
    fasulla=1; shift; shift;
  else
    echo "unknown option $1" 1>&2 ;
    echo -e "usage:\n  ${PROG_HELP[@]}" 1>&2 ; exit 1 
  fi
done 

# Get basic geometry parameters
if [[ $# -ge 1 ]]; then
  regexp="$1"; shift;
  files=( "$@" ); shift $#;
else
  echo 'wrong number of arguments "'"$1"'" ...' 1>&2 ;
  echo -e "usage:\n  ${PROG_HELP[@]}" 1>&2 ; exit 1 
fi

if [[ $# -ne 0 ]]; then
  echo 'wrong number of arguments "'"$1"'" ...' 1>&2 ;
  echo -e "usage:\n  ${PROG_HELP[@]}" 1>&2 ; exit 1 
fi

# END COMMAND LINE PARSING
# ----------------------------------------------------------------------

# Prefix for temporary file names
tmp="/tmp/$$"

# Collects uses and definitions:
egrep -n -e "${regexp}" "${files[@]}" \
  | gawk \
      -v re="${regexp}" \
      ' BEGIN{  } 
        // {
          lin=$0;
          if (match(lin, /^[^ ]*[.][ch][:][0-9]+[:]/))
            { loc = substr(lin, 1, RLENGTH);
              txt = substr(lin, 1+RLENGTH);
              # See if it looks like a definition
              def = match(txt, /^([\#]define|[a-zA-Z])/); 
              while (match(txt, re))
                { occ = substr(txt, RSTART, RLENGTH);
                  if (occ == "") { data_error("empty match"); }
                  printf "%s%s:%s\n", loc, (def ? "D" : "U"), occ;
                  txt = substr(txt, RSTART+RLENGTH);
                }
            }
          else
            { data_error("bad format"); }
          next;
        }
        function data_error(msg)
        { printf "%s:%d:%s\n", FILENAME, FNR, $0 > "/dev/stderr";
          printf "%s:%d: ** %s\n", FILENAME, FNR, msg > "/dev/stderr";
          exit 1;
        }
      ' \
  > ${tmp}.occs
  
# Sorted printout
cat ${tmp}.occs \
  | sort -t':' -b +3 -4 +2 -3 +0 -1 +1 -2 \
  | gawk \
      ' BEGIN { oitm = ""; } 
        // { 
          lin = $0;
          itm = lin; 
          gsub(/^[^:]*[:][^:]*[:][UD][:]/, "", itm);
          if (itm != oitm) 
            { printf "\n  %s\n\n", itm; oitm = itm; }
          gsub(/[:]D[:]/, ": D ", lin);
          gsub(/[:]U[:]/, ": U ", lin);
          print lin;
        }
      '

/bin/rm -f ${tmp}.occs
      
  
