#!/bin/sh
#
# Copyright (c) 1994, Sun Microsystems, Inc.
#

#ident	"@(#)makestyle	1.2	94/09/01 SMI"

#
# Utility to check style of makefile. Prints out violations of guidelines.
# This utility checks the conformance of makefile to the guidelines 
# described in document "Makefile Standards and Guidelines"
# /shared/ON/general_docs/make_std.txt
# It runs the makefile through two passes. In the first pass it does static
# analysis of the makefile and in the second pass it includes all the 
# included files and then finds out duplicate definitions of macros and 
# suffix rules besides a few other things.
# Created 5/27/94, Suhas Patil.(suhas@patil.eng.sun.com)
#

usage() 
{
	echo '
usage: makestyle [-v] [-i] [-s] [-h] [-p] filename
	-v : verbose 
	-i : show errors from included files also
	-s : skip second pass
	-h : provide heuristic checks which are sometimes wrong
	-p : perform some of the more picky checks
    
'
	exit 1
#	-d : debug mode used during maintenance of makestyle 
}

#
# function lawk1
#

lawk1()
{

PATHARG=$1

# set value of verbose option in case it is not set in environment
# and the script is being run by itself.
if [ x$VOPT = "x" ]
then
	VOPT=0 ; export VOPT
fi

if [ x$DOPT = "x" ]
then
	DOPT=0 ; export DOPT
fi

WD=`pwd`; export WD

nawk '
BEGIN {
	debug='$DOPT'
	patharg="'$PATHARG'"
	verbose='$VOPT'
	src = "'$SRC'"
	pwd = "'$WD'" 
	heur = '$HOPT'
	picky = '$POPT'

# Bourne shell reserved words are defined here
	brw[1] = "if"
	brw[2] = "then"
	brw[3] = "else"
	brw[4] = "fi"
	brw[5] = "cd"
	brw[6] = "pwd"
	
	dott[1]  =".DEFAULT"
	dott[2]  =".DONE"
	dott[3]  =".FAILED" 
	dott[4]  =".IGNORE"
	dott[5]  =".INIT"
	dott[6]  =".KEEP_STATE"
	dott[7]  =".MAKE_VERSION"
	dott[8]  =".NO_PARALLEL"
	dott[9]  =".PARALLEL"
	dott[10] =".PRECIOUS"
	dott[11] =".SCCS_GET"
	dott[12] =".SILENT"
	dott[13] =".SUFFIXES"
	dott[14] =".WAIT"

	fmt2="%s:%3d: No \"pwd\" between \"%s\" and \"%s\" \n";
	fmt3="%s:%3d: Install dependency is not in terms of ROOT: %s\n"
	fmt4="%s:%3d: Command used without macro reference\n"
	fmt5="%s:%3d: Use of an old style or commonly used var\n"
	fmt6="\nFound: %s\nExpecting: %s\n"
	fmt7="%s:%3d: Missing file name\n"
	fmt8="%s:%3d: Bad usage. %s\n"
	fmt8_V="%s:%3d: Bad usage. %s\n\tUse:\n\t\tMYMACRO =%s\n\t\t$(MYMACRO): %s\n"

	fmt9="\t\tFound    : %s\n"
	fmt10 = "%s:%3d: target all is not the first one\n"
	fmt11 = "%s:%3d: Expression exceeds 20 lines. Continuation discarded\n"
	fmt12 = "%s:%3d: Absolute install command instead of INS.dir or INS.file used\n"
	fmt13 = "%s:%3d: Absolute sccs command used\n"

	has_all=0;			# set if target all present
	all_dep = 0;		# set if install depends on all
	ident=0;			# set if identifier is found
	has_keepstate=0 ;	# set if .KEEP_STATE is found
	target=0 ;			# set to the type of target being processed
	reg_target=1 ;		# target being processed is regular
	suf_target=2 ; 		# target being processed is suffix rules
	prc_target=3 ;		# target being processed is percent rules
	ins_target=4 ;		# install target should not have actions. to keep track
	copyright=0;		# set if Copyright is found
	hasname=0 ;			# set if filename is found
	inheader=1;			# set when the program starts
	first_target=1;		# is set when first target is encountered. Later the 
						# logic verifies if the target is all and if not 
						# issues an error message

	# find the relative path of the makefile using
	# pwd and src and see if it is entered.
	relpath = ( substr(pwd, (length(src) + 2 ) ));
	if ( length (relpath) > 1 ) 
		relpath = sprintf("%s/%s", relpath, FILENAME) ;
	else
		relpath = sprintf("%s", FILENAME) ;
	expected=relpath;
	}

#
## Handle comments
#
	/^[ \t]*#/	\
	{
		if ( inheader == 1)
			handle_header();
		else
		{
			if ( debug )
				print ("COMMENT|", $0);
			next
		}
	}

NR==1 {
		if ( ! match($0, /^#/ ))
			printf("%s:%3d: No file header\n", patharg, NR);
	}
#
## Default line pre-processing
#

	# Begin processing each line
	# this should be done after the comments have been discarded.
	{
		# find out all the positions of : and =
		fcol = 0;
		scol = 0 ;
		tcol = 0 ;
		feq = 0 ;
		seq = 0 ;
		teq = 0 ;
		match($0, /:/)
		fcol = RLENGTH
		match($0, /\=/)
		feq = RLENGTH
		match($0, /:.*:/)
		scol =  RLENGTH
		match($0, /\=.*\=/)
		seq  =  RLENGTH
		tcol = RLENGTH
		teq  = RLENGTH

# printf("DEFAULT| %s\n", $0)
		amacro = 1 ;

		# Check to see if the line too long.
		# Do not check if the line has been joined because it
		# was a continuation denoted by \
		if (length ($0) > 80 )
			printf("%s:%3d: Line too long\n", patharg, NR);

		# join folded lines
		if ( match ( $0, /\\$/ ) )
		{
			origline = ""
			join_lines() $0;
			$0 = origline
		}	

		# See if either old style or commonly used variables are used
		if  ( match ($0, /[ \t:=\({]DIRS[ \t:=\)}]/ ) ||
			  match ($0, /[ \t:=\({]INC[ \t:=\)}]/ ) ||
			  match ($0, /[ \t:=\({]MFLAGS[ \t:=\)}]/ ) ||
			  match ($0, /[ \t:=\({]TXTS[ \t:=\)}]/ ) ||
			  match ($0, /[ \t:=\({]PRINTER[ \t:=\)}]/ ) ||
			  match ($0, /[ \t:=\({]ENV[ \t:=\)}]/ ) ||
			  match ($0, /[ \t:=\({]HOME[ \t:=\)}]/ ))
		{
			printf(fmt5, patharg, NR);
			if ( verbose )
				printf ("		%s\n", $0);
		}
			
		# check for trailing white spaces
		if (match($0, /[ \t]+$/))
			if ( picky == 1 )
				printf("%s:%3d: Trailing spaces\n",patharg, NR);
			
		# check to see leading space char
		if (match($0, /^[ ]+[^ \t]*/))
			printf("%s:%3d: Leading spaces\n",patharg, NR);

		# do for every line
		for ( i = 1; i <= NF ; i++)
		{
			# check for absolute path
			if (match($i, /^\//))
			{
				printf("%s:%3d: Absolute path used\n",patharg, NR);
				if ( verbose )
					printf("		%s\n", $0);
			}


			# check for "*. or .*"
			if ( match ( $i, /\*\./ ) )
				if ( picky == 1 )
				{
					printf("%s:%3d: Wildcard *. used\n",patharg, NR);
					if ( verbose )
						printf("		%s\n", $0);
				}
			if ( match ( $i, /\.\*/ ) )
				if ( picky == 1 )
				{
					printf("%s:%3d: Wildcard .* used\n",patharg, NR);
					if ( verbose )
						printf("		%s\n", $0);
					
				}
		}
	}

#
## Handle macros
#
/^[^\t].*\=/	{
# printf("DEFMAC | %s\n", $0)

		# See if it is pattern replacement rule.
		if ( match($0, /:.*%.*=.*:/ ))
		{
			# if = precedes : then it is a macro else it is pattern replacement
			# rule ( is this comment right ???)
			if ( !( match($0, /\=/) < match($0, /:/)))
			{
				# regex matches string of following type 
				# "^$(LIB_PIC:%=$(ROOTPICSDIR)/%): "
				if (  match( $0, /^\$\(.*:%.*=.*%.*\).*:[ \t]*.*/)   )
				{
					myindex=match (    $0, /\):/   ) 
					lastpart = substr($0, myindex+2 );
					firstpart = substr($0, 1, myindex);
					target=prc_target ;
					# Give bad usage message and suggest better usage
					if (verbose )
						printf(fmt8_V, patharg, NR, $0, firstpart, lastpart);
					else
						printf(fmt8, patharg, NR, $0);

				}
				if (debug)
					printf("Skipping to the next input line\n");
				next
			}
		}
		# if conditional assignment, treat like a comment. do nothing
		else if (match($0, /:=[ \t]/))
		{
			if ( debug )
				print("CND ASG|", $0);
		}
		# if shell is used , treat like a comment. do nothing
		else if (match($0, /:sh/))
		{
			if ( debug )
				print("MACRO  |", $0);
		}
		# matches $(VOBJ1): $$(VOBJ1:%.o=%.s)
		else if (match($0, /:.*=/))
		{
			# id stmts like OBJECTS = $(XBOX_OBJS:%=$(OBJS_DIR)/%)
			# as macros
				amacro = 0 ;
			if (  match($0, /\=.*:.*=/))
			{
				# this statement makes sure that statement does not 
				# get defined as macro because of an = 
				# OBJECTS     = $(XBOX_OBJS:%=$(OBJS_DIR)/%)

				amacro = 1 ;
			}
		}
		else
		{
			# here means a macro
			if ( match ($0, /\+=/ ) )
				if ( ! match ($0, /[ \t]\+=/ ) )
				{
					printf("%s:%3d: Space missing before += operator\n", patharg, NR);
					if ( verbose )
						printf("		%s\n", $0);
				}	
					
			target = 0 ;		# this is not a target
			# set the flag to false to cover the condition where there is no 
			# header at all. i.e. no comments in the begining
			inheader = 0 ;
			if ( debug )
				print ("MACRO  |" ,$0);
			if ( match( $0, /^CFLAGS/ ))
			{
				if ( match ($0, /-[ID]/))
					printf("%s:%3d: -I or -D appears in the CFLAGS macro\n", patharg, NR);
				if ( match ($0, /-L/))
					printf("%s:%3d: -L appears in the CFLAGS macro \n", patharg, NR);
			}
			else if ( match( $0, /^CPPFLAGS/) )
				if ( match ($0, /-L/))
					printf("%s:%3d: -L appears in the macro of CPPFLAGS\n", patharg, NR);
		}
		if ( amacro )
		{
#			if (debug)
#				printf(" skipping this line as it is a macro:%s\n", $0);
			next
		}	
	}

#
## Handle targets
#
/.*[^"].*:.*[ \t]?/	 && !/".*:/ && /^[^\t]/{
# printf("DEFTARG| %s\n", $0)

	# pattern matched is with no quote before :
					if (match($0, /.*%.*:/))
					{
						target=prc_target ;
						if ( debug )
							print("PRCNT  |", $0);
					}
					else if (match($0, /\..*\..*[^%][ \t]*:/))
					{
						if ( debug )
							print ("SUFFIX |" ,$0);
						target= suf_target ; 	
					}
					else if (match($0, /.*%[ \t]*:=[ 	]/))
					{
						# Handle conditional assignment
						if ( debug )
							print("CNDN ASG |", $0);
					}
					else if ( match ( $0, /^\./) )
					{					
						# Handle dot-targets
						if ( match ( $0, /.KEEP_STATE:/) )
						{
							if ( debug )
								print ("KP_STTE|" ,$0);
							has_keepstate=1
						}
						else
						{				
							matched =0
							for (i = 1; i < 15 ; i++)
							{
								if( match ($0, dott[i]))
								{
									matched = 1;
								}
							}
							if ( matched )
							{
								if (debug)
									printf("DOT TRG| %s\n", $0);
							}

							# must be regular target
							target = reg_target ;
							if ( debug )
								print (".RGTRGT|" ,$0);

						}
					}	
					else
					{
						# must be regular target
						target = reg_target ;

						if (match ( $0, /[ \t]all.*:/) || 
							match ($0, /^all.*:/) )
						{
							has_all = 1
							if ( debug )
								print ("ALL TAR|" ,$0);
							# make sure all target is the first one
							if (target == reg_target )
								if ( ! first_target )
									printf(fmt10,patharg, NR);
						}
						else if ( match ( $0, "^install:") )
							handle_install() ;
						if ( debug )
							print ("RG_TRGT|" ,$0);
	
						first_target = 0 ;
					}
#					if (debug )
#						printf("Skipping as target found\n");
					next;
			}
#
## Handle Include files
#
	/^[ \t]include[ \t]/	{
						target=0;
						if ( debug )
							printf("INCLUDE| %s \n", $0);
						next
					}
#
## Handle empty lines
#	
	NF == 0			{
						if ( debug )
							printf ("NEWLN  |\n");
						# an empty line implies the target definition, if in 
						# target, is over. hence set the flag
						target=0;
						next
					}
					
#
# Handle actions 
#
# /^[\t]+.*[^\=]/ this format treats $(AS) tmp.$(@F:.o=).s as macro
/^[\t]+.*/ {


# printf("DEFACT | %s\n", $0)

		# actions begin with tab as the first char and do 
		# not have "=" ,like macros
		if ( target ) 
		{
			if (target == ins_target )
				printf("%s:%3d: Install target has action rules\n", patharg, NR);
			# strip @, -, !, ? if they appear at
			# the begining of the line
			# Make sure that the cd and pwd occur in pairs also make
			# sure that the commands are not used by themselves

			check_cmds()
		}
		else
		{
			# if here means error. Warn the user
			printf("	makestyle: Error in parsing action at %s\n	%s	Run makestyle with -d flag\n", NR, $0);
			if ( debug )
				printf("ER_PRSE|%s\n", $0);
		}
	}
	

#
## Default line post-processing
#
	{
		# we came here because the line is non comment non empty line hence we
		# are out of header
		inheader = 0 ;	
	}

#
# All the functions are defined here 
#

function handle_header() {
	while ( inheader )
	{
		if (match($0, /^#[ \t]*.*ident/))
		{
			# Following is the definition as per /shared/ON/gen*/key*
			if (( match($0, 
					/#(ident|pragma ident)[ \t]*".Z..M.[\t].I.[\t].E. SMI/)) \
				|| (match($0, /#(ident|pragma ident)[ \t]*".W.[\t].E. SMI/)))
					# match for SunSoft South format
			{
				ident=1
				if ( debug )
					print ("EIDENT |", $0);
			}
			else
			{
				ident=1
				printf("%s:%3d: Bad format of identifier\n", patharg, NR);
				if ( verbose )
				{
					printf("		%s\n", $0);				
					printf("		Expecting:#ident\\t\"pZppMp\\tpIp\\tpEp SMI\"\n		OR\n")
					printf("		         :#ident\\t\"pWp\\tpEp SMI\"\n")
					printf("		where: p in the string denotes %% char\n");
				}
			}
		}
		else if (match($0, /^#[ \t].*[ ]*Copyright/))
		{
			copyright =1
			if ( debug )
				print ("CPYRGHT|", $0);
		}
		else if (match($0, /^#[ \t]*[^ \t]*Makefile[^ \t]*[ \t]*$/))
		{
			if ( debug )
				print ("MKFILNM|", $0);
			# see if relative path is entered
			split($0, arr1);
			# remove "#" from the list
			$0 = arr1[2]

			# print found and expected filenames
			if (debug)
				printf(fmt6,  $0, expected);
			if ( ( match ($0, expected ) && match (expected , $0 )))
			{
				# file has a Makefile name
				hasname = 1 ;
			}
			else 
			{
				# store what appears to be a Makefile name to be later 
				# printed
				foundname=$0 ;
			}
		}
		else if ( match ($0, /^$/))
		{
			if ( debug )
				print ("EMPTYL |", $0);
		}
		else if ( ! match ( $0, /^#/) ) 
		{
			inheader = 0 ;
			if ( ! ident )
				printf("%s:%3d: No identifier found\n", patharg, NR);
			if ( ! copyright )
				printf ("%s:%3d: No Copyright found\n", patharg, NR);
			if ( ! hasname )
			{
				printf(fmt7, patharg, NR );
				if ( verbose )
				{
					printf("		Expecting: %s\n", expected);
					if ( foundname != "")	
						printf(fmt9, foundname);
				}
			}
		}
		if ( inheader == 1) 
			getline
	}
}

function handle_install() {
	# Make sure the target dependencies are specified in 
	# terms of ROOT
	target = ins_target ;
	if ( debug )
		print ("INSTALL|" ,$0);
	# split all the elements of target dependencies
	elements= split($0, arr);
	# remove "install" from the list
	delete arr[1]
	# remove dot-targets like .WAIT
	for (i in arr)
	{
		if (match(arr[i], /^\./))
			delete arr[i]
	}
	# process individual elements to see ROOT
	for (i in arr)
	{
		if (match(arr[i], "all") )
		{
			# make sure install depends upon all
			all_dep = 1;	
			delete arr[i]
		}
		else
		{
			tmp = arr[i];
			split(tmp, arr2, /\(|{/ );
			tmp=arr2[2];
			split(tmp, arr3, /\)|}/ );
			fdep=arr3[1] ;
			if (!match(fdep, /ROOT/))
			{
				if (!match(fdep, /SUBDIRS/))
					if ( heur == 1)
						printf(fmt3, patharg, NR, arr[i]);
			}
		}
	}
	if ( all_dep == 0)
		if ( heur == 1)	# this is a heuristic type message
			printf("%s:%3d: install does not depend on all\n", patharg, NR);
}

function check_cmds() {
	# strip @, -, !, ? if they appear at
	# the begining of the line
	action = $0;
	# remove first tab
	action = substr ( action, 2 )
	for (i = 1; i < 4; i++)
	{
		if ( match ( action, /^[\t]*@/))
			action = substr ( action, 2 )
		if ( match ( action, /^[\t]*\-/))
			action=substr(action, 2)
		if ( match ( action, /^!/))
			action=substr(action, 2)
		if ( match ( action, /^\?/))
			action=substr(action, 2)
	}
	$0 = action;
	if ( debug )
		printf ("ACTION1| %s\n",  $0);

	# Check to make sure that $< does not happen in a regular target but
	# is allowed in % rules
	if ( target == reg_target )
	{
		if  ( match( $0, /\$</) )
			printf("%s:%3d: $< appears in the action for a regular target\n", patharg, NR);
	}

	# Make sure that the cd and pwd occur in pairs also make 
	# sure that the commands are not used by themselves

	if ( !match(action, /\\$/))
	{
		action = action " \\"
	}
	# replace \ at the end of the line by blank space
	gsub(/\\/, " ", action)
	# the command may be piped hence look for pipe and semicolon
	nsplit = split(action, actarr, /;|\|/ );
	for (k=1; k <= nsplit ; k++)
	{
		if ( actarr[k] != "" )
		{
			if ( match ( actarr[k], /cd[ \t]/ ))
			{
				if ( match ( actarr[k+1], /pwd/ ) ||
					 match ( actarr[k+1], /PWD/ ) )
				{
				; 
				}
				else
					# no pwd between cd and  ....
					printf(fmt2, patharg, NR, actarr[k], actarr[k+1]);
			}
			else
			{
				# make sure that the commands are
				# not used by themselves
				# if the command does not start with
				# "$" then it is the command itself
				if ( !match(actarr[k], /^[ \t]*\$[\({]/ ))
				{
					split(actarr[k], fcmd);
					matched =0
					for (i = 1; i <= 6 ; i++)
					{
						if( match (fcmd[1], brw[i]))
						{
							matched = 1;
						}
					}
					if (! matched )
					{
						# no use of absolute sccs commands
						if ( match( fcmd[1], /sccs/) )
							printf(fmt13, patharg, NR)
							
						# if install command used instead of INS.dir etc
						else if ( match( fcmd[1], /install/) )
							printf(fmt12, patharg, NR)
						else 
						{
							printf(fmt4, patharg, NR);
							if ( verbose )
								printf("		%s\n", $0 );
						}

					}
	
				}

			}
		}
	}
}

function join_lines() {
	# See if the statement continues on the next line by 
	# seeing the last char. if it is \ then keep reading next line.
	# stop appending after 20 lines have been read to make sure 
	# awk does not complain for internal buffer overrun.

	JOINED=0;
	lcount =  1; 
	
	while ( match ( $0, /\\$/ ) )
	{
		gsub(/\\$/, " ");
		orig = NF
		# check if line longer than 80 chars
		if (length ($0) > 80 )
			printf("%s:%3d: Line too long\n", patharg, NR);

		if ( lcount < 20)
		{
			NF = orig + NF
			origline = origline $0
			$0 = origline
			# set flag so that it does not complain for more than
			# 80 chars
			JOINED =1 ;
		}
		lcount = lcount + 1 
		if (lcount == 21)
		{
			if ( verbose )
				printf(fmt11,patharg, (NR - 21));
		}
		getline

	}
	if ( JOINED )
	{
		origline = origline $0
	}
}

#
## Postprocess here
#

END {
	if ( heur == 1)
		if ( ! has_all )
			printf("%s:%3d: Target all not defined in the file\n", patharg, NR);
	if ( ! has_keepstate )
		printf("%s:%3d: Target .KEEP_STATE not defined in the file\n",
			patharg, NR);		
} '  `basename $1`

}


#
# function lawk2
#

lawk2()
{

PATHARG=$1 ; export PATHARG
INPUTFILE=`basename $PATHARG`

# set default value of verbose option 
if [ x$VOPT = "x" ]
then
	VOPT=0 ; export VOPT
fi

# set default value for this file
if [ x$IOPT = "x" ]
then
	IOPT=0 ; export IOPT
fi

# set default value of verbose option 
if [ x$HOPT = "x" ]
then
	HOPT=0 ; export HOPT
fi

# set default value for this file
if [ x$POPT = "x" ]
then
	POPT=0 ; export POPT
fi

# set default value for this file
if [ x$NOPT = "x" ]
then
	NOPT=0 ; export NOPT
fi

# set default value for this file
if [ x$DOPT = "x" ]
then
	DOPT=0 ; export DOPT
fi
  
if [ $NOPT  -eq 0 ] 		# suppress make -DD pass. Script Debugging option
then
	if [ x$DOPT = "x1" ]
	then
		echo "Running make -DD to include files and resolve vars..." 
	fi
	AWK2IN=/tmp/make$$.in ; export AWK2IN
	TMP=/tmp/make$$.tmp ; export TMP
	/bin/rm -f $AWK2IN

	# Introduce a dummy target at the very begining of thefile. This target
	# would get executed when make parses the file and then this would fail.
	# After the failure the make file has all the included files and variables
	# resolved
	echo "DUMMY:\n\tI_Want_To_Fail_Here" > $TMP
	cat $INPUTFILE  >> $TMP
	/bin/mv $TMP $AWK2IN
	/bin/mv .make.state .makestyle.make.state > /dev/null 2>&1

	make -DD -S -f $AWK2IN > $TMP  2> /tmp/make$$.err
	# if no make.rules
#	make -DD -r -S -f $AWK2IN > $TMP  2> /tmp/make$$.err
	SIZE=`wc -c /tmp/make$$.err | awk '{print $1}'`
	grep "sh: I_Want_To_Fail_Here: not found" /tmp/make$$.err > /dev/null 2>&1
	STATUS=$?
	if [ $STATUS != 0 ] 
	then
		echo "\n\nFATAL ERROR: Second pass could not be run as make failed with following output:"
		cat /tmp/make$$.err

		# move all temporary files to their nonuniq names so that 
		# hundreds of files are not left in /tmp after doing 
		# makestyle on the complete source base but if being done 
		# interactively somebody could go through them if required
		/bin/mv /tmp/make$$.err /tmp/make.err
		/bin/mv $AWK2IN /tmp/make.in
		/bin/mv $TMP /tmp/make.tmp

		exit 1
	fi

	# remove last few lines which contain error message
	sed '/End of makefile .tmp.make'$$'.in/,/Error code 1/d' $TMP > $AWK2IN
	sed '/^DUMMY/,/I_Want_To_Fail_Here/d' $AWK2IN > $TMP
	/bin/mv $TMP $AWK2IN
	sed -e 's/\/tmp\/make'$$'.in/'$INPUTFILE'/g' /tmp/make$$.in > $TMP
	/bin/mv $TMP $AWK2IN

	#
	# awk chokes on lines longer than approx 2000 chars. Hence truncate lines
	# to smaller size
	#
	sed -e 's/^/!|/g' $AWK2IN > /tmp/makestyle1 2>/dev/null
	fold -w 2024 /tmp/makestyle1 > /tmp/makestyle1.1  
	grep "^!|" /tmp/makestyle1.1 > /tmp/makestyle2
	sed -e 's/^!|//g' /tmp/makestyle2 > /tmp/makestyle3
	/bin/mv /tmp/makestyle3 $AWK2IN
	/bin/rm /tmp/makestyle1 /tmp/makestyle1.1 /tmp/makestyle2 

else
	echo "$NOPT Skipping \"make -DD \""
	AWK2IN=$INPUTFILE
fi
nawk 'BEGIN {
		verbose='$VOPT'
		incfile='$IOPT'
		heur='$HOPT'
		picky='$POPT'
		debug='$DOPT'
		patharg="'$PATHARG'"
		inputfile="'$INPUTFILE'"
		SRC = "'$SRC'"
		INTARGET=0;     # is 1 if in the actions of a target
		src_re = SRC "/" ;
		gsub( /\// , ".",  src_re) ;

		SUFFIXRULE = 0 ;
		LIBRARY=0;
		MAKELIB=0;
		count = 0;
		dott[1]  =".DEFAULT"
		dott[2]  =".DONE"
		dott[3]  =".FAILED"
		dott[4]  =".IGNORE"
		dott[5]  =".INIT"
		dott[6]  =".KEEP_STATE"
		dott[7]  =".MAKE_VERSION"
		dott[8]  =".NO_PARALLEL"
		dott[9]  =".PARALLEL"
		dott[10] =".PRECIOUS"
		dott[11] =".SCCS_GET"
		dott[12] =".SILENT"
		dott[13] =".SUFFIXES"
		dott[14] =".WAIT"
		FMT1 = ".............................File = %s\n"
		FMT2 = ".............................End of File = %s\n"
		FMT3 = "UNKNOWN| Looks like sufffix rule but is not: %s\n"
	}

/Reading makefile /	{
			INTARGET = 0
			if (debug)
				printf(FMT1, $NF);
			files[count] = $NF
			curfile = count
			count++
			if ( match( $NF, /Makefile\.lib/ ))
				MAKELIB=1;
		}

/End of makefile/	{
			INTARGET = 0
			if (debug)
				printf (FMT2, $NF);
			curfile--
		}

#
## Handle macros
#

/^[^ \t]*[ \t]*=/	{
		INTARGET = 0
		# discard append to a macro value
		if ( ! match($0, /\+=/))
		{
			if ( match($0, /.*:=/))
			{
				# pattern replacement rule has := 
				if (debug)
					print ("PAT REP| ", $0 )
			}
			# remove conditional assignments
			else 
			{
				INTARGET = 0 ;
				split($0, mac, /\=/)
				fmac2 = mac[1]
				split(fmac2, mac)
				fmac = mac[1]
				if (debug) 
					print ("MACRO  | ", fmac )
				if ( macros[fmac] == "" )
				{
					macros[fmac] = curfile
				}
				else
				{
					if ( incfile == 0 ) 	# do not print included files
					{
						# checks for included files are not required. hence 
						# skip
						if ( files[curfile] != inputfile)
						{	
							next ;
						}	
						
					}
					
					matchindex = match(files[(macros[fmac])], src_re)
					relp2 = substr(files[(macros[fmac])], 
						(matchindex + RLENGTH))
					if ( RLENGTH > 0) 
						relp2 = "\$SRC/" relp2
					# if required in future, remove following line to get 
					# $SRC/... instead of /net/cannonball  etc
					relp2 = files[(macros[fmac])]


					matchindex = match(files[curfile], src_re)
					relp1 = substr(files[curfile], (matchindex + RLENGTH))
					if ( RLENGTH > 0) 
						relp1 = "\$SRC/" relp1
					# if required in future, remove following line to get 
					# $SRC/... instead of /net/cannonball  etc
					relp1 = files[curfile]
					# simulate that the script is running in a directory
					# different from the one containing Makefile.
					if ( relp1 == inputfile )
						relp1 = patharg
					if (heur == 1 )
					{
						printf("%s:%s: %s macro first defined in %s\n",
							patharg, relp1, fmac, relp2 );
						if ( verbose )
							printf("		%s\n", $0 )
					}
				}
			}
		}
		else
		{
			if (debug)
				print ("MAC APND| ", $0 )
		}

		next
		
	}

#
## Actions line processing 
#
/^\t/ {
		if ( INTARGET == 1 )
		{
			if (debug)
				printf("ACTION2 | %s\n", $0);
			split(ftarg, targ)		
			ftarg = targ[1]		
			if ( INTARGET  && FACTION )
			{
				FACTION = 0;
				if (targets[ftarg] == "")
					targets[ftarg] = curfile
				else
				{
					if ( incfile == 0 )
					{
						if ( files[curfile] != inputfile)
							next ;						
					}
					matchindex = match(files[(targets[ftarg])], src_re)
					relp2 = substr(files[(targets[ftarg])], 
						(matchindex + RLENGTH))
					if ( RLENGTH > 0) 
						relp2 = "\$SRC/" relp2
					# if required in future, remove following line to get 
					# $SRC/... instead of /net/cannonball  etc
					relp2 = files[(targets[ftarg])]

					matchindex = match(files[curfile], src_re)
					relp1 = substr(files[curfile], (matchindex + RLENGTH))
					if ( RLENGTH > 0) 
						relp1 = "\$SRC/" relp1		
					# if required in future, remove following line to get 
					# $SRC/... instead of /net/cannonball  etc
					relp1 = files[curfile]
					# simulate that the script is running in a directory
					# different from the one containing Makefile.
					if ( relp1 == inputfile )
						relp1 = patharg
					if ( heur == 1 )
					{
						printf("%s:%s: %s suffix rule first defined in %s\n",
							patharg, relp1, ftarg, relp2 );
						if ( verbose )
							printf("		%s\n", $0 )
					}
				}
			}
			next
		}
	}

#
## Handle targets
#

/.*:/ {
		# remove conditional assignments and percent rules
		if (! match($0, /.*:=/) && ! match($0, /.*%.*:/))
		{
			split($0, targ, /:/)
			ftarg = targ[1]
			# Handle dot-targets and suffix rules
			if ( match ( ftarg, /^\./) )
			{
				matched =0
				for (i = 1; i < 15 ; i++)
				{
					if( match (ftarg, dott[i]))
					{
						matched = 1;
					}
				}
				if ( matched )
				{
					if (debug)
						printf("DOT TRG| %s\n", ftarg);
					# No need for further processing 
					next
				}
				else
				{
					if (match($0, /\.*\.*:/)) 
					{
						if (debug)
							printf("SUF RUL| %s\n", ftarg)
						INTARGET=1;
						FACTION=1;
						SUFFIXRULE = 1;
					} 
					else 
						printf(FMT3, $0);	# has similar to suffix rule 
											# but is not  ( required ???)
				}
			}
			else if (  match ($0, /\.a[ \t]*:/) ||  match ($0, /\.so[ \t]*:/ )    ) 
			{
				# if library is being built in a subdirectory then skip
				if (! match ($0, /\/.*:/) )
				{

					# if library is being built
					if (debug)
						printf("	Library %s is being built\n", 
							substr( $0, 1, (match ($0, /(\.a.*:)|(\.so.*:)/)+2) ));
					LIBRARY=1;
				}
			}
			else
			{
				REGTARGET = 1 ;
				if (debug) 
					print ("TARGET | ", ftarg )
			}
		}
		next
	}
END	 {
		if ( debug )
			printf("\nFiles read :\n");
		for (i=0; i < (count ) ; i++)
		{
			if ( LIBRARY == 1 )
				if ( match(files[i], /Makefile\.lib/))
					MAKELIB = 1;
			if ( debug )
				printf("    %s\n", files[i]);
		}
		if (( MAKELIB == 0) && (LIBRARY == 1))
			printf("%s: %s Makefile.lib is not included\n", patharg, patharg);
	} ' $AWK2IN

	if [ $DOPT -eq 0 -a $NOPT -eq 0 ]
	then
		/bin/rm -f $AWK2IN /tmp/make$$.err
	else
		echo "\n\nLeaving intermediate file $AWK2IN because in debug mode"
	fi


	# restore the .make.state file
	/bin/mv .makestyle.make.state  .make.state > /dev/null 2>&1  

}


if [ $# -eq 0 ]
then
	usage
fi
 
VOPT=0 ; export VOPT		# option for verbose output
IOPT=0 ; export IOPT		# option to enable include file check
SOPT=0 ; export SOPT		# option to suppress second pass
HOPT=0 ; export HOPT		# option to enable heuristic checks
POPT=0 ; export POPT		# option to enable picky checks
## for script debugging
NOPT=0 ; export NOPT		# option for suppressing make -DD
DOPT=0 ; export DOPT		# option to turn debug mode on

while getopts vishpdn FLAG
do
    case $FLAG in
		v)  VOPT=1;   # verbose mode
			export VOPT ;;
			
		i)  IOPT=1;   # report errors from "this" file only 
			export IOPT ;;

		s)  SOPT=1;   # skip second pass 
			export SOPT ;;
 			 
		h)  HOPT=1;   # heuristic checks 
			export HOPT ;;
 
		p)  POPT=1;   # picky option 
			export POPT ;;
 
		d)  DOPT=1;   # debug option
			VOPT=1 ;  # we are in debug mode. turn on verbose mode
			export DOPT ;;
 
		n)  NOPT=1;   # not running make -DD 
			export NOPT ;;
 
       \?) 	usage ;;
    esac
done
shift `expr $OPTIND - 1`

if [ $SOPT -eq 1 -a $IOPT -eq 1 ]
then
    echo "Error: Included files can be checked only during second pass."
    exit 1
fi


#
# if user supplied no file arguments, quit
#
if [ $# -eq 0 ]
then
	echo "No filenames supplied."
	usage
fi

# store user's working directory
UWD=`pwd`

#
# analyze each file by cd'ing to the directory first
#

for i in $*
do
    INPUTFILE=$i
    MAKEDIR=`dirname $INPUTFILE`
    if [ -d $MAKEDIR  ]
    then
	    cd $MAKEDIR
	else
		echo "No directory $MAKEDIR "
		exit 1
	   
    fi
    if [ !  -f `basename $INPUTFILE` ]
    then
		echo "No $INPUTFILE" 
		exit 1
    fi

	lawk1 $INPUTFILE
	if [ $SOPT -eq 0 ]
	then
		lawk2 $INPUTFILE
	fi
	# Go back to the user's working directory
    cd $UWD
done
exit 0
