#!/usr/local/bin/perl

# Copyright (c) 2004-2012 Matthew Seaman. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#    1.  Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#    2.  Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials
#        provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

# @(#) $Id$
#

# Scan through the ports tree, and pick out all ports containing files
# updated after the date stored in the cache.  For deleted or added
# ports this shows up as modification of the category.  This program
# doesn't modify the cache at all, but it reads cache data.
# cache-update can use the same algorithm to find and perform updates.

use strict;
use warnings;
use BerkeleyDB;
use POSIX qw(strftime);

use FreeBSD::Portindex::Config qw(%Config read_config);
use FreeBSD::Portindex::ListVal;
use FreeBSD::Portindex::FileObject;
use FreeBSD::Portindex::Tree;

$0 =~ s@.*/@@;    # Script name for error messages

sub format_date($)
{
    my $date = shift;

    return strftime( "%Y.%m.%d.%H.%M.%S", localtime($date) );
}

MAIN:
{
    my $tree;
    my $updaters;

    read_config('portindex');

    $tree = FreeBSD::Portindex::Tree->new(
        -Env           => { -Home => $Config{CacheDir}, },
        -CacheFilename => $Config{CacheFilename},
    );

    # Redirect STDOUT if required
    if ( $Config{Output} ne '-' ) {
        open STDOUT, '>', $Config{Output}
          or die "$0: Can't open output \'$Config{Output}\' -- $!\n";
    }

    $updaters = FreeBSD::Portindex::ListVal->new();

    # Read all of the Makefiles / pkg-descr files from the cache, and
    # verify the recorded timestamp matches the mtime of the file as
    # it now is.

    for my $file ( sort $tree->allports(qr@^/@) ) {
        my $file_object;
        my $mtime;

        $file_object = $tree->get($file);
        next
          if $file_object->is_endemic();

        $mtime = $file_object->has_been_modified();

        if ($mtime) {
            print STDERR "$file: now ", format_date($mtime),
              " cached ", format_date( $file_object->MTIME() ), "\n"
              if $Config{Verbose};

            if ( $file_object->is_ubiquitous() ) {
                warn "$0: WARNING: $file modified since last update ",
                  "-- time for cache-init again?\n";
            } else {
                $updaters->insert( $file_object->USED_BY() );
            }
        }
    }

    # Print out all of the ports that have been updated
    print join( "\n", $updaters->get_sorted() ), "\n"
      if ( $updaters->length() );
}

__END__


=head1 NAME

find-updated -- generate a list of those ports depending on files that
have been modified at a time different to the time stored in the cache.

=head1 SYNOPSIS

B<find-updated> [B<-hvq>] [B<-c> F<dir>] [B<-C> F<file>] [B<-T> F<file>] [B<-p> F<dir>] [B<-o> F<file>]

=head1 DESCRIPTION

B<find-updated> scans through all of the file-objects stored in the
cache, the whole ports tree and checks the modification time of each
of them, comparing the last modification time as reported from the
filesystem with the last modification time recorded in the cache when
B<cache-init> or B<cache-update> was run.

It prints out a list of ports whose F<INDEX> entries might have been
affected by modifications to those files.  The usual exemptions for
ubiquitous (used by every port) and endemic (change unlikely to affect
the F<INDEX>) makefiles apply.

Ports being added or removed show up as modifications to the category
makefile: B<find-updated> does not currently attempt to display the
portnames for new additions, but deletions will appear.

=head2 Configuration Files

B<find-updated> shares configuration files with B<portindex>,
B<portdepends> B<cache-init>, B<cache-update> and B<make-readmes>.
Any configuration settings are taken from the following locations,
where the later items on this list override the earlier:

=over 8

=item *

Built-in settings from the B<FreeBSD::Portindex::Config> perl module.

=item *

The system wide configuration file F</usr/local/etc/portindex.cfg>

=item *

The per-user configuration file F<${HOME}/.portindexrc>.  This file is
ignored if the process is run as root.

=item *

The local configuration file, found in the current working directory
of the B<cache-init> process F<./.portindexrc>.  This file is ignored
if the process is run as root.

=item *

The program command line.

=back

All of the configuration files are optional.  A summary of the
resultant configuration options including the effect of any command
line settings is printed as part of the help text when B<portindex> is
invoked with the C<-h> option.

=head1 OPTIONS

=over 8

=item B<-h>

=item B<--help>

Print a brief usage message and a summary of the configuration
settings after command line processing and then exit.

=item B<-v>

=item B<--verbose>

Turn on verbose output printed to C<STDERR>.  This is the default.

=item B<-q>

=item B<--quiet>

=item B<--noverbose>

Turn off verbose output to C<STDERR>.  Using both the B<-v> amd B<-q>
options together does not make any sense, but neither does it generate
an error.  The last mentioned of the two options will prevail.

=item B<-c> F<dir>

=item B<--cache-dir>=F<dir>

The location of the B<portindex> data cache, by default
F</var/db/portindex>.

=item B<-C> F<file>

=item B<--cache-file>=F<file>

Berkeley DB Btree file containing the cached and post-processed values
of a number of C<make> variables for all of the ports in the tree.
This file name will be relative to the cache directory (B<-c> option
above) unless an absolute path is given.  Defaults to
F<portindex-cache.db>.

=item B<-T> F<file>

=item B<--timestamp-file>=F<file>

A file within the cache directory whose modification time marks the
last time that data was modified in or added to the cache.  Defaults
to F<portindex-timestamp>

=item B<-o> F<file>

=item B<--output>=F<file>

Filename to write the generated list of updated ports to.  Setting this to
F<-> means output to STDOUT, which is the default.

=back

=head1 FILES

=over 16

=item F</usr/ports>

The default ports directory.

=item F</var/db/portindex>

The location of the data cache.

=item F<portindex-cache.db>

Btree file containing cached C<make describe> output.

=item F<__db.001>, F<__db.002>, F<__db.003>, F<__db.004>

Files used as part of the internal workings of BerkeleyDB, for memory
pool management and DB locking.  Will be recreated automatically if
deleted.

=item F<portindex-timestamp>

This file contains the last time and date that the cache was updated
or modified.

=item F</usr/local/etc/portindex.cfg>

System-wide configuration file.

=item F<${HOME}/.portindexrc>

Per-user configuration file

=item F<./.portindexrc>

Local configuration file

=back

=head1 SEE ALSO

L<portindex(1)>, L<cache-init(1)>, L<cache-update(1)>, L<cvsup(1)>,
L<ports(7)>

=head1 BUGS

B<find-updated> does not automatically discover either new ports, or
newly created makefiles except for the files created when port options
are defined.

=cut

#
# That's All Folks!
#
