#!/bin/sh

# 
# Copyright (c) 1996, 1998 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Lonhyn T. Jasinskyj.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#        This product includes software developed by the NetBSD
#        Foundation, Inc. and its contributors.
# 4. Neither the name of The NetBSD Foundation nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# 


#
# This is a shell script that behaves rather like the makedepend(1)
# that comes with X11 except that it also understands VPATH
#
# makedepend-sh takes three sets of arguments. It parse the line of
# arguments left to right and until it encounters a '--' it
# applies command line switches to itself. After the first '--' all
# args are accumulated and will be used as arguments to a C preprocessor
# (such as cc -E). All arguments after the second '--' are filenames,
# usually object files. More formally:
#
# makedepend [-a] [-s varname] [-v] [-f file] [-p preprocessor] [-V pathlist]
#            [-e scriptfile] [-m mrulesfile [-x excludeobjs]] -- 
#            [preprocessor args] -- file1 [file2 [...]]
#
# Flags:
#
#   -a
#        append to the end of the output file (usually Makefile) instead
#        of replacing anything following a marker that looks like:
#           "# DO NOT DELETE THIS LINE -- makedepend-sh depends on it"
#        Appending is generally not the desired behavior but may be
#        useful if makedepend needs multiple, different invocations.
#
#   -s varname
#        generate a 'varname = filenames...' line in the output that is 
#        derived from the files given to makedepend. In this way a list
#        of object files can be given and they will be searched for
#        (possibly in a VPATH) and then their full pathnames will be
#        given computed and accumulated in a 'varname=' line. Thus, in a
#        Makefile the writer need only write 'OBJS=' (and possibly
#        give a VPATH if the source and object trees are seperate) and
#        makedepend will do the rest. It is often useful to choose
#        varname as "SRCS" or "CSRCS". Only C files are candidates
#        for inclusion in the source list.
#
#   -v
#        be verbose, can be repeated for more verbosity.
#
#   -f file
#        instead of using Makefile, write to this file
#
#   -m mrulesfile
#        a file in the style of a Makefile containing suffix rules
#        with transformations. Since stock make is incapable of
#        applying a transformation rule if both files are not
#        in the local directory, a specific rules has to be generated
#        by makedepend in this case. If the -m flag is given the
#        appropriate suffix rule will be extracted and an explicit
#        command will be generated.
#
#   -x excludeobjs
#        if the -m flag is given then this should contain a list
#        of objects that should not have a rule generated for them.
#
#   -i ignorefiles
#        these files will simply be ignored, this is useful if you
#        give makedepend a list of files to process but not all of
#        the files exist yet.
#
#   -p preprocessor
#        the command to invoke the preprocessor if 'cc -E' is not
#        appropriate. Normally one should quote this (e.g. -p "gcc -E")
#
#   -V pathlist
#        the path is a colon-seperated list of paths that will be searched
#        for the source file. Normally only '.' is searched.
#
#   -e scriptfile
#        the regular expression is treated as the path of an arbitrary
#        sed program that will be applied to the list of dependencies.
#        This will happen after all of the dependencies are composed
#        but before they are appended or any effects of the -s flag
#        are added. This is useful for excluding files in "/usr/include"
#        and "gcc-lib". 
#
#   -c cksumfile
#        compute a checksum (using "cksum") of this file. If a
#        file called .dep_cksum exists in the local directory then
#        also compare the new cksum output with the contents of that
#        file. If they are the same and a file called .depends also
#        exists locally then one need not redo the makedepend
#        dependency processing and can instead take .depends and
#        either append it to the Makefile or replace the previous
#        dependencies (as appropriate depending on the -a flag)
#        Finally, if new dependencies are generated, place a copy in
#        the file given as cksumfile.
#
#   --
#        
#   -preprocessor_args      
#        whatever arguments appear in this section get passed on to the
#        pre-processor.
#
#   --
#
#   file1 [file2 [...]]
#        the filenames given to makedepend. These are often object files
#        whose names will first be transformed to source files by
#        replacing final '.o' with '.c'.  These will then be sought in
#        either '.' or a list of paths given with the -V flag.
#
#
# Output:
#
#   For each file listed makedepend will output:
#
#      1. if the file was an object file, then a line like
#            file.o: dirname/file.c
#
#      2. multiple lines of the form:
#            dirname/file.c: inc1.h inc2.h
#            dirname/file.c: inc3.h inc4.h
#            etc.
#
#      3. if the -s flag was given, then a set of lines like
#         SRCS = \
#            dirname/file1.c \
#            dirname/file2.c \
#            etc.
#
#      4. if the -m flag is give then for each source file that is
#         not in the local directory, an explicit rule will be
#         generated. The actions will be derived from a parsed
#         makefile-style file.
#

