#!/usr/local/bin/perl
# spicenodes -
# script to output all nodenames found in a spice deck
# for use in looking for connectivity and heirarchy problems
# You'll almost always want to run the output through "sort -u"
#
# currently does mosfets, resistors, inductors and capacitors only.
# parsing other builtin element types could be added easily.
# subcircuits would be harder.
#
# $Log: spicenodes,v $
# Revision 1.2  2004/05/16 19:54:15  tell
# miscellaneous updates
#
# Revision 1.1  1999/12/08 14:17:21  tell
# Moved in files from the old "hcutil" that weren't RCS'ed
#
# Revision 1.1  1995/04/13  16:33:13  tell
# Initial revision
#

use Getopt::Long;
use IO::File;
use IO::Handle;
use strict qw(vars);

use vars qw($progname $fh);
$progname = "spicenodes";

sub usage {
	print STDERR "usage: $progname [options] [input-file]\n";
	print STDERR "options:
  -h|--help                    print this message
  -v|--verbose	               enable verbose debugging output
  -f|--format	STR            print each node using formatstring STR;
                               %s is replaced by nodname";
	
}

use vars qw($debug_flag $verbose $help %optctl $fh $formatstr);

# default for which spice we're using
$debug_flag = 0;
$verbose = 0;
$help = 0;

Getopt::Long::config('no_auto_abbrev',
		     'no_ignore_case','no_ignore_case_always');

%optctl = ("v|verbose!"    => \$verbose,
	   "D|debug!"    => \$debug_flag,
	   "f|format=s"    => \$formatstr,
	   "h|help!"       => \$help
);

if(!GetOptions(%optctl)) {
	&usage();
	exit 1;
}

if($help) {
	&usage();
	exit 1;
}

if($#ARGV == 0) {
	$fh = IO::File->new($ARGV[0], "r");
	if(!defined($fh)) {
		print STDERR "$ARGV[0]: $!\n";
		exit(1);
	}
} elsif($#ARGV == -1) {
	$fh = STDIN;
} else {
	usage();
	exit 1;
}

use vars qw( $inst $src $gate $drain $subst $type $stuff $n1 $n2 $val $dtype %nodehash);

line: while($_ = get_line_cont($fh)){
	chop($_);
	$_ =~ s/\*.*$//;	# wipe out comments
	next line if $_ =~ m/^\s*$/; # skip blank lines

	next line if $_ =~ m/^\./; # skip control lines
	
	if( $_ =~ /^m/i) {
		($inst, $src, $gate, $drain, $subst, $type, $stuff) = split(/[ \t\n]+/, $_, 7);

		$nodehash{$src} = 1;
		$nodehash{$gate} = 1;
		$nodehash{$drain} = 1;
		$nodehash{$subst} = 1;
 	} elsif( $_ =~ /^[rcld]/i) {
		($inst, $n1, $n2, $val, $stuff) = split(/[ \t\n]+/, $_, 5);

		$nodehash{$n1} = 1;
		$nodehash{$n2} = 1;
	} else {
		($inst, $stuff) = split(/[ \t\n]+/, $_, 2);
		if($inst) {
			$dtype = substr($inst, 0, 1);
			print STDERR "spicenodes: don't know how to handle devices of type \"$dtype\"\n";
		}
	}
}

use vars qw( @nodes $n);

@nodes = sort {$a cmp $b}  keys(%nodehash);

if($formatstr) {
#	print "formatstr=\"$formatstr\"\n";
	foreach $n (@nodes) {
		printf $formatstr, $n;
		print "\n";
	}
} else {
	print join("\n", @nodes), "\n";
}


exit 0;

#############################################################################
# Return a single logical line from a spice file,
# handling continuation with '+'
#

use vars '$pushback_line';

sub get_line_cont
{
	my($f) = @_;
	my($line, $nline);
	
	if(defined($pushback_line)) {
		$line = $pushback_line;
		undef $pushback_line;
	} else {
		$line = $f->getline;
	}
	if(! $line) {
		return $line;
	}
		
	$nline = $f->getline;
	while($nline =~ m/^\+/) {
		$nline =~ s/^\+/ /;
		$line =~ s/\n$//;
		$line .= $nline;
		$nline = $f->getline;
	}
	if($nline) {
		$pushback_line = $nline;
	}

	return $line;
}
