[patch-o-matic-ng] pom-ng: get it back into git tree with empty patchlets directory
Pablo Neira
netfilter-cvslog-bounces at lists.netfilter.org
Tue Feb 3 12:43:33 CET 2009
Gitweb: http://git.netfilter.org/cgi-bin/gitweb.cgi?p=patch-o-matic-ng.git;a=commit;h=f15dab0b607a64874ea92e18274f2a97746ef767
commit f15dab0b607a64874ea92e18274f2a97746ef767
Author: Pablo Neira Ayuso <pablo at netfilter.org>
AuthorDate: Tue Feb 3 12:42:07 2009 +0100
Commit: Pablo Neira Ayuso <pablo at netfilter.org>
CommitDate: Tue Feb 3 12:42:07 2009 +0100
pom-ng: get it back into git tree with empty patchlets directory
This patch gets pom-ng back into the Netfilter's git tree but with
an empty patchlets repository.
Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
via f15dab0b607a64874ea92e18274f2a97746ef767 (commit)
from f5b49ab74cc62336ee36e290fa85f13002f53667 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit f15dab0b607a64874ea92e18274f2a97746ef767
Author: Pablo Neira Ayuso <pablo at netfilter.org>
Date: Tue Feb 3 12:42:07 2009 +0100
pom-ng: get it back into git tree with empty patchlets directory
This patch gets pom-ng back into the Netfilter's git tree but with
an empty patchlets repository.
Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
-----------------------------------------------------------------------
Netfilter_POM.pm | 1126 +++++++++++++++++++++++++++++++++++++++++++++++
README.newpatches | 177 ++++++++
patch2pom/PatchChunk.pm | 192 ++++++++
patch2pom/PatchFile.pm | 133 ++++++
patch2pom/README | 3 +
patch2pom/patch2pom | 346 +++++++++++++++
patchlets/config | 4 +
pom2patch/pom2patch | 54 +++
runme | 510 +++++++++++++++++++++
sources.list | 17 +
10 files changed, 2562 insertions(+), 0 deletions(-)
create mode 100644 Netfilter_POM.pm
create mode 100644 README.newpatches
create mode 100644 patch2pom/PatchChunk.pm
create mode 100644 patch2pom/PatchFile.pm
create mode 100644 patch2pom/README
create mode 100644 patch2pom/patch2pom
create mode 100644 patchlets/config
create mode 100644 pom2patch/pom2patch
create mode 100755 runme
create mode 100644 sources.list
This patch gets pom-ng back into the Netfilter's git tree but with
an empty patchlets repository.
Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
diff --git a/Netfilter_POM.pm b/Netfilter_POM.pm
new file mode 100644
index 0000000..e86e2e0
--- /dev/null
+++ b/Netfilter_POM.pm
@@ -0,0 +1,1126 @@
+#!/usr/bin/perl
+#
+# Netfilter_POM.pm, part of the patch-o-matic 'next generation' package
+# (C) 2003-2004 by Harald Welte <laforge at netfilter.org>
+# (C) 2004 by Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+#
+# This code is subject to the GNU GPLv2
+#
+# $Id$
+#
+# The idea is to have the backend seperated from the frontend. Thus,
+# other frontends (like ncurses,...) could potentially be implemented on
+# top of this.
+#
+package Netfilter_POM;
+
+# we could export the public functions into caller namespace
+#require Exporter;
+#BEGIN {
+# @ISA = qw(Exporter);
+#}
+#@EXPORT = qw();
+
+use strict;
+use Carp;
+use File::Temp;
+use File::Copy;
+use File::Path;
+use File::Basename;
+#use Data::Dumper;
+
+my $BIN_PATCH = "patch";
+
+# print the last error messages
+#
+sub perror {
+ my $self = shift;
+
+ if ($self->{ERRMSG}) {
+ print STDERR $self->{ERRMSG};
+ $self->{ERRMSG} = '';
+ }
+}
+
+# count the number of hunks in a unified diff file
+#
+sub count_hunks {
+ my($file) = @_;
+ my($hunk_count);
+
+ open(INFILE, $file) || return -1;
+ while (my $line = <INFILE>) {
+ chomp($line);
+ if ($line =~ /^@@/) {
+ $hunk_count++;
+ }
+ }
+ close(INFILE);
+
+ return $hunk_count;
+}
+
+# copy patch files from the source tree, collecting
+# file names from the unified diff file
+#
+sub copy_patchfiles {
+ my $self = shift;
+ my($file, $copy, $proj) = @_;
+ my @files;
+
+ open(INFILE, $file)
+ or croak "Cannot open patch file $file: $!";
+ while (my $line = <INFILE>) {
+ chomp($line);
+ if ($line =~ /^\+\+\+ (\S+)/) {
+ push(@files, $1);
+ }
+ }
+ close(INFILE);
+ foreach $file (@files) {
+ # patch can be applied by 'patch -p1'
+ $file =~ s,[^/]+/,,;
+ my $srcfile = "$self->{projects}->{$proj}->{PATH}/$file";
+ my $destfile = "$copy/$file";
+ my $destdir = File::Basename::dirname($destfile);
+ if (!-d $destdir) {
+ if (!File::Path::mkpath($destdir)) {
+ $self->{ERRMSG} .= "unable to mkpath($destdir) while copying patchfiles: $!\n";
+ return 0;
+ }
+ }
+ # Don't copy existing files and ignore errors
+ # (there can be new files in patches (but shouldn't!)
+ File::Copy::copy($srcfile, $destfile) unless -f $destfile;
+ }
+ return 1;
+}
+
+# get the kernel version of a specified kernel tree
+#
+sub linuxversion {
+ my $self = shift;
+ my($version, $patchlevel, $sublevel);
+
+ open(MAKEFILE, "$self->{projects}->{linux}->{PATH}/Makefile")
+ or croak "No kernel Makefile in $self->{projects}->{linux}->{PATH}!";
+ while (my $line = <MAKEFILE>) {
+ chomp($line);
+ if ($line =~ /^VERSION =\s*(\S+)/) {
+ $version = $1;
+ } elsif ($line =~ /^PATCHLEVEL =\s*(\S+)/) {
+ $patchlevel = $1;
+ } elsif ($line =~ /^SUBLEVEL =\s*(\S+)/) {
+ $sublevel = $1;
+ }
+ }
+ close(MAKEFILE);
+ $self->{projects}->{linux}->{VERSION} = join('.', $version, $patchlevel, $sublevel);
+}
+
+# get the iptables version of a specified source tree
+#
+sub iptablesversion {
+ my $self = shift;
+ my($version);
+ my(%sources) = (
+ 'Makefile' => qr/^IPTABLES_VERSION:=(\S+)/,
+ '/include/xtables.h' => qr/^\#define XTABLES_VERSION "(\S+)"/,
+ );
+
+ foreach my $file (keys %sources) {
+ open(FILE, "$self->{projects}->{iptables}->{PATH}/$file")
+ or next;
+ while (my $line = <FILE>) {
+ chomp($line);
+ if ($line =~ /$sources{$file}/) {
+ $version = $1;
+ # don't support versioning like 1.2.3b!
+ $version =~ s/[^\d\.]//g;
+ close(FILE);
+ $self->{projects}->{iptables}->{VERSION} = $version;
+ return;
+ }
+ }
+ close(FILE);
+ }
+ croak "Makefile in $self->{projects}->{iptables}->{PATH} does not contain iptables version!";
+}
+
+# Check existence of elements in a patchlet
+# without springing into existence the checked elements
+sub safe_exists {
+ my $patchlet = shift;
+ my @elements = @_;
+
+ my $href = $patchlet;
+ foreach (@elements) {
+ return 0 unless exists($href->{$_}) && $href->{$_};
+ $href = $href->{$_};
+ }
+ return 1;
+}
+
+# this should be taken from RPM or something like that
+# first argument is the project we want to patch
+# second argument is the operator
+# third argument is the version of the patch we want to apply
+#
+sub version_compare {
+ my $self = shift;
+ my($proj, $op, $ver2) = @_;
+ my($ver1, @ver1, @ver2, $sv, $res);
+ my(@weight) = (10000, 100, 1);
+
+ @ver1 = split(/\./, $self->{projects}->{$proj}->{VERSION});
+ @ver2 = split(/\./, $ver2);
+
+ $ver1 = $ver2 = 0;
+ foreach $sv (0..$#ver2) {
+ $ver1 += $ver1[$sv] * $weight[$sv];
+ $ver2 += $ver2[$sv] * $weight[$sv];
+ }
+ eval "\$res = $ver1 $op $ver2";
+ # We return the numeric version of the patch
+ # for requirements_fulfilled below
+ return ($res ? $ver2 : 0);
+}
+
+# are the info file requirements for a specific patchlet fulfilled?
+#
+sub info_reqs_fulfilled {
+ my $self = shift;
+ my($patchlet, $proj, $version) = @_;
+
+ # Project version we want to patch must fulfil the version
+ # requirements of a given patchlet
+ my $pver = $proj;
+ $pver .= '-' . $version if $version;
+ foreach my $req (@{$patchlet->{info}->{requires}}) {
+ my ($prog, $op, $ver) = $req =~/(\S+)\s*(==|>=|>|<=|<)\s*(\S+)/;
+
+ # if the requirement refers to the tested patchlet,
+ # project version must fulfil the requirement.
+ # Multiple requirements are ANDed.
+ return 0 if $pver eq $prog
+ && !$self->version_compare($proj, $op, $ver);
+ }
+ return 1;
+}
+
+# are the requirements for a specific patchlet fulfilled?
+#
+sub requirements_fulfilled {
+ my $self = shift;
+ my $patchlet = shift;
+ my($type, $proj, $ver, $bingo, $match);
+ my $best_match = 0;
+
+ # Search best (nearest) match
+ foreach $type (qw(patch files ladds)) {
+ next unless exists $patchlet->{$type};
+ foreach $proj (keys %{$patchlet->{$type}}) {
+ $bingo = 0;
+ foreach $ver (keys %{$patchlet->{$type}->{$proj}}) {
+ # No version has got the lowest possible match value
+ $match = !$ver ? 1
+ : $ver =~ /$self->{projects}->{$proj}->{branch}/
+ && $self->version_compare($proj, '>=', $ver);
+ next if $bingo >= $match
+ || !$self->info_reqs_fulfilled($patchlet, $proj, $ver);
+ $bingo = $match;
+ $patchlet->{$type}->{$proj}->{best} =
+ $patchlet->{$type}->{$proj}->{$ver};
+ $best_match = 1;
+ }
+ if ($bingo == 0) {
+ delete $patchlet->{$type}->{$proj};
+ }
+ }
+ }
+FOUND:
+ #print Dumper($patchlet);
+ if ($best_match) {
+ return 1;
+ } else {
+ $self->{ERRMSG} .= "$patchlet->{name} does not match your source trees, skipping...\n";
+ return 0;
+ }
+}
+
+# recursively test if all dependencies are fulfilled
+#
+# return values:
+# 1 dependencies fulfilled
+# 0 dependencies not fulfilled
+# -1 dependencies cannot be fulfilled [conflicting patchlets]
+#
+sub dependencies_fulfilled {
+ my $self = shift;
+ my $patchlet = shift;
+ my $plname = $patchlet->{name};
+
+ for my $depend (@{$patchlet->{info}->{depends}}) {
+ # Dance around references!
+ my($inverse, $dep) = $depend =~ /^(!)?(.*)/;
+
+ if (!defined($self->{patchlets}->{$dep})) {
+ $self->{ERRMSG} .= "$plname has dependency on $dep, but $dep is not known\n";
+ return 0;
+ }
+ my $applied = grep($_ eq $dep, @{$self->{applied}});
+ if ($inverse && $applied) {
+ $self->{ERRMSG} .= "present '$dep' conflicts with to-be-installed '$plname'\n";
+ return -1;
+ } elsif ($applied || $inverse) {
+ # patch can be applied if all its dependecies had been applied
+ # Don't check the dependecies of conflicting patches
+ next;
+ }
+ my $ret = $self->dependencies_fulfilled($self->{patchlets}->{$dep});
+ return $ret if $ret <= 0;
+ if (!$self->apply_patchlet($self->{patchlets}->{$dep}, !$inverse, 1)) {
+ if (!$inverse) {
+ $self->{ERRMSG} .= "$dep not applied\n";
+ return 0;
+ } else {
+ $self->{ERRMSG} .= "present '$dep' conflicts with to-be-installed '$plname'\n";
+ return -1;
+ }
+ }
+ }
+ return 1;
+}
+
+sub apply_dependency {
+ my $self = shift;
+ my($plname, $force, $test, $copy) = @_;
+
+ return 1 if grep($_ eq $plname, @{$self->{applied}});
+
+ if (!$force) {
+ # first test, then apply
+ if (!$self->apply_patchlet($self->{patchlets}->{$plname}, 0, 1, $copy)) {
+ # test failed, maybe it's already applied? Check by testing to reverse it
+ if (!$self->apply_patchlet($self->{patchlets}->{$plname}, 1, 1, $copy)) {
+ $self->{ERRMSG} .= "apply_dependency: unable to apply dependent $plname\n";
+ return 0;
+ } else {
+ # apparently it was already applied, add it to list of applied patches
+ push(@{$self->{applied}}, $plname);
+ return 1;
+ }
+ }
+ }
+ if (!$test) {
+ if (!$self->apply_patchlet($self->{patchlets}->{$plname}, 0, 0, $copy)) {
+ $self->{ERRMSG} .= "apply_dependency: unable to apply dependent $plname\n";
+ return 0;
+ } else {
+ push(@{$self->{applied}}, $plname);
+ print("apply_dependency: successfully applied $plname\n") unless $copy;
+ }
+ }
+ return 1;
+}
+
+# apply_dependencies - recursively apply all dependencies
+# patchlet: patchlet subject to recursive dependency resolving
+# force: forcibly try to apply dependent patches (to see .rej's)
+# test: just test wether patch could be applied
+sub apply_dependencies {
+ my $self = shift;
+ my($patchlet, $force, $test, $copy) = @_;
+ my $plname = $patchlet->{name};
+
+ for my $dep (@{$patchlet->{info}->{depends}}) {
+ # don't revert existing patches
+ next if $dep =~ /^!/;
+
+ if (!defined($self->{patchlets}->{$dep})) {
+ $self->{ERRMSG} .= "$plname has dependency on $dep, but $dep is not known\n";
+ return 0;
+ }
+
+ # We have to call requirements_fulfilled because
+ # patches can be specified on commandline too.
+ # However, there can be different dependencies in
+ # different branches, so skip unmet dependencies!
+ if ($self->requirements_fulfilled($self->{patchlets}->{$dep})) {
+ return 0 unless $self->apply_dependencies($self->{patchlets}->{$dep},
+ $force, $test, $copy)
+ && $self->apply_dependency($dep, $force, $test, $copy);
+ }
+ }
+
+ return 1;
+}
+
+# recurse through subdirectories, pushing all filenames into
+# the correspondig ladds|files->project->version array by
+# differentiating between whole new files and line-adds (ladds)
+#
+sub recurse {
+ my $self = shift;
+ my($pdir, $dir, $patchlet, $proj, $ver) = @_;
+
+ opendir(DIR, $dir)
+ or croak "can't open directory $dir: $!";
+ # Don't miss .foo-test files!
+ my @dents = sort grep {!/^(\.\.?|CVS|\.svn|.*~)$/} readdir(DIR);
+ closedir(DIR);
+ foreach my $dent (@dents) {
+ my $fullpath = "$dir/$dent";
+ if (-f $fullpath) {
+ my $key = ($dent =~ /\.ladd/ ? 'ladds' : 'files');
+ push(@{$patchlet->{$key}->{$proj}->{$ver}}, "$pdir/$fullpath");
+ } elsif (-d _) {
+ $self->recurse($pdir, $fullpath, $patchlet, $proj, $ver);
+ }
+ }
+}
+
+# parse info file associated with patchlet
+#
+sub parse_patch_info {
+ my $self = shift;
+ my($info, $patchlet) = @_;
+ my($help, $list);
+
+ ($patchlet->{info}->{file} = $info) =~ s,.*/,,;
+
+ open(INFILE, $info)
+ or croak "unable to open $info: $!";
+ while (my $line = <INFILE>) {
+ chomp($line);
+ if ($help) {
+ $patchlet->{info}->{help} .= $line . "\n";
+ } elsif ($line =~ /^Title:\s+(.*)/) {
+ $patchlet->{info}->{title} = $1;
+ } elsif ($line =~ /^Author:\s+(.*)/) {
+ $patchlet->{info}->{author} = $1;
+ } elsif ($line =~ /^Status:\s+(.*)/) {
+ $patchlet->{info}->{status} = $1;
+ } elsif ($line =~ /^Repository:\s+(.*)/) {
+ $patchlet->{info}->{repository} = $1;
+ } elsif ($line =~ /^Requires:\s+(.*)\s*/) {
+ push(@{$patchlet->{info}->{requires}}, $1);
+ } elsif ($line =~ /^Depends:\s+(.*)\s*/) {
+ ($list = $1) =~ tr/,/ /;
+ push(@{$patchlet->{info}->{depends}}, split(/\s+/, $list));
+ } elsif ($line =~ /^Recompile:\s+(.*)\s*/) {
+ ($list = $1) =~ tr/,/ /;
+ push(@{$patchlet->{info}->{recompile}}, split(/\s+/, $list));
+ } elsif ($line =~ /^Successor:\s+(\S+)/) {
+ $patchlet->{info}->{successor} = $1;
+ } elsif ($line =~ /^Version:\s+(.*)/) {
+ $patchlet->{info}->{version} = $1;
+ } elsif ($line =~ /^\s*$/) {
+ $help = 1;
+ } else {
+ close(INFILE);
+ croak "unknown config key '$line' in $info";
+ }
+ }
+ close(INFILE);
+
+ croak "missing repository definition from $info!"
+ unless defined($patchlet->{info}->{repository});
+
+ # Backward compatibility
+ return if defined $patchlet->{info}->{help};
+
+ $info =~ s/info$/help/;
+ open(INFILE, $info) or return;
+ while (<INFILE>) {
+ $patchlet->{info}->{help} .= $_;
+ }
+ close(INFILE);
+
+}
+
+# Parse a single patchlet specified as parameter.
+# The collected info is stored in a hash reference
+# with the structure below. If you change the structure,
+# make notes here!
+#
+# patchlet = {
+# # basedir relative to POM dir
+# basedir => dirname,
+# # filenames are relative to basedir
+# # leading 'subdir/./' must be taken into account
+# # for files from subdirectories (files and ladds)
+# name => patchname, # dirname without trailing '/'
+# info => {
+# file => filename,
+# title => title,
+# author => author,
+# status => status,
+# repository => repository,
+# requires => [ requirement ],
+# depends => [ dependency ],
+# recompile => [ recompile ],
+# successor => patchname
+# version => patchlet version
+# help => txt,
+# },
+# patch => {
+# project => {
+# version => [ filename ],
+# },
+# },
+# files => {
+# project => {
+# version => [ filename ],
+# },
+# },
+# ladds => {
+# project => {
+# version => [ filename ],
+# },
+# },
+# }
+sub parse_patchlet {
+ my $self = shift;
+ my $patchdir = shift;
+ my $patchlet;
+
+ $patchlet->{basedir} = $patchdir;
+ ($patchlet->{name} = $patchdir) =~ s,\./,,;
+ # parse our info file
+ $self->parse_patch_info($patchdir . '/info', $patchlet);
+
+ # get list of source files that we'd need to copy
+ opendir(PDIR, $patchdir)
+ or croak "unable to open patchdir $patchdir: $!";
+ my @dents = sort grep {!/^(\.\.?|CVS|\.svn|.*~)$/} readdir(PDIR);
+ closedir(PDIR);
+
+ foreach my $pf (@dents) {
+ my $proj;
+ my $ver;
+ my $oldpwd;
+
+
+ if ($pf =~ /\.patch/) {
+ # Patch file of a project:
+ # project[-ver[.plev[.sublev]]].patch[_num]
+ $pf =~ /((-([\d\.]+))?\.patch(_\d+.*)?)$/;
+ $ver = $3;
+ ($proj = $pf) =~ s/$1//;
+ push(@{$patchlet->{patch}->{$proj}->{$ver}}, $pf);
+ } elsif (-d "$patchdir/$pf") {
+ # Project directory for ladd and whole files:
+ # project[-ver[.plev[.sublev]]]
+ $pf =~ /(-([\d\.]*))?$/;
+ $ver = $2;
+ ($proj = $pf) =~ s/$1//;
+ my $oldpwd = `pwd`;
+ chomp($oldpwd);
+ chdir("$patchdir/$pf");
+ $self->recurse($pf, '.', $patchlet, $proj, $ver);
+ chdir($oldpwd);
+ }
+ }
+
+ #print Dumper $patchlet;
+ print '.';
+ return $patchlet;
+}
+
+# parse a single update patch specified as parameter
+#
+sub parse_update {
+ my $self = shift;
+ my $pfile = shift;
+ my $patchlet;
+ my($project, $version, $txt);
+
+ $patchlet->{basedir} = File::Basename::dirname($pfile);
+ ($patchlet->{name} = $pfile) =~ s,.*/,,;
+ # parse our info file
+ $self->parse_patch_info($pfile . '.info', $patchlet);
+
+ # n_proj[-ver[.plev[.sublev]]][txt].patch
+ $patchlet->{name} =~ /^\d+_(.*?)(-([\d\.]+))(.*)\.patch$/;
+ ($project, $version, $txt) = ($1, $3, $4);
+ if (!$txt) {
+ # Incremental patch: correct version number
+ $version =~ s/(\d+)$/$1-1/e;
+ }
+ $patchlet->{patch}->{$project}->{$version} = [ $patchlet->{name} ];
+
+ # print Dumper $patchlet;
+ print '.';
+ return $patchlet;
+}
+
+# apply an old-style lineadd file
+#
+sub apply_lineadd {
+ my $self = shift;
+ my($patchlet, $laddfile, $fname, $revert, $test) = @_;
+ my @newlines;
+ my $kconfigmode;
+ my $configmode;
+ my $lookingfor;
+
+ if (!open(LADD, $laddfile)) {
+ $self->{ERRMSG} .= "unable to open ladd $laddfile\n";
+ return 0;
+ }
+
+ my ($srcfile, $extn) = $fname =~ /(.*?)(\.ladd(_\d+.*)?)?$/;
+
+ if ($srcfile =~ /Kconfig$/) {
+ $kconfigmode = 1;
+ $lookingfor = $revert ? <LADD> : "endmenu\n";
+ } elsif ($srcfile =~ /Configure\.help/) {
+ $configmode = 1;
+ $lookingfor = <LADD>;
+ } else {
+ $lookingfor = <LADD>;
+ }
+
+ if (!open(SRC, $srcfile)) {
+ close(LADD);
+ $self->{ERRMSG} .= "unable to open ladd src $srcfile\n";
+ return 0;
+ }
+
+ my $found = 0;
+ SRCLINE: while (my $line = <SRC>) {
+ push(@newlines, $line);
+ if ($line eq $lookingfor) {
+ $found = 1;
+ if ($revert == 0) {
+ my ($prev, $next, $last);
+ if ($kconfigmode) {
+ $prev = pop(@newlines);
+ } elsif ($configmode) {
+ while (($line = <SRC>) !~ /^\S/) {
+ push(@newlines, $line);
+ }
+ $next = $line;
+ }
+ while (my $newline = <LADD>) {
+ push(@newlines, $newline);
+ $last = $newline;
+ }
+ # ugly kconfig/configure.help hacks
+ if ($kconfigmode) {
+ push(@newlines, "\n");
+ push(@newlines, $prev);
+ } elsif ($configmode) {
+ push(@newlines, "\n")
+ unless $last =~ /^\s*$/;
+ push(@newlines, $next);
+ }
+ # append rest of sourcefile
+ while ($line = <SRC>) {
+ push(@newlines, $line);
+ }
+ } else {
+ pop(@newlines) if $kconfigmode;
+ while (my $newline = <LADD>) {
+ my $srcline = <SRC>;
+ if ($newline ne $srcline) {
+ $found = -1;
+ last SRCLINE;
+ }
+ }
+ }
+ }
+ }
+ close(LADD);
+ close(SRC);
+
+ if ($found == 0) {
+ $self->{ERRMSG} .= "unable to find ladd slot in src $srcfile ($laddfile)\n";
+ return 0;
+ } elsif (!$test && $found == -1) {
+ $self->{ERRMSG} .= "unable to find all to-be-removed lines in $srcfile\n";
+ return 0;
+ }
+
+ if ($test == 0) {
+ my $newfile = "${srcfile}.$$";
+ if (!open(SRC, ">${newfile}")) {
+ $self->{ERRMSG} .= "unable to write to file ${newfile}\n";
+ return 0;
+ }
+ foreach my $line (@newlines) {
+ print(SRC $line);
+ }
+ close(SRC);
+ if (!rename($newfile, $srcfile)) {
+ $self->{ERRMSG} .= "unable to replace file $srcfile\n";
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+sub apply_newfiles {
+ my $self = shift;
+ my($patchlet, $proj, $revert, $test, $copy) = @_;
+ my($projpath, $file, $srcfile, $dir, $destdir, $destfile);
+ my $test_found;
+ my $test_notfound;
+
+ return 1 unless safe_exists($patchlet, ('files', $proj, 'best'));
+
+ $projpath = $copy || $self->{projects}->{$proj}->{PATH};
+ for my $file (@{$patchlet->{files}->{$proj}->{best}}) {
+ $srcfile = "$patchlet->{basedir}/$file";
+ # project/./
+ ($dir = File::Basename::dirname($file)) =~ s,([^/]+/){2},,;
+ $destdir = "$projpath/$dir";
+ $destfile = $destdir . '/' . File::Basename::basename($file);
+ if (!$test) {
+ if (!$revert) {
+ if (!-d $destdir) {
+ if (!File::Path::mkpath($destdir)) {
+ $self->{ERRMSG} .= "unable to mkpath($destdir) while applying newfile: $!\n";
+ return 0;
+ }
+ }
+ if (!File::Copy::copy($srcfile, $destfile)) {
+ $self->{ERRMSG} .= "unable to copy $srcfile to $destfile: $!\n";
+ return 0;
+ }
+ # .foo-test is executable
+ chmod((stat($srcfile))[2] & 07777, $destfile);
+ } else {
+ if (!unlink($destfile)) {
+ $self->{ERRMSG} .= "unable to remove $destfile while reverting newfile: $!\n";
+ return 0;
+ }
+ }
+ } else {
+ # check if the file exists in the real directory, not the copy, it doesn't contain all files.
+ $destfile = $self->{projects}->{$proj}->{PATH} . "/$dir/" . File::Basename::basename($file);
+ if (-f $destfile) {
+ $test_found++;
+ } else {
+ $test_notfound++;
+ }
+ }
+ }
+
+ if ($test) {
+ if (!$revert && $test_found) {
+ $self->{ERRMSG} .= "newfile: $test_found files in our way, unable to apply\n";
+ return 0;
+ } elsif ($revert && $test_notfound) {
+ $self->{ERRMSG} .= "newfile: $test_notfound files missing, unable to revert\n";
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+sub apply_lineadds {
+ my $self = shift;
+ my($patchlet, $proj, $revert, $test, $copy) = @_;
+ my($projpath, $file, $target, $copyfile);
+
+ return 1 unless safe_exists($patchlet, ('ladds', $proj, 'best'));
+
+ # print Dumper $patchlet;
+ # apply the line-adds
+ $projpath = $copy || $self->{projects}->{$proj}->{PATH};
+ for $file (@{$patchlet->{ladds}->{$proj}->{best}}) {
+ my $basename = File::Basename::basename($file);
+ if ($proj eq 'linux') {
+ if ($self->{projects}->{$proj}->{VERSION} =~ /^2\.4\.\d+/
+ && $basename =~ /^Kconfig\.ladd/) {
+ next;
+ }
+ if ($self->{projects}->{$proj}->{VERSION} =~ /^2\.6\.\d+/
+ && ($basename =~ /^Config\.in\.ladd/
+ || $basename =~ /^Configure\.help/)) {
+ next;
+ }
+ }
+ # project/./
+ ($target = $file) =~ s,([^/]+/){2},,;
+ ($copyfile = $target) =~ s/\.ladd.*//;
+ if ($copy && ! -f "$projpath/$copyfile") {
+ my $destdir = File::Basename::dirname("$projpath/$copyfile");
+ if (!-d $destdir) {
+ if (!File::Path::mkpath($destdir)) {
+ $self->{ERRMSG} .= "unable to mkpath($destdir) while testing lineadds: $!\n";
+ return 0;
+ }
+ }
+ if (!File::Copy::copy("$self->{projects}->{$proj}->{PATH}/$copyfile", "$projpath/$copyfile")) {
+ $self->{ERRMSG} .= "unable to copy $self->{projects}->{$proj}->{PATH}/$copyfile while testing lineadds: $!\n";
+ return 0;
+ }
+ }
+ return 0 unless $self->apply_lineadd($patchlet,
+ $patchlet->{basedir}.'/'.$file,
+ $projpath.'/'.$target,
+ $revert,
+ $test);
+ }
+
+ return 1;
+}
+
+sub apply_patches {
+ my $self = shift;
+ my($patchlet, $proj, $revert, $test, $copy, $verbose) = @_;
+
+ return 1 unless safe_exists($patchlet, ('patch', $proj, 'best'));
+
+ my $projpath = $copy || $self->{projects}->{$proj}->{PATH};
+
+ my @filelist;
+ if ($revert) {
+ @filelist = reverse @{$patchlet->{patch}->{$proj}->{best}};
+ } else {
+ @filelist = @{$patchlet->{patch}->{$proj}->{best}};
+ }
+
+ for my $file (@filelist) {
+ # apply the patch itself
+ my $options;
+ if ($revert) {
+ $options .= "-R ";
+ }
+ if ($test && !$copy) {
+ $options .= "--dry-run ";
+ }
+ my $patchfile = "$patchlet->{basedir}/$file";
+ my $cmd = sprintf("%s -f -p1 -d %s %s < %s",
+ $BIN_PATCH, $projpath,
+ $options,
+ $patchfile);
+ my $missing_files;
+ my $rejects;
+ my $notempty;
+ my $hunks = count_hunks($patchfile);
+ my $patch_output = "";
+ open(PATCH, "$cmd|") || die("can't start patch '$cmd': $!\n");
+ while (my $line = <PATCH>) {
+ # FIXME: parse patch output
+ $patch_output .= ">> $line";
+ chomp($line);
+ if ($line =~ /No file to patch/) {
+ $missing_files++;
+ } elsif ($line =~ /FAILED at/) {
+ $rejects++;
+ } elsif ($line =~ /not empty after patch, as expected/) {
+ $notempty++;
+ }
+ }
+ close(PATCH);
+
+ if ($test) {
+ if ($verbose && (($missing_files != 0) || ($rejects != 0)))
+ {
+ print "patch output was:\n$patch_output\n";
+ }
+ if ($missing_files != 0) {
+ $self->{ERRMSG} .= "cannot apply $patchfile: ($missing_files missing files)\n";
+ return 0;
+ # } elsif ($rejects*2 > $hunks) {
+ } elsif ($rejects != 0) {
+ $self->{ERRMSG} .= "cannot apply $patchfile: ($rejects rejects out of $hunks hunks)\n";
+ return 0;
+ } else {
+ # could be applied!
+ #printf(" ALREADY APPLIED $patchfile: (%d rejects out of %d hunks)\n", $rejects, $hunks;
+ }
+ } else {
+ if ($missing_files != 0) {
+ $self->{ERRMSG} .= "$patchfile: ERROR ($missing_files missing files)\n";
+ return 0;
+ } elsif ($rejects != 0) {
+ $self->{ERRMSG} .= "$patchfile: ERROR ($rejects rejects out of $hunks hunks)\n";
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+# apply a given patchlet to a given kernel tree
+#
+# return value:
+# normal (non-test) mode: 1 on success, 0 on failure
+# test mode: 1 if test was successful (patch could be applied/reverted)
+# copy: directory with the shadow tree, if any
+# verbose mode: output verbose messages
+#
+sub apply_patchlet {
+ my $self = shift;
+ my($patchlet, $revert, $test, $copy, $verbose) = @_;
+ my(@projects);
+
+ # print Dumper($patchlet);
+ my %projects = ( );
+ foreach my $p ( keys %{$patchlet->{files}},
+ keys %{$patchlet->{patch}},
+ keys %{$patchlet->{ladds}} ) {
+ $projects{$p} = 1;
+ }
+ @projects = keys %projects;
+
+ foreach my $proj (@projects) {
+ for my $file (@{$patchlet->{patch}->{$proj}->{best}}) {
+ # Copy source files, if required
+ if ($copy && !$self->copy_patchfiles("$patchlet->{basedir}/$file", $copy, $proj)) {
+ File::Path::rmtree($copy);
+ return 0;
+ }
+ }
+
+ if (!(($self->apply_newfiles($patchlet, $proj, $revert, $test, $copy)
+ && $self->apply_lineadds($patchlet, $proj, $revert, $test, $copy)
+ && $self->apply_patches($patchlet, $proj, $revert, $test, $copy, $verbose))
+ || ($test
+ && defined $patchlet->{info}->{successor}
+ && defined $self->{patchlets}->{$patchlet->{info}->{successor}}
+ && $self->apply_patchlet($self->{patchlets}->{$patchlet->{info}->{successor}},
+ $revert, $test, $copy)))) {
+ $copy && File::Path::rmtree($copy);
+ return 0;
+ }
+ }
+ map { $self->{last_words}->{$_}++ } @{$patchlet->{info}->{recompile}}
+ unless $test || $copy;
+ $copy && File::Path::rmtree($copy);
+ return 1;
+}
+
+# apply a given patchlet to a given kernel tree
+#
+# return value:
+# normal (non-test) mode: 1 on success, 0 on failure
+# test mode: 1 if test was successful (patch could be applied/reverted)
+#
+sub apply {
+ my $self = shift;
+ my($patchlet, $revert, $test) = @_;
+ my($copy) = '';
+ my(@projects);
+
+ if ($test) {
+ # Check wether patchlet has got unapplied dependencies
+ foreach my $dep (@{$patchlet->{info}->{depends}}) {
+ next if $dep =~ /^!/;
+ next if grep($_ eq $dep, @{$self->{applied}});
+ $copy = "/tmp/pom-$$";
+ last;
+ }
+ # Check broken-out patches
+ foreach my $proj (keys %{$patchlet->{patch}}) {
+ last if $copy;
+ next unless safe_exists($patchlet, ('patch', $proj, 'best'));
+ $copy = "/tmp/pom-$$"
+ if $#{$patchlet->{patch}->{$proj}->{best}};
+ }
+ }
+ if ($copy) {
+ $test = 0; # otherwise we could not check broken-out patches
+ mkdir($copy) or carp "Can't create directory $copy: $!";
+ $self->{saved}->{applied} = [ @{$self->{applied}} ] ;
+ if (!$self->apply_dependencies($patchlet, 0, 0, $copy)) {
+ File::Path::rmtree($copy);
+ $self->{applied} = [ @{$self->{saved}->{applied}} ];
+ return 0;
+ }
+ } elsif (!$self->apply_dependencies($patchlet, 0, $test)) {
+ return 0;
+ }
+
+ my $ret = $self->apply_patchlet($patchlet, $revert, $test, $copy);
+
+ if ($copy) {
+ File::Path::rmtree($copy);
+ $self->{applied} = [ @{$self->{saved}->{applied}} ];
+ }
+
+ return $ret;
+}
+
+# iterate over all patchlet directories below the given base directory
+# and parse all patchlet definitions
+#
+sub parse_patchlets {
+ my $self = shift;
+
+ my $pomdir = $self->{POM}->{PATH};
+ my($patchdir, $patch, @patchlets);
+
+ $patchdir = $pomdir;
+ opendir(INDIR, $patchdir)
+ or croak "Unable to open $patchdir: $!";
+ my @alldirs = grep {!/^\./ && -d "$patchdir/$_" } readdir(INDIR);
+ closedir(INDIR);
+
+ foreach my $patch (@alldirs) {
+ next unless -f "$patchdir/$patch/info";
+ $self->{patchlets}->{$patch} =
+ $self->parse_patchlet("$patchdir/$patch");
+ }
+}
+
+sub check_versions {
+ my @versions = @_;
+ my @v;
+
+ foreach my $v (@versions) {
+ @v = split(/\./, $v);
+ die "Cannot handle update version $v\n"
+ if $#v != 2 || $v[2] == 0;
+ }
+}
+
+sub oldest_version {
+ my(@a) = split(/\./, $a);
+ my(@b) = split(/\./, $b);
+
+ $a[0] <=> $b[0] && $a[1] <=> $b[1] && $a[2] <=> $b[2];
+}
+
+#
+# Hash reference behind $self built during a POM session:
+#
+# session = {
+# POM => directory,
+# projects => {
+# project => {
+# PATH => directory,
+# VERSION => version,
+# branches => { id => regexp },
+# },
+# },
+# flags => { a_flag => 1, },
+# patchlets => { patchlets },
+# applied => { applied_patchlets },
+# }
+sub init {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $paths = shift;
+ my($proj, $fn);
+ my $self = {};
+
+ bless($self, $class);
+
+ # Paths to POM itself and projects
+ foreach $proj (keys %$paths) {
+ if ($proj eq 'POM') {
+ $self->{$proj}->{PATH} = $paths->{$proj};
+ next;
+ }
+ $self->{projects}->{$proj}->{PATH} = $paths->{$proj};
+ # get version information of all projects we know of
+ $fn = $proj . 'version';
+ eval "$fn(\$self)";
+ }
+ # Flags
+ foreach (@_) {
+ $self->{flags}->{$_}++;
+ }
+
+
+ # Load config file
+ open(CONF, "$paths->{POM}/config")
+ or croak "Unable to open $paths->{POM}/config: $!";
+ while (<CONF>) {
+ chomp;
+ my @line = split(/\s+/);
+ next unless $line[0] eq 'Branch:';
+ # Branch: project id regexp
+ croak "Unknown project '$line[1]' in $paths->{POM}/config"
+ unless $self->{projects}->{$line[1]};
+ croak "Missing id or regexp in $paths->{POM}/config"
+ unless $line[3];
+ $self->{config}->{$line[1]}->{branches}->{$line[2]} = eval $line[3];
+ }
+ close(CONF);
+
+ my($branch, $oldest);
+ foreach $proj (keys %{$self->{projects}}) {
+ foreach $branch (keys %{$self->{config}->{$proj}->{branches}}) {
+ $self->{projects}->{$proj}->{branch} =
+ $self->{config}->{$proj}->{branches}->{$branch}
+ if $self->{projects}->{$proj}->{VERSION} =~
+ /$self->{config}->{$proj}->{branches}->{$branch}/;
+ }
+ croak "Your $proj version $self->{projects}->{$proj}->{VERSION} is unknown for patch-o-matic"
+ unless $self->{projects}->{$proj}->{branch};
+ }
+ $self->{applied} = [];
+ return $self;
+}
+
+sub last_words {
+ my $self = shift;
+
+ # print anything useful
+ print <<TXT if $self->{last_words}->{kernel};
+Recompile the kernel image.
+TXT
+ if ($self->{last_words}->{netfilter}) {
+ if ($self->{last_words}->{kernel}) {
+ print <<TXT;
+Recompile the netfilter kernel modules.
+TXT
+ } else {
+ print <<TXT;
+Recompile the kernel image (if there are non-modular netfilter modules).
+Recompile the netfilter kernel modules.
+TXT
+ }
+ }
+ print <<TXT if $self->{last_words}->{iptables};
+Recompile the iptables binaries.
+TXT
+}
+
+return 1;
+
+__END__
+
+there are several diffent modes of operation:
+
+=item1 isapplied
+
+tests whether a given kernel tree does already contain a given patch. The only
+case where this is true:
+ 1) all the newfiles do exist
+ 2) all the lineadds match and their newlines can be found
+ 3) 'patch -R --dry-run' runs cleanly with no rejected hunks
+this is actually the same as 'revert+test' below.
+
+=item1 apply + test
+
+tests whether the given patchlet would apply cleanly to the given tree. The
+only case where this is true:
+ 1) all the newfiles don't exist
+ 2) all the lineadd searchlines can be found
+ 3) 'patch --dry-run' runs cleanly with no rejected hunks
+
+=item1 apply
+
+apply the given patch to the given kernel tree
+
+=item1 revert + test
+
+tests whether the given patchlet would revert cleanly in the given tree. The
+only case where this is true:
+ 1) all the newfiles exist
+ 2) all the lineadds match and their newlines can be found
+ 3) 'patch -R --dry-run' runs cleanly with no rejected hunks
+
+=item1 revert
+
+reverts the given patch from the given kernel tree
diff --git a/README.newpatches b/README.newpatches
new file mode 100644
index 0000000..fe33eb5
--- /dev/null
+++ b/README.newpatches
@@ -0,0 +1,177 @@
+Here is how to add your new `foo' patch to patch-o-matic-ng:
+
+1) Create the directory `foo' to hold the files of your patch.
+
+2) Create a kernel patch by 'diff', which can then be applied
+ inside the kernel tree by `patch -p1' and call it
+ `foo/linux.patch'. If your patch works with 2.4 or 2.6 kernel
+ tree only, then encode the version dependency in the patch name
+ as `foo/linux-2.4.patch' or `foo/linux-2.6.patch` respectively.
+
+3) Create an info file called `foo/info' with the content:
+
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+Title: terse description of the patch
+Author: author (name, E-mail address)
+Status: Testing|Experimental|Alpha|Beta|Stable
+Repository: submitted|pending|base|extra
+Requires: repository-entry ==|>|<|>=|<= kernel-version|iptables-version
+Depends: [!]patch-name
+Recompile: kernel|netfilter|iptables
+Successor: patch-name
+
+After an empty line, the description of your patch for
+patch-o-matic-ng.
+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+ The `Repository' entry is mandantory, the other ones are optional.
+ `Requires', `Depends' and `Recompile' entries may occur multiple times.
+
+ As we already mentioned, version dependency can be encoded in the
+ repository entry name. But version dependency can be specified by
+ the `Requires' entries too, where the repository entry is the name
+ of the patch file or patch directory tree under the patch directory
+ 'foo', for which the requirement must be fulfilled. For example
+ the following conditions:
+
+Requires: linux-2.4 >= 2.4.25
+Requires: linux-2.4.patch >= 2.4.25
+
+ means
+
+ - first, the files under the directory linux-2.4 and the patch file
+ linux-2.4.patch can be applied for kernels from the 2.4 series,
+ according to the name encoding
+ - and second, according to the requirement, these patches are specifically
+ valid for kernels equal or above 2.4.25 from the 2.4 series.
+
+ Please note, the same version dependency can be achieved by name encoding
+ as well: linux-2.4.25.patch can be applied for kernel versions equal above
+ 2.4.25 in the 2.4 kernel tree. However, if linux-2.4.25.patch is valid
+ for 2.4.25 only, you *must* use the additional requirement line
+
+Requires: linux-2.4.25.patch == 2.4.25
+
+ in order to fully specify the version dependency.
+
+ When checking the version requirements first name encoding is checked
+ then the requirements specified in the info file.
+
+ Dependency or clash with other patches can be specified by the `Depends'
+ entries. You specify the name of the patch your patch depends on or clashes
+ with, at the latter case the patch name preceded by '!'.
+
+ With the `Recompile' entries you can (and please do) give hints to the users
+ what to recompile after applying your patch: the kernel outside the netfilter
+ part; the netfilter part of the kernel; or the iptables binary. When adding
+ a new match/target feature patch, you usually have to add
+
+Recompile: netfilter
+Recompile: iptables
+
+ Dependencies and recompile hints can be listed separated by comma and/or space:
+
+Depends: foo, !bar
+Recompile: netfilter, iptables
+
+ There is no such possibility for requirements.
+
+ When 'bar' patch depends on 'foo' patch and both patches are already applied,
+ it can occur that patch-o-matic cannot detect that 'foo' is already applied
+ due to the "clashing" modifications is 'bar'. You can give a hint to pom then
+ by specifying
+
+Successor: bar
+
+ in the info file of 'foo' to resolve the issue, by checking wether 'bar' patch
+ is applied if 'foo' seems to be not applied. If pom finds that 'bar' applied,
+ it will assume that 'foo' applied too.
+
+4) If your patch creates a new CONFIG option, modifies Makefile, adds new
+ entry to specific files (net/ipv4/netfilter/ip_conntrack.h) or adds whole
+ files to the kernel source tree, then create a patch kernel directory tree
+ structure to hold these files, say
+
+ foo/linux/include/linux/netfilter_ipv4/
+ foo/linux/include/linux/netfilter_ipv6/
+ foo/linux/net/ipv4/netfilter
+ foo/linux/net/ipv6/netfilter
+
+ You can use version encoding in the name of the 'linux' directory too, as
+ described above.
+
+5) If your patch adds whole files to the kernel source, eliminate those
+ from the patch above and add the whole files (not as patch file!) to
+ the patch kernel directory tree.
+
+6) If your patch creates a new CONFIG option, eliminate that from the
+ patch above. Depending on the kernel version:
+
+ For a 2.4 kernel create a file called
+ `foo/linux/net/ipv{4|6}/netfilter/Config.in.ladd'. The format of
+ this file is as follows:
+
+EXACT LINE TO FOLLOW
+<text to paste in>
+
+ This allows you to specify the entry in net/ipv4/netfilter/Config.in
+ that you wish your text to follow. Note that it must be an exact match.
+ You can have more than one of these files, to make multiple entries
+ in different places as Config.in.ladd, Config.in.ladd_2, etc.
+
+ You also need to make an entry in Documentation/Configure.help;
+ once again, eliminate this from your patch file and create a file
+ called `foo/linux/Documentation/Configure.help.ladd' like so:
+
+EXACT CONFIG OPTION TO FOLLOW
+<text to paste in>
+
+ Your text will be placed after the config option you indicated
+ (with a blank line before and after). You can have more than one
+ of these files, to make multiple entries in different places, by
+ calling successive Configure.help.ladd(_n) files.
+
+ For a 2.6 kernel create a file called
+ `foo/linux/net/ipv{4|6}/netfilter/Kconfig.ladd' with your new
+ configuration options with the help text included.
+
+7) If you want to add new parts to a Makefile, ip_conntrack.h or other
+ files with already existing well defined "entry points", eliminate
+ that from the patch above and create a file `file-to-be-modified.ladd'
+ in the patch directory tree. The format of the file is as follows:
+
+EXACT LINE TO FOLLOW
+<text to paste in>
+
+ You can have more than one of these files to make multiple entries
+ in different places, by calling successive file-to-be-modified.ladd(_n)
+ files.
+
+8) If your original patch file has been completely emptied by removing
+ the parts above then just remove the empty patch file from the patch
+ directory.
+
+9) For the usespace part, create the directory tree
+
+ foo/iptables/extensions
+
+ for the libipt_foo.c whole file. Add a test for your extension called
+ `foo/iptables/extensions/.foo-test'. This should be a small shell
+ script which prints the names of the libraries to be built if the
+ corresponding include file exists in the kernel tree (this test may
+ be more complex: figure out some way of reliably detecting that the
+ kernel patch has been applied to $KERNEL_DIR). Typically your test
+ script could look like this
+
+#!/bin/sh
+# True if foo is applied
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_foo.h ] && echo foo
+
+10) Add a man page entry, describing the functionality of your extension
+ as foo/iptables/extensions/libipt_foo.man.
+
+11) If you patch the iptables source besides adding whole files, you
+ can add that part as `foo/iptables.patch'.
+
+Enjoy!
+Netfilter Core Team
diff --git a/patch2pom/PatchChunk.pm b/patch2pom/PatchChunk.pm
new file mode 100644
index 0000000..32f2711
--- /dev/null
+++ b/patch2pom/PatchChunk.pm
@@ -0,0 +1,192 @@
+#!/usr/bin/perl -w
+#
+# PatchChunk.pm helper package for the patch2pom script
+# (C) 2005 by Jonas Berlin <xkr47 at outerspace.dyndns.org>
+#
+# This code is subject to the GNU GPLv2
+#
+# This package represents a chunk of a diff -Nru style file, where a
+# chunk means a block starting with "@@ -1,2 +3,4 @@" and the
+# lines following that belong to that block.
+
+package PatchChunk;
+
+use strict;
+
+our $TYPE_NONE = 0;
+our $TYPE_BOTH = 1;
+our $TYPE_NEW = 2;
+our $TYPE_OLD = 3;
+
+my $NONEWLINE_STR = "\\ No newline at end of file\n";
+
+sub typename {
+ my $this = shift;
+ my $type = shift;
+ if($type == $TYPE_NONE) {
+ return "NONE";
+ } elsif($type == $TYPE_BOTH) {
+ return "BOTH";
+ } elsif($type == $TYPE_NEW) {
+ return "NEW";
+ } elsif($type == $TYPE_OLD) {
+ return "OLD";
+ } else {
+ return "<INVALID $type>";
+ }
+}
+
+
+sub _typechar {
+ my $type = shift;
+ if($type == $TYPE_NONE) {
+ return "";
+ } elsif($type == $TYPE_BOTH) {
+ return " ";
+ } elsif($type == $TYPE_NEW) {
+ return "+";
+ } elsif($type == $TYPE_OLD) {
+ return "-";
+ } else {
+ return "INVALIDTYPE $type ";
+ }
+}
+
+sub _warnonce {
+ my $this = shift;
+ my $warning = shift;
+
+ unless($this->{_warned}->{$warning}) {
+ $this->{_warned}->{$warning} = 1;
+ print STDERR "\033[36;1;40mWarning: $warning\033[m\n";
+ }
+}
+
+sub new {
+ my $class = shift;
+ $class = ref($class) || $class;
+ my $this = {}; # $class->SUPER::new();
+ bless $this, $class;
+
+ my $line = shift;
+ return undef unless($line =~ /^\@\@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? \@\@.*$/);
+
+ $this->{oldstart} = $1;
+ $this->{oldlen} = defined($2) ? $2 : 1;
+ $this->{newstart} = $3;
+ $this->{newlen} = defined($4) ? $4 : 1;
+
+ $this->{_oldleft} = $this->{oldlen};
+ $this->{_newleft} = $this->{newlen};
+
+ $this->{_state} = $TYPE_NONE;
+ $this->{nonewline} = 0;
+ $this->{finished} = 0;
+
+ $this->{_warned} = {};
+
+ return $this;
+}
+
+sub _finish_minichunk {
+ my $this = shift;
+
+ if($this->{_state} != $TYPE_NONE) {
+ push @{$this->{minichunks}}, $this->{lines};
+ push @{$this->{minichunktypes}}, $this->{_state};
+ }
+}
+
+# to be used at EOF or unidentified data
+sub finish {
+ my $this = shift;
+ return if($this->{finished});
+ $this->{finished} = 1;
+ $this->_finish_minichunk();
+ $this->_warnonce("diff counters broken\n") if($this->{_oldleft} != 0 || $this->{_newleft} != 0);
+}
+
+sub add_line {
+ my $this = shift;
+ my $line = shift;
+
+ if($this->{finished}) {
+ return 0;
+ }
+
+ my $newstate;
+ if($line =~ /^ /) {
+ $newstate = $TYPE_BOTH;
+ } elsif($line =~ /^\+/) {
+ $newstate = $TYPE_NEW;
+ } elsif($line =~ /^-/) {
+ $newstate = $TYPE_OLD;
+ } elsif($line eq $NONEWLINE_STR) {
+ $this->{nonewline} = 1;
+ $this->finish();
+ return 1;
+ } else {
+ $this->finish();
+ return 0;
+ }
+
+ if($newstate != $TYPE_NEW) {
+ if($this->{_oldleft} <= 0) {
+ $this->{oldlen}++;
+ $this->_warnonce("diff counters broken\n");
+ } else {
+ $this->{_oldleft}--;
+ }
+ }
+
+ if($newstate != $TYPE_OLD) {
+ if($this->{_newleft} <= 0) {
+ $this->{newlen}++;
+ $this->_warnonce("diff counters broken\n");
+ } else {
+ $this->{_newleft}--;
+ }
+ }
+
+ if($newstate != $this->{_state}) {
+ $this->_finish_minichunk();
+ $this->{lines} = [];
+ $this->{_state} = $newstate;
+ }
+
+ push @{$this->{lines}}, substr($line, 1);
+
+ return 1;
+}
+
+sub adjust_position {
+ my $this = shift;
+ my ($oldoff, $newoff) = @_;
+
+ $this->{oldstart} += $oldoff;
+ $this->{newstart} += $newoff;
+}
+
+sub format_minichunk {
+ my $this = shift;
+ my $idx = shift;
+
+ return "<INVALID IDX $idx>" if($idx < 0 || $idx >= scalar(@{$this->{minichunks}}));
+ my $char = _typechar($this->{minichunktypes}->[$idx]);
+ return $char.join($char,@{$this->{minichunks}->[$idx]});
+}
+
+sub format {
+ my $this = shift;
+ my $str;
+ $str = "@@ -".$this->{oldstart}.($this->{oldlen} != 1 ? ",".$this->{oldlen} : "");
+ $str .= " +".$this->{newstart}.($this->{newlen} != 1 ? ",".$this->{newlen} : "");
+ $str .= " @@\n";
+ for(my $i=0; $i<scalar(@{$this->{minichunks}}); ++$i) {
+ $str .= $this->format_minichunk($i);
+ }
+ $str .= $NONEWLINE_STR if($this->{nonewline});
+ return $str;
+}
+
+1
diff --git a/patch2pom/PatchFile.pm b/patch2pom/PatchFile.pm
new file mode 100644
index 0000000..3c8bb89
--- /dev/null
+++ b/patch2pom/PatchFile.pm
@@ -0,0 +1,133 @@
+#!/usr/bin/perl -w
+#
+# PatchFile.pm helper package for the patch2pom script
+# (C) 2005 by Jonas Berlin <xkr47 at outerspace.dyndns.org>
+#
+# This code is subject to the GNU GPLv2
+#
+# This package represents a single file from a diff -Nru style patch.
+
+package PatchFile;
+
+use strict;
+
+my $STATE_DIFF_OR_MINUS = 0;
+my $STATE_MINUS = 1;
+my $STATE_PLUS = 2;
+my $STATE_CHUNKS = 10;
+
+sub new {
+ my $class = shift;
+ $class = ref($class) || $class;
+ my $this = {}; # $class->SUPER::new();
+ bless $this, $class;
+
+ $this->{_state} = $STATE_DIFF_OR_MINUS;
+ $this->{finished} = 0;
+ $this->{success} = 0;
+ $this->{started} = 0;
+ $this->{chunks} = [];
+
+ return $this;
+}
+
+sub _parse_fileline {
+ my $line = $_[0];
+
+ unless($line =~ /^...\s([^\t\s]+)[\t\s]*([^\t\s]*)/) {
+ print STDERR "\033[36;1;40mInvalid file line:\n$line\033[m\n";
+ exit(10);
+ }
+
+ return { file => $1, comments => $2 };
+}
+
+sub _finish_chunk {
+ my $this = shift;
+
+ return unless(defined($this->{_chunk}));
+
+ $this->{_chunk}->finish();
+ push @{$this->{chunks}}, $this->{_chunk};
+ if($this->{_chunk}->{nonewline}) {
+ $this->{finished} = 1;
+ $this->{success} = 1;
+ }
+ $this->{_chunk} = undef;
+}
+
+# call at eof or next /^diff /
+sub finish {
+ my $this = shift;
+
+ $this->_finish_chunk();
+ $this->{finished} = 1;
+ $this->{success} = scalar(@{$this->{chunks}}) > 0;
+}
+
+sub format {
+ my $this = shift;
+ my $str = $this->{diffline};
+ $str .= "--- ".$this->{old}->{file}."\t".$this->{old}->{comments}."\n";
+ $str .= "+++ ".$this->{new}->{file}."\t".$this->{new}->{comments}."\n";
+ foreach my $chunk (@{$this->{chunks}}) {
+ $str .= $chunk->format();
+ }
+ return $str;
+}
+
+sub add_line {
+ my $this = shift;
+ my $line = shift;
+
+ return 0 if($this->{finished});
+
+ if(/^diff /) {
+ unless($this->{_state} == $STATE_DIFF_OR_MINUS) {
+ $this->finish();
+ return 0;
+ }
+ $this->{diffline} = $line;
+ $this->{_state} = $STATE_MINUS;
+ $this->{started} = 1;
+
+ } elsif(($this->{_state} == $STATE_DIFF_OR_MINUS || $this->{_state} == $STATE_MINUS) && /^--- /) {
+ $this->{old} = _parse_fileline($line);
+ $this->{_state} = $STATE_PLUS;
+ $this->{started} = 1;
+
+ } elsif($this->{_state} == $STATE_PLUS && /^\+\+\+ /) {
+ $this->{new} = _parse_fileline($line);
+ $this->{_state} = $STATE_CHUNKS;
+
+ } elsif($this->{_state} == $STATE_CHUNKS) {
+ my $isnewchunk = $line =~ /^@@/;
+
+ my $ret = 1;
+
+ if(defined($this->{_chunk})) {
+ if($isnewchunk) {
+ $this->{_chunk}->finish();
+ } else {
+ $ret = $this->{_chunk}->add_line($line);
+ }
+
+ if($this->{_chunk}->{finished}) {
+ $this->_finish_chunk();
+ }
+ }
+
+ if($isnewchunk && !$this->{finished}) {
+ $this->{_chunk} = new PatchChunk($_);
+ $ret = 0 unless(defined($this->{_chunk}));
+ }
+
+ if(!$ret) {
+ $this->finish();
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
diff --git a/patch2pom/README b/patch2pom/README
new file mode 100644
index 0000000..8d1bb95
--- /dev/null
+++ b/patch2pom/README
@@ -0,0 +1,3 @@
+
+Please see top of patch2pom script for instructions.
+
diff --git a/patch2pom/patch2pom b/patch2pom/patch2pom
new file mode 100644
index 0000000..dd04ce9
--- /dev/null
+++ b/patch2pom/patch2pom
@@ -0,0 +1,346 @@
+#!/usr/bin/perl -w
+#
+# patch2pom, helper script for the patch-o-matic 'next generation' package
+# (C) 2005 by Jonas Berlin <xkr47 at outerspace.dyndns.org>
+#
+# This code is subject to the GNU GPLv2
+#
+# The separate PatchChunk.pm and PatchFile.pm files are required for
+# proper operation.
+#
+# This script takes a diff -Nru on input and converts it to
+# patch-o-matic-ng format by extracting new files from the diff as
+# separate files and making trivial updates to known file types using
+# "ladd" files. Any changes not matching the above criteria are passed
+# forward as a normal diff.
+#
+# If run as
+#
+# ./patch2pom patch-o-matic/foobar/linux-2.6 < mypatch.patch
+#
+# .. then the command will create a directory structure below
+# patch-o-matic/foobar/linux-2.6/ with new files and/or .ladd files
+# when possible. Any parts of the diff that cannot be applied as new
+# files or .ladd files are passed forward to a patch file called
+# patch-o-matic/foobar/linux-2.6.patch. Example output directory
+# structure:
+#
+# pom-ng/foobar/linux-2.6/net/ipv4/netfilter/ipt_foobar.c
+# pom-ng/foobar/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
+# pom-ng/foobar/linux-2.6/net/ipv4/netfilter/Makefile.ladd
+# pom-ng/foobar/linux-2.6/include/linux/netfilter_ipv4/ip_foo.h.ladd
+# pom-ng/foobar/linux-2.6/include/linux/netfilter_ipv4/ip_foo.h.ladd_2
+# ...
+# pom-ng/foobar/linux-2.6.patch
+#
+# Tip: when creating new versions of already existing modules in
+# pom-ng, it might be convenient to give a different destination
+# and then compare the results.
+#
+# Note: If files already exist in the given destination directory,
+# they will not be removed, so before re-running the same
+# command, you probably want to erase the old one first. The
+# same goes for the patch optionally created by this script.
+
+BEGIN {
+ my $scripthome = $0;
+ $scripthome =~ s![^/]+$!!;
+ push @INC, $scripthome;
+}
+
+use strict;
+use File::Path;
+use Date::Parse;
+
+use PatchChunk;
+use PatchFile;
+
+my $pombase = shift @ARGV;
+
+unless(defined($pombase)) {
+
+ print STDERR 'usage: '.$0.' pomdir-base
+The command expects a diff -Nru style diff on standard input.
+
+Examples:
+
+diff -Nru orig-linux linux | '.$0.' patch-o-matic-ng/foobar/linux-2.6
+
+ This will create files in patch-o-matic-ng/foobar/linux-2.6/ and/or
+ a patch file patch-o-matic-ng/foobar/linux-2.6.patch
+
+diff -Nru orig-iptables iptables | '.$0.' patch-o-matic-ng/foobar/iptables
+
+ Creates files in .../iptables and/or patch file .../iptables.patch
+';
+ exit(1);
+}
+
+$pombase =~ s!/+$!!;
+
+sub create_file {
+ my ($file, @content) = @_;
+
+ $file = $pombase.'/'.$file;
+
+ my $dir = $file;
+ $dir =~ s![^/]+$!!;
+
+ mkpath($dir);
+
+ open NEWFILE, '>', $file;
+ print NEWFILE @content;
+ close NEWFILE;
+}
+
+my $patch_file_opened = 0;
+
+sub pprint {
+ unless($patch_file_opened) {
+ my $pomparent = $pombase;
+ $pomparent =~ s![^/]+$!!;
+ mkpath($pomparent) if(length($pomparent));
+
+ $patch_file_opened = 1 if(open PATCHFILE, '>', $pombase.'.patch');
+ }
+ print PATCHFILE @_;
+}
+
+# ensure that a FilePatch object only contains alternating "common" and "new" lines
+sub check_adds_only {
+ my $file = shift;
+
+ foreach my $chunk (@{$file->{chunks}}) {
+ my $state = 0;
+ foreach my $chunktype (@{$chunk->{minichunktypes}}) {
+ if($chunktype != ($state ? $PatchChunk::TYPE_NEW : $PatchChunk::TYPE_BOTH)) {
+ return 0;
+ }
+ $state = !$state;
+ }
+ }
+
+ return 1;
+}
+
+my %ladd_idx;
+
+
+sub ladd_filename {
+ my $filename = shift;
+
+ $ladd_idx{$filename} = 1 unless(defined($ladd_idx{$filename}));
+ my $ladd_filename = $filename.".ladd";
+ $ladd_filename .= "_".$ladd_idx{$filename} if($ladd_idx{$filename} > 1);
+ $ladd_idx{$filename}++;
+
+ return $ladd_filename;
+}
+
+###### the core logic for creating new and .ladd files and .patch file
+sub handle_file {
+ my $file = shift;
+
+ my $filename = $file->{new}->{file};
+ $filename =~ s!^[^/]+/!!;
+
+ if(scalar(@{$file->{chunks}}) == 1 &&
+ $file->{chunks}->[0]->{oldstart} == 0 &&
+ $file->{chunks}->[0]->{oldlen} == 0 &&
+ scalar(@{$file->{chunks}->[0]->{minichunks}}) == 1 &&
+ $file->{chunks}->[0]->{minichunktypes}->[0] == $PatchChunk::TYPE_NEW) {
+
+ ### It's a totally new file
+
+ my @content = @{$file->{chunks}->[0]->{minichunks}->[0]};
+ chomp $content[$#content] if($file->{chunks}->[0]->{nonewline});
+
+ create_file($filename, @content);
+
+ } elsif($filename =~ m!/Kconfig$!) {
+
+ ### 2.6 Kconfig
+
+ my $success = 0;
+
+ if(check_adds_only($file)) {
+
+ $success = 1;
+ kconfig_bail:
+ foreach my $chunk (@{$file->{chunks}}) {
+ for(my $i=0; $i<scalar(@{$chunk->{minichunks}})-1; $i+=2) {
+
+ my @lines = @{$chunk->{minichunks}->[$i+1]};
+ my $state = 0;
+ for($i=0; $i<=$#lines; ++$i) {
+ if($state == 0 && $lines[$i] =~ /^$/) {
+ # do nothing
+ } elsif(($state == 0 || $state == 3) && $lines[$i] =~ /^config \S+$/) {
+ $state = 1;
+ } elsif($state == 0) {
+ $success = 0; last kconfig_bail;
+
+ } elsif($state >= 1 && $state <= 2) {
+ if($lines[$i] =~ /^\t\S/) {
+ $state++;
+ } else {
+ $success = 0; last kconfig_bail;
+ }
+ } elsif($state == 3) {
+ unless($lines[$i] =~ /^\t|^$/) {
+ $success = 0; last kconfig_bail;
+ }
+ } else {
+ die "Internal bug, please report\n";
+ }
+ }
+
+ ##TODO## maybe check that post-context matches /^\S/
+
+ unless($state == 3) {
+ $success = 0; last kconfig_bail;
+ }
+ }
+ }
+
+ if($success) {
+ foreach my $chunk (@{$file->{chunks}}) {
+ for(my $i=0; $i<scalar(@{$chunk->{minichunks}})-1; $i+=2) {
+
+ my $ladd_filename = ladd_filename($filename);
+
+ my @lines = @{$chunk->{minichunks}->[$i+1]};
+
+ while($lines[$#lines] =~ /^$/) {
+ delete $lines[$#lines];
+ }
+
+ create_file($ladd_filename, @lines);
+ }
+ }
+ }
+ }
+
+ pprint $file->format() unless($success);
+
+ } elsif($filename =~ m!/Documentation/Configure\.help$!) {
+
+ ### 2.4 Configure.help
+
+ ##TODO## implement algorithm if somebody still wants this
+
+ pprint $file->format();
+
+ } elsif($filename =~ m!/(?:Makefile|Config\.in)$!) {
+
+ ### Makefile
+ ### 2.4 Config.in
+
+ if(check_adds_only($file)) {
+ foreach my $chunk (@{$file->{chunks}}) {
+ for(my $i=0; $i<scalar(@{$chunk->{minichunks}})-1; $i+=2) {
+
+ # pick last line of preceding context lines
+ my $context_minichunk = $chunk->{minichunks}->[$i];
+ my $context = @$context_minichunk[scalar(@{$context_minichunk})-1];
+
+ my $ladd_filename = ladd_filename($filename);
+
+ create_file($ladd_filename, $context, @{$chunk->{minichunks}->[$i+1]});
+ }
+ }
+
+ } else {
+ pprint $file->format();
+ }
+
+ } elsif($filename =~ m![^/]\.[ch]$!) {
+
+ ### *.c
+ ### *.h
+
+ my $success = 0;
+
+ if(check_adds_only($file)) {
+ $success = 1;
+
+ source_bail:
+ foreach my $chunk (@{$file->{chunks}}) {
+ for(my $i=0; $i<scalar(@{$chunk->{minichunks}})-1; $i+=2) {
+
+ # pick last line of preceding context lines
+ my $context_minichunk = $chunk->{minichunks}->[$i];
+ my $context = @$context_minichunk[scalar(@{$context_minichunk})-1];
+
+ # if "here" mentioned in comment, we accept
+ unless($context =~ m!/\*(?:\*[^/]|[^*])+\shere(?:\*[^/]|[^*])*\*/!) {
+ $success = 0; last source_bail;
+ }
+ }
+ }
+
+ ##TODO## we could accept partial .ladd conversion and modify the patch
+
+ if($success) {
+ foreach my $chunk (@{$file->{chunks}}) {
+ for(my $i=0; $i<scalar(@{$chunk->{minichunks}})-1; $i+=2) {
+
+ # pick last line of preceding context lines
+ my $context_minichunk = $chunk->{minichunks}->[$i];
+ my $context = @$context_minichunk[scalar(@{$context_minichunk})-1];
+
+ my $ladd_filename = ladd_filename($filename);
+
+ create_file($ladd_filename, $context, @{$chunk->{minichunks}->[$i+1]});
+ }
+ }
+ }
+ }
+
+ pprint $file->format() unless($success);
+
+ } else {
+
+ ### default
+
+ pprint $file->format();
+ }
+}
+
+############# main parse loop
+
+my $file = new PatchFile();
+
+while(<>) {
+ while(!$file->add_line($_)) {
+ unless($file->{started}) {
+ if(/^Files .* differ$/) {
+ print STDERR "Ignoring: $_";
+ } elsif(/^Only in/) {
+ print STDERR "ABORTING: It seems you forgot the -N flag from your diff command.. I got: $_";
+ exit(1);
+ } elsif(/^Common /) {
+ print STDERR "ABORTING: It seems you forgot the -r flag from your diff command.. I got: $_";
+ exit(1);
+ } elsif(/^[\d><]/) {
+ print STDERR "ABORTING: It seems you forgot the -u flag from your diff command.. I got: $_";
+ exit(1);
+ } else {
+ print;
+ }
+ last;
+ }
+
+ if($file->{success}) {
+ handle_file($file);
+ }
+
+ $file = new PatchFile();
+ }
+}
+
+$file->finish();
+
+handle_file($file) if($file->{success});
+
+close PATCHFILE if($patch_file_opened);
diff --git a/patchlets/config b/patchlets/config
new file mode 100644
index 0000000..b49b9b0
--- /dev/null
+++ b/patchlets/config
@@ -0,0 +1,4 @@
+# Branch: project id regexp
+Branch: linux 2.4 qr/^2\.4/
+Branch: linux 2.6 qr/^2\.6/
+Branch: iptables iptables qr/./
diff --git a/pom2patch/pom2patch b/pom2patch/pom2patch
new file mode 100644
index 0000000..3444871
--- /dev/null
+++ b/pom2patch/pom2patch
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# pom2patch - create a normal unified diff (patch) from patch-o-matic
+#
+# (C) 2002 by Harald Welte <laforge at gnumonks.org>
+#
+# Released under the terms of GNU GPLv2
+
+POMDIR=`pwd`
+
+PLAINKERNEL=$1
+PATCH=$2
+
+if [ "$1" = "" -o "$2" = "" ]; then
+ echo Please specify kernel and patch as parameters
+ exit 1
+fi
+
+
+WORKDIR=`dirname $PLAINKERNEL`
+KERNEL=`basename $PLAINKERNEL`
+KERNEL_OUT=$KERNEL-pom2patch
+
+
+if [ ! -d $WORKDIR ]; then
+ echo Workdir $WORKDIR doesnt exist
+ exit 1
+fi
+
+cd $WORKDIR
+
+if [ ! -d $KERNEL ]; then
+ echo Input directory $KERNEL doesnt exist
+ exit 1
+fi
+
+if [ -d $KERNEL_OUT ]; then
+ echo Output directory $KERNEL_OUT already exists
+ exit 1
+fi
+
+# need this to work around symlink
+cp -al $KERNEL $KERNEL_OUT
+
+OLDPWD=`pwd`
+cd $POMDIR
+export KERNEL_DIR=$WORKDIR/$KERNEL_OUT
+export IPTABLES_DIR=/usr/src/iptables
+echo "y" | ./runme $PATCH > /dev/null #2>&1
+cd $OLDPWD
+diff -Nru $KERNEL $KERNEL_OUT
+
+rm -rf $KERNEL_OUT
+
diff --git a/runme b/runme
new file mode 100755
index 0000000..03eb11b
--- /dev/null
+++ b/runme
@@ -0,0 +1,510 @@
+#!/usr/bin/perl
+#
+# runme.pl, part of the patch-o-matig 'next generation' package.
+# (C) 2003-2004 by Harald Welte <laforge at netfilter.org>
+# 2004 by Jozsef Kadlecsik <kadlec at blackhole.kfki.ho>
+# 2006 small fixes by Alan Ezust <alan.ezust at presinet.com>
+# This script is subject to the GNU GPLv2
+#
+# $Id$
+#
+
+use strict;
+use Getopt::Long;
+use Pod::Usage;
+
+require Term::Cap;
+
+my $POMNG_ROOT_DIR = './patchlets';
+my $VERSION = '$Revision$';
+
+push(@INC, $POMNG_ROOT_DIR);
+use Netfilter_POM;
+
+$|=1;
+
+sub print_header {
+ my $session = shift;
+
+ print("Welcome to Patch-o-matic ($VERSION)!\n\n");
+ printf("Kernel: %s, %s\n", $session->{projects}->{linux}->{VERSION},
+ $session->{projects}->{linux}->{PATH});
+ printf("Iptables: %s, %s\n", $session->{projects}->{iptables}->{VERSION},
+ $session->{projects}->{iptables}->{PATH});
+ print("Each patch is a new feature: many have minimal impact, some do not.\n");
+ print("Almost every one has bugs, so don't apply what you don't need!\n");
+ print("-------------------------------------------------------\n");
+ $session->perror;
+}
+
+sub last_words {
+ my $session = shift;
+
+ print("\nExcellent! Source trees are ready for compilation.\n\n");
+ $session->last_words;
+}
+
+sub print_prompt {
+ my ($reverse) = @_;
+ my $operation;
+
+ print("-----------------------------------------------------------------\n");
+ if ($reverse) {
+ $operation = 'REVERT';
+ } else {
+ $operation = 'apply';
+ }
+ printf("Do you want to %s this patch [N/y/t/f/a/r/b/w/q/?] ", $operation);
+}
+
+sub print_prompt_help {
+ print("Answer one of the following:\n");
+ print(" T to test that the patch will apply cleanly\n");
+ print(" Y to apply patch\n");
+ print(" N to skip this patch\n");
+ print(" F to apply patch even if test fails\n");
+ print(" A to restart patch-o-matic in apply mode\n");
+ print(" R to restart patch-o-matic in REVERSE mode\n");
+ print(" B to walk back one patch in the list\n");
+ print(" W to walk forward one patch in the list\n");
+ print(" Q to quit immediately\n");
+ print(" ? for help\n");
+}
+
+# main
+
+my @patchlets;
+my @repositories;
+my $clrscr = "\n\n\n\n\n\n\n\n\n\n";
+
+my $opt_batch;
+my $opt_verbose;
+my $opt_test;
+my $opt_check;
+my $opt_reverse;
+my $opt_repository;
+my $opt_help;
+my $opt_man;
+my @opt_exclude;
+my $opt_path;
+my $opt_iptpath;
+my $opt_download;
+
+my $result = GetOptions("batch" => \$opt_batch,
+ "verbose" => \$opt_verbose,
+ "test" => \$opt_test,
+ "check" => \$opt_check,
+ "reverse" => \$opt_reverse,
+ "exclude=s" => \@opt_exclude,
+ "help" => \$opt_help,
+ "man" => \$opt_man,
+ "download" => \$opt_download,
+ "kernel-path=s" => \$opt_path,
+ "iptables-path=s" => \$opt_iptpath) or pod2usage(2);
+
+pod2usage(-verbose=>2, -exitval=>0) if $opt_man;
+pod2usage(-verbose=>1, -exitval=>0) if $opt_help;
+
+if ($opt_download) {
+ # a list of commands that can download content referred to by
+ # urls and print them and _only_ them on stdout and return
+ # error code on failure
+ my @download_commands_stdout = ("curl -s -L", "wget -q -O -");
+ my @download_commands_file = ("curl -s -L -o ", "wget -q -O ");
+ # this is updated once the command to use has been determined
+ my $download_command_idx = -1;
+
+ open(SOURCES, "<sources.list") || die "could not open sources.list";
+ while (my $source = <SOURCES>) {
+ chomp($source);
+ if ($source =~ m/^\s*\#/ || $source =~ m/^\s*$/) {
+ next;
+ }
+ print "Attempting to download $source/index\n" if ($opt_verbose);
+ my $success = 0; # true if program was launched
+ my $ret = 0; # return code
+
+ # if download command undecided, try one at a time until we successfully execute one
+ if ($download_command_idx == -1) {
+ for ($download_command_idx = 0; $download_command_idx <= $#download_commands_file; ++$download_command_idx) {
+ my $ret = system($download_commands_file[$download_command_idx]." /tmp/pom-runme-index $source/index");
+ if($ret != -1) {
+ # command worked
+ $success = 1;
+ last;
+ }
+ }
+ } else {
+ $ret = system($download_commands_file[$download_command_idx]." /tmp/pom-runme-index $source/index");
+ if($ret != -1) {
+ $success = 1;
+ }
+ }
+ # none of the commands worked or the command stopped working
+ if(!$success) {
+ print STDERR "Failed to execute ".join(" or ", map { s/ .*//; $_ } @download_commands_file)." to download files. Please install one of them.\n";
+ exit(1);
+ }
+ # if process returned nonzero value, skip
+ if($ret & 0xFF00) {
+ print STDERR "Failed to get $source/index, skipping..\n";
+ next;
+ }
+
+ # read the downloaded index file
+ if(!open(INDEX, '<', "/tmp/pom-runme-index")) {
+ print STDERR "Failed to read temporary file /tmp/pom-runme-index: $? - aborting!\n";
+ exit(1);
+ }
+ while (my $patch = <INDEX>) {
+ chomp($patch);
+ if ($patch =~ m/^\s*\#/ || $patch =~ m/^\s*$/) {
+ next;
+ }
+ if ($patch =~ m/[^a-zA-Z0-9\-]/) {
+ print STDERR "$source: bad patch name $patch, ignored\n";
+ next;
+ }
+ # only allow overwriting of external patches
+ if (-f "$POMNG_ROOT_DIR/$patch/info") {
+ if (!open(INFO, "<$POMNG_ROOT_DIR/$patch/info")) {
+ print STDERR "could not open $POMNG_ROOT_DIR/$patch/info\n";
+ next;
+ }
+ my $external = 0;
+ while (<INFO>) {
+ if( /^Repository:\s+external/ ) {
+ $external = 1;
+ }
+ }
+ close(INFO);
+ if (!$external) {
+ print STDERR "$POMNG_ROOT_DIR/$patch exists and is not external\n";
+ next;
+ }
+ }
+
+ print "Attempting to download $source/$patch.tar.gz\n" if ($opt_verbose);
+ if (system($download_commands_stdout[$download_command_idx]." $source/$patch.tar.gz | tar xz -C $POMNG_ROOT_DIR $patch 2>/dev/null")) {
+ # some packages are not gzipped even if extension suggests so.. so try without before complaining
+ if (system($download_commands_stdout[$download_command_idx]." $source/$patch.tar.gz | tar x -C $POMNG_ROOT_DIR $patch 2>/dev/null")) {
+ print STDERR "could not get $source/$patch.tar.gz\n";
+ system("rm -rf $POMNG_ROOT_DIR/$patch");
+ next;
+ }
+ }
+
+ # check for "Repository: external"
+ if (!open(INFO, "<$POMNG_ROOT_DIR/$patch/info")) {
+ print STDERR "could not open $POMNG_ROOT_DIR/$patch/info\n";
+ system("rm -rf $POMNG_ROOT_DIR/$patch");
+ next;
+ }
+ my $external = 0;
+ while (<INFO>) {
+ if( /^Repository:\s+external/ ) {
+ $external = 1;
+ }
+ }
+ close(INFO);
+ if (!$external) {
+ print STDERR "patch is not marked external\n";
+ system("rm -rf $POMNG_ROOT_DIR/$patch");
+ next;
+ }
+ print "Successfully downloaded external patch $patch\n";
+ }
+ close(INDEX);
+ }
+ close(SOURCES);
+}
+
+my %paths;
+my @paths = (
+ { 'project' => 'linux',
+ 'env' => 'KERNEL_DIR',
+ 'opt' => $opt_path,
+ 'txt' => 'kernel source',
+ 'default' => '/usr/src/linux',
+ 'check' => 'Makefile',
+ },
+ { 'project' => 'iptables',
+ 'env' => 'IPTABLES_DIR',
+ 'opt' => $opt_iptpath,
+ 'txt' => 'iptables source code',
+ 'default' => '/usr/src/iptables',
+ 'check' => 'Makefile',
+ }
+);
+
+foreach my $path (@paths) {
+ if (!defined($ENV{$path->{env}}) && !$path->{opt}) {
+ print("Hey! $path->{env} is not set.\n");
+ print("Where is your $path->{txt} directory? [$path->{default}] ");
+ my $ptmp = <STDIN>;
+ chomp($ptmp);
+ if ($ptmp ne '') {
+ $paths{$path->{project}} = $ptmp;
+ } else {
+ $paths{$path->{project}} = $path->{default};
+ }
+ } elsif ($path->{opt}) {
+ $paths{$path->{project}} = $path->{opt};
+ } else {
+ $paths{$path->{project}} = $ENV{$path->{env}};
+ }
+ if (!(-d $paths{$path->{project}}
+ || (-l $paths{$path->{project}} && -d readlink($paths{$path->{project}})))) {
+ print STDERR ("$paths{$path->{project}} doesn't seem to be a directory\n");
+ exit(1);
+ }
+ if (! -f (glob("$paths{$path->{project}}/$path->{check}"))[0]) {
+ print STDERR ("$paths{$path->{project}} doesn't look like a $path->{txt} directory to me.\n");
+ exit(1);
+ }
+}
+
+if ($#ARGV >= 0 && (-f "$POMNG_ROOT_DIR/$ARGV[0]/info"
+ || -f "$POMNG_ROOT_DIR/$ARGV[0].info")) {
+ # patch or update file
+ # user wants to specify patches manually
+ for my $p (@ARGV) {
+ if (-f "$POMNG_ROOT_DIR/$p/info") {
+ push(@patchlets, $p);
+ } elsif (-f "$p.info") {
+ push(@patchlets, $p);
+ }
+ }
+ # magic repository
+ $opt_repository = 'magic';
+} else {
+ # no patch names given on cmdline, have to consider all patches
+ #
+ opendir(INDIR, $POMNG_ROOT_DIR);
+ my @allfiles = readdir(INDIR);
+ closedir(INDIR);
+ for my $f (@allfiles) {
+ if (-f "$POMNG_ROOT_DIR/$f/info") {
+ push(@patchlets, $f);
+ }
+ }
+ if ($#ARGV == 0) {
+ # single argmument is a repository name
+ $opt_repository = $ARGV[0];
+ } elsif ($#ARGV == -1) {
+ # no repository given, select 'pending'
+ $opt_repository = 'pending';
+ } else {
+ pod2usage(-verbose=>1, -exitval=>2);
+ }
+}
+
+# remove all 'excluded' patches
+my @new_patchlets;
+for my $p (@patchlets) {
+ push(@new_patchlets, $p)
+ unless grep($_ eq $p, @opt_exclude);
+}
+ at patchlets = sort(@new_patchlets);
+# 'updates' is mandatory, unless patch is explicitly specified
+unshift(@patchlets, sort(grep { $_ =~ m,.*/(.*)\.info, && ($_ = $1) } glob("$POMNG_ROOT_DIR/updates/*.patch.info")))
+ unless $opt_repository eq 'magic';
+
+# FIXME implement this as config file, not hardcoded
+if ($opt_repository eq 'magic') {
+ push(@repositories, $opt_repository);
+} else {
+ foreach my $rep (qw(updates submitted pending base extra external obsolete)) {
+ push(@repositories, $rep);
+ last if $opt_repository eq $rep;
+ }
+}
+
+if (defined($ENV{TERM}) && !$opt_batch) {
+ my $terminal = Tgetent Term::Cap { OSPEED => 9600 };
+ $clrscr = $terminal->Tputs('cl', 1);
+}
+
+# list of selected patchlets is misleading at this stage
+# print STDERR ("selected repositories: ",join("|", at repositories),"\n");
+# print STDERR ("selected patchlets: ",join("|", at patchlets),"\n");
+
+$paths{POM} = $POMNG_ROOT_DIR;
+my $session = Netfilter_POM->init(\%paths);
+print("Loading patchlet definitions");
+$session->parse_patchlets;
+printf(" done\n\n");
+sleep 1;
+
+for my $rep (@repositories) {
+
+ PATCHLET: for my $plname (@patchlets) {
+ my $patchlet = $session->{patchlets}->{$plname};
+ next PATCHLET unless defined $patchlet
+ && ($rep eq $patchlet->{info}->{repository}
+ || $rep eq 'magic');
+
+ print($clrscr);
+ print_header($session);
+ print("Already applied: ", join(" ", @{$session->{applied}}),"\n");
+
+ next PATCHLET unless $session->requirements_fulfilled($patchlet);
+ print("Testing ${plname}... ");
+ if (grep($_ eq $plname, @{$session->{applied}})) {
+ print("already applied.\n");
+ next PATCHLET;
+ } elsif ($session->apply_patchlet($patchlet, !$opt_reverse, 1, "/tmp/pom-$$", $opt_verbose)) {
+ print("Reverse Test passed - assuming already applied.\n");
+ push(@{$session->{applied}}, $plname);
+ next PATCHLET;
+ }
+ print("not applied\n");
+ printf("The %s patch:\n", $plname);
+ printf(" Author: %s\n", $patchlet->{info}->{author});
+ printf(" Status: %s\n", $patchlet->{info}->{status});
+ printf(" Version: %s\n", $patchlet->{info}->{version})
+ if exists $patchlet->{info}->{version};
+ print "\n";
+ print $patchlet->{info}->{help};
+ my $first_try = 1;
+ $session->{ERRMSG} = '';
+ PROMPT:
+ my $key;
+ if ($opt_batch && $first_try) {
+ $key = 'y';
+ $first_try = 0;
+ } else {
+ print_prompt($opt_reverse);
+ $key = <STDIN>;
+ }
+ chomp($key);
+ $key = lc($key);
+ if ($key eq 'y') {
+ if ($session->dependencies_fulfilled($patchlet) < 0) {
+ # conflict
+ $session->perror;
+ goto PROMPT;
+ }
+ if ($session->apply($patchlet, $opt_reverse, 1)
+ && $session->apply($patchlet, $opt_reverse, 0)) {
+ if (!$opt_reverse) {
+ push(@{$session->{applied}}, $plname);
+ } else {
+ $session->{applied} = [ grep($_ ne $plname, @{$session->{applied}}) ];
+ }
+ } else {
+ $session->perror;
+ goto PROMPT;
+ }
+ } elsif ($key eq 't') {
+ if ($session->dependencies_fulfilled($patchlet) < 0
+ || !$session->apply($patchlet, $opt_reverse, 1)) {
+ $session->perror;
+ } else {
+ print "Patch $plname applies cleanly\n";
+ }
+ goto PROMPT;
+ } elsif ($key eq 'f') {
+ my $ret = $session->dependencies_fulfilled($patchlet);
+ if ($ret == 0) {
+ next PATCHLET unless
+ $session->apply_dependencies($patchlet, 1, 0);
+ } elsif ($ret == -1) {
+ next PATCHLET;
+ }
+ $session->apply($patchlet, $opt_reverse, 0);
+ push(@{$session->{applied}}, $plname);
+ } elsif ($key eq 'a') {
+ $opt_reverse = !$opt_reverse if $opt_reverse;
+ goto PROMPT;
+ } elsif ($key eq 'r') {
+ $opt_reverse = !$opt_reverse unless $opt_reverse;
+ goto PROMPT;
+ } elsif ($key eq 'b') {
+ print("not implemented yet");
+ goto PROMPT;
+ } elsif ($key eq 'w') {
+ next PATCHLET;
+ } elsif ($key eq 'q') {
+ last_words($session);
+ exit(0);
+ } elsif ($key eq '?') {
+ print_prompt_help();
+ goto PROMPT;
+ }
+ }
+}
+last_words($session);
+
+__END__
+
+=head1 NAME
+
+runme - The patch-o-matic main program
+
+=head1 SYNOPSIS
+
+./runme [options] suite|suite/patch-dir
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--download>
+
+some patches are stored at external locations and are not available by
+default. To add these to the selection of patchlets, add the
+--download switch to the command line and they will be available in
+the "external" suite. You need curl or wget installed for this to
+work.
+
+=item B<--batch>
+
+batch mode, automatically applying patches.
+
+=item B<--test>
+
+test mode, automatically test patches.
+
+=item B<--check>
+
+check mode, automatically checks if patches are alreay applied.
+produces a logfile: rune.out-check
+
+=item B<--reverse>
+
+back out the selected patches.
+
+=item B<--exclude> suite/patch-dir
+
+excludes the named patch. can be used multiple times.
+
+=item B<--help>
+
+print a help message
+
+=item B<--man>
+
+print the whole manpage
+
+=item B<--kernel-path> path
+
+specify the kernel source path
+
+=item B<--iptables-path> path
+
+specify the iptables source path
+
+=item B<--verbose>
+
+verbose output (shows output of patch command)
+
+=back
+
+=head1 DESCRIPTION
+
+B<runme> is the main executable of the patch-o-matic system.
+
+Available suites: updates submitted pending base extra external obsolete
+
+=cut
diff --git a/sources.list b/sources.list
new file mode 100644
index 0000000..e9d5afb
--- /dev/null
+++ b/sources.list
@@ -0,0 +1,17 @@
+# Add the URLs for external patchlets here
+#
+
+# geoip, maintained by Nicolas Bouliane <nicboul at gmail.com>
+http://people.netfilter.org/acidfu/patchlets/
+
+# condition, maintained by Massimiliano Hofer <max at nucleus.it>
+http://www.nucleus.it/pom-repo
+
+# ipp2p, time, IPMARK and connlimit maintained by Krzysztof Oledzki <ole at ans.pl>
+http://people.netfilter.org/ole/pom/
+
+# ACCOUNT, maintained by Intra2net AG <opensource at intra2net.com>
+http://www.intra2net.com/de/produkte/opensource/ipt_account/
+
+# Portknocking and SPA (J. Federico Hernandez <frozenspot at gmail.com>)
+http://svn.berlios.de/svnroot/repos/portknocko/trunk/pom/
More information about the netfilter-cvslog
mailing list