P=`basename $0`; export P

CPP="cc -E"
MARKER="# DO NOT DELETE THIS LINE -- makedepend-sh depends on it"

CKSUM=cksum
CKSUM_STORE=.dep_cksum
DEPEND_STORE=.depends

init_mode() {
    mode="md_args";
}

incr_mode() {
    if [ "$mode" = "md_args" ] ; then
        mode="cc_args";
    else
        if [ "$mode" = "cc_args" ] ; then
            mode="fn_args";
        fi
    fi
}



append_arg() {
    case "$mode" in
	md_*)  arg_md="$arg_md $1" ;;
	cc_*)  arg_cc="$arg_cc $1" ;;
	fn_*)  arg_fn="$arg_fn $1" ;;
    esac
}


append_srcs() {
    append_to="$1"
    srcs_file="$2"

    #
    # since there is no way to echo a backslash reliably
    # with builtin echo or /bin/echo (not knowing their pedigree)
    # echo must be avoided for output.  Use printf or, in this case, awk.
    #
    cat $srcs_file | \
      awk 'BEGIN { printf("\n#\n# '$mksrcs' generated by makedepend\n#\n'$mksrcs' = ")} \
           {} { for (i = 1; i <= NF; i++) { printf("\\\n   %s ", $i) }} \
           END { printf("\n\n") }' >> $append_to
}

append_rules() {
    append_to="$1"
    rules_in="$2"
    ( \
       echo; echo '#'; \
       echo '# generated rules for source files that are not local'; \
       echo '# to this directory.'; \
       echo '#'; \
       cat $rules_in; \
       echo; echo; echo) >> $append_to
}


generate_awk() {
cat > $1 <<!
#
# this old-style awk script will read a makefile where suffix
# rules are being defined and generate a shell script defining
# each rule as a variable. Thus a rule such as ".c.o:" would 
# be transformed such that after evaluating the resulting 
# piece of Bourne shell code the variable "mkrule__c_o" will
# be set to the action for that suffix. As is obvious, the
# escaping mechanism is very primitive and will not do well
# for many characters encountered in suffix rules. However, it
# is sufficient for disambiguating all the common suffixes
# and is good enough. 
#
# Note that what defines a field depends on the implementation. As
# an example, under HP-UX 10.x a trailing field separator with
# nothing after it gives an NF that is one less than all of
# the other awks that I have seen.
#
BEGIN { FS=":" }

\$1 ~ /^\.[a-zA-Z]+\.[a-zA-Z]+$/ {
    # if we have not seen this suffix rule before record it and
    # go into rule collecting mode
    if (exists[\$1] == 0) {
        exists[\$1] = 1
        names[\$1] = \$1
        cur_suf = \$1   # the current suffix to collect into
        suff_list=suff_list " " cur_suf
        collect = 1
    } else {
        collect = 0
    }
    next
}

/^[\t#].*/  {
    if (collect) {
        # remove the initial tab and put a \t there instead
        action[cur_suf]=action[cur_suf] "\t" substr(\$0,2) "\\\n"
    }
    next
}

/.*/  {
        # collection stops when we encounter the first
        # line that does not begin with a TAB or '#' sign
        collect = 0
}

END {
    # when done print out the shell code
    for (n in names) {
        printf("mkrule_");
        for (j = 1; j <= length(n); j++) {
            c = substr(n,j,1)
            if (c ~ /[a-zA-Z0-9]/) {
                printf("%s", c)
            } else {
                # replace all wierd characters with underscores
                printf("_")
            }
        } 
        print "='" action[n] "'"
    }

    #
    # turn a string that looks like ".c.o .c .l.o .y.o" into
    # an array that contains the suffixes and then print
    # that array into a single varibale where each suffix
    # appears once
    #
    len = length(suff_list)
    for (j = 1; j <= len; j++) {
        #
        # if the character is not a seperator, accumulate it into the
        # current suffix, if it is, record the previously accumulated suffix
        #
        c = substr(suff_list,j,1)
        if (c ~ /[ .]/) {
            if (sfx !~ /^$/) {
                sfxs[sfx] = sfx
                sfx = ""
            }
        } else {
            sfx = sfx c
        }
    }

    # print out the line that will set the shell variable
    printf("mksuffixes='")
    first = 1
    for (s in sfxs) {
        if (first == 1) { printf("%s", s); first = 0 } else { printf(" %s", s) }
    }
    printf("'\n")
}
!
} # end of generate_awk()



