#!/usr/bin/perl
#
# simplistic KNF indent formatter for programming languages with
# C-like syntaxes. requires original file to be more or less good-
# looking already. i wrote this because it was faster to write than
# to find something that would do just what i needed.	--lynX
#
# available from http://perl.pages.de/bin/indentknf
# see also http://about.psyc.eu/Indent

# real KNF
my $cb = "\t";	    # code block indentation
my $lc = "    ";    # line continuation indentation
my $nl = "\n";	    # newline

# narrow style KNF
my $cb = "    ";    # code block indentation
my $lc = "  ";	    # line continuation indentation
my $nl = "\n";	    # newline

require 'getopt.pl';
&Getopt;

die <<X if $#ARGV < 0;
Usage: $0 [<file>|<directory>]+

If a directory is given, all files will be processed.

indentknf tries a simplistic re-indentation of already
more or less properly formatted code. Output is placed
in a file by the same name with a tilde appended, so
you can safely compare the old and the new and decide
which one is better.
X

require 'find.pl';
$File::Find::dont_use_nlink = 'DONT!';
&find(@ARGV);
exit;

sub wanted {
	next if /~$/;

	($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime)
		= lstat;

	my $f = $_;
	unless (open(I, $f)) {
		print STDERR "cannot access $name\n";
		return;
	}

	my $was = '';
	my $nu = '';
	my $next = '';
	my $i = 0;		# current indentation level
	my $c = 0;		# do line continuation on next line?
	my $p = '';		# parameter continuation string
	my $pre = undef;	# previous continuation string
	my $t;

	$next = <I>;
	while($t = $next) {
		$_ = <I>;
		$next = $_;
		if (/\/\*/) {
			until (/\*\//) {
				$_ = <I>;
				$next .= $_;
			}
		}
		$_ = $t;
		# fix {'s on a line by itself
		if ($next =~ /^\s*{\s*$/) {
			chomp;
			$_ .= " {\n";
			$next = <I>;
		}

#		$was .= $_;
		s/^\s+//;
		s/\s+$//;
		if ( /^\}/ ) {
			$i--;
			undef $pre;
		}
#		print "$i|$_" if $i;
		if ($_ eq "") {
			$nu .= "\n";
			$c = 0;
		} elsif ( /^#/ ) {
			$nu .= $_ . $nl;
		} elsif ( /^\*/ ) {
			$nu .= ' '. $_ . $nl;
		} else {
			s/\b(if|for|while|do|with|switch|repeat|until|foreach)\(/\1 (/;
			$nu .= $cb x $i . $lc x $c . $pre . $p . $_ . $nl;
			# parameter continuation
			if ( /,$/ ) {
			    $pre = $c ? $lc : '' unless defined($pre);
			    if ( not /\)/ and /^(.*\()/ ) {
				$p = " " x length($1);
#				print STDERR "Setting \$p to '$p' after seeing '$1' (\$c is '$c')\n";
#			    } elsif ( $c ) {
#				$p = $lc x $c;
#				print STDERR "Setting \$p to '$p' because of line continuation '$c'\n";
			    }
			    # use the line continuation only if it was
			    # already set before we got into here
			    $c = $pre;
			} elsif ( /\[$/ ) {
			    $p = $lc;
			} else {
			    $p = '';
			    $c = !/;$/;
			    $c = 0 if m!;\s+//\s+\S!;
			    $c = 0 if /^\}/;
			    $c = 1 if /\belse$/;
			    $c = 0, $i++ if /{\s*$/;
			    $c = 0 if m!^//!;
			    $c = 0 if m:\*/$:;
			}
		}
	}

#	if ($was eq $nu) {
#		print "no:\t$name\n";
#		return;
#	}
#	print "yes:\t$name\n";
	print "\t$name\n";

	if (open(O, "|unexpand >$f~")) {
		print O $nu;
		close O;
	} else {
		print STDERR "cannot create $f~ in $dir\n";
		return;
	}
	system('/usr/bin/diff', '-u', $f, "$f~") if $opt_d;
	system("/usr/bin/diff -u '$f' '$f~' | /usr/bin/vim -R -") if $opt_D;

#	rename ("Nu$f", $f) or die "the world is not logical";
}

