[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