#!/usr/bin/env perl

#---------------------------------------------------------------------
#                          file information
#---------------------------------------------------------------------

# File:     fix-nspr-pkg-inc.pl
# Purpose:  "nspr" build-related utility
# License:  BSD-style (for this file only)
# Revision: 230406

#---------------------------------------------------------------------
#                           important note
#---------------------------------------------------------------------

# This software is provided on an  AS IS basis with ABSOLUTELY NO WAR-
# RANTY.  The  entire risk as to the  quality and  performance of  the
# software is with you.  Should the software prove defective,  you as-
# sume the cost of all necessary  servicing, repair or correction.  In
# no event will any of the developers,  or any other party, be  liable
# to anyone for damages arising out of use of the software, or inabil-
# ity to use the software.

#---------------------------------------------------------------------
#                            module setup
#---------------------------------------------------------------------

require 5.10.1;
use strict;
use Carp;
use warnings;
use Cwd;
use File::Find;
                                # Trap warnings
$SIG {__WARN__} = sub { die @_; };

#---------------------------------------------------------------------
#                           basic constants
#---------------------------------------------------------------------

use constant ZERO  => 0;        # Zero
use constant ONE   => 1;        # One
use constant TWO   => 2;        # Two

use constant FALSE => 0;        # Boolean FALSE
use constant TRUE  => 1;        # Boolean TRUE

#---------------------------------------------------------------------
#                         program parameters
#---------------------------------------------------------------------

my $PROGNAME;                   # Program name (without path)
   $PROGNAME =  $0;
   $PROGNAME =~ s@.*/@@;

my $PURPOSE       = 'nspr package build utility';
my $REVISION      = '170706';
my $USE_LESS      = FALSE;      # Flag: Use "less" for usage text

my $MAX_FILE_SIZE = 15 * 1024 * 1024;
my $FlagKeepTimes = FALSE;      # Flag: Preserve timestamps

#---------------------------------------------------------------------
#                          global variables
#---------------------------------------------------------------------

my $CWD;                        # Current working directory
my @Paths = ();                 # List of pathname entries

#---------------------------------------------------------------------

# @NSPRH  should contain  a  list of all  ".h" files that are, or will
# be, stored  in the installed "nspr" package tree, omitting path com-
# ponents. Order isn't significant.  The list might need to be updated
# if "nspr" is updated.

my @NSPRH = qw
(
    nspr.h      plarena.h       plarenas.h      plbase64.h
    plerror.h   plgetopt.h      plhash.h        plstr.h
    pratom.h    prbit.h         prclist.h       prcmon.h
    prcountr.h  prcpucfg.h      prcvar.h        prdtoa.h
    prenv.h     prerr.h         prerror.h       prinet.h
    prinit.h    prinrval.h      prio.h          pripcsem.h
    prlink.h    prlock.h        prlog.h         prlong.h
    prmem.h     prmon.h         prmwait.h       prnetdb.h
    prolock.h   prpdce.h        prprf.h         prproces.h
    prrng.h     prrwlock.h      prshm.h         prshma.h
    prsystem.h  prthread.h      prtime.h        prtpool.h
    prtrace.h   prtypes.h       prvrsion.h      prwin16.h
    pprio.h     pprthred.h      prpriv.h

    pralarm.h   probslet.h      protypes.h      prsem.h
);

my $NSPRH = join '|', @NSPRH;

#---------------------------------------------------------------------
#                             usage text
#---------------------------------------------------------------------

                                # Label must be single-quoted here
my $USAGE_TEXT = << 'END_OF_USAGE_TEXT';
Usage: $PROGNAME --run

This is an "nspr" package build utility script.  It must be run, after
the package  is installed, in  the directory  which contains the pack-
age include-file directory named "nspr".  It makes some patches to the
files stored in the "nspr" tree.
END_OF_USAGE_TEXT

#---------------------------------------------------------------------
#                         usage-text routine
#---------------------------------------------------------------------

# "UsageError" prints  usage text for the current program,  then term-
# inates the program with exit status one.

#---------------------------------------------------------------------

sub UsageError
{
    $USAGE_TEXT =~ s@^\s+@@s;
    $USAGE_TEXT =~ s@\$PROGNAME@$PROGNAME@g;

    $USAGE_TEXT = << "END";     # "END" must be double-quoted here
$PROGNAME $REVISION - $PURPOSE

$USAGE_TEXT
END
    $USAGE_TEXT =~ s@\s*\z@\n@s;

    if ($USE_LESS && (-t STDOUT) && open (OFD, "|/usr/bin/less"))
    {
                                # "END" must be double-quoted here
        $USAGE_TEXT = << "END";
To exit this "help" text, press "q" or "Q".  To scroll up or down, use
PGUP, PGDN, or the arrow keys.

$USAGE_TEXT
END
        print OFD $USAGE_TEXT;
        close OFD;
    }
    else
    {
        print "\n", $USAGE_TEXT, "\n";
    }

    exit ONE;
}

#---------------------------------------------------------------------
#                    directory traversal routines
#---------------------------------------------------------------------

# "AdjustList"  is a  "find"-compatible  list preprocessor.  This pre-
# processor is used by a "find" command in the main routine.

#---------------------------------------------------------------------

sub AdjustList
{
    my (@list) = @_;
    grep { !-l && !m@^\.{1,2}\z@; } @list;
}

#---------------------------------------------------------------------

# "ProcEntry"  is a  "find"-compatible file processor.  This processor
# is used by a "find" command in the main routine.