#
# if the mrules flag was given, go and generate and awk script,
# run it on the file containing the make rules, and then set
# shell variables containing make rules
#
eval_mrules()
{
    mrulefile=$1
    eval `mktemp awkscr`
    eval `mktemp mkrule_scr`

    generate_awk $awkscr
    awk -f $awkscr < $mrulefile > $mkrule_scr
    . $mkrule_scr
    if test "$verbose" -gt 1; then
        (echo "make rules are:"; echo "==============="; \
         cat $mkrule_scr; echo "===============") | sed -e "s/^/$P: /"
    fi
}

#
# exists_rule(): true if a rule exists for this src and product suffix
#
exists_rule()
{
    # src and product suffixes
    s_sfx=$1
    p_sfx=$1
    
    rulename=\$mkrule__${s_sfx}_${prod_sfx}
    test -n "`eval echo $rulename`" && return 0
    return 1
}

#
# called with a var name as an arg this will produce a line of 
# shell code to be evaluated. Thus to set the variable FOO to
# the name of the next available temp file use
#     eval `mktemp FOO`
# This is necessary to get the auto-increment of tmp_id since 
# things in single quotes are normally run in sub-shells
#
mktemp()
{
    export tmp_id
    tmp_id=`expr ${tmp_id:-0} + 1`
    name=/tmp/mkdep.$$.${tmp_id}
    echo "$1=$name; tmp_id=$tmp_id"

}

#
# explicit_rule():
#    this will take arguments with information about the source
# file and the product to be generated. It then decides whether
# an explicit rule needs to be made and if so adds it to a file
# which can then be tacked onto the make file later.
#
explicit_rule()
{
    src="$1"
    obj="$2"
    srcdir="$3"
    rulefile="$4"
    excludes="$5" 

    test "$srcdir" = '.' && return

    src_suffix=`echo $src | sed -n -e 's/.*\.\([^.][^.]*\)$/\1/p'`
    obj_suffix=`echo $obj | sed -n -e 's/.*\.\([^.][^.]*\)$/\1/p'`

    # figure out the name of the variable where the action is stored
    rulename=\$mkrule__${src_suffix}_${obj_suffix}

    #
    # go and see if this object is one of the excluded objects, if
    # so, bag this phase
    #
    for x in $excludes; do
        test "$x" = "$obj" && {
            test "$verbose" -gt 1 && \
                echo $P: skipping explicit rule for: $obj; \
            return; \
        }
    done

    #
    # generate the explicit rule and add it to the rulefile if we
    # have a suffix transformation
    #
    if test -n "`eval echo $rulename`"; then
	echo ${obj}: ${srcdir}/${src} >> $rulefile

        #
        # use POSIX printf to get the newlines right, must use double quotes.
        # also replace all occurences of '$<' with the name of the
        # source file since '$<' is only appropriate in inference
        # rules and '$?' is not a correct substitute when the target
        # depends on multiple files, including .h files.
        #
        eval printf \"$rulename\" | \
            sed -e "s;\$<;$srcdir/$src;g" >> $rulefile
    fi
}

in_list_p()
{
    f="$1"
    ignores="$2"

    for x in $ignores; do
        test "$x" = "$f" && return 0
    done
    return 1
}

#
# see if we already have a file of dependencies and see if it is
# usable (i.e. the cksum on the cksumfile is still the same)
#
have_usable_depends()
{
    ckmatchfile="$1"
    if test -n "$ckmatchfile" -a -s "$CKSUM_STORE" -a -f "$DEPEND_STORE"; then
        $CKSUM $ckmatchfile > $CKS 2> /dev/null && \
          diff "$CKSUM_STORE" "$CKS" > /dev/null 2>&1 || \
          return 1  # no usable dependencies prestored
        test "$verbose" -gt 0 && echo $P: using dependencies from previous run
        return 0
    fi

    # return FALSE
    return 1
}

