#!/usr/bin/perl
# 
# genpatch - A utility that generates patches for submission to
# wine-patches@winehq.com
#
# Copyright Steven Elliott <elliotsl@mindspring.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

=head1 NAME

genpatch - A utility that generates patches for submission to
wine-patches@winehq.com

=head1 SYNOPSIS

genpatch [B<-v>] [B<-n> patch_name] [B<-f> patch_file]
         [B<-c> change_log] [B<-m> modified_files]
         [B<-a> added_files]

=head1 DESCRIPTION

The genpatch utility generated patches for submission to
wine-patches@winehq by acting as a wrapper to "cvs diff".  The "B<-v>"
switch specifies that verbose output should be generated.  The "B<-n>"
switch overrides the patch name ("Name" field) which defaults to a
numeric UTC date.  The "B<-f>" switch overrides the filename of the patch
which defaults to "patches/<patch_name>.diff".  The "B<-c>" switch
specifies the CVS change log entry ("ChangeLog" field) which can be
seen when "cvs log" is invoked.  The "B<-m>" ("ModifiedFiles" field) and
"B<-a>" ("AddedFiles" field) switches override the set of files that
would normally be included by the "cvs diff".  Normally "cvs diff"
includes all files modified relative to the deltas indicated by the
"CVS/Entries" file and ignores all newly added files.  The "B<-m>" switch
specifies a whitespace separated list of files that were modified.
The "B<-a>" switch specifies a whitespace separated list of files that
were added.

=head1 EXAMPLE

genpatch B<-n> NLSFix B<-c> "various fixes for NLS support" \
    B<-m> ole/ole2nls.c B<-a> ole/ole3nls.c

The above generates a patch named "NLSFix" in "patches/NLSFix.diff"
that includes a modification to "ole/ole2nls.c" and a newly added
"ole/ole3nls.c".

=cut

use strict;

use Getopt::Std;
use File::Basename;
use POSIX qw(strftime);

my $gen_date;       # date the patch was generated
my %options;        # command line options
my @modified_files; # optional list of files that were modified
my @added_files;    # added files as an array
my $added_file;     # added file being considered
my $cvs_line;       # line of output from CVS
my $mod_files_str;  # string that describes the modified files

# Default the patch name to the UTC time.  Use a more descriptive date for the
# patch generation date.
$options{n} = strftime "%Y%m%d%H%M", gmtime;
$gen_date = strftime "%Y/%m/%d %H:%M:%S UTC", gmtime;

unless(getopts("vn:f:c:m:a:p:", \%options))
{
    print STDERR "Usage: $0 [-v] [-n patch_name] [-f patch_file] " .
        "[-c change_log] [-m modified_files] [-a added_files] [-p path_to_patches]\n";
    exit 1;
}

$options{p} = "patches" unless(exists $options{p});
$options{f} = "$options{p}/$options{n}.diff" unless(exists $options{f});
$options{p} = dirname $options{f};
@added_files = split ' ', $options{a};
@modified_files = split ' ', $options{m};
$options{c} =~ s/\\n/\n\t/g;

if(-d $options{p})
{
    if(-e $options{f})
    {
        print STDERR "$options{f} already exists.  Aborting.\n";
        exit 1;
    }
}
else
{
    mkdir $options{p}, (0777 & ~umask) or
        die "Unable to mkdir $options{p}: $!";
}

$mod_files_str = exists($options{m}) ? $options{m} : "<see cvs diff>";
print "Generating $options{f}.\n" if($options{v});
open OPT_F, ">$options{f}" or die "Unable to open $options{f} for write: $!";
print OPT_F <<EOF;
Name: $options{n}
ChangeLog: $options{c}
GenDate: $gen_date
ModifiedFiles: $mod_files_str
AddedFiles: $options{a}
EOF

print "Invoking cvs diff.\n" if($options{v});
open CVS_IN, "cvs diff -u @modified_files|" or die "Unable to invoke cvs: $!";
while($cvs_line = <CVS_IN>)
{
    chomp $cvs_line;
    if($cvs_line =~ /^\? (.*)/)
    {
        push @added_files, $1 unless(exists $options{a});
    }
    else
    {
        print OPT_F <CVS_IN>;
    }
}
close CVS_IN;

foreach $added_file (@added_files)
{
    print "Adding $added_file as a new file.\n" if($options{v});
    open DIFF_IN, "diff -u /dev/null $added_file|" or die "Unable to " .
        "invoke diff: $!";
    print OPT_F <DIFF_IN>;
    close DIFF_IN;
}

close OPT_F;
