[jdeb] 01/09: Imported Upstream version 1.0.2~git20130829

Tony Mancill tmancill at moszumanska.debian.org
Sun Aug 31 05:56:52 UTC 2014


This is an automated email from the git hooks/post-receive script.

tmancill pushed a commit to branch master
in repository jdeb.

commit 924c939157902fea5193bc7beaa07b87de5b0ec5
Author: tony mancill <tmancill at debian.org>
Date:   Tue May 20 22:10:33 2014 -0700

    Imported Upstream version 1.0.2~git20130829
---
 .gitignore                                         |  12 +
 .travis.yml                                        |   6 +
 HISTORY.md                                         | 132 +++++++
 LICENSE.txt                                        | 201 ++++++++++
 NOTICE.md                                          |   2 +
 README.md                                          |  36 ++
 TODO.md                                            |   1 +
 contrib/eclipse-formatting-profile.xml             | 283 ++++++++++++++
 contrib/maven_scanpackages.sh                      |  93 +++++
 docs/ant.md                                        | 168 ++++++++
 docs/maven.md                                      | 220 +++++++++++
 pom.xml                                            | 281 ++++++++++++++
 src/examples/ant/build.xml                         |  49 +++
 src/examples/ant/src/deb/control/control           |   7 +
 .../main/java/org/vafer/jdeb/examples/Main.java    |   7 +
 src/examples/maven/CHANGES.txt                     |   2 +
 src/examples/maven/pom.xml                         |  80 ++++
 src/examples/maven/src/deb/control/control         |   7 +
 src/examples/maven/src/deb/control/postinst        |   1 +
 src/examples/maven/src/deb/control/postrm          |   1 +
 src/examples/maven/src/deb/control/preinst         |   1 +
 src/examples/maven/src/deb/control/prerm           |   1 +
 src/examples/maven/src/deb/init.d/myservice        |   0
 .../main/java/org/vafer/jdeb/examples/Main.java    |   7 +
 src/it/no-datasets/pom.xml                         |  33 ++
 src/it/no-datasets/src/deb/control/control         |   7 +
 .../main/java/org/vafer/jdeb/examples/Main.java    |   7 +
 src/it/no-datasets/verify.groovy                   |   1 +
 src/it/pom-only/pom.xml                            |  34 ++
 src/it/pom-only/src/deb/control/control            |   7 +
 src/it/pom-only/verify.groovy                      |   1 +
 src/it/settings.xml                                |  35 ++
 .../java/org/vafer/jdeb/ChangesFileBuilder.java    |  80 ++++
 src/main/java/org/vafer/jdeb/Compression.java      |  82 ++++
 src/main/java/org/vafer/jdeb/Console.java          |  29 ++
 src/main/java/org/vafer/jdeb/ControlBuilder.java   | 208 ++++++++++
 src/main/java/org/vafer/jdeb/DataBuilder.java      | 307 +++++++++++++++
 src/main/java/org/vafer/jdeb/DataConsumer.java     |  34 ++
 src/main/java/org/vafer/jdeb/DataProducer.java     |  29 ++
 src/main/java/org/vafer/jdeb/DebMaker.java         | 383 ++++++++++++++++++
 .../java/org/vafer/jdeb/PackagingException.java    |  43 +++
 src/main/java/org/vafer/jdeb/ant/Data.java         | 107 ++++++
 src/main/java/org/vafer/jdeb/ant/DebAntTask.java   | 173 +++++++++
 src/main/java/org/vafer/jdeb/ant/Link.java         | 109 ++++++
 src/main/java/org/vafer/jdeb/ant/Mapper.java       | 104 +++++
 src/main/java/org/vafer/jdeb/ant/TaskConsole.java  |  45 +++
 .../java/org/vafer/jdeb/changes/ChangeSet.java     | 105 +++++
 .../org/vafer/jdeb/changes/ChangesProvider.java    |  22 ++
 .../jdeb/changes/TextfileChangesProvider.java      | 143 +++++++
 .../jdeb/debian/BinaryPackageControlFile.java      |  85 ++++
 .../java/org/vafer/jdeb/debian/ChangesFile.java    | 102 +++++
 .../java/org/vafer/jdeb/debian/ControlField.java   | 130 +++++++
 .../java/org/vafer/jdeb/debian/ControlFile.java    | 153 ++++++++
 src/main/java/org/vafer/jdeb/mapping/LsMapper.java | 237 ++++++++++++
 src/main/java/org/vafer/jdeb/mapping/Mapper.java   |  30 ++
 .../java/org/vafer/jdeb/mapping/NullMapper.java    |  31 ++
 .../java/org/vafer/jdeb/mapping/PermMapper.java    | 107 ++++++
 .../org/vafer/jdeb/maven/AbstractPluginMojo.java   |  47 +++
 src/main/java/org/vafer/jdeb/maven/Data.java       | 217 +++++++++++
 src/main/java/org/vafer/jdeb/maven/DebMojo.java    | 427 +++++++++++++++++++++
 src/main/java/org/vafer/jdeb/maven/Mapper.java     | 104 +++++
 .../vafer/jdeb/maven/MissingSourceBehavior.java    |  20 +
 .../java/org/vafer/jdeb/maven/MojoConsole.java     |  44 +++
 .../vafer/jdeb/producers/AbstractDataProducer.java |  80 ++++
 .../vafer/jdeb/producers/DataProducerArchive.java  | 151 ++++++++
 .../jdeb/producers/DataProducerDirectory.java      | 129 +++++++
 .../org/vafer/jdeb/producers/DataProducerFile.java |  74 ++++
 .../vafer/jdeb/producers/DataProducerFileSet.java  |  87 +++++
 .../org/vafer/jdeb/producers/DataProducerLink.java |  60 +++
 .../jdeb/producers/DataProducerPathTemplate.java   |  51 +++
 .../java/org/vafer/jdeb/signing/PGPSigner.java     | 152 ++++++++
 .../java/org/vafer/jdeb/utils/FilteredFile.java    |  70 ++++
 .../vafer/jdeb/utils/InformationInputStream.java   | 187 +++++++++
 .../vafer/jdeb/utils/InformationOutputStream.java  |  58 +++
 .../org/vafer/jdeb/utils/MapVariableResolver.java  |  39 ++
 src/main/java/org/vafer/jdeb/utils/Utils.java      | 187 +++++++++
 .../org/vafer/jdeb/utils/VariableResolver.java     |  29 ++
 src/main/resources/org/vafer/jdeb/ant/antlib.xml   |   4 +
 src/test/java/org/vafer/jdeb/ArchiveVisitor.java   |  31 ++
 src/test/java/org/vafer/jdeb/ArchiveWalker.java    |  96 +++++
 .../java/org/vafer/jdeb/DataBuilderTestCase.java   |  82 ++++
 src/test/java/org/vafer/jdeb/DebMakerTestCase.java | 157 ++++++++
 .../java/org/vafer/jdeb/EmptyDataProducer.java     |  28 ++
 src/test/java/org/vafer/jdeb/NullConsole.java      |  28 ++
 .../org/vafer/jdeb/ant/AntSelectorTestCase.java    |  30 ++
 .../org/vafer/jdeb/ant/DebAntTaskTestCase.java     | 308 +++++++++++++++
 .../changes/TextfileChangesProviderTestCase.java   |  79 ++++
 .../org/vafer/jdeb/debian/ChangesFileTestCase.java |  36 ++
 .../vafer/jdeb/debian/ControlFieldTestCase.java    |  44 +++
 .../jdeb/debian/PackageControlFileTestCase.java    |  91 +++++
 .../org/vafer/jdeb/mapping/LsMapperTestCase.java   | 101 +++++
 .../java/org/vafer/jdeb/maven/DataTestCase.java    | 122 ++++++
 .../DataProducerPathTemplateTestCase.java          | 115 ++++++
 .../org/vafer/jdeb/signing/PGPSignerTestCase.java  |  74 ++++
 .../org/vafer/jdeb/utils/FilteredFileTestCase.java |  69 ++++
 .../jdeb/utils/InformationInputStreamTestCase.java |  87 +++++
 .../java/org/vafer/jdeb/utils/UtilsTestCase.java   |  97 +++++
 src/test/resources/org/vafer/gpg/fingerprint.txt   |   4 +
 src/test/resources/org/vafer/gpg/pubring.gpg       | Bin 0 -> 1182 bytes
 src/test/resources/org/vafer/gpg/secring.gpg       | Bin 0 -> 1331 bytes
 src/test/resources/org/vafer/jdeb/ar/data.ar       | Bin 0 -> 216 bytes
 .../resources/org/vafer/jdeb/changes/changes.txt   |   2 +
 .../resources/org/vafer/jdeb/deb/control/control   |  12 +
 .../resources/org/vafer/jdeb/deb/control/postinst  |   4 +
 .../resources/org/vafer/jdeb/deb/control/prerm     |   4 +
 src/test/resources/org/vafer/jdeb/deb/data.tar.bz2 | Bin 0 -> 164 bytes
 src/test/resources/org/vafer/jdeb/deb/data.tgz     | Bin 0 -> 148 bytes
 src/test/resources/org/vafer/jdeb/deb/data.zip     | Bin 0 -> 221 bytes
 .../org/vafer/jdeb/deb/data/test/testfile          |   1 +
 .../resources/org/vafer/jdeb/deb/test/testfile4    |   1 +
 .../org/vafer/jdeb/utils/utf16be-lf-bom.txt        | Bin 0 -> 22 bytes
 .../resources/org/vafer/jdeb/utils/utf16be-lf.txt  | Bin 0 -> 20 bytes
 .../org/vafer/jdeb/utils/utf16le-lf-bom.txt        | Bin 0 -> 22 bytes
 .../resources/org/vafer/jdeb/utils/utf16le-lf.txt  | Bin 0 -> 20 bytes
 .../resources/org/vafer/jdeb/utils/utf8-crlf.txt   |   1 +
 .../resources/org/vafer/jdeb/utils/utf8-lf-bom.txt |   1 +
 .../resources/org/vafer/jdeb/utils/utf8-lf.txt     |   1 +
 src/test/resources/testbuild.xml                   | 114 ++++++
 118 files changed, 8638 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5f74209
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.DS_Store
+.classpath
+.project
+.settings
+target
+eclipse
+dependency-reduced-pom.xml
+*.iml
+*.iws
+*.ipr
+/.idea
+.release
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1c86c94
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: java
+jdk:
+  - oraclejdk7
+  - oraclejdk6
+  - openjdk7
+  - openjdk6
diff --git a/HISTORY.md b/HISTORY.md
new file mode 100644
index 0000000..bf3d6ae
--- /dev/null
+++ b/HISTORY.md
@@ -0,0 +1,132 @@
+## Version 1.1, released ?
+
+* [ADD] xz compression support
+* [ADD] Added link support to the Ant task
+* [ADD] Support permission in (hard) link setup
+* [FIX] On Windows, parent directories are now created automatically when adding files to the data archive
+* [CHG] Links are now symbolic by default
+
+
+## Version 1.0.1, released 28.02.2013
+
+* [ADD] Override version via environment variable DEBVERSION
+* [FIX] Use the joint copyright
+* [FIX] Fixed the maven to debian version mapping
+
+
+## Version 1.0, released 10.01.2013
+
+* [ADD] Support for symbolic links (Maven only)
+* [FIX] Unresolved variables in package maintainer scripts no longer break the build
+* [CHG] Use "_all" as architecture postfix by default
+
+
+## Version 0.11, released 14.07.2012
+
+* [ADD] Added "timestamped" maven config to turn "SNAPSHOT" into a timestamp
+* [ADD] Added "verbose" maven config option to show/hide INFO logs
+* [ADD] Expand variables in configuration files "conffiles", "preinst", "postinst", "prerm", "postrm"
+* [ADD] Added a "template" data type to create dirs
+* [ADD] Added "missingSrc" maven config to control behavior on missing files/folders
+* [CHG] Use "~" instead of "+" when converting from a SNAPSHOT version
+* [REM] Deprecated PrefixMapper was removed (use PermMapper instead)
+
+
+## Version 0.10, released 18.02.2012
+
+Polishing and regression fixes
+
+* [FIX] Don't throw exception when detecting zip archives
+* [FIX] Make sure to close the tar output stream
+* [CHG] Have warn/info level on messages
+* [CHG] Fix line endings for control files
+* [ADD] Access to all Maven variables
+
+
+## Version 0.9, released 17.12.2011
+
+Some smaller fixes, Support for the 1.8 format, Changes support working.
+
+* [FIX] Default path for changes file with Maven
+* [FIX] Unresolved variables are now treated as null
+* [CHG] "Changes" support version 1.8 
+* [CHG] Warn if control files have non-unix line endings
+* [CHG] Throw an exception for unknown mappers
+* [REM] InvalidDescriptorException, wasn't really used anyway
+* [ADD] Added an "attach" attribute to specify whether maven artifact should be attached to project.
+* [ADD] Provide SHA1, SHA256 and not just MD5 for descriptors
+* [ADD] Provide "project.version" when using maven
+
+
+## Version 0.8, released 27.06.2010
+
+Lot of refactoring and support for configuration on the maven plugin. Easier permission mapping.
+
+* [CHG] Default maven artifact type is now "deb" instead of "deb-archive".
+* [CHG] Switched to commons compress for archive building.
+* [CHG] Renamed the maven goal from "deb" to "jdeb" to be more consistent.
+* [CHG] Maven goal no longer attached to execution phase by default. (see examples)
+* [CHG] Deprecated "FileSetDataProducer" in favor of "DataProducerFileSet"
+* [ADD] Added a "type" attribute to the "data" elements.
+* [ADD] New options to the maven plugin to configure the attached artifact.
+* [ADD] Added Examples for ant and maven.
+* [ADD] New "file" data source.
+
+
+## Version 0.7, released 18.08.2008
+
+Proper closing of streams!
+Many improvements on the Ant task.
+Quite a few fixes related to locale settings.
+Support for bzip2 and more descriptor keys.
+
+* [FIX] English locale for date format.	
+* [FIX] Proper installation size to be kbytes instead of bytes.	
+* [FIX] Close streams properly.	
+* [CHG] The Ant task now breaks on errors.
+* [ADD] Support for bzip2 compression in data element of the Ant task.
+* [ADD] Compression attribute to specify data file compression (bzip2, gzip, none).
+* [ADD] More package descriptor keys (Pre-Depends, Recommends, Suggests, Breaks, Enhances, Homepage>).
+* [ADD] Verbose attribute for the Ant task.
+* [ADD] The Ant task now accepts tarfileset elements.
+
+
+## Version 0.6, released 11.01.2008
+
+* [FIX] Fixed the trailing linefeed in the 'changes' section of the changes file.
+
+
+## Version 0.5, released 26.11.2007
+
+* [FIX] Fixed the 'ls' parsing.
+* [CHG] Switched to ArInputStream/ArOutputStream.
+* [REM] Removed deprecated ant task delegate.
+* [ADD] Added "changesSave" attribute to save release information to. No longer saving those information to "changesIn".
+
+
+## Version 0.4, released 20.09.2007
+
+* [FIX] Fixed mapper support.
+* [CHG] Changed lookup from environment (DEBEMAIL, DEBFULLNAME) to overrule the descriptor.
+* [REM] Removed deprecated prefix/strip syntax.
+* [ADD] Added support for multiple mappers.
+* [ADD] Added more fields to the package descriptor.
+
+
+## Version 0.3, released 15.09.2007
+
+* [ADD] Added maintainer lookup from environment (DEBEMAIL, DEBFULLNAME).
+* [ADD] Added plugin implementation for maven.
+* [ADD] Added stricter descriptor validation.
+
+
+## Version 0.2, released 21.08.2007
+
+* [FIX] Fixed the delete of the temporary files.
+* [ADD] Added support for signed changes files.
+* [ADD] Added support for mapping ownerships and rights.
+
+
+## Version 0.1, released 19.02.2007
+
+Initial release.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE.md b/NOTICE.md
new file mode 100644
index 0000000..01a15f7
--- /dev/null
+++ b/NOTICE.md
@@ -0,0 +1,2 @@
+The jdeb project is under a joint copyright of the developers listed in the pom.xml file.
+Thanks to all the contributors listed for their patches and contributions.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..16850e9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+[![Build Status](https://secure.travis-ci.org/tcurdt/jdeb.png)](http://travis-ci.org/tcurdt/jdeb)
+
+# Debian packages in Java
+
+This library provides an Ant task and a Maven plugin to create Debian packages
+from Java builds in a truly cross platform manner. Build your Debian packages
+on any platform that has Java support. Windows, Linux, OS X - it doesn't require
+additional native tools installed. The API underneath is well abstracted and
+can easily be adopted for other areas as well.
+
+Check the documentation on how to use it with [Maven](http://github.com/tcurdt/jdeb/blob/master/docs/maven.md)
+or [Ant](http://github.com/tcurdt/jdeb/blob/master/docs/ant.md). Especially don't forget to check out the
+[examples](http://github.com/tcurdt/jdeb/blob/master/src/examples/). Current
+[javadocs](http://tcurdt.github.com/jdeb/release/1.0.1/apidocs/) and a source
+[xref](http://tcurdt.github.com/jdeb/release/1.0.1/xref/) is also available.
+
+
+## Where to get it
+
+The jars are available in the [Maven central repository](http://repo1.maven.org/maven2/org/vafer/jdeb/).
+The source releases you can get in the [download section](http://github.com/tcurdt/jdeb/downloads).
+
+If feel adventures or want to help out feel free to get the latest code
+[via git](http://github.com/tcurdt/jdeb/tree/master).
+
+    git clone git://github.com/tcurdt/jdeb.git
+
+
+## Related projects
+
+Some links to other cross platform tools to package Linux applications:
+
+* [ant-deb-task](http://code.google.com/p/ant-deb-task)
+* [jRPM](http://jrpm.sourceforge.net)
+* [RedLine](http://www.freecompany.org/redline)
+* [Install-Toolkit](http://install-toolkit.sourceforge.net)
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..e70921c
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1 @@
+please see https://github.com/tcurdt/jdeb/issues
\ No newline at end of file
diff --git a/contrib/eclipse-formatting-profile.xml b/contrib/eclipse-formatting-profile.xml
new file mode 100644
index 0000000..15365db
--- /dev/null
+++ b/contrib/eclipse-formatting-profile.xml
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<profiles version="12">
+<profile kind="CodeFormatterProfile" name="jdeb" version="12">
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="240"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="8"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="240"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+</profile>
+</profiles>
diff --git a/contrib/maven_scanpackages.sh b/contrib/maven_scanpackages.sh
new file mode 100644
index 0000000..43f690b
--- /dev/null
+++ b/contrib/maven_scanpackages.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+###############################################################################
+#
+# Update debian repository indexes.
+#
+# It actually makes maven repositories expose a debian index.
+#
+# It also create a "virtual" stable debian repository in which is selected only
+# stable releases (filter milestonnes and release candidates).
+#
+# Requirements:
+# * This script is based on dpkg-scanpackages. It is provided by dpkg-dev package.
+# * The script expect to find (configurable)
+# ** a "releases" folder containing a maven repository with the deployed
+#    releases
+# ** a "snapshots" folder containing a maven repository with the deployed
+#    snaphots
+# ** make sure the "stable" folder exists if you want a filtered stable debian
+#    repository
+#
+# Setup:
+# You need to set the $ROOT_REP variable to where your maven repositories are
+# located.
+#
+###############################################################################
+
+ROOT_REP=/home/maven/public_html
+SNAPSHOTS_REP="snapshots"
+RELEASES_REP="releases"
+STABLE_REP="stable"
+
+cd "$ROOT_REP"
+
+## snapshots
+if [ -d $SNAPSHOTS_REP ]; then
+  echo "Generates snapshots index"
+
+  dpkg-scanpackages -m $SNAPSHOTS_REP /dev/null > $SNAPSHOTS_REP/Packages.tmp && mv -f $SNAPSHOTS_REP/Packages.tmp $SNAPSHOTS_REP/Packages
+  gzip -9c $SNAPSHOTS_REP/Packages > $SNAPSHOTS_REP/Packages.gz.tmp && mv -f $SNAPSHOTS_REP/Packages.gz.tmp $SNAPSHOTS_REP/Packages.gz
+
+  rm -rf $SNAPSHOTS_REP/Release $SNAPSHOTS_REP/Release.gpg
+  apt-ftparchive -c=$SNAPSHOTS_REP/Release.conf release $SNAPSHOTS_REP > $SNAPSHOTS_REP/Release
+  gpg -abs --default-key 0398E391 -o $SNAPSHOTS_REP/Release.gpg $SNAPSHOTS_REP/Release
+fi
+
+## releases
+if [ -d $RELEASES_REP ]; then
+  echo "Generates releases index"
+
+  dpkg-scanpackages -m $RELEASES_REP /dev/null > $RELEASES_REP/Packages.tmp && mv -f $RELEASES_REP/Packages.tmp $RELEASES_REP/Packages
+  gzip -9c $RELEASES_REP/Packages > $RELEASES_REP/Packages.gz.tmp && mv -f $RELEASES_REP/Packages.gz.tmp $RELEASES_REP/Packages.gz
+
+  rm -rf $RELEASES_REP/Release $RELEASES_REP/Release.gpg
+  apt-ftparchive -c=$RELEASES_REP/Release.conf release $RELEASES_REP > $RELEASES_REP/Release
+  gpg -abs --default-key 0398E391 -o $RELEASES_REP/Release.gpg $RELEASES_REP/Release
+fi
+
+## stable
+if [ -d stable ]; then
+  echo "Generates stable index"
+
+  rm -rf /tmp/stable_scanpackages
+  mkdir -p /tmp/stable_scanpackages/$RELEASES_REP
+
+  function link_package ()
+  {
+    basepath=`dirname $1`
+    basepath=${basepath##$ROOT_REP/}
+    basepath=${basepath%*/}
+    mkdir -p $basepath
+    fullpath=`readlink -f $1`
+    ln -sf $fullpath "/tmp/stable_scanpackages/$basepath"
+  }
+
+  cd /tmp/stable_scanpackages/
+
+  for i in $(find "$ROOT_REP/$RELEASES_REP" -name "*.[0-9][0-9].deb" ) ; do
+    link_package $i
+  done
+
+  for i in $(find "$ROOT_REP/$RELEASES_REP" -name "*.[0-9].deb" ) ; do
+    link_package $i
+  done
+
+  dpkg-scanpackages -m $RELEASES_REP /dev/null > "$ROOT_REP/$STABLE_REP/Packages.tmp" && mv -f "$ROOT_REP/$STABLE_REP/Packages.tmp" "$ROOT_REP/$STABLE_REP/Packages"
+  gzip -9c "$ROOT_REP/$STABLE_REP/Packages" > "$ROOT_REP/$STABLE_REP/Packages.gz.tmp" && mv -f "$ROOT_REP/$STABLE_REP/Packages.gz.tmp" "$ROOT_REP/$STABLE_REP/Packages.gz"
+
+  cd "$ROOT_REP"
+
+  rm -rf $STABLE_REP/Release $STABLE_REP/Release.gpg
+  apt-ftparchive -c=$STABLE_REP/Release.conf release $STABLE_REP > $STABLE_REP/Release
+  gpg -abs --default-key 0398E391 -o $STABLE_REP/Release.gpg $STABLE_REP/Release
+fi
diff --git a/docs/ant.md b/docs/ant.md
new file mode 100644
index 0000000..a27cb87
--- /dev/null
+++ b/docs/ant.md
@@ -0,0 +1,168 @@
+# How to use jdeb with Ant
+
+Attribute     | Description                                                                  | Required
+------------- | ---------------------------------------------------------------------------- | --------------------------
+destfile      | The debian package to be generated                                           | Yes
+control       | The directory containing the control files                                   | Yes
+compression   | Compression method for the data file (`gzip`, `bzip2`, `xz` or `none`)       | No; defaults to `gzip`
+verbose       | Print detailed info during the package generation                            | No; defaults to `false`
+keyring       | The file containing the PGP keys                                             | No
+key           | The name of the key to be used in the keyring                                | No
+passphrase    | The passphrase to use the key                                                | No
+changesIn     | The changes to add                                                           | No
+changesOut    | The changes file generated                                                   | No
+changesSave   | The merged changes file                                                      | No
+
+The jdeb Ant task can package up a directory as Debian package. You have to
+provide the control files defining meta information about the package (except
+the `md5sums` which gets created automatically). It creates the archive
+and if you want even a signed changes file.
+
+```xml
+    <target name="package">
+      <taskdef name="deb" classname="org.vafer.jdeb.ant.DebAntTask"/>
+      <copy todir="${deb}/control">
+        <fileset dir="src/main/resources/deb/control"/>
+        <filterset begintoken="[[" endtoken="]]">
+          <filter token="version" value="${version}"/>
+          <filter token="name" value="${ant.project.name}"/>
+        </filterset>
+      </copy>
+      <deb destfile="jdeb.deb" control="${deb}/control">
+        <data src="src/main/resources/deb/data" type="directory">
+          <exclude name="**/.svn"/>
+        </data>
+      </deb>
+    </target>
+```
+
+For cross platform builds it might be important to retain permissions,
+ownerships and links. When you provide the original tar as input the meta data
+will be kept intact gets included directly into the deb. You can apply simple
+modifications like prefixing or stripping of paths though.
+
+```xml
+    <deb destfile="jdeb.deb" control="${deb}/control">
+      <data src="src/release.tgz" type="archive">
+        <mapper type="perm" strip="1" prefix="/somewhere/else"/>
+        <exclude name="**/.svn"/>
+      </data>
+    </deb>
+```
+
+For more complex permission and ownership adjustments you can use a "ls"
+mapper. It allows you to define permissions and ownerships in a text file and
+even under Windows you will be able to build your debian package.
+
+```xml
+    <deb destfile="jdeb.deb" control="${deb}/control">
+      <data src="src/release.tgz" type="archive">
+        <mapper type="ls" src="mapping.txt" />
+      </data>
+    </deb>
+```
+
+The mapper will apply the output of an "ls -laR > mapping.txt" command
+that should look like this
+
+    ./trunk/target/test-classes/org/vafer/dependency:
+    total 176
+    drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .
+    drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..
+    -rw-r--r--    1 tcurdt  tcurdt  2934 Jun 25 03:48 DependenciesTestCase.class
+    -rw-r--r--    1 tcurdt  tcurdt  2176 Jun 25 03:48 WarTestCase.class
+    drwxr-xr-x    4 tcurdt  tcurdt   136 Jun 25 03:48 classes
+
+It's also possible to use a `fileset` or even a `tarfileset` to
+specify the set of files to include with their permissions :
+
+```xml
+    <deb destfile="jdeb.deb" control="${deb}/control">
+      <tarfileset dir="src/main/resources/deb/data"
+               prefix="/somewhere/else"
+             filemode="600"
+             username="tcurdt"
+                group="tcurdt"/>
+    </deb>
+```
+
+Links can be added by specifying a `link` element:
+
+```xml
+    <deb destfile="jdeb.deb" control="${deb}/control">
+      <link name="/usr/share/java/foo.jar" target="/usr/share/java/foo-1.0.jar"/>
+    </deb>
+```
+
+Here are the supported attributes on the `link` element:
+
+Attribute     | Description                                            | Required
+------------- | ------------------------------------------------------ | -----------------------
+name          | The path of the link                                   | Yes
+target        | The target of the link                                 | Yes
+symbolic      | The type of the link (`true`: symbolic, `false`: hard) | No; defaults to `true`
+uid           | Numerical uid                                          | No; defaults to 0
+gid           | Numerical gid                                          | No; defaults to 0
+user          | User name                                              | No; defaults to "root"
+group         | User group                                             | No; defaults to "root"
+mode          | Permissions as octet                                   | No; deftauls to 777
+
+## Changes file
+
+In order to also create a changes file you will need to provide the input and
+output of the changes. The input file is a much simpler file where you should
+list your changes. Every change should be starting with the " * " and one line
+only.
+
+    * changes for the next release
+    release distribution=staging, date=20:13 17.08.2007,version=1.4+r89114,urgency=low,by=Torsten Curdt <torsten at vafer.org>
+    * debian changes support
+
+When you do a release jdeb will add (or complete!) the release line and create
+a debian format standard changes file for you. (Don't forget to commit changes
+jdeb did to the file.) From Ant you have to call jdeb like this
+
+```xml
+    <deb destfile="jdeb.deb"
+          control="${deb}/control"
+        changesIn="changes.txt"
+       changesOut="jdeb.changes">
+      <data src="some/dir"/>
+    </deb>
+```
+
+If you also provide a `changesSave` attribute the jdeb will add release
+information to the original input and write out the new file.
+
+```xml
+    <deb destfile="jdeb.deb"
+          control="${deb}/control"
+        changesIn="changes.txt"
+       changesOut="jdeb.changes"
+      changesSave="changes.txt">
+      <data src="some/dir"/>
+    </deb>
+```
+
+## Signing changes
+
+To have the changes be signed, make sure you have the
+[BouncyCastle OpenPGP/BCPG jar](http://www.bouncycastle.org/latest_releases.html) in your
+classpath (just copy it into the `$ANT_HOME/lib` folder - next to jdeb).
+Then you can sign your changes file with:
+
+```xml
+    <deb destfile="jdeb.deb"
+          control="${deb}/control"
+        changesIn="changes.txt"
+       changesOut="jdeb.changes"
+              key="2E074D8F"
+       passphrase="secret"
+          keyring="/Users/tcurdt/.gnupg/secring.gpg">
+      <data src="some/dir"/>
+    </deb>
+```
+
+<b>Security Note</b>: Hard coding the passphrase in the `<deb>` task can be a serious
+security hole. Consider using variable substitution and asking the passphrase
+to the user with the `<input>` task, or retrieving it from a secured `.properties` file.
diff --git a/docs/maven.md b/docs/maven.md
new file mode 100644
index 0000000..83e825c
--- /dev/null
+++ b/docs/maven.md
@@ -0,0 +1,220 @@
+# How to use jdeb with Maven
+
+Generating a default Debian package with maven is particular easy. Just add
+the plugin to your POM like this
+
+```xml
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>jdeb</artifactId>
+        <groupId>org.vafer</groupId>
+        <version>1.0</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>jdeb</goal>
+            </goals>
+            <configuration>
+              <dataSet>
+                <data>
+                  <src>${project.build.directory}/${project.build.finalName}.jar</src>
+                  <type>file</type>
+                  <mapper>
+                    <type>perm</type>
+                    <prefix>/usr/share/jdeb/lib</prefix>
+                  </mapper>
+                </data>
+              </dataSet>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+```
+
+At least the one main control file is required to control the creation of the
+debian package. This required control file should be found in the control
+directory. By default the control file name is also `control` which gives a
+path of `src/deb/control/control` by default. This control file contains the
+metadata about the Debian package. Usually it will look something along the lines of
+
+    Package: [[name]]
+    Version: [[version]]
+    Section: misc
+    Priority: optional
+    Architecture: all
+    Depends: jdk (>= 1.5)
+    Maintainer: Torsten Curdt <torsten at something.com>
+    Description: jetty java servlet container
+    Distribution: development
+
+
+If the environment variables `DEBEMAIL` and `DEBFULLNAME` are both set this
+will overrule the `Maintainer` field set in there. The `Installed-Size` will
+also be injected. If a changes file is used, the `Distribution` usually comes
+from that file. The default changes file is called `CHANGES.txt`. See below
+for the syntax of the content of the changes file.
+
+Property replacement will also occur in any of the standard debian control
+files: conffiles, preinst, postinst, prerm, postrm. This allows dynamic
+configuration of the form:
+
+    /etc/[[artifactId]]/[[artifactId]].properties
+    /etc/[[artifactId]]/log4j.xml
+
+If you now do a `mvn clean install`, the `deb` goal will be called and
+artifacts consisting of the deb and potentially the changes file will
+automatically be attached to the project.
+
+The jdeb maven plugin also supports a variety of configuration options. These
+configuration options provide the same features available in the jdeb ant
+task. To configure the jdeb maven plugin, populate the jdeb configuration
+section with any of the following options:
+
+Element       | Description                                                                  | Required
+------------- | ---------------------------------------------------------------------------- | -----------------------------------------------------------------
+deb           | The debian package to be generated                                           | No; defaults to `${buildDirectory}/${artifactId}_${version}.deb`
+type          | Artifact type                                                                | No; defaults to `deb`
+classifier    | Artifact classifier                                                          | No; defaults to ''
+controlDir    | The directory containing the control files                                   | No; defaults to `src/deb/control`
+installDir    | The default directory for the project artifact if no data section is present | No; defaults to `/opt/${artifactId}`
+dataSet       | A list of directories, tarballs, or files to include in the deb package      | No; defaults to include your maven artifact
+changesIn     | The changes to add                                                           | No
+changesOut    | The changes file generated                                                   | No
+changesSave   | (NYI) The merged changes file                                                | No
+compression   | (NYI) Compression method for the data file (`gzip`, `bzip2`, `xz` or `none`) | No; defaults to `gzip`
+keyring       | (NYI) The file containing the PGP keys                                       | No
+key           | (NYI) The name of the key to be used in the keyring                          | No
+passphrase    | (NYI) The passphrase to use the key                                          | No
+attach        | Attach artifact to project                                                   | No; defaults to `true`
+submodules    | Execute the goal on all sub-modules                                          | No; defaults to `true`
+timestamped   | Turn SNAPSHOT into timestamps                                                | No; defaults to `false`
+verbose       | Verbose logging                                                              | No; defaults to `true`, will be `false` in the future
+
+If you use the `dataSet` element, you'll need to populate it with a one or
+more `data` elements. A `data` element is used to specify a directory, a
+tarball archive, or a file. You can add as many data
+elements to your dataSet as you'd like. The `data` element has the
+following options:
+
+Element          | Description                                                                  | Required
+---------------- | ---------------------------------------------------------------------------- | ------------------------------------
+src              | The directory, tarball, file to include in the package                       | Yes
+dst              | New filename at destination (type must be `file`)                            | No
+linkName         | The path of the link (type must be `link`)                                   | Yes for link
+linkTarget       | The target of the link (type must be `link`)                                 | Yes for link
+type             | Type of the data source. (archive, directory, file, link or template)        | No; but will be Yes in the future
+missingSrc       | Fail if src file/folder is missing (ignore or fail)                          | No; defaults to `fail`
+includes         | A comma seperated list of files to include from the directory or tarball     | No; defaults to all files
+excludes         | A comma seperated list of files to exclude from the directory or tarball     | No; defaults to no exclutions
+mapper           | The files to exclude from the directory or tarball                           | No
+paths/(path..)   | One or more string literal paths that will created in the package            | No; Yes for type `template`
+
+There are different kinds of mappers that can be selected via the `type` argument. The most common one is the 'perm' mapper.
+
+Element       | Description                                           | Required
+------------- | ----------------------------------------------------- | -----------------------
+type          | 'perm'                                                | Yes
+prefix        | Add this prefix to the files                          | No; defaults to ""
+uid           | Numerical uid                                         | No; defaults to 0
+gid           | Numerical gid                                         | No; defaults to 0
+user          | User name                                             | No; defaults to "root"
+group         | User group                                            | No; defaults to "root"
+filemode      | File permissions as octet                             | No; deftauls to 644
+dirmode       | Dir permissions as octet                              | No; defaults to 755
+strip         | Strip n path components from the original file        | No; defaults to 0
+
+Below is an example of how you could configure your jdeb maven plugin to
+include a directory, a tarball, and a file in your deb package:
+
+```xml
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>jdeb</artifactId>
+        <groupId>org.vafer</groupId>
+        <version>1.0</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>jdeb</goal>
+            </goals>
+            <configuration>
+
+              <dataSet>
+
+                <!-- Tarball example -->
+                <data>
+                  <src>${project.basedir}/target/my_archive.tar.gz</src>
+                  <type>archive</type>
+                  <includes>...</includes>
+                  <excludes>...</excludes>
+                  <mapper>
+                    <type>perm</type>
+                    <strip>1</strip>
+                    <prefix>/somewhere/else</prefix>
+                    <user>tcurdt</user>
+                    <group>tcurdt</group>
+                    <filemode>600</filemode>
+                  </mapper>
+                </data>
+
+                <!-- Directory example -->
+                <data>
+                  <src>${project.build.directory}/data</src>
+                  <type>directory</type>
+                  <includes/>
+                  <excludes>**/.svn</excludes>
+                  <mapper>
+                    <type>ls</type>
+                    <src>mapping.txt</src>
+                  </mapper>
+                </data>
+
+                <!-- File example -->
+                <data>
+                  <src>${project.basedir}/README.txt</src>
+                  <dst>README</dst>
+                  <type>file</type>
+                  <missingSrc>ignore</missingSrc>
+                </data>
+
+                <!-- Template example -->
+                <data>
+                  <type>template</type>
+                  <paths>
+                    <path>/etc/${artifactId}</path>
+                    <path>/var/lib/${artifactId}</path>
+                    <path>/var/log/${artifactId}</path>
+                    <path>/var/run/${artifactId}</path>
+                  </paths>
+                </data>
+
+                <!-- Hard link example -->
+                <data>
+                  <type>link</type>
+                  <linkName>/a/path/on/the/target/fs</linkName>
+                  <linkTarget>/a/link/to/the/scr/file</linkTarget>
+                  <symlink>false</symlink>
+                </data>
+
+                <!-- Symbolic link example -->
+                <data>
+                  <type>link</type>
+                  <linkName>/a/path/on/the/target/fs</linkName>
+                  <linkTarget>/a/sym/link/to/the/scr/file</linkTarget>
+                  <symlink>true</symlink>
+                </data>
+              </dataSet>
+
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+```
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d6d4991
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.vafer</groupId>
+    <artifactId>jdeb</artifactId>
+    <packaging>maven-plugin</packaging>
+    <name>jdeb</name>
+    <version>1.0.2-SNAPSHOT</version>
+    <description>
+        This library provides an Ant task and a Maven plugin to create Debian packages from Java builds in a truly cross
+        platform manner. Build your Debian packages on any platform that has Java support. Windows, Linux, OS X - it doesn't
+        require additional native tools installed.
+    </description>
+    <url>http://github.com/tcurdt/jdeb</url>
+    <developers>
+        <developer>
+            <id>tcurdt</id>
+            <name>Torsten Curdt</name>
+            <email>tcurdt at apache.org</email>
+            <roles><role>Lead Developer</role></roles>
+            <timezone>+1</timezone>
+        </developer>
+        <developer>
+            <id>ebourg</id>
+            <name>Emmanuel Bourg</name>
+            <email>ebourg at apache.org</email>
+            <timezone>+1</timezone>
+        </developer>
+        <developer>
+            <id>tmortagne</id>
+            <name>Thomas Mortagne</name>
+            <email>thomas.mortagne at gmail.com</email>
+            <timezone>+1</timezone>
+        </developer>
+    </developers>
+    <contributors>
+        <contributor><name>Bryan Sant</name></contributor>
+        <contributor><name>Christian Rigdon</name></contributor>
+        <contributor><name>Elliot West</name></contributor>
+        <contributor><name>Jeroen Rosenberg</name></contributor>
+        <contributor><name>Manuel Woelker</name></contributor>
+        <contributor><name>Patrick Schultz</name></contributor>
+        <contributor><name>Petr Kozelka</name></contributor>
+        <contributor><name>Scott Kuehn</name></contributor>
+        <contributor><name>Ralph van Etten</name></contributor>
+        <contributor><name>Alexander Horz</name></contributor>
+    </contributors>
+    <licenses>
+        <license>
+            <name>Apache License 2</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    <scm>
+        <connection>scm:git:git://github.com:tcurdt/jdeb.git</connection>
+        <developerConnection>scm:git:git://github.com:tcurdt/jdeb.git</developerConnection>
+        <url>http://github.com/tcurdt/jdeb/tree/master</url>
+    </scm>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.source>1.6</maven.compiler.source>
+        <maven.compiler.target>1.6</maven.compiler.target>
+    </properties>
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-site-plugin</artifactId>
+                    <version>3.1</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>1.7.1</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <!-- <dependencyReducedPomLocation>${basedir}/target/dependency-reduced-pom.xml</dependencyReducedPomLocation> -->
+                            <minimizeJar>true</minimizeJar>
+                            <artifactSet>
+                                <includes>
+                                    <include>org.apache.commons:commons-compress</include>
+                                    <include>commons-io:commons-io</include>
+                                </includes>
+                            </artifactSet>
+                            <relocations>
+                                <relocation>
+                                    <pattern>org.apache.commons</pattern>
+                                    <shadedPattern>org.vafer.jdeb.shaded.compress</shadedPattern>
+                                </relocation>
+                            </relocations>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.11</version>
+                <configuration>
+                    <forkMode>never</forkMode>
+                    <includes>
+                        <include>**/*TestCase.java</include>
+                    </includes>
+                    <excludes>
+                        <exclude>**/Abstract*</exclude>
+                    </excludes>
+                    <testFailureIgnore>false</testFailureIgnore>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-invoker-plugin</artifactId>
+                <version>1.6</version>
+                <configuration>
+                    <!-- <debug>true</debug> -->
+                    <projectsDirectory>src/it</projectsDirectory>
+                    <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+                    <preBuildHookScript>setup</preBuildHookScript>
+                    <postBuildHookScript>verify</postBuildHookScript>
+                    <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
+                    <settingsFile>src/it/settings.xml</settingsFile>
+                    <pomIncludes>
+                        <pomInclude>*/pom.xml</pomInclude>
+                    </pomIncludes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                          <goal>integration-test</goal>
+                            <!--
+                            <goal>verify</goal>
+                            <goal>install</goal>
+                            -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <!-- mvn versions:display-dependency-updates -->
+        <!-- mvn versions:display-plugin-updates -->
+        <dependency>
+        <groupId>commons-io</groupId>
+        <artifactId>commons-io</artifactId>
+        <version>2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-compress</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>2.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>2.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-project</artifactId>
+            <version>2.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-artifact</artifactId>
+            <version>2.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+            <version>1.4.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>1.7.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpg-jdk15on</artifactId>
+            <version>1.48</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.2</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <reporting>
+        <excludeDefaults>true</excludeDefaults>
+        <outputDirectory>${project.build.directory}/site</outputDirectory>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <dependencyDetailsEnabled>false</dependencyDetailsEnabled>
+                    <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>project-team</report>
+                            <report>dependencies</report>
+                            <report>license</report>
+                            <report>scm</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.8.1</version>
+                <configuration>
+                    <linksource>true</linksource>
+                    <links>
+                        <link>http://java.sun.com/javase/6/docs/api</link>
+                    </links>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-report-plugin</artifactId>
+                <version>2.12</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>2.7.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jxr-plugin</artifactId>
+                <version>2.3</version>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>taglist-maven-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <tags>
+                        <tag>FIXME</tag>
+                        <tag>TODO</tag>
+                        <tag>@todo</tag>
+                        <tag>@deprecated</tag>
+                    </tags>
+                    <aggregate>true</aggregate>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+</project>
diff --git a/src/examples/ant/build.xml b/src/examples/ant/build.xml
new file mode 100644
index 0000000..3af211e
--- /dev/null
+++ b/src/examples/ant/build.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<project name="jdeb-example" default="package">
+
+  <property name="version" value="1.0"/>
+  <property name="description" value="Some description"/>
+  <property name="src.dir" value="${basedir}/src"/>
+  <property name="build.dir" value="${basedir}/build"/>
+
+  <target name="clean">
+    <delete dir="${build.dir}"/>
+  </target>
+
+  <target name="compile">
+    <mkdir dir="${build.dir}/classes"/>
+    <javac srcdir="src/main/java" destdir="${build.dir}/classes" includeantruntime="false"/>
+  </target>
+
+  <target name="jar" depends="compile">
+    <mkdir dir="${build.dir}/jar"/>
+    <jar destfile="${build.dir}/jar/${ant.project.name}-${version}.jar" basedir="${build.dir}/classes">
+      <manifest>
+        <attribute name="Main-Class" value="org.vafer.jdeb.example.Main"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="package" depends="jar">
+    <taskdef name="deb" classname="org.vafer.jdeb.ant.DebAntTask"/>
+
+    <copy todir="${build.dir}/deb/control">
+      <fileset dir="src/deb/control"/>
+      <filterset begintoken="[[" endtoken="]]">
+        <filter token="version" value="${version}"/>
+        <filter token="description" value="${description}"/>
+        <filter token="name" value="${ant.project.name}"/>
+      </filterset>
+    </copy>
+
+    <deb destfile="${build.dir}/${ant.project.name}.deb" control="${build.dir}/deb/control" verbose="true">
+
+      <data src="${build.dir}/jar/${ant.project.name}-${version}.jar" type="file">
+        <mapper type="perm" prefix="/usr/share/jdeb/lib"/>
+      </data>
+
+    </deb>
+
+  </target>
+
+</project>
\ No newline at end of file
diff --git a/src/examples/ant/src/deb/control/control b/src/examples/ant/src/deb/control/control
new file mode 100644
index 0000000..6cfaa51
--- /dev/null
+++ b/src/examples/ant/src/deb/control/control
@@ -0,0 +1,7 @@
+Package: [[name]]
+Version: [[version]]
+Section: misc
+Priority: low
+Architecture: all
+Description: [[description]]
+Maintainer: tcurdt at vafer.org
diff --git a/src/examples/ant/src/main/java/org/vafer/jdeb/examples/Main.java b/src/examples/ant/src/main/java/org/vafer/jdeb/examples/Main.java
new file mode 100644
index 0000000..62a2434
--- /dev/null
+++ b/src/examples/ant/src/main/java/org/vafer/jdeb/examples/Main.java
@@ -0,0 +1,7 @@
+package org.vafer.jdeb.examples;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("jdeb example!");
+    }    
+}
diff --git a/src/examples/maven/CHANGES.txt b/src/examples/maven/CHANGES.txt
new file mode 100644
index 0000000..abd3035
--- /dev/null
+++ b/src/examples/maven/CHANGES.txt
@@ -0,0 +1,2 @@
+release date=20:13 15.12.2011,version=0.9,urgency=low,by=Torsten Curdt <torsten at youknowwhere.org>,distribution=unknown
+ * lots of fixes here
diff --git a/src/examples/maven/pom.xml b/src/examples/maven/pom.xml
new file mode 100644
index 0000000..9a61bc5
--- /dev/null
+++ b/src/examples/maven/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.vafer</groupId>
+    <artifactId>jdeb-example</artifactId>
+    <version>1.0</version>
+    <description>description from pom</description>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>jdeb</artifactId>
+                <groupId>org.vafer</groupId>
+                <version>1.0</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>jdeb</goal>
+                        </goals>
+                        <configuration>
+                            <verbose>true</verbose>
+                            <controlDir>${basedir}/src/deb/control</controlDir>
+                            <dataSet>
+
+                                <data>
+                                  <src>${project.build.directory}/${project.build.finalName}.jar</src>
+                                  <type>file</type>
+                                  <mapper>
+                                    <type>perm</type>
+                                    <prefix>/usr/share/jdeb/lib</prefix>
+                                    <user>loader</user>
+                                    <group>loader</group>
+                                  </mapper>
+                                </data>
+
+                                <data>
+                                  <type>link</type>
+                                  <symlink>true</symlink>
+                                  <linkName>/usr/share/java/jdeb.jar</linkName>
+                                  <linkTarget>/usr/share/jdeb/lib/${project.build.finalName}.jar</linkTarget>
+                                </data>
+
+                                <data>
+                                  <src>${basedir}/src/deb/init.d</src>
+                                  <type>directory</type>
+                                  <mapper>
+                                    <type>perm</type>
+                                    <prefix>/etc/init.d</prefix>
+                                    <user>loader</user>
+                                    <group>loader</group>
+                                  </mapper>
+                                </data>
+
+                                <data>
+                                  <type>template</type>
+                                  <paths>
+                                    <path>etc/${project.artifactId}</path>
+                                    <path>var/lib/${project.artifactId}</path>
+                                    <path>var/log/${project.artifactId}</path>
+                                    <path>var/run/${project.artifactId}</path>
+                                  </paths>
+                                  <mapper>
+                                    <type>perm</type>
+                                    <user>loader</user>
+                                    <group>loader</group>
+                                  </mapper>
+                                </data>
+
+                            </dataSet>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/examples/maven/src/deb/control/control b/src/examples/maven/src/deb/control/control
new file mode 100644
index 0000000..6cfaa51
--- /dev/null
+++ b/src/examples/maven/src/deb/control/control
@@ -0,0 +1,7 @@
+Package: [[name]]
+Version: [[version]]
+Section: misc
+Priority: low
+Architecture: all
+Description: [[description]]
+Maintainer: tcurdt at vafer.org
diff --git a/src/examples/maven/src/deb/control/postinst b/src/examples/maven/src/deb/control/postinst
new file mode 100644
index 0000000..1a24852
--- /dev/null
+++ b/src/examples/maven/src/deb/control/postinst
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/src/examples/maven/src/deb/control/postrm b/src/examples/maven/src/deb/control/postrm
new file mode 100644
index 0000000..1a24852
--- /dev/null
+++ b/src/examples/maven/src/deb/control/postrm
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/src/examples/maven/src/deb/control/preinst b/src/examples/maven/src/deb/control/preinst
new file mode 100644
index 0000000..1a24852
--- /dev/null
+++ b/src/examples/maven/src/deb/control/preinst
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/src/examples/maven/src/deb/control/prerm b/src/examples/maven/src/deb/control/prerm
new file mode 100644
index 0000000..1a24852
--- /dev/null
+++ b/src/examples/maven/src/deb/control/prerm
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/src/examples/maven/src/deb/init.d/myservice b/src/examples/maven/src/deb/init.d/myservice
new file mode 100644
index 0000000..e69de29
diff --git a/src/examples/maven/src/main/java/org/vafer/jdeb/examples/Main.java b/src/examples/maven/src/main/java/org/vafer/jdeb/examples/Main.java
new file mode 100644
index 0000000..ab2399c
--- /dev/null
+++ b/src/examples/maven/src/main/java/org/vafer/jdeb/examples/Main.java
@@ -0,0 +1,7 @@
+package org.vafer.jdeb.examples;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("jdeb example!");
+    }
+}
diff --git a/src/it/no-datasets/pom.xml b/src/it/no-datasets/pom.xml
new file mode 100644
index 0000000..66400ef
--- /dev/null
+++ b/src/it/no-datasets/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.vafer</groupId>
+    <artifactId>jdeb-it</artifactId>
+    <version>1.0</version>
+    <description>description from pom</description>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.vafer</groupId>
+                <artifactId>jdeb</artifactId>
+                <version>@project.version@</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>jdeb</goal>
+                        </goals>
+                        <configuration>
+                            <verbose>true</verbose>
+                            <controlDir>${basedir}/src/deb/control</controlDir>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/it/no-datasets/src/deb/control/control b/src/it/no-datasets/src/deb/control/control
new file mode 100644
index 0000000..6cfaa51
--- /dev/null
+++ b/src/it/no-datasets/src/deb/control/control
@@ -0,0 +1,7 @@
+Package: [[name]]
+Version: [[version]]
+Section: misc
+Priority: low
+Architecture: all
+Description: [[description]]
+Maintainer: tcurdt at vafer.org
diff --git a/src/it/no-datasets/src/main/java/org/vafer/jdeb/examples/Main.java b/src/it/no-datasets/src/main/java/org/vafer/jdeb/examples/Main.java
new file mode 100644
index 0000000..62a2434
--- /dev/null
+++ b/src/it/no-datasets/src/main/java/org/vafer/jdeb/examples/Main.java
@@ -0,0 +1,7 @@
+package org.vafer.jdeb.examples;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("jdeb example!");
+    }    
+}
diff --git a/src/it/no-datasets/verify.groovy b/src/it/no-datasets/verify.groovy
new file mode 100644
index 0000000..d2980bc
--- /dev/null
+++ b/src/it/no-datasets/verify.groovy
@@ -0,0 +1 @@
+assert new File( basedir, 'target/jdeb-it_1.0_all.deb' ).exists();
diff --git a/src/it/pom-only/pom.xml b/src/it/pom-only/pom.xml
new file mode 100644
index 0000000..6a0cc16
--- /dev/null
+++ b/src/it/pom-only/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.vafer</groupId>
+    <artifactId>jdeb-it</artifactId>
+    <version>1.0</version>
+    <packaging>pom</packaging>
+    <description>description from pom</description>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.vafer</groupId>
+                <artifactId>jdeb</artifactId>
+                <version>@project.version@</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>jdeb</goal>
+                        </goals>
+                        <configuration>
+                            <verbose>true</verbose>
+                            <controlDir>${basedir}/src/deb/control</controlDir>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/it/pom-only/src/deb/control/control b/src/it/pom-only/src/deb/control/control
new file mode 100644
index 0000000..6cfaa51
--- /dev/null
+++ b/src/it/pom-only/src/deb/control/control
@@ -0,0 +1,7 @@
+Package: [[name]]
+Version: [[version]]
+Section: misc
+Priority: low
+Architecture: all
+Description: [[description]]
+Maintainer: tcurdt at vafer.org
diff --git a/src/it/pom-only/verify.groovy b/src/it/pom-only/verify.groovy
new file mode 100644
index 0000000..d2980bc
--- /dev/null
+++ b/src/it/pom-only/verify.groovy
@@ -0,0 +1 @@
+assert new File( basedir, 'target/jdeb-it_1.0_all.deb' ).exists();
diff --git a/src/it/settings.xml b/src/it/settings.xml
new file mode 100644
index 0000000..2d90068
--- /dev/null
+++ b/src/it/settings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<settings>
+    <profiles>
+        <profile>
+            <id>it-repo</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>local.central</id>
+                    <url>@localRepositoryUrl@</url>
+                    <releases>
+                        <enabled>true</enabled>
+                    </releases>
+                    <snapshots>
+                        <enabled>true</enabled>
+                    </snapshots>
+                </repository>
+            </repositories>
+            <pluginRepositories>
+                <pluginRepository>
+                    <id>local.central</id>
+                    <url>@localRepositoryUrl@</url>
+                    <releases>
+                        <enabled>true</enabled>
+                    </releases>
+                    <snapshots>
+                        <enabled>true</enabled>
+                    </snapshots>
+                </pluginRepository>
+            </pluginRepositories>
+        </profile>
+    </profiles>
+</settings>
diff --git a/src/main/java/org/vafer/jdeb/ChangesFileBuilder.java b/src/main/java/org/vafer/jdeb/ChangesFileBuilder.java
new file mode 100644
index 0000000..5da3367
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ChangesFileBuilder.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.output.NullOutputStream;
+import org.vafer.jdeb.changes.ChangesProvider;
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+import org.vafer.jdeb.debian.ChangesFile;
+import org.vafer.jdeb.utils.InformationOutputStream;
+
+/**
+ * Builds the Debian changes file.
+ */
+class ChangesFileBuilder {
+
+    public ChangesFile createChanges(BinaryPackageControlFile packageControlFile, File binaryPackage, ChangesProvider changesProvider) throws IOException, PackagingException {
+
+        ChangesFile changesFile = new ChangesFile();
+        changesFile.setChanges(changesProvider.getChangesSets());
+        changesFile.initialize(packageControlFile);
+
+        changesFile.set("Date", ChangesFile.DATE_FORMAT.format(new Date()));
+        
+        try {
+            // compute the checksums of the binary package
+            InformationOutputStream md5output = new InformationOutputStream(new NullOutputStream(), MessageDigest.getInstance("MD5"));
+            InformationOutputStream sha1output = new InformationOutputStream(md5output, MessageDigest.getInstance("SHA1"));
+            InformationOutputStream sha256output = new InformationOutputStream(sha1output, MessageDigest.getInstance("SHA-256"));
+            
+            FileUtils.copyFile(binaryPackage, sha256output);
+            
+            // Checksums-Sha1:
+            //  56ef4c6249dc3567fd2967f809c42d1f9b61adf7 45964 jdeb.deb
+            changesFile.set("Checksums-Sha1", sha1output.getHexDigest() + " " + binaryPackage.length() + " " + binaryPackage.getName());
+            
+            // Checksums-Sha256:
+            //  38c6fa274eb9299a69b739bcbdbd05c7ffd1d8d6472f4245ed732a25c0e5d616 45964 jdeb.deb
+            changesFile.set("Checksums-Sha256", sha256output.getHexDigest() + " " + binaryPackage.length() + " " + binaryPackage.getName());
+            
+            StringBuilder files = new StringBuilder(md5output.getHexDigest());
+            files.append(' ').append(binaryPackage.length());
+            files.append(' ').append(packageControlFile.get("Section"));
+            files.append(' ').append(packageControlFile.get("Priority"));
+            files.append(' ').append(binaryPackage.getName());
+            changesFile.set("Files", files.toString());
+            
+        } catch (NoSuchAlgorithmException e) {
+            throw new PackagingException("Unable to compute the checksums for " + binaryPackage, e);
+        }
+        
+        if (!changesFile.isValid()) {
+            throw new PackagingException("Changes file fields are invalid " + changesFile.invalidFields() +
+                ". The following fields are mandatory: " + changesFile.getMandatoryFields() +
+                ". Please check your pom.xml/build.xml and your control file.");
+        }
+        
+        return changesFile;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/Compression.java b/src/main/java/org/vafer/jdeb/Compression.java
new file mode 100644
index 0000000..5ab1a6c
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/Compression.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.OutputStream;
+
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+
+/**
+ * Compression method used for the data file.
+ * 
+ * @author Emmanuel Bourg
+ */
+public enum Compression {
+
+    NONE(""),
+    GZIP(".gz"),
+    BZIP2(".bz2"),
+    XZ(".xz");
+
+    private String extension;
+
+    private Compression(String extension) {
+        this.extension = extension;
+    }
+
+    /**
+     * Returns the extension of the compression method
+     */
+    public String getExtension() {
+        return extension;
+    }
+
+    public OutputStream toCompressedOutputStream(OutputStream out) throws CompressorException {
+        switch (this) {
+            case GZIP:
+                return new CompressorStreamFactory().createCompressorOutputStream("gz", out);
+            case BZIP2:
+                return new CompressorStreamFactory().createCompressorOutputStream("bzip2", out);
+            case XZ:
+                return new CompressorStreamFactory().createCompressorOutputStream("xz", out);
+            default:
+                return out;
+        }
+    }
+
+    /**
+     * Returns the compression method corresponding to the specified name.
+     * The matching is case insensitive.
+     * 
+     * @param name the name of the compression method
+     * @return the compression method, or null if not recognized
+     */
+    public static Compression toEnum(String name) {
+        if ("gzip".equalsIgnoreCase(name) || "gz".equalsIgnoreCase(name)) {
+            return GZIP;
+        } else if ("bzip2".equalsIgnoreCase(name) || "bz2".equalsIgnoreCase(name)) {
+            return BZIP2;
+        } else if ("xz".equalsIgnoreCase(name)) {
+            return XZ;
+        } else if ("none".equalsIgnoreCase(name)) {
+            return NONE;
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/Console.java b/src/main/java/org/vafer/jdeb/Console.java
new file mode 100644
index 0000000..b0b21cf
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/Console.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb;
+
+/**
+ * Plug in your favorite log implementation.
+ *
+ * @author Torsten Curdt
+ */
+public interface Console {
+
+    void info( String s );
+
+    void warn( String s );
+
+}
diff --git a/src/main/java/org/vafer/jdeb/ControlBuilder.java b/src/main/java/org/vafer/jdeb/ControlBuilder.java
new file mode 100644
index 0000000..fdb4e7e
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ControlBuilder.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.NullOutputStream;
+import org.apache.tools.ant.DirectoryScanner;
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+import org.vafer.jdeb.mapping.PermMapper;
+import org.vafer.jdeb.utils.FilteredFile;
+import org.vafer.jdeb.utils.InformationInputStream;
+import org.vafer.jdeb.utils.Utils;
+import org.vafer.jdeb.utils.VariableResolver;
+
+/**
+ * Builds the control archive of the Debian package.
+ */
+class ControlBuilder {
+    
+    /** The name of the package maintainer scripts */
+    private static final Set<String> MAINTAINER_SCRIPTS = new HashSet<String>(Arrays.asList("preinst", "postinst", "prerm", "postrm", "config"));
+
+    /** The name of the other control files subject to token substitution */
+    private static final Set<String> CONFIGURATION_FILENAMES = new HashSet<String>(Arrays.asList("conffiles", "templates", "triggers"));
+
+    private Console console;
+    private VariableResolver resolver;
+
+    ControlBuilder(Console console, VariableResolver resolver) {
+        this.console = console;
+        this.resolver = resolver;
+    }
+
+    /**
+     * Build control archive of the deb
+     *
+     * @param packageControlFile the package control file
+     * @param controlFiles the other control information files (maintainer scripts, etc)
+     * @param dataSize  the size of the installed package
+     * @param checksums the md5 checksums of the files in the data archive
+     * @param output
+     * @return
+     * @throws java.io.FileNotFoundException
+     * @throws java.io.IOException
+     * @throws java.text.ParseException
+     */
+    void buildControl(BinaryPackageControlFile packageControlFile, File[] controlFiles, StringBuilder checksums, File output) throws IOException, ParseException {
+        final File dir = output.getParentFile();
+        if (dir != null && (!dir.exists() || !dir.isDirectory())) {
+            throw new IOException("Cannot write control file at '" + output.getAbsolutePath() + "'");
+        }
+
+        final TarArchiveOutputStream outputStream = new TarArchiveOutputStream(new GZIPOutputStream(new FileOutputStream(output)));
+        outputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
+        
+        // create the final package control file out of the "control" file, copy all other files, ignore the directories
+        for (File file : controlFiles) {
+            if (file.isDirectory()) {
+                // warn about the misplaced directory, except for directories ignored by default (.svn, cvs, etc)
+                if (!isDefaultExcludes(file)) {
+                    console.info("Found directory '" + file + "' in the control directory. Maybe you are pointing to wrong dir?");
+                }
+                continue;
+            }
+
+            if (CONFIGURATION_FILENAMES.contains(file.getName()) || MAINTAINER_SCRIPTS.contains(file.getName())) {
+                FilteredFile configurationFile = new FilteredFile(new FileInputStream(file), resolver);
+                addControlEntry(file.getName(), configurationFile.toString(), outputStream);
+
+            } else if (!"control".equals(file.getName())) {
+                // initialize the information stream to guess the type of the file
+                InformationInputStream infoStream = new InformationInputStream(new FileInputStream(file));
+                Utils.copy(infoStream, NullOutputStream.NULL_OUTPUT_STREAM);
+                infoStream.close();
+
+                // fix line endings for shell scripts
+                InputStream in = new FileInputStream(file);
+                if (infoStream.isShell() && !infoStream.hasUnixLineEndings()) {
+                    byte[] buf = Utils.toUnixLineEndings(in);
+                    in = new ByteArrayInputStream(buf);
+                }
+                
+                addControlEntry(file.getName(), IOUtils.toString(in), outputStream);
+                
+                in.close();
+            }
+        }
+
+        if (packageControlFile == null) {
+            throw new FileNotFoundException("No 'control' file found in " + Arrays.toString(controlFiles));
+        }
+        
+        addControlEntry("control", packageControlFile.toString(), outputStream);
+        addControlEntry("md5sums", checksums.toString(), outputStream);
+
+        outputStream.close();
+    }
+    
+    
+    /**
+     * Creates a package control file from the specified file and adds the
+     * <tt>Date</tt>, <tt>Distribution</tt> and <tt>Urgency</tt> fields if missing.
+     * The <tt>Installed-Size</tt> field is also initialized to the actual size of
+     * the package. The <tt>Maintainer</tt> field is overridden by the <tt>DEBEMAIL</tt>
+     * and <tt>DEBFULLNAME</tt> environment variables if defined.
+     * 
+     * @param file       the control file
+     * @param pDataSize  the size of the installed package
+     */
+    public BinaryPackageControlFile createPackageControlFile(File file, BigInteger pDataSize) throws IOException, ParseException {
+        FilteredFile controlFile = new FilteredFile(new FileInputStream(file), resolver);
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile(controlFile.toString());
+        
+        if (packageControlFile.get("Distribution") == null) {
+            packageControlFile.set("Distribution", "unknown");
+        }
+
+        if (packageControlFile.get("Urgency") == null) {
+            packageControlFile.set("Urgency", "low");
+        }
+
+        packageControlFile.set("Installed-Size", pDataSize.divide(BigInteger.valueOf(1024)).toString());
+
+        // override the Version if the DEBVERSION environment variable is defined
+        final String debVersion = System.getenv("DEBVERSION");
+        if (debVersion != null) {
+            packageControlFile.set("Version", debVersion);
+            console.info("Using version'" + debVersion + "' from the environment variables.");
+        }
+
+
+        // override the Maintainer field if the DEBFULLNAME and DEBEMAIL environment variables are defined
+        final String debFullName = System.getenv("DEBFULLNAME");
+        final String debEmail = System.getenv("DEBEMAIL");
+
+        if (debFullName != null && debEmail != null) {
+            final String maintainer = debFullName + " <" + debEmail + ">";
+            packageControlFile.set("Maintainer", maintainer);
+            console.info("Using maintainer '" + maintainer + "' from the environment variables.");
+        }
+        
+        return packageControlFile;
+    }
+
+
+    private static void addControlEntry(final String pName, final String pContent, final TarArchiveOutputStream pOutput) throws IOException {
+        final byte[] data = pContent.getBytes("UTF-8");
+
+        final TarArchiveEntry entry = new TarArchiveEntry("./" + pName, true);
+        entry.setSize(data.length);
+        entry.setNames("root", "root");
+        
+        if (MAINTAINER_SCRIPTS.contains(pName)) {
+            entry.setMode(PermMapper.toMode("755"));
+        } else {
+            entry.setMode(PermMapper.toMode("644"));
+        }
+        
+        pOutput.putArchiveEntry(entry);
+        pOutput.write(data);
+        pOutput.closeArchiveEntry();
+    }
+    
+    /**
+     * Tells if the specified directory is ignored by default (.svn, cvs, etc)
+     * 
+     * @param directory
+     */
+    private boolean isDefaultExcludes(File directory) {
+        for (String pattern : DirectoryScanner.getDefaultExcludes()) {
+            if (DirectoryScanner.match(pattern, directory.getAbsolutePath().replace("\\", "/"))) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/DataBuilder.java b/src/main/java/org/vafer/jdeb/DataBuilder.java
new file mode 100644
index 0000000..4768fe4
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/DataBuilder.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.compress.archivers.tar.TarConstants;
+import org.apache.commons.compress.archivers.zip.ZipEncoding;
+import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
+import org.apache.commons.compress.compressors.CompressorException;
+import org.vafer.jdeb.utils.Utils;
+
+/**
+ * Builds the data archive of the Debian package.
+ */
+class DataBuilder {
+
+    private Console console;
+    
+    private ZipEncoding encoding;
+    
+    private static final class Total {
+        private BigInteger count = BigInteger.valueOf(0);
+
+        public void add( long size ) {
+            count = count.add(BigInteger.valueOf(size));
+        }
+
+        public String toString() {
+            return "" + count;
+        }
+    }
+
+    DataBuilder(Console console) {
+        this.console = console;
+        this.encoding = ZipEncodingHelper.getZipEncoding(null);
+    }
+
+    private void checkField(String name, int length) throws IOException {
+        if (name != null) {
+            ByteBuffer b = encoding.encode(name);
+            if (b.limit() > length) {
+                throw new IllegalArgumentException("Field '" + name + "' too long, maximum is " + length);
+            }
+        }
+    }
+
+    /**
+     * Build the data archive of the deb from the provided DataProducers
+     *
+     * @param producers
+     * @param output
+     * @param checksums
+     * @param compression the compression method used for the data file
+     * @return
+     * @throws java.security.NoSuchAlgorithmException
+     * @throws java.io.IOException
+     * @throws org.apache.commons.compress.compressors.CompressorException
+     */
+    BigInteger buildData(Collection<DataProducer> producers, File output, final StringBuilder checksums, Compression compression) throws NoSuchAlgorithmException, IOException, CompressorException {
+
+        final File dir = output.getParentFile();
+        if (dir != null && (!dir.exists() || !dir.isDirectory())) {
+            throw new IOException("Cannot write data file at '" + output.getAbsolutePath() + "'");
+        }
+
+        final TarArchiveOutputStream tarOutputStream = new TarArchiveOutputStream(compression.toCompressedOutputStream(new FileOutputStream(output)));
+        tarOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
+
+        final MessageDigest digest = MessageDigest.getInstance("MD5");
+
+        final Total dataSize = new Total();
+
+        final List<String> addedDirectories = new ArrayList<String>();
+        final DataConsumer receiver = new DataConsumer() {
+            public void onEachDir( String dirname, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
+                // Check link name
+                checkField(linkname, TarConstants.NAMELEN);
+                // Check user name
+                checkField(user, TarConstants.UNAMELEN);
+                // Check group name
+                checkField(group, TarConstants.GNAMELEN);
+
+                dirname = fixPath(dirname);
+
+                createParentDirectories(dirname, user, uid, group, gid);
+
+                // The directory passed in explicitly by the caller also gets the passed-in mode.  (Unlike
+                // the parent directories for now.  See related comments at "int mode =" in
+                // createParentDirectories, including about a possible bug.)
+                createDirectory(dirname, user, uid, group, gid, mode, 0);
+
+                console.info("dir: " + dirname);
+            }
+
+            public void onEachFile( InputStream inputStream, String filename, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
+                // Check link name
+                checkField(linkname, TarConstants.NAMELEN);
+                // Check user name
+                checkField(user, TarConstants.UNAMELEN);
+                // Check group name
+                checkField(group, TarConstants.GNAMELEN);
+
+                filename = fixPath(filename);
+
+                createParentDirectories(filename, user, uid, group, gid);
+
+                final TarArchiveEntry entry = new TarArchiveEntry(filename, true);
+
+                entry.setUserName(user);
+                entry.setUserId(uid);
+                entry.setGroupName(group);
+                entry.setGroupId(gid);
+                entry.setMode(mode);
+                entry.setSize(size);
+
+                tarOutputStream.putArchiveEntry(entry);
+
+                dataSize.add(size);
+                digest.reset();
+
+                Utils.copy(inputStream, new DigestOutputStream(tarOutputStream, digest));
+
+                final String md5 = Utils.toHex(digest.digest());
+
+                tarOutputStream.closeArchiveEntry();
+
+                console.info(
+                    "file:" + entry.getName() +
+                        " size:" + entry.getSize() +
+                        " mode:" + entry.getMode() +
+                        " linkname:" + entry.getLinkName() +
+                        " username:" + entry.getUserName() +
+                        " userid:" + entry.getUserId() +
+                        " groupname:" + entry.getGroupName() +
+                        " groupid:" + entry.getGroupId() +
+                        " modtime:" + entry.getModTime() +
+                        " md5: " + md5
+                );
+
+                // append to file md5 list
+                checksums.append(md5).append(" ").append(entry.getName()).append('\n');
+            }
+
+            public void onEachLink(String path, String linkname, boolean symlink, String user, int uid, String group, int gid, int mode) throws IOException {
+                // Check link name
+                checkField(linkname, TarConstants.NAMELEN);
+                // Check user name
+                checkField(user, TarConstants.UNAMELEN);
+                // Check group name
+                checkField(group, TarConstants.GNAMELEN);
+
+                path = fixPath(path);
+
+                createParentDirectories(path, user, uid, group, gid);
+
+                final TarArchiveEntry entry = new TarArchiveEntry(path, symlink ? TarArchiveEntry.LF_SYMLINK : TarArchiveEntry.LF_LINK);
+                entry.setLinkName(linkname);
+
+                entry.setUserName(user);
+                entry.setUserId(uid);
+                entry.setGroupName(group);
+                entry.setGroupId(gid);
+                entry.setMode(mode);
+
+                tarOutputStream.putArchiveEntry(entry);
+                tarOutputStream.closeArchiveEntry();
+
+                console.info(
+                    "link:" + entry.getName() +
+                    " mode:" + entry.getMode() +
+                    " linkname:" + entry.getLinkName() +
+                    " username:" + entry.getUserName() +
+                    " userid:" + entry.getUserId() +
+                    " groupname:" + entry.getGroupName() +
+                    " groupid:" + entry.getGroupId()
+                 );
+            }
+            
+
+            private void createDirectory( String directory, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
+                // All dirs should end with "/" when created, or the test DebAndTaskTestCase.testTarFileSet() thinks its a file
+                // and so thinks it has the wrong permission.
+                // This consistency also helps when checking if a directory already exists in addedDirectories.
+        
+                if (!directory.endsWith("/")) {
+                    directory += "/";
+                }
+        
+                if (!addedDirectories.contains(directory)) {
+                    TarArchiveEntry entry = new TarArchiveEntry(directory, true);
+                    entry.setUserName(user);
+                    entry.setUserId(uid);
+                    entry.setGroupName(group);
+                    entry.setGroupId(gid);
+                    entry.setMode(mode);
+                    entry.setSize(size);
+        
+                    tarOutputStream.putArchiveEntry(entry);
+                    tarOutputStream.closeArchiveEntry();
+                    addedDirectories.add(directory); // so addedDirectories consistently have "/" for finding duplicates.
+                }
+            }
+        
+            private void createParentDirectories( String filename, String user, int uid, String group, int gid ) throws IOException {
+                String dirname = fixPath(new File(filename).getParent());
+                
+                // Debian packages must have parent directories created
+                // before sub-directories or files can be installed.
+                // For example, if an entry of ./usr/lib/foo/bar existed
+                // in a .deb package, but the ./usr/lib/foo directory didn't
+                // exist, the package installation would fail.  The .deb must
+                // then have an entry for ./usr/lib/foo and then ./usr/lib/foo/bar
+        
+                if (dirname == null) {
+                    return;
+                }
+        
+                // The loop below will create entries for all parent directories
+                // to ensure that .deb packages will install correctly.
+                String[] pathParts = dirname.split("/");
+                String parentDir = "./";
+                for (int i = 1; i < pathParts.length; i++) {
+                    parentDir += pathParts[i] + "/";
+                    // Make it so the dirs can be traversed by users.
+                    // We could instead try something more granular, like setting the directory
+                    // permission to 'rx' for each of the 3 user/group/other read permissions
+                    // found on the file being added (ie, only if "other" has read
+                    // permission on the main node, then add o+rx permission on all the containing
+                    // directories, same w/ user & group), and then also we'd have to
+                    // check the parentDirs collection of those already added to
+                    // see if those permissions need to be similarly updated.  (Note, it hasn't
+                    // been demonstrated, but there might be a bug if a user specifically
+                    // requests a directory with certain permissions,
+                    // that has already been auto-created because it was a parent, and if so, go set
+                    // the user-requested mode on that directory instead of this automatic one.)
+                    // But for now, keeping it simple by making every dir a+rx.   Examples are:
+                    // drw-r----- fs/fs   # what you get with setMode(mode)
+                    // drwxr-xr-x fs/fs   # Usable. Too loose?
+                    int mode = TarArchiveEntry.DEFAULT_DIR_MODE;
+        
+                    createDirectory(parentDir, user, uid, group, gid, mode, 0);
+                }
+            }
+        };
+
+        try {
+            for (DataProducer data : producers) {
+                data.produce(receiver);
+            }
+        } finally {
+            tarOutputStream.close();
+        }
+
+        console.info("Total size: " + dataSize);
+
+        return dataSize.count;
+    }
+
+    private String fixPath( String path ) {
+        if (path == null || path.equals(".")) {
+            return path;
+        }
+        
+        // If we're receiving directory names from Windows, then we'll convert to use slash
+        // This does eliminate the ability to use of a backslash in a directory name on *NIX,
+        // but in practice, this is a non-issue
+        if (path.contains("\\")) {
+            path = path.replace('\\', '/');
+        }
+        // ensure the path is like : ./foo/bar
+        if (path.startsWith("/")) {
+            path = "." + path;
+        } else if (!path.startsWith("./")) {
+            path = "./" + path;
+        }
+        return path;
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/DataConsumer.java b/src/main/java/org/vafer/jdeb/DataConsumer.java
new file mode 100644
index 0000000..d108888
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/DataConsumer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A DataConsumer consumes Data produced from a producer.
+ *
+ * @author Torsten Curdt
+ */
+public interface DataConsumer {
+
+    void onEachDir( String dirname, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException;
+
+    void onEachFile( InputStream input, String filename, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException;
+
+    void onEachLink( String path, String linkName, boolean symlink, String user, int uid, String group, int gid, int mode) throws IOException;
+
+}
diff --git a/src/main/java/org/vafer/jdeb/DataProducer.java b/src/main/java/org/vafer/jdeb/DataProducer.java
new file mode 100644
index 0000000..4218bf2
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/DataProducer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb;
+
+import java.io.IOException;
+
+/**
+ * Provides Data to a DataConsumer.
+ *
+ * @author Torsten Curdt
+ */
+public interface DataProducer {
+
+    void produce( DataConsumer receiver ) throws IOException;
+
+}
diff --git a/src/main/java/org/vafer/jdeb/DebMaker.java b/src/main/java/org/vafer/jdeb/DebMaker.java
new file mode 100644
index 0000000..1254dc4
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/DebMaker.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
+import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream;
+import org.apache.commons.io.IOUtils;
+import org.vafer.jdeb.changes.ChangeSet;
+import org.vafer.jdeb.changes.ChangesProvider;
+import org.vafer.jdeb.changes.TextfileChangesProvider;
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+import org.vafer.jdeb.debian.ChangesFile;
+import org.vafer.jdeb.signing.PGPSigner;
+import org.vafer.jdeb.utils.Utils;
+import org.vafer.jdeb.utils.VariableResolver;
+
+/**
+ * A generic class for creating Debian archives. Even supports signed changes
+ * files.
+ *
+ * @author Torsten Curdt
+ * @author Bryan Sant
+ */
+public class DebMaker {
+
+    /** A console to output log message with */
+    private Console console;
+
+    /** The Debian package produced */
+    private File deb;
+
+    /** The directory containing the control files to build the package */
+    private File control;
+
+    /** The name of the package. Default value if not specified in the control file */
+    private String packageName;
+
+    /** The section of the package. Default value if not specified in the control file */
+    private String section = "java";
+
+    /** The dependencies of the package. Default value if not specified in the control file */
+    private String depends = "default-jre | java6-runtime";
+
+    /** The description of the package. Default value if not specified in the control file */
+    private String description;
+
+    /** The homepage of the application. Default value if not specified in the control file */
+    private String homepage;
+
+    /** The file containing the PGP keys */
+    private File keyring;
+
+    /** The key to use in the keyring */
+    private String key;
+
+    /** The passphrase for the key to sign the changes file */
+    private String passphrase;
+
+    /** The file to read the changes from */
+    private File changesIn;
+
+    /** The file where to write the changes to */
+    private File changesOut;
+
+    /** The file where to write the changes of the changes input to */
+    private File changesSave;
+
+    /** The compression method used for the data file (none, gzip, bzip2 or xz) */
+    private String compression = "gzip";
+
+    private VariableResolver variableResolver;
+
+    private final Collection<DataProducer> dataProducers = new ArrayList<DataProducer>();
+
+
+    public DebMaker(Console console, Collection<DataProducer> dataProducers) {
+        this.console = console;
+        this.dataProducers.addAll(dataProducers);
+    }
+
+    public void setDeb(File deb) {
+        this.deb = deb;
+    }
+
+    public void setControl(File control) {
+        this.control = control;
+    }
+
+    public void setPackage(String packageName) {
+        this.packageName = packageName;
+    }
+
+    public void setSection(String section) {
+        this.section = section;
+    }
+
+    public void setDepends(String depends) {
+        this.depends = depends;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setHomepage(String homepage) {
+        this.homepage = homepage;
+    }
+
+    public void setChangesIn(File changes) {
+        this.changesIn = changes;
+    }
+
+    public void setChangesOut(File changes) {
+        this.changesOut = changes;
+    }
+
+    public void setChangesSave(File changes) {
+        this.changesSave = changes;
+    }
+
+    public void setKeyring(File keyring) {
+        this.keyring = keyring;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public void setPassphrase(String passphrase) {
+        this.passphrase = passphrase;
+    }
+
+    public void setCompression(String compression) {
+        this.compression = compression;
+    }
+
+    public void setResolver(VariableResolver variableResolver) {
+        this.variableResolver = variableResolver;
+    }
+
+    private boolean isWritableFile(File file) {
+        return !file.exists() || file.isFile() && file.canWrite();
+    }
+
+    /**
+     * Validates the input parameters.
+     */
+    public void validate() throws PackagingException {
+        if (control == null || !control.isDirectory()) {
+            throw new PackagingException("The 'control' attribute doesn't point to a directory.");
+        }
+
+        if (changesIn != null) {
+
+            if (changesIn.exists() && (!changesIn.isFile() || !changesIn.canRead())) {
+                throw new PackagingException("The 'changesIn' setting needs to point to a readable file. " + changesIn + " was not found/readable.");
+            }
+            
+            if (changesOut != null && !isWritableFile(changesOut)) {
+                throw new PackagingException("Cannot write the output for 'changesOut' to " + changesOut);
+            }
+
+            if (changesSave != null && !isWritableFile(changesSave)) {
+                throw new PackagingException("Cannot write the output for 'changesSave' to " + changesSave);
+            }
+
+        } else {
+            if (changesOut != null || changesSave != null) {
+                throw new PackagingException("The 'changesOut' or 'changesSave' settings may only be used when there is a 'changesIn' specified.");
+            }
+        }
+
+        if (Compression.toEnum(compression) == null) {
+            throw new PackagingException("The compression method '" + compression + "' is not supported (expected 'none', 'gzip', 'bzip2' or 'xz')");
+        }
+
+        if (deb == null) {
+            throw new PackagingException("You need to specify where the deb file is supposed to be created.");
+        }
+        
+        if (dataProducers.size() == 0) {
+            throw new PackagingException("You need to provide at least one reference to a tgz or directory with data.");
+        }
+    }
+
+    public void makeDeb() throws PackagingException {
+        BinaryPackageControlFile packageControlFile;
+        try {
+            console.info("Creating debian package: " + deb);
+
+            packageControlFile = createDeb(Compression.toEnum(compression));
+
+        } catch (Exception e) {
+            throw new PackagingException("Failed to create debian package " + deb, e);
+        }
+        
+        makeChangesFiles(packageControlFile);
+    }
+
+    private void makeChangesFiles(final BinaryPackageControlFile packageControlFile) throws PackagingException {
+        if (changesOut == null) {
+            changesOut = new File(deb.getParentFile(), deb.getName().replace(".deb", ".changes"));
+        }
+        
+        ChangesProvider changesProvider;
+        FileOutputStream out = null;
+        
+        try {
+            console.info("Creating changes file: " + changesOut);
+            
+            out = new FileOutputStream(changesOut);
+            
+            if (changesIn != null && changesIn.exists()) {
+                // read the changes form a textfile provider
+                changesProvider = new TextfileChangesProvider(new FileInputStream(changesIn), packageControlFile);
+            } else {
+                // create an empty changelog
+                changesProvider = new ChangesProvider() {
+                    @Override
+                    public ChangeSet[] getChangesSets() {
+                        return new ChangeSet[] {
+                                new ChangeSet(packageControlFile.get("Package"), packageControlFile.get("Version"), new Date(),
+                                        "stable", "low", packageControlFile.get("Maintainer"), new String[0])
+                        };
+                    }
+                };
+            }
+            
+            ChangesFileBuilder builder = new ChangesFileBuilder();
+            ChangesFile changesFile = builder.createChanges(packageControlFile, deb, changesProvider);
+            
+            if (keyring != null && key != null && passphrase != null) {
+                console.info("Signing the changes file with the key " + key);
+                PGPSigner signer = new PGPSigner(new FileInputStream(keyring), key, passphrase);
+                signer.clearSign(changesFile.toString(), out);
+            } else {
+                out.write(changesFile.toString().getBytes("UTF-8"));
+            }
+            out.flush();
+
+        } catch (Exception e) {
+            throw new PackagingException("Failed to create the Debian changes file " + changesOut, e);
+        } finally {
+            IOUtils.closeQuietly(out);
+        }
+        
+        if (changesSave == null || !(changesProvider instanceof TextfileChangesProvider)) {
+            return;
+        }
+        
+        try {
+            console.info("Saving changes to file: " + changesSave);
+
+            ((TextfileChangesProvider) changesProvider).save(new FileOutputStream(changesSave));
+
+        } catch (Exception e) {
+            throw new PackagingException("Failed to save debian changes file " + changesSave, e);
+        }
+    }
+    
+    /**
+     * Create the debian archive with from the provided control files and data producers.
+     *
+     * @param pControlFiles
+     * @param pData
+     * @param deb
+     * @param compression   the compression method used for the data file
+     * @return BinaryPackageControlFile
+     * @throws PackagingException
+     */
+    public BinaryPackageControlFile createDeb(Compression compression) throws PackagingException {
+        File tempData = null;
+        File tempControl = null;
+
+        try {
+            tempData = File.createTempFile("deb", "data");
+            tempControl = File.createTempFile("deb", "control");
+
+            console.info("Building data");
+            DataBuilder dataBuilder = new DataBuilder(console);
+            StringBuilder md5s = new StringBuilder();
+            BigInteger size = dataBuilder.buildData(dataProducers, tempData, md5s, compression);
+            
+            console.info("Building control");
+            ControlBuilder controlBuilder = new ControlBuilder(console, variableResolver);
+            BinaryPackageControlFile packageControlFile = controlBuilder.createPackageControlFile(new File(control, "control"), size);
+            if (packageControlFile.get("Package") == null) {
+                packageControlFile.set("Package", packageName);
+            }
+            if (packageControlFile.get("Depends") == null) {
+                packageControlFile.set("Depends", depends);
+            }
+            if (packageControlFile.get("Section") == null) {
+                packageControlFile.set("Section", section);
+            }
+            if (packageControlFile.get("Description") == null) {
+                packageControlFile.set("Description", description);
+            }
+            if (packageControlFile.get("Homepage") == null) {
+                packageControlFile.set("Homepage", homepage);
+            }
+            
+            controlBuilder.buildControl(packageControlFile, control.listFiles(), md5s, tempControl);
+            
+            if (!packageControlFile.isValid()) {
+                throw new PackagingException("Control file fields are invalid " + packageControlFile.invalidFields() +
+                    ". The following fields are mandatory: " + packageControlFile.getMandatoryFields() +
+                    ". Please check your pom.xml/build.xml and your control file.");
+            }
+
+            deb.getParentFile().mkdirs();
+            
+            
+            ArArchiveOutputStream ar = new ArArchiveOutputStream(new FileOutputStream(deb));
+            
+            addTo(ar, "debian-binary", "2.0\n");
+            addTo(ar, "control.tar.gz", tempControl);
+            addTo(ar, "data.tar" + compression.getExtension(), tempData);
+
+            ar.close();
+            
+            return packageControlFile;
+
+        } catch (Exception e) {
+            throw new PackagingException("Could not create deb package", e);
+        } finally {
+            if (tempData != null) {
+                if (!tempData.delete()) {
+                    console.warn("Could not delete the temporary file " + tempData);
+                }
+            }
+            if (tempControl != null) {
+                if (!tempControl.delete()) {
+                    console.warn("Could not delete the temporary file " + tempControl);
+                }
+            }
+        }
+    }
+
+    private void addTo(ArArchiveOutputStream pOutput, String pName, String pContent) throws IOException {
+        final byte[] content = pContent.getBytes();
+        pOutput.putArchiveEntry(new ArArchiveEntry(pName, content.length));
+        pOutput.write(content);
+        pOutput.closeArchiveEntry();
+    }
+
+    private void addTo(ArArchiveOutputStream pOutput, String pName, File pContent) throws IOException {
+        pOutput.putArchiveEntry(new ArArchiveEntry(pName, pContent.length()));
+
+        final InputStream input = new FileInputStream(pContent);
+        try {
+            Utils.copy(input, pOutput);
+        } finally {
+            input.close();
+        }
+
+        pOutput.closeArchiveEntry();
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/PackagingException.java b/src/main/java/org/vafer/jdeb/PackagingException.java
new file mode 100644
index 0000000..5fddfd7
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/PackagingException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb;
+
+/**
+ * Something went wrong while building the package
+ *
+ * @author Torsten Curdt
+ */
+public final class PackagingException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public PackagingException() {
+        super();
+    }
+
+    public PackagingException( String message, Throwable cause ) {
+        super(message, cause);
+    }
+
+    public PackagingException( String message ) {
+        super(message);
+    }
+
+    public PackagingException( Throwable cause ) {
+        super(cause);
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/ant/Data.java b/src/main/java/org/vafer/jdeb/ant/Data.java
new file mode 100644
index 0000000..b32872f
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ant/Data.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.ant;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.PatternSet;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.producers.DataProducerArchive;
+import org.vafer.jdeb.producers.DataProducerDirectory;
+import org.vafer.jdeb.producers.DataProducerFile;
+
+/**
+ * Ant "data" element acting as a factory for DataProducers.
+ * So far Archive and Directory producers are supported.
+ * Both support the usual ant pattern set matching.
+ *
+ * @author Torsten Curdt
+ */
+public final class Data extends PatternSet implements DataProducer {
+
+    private final Collection<Mapper> mapperWrapper = new ArrayList<Mapper>();
+
+    private File src;
+
+    private String type;
+
+    private String destinationName;
+
+    public void setSrc(File src) {
+        this.src = src;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setDst(String destinationName) {
+        this.destinationName = destinationName;
+    }
+
+    public void addMapper(Mapper mapper) {
+        mapperWrapper.add(mapper);
+    }
+
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+
+        if (src == null || !src.exists()) {
+            throw new FileNotFoundException("Data source not found : " + src);
+        }
+
+        org.vafer.jdeb.mapping.Mapper[] mappers = new org.vafer.jdeb.mapping.Mapper[mapperWrapper.size()];
+        final Iterator<Mapper> it = mapperWrapper.iterator();
+        for (int i = 0; i < mappers.length; i++) {
+            mappers[i] = it.next().createMapper();
+        }
+
+        if ("file".equalsIgnoreCase(type)) {
+            new DataProducerFile(
+                src,
+                destinationName,
+                getIncludePatterns(getProject()),
+                getExcludePatterns(getProject()),
+                mappers
+            ).produce(pReceiver);
+
+        } else if ("archive".equalsIgnoreCase(type)) {
+            new DataProducerArchive(
+                src,
+                getIncludePatterns(getProject()),
+                getExcludePatterns(getProject()),
+                mappers
+            ).produce(pReceiver);
+
+        } else if ("directory".equalsIgnoreCase(type)) {
+            new DataProducerDirectory(
+                src,
+                getIncludePatterns(getProject()),
+                getExcludePatterns(getProject()),
+                mappers
+            ).produce(pReceiver);
+        }
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/ant/DebAntTask.java b/src/main/java/org/vafer/jdeb/ant/DebAntTask.java
new file mode 100644
index 0000000..bf8ea43
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ant/DebAntTask.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.ant;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.Tar;
+import org.apache.tools.ant.types.FileSet;
+import org.vafer.jdeb.Console;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.DebMaker;
+import org.vafer.jdeb.PackagingException;
+import org.vafer.jdeb.producers.DataProducerFileSet;
+
+/**
+ * AntTask for creating debian archives.
+ *
+ * @author Torsten Curdt
+ */
+public class DebAntTask extends MatchingTask {
+
+    /** The Debian package produced */
+    private File deb;
+
+    /** The directory containing the control files to build the package */
+    private File control;
+
+    /** The file containing the PGP keys */
+    private File keyring;
+
+    /** The key to use in the keyring */
+    private String key;
+
+    /** The passphrase for the key to sign the changes file */
+    private String passphrase;
+
+    /** The file to read the changes from */
+    private File changesIn;
+
+    /** The file where to write the changes to */
+    private File changesOut;
+
+    /** The file where to write the changes of the changes input to */
+    private File changesSave;
+
+    /** The compression method used for the data file (none, gzip, bzip2 or xz) */
+    private String compression = "gzip";
+
+    /** Trigger the verbose mode detailing all operations */
+    private boolean verbose;
+
+    private Collection<Link> links = new ArrayList<Link>();
+
+    private Collection<DataProducer> dataProducers = new ArrayList<DataProducer>();
+
+
+    public void setDestfile( File deb ) {
+        this.deb = deb;
+    }
+
+    public void setControl( File control ) {
+        this.control = control;
+    }
+
+    public void setChangesIn( File changes ) {
+        this.changesIn = changes;
+    }
+
+    public void setChangesOut( File changes ) {
+        this.changesOut = changes;
+    }
+
+    public void setChangesSave( File changes ) {
+        this.changesSave = changes;
+    }
+
+    public void setKeyring( File keyring ) {
+        this.keyring = keyring;
+    }
+
+    public void setKey( String key ) {
+        this.key = key;
+    }
+
+    public void setPassphrase( String passphrase ) {
+        this.passphrase = passphrase;
+    }
+
+    public void setCompression( String compression ) {
+        this.compression = compression;
+    }
+
+    public void setVerbose( boolean verbose ) {
+        this.verbose = verbose;
+    }
+
+    public void addFileSet( FileSet fileset ) {
+        dataProducers.add(new DataProducerFileSet(fileset));
+    }
+
+    public void addTarFileSet( Tar.TarFileSet fileset ) {
+        dataProducers.add(new DataProducerFileSet(fileset));
+    }
+
+    public void addData( Data data ) {
+        dataProducers.add(data);
+    }
+
+    public void addLink( Link link ) {
+        links.add(link);
+    }
+
+    public void execute() {
+        // add the data producers for the links
+        for (Link link : links) {
+            dataProducers.add(link.toDataProducer());
+        }
+        
+        // validate the type of the <data> elements
+        for (DataProducer dataProducer : dataProducers) {
+            if (dataProducer instanceof Data) {
+                Data data = (Data) dataProducer;
+                if (data.getType() == null) {
+                    throw new BuildException("The type of the data element wasn't specified (expected 'file', 'directory' or 'archive')");
+                } else if (!Arrays.asList("file", "directory", "archive").contains(data.getType().toLowerCase())) {
+                    throw new BuildException("The type '" + data.getType() + "' of the data element is unknown (expected 'file', 'directory' or 'archive')");
+                }
+            }
+        }
+        
+        Console console = new TaskConsole(this, verbose);
+        
+        DebMaker debMaker = new DebMaker(console, dataProducers);
+        debMaker.setDeb(deb);
+        debMaker.setControl(control);
+        debMaker.setChangesIn(changesIn);
+        debMaker.setChangesOut(changesOut);
+        debMaker.setChangesSave(changesSave);
+        debMaker.setKeyring(keyring);
+        debMaker.setKey(key);
+        debMaker.setPassphrase(passphrase);
+        debMaker.setCompression(compression);
+        
+        try {
+            debMaker.validate();
+            debMaker.makeDeb();
+            
+        } catch (PackagingException e) {
+            log("Failed to create the Debian package " + deb, e, Project.MSG_ERR);
+            throw new BuildException("Failed to create the Debian package " + deb, e);
+        }
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/ant/Link.java b/src/main/java/org/vafer/jdeb/ant/Link.java
new file mode 100644
index 0000000..7a7a928
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ant/Link.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.ant;
+
+import org.apache.commons.compress.archivers.zip.UnixStat;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.PermMapper;
+import org.vafer.jdeb.producers.DataProducerLink;
+
+/**
+ * Defines a symbolic or hard link.
+ * 
+ * @author Emmanuel Bourg
+ * @version $Revision$, $Date$
+ */
+public final class Link {
+
+    private String name;
+    private String target;
+    private boolean symbolic = true;
+    private String username = "root";
+    private String group = "root";
+    private int uid = 0;
+    private int gid = 0;
+    private int mode = UnixStat.LINK_FLAG | UnixStat.DEFAULT_LINK_PERM;
+
+    DataProducer toDataProducer() {
+        org.vafer.jdeb.mapping.Mapper mapper = new PermMapper(uid, gid, username, group, mode, mode, 0, null);
+        return new DataProducerLink(name, target, symbolic, null, null, new org.vafer.jdeb.mapping.Mapper[]{mapper});
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getTarget() {
+        return target;
+    }
+
+    public void setTarget(String target) {
+        this.target = target;
+    }
+
+    public boolean isSymbolic() {
+        return symbolic;
+    }
+
+    public void setSymbolic(boolean symbolic) {
+        this.symbolic = symbolic;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public int getUid() {
+        return uid;
+    }
+
+    public void setUid(int uid) {
+        this.uid = uid;
+    }
+
+    public int getGid() {
+        return gid;
+    }
+
+    public void setGid(int gid) {
+        this.gid = gid;
+    }
+
+    public int getMode() {
+        return mode;
+    }
+
+    public void setMode(String mode) {
+        this.mode = UnixStat.LINK_FLAG | Integer.parseInt(mode, 8);
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/ant/Mapper.java b/src/main/java/org/vafer/jdeb/ant/Mapper.java
new file mode 100644
index 0000000..e99dd1b
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ant/Mapper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.vafer.jdeb.mapping.LsMapper;
+import org.vafer.jdeb.mapping.PermMapper;
+
+/**
+ * Ant "mapper" element acting as factory for the entry mapper.
+ * Supported types: ls, perm
+ *
+ * @author Torsten Curdt
+ */
+public final class Mapper {
+
+    private String mapperType = "perm";
+    private File src;
+
+    private String prefix;
+    private int strip;
+    private int uid = -1;
+    private int gid = -1;
+    private String user;
+    private String group;
+    private int fileMode = -1;
+    private int dirMode = -1;
+
+    public void setType( final String pType ) {
+        mapperType = pType;
+    }
+
+    public void setSrc( final File pSrc ) {
+        src = pSrc;
+    }
+
+
+    public void setPrefix( final String pPrefix ) {
+        prefix = pPrefix;
+    }
+
+    public void setStrip( final int pStrip ) {
+        strip = pStrip;
+    }
+
+
+    public void setUid( final int pUid ) {
+        uid = pUid;
+    }
+
+    public void setGid( final int pGid ) {
+        gid = pGid;
+    }
+
+    public void setUser( final String pUser ) {
+        user = pUser;
+    }
+
+    public void setGroup( final String pGroup ) {
+        group = pGroup;
+    }
+
+    public void setFileMode( final int pFileMode ) {
+        fileMode = pFileMode;
+    }
+
+    public void setDirMode( int pDirMode ) {
+        dirMode = pDirMode;
+    }
+
+    public org.vafer.jdeb.mapping.Mapper createMapper() throws IOException {
+
+        if ("perm".equalsIgnoreCase(mapperType)) {
+            return new PermMapper(uid, gid, user, group, fileMode, dirMode, strip, prefix);
+        }
+
+        if ("ls".equalsIgnoreCase(mapperType)) {
+            try {
+                return new LsMapper(new FileInputStream(src));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        throw new IOException("Unknown mapper type '" + mapperType + "'");
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/ant/TaskConsole.java b/src/main/java/org/vafer/jdeb/ant/TaskConsole.java
new file mode 100644
index 0000000..066455f
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/ant/TaskConsole.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.ant;
+
+import org.apache.tools.ant.Task;
+import org.vafer.jdeb.Console;
+
+/**
+ * Console implementation for Ant tasks.
+ */
+class TaskConsole implements Console {
+
+    private final Task task;
+    private final boolean verbose;
+
+    public TaskConsole(Task task, boolean verbose) {
+        this.task = task;
+        this.verbose = verbose;
+    }
+
+    public void info(String message) {
+        if (verbose) {
+            task.log(message);
+        }
+    }
+
+    public void warn(String message) {
+        task.log(message);
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/changes/ChangeSet.java b/src/main/java/org/vafer/jdeb/changes/ChangeSet.java
new file mode 100644
index 0000000..4ebc3c5
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/changes/ChangeSet.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.changes;
+
+import java.util.Date;
+
+/**
+ * A ChangeSet basically reflect a release as defined in the changes file.
+ *
+ * <pre>
+ * package (version) distribution(s); urgency=urgency
+ *        [optional blank line(s), stripped]
+ *   * change details
+ *     more change details
+ *        [blank line(s), included in output of dpkg-parsechangelog]
+ *   * even more change details
+ *        [optional blank line(s), stripped]
+ *  -- maintainer name <email address>[two spaces]  date
+ * </pre>
+ * 
+ * @author Torsten Curdt
+ * @see <a href="http://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog">Debian Policy Manual - Debian changelog</a>
+ */
+public final class ChangeSet {
+
+    private final String packageName;
+    private final String version;
+    private final Date date;
+    private final String distribution;
+    private final String urgency;
+    private final String changedBy;
+    private final String[] changes;
+
+    public ChangeSet(String packageName, String version, Date date, String distribution, String urgency, String changedBy, String[] changes) {
+        this.packageName = packageName;
+        this.version = version;
+        this.date = date;
+        this.distribution = distribution;
+        this.urgency = urgency;
+        this.changedBy = changedBy;
+        this.changes = changes;
+    }
+
+    public String getPackage() {
+        return packageName;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public String getDistribution() {
+        return distribution;
+    }
+
+    public String getUrgency() {
+        return urgency;
+    }
+
+    public String getChangedBy() {
+        return changedBy;
+    }
+
+    public String[] getChanges() {
+        return changes;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append(getTitle()).append('\n');
+        
+        if (changes.length > 0) {
+            sb.append("\n");
+        }
+        
+        for (String change : changes) {
+            sb.append("  * ").append(change).append('\n');
+        }
+
+        return sb.toString();
+    }
+
+    private String getTitle() {
+        return getPackage() + " (" + getVersion() + ") " + getDistribution() + "; urgency=" + getUrgency();
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/changes/ChangesProvider.java b/src/main/java/org/vafer/jdeb/changes/ChangesProvider.java
new file mode 100644
index 0000000..235ded8
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/changes/ChangesProvider.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.changes;
+
+public interface ChangesProvider {
+
+    ChangeSet[] getChangesSets();
+
+}
diff --git a/src/main/java/org/vafer/jdeb/changes/TextfileChangesProvider.java b/src/main/java/org/vafer/jdeb/changes/TextfileChangesProvider.java
new file mode 100644
index 0000000..ed97531
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/changes/TextfileChangesProvider.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.changes;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+
+/**
+ * Gets the changes from a changes file. The first entry are the current changes.
+ * The release line will be added. Example:
+ *
+ * release date=22:13 19.08.2007,version=1.5+r90114,urgency=low,by=Torsten Curdt <torsten at vafer.org>
+ *   * debian changes support
+ * release date=20:13 17.08.2007,version=1.4+r89114,urgency=low,by=Torsten Curdt <torsten at vafer.org>
+ *   * debian changes support
+ *
+ * @author Torsten Curdt
+ */
+public final class TextfileChangesProvider implements ChangesProvider {
+
+    private final ChangeSet[] changeSets;
+    
+    private DateFormat fmt = new SimpleDateFormat("HH:mm dd.MM.yyyy");
+
+    public TextfileChangesProvider( final InputStream pInput, final BinaryPackageControlFile packageControlFile ) throws IOException, ParseException {
+
+        final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput));
+        
+        String packageName = packageControlFile.get("Package");
+        String version = packageControlFile.get("Version");
+        Date date = new Date();
+        String distribution = packageControlFile.get("Distribution");
+        String urgency = packageControlFile.get("Urgency");
+        String changedBy = packageControlFile.get("Maintainer");
+        Collection<String> changesColl = new ArrayList<String>();
+        Collection<ChangeSet> changeSetColl = new ArrayList<ChangeSet>();
+
+
+        while (true) {
+            final String line = reader.readLine();
+            if (line == null) {
+                final String[] changes = changesColl.toArray(new String[changesColl.size()]);
+                final ChangeSet changeSet = new ChangeSet(packageName, version, date, distribution, urgency, changedBy, changes);
+                changeSetColl.add(changeSet);
+                break;
+            }
+
+            if (line.startsWith("release ")) {
+
+                if (changesColl.size() > 0) {
+                    final String[] changes = changesColl.toArray(new String[changesColl.size()]);
+                    final ChangeSet changeSet = new ChangeSet(packageName, version, date, distribution, urgency, changedBy, changes);
+                    changeSetColl.add(changeSet);
+                    changesColl.clear();
+                }
+
+                final String[] tokens = line.substring("release ".length()).split(",");
+                for (String token : tokens) {
+                    final String[] lr = token.trim().split("=");
+                    final String key = lr[0];
+                    final String value = lr[1];
+
+                    if ("urgency".equals(key)) {
+                        urgency = value;
+                    } else if ("by".equals(key)) {
+                        changedBy = value;
+                    } else if ("date".equals(key)) {
+                        date = fmt.parse(value);
+                    } else if ("version".equals(key)) {
+                        version = value;
+                    } else if ("distribution".equals(key)) {
+                        distribution = value;
+                    }
+                }
+                continue;
+            }
+
+            if (line.startsWith(" * ")) {
+                changesColl.add(line.substring(" * ".length()));
+                continue;
+            }
+
+            throw new ParseException("Unknown line syntax [" + line + "]", 0);
+        }
+
+        reader.close();
+
+        changeSets = changeSetColl.toArray(new ChangeSet[changeSetColl.size()]);
+    }
+
+    public void save(OutputStream pOutput) throws IOException {
+        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(pOutput));
+
+        for (ChangeSet changeSet : changeSets) {
+            writer.write("release ");
+            writer.write("date=" + fmt.format(changeSet.getDate()) + ",");
+            writer.write("version=" + changeSet.getVersion() + ",");
+            writer.write("urgency=" + changeSet.getUrgency() + ",");
+            writer.write("by=" + changeSet.getChangedBy() + ",");
+            writer.write("distribution=" + changeSet.getDistribution());
+            writer.write("\n");
+
+            for (String change : changeSet.getChanges()) {
+                writer.write(" * ");
+                writer.write(change);
+                writer.write("\n");
+            }
+        }
+
+        writer.close();
+    }
+
+    public ChangeSet[] getChangesSets() {
+        return changeSets;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/debian/BinaryPackageControlFile.java b/src/main/java/org/vafer/jdeb/debian/BinaryPackageControlFile.java
new file mode 100644
index 0000000..274b53d
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/debian/BinaryPackageControlFile.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+
+/**
+ * Binary package control file.
+ *
+ * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-binarycontrolfiles">Debian Policy Manual - Binary package control files</a>
+ * @author Torsten Curdt
+ */
+public final class BinaryPackageControlFile extends ControlFile {
+
+    private static final ControlField[] FIELDS = {
+            new ControlField("Package", true),
+            new ControlField("Source"),
+            new ControlField("Version", true),
+            new ControlField("Section", true),
+            new ControlField("Priority", true),
+            new ControlField("Architecture", true),
+            new ControlField("Essential"),
+            new ControlField("Depends"),
+            new ControlField("Pre-Depends"),
+            new ControlField("Recommends"),
+            new ControlField("Suggests"),
+            new ControlField("Breaks"),
+            new ControlField("Enhances"),
+            new ControlField("Conflicts"),
+            new ControlField("Provides"),
+            new ControlField("Replaces"),
+            new ControlField("Installed-Size"),
+            new ControlField("Maintainer", true),
+            new ControlField("Description", true, ControlField.Type.MULTILINE),
+            new ControlField("Homepage")
+    };
+
+    public BinaryPackageControlFile() {
+        set("Architecture", "all");
+        set("Priority", "optional");
+    }
+
+    public BinaryPackageControlFile(String input) throws IOException, ParseException {
+        parse(input);
+    }
+
+    public BinaryPackageControlFile(InputStream input) throws IOException, ParseException {
+        parse(input);
+    }
+
+    @Override
+    protected ControlField[] getFields() {
+        return FIELDS;
+    }
+
+    /**
+     * Returns the short description of the package. The short description
+     * consists in the first line of the Description field.
+     * 
+     * @return
+     */
+    public String getShortDescription() {
+        if (get("Description") == null) {
+            return null;
+        }
+        
+        return get("Description").split("\n")[0];
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/debian/ChangesFile.java b/src/main/java/org/vafer/jdeb/debian/ChangesFile.java
new file mode 100644
index 0000000..49bc6b9
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/debian/ChangesFile.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.vafer.jdeb.changes.ChangeSet;
+
+/**
+ * Reflecting a changes file
+ *
+ * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-debianchangesfiles">Debian Policy Manual - Debian changes files</a>
+ * @author Torsten Curdt
+ */
+public final class ChangesFile extends ControlFile {
+
+    public static final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH); // RFC 2822 format
+
+    private static final ControlField[] FIELDS = {
+            new ControlField("Format", true),
+            new ControlField("Date", true),
+            new ControlField("Source", true),
+            new ControlField("Binary", true),
+            new ControlField("Architecture", true),
+            new ControlField("Version", true),
+            new ControlField("Distribution", true),
+            new ControlField("Urgency", true),
+            new ControlField("Maintainer", true),
+            new ControlField("Changed-By"),
+            new ControlField("Description", true, ControlField.Type.MULTILINE, true),
+            new ControlField("Changes", true, ControlField.Type.MULTILINE, true),
+            new ControlField("Closes"),
+            new ControlField("Checksums-Sha1", true, ControlField.Type.MULTILINE, true),
+            new ControlField("Checksums-Sha256", true, ControlField.Type.MULTILINE, true),
+            new ControlField("Files", true, ControlField.Type.MULTILINE, true)
+    };
+
+    public ChangesFile() {
+        set("Format", "1.8");
+        set("Distribution", "stable");
+    }
+
+    /**
+     * Initializes the fields on the changes file with the values of the specified
+     * binary package control file.
+     * 
+     * @param packageControlFile
+     */
+    public void initialize(BinaryPackageControlFile packageControlFile) {
+        set("Binary",       packageControlFile.get("Package"));
+        set("Source",       packageControlFile.get("Package"));
+        set("Architecture", packageControlFile.get("Architecture"));
+        set("Version",      packageControlFile.get("Version"));
+        set("Maintainer",   packageControlFile.get("Maintainer"));
+        set("Changed-By",   packageControlFile.get("Maintainer"));
+        
+        StringBuilder description = new StringBuilder();
+        description.append(packageControlFile.get("Package"));
+        if (packageControlFile.get("Description") != null) {
+            description.append(" - ");
+            description.append(packageControlFile.getShortDescription());
+        }
+        set("Description",  description.toString());
+    }
+
+    public void setChanges(ChangeSet[] changeSets) {
+        StringBuilder sb = new StringBuilder();
+
+        if (changeSets.length > 0) {
+            final ChangeSet mostRecentChangeSet = changeSets[0];
+            set("Urgency", mostRecentChangeSet.getUrgency());
+            set("Changed-By", mostRecentChangeSet.getChangedBy());
+            
+            for (ChangeSet changeSet : changeSets) {
+                sb.append(changeSet.toString());
+            }
+        }
+
+        set("Changes", sb.toString());
+    }
+
+    @Override
+    protected ControlField[] getFields() {
+        return FIELDS;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/debian/ControlField.java b/src/main/java/org/vafer/jdeb/debian/ControlField.java
new file mode 100644
index 0000000..73e37ba
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/debian/ControlField.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+/**
+ * A field of a control file. This class is immutable.
+ *
+ * @author Emmanuel Bourg
+ */
+public class ControlField {
+
+    /**
+     * The format of a field.
+     */
+    public enum Type {
+        /** Value on a single line */
+        SIMPLE,
+        /** Value on multiple lines, space characters are ignored */
+        FOLDED,
+        /** Value on multiple lines, space characters are preserved */
+        MULTILINE
+    }
+
+    /** The name of the field */
+    private String name;
+
+    /** Tells if the field is mandatory */
+    private boolean mandatory;
+
+    /** The type of the field */
+    private Type type = Type.SIMPLE;
+
+    /** Tells is the first line of the field must be empty (for MULTILINE values only) */
+    private boolean firstLineEmpty;
+
+
+    public ControlField(String name) {
+        this(name, false);
+    }
+
+    public ControlField(String name, boolean mandatory) {
+        this(name, mandatory, Type.SIMPLE);
+    }
+
+    public ControlField(String name, boolean mandatory, Type type) {
+        this(name, mandatory, type, false);
+    }
+
+    public ControlField(String name, boolean mandatory, Type type, boolean firstLineEmpty) {
+        this.name = name;
+        this.mandatory = mandatory;
+        this.type = type;
+        this.firstLineEmpty = firstLineEmpty;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isMandatory() {
+        return mandatory;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public boolean isFirstLineEmpty() {
+        return firstLineEmpty;
+    }
+
+    /**
+     * Returns the field with the specified value properly formatted. Multiline
+     * values are automatically indented, and dots are added on the empty lines.
+     * 
+     * <pre>
+     * Field-Name: value
+     * </pre>
+     */
+    public String format(String value) {
+        StringBuilder s = new StringBuilder();
+        
+        if (value != null && value.trim().length() > 0) {
+            boolean continuationLine = false;
+            
+            s.append(getName()).append(":");
+            if (isFirstLineEmpty()) {
+                s.append("\n");
+                continuationLine = true;
+            }
+            
+            try {
+                BufferedReader reader = new BufferedReader(new StringReader(value));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    if (continuationLine && line.trim().length() == 0) {
+                        // put a dot on the empty continuation lines
+                        s.append(" .\n");
+                    } else {
+                        s.append(" ").append(line).append("\n");
+                    }
+
+                    continuationLine = true;
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        
+        return s.toString();
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/debian/ControlFile.java b/src/main/java/org/vafer/jdeb/debian/ControlFile.java
new file mode 100644
index 0000000..00c8b10
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/debian/ControlFile.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A control file as specified by the <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html">Debian policy</a>.
+ *
+ * @author Torsten Curdt
+ */
+public abstract class ControlFile {
+
+    protected final Map<String, String> values = new LinkedHashMap<String, String>();
+
+    public void parse(String input) throws IOException, ParseException {
+        parse(new ByteArrayInputStream(input.getBytes("UTF-8")));
+    }
+
+    public void parse(InputStream input) throws IOException, ParseException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
+        StringBuilder buffer = new StringBuilder();
+        String field = null;
+        int linenr = 0;
+        while (true) {
+            final String line = reader.readLine();
+
+            if (line == null) {
+                if (buffer.length() > 0) {
+                    // flush value of the previous field
+                    set(field, buffer.toString());
+                }
+                break;
+            }
+
+            linenr++;
+
+            if (line.length() == 0) {
+                throw new ParseException("Empty line", linenr);
+            }
+
+            final char first = line.charAt(0);
+            if (Character.isLetter(first)) {
+
+                // new field
+
+                if (buffer.length() > 0) {
+                    // flush value of the previous field
+                    set(field, buffer.toString());
+                    buffer = new StringBuilder();
+                }
+
+
+                final int i = line.indexOf(':');
+
+                if (i < 0) {
+                    throw new ParseException("Line misses ':' delimiter", linenr);
+                }
+
+                field = line.substring(0, i);
+                buffer.append(line.substring(i + 1).trim());
+
+                continue;
+            }
+
+            // continuing old value, lines with only a dot are ignored
+            buffer.append('\n');
+            if (!".".equals(line.substring(1).trim())) {
+                buffer.append(line.substring(1));
+            }
+        }
+        reader.close();
+
+    }
+
+    public void set(final String field, final String value) {
+        if (!"".equals(value)) {
+            values.put(field, value);
+        }
+    }
+
+    public String get(String field) {
+        return values.get(field);
+    }
+
+    protected abstract ControlField[] getFields();
+
+    public List<String> getMandatoryFields() {
+        List<String> fields = new ArrayList<String>();
+
+        for (ControlField field : getFields()) {
+            if (field.isMandatory()) {
+                fields.add(field.getName());
+            }
+        }
+        
+        return fields;
+    }
+
+    public boolean isValid() {
+        return invalidFields().size() == 0;
+    }
+
+    public Set<String> invalidFields() {
+        Set<String> invalid = new HashSet<String>();
+        
+        for (ControlField field : getFields()) {
+            if (field.isMandatory() && get(field.getName()) == null) {
+                invalid.add(field.getName());
+            }
+        }
+
+        return invalid;
+    }
+
+    public String toString(ControlField... fields) {
+        StringBuilder s = new StringBuilder();
+        for (ControlField field : fields) {
+            String value = values.get(field.getName());
+            s.append(field.format(value));
+        }
+        return s.toString();
+    }
+
+    public String toString() {
+        return toString(getFields());
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/mapping/LsMapper.java b/src/main/java/org/vafer/jdeb/mapping/LsMapper.java
new file mode 100644
index 0000000..fdf19c5
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/mapping/LsMapper.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.mapping;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+
+/**
+ * Reads permissions and ownerships from a "ls -laR > mapping.txt" dump and
+ * maps entries accordingly.
+ *
+ * @author Torsten Curdt
+ */
+public final class LsMapper implements Mapper {
+
+    private final Map<String, TarArchiveEntry> mapping;
+
+
+    public final static class ParseError extends Exception {
+
+        private static final long serialVersionUID = 1L;
+
+        public ParseError( String message ) {
+            super(message);
+        }
+    }
+
+
+    public LsMapper( final InputStream pInput ) throws IOException, ParseError {
+        mapping = parse(pInput);
+    }
+
+    /*
+./trunk/target/test-classes/org/vafer/dependency:
+total 176
+drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .
+drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..
+-rw-r--r--    1 tcurdt  tcurdt  2934 Jun 25 03:48 DependenciesTestCase.class
+-rw-r--r--    1 tcurdt  tcurdt   786 Jun 25 03:48 JarCombiningTestCase$1.class
+-rw-r--r--    1 tcurdt  tcurdt  2176 Jun 25 03:48 WarTestCase.class
+drwxr-xr-x    4 tcurdt  tcurdt   136 Jun 25 03:48 classes
+
+./trunk/target/test-classes/org/vafer/dependency/classes:
+     */
+
+    final private Pattern basePattern = Pattern.compile("^\\./(.*):$");
+    final private Pattern totalPattern = Pattern.compile("^total ([0-9]+)$");
+    final private Pattern dirPattern = Pattern.compile("^d([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+[\\.]{1,2}$");
+    final private Pattern filePattern = Pattern.compile("^([d-])([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+(.*)$");
+    final private Pattern newlinePattern = Pattern.compile("$");
+
+    private String readBase( final BufferedReader reader ) throws IOException, ParseError {
+        final String line = reader.readLine();
+        if (line == null) {
+            return null;
+        }
+        final Matcher matcher = basePattern.matcher(line);
+        if (!matcher.matches()) {
+            throw new ParseError("expected base line but got \"" + line + "\"");
+        }
+        return matcher.group(1);
+    }
+
+    private String readTotal( final BufferedReader reader ) throws IOException, ParseError {
+        final String line = reader.readLine();
+        final Matcher matcher = totalPattern.matcher(line);
+        if (!matcher.matches()) {
+            throw new ParseError("expected total line but got \"" + line + "\"");
+        }
+        return matcher.group(1);
+    }
+
+    private TarArchiveEntry readDir( final BufferedReader reader, final String base ) throws IOException, ParseError {
+        final String current = reader.readLine();
+        final Matcher currentMatcher = dirPattern.matcher(current);
+        if (!currentMatcher.matches()) {
+            throw new ParseError("expected dirline but got \"" + current + "\"");
+        }
+
+        final String parent = reader.readLine();
+        final Matcher parentMatcher = dirPattern.matcher(parent);
+        if (!parentMatcher.matches()) {
+            throw new ParseError("expected dirline but got \"" + parent + "\"");
+        }
+
+        final TarArchiveEntry entry = new TarArchiveEntry(base, true);
+
+        entry.setMode(convertModeFromString(currentMatcher.group(1)));
+        entry.setUserName(currentMatcher.group(3));
+        entry.setGroupName(currentMatcher.group(4));
+
+        return entry;
+    }
+
+
+    private int convertModeFromString( final String mode ) {
+
+        final char[] m = mode.toCharArray();
+        /*
+           -rwxrwxrwx
+
+           4000    set-user-ID-on-execution bit
+           2000    set-user-ID-on-execution bit
+           1000    sticky bit
+           0400    allow read by owner.
+           0200    allow write by owner.
+           0100    execute / search
+           0040    allow read by group members.
+           0020    allow write by group members.
+           0010    execute / search
+           0004    allow read by others.
+           0002    allow write by others.
+           0001    execute / search
+         */
+        // TODO: simplified - needs fixing
+        int sum = 0;
+        int bit = 1;
+        for (int i = m.length - 1; i >= 0; i--) {
+            if (m[i] != '-') {
+                sum += bit;
+            }
+            bit += bit;
+        }
+        return sum;
+    }
+
+    private TarArchiveEntry readFile( final BufferedReader reader, final String base ) throws IOException, ParseError {
+
+        while (true) {
+            final String line = reader.readLine();
+
+            if (line == null) {
+                return null;
+            }
+
+            final Matcher currentMatcher = filePattern.matcher(line);
+            if (!currentMatcher.matches()) {
+                final Matcher newlineMatcher = newlinePattern.matcher(line);
+                if (newlineMatcher.matches()) {
+                    return null;
+                }
+                throw new ParseError("expected file line but got \"" + line + "\"");
+            }
+
+            final String type = currentMatcher.group(1);
+            if (type.startsWith("-")) {
+                final TarArchiveEntry entry = new TarArchiveEntry(base + "/" + currentMatcher.group(8), true);
+
+                entry.setMode(convertModeFromString(currentMatcher.group(2)));
+                entry.setUserName(currentMatcher.group(4));
+                entry.setGroupName(currentMatcher.group(5));
+
+                return entry;
+            }
+        }
+
+    }
+
+    private Map<String, TarArchiveEntry> parse( final InputStream pInput ) throws IOException, ParseError {
+        final Map<String, TarArchiveEntry> mapping = new HashMap<String, TarArchiveEntry>();
+
+        final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput));
+
+        boolean first = true;
+        while (true) {
+
+            final String base;
+            if (first) {
+                base = "";
+                first = false;
+            } else {
+                base = readBase(reader);
+                if (base == null) {
+                    break;
+                }
+            }
+
+            readTotal(reader);
+            final TarArchiveEntry dir = readDir(reader, base);
+            mapping.put(dir.getName(), dir);
+
+            while (true) {
+                final TarArchiveEntry file = readFile(reader, base);
+
+                if (file == null) {
+                    break;
+                }
+
+                mapping.put(file.getName(), file);
+            }
+        }
+
+        return mapping;
+    }
+
+    public TarArchiveEntry map( final TarArchiveEntry pEntry ) {
+
+        final TarArchiveEntry entry = mapping.get(pEntry.getName());
+
+        if (entry != null) {
+
+            final TarArchiveEntry newEntry = new TarArchiveEntry(entry.getName(), true);
+            newEntry.setUserId(entry.getUserId());
+            newEntry.setGroupId(entry.getGroupId());
+            newEntry.setUserName(entry.getUserName());
+            newEntry.setGroupName(entry.getGroupName());
+            newEntry.setMode(entry.getMode());
+            newEntry.setSize(entry.getSize());
+
+            return newEntry;
+        }
+
+        return pEntry;
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/mapping/Mapper.java b/src/main/java/org/vafer/jdeb/mapping/Mapper.java
new file mode 100644
index 0000000..1229fda
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/mapping/Mapper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.mapping;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+
+
+/**
+ * Maps one entry to another. So you modify ownerships permissions etc in a Mapper.
+ *
+ * @author Torsten Curdt
+ */
+public interface Mapper {
+
+    public TarArchiveEntry map( final TarArchiveEntry entry );
+
+}
diff --git a/src/main/java/org/vafer/jdeb/mapping/NullMapper.java b/src/main/java/org/vafer/jdeb/mapping/NullMapper.java
new file mode 100644
index 0000000..de5b115
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/mapping/NullMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.mapping;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+
+public final class NullMapper implements Mapper {
+
+    public static final Mapper INSTANCE = new NullMapper();
+
+    private NullMapper() {
+    }
+
+    public TarArchiveEntry map( final TarArchiveEntry pEntry ) {
+        return pEntry;
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/mapping/PermMapper.java b/src/main/java/org/vafer/jdeb/mapping/PermMapper.java
new file mode 100644
index 0000000..f3c6701
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/mapping/PermMapper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.mapping;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.vafer.jdeb.utils.Utils;
+
+/**
+ * Applies a uniform set of permissions and ownership to all entries.
+ *
+ * @author Bryan Sant
+ */
+public final class PermMapper implements Mapper {
+
+    private final int strip;
+    private final String prefix;
+    private int uid = -1;
+    private int gid = -1;
+    private String user;
+    private String group;
+    private int fileMode = -1;
+    private int dirMode = -1;
+
+    public static int toMode( String modeString ) {
+        int mode = -1;
+        if (modeString != null && modeString.length() > 0) {
+            mode = Integer.parseInt(modeString, 8);
+        }
+        return mode;
+    }
+
+    public PermMapper( int uid, int gid, String user, String group, int fileMode, int dirMode, int strip, String prefix ) {
+        this.strip = strip;
+        this.prefix = (prefix == null) ? "" : prefix;
+        this.uid = uid;
+        this.gid = gid;
+        this.user = user;
+        this.group = group;
+        this.fileMode = fileMode;
+        this.dirMode = dirMode;
+    }
+
+    public PermMapper( int uid, int gid, String user, String group, String fileMode, String dirMode, int strip, String prefix ) {
+        this(uid, gid, user, group, toMode(fileMode), toMode(dirMode), strip, prefix);
+    }
+
+    public TarArchiveEntry map( final TarArchiveEntry entry ) {
+        final String name = entry.getName();
+
+        final TarArchiveEntry newEntry = new TarArchiveEntry(prefix + '/' + Utils.stripPath(strip, name), true);
+
+        // Set ownership
+        if (uid > -1) {
+            newEntry.setUserId(uid);
+        } else {
+            newEntry.setUserId(entry.getUserId());
+        }
+        if (gid > -1) {
+            newEntry.setGroupId(gid);
+        } else {
+            newEntry.setGroupId(entry.getGroupId());
+        }
+        if (user != null) {
+            newEntry.setUserName(user);
+        } else {
+            newEntry.setUserName(entry.getUserName());
+        }
+        if (group != null) {
+            newEntry.setGroupName(group);
+        } else {
+            newEntry.setGroupName(entry.getGroupName());
+        }
+
+        // Set permissions
+        if (newEntry.isDirectory()) {
+            if (dirMode > -1) {
+                newEntry.setMode(dirMode);
+            } else {
+                newEntry.setMode(entry.getMode());
+            }
+        } else {
+            if (fileMode > -1) {
+                newEntry.setMode(fileMode);
+            } else {
+                newEntry.setMode(entry.getMode());
+
+            }
+        }
+
+        newEntry.setSize(entry.getSize());
+
+        return newEntry;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/maven/AbstractPluginMojo.java b/src/main/java/org/vafer/jdeb/maven/AbstractPluginMojo.java
new file mode 100644
index 0000000..de3cf65
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/maven/AbstractPluginMojo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.maven;
+
+import java.io.File;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.project.MavenProject;
+
+public abstract class AbstractPluginMojo extends AbstractMojo {
+
+    /**
+     * @parameter expression="${project}"
+     * @required
+     * @readonly
+     */
+    private MavenProject project;
+
+    /**
+     * @parameter expression="${project.build.directory}"
+     * @required
+     * @readonly
+     */
+    protected File buildDirectory;
+
+    protected MavenProject getProject() {
+        if (project.getExecutionProject() != null) {
+            return project.getExecutionProject();
+        }
+
+        return project;
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/maven/Data.java b/src/main/java/org/vafer/jdeb/maven/Data.java
new file mode 100644
index 0000000..0f1dd41
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/maven/Data.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.maven;
+
+import static org.vafer.jdeb.maven.MissingSourceBehavior.FAIL;
+import static org.vafer.jdeb.maven.MissingSourceBehavior.IGNORE;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.producers.DataProducerArchive;
+import org.vafer.jdeb.producers.DataProducerDirectory;
+import org.vafer.jdeb.producers.DataProducerFile;
+import org.vafer.jdeb.producers.DataProducerLink;
+import org.vafer.jdeb.producers.DataProducerPathTemplate;
+
+/**
+ * Maven "data" element acting as a factory for DataProducers. So far Archive and
+ * Directory producers are supported. Both support the usual ant pattern set
+ * matching.
+ *
+ * @author Bryan Sant
+ */
+public final class Data implements DataProducer {
+
+    private File src;
+
+    /**
+     * @parameter expression="${src}"
+     */
+    public void setSrc( File src ) {
+        this.src = src;
+    }
+
+    private String dst;
+
+    /**
+     * @parameter expression="${dst}"
+     */
+    public void setDst( String dst ) {
+        this.dst = dst;
+    }
+
+    private String type;
+
+    /**
+     * @parameter expression="${type}"
+     */
+    public void setType( String type ) {
+        this.type = type;
+    }
+
+    private MissingSourceBehavior missingSrc = FAIL;
+
+    /**
+     * @parameter expression="${missingSrc}"
+     */
+    public void setMissingSrc( String missingSrc ) {
+        MissingSourceBehavior value = MissingSourceBehavior.valueOf(missingSrc.trim().toUpperCase());
+        if (value == null) {
+            throw new IllegalArgumentException("Unknown " + MissingSourceBehavior.class.getSimpleName() + ": " + missingSrc);
+        }
+        this.missingSrc = value;
+    }
+
+    private String linkName;
+
+    /**
+     * @parameter expression="${linkName}"
+     */
+    public void setLinkName(String linkName) {
+        this.linkName = linkName;
+    }
+
+    private String linkTarget;
+
+    /**
+     * @parameter expression="${linkTarget}"
+     */
+    public void setLinkTarget(String linkTarget) {
+        this.linkTarget = linkTarget;
+    }
+
+    private boolean symlink = true;
+
+    /**
+     * @parameter expression="${symlink}"
+     */
+    public void setSymlink(boolean symlink) {
+        this.symlink = symlink;
+    }
+
+    private String[] includePatterns;
+
+    /**
+     * @parameter expression="${includes}" alias="includes"
+     */
+    public void setIncludes( String includes ) {
+        includePatterns = splitPatterns(includes);
+    }
+
+    private String[] excludePatterns;
+
+    /**
+     * @parameter expression="${excludes}" alias="excludes"
+     */
+    public void setExcludes( String excludes ) {
+        excludePatterns = splitPatterns(excludes);
+    }
+
+    /**
+     * @parameter expression="${mapper}"
+     */
+    private Mapper mapper;
+
+    /**
+     * @parameter expression="${paths}"
+     */
+    private String[] paths;
+
+    /* For testing only */
+    void setPaths( String[] paths ) {
+        this.paths = paths;
+    }
+
+    public String[] splitPatterns( String patterns ) {
+        String[] result = null;
+        if (patterns != null && patterns.length() > 0) {
+            List<String> tokens = new ArrayList<String>();
+            StringTokenizer tok = new StringTokenizer(patterns, ", ", false);
+            while (tok.hasMoreTokens()) {
+                tokens.add(tok.nextToken());
+            }
+            result = tokens.toArray(new String[tokens.size()]);
+        }
+        return result;
+    }
+
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+        org.vafer.jdeb.mapping.Mapper[] mappers = null;
+        if (mapper != null) {
+            mappers = new org.vafer.jdeb.mapping.Mapper[] { mapper.createMapper() };
+        }
+
+        // link type
+
+        if ("link".equalsIgnoreCase(type)) {
+            if (linkName == null) {
+                throw new RuntimeException("linkName is not set");
+            }
+            if (linkTarget == null) {
+                throw new RuntimeException("linkTarget is not set");
+            }
+
+            new DataProducerLink(linkName, linkTarget, symlink, includePatterns, excludePatterns, mappers).produce(pReceiver);
+            return;
+        }
+
+        // template type
+
+        if ("template".equalsIgnoreCase(type)) {
+            if (paths == null || paths.length == 0) {
+                throw new RuntimeException("paths is not set");
+            }
+
+            new DataProducerPathTemplate(paths, includePatterns, excludePatterns, mappers).produce(pReceiver);
+            return;
+        }
+
+        // Types that require src to exist
+
+        if (src == null || !src.exists()) {
+            if (missingSrc == IGNORE) {
+                return;
+            } else {
+                throw new FileNotFoundException("Data source not found : " + src);
+            }
+        }
+
+        if ("file".equalsIgnoreCase(type)) {
+            new DataProducerFile(src, dst, includePatterns, excludePatterns, mappers).produce(pReceiver);
+            return;
+        }
+
+        if ("archive".equalsIgnoreCase(type)) {
+            new DataProducerArchive(src, includePatterns, excludePatterns, mappers).produce(pReceiver);
+            return;
+        }
+
+        if ("directory".equalsIgnoreCase(type)) {
+            new DataProducerDirectory(src, includePatterns, excludePatterns, mappers).produce(pReceiver);
+            return;
+        }
+
+        throw new IOException("Unknown type '" + type + "' (file|directory|archive|template|link) for " + src);
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/maven/DebMojo.java b/src/main/java/org/vafer/jdeb/maven/DebMojo.java
new file mode 100644
index 0000000..9d67332
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/maven/DebMojo.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.maven;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.apache.tools.tar.TarEntry;
+import org.vafer.jdeb.Console;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.DebMaker;
+import org.vafer.jdeb.PackagingException;
+import org.vafer.jdeb.utils.FilteredFile;
+import org.vafer.jdeb.utils.MapVariableResolver;
+import org.vafer.jdeb.utils.Utils;
+import org.vafer.jdeb.utils.VariableResolver;
+
+/**
+ * Creates deb archive
+ *
+ * @goal jdeb
+ * @phase package
+ */
+public class DebMojo extends AbstractPluginMojo {
+
+    /**
+     * @component
+     */
+    private MavenProjectHelper projectHelper;
+    
+    /**
+     * Defines the name of deb package.
+     *
+     * @parameter
+     */
+    private String name;
+
+    /**
+     * Defines the pattern of the name of final artifacts. Possible
+     * substitutions are [[baseDir]] [[buildDir]] [[artifactId]] [[version]]
+     * [[extension]] and [[groupId]].
+     *
+     * @parameter default-value="[[buildDir]]/[[artifactId]]_[[version]]_all.[[extension]]"
+     */
+    private String deb;
+
+    /**
+     * Explicitly defines the path to the control directory. At least the
+     * control file is mandatory.
+     *
+     * @parameter default-value="[[baseDir]]/src/deb/control"
+     */
+    private String controlDir;
+
+    /**
+     * Explicitly define the file to read the changes from.
+     *
+     * @parameter default-value="[[baseDir]]/CHANGES.txt"
+     */
+    private String changesIn;
+
+    /**
+     * Explicitly define the file where to write the changes to.
+     *
+     * @parameter default-value="[[buildDir]]/[[artifactId]]_[[version]]_all.changes"
+     */
+    private String changesOut;
+
+    /**
+     * Explicitly define the file where to write the changes of the changes
+     * input to.
+     *
+     * @parameter default-value="[[baseDir]]/CHANGES.txt"
+     */
+    private String changesSave;
+
+    /**
+     * The compression method used for the data file (none, gzip, bzip2 or xz)
+     *
+     * @parameter default-value="gzip"
+     */
+    private String compression;
+
+
+    /**
+     * Boolean option whether to attach the artifact to the project
+     *
+     * @parameter default-value="true"
+     */
+    private String attach;
+
+    /**
+     * The location where all package files will be installed. By default, all
+     * packages are installed in /opt (see the FHS here:
+     * http://www.pathname.com/
+     * fhs/pub/fhs-2.3.html#OPTADDONAPPLICATIONSOFTWAREPACKAGES)
+     *
+     * @parameter default-value="/opt/[[artifactId]]"
+     */
+    private String installDir;
+
+
+    /**
+     * The type of attached artifact
+     *
+     * @parameter default-value="deb"
+     */
+    private String type;
+
+    /**
+     * The project base directory
+     *
+     * @parameter default-value="${basedir}"
+     * @required
+     * @readonly
+     */
+    private File baseDir;
+
+    /**
+     * Run the plugin on all sub-modules.
+     * If set to false, the plugin will be run in the same folder where the
+     * mvn command was invoked
+     *
+     * @parameter expression="${submodules}" default-value="true"
+     */
+    private boolean submodules;
+
+    /**
+     * The Maven Session Object
+     *
+     * @parameter expression="${session}"
+     * @required
+     * @readonly
+     */
+    private MavenSession session;
+
+    /**
+     * The classifier of attached artifact
+     *
+     * @parameter
+     */
+    private String classifier;
+
+    /**
+     * "data" entries used to determine which files should be added to this deb.
+     * The "data" entries may specify a tarball (tar.gz, tar.bz2, tgz), a
+     * directory, or a normal file. An entry would look something like this in
+     * your pom.xml:
+     *
+     * <pre>
+     *   <build>
+     *     <plugins>
+     *       <plugin>
+     *       <artifactId>jdeb</artifactId>
+     *       <groupId>org.vafer</groupId>
+     *       ...
+     *       <configuration>
+     *         ...
+     *         <dataSet>
+     *           <data>
+     *             <src>${project.basedir}/target/my_archive.tar.gz</src>
+     *             <include>...</include>
+     *             <exclude>...</exclude>
+     *             <mapper>
+     *               <type>perm</type>
+     *               <strip>1</strip>
+     *               <prefix>/somewhere/else</prefix>
+     *               <user>santbj</user>
+     *               <group>santbj</group>
+     *               <mode>600</mode>
+     *             </mapper>
+     *           </data>
+     *           <data>
+     *             <src>${project.build.directory}/data</src>
+     *             <include></include>
+     *             <exclude>**/.svn</exclude>
+     *             <mapper>
+     *               <type>ls</type>
+     *               <src>mapping.txt</src>
+     *             </mapper>
+     *           </data>
+     *           <data>
+     *             <type>link</type>
+     *             <linkName>/a/path/on/the/target/fs</linkName>
+     *             <linkTarget>/a/sym/link/to/the/scr/file</linkTarget>
+     *             <symlink>true</symlink>
+     *           </data>
+     *           <data>
+     *             <src>${project.basedir}/README.txt</src>
+     *           </data>
+     *         </dataSet>
+     *       </configuration>
+     *     </plugins>
+     *   </build>
+     * </pre>
+     *
+     * @parameter expression="${dataSet}"
+     */
+    private Data[] dataSet;
+
+    /**
+     * When SNAPSHOT version replace <code>SNAPSHOT</code> with current date
+     * and time to make sure each build is unique.
+     *
+     * @parameter expression="${timestamped}" default-value="false"
+     */
+    private boolean timestamped;
+
+    /**
+     * If verbose is true info messages also get logged.
+     * Will be changed to "false" in future versions.
+     * Left to "true" for the transition.
+     *
+     * @parameter expression="${verbose}" default-value="true"
+     */
+    private boolean verbose;
+
+    /* end of parameters */
+
+    private String openReplaceToken = "[[";
+    private String closeReplaceToken = "]]";
+    private Collection<DataProducer> dataProducers = new ArrayList<DataProducer>();
+
+    public void setOpenReplaceToken( String openReplaceToken ) {
+        this.openReplaceToken = openReplaceToken;
+        // FIXME yuck!
+        FilteredFile.setOpenToken(openReplaceToken);
+    }
+
+    public void setCloseReplaceToken( String closeReplaceToken ) {
+        this.closeReplaceToken = closeReplaceToken;
+        // FIXME yuck!
+        FilteredFile.setCloseToken(closeReplaceToken);
+    }
+
+    protected void setData( Data[] dataSet ) {
+        this.dataSet = dataSet;
+        dataProducers.clear();
+        if (dataSet != null) {
+            Collections.addAll(dataProducers, dataSet);
+        }
+    }
+
+    protected VariableResolver initializeVariableResolver( Map<String, String> variables ) {
+        ((Map) variables).putAll(getProject().getProperties());
+        ((Map) variables).putAll(System.getProperties());
+        variables.put("name", name != null ? name : getProject().getName());
+        variables.put("artifactId", getProject().getArtifactId());
+        variables.put("groupId", getProject().getGroupId());
+        variables.put("version", getProjectVersion());
+        variables.put("description", getProject().getDescription());
+        variables.put("extension", "deb");
+        variables.put("baseDir", getProject().getBasedir().getAbsolutePath());
+        variables.put("buildDir", buildDirectory.getAbsolutePath());
+        variables.put("project.version", getProject().getVersion());
+        variables.put("url", getProject().getUrl());
+
+        return new MapVariableResolver(variables);
+    }
+
+    /**
+     * Doc some cleanup and conversion on the Maven project version.
+     * <ul>
+     * <li>any "-" is replaced by "+"</li>
+     * <li>"SNAPSHOT" is replaced with the current time and date, prepended by "~"</li>
+     * </ul>
+     *
+     * @return the Maven project version
+     */
+    private String getProjectVersion() {
+        return Utils.convertToDebianVersion(getProject().getVersion(), this.timestamped ? session.getStartTime() : null);
+    }
+
+    /**
+     * @return whether or not Maven is currently operating in the execution root
+     */
+    private boolean isSubmodule() {
+        // FIXME there must be a better way
+        return !session.getExecutionRootDirectory().equalsIgnoreCase(baseDir.toString());
+    }
+
+    /**
+     * @return whether or not the main artifact was created
+     */
+    private boolean hasMainArtifact() {
+        final MavenProject project = getProject();
+        final Artifact artifact = project.getArtifact();
+        return artifact.getFile() != null && artifact.getFile().isFile();
+    }
+
+    /**
+     * Main entry point
+     *
+     * @throws MojoExecutionException on error
+     */
+    @Override
+    public void execute() throws MojoExecutionException {
+
+        final MavenProject project = getProject();
+
+        if (isSubmodule() && !submodules) {
+            getLog().info("skipping sub module: jdeb executing at top-level only");
+            return;
+        }
+
+        setData(dataSet);
+
+        Console console = new MojoConsole(getLog(), verbose);
+
+        final VariableResolver resolver = initializeVariableResolver(new HashMap<String, String>());
+
+        final File debFile = new File(Utils.replaceVariables(resolver, deb, openReplaceToken, closeReplaceToken));
+        final File controlDirFile = new File(Utils.replaceVariables(resolver, controlDir, openReplaceToken, closeReplaceToken));
+        final File installDirFile = new File(Utils.replaceVariables(resolver, installDir, openReplaceToken, closeReplaceToken));
+        final File changesInFile = new File(Utils.replaceVariables(resolver, changesIn, openReplaceToken, closeReplaceToken));
+        final File changesOutFile = new File(Utils.replaceVariables(resolver, changesOut, openReplaceToken, closeReplaceToken));
+        final File changesSaveFile = new File(Utils.replaceVariables(resolver, changesSave, openReplaceToken, closeReplaceToken));
+
+        // if there are no producers defined we try to use the artifacts
+        if (dataProducers.isEmpty()) {
+
+            if (!hasMainArtifact()) {
+
+                final String packaging = project.getPackaging();
+                if ("pom".equalsIgnoreCase(packaging)) {
+                    getLog().warn("Creating empty debian package.");
+                } else {
+                    throw new MojoExecutionException(
+                        "Nothing to include into the debian package. " +
+                            "Did you maybe forget to add a <data> tag or call the plugin directly?");
+                }
+
+            } else {
+
+                Set<Artifact> artifacts = new HashSet<Artifact>();
+
+                artifacts.add(project.getArtifact());
+
+                for (Artifact artifact : (Set<Artifact>) project.getArtifacts()) {
+                    artifacts.add(artifact);
+                }
+
+                for (Artifact artifact : (List<Artifact>) project.getAttachedArtifacts()) {
+                    artifacts.add(artifact);
+                }
+
+                for (Artifact artifact : artifacts) {
+                    final File file = artifact.getFile();
+                    if (file != null) {
+                        dataProducers.add(new DataProducer() {
+                            @Override
+                            public void produce( final DataConsumer receiver ) {
+                                try {
+                                    receiver.onEachFile(
+                                        new FileInputStream(file),
+                                        new File(installDirFile, file.getName()).getAbsolutePath(),
+                                        "",
+                                        "root", 0, "root", 0,
+                                        TarEntry.DEFAULT_FILE_MODE,
+                                        file.length());
+                                } catch (Exception e) {
+                                    getLog().error(e);
+                                }
+                            }
+                        });
+                    } else {
+                        getLog().error("No file for artifact " + artifact);
+                    }
+                }
+            }
+        }
+
+        try {
+            DebMaker debMaker = new DebMaker(console, dataProducers);
+            debMaker.setDeb(debFile);
+            debMaker.setControl(controlDirFile);
+            debMaker.setPackage(getProject().getArtifactId());
+            debMaker.setDescription(getProject().getDescription());
+            debMaker.setHomepage(getProject().getUrl());
+            debMaker.setChangesIn(changesInFile);
+            debMaker.setChangesOut(changesOutFile);
+            debMaker.setChangesSave(changesSaveFile);
+            debMaker.setCompression(compression);
+            debMaker.setResolver(resolver);
+            debMaker.validate();
+            debMaker.makeDeb();
+
+            // Always attach unless explicitly set to false
+            if ("true".equalsIgnoreCase(attach)) {
+                getLog().info("Attaching created debian archive " + debFile);
+                projectHelper.attachArtifact(project, type, classifier, debFile);
+            }
+
+        } catch (PackagingException e) {
+            getLog().error("Failed to create debian package " + debFile, e);
+            throw new MojoExecutionException("Failed to create debian package " + debFile, e);
+        }
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/maven/Mapper.java b/src/main/java/org/vafer/jdeb/maven/Mapper.java
new file mode 100644
index 0000000..d44a3d2
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/maven/Mapper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.maven;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.vafer.jdeb.mapping.LsMapper;
+import org.vafer.jdeb.mapping.NullMapper;
+import org.vafer.jdeb.mapping.PermMapper;
+
+/**
+ * Maven "mapper" element acting as factory for the entry mapper.
+ * Supported types: ls, perm
+ *
+ * @author Bryan Sant
+ */
+public final class Mapper {
+
+    /**
+     * @parameter
+     * @required
+     */
+    private String type;
+
+    /**
+     * @parameter
+     */
+    private int uid = -1;
+
+    /**
+     * @parameter
+     */
+    private int gid = -1;
+
+    /**
+     * @parameter
+     */
+    private String user;
+
+    /**
+     * @parameter
+     */
+    private String group;
+
+    /**
+     * @parameter
+     */
+    private String filemode;
+
+    /**
+     * @parameter
+     */
+    private String dirmode;
+
+    /**
+     * @parameter
+     */
+    private String prefix;
+
+    /**
+     * @parameter
+     */
+    private int strip;
+
+    /**
+     * @parameter
+     */
+    private File src;
+
+
+    public org.vafer.jdeb.mapping.Mapper createMapper() throws IOException {
+
+        if ("ls".equalsIgnoreCase(type)) {
+            try {
+                return new LsMapper(new FileInputStream(src));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        if ("perm".equalsIgnoreCase(type)) {
+            return new PermMapper(uid, gid, user, group, filemode, dirmode, strip, prefix);
+        }
+
+        /* NullMapper required for DataProducerPathTemplate */
+        return NullMapper.INSTANCE;
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/maven/MissingSourceBehavior.java b/src/main/java/org/vafer/jdeb/maven/MissingSourceBehavior.java
new file mode 100644
index 0000000..684e871
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/maven/MissingSourceBehavior.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.maven;
+
+public enum MissingSourceBehavior {
+    IGNORE, FAIL
+}
diff --git a/src/main/java/org/vafer/jdeb/maven/MojoConsole.java b/src/main/java/org/vafer/jdeb/maven/MojoConsole.java
new file mode 100644
index 0000000..da78148
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/maven/MojoConsole.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.maven;
+
+import org.apache.maven.plugin.logging.Log;
+import org.vafer.jdeb.Console;
+
+/**
+ * Console implementation for Maven plugins.
+ */
+class MojoConsole implements Console {
+
+    private final Log log;
+    private final boolean verbose;
+
+    public MojoConsole(Log log, boolean verbose) {
+        this.log = log;
+        this.verbose = verbose;
+    }
+
+    public void info(String s) {
+        if (verbose) {
+            log.info(s);
+        }
+    }
+
+    public void warn(String s) {
+        log.warn(s);
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/producers/AbstractDataProducer.java b/src/main/java/org/vafer/jdeb/producers/AbstractDataProducer.java
new file mode 100644
index 0000000..47d8764
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/AbstractDataProducer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+
+/**
+ * Base Producer class providing including/excluding.
+ *
+ * @author Torsten Curdt
+ */
+public abstract class AbstractDataProducer implements DataProducer {
+
+    private final String[] includes;
+    private final String[] excludes;
+    private final Mapper[] mappers;
+
+
+    public AbstractDataProducer( final String[] pIncludes, final String[] pExcludes, final Mapper[] pMapper ) {
+        excludes = (pExcludes != null) ? pExcludes : new String[0];
+        includes = (pIncludes != null) ? pIncludes : new String[] { "**" };
+        mappers = (pMapper != null) ? pMapper : new Mapper[0];
+    }
+
+    public boolean isIncluded( final String pName ) {
+        if (!isIncluded(pName, includes)) {
+            return false;
+        }
+        if (isExcluded(pName, excludes)) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean isIncluded( String name, String[] includes ) {
+        for (String include : includes) {
+            if (SelectorUtils.matchPath(include, name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    private boolean isExcluded( String name, String[] excludes ) {
+        for (String exclude : excludes) {
+            if (SelectorUtils.matchPath(exclude, name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public TarArchiveEntry map( final TarArchiveEntry pEntry ) {
+
+        TarArchiveEntry entry = pEntry;
+
+        for (Mapper mapper : mappers) {
+            entry = mapper.map(entry);
+        }
+
+        return entry;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/producers/DataProducerArchive.java b/src/main/java/org/vafer/jdeb/producers/DataProducerArchive.java
new file mode 100644
index 0000000..8160759
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/DataProducerArchive.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.CompressorInputStream;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+
+/**
+ * Providing data from an archive keeping permissions and ownerships.
+ *
+ * @author Torsten Curdt
+ */
+public final class DataProducerArchive extends AbstractDataProducer implements DataProducer {
+
+    private final File archive;
+
+    public DataProducerArchive( final File pArchive, final String[] pIncludes, final String[] pExcludes, final Mapper[] pMappers ) {
+        super(pIncludes, pExcludes, pMappers);
+        archive = pArchive;
+    }
+
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+
+        InputStream is = new BufferedInputStream(new FileInputStream(archive));
+
+        CompressorInputStream compressorInputStream = null;
+
+        try {
+            compressorInputStream = new CompressorStreamFactory().createCompressorInputStream(is);
+        } catch (CompressorException e) {
+            // expected if the input file is a zip archive
+        }
+
+        if (compressorInputStream != null) {
+            is = new BufferedInputStream(compressorInputStream);
+        }
+
+        ArchiveInputStream archiveInputStream = null;
+
+        try {
+            archiveInputStream = new ArchiveStreamFactory().createArchiveInputStream(is);
+        } catch (ArchiveException e) {
+            throw new IOException("Unsupported archive format: " + archive, e);
+        }
+
+        EntryConverter converter = null;
+
+        if (archiveInputStream instanceof TarArchiveInputStream) {
+
+            converter = new EntryConverter() {
+                public TarArchiveEntry convert( ArchiveEntry entry ) {
+                    TarArchiveEntry src = (TarArchiveEntry) entry;
+                    TarArchiveEntry dst = new TarArchiveEntry(src.getName(), true);
+
+                    dst.setSize(src.getSize());
+                    dst.setGroupName(src.getGroupName());
+                    dst.setGroupId(src.getGroupId());
+                    dst.setUserId(src.getUserId());
+                    dst.setMode(src.getMode());
+                    dst.setModTime(src.getModTime());
+
+                    return dst;
+                }
+            };
+
+        } else if (archiveInputStream instanceof ZipArchiveInputStream) {
+
+            converter = new EntryConverter() {
+                public TarArchiveEntry convert( ArchiveEntry entry ) {
+                    ZipArchiveEntry src = (ZipArchiveEntry) entry;
+                    TarArchiveEntry dst = new TarArchiveEntry(src.getName(), true);
+
+                    dst.setSize(src.getSize());
+                    dst.setMode(src.getUnixMode());
+                    dst.setModTime(src.getTime());
+
+                    return dst;
+                }
+            };
+
+        } else {
+            throw new IOException("Unsupported archive format: " + archive);
+        }
+
+
+        try {
+            while (true) {
+
+                ArchiveEntry archiveEntry = archiveInputStream.getNextEntry();
+
+                if (archiveEntry == null) {
+                    break;
+                }
+
+                if (!isIncluded(archiveEntry.getName())) {
+                    continue;
+                }
+
+                TarArchiveEntry entry = converter.convert(archiveEntry);
+
+                entry = map(entry);
+
+                if (entry.isDirectory()) {
+                    pReceiver.onEachDir(entry.getName(), entry.getLinkName(), entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode(), entry.getSize());
+                    continue;
+                }
+                pReceiver.onEachFile(archiveInputStream, entry.getName(), entry.getLinkName(), entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode(), entry.getSize());
+            }
+
+        } finally {
+            if (archiveInputStream != null) {
+                archiveInputStream.close();
+            }
+        }
+    }
+
+    private interface EntryConverter {
+        public TarArchiveEntry convert( ArchiveEntry entry );
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/vafer/jdeb/producers/DataProducerDirectory.java b/src/main/java/org/vafer/jdeb/producers/DataProducerDirectory.java
new file mode 100644
index 0000000..70eff71
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/DataProducerDirectory.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.tools.ant.DirectoryScanner;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+import org.vafer.jdeb.utils.Utils;
+
+/**
+ * DataProducer iterating over a directory.
+ * For cross-platform permissions and ownerships you probably want to use a Mapper, too.
+ *
+ * @author Torsten Curdt
+ */
+public final class DataProducerDirectory extends AbstractDataProducer implements DataProducer {
+
+    private final DirectoryScanner scanner = new DirectoryScanner();
+
+    public DataProducerDirectory( final File pDir, final String[] pIncludes, final String[] pExcludes, final Mapper[] pMappers ) {
+        super(pIncludes, pExcludes, pMappers);
+        scanner.setBasedir(pDir);
+        scanner.setIncludes(pIncludes);
+        scanner.setExcludes(pExcludes);
+        scanner.setCaseSensitive(true);
+        scanner.setFollowSymlinks(true);
+    }
+
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+
+        scanner.scan();
+
+        final File baseDir = scanner.getBasedir();
+        
+        for (String dir : scanner.getIncludedDirectories()) {
+            final File file = new File(baseDir, dir);
+            String dirname = getFilename(baseDir, file);
+
+            if ("".equals(dirname)) {
+                continue;
+            }
+
+            if (!isIncluded(dirname)) {
+                continue;
+            }
+
+            if ('/' != File.separatorChar) {
+                dirname = dirname.replace(File.separatorChar, '/');
+            }
+
+            if (!dirname.endsWith("/")) {
+                dirname += "/";
+            }
+
+            TarArchiveEntry entry = new TarArchiveEntry(dirname, true);
+            entry.setUserId(0);
+            entry.setUserName("root");
+            entry.setGroupId(0);
+            entry.setGroupName("root");
+            entry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
+
+            entry = map(entry);
+
+            entry.setSize(0);
+
+            pReceiver.onEachDir(entry.getName(), entry.getLinkName(), entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode(), entry.getSize());
+        }
+
+
+        for (String f : scanner.getIncludedFiles()) {
+            final File file = new File(baseDir, f);
+            String filename = getFilename(baseDir, file);
+
+            if (!isIncluded(filename)) {
+                continue;
+            }
+
+            if ('/' != File.separatorChar) {
+                filename = filename.replace(File.separatorChar, '/');
+            }
+
+            TarArchiveEntry entry = new TarArchiveEntry(filename, true);
+            entry.setUserId(0);
+            entry.setUserName("root");
+            entry.setGroupId(0);
+            entry.setGroupName("root");
+            entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE);
+
+            entry = map(entry);
+
+            entry.setSize(file.length());
+
+            final InputStream inputStream = new FileInputStream(file);
+            try {
+                pReceiver.onEachFile(inputStream, entry.getName(), entry.getLinkName(), entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode(), entry.getSize());
+            } finally {
+                inputStream.close();
+            }
+        }
+    }
+
+    private String getFilename( File root, File file ) {
+
+        final String relativeFilename = file.getAbsolutePath().substring(root.getAbsolutePath().length());
+
+        return Utils.stripLeadingSlash(relativeFilename);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/vafer/jdeb/producers/DataProducerFile.java b/src/main/java/org/vafer/jdeb/producers/DataProducerFile.java
new file mode 100644
index 0000000..17fb3bd
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/DataProducerFile.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+
+/**
+ * DataProducer representing a single file
+ * For cross-platform permissions and ownerships you probably want to use a Mapper, too.
+ *
+ * @author Torsten Curdt
+ */
+public final class DataProducerFile extends AbstractDataProducer implements DataProducer {
+
+    private final File file;
+
+    private final String destinationName;
+
+    public DataProducerFile( final File pFile, String pDestinationName, String[] pIncludes, String[] pExcludes, Mapper[] pMapper ) {
+        super(pIncludes, pExcludes, pMapper);
+        file = pFile;
+        destinationName = pDestinationName;
+    }
+
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+        String fileName;
+        if (destinationName != null && destinationName.trim().length() > 0) {
+            fileName = destinationName.trim();
+        } else {
+            fileName = file.getName();
+        }
+
+        TarArchiveEntry entry = new TarArchiveEntry(fileName, true);
+        entry.setUserId(0);
+        entry.setUserName("root");
+        entry.setGroupId(0);
+        entry.setGroupName("root");
+        entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE);
+
+        entry = map(entry);
+
+        entry.setSize(file.length());
+
+        final InputStream inputStream = new FileInputStream(file);
+        try {
+            pReceiver.onEachFile(inputStream, entry.getName(), entry.getLinkName(), entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode(), entry.getSize());
+        } finally {
+            inputStream.close();
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/producers/DataProducerFileSet.java b/src/main/java/org/vafer/jdeb/producers/DataProducerFileSet.java
new file mode 100644
index 0000000..b4eaf4d
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/DataProducerFileSet.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.taskdefs.Tar;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.tar.TarEntry;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+
+/**
+ * DataProducer providing data from an Ant fileset. TarFileSets are also
+ * supported with their permissions.
+ *
+ * @author Emmanuel Bourg
+ */
+public final class DataProducerFileSet implements DataProducer {
+
+    private final FileSet fileset;
+
+    public DataProducerFileSet( final FileSet fileset ) {
+        this.fileset = fileset;
+    }
+
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+        String user = "root";
+        int uid = 0;
+        String group = "root";
+        int gid = 0;
+        int filemode = TarEntry.DEFAULT_FILE_MODE;
+        int dirmode = TarEntry.DEFAULT_DIR_MODE;
+        String prefix = "";
+
+        if (fileset instanceof Tar.TarFileSet) {
+            Tar.TarFileSet tarfileset = (Tar.TarFileSet) fileset;
+            user = tarfileset.getUserName();
+            uid = tarfileset.getUid();
+            group = tarfileset.getGroup();
+            gid = tarfileset.getGid();
+            filemode = tarfileset.getMode();
+            dirmode = tarfileset.getDirMode(tarfileset.getProject());
+            prefix = tarfileset.getPrefix(tarfileset.getProject());
+        }
+
+        final DirectoryScanner scanner = fileset.getDirectoryScanner(fileset.getProject());
+        scanner.scan();
+
+        final File basedir = scanner.getBasedir();
+        
+        for (String directory : scanner.getIncludedDirectories()) {
+            String name = directory.replace('\\', '/');
+
+            pReceiver.onEachDir(prefix + "/" + name, null, user, uid, group, gid, dirmode, 0);
+        }
+        
+        for (String filename : scanner.getIncludedFiles()) {
+            final String name = filename.replace('\\', '/');
+            final File file = new File(basedir, name);
+
+            final InputStream inputStream = new FileInputStream(file);
+            try {
+                pReceiver.onEachFile(inputStream, prefix + "/" + name, null, user, uid, group, gid, filemode, file.length());
+            } finally {
+                inputStream.close();
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/producers/DataProducerLink.java b/src/main/java/org/vafer/jdeb/producers/DataProducerLink.java
new file mode 100644
index 0000000..69c851f
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/DataProducerLink.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.IOException;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+
+/**
+ * DataProducer representing a single file
+ * For cross-platform permissions and ownerships you probably want to use a Mapper, too.
+ *
+ * @author Thomas Mortagne
+ */
+public final class DataProducerLink extends AbstractDataProducer implements DataProducer {
+
+    private final String path;
+    private final String linkName;
+    private final boolean symlink;
+
+    public DataProducerLink(final String path, final String linkName, final boolean symlink, String[] pIncludes, String[] pExcludes, Mapper[] pMapper) {
+        super(pIncludes, pExcludes, pMapper);
+        this.path = path;
+        this.symlink = symlink;
+        this.linkName = linkName;
+    }
+
+    @Override
+    public void produce( final DataConsumer pReceiver ) throws IOException {
+        TarArchiveEntry entry = new TarArchiveEntry(path, symlink ? TarArchiveEntry.LF_SYMLINK : TarArchiveEntry.LF_LINK);
+        entry.setLinkName(linkName);
+
+        entry.setUserId(0);
+        entry.setUserName("root");
+        entry.setGroupId(0);
+        entry.setGroupName("root");
+        entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE);
+
+        entry = map(entry);
+
+        pReceiver.onEachLink(path, linkName, symlink, entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode());
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/producers/DataProducerPathTemplate.java b/src/main/java/org/vafer/jdeb/producers/DataProducerPathTemplate.java
new file mode 100644
index 0000000..bd6cfd0
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/producers/DataProducerPathTemplate.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.IOException;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+
+public class DataProducerPathTemplate extends AbstractDataProducer implements DataProducer {
+
+    private final String[] literalPaths;
+
+    public DataProducerPathTemplate( String[] pLiteralPaths, String[] pIncludes, String[] pExcludes, Mapper[] pMapper ) {
+        super(pIncludes, pExcludes, pMapper);
+        literalPaths = pLiteralPaths;
+    }
+
+    public void produce( DataConsumer pReceiver ) throws IOException {
+        for (String literalPath : literalPaths) {
+            TarArchiveEntry entry = new TarArchiveEntry(literalPath, true);
+            entry.setUserId(0);
+            entry.setUserName("root");
+            entry.setGroupId(0);
+            entry.setGroupName("root");
+            entry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
+
+            entry = map(entry);
+
+            entry.setSize(0);
+
+            pReceiver.onEachDir(entry.getName(), entry.getLinkName(), entry.getUserName(), entry.getUserId(), entry.getGroupName(), entry.getGroupId(), entry.getMode(), entry.getSize());
+        }
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/signing/PGPSigner.java b/src/main/java/org/vafer/jdeb/signing/PGPSigner.java
new file mode 100644
index 0000000..b2af9e5
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/signing/PGPSigner.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.signing;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
+import java.util.Iterator;
+
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.bcpg.BCPGOutputStream;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
+
+/**
+ * Signing with OpenPGP.
+ * 
+ * @author Torsten Curdt
+ * @author Emmanuel Bourg
+ */
+public class PGPSigner {
+
+    private static final byte[] EOL = "\r\n".getBytes(Charset.forName("UTF-8"));
+
+    private PGPPrivateKey privateKey;
+
+    public PGPSigner(InputStream keyring, String keyId, String passphrase) throws IOException, PGPException {
+        PGPSecretKey secretKey = getSecretKey(keyring, keyId);
+        privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passphrase.toCharArray()));
+    }
+
+    /**
+     * Creates a clear sign signature over the input data. (Not detached)
+     *
+     * @param input      the content to be signed
+     * @param output     the output destination of the signature
+     */
+    public void clearSign(String input, OutputStream output) throws IOException, PGPException, GeneralSecurityException {
+        clearSign(new ByteArrayInputStream(input.getBytes("UTF-8")), output);
+    }
+
+    /**
+     * Creates a clear sign signature over the input data. (Not detached)
+     *
+     * @param input      the content to be signed
+     * @param output     the output destination of the signature
+     */
+    public void clearSign(InputStream input, OutputStream output) throws IOException, PGPException, GeneralSecurityException {
+        int digest = PGPUtil.SHA1;
+        
+        PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(privateKey.getPublicKeyPacket().getAlgorithm(), digest));
+        signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privateKey);
+        
+        ArmoredOutputStream armoredOutput = new ArmoredOutputStream(output);
+        armoredOutput.beginClearText(digest);
+        
+        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+        
+        String line;
+        while ((line = reader.readLine()) != null) {
+            // trailing spaces must be removed for signature calculation (see http://tools.ietf.org/html/rfc4880#section-7.1)
+            byte[] data = trim(line).getBytes("UTF-8");
+            
+            armoredOutput.write(data);
+            armoredOutput.write(EOL);
+            
+            signatureGenerator.update(data);
+            signatureGenerator.update(EOL);
+        }
+
+        armoredOutput.endClearText();
+        
+        PGPSignature signature = signatureGenerator.generate();
+        signature.encode(new BCPGOutputStream(armoredOutput));
+        
+        armoredOutput.close();
+    }
+    
+    /**
+     * Returns the secret key matching the specified identifier.
+     * 
+     * @param input the input stream containing the keyring collection
+     * @param keyId the 4 bytes identifier of the key
+     */
+    private PGPSecretKey getSecretKey(InputStream input, String keyId) throws IOException, PGPException {
+        PGPSecretKeyRingCollection keyrings = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input));
+        
+        Iterator rIt = keyrings.getKeyRings();
+
+        while (rIt.hasNext()) {
+            PGPSecretKeyRing kRing = (PGPSecretKeyRing) rIt.next();
+            Iterator kIt = kRing.getSecretKeys();
+
+            while (kIt.hasNext()) {
+                PGPSecretKey key = (PGPSecretKey) kIt.next();
+                
+                if (key.isSigningKey() && Long.toHexString(key.getKeyID() & 0xFFFFFFFFL).equals(keyId.toLowerCase())) {
+                    return key;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Trim the trailing spaces.
+     * 
+     * @param line
+     */
+    private String trim(String line) {
+        char[] chars = line.toCharArray();
+        int len = chars.length;
+
+        while (len > 0) {
+            if (!Character.isWhitespace(chars[len - 1])) {
+                break;
+            }
+            len--;
+        }
+        
+        return line.substring(0, len);
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/utils/FilteredFile.java b/src/main/java/org/vafer/jdeb/utils/FilteredFile.java
new file mode 100644
index 0000000..c6f2beb
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/utils/FilteredFile.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FilteredFile {
+
+    private static String openToken = "[[";
+    private static String closeToken = "]]";
+    private List<String> lines = new ArrayList<String>();
+
+    public FilteredFile(InputStream in, VariableResolver resolver) throws IOException {
+        parse(in, resolver);
+    }
+
+    public static void setOpenToken(String token) {
+        openToken = token;
+    }
+
+    public static void setCloseToken(String token) {
+        closeToken = token;
+    }
+
+    private void parse(InputStream in, VariableResolver resolver) throws IOException {
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new InputStreamReader(in));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (resolver != null) {
+                    lines.add(Utils.replaceVariables(resolver, line, openToken, closeToken));
+                } else {
+                    lines.add(line);
+                }
+            }
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        for (String line : lines) {
+            builder.append(line).append('\n');
+        }
+        return builder.toString();
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/utils/InformationInputStream.java b/src/main/java/org/vafer/jdeb/utils/InformationInputStream.java
new file mode 100644
index 0000000..7907176
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/utils/InformationInputStream.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.utils;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class InformationInputStream extends FilterInputStream {
+
+    private long i;
+    private long ascii;
+    private long nonascii;
+    private long cr;
+    private long lf;
+    private long zero;
+
+    private final Map<BOM, Integer> bomPositions = new HashMap<BOM, Integer>();
+    private final Map<Shell, Integer> shellPositions = new HashMap<Shell, Integer>();
+
+    /**
+     * Byte Order Marks
+     */
+    private enum BOM {
+        NONE(null),
+        UTF8("UTF-8", 0xEF, 0xBB, 0xBF),
+        UTF16LE("UTF-16LE", 0xFF, 0xFE),
+        UTF16BE("UTF-16BE", 0xFE, 0xFF);
+
+        int[] sequence;
+        String encoding;
+
+        private BOM( String encoding, int... sequence ) {
+            this.encoding = encoding;
+            this.sequence = sequence;
+        }
+    }
+
+    /**
+     * Shebang for shell scripts in various encodings.
+     */
+    private enum Shell {
+        NONE,
+        ASCII(0x23, 0x21),
+        UTF16BE(0x00, 0x23, 0x00, 0x21),
+        UTF16LE(0x23, 0x00, 0x21, 0x00);
+
+        int[] header;
+
+        private Shell( int... header ) {
+            this.header = header;
+        }
+    }
+
+    private BOM bom = BOM.NONE;
+    private Shell shell = Shell.NONE;
+
+    public InformationInputStream( InputStream in ) {
+        super(in);
+    }
+
+    public boolean hasBom() {
+        return bom != BOM.NONE;
+    }
+
+    public boolean isShell() {
+        return shell != Shell.NONE;
+    }
+
+    public boolean hasUnixLineEndings() {
+        return cr == 0;
+    }
+
+    public String getEncoding() {
+        String encoding = bom.encoding;
+
+        if (encoding == null) {
+            // guess the encoding from the shebang
+            if (shell == Shell.UTF16BE) {
+                encoding = BOM.UTF16BE.encoding;
+            } else if (shell == Shell.UTF16LE) {
+                encoding = BOM.UTF16LE.encoding;
+            }
+        }
+
+        return encoding;
+    }
+
+    private void add( int c ) {
+        if (i < 10) {
+            if (shell == Shell.NONE) {
+                for (Shell shell : Shell.values()) {
+                    int position = shellPositions.containsKey(shell) ? shellPositions.get(shell) : 0;
+                    if (position < shell.header.length) {
+                        if (c == shell.header[position]) {
+                            shellPositions.put(shell, position + 1);
+                        } else {
+                            shellPositions.put(shell, 0);
+                        }
+                    } else {
+                        this.shell = shell;
+                    }
+                }
+            }
+
+            if (bom == BOM.NONE) {
+                for (BOM bom : BOM.values()) {
+                    int position = bomPositions.containsKey(bom) ? bomPositions.get(bom) : 0;
+                    if (position < bom.sequence.length) {
+                        if (c == bom.sequence[position] && position == i) {
+                            bomPositions.put(bom, position + 1);
+                        } else {
+                            bomPositions.put(bom, 0);
+                        }
+                    } else {
+                        this.bom = bom;
+                    }
+                }
+            }
+        }
+
+        i++;
+
+        if (c == '\n') {
+            lf++;
+            return;
+        }
+        if (c == '\r') {
+            cr++;
+            return;
+        }
+        if (c >= ' ' && c <= '~') {
+            ascii++;
+            return;
+        }
+        if (c == 0) {
+            zero++;
+            return;
+        }
+        nonascii++;
+    }
+
+    public int read() throws IOException {
+        int b = super.read();
+        if (b != -1) {
+            add(b & 0xFF);
+        }
+        return b;
+    }
+
+    public int read( byte[] b, int off, int len ) throws IOException {
+        int length = super.read(b, off, len);
+        for (int i = 0; i < length; i++) {
+            add(b[off + i] & 0xFF);
+        }
+        return length;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        sb.append("total=").append(i);
+        sb.append(",noascii=").append(nonascii);
+        sb.append(",ascii=").append(ascii);
+        sb.append(",cr=").append(cr);
+        sb.append(",lf=").append(lf);
+        sb.append(",zero=").append(zero);
+        sb.append("}");
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/utils/InformationOutputStream.java b/src/main/java/org/vafer/jdeb/utils/InformationOutputStream.java
new file mode 100644
index 0000000..63f11c4
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/utils/InformationOutputStream.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.utils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+
+/**
+ * Convenience class to provide digest info and length of a stream.
+ *
+ * ATTENTION: don't use outside of jdeb
+ *
+ * @author Torsten Curdt <tcurdt at vafer.org>
+ */
+public class InformationOutputStream extends DigestOutputStream {
+
+    private final MessageDigest digest;
+    private long size;
+
+    public InformationOutputStream( OutputStream pStream, MessageDigest pDigest ) {
+        super(pStream, pDigest);
+        digest = pDigest;
+        size = 0;
+    }
+
+    public String getHexDigest() {
+        return Utils.toHex(digest.digest());
+    }
+
+    public void write( byte[] b, int off, int len ) throws IOException {
+        super.write(b, off, len);
+        size += len;
+    }
+
+    public void write( int b ) throws IOException {
+        super.write(b);
+        size++;
+    }
+
+    public long getSize() {
+        return size;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java b/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java
new file mode 100644
index 0000000..de50343
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.utils;
+
+import java.util.Map;
+
+/**
+ * Resolve variables based on a Map.
+ *
+ * ATTENTION: don't use outside of jdeb
+ *
+ * @author Torsten Curdt <tcurdt at vafer.org>
+ */
+public final class MapVariableResolver implements VariableResolver {
+
+    private final Map<String, String> map;
+
+    public MapVariableResolver( Map<String, String> map ) {
+        this.map = map;
+    }
+
+    public String get( String key ) {
+        return map.get(key);
+    }
+
+}
diff --git a/src/main/java/org/vafer/jdeb/utils/Utils.java b/src/main/java/org/vafer/jdeb/utils/Utils.java
new file mode 100644
index 0000000..b078f22
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/utils/Utils.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Date;
+import java.text.SimpleDateFormat;
+
+import org.apache.tools.ant.filters.FixCrLfFilter;
+import org.apache.tools.ant.util.ReaderInputStream;
+
+/**
+ * Simple utils functions.
+ *
+ * ATTENTION: don't use outside of jdeb
+ *
+ * @author Torsten Curdt <tcurdt at vafer.org>
+ */
+public final class Utils {
+
+    public static int copy( final InputStream pInput, final OutputStream pOutput ) throws IOException {
+        final byte[] buffer = new byte[2048];
+        int count = 0;
+        int n;
+        while (-1 != (n = pInput.read(buffer))) {
+            pOutput.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    public static String toHex( final byte[] bytes ) {
+        final StringBuilder sb = new StringBuilder();
+
+        for (byte b : bytes) {
+            sb.append(Integer.toHexString((b >> 4) & 0x0f));
+            sb.append(Integer.toHexString(b & 0x0f));
+        }
+
+        return sb.toString();
+    }
+
+    public static String stripPath( final int p, final String s ) {
+
+        if (p <= 0) {
+            return s;
+        }
+
+        int x = 0;
+        for (int i = 0; i < p; i++) {
+            x = s.indexOf('/', x + 1);
+            if (x < 0) {
+                return s;
+            }
+        }
+
+        return s.substring(x + 1);
+    }
+
+    public static String stripLeadingSlash( final String s ) {
+        if (s == null) {
+            return s;
+        }
+        if (s.length() == 0) {
+            return s;
+        }
+        if (s.charAt(0) == '/' || s.charAt(0) == '\\') {
+            return s.substring(1);
+        }
+        return s;
+    }
+
+
+    /**
+     * Substitute the variables in the given expression with the
+     * values from the resolver
+     *
+     * @param pResolver
+     * @param pExpression
+     */
+    public static String replaceVariables( final VariableResolver pResolver, final String pExpression, final String pOpen, final String pClose ) {
+        final char[] open = pOpen.toCharArray();
+        final char[] close = pClose.toCharArray();
+        
+        final StringBuilder out = new StringBuilder();
+        StringBuilder sb = new StringBuilder();
+        char[] watch = open;
+        int w = 0;
+        for (char c : pExpression.toCharArray()) {
+            if (c == watch[w]) {
+                w++;
+                if (watch.length == w) {
+                    // found the full token to watch for
+
+                    if (watch == open) {
+                        // found open
+                        out.append(sb);
+                        sb = new StringBuilder();
+                        // search for close
+                        watch = close;
+                    } else {
+                        // found close
+                        final String variable = pResolver.get(sb.toString());
+                        if (variable != null) {
+                            out.append(variable);
+                        } else {
+                            out.append(pOpen);
+                            out.append(sb);
+                            out.append(pClose);
+                        }
+                        sb = new StringBuilder();
+                        // search for open
+                        watch = open;
+                    }
+                    w = 0;
+                }
+            } else {
+
+                if (w > 0) {
+                    sb.append(watch, 0, w);
+                }
+
+                sb.append(c);
+
+                w = 0;
+            }
+        }
+
+        if (watch == close) {
+            out.append(open);
+        }
+        out.append(sb);
+
+        return out.toString();
+    }
+
+    /**
+     * Replaces new line delimiters in the input stream with the Unix line feed.
+     *
+     * @param input
+     */
+    public static byte[] toUnixLineEndings( InputStream input ) throws IOException {
+        String encoding = "ISO-8859-1";
+        FixCrLfFilter filter = new FixCrLfFilter(new InputStreamReader(input, encoding));
+        filter.setEol(FixCrLfFilter.CrLf.newInstance("unix"));
+
+        ByteArrayOutputStream filteredFile = new ByteArrayOutputStream();
+        Utils.copy(new ReaderInputStream(filter, encoding), filteredFile);
+
+        return filteredFile.toByteArray();
+    }
+
+    /**
+     * convert to debian version format
+     */
+    public static String convertToDebianVersion( String version, Date timestamp ) {
+        version = version.replace('-', '+');
+        if (version.endsWith("+SNAPSHOT")) {
+            version = version.substring(0, version.length() - "+SNAPSHOT".length());
+            version += "~";
+
+            if (timestamp != null) {
+                version += new SimpleDateFormat("yyyyMMddHHmmss").format(timestamp);
+            } else {
+                version += "SNAPSHOT";
+            }
+        }
+        return version;
+    }
+}
diff --git a/src/main/java/org/vafer/jdeb/utils/VariableResolver.java b/src/main/java/org/vafer/jdeb/utils/VariableResolver.java
new file mode 100644
index 0000000..9e71ce8
--- /dev/null
+++ b/src/main/java/org/vafer/jdeb/utils/VariableResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.utils;
+
+/**
+ * Interface for resolving variables.
+ * See Utils.replaceVariables()
+ *
+ * ATTENTION: don't use outside of jdeb
+ *
+ * @author Torsten Curdt <tcurdt at vafer.org>
+ */
+
+public interface VariableResolver {
+    public String get( final String pKey );
+}
diff --git a/src/main/resources/org/vafer/jdeb/ant/antlib.xml b/src/main/resources/org/vafer/jdeb/ant/antlib.xml
new file mode 100644
index 0000000..210d6a0
--- /dev/null
+++ b/src/main/resources/org/vafer/jdeb/ant/antlib.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<antlib>
+  <taskdef name="jdeb" classname="org.vafer.jdeb.ant.DebAntTask" />
+</antlib>
diff --git a/src/test/java/org/vafer/jdeb/ArchiveVisitor.java b/src/test/java/org/vafer/jdeb/ArchiveVisitor.java
new file mode 100644
index 0000000..5c3ebc1
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/ArchiveVisitor.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 Emmanuel Bourg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.IOException;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+
+/**
+ * Callback used for inspecting an archive.
+ * 
+ * @author Emmanuel Bourg
+ */
+public interface ArchiveVisitor<E extends ArchiveEntry> {
+    
+    void visit(E entry, byte[] content) throws IOException;
+}
diff --git a/src/test/java/org/vafer/jdeb/ArchiveWalker.java b/src/test/java/org/vafer/jdeb/ArchiveWalker.java
new file mode 100644
index 0000000..4fa02ee
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/ArchiveWalker.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Emmanuel Bourg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
+import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
+
+/**
+ * Support class for inspecting the content of an archive.
+ * 
+ * @author Emmanuel Bourg
+ */
+public class ArchiveWalker {
+    
+    public static void walk(ArchiveInputStream in, ArchiveVisitor visitor) throws IOException {
+        try {
+            ArchiveEntry entry;
+            while ((entry = in.getNextEntry()) != null) {
+                byte[] content = new byte[(int) entry.getSize()];
+                if (entry.getSize() > 0) {
+                    int length = in.read(content);
+                    if (length != entry.getSize()) {
+                        throw new IOException("Couldn't read entry " + entry.getName() + " : read " + length + ", expected " + entry.getSize());
+                    }
+                }
+                
+                visitor.visit(entry, content);
+            }
+        } finally {
+            in.close();
+        }
+    }
+
+    public static boolean walkControl(File deb, final ArchiveVisitor<TarArchiveEntry> visitor) throws IOException {
+        return walkEmbedded(deb, "control.tar", visitor, Compression.GZIP);
+    }
+
+    public static boolean walkData(File deb, final ArchiveVisitor<TarArchiveEntry> visitor, final Compression compression) throws IOException {
+        return walkEmbedded(deb, "data.tar", visitor, compression);
+    }
+
+    public static boolean walkEmbedded(File deb, final String name, final ArchiveVisitor<TarArchiveEntry> visitor, final Compression compression) throws IOException {
+        final AtomicBoolean found = new AtomicBoolean(false);
+        ArArchiveInputStream in = new ArArchiveInputStream(new FileInputStream(deb));
+        ArchiveWalker.walk(in, new ArchiveVisitor<ArArchiveEntry>() {
+            public void visit(ArArchiveEntry entry, byte[] content) throws IOException {
+                if (entry.getName().equals(name + compression.getExtension())) {
+                    InputStream in = new ByteArrayInputStream(content);
+                    if (compression == Compression.GZIP) {
+                        in = new GZIPInputStream(in);
+                    } else if (compression == Compression.XZ) {
+                        in = new XZCompressorInputStream(in);
+                    } else if (compression == Compression.BZIP2) {
+                        in = new BZip2CompressorInputStream(in);
+                    }
+                    
+                    ArchiveWalker.walk(new TarArchiveInputStream(in), new ArchiveVisitor<TarArchiveEntry>() {
+                        public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+                            found.set(true);
+                            visitor.visit(entry, content);
+                        }
+                    });
+                }
+            }
+        });
+        return found.get();
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/DataBuilderTestCase.java b/src/test/java/org/vafer/jdeb/DataBuilderTestCase.java
new file mode 100644
index 0000000..5a0bc17
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/DataBuilderTestCase.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.vafer.jdeb.producers.DataProducerFile;
+import org.vafer.jdeb.producers.DataProducerFileSet;
+
+public class DataBuilderTestCase extends TestCase {
+    
+    /**
+     * Checks if the file paths in the md5sums file use only unix file separators
+     * (this test can only fail on Windows)
+     */
+    public void testBuildDataWithFileSet() throws Exception {
+        DataBuilder builder = new DataBuilder(new NullConsole());
+
+        Project project = new Project();
+        project.setCoreLoader(getClass().getClassLoader());
+        project.init();
+
+        FileSet fileset = new FileSet();
+        fileset.setDir(new File(getClass().getResource("deb/data").toURI()));
+        fileset.setIncludes("**/*");
+        fileset.setProject(project);
+
+        StringBuilder md5s = new StringBuilder();
+        builder.buildData(Arrays.asList((DataProducer) new DataProducerFileSet(fileset)), new File("target/data.tar"), md5s, Compression.GZIP);
+
+        assertTrue("empty md5 file", md5s.length() > 0);
+        assertFalse("windows path separator found", md5s.indexOf("\\") != -1);
+    }
+    
+    public void testCreateParentDirectories() throws Exception {
+        File archive = new File("target/data.tar");
+        if (archive.exists()) {
+            archive.delete();
+        }
+        
+        DataBuilder builder = new DataBuilder(new NullConsole());
+        
+        DataProducer producer = new DataProducerFile(new File("pom.xml"), "/usr/share/myapp/pom.xml", null, null, null); 
+        
+        builder.buildData(Arrays.asList(producer), archive, new StringBuilder(), Compression.NONE);
+        
+        int count = 0;
+        TarArchiveInputStream in = null;
+        try {
+            in = new TarArchiveInputStream(new FileInputStream(archive));
+            while (in.getNextTarEntry() != null) {
+                count++;
+            }
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+        
+        assertEquals("entries", 4, count);
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/DebMakerTestCase.java b/src/test/java/org/vafer/jdeb/DebMakerTestCase.java
new file mode 100644
index 0000000..7eaf17c
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/DebMakerTestCase.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.io.output.NullOutputStream;
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+import org.vafer.jdeb.producers.DataProducerArchive;
+import org.vafer.jdeb.producers.DataProducerDirectory;
+import org.vafer.jdeb.producers.DataProducerLink;
+import org.vafer.jdeb.utils.InformationInputStream;
+import org.vafer.jdeb.utils.MapVariableResolver;
+
+public class DebMakerTestCase extends TestCase {
+
+    public void testCreation() throws Exception {
+
+        File control = new File(getClass().getResource("deb/control/control").toURI());
+        File archive1 = new File(getClass().getResource("deb/data.tgz").toURI());
+        File archive2 = new File(getClass().getResource("deb/data.tar.bz2").toURI());
+        File archive3 = new File(getClass().getResource("deb/data.zip").toURI());
+        File directory = new File(getClass().getResource("deb/data").toURI());
+
+        DataProducer[] data = new DataProducer[] {
+            new DataProducerArchive(archive1, null, null, null),
+            new DataProducerArchive(archive2, null, null, null),
+            new DataProducerArchive(archive3, null, null, null),
+            new DataProducerDirectory(directory, null, new String[] { "**/.svn/**" }, null),
+            new DataProducerLink("/link/path-element.ext", "/link/target-element.ext", true, null, null, null)
+        };
+
+        File deb = File.createTempFile("jdeb", ".deb");
+
+        DebMaker maker = new DebMaker(new NullConsole(), Arrays.asList(data));
+        maker.setControl(new File(getClass().getResource("deb/control").toURI()));
+        maker.setDeb(deb);
+        
+        BinaryPackageControlFile packageControlFile = maker.createDeb(Compression.GZIP);
+        
+        assertTrue(packageControlFile.isValid());
+
+        final Map<String, TarArchiveEntry> filesInDeb = new HashMap<String, TarArchiveEntry>();
+        
+        ArchiveWalker.walkData(deb, new ArchiveVisitor<TarArchiveEntry>() {
+            public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+                filesInDeb.put(entry.getName(), entry);
+            }
+        }, Compression.GZIP);
+        
+        assertTrue("testfile wasn't found in the package", filesInDeb.containsKey("./test/testfile"));
+        assertTrue("testfile2 wasn't found in the package", filesInDeb.containsKey("./test/testfile2"));
+        assertTrue("testfile3 wasn't found in the package", filesInDeb.containsKey("./test/testfile3"));
+        assertTrue("testfile4 wasn't found in the package", filesInDeb.containsKey("./test/testfile4"));
+        assertTrue("/link/path-element.ext wasn't found in the package", filesInDeb.containsKey("./link/path-element.ext"));
+        assertEquals("/link/path-element.ext has wrong link target", "/link/target-element.ext", filesInDeb.get("./link/path-element.ext").getLinkName());
+
+        assertTrue("Cannot delete the file " + deb, deb.delete());
+    }
+
+    public void testControlFilesPermissions() throws Exception {
+        File deb = new File("target/test-classes/test-control.deb");
+        if (deb.exists() && !deb.delete()) {
+            fail("Couldn't delete " + deb);
+        }
+        
+        Collection<DataProducer> producers = Arrays.asList(new DataProducer[] {new EmptyDataProducer()});
+        DebMaker maker = new DebMaker(new NullConsole(), producers);
+        maker.setDeb(deb);
+        maker.setControl(new File("target/test-classes/org/vafer/jdeb/deb/control"));
+        
+        maker.createDeb(Compression.NONE);
+        
+        // now reopen the package and check the control files
+        assertTrue("package not build", deb.exists());
+        
+        boolean found = ArchiveWalker.walkControl(deb, new ArchiveVisitor<TarArchiveEntry>() {
+            public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+                assertFalse("directory found in the control archive", entry.isDirectory());
+                assertTrue("prefix", entry.getName().startsWith("./"));
+                
+                InformationInputStream infoStream = new InformationInputStream(new ByteArrayInputStream(content));
+                IOUtils.copy(infoStream, NullOutputStream.NULL_OUTPUT_STREAM);
+                
+                if (infoStream.isShell()) {
+                    assertTrue("Permissions on " + entry.getName() + " should be 755", entry.getMode() == 0755);
+                } else {
+                    assertTrue("Permissions on " + entry.getName() + " should be 644", entry.getMode() == 0644);
+                }
+                
+                assertTrue(entry.getName() + " doesn't have Unix line endings", infoStream.hasUnixLineEndings());
+                
+                assertEquals("user", "root", entry.getUserName());
+                assertEquals("group", "root", entry.getGroupName());
+            }
+        });
+        
+        assertTrue("Control files not found in the package", found);
+    }
+
+    public void testControlFilesVariables() throws Exception {
+        File deb = new File("target/test-classes/test-control.deb");
+        if (deb.exists() && !deb.delete()) {
+            fail("Couldn't delete " + deb);
+        }
+        
+        Map<String, String> variables = new HashMap<String, String>();
+        variables.put("name", "jdeb");
+        variables.put("version", "1.0");
+        
+        Collection<DataProducer> producers = Arrays.asList(new DataProducer[] {new EmptyDataProducer()});
+        DebMaker maker = new DebMaker(new NullConsole(), producers);
+        maker.setDeb(deb);
+        maker.setControl(new File("target/test-classes/org/vafer/jdeb/deb/control"));
+        maker.setResolver(new MapVariableResolver(variables));
+        
+        maker.createDeb(Compression.NONE);
+        
+        // now reopen the package and check the control files
+        assertTrue("package not build", deb.exists());
+                
+        boolean found = ArchiveWalker.walkControl(deb, new ArchiveVisitor<TarArchiveEntry>() {
+            public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+                if (entry.getName().contains("postinst") || entry.getName().contains("prerm")) {
+                    String body = new String(content, "ISO-8859-1");
+                    assertFalse("Variables not replaced in the control file " + entry.getName(), body.contains("[[name]] [[version]]"));
+                    assertTrue("Expected variables not found in the control file " + entry.getName(), body.contains("jdeb 1.0"));
+                }
+            }
+        });
+        
+        assertTrue("Control files not found in the package", found);
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/EmptyDataProducer.java b/src/test/java/org/vafer/jdeb/EmptyDataProducer.java
new file mode 100644
index 0000000..9b38ca6
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/EmptyDataProducer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+import java.io.IOException;
+
+/**
+ * @author Emmanuel Bourg
+ */
+public class EmptyDataProducer implements DataProducer {
+
+    public void produce(DataConsumer receiver) throws IOException {
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/NullConsole.java b/src/test/java/org/vafer/jdeb/NullConsole.java
new file mode 100644
index 0000000..97a8f85
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/NullConsole.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb;
+
+public class NullConsole implements Console {
+
+    @Override
+    public void info(String s) {
+    }
+
+    @Override
+    public void warn(String s) {
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/ant/AntSelectorTestCase.java b/src/test/java/org/vafer/jdeb/ant/AntSelectorTestCase.java
new file mode 100644
index 0000000..05beec9
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/ant/AntSelectorTestCase.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.ant;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
+public final class AntSelectorTestCase extends TestCase {
+
+    public void testExclusion() throws Exception {
+        assertTrue("should match",
+            SelectorUtils.matchPath("**/bin/**", "/some/bin/stuff"));
+        assertFalse("should not match",
+            SelectorUtils.matchPath("**/bin/**", "/some/stuff"));
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/ant/DebAntTaskTestCase.java b/src/test/java/org/vafer/jdeb/ant/DebAntTaskTestCase.java
new file mode 100644
index 0000000..ee553ca
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/ant/DebAntTaskTestCase.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.ant;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.TestCase;
+import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
+import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.tar.TarInputStream;
+import org.vafer.jdeb.ArchiveVisitor;
+import org.vafer.jdeb.ArchiveWalker;
+import org.vafer.jdeb.Compression;
+
+/**
+ * @author Emmanuel Bourg
+ */
+public final class DebAntTaskTestCase extends TestCase {
+
+    private Project project;
+
+    protected void setUp() throws Exception {
+        project = new Project();
+        project.setCoreLoader(getClass().getClassLoader());
+        project.init();
+
+        File buildFile = new File("target/test-classes/testbuild.xml");
+        project.setBaseDir(buildFile.getParentFile());
+
+        final ProjectHelper helper = ProjectHelper.getProjectHelper();
+        helper.parse(project, buildFile);
+
+        // remove the package previously build
+        File deb = new File("target/test.deb");
+        if (deb.exists()) {
+            assertTrue("Unable to remove the test archive", deb.delete());
+        }
+    }
+
+    public void testMissingControl() {
+        try {
+            project.executeTarget("missing-control");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testInvalidControl() {
+        try {
+            project.executeTarget("invalid-control");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testMissingDestFile() {
+        try {
+            project.executeTarget("missing-destfile");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testEmptyPackage() {
+        try {
+            project.executeTarget("empty-package");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testPackageWithArchive() {
+        project.executeTarget("with-archive");
+
+        assertTrue("package not build", new File("target/test-classes/test.deb").exists());
+    }
+
+    public void testPackageWithMissingArchive() {
+        try {
+            project.executeTarget("with-missing-archive");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testPackageWithDirectory() {
+        project.executeTarget("with-directory");
+
+        assertTrue("package not build", new File("target/test-classes/test.deb").exists());
+    }
+
+    public void testPackageWithMissingDirectory() {
+        try {
+            project.executeTarget("with-missing-directory");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Redirects the Ant output to the specified stream.
+     */
+    private void redirectOutput( OutputStream out ) {
+        DefaultLogger logger = new DefaultLogger();
+        logger.setOutputPrintStream(new PrintStream(out));
+        logger.setMessageOutputLevel(Project.MSG_INFO);
+        project.addBuildListener(logger);
+    }
+
+    public void testVerboseEnabled() {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        redirectOutput(out);
+
+        project.executeTarget("verbose-enabled");
+
+        assertTrue(out.toString().contains("Total size"));
+    }
+
+    public void testVerboseDisabled() {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        redirectOutput(out);
+
+        project.executeTarget("verbose-disabled");
+
+        assertTrue(!out.toString().contains("Total size"));
+    }
+
+    public void testMissingDataType() {
+        try {
+            project.executeTarget("missing-data-type");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testUnknownDataType() {
+        try {
+            project.executeTarget("unknown-data-type");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testFileSet() {
+        project.executeTarget("fileset");
+
+        assertTrue("package not build", new File("target/test-classes/test.deb").exists());
+    }
+
+    public void testTarFileSet() throws Exception {
+        project.executeTarget("tarfileset");
+
+        File deb = new File("target/test-classes/test.deb");
+        assertTrue("package not build", deb.exists());
+
+        ArchiveWalker.walkData(deb, new ArchiveVisitor<TarArchiveEntry>() {
+            public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+                assertTrue("prefix: " + entry.getName(), entry.getName().startsWith("./foo/"));
+                if (entry.isDirectory()) {
+                    assertEquals("directory mode (" + entry.getName() + ")", 040700, entry.getMode());
+                } else {
+                    assertEquals("file mode (" + entry.getName() + ")", 0100600, entry.getMode());
+                }
+                assertEquals("user", "ebourg", entry.getUserName());
+                assertEquals("group", "ebourg", entry.getGroupName());
+            }
+        }, Compression.GZIP);
+    }
+
+    public void testLink() throws Exception {
+        project.executeTarget("link");
+        
+        File deb = new File("target/test-classes/test.deb");
+        assertTrue("package not build", deb.exists());
+        
+        final AtomicBoolean linkFound = new AtomicBoolean(false);
+        
+        ArchiveWalker.walkData(deb, new ArchiveVisitor<TarArchiveEntry>() {
+            public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+                if (entry.isSymbolicLink()) {
+                    linkFound.set(true);
+                    assertEquals("link mode (" + entry.getName() + ")", 0120755, entry.getMode());
+                }
+                assertEquals("user", "ebourg", entry.getUserName());
+                assertEquals("group", "ebourg", entry.getGroupName());
+            }
+        }, Compression.GZIP);
+        
+        assertTrue("Link not found", linkFound.get());
+    }
+
+    public void testUnkownCompression() throws Exception {
+        try {
+            project.executeTarget("unknown-compression");
+            fail("No exception thrown");
+        } catch (BuildException e) {
+            // expected
+        }
+    }
+
+    public void testBZip2Compression() throws Exception {
+        project.executeTarget("bzip2-compression");
+
+        File deb = new File("target/test-classes/test.deb");
+        assertTrue("package not build", deb.exists());
+
+        final AtomicBoolean found = new AtomicBoolean(false); 
+        
+        ArArchiveInputStream in = new ArArchiveInputStream(new FileInputStream(deb));
+        ArchiveWalker.walk(in, new ArchiveVisitor<ArArchiveEntry>() {
+            public void visit(ArArchiveEntry entry, byte[] content) throws IOException {
+                if (entry.getName().equals("data.tar.bz2")) {
+                    found.set(true);
+
+                    assertEquals("header 0", (byte) 'B', content[0]);
+                    assertEquals("header 1", (byte) 'Z', content[1]);
+
+                    TarInputStream tar = new TarInputStream(new BZip2CompressorInputStream(new ByteArrayInputStream(content)));
+                    while ((tar.getNextEntry()) != null) ;
+                    tar.close();
+                }
+            }
+        });
+        
+        assertTrue("bz2 file not found", found.get());
+    }
+
+    public void testXZCompression() throws Exception {
+        project.executeTarget("xz-compression");
+
+        File deb = new File("target/test-classes/test.deb");
+        assertTrue("package not build", deb.exists());
+
+        final AtomicBoolean found = new AtomicBoolean(false); 
+        
+        ArArchiveInputStream in = new ArArchiveInputStream(new FileInputStream(deb));
+        ArchiveWalker.walk(in, new ArchiveVisitor<ArArchiveEntry>() {
+            public void visit(ArArchiveEntry entry, byte[] content) throws IOException {
+                if (entry.getName().equals("data.tar.xz")) {
+                    found.set(true);
+                    
+                    assertEquals("header 0", (byte) 0xFD, content[0]);
+                    assertEquals("header 1", (byte) '7', content[1]);
+                    assertEquals("header 2", (byte) 'z', content[2]);
+                    assertEquals("header 3", (byte) 'X', content[3]);
+                    assertEquals("header 4", (byte) 'Z', content[4]);
+                    assertEquals("header 5", (byte) '\0', content[5]);
+                    
+                    TarInputStream tar = new TarInputStream(new XZCompressorInputStream(new ByteArrayInputStream(content)));
+                    while ((tar.getNextEntry()) != null) ;
+                    tar.close();
+                }
+            }
+        });
+        
+        assertTrue("xz file not found", found.get());
+    }
+
+    public void testNoCompression() throws Exception {
+        project.executeTarget("no-compression");
+
+        File deb = new File("target/test-classes/test.deb");
+        assertTrue("package not build", deb.exists());
+
+        boolean found = ArchiveWalker.walkData(deb, new ArchiveVisitor<TarArchiveEntry>() {
+            public void visit(TarArchiveEntry entry, byte[] content) throws IOException {
+            }
+        }, Compression.NONE);
+
+        assertTrue("tar file not found", found);
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/changes/TextfileChangesProviderTestCase.java b/src/test/java/org/vafer/jdeb/changes/TextfileChangesProviderTestCase.java
new file mode 100644
index 0000000..e36a3d9
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/changes/TextfileChangesProviderTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.changes;
+
+import java.io.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+
+public final class TextfileChangesProviderTestCase extends TestCase {
+
+    public void testParsing() throws Exception {
+
+        final String input =
+            " * change1\n" +
+                " * change2\n" +
+                "release date=14:00 13.01.2007, version=12324, urgency=low, by=tcurdt at joost.com\n" +
+                " * change1\n" +
+                " * change2\n" +
+                "release date=12:00 10.01.2007, version=10324, urgency=low, by=tcurdt at joost.com\n" +
+                " * change1\n" +
+                " * change2\n";
+
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile();
+        packageControlFile.set("Package", "package");
+        packageControlFile.set("Version", "version");
+        packageControlFile.set("Distribution", "distribution");
+        packageControlFile.set("Date", "Mon, 20 Aug 2007 15:25:57 +0200");
+
+        final TextfileChangesProvider provider = new TextfileChangesProvider(new ByteArrayInputStream(input.getBytes("UTF-8")), packageControlFile);
+        final ChangeSet[] changeSets = provider.getChangesSets();
+
+        assertNotNull(changeSets);
+        assertEquals(3, changeSets.length);
+    }
+
+    public void testDistributionFromChangesProvider() throws Exception {
+
+        final String input =
+            "release distribution=production\n" +
+                " * change1\n" +
+                " * change2\n" +
+                "release distribution=staging, date=14:00 13.01.2007, version=12324, urgency=low, by=tcurdt at joost.com\n" +
+                " * change1\n" +
+                " * change2\n" +
+                "release distribution=development, date=12:00 10.01.2007, version=10324, urgency=low, by=tcurdt at joost.com\n" +
+                " * change1\n" +
+                " * change2\n";
+
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile();
+        packageControlFile.set("Package", "package");
+        packageControlFile.set("Version", "version");
+        packageControlFile.set("Date", "Mon, 20 Aug 2007 15:25:57 +0200");
+
+        final TextfileChangesProvider provider = new TextfileChangesProvider(new ByteArrayInputStream(input.getBytes("UTF-8")), packageControlFile);
+        final ChangeSet[] changeSets = provider.getChangesSets();
+
+        assertNotNull(changeSets);
+        assertEquals(3, changeSets.length);
+
+        assertEquals("production", changeSets[0].getDistribution());
+        assertEquals("staging", changeSets[1].getDistribution());
+        assertEquals("development", changeSets[2].getDistribution());
+    }
+
+}
diff --git a/src/test/java/org/vafer/jdeb/debian/ChangesFileTestCase.java b/src/test/java/org/vafer/jdeb/debian/ChangesFileTestCase.java
new file mode 100644
index 0000000..c8e68aa
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/debian/ChangesFileTestCase.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import junit.framework.TestCase;
+import org.vafer.jdeb.changes.ChangeSet;
+
+public final class ChangesFileTestCase extends TestCase {
+
+    public void testToString() throws Exception {
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile();
+        packageControlFile.set("Package", "test-package");
+        packageControlFile.set("Description", "This is\na description\non several lines");
+        packageControlFile.set("Version", "1.0");
+        
+        ChangesFile changes = new ChangesFile();
+        changes.setChanges(new ChangeSet[0]);
+        changes.initialize(packageControlFile);
+        
+        assertEquals("1.0", changes.get("Version"));
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/debian/ControlFieldTestCase.java b/src/test/java/org/vafer/jdeb/debian/ControlFieldTestCase.java
new file mode 100644
index 0000000..35ce579
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/debian/ControlFieldTestCase.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Emmanuel Bourg
+ * @version $Revision$, $Date$
+ */
+public class ControlFieldTestCase extends TestCase {
+
+    public void testFormatSimpleValue() {
+        ControlField field = new ControlField("Field-Name");
+        
+        assertEquals("Field-Name: value\n", field.format("value"));
+    }
+    
+    public void testFormatMultilineValue1() {
+        ControlField field = new ControlField("Field-Name", false, ControlField.Type.MULTILINE);
+        
+        assertEquals("Field-Name: value1\n value2\n .\n value3\n", field.format("value1\nvalue2\n\nvalue3"));
+    }
+    
+    public void testFormatMultilineValue2() {
+        ControlField field = new ControlField("Field-Name", false, ControlField.Type.MULTILINE, true);
+        
+        assertEquals("Field-Name:\n value1\n value2\n .\n value3\n", field.format("value1\nvalue2\n\nvalue3"));
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/debian/PackageControlFileTestCase.java b/src/test/java/org/vafer/jdeb/debian/PackageControlFileTestCase.java
new file mode 100644
index 0000000..7c18587
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/debian/PackageControlFileTestCase.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.debian;
+
+import java.io.FileInputStream;
+import java.text.ParseException;
+
+import junit.framework.TestCase;
+
+public final class PackageControlFileTestCase extends TestCase {
+
+    public void testParse() throws Exception {
+        String input =
+                "Key1: Value1\n" +
+                "Key2: Value2\n" +
+                " Value2.1\n" +
+                " Value2.2\n" +
+                "Key3: Value3\n";
+        
+        BinaryPackageControlFile d = new BinaryPackageControlFile(input);
+        assertFalse(d.isValid());
+
+        assertEquals("key 1", "Value1", d.get("Key1"));
+        assertEquals("key 2", "Value2\nValue2.1\nValue2.2", d.get("Key2"));
+        assertEquals("key 3", "Value3", d.get("Key3"));
+    }
+
+    public void testToString() throws Exception {
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile();
+        packageControlFile.set("Package", "test-package");
+        packageControlFile.set("Description", "This is\na description\non several lines");
+        packageControlFile.set("Version", "1.0");
+        
+        String s = packageControlFile.toString();
+        
+        BinaryPackageControlFile packageControlFile2 = new BinaryPackageControlFile(s);
+        assertEquals("Package", packageControlFile.get("Package"), packageControlFile2.get("Package"));
+        assertEquals("Description", packageControlFile.get("Description"), packageControlFile2.get("Description"));
+        assertEquals("Version 3", packageControlFile.get("Version"), packageControlFile2.get("Version"));
+    }
+
+    public void testEmptyLines() throws Exception {
+        String input =
+                "Key1: Value1\n" +
+                "Key2: Value2\n" +
+                "\n";
+        try {
+            new BinaryPackageControlFile(input);
+            fail("Should throw a ParseException");
+        } catch (ParseException e) {
+        }
+    }
+    
+    public void testGetShortDescription() {
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile();
+        
+        assertNull(packageControlFile.getShortDescription());
+        
+        packageControlFile.set("Description", "This is the short description\nThis is the loooooong description");
+        
+        assertEquals("short description", "This is the short description", packageControlFile.getShortDescription());
+        
+        packageControlFile.set("Description", "\nThere is no short description");
+        
+        assertEquals("short description", "", packageControlFile.getShortDescription());
+    }
+
+    public void testGetDescription() throws Exception {
+        BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile();
+        packageControlFile.parse(new FileInputStream("target/test-classes/org/vafer/jdeb/deb/control/control"));
+        
+        assertEquals("Description", "revision @REVISION@, test package\n" +
+                "This is a sample package control file.\n\n" +
+                "Use for testing purposes only.",
+                packageControlFile.get("Description"));
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/mapping/LsMapperTestCase.java b/src/test/java/org/vafer/jdeb/mapping/LsMapperTestCase.java
new file mode 100644
index 0000000..309759b
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/mapping/LsMapperTestCase.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.mapping;
+
+import java.io.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.vafer.jdeb.mapping.LsMapper.ParseError;
+
+public final class LsMapperTestCase extends TestCase {
+
+    private final static String output =
+        "total 0\n" +
+            "drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .\n" +
+            "drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..\n" +
+            "\n" +
+            "./trunk/target/test-classes/org/vafer/dependency:\n" +
+            "total 176\n" +
+            "drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .\n" +
+            "drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..\n" +
+            "-rw-r--r--    1 tcurdt  tcurdt  2934 Jun 25 03:48 DependenciesTestCase.class\n" +
+            "-rw-r--r--    1 tcurdt  tcurdt   786 Jun 25 03:48 JarCombiningTestCase$1.class\n" +
+            "drwxr-xr-x    4 tcurdt  tcurdt   136 Jun 25 03:48 classes\n" +
+            "\n" +
+            "./trunk/src/test-classes/org/vafer/dependency:\n" +
+            "total 76\n" +
+            "drwxr-xr-x   23 tcurdt  tcurdt   782 Jun 25 03:48 .\n" +
+            "drwxr-xr-x    3 tcurdt  tcurdt   102 Jun 25 03:48 ..\n" +
+            "-rw-r--r--    1 tcurdt  tcurdt  2934 Jun 25 03:48 DependenciesTestCase.class\n" +
+            "-rw-r--r--    1 tcurdt  tcurdt   786 Jun 25 03:48 JarCombiningTestCase$1.class\n" +
+            "drwxr-xr-x    4 tcurdt  tcurdt   136 Jun 25 03:48 classes\n" +
+            "\n";
+
+    public void testModes() throws Exception {
+        final ByteArrayInputStream is = new ByteArrayInputStream(output.getBytes("UTF-8"));
+
+        final Mapper mapper = new LsMapper(is);
+
+        final TarArchiveEntry entry1 = mapper.map(new TarArchiveEntry("trunk/target/test-classes/org/vafer/dependency", true));
+
+        assertEquals(493, entry1.getMode());
+        assertEquals("tcurdt", entry1.getUserName());
+        assertEquals("tcurdt", entry1.getGroupName());
+
+        final TarArchiveEntry entry2 = mapper.map(new TarArchiveEntry("trunk/target/test-classes/org/vafer/dependency/DependenciesTestCase.class", true));
+
+        assertEquals(420, entry2.getMode());
+        assertEquals("tcurdt", entry2.getUserName());
+        assertEquals("tcurdt", entry2.getGroupName());
+    }
+
+    public void testSuccessfulParsing() throws Exception {
+        final ByteArrayInputStream is = new ByteArrayInputStream(output.getBytes("UTF-8"));
+
+        final Mapper mapper = new LsMapper(is);
+
+        final TarArchiveEntry unknown = new TarArchiveEntry("xyz", true);
+        assertSame(unknown, mapper.map(unknown));
+
+        final TarArchiveEntry known = new TarArchiveEntry("trunk/target/test-classes/org/vafer/dependency", true);
+        final TarArchiveEntry knownMapped = mapper.map(known);
+
+        assertNotSame(known, knownMapped);
+
+    }
+
+    public void testPrematureEOF() throws Exception {
+        final ByteArrayInputStream is = new ByteArrayInputStream(output.substring(0, 200).getBytes("UTF-8"));
+
+        try {
+            new LsMapper(is);
+            fail("should fail to parse");
+        } catch (ParseError e) {
+        }
+    }
+
+    public void testWrongFormat() throws Exception {
+        final ByteArrayInputStream is = new ByteArrayInputStream("asas\n".getBytes("UTF-8"));
+
+        try {
+            new LsMapper(is);
+            fail("should fail to parse");
+        } catch (ParseError e) {
+        }
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/maven/DataTestCase.java b/src/test/java/org/vafer/jdeb/maven/DataTestCase.java
new file mode 100644
index 0000000..fb89491
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/maven/DataTestCase.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.maven;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+/*
+ * Admittedly not the nicest way to assert that failOnMissingSrc functions. However, the best that can be done without
+ * refactoring, mocking, or extending the scope of the test beyond this unit.
+ */
+public class DataTestCase extends TestCase {
+
+    private Data data;
+    private File missingFile;
+    private File file;
+
+    protected void setUp() throws Exception {
+        data = new Data();
+        missingFile = new File("this-file-does-not-exist");
+        file = File.createTempFile(getClass().getSimpleName(), "dat");
+    }
+
+    protected void tearDown() throws Exception {
+        if (file != null) {
+            file.delete();
+        }
+    }
+
+    public void testFailOnUnknownValue() throws IOException {
+        try {
+            data.setSrc(missingFile);
+            data.setMissingSrc("not a value value");
+            data.produce(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testFailOnMissingSrcDefaultFileMissing() throws IOException {
+        try {
+            data.setSrc(missingFile);
+            data.produce(null);
+            fail();
+        } catch (FileNotFoundException expected) {
+        }
+    }
+
+    public void testFailOnMissingSrcIgnoreFileMissing() throws IOException {
+        data.setSrc(missingFile);
+        data.setMissingSrc("ignore");
+        data.produce(null);
+    }
+
+    public void testFailOnMissingSrcIgnoreFileMissingVaryInput() throws IOException {
+        data.setSrc(missingFile);
+        data.setMissingSrc(" IGNORE ");
+        data.produce(null);
+    }
+
+    public void testFailOnMissingSrcFailFileMissing() throws IOException {
+        try {
+            data.setSrc(missingFile);
+            data.setMissingSrc("fail");
+            data.produce(null);
+            fail();
+        } catch (FileNotFoundException expected) {
+        }
+    }
+
+    public void testFailOnMissingSrcDefaultFileExists() throws IOException {
+        IOException unknownTypeException = null;
+        try {
+            data.setSrc(file);
+            data.produce(null);
+        } catch (IOException expected) {
+            unknownTypeException = expected;
+        }
+        assertTrue(unknownTypeException.getMessage().startsWith("Unknown type"));
+    }
+
+    public void testFailOnMissingSrcIgnoreFileExists() throws IOException {
+        IOException unknownTypeException = null;
+        try {
+            data.setSrc(file);
+            data.setMissingSrc("ignore");
+            data.produce(null);
+        } catch (IOException expected) {
+            unknownTypeException = expected;
+        }
+        assertTrue(unknownTypeException.getMessage().startsWith("Unknown type"));
+    }
+
+    public void testFailOnMissingSrcFailFileExists() throws IOException {
+        IOException unknownTypeException = null;
+        try {
+            data.setSrc(file);
+            data.setMissingSrc("fail");
+            data.produce(null);
+        } catch (IOException expected) {
+            unknownTypeException = expected;
+        }
+        assertTrue(unknownTypeException.getMessage().startsWith("Unknown type"));
+    }
+
+}
diff --git a/src/test/java/org/vafer/jdeb/producers/DataProducerPathTemplateTestCase.java b/src/test/java/org/vafer/jdeb/producers/DataProducerPathTemplateTestCase.java
new file mode 100644
index 0000000..60ac425
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/producers/DataProducerPathTemplateTestCase.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.producers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.vafer.jdeb.DataConsumer;
+import org.vafer.jdeb.DataProducer;
+import org.vafer.jdeb.mapping.Mapper;
+
+public class DataProducerPathTemplateTestCase extends TestCase {
+
+    private static final String[] INCLUDES = { };
+    private static final String[] EXCLUDES = { };
+
+    private CaptureDataConsumer captureDataConsumer = new CaptureDataConsumer();
+
+    private Mapper[] mappers = new Mapper[0];
+    private DataProducer dataProducer;
+
+    public void testTypical() throws Exception {
+
+        String[] paths = { "/var/log", "/var/lib" };
+        dataProducer = new DataProducerPathTemplate(paths, INCLUDES, EXCLUDES, mappers);
+        dataProducer.produce(captureDataConsumer);
+
+        assertEquals(2, captureDataConsumer.invocations.size());
+
+        CaptureDataConsumer.Invocation invocation = captureDataConsumer.invocations.get(0);
+        assertEquals(invocation.dirname, "/var/log");
+        assertEquals(invocation.gid, 0);
+        assertEquals(invocation.group, "root");
+        assertEquals(invocation.linkname, "");
+        assertEquals(invocation.mode, TarArchiveEntry.DEFAULT_DIR_MODE);
+        assertEquals(invocation.size, 0);
+        assertEquals(invocation.uid, 0);
+        assertEquals(invocation.user, "root");
+
+        invocation = captureDataConsumer.invocations.get(1);
+        assertEquals(invocation.dirname, "/var/lib");
+        assertEquals(invocation.gid, 0);
+        assertEquals(invocation.group, "root");
+        assertEquals(invocation.linkname, "");
+        assertEquals(invocation.mode, TarArchiveEntry.DEFAULT_DIR_MODE);
+        assertEquals(invocation.size, 0);
+        assertEquals(invocation.uid, 0);
+        assertEquals(invocation.user, "root");
+    }
+
+    public class CaptureDataConsumer implements DataConsumer {
+
+        private List<Invocation> invocations;
+
+        public CaptureDataConsumer() {
+            invocations = new ArrayList<Invocation>();
+        }
+
+        @Override
+        public void onEachDir( String dirname, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
+            invocations.add(new Invocation(dirname, linkname, user, uid, group, gid, mode, size));
+        }
+
+        @Override
+        public void onEachFile( InputStream input, String filename, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
+        }
+
+        @Override
+        public void onEachLink(String path, String linkName, boolean symlink, String user, int uid, String group, int gid, int mode) throws IOException {
+        }
+
+        private class Invocation {
+
+            private String dirname;
+            private String linkname;
+            private String user;
+            private int uid;
+            private String group;
+            private int gid;
+            private int mode;
+            private long size;
+
+            private Invocation( String dirname, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
+                this.dirname = dirname;
+                this.linkname = linkname;
+                this.user = user;
+                this.uid = uid;
+                this.group = group;
+                this.gid = gid;
+                this.mode = mode;
+                this.size = size;
+            }
+
+        }
+
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/signing/PGPSignerTestCase.java b/src/test/java/org/vafer/jdeb/signing/PGPSignerTestCase.java
new file mode 100644
index 0000000..0e1c087
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/signing/PGPSignerTestCase.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.signing;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+public final class PGPSignerTestCase extends TestCase {
+
+    public void testClearSign() throws Exception {
+
+        final InputStream ring = getClass().getClassLoader().getResourceAsStream("org/vafer/gpg/secring.gpg");
+
+        assertNotNull(ring);
+
+        String input = "TEST1 \n-TEST2 \n  \nTEST3 \n";
+
+        final String expectedOutputStr =
+            "-----BEGIN PGP SIGNED MESSAGE-----\n" +
+                "Hash: SHA1\n" +
+                "\n" +
+                "TEST1\n" +
+                "- -TEST2\n" +
+                "\n" +
+                "TEST3\n" +
+                "-----BEGIN PGP SIGNATURE-----\n" +
+                "Version: BCPG v1.48\n" +
+                "\n" +
+                "iEYEARECABAFAkax1rgJEHM9pIAuB02PAABIJgCghFmoCJCZ0CGiqgVLGGPd/Yh5\n" +
+                "FQQAnRVqvI2ij45JQSHYJBblZ0Vv2meN\n" +
+                "=aAAT\n" +
+                "-----END PGP SIGNATURE-----\n";
+
+        final byte[] expectedOutput = expectedOutputStr.getBytes("UTF-8");
+
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        
+        PGPSigner signer = new PGPSigner(ring, "2E074D8F", "test");
+        signer.clearSign(input, os);
+        
+        final byte[] output = fixCRLF(os.toByteArray());
+
+        final int from = expectedOutputStr.indexOf("iEYEAREC");
+        final int until = expectedOutputStr.indexOf("=aAAT") + 5;
+        Arrays.fill(output, from, until, (byte) '?');
+        Arrays.fill(expectedOutput, from, until, (byte) '?');
+
+        assertEquals(new String(expectedOutput), new String(output));
+    }
+
+    private byte[] fixCRLF(byte[] b) {
+        String s = new String(b);
+        s = s.replaceAll("\r\n", "\n");
+        return s.getBytes();
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/utils/FilteredFileTestCase.java b/src/test/java/org/vafer/jdeb/utils/FilteredFileTestCase.java
new file mode 100644
index 0000000..e2d6eb8
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/utils/FilteredFileTestCase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.vafer.jdeb.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.util.ReaderInputStream;
+import org.vafer.jdeb.debian.BinaryPackageControlFile;
+
+public class FilteredFileTestCase extends TestCase {
+
+    private VariableResolver variableResolver;
+
+    protected void setUp() throws Exception {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("artifactId", "jdeb");
+        map.put("myProperty1", "custom1");
+        map.put("myProperty2", "custom2");
+        variableResolver = new MapVariableResolver(map);
+    }
+
+    public void testTokenSubstitution() throws Exception {
+        InputStream in = new ReaderInputStream(new StringReader("#!/bin/sh\ncat [[artifactId]][[myProperty1]] \necho '[[myProperty2]]'\n"));
+
+        FilteredFile placeHolder = new FilteredFile(in, variableResolver);
+
+        String actual = placeHolder.toString();
+        assertEquals("#!/bin/sh\ncat jdebcustom1 \necho 'custom2'\n", actual);
+    }
+
+    public void testVariableSubstitution() throws Exception {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("VERSION", "1.2");
+        map.put("MAINTAINER", "Torsten Curdt <tcurdt at vafer.org>");
+        
+        String controlFile = 
+                "Version: [[VERSION]]\n"
+                + "Maintainer: [[MAINTAINER]]\n"
+                + "NoResolve1: test[[test\n"
+                + "NoResolve2: [[test]]\n";
+
+        FilteredFile filteredFile = new FilteredFile(new ByteArrayInputStream(controlFile.getBytes()), new MapVariableResolver(map));
+        
+        BinaryPackageControlFile d = new BinaryPackageControlFile(filteredFile.toString());
+        
+        assertEquals("1.2", d.get("Version"));
+        assertEquals("Torsten Curdt <tcurdt at vafer.org>", d.get("Maintainer"));
+        assertEquals("test[[test", d.get("NoResolve1"));
+        assertEquals("[[test]]", d.get("NoResolve2"));
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/utils/InformationInputStreamTestCase.java b/src/test/java/org/vafer/jdeb/utils/InformationInputStreamTestCase.java
new file mode 100644
index 0000000..478e5a6
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/utils/InformationInputStreamTestCase.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.utils;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+public class InformationInputStreamTestCase extends TestCase {
+
+    private InputStream getStream( String file ) {
+        return getClass().getClassLoader().getResourceAsStream("org/vafer/jdeb/utils/" + file);
+    }
+
+    public void testUTF8CRLF() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf8-crlf.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Should be windows line endings", !informationStream.hasUnixLineEndings());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM detected", !informationStream.hasBom());
+        assertEquals("Encoding", null, informationStream.getEncoding());
+    }
+
+    public void testUTF8() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf8-lf.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM detected", !informationStream.hasBom());
+        assertEquals("Encoding", null, informationStream.getEncoding());
+    }
+
+    public void testUTF8BOM() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf8-lf-bom.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM not detected", informationStream.hasBom());
+        assertEquals("Encoding", "UTF-8", informationStream.getEncoding());
+    }
+
+    public void testUTF16BE() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf16be-lf.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM detected", !informationStream.hasBom());
+        assertEquals("Encoding", "UTF-16BE", informationStream.getEncoding());
+    }
+
+    public void testUTF16BEBOM() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf16be-lf-bom.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM not detected", informationStream.hasBom());
+        assertEquals("Encoding", "UTF-16BE", informationStream.getEncoding());
+    }
+
+    public void testUTF16LE() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf16le-lf.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM detected", !informationStream.hasBom());
+        assertEquals("Encoding", "UTF-16LE", informationStream.getEncoding());
+    }
+
+    public void testUTF16LEBOM() throws Exception {
+        InformationInputStream informationStream = new InformationInputStream(getStream("utf16le-lf-bom.txt"));
+        Utils.copy(informationStream, new ByteArrayOutputStream());
+        assertTrue("Shebang not detected", informationStream.isShell());
+        assertTrue("BOM not detected", informationStream.hasBom());
+        assertEquals("Encoding", "UTF-16LE", informationStream.getEncoding());
+    }
+}
diff --git a/src/test/java/org/vafer/jdeb/utils/UtilsTestCase.java b/src/test/java/org/vafer/jdeb/utils/UtilsTestCase.java
new file mode 100644
index 0000000..e7b4777
--- /dev/null
+++ b/src/test/java/org/vafer/jdeb/utils/UtilsTestCase.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2013 The jdeb developers.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.vafer.jdeb.utils;
+
+
+import java.io.ByteArrayInputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import junit.framework.TestCase;
+
+public class UtilsTestCase extends TestCase {
+
+    public void testStripPath() throws Exception {
+        assertEquals("foo/bar", Utils.stripPath(0, "foo/bar"));
+
+        assertEquals("bar", Utils.stripPath(1, "foo/bar"));
+
+        assertEquals("bar/baz", Utils.stripPath(1, "foo/bar/baz"));
+        assertEquals("baz", Utils.stripPath(2, "foo/bar/baz"));
+
+        assertEquals("foo/", Utils.stripPath(0, "foo/"));
+        assertEquals("", Utils.stripPath(1, "foo/"));
+        assertEquals("foo/", Utils.stripPath(2, "foo/"));
+    }
+
+    private String convert(String s) throws Exception {
+        byte[] data = Utils.toUnixLineEndings(new ByteArrayInputStream(s.getBytes("UTF-8")));
+        return new String(data, "UTF-8");
+    }
+
+    public void testNewlineConversionLF() throws Exception {
+        String expected = "test\ntest\n\ntest\n";
+        String actual = convert("test\ntest\n\ntest");
+        assertEquals(expected, actual);
+    }
+
+    public void testNewlineConversionCRLF() throws Exception {
+        String expected = "test\ntest\n\ntest\n";
+        String actual = convert("test\r\ntest\r\n\r\ntest");
+        assertEquals(expected, actual);
+    }
+
+    public void testNewlineConversionCR() throws Exception {
+        String expected = "test\ntest\n\ntest\n";
+        String actual = convert("test\rtest\r\rtest");
+        assertEquals(expected, actual);
+    }
+
+    public void testReplaceVariables() {
+        Map<String, String> variables = new HashMap<String, String>();
+        variables.put("version", "1.2.3");
+        variables.put("name", "jdeb");
+        variables.put("url", "https://github.com/tcurdt/jdeb");
+
+        VariableResolver resolver = new MapVariableResolver(variables);
+
+        // main case
+        String result = Utils.replaceVariables(resolver, "Version: [[version]]", "[[", "]]");
+        assertEquals("Version: 1.2.3", result);
+
+        // multiple variables in the same expression
+        result = Utils.replaceVariables(resolver, "[[name]] [[version]]", "[[", "]]");
+        assertEquals("jdeb 1.2.3", result);
+
+        // collision with script syntax
+        result = Utils.replaceVariables(resolver, "if [[ \"${HOST_TYPE}\" -eq \"admin\" ]] ; then", "[[", "]]");
+        assertEquals("if [[ \"${HOST_TYPE}\" -eq \"admin\" ]] ; then", result);
+
+        // mixed valid and unknown variables
+        result = Utils.replaceVariables(resolver, "[[name]] [[test]]", "[[", "]]");
+        assertEquals("jdeb [[test]]", result);
+    }
+
+    public void testVersionConversion() {
+        Calendar cal = new GregorianCalendar(2013, 02-1, 17);
+        assertEquals("should match", "1.0", Utils.convertToDebianVersion("1.0", null));
+        assertEquals("should match", "1.0~SNAPSHOT", Utils.convertToDebianVersion("1.0+SNAPSHOT", null));
+        assertEquals("should match", "1.0~20130217000000", Utils.convertToDebianVersion("1.0+SNAPSHOT", cal.getTime()));
+    }
+}
diff --git a/src/test/resources/org/vafer/gpg/fingerprint.txt b/src/test/resources/org/vafer/gpg/fingerprint.txt
new file mode 100644
index 0000000..07837c4
--- /dev/null
+++ b/src/test/resources/org/vafer/gpg/fingerprint.txt
@@ -0,0 +1,4 @@
+pub   1024D/2E074D8F 2007-08-02
+      Key fingerprint = 5A17 CB12 37F0 85BC 8264  E649 733D A480 2E07 4D8F
+uid                  Unit test user (only for testing) <test at vafer.org>
+sub   2048g/5E8CA506 2007-08-02
diff --git a/src/test/resources/org/vafer/gpg/pubring.gpg b/src/test/resources/org/vafer/gpg/pubring.gpg
new file mode 100644
index 0000000..c3c67a8
Binary files /dev/null and b/src/test/resources/org/vafer/gpg/pubring.gpg differ
diff --git a/src/test/resources/org/vafer/gpg/secring.gpg b/src/test/resources/org/vafer/gpg/secring.gpg
new file mode 100644
index 0000000..6658556
Binary files /dev/null and b/src/test/resources/org/vafer/gpg/secring.gpg differ
diff --git a/src/test/resources/org/vafer/jdeb/ar/data.ar b/src/test/resources/org/vafer/jdeb/ar/data.ar
new file mode 100644
index 0000000..531480f
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/ar/data.ar differ
diff --git a/src/test/resources/org/vafer/jdeb/changes/changes.txt b/src/test/resources/org/vafer/jdeb/changes/changes.txt
new file mode 100644
index 0000000..8d16f99
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/changes/changes.txt
@@ -0,0 +1,2 @@
+release date=20:13 17.08.2007,version=1.4+r89114,urgency=low,by=Torsten Curdt <torsten at vafer.org>
+ * debian changes support
\ No newline at end of file
diff --git a/src/test/resources/org/vafer/jdeb/deb/control/control b/src/test/resources/org/vafer/jdeb/deb/control/control
new file mode 100644
index 0000000..226eca2
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/deb/control/control
@@ -0,0 +1,12 @@
+Package: test
+Version: 1.0.1
+Section: misc
+Priority: optional
+Architecture: i386
+Depends: some-package
+Maintainer: John Doe <john at doe.org>
+Distribution: development
+Description: revision @REVISION@, test package
+ This is a sample package control file.
+ .
+ Use for testing purposes only.
diff --git a/src/test/resources/org/vafer/jdeb/deb/control/postinst b/src/test/resources/org/vafer/jdeb/deb/control/postinst
new file mode 100644
index 0000000..44c84cd
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/deb/control/postinst
@@ -0,0 +1,4 @@
+#!/bin/sh
+#
+# Post installation script for [[name]] [[version]]
+#
diff --git a/src/test/resources/org/vafer/jdeb/deb/control/prerm b/src/test/resources/org/vafer/jdeb/deb/control/prerm
new file mode 100644
index 0000000..475c5de
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/deb/control/prerm
@@ -0,0 +1,4 @@
+#!/bin/sh
+#
+# Pre removal script for [[name]] [[version]]
+#
diff --git a/src/test/resources/org/vafer/jdeb/deb/data.tar.bz2 b/src/test/resources/org/vafer/jdeb/deb/data.tar.bz2
new file mode 100644
index 0000000..f2e1e53
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/deb/data.tar.bz2 differ
diff --git a/src/test/resources/org/vafer/jdeb/deb/data.tgz b/src/test/resources/org/vafer/jdeb/deb/data.tgz
new file mode 100644
index 0000000..d208640
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/deb/data.tgz differ
diff --git a/src/test/resources/org/vafer/jdeb/deb/data.zip b/src/test/resources/org/vafer/jdeb/deb/data.zip
new file mode 100644
index 0000000..7186f01
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/deb/data.zip differ
diff --git a/src/test/resources/org/vafer/jdeb/deb/data/test/testfile b/src/test/resources/org/vafer/jdeb/deb/data/test/testfile
new file mode 100644
index 0000000..e7cbb71
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/deb/data/test/testfile
@@ -0,0 +1 @@
+testfile
\ No newline at end of file
diff --git a/src/test/resources/org/vafer/jdeb/deb/test/testfile4 b/src/test/resources/org/vafer/jdeb/deb/test/testfile4
new file mode 100644
index 0000000..e0a0ee6
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/deb/test/testfile4
@@ -0,0 +1 @@
+testfile4
\ No newline at end of file
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf16be-lf-bom.txt b/src/test/resources/org/vafer/jdeb/utils/utf16be-lf-bom.txt
new file mode 100644
index 0000000..ef566e6
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/utils/utf16be-lf-bom.txt differ
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf16be-lf.txt b/src/test/resources/org/vafer/jdeb/utils/utf16be-lf.txt
new file mode 100755
index 0000000..5004043
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/utils/utf16be-lf.txt differ
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf16le-lf-bom.txt b/src/test/resources/org/vafer/jdeb/utils/utf16le-lf-bom.txt
new file mode 100644
index 0000000..9445f81
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/utils/utf16le-lf-bom.txt differ
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf16le-lf.txt b/src/test/resources/org/vafer/jdeb/utils/utf16le-lf.txt
new file mode 100755
index 0000000..ade2a1a
Binary files /dev/null and b/src/test/resources/org/vafer/jdeb/utils/utf16le-lf.txt differ
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf8-crlf.txt b/src/test/resources/org/vafer/jdeb/utils/utf8-crlf.txt
new file mode 100644
index 0000000..0774808
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/utils/utf8-crlf.txt
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf8-lf-bom.txt b/src/test/resources/org/vafer/jdeb/utils/utf8-lf-bom.txt
new file mode 100755
index 0000000..3c55952
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/utils/utf8-lf-bom.txt
@@ -0,0 +1 @@
+#! /bin/sh
\ No newline at end of file
diff --git a/src/test/resources/org/vafer/jdeb/utils/utf8-lf.txt b/src/test/resources/org/vafer/jdeb/utils/utf8-lf.txt
new file mode 100644
index 0000000..ee600ae
--- /dev/null
+++ b/src/test/resources/org/vafer/jdeb/utils/utf8-lf.txt
@@ -0,0 +1 @@
+#! /bin/sh
\ No newline at end of file
diff --git a/src/test/resources/testbuild.xml b/src/test/resources/testbuild.xml
new file mode 100644
index 0000000..56acc50
--- /dev/null
+++ b/src/test/resources/testbuild.xml
@@ -0,0 +1,114 @@
+<project name="JDeb Ant tests">
+
+  <taskdef name="deb" classname="org.vafer.jdeb.ant.DebAntTask"/>
+
+  <target name="missing-control">
+    <deb destfile="test.deb"/>
+  </target>
+
+  <target name="invalid-control">
+    <deb destfile="test.deb" control="testbuild.xml"/>
+  </target>
+
+  <target name="missing-destfile">
+    <deb control="org/vafer/jdeb/deb/control">
+      <data src="org/vafer/jdeb/deb/invalid.tgz" type="archive"/>
+    </deb>
+  </target>
+
+  <target name="empty-package">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control"/>
+  </target>
+
+  <target name="with-archive">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <data src="org/vafer/jdeb/deb/data.tgz" type="archive"/>
+    </deb>
+  </target>
+
+  <target name="with-missing-archive">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <data src="org/vafer/jdeb/deb/invalid.tgz" type="archive"/>
+    </deb>
+  </target>
+
+  <target name="with-directory">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <data src="org/vafer/jdeb/deb/data" type="directory"/>
+    </deb>
+  </target>
+
+  <target name="with-missing-directory">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <data src="org/vafer/jdeb/deb/invalid" type="directory"/>
+    </deb>
+  </target>
+
+  <target name="verbose-enabled">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" verbose="true">
+      <data src="org/vafer/jdeb/deb/data" type="directory"/>
+    </deb>
+  </target>
+
+  <target name="verbose-disabled">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" verbose="false">
+      <data src="org/vafer/jdeb/deb/data" type="directory"/>
+    </deb>
+  </target>
+
+  <target name="missing-data-type">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" verbose="false">
+      <data src="org/vafer/jdeb/deb/data"/>
+    </deb>
+  </target>
+
+  <target name="unknown-data-type">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" verbose="false">
+      <data src="org/vafer/jdeb/deb/data" type="container"/>
+    </deb>
+  </target>
+
+  <target name="fileset">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <fileset dir="org/vafer/jdeb/deb/data"/>
+    </deb>
+  </target>
+
+  <target name="tarfileset">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <tarfileset dir="org/vafer/jdeb/deb/data" prefix="foo" dirmode="700" filemode="600" username="ebourg" group="ebourg"/>
+    </deb>
+  </target>
+
+  <target name="link">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control">
+      <tarfileset dir="org/vafer/jdeb/deb/data" prefix="foo" dirmode="700" filemode="600" username="ebourg" group="ebourg"/>
+      <link name="/usr/share/java/foo.jar" target="/usr/share/java/foo-1.0.jar" username="ebourg" group="ebourg" mode="755"/>
+    </deb>
+  </target>
+
+  <target name="unknown-compression">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" compression="rar">
+      <fileset dir="org/vafer/jdeb/deb/data"/>
+    </deb>
+  </target>
+
+  <target name="bzip2-compression">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" compression="bzip2">
+      <fileset dir="org/vafer/jdeb/deb/data"/>
+    </deb>
+  </target>
+
+  <target name="xz-compression">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" compression="xz">
+      <fileset dir="org/vafer/jdeb/deb/data"/>
+    </deb>
+  </target>
+
+  <target name="no-compression">
+    <deb destfile="test.deb" control="org/vafer/jdeb/deb/control" compression="none">
+      <fileset dir="org/vafer/jdeb/deb/data"/>
+    </deb>
+  </target>
+
+</project>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jdeb.git



More information about the pkg-java-commits mailing list