#!/usr/bin/perl
	eval 'exec perl -S $0 "$@"'
		if $runnning_under_some_shell;

use FindBin;
$p5_metaconfig_base = "$FindBin::Bin/../";

# $Id: jmake.SH 20 2008-01-04 23:14:00Z rmanfredi $
#
#  Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi
#  
#  You may redistribute only under the terms of the Artistic Licence,
#  as specified in the README file that comes with the distribution.
#  You may reuse parts of this distribution only within the terms of
#  that same Artistic Licence; a copy of which may be found at the root
#  of the source tree for dist 4.0.
#
# $Log: jmake.SH,v $
# Revision 3.0.1.9  2004/08/22 09:01:42  ram
# patch71: renamed |test as |case as the construct has its syntax
# patch71: added |subst section to allow variable substitutions
#
# Revision 3.0.1.8  2004/08/21 23:19:46  ram
# patch71: added '|shell' section to emit verbatim code in Makefile.SH
# patch71: new '|test' to conditionally generate Makefile sections
#
# Revision 3.0.1.7  2004/08/21 20:59:57  ram
# patch71: replaced old "do foo()" with modern "&foo()" syntax
# patch71: take care of junk emitted by GNU make when running commands
# patch71: new ^^^ escape sequence, removing extra spaces afterwards
#
# Revision 3.0.1.6  1995/09/25  09:08:01  ram
# patch59: will now force macro definitions to the left
#
# Revision 3.0.1.5  1995/07/25  13:34:47  ram
# patch56: all error messages are now prefixed with the program name
#
# Revision 3.0.1.4  1995/03/21  08:45:27  ram
# patch52: now invokes cpp through new fixcpp script
# patch52: first pass now skips cpp comments alltogether
#
# Revision 3.0.1.3  1994/10/29  15:47:01  ram
# patch36: added various escapes in strings for perl5 support
#
# Revision 3.0.1.2  1993/08/24  12:12:50  ram
# patch3: privlib dir was ~name expanded in the wrong place
#
# Revision 3.0.1.1  1993/08/19  06:42:13  ram
# patch1: leading config.sh searching was not aborting properly
#
# Revision 3.0  1993/08/18  12:04:17  ram
# Baseline for dist 3.0 netwide release.
#

$dir = "$p5_metaconfig_base/dist/files";
$cpp = '/usr/bin/cpp';
$version = '3.5';
$patchlevel = '0';

($me = $0) =~ s|.*/(.*)|$1|;
$dir = &tilda_expand($dir);		# ~name expansion
$file = $dir . '/Jmake.tmpl';

$cpp_opt = "-I. ";				# For Jmakefile, which is local
while ($ARGV[0] =~ /^-/) {
	$_ = shift;
	last if /--/;
	$cpp_opt .= "$_ ";
}
$cpp_opt .= "-I$dir";

# Pass 0 is looking at the template for "?CP:CP =" lines that are to be
# emitted if the CP variable is needed.  Later on, when we see $(CP) being
# used, we'll be able to set the $symbol{CP} entry to 1 to have the CP
# variable initialized by the template.

open(TMPL, $file) || die "$me: can't open $file: $!\n";
while (<TMPL>) {
	next unless /^\?([\w_]+):\1\s+=/;
	$wanted{$1}++;
}
close TMPL;

# Thank you HP-UX for having a cpp that blindly strips trailing backslashes
# in the text. Run through cpp by using the fixcpp script...