#
# maybe_store_depends:
#    we should squirrel away the dependencies that have been generated
#    if the user has specified a -c flag on the command line (i.e. a
#    file to checksum against). If that is the case then store the
#    dependencies in DEPEND_STORE and calculate a new checksum into
#    CKSUM_STORE.
#
maybe_store_depends()
{
    ckmatchfile="$1"
    depsfile="$2"

    if test -n "$ckmatchfile" ; then

        $CKSUM "$ckmatchfile" > $CKS 2> /dev/null && \
          test -s "$CKS" || return 1

        # only store if different
        diff "$CKS" "$CKSUM_STORE" > /dev/null 2>&1 || cdiff=yes
        diff "$depsfile" "$DEPEND_STORE" > /dev/null 2>&1 || ddiff=yes

        if test "$cdiff" = yes -o "$ddiff" = yes; then
            cp "$CKS" "$CKSUM_STORE" && cp "$depsfile" "$DEPEND_STORE" || \
              return 1
            test "$verbose" -gt 0 && echo $P: stored new dependencies for reuse
        fi
    fi

    # success unless we botched things above and returned a 1
    return 0
}


#
# gen_depends():
#   generate the depend information into the file named in TMP.
#
gen_depends()
{
    #
    # go through the source files. Find each in VPATH
    # do the compile there and accumulate the dependencies
    #
    true > $TMP   # truncate TMP
    
    for f in $arg_fn; do
    
      # let us assume that all products are '.o' files
      prod_sfx='o'
    
      # figure out the stem: i.e. /a/b/c/foo.o --> foo
      stem=`echo $f | sed -e 's/.o$//'`
    
      # did we actually find a source file
      found_src=no
    
      # if this file is in the ignore list, skip it
      in_list_p "$f" "$ignobjs" && { test "$verbose" -gt 0 && \
          echo $P: ignoring file: $f; continue; }
    
      #
      # go through each possible directory and try each possible
      # source file
      #
      for d in $VPATH; do
    
        #
        # as soon as we find the first src file, we can go onto the
        # next argument (i.e. object file)
        #
        test "$found_src" = yes && break
    
        for src_sfx in $mksuffixes; do
    
          # cannot make a file from itself
          test "$prod_sfx" = "$src_sfx" && continue
    
          #
          # if we were given rules and there is no rule for this
          # transformation then skip this suffix combination
          #
          test -n "$mrules" && exists_rule $src_sfx $prod_sfx || continue
    
          s=${stem}.${src_sfx}
          test "$verbose" -gt 2 && echo "$P: checking for: $d/$s"
    
          if [ -f "$d/$s" ]; then
    
            found_src=yes
      
            test "$verbose" -gt 0 && echo $P: processing $d/$s
      
            case "$src_sfx" in
              c*|C*)
                #
                # This is a file that is a candidate for cpp:
                #
                # process the output of cpp and massage it, then sort
                # it uniquely and as a final step remove the trailing
                # numbers or any other cruft that various compilers
                # might generate. Then remove any lines that
                # make the file depend on itself by removing any
                # dependencies on files not ending in .h or .H
                #
                # also add it to the list of srcs
                #
                test -n "$mksrcs" && echo $d/$s >> $SRCS
                test "$verbose" -lt 2 && errout='2> /dev/null'
    
                eval $CPP $arg_cc $d/$s $errout | \
                  sed -n -e "s;^\# [0-9][0-9 ]*\"\(.*\)\";$f: \1;p" | \
                  grep -v "//\$" | \
                  grep -v "$s\$" | grep -v command | grep -v built-in | \
                  sed -e 's;\([^ :]*: [^ ]*\).*;\1;' \
                  >> $TMP
                ;;
    
              *)
                test "$verbose" -gt 1 && echo "$P: no preprocessing for $s" ;;
            esac
          
            #
            # explicit rules can be generated for all sorts of sources
            #
            explicit_rule "$s" "$f" "$d" "$RULES" "$xobjs" 
      
            break
          fi
        done
      done
      test "$found_src" = no && echo Did not find the source for: $f
    done
    
    #
    # optionally invoke a user-specified sed script on the output. In either
    # case, sort the output uniquely first.
    #
    if test -n "$sedprog"; then
        sort -u < "$TMP" | sed -f "$sedprog" > "$TMP"_a
        mv "$TMP"_a "$TMP" 2>&1 > /dev/null
    else
        sort -u -o "$TMP" "$TMP"
    fi
    
    
    #
    # now see if we want the SRCS= line and generation of
    # explicit rules per file for source files not in our
    # current directory
    #
    test -n "$mksrcs" && test -r "$SRCS" && append_srcs "$TMP" "$SRCS"
    
    #
    # if we wanted explicit rules then these have been diverted to
    # a seperate file, include them now
    #
    test -n "$mrules" && test -r "$RULES" && append_rules "$TMP" "$RULES"
}



