From aks@anywhere.ucsb.edu Fri May  3 10:51:57 1991
From: aks@anywhere.ucsb.edu (Alan Stebbens)
Newsgroups: comp.lang.perl
Subject: Program to list (and comprehend) nested includes
Summary: Program to list nested `cpp' includes
Keywords: Program lists nested #includes
Date: 1 May 91 23:57:43 GMT
Organization: CCSE, Univ. of CA, Santa Barbara

When trying to port software to the IBM RS3000 running AIX 3.1, it
often occurs that I have to isolate a missing or malformed rerenced to
some "standard" macro or define.  Also, there are many include files in
the /usr/include hierarchy which are ultimately recursive by
re-invoking, for example, /usr/include/standards.h.  It becomes very
difficult to understand the dependencies of these include files when
such recursion occurs is several places or is very deep.

The following program, "includes", is my "quick 'n dirty" solution to
this problem:  it will list, and optionally indent and line-number, the
standard `cpp' include directives.

Oh yeah, a caveat: I had originally intended the program to be able to
dynamically interpret #define's and #undef's, but never followed up
with the code to do the job.  Thus, the internal documentation and
"usage:" show a -D and -U option usage, but don't really do anything
with it.  Perhaps, some other time, I'll make it work as intended.  Or,
someone else will.

Enjoy.

Alan Stebbens        <aks@hub.ucsb.edu>             (805) 893-3221
     Center for Computational Sciences and Engineering (CCSE)
          University of California, Santa Barbara (UCSB)
           3111 Engineering I, Santa Barbara, CA 93106

============================= cut here ===================================
#!/bin/perl
# includes [[-[vincu]...] [-Idir] [-Dname] [-Uname]]... files
#
# Given a list of files, determine the #include dependency list
# given '-c', use CPP to determine the _dynamic_ dependency list,
# otherwise, the static include file dependency list is produced.
#
# If the `-D' or `-U' options are given, `-c' is assumed.
#
#
$IndentStr = ' ' x 4;
@IncludeDirs = ('.');
@Defines = ();
$Verbose = 0;
$Pathnames = 0;
$LineNums = 0;
$Indent = 0;
$SortUniq = 0;
&Usage unless $#ARGV >= $[;
while ($_ = shift) { 	# scan arguments
    if (!/^-/) {
	unshift(@ARGV,$_);
	last;
    }
    while (s/^-(.)/-/) {
	$Compile++ 			if $1 eq 'c';
	$Indent++			if $1 eq 'i';
	$LineNums++			if $1 eq 'n';
	$Pathnames++			if $1 eq 'p';
	$SortUniq++			if $1 eq 's';
	$Verbose++			if $1 eq 'v';
	if ($1 =~ /[IDU]/) {
	    $opt = $1;
	    s/^-(.*)/-$opt$1/;		# put option back in
	    push(@IncludeDirs,$_)	if $opt eq 'I';
	    push(@Defines,$_)		if $opt =~ /DU/;
	    last;
	}
	&Usage unless $1 =~ /[cinpsv]/;
    }
}
&Usage if $SortUniq && ($LineNums || $Indent);
$Compile++ if $#Defines >= $[;		# any defines? compile it
$Pathnames = $LineNums = $Indent = 1 if $Verbose;
@Files = @ARGV;
while ($File = shift(@Files)) {
    printf "%s:\n",$File;
    if ($SortUniq) {
	select(STDOUT); $| = 1;
	open(SORT,"|sort -u +1 -2") || die "Can't open pipe to `sort -u' filter because $!\n";
	select(SORT);	$| = 1;
    }
    &ShowIncludes($File,0);
    if ($SortUniq) {
	close(SORT);
	select(STDOUT);
    }
}
exit;

%Included = ();
sub ShowIncludes {
    local($name,$level) = @_;
    local(*FILE);
    local($_,$.);
    local($pathname,$lq,$incname,$rq);
    local($indent) = $Indent ? $IndentStr x $level : '';

    if (open(FILE,$name)) {
	$Included{$name}++;
	while (<FILE>) {
	    next unless /^#include/;
	    chop;
	    if (/^#include\s*(")([^"]*)(")/ ||
		/^#include\s*(<)([^>]*)(>)/) {
		($lq,$incname,$rq) = ($1,$2,$3);
		print $indent;
		printf "%3d:",$. if $LineNums;
		printf "#include %s%s%s",$lq,$incname,$rq;
		if ($file = &FindFile($incname,($lq eq '"'))) {
		    printf "\t(%s)",$file if $Pathnames;
		    print "\n";
		    &ShowIncludes($file,$level+1) unless $Included{$file};
		} else {
		    printf ": can't find $incname\n";
		}
	    } else {
		print ": bad syntax.\n";
	    }
	}
	close FILE;
    } else {
	print ": can't open $name because $!\n";
    }
}