open(CPP, "$dir/fixcpp $cpp_opt $file |");
while (<CPP>) {
	# Record defined symbols in Jmakefile. We won't catch symbols
	# in conditional commands, but that's ok, I hope.
	if ($in_symbol) {
		$val = $_;
		$in_symbol = 0 if !($val =~ s/\\\s*$//);	# Last line
		if ($val = /^\|expand/) {		# Found an expand command
			$in_symbol = 0;				# Stop gathering value
			$val .= "void::x";			# Stop any incomplete escape sequence
		}
		chop($val);
		$Makesym{$current_symbol} .= $val;
	} elsif (/^\s*(\w+)\s*=(.*)/ && !$in_symbol) {
		# Found a makefile's macro declaration
		$val = $2;
		$current_symbol = $1;
		if ($val =~ s/\\\s*$//) {	# Remove final '\'
			$in_symbol = 1;			# This is a continuation line
		}
		$Makesym{$current_symbol} = $val;
		push(@Order, $current_symbol);	# Keep track of order
	}
	# Protect RCS keyword Id or Header from normal substitution
	s/\$(Id|Header|Log)/\$X-$1/;
	# Restore possibly escaped C comments
	s|/#\*|/*|g;
	s|\*#/|*/|g;
	# Remove all ^^^ (null space character) up to next non-space character
	s|\^\^\^\s*||g;
	# Remove all ^^ (null space character)
	s|\^\^||g;
	# Restore escaped ^^ and ^^^ sequences
	s|\^\\\^\\\^|^^^|g;
	s|\^\\\^|^^|g;
	next if /^#\s+\d+/;		# Skip cpp commments

	s/^;#/#/;
	s/@#\s?/\n/g;		# Kept for backward compatibility
	s/@!\s?/\n/g;
	s/@@\s?/\n\t/g;

	# A '\r' is added to all lines, in order to let 'split' keep them
	# As lines ending with '\' won't fit in the next regular
	# expression (why ?), we have to treat that special case separately
	s/\n$/\r\n/gm;
	s/\\\s*$/\\\r/gm;	# Remove spaces after final '\' and add '\r'
	@macro = split(/\n/);
	for ($i = 0; $i <= $#macro; $i++) {
		chop($_ = $macro[$i]);				# Remove final '\r'
		s/\s+$//g;							# Remove possible useless spaces
		if (/^TOP\s*=\s*(\S+)/) {			# Get the top from generated file
			$top = $1;
		}
		find_wanted($_);					# Look for known $(VAR) usage
		if (s/^\s*>//) {					# '>' means "symbol wanted"
			warn "$me: the '>$_' construct is deprecated for known symbols\n"
				if $wanted{$_} && !$warned_wanted_symbol_deprecated++;
			$symbol{$_} = 1;
		} elsif (s/^\s*\+//) {				# '+' means "initialization section"
			if (s/^\+(\w+)//) {				# '++' means add to variable list
				$added{$1} .= $_;
			} else {						# A single '+' means "add as is".
				push(@init, $_);
			}
		} elsif (s/^\|//) {					# Command for us
			if (/suffix\s+(\S+)/) {			# Add suffix
				push(@suffix, $1) unless $seen{$1};
				$seen{$1} = 1;
			} elsif (s/^rule://) {			# Add building rule
				s/^\s(\s*\S+)/\t$1/;		# Make sure leading tab is there
				push(@rule, $_);
			} elsif (/^skip/) {				# Unconditional skip... funny!
				push(@makefile, "|$_");		# Skip handled in pass 2
			} elsif (/^expand/) {
				push(@makefile, "|$_");		# Expand handled in pass 2
			} elsif (/^once\s+(.*)/) {		# Once handled in pass 1
				if ($Once{$1}++) {			# Symbol already seen -- skip
					for (; $i <= $#macro; $i++) {
						last if $macro[$i] =~/^-once/;
					}
					warn("$me: -once not found for $1")
						unless $macro[$i] =~/^-once/;
				}
			} elsif (/^shell/) {			# Escaping to shell
				push(@makefile, "|$_");		# will be handled in pass 2
			} elsif (/^case/) {				# Conditional generation
				push(@makefile, "|$_");		# will be handled in pass 2
			} elsif (/^subst/) {			# Section with var substitution
				push(@makefile, "|$_");		# will be handled in pass 2
			} else {
				print "$me: Warning: unknown command $_\n";
			}
		} else {
			next if /^-once/;			# Control statement removed
			push(@makefile, $_);
		}
	}
}
close CPP;

@key = keys(%added);
$last_was_blank = 1;	# To avoid blank line at the top of the file
$symbol{'INIT'} = 1 if ($#init >= 0 || $#key >=0);		# Initializations
$symbol{'SUFFIX'} = 1 if ($#suffix >= 0 || $#rule >=0);	# Rules or suffixes
$symbol{'TOP'} = 1 if $top eq '.';		# If imake invoked for the top

$shellmode = 0;			# Set to true within "shell" section
$casemode = 0;			# Counts nesting levels within "case" section
$substmode = 0;			# True when within section with variable substitution

$SPIT_START = "\$spitshell >>Makefile <<'!NO!SUBS!'\n";
$SPIT_END = "!NO!SUBS!\n";
$GROK_START = "\$spitshell >>Makefile <<!GROK!THIS!\n";
$GROK_END = "!GROK!THIS!\n";

open(MAKEFILE, ">Makefile.SH");
# We have to use for instead of foreach to handle 'skip' easily
line: for ($i = 0; $i <= $#makefile; $i++) {
	$_ = $makefile[$i];
	next if /^-skip|-expand/;		# They might have made a mistake

	# Strip consecutive blank lines in generated file

	if (/^\s*$/) {
		next if ($last_was_blank);
		$last_was_blank = 1;
	} else {
		$last_was_blank = 0;
	}

	# In shell mode, we're transparent, untill we reach a "-shell"
	# We don't call print_makefile() as we don't want to record
	# those non-makefile lines in the @Generated array.

	if ($shellmode) {
		if (/^-shell/) {			# Ending shell mode, back to Makefile
			print MAKEFILE $substmode ? $GROK_START : $SPIT_START;
			$shellmode = 0;
		} elsif (/^\|shell/) {
			die "$me: can't nest 'shell' sections.\n";
		} else {
			print MAKEFILE "$_\n";
		}
		next;
	} elsif (/^\|shell/) {
		print MAKEFILE $substmode ? $GROK_END : $SPIT_END;
		$shellmode = 1;				# Next lines emitted verbatim as shell
		next;
	}

	# In subst mode, the section until "-subst" is emitted regularily,
	# excepted that it will be in a grok section, so its $var will be
	# substituted by the shell.

	if ($substmode) {
		if (/^-subst/) {			# Ending subst mode, back to regular
			print MAKEFILE $GROK_END;
			print MAKEFILE $SPIT_START;
			$substmode = 0;
			next;
		} elsif (/^\|subst/) {
			die "$me: can't nest 'subst' sections.\n";
		} 
		# Continue with line
	} elsif (/^\|subst/) {
		print MAKEFILE $SPIT_END;	# End spit section in Makefile.SH
		print MAKEFILE $GROK_START;
		$substmode = 1;				# Next lines subject to $ interpretation
		next;
	}

	# In a "case" section, the Makefile will be conditionally generated
	# based on the value of the supplied variable, as evaluated by the shell.
	# We can nest "case" sections without problems.

	if (/^-case/) {					# Ending current case section
		if ($casemode == 0) {
			warn "$me: ignoring spurious '-case'\n";
			next;
		}
		print MAKEFILE $substmode ? $GROK_END : $SPIT_END;
		my $indent = "\t" x ($casemode - 1);
		print MAKEFILE "${indent}\t;;\n";
		print MAKEFILE "${indent}esac\n";
		print MAKEFILE "${indent}", $substmode ? $GROK_START : $SPIT_START;
		$casemode--;
		next;
	}

	if (/^\|case/) {
		my ($var, $value) = /^\|case\s+(\w+)\s+in\s+(.*)/;
		die "$me: unparseable directive '$_'\n" if $var eq '';
		$casemode++;
		print MAKEFILE $substmode ? $GROK_END : $SPIT_END;
		my $indent = "\t" x ($casemode - 1);
		print MAKEFILE "${indent}case \"\$$var\" in\n";
		print MAKEFILE "${indent}$value)\n";
		print MAKEFILE "${indent}\t", $substmode ? $GROK_START : $SPIT_START;
		next;
	}

	# Process regular line to be generated in Makefile.SH

	s/<TAG>/[jmake $version PL$patchlevel]/;

	# Lines starting with ?SYMBOL: (resp. %SYMBOL:) are to be processed
	# only if SYMBOL is defined (resp. undefined).

	# Apply in sequence
	while (/^\s*\?|\s*%/) {
		if (s/^\s*\?(\w+)://) {					# Wanted symbol ?
			next line unless $symbol{$1};
		} elsif (s/^\s*%(\w+)://) {				# Unwanted symbol ?
			next line if $symbol{$1};
		} else {
			print "$me: Warning: missing ':' in $_\n";
			last;
		}
	}

	# We wish to make sure there is a leading tab if the line starts with
	# a space to prevent problems later on. However, variable definitions
	# might want to be aligned on the '=' (imake style). Not all make
	# may be able to cope with those though, so they are left justified
	# again.

	s/^\s/\t/ unless /^\s+\w+\s+=/;		# Make sure leading tab is there
	s/^\s+(\w+\s+=)/$1/;				# Left justify variable definition
	s/^;#/#/;							# Comments in Jmakefile

	if (s/^\|//) {						# Command for us
		if (/^skip/) {					# Skip until -skip
			for (; $i <= $#makefile; $i++) {
				last if $makefile[$i] =~ /^-skip/;
			}
		} elsif (s/^expand//) {
			&init_expand($_);			# Initializes data structures
			$i++;						# Skip expand line
			undef @Expand;				# Storage for expanded lines
			$pattern = '';				# Assume no pattern
			for (; $i <= $#makefile; $i++) {
				$_ = $makefile[$i];
				if (s/^-expand//) {			# Reached end of expansion
					if (s/^\s*(.*)/$1/) {	# Expand followed by a pattern
						$pattern = $_;		# Get pattern to be removed
					}
					last;
				}
				s/^\s/\t/;				# Make sure leading tab is there
				push(@Expand, $_);		# Line to be expanded
			}
			&expand($pattern);			# Expand all lines in buffer
		} else {
			die "$me: unknown command $_\n";
		}
	} elsif (/^INIT/) {						# Initialization section
		# All the initializations are put in the variable substitution
		# section of the Makefile.SH. Therefore, we have to protect all
		# the '$' signs that are not followed by an alphanumeric character.
		foreach (@init) {
			# Dumps core sometimes with perl 4.0 PL10
			# &protect_dollars(*_);
			$_ = &protect_dollars($_);
			&print_makefile($_);
		}
		foreach (@key) {	# @key set earlier to keys(%added)
			$_ .= " = " . $added{$_};
			# Dumps core sometimes with perl 4.0 PL10
			# &protect_dollars(*_);
			$_ = &protect_dollars($_);
			&print_makefile($_);
		}
	} elsif (/^SUFFIX/) {					# Suffixes/Rules section
		# Rules and suffixes are put in the variable substitution
		# section of the Makefile.SH. Therefore, we have to protect all
		# the '$' signs that are not followed by an alphanumeric character.
		if ($#suffix >= 0) {
			print MAKEFILE ".SUFFIXES:";
			foreach (@suffix) {
				# Dumps core sometimes with perl 4.0 PL10
				# &protect_dollars(*_);
				$_ = &protect_dollars($_);
				print MAKEFILE " $_";
			}
			print MAKEFILE "\n\n";
		}
		foreach (@rule) {
			# Dumps core sometimes with perl 4.0 PL10
			# &protect_dollars(*_);
			$_ = &protect_dollars($_);
			print MAKEFILE "$_\n";
		}
	} else {
		&print_makefile($_);
	}
}
close MAKEFILE;

sub protect_dollars {
	# Dumps core sometimes with perl 4.0 PL10
	# local(*_) = shift(@_);
	s/\\\$/\\=/g;		# Protect already escaped '$'
	s/(\$\W)/\\$1/g;	# Escape unprotected '$'
	s/\\=/\\\$/g;		# Restore escaped '$'
	$_;					# Because perl dumps core... :-(
}

# Initializes data structures for expansion. If we detect Makefile
# macro in the 'expand' line (the argument), then we write a small
# makefile that will do the substitution for us -- I'm lazy today :-)
sub init_expand {
	local($_) = shift(@_);
	undef %Vars;		# Reset array of variables
	$Vars_len = 0;		# Number of "symbols" in first expanded
	if (/\$\(\w+\)/) {	# If at least one macro
		local($make) = "/tmp/mkjm$$";
		open(MAKE, ">$make") || die "$me: can't create $make: $!\n";
		&gen_variables();			# Generates already computed variables
		foreach $var (@Order) {		# Print each in order we found them
			print MAKE "$var = $Makesym{$var}\n" if !$Gvars{$var};
		}
		# We prepend OUTPUT: in front of the line that interests us, because
		# some makes can print extra information, especially GNU make with
		# its entering/leaving blurb when invoked from another makefile.
		print MAKE "all:\n\t\@echo 'OUTPUT: $_'\n";
		close MAKE;
		chop($_ = `make -f $make all | grep ^OUTPUT:`);
		unlink($make);
	}
	s/^OUTPUT: //;
	while (s/^\s*(\w+)!([^!]*)!//) {
		$Vars{$1} = $2;
		# Record only length for _first_ expanded symbol
		$Vars_len = split(/\s\s*/, $2) unless $Vars_len;
	}
}

# Expand lines in the @Expand array. The argument is a pattern which is to
# be removed from the last chunk of expanded lines.
# For each symbol s, !s is replaced by the next item, and !s:p=q does the
# same after having replaced the pattern 'p' by pattern 'q' in the item.
# Spaces are NOT allowed in 'p' or 'q'. Substitution is done once (no /g).
sub expand {
	local($pattern) = shift;		# To-be-removed pattern for last chunk
	local($_);
	local($sub);
	local($i);
	local(@expands);
	for ($i = 0; $i < $Vars_len; $i++) {
		foreach $line (@Expand) {
			$_ = $line;		# Don't modify elements in array
			foreach $sym (keys %Vars) {
				@expands = split(/\s\s*/, $Vars{$sym});
				$sub = $expands[$i];
				$sub =~ s/\/\///g;		# // is a void value
				while (s/!${sym}:([^\s]*)=([^\s]*)/,x##x,/) {
					# Replacing item is altered by some pattern
					local($p) = $1;
					local($q) = $2;
					local($subq) = $sub;
					eval "\$subq =~ s=${p}=${q}=";
					s/,x##x,/${subq}/;
				}
				s/!${sym}/${sub}/g;
			}
			# Protect substitution in an 'eval' in case of error
			eval "s/${pattern}\$//" if $pattern && $i == ($Vars_len - 1);
			&print_makefile($_);
		}
	}
}

# Prints its argument in MAKEFILE and records it also in Generated
sub print_makefile {
	local($_) = shift(@_);		# Line to be printed
	print MAKEFILE "$_\n";
	push(@Generated, "$_\n");
}

# Generates in MAKE file all the generated variable we have so far for
# final Makefile. This is mainly intended to allow expansion of variables
# which are already defined with an expand.
sub gen_variables {
	undef %Gvars;				# Reset already generated variables
	local ($in_symbol) = 0;		# True when in variable (Makefile's macro)
	foreach (@Generated) {
		if ($in_symbol) {
			if (/^\s*(\w+)\s*=(.*)/) {		# Missed the end of previous macro
				$in_symbol = 0;
				$Gvars{$1} = 1;					# Definition of variable seen
				$in_symbol = 1 if (/\\\s*$/);	# There is a final '\'
				print MAKE "void::\n";			# Cut incomplete esc sequence
			} else  {
				$in_symbol = 0 if !(/\\\s*$/);	# Last line
			}
			print MAKE;
		} elsif (/^\s*(\w+)\s*=(.*)/ && !$in_symbol) {
			# Found a makefile's macro declaration
			$Gvars{$1} = 1;					# Definition of variable seen
			$in_symbol = 1 if (/\\\s*$/);	# There is a final '\'
			print MAKE;
		}
	}
	print MAKE "void::\n";		# Cut incomplete escape sequence
}

# Parse line to extract all $(VAR) usage and trigger the symbol if VAR
# is among the wanted set, as if they had manually said ">VAR" like in
# the old days.
sub find_wanted {
	my ($l) = @_;
	while ($l =~ s/\$\(([\w_]+)\)//) {
		$symbol{$1}++ if $wanted{$1};
	}
}

# Perform ~name expansion ala ksh...
# (banish csh from your vocabulary ;-)
sub tilda_expand {
	local($path) = @_;
	return $path unless $path =~ /^~/;
	$path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e;			# ~name
	$path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e;	# ~
	$path;
}