#
# this is the beginning of the main routine
#
init_mode

eval `mktemp TMP`
eval `mktemp ED`
eval `mktemp RULES`
eval `mktemp SRCS`
eval `mktemp CKS`

VP=.; export VP
mksrcs=""; export mksrcs
MF=Makefile; export MF
append=no; export append
verbose=0; export verbose
sedprog=""; export sedprog
mrules=""; export mrules
xobjs=""; export xobjs
ignobjs=""; export ignobjs
cksumfile=""; export cksumfile

trap '/bin/rm /tmp/mkdep.$$.* > /dev/null 2>&1' 0 1 2 3 15

while [ $# -gt 0 ] ; do
    if [ "$mode" = "md_args" ]; then
        #
        # these are arguments to the makedepend script itself
        #
        case "$1" in
	  -V*) shift; VP=$1 ;;  # VPATH
	  -s*) shift; mksrcs=$1 ;;    # generate a SRCS= line based on OBJS
	  -f*) shift; MF=$1 ;;  # dependfile name
	  -p*) shift; CPP="$1" ;;  # pre-processor to use
	  -e*) shift; sedprog="$1" ;;
	  -c*) shift; cksumfile="$1" ;;  # file to cksum (e.g. Makefile.in)
	  -m*) shift; mrules="$1" ;; # the make rules file
	  -x*) shift; xobjs="$1" ;; # no rules for this list of objs
	  -i*) shift; ignobjs="$1" ;; # list of objs to be ignored
	  -a*) append=yes ;;
	  -v*) verbose=`expr $verbose + 1` ;;
	  --)  incr_mode ;;
	  *)   echo $P: Illegal argument \'"$1"\' >&2; exit 1 ;;
	esac
	shift
    else
	case "$1" in
	  --)  incr_mode ;;
	  *)   append_arg "$1" ;;
	esac
        shift;
    fi
done

if test -n "$xobjs" -a -z "$mrules"; then
    echo $P: cannot use -x without -m >&2
    exit 1
fi

if test "$append" = yes -a -n "$mrules"; then
    echo $P: cannot use append mode if -m is specified >$2
    echo $P:    since multiple action definitions are prohibited >&2
    exit 1
fi

test -n "$mrules" && eval_mrules $mrules

VPATH="`echo .:$VP | sed -e 's/:/ /g'`"

test "$verbose" -gt 1 && echo "$P: CFLAGS are: $arg_cc"
test "$verbose" -gt 1 && echo "$P: VPATH dirs are: $VPATH"

#
# the list of suffixes is either found in the make rules
# and set by calling eval_mrules() earlier, or
# if we did not process any, then '.c' will do
#
mksuffixes=${mksuffixes:-'c'}

#
# if we find that we can avoid generation of new dependency
# rules by looking in prestored rules then do that. This
# way, even though Makefile was regenerated and rules
# removed we can use the old ones if Makefile.in was
# not changed. Ideally, make would have an include directive
# but that is not universally the case.
#
have_usable_depends "$cksumfile" && cp "$DEPEND_STORE" "$TMP" || gen_depends


if [ "$append" = "yes" ]; then
    # if the marker is already in the file, do not put a second one in
    grep "^$MARKER" $MF > /dev/null 2>&1 || echo "$MARKER" >> $MF
    cat $TMP >> $MF
else
    #
    # create the ed script that depends on $MARKER and on whether we are
    # appending. Then feed it to ed. Since ed exits at the first failure
    # when taking its commands from a file, we must take care of the
    # case where we already have a $MARKER while constructing the script
    #
    if grep "^$MARKER" $MF > /dev/null 2>&1 ; then
        #
        # we delete and then add since there may be no line .+1 if
        # the marker was the last line
        #
        (echo "/^$MARKER"; echo '.,$d') >> $ED
    fi

    #
    # chose not to do a here script for neatness
    #
    echo '$' >> $ED
    echo 'a' >> $ED
    echo "$MARKER" >> $ED
    echo '.' >> $ED
    echo "r $TMP" >> $ED
    echo 'w' >> $ED
    echo 'q' >> $ED
    echo 'q' >> $ED  # just in case ed gets botched, prevent a hang
    ed $MF < $ED > /dev/null 2>&1
fi

#
# decide whether we are using the checksum shortcut and storing depends
# and if so store the new checksum and new depends
#
maybe_store_depends "$cksumfile" "$TMP" || \
    echo $P: error storing dependencies for later use

/bin/rm $TMP $ED > /dev/null 2>&1