sub FindFile {
    local($file,$localflag) = @_;
    local($path);
    if ($localflag) {
	foreach $dir (@IncludeDirs) {
	    ($path = $dir . '/' . $file) =~ s/^-I//;
	    return $path if -f $path;
	}
    }
    foreach $dir ('/usr/local/include', '/usr/include') {
	return $path if -f ($path = $dir . '/' . $file);
    }
    '';
}

sub Usage {
    select(STDERR);
    print STDERR <<"EOF";
usage: includes [-cinspv] [-Ipath]... [-[D|U]name]... files
Prints the '#include' statements referenced by the named files, automatically
following nested #includes; statically recursive #includes are detected.
 -c	Compile (interpret) #ifdef's and expressions
 -i	Indent nested #includes
 -n	Print the line numbers of the #include statements
 -p	Print the absolute path names for each include file
 -s	Run the #includes through a `sort -u' filter
 -v	Verbose mode; implies `-inp' options
 -Ipath Add `path' to the include search path.
 -Dname	Define `name'; implies the `-c' option.
 -Uname	Undefine `name'; implies the `-c' option.
Sorting is mutually exclusive with indents and line numbering.
EOF
    exit 1;
}
  
============================= cut here ===================================
--

Alan Stebbens

From aks@anywhere.ucsb.edu Fri May  3 10:52:08 1991
From: aks@anywhere.ucsb.edu (Alan Stebbens)
Newsgroups: comp.lang.perl
Subject: Program to check for balanced #{if|else|endif}s
Summary: List, with optional line numbers and indention, `cpp' conditionals
Keywords: CPP #ifdef #else #endif listing
Date: 1 May 91 23:47:36 GMT
Organization: CCSE, Univ. of CA, Santa Barbara

The following is a perl program to list, with optional indentions 
and line numbers, the `cpp' conditionals: #ifdef, #elif, #else, #endif.
I wrote this because I was unsuccessfully trying to find a missing 
#ifdef in some newly patched software.

Enjoy.

Alan Stebbens        <aks@hub.ucsb.edu>             (805) 893-3221
     Center for Computational Sciences and Engineering (CCSE)
          University of California, Santa Barbara (UCSB)
           3111 Engineering I, Santa Barbara, CA 93106

============================= cut here ===================================
#!/bin/perl
# ifdefs [-n] [-i INDENT] [-l]
require 'getopts.pl';

&Getopts('i:nl') || die "Usage: ifdefs [-i indent] [-l] [-n] [files...]\n";

$INDENT = $opt_i ? $opt_i : 3;
$indent = 0;
@block = ();
@state = (0);
# States: 0 - nothing; 1 = #if ; 2 = #elsif ; 3 = #else 
select(STDOUT); $| = 1;
while (<>) {
    if (/^#\s*(if|ifn?def|elif|elsif|elseif|else|endif)/) {
	$key = $1;
	s/[ \t]+/ /g; 
	$nextindent = $indent;
	if ($key eq 'if' || $key =~ /ifn?def/) {
	    $state[$indent] = 1;
	    $block[$indent] = "$key $.";
	    $state[++$nextindent] = 0;
	} elsif ($key eq 'elseif' || $key eq 'elsif' || $key eq 'elif') {
	    $indent--;
	    &Error if !$indent || $state[$indent] < 1 || $state[$indent] > 2;
	    $block[$indent] = "$key $.";
	    $state[$indent] = 2;
	} elsif ($key eq 'else') {
	    &Error if !$indent;
	    $indent--;
	    &Error if $state[$indent] < 1 || $state[$indent] > 2;
	    $state[$indent] = 3;
	    $block[$indent] = "$key $.";
	} elsif ($key eq 'endif') {
    	    &Error if !$indent || $state[$indent];
	    $indent--;
	    &Error if !$state[$indent];
	    $state[$indent] = 0;
	    $block[$indent] = '';
	    $nextindent--;
	}
    } else {
	next unless $opt_l;
    }
    printf "%4d:",$. if $opt_n;
    printf "%s%s",(' 'x($indent * $INDENT)),$_;
    $indent = $nextindent;
}

sub Error {
    if (!$indent) {
	printf "ERROR: Unmatched \#%s:\n%4d: %s",$key,$.,$_;
    } else {
	printf "ERROR: Out of place \#%s:\n%4d: %s",$key,$.,$_;
	($keyw,$keyl) = split(' ',$block[$indent]);
	printf "Previous \#%s at line %d not terminated\n",$keyw,+$keyl;
    }
    next;
}
============================= cut here ===================================
--

Alan Stebbens