# Note: This routine must preserve "$_".

#---------------------------------------------------------------------

sub ProcEntry
{
    my $path;                   # Absolute pathname
                                # Filename (provided by "File::Find")
    my $name = "$File::Find::name";

    $name =~ s@^(\./)+@@;       # Strip leading occurrences of "./"
    $path =  "$CWD/$name";      # Absolute pathname
    $path =~ s@^//+@/@;         # Kludge

    push (@Paths, $path);       # Save path
    undef;
}

#---------------------------------------------------------------------
#                            main routine
#---------------------------------------------------------------------

sub Main
{
    my $OptRun;                 # Flag: "--run" option
    my $BaseDir;                # Base directory
    my $OutPath;                # Pathname to display
    my $LEN_CWD;                # Length of CWD path

    my $data;                   # Data buffer
    my $path;                   # Absolute path
    my $str;                    # Scratch

#---------------------------------------------------------------------
# Initial setup.

    select (STDERR); $| = ONE;
    select (STDOUT); $| = ONE;
                                # Fix problems for some filesystems
    $File::Find::dont_use_nlink = ONE;

    undef $/;

#---------------------------------------------------------------------
# Process the command line.

    for my $arg (@ARGV)
    {
        if ($arg =~ m@^-+run\z@i)
        {
            $OptRun = TRUE;
        }
        else
        {
            die "Invalid argument: $arg\n";
        }
    }
                                # Check for required switch
    &UsageError() unless $OptRun;

#---------------------------------------------------------------------
# Directory setup operations.

    if (!chdir ('nspr') || (!-f 'nspr.h'))
    {
        print STDERR << "END";

Error: $PROGNAME is intended for use:
after the "nspr" package is  built and installed.  It must be executed
in the directory  which contains the  package  include-file  directory
named "nspr".  It makes some patches to the files stored in the "nspr"
tree.
END
        exit (ONE);
    }

    $BaseDir =  '.';            # Base is '.' (current directory)

                                # The  next  four  statements  have no
                                # effect now,  but they  may be useful
                                # later
    $BaseDir =  '.' if !defined ($BaseDir);
    $BaseDir =~ s@^(.+)/$@$1@;
    while ($BaseDir =~ s@/(\./)+@/@g) {}
    while ($BaseDir =~ s@/[^/]+/\.\./@/@g) {}

                                # Go to base directory
    chdir ($BaseDir) ||
        die "Error #01: Can't access directory: $!\n$BaseDir";

    $CWD     = getcwd();        # Get absolute path for directory
    $LEN_CWD = length ($CWD);   # Save length of absolute path

#---------------------------------------------------------------------
# Future change: Document this code.

    my $base;
    ($base) = $CWD =~ m@/([^/]+)\z@;
    die "Error #02\n" if !defined ($base);

#---------------------------------------------------------------------
# Build a pathname list.

    my %args =
    (
        bydepth    => TRUE ,
        preprocess => \&AdjustList ,
        wanted     => \&ProcEntry
    );

    find (\%args, '.');
    @Paths = sort @Paths;

#---------------------------------------------------------------------
# Process directory tree.

    for $path (@Paths)
    {
        $str     =  $path;
        $str     =~ s@^(.{$LEN_CWD})@@;
        $str     =~ s@^/+@@;
        $OutPath =  $str;

# Note:  ".pdf" files  *must* be skipped,  because the "-T" test isn't
# reliable for PDFs. For most other file types, filename-based exclus-
# ion isn't strictly necessary,  but it speeds things up,  because the
# time required for "-l", "-f", and "-T" tests may be  significant for
# large directory trees.

        next if $path =~
m@\.(bz2|gif|gz|jpe?g|mp[234]|pdf|png|tar|tgz|xls|wav|wmv|zip)\z@i;

        next if -l $path;
        next unless $path =~ m@\.([ch]|cc)\z@i;

        my @stat = stat $path;
        next if !-f _;
        next if !-T _;
        next if $stat [7] > $MAX_FILE_SIZE;

        open (IFD, "<$path") || die "Error #03: $OutPath\n";
        binmode (IFD);
        $data = <IFD>;
        $data = "" unless defined ($data);
        close (IFD);

        next if $data =~ m@sub\s+ProcEntry@;
        my $orig_data = $data;

#---------------------------------------------------------------------
# Make the appropriate change.

        my $IPAT =  '#include[\011\040]+';
        my $INC  =  '#include';
        my $PR   =  'private';
        my $QC   =  '["<>]';
        $data    =~
            s@$IPAT${QC}($NSPRH)${QC}@$INC <nspr/$1>@g;
        $data    =~
            s@$IPAT${QC}$PR/($NSPRH)${QC}@$INC <nspr/$PR/$1>@g;

#---------------------------------------------------------------------
# Save changes to current file (if necessary).

        next if $data eq $orig_data;

        my ($mtime) = (stat $path) [9];
        die "Error #04: $path\n" if !defined ($mtime);

        open (OFD, ">$path") || die "Error #05: $path\n";
        print OFD $data;
        close (OFD) || die "Error #06: $path\n";
        utime ($mtime, $mtime, $path) if $FlagKeepTimes;
        print "$path\n";
    }

#---------------------------------------------------------------------
# Wrap it up.

                                # Print a status message
    print "$PROGNAME done\n";
    undef;
}

#---------------------------------------------------------------------
#                            main program
#---------------------------------------------------------------------

&Main();                        # Call the main routine
exit ZERO;                      # Normal exit
